Многие любители не испытывают серьезных трудностей в
овладении БЕЙСИКом. Для этого достаточно немного практики. Но рано или поздно
они приходят к барьеру «машинного кода». Как это ни печально, но некоторые так
перед ним и останавливаются. Это ни в коей мере не связано с отсутствием
желания или способностей, просто многие не знают, с чего начать. Если в БЕЙСИКе
можно начинать с чего угодно (при ошибке компьютер сам Вас поправит), то здесь
Вы оказываетесь с процессором один на один, и такой метод проб и ошибок не
срабатывает.
Одним словом, есть некий психологический барьер, который
бывает трудно преодолеть в одиночку. Известно, что для того, чтобы научиться
программировать, надо взять и начать программировать. «ИНФОРКОМ» предлагает Вам
следующий компромиссный подход - сначала в рамках этой главы мы, беря «быка за
рога», просто начнем программировать, а затем посвятим оставшуюся часть книги
систематическому изложению материала.
Итак, давайте напишем первую программу в машинном коде.
Прежде всего, выделим для нее область памяти. Если Вы читали нашу книгу
"Большие возможности Вашего «ZX-Spectrum`а», то знаете, что для БЕЙСИКа в
оперативной памяти компьютера отведена область памяти, начинающаяся с адреса,
на который указывает системная переменная PROG и заканчивается адресом, на
который указывает системная переменная RAMTOP. Предположим, что Вы хотите
записать программу в машинных кодах, начиная с адреса 30000. Дайте команду
CLEAR 29999. Эта команда установит RAMTOP в 29999 и Ваша программа будет
защищена от возможной порчи из БЕЙСИКа. Даже если Вы дадите команду NEW,
области памяти, находящиеся выше RAMTOP, не будут поражены.
Теперь дайте две прямые команды одну за другой:
POKE 30000,0
POKE 30001,201
Мы сейчас записали два числа в нужные нам адреса. Они
образуют простейшую программу. Выполнить ее можно командой RANDOMIZE USR 30000.
Попробуйте сами... Вам покажется, что ничего не произошло, но это не так.
Сначала процессор обратился по адресу 30000 и нашел там число 0, которое
обозначает машинный код операции NOP. Операция NOP (no operation
- нет операции) дает команду процессору, что ничего делать не надо. В течение
0,0000014 сек. он действительно ничего не делает, а затем переходит к
следующему адресу, где находит число 201.
Это команда RET (return -
возврат). Она дает процессору указание прекратить в этом месте программу в
машинных кодах и вернуться туда, откуда она вызывалась, т.е. в нашем случае - в
БЕЙСИК. Это самое процессор и сделал, о чем Вы получили сообщение БЕЙСИКа
«О.К.».
Если все, что Вы здесь прочитали, Вам понятно, то Вы уже
поняли, как составляются программы в машинных кодах. Можно, конечно, возразить,
что пользы от такой программы не очень много, но сейчас не в этом суть. Важно,
чтобы Вы поняли, что некая последовательность чисел может быть
последовательностью команд для процессора Z-80.
К сожалению, для нас мало, что говорит простая
последовательность чисел вроде таких, как 0 и 201. Держать в памяти коды всех
команд процессора (а их около семисот) непросто, но дело упрощается тем, что
есть промежуточный язык между процессором и человеком - язык Ассемблера. У
каждого кода есть своя мнемоника Ассемблера. Мнемоника - это набор букв,
являющихся сокращением английских слов. Для нашего примера программа на Ассемблере
выглядит так:
NOP
RET
Перевод этих мнемоник в машинные коды тоже можно поручить
компьютеру. Для этого существуют специальные программы, которые тоже называют Ассемблерами.
Есть и противоположные программы - Дизассемблеры. Они наоборот переводят
машинные коды в мнемоники Ассемблера.
И тех программ и других достаточно много. Часто они
объединяются в пакеты. Широко распространены пакеты GENS3/MONS3 фирмы HISOFT и EDITAS/MONITOR 16/48 фирмы PICTURESQUE. Здесь
GENS3 и EDITAS - Ассемблеры, а MONS3, MONITOR 16 и MONITOR
48 - Дизассемблеры.
Теперь давайте вернемся к нашей первой программе и
попробуем ее несколько развить, чтобы она все же что-то делала. Процессор Z-80
имеет несколько регистров, у которых есть имена – «А», «В», «С»
и т.д. Каждый из них может содержать одно какое-либо целое число от 0 до 255
(т.е. один байт).
Существуют десятки команд процессора, которые позволяют
копировать содержимое регистров из одного в другой, а также выполнять связь с
внешним миром, в т.ч. и с оперативной памятью.
Так, например, команда Ассемблера LD B,A (машинный
код - 71) означает «загрузить содержимое регистра А в регистр В».
LD - это сокращение от LOAD (загрузка).
Точно так же LD C,B (машинный код 72) означает «загрузить
в регистр С содержимое регистра В». Можно загружать в регистры и
целые числа. Например, LD A,n - означает
«загрузить в регистр А целое число n»,
где n может быть числом от 0 до 255. До этого
все команды были однобайтными. Эта же команда - двухбайтная. Сначала идет
машинный код - 62, а за ним само число - n. Так,
например, LD A, 77 (загрузить в регистр А число 77) будет
выглядеть так: 62,77. Здесь 62 - код операции, - он сообщает процессору, что
надо сделать, а 77 - это операнд. Заметим здесь же, что бывают операции и
трехбайтные и четырехбайтные. Первый байт, как правило, - код операции, а
следующие за ним - операнды. Мы говорим «как правило» потому, что есть
некоторые операции, код которых записывается двумя байтами [прим.1].
Итак, мы уже готовы к тому, чтобы написать программу,
которая будет перебрасывать какое-либо число из одного регистра процессора в
другой.
Ассемблер
|
Адрес
|
Машинный код
|
NOP
|
30000
|
0
|
LD A, 77
|
30001
|
62
|
|
30002
|
77
|
LD B, A
|
30003
|
71
|
LD C, B
|
30004
|
72
|
RET
|
30005
|
201
|
Гораздо интереснее было бы организовать обмен между
процессором и оперативной памятью компьютера. Команд, которые позволяют это
сделать, тоже очень много. Например, это команда LD (nn),A. Ее машинный код - 50. Она означает следующее: «Загрузить
в ячейку памяти, адрес которой задан двухбайтным числом nn,
содержимое регистра A». Эта команда - трехбайтная. Первым идет код
операции (50), а за ним - двухбайтный операнд nn,
который задает нужный Вам адрес. Например, если Вам нужен адрес 30008, то тогда
операнд nn будет иметь вид: 56, 117 потому, что 117*256
+ 56 = 30008.
Этот двухбайтный операнд имеет старшую часть и младшую.
Запомните, что двухбайтные числа (как правило, это адреса) хранятся в памяти
всегда в обратном порядке, т.е. сначала младший байт, а потом старший [прим.2].
Таким образом, команда Ассемблера LD (30008),A в
машинных кодах запишется так: 50, 56, 117.
Существует и противоположная команда LD A,(nn) – «Загрузить в регистр А содержимое ячейки
памяти, заданной адресом nn». Ее код - 58, за
которым следуют два байта, указывающие на адрес: сначала младший, а потом -
старший. Тогда LD A,(30007) запишется так: 58, 55, 117.
Теперь мы уже можем перемещать содержимое ячеек памяти из
одной в другую, копируя его через регистр А процессора.
До
операции
После
операции
Ассемблер
|
Адрес
|
Машинный код
|
LD A,(30007)
|
30000
|
58
|
|
30001
|
55
|
|
30002
|
117
|
LD (30008),A
|
30003
|
50
|
|
30004
|
56
|
|
30005
|
117
|
RET
|
30006
|
201
|
Можно еще несколько усложнить задачу и попытаться
выполнить сложение содержимого двух каких-либо ячеек памяти и отправить
результат на хранение в какую-либо ячейку. Для выполнения арифметических
действий, например сложения, процессор Z-80 также имеет несколько команд.
Рассмотрим команду ADD A,B (ее машинный код - 128). ADD - addition (сложение).
ADD A,B означает следующее: «Прибавить содержимое
регистра B процессора к содержимому регистра А и результат
оставить в регистре А».
Как видите, эта команда однобайтная, т.к. все указания на
то, что откуда взять, что с ним сделать и куда отправить результат, в ней есть
уже сами собой.
Предположим, что Вы хотите сложить содержимое ячеек 30013
и 30014, а результат поместить, а ячейку 30015. Тогда программа на Ассемблере и
в машинных кодах будет иметь вид:
Ассемблер
|
Адрес
|
Машинный код
|
Комментарий
|
LD A,(30013)
|
30000
|
58
|
Загрузить
в регистр А число, содержащееся в адресе 30013.
|
|
30001
|
61
|
|
30002
|
117
|
LD B,A
|
30003
|
71
|
Загрузить
в регистр В содержимое регистра А
|
LD A,(30014)
|
30004
|
58
|
Загрузить
в регистр А число, содержащееся в адресе 30014.
|
|
30005
|
62
|
|
30006
|
117
|
ADD A,B
|
30007
|
128
|
Прибавить
к содержимому А содержимое регистра В.
|
LD (30015),A
|
30008
|
50
|
Выгрузить
содержимое регистра А в адрес 30015.
|
|
30009
|
63
|
|
30010
|
117
|
NOP
|
30011
|
0
|
Нет
операции. Пауза.
|
RET
|
30012
|
201
|
Возврат
туда, откуда эта программа вызывалась.
|
Попробуйте эту программу в работе. Пусть Вы хотите
сложить два числа, скажем 50 и 70. Сначала выделим память для этой программы в
машинных кодах.
10 CLEAR 29999
Теперь введем эту программу в память, начиная с адреса 30000.
20 FOR i=30000 TO 30012: READ q: POKE
i,q: NEXT i
30 DATA 58, 61, 117, 71, 58, 62, 117, 128, 50, 63, 117,
0, 201
Запишем в ячейки 30013 и 30014 те два числа, которые мы желаем
сложить:
40 POKE 30013, 50: POKE 30014, 70
Введем команду на исполнение нашей программы в
машинных кодах.
50 RANDOMIZE USR 30000
И, наконец, стартуем нашу БЕЙСИК-программу - RUN.
Через долю секунды она отработает и на экране появится
сообщение О.К.
Вроде бы ничего не изменилось, но если Вы проверите, что
находится в ячейке 30015, то убедитесь, что там находится результат сложения,
т.е. число 120.
PRINT PEEK 30015 даст Вам: 120 О.К.
Итак, мы уже познакомились с несколькими командами
процессора Z-80. Теперь нам придется прервать такое популярное изложение,
поскольку система команд этого процессора довольно обширна и при таком подходе
пришлось бы отвести ей около тысячи страниц. Мы надеемся, что психологический
барьер Вы уже преодолели и далее мы переходим к систематическому и постепенному
освоению системы команд процессора.
Вы, по-видимому, поняли, что всякой команде в машинных
кодах соответствует своя мнемоника Ассемблера, в которой в нескольких буквах
зашифрована суть операции. К сожалению, обычно литературу пишут профессионалы
для профессионалов и они не утруждают себя переводом мнемоник на русский язык,
тем более, что по Z-80 книг на русском языке почти нет (есть несколько ведомственных
переводов). Поэтому «Инфорком» в своем «Справочнике…» (часть 3) уделил место
словарю для перевода мнемоник Ассемблера в нормальный английский, а через него
и в русский язык.
В заключение этой главы несколько общих замечаний.
Различные процессоры имеют различные системы команд. Знание системы команд
процессора Z-80 еще не означает, что Вы сможете разбираться в машинном коде
других компьютеров, собранных на других процессорах, но совершенно точно, что
их освоение пройдет у Вас в десятки раз быстрее.
Даже для одного только процессора Z-80 мнемоники до сих
пор не стандартизированы. Многие ассемблирующие программы, например, EDITAS, ZEUS, GENS1…GENS3 и др. имеют отклонения в форме записи мнемоник, но они
незначительны и всегда оговариваются в сопроводительной инструкции к программе.