gb-tarot/main.asm
2025-04-25 20:24:12 -04:00

591 lines
19 KiB
NASM

; infinite thanks to:
; gbdev.io and the pandocs for everything
; alex for consulting on tarot endlessly
; moss for keeping me from working sixteen hours a day and burning out
; yuri for letting me bounce ideas off you at all times
; sara for being a perfect light
; fae for all your help on music and sound and sunday mornings
; sadie for hardware and packaging help
def MY_OAM equ $c000
; $c100 - c120 call handles, scene stack, interrupt, card functions
; 0xc100 CALL
; 0xc101 LOW - SCENE_SETUP points to this
; 0xc102 HIGH
; 0xc103 RET
; 0xc104 CALL ...
DEF SCENE_SETUP EQU $c101
DEF SCENE_UPDATE EQU SCENE_SETUP + 4 ; call then ret is 3+1 bytes
DEF SCENE_DRAW EQU SCENE_UPDATE + 4
DEF SCENE_TEARDOWN EQU SCENE_DRAW + 4
DEF INTERRUPT_LCD EQU SCENE_TEARDOWN + 4
def CARD_INIT equ INTERRUPT_LCD + 4
def CARD_UPDATE equ CARD_INIT + 4
def CARD_DRAW equ CARD_UPDATE + 4
println "Card Draw is ", SCENE_DRAW
; each of these sections is way bgger than it needs to be
; i doubt any of them will hold more than $20 bytes at all
; but might as well put them at round numbers for now
def ASYNC_VARS_START equ $c200 ; this space's layout defined manually in async.inc
def SYSTEM_VARS_START equ $c300 ; system variables like buttons pressed, rng, dtime
def GLOBAL_VARS_START equ $c400 ; program-wide state defined mostly in mainmenu
def SCREEN_VARS_START equ $c500 ; per-screen variables like animation stuff
def CARD_HELPER_VARS_START equ $c600 ; variables for card data
def CARD_VARS_START equ $c700 ; variables for animation of individual cards
def CVS equ CARD_VARS_START ; handy to be able to refer to CVS by a short name
def AUDIO_VARS_START equ $c800 ; variables for the audio subsystem
def PRINTER_VARS_START equ $c900
def SHUFFLED_DECK equ $ca00 ; location for the shuffled deck
pushs "work spaces", WRAMX[$D000]
ZEROES: ds $200
ONES: ds $200
BUFFER_ONE: ds $300
BUFFER_TWO: ds $300
pops
; system variables, which are like program-wide state vars but More Different
PUSHS "System Variables", WRAM0[SYSTEM_VARS_START]
rMYBTN: db ; EQU SYSTEM_VARS_START
rMYBTNP: db ;EQU rMYBTN + 1
rDELTAT: db ; EQU rMYBTNP + 1 ; delta_t where $1000 = 1 second
rLFSR: dw ; equ rDELTAT + 1 ; 16 bit
POPS
def SAFE_DMA_LOCATION equ $ffc0
def VARIABLE_TILES_START equ 26 ; where in VRAM the variable tiles start
; (i.e. we allocate VARIABLE_TILES_START-1 slots out of that block of 128
; for ever-present ui tiles)
INCLUDE "hardware.inc"
SECTION "Interrupts", ROM0[$0]
ds INT_HANDLER_VBLANK - @, 0
reti
ds INT_HANDLER_STAT - @, 0
call INTERRUPT_LCD - 1
ret
SECTION "Header", ROM0[$100]
jp EntryPoint
ds $147 - @, 0 ; skip to the mbc byte
db $01 ; MBC1 with no ram or battery :)
db $01 ; 64kb rom :)
ds $150 - @, 0 ; Make room for the header
EntryPoint:
; Shut down audio circuitry
ld a, 0
ldh [rNR52], a ; shut down audio
ldh [rDIV], a ; reset timer just in case??
ldh [rLCDC], a ; shut down screen
ldh [rIE], a
ldh [rIF], a
ld a, 0
ld hl, ZEROES
ld bc, $200
.buildZeros
ld a, 0
ld [hl+], a
ld a, b
or a, c
dec bc ; we still need to do the `or` to set the zero flag bc dec r16 doesn't do that
jp nz, .buildZeros
ld a, 1
ld hl, ONES
ld bc, $200
.buildOnes
ld a, 1
ld [hl+], a
ld a, b
or a, c
dec bc ; we still need to do the `or` to set the zero flag bc dec r16 doesn't do that
jp nz, .buildOnes
ld hl, UITiles
ld de, _VRAM + $1000
ld bc, UITiles.end - UITiles
call CopyRange
ld hl, LetterTiles ; map the small font into vram at ascii locations
ld de, _VRAM + $800 + ($10)*16
ld bc, LetterTiles.end - LetterTiles
call CopyRange
; initialize global variables
ld a, 0
ld [vSelectedSpreadIndex], a
ld [vSelectedCardIndex], a
ld hl, Cards
ld b, [hl]
ld hl, SHUFFLED_DECK
ld [hl], b
inc hl
.writeCard
ld [hl+], a
inc a
cp a, b
jr nz, .writeCard
ld a, %1010_1010
ld [rLFSR], a
; move on to framework stuff
; set up the scene and interrupt vectors
ld hl, Instructions
ld a, [hl] ; get the value of a call instruction so we can shove it into our handles
ld hl, SCENE_SETUP - 1
ld [hl], a
ld hl, SCENE_UPDATE - 1
ld [hl], a
ld hl, SCENE_DRAW - 1
ld [hl], a
ld hl, SCENE_TEARDOWN - 1
ld [hl], a
ld hl, INTERRUPT_LCD - 1
ld [hl], a
ld hl, CARD_INIT - 1
ld [hl], a
ld hl, CARD_UPDATE - 1
ld [hl], a
ld hl, CARD_DRAW - 1
ld [hl], a
ld hl, Instructions
inc hl
ld a, [hl] ; get the value of a call instruction so we can shove it into our handles
ld hl, SCENE_SETUP
ld [hl], a
ld hl, SCENE_UPDATE
ld [hl], a
ld hl, SCENE_DRAW
ld [hl], a
ld hl, SCENE_TEARDOWN
ld [hl], a
ld hl, INTERRUPT_LCD
ld [hl], a
ld hl, CARD_INIT
ld [hl], a
ld hl, CARD_UPDATE
ld [hl], a
ld hl, CARD_DRAW
ld [hl], a
ld hl, Instructions
inc hl
inc hl
ld a, [hl] ; get the value of a call instruction so we can shove it into our handles
ld hl, SCENE_SETUP + 1
ld [hl], a
ld hl, SCENE_UPDATE + 1
ld [hl], a
ld hl, SCENE_DRAW + 1
ld [hl], a
ld hl, SCENE_TEARDOWN + 1
ld [hl], a
ld hl, INTERRUPT_LCD + 1
ld [hl], a
ld hl, CARD_INIT + 1
ld [hl], a
ld hl, CARD_UPDATE + 1
ld [hl], a
ld hl, CARD_DRAW + 1
ld [hl], a
ld hl, Instructions
inc hl
inc hl
inc hl
ld a, [hl] ; get the value of a ret instruction so we can shove it into our handles
ld hl, SCENE_SETUP + 2
ld [hl], a
ld hl, SCENE_UPDATE + 2
ld [hl], a
ld hl, SCENE_DRAW + 2
ld [hl], a
ld hl, SCENE_TEARDOWN + 2
ld [hl], a
ld hl, INTERRUPT_LCD + 2
ld [hl], a
ld hl, CARD_INIT + 2
ld [hl], a
ld hl, CARD_UPDATE + 2
ld [hl], a
ld hl, CARD_DRAW + 2
ld [hl], a
; set up our scene handle
ld hl, ScreenMainMenu
call ChangeScene
call SoundSetup
call PrepNetwork
Loop:
; okay this is kinda sketchy. we want a delta time variable.
; we've got two eight-bit counters, one at 4096hz and one at 16384hz
; we can definitely reset the faster counter.
; but! that's gonna overflow every frame.
; however! it should always overflow the same amount
; because we're basically running once per frame (AwaitLine call later)
; if we assume it overflows once per frame, then the total value should be
; 256 + [rDIV]. so if we divide by two (or more!) the total value will be
; 256/2 + [rDIV]/2. so we just have to right shift and add 128.
; or right shift twice and add 64. this loses us precision but we didn't need
; it anyway.
ldh a, [rDIV]
ldh [rDIV], a ; this will reset the timer
sra a
sra a
add a, 64
ld [rDELTAT], a
xor a, a ; zero a out in one cycle
; store dpad and btn state in the rMYBTN register
ld hl, rP1
ld [hl], P1F_GET_DPAD
ld a, [hl]
ld a, [hl]
ld a, [hl]
ld a, [hl]
cpl
and a, %00001111
ld b, a
ld [hl], P1F_GET_BTN
ld a, [hl]
ld a, [hl]
ld a, [hl]
ld a, [hl]
cpl
and a, %00001111
swap a
or a, b
ld b, a ; put new input state in b
ld a, [rMYBTN] ; previous input state in a
cpl ; a holds buttons which weren't pressed last frame
and a, b ; select buttons which were pressed this frame, but not last frame
ld [rMYBTNP], a ; save that as btnp state
ld a, b ; select buttons which were pressed this frame
ld [rMYBTN], a ; save that as btn state
call SoundUpdate
println "scene update is ", SCENE_UPDATE - 1
call SCENE_UPDATE - 1 ; hope this takes not too many scanlines!
di
ld a, [rIE]
or a, IEF_VBLANK
ld [rIE], a
ei
halt
println "scene draw is ", SCENE_DRAW - 1
call SCENE_DRAW - 1 ; hope this takes fewer than 9 scanlines!
; either way it's going to eat into the update timing
; at this point we want to make sure that scanline 153 has passed
; we should check if we're past there and skip this await if necessary
ld b, 5
call AwaitLine
jp Loop
ChangeScene: ; hl should be a pointer to, in sequence, setup update draw teardown
;call SCENE_TEARDOWN - 1
ld a, [hl+]
ld [SCENE_SETUP], a
ld a, [hl+]
ld [SCENE_SETUP+1], a
ld a, [hl+]
ld [SCENE_UPDATE], a
ld a, [hl+]
ld [SCENE_UPDATE+1], a
ld a, [hl+]
ld [SCENE_DRAW], a
ld a, [hl+]
ld [SCENE_DRAW+1], a
ld a, [hl+]
ld [SCENE_TEARDOWN], a
ld a, [hl+]
ld [SCENE_TEARDOWN+1], a
call SCENE_SETUP - 1
ret
AwaitLine: ; put the line you want to reach in b
ld a, [rLY]
cp b
jp nz, AwaitLine
ret
ArrayClampLoopingB:
cp a, b
jp nz, :+
ld a, 0
:
cp a, $FF
jp nz, :+
ld a, b
dec a
:
ret
ArrayClampLooping: ; loops a to be in the array, assuming hl points to the length
cp a, [hl] ; if a == length...
jp nz, :+
ld a, 0 ; set it to 0
:
cp a, $FF ; otherwise if a == $FF...
jp nz, :+
ld a, [hl]
dec a ; then set it to length-1
:
ret ; a is return value
ArrayClamp:
cp a, $FF
jp nz, :+
ld a, 0
ret
: cp a, [hl]
ret c
: ld a, [hl]
dec a
ret
:
PassList: ; hl has the address of a list. step past it.
ld b, 0
ld c, [hl] ; bc has length
inc hl ; step over length
add hl, bc ; step past all the entries
ret
PrintString: ; write ascii string which has been prefixed by its length. at hl
; de should be location in tile map to write
ld b, [hl]
ld a, b
cp a, 0
ret z
inc hl
PrintBChars: ;write ascii characters. will not respect newlines or anything like that
; hl should be the source of ascii text
; de should be the location in the tile map to start writing at
; b should be the length
ld a, [hli]
or a, %10000000
sub a, $10
ld [de], a
inc de
dec b
jp nz, PrintBChars
ret
;thanks to https://gbdev.io/pandocs/OAM_DMA_Transfer.html for this routine
; a should be the high byte of where we're DMAing from (ld a, $c0 in my case probably)
; de should have the address in hram for where to paste to (SAFE_DMA_LOCATION probably)
RunDMA:
di
push de
push af
ld hl, run_dma_tail
ld bc, run_dma_tail.run_dma_tail_end - run_dma_tail
call CopyRange
pop af
run_dma: ; This part must be in ROM.
ld bc, $2846 ; B: wait time; C: LOW($FF46)
ret ; we pushed de before, so this should "return" to execute that (HRAM) address
run_dma_tail: ; This part must live in HRAM.
ldh [c], a
.wait
dec b
jr nz, .wait
ei
ret z ; Conditional `ret` is 1 M-cycle slower, which avoids reading from the
; stack on the last M-cycle of DMA.
.run_dma_tail_end
nop
nop
nop
halt
nop
nop
Instructions:
call .rett
.rett:
ret
EndOfInstructions:
UITiles:
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
db $05,$00,$05,$00,$05,$00,$05,$00,$05,$00,$fd,$00,$00,$00,$fd,$00
db $00,$ff,$ff,$ff,$ff,$ff,$ff,$00,$00,$ff,$00,$00,$00,$00,$00,$00
db $70,$e8,$70,$e8,$70,$e8,$70,$e8,$70,$e8,$70,$e8,$70,$e8,$70,$e8
db $0e,$17,$0e,$17,$0e,$17,$0e,$17,$0e,$17,$0e,$17,$0e,$17,$0e,$17
db $00,$00,$00,$00,$00,$00,$00,$ff,$ff,$00,$ff,$ff,$ff,$ff,$00,$ff
db $70,$e8,$70,$e8,$70,$e8,$70,$ef,$7f,$f0,$3f,$7f,$1f,$3f,$00,$1f
db $0e,$17,$0e,$17,$0e,$17,$0e,$f7,$fe,$0f,$fc,$fe,$f8,$fc,$00,$f8
db $00,$f8,$f8,$fc,$fc,$fe,$fe,$0f,$0e,$f7,$0e,$17,$0e,$17,$0e,$17
db $00,$1f,$1f,$3f,$3f,$7f,$7f,$f0,$70,$ef,$70,$e8,$70,$e8,$70,$e8
db $00,$00,$ff,$00,$ff,$00,$aa,$55,$00,$ff,$00,$ff,$ff,$ff,$ff,$ff
db $63,$1f,$73,$0f,$63,$1f,$73,$0f,$63,$1f,$73,$0f,$63,$1f,$73,$0f
db $ce,$f0,$c6,$f8,$ce,$f0,$c6,$f8,$ce,$f0,$c6,$f8,$ce,$f0,$c6,$f8
db $ff,$ff,$ff,$ff,$00,$ff,$00,$ff,$55,$aa,$ff,$00,$ff,$00,$00,$00
db $00,$00,$3f,$00,$7f,$00,$7a,$05,$68,$17,$70,$0f,$63,$1f,$73,$0f
db $00,$00,$fc,$00,$fe,$00,$ae,$50,$1e,$e0,$06,$f8,$ce,$f0,$c6,$f8
db $ce,$f0,$c6,$f8,$0e,$f0,$16,$e8,$5e,$a0,$fe,$00,$fc,$00,$00,$00
db $63,$1f,$73,$0f,$60,$1f,$78,$07,$75,$0a,$7f,$00,$3f,$00,$00,$00
db $00,$77,$00,$88,$00,$88,$00,$88,$00,$88,$00,$88,$00,$88,$00,$77
db $00,$00,$00,$80,$00,$80,$00,$80,$00,$80,$00,$80,$00,$80,$00,$00
db $00,$77,$00,$88,$20,$a8,$20,$a8,$20,$a8,$20,$a8,$00,$88,$00,$77
db $00,$77,$00,$88,$02,$8a,$02,$8a,$02,$8a,$02,$8a,$00,$88,$00,$77
db $56,$c3,$6a,$c3,$56,$c3,$6a,$c3,$56,$c3,$6a,$c3,$7e,$ff,$00,$ff
db $00,$ff,$7e,$ff,$56,$c3,$6a,$c3,$56,$c3,$6a,$c3,$56,$c3,$6a,$c3
db $99,$5a,$99,$5a,$99,$5a,$99,$5a,$99,$5a,$81,$42,$81,$7e,$ff,$00
db $ff,$00,$81,$7e,$81,$42,$99,$5a,$99,$5a,$99,$5a,$99,$5a,$99,$5a
.end
LetterTiles:
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
db $03,$03,$07,$07,$0f,$0f,$0e,$0e,$1c,$1c,$30,$30,$c0,$c0,$c0,$c0
db $24,$24,$6c,$6c,$6c,$6c,$48,$48,$00,$00,$00,$00,$00,$00,$00,$00
db $1a,$1a,$1a,$1a,$7f,$7f,$34,$34,$34,$34,$fe,$fe,$68,$68,$68,$68
db $08,$08,$3c,$3c,$6a,$6a,$68,$68,$3c,$3c,$0a,$0a,$6a,$6a,$3c,$3c
db $63,$63,$a6,$a6,$ac,$ac,$d8,$d8,$1b,$1b,$35,$35,$65,$65,$c6,$c6
db $18,$18,$24,$24,$28,$28,$18,$18,$2c,$2c,$66,$66,$67,$67,$39,$39
db $18,$18,$18,$18,$18,$18,$30,$30,$00,$00,$00,$00,$00,$00,$00,$00
db $04,$04,$0c,$0c,$18,$18,$10,$10,$10,$10,$18,$18,$0c,$0c,$04,$04
db $20,$20,$30,$30,$18,$18,$08,$08,$08,$08,$18,$18,$30,$30,$20,$20
db $00,$00,$08,$08,$28,$28,$1e,$1e,$78,$78,$14,$14,$10,$10,$00,$00
db $00,$00,$00,$00,$18,$18,$18,$18,$7e,$7e,$18,$18,$18,$18,$00,$00
db $00,$00,$00,$00,$00,$00,$00,$00,$0c,$0c,$0c,$0c,$0c,$0c,$18,$18
db $00,$00,$00,$00,$00,$00,$1c,$1c,$38,$38,$00,$00,$00,$00,$00,$00
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$18,$18,$18,$18,$00,$00
db $04,$04,$0c,$0c,$0c,$0c,$18,$18,$18,$18,$30,$30,$30,$30,$20,$20
db $00,$00,$38,$38,$64,$64,$64,$64,$64,$64,$64,$64,$64,$64,$38,$38
db $08,$08,$18,$18,$38,$38,$18,$18,$18,$18,$18,$18,$18,$18,$3c,$3c
db $00,$00,$38,$38,$4c,$4c,$0c,$0c,$0c,$0c,$18,$18,$30,$30,$7c,$7c
db $00,$00,$3c,$3c,$06,$06,$06,$06,$1c,$1c,$06,$06,$06,$06,$3c,$3c
db $00,$00,$04,$04,$26,$26,$26,$26,$3e,$3e,$06,$06,$06,$06,$04,$04
db $00,$00,$7c,$7c,$64,$64,$60,$60,$78,$78,$44,$44,$04,$04,$38,$38
db $00,$00,$20,$20,$30,$30,$30,$30,$3c,$3c,$32,$32,$32,$32,$1c,$1c
db $00,$00,$7c,$7c,$4c,$4c,$0c,$0c,$3e,$3e,$18,$18,$30,$30,$20,$20
db $00,$00,$78,$78,$64,$64,$64,$64,$38,$38,$64,$64,$64,$64,$3c,$3c
db $00,$00,$38,$38,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$0c,$0c,$18,$18
db $00,$00,$00,$00,$18,$18,$18,$18,$00,$00,$18,$18,$18,$18,$00,$00
db $00,$00,$00,$00,$0c,$0c,$0c,$0c,$00,$00,$0c,$0c,$0c,$0c,$18,$18
db $00,$00,$0c,$0c,$18,$18,$30,$30,$60,$60,$30,$30,$18,$18,$0c,$0c
db $00,$00,$00,$00,$3c,$3c,$3c,$3c,$00,$00,$3c,$3c,$3c,$3c,$00,$00
db $00,$00,$30,$30,$18,$18,$0c,$0c,$06,$06,$0c,$0c,$18,$18,$30,$30
db $38,$38,$6c,$6c,$24,$24,$0c,$0c,$18,$18,$18,$18,$00,$00,$18,$18
db $3c,$3c,$62,$62,$59,$59,$45,$45,$5d,$5d,$5d,$5d,$63,$63,$38,$38
db $00,$00,$38,$38,$34,$34,$32,$32,$3e,$3e,$32,$32,$32,$32,$32,$32
db $00,$00,$7c,$7c,$32,$32,$32,$32,$3c,$3c,$32,$32,$32,$32,$1c,$1c
db $00,$00,$3c,$3c,$72,$72,$62,$62,$60,$60,$60,$60,$62,$62,$3c,$3c
db $00,$00,$f8,$f8,$64,$64,$62,$62,$62,$62,$62,$62,$62,$62,$3c,$3c
db $00,$00,$fc,$fc,$60,$60,$60,$60,$78,$78,$60,$60,$60,$60,$3c,$3c
db $00,$00,$fc,$fc,$60,$60,$60,$60,$78,$78,$60,$60,$60,$60,$20,$20
db $00,$00,$78,$78,$64,$64,$60,$60,$60,$60,$6e,$6e,$64,$64,$3c,$3c
db $00,$00,$40,$40,$62,$62,$62,$62,$7e,$7e,$62,$62,$62,$62,$22,$22
db $00,$00,$10,$10,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$08,$08
db $00,$00,$08,$08,$0c,$0c,$0c,$0c,$0c,$0c,$4c,$4c,$6c,$6c,$3c,$3c
db $00,$00,$40,$40,$64,$64,$68,$68,$70,$70,$68,$68,$64,$64,$22,$22
db $00,$00,$20,$20,$30,$30,$30,$30,$30,$30,$30,$30,$32,$32,$1e,$1e
db $00,$00,$3e,$3e,$6a,$6a,$6a,$6a,$6a,$6a,$62,$62,$62,$62,$22,$22
db $00,$00,$72,$72,$6a,$6a,$6a,$6a,$6a,$6a,$66,$66,$62,$62,$22,$22
db $00,$00,$3c,$3c,$66,$66,$62,$62,$62,$62,$62,$62,$32,$32,$1e,$1e
db $00,$00,$f8,$f8,$64,$64,$64,$64,$7c,$7c,$60,$60,$60,$60,$20,$20
db $00,$00,$38,$38,$64,$64,$62,$62,$62,$62,$6a,$6a,$64,$64,$3b,$3b
db $00,$00,$f8,$f8,$64,$64,$64,$64,$7c,$7c,$68,$68,$64,$64,$23,$23
db $00,$00,$38,$38,$64,$64,$60,$60,$38,$38,$04,$04,$64,$64,$38,$38
db $00,$00,$7e,$7e,$5a,$5a,$18,$18,$18,$18,$18,$18,$18,$18,$08,$08
db $00,$00,$40,$40,$62,$62,$62,$62,$62,$62,$62,$62,$62,$62,$3c,$3c
db $00,$00,$40,$40,$62,$62,$62,$62,$62,$62,$24,$24,$34,$34,$18,$18
db $00,$00,$c3,$c3,$62,$62,$62,$62,$6a,$6a,$6a,$6a,$6a,$6a,$34,$34
db $00,$00,$42,$42,$62,$62,$34,$34,$18,$18,$3c,$3c,$66,$66,$42,$42
db $00,$00,$4c,$4c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$4c,$4c,$78,$78
db $00,$00,$7e,$7e,$46,$46,$0c,$0c,$18,$18,$30,$30,$62,$62,$7e,$7e
db $7c,$7c,$60,$60,$60,$60,$60,$60,$60,$60,$60,$60,$60,$60,$7c,$7c
db $20,$20,$30,$30,$30,$30,$18,$18,$18,$18,$0c,$0c,$0c,$0c,$04,$04
db $3e,$3e,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$3e,$3e
db $18,$18,$3c,$3c,$66,$66,$42,$42,$00,$00,$00,$00,$00,$00,$00,$00
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$7c,$7c,$3e,$3e
db $30,$30,$18,$18,$0c,$0c,$04,$04,$00,$00,$00,$00,$00,$00,$00,$00
db $00,$00,$00,$00,$38,$38,$0c,$0c,$3c,$3c,$4c,$4c,$3a,$3a,$00,$00
db $00,$00,$30,$30,$30,$30,$3c,$3c,$32,$32,$32,$32,$3c,$3c,$00,$00
db $00,$00,$00,$00,$1c,$1c,$30,$30,$30,$30,$30,$30,$1c,$1c,$00,$00
db $00,$00,$0c,$0c,$0c,$0c,$3c,$3c,$4c,$4c,$4c,$4c,$3c,$3c,$00,$00
db $00,$00,$00,$00,$38,$38,$64,$64,$78,$78,$60,$60,$3c,$3c,$00,$00
db $00,$00,$0e,$0e,$18,$18,$3c,$3c,$18,$18,$18,$18,$18,$18,$00,$00
db $00,$00,$00,$00,$3c,$3c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$38,$38
db $00,$00,$60,$60,$60,$60,$78,$78,$64,$64,$64,$64,$64,$64,$00,$00
db $00,$00,$10,$10,$00,$00,$38,$38,$18,$18,$18,$18,$3c,$3c,$00,$00
db $00,$00,$08,$08,$00,$00,$0c,$0c,$0c,$0c,$0c,$0c,$2c,$2c,$18,$18
db $00,$00,$60,$60,$68,$68,$68,$68,$70,$70,$68,$68,$64,$64,$00,$00
db $00,$00,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$18,$18,$00,$00
db $00,$00,$00,$00,$74,$74,$6a,$6a,$6a,$6a,$6a,$6a,$6a,$6a,$00,$00
db $00,$00,$00,$00,$78,$78,$64,$64,$64,$64,$64,$64,$64,$64,$00,$00
db $00,$00,$00,$00,$38,$38,$64,$64,$64,$64,$64,$64,$38,$38,$00,$00
db $00,$00,$00,$00,$78,$78,$64,$64,$64,$64,$78,$78,$60,$60,$60,$60
db $00,$00,$00,$00,$3c,$3c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$0e,$0e
db $00,$00,$00,$00,$1c,$1c,$30,$30,$30,$30,$30,$30,$30,$30,$00,$00
db $00,$00,$00,$00,$38,$38,$60,$60,$38,$38,$0c,$0c,$78,$78,$00,$00
db $00,$00,$30,$30,$78,$78,$30,$30,$30,$30,$30,$30,$1c,$1c,$00,$00
db $00,$00,$00,$00,$64,$64,$64,$64,$64,$64,$64,$64,$3c,$3c,$00,$00
db $00,$00,$00,$00,$64,$64,$64,$64,$64,$64,$28,$28,$10,$10,$00,$00
db $00,$00,$00,$00,$62,$62,$6a,$6a,$6a,$6a,$6a,$6a,$34,$34,$00,$00
db $00,$00,$00,$00,$64,$64,$38,$38,$18,$18,$2c,$2c,$46,$46,$00,$00
db $00,$00,$00,$00,$4c,$4c,$4c,$4c,$4c,$4c,$3c,$3c,$0c,$0c,$38,$38
db $00,$00,$00,$00,$7e,$7e,$0c,$0c,$18,$18,$30,$30,$7e,$7e,$00,$00
db $3c,$3c,$60,$60,$30,$30,$60,$60,$60,$60,$30,$30,$60,$60,$3c,$3c
db $20,$20,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$10,$10
db $3c,$3c,$06,$06,$0c,$0c,$06,$06,$06,$06,$0c,$0c,$06,$06,$3c,$3c
db $00,$00,$00,$00,$31,$31,$7b,$7b,$de,$de,$8c,$8c,$00,$00,$00,$00
db $c0,$c0,$a0,$a0,$b8,$b8,$b0,$b0,$da,$da,$12,$12,$1a,$1a,$03,$03
.end
INCLUDE "CopyRange.inc"
INCLUDE "CopyTiles.inc"
INCLUDE "Async.inc"
INCLUDE "Random.inc"
INCLUDE "RecreatingCards.inc"
INCLUDE "GraphicsManipulation.inc"
INCLUDE "Printing.inc"
INCLUDE "ScreenMainMenu.inc"
INCLUDE "ScreenSpreadSelect.inc"
INCLUDE "CardHelpers.inc"
INCLUDE "ScreenCardRead.inc"
INCLUDE "ScreenCardBrowse.inc"
INCLUDE "ScreenShuffle.inc"
INCLUDE "CardLibrary.inc"
INCLUDE "Audio.inc"