__________________________________________
(C) Devil of eTc/Scene'99
..... Moving Shit .....
Данная статья описывает древний эффект
под хитрым названием Moving Shit. Я уверен
что многие наши читатели давно его знают,
однако для начинающего програмиста она
окажется, я надеюсь, интересной.
Попутно мы рассмотрим множество проблем
возникающих при опимизации алгоритмов,
таких как:
- переброска спрайта на экран с
исполизованием стека,
- рациональное использование таблиц для
расчета адреса экрана, адреса линии в
спрайте и т.д. и т.п.
Для начала суть алгоритма.
К примеру мы имеем таблицу синусов, а
так же две переменные характеризующие
смещение в данной таблице.
Для определенности, пусть таблица у нас
будет длинной 256 байт и будет принимать
значения от 0 до 64. Так же, для ускорения
кода, расположим ее по кратному адресу.
Переменные смещения примем 8-ми битными
и пусть они находится соответствено в
переменных A и B.
Теперь нам надо создать спрайт в каком
либо редакторе. При этом спрат может иметь
произвольную ширину (в разумных пределах),
но длинна должна быть равна удвоенному
муксимальному значению нешей синусоиды
т.е. 64*2=128. В спрайте может находится
все что угодно: круги теугольники и т.д.,
в общем все зависит от вашей фантазии (для
примера я создал два разных спрайта и Вы
можете сразу увидеть разницу в эффекте).
Далее нам надо организовать цикл в
котором мы как бы будем выводить спрайт на
экран, но не напрямую, а расчитывая адрес
каждой линии исходя из того, что
NumLin:=(SIN[A+n]+SIN[B+n]);
AddrLine:=AddrSpr+NumLin*len;
где NumLin - номер выводимой линии;
n - берется из счетка цикла;
AddrSpr - ну тут все ясно это ад-
рес спрайта в памяти;
len - ширина спрайта в знакоместах;
После того как мы получили адрес линии,
смело выводим ее на экран и переходим к
расчету следующей.
После того как мы построили спрайт надо
изменть переменные A и B по формуле типа:
A:=A+Step1;
B:=B+Step2;
{Step1 не равно Step2}
Вот и все. Если вы все понели то дальше
можете не читать, а для сомневающихся
привожу пример исходника с подробными
пояснениями.
ORG #7000
Y_S EQU 129 ;длинна спрайта
CALL INIT_P ;Инициализируем все
;таблицы и кидетели
;подробнее в самой
;процедуре
START LD A,#7F ;проверяем на нажатие
IN A,(#FE) ;пробела и выходим
RRA
RET NC
EI
XOR A
OUT (#FE),A
HALT ;ждем INT'а
LD A,1
OUT (#FE),A
DI
LD D,SINTAB[ ;заносим в D и H
LD H,D ;старший адрес табл.
;синусов
POS1 LD A,0 ;INC'аем переменную
.1 INC A ;A (см. выше)
LD (POS1+1),A
LD E,A
POS2 LD A,0 ;DEC'аем переменную
.4 DEC A ;B (см. выше)
LD (POS2+1),A
LD L,A
;в принципе INC или DEC подбирайте сами по
;вкусу, главное, чтобы прирошение
;переменных A и B было как можно более
;разным
EXX
LD H,TBLADD[ ;заносим в H',старший
EXX ;байт таблицы по ко-
;торой мы считаем
;AddrLine, подставляя
;в L', NumLin
LD (SPBUF),SP
LD SP,BUFADR ;в SP,таблица адресов
;линий в раскрытом
;кидателе спрайта
GETAD INC E,L
LD A,(DE)
ADD A,(HL)
EXX
ADD A,A ;набодим NumLin
LD H,TBLADD[
LD L,A ;L=NumLin
POP DE ;DE,адрес в кидателе
;спрайта
LDI ;заносим AddrLin
LDI ;в раскрытый кидатель
EXX ;спрайта
ENDGET
DS (ENDGET-GETAD)*Y_S
;В этом буфере у нас повтруен 129 раз
;участок кода от GETAD до ENDGET, для
;ускорения
LD SP,(SPBUF)
CALL PUTSP ;собственно кидаем на
;экран спрайт
JP START ;на начало
DOWN_HL INC H
LD A,H
AND 7
RET NZ
LD A,L
ADD A,32
LD L,A
RET C
LD A,H
SUB 8
LD H,A
RET
;Процедура INIT_P генерирует кидатель
;спрайта с применением стека
INIT_P
LD HL,PUTLIN
LD DE,ENDPUTL
LD BC,(ENDPUTL-PUTLIN)*(Y_S-1)
LDIR
EX DE,HL
LD (HL),#ED
INC HL
LD (HL),#7B
INC HL
LD (HL),SPBUF]
INC HL
LD (HL),SPBUF[
INC HL
LD (HL),#C9
;далее идет процедура которая генерит
;процедуру расчета таблицы адресов линий
LD HL,GETAD
LD DE,ENDGET
LD BC,(ENDGET-GETAD)*(Y_S-1)
LDIR
LD B,Y_S
LD HL,#4010
LD IX,ADRSCR+1
LD DE,ENDPUTL-PUTLIN
INIT1 LD (IX),L
LD (IX+1),H
ADD IX,DE
CALL DOWN_HL
DJNZ INIT1
LD B,Y_S
LD DE,ENDPUTL-PUTLIN
LD IX,BUFADR
LD HL,PUTLIN+1
INIT2 LD (IX),L
LD (IX+1),H
INC IX
INC IX
ADD HL,DE
DJNZ INIT2
LD B,Y_S
LD HL,0
LD DE,SPRAIT
LD IX,TBLADD
INIT3 LD C,L
.4 ADD HL,HL
ADD HL,DE
LD (IX),L
INC IX
LD (IX),H
INC IX
LD H,0
LD L,C
INC L
DJNZ INIT3
RET
;SPRAIT INCB "SHIT_SP1"
SPRAIT INCB "SHIT_SP2"
SPBUF DW 0
BUFADR DS Y_S*2
ORG (($-1)[+1)*256
SINTAB INCB "STAB"
ORG (($-1)[+1)*256
TBLADD DS Y_S*2
PUTSP LD (SPBUF),SP;20
PUTLIN LD SP,0; 10
EXX ; 4
POP HL; 10
POP DE; 10
POP BC; 10
POP AF; 10
EXX ; 4
POP DE; 10
POP BC; 10
POP HL; 10
POP IY; 10
ADRSCR LD SP,0; 6
PUSH IY; 11
PUSH HL; 11
PUSH BC; 11
PUSH DE; 11
EXX ; 4
PUSH AF; 11
PUSH BC; 11
PUSH DE; 11
PUSH HL; 11
EXX ; 4
ENDPUTL ;итого: 222 такта
;т.е. 13,87 такта на байт
;что не так уж и плохо...
На сим прощоюсь и в заключение хочу
добавить что пути в оптимизации
неисчислимы и как сказал один из великих:
"Не существует программы, которую нельзя
сделать на один байт короче, либо на один
такт быстрее."
Вопросы шлите на адрес:
to: Sergey Movchan 2:4635/100.128@FidoNet
__________________________________________