Как устанавливается флаг переноса в этом коде сборки?
Учитывая следующий код сборки для 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);
}
Я смущен двумя вещами.
В этой строке...
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 года, чья годовщина выпуска АН недавно прошла; бесплатное подтверждение правильной догадки:-)В следующей строке...
uint8_t carry = HIGH(v1) ? 1 : 0;
Почему это так работает? Я читаю это как: "Установите флаг переноса, если и только если старший байт ненулевой". Но не будет ли указание на переполнение только в том случае, если старший байт равен нулю? (Я, вероятно, неправильно понимаю, что делает линия.)
Заранее спасибо за любые идеи.
2 ответа
- но как мы можем быть уверены, что флаг переноса установлен в 1? Какая предыдущая операция гарантировала бы это?
$80/8137 38 SEC ; SEt Carry flag
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 битов для извлечения так называемого "переноса".