Game Boy: флаг полупереноса и 16-битные инструкции (особенно код операции 0xE8)

Как и многие другие, я пишу эмулятор Game Boy, и у меня есть пара вопросов по инструкции 0xE8 (ADD SP, n с 8-битным немедленным).

Утверждаются здесь, что в 16-битовых командах флаг половинного переноса устанавливается, если происходит перенос из бита 7 до бита 8, в то время как здесь сказано, что флаг переноса половины показывает перенос из бита 11 к биту 12. В этом В ветке Reddit, похоже, возникла некоторая путаница по поводу этой проблемы, и в руководстве по процессору Game Boy (заведомо ошибочному, как я слышал), похоже, тоже нет ничего полезного.

У меня следующие вопросы:

  1. Как ведет себя флаг полупереноса в коде операции 0xE8?
  2. Как код операции 0xE8 реализован на физическом оборудовании?
  3. Что правильно, этот полуперенос происходит с бита 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-битных операций (сначала младший байт, затем старший байт), если вы хотите рассуждать об операции.

  1. Как ведет себя флаг полупереноса в коде операции 0xE8?

Как указано в другом ответе, H устанавливается, когда есть перенос из бита 3 (и C устанавливается, когда есть перенос из бита 7).

Вот интересное упражнение на мысли: если SP=$FFFF и вы выполняете ADD SP, -1, Вы получаете SP=$FFFE и оба представляют собой Н и С установлены. Вы понимаете почему?

Из-за того, как работают числа со знаком, операция младшего байта в этом случае в основном является обычным сложением. -1 знак равно $FF, поэтому он рассчитывает $FF+ $FF.

Подсказка вверху ↑

  1. Как код операции 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!

  1. Что правильно, этот полуперенос происходит с бита 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.

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