Compare commits

...

2 Commits

Author SHA1 Message Date
Randy Thiemann 4fa9f68107 Deploy new build. 2023-10-27 21:37:40 +02:00
Randy Thiemann 76fa7b0816 Code cleanup and documentation. 2023-10-27 21:37:32 +02:00
19 changed files with 340 additions and 281 deletions

Binary file not shown.

Binary file not shown.

View File

@ -25,7 +25,7 @@ INCLUDE "globals.asm"
SECTION "High Banking Variables", HRAM SECTION "High Banking Variables", HRAM
hBankBackup: ds 1 hBankBackup: ds 1
; 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, and 0x38
SECTION "Switch Bank", ROM0[$08] SECTION "Switch Bank", ROM0[$08]
; Saves the current bank and switches to the bank in b. ; Saves the current bank and switches to the bank in b.
RSTSwitchBank:: RSTSwitchBank::

View File

@ -23,6 +23,7 @@ INCLUDE "globals.asm"
SECTION "DMG Intro Effect", ROM0 SECTION "DMG Intro Effect", ROM0
; Does a small effect on boot with the nintendo logo.
DoDMGEffect:: DoDMGEffect::
ld a, [wInitialA] ld a, [wInitialA]
cp a, $11 cp a, $11

View File

@ -62,6 +62,10 @@ hShouldLockIfGrounded: ds 1
SECTION "Field Functions", ROM0 SECTION "Field Functions", ROM0
; Initializes the field completely blank.
; Initializes the combo counter to 1.
; Initializes the bravo counter to 0.
; Initializes the shadow field.
FieldInit:: FieldInit::
xor a, a xor a, a
ldh [hBravo], a ldh [hBravo], a
@ -69,14 +73,14 @@ FieldInit::
ldh [hComboCt], a ldh [hComboCt], a
ld hl, wField ld hl, wField
ld bc, 10*24 ld bc, 10*24
ld d, 1 ld d, TILE_BLANK
call UnsafeMemSet call UnsafeMemSet
ld hl, wShadowField ld hl, wShadowField
ld bc, 14*26 ld bc, 14*26
ld d, $FF ld d, $FF
jp UnsafeMemSet jp UnsafeMemSet
; Fills the field with the empty tile.
FieldClear:: FieldClear::
ld hl, wField ld hl, wField
ld bc, 10*24 ld bc, 10*24
@ -84,13 +88,16 @@ FieldClear::
jp UnsafeMemSet jp UnsafeMemSet
; Backs up the field.
; This backup field is used for pausing the game.
ToBackupField:: ToBackupField::
ld hl, wBackupField
ld de, wField ld de, wField
ld hl, wBackupField
ld bc, 10*24 ld bc, 10*24
jp UnsafeMemCopy jp UnsafeMemCopy
; Restores the backup of the field for ending pause mode.
FromBackupField:: FromBackupField::
ld hl, wField ld hl, wField
ld de, wBackupField ld de, wBackupField
@ -98,6 +105,8 @@ FromBackupField::
jp UnsafeMemCopy jp UnsafeMemCopy
; Copies the field to the shadow field.
; This shadow field is used to calculate whether or not the piece can fit.
ToShadowField:: ToShadowField::
ld hl, wField ld hl, wField
ld de, wShadowField+2 ld de, wShadowField+2
@ -119,6 +128,7 @@ ToShadowField::
ret ret
; Restores the shadow field to the main field.
FromShadowField: FromShadowField:
ld hl, wField ld hl, wField
ld de, wShadowField+2 ld de, wShadowField+2
@ -140,9 +150,10 @@ FromShadowField:
ret ret
; This routine will copy wField onto the screen. ; Blits the field onto the tile map.
; On the GBC, this chain calls into a special version that takes
; advantage of the GBC's CPU.
BlitField:: BlitField::
; Hold on, are we on a gbc?
ld a, [wInitialA] ld a, [wInitialA]
cp a, $11 cp a, $11
jp z, GBCBlitField jp z, GBCBlitField
@ -190,39 +201,39 @@ BlitField::
add hl, bc add hl, bc
ENDR ENDR
; This has to finish just before the first LCDC interrupt of the frame or stuff will break in weird ways. ; This function is actually called as the vblank handler for the gameplay state.
; This is why it jumps straight back to the event loop.
jp EventLoop jp EventLoop
; The current piece ID is used to get the offset into the rotation states
; corresponding to that piece's zero rotation.
SetPieceData: SetPieceData:
ldh a, [hCurrentPiece] ldh a, [hCurrentPiece]
sla a
sla a
sla a
sla a
ld c, a
ld b, 0
ld hl, sPieceRotationStates ld hl, sPieceRotationStates
ld de, 16 add hl, bc
: cp a, 0 ld a, l
jr z, :+
add hl, de
dec a
jr :-
: ld a, l
ldh [hPieceDataBase], a ldh [hPieceDataBase], a
ld a, h ld a, h
ldh [hPieceDataBase+1], a ldh [hPieceDataBase+1], a
ldh a, [hCurrentPiece]
ld hl, sPieceFastRotationStates ld hl, sPieceFastRotationStates
ld de, 16 add hl, bc
: cp a, 0 ld a, l
jr z, :+
add hl, de
dec a
jr :-
: ld a, l
ldh [hPieceDataBaseFast], a ldh [hPieceDataBaseFast], a
ld a, h ld a, h
ldh [hPieceDataBaseFast+1], a ldh [hPieceDataBaseFast+1], a
ret ret
; The rotation state is a further offset of 4 bytes.
SetPieceDataOffset: SetPieceDataOffset:
ldh a, [hCurrentPieceRotationState] ldh a, [hCurrentPieceRotationState]
sla a sla a
@ -248,7 +259,6 @@ XYToSFieldPtr:
ret ret
; Converts piece Y in B and a piece X in A to a pointer to the field in HL. ; Converts piece Y in B and a piece X in A to a pointer to the field in HL.
XYToFieldPtr: XYToFieldPtr:
ld hl, wField-2 ld hl, wField-2
@ -266,6 +276,9 @@ XYToFieldPtr:
ret ret
; This function makes HL point to the correct offset into the rotation data.
; This version of the data is used for thorough checking (T, J, and L have
; a middle column exception.)
GetPieceData: GetPieceData:
ldh a, [hPieceDataBase] ldh a, [hPieceDataBase]
ld l, a ld l, a
@ -273,12 +286,13 @@ GetPieceData:
ld h, a ld h, a
ldh a, [hPieceDataOffset] ldh a, [hPieceDataOffset]
ld c, a ld c, a
xor a, a ld b, 0
ld b, a
add hl, bc add hl, bc
ret ret
; Same as the above but for the fast data. This data is used when the exact
; cell that failed isn't important.
GetPieceDataFast: GetPieceDataFast:
ldh a, [hPieceDataBaseFast] ldh a, [hPieceDataBaseFast]
ld l, a ld l, a
@ -291,61 +305,12 @@ GetPieceDataFast:
add hl, bc add hl, bc
ret ret
; Checks if the piece can fit at the current position, but fast.
; HL should point to the piece's rotation state data.
; DE should be pointing to the right place in the SHADOW field.
CanPieceFitFast:
ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, $FF
ret
; Checks if the piece can fit at the current position. ; Checks if the piece can fit at the current position.
; HL should point to the piece's rotation state data. ; HL should point to the piece's rotation state data.
; DE should be pointing to the right place in the SHADOW field. ; DE should be pointing to the right place in the SHADOW field.
; This will return with $FF in A if the piece fits, or with the
; exact cell that caused the first failure in A.
CanPieceFit: CanPieceFit:
xor a, a xor a, a
ld b, a ld b, a
@ -503,6 +468,63 @@ CanPieceFit:
ret ret
; Checks if the piece can fit at the current position, but fast.
; HL should point to the piece's fast rotation state data.
; DE should be pointing to the right place in the SHADOW field.
; This will return with $FF in A if the piece fits, or with a non-$FF
; value if it doesn't.
CanPieceFitFast:
ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, $FF
ret
; This function will draw the piece even if it can't fit.
; We use this to draw a final failed spawn before going game
; over.
ForceSpawnPiece:: ForceSpawnPiece::
call SetPieceData call SetPieceData
call SetPieceDataOffset call SetPieceDataOffset
@ -513,8 +535,7 @@ ForceSpawnPiece::
ld d, h ld d, h
ld e, l ld e, l
call GetPieceData call GetPieceData
ld a, GAME_OVER_OTHER ld b, GAME_OVER_OTHER
ld b, a
push hl push hl
push de push de
pop hl pop hl
@ -522,6 +543,8 @@ ForceSpawnPiece::
jp DrawPiece jp DrawPiece
; Initialize the state for a new piece and attempts to spawn it.
; On return, A will be $FF if the piece fit.
TrySpawnPiece:: TrySpawnPiece::
; Always reset these for a new piece. ; Always reset these for a new piece.
xor a, a xor a, a
@ -708,6 +731,8 @@ FindMaxG:
ret ret
; This is the main function that will process input, gravity, and locking.
; It should be ran once per frame as long as lock delay is greater than 0.
FieldProcess:: FieldProcess::
; ************************************************************** ; **************************************************************
; SETUP ; SETUP
@ -836,7 +861,7 @@ FieldProcess::
cp a, PIECE_Z cp a, PIECE_Z
jr z, .trykickright jr z, .trykickright
; I piece only kicks in TGM3/TGW3/EASY/EAWY ; I piece only kicks in ARS2
cp a, PIECE_I cp a, PIECE_I
jr nz, :+ jr nz, :+
ld a, [wRotModeState] ld a, [wRotModeState]
@ -932,7 +957,7 @@ FieldProcess::
ldh [hLockDelayForce], a ldh [hLockDelayForce], a
jp .norot jp .norot
; In TGM3, TGW3, EASY, and EAWY modes, there are a few other kicks possible. ; In ARS2 mode, there are a few other kicks possible.
.maybetgm3rot .maybetgm3rot
ld a, [wRotModeState] ld a, [wRotModeState]
cp a, ROT_MODE_ARSTI cp a, ROT_MODE_ARSTI
@ -1228,6 +1253,7 @@ FieldProcess::
ld a, $FF ld a, $FF
ldh [hAwardDownBonus], a ldh [hAwardDownBonus], a
ld a, 20 ld a, 20
ldh [hWantedG], a
ld b, a ld b, a
ldh a, [hActualG] ldh a, [hActualG]
cp a, b cp a, b
@ -1260,7 +1286,7 @@ FieldProcess::
; Gravity? ; Gravity?
: ldh a, [hCurrentFractionalGravity] : ldh a, [hCurrentFractionalGravity]
cp a, $00 cp a, $00 ; 0 is the sentinel value that should be interpreted as "every frame"
jr z, :+ jr z, :+
ld b, a ld b, a
ldh a, [hGravityCtr] ldh a, [hGravityCtr]
@ -1443,7 +1469,7 @@ FieldProcess::
ldh a, [hWantedG] ldh a, [hWantedG]
cp a, 1 cp a, 1
jr nz, .postghost jr nz, .postghost
ld a, [wInitialA] ld a, [wInitialA] ; Let's not do the flickering on the GBC.
cp a, $11 cp a, $11
jr z, .ghost jr z, .ghost
ldh a, [hEvenFrame] ldh a, [hEvenFrame]
@ -1481,20 +1507,22 @@ FieldProcess::
cp a, b cp a, b
jr z, .drawpiece jr z, .drawpiece
; If we're not grounded, draw the piece normally.
ldh a, [hGrounded]
cp a, $FF
jr nz, .drawpiece
; If the lock delay is 0, draw the piece in the final color. ; If the lock delay is 0, draw the piece in the final color.
ldh a, [hCurrentPiece] ldh a, [hWantedTile]
ld b, TILE_PIECE_0+7 add a, 7
add a, b
ldh [hWantedTile], a ldh [hWantedTile], a
ldh a, [hCurrentLockDelayRemaining] ldh a, [hCurrentLockDelayRemaining]
cp a, 0 cp a, 0
jr z, .drawpiece jr z, .drawpiece
; If we're not grounded, draw the piece normally.
ldh a, [hWantedTile]
sub a, 7
ldh [hWantedTile], a
ldh a, [hGrounded]
cp a, $FF
jr nz, .drawpiece
; Otherwise, look it up. ; Otherwise, look it up.
call GetTileShade call GetTileShade
@ -1515,7 +1543,7 @@ FieldProcess::
call DrawPiece call DrawPiece
ret ret
; Performs a lookup to see how "locked" the piece is.
GetTileShade: GetTileShade:
ldh a, [hCurrentLockDelay] ldh a, [hCurrentLockDelay]
cp a, 30 cp a, 30
@ -1623,6 +1651,8 @@ GetTileShade:
ret ret
; This is called every frame after a piece has been locked until the delay state ends.
; Lines are cleared, levels and score are awarded, and ARE time is waited out.
FieldDelay:: FieldDelay::
; Switch on the delay state. ; Switch on the delay state.
ld a, [wDelayState] ld a, [wDelayState]
@ -1661,7 +1691,7 @@ FieldDelay::
and a, d and a, d
cp a, $FF cp a, $FF
jr z, .skip jr z, .skip
ld a, DELAY_STATE_LINE_PRE_CLEAR ; If there were line clears, do a line clear delay, then an ARE delay. ld a, DELAY_STATE_LINE_PRE_CLEAR ; If there were line clears, do a line clear delay, then a LINE_ARE delay.
ld [wDelayState], a ld [wDelayState], a
ldh a, [hCurrentLineClearDelay] ldh a, [hCurrentLineClearDelay]
ldh [hRemainingDelay], a ldh [hRemainingDelay], a
@ -1804,7 +1834,7 @@ FieldDelay::
; Line clear delay. ; Line clear delay.
; Count down the delay. If we're out of delay, clear the lines and go to ARE. ; Count down the delay. If we're out of delay, clear the lines and go to LINE_ARE.
.lineclear .lineclear
ldh a, [hRemainingDelay] ldh a, [hRemainingDelay]
dec a dec a
@ -1858,12 +1888,12 @@ FieldDelay::
ldh [hCurrentPiece], a ldh [hCurrentPiece], a
call GetNextPiece call GetNextPiece
; Kill the sound for the next piece. ; Kill the sound for the next piece.
call SFXKill jp SFXKill
ret
; Shifts B into the line clear list.
; Also increments the line clear count.
AppendClearedLine: AppendClearedLine:
ldh a, [hLineClearCt] ldh a, [hLineClearCt]
inc a inc a
@ -1879,6 +1909,8 @@ AppendClearedLine:
ret ret
; Scans the field for lines that are completely filled with non-empty spaces.
; Every time one is found, it is added to a list.
FindClearedLines: FindClearedLines:
xor a, a xor a, a
ldh [hLineClearCt], a ldh [hLineClearCt], a
@ -1914,7 +1946,7 @@ FindClearedLines:
ret ret
; Goes through the list of cleared lines and marks those lines with the "line clear" tile.
MarkClear: MarkClear:
ldh a, [hClearedLines] ldh a, [hClearedLines]
cp a, $FF cp a, $FF
@ -1966,10 +1998,10 @@ MarkClear:
jr nz, :- jr nz, :-
ld bc, 10 ld bc, 10
ld d, TILE_CLEARING ld d, TILE_CLEARING
call UnsafeMemSet jp UnsafeMemSet
ret
; Once again, scans the field for cleared lines, but this time removes them.
ClearLines: ClearLines:
ld de, 0 ld de, 0

