Н А П И С А Н И Е П Р О Г Р А М М Д Л Я "С П Е К Т Р У M"
ПЛАНИРОВАНИЕ ВАШЕЙ ПРОГРАММЫ НА МАШИННОМ ЯЗЫКЕ
Программирование на машинном языке обладает
чрезвычайной гибкостью в том смысле, что позволяет вам делать
все, что угодно.
Поскольку от всех языков более высокого уровня в
конечном счете нужно переходить к машинному языку, все, что
вы можете запрограммировать на языке "Фортран", или "Кобол",
или любом другом, можно запрограммировать на машинном языке.
Дополнительным преимуществом будет то, что программа
на машинном языке выполняется быстрее.
Эта абсолютная гибкость может, однако, оказаться
ловушкой для неосторожного программиста. При наличии такой
полной свободы можно делать все, что угодно. В отличие от
операционной системы "Спектрум" для языка "Бейсик", например,
отсутствуют проверки предложений на допустимость.
Поскольку любые вводимые вами числа будут командами
того или иного типа, чип Z80 будет обрабатывать все на
свете.
Но даже не беря в расчет проблемы проверки
допустимости синтаксиса, нужно отметить, что программирование
на машинном языке не накладывает ограничений на используемую
вами логику. Вы можете выполнять функции, переходы и т.п.,
которые будут абсолютно недопустимы в любом языке более
высокого уровня. Поэтому огромное значение приобретает
самодисциплина при разработке программ на машинном языке.
Невозможно преувеличить значение концепции "нисходящего"
подхода в программировании в целом, но в особенности это
касается программирования на машинном языке.
"Нисходящий подход заставляет вас разбивать задачу
на более мелкие единицы и позволяет проверить логику вашей
разработки, на протяжении долгого времени не записывая ни
одной строки текста программы.
Предположим, вам захотелось написать программу
посадки на луну. Самый первый путь мог бы напомнить нечто
подобное:
INSTR выдача на экран инструкций; переход назад на
INSTR, пока не будет нажата кла- виша ENTER;
DRAW нарисовать ландшафт, начать движение
спускаемого аппарата с вершины экрана;
LAND движение спускаемого аппарата если горючее
кончилось, перейти на CRASH перейти назад на
LAND, если поверхность не до- стигнута;
GROUND напечатать поздравления перейти назад на
INSTR для следующего прогона CRASH напечатать
соболезнования по поводу неудачной посадки
перейти назад на INSTR для следующего
прогона.
Обратите внимание, что вся эта "программа" написана
на русском языке. На этом этапе не принималось никаких
решений, будет ли программа писаться на языке "Бейсик" или на
машинном языке. Такое решение и не нужно принимать. Концепция
программы посадки на луну не зависит от способа записи ее
текста.
Теперь наступает этап логической проверки.
Вы выполняете роль ЭВМ и смотрите, все ли
возможности, которые вы хотели включить в программу, имеются
в наличии. Нет ли переходов на объекты, которые вы хотели
написать, но забыли? Все ли есть? Нет ли избыточных программ?
Не следует ли некоторые объекты перенести в подпрограммы?
Давайте снова посмотрим на "программу" - ох-ох-ох! -
мы забыли как-нибудь закончить программу.
Описанная выше логика может быть прекрасной для
некоторых прикладных задач, таких как игровая ЭВМ, но в своей
программе вам может прийти в голову, что неплохо бы иметь
возможность выключить работу программы.
Теперь мы изменим последнюю часть программы
следующим образом:
GROUND напечатать поздравления перейти назад на
INSTR для следующего прогона
CRASH напечатать соболезнования по поводу неудачной
посадки
FINISH спросить игрока, нужно ли закончить если нет,
перейти на INSTR если да, STOP
Обратите внимание, что мы использовали метки для
описания определенных строк программы. Метки - очень ценный
аппарат, особенно если вы будете выбирать их краткими и
содержательными.
Когда этот уровень закончен, вы перемещаетесь на
уровень глубже, чтобы проделать то же самое с одной из строк
или модулей, приведенных выше.
Именно поэтому этот подход называется нисходящим.
Например, мы можем следующим образом расписать
модуль "FINISH", приведенный выше.
FINISH -очистить экран
-напечатать:"хотите ли закончить?"
-опросить клавиатуру в ожидании ответа
-если ответ =ДА, то закончить
-перейти на INSTR
Еще одно преимущество нисходящего подхода состоит в
том, что вы можете тестировать и выполнять конкретный модуль
автономно,так что он будет отлажен для включения в
окончательный текст программы.
Давайте спустимся еще на один уровень и посмотрим на
строку "очистить экран" более подробно.
На этом этапе нам нужно решить,на каком языке мы
будем писать программу, и давайте выберем машинный язык
"Синклера". Если бы вы писали программу на языке "Бейсик", то
вам было бы достаточно написать:
900 CLS,
но на машинном языке это простое предложение "очистить экран"
может оказаться обманчивым.
Поэтому мы могли бы сделать что-то в таком роде:
CLEAR -найти начало экрана
-заполнить следующие 6144 позиции пробелами
Мы все еще не написали ни строчки текста программы,
но, очевидно, подход основан на машинном языке. Давайте
посмотрим более пристально, что должна делать эта программа
очистки экрана и что она делает на самом деле.
Вы возможно помните из руководства по "Спектрум",
что экран состоит из 6144 ячеек и что есть еще 768 ячеек,
описывающих атрибуты экрана: цвет бумаги, цвет чернил и т.п.
Приведенное выше краткое описание программы,
конечно, очистит часть экрана, но никак не повлияет на файл
атрибутов. Если не у всех позиций экрана совпадает цвет
бумаги или если позиции некоторых литер имеют включенные
атрибуты мигания или яркости, то ясная программа очистки
экрана, приведенная выше, окажется явно неадекватной.
Нам придется также обработать и файл атрибутов.
Обратите внимание насколько сложнее оказываются некоторые
задачи на машинном языке, чем на языке "Бейсик".
Поэтому нам надо расширить программу до следующего
вида:
- найти начало экрана
- заполнить следующие 6144 байта пробелами
- найти начало файла атрибутов
- заполнить следующие 768 байтов требуемыми
значениями атрибутов бумаги (чернил,...)
Следующий нижележащий уровень - это уже тот, на
котором вы должны, наконец, писать текст программы. Так что
давайте посмотрим, как экран заполняется пробелами.
CLEAR LD HL,SCREEN : SCREEN START
LD BC,6144 : BYTES TO CLEAR
LD D,0 : D = BLANK
LOOP LD (HL),D : FILL BLANK
INC HL : NEXT POSITION
DEC BC : REDUCE COUNT
LD A,B
OR C : TEST IF BC =0
JR NZ,LOOP : AGAIN IF NOT END
SCREEN START - начало экрана;
BYTES TO CLEAR - очищаемые байты;
BLANK -пробел;
FILL BLANK - заполнение пробелом;
NEXT POSITION - следующая позиция;
REDUCE COUNT - уменьшение счетчика;
TEST -проверка;
AGAIN IF NOT END - повторение, если не достигнут конец.
Теперь вы достаточно легко можете работать с
программами такой длины и таким способом строить достаточно
большие программы. Кстати, вы теперь без сомнения понимаете,
почему программы на машинном языке часто так велики по объему
и почему люди изобрели программы на языках высокого уровня.
УПРАЖНЕНИЯ
Чтобы написать любую конкретную программу, есть
всегда больше одного способа, так что давайте рассмотрим
простую программу очистки экрана, написанную выше. Для этого
есть несколько разных способов.
УПРАЖНЕНИЕ 1
Можете ли вы придумать способ, позволяющий в цикле
опробелить 6144 позиций без применения регистра BC, а только
с помощью регистра B так, чтобы мы могли пользоваться
командой DJNZ?
УПРАЖНЕНИЕ 2
Можете ли вы придумать способ, позволяющий
опробелить 6144 позиций с помощью более мощной команды LDIR?
Тщательно продумайте, что делает команда LDIR: не всегда
необходимо иметь где-то еще 6144 пробельных позиций.
ОТВЕТЫ
Есть несколько правильных ответов. Единственная
проверка - будет ли это работать? Иными словами, делает ли
программа то, что вам нужно? С помощью DJNZ:
CLEAR LD HL,SCREEN
LD A,0
LD B,24 : SET B=24
BIGLOOP PUSH BC : SAVE VALUE
LD B,A : SET B= 256
LITLOOP LD (HL),A
INC HL : FILL IN 256 BLANKS
DJNZ LITLOOP
POP BC : GET BACK VALUE OF B
DJNZ BIGLOOP : DO IT UNTIL END
SET - установить;
SAVE VALUE - запомнить значение;
FILL IN 256
BLANKS - заполнить 256 пробелов;
GET BACK VALUE OF B - получить назад значение B;
DO IT UNTIL END - выполнять до достижения конца.
Мы смогли использовать 24 раза по 256 (=6144) для
очистки экрана. Нужно отметить следующее:
мы можем задать B=0, чтобы пройти цикл DJNZ 256 раз. Почему?
Эта процедура обычно не будет применяться в программе, если
мы только не станем использовать регистр C для других целей.
Применение LDIR.
CLEAR LD HL,SCREEN : SOURCE
PUSH HL
POP DE
INC DE : DEST = HL + 1
LD BC,6144 : HOW MANY
LD (HL),0 : LAT POS = 0
LDIR : MOVE IT
SOURCE - источник;
HOW - сколько;
LAT - первая;
MOVE - перемещение
Обратите внимание, что мы получили DE = HL + 1, задавая DE=HL
и давая приращение DE. Это можно сделать проще, загружая
значение SCREEN + 1 в DE непосредственно, но для этого
требуется на один байт больше. Причина, по которой эта
команда LDIR срабатывает, состоит в том, что испиользуется
факт, что в процессе обработки данные перезаписываются в
блок. Здесь происходит применение с положительным результатом
задачи, рассмотренной нами в главе о перемещении блоков.
Если просуммировать потребную память, то при первом
методе требуется 14 байтов, при втором - 16 байтов, а при
последнем - 13 байтов.
С Р Е Д С Т В А "Z X С П Е К Т Р У М"
Настало время рассмотреть средства вашего "ZX
Спектрум", полезные при разработке для него программ на
машинном языке.
ВВОД - КЛАВИАТУРА
Что касается ввода информации в "Спектрум", мы будем
игнорировать ввод с кассетного магнитофона и сконцентрируемся
на клавиатуре.
Клавиатура - единственный источник информации,
предоставляющий связь в реальном масштабе времени. Она может
динамически влиять на выполнение любой программы, как
операционной системы в ПЗУ, так и пользовательской программы
в памяти с произвольным доступом. Логически мы можем
рассматривать клавиатуру как двухмерную матрицу с восемью
рядами и пятью столбцами, как в приложении А. Каждое из 40
пересечений представляет клавишу клавиатуры. В нормальном
состоянии (когда они не нажаты) они всегда в хорошем
настроении , т.е. пересечение устанавливается равным 1. При
нажатии конкретной клавиши "нажатое" пересечение,
соответствующее этой клавише, будет сброшено в плохое
настроение, т.е. 0. Зная связь между клавиатурой и этой
внутренней матрицей представления, мы можем вывести
логический способ проверки нажатия клавиши, который можно
применять в программировании на машинном языке.
В языке "Бейсик" при сканировании клавиатуры нам
нужно задать адрес той конкретной половины ряда клавиатуры,
где располагается нужная клавиша, прежде чем использовать
функцию IN.
Аналогичным образом в программе на машинном языке мы
должны загрузить в накопитель значение, соответствующее
адресу полуряда клавиш, который мы хотим проверить. Требуемое
значение для каждой половины ряда приводится в самой левой
колонке таблицы в приложении А.
Например, для "H - ENTER" (полуряда) мы загружаем в
регистр A значение
LD A,BFH
затем значение A будет использоваться для поиска байта,
содержащего состояние той конкретной половины ряда клавиш и
возврата в A при задании команды INPUT.
Например, используется порт FEH
IN A,(FEH)
Поскольку в половине ряда имеются пять клавиш, нас
интересуют только пять младших битов байта, возвращаемого в
регистр A. Если в этой половине ряда никакие клавиши нажаты
не были, то значения пяти младших битов будут:
(2^4 + 2^3 + 2^2 + 2^1 + 2^0 т.е. 16 + 8 + 4 + 2 + 1 = 31).
Регистр A = XXX11111, когда нет нажатых клавиш.
Если мы хотим проверить, нажат ли самый первый бит,
то мы проверяем, сброшен ли он. Есть два способа проверить
это.
1.С помощью команды проверки бита, например, BIT 0,A. Если
бит сброшен (не установлен), то будет установлен флаг нуля.
2.С помощью команд логического И (AND 1). Если бит сброшен
(не установлен), то результат будет нулевым и флаг нуля будет
установлен.
Первый способ проще, поскольку конкретный бит,
подлежащий проверке, указан непосредственно в команде
проверки бита. Его недостаток в том, что если нам нужно
проверить две клавиши в этой половине ряда,нам придется
применять две команды проверки бита, и, возможно, два
относительных перехода.
Например, чтобы проверить биты 0 и 1 с помощью
первого способа
BIT 0,A : TEST BIT 0 OF A SET OR NOT
JR Z,NPRESS : JUMP IF NOT PRESSED
BIT 1,A : TEST BIT 1 OF A SET OR NOT
JR Z,NPRESS : JUMP IF NOT PRESSED
.
DO WHATEVER IF BOTH ARE PRESSED
.
NPRESS .
TEST BIT 0 OF SET OR NOT - проверить, установлен или нет бит
0 регистра A;
JUMP IF NOT PRESSED - переход, если не нажата;
TEST
BIT 1 OF SET OR NOT - проверить, установлен или нет бит
регистра A;
DO WHATEVER IF BOTH ARE PRESSED -выполнить то, что следует,
если обе клавиши нажаты.
Второй способ проверки с помощью логического И
требует несколько больше логических ухищрений. Для проверки
бита 0 мы используем AND 1, для проверки бита 1 - AND 2, для
проверки бита 2 - AND 4 и т.д.
Для проверки двух клавиш мы применяем AND X, где X -
сумма значений, используемых для проверки каждой из клавиш в
отдельности. Например, чтобы проверить, что бит 0 и бит 1
регистра A установлены:
AND 1 : TEST BOTH BIT 0 AND BIT 1 IS SET *
CP 1 : TEST IF BOTH SET **
JK NZ,NBOTH : JUMP IF NOT BOTH PRESSED ***
* проверить, что оба бита установлены;
** установлены ли;
*** переход, если не обе клавиши нажаты.
Чтобы проверить, что установлен один из битов 0 или
1 регистра A.
AND 3 : TEST EATHER BIT 0 AND BIT 1 IS SET *
JR Z,NOTONE : JUMP IF NOT ONE IS PRESSED **
* проверить, что один из битов 0 или 1 установлен;
** переход, если ни одна клавиша не нажата.
УПРАЖНЕНИЕ
Чтобы подвести итог всему, что мы узнали о
клавиатуре, попробуйте запрограммировать на машинном языке
программу для вашего "Спектрума", выполняющую прерывание по
нажатию клавиши "ENTER". Вам понадобится:
а) Проверить адрес ряда, который нужно загрузить в
регистр A;
б) Послать его на входной порт FEH;
в) проверить бит, устанавливаемый клавишей "ENTER".
ВЫВОД - ДИСПЛЕЙ С ТЕЛЕВИЗИОННЫМ ЭКРАНОМ
Дисплей с телевизионным экраном - основное средство
вывода для ЭВМ при общении с пользователем.
Приводимая ниже программа на машинном языке
показывает план организации памяти экрана в "Спектрум".
210040 LD HL,4000H : LOAD HL WITH START OF DISPLAY FILE *
36FF LD (HL),FFH : FILL THAT SCREEN LOCATION **
110140 LD DE,4001H : LOAD DE WITH BYTE IN DISPLAY ***
010100 LD BC,1 : BC CONTAINS NUMBER OF BYTES TO BE
: TRANSFERRED ****
EDB0 LDIR : MOVE A BLOCK LENGTH BC FROM (HL)
: TO (DE) *****
C9 RET : END OF PROGRAM ******
* загрузить в HL начало файла дисплея;
** заполнить эту ячейку экрана;
*** загрузить в DE следующий байт дисплея;
**** в BC содержится число передаваемых байтов;
***** переместить блок длиной BC из (HL) в (DE);
****** конец программы.
Загрузите приведенную выше программу в свой
"Спектрум" и выполните программу на машинном языке. В том
виде, как она написана выше, из (HL) в (DE) будет перемещен
всего один байт. Теперь давайте изменим четвертую строчку
так: LD BC,31 (011F00). Вас, возможно, удивит какие будут
высвечены первые 32 байта на экране. Обратите внимание,
вверху экрана будет проведена очень тонкая полоса. Первые 32
байта экранной памяти относятся к первому байту каждого из
первых 32 символов.
Теперь изменим эту строку так: LD BC,255 (01FF00).
Вновь, возможно, вы удивитесь. Следующий байт после 32-го не
окажется во втором ряду точек на экране. Это - первый байт 32
символа. И так далее вплоть до 256-го символа.
Сможете ли вы предсказать, куда попадет следующий
байт? Измените эту строку так: LD BC,2047 (01FF07) и
выполните программу. Вы обнаружите, что заполненной оказалась
только верхняя треть экрана.
Вы можете поэкспериментировать с этим, пользуясь
разными значениями BC вплоть до LD BC,6143 (01FF17). Таким
способом вы можете посмотреть как "Спектрум" организует
экран. Экранная память на самом деле разделена на три части.
1. Память с 4000H по 47FFH (===) первые восемь строк.
2. Память с 4800H по 4FFFH (===) вторые восемь строк.
3. Память с 5000H по 57FFH (===) третьи восемь строк.
Но не только это. Вы еще вспомните, что в "Спектрум"
каждая литера состоит из восьми 8-битовых байта, что
составляет 64 точки. Например, для литеры "!" представление
имеет вид:
0 00000000 0H
16 00010000 10H
16 00010000 10H
16 00010000 10H
16 00010000 10H
0 00000000 0H
16 00010000 10H
0 00000000 0H
Структура памяти экрана дисплея "Спектрум" такова,
что первые 256 байтов с 4000H по 40FFH соответствуют первым
байтам каждой из 256 8-байтовых литер первых восьми строк.
Далее следующие 256 байтов ячеек памяти с 4100H по 42FFH
соответствуют вторым байтам каждой 256 8-байтовых литер
первых восьми строк и т.д.
Таким образом, расположение в памяти восьми байтов,
соответствующих первой литере на экране, будет следкющим:
1ST BYTE 4000H
2ND BYTE 4100H
3RD BYTE 4200H
4TH BYTE 4300H
5TH BYTE 4400H
6TH BYTE 4500H
7TH BYTE 4600H
8TH BYTE 4700H
1ST BYTE - первый байт.
Странно, не так ли? Но приходится принимать
"Спектрум" таким, как его сконструировали.
Сможете ли вы записать восемь байтов,
соответствующих 31-й литере третьей строки экрана? Вы можете
обратиться к приложению B (карта памяти экрана). /405EH,
415EH, 425EH,......475EH/.
В соответствии с принятым нами способом выдачи на
экран ячеек памяти, соответствующие первой литере второй
группы из восьми строк, будут такими:
4800H, 4900H, 4A00H, 4B00H, 4C00H, 4D00H, 4E00H, 4F00H
Аналогичным образом, первая литера третьей группы из
восьми строк отображается восьмью байтами в следующих
ячейках:
5000H, 5100H, 5200H, 5300H, 5400H, 5500H, 5600H, 5700H.
В применении машинного языка, тем не менее, есть
определенные преимущества. Очевидные трудности стоит
преодолевать. Вот тривиальный пример из языка "Бейсик". Если
вы попытаетесь выполнить команду PRINT для вводной части
экрана (нижние две строки), то система "Бейсик" этому резко
воспротивится. Но на машинном языке у вас имеется полный
доступ ко всему экрану. Если вы более внимательно
присмотритесь к организации экрана дисплея, вы увидите, что
старший бит первого байта (H0BFB) каждой литеры определяет, к
какой из трех групп памяти литера относится.
Например:
Если 40H = (H0BFB) 41H литера находится в первой группе из
восьми строк
Если 48H = (H0BFB) 49H литера находится во второй группе из
восьми строк
Если 50H = (H0BFB) 51H литера находится в третьей группе из
восьми строк
Помимо этого три младших бита HOB (HIGH ORDER BYTE)
старшего байта определяют, к какому из восьми байтов литеры
он принадлежит.
Ситуация несколько проясняется? Посмотрите
приложение B и постарайтесь понять связь между ячейками
памяти и экраном дисплея (если она имеется?!!!).
Рассмотрим следующий пример.
Предположим, нам задан адрес 4A36H. Старший байт
адреса будет 4AH, так что:
1.Мы знаем, что он находится в пределах памяти экрана
дисплея, поскольку его значение находится между 40H и 58H.
2.Его двоичное представление имеет вид 01001010.
3.По младшим трем битам мы знаем,что он принадлежит
третьему байту позиции литеры на экране.
4,Если мы сделаем младшие три бита равными нулю, то
значение HOB будет равно 48H. Таким образом, мы знаем, что он
при- надлежит второй группе из восьми строк, т.е. средней
порции экрана.
Мы можем прийти к выводу, что заданный байт
относится к третьему байту литеры в средней порции памяти
дисплея.
Какой литере из серединной порции этот байт
принадлежит? Для ответа на этот вопрос нам потребуется знать
значение младшего байта адреса. Мы знаем, что младший байт
адреса равен 36H. Так что адрес относится к литере 36H (48 +
6), т.е. к 54 позиции, считая от первой литеры серединной
позиции. Поскольку в каждой строке 32 литеры, заданная
позиция находится во второй строке серединной порции экрана
дисплея и будет (54 - 32 + 1)-й литерой в этой строке.
Вывод, который мы можем сделать, то, что заданный
байт является третьим байтом 23-ей литеры 10-ой строки от
начала экрана.
УПРАЖНЕНИЕ
К какому байту какой литеры относится адрес 564FH?
Можете ли вы написать короткую программу, выводящую
на экран восклицательный знак? Байты, составляющие эту
литеру, приведены выше.
ВЫВОД - АТРИБУТЫ ТЕЛЕВИЗИОННОГО ЭКРАНА
Память, предназначенная для хранения атрибутов
экрана, легче для понимания, чем сама память
дисплея,поскольку у нее имеется взаимно однозначное
соответствие с литерами дисплея. Файл атрибутов расположен в
памяти с 5800H и 5AFFH. Он содержит 768 байтов, что
соответствует 24 строкам по 32 литеры каждая. Иными словами,
имеется по одному байту - атрибуту для каждой позиции
литеры.
Так, 5800H соответствует атрибуту певой литеры
первой строки, 5801H -второй литеры, 5802 - третьей,.....,
581FH - 32-ой литере первой строки.
Мы знаем, что для каждой позиции литеры на экране
имеется соответствующий байт-атрибут в памяти атрибутов,
составленный следующим образом.
Байт-атрибут
Биты 0 - 2 представляет цвет чернил от 0 до 7.
Биты 3 - 5 представляет цвет бумаги от 0 до 7.
Бит 6 повышенная яркость - 1, нормальная - 0
Бит 7 мигание - 1, отсутствие мигания - 0
УПРАЖНЕНИЕ
Каков адрес байта-атрибута, соответствующего первому
байту серединной части экрана? Каков адрес первого байта
третьей части? Ответы приведены ниже, но постарайтесь
получить их сами.
Сможете ли вы написать подпрограмму, преобразующую
заданный адрес на экране в адрес соответствующего ему
атрибута, например, 4529H? По существу вам нужно определить,
какой литере экрана соответствует этот адрес, а затем
прибавить это значение к 5800H.
Приводимая ниже программа показывает как этого
достичь быстро.
LD HL,4529H : загрузить заданный адрес в HL
LD A,H : загрузить старший байт в A
AND 18H : ловушка для битов 3 и 4 для определения
: того, какой части экрана соответствует
: адрес
SRA A : сдвиг накапливающего регистра вправо
SRA A : три раза, т.е. деление на 8. Результат
SRA A : может быть равен 0, 1 или 2 в зависимости
: от того, чему равнялся H:48H, 50H или 54H
ADD A,58H : преобразования в память атрибутов
LD H,A : В HL - адрес атрибута, т.е. H = 58H, 59H
: или 60H. L остается неизменным!!!
Вам, возможно, придется немного обдумать это.
Ответ на первое упражнение связан со способом работы
программы:
1ST CHAR. OF 1ST SCREEN SECTION=4000H ATTRIBUTE ADDRESS=5800H
1ST CHAR. OF 2ND SCREEN SECTION=4800H ATTRIBUTE ADDRESS=5900H
1ST CHAR. OF 3RD SCREEN SECTION=5000H ATTRIBUTE ADDRESS=5A00
2ND CHAR. OF 1ST SCREEN SECTION=4801H ATTRIBUTE ADDRESS=5801H
ETC....
ETC.....
1ST CHAR. OF 1ST SCREEN SECTION-1-я литера...-й части экрана
ATTRIBUTE ADDRESS - адрес атрибута
Это должно все прояснить.
ВЫВОД - ЗВУКОВОЙ СИГНАЛ
Еще одно средство связи в реальном масштабе времени,
предо- ставляемое вашей микро-ЭВМ "Спектрум" - звуковой
сигнал. Было бы глупо не воспользоваться этим средством в
полной мере.
В машинном языке "Спектрум" есть два основных
способа генерирования звука.
1. Посылка сигналов на выходной порт 254 для кассетного
магнитофона в течение определенного промежутка времени с
помощью команды OUT 254. Например, OUT (254),A.
2. Установить определенные значения в HL,DE и вызвать
программу звукового сигнала из ПЗУ для генерации сигнала.
Входные параметры:
DE - продолжительность в секундах * частота
HL - (437 500 / частота) - 30,125
потом
CALL 0385H
Преимуществом первого способа генерации звука
является отсутствие обращения к ПЗУ. Он выполняется очень
быстро, но...
Поскольку ULA постоянно обращается к первым 16К
памяти с произвольным доступом для выполнения вывода на
экран, ваша программа, если она размещена в первых 16К, будет
часто подвергаться кратковременным прерываниям.
Если программа генерирует звуковой сигнал, то звук
будет издаваться в виде непредсказуамых по продолжительности
гудков. Один из способов преодоления этой трудности состоит в
перемещении той части программы, которая генерирует звук, в
область больших значений адресов памяти, если у вас ЭВМ с
объемом памяти 48К.
Если же у вас нет ЭВМ с объемом памяти 48К, то вы
все-таки можете генерировать звук этим методом, но это не
будет "чистый звук". Вам придется применять второй способ
генерации звука, чтобы добиться нужного результата.
Обратите внимание, что при посылке значений на порт
вывода 254 они будут также влиять на цвет окаймления и
включать MIC, а также громкоговоритель, в зависимости от
посылаемого значения.
С другой стороны, программа из ПЗУ для генерирования
звука по существу позволяет вам применять из своей программы
на машинном языке команду BEEP. Вы можете считать, что в паре
регистров содержится значение продолжительности звукового
сигнала, а в HL - значение частоты. Поэкспериментируйте с
различными значениями HL и DE, пока не получите нужный вам
звук.
Ограниченность этого метода, конечно, состоит в том,
что вы не можете выйти за пределы того диапазона звуков,
которые дает возможность издавать команда BEEP.