ZX Review #11-12 Из журнала ZX - РЕВЮ 11 - 12 27 ноября 1997 г.

Этюды


Цвет
Насыщенность
Размер




????????????????????????????????
?                              ?
?            ЭТЮДЫ             ?
?                              ?
????????????????????????????????

Music by ZET and MITCHELL
(c) Колотов Сергей, г.Шадринск,
SerzhSoft, июль, 1997

   РЕМЕЙК НЕКОТОРЫХ ПРОЦЕДУР
           1993 ГОДА
           
  В ZX-РЕВЮ-93 с.121 была  опу-
бликована процедура  Н.М.Стинова
под  названием  DOORS, реализую-
щая эффект очистки экрана  путем
раздвигания изображения в сторо-
ны. Процедура занимает 101  байт
и, честно  говоря,  написана  не
очень  эффективно.  Использованы
альтернативные регистры, но  ре-
гистр HL так нигде и  не  сохра-
няется, что не дает  возможности
вызывать эту  процедуру  из  BA-
SIC'а. Применив команды  блочной
пересылки  LDIR и  LDDR, удалось
"сдружить"  новую  процедуру   с
бейсиком (благо в ней  альтерна-
тивные регистры  вообще  не  ис-
пользуются), более чем вдвое со-
кратить обьем занимаемой  памяти
и значительно  повысить  быстро-
действие. Новая процедура  также
стала  полностью   перемещаемой.
При очистке изображения на экра-
не устанавливаются текущие  сис-
темные атрибуты из ячейки 23693.
То есть  существует  возможность
изменить цвета прямо из  бейсика
командами  INK,  PAPER,  BRIGHT,
FLASH или напрямую подав  коман-
ду POKE  23693,n  перед  вызовом
процедуры по RANDOMIZE USR  adr.
Выполнив POKE adr+1,n (n=1..16),
можно   установить    количество
сдвигов, выполняемых за один за-
пуск процедуры.

;  SCREEN-APART
;(c) SerzhSoft, 11 july 1997
;
        ORG     60000        ;адрес ассемблирования
;
ATTR_P  EQU     23693        ;текущие атрибуты основного экрана
;
APART
        LD      B,#10        ;число пар сдвигов экрана в стороны
LP_APR1 PUSH    BC           ;сохраняем счетчик сдвигов на стеке
        LD      A,(ATTR_P)   ;A=атрибуты для заполн. после сдига
        LD      DE,#5AFF     ;самый последний байт атрибутов
LP_APR2 LD      H,D          ;скопировали этот адрес
        LD      L,E          ; в HL и уменьшили на 1:
        DEC     HL           ; адрес предпоследнего байта линии
        LD      BC,#000F     ;сдвигать: 15 байт
        LDDR                 ;сдвиг правой полу-линии вправо
        LD      (DE),A       ;устанавливаем новое значение
        LD      BC,#FFF2     ;коррекция р-ра HL - теперь он
        ADD     HL,BC        ; указывает на второй байт линии
        LD      E,L          ;DE=HL
        DEC     E            ;первый байт слева в линии
        LD      BC,#000F     ;будем сдвигать 15 байт
        LDIR                 ;сдвиг левой полу-линии влево
        LD      (DE),A       ;устанавливаем новый байт (очистка)
        LD      BC,#FFEF     ;коррекция регистра DE -
        ADD     HL,BC        ; переход на одну
        EX      DE,HL        ; линию вверх в экране (атрибутах)
        BIT     3,D          ;если атрибуты не кончились,
        JR      NZ,LP_APR2   ; то крутим цикл
        XOR     A            ;перешли в область графики
        BIT     6,D          ;если мы пока еще не залезли в ПЗУ,
        JR      NZ,LP_APR2   ; то продолжаем крутить цикл
        POP     BC           ;восстанавливаем счетчик сдвигов
        DJNZ    LP_APR1      ;крутим цикл положенное число раз
        RET                  ;выход из процедуры

   Шестнадцатеричный дамп проце-
дуры APART:

    EA60: 06 10 C5 3A 8D 5C 11 FF :58
    EA68: 5A 62 6B 2B 01 0F 00 ED :A1
    EA70: B8 12 01 F2 FF 09 5D 1D :99
    EA78: 01 0F 00 ED B0 12 01 EF :11
    EA80: FF 09 EB CB 5A 20 E2 AF :33
    EA88: CB 72 20 DD C1 10 D3 C9 :19   

Сохранение: SAVE "aрart.c"  CODE
60000,48

   На  стр.35  ZX-РЕВЮ-93   была
опубликована программа  "теневой
контур". Мне удалось  существен-
но улучшить  эту  процедуру, для
этого просто переписав ее "с ну-
ля".  Новая  процедура  занимает
138 байт (старая - 211  байт)  и
стала релоцируемой. Как и  преж-
де, чтобы получить  псевдо-трех-
мерное  изображение,  достаточно
выделить атрибутами  с  повышен-
ной яркостью те места на экране,
которым необходима тень, и  выз-
вать  процедуру  SHADOW.  Сущес-
твует возможность изменять  кон-
фигурацию самой тени. Для  этого
необходимо поменять  значения  в
двух командах загрузки регистро-
вой  пары  DE, которые  помечены
звездочками.  В   представленной
программе  установлены  значения
#5500, #5500 соответственно, что
дает тень с  немного  закруглен-
ными  краями.  Подставив  #2A80,
#AA80, получим  тень  с  прямыми
краями. Чтобы  получить  тот  же
результат, что и в  оригинальном
варианте, необходимо  установить
#AA00,  #2A80.  Ну  а   значения
#7F80, #FF80 покажут Вам  сплош-
ную, нештрихованную  тень.

;SHADOW-PROCESSOR
; (c) SerzhSoft, 11 july 1997
;
        ORG     60000        ;адрес ассемблирования
SHADOW
        LD      HL,#5AFF     ;последний байт атрибутов
LP_SHD1 LD      BC,#0020     ;число байт в одной линии атрибутов
        DEC     HL           ;переход к предыдущему атрибуту
        BIT     3,H          ;мы еще не "заехали" в графику?
        RET     Z            ; если уже, то выход из процедуры
        BIT     6,(HL)       ;это атрибут с повышенной яркостью?
