Инструкция Z80 DAA
Извиняется за этот, казалось бы, второстепенный вопрос, но я не могу найти ответ нигде - я просто приступаю к реализации инструкции DAA в своем эмуляторе Z80, и я заметил в руководстве Zilog, что это для цели настройки аккумулятора для двоичной кодированной десятичной арифметики. Он говорит, что инструкция предназначена для запуска сразу после инструкции сложения или вычитания.
Мои вопросы:
- что произойдет, если он запускается после другой инструкции?
- как он узнает, какое обучение преследовало его?
- Я понимаю, что есть флаг N, но это, безусловно, не означало бы окончательно указать, что предыдущая инструкция была инструкцией сложения или вычитания?
- Разве это просто модифицирует аккумулятор в зависимости от условий, указанных в таблице DAA, независимо от предыдущей инструкции?
Ответы
Ответ 1
В любом случае он просто изменяет аккумулятор, основываясь на условиях, изложенных в таблице DAA, независимо от предыдущей инструкции?
Да. Документация только говорит вам, для чего предназначен DAA. Возможно, вы ссылаетесь на таблицу по этой ссылке:
--------------------------------------------------------------------------------
| | C Flag | HEX value in | H Flag | HEX value in | Number | C flag|
| Operation | Before | upper digit | Before | lower digit | added | After |
| | DAA | (bit 7-4) | DAA | (bit 3-0) | to byte | DAA |
|------------------------------------------------------------------------------|
| | 0 | 0-9 | 0 | 0-9 | 00 | 0 |
| ADD | 0 | 0-8 | 0 | A-F | 06 | 0 |
| | 0 | 0-9 | 1 | 0-3 | 06 | 0 |
| ADC | 0 | A-F | 0 | 0-9 | 60 | 1 |
| | 0 | 9-F | 0 | A-F | 66 | 1 |
| INC | 0 | A-F | 1 | 0-3 | 66 | 1 |
| | 1 | 0-2 | 0 | 0-9 | 60 | 1 |
| | 1 | 0-2 | 0 | A-F | 66 | 1 |
| | 1 | 0-3 | 1 | 0-3 | 66 | 1 |
|------------------------------------------------------------------------------|
| SUB | 0 | 0-9 | 0 | 0-9 | 00 | 0 |
| SBC | 0 | 0-8 | 1 | 6-F | FA | 0 |
| DEC | 1 | 7-F | 0 | 0-9 | A0 | 1 |
| NEG | 1 | 6-F | 1 | 6-F | 9A | 1 |
|------------------------------------------------------------------------------|
Я должен сказать, что я никогда не видел спецификацию инструкции после этого. Если вы внимательно изучите таблицу, то увидите, что действие инструкции зависит только от флагов C
и H
и значения в аккумуляторе - оно вообще не зависит от предыдущей инструкции. Кроме того, он не разглашает, что произойдет, если, например, C=0
, H=1
и нижняя цифра в аккумуляторе равна 4 или 5. Так что вам придется выполнить NOP
в таких случаях или сгенерировать сообщение об ошибке или что-то.
Ответ 2
Просто хотел добавить, что флаг N - это то, что они имеют в виду, когда говорят о предыдущей операции. Множества дополнений N = 0, множество вычетов N = 1. Таким образом, содержимое регистра A и флагов C, H и N определяет результат.
Эта инструкция предназначена для поддержки арифметики BCD, но имеет другие цели. Рассмотрим этот код:
and 15
add a,90h
daa
adc a,40h
daa
Он заканчивает преобразование нижних 4 бит регистра A в значения ASCII '0', '1',... '9', 'A', 'B',..., 'F'. Другими словами, двоичный шестнадцатеричный конвертер.
Ответ 3
Я нашел эту инструкцию довольно запутанной, но я нашел это описание ее поведения от z80-heaven, чтобы быть наиболее полезным.
Когда эта команда выполняется, регистр A записывается BCD с использованием содержимого флагов. Точный процесс заключается в следующем: если наименее значимые четыре бита A содержат цифру не BCD (то есть больше 9) или установлен флаг H, то в регистр добавляется $06. Затем проверяются четыре наиболее значимых бита. Если эта более значимая цифра также больше 9 или установлен флаг C, то добавляется $60.
Это обеспечивает простой шаблон для команды:
- если нижние 4 бита образуют число больше 9 или H, добавьте $06 к аккумулятору
- если верхние 4 бита образуют число больше 9 или C, добавьте $60 к аккумулятору
Кроме того, хотя DAA предназначен для запуска после сложения или вычитания, его можно запустить в любое время.
Ответ 4
Это производственный код, который правильно реализует DAA и проходит тестовые наборы кода операции zexall/zexdoc/z80test Z80.
Основано на документальном документе Z80 без документации, стр. 17-18.
void daa()
{
int t;
t=0;
// 4 T states
T(4);
if(flags.H || ((A & 0xF) > 9) )
t++;
if(flags.C || (A > 0x99) )
{
t += 2;
flags.C = 1;
}
// builds final H flag
if (flags.N && !flags.H)
flags.H=0;
else
{
if (flags.N && flags.H)
flags.H = (((A & 0x0F)) < 6);
else
flags.H = ((A & 0x0F) >= 0x0A);
}
switch(t)
{
case 1:
A += (flags.N)?0xFA:0x06; // -6:6
break;
case 2:
A += (flags.N)?0xA0:0x60; // -0x60:0x60
break;
case 3:
A += (flags.N)?0x9A:0x66; // -0x66:0x66
break;
}
flags.S = (A & BIT_7);
flags.Z = !A;
flags.P = parity(A);
flags.X = A & BIT_5;
flags.Y = A & BIT_3;
}
Для визуализации взаимодействий DAA в целях отладки я написал небольшую программу сборки Z80, которую можно запускать в реальном ZX Spectrum или в эмуляции, которая точно имитирует DAA: https://github.com/ruyrybeyro/daatable
Что и как ведет себя, получил таблицу флагов N, C, H и регистр A до и после DAA, созданного с помощью этой программы сборки: https://github.com/ruyrybeyro/daatable/blob/master/daaoutput.txt