我如何有效地在8080上进行签名比较? [英] How do I efficiently do signed comparisons on the 8080?

查看:114
本文介绍了我如何有效地在8080上进行签名比较?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to compare two 16-bit numbers and branch on the result: the equivalent of if (a<b) goto negative. I'm using an Intel 8080.

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

The Z80 has a signed arithmetic overflow flag which can be used for this with some degree of effort. The standard code is:

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标志,并产生有趣的结果.

But the 8080 isn't a strict subset of the Z80, and the code above won't work there --- on the 8080, arithmetic instructions set the P flag based on the parity of the result, with hilarious results.

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

So what's the idiomatic way to do signed comparisons on the 8080?

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

Actually calculating the overflow flag is possible but really painful as it requires doing bit operations on both the operands and the result, and I'm running out of registers. Also, it's not actually what I want here; I don't actually care about the overflow. I just want to do the comparison.

(如果结果为负,我不能简单地减去并分支,因为这在所有情况下都不起作用.请考虑INT_MIN< INT_MAX.这是0x8000-0x7fff = 1,这显然是正数.)

(I can't simply subtract and branch if the result is negative because that doesn't work in all cases. Consider INT_MIN < INT_MAX. That's 0x8000 - 0x7fff = 1, which is obviously positive.)

推荐答案

考虑到已接受的答案及其评论,我将重新考虑Jester的建议(与拟议的Z80代码相比,在我看来,该建议仅+ 4B,但代码有所不同)布局,即更少/greater_equal分支所在的位置,这可能会进一步使您的代码复杂化或简化……加上它的性能要好于每次对D和H进行xor 0x80运算):

Considering the accepted answer and its comments, I would reconsider Jester's advice (which seems to me only +4B when compared to proposed Z80 code, but with somewhat different code layout, i.e. where less/greater_equal branches reside, which may further complicate or simplify your code ... plus it should perform better than doing xor 0x80 every time to both D and H):

    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
    ...

您还可以通过以下方式将其修改为在DE< HL时返回CF = 1的过程:

You can also modify it as procedure which returns CF=1 when DE<HL by doing:

: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)

顺便说一句,您可以通过sbb a将CF = 0/1转换为A = 0/〜0-有时0/255方便进行进一步的计算...

BTW you can turn CF=0/1 into A=0/~0 by sbb a - sometimes that 0/255 is handy for further calculations...

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

But as I commented under question, many time this is worth of revisit on architectural level, to see if the whole code logic can't be turned into unsigned 0..FFFF mode of operation, maybe it would lead to adjusting (by -32768) values like "_left" just at one/two specific spots (like final output to user), while many more other internal comparisons/usages would work in unsigned way.

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

Some variants for compares against constants (for one-time constant it's probably better (size-wise) to just load it into other RP and use the generic RP1 vs RP2 compare, especially if you have spare RP and the generic compare is already instantiated for other code ... but for multiple uses of the same constant this will probably win both size-wise and speed-wise ... inlining wins speed-wise? May get on par with subroutine, depends how the result is used).

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

reg-pair (actually also any 8b reg) against zero:

; 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

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

reg-pair against any 16b #XY constant:

; signed 16b compare RP (HL/DE/BC) vs nonzero constant #XY
; subroutine, returns CF=1 if RP < #XY, modifies A
    mov     a,R
    xri     0x80    ; convert 8000..7FFF into 0000..FFFF
    cpi     #X^0x80 ; "X" is xor-ed with 0x80 too to have it in 0000..FFFF range
    rnz             ; if ZF=0, then CF=1 is (RP < XY) and CF=0 is (RP > XY)
    ; R == X, the low 8b P vs Y will decide
    mov     a,P
    cpi     #Y      ; CF=1 if (RP < XY)
    ret             ; 10B for particular #XY constant and RP

; inlined form
    mov     a,R
    xri     0x80    ; convert 8000..7FFF into 0000..FFFF
    cpi     #X^0x80 ; "X" is xor-ed with 0x80 too to have it in 0000..FFFF range
    jnz     HiByteWasDecisive   ; if ZF=0, then CF is set correctly, done
    mov     a,P     ; R == #X, the low 8b P vs #Y will decide final CF
    cpi     #Y      ; CF=1 if (RP < #XY)
:HiByteWasDecisive
    ; CF=1 is (RP < #XY) and CF=0 is (RP >= #XY)
    ...

这篇关于我如何有效地在8080上进行签名比较?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