; Super Mario Bros. 3 Full Disassembly by Southbird 2012 ; For more info, see http://www.sonicepoch.com/sm3mix/ ; ; PLEASE INCLUDE A CREDIT TO THE SOUTHBIRD DISASSEMBLY ; AND THE ABOVE LINK SOMEWHERE IN YOUR WORKS :) ; ; Original disassembler source generated by DCC6502 version v1.4 ; (With labels, comments, and some syntax corrections for nesasm by Southbird) ; For more info about DCC6502, e-mail veilleux@ameth.org ; ; This source file last updated: 2011-11-18 21:50:36.000000000 -0600 ; Distribution package date: Fri Apr 6 23:46:16 UTC 2012 ; DMC sounds are here! ; Note that due to limitation of the register, these sounds must be aligned on 64-byte ($40) boundaries DMC01: .byte $55, $55, $55, $95, $AA, $2A, $95, $E0, $7F, $FC, $C0, $F1, $03, $28, $FE, $FF .byte $FF, $F1, $5F, $3F, $00, $00, $00, $00, $00, $00, $08, $80, $C0, $F1, $FF, $C7 .byte $8B, $1F, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $AB, $6A, $9B, $AA, $92, $52, $25 .byte $95, $24, $25, $92, $80, $4A, $50, $05, $00, $00, $80, $20, $42, $04, $08, $E2 .byte $FF, $01, $80, $6D, $AB, $6D, $DF, $BB, $77, $7B, $AB, $AF, $DD, $D5, $7D, $DD .byte $BF, $FF, $EF, $B6, $6D, $6F, $BB, $6D, $AF, $2A, $95, $94, $24, $49, $92, $88 .byte $42, $84, $48, $88, $04, $09, $49, $92, $24, $51, $22, $A5, $92, $22, $49, $AA .byte $52, $A9, $AA, $5A, $55, $AD, $55, $55, $AD, $6D, $B5, $AD, $6D, $B5, $6D, $DB DMC01_End DMC02: .byte $55, $60, $6B, $79, $EA, $F8, $FF, $43, $82, $24, $00, $20, $8E, $ED, $C7, $A5 .byte $1F, $10, $B7, $7F, $FF, $FF, $4D, $63, $C1, $15, $24, $41, $92, $28, $09, $4D .byte $25, $44, $42, $10, $CA, $BE, $FB, $FF, $7F, $DF, $F5, $EE, $D3, $31, $0E, $FF .byte $41, $10, $00, $21, $92, $20, $00, $F4, $38, $56, $5D, $EB, $F9, $9D, $3C, $BF .byte $ED, $E7, $D7, $AA, $6C, $59, $16, $A2, $00, $61, $83, $F0, $72, $3A, $0B, $81 .byte $45, $41, $09, $DA, $F1, $FD, $FF, $FF, $5E, $AF, $C4, $4B, $D3, $C9, $8A, $82 .byte $24, $89, $28, $06, $02, $47, $55, $E1, $3D, $AE, $EF, $BC, $F4, $52, $D5, $55 .byte $BD, $BC, $7A, $1E, $57, $51, $0A, $25, $29, $A8, $A8, $38, $69, $D3, $88, $AA .byte $18, $78, $6C, $BA, $DE, $7D, $ED, $FB, $43, $E7, $34, $4C, $2C, $25, $AD, $92 .byte $A4, $51, $A5, $C8, $62, $E2, $06, $8D, $C7, $F1, $AD, $65, $37, $B4, $AC, $5A .byte $4D, $9F, $3E, $6D, $3C, $A5, $B2, $24, $A9, $A2, $92, $86, $A9, $44, $45, $EE .byte $2C, $5E, $AA, $2D, $AE, $53, $9F, $4A, $9D, $3D, $4E, $8D, $3B, $8A, $A9, $E5 .byte $C8, $B2, $2C, $34, $52, $D4, $59, $B1, $D2, $8A, $55, $C3, $5B, $A9, $55, $8B .byte $76, $7C, $79, $6C, $2B, $CE, $F2, $64, $31, $8E, $64, $45, $72, $64, $D1, $92 .byte $E3, $54, $AD, $5A, $35, $55, $D7, $58, $1E, $1D, $55, $4E, $1D, $57, $56, $55 .byte $CB, $AA, $AA, $2A, $69, $A5, $4A, $AA, $AA, $4A, $4D, $6A, $31, $4D, $D3, $A9 .byte $6A, $AD, $7A, $5C, $65, $AB, $AA, $AC, $CA, $72, $AA, $4A, $B5, $D0, $8A, $95 .byte $AC, $54, $95, $6C, $D9, $52, $5B, $A5, $AA, $D5, $4A, $B5, $2C, $4B, $B6, $D4 .byte $34, $9D, $95, $56, $55, $55, $55, $55, $9A, $2A, $15, $A7, $AA, $52, $2D, $55 .byte $69, $95, $B6, $AA, $65, $D5, $6A, $AA, $5A, $EA, $58, $55, $69, $6A, $A9, $96 .byte $D2, $34, $95, $53, $A9, $A9, $2A, $35, $55, $D3, $AA, $6A, $55, $B5, $E2, $AA .byte $34, $B5, $9C, $C6, $B1, $95, $AA, $A9, $AA, $B4, $52, $55, $8B, $A9, $9A, $A6 .byte $AA, $9A, $5A, $6A, $A5, $56, $56, $65, $55, $55, $AD, $B4, $B2, $AA, $54, $55 .byte $2D, $D5, $AA, $AC, $AA, $52, $2B, $4B, $95, $AA, $AA, $B2, $2A, $AB, $AA, $6A .byte $A5, $55, $D5, $B4, $AA, $AA, $AA, $A9, $AA, $AA, $52, $55, $59, $A9, $A5, $AA .byte $56, $5A, $A5, $95, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55 .byte $55, $55, $55, $55, $55, $55, $53, $35, $55, $D5, $54, $55, $55, $55, $A9, $A5 .byte $AA, $AA, $6A, $59, $69, $55, $55, $B5, $AA, $AA, $B2, $2C, $2B, $55, $55, $55 DMC02_End ; .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF Music_PlayDMC: LDA DMC_Queue ; Get value queued for DMC CMP #$7e BEQ Music_StopDMC ; If rest value was queued, jump to Music_StopDMC LDA DMC_Queue ; A = queue value BNE PRG031_E2E1 ; If something was queued, jump to PRG031_E2E1 ; Nothing queued... LDA DMC_Current BEQ Music_StopDMC ; If nothing is playing, jump to Music_StopDMC DEC DMC_Time ; DMC_Time-- BNE PRG031_E2E0 ; If time has not expired on DMC, jump to PRG031_E2E0 (RTS) Music_StopDMC: LDA #$00 STA DMC_Current ; Stop any current DMC sound LDA #$0f STA PAPU_EN ; Disable DMC PRG031_E2E0: RTS ; Return PRG031_E2E1: STA DMC_Current TAY ; Y = DMC_Current LDA DMC_MODCTL_LUT-1,Y ; Subtract 1 from LUT address since $00 is disabled and $01 is first DMC sound STA PAPU_MODCTL ; Update PAPU_MODCTL STA PAPU_MODCTL_Copy ; ... and it's copy LDA DMC_MODADDR_LUT-1,Y ; Subtract 1 from LUT address since $00 is disabled and $01 is first DMC sound STA PAPU_MODADDR ; Set PAPU_MODADDR (address of sound) LDA DMC_MODLEN_LUT-1,Y ; Subtract 1 from LUT address since $00 is disabled and $01 is first DMC sound STA PAPU_MODLEN ; Set PAPU_MODLEN (length of sound) LDA #$a0 STA DMC_Time ; DMC_Time = $A0 (always apparently) LDA #$0f STA PAPU_EN ; Disable DMC LDA #$1f STA PAPU_EN ; Enable DMC RTS ; Return ; The address are $C000 | (value << 6) MADR .func ((\1 & $3FFF) >> 6) ; The length is (value << 4) + 1 (minimum 1 byte long to $FF1 bytes long) MLEN .func ((\2 - \1) >> 4) ; Sample 3: "BAD SAMPLE LENGTH" ; Seems there is an errorenous, very long/very wrong sample length on ; Sample 3. Whether this is a mistake or a "lost DMC sample" I don't ; know. In any case, Sample 1 plays DMC02 correctly, Sample 3 would ; play DMC02 and just continue on through code, which would be noisy. DMC_MODADDR_LUT: .byte MADR(DMC01) ; Sample 0 (DMC01) .byte MADR(DMC02) ; Sample 1 (DMC02) .byte MADR(DMC03) ; Sample 2 (DMC03) .byte MADR(DMC02) ; Sample 3 (DMC02 BAD SAMPLE LENGTH) .byte MADR(DMC04) ; Sample 4 (DMC04) .byte MADR(DMC05) ; Sample 5 (DMC05) .byte MADR(DMC05) ; Sample 6 (DMC05 3/4 length) .byte MADR(DMC06) ; Sample 7 (DMC06) .byte MADR(DMC06) ; Sample 8 (DMC06 slower) .byte MADR(DMC07) ; Sample 9 (DMC07) .byte MADR(DMC07) ; Sample 10 (DMC07 slower) .byte MADR(DMC05) ; Sample 11 (DMC05 1/2 length) .byte MADR(DMC08) ; Sample 12 (DMC08) .byte MADR(DMC09) ; Sample 13 (DMC09) .byte MADR(DMC09) ; Sample 14 (DMC09 slower) .byte MADR(DMC09) ; Sample 15 (DMC09 even slower) DMC_MODLEN_LUT: ; these are (value << 4) + 1, that is minimum 1 byte long to FF1 bytes .byte MLEN(DMC01, DMC01_End) ; Sample 0 (DMC01) .byte MLEN(DMC02, DMC02_End) ; Sample 1 (DMC02) .byte MLEN(DMC03, DMC03_End) ; Sample 2 (DMC03) .byte MLEN(DMC02, DMC02_Bad) ; Sample 3 (DMC02 BAD SAMPLE LENGTH) .byte MLEN(DMC04, DMC04_End) ; Sample 4 (DMC04) .byte MLEN(DMC05, DMC05_End) ; Sample 5 (DMC05) .byte MLEN(DMC05, DMC05_C) ; Sample 6 (DMC05 3/4 length) .byte MLEN(DMC06, DMC06_End) ; Sample 7 (DMC06) .byte MLEN(DMC06, DMC06_End) ; Sample 8 (DMC06 slower) .byte MLEN(DMC07, DMC07_End) ; Sample 9 (DMC07) .byte MLEN(DMC07, DMC07_End) ; Sample 10 (DMC07 slower) .byte MLEN(DMC05, DMC05_B) ; Sample 11 (DMC05 1/2 length) .byte MLEN(DMC08, DMC08_End) ; Sample 12 (DMC08) .byte MLEN(DMC09, DMC09_End) ; Sample 13 (DMC09) .byte MLEN(DMC09, DMC09_End) ; Sample 14 (DMC09 slower) .byte MLEN(DMC09, DMC09_End) ; Sample 15 (DMC09 even slower) DMC_MODCTL_LUT: .byte $0F ; Sample 0 (DMC01) .byte $0F ; Sample 1 (DMC02) .byte $0F ; Sample 2 (DMC03) .byte $0F ; Sample 3 (DMC02 BAD SAMPLE LENGTH) .byte $0F ; Sample 4 (DMC04) .byte $0F ; Sample 5 (DMC05) .byte $0E ; Sample 6 (DMC05 3/4 length) .byte $0F ; Sample 7 (DMC06) .byte $0E ; Sample 8 (DMC06 slower) .byte $0F ; Sample 9 (DMC07) .byte $0E ; Sample 10 (DMC07 slower) .byte $0D ; Sample 11 (DMC05 1/2 length) .byte $0F ; Sample 12 (DMC08) .byte $0F ; Sample 13 (DMC09) .byte $0E ; Sample 14 (DMC09 slower) .byte $0D ; Sample 15 (DMC09 even slower) .byte $60 ; ??? Music_GetRestTicks: ; A is a byte from the music segment, $80-$fe/$ff on non-squares ; Only the lower 4 bits are used here ; ; The rest value returned is: ; Music_RestH_LUT[Music_RestH_Base + Music_RestH_Off + (0 to 15)] ; ; Note that Music_RestH_Base is always divisible by $10 and ; Music_RestH_Off is always $00 or $10 (low time warning) AND #$0f ; Get lower 4 bits ADD Music_RestH_Base ; Add this to Music_RestH_Base ADC Music_RestH_Off ; Add this to Music_RestH_Off TAY ; Y = A LDA Music_RestH_LUT,Y ; Get value from Music_RestH_LUT RTS ; Return SndMus_QueueCommonJ: JMP SndMus_QueueCommon ; FIXME: Anybody want to claim this? ; $E34C JMP Music_StopAll Sound_PlayMusic: LDA Sound_QMusic1 BNE SndMus_QueueCommonJ ; If music from set 1 has been queued, jump to SndMus_QueueCommonJ ; Music has not been queued from set 1... LDA SndCur_Music1 BEQ PRG031_E364 ; If no music is currently playing in set 1, jump to PRG031_E364 LDA Sound_QMusic2 BEQ PRG031_E361 ; If music from set 2 has NOT been queued, jump to PRG031_E361 ; Music from set 2 has been queued! STA Music2_Hold ; Store it into the hold until the Set 1 song has expired! PRG031_E361: JMP Music_Sq2Track PRG031_E364: ; No music in *Set 1* is currently playing ; Music from Set 2 MAY have been queued ; The following check is probably superfluous, as Set 1 should have ; already been handled above... LDA Sound_QMusic1 BNE SndMus_QueueCommonJ ; If music from set 1 has been queued, jump to SndMus_QueueCommonJ ; Set 2 is funny; it uses index in low bit and high bit. ($01-$0F and $10-$F0) ; Not really sure what the point of that is, except perhaps to break it up into two sets. ; Allows 16 values though not all are used ($D0, $E0, $F0) ; I'm breaking the Set 2 into Set 2A ($01-$0F) and Set 2B ($10-$F0) LDA Sound_QMusic2 AND #MUS2B_MASK BEQ SndMus_QueueCommonJ ; If music selection is not Set 2B (or none queued), jump to SndMus_QueueCommonJ ; If you're here, that means that there is a Set 2B queue ; somewhere between $10 and $F0... LDA Sound_QMusic2 BNE SndMus_QueueSet2B ; Whatever was queued, jump to SndMus_QueueSet2B LDA SndCur_Music2 BNE PRG031_E3EB ; If something is playing in Set 2, jump to PRG031_E3EB RTS ; Return ;;;; SndMus_QueueSet2B: ; For queuing any Set 2B music $10 - $F0 STA SndCur_Music2 ; Store which Set 2 song we're playing LDY #$00 STY SndCur_Music1 ; Stop any Set 1 song ; Shift it down 4 bits to make it a 1-15 index LSR A LSR A LSR A LSR A TAY ; Y = A LDA Music_Set2B_Starts-1,Y ; Because Y starts at '1', we must subtract 1 from the LUT address STA Music_Start ; Set Music_Start LDA Music_Set2B_Ends-1,Y ; Because Y starts at '1', we must subtract 1 from the LUT address ADD #$02 ; A += 2 (Reason for adding two: The index counter is always one ahead, and the end index is INCLUSIVE!) STA Music_End ; Set Music_End LDA Music_Set2B_Loops-1,Y ; Because Y starts at '1', we must subtract 1 from the LUT address STA Music_Loop ; Set Music_Loop LDA Music_Start ; A = Music_Start SndMus2B_NextOrStop: STA Music_NextIndex ; Update Music_NextIndex SndMus2B_Next: INC Music_NextIndex ; Music_NextIndex++ (This makes the index always 1 ahead) LDY Music_NextIndex ; CPY Music_End ; BNE SndMus2B_LoadNext ; If Music_NextIndex <> Music_End, jump to SndMus2B_LoadNext ; We've reached the end of the song! LDA Music_Loop ; Reload with the loop start value BNE SndMus2B_NextOrStop ; Jump back so we play this index (unless loop = 0, in which case we stop) ; If loop = 0, stop music JMP Music_StopAll SndMus2B_LoadNext: ; Load next "index" (Y) of Music Set 2 song... LDA Music_Set2B_IndexOffs-1,Y ; Get offset for the current index; it is always one ahead, so -1 from the LUT TAY ; Use this offset value in Y ; Get and store rest lookup base index for this segment in Music_RestH_Base LDA Music_Set2B_Headers,Y STA Music_RestH_Base ; Get and store the base address into [Music_Base_H][Music_Base_L] LDA Music_Set2B_Headers+1,Y STA Music_End, jump to SndMus2A_LoadNext ; We've reached the end of the song! LDA Music_Loop ; Reload with the loop start value BNE SndMus2A_NextOrStop ; Jump back so we play this index (unless loop = 0, in which case we stop) ; If loop = 0, stop music JMP Music_StopAll SndMus_Queue1: STA SndCur_Music1 ; SndCur_Music1 = queued music LDY SndCur_Music2 ; Y = SndCur_Music2 STY Music2_Hold ; If a "set 2" song is playing, back up which one it is; we'll restart it after this song finishes (only really used for the "time low" song) LDY #$00 ; STY SndCur_Music2 ; Stop "set 2" song (if any) STY Music_RestH_Off ; Clear the rest lookup base offset value ; The following loop transforms the queue value into an index value. ; This is the same basic procedure used for sound effects, and even ; gives "priority" to different tunes, but that feature is probably ; not really used at all... PRG028_E456: INY ; Y++ LSR A ; BCC PRG028_E456 ; If we haven't hit a bit, go around again... SndMus2A_LoadNext: ; Both Set 1 and 2A enter here... ; The only difference is that Set 1 enters directly with an index in mind ; Set 2A enters using the index counting system like 2B. So it works since ; 2B's lowest index is 08! ; 'Y' is the next 1/2A index we're going to play LDA Music_Set1_Set2A_IndexOffs-1,Y ; This selects the offset to the track offset headers for this song; we subtract 1 since 'Y' starts at 1 TAY ; Set rest lookup base index LDA Music_Set1_Set2A_Headers,Y STA Music_RestH_Base ; Set music base address LDA Music_Set1_Set2A_Headers+1,Y STA A STA PAPU_CTL2 ; Store patch value into PAPU_CTL2 LDX #$7f STX PAPU_RAMP2 ; Everything but the actual ramping BNE Music_Sq1Track PRG031_E5A7: LDA #$00 STA Music_Sq2Bend ; When playing a level 1 sound, prevent bend effects! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Square 1's music track code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Music_Sq1Track: DEC Music_Sq1Rest ; Music_Sq1Rest-- BNE PRG031_E62B ; If still resting, jump to PRG031_E62B PRG031_E5B1: ; Rest is complete! LDY Music_Sq1TrkOff ; Y = Music_Sq1TrkOff INC Music_Sq1TrkOff ; Music_Sq1TrkOff++ LDA [Music_Base_L],Y ; Get next byte from music segment data BPL PRG031_E5DA ; If byte is $00 - $7f, jump to PRG031_E5DA CMP #$ff BNE PRG031_E5C5 ; If not $ff, jump to PRG031_E5C5 ; $ff only works after a "note on" event INC Music_Sq1TrkOff ; Music_Sq1TrkOff++ JMP PRG031_E5B1 ; Get next byte... PRG031_E5C5: ; Byte was $80 - $fe ... TAX ; Save A AND #$f0 ; A &= $f0 STA Music_Sq1Patch ; Result -> Music_Sq1Patch TXA ; Restore A (current byte of music data) JSR Music_GetRestTicks STA Music_Sq1RestH ; Store new rest value (returned by Music_GetRestTicks) PRG031_E5D2: LDY Music_Sq1TrkOff ; Y = Music_Sq1TrkOff INC Music_Sq1TrkOff ; Music_Sq1TrkOff++ LDA [Music_Base_L],Y ; Get next byte from music segment... PRG031_E5DA: TAY ; Y = A BNE Music_Sq1NoteOn ; If next byte is NOT $00, jump to Music_Sq1NoteOn ; Next byte in segment was $00... ; Activates a ramp effect LDA #$83 STA PAPU_CTL1 LDA #$94 STA PAPU_RAMP1 STA Music_Sq1AltRamp ; Music_Sq1AltRamp holds this alternate ramp value BNE PRG031_E5B1 ; (technically always) jump to PRG031_E5B1 Music_Sq1NoteOn: ; Value in range $01 - $7f TAX LDA SndCur_Player ORA SndCur_Map BNE PRG031_E625 ; If Player or Map sounds active, you can't play on this track! Jump to PRG031_E625 TXA ; Restore A JSR Sound_Sq1_NoteOn_NoPAPURAMP ; Play note! Returns 0 if $7e rest was played... BNE PRG031_E601 ; If last note was NOT a rest, jump to PRG031_E601 LDA Music_LOST4FB ; A = Music_LOST4FB JMP PRG031_E60A ; Jump to PRG031_E60A... PRG031_E601: LDA Music_Sq1RestH LDX Music_LOST4FC ; X = Music_LOST4FC (Music_CalcNoteLen is going to override it anyway though??) JSR Music_CalcNoteLen ; X = $82 A = $3f or $16 PRG031_E60A: STA Music_Sq1NoteLen ; Set new note length LDY Music_Sq1TrkOff ; Y = Music_Sq1TrkOff INC Music_Sq1TrkOff ; Music_Sq1TrkOff++ LDA [Music_Base_L],Y ; Get next byte from music segment CMP #$ff BNE PRG031_E622 ; If byte <> $ff, jump to PRG031_E622 ; Activate bend effects on square 1! ; Music_Sq1Bend = Sound_Sq1_CurFL LDA Sound_Sq1_CurFL STA Music_Sq1Bend JMP PRG031_E5D2 ; Jump to PRG031_E5D2 PRG031_E622: DEC Music_Sq1TrkOff ; Music_Sq1TrkOff-- PRG031_E625: ; Restore Music_Sq1Rest from hold value LDA Music_Sq1RestH STA Music_Sq1Rest PRG031_E62B: LDA SndCur_Player ORA SndCur_Map BNE PRG031_E660 ; If playing "player" or "map" sounds, jump to PRG031_E660 LDA Music_Sq1Bend BEQ PRG031_E640 ; If Music_Sq1Bend = 0, jump to PRG031_E640 LDX #$00 ; X = 0 (Square 1) LDA Music_Sq1Rest ; A = rest time remaining JSR Music_UpdateBend ; Update bend effects! PRG031_E640: LDY Music_Sq1NoteLen ; Y = Music_Sq1NoteLen BEQ PRG031_E648 ; If Music_Sq1NoteLen = 0, jump to PRG031_E648 DEC Music_Sq1NoteLen ; Music_Sq1NoteLen-- PRG031_E648: LDA Music_Sq1RestH ; A = current rest value LDX Music_Sq1Patch ; X = current "patch" JSR Music_PatchGetCTL ; Gets a PAPU_CTLx value for the current patch -> A STA PAPU_CTL1 ; Store patch value into PAPU_CTL1 LDA Music_Sq1AltRamp BNE PRG031_E65B ; If the alternate ramp value is in effect, don't lose it! LDA #$7f ; Standard ramp value PRG031_E65B: STA PAPU_RAMP1 ; Use appropriate ramp value BNE Music_TriTrack ; (technically always) jump to Music_TriTrack PRG031_E660: ; "Player" or "Map" sound in effect; cancel bend effects LDA #$00 STA Music_Sq1Bend ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Triangle's music track code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Music_TriTrack: LDA Music_TriTrkPos ; Get triangle track's position BEQ Music_NseTrack ; If Music_TriTrkPos = 0 (Disabled!), jump to Music_NseTrack DEC Music_TriRest ; Music_TriRest-- BNE Music_NseTrack ; If not done resting, jump to the noise track LDY Music_TriTrkPos ; Y = Music_TriTrkPos INC Music_TriTrkPos ; Music_TriTrkPos++ LDA [Music_Base_L],Y ; Get next byte from triangle track BPL Music_TriNoteOn ; Byte $00 - $7f, jump to Music_TriNoteOn ; Byte $80 - $ff... goes directly to the rest value routine ; No "patches" available on the triangle track JSR Music_GetRestTicks STA Music_TriRestH ; Update rest hold value LDA #$1f STA PAPU_TCR1 ; $1f written to PAPU_TCR1 LDY Music_TriTrkPos ; Y = Music_TriTrkPos INC Music_TriTrkPos ; Music_TriTrkPos++ LDA [Music_Base_L],Y ; Get next byte from music segment BEQ PRG031_E6B4 ; If $00 came up, jump to PRG031_E6B4 Music_TriNoteOn: ; Triangle track, value $00 - $7f JSR Sound_Tri_NoteOn ; Play note! (Will return $00 if $7e rest was played) BEQ PRG031_E6B2 ; If last note was a rest, jump to PRG031_E6B2 LDA Music_TriRestH CMP #$0a BLT PRG031_E6A6 ; If Music_TriRestH < $0A, jump to PRG031_E6A6 (A = $18) CMP #$13 BLT PRG031_E6AA ; If Music_TriRestH < $13, jump to PRG031_E6AA (A = $20) CMP #$25 BGE PRG031_E6AE ; If Music_TriRestH >= $25, jump to PRG031_E6AE (A = $ff) ; Music_TriRestH >= $13 && Music_TriRestH < $25 LDA #$50 BNE PRG031_E6B4 ; (technically always) jump to PRG031_E6B4 PRG031_E6A6: LDA #$18 BNE PRG031_E6B4 ; (technically always) jump to PRG031_E6B4 PRG031_E6AA: LDA #$20 BNE PRG031_E6B4 ; (technically always) jump to PRG031_E6B4 PRG031_E6AE: LDA #$ff BNE PRG031_E6B4 ; (technically always) jump to PRG031_E6B4 PRG031_E6B2: LDA #$80 PRG031_E6B4: STA PAPU_TCR1 ; Start (if note played) or Stop (if $00 after $80-$ff) the triangle sound LDX Music_TriRestH STX Music_TriRest ; Music_TriRest = Music_TriRestH ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Noise's music track code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Music_NseTrack: LDA Music_NseTrkPos ; Music_NseTrack BEQ Music_PCMTrack ; If Music_NseTrack = 0 (disabled), jump to Music_PCMTrack DEC Music_NoiseRest ; Music_NoiseRest-- BNE Music_PCMTrack ; If track is not done resting, just jump to Music_PCMTrack PRG031_E6C7: LDY Music_NseTrkPos ; Y = Music_NseTrkPos INC Music_NseTrkPos ; Music_NseTrkPos++ LDA [Music_Base_L],Y ; Get next byte from music segment track BEQ PRG031_E700 ; If next byte is $00, jump to PRG031_E700 BPL Music_NseNoteOn ; $01 - $7f is note on, jump to Music_NseNoteOn ; Byte $80 - $ff... goes directly to the rest value routine ; No "patches" available on the noise track JSR Music_GetRestTicks STA Music_NseRestH ; Update rest hold LDY Music_NseTrkPos ; Y = Music_NseTrkPos INC Music_NseTrkPos ; Music_NseTrkPos++ LDA [Music_Base_L],Y ; Get next byte in music segment track BEQ PRG031_E700 ; If byte $00 comes up, jump to PRG031_E700 Music_NseNoteOn: ; Noise isn't really a "note" event, but it has a LUT for a "note" value... LSR A ; Shift in half (because notes are usually a 2-byte index) TAY ; Y = A ; Note that the normal $7E Note Off is not implemented here; instead, use $01 ; Plug in appropriate noise values for this "note" LDA Music_NoiseLUTA,Y STA PAPU_NCTL1 LDA Music_NoiseLUTB,Y STA PAPU_NFREQ1 LDA Music_NoiseLUTC,Y STA PAPU_NFREQ2 ; Reset rest value LDA Music_NseRestH STA Music_NoiseRest ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; DMC's music track code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Music_PCMTrack: JMP PRG031_E709 PRG031_E700: ; When byte $00 read, Reset noise track to start LDA Music_NseStart STA Music_NseTrkPos JMP PRG031_E6C7 ; Back into the fray... PRG031_E709: LDA Music_PCMTrkPos BEQ PRG031_E738 ; If Music_PCMTrkPos = 0 (disabled), jump to PRG031_E738 DEC Music_DMCRest ; Music_DMCRest-- BNE PRG031_E738 ; If not done resting, jump to PRG031_E738 PRG031_E713: LDY Music_PCMTrkPos ; Y = Music_PCMTrkPos INC Music_PCMTrkPos ; Music_PCMTrkPos++ LDA [Music_Base_L],Y ; Get next byte in music segment track BEQ PRG031_E741 ; If next byte is $00, jump to PRG031_E741 BPL PRG031_E72F ; If byte is $01 - $7f, jump to PRG031_E72F JSR Music_GetRestTicks STA Music_DMCRestH ; Update rest hold value LDY Music_PCMTrkPos ; Y = Music_PCMTrkPos INC Music_PCMTrkPos ; Music_PCMTrkPos++ LDA [Music_Base_L],Y ; Get next byte in music segment track BEQ PRG031_E741 ; If next byte is $00, jump to PRG031_E741 PRG031_E72F: STA DMC_Queue ; Store note into DMC queue LDA Music_DMCRestH STA Music_DMCRest ; Music_DMCRest = Music_DMCRestH PRG031_E738: JSR Music_PlayDMC ; Play DMC sound! LDA #$00 STA DMC_Queue ; Clear the DMC queue RTS ; Return PRG031_E741: ; When byte $00 read, reset DMC track to start LDA Music_PCMStart STA Music_PCMTrkPos JMP PRG031_E713 ; Back into the fray... ; Noise track values per "note"; the note is shifted down by 1, ; (because for other channels, note is a 2-byte lookup, but here it is ; only 1.) The first note value is $01 (which shifted becomes zero), ; so basically $01, $02-$03, $04-$05, $06-$07 are the only valid "notes" ; $01 (first triple) is used as a "rest" / Note Off Music_NoiseLUTA: .byte $10, $1E, $1F, $1F Music_NoiseLUTB: .byte $00, $03, $0A, $02 Music_NoiseLUTC: .byte $00, $18, $18, $58 Music_CalcNoteLen: ; Just played a note which was not a rest ; A = current time between events (RestH) CMP #19 BLT PRG031_E75E ; If A < 19, jump to PRG031_E75E (use A = $16) LDA #$3f ; Otherwise, use A = $3F BNE PRG031_E760 ; (technically always) jump to PRG031_E760 PRG031_E75E: LDA #$16 PRG031_E760: ; These values appear to be unused?? LDX #$82 LDY #$7f RTS ; Return Music_PatchTableLong: ; playing notes greater than 19 in length .word PatL1, PatL2, PatL3, PatL4, PatL5, PatL6, PatL2, PatL8 Music_PatchTableShort: ; shorter notes .word PatS1, PatS2, PatS3, PatS4, PatS5, PatS6, PatS2, PatS8 Music_PatchGetCTL: ; A = Rest hold value (< 19 considered "short" patches) ; X = Patch # ; Y = Note length PHA ; Save A ; X = (X & $70) >> 3 ; This takes the lower 3 bits of the upper nibble in the ; patch value to make an index into the patch table... ; This amounts to the value $0, $2, $4, $6, $8, $A, $E, $10 TXA AND #$70 LSR A LSR A LSR A TAX PLA ; Restore A CMP #19 BLT PRG031_E79E ; If rest value < 19, jump to PRG031_E79E ; Music playing not too quickly uses the "long" set ; Copy address for this patch from the LUT to [Music_PatchAdrH][Music_PatchAdrL] LDA Music_PatchTableLong,X STA $7E, jump to PRG031_E7C8 ; If note is $7E (rest), then we're done! LDA #$00 ; To alert music system we just played a "rest" RTS ; Return PRG031_E7C8: LDY #$01 STY Sound_Octave ; Sound_Octave = 1 (interestingly, this sets a "center octave", so at 0 everything is lower, at 2 everything is higher...) PHA ; Save original note TAY ; Y = Note BMI PRG031_37D9 ; If Note & $80, skip the octave adjust?? ; Note: ; Notes are stored as double value for convenience here (spares a shift) ; So "24" is an entire octave for doubled note values, get it? ; The following loop transforms a note into a relative ; "octave lookup" for the note base frequency; essentially, ; this is (Note MOD 12), just no such instruction available PRG031_E7D1: INC Sound_Octave ; Sound_Octave++ (Sound_Octave will have the octave level after this, essentially Note / 12) SUB #24 ; A -= 24 (down an octave) BPL PRG031_E7D1 ; While above zero, loop! PRG031_37D9: ADD #24 ; A += 24 (recover from last subtraction) TAY ; Y = A (Y is now the offset into the LUT to get the base frequency for this note) ; Y should now be a lookup into the note table! ; Store this resultant frequency into Sound_Sqr_FreqL/H LDA Square1_Table_Notes,Y STA 0, loop! PLA ; Retrieve original note CMP #56 ; BCC PRG031_E7F7 ; If note is less than 56, skip the decrement DEC 3, jump to PRG031_E860 LSR A BCC PRG031_E860 LSR A BCC PRG031_E860 ; Diminish the bend DEC Music_Sq1Bend,X PRG031_E860: LDA Music_Sq1Bend,X ; Get current bend CMP Sound_Sq1_CurFL,X ; Compare to current frequency BGE PRG031_E870 ; If bend >= Sound_Sq1_CurFL, jump to PRG031_E870 LDA #$00 STA Music_Sq1Bend,X ; Clear/disable bend LDA Sound_Sq1_CurFL,X ; Just get current frequency PRG031_E870: STA PAPU_FT1,X ; Update PAPU_FT1/2! RTS ; Return ; Music_RestH_LUT is indexed by (Music_RestH_Base + Music_RestH_Off + [0 to 15]) ; * Music_RestH_Base is always divisible by $10, Music_RestH_Off is $00 or $10 ; ; * Offset (0 to 15) comes from the lower 4 bits of $80+ values ; (The upper 4 bits set a patch value, unrelated) ; ; * Result is a "rest" (in ticks) for the pause between events ; ; * Obviously that means a song is "optimized" by selecting the best set, and ; must have a correct row +$10 if it plans on being "low time warning compatible" Music_RestH_LUT: .byte $08, $08, $0B, $0A, $10, $18, $15, $16, $20, $30, $40, $60, $80, $01, $1F, $00 ; $00 - $0F .byte $07, $08, $0A, $0A, $0F, $16, $14, $14, $1E, $2D, $3C, $5A, $78, $05, $00, $00 ; $10 - $1F .byte $07, $07, $09, $0A, $0E, $15, $13, $12, $1C, $2A, $38, $54, $70, $01, $04, $00 ; $20 - $2F .byte $06, $06, $08, $08, $0C, $12, $10, $10, $18, $24, $30, $48, $60, $04, $02, $16 ; $30 - $3F .byte $05, $05, $07, $06, $0A, $0F, $0D, $0E, $14, $1E, $28, $3C, $50, $03, $01, $13 ; $40 - $4F .byte $04, $05, $06, $06, $09, $0D, $0C, $0C, $12, $1B, $24, $36, $48, $1E, $03, $00 ; $50 - $5F .byte $04, $04, $05, $06, $08, $0C, $0B, $0A, $10, $18, $20, $30, $40, $00, $00, $00 ; $60 - $6F .byte $03, $04, $05, $04, $07, $0A, $09, $0A, $0E, $15, $1C, $2A, $38, $0B, $00, $00 ; $70 - $7F .byte $03, $03, $04, $04, $06, $09, $08, $08, $0C, $12, $18, $24, $30, $02, $00, $00 ; $80 - $8F .byte $02, $02, $03, $02, $04, $06, $05, $06, $08, $0C, $10, $18, $20 ; $90 - $9C ; BEGIN UNUSED SPACE (alignment for DMC04) ; NOTE NOTE NOTE!! ; If you're creating a custom hack, delete these $FFs and use the following line instead: ; .AlignDMC04: DMCAlign .AlignDMC04 .byte $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF ; END UNUSED SPACE DMC04: .byte $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $B5, $82, $DC .byte $7F, $00, $E0, $FF, $03, $E8, $FF, $03, $00, $F8, $FF, $00, $F0, $FF, $62, $0B .byte $40, $DF, $8B, $EA, $27, $00, $FC, $BF, $00, $14, $FD, $FF, $03, $00, $F6, $FF .byte $07, $00, $70, $DF, $FF, $02, $00, $F8, $FF, $07, $00, $E0, $FF, $0F, $00, $D4 .byte $FE, $FF, $04, $80, $FE, $BF, $04, $00, $EC, $FF, $9F, $00, $80, $FE, $FF, $01 .byte $00, $FF, $FF, $03, $00, $A0, $FF, $FF, $00, $00, $FF, $7F, $02, $00, $FA, $FF .byte $07, $00, $EC, $FF, $0F, $00, $E8, $FE, $9F, $00, $80, $FF, $5F, $01, $A0, $FA .byte $FE, $8A, $00, $B4, $FF, $17, $01, $50, $FF, $5F, $02, $A0, $F6, $BF, $02, $41 .byte $ED, $ED, $56, $02, $52, $F7, $AF, $04, $C8, $F6, $5B, $49, $90, $D5, $DB, $4A .byte $92, $AA, $56, $AF, $4A, $90, $ED, $B6, $92, $24, $B5, $5D, $93, $A4, $AA, $AD .byte $56, $89, $D4, $B6, $96, $2A, $55, $6A, $AD, $AA, $92, $AA, $B6, $96, $52, $A9 .byte $56, $AB, $4A, $95, $55, $AB, $AA, $94, $AA, $55, $AB, $54, $AA, $D5, $AA, $52 .byte $55, $55, $AD, $2A, $55, $55, $AD, $AA, $52, $55, $D5, $AA, $52, $55, $55, $AB .byte $4A, $55, $55, $2B, $55, $55, $55, $55, $AB, $54, $55, $2D, $AB, $4A, $55, $55 .byte $B5, $2A, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55 .byte $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55 DMC04_End DMC06: .byte $55, $55, $55, $55, $55, $55, $D5, $AA, $D5, $37, $02, $00, $F4, $A3, $FF, $FF .byte $7F, $00, $2A, $00, $00, $80, $FF, $FF, $FF, $7F, $05, $00, $00, $E0, $FF, $1F .byte $C0, $FF, $17, $00, $F0, $FF, $C0, $89, $FF, $00, $00, $E0, $FF, $FF, $FF, $03 .byte $00, $00, $00, $FA, $FF, $3F, $00, $EC, $FF, $7F, $00, $FF, $7F, $00, $E0, $08 .byte $00, $F8, $F7, $FF, $07, $0E, $90, $00, $80, $FE, $FF, $7F, $00, $C0, $FE, $7F .byte $7F, $01, $FE, $2F, $00, $8C, $00, $80, $FF, $FF, $4F, $00, $C0, $7F, $00, $F8 .byte $0F, $FC, $07, $F0, $7F, $FF, $A6, $BF, $40, $5B, $00, $C0, $0A, $01, $FB, $8B .byte $FE, $04, $00, $FC, $FF, $00, $FC, $04, $FE, $91, $FB, $FF, $1F, $E0, $FF, $03 .byte $00, $00, $60, $7F, $2B, $48, $C0, $FF, $02, $D0, $FE, $1F, $00, $FE, $09, $FA .byte $A5, $FF, $FF, $21, $A2, $FF, $01, $00, $04, $B0, $EF, $17, $81, $1D, $C0, $FE .byte $0F, $54, $0B, $AA, $FA, $3F, $00, $FF, $FE, $FF, $00, $F4, $57, $02, $00, $89 .byte $A4, $3E, $50, $FF, $0F, $80, $FF, $0B, $01, $EA, $C4, $FD, $27, $DA, $B6, $FB .byte $2F, $E0, $85, $56, $02, $C0, $0B, $A8, $90, $BA, $FF, $2F, $80, $FD, $07, $C0 .byte $56, $D2, $FE, $83, $FF, $89, $4F, $A5, $D6, $0A, $24, $01, $FC, $03, $F0, $02 .byte $F8, $FF, $6B, $52, $BB, $09, $80, $BE, $BD, $84, $F6, $7F, $DF, $09, $00, $FB .byte $4B, $00, $D0, $8A, $4A, $55, $49, $FD, $AA, $FA, $AD, $5A, $05, $08, $7B, $B7 .byte $A2, $52, $FF, $FF, $03, $00, $6D, $2B, $40, $52, $13, $D1, $B6, $EA, $AB, $42 .byte $ED, $7F, $12, $92, $5A, $51, $9F, $A8, $4B, $DB, $B7, $AF, $40, $4B, $00, $D0 .byte $2E, $91, $AA, $AA, $FD, $1F, $01, $F6, $BF, $08, $AD, $64, $2B, $95, $FA, $26 .byte $B5, $A5, $7E, $A9, $2A, $01, $10, $D8, $2B, $B1, $EA, $BE, $7D, $25, $49, $6D .byte $15, $A9, $6B, $95, $C8, $EA, $DD, $49, $52, $5B, $2B, $49, $13, $41, $88, $D4 .byte $F7, $91, $FA, $BE, $6D, $25, $28, $95, $D4, $5E, $55, $85, $B6, $BD, $64, $4B .byte $52, $AD, $88, $6A, $25, $08, $6A, $FB, $AA, $55, $6F, $FB, $91, $54, $09, $A8 .byte $AD, $DA, $AD, $24, $BB, $5D, $05, $A9, $49, $92, $BA, $92, $24, $59, $6D, $57 .byte $DB, $56, $D3, $2E, $AD, $2A, $08, $B0, $7B, $5B, $49, $DB, $57, $49, $22, $A9 .byte $92, $64, $2B, $B5, $95, $A4, $B7, $6B, $B5, $6A, $25, $B5, $55, $22, $A4, $B2 .byte $ED, $4A, $7D, $17, $A9, $89, $D4, $04, $50, $DB, $DB, $15, $EA, $B6, $6A, $9B .byte $D4, $96, $A4, $5A, $49, $55, $49, $6C, $AF, $76, $95, $54, $55, $55, $02, $A8 .byte $56, $DB, $5E, $69, $5F, $A5, $52, $6D, $25, $89, $6A, $5B, $AA, $24, $B5, $55 .byte $DB, $25, $69, $95, $54, $95, $20, $B5, $6A, $DB, $F6, $6D, $92, $5A, $95, $2A .byte $41, $EA, $2B, $B5, $52, $5A, $AB, $52, $6D, $AB, $92, $50, $B5, $52, $25, $A9 .byte $ED, $6B, $B7, $91, $AD, $24, $95, $24, $A5, $56, $6B, $B5, $B6, $25, $A4, $B6 .byte $55, $49, $A2, $D5, $4A, $55, $95, $D6, $6D, $6D, $AB, $AA, $44, $29, $95, $54 .byte $A5, $6A, $DF, $A6, $4A, $A5, $AA, $92, $54, $55, $B5, $2A, $B5, $B5, $AA, $54 .byte $7D, $AB, $2A, $11, $D5, $54, $92, $AA, $DA, $B6, $D5, $56, $52, $25, $29, $95 .byte $AA, $56, $A9, $7B, $55, $55, $2A, $DB, $AA, $AA, $A4, $52, $2A, $55, $55, $AD .byte $AA, $76, $5B, $49, $15, $52, $55, $A9, $D4, $B6, $B5, $D5, $5A, $A9, $54, $49 .byte $B5, $9A, $4A, $89, $DA, $4E, $49, $6B, $B5, $AD, $2A, $29, $A9, $92, $CA, $6A .byte $AB, $B6, $55, $DB, $54, $4A, $85, $AA, $55, $AA, $AA, $6A, $55, $55, $95, $D5 .byte $AA, $AA, $55, $09, $95, $AA, $B6, $95, $DA, $B6, $B5, $92, $4A, $49, $95, $A4 .byte $B6, $AA, $5A, $B5, $6A, $55, $52, $AA, $DA, $56, $92, $54, $B5, $AA, $AA, $B5 .byte $AD, $56, $A9, $2A, $25, $49, $AA, $5A, $B5, $5A, $B5, $6A, $AD, $24, $A9, $AA .byte $B4, $6A, $55, $A5, $6A, $55, $AB, $55, $D5, $CA, $AA, $24, $49, $AA, $DA, $54 .byte $AD, $6B, $55, $95, $AA, $56, $82, $5A, $D5, $56, $53, $D5, $6A, $55, $A9, $AA .byte $6A, $55, $49, $A5, $4A, $A9, $55, $B5, $6D, $55, $55, $55, $95, $A4, $92, $6A .byte $AB, $55, $6B, $55, $55, $55, $A9, $4A, $A5, $D6, $4A, $A5, $4A, $55, $6D, $AB .byte $AA, $55, $55, $95, $92, $54, $B5, $52, $AB, $6D, $AB, $54, $6A, $2B, $25, $4A .byte $55, $AB, $55, $95, $5A, $55, $5A, $B5, $6A, $55, $A5, $52, $95, $4A, $55, $AD .byte $B6, $B5, $AA, $54, $55, $95, $24, $55, $55, $AB, $6A, $6B, $A9, $52, $AB, $AA .byte $52, $AD, $AA, $52, $A5, $D4, $AA, $D5, $5A, $B5, $AA, $54, $52, $AA, $AA, $52 .byte $55, $B5, $6D, $95, $AA, $A9, $AA, $4A, $AA, $5A, $55, $55, $55, $55, $A9, $55 .byte $5B, $B5, $94, $2A, $95, $AA, $54, $55, $AD, $D6, $56, $55, $55, $95, $52, $A9 .byte $6A, $AA, $5A, $AD, $5A, $29, $55, $AB, $AA, $AA, $AA, $2A, $95, $54, $B5, $AA .byte $5A, $AB, $55, $AB, $24, $55, $55, $55, $AA, $6A, $D5, $AA, $55, $55, $AA, $AA .byte $54, $55, $AB, $54, $AA, $55, $A9, $AA, $D5, $5A, $55, $55, $A9, $52, $95, $AA .byte $AA, $55, $B5, $6A, $55, $55, $4A, $55, $55, $95, $55, $55, $55, $2B, $55, $55 DMC06_End DMC09: .byte $DA, $BB, $77, $7B, $B5, $6D, $AD, $5A, $B5, $D5, $54, $B5, $5B, $25, $91, $24 .byte $21, $49, $A5, $54, $95, $2A, $A5, $AD, $12, $49, $68, $DB, $F7, $6E, $DB, $D6 .byte $AA, $55, $AB, $55, $DB, $D6, $4A, $12, $09, $92, $94, $54, $AA, $94, $54, $AA .byte $2A, $A5, $56, $6B, $DB, $B6, $AD, $B6, $55, $DB, $DA, $AD, $24, $A4, $AA, $AD .byte $4A, $92, $84, $54, $A9, $54, $49, $A9, $95, $44, $55, $BB, $B6, $ED, $6D, $4B .byte $55, $6B, $DB, $AD, $52, $5B, $55, $25, $48, $92, $2A, $A5, $92, $54, $55, $92 .byte $24, $54, $7D, $6F, $DB, $6D, $AF, $A4, $6A, $65, $BB, $6D, $2B, $09, $4A, $15 .byte $49, $52, $55, $A5, $6A, $AB, $24, $A2, $5D, $4B, $52, $6B, $B5, $6A, $7B, $B7 .byte $A9, $5A, $B5, $52, $DA, $5E, $55, $92, $54, $42, $04, $D1, $6E, $2B, $45, $40 .byte $ED, $56, $A9, $D5, $B6, $6D, $7B, $AB, $AA, $6D, $B5, $52, $55, $A1, $92, $2A .byte $55, $B5, $24, $09, $82, $BA, $D6, $5D, $AB, $56, $92, $52, $D5, $6A, $ED, $76 .byte $55, $5F, $09, $A2, $6C, $BB, $B5, $2A, $59, $95, $24, $24, $A9, $52, $A5, $B6 .byte $25, $21, $52, $DB, $B6, $EF, $6D, $55, $55, $D5, $5A, $A9, $DA, $AA, $4A, $A4 .byte $AA, $52, $12, $A1, $4A, $6D, $57, $A2, $95, $DA, $EE, $5B, $21, $92, $EA, $D6 .byte $EA, $65, $AB, $4A, $94, $A4, $AD, $ED, $A4, $6E, $97, $24, $80, $DA, $4A, $48 .byte $93, $DA, $56, $DF, $0A, $50, $DB, $56, $B5, $DB, $B7, $B5, $24, $A5, $A2, $AA .byte $AA, $96, $52, $29, $11, $92, $AA, $AA, $DA, $B6, $77, $5B, $49, $EA, $9B, $00 .byte $52, $AD, $F6, $EF, $92, $48, $54, $9B, $24, $55, $FF, $36, $55, $15, $49, $52 .byte $AA, $4A, $12, $6A, $F5, $4D, $22, $DA, $ED, $2B, $A9, $B6, $FB, $AA, $6A, $D5 .byte $44, $40, $AA, $CA, $6E, $AF, $24, $11, $12, $A5, $EA, $55, $B5, $ED, $DB, $0A .byte $82, $E4, $5E, $55, $6A, $77, $15, $24, $BB, $AD, $24, $A5, $7D, $FF, $4A, $42 .byte $92, $24, $91, $A4, $2A, $95, $DC, $2E, $49, $ED, $B6, $6A, $55, $AB, $AD, $5A .byte $D5, $B6, $25, $55, $42, $A4, $B6, $B7, $92, $24, $11, $84, $DA, $F6, $BB, $56 .byte $95, $55, $49, $48, $2A, $B5, $EF, $DB, $54, $49, $A4, $DA, $B6, $DB, $55, $AA .byte $24, $54, $55, $2A, $49, $4A, $2B, $49, $52, $AD, $BE, $5F, $84, $CA, $6A, $BB .byte $B6, $B6, $BB, $4A, $92, $24, $C9, $B6, $55, $49, $52, $AD, $4A, $92, $94, $AA .byte $AA, $AA, $F6, $AA, $6A, $5B, $89, $64, $F5, $B6, $94, $DA, $76, $57, $49, $90 .byte $54, $BB, $BD, $92, $24, $92, $A4, $AA, $92, $44, $55, $B5, $7A, $B7, $55, $D5 .byte $56, $55, $55, $D5, $B6, $6D, $55, $92, $50, $55, $B5, $5A, $29, $91, $44, $D5 .byte $4A, $A5, $2A, $69, $AD, $DE, $6A, $AD, $B6, $A9, $95, $4A, $55, $55, $B5, $6D .byte $AD, $34, $95, $24, $49, $6A, $5B, $25, $91, $52, $55, $4A, $55, $B5, $AA, $D5 .byte $F6, $7D, $25, $21, $AD, $6D, $B5, $AA, $2A, $B5, $AD, $92, $8A, $92, $52, $49 .byte $95, $2A, $49, $95, $2A, $DB, $76, $BB, $4A, $A9, $D5, $DB, $56, $55, $29, $95 .byte $A4, $B4, $77, $95, $44, $AA, $55, $49, $A9, $2A, $A9, $AA, $A5, $24, $95, $DA .byte $DE, $B5, $54, $55, $6D, $6B, $D5, $AA, $9A, $AA, $6A, $93, $A4, $AA, $95, $4A .byte $49, $12, $49, $B5, $9A, $AA, $52, $AD, $56, $AD, $DD, $B6, $AD, $52, $DB, $AA .byte $92, $24, $A9, $2A, $B5, $AA, $5A, $55, $92, $52, $55, $55, $55, $55, $B5, $2A .byte $25, $49, $6D, $DB, $B6, $5A, $DB, $AA, $AA, $54, $AA, $AA, $5A, $AD, $A4, $AA .byte $52, $A5, $54, $4A, $25, $49, $52, $EB, $B6, $AA, $6A, $AB, $B5, $D5, $F6, $AA .byte $A5, $4A, $89, $84, $AA, $B6, $52, $A9, $AD, $AA, $54, $AA, $54, $D5, $6A, $55 .byte $49, $4A, $6B, $DB, $52, $2A, $D5, $6A, $DB, $F6, $AA, $12, $A1, $AA, $55, $A9 .byte $56, $AB, $2A, $A9, $24, $29, $A9, $54, $D5, $6A, $6B, $AB, $6D, $55, $A9, $6A .byte $D5, $DA, $B6, $2A, $25, $91, $A4, $D4, $AA, $94, $5A, $6D, $AB, $54, $AA, $55 .byte $A5, $AA, $5A, $35, $95, $52, $B5, $56, $55, $AB, $56, $55, $B5, $6D, $2B, $49 .byte $90, $54, $59, $6D, $DB, $2A, $49, $2A, $55, $A9, $AA, $AA, $56, $AB, $5A, $B5 .byte $5A, $55, $55, $DB, $2A, $95, $AA, $AA, $92, $84, $24, $AD, $6D, $DB, $B6, $5A .byte $49, $92, $52, $B5, $56, $AB, $4A, $52, $6B, $AB, $A9, $94, $56, $AD, $D5, $5A .byte $55, $25, $21, $A9, $6A, $D5, $B6, $ED, $AA, $92, $10, $A9, $AA, $AD, $AA, $AA .byte $55, $AB, $4A, $55, $55, $B5, $D5, $5A, $A9, $94, $2A, $55, $2A, $4B, $55, $55 .byte $BB, $6D, $55, $29, $51, $2A, $AB, $AA, $DA, $5A, $95, $94, $54, $B5, $5A, $55 .byte $55, $D5, $DA, $AA, $92, $A4, $92, $6A, $DB, $B6, $55, $55, $B5, $92, $44, $92 .byte $54, $B5, $B5, $5A, $55, $55, $A5, $54, $55, $DB, $5E, $55, $AA, $4A, $95, $24 .byte $55, $B5, $5A, $AB, $B5, $D5, $54, $4A, $A2, $A4, $B2, $6D, $AB, $52, $55, $55 .byte $49, $55, $B5, $B5, $2A, $D5, $B6, $55, $49, $42, $AD, $F6, $6A, $5B, $55, $92 .byte $52, $29, $49, $2A, $A9, $6A, $6B, $B5, $6A, $AD, $AA, $4A, $55, $AB, $B5, $5A .byte $4B, $49, $92, $B4, $6A, $55, $AB, $B6, $5A, $29, $49, $55, $A9, $52, $55, $D5 .byte $AA, $55, $35, $55, $2A, $B5, $55, $55, $AB, $B6, $5A, $A5, $AA, $94, $AA, $B6 .byte $AD, $2A, $A9, $24, $29, $95, $52, $A5, $AA, $5A, $AB, $6D, $5B, $55, $95, $AA .byte $D6, $AA, $55, $A9, $AA, $52, $49, $A9, $AA, $D6, $6A, $55, $A9, $AA, $94, $4A .byte $55, $AB, $2A, $55, $55, $B5, $56, $AB, $AA, $4A, $AB, $56, $AD, $6A, $55, $A9 .byte $52, $A9, $6A, $55, $A5, $4A, $55, $49, $49, $55, $55, $55, $AB, $B6, $B6, $5A .byte $AB, $55, $95, $4A, $55, $AD, $AA, $4A, $A5, $54, $A9, $AA, $54, $B5, $D5, $56 .byte $55, $2A, $49, $A9, $AA, $B6, $56, $55, $55, $55, $6B, $55, $4D, $55, $A9, $AA .byte $D5, $AA, $AA, $52, $29, $A9, $6A, $D5, $4A, $55, $49, $49, $55, $AB, $56, $D5 .byte $5A, $6D, $AD, $5A, $A9, $AA, $AA, $4A, $53, $A9, $52, $AB, $AA, $52, $AA, $D4 .byte $AA, $5A, $55, $55, $AD, $4A, $95, $52, $55, $6D, $AB, $56, $69, $55, $55, $55 .byte $55, $55, $AA, $AA, $56, $A9, $2A, $55, $A9, $AA, $AA, $AA, $5A, $55, $A9, $52 .byte $55, $B5, $6A, $D5, $AA, $D5, $5A, $55, $A5, $4A, $95, $95, $2A, $55, $55, $55 .byte $A9, $AA, $AA, $AA, $AA, $AA, $55, $AD, $54, $55, $55, $A9, $AA, $D5, $5A, $AD .byte $AA, $AA, $52, $55, $A9, $52, $AA, $AA, $56, $55, $55, $A9, $6A, $55, $55, $55 .byte $55, $55, $55, $55, $55, $55, $55, $B5, $D5, $AA, $AA, $AA, $4A, $49, $55, $55 .byte $55, $55, $A9, $95, $AA, $AA, $AA, $5A, $55, $55, $AB, $AA, $AA, $AA, $54, $55 DMC09_End DMC05: .byte $00, $FE, $FF, $5F, $62, $00, $00, $00, $FF, $FF, $FF, $27, $00, $00, $E8, $FF .byte $FF, $07, $00, $FC, $FF, $FF, $FF, $00, $00, $00, $F8, $FF, $03, $00, $80, $FD .byte $FF, $7F, $00, $00, $B0, $FF, $FF, $01, $00, $00, $FF, $FF, $FF, $0F, $00, $00 .byte $FE, $FF, $05, $00, $00, $FA, $FF, $5F, $04, $80, $EE, $FF, $FF, $1F, $00, $00 .byte $00, $FA, $FF, $03, $00, $BA, $FF, $FF, $5F, $00, $00, $D0, $EF, $EF, $08, $00 .byte $40, $FF, $FF, $FF, $08, $00, $D8, $FF, $FF, $02, $00, $00, $F8, $FF, $6F, $13 .byte $01, $6A, $FB, $B6, $AA, $00, $40, $B6, $FF, $B7, $12, $24, $B5, $ED, $6B, $49 .byte $00, $64, $BB, $B7, $93, $44, $A9, $55, $F5, $ED, $AB, $44, $80, $94, $A6, $B7 .byte $9B, $54, $6A, $95, $56, $B5, $5A, $35, $95, $22, $A4, $B6, $6A, $DB, $6A, $25 .byte $A9, $AA, $6D, $BB, $15, $04, $A4, $6A, $6F, $AB, $12, $6A, $DB, $B6, $95, $54 .byte $92, $A2, $D4, $6A, $6F, $A5, $42, $55, $D5, $EA, $5B, $09, $49, $AD, $B6, $56 .byte $25, $28, $E2, $ED, $7E, $4C, $2B, $41, $92, $6D, $BF, $AB, $24, $00, $52, $6F .byte $B7, $B5, $6A, $49, $55, $5B, $AB, $6A, $13, $08, $69, $DB, $5A, $95, $24, $BA .byte $6F, $57, $45, $48, $55, $DB, $AD, $85, $24, $42, $D5, $F6, $BD, $AD, $24, $02 .byte $D5, $F6, $56, $93, $08, $51, $DB, $BE, $AD, $2A, $24, $49, $6F, $B7, $A9, $22 .byte $08, $B5, $DD, $AB, $A9, $44, $59, $6D, $6F, $25, $91, $94, $AA, $B6, $6D, $A9 DMC05_B:.byte $92, $A8, $DA, $75, $AB, $24, $91, $6A, $BB, $5D, $25, $41, $94, $6D, $D7, $AA .byte $52, $29, $55, $6D, $B5, $A9, $52, $49, $B5, $6D, $4B, $A2, $54, $D5, $B6, $5A .byte $29, $92, $54, $DB, $6D, $2D, $29, $52, $A5, $B6, $6D, $93, $4A, $52, $6B, $D5 .byte $92, $AA, $AA, $55, $5B, $55, $91, $54, $59, $B5, $55, $55, $49, $A5, $DA, $B6 DMC05_C:.byte $AB, $8A, $42, $A5, $F6, $B2, $25, $49, $56, $6D, $B5, $A9, $94, $CA, $AA, $6A .byte $AB, $4A, $A5, $54, $69, $DB, $2B, $A9, $A4, $AA, $6A, $6D, $25, $49, $AD, $56 .byte $55, $65, $45, $95, $6B, $D5, $55, $85, $8A, $52, $EF, $B6, $B5, $24, $A0, $AA .byte $D5, $55, $95, $52, $55, $B5, $5A, $AB, $A2, $AA, $AA, $B6, $55, $A5, $82, $54 .byte $DB, $6D, $55, $49, $48, $DB, $DE, $AA, $52, $09, $95, $6A, $5B, $55, $93, $2A .byte $55, $6B, $CB, $96, $52, $C9, $EA, $D5, $54, $42, $52, $B5, $6D, $6B, $A9, $42 .byte $25, $BB, $76, $AD, $54, $88, $AA, $B6, $6D, $55, $09, $69, $6B, $AB, $55, $92 .byte $24, $55, $7F, $A5, $52, $A5, $54, $6A, $57, $55, $55, $95, $AA, $B5, $55, $51 .byte $49, $52, $BB, $B7, $25, $24, $A9, $D8, $F5, $55, $95, $48, $92, $DD, $B7, $53 .byte $22, $01, $69, $DF, $B7, $96, $22, $24, $69, $DF, $5E, $95, $12, $29, $69, $5B .byte $AB, $A9, $20, $A9, $6D, $6F, $5B, $51, $2A, $55, $96, $AC, $4A, $B5, $2D, $4B .byte $A9, $65, $55, $95, $AD, $2A, $95, $A5, $D4, $6A, $57, $25, $92, $AA, $DA, $6D DMC05_End ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The following two LUTs are used together via Update_Request ; See index values UPDATERASTER_* IntNMI_UpdSel_Table: .byte $a0 ; UPDATERASTER_32PIXPART (opt. flag UPDATERASTER_32PIXSHOWSPR) .byte $40 ; UPDATERASTER_SPADEGAME .byte $a0 ; UPDATERASTER_WATERLINE IntNMI_Raster_Table: .byte $40 ; UPDATERASTER_32PIXPART (opt. flag UPDATERASTER_32PIXSHOWSPR) .byte $60 ; UPDATERASTER_SPADEGAME .byte $a0 ; UPDATERASTER_WATERLINE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; NMI INTERRUPT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IntNMI: SEI ; Prevent further masked Interrupts ; This pushes all the registers onto the stack PHP ; Push processor status onto stack PHA ; Push accumulator onto stack TXA ; Reg X -> A PHA ; Push A (X) onto stack TYA ; Reg Y -> A PHA ; Push A (Y) onto stack ; Push the three temp vars onto the stack LDA = 1, this converts it to a zero-based index AND #$7f ; Keep lower 7 bits TAY ; -> 'Y' DEY ; Y-- (zero base index) ; Get which update path we're going to follow LDA IntNMI_UpdSel_Table,Y STA Update_Select ; Apply Raster_Effect as requested LDA IntNMI_Raster_Table,Y STA Raster_Effect PRG031_F4AB: LDA UpdSel_Disable BEQ PRG031_F4B3 ; If UpdSel_Disable is not set, jump to PRG031_F4B3 (perform Update_Select activity) JMP PRG031_F567 ; Otherwise, jump to PRG031_F567 PRG031_F4B3: ; Update_Select activity begin... LDA Update_Select ; Get the Update_Select value CMP #$80 ; BNE PRG031_F4BD ; If Update_Select <> $80 (Purely vertical level), jump to PRG031_F4BD JMP UpdSel_Vertical ; Otherwise, jump to UpdSel_Vertical PRG031_F4BD: CMP #$40 ; BNE PRG031_F4C4 ; If Update_Select <> $40 (Spade Game), jump to PRG031_F4C4 JMP UpdSel_Roulette ; Otherwise, jump to UpdSel_Roulette PRG031_F4C4: CMP #$00 ; BNE PRG031_F4D5 ; If Update_Select <> $00 FIXME, jump to PRG031_F4D5 ; MMC3 event LDA #MMC3_8K_TO_PRG_A000 ; Changing PRG ROM at A000 STA MMC3_COMMAND ; Set MMC3 command LDA #26 ; Page 26 STA MMC3_PAGE ; Set MMC3 page JMP PRG031_F610 ; PRG031_F4D5: CMP #$a0 ; BNE PRG031_F4DC ; If Update_Select <> $A0 (32 pixel parition), jump to PRG031_F4DC JMP UpdSel_32PixPart ; Otherwise, jump to UpdSel_32PixPart PRG031_F4DC: CMP #$20 ; BNE PRG031_F4E3 ; If Update_Select <> $20 (Title Screen), jump to PRG031_F4E3 JMP UpdSel_Title ; Otherwise, jump to UpdSel_Title PRG031_F4E3: ; "Normal" update ($C0) LDA #$00 ; A = 0 STA PPU_CTL2 ; Hide sprites and bg (most importantly) STA PPU_SPR_ADDR ; Resets to sprite 0 in memory LDA #$02 ; A = 2 STA SPR_DMA ; DMA sprites from RAM @ $200 (probably trying to blank them out) JSR PT2_Full_CHRROM_Switch ; Set up PT2 (Sprites) CHRROM LDA 0, jump to PRG031_F51D LDA #MMC3_8K_TO_PRG_A000 ; Changing PRG ROM at A000 STA MMC3_COMMAND ; Set MMC3 command LDA #26 ; Page 26 STA MMC3_PAGE ; Set MMC3 page JSR Scroll_Commit_Column ; Update nametable as screen scrolls (differs from call made in UpdSel_Vertical, UpdSel_32PixPart) JSR Video_Misc_Updates ; Various updates other than scrolling (palettes, status bar, etc.) JSR TileChng_VRAMCommit ; Commit 16x16 tile change to VRAM ; Set pages at A000 and C000 JSR PRGROM_Change_Both LDA 0, jump to PRG031_F5D3 LDA #MMC3_8K_TO_PRG_A000 ; Changing PRG ROM at A000 STA MMC3_COMMAND ; Set MMC3 command LDA #26 ; Page 26 STA MMC3_PAGE ; Set MMC3 page JSR Scroll_ToVRAM_Apply ; Applies Scroll_ToVRAMHi and Scroll_ToVRAMHA updates JSR Video_Misc_Updates ; Various updates other than scrolling (palettes, status bar, etc.) JSR TileChng_VRAMCommit ; Commit 16x16 tile change to VRAM ; Set pages at A000 and C000 JSR PRGROM_Change_Both LDA 1 (only other value would be 2, during the level "opening" effect, not used in US version), jump to PRG031_F62E ; This is being called while level is being entered... JSR Map_EnterLevel_Effect ; World map "entering" effect on page 26 JMP PRG031_F631 ; Jump to PRG031_F631 PRG031_F62E: JSR Level_Opening_Effect ; Level "opening" effect on page 26 (unused on US release) PRG031_F631: LDA PPU_STAT LDA 0, jump to PRG031_F6BC LDA #MMC3_8K_TO_PRG_A000 ; Changing PRG ROM at A000 STA MMC3_COMMAND ; Set MMC3 command LDA #26 ; Page 26 STA MMC3_PAGE ; Set MMC3 page JSR Scroll_Commit_Column ; Update nametable as screen scrolls (differs from call made in UpdSel_Vertical, UpdSel_32PixPart) JSR Video_Misc_Updates ; Various updates other than scrolling (palettes, status bar, etc.) JSR TileChng_VRAMCommit ; Commit 16x16 tile change to VRAM ; Set pages at A000 and C000 JSR PRGROM_Change_Both LDA 0, go to PRG031_F748 LDA A PHA ; Push A (X) onto stack TYA ; Reg Y -> A PHA ; Push A (Y) onto stack LDA Reset_Latch CMP #$5a BEQ PRG031_F7B0 ; If Reset_Latch = $5A, go to PRG031_F7B0 ; Reset_Latch was something other than the magic $5A value... reset!! ; This gets the address of the Reset entry point -> [Temp_Var2][Temp_Var1] LDA Vector_Table+2 STA 17 (N-Spade), jump to PRG031_F7F8 LDY #$03 ; Y = $03 PRG031_F7F8: LDX #$00 ; X = 0 CMP #$00 ; BEQ PRG031_F871 ; If tileset = 0 (World map), go to PRG031_F871 CMP #$07 ; BEQ PRG031_F871 ; If tileset = 7 (Toad house), go to PRG031_F871 CMP #17 BEQ PRG031_F871 ; If tileset = 17 (N-Spade), go to PRG031_F871 ; Unknown hardware thing? Is this for synchronization? LDA #$00 STA PPU_VRAM_ADDR LDA #$00 STA PPU_VRAM_ADDR STA PPU_VRAM_ADDR STA PPU_VRAM_ADDR STX PPU_CTL2 ; Sprites + BG invisible LDA PPU_STAT ; ; Because vertical scroll will not change after frame begins (second write to ; PPU_SCROLL will always be unused until next frame), the hack for MMC3 split ; vertical scrolling is to change the nametable address that the PPU is reading ; at to where we would like it to be... ; In this case, the location of the beginning of the status bar! STY PPU_VRAM_ADDR ; This is $0B unless tileset = $11, which it is then $03 LDA #$00 STA PPU_VRAM_ADDR ; ... so we're now reading at $1100 or $0300 LDA PPU_VRAM_DATA ; Load status bar graphics and hide any sprites from appearing over the status bar ; Load two parts of Status Bar LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND LDA StatusBarCHR_0000 STA MMC3_PAGE LDA #MMC3_2K_TO_PPU_0800 STA MMC3_COMMAND LDA StatusBarCHR_0800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND ; Use blank tiles for all sprite graphics LDA SpriteHideCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteHideCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteHideCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteHideCHR_1C00 STA MMC3_PAGE LDA #$18 ; STA PPU_CTL2 ; Sprites + BG now visible JMP PRG031_F8B3 PRG031_F871: ; World map / Toad House / N-Spade ; Load status bar graphics and hide any sprites from appearing over the status bar ; Load two parts of Status Bar LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND LDA StatusBarMTCHR_0000 STA MMC3_PAGE LDA #MMC3_2K_TO_PPU_0800 STA MMC3_COMMAND LDA StatusBarMTCHR_0800 ; Load sprite graphics appropriate for World Map / Toad House / N-Spade STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND LDA SpriteMTCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteMTCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteMTCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteMTCHR_1C00 STA MMC3_PAGE PRG031_F8B3: LDA 0, loop ; Unknown hardware thing? Is this for synchronization? LDA #$00 STA PPU_VRAM_ADDR LDX #$00 STX PPU_VRAM_ADDR STX PPU_VRAM_ADDR STX PPU_VRAM_ADDR STX PPU_CTL2 ; Hide BG + Sprites LDA PPU_STAT ; ; Because vertical scroll will not change after frame begins (second write to ; PPU_SCROLL will always be unused until next frame), the hack for MMC3 split ; vertical scrolling is to change the nametable address that the PPU is reading ; at to where we would like it to be... LDY #$07 STY PPU_VRAM_ADDR STX PPU_VRAM_ADDR ; ... so we're now reading at $0700 LDA PPU_VRAM_DATA ; Couple of tilesets have slightly different effects LDA Level_Tileset CMP #$00 ; BEQ PRG031_F955 ; If A = 0 (On world map), go to PRG031_F955 CMP #$07 ; BEQ PRG031_F955 ; If A = 7 (Toad house), go to PRG031_F955 ; But not N-Spade? ; Load status bar graphics and hide any sprites from appearing over the status bar ; Load two parts of Status Bar LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND LDA StatusBarCHR_0000 STA MMC3_PAGE LDA #MMC3_2K_TO_PPU_0800 STA MMC3_COMMAND LDA StatusBarCHR_0800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND ; Use blank tiles for all sprite graphics LDA SpriteHideCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteHideCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteHideCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteHideCHR_1C00 STA MMC3_PAGE JMP PRG031_F997 ; Jump to PRG031_F997 PRG031_F955: ; World Map and Toad House alternate (but not N-Spade?) ; Load status bar graphics and hide any sprites from appearing over the status bar ; Load two parts of Status Bar LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND LDA StatusBarMTCHR_0000 STA MMC3_PAGE LDA #MMC3_2K_TO_PPU_0800 STA MMC3_COMMAND LDA StatusBarMTCHR_0800 STA MMC3_PAGE ; Load sprite graphics appropriate for World Map / Toad House / N-Spade LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND LDA SpriteMTCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteMTCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteMTCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteMTCHR_1C00 STA MMC3_PAGE PRG031_F997: LDA #$18 ; A | 18 (BG + SPR) STA PPU_CTL2 ; Sprites/BG are visible LDA 0, loop LDA #$10 ; STA PPU_CTL2 ; Only show sprites (?) LDA PPU_STAT ; ; Because vertical scroll will not change after frame begins (second write to ; PPU_SCROLL will always be unused until next frame), the hack for MMC3 split ; vertical scrolling is to change the nametable address that the PPU is reading ; at to where we would like it to be... LDY #$0a ; Y = $0A LDA #$80 ; A = $80 STY PPU_VRAM_ADDR ; STA PPU_VRAM_ADDR ; ... so we're now reading at $0A80, the top of the last two rows of tiles LDA PPU_VRAM_DATA ; JMP IntIRQ_32PixelPartition_Part2 ; Jump to IntIRQ_32PixelPartition_Part2 ; Something removed? NOP NOP NOP NOP IntIRQ_32PixPart_HideSprites: ; $F9E3 ; This part is skippable based on a flag; only loads ; Pattern Table 2 in this case... LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND LDA SpriteHideCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteHideCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteHideCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteHideCHR_1C00 STA MMC3_PAGE IntIRQ_32PixelPartition_Part3: LDA PPU_STAT LDA 0, loop ; Unknown hardware thing? Is this for synchronization? LDA #$00 STA PPU_VRAM_ADDR LDX #$00 STX PPU_VRAM_ADDR STX PPU_VRAM_ADDR STX PPU_VRAM_ADDR STX PPU_CTL2 ; Sprites + BG hidden LDA PPU_STAT ; ; Because vertical scroll will not change after frame begins (second write to ; PPU_SCROLL will always be unused until next frame), the hack for MMC3 split ; vertical scrolling is to change the nametable address that the PPU is reading ; at to where we would like it to be... LDY #$0b STY PPU_VRAM_ADDR STX PPU_VRAM_ADDR ; ... so now we're reading at $0B00 LDA PPU_VRAM_DATA ; This loads graphics into the "BG" side (PT1) ; I think the only reason they're using labeled constants ; is so they could put this code in multiple spots, but it'd ; stay in sync if they needed to change the CHRROM banks. ; But that'd be the job of an assembler label, wouldn't it?? LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND LDA StatusBarCHR_0000 STA MMC3_PAGE LDA #MMC3_2K_TO_PPU_0800 STA MMC3_COMMAND LDA StatusBarCHR_0800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND LDA SpriteHideCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteHideCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteHideCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteHideCHR_1C00 STA MMC3_PAGE LDA #$18 ; STA PPU_CTL2 ; Sprites + BG now visible LDA 0, loop... CPY #$03 ; BNE PRG031_FB34 ; If Raster_State <> 3, jump to PRG031_FB34 ; Raster_State = 3 (load status bar graphics) ; This loads graphics into the "BG" side (PT1) ; I think the only reason they're using labeled constants ; is so they could put this code in multiple spots, but it'd ; stay in sync if they needed to change the CHRROM banks. ; But that'd be the job of an assembler label, wouldn't it?? LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND LDA StatusBarCHR_0000 STA MMC3_PAGE LDA #MMC3_2K_TO_PPU_0800 STA MMC3_COMMAND LDA StatusBarCHR_0800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND LDA SpriteHideCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteHideCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteHideCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteHideCHR_1C00 STA MMC3_PAGE PRG031_FB34: LDA PPU_STAT ; CPY #$03 ; BEQ PRG031_FB57 ; If Raster_State = 3, jump to PRG031_FB57 ; Raster_State < 3... LDA Roulette_PosHi,Y ; Get position for this row AND #$01 ; Nametable swaps $2000 / $2400 every odd/even unit (??) ORA 0, loop LDA #$10 ; STA PPU_CTL2 ; Only show sprites ; Only loads Pattern Table 2 in this case... LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND LDA SpriteHideCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteHideCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteHideCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteHideCHR_1C00 STA MMC3_PAGE LDA PPU_STAT LDA = 0, loop LDA #$18 ; STA PPU_CTL2 ; Sprites + BG now visible ; Dead code? Or timing/cycle filler NOP NOP NOP ; The following JSR does a delay then updates the IRQ ; counter latch and Resets it JSR PRG030_SUB_9F50 JMP IntIRQ_Finish_NoDis ; Clean up for THIS Raster_State... PRG031_FBE5: ; Raster_State <> 0... ; Some kind of delay loop? LDX #$03 ; X = $14 PRG031_FBE7: NOP ; ? DEX ; X-- BNE PRG031_FBE7 ; While X > 0, loop ; Unknown hardware thing? Is this for synchronization? LDA #$00 STA PPU_VRAM_ADDR LDX #$00 STX PPU_VRAM_ADDR STX PPU_VRAM_ADDR STX PPU_VRAM_ADDR STX PPU_CTL2 ; Most importantly, hide BG + Sprites LDA PPU_STAT ; ; Because vertical scroll will not change after frame begins (second write to ; PPU_SCROLL will always be unused until next frame), the hack for MMC3 split ; vertical scrolling is to change the nametable address that the PPU is reading ; at to where we would like it to be... LDY #$0b STY PPU_VRAM_ADDR STX PPU_VRAM_ADDR ; ... so now we're reading at $0B00 LDA PPU_VRAM_DATA ; This loads graphics into the "BG" side (PT1) ; I think the only reason they're using labeled constants ; is so they could put this code in multiple spots, but it'd ; stay in sync if they needed to change the CHRROM banks. ; But that'd be the job of an assembler label, wouldn't it?? LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND LDA StatusBarCHR_0000 STA MMC3_PAGE LDA #MMC3_2K_TO_PPU_0800 STA MMC3_COMMAND LDA StatusBarCHR_0800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1000 STA MMC3_COMMAND LDA SpriteHideCHR_1000 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1400 STA MMC3_COMMAND LDA SpriteHideCHR_1400 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1800 STA MMC3_COMMAND LDA SpriteHideCHR_1800 STA MMC3_PAGE LDA #MMC3_1K_TO_PPU_1C00 STA MMC3_COMMAND LDA SpriteHideCHR_1C00 STA MMC3_PAGE LDA #$18 ; STA PPU_CTL2 ; Sprites + BG now visible LDA = 0, loop! RTS ; Return StatusBar_DrawCardPiece: STY 0, loop! PRG031_FD7F: PLA ; Restore the target item STA Inventory_Items,Y ; Give it to the Player! RTS ; Return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Sprite_RAM_Clear ; ; This subroutine clears the local copy of OAM by filling ; the X/Y values in with $F8 and $01, essentially making ; them all invisible until actually needed ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Sprite_RAM_Clear: ; $FD84 LDY #$00 ; Y = 0 PRG031_FD86: LDA #$f8 ; A = $F8 STA Sprite_RAM, Y ; Next X value LDA #$01 ; A = $01 STA Sprite_RAM+1,Y ; Next Y value INY ; INY ; INY ; INY ; Y += 4 BNE PRG031_FD86 ; While Y does not equal zero (covers all 256 bytes) RTS ; Return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Scroll_PPU_Reset ; ; This subroutine Resets the scroll position of the nametable ; and its associated variables back to zero, and also reasserts ; that the PPU is displaying sprites in PT2, but interestingly ; does not set them to 8x16 ?? ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Scroll_PPU_Reset: ; $FD97 LDA #$00 ; STA PPU_SCROLL ; Horizontal scroll = 0 STA 0, loop (will write 256 times) DEX ; X-- BNE PRG031_FDE1 ; While <> 0, loop (will write 4 times) LDA 0, loop (will write 64 times) RTS ; Return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Clear_Nametable_Short ; ; This subroutine sets ClearPattern over half a nametable, which ; is the initial value of A ($20 for NT 0, $28 for NT2) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Clear_Nametable_Short: ; $FE02 LDA PPU_STAT ; LDA #$00 ; STA PPU_CTL1 ; Most likely most importantly to prevent any more Resets LDA 0, loop DEX ; X-- BNE PRG031_FE1B ; While X <> 0, loop! RTS ; Return ; FIXME: Anybody want to claim this? ; Seems to clear the screen based on specific Vert_Scroll values PRG031_FE25: .byte $00, $30, $70, $B0, $EF ; High and Low VRAM Addresses PRG031_FE2A: .byte $20, $20, $21, $22, $28 PRG031_FE2F: .byte $00, $C0, $C0, $C0, $00 ; $FE34 LDY #$04 ; Y = 4 LDA = 0, loop PRG031_FE40: ; Load FIXME values -> Temp_Var1/2 LDA PRG031_FE2A,Y STA 0, jump to PRG031_FE76 DEX ; X-- BEQ PRG031_FE98 ; If X = 0, jump to PRG031_FE98 (RTS) PRG031_FE76: ; Next VRAM byte LDA A PHA ; Push A JSR Read_Joypad ; Read Joypad PLA ; Pull A CMP X PLA ; Pull A AND #$f0 ; A &= $F0 ORA Read_Joypads_UnkTable,X ; FIXME: A |= Read_Joypads_UnkTable[X] PHA ; Save A STA Carry STA JOYPAD ; Clear strobe joypad 1 ; Needs cleanup and commentary, but basically this does 8 loops to ; read all buttons and store the result for return LDX #$08 ; X = 8 Read_Joypad_Loop: LDA JOYPAD,Y ; Get joypad data LSR A ROL Stack Pointer (NMI stack) LDA #MMC3_2K_TO_PPU_0000 STA MMC3_COMMAND ; Command MMC3 to change out first 2K of PPU LDY #$00 ; Y = 0 STY PPU_SCROLL ; Horizontal scroll = 0 STY PPU_SCROLL ; Vertical scroll = 0 STY MMC3_SRAM_EN ; Disable MMC3 SRAM (?) STY MMC3_IRQDISABLE ; Disable MMC3 IRQ generation LDA #%00001111 ; STA PAPU_EN ; Enable rectangle wave 1 & 2, triangle, and noise channels LDA #$00 ; STA PAPU_MODCTL ; disable DMC IRQs ; Any write to $4017 resets both the frame counter, and the clock divider. ; Sometimes, games will write to this register in order to synchronize the ; sound hardware's internal timing, to the sound routine's timing (usually ; tied into the Reset code). The frame IRQ frequency is slightly smaller than ; the PPU's vertical retrace frequency, so you can see why games would desire ; this syncronization. LDA #$40 ; STA FRAMECTR_CTL ; disable APU frame IRQ LDA PPU_STAT LDA #16 ; A = 16 TAX ; X = 16 ; The following writes 16 twice to PPU_VRAM_ADDR a total of 16 times ; Not sure what the point of this is? Must be some kind of hardware thing?? PRG031_FF80: STA PPU_VRAM_ADDR ; STA PPU_VRAM_ADDR ; EOR #$00 ; Do nothing?? DEX ; X-- BNE PRG031_FF80 ; While X > 0, loop LDA #$01 ; STA MMC3_MIRROR ; MMC3 command for Vertical mirroring LDA #$80 ; STA MMC3_SRAM_EN ; Re-enable MMC3 SRAM (?) ; Clear $07FF - $0000, excluding $01xx LDY #$07 JSR Clear_RAM_thru_ZeroPage LDA #25 STA PAGE_C000 LDA #24 STA PAGE_A000 JSR PRGROM_Change_Both2 ; Change A000 to page 25 and C000 to page 24 ; NOPs? NOP NOP NOP JMP IntReset_Part2 ; Rest of Reset continues in the $8000 bank... PT2_Full_CHRROM_Switch: ; $FFAD ; This subroutine does a full pattern table switchout LDY #$05 ; Loop Y from 5 to 0, effecting all pattern selections PT2_Full_CHRROM_Loop: TYA ; A = Y ORA #$40 ; A = 5 | $40 = $45 (When 5, MMC3_1K_TO_PPU_1C00; decrements thru other pages) STA MMC3_COMMAND ; Set MMC3 command LDA PatTable_BankSel,Y ; Offset into the Pattern Table 2 LUT for this page STA MMC3_PAGE ; Set MMC3 page DEY ; Y-- BPL PT2_Full_CHRROM_Loop ; While Y >= 0, loop! RTS ; Return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; PRGROM_Change_Both2 ; ; This subroutine sets both PRG ROM pages A000 and C000 together. ; It also writes to $0721, apparently to allow where it returns to ; reissue the MMC3 command (FIXME reason yet unknown ???) ; A000 is set to the page value found at RAM location PAGE_A000 ; C000 is set to the page value found at RAM location PAGE_C000 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PRGROM_Change_Both2: ; $FFBF JSR PRGROM_Change_C000 ; Change C000 first ; Continues into PRGROM_Change_A000 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; PRGROM_Change_A000 ; ; This subroutine sets the PRG ROM page at C000 ; C000 is set to the page value found at RAM location PAGE_A000 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PRGROM_Change_A000: ; $FFC2 LDA #MMC3_8K_TO_PRG_A000 ; Changing PRG ROM at A000 STA PAGE_CMD ; FIXME: Store @ PAGE_CMD STA MMC3_COMMAND ; Set MMC3 command LDA PAGE_A000 ; Page @ PAGE_A000 STA MMC3_PAGE ; Set MMC3 page RTS ; Return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; PRGROM_Change_C000 ; ; This subroutine sets the PRG ROM page at A000 ; A000 is set to the page value found at RAM location PAGE_C000 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PRGROM_Change_C000: ; $FFD1 LDA #MMC3_8K_TO_PRG_C000 ; Changing PRG ROM at C000 STA PAGE_CMD ; FIXME: Store @ PAGE_CMD STA MMC3_COMMAND ; Set MMC3 command LDA PAGE_C000 ; Page @ PAGE_C000 STA MMC3_PAGE ; Set MMC3 page RTS ; Return .byte $FF, $FF, $FF ; A marker of some kind? :) .byte "SUPER MARIO 3" ; Signature? .byte $00, $00, $6C, $56, $03, $00, $01, $0C, $01, $2D ; ASSEMBLER BOUNDARY CHECK, END OF $FFFA .Bound_FFFA: BoundCheck .Bound_FFFA, $FFFA, PRG031: Vector space ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; VECTORS ; Must appear at $FFFA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .org $FFFA Vector_Table: .word IntNMI ; $FFFA - NMI Interrupt (VBlank) .word IntReset ; $FFFC - Reset Interrupt (boot up) .word IntIRQ ; $FFFE - IRQ Interrupt (scanline from MMC3)