View File

@ -38,9 +38,11 @@ DEF R3 EQU %0000000000011111
SECTION "GBC Shadow Tilemap", WRAM0, ALIGN[8] SECTION "GBC Shadow Tilemap", WRAM0, ALIGN[8]
wShadowTilemap:: ds 32*32 wShadowTilemap:: ds 32*32
SECTION "GBC Shadow Tile Attributes", WRAM0, ALIGN[8] SECTION "GBC Shadow Tile Attributes", WRAM0, ALIGN[8]
wShadowTileAttrs:: ds 32*32 wShadowTileAttrs:: ds 32*32
SECTION "GBC Variables", WRAM0 SECTION "GBC Variables", WRAM0
wOuterReps:: ds 1 wOuterReps:: ds 1
wInnerReps:: ds 1 wInnerReps:: ds 1
@ -48,6 +50,7 @@ wTitlePal:: ds 1
SECTION "GBC Functions", ROM0 SECTION "GBC Functions", ROM0
; Copies the shadow tile attribute map to vram using instant HDMA.
ToATTR:: ToATTR::
ld a, [wInitialA] ld a, [wInitialA]
cp a, $11 cp a, $11
@ -71,38 +74,7 @@ ToATTR::
ret ret
ToVRAM:: ; Sets up GBC registers for the title state.
; Bank 1
ld a, 1
ldh [rVBK], a
ld a, HIGH(wShadowTileAttrs)
ldh [rHDMA1], a
ld a, LOW(wShadowTileAttrs)
ldh [rHDMA2], a
ld a, HIGH($9800)
ldh [rHDMA3], a
ld a, LOW($9800)
ldh [rHDMA4], a
ld a, 40
ldh [rHDMA5], a
; Bank 0
ld a, 0
ldh [rVBK], a
ld a, HIGH(wShadowTilemap)
ldh [rHDMA1], a
ld a, LOW(wShadowTilemap)
ldh [rHDMA2], a
ld a, HIGH($9800)
ldh [rHDMA3], a
ld a, LOW($9800)
ldh [rHDMA4], a
ld a, 39 | $80
ldh [rHDMA5], a
jp EventLoop
GBCTitleInit:: GBCTitleInit::
ld a, [wInitialA] ld a, [wInitialA]
cp a, $11 cp a, $11
@ -384,7 +356,7 @@ GBCTitleInit::
ld [wTitlePal], a ld [wTitlePal], a
ret ret
; Sets the GBC registers for the gameplay state.
GBCGameplayInit:: GBCGameplayInit::
ld a, [wInitialA] ld a, [wInitialA]
cp a, $11 cp a, $11
@ -678,6 +650,7 @@ GBCGameplayInit::
ret ret
; Additional GBC effects for the title screen process state.
GBCTitleProcess:: GBCTitleProcess::
ld a, [wInitialA] ld a, [wInitialA]
cp a, $11 cp a, $11
@ -725,10 +698,10 @@ GBCTitleProcess::
ld a, 3 ld a, 3
ld d, a ld d, a
ld bc, 32 ld bc, 32
call UnsafeMemSet jp UnsafeMemSet
ret
; Additional GBC effects for the gameplay process state.
GBCGameplayProcess:: GBCGameplayProcess::
ld a, [wInitialA] ld a, [wInitialA]
cp a, $11 cp a, $11
@ -946,8 +919,38 @@ GBCGameplayProcess::
ret ret
; Copies the shadow tile maps to VRAM using HDMA. The attributes are copied using instant mode
; The tile data is done using hblank mode.
GBCBlitField:: GBCBlitField::
jp ToVRAM ToVRAM::
; Bank 1
ld a, 1
ldh [rVBK], a
ld a, HIGH(wShadowTileAttrs)
ldh [rHDMA1], a
ld a, LOW(wShadowTileAttrs)
ldh [rHDMA2], a
ld a, HIGH($9800)
ldh [rHDMA3], a
ld a, LOW($9800)
ldh [rHDMA4], a
ld a, 40
ldh [rHDMA5], a
; Bank 0
ld a, 0
ldh [rVBK], a
ld a, HIGH(wShadowTilemap)
ldh [rHDMA1], a
ld a, LOW(wShadowTilemap)
ldh [rHDMA2], a
ld a, HIGH($9800)
ldh [rHDMA3], a
ld a, LOW($9800)
ldh [rHDMA4], a
ld a, 39 | $80
ldh [rHDMA5], a
jp EventLoop
ENDC ENDC

