ZX-Ревю 1994 №2 1993 г.

Профессиональный подход - Использование прерыываний 2 рода.


ПРОГРАММЫ, ИСПОЛЬЗУЮЩИЕ ПРЕРЫВАНИЯ 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 рода.

A, #FA I,A 2

START

LD LD IM RET

STOP

;Отключ. режима прерываний 2 рода.

FAF 6 ED5 6 FAF8 C9

1

IM RET

VARS

DEFB DEFB DEFB DEFB DEFB DEFB

DEFB #FB01

;Счетчик прерываний. ;Счетчик секунд. ;Счетчик минут. ;Счетчик часов.

;Задание времени таймера - минуты.

;Задание времени таймера - часы. ;Адрес процедуры, обсл. прерывание.

FAF 9 FAFA FAFB FAFC FAFD FAFE FAFF

32 00 00 00 60 24

01FB

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 знакомест.

FB28 211858

ATTR

FB2B FB2D FB2F FB30

0608 3 64E 23

10FB

ATTR1

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

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. Иначе -

обнуление счетчика. Повторение для счетчика минут. Занесение получ. значения в счетчик.

(HL),A NZ,#FB2 7

Измененная процедура 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, поэтому адрес обработки прерываний не читается.

* * *




СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Мыльница - Пена: почтовый раздел.
Интервью - Интервью Jaan aka DJ Phantasy.
КлинМозгов - R0ma - ценный человек, поскольку снабжает газету текстами.
От редакции - саpанская компьютеpная газета CITY не имеет никакого отношения ни к газете ZX-CITY.
ICQ - Лог за 09.02.2003г.

В этот день...   24 ноября