|
Born Dead
#0A
02 июня 1999 |
|
Coding - Как кодить оптимально: генерация синус-таблицы, вывод атрибутного черно/белого спрайта.

════════════════════════════════════════════════════════════════
ЇЄ╒▐╟░░╟▐╒ЄєЇ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. |
Похожие статьи:
В этот день... 2 ноября