ZX-Ревю 1993 №5-6 1992 г.

Применение ассемблера для создания быстроработающих программ - продолжение. Начало см. с. 9-12, 48-61. Случайные числа. Обслуживание клавиатуры.


ПРИМЕНЕНИЕ АССЕМБЛЕРА ДЛЯ СОЗДАНИЯ БЫСТРОРАБОТАЮЩИХ ПРОГРАММ

Перевод с английского Пашорина В. И.

Продолжение. Начало см. с. 9-12, 48-61.

4. СЛУЧАЙНЫЕ ЧИСЛА

Случайные числа являются неотъемлемой частью большинства игровых программ. Получить случайные числа с помощью программы в машинных кодах довольно легко и, в зависимости от диапазона выборки, определяющего границы интервала, в котором необходимо получить случайные числа, различают два способа.

Первый способ, вполне удовлетворительный для большинства игровых программ, основан на использовании системных переменных SEED и FRAMES. Применяться этот способ может при следующих диапазонах выборки:

0.

.1,

0.

.3,

0.

.7,

0.

.15,

0.

.31,

0.

.63,

0.

.127

6.

.255

Для этого в регистровую пару HL записывается значение системной переменной SEED (5C76HEX = 23670 DEC), а в регистровую пару DE - значение младшего байта системной переменной FRAMES (5C78HEX = 23672 DEC). Затем эти два числа суммируется и результат заносится в системную переменную SEED (это нужно для выборки очередного случайного числа). В регистр A поочередно записываются значения H и L и путем логической операции AND маскируются незначимые биты для задания диапазона выборки случайного числа.

В программе 4.1, к примеру, для задания диапазона выборки 0...31 т.е. для получения таких же случайных чисел, как к по BASIC-команде INT(RND*32), в регистре A маскируются биты с пятого по седьмой.

Как уже отмечалось, такой способ может быть использован для получения случайных чисел в строго определенных диапазонах. Однако, представленную программу можно усовершенствовать и расширить диапазон фиксированных интервалов для случайных чисел. Например, для реализации INT(RND*23) необходимо добавить:

LD A,H AND 15 LD H,A LD A,L AND 7 ADD A,H

Обратите внимание на то, что в программе 4.1 регистр B используется как счетчик шагов цикла (10 шагов) и значение этого же регистра используется в PRINT-процедуре: PRINT AT B,10 для вывода случайных чисел на экран.

Листинг 4.1

АДРЕС

МАШ

и. КОД

АССЕМБЛЕР КОММЕНТАРИЙ

ORG 23760

23760

06

0A

LD B, 10

Счетчик на 10 шагов.

23762

C5

L1

PUSH BC

Запомнили состояние счетчика.

23763

2A

76

LD HL,(23670)

Приняли в HL состояние системной переменной SEED.

23766

ED

78 5С

LD DE,(23672)

Приняли в DE состояние системной перем. FRAMES.

23770

19

ADD HL,DE

Нашли их сумму

23771

22

76

LD (23670),HL

и установили результат в SEED.

23774

7D

LD A,L

23775

E6

1F

AND 31

Гашение ненужных битов.

23777

F5

PUSH AF

Запомнили результат.

23776

3E

02

LD A,2

I

23780

CD

01

16

CALL 5633

|Открытие канала печати на

23783

3E

16

LD A,22

|экран и выдача управля

23785

D7

RST 16

ющих кодов для установки

23786

78

LD A,B

|позиции печати

23787

D7

RST 16

|

23788

3E

0A

LD A,10

| AT B, 10

23790

D7

RST 16

|

23791

F1

POP AF

Восстановление числа со стека.

23792

CD

28

2D

CALL 11560

