ПРИМЕНЕНИЕ АССЕМБЛЕРА ДЛЯ СОЗДАНИЯ БЫСТРОРАБОТАЮЩИХ ПРОГРАММ
Перевод с английского Пашорина В.И.
Продолжение. Начало см. с. 9-12, 48-61, 97-103
7. ДВИЖЕНИЕ
Теперь, когда мы познакомились со способами вывода информации на экран и способами считывания состояния клавиатуры, мы можем рассмотреть способы задания перемещения объектов на экране дисплея. Это, безусловно, важная часть любой игровой программы. В этой главе мы рассмотрим все возможные способы задания перемещения.
Для организации движения изображения по экрану большинство программистов используют знакоместа, что позволяет ускорить выполнение программы и оперировать с любым из возможных цветов. Весь экран представляет собой прямоугольную матрицу 24x32, т.е. всего 768 знакомест и поэтому перемещение символа из одного знакоместа в другое, соседнее с ним, выглядит не совсем плавным. При этом, если необходимо показать перемещение только одного пиксела, приходится переписывать весь символ из одного знакоместа в другое. Однако при этом, в отличие от BASIC-команды PLOT, нет ограничений на выбор цвета.
Перемещение знакоместа переднего плана экрана
Для начала необходимо определить стартовую позицию шаблона и затем, используя способы, рассмотренные в главе 5, установить шаблон в это знакоместо. Обычно начальное положение дублируется, поскольку полностью задать положение шаблона - это значит установить его текущее и последующее положения на экране дисплея. Для исключения мигания при перемещении шаблона необходимо сразу же выводить его в новой позиции целиком. Такой способ используется для задания режима OVER 1, при котором перемещение шаблона осуществляется на фоне заднего плана без изменения последнего.
Программа 7.1 демонстрирует этот способ. Здесь шаблон в виде космического корабля перемещается по экрану вверх/вниз при нажатии клавиш 1/Q и вправо /влево при нажатии клавиш 9/0. Клавиша "SPACE" используется для возврата в БЕЙСИК.
Листинг 7. 1.
КОММЕНТАРИЙ
;Отключение прерываний. ;Переход на начало работы. ;Шаблон символа CHR$ 144. ;Шаблон символа CHR$ 145. ;Шаблон символа CHR$ 145. ;Начало области UDG.
23760
23761
23763 DATA_1
23771
23779
23787 START 23791 23794 23797
135 7
LD DE, (23675)
LD HL,DATA_1
LD BC,24
18 0A JR BEGIN
22 0 0 16 8 17 8 144 145 146
LD (IY+14),40 LD (IY+83),15
Задание трех первых символов графики UDG.
Переход на начало работы. Эта последовательность кодов является символьной строкой, которую можно трактовать как AT 0,0;PAPER 8;INK 8; CHR$ 144;CHR$ 145;CHR$ 146. Выставляем цвет бордюра. Выставляем INK и PAPER.
ORG 23760 F3 DI
18 18 JR START
0 0 15 24 49 226 225 224 24 60 255 255 153 255 231 60
0 0 240 24 140 71 ED 5B 7B 5C 21 D3 5C
01 18 00
ED B0 LDIR
23819 |
|
3E |
02 |
|
LD A,02 |
|
23821 |
|
CD |
01 |
16 |
CALL 5633 |
Открыли канал печати на экран |
23824 |
|
CD |
6B |
0D |
CALL 3435 |
Очистка экрана. |
23827 |
|
3E |
02 |
|
LD A,02 |
|
23829 |
|
CD |
01 |
16 |
CALL 5633 |
|
23832 |
|
11 |
F9 |
5C |
LD DE,DATA 2 |
Начало символьной строки. |
23835 |
|
01 |
0A |
00 |
LD BC,10 |
Длина символьной строки. |
23838 |
|
CD |
3C |
20 |
CALL 8252 |
Печать символьной строки. |
23841 |
L7 |
11 |
00 |
5B |
LD DE,23296 |
Начало буфера принтера. |
23844 |
|
21 |
F9 |
5C |
LD HL,DATA 2 |
Начало символьной строки. |
23847 |
|
01 |
0A |
00 |
LD BC,10 |
Длина символьной строки. |
23850 |
|
ED |
B0 |
|
LDIR |
Переброска символьной строки DATA_2 в начало буфера принтера. |
23852 |
|
2A |
01 |
5B |
LD HL,(23297) |
Координаты текущей позиции печати. |
23855 |
|
01 |
FE |
EF |
LD BC,61438 |
|
23858 |
|
ED |
78 |
|
IN A,(C) |
Ввод данных с порта клавиатуры. Согласно тому числу, которое установлено в ВС,здесь принимается сигнал с правой половины верхнего ряда клавиш (клавиши 6...0). |
23860 |
|
CB |
47 |
|
BIT 0,A |
Проверка клавиши "0". |
23862 |
|
20 |
01 |
|
JR NZ,L1 |
Переход, если не нажата. |
23864 |
|
24 |
|
|
INC H |
Если нажата. |
23865 |
L1 |
CB |
4F |
|
BIT 1,A |
Проверка клавиши "9". |
23867 |
|
20 |
02 |
|
JR NZ,L2 |
Переход, если не нажата. |
23869 |
|
25 |
|
|
DEC H |
Если нажата. |
23870 |
L2 |
01 |
FE |
F7 |
LD BC,63486 |
|
23873 |
|
ED |
78 |
|
IN A,(C) |
Опрос полуряда (1...5). |
23875 |
|
CB |
47 |
|
BIT 0,A |
Проверка клавиши " 1 " . |
23877 |
|
20 |
01 |
|
JR NZ,L3 |
Переход, если не нажата. |
23879 |
|
2D |
|
|
DEC L |
Если нажата. |
23880 |
L3 |
01 |
FE |
FB |
LD BC,64510 |
|
23883 |
|
ED |
78 |
|
IN A,(C) |
Опрос полуряда (Q...T). |
23885 |
|
CB |
47 |
|
BIT 0,A |
Проверка клавиши "Q". |
23887 |
|
20 |
01 |
|
JR NZ,L4 |
Переход, если не нажата. |
23889 |
|
2C |
|
|
INC L |
Если нажата. |
23890 |
L4 |
7D |
|
|
LD A,L |
Координата по вертикали. |
23891 |
|
FE |
16 |
|
CP 22 |
Проверка на выход за пределы экрана снизу. |
23893 |
|
20 |
03 |
|
JR NZ,L5 |
Переход, если все в порядке. |
23895 |
|
2D |
|
|
DEC L |
Иначе уменьшаем L |
23896 |
|
18 |
05 |
|
JR L6 |
и переходим на метку L6. |
23898 |
L5 |
FE |
FF |
|
CP 255 |
Проверка на выход за пределы экрана сверху. |
23900 |
|
20 |
01 |
|
JR NZ,L6 |
Переход, если все в порядке. |
23902 |
|
2C |
|
|
INC L |
Иначе увеличиваем L. |
23903 |
L6 |
7C |
|
|
LD A,H |
|
23904 |
|
FE |
1E |
|
CP 30 |
Проверка на выход за пределы экрана справа. |
23906 |
|
20 |
03 |
|
JR NZ,L8 |
Переход, если все в порядке. |
23908 |
|
25 |
|
|
DEC H |
Иначе уменьшаем Н. |
23909 |
|
18 |
05 |
|
JR L9 |
и переходим на метку L9. |
23911 |
L8 |
FE |
FF |
|
CP 255 |
Проверка на выход за пределы экрана слева. |
23913 |
|
20 |
01 |
|
JR NZ,L9 |
Переход, если все в порядке. |
23915 |
|
24 |
|
|
INC H |
Иначе увеличиваем Н. |
23916 |
L9 |
ED |
5B |
01 5B |
LD DE,(23297) |
Координаты старой позиции печати. |
23920 |
|
AF |
|
|
XOR A |
Обнуление флага переноса (это важный момент при подготовке к исполнению команды SBC). |
23921 |
|
E5 |
|
|
PUSH HL |
Запомнили координаты новой |
|
|
|
|
|
|
позиции печати на стеке. |
23922 |
ED |
52 |
|
SBC HL,DE |
|
Проверяем изменилась ли позиция печати. |
23924 |
E1 |
|
|
POP HL |
|
Восстановили новую позицию. |
23925 |
28 |
2C |
|
JR Z,L12 |
|
Если позиция печати не не-нялась, делаем обход на L12 |
23927 |
22 |
01 |
5B |
LD (23297) |
HL |
Запомнили координаты новой позиции печати в буфере принтера. |
23930 |
FD |
36 |
57 03 |
LD (IY+87) |
3 |
Включение OVER 1. |
23934 |
11 |
F9 |
5C |
LD DE,DATA |
2 |
|
23937 |
01 |
0A |
00 |
LD BC,10 |
|
|
23940 |
CD |
3C |
20 |
CALL 8252 |
|
Печать в старых координатах |
а поскольку включен режим OVER 1, то это на самом деле не печать, а стирание.
23943 |
|
11 |
00 |
5B |
LD DE,23296 |
|
23946 |
|
01 |
0A |
00 |
LD BC,10 |
|
23949 |
|
CD |
3C |
20 |
CALL 8252 |
;Печать в новой позиции. |
23952 |
|
11 |
F9 |
5C |
LD DE,DATA 2 |
|
23955 |
|
21 |
00 |
5B |
LD HL,23296 |
|
23958 |
|
01 |
0A |
00 |
LD BC,10 |
|
23961 |
|
LD |
B0 |
|
LDIR |
;Копирование новой позиции ;в старую. |
23963 |
|
21 |
88 |
13 |
LD HL,5000 |
I |
23966 |
L11 |
2B |
|
|
DEC HL |
I |
23967 |
|
7C |
|
|
LD A,H |
|Цикл задержки. |
23968 |
|
B5 |
|
|
OR L |
I |
23969 |
|
20 |
FB |
|
JR NZ,L11 |
I |
23971 |
L12 |
01 |
FE |
7F |
LD BC,32766 |
|Опрос нажатия |
23974 |
|
ED |
78 |
|
IN A,(C) |
|клавиши |
23976 |
|
CB |
47 |
|
BIT 0,A |
|"ПРОБЕЛ". |
23978 |
|
C2 |
21 |
5D |
JP NZ,L7 |
;Если не нажата, переход на |
23981 |
|
FD |
36 |
57 |
00 LD (IY+87),0 |
;Восстановление режима OVER |
23985 |
|
FB |
|
|
EI |
;Разрешение прерываний. |
23986 |
|
C9 |
|
|
RET |
;Возврат в БЕЙСИК. |
Космический корабль может перемещаться одновременно в двух направлениях без изменения изображения заднего плана. Корабль выводится в цветах PAPER 8 и INK 8, чтобы оставаться контрастный на любом фоне заднего плана изображения. Скорость перемещения корабля регулируется с помощью цикла задержки. Если же корабль остается неподвижным, то процедура стирающая старое изображение и печатающая новое обходится, чтобы избежать мерцания.
Процедура хоть и выглядит немалой по своим размерам, тем не менее очень проста. Просмотрите ее, в ней все должно быть понятно и доступно.
Вы можете усовершенствовать эту программу так, чтобы космический корабль при выходе его за пределы экрана, появлялся с противоположной стороны.
Процедура в машинных кодах выполняется довольно быстро и если в цикле задержки записать LD HL,1, то за время перемещения корабля его изображение не успеет полностью восстановиться на новой позиции. Поэтому для большинства программ необходимы циклы задержки, чтобы сохранить изображение на время перемещения.
Перемещение изображения заднего плана экрана
Для задания перемещения заднего плана необходимо организовать перемещение не одного знакоместа, а всего экрана или его части. Это перемещение обычно выполняется в одном из 4 направлений: вверх, вниз, вправо, влево; причем перемещаются одновременно и символы и их атрибуты. Если смещение всего экрана происходит на одну символьную позицию, то необходимо решить, как поступить с символами, вышедшими за пределы экрана. Мы можем либо потерять их и выводить на противоположной стороне экрана пустые знакоместа, либо запомнить их вместе с атрибутами и затем выводить на экран в соответствующем месте на противоположной стороне. Такой эффект зацикливания изображения весьма полезен для игровых программ.
Программы с 7.2 по 7.5 демонстрируют смещение атрибутов без возврата с противоположной стороны для всех четырех направлений (такое движение принято называть "протяжкой" - SCROLL). Программы с 7.6 по 7.9 демонстрируют смещение атрибутов по всем четырем направлениям с возвратом (назовем это движение "прокруткой" - ROLL).
"Прокрутку" и "протяжку" символов для всех четырех направлений демонстрируют программы 7.10 ... 7.18.
Листинг 7.2
SCROLL вверх для атрибутов.
АДРЕС |
МЕТКА |
МАШ |
. КОД |
АССЕМБЛЕР КОММЕНТАРИЙ |
|
|
|
|
|
ORG 23760 |
|
23760 |
|
11 |
00 |
58 |
LD DE,22528 |
Начало области экранных атрибутов. |
23763 |
|
21 |
20 |
58 |
LD HL,22560 |
Начало области атрибутов для второго ряда знакомест. |
23766 |
|
01 |
E0 |
02 |
LD BC,736 |
Длина перебрасываемого блока (23 нижних строки по 32 знакоместа). |
23769 |
|
ED |
B0 |
|
LDIR |
Перенос атрибутов на одну строку вверх. |
23771 |
|
06 |
20 |
|
LD B,32 |
Заполнение нижней строки |
23773 |
|
3A |
8D |
5C |
LD A,(23693) |
экрана постоянными атрибута- |
23776 |
L2 |
12 |
|
|
LD (DE),A |
ми, взятыми из системной пе |
23777 |
|
13 |
|
|
INC DE |
ременной ATTR P(23693) |
23778 |
|
10 |
F2 |
|
DJNZ L2 |
|
23780 |
|
C9 |
|
|
RET |
Возврат. |
Эти программы легко усовершенствовать для организации ROLL и SCROLL только части экрана. Весь экран условно разбит на 3 сегмента, по 8 рядов в каждом. Каждый ряд, в свою очередь, состоит из 8 линий. Каждая линия в дисплейной области памяти занимает 32 байта. Причем записывается в эту область памяти сначала первая линия первого ряда, затем первая линия второго ряда и т.д. последовательно до первой линии восьмого ряда, включительно.
Затем записываются вторые линии первых восьми рядов, затем третьи и так последовательно весь сегмент, после чего записывается второй и за ним последовательно третий сегмент экрана. Каждый сегмент занимает 2046 байтов в дисплейной области памяти.
Обратите внимание, что в процедурах 7.3...7.18 используются команды передачи блока байтов - LDIR и LDDR. После выполнения этих команд в регистровых парах HL и DE остается записанный конечный адрес блока +1 (для LDIR) и конечный адрес - 1 (для LDDR), Это можно использовать для задания адреса продолжения выполнения программ процедуры, а не перезаписывать адреса в HL и DE.
Программы 7.2...7.18 написаны так, чтобы не вносить в них изменения при смене адреса загрузки. Программа 7.19 является усовершенствованной версией программы SCREEN UP SCROLL. Если Вам все понятно в ней, попробуйте сами усовершенствовать программу SCREEN DOWN SCROLL.
Сравнивая программы 7.15 и 7.19 Вы заметите что вторая занимает уже не 99, а только 87 байтов, хотя и это не абсолютно компактная программа. Весьма полезные детали искусства программирования, а также способы уменьшения объема памяти и времени работы показаны в программе 7.15. Эта программа перемещает вверх каждый символ ряда, линия за линией.
SCROLL вниз для атрибутов
LD DE,23295 LD HL,23253 LD BC,736 LDDR LD B,32 LD A,(23593) L7 LD (BE),A INC DE DJNZ L7 RET
Листинг 7.4
SCROLL влево для атрибутов
LD B,24 LD HL,22528 L12 PUSH HL POP DE PUSH BC INC HL LD BC,31 LDIR
LD A,(23693) LD (DE),A POP BC DJNZ L12 RET
Листинг 7.5
SCROLL вправо для атрибутов
LD B,24 LD HL,23295 L14 PUSH HL POP DE PUSH BC DEC HL LD BC,31 LDDR
LD A,(23693) LD (DE),A POP BC DJNZ,L14 RET
Листинг 7.6
ROLL вверх для атрибутов.
L16
L17
LD HL,22528 |
Начало ат |
|
рибутов. |
PUSH HL |
Копирование |
POP DE |
через стек. |
LD B,32 |
32 знако- |
|
места ряда. |
LD A,(HL) |
Запомнили |
PUSH AF |
атрибуты на |
|
стеке. |
INC HL |
Знакомес- |
|
то справа. |
DJNZ L16 |
Повтор для |
|
всего ряда. |
LD BC,736 |
Протяжка для |
LDIR |
23-х нижних |
|
рядов. |
LD B,32 |
Восстанавли- |
POP AF |
ваем атрибуты |
DEC HL |
верхнего ряда |
LD (HL),A |
в нижнем ряду |
DJNZ L17 |
экрана. |
RET |
Возврат. |
Листинг 7.7
ROLL вниз для атрибутов
LD HL,23295 PUSH HL POP DE LD B,32 LD A,(HL) PUSH AF DEC HL DJNZ L24 LD BC,736 LDDR LD B,32 POP AF INC HL LD (HL),A DJNZ L25 RET
Листинг 7.8
ROLL влево для атрибутов
LD B,24 LD HL,22528 PUSH HL POP DE PUSH BC LD A,(HL) INC HL LD BC,31 LDIR
LD (DE),A POP BC DJNZ L32 RET
Листинг 7.10 ROLL влево для
LD B,192 LD HL,16384
PUSH HL POP DE PUSH ВС LD A,(HL)
INC HL LD ВС,31 LDIR
LD (DE,A)
POP BC DJNZ L1
RET
символов.
Высота экрана. Начало экранной области. Копирование через стек. Запомнили BC. Запомнили текущую линию самого левого знакоместа. Адрес линии справа.
31 знакоместо в ряду. Копирование. Копирование линии самого левого знакоместа в самое правое.
Восстановление счетчика линий Если не все линии скопированы, то возврат на L1. Выход.
Листинг 7.9
ROLL вправо для атрибутов
LD B,24 LD HL,23295 PUSH HL POP DE PUSH BC LD A,(HL) DEC HL LD BC,31 LDDR
LD (DE),A POP BC DJNZ L33 RET
Листинг 7.11
ROLL вправо для символов
LD B,192 LD HL,22527 PUSH HL POP DE PUSH BC LD A,(HL) DEC HL LD BC,31 LDDR
LD (DE),A POP BC DJNZ,L1 RET
Листинг 7.12
SCROLL влево для символов.
LD HL,16384 XOR A LD B,192 L13 PUSH BC PUSH HL POP DE INC HL LD BC,31 LDIR
LD (DE),A POP BC DJNZ L13 RET
Листинг 7.13
SCROLL влево для символов. (второй вариант)
LD HL,16384 LD DE,32 LD B,192 L1 LD (HL),0 ADD HL,DE DJNZ L1 LD HL,16385 LD DE,16384 LD BC,6143 LDIR
EX DE,HL LD (HL),0 RET
Листинг 7.14
SCROLL вправо для символов.
LD HL,22527 XOR A LD B,192 L14 PUSH BC PUSH HL POP DE DEC HL LD BC,31 LDDR
LD (DE),A POP BC DJNZ L14 RET
SCROLL вверх для символов.
LD DE,16384 LD HL,16416 LD BC,2016 LDIR LD B,8 LD HL,18432 LD DE,16608 L3 PUSH BC PUSH HL PUSH DE LD BC,32 LDIR POP DE POP HL POP BC INC D INC H DJNZ L3 LD DE,18432 LD HL,18464 LD BC,2016 LDIR LD B,8 LD HL,20460 LD DE,18656 L4 PUSH BC PUSH HL PUSH DE LD BC,32 LDIR POP DE POP HL POP BC INC D INC H DJNZ L4 LD DE,20480 LD HL,20512 LD BC,2016 LDIR LD B,8 LD HL,20704 L5 PUSH BC PUSH HL LD B,32 L6 LD(HL),0 INC HL DJNZ L6 POP HL POP BC INC H DJNZ L5 RET
Листинг 7.16
ROLL вверх для символов.
LD HL,16384 LD B,32 LD A,(HL) PUSH AF INC HL DJNZ L19 XOR A LD L,A INC H LD A,H CP 72 JR NZ,L18 LD DE,16384 LD HL,16415 LD BC,2016 LDIR LD B,8 LD HL,18342 LD DE,16608 PUSH BC PUSH HL PUSH DE LD BC,32 LDIR POP DE POP HL POP BC INC D INC H DJNZ L20 LD DE,18432 LD HL,18464 LD BC,2016 LDIR LD B,8 LD HL,20480 LD DE 18656 PUSH BC PUSH HL PUSH DE LD BC,32 LDIR POP DE POP HL POP BC INC D INC H DJNZ L21 LD DE,20480 LD HL,20512 LD BC,2016 LDIR
LD HL,22527 LD B,32 POP AF LD (HL),A DEC HL DJNZ L23 LD A,255 LD L,A DEC H LD A,H CP 79 JR NZ,L22 RET
SCROLL вниз для символов.
LD DE,22527 LD HL,22495 LD BC,2016 LDDR LD B,8 LD HL,18656 LD DE,20480 L8 PUSH BC PUSH HL PUSH DE LD BC,32 LDIR POP DE POP HL POP BC INC D INC H DJNZ L6 LD DE,20479 LD HL,20447 LD BC,2016 LDDR LD B,8 LD HL,16608 LD DE,18432
L9 PUSH BC PUSH HL PUSH DE LD BC,32 LDIR POP DE POP HL POP BC INC D INC H DJNZ L9 LD DE,18431 LD HL,18399 LD BC,2016 LDDR
LD HL,16384 L10 LD B,32 L11 LD(HL),0 INC HL DJNZ L11 XOR A LD L,A INC H LD A,H CP 72 JR NZ,L10 RET
Листинг 7.18
ROLL вниз для символов
LD HL,22527 L26 LD B,32
Конец дисплейной области.
32 знакоместа в строке, Запомнили нижнюю линию текущего знакоместа. Знакоместо слева.
И так 8 раз. Возврат к предыдущей линии последнего знако -места ряда. Проверка на выход в 23-ю строку экрана Если нет, то возврат назад Копирование информации в третьем сегменте на ряд вниз.
Восемь линий. Адрес начала первой линии последнего ряда второго
L27 LD A,(HL)
PUSH AF
DEC HL
DJNZ L27 LD A,255
LD L,A DEC H LD A,H
CP 79
JR NZ,L26
LD DE,22527 LD HL,22495 LD BC,2016 LDDR
LD B,8 LD HL,18656
сегмента
LD DE,20480
L28 PUSH BC
PUSH HL PUSH DE
LD BC,32 LDIR
POP DE
POP HL
POP BC
INC D
INC H
DJNZ L28
LD DE,20479 LD HL,20447
LD BC,2016 LDDR
Адрес начала первой линии первого ряда третьего сег мента.
Сохранили РР-гистры от порчи командой LDIR. 32 байта в каждой линии. Перенос одной линии через границу сегментов. восстановление регистров
Переход к очередной линии ряда.
Возврат для переноса 8-ми линий через границу сегмента.
Движение информации во втором сегменте на ряд вниз.
LD B,8 LD HL,16608 LD DE,18432 L29 PUSH BC
PUSH HL PUSH DE LD BC,32 LDIR POP DE POP HL POP BC INC D INC H DJNZ L29 LD DE,18431 LD HL,18399 LD BC,2016 LDDR
LD HL,16384 LD B,32 POP AF LD (HL),A INC HL DJNZ L31 XOR A LD L,A INC H LD A,H CP 72
|Перенос
|информации
|через
|границу
|между
|первым
|и вторым
|сегментами.
;Движение ин-;формации в ;первом сег-;менте на ряд ;вниз.
;Начало экрана I
|Восстановле-|ние со стека |информации |для восьми |линий первого |ряда (на стек |она поступила |из восьми |нижних линий
Листинг 7.19
SCROLL вверх для символов (2-ой вариант)
;Начало дис-;плейной об: ласти памяти. ;Движение пер-;вого сегмента | Перенос |ряда через |границу
|сегментов. |
;Движение вто-;рого сегмента |Перенос ряда |через границу |сегментов.
CALL SEGM
EX DE,HL LD HL,18438 PUSH HL CALL UP POP HL CALL SEGM
EX DE,HL LD HL,20480 PUSH HL CALL UP POP HL CALL SEGM
Движение третьего сегмента.
Обнуление (стирание) информации в последнем ряду
третьего сегмента.
LD HL,20704 PUSH HL LD 8,32 LD (HL),0 INC HL DJNZ L3 POP HL INC H LD A,H CP 88 JR NZ,L4 RET
Продолжение Л. 7.19.
Нижеследующая процедура копирует все ряды (кроме верхнего) в текущем сегменте на один ряд выше.
Первый ряд сегмента не переносим, потому 7
Сохранили от "порчи".
Соответствующая линия следующего ряда отстоит на 32 байта от текущей. Копирование адреса через стек HL указывает на ряд ниже
Сохранили HL и DE от "порчи" командой LDIR Копирование ря-
PUSH HL POP DE ADD HL,BC
PUSH HL
PUSH DE CALL UP
;да вверх.
POP DE
POP HL POP BC DJNZ L1
Восстановление регистров.
Если не все 7 рядов скопированы, возврат. Выход.
Нижеследующая процедура копирует содержимое экранного ряда на один ряд выше.
LD B,8 |
8 линий в ряду. |
PUSH BC |
Сохранили регис |
PUSH HL |
тры от "порчи" |
PUSH DE |
командой LDIR. |
LD BC,32 |
32 байта в линии |
LDIR |
Перенос на один |
|
ряд вверх. |
POP DE |
|
POP HL |
;Восстановление |
POP BC |
;регистров. |
INC D |
Переход к оче- |
|
редной линии |
INC H |
текущего ряда. |
DJNZ L2 |
Если не все 8 |
|
линий скопирова- |
|
ны, возврат. |
RET |
Выход. |
Вместо программы, выполняющей перемещение сразу же целого ряда, можно написать программу, выполняющую перемещение только одного столбца. Чтобы переместить весь ряд, эта программа должна повториться 32 раза. В то же время, такое решение позволит перемещать не весь ряд, а только заданное количество столбцов и, более того, при таком решении нет необходимости хранить в памяти все 256 байтов верхнего ряда экрана (как, например, в программе 7.18), а только 8 байтов одного столбца ряда. Программа 7.20 показывает, как достигается такое уменьшение объема памяти. Здесь адрес первого байта смещаемого столбца записывается в регистровую пару DE, а общее число смещаемых столбцов записывается в регистр B. Эта программа может быть размещена в любом участке памяти.
Листинг 7.20.
SCROLL вверх по столбцам.
XOR A |
Сброс ак-ра. |
LD DE,16388 |
Адрес верхней |
|
линии 4-го |
|
столбца. |
LD B,10 |
Двигаем вверх |
|
10 столбцов. |
PUSH DE |
Сохранение ре- |
|
гистров |
PUSH BC |
от коррупции. |
PUSH DE |
Копирование из |
POP HL |
DE в HL. |
LD B,8 |
8 линий ряда. |
LD A,(HL) |
Запоминаем на |
PUSH AF |
стеке восемь |
INC H |
линий верхнего |
DJNZ L6 |
ряда. |
LD B,7 |
|
|
7 рядов сег |
|
мента. |
PUSH BC LD HL,32 ADD HL,DE
PUSH HL LD B,8
LD A,(HL) LD (DE),A INC H INC D DJNZ L1 POP DE POP BC DJNZ L2
Указание на следующую линию ряда.
Восемь линий в ряду. Копирование через акк-р для одного ряда
Восстановление регистров. Повтор для очередного ряда в сегменте. Переход через границу сегм. Проверка на конец экранной области.
Перенос восьми линий через границу сегмента.
LD HL,1824 ADD HL,DE LD A,H CP 88 JR Z,END PUSH HL LD B,8 LD A,(HL) LD (DE),A INC H INC D DJNZ L3 POP DE JR L4
Возврат для работы со следующий сегм. Восстановление со стека в нижнем ряду столбца того, что запомнили в самом верхнем ряду. Восстановление регистров. Переход к очередному столбцу экрана. Если не все 10 столбцов обслужены, возврат на L5. Выход.
LD DE,32 SBC HL,DE LD B,8 POP AF LD (HL),A DEC HL DJNZ L7 POP BC POP DE INC DE
Дальнейшее уменьшение объема памяти, занимаемого программой, может быть достигнуто, если выполнять перемещение одновременно не всех рядов, а только заданного их количества. При этом можно реализовать смещение на экране определенного окна с заданными размерами. Программа 7.31, занимающая всего 58 байтов, демонстрирует это решение. Для проверки выхода на границу сегмента экрана здесь используется команда BIT 0,H. Если нулевой бит регистра H не равен 0, то мы находимся на границе сегмента экрана и вместо добавления числа 32 к числу, записанному в регистровой паре DE, необходимо добавить число 1824 для перевода "первого" ряда в следующий сегмент. Число смещаемых рядов определяется числом шагов цикла L4. Адрес ячейки верхнего левого угла смещаемого окна может быть определен по формуле:
16384+(1824*INT (ROW/8)) + ((ROW - INT(ROW/8))*32 + COLUMN
Здесь ROW - номер экранной строки (в знакоместах). COLUMN - номер экранного столбца (в знакоместах).
Для смещения всего экрана в регистровой паре DE должно быть записано число 16384, в регистре B перед циклом L6 число 32 и здесь же, но перед циклом L4 -число 23. Эту программу можно переделать для организации смещения вверх/вниз.
Листинг 7.21.
XOR A
LD DE,16515
Левый верхний угол прокручиваемого "окна" имеет координаты X=3; Y=4.
Ширина "окна" - 7 знакомест.
Высота "окна" - 13 знакомест
LD B,7
PUSH DE PUSH BC PUSH DE POP HL LD B,8 LD A,(HL) PUSH AF INC H DJNZ L1 LD B,13
PUSH BC LD HL, 32 ADD HL,DE BIT 0,H JR Z,L2 LD HL,1824 ADD HL,DE PUSH HL LD B,8 LD A,(HL) LD (DE),A INC D INC H DJNZ L3 POP DE POP BC DJNZ L4 LD B,8 DEC H POP AF LD (HL),A DJNZ L5 POP BC POP DE POP DE INC DE DJNZ L6 RET
В ПЗУ компьютера ZX Spectrum имеется одна из наиболее полезных процедур -SCROLL n, позволяющая смещать n строк вместе с их атрибутами. Число n должно быть больше или равным 2 (но, конечно, не больше, чем 24).
Программа 7.22 показывает, как можно использовать эту процедуру для очистки строк с 16-ой по 24-ю включительно. Число строк отсчитывается от 24-й, при этом в регистр B записывается число строк-1 и девять раз вызывается процедура SCROLL n, находящаяся по
адресу 3584.
Листинг 7.22
LD B,9 L1 PUSH BC LD B,8 CALL 3584 POP BC DJNZ L1 RET
Ну, и в конце этой главы разберем процедуру, позволяющую одновременно смещать влево (или вправо) сразу же половину символа. Эта процедура хоть и короткая, но довольно сложная, т.к. здесь используются новые команды. Например, команда RLD перемещает биты с 0-го по 3-й в биты с 7-го по 4-й в ячейке, адрес которой находится в регистровой паре HL. Биты же с 7-го по 4-й из этой ячейки переписываются в биты с 0-го по 3-й в регистр А. А данные, которые были в этом регистре в битах с 0-го по 3-й переписываются в ячейку, адрес которой находится в регистровой паре HL, и занимают здесь соответственно 0...3 биты. Эта команда не влияет на флаги состояния процессора.
Листинг 7.23
DI
LD HL,22527 LD B,196 L1 PUSH BC XOR A LD B,32 PUSH HL L2 RLD
DEC HL DJNZ L2 POP DE EX DE,HL ADD A,(HL) LD (HL),A EX DE,HL POP BC DJNZ L1 EI RET
В программе 7.23 для каждой строки экрана используется команда RLD для перемещения правой половины каждого символа в левую часть. Левая половина первого символа при этом остается пустой, т.к. для каждой строки изначально в регистре A записан 0. Чтобы организовать циркулярное смещение, необходимо временно запоминать значение левой половины первого символа, а затем выводить ее, как правую половину последнего символа.