View File

@ -35,6 +35,7 @@ hSelectState:: ds 1
SECTION "Input Functions", ROM0 SECTION "Input Functions", ROM0
; Zeroes out all button states.
InputInit:: InputInit::
xor a, a xor a, a
ldh [hUpState], a ldh [hUpState], a
@ -48,6 +49,9 @@ InputInit::
ret ret
; Gets the current state of all buttons.
; Held buttons are incremented. Buttons that aren't held are reset to 0.
; Left/Right cause Up/Down to be reset as well.
GetInput:: GetInput::
; Get the button state. ; Get the button state.
.btns .btns

View File

@ -27,12 +27,13 @@ hLCDCCtr:: ds 1
SECTION "Interrupt Initialization Functions", ROM0 SECTION "Interrupt Initialization Functions", ROM0
; Zeroes out the interrupt counter.
IntrInit:: IntrInit::
xor a, a xor a, a
ldh [hLCDCCtr], a ldh [hLCDCCtr], a
ret ret
; Sets up the STAT interrupt.
InitializeLCDCInterrupt:: InitializeLCDCInterrupt::
ld a, STATF_LYC ld a, STATF_LYC
ldh [rSTAT], a ldh [rSTAT], a
@ -49,6 +50,8 @@ InitializeLCDCInterrupt::
SECTION "LCDC Interrupt", ROM0[INT_HANDLER_STAT] SECTION "LCDC Interrupt", ROM0[INT_HANDLER_STAT]
; This interrupt handler will be called every 7 scanlines, scrolling up the tile map by 1 line. This has the
; effect of making the tiles appear as 8x7 pixels, and making 20 rows fit on the screen.
LCDCInterrupt: LCDCInterrupt:
push af push af
push hl push hl

