Из журнала Demo or Die #2, 1999 (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 __________________________________________