在此汇编代码中如何设置进位标志?

How is the carry flag being set in this assembly code?

给定以下 16 位 PRNG 函数的汇编代码,

/8111 E2 20       SEP #   ; set 8-bit mode accumulator
/8113 AD E5 05    LDA E5  ; load low byte of last random number
/8116 8D 02 42    STA 02
/8119 A9 05       LDA #   ; multiply it by 5
/811B 8D 03 42    STA 03
/811E EA          NOP
/811F C2 20       REP #   ; set 16-bit mode accumulator
/8121 AD 16 42    LDA 16  ; load the resultant product
/8124 48          PHA        ; push it onto the stack
/8125 E2 20       SEP #   ; 8-bit
/8127 AD E6 05    LDA E6  ; load high byte of last random number
/812A 8D 02 42    STA 02
/812D A9 05       LDA #   ; multiply by 5
/812F 8D 03 42    STA 03
/8132 EB          XBA        ; exchange high and low bytes of accumulator
/8133 EA          NOP
/8134 AD 16 42    LDA 16  ; load low byte of product
/8137 38          SEC
/8138 63 02       ADC ,s  ; add to it the high byte of the original product
/813A 83 02       STA ,s  ; save it to the high byte of the original product
/813C C2 20       REP #   ; 16-bit
/813E 68          PLA        ; pull it from the stack
/813F 69 11 00    ADC #[=10=]11 ; add 11
/8142 8D E5 05    STA E5  ; save as new random number
/8145 6B          RTL

名为@sagara 的用户将代码翻译成 C:

#define LOW(exp)  ((exp) & 0x00FF)
#define HIGH(exp) (((exp) & 0xFF00) >> 8)

uint16_t prng(uint16_t v) {

    uint16_t low  = LOW(v);
    uint16_t high = HIGH(v);

    uint16_t mul_low  = low  * 5;
    uint16_t mul_high = high * 5;

    // need to check for overflow, since final addition is adc as well
    uint16_t v1    = LOW(mul_high) + HIGH(mul_low) + 1;
    uint8_t  carry = HIGH(v1) ? 1 : 0;

    uint16_t v2 = (LOW(v1) << 8) + LOW(mul_low);

    return (v2 + 0x11 + carry);
}

我对两件事感到困惑。

  1. 这一行...

    uint16_t v1    = LOW(mul_high) + HIGH(mul_low) + 1;
    

    为什么会有+ 1?我认为是因为ADC操作,但我们如何确定进位标志设置为1?之前的操作可以保证这一点? XBC?我读了一些帖子,例如 Assembly ADC (Add with carry) to C++ and Overflow and Carry flags on Z80 but it's not clear to me because the instruction set appears to be different I'm not familiar with 65C816 assembly。 (这是来自一款流行的 1994 SNES 游戏,其 NA 发布周年纪念日刚刚过去;免费投票给正确的猜测:-)

  2. 下一行...

    uint8_t  carry = HIGH(v1) ? 1 : 0;
    

    为什么会这样?我把它读成 "Set the carry flag if and only if the high byte is non-zero." 但是只有当高字节 零时才不会指示溢出?(我可能误解了这条线在做什么。)

提前感谢您的任何见解。

  1. but how can we be sure that the carry flag is set to 1? What previous operation would guarantee this?
/8137 38          SEC   ; SEt Carry flag

  1. uint8_t carry = HIGH(v1) ? 1 : 0;
    Why would it work this way? I read this as, "Set the carry flag if and only if the high byte is non-zero." But wouldn't the indication of an overflow be only if the high byte is zero?

加法 ADC #[=12=]11 使用了 ADC ,s 的进位。当执行 ADC ,s 时,累加器设置为 8 位(因为 SEP #),因此如果 ADC ,s 的结果超过 8 位(也就是说,如果你在 16 位模式下得到了 >= $100 的东西。
在 C 版本中,您有一个 16 位变量 (v1) 来保存结果,因此您的进位将位于 v1 的第 8 位,您可以使用 HIGH(v1) == 1 对其进行测试, 或者简单地 HIGH(v1) 因为它要么是 1 要么是 0.

1) 行

/8137 38     SEC

明确地在 ADC add-with-carry 指令之前设置进位,这就是 C 代码中 +1 的原因。

2) 处理器有一个8位累加器,加法会溢出到进位,为下一条ADC指令做准备。但是,C 代码使用的是 16 位变量 v1,进位保留在高 8 位。因此测试那些高 8 位以提取所谓的 "carry".