Процедура STACK-A (помещает на стек калькулятора число, записанное в аккумуляторе процессора.

23795

CD

E3

2D

CALL 11747

Процедура PRINT_FP. Печата ет действительное число, находящееся на вершине стека калькулятора.

23798

C1

POP BC

Восстановление счетчика.

23799

10

D9

DJNZ L1

Уменьшение счетчика и возврат на новый шаг.

23801

C9

RET

Выход из процедуры.

Для получения случайных чисел из произвольного интервала применяется второй способ. Этот способ основан на использовании RND-процедуры из системного ПЗУ компьютера. К сожалению, эту процедуру нельзя вызвать простой командой CALL RND, т.к. она не завершается командой RET. Процедура эта занимает участок памяти с адреса 9725 по адрес 9765 (25FDHEX...2625HEX) и в своей работе использует встроенный калькулятор, а по завершении работы меняет значение переменной SEED. Для этого в начале работы это значение извлекается из SEED, преобразуется, а затем дважды записывается на вершину калькуляторного стека. Первое значение на вершине стека передается в системную переменную SEED для генерации очередного случайного числа, а оставшееся на вершине стека значение преобразуется для получения соответствующего ему числа в диапазоне 0...1 (но не = 1). Чтобы использовать эту процедуру, необходимо ее скопировать из ПЗУ и затем вызывать, как подпрограмму.

Подпрограмма 4.2 показывает применение этого способа для получения случайных

чисел

INT (RND*12345)

и вывода 44 из них в два столбца на экране путем использования CHR$6.

RND-процедура использует несколько новых для нас кодов калькулятора, которые могут быть полезны и для других процедур:

Код 161 (A1H) - stack_one - помещает на вершину стека калькулятора единицу.

Код 52 (34H) - stack_data - интерпретирует следующие за ним пять байтов как действительное число с плавающей точкой, записанное в интегральной форме и помещает

его на вершину стека калькулятора.

Код 50 (32H) - n_mod_m - вместо делимого (n) и делителя (m) эта команда помещает на стек остаток от деления и целое частное.

Код 02 - delete - команда служит для удаления числа с вершины стека калькулятора.

Последние два кода редко используются в игровых программах. Вызов процедуры FP_TO_BC, находящейся по адресу 11682 DEC (2DA2HEX) - CALL 11682, позволяет извлечь число с вершины стека, округлить его до ближайшего целого числа и затем записать в регистровую пару BC. В отличие от этой процедуры, процедура FF_TO_A, находящаяся по адресу 11733DEC (2BD5HEX), передает число с вершины стека в регистр A.

В данной программе скопированная RND-процедура заканчивается по адресу 23810, а начиная с адреса 23811 и по адрес 23820 находится процедура, осуществляющая дальнейшее преобразование случайного числа, записанного на стеке. Для этого на стек записывается число 12345, определяющее диапазон выборки, а затем производится перемножение случайного числа из диапазона 0...1, находящегося на стеке, с этим числом. Затем кодом калькулятора 02 (delete) удаляем со стека дробную часть произведения и на вершине стека остается целое число INT(RND*12345). Процедура PRINT VALUE выводит на экран 44 случайных числа в 2 столбца.

Листинг 4. 2

АДРЕС

МАШ

. КОД

23760

3E

02

23762

CD

01

16

23765

CD

0D

23768

06

2C

23770

С5

23771

ED

4B

76

23775

CD

2B

2D

23778

EF

23779

A1

23780

0F

23781

34

37

16

23784

04

23785

34

80

41

23791

32

23792

02

23793

A1

23794

03

23795

31

23796

36

23797

СО

A2

2D

23800

ED

43

76

23804

7E

A7

23805

23806 23808

28 03 D6 10

23810

77

АССЕМБЛЕР ORG 23760 LD A,2 CALL 5633 CALL 3435 LD B,44 L1 PUSH BC

LD BC,(23670)

CALL 11563

RST 40

stack_one

add

stack_number multiply stack_number n_mod_m

delete

stack_one subtract duplicate endcalc CALL 11682

LD (23670),ВС

LD A,(HL)

AND А JR Z,L2 SUB 16

LD (HL),A

КОММЕНТАРИЙ

Открываем канал для печати на экран. Очистка экрана. Инициализация счетчика. Запомнили показания счетчика.

Приняли в ВС текущее значение системной переменной SEED.

Переброска содержимого BC на стек калькулятора. Включение калькулятора. На стеке (SEED), 1 (SEED)+1

На стек помещаем число 75.

((SEED)+1)*75

Число 65537

Деление на 65537 с остатком.

Частное удалили, оставили только остаток. "остаток",1 "остаток"-1

Ha стеке сделали копию. Выключение калькулятора. Перенос результата со стека калькулятора в регистровую пару BC. Установка нового значения системной переменной SEED. Экспонента числа, находя-шегося на вершине стека. Выставление флагов. Обход, если экспонента = 0 Вычитание числа 16 из экспоненты эквивалентно делению на 65536.

Изменили экспоненту числа, на вершине стека. Теперь на вершине стека

;случайное число в диапазоне от 0 до 1.

L2

23811

01

39

30

LD ВС,12345

23814

CD

2B

2D

CALL 11563

Число 12345 DEC переносим на стек.

23817

EF

RST 40

Включение калькулятора.

23818

04

multiply

Умножение. На стеке случайное число от 0 до 12345

23819

27

int

Выделение целой части.

23820

38

endcalc

Выключение калькулятора.

23821

ЗЕ

02

LD A,02

23823

CD

01

16

CALL 5633

Открыли канал печати на экран.

23826

CD

E3

2D

CALL 11747

Процедура PRINT_FP. Печата ет действительное число, находящееся на вершине стека калькулятора.

25829

3E

06

LD A,06

23831

D7

RST 16

Печать кода CHR$ 6.

23832

C1

POP BC

Восстановление счетчика.

23833

10

BF

DJNZ L1

Возврат на повторный шаг.

23835

C9

RET

Выход из процедуры.

Демонстрационная программа 4.3 показывает, как применяется процедура RND. Здесь эта процедура используется для задания случайным образом цвета и высоты домов в городе и может быть использована в программах типа "Бомбардировщик". Все необходимые символы UDG здесь уже определены и Вы можете легко усовершенствовать эту программу для своих целей.

Начальное изображение на экране формируется с помощью БЕЙСИКа, а последующее, через несколько секунд, с помощью программы в машинных кодах, что позволяет сравнить скорость выполнения функции RND. Чтобы не менять начальное значение для генератора случайных чисел, используйте для вызова программы в машинных кодах не RANDOMIZE USR "адрес", а LET "переменная" = USR "адрес". Можно использовать команду PRINT USR "адрес", если Вы хотите получить число, записанное в регистровой паре BC после выхода из процедуры в машинных кодах. Для установки цветов PAPER и INK в процедуре используется LD (IY+83),47 вместо

LD A,47 и LD (IY+83),A.

как это было в предыдущих примерах, что позволяет уменьшить требуемый для процедуры объем памяти.

Перед вызовом генератора случайных чисел в регистр A записывается значение коэффициента n в формуле RND*n, которое затем передается в ячейку по адресу 23681 (в стандартной версии это неиспользуемая ячейка в области системных переменных). Это число округляется до ближайшего целого числа и записывается опять в регистр A, а оттуда, с помощью процедуры CALL 11733 переписывается на вершину стека.

Необходимая для организации вывода на экран информация находится в буфере принтера в следующей последовательности: AT(22), x,y, INK(16),a$, b$.

5. ОБСЛУЖИВАНИЕ КЛАВИАТУРЫ

Существуют два способа создания программ в машинных кодах для опроса состояния клавиатуры. Оба способа вполне приемлемы, но конкретный выбор одного из них зависит от типа программы.

5.1. Использование системной переменной LAST_KEY.

В области памяти ОЗУ компьютера "ZX-Spectrum" имеется процедура KEYSCAN которая вызывается каждую 1/50 секунды для опроса состояния клавиатуры и изменения соответствующих системных переменных. В случае, если будет нажата произвольная клавиша, то установится 5-й бит системной переменной FLAGS(23611 DEC = 5С3B HEX), сигнализирующий о факте нажатия клавиши, а код нажатой клавиши запишется в системную переменную LAST_KEY. Если же не будет нажата ни одна клавиша, то при каждом вызове процедуры KEYSCAN будет увеличиваться на 1 только значение системной переменной FRAMES.

Поскольку команда процессора HALT приостанавливает выполнение программы до очередного вызова процедуры KEYSCAN, то можно реализовать в машинных кодах программу, аналогичную по действию BASIC-команде PAUSE 0 (см. листинг 5.1). Эта программа зациклена на непрерывной проверке 5-го бита системной переменной FLAGS и, как только будет обнаружено, что этот бит включен (т.е. после очередного нажатия клавиши), выполнение программы будет продолжено дальше. Необходимо при этом сразу же сбросить 5-й бит переменной FLAGS для того, чтобы при очередном вызове процедуры опять сработала задержка до нажатия произвольной клавиши.

АДРЕС

МАШ

.КОД

АССЕМБЛЕР

ORG 23760

23760

3E

00

LD A,0

23762

CD

22

CALL 8859

23765

FD

36

53

2F

LD (IY+83),47

23769

3E

02

LD A,2

23771

CD

01

16

CALL 5633

23774

CD

0D

CALL 3435

23777

FD

21

00

LD IY,23296

23781

FD

36

00

16

LD (IY+0),22

23785

FD

36

03

10

LD (IY+03),16

23789

FD

21

LD IY,23610

23793

3E

1F

LD A,31

L1

23795

32

02

LD (23298),А

23798

3E

0A

LD A,10

23800

32

81

LD (23681),A

23803

CD

61

5D

CALL RND

23806

6F

LD L,A

23807

3E

13

LD A,19

23809

95

SUB L

23810

32

01

LD (23297),А

23813

3E

02

LD A,2

23815

32

81

LD (23681),А

23818

CD

61

CALL RND

23821

C6

94

ADD A,148

23823

32

05

LD (23301),А

23826

3E

04

LD A,4

23826

32

81

LD (23681),А

23831

CD

61

5D

CALL RND

23834

32

04

5B

LD (23300),A

Листинг 4.3

КОММЕНТАРИЙ

;Черный цвет бордюра. ;Окраска бордюра. ;Установка цвета: ;PAPER = 5; INK = 7 ;Канал экрана. ;Открыли канал. ;Очистка экрана CLS. ;Регистровая пара IY становится ;указателем на буфер принтера. ;B буфер принтера помещаем ;код 22 - код AT. ;Туда же (со смещением +3) ;помещаем код 16 - код INK. ;Восстановление IY в исходное состояние. ;Счетчик столбцов экрана.

;Текущую координату X поместили на ее место в буфере принтера. ;Множитель 10 для RND. ;Сохраняем его в 23681. ;Вызов копии процедуры RED. ;Копия организована в 23905 ;Случайное число.

;19-RND*10 - это Y-координата ;позиции печати. ;Заложили ее на свое место ;в буфере принтера.

;Множитель при RND равен 2. ;INT (RND*2)

;INT (RND*2) + 148 - выбор ;символа UDG - 148 или 149. ;Код печатаемого символа ;переносим в буфер принтера

;Множитель при RND равен 4. ;INT (RND*4) ;Код цвета INK 0...3

23837

3E

02

LD A,02

Канал 2.

23839

CD

01

16

CALL 5633

Открыли канал экрана.

23842

3E

16

LD A,22

23644

D7

RST 16

Печать кода 22 - AT.

23845

3A

01

LD A,(23297)

Координата Y.

23848

3D

DEC A

Y-1

23849

D7

RST 16

...AT (Y-1)....

23850

3A

02

5B

LD A,(23298)

Координата X.

23853

D7

RST 16

...AT (Y-1),X

23854

3E

93

LD A,147

Символ UDG (CHR$ 147).

23856

D7

RST 16

PRINT AT (Y-1),X ;CHR$ 147

23857

11

00

5B

LD DE,23296

Начало буфера принтера.

23860

01

06

00

LD ВС,06

Шесть кодов из буфера.

23863

CD

3C

20

CALL 8252

Печать серии символов.

23866

3A

01

LD A,(23297)

Координата Y.

23869

3C

INC A

23870

32

01

LD (23297),A

Y=Y+1

23873

FE

15

CP 21

Проверка на конец игрово

го поля по вертикали.

23875

20

ЕС

JR NZ,L2

Возврат на L2, если конец

не достигнут.

23877

3E

99

LD A,153

Код символa UDG.

23879

32

05

LD (23301),А

23882

3E

07

LD A,7

Белый цвет INK.

23884

32

04

LD (23300),А

23887

11

00

LD DE,23296

Начало буфера принтера.

23890

01

06

00

LD ВС,06

Шесть кодов из буфера.

23893

CD

3C

20

CALL 8252

Печать управляющих кодов.

23896

3A

02

LD A,(23298)

Координата X.

23899

3D

DEC A

X=X-1

23900

FE

FF

CP FF

X сравнили с -1.

23902

20

93

JR NZ,L1

Если еще не все столбцы

обработаны, то возврат на

метку L1.

23904

C9

RET

Возврат.

ORG 23905

RND

23905

ED

76

LD ВС,(23670)

Значение SEED,

23909

CD

2D

CALL 11563

Переносим его на стек.

23912

EF

RST 40

Включение калькулятора.

23913

A1

stack one

23914

0F

add

23915

34

37

16

stack_number

Копия системной процедуры

23918

04

multiply

RND. Комментарий см. в 4.1

23919

34

80

41

00 00 80

stack_number

23925

32

n_mod_m

23926

02

delete

23927

A1

stack_one

23928

03

subtract

23929

31

duplicate

23930

38

endcalc

23931

CD

A2

2D

CALL 11682

23934

ED

43

76

5C

LD (23670),ВС

23938

7E

LD A,(HL)

23939

A7

AND A

23940

28

03

JR Z,L3

23942

D6

10

SUB 16

23944

77

LD (HL),A

L3

23945

3A

81

5C

LD A,(23681)

Множитель при RND.

23948

CD

28

2D

CALL 11560

Поместили его на стек.

23951

EF

RST 40

Включение калькулятора.

23952

04

multiply

Умножение.

23953

27

int

Выделение целой части.

Выключение калькулятора. Перенос результата в акк-р Выход из процедуры RND.

23954

23955 23958

38

CD D5 2D C9

endcalc CALL 11733 RET

Листинг 5.1

КОММЕНТАРИЙ

Проверка 5-го бита системной

переменной FLAGS.

Зацикливание на метку L1,

если никакая клавиша не нажата.

Принудительное выключение

5-го бита, если клавиша была нажата.

Выход из процедуры.

АДРЕС ORG 23760 L1

23760 23764 23766 23770

МАШ.КОД

FD CB 01 6Е 28 FA

FD CB 01 АЕ C9

АССЕМБЛЕР

BIT 5,(IY+1) JR Z,L1 RES 5,(IY+1) RET

5.2 Опрос клавиатуры, как внешнего порта: IN A,(C)

Аналогично команде БЕЙСИКа IN, при этом способе проверяется состояние одной из половин ряда клавиатуры. Спецификация проверяемого ряда находится в регистровой паре ВС, а код нажатой клавиши передается в регистр A. Так как каждая половина ряда клавиатуры включает по 5 клавиш, то для идентификации нажатой клавиши используют с 0 по 4 биты аккумулятора. Нулевой бит соответствует внешней клавише ряда, а четвертый бит соответствует внутренней клавише полуряда клавиатуры.

Все биты регистра A изначально установлены и, если не нажата ни одна из клавиш, то значение регистра A должно быть равно 255. Если же одна из клавиш будет нажата, то сбрасывается бит, соответствующий этой клавише. Например, если будет нажата клавиша "0", то сбрасывается нулевой бит регистра A и значение этого регистра теперь будет равно 254. При этом способе можно определить одновременное нажатие более чем одной клавиши путем анализа битов аккумулятора.

Этот способ оказывается весьма полезным для создания игр, в которых участвуют два и более игроков, либо для организации перемещения объектов по экрану одновременно в двух направлениях. Одним словом, этим приемом пользуются в тех случаях, когда надо обрабатывать нажатие пользователем второй клавиши при неотпущенной первой. В программе 5.2 показано, как осуществить задержку в выполнении программ до тех пор, пока не будет нажата клавиша "0".

Есть еще один случай, когда опрос клавиатуры как внешнего порта оказывается единственным рабочим приемом - случай, когда прерывания работы процессора отключены командой DI.

Даже во время выполнения Вашей собственной программы, написанной в машинных кодах, 50 раз в секунду будет вызываться процедура KEYSCAN и, в результате, увеличивается время выполнения Вашей программы. Можно отменить эти прерывания, чтобы сократить время выполнения программы, используя команду DI. Однако, это будет означать, что все прерывания запрещены и, следовательно, нельзя будет использовать процедуру LAST_KEY для считывания состояния клавиатуры, т.е. команда IN остается единственный доступным способом.

В программе 5.3 показан порядок работы при запрете прерываний.

Всегда помните, что перед выходом из процедуры, где Вы используете команду DI, необходимо записать команду EI, разрешающую прерывания. Попробуйте выполнить программу 5.3 без этой команды и увидите, что из этого получится. Сброса программы не произошло, но компьютер "завис" и для того, чтобы клавиатура вновь заработала, необходимо выключить и опять включить компьютер.

Программа 5.4 является аналогом БЕЙСИК-команды PAUSE n, т.е. позволяет остановить выполнение программы на заданный промежуток времени, либо продолжить выполнение сразу же после нажатия произвольной клавиши. Эта программа основана на том, что сканирование клавиатуры происходит строго через каждые 0.02 секунды, а команда HALT приостанавливает выполнение программы до очередного сканирования клавиатуры.

Листинг 5.2

КОММЕНТАРИЙ

АДРЕС L1

23760

МАШ.КОД 01 FE EF

ED 78 CB 47

C8

18 F6

АССЕМБЛЕР ORG 23760

LD BC,61438

23763 23705

23767

23768

IN A,(C) BIT 0,A

SET Z

JR L1

Установка в BC адреса внешнего порта, соответствующего верхнему правому полуряду клавиатуры. Ввод данных из порта в аккумулятор. Проверка нулевого бита, который соответствует внешней клавише полуряда. Если бит выключен, то клавиша нажата, следует возврат из процедуры. В противном случае состояние ожидания следует продолжить и вернуться на L1.

Листинг 5.3

АССЕМБЛЕР

ORG 23760 DI

LD BC,61438

IN A,(C)

BIT 0,A JR NZ,L1

EI RET

АССЕМБЛЕР

ORG 23760 LD BC,500

HALT

АДРЕС

МАШ.КОД F3

01 FE EF

ED 78

CB 47 20 F7

FB

C9

МАШ.КОД

01 F4 01 76

FD CB 01 6E 20 05

23760 L1

23761

23764

23766 23760

23770

23771

АДРЕС

23760 L1

23763

23761 23768

BIT 5,(IY+1) JR NZ,L2

23770

23771

23772

23773

L2

23775 23779

0B 78 B1

20 F7

FD CB 01 AE C9

DEC BC LD A,B OR C

JR NZ,L1

RES 5,(IY+1) RET

КОММЕНТАРИЙ

Запрет прерываний.

Установка в BC адреса внешнего порта, соответствующего верхнему правому полуряду клавиатуры. Ввод данных из порта в аккумулятор. Проверка нулевого бита. Если бит включен, то клавиша не нажата. Следует -повторять проверку. Разрешение прерываний перед выходом. Выход из процедуры.

Листинг 5.4

КОММЕНТАРИЙ

Размер паузы

Остановка процессора до прохождения прерывания. Проверка 9-го бита системной переменной FLAGS. Если 5-ый бит FLAGS включился, значит была нажата какая-то клавиша и пауза прерывается, следует переход на L2.

Уменьшение счетчика.

Проверка счетчика на 0. Если счетчик не обнулен, то пауза продолжается.

Принудительное выключение пятого бита FLAGS. Выход из процедуры.




СОДЕРЖАНИЕ:


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

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



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

Похожие статьи:
Паралельные измерения - демосцена на БК0011м.
Так кто же первый? - Когда и как появился Первый Отечественный Спектрум?..
Десяточка игр - TOP 10 игр.
For Coderz - Распознавание и вычисление арифметических выражений по их символьной записи.
SOFTWARE - О работе с программой для печати изображений XL-graph.

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