ZXNet эхоконференция «code.zx»


тема: статья о выводе спрайтов(из неопубликованого :)



от: Alexander Kucher
кому: All
дата: 18 Sep 2000
Привет All!


=== Цитирую файл SPRITE.TX ===
(c) Stels
Hello всем, кто решил заглянуть в этот
раздел нашего журнала !

Данная статья расчитана на более-менее
подготовленого читателя, хотя можно
конечно и не вникать во все тонкости
этого дела.
Редко перед каким программистом не воз-
никала проблема вывода спрайта с точ-
ностью до пиксела. Причем если перед-
вигать спрайт по вертикали довольно
легко, то с движением по горизонтали уже
возникают довольно существенные труд-
ности. Эту проблему можно решить нес-
колькими способами, которые зависят от
конкретной ситуации.
Hапример можно построить в памяти
восемь сдвинутых друг относительно друга
спрайтов, этот способ позволяет получить
высокую скорость вывода спрайтов, но
имеет свои недостатки. Самое главное,
что каждый спрайт занимает в 8 раз
больше памяти, а следовательно его можно
применять лишь при небольшом количестве
спрайтов.
Второй способ это сдвигать каждый
выводимый байт спрайта вправо на 0..7
позиций, что занимает довольно много
времени, но зато высвобождает больше
памяти.
Третий способ, это способ немного
напоминающий второй, но работающий
значительно быстрее. Hедостаток этого
способа в том, что сама процедура вывода
занимает намного больше места, чем в
двух первых случаях, и поэтому не
рекомендуется его применять если вы
не испытываете недостатка в памяти или
вам не требуется большое быстродействие.
Также скажу, что этот способ вывода
спрайтов с небольшими изменениями
позаимствован мною из игры
Вячеслава Медноногова "Черный Ворон".
Теперь о некоторых ограничениях:
-нельзя выводить спрайты, выходящие за
границы экрана.
-т.к. процедура использует стек, то
нужно или специальным образом изменить
обработчик прерываний, или вообще их
запретить. В противном случае спрайт
будет затираться мусором.

Фактически нам предстоит написать 8
почти одинаковых процедур вывода, каждая
из которых будет выводить на экран
спрайт со сдвигом относительно
знакоместа на 0..7 пикселей.

Сначала пишем процедуру, которая выводит
один вертикальный столбец спрайта со
сдвигом 0 , т.е без сдвига.
Входные данные для всех восьми процедур
будут такие:
HL - Адрес в экране.
B - Высота спрайта в пикселах.
SP - Адрес выводимого столбца спрайта,
увеличеный на 2.
DE - Первые два байта спрайта.
(D - спрайт,E - маска).

Put0 ld a,(hl)
or e
xor d
ld (hl),a
pop de ;берем следующие байты
;спрайта и маски.
djnz Put0_
jp Exit ;Выход если нарисовали
;весь столбец.
Put0_ inc h ;Вычисляем адрес
ld a,h ;следующей строки
and 7 ;в экране.
jp nz,Put0
ld a,l
add a,#20
ld l,a
jr c,Put0
ld a,h
sub 8
ld h,a
jr Put0

Остальные семь процедур отличаются друг
от друга только первой частью, хотя
конечно некоторые заметят,что вычисление
следующего адреса строки можно было-бы
оформить в виде процедуры и вызывать
через CALL ,однако это существенно
снизило бы скорость работы.

Теперь напишем вторую часть процедуры,
которая выводит этот-же столбец спрайта,
но уже со сдвигом вправо на 1 бит.
Входные параметры те-же.

Put1 ld a,e
rrca ;сдвигаем вправо маску
ld c,a ;сохраняем
and #7F ;отбрасываем бит,
;кот.вылез слева
or (hl)
ld (hl),a
ld a,c
and #80 ;переносим бит на
inc l ;знакоместо правее
or (hl)
ld (hl),a
;повторяем те-же операции
ld a,d ;для изображения
rrca
ld c,a
and #80
xor (hl)
ld (hl),a
ld a,c
and #7F
dec l
xor (hl)
ld (hl),a
pop de
djnz Put1_
jp Exit
Put1_ inc h ;Вычисляем адрес
ld a,h ;следующей строки
and 7 ;в экране.
jp nz,Put1
ld a,l
add a,#20
ld l,a
jr c,Put1
ld a,h
sub 8
ld h,a
jr Put1

В процедуре Put2 выполняем те-же опера-
ции, но только сдвигаем на два бита, и
переносим эти-же два бита на знакоместо
правее.

Однако следует заметить, что в процедуре
Put5 мы сдвигаем байт не на 5 битов,
а всего лишь на 3, но влево что даст
тот-же результат, но будет работать
быстрее.
В процедуре Put6 на два бита вправо,
а в процедуре Put7 на 1.

