用 8085 微处理器汇编语言求一个数的绝对值

Finding the absolute value of a number in 8085 microprocessor assembly language

我的任务是用 8085 汇编语言求任意给定数字的绝对值。

算法如下(网上找的):

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

(掩码 + n) 异或掩码

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

如有任何想法,我们将不胜感激。 干杯。

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

然后你用它来做任何事情 (n+0) ^ 0 或者做 2 的补码取反 "manually" 作为 -n = (n + (-1)) ^ -1 = ~(n-1).

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


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

在8085上,简单实现一下:if(n<0) n=-n;

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

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

我在 https://www.daenotes.com/electronics/digital-electronics/instruction-set-intel-8085 so you you could zero another register and sub, or you could negate the accumulator in place with a 2's complement identity like CMA (NOT A) 中没有看到 neg 指令; inr a (累加器 += 1) 而不是移动到另一个 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 个周期。

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

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


在 8085 上实现 branchless bithack

SBB A根据CF

设置A=0或-1

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

为了好玩,我尝试用这个技巧无分支地实现 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 技巧在这里也适用之前就已经输入了它。)