Программирование GameBoyColor.
Вопросом программирования я занялся по одной причине - на pouet.net
обсуждался красивый эффект X-Rotator, часть ссылок вела на демки для
GBC.

Mental Respirator, http://www.pouet.net/prod.php?which=16402
Горимый желанием разобраться "как это работает?", я скачал демки,
эмулятор bgb, запустил демо, вылез в отладчик и... ничего не понял.
Так и остались лежать файлы в папке.
Спустя некоторое время я вернулся к GBC, и вот что дал поиск по
гуглу и по web.archive.org(так как платформа непопулярна, то часть
сайтов канула в Лету).
Итак, что у нас есть:
- процессор Z80 с другим набором команд. Это означает - забудьте об
LDI, IN, OUT, LD(NNNN),HL, LD(NNNN),DE, LD(NNNN),BC
- так как программа есть ROM, то самомодификация кода отменяется.
- экран отображает тайлы и спрайты.
Первый старт.
Нужно скачать ассемблер RGBDS, или tasm69 с набором инструкций 69.
Любители острых ощущений могут порыскать Си. Заодно понадобится
эмулятор, я рекомендую bgb по той причине, что он просто
удобен. Вот так выглядит отладчик:

Добавьте еще инструмент VRAM viewer, и выбор эмулятора очевиден. Для
разработки.
.bat для сборки выглядит так:
set Nam=plasma
@echo off
REM SIMPLE COMMAND.COM SCRIPT TO ASSEMBLE GAMEBOY FILES
REM REQUIRES MAKELNK.BAT
REM JOHN HARRISON
REM UPDATED 2008-01-28
del %Nam%.gb
del %Nam%.obj
command /c makelnk %Nam% > %Nam%.link
:begin
set assemble=1
echo assembling...
rgbasm95 -o%Nam%.obj %Nam%.asm
if errorlevel 1 goto end
echo linking...
xlink95 -mmap %Nam%.link
if errorlevel 1 goto end
echo fixing...
rgbfix95 -v %Nam%
del %Nam%.obj
bgb %Nam%.gb
:end
pause
и файл plasma.link
#autocreated Linkfile
#
#
[Objects]
plasma.obj
#
[Output]
plasma.gb
Программа должна размещаться с адреса 0 или с адреса $100
с адреса 0 размешаются векторы прерываний - LCD,timer и другие
с адреса $100 находится код программы, обычно указывается как
NOP:JP START
с адреса $104 хранится логотип Nintendo, без него ROM просто не
запустится. Например, вот так:
SECTION "Vblank",HOME[$0040]
jp vblank_v ; $40
reti
SECTION "LCDC",HOME[$0048]
jp LCDC_v ; $48
reti
SECTION "Timer_Overflow",HOME[$0050]
reti
SECTION "Serial",HOME[$0058]
reti
SECTION "p1thru4",HOME[$0060]
reti
SECTION "start",HOME[$0100]
db 0
;---------------------------------------------------------------------------
jp start_th
;---------------------------------------------------------------------------
; "Nintendo" Character Data
DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D
DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
DB$BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E
; Game Title
db "GB-THING "
; "123456789012345" <- Title must be exactly that long, in caps
db $C0; Colour Compatibility Code ($80 : yes, $00 : no)
db $C0
db $58; Maker Code
db $58; Game Unit Code(00=Gameboy, 03=Super Gameboy functions
db 0; Cartridge type:
db 0; Rom Size:
db 0; External Ram Size:
db 1; Destination code (0 - Japanese, 1 - Non-Japanese
db 1; Old Licensee code (33 - Check Maker Code)
db $33
db $76
db $31
db $6E
дополнительное данные указаны не совсем верно, заголовок был взят с
одной старой демы.
Теперь возникает вопрос - а что делать? Прежде чем писать программу,
следует посмотреть на карту памяти:
$FFFF |
флаг запрета прерываний
|
|
$FF80-$FFFE |
Zero Page - 127 bytes |
|
$FF00-$FF7F |
регистры ввода-вывода(порты)
|
|
$FEA0-$FEFF |
не используется
|
|
$FE00-$FE9F |
OAM - спрайты |
|
$E000-$FDFF |
Echo RAM - Reserved, Do Not Use |
|
$D000-$DFFF |
Internal RAM - Bank 1-7 (switchable - CGB
only) |
|
$C000-$CFFF |
Internal RAM - Bank 0 (fixed) |
|
$A000-$BFFF |
Cartridge RAM (If Available) |
|
$9C00-$9FFF |
BG Map Data 2 -атрибуты тайлов |
|
$9800-$9BFF |
BG Map Data 1 -атрибуты тайлов
|
|
$8000-$97FF |
RAM тайлов
|
|
$4000-$7FFF |
ROM картириджа - переключаемые банки
|
|
$0150-$3FFF |
ROM картриджа - Bank 0 (fixed) |
|
$0100-$014F |
Заголовок картриджа
|
|
$0000-$00FF |
Вектора прерываний
|
Весь экран покрыт тайлами 32х32, каждый тайл описывается 8х8 точек
из 4х цветов, формат данных задается следующим образом:

таким образом на один из тайлов нужно 16 байт. Экран захватывает
20х18 тайлов, смещение по карте определяется
ячейками памяти $FF42(SCY) и $FF43(SCX).
и еще один нюанс - нельзя сразу записывать в видеопамять, нужен
следующий фрагмент кода:
lcd_WaitVRAM: MACRO
ld a,[$FF41] ; <---+
and 2;STATF_BUSY ; |
jr nz,@-4 ; ----+
ENDM
А как сделать эту запись в память быстро? для этого есть DMA.
Например:
DMA_trans:;D.E=Src.Dest,C=count
xor a
ld [$FF52], a ; DMAsrc lo
ld [$FF54], a ; DMA dest lo
ld a, d
ld [$FF51], a ; DMAsrc hi
ld a, e
ld [$FF53], a ; DMA dest hi
ld a, c;$FF;(16*256)/$10-1
ld [$FF55], a ; DMA count val*$10-1
wait_dma:
ldh a,[$55]
and $80
jr z,wait_dma
ret
wait_dma это цикл ожидания готовности, в спецификации(эта ссылка
полезна:http://nocash.emubase.de/pandocs.htm)
описывается другой способ:
ld a,28h ;delay...
wait: ;total 5x40 cycles, approx 200ms
dec a ;1 cycle
jr nz,wait ;4 cycles
мой способ работает.
Далее, по адресу $9800 хранятся атрибуты для тайлов в следующем
формате:
Bit 0-2 номер палитры (BGP0-7)
Bit 3 расположение тайла в VRAM
банке (0=Bank 0, 1=Bank 1)
Bit 4 не используется
Bit 5 Horizontal Flip(0=Normal, 1=Mirror
horizontally)
Bit 6 Vertical Flip(0=Normal, 1=Mirror
vertically)
Bit 7 приоритет тайлов и спрайтов.
банки VRAM переключаются ячейкой $FF4F, в банках хранятся различные
данные для тайлов по адресу $8000 и атрибуты по $9800
Раз упомянуты цвета, то стоит рассмотреть формат цвета и способ
записи
ld a,
$80;Bit 0-5 Index (00-3F), Bit 7 Auto Increment (0=Disabled,
1=Increment after Writing)
ld
[$FF68], a;bgpalsel
бит 7 используется для последовательной записи данных цвета в
$FF69
формат цвета следующий: xBBBBBGGGGGRRRRR, т.е. данные слова
записываются по очереди - младший байт и старший.
при создании демо я столкнулся со странной проблемой - запись цветов
не работала, решил выключением LCD и последовательным включением.
Опрос джойстика
выглядит процедура несколько странно:
ld a, $20
ld [$FF00], a
ld a, [$FF00]
ld a, [$FF00]
ld a, [$FF00]
ld a, [$FF00]
cpl
and $0F
jr z,joypad
значения описаны следущие:
Bit 7 - Not used
Bit 6 - Not used
Bit 5 - P15 Select Button Keys (0=Select)
Bit 4 - P14 Select Direction Keys (0=Select)
Bit 3 - P13 Input Down or Start (0=Pressed) (Read Only)
Bit 2 - P12 Input Up or Select (0=Pressed) (Read Only)
Bit 1 - P11 Input Left or Button B (0=Pressed) (Read Only)
Bit 0 - P10 Input Right or Button A (0=Pressed) (Read Only)
Спрайты
спрайты создаются из тайлов, существует несколько ограничений:
-40 спрайтов
-не больше 10 спрайтов на одну линию(scanline)
Как было описано, хранятся данные по адресу $FE00, формат следующий:
координатаX,координатаY,номер тайла,флаги
iq: Specifications
Bit7 OBJ-to-BG Priority (0=OBJ Above BG, 1=OBJ Behind BG color 1-3)
(Used for both BG and Window. BG color 0 is always behind OBJ)
Bit6 Y flip (0=Normal, 1=Vertically mirrored)
Bit5 X flip (0=Normal, 1=Horizontally mirrored)
Bit4 Palette number **Non CGB Mode Only** (0=OBP0, 1=OBP1)
Bit3 Tile VRAM-Bank **CGB Mode Only** (0=Bank 0, 1=Bank 1)
Bit2-0 Palette number **CGB Mode Only** (OBP0-7)
|
цвета задаются похожим образом, как описано для палитр, используются
$FF6A и $FF6B
осталось упомянуть о Sprite Bug, который я не заметил.Суть его в
том, что при использовании INC rr/DEC rr(rr-BC,DE,HL) память
спрайта
забивается мусором.
Заодно по аналогии с картой тайлов записать данные нельзя,
используется следующая процедура:
ld hl,$0FF41;-STAT Register
wait1: ;\
bit 1,[hl] ; Wait
until Mode is -NOT- 0 or 1
jr z,wait1 ;/
wait2: ;\
bit 1,[hl] ; Wait until Mode 0 or 1 -BEGINS-
jr nz,wait2 ;/
Избавиться от подобных заморочек поможет только DMA, отошлю по
ссылке, которую я привел.
И последнее, что необходимо - конфигурация LCD, которая задается
адресом $FF40. Для тренировки предлагаю разобрать флаги в значении:
ld a, $D1
ld [$FF40], a;lcd ctrl
На этом мое повествование заканчивается, домашним заданием останется
изучение прерываний($FFFF) и звуков.
Утилит для gbc существует не так уж и много, хотя
последние события дали еще несколько программ - Tile Buddy для
графики и
gbt-player (https://github.com/AntonioND/gbt-player)