mercredi 20 avril 2016

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

Given the following assembly code for a 16-bit PRNG function,

$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

a user by the name of @sagara translated the code to 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;
    uint8_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);
}

I'm confused by two things.

  1. In this line...

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

    Why is there a + 1? I think it's because of the ADC operation, but how can we be sure that the carry flag is set to 1? What previous operation would guarantee this? The XBC? I read a few posts such as 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. (This is from a popular 1994 SNES game whose NA release anniversary recently passed; free upvote to the correct guess :-)

  2. In the next line...

    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? (I'm probably misinterpreting what the line is doing.)

Thanks in advance for any insights.




Aucun commentaire:

Enregistrer un commentaire