【问题标题】:Finding the absolute value of a number in 8085 microprocessor assembly language用 8085 微处理器汇编语言求一个数的绝对值
【发布时间】:2019-12-05 04:17:10
【问题描述】:

我的任务是在 8085 汇编语言中找到任何给定数字的绝对值。

算法如下(在互联网上找到):

mask = n >> 7(数字本身是8位)

(掩码 + n) 异或掩码

我的问题是如何用汇编语言实现它。 看来我应该使用“RRC”命令,但是对数字执行循环移位,算法似乎不起作用。

任何想法将不胜感激。 干杯。

【问题讨论】:

    标签: assembly bit-manipulation bit-shift 8085


    【解决方案1】:

    abs 算法中的n>>7 是一个算术右移,它会在符号位的副本中移动,所以你得到-1 用于负n,0 用于非消极的。 (在 2 的补码中,-1 的位模式已设置所有位)。

    然后你用它要么什么都不做(n+0) ^ 0,要么用-n = (n + (-1)) ^ -1 = ~(n-1)“手动”做2的补码否定。

    请参阅How to prove that the C statement -x, ~x+1, and ~(x-1) yield the same results? 了解 2 的补码恒等式。与全一的异或是按位非。添加mask = -1当然是n-1


    分支机构很便宜,而且创建和使用0-1(根据数字符号)所涉及的寄存器复制加起来。 (虽然我确实想出了一种方法,只用 6 字节的代码来实现这一点,与分支版本的代码大小相同。)

    在 8085 上,只需简单的方式实现:if(n<0) n=-n;

    (将结果视为无符号;注意 -0x80 = 0x80 为 8 位。如果您假设它在 abs 之后是有符号正数,那么对于最负数的输入,您将是错误的。)

    这对于否定条件分支的条件分支应该是微不足道的; 8085 确实有依赖于符号位的分支。 (一般不进行签名比较,除非您使用未记录的 k 标志 = 签名溢出)。 根据A 设置标志,然后JP 否定。 (“加号”条件测试 Sign 标志 = 0,因此它实际上是在测试非负而不是严格正)

    我在https://www.daenotes.com/electronics/digital-electronics/instruction-set-intel-8085 中没有看到neg 指令,因此您可以将另一个寄存器和sub 归零,或者您可以使用像CMANOT A)这样的2 补码标识来否定累加器) ; inr a (accumulator += 1) 而不是 mov 到另一个 reg 并从 A=0 中减去。

    8085 具有廉价的分支,不像现代流水线 CPU,在分支错误预测时分支可能代价高昂。 mask = n >> 31 或无分支 abs 的等效项在那里很有用,整个事情通常只有 3 或 4 条指令。 (8085 仅具有移位 1 指令;包括现代 x86 在内的后来的 ISA 具有快速立即移位,可以在单个指令中执行 n >> 31,通常具有良好的延迟,例如 1 个周期。)

    未经测试的 A = abs(A)

    ; total 6 bytes.  (jumps are opcode + 16-bit absolute target address)
        ana  A              ; set flags from A&A
        jp  non_negative    ; jump if MSB was clear
        cma
        inr  A              ; A = ~A+1 = -A
     non_negative:
       ; unsigned A = abs(signed A) at this point
    

    http://pastraiser.com/cpu/i8085/i8085_opcodes.html 有一个带有循环时序的操作码映射。 1 字节 ALU 寄存器指令需要 4 个周期,2 字节 ALU reg 指令(带立即数)需要 7 个周期。条件分支需要 7 个周期未占用,10 个周期占用。

    • 对于非负输入(采用):周期成本为 4 (ANA) + 10 (JP) = 14 个周期
    • 对于负输入(未采用):4(ANA) + 7(JP) + 4 + 4 = 19 个周期。

    (时序计算似乎微不足道;每条指令只有一个固定成本,这与现代流水线超标量乱序 CPU 不同,后者的吞吐量和延迟是独立的,并非每条指令都可以在每个执行端口上运行...... )


    在 8085 上实现无分支 bithack

    SBB A 根据 CF 设置 A = 0 或 -1

    这是一个众所周知的汇编技巧,用于将比较条件转换为 0 / -1 掩码。您只需要将值的 MSB 放入进位标志,例如用 A+A 或旋转。这为您提供了 xor/add 所需的 n >> 7 0 : -1 值。

    只是为了好玩,我尝试用这个技巧无分支地实现 abs()。这是我想出的最好的。 仅当您需要对时序攻击免疫时才使用此选项,因此时钟周期成本不取决于输入数据。(或者对于与位置无关的代码;跳转使用绝对目标地址,而不是 +- 相对偏移量。)

    它的优点是可以将原件保存在另一个寄存器中。

    ;;;   UNTESTED slower branchless abs
    ;; a = abs(b).  destroys c (or pick any other tmp reg)
    ;; these are all 1-byte instructions (4 cycles each)
       mov  a, b
       add  a         ; CF = sign bit
       sbb  a         ; A = n-n-CF = -CF.  0 or -1
       mov  c, a
       xra  b         ;  n         or    ~n
       sub  a, c      ; n-0 = n    or    ~n-(-1) = ~n+1 = -n
    
    ; uint8_t A = abs(int8_t B)
    

    这仍然只有 6 个字节,和 branchy 一样,但需要 6*4 = 24 个周期。

    如果 XRA 不影响标志,我们可以 sbi 0 执行 -1 步骤。但它确实总是清除 CF。我看不到保存 0 / -1 结果副本的方法。而且我们无法计算到B 就地进行; 8085是一种蓄能器机器。当您需要时,8086 的 1 字节交换与累加器在哪里? xchg a,b 会很有用。

    如果你的值从A开始,你需要把它复制到别的地方,所以你需要销毁两个其他寄存器。


    将 A 的符号位广播到所有位置的更糟糕的选择:

       RLC     ; low bit of accumulator = previous sign bit
       CMA     ; Bitwise NOT: 0 for negative, 1 for non-negative
       ANI  1  ; isolate it, clearing higher bits
       DCR  A  ; 0 or 1  -> -1 or 0
    

    这比rlc/sbb a还要糟糕;我仅将它作为位操作的练习包含在内,以了解它为什么起作用。 (而且因为我在记住我从其他 ISA 知道的 SBB 技巧也可以在这里使用之前,已经输入了它。)

    【讨论】:

    • 太好了,非常感谢您的详细回答。问候。
    • @franklin:更新了我发布第一个版本后的一些其他想法,以防你好奇。
    • 这很有帮助,非常感谢我的好人。很好的答案,现在对我来说事情变得更清楚了,当然。问候。
    • @franklin:我记得创建 0 : -1 值的另一个常见技巧。我以前从未对 8085 做过很多事情(尽管我对现代 x86 非常了如指掌;8086 受到 8080 的影响)。我很好奇,实际上以两种方式实现了它,看看它们是如何比较的。更新了我的答案。 Branchy 仍然更快,但现在没有那么落后。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多