View File

@ -41,22 +41,11 @@ hPrevHundreds:: ds 1
SECTION "Level Functions", ROM0 SECTION "Level Functions", ROM0
; Loads the initial state of the speed curve.
LevelInit:: LevelInit::
xor a, a xor a, a
ldh [hLevel], a
ldh [hLevel+1], a
ldh [hCLevel], a
ldh [hCLevel+1], a
ldh [hCLevel+2], a
ldh [hCLevel+3], a
ldh [hNLevel], a
ldh [hNLevel+2], a ; Note +1 is inited later.
ldh [hNLevel+3], a
ldh [hRequiresLineClear], a ldh [hRequiresLineClear], a
ld a, 1
ldh [hNLevel+1], a
ldh a, [hStartSpeed] ldh a, [hStartSpeed]
ld l, a ld l, a
ldh a, [hStartSpeed+1] ldh a, [hStartSpeed+1]
@ -109,8 +98,8 @@ LevelInit::
and a, $0F and a, $0F
ldh [hNLevel], a ldh [hNLevel], a
call DoSpeedUp jp DoSpeedUp
ret
; Increment level and speed up if necessary. Level increment in E. ; Increment level and speed up if necessary. Level increment in E.
; Levels may only increment by single digits. ; Levels may only increment by single digits.
@ -199,8 +188,7 @@ LevelUp::
ldh [hLevel+1], a ldh [hLevel+1], a
call DoSpeedUp call DoSpeedUp
ld a, SFX_RANKUP ld a, SFX_RANKUP
call SFXEnqueue jp SFXEnqueue
ret
.checknlevel .checknlevel
; Make wNLevel make sense. ; Make wNLevel make sense.
@ -306,16 +294,14 @@ LevelUp::
: ldh a, [hNextSpeedUp+1] : ldh a, [hNextSpeedUp+1]
and a, $0F and a, $0F
jr z, :+ jr z, DoSpeedUp
ld hl, hCLevel+3 ld hl, hCLevel+3
cp a, [hl] cp a, [hl]
jr z, :+ jr z, DoSpeedUp
ret nc ret nc ; This can fall through to the next function here. This is intentional.
: call DoSpeedUp
ret
; Iterates over the speed curve and loads the new constants.
DoSpeedUp: DoSpeedUp:
; Load curve ptr. ; Load curve ptr.
ldh a, [hSpeedCurvePtr] ldh a, [hSpeedCurvePtr]

View File

@ -39,10 +39,6 @@ wAlways20GState:: ds 1
wInitialA:: ds 1 wInitialA:: ds 1
wInitialB:: ds 1 wInitialB:: ds 1
wInitialC:: ds 1 wInitialC:: ds 1
wInitialD:: ds 1
wInitialE:: ds 1
wInitialH:: ds 1
wInitialL:: ds 1
SECTION "Stack", WRAM0 SECTION "Stack", WRAM0
@ -52,6 +48,7 @@ wStackEnd::
SECTION "Code Entry Point", ROM0 SECTION "Code Entry Point", ROM0
; Main entry point. Does some set up and then goes into an infinite event loop initialized on the title screen.
Main:: Main::
; Load the initial registers. For reasons. ; Load the initial registers. For reasons.
ld [wInitialA], a ld [wInitialA], a
@ -59,14 +56,6 @@ Main::
ld [wInitialB], a ld [wInitialB], a
ld a, c ld a, c
ld [wInitialC], a ld [wInitialC], a
ld a, d
ld [wInitialD], a
ld a, e
ld [wInitialE], a
ld a, h
ld [wInitialH], a
ld a, l
ld [wInitialL], a
; Let the DMG have some fun with the initial screen. ; Let the DMG have some fun with the initial screen.
call DoDMGEffect call DoDMGEffect
@ -128,6 +117,7 @@ Main::
call SwitchToTitle call SwitchToTitle
; Event loop time!
EventLoop:: EventLoop::
; Play the sound effect, if any. ; Play the sound effect, if any.
call SFXPlay call SFXPlay

