ретро
Итак, мы начинаем новую рубрику "РЕТРО" В нее мы будем включать лучшие материалы из выпусков ZX РЕВЮ и других изданий прошлых лет, тиражи которых уже закончились и полное переиздание которых нецелесообразно в силу как экономических, так и чисто творческих причин.
Основными критериями отбора материалов для новой рубрики, кроме качества и актуальности, нами выбрана еще и образовательная направленность. Надеемся, что эта рубрика понравится нашим новым читателям, а старые не будут в обиде, т.к. ее объели we будет слишком большгсм.
Работа, представленная ниже, была опубликована в ZX РЕВЮ 1992 года и вызвала в свое время самые восторженные отклики читателей.
Дж.Хардман, Э.Хьюзон Перевод Н.А.Тихоновой
40 липших процедур
ОТ ПЕРЕВОДЧИКА
Данная книга является сокращенным переводом книги "40 Best Machine Code Routines For The ZX Spectrum With Explanatory Text", J.Hardman & A.Hewson, содержащей в себе набор программ в машинных кодах с весьма подробными разъяснениями принципов их работы.
РАЗДЕЛ А 1. ВВЕДЕНИЕ
Цель этой книги обеспечить как начинающего, так и опытного пользователя компьютера ZX SPECTRUM полезными, интересными и развлекательными программами в машинных кодах.
Книга имеет два раздела. Раздел А описывает те особенности ZX SPECTRUM'a, которые важны при программировании в машинных кодах, некоторые процедуры системного ПЗУ, а также структуру машинного языка.
Раздел В представляет сами программы. Они представлены в стандартном формате, который детально описан в начале раздела. Программы полностью закончены, т.е. они могут быть загружены и использованы индивидуально,«без обращений к другим программам.
Предлагаемые программы могут быть загружены с помощью простого загрузчика машинных кодов (МС LOADER - маш. ЗАГРУЗЧИК), описанного в начале раздела В.
____дас^^цр _________
ретро
ОБЩИЕ СВЕДЕНИЯ О БЕЙСИКЕ И МАШИННЫХ КОДАХ
Микропроцессор Z80A, на базе которого сделан ZX SPECTRUM, не понимает непосредственно слова Бейсика, такие как PRINT, IF, TAB и т.д. Вместо этого он выполняет приказы специального языка - своего внутреннего машинного кода. Процедуры ПЗУ, которые придают ZX SPECTRUM'y его индивидуальность, написаны на этом специальном языке и состоят из большого количества стандартных подпрограмм для ввода-вывода листинга, интерпретирования и выполнения команд BASIC'a и др.
Например, стандартные подпрограммы говорят процессору Z80A "ЧТО ДЕЛАТЬ, ЕСЛИ... Если, например, команда BASIC'a - слово PRINT, то что делать, если следующий элемент - имя переменной; или что делать, если следующий элемент - запятая и т.д.
Машинный код состоит из последовательности положительных целых чисел (от 0 до 255), которые диктуют действия для Z80A. Хотя машина использует двоичную форму представления чисел, нет необходимости для человека изучать команды в такой форме. Мы будем использовать десятичную форму, которая обрабатывается МС ЗАГРУЗЧИК'ом из раздела В.
Однако даже однообразную строку десятичных чисел трудно интерпретировать, и поэтому десятичные числа обычно преобразовывают в специальный язык (ассемблер), который представляет собой определенные аббревиатуры. Язык ассемблера называется так потому, что специальная программа, называемая АССЕМБЛЕРОМ, используется для обработки ("сбора" или ассемблирования) команд в машинных кодах при написании (формировании) программы.
Требуется только одно число, чтобы точно определить простую команду Z80A. Например, команда СКОПИРОВАТЬ содержимое регистра С в регистр D - это десятичное число 81 (термин "регистр" более детально описан в главе 3, а пока достаточно если Вы будете воспринимать С и D как переменные BASIC'a). Для таких команд есть точное соответствие между десятичным числом и командой: 81, например, записывается на языке АССЕМБ-JIEPa как LD D,C ("LD", кстати, сокращение слова "load" - загрузить). Многие команды ассемблера состоят из подобных простых аббревиатур. По этой причине они часто называются мнемониками. Более сложные команды требуют 2, 3 или 4 числа, но все равно для представления их используется одна команда ассемб-
лера. Таблица 1.1 показывает список нескольких чисел, их мнемо-ник и краткое объяснение действия Z80A.
Таблица 1.1. Некоторые примеры машинных команд Z80A |
Ссылка |
Десятичное число |
Мнемоника |
Комментарий |
(а) |
81 |
LD D,C |
Загрузить в D содержимое С |
(Ь) |
14 27 |
LD С,27 |
Поместить число 27 в С |
(с) |
14 13 |
LD С,13 |
Поместить число 13 в С |
(d) |
33 27 52 |
LD HL, 13339 |
Поместить 13339 в пару регистров HL. Обратите внимание: 27 4- 256 * 52 = 13339; 27 поместить в L; 52 поместить в Н |
(е) |
221 33 27 52 |
LD IX,13339 |
Поместить 13339 в пару регистров IX |
Строка (а) таблицы пример LD D,C - рассматривался выше. Строки (Ь) и (с) показывают, как положительное число может быть загружено в регистр (используются два числа: первое определяет действие, которое должно быть выполнено, а второе определяет число, которое должно быть загружено). Строка (d) показывает, как большое целое число может быть загружено в два регистра (Н и L) вместе. Здесь второе и третье числа определяют, какие числа должны быть загружены. Последний пример в строке (е) иллюстрирует четырехбайтовый код для загрузки большого целого числа в пару регистров IX. Обратите внимание, что три из четырех чисел такие же, как в строке (d). А дополнительное первое число определяет пару регистров IX вместо HL.
Структура машинного языка объясняется более подробно в главе 3, а полный список мнемоник ассемблера Z80A можно найти в литературе по микропроцессорной технике и программированию.
Итак, наиболее насущный вопрос: ЗАЧЕМ ИСПОЛЬЗУЮТ МАШИННЫЙ КОД?
На некоторых компьютерах это делается потому, что задачи, которые пользователь желает выполнить, слишком медленно выполняются. В этом отношении ZX SPECTRUM не исключение. Рассмотрим, например, проблему сохранения полного экрана в RAM (ОЗУ) или копирования его обратно на экран с целью создания эффекта мультипликации.
_97/l~2 _____
.......................................................................ретро
Файл изображения и цветовые атрибуты занимают 6912 байт. Следующая программа BASIC'a сохранит отображение экрана, но это займет много времени - около 70 секунд: 5 CLEAR 58623 10 FOR 1=0 ТО 6911 20 РОКЕ 58624+1, РЕЕК (16384+1) 30 NEXT 1
Причина такой медленной работы в том, что ZX SPECTRUM тратит больше всего времени на декодирование команд BASIC'a перед их выполнением. Некоторое количество времени также тратится на преобразование чисел в двухбайтную форму (которую понимает Z80A) из десятичных чисел в пяти-байтной форме (с которой оперирует BASIC), а также на выполнение пятибайтовой арифметики.
Так, в нашем примере по переброске экрана должны быть выполнены следующий шаги:
1. Прибавить 1 к 16384.
2. Преобразовать результат в форму двух байтов.
3. Восстановить содержимое адреса РЕЕК.
4. Прибавить 1 к 58624.
5. Преобразовать результат в форму 2-х байтов.
6. Сохранить полученное (РЕЕК) значение по заданному адресу (РОКЕ).
7. Прибавить 1 к значению 1 и сохранить результат.
8. Вычесть 1 из 6911. Если результат положительный, то идти на пункт 1.
Во время прохождения цикла ZX SPECTRUM должен декодировать каждую команду снова, так как в данном случае память не используется для сохранения последовательности предыдущих действий. Легко увидеть, что компьютер тратит более 99% времени на подготовку к выполнению задачи, а не на выполнение самой задачи. Аналогичная программа в машинных кодах для сохранения экрана выполняется практически мгновенно. Пример такой программы дан в разделе В.
2. ВНУТРЕННЯЯ СТРУКТУРА ZX SPECTRUM
Компьютер это машина, которая способна запомнить последовательность команд и затем выполнить их. Конечно, чтобы сделать так, требуется память, в которой команды могут быть сохранены. ZX SPECTRUM имеет два вида памяти.
Первый тип - ПЗУ (ROM), в котором содержится фиксированная последовательность инструкций, введенных в машину ее изготовителем.
Второй тип - ОЗУ (RAM). RAM - это "блокнот для записей" компьютера.Когда компьютер выполняет задачу, он непрерывно просматривает, что находится в RAM ("чтение" из памяти) и обновляет содержимое RAM ("запись" в память). ZX SPECTRUM не использует свой "блокнот" для записей случайно. Различные части RAM используются для хранения различных видов информации. Например, программа BASIC'a, введенная пользователем, хранится в одной части RAM, в то время как переменные, используемые этой программой, хранятся в другом месте. Размер "блокнота" для записей ограничен и потому машина точна при распределении пространства для информации, которую она хранит. Свободное пространство собрано в одном месте и если пользователь хочет добавить строку в свою программу, информация в RAM должна быть "перетасована" по всей длине, используя некоторую свободную область для вставки дополнительной строки.
В значительной степени эта глава посвящена объяснению организации RAM ZX SPECTRUM'a, так как многие программы радела В предназначены для манипуляций с RAM. Глава содержит в себе описание дисплейного файла, атрибутов, буфера принтера, системных переменных, программной области и области программых переменных. В конце раздела описываются стандартные подпрограммы из ROM, к которым ссылаются программы в разделе В.
КАРТА ПАМЯТИ
RAM имеет 49152 ячейки памяти. Каждая ячейка может хранить одиночное целое число от 0 до 255 включительно и задается адресом, который является положительным целым числом от О до 65535. Адреса от 0 до 16383 зафиксированы для постоянной памяти ROM. Первый адрес RAM (ОЗУ) 16384. Таблица 2.1
упрощенная карта памяти ZX-SPECTRUM'a, которая показывает как используется RAM с адреса 16384.__
Таблица 2.1. Карта памяти. Указатель стека SP хранится не в RAM, а в SP-регистре микропроцессора Z80A. |
Стартовый адрес или имя системной переменной |
Ячейка системной переменной |
Содержание памяти |
16384 |
- |
Дисплейный файл |
. 9Т/1
ретро
22528 |
- |
Атрибуты |
23296 |
- |
Буфер принтера |
23552 |
- |
Системные переменные |
23734 |
- |
Карта микродрайва |
CHANS |
23631 |
Область информации о каналах |
PROG |
23635 |
Адрес начала программы на BASIC'e |
VARS |
23627 |
Адрес начала области программных переменных |
E-LINE |
23641 |
Адрес области редактирования |
WORKSP |
23649 |
Буфер INPUT |
STKBOT |
23651 |
4 Стек калькулятора |
STKEND |
23653 |
Свободная область |
SP |
|
Машинный стек и стек GOSUB |
RAMTOP |
23730 |
Пользовательские подпрограммы в машинном коде |
. UDG |
23675 |
Графика пользователя |
P-AMT |
23732 |
Физическая вершина ОЗУ |
Дисплейный ( |
эайл, который хранит отображаемую на экра- |
не информацию, занимает ячейки от 16384 до 22527. Атрибуты, которые определяют цвет, яркость и т.д. для каждого знакоместа экрана, следуют непосредственно дальше в ячейках 22528 - 23295.
Первые 5 адресов в колонке 1 таблицы 2.1 являются фиксированными, т.к. дисплейный файл, атрибуты и т.д. занимают фиксированное положение. Пятая область назначена для карты памяти микродрайва. Это небольшое периферийное устройство представляет собой нечто среднее между магнитофоном и дисководом. Грубо говоря это дешевая альтернатива дисководу. Высокая скорость его работы достигается за счет того, что с одной стороны лента движется с очень большой скоростью, а с другой за счет организации прямого доступа к файлам, как на диске, а не последовательного, как на магнитофонной кассете. Вот для того, чтобы иметь в некоем месте хранилище с данными о том, что записано на каждом секторе микродрайва, в памяти компьютера и выделена область карты памяти микродрайва. Если микродрайв (а точнее INTERFACE-1, через который он подключается) подсоединен к ZX SPECTRUM'y, эта область содержит информацию
_zx-pemfr 97/1*3_^^^^
......................................................................ретро'
о его секторах. Если же не подсоединен, эта область не нужна, и в этом случае шестая область (информация о каналах) размещена непосредственно за четвертой областью, системными переменными, т.е. стартовый адрес области информации о каналах и всех последующих областей не фиксирован, а может "плавать" вверх-вниз в RAM.
Z^ SPECTRUM хранит стартовый адрес всех этих областей в системных переменных. Область системных переменных находится перед картой микродрайва в ячейках 23552 23733 включительно. Эти адреса являются все время строго фиксированными.
Адреса ячеек, которые хранят стартовые адреса всех "плавающих" областей, даны в колонке 2 таблицы 2.1. Адрес области начала программы BASIC'a, например, хранится в ячейках 23635 и 23636 в области системных переменных.
Примечание: Ссылка к системной переменной с помощью адреса, по которому она находится, довольно. неудобна. По этой причине в таблице 2.1 в 1-й колонке даны условные имена системных переменных. Эти имена удобны только для пользователей, в то время как ZX SPECTRUM'ом они, естественно, не распознаются. Например, введенная строка: PRINT PROG даст сообщение об ошибке "2: Variable not found" ("Переменная не найдена").
РЕЕК и РОКЕ
Карта памяти это ключ для понимания того, как компьютером используется RAM-память. Для непосредственного управления RAM используются ключевые слова BASIC'a РЕЕК и РОКЕ, которые позволяют просмотреть и изменить содержание любой ячейки памяти.
РЕЕК - функция вида: РЕЕК адрес
Адрес это целое положительное число от 0 до 65535 или арифметическое выражение, которое выполняясь дает положительное число. Важно заключить арифметическое выражение в скобки, т.к.
РЕЕК 16384 + 2
интерпретируется как (2 + результат РЕЕК 16384), тогда как
РЕЕК (16384+ 2)
интерпретируется как
РЕЕК 16386
_жк~р<*п*> ...'...
РЕТРО
Значение, выдаваемое функцией РЕЕК это число, хранящееся по данному адресу в текущий момент. Оно всегда будет положительным от 0 до 255.
Ваше объяснялось, что системная переменная PROG хранится по адресу 23635, но значение PROG (т.е. адрес в RAM) всегда больше числа 255. Следовательно, для его хранения необходимы две смежных ячейки с адресами 23635 и 23636. Значение PROG может быть получено с помощью командной строки: PRINT "PROG="; PEEK 23635 + 256*РЕЕК 23636
Все адреса хранятся в 2-х смежных ячейках в такой форме и могут быть получены вводом:
PRINT РЕЕК 1-я ячейка + 256*РЕЕК 2-я ячейка
Например, если ZX SPECTRUM используется без подсоединенного микродрайва, область карты микродрайва не будет существовать и информация о каналах будет располагаться непосредственно после области системных переменных. Т.о. системная переменная CHANS должна быть такой же, как стартовый адрес карты микродрайва, когда он существует, т.е. 23734. CHANS хранится в 23631 и 23632 и, следовательно, после ввода: PRINT РЕЕК 23631 + 256*РЕЕК 23632 будет получено значение 23734.
Функция РЕЕК может быть использована также для просмотра содержимого любой из ячеек ПЗУ Это очень полезно. Просмотр любой ячейки не приводит к разрушению или искажению программы или переменных. Иногда результаты РЕЕК могут быть обманчивыми, т.к. содержимое ячейки, которая просматривалась, может изменяться в течение или непосредственно после выполнения команды просмотра.
Например, если просматривается содержимое ячеек, которые связаны с левым верхним углом экрана дисплей, то результаты, распечатанные в верхнем левом углу экрана будут уже устаревшими, т.к. в момент вывода изображения мы уже изменили эти ячейки.
Команда РОКЕ более рискованная, чем функция РЕЕК, т.к. задавая ее, пользователь вмешивается в функционирование компьютера. Использование этой команды может быть причиной сбоя машины или ее останова.
Формат команды:
РОКЕ адрес, число
Адрес - это положительное целое число от 0 до 65535 включительно или арифметическое выражение, которое дает такое __Х&Ррто ^У/.ЬЙ __
...................................................................ретро
число. В этом случае нет необходимости заключать арифметическое выражение в скобки, т.к. РОКЕ это команда, а не функция (хотя есть негласная заповедь, что лишними скобками программу не испортишь). Число помещаемое в ячейку, может быть от 0 до 255 включительно.
ДИСПЛЕЙНЫЙ ФАЙЛ
Обычно дисплей содержит 24 строки по 32 символа. Дисплейный файл занимает ячейки от 16384 до 22527, т.е. 6144 ячейки в общей сложности. Следовательно, количество ячеек, используемое для символа:
6144/(24*32)=8
Эти 8 ячеек формируют изображение символа на экране, называемое знакоместом.
Наиболее легкий путь получения общего впечатления о том, как организован дисплейный файл - это печать (PRINT) картинки на экране, сброс (SAVE) экрана на ленту, очистка экрайа (CLS) и загрузка (LOAD) картинки экрана вновь. Программа Р2.1 сохраняет (SAVE) и загружает (LOAD) экран, используя графический символ на клавише 5 для создания оригинальной картинки.
Программа Р2.1
100 FOR 1=0 ТО 703
110 PRINT " REM Символ, находящийся на клавише
"5" в G- режиме
120 NEXT 1
130 SAVE "Picture" SCREENS
140 CLS
150 INPUT "Перемотайте ленту и включите воспроизведение"^
160 LOAD "Picture" SCREENS
Когда картинка загружается с ленты, видно, что дисплей разделен на три зоны по 8 символьных строк в каждой, а каждая строка разделяется на восемь пиксельных линий. ZX-SPECTRUM загружает сначала верхние пиксельные линии для первых восьми строк, затем следующие пиксельные линии тех же восьми строк и т.д. Таким же образом формируются средняя и нижняя части дисплея.
Другой путь понимания формирования дисплея это рассмотреть. где хранятся 8 байтов, которые используются для формирования символа в верхнем левом углу экрана. Первый байт
_ZX^Peyia g7/l»2
ретро
формирует самую верхнюю 1/8 часть символа и размещается в начале дисплейного файла по адресу 16384.
Команде РОКЕ 16384,0 очистит верхнюю линию пикселей самого верхнего левого знакоместа, в то время, как РОКЕ 16384,255 закрасит всю эту линию. При помещении в эту ячейку числа от 0 до 255 мы получим в этом месте экрана различные штрихи. Вторая сверху линия в первом знакоместе на экране не сформирована числом, хранящимся в ячейке 16385 эта ячейка используется для верхней линии пикселей в соседнем символе. Вторая линия сверху в первом знакоместе формируется числом, хранящимся в ячейке
16384+32*8=16640
Подобным же образом вычисляются адреса оставшихся шести линий этого знакоместа.
Следовательно, образ символа в верхнем левом углу экрана определяется содержимым адресов 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176.
Программа Р2.2 позволяет Вам экспериментировать, помещая различные числа в эти восемь ячеек. Каждая ячейка в дисплейном файле определяет восемь пикселей на экране. При этом число, которое хранится в данной ячейке, преобразуется в двоичную форму и затем устанавливаются восемь пикселей соответственно двоичным цифрам. Например, 240 после преобразования в двоичное число дает: 11110000
Следовательно, если ячейка содержит число 240, четыре из восьми пикселей, соответствующие единицам, будут высвечены, а оставшиеся - нет.
Программа Р2.2. Программа для создания символа в верхнем левом углу экрана 10 REM Подпрограмма установки символа в верхнем углу экрана
20 INPUT "Символ состоит из восьми байтов, каждый из которых - число в диапазоне от 0 до 255. Введите номер байта (от 0 до 7)";п
30 IF n<0 OR n>7 OR n<>INT n THEN BEEP 0.2, 24: GO TO 20
40 INPUT "Ввести содержимое байта";т
50 IF m<0 OR m>255 OR m<>INT m THEN BEEP 0.2, 24:
GO TO 40
60 POKE 16384+8*32*n,m
_ ZX^ia 97/1 »2_
РЕТРО
Таким образом, дисплейный файл состоит из 6144 ячеек памяти по 8 ячеек для одного знакоместа. Каждая ячейка определяет горизонтальную полосу из восьми пикселей. Ячейки, относящиеся к данному знакоместу, не расположены последовательно, одна за другой.
АТРИБУТЫ
Содержание дисплейного файла определяет, какие пиксели высвечиваются на экране. Цвет фона (PAPER), символа (INK),яркость (BRIGHT) и мерцание (FLASH) определяются с помощью атрибутов. Область атрибутов занимает ячейки 22528 23295 - по одной ячейке для каждого из 768 знакомест.
Соответствие между содержимым ячеек памяти файла атрибутов и самими атрибутами следующее:
Значение атрибута = 128*FLASH + 64*BRIGHT + 8*PAPER + INK
FLASH и BRIGHT принимают значение 1, если соответствующее условие установлено, a PAPER и INK принимают значение требуемого цвета, как показано на клавиатуре (красный, например, 2).
Программа Р2.3 декодирует атрибуты, т.е. данное значение атрибута распечатывает с соответствующим цветом PAPER и INK.
Программа Р2.3. Программа декодирования атрибута 10 REM Декодер атрибутов 20 DATA "Black", "Blue", "Red", "Magenta", "Green","Cyan"."Yellow", "White", "Bright", "Flash" 30 DIM C$(8,7) 40 FOR 1 = 1 TO 8 50 READ C$(1) 60 NEXT 1
100 REM Декодер атрибутов
110 INPUT "Ввести число от 0 до 255. Эта программа декодирования интерпретирует его в файл атрибутов";п 120 IF n<0 OR n>255 OR n<> INT n THEN ВЕЕР .2,24:GO ТО 110
200 PRINT "Цвет символа - "; C$(1+n-8*INT(n/8)) 210 PRINT "Цвет фона - "; C$(1+INT(n/8)-8*INT(n/64)) 220 IF INT (n/64)=1 OR INT (n/64)=3 THEN PRINT "Символ BRIGHT"
_ЖК~Р<жю У 7/1_
ретро
230 IF n>127 THEN PRINT "Символ будет мерцать (FLASH)"
300 PRINT AT 6,0; "I 11 11 11 I I I 11 11 I 11 I I 11 I I 11 I 11 I 11 I"
310 FOR 1=22720 TO 22751
320 POKE 1 ,n
330 NEXT 1
500 INPUT "Для повторения - ENTER";Z$
510 CLS
520 GO TO 110
БУФЕР ПРИНТЕРА
256 ячеек ОЗУ, следующие за областью атрибутов, используются как буфер для хранения строки символов, которая должна быть передана на принтер.
Многие программы в разделе В будут использовать буфер принтера для передачи данных BASIC'a или от клавиатуры в подпрограммы. Буфер подходит для этой цели, т.к. его ячейки фиксированы и маловероятно, что пользователь пожелает его использовать для других целей.
Единственно важное ограничение в этом случае не использовать команды BASIC'a, которые требуют работы с принтером - LLIST, LPRINT, COPY.
Есть еще и ограничение для владельцев 128-килобайтных машин. У них нет буфера принтера. Дело в том, что буфер принтера предназначался в оригинальной модели для поддержки недорогого специализированного узкопечатного ZX-принтера. В фирменной модели 128-килобайтных машин есть порт подключения полноценного матричного принтера, обладающего собственным буфером, и необходимость в этом буфере отпала. Зато в этих моделях необходимо больше системных переменных и под них отдали область буфера ZX-принтера. Теперь, если в режиме 128К пользователь что-либо зашлет в эту область, то нарушив системные переменные он выведет программу из строя.
ОБЛАСТЬ ПРОГРАММ НА BASIC'e
Обычно эта область начинается с адреса 23755, и на нее указывает содержимое системной переменной PROG (23635,23636), но есть и исключения за счет некоторых видов периферийных устройств.
Если, например, к компьютеру подсоединен микродрайв, то начало этой области сдвигается. В этом самом общем случае начало области программ на BASIC'e и определяют с помощью сис-_ Ж^^.^У/^Й ;.................
ретро
темной переменной PROG. Ниже предполагается, что такая периферия не подсоединена.
Программа Р2.4 распечатывает содержимое 18-ти ячеек в начале программной области, как показано на рис.2.1. В этих 18 ячейках хранится первая строка данной программы. Программа Р2.4 Программа для просмотра содержимого первых 18-ти ячеек в программной области 10 REM Peek program 20 FOR 1=23755 TO 23772 30 PRINT 1 ,PEEK 1 40 NEXT 1
23755 |
0 |
23756 |
10 |
23757 |
14 |
23758 |
0 |
23759 |
234 |
23760 |
80 |
23761 |
101 |
23762 |
101 |
23763 |
107 |
23764 |
32 |
23765 |
112 |
23766 |
114 |
23767 |
111 |
23768 |
103 |
23769 |
114 |
23770 |
97 |
23771 |
109 |
23772 |
13 |
Рис. 2.1. Формат, в которой строка 10 REM peek program хранится в программной области Номер строки (10) хранится в первых двух ячейках в форме:
Номер строки = 256*РЕЕК первый адрес + РЕЕК второй адрес.
Следующие две ячейки 23757 и 23758 хранят длину оставшейся части строки, начинающейся в ячейке 23759. В нашем случае:
14+256*0=14
_ЖК~Р<н**> ИГ/1 »2 ___'
ретро
Таким образом, следующая строка начинается с ячейки: 23759+14=23773
Ячейка 23759 хранит в себе число 234, которое является кодом ключевого слова (токена) REM. Следующие 12 ячеек хранят коды символов одиннадцати букв и пробела, составляющих фразу Peek program. Последняя ячейка хранит число 13, которое является кодом для ENTER, определяющим конец строки.
Таблица 2.2 показывает метод кодирования программы в программной области._
Таблица 2.2. Этот метод используется для кодирования строк программы |
Ячейки |
Содержимое |
1 и 2 |
номер строки |
3 и 4 |
длина строки, исключая первые 4 ячейки |
5 |
код команды |
Конец |
символ ENTER, число 13 |
Момент, который опущен в таблице - это описание метода хранения числовых значений, имеющих место в программе. Этот метод может быть исследован на примере строки:
10 LET а=1443 Введите ее в программу Р2.4. На рис. 2.2 показан результат работы программы в этом случае._
23755 |
0 |
23756 |
10 |
23757 |
14 |
23758 |
0 |
23759 |
241 |
23760 |
97 |
23761 |
61 |
23762 |
49 |
23763 |
52 |
23764 |
52 |
23765 |
51 |
23766 |
14 |
23767 |
0 |
23768 |
0 |
23769 |
163 |
Рис. 2.2. Формат, в которой строка 10 LET а=1443 хранится в программной области
Ячейки 23755-23758 такие же, как в прошлом примере, затем следуют коды для LET, а, = и четыре кода цифр, которые вместе формируют число 1443. Следующий элемент, находящийся в ячейке 23766, - это код 14. Этот код указывает, что следующие 5 ячеек хранят число в специальном пятибайтном формате. Линия заканчивается в ячейке 23772 вводом символа ENTER, как и ранее.
* * *
Примечание ИНФОРКОМа
Получается так, что в одной строке число как бы записано дважды. Первый раз своими символами, а второй раз в пятибайтной форме после кода 14. Зачем это нужно?
Дело в том, что первое представление используется для того, чтобы Бейсик-интерпретатор знал, что Вам показать на экране, а второе - для расчетов во внутреннем калькуляторе компьютера. (О калькуляторе читайте в нашей книге "Программирование в машинных кодах").
Самое интересное, что они могут и не совпадать. В этом случае на экране Вы будете видеть одно, а программа будет обрабатывать совсем другое число. И этим нередко пользуются в защите программ. Например, Вы видите на экране
LET а=1257: GO ТО а
и пытаетесь проследить работу программы, а на самом деле там было записано нечто совсем другое и компьютер делает переход не к строке 1257, а туда, куда надо.
Желающие могут посмотреть загрузчик программы BOMBJACK. В череде относительно несложных вывертов этот там стоит одним из первых, но если на него "клюнуть", атака
на программу никак не получится.
* *
ЦИФРОВОЙ ПЯТИБАЙТНЫЙ ФОРМАТ
Пять ячеек памяти используется для хранения чисел в программе на BASIC'e (исключая номера строк). Целые числа в диапазоне от -65535 до +65535 хранятся таким же образом, как в ___у7/1»2_
ретро
формате Z80A. Для этих чисел первые две ячейки и последняя содержат 0, а третья и четвертая хранят число в двухбайтной форме:
число = РЕЕК 3-я ячейка + 256*РЕЕК 4-я ячейка Таким образом 16533 хранится в пяти ячейках как О 0 169 64 О
потому, что
169+256*64=16553 Дробные числа хранятся в формате с плавающей запятой таким образом: экспонента в первой ячейке, а мантисса в следующих четырех ячейках, то есть:
число = мантисса * 2~экспонента Первая ячейка мантиссы используется также для определения знака числа. Если ячейка содержит значение в пределах от О до 127, число положительное, если нет - отрицательное.
Программа Р2.5 может быть использована для восстановления дробного числа из составляющих его пяти компонентов.
Программа Р2.5. Эта программа восстанавливает дробное
число
10 PRiNT "Ввести экспоненту и четыре байта мантиссы. Всё числа должны находиться между 0 и 255 включительно." 20 INPUT e.a.b.c.d 30 PRINT „ " Exponerit= ";e 40 PRINT "Mantissa= ";а„Ь„с„с1 50 PRINT ,, "The nember= ";(2*(a<128)-1)*2~(e-160) * (((256*(a+128*(a< 128))+b)*256+c)*256+d) ОБЛАСТЬ ПЕРЕМЕННЫХ Область переменных начинается в ячейке, адрес которой хранится в системной переменной VARS (ячейка 23627). Как бы ни была объявлена новая переменная, т.е. в программе или непосредственным вводом с клавиатуры, для нее резервируется соответствующее количество свободного пространства в этой области.
Все имена переменных начинаются с буквы. Различий между верхним и нижним регистром нет. Эти ограничения позволяют ZX-SPECTRUM'y манипулировать с кодом первого символа каждой переменной и таким образом он может различить шесть типов переменных просмотром диапазона, в котором находится код.
Все цифровые переменные с односимвольными именами, например, имеют коды в пределах от 97 до 122; буква а 97; b
98; с 99 и т.д. Подобным же образом цифровые массивы имеют коды в пределах 129 - 153, т.е. а - 129; Ъ - 130; с - 131 и т.д. Диапазоны кодов представлены в таблице 2.3. Длина каждого типа переменной также показана в таблице 2.3._
Таблица 2.3. Переменные, диапазон кодов и длина переменных |
Тип переменной |
Диапазон символьного кода |
Длина в области переменных |
Цифровой (односимвольное имя) |
97 122 |
6 |
Цифровой
(многосимвольное
имя) |
161 186 |
5 + длина имени |
Цифровой массив |
129 - 154 |
4 4- 2*размерность + 5*общее коли чество элементов |
Управляющая переменная цикла FOR - NEXT |
225 - 250 |
18 |
Символьная строка |
65 - 90 |
3 4- длина строки |
Символьный массив |
193 - 218 |
4 + 2*размерность 4- общее число элементов |
ПОДПРОГРАММЫ ПЗУ
Некоторые из представленных в книге программ (раздел В) используют стандартные подпрограммы ПЗУ:
• RST 16 - распечатывает содержимое аккумулятора,
• CALL 3976 вставляет символ, хранящийся в аккумуляторе, по адресу, хранящемуся в паре регистров HL,
• CALL 6326 если аккумулятор хранит код 14, устанавливается нулевой флаг и увеличивается пара регистров HL в пять раз,
• CALL 6510 возврат в HL адреса в ОЗУ той строки, номер которой был передан в эту подпрограмму через HL.
3. МАШИННЫЙ ЯЗЫК Z80 РЕГИСТРЫ Z80A
Во время выполнения программы компьютер не обновляет непосредственно содержимое памяти. Он копирует содержимое
__ ...................
1 .....ретро ......................
ячейки памяти в регистр и оперирует содержимым регистра. Регистры в машинном языке имеют функцию, схожую с переменными в BASIC'e, т.е. используются для хранения чисел. Они отличаются от переменных BASIC'a тем, что число их ограничено и они существуют в процессоре, а не в RAM. Кроме того, они могут хранить только один байт или два байта в паре регистров.
Z80A имеет несколько регистров и потому может хранить несколько чисел одновременно. Благодаря этому снижается время при обмене информацией между процессором и памятью.
РЕГИСТР "А" (АККУМУЛЯТОР)
Аккумулятор это наиболее важный регистр, потому что больше всего арифметических и логических команд выполняется с содержимым именно Этого регистра. Этот регистр называется аккумулятором, т.к. результат последовательных операций накапливается ("аккумулируется") в нем. Некоторые команды, которые обращаются к аккумулятору, используют другой регистр или адрес памяти в качестве источника данных. Например, инструкция
ADD А, В
дает процессору команду прибавить содержимое регистра В к содержимому регистра А, поместив результат в А.
ФЛАГ-РЕГИСТР "F" (РЕГИСТР СОСТОЯНИЙ)
Регистр F довольно значительно отличается от всех остальных, т.к. его содержимое не рассматривают как один байт, а рассматривают как 8 индивидуальных битов, что конечно же одно и то же. Эти биты используются в качестве так называемых флагов для управления последовательностью выполнения программы. Каждый флаг используется для определения того, какое из двух логически противоположных условий имело место при выполнении предыдущей операции. Например, флаг нуля определяет был ли равен нулю результат последней операции сложения, вычитания и т.п. Только четыре из восьми флагов наиболее интересны для пользователей. Их свойства кратко изложены в таблице 3.2.
Таблица 3.2. Четыре флага, которые контролируют наибольшее количество операций Z80A |
Флаг |
Мнемоника |
Использование |
Знак |
N |
Р |
Включается, когда результат последней операции отрицательный |
97
4 Зак. 119
Нуль |
Z |
NZ |
Включается, когда результат последней операции равен нулю или имело место совпадение |
Перенос |
С |
NC |
Включается, когда происходит переполнение регистра, т.е. последний результат больше того, что может быть записанным в один байт (или в два байта для операций с парой регистров) |
Четность/Пе-реполнение |
РЕ |
РО |
Флаг включается, если в байте результата предыдущей операции количество включенных битов есть величина четная. В некоторых операциях этот флаг свидетельствует о переполнении |
Флаг знака наиболее простой. Кроме того, условлено, что в операциях со знаковыми числами седьмой бит (старший бит в байте) тоже используется для хранения знака. Он включается, когда число отрицательно, ина!че снимается. Этот флаг отражает знак последнего результата.
Флаг нуля устанавливается, если результат последней операции равен нулю. Он также используется инструкциями сравнения, которые фактически аналогичны вычитанию, при котором результат игнорируется.
Флаг переноса регистрирует переполнение, которое имеет место, если результат сложения длиннее чем то, что может быть записано в регистр, либо имеет место заем при вычитании. Имеется также несколько инструкций сдвига, в которых биты в регистре А сдвигаются влево или вправо циклически через флаг переноса.
Флаг четности/переполнения это два флага в одном. Он используется как флаг переполнения при выполнении арифметических операций для определения был ли бит 7 получен в результате переноса или сгенерирован битом 6 при "взятии займа" Это используется в случае, если бит знака был искажен. Логические инструкции используют тот же самый флаг для определения четности результата.
Примечание: четность двоичного числа - это четность количества битов, установленных в единицу. Если количество четно, то говорят, что имеет место четность, если оно нечетно, то гово-__ДИВфш 97/1*3_
ретро
рят, что имеет место нечетность. Флаг устанавливается, если имеет место четность.
Результат некоторых инструкций зависит от текущих установок отдельных флагов. Например, инструкция
JR Z,D
позволяет Z80A "перепрыгнуть" через несколько инструкций, если флаг нуля установлен (т.е. сделать условный переход типа IF...THEN GO ТО). Если флаг нуля не установлен, процессор выполняет следующую инструкцию в обычной последовательности. Т.о. флаговый регистр очень важен, т.к. он позволяет процессору принимать решения и переходить к другой части программы.
РЕГИСТРЫ-СЧЕТЧИКИ "В" и "С"
Регистр В и, в некоторой степени, регистр С, с которым он может использоваться в паре, доступен для нескольких применений. Но наиболее важно использование его в качестве счетчика. Мы уже видели, как выполнение программы может управляться использованием флага 0 в инструкции JR Z,n.
Другая инструкция DJNZ п использует флаг 0 для создания циклов, используя регистр В в качестве счетчика аналогично циклам FOR-NEXT в BASIC'e. Когда встречается эта инструкция, Z80A уменьшает содержимое регистра В на 1. Если результат равен нулю, то выполняется следующая инструкция в нормаль^ ной последовательности. Если результат не равен нулю, подпрограмма "перескакивает" п инструкций. Если программист использует отрицательное значение для п, "прыжок" осуществляется в программе назад и, если здесь нет других переходов, процессор в конце концов встретит ту же самую инструкцию вновь.
Таким образом загрузка регистра В определенным числом и установкой соответствующего смещения п определенная часть кодов может быть выполнена заданное количество раз. Регистр В содержит только один байт и поэтому может устанавливаться на любое число от 0 до 255, т.е. используя описанный механизм через определенный блок кодов может быть сделано максимум 255 проходов. Для осуществления более 255 проходов в цикле нет эквивалентных инструкций, но имеются очень мощные инструкции, которые используют все 16 битов регистровой пары ВС как счетчик с максимальным значением 65535. Например, инструкция CPDR. Когда Z80A выполняет ее, то:
1) содержимое ВС уменьшается на 1;
2) уменьшается содержимое HL (о регистровой паре HL см. ниже);
__ , ..................................
ретро
3) сравнивается содержимое аккумулятора А с содержимым ячейки памяти, адрес которой находится в паре регистров HL.
Процессор повторяет эти действия до тех пор, пока не будет совпадения между содержимым А и ячейки памяти, либо пока не обнулится ВС. Таким образом эта инструкция может быть использована для поиска ячейки, содержащей определенное число.
АДРЕСНЫЕ РЕГИСТРЫ "DE" и "HL"
Регистры D и Е не имеют особых функций и, в основном, используются в качестве временной быстродоступной памяти. Они также используются в паре для хранения адреса ячейки, которая интересует нас в текущий момент.
Основная функция регистров Н и L хранить в паре адрес ячейки памяти. Мы уже видели, как определенная инструкция использует пару HL с этой целью. Н хранит старший байт, L младший. Полный адрес доступен в форме:
адрес = 256* Н + L, что дает максимум 65536 возможных адресов.
ИНДЕКСНЫЕ РЕГИСТРЫ "IX" и "IV"
Индексные регистры IX и IV являются 16-битовыми регистрами и могут быть использованы только так в отличие от регистров ВС, DE и HL, которые могут использоваться и в паре, как 16-битовые регистры, и индивадуально, как 8-битовые. IX и IV используются, главным образом, подобно паре HL.
IX и IV, кроме того, имеют одно свойство, которое недоступно для HL они могут быть использованы со смещением s. Имеется в виду, что инструкция, которая ссылается к (IX+s), использует не ячейку памяти, адрес которой хранится в IX, а сначала смещение s прибавляется к значению в IX, чтобы получить новый адрес, и с ним и работает инструкция.
УКАЗАТЕЛЬ СТЕКА "SP"
Стек - это область в верхней части ОЗУ, которая используется для временного хранения содержимого пар регистров. Стек растет вниз, когда идет заполнение и поднимается вверх при его опустошении. Начало стека фиксировано в ZX-SPECTRUM - оно находится непосредственно ниже ячейки, на которую указывает системная переменная RAMTOP. Вершина стека находится ниже нижней границы стека, т.к. стек увеличивается вниз, а уменьшается вверх. Адрес текущей ячейки верхней границы стека хра-_ _
ретро
нится в SP-регистре. Передача в стек или из стека происходит с помощью инструкций PUSH и POP. Например:
PUSH HL
В этом случае процессор:
1) уменьшает SP;
2) копирует содержимое регистра Н в ячейку, указанную указателем стека SP;
3) уменьшает SP;
4) копирует содержимое регистра L в ячейку, указанную указателем стека SP.
Инструкция POP выполняет противоположное действие. По этому методу последняя поступившая пара чисел, помещенная в стек, является всегда первой парой, которая снимается со стека. Это обеспечивает простой и удобный способ временного хранения содержимого регистров во время вызова подпрограмм.
ПРОГРАММНЫЙ СЧЕТЧИК "PC"
Программный счетчик PC очень важный 16-битовый регистр, т.к. он хранит адрес следующей инструкции, которая должна быть выполнена. Нормальный ход работы когда инструкция выполнятся следующим образом:
1) Скопировать содержимое ячейки, указанной программным счетчиком PC в специальный регистр процессора.
2) Если инструкция содержится в нескольких байтах, увеличить PC и скопировать содержимое следующей ячейки во второй специальный регистр.
3) Увеличить PC таким образом, чтобы он указывал на следующую инструкцию, которая должна быть выполнена.
4) Выполнить инструкцию, которая только что была прочитана.
Команда JUMP так же, как и DJNZ п или JR Z,n, изменяет нормальный ход выполнения программы с помощью изменения PC во время выполнения шага 4. Заметим, что это изменение имеет место после увеличения PC. Таким образом, значение смещения п всегда будет отсчитываться относительно позиции инструкции следующей за инструкцией, содержащей смещение.
АЛЬТЕРНАТИВНЫЕ РЕГИСТРЫ AF, ВС', DE', Н1/
Z80A имеет копию каждого из А, В, С, D, Е, Н и L регистров. Отличаются копии использованием знака ,м" возле обозначения регистра, например, А' это копия регистра А. Инструкции не работают с этими регистрами непосредственно, но инструкции обмена позволяют вывести из использования 2 или более регистров и ввести в использование вместо них их копии.
Команды обмена выполняются очень быстро, т.к. содержимое регистра физически не копируется из одного регистра в другой, а вместо этого установка внутренних коммуникаций процессора изменяется так, что дублирующий регистр становится основным, а оригинальный регистр - запасным.
О СИСТЕМЕ КОМАНД ПРОЦЕССОРА Z80
В системе команд Z80 около 700 инструкций. Поскольку возможны только 256 различных комбинаций из 8 битов (2"8=256), то лишь менее половины команд может быть выражено одним байтом. Оставшиеся инструкции хранятся в двух или трех байтах. Некоторые команды следуют с однобайтовым смещением s, или однобайтовым числом п, или дуэфайтовым числом (адресом) пп, к которым инструкция ссылается. Всего же одна
инструкция может занять максимум 4 байта.
* * *
Примечание ИНФОРКОМа
ZX SPECTRUM располагает еще встроенным программируемым калькулятором, имеющим свою систему команд. Доступ к ним возможен только из машинного кода. Команды калькулятора могут быть более длинными, чем команды процессора
и занимать более 4-х байт.
♦ # *
Для удобства работы на ассемблере все команды записываются с помощью мнемоник (OP CODE). Мнемоника это сокращенное описание каждой команды. Правила задания команд в
ассемблере:
1) Одиночные регистры описываются буквой, например, В. Пара регистров именуется в алфавитном порядке, например, ВС.
2) Смещение s положительно, если оно находится в пределах от О до 127, и отрицательно, если оно находится в пределах от 128 до
255. Большие или меньшие числа недопустимы.
Отрицательное значение подсчитывается вычитанием s из
256. Например, команда относительного безусловного перехода
JR s
вызывает переход вперед на 8 байтов, если s=8 и переход назад на 8 байтов, если s=248, т.к. 256-8=248.
........ffifrPeiю _,
ретро
Запомните при подсчете смещения, что переход осуществляется с адреса первого байта следующей команды.
3) Одцобайтовое число п лежит в пределах от 0 до 255 включительно.
4) Двухбайтовое число или адрес представляется как пп и лежит в пределах от 0 до 65535 включительно.
5) пп, заключенное в скобки, т.е. (пп), подразумевает "содержимое ячейки по адресу пп", тогда как пп без скобок подразумевает "число пп" Таким образом, LD HL,(23627) подразумевает загрузку пары регистров HL содержимым ячеек 23627 и 23628, тогда как:
LD HL, 23627
подразумевает загрузку HL числом 23627.
Таким же образом (HL) подразумевает "содержимое ячейки с адресом, хранящимся в HL", тогда как HL без скобок означает "число, хранящееся в HL"
Такой вид адресации называется косвенной адресацией.
6) Место назначения результата операции всегда задается первым. Например:
ADD А, В
означает "прибавить содержимое регистра В к содержимому регистра А и результат оставить в регистре А"
ПРОДОЛЖЕНИЕ СЛЕДУЕТ