Game Boy: флаг полупереноса и 16-битные инструкции (особенно код операции 0xE8)
Как и многие другие, я пишу эмулятор Game Boy, и у меня есть пара вопросов по инструкции 0xE8 (ADD SP, n
с 8-битным немедленным).
Утверждаются здесь, что в 16-битовых командах флаг половинного переноса устанавливается, если происходит перенос из бита 7 до бита 8, в то время как здесь сказано, что флаг переноса половины показывает перенос из бита 11 к биту 12. В этом В ветке Reddit, похоже, возникла некоторая путаница по поводу этой проблемы, и в руководстве по процессору Game Boy (заведомо ошибочному, как я слышал), похоже, тоже нет ничего полезного.
У меня следующие вопросы:
- Как ведет себя флаг полупереноса в коде операции 0xE8?
- Как код операции 0xE8 реализован на физическом оборудовании?
- Что правильно, этот полуперенос происходит с бита 7 на бит 8 или этот полуперенос происходит с бита 11 на бит 12 (в случае 16-битных инструкций)?
2 ответа
Ядро процессора SM83, используемое в Game Boy, почти наверняка имеет 8-битное ALU, что означает, что операции 16-битного ALU фактически состоят из двух 8-битных операций. Как и обычный ЦП Z80, он также имеет специальный 16-битный модуль увеличения / уменьшения / загрузки, который может быстро обрабатывать определенные 16-битные операции, но не может обновлять флаги. В основном:
- если флаги обновлены, 16-битная операция определенно включает в себя ALU, поэтому фактически под капотом используются две 8-битные операции ALU
- если флаги не обновляются, а 16-битная операция просто +1 / -1 / загрузка, это делается с помощью 16-битного инкрементального модуля
Итак, всякий раз, когда вы имеете дело с флагами, попробуйте мыслить в терминах 8-битных операций (сначала младший байт, затем старший байт), если вы хотите рассуждать об операции.
- Как ведет себя флаг полупереноса в коде операции 0xE8?
Как указано в другом ответе, H устанавливается, когда есть перенос из бита 3 (и C устанавливается, когда есть перенос из бита 7).
Вот интересное упражнение на мысли: если SP=$FFFF
и вы выполняете ADD SP, -1
, Вы получаете SP=$FFFE
и оба представляют собой Н и С установлены. Вы понимаете почему?
Из-за того, как работают числа со знаком, операция младшего байта в этом случае в основном является обычным сложением.
-1
знак равно$FF
, поэтому он рассчитывает$FF
+$FF
.
Подсказка вверху ↑
- Как код операции 0xE8 реализован на физическом оборудовании?
У нас еще нет полного понимания этого на самом низком уровне, но я знаю, что есть две 8-битные операции. С помощью своей тестовой системы Game Boy я подтвердил, что сначала есть операция ALU, которая обновляет флаги (H, C), но не SP, затем другая операция, и, наконец, SP обновляется атомарно за один раз. Это говорит о том, чтоADD SP, e
может фактически вычислить результат в некоторый временный регистр (например, реальный Z80 имеет невидимый временный регистр WZ для некоторых операций ALU) в двух отдельных 8-битных операциях, а затем загрузить SP из него.
я думаю ADD HL, BC
- это немного более интересный пример... с помощью моего тестового стенда я подтвердил, что он сначала обновляет L, а затем H, а флаги обновляются дважды. Это означает, что он буквально выполняет что-то вроде
ADD L, C
ADC H, B
Последняя 8-битная операция обновляет флаги, поэтому мы никогда не видим результирующие флаги ADD L, C
. Но флаг полупереноса может быть временно установлен, если есть перенос из L бита 3!
- Что правильно, этот полуперенос происходит с бита 7 на бит 8 или этот полуперенос происходит с бита 11 на бит 12 (в случае 16-битных инструкций)?
Это зависит от инструкции, но флаги всегда обновляются на основе одних и тех же позиций битов, если вы думаете в терминах 8-битных значений... это просто варьируется, говорим мы о старшем или младшем байте 16-битного ценность. Бит 11 - это просто 3-й бит старшего байта.
ADD SP, e
: H из бита 3, C из бита 7 (флаги из младшего байта op)LD HL, SP+e
: H из бита 3, C из бита 7 (флаги из младшего байта op)ADD HL, rr
: H из бита 11, C из бита 15 (флаги из старшего байта op)INC rr
: нет обновлений флагов (выполняется 16-битным устройством увеличения / уменьшения)DEC rr
: нет обновлений флагов (выполняется 16-битным устройством увеличения / уменьшения)
TL;DR: Для ADD SP,n
, H-флаг устанавливается, когда происходит перенос из бита 3 в бит 4.
Я решил проверить это на реальном оборудовании, поэтому я написал простой тестовый ПЗУ на сборке GB-Z80, который проверяет следующие сценарии:
[SP = 000F]ADD SP,$01
[SP = $00F0]ADD SP,$10
[SP = $0FF0]ADD SP,$10
Для каждого случая я сохраняю значение регистра F
после ADD
в памяти, и позже я отображу бит 5 (H-флаг) каждого из этих байтов на экране.
Я запустил это на трех разных моделях (Gameboy Pocket, Gameboy Color и Gameboy Advance SP) и получил следующий результат на всех трех устройствах: 1 0 0
. Таким образом, перенос из бита 3->4 вызывал установку H, а перенос из бита 7->8 или 11->12 - нет.
За ADD HL,rr
(где rr
является BC/DE/HL/SP
) похоже это отдельная история. Основываясь на моем тестировании, H устанавливается, если происходит перенос с бита 11 на бит 12.