ПРОГРАММЫ, ИСПОЛЬЗУЮЩИЕ ПРЕРЫВАНИЯ 2 РОДА.
В последнее время можно заметить следующую тенденцию. У программистов появляется все больший интерес к программам, в которых используются нетрадиционные приемы и методы, позволяющие достичь результата, недоступного при помощи традиционного подхода. Мы, со своей стороны, стараемся дать пользователям "Спектрума" как можно больше информации по этим вопросам. Это, в частности, применение прерываний второго рода. Мы уже подробно разбирали программу, переключающую символьный набор при помощи нажатия SYMBOL SHIFT + ENTER. Сейчас мы хотим поделиться с читателями еще одной программой, которая использует прерывание второго рода.
ПРОГРАММЫ "CLOCK", "COUNTER" и "TIMER" © Алексеев А.Г., 1994.
Эта программы были разработаны по мотивам программы, приведенной в "ZX Computing" 1/2 1984. В отличие от прототипа, предлагаемые программы короче и обладают дополнительными свойствами.
Что делает "CLOCK"? Она запускает внутренние часы, которые работают независимо от того, чем в данный момент занимается компьютер: выполнением программы или редактированием Бейсик-строки. Часы выводятся в правом верхнем углу экрана и исправно отсчитывают секунды. Вы спросите, для чего практически это нужно, кроме того, что это просто красиво и напоминает работу на IBM? Ну, например, таймер может использоваться для того, чтобы ограничивать время на обдумывание ответа на вопрос. Действительно, представьте, например, работу обучающей или экзаменующей программы. Задан вопрос и ожидается ввод ответа при помощи оператора INPUT. Как ограничить время, отведенное на ответ (если ученик слишком долго думает, значит, он не знает ответа). При помощи обычного Бейсика - никак. Программа может бесконечно стоять на операторе INPUT, ожидая ввода ответа. Применяя прерывания второго рода, можно прекратить это ожидание и перейти к следующему пункту программы, сделав соответствующие выводы (в зависимости от используемого сценария). Или, например, всевозможные программы-тесты, викторины, в которых регламентировано время выполнения задания. Или еще такой момент. Как известно, врачи рекомендуют ограничивать время, проводимое ребенком за дисплеем. Встроенный в программу таймер напомнит о необходимости сделать перерыв. Или работа с текстовым редактором, базой данных или набиванием и отладкой программы. Таймер напомнит Вам о том, что пора бы уже сохранить результаты работы, а то, как бы неожиданно не произошел сбой компьютера из-за какого-нибудь броска напряжения в сети или чего другого. Кроме того, можно организовать считывание показаний часов таким образом, что записываемый файл будет иметь имя согласно времени его записи, например: "23.45.05". Потом, просматривая версии программы или данных, будет проще ориентироваться в записанной информации.
В общем, на эту тему можно говорить и думать бесконечно. В основе всех этих возможностей лежит блок кодов под названием "CLOCK", который я Вам и предлагаю (см. Листинг_1).
Этот материал рассчитан на читателей, уже немного овладевших программированием в машинных кодах. Предлагается идея и демонстрационные программы. А как реализовать этот способ в конкретных программах -решать Вам.
Листинг_1.
Задание в ячейке FAFFH адреса процедуры, обслуживающей прерывание. Включ. режима прерываний 2 рода.
;Отключ. режима прерываний 2 рода.
VARS
DEFB DEFB DEFB DEFB DEFB DEFB
DEFB #FB01
;Счетчик прерываний. ;Счетчик секунд. ;Счетчик минут. ;Счетчик часов.
;Задание времени таймера - минуты.
;Задание времени таймера - часы. ;Адрес процедуры, обсл. прерывание.
FAF 9 FAFA FAFB FAFC FAFD FAFE FAFF
ADR
FB01 |
C5 |
INT |
PUSH |
BC |
;Сохранение на стеке регистров |
FB02 |
D5 |
|
PUSH |
DE |
;процессора, которые будут изме- |
FB03 |
E5 |
|
PUSH |
HL |
; нены при работе процедуры, обслу- |
FB04 |
F5 |
|
PUSH |
AF |
;живающей прерывание. |
FB05 |
21F9FA |
|
LD |
HL,#FAF 9 |
;В HL адрес счетчика прерываний. |
FB08 |
35 |
|
DEC |
(HL) |
;Уменьшение его на 1. |
FB09 |
201D |
|
JR |
NZ,#FB2 8 |
; Если не ноль, то переход на ATTR. |
FB0B |
3632 |
|
LD |
(HL),#32 |
;Исходное значение счетчика прерыв |
FB0D |
0602 |
|
LD |
B, #02 |
;Цикл для изменения секунд и минут |
FB0F |
23 |
INT1 |
INC |
HL |
FB10 |
7E |
|
LD |
A,(HL) |
FB11 |
3C |
|
INC |
A |
FB12 |
B7 |
|
OR |
A |
FB13 |
27 |
|
DAA |
|
FB14 |
FE60 |
|
CP |
#60 |
FB16 |
380F |
|
JR |
C,#FB2 7 |
FB18 |
AF |
|
XOR |
A |
FB19 |
77 |
|
LD |
(HL),A |
FB1A |
10F3 |
|
DJNZ |
#FB0F |
FB1C |
23 |
|
INC |
HL |
FB1D |
7E |
|
LD |
A,(HL) |
FB1E |
3C |
|
INC |
A |
FB2F |
B7 |
|
OR |
A |
FB2 0 |
27 |
|
DAA |
|
FB21 |
FE13 |
|
CP |
#13 |
FB23 |
3802 |
|
JR |
NZ,#FB2 7 |
FB25 |
3E01 |
|
LD |
A, #01 |
FB27 |
77 |
INT2 |
LD |
(HL),A |
В HL - адрес сч. секунд (минут). Увеличение на единицу значения секунд (минут). Преобразование в десятичный вид, удобный для хранения и индикации. Проверка на достижение макс.знач. Если не достигнуто, перех. на INT2. Иначе -
обнуление счетчика секунд (минут). Повтор действий для счетчика минут. Теперь в HL значение счетч. часов. Увеличение на единицу значения часов.
Преобразование в десятичный вид, удобный для хранения и индикации. Проверка на достижение макс.знач. Если не достигнуто, перех. на INT2. Иначе в сч. часов надо занести 1. Занесение получ. значения в счетч.
LD HL,#5818 ,-Адрес начальной позиции для вывода
;показаний в файле атрибутов экрана. LD B,#08 ; Цикл для 8 знакомест экрана.
LD (HL),#4E ,-Занесение значения в яч. атрибутов.
INC HL ;Переход к следующей ячейке.
DJNZ #FB2D ; Повторение для всех 8 знакомест.
FB32 111840 OUT LD DE,#4018 ,-Адрес начальной позиции для вывода
; показаний в дисплейном файле.
FB35 |
21FCFA |
|
LD |
HL,#FAFC |
FB38 |
0602 |
|
LD |
B, #02 |
FB3A |
7E |
OUT1 |
LD |
A,(HL) |
FB3B |
CD54FB |
|
CALL |
#FB54 |
FB3E |
3E0A |
|
LD |
A, #0A |
FB40 |
CD63FB |
|
CALL |
#FB63 |
FB4 3 |
2B |
|
DEC |
HL |
FB4 4 |
10F4 |
|
DJNZ |
#FB3A |
FB4 6 |
7E |
|
LD |
A,(HL) |
FB4 7 |
CD54FB |
|
CALL |
#FB54 |
В HL - адрес счетчика часов. Вывод часов (минут). В аккумуляторе - часы (минуты) Печать значения часов (минут) В акк. - код для печати ":". Печать разделителя ":". Переход к счетчику минут (секунд). Повторение для печати минут. Теперь в HL адрес счетчика секунд. Печать значения секунд.
FB4A CD80FB CONTROL CALL #FB80 ;Проверка срабатывания таймера.
FB4D |
F1 |
END |
POP |
AF |
; Восстановление со стека значений |
FB4E |
E1 |
|
POP |
HL |
;регистров процессора, которые были |
FB4F |
D1 |
|
POP |
DE |
; изменены при работе процедуры, об- |
FB5 0 |
C1 |
|
POP |
BC |
; служивающей прерывание. |
FB51 |
C33800 |
|
JP |
#0038 |
; Переход на продолжение обработки |
; прерывания, как это было бы при ; обычной работе компьютера.
FB54 |
F5 |
PRINT |
PUSH |
AF |
FB55 |
CB3F |
|
SRL |
A |
FB57 |
CB3F |
|
SRL |
A |
FB59 |
CB3F |
|
SRL |
A |
FB5B |
CB3F |
|
SRL |
A |
FB5D |
CD63FB |
|
CALL |
#FB63 |
FB60 |
F1 |
|
POP |
AF |
FB61 |
E60F |
|
AND |
#0F |
FB63 |
C5 |
PRN |
PUSH |
BC |
FB64 |
D5 |
|
PUSH |
DE |
Сохранение значения А на стеке. Выделение старшего полубайта показаний часов, минут или секунд. Старший полубайт соотв. "десяткам", а младший - "единицам" показаний. Печать символа на экране. Восстановление значения А. Выделение старшего полубайта показаний. Печать его выполняется прямым проходом на процедуру PRN.
;Сохранение на стеке регистров ;процессора, которые будут изме-
FB65 |
E5 |
|
PUSH |
HL |
нены при работе процедуры печати. |
FB66 |
87 |
|
ADD |
A, A |
Умножение кода символа на 8, выпол |
FB67 |
87 |
|
ADD |
A, A |
няемое при помощи троекратного пов |
FB68 |
87 |
|
ADD |
A, A |
торения сложения А с ним же самим. |
FB69 |
C680 |
|
ADD |
A, #80 |
Увеличение на 128 для получения дистанции от начала симв. набора. |
FB6B |
2117FA |
|
LD |
HL,#FA17 |
В HL значение сист. перем. CHARS. |
FB6E |
0601 |
|
LD |
B, #01 |
Получение в ВС величины дистанции |
FB70 |
4F |
|
LD |
C,A ; |
от CHARS до начала симв. набора. |
FB71 |
09 |
|
ADD |
HL, BC |
Теперь в HL адрес начала образа символа в символьном наборе. |
FB72 |
0608 |
|
LD |
B, #08 |
Цикл для 8 байт (линий) символа. |
FB74 |
7E |
PRN1 |
LD |
A,(HL) |
Занесение байта в аккумулятор |
FB75 |
2F |
|
CPL |
|
и инвертирование его перед печатью |
FB7 6 |
12 |
|
LD |
(DE),A |
Перенесение байта в диспл. файл. |
FB77 |
23 |
|
INC |
HL |
Перех. к след. байту симв. набора. |
FB78 |
14 |
|
INC |
D |
Перех. к след. линии в диспл.файле |
FB7 9 |
10F9 |
|
DJNZ |
#FB74 |
Повтор для печати 8 линий символа. |
FB7B |
E1 |
|
POP |
HL |
Восстановление со стека регистров |
FB7C |
D1 |
|
POP |
DE |
процессора, которые были изменены |
FB7D |
C1 |
|
POP |
BC |
при работе процедуры печати. |
FB7E |
13 |
|
INC |
DE |
Переход к следующему знакоместу. |
FB7F |
C9 |
|
RET |
|
|
FB80 |
2AFBFA |
ALARM |
LD |
HL,(#FAFB) |
Текущее значение минут и часов. |
FB83 |
ED5BFDFA |
|
LD |
DE,(#FAFD) |
Заданные значения для таймера. |
FB87 |
B7 |
|
OR |
A |
Сброс флага переноса. |
FB88 |
ED52 |
|
SBC |
HL, DE |
Сравнение показаний вычитанием. |
FB8A |
2804 |
|
JR |
Z,#FB90 |
Если результат нулевой, то перех. на ALARM1 для установки мигания. |
FB8C |
3E4E |
|
LD |
A, #4E |
Иначе - отключение мигания. |
FB8E |
1802 |
|
JR |
#FB92 ; |
Переход на ALARM2 |
FB90 |
3ECE |
ALARM1 |
LD |
A, #CE |
Подготовка атрибутов для включения режима мигания (FLASH=1). |
FB92 |
322EFB |
ALARM2 |
LD |
(#FB2E) ,A |
Изменение значения параметра, заносимого в файл атрибутов. |
FB95 |
C9 |
|
RET |
|
|
При работе программы определены следующие ячейки для хранения переменных. FAF9H (64249) счетчик прерываний
FAFAH (64250) счетчик секунд FAFBH (64251) счетчик минут FAFCH (64252) счетчик часов FAFDH (64253) задание таймера:мин. fafeh (64254) задание таймера:час.
Переключение на режим прерываний второго рода происходит при выполнении процедуры инициализации START с адреса FAEFH (64239), где задается адрес обслуживающей процедуры. Точнее, старший байт этого адреса. Младший байт в "Спектруме" всегда будет равен FFH. При прерывании будет считан адрес, записанный в FAFFH. Он равен FB01. Это начало обслуживающей процедуры INT.
При переходе программы на этот адрес, прежде всего на стеке сохраняются значения всех регистров процессора, которые будут изменены при работе обслуживающей процедуры. Далее в регистр HL загружается адрес ячейки, в которой содержится счетчик прерываний. Первоначально в этой ячейке было задано число 32Н (50). Мы знаем, что прерывание происходит 50 раз в секунду. Поэтому, уменьшая при каждом прерывании значение этого счетчика, через 50 прерываний получим ноль, что и послужит сигналом для увеличения на 1 счетчика секунд. Практически это происходит так. Если после уменьшения на единицу счетчик имеет ненулевое значение, то происходит переход на ATTR - это продолжение работы обслуживающей процедуры. Если же в счетчике 0, то туда опять записывается число 32Н (50) для следующего цикла.
Далее организуются два цикла для начисления секунд и минут (так как в обоих случаях требуется вести подсчет значений от 0 до 59). Это происходит так. Увеличивается на единицу значение HL, теперь оно содержит адрес счетчика секунд. Так как зафиксирован факт прохождения 50 прерываний, то счетчик секунд увеличивается на единицу (для этого его значение загружается в регистр А). Полученное значение преобразуется в десятичную форму для удобства контроля и индикации. Преобразование выполняется при помощи команды DAA. При этом происходит перестроение содержимого аккумулятора из двоичной формы в десятичную. Это показано на примере:
00 ^ DAA ^ 00
01 |
01 |
09 |
09 |
0A |
10 |
29 |
29 |
2А |
30 |
и т.д.
Этот прием позволяет хранить информацию о часах, минутах и секундах, состоящую из двух байтов, в одной ячейке памяти. В старшем полубайте находится значение десятков, а в младшем полубайте единиц. Посмотрите на правый столбец приведенного примера это готовые данные для вывода на экран.
Команда OR A, выполняемая непосредственно перед DAA, необходима для того, чтобы гарантированно сбросить флаг переноса. При ее отсутствии, и даже в случае применения для этой цели команд AND A или CP A, часы правильно функционировали в нормальном режиме работы "Спектрума", однако при работе с интерфейсом принтера LPRINT III, возникали сбои в показаниях часов.
После преобразования в десятичный вид происходит проверка полученного результата на достижение максимального значения: 60. Если оно не достигнуто, то переход на ATTR, а если достигнуто значение 60, то счетчик секунд обнуляется и происходит возврат для повторения цикла контроля значения минут точно так же, как это было выполнено для секунд. Если значение минут равно 60, то счетчик минут обнуляется и далее происходит контроль значения часов. Здесь имеются некоторые особенности. В приведенном листинге часы работают в 12-часовом режиме. Если значение часов стало равно 13, то оно принимает значение 1 (один час дня). Вы можете по желанию изменить режим работы часов на 24-часовой. Для этого надо изменить команды по адресам FB21H и FB25H: FB21 FE24 CP #24
FB25 3E00 LD A,#00
Что означает:
Проверка на достижение макс. значения.
Иначе в счетчик часов надо занести 0.
После того, как выполнено преобразование и контроль значений секунд, минут и часов, продолжается выполнение обслуживающей процедуры - вывод показаний часов на экран. Вначале производится изменение атрибутов тех знакомест, где будет произведена печать. Это выполняется процедурой ATTR. Адрес в файле атрибутов, соответствующий первому знакоместу с часами, задается в регистре HL. Затем, при помощи восьмикратного повторения, нужные атрибуты байт за байтом заполняются заданным значением 4ЕН. Это значение соответствует желтым чернилам, синей бумаге и повышенной яркости: 6+1*8+1*64=78=4ЕН.
Процедура OUT выполняет печать значений часов, минут и секунд на экране. Адрес в дисплейном файле, куда будет производиться вывод, задается в регистре DE. Значение 4018Н соответствует верхней пиксельной линии в знакоместе с координатами AT 0,24; сюда будут выводиться показания часов. Адрес счетчика часов задается в регистре HL. Значение часов загружается в регистр A и выполняется подпрограмма печати PRINT, которая выполняет печать показаний часов в виде двух знаков: десятков и единиц.
Далее в регистр A загружается код 0АН. Как будет пояснено дальше, этот код соответствует разделителю ":". Его печать выполняется непосредственно процедурой PRN, так как печатать предстоит только один знак. Затем значение HL уменьшается на единицу, принимая значение счетчика минут и происходит возврат на повтор PRINT и PRN для печати минут и опять разделителя ":". После следующего уменьшения значения HL, в нем будет адрес счетчика секунд, которые печатаются аналогично при помощи процедуры PRINT.
Затем выполняется процедура CONTROL, которая состоит всего из одной команды: вызов подпрограммы ALARM. Она сравнивает показания текущего значения часов с заданным. Если эти значения не равны, то происходит возврат из подпрограммы, ничего не делая. Представим как раз такую ситуацию и пока что пропустим эту подпрограмму, поскольку особый разговор о ней будет впереди. Кстати, если Вы не предполагаете пользоваться таймером, а только лишь часами, то можете вместо вызова подпрограммы ALARM подставить в ячейках с адреса FB4AH три команды NOP и выбросить вообще подпрограмму ALARM, несколько сократив размеры кодового блока.
Процедурой END заканчиваются операции по контролю значения часов и выводу их показаний на экран и выполняются действия по завершению подпрограммы INT, обслуживающей прерывание: восстановление со стека значений регистров процессора и переход на обычную для "Спектрума" процедуру обработки прерываний первого рода 0038Н.
Теперь рассмотрим подробно процедуры печати PRINT и PRN. Первая выполняет печать двух символов ("десятков" и "единиц") значений часов, минут и секунд. Для этого, прежде всего, происходит выделение старшего полубайта, соответствующего "десяткам". Значение регистра A запоминается на стеке, после чего путем выполнения четырех команд логического сдвига вправо выделяется старший полубайт (младший полубайт теряется в процессе сдвига). Печать полученного значения "десятков" выполняется вызовом PRN. Далее со стека восстанавливается значение регистра A и происходит выделение младшего полубайта, для чего происходит зануление 4-х старших битов при помощи команды AND. Печать полученного значения "единиц" выполняется путем прямого прохода на PRN.
ШЭфессмонялъяыйЖОЮХОЮ
Процедура PRN выполняет печать символа, код которого находится в регистре A. Для этого производится вычисление адреса в символьном наборе, с которого расположено изображение требуемого символа. Это происходит следующим образом. Так как на каждый символ отведено по 8 байтов, то, умножив на 8 то число, которое должно быть, выведено, получим шаг для расчета требуемого адреса. Умножение выполняется путем последовательного выполнения трех команд сложения содержимого аккумулятора с ним же самим. Сложив полученное значение с числом 80Н (128), получим дистанцию требуемого символа от первого символа символьного набора (пробела). Далее в регистр HL помещается значение системной переменной CHARS. А в регистре ВС задается дистанция: в C -расстояние от начала символьного набора, а вместе со значением регистра В получится расстояние от адреса, определенного системной переменной CHARS. Сложив значение CHARS (находится в HL) с дистанцией (находится в ВС), получим адрес, соответствующий печатаемой цифре. Так для печати символа "0" в регистре A должен быть код 00Н, для печати "1", в A должен быть код 01Н и так далее до "9". Так как в символьном наборе после символа "9" расположен символ ":", то, задав код 10 (0АН), можно выполнить печать разделителя.
Несколько слов надо сказать о том, какими символами будет производиться вывод показаний часов. По адресу FB6BH находится команда, задающая значение CHARS. Если поместить туда такую команду: FB6B 21003C LD HL,#3C00
то вывод будет производиться всегда символами символьного набора из ПЗУ. Интереснее, конечно, если вывод будет производиться символами загружаемого стилизованного символьного набора. Для этого в команде по адресу FB6BH подставьте значение, соответствующее загруженному символьному набору, применяемому в Вашей программе (имеется в виду та программа, в которую Вы встраиваете часы). Теперь, независимо от того, какой символьный набор в данный момент включен, в Вашей программе, вывод будет всегда осуществляться символами загруженного символьного набора. Заменив эту команду на такую: FB6B 2A365C LD HL,(#5C36)
получим вывод часов символами того символьного набора, который в данный момент является активным в Вашей программе.
В приведенной программе я использовал для часов специальный символьный набор. Это легко выполняется, так как требуется всего 10 цифр от 0 до 9 и плюс один символ, изображающий разделитель ":", то есть всего 11 символов или, что то же самое, 88 дополнительных байтов памяти. Зато появляется возможность изображения символов в оригинальном, стилизованном под электронные часы виде. Кроме того, вместо разделителя ":" можно задать любой другой, например, "." или еще какой-нибудь, какой Вам больше нравится. Для этого надо изменить 8 байтов, определяющих образ этого символа в дополнительном символьном наборе (см. в листинге ниже с адреса FBE7H). Для примера привожу такой набор символов специально для часов. Он располагается с адреса FB97H:
FB97 |
00 |
7E |
46 |
46 |
46 |
46 |
7E |
00 |
"0" |
FB9F |
00 |
18 |
18 |
18 |
18 |
18 |
18 |
00 |
"1" |
FBA7 |
00 |
7E |
06 |
06 |
7E |
40 |
7E |
00 |
"2" |
FBAF |
00 |
7E |
06 |
3E |
06 |
06 |
7E |
00 |
"3" |
FBB7 |
00 |
46 |
46 |
46 |
7E |
06 |
06 |
00 |
"4" |
FBBF |
00 |
7E |
40 |
7E |
06 |
06 |
7E |
00 |
"5" |
FBC7 |
00 |
7E |
60 |
7E |
62 |
62 |
7E |
00 |
"6" |
FBCF |
00 |
7E |
06 |
06 |
06 |
06 |
06 |
00 |
"7" |
FBD7 |
00 |
7E |
46 |
7E |
46 |
46 |
7E |
00 |
"8" |
FBDF |
00 |
7E |
46 |
46 |
7E |
06 |
7E |
00 |
" 9' |
FBE7 |
00 |
00 |
18 |
18 |
00 |
18 |
18 |
00 |
" : " |
Значение CHARS для этого дополнительного символьного набора будет равно FA17H. Здесь учтен тот факт, что символ "0" должен быть расположен, отступая 16 символов от начала символьного набора - символа "ПРОБЕЛ". Команда по адресу FB6BH (см. листинг блока кодов "CLOCK") задает это значение.
После определения символьного набора, надо переписать в дисплейный файл 8 байтов из него, соответствующие требуемому символу. Для этого организуется цикл из 8 повторений. Адрес в дисплейном файле, соответствующий позиции для вывода, как мы помним, перед вызовом PRINT был задан в регистре DE.
В аккумулятор загружается значение байта из символьного набора, затем это значение инвертируется путем выполнения команды CPL, затем заносится в дисплейный файл. Для перехода к следующему адресу в символьном наборе значение HL увеличивается на единицу, а для перехода к следующей пиксельной линии в дисплейном файле значение DE увеличивается на 256 (путем увеличения на 1 значения D). После завершения печати всех восьми пиксельных линий возвращаются со стека значения регистров, и происходит увеличение на 1 содержимого DE, что соответствует переходу к печати в следующем знакоместе.
По адресу FB75H находится команда CPL, которая инвертирует изображение выводимых символов. Так, если были заданы желтые чернила и синяя бумага, то выведены будут синие цифры на желтом фоне. Можно заменить команду CPL командой NOP, тогда инверсии не будет. В чем тут разница и как это практически использовать. Ну, например, Вы не хотите менять атрибуты тех знакомест, где будет производиться вывод часов. Вы можете это сделать, заменив командами NOP область с адреса FB28H по адрес FB31H включительно.
Теперь перейдем к подпрограмме ALARM. В общем, все делается ради нее. От того, для чего будет, в конечном счете, предназначена программа "CLOCK", зависит эта процедура. Здесь (Листинг_1) выполняются действия, связанные с реакцией на срабатывание таймера. Для примера приведена простая подпрограмма, которая служит скорее для демонстрационных целей, чем для практического применения. Вы сами должны будете придумать это звено, связующее блок кодов "CLOCK" с Вашей собственной программой, в которую Вы встраиваете часы. Забегая вперед, скажу, что при рассмотрении следующей программы "TIMER", будет дан практический пример по ограничению времени ввода параметров при помощи оператора INPUT.
Итак, при выполнении программы, обслуживающей прерывание, с адреса FB4AH мы попадаем на подпрограмму ALARM. Здесь происходит следующее. В регистр HL загружается текущее значение времени, а в регистр DE - заданное значение срабатывания таймера. Теперь вычисляем их разность (предварительно для получения правильного результата надо сбросить флаг переноса, что выполняется командой OR A). В том случае, когда разность не равна нулю, в ячейку, отвечающую за атрибуты (FB2EH), заносится значение 4ЕН, а если разность равна нулю, то заносится CEH. Новое значение отличается тем, что у него включен 7-й бит, отвечающий за мигание (FLASH 1). На практике это означает, что когда показания часов и минут совпадут со значениями, заданными для срабатывания таймера, то в течение одной минуты изображение часов будет мигать, обращая на себя Ваше внимание. Через одну минуту показания вновь разойдутся и мигание прекратится.
Можно только включать мигание и не выключать его, применив другой вариант подпрограммы ALARM, он немного короче - см. Листинг_2.
В этом варианте (Листинг_2), если разность HL и DE не равна нулю, то сразу происходит выход из подпрограммы, а если равна нулю, то в ячейку, отвечающую за атрибуты, заносится CEH. В дальнейшем это значение не изменяется и мигание будет происходить все время.
Для остановки часов служит подпрограмма STOP. Вызвать ее можно, подав прямую команду: RANDOMIZE USR 64246. При этом происходит восстановление режима прерываний первого рода (обычного режима работы "Спектрума" и, следовательно, остановка часов.
Бейсик-программа, показывающая часы-таймер в действии, может выглядеть, например, следующим образом:
1 GO TO 100: REM : 64239 - START 64246 - STOP
2 CLEAR 64238: LOAD "clock" CODE
3 LOAD "chr" CODE 64600
4 RUN
8 POKE 23606,88: POKE 23607,251: RETURN
9 POKE 23606,0: POKE 23607,60: RETURN 100 RANDOMIZE USR 64246
110 BORDER 7: PAPER 7: INK 0: CLS 120 GO SUB 8
200 PRINT "ВВЕДИТЕ ТЕКУЩЕЕ ВРЕМЯ"'"(ЧАСЫ, МИНУТЫ, СЕКУНДЫ)"''"ПРИМЕР: 08.45.00"''"
ENTER - ОТСЧЕТ ВРЕМЕНИ С НУЛЯ." 210 INPUT LINE T$: IF T$="" THEN LET T$="00.00.00" 220 POKE 642 52,VAL T$(1)*16+VAL T$(2) 230 POKE 642 51,VAL T$(4)*16+VAL T$(5) 240 POKE 642 5 0,VAL T$(7)*16+VAL T$(8)
300 PRINT ' ' '"ЗАДАЙТЕ ВРЕМЯ ТАЙМЕРА"'" (ЧАСЫ И МИНУТЫ) "' '"ПРИМЕР: 08.4 6"' ' " ENTER -
ТАЙМЕР НЕ НУЖЕН." 310 INPUT LINE T$: IF T$="" THEN LET T$="24.60" 320 POKE 642 54,VAL T$(1)*16+VAL T$(2) 330 POKE 642 53,VAL T$(4)*16+VAL T$(5)
340 IF T$<>"2 4.60" THEN PRINT AT 1,0;TAB 16; INK 6; PAPER 2; BRIGHT 1,-"ТАЙМЕР: ";T$(1
TO 2);":";T$(4 TO 5);":";"00" 1000 PRINT AT 18,0,-"ДЛЯ ОСТАНОВКИ ЧАСОВ ВЫПОЛНИТЕ:"''" RANDOMIZE USR 64246"
2000 POKE 64249,50: RANDOMIZE USR 64239
Листинг_2.
FB80 |
2AFBFA ALARM |
LD |
HL,(#FAFB) |
;Текущее значение минут и часов. |
FB83 |
ED5BFDFA |
LD |
DE,(#FAFD) |
;Заданные значения для таймера. |
FB87 |
B7 |
OR |
A |
;Сброс флага переноса. |
FB88 |
ED52 |
SBC |
HL, DE |
;Сравн. показаний, вычитанием. |
FB8A |
C0 |
RET |
NZ |
; Если результат нулевой - выход. |
FB8B |
3ECE |
LD |
A, #CE |
;Подготовка атрибутов с включенным ;режимом мигания (FLASH 1). |
FB8D |
322EFB |
LD |
(#FB2E) ,A |
; Изменение значения параметра, ;заносимого в файл атрибутов. |
FB90 |
C9 |
RET |
|
; |
Блок кодов "CLOCK", листинг которого приведен выше, оформлен в файл "clock" CODE 64239,256. Для печати сообщений на русском языке используется русский символьный набор - файл "chr" CODE 64600,768. Включается он командой GO SUB 8. Автостарт программы выполняется со строки 2, где происходит загрузка этих файлов.
После старта программа запрашивает текущее время и время срабатывания таймера. Если ничего не вводя, нажимать ENTER, то отсчет часов будет начат с 00:00:00, а таймер будет вообще отключен (см. содержимое FAFDH -FAFEH).
ШЭфессмонялъяыйЖОЮХОЮ
* * *
Если немного модифицировать программу "CLOCK", то можно использовать ее для отсчета времени в минутах, секундах и сотых долях секунд (точнее, дискретность равна 0.02 сек, так как прерывания происходят 50 раз в секунду). В качестве примера, показывающего, как можно практически использовать программу - часы, рассматривается вариант "COUNTER", позволяющий ограничить время, отведенное для ввода ответа при помощи оператора INPUT в Бейсике. Цифровое табло в углу экрана будет показывать, сколько времени играющий обдумывает ответ. Причем по условиям игры на это ему отводится ограниченное время. Когда заданное время истечет, ввод параметра при помощи INPUT будет завершен принудительной подачей команды ENTER. Счетчик при этом будет остановлен и его показания можно использовать для оценки способностей играющего.
Процедуры START, STOP, ATTR, CONTROL, PRINT, PRN - те же, что и в блоке кодов "CLOCK". Отличаются процедуры INT и ALARM (см. Листинг_3), а также несколько иное назначение имеют системные ячейки VARS.
Листинг_3.
Преобразование значения в десятичный вид, удобный для индикации. Занесение в счетчик сотых долей. Если переполнения счетчика не было, то переход на INT2. Цикл изменения секунд и минут. Теперь в HL адрес сч. секунд (минут). Увеличение его на единицу.
Преобразование значения в десятичный вид, удобный для индикации. Проверка на достижение макс.значения. Если не достигнуто, переход на INT2. Иначе -
обнуление счетчика. Повторение для счетчика минут. Занесение получ. значения в счетчик.
Измененная процедура INT.
FB01 |
C5 |
INT |
PUSH |
BC |
;Сохранение на стеке значений регист |
FB02 |
D5 |
|
PUSH |
DE |
ров процессора, которые будут изме- |
FB03 |
E5 |
|
PUSH |
HL |
; нены при работе процедуры, обслужи- |
FB04 |
F5 |
|
PUSH |
AF |
; вающей прерывание. |
FB05 |
21F9FA |
|
LD |
HL,#FAF 9 |
; В HL адрес счетчика прерываний. |
FB08 |
00 |
|
NOP |
; |
|
FB0F |
00 |
|
NOP |
; |
|
FB10 |
7E |
|
LD |
A,(HL) |
; В А - значение счетчика прерываний. |
FB11 |
3C |
|
INC |
A |
;Уменьшение его на 2, т.к. прерывание |
FB12 |
3C |
|
INC |
A |
; происходит 50 раз в секунду. |
FB13 FB14 FB15 FB16
B7 27 77
200F
OR DAA LD JR
A
FB18 0602 LD B, #02
FB1A 23 INT1 INC HL
FB1B 7E LD A, (HL)
FB1C 3C INC A
FB1D B7 OR A
FB1E 27 DAA
FB1F FE60 CP #60
FB21 3804 JR C,#FB27
FB2 3 AF XOR A
FB24 77 LD (HL) ,A
FB25 10F3 DJNZ #FB1A
FB27 77 INT2 LD (HL) , A
Процедура OUT отличается только одной командой:
FB35 21FBFA LD HL,#FAFB ; В HL - адрес счетчика минут.
Измененная подпрограмма ALARM:
FB80 |
2AFBFA ALARM |
LD |
HL,(#FAFB) |
; Текущее значение минут и часов. |
FB83 |
ED5BFDFA |
LD |
DE,(#FAFD) |
; Заданные значения для таймера. |
FB87 |
B7 |
OR |
A |
;Сброс флага переноса. |
FB88 |
ED52 |
SBC |
HL, DE |
;Сравнение показаний, вычитанием. |
FB8A |
C0 |
RET |
NZ |
; Выход, если не ноль. |
FB8B |
3E0D |
LD |
A, #0D |
; В аккумуляторе код "ENTER". |
FB8D |
32085C |
LD |
(#5C08),A |
;Занесение кода "ENTER" в LAST_K. |
FB90 |
FDCB01EE |
SET |
5,(IY+1) |
; Установка 5 бита FLAGS, которое |
|
|
|
|
;имитирует сигнал "клавиша нажата" |
FB94 |
C3F6FA |
JP |
#FAF 6 |
;Остановка таймера, отключение IM2 |
Системные ячейки для варианта "COUNTER": FAF9H (64249) счетчик сотых долей секунд FAFAH (64250) счетчик секунд FAFBH (64251) счетчик минут FAFCH (64252) не используется
fafdh (64253) задание таймера:сек. FAFEH (64254) задание таймера:мин.
Перед запуском "COUNTER" надо в системные ячейки FAFDH и FAFEH занести время, на которое устанавливается таймер. Запуск программы происходит так же, как и в предыдущем случае, вызовом процедуры START. Отличия начинаются в процедуре обработки прерываний INT.
Так как в секунду происходит 50 прерываний, то показания сотых долей секунд должны уменьшаться на 0.02 за одно прерывание. Для этого команда DEC (HL) используется два раза.
При подсчете количества прерываний после команд INC используется преобразование в десятичный вид при помощи DAA, поэтому проверка переполнения выполняется очень просто - при помощи проверки на ноль. При работе DAA показания будут меняться следующим образом:
00 ^ DAA ^ 00
01 01
99 99
9A ^ DAA ^ 100 - то есть 00, так
как счет в шестнадцатеричной системе.
Некоторое отличие есть теперь в процедуре OUT, где в регистр HL заносится теперь адрес первого выводимого показания, соответствующего минутам, а не часам, как в программе "CLOCK" (см. команду по адресу FB35H).
Отличается также подпрограмма ALARM, которая теперь в случае совпадения текущего времени с заданным, выполняет действия, имитирующие нажатие клавиши ENTER. Это происходит следующим образом. В системную переменную LAST_K заносится значение 0DH, соответствующее коду "ENTER". После этого устанавливается 5-бит системной переменной FLAGS, что имитирует сигнал "нажата клавиша". Далее выполняется переход на процедуру STOP, которая отключает режим прерываний второго рода, восстанавливая режим прерываний первого рода. После возврата по RET из этой подпрограммы произойдет переход в точку вызова программы ALARM, то есть на адрес FB4DH процедуру END. После восстановления со стека значений регистров, происходит переход на адрес 0038Н, где выполняются точно такие же действия, какие были бы выполнены при нажатии на клавишу ENTER.
В приведенном варианте отсчет времени будет выполняться от 00:00:00 до 59:59:98 (то есть до 1 часа), после чего, если срабатывание таймера заблокировано, показания опять станут нулевыми. Блокировка достигается, если занести в одну из ячеек FAFDH или FAFEH число, большее 59Н.
Если несколько изменить программу: FB23 3600 LD (HL),#00
то отсчет времени будет производиться от нуля до 99.59.98 (то есть до 100 минут).
С адреса FB97H так же, как и в программе "CLOCK", находится символьный набор часов. Приведенная ниже Бейсик-программа, позволяет наглядно продемонстрировать работу программы "COUNTER".
1 GO TO 100: REM : 64239 - START 64246 - STOP
2 CLEAR 64238: LOAD "count"CODE
3 LOAD "chr"CODE 64600
4 RUN
8 POKE 23606,88: POKE 23607,251: RETURN
9 POKE 23606,0: POKE 23607,60: RETURN
40 PRINT AT 0,0; INK 6; PAPER 1; BRIGHT 1; INVERSE 1;" ВЫ УЖЕ ДУМАЕТЕ: "
42 FOR A= 642 4 9 TO 64252: POKE A,0: NEXT A 44 RANDOMIZE USR 64239 49 RETURN
5 0 PRINT AT 0,0;" " 54 RANDOMIZE USR 64246
59 RETURN
100 RANDOMIZE USR 64246
110 BORDER 7: PAPER 7: INK 0: CLS
120 GO SUB 8
300 PRINT '''"ЗАДАЙТЕ ВРЕМЯ ТАЙМЕРА"'"(МИНУТЫ И СЕКУНДЫ)"''"ПРИМЕР: 00.30"''"ИЛИ
НАЖМИТЕ ENTER " 310 INPUT LINE T$: IF T$="" THEN LET T$="00.30" 320 POKE 642 54,VAL T$(1)*16+VAL T$(2) 330 POKE 642 53,VAL T$(4)*16+VAL T$(5)
340 PRINT INK 6; PAPER 2; BRIGHT 1;AT 1,16;"ТАЙМЕР: ";T$(1 TO 2);":";T$(4 TO 5);":";"00"
1000 PRINT AT 11,0;" ПРОГРАММА ДЕМОНСТРИРУЕТ ЭФ-ФЕКТ ОГРАНИЧЕНИЯ ВРЕМЕНИ ДЛЯ ОБДУМЫВАНИЯ ОТВЕТА ПРИ ВВОДЕ ПАРА-МЕТРА ПРИ ПОМОЩИ INPUT." 1010 PRINT " ПО ДОСТИЖЕНИИ ЗАДАННОГО ВРЕ-МЕНИ, ВВОД БУДЕТ ПРИНУДИТЕЛЬНОЗАВЕРШЕН ПОДАЧЕЙ КОМАНДЫ ENTER"
ШЭ&ЕССМОНЯЛЪШ1Йжоюхою
2000 PRINT '"ВВЕДИТЕ ЧТО-НИБУДЬ ДЛЯ ПРИМЕРА, (НО ENTER - НЕ НАЖИМАЙТЕ):" 2010 GO SUB 40: INPUT LINE A$: GO SUB 50
3000 PRINT INK 7; PAPER 2; BRIGHT 1; INVERSE 1;AT 0,0;" ВЫ ",-("НИЧЕГО НЕ " AND A$="");"ВВЕЛИ "; BRIGHT 0'A$
Теперь предлагаю несколько видоизмененный вариант программы, который называется "TIMER". Его отличие от приведенной выше программы "COUNTER" в том, что отсчет времени осуществляется от заданного времени в сторону уменьшения, пока результат не станет, равен нулю.
Процедуры START, STOP, ATTR, CONTROL, PRINT, PRN - те же, что и в блоках кодов "CLOCK" и "COUNTER". Отличаются процедуры INT, ALARM (см. Листинг_4) и назначение системных ячеек VARS.
Листинг_4.
Измененная процедура INT.
FB01 |
C5 |
INT |
PUSH |
BC ; |
Сохранение на стеке значений регист |
FB02 |
D5 |
|
PUSH |
DE ; |
ров процессора, которые будут изме |
FB03 |
E5 |
|
PUSH |
HL ; |
нены при работе процедуры, обслужи |
FB04 |
F5 |
|
PUSH |
AF ; |
вающей прерывание. |
FB05 |
21F9FA |
|
LD |
HL,#FAF9 |
В HL адрес счетчика прерываний. |
FB08 |
35 |
|
DEC |
(HL) |
Уменьшение его на 2, т.к. прерывание |
FB09 |
35 |
|
DEC |
(HL) |
происходит 50 раз в секунду. |
FB0A |
3EF0 |
|
LD |
A,#F0 ; |
Проверка, был ли |
FB0C |
BE |
|
CP |
(HL) |
переход через ноль. |
FB0D |
300F |
|
JR |
NC,#FB1E |
Если не было, то переход на DEC для перевода показаний в десятичный вид. |
FB0F |
3698 |
|
LD |
(HL),#98 |
Занесение начального значения в счетчик сотых долей секунд. |
FB11 |
0602 |
|
LD |
B, #02 |
Цикл изменения секунд и минут. |
FB13 |
23 |
INT1 |
INC |
HL ; |
В HL адрес счетчика секунд (минут). |
FB14 |
35 |
|
DEC |
(HL) |
Уменьшение значения счетчика на 1. |
FB15 |
3EF0 |
|
LD |
A, #F0 |
Проверка, был ли |
FB16 |
BE |
|
CP |
(HL) |
переход через ноль. |
FB18 |
3004 |
|
JR |
NC,#FB1E |
Если не было, то переход на DEC. |
FB1A |
3659 |
|
LD |
(HL),#59 |
Занесение в счетчик начального знач. |
FB1C |
10F5 |
|
DJNZ |
#FB13 ; |
Повторение для счетчика минут. |
FB1E |
7E |
DEC |
LD |
A,(HL) |
; Преобразование значения |
|
FB1F |
B7 |
|
OR |
A |
; в десятичный вид, удобный |
|
FB2 0 |
27 |
|
DAA |
|
; для хранения и индикации. |
|
FB21 |
BE |
|
CP |
(HL) |
;Если в результате работы DAA значение |
FB22 |
2804 |
|
JR |
Z,#FB2 8 |
; было изменено, то значит был переход |
FB24 |
7E |
|
LD |
A,(HL) |
;через ноль (0 -> F), поэтому надо |
вы- |
FB25 |
D606 |
|
SUB |
#06 |
;честь "6" для получения "9" (F -> |
9) . |
FB27 |
77 |
|
LD |
(HL),A |
;Коррекция значения в счетчике. |
|
Измененная подпрограмма |
ALARM |
|
|
|
FB80 |
21F9FA |
ALARM |
LD |
HL,#FAF 9 |
;В HL адрес начала VARS. |
|
FB83 |
7E |
|
LD |
A,(HL) |
;Проверка на достижение нулевого |
|
FB84 |
23 |
|
INC |
HL |
; значения счетчиков сотых долей |
|
FB85 |
B6 |
|
OR |
(HL) |
; секунды, секунд и минут, а так- |
|
FB86 |
23 |
|
INC |
HL |
;же неиспользуемой ячейки FAFCH, |
|
FB87 |
B6 |
|
OR |
(HL) |
; которая может использоваться |
|
FB88 |
23 |
|
INC |
HL |
;для отключ. таймера, если туда |
|
FB89 |
B6 |
|
OR |
(HL) |
; занести ненулевое значение. |
|
FB8A |
C0 |
|
RET |
NZ |
; Выход, если не ноль. |
|
FB8B |
3E0D |
|
LD |
A, #0D |
; В аккумуляторе код "ENTER". |
|
FB8D |
32085C |
|
LD |
(#5C08),A |
;Задание кода "ENTER" в LAST_K. |
|
FB90 |
FDCB01EE |
|
SET |
5,(IY+1) |
;Установка 5 бита FLAGS, имити- |
|
|
|
|
|
|
;рует сигнал "клавиша нажата". |
|
FB94 |
C3F6FA |
|
JP |
#FAF 6 |
; Остановка таймера, отключение |
|
|
|
|
|
|
; прерываний 2 рода. |
|
|
Системные |
ячейки для варианта |
"TIMER": |
|
|
FAF9H (64249) счетчик сотых долей секунд
FAFAH (64250) счетчик секунд FAFBH (64251) счетчик минут
FAFCH (64252) блокировка срабатывания таймера
FAFDH (64253) не используется FAFEH (64254) не используется
По адресу FB0AH выполняется проверка, был ли переход через ноль, то есть было ли переполнение. В том случае, если переполнения не было, то выполняется переход на процедуру DEC. Если было переполнение, то происходит продолжение выполнения программы. Так как после перехода через ноль (после двух команд DEC) в счетчике прерываний будет FEH, то в системной ячейке FAF9H надо установить исходное значение сотых долей секунд: 98. Далее организуется цикл из двух повторений для секунд и минут, так как в обоих случаях счет идет от 59 до 00.
После уменьшения на единицу значения счетчика, проверяется, был ли переход через ноль. Если переход через ноль был, (то есть вместо 00H станет FFH), то в счетчик заносится исходное значение для отсчета: 59.
В новом варианте процедуры ALARM происходит проверка содержимого всех счетчиков на достижение нулевых показаний. Проверка выполняется с использованием команды OR, при этом, если хотя бы в одной ячейке окажется не ноль, то будет сброшен флаг нуля. Кроме проверки счетчиков десятых долей секунды, счетчиков секунд и минут, выполняется проверка содержимого следующей за ними ячейки FAFCH. Эта ячейка не используется при обычной работе таймера. Однако с ее помощью легко можно отключить срабатывание таймера, если занести в эту ячейку (хотя бы при помощи POKE) любое ненулевое значение. Содержимое этой ячейки может меняться из других программ, разрешая или запрещая, таким образом, срабатывание таймера. В том случае, если в этой ячейке не ноль, то есть срабатывание таймера запрещено, то после достижения показаний 00:00:00, произойдет установка показаний 59:59:98 и продолжение отсчета.
В том случае, если в ячейке FAFCH ноль, то есть срабатывание таймера разрешено, выполняются действия, аналогичные рассмотренным в программе "COUNTER".
Бейсик-программа, демонстрирующая работу блока кодов "TIMER".
1 GO TO 100: REM : 64239 - START 64246 - STOP
2 CLEAR 64238: LOAD "timer" CODE
3 LOAD "chr" CODE 64600
4 RUN
8 POKE 23606,88: POKE 23607,251: RETURN
9 POKE 23606,0: POKE 23607,60: RETURN
40 PRINT AT 0,0; INK 6; PAPER 1; BRIGHT 1; INVERSE 1;" ВАМ ОСТАЛОСЬ ДУМАТЬ: " 42 POKE 64249,0: POKE 64252,0 44 RANDOMIZE USR 64239 49 RETURN
5 0 PRINT AT 0,0;" " 54 RANDOMIZE USR 64246
59 RETURN
100 RANDOMIZE USR 64246
110 BORDER 7: PAPER 7: INK 0: CLS
120 GO SUB 8
300 PRINT ' ' '"ЗАДАЙТЕ ВРЕМЯ ТАЙМЕРА"'" (МИНУТЫ И СЕКУНДЫ) "' '"ПРИМЕР: 00.30" ' ' "ИЛИ
НАЖМИТЕ ENTER" 310 INPUT LINE T$: IF T$="" THEN LET T$="00.30" 320 POKE 642 51,VAL T$(1)*16+VAL T$(2) 330 POKE 642 5 0,VAL T$(4)*16+VAL T$(5)
1000 PRINT ''" ПРОГРАММА ДЕМОНСТРИРУЕТ ЭФ-ФЕКТ ОГРАНИЧЕНИЯ ВРЕМЕНИ ДЛЯ ОБДУМЫВАНИЯ ОТВЕТА ПРИ ВВОДЕ ПАРА-МЕТРА ПРИ ПОМОЩИ INPUT." 1010 PRINT " ПО ИСТЕЧЕНИИ ЗАДАННОГО ВРЕМЕ-НИ, ВВОД БУДЕТ ПРИНУДИТЕЛЬНО ЗА-ВЕРШЕН
ПОДАЧЕЙ КОМАНДЫ ENTER ." 2000 PRINT '"ВВЕДИТЕ ЧТО-НИБУДЬ ДЛЯ ПРИМЕРА, (НО ENTER - НЕ НАЖИМАЙТЕ):" 2010 GO SUB 40: INPUT LINE A$: GO SUB 50
3000 PRINT INK 7; PAPER 2; BRIGHT 1; INVERSE 1;AT 0,0;" ВЫ ";("НИЧЕГО НЕ " AND A$="");"ВВЕЛИ "; BRIGHT 0'A$
ВМЕСТО ПОСЛЕСЛОВИЯ.
Практическая работа с программами, использующими прерывания 2 рода, выявила еще один вариант несовместимости компьютеров со стандартным Спектрумом. Встречаются компьютеры, на которых эти программы не работают. Программы, использующие прерывания 2 рода, и раньше публиковались на страницах РЕВЮ. Это, например, резидентный русификатор (РЕВЮ-93, № 1-2, стр. 28), и его усовершенствованный вариант (тот же номер РЕВЮ, стр. 30), отладчик машинного кода TRACER (тот же РЕВЮ, стр. 33).
При проверке работоспособности (на некоторых компьютерах) программ, использующих прерывания 2 рода, оказалась интересная вещь: нормально работает программа-русификатор на стр. 28, но никак не хочет работать его усовершенствованный вариант (стр. 30). Не идет и отладчик TRACER (стр. 33).
Анализ программ показал, что причину надо искать в аппаратной части. Суть же вот в чём. На стр. 30 в средней колонке (чуть ниже середины) есть такие слова: "... при существующей архитектуре "Спектрума" младший
ШЭфЕССМОНЯЛЪШ1Йжоюхою
байт всегда будет равен FF..." Это - ключевой момент. Видимо, в тех вариантах "Спектрума", где эти программы не идут - это не так. Поэтому компьютер не находит адрес обработки прерываний, который задан в FBFFH. Программа на стр. 28 идет, так как там несколько иная ситуация: каким бы ни был младший байт, в любой ячейке со старшим байтом СС задано одно и то же число: CDCDH. По этой же причине не идет и программа "TRACER". На стр. 33 правая колонка внизу: ".при прерывании произойдет переход на программу, адрес которой записан в ячейке FEFFH...". Опять же, в тех компьютерах, где программа не идет, младший байт не FF, поэтому адрес обработки прерываний не читается.
* * *