ОРГАНИЗАЦИЯ УПРАВЛЕНИЯ И ИНТЕРФЕЙС "СТРЕЛКА".
(c) Сергей Колотов, г. Шадринск
copyright SerzhSoft in 1996
Как уже не раз замечалось в ZX РЕВЮ, множество авторских программ, мягко
говоря, хронически страдает от некорректно организованного управления (опроса
джойстиков, клавиатуры). Отсутствует стандарт, каждый программист, например,
может "подвязать" свои особые клавиши, удобные лишь только для
него самого. Хотя и существуют некоторые неофициальные стандарты (например,
клавиши Q,A,O,P,SPACE), но и они не всегда выполняются.
Данная статья была написана в надежде на то, что бесчисленные отряды
отечественных программистов все же постараются при разработке своих программных
продуктов быть ближе к народу, к пользователям. Пусть установка каждого кодера
будет такой: "все лучшее - юзерам" ! Автор надеется, что представленные в этой
статье программы, будут интересны как начинающим программистам, так и более
опытным, которых, как мне кажется, в нашей стране большинство.
При разработке любой программы, рассчитанной на широкий круг пользователей,
необходимо удовлетворить любого и всех вместе. Мудрая пословица гласит: "На
вкус и цвет - товарищей нет!" При разработке программных продуктов необходимо
отталкиваться именно от этой истины.
Приведенная ниже библиотека процедур предназначена для опроса всех видов
джойстиков и клавиатуры. Представлены: KEMPSTON, CURSOR, SINCLAIR (LEFT&RIGHT)
джойстики; клавиши Q,A,O,P,SPACE; клавиши, заданные "по вкусу" (в данном
случае: Z,X,C,F,G). Все подпрограммы можно вызывать как по отдельности, так
и все вместе, параллельно. Полученные данные преобразуются в формат KEMPSTON-
джойстика, т. е. результатом является байт с установленными в нем битами,
отвечающими за флаги направлений и FIRE. Этот результат помещается как в
регистр A (аккумулятор), так и в ячейку DIRECT.
Напомню вкратце -
Формат KEMPSTON:
!-------!-----------!-------!-----------!
! биты: ! означают: ! биты: ! означают: !
!-------!-----------!-------!-----------!
! 0 ! вправо ! 3 ! вверх !
! 1 ! влево ! 4 ! огонь !
! 2 ! вниз ! 5,6,7 ! не исп. !
!-------!-----------!-------!-----------!
Итак, библиотека SCAN LIBRARY:
Listing 1.
; Written by Kolotov Sergey
; copyright SerzhSoft from Shadrinsk (C)1996
;
ORG 64000 ;адрес ассемблирования
;----------------------------------;
; SCAN LIBRARY ;
; (JOYSTICKS AND KEYBOARD SCAN) ;
; переводит все в KEMPSTON-формат ;
;----------------------------------;
;можно вызывать:
; CALL DIRSCN - опрос всех дж-ов и кл-ры
; CALL KEMPST - опрос KEMPSTON-дж.
; CALL CURSOR - опрос CURSOR-дж. ( клавиши 5,6,7,8,0 )
; CALL SINC_L - опрос левого SINCLAIR-дж. ( 1,2,3,4,5 )
; CALL SINC_R - опрос правого SINCLAIR-дж. ( 6,7,8,9,0 )
; CALL QAOPSP - опрос клавиатуры ( Q,A,O,P,SPACE )
; CALL DEFINE - опрос заданных клави{ ( Z,X,C,F,G )
;все вышеуказанные процедуры на выходе устанавливают определен-
; ные биты в аккымуляторе (KEMPSTON-формат) - результат опроса.
; также результат запоминается в переменной DIRECT
;
; CALL OUTDIR - 'расфасовка' битов направлений из ячейки
; DIRECT в переменные D_RIGH...D_FIRE
; удобно использовать из BASIC
;----------------------------------;
DIRSCN ;опрос всех джойстиков и клавиатуры
CALL KEMPST ;опрос KEMPSTON'а
LD E,A ;запомнить в регистре E
LD A,#FE ;если
IN A,(#FE) ; нажат
RRCA ; CAPS SHIFT,
JR NC,INCURS ; то перейти на опрос CURSOR-джойст.
CALL SINC_L ;иначе - опрос левого синклера
OR E ; и добавление по OR
LD E,A ; к регистру E
CALL SINC_R ;опрос правого синклера
NOSINC OR E ;добавление по OR
LD E,A ; к регистру E
CALL QAOPSP ;опрос клави{ Q,A,O,P,SPACE
OR E ;добавка по OR
LD E,A ;запомнить в E
CALL DEFINE ;опрос заданных клавиш
OR E ;добавка по OR
EXDIR0 AND #1F ;отброс лишних битов (7,6,5)
LD (DIRECT),A ;все флаги направлений - в ячейку
RET ;выход из процедуры
INCURS CALL CURSOR ;опрос CURSOR-джойстика
JR NOSINC ;переход на продолжение
;
OUTDIR ;'расфасовка' флагов направлений
LD HL,DIRECT ; из ячейки DIRECT по Oдному биту
LD A,(HL) ; в ячейки D_RIGH...D_FIRE
LD B,#05 ; ( сделано преимущественно Dля
LP_DIR INC HL ; BASIC'а )
LD (HL),#00 ;то есть, например, Eсли в перемен-
RRCA ; ной D_LEFT после вызова OUTDIR
RL (HL) ; появилась единичка, то значит в
DJNZ LP_DIR ; ячейке DIRECT был включен бит 1,
RET ; что означает 'движение влево'
;
KEMPST ;опрос KEMPSTON-джойстика
IN A,(#1F) ;в A - значение порта 31
INC A ;если а=255 (дж. не подсоединен),
JR Z,EXDIR0 ; то все флаги напр-й равны нулю
DEC A ;иначE - оставить все как было
JR EXDIR0 ;переход на завершение
;
QAOPSP ;опрос клави{ Q,A,O,P,SPACE
LD A,#7F ;старший байт порта клавиатуры
IN A,(#FE) ;A=IN #7FFE ( SPACE,S.S.,M,N,B )
RRCA ;C(FLAG)=0, если кл. 'SPACE' нажата
RL C ;'провернуть' в регистр C
LD A,#FB ;старший байт
IN A,(#FE) ;A=IN #FBFE ( Q,W,E,R,T,Y )
RRCA ;C(FLAG)=0, если кл. 'Q' нажата
RL C ;'провернуть'
LD A,#FD ;старший байт
IN A,(#FE) ;A=IN #FDFE ( A,S,D,F,G )
RRCA ;C(FLAG)=0, если кл. 'A' нажата
RL C ;'провернуть'
LD A,#DF ;старший байт
IN A,(#FE) ;A=IN #DFFE
EXDIR1 RRCA ;
RRCA ;C(FLAG)=0, если кл. 'о' нажата
RL C ;'провернуть'
RLCA ;
RLCA ;C(FLAG)=0, если кл. 'P' нажата
EXDIR2 RL C ;'провернуть'
EXDIR3 LD A,C ;занести в а инвертированные флаги
CPL ; направлений и проинвертировать
JR EXDIR0 ;переход на окончание
;
CURSOR ;опрос CURSOR-джойстика
LD A,#EF ;( клавиши 5,6,7,8,0 )
IN A,(#FE) ;A=IN #EFFE ( 6,7,8,9,0 )
RRCA ;C(FLAG)=0, если кл. '0' нажата
RL C ;'провернуть' в регистр с
RRCA ;
RRCA ;C(FLAG)=0, если кл. '8' нажата
PUSH AF ;занести в стек
RRCA ;C(FLAG)=0, если кл. '7' нажата
RL C ;'провернуть'
RRCA ;C(FLAG)=0, если кл. '6' нажата
RL C ;'провернуть'
LD A,#F7 ;старший байт порта клавиатуры
IN A,(#FE) ;A=IN #F7FE ( 1,2,3,4,5 )
AND #10 ;проверка 4-го бита ( кл. '5' )
ADD A,#FF ;C(FLAG)=0, если кл. '5' нажата
RL C ;'провернуть'
POP AF ;восстановить AF
JR EXDIR2 ;переход на продолжение
;
SINC_L ;опрос левого SINCLAIR-джойстика
LD A,#F7 ;( клавиши 1,2,3,4,5 )
IN A,(#FE) ;A=IN #F7FE
RRCA ;C(FLAG)=0, если кл. '1' нажата
PUSH AF ;запомнить в стеке
RRCA ;биты : 2 1 0
LD C,A ;знач.: огонь вверх вниз
POP AF ;восстановить AF из стека
RL C ;'провернуть' в C
RRCA ;провернуть A
JR EXDIR2 ;перейти на продолжение
;
SINC_R ;опрос правого SINCLAIR-джойстика
LD A,#EF ;( клавиши 6,7,8,9,0 )
IN A,(#FE) ;A=IN #EFFE
LD B,#03 ;пихать в регистр C
LPSINC RRCA ; флаги:
RL C ; огонь вверх вниз
DJNZ LPSINC ; ( биты 2 1 0 )
JR EXDIR1 ;переход на продолжение
;
DEFINE ;опрос определенных клавиш
LD HL,KEYTBL ;HL=адрес таблицы клавиш
LD B,#05 ;B=5 - число клавиш
LP_DEF LD A,(HL) ;A=старший байт порта клавиатуры
INC HL ;следующий байт таблицы
IN A,(#FE) ;считали состояние клавиатуры
AND (HL) ;оставили нужный бит (по таблице)
INC HL ;следующий байт таблицы
ADD A,#FF ;если клавиша нажата, то C(FLAG)=0
RL C ;'отправили' бит в регистр C
DJNZ LP_DEF ;цикл из пяти раз - пять клавиш
JR EXDIR3 ;переход на окончание
;
KEYTBL ; порт,бит ;таблица заданных клавиш
DEFB #FD,#10 ;'G'-FIRE огонь
DEFB #FD,#08 ;'F'-UP вверх
DEFB #FE,#08 ;'C'-DOWN вниз
DEFB #FE,#02 ;'Z'-LEFT влево
DEFB #FE,#04 ;'X'-RIGHT вправо
;
DIRECT DEFB 0 ;байт всех флагов направлений
;биты : 0 1 2 3 4
;значения : вправо влево вниз вверх огонь
;
D_RIGH DEFB 0 ;флаг 'направление вправо'
D_LEFT DEFB 0 ;флаг 'направление влево'
D_DOWN DEFB 0 ;флаг 'направление вниз'
D_UP DEFB 0 ;флаг 'направление вверх'
D_FIRE DEFB 0 ;флаг 'огонь'
;
;END OF SCAN LIBRARY
Длина процедуры вместе с переменными равна 192 байта.
Итак, можете вызывать:
CALL DIRSCN - сканирование всех джойстиков и клавиатуры. Если клавиша
CAPS SHIFT нажата, то опрашиваются курсоры, иначе - SINCLAIR-джойстики.
CALL OUTDIR - если Вам привычно работать не с одним единственным байтом с
изменяющимися битами направлений, а с отдельными переменными (а в бейсике
иначе и не получится - слишком сложно!), то вызывайте эту подпрограмму.
Биты из переменной DIRECT "расфасуются" в переменные D_RIGHT, D_LEFT,
D_DOWN, D_UP, D_FIRE (0-выкл/1-вкл).
CALL KEMPST - опрос кемпстон-джойстика.
CALL QAOPSP - клавиши Q,A,O,P,SPACE.
CALL CURSOR - курсоры.
CALL SINC_L - левый синклер-джойстик.
CALL SINC_R - правый синклер-джойстик.
CALL DEFINE - опрос клавиш, заданных по желанию. Описание клавиш помещается в
таблицу KEYTBL. Первый байт - не что иное, как старший байт порта клавиатуры,
а второй байт - маска нужной клавиши (бит установлен в единицу).
Переменные:
DIRECT:db(0..31) - байт с флагами на-
правлений в формате KEMPSTON
D_RIGH:db(0/1) - "направление вправо"
D_LEFT:db(0/1) - "направление влево"
D_DOWN:db(0/1) - "направление вниз"
D_UP :db(0/1) - "направление вверх"
D_FIRE:db(0/1) - "огонь"
Шестнадцатеричный дамп SCAN LIBRARY:
( для примера оттранслировано
под адрес 64000 (#FA00) )
Listing 2.
Шестнадцатеричный дамп
SCAN LIBRARY
FA00: CD 38 FA 5F 3E FE DB FE :6D
FA08: 0F 30 19 CD 82 FA B3 5F :B5
FA10: CD 90 FA B3 5F CD 40 FA :7A
FA18: B3 5F CD 9D FA B3 E6 1F :40
FA20: 32 BA FA C9 CD 65 FA 18 :0D
FA28: EA 21 BA FA 7E 06 05 23 :8D
FA30: 36 00 0F CB 16 10 F8 C9 :21
FA38: DB 1F 3C 28 E1 3D 18 DE :A4
FA40: 3E 7F DB FE 0F CB 11 3E :F9
FA48: FB DB FE 0F CB 11 3E FD :3C
FA50: DB FE 0F CB 11 3E DF DB :06
FA58: FE 0F 0F CB 11 07 07 CB :23
FA60: 11 79 2F 18 B9 3E EF DB :EC
FA68: FE 0F CB 11 0F 0F F5 0F :6D
FA70: CB 11 0F CB 11 3E F7 DB :41
FA78: FE E6 10 C6 FF CB 11 F1 :F8
FA80: 18 DD 3E F7 DB FE 0F F5 :81
FA88: 0F 4F F1 CB 11 0F 18 CF :A3
FA90: 3E EF DB FE 06 03 0F CB :73
FA98: 11 10 FB 18 BC 21 B0 FA :4D
FAA0: 06 05 7E 23 DB FE A6 23 :E8
FAA8: C6 FF CB 11 10 F4 18 B1 :10
FAB0: FD 10 FD 08 FE 08 FE 02 :C2
FAB8: FE 04 00 00 00 00 00 00 :B4
Сохранение: SAVE "scnlib.c"CODE 64000,192
Адреса процедур и переменных:
DIRSCN 64000 #FA00 KEYTBL 64176 #FAB0
OUTDIR 64041 #FA29 DIRECT 64186 #FABA
KEMPST 64056 #FA38 D_RIGH 64187 #FABB
QAOPSP 64064 #FA40 D_LEFT 64188 #FABC
CURSOR 64101 #FA65 D_DOWN 64189 #FABD
SINC_L 64130 #FA82 D_UP 64190 #FABE
SINC_R 64144 #FA90 D_FIRE 64191 #FABF
DEFINE 64157 #FA9D
Преимущества использования
SCAN LIBRARY:
1. Простота и малый объем.
2. Опрос любых типов джойстиков и клавиатуры.
3. Возможность одновременного параллельного сканирования всех видов управления.
4. Единый стандарт выходных данных (KEMPSTON-формат).
5. Возможность использования из BASIC.
Очень простая организация движения "наискосок" и/или с нажатием FIRE,
что в бейсике реализовать довольнотаки сложно.
Пример BASIC-программы, использующей SCAN LIBRARY.
Listing 3.
Демонстрационный пример BASIC-программы,
использующей SCAN LIBRARY.
( простейший граф-редактор )
10 BORDER 0:POKE 23693,56:CLEAR 29999
20 LET x=10:LET y=10
30 LET a$=SCREEN$ (y,x)
33 PRINT AT y,x; OVER 1;"#"
36 PRINT #0;AT 1,0;"Under Cursor:";OVER 0;a$
40 RANDOMIZE USR 64000
50 RANDOMIZE USR 64041
56 PRINT AT y,x;OVER 0;a$
58 IF PEEK 64191 THEN PRINT AT y,x;OVER 1;"#"
60 LET x=x+(PEEK 64187 AND x<31)-(PEEK 64188 AND x>0)
70 LET y=y-(PEEK 64190 AND y>0)+(PEEK 64189 AND y<21)
80 GO TO 30
Описание работы:
В строке 10 устанавливаются атрибуты экрана (BORDER 0:PAPER 7:INK 0) и
выполняется очистка переменных, стек переносится в адрес 29999. Затем (строка
20) инициализируются переменные, указывающие на текущие координаты в экране.
В 30-ой строке в a$ заносится символ, взятый с экрана в текущих координатах
(символ под курсором - это и печатается внизу экрана, см. строку 36). На символ
в экране тут же накладывается "#" (ведь надо же отметить положение курсора!).
Далее, в строке 40 происходит вызов SCAN LIBRARY - опрос всех джойстиков и
клавиатуры. 50 строка ознаменует собой великое дело - вызов процедуры OUTDIR,
что преобразует одну переменную DIRECT в целую "пачку" - D_RIGH...D_FIRE. В 56
строке восстанавливается символ экрана, и если нажато FIRE, то в строке 58 этот
символ меняется на противоположный.
60, 70-ая строки изменяют текущие координаты в зависимости от содержимого
переменных D_RIGH...D_UP (флаги направлений). Команды AND здесь присутствуют
для того, чтобы координаты не выходили за край экрана (x=x-(1 AND x>0) - будет
вычитать единицу, только если x>0).
Ну, а 80-ая строка, как уже все догадались, выполняет переход к 30-ой.
Данная демонстрационная программа на бейсике представляет из себя простейший
графический редактор, позволяющий рисовать "картинки", используя символ "#"
(можно заменить на любой другой).
Примером использования SCAN LIBRARY в машинных кодах авляется, по сути,
отдельная разработка - ARROW LIBRARY. Это библиотека процедур для управления
передвижением стрелки по экрану. Она состоит из нескольких подпрограмм:
формирование образа стрелки с сохранением изображения под ней, восстановление
прежнего вида экрана, контроль перемещения стрелки и т. д.
ARROW LIBRARY - это интеллектуальный пользовательский интерфейс. Вы спросите:
почему интеллектуальный? Все дело в том, что этот интерфейс как-бы
"подстраивается" под пользователя. Человек работает с какой-либо программой,
перемещает стрелку по экрану, выбирая определенные пункты меню, рисуя и т. д.
Но, если скорость стрелки будет постоянна, то при необходимости перегнать ее с
одного края экрана к другому, пользователь неизбежно будет вынужден ждать,
попусту теряя время. Можно попробовать этого избежать, увеличив скорость, но в
итоге появится еще более неприятная проблема - снизится точность.
Разъяренный USER, безбожно ругая очень нехорошими словами и программу, и
программиста, ее написавшего, будет безрезультатно пытаться попасть в заветный
пункт меню и, вполне вероятно, вскоре забросит эту программу куда подальше...
Интелектуальный интерфейс действует более эффективно. Если человек, например,
долго держит в нажатом состоянии кнопку "вправо", то через определенное число
шагов скорость автоматически увеличивается. При отпускании кнопки, скорость
вновь устанавливается минимальной. Так и организуется управление, более удобное
для пользователя.
Но хватит слов, больше дела... Вот листинг библиотеки ARROW LIBRARY:
Listing 4.
; Written by Kolotov Sergey
; copyright SerzhSoft from Shadrinsk (C)1996
;
ORG 64192
;----------------------------------;
; ARROW LIBRARY ;
; ( вызывает SCAN LIBRARY ) ;
;----------------------------------;
;использование :
; CALL ARWINT - автоматическая организация уравления стрелкой.
; нажатие FIRE - выход. разрешает прерывания
; CALL INITMV - инициализация переменных стрелки
; CALL PUTARW - поместить стрелку на экран в текущих координатах
; CALL MOVES - перемещение стрелки, изменение скорости...
; CALL GETARW - убрать стрелку (восстановить место под стрелкой)
; CALL INC_Y - переход к следующей строке экрана
;----------------------------------;
ARWINT ;управление стрелкой (огонь - выход)
EI ;разрешить прерывания ( для HALT'а )
CALL INITMV ;инициализация переменных стрелки
LPINT CALL PUTARW ;вывод стрелки в текуших координатах
CALL DIRSCN ;сканирование клав-ры и джойстиков
CALL MOVES ;перемещение стрелки
HALT ;дождаться вывода на экран
CALL GETARW ;восстановить место под стрелкой
LD A,(DIRECT) ;флаги направлений
AND #10 ;флаг 'огонь' включен ?
JR Z,LPINT ;ели нет, то повторять цикл
RET ;возврат из процедуры
;
INITMV ;инициализация переменных стрелки
LD HL,STEP ;в HL - адрес констант
LD DE,STPCNT ;в DE - адрес переменных
LD BC,3 ;всего: 3 байта
LDIR ;переброска
RET ;возврат из процедуры
;
MOVES ;управление движением стрелки
LD A,(DIRECT) ;взять флаги направлений
PUSH AF ;запомнить байт флагов направлений
CALL MVEXAM ;вызов процедуры изменения скорости
POP AF ;вспомнить флаги направлений
LD C,A ;скопировать в регистр C
RRCA ;промотать вправо
AND #05 ;оставить только 2 бита: 0,2
XOR C ;проксорить
LD B,A ;поместить в регистр B
RRCA ;C(FLAG)=1, если влево/вправо
LD HL,ARWXMN ;в HL - адрес X-минимума
LD DE,ARW_X ;в DE - адрес X-координаты
PUSH BC ;запомнить BC
CALL C,XYMOVE ;если влево/вправо, то процедура
POP BC ;восстановить BC
RRC C ;
RRC C ;промотать регистр C вправо 2 раза
LD A,B ;A=B (ксорка направлений)
RRCA ;
RRCA ;
RRCA ;мотать вправо 3 раза
INC DE ;DE=адрес(ARW_Y) - Y-координата
LD HL,ARWYMN ;HL=адрес(ARWYMN) - Y-минимум
JR C,XYMOVE ;если вверх/вниз, то переход
RET ;возврат из процедуры
;
MVEXAM ;проверка и изменение скорости
LD BC,(DIROLD) ;в C - старые флаги направлений
LD (DIROLD),A ;поместить новые флаги в ячейку
CP C ;если старые не равны новым, то
JR NZ,INITMV ; инициализация переменных
AND #0F ;отброс бита (флага) 'огонь'
JR Z,INITMV ;если нет направлений, то иниц-я
LD HL,STPCNT ;HL=адрес переменной числа шагов
DEC (HL) ;уменьшить число шагов
RET NZ ;если не равно нулю, то выход
LD A,(STEP) ;A=константа числа шагов
LD (HL),A ;поместить в переменную
INC HL ;перейти к переменной LEVCNT
LD A,(HL) ;A=число уровней изменения скорости
DEC A ;уменьшить
RET Z ;выход, если A=0 (LEVCNT=1)
LD (HL),A ;поместить в переменную
INC HL ;перейти к переменной SPDCNT
INC (HL) ;увеличить скорость. при желании,
; можно заменить на RLC (HL), но
; тогда LEVEL должен быть <= 8
RET ;возврат из подпрограммы
;
XYMOVE ;перемещение по X или по Y
LD A,C ;
RRCA ;C(FLAG)=1, если увеличение коорD-T
LD A,(SPDCNT) ;в A - значение скорости
LD C,A ;поместить в регистр C
LD A,(DE) ;в A - значение координаты
JR C,XYINC ;если увел. координат, то переход
SUB C ;вычесть из координаты скорость
JR C,XYMNX ;если меньше нуля, то мин. зн-е
LD (DE),A ;новая координата
CP (HL) ;проверка на минимальную координату
JR C,XYMNX ;если меньше минимума, то мин. зн-е
RET ;возврат из процедуры
;
XYINC ADD A,C ;прибавить к координате скорость
INC HL ;перейти к максимальному значению
JR C,XYMNX ;если координата > 255 то макс. зн-е
LD (DE),A ;новая координата
CP (HL) ;проверка на макс. значение
RET C ;возврат, если меньше максимума
XYMNX LD A,(HL) ;граничное значение поместить
LD (DE),A ; в координату
RET ;возврат из процедуры
;
PUTARW ;вывод стрелки в текущие координаты
LD DE,(ARW_X) ;E=текущая X, D=текущая Y координаты
LD A,D ;
OR A ;Y=0 ?
JR Z,ZERO_1 ;если да, то не уменьшать
DEC D ;Y=Y-1 (для пустой линии вверху)
ZERO_1 LD A,E ;
OR A ;X=0 ?
JR Z,ZERO_2 ;если да, то не уменьшать
DEC E ;X=X-1 (для пустой линии слева)
ZERO_2 LD A,D ;-----------------------------------
RRA ;расчет адреса верхней линии
SCF ; стрелки в экране
RRA ; вход:
RRA ; E=X-координата
AND #5F ; D=Y-координата
LD H,A ; выход:
XOR E ; HL=адрес байта пикселя (X,Y)
AND #07 ; в экранной области
XOR E ;
RRCA ; 22 байта, 85 тактов
RRCA ;
RRCA ;
LD L,A ;
LD A,D ;
XOR H ;
AND #07 ;
XOR H ;
LD H,A ;-----------------------------------
LD (XPLACE),HL ;в ячейку - для пр-ры GETARW
CP #58 ;если выход за пределы экрана,
RET NC ; то закончить процедуру
PUSH HL ;запомнить на стеке
LD DE,PLACE ;DE=адрес буфера
DEFB 1,#FF,ARW_YR; LD C,255 ; LD B,ARW_YR
LP1 LDI ;перенести
LDI ; два байта
DEC HL ;вернуться
DEC HL ; назад
CALL INC_Y ;адрес следующей линии экрана
DJNZ LP1 ;цикл ARW_YR раз
POP HL ;вспомнить адрес верх. линии стрелки
LD A,(ARW_X) ;A=X-координата стрелки
SUB 1 ;если A-1<0, TO
JR C,ZERO_3 ; переход
AND 7 ;отбросить лишние биты
ZERO_3 SUB 8 ;вычесть 8
NEG ;A=-A
LD B,A ;B=число ротаций байтов стр-ки влево
LD DE,ARWSPR ;адрес образа стрелки
LD C,ARW_YR ;C=высота стрелки
LD A,(ARW_Y) ;A=Y-координата стрелки
OR A ;если Y<>0, то
JR NZ,LP2 ; переход
INC DE ;т.к. Y-координата равна 0,
INC DE ; то верхнюю пустую линию
DEC C ; не печатаем (пропускаем)
LP2 EX DE,HL ;поменять HL и DE
PUSH BC ;запомнить
PUSH HL ; регистры
PUSH DE ; на стеке
LD E,(HL) ;байт маски стрелки
LD D,#FF ;старший байт маски всегда заполнен
INC HL ;переход к байту рисунка стрелки
LD L,(HL) ;его считывание
LD H,#00 ;здесь ст. байт всегда равен 0
LP3 SLI E ;вращение
RL D ; маски стрелки
ADD HL,HL ;вращение образа стрелки
DJNZ LP3 ;повторять нужное кол-во раз
LD B,H ;B=H
LD C,L ;C=L
POP HL ;восстановить HL
LD A,(HL) ;байт из экрана
AND D ;убрать лишние биты по маске
OR B ;наложить образ стрелки
LD (HL),A ;назад в экран
INC L ;следующий байт
LD A,L ;если вышли
AND 31 ; за правый край экрана,
JR Z,ZERO_4 ; то след. байт не печатаем
LD A,(HL) ;иначе -
AND E ; те же операции
OR C ; выполняются
LD (HL),A ; для второго байта
ZERO_4 DEC L ;вернулись назад
CALL INC_Y ;следующая линия экрана
POP DE ;восстановление
POP BC ; регистров
INC DE ;возврат
INC DE ; назад
LD A,H ;проверка на выход
CP #58 ; за пределы экрана
RET NC ;если - да, то выход
DEC C ;сленующая линия
JR NZ,LP2 ;повторить цикл
RET ;возврат из процедуры
;
GETARW ;восстановление места на экране
LD HL,(XPLACE) ; под стрелкой
LD DE,PLACE
LD B,ARW_YR ;B=высота стрелки
LP4 LD A,H ;если адрес стрелки вне экрана
CP #58 ; (адрес > 22527),
RET NC ; то выйти из процедуры
LD A,(DE) ;перенос байта
LD (HL),A ; из буфера на экран
INC HL ;следующий
INC DE ; байт
LD A,(DE) ;перенос
LD (HL),A ; следующего байта
INC DE ;возврат
DEC HL ; на байт назад
CALL INC_Y ;следующая линия экрана
DJNZ LP4 ;цикл по строкам
LD H,#58 ;все, один раз напечатали и
LD (XPLACE),HL ; больше не нужно
RET ;возврат из процедуры
;
INC_Y ;вычисление адреса следующей линии в экране
INC H ;увеличить H
LD A,H ;если следующая линия находится в том же
AND 7 ; знакоместе, что и предыдущая,
RET NZ ; то выйти
LD A,L ;иначе
ADD A,32 ; прибавить 32 к регистру L
LD L,A ; ( переход к следующему ряду экрана )
RET C ;если след. сегмент экрана, то все о.к.
LD A,H ;иначе
SUB 8 ; уменьшить
LD H,A ; регистр H на 8
RET ;возврат из процедуры
;
ARW_YR EQU 12 ;высота стрелки в пикселях
;
ARW_X DEFB 127 ;координата X стрелки
ARW_Y DEFB 95 ;координата Y стрелки
;
ARWXMN DEFB 0 ;минимальная X-координата
ARWXMX DEFB 255 ;максимальная X-координата
ARWYMN DEFB 0 ;минимальная Y-координата
ARWYMX DEFB 191 ;максимальная Y-координата
;
STEP DEFB 8 ;число шагов, после которого увеличивается
; скорость стрелки
LEVEL DEFB 10 ;макс. число уровней увеличения скорости
SPEED DEFB 1 ;начальная скорость (в пикселях)
;
STPCNT DEFB 0 ;счетчик шагов
LVLCNT DEFB 0 ;счетчик уровней
SPDCNT DEFB 0 ;счетчик скорости
;
DIROLD DEFB 0 ;хранение предыдущих флагов направлений
;
XPLACE DEFW 22528;адрес места под стрелкой на экране
;
ARWSPR ;образ стрелки
DEFB #3F,#00 ;1 байт - маска
DEFB #1F,#40 ;2 байт - стрелка
DEFB #0F,#60 ;всего 12*2=24 байта
DEFB #07,#70
DEFB #03,#78
DEFB #01,#7C
DEFB #03,#70
DEFB #03,#48
DEFB #A3,#08
DEFB #F1,#04
DEFB #F1,#04
DEFB #F9,#00
;
PLACE DEFS 12*2 ;копия места на экране под стрелкой
;(буфер)
; END OF ARROW LIBRARY
Длина вместе с переменными (и с системой SCAN LIBRARY) равна 577 байт.
Описание процедур:
ARWINT - автоматическая организация управления движением стрелки на экране.
Изменение скорости происходит при продолжительном удерживании клавиш
направлений. Нажатие FIRE - выход.
INITMV - инициализация изменяемых параметров стрелки (нач. скорость, число
шагов, для увеличения скорости, число уровней увел. скорости).
MOVES - контроль нажатий клавиш, реагирование - изменение координат, скорости и
т. д.
PUTARW - вывод стрелки на экран в текущих координатах.
GETARW - восстановление прежнего вида экрана под стрелкой.
INC_Y - расчет адреса следующей линии экрана. Используется множеством различных
графических процедур - можете использовать, вызывать.
Описание данных и переменных:
ARW_X :db(0..255) - текущая X-координата стрелки
ARW_Y :db(0..255) - текущая Y-координата стрелки
ARWXMN:db(0..255) - минимально возможное значение, принимаемое коор. X
ARWXMX:db(0..255) - максимально возможное значение, принимаемое коор. X
ARWYMN:db(0..255) - Y-минимум
ARWYMX:db(0..255) - Y-максимум
STEP :db(0..255) - число шагов, после которых скорость возрастает
LEVEL :db(0..255) - число уровней возрастания скорости
SPEED :db(0..255) - начальная скорость
ARWSPR:ds(24) - массив с битовым образом стрелки. Первый байт - маска
(накладывание по AND), второй байт - образ (накладывание по OR); третий байт
- опять маска (уже второй линии изображения стрелки) и т.д.
Высота стрелки равна 12 пикселов.
Примечание. Запись db означает 1 байт данных, ds - последовательность из N
байт.
Дамп библиотеки ARROW LIBRARY
(с адреса 64192):
Listing 5.
Шестнадцатеричный дамп ARROW LIBRARY
( требует присутствия SCAN LIBRARY с адреса 64000 )
FAC0: FB CD D9 FA CD 48 FB CD :32
FAC8: 00 FA CD E5 FA 76 CD D4 :7F
FAD0: FB 3A BA FA E6 10 28 EC :BD
FAD8: C9 21 08 FC 11 0B FC 01 :D9
FAE0: 03 00 ED B0 C9 3A BA FA :31
FAE8: F5 CD 0E FB F1 4F 0F E6 :E2
FAF0: 05 A9 47 0F 21 04 FC 11 :20
FAF8: 02 FC C5 DC 2D FB C1 CB :45
FB00: 09 CB 09 78 0F 0F 0F 13 :90
FB08: 21 06 FC 38 20 C9 ED 4B :7F
FB10: 0E FC 32 0E FC B9 20 C1 :EB
FB18: E6 0F 28 BD 21 0B FC 35 :4A
FB20: C0 3A 08 FC 77 23 7E 3D :6E
FB28: C8 77 23 34 C9 79 0F 3A :44
FB30: 0D FC 4F 1A 38 08 91 38 :A6
FB38: 0C 12 BE 38 08 C9 81 23 :BC
FB40: 38 03 12 BE D8 7E 12 C9 :77
FB48: ED 5B 02 FC 7A B7 28 01 :E3
FB50: 15 7B B7 28 01 1D 7A 1F :71
FB58: 37 1F 1F E6 5F 67 AB E6 :05
FB60: 07 AB 0F 0F 0F 6F 7A AC :CF
FB68: E6 07 AC 67 22 0F FC FE :8E
FB70: 58 D0 E5 11 29 FC 01 FF :AE
FB78: 0C ED A0 ED A0 2B 2B CD :BC
FB80: F3 FB 10 F5 E1 3A 02 FC :87
FB88: D6 01 38 02 E6 07 D6 08 :5F
FB90: ED 44 47 11 11 FC 0E 0C :3B
FB98: 3A 03 FC B7 20 03 13 13 :CC
FBA0: 0D EB C5 E5 D5 5E 16 FF :85
FBA8: 23 6E 26 00 CB 33 CB 12 :35
FBB0: 29 10 F9 44 4D E1 7E A2 :6F
FBB8: B0 77 2C 7D E6 1F 28 04 :B4
FBC0: 7E A3 B1 77 2D CD F3 FB :EC
FBC8: D1 C1 13 13 7C FE 58 D0 :1D
FBD0: 0D 20 CE C9 2A 0F FC 11 :D5
FBD8: 29 FC 06 0C 7C FE 58 D0 :AC
FBE0: 1A 77 23 13 1A 77 13 2B :71
FBE8: CD F3 FB 10 EF 26 58 22 :3D
FBF0: 0F FC C9 24 7C E6 07 C0 :0C
FBF8: 7D C6 20 6F D8 7C D6 08 :F7
FC00: 67 C9 7F 5F 00 FF 00 BF :C8
FC08: 08 0A 01 00 00 00 00 00 :17
FC10: 58 3F 00 1F 40 0F 60 07 :78
FC18: 70 03 78 01 7C 03 70 03 :F2
FC20: 48 A3 08 F1 04 F1 04 F9 :F2
FC28: 00 00 00 00 00 00 00 00 :24
FC30: 00 00 00 00 00 00 00 00 :2C
FC38: 00 00 00 00 00 00 00 00 :34
FC40: 00 00 00 00 00 00 00 00 :3C
Сохранение: SAVE "scnarw.c"CODE 64000,577
Адреса процедур и данных:
ARWINT 64192 #FAC0 ARW_X 64514 #FC02
INITMV 64217 #FAD9 ARW_Y 64515 #FC03
MOVES 64229 #FAE5 ARWXMN 64516 #FC04
PUTARW 64328 #FB48 ARWXMX 64517 #FC05
GETARW 64468 #FBD4 ARWYMN 64518 #FC06
INC_Y 64499 #FBF3 ARWYMX 64519 #FC07
STEP 64520 #FC08
LEVEL 64521 #FC09
SPEED 64522 #FC0A
ARWSPR 64529 #FC11
Преимущества использования ARROW LIBRARY:
1. Относительно небольшой размер кода.
2. Возможность изменения размеров окна для перемещения стрелки.
3. Вывод стрелки в любых (!) координатах экрана, особенно важно: вывод при
X=0 (Y=0) - большинство аналогичных интерфейсов не реализуют данную
возможность!
4. Автоматическое изменение скорости движения при длительном удерживании
направлений.
5. Возможность регулировки п.4 (начальная скорость, число уровней изменения
скорости, число шагов для изменения скорости стрелки).
Пример BASIC-программы,
использующей ARROW LIBRARY:
Listing 6.
Демонстрационный пример BASIC-программы, использующей ARROW LIBRARY.
( тоже простейший граф-редактор )
10 BORDER 0:POKE 23693,15:CLEAR 29999
20 POKE 64519,175
30 RANDOMIZE USR 64000
40 IF PEEK 64186=0 THEN GO TO 60
50 PLOT PEEK 64514,175-PEEK 64515
60 RANDOMIZE USR 64192
70 GO TO 30
Описание работы.
Как всегда, вначале устанавливаются атрибуты экрана (BORDER 0: PAPER 1: INK7)
и стек (29999). Затем, в строке 20 производится запись числа 175 в ячейку
ARWYMX, что означает: "установить верхнюю границу для возможных перемещений
стрелки равной 175" (координата Y у команды бейсика PLOT не должна превышать
175!).
В строке 30 происходит вызов SCAN LIBRARY - опрос всех джойстиков и
клавиатуры. И все это сделано лишь для того, чтобы в строке 40 проверить, а
нажато ли что-нибудь (DIRECT <> 0)? Если ничего не нажато, то выполняется
переход к строке 60, иначе в строке 50 производится печать точки в координатах,
взятых из переменных ARW_X и ARW_Y, которые указывают на текущие координаты
стрелки в экране. Координата Y равна 175-ARW_Y, т. к. отсчет координат в
бейсике идет снизу-вверх, а в ARROW LIBRARY - наоборот, сверху-вниз.
Далее, идет 60-ая строка, в которой происходит вызов процедуры
автоматического перемещения стрелки (ARWINT). Выход из нее назад, в бейсик,
происходит только по нажатию FIRE. Затем следует переход к строке 70, далее - к строке
30, а там и печать самой точки...
Вот, Вы и ознакомились с данной статьей. Самое время садиться за любимый
SPECCY, запускать ассемблер, MEMORY EDITOR и работать, работать... Быть
может, данная публикация натолкнет кого-нибудь на интересную идею, направит
беспорядочные мысли в нужное русло. А, возможно, и поможет поближе познакомиться
со своим компьютером, узнать его секреты...
Если появятся различные вопросы, дополнения, предложения - пишите, помощь
всегда придет! Быть может кому-то будет интересно узнать о других подобных
процедурах, организации интерфейсов (типа "выпадающие меню", кнопки...) и т. д.