LP_SHD0 JR      Z,LP_SHD1    ; если нет, то предыдущий атрибут
        PUSH    HL           ;сохраняем адрес атрибута на стеке
        INC     L            ;переход к следующему атрибуту
        LD      A,L          ;проверка на переход
        AND     #1F          ; к следующей линии атрибутов
        JR      Z,GO_SHD2    ;если да, то перепрыгиваем
        BIT     6,(HL)       ;включена ли яркость?
        JR      NZ,GO_SHD2   ; если да, то перепрыгиваем
        PUSH    HL           ;сохраняем адрес атрибута на стеке
        ADD     HL,BC        ;переход к атрибуту на линию ниже
        LD      C,(HL)       ;поместили этот атрибут в регистр C
        POP     HL           ;восстанавливаем адрес атрибута
        LD      A,H          ;расчет адреса
        AND     #03          ; верхней линии
        RLCA                 ; знакоместа
        RLCA                 ; в экране
        RLCA                 ; по заданному
        OR      #40          ; адресу в
        LD      H,A          ; области атрибутов
        LD      DE,#5500     ;* образ для создания тени справа *
        LD      B,#0C        ;высота тени: 12 пикселов
LP_SHD2 LD      A,E          ;сдвигаемый байт образа линии тени
        AND     #F0          ;отбрасываем лишние "правые" биты
        OR      (HL)         ;накладываем содержимое экрана
        LD      (HL),A       ;и записываем новый байт в экран
        INC     H            ;----------------------------------
        LD      A,H          ;
        AND     #07          ;стандартная
        JR      NZ,GO_SHD0   ; последовательность
        LD      A,L          ; команд для перехода
        ADD     A,#20        ; на линию ниже
        LD      L,A          ; в экранной области
        JR      C,GO_SHD1    ;
        LD      A,H          ;
        SUB     #08          ;
        LD      H,A          ;----------------------------------
GO_SHD1 LD      A,H          ;старший байт адреса в графике
        CP      #58          ;провер. на выход за границы экрана
        JR      NC,GO_SHD2   ;если да, то перепрыгиваем
        BIT     6,C          ;проверка яркости: можно ли изобра-
        JR      NZ,GO_SHD2   ; жать тень. если нет, то прыжок
GO_SHD0 SRL     D            ;вращение образа
        RR      E            ; линии тени вправо
        DJNZ    LP_SHD2      ;цикл по числу линий в тени
GO_SHD2 POP     HL           ;восстанавливаем адрес атрибута
        PUSH    HL           ;сохраняем адрес атрибута на стеке
        LD      BC,#0020     ;число байт в одной линии атрибутов
        ADD     HL,BC        ;переход к атрибуту под текущим
        LD      A,H          ;проверка нового адреса атрибута
        CP      #5B          ; на вхождение в границы экрана
        JR      NC,GO_SHD4   ;если за пределами, то переход
        BIT     6,(HL)       ;если же входит в экран, но яркость
        JR      NZ,GO_SHD4   ; включена, то все равно - переход
        INC     L            ;следующий байт атрибутов (правый)
        LD      C,(HL)       ; помещаем в регистр C
        LD      A,H          ;расчет адреса
        AND     #03          ; верхней линии
        RLCA                 ; знакоместа
        RLCA                 ; в экране
        RLCA                 ; по заданному
        OR      #40          ; адресу в
        LD      H,A          ; области атрибутов
        LD      DE,#5500     ;* образ для создания тени снизу *
        LD      B,#04        ;высота тени снизу
LP_SHD3 DEC     L            ;возврат на байт назад
        LD      A,(HL)       ;взяли байт с экрана
        OR      D            ;наложили левый байт образа линии
        LD      (HL),A       ; тени и поместили назад в экран
        INC     L            ;переход на байт вправо
        LD      A,L          ;проверка на выход
        AND     #1F          ; за правую границу экрана:
        JR      Z,GO_SHD3    ; если да, то перепрыгиваем
        BIT     6,C          ;если же атрибут с яркостью,
        JR      NZ,GO_SHD3   ; то так же - перепрыгиваем
        LD      A,(HL)       ;взяли байт с экрана
        OR      E            ;наложили правый байт образа линии
        LD      (HL),A       ; тени и поместили назад в экран
GO_SHD3 INC     H            ;переход к следующей линии
        SRL     D            ;вращение образа одной
        RR      E            ; линии тени вправо
        DJNZ    LP_SHD3      ;цикл по числу линий в образе тени
GO_SHD4 POP     HL           ;восстанавливаем адрес атрибута
        XOR     A            ;уст. флаг Z, чтобы JR Z сработала
        JR      LP_SHD0      ;переходим на команду JR Z,LP_SHD1

   Шестнадцатеричный дамп проце-
дуры SHADOW:

    EA60: 21 FF 5A 01 20 00 2B CB :DB
    EA68: 5C C8 CB 76 28 F5 E5 2C :E5
    EA70: 7D E6 1F 28 3A CB 76 20 :9F
    EA78: 36 E5 09 4E E1 7C E6 03 :1A
    EA80: 07 07 07 F6 40 67 11 00 :2D
    EA88: 55 06 0C 7B E6 F0 B6 77 :57
    EA90: 24 7C E6 07 20 13 7D C6 :7D
    EA98: 20 6F 38 04 7C D6 08 67 :0E
    EAA0: 7C FE 58 30 0A CB 71 20 :F2
    EAA8: 06 CB 3A CB 1B 10 DC E1 :50
    EAB0: E5 01 20 00 09 7C FE 5B :7E
    EAB8: 30 2C CB 76 20 28 2C 4E :01
    EAC0: 7C E6 03 07 07 07 F6 40 :5A
    EAC8: 67 11 00 55 06 04 2D 7E :34
    EAD0: B2 77 2C 7D E6 1F 28 07 :C0
    EAD8: CB 71 20 03 7E B3 77 24 :ED
    EAE0: CB 3A CB 1B 10 E8 E1 AF :3D
    EAE8: 18 82 00 00 00 00 00 00 :6C