View File

@ -23,7 +23,7 @@ INCLUDE "globals.asm"
SECTION "Memory Functions", ROM0 SECTION "Memory Functions", ROM0
; Copies data from de to hl, bc bytes ; Copies data from de to hl, bc bytes. Doesn't check for vram access.
UnsafeMemCopy:: UnsafeMemCopy::
ld a, [de] ld a, [de]
ld [hl+], a ld [hl+], a
@ -35,7 +35,7 @@ UnsafeMemCopy::
ret ret
; Copies data from de to hl, bc bytes ; Copies data from de to hl, bc bytes. Checks for vram access.
SafeMemCopy:: SafeMemCopy::
wait_vram wait_vram
ld a, [de] ld a, [de]
@ -47,7 +47,8 @@ SafeMemCopy::
jr nz, SafeMemCopy jr nz, SafeMemCopy
ret ret
; Sets memory from hl to hl+bc to d
; Sets memory from hl to hl+bc to d. Doesn't check for vram access.
UnsafeMemSet:: UnsafeMemSet::
ld [hl], d ld [hl], d
inc hl inc hl
@ -57,6 +58,8 @@ UnsafeMemSet::
jr nz, UnsafeMemSet jr nz, UnsafeMemSet
ret ret
; Sets memory from hl to hl+bc to d. Checks for vram access.
SafeMemSet:: SafeMemSet::
wait_vram wait_vram
ld [hl], d ld [hl], d

View File

@ -38,6 +38,7 @@ wTGM3WorstDroughtIdx: ds 1
section "RNG Functions", ROM0 section "RNG Functions", ROM0
; Snapshots the initial seed for a game, then initializes the history and piece queue.
RNGInit:: RNGInit::
; Do some bit fuckery on the seed using the gameboy's free-running timers. ; Do some bit fuckery on the seed using the gameboy's free-running timers.
ldh a, [rDIV] ldh a, [rDIV]
@ -112,8 +113,7 @@ RNGInit::
; Generate the next 2 to fill up the queue. ; Generate the next 2 to fill up the queue.
call GetNextPiece call GetNextPiece
call GetNextPiece jp GetNextPiece
ret
; Shift the generated piece into the history and save it. ; Shift the generated piece into the history and save it.
@ -354,21 +354,27 @@ GetNextTGM3Piece:
ld [hl], a ld [hl], a
ret ret
; Gets the next piece depending on RNG mode.
GetNextPiece:: GetNextPiece::
ld hl, .nextpiecejumps
ld a, [wRNGModeState] ld a, [wRNGModeState]
cp a, RNG_MODE_HELL ld b, 0
jp z, GetNextHellPiece ld c, a
cp a, RNG_MODE_TGM1 add a, c
jp z, GetNextTGM1Piece add a, c
cp a, RNG_MODE_TGM2 ld c, a
jp z, GetNextTGM2Piece add hl, bc
cp a, RNG_MODE_TGM3 jp hl
jp z, GetNextTGM3Piece
cp a, RNG_MODE_NES .nextpiecejumps
jp z, GetNextNesPiece jp GetNextTGM1Piece
jp GetNextTGM2Piece
jp GetNextTGM3Piece
jp GetNextHellPiece
jp GetNextNesPiece
; Tries generating bytes until it gets one in [0; 35)
Next35Piece: Next35Piece:
: call NextByte : call NextByte
and a, $3F and a, $3F
@ -377,6 +383,7 @@ Next35Piece:
ret ret
; Tries generating bytes until it gets one in [0; 7)
Next7Piece: Next7Piece:
: call NextByte : call NextByte
and a, $07 and a, $07
@ -385,6 +392,7 @@ Next7Piece:
ret ret
; Cyrcles the RNG returning a random byte in a.
NextByte: NextByte:
; Load seed ; Load seed
ld hl, hRNGSeed+3 ld hl, hRNGSeed+3

View File

@ -30,6 +30,7 @@ hScoreIncrementHead:: ds 1
SECTION "Score Functions", ROM0 SECTION "Score Functions", ROM0
; Wipes the score.
ScoreInit:: ScoreInit::
xor a, a xor a, a
ldh [hScore], a ldh [hScore], a
@ -49,6 +50,7 @@ ScoreInit::
ldh [hScoreIncrementBCD+5], a ldh [hScoreIncrementBCD+5], a
ret ret
; Increases the current score by the amount in wScoreIncrement. ; Increases the current score by the amount in wScoreIncrement.
IncreaseScore:: IncreaseScore::
; Wipe the old BCD score. ; Wipe the old BCD score.
@ -186,8 +188,7 @@ IncreaseScore::
xor a, a xor a, a
ldh [hScore], a ldh [hScore], a
ld a, SFX_RANKUP ld a, SFX_RANKUP
call SFXEnqueue jp SFXEnqueue
ret
ENDC ENDC

View File

