├── asm6f.exe ├── .gitignore ├── bin └── drmario_chr.bin ├── unused ├── drmario_unused_data_ffd9.asm ├── drmario_unused_data_eu_fc29.asm ├── drmario_unused_data_d2cc.asm ├── drmario_unused_data_ced5.asm ├── drmario_unused_data_ff32.asm └── drmario_unused_data_fafd.asm ├── compile.bat ├── prg ├── drmario_prg_audio_linker.asm ├── routines │ ├── drmario_prg_audio_init_status.asm │ ├── drmario_prg_audio_sfx_counter.asm │ ├── drmario_prg_audio_rnd_noise.asm │ ├── drmario_prg_audio_copy_sfx_data.asm │ └── drmario_prg_audio_update.asm ├── drmario_prg_reset.asm ├── drmario_prg_audio_general_a.asm ├── drmario_prg_audio_general_b.asm ├── drmario_prg_game_init.asm ├── drmario_prg_level_init.asm ├── drmario_prg_general.asm ├── drmario_prg_level_end.asm └── drmario_prg_visual_nametable.asm ├── header ├── drmario_header_ines.asm └── drmario_header_nintendo.asm ├── README.md ├── defines ├── drmario_macros.asm ├── drmario_registers.asm ├── drmario_ram_zp.asm └── drmario_ram.asm ├── data ├── drmario_data_demo_field_pills.asm ├── drmario_data_sfx.asm ├── drmario_data_demo_inputs.asm ├── drmario_data_metasprites.asm └── drmario_data_nametables.asm ├── drmario.asm └── samples └── drmario_samples_dmc.asm /asm6f.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nostaljipi/dr-mario-disassembly/HEAD/asm6f.exe -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _docs/ 2 | _original/ 3 | .vscode/ 4 | *.nes 5 | *.mlb 6 | compile_debug.bat 7 | checksum.py -------------------------------------------------------------------------------- /bin/drmario_chr.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nostaljipi/dr-mario-disassembly/HEAD/bin/drmario_chr.bin -------------------------------------------------------------------------------- /unused/drmario_unused_data_ffd9.asm: -------------------------------------------------------------------------------- 1 | if !removeUnused 2 | UNUSED_empty_FFD9: 3 | ;.db $FF,$FF,$FF,$FF,$FF,$FF,$FF 4 | pad $FFE0,$FF 5 | endif -------------------------------------------------------------------------------- /compile.bat: -------------------------------------------------------------------------------- 1 | @echo. 2 | @echo Compiling... 3 | asm6f drmario.asm drmario.nes 4 | @IF ERRORLEVEL 1 GOTO failure 5 | @echo. 6 | @echo. 7 | @echo Success! 8 | @pause 9 | 10 | @GOTO endbuild 11 | 12 | :failure 13 | @echo. 14 | @echo Build error! 15 | @pause 16 | 17 | :endbuild -------------------------------------------------------------------------------- /prg/drmario_prg_audio_linker.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; audio linkers [$FFD0] 3 | ;; 4 | ;; Seems to be there in order to support PRG bank swapping, assumed safe to optimize 5 | ;; 6 | if !optimize 7 | toAudioUpdate: 8 | jmp audioUpdate 9 | 10 | toInitAPU_var_chan: 11 | jmp initAPU_variablesAndChannels 12 | 13 | toInitAPU_status: 14 | jmp initAPU_status 15 | endif -------------------------------------------------------------------------------- /header/drmario_header_ines.asm: -------------------------------------------------------------------------------- 1 | INES_MAPPER = 1 ; 1 = Nintendo MMC1 2 | INES_MIRROR = 0 ; 0 = horizontal mirroring, 1 = vertical mirroring 3 | INES_SRAM = 0 ; 0 = no SRAM 4 | 5 | .byte 'N', 'E', 'S', $1A ; ID 6 | .byte $02 ; 16k PRG chunk count 7 | .byte $04 ; 8k CHR chunk count 8 | .byte INES_MIRROR | (INES_SRAM << 1) | ((INES_MAPPER & $0F) << 4) 9 | .byte (INES_MAPPER & %11110000) ;mapper high nybble 10 | .byte $00, $00, $00, $00, $00, $00, $00, $00 ; padding 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dr. Mario (NES) Disassembly 2 | This is a disassembly of Dr. Mario for the Nintendo Entertainment System. 3 | 4 | It builds Dr. Mario (Japan, USA) (Rev A).nes using ASM6f (https://github.com/freem/asm6f). 5 | 6 | Thanks to Sour, author of the excellent NES emulator Mesen (https://www.mesen.ca/), which debug options were vital to this project. 7 | 8 | Also, thanks to all contributors of the Dr.Mario articles at Data Crystal (https://datacrystal.romhacking.net/wiki/Dr._Mario) and The Cutting Room Floor (https://tcrf.net/Dr._Mario_(NES)). -------------------------------------------------------------------------------- /prg/routines/drmario_prg_audio_init_status.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; initAPU_status [$D2B7] 3 | ;; 4 | ;; Is only called during game init, enables all channels (except DMC), sets the random noise seed and then inits APU variables and channels 5 | ;; 6 | initAPU_status: 7 | lda #APUSTATUS_sq0_on + APUSTATUS_sq1_on + APUSTATUS_trg_on + APUSTATUS_noise_on 8 | sta APUSTATUS 9 | lda #rngSeed_noise 10 | sta sfx_rndNoisePeriod 11 | jsr initAPU_variablesAndChannels 12 | rts -------------------------------------------------------------------------------- /unused/drmario_unused_data_eu_fc29.asm: -------------------------------------------------------------------------------- 1 | if !removeUnused 2 | UNUSED_DATA_EU_FC29: 3 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 4 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 5 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 6 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 7 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 8 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 9 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 10 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 11 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 12 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 13 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF 14 | endif -------------------------------------------------------------------------------- /prg/routines/drmario_prg_audio_sfx_counter.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; sfx_increaseSectionCounter [$D2AC] 3 | ;; 4 | ;; Increases the current channel's sfx counter, and if we reach the length pre-determined for that section, reset the counter to 0 5 | ;; 6 | ;; Returns: 7 | ;; Zero flag: clear if section finished, set if not 8 | ;; 9 | sfx_increaseSectionCounter: 10 | ldx sfx_channel 11 | inc sfx_sectionCounter_noise,X 12 | lda sfx_sectionCounter_noise,X 13 | cmp sfx_sectionLength_noise,X 14 | bne @exit_sfx_increaseSectionCounter 15 | lda #$00 16 | sta sfx_sectionCounter_noise,X 17 | @exit_sfx_increaseSectionCounter: 18 | rts -------------------------------------------------------------------------------- /unused/drmario_unused_data_d2cc.asm: -------------------------------------------------------------------------------- 1 | if !removeUnused 2 | UNUSED_DATA_D2CC: 3 | if !ver_EU 4 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 5 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 6 | .db $FF,$FF,$FF,$FF,$00,$00,$00,$00 7 | .db $00,$00,$00,$00,$00,$00,$00,$00 8 | .db $00,$00,$00,$00,$00,$00,$00,$00 9 | .db $00,$00,$00,$00,$00,$00,$00,$00 10 | .db $00,$00,$00,$00 11 | else 12 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 13 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 14 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 15 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 16 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 17 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 18 | .db $FF,$FF,$FF,$FF 19 | endif 20 | endif -------------------------------------------------------------------------------- /prg/routines/drmario_prg_audio_rnd_noise.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; randomizeNoisePeriod [$D295] 3 | ;; 4 | ;; Generates two random numbers for use as noise period 5 | ;; An almost direct copy paste of randomNumberGenerator 6 | ;; 7 | randomizeNoisePeriod: 8 | if !optimize 9 | lda sfx_rndNoisePeriod 10 | and #$02 11 | sta sfx_rndNoisePeriod_tmp2 12 | lda sfx_rndNoisePeriod_tmp1 13 | and #$02 14 | eor sfx_rndNoisePeriod_tmp2 15 | clc 16 | beq @randomizeNoisePeriod_rotateRight 17 | sec 18 | @randomizeNoisePeriod_rotateRight: 19 | ror sfx_rndNoisePeriod 20 | ror sfx_rndNoisePeriod_tmp1 21 | rts 22 | else 23 | ldx #sfx_rndNoisePeriod 24 | ldy #rngSize 25 | jmp randomNumberGenerator 26 | endif -------------------------------------------------------------------------------- /defines/drmario_macros.asm: -------------------------------------------------------------------------------- 1 | ;Macros 2 | .macro setPPUADDR_absolute absADDR 3 | lda #>absADDR 4 | sta PPUADDR 5 | lda #> 1 27 | if n!=0 28 | lsr A 29 | endif 30 | endr 31 | n=i 32 | .endm 33 | 34 | .macro generateRandNum 35 | ldx #rng0 36 | ldy #rngSize 37 | jsr randomNumberGenerator 38 | .endm 39 | 40 | .macro _dw dwaddr 41 | .db >dwaddr 42 | .db APU ;High byte of APU register, starts at $4000 30 | sta APUADDR_hi 31 | sty sfxData_lo ;Low byte of sfx data (was held in y) 32 | lda #>sfx_data ;High byte of sfx data: starts at $D300 in vanilla 33 | sta sfxData_hi 34 | ldy #$00 35 | @fillChannelRegisters_loop: 36 | lda (sfxData),Y 37 | sta (APUADDR),Y 38 | iny 39 | if !optimize 40 | tya ;Why not compare directly to Y here? 41 | cmp #APU_channel_register_size ;4 times 42 | else 43 | cpy #APU_channel_register_size 44 | endif 45 | bne @fillChannelRegisters_loop 46 | _exit_sfxUses: 47 | rts -------------------------------------------------------------------------------- /defines/drmario_registers.asm: -------------------------------------------------------------------------------- 1 | ;PPU registers 2 | PPUCTRL = $2000 3 | PPUMASK = $2001 4 | PPUSTATUS = $2002 5 | OAMADDR = $2003 ;Set to zero, uses OAMDMA instead 6 | OAMDATA = $2004 ;Unused in this game 7 | PPUSCROLL = $2005 8 | PPUADDR = $2006 9 | PPUDATA = $2007 10 | OAMDMA = $4014 11 | 12 | ;APU registers (general) 13 | APU = $4000 14 | APU_SQ0 = $4000 15 | APU_SQ1 = $4004 16 | APU_TRG = $4008 17 | APU_NOISE = $400C 18 | APU_DMC = $4010 19 | 20 | ;APU registers (specific) 21 | SQ0_DUTY = $4000 22 | SQ0_SWEEP = $4001 23 | SQ0_TIMER = $4002 24 | SQ0_LENGTH = $4003 25 | SQ1_DUTY = $4004 26 | SQ1_SWEEP = $4005 27 | SQ1_TIMER = $4006 28 | SQ1_LENGTH = $4007 29 | TRG_LINEAR = $4008 30 | TRG_TIMER = $400A 31 | TRG_LENGTH = $400B 32 | NOISE_VOLUME = $400C 33 | NOISE_PERIOD = $400E 34 | NOISE_LENGTH = $400F 35 | DMC_FREQ = $4010 36 | DMC_COUNTER = $4011 37 | DMC_ADDR = $4012 38 | DMC_LENGTH = $4013 39 | APUSTATUS = $4015 40 | APUCOUNTER = $4017 ;Frame Counter, write-only 41 | 42 | ;Controller registers 43 | CTRL1 = $4016 44 | CTRL2 = $4017 ;Read-only 45 | 46 | ;Mapper registers (MMC1) 47 | MMCConfig = $9FFF 48 | CHRBank0 = $BFFF 49 | CHRBank1 = $DFFF 50 | PRGBank = $FFF0 51 | 52 | resetMMC1_UNUSED = $8000 ;I think the value at this memory address must be $80 or higher -------------------------------------------------------------------------------- /unused/drmario_unused_data_ff32.asm: -------------------------------------------------------------------------------- 1 | if !removeUnused 2 | UNUSED_DATA_FF32: 3 | if ver_revA 4 | .db $00,$00,$00,$00,$00,$00 5 | .db $00,$00,$00,$00,$00,$00,$00,$00 6 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 7 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 8 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 9 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 10 | .db $00,$00,$00,$00,$00,$00,$00,$00 11 | .db $00,$00,$00,$00,$00,$00,$00,$00 12 | .db $00,$00,$00,$00,$00,$00,$00,$00 13 | .db $00,$00,$00,$00,$00,$00,$00,$00 14 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 15 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 16 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 17 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 18 | .db $00,$00,$00,$00,$00,$00,$00,$00 19 | .db $00,$00,$00,$00,$00,$00,$00,$00 20 | .db $00,$00,$00,$00,$00,$00,$00,$00 21 | .db $00,$00,$00,$00,$00,$00,$00,$00 22 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 23 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 24 | elseif ver_EU 25 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 26 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 27 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 28 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 29 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 30 | .db $FF,$FF 31 | else 32 | .db $00,$00,$00,$00,$00,$00,$00,$00 33 | .db $00,$00,$00,$00,$00,$00,$00,$00 34 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 35 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 36 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 37 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 38 | .db $00,$00,$00,$00,$00,$00,$00,$00 39 | .db $00,$00,$00,$00,$00,$00,$00,$00 40 | .db $00,$00,$00,$00,$00,$00,$00,$00 41 | .db $00,$00,$00,$00,$00,$00,$00,$00 42 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 43 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 44 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 45 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 46 | .db $00,$00,$00,$00,$00,$00,$00,$00 47 | .db $00,$00,$00,$00,$00,$00,$00,$00 48 | .db $00,$00,$00,$00,$00,$00,$00,$00 49 | .db $00,$00,$00,$00,$00,$00,$00,$00 50 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 51 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 52 | endif 53 | endif -------------------------------------------------------------------------------- /drmario.asm: -------------------------------------------------------------------------------- 1 | ;Build config 2 | include build_config.asm 3 | 4 | ;Defines and macros 5 | include defines/drmario_ram_zp.asm 6 | include defines/drmario_ram.asm 7 | include defines/drmario_registers.asm 8 | include defines/drmario_constants.asm 9 | include defines/drmario_macros.asm 10 | 11 | ;iNes header 12 | include header/drmario_header_ines.asm 13 | 14 | ;Set the starting address 15 | org $8000 16 | 17 | ;PRG chunk 1 18 | include prg/drmario_prg_game_init.asm 19 | include prg/drmario_prg_level_init.asm 20 | include prg/drmario_prg_visual_nametable.asm 21 | include prg/drmario_prg_visual_sprites.asm 22 | include prg/drmario_prg_game_logic.asm 23 | 24 | ;Data chunk 1 25 | include data/drmario_data_game.asm 26 | include data/drmario_data_metasprites.asm 27 | 28 | ;PRG chunk 2 29 | include prg/drmario_prg_level_end.asm 30 | include prg/drmario_prg_general.asm 31 | 32 | ;Data chunk 2 33 | include data/drmario_data_nametables.asm 34 | include unused/drmario_unused_data_ced5.asm 35 | include data/drmario_data_demo_field_pills.asm 36 | align 256 ;Must be aligned on a 256-byte boundary (vanilla rom = $D000) 37 | include data/drmario_data_demo_inputs.asm 38 | 39 | ;Audio engine - general & sfx 40 | align 256 ;Must be aligned on a 256-byte boundary (vanilla rom = $D200) 41 | include prg/drmario_prg_audio_general_a.asm 42 | include unused/drmario_unused_data_d2cc.asm 43 | align 256 ;Must be aligned on a 256-byte boundary (vanilla rom = $D300) 44 | include data/drmario_data_sfx.asm 45 | include prg/drmario_prg_audio_general_b.asm 46 | include prg/drmario_prg_audio_sfx.asm 47 | 48 | ;Audio engine - music 49 | include prg/drmario_prg_audio_music.asm 50 | include data/drmario_data_music.asm 51 | if ver_EU 52 | include unused/drmario_unused_data_eu_fc29.asm 53 | include prg/drmario_prg_reset.asm 54 | endif 55 | include unused/drmario_unused_data_fafd.asm 56 | if $<$C000 57 | org $C000 ;Samples cannot be located before $C000 58 | endif 59 | align 64 ;Must be aligned on a 64-byte boundary (vanilla rom = $FD00) 60 | include samples/drmario_samples_dmc.asm 61 | 62 | ;End of rom 63 | if !ver_EU 64 | include prg/drmario_prg_reset.asm 65 | else 66 | include prg/routines/drmario_prg_audio_update.asm 67 | endif 68 | include unused/drmario_unused_data_ff32.asm 69 | include prg/drmario_prg_audio_linker.asm 70 | include unused/drmario_unused_data_ffd9.asm 71 | 72 | ;Nintendo header 73 | org $FFE0 ;Is on a specific address (doesn't impact the game though) 74 | include header/drmario_header_nintendo.asm 75 | 76 | ;Interrupt vectors 77 | org $FFFA ;Must be at this specific address 78 | word nmi, reset, irq 79 | 80 | ;CHR 81 | incbin bin/drmario_chr.bin -------------------------------------------------------------------------------- /samples/drmario_samples_dmc.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; dmcSample_code20_and40 & others [$FD00] 3 | ;; 4 | ;; 1-bit delta-PCM samples used by DMC channel (both are bongo/snare-like instruments, third one is unused) 5 | ;; 6 | dmcSample_code20_and40: ;F4 written to Dmc Address (in vanilla), actually 113 bytes long, not the full 128 bytes here (dmcSample_code20_and40) 7 | .db $74,$FF,$FF,$03,$24,$01,$82,$54 8 | .db $01,$10,$FF,$FF,$FF,$FF,$0F,$00 9 | .db $00,$B0,$FF,$7F,$09,$00,$00,$B8 10 | .db $FF,$FF,$0F,$00,$E0,$FF,$FF,$02 11 | .db $01,$00,$C0,$FF,$FF,$FF,$01,$00 12 | .db $00,$FE,$FF,$FF,$01,$00,$C0,$FE 13 | .db $5F,$02,$00,$FC,$FF,$FF,$FF,$00 14 | .db $00,$00,$E0,$FF,$FF,$07,$00,$00 15 | .db $FF,$FF,$1F,$00,$00,$F4,$FF,$FF 16 | .db $03,$00,$00,$FC,$FF,$FF,$1F,$00 17 | .db $00,$E0,$FF,$FF,$03,$00,$80,$FF 18 | .db $FF,$3F,$00,$00,$F0,$FF,$FF,$0F 19 | .db $00,$00,$80,$FF,$FF,$7F,$00,$00 20 | .db $FE,$FF,$02,$00,$00,$F8,$FF,$FF 21 | .db $3F 22 | if !removeMoreUnused 23 | dmcSample_code20_and40_overflow: 24 | .db $01,$00,$C0,$FF,$FF,$3F,$00,$00 ;Unused, but can't really be used for anything else (unless this data would be $0F bytes long maximum) 25 | .db $C0,$FF,$BF,$04,$25,$A9,$FF 26 | endif 27 | 28 | align 64 ;Each sample must be aligned on a 64-byte boundary 29 | 30 | dmcSample_code60_and80: ;F6 written to Dmc adress (in vanilla), actually 241 bytes long (dmcSample_code60_and80) 31 | .db $FF,$02,$00,$00,$E0,$FF,$FF,$5F 32 | .db $00,$00,$E0,$FF,$FF,$97,$04,$00 33 | .db $AC,$55,$B7,$93,$44,$F7,$EF,$5F 34 | .db $41,$00,$00,$D5,$7D,$EF,$5E,$11 35 | .db $00,$F5,$7D,$DF,$57,$02,$24,$5A 36 | .db $82,$6A,$35,$B5,$AA,$F6,$FD,$2F 37 | .db $25,$02,$00,$B5,$75,$7F,$4B,$42 38 | .db $04,$B4,$FD,$FF,$7E,$11,$00,$54 39 | .db $AA,$FA,$56,$82,$52,$DB,$7D,$AF 40 | .db $2A,$02,$80,$F6,$FF,$3F,$09,$00 41 | .db $00,$FB,$FD,$BF,$46,$00,$A5,$F6 42 | .db $FF,$2A,$00,$00,$E8,$FE,$FF,$2F 43 | .db $01,$00,$6A,$FF,$FF,$25,$00,$00 44 | .db $DC,$FF,$BF,$12,$00,$B0,$FB,$FF 45 | .db $5F,$04,$00,$00,$FB,$FF,$BF,$00 46 | .db $00,$E8,$FF,$FF,$13,$00,$00,$75 47 | .db $FF,$FF,$09,$00,$40,$EE,$FF,$7F 48 | .db $11,$00,$C0,$FD,$FF,$4B,$00,$00 49 | .db $B5,$FD,$FF,$2B,$00,$80,$F6,$FE 50 | .db $FF,$08,$02,$00,$F6,$FF,$5F,$49 51 | .db $00,$50,$F7,$7F,$AF,$08,$00,$A4 52 | .db $F6,$FF,$2D,$04,$11,$DA,$F7,$76 53 | .db $2B,$04,$90,$6B,$DF,$AD,$12,$44 54 | .db $50,$FD,$FE,$6A,$25,$41,$52,$D5 55 | .db $F6,$AA,$24,$51,$69,$DB,$DE,$4A 56 | .db $92,$28,$55,$DB,$B6,$AA,$52,$A1 57 | .db $52,$5B,$37,$55,$95,$54,$A9,$AD 58 | .db $5A,$55,$A5,$54,$55,$55,$AB,$55 59 | .db $25,$55,$A9,$D3,$D6,$4A,$95,$A4 60 | .db $AA,$5A,$AB,$4D,$95,$54,$B2,$DA 61 | .db $B6 62 | if !removeMoreUnused 63 | dmcSample_code60_and80_overflow: 64 | .db $A9,$92,$54,$54,$EB,$DA,$AA,$92 65 | .db $A4,$4A,$6B,$B7,$69,$92,$A4 66 | endif 67 | 68 | if !removeUnused 69 | dmcSample_UNUSED: ;If FA was written as sample addr, would have to be 113 bytes long I think (dmcSample_UNUSED) 70 | .db $52,$6F,$B5,$A9,$24,$A4,$6A,$DB 71 | .db $B6,$95,$22,$51,$6A,$FB,$56,$15 72 | .db $12,$29,$B5,$FB,$D6,$92,$90,$54 73 | .db $6D,$7B,$55,$91,$90,$A4,$77,$BF 74 | .db $A6,$02,$12,$A9,$F7,$DB,$4A,$11 75 | .db $92,$B4,$ED,$5D,$55,$82,$A4,$B6 76 | .db $B7,$2B,$49,$90,$A4,$F6,$77,$5B 77 | .db $49,$40,$52,$BB,$77,$2B,$09,$A1 78 | .db $6C,$BB,$6D,$95,$48,$A8,$DA,$DE 79 | .db $B5,$92,$08,$91,$6D,$F7,$B6,$24 80 | .db $89,$52,$6D,$DB,$AA,$24,$49,$55 81 | .db $AF,$5E,$95,$14,$A1,$B2,$7B,$B7 82 | .db $92,$92,$48,$69,$DB,$56,$95,$52 83 | .db $A9,$6A,$DB,$AA,$92,$24,$D5,$6A 84 | .db $AF 85 | dmcSample_UNUSED_overflow: 86 | .db $44,$44,$55,$44,$44,$44,$44,$33 87 | .db $22,$55,$AA,$00,$55,$00,$00 88 | endif -------------------------------------------------------------------------------- /data/drmario_data_sfx.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; sfx_data [$D300] 3 | ;; 4 | ;; The 4 bytes of sfx data to copy in a channel's register (each byte represents something slightly different depending on the channel) 5 | ;; 6 | sfx_data: 7 | 8 | if !removeUnused 9 | __sfx_UNK_D300_noCode: ;These unknown/unused must be left there because their index order must be preserved 10 | .db $03,$7F,$0F,$C0 ;Was also present in Tetris 11 | endif 12 | 13 | sfx_UFO_beam_NOISE: 14 | .db $33,$7F,$80,$C0 15 | 16 | if !removeUnused 17 | __sfx_UNK_D308_NOISE_noIndex: 18 | .db $3F,$7F,$82,$C0 19 | endif 20 | 21 | if !removeMoreUnused 22 | sfx_highToLowPitch_NOISE_UNUSED: 23 | .db $39,$7F,$00,$C0 24 | endif 25 | 26 | sfx_UFO_leaves_NOISE: 27 | .db $39,$7F,$0E,$C0 28 | 29 | sfx_UFO_any_NOISE_finished: 30 | .db $32,$7F,$0E,$C0 31 | 32 | sfx_explosion_NOISE: 33 | .db $1F,$7F,$0F,$C0 34 | 35 | sfx_bigVirusHurt_NOISE: 36 | .db $14,$7F,$02,$C0 37 | 38 | if !removeUnused 39 | __sfx_UNK_D320_noCode: 40 | .db $B0,$7F,$1C,$40 41 | 42 | __sfx_UNK_D324_noCode: 43 | .db $B0,$7F,$32,$40 44 | endif 45 | 46 | sfx_UFO_siren_SQ0: 47 | .db $B0,$7F,$30,$40 48 | 49 | sfx_UFO_siren_SQ1: 50 | .db $B0,$7F,$10,$40 51 | 52 | sfx_pause_SQ0_step1: 53 | .db $9D,$7F,$7A,$28 54 | 55 | sfx_pause_SQ0_step2: 56 | .db $9D,$7F,$40,$28 57 | 58 | sfx_flipPill_SQ0_step1: 59 | .db $96,$7F,$A3,$28 60 | 61 | sfx_flipPill_SQ0_step2: 62 | .db $B2,$7F,$A3,$08 63 | 64 | sfx_speedUp_SQ0: 65 | .db $96,$7F,$D4,$28 66 | 67 | sfx_pillLand_SQ0: 68 | .db $9B,$84,$FF,$0B 69 | 70 | sfx_cursorVertical_SQ0_step1: 71 | .db $D7,$7F,$40,$30 72 | 73 | sfx_cursorVertical_SQ0_step2: 74 | .db $D2,$7F,$40,$30 75 | 76 | if !removeMoreUnused 77 | sfx_alert_SQ0_UNUSED: 78 | .db $00,$7F,$78,$08 79 | 80 | sfx_alertSlow_SQ0_UNUSED: 81 | .db $01,$7F,$B2,$08 82 | endif 83 | 84 | sfx_bigVirus_eradicated_SQ0: 85 | .db $9E,$99,$F0,$08 86 | 87 | sfx_pillCleared_SQ0: 88 | .db $9C,$BB,$80,$09 89 | 90 | sfx_virusEliminated_SQ0: 91 | .db $9F,$87,$FF,$08 92 | 93 | if !removeMoreUnused 94 | sfx_highPitchedGlassLike_SQ0_step1_UNUSED: 95 | .db $96,$7F,$5E,$20 ;Similar sfx in Tetris, but 3rd byte is different 96 | 97 | sfx_highPitchedGlassLike_SQ0_step2_UNUSED: 98 | .db $82,$7F,$53,$F8 ;Similar sfx in Tetris, but 3rd byte is different 99 | endif 100 | 101 | sfx_cursorHorizontal_SQ0: 102 | .db $80,$7F,$62,$18 103 | 104 | sfx_p1_attack_SQ0: 105 | .db $01,$7F,$58,$08 106 | 107 | sfx_p1_attack_SQ1: 108 | .db $01,$7F,$59,$08 109 | 110 | sfx_p2_attack_SQ0: 111 | .db $FA,$7F,$8D,$08 112 | 113 | sfx_p2_attack_SQ1: 114 | .db $F7,$7F,$8E,$08 115 | 116 | sfx_UFO_motor_TRG: 117 | .db $FF,$7F,$20,$0A 118 | 119 | sfx_UFO_beam_TRG: 120 | .db $FF,$7F,$52,$0A 121 | 122 | if !removeMoreUnused 123 | sfx_lowPitchedCursor_TRG_UNUSED: 124 | .db $03,$7F,$3D,$18 ;Also present in Tetris 125 | 126 | sfx_highPitchedGlassLike_SQ0_dutyVolEnv_UNUSED: 127 | .db $14,$93,$94,$D3 ;Also present in Tetris 128 | endif 129 | 130 | ;; 131 | ;; sfx_fail_NOISE_env [$D390] 132 | ;; 133 | ;; Envelope data for the fail noise (explosion), 32-bytes long, for both the period and volume 134 | ;; 135 | sfx_fail_NOISE_periodEnv: 136 | .db $FE,$FE,$FF,$EF,$FE,$EF,$FE,$EF 137 | .db $EF,$FE,$EF,$FE,$EF,$FF,$EE,$EE 138 | .db $FF,$EF,$FF,$FF,$FF,$EF,$EF,$FF 139 | .db $FD,$EF,$EF,$EF,$FE,$EF,$EF,$FF 140 | 141 | sfx_fail_NOISE_volEnv: 142 | .db $F1,$FF,$E1,$EF,$EF,$EF,$DF,$FB 143 | .db $FB,$AE,$AA,$99,$98,$87,$76,$66 144 | .db $55,$44,$44,$44,$44,$43,$33,$33 145 | .db $22,$22,$22,$22,$21,$11,$11,$11 -------------------------------------------------------------------------------- /prg/drmario_prg_audio_general_a.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; jumptable_sfx [$D200] 3 | ;; 4 | ;; Jumptables for various sfx, seperated by channel use (and also if starting to play or already playing) 5 | ;; 6 | jumptable_sfx: 7 | jumpTable_sfx_noise_play: 8 | if !removeMoreUnused 9 | .word playSFX_highToLowPitch_NOISE_UNUSED 10 | endif 11 | .word playSFX_explosion_NOISE 12 | .word playSFX_bigVirus_hurt_NOISE 13 | .word playSFX_UFO_leave_NOISE 14 | .word playSFX_UFO_boom_NOISE 15 | .word playSFX_UFO_beam_NOISE 16 | .word playSFX_UFO_postBeam_NOISE 17 | .word _exit_sfxUses 18 | 19 | jumpTable_sfx_noise_playing: 20 | if !removeMoreUnused 21 | .word sfxPlaying_highToLowPitch_NOISE_UNUSED 22 | endif 23 | .word sfxPlaying_explosion_NOISE 24 | .word sfxPlaying_bigVirus_hurt_NOISE 25 | .word sfxPlaying_UFO_leave_NOISE 26 | .word sfxPlaying_UFO_boom_NOISE 27 | .word sfxPlaying_UFO_beam_NOISE 28 | .word _exit_sfxUses 29 | .word _exit_sfxUses 30 | 31 | jumpTable_sfx_sq0_play: 32 | .word playSFX_cursor_vertical 33 | .word playSFX_pillMatch_noVirus 34 | .word playSFX_cursor_horizontal 35 | .word playSFX_virusEliminated 36 | .word playSFX_flipPill 37 | .word playSFX_speedUp 38 | .word playSFX_pillLand 39 | if !removeMoreUnused 40 | .word playSFX_highPitchedGlassLike_SQ0_UNUSED 41 | endif 42 | .word playSFX_bigVirus_eradicated 43 | if !removeMoreUnused 44 | .word playSFX_alert_SQ0_UNUSED 45 | .word playSFX_slowAlert_SQ0_UNUSED 46 | endif 47 | 48 | jumpTable_sfx_sq0_playing: 49 | .word sfxPlaying_cursor_vertical 50 | .word sfxPlaying_pillMatch_noVirus 51 | .word sfxPlaying_increaseDataPointer 52 | .word sfxPlaying_virusEliminated 53 | .word sfxPlaying_flipPill 54 | .word sfxPlaying_speedUp 55 | .word sfxPlaying_increaseDataPointer 56 | if !removeMoreUnused 57 | .word sfxPlaying_highPitchedGlassLike_SQ0_UNUSED 58 | endif 59 | .word sfxPlaying_bigVirus_eradicated 60 | if !removeMoreUnused 61 | .word sfxPlaying_alert_SQ0_UNUSED 62 | .word sfxPlaying_slowAlert_SQ0_UNUSED 63 | endif 64 | 65 | jumpTable_sfx_trg_play: 66 | .word playSFX_UFO_beam_TRG 67 | if !removeMoreUnused 68 | .word playSFX_lowPitchedCursor_TRG_UNUSED 69 | endif 70 | .word playSFX_UFO_motor_fast_TRG 71 | .word playSFX_UFO_motor_TRG 72 | 73 | jumpTable_sfx_trg_playing: 74 | .word sfxPlaying_UFO_motor_TRG 75 | if !removeMoreUnused 76 | .word sfxPlaying_lowPitchedCursor_TRG_UNUSED 77 | endif 78 | .word sfxPlaying_UFO_motor_TRG 79 | .word sfxPlaying_UFO_motor_TRG 80 | 81 | jumpTable_sfx_sq0_sq1_play: 82 | .word playSFX_p1_attack 83 | .word playSFX_p2_attack 84 | .word playSFX_UFO_siren_squares 85 | 86 | jumpTable_sfx_sq0_sq1_playing: 87 | .word sfxPlaying_p1_attack 88 | .word sfxPlaying_p2_attack 89 | .word sfxPlaying_UFO_siren_squares 90 | 91 | jumpTable_sfx_initApu: 92 | .word initAPU_variablesAndChannels 93 | 94 | jumpTable_sfx_dmc_UNUSED: 95 | if !removeMoreUnused 96 | .word playSFX_UNK_DA50_DMC 97 | .word playSFX_UNK_DA48_DMC 98 | endif 99 | 100 | if !ver_EU 101 | include prg/routines/drmario_prg_audio_copy_sfx_data.asm 102 | include prg/routines/drmario_prg_audio_rnd_noise.asm 103 | include prg/routines/drmario_prg_audio_sfx_counter.asm 104 | include prg/routines/drmario_prg_audio_init_status.asm 105 | else 106 | include prg/routines/drmario_prg_audio_sfx_counter.asm 107 | include prg/routines/drmario_prg_audio_init_status.asm 108 | include prg/routines/drmario_prg_audio_copy_sfx_data.asm 109 | include prg/routines/drmario_prg_audio_rnd_noise.asm 110 | endif 111 | 112 | 113 | -------------------------------------------------------------------------------- /prg/routines/drmario_prg_audio_update.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; audioUpdate [$D3D0] 3 | ;; 4 | ;; Main routine to update all the audio (sfx and music) 5 | ;; 6 | _gameWasJustPaused: 7 | inc flag_pause_forAudio_internal 8 | jsr initAPU_channels ;A is set to zero when we rts from this subroutine 9 | sta sfxPause_soundProgress 10 | rts 11 | _audioOnPause: 12 | lda flag_pause_forAudio_internal 13 | beq _gameWasJustPaused 14 | lda sfxPause_soundProgress 15 | cmp #sfx_pause_length 16 | beq @exit_audioOnPause ;If so, pause sound has finished playing 17 | @continuePlaying_pauseSFX: 18 | and #sfx_pause_change_step ;Changes sfx step at every $03 frames 19 | cmp #sfx_pause_change_step 20 | bne @increasePauseSFXcounter 21 | inc sfxPause_soundProgress_div 22 | ldy # btn_right pressed, for 8 frames 6 | ;; 7 | demo_instructionSet: 8 | if !ver_EU 9 | .db $00,$00,$00,$08,$00,$97,$01,$08 10 | .db $41,$06,$01,$17,$00,$0E,$04,$08 11 | .db $00,$24,$01,$06,$00,$03,$01,$06 12 | .db $00,$09,$04,$0E,$00,$31,$04,$0C 13 | .db $00,$27,$01,$0C,$41,$07,$01,$03 14 | .db $41,$06,$40,$00,$00,$6C,$01,$09 15 | .db $00,$05,$01,$04,$00,$02,$40,$05 16 | .db $00,$04,$40,$05,$00,$11,$04,$03 17 | .db $00,$6E,$02,$05,$00,$1B,$04,$07 18 | .db $00,$3A,$01,$04,$00,$07,$01,$06 19 | .db $00,$09,$40,$04,$00,$09,$01,$09 20 | .db $00,$00,$40,$06,$00,$10,$04,$07 21 | .db $00,$27,$40,$04,$00,$05,$04,$07 22 | .db $00,$69,$40,$05,$00,$05,$40,$06 23 | .db $00,$05,$04,$08,$00,$24,$40,$04 24 | .db $00,$05,$40,$04,$00,$11,$04,$06 25 | .db $00,$7A,$02,$05,$00,$0C,$04,$0C 26 | .db $00,$27,$01,$1C,$41,$05,$01,$04 27 | .db $41,$04,$40,$01,$00,$11,$04,$0C 28 | .db $00,$2D,$01,$05,$41,$00,$40,$06 29 | .db $00,$06,$04,$0E,$00,$24,$01,$11 30 | .db $41,$06,$01,$03,$41,$02,$40,$03 31 | .db $00,$0F,$04,$07,$00,$7B,$01,$04 32 | .db $00,$00,$80,$08,$00,$09,$04,$09 33 | .db $00,$88,$01,$07,$00,$0E,$04,$0B 34 | .db $00,$5F,$01,$15,$40,$04,$00,$10 35 | .db $04,$10,$00,$76,$01,$1A,$00,$04 36 | .db $40,$05,$00,$05,$40,$05,$00,$19 37 | .db $04,$08,$00,$0C,$01,$25,$41,$03 38 | .db $01,$04,$00,$02,$40,$03,$00,$10 39 | .db $04,$0D,$00,$3C,$02,$07,$40,$05 40 | .db $00,$09,$04,$0C,$00,$3E,$02,$04 41 | .db $00,$08,$40,$06,$00,$07,$04,$0B 42 | .db $00,$49,$01,$06,$00,$17,$80,$07 43 | .db $00,$0A,$04,$0C,$00,$6B,$01,$29 44 | .db $00,$04,$04,$0C,$00,$51,$01,$06 45 | .db $00,$05,$01,$05,$00,$0B,$04,$09 46 | .db $00,$67,$01,$04,$00,$18,$80,$06 47 | .db $00,$0C,$04,$0D,$00,$86,$02,$09 48 | .db $42,$07,$02,$03,$42,$06,$02,$0B 49 | .db $00,$04,$04,$08,$00,$2B,$01,$05 50 | .db $00,$00,$40,$05,$00,$0B,$04,$14 51 | .db $00,$6F,$02,$0C,$42,$09,$02,$09 52 | .db $00,$96,$02,$05,$00,$08,$40,$05 53 | .db $00,$05,$40,$05,$00,$14,$04,$0D 54 | .db $00,$7D,$04,$08,$00,$51,$02,$02 55 | .db $42,$03,$40,$04,$00,$0A,$04,$13 56 | .db $00,$53,$01,$02,$00,$01,$40,$06 57 | .db $00,$02,$02,$05,$00,$0D,$04,$0E 58 | .db $00,$95,$04,$0A,$00,$34,$02,$11 59 | .db $00,$22,$02,$1B,$42,$06,$02,$0C 60 | .db $00,$08,$04,$0D,$00,$3D,$02,$05 61 | .db $00,$0C,$04,$09,$00,$22,$02,$07 62 | .db $00,$47,$04,$09,$00,$45,$02,$05 63 | .db $00,$0C,$02,$07,$00,$68,$02,$08 64 | .db $00,$04,$02,$05,$00,$03,$40,$04 65 | .db $00,$06,$04,$09,$00,$40,$40,$05 66 | .db $00,$05,$04,$20,$00,$3E,$01,$05 67 | .db $00,$0D,$40,$07,$00,$16,$04,$17 68 | .db $00,$39,$02,$1F,$00,$12,$04,$0F 69 | .db $00,$32,$01,$02,$00,$13,$40,$03 70 | .db $00,$06,$40,$04,$00,$08,$04,$10 71 | .db $00,$15,$01,$1F,$00,$06,$01,$07 72 | .db $00,$14,$04,$13,$00,$24,$01,$09 73 | else 74 | .db $00,$00,$20,$03,$00,$89,$40,$07 75 | .db $00,$0A,$04,$15,$00,$25,$02,$07 76 | .db $00,$03,$40,$01,$42,$03,$40,$00 77 | .db $00,$02,$40,$06,$00,$1C,$04,$09 78 | .db $00,$47,$04,$06,$00,$1E,$01,$08 79 | .db $41,$06,$01,$05,$41,$06,$01,$04 80 | .db $00,$68,$02,$05,$00,$04,$02,$07 81 | .db $00,$09,$04,$0E,$00,$20,$01,$07 82 | .db $00,$04,$01,$06,$00,$02,$01,$07 83 | .db $00,$17,$04,$06,$00,$29,$02,$03 84 | .db $00,$05,$02,$04,$00,$0C,$04,$07 85 | .db $00,$5C,$40,$07,$00,$0E,$04,$09 86 | .db $00,$23,$01,$05,$00,$25,$04,$07 87 | .db $00,$23,$01,$03,$00,$1A,$40,$07 88 | .db $00,$03,$04,$06,$00,$92,$01,$20 89 | .db $00,$5C,$02,$06,$00,$03,$02,$05 90 | .db $00,$07,$40,$05,$00,$06,$40,$05 91 | .db $00,$0F,$04,$11,$00,$10,$01,$2D 92 | .db $00,$1D,$04,$05,$00,$5E,$02,$05 93 | .db $00,$04,$02,$05,$00,$02,$40,$06 94 | .db $00,$04,$40,$06,$00,$93,$40,$08 95 | .db $00,$20,$80,$06,$00,$07,$04,$0D 96 | .db $00,$23,$42,$06,$00,$05,$40,$04 97 | .db $42,$00,$02,$05,$00,$0E,$04,$0B 98 | .db $00,$65,$40,$07,$00,$01,$02,$03 99 | .db $00,$1E,$04,$0F,$00,$41,$01,$06 100 | .db $00,$10,$40,$06,$00,$04,$40,$08 101 | .db $00,$07,$04,$07,$00,$2E,$01,$03 102 | .db $00,$11,$40,$06,$00,$06,$40,$07 103 | .db $00,$06,$04,$06,$00,$7A,$01,$06 104 | .db $40,$06,$00,$16,$04,$10,$00,$2A 105 | .db $01,$05,$00,$06,$01,$05,$00,$08 106 | .db $40,$06,$00,$0B,$01,$07,$00,$07 107 | .db $04,$08,$00,$29,$01,$06,$00,$01 108 | .db $40,$06,$00,$04,$40,$06,$00,$0C 109 | .db $04,$07,$00,$6A,$02,$12,$42,$06 110 | .db $02,$03,$42,$02,$40,$04,$00,$11 111 | .db $04,$09,$00,$1F,$02,$06,$00,$03 112 | .db $40,$06,$00,$06,$04,$08,$00,$09 113 | .db $02,$0D,$00,$12,$04,$06,$00,$17 114 | .db $02,$13,$42,$06,$02,$03,$42,$07 115 | .db $02,$01,$00,$BD,$01,$19,$00,$11 116 | .db $80,$07,$00,$0E,$04,$06,$00,$93 117 | .db $01,$07,$00,$07,$01,$04,$40,$05 118 | .db $00,$9D,$01,$08,$00,$1C,$04,$0A 119 | .db $00,$BD,$40,$04,$00,$05,$40,$06 120 | .db $00,$79,$02,$05,$00,$0F,$80,$05 121 | .db $00,$15,$04,$0B,$00,$92,$01,$05 122 | .db $00,$06,$01,$03,$00,$02,$40,$06 123 | .db $00,$2D,$04,$0B,$00,$28,$02,$05 124 | .db $40,$05,$00,$C8,$02,$1B,$42,$06 125 | .db $02,$04,$42,$01,$40,$03,$00,$24 126 | .db $04,$09,$00,$13,$02,$18,$42,$06 127 | .db $02,$09,$00,$09,$04,$0C,$00,$74 128 | .db $02,$06,$00,$04,$02,$04,$00,$0C 129 | .db $04,$13,$00,$25,$02,$05,$00,$06 130 | .db $02,$05,$00,$16,$04,$10,$00,$2E 131 | .db $01,$04,$00,$05,$01,$05,$00,$11 132 | .db $04,$08,$00,$82,$01,$04,$00,$07 133 | .db $40,$05,$00,$19,$04,$09,$00,$3B 134 | .db $80,$0B,$00,$4A,$40,$06,$00,$17 135 | .db $01,$05,$00,$68,$04,$0B,$00,$2B 136 | .db $02,$05,$00,$04,$02,$06,$00,$04 137 | .db $40,$07,$00,$07,$04,$0C,$00,$8E 138 | endif -------------------------------------------------------------------------------- /unused/drmario_unused_data_fafd.asm: -------------------------------------------------------------------------------- 1 | if !removeUnused 2 | UNUSED_DATA_7AFD: 3 | if ver_revA 4 | .db $00,$00,$00 5 | .db $FF,$FF,$FF,$FD,$FF,$FF,$FF,$FF 6 | .db $FF,$FF,$FF,$FF,$FF,$FF,$CF,$F4 7 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 8 | .db $FF,$FF,$FF,$FF,$CF,$7A,$FF,$FF 9 | .db $00,$00,$00,$00,$00,$00,$00,$00 10 | .db $00,$00,$00,$00,$00,$00,$00,$00 11 | .db $00,$00,$00,$00,$00,$00,$00,$00 12 | .db $00,$00,$00,$00,$00,$00,$00,$00 13 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 14 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 15 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 16 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 17 | .db $00,$00,$00,$00,$00,$00,$00,$00 18 | .db $00,$00,$00,$00,$00,$00,$00,$00 19 | .db $00,$00,$00,$00,$00,$00,$00,$00 20 | .db $00,$00,$00,$00,$00,$00,$00,$00 21 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 22 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 23 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 24 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 25 | .db $00,$00,$00,$00,$00,$00,$00,$00 26 | .db $00,$00,$00,$00,$00,$00,$00,$00 27 | .db $00,$00,$00,$00,$00,$00,$00,$00 28 | .db $00,$00,$00,$00,$00,$00,$00,$00 29 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 30 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 31 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 32 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 33 | .db $00,$00,$00,$00,$00,$00,$00,$00 34 | .db $00,$00,$00,$00,$00,$00,$00,$00 35 | .db $00,$00,$00,$00,$00,$00,$00,$00 36 | .db $00,$00,$00,$00,$00,$00,$00,$00 37 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 38 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 39 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 40 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 41 | .db $00,$00,$00,$00,$00,$00,$00,$00 42 | .db $00,$00,$00,$00,$00,$00,$00,$00 43 | .db $00,$00,$00,$00,$00,$00,$00,$00 44 | .db $00,$00,$00,$00,$00,$00,$00,$00 45 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 46 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 47 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 48 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 49 | .db $00,$00,$00,$00,$00,$00,$00,$00 50 | .db $00,$00,$00,$00,$00,$00,$00,$00 51 | .db $00,$00,$00,$00,$00,$00,$00,$00 52 | .db $00,$00,$00,$00,$00,$00,$00,$00 53 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 54 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 55 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 56 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 57 | .db $00,$00,$00,$00,$00,$00,$00,$00 58 | .db $00,$00,$00,$00,$00,$00,$00,$00 59 | .db $00,$00,$00,$00,$00,$00,$00,$00 60 | .db $00,$00,$00,$00,$00,$00,$00,$00 61 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 62 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 63 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 64 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 65 | .db $00,$00,$00,$00,$00,$00,$00,$00 66 | .db $00,$00,$00,$00,$00,$00,$00,$00 67 | .db $00,$00,$00,$00,$00,$00,$00,$00 68 | .db $00,$00,$00,$00,$00,$00,$00,$00 69 | elseif ver_EU 70 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 71 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 72 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 73 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 74 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 75 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 76 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 77 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 78 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 79 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF 80 | else 81 | .db $00,$00,$00 82 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 83 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 84 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 85 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 86 | .db $00,$00,$00,$00,$00,$00,$00,$00 87 | .db $00,$00,$00,$00,$00,$00,$00,$00 88 | .db $00,$00,$00,$00,$00,$00,$00,$00 89 | .db $00,$00,$00,$00,$00,$00,$00,$00 90 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 91 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 92 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 93 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 94 | .db $00,$00,$00,$00,$00,$00,$00,$00 95 | .db $00,$00,$00,$00,$00,$00,$00,$00 96 | .db $00,$00,$00,$00,$00,$00,$00,$00 97 | .db $00,$00,$00,$00,$00,$00,$00,$00 98 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 99 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 100 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 101 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 102 | .db $00,$00,$00,$00,$00,$00,$00,$00 103 | .db $00,$00,$00,$00,$00,$00,$00,$00 104 | .db $00,$00,$00,$00,$00,$00,$00,$00 105 | .db $00,$00,$00,$00,$00,$00,$00,$00 106 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 107 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 108 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 109 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 110 | .db $00,$00,$00,$00,$00,$00,$00,$00 111 | .db $00,$00,$00,$00,$00,$00,$00,$00 112 | .db $00,$00,$00,$00,$00,$00,$00,$00 113 | .db $00,$00,$00,$00,$00,$00,$81,$08 114 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 115 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 116 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 117 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 118 | .db $00,$00,$00,$00,$00,$00,$00,$00 119 | .db $00,$00,$00,$00,$00,$00,$00,$00 120 | .db $00,$00,$00,$00,$00,$00,$00,$00 121 | .db $00,$00,$00,$00,$00,$00,$00,$00 122 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 123 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 124 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 125 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 126 | .db $00,$00,$00,$00,$00,$00,$00,$00 127 | .db $00,$00,$00,$00,$00,$00,$00,$00 128 | .db $00,$00,$00,$00,$00,$00,$00,$00 129 | .db $00,$00,$00,$00,$00,$00,$00,$00 130 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 131 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 132 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 133 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 134 | .db $00,$00,$00,$00,$00,$00,$00,$00 135 | .db $00,$00,$00,$00,$00,$00,$00,$00 136 | .db $00,$00,$00,$00,$00,$00,$00,$00 137 | .db $00,$00,$00,$00,$00,$00,$00,$00 138 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 139 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 140 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 141 | .db $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF 142 | .db $00,$00,$00,$00,$00,$00,$00,$00 143 | .db $00,$00,$00,$00,$00,$00,$00,$00 144 | .db $00,$00,$00,$00,$00,$00,$00,$00 145 | .db $00,$00,$00,$00,$00,$00,$00,$00 146 | endif 147 | endif -------------------------------------------------------------------------------- /prg/drmario_prg_audio_general_b.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; audioUpdate_initVariablesChannels & sfxUpdate [$D3D0] 3 | ;; 4 | ;; Each "entry point" to this routine updates a different sfx channel (or inits the APU) 5 | ;; 6 | ;; Local variables: 7 | jumptable_sfx_chan = tmpE0_audio 8 | jumptable_sfx_chan_lo = tmpE0_audio 9 | jumptable_sfx_chan_hi = tmpE1_audio 10 | sfxRoutine = tmpE2_audio 11 | sfxRoutine_lo = tmpE2_audio 12 | sfxRoutine_hi = tmpE3_audio 13 | sfxToPlay_offset = tmpE2_audio 14 | 15 | audioUpdate_initVariablesChannels: 16 | ldx #sfx_channel_init ;Would normally be for SQ1, but seems to init APU variables and channels instead (INCLUDING music) 17 | lda #jumpTable_sfx_initApu-jumptable_sfx 18 | ldy #jumpTable_sfx_initApu-jumptable_sfx 19 | bne _SFX_Mixer 20 | sfxUpdate_TRG: 21 | ldx #sfx_channel_trg ;x is "sfx channel" number 22 | lda #jumpTable_sfx_trg_play-jumptable_sfx ;This holds the offset for the index table when "sfx to play" 23 | ldy #jumpTable_sfx_trg_playing-jumptable_sfx ;This holds the offset for the index table when "sfx is playing" 24 | bne _SFX_Mixer 25 | sfxUpdate_SQ0_SQ1: 26 | ldx #sfx_channel_sq0_sq1 ;Always both squares 27 | lda #jumpTable_sfx_sq0_sq1_play-jumptable_sfx 28 | ldy #jumpTable_sfx_sq0_sq1_playing-jumptable_sfx 29 | bne _SFX_Mixer 30 | sfxUpdate_SQ0: 31 | lda sfx_playing_sq0_sq1 32 | bne _exit_channelAudioUpdate 33 | ldx #sfx_channel_sq0 34 | lda #jumpTable_sfx_sq0_play-jumptable_sfx 35 | ldy #jumpTable_sfx_sq0_playing-jumptable_sfx 36 | bne _SFX_Mixer 37 | sfxUpdate_NOISE: 38 | ldx #sfx_channel_noise 39 | lda #jumpTable_sfx_noise_play-jumptable_sfx 40 | ldy #jumpTable_sfx_noise_playing-jumptable_sfx 41 | _SFX_Mixer: 42 | sta jumptable_sfx_chan_lo ;In this case this var is the offset (or basis) for the sfx jump table 43 | stx sfx_channel 44 | lda sfx_toPlay_noise,X 45 | beq _noSFXToPlay_checkSFXPlaying 46 | _sfxToPlay_orIsPlaying: 47 | sta audioToPlay_copy 48 | sta sfxToPlay_offset 49 | ldy #>jumptable_sfx ;High byte of sfx jump table address 50 | sty jumptable_sfx_chan_hi 51 | if !optimize 52 | and #%00000111 ;We then start from the index nb of the sfx to play, and transform into an offset for the sfx jump table 53 | tay ;It looks to me like a very complicated way of simply doing minus 1 (-1), then multiply by 2 (x2) 54 | lda sfx_jumpTable_offset,Y 55 | tay 56 | dec sfxToPlay_offset 57 | lda sfxToPlay_offset 58 | and #%11111000 59 | sta sfxToPlay_offset 60 | asl sfxToPlay_offset 61 | tya 62 | ora sfxToPlay_offset 63 | tay 64 | else 65 | dec sfxToPlay_offset 66 | asl sfxToPlay_offset 67 | ldy sfxToPlay_offset 68 | endif 69 | lda (jumptable_sfx_chan),Y ;We now have the offset for the current channel's sfx jump table address in Y 70 | sta sfxRoutine_lo 71 | iny 72 | lda (jumptable_sfx_chan),Y 73 | sta sfxRoutine_hi 74 | jmp (sfxRoutine) ;Here we jump to the routine that plays/updates the SFX 75 | if !optimize 76 | sfx_jumpTable_offset: 77 | .db $0E,$00,$02,$04,$06,$08,$0A,$0C ;Part of the rather complicated code to reduce by 1 and mult by 2 78 | endif 79 | _noSFXToPlay_checkSFXPlaying: 80 | lda sfx_playing_noise,X 81 | beq _exit_channelAudioUpdate 82 | sty jumptable_sfx_chan_lo ;Y holds the address of the sfx_playing data 83 | bne _sfxToPlay_orIsPlaying 84 | _exit_channelAudioUpdate: 85 | rts 86 | 87 | if !ver_EU 88 | include prg/routines/drmario_prg_audio_update.asm 89 | endif 90 | 91 | ;; 92 | ;; initAPU_variablesAndChannels [$D4E5] 93 | ;; 94 | ;; Inits several APU variables & channels (muting everything including music) 95 | ;; 96 | initAPU_variablesAndChannels: 97 | jsr initAPU_variables 98 | initAPU_channelsAndDMC: 99 | jsr initAPU_channels 100 | lda #$00 101 | if !optimize 102 | sta DMC_COUNTER ;This was litteraly done just a couple cycles ago in initAPU_channels 103 | endif 104 | sta music_trg_envelope_index 105 | rts 106 | 107 | ;; 108 | ;; initAPU_variables [$D4F4] 109 | ;; 110 | ;; Inits plenty of variables related to sfx + music playing 111 | ;; 112 | initAPU_variables: 113 | lda #$00 ;Also when playing ending music 114 | sta sfxPlaying_forMusic_sq0 115 | sta sfxPlaying_forMusic_sq1 116 | sta sfxPlaying_forMusic_trg 117 | if !optimize 118 | sta flag_audio_68C_UNK ;Is never used 119 | endif 120 | sta sfx_sq0_data ;Not sure why specifically we init this variable 121 | sta flag_sfxJustFinished 122 | tay 123 | if !optimize 124 | @resetAudioPlaying: 125 | lda #$00 ;Could be optimized 126 | sta sfx_playing_noise,Y 127 | iny 128 | tya 129 | cmp #mixer_channels ;We have 6 "mixer channels" (5 for sfx and the last one for music) 130 | else 131 | lda #$00 132 | @resetAudioPlaying: 133 | sta sfx_playing_noise,Y 134 | iny 135 | cpy #mixer_channels 136 | endif 137 | bne @resetAudioPlaying 138 | rts 139 | 140 | ;; 141 | ;; initAPU_channels [$D515] 142 | ;; 143 | ;; Mutes all channels 144 | ;; 145 | initAPU_channels: 146 | if !optimize 147 | lda #$00 148 | sta DMC_COUNTER ;This can be done later with the TRG_LINEAR 149 | endif 150 | lda #SQ_DUTY_NOISE_mute 151 | sta SQ0_DUTY 152 | sta SQ1_DUTY 153 | sta NOISE_VOLUME 154 | lda #$00 155 | sta TRG_LINEAR 156 | if optimize 157 | sta DMC_COUNTER 158 | endif 159 | rts -------------------------------------------------------------------------------- /defines/drmario_ram_zp.asm: -------------------------------------------------------------------------------- 1 | ;Zero page RAM ($00 to $FF) 2 | tmp0 = $00 3 | ptr = $00 ;Shared 4 | ptr_lo = $00 ;Shared 5 | rng0_offset = $00 ;Shared 6 | CTRL_exp1 = $00 ;Shared 7 | 8 | tmp1 = $01 9 | ptr_hi = $01 ;Shared 10 | rng1_offset = $01 ;Shared 11 | CTRL_exp2 = $01 ;Shared 12 | 13 | PPUDATA_init_attr = $02 14 | 15 | ptrTemp_low = $05 16 | ptrTemp_high = $06 17 | 18 | rng0 = $17 19 | rng1 = $18 20 | 21 | nmiFlag = $33 ;Set to 1 during NMI 22 | flag_initDone = $34 23 | 24 | ptr_demoInstruction_low = $36 25 | ptr_demoInstruction_high = $37 26 | bigVirusYellow_XPos = $38 27 | virusGroup_XPos = $38 ;Shared 28 | bigVirusYellow_YPos = $39 29 | virusGroup_YPos = $39 ;Shared 30 | bigVirusRed_XPos = $3A 31 | flyingObjectNb = $3A ;Shared 32 | bigVirusRed_YPos = $3B 33 | flyingObject_XPos = $3B ;Shared 34 | bigVirusBlue_XPos = $3C 35 | flyingObject_YPos = $3C ;Shared 36 | bigVirusBlue_YPos = $3D 37 | flyingObjectStatus = $3D ;Shared 38 | bigVirus_circularPos = $3E 39 | flyingObject_IndexOffset = $3E ;Shared 40 | 41 | spritePointer = $42 42 | frameCounter = $43 43 | spriteXPos = $44 44 | spriteYPos = $45 45 | mode = $46 ;See constants below for valid values 46 | tmp47 = $47 47 | tmp48 = $48 48 | tmp49 = $49 49 | tmp4A = $4A 50 | tmp4B = $4B 51 | tmp4C = $4C 52 | 53 | spriteXPos_UFO = $4F 54 | spriteYPos_UFO = $50 55 | waitFrames = $51 56 | visualUpdateFlags = $52 57 | spriteIndex = $53 58 | metaspriteIndex = $53 ;Shared 59 | enablePause = $54 60 | whoWon = $55 61 | 62 | currentP_fieldPointer = $57 63 | currentP = $58 ;4 for player, 5 for player 2 64 | fieldPos = $59 65 | fieldPos_tmp = $5A 66 | currentP_btnsPressed = $5B 67 | currentP_btnsHeld = $5C 68 | flag_inLevel_NMI = $5D 69 | finalCutsceneStep = $5E 70 | spriteAttribute = $60 71 | whoFailed = $61 ;Is used to set the position of the red lose virus (Draw sets this value to zero, otherwise p1=1 & p2=2) 72 | 73 | optionSectionHighlight = $65 ;0 = Level, 1 = Speed, 2 = Music 74 | palToChangeTo = $66 75 | 76 | visualUpdateFlags_options = $68 77 | counterDemoInstruction = $69 78 | demo_inputs = $70 79 | 80 | bigVirusYellow_health = $72 81 | bigVirusRed_health = $73 82 | bigVirusBlue_health = $74 83 | bigVirusYellow_state = $75 84 | bigVirusRed_state = $76 85 | bigVirusBlue_state = $77 86 | bigVirusYellow_frame = $78 87 | bigVirusRed_frame = $79 88 | bigVirusBlue_frame = $7A 89 | danceFrame = $7B ;Shared by all 3 viruses and Mario on title 90 | 91 | pillThrownFrame = $7D 92 | marioThrowFrame = $7E 93 | cutsceneFrame = $7F 94 | 95 | ;Current player zero-page RAM ($80 to $AF) 96 | currentP_RAM = $80 ;$30 bytes long 97 | currentP_status = $80 ;Is most of the time analoguous to the row at which the game is currently treating the player's field. 98 | currentP_fallingPill1stColor = $81 99 | currentP_fallingPill2ndColor = $82 100 | currentP_fallingPill3rdColor = $83 101 | currentP_fallingPill4thColor = $84 102 | currentP_fallingPillX = $85 103 | currentP_fallingPillY = $86 104 | currentP_pillPlacedStep = $87 105 | currentP_UNK_88 = $88 106 | currentP_levelFailFlag = $89 107 | currentP_speedUps = $8A 108 | currentP_speedSetting = $8B 109 | currentP_UNUSED_8C = $8C 110 | currentP_UNUSED_8D = $8D 111 | currentP_chainLength_0based = $8E 112 | currentP_comboCounter = $8F 113 | currentP_pillsCounter_decimal = $90 114 | currentP_pillsCounter_hundreds = $91 115 | currentP_speedCounter = $92 116 | currentP_horVelocity = $93 117 | currentP_matchFlag = $94 118 | currentP_chainStartRow = $95 119 | currentP_level = $96 120 | currentP_nextAction = $97 121 | currentP_attackSize = $98 122 | currentP_UNUSED_99 = $99 123 | currentP_nextPill1stColor = $9A 124 | currentP_nextPill2ndColor = $9B 125 | currentP_nextPill3rdColor = $9C 126 | currentP_nextPill4thColor = $9D 127 | currentP_victories = $9E 128 | currentP_UNUSED_9F = $9F 129 | currentP_speedIndex = $A0 130 | currentP_chainLength = $A1 131 | currentP_nextPillRotation = $A2 132 | currentP_nextPillSize = $A3 133 | currentP_virusLeft = $A4 134 | currentP_fallingPillRotation = $A5 135 | currentP_fallingPillSize = $A6 136 | currentP_pillsCounter = $A7 137 | currentP_virusToAdd = $A8 138 | currentP_attackColors = $A9 ;4 bytes long 139 | currentP_scoreMultiplier = $AD 140 | currentP_UNUSED_AE = $AE 141 | currentP_UNUSED_AF = $AF 142 | 143 | ;Audio zero-page RAM ($E0 to $EF) 144 | tmpE0_audio = $E0 145 | pitchEnvelope_index = $E0 ;Shared 146 | tmpE1_audio = $E1 147 | currentPitch = $E1 ;Shared 148 | tmpE2_audio = $E2 149 | tmpE3_audio = $E3 150 | audioData_envelope = $E4 ;Either volume or pitch 151 | 152 | music_currentChan_startDataPointer = $E6 153 | music_currentChan_startDataPointer_lowByte = $E6 154 | music_currentChan_startDataPointer_highByte = $E7 155 | 156 | tmpEA_audio = $EA ;Seems unused (part of music routine, but only ever stored, never read) 157 | sfx_rndNoisePeriod = $EB 158 | sfx_rndNoisePeriod_tmp1 = $EC 159 | sfx_channel = $ED ;0 = Noise, 1 = Square 0, 2 = Square 1, 3 = Triangle, 4 = DMC 160 | music_channel = $EE ;0 = Square 0, 4 = Square 1, 8 = Triangle, 12 = Noise 161 | audioToPlay_copy = $EF 162 | 163 | ;Controllers zero-page RAM 164 | p1_btns_held_UNUSED = $F1 165 | p2_btns_held_UNUSED = $F2 166 | exp1_btns_held_UNUSED = $F3 167 | exp2_btns_held_UNUSED = $F4 168 | p1_btns_pressed = $F5 169 | p2_btns_pressed = $F6 170 | p1_btns_held = $F7 171 | exp1_btns_pressed_UNUSED = $F7 ;Shared 172 | p2_btns_held = $F8 173 | exp2_btns_pressed_UNUSED = $F8 ;Shared 174 | 175 | ctrlPort = $FB ;Should be left to zero, having a value of 2 of 4 would mean using the expansion ports 176 | 177 | ;PPU zero-page RAM 178 | PPUSCROLL_y = $FC 179 | PPUSCROLL_x = $FD 180 | PPUMASK_RAM = $FE 181 | PPUCTRL_RAM = $FF -------------------------------------------------------------------------------- /prg/drmario_prg_game_init.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; toInit [$8000] 3 | ;; 4 | ;; This routine is called by the reset handler, and simply jumps to the game init routine 5 | ;; 6 | toInit: 7 | if !removeMoreUnused 8 | ldx #$00 ;The value stored in x is never used. Seems to be used by unused MMC1 config routine (ldx has its bit 7 set, so writing this value would clear the shift register) 9 | endif 10 | jmp init 11 | 12 | 13 | ;; 14 | ;; nmi [$8005] 15 | ;; 16 | ;; The nmi handler, renders graphics, increases the frame counter and gets players (or demo) inputs 17 | ;; 18 | nmi: 19 | pha ;Store the values from registers A,X,Y in the stack 20 | txa 21 | pha 22 | tya 23 | pha 24 | if ver_revA 25 | lda PPUMASK_RAM ;Write to PPUMASK register according to value set in RAM (can change, for instance, when pausing) 26 | sta PPUMASK 27 | endif 28 | jsr render_sprites ;Render all things that can change/move while staying in the same screen 29 | jsr render_palChange_or_bkgOverlays 30 | jsr render_fieldRow_bothPlayers 31 | jsr render_cutscene_txt 32 | lda frameCounter ;Increase the frame counter 33 | clc 34 | adc #$01 35 | sta frameCounter 36 | lda #$00 ;There is no background scrolling, so x and y both stay at 0 37 | sta PPUSCROLL 38 | sta PPUSCROLL 39 | lda #$01 ;Sets the nmiFlag to indicate a nmi just occured 40 | sta nmiFlag 41 | jsr getInputs_checkMode ;Gets either players inputs, or demo inputs, depending on the current mode 42 | lda #$00 ;Now that we have rendered the sprites, reset the sprite pointer 43 | sta spritePointer 44 | pla ;Restore the values of registers Y,X,A 45 | tay 46 | pla 47 | tax 48 | pla ;Continues into the next routine for the rti 49 | 50 | 51 | ;; 52 | ;; irq [$803A] 53 | ;; 54 | ;; IRQs are not used in this game, hence the single rti 55 | ;; 56 | irq: 57 | rti 58 | 59 | 60 | ;; 61 | ;; init [$803B] 62 | ;; 63 | ;; Reset and cold boot memory init + PPU and APU registers init, then main loop 64 | ;; 65 | ;; Local variables: 66 | currentLetter = tmp47 67 | init: 68 | lda p1_level ;Saves option menu settings 69 | sta p1_level_tmp 70 | lda p2_level 71 | sta p2_level_tmp 72 | lda p1_speedSetting 73 | sta p1_speedSetting_tmp 74 | lda p2_speedSetting 75 | sta p2_speedSetting_tmp 76 | ldy #>(persistentRAM-1) ;Wipe all non-persistent RAM (from $00 to $06FF) 77 | sty ptr_hi 78 | ldy #$00 79 | sty ptr_lo 80 | lda #$00 ;Sets everything to value $00 81 | @wipeMemory: 82 | sta (ptr),Y 83 | dey 84 | bne @wipeMemory ;Inner loop (256x) 85 | dec tmp1 86 | bpl @wipeMemory ;Outer loop (7x) 87 | @restoreOptionSettings: 88 | lda p1_level_tmp ;Restore option menu settings 89 | sta p1_level 90 | lda p2_level_tmp 91 | sta p2_level 92 | lda p1_speedSetting_tmp 93 | sta p1_speedSetting 94 | lda p2_speedSetting_tmp 95 | sta p2_speedSetting 96 | ldx #gameName_length ;Checks if the game name is loaded in RAM 97 | @gameNameLoadedValidation: 98 | lda gameName_RAM-1,X 99 | sta currentLetter 100 | lda gameName_ROM-1,X 101 | cmp currentLetter 102 | bne @coldBootInit ;This is used to see if this is a cold boot or reset (name already loaded = reset) 103 | dex 104 | bne @gameNameLoadedValidation 105 | jmp @storeRngSeeds 106 | @coldBootInit: 107 | if !removeMoreUnused 108 | ldx #$00 ;Not sure what this is used for 109 | endif 110 | if ver_revA | ver_EU 111 | lda #$FF ;Wipe all memory from $700 to $07FF, using value $FF 112 | ldx #>persistentRAM 113 | ldy #>persistentRAM_end 114 | jsr copy_valueA_fromX00_toY00_plusFF 115 | endif 116 | lda #$00 ;Sets highscore to 1000 (display actually adds a 0 at the end to give the impression that it is 10x higher) 117 | sta highScore+0 118 | sta highScore+1 119 | sta highScore+2 120 | sta highScore+4 121 | sta highScore+5 122 | lda #$01 123 | sta highScore+3 124 | lda #$00 ;Sets game config 125 | if !optimize 126 | sta config_bypassSpeedUp 127 | sta config_pillsStayMidAir 128 | endif 129 | sta lvlsOver20 130 | sta p1_level 131 | sta p2_level 132 | sta musicType 133 | lda #$01 134 | if !removeMoreUnused 135 | sta config_unknown720 136 | endif 137 | sta nbPlayers 138 | sta p1_speedSetting 139 | sta p2_speedSetting 140 | if !removeMoreUnused 141 | lda #$02 142 | sta config_unknown722 143 | endif 144 | lda #$03 145 | sta config_victoriesRequired 146 | ldx #gameName_length ;Copies game name from ROM to RAM 147 | @copyGameName: 148 | lda gameName_ROM-1,X 149 | sta gameName_RAM-1,X 150 | dex 151 | bne @copyGameName 152 | @storeRngSeeds: 153 | ldx #rngSeed ;Store rng seed 154 | stx rng0 155 | dex 156 | stx rng1 157 | @initPPUandAPU: 158 | ldy #$00 ;Scroll position at 0,0 159 | sty PPUSCROLL_x 160 | sty PPUSCROLL 161 | ldy #$00 162 | sty PPUSCROLL_y 163 | sty PPUSCROLL 164 | lda #ppuctrl_nmi_on + ppuctrl_bkg_tbl_1 165 | sta PPUCTRL_RAM ;Generate an NMI at the start of the vertical blanking interval + background pattern table address = $1000 166 | sta PPUCTRL 167 | lda #ppumask_bkg_col1_enable + ppumask_spr_col1_enable 168 | sta PPUMASK ;Show background + sprites in leftmost 8 pixels of screen, disable rendering 169 | if ver_revA 170 | sta PPUMASK_RAM 171 | endif 172 | if !optimize 173 | jsr toInitAPU_status ;Init APU status and variables for each channels 174 | jsr toInitAPU_var_chan 175 | else 176 | jsr initAPU_status 177 | jsr initAPU_variablesAndChannels 178 | endif 179 | lda #$C0 ;*NOTE: Don't know what both these addresses are for, maybe some fallback in case of overflow? 180 | sta stack+0 181 | lda #$80 182 | sta stack+1 183 | lda #$35 184 | sta stack+3 185 | lda #$AC 186 | sta stack+4 187 | jsr audioUpdate_NMI_disableRendering 188 | jsr NMI_off 189 | lda #>nametable0 ;Clears all 4 nametables 190 | jsr initPPU_addrA_hiByte 191 | lda #>nametable1 ;*NOTE: It seems these are mirrored, so maybe we don't need to init all 4 nametables? 192 | jsr initPPU_addrA_hiByte 193 | lda #>nametable2 194 | jsr initPPU_addrA_hiByte 195 | lda #>nametable3 196 | jsr initPPU_addrA_hiByte 197 | lda #$00 198 | sta flag_inLevel_NMI ;We are not in a level at this moment (heading to title screen) 199 | jsr initField_bothPlayers ;Clear both players field RAM 200 | jsr finishVblank_NMI_on 201 | jsr visualAudioUpdate_NMI ;Couple of updates (visual + audio), making sure everything is properly initiated 202 | jsr audioUpdate_NMI_enableRendering 203 | jsr visualAudioUpdate_NMI 204 | if !removeMoreUnused 205 | lda #$0E 206 | sta flag_initDone ;Sets a flag that is, as far as I know, never used 207 | endif 208 | @mainLoop: 209 | jsr toModeAddress ;Loops in this when exiting any mode 210 | jsr checkPauseReset 211 | jsr visualAudioUpdate_NMI 212 | if !removeMoreUnused 213 | jsr initUnusedSpriteRAM ;This seems useless because sprite RAM is always reset in previous subroutine 214 | endif 215 | jmp @mainLoop 216 | 217 | 218 | ;; 219 | ;; toModeAddress [$8167] 220 | ;; 221 | ;; This routine changes the game's "mode", or current screen if you will 222 | ;; 223 | toModeAddress: 224 | lda mode 225 | jsr toAddressAtStackPointer_indexA 226 | _jumpTable_mode: 227 | .word toTitle 228 | .word toDemo_orOptions 229 | .word toLevel 230 | .word initData_level 231 | .word mainLoop_level 232 | .word anyPlayerLoses 233 | if !removeMoreUnused 234 | .word toMode4 235 | endif 236 | .word playerLoses_endScreen 237 | .word levelIntro -------------------------------------------------------------------------------- /prg/drmario_prg_level_init.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; toLevel [$817E] 3 | ;; 4 | ;; Changes the background, init score/victories and move on to next mode 5 | ;; 6 | toLevel: 7 | jsr audioUpdate_NMI_disableRendering ;Disable rendering while changing graphics 8 | jsr NMI_off 9 | lda #CHR_levelSprites ;Load level sprites 10 | jsr changeCHRBank0 11 | if !optimize 12 | lda #CHR_titleTiles_frame0 ;Why is the title CHR loaded for a brief moment? 13 | jsr changeCHRBank1 14 | endif 15 | lda nbPlayers ;Depending on nb of players, prep approriate visual 16 | cmp #$01 17 | bne @prepLevelVisual_2P 18 | jsr prepLevelVisual_1P 19 | jmp @initLevelRAM 20 | @prepLevelVisual_2P: 21 | jsr copyBkgOrPalToPPU ;Copy 2-player level bkg to PPU nametable 22 | @bkgLevel_2P_ptr: 23 | .word bkgLevel_2P 24 | @displaySpeedText_2P: 25 | setPPUADDR_absolute speedText_P1_VRAMaddr ;Display speed text for player 1 (as bkg, not sprite) 26 | ;lda #>speedText_P1_VRAMaddr 27 | ;sta PPUADDR 28 | ;lda #speedText_P2_VRAMaddr 42 | ;sta PPUADDR 43 | ;lda #speedText_1P_VRAMaddr 222 | ;sta PPUADDR 223 | ;lda #sprites 24 | ldy #>sprites 25 | jsr copy_valueA_fromX00_toY00_plusFF 26 | rts 27 | 28 | ;; 29 | ;; audioUpdate_then_NMI [$B68A] 30 | ;; 31 | ;; Performs audio update then waits for a NMI. Only called when changing screen (except pause). 32 | ;; 33 | audioUpdate_then_NMI: 34 | if !optimize 35 | jsr toAudioUpdate 36 | else 37 | jsr audioUpdate 38 | endif 39 | lda #$00 40 | sta nmiFlag 41 | @waitNMI: 42 | lda nmiFlag 43 | beq @waitNMI 44 | rts 45 | 46 | ;; 47 | ;; audioUpdate_NMI_disableRendering [$B696] 48 | ;; 49 | ;; Performs audioUpdate_then_NMI then disables rendering 50 | ;; 51 | audioUpdate_NMI_disableRendering: 52 | jsr audioUpdate_then_NMI 53 | lda PPUMASK_RAM 54 | and #~ppumask_enable_all ;This disables rendering 55 | _setPPUMASK: 56 | sta PPUMASK ;This sub routine part is used by the next routine as well 57 | sta PPUMASK_RAM 58 | rts 59 | 60 | ;; 61 | ;; audioUpdate_NMI_enableRendering [$B6A3] 62 | ;; 63 | ;; Performs audioUpdate_then_NMI then enables rendering 64 | ;; 65 | audioUpdate_NMI_enableRendering: 66 | jsr audioUpdate_then_NMI 67 | jsr setPPUSCROLL_and_PPUCTRL 68 | lda PPUMASK_RAM 69 | ora #ppumask_enable_all ;This enables rendering 70 | bne _setPPUMASK 71 | 72 | ;; 73 | ;; finishVblank_NMI_on [$B6AF] 74 | ;; 75 | ;; As its name suggests, finishes vblank then enables NMI 76 | ;; 77 | finishVblank_NMI_on: 78 | lda PPUSTATUS 79 | and #ppustatus_vblank_in 80 | bne finishVblank_NMI_on ;Loop this while in vblank 81 | lda PPUCTRL_RAM 82 | ora #ppuctrl_nmi_on ;Then enable NMI 83 | bne _storeA_PPUCTRL 84 | 85 | ;; 86 | ;; NMI_off [$B6BC] 87 | ;; 88 | ;; Simply deactivates NMI 89 | ;; 90 | NMI_off: 91 | lda PPUCTRL_RAM 92 | and #ppuctrl_nmi_off_mask ;Deactivates NMI (~ppuctrl_nmi_on does not compile) 93 | _storeA_PPUCTRL: 94 | sta PPUCTRL ;This sub routine part is used by the previous routine as well 95 | sta PPUCTRL_RAM 96 | rts 97 | 98 | ;; 99 | ;; initPPU_addrA_hiByte [$B6C6] 100 | ;; 101 | ;; Simply sets the values to use to init PPU memory 102 | ;; 103 | ;; Input: 104 | ;; A = high byte of PPU address to init 105 | ;; 106 | initPPU_addrA_hiByte: 107 | ldx #$FF ;Value used to init PPU nametable 108 | ldy #$00 ;Value used to init PPU attribute table 109 | jsr initPPU_addrA_dataX_then_attrY 110 | rts 111 | 112 | ;; 113 | ;; setPPUSCROLL_and_PPUCTRL [$B6CE] 114 | ;; 115 | ;; Sets the PPUSCROLL to coordinates 0,0 and updates PPUCTRL with RAM 116 | ;; 117 | setPPUSCROLL_and_PPUCTRL: 118 | lda #$00 119 | sta PPUSCROLL ;x camera position 120 | sta PPUSCROLL ;y camera position 121 | lda PPUCTRL_RAM 122 | sta PPUCTRL 123 | rts 124 | 125 | ;; 126 | ;; copyBkgOrPalToPPU [$B6DC] 127 | ;; 128 | ;; Copies the nametable data for a bkg or palette from RAM to PPU 129 | ;; 130 | ;; Input: 131 | ;; The address to the data for the bkg/pal must be right after the "jsr copyBkgOrPalToPPU" used to get to this routine (this is because we use stack manipulations to access this data) 132 | ;; 133 | copyBkgOrPalToPPU: 134 | jsr getPointer_fromStack ;This gets the data address in ram address "ptr" 135 | jmp @getBkgData_nextByte 136 | @setPPUADDR: 137 | pha ;High byte of PPUADDR is pushed to stack 138 | sta PPUADDR 139 | iny 140 | lda (ptr),Y ;Low byte is next in bkg data 141 | sta PPUADDR 142 | iny 143 | lda (ptr),Y ;Next is the number of tiles to add 144 | asl A ;Doubled than stored in stack, if a bit gets in carry, we store PPUDATA vertically, otherwise, we stay horizontal 145 | pha 146 | lda PPUCTRL_RAM 147 | ora #ppuctrl_vram_dir_down ;PPUDATA will be added vertically 148 | bcs @setPPUCTRL 149 | and #~ppuctrl_vram_dir_down ;PPUDATA will be added horizontally 150 | @setPPUCTRL: 151 | sta PPUCTRL 152 | sta PPUCTRL_RAM 153 | pla ;Restore number of tiles to add 154 | asl A ;Double again, could potentially set the carry flag 155 | php ;Push processor status 156 | bcc @checkIfTilesToAdd_isZero 157 | ora #$02 ;Never gets here, but it would if we added $40 tiles at a time instead of the usual row of $20, seems to be some sort of compression method (when having to fill entire rows with the same tile) 158 | iny 159 | @checkIfTilesToAdd_isZero: 160 | plp ;Pull processor status then clear carry 161 | clc 162 | bne @restoreTilesToAdd 163 | sec ;Never gets here, but makes so that if tiles to add is now zero, the next ROR and LSR will result in $40 (64) tiles 164 | @restoreTilesToAdd: 165 | ror A 166 | lsr A 167 | tax ;Tiles to add is now in x, and carry is set if we tried to add $40 tiles 168 | @fill_PPUDATA_loop: ;Most of the times, this loops for 1 row (32 tiles) (except maybe for palette data) 169 | bcs @storeBkgData_PPUDATA 170 | iny ;Gets the next tile in the data if carry bit is not set 171 | @storeBkgData_PPUDATA: 172 | lda (ptr),Y 173 | sta PPUDATA 174 | dex 175 | bne @fill_PPUDATA_loop 176 | @checkIf_PPUADDR_3F: 177 | pla ;High byte of PPUADDR is recovered from stack 178 | cmp #>PPUPAL ;Check if we just filled the palette data 179 | bne @updateBkgDataPointer 180 | @reset_PPUADDR: ;If so, then reset PPUADDR 181 | if !optimize 182 | sta PPUADDR ;This seems unncessary since only the last two writes will count 183 | stx PPUADDR 184 | endif 185 | stx PPUADDR 186 | stx PPUADDR 187 | @updateBkgDataPointer: 188 | sec 189 | tya 190 | adc ptr_lo ;Add data offset 191 | sta ptr_lo 192 | lda #$00 193 | adc ptr_hi ;This takes care of anything that carries over 194 | sta ptr_hi 195 | @getBkgData_nextByte: ;Here is the actual enrty to this routine 196 | ldx PPUSTATUS ;Read PPUSTATUS, store it in x (not sure if the storing is useful though) 197 | ldy #$00 198 | lda (ptr),Y ;Get the first byte at the address from the pointer (aka, the actual bkg/pal data) 199 | bpl @bkgData_checkIf60 200 | rts ;If it reaches a $FF then it means the bkg is fully loaded 201 | @bkgData_checkIf60: 202 | cmp #bkgData_restorePtr 203 | bne @bkgData_checkIf4C 204 | pla ;Code never gets there... it seems to return back to the original bkg after jumping to a "sub" bkg (see next part of this routine) 205 | sta ptr_hi 206 | pla 207 | sta ptr_lo 208 | ldy #$02 ;Restore previous data pointer from stack plus 2 bytes 209 | bne @updateBkgDataPointer 210 | @bkgData_checkIf4C: 211 | cmp #bkgData_changePtr 212 | bne @setPPUADDR 213 | lda ptr_lo ;Code never gets there... it seems to essentially change the bkg data address temporarily (ex: to reuse bkg parts from another bkg) 214 | pha 215 | lda ptr_hi ;Backs up both ptr_lo and ptr_hi 216 | pha 217 | iny 218 | lda (ptr),Y ;Get next 2 bytes in bkg data 219 | tax 220 | iny 221 | lda (ptr),Y 222 | sta ptr_hi ;Then they become the new address for the bkg data 223 | stx ptr_lo 224 | bcs @getBkgData_nextByte 225 | 226 | ;; 227 | ;; getPointer_fromStack [$B765] 228 | ;; 229 | ;; As its name suggest, this gets a pointer from the stack 230 | ;; 231 | ;; Input: 232 | ;; The address we want to be the pointer must the before-last address on the stack (in other words, two jsr ago) 233 | ;; 234 | getPointer_fromStack: 235 | tsx 236 | lda stack+3,X ;Gets before to last stack address that was accessed (low byte) 237 | sta ptrTemp_low 238 | lda stack+4,X ;Gets before to last stack address that was accessed (high byte) 239 | sta ptrTemp_high 240 | ldy #$01 241 | lda (ptrTemp_low),Y 242 | sta ptr_lo ;Gets the value in the first byte following before to last stack address (becomes the low byte of the address we want to go t) 243 | iny 244 | lda (ptrTemp_low),Y 245 | sta ptr_hi ;Gets the value in the second byte following before to last stack address (becomes the high byte of the address we want to go to) 246 | clc 247 | lda #$02 ;Then increase the stack address that was "pulled" so that we jump over the data when returning 248 | adc ptrTemp_low 249 | sta stack+3,X 250 | lda #$00 ;This here is in case we need to add carry 251 | adc ptrTemp_high 252 | sta stack+4,X 253 | rts 254 | 255 | ;; 256 | ;; randomNumberGenerator [$B78B] 257 | ;; 258 | ;; Generates random number(s) into a given address 259 | ;; Always used with x=$17 and y=$02... fill 2 bytes, starting at memory $17 260 | ;; 261 | ;; Inputs: 262 | ;; X: The address at which to start generating random number(s) 263 | ;; Y: The qty of numbers (and thus bytes) 264 | ;; 265 | ;; Local variables: 266 | rng0_tmp = tmp0 267 | 268 | randomNumberGenerator: 269 | lda rng0_offset,X 270 | and #$02 ;Get the number currently at the specified rnd address, then reduce it to either 0 or 2 271 | sta rng0_tmp 272 | lda rng1_offset,X 273 | and #$02 ;We do the same for a second address 274 | eor rng0_tmp ;Then we EOR to get either a 0 or 2, which will impact if we set the carry or not 275 | clc 276 | beq @RNG_loop 277 | sec ;Set carry if EOR was 2 278 | @RNG_loop: 279 | ror rng0_offset,X ;ror gets us a new number, which can vary depending on carry flag status 280 | inx ;Increase to next address 281 | dey ;Decrease qty of rng numbers left to change 282 | bne @RNG_loop ;Loop until y = 0 283 | rts 284 | 285 | ;; 286 | ;; render_sprites [$B7A2] 287 | ;; 288 | ;; Copies sprite data from RAM to PPUOAM 289 | ;; 290 | render_sprites: 291 | lda #$00 ;Init OAMADDR 292 | sta OAMADDR 293 | lda #>sprites ;Copy all 256 bytes from $0200 to PPUOAM 294 | sta OAMDMA 295 | rts 296 | 297 | ;; 298 | ;; get_CTRL_inputs [$B7AD] 299 | ;; 300 | ;; Strobes controllers then reads button inputs for both controllers (as well as expansion ports for Famicom) 301 | ;; 302 | get_CTRL_inputs: 303 | ldx ctrlPort 304 | inx 305 | stx CTRL1 306 | dex 307 | stx CTRL1 ;1/0 write to get button states 308 | ldx #$08 ;Then eight sequential reads 309 | @read_CTRL_loop: 310 | lda CTRL1 311 | lsr A 312 | rol p1_btns_pressed ;Data from standard controller goes here 313 | lsr A 314 | rol CTRL_exp1 ;Data from expansion port goes here (Famicom only) 315 | lda CTRL2 ;Same principle for controller 2 and expansion 2 316 | lsr A 317 | rol p2_btns_pressed 318 | lsr A 319 | rol CTRL_exp2 320 | dex 321 | bne @read_CTRL_loop 322 | rts 323 | 324 | ;; 325 | ;; addExpansionCTRL [$B7CF] 326 | ;; 327 | ;; Expansion controllers are considered duplicates of controllers 1 and 2 in this game. In other words, inputs from player 1 is the addition of inputs for controller 1 and expansion 1. 328 | ;; 329 | addExpansionCTRL: 330 | lda CTRL_exp1 331 | ora p1_btns_pressed 332 | sta p1_btns_pressed 333 | lda CTRL_exp2 334 | ora p2_btns_pressed 335 | sta p2_btns_pressed 336 | rts 337 | 338 | if !removeUnused 339 | getInputs_UNUSED: 340 | jsr get_CTRL_inputs ;Looks like this simply skips the redundancy check for buttons pressed if no button presses are detected. 341 | beq _pressedVsHeld 342 | endif 343 | 344 | ;; 345 | ;; getInputs [$B7E1] 346 | ;; 347 | ;; Get inputs twice to ensure accuracy, then process which ones are held or simply pressed 348 | ;; 349 | ;; Local variables: 350 | p1_btns_pressed_tmp = tmp48 351 | p2_btns_pressed_tmp = tmp49 352 | getInputs: 353 | jsr get_CTRL_inputs 354 | jsr addExpansionCTRL 355 | lda p1_btns_pressed 356 | sta p1_btns_pressed_tmp 357 | lda p2_btns_pressed 358 | sta p2_btns_pressed_tmp 359 | jsr get_CTRL_inputs ;Redundancy to ensure the button press was not a false positive 360 | jsr addExpansionCTRL 361 | lda p1_btns_pressed 362 | and p1_btns_pressed_tmp 363 | sta p1_btns_pressed 364 | lda p2_btns_pressed 365 | and p2_btns_pressed_tmp 366 | sta p2_btns_pressed 367 | _pressedVsHeld: 368 | ldx #$01 ;Loop once each time for each controller 369 | @pressedVsHeld_loop: 370 | lda p1_btns_pressed,X 371 | tay 372 | eor p1_btns_held,X 373 | and p1_btns_pressed,X 374 | sta p1_btns_pressed,X ;If a button was held, do not count as pressed 375 | sty p1_btns_held,X ;Keeps held, adds newly pressed, removes not held anymore (in other words, currently pressed buttons) 376 | dex 377 | bpl @pressedVsHeld_loop 378 | rts 379 | 380 | if !removeUnused 381 | getInputs_redundancy_UNUSED: 382 | jsr get_CTRL_inputs ;Alternate redundancy check for controllers, aims for perfect redundancy 383 | @getInputs_redundancy_loop: 384 | ldy p1_btns_pressed 385 | lda p2_btns_pressed 386 | pha 387 | jsr get_CTRL_inputs 388 | pla 389 | cmp p2_btns_pressed 390 | bne @getInputs_redundancy_loop 391 | cpy p1_btns_pressed 392 | bne @getInputs_redundancy_loop 393 | beq _pressedVsHeld 394 | 395 | getInputs_redundancy_expansion_UNUSED: 396 | jsr get_CTRL_inputs ;Same as previous routine, but taking into consideration expansion controllers 397 | jsr addExpansionCTRL 398 | @getInputs_redundancy_expansion_loop: 399 | ldy p1_btns_pressed 400 | lda p2_btns_pressed 401 | pha 402 | jsr get_CTRL_inputs 403 | jsr addExpansionCTRL 404 | pla 405 | cmp p2_btns_pressed 406 | bne @getInputs_redundancy_expansion_loop 407 | cpy p1_btns_pressed 408 | bne @getInputs_redundancy_expansion_loop 409 | beq _pressedVsHeld 410 | 411 | getInputs_4players_UNUSED: 412 | jsr get_CTRL_inputs 413 | lda tmp0 414 | sta exp1_btns_pressed_UNUSED ;In this case, the expansion ctrl has its seperate RAM 415 | lda tmp1 416 | sta exp2_btns_pressed_UNUSED 417 | ldx #$03 418 | @pressedVsHeld_expansion_loop: 419 | lda p1_btns_pressed,X 420 | tay 421 | eor p1_btns_held_UNUSED,X 422 | and p1_btns_pressed,X 423 | sta p1_btns_pressed,X 424 | sty p1_btns_held_UNUSED,X 425 | dex 426 | bpl @pressedVsHeld_expansion_loop 427 | rts 428 | endif 429 | 430 | ;; 431 | ;; initPPU_addrA_dataX_then_attrY [$B860] 432 | ;; 433 | ;; Init PPU addresses (mainly nametables), with an optional different value for the pattern table 434 | ;; 435 | ;; Input: 436 | ;; A = high byte of PPU address to init 437 | ;; X = the value to copy in PPUDATA nametable 438 | ;; Y = the value to copy in PPUDATA nametable attribute 439 | ;; 440 | ;; Local variables: 441 | PPUADDR_hiByte = tmp0 442 | PPUDATA_init = tmp1 443 | 444 | initPPU_addrA_dataX_then_attrY: 445 | sta PPUADDR_hiByte 446 | stx PPUDATA_init 447 | sty PPUDATA_init_attr 448 | lda PPUSTATUS ;Read PPUSTATUS 449 | lda PPUCTRL_RAM 450 | and #~ppuctrl_vram_dir_down 451 | sta PPUCTRL ;Makes sure VRAM increments horizontally 452 | sta PPUCTRL_RAM 453 | lda PPUADDR_hiByte 454 | sta PPUADDR 455 | ldy #$00 456 | sty PPUADDR ;Sets the PPUADDR to xx00 (xx being the high byte input in A) 457 | ldx #>nametable_size ;Will init a complete nametable (4 times 256 bytes of data) 458 | if !optimize 459 | cmp #>nametable0 ;This portion here seems pretty much unnecessary, seems to be in case we try to init more than $2000 bytes, then we'd use the PPUDATA_2 as the number of times to init 256 bytes... probably in case we want to init pattern tables as well? 460 | bcs @storePPUData_loop1_prep 461 | ldx PPUDATA_init_attr 462 | endif 463 | @storePPUData_loop1_prep: 464 | ldy #$00 465 | lda PPUDATA_init 466 | @storePPUData_loop1: 467 | sta PPUDATA 468 | dey 469 | bne @storePPUData_loop1 ;Inner loop for 256 bytes 470 | dex 471 | bne @storePPUData_loop1 ;Outer loop to do inner loop 4 times 472 | @storePPUData_loop2_prep: 473 | ldy PPUDATA_init_attr 474 | lda PPUADDR_hiByte 475 | if !optimize 476 | cmp #>nametable0 ;Again, probably in case we want to init pattern tables 477 | bcc @restore_valX 478 | adc #$02 479 | else 480 | clc ;It feels cleaner to just clear the carry flag and add the actual correct high byte relative address to the attribute table 481 | adc #>nametable_attr_addr 482 | endif 483 | sta PPUADDR 484 | lda #p1_field 122 | ldy #>p2_field 123 | jsr copy_valueA_fromX00_toY00_plusFF 124 | lda #$0F ;We also have a routine that does just this 125 | sta p1_status 126 | sta p2_status 127 | lda #statusUpdate_delay 128 | jsr waitFor_A_frames 129 | else 130 | jsr initField_bothPlayers 131 | jsr fullStatusUpdate 132 | endif 133 | @switchMode_thenExit: 134 | lda #mode_initData_level 135 | sta mode 136 | if !optimize 137 | jmp @exit_checkVirusLeft ;Definitely not needed 138 | endif 139 | @exit_checkVirusLeft: 140 | rts 141 | 142 | ;; 143 | ;; displayStageClear [$B351] 144 | ;; 145 | ;; Simply displays the "Stage Clear" box over the level 146 | ;; 147 | displayStageClear: 148 | lda currentP 149 | tax 150 | tay 151 | lda #fieldPosEmpty 152 | jsr copy_valueA_fromX00_toY00_plusFF ;Clears this single player's playfield 153 | lda #$00 154 | sta currentP_fieldPointer 155 | ldy #$00 156 | @displayStageClear_loop: 157 | lda txtBox_stageClear,Y 158 | beq @exit_displayStageClear ;When data is zero, this means we are finished 159 | sta (currentP_fieldPointer),Y 160 | iny 161 | jmp @displayStageClear_loop 162 | @exit_displayStageClear: 163 | rts 164 | 165 | ;; 166 | ;; fireworksData_toRAM [$B36C] 167 | ;; 168 | ;; Simple routine that copies the fireworks data to RAM 169 | ;; 170 | fireworksData_toRAM: 171 | ldx #$00 172 | ldy #$00 173 | @fireworksData_toRAM_loop: 174 | lda fireworksData_yPos,X 175 | sta fireworksData_RAM,Y 176 | iny 177 | lda fireworksData_sprIndex,X ;Holds the color 178 | sta fireworksData_RAM,Y 179 | iny 180 | lda fireworksData_xPos,X 181 | sta fireworksData_RAM,Y 182 | iny 183 | inx 184 | if !bugfix 185 | cpx #fireworksData_size + 1 ;Data is incomplete for the 26th firework 186 | else 187 | cpx #fireworksData_size 188 | endif 189 | bne @fireworksData_toRAM_loop 190 | rts 191 | 192 | ;; 193 | ;; fireworksAnim [$B38B] 194 | ;; 195 | ;; Continually loops the falling fireworks (also manages their change of color) 196 | ;; 197 | ;; Local variables: 198 | fireworks_yPos = tmp47 199 | fireworks_color = tmp47 ;Re-used 200 | fireworksAnim: 201 | if !optimize 202 | lda currentP_speedSetting ;Don't think this is necessary here since we only play the final cutscene on hi speed setting 203 | cmp #speed_hi 204 | bne @exit_fireworksAnim 205 | endif 206 | ldx #$00 207 | ldy spritePointer 208 | @fireworksAnim_loop: 209 | lda fireworksData_RAM,X 210 | sta fireworks_yPos 211 | lda frameCounter 212 | and #fireworks_fall_speed ;Every $03 frames, increase Y pos 213 | bne @fireworksAnim_updateYpos 214 | inc fireworks_yPos 215 | @fireworksAnim_updateYpos: 216 | lda fireworks_yPos 217 | sta fireworksData_RAM,X 218 | sta sprites,Y 219 | inx 220 | iny ;Then update sprite index (color) 221 | lda fireworksData_RAM,X 222 | sta fireworks_color 223 | lda frameCounter 224 | and #fireworks_color_change_speed ;Cycles through all 4 colors. Updated every other frame. 225 | clc 226 | adc fireworks_color 227 | and #mask_color ;Isolate color bits 228 | ora #tileNB_fireworks ;Is the sprite index for the firework dot 229 | sta fireworksData_RAM,X 230 | sta sprites,Y 231 | inx ;Then store attributes 232 | iny 233 | lda #ppuoam_attr_behind ;Fireworks are behind the bkg 234 | sta sprites,Y 235 | iny ;Then finally store x pos 236 | lda fireworksData_RAM,X 237 | sta sprites,Y 238 | inx 239 | iny 240 | if !bugfix 241 | cpx #(fireworksData_size + 1) * 3 ;Data is incomplete for the 26th firework 242 | else 243 | cpx #fireworksData_size * 3 244 | endif 245 | bne @fireworksAnim_loop 246 | sty spritePointer 247 | @exit_fireworksAnim: 248 | rts 249 | 250 | ;; 251 | ;; fireworksData [$B3D9] 252 | ;; 253 | ;; Starting y-pos, x-pos and sprite index (color) for each firework 254 | ;; 255 | fireworksData_yPos: 256 | .db $00,$07,$0E,$15,$1C,$23,$2A,$31 ;Each is $25 bytes (fireworksData_size) long 257 | .db $38,$3F,$46,$4D,$54,$5B,$62,$69 258 | .db $70,$77,$7E,$85,$8C,$93,$9A,$A1 259 | .db $A8,$AF,$B6,$BD,$C4,$CB,$D2,$D9 260 | .db $E0,$E7,$EE,$F5,$FC 261 | if !bugfix 262 | .db $FF ;For some reasons, this one has a 26th byte 263 | endif 264 | fireworksData_xPos: 265 | .db $58,$C0,$40,$E0,$90,$10,$60,$A8 266 | .db $70,$C0,$30,$D8,$50,$E8,$90,$B0 267 | .db $10,$68,$C8,$38,$78,$20,$A8,$50 268 | .db $D8,$C0,$88,$E8,$48,$98,$30,$C8 269 | .db $60,$D8,$10,$A0,$80 270 | fireworksData_sprIndex: 271 | .db $F1,$F0,$F2,$F1,$F2,$F1,$F0,$F0 ;Strangely here $Fx is used when the actual sprites for fireworks are $4x 272 | .db $F1,$F2,$F2,$F0,$F0,$F1,$F1,$F2 273 | .db $F0,$F2,$F0,$F1,$F0,$F2,$F2,$F0 274 | .db $F1,$F0,$F2,$F2,$F1,$F1,$F0,$F2 275 | .db $F2,$F1,$F2,$F0,$F1 276 | 277 | ;; 278 | ;; checkFinalCutscene [$B449] 279 | ;; 280 | ;; Checks if we need to start the final cutscene (according to level and speed setting) 281 | ;; 282 | checkFinalCutscene: 283 | lda currentP_speedSetting 284 | cmp #speed_hi 285 | bne @exit_checkFinalCutscene 286 | lda currentP_level 287 | cmp #finalCutsceneLv + 1 ;+1 is beacause the cutscene actually plays before the beginning of the next level 288 | bne @exit_checkFinalCutscene 289 | jsr finalCutscene_stepDispatch 290 | @exit_checkFinalCutscene: 291 | rts 292 | 293 | ;; 294 | ;; finalCutscene_stepDispatch [$B459] 295 | ;; 296 | ;; Jumps to the current final cutscene step 297 | ;; 298 | finalCutscene_stepDispatch: 299 | lda finalCutsceneStep 300 | jsr toAddressAtStackPointer_indexA 301 | @jumpTable_finalCutscene_steps 302 | .word spriteUpdate_clouds 303 | .word spriteUpdate_clouds 304 | .word spriteUpdate_clouds 305 | .word changeBKGTo_dusk1 306 | .word spriteUpdate_clouds 307 | .word spriteUpdate_clouds 308 | .word changeBKGTo_dusk2 309 | .word spriteUpdate_clouds 310 | .word spriteUpdate_clouds 311 | .word changeBKGTo_night 312 | .word finalCutscene_incStep 313 | .word waitFrame0 314 | .word playSirenSFX 315 | .word updateLightning 316 | .word UFOArrives 317 | .word spriteUpdate_UFO 318 | .word changeBKGTo_UFOBeam_fireworks 319 | .word playBeamSFX 320 | .word beamBlink 321 | .word blinkBeam_absorbVirusGroup 322 | .word stopBeamSFX 323 | .word spriteUpdate_UFO 324 | .word UFOLeaves_slow 325 | .word playUFO_flyAwaySFX 326 | .word UFOLeaves_fast 327 | .word waitFrame0 328 | .word updateLightning 329 | .word changeBKGTo_fireworks 330 | .word changeBKGTo_UFOBeam_fireworks 331 | .word playEndingMusic 332 | if !optimize 333 | .word jsrTo_fireworksAnim ;This step is basically just a jsr to fireworksAnim 334 | else 335 | .word fireworksAnim 336 | endif 337 | rts 338 | 339 | ;; 340 | ;; various final cutscene steps [$B49D] 341 | ;; 342 | ;; Each routine is described below 343 | ;; 344 | playSirenSFX: 345 | lda #sq0_sq1_UFO_siren ;Plays the UFO "siren" sfx 346 | sta sfx_toPlay_sq0_sq1 347 | inc finalCutsceneStep 348 | rts 349 | 350 | playUFO_flyAwaySFX: 351 | jsr updateSprite_UFO ;Plays the UFO flying away sfx 352 | lda #noise_UFO_leaves 353 | sta sfx_toPlay_noise 354 | inc finalCutsceneStep 355 | rts 356 | 357 | playBeamSFX: 358 | jsr updateSprite_UFO ;Plays the UFO beam sfx (trg + noise) 359 | lda #trg_UFO_beam 360 | sta sfx_toPlay_trg 361 | lda #noise_beam_start 362 | sta sfx_toPlay_noise 363 | inc finalCutsceneStep 364 | rts 365 | 366 | stopBeamSFX: 367 | jsr updateSprite_UFO ;Stop the UFO beam sfx and increase the motor speed sfx 368 | lda #trg_UFO_motor_fast 369 | sta sfx_toPlay_trg 370 | lda #noise_beam_stop 371 | sta sfx_toPlay_noise 372 | inc finalCutsceneStep 373 | rts 374 | 375 | playEndingMusic: 376 | jsr updateSprite_UFO ;Plays ending music 377 | lda #mus_cutscene_explosion ;Includes music + explosion at start 378 | sta music_toPlay 379 | inc finalCutsceneStep 380 | rts 381 | 382 | finalCutscene_incStep: 383 | lda #cutsceneFrame_removeTxt ;Changes cutscene frame (this has the effect of removing "transparent" letters) and increases step 384 | sta cutsceneFrame 385 | inc finalCutsceneStep 386 | rts 387 | 388 | if !removeUnused 389 | finalCutscene_incStep_UNUSED: 390 | lda #$41 ;Probably used to wait longer or shorter than previous routine 391 | sta cutsceneFrame 392 | inc finalCutsceneStep 393 | rts 394 | endif 395 | 396 | updateLightning: 397 | lda frameCounter 398 | cmp #lightning_delay ;Update lightning only if frame counter = or > $C0 399 | bcc waitFrame0 400 | and #$0F ;Isolate low 4-bits 401 | tax 402 | lda finalCutscene_lightningPAL_change,X ;16 frames of animation 403 | sta palToChangeTo 404 | jmp waitFrame0 405 | 406 | beamBlink: 407 | jsr blinkBeam ;Blinks the beam then waits for frame 0 408 | jmp waitFrame0 409 | 410 | spriteUpdate_clouds: 411 | jsr cutscene_spriteUpdate_clouds ;Moves the clouds then wait for frame 0 412 | jmp waitFrame0 413 | 414 | spriteUpdate_UFO: 415 | jsr updateSprite_UFO ;Updates the UFO metasprite, then segues into the next routine 416 | 417 | waitFrame0: 418 | lda frameCounter ;Increase step only when frame counter wraps around to zero 419 | bne @exit_finalCutscene_step 420 | inc finalCutsceneStep 421 | @exit_finalCutscene_step: 422 | rts 423 | 424 | changeBKGTo_dusk1: 425 | lda #palNb_cutscene_dusk1 ;Changes palette to dusk 1 then increase cutscene step 426 | sta palToChangeTo 427 | inc finalCutsceneStep 428 | rts 429 | 430 | changeBKGTo_dusk2: 431 | lda #palNb_cutscene_dusk2 ;Changes palette to dusk 2 then increase cutscene step 432 | sta palToChangeTo 433 | inc finalCutsceneStep 434 | rts 435 | 436 | changeBKGTo_night: 437 | lda #palNb_cutscene_night ;Changes palette to night then increase cutscene step 438 | sta palToChangeTo 439 | inc finalCutsceneStep 440 | rts 441 | 442 | changeBKGTo_fireworks: 443 | lda #palNb_cutscene_fireworks ;Changes palette to fireworks then increase cutscene step 444 | sta palToChangeTo 445 | inc finalCutsceneStep 446 | rts 447 | 448 | changeBKGTo_UFOBeam_fireworks: 449 | jsr updateSprite_UFO ;Update UFO sprite then change palette 450 | lda #palNb_cutscene_UFO_fireworks ;Shared by both UFO beam and fireworks 451 | sta palToChangeTo 452 | inc finalCutsceneStep 453 | rts 454 | 455 | UFOArrives: 456 | lda spriteYPos_UFO 457 | cmp #spr_UFO_y_low ;Increase UFO y until reaching $44 458 | bne @increaseUFO_yPos 459 | inc finalCutsceneStep 460 | jmp @exit_UFOArrives 461 | @increaseUFO_yPos: 462 | lda frameCounter 463 | and #spr_UFO_ver_speed_slow ;Move every 4 frames 464 | bne @exit_UFOArrives 465 | inc spriteYPos_UFO 466 | @exit_UFOArrives: 467 | jsr updateSprite_UFO 468 | rts 469 | 470 | UFOLeaves_slow: 471 | lda spriteYPos_UFO 472 | cmp #spr_UFO_y_high ;Decrease UFO y until reaching $44 473 | bne @decreaseUFO_yPos 474 | inc finalCutsceneStep 475 | jmp @exit_UFOLeaves_slow 476 | @decreaseUFO_yPos: 477 | lda frameCounter 478 | and #spr_UFO_ver_speed_slow ;Move every 4 frames 479 | bne @exit_UFOLeaves_slow 480 | dec spriteYPos_UFO 481 | @exit_UFOLeaves_slow: 482 | jsr updateSprite_UFO 483 | rts 484 | 485 | UFOLeaves_fast: 486 | lda spriteXPos_UFO 487 | cmp #spr_UFO_x_final ;Move UFO until x reaches $E0 (meaning it is offscreen) 488 | bne @decreaseUFO_xPos_yPos 489 | inc finalCutsceneStep 490 | jmp @exit_UFOLeaves_fast 491 | @decreaseUFO_xPos_yPos: 492 | rept spr_UFO_hor_speed_fast 493 | dec spriteXPos_UFO 494 | endr 495 | ;dec spriteXPos_UFO ;Move twice in x every frame 496 | ;dec spriteXPos_UFO 497 | lda frameCounter 498 | and #spr_UFO_ver_speed_fast ;Move in y every 2 frames 499 | bne @exit_UFOLeaves_fast 500 | dec spriteYPos_UFO 501 | @exit_UFOLeaves_fast: 502 | jsr updateSprite_UFO 503 | rts 504 | 505 | jsrTo_fireworksAnim: 506 | jsr fireworksAnim ;Seems unecessary to have a step that is just a jsr and then an rts 507 | rts 508 | 509 | updateSprite_UFO: 510 | lda spriteXPos_UFO ;Updates the UFO's position and animations (bypassed if UFO x position is zero) 511 | beq @exit_updateSprite_UFO 512 | sta spriteXPos 513 | lda spriteYPos_UFO 514 | sta spriteYPos 515 | lda #spr_UFO_top ;Start with UFO top 516 | sta metaspriteIndex 517 | jsr metaspriteUpdate 518 | lda frameCounter 519 | and #spr_UFO_bottom_speed ;Every 4 frames, change the UFO bottom anim frame 520 | lsr A 521 | lsr A 522 | clc 523 | adc #spr_UFO_bottom_frame0 524 | sta metaspriteIndex 525 | jsr metaspriteUpdate 526 | @exit_updateSprite_UFO: 527 | rts 528 | 529 | blinkBeam_absorbVirusGroup: 530 | jsr blinkBeam ;First update UFO and blink beam 531 | lda virusGroup_YPos 532 | sec 533 | sbc #spr_virusGroup_minDist_UFO ;Until within a distance of $24 of the UFO, decrease virus group Y 534 | cmp spriteYPos_UFO 535 | bcs @updateVirusGroup_yPos 536 | lda #spr_offscreen_y ;When reach predetermined height, set Y to $F8 (puts it offscreen) 537 | sta virusGroup_YPos 538 | inc finalCutsceneStep 539 | jmp @exit_blinkBeam_absorbVirusGroup 540 | @updateVirusGroup_yPos: 541 | lda frameCounter 542 | and #spr_virusGroupCutscene_ver_speed ;Move every 4 frames 543 | bne @exit_blinkBeam_absorbVirusGroup 544 | dec virusGroup_YPos 545 | @exit_blinkBeam_absorbVirusGroup: 546 | rts 547 | 548 | blinkBeam: 549 | jsr updateSprite_UFO ;Updates the UFO sprite then blinks the beam 550 | lda frameCounter 551 | and #spr_UFO_beam_speed ;Blink beam every 2 frames 552 | beq @exit_blinkBeam 553 | lda #spr_UFO_beam 554 | sta metaspriteIndex 555 | jsr metaspriteUpdate 556 | @exit_blinkBeam: 557 | rts 558 | 559 | ;; 560 | ;; cutscene_spriteUpdate_flyingObject [$B5D2] 561 | ;; 562 | ;; Decides when, where and how the flying object in the cutscene moves 563 | ;; 564 | ;; Local variables: 565 | flyingObject_moveFrame = tmp47 566 | flyingObject_animSpeed = tmp47 ;Shared 567 | cutscene_spriteUpdate_flyingObject: 568 | if !optimize 569 | lda currentP_speedSetting 570 | asl A 571 | asl A 572 | asl A 573 | asl A 574 | asl A 575 | clc 576 | adc currentP_level 577 | tax 578 | lda cutscene_objectFlying_basedOnSpeedAndLvl,X 579 | else 580 | jsr cutscene_objectFlying_check 581 | endif 582 | bne @cutsceneNotEmpty ;If table returns NOT a zero, than there is a cutscene 583 | jmp @exit_cutscene_spriteUpdate_flyingObject 584 | @cutsceneNotEmpty: 585 | lda flyingObjectStatus 586 | cmp #flyingObjectStatus_removeTxt 587 | bne @flyingObjectStatus_not4 588 | lda #cutsceneFrame_removeTxt 589 | sta cutsceneFrame 590 | inc flyingObjectStatus ;If flying object status is 4, increase it to 5 591 | @flyingObjectStatus_not4: 592 | lda flyingObjectStatus 593 | cmp #flyingObjectStatus_moving 594 | beq @flyingObject_checkIf_moveX 595 | @flyingObjectStatus_not6: 596 | lda frameCounter 597 | beq @frameCounter_isZero 598 | jmp @exit_cutscene_spriteUpdate_flyingObject 599 | @frameCounter_isZero: 600 | inc flyingObjectStatus ;If flying object status is NOT 4 nor 6, and that we are at frame 0, then increase flying object status 601 | lda flyingObjectStatus 602 | cmp #flyingObjectStatus_appears 603 | beq @flyingObject_appears 604 | jmp @exit_cutscene_spriteUpdate_flyingObject 605 | @flyingObject_appears: 606 | if !optimize 607 | lda currentP_speedSetting ;If flying object status is 2 after increasing, then display the object 608 | asl A 609 | asl A 610 | asl A 611 | asl A 612 | asl A 613 | clc 614 | adc currentP_level 615 | tax 616 | lda cutscene_objectFlying_basedOnSpeedAndLvl,X 617 | else 618 | jsr cutscene_objectFlying_check 619 | endif 620 | sta flyingObjectNb ;There probably is a way to store this way before and optimize some more 621 | lda flyingObjectNb 622 | tax 623 | lda cutscene_objectFlying_leftOrRight,X ;Gives the starting position, either left or right of screen 624 | sta flyingObject_XPos 625 | lda #spr_cutscene_flyingObject_y 626 | sta flyingObject_YPos 627 | @flyingObject_checkIf_moveX: 628 | ldx flyingObjectNb 629 | lda cutscene_objectFlying_moveFrame,X ;Get at what frame interval the object moves 630 | sta flyingObject_moveFrame 631 | lda frameCounter 632 | and flyingObject_moveFrame 633 | bne @check_changeAnimFrame 634 | @flyingObject_moveX: 635 | lda cutscene_objectFlying_XOffset,X ;Every time the object is moving, it moves in X by what is in this table 636 | clc 637 | adc flyingObject_XPos 638 | sta flyingObject_XPos 639 | cmp #spr_cutscene_right_boundary_x+1 ;Keep moving until x pos is = or > $F1 640 | bcc @check_changeAnimFrame 641 | lda #spr_cutscene_offscreen_x ;Don't bother changing anim frame if we are offscreen 642 | sta flyingObject_XPos 643 | jmp @exit_cutscene_spriteUpdate_flyingObject 644 | @check_changeAnimFrame: 645 | lda cutscene_objectFlying_flipFrame,X ;This tells us at what frame interval we update the anim 646 | sta flyingObject_animSpeed 647 | lda frameCounter 648 | and flyingObject_animSpeed 649 | bne @spriteUpdate_flyingObject 650 | @changeAnimFrame: 651 | lda flyingObject_IndexOffset 652 | eor #spr_cutscene_flyingObject_frames ;All anims are only 2 frames, this switches between frames 0 and 1 653 | and #spr_cutscene_flyingObject_frames 654 | sta flyingObject_IndexOffset 655 | @spriteUpdate_flyingObject: 656 | lda flyingObject_XPos ;Then store all data and update metasprite 657 | sta spriteXPos 658 | lda flyingObject_YPos 659 | sta spriteYPos 660 | ldx flyingObjectNb 661 | lda cutscene_objectFlying_sprIndex,X 662 | clc 663 | adc flyingObject_IndexOffset 664 | sta metaspriteIndex 665 | jsr metaspriteUpdate 666 | @exit_cutscene_spriteUpdate_flyingObject: 667 | rts -------------------------------------------------------------------------------- /prg/drmario_prg_visual_nametable.asm: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; render_fieldRow_bothPlayers [$8307] 3 | ;; 4 | ;; Renders a single row for player 1 (and 2 if 2 players) 5 | ;; 6 | render_fieldRow_bothPlayers: 7 | lda #player1 ;Make player 1 the current player 8 | sta currentP 9 | lda p1_status 10 | sta currentP_status 11 | jsr render_fieldRow ;Render 1 row 12 | lda currentP_status ;Return player 1 status 13 | sta p1_status 14 | lda nbPlayers ;If 2 players, do the same for player 2, otherwise exit 15 | cmp #$02 16 | bne @exit_render_fieldRow_bothPlayers 17 | lda #player2 18 | sta currentP 19 | lda p2_status 20 | sta currentP_status 21 | jsr render_fieldRow 22 | lda currentP_status 23 | sta p2_status 24 | @exit_render_fieldRow_bothPlayers: 25 | rts 26 | 27 | 28 | ;; 29 | ;; render_fieldRow [$8331] 30 | ;; 31 | ;; Single field row render for current player 32 | ;; 33 | render_fieldRow: 34 | lda currentP_status ;Check if a row needs update (if $FF, it doesn't) 35 | cmp #$FF 36 | beq @exit_render_fieldRow 37 | lda nbPlayers ;Different routine if 1 or 2 players 38 | cmp #$01 39 | bne @render_fieldRow_2players 40 | @render_fieldRow_1player: 41 | lda currentP_status ;Get field row to render, multiply by 2 to get an index to a 2-byte PPU address 42 | asl A 43 | tay 44 | setPPUADDR_fromTable fieldRow_PPUADDR_1P 45 | ;lda fieldRow_PPUADDR_1P+0,Y 46 | ;sta PPUADDR 47 | ;lda fieldRow_PPUADDR_1P+1,Y 48 | ;sta PPUADDR 49 | jmp @render_fieldRow_prepare 50 | @render_fieldRow_2players: 51 | lda currentP ;Different routine if player 1 or 2 in 2-player mode 52 | cmp #player1 53 | bne @render_fieldRow_2players_p2 54 | @render_fieldRow_2players_p1: 55 | lda currentP_status ;Same as 1-player, but with different PPU address table 56 | asl A 57 | tay 58 | setPPUADDR_fromTable fieldRow_PPUADDR_2P_P1 59 | ;lda fieldRow_PPUADDR_2P_P1+0,Y 60 | ;sta PPUADDR 61 | ;lda fieldRow_PPUADDR_2P_P1+1,Y 62 | ;sta PPUADDR 63 | jmp @render_fieldRow_prepare 64 | @render_fieldRow_2players_p2: 65 | lda currentP_status ;Same as 1-player, but with different PPU address table 66 | asl A 67 | tay 68 | setPPUADDR_fromTable fieldRow_PPUADDR_2P_P2 69 | ;lda fieldRow_PPUADDR_2P_P2+0,Y 70 | ;sta PPUADDR 71 | ;lda fieldRow_PPUADDR_2P_P2+1,Y 72 | ;sta PPUADDR 73 | @render_fieldRow_prepare: 74 | lda #$00 ;Init field pointer 75 | sta currentP_fieldPointer 76 | lda currentP_status ;Get current row, and multiply by 8 to get current field position (since there are 8 positions per row) 77 | asl A 78 | asl A 79 | asl A 80 | tay ;Becomes the offset to the field pointer 81 | ldx #rowSize ;We are going to loop rowSize times (8) 82 | @render_fieldPos_loop: 83 | lda (currentP_fieldPointer),Y ;Get the field object type 84 | and #mask_fieldobject_type 85 | cmp #clearedPillOrVirus ;Don't change anything unless it is a cleared pill or virus 86 | bne @keepFieldPosValue 87 | lda (currentP_fieldPointer),Y ;Get the field object color 88 | and #mask_fieldobject_color 89 | ora #fieldPosJustEmptied ;And signal as empty field position 90 | jmp @render_fieldPos_PPUDATA 91 | @keepFieldPosValue: 92 | lda (currentP_fieldPointer),Y 93 | @render_fieldPos_PPUDATA: 94 | sta PPUDATA 95 | iny ;Check next field position, unless the loop (aka row) is finished 96 | dex 97 | bne @render_fieldPos_loop 98 | dec currentP_status ;Rows are numbered from top to bottom, so decreasing it means the next row will be 1 higher 99 | @exit_render_fieldRow: 100 | rts 101 | 102 | 103 | ;; 104 | ;; render_palChange_or_bkgOverlays [$83A3] 105 | ;; 106 | ;; First checks if it needs to update the palette, if so, update the palette then exit. If not, render bkg overlays (options, score, text, victories). 107 | ;; Both cannot be done at the same time. 108 | ;; 109 | ;; Local variables: 110 | PPUADDR_tmp_hi = tmp48 111 | PPUADDR_tmp_lo = tmp47 112 | render_palChange_or_bkgOverlays: 113 | lda palToChangeTo ;Check if we need to update the palette 114 | beq @noPalChange 115 | jmp palChange 116 | @noPalChange: 117 | lda visualUpdateFlags_options ;If not, check if we need to update options overlays 118 | beq @noOptionsChange 119 | jmp render_optionsOverlays 120 | @noOptionsChange: 121 | lda visualUpdateFlags ;If not, check if we need to update any other overlays. and exit routine if still nothing to update 122 | bne @renderScore_Check 123 | jmp @exit_render_palChange_or_bkgOverlays 124 | @renderScore_Check: 125 | lda nbPlayers ;Otherwise, check if we are in 1-player mode (so we have to display the score) 126 | cmp #$01 127 | beq @renderScore 128 | jmp @renderVictories_Check ;If not, skip both the score, highscore, virus left and level nb check 129 | @renderScore: 130 | lda visualUpdateFlags ;Check if the score is marked for update 131 | and #vu_pScore 132 | beq @renderHighScore ;If not, skip to next check 133 | setPPUADDR_absolute score_VRAMaddr 134 | ;lda #$21 135 | ;sta PPUADDR 136 | ;lda #$62 137 | ;sta PPUADDR 138 | lda score+5 ;Write score to appropriate PPU address 139 | sta PPUDATA 140 | lda score+4 141 | sta PPUDATA 142 | lda score+3 143 | sta PPUDATA 144 | lda score+2 145 | sta PPUDATA 146 | lda score+1 147 | sta PPUDATA 148 | lda score+0 149 | sta PPUDATA 150 | lda visualUpdateFlags 151 | and #~vu_pScore ;Remove player score from visual update flag 152 | sta visualUpdateFlags 153 | @renderHighScore: 154 | lda visualUpdateFlags ;Same principle as player score, but this time for highscore 155 | and #vu_hScore 156 | beq @renderVirusLeft 157 | setPPUADDR_absolute highScore_VRAMaddr 158 | ;lda #$21 159 | ;sta PPUADDR 160 | ;lda #$02 161 | ;sta PPUADDR 162 | lda highScore+5 163 | sta PPUDATA 164 | lda highScore+4 165 | sta PPUDATA 166 | lda highScore+3 167 | sta PPUDATA 168 | lda highScore+2 169 | sta PPUDATA 170 | lda highScore+1 171 | sta PPUDATA 172 | lda highScore+0 173 | sta PPUDATA 174 | lda visualUpdateFlags 175 | and #~vu_hScore 176 | sta visualUpdateFlags 177 | @renderVirusLeft: 178 | lda visualUpdateFlags ;Check if the nb of virus left is marked for update 179 | and #vu_virusLeft 180 | beq @renderLevelNb ;If not, skip to next check 181 | setPPUADDR_absolute virusLeft_VRAMaddr 182 | ;lda #$23 183 | ;sta PPUADDR 184 | ;lda #$3B 185 | ;sta PPUADDR 186 | lda p1_virusLeft ;This value is in decimal, so we isolate and write the first digit 187 | and #$F0 188 | lsr A 189 | lsr A 190 | lsr A 191 | lsr A 192 | sta PPUDATA 193 | lda currentP_virusLeft ;Strangely, currentP is used here instead of p1 194 | and #$0F ;We then isolate and write the second digit 195 | sta PPUDATA 196 | lda visualUpdateFlags 197 | and #~vu_virusLeft ;Remove virus left from visual update flag 198 | sta visualUpdateFlags 199 | @renderLevelNb: 200 | lda visualUpdateFlags ;Check if the lvl nb is marked for update 201 | and #vu_lvlNb 202 | beq @renderVictories_Check ;If not, skip to next check 203 | setPPUADDR_absolute levelNb_VRAMaddr 204 | ;lda #$22 205 | ;sta PPUADDR 206 | ;lda #$7B 207 | ;sta PPUADDR 208 | ldx p1_level ;Transform hex lvl number into decimal with a lookup table (the lvl nb is capped to $18 (24 in decimal)) 209 | lda levelForDisplay,X 210 | jsr renderLevelNb_2digits 211 | lda visualUpdateFlags 212 | and #~vu_lvlNb ;Remove lvl nb from visual update flag 213 | sta visualUpdateFlags 214 | @renderVictories_Check: 215 | lda nbPlayers ;Check if 2 players, if so, we render victories, if not we exit this routine 216 | cmp #$02 217 | beq @renderVictories 218 | jmp @exit_render_palChange_or_bkgOverlays 219 | @renderVictories: 220 | lda visualUpdateFlags ;Check if victories (which occur at end of level) are marked for update 221 | and #vu_endLvl 222 | bne @renderVictories_p1 ;If not, exit this routine 223 | jmp @exit_render_palChange_or_bkgOverlays 224 | @renderVictories_p1: 225 | lda p1_victories ;Check if p1 has victories, if not we skip to p2 226 | beq @renderVictories_p2 227 | asl A ;Otherwise, we transform p1 victories into a zero-based index for 2-bytes addresses 228 | tax 229 | dex 230 | dex 231 | lda crownsP1_PPUADDR+0,X 232 | sta PPUADDR 233 | sta PPUADDR_tmp_hi ;PPUADDR is stored so that the second row of graphics is relative to first row 234 | lda crownsP1_PPUADDR+1,X 235 | sta PPUADDR 236 | sta PPUADDR_tmp_lo 237 | lda #tileNb_crown_topLeft 238 | sta PPUDATA 239 | lda #tileNb_crown_topRight 240 | sta PPUDATA 241 | lda PPUADDR_tmp_lo ;We then offset the low byte by 1 row in the nametable ($20 aka 32 tiles) 242 | clc 243 | adc #vram_row 244 | sta PPUADDR_tmp_lo 245 | lda #$00 ;Increase the high byte if ever there is a carry 246 | adc PPUADDR_tmp_hi 247 | sta PPUADDR 248 | lda PPUADDR_tmp_lo 249 | sta PPUADDR 250 | lda #tileNb_crown_bottomLeft 251 | sta PPUDATA 252 | lda #tileNb_crown_bottomRight 253 | sta PPUDATA 254 | @renderVictories_p2: 255 | lda p2_victories ;Same principle as p1 victories, only starting from a different PPU address table 256 | beq @renderVictories_finished 257 | asl A 258 | tax 259 | dex 260 | dex 261 | lda crownsP2_PPUADDR+0,X 262 | sta PPUADDR 263 | sta PPUADDR_tmp_hi 264 | lda crownsP2_PPUADDR+1,X 265 | sta PPUADDR 266 | sta PPUADDR_tmp_lo 267 | lda #tileNb_crown_topLeft 268 | sta PPUDATA 269 | lda #tileNb_crown_topRight 270 | sta PPUDATA 271 | lda PPUADDR_tmp_lo 272 | clc 273 | adc #vram_row 274 | sta PPUADDR_tmp_lo 275 | lda #$00 276 | adc PPUADDR_tmp_hi 277 | sta PPUADDR 278 | lda PPUADDR_tmp_lo 279 | sta PPUADDR 280 | lda #tileNb_crown_bottomLeft 281 | sta PPUDATA 282 | lda #tileNb_crown_bottomRight 283 | sta PPUDATA 284 | @renderVictories_finished: 285 | lda visualUpdateFlags 286 | if !bugfix 287 | and cutsceneFrame ;Seems like it was meant to be an absolute address (#~vu_endLvl and thus #$7F), but that the zero page mode was used instead, resulting in a reference to cutsceneFrame (and thus $7F) 288 | else 289 | and #vu_endLvl_mask ;Suggested bugfix (inconsequential) (for some reasons "~vu_endLvl" doesn't compile, so I went for a seperate constant "vu_endLvl_mask") 290 | endif 291 | sta visualUpdateFlags 292 | @exit_render_palChange_or_bkgOverlays: 293 | rts 294 | 295 | 296 | ;; 297 | ;; render_optionsOverlays [$8518] 298 | ;; 299 | ;; This routine updates the vertical options selector, as well as level nb in the box on the right 300 | ;; 301 | render_optionsOverlays: 302 | lda visualUpdateFlags_options 303 | and #vu_options_ver_mask ;Checks if there's anything to update beside vertical options selector, if so then it means a lvl change 304 | bne @options_checkIfLvlChange 305 | lda visualUpdateFlags_options ;No lvl change so we check if vertical options cursor is on the level 306 | @options_checkIfOnLvl: 307 | cmp #vu_options_ver_lvl 308 | bne @options_checkIfOnSpeed 309 | @options_onLvl: 310 | jsr copyBkgOrPalToPPU ;If so we update PPU with the corresponding data 311 | .word bkgOptions_cursorOnLvl 312 | lda #$00 ;Then we reset the options visual update flag and exit this routine 313 | sta visualUpdateFlags_options 314 | jmp @exit_render_optionsOverlays 315 | @options_checkIfOnSpeed: 316 | cmp #vu_options_ver_speed ;Same as for options on level, but for speed 317 | bne @options_checkIfOnMusic 318 | @options_onSpeed: 319 | jsr copyBkgOrPalToPPU 320 | .word bkgOptions_cursorOnSpeed 321 | lda #$00 322 | sta visualUpdateFlags_options 323 | jmp @exit_render_optionsOverlays 324 | @options_checkIfOnMusic: 325 | cmp #vu_options_ver_music ;Same as for options on level, but for music type 326 | bne @exit_render_optionsOverlays ;Exit the routine if not on music since we already checked if there was a level change 327 | @options_onMusic: 328 | jsr copyBkgOrPalToPPU 329 | .word bkgOptions_cursorOnMusic 330 | lda #$00 331 | sta visualUpdateFlags_options 332 | jmp @exit_render_optionsOverlays 333 | @options_checkIfLvlChange: 334 | if !optimize 335 | lda visualUpdateFlags_options ;Check if the level nb display needs update (we already know that it's the case from a previous check, probably safe to remove) 336 | and #vu_options_lvl_nb 337 | beq @exit_render_optionsOverlays 338 | endif 339 | @options_lvlChange: 340 | lda visualUpdateFlags_options ;If so, reset the level nb display update flag 341 | and #~vu_options_lvl_nb 342 | sta visualUpdateFlags_options 343 | setPPUADDR_absolute lvlNbP1_VRAMaddr 344 | ;lda #$21 345 | ;sta PPUADDR 346 | ;lda #$57 347 | ;sta PPUADDR 348 | ldx p1_level ;Update player 1 level display (in decimal) according to table 349 | lda levelForDisplay,X 350 | jsr renderLevelNb_2digits 351 | lda nbPlayers ;Check if there's 2 players, if so, update the level display for player 2 also 352 | cmp #$02 353 | bne @exit_render_optionsOverlays 354 | setPPUADDR_absolute lvlNbP2_VRAMaddr 355 | ;lda #$21 356 | ;sta PPUADDR 357 | ;lda #$B7 358 | ;sta PPUADDR 359 | ldx p2_level 360 | lda levelForDisplay,X 361 | jsr renderLevelNb_2digits 362 | @exit_render_optionsOverlays: 363 | rts 364 | 365 | 366 | ;; 367 | ;; palChange [$858A] 368 | ;; 369 | ;; This routine changes the palette according to the current screen (based on the value stored in palToChangeTo) 370 | ;; 371 | palChange: 372 | lda palToChangeTo ;Checks if palette to update is for a 1-player level 373 | cmp #palNb_level_1P 374 | bne @palChange_check_title 375 | jsr copyBkgOrPalToPPU ;If so, copy the 1-player level palette 376 | .word palLevel_1P 377 | jsr setBkgColor_basedOnSpeed ;Then update the color according to speed 378 | jmp resetPalToChangeTo 379 | @palChange_check_title: 380 | cmp #palNb_title ;Basically the same for all the different screen palettes 381 | bne @palChange_check_options 382 | jsr copyBkgOrPalToPPU 383 | .word palTitle 384 | jmp resetPalToChangeTo 385 | @palChange_check_options: 386 | cmp #palNb_options 387 | bne @palChange_check_level2p 388 | jsr copyBkgOrPalToPPU 389 | .word palOptions 390 | jmp resetPalToChangeTo 391 | @palChange_check_level2p: 392 | cmp #palNb_level_2p 393 | bne @palChange_check_cutscene 394 | jsr copyBkgOrPalToPPU 395 | .word palLevel_2P 396 | jsr setBkgColor_basedOnSpeed ;2-player level also has the same principle of chanding the color based on speed 397 | jmp resetPalToChangeTo 398 | @palChange_check_cutscene: 399 | cmp #palNb_cutscene 400 | bne @palChange_check_cutscene_night 401 | jsr copyBkgOrPalToPPU 402 | .word palCutscene 403 | jmp resetPalToChangeTo 404 | @palChange_check_cutscene_night: 405 | cmp #palNb_cutscene_night 406 | bne @palChange_check_lv20Low_ending 407 | jsr copyBkgOrPalToPPU 408 | .word palCutscene_night 409 | jmp resetPalToChangeTo 410 | @palChange_check_lv20Low_ending: 411 | cmp #palNb_lvl20Low_ending 412 | bne @palChange_check_cutscene_UFO_fireworks 413 | jsr copyBkgOrPalToPPU 414 | .word palLvl20Low_ending 415 | jmp resetPalToChangeTo 416 | @palChange_check_cutscene_UFO_fireworks: 417 | cmp #palNb_cutscene_UFO_fireworks 418 | bne @palChange_check_cutscene_dusk1 419 | jsr copyBkgOrPalToPPU 420 | .word palCutscene_UFO_fireworks_spritesOnly 421 | jmp resetPalToChangeTo 422 | @palChange_check_cutscene_dusk1: 423 | cmp #palNb_cutscene_dusk1 424 | bne @palChange_check_cutscene_dusk2 425 | jsr copyBkgOrPalToPPU 426 | .word palCutscene_dusk1 427 | jmp resetPalToChangeTo 428 | @palChange_check_cutscene_dusk2: 429 | cmp #palNb_cutscene_dusk2 430 | bne @palChange_check_cutscene_lightning 431 | jsr copyBkgOrPalToPPU 432 | .word palCutscene_dusk2 433 | jmp resetPalToChangeTo 434 | @palChange_check_cutscene_lightning: 435 | cmp #palNb_cutscene_lightning 436 | bne @palChange_check_cutscene_fireworks 437 | jsr copyBkgOrPalToPPU 438 | .word palCutscene_lightning 439 | jmp resetPalToChangeTo 440 | @palChange_check_cutscene_fireworks: 441 | cmp #palNb_cutscene_fireworks 442 | bne resetPalToChangeTo 443 | jsr copyBkgOrPalToPPU 444 | .word palCutscene_fireworks_bkgOnly 445 | jmp resetPalToChangeTo 446 | resetPalToChangeTo: 447 | lda #$00 ;Reset palToChangeTo then exit 448 | sta palToChangeTo 449 | rts 450 | 451 | 452 | ;; 453 | ;; setBkgColor_basedOnSpeed [$8627] 454 | ;; 455 | ;; Updates the last color of bkg palette 0, 1 and 3 according to the speed setting of the current player 456 | ;; Since the checks are done p1, then p2, this is why p2 speed always determines the bkg in 2 player game 457 | ;; 458 | setBkgColor_basedOnSpeed: 459 | setPPUADDR_absolute (PPUPAL_BKG0+3) ;Set the PPUADDR to fourth color of bkg 0 palette 460 | ;lda #$3F 461 | ;sta PPUADDR 462 | ;lda #$03 463 | ;sta PPUADDR 464 | ldx currentP_speedSetting ;Use current speed as index to get color to use 465 | lda bkgColor_basedOnSpeed,X 466 | sta PPUDATA 467 | setPPUADDR_absolute (PPUPAL_BKG1+3) ;Do the same for bkg 1 palette 468 | ;lda #$3F 469 | ;sta PPUADDR 470 | ;lda #$07 471 | ;sta PPUADDR 472 | ldx currentP_speedSetting 473 | lda bkgColor_basedOnSpeed,X 474 | sta PPUDATA 475 | setPPUADDR_absolute (PPUPAL_BKG3+3) ;Do the same for bkg 3 palette 476 | ;lda #$3F 477 | ;sta PPUADDR 478 | ;lda #$0F 479 | ;sta PPUADDR 480 | ldx currentP_speedSetting 481 | lda bkgColor_basedOnSpeed,X 482 | sta PPUDATA 483 | rts 484 | 485 | 486 | ;; 487 | ;; renderLevelNb_2digits [$865E] 488 | ;; 489 | ;; This helper routine writes both digits of a decimal number to PPUDATA 490 | ;; 491 | ;; Input: 492 | ;; a: holds a two digit decimal number 493 | ;; 494 | ;; Local variables: 495 | levelNb_2digits = tmp47 496 | renderLevelNb_2digits: 497 | sta levelNb_2digits 498 | and #$F0 ;Isolate the first digit 499 | lsr A ;Shift 4 times to the right to get first digit in the lower 4 bits, then store it 500 | lsr A 501 | lsr A 502 | lsr A 503 | sta PPUDATA 504 | lda levelNb_2digits 505 | and #$0F ;Isolate the second digit, store as is 506 | sta PPUDATA 507 | rts 508 | 509 | 510 | ;; 511 | ;; render_cutscene_txt [$8671] 512 | ;; 513 | ;; These bundled routines add or remove text during the cutscenes 514 | ;; 515 | render_cutscene_txt: 516 | lda cutsceneFrame ;Check cutscene frame, if 0, nothing to render 517 | beq @exit_render_cutscene_txt 518 | cmp #$80 ;If frame is $80 or more, this means text has to be removed 519 | bcs _cutscene_removeTxt 520 | lda frameCounter ;Update cutscene text only every txtScrollSpeed_cutscene frames from the frame COUNTER (not cutscene frame) (8 frames in vanilla) 521 | and #txtScrollSpeed_cutscene 522 | bne @exit_render_cutscene_txt 523 | lda cutsceneFrame ;Store the cutscene frame in X to use as an index later 524 | tax 525 | lda #sq0_letter_cutscene ;Play sfx for letter added 526 | sta sfx_toPlay_sq0 527 | lda #>cutsceneText_VRAMaddr ;All text shares the same high byte address, and gets low byte and data from a table 528 | sta PPUADDR 529 | lda cutsceneText_PPUaddr,X 530 | sta PPUADDR 531 | lda cutsceneText,X 532 | jsr _cutsceneText_interpreter 533 | inc cutsceneFrame ;Increase cutscene frame. If not $FF, exit routine. Otherwise the cutscene is finished 534 | cmp #$FF 535 | bne @exit_render_cutscene_txt 536 | lda #$00 537 | sta cutsceneFrame 538 | @exit_render_cutscene_txt: 539 | rts 540 | 541 | _cutsceneText_interpreter: 542 | bne @cutscene_lvlNb_check ;Current cutsceneText data is in A. If $00, we add the level speed text, otherwise, check if we add lvl 543 | @cutscene_speedTxt: 544 | lda currentP_speedSetting ;Multiply current speed setting by four to get an index for the 3-letter speed text 545 | asl A 546 | asl A 547 | tax 548 | lda cutsceneText_speed+0,X 549 | sta PPUDATA 550 | lda cutsceneText_speed+1,X 551 | sta PPUDATA 552 | lda cutsceneText_speed+2,X 553 | sta PPUDATA 554 | rts 555 | @cutscene_lvlNb_check: 556 | cmp #$01 ;Current cutsceneText data is in A. If $01, we add the level nb, otherwise, data is a single letter 557 | bne @cutscene_txt_singleLetter 558 | @cutscene_lvlNb: 559 | ldx currentP_level 560 | dex ;Decrease by one because level was increased prior to cutscene 561 | lda levelForDisplay,X 562 | if !optimize 563 | and #$F0 ;Exact replica of the renderLevelNb_2digits helper routine 564 | lsr A 565 | lsr A 566 | lsr A 567 | lsr A 568 | sta PPUDATA 569 | lda levelForDisplay,X 570 | and #$0F 571 | sta PPUDATA 572 | rts 573 | else 574 | jmp renderLevelNb_2digits 575 | endif 576 | @cutscene_txt_singleLetter: 577 | sta PPUDATA ;Current cutsceneText letter is in A, store as is 578 | rts 579 | 580 | _cutscene_removeTxt: 581 | lda cutsceneFrame ;Drop highest bit from cutscene frame (is the equivalent of subtracting $80) 582 | and #$7F 583 | tax ;Set the PPUADDR similarly to how it is done in the render_cutscene_txt routine 584 | lda #>cutsceneText_VRAMaddr 585 | sta PPUADDR 586 | lda cutsceneText_PPUaddr,X 587 | sta PPUADDR 588 | lda cutsceneText,X 589 | jsr _cutsceneText_remove_interpreter 590 | inc cutsceneFrame 591 | lda cutsceneText,X ;If cutscene text data is $FF, then cutscene is finished, otherwise, exit routine 592 | cmp #$FF 593 | bne @exit_cutscene_removeTxt 594 | lda #$00 595 | sta cutsceneFrame 596 | @exit_cutscene_removeTxt: 597 | rts 598 | 599 | _cutsceneText_remove_interpreter: 600 | bne @cutscene_removeTxt_lvlNb_check ;Same principle as cutsceneText_interpreter, but for removing characters instead of adding them 601 | lda #$FF 602 | sta PPUDATA ;Speed is 3 letters long 603 | sta PPUDATA 604 | sta PPUDATA 605 | rts 606 | @cutscene_removeTxt_lvlNb_check: 607 | cmp #$01 608 | bne @cutscene_removeTxt_singleLetter 609 | lda #$FF 610 | sta PPUDATA ;lvlNb is 2 letters long 611 | sta PPUDATA 612 | rts 613 | @cutscene_removeTxt_singleLetter: 614 | lda #$FF ;Everything else is 1-letter long 615 | sta PPUDATA 616 | rts --------------------------------------------------------------------------------