Сохранение: SAVE "shadow.c" CODE
60000,13

   В ZX-РЕВЮ-93 на стр.117  была
напечатана процедура LOOK  BASIC
PROGRAMMS, автор - Бессонов Але-
ксандр. Она занимает 344  байта,
что на мой  взгляд - просто  ко-
щунственно! Мне  удалось  сокра-
тить  эту  программу  более  чем
вдвое (точнее, переписать ее за-
ново). Принцип работы  почти  не
изменился, правда, появилась од-
на новая возможность. При  печа-
ти обозначения какого-либо упра-
вляющего кода вслед за ним выво-
дится  значение  его  параметра.
Например,  после  упр.  кода  16
(INK CTRL)  идет  байт  значения
цвета, что на экране будет  выг-
лядеть примерно так:  I[7]  (все
инверсно). А после кодов 22 и 23
(AT CTRL, TAB CTRL) должно  сле-
довать  два  значения:  A[2][10]
или T[18][0].  В остальном прог-
рамма  работает  так  же, как  и
оригинал. Процедура занимает 158
байт.

;
;   LOOK BASIC PROGRAMS II
; (c) SerzhSoft, 11 july 1997
;
        ORG     65000        ;адрес ассемблирования
;
PROG    EQU     #5C53 ;сист. пер. "адрес начала BASIC-программы"
VARS    EQU     #5C4B ;сис. пер. "адрес начала BASIC-переменных"
;
OUT_NUM_2 EQU   #1A28 ;сис. пр-ра "печать номера строки (HL)"
CHAN_OPEN EQU   #1601 ;сис. пр-ра "открытие канала вывода"
STACK_NUM EQU   #33B4 ;сис. пр-ра "поместить (HL) на стек к-ра"
STACK_A   EQU   #2D28 ;сис. пр-ра "поместить A на стек кальк-ра"
STACK_BC  EQU   #2D2B ;сис. пр-ра "поместить BC на стек к-ра"
PRINT_FP  EQU   #2DE3 ;сис. пр-ра "печать содержимого стека к."
;
LBP_2
        LD      A,#02        ;открываем канал вывода
        CALL    CHAN_OPEN    ; на основной экран
        LD      HL,(PROG)    ;адрес начала BASIC-программы
LP_LBP1 LD      DE,(VARS)    ;адрес начала BASIC-переменных
        AND     A            ;сброс флага переноса для SBC
        SBC     HL,DE        ;проверка на окончание программы
        RET     NC           ;если HL>=DE, то выход из процедуры
        ADD     HL,DE        ;восстанавливаем значение HL
        PUSH    HL           ;сохраняем адрес строки на стеке
        CALL    OUT_NUM_2    ;печать номера строки
        POP     HL           ;восстан-ем адрес строки со стека
        PUSH    HL           ; и - опять же - сохраняем
        LD      B,H          ;скопировали
        LD      C,L          ; HL в BC
        CALL    PRN_BC       ;распечатали адрес строки в памяти
        POP     HL           ;восстан-ем адрес строки со стека
        INC     HL           ;переход к
        INC     HL           ; длине строки
        LD      C,(HL)       ;младший байт длины строки
        INC     HL           ;дальше
        LD      B,(HL)       ;старший байт длины строки
        INC     HL           ;переход к содержимому строки
        PUSH    HL           ;сохраняем адрес на стеке
        CALL    PRN_BC       ;печать длины текущей BASIC-строки
        POP     HL           ;восстанавливаем адрес со стека
LP_LBP2 LD      A,(HL)       ;текущий символ строки
        INC     HL           ;переход к следующему
        CP      #0D          ;проверка на конец строки
        JR      Z,GO_LBP1    ;если да, то переход на обработку
        LD      DE,LP_LBP2   ;адрес цикла по символам кидаем на
        PUSH    DE           ; стек, чтобы "вернуться" по RET
        CP      #0E          ;наткнулись на число?
        JR      Z,GO_LBP2    ; если да, то переход на обработку
        CP      #08          ;код сдвига на одну позицию назад?
        JR      Z,GO_LBP3    ;если да, то обрабатываем
        CP      #10          ;если код символа меньше 16,
        JR      C,GO_LBP0    ; то печатаем его и -> к сл. симв.
        CP      #18          ;если 16 <= код символа <= 23,
        JR      C,GO_LBP4    ; то переход на обработку упр. кода
GO_LBP0 RST     #10          ;печать одного символа
        RET                  ;переход на LP_LBP2
;
GO_LBP1 RST     #10          ;печать кода #0D - перевод строки
        LD      A,#0D        ;и еще раз
        RST     #10          ; переводим строку
        JR      LP_LBP1      ;переход к следующей BASIC-строке
;
GO_LBP2 PUSH    HL           ;сохраняем адрес на стеке
        CALL    STACK_NUM    ;5-байтное число -> на стек кальк.
        CALL    PRN_NUM      ;печатаем его
        POP     HL           ;восстанавливаем адрес со стека
        LD      BC,#0005     ;переход к символу BASIC-строки за
        ADD     HL,BC        ; данным 5-байтным числом
        RET                  ;переход на LP_LBP2
;
GO_LBP3 ADD     A,#07        ;корректировка кода для #08
GO_LBP4 SUB     #0F          3;получили число #00...#07 и занесли
        LD      C,A          ; его в BC - это индекс (позиция)
        LD      B,#00        ; обозначения упр. кода в таблице
        PUSH    HL           ;сохраняем адрес на стеке
        LD      HL,TBLCODE   ;адрес таблицы обозначений упр. к.
        ADD     HL,BC        ;адресовали элемент в этой таблице
        SET     2,(IY+87)    ;включить инверсию при печати
        LD      A,(HL)       ;взяли обозн. упр. кода из таблицы
        RST     #10          ;напечатали его (обозначение)
        RES     2,(IY+87)    ;отключаем инверсию при печати
        POP     HL           ;восстанавливаем адрес со стека
        LD      A,C          ;определяем размер управл. кода:
        OR      A            ;если это был код #08,
        RET     Z            ;то выходим (у него нет параметров)
        CP      #06          ;если это были коды #16, #17, то
        CALL    NC,GO_LBP5   ; надо печатать два пераметра
