;==========================================================================================
;
;   UNOFFICIAL SNES POWERPAK FIRMWARE V2.02 (CODENAME: "SIMBA")
;   (c) 2012/2013 by ManuLöwe (http://www.manuloewe.de/)
;
;	*** MAIN BOOTLOADER CODE ***
;	Code in this file based on v1.0X code written by:
;	- bunnyboy (SNES PowerPak creator), (c) 2009
;
;==========================================================================================



LoadRomVersion:
	ClearLine 9

	SetCursorPos 9, 8
	PrintString "Firmware v2.02 \"SIMBA\""
rts



Main:

; ************************** Initialize SNES ***************************

	sei					; disable interrupts
	clc
	xce					; switch to native mode

	rep #$38				; mem/A = 16 bit, X/Y = 16 bit, decimal mode off

	ldx #$1FFF				; set up the stack
	txs

	jsl InitSNES				; continue initialization in separate routine

	rep #AXY_8BIT				; A/X/Y = 16 bit



; ************************** Build jump table **************************

	ldy #$0000

JumpTableLoop:
	lda JumpTableStart, y
	sta jumpTable, y
	iny
	iny
	cpy #$0080				; 128 bytes = 64 functions
	bne JumpTableLoop

	sep #A_8BIT				; A = 8 bit

	bra JumpTableEnd

JumpTableStart:					; add RAM jumptable
	.DW CardReadSector
	.DW CardWriteSector
	.DW Forever
	.DW CardReset
	.DW CardWaitNotBusy
	.DW CardWaitReady
	.DW CardWaitDataReq
	.DW CardCheckError
	.DW CardLoadLBA
	.DW CardReadBytesNoDMA
	.DW CardReadBytesToWRAM
	.DW CardReadBytesToFPGA
	.DW CardReadBytesToSDRAM
	.DW CardReadBytesToSDRAMNoDMA
	.DW CardReadFile
	.DW CardWriteFile
	.DW CardWriteBytesFromWRAM
	.DW CardWriteBytesFromSDRAM
	.DW CardLoadModule
	.DW CardLoadDirClearEntryName
	.DW CardLoadDir
	.DW ClusterToLBA
	.DW NextCluster
	.DW DirPrintDir
	.DW DirPrintEntry
	.DW DirGetEntry
	.DW DirFindEntry
	.DW NextDir
	.DW PrintF
	.DW PrintInt8_noload
	.DW PrintHex8_noload
	.DW PrintClearLine
	.DW PrintClearScreen
	.DW DoScrolling
	.DW ScrollUp
	.DW ScrollDown
	.DW LoadNextSectorNum
	.DW CardLoadFPGA
	.DW ClearFindEntry
	.DW GameGeniePrint
	.DW GameGenieClearCode
	.DW GameGenieDecode
	.DW GameGenieNextChar
	.DW GameGeniePrevChar
	.DW GameGenieClearAll
	.DW GameGenieWriteCode
	.DW LoadDevMusic
	.DW LoadLogo
	.DW LoadRomVersion
	.DW MemCheck
	.DW CopyROMInfo
	.DW CopyBanks
	.DW PrintBanks
	.DW LogScreen
	.DW SWCHeaderCheck
	.DW GD3HeaderCheck
	.DW WaitHblank
	.DW spcPlay
	.DW spcStop
	.DW spcProcess
	.DW spcSetModuleVolume			; 61 functions, max = 64

JumpTableEnd:



	jsr SpriteInit				; set up the sprite buffer

	sep #A_8BIT				; 8 bit Accumulator, 16 bit X/Y
	rep #XY_8BIT

	jsr QuickSetup				; set VRAM, video mode, background and character pointers,
						; and turn on the screen

	sep #A_8BIT				; 8 bit Accumulator, 16 bit X/Y
	rep #XY_8BIT

	jsr JoyInit				; initialize joypads and enable NMI

	sep #A_8BIT				; 8 bit Accumulator, 16 bit X/Y
	rep #XY_8BIT

	jump jLoadRomVersion



; ************************ Begin CF interaction ************************

	rep #A_8BIT				; A = 16 bit

	stz gameName.gCluster			; clear out cluster info
	stz gameName.gCluster+2

	stz saveName.sCluster
	stz saveName.sCluster+2

	sep #A_8BIT				; A = 8 bit

	jump jCardReset

	wai

StateCardNotInserted:				; wait until card inserted, show no card message
	lda CARDSTATUS
	sta errorCode
	and #%11110000				; open bus = A0
	cmp #$A0
	beq StateCardNotInsertedLoop
	jmp StateCardNotInsertedDone

StateCardNotInsertedLoop:
	wai
	wai
	wai
	ClearLine 10
	SetCursorPos 10, 6
	PrintString "Error $"
	PrintHexNum errorCode
	PrintString " - CF card not found"

ShowConsoleVersion:
	lda $4210				; CPU revision
	and #$0F
	sta temp
	lda $213E				; PPU1 revision
	and #$0F
	sta temp+1
	lda $213F				; PPU2 revision
	and #$0F
	sta temp+2

	SetCursorPos 12,23			; print chipset revision
	PrintString "CPU: "
	PrintHexNum temp

	SetCursorPos 13,22
	PrintString " PPU1: "
	PrintHexNum temp+1

	SetCursorPos 14,22
	PrintString " PPU2: "
	PrintHexNum temp+2

	lda #$01				; switch to standard blue HDMA background
	sta configBGdesign

;	lda #$00				; logo tile difference ($00 = PowerPak, $7C = Simba, $84 = SFC dots)
	stz temp
;	lda #$00				; logo palette diff. ($00 = PowerPak, $02 = Simba, $04 = SFC dots)
	stz temp+1
;	lda #$00				; standard PowerPak cursor
	stz temp+2
	lda #$06				; standard PowerPak cursor palette
	sta temp+3

	jump jLoadLogo				; show logo



NoCard:
	jump jCardReset

	lda CARDSTATUS
	and #%11110000				; open bus = A0
	cmp #$A0
	beq NoCard

StateCardNotInsertedDone:
	ClearLine 12				; hide chipset revision
	ClearLine 13
	ClearLine 14



StateCardBusy:					; wait until card not busy, show card busy message
	lda CARDSTATUS
	sta errorCode
	and #%10000000
	cmp #%10000000				; busy = $80
	bne StateCardBusyDone
	wai
	ClearLine 10
	SetCursorPos 10, 7
	PrintString " Error $"
	PrintHexNum errorCode
	PrintString " - CF card busy"
	bra StateCardBusy

StateCardBusyDone:



StateCardReady:					; wait until card ready, show card not ready message
	lda CARDSTATUS
	sta errorCode
	and #%01010000
	cmp #%01010000				; ready = $50
	beq StateCardReadyDone
	wai
	ClearLine 10
	SetCursorPos 10, 6
	PrintString "Error $"
	PrintHexNum errorCode
	PrintString " - CF card not ready"
	bra StateCardReady

StateCardReadyDone:



	ClearLine 10
	SetCursorPos 10, 10
	PrintString "CF card booted"

	lda #$01				; not using DMA
	sta dontUseDMA

;	jsr Delay5Sec				; for debugging


;;;;
;; set some features
;  jump jCardWaitNotBusy
;  jump jCardWaitReady
;  jump jCardCheckError

;  SetCursorPos 12, 0
;  PrintString " card ready     "

;;select disk 0
;  lda #%00000000
;  sta CARDLBA3             ;select card 0

;  SetCursorPos 12, 0
;  PrintString " card 0 selected"

;; send feature = 01 - 8 bit transfers
;  lda #$01
;  sta CARDERROR
;  lda #$EF
;  sta CARDCOMMAND

;  SetCursorPos 12, 0
;  PrintString " feature written"

;  jump jCardCheckError



;  lda #$10
;  sta temp+5      ;;try copying 16 times
;StateCardBooted:
;  lda temp+5
;  sta temp+6
;StateCardBootedWait:
;  dec temp+6
;  bne StateCardBootedWait  ;;try to shift by some clocks
;  stz sourceSector
;  stz sourceSector+1
;  stz sourceSector+2
;  stz sourceSector+3
;  lda #<sectorBuffer1
;  sta destLo
;  lda #>sectorBuffer1
;  sta destHi
;  stz destBank
;  lda #kDestWRAM
;  sta destType
;  stz sectorBuffer1+$1FE
;  stz sectorBuffer1+$1FF
;  jump jCardReadSector      ;read sector 0 to internal ram
;  lda sectorBuffer1+$1FE
;  cmp #$55
;  bne DMAError              ;last word check  should be $55AA
;  lda sectorBuffer1+$1FF
;  cmp #$AA
;  bne DMAError              ;last word check  should be $55AA
;  dec temp+5
;  bne StateCardBooted
;  jmp CardFormatCheck
;DMAError:
;  lda #$01
;  sta dontUseDMA
;  ClearLine 3
;  SetCursorPos 3, 1
;  PrintString "DMA OFF\n"



	stz sourceSector
	stz sourceSector+1
	stz sourceSector+2
	stz sourceSector+3

	lda #<sectorBuffer1
	sta destLo
	lda #>sectorBuffer1
	sta destHi
	stz destBank

	lda #kDestWRAM				; try with DMA on
	sta destType

	jump jCardReadSector			; read sector 0 to internal RAM

	lda sectorBuffer1+$1FE			; last word check, should be $55AA
	cmp #$55
	beq CardFormatCheck			; ### FIXME

; jmp CardFormatError

CardFormatCheck:
	lda sectorBuffer1+$1FF
	cmp #$AA
	bne CardFormatErrorJump

	ldy #$0000				; y = partition number  0-3
	ldx #$0000				; x = partition index   0, 16, 32, 48

CardFormatCheckLoop:
	lda #$01
	sta fat32Enabled

	lda sectorBuffer1+$1C2, x		; format  fat32 = 0x0B   FAT32
	cmp #$0B
	beq CardGoodFormat

	lda sectorBuffer1+$1C2, x		; format  fat32 = 0x0C   FAT32 LBA
	cmp #$0C
	beq CardGoodFormat

	stz fat32Enabled

	lda sectorBuffer1+$1C2, x		; format  fat16 = 0x06  larger than 32MB
	cmp #$06
	beq CardGoodFormat

	lda sectorBuffer1+$1C2, x		; format  fat16 = 0x04  smaller than 32MB
	cmp #$04
	beq CardGoodFormat

	lda sectorBuffer1+$1C2, x		; format  fat16 = 0x0E  with LBA
	cmp #$0E
	beq CardGoodFormat

	inx
	inx
	inx
	inx

	inx
	inx
	inx
	inx

	inx
	inx
	inx
	inx

	inx
	inx
	inx
	inx

	iny
	cpy #$0004
	bne CardFormatCheckLoop

CardFormatErrorJump:
	jmp CardFormatError



CardGoodFormat:					; FAT32/FAT16 partition found
	stx partitionIndex

	ClearLine 10
	SetCursorPos 10, 10
	PrintString "FAT32/16 found"

;	jsr Delay5Sec				; for debugging

	ldx partitionIndex
	ldy #$0000

CardCopyPartitionLBABegin:			; copy partitionLBABegin from offset 455
	lda sectorBuffer1+$1C6, x
	sta sourceSector, y
	sta partitionLBABegin, y
	inx
	iny
	cpy #$0004
	bne CardCopyPartitionLBABegin

	lda #<sectorBuffer1
	sta destLo
	lda #>sectorBuffer1
	sta destHi
	stz destBank
	lda #kDestWRAM
	sta destType

	jump jCardReadSector			; read FAT16/FAT32 Volume ID sector  (partition boot record)

	lda sectorBuffer1+$0D			; copy FAT16/FAT32 sectorsPerCluster from offset 13
	sta sectorsPerCluster

	lda sectorBuffer1+$0E			; copy FAT16/FAT32 reservedSectors from offset 14
	sta reservedSectors

	lda sectorBuffer1+$0F
	sta reservedSectors+1

	ldx #$0000

CardCopySectorsPerFat:				; copy FAT32 sectorsPerFat from offset 36
	lda sectorBuffer1+$24, x
	sta sectorsPerFat, x
	inx
	cpx #$0004
	bne CardCopySectorsPerFat

	ldx #$0000

CardCopyRootDirCluster32:			; copy FAT32 rootDirCluster from offset 44
	lda sectorBuffer1+$2C, x
	sta rootDirCluster, x
	inx
	cpx #$0004
	bne CardCopyRootDirCluster32

	stz fat16RootSectors			; FAT32 no entry limit in root directory

	lda fat32Enabled
	cmp #$01
	beq CardCopyFatBeginLBA			; when FAT32, leave alone



;;;SPECIAL FAT16 handling

CardCopyRootDirCluster16:
	stz rootDirCluster
	stz rootDirCluster+1			; when FAT16, rootDirCluster = 0000 (root dir is not in cluster nums)
	stz rootDirCluster+2
	stz rootDirCluster+3

	lda sectorBuffer1+$16
	sta sectorsPerFat			; FAT16 copy sectorsPerFat from offset 22
	lda sectorBuffer1+$17
	sta sectorsPerFat+1
	stz sectorsPerFat+2
	stz sectorsPerFat+3			; FAT16 sectors per fat = 16 bits, in different place than FAT32


CardCopyRootDirEntries16:			; copy max root directory entries from offset 17
	lda sectorBuffer1+$11
	sta fat16RootSectors
	lda sectorBuffer1+$12
	sta fat16RootSectors+1

;  clc
;  lsr fat16RootSectors
;  lsr fat16RootSectors           ; FAT16 rootSectors =  (max root entries * 32) / 512   =   fat16RootSectors/16  ASR x4
;  lsr fat16RootSectors
;  lsr fat16RootSectors

	lda #$20
	sta fat16RootSectors			; FAT16 root dir fixed at 512 entries = 32 sectors



CardCopyFatBeginLBA:
	rep #A_8BIT				; A = 16 bit

	clc					; fatBeginLBA(4) = partitionLBABegin(4) + reservedSectors(2)
	lda reservedSectors
	adc partitionLBABegin
	sta fatBeginLBA

	lda partitionLBABegin+2
	adc #$0000
	sta fatBeginLBA+2

;CardCopyClusterBeginLBA:
	lda sectorsPerFat
	sta clusterBeginLBA

	lda sectorsPerFat+2
	sta clusterBeginLBA+2

	clc
	asl clusterBeginLBA			; clusterBeginLBA(4) = fatBeginLBA(4) + (2 * sectorPerFat(4))
	rol clusterBeginLBA+2

	sep #A_8BIT				; A = 8 bit

	clc					; FAT16 = 32 sectors       FAT32 = 0 sectors
	lda clusterBeginLBA			; clusterBeginLBA(4) = clusterBeginLBA(4) + fat16RootSectors(1)
	adc fat16RootSectors
	sta clusterBeginLBA

	lda clusterBeginLBA+1
	adc #$00
	sta clusterBeginLBA+1

	rep #A_8BIT				; A = 16 bit

	lda clusterBeginLBA+2
	adc #$0000
	sta clusterBeginLBA+2

	clc
	lda clusterBeginLBA
	adc fatBeginLBA
	sta clusterBeginLBA

	lda clusterBeginLBA+2
	adc fatBeginLBA+2
	sta clusterBeginLBA+2

	lda rootDirCluster
	sta sourceCluster

	lda rootDirCluster+2
	sta sourceCluster+2

	sep #A_8BIT				; A = 8 bit

 ; PrintString "\n"
 ; PrintHexNum sectorsPerCluster
 ; PrintString "\n"



.IFDEF DEBUG

	PrintString "fat16 != "
	PrintHexNum fat32Enabled
	PrintString "  RootSectors $"
	PrintHexNum fat16RootSectors
	PrintString "\n"

	PrintString "sectorsPerFat $"
	PrintHexNum sectorsPerFat+3
	PrintHexNum sectorsPerFat+2
	PrintHexNum sectorsPerFat+1
	PrintHexNum sectorsPerFat+0
	PrintString "\n"

	PrintString "partitionLBABegin $"
	PrintHexNum partitionLBABegin+3
	PrintHexNum partitionLBABegin+2
	PrintHexNum partitionLBABegin+1
	PrintHexNum partitionLBABegin+0
	PrintString "\n"

	PrintString "reservedSectors $"
	PrintHexNum reservedSectors+1
	PrintHexNum reservedSectors+0
	PrintString "\n"

	PrintString "fatBeginLBA $"
	PrintHexNum fatBeginLBA+3
	PrintHexNum fatBeginLBA+2
	PrintHexNum fatBeginLBA+1
	PrintHexNum fatBeginLBA+0
	PrintString "\n"

	PrintString "rootDirCluster $"
	PrintHexNum rootDirCluster+3
	PrintHexNum rootDirCluster+2
	PrintHexNum rootDirCluster+1
	PrintHexNum rootDirCluster+0
	PrintString "\n"

.ENDIF



; ************************* Load Configuration *************************

	jump jCardLoadDir			; root dir

	lda #'P'
	sta findEntry
	sta findEntry+5

	lda #'O'
	sta findEntry+1

	lda #'W'
	sta findEntry+2

	lda #'E'
	sta findEntry+3

	lda #'R'
	sta findEntry+4

	lda #'A'
	sta findEntry+6

	lda #'K'
	sta findEntry+7

	jump jDirFindEntry			; "POWERPAK" dir into tempEntry

	rep #A_8BIT				; A = 16 bit

	lda tempEntry.tempCluster		; "POWERPAK" dir found, write base cluster
	sta baseDirCluster

	lda tempEntry.tempCluster+2
	sta baseDirCluster+2

	sep #A_8BIT				; A = 8 bit



.IFDEF DEBUG

	PrintString "baseDirCluster $"
	PrintHexNum baseDirCluster+3
	PrintHexNum baseDirCluster+2
	PrintHexNum baseDirCluster+1
	PrintHexNum baseDirCluster+0
	PrintString "\n"

.ENDIF



LoadConfig:
	FindFile "CONFIG.TXT"

	lda #<sectorBuffer1
	sta destLo
	lda #>sectorBuffer1
	sta destHi				; put into sector RAM
	stz destBank

	stz sectorCounter
	stz bankCounter

	jump jClusterToLBA			; sourceCluster -> first sourceSector

	lda #kDestWRAM
	sta destType

	jump jCardReadSector			; sector -> WRAM

	ldy #$0000

LoadDMAConfigLoop:				; look for DMA=0 / DMA=1
	lda sectorBuffer1, y
	cmp #'D'
	beq LoadDMAConfigLoopFound

LoadDMAConfigLoopNext:
	iny
	cpy #$0200				; changed to 512 bytes
	bne LoadDMAConfigLoop

	bra __DMAStandard			; in case DMA=X entry isn't found, load standard setting below

LoadDMAConfigLoopFound:
	iny
	lda sectorBuffer1, y
	cmp #'M'
	bne LoadDMAConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'A'
	bne LoadDMAConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'='
	bne LoadDMAConfigLoopNext

LoadDMAConfigLoopRecord:
	iny
	lda sectorBuffer1, y
	cmp #'0'
	bne __DMAStandard
	stz configDMA				; if DMA=0, write $00 into configuration buffer
	bra LoadDMAConfigDone

__DMAStandard:
	lda #$01				; ... and $01 in any other case (switch DMA on)
	sta configDMA
	stz dontUseDMA				; tell DMA "blocker" variable, too (not needed with DMA=0 as DMA is initially off)

LoadDMAConfigDone:



	ldy #$0000

UserLogoConfigLoop:				; look for LOGO=1 / LOGO=2 / LOGO=3
	lda sectorBuffer1, y
	cmp #'L'
	beq UserLogoConfigLoopFound

UserLogoConfigLoopNext:
	iny
	cpy #$0200
	bne UserLogoConfigLoop

	bra __LogoStandard			; in case LOGO=X entry isn't found, load standard logo below

UserLogoConfigLoopFound:
	iny
	lda sectorBuffer1, y
	cmp #'O'
	bne UserLogoConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'G'
	bne UserLogoConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'O'
	bne UserLogoConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'='
	bne UserLogoConfigLoopNext

UserLogoConfigLoopRecord:
	iny
	lda sectorBuffer1, y
	cmp #'2'				; if LOGO=2, write $02 into configuration buffer
	bne +
	lda #$02
	bra UserLogoConfigDone
+
	cmp #'3'				; if LOGO=3, write $03 into configuration buffer
	bne __LogoStandard
	lda #$03
	bra UserLogoConfigDone

__LogoStandard:
	lda #$01				; in any other case, $01 (standard PowerPak logo)

UserLogoConfigDone:
	sta configLogo



	ldy #$0000

BGDesignConfigLoop:				; look for BGDESIGN=1...6
	lda sectorBuffer1, y
	cmp #'B'
	beq BGDesignConfigLoopFound

BGDesignConfigLoopNext:
	iny
	cpy #$0200
	bne BGDesignConfigLoop

	bra __BGDesignStandard			; in case BGDESIGN=X entry isn't found, load standard design below

BGDesignConfigLoopFound:
	iny
	lda sectorBuffer1, y
	cmp #'G'
	bne BGDesignConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'D'
	bne BGDesignConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'E'
	bne BGDesignConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'S'
	bne BGDesignConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'I'
	bne BGDesignConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'G'
	bne BGDesignConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'N'
	bne BGDesignConfigLoopNext
	iny
	lda sectorBuffer1, y
	cmp #'='
	bne BGDesignConfigLoopNext

BGDesignConfigLoopRecord:
	iny
	lda sectorBuffer1, y
	cmp #'2'				; if BGDESIGN=2...6, write $02...$06 into configuration buffer
	bne +
	lda #$02
	bra BGDesignConfigDone
+
	cmp #'3'
	bne +
	lda #$03
	bra BGDesignConfigDone
+
	cmp #'4'
	bne +
	lda #$04
	bra BGDesignConfigDone
+
	cmp #'5'
	bne +
	lda #$05
	bra BGDesignConfigDone
+
	cmp #'6'
	bne __BGDesignStandard
	lda #$06
	bra BGDesignConfigDone

__BGDesignStandard:				; in any other case, $01 ("Ocean" BG design)
	lda #$01

BGDesignConfigDone:

	sta configBGdesign



; ************************* Load intro & logo **************************

LoaderLoadModuleDone:
	wai

	FindFile "SI.MAP"

	jump jCardLoadModule			; load intro module



Forever:
	lda #$81
	sta REG_NMI_TIMEN			; turn on NMI

ForeverLoop:
	wai					; wait for next frame
	bra ForeverLoop



LoadLogo:
	lda #$5A				; X
	sta SpriteBuf1+16
	lda #$10				; Y
	sta SpriteBuf1+17
	lda #$04				; tile num
	clc
	adc temp				; plus diff, depending on user setting
	sta SpriteBuf1+18
	lda #$00				; vhoopppc Vert Horiz priOrity Palette Charmsb
	clc
	adc temp+1				; plus palette diff, depending on user setting
	sta SpriteBuf1+19

	lda #$7A				; X
	sta SpriteBuf1+20
	lda #$10				; Y
	sta SpriteBuf1+21
	lda #$08				; tile num
	clc
	adc temp				; plus diff, depending on user setting
	sta SpriteBuf1+22
	lda #$00				; vhoopppc Vert Horiz priOrity Palette Charmsb
	clc
	adc temp+1				; plus palette diff, depending on user setting
	sta SpriteBuf1+23

	lda #$5A				; X
	sta SpriteBuf1+24
	lda #$30				; Y
	sta SpriteBuf1+25
	lda #$44				; tile num
	clc
	adc temp				; plus diff, depending on user setting
	sta SpriteBuf1+26
	lda #$00				; vhoopppc Vert Horiz priOrity Palette Charmsb
	clc
	adc temp+1				; plus palette diff, depending on user setting
	sta SpriteBuf1+27

	lda #$7A				; X
	sta SpriteBuf1+28
	lda #$30				; Y
	sta SpriteBuf1+29
	lda #$48				; tile num
	clc
	adc temp				; plus diff, depending on user setting
	sta SpriteBuf1+30
	lda #$00				; vhoopppc Vert Horiz priOrity Palette Charmsb
	clc
	adc temp+1				; plus palette diff, depending on user setting
	sta SpriteBuf1+31

	lda #$AA
	sta SpriteBuf2+1			; 32x32 sprites

	lda temp+2				; tile num for cursor, next is palette
	sta SpriteBuf1+2
	lda temp+3				; vhoopppc Vert Horiz priOrity Palette Charmsb
	sta SpriteBuf1+3

	SetCursorPos 7, 7
	PrintString "S N E S    P O W E R P A K"
rts



; ************************* CF error handling **************************

CardFormatError:
	ClearLine 19
	ClearLine 20
	ClearLine 21
	SetCursorPos 19, 1
	PrintString "Card format error:"
	PrintString "\n  FAT32 or FAT16 expected!"
	PrintString "\n  1C2(fm)="
	lda sectorBuffer1+$1C2			; card last word read
	sta errorCode
	PrintHexNum errorCode
	PrintString " 1FE(55)="
	lda sectorBuffer1+$1FE			; card last word read
	sta errorCode
	PrintHexNum errorCode
	PrintString " 1FF(AA)="
	lda sectorBuffer1+$1FF			; card last word read
	sta errorCode
	PrintHexNum errorCode

	jump jForever



CardReset:
	lda #%00000000
	sta CARDLBA3				; select card 0

	lda #%00000110				; do card sw reset
	sta CARDDEVICE				; in device ctl reg
	wai
	wai
	lda #%00000010
	sta CARDDEVICE				; clear reset
	wai
	wai
rts



CardWaitNotBusy:

CardWaitNotBusyLoop:				; wait for not busy
	lda CARDSTATUS				; card status read
	sta errorCode
	and #%10000000
	cmp #%10000000				; check busy bit
	bne CardWaitNotBusyDone
;  SetCursorPos 12, 0
;  PrintString "    Error $"
;  PrintHexNum errorCode
;  PrintString " - CF card busy    "
	bra CardWaitNotBusyLoop

CardWaitNotBusyDone:

rts



CardWaitReady:

CardWaitReadyLoop:				; wait until card ready
	lda CARDSTATUS
	sta errorCode
	and #%01010000
	cmp #%01010000
	beq CardWaitReadyDone
;  SetCursorPos 12, 0
;  PrintString "    Error $"
;  PrintHexNum errorCode
;  PrintString " - CF not ready    "
	bra CardWaitReadyLoop

CardWaitReadyDone:

rts



CardWaitDataReq:				; wait for not busy, ready, datareq, no error

CardWaitDataReqLoop:
	lda CARDSTATUS
	sta errorCode
	and #%01011000
	cmp #%01011000
	beq CardWaitDataReqDone
;  SetCursorPos 12, 0
;  PrintString "    Error $"
;  PrintHexNum errorCode
;  PrintString " - CF no data req    "
	bra CardWaitDataReqLoop

CardWaitDataReqDone:

rts



CardCheckError:
	lda CARDSTATUS				; get card status, check for general error
	sta errorCode
	and #%00000001
	cmp #%00000001
	beq CardError
rts

CardError:
	ClearLine 19
	ClearLine 20
	SetCursorPos 19, 1
	PrintString "Error $"
	PrintHexNum errorCode
	PrintString " - CF card status"
	SetCursorPos 20, 1
	lda CARDERROR
	sta errorCode
	PrintString "Error $"
	PrintHexNum errorCode
	PrintString " - CF card error"

;.IFDEF DEBUG

	lda CARDSECTORCOUNTREAD
	sta errorCode
	PrintString "\n  c=$"
	PrintHexNum errorCode

	lda CARDLBA0READ
	sta errorCode
	PrintString " 0=$"
	PrintHexNum errorCode

	lda CARDLBA1READ
	sta errorCode
	PrintString " 1=$"
	PrintHexNum errorCode

	lda CARDLBA2READ
	sta errorCode
	PrintString " 2=$"
	PrintHexNum errorCode

	lda CARDLBA3READ
	sta errorCode
	PrintString " 3=$"
	PrintHexNum errorCode
	PrintString "\n"

;.ENDIF

CardErrorForever:
	jump jForever



CardLoadLBA:
	jump jCardWaitNotBusy

	jump jCardWaitReady

	jump jCardCheckError

	lda #$01
	sta CARDSECTORCOUNT

	jump jCardWaitNotBusy			; LAST check busy/ready/error after each command

	jump jCardWaitReady

;	jump jCardCheckError

	lda sourceSector
	sta CARDLBA0

	jump jCardWaitNotBusy

	jump jCardWaitReady

;	jump jCardCheckError

	lda sourceSector+1
	sta CARDLBA1

	jump jCardWaitNotBusy

	jump jCardWaitReady

;	jump jCardCheckError

	lda sourceSector+2
	sta CARDLBA2

	jump jCardWaitNotBusy

	jump jCardWaitReady

;	jump jCardCheckError

	lda sourceSector+3			; load LBA number
	and #%00001111
	ora #%11100000
	sta sourceSector+3
	sta CARDLBA3

	jump jCardWaitNotBusy

	jump jCardWaitReady

	jump jCardCheckError
rts



; ************************* Read from CF card **************************

CardReadSector:
	stz REG_NMI_TIMEN			; turn OFF NMI

	jump jCardLoadLBA

	stz sourceBytes16
	lda #$02
	sta sourceBytes16+1

;	jump jCardWaitNotBusy

;	jump jCardWaitReady

;	jump jCardCheckError

	lda #$20
	sta CARDCOMMAND				; send card read sector command

	jump jCardWaitNotBusy

	jump jCardWaitReady

	jump jCardCheckError

	jump jCardWaitDataReq



CardReadSectorToWRAM:				; CF to WRAM
	lda destType
	cmp #kDestWRAM
	bne CardReadSectorToWRAMDone

	lda dontUseDMA
	beq CardReadSectorToWRAMJump		; if dontUseDMA=1, no DMA

	jump jCardReadBytesNoDMA

	bra CardReadSectorCheckDone

CardReadSectorToWRAMJump:
	jump jCardReadBytesToWRAM		; CF to WRAM - DMA

	bra CardReadSectorCheckDone

CardReadSectorToWRAMDone:

CardReadSectorToFPGA:				; CF to FPGA - can't use DMA
	lda destType
	cmp #kDestFPGA
	bne CardReadSectorToFPGADone

	jump jCardReadBytesToFPGA

	bra CardReadSectorCheckDone

CardReadSectorToFPGADone:

CardReadSectorToSDRAM:				; CF to SDRAM - DMA, no inc
	lda destType
	cmp #kDestSDRAM
	bne CardReadSectorToSDRAMDone

	jump jCardReadBytesToSDRAM

	bra CardReadSectorCheckDone

CardReadSectorToSDRAMDone:



CardReadSectorToSDRAMNoDMA:			; CF to SDRAM - no DMA
	lda destType
	cmp #kDestSDRAMNoDMA
	bne CardReadSectorToSDRAMNoDMADone

	jump jCardReadBytesToSDRAMNoDMA

	bra CardReadSectorCheckDone

CardReadSectorToSDRAMNoDMADone:

	jump jCardReadBytesNoDMA		; CF to somewhere, no DMA

CardReadSectorCheckDone:

	jump jCardWaitNotBusy

	jump jCardWaitReady

	jump jCardCheckError

; lda CARDSECTORCOUNTREAD
; sta errorCode
; bne CardReadSectorFailed			; LAST make sure sectors = 0



CardReadSectorPassed:
; jump jCardWaitNotBusy			; LAST check for busy before error
; jump jCardWaitReady
; jump jCardCheckError
	lda $4210				; clear NMI Flag

	lda #$81
	sta REG_NMI_TIMEN			; turn ON NMI

CardReadSectorDone:

rts



CardReadSectorFailed:
	ClearLine 19
	ClearLine 20

	SetCursorPos 19, 1
	PrintString "Error CF sectors left = "
	PrintHexNum errorCode

	SetCursorPos 20, 1

	lda CARDERROR
	sta errorCode
	PrintString "Error $"
	PrintHexNum errorCode
	PrintString " - CF card error"

CardReadSectorFailedForever:

	jump jForever



CardReadBytesNoDMA:				; read source256*sourceBytes bytes into RAM
	ldx sourceBytes16
	ldy #$0000

CardReadBytesNoDMALoop:
	lda CARDDATAREAD
; sta $000200, y
	sta [destLo], y
	iny
	dex
	bne CardReadBytesNoDMALoop
; PrintHexNum bankCounter
; PrintString "CardReadBytesDone\n"

rts



CardReadBytesToFPGA:				; no DMA
	ldx sourceBytes16

CardReadBytesToFPGALoop:
	lda CARDDATAREAD			; READ DATA BYTE
	sta FPGADATAWRITE			; WRITE TO FPGA
	dex
	bne CardReadBytesToFPGALoop
rts



CardReadBytesToWRAM:				; use DMA, no increment source or dest
	ldx destLo
	stx $2181
	stz $2183				; load the RAM dest address

	DMA_WaitHblank $08, CARDDATAREADbank, CARDDATAREADhigh, CARDDATAREADlow, $80, sourceBytes16
rts



CardReadBytesToSDRAMNoDMA:
	ldx sourceBytes16

CardReadBytesToSDRAMLoop:
	lda CARDDATAREAD			; READ DATA BYTE
	sta $0021FF				; WRITE TO SDRAM   //   SEE IF A021FF WORKS
	dex
	bne CardReadBytesToSDRAMLoop
rts



;;TEST THIS ONE
CardReadBytesToSDRAM:
	DMA_WaitHblank $08, CARDDATAREADbank, CARDDATAREADhigh, CARDDATAREADlow, $FF, sourceBytes16
rts



CardReadFile:					; sourceCluster already set
	stz gameSize
	stz gameSize+1

	jump jClusterToLBA			; sourceCluster -> first sourceSector

__CardReadFileLoop:
;  PrintHexNum sourceSector+3
;  PrintHexNum sourceSector+2
;  PrintHexNum sourceSector+1
;  PrintHexNum sourceSector+0
;  PrintString "."

	rep #A_8BIT				; A = 16 bit

	inc gameSize				; keep track of game size

	sep #A_8BIT				; A = 8 bit

	lda #kDestSDRAM
	sta destType

	jump jCardReadSector

	IncrementSectorNum

	inc destHi
	inc destHi
	inc bankCounter

	bra __CardReadFileLoop
__ rts



CardWriteFile:					; sourceCluster already set
	stz sectorCounter
	stz bankCounter

	lda #kDestWRAM
	sta destType

	jump jClusterToLBA			; sourceCluster -> first sourceSector

__CardWriteFileLoop:
	jump jCardWriteSector

	IncrementSectorNum

	inc sourceHi
	inc sourceHi
	inc bankCounter

	bra __CardWriteFileLoop
__ rts



CardWriteSector:
	jump jCardWaitNotBusy

	jump jCardWaitReady

	jump jCardCheckError

	jump jCardLoadLBA

	stz sourceBytes16
	lda #$02
	sta sourceBytes16+1

	jump jCardWaitNotBusy

	jump jCardWaitReady

	jump jCardCheckError

	lda #$30
	sta CARDCOMMAND				; send card write sector command
	nop
	nop

	jump jCardCheckError

	jump jCardWaitDataReq

CardWriteSectorSDRAM:
	lda sourceType
	cmp #kSourceSDRAM
	bne CardWriteSectorNotSDRAM

	jump jCardWriteBytesFromSDRAM

CardWriteSectorNotSDRAM:

CardWriteSectorWRAM:
	lda sourceType
	cmp #kSourceWRAM
	bne CardWriteSectorNotWRAM

	jump jCardWriteBytesFromWRAM

CardWriteSectorNotWRAM:

	jump jCardCheckError

CardWriteSectorDone:

rts



CardWriteBytesFromWRAM:				; write source256*sourceBytes bytes into CF card
	ldx sourceBytes16
	ldy #$0000

CardWriteBytesWRAMLoop:
	lda [sourceLo], y
	sta CARDDATAWRITE
	iny
	dex
	bne CardWriteBytesWRAMLoop
rts



CardWriteBytesFromSDRAM:			; write source256*sourceBytes bytes into CF card
	ldx sourceBytes16

CardWriteBytesSDRAMLoop:
	lda DMAREADDATA
	sta CARDDATAWRITE			; FIXME CHANGE TO USE DMA FROM SDRAM TO CF
	dex
	bne CardWriteBytesSDRAMLoop
rts



; cluster->lba     lba_addr(4) = clusterBeginLBA(4) + (cluster_number(2)-2 * sectorsPerCluster(1))
ClusterToLBA:
	rep #A_8BIT				; A = 16 bit

	sec
	lda sourceCluster
	sbc #$0002
	sta sourceSector

	lda sourceCluster+2
	sbc #$0000
	sta sourceSector+2			; sourceSector = sourceCluster - 2

	sep #A_8BIT				; A = 8 bit

	lda sectorsPerCluster
	sta source256

	lsr source256
	beq +					; handle 1 sector per cluster

__ClusterToLBALoop:
	rep #A_8BIT				; A = 16 bit

	asl sourceSector
	rol sourceSector+2			; sourceSector = sourceSector * sectorsPerCluster

	sep #A_8BIT				; A = 8 bit

	lsr source256
	bne __ClusterToLBALoop

+	rep #A_8BIT				; A = 16 bit

	clc
	lda sourceSector
	adc clusterBeginLBA
	sta sourceSector

	lda sourceSector+2
	adc clusterBeginLBA+2
	sta sourceSector+2			; sourceSector = sourceSector(4) + clusterBeginLBA(4)

	sep #A_8BIT				; A = 8 bit
rts



CardLoadDirClearEntryName:
	stz lfnFound

	rep #A_8BIT				; A = 16 bit

	lda #$0000
	ldy #$0000

-	sta tempEntry, y
	iny
	iny
	cpy #$0080
	bne -

	sep #A_8BIT				; A = 8 bit
rts



; ********************* Load dir from CF to buffer *********************

; Loads the contents of a directory and stores relevant files in the
; selected buffer (WRAM/SDRAM). bufferType is reset on RTS, so WRAM is
; the standard buffer.

; OLD COMMENTS (v1.0X):
; dest = sector dest  0200
; sourceEntry = where to get entry in 0200-0400, when overflow, load next sector
; destBank =  which bank to select in prg ram
; destEntry = where to put entry in prg ram 6000-7FFF, when overflow, increment bank

CardLoadDir:
	lda #>sectorBuffer1
	sta destHi				; sourceSector1 = where to put sector, where to read entry
	sta sourceEntryHi
	lda #<sectorBuffer1
	sta destLo
	sta sourceEntryLo
	stz destBank
	stz sourceEntryBank

	lda #$7F				; start at WRAM offset $7F0000
	sta destEntryBank
	stz destEntryHi
	stz destEntryLo

	lda #$00				; start at SDRAM offset $000000
	sta DMAWRITELO
	sta DMAWRITEHI
	sta DMAWRITEBANK

	stz filesInDir
	stz filesInDir+1
	stz sectorCounter
	stz temp
	stz temp+1
	stz selectedEntry
	stz selectedEntry+1
	stz lfnFound

	jump jClusterToLBA			; sourceCluster -> first sourceSector

	rep #A_8BIT				; A = 16 bit

	lda fat32Enabled
	and #$0001
	bne __CLD_ReadSector			; FAT32 detected, go to read sector

	lda sourceCluster			; FAT16 check if trying to load root dir
	cmp rootDirCluster
	bne __CLD_ReadSector

	lda sourceCluster+2
	cmp rootDirCluster+2
	bne __CLD_ReadSector

	sep #A_8BIT				; A = 8 bit

	sec
	lda clusterBeginLBA			; FAT16 sourceSector = root dir first sector => clusterLBABegin(4) - fat16RootSectors(1)
	sbc fat16RootSectors
	sta sourceSector

	lda clusterBeginLBA+1
	sbc #$00
	sta sourceSector+1

	rep #A_8BIT				; A = 16 bit

	lda clusterBeginLBA+2
	sbc #$0000
	sta sourceSector+2

__CLD_ReadSector:
	sep #A_8BIT				; A = 8 bit

	lda #kDestWRAM
	sta destType

	jump jCardReadSector			; put into dest

	jump jCardLoadDirClearEntryName		; clear entry name



CardLoadDirLoop:



; -------------------------- check for last entry
	ldy #$0000

	lda [sourceEntryLo], y			; if name[0] = 0x00, no more entries
	bne __CLD_EntryNotLast

	jmp __CLD_LastEntryFound

__CLD_EntryNotLast:



; -------------------------- check for unused entry
	ldy #$0000

	lda [sourceEntryLo], y
	cmp #$E5				; if name[0] = 0xE5, entry unused, skip
	bne __CLD_EntryNotUnused

	stz lfnFound

	jmp __CLD_NextEntry

__CLD_EntryNotUnused:



; -------------------------- check for LFN entry
	ldy #$000B

	lda [sourceEntryLo], y			; if flag = %00001111, long file name entry found
	and #$0F
	cmp #$0F
	bne __CLD_EntryNotLFN

	ldy #$0000				; check first byte of LFN entry = ordinal field

	lda [sourceEntryLo], y
	and #%10111111				; mask off "last entry" bit
	cmp #$0A				; if index = 1...9, load name
	bcc +

	jump jCardLoadDirClearEntryName		; if index >= 10, skip entry (reminder: index = 0 doesn't seem to exist ??)

	bra __CLD_EntrySkipLFN

+	sta $211B				; PPU multiplication
	stz $211B

	lda #$0D				; LFNs consist of 13 unicode characters
	sta $211C

	rep #A_8BIT				; A = 16 bit

	lda $2134				; read result
	sec
	sbc #$000D				; subtract 13 to start at tempEntry's beginning

	tax					; transfer to X register

	sep #A_8BIT				; A = 8 bit

	jsr CLD_LoadLFN				; load LFN entry, store characters to tempEntry

__CLD_EntrySkipLFN:
	jmp __CLD_NextEntry

__CLD_EntryNotLFN:



; -------------------------- check for volume ID entry
	ldy #$000B

	lda [sourceEntryLo], y			; if flag = volume id, skip
	and #$08
	cmp #$08
	bne __CLD_EntryNotVolumeID

	stz lfnFound

	jump jCardLoadDirClearEntryName

	jmp __CLD_NextEntry

__CLD_EntryNotVolumeID:



; -------------------------- short file name found, perform additional checks

; -------------------------- check for hidden entry
	ldy #$000B

	lda [sourceEntryLo], y
	and #$02				; if flag = 0x02, hidden, mark as such
	beq __CLD_EntryHiddenCheckDone

	tsb tempEntry.tempFlags			; save "hidden" flag

__CLD_EntryHiddenCheckDone:



; -------------------------- check for directory entry
	ldy #$000B

	lda [sourceEntryLo], y			; if flag = directory, load entry
	and #$10
	cmp #$10
	bne __CLD_EntryNotDirectory

	lda #$01
	tsb tempEntry.tempFlags			; save "dir" flag

	bra __CLD_ProcessMatchingEntry

__CLD_EntryNotDirectory:



; -------------------------- check for entry extension
	ldx extNum				; check if entry matches any extension wanted,
	dex					; backwards from the Nth (= $NNNN-1) to the 1st (= $0000) extension

__CLD_CheckExtLoop:
	ldy #$0008

	lda [sourceEntryLo], y
	cmp extMatch1, x
	bne +

	iny
	lda [sourceEntryLo], y
	cmp extMatch2, x
	bne +

	iny
	lda [sourceEntryLo], y
	cmp extMatch3, x
	beq __CLD_ProcessMatchingEntry		; extension matches, branch

+	dex					; otherwise, try next extension indicated
	bpl __CLD_CheckExtLoop

	stz lfnFound				; extension doesn't match, skip entry

	jump jCardLoadDirClearEntryName

	jmp __CLD_NextEntry



; -------------------------- load long/short entry with matching ext.
__CLD_ProcessMatchingEntry:
	rep #A_8BIT				; A = 16 bit

	inc filesInDir				; increment file counter

	sep #A_8BIT				; A = 8 bit

	lda lfnFound
	cmp #$01
	beq __CLD_PrepareSaveEntry

	ldy #$0000				; if lfnFound = 0, copy short file name only

-	lda [sourceEntryLo], y
	sta tempEntry, y
	iny
	cpy #$0008
	bne -

	lda tempEntry.tempFlags
	and #%00000001				; check for "dir" flag
	bne __CLD_PrepareSaveEntry

	lda #'.'				; if not directory, copy short file name extension
	sta tempEntry+$8
	ldy #$0008
	lda [sourceEntryLo], y
	sta tempEntry+$9
	iny
	lda [sourceEntryLo], y
	sta tempEntry+$A
	iny
	lda [sourceEntryLo], y
	sta tempEntry+$B

__CLD_PrepareSaveEntry:
	ldy #$001A

	lda [sourceEntryLo], y			; copy clusterhilo to last 4 bytes of entry
	sta tempEntry.tempCluster

	iny

	lda [sourceEntryLo], y
	sta tempEntry.tempCluster+1

	ldy #$0014

	lda [sourceEntryLo], y
	sta tempEntry.tempCluster+2

	iny

	lda [sourceEntryLo], y
	sta tempEntry.tempCluster+3

	ldy #$0000

	lda bufferType				; check for buffer location
	cmp #'S'
	beq __CLD_UseSDRAMbuffer

	jsr CLD_SaveEntryToWRAM

	rep #A_8BIT				; A = 16 bit

	lda filesInDir				; check if file counter has reached 512
	cmp #$0200
	bcc __CLD_WRAMcontinue

	sep #A_8BIT				; if so, A = 8 bit

	jmp __CLD_LastEntryFound		; ... and don't process any more entries

__CLD_WRAMcontinue:
	sep #A_8BIT				; otherwise, A = 8 bit

	jump jCardLoadDirClearEntryName

	bra __CLD_NextEntry			; ... and continue



__CLD_UseSDRAMbuffer:
	lda nextModule
	cmp #'S'				; don't skip hidden files/folders if nextModule = #'S'
	beq _f

	lda tempEntry.tempFlags
	and #%00000010				; check for "hidden" flag
	beq _f

	rep #A_8BIT				; A = 16 bit

	dec filesInDir				; hidden dir/file found, decrement file counter ...

	bra __CLD_SDRAMcontinue			; ... and jump out

__	lda tempEntry, y			; save entry to SDRAM
	sta DMAREADDATA
	iny
	cpy #$0080
	bne _b

	rep #A_8BIT

	lda filesInDir
	cmp #$FFFF				; check if file counter has reached 65535
	bcc __CLD_SDRAMcontinue

	sep #A_8BIT				; if so, A = 8 bit

	jmp __CLD_LastEntryFound		; ... and don't process any more entries

__CLD_SDRAMcontinue:
	sep #A_8BIT				; otherwise, A = 8 bit, and continue

	jump jCardLoadDirClearEntryName



; -------------------------- finally, increment to next entry, and loop
__CLD_NextEntry:
	rep #A_8BIT				; A = 16 bit

	clc					; increment entry source address
	lda sourceEntryLo			; sourceEntry += 32 in 0200-0400
	adc #$0020
	sta sourceEntryLo

	sep #A_8BIT				; A = 8 bit

	lda sourceEntryHi			; if source overflows, get next sector
	cmp #>sectorBuffer1+$02			; LAST CHANGE
	beq __CLD_NextSector

	jmp CardLoadDirLoop



; -------------------------- if necessary, increment source sector
__CLD_NextSector:
	rep #A_8BIT				; A = 16 bit

	clc
	lda sourceSector
	adc #$0001
	sta sourceSector			; go to next sector num

	lda sourceSector+2
	adc #$0000
	sta sourceSector+2

	lda fat32Enabled			; if FAT32, increment sector
	and #$0001
	bne __CLD_SectorIncrement

	lda sourceCluster			; FAT16 check if trying to load root dir
	cmp rootDirCluster
	bne __CLD_SectorIncrement

	lda sourceCluster+2
	cmp rootDirCluster+2
	bne __CLD_SectorIncrement

	sep #A_8BIT				; A = 8 bit

	inc sectorCounter			; FAT16 root dir all sequential sectors

	lda sectorCounter
	cmp fat16RootSectors			; if sectorCounter = fat16RootSectors, jump out
	beq __CLD_LastEntryFound

	bra __CLD_LoadNextSector		; FAT16 skip cluster lookup when max root sectors not reached

__CLD_SectorIncrement:
	sep #A_8BIT				; A = 8 bit

	inc sectorCounter			; one more sector

	lda sectorCounter
	cmp sectorsPerCluster			; make sure cluster isn't overflowing
	bne __CLD_LoadNextSector

	jump jNextCluster			; move to next cluster

	rep #A_8BIT				; A = 16 bit

; check for last sector
; FAT32 last cluster = 0x0FFFFFFF
; FAT16 last cluster = 0x0000FFFF

	lda fat32Enabled			; check for FAT32
	and #$0001
	bne __CLD_LastClusterMaskFAT32

	stz temp+2				; if FAT16, high word = $0000
	bra __CLD_LastClusterMaskDone

__CLD_LastClusterMaskFAT32:
	lda #$0FFF				; if FAT32, high word = $0FFF
	sta temp+2

__CLD_LastClusterMaskDone:			; if cluster = last cluster, jump to last entry found
	lda sourceCluster
	cmp #$FFFF				; low word = $FFFF (FAT16/32)
	bne __CLD_NextSectorNum

	lda sourceCluster+2
	cmp temp+2
	bne __CLD_NextSectorNum

	sep #A_8BIT				; A = 8 bit

	bra __CLD_LastEntryFound		; last cluster, jump out

__CLD_NextSectorNum:
	sep #A_8BIT				; A = 8 bit

	jump jClusterToLBA			; sourceCluster -> first sourceSector

	stz sectorCounter			; reset sector counter

__CLD_LoadNextSector:
	lda #<sectorBuffer1
	sta destLo				; reset sector dest
	sta sourceEntryLo			; reset entry source

	lda #>sectorBuffer1
	sta destHi
	sta sourceEntryHi

	stz destBank
	stz sourceEntryBank

	lda #kDestWRAM
	sta destType

	jump jCardReadSector

	jmp CardLoadDirLoop

__CLD_LastEntryFound:

	stz bufferType				; reset bufferType variable
rts



; -------------------------- CardLoadDir subroutine: load LFN entry
CLD_LoadLFN:
	ldy #$0001

__CLD_LoadLFNLoop1:
	lda [sourceEntryLo], y			; copy unicode chars 1-5 from offsets $01, $03, $05, $07, $09
	cmp #$FF
	beq +

	sta tempEntry, x

+	inx
	iny
	iny
	cpy #$000B
	bne __CLD_LoadLFNLoop1

	ldy #$000E

__CLD_LoadLFNLoop2:
	lda [sourceEntryLo], y			; copy unicode chars 6-11 from offsets $0E, $10, $12, $14, $16, $18
	cmp #$FF
	beq +

	sta tempEntry, x

+	inx
	iny
	iny
	cpy #$001A
	bne __CLD_LoadLFNLoop2

	ldy #$001C

__CLD_LoadLFNLoop3:
	lda [sourceEntryLo], y			; copy unicode chars 12-13 from offsets $1C, $1E
	cmp #$FF
	beq +

	sta tempEntry, x

+	inx
	iny
	iny
	cpy #$0020
	bne __CLD_LoadLFNLoop3

	lda #$01
	sta lfnFound
rts



; -------------------------- CardLoadDir subroutine: save entry to WRAM buffer
CLD_SaveEntryToWRAM:
	rep #A_8BIT				; A = 16 bit

-	lda tempEntry, y
	sta [destEntryLo], y
	iny
	iny
	cpy #$0080
	bne -

	clc
	lda destEntryLo				; destEntryHiLo += 128
	adc #$0080
	sta destEntryLo

	sep #A_8BIT				; A = 8 bit
rts



; **************************** Next cluster ****************************

; Load FAT entry for sourceCluster, store next cluster number into
; sourceSector.

NextCluster:
	rep #A_8BIT				; A = 16 bit

	asl sourceCluster			; FAT16: offset = clusternum << 1
	rol sourceCluster+2			; mask = 00 00 ff ff

	lda fat32Enabled
	and #$0001
	beq NextClusterSectorNum

	asl sourceCluster			; FAT32: offset = clutsernum << 2 || cluster 2 << 2 = 8
	rol sourceCluster+2			; mask = 0f ff ff ff

NextClusterSectorNum:				; FAT sector num = fatBeginLBA + (offset / 512) || cluster = $2f8
	lda sourceCluster+1			; first, divide by 256 by shifting sourceCluster 8 bits right,
	sta sourceSector+0			; and store to sourceSector

	sep #A_8BIT				; A = 8 bit

	lda sourceCluster+3
	sta sourceSector+2

	stz sourceSector+3			; division by 256 done || sector = 02

	rep #A_8BIT				; A = 16 bit

	lsr sourceSector+2			; next, shift sourceSector 1 more bit right (div. by 512) || sector = 01

	ror sourceSector+0

	clc
	lda sourceSector			; add fatBeginLBA || sector = 60 = $c000
	adc fatBeginLBA
	sta sourceSector

	lda sourceSector+2
	adc fatBeginLBA+2
	sta sourceSector+2

	sep #A_8BIT				; A = 8 bit

	lda #>sectorBuffer1			; load FAT sector
	sta destHi
	lda #<sectorBuffer1
	sta destLo
	stz destBank

	lda #kDestWRAM
	sta destType

	jump jCardReadSector

; offset = offset % 512 -- offset of FAT entry within loaded sector 0-511
	lda sourceCluster+1
	and #%00000001
	sta sourceCluster+1			; cluster+1=0

	lda #<sectorBuffer1			; next cluster = [sector], offset
	sta sourceLo
	stz sourceBank

; sourceHi = sectorBuffer1(high) or sectorBuffer1(high)+1 for which 256 byte block to look in
	lda #>sectorBuffer1
	adc sourceCluster+1
	sta sourceHi

	lda sourceCluster
	sta temp+2
	stz temp+3

	ldy temp+2
	ldx #$0000

NextClusterLoop:
	lda [sourceLo], y
	sta sourceCluster, x
	iny
	inx
	cpx #$0004
	bne NextClusterLoop

	lda sourceCluster+3			; FAT32 mask off top 4 bits
	and #$0F
	sta sourceCluster+3

	lda fat32Enabled
	cmp #$01
	beq NextClusterDone			; no more mask for FAT32

	stz sourceCluster+3			; FAT16 mask
	stz sourceCluster+2

NextClusterDone:

rts



; ********************** Print directory content ***********************

DirPrintDir:					; loop through entries 0 to #maxFiles or 0 to filesInDir
	jump jPrintClearScreen

	stz selectedEntry
	stz selectedEntry+1

	SetCursorPos 0, 0

__DirPrintDirLoop:
	jump jDirPrintEntry

	rep #A_8BIT				; A = 16 bit

	inc selectedEntry			; increment entry index

	lda selectedEntry
	cmp #maxFiles				; check if printY max reached, jump out
	beq __DirPrintDirLoopDone

	cmp filesInDir				; check if last file reached, jump out
	beq __DirPrintDirLoopDone

	sep #A_8BIT				; A = 8 bit

	bra __DirPrintDirLoop

__DirPrintDirLoopDone:

.ACCU 16

	stz selectedEntry			; reset entry index

	sep #A_8BIT				; A = 8 bit

	lda #$0D				; in 1-byte/1-pixel-steps
	sta cursorX				; indent cursor, too

	lda #cursorYmin
	sta cursorY

	stz cursorYCounter
	stz cursorYUp
	stz cursorYDown

	stz scrollY
	stz scrollYCounter
	stz scrollYUp
	stz scrollYDown

	stz speedCounter
	stz speedScroll

	lda #insertStandardTop
	sta insertTop

	lda #insertStandardBottom
	sta insertBottom
rts



; ************************* Print single entry *************************

DirPrintEntry:
	lda #'S'
	sta bufferType				; use SDRAM buffer

	jump jDirGetEntry

	stz tempEntry+56			; NUL-terminate entry string after 56 characters

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	lda tempEntry.tempFlags			; if "dir" flag is set, then print a slash in front of entry name
	and #%00000001
	beq __PrintFileOnly

	PrintString " /%s\n"
	bra __DirPrintEntryDone

__PrintFileOnly:
	PrintString "  %s\n"

__DirPrintEntryDone:

	stz bufferType				; reset bufferType variable
rts



; ********************** Get entry into tempEntry **********************

; This function calculates selectedEntry's buffer address, then copies
; the 128-byte-long entry to the tempEntry variable.
;
; WRAM procedure (using the upper 64K only):
; sourceEntryLo[16bit] = selectedEntry[16bit] * 128
; max. 512 entries, last entry's address = $(7F)FF80
;
; SDRAM procedure (using the lower 8MB only):
; DMAWRITELO[24bit] = selectedEntry[16bit] * 128
; max. 65535 entries, last entry's address = $7FFF80
;
; Calculation code using bitwise operations is based on a code snippet
; by thefox. We "shift" selectedEntry[16bit] left 8 bits (no actual
; shifting because it's on byte boundary), then shift the whole value
; right one bit.

DirGetEntry:
	lda bufferType				; check for buffer location (unspecified = $00 = WRAM)
	beq __GetEntryFromWRAM

	lda selectedEntry+1			; get entry from SDRAM
	lsr a
	sta DMAWRITEBANK

	lda selectedEntry
	ror a
	sta DMAWRITEHI

	lda #$00
	ror a
	sta DMAWRITELO

	ldy #$0000

__GetEntryFromSDRAMLoop:
	lda DMAREADDATA
	sta tempEntry, y
	iny
	cpy #$0080
	bne __GetEntryFromSDRAMLoop
rts



__GetEntryFromWRAM:
	lda #$01				; set bank = $7F
	sta $2183

	lda selectedEntry+1
	lsr a					; selectedEntry+1 into carry bit

	lda selectedEntry
	ror a
	sta $2182

	lda #$00
	ror a
	sta $2181

	ldy #$0000

__GetEntryFromWRAMLoop:
	lda $2180
	sta tempEntry, y
	iny
	cpy #$0080
	bne __GetEntryFromWRAMLoop
rts



; ************************ Find specific entry *************************

DirFindEntry:
	rep #A_8BIT				; A = 16 bit

	stz selectedEntry			; reset selectedEntry

	lda filesInDir				; only do the search if directory isn't empty
	beq __DirFindEntryFailed

__DirFindEntryLoop:
	sep #A_8BIT				; A = 8 bit

	jump jDirGetEntry

	ldy #$0000

__DirFindEntryCharLoop:				; check if entry matches, only look at first 8 chars
	lda tempEntry, y
	cmp findEntry, y
	bne __DirFindEntryNext
	iny
	cpy #$0008
	bne __DirFindEntryCharLoop
rts						; all 8 chars match

__DirFindEntryNext:
	rep #A_8BIT				; A = 16 bit

	inc selectedEntry			; increment to next entry index

	lda selectedEntry			; check for max. no. of files
	cmp filesInDir
	bne __DirFindEntryLoop

__DirFindEntryFailed:
	sep #A_8BIT				; A = 8 bit

	ClearLine 20
	ClearLine 21				; hide help
	ClearLine 22

	SetCursorPos 20, 6

	ldx #$0000

-	lda findEntry, x
	cmp #' '				; only load actual filename characters to print 
	beq +
	inx
	cpx #$0008
	bne -

+	stz findEntry, x			; NUL-terminate filename string at first space, or after 8 chars

	lda #<findEntry
	sta strPtr
	lda #>findEntry
	sta strPtr+1

	ldy #strPtr

	PrintString "Error: %s"

	lda tempEntry.tempFlags			; if directory, skip printing of file extension
	and #%00000001				; "dir" flag
	bne +

	lda extMatch1				; load file extension into tempEntry for printing
	sta tempEntry
	lda extMatch2
	sta tempEntry+1
	lda extMatch3
	sta tempEntry+2
	stz tempEntry+3				; NUL-terminate file extension string

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	PrintString ".%s"			; result: e.g. "Error: SI.MAP file not found!"
+	PrintString " file not found!"		; or (if dir) "Error: POWERPAK file not found!"

	jump jForever



; ************************** Load CF modules ***************************

CardLoadModule:					; expects module file sourceCluster set via FindFile macro!
	lda #<codeBuffer
	sta destLo
	lda #>codeBuffer
	sta destHi				; put into code RAM

	stz destBank

	stz sectorCounter
	stz bankCounter

	jump jClusterToLBA			; sourceCluster -> first sourceSector

CardLoadModuleLoop:
	lda #kDestWRAM
	sta destType

	jump jCardReadSector			; sector -> code RAM

	IncrementSectorNum

	inc destHi
	inc destHi
	inc bankCounter

	lda bankCounter
	cmp #$0A				; only load 10x512=5KB for modules
	beq _f

	bra CardLoadModuleLoop

__	stz scrollY
	stz scrollYCounter
	stz scrollYUp
	stz scrollYDown

	pla					; Clean up the stack, this is necessary to avoid a stack overflow
	pla					; that plagued v1.0X and usually occurred after many module changes.
	pla					; The bug was likely caused by the PHX/JSR in the jump macro, from
	pla					; where there is no RTS (and thus, no PLX) once a new module is loaded.
						; (Test results showed that S = S - 4 bytes per "jump jCardLoadModule".)

	jml codeBuffer				; go to module begin



; ************************** Get next sector ***************************

LoadNextSectorNum:				; get next sector, use sectorCounter to check if next cluster needed
	rep #A_8BIT				; A = 16 bit

	clc
	lda sourceSector
	adc #$0001
	sta sourceSector			; go to next sector num

	lda sourceSector+2
	adc #$0000
	sta sourceSector+2

	sep #A_8BIT				; A = 8 bit

	inc sectorCounter			; one more sector

	lda sectorCounter
	cmp sectorsPerCluster
	beq LoadNextClusterNum
rts

LoadNextClusterNum:
	stz sectorCounter

	jump jNextCluster			; get cluster num into sourceCluster from FAT table

.IFDEF DEBUG

	PrintString "cluster="
	PrintString sourceCluster+3
	PrintString sourceCluster+2
	PrintString sourceCluster+1
	PrintString sourceCluster+0
	PrintString "\n"

.ENDIF

	jump jClusterToLBA			; get sector num into sourceSector

.IFDEF DEBUG

	PrintString "sector="
	PrintHexNum sourceSector+3
	PrintHexNum sourceSector+2
	PrintHexNum sourceSector+1
	PrintHexNum sourceSector+0
	PrintString "\n"

	jsr Delay5Sec

.ENDIF

rts



; *********************** Load another directory ***********************

NextDir:
	rep #A_8BIT

	lda tempEntry.tempCluster
	sta sourceCluster
	bne __NotRootDir			; check if cluster = root dir

	lda tempEntry.tempCluster+2
	bne __NextDirDone

	lda rootDirCluster			; if yes, load root dir
	sta sourceCluster

	lda rootDirCluster+2
	bra __NextDirDone

__NotRootDir:
	lda tempEntry.tempCluster+2

__NextDirDone:
	sta sourceCluster+2

	sep #A_8BIT

	jsr ShowLoadingMsg

	jump jCardLoadDir

	jump jDirPrintDir			; this includes PrintClearScreen / SetCursorPos 0, 0
rts



; ***************************** Load FPGA ******************************

CardLoadFPGA:
	lda #$01
	sta FPGAPROGRAMWRITE			; SEND PROGRAM SIGNAL TO FPGA
	lda #$00
	sta FPGAPROGRAMWRITE			; SEND PROGRAM SIGNAL TO FPGA
	wai
	wai
	lda #$01
	sta FPGAPROGRAMWRITE			; SEND PROGRAM SIGNAL TO FPGA

	stz sectorCounter			; BIT file cluster already in sourceCluster
	stz bankCounter

	jump jClusterToLBA			; sourceCluster -> first sourceSector

	wai

	lda #$80
	sta $2100				; turn screen off

CardLoadFPGALoop:
	lda #kDestFPGA
	sta destType

	jump jCardReadSector			; sector -> FPGA

	jump jLoadNextSectorNum

	rep #A_8BIT				; A = 16 bit

; check for last sector
; FAT32 last cluster = 0x0FFFFFFF
; FAT16 last cluster = 0x0000FFFF

	lda fat32Enabled			; check for FAT32
	and #$0001
	bne __FPGALastClusterMaskFAT32

	stz temp+2				; if FAT16, high word = $0000
	bra __FPGALastClusterMaskDone

__FPGALastClusterMaskFAT32:
	lda #$0FFF				; if FAT32, high word = $0FFF
	sta temp+2

__FPGALastClusterMaskDone:			; if cluster = last cluster, jump to last entry found
	lda sourceCluster
	cmp #$FFFF				; low word = $FFFF (FAT16/32)
	bne __FPGANextSector

	lda sourceCluster+2
	cmp temp+2
	bne __FPGANextSector

	sep #A_8BIT				; A = 8 bit

	bra __LoadFPGADone			; last cluster, jump out

__FPGANextSector:
	sep #A_8BIT				; A = 8 bit

	inc bankCounter

	lda bankCounter
	cmp #$6B				; 437312 bits = 54664 bytes = 107 sectors = $6B
	bne CardLoadFPGALoop

__LoadFPGADone:

	lda #$0F
	sta $2100				; turn screen on
rts



; ************************* GameGenie Handler **************************

GameGenieClearAll:
	rep #A_8BIT				; A = 16 bit

	ldy #$0000
	lda #$1010				; $10 = underscore on GG code display

-	sta GameGenie.Codes, y
	iny
	iny
	cpy #$0028				; 5 x 8 = 40 characters
	bne -

	sep #A_8BIT				; A = 8 bit
rts



GameGenieClearCode:				; clears out one GG code at a time, expects code no. set to Y
	ldx #$0000				; (#$0000 = code 1, #$0008 = code 2, ... #$0020 = code 5)
	lda #$10				; $10 = underscore on code display

-	sta GameGenie.Codes, y
	iny
	inx
	cpx #$0008
	bne -
rts



GameGenieCharDB:
	.DB '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '_'

GGCharConvertedToHexDB:
	.DB $04, $06, $0D, $0E, $02, $07, $08, $03, $0B, $05, $0C, $09, $0A, $00, $0F, $01, $00



GameGeniePrint:					; code to print already set to Y
	phy

	jsr CardLoadDirClearEntryName

	ply

	stz GameGenie.CharOffset+1

	ldx #$0000

-	lda GameGenie.Codes, y
	sta GameGenie.CharOffset
	iny					; advance code character offset

	phx

	ldx GameGenie.CharOffset
	lda.l GameGenieCharDB, x		; look up ASCII equivalent for code char

	plx

	sta tempEntry, x
	inx

	lda #' '				; put 3 spaces after each char

	sta tempEntry, x
	inx
	sta tempEntry, x
	inx
	sta tempEntry, x
	inx

	cpx #$0020				; (1 char + 3 spaces) * 8 = 32 chars to print
	bne -

	stz tempEntry, x			; NUL-terminator

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	PrintString "%s"
rts



GameGenieDecode:				; code to decode set to Y
	phy

	stz GameGenie.CharOffset+1

	ldx #$0000

-	lda GameGenie.Codes, y
	sta GameGenie.CharOffset
	iny					; advance code character offset

	phx

	ldx GameGenie.CharOffset
	lda.l GGCharConvertedToHexDB, x		; look up real hex equivalent for GG hex character

	plx

	sta GameGenie.RealHex, x
	inx

	cpx #$0008				; 8 characters per code
	bne -

	ply

	phy

	ldx #$0000
	ldy #$0000

-	lda GameGenie.RealHex, x
	asl a
	asl a
	asl a
	asl a
	ora GameGenie.RealHex+1, x
	sta GameGenie.Scratchpad, y		; get code as DDAA-AAAA in byte format at Scratchpad+0/+1/+2/+3
	inx
	inx
	iny
	cpx #$0008				; 8 chars
	bne -

	ply

	lda GameGenie.Scratchpad
	sta GameGenie.Decoded, y		; DATA done

; ijklqrst opabcduv wxefghmn --> Genie ADDR (i = bit 23 ... n = bit 0)
; abcdefgh ijklmnop qrstuvwx --> SNES ADDR (a = bit 23 ... x = bit 0)

	rep #A_8BIT				; A = 16 bit

	lda GameGenie.Scratchpad+2		; wxefghmn opabcduv
	pha

	asl a					; xefghmno pabcduv0, w = carry bit
	rol GameGenie.Scratchpad+2		; xefghmno pabcduvw
	asl a					; efghmnop abcduv00, x = carry bit
	rol GameGenie.Scratchpad+2		; efghmnop abcduvwx

	pla					; wxefghmn opabcduv

	sep #A_8BIT				; A = 8 bit

	xba					; wxefghmn
	lsr a
	lsr a					; 00wxefgh
	sta temp

	lda GameGenie.Scratchpad+1		; ijklqrst
	sta temp+1

	rep #A_8BIT				; A = 16 bit

	lda #$0FF0
	trb temp				; ijkl0000 0000efgh

	lda GameGenie.Scratchpad+2		; efghmnop abcduvwx
	and #$0FF0				; 0000mnop abcd0000
	ora temp				; ijklmnop abcdefgh
	sta GameGenie.Decoded+1, y
	
	sep #A_8BIT				; A = 8 bit

	lda GameGenie.Scratchpad+1		; ijklqrst
	asl a
	asl a
	asl a
	asl a
	sta temp				; qrst0000

	lda GameGenie.Scratchpad+2		; abcduvwx
	and #%00001111				; 0000uvwx
	ora temp
	sta GameGenie.Decoded+3, y		; qrstuvwx

	PrintString "$"

	lda GameGenie.Decoded+1, y
	sta temp
	PrintHexNum temp

	lda GameGenie.Decoded+2, y
	sta temp
	PrintHexNum temp

	lda GameGenie.Decoded+3, y
	sta temp
	PrintHexNum temp

	PrintString " = $"

	lda GameGenie.Decoded, y
	sta temp
	PrintHexNum temp
rts



GameGenieNextChar:
	ldx GameGenie.CharOffset

	lda GameGenie.Codes, x
	inc a
	sta GameGenie.Codes, x
	cmp #$11
	bne +

	stz GameGenie.Codes, x

+	jump jGameGeniePrint
rts



GameGeniePrevChar:
	ldx GameGenie.CharOffset

	lda GameGenie.Codes, x
	dec a
	sta GameGenie.Codes, x
	cmp #$FF
	bne +

	lda #$10
	sta GameGenie.Codes, x

+	jump jGameGeniePrint
rts



GameGenieWriteCode:
	PrintString "GG-> "

	jump jGameGenieDecode

	lda GameGenie.Codes, y
	cmp #16
	beq GameGenieWriteCodeSkip

	lda GameGenie.Codes+1, y
	cmp #16
	beq GameGenieWriteCodeSkip

	lda GameGenie.Codes+2, y
	cmp #16
	beq GameGenieWriteCodeSkip

	lda GameGenie.Codes+3, y
	cmp #16
	beq GameGenieWriteCodeSkip

	lda GameGenie.Codes+4, y
	cmp #16
	beq GameGenieWriteCodeSkip

	lda GameGenie.Codes+5, y
	cmp #16
	beq GameGenieWriteCodeSkip

	lda GameGenie.Codes+6, y
	cmp #16
	beq GameGenieWriteCodeSkip

	lda GameGenie.Codes+7, y
	cmp #16
	beq GameGenieWriteCodeSkip

	bra GameGenieWriteCodeGood

GameGenieWriteCodeSkip:
	PrintString " code unused"

	jmp GameGenieWriteCodeDone

GameGenieWriteCodeGood:
	lda GameGenie.Decoded, y		; data
	sta ggcode

	lda GameGenie.Decoded+1, y		; bank
	sta ggcode+1

	lda GameGenie.Decoded+2, y		; high
	sta ggcode+2

	lda GameGenie.Decoded+3, y		; low
	sta ggcode+3

	lda #$00
	sta CONFIGWRITESRAMSIZE

	lda ggcode+1				; GG addr = bbhhll
	and #$F0
	lsr a
	lsr a					; bank index = bb >> 3  Fx->1F
	lsr a
	sta sourceLo

	PrintString " b="
	PrintHexNum sourceLo

	stz sourceHi

	ldx sourceLo
	lda gameBanks, x
	sta errorCode

	inx					; THIS IS WRONG?  SHOULD BE +$10?

	lda gameBanks, x
	sta errorCode+1

	lda errorCode

	sta CONFIGWRITEBANK+$1E0		; put indexed bank into F0low reg

	PrintString " blo="
	PrintHexNum errorCode

;	jsr Delay5Sec

	lda errorCode+1

	sta CONFIGWRITEBANK+$1F0		; put indexed bank into F0hi reg

	PrintString " bhi="
	PrintHexNum errorCode+1

;	jsr Delay5Sec

	lda ggcode+3
	sta destLo
	lda ggcode+2
	sta destHi
	lda ggcode+1
	and #$0F
	sta errorCode

	PrintHexNum errorCode
	PrintHexNum destHi
	PrintHexNum destLo

	ldx destLo
	lda errorCode
	cmp #$00
	beq GGF0
	cmp #$01
	beq GGF1
	cmp #$02
	beq GGF2
	cmp #$03
	beq GGF3
	cmp #$04
	beq GGF4
	cmp #$05
	beq GGF5
	cmp #$06
	beq GGF6
	cmp #$07
	beq GGF7
	bra GGFjump

GGF0:
	lda ggcode				; write GG data to F0hhll
	sta $F00000, x
	jmp GGCheck

GGF1:
	lda ggcode				; write GG data to F0hhll
	sta $F10000, x
	jmp GGCheck

GGF2:
	lda ggcode				; write GG data to F0hhll
	sta $F20000, x
	jmp GGCheck

GGF3:
	lda ggcode				; write GG data to F0hhll
	sta $F30000, x
	bra GGCheck

GGF4:
	lda ggcode				; write GG data to F0hhll
	sta $F40000, x
	bra GGCheck

GGF5:
	lda ggcode				; write GG data to F0hhll
	sta $F50000, x
	bra GGCheck

GGF6:
	lda ggcode				; write GG data to F0hhll
	sta $F60000, x
	bra GGCheck

GGF7:
	lda ggcode				; write GG data to F0hhll
	sta $F70000, x
	bra GGCheck

GGFjump:
	cmp #$08
	beq GGF8
	cmp #$09
	beq GGF9
	cmp #$0A
	beq GGFA
	cmp #$0B
	beq GGFB
	cmp #$0C
	beq GGFC
	cmp #$0D
	beq GGFD
	cmp #$0E
	beq GGFE
	cmp #$0F
	beq GGFF

GGF8:
	lda ggcode				; write GG data to F0hhll
	sta $F80000, x
	bra GGCheck

GGF9:
	lda ggcode				; write GG data to F0hhll
	sta $F90000, x
	bra GGCheck

GGFA:
	lda ggcode				; write GG data to F0hhll
	sta $FA0000, x
	bra GGCheck

GGFB:
	lda ggcode				; write GG data to F0hhll
	sta $FB0000, x
	bra GGCheck

GGFC:
	lda ggcode				; write GG data to F0hhll
	sta $FC0000, x
	bra GGCheck

GGFD:
	lda ggcode				; write GG data to F0hhll
	sta $FD0000, x
	bra GGCheck

GGFE:
	lda ggcode				; write GG data to F0hhll
	sta $FE0000, x
	bra GGCheck

GGFF:
	lda ggcode				; write GG data to F0hhll
	sta $FF0000, x
;	bra GGCheck

GGCheck:
	lda $F00000, x
	sta errorCode
	stx sourceLo

	lda sramSizeByte
	sta CONFIGWRITESRAMSIZE

	lda gameBanks+$1E
	sta CONFIGWRITEBANK+$1E0		; put bank back

	lda gameBanks+$1F
	sta CONFIGWRITEBANK+$1F0		; put bank back

GameGenieWriteCodeDone:

rts



; ************************ 5-second-long delay *************************

Delay5Sec:
	ldx #$00AC				; 172 frames, actually

-	wai
	dex
	bne -
rts



; **************************** SDRAM check *****************************

MemCheck:
	wai
	ClearLine 16
	ClearLine 17
	ClearLine 18

	SetCursorPos 16, 1
	PrintString "SDRAM check ..."

	SetCursorPos 22, 11
	PrintString " B: cancel"

	stz errorCode
	stz temp
	stz temp+1
	stz temp+2

	lda #$00
	sta DMAWRITEBANK
	sta DMAWRITELO
	sta DMAWRITEHI

	SetCursorPos 17, 1
	PrintString "Writing bank:"

MemCheckWriteLoop:
	lda Joy1New+1				; check for user input
	and #%10000000				; B button
	beq +

	SetCursorPos 16, 7
	PrintString "cancelled!"

	jsr Delay5Sec

	ClearLine 16
	ClearLine 17
rts

+	lda errorCode
	sta DMAREADDATA
	inc errorCode

	lda temp+2
	sta temp+3
	lda temp
	clc
	adc #$0F
	sta temp
	lda temp+1
	adc #$00
	sta temp+1
	lda temp+2
	adc #$00
	sta temp+2
	cmp temp+3
	beq MemCheckWriteLoopNext

	jsr MemCheckUpdateBank

MemCheckWriteLoopNext:
	lda temp+2
	cmp #$FF
	bne MemCheckWriteLoop

	stz errorCode
	stz temp
	stz temp+1
	stz temp+2

	lda #$00
	sta DMAWRITEBANK
	sta DMAWRITELO
	sta DMAWRITEHI

	SetCursorPos 17, 1
	PrintString "Read"			; "Reading bank:"

MemCheckReadLoop:
	lda Joy1New+1				; check for user input
	and #%10000000				; B button
	beq +

	SetCursorPos 16, 7
	PrintString "cancelled!"

	jsr Delay5Sec

	ClearLine 16
	ClearLine 17
rts

+	lda DMAREADDATA
	sta temp+4
	cmp errorCode
	bne MemCheckError

	inc errorCode

	lda temp+2
	sta temp+3
	lda temp
	clc
	adc #$0F
	sta temp
	lda temp+1
	adc #$00
	sta temp+1
	lda temp+2
	adc #$00
	sta temp+2
	cmp temp+3
	beq MemCheckReadLoopNext

	jsr MemCheckUpdateBank

MemCheckReadLoopNext:
	lda temp+2
	cmp #$FF
	bne MemCheckReadLoop

	SetCursorPos 16, 7
	PrintString "passed!"

	ClearLine 17
rts



MemCheckError:
	ClearLine 22				; remove cancel button hint

	SetCursorPos 16, 4
	PrintString "ERROR!   "			; "SDRAM ERROR!"

	SetCursorPos 17, 9
	PrintString ", read byte value: "
	PrintHexNum temp+4

	SetCursorPos 18, 1
	PrintString "Expected byte value: "
	PrintHexNum errorCode

	PrintString " at offset $"
	PrintHexNum temp+2
	PrintHexNum temp+1
	PrintHexNum temp

	SetCursorPos 19, 1
	PrintString "Trying again 4 times:"

	ldy #$0004				; 4 retries, the exact amount doesn't really matter ;-)

-	dey
	PrintString "  "

	lda temp+2
	sta DMAWRITEBANK
	lda temp+1
	sta DMAWRITEHI
	lda temp+0
	sta DMAWRITELO

	lda DMAREADDATA
	sta temp+4
	PrintHexNum temp+4

	cpy #$0000
	bne -

	SetCursorPos 21, 1
	PrintString "CRITICAL HARDWARE ERROR! PLEASE CHECK LOG.TXT!"

	jump jLogScreen				; save to LOG.TXT for later review

	jump jForever				; SDRAM error means potentially faulty PowerPak --> halt



MemCheckUpdateBank:
	SetCursorPos 17, 8
	PrintHexNum temp+2			; shows bank no. while writing/reading SDRAM
rts



; ************************* Clear "find entry" *************************

ClearFindEntry:
	lda #' '
	sta findEntry
	sta findEntry+1
	sta findEntry+2
	sta findEntry+3
	sta findEntry+4
	sta findEntry+5
	sta findEntry+6
	sta findEntry+7
rts



; ************************ Load ROM information ************************

CopyROMInfo:
	ldx #$0000

-	lda DMAREADDATA				; start at C0 (game title)
	cmp #$80				; check if character exceeds standard ASCII
	bcc +
	lda #'?'				; if so, replace character with question mark
+	sta tempEntry, x
	inx
	cpx #$0015				; 21 bytes
	bne -

	stz tempEntry, x			; NUL-terminate game title

	lda DMAREADDATA				; D5
	sta errorCode
	and #%11101111				; mask off fast/slow
	sta gameROMMapper
	PrintString "\nMode $"
	PrintHexNum errorCode

	lda DMAREADDATA				; D6
	sta gameROMType
	PrintString ", Type $"
	PrintHexNum gameROMType

	lda DMAREADDATA				; D7
;	sta gameROMSize				; ROM size is taken from file size!
	sta errorCode
	PrintString ", Size $"
	PrintHexNum errorCode

	lda DMAREADDATA				; D8
	sta saveSize
	PrintString ", SRAM $"
	PrintHexNum saveSize

	lda #$FC
	sta DMAWRITELO

	lda DMAREADDATA				; FC
	sta gameResetVector

	lda DMAREADDATA				; FD
	sta gameResetVector+1

	PrintString ", Reset $"
	PrintHexNum gameResetVector+1
	PrintHexNum gameResetVector
rts



CopyBanks:
	ldy #$0000

	lda [destLo], y
	sta CONFIGWRITEBANK+$010
	sta gameBanks+$01

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$030
	sta gameBanks+$03

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$050
	sta gameBanks+$05

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$070
	sta gameBanks+$07

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$090
	sta gameBanks+$09

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$0B0
	sta gameBanks+$0B

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$0D0
	sta gameBanks+$0D

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$0F0
	sta gameBanks+$0F

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$110
	sta gameBanks+$11

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$130
	sta gameBanks+$13

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$150
	sta gameBanks+$15

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$170
	sta gameBanks+$17

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$190
	sta gameBanks+$19

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$1B0
	sta gameBanks+$1B

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$1D0
	sta gameBanks+$1D

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$1F0
	sta gameBanks+$1F

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$000
	sta gameBanks+$00

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$020
	sta gameBanks+$02

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$040
	sta gameBanks+$04

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$060
	sta gameBanks+$06

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$080
	sta gameBanks+$08

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$0A0
	sta gameBanks+$0A

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$0C0
	sta gameBanks+$0C

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$0E0
	sta gameBanks+$0E

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$100
	sta gameBanks+$10

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$120
	sta gameBanks+$12

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$140
	sta gameBanks+$14

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$160
	sta gameBanks+$16

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$180
	sta gameBanks+$18

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$1A0
	sta gameBanks+$1A

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$1C0
	sta gameBanks+$1C

	iny

	lda [destLo], y
	sta CONFIGWRITEBANK+$1E0
	sta gameBanks+$1E
rts



PrintBanks:
	PrintString "\n0 1 2 3 4 5 6 7 8 9 A B C D E F\tBanks2\t    Banks3\n"

	ldx #$0001
	ldy #$0010

__PrintBanks1:
	lda gameBanks, x
	sta errorCode
	PrintHexNum errorCode
	inx
	inx
	dey
	bne __PrintBanks1

	PrintString "\t"

	ldx #$0008
	ldy #$0004

__PrintBanks2:
	lda gameBanks, x
	sta errorCode
	PrintHexNum errorCode
	inx
	inx
	dey
	bne __PrintBanks2

	PrintString "    "

	ldx #$0018
	ldy #$0004

__PrintBanks3:
	lda gameBanks, x
	sta errorCode
	PrintHexNum errorCode
	inx
	inx
	dey
	bne __PrintBanks3
rts



; ************************** Log error output **************************

LogScreen:
	FindFile "LOG.TXT"

	lda #$FF				; set WRAM text buffer address = $7EF7FF
	sta $2181
	lda #$F7
	sta $2182
	stz $2183

	ldx #$0000

__Copy2bppBuffer:
	lda $2180				; read byte (address is incremented automatically)
	lsr a					; reconvert hi-res tile data to plain ASCII characters
	sta LogBuffer, x			; put it to the log buffer
	inx					; increment counter
	inx					; skip a byte, to be filled during 4bpp loop
	cpx #$0800				; read 1024 bytes [sic, because of double "inx"]
	bne __Copy2bppBuffer

	ldx #$0000

__Copy4bppBuffer:
	lda $2180				; read byte (address has reached the 4bpp buffer by now)
	lsr a
	inx					; skip byte written during 2bpp loop
	sta LogBuffer, x
	inx					; increment counter
	cpx #$0800				; after another 1024 bytes, we're done :-)
	bne __Copy4bppBuffer

	lda #<LogBuffer
	sta sourceLo
	lda #>LogBuffer
	sta sourceHi
	lda #$7E
	sta sourceBank

	lda #kSourceWRAM
	sta sourceType

	jump jCardWriteFile
rts



; ************************ Copier header checks ************************

SWCHeaderCheck:					; Super WildCard headers contain "AABB04" (in hex) at offset $08-$0A
	lda sectorBuffer1+$08
	cmp #$AA
	bne __SWCHeaderCheckDone

	lda sectorBuffer1+$09
	cmp #$BB
	bne __SWCHeaderCheckDone

	lda sectorBuffer1+$0A
	cmp #$04
	bne __SWCHeaderCheckDone

	lda #$01
	sta headerType

__SWCHeaderCheckDone:

rts



GD3HeaderCheck:
	lda sectorBuffer1+$00			; check for "GAME"
	cmp #'G'
	bne __GD3HeaderCheckDone

	lda sectorBuffer1+$01
	cmp #'A'
	bne __GD3HeaderCheckDone

	lda sectorBuffer1+$02
	cmp #'M'
	bne __GD3HeaderCheckDone

	lda sectorBuffer1+$03
	cmp #'E'
	bne __GD3HeaderCheckDone

	lda #$02
	sta headerType

__GD3HeaderCheckDone:

rts



; ************************ Show loading message ************************

ShowLoadingMsg:
	lda #$FF				; hide cursor
	sta cursorX
	lda #$F0
	sta cursorY

	stz scrollY				; make sure prior scrolling won't affect string position
	stz scrollYCounter
	stz scrollYUp
	stz scrollYDown
	stz speedCounter
	stz speedScroll

	jump jPrintClearScreen

	SetCursorPos 11, 11
	PrintString "Loading ..."
rts



; ************************** Wait for Hblank ***************************

WaitHblank:
	pha

-	lda HVBJOY
	and #%01000000				; Hblank period flag
	cmp #%01000000
	bne -

	pla
rts



; *********************** Music loading routines ***********************

LoadDevMusic:
	sei					; disable NMI & IRQ before loading music
	stz REG_NMI_TIMEN

	jsr spcBoot				; boot SNESMOD

	lda #:SOUNDBANK				; give soundbank
	sta spc_bank

;	ldx #SOUNDBANK				; load module into SPC
	ldx #$0000

	jsr spcLoad

	lda #39					; allocate around 10K of sound ram (39 256-byte blocks)

	jsr spcAllocateSoundRegion

	lda #$81				; done, re-enable Vblank NMI + Auto Joypad Read
	sta REG_NMI_TIMEN
	cli
rts