@ -70,8 +70,9 @@ hNoisePlayhead:: ds 2
SECTION "SFX Functions", ROM0 SECTION "SFX Functions", ROM0
SFXInit::
; Audio on, volume on, and enable all channels. ; Audio on, volume on, and enable all channels.
; Zeroes out all playheads and the queue.
SFXInit::
ld a, $80 ld a, $80
ldh [rNR52], a ldh [rNR52], a
ld a, $FF ld a, $FF
@ -92,8 +93,8 @@ SFXInit::
ret ret
SFXPopQueue:
; Pop the head of the queue into A, the tail of the queue will be set to $FF. ; Pop the head of the queue into A, the tail of the queue will be set to $FF.
SFXPopQueue:
ldh a, [hPlayQueue] ldh a, [hPlayQueue]
ld b, a ld b, a
ldh a, [hPlayQueue+1] ldh a, [hPlayQueue+1]
@ -108,8 +109,8 @@ SFXPopQueue:
ret ret
SFXPushQueue:
; Push A onto the tail of the queue, the head of the queue will be pushed off. ; Push A onto the tail of the queue, the head of the queue will be pushed off.
SFXPushQueue:
ld b, a ld b, a
ldh a, [hPlayQueue+1] ldh a, [hPlayQueue+1]
ldh [hPlayQueue], a ldh [hPlayQueue], a
@ -122,18 +123,18 @@ SFXPushQueue:
ret ret
; Process the queue, if there's more to play, it will do so.
SFXProcessQueue: SFXProcessQueue:
; Clear the playhead. ; Clear the playhead.
xor a, a xor a, a
ldh [hPlayhead], a ldh [hPlayhead], a
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
; Music will just repeat.
ldh a, [hPlayQueue] ldh a, [hPlayQueue]
cp a, MUSIC_MENU cp a, MUSIC_MENU
jr nz, :+ jr nz, :+
call SFXEnqueue jr SFXEnqueue
ret
; Try 4 times to pop a sound effect off the queue. ; Try 4 times to pop a sound effect off the queue.
: call SFXPopQueue : call SFXPopQueue
@ -150,10 +151,10 @@ SFXProcessQueue:
ret z ret z
; If we got a valid sound effect, then play it. ; If we got a valid sound effect, then play it.
call SFXEnqueue jr SFXEnqueue
ret
; Noise effects use their own playhead that can play at the same time as the normal queue.
SFXTriggerNoise:: SFXTriggerNoise::
cp a, SFX_LINE_CLEAR cp a, SFX_LINE_CLEAR
jr nz, :+ jr nz, :+
@ -191,8 +192,7 @@ SFXEnqueue::
or a, l or a, l
jr z, :+ jr z, :+
ld a, b ld a, b
call SFXPushQueue jr SFXPushQueue
ret
; Menu music ; Menu music
: ld a, b : ld a, b
@ -203,9 +203,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sMusicMenu) ld a, HIGH(sMusicMenu)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
; Piece jingles. ; Piece jingles.
: ld a, b : ld a, b
@ -215,8 +213,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceI) ld a, HIGH(sSFXPieceI)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_I | SFX_IRS cp a, PIECE_I | SFX_IRS
@ -225,8 +222,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceIRSI) ld a, HIGH(sSFXPieceIRSI)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_S cp a, PIECE_S
@ -235,8 +231,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceS) ld a, HIGH(sSFXPieceS)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_S | SFX_IRS cp a, PIECE_S | SFX_IRS
@ -245,8 +240,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceIRSS) ld a, HIGH(sSFXPieceIRSS)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_Z cp a, PIECE_Z
@ -255,8 +249,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceZ) ld a, HIGH(sSFXPieceZ)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_Z | SFX_IRS cp a, PIECE_Z | SFX_IRS
@ -265,8 +258,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceIRSZ) ld a, HIGH(sSFXPieceIRSZ)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_J cp a, PIECE_J
@ -275,8 +267,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceJ) ld a, HIGH(sSFXPieceJ)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_J | SFX_IRS cp a, PIECE_J | SFX_IRS
@ -285,8 +276,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceIRSJ) ld a, HIGH(sSFXPieceIRSJ)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_L cp a, PIECE_L
@ -295,8 +285,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceL) ld a, HIGH(sSFXPieceL)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_L | SFX_IRS cp a, PIECE_L | SFX_IRS
@ -305,8 +294,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceIRSL) ld a, HIGH(sSFXPieceIRSL)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_O cp a, PIECE_O
@ -315,8 +303,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceO) ld a, HIGH(sSFXPieceO)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_O | SFX_IRS cp a, PIECE_O | SFX_IRS
@ -325,8 +312,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceIRSO) ld a, HIGH(sSFXPieceIRSO)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_T cp a, PIECE_T
@ -335,8 +321,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceT) ld a, HIGH(sSFXPieceT)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: ld a, b : ld a, b
cp a, PIECE_T | SFX_IRS cp a, PIECE_T | SFX_IRS
@ -345,9 +330,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXPieceIRST) ld a, HIGH(sSFXPieceIRST)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
; IRS ; IRS
: cp a, SFX_IHS : cp a, SFX_IHS
@ -356,8 +339,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXIHS) ld a, HIGH(sSFXIHS)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
: cp a, SFX_IHS | SFX_IRS : cp a, SFX_IHS | SFX_IRS
jr nz, :+ jr nz, :+
@ -365,9 +347,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXIHSIRS) ld a, HIGH(sSFXIHSIRS)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jp SFXPlay
ret
; Leveling ; Leveling
: cp a, SFX_LEVELLOCK : cp a, SFX_LEVELLOCK
@ -376,8 +356,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXLevelLock) ld a, HIGH(sSFXLevelLock)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jr SFXPlay
ret
: cp a, SFX_LEVELUP : cp a, SFX_LEVELUP
jr nz, :+ jr nz, :+
@ -385,9 +364,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXLevelUp) ld a, HIGH(sSFXLevelUp)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jr SFXPlay
ret
; Other ; Other
: cp a, SFX_RANKUP : cp a, SFX_RANKUP
@ -396,8 +373,7 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXRankUp) ld a, HIGH(sSFXRankUp)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jr SFXPlay
ret
: cp a, SFX_READYGO : cp a, SFX_READYGO
ret nz ret nz
@ -405,10 +381,9 @@ SFXEnqueue::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, HIGH(sSFXReadyGo) ld a, HIGH(sSFXReadyGo)
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
call SFXPlay jr SFXPlay
ret
; Kill the non-noise sound and clear the queue.
SFXKill:: SFXKill::
; Kill all sound without pops. ; Kill all sound without pops.
ld a, %00111111 ld a, %00111111
@ -416,12 +391,10 @@ SFXKill::
ldh [rNR21], a ldh [rNR21], a
ld a, $FF ld a, $FF
ldh [rNR31], a ldh [rNR31], a
;ldh [rNR41], a
ld a, %01000000 ld a, %01000000
ldh [rNR14], a ldh [rNR14], a
ldh [rNR24], a ldh [rNR24], a
ldh [rNR34], a ldh [rNR34], a
;ldh [rNR44], a
; Clear the queue. ; Clear the queue.
ld a, $FF ld a, $FF
@ -435,6 +408,8 @@ SFXKill::
ret ret
; Play routine for the noise channel.
; Must be called every frame.
SFXPlayNoise:: SFXPlayNoise::
; Get the noise playhead. ; Get the noise playhead.
ldh a, [hNoisePlayhead] ldh a, [hNoisePlayhead]
@ -484,11 +459,11 @@ SFXPlayNoise::
ldh [hNoisePlayhead], a ldh [hNoisePlayhead], a
ld a, h ld a, h
ldh [hNoisePlayhead+1], a ldh [hNoisePlayhead+1], a
rst RSTRestoreBank jp RSTRestoreBank
ret
; This play routine must be called every frame. ; Play routine for the regular sfx channels.
; Must be called every frame.
SFXPlay:: SFXPlay::
; Bank to correct bank. ; Bank to correct bank.
ldh a, [hPlayQueue] ldh a, [hPlayQueue]
@ -510,8 +485,7 @@ SFXPlay::
; Nothing to do if it's a null ptr. ; Nothing to do if it's a null ptr.
or a, l or a, l
jr nz, .getRegister jr nz, .getRegister
rst RSTRestoreBank jp RSTRestoreBank
ret
; Otherwise, get the register to write to. ; Otherwise, get the register to write to.
.getRegister .getRegister
@ -522,8 +496,7 @@ SFXPlay::
cp a, $FE cp a, $FE
jr nz, :+ jr nz, :+
rst RSTRestoreBank rst RSTRestoreBank
call SFXProcessQueue jp SFXProcessQueue
ret
; If it's $FF, then we're done for this frame. ; If it's $FF, then we're done for this frame.
: cp a, $FF : cp a, $FF
@ -546,8 +519,7 @@ SFXPlay::
ldh [hPlayhead], a ldh [hPlayhead], a
ld a, h ld a, h
ldh [hPlayhead+1], a ldh [hPlayhead+1], a
rst RSTRestoreBank jp RSTRestoreBank
ret
ENDC ENDC

