Born Dead #0A
02 июня 1999

Coding - Как кодить оптимально: генерация синус-таблицы, вывод атрибутного черно/белого спрайта.

<b>Coding</b> - Как кодить оптимально: генерация синус-таблицы, вывод атрибутного черно/белого спрайта.
════════════════════════════════════════════════════════════════
ЇЄ╒▐╟░░╟▐╒ЄєЇCODING ЇєЄ╒▐╟░░╟▐╒ЄєЇ      
════════════════════════════════════════════════════════════════

(c) ALK/XTM

                       КАК КОДИТЬ ОПТИМАЛЬНО.

                               - Любую    программу        можно
                                 соптимизировать  до  двух  байт
                                 без потери её функциональности.
                                 (Из  устных высказываний М.М.А)

                               - Ох, Василь Иваныч, не  нравится
                                 мне этот Сенкевич...
                               - Не нравится - не ешь!
                                 (Из    народного     фольклора)

  Нет у меня желания говорить. И не было. Да и о чём собственно?
- Как научиться программировать...  Просто ламерство какое-то...
                    Depression rules my mind...

  Не  хочу  выглядеть  этаким  ментором.  Просто не хочу. Но что
делать,  надо  Born Dead юбилейный выпустить, Chaos Construction
на  носу  опять-же, вот и распирает меня (вроде бы) поделиться с
миром о чудесах кодинга (да уж...кто-бы говорил!). В общем, так.

  Так  уж получилось, на этот раз мы с М.М.А представили на CC99
только  лишь 512-байтную интру ABSENT. Казалось-бы, ну что можно
написать в этих несчастных байтах? Оказалось, многое. В процессе
написания    интра   подвергалась   16-ти   (если   не   больше)
оптимизациям, и некоторые их примеры заслуживают (на мой взгляд)
внимания.
----------------------------------------------------------------
  Взять,  например,  синус.  Его  можно  использовать  во многих
эффектах,  таких,  как  плазма  (для  интры  сойдёт),  flying по
траекториям   Лиссажу  (no  comments),  rotation  mapping  (типа
повороты  текстур или спрайтов), ну и конечно же 3D - rendering.
Обычно  синус-таблицу  рассчитывают  заранее  (на  Бейсике), и в
демах/интрах  эта таблица может "кушать" от 256 (8-ми битная) до
512 (16-ти битная) байтов. И что мы будем иметь в 512-ти байтах?
Половину  займёт  синус,  а  в  другой  половине - ну максимум 2
call-ных эффекта.
                    Нет, это не наш метод!

Следующая процедура генерирует синус-таблицу.
Размер:75 байт = 44 байт кода + 31 байт таблиц.

  Размер  получаемой  таблицы  - 256 байт. Собственно, в таблице
хранятся  значения  синуса от 0 до 2-х PI с шагом 1/128 PI. Если
интерпретировать  их  как  дробные  части  чисел, то они лежат в
пределах   от  0  до  255/256,  т.е.  это  классический  синус с
точностью  1/256.  Первые  128 байт таблицы при расчётах следует
брать  со  знаком  плюс,  последние  128 - со знаком минус. Если
мысленно  представить  эту  синусоиду,  то  оба  её  полупериода
окажутся положительными, и при расчётах нужно следить за знаком.
Сразу   оговорюсь:  синус  не  рассчитывается,  слишком  долгое,
длинное и грустное это занятие. В данном примере значения синуса
распаковываются  из  готового,  так  сказать,  архива. В таблице
RLESIN  находятся  DELTA  RLE-данные,  по  которым  строится 1/4
периода  таблицы. Вторая четверть - это "зеркальное" отображение
первой, ну а второй полупериод - копия первого. Легко!

SINTAB  EQU     #C000
        XOR     A
        LD      E,A
        LD      D,SINTAB&H;таблица всегда располагается по
                          ;"ровным" адресам #XX00
        LD      HL,RLESIN
        EX      AF,AF'    ;в A' будет формироваться очередное
                          ;значение синуса, начальное значение=0
LRP1    LD      A,(HL)
        RRA
        RRA
        RRA
        AND     #F    ;биты 3..6 данных - количество байтов
                      ;в пакете
        LD      B,A
        LD      A,(HL)
        AND     7     ;младшие 3 бита данных - приращение синуса
                      ;от предыдущего
        LD      C,A   ; C=+0..+7
        EX      AF,AF'
PVT     LD      (DE),A;кладём очередное значение в массив
        INC     E
        ADD     A,C   ;приращение значения
        DJNZ    PVT   ;к следующему значению в пакете
        EX      AF,AF'
        INC     HL    ;следующие RLE-данные
        BIT     6,E
        JR      Z,LRP1;проверка на конец 1/4 периода - >=64 байт
                      ;после выхода DE=#C040
        LD      L,E
        LD      H,D
        EX      AF,AF';A=#FF
