From 4f2c2aac0bdc028a050691cc84ec444dd4fe34a8 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sat, 10 Jun 2023 21:05:11 -0500 Subject: [PATCH] Wired all APU access to emulation layer. No sound yet :( --- demos/smb/Main.s | 15 +- demos/smb/apu.s | 544 +++++++++++++++++++++++++++++++++++++++++++++++ demos/smb/ppu.s | 89 +++++--- demos/smb/rom2.s | 207 +++++++++++++----- 4 files changed, 758 insertions(+), 97 deletions(-) create mode 100644 demos/smb/apu.s diff --git a/demos/smb/Main.s b/demos/smb/Main.s index a9495b9..ab8b055 100644 --- a/demos/smb/Main.s +++ b/demos/smb/Main.s @@ -81,6 +81,11 @@ FTblTmp equ 228 adc #$1FF ; Stack starts at the top of the page sta ROMStk +; Initialize the sound hardware for APU emulation + + jsr APUStartUp + +; Start up GTE to drive the graphics ; brl :debug lda #ENGINE_MODE_USER_TOOL ; Engine in Fast Mode as a User Tool @@ -192,15 +197,15 @@ EvtLoop stz nmiCount ; sep #$20 -; lda #1 +; lda #7 ; stal ROMBase+$075f ; stal ROMBase+$0766 -; lda #2 +; lda #3 ; stal ROMBase+$0763 ; stal ROMBase+$075c -; lda #$2 +; lda #3 ; stal ROMBase+$0767 ; stal ROMBase+$0760 ; rep #$30 @@ -283,6 +288,7 @@ EvtLoop Exit _GTEShutDown + jsr APUShutDown Quit _QuitGS qtRec qtRec adrl $0000 @@ -1599,4 +1605,5 @@ Area3Palette dw $0F, $00, $30, $10, $00, $16, $17, $27, $1C, $36, $1D, $00, WaterPalette dw $22, $00, $15, $12, $25, $3A, $1A, $0F, $30, $12, $27, $10, $16, $00, $16, $18 ; Palette remapping - put pal_w11.s \ No newline at end of file + put pal_w11.s + put apu.s \ No newline at end of file diff --git a/demos/smb/apu.s b/demos/smb/apu.s new file mode 100644 index 0000000..c77cc3d --- /dev/null +++ b/demos/smb/apu.s @@ -0,0 +1,544 @@ +; Init + +sound_control = $3c ;really at $e1c03c +sound_data = $3d ;really at $e1c03d +sound_address = $3e ;really at $e1c03e + +sound_interrupt_ptr = $e1002c +irq_volume = $e100ca +osc_interrupt = $e100cc + + + mx %10 + +access_doc_registers = * + ldal irq_volume + sta sound_control + rts + +access_doc_ram = * + ldal irq_volume + ora #%0110_0000 + sta sound_control + rts + +access_doc_ram_no_inc = * + ldal irq_volume + ora #%0100_0000 + sta sound_control + rts + + mx %00 + +APUStartUp + sei + phd + pea $c000 + pld + jsr copy_instruments_to_doc +; jsr setup_doc_registers + jsr setup_interrupt + pld + cli + rts + +APUShutDown = * + sei + phd + + lda #$c000 + tcd + + jsr stop_playing + + lda backup_interrupt_ptr ; restore old interrupt ptr + stal sound_interrupt_ptr + lda backup_interrupt_ptr+2 + stal sound_interrupt_ptr+2 + + cli + pld + clc + rts + +stop_playing = * + + ldy #7 ; Number of oscillators + + sep #$20 + mx %10 + + jsr access_doc_registers + + lda #$a0 ; stop all oscillators in use + sta sound_address + lda #%11 +]loop sta sound_data + inc sound_address + dey + bne ]loop + + lda #$a0+interrupt_oscillator ; stop interrupt oscillator + sta sound_address + lda #3 + sta sound_data + + rep #$20 + mx %00 + rts + +; Copy in 4 different square wave duty cycles and a triangle wave +copy_instruments_to_doc + jsr setup_docram + + lda #$0100 + jsr make_eigth_pulse + + lda #$0200 + jsr make_quarter_pulse + + lda #$0300 + jsr make_half_pulse + + lda #$0400 + jsr make_inv_quarter_pulse + + lda #$0500 + jsr copy_triangle + rts + +;-------------------------- + +setup_docram + sep #$20 + mx %10 + + jsr access_doc_ram + + stz sound_address + + lda #$80 + ldx #256 ;make sure that page 00 has nonzero data for interrupt +:loop sta sound_data + dex + bne :loop + + rep #$20 + mx %00 + rts + +;-------------------------- +make_eigth_pulse + ldy #32 + jmp make_pulse + +make_quarter_pulse + ldy #64 + jmp make_pulse + +make_half_pulse + ldy #128 + jmp make_pulse + +make_inv_quarter_pulse + ldy #192 + jmp make_pulse + +make_pulse + sep #$30 + mx %11 + + stz sound_address + xba + sta sound_address+1 + + ldx #0 + +:loop1 + lda #$01 + sta sound_data + inx + dey + bne :loop1 + +:loop2 + lda #$FF + sta sound_data + inx + bne :loop2 + + rep #$30 + mx %00 + rts + +copy_triangle + sep #$30 + mx %11 + + stz sound_address + xba + sta sound_address+1 + + ldx #0 +:loop + lda triangle_wave,x + sta sound_data + inx + bne :loop + + rep #$30 + mx %00 + rts + +;-------------------------- + +triangle_wave + hex 80828486888a8c8e90929496989a9c9e + hex a0a2a4a6a8aaacaeb0b2b4b6b8babcbe + hex c0c1c3c5c7c9cbcdcfd1d3d5d7d9dbdd + hex dfe1e3e5e7e9ebedeff1f3f5f7f9fbfd + hex fffdfbf9f7f5f3f1efedebe9e7e5e3e1 + hex dfdddbd9d7d5d3d1cfcdcbc9c7c5c3c1 + hex c0bebcbab8b6b4b2b0aeacaaa8a6a4a2 + hex a09e9c9a98969492908e8c8a88868482 + hex 807e7c7a78767472706e6c6a68666462 + hex 605e5c5a58565452504e4c4a48464442 + hex 413f3d3b39373533312f2d2b29272523 + hex 211f1d1b19171513110f0d0b09070503 + hex 01030507090b0d0f11131517191b1d1f + hex 21232527292b2d2f31333537393b3d3f + hex 41424446484a4c4e50525456585a5c5e + hex 60626466686a6c6e70727476787a7c7e + +;-------------------------- + +setup_doc_registers + sep #$20 + mx %10 + + jsr access_doc_registers + + rep #$20 + mx %00 + + rts + +;-------------------------- + +setup_interrupt = * + ldal sound_interrupt_ptr + sta backup_interrupt_ptr + ldal sound_interrupt_ptr+2 + sta backup_interrupt_ptr+2 + + lda #$5c + stal sound_interrupt_ptr + phk + phk + pla + stal sound_interrupt_ptr+2 + lda #interrupt_handler + stal sound_interrupt_ptr+1 + + sep #$20 + mx %10 + + jsr access_doc_registers + + ldy #0 +]loop lda timer_sound_settings,y ; Set DOC registers for the interrupt oscillator + sta sound_address + iny + lda timer_sound_settings,y + sta sound_data + iny + cpy #7*2 + bne ]loop + + rep #$20 + mx %00 + + rts + +interrupt_oscillator = 31 +reference_freq = 1195 ; interrupt frequence (240Hz) +timer_sound_settings = * ; set up oscillator 30 for interrupts + dfb $00+interrupt_oscillator,reference_freq ; frequency low register + dfb $20+interrupt_oscillator,reference_freq/256 ; frequency high register + dfb $40+interrupt_oscillator,0 ; volume register, volume = 0 + dfb $80+interrupt_oscillator,0 ; wavetable pointer register, point to 0 + dfb $c0+interrupt_oscillator,0 ; wavetable size register, 256 byte length + dfb $e1,$3e ; oscillator enable register + dfb $a0+interrupt_oscillator,$08 ; mode register, set to free run + +pulse1_oscillator = 0 +pulse2_oscillator = 2 +triangle_oscillator = 4 +default_freq = 16384 +pulse1_sound_settings = * + dfb $00+pulse1_oscillator,default_freq ; frequency low register + dfb $20+pulse1_oscillator,default_freq/256 ; frequency high register + dfb $40+pulse1_oscillator,0 ; volume register, volume = 0 + dfb $80+pulse1_oscillator,3 ; wavetable pointer register, point to $0300 by default (50% duty cycle) + dfb $c0+pulse1_oscillator,0 ; wavetable size register, 256 byte length + dfb $a0+pulse1_oscillator,0 ; mode register, set to free run + +pulse2_sound_settings = * + dfb $00+pulse2_oscillator,default_freq ; frequency low register + dfb $20+pulse2_oscillator,default_freq/256 ; frequency high register + dfb $40+pulse2_oscillator,0 ; volume register, volume = 0 + dfb $80+pulse2_oscillator,3 ; wavetable pointer register, point to $0300 by default (50% duty cycle) + dfb $c0+pulse2_oscillator,0 ; wavetable size register, 256 byte length + dfb $a0+pulse2_oscillator,0 ; mode register, set to free run + +triangle_sound_settings = * + dfb $00+triangle_oscillator,default_freq ; frequency low register + dfb $20+triangle_oscillator,default_freq/256 ; frequency high register + dfb $40+triangle_oscillator,0 ; volume register, volume = 0 + dfb $80+triangle_oscillator,5 ; wavetable pointer register, point to $0500 + dfb $c0+triangle_oscillator,0 ; wavetable size register, 256 byte length + dfb $a0+triangle_oscillator,0 ; mode register, set to free run + +backup_interrupt_ptr ds 4 + +;----------------------------------------------------------------------------------------- +; interupt handler +;----------------------------------------------------------------------------------------- + +interrupt_handler = * + + phb + phd + + phk + plb + + clc + xce + rep #$30 + mx %00 + + lda #$c000 + tcd + + sep #$20 + mx %10 + + jsr access_doc_registers + + ldal osc_interrupt ; which oscillator generated the interrupt? + +interrupt_handler_loop + and #%00111110 + lsr + cmp #interrupt_oscillator + bne :not_timer ; Only service timer interrupts + +; Set the parameters for the first square wave channel + + lda APU_PULSE1_REG1 ; Get the cycle duty bits + rol + rol + rol + and #$03 + tax + + lda #$80+pulse1_oscillator + sta sound_address + lda duty_cycle_page,x + sta sound_data + + lda APU_PULSE1_REG1 ; Get the volume + and #$0F + asl + asl + asl + asl + tax + lda #$40+pulse1_oscillator + sta sound_address + txa + sta sound_data + + rep #$30 + mx %00 + lda APU_PULSE1_REG3 + and #$07FF ; Load the timer value (11-bits); freq = 1.79MHz / (16 * (t - 1)) = 111860Hz / (t-1) + dec + lsr ; Divide top and bottom by 2 -- 55930 / ((t - 1)/2) + sta divisor + lda #55930 + sta dividend + + lda #0 + ldx #10 ; 10 bits of division + asl dividend +:dl1 rol + cmp divisor + bcc :dl2 + sbc divisor +:dl2 rol dividend + dex + bne :dl1 + + lda dividend + and #$3FF ; This is the NES APU frequence in hertz + sta dividend + asl + asl + adc dividend ; multiple by 5 to get the approx DOC value (0.2Hz per) + sta dividend + + sep #$20 + mx %10 + + lda #$00+pulse1_oscillator + sta sound_address + lda dividend + sta sound_data + lda #$20+pulse1_oscillator + sta sound_address + lda dividend+1 + sta sound_data + +; lda border_color +; inc +; and #$03 +; sta border_color +; jsr setborder + +:not_timer + sep #$30 + pld + plb + clc + rtl + +duty_cycle_page dfb $01,$02,$03,$04 ; Page of DOC RAM that holds the different duty cycle wavforms +border_color dw 0 +dividend dw 0 +divisor dw 0 + + +; 8-bit mode +; A = register number +; X = register value + mx %00 +SetDOCReg + stal $E0C03E + txa + stal $E0C03D + rts + +_SetDOCReg mac + lda ]1 ; Select the oscillator enable registers + ldx ]2 + jsr SetDOCReg + <<< + +; Pulse Channel 1 +APU_PULSE1 +APU_PULSE1_REG1 ds 1 ; DDLC NNNN - Duty, loop envelope/disable length counter, constant volume, envelope period/volume +APU_PULSE1_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count +APU_PULSE1_REG3 ds 1 ; LLLL LLLL - Timer Low +APU_PULSE1_REG4 ds 1 ; llll lHHH - Length counter load, timer high (also resets duty and starts envelope) + +APU_PULSE2 +APU_PULSE2_REG1 ds 1 ; DDLC NNNN - Duty, loop envelope/disable length counter, constant volume, envelope period/volume +APU_PULSE2_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count +APU_PULSE2_REG3 ds 1 ; LLLL LLLL - Timer Low +APU_PULSE2_REG4 ds 1 ; llll lHHH - Length counter load, timer high (also resets duty and starts envelope) + +APU_TRIANGLE +APU_TRIANGLE_REG1 ds 1 ; DDLC NNNN - Duty, loop envelope/disable length counter, constant volume, envelope period/volume +APU_TRIANGLE_REG2 ds 1 ; EPPP NSSS - Sweep unit: enabled, period, negative, shift count +APU_TRIANGLE_REG3 ds 1 ; LLLL LLLL - Timer Low +APU_TRIANGLE_REG4 ds 1 ; llll lHHH - Length counter load, timer high (also resets duty and starts envelope) + +APU_STATUS ds 1 + + mx %11 +APU_PULSE1_REG1_WRITE ENT + stal APU_PULSE1_REG1 + rtl + +APU_PULSE1_REG2_WRITE ENT + stal APU_PULSE1_REG2 + rtl + +APU_PULSE1_REG3_WRITE ENT + stal APU_PULSE1_REG3 + rtl + +APU_PULSE1_REG4_WRITE ENT + stal APU_PULSE1_REG4 + rtl + + +APU_PULSE2_REG1_WRITE ENT + stal APU_PULSE2_REG1 + rtl + +APU_PULSE2_REG2_WRITE ENT + stal APU_PULSE2_REG2 + rtl + +APU_PULSE2_REG3_WRITE ENT + stal APU_PULSE2_REG3 + rtl + +APU_PULSE2_REG4_WRITE ENT + stal APU_PULSE2_REG4 + rtl + + +APU_TRIANGLE_REG1_WRITE ENT + stal APU_TRIANGLE_REG1 + rtl + +APU_TRIANGLE_REG2_WRITE ENT + stal APU_TRIANGLE_REG2 + rtl + +APU_TRIANGLE_REG3_WRITE ENT + stal APU_TRIANGLE_REG3 + rtl + +APU_TRIANGLE_REG4_WRITE ENT + stal APU_TRIANGLE_REG4 + rtl + + +APU_STATUS_WRITE ENT + stal APU_STATUS + pha + +; Pulse 1 is OSC 0 + bit #$01 + beq :pulse1_off + _SetDOCReg #$40+pulse1_oscillator;#128 + bra :pulse1_end +:pulse1_off + _SetDOCReg #$40+pulse1_oscillator;#0 +:pulse1_end + +; Pulse 2 is OSC 2 + bit #$02 + beq :pulse2_off + _SetDOCReg #$40+pulse2_oscillator;#128 + bra :pulse2_end +:pulse2_off + _SetDOCReg #$40+pulse2_oscillator;#0 +:pulse2_end + +; Triangle is OSC 4 + bit #$03 + beq :triangle_off + _SetDOCReg #$40+triangle_oscillator;#128 + bra :triangle_end +:triangle_off + _SetDOCReg #$40+triangle_oscillator;#0 +:triangle_end + + pla + rtl diff --git a/demos/smb/ppu.s b/demos/smb/ppu.s index d79cea0..298012e 100644 --- a/demos/smb/ppu.s +++ b/demos/smb/ppu.s @@ -606,6 +606,7 @@ y_offset_rows equ 2 y_height_rows equ 25 y_offset equ {y_offset_rows*8} y_height equ {y_height_rows*8} +max_nes_y equ {y_height+y_offset-8} x_offset equ 16 @@ -628,7 +629,7 @@ scanOAMSprites :loop lda PPU_OAM,x ; Y-coordinate - cmp #y_height+y_offset-9 + cmp #max_nes_y+1 ; Skip anything that is beyond this lint bcs :skip cmp #y_offset bcc :skip @@ -648,38 +649,7 @@ scanOAMSprites sta OAM_COPY+2,y sep #$20 -* ; Debug OAM values -* phy -* phx - -* rep #$30 -* ldx Tmp5 -* cpx #{160*190} -* bcs :nodraw - -* lda OAM_COPY+2,y -* pha -* lda OAM_COPY,y -* ldy #$FFFF -* jsr DrawWord - -* lda Tmp5 -* clc -* adc #128+16 -* tax -* ldy #$FFFF -* pla -* jsr DrawWord - -* lda Tmp5 -* clc -* adc #8*160 -* sta Tmp5 - -* :nodraw -* sep #$30 -* plx -* ply + jsr debug_values iny iny @@ -697,6 +667,54 @@ scanOAMSprites rep #$30 rts +debug_values +; Debug APU values + phy + phx + + rep #$30 + + ldx #0 + ldy #$FFFF + lda APU_STATUS + and #$00FF + jsr DrawWord + + ldx #8*160 + ldy #$FFFF + lda APU_PULSE1_REG1 + jsr DrawWord + + ldx #16*160 + ldy #$FFFF + lda APU_PULSE1_REG3 + jsr DrawWord + + ldx #24*160 + ldy #$FFFF + lda APU_PULSE2_REG1 + jsr DrawWord + + ldx #32*160 + ldy #$FFFF + lda APU_PULSE2_REG3 + jsr DrawWord + + ldx #40*160 + ldy #$FFFF + lda APU_TRIANGLE_REG1 + jsr DrawWord + + ldx #48*160 + ldy #$FFFF + lda APU_TRIANGLE_REG3 + jsr DrawWord + + sep #$30 + plx + ply + rts + ; Screen is 200 lines tall. It's worth it be exact when building the list because one extra ; draw + shadow sequence takes at least 1,000 cycles. shadowBitmap ds 32 ; Provide enough space for the full ppu range (240 lines) + 16 since the y coordinate can be off-screen @@ -728,7 +746,8 @@ buildShadowBitmap ; ldy PPU_OAM,x ldy OAM_COPY,x - iny ; This is the y-coordinate of the top of the sprite + cpy #max_nes_y ; Don't increment something right on the edge (allows ) +; iny ; This is the y-coordinate of the top of the sprite ldx y2idx,y ; Get the index into the shadowBitmap array for this y coordinate (y -> blk_y) lda y2low,y ; Get the bit pattern for the first byte @@ -1124,7 +1143,7 @@ oam_loop phx ; Save x lda OAM_COPY,x ; Y-coordinate - inc ; Compensate for PPU delayed scanline +; inc ; Compensate for PPU delayed scanline rep #$30 and #$00FF diff --git a/demos/smb/rom2.s b/demos/smb/rom2.s index 06685e6..aff7e0e 100644 --- a/demos/smb/rom2.s +++ b/demos/smb/rom2.s @@ -682,10 +682,20 @@ PPUDATA_READ EXT PPUDATA_WRITE EXT PPUDMA_WRITE EXT -APU_PUSLE1_REG1_WRITE EXT -APU_PUSLE1_REG2_WRITE EXT -APU_PUSLE1_REG3_WRITE EXT -APU_PUSLE1_REG4_WRITE EXT +APU_PULSE1_REG1_WRITE EXT +APU_PULSE1_REG2_WRITE EXT +APU_PULSE1_REG3_WRITE EXT +APU_PULSE1_REG4_WRITE EXT +APU_PULSE2_REG1_WRITE EXT +APU_PULSE2_REG2_WRITE EXT +APU_PULSE2_REG3_WRITE EXT +APU_PULSE2_REG4_WRITE EXT +APU_TRIANGLE_REG1_WRITE EXT +APU_TRIANGLE_REG2_WRITE EXT +APU_TRIANGLE_REG3_WRITE EXT +APU_TRIANGLE_REG4_WRITE EXT + +APU_STATUS_WRITE EXT ROMBase ENT ds $7A00 @@ -720,36 +730,80 @@ APU_IND_X_REG4_W dw APU_TRIANGLE_REG4_W,APU_TRIANGLE_REG4_W APU_PULSE1_REG1_W -; jsl APU_PULSE1_REG1_WRITE + jsl APU_PULSE1_REG1_WRITE rts APU_PULSE1_REG1_WX -; jsl APU_PULSE1_REG1_WRITE + pha + txa + jsl APU_PULSE1_REG1_WRITE + pla rts APU_PULSE1_REG2_W -; jsl APU_PULSE1_REG2_WRITE + jsl APU_PULSE1_REG2_WRITE rts APU_PULSE1_REG2_WY -; jsl APU_PULSE1_REG2_WRITE + pha + tya + jsl APU_PULSE1_REG2_WRITE + pla rts APU_PULSE1_REG3_W -; jsl APU_PULSE1_REG3_WRITE + jsl APU_PULSE1_REG3_WRITE rts APU_PULSE1_REG4_W -; jsl APU_PULSE1_REG4_WRITE + jsl APU_PULSE1_REG4_WRITE rts +APU_PULSE2_REG1_W + jsl APU_PULSE2_REG1_WRITE + rts +APU_PULSE2_REG1_WX + pha + txa + jsl APU_PULSE2_REG1_WRITE + pla + rts +APU_PULSE2_REG2_W + jsl APU_PULSE2_REG2_WRITE + rts +APU_PULSE2_REG2_WY + pha + tya + jsl APU_PULSE2_REG2_WRITE + pla + rts +APU_PULSE2_REG2_WX + pha + txa + jsl APU_PULSE2_REG2_WRITE + pla + rts APU_PULSE2_REG3_W -; jsl APU_PUSLE2_REG3_WRITE + jsl APU_PULSE2_REG3_WRITE rts APU_PULSE2_REG4_W -; jsl APU_PULSE2_REG4_WRITE + jsl APU_PULSE2_REG4_WRITE rts +APU_TRIANGLE_REG1_W + jsl APU_TRIANGLE_REG1_WRITE + rts +APU_TRIANGLE_REG2_W + jsl APU_TRIANGLE_REG2_WRITE + rts APU_TRIANGLE_REG3_W -; jsl APU_TRIANGLE_REG3_WRITE + jsl APU_TRIANGLE_REG3_WRITE rts APU_TRIANGLE_REG4_W -; jsl APU_TRIANGLE_REG4_WRITE + jsl APU_TRIANGLE_REG4_WRITE + rts +APU_STATUS_W + jsl APU_STATUS_WRITE + rts +APU_STATUS_WX + phx + jsl APU_STATUS_WRITE + plx rts ; Hooks to call back to the GTE harness for PPU memory-mapped accesses mx %11 @@ -827,7 +881,8 @@ ColdBoot jsr InitializeMemory ;clear memory using pointer in Y sta WarmBootValidation sta PseudoRandomBitReg ;set seed for pseudorandom register lda #%00001111 - sta SND_MASTERCTRL_REG ;enable all sound channels except dmc +; sta SND_MASTERCTRL_REG ;enable all sound channels except dmc + jsr APU_STATUS_W lda #%00000110 jsr PPU_MASK_W ;turn off clipping for OAM and background jsr MoveAllSpritesOffscreen @@ -875,7 +930,7 @@ NonMaskableInterrupt ENT bne ScreenOff ;if set, used bits as-is lda Mirror_PPU_CTRL_REG2 ;otherwise reenable bits and save them ora #%00011110 -ScreenOff sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in register at the moment +ScreenOff sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in register at the moment and #%11100111 ;disable screen for now jsr PPU_MASK_W jsr PPU_STATUS_RX ;reset flip-flop and reset scroll registers to zero @@ -895,14 +950,14 @@ ScreenOff sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in register cpx #$06 bne InitBuffer iny ;get offset based on usage -InitBuffer ldx VRAM_Buffer_Offset,y +InitBuffer ldx VRAM_Buffer_Offset,y lda #$00 ;clear buffer header at last location sta VRAM_Buffer1_Offset,x sta VRAM_Buffer1,x sta VRAM_Buffer_AddrCtrl ;reinit address control to $0301 lda Mirror_PPU_CTRL_REG2 ;copy mirror of $2001 to register jsr PPU_MASK_W -; jsr SoundEngine ;play sound + jsr SoundEngine ;play sound jsr ReadJoypads ;read joypads jsr PauseRoutine ;handle pause jsr UpdateTopScore @@ -15573,30 +15628,34 @@ SetHFAt ora $04 ;add other OAM attributes if necessary SoundEngine lda OperMode ;are we in title screen mode? bne SndOn - sta SND_MASTERCTRL_REG ;if so, disable sound and leave +; sta SND_MASTERCTRL_REG ;if so, disable sound and leave + jsr APU_STATUS_W rts -SndOn lda #$ff +SndOn lda #$ff sta JOYPAD_PORT2 ;disable irqs and set frame counter mode??? lda #$0f - sta SND_MASTERCTRL_REG ;enable first four channels +; sta SND_MASTERCTRL_REG ;enable first four channels + jsr APU_STATUS_W lda PauseModeFlag ;is sound already in pause mode? bne InPause lda PauseSoundQueue ;if not, check pause sfx queue cmp #$01 bne RunSoundSubroutines ;if queue is empty, skip pause mode routine -InPause lda PauseSoundBuffer ;check pause sfx buffer +InPause lda PauseSoundBuffer ;check pause sfx buffer bne ContPau lda PauseSoundQueue ;check pause queue beq SkipSoundSubroutines sta PauseSoundBuffer ;if queue full, store in buffer and activate sta PauseModeFlag ;pause mode to interrupt game sounds lda #$00 ;disable sound and clear sfx buffers - sta SND_MASTERCTRL_REG +; sta SND_MASTERCTRL_REG + jsr APU_STATUS_W sta Square1SoundBuffer sta Square2SoundBuffer sta NoiseSoundBuffer lda #$0f - sta SND_MASTERCTRL_REG ;enable sound again +; sta SND_MASTERCTRL_REG ;enable sound again + jsr APU_STATUS_W lda #$2a ;store length of sound in pause counter sta Squ1_SfxLenCounter PTone1F lda #$44 ;play first tone @@ -15615,7 +15674,8 @@ PTRegC ldx #$84 DecPauC dec Squ1_SfxLenCounter ;decrement pause sfx counter bne SkipSoundSubroutines lda #$00 ;disable sound if in pause mode and - sta SND_MASTERCTRL_REG ;not currently playing the pause sfx +; sta SND_MASTERCTRL_REG ;not currently playing the pause sfx + jsr APU_STATUS_W lda PauseSoundBuffer ;if no longer playing pause sfx, check to see cmp #$02 ;if we need to be playing sound again bne SkipPIn @@ -15656,8 +15716,10 @@ StrWave sty SND_DELTA_REG+1 ;store into DMC load register (??) ;-------------------------------- Dump_Squ1_Regs - sty SND_SQUARE1_REG+1 ;dump the contents of X and Y into square 1's control regs - stx SND_SQUARE1_REG +; sty SND_SQUARE1_REG+1 ;dump the contents of X and Y into square 1's control regs +; stx SND_SQUARE1_REG + jsr APU_PULSE1_REG2_WY + jsr APU_PULSE1_REG1_WX rts PlaySqu1Sfx @@ -15670,15 +15732,19 @@ Dump_Freq_Regs tay lda FreqRegLookupTbl+1,y ;use previous contents of A for sound reg offset beq NoTone ;if zero, then do not load - sta SND_REGISTER+2,x ;first byte goes into LSB of frequency divider +; sta SND_REGISTER+2,x ;first byte goes into LSB of frequency divider + jsr APU_IND_X_REG3_W lda FreqRegLookupTbl,y ;second byte goes into 3 MSB plus extra bit for ora #%00001000 ;length counter - sta SND_REGISTER+3,x +; sta SND_REGISTER+3,x + jsr APU_IND_X_REG4_W NoTone rts Dump_Sq2_Regs - stx SND_SQUARE2_REG ;dump the contents of X and Y into square 2's control regs - sty SND_SQUARE2_REG+1 +; stx SND_SQUARE2_REG ;dump the contents of X and Y into square 2's control regs +; sty SND_SQUARE2_REG+1 + jsr APU_PULSE2_REG1_WX + jsr APU_PULSE2_REG2_WY rts PlaySqu2Sfx @@ -15752,7 +15818,8 @@ ContinueBumpThrow cmp #$06 bne DecJpFPS lda #$bb ;load second part directly - sta SND_SQUARE1_REG+1 +; sta SND_SQUARE1_REG+1 + jsr APU_PULSE1_REG2_W DecJpFPS bne BranchToDecLength1 ;unconditional branch @@ -15807,11 +15874,13 @@ PlaySwimStomp ContinueSwimStomp ldy Squ1_SfxLenCounter ;look up reg contents in data section based on lda SwimStompEnvelopeData-1,y ;length of sound left, used to control sound's - sta SND_SQUARE1_REG ;envelope +; sta SND_SQUARE1_REG ;envelope + jsr APU_PULSE1_REG1_W cpy #$06 bne BranchToDecLength1 lda #$9e ;when the length counts down to a certain point, put this - sta SND_SQUARE1_REG+2 ;directly into the LSB of square 1's frequency divider +; sta SND_SQUARE1_REG+2 ;directly into the LSB of square 1's frequency divider + jsr APU_PULSE1_REG3_W BranchToDecLength1 bne DecrementSfx1Length ;unconditional branch (regardless of how we got here) @@ -15830,11 +15899,13 @@ ContinueSmackEnemy cpy #$08 bne SmSpc lda #$a0 ;if we're at the about-halfway point, make the second tone - sta SND_SQUARE1_REG+2 ;in the smack enemy sound +; sta SND_SQUARE1_REG+2 ;in the smack enemy sound + jsr APU_PULSE1_REG3_W lda #$9f bne SmTick -SmSpc lda #$90 ;this creates spaces in the sound, giving it its distinct noise -SmTick sta SND_SQUARE1_REG +SmSpc lda #$90 ;this creates spaces in the sound, giving it its distinct noise +;SmTick sta SND_SQUARE1_REG +SmTick jsr APU_PULSE1_REG1_W DecrementSfx1Length dec Squ1_SfxLenCounter ;decrement length of sfx @@ -15844,9 +15915,11 @@ StopSquare1Sfx ldx #$00 ;if end of sfx reached, clear buffer stx $f1 ;and stop making the sfx ldx #$0e - stx SND_MASTERCTRL_REG +; stx SND_MASTERCTRL_REG + jsr APU_STATUS_WX ldx #$0f - stx SND_MASTERCTRL_REG +; stx SND_MASTERCTRL_REG + jsr APU_STATUS_WX ExSfx1 rts PlayPipeDownInj @@ -15906,7 +15979,8 @@ ContinueCGrabTTick cmp #$30 ;timer tick sound also executes this, not sure why bne N2Tone lda #$54 ;if so, load the tone directly into the reg - sta SND_SQUARE2_REG+2 +; sta SND_SQUARE2_REG+2 + jsr APU_PULSE2_REG3_W N2Tone bne DecrementSfx2Length PlayBlast @@ -15950,9 +16024,11 @@ EmptySfx2Buffer StopSquare2Sfx ldx #$0d ;stop playing the sfx - stx SND_MASTERCTRL_REG +; stx SND_MASTERCTRL_REG + jsr APU_STATUS_WX ldx #$0f - stx SND_MASTERCTRL_REG +; stx SND_MASTERCTRL_REG + jsr APU_STATUS_WX ExSfx2 rts Square2SfxHandler @@ -16047,7 +16123,8 @@ PlayGrowVine GrowItemRegs sta Squ2_SfxLenCounter lda #$7f ;load contents of reg for both sounds directly - sta SND_SQUARE2_REG+1 +; sta SND_SQUARE2_REG+1 + jsr APU_PULSE2_REG2_W lda #$00 ;start secondary counter for both sounds sta Sfx_SecondaryCounter @@ -16059,7 +16136,8 @@ ContinueGrowItems cpy Squ2_SfxLenCounter ;have we reached the end yet? beq StopGrowItems ;if so, branch to jump, and stop playing sounds lda #$9d ;load contents of other reg directly - sta SND_SQUARE2_REG +; sta SND_SQUARE2_REG + jsr APU_PULSE2_REG1_W lda PUp_VGrow_FreqData,y ;use secondary counter / 2 as offset for frequency regs jsr SetFreq_Squ2 rts @@ -16216,9 +16294,11 @@ LoadHeader sta MusicOffset_Square2 sta AltRegContentFlag ;initialize alternate control reg data used by square 1 lda #$0b ;disable triangle channel and reenable it - sta SND_MASTERCTRL_REG +; sta SND_MASTERCTRL_REG + jsr APU_STATUS_W lda #$0f - sta SND_MASTERCTRL_REG +; sta SND_MASTERCTRL_REG + jsr APU_STATUS_W HandleSquare2Music dec Squ2_NoteLenCounter ;decrement square 2 note length @@ -16244,10 +16324,13 @@ NotTRO and #VictoryMusic ;check for victory music (the only secondary tha lda #$00 ;clear primary and secondary buffers and initialize sta AreaMusicBuffer ;control regs of square and triangle channels sta EventMusicBuffer - sta SND_TRIANGLE_REG +; sta SND_TRIANGLE_REG + jsr APU_TRIANGLE_REG1_W lda #$90 - sta SND_SQUARE1_REG - sta SND_SQUARE2_REG +; sta SND_SQUARE1_REG +; sta SND_SQUARE2_REG + jsr APU_PULSE1_REG1_W + jsr APU_PULSE2_REG1_W rts MusicLoopBack @@ -16284,9 +16367,11 @@ MiscSqu2MusicTasks beq NoDecEnv1 dec Squ2_EnvelopeDataCtrl ;decrement unless already zero NoDecEnv1 jsr LoadEnvelopeData ;do a load of envelope data to replace default - sta SND_SQUARE2_REG ;based on offset set by first load unless playing +; sta SND_SQUARE2_REG ;based on offset set by first load unless playing + jsr APU_PULSE2_REG1_W ldx #$7f ;death music or d4 set on secondary buffer - stx SND_SQUARE2_REG+1 +; stx SND_SQUARE2_REG+1 + jsr APU_PULSE2_REG2_WX HandleSquare1Music ldy MusicOffset_Square1 ;is there a nonzero offset here? @@ -16300,9 +16385,11 @@ FetchSqu1MusicData lda (MusicData),y bne Squ1NoteHandler ;if nonzero, then skip this part lda #$83 - sta SND_SQUARE1_REG ;store some data into control regs for square 1 +; sta SND_SQUARE1_REG ;store some data into control regs for square 1 + jsr APU_PULSE1_REG1_W lda #$94 ;and fetch another byte of data, used to give - sta SND_SQUARE1_REG+1 ;death music its unique sound +; sta SND_SQUARE1_REG+1 ;death music its unique sound + jsr APU_PULSE1_REG2_W sta AltRegContentFlag bne FetchSqu1MusicData ;unconditional branch @@ -16329,11 +16416,13 @@ MiscSqu1MusicTasks beq NoDecEnv2 dec Squ1_EnvelopeDataCtrl ;decrement unless already zero NoDecEnv2 jsr LoadEnvelopeData ;do a load of envelope data - sta SND_SQUARE1_REG ;based on offset set by first load +; sta SND_SQUARE1_REG ;based on offset set by first load + jsr APU_PULSE1_REG1_W DeathMAltReg lda AltRegContentFlag ;check for alternate control reg data bne DoAltLoad lda #$7f ;load this value if zero, the alternate value -DoAltLoad sta SND_SQUARE1_REG+1 ;if nonzero, and let's move on +;DoAltLoad sta SND_SQUARE1_REG+1 ;if nonzero, and let's move on +DoAltLoad jsr APU_PULSE1_REG2_W HandleTriangleMusic lda MusicOffset_Triangle @@ -16347,7 +16436,8 @@ HandleTriangleMusic jsr ProcessLengthData ;otherwise, it is length data sta Tri_NoteLenBuffer ;save contents of A lda #$1f - sta SND_TRIANGLE_REG ;load some default data for triangle control reg +; sta SND_TRIANGLE_REG ;load some default data for triangle control reg + jsr APU_TRIANGLE_REG1_W ldy MusicOffset_Triangle ;fetch another byte inc MusicOffset_Triangle lda (MusicData),y @@ -16375,8 +16465,9 @@ MediN lda #$1f ;secondary besides death and d4 except win cast bne LoadTriCtrlReg ;a short note, and load value $ff if playing a long note on water, castle LongN lda #$ff ;or any secondary (including win castle) except death and d4 -LoadTriCtrlReg - sta SND_TRIANGLE_REG ;save final contents of A into control reg for triangle +LoadTriCtrlReg +; sta SND_TRIANGLE_REG ;save final contents of A into control reg for triangle + jsr APU_TRIANGLE_REG1_W HandleNoiseMusic lda AreaMusicBuffer ;check if playing underground or castle music