Мне тоже кажется, что для начала так будет понятнее.
Вид для печати
Доброго дня! Пока изучаю основы, и мне нужно напечатать карту игрового поля. Допустим я нарисовал её в ZX-Paintbrush, размеры 15*15 знакомест. Данные по изображению есть в формате :
; line based output of pixel data:
defb 255......
; line based output of attribute data:
defb 38h....
Какой несложной процедурой её можно распечатать, учитывая что она будет в качестве фона, статична и всегда в одном и том же месте (начальная позиция 3,9 в знакоместах).
Тогда лучше подогнать пиксельные строки под структуру экрана Спектрума. Т.е.:
1-я пиксельная строка 1-й строки знакомест
1-я пиксельная строка 2-й строки знакомест
...
1-я пиксельная строка 8-й строки знакомест
2-я пиксельная строка 1-й строки знакомест
...
2-я пиксельная строка 8-й строки знакомест
...
8-я пиксельная строка 8-й строки знакомест
1-я пиксельная строка 9-й строки знакомест
...
и т.д.
А потом командой ldir всё копировать из памяти на экран (до нее задать hl=адрес памяти, de=первый адрес экрана, bc=количество копируемых байт).
А если карта состоит из повторяемых элементов, то пиксельные строки - это расточительство.
- - - Добавлено - - -
Еще вариант - оставить данные со строками как есть у тебя, а в цикле по количеству пиксельных строк устанавливать hl,de,bc нужными и командой ldir копировать каждую пиксельную строку.
Изначально, самой простой идеей было "загнать" каждый элемент карты в символ UDG и распечатывать игровое поле, как из кусочков. Но проблема в том что каждая "ячейка" карты имеет размер 3*3 знакоместа (это соотвественно 9 символов UDG), и "собирать" её становиться нелегко.
У тебя каждая клетка - спрайт. А карта состоит из однобайтовых ID спрайтов. Вот и оперируй ими.
Вот процедура вывода двумерной карты из спрайтов 2х2 знакоместа, взята из моего кросс-компилятора Паскаля:
Процедуру несложно переделать под спрайты 3х3, как ты хочешь (задание на дом :) ).Код:; вывод участка карты на экран, состоящей из спрайтов 2х2 знакоместа
map_put ld a,(map_xmax_array)
ld c,a
ld b,0
ld a,(map_y)
dec a
ld e,a
ld d,0
call mul
ld a,(map_x)
dec a
ld e,a
ld d,0
add hl,de
ld de,(map_addr_array)
add hl,de
ld (map_addr_curr),hl
ld hl,(map_xscr)
ld (map_xscr_curr),hl
ld a,(map_height_scr)
ld b,a
map_put_01 push bc
ld a,(map_width_scr)
ld b,a
map_put_02 push bc
ld hl,(map_addr_curr)
ld a,(hl)
and a
ld hl,(map_addr_sprites)
jp z,map_put_04
ld b,a
ld de,36
map_put_03 add hl,de
djnz map_put_03
map_put_04 call map_put_10
ld hl,(map_addr_curr)
inc hl
ld (map_addr_curr),hl
ld hl,map_xscr_curr
inc (hl)
inc (hl)
pop bc
djnz map_put_02
ld hl,(map_addr_curr)
ld a,(map_ymax_array)
ld e,a
ld d,0
add hl,de
ld a,(map_width_scr)
ld e,a
and a
sbc hl,de
ld (map_addr_curr),hl
ld hl,map_yscr_curr
inc (hl)
inc (hl)
ld a,(map_xscr)
ld (map_xscr_curr),a
pop bc
djnz map_put_01
ret
map_put_10 ex de,hl
ld bc,(map_xscr_curr)
LD A,B
RRCA
RRCA
RRCA
LD L,A
AND 31
OR 88
LD H,A
LD A,L
AND 252
OR C
LD L,A
ld a,(de)
ld (hl),a
inc de
inc hl
ld a,(de)
ld (hl),a
inc de
ld bc,31
add hl,bc
ld a,(de)
ld (hl),a
inc de
inc hl
ld a,(de)
ld (hl),a
inc de
ld bc,(map_xscr_curr)
LD A,B
LD H,A
RRCA
RRCA
RRCA
AND A,224
ADD A,C
LD L,A
LD A,H
AND 24
OR 64
LD H,A
dup 7
ld a,(de)
ld (hl),a
inc de
inc h
edup
ld a,(de)
ld (hl),a
inc de
ld bc,#6ff
and a
sbc hl,bc
dup 7
ld a,(de)
ld (hl),a
inc de
inc h
edup
ld a,(de)
ld (hl),a
inc de
ld bc,#6e1
and a
sbc hl,bc
dup 7
ld a,(de)
ld (hl),a
inc de
inc h
edup
ld a,(de)
ld (hl),a
inc de
ld bc,#6ff
and a
sbc hl,bc
dup 7
ld a,(de)
ld (hl),a
inc de
inc h
edup
ld a,(de)
ld (hl),a
ret
map_addr_array defw 0 ; начальный адрес карты, состоящей из однобайтовых id спрайтов
map_xmax_array defb 0 ; размер карты в байтах по горизонтали
map_ymax_array defb 0 ; размер карты в байтах по вертикали
map_addr_sprites defw 0 ; начальный адрес спрайтов карты
map_xscr defb 0 ; столбец экрана, с которого выводить карту
map_yscr defb 0 ; строка экрана, с которой выводить карту
map_width_scr defb 0 ; количество выводимых клеток карты по горизонтали
map_height_scr defb 0 ; количество выводимых клеток карты по вертикали
map_x defb 0 ; горизонтальная координата клетки карты, с которой выводить
map_y defb 0 ; вертикальная координата клетки карты, с которой выводить
map_addr_curr defw 0 ; текущий адрес карты
map_xscr_curr defb 0 ; текущий столбец экрана
map_yscr_curr defb 0 ; текущая строка экрана
; функция беззнакового умножения 16-разрядных чисел
; (C) Kirill Frolov
; idea by Vasil Ivanov
; dehl=de*bc ~730t.
mul ld a,d
and a
jp nz,mul_00
ld ix,mul_00
ld h,b
ld l,c
ld a,e
jp mul_02
mul_00 ld a,b
and a
jp nz,mul_01
ld ix,mul_01
ld h,d
ld l,e
ld a,c
jp mul_02
mul_01
ld hl, 0
ld a, c
ld c, l ; 18
add a, a
jr nc, $+4
add hl, de
adc a, c ; 29
; REPEAT 7
add hl, hl
rla
jr nc, $+4
add hl, de
adc a, c ; 40*7 309
add hl, hl
rla
jr nc, $+4
add hl, de
adc a, c
add hl, hl
rla
jr nc, $+4
add hl, de
adc a, c
add hl, hl
rla
jr nc, $+4
add hl, de
adc a, c
add hl, hl
rla
jr nc, $+4
add hl, de
adc a, c
add hl, hl
rla
jr nc, $+4
add hl, de
adc a, c
add hl, hl
rla
jr nc, $+4
add hl, de
adc a, c
; ENDR
ld c, a
push hl ; 0chl
ld hl, 0
ld a, b
ld b, l ; 33
add a, a
jr nc, $+4
add hl, de
add a, b ; 29
; REPEAT 7
add hl, hl
rla
jr nc, $+4
add hl, de
add a, b ; 40*7 309
add hl, hl
rla
jr nc, $+4
add hl, de
add a, b
add hl, hl
rla
jr nc, $+4
add hl, de
add a, b
add hl, hl
rla
jr nc, $+4
add hl, de
add a, b
add hl, hl
rla
jr nc, $+4
add hl, de
add a, b
add hl, hl
rla
jr nc, $+4
add hl, de
add a, b
add hl, hl
rla
jr nc, $+4
add hl, de
add a, b
; ENDR
; ahl0 + 0cde = hlde
pop de
ld b, l
ld l, h
ld h, a
ld a, d
add a, b
ld d, a
ld b, 0
adc hl, bc
ex de, hl ; 60
ret ; 729t.
mul_02 and a
jp z,mul_n0
cp 1
jp z,mul_n1
cp 2
jp z,mul_n2
cp 3
jp z,mul_n3
cp 4
jp z,mul_n4
cp 5
jp z,mul_n5
cp 8
jp z,mul_n8
cp 10
jp z,mul_n10
cp 15
jp z,mul_n15
cp 16
jp z,mul_n16
cp 20
jp z,mul_n20
cp 32
jp z,mul_n32
cp 50
jp z,mul_n50
cp 64
jp z,mul_n64
cp 100
jp z,mul_n100
cp 128
jp z,mul_n128
jp (ix)
mul_n0 ld hl,0
ret
mul_n1 ret
mul_n2 add hl,hl
ret
mul_n3 ld d,h
ld e,l
add hl,hl
add hl,de
ret
mul_n4 add hl,hl
add hl,hl
ret
mul_n5 ld d,h
ld e,l
add hl,hl
add hl,hl
add hl,de
ret
mul_n8 add hl,hl
add hl,hl
add hl,hl
ret
mul_n10 add hl,hl
ld d,h
ld e,l
add hl,hl
add hl,hl
add hl,de
ret
mul_n15 ld d,h
ld e,l
add hl,hl
add hl,hl
add hl,hl
add hl,hl
and a
sbc hl,de
ret
mul_n16 add hl,hl
add hl,hl
add hl,hl
add hl,hl
ret
mul_n20 add hl,hl
add hl,hl
ld d,h
ld e,l
add hl,hl
add hl,hl
add hl,de
ret
mul_n32 add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
ret
mul_n50 add hl,hl
ld d,h
ld e,l
add hl,hl
add hl,hl
add hl,hl
ld b,h
ld c,l
add hl,hl
add hl,bc
add hl,de
ret
mul_n64 add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
ret
mul_n100 add hl,hl
add hl,hl
ld d,h
ld e,l
add hl,hl
add hl,hl
add hl,hl
ld b,h
ld c,l
add hl,hl
add hl,bc
add hl,de
ret
mul_n128 add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
ret
Спрайты карты
Один спрайт занимает 2×2 знакоместа (в принятом формате – всегда 36 байт с атрибутами). Спрайты идут последовательно в памяти, согласно кодам элементов карты. Коды элементов карты должны быть последовательными от 0 до максимума, не более 255. Количество спрайтов должно строго соответствовать количеству кодов карты.
Формат одного спрайта карты:
NAME
DEFB 3,3,3,3
DEFB 7,31,63,127,120,247,252,249
DEFB 192,240,248,252,124,62,62,62
DEFB 243,243,120,127,63,31,7,0
DEFB 62,62,60,252,248,240,192,0
NAME – метка начала спрайтов элементов карты, имя метки обязательно заглавными буквами. Метка ставится вначале всех спрайтов карты, всегда одна для всех спрайтов карты.
DEFB 3,3,3,3 – атрибуты для каждого из 4-х знакомест спрайта, могут быть различными.
Далее идет по 8 байт изображения для каждого из 4-х знакомест спрайта.
И еще пару-тройку советов, если позволите. :) До них я допёр с помощью инета уже после прочтения книги "Как написать игру", в книгах для начинающих этого не пишут почему-то.
1. В вышеприведенной процедуре используется отдельная процедура mul умножения двух регистровых пар. Вообще-то, это занимает много времени, поэтому для улучшения быстродействия очень желательно делать индексы массивов (в данном случае размерность карты) кратным 2^n (два в степени n), т.е. 4,8,16,32,64,128 или 256 и т.д. Тогда процедура "честного" умножения mul выкидывается полностью, а умножение заменяется банальной последовательностью сложений рег.пары hl самой с собой. Например, умножение hl на 8:
add hl,hl
add hl,hl
add hl,hl
И вообще, в прогах по возможности нужно избегать умножений и делений на числа, не кратных 2. Для кратных 2 числам просто применяются команды сдвигов влево (для умножения) или вправо (для деления).
2. Вывод спрайтов в книге Капульцевичей производится с расчетом для каждого знакоместа адреса вывода на экран изображения и адреса вывода атрибутов. Это идиотизм, если знакоместа находятся рядом. Проще рассчитать адрес первого знакоместа, а остальные адреса получать смещением от начального - сложением текущего адреса hl и константы. Что я и сделал, см. код ниже с метки map_put_10. Сначала рассчитывается адрес экрана в области атрибутов, затем змейкой друг за другом от него выводятся все 4 атрибута знакомест сразу. Затем то же самое для изображений знакомест - змейкой. Если спрайт 3х3, то змейкой можно выводить сразу 9 знакомест.
3. Не экономь память в коде при выводе изображения, это повышает быстродействие. В книге Капульцевичей вывод 8 строк-байт одного знакоместа производится в цикле:
ld b,8
loop ld a,(de)
ld (hl),a
inc de
inc h
djnz loop
Перцы покруче раскрывают цикл и пишут подряд 8 раз:
ld a,(de)
ld (hl),a
inc de
inc h
ld a,(de)
ld (hl),a
inc de
inc h
ld a,(de)
ld (hl),a
inc de
inc h
...
Или, если написать это короче:
dup 8
ld a,(de)
ld (hl),a
inc de
inc h
edup
Это занимает больше памяти, зато процессору не надо выполнять лишние такты на djnz loop для каждого байта. А особенно если еще встречается в начале и конце цикла какое-нибудь push bc / pop bc, то выгода по тактам очень огромна.
А самые крутые перцы делают вообще через стек: сначала адрес стека SP устанавливают на адрес в экране, затем загоняют по два байта изображения из памяти в рег.пару, а потом выводят в стек-экран командой push. Это занимает еще меньше времени по тактам.
Какой тонкий юмор! Я бы даже сказал, утончённый садизм. :)
Заявив о медлительности индексной адресации, привести в качестве альтернативы одну значительно более медленную, а другую одинаковую по скорости процедуры. :) Да к тому же, в отличие от индексной адресации, требующих дополнительных регистров :)
LD r,(IX+n) занимает 19 тактов.
А вторая процедура, хоть, и 11, но требует загрузки рег.пары H, а это уже +10т. Итого: 21т., что медленнее, чем через индексные регистры.
Можно и за 8 тактов HL загрузить - тогда общее время будет 19т.
Всем привет!!! Потихоньку продвигаюсь в написании своей концептуальной игры. Уже нашКОДил примерно 40%.
Разобрался таки со скроллами окон. Если кто видел сообщение оно уже не актуально)
Подскажите, вот я работаю в zx-spin. Мне нужно понять сколько в кб "весит" мой текущий код. Нужно же знать сколько еще у меня запаса памяти на игру)