GO_LBP5 LD      A,(HL)       ;взяли один байт из памяти (пар-р)
        PUSH    HL           ;сохраняем адрес на стеке
        CALL    STACK_A      ;забрасываем значение A на стек к.
        CALL    PRN_NUM      ;печатаем это значение (параметр)
        POP     HL           ;восстанавливаем адрес со стека
        INC     HL           ;переход к след. символу в строке
        RET                  ;"возврат" на GO_LBP5 или LP_LBP2
;
PRN_BC  CALL    STACK_BC     ;помещаем BC на стек калькулятора
;
PRN_NUM SET     2,(IY+87)    ;включить инверсию при печати
        LD      A,"["        ;распечатываем
        RST     #10          ; левую скобку (открывающую),
        CALL    PRINT_FP     ; затем - число со стека кальк-ра,
        LD      A,"]"        ; и, наконец печатаем
        RST     #10          ; правую скобку (закрывающую)
        RES     2,(IY+87)    ;отключаем инверсию при печати
        RET                  ;возврат из подпрограммы
;
TBLCODE DB      "IPFBVAT"   ;таблица обозначений управл. кодов

   Шестнадцатеричный дамп проце-
дуры LBP_2:

    FDE8: 3E 02 CD 01 16 2A 53 5C :E2
    FDF0: ED 5B 4B 5C A7 ED 52 D0 :92
    FDF8: 19 E5 CD 28 1A E1 E5 44 :0C
    FE00: 4D CD 69 FE E1 23 23 4E :F4
    FE08: 23 46 23 E5 CD 69 FE E1 :8C
    FE10: 7E 23 FE 0D 28 16 11 10 :19
    FE18: FE D5 FE 0E 28 14 FE 08 :37
    FE20: 28 1D FE 10 38 04 FE 18 :C3
    FE28: 38 17 D7 C9 D7 3E 0D D7 :0E
    FE30: 18 BE E5 CD B4 33 CD 6C :D6
    FE38: FE E1 01 05 00 09 C9 C6 :B3
    FE40: 07 D6 0F 4F 06 00 E5 21 :85
    FE48: 7E FE 09 FD CB 57 D6 7E :3E
    FE50: D7 FD CB 57 96 E1 79 B7 :EB
    FE58: C8 FE 06 D4 5E FE 7E E5 :B5
    FE60: CD 28 2D CD 6C FE E1 23 :BB
    FE68: C9 CD 2B 2D FD CB 57 D6 :49
    FE70: 3E 5B D7 CD E3 2D 3E 5D :56
    FE78: D7 FD CB 57 96 C9 3C 49 :50
    FE80: 50 46 42 56 41 54 00 00 :41

Сохранение: SAVE "lbp_02.c" CODE
65000,15

           *   *   *

(c) Иван Рощин, Москва, 1997

 Несколько графических эффектов

          1. "Пламя"

   Существует множество  вариан-
тов этого  эффекта  (см., напри-
мер, музыкальные demo  F.S.R.D.,
INSANE, S4B, NOUMENON, EYE  ACHE
2, RAGE и т.п.). Я  же  реализо-
вал до сих пор нигде  не  встре-
чавшийся:  на  экране  находится
некоторая картинка (скажем, зас-
тавка  программы), и  на  заднем
фоне (а  это  участки  картинки,
окрашенные  черным  цветом)  как
раз и виднеется "пламя". Как это
выглядит, вы можете увидеть, за-
пустив "ZX FORUM 4" (эффект  ис-
пользуется в главном меню  этого
электронного издания).
   Итак, текст программы (при ее
разработке я  использовал  фраг-
менты процедуры  "Пламя", взятой
из статьи С.Телицына в  ZX  РЕВЮ
5-6 1997):

BUF1    EQU  #8000 ;Здесь запоминаются
                   ;атрибуты картинки.
BUF2    EQU  #8300 ;Массив, соотв.
                   ;выводимому изобра-
                   ;жению (длина #340).
BUF3    EQU  #8700 ;Палитра (младший
                   ;байт адреса = 0!)

        LD      HL,BUF3
        EXX

        XOR     A
        LD      (23560),A

        LD      HL,BUF3     ;формируем
        LD      DE,BUF3+1   ;палитру
        LD      (HL),0
        LD      BC,8
        LDIR
        LD      A,%1000
        LD      (BUF3+4),A
        LD      (HL),A
        LD      BC,8
        LDIR
        LD      (HL),%10000
        LD      BC,8
        LDIR
        LD      (HL),%11000
        LD      BC,#E7
        LDIR

        LD      HL,#9000    ;перебросили
        LD      DE,#4000    ;картинку
        LD      BC,#1B00    ;на экран
        LDIR

        LD      HL,#5800    ;сохранили
        LD      DE,BUF1     ;ее атрибуты
        LD      BC,#300
        LDIR

        LD      HL,BUF2     ;обнулили
        LD      (HL),0      ;рабочий
        LD      DE,BUF2+1   ;буфер
        LD      BC,#2FF
        LDIR

SSAVER  LD      HL,BUF2+#320 ;заполняем
        LD      (HL),-60     ;часть
        LD      DE,BUF2+#321 ;буфера
        LD      BC,#1F       ;числом -60
                    ;(от его значения
        LDIR        ;зависит вид эффекта)

        LD      HL,0 ;исходное положение
                     ;в ПЗУ

;Цикл обновления содержимого буфера BUF2:

LOOP_1  LD      DE,BUF2+#300
        LD      B,#20

LOOP_2  INC     HL
        LD      A,H
        AND     #1F
        LD      H,A
        LD      A,(HL)   ;сл.байт из ПЗУ
        AND     %00111111
        ADD     A,%11000
        JR      NC,LL
        LD      A,%11000
LL      LD      (DE),A
        INC     DE
        DJNZ    LOOP_2

        PUSH    HL

        LD      IX,BUF2
        LD      DE,#5800
        LD      HL,BUF1
        LD      BC,#2FF
LOOP_4  LD      A,(IX+32)
        ADD     A,(IX+33)
        ADD     A,(IX+34)
        ADD     A,(IX+65)
        SRL     A
        SRL     A
        JR      Z,LOOP_5
        DEC     A
