Работа со стеком
Как вы знаете, действительные преимущества команд PUSH и pop состоят в том, что нам не
приходится думать об адресах чисел, над которыми эти команды выполняются..
Вы, безусловно, согласитесь, что не всегда имеет смысл, чтобы одна и та же область па-
мяти служила стеком независимо от того, будет у вас 16к или 48к на самом деле ЦП отслежи-
вает адрес стека с помощью 'указателя стека", который можно считать 16-битовым регистром.
Мы кратко упомянули об этом, когда рассматривали регистры, но ничего не говорили об этсм
при рассмотрении команд LOAD и тому подобных, поскольку это не такой регистр, которьг
можно обрабатывать так же, как остальные регистоы.
Основное, что желательно уметь делать с указателем стека, это определять его положение
в памяти, и именно такой тип команды имеется:
LD SP, NN
LD SP, (NN)
LD SP, IX
LD SP, IY вы можете проверить стек "спектрум" с помощью команды "MEM"
программы "редактора машинного языка в коде EZ", просматривая последние 30 - 40 бэйтоз
перед RAMT0P.
Не изменяйте содержимое ячеек стека!
Почти любое изменение приведет к остановке "спектрум" экоан погаснет и вам придется
вновь включать питание. Так происходит потому, что операционная система помещает много
информации, необходимой ей, в стек, и изменения приводят к тому, что она идет вразнос.
По этой же самой причине не пытайтесь менять положение указателя стека, если только у
вас нет полной уверенности в том, что вы делаете.
Замечание:
В правильно организованной программе количество команд pop и PUSH должно совпадав,
независимо от того, по какой ветви шла программа. Любые ошибки в подсчете могут привести
к странным результатам.
Арифметические команды для 16-разрядных чисел
MNEMONIC |
BYTES |
TIME TAKEN |
EFFECT ON FLAGS |
С |
Z |
PV |
S |
N |
1 |
ADD HL.REG PAIR |
1 |
11 |
it |
- |
- |
- |
0 |
? |
ADD HL,SP |
2 |
11 |
# |
- |
- |
- |
0 |
|
ADC HL REG PAIR |
2 |
15 |
it |
# |
tt |
i |
0 |
|
ADC IX,SP |
2 |
15 |
й |
it |
it |
it |
0 |
|
ADD IX,BC OR DE |
2 |
15 |
tt |
- |
- |
- |
0 |
|
ADD IX,IX |
2 |
15 |
й |
- |
- |
- |
0 |
|
ADD IX SP |
2 |
15 |
it |
- |
- |
- |
0 |
|
ADD IY.bc OR DE |
2 |
15 |
it |
- |
- |
- |
0 |
|
ADD IY IV |
2 |
15 |
* |
- |
- |
- |
0 |
|
ADD IY.SP |
2 |
15 I |
1 * |
- |
- |
- |
0 |
|
SBC HL,PEG PAIR SBC HL, SP |
2
2 |
I 15
II 15 |
1 i
1 й |
i * |
i ii |
it ii |
1 1 |
|
MNEMONIC -мнемоническое обозначение; BYTES -байты; TIME TaKEN -время выполнения;
EFFECT ON FLAGS -воздействие на флаги; REG PAIR - пара регистров; OR - или;
обозначение флагов
8 означает, что флаг изменяется в результате операции;
0 означает, что олаг сбрасьвается;
1 означает, что олаг устанавливается
означает, что флаг не изменяется;
? Означает, что результат неизвестен.
Арифметические действия
Одно из преимуществ возможности 16-битового представления чисел в 3-битовом процессоре
состоит в том, что мы можем применять 16 битов для задания адреса и для вьголнения вы-
числений над цельыи числами до 65 355 (или в диапазоне от -327Б 8 до о2767, если допуска-
ются отрицательные числа).
В свете выиесказанного легко понять, почему в некоторых ранних мсделях микро-ЗВМ, та-
ких как первоначальная модель "синклер-2ХЗЗ , все арифметические действия в язьке
"бейсик" ограничивались целыми числами из диапазона -32 002! до +32 000.
Привилегированная пара регистров
Совершенно так же. как регистр "а" является привилегированным с точки зрения арифмети-
ческих действий для 8-разр. Чисел, есть и привилегированна? пара регистроз для арифмети-
ческих действий над числами для 16-разр. Чисел, и это - пара регистров HL
Зта привилегированность не столь явно выоахеча, как в случае 8-битовых чисел, так чт:
мы не опускаем имя пары регистров.
Сложение
операции сложения вполне прямолинейны:
ADD HL, вс
ADD HL DE
ADD HL, HL
ADD HL,•SP но именно так они и записываются!
Обратите внимание, что не удается сложить абсолютное число с HL, - например не до-
пускается "ADD HL, NN". Чтобы вьлолнить вычисления такого типа, нам нужно:
LD DE, NN
ADD НС, DE
Когда вы учтете, что теперь у вас связанными оказались четыре из 8-битовых регистра,
которых всего 7, вы сразу поймете, что вы не захотите делать это слишком часто.
Обратите также внимание, что между HL и индексными регистрами нельзя выполнить сложе-
ние. Вы также вспомните, что нет команды LOAD, которая позволяла бы вам передавать содер-
жимое IX или IY в вс или DE, так что единственным способом выполнения такого сложения зь-
ло бы:
PUSH IX
pop DE
ADD HL, DE адин из моментов, о котором следует упомянуть -это регист
"SP" - указатель стека. Зто-одна из очень немногих операций, в которых "SP" обрабатыва-
ется как настоящий регистр, но, очевидно, вы не можете применять его в качестве перемен-
ной! Подумайте, что случилось бы со всеми командами pop и PUSH, если бы вы произвольным
образом меняли содержимое указателя стека!
Воздействие на флаги
16-битовые арифметические операции - это именно та область, в которой флаг переноса
играет присущую ему роль, поскольку, как можно видеть из таблицы, помещенной в начале
этой главы, единственный другой флаг, на который оказьвает влияние команда "ADD" (сложе-
ние) - это флаг "вычитания (и все что он в этом случае означает - это то, что команде
сложения не является вычитанием.
Флаг переноса будет установлен, - если имеется переполнение в старшем бите "н", - любое
переполнение в "L" автоматически помещается в результате вычисления в "н".
Сложение с переносом
Из-за ограниченного характера работы с 16-битовыми числами мы можем выполнять сложение
цепочкой точно так же, как и в 8-битовом случае. Команда "сложение с переносом" - ее мне-
моническое обозначение "ADC" (ADD WITH CARPY) - действует аналогично команде "ADD" и име-
ет тот жедиапазон пар регистров:
ADC HL! DE
ADC HL, HL
ADC HL! SP
16-битовое вычитание
16-битовое вычитание - тоже достаточно простая операция, но вычитания без переноса не
бывает: если вы не уверены в состоянии флага переноса, удостовертесь, что в ваше/'прог-
рамме имеется строка, очищающая флаг переноса перед каждой операцией вычитания
SBC HL, вс
SBC HL DE
SBC HL, HL
SBC HL, SP
Воздействие арифметических операций с пеоеноссм на флаги
Вы, возможно, заметили, что три других флзгз изменяются в результате команд "сложен
с переносом" и вычитание с переносом", хотр они не изменялись в результате простьх ус-
манд 16-битового сложения.
Зти флаги - флаг нуля, флаг знака и флаг переполнения. Каждый из них устанавливается з
соответствии с результатом операции.
Арифметические операции с индексным регистром
Индексные регистры ограничиваются только операцией сложения без переноса!
Более того, диапазон регистров, которые можно складывать с индексными регистрами,
чрезвычайно ограничен:
сложение с парой регистров "вс" или "DE";
сложение индексного регистра с самим собой;
сложение с указателем стека.
Упражнение на решение задачи о свободной памяти
Конец пространства памяти, используемого программой, определяется содержимым ячейки
памяти STKEND. В руководстве по "спектрум" она определяется адресами 23653 и 23564.
Очевидно, если мы загрузим содержимое этой ячеики в HL, то задача наполовину будет ре-
шена:
LD HL, (STKEND) затем нужно вычесть "указатель стека" (SBC HL, SP?).
Из-за "переноса" нам необходимо очистить флаг переноса. Наиболее легко это достигается
с помощью команды "AND а", рассмотренной в этой книге раньше.
AND а
SBC HL, SP
Можете считать задачу решенной на три четверти, если вы знали, что нужно учесть пере-
нос, но не знали, как это сделать. Если вы вовсе забыли о переносе, задача решена на чет-
верть.
Поскольку, указатель стека задает положение в памяти выше, чем наибольший адрес ватей
программы (иначе у вас возникли оы серьезные трудности), результат будет отрицательны^.
Теперь давайте получим количество байтов, оставшихся свободными, з виде положительного
числа с помощью регистра "вс" (точно так же -для этого Родосе'. бы и регисто "DE"). Сиэтле
мы хотим сдвинуть HL и вс, но нет команды "загрузить" для такого действия нам придем
использовать последовательно команды PUSh и оор:
PUSH HL
pop вс
в HL по-прежнему находится та же информация, так что HL=bc.
' Чтобы получить HL = -вс, нужно вычесть вс из HL дважды (но не забудьте, что флаг пере-
носа только что был установлен командой вычитания, так что его необходимо внсвь
очистить):
AND а
SBC HL, вс
SBC HL, вс
в HL теперь содержится отрицательное значение, по модулю равное прежнему, т. Е. Положи-
тельное число оставшихся свободными байтов.
Теперь нам нужно получить это число снова в паре регистров вс, чтобы получить резуль-
тат из функции USP". Чтобы получить HL назад в вс:
PUSH HL
pop BS
и, наконец, возврат из функции USP:
Правильно ли вы написали программу? Обратили ли вы внимание, как удобно пользоваться
стеком!
Циклы И ПСрСХОАЫ
В "бейсике" вы знаете команду "GO то", передающую управление вашей программой командам
в той строчке, на которую указывает "GO то".
Нет ничего проще, чем реализовать это на машинном языке, просто задайте ячейку памяти,
в которой вы хотели бы, чтобы ЦП обнаружил следующую команду, и задача наполовину решена.
Наиболее простой вариант - команда "перейти на' (JUMP то)
JP xx xx
JP (HL)
JP IX
JP (IY)
Можно сделать так, чтобы одна из этих команд зависела от состояния одного из флагов,
например флага переноса. Зто команда условного перехода имеет вид:
JP сс, NN
где сс- условие, выполнение которого проверяется. Если бы у нас было, напримео,
JP Z, 0000
то это читалось бы [переход по адресу 0000, если флаг нуля установлен". (Зто - адоес, по
которому "спектрум" осуществляет переход при включении питания, и з таком виде команда
"JP" на ноль может применяться в программе на машинном языке, если вы захотели очисчгь
всю память и начать заново с помощью "к")
Теперь обратите внимание, что ЦП не допускает никаких ошибок если вь< говорите W,
он сделает переход. Поскольку почти любой код может быть воспринят как команда, !!" не
принимает во внимание, что вы могли сделать переход в середину массива данных или во го-
рой байт двухбайтовой команды: он будет считывать байт по найденному адресу и считэть,
что это - начало следующей команды.
Способ, которым ЦП обрабатывает команду перехода на самом деле совершенно прост: у ^о-
го есть небольшой счетчик, называемый счетчиком команд, говорящий ему, где искать следую-
щую вьголняемую команду. При нормальном ходе программирования (т.е. без переходов/ iln
проверяет подлежащую выполнению команду и добавляет < счетчику команд столько, сколько
байтов содержит команда.
Так, если он встречает 2-байтовую команду, он добавляет 2, тогда как 4-байтовая коман-
да заставит его добавить 4 к счетчику команд.
Когда он встречает команду "перехода", он просто замещает содержимое счетчика команд
тем значением, которое вы указали.
Именно поэтому вы не можете допустить проникновения в программу каких-либо ошибок.
Длинные переходы и короткие переходы
Мы можем считать приведенные выше команды эквивалентом длинного перехода в машинное
языке, поскольку 16-битовый адрес позволяет нам осуществить переход в любое место, куда
позволяет перейти чип Z80.
Недостатки длинного перехода:
а) часто нам нет необходимости в таком длинном переходе, но нам все-таки приходится при-
менять 3-байтовую команду.
б) мы не можем без затруднений переместить программу в другую часть памяти, потому что
мы задаем абсолютный адрес. Именно для преодоления этих двух недостатков был введен "ко-
роткий переход". Он называется "относительным переходом" и позволяет нам совершать пере-
ход от текущей позиции на +127 или -128 байтов, т.е. расстояние перехода можно задать в
одном байте!
Команда относительного перехода
JP д, где д - относительное смещение.
Мы можем также сделать относительный переход в зависимости от некоторого условия, нап-
ример, от того, установлен ли флаг переноса или флаг нуля. Зти условные переходы записы-
ваются следующим образом:
JP cc, д, где cc - проверяемое условие.
Значение смещения д добавляется к счетчику команд.
Зто означает, что берется текущее значение счетчика команд и к нему прибавляется за-
данное вами относительное значение. Вы можете задавать либо положительное (переход впе-
ред), либо отрицательное (переход назад) значение. Если вы посмотрите, б предшествующей
главе об отрицательных числах, то вы поймете, что зто означает ограничение относительных
переходов, диапазоном от -128 до +127.
Обратите внимание, что когда ЦП выполняет команду относительного перехода, счетчик ко-
манд уже указывает на следующую команду, которая выполнялась бы, если условие не удове~-
ворялось.
Так происходит потому, что когда ЦП встречает "JP", он знает, что имеет дело с 2-бай-
товой командой, и добавляет 2 к счетчику команд - поэтому счетчик команд указывает ча ко-
, следующую за относительным переходом!
ример. В такой программе, как
ячейка текст
32000 ADD а, в
32001 JP Z, 02н
32003 LD в, 0
32005 NEXT LD HL, 4000H
Ниже показано, как ЦП работает с этой программой, если игнорируется команда перехода,
расположенная по адресу 32001 (т.е. при сброшенном флаге нуля):
Загрузить байт по адресу 32000 поскольку в этом байте содержится команда, состоящая
только из одного байта, счетчик команд нужно задать равным 32001.
Выполнить команду.
Загрузить байт, заданный счетчиком команд (32001). Зтот байт будет являться частью
2-байтовой команды, так что нужно прибавить 2 к счетчику команд и сделать его оэвчым
Получить следующий байт, чтобы сделать команду полной.
Выполнить команду.
Загрузить байт, заданный счетчиком команд (32003). Зтот байт является частью 2-байто-
вой команды, так что нужно прибавить к счетчику команд 2 (теперь он становится разным
32005)
Получить следующий байт, чтобы сделать команду полной.
Выполнить команду.
В ячейке 32001 программа встречает команду относительного перехода. Если флаг нулг не
установлен, как в приведенном выше примере, то ЦП ничего не выполняет.. В общем случае iir
выполняет команду перехода следующим образом:
если флаг нуля установлен, прибавить 2 к счетчику команд
(это составит 32005),
если флаг нуля сброшен, не нужно делать ничего (счетчик команд остается равным 32003).
Иными словами, относительный переход позволяет нам в определенных случаях перескаки-
вать через команду "LD в, 0".
Зто также объясняет, почему для этой команды указаны два времени выполнения. Подсчет
нового значения счетчика команд занимает больше времени, чем невыполнение никаких
действий.
Поэтому ЦП выполнит либо команду, расположенную по адресу 32003, либо - по адресу
32005, в зависимости от значения флага нуля.
Как мы уже говорили, можно также сделать относительный пере ход с отрицательным значе-
нием.
Циклы ожидания
В программах на машинном языке бывают ситуации, когда события происходят с такой боль-
шой скоростью, что необходимо ввести некоторое ожидание.
Примеры, сразу приходящие на ум - посылка информации на кассету магнитофона (сигналы
должны быть расположены достаточно далеко друг от друга, чтобы их потом можно было прочи-
тать) или посылка информации на печатающую машинку ( представьте себе только печать со
скоростью нескольких тысяч знаков в секунду).
Поэтому полезно установить циклы ожидания с помощью команды DJNZ:
LD в, COUNT
I4AIT DJNZ МАИ
COUNT - счетчик; MftIT - ожидание
Команда "DJNZ MftIT" заставит ЦП возвратиться к команде DJNZ столько раз, сколько нуж-
но, чтобы регистр "в" вновь обнулился, прежде чем обработка пойдет дальм.
Это должно вам дать ответ на наае упражнение, в котором спрашивалось, что произойдет,
если вы напиаете
WAIT JR ДО1Т
вам придется довольно долго ждать, пока ЦП выйдет из этого цикла!
Команды группы вызова и возврата
MNEMONIC |
BYTES |
TIME |
EFFEKT ON FLAGS |
TAKEN |
С |
Z |
PV |
s |
N |
H |
CALL ADORES CALL сс,ADDRESS RET RET cc |
3 3 1 1 |
17 10/17 10
5/11 |
|
|
|
|
|
|
MNEMONIC -мнемоническое обозначение; BYTES -количество байтов; TI№. TAKEN - время вы-
полнения; EFFEST ON FLAGS - воздействие на флаги; ADDRESS - адрес.
Замечание: сс - условие, которое должно выполняться для истощима команды. Ниже при-
водятся используемые условия:
флаг сокращение значение
перенос с установка флага переноса (=1)
NC сброс флага переноса =0)
ноль Z установка флага нуля (=1)
NZ сброс флага нуля (=0)
четность ре флаг четности четный (=1)
ро флаг четности нечетный (=0)
знак м знак минус (=1)
р знак положительный (=0)
Воздействие на флаги: обратите внимание, что ни один из флагов не изменяется при ко-
мандах вызова и возврата.
Время вьлолнения: если указаны два значения времени, то более короткое относится . к
случаю, когда условие не вьголняется.
Применение поАпрограмм в ваших программах на
машинном языке
Применение подпрограмм в машинном язьке столь же просто, как в обычных программах на
языке "бейсик", если не проще.
На самом деле, вы помните, что применяя функцию "USR" в своей программе на языке
"бейсик", вы в действительности вызываете подпрограмму, и, как вы помните, для завершения
нам нужна команда "RETURN"!
Поэтому вам очень просто проверить определенные подпрограммы независимо, от вашей
основной программы на машинном языке.
Основное различие, с которым вам придется столкнуться при реализации подпрограмм в ва-
ших программах на машинном языке, состоит в том, что вам нужно знать адрес начала подп-
рограммы.
Зто может вызвать трудности, если вы храните программы на машинном язьке в переменном
массиве, поскольку адрес этой переменной не обязательно фиксирован. Зто также означает,
что программа на манинном языке не может быть легко перемещена в новую позицию памяти,
если в ней используются подпрограммы.
Подпрограммы также можно вызывать в зависимости от условий. Зто - эквивалент на мами-
ном язьке следуюшего предложения языка "бейсик":
IF (CONDITION) THEN GOSUB (LINE) CONDITION - условие; LINE - строка.
Находясь в подпрограмме, нужно проявлять осторожность, чтобы не изменить какие-либо
флаги или регистры, требующиеся для дальнейших сравнений. Зто необх9димо для того, чтобы
вы не ушли на ветвь алгоритма снова по следующему предложению "CALL "после возврата ту-
да, откуда вьили".
Различие состоит в том, что единственными допустимыми условиями являются состояния че-
тырех флагов:
флаг переноса;
лаг нуля;
лаг четности (а также флаг переполнения)
лаг знака.
напомним, что все эти {лаги устанавливаются в соответствии с последней командой, пов-
лиявшей на этот конкретный флаг.
Поэтому хороший стиль программирования предполагает помещение команды CALL или
"RETURN" непосредственно после команды, устанавливающей флаг.
Например,
LD а, №Ш)
ср 1
CALL Z, ONE
ср 2
CALL Z, TWO
ср 3
CALL Z. THRE
NUMJER - число; 0ЙЕ - один; TWO - два; THREE - три.
Приведенная выше программа позволяет вам переходить к различным программам, в зависи-
мости от значения, хранимого в ячейке "NUMBER" (число), но обратите внимание, что она
предполагает, что подпрограммы не изменяют значение в регистре а!!
Можно применить и более короткую программу, если вам известно, что для хранимого в
"NUMJER" значения имеются только три заданные выше возможности:
LD а, №Ш)
ср 2
CALL Z, TWO: а = 2
CALL с, ONE: а (2 =) а =1
CALL ТЙРЕЕ: а) 2 =) а =3
NUMJER -число; TWO - два; ONE - один; THREE - три.
Так происходит потому, что команда "ср 2" устанавливает и флаг нуля, и флаг переноса,
а команда вызова не влияют на какие оы то ни было флаги.
Аналогичным образом, очень полезным оказывается условный возврат из подпрограммы. (Но
это не считается хорошим стилем программирования).
Команды группы сравнения и перемещения блоков
MNEMONIC |
TIME
BYTES
TAKEN |
I EFFECT ON FLAGS |
С |
Z |
PV |
s |
N |
H |
LDI LDD |
2 16 2 J 16 |
- |
- |
I |
- |
0 0 |
0 0 |
LDIR I 2 I 21/16 LDDR 1 2 I 21/16 |
- |
- |
0 0 |
- |
0
' 0 |
0 0 |
I CPI |
2 |
16 |
# |
# |
II |
1 |
» |
CPD |
2 |
1 16 1 " |
|
|
» |
1 |
II |
CPIR |
2 |
21/16 - |
It |
# |
» |
1 |
К |
CPDR |
2 |
1 21/16 1 - |
# |
II |
II |
1 |
"1 |
MNEMONIC -мнемоническое обозначение; BYTES -количество байтов; TIME TAKEN -время вы-
полнения; EFFECT ON FLAGS-воздействие на флаги.
Обозначение флагов:
It показывает, что флаг изменяется в результате операции;
0 показывает, что олаг сбрасывается;
1 показывает, что олаг устанавливается;
- показывает, что флаг не меняется.
Время выполнения:
для команд повторения указанное время относится к каждому циклу. Более короткое значение
относится к прерыванию команды, например, для CPIR либо вс=0, либо а= (HL).