#math #bcd #z80
#математика #bcd #z80
Вопрос:
Прошу прощения за этот, казалось бы, незначительный вопрос, но, похоже, я нигде не могу найти ответа — я как раз подхожу к реализации инструкции 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 |
|------------------------------------------------------------------------------|
Должен сказать, я никогда не видел спецификации инструкции daafter. Если вы внимательно изучите таблицу, то увидите, что действие инструкции зависит только от флагов C
и H
и значения в накопителе — оно вообще не зависит от предыдущей инструкции. Кроме того, в ней не раскрывается, что произойдет, если, например, C=0
, H=1
и младшая цифра в сумматоре равна 4 или 5. Поэтому в таких случаях вам придется выполнить NOP
, или сгенерировать сообщение об ошибке, или что-то в этом роде.
Комментарии:
1. Большое спасибо — я надеюсь найти не слишком много более неоднозначных инструкций, подобных этой 🙂
2. DAA Z80 должен быть эквивалентен DAA и DAS для x86, поскольку они имеют одинаковое назначение. Ознакомьтесь с описаниями обоих для x86. Какой-то DAA доступен на многих процессорах.
3. @Alex: чипы x86 имеют две инструкции настройки десятичной дроби: DAA (настройка десятичной дроби после сложения) и DAS (настройка десятичной дроби после вычитания). Инструкция Z80 DAA объединяет их в одну, предполагая, что операнды самого последнего сложения / вычитания были действительными числами BCD.
4. Имейте в виду, что 8080 DAA отличается от Z80 тонкими, но важными особенностями (о которых я мало знаю).
5. Таблицу можно значительно улучшить, если заменить ADD, ADC, INC на N = 0, а SUB, SBC, DEC, NEG на N = 1. DAA не имеет представления, какая инструкция была выполнена последней.
Ответ №2:
Просто хотел добавить, что флаг N — это то, что они имеют в виду, когда говорят о предыдущей операции. Набор сложений N = 0, набор вычитаний N = 1. Таким образом, содержимое регистра A и флагов C, H и N определяют результат.
Инструкция предназначена для поддержки BCD-арифметики, но имеет и другие применения. Рассмотрим этот код:
and 15
add a,90h
daa
adc a,40h
daa
Завершается преобразование младших 4 бит регистра в значения 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 предназначен для запуска после сложения или вычитания, его можно запустить в любое время.
Комментарии:
1. Похоже, что здесь отсутствуют последние четыре операции (SUB, SBC, NEG и DEC).
2. @Salgat То же правило применяется, когда
N=1
. Единственное, что вам нужно вычесть поправку, когдаN=1
.
Ответ №4:
Это рабочий код, правильно реализующий DAA и проходящий тесты zexall / zexdoc / z80test с кодом операции Z80.
Основана на недокументированном Z80 Documented, pag 17-18.
void daa()
{
int t;
t=0;
// 4 T states
T(4);
if(flags.H || ((A amp; 0xF) > 9) )
t ;
if(flags.C || (A > 0x99) )
{
t = 2;
flags.C = 1;
}
// builds final H flag
if (flags.N amp;amp; !flags.H)
flags.H=0;
else
{
if (flags.N amp;amp; flags.H)
flags.H = (((A amp; 0x0F)) < 6);
else
flags.H = ((A amp; 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 amp; BIT_7);
flags.Z = !A;
flags.P = parity(A);
flags.X = A amp; BIT_5;
flags.Y = A amp; BIT_3;
}
Для визуализации взаимодействий DAA, в целях отладки, я написал небольшую программу сборки Z80, которая может быть запущена в реальном ZX Spectrum или в эмуляции, которая точно эмулирует DAA: https://github.com/ruyrybeyro/daatable
Что касается ее поведения, то я получил таблицу флагов N, C, H и register A до и после DAA, созданную с помощью вышеупомянутой программы сборки: https://github.com/ruyrybeyro/daatable/blob/master/daaoutput.txt