【问题标题】:What techniques are available for memory optimizing in 8051 assembly language?8051 汇编语言中内存优化有哪些技术可用?
【发布时间】:2010-09-25 00:47:42
【问题描述】:

我需要优化代码以便为一些新代码腾出空间。我没有空间进行所有更改。我不能使用代码库切换(80c31 和 64k)。

【问题讨论】:

    标签: optimization assembly embedded intel 8051


    【解决方案1】:

    您在这里并没有真正做太多事情,但是您可以考虑两个主要级别的优化:

    微优化: 例如。 XOR A 而不是 MOV A,0 Adam 之前已经很好地介绍了其中的一些内容。

    宏观优化: 查看程序的结构、使用的数据结构和算法、执行的任务,并认真思考如何重新排列甚至删除这些内容。是否存在实际上没有使用的整块代码?您的代码是否充满了用户从未见过的调试输出语句?是否有特定于单个客户的功能可以在一般版本中省略?

    要很好地处理这个问题,您需要找出内存在哪里被用完。 Linker map 是一个很好的起点。宏观优化是大获全胜的地方。

    顺便说一句,您可以 - 认真地 - 尝试使用优化的 C 编译器重写部分代码。您可能会惊讶于代码的紧凑程度。一个真正的汇编能手可能会改进它,但它很容易比大多数编码器更好。大约 20 年前,我使用了 IAR 一个,它让我大吃一惊。

    【讨论】:

    • 第二个优化编译器:有些参数不是为了速度,而是为了空间。
    【解决方案2】:

    使用汇编语言,您必须手动进行优化。以下是一些技巧:

    注意:IANA8051P(我不是 8501 程序员,但我在其他 8 位芯片上做了很多组装)。

    通过代码寻找任何重复的位,无论多小,并使它们起作用。

    了解一些比较不寻常的指令,看看是否可以使用它们进行优化,例如。一个不错的技巧是使用 XOR A 来清除累加器而不是 MOV A,0 - 它节省了一个字节。

    另一个巧妙的技巧是,如果您在返回之前调用一个函数,只需跳转到它,例如,而不是:

    CALL otherfunc
    RET
    

    只要做:

    JMP otherfunc
    

    始终确保尽可能进行相对跳转和分支,它们使用的内存比绝对跳转少。

    这就是我暂时能想到的。

    【讨论】:

    • @jschmier:其他一些不错的技巧不适用于 TCO。例如,如果“打印消息”例程采用 DPTR 中的地址并返回 DPTR 指向尾随的 null,则定义类似 [可能需要调整]PrImm: POP DPH / POP DPL / CALL PrintMsg / INC DPTR / PUSH DPL / PUSH DPH / RET 的内容可能很有用。每次调用 Primm 只需要 3 个字节加上要输出的实际字符。
    【解决方案3】:

    对不起,我来晚了,但我曾经遇到过完全相同的问题,它变成了一个反复出现的问题,不断地回到我身边。就我而言,该项目是一部电话,在 8051 系列处理器上,我已经完全用尽了 ROM(代码)内存。它不断地回到我身边,因为管理层不断要求新功能,所以每个新功能都变成了一个两步过程。 1)优化旧东西腾出空间 2)实现新功能,用完我刚刚腾出的空间。

    有两种优化方法。战术和战略。战术优化使用微优化思想一次节省几个字节。我认为您需要进行战略优化,包括对您的工作方式进行更彻底的重新思考。

    我记得的一些东西对我有用,也可以为你工作;

    看看你的代码必须做什么的本质,并尝试提炼出一些非常强大的灵活的原始操作。然后重建您的顶级代码,以便它除了调用原语之外根本不做任何低级代码。理想情况下使用基于表格的方法,您的表格包含以下内容;输入状态、事件、输出状态、原语……换句话说,当事件发生时,在表中查找当前状态下的该事件的单元格。该单元格告诉您要更改为(可选)什么新状态以及要执行什么原语(如果有)。对于不同的层/子系统,您可能需要多组状态/事件/表/基元。

    这种方法的许多好处之一是您可以将其视为针对您的特定问题构建自定义语言,您可以通过修改表格非常有效地(即使用最少的额外代码)创建新功能。

    抱歉,我迟到了几个月,你可能没有时间做这么激进的事情。据我所知,您已经在使用类似的方法!但我的回答可能有一天会帮助其他知道的人。

    【讨论】:

    • 在类似的情况下(也使用 8051 芯片),我经常设法在硬件重新设计期间设法重写代码的核心。对我来说最大的胜利是创建标准计数器(即一个包含 0..1000 之间的毫秒,另一秒为 0..1000 的计数器),然后将其他功能与这些计数器链接起来。这确实节省了很多内存。我喜欢您的标准函数和表格方法,类似于自定义字节码解释器。我用它在一个为儿童播放歌曲的小 UI 板上播放曲调/闪光灯。
    【解决方案4】:

    在被淘汰的部门中,您还可以考虑压缩部分代码,只保留在任何特定时间点被积极使用的部分解压缩。我很难相信压缩/解压缩系统所需的代码将是 8051 微小内存的一部分,足以让这变得有价值,但在稍大的系统上创造了奇迹。

    另一种方法是转向字节码格式或某些状态机工具输出的那种表驱动的代码——让机器了解您的应用正在做什么并生成一个完全难以理解的实现可能是一个很好的方法节省空间的方法:)

    最后,如果代码确实是用 C 编译的,我建议使用一系列不同的选项进行编译,看看会发生什么。另外,I wrote a piece on compact C coding for the ESC back in 2001 that is still pretty current. 请参阅该文本以了解小型机器的其他技巧。

    【讨论】:

    • +1 为你的论文“Getting the Least Out of Your C Compiler”的真棒名称
    • AFAIK 8051 无法从 RAM 运行代码,因此这种方法不起作用。字节码是个好主意,如果你能让解释器足够紧凑的话。
    【解决方案5】:

    1) 尽可能将变量保存在 Idata 而不是 xdata
    2) 查看你的 Jmp 语句——利用 SJmp 和 AJmp

    【讨论】:

      【解决方案6】:

      我假设您知道它不适合,因为您编写/遵守并得到“内存不足”错误。 :) 看来答案非常准确地解决了您的问题;缺少代码示例。

      不过,我会推荐一些额外的想法;

      1. 确保所有代码都是真的 正在使用——代码覆盖测试?一个 未使用的潜艇是一个巨大的胜利——这是一个 艰难的一步——如果你是原创者 作者,这可能更容易——(好吧,也许):)
      2. 确保“验证”级别 和初始化——有时我们 有过分热心的倾向 在确保我们已经初始化 变量/内存,果然 没错,我们有多少次 被它咬过。不是说不要 初始化(duh),但如果我们正在做 记忆的移动,目的地 不需要首先被归零—— 这与

        1 --

      3. 评估新功能 -- 可以 现有的子被增强以覆盖 两个功能,或者也许是一个 现有功能被替换了吗?
      4. 如果有一段代码,请拆分大代码 大代码可以节省创建一个新的 小代码。

      或者现在桌面上可能有硬件版本 2.0 的争论...... :)

      问候

      【讨论】:

        【解决方案7】:

        除了已经提到的(或多或少)明显的优化之外,还有一个非常奇怪(几乎不可能实现)的优化:代码重用。对于代码重用,我并不是指正常的重用,而是a)将您的代码重用为数据或b)将您的代码重用为其他代码。也许您可以创建一个 lut(或任何静态数据),它可以由 asm 十六进制操作码表示(在这里您必须查看哈佛与冯诺依曼架构)。

        当你处理不同的代码时,另一个会通过赋予代码不同的含义来重用代码。这里有一个例子来说明我的意思。如果代码的字节如下所示:地址 X 处的 AABCCCDDEEFFGGHH,其中每个字母代表一个操作码,假设您现在将跳转到 X+1。也许你会得到一个完全不同的功能,现在由空格分隔的字节形成新的操作码:ABC CCD DE EF GH。

        但请注意:这不仅难以实现(也许不可能),而且难以维持。因此,如果您不是演示代码(或类似的外来代码),我建议您使用已经提到的其他方法来保存内存。

        【讨论】:

        • 是啊啊啊,这些优化已经被淘汰了。
        • 我赞成你回到零,因为我认为你的想法如果有点不切实际的话是富有想象力的。我曾在很小的范围内看到过类似的工作;用很少的字节数实现特定功能的聪明技巧,而不是帮助这个提问者的通用技术。
        猜你喜欢
        • 1970-01-01
        • 2010-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-07
        • 2012-04-30
        • 2011-08-26
        • 1970-01-01
        相关资源
        最近更新 更多