LSI2
        LD      (DE),A;построение второй четверти периода
        INC     E
        DEC     L
        LD      A,(HL);копированием задом-наперёд
        JR      NZ,LSI2
        LD      C,E   ;DE=#C080 HL=#C000 BC=#0080
        LDIR          ;копирование первого полупериода во второй
        RET
RLESIN                ;DELTA-RLE таблица 1/4 периода синуса
        DEFB    #E,#F,#26,#F,#4E,#D
        DEFB    #16,#D,#E,#15,#E,#2D
        DEFB    #C,#D,#C,#D,#2C,#B
        DEFB    #C,#B,#C,#23,#A,#B
        DEFB    #2A,9,#A,#29,8,9,#18

  Раньше эта самая процедура "весила" ~115 байт.  Кому не лень -
посмотрите в BlAME.   (и  как  такое  могло  тогда просочиться?)
----------------------------------------------------------------
  Конечно,  в 512 байтах не развернёшься - можно рассчитывать на
атрибутные эффекты...

Вот процедура вывода атрибутного ч/б спрайта: кто может короче ?

ADRSCR  EQU     #5800
        LD      HL,SPRITE
        LD      DE,ADRSCR
LG2
        LD      B,8
LG1
        RLC     (HL)
        SBC     A,A;A=0/#FF
                   ;если   сюда  вставить   AND #COLOR,
                   ;то  будет  чёрно-#color-ный спрайт.
                   ;если-же добавить ещё ADD A,#COLOR2,
                   ;то будет совсем 2-х цветный спрайт.
        LD      (DE),A
        INC     E
        DJNZ    LG1
        INC     HL
        LD      A,E
        SUB     ESPR-SPRITE*8&L; =SUB #E0
        JR      NZ,LG2
        RET
SPRITE
        DEFB    #1D,#C7,#78,#FE
        DEFB    #35,#AC,#60,#18
        DEFB    #35,#AC,#63,#98
        DEFB    #3D,#C6,#73,#58
        DEFB    #35,#A3,#63,#58
        DEFB    #35,#A3,#63,#58
        DEFB    #35,#CE,#7B,#58
ESPR
----------------------------------------------------------------
  То же самое, но только спрайт получается псевдо-цветной:

        LD      HL,SPRITE
        LD      DE,ADRSCR
LG2
        LD      B,8
LG1
        RLC     (HL)
        SBC     A,A
        AND     E
        RRA           ;RRA - только для исключения FLASH
        LD      (DE),A
        INC     E
        DJNZ    LG1
        INC     HL
        LD      A,E
        SUB     ESPR-SPRITE*8&L
        JR      NZ,LG2
        RET
----------------------------------------------------------------
  Оно же. Спрайт плавно "проявляется".

        LD      HL,SPRITE
        LD      DE,ADRSCR
        LD      C,0
        EI            ;только чтобы не зависло
LG3
        PUSH    HL
        PUSH    DE
        HALT          ;можно и побольше - дело вкуса
        HALT
LG2
        LD      B,8
LG1
        RLC     (HL)
        SBC     A,A
        AND     E
        AND     C
        RRA
        LD      (DE),A
        INC     E
        DJNZ    LG1
        INC     HL
        LD      A,E
        SUB     ESPR-SPRITE*8&L
        JR      NZ,LG2
        POP     DE
        POP     HL
        SLI     C     ;C=0,1,3,7,#F,#1F,#3F,#7F,#FF
                      ;говорят, недокументированная команда.. :)
        JR      NC,LG3
        RET
----------------------------------------------------------------
  Ещё   для  атрибутных  эффектов  "заливают"  экран  "шахматным
полем", чтобы получить большее количество полутонов:

        LD      C,#AA
        LD      HL,#4000
LP1
        LD      (HL),C
        INC     L
        JR      NZ,LP1
        RRC     C
        INC     H
        LD      A,H
        SUB     #58
        JR      NZ,LP1
        RET
----------------------------------------------------------------
Далее я буду изъясняться тезисами. (M.M.A: Апрельские тезисы? :)
----------------------------------------------------------------
  Если  процедура  выполняется один раз - зачем её оформлять как
процедуру?
----------------------------------------------------------------
  По-моему, тем, кто использует конструкции типа:
                         CALL SUBR1
                         RET
следует 'публично высказывать фи'.
----------------------------------------------------------------
Необходимо 4 раза подряд в двух местах выполнить процедуру SUBR2

Неправильно:           Нормально:               Правильно:

