diff --git a/bin/DMGTRIS.GBC b/bin/DMGTRIS.GBC index 35ad783..5c5d58d 100644 Binary files a/bin/DMGTRIS.GBC and b/bin/DMGTRIS.GBC differ diff --git a/src/banking.asm b/src/banking.asm index a0ee652..1a621f2 100644 --- a/src/banking.asm +++ b/src/banking.asm @@ -25,7 +25,7 @@ INCLUDE "globals.asm" SECTION "High Banking Variables", HRAM hBankBackup: ds 1 -; 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, and 0x38 + SECTION "Switch Bank", ROM0[$08] ; Saves the current bank and switches to the bank in b. RSTSwitchBank:: diff --git a/src/dmgfx.asm b/src/dmgfx.asm index d1665c6..48bb236 100644 --- a/src/dmgfx.asm +++ b/src/dmgfx.asm @@ -23,6 +23,7 @@ INCLUDE "globals.asm" SECTION "DMG Intro Effect", ROM0 + ; Does a small effect on boot with the nintendo logo. DoDMGEffect:: ld a, [wInitialA] cp a, $11 diff --git a/src/field.asm b/src/field.asm index 2edd26a..4e17cc6 100644 --- a/src/field.asm +++ b/src/field.asm @@ -62,6 +62,10 @@ hShouldLockIfGrounded: ds 1 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:: xor a, a ldh [hBravo], a @@ -69,14 +73,14 @@ FieldInit:: ldh [hComboCt], a ld hl, wField ld bc, 10*24 - ld d, 1 + ld d, TILE_BLANK call UnsafeMemSet ld hl, wShadowField ld bc, 14*26 ld d, $FF jp UnsafeMemSet - + ; Fills the field with the empty tile. FieldClear:: ld hl, wField ld bc, 10*24 @@ -84,13 +88,16 @@ FieldClear:: jp UnsafeMemSet + ; Backs up the field. + ; This backup field is used for pausing the game. ToBackupField:: - ld hl, wBackupField ld de, wField + ld hl, wBackupField ld bc, 10*24 jp UnsafeMemCopy + ; Restores the backup of the field for ending pause mode. FromBackupField:: ld hl, wField ld de, wBackupField @@ -98,6 +105,8 @@ FromBackupField:: jp UnsafeMemCopy + ; Copies the field to the shadow field. + ; This shadow field is used to calculate whether or not the piece can fit. ToShadowField:: ld hl, wField ld de, wShadowField+2 @@ -119,6 +128,7 @@ ToShadowField:: ret + ; Restores the shadow field to the main field. FromShadowField: ld hl, wField ld de, wShadowField+2 @@ -140,9 +150,10 @@ FromShadowField: 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:: - ; Hold on, are we on a gbc? ld a, [wInitialA] cp a, $11 jp z, GBCBlitField @@ -190,39 +201,39 @@ BlitField:: add hl, bc 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 + ; The current piece ID is used to get the offset into the rotation states + ; corresponding to that piece's zero rotation. SetPieceData: ldh a, [hCurrentPiece] + sla a + sla a + sla a + sla a + ld c, a + ld b, 0 + ld hl, sPieceRotationStates - ld de, 16 -: cp a, 0 - jr z, :+ - add hl, de - dec a - jr :- -: ld a, l + add hl, bc + ld a, l ldh [hPieceDataBase], a ld a, h ldh [hPieceDataBase+1], a - ldh a, [hCurrentPiece] ld hl, sPieceFastRotationStates - ld de, 16 -: cp a, 0 - jr z, :+ - add hl, de - dec a - jr :- -: ld a, l + add hl, bc + ld a, l ldh [hPieceDataBaseFast], a ld a, h ldh [hPieceDataBaseFast+1], a ret + ; The rotation state is a further offset of 4 bytes. SetPieceDataOffset: ldh a, [hCurrentPieceRotationState] sla a @@ -248,7 +259,6 @@ XYToSFieldPtr: ret - ; Converts piece Y in B and a piece X in A to a pointer to the field in HL. XYToFieldPtr: ld hl, wField-2 @@ -266,6 +276,9 @@ XYToFieldPtr: 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: ldh a, [hPieceDataBase] ld l, a @@ -273,12 +286,13 @@ GetPieceData: ld h, a ldh a, [hPieceDataOffset] ld c, a - xor a, a - ld b, a + ld b, 0 add hl, bc ret + ; Same as the above but for the fast data. This data is used when the exact + ; cell that failed isn't important. GetPieceDataFast: ldh a, [hPieceDataBaseFast] ld l, a @@ -291,61 +305,12 @@ GetPieceDataFast: add hl, bc 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. ; HL should point to the piece's 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 the + ; exact cell that caused the first failure in A. CanPieceFit: xor a, a ld b, a @@ -503,6 +468,63 @@ CanPieceFit: 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:: call SetPieceData call SetPieceDataOffset @@ -513,8 +535,7 @@ ForceSpawnPiece:: ld d, h ld e, l call GetPieceData - ld a, GAME_OVER_OTHER - ld b, a + ld b, GAME_OVER_OTHER push hl push de pop hl @@ -522,6 +543,8 @@ ForceSpawnPiece:: 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:: ; Always reset these for a new piece. xor a, a @@ -708,6 +731,8 @@ FindMaxG: 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:: ; ************************************************************** ; SETUP @@ -836,7 +861,7 @@ FieldProcess:: cp a, PIECE_Z jr z, .trykickright - ; I piece only kicks in TGM3/TGW3/EASY/EAWY + ; I piece only kicks in ARS2 cp a, PIECE_I jr nz, :+ ld a, [wRotModeState] @@ -932,7 +957,7 @@ FieldProcess:: ldh [hLockDelayForce], a 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 ld a, [wRotModeState] cp a, ROT_MODE_ARSTI @@ -1228,6 +1253,7 @@ FieldProcess:: ld a, $FF ldh [hAwardDownBonus], a ld a, 20 + ldh [hWantedG], a ld b, a ldh a, [hActualG] cp a, b @@ -1260,7 +1286,7 @@ FieldProcess:: ; Gravity? : ldh a, [hCurrentFractionalGravity] - cp a, $00 + cp a, $00 ; 0 is the sentinel value that should be interpreted as "every frame" jr z, :+ ld b, a ldh a, [hGravityCtr] @@ -1443,7 +1469,7 @@ FieldProcess:: ldh a, [hWantedG] cp a, 1 jr nz, .postghost - ld a, [wInitialA] + ld a, [wInitialA] ; Let's not do the flickering on the GBC. cp a, $11 jr z, .ghost ldh a, [hEvenFrame] @@ -1481,20 +1507,22 @@ FieldProcess:: cp a, b 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. - ldh a, [hCurrentPiece] - ld b, TILE_PIECE_0+7 - add a, b + ldh a, [hWantedTile] + add a, 7 ldh [hWantedTile], a ldh a, [hCurrentLockDelayRemaining] cp a, 0 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. call GetTileShade @@ -1515,7 +1543,7 @@ FieldProcess:: call DrawPiece ret - + ; Performs a lookup to see how "locked" the piece is. GetTileShade: ldh a, [hCurrentLockDelay] cp a, 30 @@ -1623,6 +1651,8 @@ GetTileShade: 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:: ; Switch on the delay state. ld a, [wDelayState] @@ -1661,7 +1691,7 @@ FieldDelay:: and a, d cp a, $FF 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 ldh a, [hCurrentLineClearDelay] ldh [hRemainingDelay], a @@ -1804,7 +1834,7 @@ FieldDelay:: ; 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 ldh a, [hRemainingDelay] dec a @@ -1858,12 +1888,12 @@ FieldDelay:: ldh [hCurrentPiece], a call GetNextPiece - ; Kill the sound for the next piece. - call SFXKill - ret + jp SFXKill + ; Shifts B into the line clear list. + ; Also increments the line clear count. AppendClearedLine: ldh a, [hLineClearCt] inc a @@ -1879,6 +1909,8 @@ AppendClearedLine: 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: xor a, a ldh [hLineClearCt], a @@ -1914,7 +1946,7 @@ FindClearedLines: ret - + ; Goes through the list of cleared lines and marks those lines with the "line clear" tile. MarkClear: ldh a, [hClearedLines] cp a, $FF @@ -1966,10 +1998,10 @@ MarkClear: jr nz, :- ld bc, 10 ld d, TILE_CLEARING - call UnsafeMemSet - ret + jp UnsafeMemSet + ; Once again, scans the field for cleared lines, but this time removes them. ClearLines: ld de, 0 diff --git a/src/gbc.asm b/src/gbc.asm index ae71675..4e5f960 100644 --- a/src/gbc.asm +++ b/src/gbc.asm @@ -38,9 +38,11 @@ DEF R3 EQU %0000000000011111 SECTION "GBC Shadow Tilemap", WRAM0, ALIGN[8] wShadowTilemap:: ds 32*32 + SECTION "GBC Shadow Tile Attributes", WRAM0, ALIGN[8] wShadowTileAttrs:: ds 32*32 + SECTION "GBC Variables", WRAM0 wOuterReps:: ds 1 wInnerReps:: ds 1 @@ -48,6 +50,7 @@ wTitlePal:: ds 1 SECTION "GBC Functions", ROM0 + ; Copies the shadow tile attribute map to vram using instant HDMA. ToATTR:: ld a, [wInitialA] cp a, $11 @@ -71,38 +74,7 @@ ToATTR:: ret -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 - - + ; Sets up GBC registers for the title state. GBCTitleInit:: ld a, [wInitialA] cp a, $11 @@ -384,7 +356,7 @@ GBCTitleInit:: ld [wTitlePal], a ret - + ; Sets the GBC registers for the gameplay state. GBCGameplayInit:: ld a, [wInitialA] cp a, $11 @@ -678,6 +650,7 @@ GBCGameplayInit:: ret + ; Additional GBC effects for the title screen process state. GBCTitleProcess:: ld a, [wInitialA] cp a, $11 @@ -725,10 +698,10 @@ GBCTitleProcess:: ld a, 3 ld d, a ld bc, 32 - call UnsafeMemSet - ret + jp UnsafeMemSet + ; Additional GBC effects for the gameplay process state. GBCGameplayProcess:: ld a, [wInitialA] cp a, $11 @@ -946,8 +919,38 @@ GBCGameplayProcess:: 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:: - 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 diff --git a/src/input.asm b/src/input.asm index c8bb681..3199b9a 100644 --- a/src/input.asm +++ b/src/input.asm @@ -35,6 +35,7 @@ hSelectState:: ds 1 SECTION "Input Functions", ROM0 + ; Zeroes out all button states. InputInit:: xor a, a ldh [hUpState], a @@ -48,6 +49,9 @@ InputInit:: 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:: ; Get the button state. .btns diff --git a/src/interrupts.asm b/src/interrupts.asm index d9c18bb..f5de576 100644 --- a/src/interrupts.asm +++ b/src/interrupts.asm @@ -27,12 +27,13 @@ hLCDCCtr:: ds 1 SECTION "Interrupt Initialization Functions", ROM0 + ; Zeroes out the interrupt counter. IntrInit:: xor a, a ldh [hLCDCCtr], a ret - + ; Sets up the STAT interrupt. InitializeLCDCInterrupt:: ld a, STATF_LYC ldh [rSTAT], a @@ -49,6 +50,8 @@ InitializeLCDCInterrupt:: 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: push af push hl diff --git a/src/level.asm b/src/level.asm index 250fd43..b3d03c1 100644 --- a/src/level.asm +++ b/src/level.asm @@ -41,22 +41,11 @@ hPrevHundreds:: ds 1 SECTION "Level Functions", ROM0 + ; Loads the initial state of the speed curve. LevelInit:: 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 - ld a, 1 - ldh [hNLevel+1], a - ldh a, [hStartSpeed] ld l, a ldh a, [hStartSpeed+1] @@ -109,8 +98,8 @@ LevelInit:: and a, $0F ldh [hNLevel], a - call DoSpeedUp - ret + jp DoSpeedUp + ; Increment level and speed up if necessary. Level increment in E. ; Levels may only increment by single digits. @@ -199,8 +188,7 @@ LevelUp:: ldh [hLevel+1], a call DoSpeedUp ld a, SFX_RANKUP - call SFXEnqueue - ret + jp SFXEnqueue .checknlevel ; Make wNLevel make sense. @@ -306,16 +294,14 @@ LevelUp:: : ldh a, [hNextSpeedUp+1] and a, $0F - jr z, :+ + jr z, DoSpeedUp ld hl, hCLevel+3 cp a, [hl] - jr z, :+ - ret nc - -: call DoSpeedUp - ret + jr z, DoSpeedUp + ret nc ; This can fall through to the next function here. This is intentional. + ; Iterates over the speed curve and loads the new constants. DoSpeedUp: ; Load curve ptr. ldh a, [hSpeedCurvePtr] diff --git a/src/main.asm b/src/main.asm index 9012594..b7000d4 100644 --- a/src/main.asm +++ b/src/main.asm @@ -39,10 +39,6 @@ wAlways20GState:: ds 1 wInitialA:: ds 1 wInitialB:: ds 1 wInitialC:: ds 1 -wInitialD:: ds 1 -wInitialE:: ds 1 -wInitialH:: ds 1 -wInitialL:: ds 1 SECTION "Stack", WRAM0 @@ -52,6 +48,7 @@ wStackEnd:: 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:: ; Load the initial registers. For reasons. ld [wInitialA], a @@ -59,14 +56,6 @@ Main:: ld [wInitialB], a ld a, c 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. call DoDMGEffect @@ -128,6 +117,7 @@ Main:: call SwitchToTitle + ; Event loop time! EventLoop:: ; Play the sound effect, if any. call SFXPlay diff --git a/src/memory.asm b/src/memory.asm index c43033c..2498619 100644 --- a/src/memory.asm +++ b/src/memory.asm @@ -23,7 +23,7 @@ INCLUDE "globals.asm" 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:: ld a, [de] ld [hl+], a @@ -35,7 +35,7 @@ UnsafeMemCopy:: ret -; Copies data from de to hl, bc bytes + ; Copies data from de to hl, bc bytes. Checks for vram access. SafeMemCopy:: wait_vram ld a, [de] @@ -47,7 +47,8 @@ SafeMemCopy:: jr nz, SafeMemCopy 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:: ld [hl], d inc hl @@ -57,6 +58,8 @@ UnsafeMemSet:: jr nz, UnsafeMemSet ret + + ; Sets memory from hl to hl+bc to d. Checks for vram access. SafeMemSet:: wait_vram ld [hl], d diff --git a/src/rng.asm b/src/rng.asm index e608320..c50c297 100644 --- a/src/rng.asm +++ b/src/rng.asm @@ -38,6 +38,7 @@ wTGM3WorstDroughtIdx: ds 1 section "RNG Functions", ROM0 + ; Snapshots the initial seed for a game, then initializes the history and piece queue. RNGInit:: ; Do some bit fuckery on the seed using the gameboy's free-running timers. ldh a, [rDIV] @@ -112,8 +113,7 @@ RNGInit:: ; Generate the next 2 to fill up the queue. call GetNextPiece - call GetNextPiece - ret + jp GetNextPiece ; Shift the generated piece into the history and save it. @@ -354,21 +354,27 @@ GetNextTGM3Piece: ld [hl], a ret - + ; Gets the next piece depending on RNG mode. GetNextPiece:: + ld hl, .nextpiecejumps ld a, [wRNGModeState] - cp a, RNG_MODE_HELL - jp z, GetNextHellPiece - cp a, RNG_MODE_TGM1 - jp z, GetNextTGM1Piece - cp a, RNG_MODE_TGM2 - jp z, GetNextTGM2Piece - cp a, RNG_MODE_TGM3 - jp z, GetNextTGM3Piece - cp a, RNG_MODE_NES - jp z, GetNextNesPiece + ld b, 0 + ld c, a + add a, c + add a, c + ld c, a + add hl, bc + jp hl + +.nextpiecejumps + jp GetNextTGM1Piece + jp GetNextTGM2Piece + jp GetNextTGM3Piece + jp GetNextHellPiece + jp GetNextNesPiece + ; Tries generating bytes until it gets one in [0; 35) Next35Piece: : call NextByte and a, $3F @@ -377,6 +383,7 @@ Next35Piece: ret + ; Tries generating bytes until it gets one in [0; 7) Next7Piece: : call NextByte and a, $07 @@ -385,6 +392,7 @@ Next7Piece: ret + ; Cyrcles the RNG returning a random byte in a. NextByte: ; Load seed ld hl, hRNGSeed+3 diff --git a/src/score.asm b/src/score.asm index ad1d670..8138848 100644 --- a/src/score.asm +++ b/src/score.asm @@ -30,6 +30,7 @@ hScoreIncrementHead:: ds 1 SECTION "Score Functions", ROM0 + ; Wipes the score. ScoreInit:: xor a, a ldh [hScore], a @@ -49,6 +50,7 @@ ScoreInit:: ldh [hScoreIncrementBCD+5], a ret + ; Increases the current score by the amount in wScoreIncrement. IncreaseScore:: ; Wipe the old BCD score. @@ -186,8 +188,7 @@ IncreaseScore:: xor a, a ldh [hScore], a ld a, SFX_RANKUP - call SFXEnqueue - ret + jp SFXEnqueue ENDC diff --git a/src/sfx.asm b/src/sfx.asm index 17d5ed4..3bc1413 100644 --- a/src/sfx.asm +++ b/src/sfx.asm @@ -70,8 +70,9 @@ hNoisePlayhead:: ds 2 SECTION "SFX Functions", ROM0 -SFXInit:: ; Audio on, volume on, and enable all channels. + ; Zeroes out all playheads and the queue. +SFXInit:: ld a, $80 ldh [rNR52], a ld a, $FF @@ -92,8 +93,8 @@ SFXInit:: ret -SFXPopQueue: ; Pop the head of the queue into A, the tail of the queue will be set to $FF. +SFXPopQueue: ldh a, [hPlayQueue] ld b, a ldh a, [hPlayQueue+1] @@ -108,8 +109,8 @@ SFXPopQueue: ret -SFXPushQueue: ; Push A onto the tail of the queue, the head of the queue will be pushed off. +SFXPushQueue: ld b, a ldh a, [hPlayQueue+1] ldh [hPlayQueue], a @@ -122,18 +123,18 @@ SFXPushQueue: ret + ; Process the queue, if there's more to play, it will do so. SFXProcessQueue: ; Clear the playhead. xor a, a ldh [hPlayhead], a ldh [hPlayhead+1], a + ; Music will just repeat. ldh a, [hPlayQueue] cp a, MUSIC_MENU jr nz, :+ - call SFXEnqueue - ret - + jr SFXEnqueue ; Try 4 times to pop a sound effect off the queue. : call SFXPopQueue @@ -150,10 +151,10 @@ SFXProcessQueue: ret z ; If we got a valid sound effect, then play it. - call SFXEnqueue - ret + jr SFXEnqueue + ; Noise effects use their own playhead that can play at the same time as the normal queue. SFXTriggerNoise:: cp a, SFX_LINE_CLEAR jr nz, :+ @@ -191,8 +192,7 @@ SFXEnqueue:: or a, l jr z, :+ ld a, b - call SFXPushQueue - ret + jr SFXPushQueue ; Menu music : ld a, b @@ -203,9 +203,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sMusicMenu) ldh [hPlayhead+1], a - call SFXPlay - ret - + jp SFXPlay ; Piece jingles. : ld a, b @@ -215,8 +213,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceI) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_I | SFX_IRS @@ -225,8 +222,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceIRSI) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_S @@ -235,8 +231,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceS) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_S | SFX_IRS @@ -245,8 +240,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceIRSS) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_Z @@ -255,8 +249,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceZ) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_Z | SFX_IRS @@ -265,8 +258,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceIRSZ) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_J @@ -275,8 +267,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceJ) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_J | SFX_IRS @@ -285,8 +276,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceIRSJ) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_L @@ -295,8 +285,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceL) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_L | SFX_IRS @@ -305,8 +294,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceIRSL) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_O @@ -315,8 +303,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceO) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_O | SFX_IRS @@ -325,8 +312,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceIRSO) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_T @@ -335,8 +321,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceT) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : ld a, b cp a, PIECE_T | SFX_IRS @@ -345,9 +330,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXPieceIRST) ldh [hPlayhead+1], a - call SFXPlay - ret - + jp SFXPlay ; IRS : cp a, SFX_IHS @@ -356,8 +339,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXIHS) ldh [hPlayhead+1], a - call SFXPlay - ret + jp SFXPlay : cp a, SFX_IHS | SFX_IRS jr nz, :+ @@ -365,9 +347,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXIHSIRS) ldh [hPlayhead+1], a - call SFXPlay - ret - + jp SFXPlay ; Leveling : cp a, SFX_LEVELLOCK @@ -376,8 +356,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXLevelLock) ldh [hPlayhead+1], a - call SFXPlay - ret + jr SFXPlay : cp a, SFX_LEVELUP jr nz, :+ @@ -385,9 +364,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXLevelUp) ldh [hPlayhead+1], a - call SFXPlay - ret - + jr SFXPlay ; Other : cp a, SFX_RANKUP @@ -396,8 +373,7 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXRankUp) ldh [hPlayhead+1], a - call SFXPlay - ret + jr SFXPlay : cp a, SFX_READYGO ret nz @@ -405,10 +381,9 @@ SFXEnqueue:: ldh [hPlayhead], a ld a, HIGH(sSFXReadyGo) ldh [hPlayhead+1], a - call SFXPlay - ret - + jr SFXPlay + ; Kill the non-noise sound and clear the queue. SFXKill:: ; Kill all sound without pops. ld a, %00111111 @@ -416,12 +391,10 @@ SFXKill:: ldh [rNR21], a ld a, $FF ldh [rNR31], a - ;ldh [rNR41], a ld a, %01000000 ldh [rNR14], a ldh [rNR24], a ldh [rNR34], a - ;ldh [rNR44], a ; Clear the queue. ld a, $FF @@ -435,6 +408,8 @@ SFXKill:: ret + ; Play routine for the noise channel. + ; Must be called every frame. SFXPlayNoise:: ; Get the noise playhead. ldh a, [hNoisePlayhead] @@ -484,11 +459,11 @@ SFXPlayNoise:: ldh [hNoisePlayhead], a ld a, h ldh [hNoisePlayhead+1], a - rst RSTRestoreBank - ret + jp RSTRestoreBank - ; This play routine must be called every frame. + ; Play routine for the regular sfx channels. + ; Must be called every frame. SFXPlay:: ; Bank to correct bank. ldh a, [hPlayQueue] @@ -510,8 +485,7 @@ SFXPlay:: ; Nothing to do if it's a null ptr. or a, l jr nz, .getRegister - rst RSTRestoreBank - ret + jp RSTRestoreBank ; Otherwise, get the register to write to. .getRegister @@ -522,8 +496,7 @@ SFXPlay:: cp a, $FE jr nz, :+ rst RSTRestoreBank - call SFXProcessQueue - ret + jp SFXProcessQueue ; If it's $FF, then we're done for this frame. : cp a, $FF @@ -546,8 +519,7 @@ SFXPlay:: ldh [hPlayhead], a ld a, h ldh [hPlayhead+1], a - rst RSTRestoreBank - ret + jp RSTRestoreBank ENDC diff --git a/src/sprites.asm b/src/sprites.asm index aa8c665..143988d 100644 --- a/src/sprites.asm +++ b/src/sprites.asm @@ -90,14 +90,15 @@ OAMDMAEnd:: SECTION "OAM Functions", ROM0 + ; Copies the OAM handler to HRAM. CopyOAMHandler:: ld de, OAMDMA ld hl, hOAMDMA ld bc, OAMDMAEnd - OAMDMA - call UnsafeMemCopy - ret + jp UnsafeMemCopy + ; Clears OAM and shadow OAM. ClearOAM:: ld hl, _OAMRAM ld bc, $9F @@ -106,12 +107,12 @@ ClearOAM:: ld hl, wShadowOAM ld bc, $9F ld d, 0 - call SafeMemSet - ret + jp UnsafeMemSet SECTION "Domain Specific Functions", ROM0 + ; Puts the mode tells into sprites and displays them. ApplyTells:: ld a, TELLS_BASE_Y ld [wSPRModeRNG], a @@ -155,7 +156,8 @@ ApplyTells:: ret -; Index of next piece in A. + ; Draws the next pieces as a sprite. + ; Index of next piece in A. ApplyNext:: ; Correct color ld [wSPRNext1+3], a @@ -219,7 +221,6 @@ ApplyNext:: add a, NEXT_BASE_Y ld [wSPRNext4+0], a - ; Queue ld a, QUEUE_BASE_Y ld [wSPRQueue1A], a @@ -254,17 +255,10 @@ ApplyNext:: ld [wSPRQueue2B+2], a 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 ld [wSPRHold1+3], a ld [wSPRHold2+3], a @@ -282,7 +276,6 @@ ApplyHold:: ld a, b jr z, .show - .hide ld b, a ld a, TILE_BLANK @@ -350,8 +343,9 @@ ApplyHold:: ret -; Address of first sprite in hl. -; Address of first digit in de. + ; Generic function to draw a BCD number (6 digits) as 6 sprites. + ; Address of first sprite in hl. + ; Address of first digit in de. ApplyNumbers:: inc hl inc hl @@ -390,11 +384,10 @@ ApplyNumbers:: ld a, [de] add a, TILE_0 ld [hl], a - add hl, bc - inc de ret + ; Positions all number sprites for gameplay. SetNumberSpritePositions:: ld a, SCORE_BASE_X ld hl, wSPRScore1 diff --git a/src/sram.asm b/src/sram.asm index f481b29..5447a96 100644 --- a/src/sram.asm +++ b/src/sram.asm @@ -34,8 +34,9 @@ rSelectedStartLevel:: ds 2 SECTION "SRAM Functions", ROM0 -RestoreSRAM:: ; Check if our SRAM is initialized and of the correct version. + ; Restores it if so, otherwise initializes it. +RestoreSRAM:: ld a, [rCheck] cp a, LOW(__UTC_YEAR__) jr nz, InitializeSRAM @@ -75,6 +76,7 @@ RestoreSRAM:: ldh [hStartSpeed+1], a ret + ; Initializes SRAM with default values. InitializeSRAM: ; Set the magic id. ld a, LOW(__UTC_YEAR__) @@ -125,4 +127,5 @@ InitializeSRAM: ld [rSelectedStartLevel+1], a ret + ENDC diff --git a/src/state_gameplay.asm b/src/state_gameplay.asm index 72d5b62..6f72bb6 100644 --- a/src/state_gameplay.asm +++ b/src/state_gameplay.asm @@ -48,6 +48,7 @@ hRequestedJingle: ds 1 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:: ; Turn the screen off if it's on. ldh a, [rLCDC] @@ -113,6 +114,7 @@ SwitchToGameplay:: ret + ; Main gameplay event loop. GamePlayEventLoopHandler:: ; What mode are we in? ld hl, .modejumps @@ -134,6 +136,7 @@ GamePlayEventLoopHandler:: jp preGameOverMode jp pauseMode + ; Draw "READY" and wait a bit. leadyMode: ldh a, [hModeCounter] @@ -359,6 +362,7 @@ delayMode: : jp drawStaticInfo + preGameOverMode: ; Spawn the failed piece. call ForceSpawnPiece @@ -578,6 +582,7 @@ drawStaticInfo: jp EventLoopPostHandler + ; Do the hold action. DoHold: ; Mark hold as spent. ld a, $FF diff --git a/src/state_title.asm b/src/state_title.asm index 7fd6bd2..4b2aa02 100644 --- a/src/state_title.asm +++ b/src/state_title.asm @@ -27,6 +27,7 @@ wSelected:: ds 1 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:: ; Turn the screen off if it's on. ldh a, [rLCDC] @@ -136,6 +137,7 @@ SwitchToTitle:: ret + ; Handles title screen input. TitleEventLoopHandler:: call GBCTitleProcess @@ -209,6 +211,8 @@ TitleEventLoopHandler:: .done jp EventLoopPostHandler + + ; Decrements the currently selected option. DecrementOption: .opt0 ld a, [wSelected] @@ -307,6 +311,8 @@ DecrementOption: jr DecrementLevel jp EventLoopPostHandler + + ; Decrements start level. DecrementLevel: ; Decrement ldh a, [hStartSpeed] @@ -324,6 +330,7 @@ DecrementLevel: jp CheckLevelRange + ; Increments the selected option. IncrementOption: .opt0 ld a, [wSelected] @@ -422,6 +429,8 @@ IncrementOption: jr IncrementLevel jp EventLoopPostHandler + + ; Increments start level. IncrementLevel: ; Increment ldh a, [hStartSpeed] @@ -438,11 +447,11 @@ IncrementLevel: ld [rSelectedStartLevel+1], a jp CheckLevelRange + + ; Wipes the start level upon selecting a new speed curve. InitSpeedCurve: ld a, [wSpeedCurveState] call GetStart - -.set ld a, l ldh [hStartSpeed], a ld [rSelectedStartLevel], a @@ -452,7 +461,7 @@ InitSpeedCurve: ret - + ; Gets the end of a speed curve. GetEnd: ld a, [wSpeedCurveState] cp a, SCURVE_DMGT @@ -478,6 +487,8 @@ GetEnd: : ld bc, sCHILSpeedCurveEnd ret + + ; Gets the beginning of a speed curve. GetStart: ld a, [wSpeedCurveState] cp a, SCURVE_DMGT @@ -503,6 +514,8 @@ GetStart: : ld hl, sCHILSpeedCurve ret + + ; Make sure we don't overflow the level range. CheckLevelRange: ; At end? call GetEnd @@ -547,6 +560,7 @@ CheckLevelRange: jp EventLoopPostHandler + ; Handles the display of the menu. TitleVBlankHandler:: call ToATTR diff --git a/src/time.asm b/src/time.asm index 0286d15..cd0b4ca 100644 --- a/src/time.asm +++ b/src/time.asm @@ -27,22 +27,63 @@ hFrameCtr:: ds 1 hEvenFrame:: ds 1 +SECTION "Time Variables", WRAM0 +wMinutes:: ds 1 +wSeconds:: ds 1 +wFrames:: ds 1 + + SECTION "Time Functions", ROM0 + ; Zeroes all timers and gets the free-running timer ticking. TimeInit:: xor a, a ldh [rTMA], a ldh [hEvenFrame], a ldh [hFrameCtr], a + ld [wMinutes], a + ld [wSeconds], a + ld [wFrames], a ld a, TACF_262KHZ | TACF_START ldh [rTAC], a 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:: ldh a, [hFrameCtr] inc a ldh [hFrameCtr], a and 1 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