View File

@ -90,14 +90,15 @@ OAMDMAEnd::
SECTION "OAM Functions", ROM0 SECTION "OAM Functions", ROM0
; Copies the OAM handler to HRAM.
CopyOAMHandler:: CopyOAMHandler::
ld de, OAMDMA ld de, OAMDMA
ld hl, hOAMDMA ld hl, hOAMDMA
ld bc, OAMDMAEnd - OAMDMA ld bc, OAMDMAEnd - OAMDMA
call UnsafeMemCopy jp UnsafeMemCopy
ret
; Clears OAM and shadow OAM.
ClearOAM:: ClearOAM::
ld hl, _OAMRAM ld hl, _OAMRAM
ld bc, $9F ld bc, $9F
@ -106,12 +107,12 @@ ClearOAM::
ld hl, wShadowOAM ld hl, wShadowOAM
ld bc, $9F ld bc, $9F
ld d, 0 ld d, 0
call SafeMemSet jp UnsafeMemSet
ret
SECTION "Domain Specific Functions", ROM0 SECTION "Domain Specific Functions", ROM0
; Puts the mode tells into sprites and displays them.
ApplyTells:: ApplyTells::
ld a, TELLS_BASE_Y ld a, TELLS_BASE_Y
ld [wSPRModeRNG], a ld [wSPRModeRNG], a
@ -155,6 +156,7 @@ ApplyTells::
ret ret
; Draws the next pieces as a sprite.
; Index of next piece in A. ; Index of next piece in A.
ApplyNext:: ApplyNext::
; Correct color ; Correct color
@ -219,7 +221,6 @@ ApplyNext::
add a, NEXT_BASE_Y add a, NEXT_BASE_Y
ld [wSPRNext4+0], a ld [wSPRNext4+0], a
; Queue ; Queue
ld a, QUEUE_BASE_Y ld a, QUEUE_BASE_Y
ld [wSPRQueue1A], a ld [wSPRQueue1A], a
@ -254,17 +255,10 @@ ApplyNext::
ld [wSPRQueue2B+2], a ld [wSPRQueue2B+2], a
ret ret
; Index of hold piece in A.
ApplyHold::
cp 255
jr nz, .doApplyHold
ld hl, wSPRHold1
ld bc, 16
ld d, 0
call UnsafeMemSet
ret
.doApplyHold ; Draws the held piece.
; Index of held piece in A.
ApplyHold::
; Correct color ; Correct color
ld [wSPRHold1+3], a ld [wSPRHold1+3], a
ld [wSPRHold2+3], a ld [wSPRHold2+3], a
@ -282,7 +276,6 @@ ApplyHold::
ld a, b ld a, b
jr z, .show jr z, .show
.hide .hide
ld b, a ld b, a
ld a, TILE_BLANK ld a, TILE_BLANK
@ -350,6 +343,7 @@ ApplyHold::
ret ret
; Generic function to draw a BCD number (6 digits) as 6 sprites.
; Address of first sprite in hl. ; Address of first sprite in hl.
; Address of first digit in de. ; Address of first digit in de.
ApplyNumbers:: ApplyNumbers::
@ -390,11 +384,10 @@ ApplyNumbers::
ld a, [de] ld a, [de]
add a, TILE_0 add a, TILE_0
ld [hl], a ld [hl], a
add hl, bc
inc de
ret ret
; Positions all number sprites for gameplay.
SetNumberSpritePositions:: SetNumberSpritePositions::
ld a, SCORE_BASE_X ld a, SCORE_BASE_X
ld hl, wSPRScore1 ld hl, wSPRScore1

View File

@ -34,8 +34,9 @@ rSelectedStartLevel:: ds 2
SECTION "SRAM Functions", ROM0 SECTION "SRAM Functions", ROM0
RestoreSRAM::
; Check if our SRAM is initialized and of the correct version. ; Check if our SRAM is initialized and of the correct version.
; Restores it if so, otherwise initializes it.
RestoreSRAM::
ld a, [rCheck] ld a, [rCheck]
cp a, LOW(__UTC_YEAR__) cp a, LOW(__UTC_YEAR__)
jr nz, InitializeSRAM jr nz, InitializeSRAM
@ -75,6 +76,7 @@ RestoreSRAM::
ldh [hStartSpeed+1], a ldh [hStartSpeed+1], a
ret ret
; Initializes SRAM with default values.
InitializeSRAM: InitializeSRAM:
; Set the magic id. ; Set the magic id.
ld a, LOW(__UTC_YEAR__) ld a, LOW(__UTC_YEAR__)
@ -125,4 +127,5 @@ InitializeSRAM:
ld [rSelectedStartLevel+1], a ld [rSelectedStartLevel+1], a
ret ret
ENDC ENDC

View File

