ZX-Forum №1 1993 г.

Системные программы - Резидентная программа "Часы".


Резидентная программа "Часы".

© Константин Парчевский, п. Научный, Крым, 1994.

Внутренние часы SPECTRUMa "тикают" примерно 50 раз в секунду. Эти "тики" подсчитываются резидентной программой и хранятся в переменной TIC. Каждые полсекунды время выводится в правый верхний угол экрана в формате "час: мин", причем, когда TIC=25 - с двоеточием, а когда TIC=50 - без двоеточия). Этим достигается мигание двоеточия с частотой 1 Гц. Однако все оказалось не так просто. Так как внутри процедуры происходит вывод на экран, то мы должны следить за тем, чтобы случайно не сменить текущий канал, ведь процедура может быть вызвана в любой момент времени. Последовательность действий при выводе информации на экран следующая.

1. Очистить место на экране, куда будет выводиться информация, ведь вывод ведётся в режиме OVER 1 (наложить) и новая информация наложится на старую, сделав её нечитаемой.

2. Запомнить, какой канал в данный момент открыт.

3. Открыть канал 2 (вывод на основной экран), вывести текущее время.

4. Восстановить прежний канал.

Ещё одна тонкость, связанная с выводом. Мы хотим видеть на экране десятичное представление времени, в то время как в компьютере вся информация представлена в HEX формате (в конечном итоге в двоичном коде), значит, в процессе вывода мы должны перекодировать числа из шестнадцатеричной системы в десятичную. Это трудоёмко и занимает много времени. (Не забывайте, что программа работает постоянно и весьма важно, чтобы время её выполнения было как можно меньше). Для уменьшения времени перекодировки из HEX- в DEC-систему часы, минуты и секунды представлены в упакованном BCD формате (если, скажем, значение часов равно 21, то в упакованном BCD формате оно будет равно #21). Таким образом, для перекодировки достаточно занести старший и младший полубайты #21 в отдельные байты, прибавить к ним #30 и напечатать получившиеся ASCII коды, как символы. Теперь проблема состоит в том, что мы не можем производить арифметические действия с такими байтами как с обычными числами. К счастью, у профессора Z-80 есть команда DAA десятичной коррекции при работе с числами в BCD формате, так что достаточно давать эту команду после каждой команды, выполняющей арифметические действия - и всё будет в порядке.

Хотелось бы остановиться на методе определения номера текущего потока. По-видимому, в ПЗУ нет стандартной процедуры, выполняющей такие действия (по крайней мере, я такой процедуры не нашел). Где-то в оперативной памяти (не важно, по какому адресу) есть область, содержащая информацию о каналах (смахивает на начало русской народной сказки "В тридевятом царстве, в некотором государстве...). На каждый канал нам отводится по 5 байт. Адрес этой области можно взять из системной переменной CHANS (#5C4F). В системной переменной CURCHL (#5С51) находится адрес первого байта описания текущего канала (в области информации о каналах). Чтобы получить смещение адреса текущего канала относительно начала, необходимо от содержимого CURCHL отнять содержимое CHANS. Запомним этот адрес и обозначим его, скажем, как OFFSET_1. Далее, в области системных переменных с адреса STRMS (#5С14) расположена таблица двухбайтовых чисел, указывающих на то, какой поток с каким каналом связан. Первое число соответствует потоку - 3, следующее - 2, и т.д. Эти двухбайтовые числа содержат значение на единицу большее смещения канала, который связывается с данным потоком, относительно начала области информации о каналах (Уф, ну и предложеньеце! Хорошо бы в грамматику ввести скобки, указывающие приоритет). Короче, если мы начнем сравнивать OFFSET_1+1 с этими двухбайтовыми числами, то номер того числа, с которым совпадет это значение и укажет нам номер текущего потока. Напомним, что потоки нумеруются с -3.

После запуска программы выяснилось, что часы отстают примерно на 2,5 "тика" в секунду. Это, видимо, связано с тем, что дважды в секунду происходит вывод на экран, причем он длится чуть больше 1/50 секунды, так что два "тика" пропускаются полностью и ещё чуть-чуть. В исходный текст уже внесена коррекция (вместо 25 стоит 24, а вместо 50 стоит 48), но отставание на "полтика" в секунду осталось. Можно сократить число "тиков" на единицу, поставив 47, но тогда часы будут спешить на "полтика" в секунду. Количественно отставание на "полтика" в

секунду проявляется в том, что часы за час отстают на 30 секунд, что не так уж и плохо, особенно если учесть, что существенно большей точности добиться невозможно, так как часы останавливаются, когда запрещены прерывания (скажем при работе с магнитофоном (ввод/вывод). Листинг резидентной программы "Часы".

00010

ORG

#FDFF

00020

DISP

0 - #9000

00030

DEFW

CLOCK

;Вектор прерывания.

00040

TIME

DEFB

21,1,22,0,26

;PRINT OVER 1; AT 0,2 6;

00050

DEFM

" 00 00"

;Маска для печати времени.

00060

HOR

DEFB

0

;Часы в BCD формате.

00070

MIN

DEFB

0

;Минуты в BCD формате.

00080

SEC

DEFB

0

;Секунды в BCD формате.

00090

TIC

DEFB

0

;Счетчик "тиков".

00100

MSG

DEFB

#80

;Таблица сообщений для вывода на

00110

DEFM

"Input hour:"

00120

DEFB

#A0,13

00130

DEFM

"Input min.:"

00140

DEFB

#A0

00150

; Резидентный

обработчик маскируемого прерывания.

00160

CLOCK

PUSH

AF

00170

PUSH

BC

00180

PUSH

DE

00190

PUSH

HL

00200

LD

HL,TIC

;Увеличить значение

00210

INC

(HL)

;счетчика "тиков".

00220

LD

A,(HL)

00230

CP

24

;Если (TIC)=24

00240

JR

NZ,LOC1

;то переход.

00250

LD

HL,TIME+8

;Прошло полсекунды, (TIC)=24.

00260

LD

(HL) ,":

;Установить ":" в буфер печати.

00270

PUSH

HL

00280

CALL

PTIME

;Напечатать время.

00290

POP

HL

00300

LD

(HL) ,#20

00310

JR

END

00320

LOC1

CP

48

00330

JR

NZ,END

;Переход, если (TIC)=48.

00340

LD

HL,TIC

;Прошла секунда, (TIC)=48.

00350

LD

(HL) ,0

;Обнулить счётчик "тиков".

00360

DEC

HL

;HL=SEC.

00370

LD

B,1

00380

LOOP1

LD

A,(HL)

;Число секунд (минут)

00390

INC

A

;увеличить на 1.

00400

DAA

00410

CP

#60

00420

JR

NZ,LOC2

;Переход, если нет переполнения.

00430

LD

(HL) ,0

;Иначе обнулить секунды.

00440

DEC

HL

;HL=MIN (HL=HOR).

00450

DEC

B

00460

JR

NZ,LOOP1

;Повторить для минут.

00470

LD

A,(HL)

;HL=HOR.

00480

INC

A

00490

DAA

00500

CP

#24

00510

JR

NZ,LOC2

;Переход, если нет переполнения.

00520

LD

(HL) ,0

00530

JR

LOC7

00540

LOC2

LD

(HL),A

;Сохранить часы, мин. или сек.

00550

LOC7

CALL

PTIME

;Печать с пробелом вместо ":".

00560

END

POP

HL

00570

POP

DE

00580

POP

BC

00590

POP

AF

00600

JP

#38

;На стандартную обработку прерывания

00610

; Процедура печати времени на

экране.

00620

PTIME

LD

DE,TIME+6

00630

LD

HL,HOR

00640

LD

A,(HL)

00650

CALL

SBYT

;Преобразовать HOR из BCD в ASCII.

00660

INC

HL

00670

INC

DE

00680

INC

DE

00690

LD

A,(HL)

00700

CALL

SBYT

;Преобразовать MIN из BCD в ASCII.

00710

CALL

CLST

;Очистка места для печати.

00720

CALL

CHNL

;Возвращает в B текущий поток.

00730

LD

A, B

00740

CP

2

00750

JR

NZ,LOC5

00760

LD

DE,TIME

;Текущим является вывод на экран

00770

LD

BC, 11

00780

CALL

#203C

;Напечатать время.

00790

RET

00800

LOC5

PUSH

AF

;Текущий поток=2, запомнить его.

00810

LD

A, 2

00820

CALL

#1601

;Открыть поток "S".

00830

LD

DE,TIME

00840

LD

BC, 11

00850

CALL

#203C

;Напечатать время.

00860

POP

AF

00870

CALL

#1601

;Восстановить текущий поток.

00880

RET

00890

; Процедура определения текущего потока.

00900

; Возвращает

в В номер текущего потока (-1,0,1,2,...)

00910

CHNL

LD

B,#FF

00920

LD

HL,(#5C51)

;HL=адрес байта текущего канала.

00930

LD

DE,(#5C4F)

;DE=адрес области с информацией о ;каналах.

00940

XOR

A

;CY=0.

00950

SBC

HL, DE

00960

INC

HL

;HL содержит ту же информацию, ;что содержится в таблице STRMS.

00970

LD

DE,#5C14

;DE=STRMS.

00980

LOOP2

LD

A,(DE)

;Поиск в табл.STRMS значения HL.

00990

CP

L

01000

JR

Z,LOC3

;Переход, если L совпало.

01010

INC

DE

01020

LOC4

INC

DE

01030

INC

B

;Перейти к следующему слову в таблице

01040

JR

LOOP2

;При этом B содержит номер потока.

01050

LOC3

INC

DE

01060

LD

A, (DE)

01070

CP

H

01080

RET

Z ;Закончить, если и Н совпало.

01090

JR

LOC4

01100

; Процедура очистки области на экране.

01110

; Т. к.

вывод времени на экран ведется в режиме OVER 1,

01120

;то прежде, чем

что-нибудь выводить, необходимо очистить

01130

;место. Ниже приводятся адреса экрана, куда будет

01140

;осуществляться

вывод и которые нужно очистить.

01150

; #401A #401B...

#401F, #411A #411B... #411F,...

01160

; ... #471A #471B...#471F.

01170

;

01180

CLST

LD

HL,#401A

01190

LOOP4

PUSH

HL

01200

LOOP3

LD

(HL),0

01210

INC

HL

01220

LD

A, L

01230

CP

#20

01240

JR

NZ,LOOP3

01250

POP

HL

01260

INC

H

01270

LD

A, H

01280

CP

#48

01290

JR

NZ,LOOP4

01300

RET

01310

; Процедура перевода упакованного BCD байта в 2 байта

01320

; кода

ASCII и запись их в буфер печати.

01330

;Входные параметры: А - упакованный BCD байт,

01340

; DE -

указатель

на буфер ASCII символов.

01350

SBYT

PUSH

AF

01360

SRL

A

01370

SRL

A

01380

SRL

A

01390

SRL

A

01400

ADD

A, #30

01410

LD

(DE) ,A

01420

INC

DE

01430

POP

AF

01440

AND

#F

01450

ADD

A, #30

01460

LD

(DE) ,A

01470

RET

01480

; Процедура инициализации часов.

01490

INIT

IM

1 ;Часы - останавливаются.

01500

LD

A, #FD

01510

LD

I,A ;!=старший байт адреса вектора ;прерывания.

01520

CALL

#D6B ;Очистить экран.

01530

LD

A, 2 ;Открыть

01540

CALL

#1601 ;поток 2.

01550

LD

DE,MSG ;DE=адрес таблицы сообщений.

01560

XOR

A ;А=0.

01570

LD

(SEC) ,A ; Удобный случай обнулить секун.

01580

CALL

#C0A ;Печать сообщения "Input hour:

01590

CALL

INBYT ;Ввод значения для установки часов.

01600

LD

(HOR),A ;Установить часы.

01610

LD

DE,MSG

01620

LD

A,1

01630

CALL

#C0A ;Печать сообщения "Input min.:".

01640

CALL

INBYT ;Ввод значения для установки минут.

01650

LD

(MIN),A ;Установить минуты.

01660

IM

2 ;Запустить часы.

01670

RET

01680

; Процедура ввода байта в BCD формате.

01690

/Возвращает: А -

байт в BCD формате.

01700

INBYT CALL

INDIG

01710

LD

B, A

01720

SLA

B

01730

SLA

B

01740

SLA

B

01750

SLA

B

01760

CALL

INDIG

01770

OR

B

01780

RET

01790

; Процедура ввода BCD цифры.

01800

/Возвращает: А -

цифра в BCD формате.

01810

INDIG LD

HL,#5C3B

01820

RES

5, (HL)

01830

IN1 BIT

5,(HL)

01840

JR

Z,IN1 ;Ждать нажатия клавиши.

01850

LD

A, (#5C08) ;Читать код клавиши из LAST K.

01860

PUSH

AF

01870

RST

#10 ;Эхо на экран.

01880

POP

AF

01890

SUB

#30 ;Преобразовать код клавиши в BCD цифру.

01900

RET

01910

; Процедура деинициализации часов.

01920

SHUT IM

1

01930

RET

Перед тем, как начать набор текста в ZEUS ASSEMBLER, необходимо выйти в бейсик

командой "Q", после чего дать команду CLEAR 27999 и вернуться в ZEUS (RANDOMIZE USR

57344)

После набора текста

и его ассемблирования сохраните готовый машинный код командой:

SAVE

'clock c"CODE 28159,331.

Бейсик-загрузчик.

10 REM SET Clock = 65269

20 REM OFF Clock = 65351

30 CLEAR 65022

40 LOAD "clock c" CODE

65023

50 RANDOMIZE USR 652 69

ИФК: Кодовый блок

программы "часы" можно набрать, пользуясь шестнадцатеричным

дампом с контрольными суммами, который мы даём ниже.

FDF8

00

00

00

00

00

00

00

2A

1F

FE0 0

FE

15

01

16

00

1A

20

30

92

FE0 8

30

20

30

30

00

00

00

00

B6

FE10

80

49

6E

70

75

74

20

68

26

FE18

6F

75

72

3A

A0

0D

49

6E

0A

FE2 0

70

75

74

20

6D

69

6E

2E

09

FE2 8

3A

A0

F5

C5

D5

E5

21

0F

A4

FE30

FE

34

7E

FE

18

20

0E

21

43

FE38

09

FE

36

3A

E5

CD

74

FE

D1

FE4 0

E1

36

20

18

28

FE

30

20

03

FE4 8

24

21

0F

FE

36

00

2B

06

FF

FE50

01

7E

3C

27

FE

60

20

11

BF

FE58

36

00

2B

05

20

F3

7E

3C

89

FE60

27

FE

24

20

04

36

00

18

19

FE68

01

77

CD

74

FE

E1

D1

C1

90

FE7 0

F1

C3

38

00

11

07

FE

21

91

FE7 8

0C

FE

7E

CD

E1

FE

23

13

E0

FE80

13

7E

CD

E1

FE

CD

CD

FE

53

FE88

CD

AE

FE

78

FE

02

20

0A

A1

FE90

11

01

FE

01

0B

00

CD

3C

B3

FE98

20

C9

F5

3E

02

CD

01

16

98

FEA0

11

01

FE

01

0B

00

CD

3C

C3

FEA8

20

F1

CD

01

16

C9

06

FF

69

FEB0

2A

51

5C

ED

5B

4F

5C

AF

27

FEB8

ED

52

23

11

14

5C

1A

BD

70

FEC0

28

05

13

13

04

18

F7

13

37

FEC8

1A

BC

C8

18

F6

21

1A

40

ED

FED0

E5

36

00

23

7D

FE

20

20

C7

FED8

F8

E1

24

7C

FE

48

20

F0

A5

FEE0

C9

F5

CB

3F

CB

3F

CB

3F

BA

FEE8

CB

3F

C6

30

12

13

F1

E6

E2

FEF0

0F

C6

30

12

C9

ED

56

3E

4 F

FEF8

FD

ED

47

CD

6B

0D

3E

02

AC

FF00

CD

01

16

11

10

FE

AF

32

E3

FF08

0E

FE

CD

0A

0C

CD

24

FF

E 6

FF10

32

0C

FE

11

10

FE

3E

01

A9

FF18

CD

0A

0C

CD

24

FF

32

0D

29

FF2 0

FE

ED

5E

C9

CD

35

FF

47

79

FF2 8

CB

20

CB

20

CB

20

CB

20

D3

FF30

CD

35

FF

B0

C9

21

3B

5C

61

FF38

CB

AE

CB

6E

28

FC

3A

08

4 F

FF4 0

5C

F5

D7

F1

D6

30

C9

ED

14

FF4 8

56

C9

00

00

00

00

00

00

66




СОДЕРЖАНИЕ:


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

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



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

Похожие статьи:
Система - Как это не прискорбно отметить, все большее число синклеристов переxодит на другие платформы, преимущественно на IВM.
От редакции - история рождения газеты.
Demoscene - отчет с Chaos Constructions 2004 от C-jeff'a.
Железо - Назначение перемычек 3.5" дисководов.
IS-DOS - начинающим: IS-DOS - первое знакомство

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