Теперь осталось лишь написать управля-
ющую часть процедуры.

;Hа входе процедуры:
;HL-Адрес спрайта
;DE-Координаты на экране (Y,X)
;B-Высота спрайта в пикселах
;C-Длина спрайта в знакоместах
PutSpr push bc
push hl
call ScrPut
ex de,hl
pop hl
pop bc
PutSpr0 push bc
push de
call Line0
pop de
inc e
pop bc
dec c
jr nz,PutSpr0
ret

;Вход: DE-Y(0..191),X(0..255)
;Выход:HL-Адрес в экране
; Переключает на нужную процедуру
ScrPut call Scr
ex de,hl
ld bc,PutTable
ld h,0
ld l,a
add hl,hl
add hl,bc
ld a,(hl) ;Берем адрес процед.
inc hl
ld h,(hl)
ld l,a
ld (Jump0+1),hl ;переключаем
;на вывод этой процедурой
ex de,hl
ret
;Таблица процедур
PutTable dw Put0,Put1,Put2,Put3
dw Put4,Put5,Put6,Put7

;Вх.:DE-Y(0..191),X(0..255)
;Выход:HL-Адрес в экране
; A-(0..7) на сколько нужно сдвинуть
; спрайт вправо.
Scr ld a,e
and 7
push af
srl e
srl e
srl e
ld a,d
rrca
rrca
rrca
and #18
ld h,a
ld a,d
and 7
add a,h
ld b,a
ld a,#40
add a,b
ld h,a
ld a,d
rla
rla
and #E0
or e
ld l,a
pop af
ret

;Рисует один вертикальный столбец
;HL-адрес спрайта
;DE-адрес вывода в экране
;B-высота спрайта
Line0 push bc
ld c,(hl)
inc hl
ld b,(hl)
inc hl
ld (Line1+1),hl
ld h,b
ld l,c
ex de,hl
pop bc
ld (Exit0+1),sp
Line1 ld sp,0
Jump0 jp 0 ;Переход на процедуру

Exit ld hl,0
add hl,sp
dec hl
dec hl ;адрес следующего
;столбца спрайта
Exit0 ld sp,0 ;восстановить стек
ret

О формате самих спрайтов вы можете
узнать внимательно изучив структуру
процедуры.
Данные спрайта располагаются в следу-
ющей последовательности:
Сверху вниз, слева направо чередуются
байты маски и спрайта, причем первый
байт - маска.

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
Организация прерываний
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Внимательно изучив процедуры, некоторые
из вас вероятно заметят, что процедура
Line0 написана очень заворочено, ведь
ее можно упростить в несколько раз:

Line0
Line1 ld sp,0
dec sp
dec sp
pop de
Jump0 jp 0
Hа самом деле такая навороченость нужна
для правильной работы прерываний, ведь
при вызове прерывания часть спрайта
затирается адресом возврата и чтобы
восстановить спрайт, в DE нужно иметь
испорченые два байта спрайта, которые
мы по окончанию обработки возвращаем на
место. Однако т.к. мы сначала устанав-
ливаем стек, а только потом читаем DE,
то при приходе прерывания в этот момент
в DE не окажется испорченых данных.

Теперь привожу процедуру обработки
прерываний:
Inter ld (SP__+1),sp
ld sp,Stack ;Временный стек
push af,bc,de,hl,ix,iy
exx
ex af,af'
push af,bc,de,hl

;Ваш обработчик

pop hl,de,bc,af
ex af,af'
exx
pop iy,ix
;Сдесь мы узнаем находится ли
;сейчас стек на спрайте
SP__ ld hl,0
ld de,#6000 ;Hижняя граница
;спрайтов
sbc hl,de
jr c,Normal
ld hl,(SP__+1)
ld e,(hl)
inc hl
ld d,(hl)
ld (Jump+1),de
pop hl,de,bc,af
ld sp,(SP__+1)
inc sp
inc sp
push de
ei
Jump jp 0
;Если стек ниже спрайтов
Normal pop hl,de,bc,af
ld sp,(SP__+1)
ei
ret

Следует также отметить, что спрайты
должны располагаться выше границы
спрайтов, а стек ниже этой границы.
Вы конечно-же можете установить свое
значение границы.


В приложении вы найдете все исходники.


=== Конец цитаты ===

С уважением, Alexander 18 сентября 2000 года




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

Похожие статьи:
Графика - картинка АNSI графики.
Печатается с продолж. - ZILOG - основные сведения о процессорах.
В гостях у сказки - Сказка о рыбаке и рыбке (в компьютерном изложении).
Премьера - Mortal Kombat: что ждёт Вас в полной версии игры и некоторые коментарии к demo версии.
Железо - этот таинсвенный Sprinter!

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