@ -48,6 +48,7 @@ hRequestedJingle: ds 1
SECTION "Gameplay Functions", ROM0 SECTION "Gameplay Functions", ROM0
; Change to game play mode. The event loop will call the event loop and vblank handlers for this mode after this returns.
SwitchToGameplay:: SwitchToGameplay::
; Turn the screen off if it's on. ; Turn the screen off if it's on.
ldh a, [rLCDC] ldh a, [rLCDC]
@ -113,6 +114,7 @@ SwitchToGameplay::
ret ret
; Main gameplay event loop.
GamePlayEventLoopHandler:: GamePlayEventLoopHandler::
; What mode are we in? ; What mode are we in?
ld hl, .modejumps ld hl, .modejumps
@ -134,6 +136,7 @@ GamePlayEventLoopHandler::
jp preGameOverMode jp preGameOverMode
jp pauseMode jp pauseMode
; Draw "READY" and wait a bit. ; Draw "READY" and wait a bit.
leadyMode: leadyMode:
ldh a, [hModeCounter] ldh a, [hModeCounter]
@ -359,6 +362,7 @@ delayMode:
: jp drawStaticInfo : jp drawStaticInfo
preGameOverMode: preGameOverMode:
; Spawn the failed piece. ; Spawn the failed piece.
call ForceSpawnPiece call ForceSpawnPiece
@ -578,6 +582,7 @@ drawStaticInfo:
jp EventLoopPostHandler jp EventLoopPostHandler
; Do the hold action.
DoHold: DoHold:
; Mark hold as spent. ; Mark hold as spent.
ld a, $FF ld a, $FF

View File

@ -27,6 +27,7 @@ wSelected:: ds 1
SECTION "Title Functions", ROM0 SECTION "Title Functions", ROM0
; Change to title mode. The event loop will call the event loop and vblank handlers for this mode after this returns.
SwitchToTitle:: SwitchToTitle::
; Turn the screen off if it's on. ; Turn the screen off if it's on.
ldh a, [rLCDC] ldh a, [rLCDC]
@ -136,6 +137,7 @@ SwitchToTitle::
ret ret
; Handles title screen input.
TitleEventLoopHandler:: TitleEventLoopHandler::
call GBCTitleProcess call GBCTitleProcess
@ -209,6 +211,8 @@ TitleEventLoopHandler::
.done .done
jp EventLoopPostHandler jp EventLoopPostHandler
; Decrements the currently selected option.
DecrementOption: DecrementOption:
.opt0 .opt0
ld a, [wSelected] ld a, [wSelected]
@ -307,6 +311,8 @@ DecrementOption:
jr DecrementLevel jr DecrementLevel
jp EventLoopPostHandler jp EventLoopPostHandler
; Decrements start level.
DecrementLevel: DecrementLevel:
; Decrement ; Decrement
ldh a, [hStartSpeed] ldh a, [hStartSpeed]
@ -324,6 +330,7 @@ DecrementLevel:
jp CheckLevelRange jp CheckLevelRange
; Increments the selected option.
IncrementOption: IncrementOption:
.opt0 .opt0
ld a, [wSelected] ld a, [wSelected]
@ -422,6 +429,8 @@ IncrementOption:
jr IncrementLevel jr IncrementLevel
jp EventLoopPostHandler jp EventLoopPostHandler
; Increments start level.
IncrementLevel: IncrementLevel:
; Increment ; Increment
ldh a, [hStartSpeed] ldh a, [hStartSpeed]
@ -438,11 +447,11 @@ IncrementLevel:
ld [rSelectedStartLevel+1], a ld [rSelectedStartLevel+1], a
jp CheckLevelRange jp CheckLevelRange
; Wipes the start level upon selecting a new speed curve.
InitSpeedCurve: InitSpeedCurve:
ld a, [wSpeedCurveState] ld a, [wSpeedCurveState]
call GetStart call GetStart
.set
ld a, l ld a, l
ldh [hStartSpeed], a ldh [hStartSpeed], a
ld [rSelectedStartLevel], a ld [rSelectedStartLevel], a
@ -452,7 +461,7 @@ InitSpeedCurve:
ret ret
; Gets the end of a speed curve.
GetEnd: GetEnd:
ld a, [wSpeedCurveState] ld a, [wSpeedCurveState]
cp a, SCURVE_DMGT cp a, SCURVE_DMGT
@ -478,6 +487,8 @@ GetEnd:
: ld bc, sCHILSpeedCurveEnd : ld bc, sCHILSpeedCurveEnd
ret ret
; Gets the beginning of a speed curve.
GetStart: GetStart:
ld a, [wSpeedCurveState] ld a, [wSpeedCurveState]
cp a, SCURVE_DMGT cp a, SCURVE_DMGT
@ -503,6 +514,8 @@ GetStart:
: ld hl, sCHILSpeedCurve : ld hl, sCHILSpeedCurve
ret ret
; Make sure we don't overflow the level range.
CheckLevelRange: CheckLevelRange:
; At end? ; At end?
call GetEnd call GetEnd
@ -547,6 +560,7 @@ CheckLevelRange:
jp EventLoopPostHandler jp EventLoopPostHandler
; Handles the display of the menu.
TitleVBlankHandler:: TitleVBlankHandler::
call ToATTR call ToATTR

View File

@ -27,22 +27,63 @@ hFrameCtr:: ds 1
hEvenFrame:: ds 1 hEvenFrame:: ds 1
SECTION "Time Variables", WRAM0
wMinutes:: ds 1
wSeconds:: ds 1
wFrames:: ds 1
SECTION "Time Functions", ROM0 SECTION "Time Functions", ROM0
; Zeroes all timers and gets the free-running timer ticking.
TimeInit:: TimeInit::
xor a, a xor a, a
ldh [rTMA], a ldh [rTMA], a
ldh [hEvenFrame], a ldh [hEvenFrame], a
ldh [hFrameCtr], a ldh [hFrameCtr], a
ld [wMinutes], a
ld [wSeconds], a
ld [wFrames], a
ld a, TACF_262KHZ | TACF_START ld a, TACF_262KHZ | TACF_START
ldh [rTAC], a ldh [rTAC], a
ret ret
; Resets the minute-second timer.
ResetTime::
xor a, a
ld [wMinutes], a
ld [wSeconds], a
ld [wFrames], a
ret
; Increments the global timer. Also saves whether we're on an even frame.
HandleTimers:: HandleTimers::
ldh a, [hFrameCtr] ldh a, [hFrameCtr]
inc a inc a
ldh [hFrameCtr], a ldh [hFrameCtr], a
and 1 and 1
ldh [hEvenFrame], a ldh [hEvenFrame], a
ld a, [wFrames]
inc a
ld [wFrames], a
cp a, 60
ret nz
xor a, a
ld [wFrames], a
ld a, [wSeconds]
inc a
ld [wSeconds], a
cp a, 60
ret nz
xor a, a
ld [wSeconds], a
ld a, [wMinutes]
inc a
ld [wMinutes], a
ret ret