Программирование 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)