7. ОБЗОР ТИПИЧНЫХ ОШИБОК, ВОЗНИКАЮЩИХ ПРИ ПРОГРАММИРОВАНИИ В МАШИННЫХ КОДАХ.
Классификация ошибок.
становка операндов. авильное использование флагов.
ница в использовании регистров и регистровых пар. ница в работе с адресами и с данными. авильная обработка массивов. ет косвенных эффектов.
авильное задание начальных условий.
ки, связанные с неправильной организацией программ. 7.1. Перестановка операндов.
Здесь наиболее характерны три типа ошибок. Чтобы избежать их, надо твердо помнить три правила.
1. В командах копирования регистров, например LD D,reg содержимое регистра REG копируется в регистр D, а не наоборот. Это одна из самых частых ошибок при программировании в машинных кодах.
2. Часто забывают, что 16-битные данные хранятся в памяти в следующем порядке: сначала младший байт, а потом старший. Особенно часто ошибка возникает в операциях загрузки регистров и в операциях между стеком и регистровыми парами.
3. Команда сравнения CP во время работы вычитает свой операнд из аккумулятора, а не наоборот. Флаги выставляются так, как будто было выполнено вычитание A-N.
7.2. Неправильное использование флагов.
1. Команды загрузки регистров LD не влияют на флаги. Чтобы установить флаги для последующей проверки, надо применять операции AND, DEC, INC, OR, XOR.
2. Часто путают состояние флага нуля (Z). Если после операции сравнения операнды оказываются равными, т.е. разность между ними равна нулю, то флаг включается, т. е. устанавливается
1. Возникает мнемонический конфуз "если результат операции -ноль, то флаг нуля - не ноль". Это приводит к неправильному применению JR NZ, N и JR Z,N.
3. В операциях сравнения CP флаг С указывает, какой операнд больше. Если аккумулятор больше, то флаг выключен, если больше операнд, то включен, но если они равны, то флаг выключается. Получается, что по этому флагу проверяется альтернатива для аккумулятора: БОЛЬШЕ ИЛИ РАВЕН/МЕНЬШЕ.
А если Вам надо проверить альтернативу БОЛЬШЕ/МЕНЬШЕ ИЛИ РАВЕН? Тогда перед операцией сравнения Вы должны добавить единицу к операнду или вычесть единицу из аккумулятора.
4. Программисты хорошо помнят, что в арифметических операциях с 16-разрядными регистрами DEC и INC не влияют на флаги вообще, а ADD влияет только на флаг переноса, но забывают, что ADC и SBC влияют на все флаги.
7.3. Путаница с регистрами и регистровыми парами.
Здесь ошибки возникают, когда пытаются применить для 8-разрядных операндов команды, предназначенные для 16-разрядных и наоборот. К счастью, ошибки этой группы возникают только при программировании в ассемблирующей программе, а она при компиляции сама найдет эти ошибки и укажет на них. Тем не менее, надо помнить несколько простых правил и руководствоваться ими:
ADC, ADD, DEC, INC, LD, SBC могут использоваться как с 8-битными операндами, так и с 16-битными регистровыми парами, причем ADD, DEC, INC и LD могут также применяться и в работе с индексными регистрами.
AND, OR, SUB, XOR применимы только к 8-битным операндам.
EX, POP, PUSH - применимы только к 16-битным операндам.
Часто пользуются косвенной адресацией через регистровую пару HL. Например, LD A, (HL) ; LD B, (HL) ... и т.п. Но иногда делают и вполне законную, но не очень корректную операцию LD L, (HL), после которой нарушается содержимое HL. То же относится и к операции LD H, (HL) .
Это самая распространенная ошибка при программировании в машинных кодах или на языке АССЕМБЛЕРа. Так, LD при выполнении копирования данных из регистра в память или из памяти в регистр всегда требует указания адреса. Этот адрес должен стоять в скобках. Короче говоря, все, что не стоит в скобках, АССЕМБЛЕР воспринимает как данные, а все, что стоит в скобках - как адреса. Если Вы забудете поставить скобки, то результат может быть совершенно непредсказуемым.
Часто возникает следующая ошибка. Допустим, Вы имеете набор процедур для выполнения каких-либо операций. Допустим, где-то в памяти Вами организована таблица адресов входа в эти процедуры. Правильная последовательность действий при этом такова: взять адрес из таблицы, поместив, допустим в HL, - здесь он фигурирует как данные; а далее стартовать процедуру с того адреса, на который указывает HL - здесь он уже выступает как адрес. На практике же часто пытаются стартовать процедуру переходом в таблицу, где содержится настоящий адрес старта. Это адрес старта, но это не место старта. Место старта находится там, куда указывает адрес старта.
Запомните также простое правило: DJNZ, JP, JR и CALL всегда требуют после себя указания адреса (или величины смещения, что в принципе одно и то же) .
7.5. Неправильная обработка массивов.
Наиболее типичными ошибками здесь являются:
- выполнение лишнего цикла вычислений;
- недовыполнение цикла вычислений.
Обратите серьезное внимание на широкоизвестный факт, что если массив начинается с адреса BASE и кончается в адресе BASE+N, то этот массив имеет N+1 элемент. В этом и кроется основная причина ошибок. Забывают использовать либо первый, либо последний элемент. С другой стороны, если Ваш массив имеет N элементов, то он должен закончиться в адресе BASE+N-1. Часто же продолжают вычисления до адреса BASE+N, т.е. "прихватывают" лишний элемент.
Вот список косвенных эффектов, которые необходимо учитывать при программировании. Невнимание к этим особенностям приводит к ошибкам.
1. Все логические операции кроме CPL сбрасывают флаг
CARRY.
2. В операциях LD rp, (ADDR) ; LD (ADDR) , rp; LD xy, (ADDR) ; LD (ADDR) , xy участвует не только указанный адрес, но и адрес, следующий за ним.
3. Операции POP, PUSH, CALL, RET, RET, RETN, RST влияют на указатель стека.
4. Операции CALL и RST засылают адрес на стек.
5. Операция DJNZ относится не к аккумулятору, как большинство других операций, имя регистра в которых не указывается, а к регистру B.
6. Команды блочного поиска, сравнения, перемещения байтов оказывают косвенное влияние на содержимое регистровых пар BC, DE, HL.
7. В командах LDD, LDI, CPD, CPDR, CPI, CPIR флаг P/O используется не по назначению, а служит для указания на то, что счетчик байтов в BC уменьшился до нуля.
8. Команды ротации в BCD-арифметике RRD, RLD вовлекают в ротацию содержимое регистра HL.
7.7. Неправильное задание начальных условий.
1. Не думайте, что если Вы не используете какие-либо ячейки памяти, то там должен быть 0. В начале работы программы, при инициализации желательно выставлять в рабочей области ОЗУ начальные значения.
2. При начале работы программы установите в регистрах и во флагах начальные значения, чтобы избежать неопределенностей.
3. Прежде, чем давать команду косвенной адресации, потрудитесь выставить адрес в регистре, через который производится адресация. Не полагайтесь на то, что он уже давно там есть.
7.8. Ошибки, связанные с неправильной организацией программ.
1. Иногда программисты забывают сохранить результат. Выполнив расчеты и получив в итоге результат в аккумуляторе, тут же засылают в аккумулятор новые данные для других вычислений.
2. Внимательно смотрите за всеми ветвлениями в программе. Бывает, что управление передается процедурам инициализации программы, хотя это вовсе и не нужно.
3. В сложных программах нередко оказывается запутанным порядок исполнения процедур. Если Вы плохо разбираетесь в структуре собственной программы, может произойти исполнение блоков, которые исполнять не надо. Не забывайте ставить вокруг них обходные пути.