ZX-Ревю 1996 №9 1995 г.

Читатель - читателю - организация управления и интерфейс "Стрелка".



               ОРГАНИЗАЦИЯ УПРАВЛЕНИЯ И ИНТЕРФЕЙС "СТРЕЛКА".

(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 и работать, работать... Быть 
может, данная публикация натолкнет  кого-нибудь  на  интересную идею, направит
беспорядочные мысли в нужное русло. А, возможно, и поможет поближе познакомиться 
со своим компьютером, узнать его секреты...
   Если появятся различные вопросы, дополнения, предложения - пишите,  помощь
всегда придет! Быть может кому-то будет интересно узнать о других подобных 
процедурах, организации интерфейсов  (типа "выпадающие меню", кнопки...) и т. д.



СОДЕРЖАНИЕ:


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

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



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

Похожие статьи:
Post... - Ощущения, что мы делаем газету не для себя.
Реклама - Реклама и объявления.
Новости - в Челябинске будет издаваться новый журнал "Miracle", Новости from Excess/Kazan, группа из Полтавы разработала плеер MIDI для спектрума.
Новелла - новелла по игре TECHNODROМ.
Министроки - стих "Старик".

В этот день...   29 марта