Как устанавливается флаг переноса в этом коде сборки?

Учитывая следующий код сборки для 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) для C++ и флаги Overflow и Carry на Z80, но мне это не ясно, потому что набор инструкций кажется другим, я не знаком со сборкой 65C816. (Это из популярной игры SNES 1994 года, чья годовщина выпуска АН недавно прошла; бесплатное подтверждение правильной догадки:-)

  2. В следующей строке...

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

    Почему это так работает? Я читаю это как: "Установите флаг переноса, если и только если старший байт ненулевой". Но не будет ли указание на переполнение только в том случае, если старший байт равен нулю? (Я, вероятно, неправильно понимаю, что делает линия.)

Заранее спасибо за любые идеи.

2 ответа

Решение
  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 бит (т.е. если бы вы получили что-то>= 100 долларов в 16-битном режиме).
В версии C у вас есть 16-битная переменная (v1), чтобы сохранить результат, так что ваш перенос будет в бите 8 v1, который вы можете проверить с HIGH(v1) == 1или просто HIGH(v1) так как это будет либо 1, либо 0.

1) Линия

$80/8137 38     SEC

это эксплозия, устанавливающая перенос как раз перед ADC инструкция "добавить с переносом", поэтому +1 в коде C.

2) Процессор имеет 8-разрядный аккумулятор, и сложение переполнится для переноса, готового к следующему ADC инструкция. Тем не менее, код C использует 16-битную переменную v1и перенос остается в верхних 8 битах. Отсюда и проверка этих старших 8 битов для извлечения так называемого "переноса".

Другие вопросы по тегам