Всем привет. Есть мысль разработать оптимизированный набор процедур - аналогов команд LASER BASIC.
Я пока набросал универсальную подпрограмму вывода познакоместного спрайта с атрибутами. Требуется ваша помощь (особенно обращаюсь к Destr'у, AzAtom'у и Reobne). Код тестировал на спрайтах разного размера. По-моему, получилось довольно хорошо.
- Конструктивная критика (баги? ускорить без серьёзного увеличения размера?)
- Нужно модифицировать, чтобы можно было выводить спрайты с выходом за пределы экрана (как по X, так и по Y).
- Вообще философский момент. Я кинулся кодить, но потом понял, что реализовал формат, не совместимый с оригинальным LASER BASIC'ом. Там сперва спрайт выводится весь по ширине, потом следующая пиксельная линия и т.д. А у меня получилось так: выводится первое знакоместо целиком, потом второе, и так на ширину линии, потом преход к следующей знакоместной строке. Чтобы я не занимался мазохизмом, скажите, пожалуйста, есть ли выверенные методики вывода подобных спрайтов, которые диктуют наиболее удачный формат для быстрого вывода? Берём в расчёт то, что самый внутренний цикл в моей реализации крутится 8 раз (выводим знакоместо), а в лазерной - количество, равное ширине спрайта (выводим строку). То есть мой вариант формата во внутреннем цикле оптимальнее лазерного для спрайтов, ширина которых меньше восьми знакомест.
Так что есть альтернатива: или слегка модифицировать формат и написать перекодировщик. Или остаться на старом лазерном формате. Это вопрос, на который я ещё себе не ответил.
Атрибуты бы целенаправленно не хотелось вставлять вперемешку с блоками пикселей, хотя такой вариант я тестировал. Но решено, что у нас останется возможность выводить спрайты без атрибутов, и тогда, если они в конце, их вывод просто легче отрезать, чем обходить атрибутные данные доп. кодом.
Критикуем, господа.
Код:/*********************/
/* LASER BASIC 2 */
/* by Oleg N. Cher */
/* zx.oberon2.ru */
/*********************/
extern unsigned int Laser2_SPRT_ADR; // Sprite file start address
/* Спрайты хранятся в памяти в следующем формате:
Байт 1 - номер спрайта.
Байт 2 - младший байт размера спрайта (9*HGT*LEN+3).
Байт 3 - старший байт размера спрайта.
Байт 4 - длина спрайта.
Байт 5 - высота спрайта.
8*HGT*LEN - данные о состоянии
пикселей.
HGT*LEN - атрибуты.
ИТОГО: 9*HGT*LEN+5 байтов.
*/
/*--------------------------------- Cut here ---------------------------------*/
void Laser2_ATOF (void)
{
__asm
LD A, #0xC9 ; "RET"
LD (Laser2_ATOF_IN), A
__endasm;
} //Laser2_ATOF
/*--------------------------------- Cut here ---------------------------------*/
void Laser2_ATON (void)
{
__asm
LD A, #0xED ; "LD DE,(ADDR) ED5BXXXX"
LD (Laser2_ATOF_IN), A
__endasm;
} //Laser2_ATON
/*--------------------------------- Cut here ---------------------------------*/
void Laser2_PTBL (signed char col, signed char row, unsigned char spn) __naked {
__asm
POP HL
POP BC ; C = col; B = row
POP DE ; E = spn
PUSH DE
PUSH BC
PUSH HL
XOR A ; NOP
JP _Laser2_PUT_SPRITE
__endasm;
} //Laser2_PTBL
/*--------------------------------- Cut here ---------------------------------*/
void Laser2_PTOR (signed char col, signed char row, unsigned char spn) __naked {
__asm
POP HL
POP BC ; C = col; B = row
POP DE ; E = spn
PUSH DE
PUSH BC
PUSH HL
LD A, #0xB6 ; OR (HL)
JP _Laser2_PUT_SPRITE
__endasm;
} //Laser2_PTOR
/*--------------------------------- Cut here ---------------------------------*/
void Laser2_PTXR (signed char col, signed char row, unsigned char spn) __naked {
__asm
POP HL
POP BC ; C = col; B = row
POP DE ; E = spn
PUSH DE
PUSH BC
PUSH HL
LD A, #0xAE ; XOR (HL)
JP _Laser2_PUT_SPRITE
__endasm;
} //Laser2_PTXR
/*--------------------------------- Cut here ---------------------------------*/
void Laser2_PTND (signed char col, signed char row, unsigned char spn) __naked {
__asm
POP HL
POP BC ; C = col; B = row
POP DE ; E = spn
PUSH DE
PUSH BC
PUSH HL
LD A, #0xA6 ; AND (HL)
JP _Laser2_PUT_SPRITE
__endasm;
} //Laser2_PTND
/*--------------------------------- Cut here ---------------------------------*/
void Laser2_PUT_SPRITE (void) __naked {
// A: mode; C: col; B: row; E: spn
__asm
.globl Laser2_ATOF_IN
LD (SPRT_MODE_IN$), A ; Set draw mode
; Процедура расчёта адреса экрана из координат
; Вход: C=x, B=y
; Выход: HL = адрес видеопамяти
LD A, B
AND #7
RRCA
RRCA
RRCA
OR C
LD L, A
LD A, B
AND #24
OR #0x40
LD H, A ; 14 байт, 53 такта
LD (SCR_ADR_IN$+1), HL
LD HL, (_Laser2_SPRT_ADR)
FIND_BY_N_IN$: LD A, (HL) ; N of a sprite
OR A
RET Z
INC HL
CP E ; spn
JR Z, SPRT_FOUND_IN$
LD C, (HL)
INC HL
LD B, (HL)
ADD HL, BC ; + offset to next sprite
JR FIND_BY_N_IN$
SPRT_FOUND_IN$: INC HL
INC HL
LD C, (HL) ; length of sprite
INC HL
LD B, (HL) ; height of sprite
LD (SPRT_HGT_LEN_IN$+1), BC
INC HL
SCR_ADR_IN$: LD DE, #0 ; adr of sprite (up left corner)
SPRT_HLINE_IN$: LD A, E ; Begin of loop on charlines
LD (SCR_LOBYTE_IN$+1), A
PUSH BC
LD B, #8
SPRT_CHAR_IN$: LD A, (HL)
SPRT_MODE_IN$: NOP ; NOP | AND (HL) | OR (HL) | XOR (HL)
LD (DE), A
INC HL
INC D
DJNZ SPRT_CHAR_IN$
LD B, #8 ; Draw 8 bytes (one charline)
LD A, D
SUB A, B
LD D, A
INC E ; Next screen line
DEC C
JR NZ, SPRT_CHAR_IN$
SCR_LOBYTE_IN$: LD A, #0
ADD #0x20 ; Next charline
LD E, A ; If carry then jump to next third of screen
JR NC, CONTIN_1_3_IN$
LD A, D ; Next third of screen
ADD B
LD D, A ; DE := DE + 0x0800
CONTIN_1_3_IN$: POP BC
DJNZ SPRT_HLINE_IN$ ; End of loop on charlines (the same third)
Laser2_ATOF_IN: ; RET
LD DE, (SCR_ADR_IN$+1)
LD A, D ; Calculate attribute address
RRCA
RRCA
RRCA
AND #3
OR #0x58
LD D, A
SPRT_HGT_LEN_IN$: LD BC, #0
LD A, #32
SUB A, C ; 32 - len
LD (SPRT_HGT_DIS_IN$+1), A
DRAW_ATRLINE_IN$: PUSH BC ; Begin of loop on charlines
LD B, #0
LDIR
LD A, E
SPRT_HGT_DIS_IN$: ADD #0
LD E, A
LD A, C
ADC D
LD D, A
POP BC
DJNZ DRAW_ATRLINE_IN$
RET
__endasm;
} //Laser2_PUT_SPRITE