LOOP_5  INC     IX
        INC     HL
        INC     DE
        LD      (IX),A

;Изменили значение байта в буфере,
;теперь изменяем соотв. атрибут на экране:

        PUSH    BC

        EXX
        LD      L,A    ;адрес в палитре
        LD      A,(HL) ;A = нужный цвет
        EXX
        LD      B,A

;Для текущего атрибута устанавливаем
;вместо черного цвета INK или PAPER
;тот цвет, который взяли из палитры;
;т.о. черный цвет является как бы
;"прозрачным":

        LD      A,(HL)    ;атр. картинки
        AND     %00111000 ;PAPER
        JP      NZ,TST_2  ;JP-для скорости
        LD      A,B       ;теперь вместо
        OR      (HL)      ;черного -
        LD      (DE),A    ;другой цвет

TST_2   LD      A,(HL)
        AND     %00000111 ;INK
        JR      NZ,TST_Q
        LD      A,B
        AND     %00111000
        RRCA
        RRCA
        RRCA
        OR      (HL)
        LD      (DE),A

TST_Q   POP     BC
        DEC     BC
        LD      A,B
        OR      C
        JP      NZ,LOOP_4
        POP     HL

;Проверка клавиатуры:

        LD      A,(23560)
        AND     A
        JP      Z,LOOP_1
        RET

   Что хотелось бы отметить: во-
первых, нетрудно, изменив  прог-
рамму, объявить "прозрачным"  не
черный, а любой другой цвет (или
даже  сразу  несколько  цветов).
Во-вторых, можно очень точно ре-
гулировать цвет "пламени" с  по-
мощью  палитры  (массив   BUF3).
Каждый байт палитры содержит од-
но из восьми  значений,  опреде-
ляющее цвет: %000000,%001000,...
%111000. В процессе работы прог-
раммы значения  из  буфера  BUF2
(числа от  0  до  255), которые,
можно сказать,  определяют  "яр-
кость" пламени, перекодируются в
соответствующие значения атрибу-
тов в соответствии с этой палит-
рой.
   Хотя этот эффект (как и  все,
рассмотренные в статье) не  син-
хронизируется  по   прерываниям,
они должны быть  разрешены - это
нужно для опроса клавиатуры.

          2. "Плазма"

   Это целый ряд красивых эффек-
тов. Вот  первый  из  них, самый
простой:


;--------------------------;
; (c) Иван Рощин, 1997     ;
; ЭФФЕКТ "PLASMA"          ;
;--------------------------;

        LD      HL,#4000  ;CLS
        LD      (HL),L
        LD      DE,#4001
        LD      BC,#1AFF
        LDIR

;Переносим содержимое верхней строки
;вниз и наоборот, т.е. экран как бы
;замкнут по вертикали:

M2      LD      HL,#5820
        LD      DE,#5AE0
        LD      BC,32
        LDIR
        LD      HL,#5AC0
        LD      DE,#5800
        LD      BC,32
        LDIR

;В цикле обновляем атрибуты #5820-#5AE0:

        LD      IX,#5820
M1      LD      A,(IX+1)
        ADD     A,(IX-1)
        ADD     A,(IX+32)
        ADD     A,(IX-32)

        DEC     A      ;!
        SRL     A      ;!
        SRL     A      ;!

        LD      (IX),A
        INC     IX
        LD A,XL        ;DEFB #DD,#7D
        CP      #E0
        JR      NZ,M1
        LD      A,XH   ;DEFB #DD,#7C
        CP      #5A
        JR      NZ,M1

        XOR     A
        IN      A,(254)
        CPL
        AND     31
        JR      Z,M2
        RET

Примечание: 
Если вы пользуетесь Gens4D,то он
может не понять половинки регис-
тровых пар XL или XH.  Для этого
нужно заменить мнемонику
для LD A,XL = DEFB #DD,#7D
для LD A,XH = DEFB #DD,#7C

   Вы можете  экспериментировать
с этой  программой,  изменяя  ее
участок, помеченный "!".  Попро-
буйте такие варианты:

а) DEC A    б) SRL A    в) SRL A
   SRL A       DEC A       SRL A
   SRL A       SRL A       DEC A

г) SRL A    д) ADD A,8
   SRL A       SRL A
   INC A       SRL A
               DEC A

   После этого участка можно до-
бавить команду AND #3F  или  AND
#7F - это тоже влияет на вид эф-
фекта.

   Можно изменить и другой фраг-
мент программы:

        LD      A,(IX+1)
        ADD     A,(IX-1)
        ADD     A,(IX+32)
        ADD     A,(IX-32)

Он определяет, что значение  те-
кущего атрибута зависит от  зна-
чений  атрибутов  слева, справа,
снизу и сверху от него:

   ?????
   ? ? ?  ? - текущий атрибут
   ?????
   ? ? ?  ? - влияющие на него атрибуты
   ?????

   Интересные  результаты  полу-
чаются при определении таких за-
висимостей:

????? LD  A,(IX+1)     ????? LD  A,(IX)
? ? ? ADD A,(IX)       ?   ? ADD A,(IX)
? ??? ADD A,(IX+32)    ? ? ? ADD A,(IX+31)
? ? ? ADD A,(IX-32)    ??? ? ADD A,(IX+32)
?????                  ?????

????? LD  A,(IX-33)    ????? LD  A,(IX)
?? ?? ADD A,(IX-31)    ?   ? ADD A,(IX)
? ? ? ADD A,(IX+31)    ? ? ? ADD A,(IX+32)
?? ?? ADD A,(IX+33)    ? ??? ADD A,(IX+33)
?????                  ?????

  А теперь - следующий  эффект.
По логике  работы  он  похож  на
предыдущий, но  выглядит  совер-
шенно по-другому. Здесь происхо-
дит  не  изменение  атрибутов, а
изменение  штриховки  знакомест.
Именно он используется в  качес-
тве Screen Saver'а в этом номере 
"РЕВЮ" (нажмите CS+S для его вы-
зова, а клавишей C  переключайте
цвета).

;--------------------------;
; (c) Иван Рощин, 1997     ;
; ЭФФЕКТ "PLASMA-2"        ;
;--------------------------;