CALL SUBR2;3           CALL SUBR_X;3            CALL SUBR_X;3
CALL SUBR2;3           ....                     ....
CALL SUBR2;3           CALL SUBR_X;3            CALL SUBR_X;3
CALL SUBR2;3           ....                     ....
....                   ....                     ....
....                   ....                     ....
CALL SUBR2;3    SUBR_X LD B,4     ;2     SUBR_X CALL SUBR_Y;3
CALL SUBR2;3       LLX PUSH BC    ;1     SUBR_Y CALL SUBR2 ;3
CALL SUBR2;3           CALL SUBR2 ;3     SUBR2  ....
CALL SUBR2;3           POP BC     ;1            ....
....                   DJNZ LLX   ;2            RET
                       RET        ;1
          ;24                     ;16                      ;12
----------------------------------------------------------------
  Если  в  предыдущем  фрагменте  программы в регистрах остаются
полезные данные, почему бы ими не воспользоваться?

Неправильно:           Нормально:               Правильно:

CALL SUBR_N            CALL SUBR_N              CALL SUBR_N
LD HL,0    ;3          XOR A      ;1            LD E,D      ;1
LD DE,0    ;3          LD H,A     ;1            INC DE      ;1
                       LD L,A     ;1            LD H,E      ;1
                       LD E,A     ;1            LD L,E      ;1
                       LD D,A     ;1
           ;6                     ;5                        ;4
SUBR_N
       ....
       LD DE,#FFBF
       RET

Неправильно:           Правильно:

LD A,E    ;            LD A,E    ;
CP #5B    ;            SUB #5B   ;
JR NZ,$-10;            JR NZ,$-10;
XOR A     ;1           OUT (C),A ;
OUT (C),A ;
          ;1                     ;0
----------------------------------------------------------------
  Одного и того же результата можно добиться разными путями.

Неправильно:           Правильно:

LD B,D                 LD B,D
OUT (C),A              OUT (C),A
LD B,E                 LD B,E
EX AF,AF';1            OUTI     ;2
LD A,(HL);1
OUT (C),A;2
INC HL   ;1
EX AF,AF';1
         ;6                     ;2


Ламерство:     Нормально:     Правильно:     Быстро:

LD HL,#4000;3  LD HL,#5AFF;3  LD HL,#5B00;3  XOR A      ;1
LD BC,#1B00;3  LD DE,#5AFE;3  XOR A      ;1  LD L,A     ;1
LD (HL),0  ;2  LD BC,#1B00;3  DEC HL     ;1  LD H,A     ;1
INC HL     ;1  LD (HL),C  ;1  LD (HL),A  ;1  LD E,A     ;1
DEC BC     ;1  LDDR       ;2  BIT 6,H    ;2  LD D,A     ;1
LD A,C     ;1                 JR NZ,$-4  ;2  ADD HL,SP  ;1
OR B       ;1                                EI         ;1
JP NZ,$-6  ;3                                HALT       ;1
                                             LD SP,#5B00;3
                                       .3456 PUSH DE    ;3456*1
                                             LD SP,HL   ;1
           ;15            ;12            ;10            ;3468

       Как все:            Правильно:

       CALL SUBRA          CALL SUBRA
       PUSH HL   ;1        ....
       ....                CALL SUBRA
       CALL SUBRA          ....
       PUSH HL   ;1        CALL SUBRA
       ....                ....
       CALL SUBRA          ....
       PUSH HL   ;1        ....
       ....                ....
       POP BC              POP BC
       POP DE              POP DE
       POP HL              POP HL
       ....                ....

SUBRA  ....         SUBRA  ....
       LD L,H              LD L,H
       SBC A,A             SBC A,A
       LD H,A              LD H,A
       RET       ;1        EX (SP),HL;1
                           JP (HL)   ;1
                 ;4                  ;2
----------------------------------------------------------------
                Ну, всё, что знал - рассказал.



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

Coding - Как кодить оптимально: генерация синус-таблицы, вывод атрибутного черно/белого спрайта.

Games of Millennium - Игры по мотивам фильма Renegade (Отступник).

Вступление - Ах, вот я...

Голос из могилы - Loki: Super Spectrum или Sprinter 80-ых?

Злоба дня - CC'999: мы же Вас предупреждали...

Злоба дня - F.A....Q

Новости - Ожидание CC'999 хуже самого CC'999.

Обзор - Обзор новинок: Forever, Heresy #1, Helicopter, Pussy.

Приложение - Работы с CC'99: Apocalips, Eclipse, Absent.


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

Похожие статьи:
Вступление - Итак, SEA QUEST... Эта игра была задумана нами с Тарасиком. К сожалению, вышло только играбельное дэмо, состоящее из одного уровня.
Мысли - Навеяные MSF: Философия.
Top 13 - Наша дюжина.
Учимся кoдить "Bесчи" - II - Уневеpсaльная пoдпpoгpaмма печaти шpифтoм 32*24.
For Coderz - Описание персонального компьютера ATM-TURBO 2+.

В этот день...   25 апреля