【问题标题】:How is the carry flag being set in this assembly code?这个汇编代码中的进位标志是如何设置的?
【发布时间】:2016-04-20 13:36:45
【问题描述】:

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

$80/8111 E2 20       SEP #$20   ; set 8-bit mode accumulator
$80/8113 AD E5 05    LDA $05E5  ; load low byte of last random number
$80/8116 8D 02 42    STA $4202
$80/8119 A9 05       LDA #$05   ; multiply it by 5
$80/811B 8D 03 42    STA $4203
$80/811E EA          NOP
$80/811F C2 20       REP #$20   ; set 16-bit mode accumulator
$80/8121 AD 16 42    LDA $4216  ; load the resultant product
$80/8124 48          PHA        ; push it onto the stack
$80/8125 E2 20       SEP #$20   ; 8-bit
$80/8127 AD E6 05    LDA $05E6  ; load high byte of last random number
$80/812A 8D 02 42    STA $4202
$80/812D A9 05       LDA #$05   ; multiply by 5
$80/812F 8D 03 42    STA $4203
$80/8132 EB          XBA        ; exchange high and low bytes of accumulator
$80/8133 EA          NOP
$80/8134 AD 16 42    LDA $4216  ; load low byte of product
$80/8137 38          SEC
$80/8138 63 02       ADC $02,s  ; add to it the high byte of the original product
$80/813A 83 02       STA $02,s  ; save it to the high byte of the original product
$80/813C C2 20       REP #$20   ; 16-bit
$80/813E 68          PLA        ; pull it from the stack
$80/813F 69 11 00    ADC #$0011 ; add 11
$80/8142 8D E5 05    STA $05E5  ; save as new random number
$80/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++Overflow and Carry flags on Z80,但我并不清楚,因为指令集似乎不同我不熟悉65C816 assembly。 (这是 1994 年流行的 SNES 游戏,它的北美发行周年纪念日刚刚过去;免费投票给正确的猜测 :-)

  2. 在下一行...

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

    为什么会这样?我将其读为,“当且仅当高字节非零时设置进位标志。” 但是,如果高字节,则不是溢出的指示/em> 零?(我可能误解了这条线在做什么。)

提前感谢您提供任何见解。

【问题讨论】:

  • 这是什么汇编语言?您链接的帖子适用于不同的指令集。请适当地标记它。
  • 现在我知道这看起来很熟悉了。自从我使用 NES 组装以来已经有一段时间了。
  • 是的,那是 65C816 组件,这是 SNES 的 5A22 所基于的 CPU。
  • 所有的摩托罗拉组装机看起来都差不多。
  • $80/8137 38 SEC 行明确地在 ADC add-with-carry 指令之前设置进位,这就是 C 代码中 +1 的原因。

标签: c assembly random carryflag 65816


【解决方案1】:
  1. 但是我们如何确定进位标志设置为 1?以前的哪些操作可以保证这一点?
$80/8137 38          SEC   ; SEt Carry flag

  1. uint8_t carry = HIGH(v1) ? 1 : 0;
    为什么会这样?我将其读作“当且仅当高字节非零时设置进位标志”。但是,不是只有高字节为零才表示溢出吗?

加法ADC #$0011 使用来自ADC $02,s 的进位。执行ADC $02,s 时,累加器设置为 8 位(因为SEP #$20),因此如果ADC $02,s 的结果超过 8 位(即如果你已经在 16 位模式下得到了 >= 100 美元的东西)。
在 C 版本中,您有一个 16 位变量 (v1) 来保存结果,因此您的进位将位于 v1 的第 8 位,您可以使用 HIGH(v1) == 1 或简单地使用 HIGH(v1) 进行测试因为它要么是 1 要么是 0。

【讨论】:

  • 啊,我已经意识到为什么 (2) 让我失望了。我错误地将LOW(mul_high) + HIGH(mul_low) 解读为连接两个 8 位字符串(好像其中一个被移位),而不是简单地添加。所以我的印象是HIGH(v1) 可以是任何数字范围,因此我的整个“非零”事物。但是您的解释清楚了:在这种情况下只能设置 1 位。谢谢!
  • @Andrew Cheong 这样做是因为 C 没有“进位”。
【解决方案2】:

1) 线

$80/8137 38     SEC

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-18
    • 2015-05-12
    • 1970-01-01
    • 1970-01-01
    • 2017-10-20
    • 2014-01-01
    相关资源
    最近更新 更多