ref: 4bc2564a6b217ef269c7c763b98fc4fb41967a97
parent: 479c18920e610a5bb6c2b5b78ca00db56cf4cad5
author: yenatch <[email protected]>
date: Sat Feb 2 02:14:56 EST 2013
VBlank and dependencies VBlank has seven different modes. The one that sees the most use is mode 0, which performs all basic display and audio functions. The purpose of the others is unknown. VBlank relies on the following functions: -joypad -cgb palette update -dmg -> cgb pal conversion -bg map update -vram tile update -tile animations -serial request -game timer Tile animation is large enough to warrant its own commit.
--- a/main.asm
+++ b/main.asm
@@ -35,7 +35,7 @@
rst $38
SECTION "vblank",HOME[$40] ; vblank interrupt
- jp $0283
+ jp VBlank
SECTION "lcd",HOME[$48] ; lcd interrupt
jp $0552
@@ -56,8 +56,542 @@
SECTION "start",HOME[$150]
-INCBIN "baserom.gbc",$150,$45a - $150
+INCBIN "baserom.gbc",$150,$283 - $150
+VBlank: ; 283
+ push af
+ push bc
+ push de
+ push hl
+
+; get vblank type
+ ld a, [$ff9e]
+ and $7
+
+; get fn pointer
+ ld e, a
+ ld d, $0
+ ld hl, .VBlanks
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+
+; down to business
+ call JpHl
+
+; since this is called once per frame
+ call GameTimer
+
+ pop hl
+ pop de
+ pop bc
+ pop af
+ reti
+; 2a1
+
+.VBlanks ; 2a1
+ dw VBlank0 ; 0
+ dw VBlank1 ; 1
+ dw VBlank2 ; 2
+ dw VBlank3 ; 3
+ dw VBlank4 ; 4
+ dw VBlank5 ; 5
+ dw VBlank6 ; 6
+ dw VBlank0 ; 7
+; 2b1
+
+
+VBlank0: ; 2b1
+; normal operation
+
+; rng
+; scx, scy, wy, wx
+; bg map buffer
+; palettes
+; dma transfer
+; bg map
+; tiles
+; oam
+; joypad
+; sound
+
+; inc frame counter
+ ld hl, $ff9b
+ inc [hl]
+
+; advance rng
+ ld a, [$ff04] ; divider
+ ld b, a
+ ld a, [$ffe1]
+ adc b
+ ld [$ffe1], a
+
+ ld a, [$ff04] ; divider
+ ld b, a
+ ld a, [$ffe2]
+ sbc b
+ ld [$ffe2], a
+
+; save bank
+ ld a, [$ff9d] ; current bank
+ ld [$ff8a], a
+
+; scroll x
+ ld a, [$ffcf]
+ ld [$ff43], a ; scx
+; scroll y
+ ld a, [$ffd0]
+ ld [$ff42], a ; scy
+; window y
+ ld a, [$ffd2]
+ ld [$ff4a], a ; wy
+; window x + 7
+ ld a, [$ffd1]
+ ld [$ff4b], a ; wx
+
+; some time management is in order
+; only have time for one of these during vblank
+
+; bg map buffer has priority
+ call UpdateBGMapBuffer
+ jr c, .doneframeaction
+
+; then pals
+ call UpdatePalsIfCGB
+ jr c, .doneframeaction
+
+; dma transfer
+ call DMATransfer
+ jr c, .doneframeaction
+
+; bg map
+ call UpdateBGMap
+
+; these have their own timing checks
+ call SafeLoadTiles
+ call SafeLoadTiles2
+ call SafeTileAnimation
+
+.doneframeaction
+; oam update off?
+ ld a, [$ffd8]
+ and a
+ jr nz, .vblankoccurred
+
+; update oam by dma transfer
+ call $ff80
+; 403f:
+; ld a, $c4
+; ld [$ff46], a ; oam dma
+; ld a, $28
+; .loop
+; dec a
+; jr nz, .loop
+; ret
+
+
+; vblank-sensitive operations are done
+
+.vblankoccurred
+; tell other fns vblank happened
+ xor a
+ ld [VBlankOccurred], a
+
+; dec $cfb1 until 0
+ ld a, [$cfb1]
+ and a
+ jr z, .textdelay
+ dec a
+ ld [$cfb1], a
+
+.textdelay
+; dec text delay counter until 0
+ ld a, [TextDelayFrames]
+ and a
+ jr z, .joypad
+ dec a
+ ld [TextDelayFrames], a
+
+.joypad
+ call Joypad
+
+; update sound
+ ld a, BANK(UpdateSound)
+ rst Bankswitch ; bankswitch
+ call UpdateSound
+ ld a, [$ff8a]
+ rst Bankswitch ; restore bank
+
+;
+ ld a, [$ff98]
+ ld [$ffe3], a
+
+ ret
+; 325
+
+
+VBlank2: ; 325
+; sound only
+
+; save bank
+ ld a, [$ff9d]
+ ld [$ff8a], a
+
+; update sound
+ ld a, BANK(UpdateSound)
+ rst Bankswitch ; bankswitch
+ call UpdateSound
+
+; restore bank
+ ld a, [$ff8a]
+ rst Bankswitch
+
+; tell other fns vblank happened
+ xor a
+ ld [VBlankOccurred], a
+ ret
+; 337
+
+
+VBlank1: ; 337
+; scx, scy
+; palettes
+; bg map
+; tiles
+; oam
+; sound / lcd stat
+
+; save bank
+ ld a, [$ff9d]
+ ld [$ff8a], a
+
+; scroll x
+ ld a, [$ffcf]
+ ld [$ff43], a ; scx
+
+; scroll y
+ ld a, [$ffd0]
+ ld [$ff42], a ; scy
+
+; time-sensitive fns
+ call UpdatePals
+ jr c, .vblankoccurred
+
+; these have their own timing checks
+ call UpdateBGMap
+ call LoadTiles
+; update oam by dma transfer
+ call $ff80
+; 403f:
+; ld a, $c4
+; ld [$ff46], a ; oam dma
+; ld a, $28
+; .loop
+; dec a
+; jr nz, .loop
+; ret
+
+.vblankoccurred
+; tell other fns vblank happened
+ xor a
+ ld [VBlankOccurred], a
+
+; get requested ints
+ ld a, [$ff0f] ; IF
+ ld b, a
+; discard requested ints
+ xor a
+ ld [$ff0f], a ; IF
+; enable lcd stat
+ ld a, %10 ; lcd stat
+ ld [$ffff], a ; IE
+; rerequest serial int if applicable (still disabled)
+; request lcd stat
+ ld a, b
+ and %1000 ; serial
+ or %10 ; lcd stat
+ ld [$ff0f], a ; IF
+
+ ei
+; update sound
+ ld a, BANK(UpdateSound)
+ rst Bankswitch ; bankswitch
+ call UpdateSound
+; restore bank
+ ld a, [$ff8a]
+ rst Bankswitch
+ di
+
+; get requested ints
+ ld a, [$ff0f] ; IF
+ ld b, a
+; discard requested ints
+ xor a
+ ld [$ff0f], a ; IF
+; enable ints besides joypad
+ ld a, %1111 ; serial timer lcdstat vblank
+ ld [$ffff], a ; IE
+; rerequest ints
+ ld a, b
+ ld [$ff0f], a ; IF
+ ret
+; 37f
+
+
+UpdatePals: ; 37f
+; update pals for either dmg or cgb
+
+; check cgb
+ ld a, [$ffe6]
+ and a
+ jp nz, UpdateCGBPals
+
+; update gb pals
+ ld a, [$cfc7]
+ ld [$ff47], a ; BGP
+
+ ld a, [$cfc8]
+ ld [$ff48], a ; OBP0
+
+ ld a, [$cfc9]
+ ld [$ff49], a ; 0BP1
+
+ and a
+ ret
+; 396
+
+
+VBlank3: ; 396
+; scx, scy
+; palettes
+; bg map
+; tiles
+; oam
+; sound / lcd stat
+
+; save bank
+ ld a, [$ff9d]
+ ld [$ff8a], a
+
+; scroll x
+ ld a, [$ffcf]
+ ld [$ff43], a ; scx
+; scroll y
+ ld a, [$ffd0]
+ ld [$ff42], a ; scy
+
+; any pals to update?
+ ld a, [$ffe5]
+ and a
+ call nz, ForceUpdateCGBPals
+ jr c, .vblankoccurred
+; else
+ call UpdateBGMap
+ call LoadTiles
+
+; update oam by dma transfer
+ call $ff80
+; 403f:
+; ld a, $c4 ; Sprites / $100
+; ld [$ff46], a ; oam dma
+; ld a, $28
+; .loop
+; dec a
+; jr nz, .loop
+; ret
+
+.vblankoccurred
+; tell other fns vblank happened
+ xor a
+ ld [VBlankOccurred], a
+
+; save int flag
+ ld a, [$ff0f] ; IF
+ push af
+; reset ints
+ xor a
+ ld [$ff0f], a ; IF
+; force lcdstat int during sound update
+ ld a, %10 ; lcd stat
+ ld [$ffff], a ; IE
+ ld [$ff0f], a ; IF
+
+ ei
+; update sound
+ ld a, BANK(UpdateSound)
+ rst Bankswitch ; bankswitch
+ call UpdateSound
+; restore bank
+ ld a, [$ff8a]
+ rst Bankswitch
+ di
+
+; request lcdstat
+ ld a, [$ff0f] ; IF
+ ld b, a
+; and any other ints
+ pop af
+ or b
+ ld b, a
+; reset ints
+ xor a
+ ld [$ff0f], a ; IF
+; enable ints besides joypad
+ ld a, %1111 ; serial timer lcdstat vblank
+ ld [$ffff], a ; IE
+; request ints
+ ld a, b
+ ld [$ff0f], a ; IF
+ ret
+; 3df
+
+
+VBlank4: ; 3df
+; bg map
+; tiles
+; oam
+; joypad
+; serial
+; sound
+
+; save bank
+ ld a, [$ff9d]
+ ld [$ff8a], a
+
+ call UpdateBGMap
+ call SafeLoadTiles
+
+; update oam by dma transfer
+ call $ff80
+; 403f:
+; ld a, $c4
+; ld [$ff46], a ; oam dma
+; ld a, $28
+; .loop
+; dec a
+; jr nz, .loop
+; ret
+
+; update joypad
+ call Joypad
+
+; tell other fns vblank happened
+ xor a
+ ld [VBlankOccurred], a
+
+; handshake
+ call AskSerial
+
+; update sound
+ ld a, BANK(UpdateSound)
+ rst Bankswitch ; bankswitch
+ call UpdateSound
+; restore bank
+ ld a, [$ff8a]
+ rst Bankswitch
+ ret
+; 400
+
+
+VBlank5: ; 400
+; scx
+; palettes
+; bg map
+; tiles
+; joypad
+;
+
+; save bank
+ ld a, [$ff9d]
+ ld [$ff8a], a
+
+; scroll x
+ ld a, [$ffcf]
+ ld [$ff43], a ; scx
+
+; if we can update pals, skip this part
+ call UpdatePalsIfCGB
+ jr c, .vblankoccurred
+
+ call UpdateBGMap
+ call SafeLoadTiles
+
+.vblankoccurred
+; tell other fns vblank happened
+ xor a
+ ld [VBlankOccurred], a
+
+; joypad
+ call Joypad
+
+; discard requested ints
+ xor a
+ ld [$ff0f], a ; IF
+; enable lcd stat
+ ld a, %10 ; lcd stat
+ ld [$ffff], a ; IE
+; request lcd stat
+ ld [$ff0f], a ; IF
+
+ ei
+; update sound
+ ld a, BANK(UpdateSound)
+ rst Bankswitch ; bankswitch
+ call UpdateSound
+; restore bank
+ ld a, [$ff8a]
+ rst Bankswitch
+ di
+
+; discard requested ints
+ xor a
+ ld [$ff0f], a ; IF
+; enable ints besides joypad
+ ld a, %1111 ; serial timer lcdstat vblank
+ ld [$ffff], a ; IE
+ ret
+; 436
+
+
+VBlank6: ; 436
+; palettes
+; tiles
+; dma transfer
+; sound
+
+; save bank
+ ld a, [$ff9d]
+ ld [$ff8a], a
+
+; inc frame counter
+ ld hl, $ff9b
+ inc [hl]
+
+ call UpdateCGBPals
+ jr c, .vblankoccurred
+
+ call SafeLoadTiles
+ call SafeLoadTiles2
+ call DMATransfer
+
+.vblankoccurred
+; tell other fns vblank happened
+ xor a
+ ld [VBlankOccurred], a
+
+; update sound
+ ld a, BANK(UpdateSound)
+ rst Bankswitch ; bankswitch
+ call UpdateSound
+; restore bank
+ ld a, [$ff8a]
+ rst Bankswitch
+ ret
+; 45a
+
+
DelayFrame: ; 0x45a
; delay for one frame
ld a, $1
@@ -369,10 +903,8 @@
ret
; 658
-
INCBIN "baserom.gbc",$658,$691 - $658
-
SetClock: ; 691
; set clock data from hram
@@ -421,8 +953,88 @@
ret
; 6c4
-INCBIN "baserom.gbc",$6c4,$984 - $6c4
+INCBIN "baserom.gbc",$6c4,$935 - $6c4
+Joypad: ; 935
+; update joypad state
+; $ffa2: released
+; $ffa3: pressed
+; $ffa4: input
+; $ffa5: total pressed
+
+;
+ ld a, [$cfbe]
+ and $d0
+ ret nz
+
+; pause game update?
+ ld a, [$c2cd]
+ and a
+ ret nz
+
+; d-pad
+ ld a, $20
+ ld [$ff00], a
+ ld a, [$ff00]
+ ld a, [$ff00]
+; hi nybble
+ cpl
+ and $f
+ swap a
+ ld b, a
+
+; buttons
+ ld a, $10
+ ld [$ff00], a
+; wait to stabilize
+ ld a, [$ff00]
+ ld a, [$ff00]
+ ld a, [$ff00]
+ ld a, [$ff00]
+ ld a, [$ff00]
+ ld a, [$ff00]
+; lo nybble
+ cpl
+ and $f
+ or b
+ ld b, a
+
+; reset joypad
+ ld a, $30
+ ld [$ff00], a
+
+; get change in input
+ ld a, [$ffa4] ; last frame's input
+ ld e, a
+ xor b ; current frame input
+ ld d, a
+; released
+ and e
+ ld [$ffa2], a
+; pressed
+ ld a, d
+ and b
+ ld [$ffa3], a
+
+; total pressed
+ ld c, a
+ ld a, [$ffa5]
+ or c
+ ld [$ffa5], a
+
+; original input
+ ld a, b
+ ld [$ffa4], a
+
+; A+B+SELECT+START
+ and $f
+ cp $f
+ jp z, $0150 ; reset
+
+ ret
+; 984
+
+
GetJoypadPublic: ; 984
; update mirror joypad input from $ffa4 (real input)
@@ -588,8 +1200,137 @@
ret
; a1b
-INCBIN "baserom.gbc",$a1b,$c9f - $a1b
+INCBIN "baserom.gbc",$a1b,$c2f - $a1b
+UpdatePalsIfCGB: ; c2f
+; update bgp data from BGPals
+; update obp data from OBPals
+; return carry if successful
+
+; check cgb
+ ld a, [$ffe6]
+ and a
+ ret z
+
+UpdateCGBPals: ; c33
+; return carry if successful
+; any pals to update?
+ ld a, [$ffe5]
+ and a
+ ret z
+
+ForceUpdateCGBPals: ; c37
+; save wram bank
+ ld a, [$ff70] ; wram bank
+ push af
+; bankswitch
+ ld a, 5 ; BANK(BGPals)
+ ld [$ff70], a ; wram bank
+; get bg pal buffer
+ ld hl, BGPals ; 5:d080
+
+; update bg pals
+ ld a, %10000000 ; auto increment, index 0
+ ld [$ff68], a ; BGPI
+ ld c, $69 ; $ff69
+ ld b, 4 ; NUM_PALS / 2
+
+.bgp
+; copy 16 bytes (8 colors / 2 pals) to bgpd
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+; done?
+ dec b
+ jr nz, .bgp
+
+; hl is now 5:d0c0 OBPals
+
+; update obj pals
+ ld a, %10000000 ; auto increment, index 0
+ ld [$ff6a], a
+ ld c, $6b ; $ff6b - $ff00
+ ld b, 4 ; NUM_PALS / 2
+
+.obp
+; copy 16 bytes (8 colors / 2 pals) to obpd
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+ ld a, [hli]
+ ld [$ff00+c], a
+; done?
+ dec b
+ jr nz, .obp
+
+; restore wram bank
+ pop af
+ ld [$ff70], a ; wram bank
+; clear pal update queue
+ xor a
+ ld [$ffe5], a
+; successfully updated palettes
+ scf
+ ret
+; c9f
+
+
DmgToCgbBGPals: ; c9f
; exists to forego reinserting cgb-converted image data
@@ -1074,8 +1815,618 @@
pop hl
ret
-INCBIN "baserom.gbc",$135a,$185d - $135a
+INCBIN "baserom.gbc",$135a,$15d8 - $135a
+DMATransfer: ; 15d8
+; DMA transfer
+; return carry if successful
+
+; anything to transfer?
+ ld a, [$ffe8]
+ and a
+ ret z
+; start transfer
+ ld [$ff55], a ; hdma5
+; indicate that transfer has occurred
+ xor a
+ ld [$ffe8], a
+; successful transfer
+ scf
+ ret
+; 15e3
+
+
+UpdateBGMapBuffer: ; 15e3
+; write [$ffdc] 16x8 tiles from BGMapBuffer to bg map addresses in BGMapBufferPtrs
+; [$ffdc] must be even since this is done in 16x16 blocks
+
+; return carry if successful
+
+; any tiles to update?
+ ld a, [$ffdb]
+ and a
+ ret z
+; save wram bank
+ ld a, [$ff4f] ; vram bank
+ push af
+; save sp
+ ld [$ffd9], sp
+
+; temp stack
+ ld hl, BGMapBufferPtrs
+ ld sp, hl
+; we can now pop the addresses of affected spots in bg map
+
+; get pal and tile buffers
+ ld hl, BGMapPalBuffer
+ ld de, BGMapBuffer
+
+.loop
+; draw one 16x16 block
+
+; top half:
+
+; get bg map address
+ pop bc
+; update palettes
+ ld a, $1
+ ld [$ff4f], a ; vram bank
+; tile 1
+ ld a, [hli]
+ ld [bc], a
+ inc c
+; tile 2
+ ld a, [hli]
+ ld [bc], a
+ dec c
+; update tiles
+ ld a, $0
+ ld [$ff4f], a ; vram bank
+; tile 1
+ ld a, [de]
+ inc de
+ ld [bc], a
+ inc c
+; tile 2
+ ld a, [de]
+ inc de
+ ld [bc], a
+
+; bottom half:
+
+; get bg map address
+ pop bc
+; update palettes
+ ld a, $1
+ ld [$ff4f], a ; vram bank
+; tile 1
+ ld a, [hli]
+ ld [bc], a
+ inc c
+; tile 2
+ ld a, [hli]
+ ld [bc], a
+ dec c
+; update tiles
+ ld a, $0
+ ld [$ff4f], a ; vram bank
+; tile 1
+ ld a, [de]
+ inc de
+ ld [bc], a
+ inc c
+; tile 2
+ ld a, [de]
+ inc de
+ ld [bc], a
+
+; we've done 2 16x8 blocks
+ ld a, [$ffdc]
+ dec a
+ dec a
+ ld [$ffdc], a
+
+; if there are more left, get the next 16x16 block
+ jr nz, .loop
+
+
+; restore sp
+ ld a, [$ffd9]
+ ld l, a
+ ld a, [$ffda]
+ ld h, a
+ ld sp, hl
+
+; restore vram bank
+ pop af
+ ld [$ff4f], a ; vram bank
+
+; we don't need to update bg map until new tiles are loaded
+ xor a
+ ld [$ffdb], a
+
+; successfully updated bg map
+ scf
+ ret
+; 163a
+
+
+WaitTop: ; 163a
+ ld a, [$ffd4]
+ and a
+ ret z
+
+; wait until top third of bg map can be updated
+ ld a, [$ffd5]
+ and a
+ jr z, .quit
+
+ call DelayFrame
+ jr WaitTop
+
+.quit
+ xor a
+ ld [$ffd4], a
+ ret
+; 164c
+
+
+UpdateBGMap: ; 164c
+; get mode
+ ld a, [$ffd4]
+ and a
+ ret z
+
+; don't save bg map address
+ dec a ; 1
+ jr z, .tiles
+ dec a ; 2
+ jr z, .attr
+ dec a ; ?
+
+; save bg map address
+ ld a, [$ffd6]
+ ld l, a
+ ld a, [$ffd7]
+ ld h, a
+ push hl
+
+; bg map 1 ($9c00)
+ xor a
+ ld [$ffd6], a
+ ld a, $9c
+ ld [$ffd7], a
+
+; get mode again
+ ld a, [$ffd4]
+ push af
+ cp 3
+ call z, .tiles
+ pop af
+ cp 4
+ call z, .attr
+
+; restore bg map address
+ pop hl
+ ld a, l
+ ld [$ffd6], a
+ ld a, h
+ ld [$ffd7], a
+ ret
+
+.attr
+; switch vram banks
+ ld a, 1
+ ld [$ff4f], a ; vram bank
+; bg map 1
+ ld hl, AttrMap
+ call .getthird
+; restore vram bank
+ ld a, 0
+ ld [$ff4f], a ; vram bank
+ ret
+
+.tiles
+; bg map 0
+ ld hl, TileMap
+
+.getthird
+; save sp
+ ld [$ffd9], sp
+
+; # tiles to move down * 6 (which third?)
+ ld a, [$ffd5]
+ and a ; 0
+ jr z, .top
+ dec a ; 1
+ jr z, .middle
+
+; .bottom ; 2
+; move 12 tiles down
+ ld de, $00f0 ; TileMap(0,12) - TileMap
+ add hl, de
+; stack now points to source
+ ld sp, hl
+; get bg map address
+ ld a, [$ffd7]
+ ld h, a
+ ld a, [$ffd6]
+ ld l, a
+; move 12 tiles down
+ ld de, $0180 ; bgm(0,12)
+ add hl, de
+; start at top next time
+ xor a
+ jr .start
+
+.middle
+; move 6 tiles down
+ ld de, $0078 ; TileMap(0,6) - TileMap
+ add hl, de
+; stack now points to source
+ ld sp, hl
+; get bg map address
+ ld a, [$ffd7]
+ ld h, a
+ ld a, [$ffd6]
+ ld l, a
+; move 6 tiles down
+ ld de, $00c0 ; bgm(0,6)
+ add hl, de
+; start at bottom next time
+ ld a, 2
+ jr .start
+
+.top
+; stack now points to source
+ ld sp, hl
+; get bg map address
+ ld a, [$ffd7]
+ ld h, a
+ ld a, [$ffd6]
+ ld l, a
+; start at middle next time
+ ld a, 1
+
+.start
+; which third to draw next update
+ ld [$ffd5], a
+; # rows per third
+ ld a, 6 ; SCREEN_HEIGHT / 3
+; # tiles from the edge of the screen to the next row
+ ld bc, $000d ; BG_WIDTH + 1 - SCREEN_WIDTH
+
+.row
+; write a row of 20 tiles
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+; next row
+ add hl, bc
+; done?
+ dec a
+ jr nz, .row
+
+; restore sp
+ ld a, [$ffd9]
+ ld l, a
+ ld a, [$ffda]
+ ld h, a
+ ld sp, hl
+ ret
+; 170a
+
+
+SafeLoadTiles2: ; 170a
+; only execute during first fifth of vblank
+; any tiles to draw?
+ ld a, [$cf6c]
+ and a
+ ret z
+; abort if too far into vblank
+ ld a, [$ff44] ; LY
+; ly = 144-145?
+ cp 144
+ ret c
+ cp 146
+ ret nc
+
+GetTiles2: ; 1717
+; load [$cf6c] tiles from [$cf6d-e] to [$cf6f-70]
+; save sp
+ ld [$ffd9], sp
+
+; sp = [$cf6d-e] tile source
+ ld hl, $cf6d
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld sp, hl
+
+; hl = [$cf6f-70] tile dest
+ ld hl, $cf6f
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+
+; # tiles to draw
+ ld a, [$cf6c]
+ ld b, a
+
+; clear tile queue
+ xor a
+ ld [$cf6c], a
+
+.loop
+; put 1 tile (16 bytes) into hl from sp
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+; next tile
+ inc hl
+; done?
+ dec b
+ jr nz, .loop
+
+; update $cf6f-70
+ ld a, l
+ ld [$cf6f], a
+ ld a, h
+ ld [$cf70], a
+
+; update $cf6d-e
+ ld [$cf6d], sp
+
+; restore sp
+ ld a, [$ffd9]
+ ld l, a
+ ld a, [$ffda]
+ ld h, a
+ ld sp, hl
+ ret
+; 1769
+
+
+SafeLoadTiles: ; 1769
+; only execute during first fifth of vblank
+; any tiles to draw?
+ ld a, [$cf67]
+ and a
+ ret z
+; abort if too far into vblank
+ ld a, [$ff44] ; LY
+; ly = 144-145?
+ cp 144
+ ret c
+ cp 146
+ ret nc
+ jr GetTiles
+
+LoadTiles: ; 1778
+; use only if time is allotted
+; any tiles to draw?
+ ld a, [$cf67]
+ and a
+ ret z
+; get tiles
+
+GetTiles: ; 177d
+; load [$cf67] tiles from [$cf68-9] to [$cf6a-b]
+
+; save sp
+ ld [$ffd9], sp
+
+; sp = [$cf68-9] tile source
+ ld hl, $cf68
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld sp, hl
+
+; hl = [$cf6a-b] tile dest
+ ld hl, $cf6a
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+
+; # tiles to draw
+ ld a, [$cf67]
+ ld b, a
+; clear tile queue
+ xor a
+ ld [$cf67], a
+
+.loop
+; put 1 tile (16 bytes) into hl from sp
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+; next tile
+ inc hl
+; done?
+ dec b
+ jr nz, .loop
+
+; update $cf6a-b
+ ld a, l
+ ld [$cf6a], a
+ ld a, h
+ ld [$cf6b], a
+
+; update $cf68-9
+ ld [$cf68], sp
+
+; restore sp
+ ld a, [$ffd9]
+ ld l, a
+ ld a, [$ffda]
+ ld h, a
+ ld sp, hl
+ ret
+; 17d3
+
+
+SafeTileAnimation: ; 17d3
+; call from vblank
+
+ ld a, [$ffde]
+ and a
+ ret z
+
+; abort if too far into vblank
+ ld a, [$ff44] ; LY
+; ret unless ly = 144-150
+ cp 144
+ ret c
+ cp 151
+ ret nc
+
+; save affected banks
+; switch to new banks
+ ld a, [$ff9d]
+ push af ; save bank
+ ld a, BANK(DoTileAnimation)
+ rst Bankswitch ; bankswitch
+
+ ld a, [$ff70] ; wram bank
+ push af ; save wram bank
+ ld a, $1 ; wram bank 1
+ ld [$ff70], a ; wram bank
+
+ ld a, [$ff4f] ; vram bank
+ push af ; save vram bank
+ ld a, $0 ; vram bank 0
+ ld [$ff4f], a ; vram bank
+
+; take care of tile animation queue
+ call DoTileAnimation
+
+; restore affected banks
+ pop af
+ ld [$ff4f], a ; vram bank
+ pop af
+ ld [$ff70], a ; wram bank
+ pop af
+ rst Bankswitch ; bankswitch
+ ret
+; 17ff
+
+INCBIN "baserom.gbc",$17ff,$185d - $17ff
+
GetTileType: ; 185d
; checks the properties of a tile
; input: a = tile id
@@ -1099,8 +2450,178 @@
ret
; 1875
-INCBIN "baserom.gbc",$1875,$261f - $1875
+INCBIN "baserom.gbc",$1875,$2063 - $1875
+AskSerial: ; 2063
+; send out a handshake while serial int is off
+ ld a, [$c2d4]
+ bit 0, a
+ ret z
+
+ ld a, [$c2d5]
+ and a
+ ret nz
+
+; once every 6 frames
+ ld hl, $ca8a
+ inc [hl]
+ ld a, [hl]
+ cp 6
+ ret c
+
+ xor a
+ ld [hl], a
+
+ ld a, $c
+ ld [$c2d5], a
+
+; handshake
+ ld a, $88
+ ld [$ff01], a
+
+; switch to internal clock
+ ld a, %00000001
+ ld [$ff02], a
+
+; start transfer
+ ld a, %10000001
+ ld [$ff02], a
+
+ ret
+; 208a
+
+INCBIN "baserom.gbc",$208a,$209e - $208a
+
+GameTimer: ; 209e
+; precautionary
+ nop
+
+; save wram bank
+ ld a, [$ff70] ; wram bank
+ push af
+
+ ld a, $1
+ ld [$ff70], a ; wram bank
+
+ call UpdateGameTimer
+
+; restore wram bank
+ pop af
+ ld [$ff70], a ; wram bank
+ ret
+; 20ad
+
+
+UpdateGameTimer: ; 20ad
+; increment the game timer by one frame
+; capped at 999:59:59.00 after exactly 1000 hours
+
+; pause game update?
+ ld a, [$c2cd]
+ and a
+ ret nz
+
+; game timer paused?
+ ld hl, GameTimerPause
+ bit 0, [hl]
+ ret z
+
+; reached cap? (999:00:00.00)
+ ld hl, GameTimeCap
+ bit 0, [hl]
+ ret nz
+
+; increment frame counter
+ ld hl, GameTimeFrames ; frame counter
+ ld a, [hl]
+ inc a
+
+; reached 1 second?
+ cp 60 ; frames/second
+ jr nc, .second ; 20c5 $2
+
+; update frame counter
+ ld [hl], a
+ ret
+
+.second
+; reset frame counter
+ xor a
+ ld [hl], a
+
+; increment second counter
+ ld hl, GameTimeSeconds
+ ld a, [hl]
+ inc a
+
+; reached 1 minute?
+ cp 60 ; seconds/minute
+ jr nc, .minute
+
+; update second counter
+ ld [hl], a
+ ret
+
+.minute
+; reset second counter
+ xor a
+ ld [hl], a
+
+; increment minute counter
+ ld hl, GameTimeMinutes
+ ld a, [hl]
+ inc a
+
+; reached 1 hour?
+ cp 60 ; minutes/hour
+ jr nc, .hour
+
+; update minute counter
+ ld [hl], a
+ ret
+
+.hour
+; reset minute counter
+ xor a
+ ld [hl], a
+
+; increment hour counter
+ ld a, [GameTimeHours]
+ ld h, a
+ ld a, [GameTimeHours+1]
+ ld l, a
+ inc hl
+
+; reached 1000 hours?
+ ld a, h
+ cp $3 ; 1000 / $100
+ jr c, .updatehr
+
+ ld a, l
+ cp $e8 ; 1000 & $ff
+ jr c, .updatehr
+
+; cap at 999:59:59.00
+ ld hl, GameTimeCap
+ set 0, [hl] ; stop timer
+
+ ld a, 59
+ ld [GameTimeMinutes], a
+ ld [GameTimeSeconds], a
+
+; this will never be run again
+ ret
+
+.updatehr
+ ld a, h
+ ld [GameTimeHours], a
+ ld a, l
+ ld [GameTimeHours+1], a
+ ret
+; 210f
+
+INCBIN "baserom.gbc",$210f,$261f - $210f
+
PushScriptPointer: ; 261f
; used to call a script from asm
; input:
@@ -1496,8 +3017,12 @@
ret
; 2fec
-INCBIN "baserom.gbc",$2fec,$300b-$2fec
+JpHl: ; 2fec
+ jp [hl]
+; 2fed
+INCBIN "baserom.gbc",$2fed,$300b-$2fed
+
ClearSprites: ; 300b
ld hl, Sprites
ld b, TileMap - Sprites
@@ -2399,7 +3924,7 @@
ret
; 3e32
-INCBIN "baserom.gbc",$3e32,$4000 - $3e32
+INCBIN "baserom.gbc",$3e32,$3fb5 - $3e32
SECTION "bank1",DATA,BANK[$1]
@@ -89885,6 +91410,8 @@
INCBIN "baserom.gbc",$FBCCF,$fc000-$fbccf
SECTION "bank3F",DATA,BANK[$3F]
+
+DoTileAnimation:
INCBIN "baserom.gbc",$FC000,$fcdc2-$fc000