我如何有效地在8080上进行签名比较?

时间:2019-02-11 21:56:13

标签: assembly comparison signed intel-8080

我想比较两个16位数字并跳转结果:相当于if (a<b) goto negative。我正在使用Intel 8080。

Z80具有带符号的算术溢出标志,可以在不费吹灰之力的情况下使用它。标准代码是:

ld de, _left
ld hl, _right
ld a, e
sub a, l
ld a, d
sbc a, h
jp po, $+5  ; branch on overflow flag not set
xor a, 0x80 ; flip sign bit
jm negative ; actually do the test

但是8080不是Z80的严格子集,并且上面的代码在那里不起作用---在8080上,算术指令根据结果的奇偶性设置P标志,并产生有趣的结果。

那么在8080上进行签名比较的惯用方式是什么?

实际上可以计算溢出标志,但确实很麻烦,因为它需要对操作数和结果都进行位运算,而我用光了寄存器。另外,这实际上并不是我想要的;我实际上并不关心溢出。我只想进行比较。

(如果结果为负,我不能简单地减去并分支,因为这在所有情况下都不起作用。考虑INT_MIN

2 个答案:

答案 0 :(得分:3)

在8085上有两个undocumented flags K and V用于带符号的比较和带符号的溢出,以及用于跳过小于/大于的带符号的未记录的JK / JNK指令

不确定它们是否可用于8080。但是,如果他们不这样做,您可以通过切换最高位来convert a signed comparison to an unsigned one and vice versa

bool signedCmp(int a, int b)
{
    return unsignedCmp(a ^ INT_MIN, b ^ INT_MIN);
}

我不知道8080的装配体,但是也许可以使用类似的东西来比较DE和HL

mov a, e
sub a, l     ; e - l
mov a, h
xri a, 0x80
mov h, a     ; h ^= 0x80
mov a, d
xri a, 0x80  ; a = d ^ 0x80
sbb a, h     ; d - h
jc lessthan  ; branch on carry
    ; >= case
:lessthan
    ; < case

答案 1 :(得分:2)

考虑到已接受的答案及其评论,我将重新考虑Jester的建议(与拟议的Z80代码相比,在我看来,该建议仅+ 4B,但代码布局略有不同,即,较少/ greater_equal分支所在的位置,可能会使情况更加复杂)或简化您的代码...而且它的性能应该比每次对D和H都执行xor 0x80更好):

for (i in 1:length(cell_ranges)) {
  a <- c()
  for(sheet in sheet_names) {
    a <- c(a, read_excel("file.xlsx", sheet = sheet,range=cell_ranges[i]))
    assign(var_names[i], a)
  }
}

您还可以通过以下方式将其修改为在DE

    mov     a,d
    xra     h
    jp      sameSigns   ; as "JNS" in 8086 / "jp p," in Z80
    ; sign bits are different, signed overflow may happen
    ; but if H positive, then DE is less than HL
    xra     d           ; make A=H and set sign flag
    jm      DeIsGreaterEqualThanHl
:DeIsLessThanHl
    ; DE < HL
    ...

:sameSigns
    ; sign bits are equal, it is safe to do ordinary sub
    mov     a,e
    sub     l
    mov     a,d
    sbb     h
    jc      DeIsLessThanHl
:DeIsGreaterEqualThanHl
    ; DE >= HL
    ...

顺便说一句,您可以通过:SignedCmpDeHl mov a,d xra h jp sameSigns ; as "JNS" in 8086 / "jp p," in Z80 ; sign bits are different, signed overflow may happen ; but if H positive, then DE is less than HL xra d ; make A=H and set sign flag (CF=0) rm ; return CF=0 when DE >= HL (H is negative) stc ret ; return CF=1 when DE < HL (H is positive/zero) :sameSigns ; sign bits are equal, it is safe to do ordinary sub mov a,e sub l mov a,d sbb h ret ; return with CF=1 when DE < HL (CF=0 DE >= HL) 将CF = 0/1转换为A = 0 /〜0-有时sbb a很方便进行进一步的计算...

但是正如我所质疑的那样,很多时候值得在体系结构级别上进行重新研究,以查看是否无法将整个代码逻辑转换为无符号0..FFFF操作模式,也许会导致调整( (由-32768表示)仅在一个/两个特定位置(例如向用户的最终输出)的“ _left”值,而其他许多内部比较/用法将以无符号方式工作。

编辑:

一些与常量进行比较的变体(对于一次性常量,最好将其加载到其他RP中并使用通用RP1与RP2比较(在大小上更好),尤其是当您有备用RP并且通用比较为已经为其他代码实例化了...但是对于相同常量的多次使用,这可能会同时赢得大小和速度方面的好处...内联赢得速度方面的好处?可以与子例程相提并论,取决于结果的使用方式)。

reg对(实际上也是8b reg)对零:

0/255

针对任何16b #XY常数的reg对:

; signed compare 8b or 16b register vs 0, into SF, destroys A
    xra     a       ; A=0
    ora     R       ; 16b R=[HDB], or any 8b R: SF = (RP < 0 or R < 0)
    ...i.e. "jm hlIsLessThanZero"

; signed compare 8b or 16b register vs 0, into CF, destroys A
    mov     a,R     ; 16b R=[HDB], or any 8b R
    ral             ; CF = (RP < 0) or (R < 0)
    ...i.e. "jc hlIsLessThanZero" or "sbb a" to get 0/255