BUF     EQU     #8000  ;Адрес буфера

;Установка атрибутов:

        LD      HL,#5800
        LD      (HL),%00101111
        LD      DE,#5801
        LD      BC,#2FF
        LDIR

;Очищаем буферную область
;(#640 байт с адреса BUF):

        LD      HL,BUF
        LD      (HL),0
        LD      DE,BUF+1
        LD      BC,#63F
        LDIR

;Переносим содержимое верхней строки
;буферной области вниз и наоборот,
;т.е. экран как бы замкнут по вертикали:

M2      LD      HL,BUF+32
        LD      DE,BUF+#300+32
        LD      BC,32
        LDIR
        LD      HL,BUF+#300
        LD      DE,BUF
        LD      BC,32
        LDIR

;В цикле обновляем атрибуты в буфере:

        LD      IX,BUF+32
M1      LD      A,(IX+1)
        ADD     A,(IX-1)
        ADD     A,(IX+32)
        ADD     A,(IX-32)

        SRL     A
        DEC     A
        SRL     A

        LD      (IX),A
        CALL    TO_SCR
        INC     IX
        LD      A,XL
        CP   BUF+#320-((BUF+#320)/256*256)
        JP      NZ,M1
        LD      A,XH
        CP      (BUF+#320)/256
        JP      NZ,M1

;Обновили весь буфер:

        XOR     A
        IN      A,(254)
        CPL
        AND     31
        JR      Z,M2
        RET

;--------------------------------------
;Процедура TO_SCR штрихует текущее
;знакоместо.
;
;Вход: A  - значение из буфера,
;           определяющее тип штриховки
;      IX - текущий адрес в буфере

TO_SCR  AND     %00111000
        RRCA
        LD      L,A
        LD      H,0
        LD      DE,TAB-4
        ADD     HL,DE
        LD      B,H
        LD      C,L

;BC - адрес штриховки

        PUSH    IX
        POP     HL
        LD      DE,#320
        ADD     HL,DE

;HL - адрес во вспомогательном буфере,
;     где хранится прошлое значение
;     штриховки для этого знакоместа

        CP      (HL)  ;значения совпали?
        RET     Z     ;если да - выходим

        PUSH    AF
        LD      (HL),A
        LD      DE,-BUF-#340
        ADD     HL,DE

;HL - смещение в основном буфере, узнаем
;     по нему координаты знакоместа:

        LD      A,L
        AND     31
        LD      D,A    ;X
        RL      L
        RL      H
        RL      L
        RL      H
        RL      L
        RL      H
        LD      A,H
        AND     31
        LD      E,A    ;Y

        AND     #18
        OR      #40
        LD      H,A
        LD      A,E
        AND     #7
        RRA
        RRA
        RRA
        RRA
        ADD     A,D
        LD      L,A

;HL - адрес на экране

        POP     AF       ;если A=0,
        AND     A        ;просто очищаем
        JP      Z,TO_CL  ;знакоместо

        LD      D,H
        LD      E,L
        INC     D
        INC     D
        INC     D
        INC     D

;HL указывает на верхнюю линию знакоместа,
;а DE - на четыре линии ниже

        LD      A,(BC)
        LD      (HL),A
        LD      (DE),A
        INC     H
        INC     D
        INC     BC
        LD      A,(BC)
        LD      (HL),A
        LD      (DE),A
        INC     H
        INC     D
        INC     BC
        LD      A,(BC)
        LD      (HL),A
        LD      (DE),A
        INC     H
        INC     D
        INC     BC
        LD      A,(BC)
        LD      (HL),A
        LD      (DE),A
        RET

;Если нужно просто очистить знакоместо:

TO_CL   LD      (HL),A
        INC     H
        LD      (HL),A
        INC     H
        LD      (HL),A
        INC     H
        LD      (HL),A
        INC     H
        LD      (HL),A
        INC     H
        LD      (HL),A
        INC     H
        LD      (HL),A
        INC     H
        LD      (HL),A
        RET

;Таблица, определяющая различные типы
;штриховок:

TAB     DB  %01000100
        DB  %00000000
        DB  %00010001
        DB  %00000000

        DB  %01000100
        DB  %00010001
        DB  %01000100
        DB  %00010001

        DB  %01010101
        DB  %10101010
        DB  %01010101
        DB  %10101010

        DB  %11011101
        DB  %10101010
        DB  %01110111
        DB  %10101010

        DB  %01010101
        DB  %10101010
        DB  %01010101
        DB  %10101010

        DB  %01000100
        DB  %00010001
        DB  %01000100
        DB  %00010001

        DB  %01000100
        DB  %00000000
        DB  %00010001
        DB  %00000000

   Ну и, наконец, последний  эф-
фект (использовался  как  Screen
Saver в ZX-РЕВЮ 7-10 и  ZX-FORUM
4):

;--------------------------;
; (c) Иван Рощин, 1997     ;
; ЭФФЕКТ "PLASMA-3"        ;
;--------------------------;

BUF     EQU #4000    ;адрес расположения
                     ;буфера длиной #410
                     ;байтов (мл.байт
                     ;адреса = 0!)
B_SCR   EQU BUF+#20  ;адрес первой
                     ;обновляемой строки
                     ;в буфере
B_UP    EQU BUF      ;верхняя строка
B_DOWN  EQU BUF+#320 ;нижняя строка

SSAVER  LD      HL,(RND+1)
NEW_RND LD      A,(HL)
        INC     HL
        AND     7
        CP      6
        JR      NC,NEW_RND

        AND     A
        RLCA
        RLCA
        LD      HL,TABL1
        LD      D,0
        LD      E,A
        ADD     HL,DE
        LD      DE,BUF+#400
        LD      BC,4
        LDIR

        LD      HL,TABL2
        LD      BC,12
        LDIR

;В H' заносим старший байт адреса
;палитры:

        LD      H,D
        EXX

;Переносим содержимое верхней строки
;буферной области вниз и наоборот,
;т.е. экран как бы замкнут по вертикали:

M2      LD      HL,B_SCR
        LD      DE,B_DOWN
        LD      BC,32
        LDIR

        LD      HL,B_SCR+#2E0
        LD      DE,B_UP
        LD      BC,32
        LDIR

;В цикле обновляем атрибуты в буфере:

        LD      IX,B_SCR
M1      LD      A,(IX-32)
        ADD     A,(IX-1)
        ADD     A,(IX+1)
        ADD     A,(IX+32)
        SRL     A
        SRL     A
        LD      (IX),A

;--------------------------------------
;По значению байта из буфера определяем
;его цвет и выводим на экран:

        EXX
        RRCA
        RRCA
        AND     15
        LD      L,A
        LD      A,(HL)
        EXX

        PUSH    IX
        POP     HL
        LD      DE,#5800-B_SCR
        ADD     HL,DE
        EX      AF,AF'      ;!
        LD      A,(IX-1)    ;!
        CP      (IX)        ;!
        JR      C,TO_SCR1   ;!
        CP      16          ;!
        JR      C,TO_SCR1   ;!
        DEC     HL          ;!
        LD      A,(HL)      ;!
        INC     HL          ;!
        LD      (HL),A      ;!
        JR      NO_CP       ;!

TO_SCR1 EX      AF,AF'      ;!
        LD      (HL),A
;--------------------------------------

NO_CP   INC     IX
        LD      DE,1-#5800+B_SCR-B_DOWN
        ADD     HL,DE
        LD      A,H
        OR      L
        JR      NZ,M1

;Обновили весь буфер, теперь некоторые
;байты буфера принимают случайные
;значения:

        LD      B,5
L23     PUSH    BC
        CALL    RND
        POP     BC
        DJNZ    L23

;На клавиатуре что-то нажато?

        LD      A,(23560)
        AND     A
        JR      Z,M2
        RET

;=================================
;Процедура RND изменяет случайным
;образом случ. выбранное число в
;буфере экрана (BUF..BUF+#400)

RND     LD      HL,0
        LD      A,H
        AND     #3F
        LD      H,A
        LD      D,(HL)
        INC     HL
        LD      E,(HL)
        INC     HL
        LD      A,(HL)
        XOR     D
        XOR     E
        SUB     16
        LD      B,A
        LD      A,D
        AND     3
        LD      D,A
        LD      E,B
        LD      (RND+1),HL
        LD      HL,BUF
        ADD     HL,DE
        LD      A,B
        AND     %01111111
        LD      (HL),A
        DEC     HL
        LD      (HL),A
        DEC     HL
        LD      (HL),A
        RET

;Палитра:

TABL1   DB      %00001001
        DB      %01001001
        DB      %00011011
        DB      %01011011

        DB      %00001001
        DB      %01001001
        DB      %00101101
        DB      %01101101

        DB      %00010010
        DB      %01010010
        DB      %00011011
        DB      %01011011

        DB      %00010010
        DB      %01010010
        DB      %00110110
        DB      %01110110

        DB      %00100100
        DB      %01100100
        DB      %00101101
        DB      %01101101

        DB      %00100100
        DB      %01100100
        DB      %00110110
        DB      %01110110

TABL2   DB      %00111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111
        DB      %01111111

   Строки, помеченные  "!", пер-
воначально отсутствовали в  этой
программе (в таком виде она  ис-
пользовалась в ZX-РЕВЮ 7-10), но
потом они были добавлены с целью
улучшить внешний вид  эффекта  и
сделать более плавными  переходы
цветов.

           *   *   *

  А теперь - несколько полезных
советов  любителям   музыкальных
DEMO:
   EYE ACHE 2 -  если  во  время
подгрузки с диска  музыка  начи-
нает тормозить, попробуйте пере-
писать программу на диск  с  че-
редованием секторов = 1  (обяза-
тельно в начало диска!)
   RAGE - если при запуске удер-
живать  нажатой  клавишу   SPACE
(или запустить программу в режи-
ме 48K), можно прочитать интере-
сный текст, в котором содержится
несколько советов начинающим де-
момейкерам.

           *   *   *

(c) Иван Рощин, Москва, 1997

   Быстрая переброска экрана

   Известно, что  пересылка  со-
держимого экранной памяти (#1800
байтов) занимает больше времени,
чем промежуток между двумя  пре-
рываниями.  Тем  не  менее, если
изображение должно изменяться не
каждые 1/50 секунды, а  хотя  бы
каждые 1/25 секунды, есть способ
так выполнить пересылку, что по-
ка луч  рисует текущий кадр - мы
видим первую картинку; когда луч
рисует следующий кадр - мы видим
уже вторую картинку.
   Рассмотрим, в чем заключается
этот способ. Пусть в видеопамяти
(#4000-#5800)  находится  первая
картинка. После прихода импульса
прерывания луч начинает рисовать
изображение, начиная с  верхнего
края экрана, рисует бордюр  и  в
конце концов прорисовывает самую
верхнюю строку малого экрана.

  ??????????????????????\
  ?       бордюр       ? ?
  ?  ????????????????  ? ?
  ?  ?              ?  ? ?
  ?  ?              ?  ? ?телевизионный
  ?  ? малый  экран ?  ? ?экран
  ?  ?              ?  ? ?
  ?  ?              ?  ? ?
  ?  ????????????????  ? ?
  ?                    ? ?
  ??????????????????????/

   Вот тут-то мы и начинаем  пе-
ребрасывать вторую  картинку  из
буфера  в  видеопамять, строго в
том же порядке, как она  изобра-
жается на экране (т.е. по  лини-
ям, сверху вниз). Поскольку  луч
формирует  изображение  быстрее,
чем мы обновляем содержимое  ви-
деопамяти, на экране будет изоб-
ражена первая картинка.
   Переброска картинки продолжа-
ется до того, как луч  прорисует
весь  экран.  Посчитаем, сколько
это займет времени: весь экран -
320 строк, верхняя часть  бордю-
ра - 80 строк, и еще одну строку
мы специально  пропустили.  Т.к.
время прорисовки одной  строки -
224 такта, получаем (320-81)*224
=53536 тактов. За это время, ко-
нечно, полностью обновить видео-
память не получится, но это пока
и не нужно.
   После того, как луч  прорису-
ет весь экран, поступит  следую-
щий импульс прерывания, и начнет
прорисовываться следующий  кадр.
Пока луч  рисует  верхнюю  часть
бордюра и верхнюю  часть  малого
экрана  (т.е. уже вторую картин-
ку), мы, не теряя времени, пере-
брасываем в видеопамять оставшу-
юся часть второй картинки. Пере-
броска должна быть завершена  до
того момента, как луч начнет ри-
совать нижнюю строку малого  эк-
рана, т.е. процедура  переброски
должна укладываться в 53536+(80+
192-1)*224=114240 тактов. А это-
го с избытком  хватит  даже  для
переброски картинки вместе с ат-
рибутами (в этом случае процеду-
ра должна пересылать по  очереди
8 линий картинки и атрибуты  для
этих 8 линий).
   Это расчеты  для  "Пентагона-
128". Фирменный Спектрум отлича-
ется от него по следующим  пара-
метрам:  верхняя  часть  бордюра
составляет 64 строки вместо 80 и
на экране 312 строк вместо  320.
Чтобы процедура переброски рабо-
тала и на нем, она должна  зани-
мать не более  (312-81)*224+(64+
191)*224=108864 тактов. Но этого
все  равно  хватает, даже учиты-
вая, что при  записи/чтении  ви-
деопамяти ULA  тормозит  процес-
сор.  А вот если  процессор, на-
оборот, будет  работать  быстрее
(турбированный Спектрум), проце-
дура переброски может и не  дать
нужного  результата.  Чтобы  она
правильно работала и в этом слу-
чае, нужно, чтобы она "укладыва-
лась в прерывание"  и  завершала
свою работу до того, как  начнет
прорисовываться нижняя линия ма-
лого экрана).

   А теперь - две программы, вы-
полняющие одну и ту  же  задачу:
каждые  1/25  секунды они меняют
цвет экрана с белого на черный и
наоборот. Первая  программа  ис-
пользует LDIR, а вторая работает
по рассмотренному выше  алгорит-
му. Почувствуйте разницу!

    Программа 1:

        LD      HL,#8000 ;1-я картинка
        LD      (HL),0
        LD      DE,#8001
        LD      BC,#17FF
        LDIR

        LD      HL,#9800 ;2-я картинка
        LD      (HL),#FF
        LD      DE,#9801
        LD      BC,#17FF
        LDIR

L1      HALT
        LD      HL,#8000
        LD      DE,#4000
        LD      BC,#1800
        LDIR
        HALT
        LD      HL,#9800
        LD      DE,#4000
        LD      BC,#1800
        LDIR

        XOR     A
        IN      A,(254)
        CPL
        AND     31
        JR      Z,L1
        RET


   Программа 2:

PIC1    EQU     #8000    ;1-я картинка
PIC2    EQU     #9800    ;2-я картинка

        LD      HL,PIC1
        LD      (HL),0
        LD      DE,PIC1+1
        LD      BC,#17FF
        LDIR

        LD      HL,PIC2
        LD      (HL),#FF
        LD      DE,PIC2+1
        LD      BC,#17FF
        LDIR

;Строим таблички с адресами переброски:

        LD      BC,PIC1-#4000
        LD      HL,TABL1
        LD      DE,#4000
        CALL    MAKE_T
        LD      BC,PIC2-#4000
        LD      HL,TABL2
        LD      DE,#4000
        CALL    MAKE_T

;Главный цикл программы:

LOOP1   LD      HL,TABL1
        LD      DE,BUFER+100
        LD      BC,192*4
        LDIR
        CALL    MOVER

        LD      HL,TABL2
        LD      DE,BUFER+100
        LD      BC,192*4
        LDIR
        CALL    MOVER

        XOR     A
        IN      A,(254)
        CPL
        AND     31
        JR      Z,LOOP1

;---------------------------------------
;Процедура MOVER непосредственно
;выполняет пересылку:

MOVER   HALT

;Чтобы не было задержек на обработку
;прерывания, можно поставить здесь DI
;(тогда при выходе из этой процедуры
;нужно поставить EI)

        LD      (QUIT+1),SP
        LD      SP,BUFER+100

;Задержка, пока не прорисуется 1-я строка

        LD      HL,0
        LD      DE,0
        LD      BC,#330
        LDIR

        LD      BC,#1800

;Берем со стека новые значения HL и DE,
;указывающие на следующую строку:

MMMM    POP     DE
        POP     HL

TO_LDI  LDI:LDI:LDI:LDI:LDI:LDI:LDI:LDI
        LDI:LDI:LDI:LDI:LDI:LDI:LDI:LDI
        LDI:LDI:LDI:LDI:LDI:LDI:LDI:LDI
        LDI:LDI:LDI:LDI:LDI:LDI:LDI:LDI

        JP      PE,MMMM ;продолжаем...

QUIT    LD      SP,0
        RET

;---------------------------------------
;Процедура MAKE_T строит табличку:

MAKE_T  LD      A,D
        CP      #58
        RET     Z
        LD      (HL),E
        INC     HL
        LD      (HL),D
        INC     HL
        EX      DE,HL
        ADD     HL,BC
        EX      DE,HL
        LD      (HL),E
        INC     HL
        LD      (HL),D
        INC     HL
        EX      DE,HL
        AND     A
        SBC     HL,BC
        EX      DE,HL
        INC     D
        LD      A,D
        AND     7
        JR      NZ,MAKE_T
        LD      A,E
        SUB     #E0
        LD      E,A
        JR      NC,MAKE_T
        LD      A,D
        SUB     8
        LD      D,A
        JR      MAKE_T

;---------------------------------------

TABL1   DS      192*4
TABL2   DS      192*4

;Стек для процедуры
;обработки прерываний (если во время
;работы проц. MOVER прерывания
;запрещены, он не нужен):

BUFER   DS      100

;"Одноразовая" табличка адресов строк
;(разрушается процедурой обработки
;прерываний и нуждается в обновлении):

        DS      192*4

           *   *   *

        
          (с) Vladik Ermolin



Другие статьи номера:



ZXBOOKS — Журналы, газеты и книги для ZX Spectrum
© 2008-2013 Ermolin Vladislav

Hosted by uCoz