IF YOU WANT TO GET THE MOST OUT OF THE DRIVER I AM ABOUT TO RELEASE HERE, THEN PLEASE READ THIS ENTIRE POST BEFORE USING/RESPONDING. ANYONE WHO RESPONDS BECAUSE THEY DIDN'T READ, I SHALL NOT RESPOND TO. What is it? Dual PCM is a Z80 sound driver for the SEGA Mega Drive, it can play any two PCM samples, of any size, at any single time, through the DAC channel. It comes complete with Jester Stream Technologyâ„¢ allowing it to play the samples at the least damaging quality possible. Here is a test ROM of Sonic 1, with Dual PCM, Download. This one has a few edits to the original songs, Green Hill Zone has a set of piano chords during the chorus, as does Labyrinth Zone, Scrap Brain Zone has it's Timpani drums seperated onto the second channel, and Spring Yard Zone and Invincible have a PCM bass guitar now. This is just an example of what you can do with this driver. Requirements: Dual PCM simply requires the 68k (Main CPU) to give it the sample information in order for it to play the sample(s), Dual PCM also requires the 68k to tell it when it's about to request VDP DMA transfers, and when it's finished. If you intend on playing FM/PSG sounds, this MUST be handled by the 68k, so if you have a sound driver that plays back tracker data (for example SMPS), then it must be a 68k variant, Dual PCM operates on its own and requires all of the Z80's resources. It is a puppet, and the 68k is the puppeteer. Installation Please use this as a "guide" and nothing more, it doesn't have to be used with 68k SMPS, it can be used with any 68k code that can operate it properly. This guide below will simply assume you're using Sonic 1 and its 68k SMPS variant, I'll be using the 2005 Hivebrain disassembly (known as "Sonic 1 (Split and Text by Hivebrain) (ASM68K)" on this page), nothing against other disassembly variants, I just know this one best. You must handle the splitting and normal setup of the disassembly yourself, however, I will place a download link to a disassembly of the changes below made already, for your conveniece. Step 01 - Replacing the Z80 driver Sonic 1 has a Z80 DPCM playback driver, we must first rip this out, and then replace it with the new driver. >> 1. Inside the "sound" folder, you'll have "z80_1.bin" and "z80_2.bin", you won't need these, so delete them. >> 2. Next, open up "sonic1.asm", and find this: Spoiler Code (Text): Kos_Z80: incbin sound\z80_1.bin dc.w ((SegaPCM&$FF)<<8)+((SegaPCM&$FF00)>>8) dc.b $21 dc.w (((EndOfRom-SegaPCM)&$FF)<<8)+(((EndOfRom-SegaPCM)&$FF00)>>8) incbin sound\z80_2.bin even Replace it with: Spoiler Code (Text): Z80ROM: incbin "Dual PCM\Z80.bin" Z80ROM_End: even >> 3. Find this: Spoiler Code (Text): SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen nop move.w #$100,($A11100).l ; stop the Z80 move.w #$100,($A11200).l ; reset the Z80 lea (Kos_Z80).l,a0 ; load sound driver lea ($A00000).l,a1 bsr.w KosDec ; decompress move.w #0,($A11200).l nop nop nop nop move.w #$100,($A11200).l ; reset the Z80 move.w #0,($A11100).l ; start the Z80 rts ; End of function SoundDriverLoad Replace it with: Spoiler Code (Text): SoundDriverLoad: lea (Z80ROM).l,a0 ; load Z80 ROM data lea ($A00000).l,a1 ; load Z80 RAM space address move.w #(Z80ROM_End-Z80ROM)-$01,d1 ; set repeat times move.w #$0100,($A11100).l ; request Z80 stop (ON) move.w #$0100,($A11200).l ; request Z80 reset (OFF) btst.b #$00,($A11100).l ; has the Z80 stopped yet? bne.s *-$08 ; if not, branch SM_LoadZ80: move.b (a0)+,(a1)+ ; dump Z80 data to Z80 space dbf d1,SM_LoadZ80 ; repeat til done lea (StopSample).l,a0 ; load stop/mute sample address lea ($A00000+MuteSample).l,a1 ; load Z80 RAM space where the pointer is to be stored move.b (a0)+,(a1)+ ; copy pointer over into Z80 move.b (a0)+,(a1)+ ; '' move.b (a0)+,(a1)+ ; '' move.w #$0000,($A11200).l ; request Z80 reset (ON) moveq #$7F,d1 ; set repeat times dbf d1,* ; there's no way of checking for reset, so a manual delay is necessary move.w #$0000,($A11100).l ; request Z80 stop (OFF) move.w #$0100,($A11200).l ; request Z80 reset (OFF) rts ; return >> 4. Make a new folder at the root of your disassembly, and name it "Dual PCM": >> 5. Download this new Z80 sound driver source code, and place it in the "Dual PCM" folder. Step 02 - Replacing the assembly tools We need an assembler for the Z80, as well as some tools to help assemble/convert the new driver >> 1. Delete "build.bat", "ASM68K.EXE", "fixheadr.exe", and "rompad.exe". >> 2. Download this folder of tools and extract it at the root of your disassembly: Download (32-Bit) Download (64-Bit) >> 3. Download this new batch file and place it at the root of your disassembly: Step 03 - Editing the 68k Sound Driver The sound driver is still handling the old Z80 driver, as well as only handling one DAC channel, and a few unoptimal things (this one is quite lengthy...) >> 1. Find: Spoiler Code (Text): sub_71B4C: ; XREF: loc_B10; PalToCRAM move.w #$100,($A11100).l ; stop the Z80 nop nop nop loc_71B5A: btst #0,($A11100).l bne.s loc_71B5A btst #7,($A01FFD).l beq.s loc_71B82 move.w #0,($A11100).l ; start the Z80 nop nop nop nop nop bra.s sub_71B4C ; =========================================================================== loc_71B82: lea ($FFF000).l,a6 And replace with: Spoiler Code (Text): YM_Access_WaitRead: StartZ80 ; EXT: request Z80 stop off (allow it to continue) rept $10 ; EXT: delay for a long enough time to allow the... nop ; EXT: '' ...68k pointer to be saved correctly. endr ; EXT: '' sub_71B4C: lea ($FFF000).l,a6 lea ($A00000+YM_Access).l,a0 ; EXT: load access address in Z80 move.l #$A00000,d0 ; EXT: prepare Z80 RAM address in d0 (i.e. start of Cue list address) StopZ80 ; EXT: request Z80 stop on tst.b (a0)+ ; EXT: is the Z80 accessing the 68k pointer? bne.s YM_Access_WaitRead ; EXT: if so, branch and wait for it to finish... move.b (a0)+,d1 ; EXT: load lower byte of pointer move.b (a0)+,d0 ; EXT: load upper byte of pointer StartZ80 ; EXT: request Z80 stop off lsl.w #$08,d0 ; EXT: shift upper byte up move.b d1,d0 ; EXT: put lower byte with it move.l d0,$10(a6) ; EXT: store the cue address >> 2. Find: Spoiler Code (Text): loc_71BC8: lea $40(a6),a5 tst.b (a5) bpl.s loc_71BD4 jsr sub_71C4E(pc) loc_71BD4: And replace with: Spoiler Code (Text): loc_71BC8: lea $40-$30(a6),a5 ; MJ: making correction for flow below moveq #2-1,d7 ; MJ: set number of PCM channels to run move.b #$80-1,$08(a6) ; MJ: reset as PCM channel SD_NextPCM: addq.b #$01,$08(a6) ; MJ: advance PCM channel ID lea $30(a5),a5 ; MJ: advance to next channel tst.b (a5) bpl.s loc_71BD4 jsr sub_71C4E(pc) loc_71BD4: dbf d7,SD_NextPCM ; MJ: repeat for number of PCM channels available >> 3. Goto "loc_71C44:" and replace this: Spoiler Code (Text): move.w #0,($A11100).l ; start the Z80 With this: Spoiler Code (Text): bra.s YM_Access_TestWrite ; EXT: jump into the access loop YM_Access_WaitWrite: StartZ80 ; EXT: request Z80 stop off (allow it to continue) rept $10 ; EXT: delay for a long enough time to let the Z80... nop ; EXT: '' ...finish writing the 68k pointer, so it doesn't... endr ; EXT: '' ...clash with 68k's pointer writing. YM_Access_TestWrite: lea ($A00000+YM_Access).l,a0 ; EXT: load access address in Z80 lea $13(a6),a1 ; EXT: load the 68k's pointer finish location StopZ80 ; EXT: request Z80 stop on tst.b (a0)+ ; EXT: is the Z80 accessing the 68k pointer? bne.s YM_Access_WaitWrite ; EXT: if so, branch and wait for it to finish... move.b (a1),(a0)+ ; EXT: save lower byte of pointer move.b -(a1),(a0) ; EXT: save upper byte of pointer StartZ80 ; EXT: request Z80 stop off Spoiler Code (Text): move.w #0,($A11100).l ; start the Z80 >> 4. Goto "sub_71C4E:" and replace this: Spoiler Code (Text): sub_71C4E: ; XREF: sub_71B4C subq.b #1,$E(a5) bne.s locret_71CAA move.b #$80,8(a6) movea.l 4(a5),a4 With this: Spoiler Code (Text): sub_71C4E: ; XREF: sub_71B4C subq.b #1,$E(a5) bne.w locret_71CAA movea.l 4(a5),a4 (Note the branch size change) >> 5. Find this: Spoiler Code (Text): loc_71C88: move.l a4,4(a5) btst #2,(a5) bne.s locret_71CAA moveq #0,d0 move.b $10(a5),d0 cmpi.b #$80,d0 beq.s locret_71CAA btst #3,d0 bne.s loc_71CAC move.b d0,($A01FFF).l locret_71CAA: rts ; =========================================================================== loc_71CAC: subi.b #$88,d0 move.b byte_71CC4(pc,d0.w),d0 move.b d0,($A000EA).l move.b #$83,($A01FFF).l rts ; End of function sub_71C4E ; =========================================================================== byte_71CC4: dc.b $12, $15, $1C, $1D, $FF, $FF ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| And replace with: Spoiler Code (Text): loc_71C88: move.l a4,4(a5) btst #2,(a5) bne.s locret_71CAA moveq #0,d0 move.b $10(a5),d0 subi.b #$80,d0 ; MJ: minus 80 add.w d0,d0 ; MJ: multiply by 4 (long-word size) add.w d0,d0 ; MJ: '' movem.l a0/a1,-(sp) ; MJ: store register data lea (SampleList).l,a0 ; MJ: load sample list move.l (a0,d0.w),a0 ; MJ: load correct sample z80 pointer address cmpi.b #$80,$08(a6) ; MJ: is this PCM 1? bne.s WritePCM2 ; MJ: if not, branch for PCM 2 writing ; --- Writing to PCM 1 --- StopZ80 lea ($A00000+PCM1_Sample).l,a1 ; MJ: load PCM 1 slot address move.b (a0)+,(a1)+ ; MJ: set address of sample move.b (a0)+,(a1)+ ; MJ: '' move.b (a0)+,(a1)+ ; MJ: '' move.b #(CUPCM1_NewSample&$FF),($A00000+CU_Stack).l ; MJ: set routine to run move.b #(CUPCM1_NewSample>>$08),($A00000+CU_Stack+1).l ; MJ: '' move.b #%11001001,($A00000+CUPCM1_RET).l ; MJ: change "NOP" to "RET" StartZ80 movem.l (sp)+,a0/a1 ; MJ: restore register data locret_71CAA: rts ; MJ: return ; --- Writing to PCM 2 --- WritePCM2: StopZ80 lea ($A00000+PCM2_Sample).l,a1 ; MJ: load PCM 2 slot address move.b (a0)+,(a1)+ ; MJ: '' move.b (a0)+,(a1)+ ; MJ: '' move.b (a0)+,(a1)+ ; MJ: '' move.b #%00101000,($A00000+CUPCM2_RET).l ; change "JR NZ" to "JR Z" StartZ80 movem.l (sp)+,a0/a1 ; MJ: restore register data rts ; MJ: return ; =========================================================================== >> 6. Find this: Spoiler Code (Text): loc_71E94: ; XREF: loc_71E50 clr.b 3(a6) moveq #$30,d3 lea $40(a6),a5 moveq #6,d4 loc_71EA0: btst #7,(a5) beq.s loc_71EB8 btst #2,(a5) bne.s loc_71EB8 move.b #-$4C,d0 move.b $A(a5),d1 jsr sub_72722(pc) loc_71EB8: adda.w d3,a5 dbf d4,loc_71EA0 lea $220(a6),a5 moveq #2,d4 loc_71EC4: btst #7,(a5) beq.s loc_71EDC btst #2,(a5) bne.s loc_71EDC move.b #-$4C,d0 move.b $A(a5),d1 jsr sub_72722(pc) loc_71EDC: adda.w d3,a5 dbf d4,loc_71EC4 lea $340(a6),a5 btst #7,(a5) beq.s loc_71EFE btst #2,(a5) bne.s loc_71EFE move.b #-$4C,d0 move.b $A(a5),d1 jsr sub_72722(pc) loc_71EFE: bra.w loc_71C44 And replace with: Spoiler Code (Text): loc_71E94: ; XREF: loc_71E50 clr.b 3(a6) moveq #$30,d3 lea $40(a6),a5 moveq #7,d4 ; MJ: number of YM2612 based channels loc_71EA0: btst #7,(a5) beq.s loc_71EB8 btst #2,(a5) bne.s loc_71EB8 move.b #-$4C,d0 move.b $A(a5),d1 jsr sub_72722(pc) loc_71EB8: adda.w d3,a5 dbf d4,loc_71EA0 lea $250(a6),a5 ; MJ: new SFX location moveq #2,d4 loc_71EC4: btst #7,(a5) beq.s loc_71EDC btst #2,(a5) bne.s loc_71EDC move.b #-$4C,d0 move.b $A(a5),d1 jsr sub_72722(pc) loc_71EDC: adda.w d3,a5 dbf d4,loc_71EC4 lea $370(a6),a5 ; MJ: new SFX location btst #7,(a5) beq.s loc_71EFE btst #2,(a5) bne.s loc_71EFE move.b #-$4C,d0 move.b $A(a5),d1 jsr sub_72722(pc) loc_71EFE: bra.w loc_71C44 >> 7. Find this: Spoiler Code (Text): ; =========================================================================== ; --------------------------------------------------------------------------- ; Play "Say-gaa" PCM sound ; --------------------------------------------------------------------------- Sound_E1: ; XREF: Sound_ExIndex move.b #$88,($A01FFF).l move.w #0,($A11100).l ; start the Z80 move.w #$11,d1 loc_71FC0: move.w #-1,d0 loc_71FC4: nop dbf d0,loc_71FC4 dbf d1,loc_71FC0 addq.w #4,sp rts ; =========================================================================== And replace with:: Spoiler Code (Text): ; =========================================================================== ; --------------------------------------------------------------------------- ; Play "Say-gaa" PCM sound ; --------------------------------------------------------------------------- Sound_E1: StopZ80 ; MJ: request Z80 stop "ON" lea (SegaPCM).l,a2 ; MJ: load sample address lea ($A04000).l,a3 ; MJ: load YM2612 port move.b #$2A,(a3)+ ; MJ: set YM2612 address to the PCM data port move.l #(SegaPCM_End-SegaPCM)-$01,d4 ; MJ: prepare size move.w d4,d3 ; MJ: get lower word size swap d4 ; MJ: get upper word size PlayPCM_Loop: move.b (a2)+,(a3) ; MJ: save sample data to port moveq #$2B,d0 ; MJ: set delay time dbf d0,* ; MJ: delay... dbf d3,PlayPCM_Loop ; MJ: repeat til done dbf d4,PlayPCM_Loop ; MJ: '' move.b #$80,(a3) ; MJ: save mute data to port addq.w #$04,sp ; MJ: skip return address subq.w #$01,a3 ; MJ: move back to address port tst.b (a3) ; MJ: is the YM2612 busy? bmi.s *-$02 ; MJ: if so, branch and recheck move.b #$2A,(a3) ; MJ: write address (set it back to DAC port for the Z80) StartZ80 ; MJ: request Z80 stop "OFF" rts ; MJ: return ; =========================================================================== >> 8. Find all of this: Spoiler Code (Text): ; =========================================================================== ; --------------------------------------------------------------------------- ; Play music track $81-$9F ; --------------------------------------------------------------------------- Sound_81to9F: ; XREF: Sound_ChkValue cmpi.b #$88,d7 ; is "extra life" music played? bne.s loc_72024 ; if not, branch tst.b $27(a6) bne.w loc_721B6 lea $40(a6),a5 moveq #9,d0 loc_71FE6: bclr #2,(a5) adda.w #$30,a5 dbf d0,loc_71FE6 lea $220(a6),a5 moveq #5,d0 loc_71FF8: bclr #7,(a5) adda.w #$30,a5 dbf d0,loc_71FF8 clr.b 0(a6) movea.l a6,a0 lea $3A0(a6),a1 move.w #$87,d0 loc_72012: move.l (a0)+,(a1)+ dbf d0,loc_72012 move.b #$80,$27(a6) clr.b 0(a6) bra.s loc_7202C ; =========================================================================== loc_72024: clr.b $27(a6) clr.b $26(a6) loc_7202C: jsr sub_725CA(pc) movea.l (off_719A0).l,a4 subi.b #$81,d7 move.b (a4,d7.w),$29(a6) movea.l (Go_MusicIndex).l,a4 lsl.w #2,d7 movea.l (a4,d7.w),a4 moveq #0,d0 move.w (a4),d0 add.l a4,d0 move.l d0,$18(a6) move.b 5(a4),d0 move.b d0,$28(a6) tst.b $2A(a6) beq.s loc_72068 move.b $29(a6),d0 loc_72068: move.b d0,2(a6) move.b d0,1(a6) moveq #0,d1 movea.l a4,a3 addq.w #6,a4 moveq #0,d7 move.b 2(a3),d7 beq.w loc_72114 subq.b #1,d7 move.b #-$40,d1 move.b 4(a3),d4 moveq #$30,d6 move.b #1,d5 lea $40(a6),a1 lea byte_721BA(pc),a2 loc_72098: bset #7,(a1) move.b (a2)+,1(a1) move.b d4,2(a1) move.b d6,$D(a1) move.b d1,$A(a1) move.b d5,$E(a1) moveq #0,d0 move.w (a4)+,d0 add.l a3,d0 move.l d0,4(a1) move.w (a4)+,8(a1) adda.w d6,a1 dbf d7,loc_72098 cmpi.b #7,2(a3) bne.s loc_720D8 moveq #$2B,d0 moveq #0,d1 jsr sub_7272E(pc) bra.w loc_72114 ; =========================================================================== loc_720D8: moveq #$28,d0 moveq #6,d1 jsr sub_7272E(pc) move.b #$42,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #$4A,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #$46,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #$4E,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #-$4A,d0 move.b #-$40,d1 jsr sub_72764(pc) loc_72114: moveq #0,d7 move.b 3(a3),d7 beq.s loc_72154 subq.b #1,d7 lea $190(a6),a1 lea byte_721C2(pc),a2 loc_72126: bset #7,(a1) move.b (a2)+,1(a1) move.b d4,2(a1) move.b d6,$D(a1) move.b d5,$E(a1) moveq #0,d0 move.w (a4)+,d0 add.l a3,d0 move.l d0,4(a1) move.w (a4)+,8(a1) move.b (a4)+,d0 move.b (a4)+,$B(a1) adda.w d6,a1 dbf d7,loc_72126 loc_72154: lea $220(a6),a1 moveq #5,d7 loc_7215A: tst.b (a1) bpl.w loc_7217C moveq #0,d0 move.b 1(a1),d0 bmi.s loc_7216E subq.b #2,d0 lsl.b #2,d0 bra.s loc_72170 ; =========================================================================== loc_7216E: lsr.b #3,d0 loc_72170: lea dword_722CC(pc),a0 movea.l (a0,d0.w),a0 bset #2,(a0) loc_7217C: adda.w d6,a1 dbf d7,loc_7215A tst.w $340(a6) bpl.s loc_7218E bset #2,$100(a6) loc_7218E: tst.w $370(a6) bpl.s loc_7219A bset #2,$1F0(a6) loc_7219A: lea $70(a6),a5 moveq #5,d4 loc_721A0: jsr sub_726FE(pc) adda.w d6,a5 dbf d4,loc_721A0 moveq #2,d4 loc_721AC: jsr sub_729A0(pc) adda.w d6,a5 dbf d4,loc_721AC loc_721B6: addq.w #4,sp rts ; =========================================================================== byte_721BA: dc.b 6, 0, 1, 2, 4, 5, 6, 0 even byte_721C2: dc.b $80, $A0, $C0, 0 even ; =========================================================================== ; --------------------------------------------------------------------------- ; Play normal sound effect ; --------------------------------------------------------------------------- Sound_A0toCF: ; XREF: Sound_ChkValue tst.b $27(a6) bne.w loc_722C6 tst.b 4(a6) bne.w loc_722C6 tst.b $24(a6) bne.w loc_722C6 cmpi.b #$B5,d7 ; is ring sound effect played? bne.s Sound_notB5 ; if not, branch tst.b $2B(a6) bne.s loc_721EE move.b #$CE,d7 ; play ring sound in left speaker loc_721EE: bchg #0,$2B(a6) ; change speaker Sound_notB5: cmpi.b #$A7,d7 ; is "pushing" sound played? bne.s Sound_notA7 ; if not, branch tst.b $2C(a6) bne.w locret_722C4 move.b #$80,$2C(a6) Sound_notA7: movea.l (Go_SoundIndex).l,a0 subi.b #$A0,d7 lsl.w #2,d7 movea.l (a0,d7.w),a3 movea.l a3,a1 moveq #0,d1 move.w (a1)+,d1 add.l a3,d1 move.b (a1)+,d5 move.b (a1)+,d7 subq.b #1,d7 moveq #$30,d6 loc_72228: moveq #0,d3 move.b 1(a1),d3 move.b d3,d4 bmi.s loc_72244 subq.w #2,d3 lsl.w #2,d3 lea dword_722CC(pc),a5 movea.l (a5,d3.w),a5 bset #2,(a5) bra.s loc_7226E ; =========================================================================== loc_72244: lsr.w #3,d3 lea dword_722CC(pc),a5 movea.l (a5,d3.w),a5 bset #2,(a5) cmpi.b #$C0,d4 bne.s loc_7226E move.b d4,d0 ori.b #$1F,d0 move.b d0,($C00011).l bchg #5,d0 move.b d0,($C00011).l loc_7226E: movea.l dword_722EC(pc,d3.w),a5 movea.l a5,a2 moveq #$B,d0 loc_72276: clr.l (a2)+ dbf d0,loc_72276 move.w (a1)+,(a5) move.b d5,2(a5) moveq #0,d0 move.w (a1)+,d0 add.l a3,d0 move.l d0,4(a5) move.w (a1)+,8(a5) move.b #1,$E(a5) move.b d6,$D(a5) tst.b d4 bmi.s loc_722A8 move.b #$C0,$A(a5) move.l d1,$20(a5) loc_722A8: dbf d7,loc_72228 tst.b $250(a6) bpl.s loc_722B8 bset #2,$340(a6) loc_722B8: tst.b $310(a6) bpl.s locret_722C4 bset #2,$370(a6) locret_722C4: rts ; =========================================================================== loc_722C6: clr.b 0(a6) rts ; =========================================================================== dword_722CC: dc.l $FFF0D0 dc.l 0 dc.l $FFF100 dc.l $FFF130 dc.l $FFF190 dc.l $FFF1C0 dc.l $FFF1F0 dc.l $FFF1F0 dword_722EC: dc.l $FFF220 dc.l 0 dc.l $FFF250 dc.l $FFF280 dc.l $FFF2B0 dc.l $FFF2E0 dc.l $FFF310 dc.l $FFF310 ; =========================================================================== ; --------------------------------------------------------------------------- ; Play GHZ waterfall sound ; --------------------------------------------------------------------------- Sound_D0toDF: ; XREF: Sound_ChkValue tst.b $27(a6) bne.w locret_723C6 tst.b 4(a6) bne.w locret_723C6 tst.b $24(a6) bne.w locret_723C6 movea.l (Go_SoundD0).l,a0 subi.b #$D0,d7 lsl.w #2,d7 movea.l (a0,d7.w),a3 movea.l a3,a1 moveq #0,d0 move.w (a1)+,d0 add.l a3,d0 move.l d0,$20(a6) move.b (a1)+,d5 move.b (a1)+,d7 subq.b #1,d7 moveq #$30,d6 loc_72348: move.b 1(a1),d4 bmi.s loc_7235A bset #2,$100(a6) lea $340(a6),a5 bra.s loc_72364 ; =========================================================================== loc_7235A: bset #2,$1F0(a6) lea $370(a6),a5 loc_72364: movea.l a5,a2 moveq #$B,d0 loc_72368: clr.l (a2)+ dbf d0,loc_72368 move.w (a1)+,(a5) move.b d5,2(a5) moveq #0,d0 move.w (a1)+,d0 add.l a3,d0 move.l d0,4(a5) move.w (a1)+,8(a5) move.b #1,$E(a5) move.b d6,$D(a5) tst.b d4 bmi.s loc_72396 move.b #$C0,$A(a5) loc_72396: dbf d7,loc_72348 tst.b $250(a6) bpl.s loc_723A6 bset #2,$340(a6) loc_723A6: tst.b $310(a6) bpl.s locret_723C6 bset #2,$370(a6) ori.b #$1F,d4 move.b d4,($C00011).l bchg #5,d4 move.b d4,($C00011).l locret_723C6: rts ; End of function Sound_ChkValue ; =========================================================================== dc.l $FFF100 dc.l $FFF1F0 dc.l $FFF250 dc.l $FFF310 dc.l $FFF340 dc.l $FFF370 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Snd_FadeOut1: ; XREF: Sound_E0 clr.b 0(a6) lea $220(a6),a5 moveq #5,d7 loc_723EA: tst.b (a5) bpl.w loc_72472 bclr #7,(a5) moveq #0,d3 move.b 1(a5),d3 bmi.s loc_7243C jsr sub_726FE(pc) cmpi.b #4,d3 bne.s loc_72416 tst.b $340(a6) bpl.s loc_72416 lea $340(a6),a5 movea.l $20(a6),a1 bra.s loc_72428 ; =========================================================================== loc_72416: subq.b #2,d3 lsl.b #2,d3 lea dword_722CC(pc),a0 movea.l a5,a3 movea.l (a0,d3.w),a5 movea.l $18(a6),a1 loc_72428: bclr #2,(a5) bset #1,(a5) move.b $B(a5),d0 jsr sub_72C4E(pc) movea.l a3,a5 bra.s loc_72472 ; =========================================================================== loc_7243C: jsr sub_729A0(pc) lea $370(a6),a0 cmpi.b #$E0,d3 beq.s loc_7245A cmpi.b #$C0,d3 beq.s loc_7245A lsr.b #3,d3 lea dword_722CC(pc),a0 movea.l (a0,d3.w),a0 loc_7245A: bclr #2,(a0) bset #1,(a0) cmpi.b #$E0,1(a0) bne.s loc_72472 move.b $1F(a0),($C00011).l loc_72472: adda.w #$30,a5 dbf d7,loc_723EA rts ; End of function Snd_FadeOut1 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Snd_FadeOut2: ; XREF: Sound_E0 lea $340(a6),a5 tst.b (a5) bpl.s loc_724AE bclr #7,(a5) btst #2,(a5) bne.s loc_724AE jsr loc_7270A(pc) lea $100(a6),a5 bclr #2,(a5) bset #1,(a5) tst.b (a5) bpl.s loc_724AE movea.l $18(a6),a1 move.b $B(a5),d0 jsr sub_72C4E(pc) loc_724AE: lea $370(a6),a5 tst.b (a5) bpl.s locret_724E4 bclr #7,(a5) btst #2,(a5) bne.s locret_724E4 jsr loc_729A6(pc) lea $1F0(a6),a5 bclr #2,(a5) bset #1,(a5) tst.b (a5) bpl.s locret_724E4 cmpi.b #-$20,1(a5) bne.s locret_724E4 move.b $1F(a5),($C00011).l locret_724E4: rts ; End of function Snd_FadeOut2 ; =========================================================================== ; --------------------------------------------------------------------------- ; Fade out music ; --------------------------------------------------------------------------- Sound_E0: ; XREF: Sound_ExIndex jsr Snd_FadeOut1(pc) jsr Snd_FadeOut2(pc) move.b #3,6(a6) move.b #$28,4(a6) clr.b $40(a6) clr.b $2A(a6) rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_72504: ; XREF: sub_71B4C move.b 6(a6),d0 beq.s loc_72510 subq.b #1,6(a6) rts ; =========================================================================== loc_72510: subq.b #1,4(a6) beq.w Sound_E4 move.b #3,6(a6) lea $70(a6),a5 moveq #5,d7 loc_72524: tst.b (a5) bpl.s loc_72538 addq.b #1,9(a5) bpl.s loc_72534 bclr #7,(a5) bra.s loc_72538 ; =========================================================================== loc_72534: jsr sub_72CB4(pc) loc_72538: adda.w #$30,a5 dbf d7,loc_72524 moveq #2,d7 loc_72542: tst.b (a5) bpl.s loc_72560 addq.b #1,9(a5) cmpi.b #$10,9(a5) bcs.s loc_72558 bclr #7,(a5) bra.s loc_72560 ; =========================================================================== loc_72558: move.b 9(a5),d6 jsr sub_7296A(pc) loc_72560: adda.w #$30,a5 dbf d7,loc_72542 rts ; End of function sub_72504 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7256A: ; XREF: Sound_E4; sub_725CA moveq #2,d3 moveq #$28,d0 loc_7256E: move.b d3,d1 jsr sub_7272E(pc) addq.b #4,d1 jsr sub_7272E(pc) dbf d3,loc_7256E moveq #$40,d0 moveq #$7F,d1 moveq #2,d4 loc_72584: moveq #3,d3 loc_72586: jsr sub_7272E(pc) jsr sub_72764(pc) addq.w #4,d0 dbf d3,loc_72586 subi.b #$F,d0 dbf d4,loc_72584 rts ; End of function sub_7256A ; =========================================================================== ; --------------------------------------------------------------------------- ; Stop music ; --------------------------------------------------------------------------- Sound_E4: ; XREF: Sound_ChkValue; Sound_ExIndex; sub_72504 moveq #$2B,d0 move.b #$80,d1 jsr sub_7272E(pc) moveq #$27,d0 moveq #0,d1 jsr sub_7272E(pc) movea.l a6,a0 move.w #$E3,d0 loc_725B6: clr.l (a0)+ dbf d0,loc_725B6 move.b #$80,9(a6) ; set music to $80 (silence) jsr sub_7256A(pc) bra.w sub_729B6 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_725CA: ; XREF: Sound_ChkValue movea.l a6,a0 move.b 0(a6),d1 move.b $27(a6),d2 move.b $2A(a6),d3 move.b $26(a6),d4 move.w $A(a6),d5 move.w #$87,d0 loc_725E4: clr.l (a0)+ dbf d0,loc_725E4 move.b d1,0(a6) move.b d2,$27(a6) move.b d3,$2A(a6) move.b d4,$26(a6) move.w d5,$A(a6) move.b #$80,9(a6) jsr sub_7256A(pc) bra.w sub_729B6 ; End of function sub_725CA ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7260C: ; XREF: sub_71B4C move.b 2(a6),1(a6) lea $4E(a6),a0 moveq #$30,d0 moveq #9,d1 loc_7261A: addq.b #1,(a0) adda.w d0,a0 dbf d1,loc_7261A rts ; End of function sub_7260C ; =========================================================================== ; --------------------------------------------------------------------------- ; Speed up music ; --------------------------------------------------------------------------- Sound_E2: ; XREF: Sound_ExIndex tst.b $27(a6) bne.s loc_7263E move.b $29(a6),2(a6) move.b $29(a6),1(a6) move.b #$80,$2A(a6) rts ; =========================================================================== loc_7263E: move.b $3C9(a6),$3A2(a6) move.b $3C9(a6),$3A1(a6) move.b #$80,$3CA(a6) rts ; =========================================================================== ; --------------------------------------------------------------------------- ; Change music back to normal speed ; --------------------------------------------------------------------------- Sound_E3: ; XREF: Sound_ExIndex tst.b $27(a6) bne.s loc_7266A move.b $28(a6),2(a6) move.b $28(a6),1(a6) clr.b $2A(a6) rts ; =========================================================================== loc_7266A: move.b $3C8(a6),$3A2(a6) move.b $3C8(a6),$3A1(a6) clr.b $3CA(a6) rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7267C: ; XREF: sub_71B4C tst.b $25(a6) beq.s loc_72688 subq.b #1,$25(a6) rts ; =========================================================================== loc_72688: tst.b $26(a6) beq.s loc_726D6 subq.b #1,$26(a6) move.b #2,$25(a6) lea $70(a6),a5 moveq #5,d7 loc_7269E: tst.b (a5) bpl.s loc_726AA subq.b #1,9(a5) jsr sub_72CB4(pc) loc_726AA: adda.w #$30,a5 dbf d7,loc_7269E moveq #2,d7 loc_726B4: tst.b (a5) bpl.s loc_726CC subq.b #1,9(a5) move.b 9(a5),d6 cmpi.b #$10,d6 bcs.s loc_726C8 moveq #$F,d6 loc_726C8: jsr sub_7296A(pc) loc_726CC: adda.w #$30,a5 dbf d7,loc_726B4 rts ; =========================================================================== loc_726D6: bclr #2,$40(a6) clr.b $24(a6) rts ; End of function sub_7267C ; =========================================================================== loc_726E2: ; XREF: sub_71CCA btst #1,(a5) bne.s locret_726FC btst #2,(a5) bne.s locret_726FC moveq #$28,d0 move.b 1(a5),d1 ori.b #-$10,d1 bra.w sub_7272E ; =========================================================================== locret_726FC: rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_726FE: ; XREF: sub_71CEC; sub_71D9E; Sound_ChkValue; Snd_FadeOut1 btst #4,(a5) bne.s locret_72714 btst #2,(a5) bne.s locret_72714 loc_7270A: ; XREF: Snd_FadeOut2 moveq #$28,d0 move.b 1(a5),d1 bra.w sub_7272E ; =========================================================================== locret_72714: rts ; End of function sub_726FE ; =========================================================================== loc_72716: ; XREF: sub_72A5A btst #2,(a5) bne.s locret_72720 bra.w sub_72722 ; =========================================================================== locret_72720: rts And replace it all with: Spoiler Code (Text): ; =========================================================================== ; --------------------------------------------------------------------------- ; Play music track $81-$9F ; --------------------------------------------------------------------------- Sound_81to9F: ; XREF: Sound_ChkValue StopZ80 lea (StopSample).l,a0 ; MJ: load stop sample address lea ($A00000+PCM1_Sample).l,a1 ; MJ: load PCM 1 slot address move.b (a0)+,(a1)+ ; MJ: set address of sample move.b (a0)+,(a1)+ ; MJ: '' move.b (a0)+,(a1)+ ; MJ: '' move.b #(CUPCM1_NewSample&$FF),($A00000+CU_Stack).l ; MJ: set routine to run move.b #(CUPCM1_NewSample>>$08),($A00000+CU_Stack+1).l ; MJ: '' move.b #%11001001,($A00000+CUPCM1_RET).l ; MJ: change "NOP" to "RET" lea (StopSample).l,a0 ; MJ: load stop sample address lea ($A00000+PCM2_Sample).l,a1 ; MJ: load PCM 2 slot address move.b (a0)+,(a1)+ ; MJ: '' move.b (a0)+,(a1)+ ; MJ: '' move.b (a0)+,(a1)+ ; MJ: '' move.b #%00101000,($A00000+CUPCM2_RET).l ; MJ: change "JR NZ" to "JR Z" StartZ80 cmpi.b #$88,d7 ; is "extra life" music played? bne.s loc_72024 ; if not, branch tst.b $27(a6) bne.w loc_721B6 lea $40(a6),a5 moveq #10,d0 ; MJ: number of channels in total loc_71FE6: bclr #2,(a5) adda.w #$30,a5 dbf d0,loc_71FE6 lea $250(a6),a5 ; MJ: new SFX location moveq #5,d0 loc_71FF8: bclr #7,(a5) adda.w #$30,a5 dbf d0,loc_71FF8 clr.b 0(a6) movea.l a6,a0 lea $3D0(a6),a1 ; MJ: new SFX location move.w #$87,d0 loc_72012: move.l (a0)+,(a1)+ dbf d0,loc_72012 move.b #$80,$27(a6) clr.b 0(a6) bra.s loc_7202C ; =========================================================================== loc_72024: clr.b $27(a6) clr.b $26(a6) loc_7202C: jsr sub_725CA(pc) movea.l (off_719A0).l,a4 subi.b #$81,d7 move.b (a4,d7.w),$29(a6) movea.l (Go_MusicIndex).l,a4 lsl.w #2,d7 movea.l (a4,d7.w),a4 moveq #0,d0 move.w (a4),d0 add.l a4,d0 move.l d0,$18(a6) move.b 5(a4),d0 move.b d0,$28(a6) tst.b $2A(a6) beq.s loc_72068 move.b $29(a6),d0 loc_72068: move.b d0,2(a6) move.b d0,1(a6) moveq #0,d1 movea.l a4,a3 addq.w #6,a4 moveq #0,d7 move.b 2(a3),d7 beq.w loc_72114 subq.b #1,d7 move.b #-$40,d1 move.b 4(a3),d4 moveq #$30,d6 move.b #1,d5 lea $40(a6),a1 lea byte_721BA(pc),a2 loc_72098: bset #7,(a1) move.b (a2)+,1(a1) move.b d4,2(a1) move.b d6,$D(a1) move.b d1,$A(a1) move.b d5,$E(a1) moveq #0,d0 move.w d0,$10(a1) ; MJ: clear FM's frequency (ensures no frequency writing) move.w (a4)+,d0 add.l a3,d0 move.l d0,4(a1) move.w (a4)+,8(a1) adda.w d6,a1 dbf d7,loc_72098 moveq #$2B,d0 ; MJ: set YM2612 address to DAC/FM6 switch move.b #%10000000,d1 ; MJ: set to turn DAC on cmpi.b #8,2(a3) ; MJ: changed to 8 (8 = 6FM channels, no DAC) bne.s loc_720D8 ; moveq #$2B,d0 ; MJ: removed... moveq #0,d1 jsr sub_7272E(pc) bra.w loc_72114 ; =========================================================================== loc_720D8: jsr sub_7272E(pc) ; MJ: added... (turn DAC on) moveq #$28,d0 moveq #6,d1 jsr sub_7272E(pc) move.b #$42,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #$4A,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #$46,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #$4E,d0 moveq #$7F,d1 jsr sub_72764(pc) move.b #-$4A,d0 move.b #-$40,d1 jsr sub_72764(pc) loc_72114: moveq #$02,d5 ; EXT: set PSG to delay for 1 extra frame (This is to match the PSG with the FM/DAC which is delayed a frame by the Z80) moveq #0,d7 move.b 3(a3),d7 beq.s loc_72154 subq.b #1,d7 lea $1C0(a6),a1 ; MJ: new BGM/SFX location lea byte_721C2(pc),a2 loc_72126: bset #7,(a1) move.b (a2)+,1(a1) move.b d4,2(a1) move.b d6,$D(a1) move.b d5,$E(a1) move.w #$FFFF,$10(a1) ; MJ: clear PSG's frequency (ensures no frequency writing) move.b #$01,$12(a1) ; MJ: set key release rate to 1 moveq #0,d0 move.w (a4)+,d0 add.l a3,d0 move.l d0,4(a1) move.w (a4)+,8(a1) move.b (a4)+,d0 move.b (a4)+,$B(a1) adda.w d6,a1 dbf d7,loc_72126 loc_72154: lea $250(a6),a1 ; MJ: new SFX location moveq #5,d7 loc_7215A: tst.b (a1) bpl.w loc_7217C moveq #0,d0 move.b 1(a1),d0 bmi.s loc_7216E subq.b #2,d0 lsl.b #2,d0 bra.s loc_72170 ; =========================================================================== loc_7216E: lsr.b #3,d0 loc_72170: lea dword_722CC(pc),a0 movea.l (a0,d0.w),a0 bset #2,(a0) loc_7217C: adda.w d6,a1 dbf d7,loc_7215A tst.w $370(a6) ; MJ: new SFX location bpl.s loc_7218E bset #2,$130(a6) ; MJ: new BGM location loc_7218E: tst.w $3A0(a6) ; MJ: new SFX location bpl.s loc_7219A bset #2,$220(a6) ; MJ: new BGM location loc_7219A: lea $A0(a6),a5 ; MJ: new FM location moveq #5,d4 loc_721A0: jsr sub_726FE(pc) adda.w d6,a5 dbf d4,loc_721A0 moveq #2,d4 loc_721AC: jsr sub_729A0(pc) adda.w d6,a5 dbf d4,loc_721AC loc_721B6: addq.w #4,sp rts ; =========================================================================== byte_721BA: dc.b 6, 6, 0, 1, 2, 4, 5, 6, 0 ; MJ: extra 6 (for PCM 2) even byte_721C2: dc.b $80, $A0, $C0, 0 even ; =========================================================================== ; --------------------------------------------------------------------------- ; Play normal sound effect ; --------------------------------------------------------------------------- Sound_A0toCF: ; XREF: Sound_ChkValue tst.b $27(a6) bne.w loc_722C6 tst.b 4(a6) bne.w loc_722C6 tst.b $24(a6) bne.w loc_722C6 cmpi.b #$B5,d7 ; is ring sound effect played? bne.s Sound_notB5 ; if not, branch tst.b $2B(a6) bne.s loc_721EE move.b #$CE,d7 ; play ring sound in left speaker loc_721EE: bchg #0,$2B(a6) ; change speaker Sound_notB5: cmpi.b #$A7,d7 ; is "pushing" sound played? bne.s Sound_notA7 ; if not, branch tst.b $2C(a6) bne.w locret_722C4 move.b #$80,$2C(a6) Sound_notA7: movea.l (Go_SoundIndex).l,a0 subi.b #$A0,d7 lsl.w #2,d7 movea.l (a0,d7.w),a3 movea.l a3,a1 moveq #0,d1 move.w (a1)+,d1 add.l a3,d1 move.b (a1)+,d5 move.b (a1)+,d7 subq.b #1,d7 moveq #$30,d6 loc_72228: moveq #0,d3 move.b 1(a1),d3 moveq #$02,d2 ; EXT: set PSG to delay for 1 extra frame (This is to match the PSG with the FM/DAC which is delayed a frame by the Z80) move.b d3,d4 bmi.s loc_72244 move.b #$01,d2 ; EXT: set DAC/FM to delay for 0 frames like normal (these have an auto delay of 1 frame in the Z80) subq.w #2,d3 lsl.w #2,d3 lea dword_722CC(pc),a5 movea.l (a5,d3.w),a5 bset #2,(a5) bra.s loc_7226E ; =========================================================================== loc_72244: lsr.w #3,d3 lea dword_722CC(pc),a5 movea.l (a5,d3.w),a5 bset #2,(a5) cmpi.b #$C0,d4 bne.s loc_7226E move.b d4,d0 ori.b #$1F,d0 move.b d0,($C00011).l bchg #5,d0 move.b d0,($C00011).l loc_7226E: movea.l dword_722EC(pc,d3.w),a5 movea.l a5,a2 moveq #$B,d0 loc_72276: clr.l (a2)+ dbf d0,loc_72276 move.w (a1)+,(a5) move.b d5,2(a5) moveq #0,d0 move.w (a1)+,d0 add.l a3,d0 move.l d0,4(a5) move.w (a1)+,8(a5) move.b d2,$E(a5) ; EXT: moving d2 contents (1 for FM/4 for PSG) move.b d6,$D(a5) tst.b d4 bmi.s loc_722A8 move.b #$C0,$A(a5) move.l d1,$20(a5) loc_722A8: dbf d7,loc_72228 tst.b $280(a6) ; MJ: new SFX location bpl.s loc_722B8 bset #2,$370(a6) ; MJ: new SFX location loc_722B8: tst.b $340(a6) ; MJ: new SFX location bpl.s locret_722C4 bset #2,$3A0(a6) ; MJ: new SFX location locret_722C4: rts ; =========================================================================== loc_722C6: clr.b 0(a6) rts ; =========================================================================== dword_722CC: dc.l $FFF0D0+$30 ; MJ: new locations (see all +$30) dc.l 0 dc.l $FFF100+$30 dc.l $FFF130+$30 dc.l $FFF190+$30 dc.l $FFF1C0+$30 dc.l $FFF1F0+$30 dc.l $FFF1F0+$30 dword_722EC: dc.l $FFF220+$30 dc.l 0 dc.l $FFF250+$30 dc.l $FFF280+$30 dc.l $FFF2B0+$30 dc.l $FFF2E0+$30 dc.l $FFF310+$30 dc.l $FFF310+$30 ; =========================================================================== ; --------------------------------------------------------------------------- ; Play GHZ waterfall sound ; --------------------------------------------------------------------------- Sound_D0toDF: ; XREF: Sound_ChkValue tst.b $27(a6) bne.w locret_723C6 tst.b 4(a6) bne.w locret_723C6 tst.b $24(a6) bne.w locret_723C6 movea.l (Go_SoundD0).l,a0 subi.b #$D0,d7 lsl.w #2,d7 movea.l (a0,d7.w),a3 movea.l a3,a1 moveq #0,d0 move.w (a1)+,d0 add.l a3,d0 move.l d0,$20(a6) move.b (a1)+,d5 move.b (a1)+,d7 subq.b #1,d7 moveq #$30,d6 loc_72348: move.b 1(a1),d4 bmi.s loc_7235A bset #2,$130(a6) ; MJ: new BGM location lea $370(a6),a5 ; MJ: new SFX location bra.s loc_72364 ; =========================================================================== loc_7235A: bset #2,$220(a6) ; MJ: new BGM location lea $3A0(a6),a5 ; MJ: new SFX location loc_72364: movea.l a5,a2 moveq #$B,d0 loc_72368: clr.l (a2)+ dbf d0,loc_72368 move.w (a1)+,(a5) move.b d5,2(a5) moveq #0,d0 move.w (a1)+,d0 add.l a3,d0 move.l d0,4(a5) move.w (a1)+,8(a5) move.b #1,$E(a5) move.b d6,$D(a5) tst.b d4 bmi.s loc_72396 move.b #$C0,$A(a5) loc_72396: dbf d7,loc_72348 tst.b $280(a6) ; MJ: new SFX location bpl.s loc_723A6 bset #2,$370(a6) ; MJ: new SFX location loc_723A6: tst.b $340(a6) ; MJ: new SFX location bpl.s locret_723C6 bset #2,$3A0(a6) ; MJ: new SFX location ori.b #$1F,d4 move.b d4,($C00011).l bchg #5,d4 move.b d4,($C00011).l locret_723C6: rts ; End of function Sound_ChkValue ; =========================================================================== dc.l $FFF100+$30 ; MJ: new channel locations (see +$30) dc.l $FFF1F0+$30 dc.l $FFF250+$30 dc.l $FFF310+$30 dc.l $FFF340+$30 dc.l $FFF370+$30 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Snd_FadeOut1: ; XREF: Sound_E0 clr.b 0(a6) lea $250(a6),a5 ; MJ: new SFX location moveq #5,d7 loc_723EA: tst.b (a5) bpl.w loc_72472 bclr #7,(a5) moveq #0,d3 move.b 1(a5),d3 bmi.s loc_7243C jsr sub_726FE(pc) cmpi.b #4,d3 bne.s loc_72416 tst.b $370(a6) ; MJ: new SFX location bpl.s loc_72416 lea $370(a6),a5 ; MJ: new SFX location movea.l $20(a6),a1 bra.s loc_72428 ; =========================================================================== loc_72416: subq.b #2,d3 lsl.b #2,d3 lea dword_722CC(pc),a0 movea.l a5,a3 movea.l (a0,d3.w),a5 movea.l $18(a6),a1 loc_72428: bclr #2,(a5) bset #1,(a5) move.b $B(a5),d0 jsr sub_72C4E(pc) movea.l a3,a5 bra.s loc_72472 ; =========================================================================== loc_7243C: jsr sub_729A0(pc) lea $3A0(a6),a0 ; MJ: new SFX location cmpi.b #$E0,d3 beq.s loc_7245A cmpi.b #$C0,d3 beq.s loc_7245A lsr.b #3,d3 lea dword_722CC(pc),a0 movea.l (a0,d3.w),a0 loc_7245A: bclr #2,(a0) bset #1,(a0) cmpi.b #$E0,1(a0) bne.s loc_72472 move.b $1F(a0),($C00011).l loc_72472: adda.w #$30,a5 dbf d7,loc_723EA rts ; End of function Snd_FadeOut1 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Snd_FadeOut2: ; XREF: Sound_E0 lea $370(a6),a5 ; MJ: new SFX location tst.b (a5) bpl.s loc_724AE bclr #7,(a5) btst #2,(a5) bne.s loc_724AE jsr loc_7270A(pc) lea $130(a6),a5 ; MJ: new BGM location bclr #2,(a5) bset #1,(a5) tst.b (a5) bpl.s loc_724AE movea.l $18(a6),a1 move.b $B(a5),d0 jsr sub_72C4E(pc) loc_724AE: lea $3A0(a6),a5 ; MJ: new SFX location tst.b (a5) bpl.s locret_724E4 bclr #7,(a5) btst #2,(a5) bne.s locret_724E4 jsr loc_729A6(pc) lea $220(a6),a5 ; MJ: new BGM location bclr #2,(a5) bset #1,(a5) tst.b (a5) bpl.s locret_724E4 cmpi.b #-$20,1(a5) bne.s locret_724E4 move.b $1F(a5),($C00011).l locret_724E4: rts ; End of function Snd_FadeOut2 ; =========================================================================== ; --------------------------------------------------------------------------- ; Fade out music ; --------------------------------------------------------------------------- Sound_E0: ; XREF: Sound_ExIndex jsr Snd_FadeOut1(pc) jsr Snd_FadeOut2(pc) move.b #3,6(a6) move.b #$28,4(a6) clr.b $40(a6) clr.b $70(a6) ; MJ: stop PCM 2 as well clr.b $2A(a6) rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_72504: ; XREF: sub_71B4C move.b 6(a6),d0 beq.s loc_72510 subq.b #1,6(a6) rts ; =========================================================================== loc_72510: subq.b #1,4(a6) beq.w Sound_E4 move.b #3,6(a6) lea $A0(a6),a5 ; MJ: new FM location moveq #5,d7 loc_72524: tst.b (a5) bpl.s loc_72538 addq.b #1,9(a5) bpl.s loc_72534 bclr #7,(a5) bra.s loc_72538 ; =========================================================================== loc_72534: jsr sub_72CB4(pc) loc_72538: adda.w #$30,a5 dbf d7,loc_72524 moveq #2,d7 loc_72542: tst.b (a5) bpl.s loc_72560 addq.b #1,9(a5) cmpi.b #$10,9(a5) bcs.s loc_72558 bclr #7,(a5) bra.s loc_72560 ; =========================================================================== loc_72558: move.b 9(a5),d6 jsr sub_7296A(pc) loc_72560: adda.w #$30,a5 dbf d7,loc_72542 rts ; End of function sub_72504 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7256A: ; XREF: Sound_E4; sub_725CA moveq #2,d3 moveq #$28,d0 loc_7256E: move.b d3,d1 jsr sub_7272E(pc) addq.b #4,d1 jsr sub_7272E(pc) dbf d3,loc_7256E moveq #$40,d0 moveq #$7F,d1 moveq #2,d4 loc_72584: moveq #3,d3 loc_72586: jsr sub_7272E(pc) jsr sub_72764(pc) addq.w #4,d0 dbf d3,loc_72586 subi.b #$F,d0 dbf d4,loc_72584 rts ; End of function sub_7256A ; =========================================================================== ; --------------------------------------------------------------------------- ; Stop music ; --------------------------------------------------------------------------- Sound_E4: StopZ80 lea (StopSample).l,a0 ; EXT: load stop sample address lea ($A00000+PCM1_Sample).l,a1 ; EXT: load PCM 1 slot address move.b (a0)+,(a1)+ ; EXT: set address of sample move.b (a0)+,(a1)+ ; EXT: '' move.b (a0)+,(a1)+ ; EXT: '' move.b #(CUPCM1_NewSample&$FF),($A00000+CU_Stack).l ; EXT: set routine to run move.b #(CUPCM1_NewSample>>$08),($A00000+CU_Stack+1).l ; EXT: '' move.b #%11001001,($A00000+CUPCM1_RET).l ; EXT: change "NOP" to "RET" lea (StopSample).l,a0 ; EXT: load stop sample address lea ($A00000+PCM2_Sample).l,a1 ; EXT: load PCM 2 slot address move.b (a0)+,(a1)+ ; EXT: '' move.b (a0)+,(a1)+ ; EXT: '' move.b (a0)+,(a1)+ ; EXT: '' move.b #%00101000,($A00000+CUPCM2_RET).l ; EXT: change "JR NZ" to "JR Z" StartZ80 moveq #$2B,d0 move.b #$80,d1 jsr sub_7272E(pc) moveq #$27,d0 moveq #0,d1 jsr sub_7272E(pc) movea.l a6,a0 move.l $10(a6),d6 ; EXT: store YM Cue list pointer move.w #$EF,d0 ; MJ: new size of data to clear loc_725B6: clr.l (a0)+ dbf d0,loc_725B6 move.l d6,$10(a6) ; EXT: restore YM Cue list pointer move.b #$80,9(a6) ; set music to $80 (silence) jsr sub_7256A(pc) bra.w sub_729B6 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_725CA: ; XREF: Sound_ChkValue movea.l a6,a0 move.b 0(a6),d1 move.b $27(a6),d2 move.b $2A(a6),d3 move.b $26(a6),d4 move.w $A(a6),d5 move.l $10(a6),d6 ; EXT: store YM Cue list pointer move.w #$93,d0 ; MJ: new size loc_725E4: clr.l (a0)+ dbf d0,loc_725E4 move.b d1,0(a6) move.b d2,$27(a6) move.b d3,$2A(a6) move.b d4,$26(a6) move.w d5,$A(a6) move.l d6,$10(a6) ; EXT: restore YM Cue list pointer move.b #$80,9(a6) jsr sub_7256A(pc) bra.w sub_729B6 ; End of function sub_725CA ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7260C: ; XREF: sub_71B4C move.b 2(a6),1(a6) lea $4E(a6),a0 moveq #$30,d0 moveq #10,d1 ; MJ: new number of channels loc_7261A: addq.b #1,(a0) adda.w d0,a0 dbf d1,loc_7261A rts ; End of function sub_7260C ; =========================================================================== ; --------------------------------------------------------------------------- ; Speed up music ; --------------------------------------------------------------------------- Sound_E2: ; XREF: Sound_ExIndex tst.b $27(a6) bne.s loc_7263E move.b $29(a6),2(a6) move.b $29(a6),1(a6) move.b #$80,$2A(a6) rts ; =========================================================================== loc_7263E: move.b $3F9(a6),$3D2(a6) ; MJ: new location move.b $3F9(a6),$3D1(a6) ; MJ: new location move.b #$80,$3FA(a6) rts ; =========================================================================== ; --------------------------------------------------------------------------- ; Change music back to normal speed ; --------------------------------------------------------------------------- Sound_E3: ; XREF: Sound_ExIndex tst.b $27(a6) bne.s loc_7266A move.b $28(a6),2(a6) move.b $28(a6),1(a6) clr.b $2A(a6) rts ; =========================================================================== loc_7266A: move.b $3F8(a6),$3D2(a6) move.b $3F8(a6),$3D1(a6) clr.b $3FA(a6) rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7267C: ; XREF: sub_71B4C tst.b $25(a6) beq.s loc_72688 subq.b #1,$25(a6) rts ; =========================================================================== loc_72688: tst.b $26(a6) beq.s loc_726D6 subq.b #1,$26(a6) move.b #2,$25(a6) lea $A0(a6),a5 ; MJ: new SFX location moveq #5,d7 loc_7269E: tst.b (a5) bpl.s loc_726AA subq.b #1,9(a5) jsr sub_72CB4(pc) loc_726AA: adda.w #$30,a5 dbf d7,loc_7269E moveq #2,d7 loc_726B4: tst.b (a5) bpl.s loc_726CC subq.b #1,9(a5) move.b 9(a5),d6 cmpi.b #$10,d6 bcs.s loc_726C8 moveq #$F,d6 loc_726C8: jsr sub_7296A(pc) loc_726CC: adda.w #$30,a5 dbf d7,loc_726B4 rts ; =========================================================================== loc_726D6: bclr #2,$40(a6) bclr #2,$70(a6) ; MJ: do PCM 2 as well... clr.b $24(a6) rts ; End of function sub_7267C ; =========================================================================== loc_726E2: ; XREF: sub_71CCA btst #1,(a5) bne.s locret_726FC btst #2,(a5) bne.s locret_726FC moveq #$28,d0 move.b 1(a5),d1 ori.b #-$10,d1 bra.w sub_7272E ; =========================================================================== locret_726FC: rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_726FE: ; XREF: sub_71CEC; sub_71D9E; Sound_ChkValue; Snd_FadeOut1 btst #4,(a5) bne.s locret_72714 btst #2,(a5) bne.s locret_72714 loc_7270A: ; XREF: Snd_FadeOut2 moveq #$28,d0 move.b 1(a5),d1 bra.w sub_7272E ; =========================================================================== locret_72714: rts ; End of function sub_726FE ; =========================================================================== loc_72716: ; XREF: sub_72A5A btst #2,(a5) bne.s locret_72720 bra.w sub_72722 ; =========================================================================== locret_72720: rts >> 9. Next the YM2612 writing subroutine, find this: Spoiler Code (Text): sub_72722: ; XREF: sub_71E18; sub_72C4E; sub_72CB4 btst #2,1(a5) bne.s loc_7275A add.b 1(a5),d0 ; End of function sub_72722 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7272E: ; XREF: loc_71E6A move.b ($A04000).l,d2 btst #7,d2 bne.s sub_7272E move.b d0,($A04000).l nop nop nop loc_72746: move.b ($A04000).l,d2 btst #7,d2 bne.s loc_72746 move.b d1,($A04001).l rts ; End of function sub_7272E ; =========================================================================== loc_7275A: ; XREF: sub_72722 move.b 1(a5),d2 bclr #2,d2 add.b d2,d0 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_72764: ; XREF: loc_71E6A; Sound_ChkValue; sub_7256A; sub_72764 move.b ($A04000).l,d2 btst #7,d2 bne.s sub_72764 move.b d0,($A04002).l nop nop nop loc_7277C: move.b ($A04000).l,d2 btst #7,d2 bne.s loc_7277C move.b d1,($A04003).l rts ; End of function sub_72764 And replace it with this newer, more optimised version (this should help reduce the Z80 waiting time): Spoiler Code (Text): sub_72722: ; XREF: sub_71E18; sub_72C4E; sub_72CB4 btst #2,1(a5) bne.s loc_7275A add.b 1(a5),d0 ; End of function sub_72722 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_7272E: movem.l d2/a0,-(sp) ; EXT: store register data movea.l $10(a6),a0 ; EXT: load Cue pointer addq.w #$01,a0 ; EXT: skip $40 move.b #$00,d2 ; EXT: prepare d2 for YM2612 port address ($4000 - $4001) StopZ80 ; EXT: request Z80 stop "ON" move.b d2,(a0)+ ; EXT: write YM2612 port address move.b d1,(a0)+ ; EXT: write YM2612 data move.b d0,(a0)+ ; EXT: write YM2612 address StartZ80 ; EXT: request Z80 stop "OFF" move.w a0,d2 ; EXT: load Cue pointer andi.w #$0FFF,d2 ; EXT: wrap it ori.w #$1000,d2 ; EXT: '' move.w d2,$12(a6) ; EXT: update it movem.l (sp)+,d2/a0 ; EXT: restore register data rts ; EXT: return ; =========================================================================== loc_7275A: ; XREF: sub_72722 move.b 1(a5),d2 bclr #2,d2 add.b d2,d0 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_72764: movem.l d2/a0,-(sp) ; EXT: store register data movea.l $10(a6),a0 ; EXT: load Cue pointer addq.w #$01,a0 ; EXT: skip $40 move.b #$02,d2 ; EXT: prepare d2 for YM2612 port address ($4002 - $4003) StopZ80 ; EXT: request Z80 stop "ON" move.b d2,(a0)+ ; EXT: write YM2612 port address move.b d1,(a0)+ ; EXT: write YM2612 data move.b d0,(a0)+ ; EXT: write YM2612 address StartZ80 ; EXT: request Z80 stop "OFF" move.w a0,d2 ; EXT: load Cue pointer andi.w #$0FFF,d2 ; EXT: wrap it ori.w #$1000,d2 ; EXT: '' move.w d2,$12(a6) ; EXT: update it movem.l (sp)+,d2/a0 ; EXT: restore register data rts ; EXT: return >> 10. Find this: Spoiler Code (Text): loc_72B14: ; XREF: loc_72A64 movea.l a6,a0 lea $3A0(a6),a1 move.w #$87,d0 loc_72B1E: move.l (a1)+,(a0)+ dbf d0,loc_72B1E bset #2,$40(a6) movea.l a5,a3 move.b #$28,d6 sub.b $26(a6),d6 moveq #5,d7 lea $70(a6),a5 And replace with: Spoiler Code (Text): loc_72B14: ; XREF: loc_72A64 movea.l a6,a0 lea $3D0(a6),a1 ; MJ: new SFX location move.w #$93,d0 ; MJ: new size to store loc_72B1E: move.l (a1)+,(a0)+ dbf d0,loc_72B1E bset #2,$40(a6) bset #2,$70(a6) ; MJ: enable PCM 2 movea.l a5,a3 move.b #$28,d6 sub.b $26(a6),d6 moveq #5,d7 lea $A0(a6),a5 ; MJ: new FM location >> 11. Goto "loc_72B78:" and delete this line: Spoiler Code (Text): move.w #0,($A11100).l >> 12. Find this: Spoiler Code (Text): loc_72BD0: ; XREF: loc_72A64 lea $40(a6),a0 move.b (a4)+,d0 moveq #$30,d1 moveq #9,d2 loc_72BDA: move.b d0,2(a0) adda.w d1,a0 dbf d2,loc_72BDA rts ; =========================================================================== loc_72BE6: ; XREF: loc_72A64 move.b (a4)+,d0 add.b d0,9(a5) rts ; =========================================================================== loc_72BEE: ; XREF: loc_72A64 clr.b $2C(a6) rts ; =========================================================================== loc_72BF4: ; XREF: loc_72A64 bclr #7,(a5) bclr #4,(a5) jsr sub_726FE(pc) tst.b $250(a6) bmi.s loc_72C22 movea.l a5,a3 lea $100(a6),a5 movea.l $18(a6),a1 bclr #2,(a5) bset #1,(a5) move.b $B(a5),d0 jsr sub_72C4E(pc) movea.l a3,a5 loc_72C22: addq.w #8,sp rts And replace with: Spoiler Code (Text): loc_72BD0: ; XREF: loc_72A64 lea $40(a6),a0 move.b (a4)+,d0 moveq #$30,d1 moveq #10,d2 ; MJ: extra channel loc_72BDA: move.b d0,2(a0) adda.w d1,a0 dbf d2,loc_72BDA rts ; =========================================================================== loc_72BE6: ; XREF: loc_72A64 move.b (a4)+,d0 add.b d0,9(a5) rts ; =========================================================================== loc_72BEE: ; XREF: loc_72A64 clr.b $2C(a6) rts ; =========================================================================== loc_72BF4: ; XREF: loc_72A64 bclr #7,(a5) bclr #4,(a5) jsr sub_726FE(pc) tst.b $280(a6) ; MJ: new SFX location bmi.s loc_72C22 movea.l a5,a3 lea $130(a6),a5 ; MJ: new BGM location movea.l $18(a6),a1 bclr #2,(a5) bset #1,(a5) move.b $B(a5),d0 jsr sub_72C4E(pc) movea.l a3,a5 loc_72C22: addq.w #8,sp rts >> 13. Find this: Spoiler Code (Text): loc_72D78: tst.b $E(a6) bpl.w loc_72E02 clr.b 0(a6) moveq #0,d0 move.b 1(a5),d0 bmi.s loc_72DCC lea dword_722CC(pc),a0 movea.l a5,a3 cmpi.b #4,d0 bne.s loc_72DA8 tst.b $340(a6) bpl.s loc_72DA8 lea $340(a6),a5 movea.l $20(a6),a1 bra.s loc_72DB8 ; =========================================================================== loc_72DA8: subq.b #2,d0 lsl.b #2,d0 movea.l (a0,d0.w),a5 tst.b (a5) bpl.s loc_72DC8 movea.l $18(a6),a1 loc_72DB8: bclr #2,(a5) bset #1,(a5) move.b $B(a5),d0 jsr sub_72C4E(pc) loc_72DC8: movea.l a3,a5 bra.s loc_72E02 ; =========================================================================== loc_72DCC: lea $370(a6),a0 tst.b (a0) bpl.s loc_72DE0 cmpi.b #$E0,d0 beq.s loc_72DEA cmpi.b #$C0,d0 beq.s loc_72DEA loc_72DE0: lea dword_722CC(pc),a0 lsr.b #3,d0 movea.l (a0,d0.w),a0 loc_72DEA: bclr #2,(a0) bset #1,(a0) cmpi.b #$E0,1(a0) bne.s loc_72E02 move.b $1F(a0),($C00011).l loc_72E02: addq.w #8,sp rts ; =========================================================================== loc_72E06: ; XREF: loc_72A64 move.b #$E0,1(a5) move.b (a4)+,$1F(a5) btst #2,(a5) bne.s locret_72E1E move.b -1(a4),($C00011).l locret_72E1E: rts And replace with: Spoiler Code (Text): loc_72D78: tst.b $E(a6) bpl.w loc_72E02 clr.b 0(a6) moveq #0,d0 move.b 1(a5),d0 bmi.s loc_72DCC lea dword_722CC(pc),a0 movea.l a5,a3 cmpi.b #4,d0 bne.s loc_72DA8 tst.b $370(a6) ; MJ: new SFX location bpl.s loc_72DA8 lea $370(a6),a5 ; MJ: new SFX location movea.l $20(a6),a1 bra.s loc_72DB8 ; =========================================================================== loc_72DA8: subq.b #2,d0 lsl.b #2,d0 movea.l (a0,d0.w),a5 tst.b (a5) bpl.s loc_72DC8 movea.l $18(a6),a1 loc_72DB8: bclr #2,(a5) bset #1,(a5) move.b $B(a5),d0 jsr sub_72C4E(pc) loc_72DC8: movea.l a3,a5 bra.s loc_72E02 ; =========================================================================== loc_72DCC: lea $3A0(a6),a0 ; MJ: new SFX location tst.b (a0) bpl.s loc_72DE0 cmpi.b #$E0,d0 beq.s loc_72DEA cmpi.b #$C0,d0 beq.s loc_72DEA loc_72DE0: lea dword_722CC(pc),a0 lsr.b #3,d0 movea.l (a0,d0.w),a0 loc_72DEA: bclr #2,(a0) bset #1,(a0) cmpi.b #$E0,1(a0) bne.s loc_72E02 move.b $1F(a0),($C00011).l loc_72E02: addq.w #4,sp ; MJ: changed to 4 (go back, but not out of sound driver) tst.b $08(a6) ; MJ: is this a PCM channel? bne.s FlagF2_NoWaitFrame ; MJ: if so, branch addq.w #4,sp ; MJ: go back outside the sound driver like normal FlagF2_NoWaitFrame: rts ; =========================================================================== loc_72E06: ; XREF: loc_72A64 move.b #$E0,1(a5) move.b (a4)+,$1F(a5) btst #2,(a5) bne.s locret_72E1E move.b -1(a4),d0 ; MJ: reload F3 setting to d0 move.b d0,($C00011).l ; MJ: save F3 setting (should be EX (PSG 4) related) andi.b #%00000011,d0 ; MJ: get only frequency mode bits cmpi.b #%00000011,d0 ; MJ: has it been set to use PSG 3's frequency? bne.s locret_72E1E ; MJ: if not, branch move.b #%11011111,($C00011).l ; MJ: mute PSG 3's volume locret_72E1E: rts >> 14. Find this: Spoiler Code (Text): SegaPCM: incbin sound\segapcm.bin even And replace with: Spoiler Code (Text): SegaPCM: incbin sound\segapcm.wav,$3A SegaPCM_End: even Step 04 - Some new files Some macro/Sample files to be added and included. >> 1. Open up "sonic1.asm", and replace: Spoiler Code (Text): align macro cnop 0,\1 endm With this: Spoiler Code (Text): include "Equz80.asm" include "Macros.asm" >> 2. Download this file, and place at the root of the disassembly: >> 3. Download this SEGA wav file, and place it in the "sound" folder (and delete the old "segapcm.bin" while you're at it): >> 4. Inside the "Dual PCM" folder, create a new folder and name it "Samples": >> 5. Download and extract this into that new "Samples" folder: Download (32-bit) Download (64-bit) (For those who want the source for ConvPCM, download here). >> 6. Open up "sonic1.asm" and go to the very bottom where you'll find: Spoiler Code (Text): ; end of 'ROM' EndOfRom: END Above this, place the following: Spoiler Code (Text): ; =========================================================================== ; --------------------------------------------------------------------------- ; Sample 68k PCM list ; --------------------------------------------------------------------------- SampleList: dc.l StopSample ; 80 (THIS IS A REST NOTE, DO NOT EDIT...) dc.l Sonic1Kick ; 81 dc.l Sonic1Snare ; 82 dc.l Sonic1TimpaniLow ; 83 dc.l StopSample ; 84 dc.l StopSample ; 85 dc.l StopSample ; 86 dc.l StopSample ; 87 dc.l Sonic1TimpaniHigh ; 88 dc.l Sonic1TimpaniMid ; 89 dc.l Sonic1TimpaniLow ; 8A dc.l Sonic1TimpaniLower ; 8B ; --------------------------------------------------------------------------- ; Sample z80 pointers ; --------------------------------------------------------------------------- StopSample: dcz80 SWF_StopSample Sonic1Kick: dcz80 SWF_S1Kick Sonic1Snare: dcz80 SWF_S1Snare Sonic1TimpaniHigh: dcz80 SWF_S1TimpaniHigh Sonic1TimpaniMid: dcz80 SWF_S1TimpaniMid Sonic1TimpaniLow: dcz80 SWF_S1TimpaniLow Sonic1TimpaniLower: dcz80 SWF_S1TimpaniLower ; --------------------------------------------------------------------------- ; Sample file includes ; --------------------------------------------------------------------------- align $20,$FF SWF_StopSample: dcb.b $7FFF,$00 dc.b $80 ; --------------------------------------------------------------------------- SWF_S1Kick: incbin "Dual PCM\Samples\incswf\Sonic 1 Kick.swf" SWF_S1Snare: incbin "Dual PCM\Samples\incswf\Sonic 1 Snare.swf" SWF_S1TimpaniHigh: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani High.swf" SWF_S1TimpaniMid: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani Mid.swf" SWF_S1TimpaniLow: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani Low.swf" SWF_S1TimpaniLower: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani Lower.swf" ; =========================================================================== Step 05 - Adjustments The new driver will make the current SMPS music files incompatible, and we need to make the new samples compatible also. >> 1. Drag and drop all of the "music" files from the "sound" folder, onto a program inside the "_Assembly Tools" folder named "AddDAC.exe", like so: YOU ONLY NEED TO DO THIS ONCE, DO NOT DO IT MORE THAN ONCE. This will give all of the music files an extra PCM channel, and point it to an F2 stop flag, this will simply make it compatible for the new driver, and won't actually change the song's result. >> 2. Inside the "Dual PCM\Samples" folder, you will need to drag and drop all of the ".wav" files onto that program named "ConvPCM.exe", like so: Let the program do its thing, and press enter when it's finished, this will create a folder named "incswf", it'll convert the ".wav" files into the correct format and rate, and save the converted files inside "incswf" as ".swf" files, and THOSE are the files that are included into the source, the program will NEVER tamper with the original ".wav" files, so you may keep the original files in their former format/quality without having to worry. Step 06 - DMA Stops There are a series of Z80 stops when DMA transfers are due to occur, we need to replace those with Z80 DMA requests. >> 1. Replace all of this: Spoiler Code (Text): loc_BBA: move.w #1,($FFFFF644).w move.w #$100,($A11100).l loc_BC8: btst #0,($A11100).l bne.s loc_BC8 tst.b ($FFFFF64E).w bne.s loc_BFE lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) bra.s loc_C22 ; =========================================================================== loc_BFE: ; XREF: loc_BC8 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_C22: ; XREF: loc_BC8 move.w ($FFFFF624).w,(a5) move.w #0,($A11100).l bra.w loc_B5E ; =========================================================================== loc_C32: ; XREF: off_B6E bsr.w sub_106E loc_C36: ; XREF: off_B6E tst.w ($FFFFF614).w beq.w locret_C42 subq.w #1,($FFFFF614).w locret_C42: rts ; =========================================================================== loc_C44: ; XREF: off_B6E bsr.w sub_106E bsr.w sub_6886 bsr.w sub_1642 tst.w ($FFFFF614).w beq.w locret_C5C subq.w #1,($FFFFF614).w locret_C5C: rts ; =========================================================================== loc_C5E: ; XREF: off_B6E bsr.w sub_106E rts ; =========================================================================== loc_C64: ; XREF: off_B6E cmpi.b #$10,($FFFFF600).w ; is game mode = $10 (special stage) ? beq.w loc_DA6 ; if yes, branch loc_C6E: ; XREF: off_B6E move.w #$100,($A11100).l ; stop the Z80 loc_C76: btst #0,($A11100).l ; has Z80 stopped? bne.s loc_C76 ; if not, branch bsr.w ReadJoypads tst.b ($FFFFF64E).w bne.s loc_CB0 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) bra.s loc_CD4 ; =========================================================================== loc_CB0: ; XREF: loc_C76 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_CD4: ; XREF: loc_C76 move.w ($FFFFF624).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) tst.b ($FFFFF767).w beq.s loc_D50 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_D50: move.w #0,($A11100).l movem.l ($FFFFF700).w,d0-d7 movem.l d0-d7,($FFFFFF10).w movem.l ($FFFFF754).w,d0-d1 movem.l d0-d1,($FFFFFF30).w cmpi.b #$60,($FFFFF625).w bcc.s Demo_Time move.b #1,($FFFFF64F).w addq.l #4,sp bra.w loc_B64 ; --------------------------------------------------------------------------- ; Subroutine to run a demo for an amount of time ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Demo_Time: ; XREF: loc_D50; PalToCRAM bsr.w LoadTilesAsYouMove jsr AniArt_Load jsr HudUpdate bsr.w sub_165E tst.w ($FFFFF614).w ; is there time left on the demo? beq.w Demo_TimeEnd ; if not, branch subq.w #1,($FFFFF614).w ; subtract 1 from time left Demo_TimeEnd: rts ; End of function Demo_Time ; =========================================================================== loc_DA6: ; XREF: off_B6E move.w #$100,($A11100).l ; stop the Z80 loc_DAE: btst #0,($A11100).l ; has Z80 stopped? bne.s loc_DAE ; if not, branch bsr.w ReadJoypads lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.w #0,($A11100).l bsr.w PalCycle_SS tst.b ($FFFFF767).w beq.s loc_E64 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_E64: tst.w ($FFFFF614).w beq.w locret_E70 subq.w #1,($FFFFF614).w locret_E70: rts ; =========================================================================== loc_E72: ; XREF: off_B6E move.w #$100,($A11100).l ; stop the Z80 loc_E7A: btst #0,($A11100).l ; has Z80 stopped? bne.s loc_E7A ; if not, branch bsr.w ReadJoypads tst.b ($FFFFF64E).w bne.s loc_EB4 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) bra.s loc_ED8 ; =========================================================================== loc_EB4: ; XREF: loc_E7A lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_ED8: ; XREF: loc_E7A move.w ($FFFFF624).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) loc_EEE: move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) tst.b ($FFFFF767).w beq.s loc_F54 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_F54: move.w #0,($A11100).l ; start the Z80 movem.l ($FFFFF700).w,d0-d7 movem.l d0-d7,($FFFFFF10).w movem.l ($FFFFF754).w,d0-d1 movem.l d0-d1,($FFFFFF30).w bsr.w LoadTilesAsYouMove jsr AniArt_Load jsr HudUpdate bsr.w sub_1642 rts ; =========================================================================== loc_F8A: ; XREF: off_B6E bsr.w sub_106E addq.b #1,($FFFFF628).w move.b #$E,($FFFFF62A).w rts ; =========================================================================== loc_F9A: ; XREF: off_B6E bsr.w sub_106E move.w ($FFFFF624).w,(a5) bra.w sub_1642 ; =========================================================================== loc_FA6: ; XREF: off_B6E move.w #$100,($A11100).l ; stop the Z80 loc_FAE: btst #0,($A11100).l ; has Z80 stopped? bne.s loc_FAE ; if not, branch bsr.w ReadJoypads lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.w #0,($A11100).l ; start the Z80 tst.b ($FFFFF767).w beq.s loc_1060 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_1060: tst.w ($FFFFF614).w beq.w locret_106C subq.w #1,($FFFFF614).w locret_106C: rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_106E: ; XREF: loc_C32; et al move.w #$100,($A11100).l ; stop the Z80 loc_1076: btst #0,($A11100).l ; has Z80 stopped? bne.s loc_1076 ; if not, branch bsr.w ReadJoypads tst.b ($FFFFF64E).w bne.s loc_10B0 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) bra.s loc_10D4 ; =========================================================================== loc_10B0: ; XREF: sub_106E lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_10D4: ; XREF: sub_106E lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.w #0,($A11100).l ; start the Z80 rts ; End of function sub_106E With this: Spoiler Code (Text): loc_BBA: move.w #1,($FFFFF644).w Z80DMA_ON tst.b ($FFFFF64E).w bne.s loc_BFE lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) bra.s loc_C22 ; =========================================================================== loc_BFE: ; XREF: loc_BC8 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_C22: ; XREF: loc_BC8 move.w ($FFFFF624).w,(a5) Z80DMA_OFF bra.w loc_B5E ; =========================================================================== loc_C32: ; XREF: off_B6E bsr.w sub_106E loc_C36: ; XREF: off_B6E tst.w ($FFFFF614).w beq.w locret_C42 subq.w #1,($FFFFF614).w locret_C42: rts ; =========================================================================== loc_C44: ; XREF: off_B6E bsr.w sub_106E bsr.w sub_6886 bsr.w sub_1642 tst.w ($FFFFF614).w beq.w locret_C5C subq.w #1,($FFFFF614).w locret_C5C: rts ; =========================================================================== loc_C5E: ; XREF: off_B6E bsr.w sub_106E rts Return: bsr.w ReadJoypads rts ; =========================================================================== loc_C64: ; XREF: off_B6E cmpi.b #$10,($FFFFF600).w ; is game mode = $10 (special stage) ? beq.w loc_DA6 ; if yes, branch loc_C6E: ; XREF: off_B6E Z80DMA_ON bsr.w ReadJoypads tst.b ($FFFFF64E).w bne.s loc_CB0 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) bra.s loc_CD4 ; =========================================================================== loc_CB0: ; XREF: loc_C76 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_CD4: ; XREF: loc_C76 move.w ($FFFFF624).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) tst.b ($FFFFF767).w beq.s loc_D50 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_D50: Z80DMA_OFF movem.l ($FFFFF700).w,d0-d7 movem.l d0-d7,($FFFFFF10).w movem.l ($FFFFF754).w,d0-d1 movem.l d0-d1,($FFFFFF30).w cmpi.b #$60,($FFFFF625).w bcc.s Demo_Time move.b #1,($FFFFF64F).w addq.l #4,sp bra.w loc_B64 ; --------------------------------------------------------------------------- ; Subroutine to run a demo for an amount of time ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Demo_Time: ; XREF: loc_D50; PalToCRAM bsr.w LoadTilesAsYouMove jsr AniArt_Load jsr HudUpdate bsr.w sub_165E tst.w ($FFFFF614).w ; is there time left on the demo? beq.w Demo_TimeEnd ; if not, branch subq.w #1,($FFFFF614).w ; subtract 1 from time left Demo_TimeEnd: rts ; End of function Demo_Time ; =========================================================================== loc_DA6: ; XREF: off_B6E bsr.w ReadJoypads Z80DMA_ON lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) bsr.w PalCycle_SS tst.b ($FFFFF767).w beq.s loc_E64 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_E64: Z80DMA_OFF tst.w ($FFFFF614).w beq.w locret_E70 subq.w #1,($FFFFF614).w locret_E70: rts ; =========================================================================== loc_E72: ; XREF: off_B6E bsr.w ReadJoypads Z80DMA_ON tst.b ($FFFFF64E).w bne.s loc_EB4 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) bra.s loc_ED8 ; =========================================================================== loc_EB4: ; XREF: loc_E7A lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_ED8: ; XREF: loc_E7A move.w ($FFFFF624).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) loc_EEE: move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) tst.b ($FFFFF767).w beq.s loc_F54 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_F54: Z80DMA_OFF movem.l ($FFFFF700).w,d0-d7 movem.l d0-d7,($FFFFFF10).w movem.l ($FFFFF754).w,d0-d1 movem.l d0-d1,($FFFFFF30).w bsr.w LoadTilesAsYouMove jsr AniArt_Load jsr HudUpdate bsr.w sub_1642 rts ; =========================================================================== loc_F8A: ; XREF: off_B6E bsr.w sub_106E addq.b #1,($FFFFF628).w move.b #$E,($FFFFF62A).w rts ; =========================================================================== loc_F9A: ; XREF: off_B6E bsr.w sub_106E move.w ($FFFFF624).w,(a5) bra.w sub_1642 ; =========================================================================== loc_FA6: ; XREF: off_B6E bsr.w ReadJoypads Z80DMA_ON lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) tst.b ($FFFFF767).w beq.s loc_1060 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w loc_1060: Z80DMA_OFF tst.w ($FFFFF614).w beq.w locret_106C subq.w #1,($FFFFF614).w locret_106C: rts ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_106E: ; XREF: loc_C32; et al bsr.w ReadJoypads Z80DMA_ON tst.b ($FFFFF64E).w bne.s loc_10B0 lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) PAL1_LOAD: bra.s loc_10D4 ; =========================================================================== loc_10B0: lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9540,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) loc_10D4: lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) Z80DMA_OFF rts ; End of function sub_106E Basically, all we've done is replaced all of these instances: Spoiler Code (Text): move.w #$100,($A11100).l ; stop the Z80 location: btst #0,($A11100).l ; has Z80 stopped? bne.s location ; if not, branch With these: Spoiler Code (Text): Z80DMA_ON And all of these instances: Spoiler Code (Text): move.w #0,($A11100).l ; start the Z80 With these: Spoiler Code (Text): Z80DMA_OFF So now instead of stopping the Z80, it'll just send it a note saying that there's a DMA transfer to occur. Finished Your disassembly is now setup with the new Dual PCM driver, here is a download link to a Sonic 1 disassembly with the changes already applied. Download (32-bit) Download (64-bit) Adding in new samples Alright, so now that everything's pretty much setup, you'll no doubt want to put new samples in, or, replace the old ones, or whatever. Here, we look at putting in the sample, converting it, and including it properly. Step 01 - Adjusting the volume of your sample This driver fuses two samples together by "addition" before flushing out the final result, the problem is, when you add two samples together, you are in danger of overflowing the 8-bit limit. Getting Dual PCM to cap the limit if it overflows would take extra processing time, and this would cause the sample rate to drop really low. So, instead, the tool "ConvPCM" will automatically convert the sample to 7-bit, this ensures that the samples (when added/fused together) don't overflow, this creates a problem though, 7-bit samples playing in the lower of 8-bit will sound very quiet and low volume, there is no avoiding this. What I would advise you to do, is to raise the volume of your sample by twice as much before putting it into the game. Now you might say "won't this cause the sample to overflow", quite possibly, however, there is a phenomina known as the "Loudness Wars", long story short, sharp sounds (such as drums or waves that jitter very eratically) can be overflown to a degree without any notice-able damage to the quality. Because they're so sharp, the resulting overflow is also sharp, therefore there appears to be no difference in quality. You can use this to your advantage, many of your samples you will be able to get away with multiplying the volume to 200% (an extra 100%), this will help the volume of the sample sound normal in-game. There are plenty of tools out there you can use to edit the sample's volume (Audacity for example). The tool doesn't adjust the volume twice as loud to compensate for you, the reason is that you might want the sample a specific volume (that, and not all samples sound great at 200%, some may just need 150%), the Sonic 1 samples provided above have their volumes already raised to 200%, and if you listen, you'll find barely any difference in quality (in fact, it'll be higher quality because of the driver, which is irrelevant to the conversion anyway...). Step 02 - Putting your sample file in Put the ".wav" or ".bin" file Inside the "Dual PCM\Samples" folder, for example, we'll have a sample called "Scratch.wav" just as an example: If you open up "ConvPCM.txt" you'll find this: Spoiler Code (Text): Sample Rate: 20500 Interpolation: TRUE It is recommended that you keep the "Sample Rate" at 20500, this is just for any changes to the driver that'll cause a sample rate change, what might be of interest to you is the "Interpolation" setting, I've had it set to "TRUE" by default, this simply smooths out the samples when they change from say... 44,100Hz to 20,500Hz, this will make all sharp sounds that might sound horrible at 20,500Hz, soften up. If you do not like the result and want all of your samples to remain sharp, change this to "FALSE" (this will effect any samples you convert). Now, all that's left, is to drag and drop "Scratch.wav" onto "ConvPCM.exe": A "Scratch.swf" file shall now exist in the "incswf" folder, and we can now include it into the game. Open up "sonic1.asm" and go down to here: Spoiler Code (Text): ; --------------------------------------------------------------------------- ; Sample file includes ; --------------------------------------------------------------------------- align $20,$FF SWF_StopSample: dcb.b $7FFF,$00 dc.b $80 ; --------------------------------------------------------------------------- SWF_S1Kick: incbin "Dual PCM\Samples\incswf\Sonic 1 Kick.swf" SWF_S1Snare: incbin "Dual PCM\Samples\incswf\Sonic 1 Snare.swf" SWF_S1TimpaniHigh: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani High.swf" SWF_S1TimpaniMid: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani Mid.swf" SWF_S1TimpaniLow: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani Low.swf" SWF_S1TimpaniLower: incbin "Dual PCM\Samples\incswf\Sonic 1 Timpani Lower.swf" You can now insert your new incbin for "Scratch.swf" on the end: Spoiler Code (Text): SWF_Scratch: incbin "Dual PCM\Samples\incswf\Scratch.swf" Next thing to do, is to create a Z80 pointer for it here: Spoiler Code (Text): ; --------------------------------------------------------------------------- ; Sample z80 pointers ; --------------------------------------------------------------------------- StopSample: dcz80 SWF_StopSample Sonic1Kick: dcz80 SWF_S1Kick Sonic1Snare: dcz80 SWF_S1Snare Sonic1TimpaniHigh: dcz80 SWF_S1TimpaniHigh Sonic1TimpaniMid: dcz80 SWF_S1TimpaniMid Sonic1TimpaniLow: dcz80 SWF_S1TimpaniLow Sonic1TimpaniLower: dcz80 SWF_S1TimpaniLower Add it onto the end of the list: Spoiler Code (Text): Scratch: dcz80 SWF_Scratch Finally, you now need to place it in a slot somewhere, right here: Spoiler Code (Text): ; --------------------------------------------------------------------------- ; Sample 68k PCM list ; --------------------------------------------------------------------------- SampleList: dc.l StopSample ; 80 (THIS IS A REST NOTE, DO NOT EDIT...) dc.l Sonic1Kick ; 81 dc.l Sonic1Snare ; 82 dc.l Sonic1TimpaniLow ; 83 dc.l StopSample ; 84 dc.l StopSample ; 85 dc.l StopSample ; 86 dc.l StopSample ; 87 dc.l Sonic1TimpaniHigh ; 88 dc.l Sonic1TimpaniMid ; 89 dc.l Sonic1TimpaniLow ; 8A dc.l Sonic1TimpaniLower ; 8B You can use any of the slots already taken so far, by replacing the name with "Scratch", for example, we could replace "Sonic1Snare" with "Scratch", and this will cause the DAC ID 82 to play the scratch sample instead of Sonic 1's snare sample. You could use slot 84, 85, 86 or 87 since they're unused, or, you can add in new slots onto the end like so: Spoiler Code (Text): SampleList: dc.l StopSample ; 80 (THIS IS A REST NOTE, DO NOT EDIT...) dc.l Sonic1Kick ; 81 dc.l Sonic1Snare ; 82 dc.l Sonic1TimpaniLow ; 83 dc.l StopSample ; 84 dc.l StopSample ; 85 dc.l StopSample ; 86 dc.l StopSample ; 87 dc.l Sonic1TimpaniHigh ; 88 dc.l Sonic1TimpaniMid ; 89 dc.l Sonic1TimpaniLow ; 8A dc.l Sonic1TimpaniLower ; 8B dc.l Scratch ; 8C From now on, DAC ID "8C" will play the scratch sample. The SMPS format only allows you to play samples from 81 to DF, so that will be your limit, this is not a limit of Dual PCM, this is a limit of SMPS The scratch sample itself doesn't have to be any specific size, it can be a 1 minute sample if you really wanted it to, what you have to watch out for though, is your ROM limit, again, not a limit of Dual PCM, but rather a limit of the CPU's memory map. One important thing to note, I have used ".wav" files here, and the program converts them from any uncompressed "RIFF" format to the right format for Dual PCM, you can use ".bin" files if you want, but your files MUST be 8-bit, MUST be mono, MUST be unsigned, and MUST be 20,500Hz, the program will only convert the important related parts, it cannot know the what bit format, rate, channels, etc, so it simply "assumes" that all ".bin" files will be 8-bit, mono, unsigned, 20,500Hz samples, with NO extra headers/information, just RAW sample data. If you make a change to any of the sample files, you MUST redrag them onto the program to convert them once again to update the ".swf" files. SMPS Tracker Format The only difference to the SMPS files now, is that there's an extra pointer put in for the second PCM channel, and an F2 stop flag for it, and the number of FM/DAC channels has been increased by 1: After: And that's it, treat this new channel as you would a normal DAC channel, it is nothing more than having another DAC channel. Optimising SFX The more FM channels you have running, then the more the Z80 needs to be accessed by the 68k for passing on FM operator data. However, I do have something to combat this, a new set of optimised SFX tracks, you can download them here: Optimised Sound Pack Not all SFX have changed, only some of them (I forget which so I included all of them...). A lot of Sonic 1's SFX used multiple channels, even though they really didn't need to, there were also some stereo effects, and so on, and while they might have seem awesome at the time, they are often something that people don't really notice... ..and do you know how I know? Because I've used these optimised SFX files (and improved on them) ever since Sonic 1 Brother Trouble, I even used them in Sonic 1 The Next Level, and you know what? No-one even noticed. The only person who came close to noticing was one person who mentioned that a track on Brother Trouble seemed to use "extra channels", what they didn't realise was that it's because the SFX didn't mute the music much, they use less channels and sound almost the same. But its use here is more for prevention of quality loss in the PCM playback, the lack of channel stopping is a bonus, you can't say fairer than that~ Final Stuff If you plan to perform DMA transfers, ALWAYS put in a "Z80DMA_ON" before, and a "Z80DMA_OFF" afterwards, to allow the Z80 to avoid getting stopped during a transfer. If you have multiple transfers, DO NOT turn DMA on and off and on and off between each transfer. Turn it on, do all of your transfers, then turn it off. This will reduce the amount of changes the Z80 needs to make to account for the transfers. If you plan to write something to the YM2612 from the 68k side, make sure that once you're finished, you write $2A to the YM2612 address port before starting the Z80 again. The Dual PCM driver will NOT set the YM2612 address, it simply "assumes" it already is at $2A, this is to save on Z80 processing time. All samples MUST be aligned to a $20th position in the ROM, and the sample MUST end with $80 on the $1Fth position. I have already set this up for you, the program that converts the ".wav" files into ".swf" files will automatically pad to the next $20, and place an $80 on the last byte. And the alignment is already done in the source here: Spoiler Code (Text): ; --------------------------------------------------------------------------- ; Sample file includes ; --------------------------------------------------------------------------- align $20,$FF As long as you place all includes here, you will be fine. A thank you to "Ralakimus" for providing 32-bit builds of my programs, and to "Egor The Cat" for his helpful guide, and for reminding me of extra details I forgot to add. I will be doing some clean up and some quality improvements as time goes on, but I am hoping that some other Z80 programmers out there will contribute to improve the driver, we need as many improvements as we can get if we want Pitch/Volume support. Adding those may cause a major drop in sample rate, and so, we need to be careful how we go about this. If any of you have any ideas, or wish to contribute in some way, by all means post away, I'll be happy to at least "attempt" to implement or allow the implementation of ideas into the product as it grows (if it grows). I need to stop typing more stuff now as the site's editor is lagging from the amount of text and code put in this tiny dialogue box, have fun~
Many of you might think that it's a small detail, but one thing I really like is how the pitches of the timpanis are correct. Many custom drivers do terrible things to the timpanis for some reason, and while it's particularly noticeable in the 1-up jingle and in the boss theme, many people don't even seem to notice that. Aside from all the obvious things which can be praised about this driver (sound quality, dual channel, ease of use, and so on), having proper timpanis is always good.
Your reputation proceeds you Nineko! I am so glad you noticed, I spent hours adjusting the sample rate value in the ".wav" files, until the output pitch matched the originals.
I may try to add this to my base rom, wish me luck! (GitHub's modern disassembly user here) This is so awesome it's like black magic or something, thank you very much for doing this! Now, correct me if I'm wrong, but as far I can notice, since I have to double the volume of the samples to play properly on the lower bit, theese samples have to be forever bound to the second channel, am I right? Not that it is a problem, not at all, it's just to make sure I'm understanding things right since I'm a bit dumb for theese things. Also, before starting, one serious doubt: do I have to first insert a 68k sound driver and after that putting this? This raises my faliure possibilities to like, quad as much as before :p.
No. When you use the program of your choice to raise the volume of your samples to twice as loud, that program will most definitely cap it if it overflows. ConvPCM.exe will convert it to 7-bit no matter how loud you have it, allowing the samples to be played on any of the two channels at any time, without overflow, so you do not have to bound your samples to a specific channel. In the test ROM shared at the top of the first post, the Sonic 1 samples have their volumes doubled, as well as the piano/guitar samples, ConvPCM forced them to a capped 7-bit, this is why they don't ever overflow. You do not have to worry about which channel you use the samples on, when you use them, or anything like that. Use both of them as much to your heart's contempt, abuse them, get the most out of them! Just raise the volume of your samples until it sounds loud enough for your liking. You will need a 68k driver for FM/PSG/tracker playback, like I said, Dual PCM needs the entire Z80's resources to do its job. If you are hacking a game that does not have a 68k FM/PSG/tracker playback driver, then I'd suggest looking into the Clone Driver (originally by Tweaker, and someone else I currently forget who. And lately being improved by Clownacy with heaps of bug-fixes). The guide above, is nothing more than a guide.
Update... Some changes that will improve the quality slightly... Open up "sonic1.asm" source, find this: Spoiler Code (Text): Z80ROM: incbin "Dual PCM\Z80.bin" Z80ROM_End: even Change it to this: Spoiler Code (Text): Z80ROM: incbin "Dual PCM\Z80.bin" dcz80 SWF_StopSample Z80ROM_End: even Then find this: Spoiler Code (Text): SWF_StopSample: dcb.b $1F,$00 dc.b $80 Change it to this: Spoiler Code (Text): SWF_StopSample: dcb.b $7FFF,$00 dc.b $80 Download this new Z80.asm file, and replace the old one. And finally, open up "ConvPCM.txt" and change "19500" to "20500", and of course, drag and drop your samples again. ------------------------------------------------------------------------------------ Basically, the sample flushing is staggered, but it wasn't 100% smooth/consistent. I analysed the output waveform and found difference in times between each byte flush, I've changed the order slightly, and now it's considerably smoother, this'll remove the "crinkly" sound you here a little bit. In addition, some emulators (like KEGA) as well as hardware had samples playing at a slightly higher pitch when one of the channels were resting, I'm not too sure though I suspect the Z80 accessing the window is slightly slower. And so, the null/mute sample that the channels will play when resting, will now be on the 68k side and accessed through the window instead. This will cost you $8000 bytes of ROM space. But it'll mean the pitch will also remain consistent now. I'll update the first post to include this update in a moment.
I am a little embarrassed to report that... ...well, I've made a fatal error that caused the sound driver to sound aweful on hardware. I STRONGLY suggest fixing it by opening up "Macro.asm", and in "Z80DMA_ON", changing this: Code (Text): move.w #$0000,d7 To this: Code (Text): move.w #$0180,d7 This didn't allow the Z80 enough time, and well, the results were horrible, this will fix the quality greatly... You won't notice this quality on emulation, but you will definately on hardware! I will have an update coming later on tonight which will ensure the highest quality possible for 20,500Hz Dual samples, it's gonna be AMAZING guys, because you'll be able to hear the difference on emulation as well as hardware~ Edit; here's a recording of it so far from hardware: Model 1 Recording Nomad Recording I couldn't get a Model 2 recording because it lacks a headphone jack, but it sounds pretty damn fine on there (in fact, Model 2's filter seems to do the samples some extra justice).
UPDATE!! We now have an update, this driver now avoids Z80 stops caused by FM operator data being written, I shall update the first post with the most recent version, but here is a list of steps you those that already have the driver installed and in use. >> 1. Download this new "Z80.asm" source, and replace the old one in the "Dual PCM" folder. >> 2. Download this new "Macros.asm" source, and replace the old one. >> 3. Open up "sonic1.asm" and find "sub_71B4C:": Code (Text): sub_71B4C: ; XREF: loc_B10; PalToCRAM lea ($FFF000).l,a6 Replace this, with: Code (Text): YM_Access_WaitRead: StartZ80 ; EXT: request Z80 stop off (allow it to continue) rept $10 ; EXT: delay for a long enough time to allow the... nop ; EXT: '' ...68k pointer to be saved correctly. endr ; EXT: '' sub_71B4C: lea ($FFF000).l,a6 lea ($A00000+YM_Access).l,a0 ; EXT: load access address in Z80 move.l #$A00000,d0 ; EXT: prepare Z80 RAM address in d0 (i.e. start of Cue list address) StopZ80 ; EXT: request Z80 stop on tst.b (a0)+ ; EXT: is the Z80 accessing the 68k pointer? bne.s YM_Access_WaitRead ; EXT: if so, branch and wait for it to finish... move.b (a0)+,d1 ; EXT: load lower byte of pointer move.b (a0)+,d0 ; EXT: load upper byte of pointer StartZ80 ; EXT: request Z80 stop off lsl.w #$08,d0 ; EXT: shift upper byte up move.b d1,d0 ; EXT: put lower byte with it move.l d0,$10(a6) ; EXT: store the cue address >> 4. Find "loc_71C44:": Code (Text): loc_71C44: rts Replace it with this: Code (Text): loc_71C44: bra.s YM_Access_TestWrite ; EXT: jump into the access loop YM_Access_WaitWrite: StartZ80 ; EXT: request Z80 stop off (allow it to continue) rept $10 ; EXT: delay for a long enough time to let the Z80... nop ; EXT: '' ...finish writing the 68k pointer, so it doesn't... endr ; EXT: '' ...clash with 68k's pointer writing. YM_Access_TestWrite: lea ($A00000+YM_Access).l,a0 ; EXT: load access address in Z80 lea $13(a6),a1 ; EXT: load the 68k's pointer finish location StopZ80 ; EXT: request Z80 stop on tst.b (a0)+ ; EXT: is the Z80 accessing the 68k pointer? bne.s YM_Access_WaitWrite ; EXT: if so, branch and wait for it to finish... move.b (a1),(a0)+ ; EXT: save lower byte of pointer move.b -(a1),(a0) ; EXT: save upper byte of pointer StartZ80 ; EXT: request Z80 stop off rts >> 5. Find routine "loc_72068:", and delete these: Code (Text): moveq #$FFFFFFFF,d2 ; MJ: clear PCM finish flag Code (Text): addq.b #$01,d2 ; MJ: set PCM finish flag beq.s SD_NoFM ; MJ: if it hasn't finished yet (not for two turns), branch moveq #$02,d5 ; MJ: set longer delay for FM/PSG channels (DAC has delay now...) SD_NoFM: >> 6. Find routine "loc_72114:", and insert this instruction directly after the label: Code (Text): moveq #$02,d5 ; EXT: set PSG to delay for 1 extra frame (This is to match the PSG with the FM/DAC which is delayed a frame by the Z80) >> 7. Find routine "loc_72228:": Code (Text): loc_72228: moveq #0,d3 move.b 1(a1),d3 move.b d3,d4 bmi.s loc_72244 subq.w #2,d3 lsl.w #2,d3 And replace it with: Code (Text): loc_72228: moveq #0,d3 move.b 1(a1),d3 moveq #$02,d2 ; EXT: set PSG to delay for 1 extra frame (This is to match the PSG with the FM/DAC which is delayed a frame by the Z80) move.b d3,d4 bmi.s loc_72244 move.b #$01,d2 ; EXT: set DAC/FM to delay for 0 frames like normal (these have an auto delay of 1 frame in the Z80) subq.w #2,d3 lsl.w #2,d3 >> 8. Find "loc_72276:", further down you will find this: Code (Text): move.b #1,$E(a5) Replace it with this: Code (Text): move.b d2,$E(a5) ; EXT: moving d2 contents (1 for FM/4 for PSG) >> 9. Find "Sound_E4:": Code (Text): Sound_E4: ; XREF: Sound_ChkValue; Sound_ExIndex; sub_72504 moveq #$2B,d0 move.b #$80,d1 jsr sub_7272E(pc) moveq #$27,d0 moveq #0,d1 jsr sub_7272E(pc) movea.l a6,a0 move.w #$EF,d0 ; MJ: new size of data to clear loc_725B6: clr.l (a0)+ dbf d0,loc_725B6 move.b #$80,9(a6) ; set music to $80 (silence) jsr sub_7256A(pc) bra.w sub_729B6 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_725CA: ; XREF: Sound_ChkValue movea.l a6,a0 move.b 0(a6),d1 move.b $27(a6),d2 move.b $2A(a6),d3 move.b $26(a6),d4 move.w $A(a6),d5 move.w #$93,d0 ; MJ: new size loc_725E4: clr.l (a0)+ dbf d0,loc_725E4 move.b d1,0(a6) move.b d2,$27(a6) move.b d3,$2A(a6) move.b d4,$26(a6) move.w d5,$A(a6) move.b #$80,9(a6) jsr sub_7256A(pc) bra.w sub_729B6 ; End of function sub_725CA Replace it with this: Code (Text): Sound_E4: StopZ80 lea (StopSample).l,a0 ; EXT: load stop sample address lea ($A00000+PCM1_Sample).l,a1 ; EXT: load PCM 1 slot address move.b (a0)+,(a1)+ ; EXT: set address of sample move.b (a0)+,(a1)+ ; EXT: '' move.b (a0)+,(a1)+ ; EXT: '' move.b #(CUPCM1_NewSample&$FF),($A00000+CU_Stack).l ; EXT: set routine to run move.b #(CUPCM1_NewSample>>$08),($A00000+CU_Stack+1).l ; EXT: '' move.b #%11001001,($A00000+CUPCM1_RET).l ; EXT: change "NOP" to "RET" lea (StopSample).l,a0 ; EXT: load stop sample address lea ($A00000+PCM2_Sample).l,a1 ; EXT: load PCM 2 slot address move.b (a0)+,(a1)+ ; EXT: '' move.b (a0)+,(a1)+ ; EXT: '' move.b (a0)+,(a1)+ ; EXT: '' move.b #%00101000,($A00000+CUPCM2_RET).l ; EXT: change "JR NZ" to "JR Z" StartZ80 moveq #$2B,d0 move.b #$80,d1 jsr sub_7272E(pc) moveq #$27,d0 moveq #0,d1 jsr sub_7272E(pc) movea.l a6,a0 move.l $10(a6),d6 ; EXT: store YM Cue list pointer move.w #$EF,d0 ; MJ: new size of data to clear loc_725B6: clr.l (a0)+ dbf d0,loc_725B6 move.l d6,$10(a6) ; EXT: restore YM Cue list pointer move.b #$80,9(a6) ; set music to $80 (silence) jsr sub_7256A(pc) bra.w sub_729B6 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_725CA: ; XREF: Sound_ChkValue movea.l a6,a0 move.b 0(a6),d1 move.b $27(a6),d2 move.b $2A(a6),d3 move.b $26(a6),d4 move.w $A(a6),d5 move.l $10(a6),d6 ; EXT: store YM Cue list pointer move.w #$93,d0 ; MJ: new size loc_725E4: clr.l (a0)+ dbf d0,loc_725E4 move.b d1,0(a6) move.b d2,$27(a6) move.b d3,$2A(a6) move.b d4,$26(a6) move.w d5,$A(a6) move.l d6,$10(a6) ; EXT: restore YM Cue list pointer move.b #$80,9(a6) jsr sub_7256A(pc) bra.w sub_729B6 ; End of function sub_725CA >> 10. Find this: Code (Text): sub_7272E: pea (a0) ; MJ: store register lea ($A04000).l,a0 ; MJ: load YM2612 address port StopZ80 ; MJ: request Z80 stop "ON" move.b d0,(a0) ; MJ: write address rept $0F nop ; MJ: delay (Tested on MD1 and 2. F nops are required) endr move.b d1,$01(a0) ; MJ: write data rept $0F nop ; MJ: delay (Tested on MD1 and 2. F nops are required) endr move.b #$2A,(a0) ; MJ: write address (set it back to DAC port for the Z80) StartZ80 ; MJ: request Z80 stop "OFF" move.l (sp)+,a0 ; MJ: restore register rts ; MJ: return ; End of function sub_7272E ; =========================================================================== loc_7275A: ; XREF: sub_72722 move.b 1(a5),d2 bclr #2,d2 add.b d2,d0 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_72764: pea (a0) ; MJ: store register lea ($A04000).l,a0 ; MJ: load YM2612 address port StopZ80 ; MJ: request Z80 stop "ON" move.b d0,$02(a0) ; MJ: write address rept $0F nop ; MJ: delay (Tested on MD1 and 2. F nops are required) endr move.b d1,$03(a0) ; MJ: write data rept $0F nop ; MJ: delay (Tested on MD1 and 2. F nops are required) endr move.b #$2A,(a0) ; MJ: write address (set it back to DAC port for the Z80) StartZ80 ; MJ: request Z80 stop "OFF" move.l (sp)+,a0 ; MJ: restore register rts ; MJ: return Replace it with: Code (Text): sub_7272E: movem.l d2/a0,-(sp) ; EXT: store register data movea.l $10(a6),a0 ; EXT: load Cue pointer addq.w #$01,a0 ; EXT: skip $40 move.b #$00,d2 ; EXT: prepare d2 for YM2612 port address ($4000 - $4001) StopZ80 ; EXT: request Z80 stop "ON" move.b d2,(a0)+ ; EXT: write YM2612 port address move.b d1,(a0)+ ; EXT: write YM2612 data move.b d0,(a0)+ ; EXT: write YM2612 address StartZ80 ; EXT: request Z80 stop "OFF" move.w a0,d2 ; EXT: load Cue pointer andi.w #$0FFF,d2 ; EXT: wrap it ori.w #$1000,d2 ; EXT: '' move.w d2,$12(a6) ; EXT: update it movem.l (sp)+,d2/a0 ; EXT: restore register data rts ; EXT: return ; =========================================================================== loc_7275A: ; XREF: sub_72722 move.b 1(a5),d2 bclr #2,d2 add.b d2,d0 ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| sub_72764: movem.l d2/a0,-(sp) ; EXT: store register data movea.l $10(a6),a0 ; EXT: load Cue pointer addq.w #$01,a0 ; EXT: skip $40 move.b #$02,d2 ; EXT: prepare d2 for YM2612 port address ($4002 - $4003) StopZ80 ; EXT: request Z80 stop "ON" move.b d2,(a0)+ ; EXT: write YM2612 port address move.b d1,(a0)+ ; EXT: write YM2612 data move.b d0,(a0)+ ; EXT: write YM2612 address StartZ80 ; EXT: request Z80 stop "OFF" move.w a0,d2 ; EXT: load Cue pointer andi.w #$0FFF,d2 ; EXT: wrap it ori.w #$1000,d2 ; EXT: '' move.w d2,$12(a6) ; EXT: update it movem.l (sp)+,d2/a0 ; EXT: restore register data rts ; EXT: return >> 11. Find this: Code (Text): SoundDriverLoad: lea (Z80ROM).l,a0 ; load Z80 ROM data lea ($A00000).l,a1 ; load Z80 RAM space address move.w #(Z80ROM_End-Z80ROM)-$01,d1 ; set repeat times move.w #$2000,d2 ; prepare total Z80 size sub.w d1,d2 ; subtract repeat times from total size subq.w #$02,d2 ; decrease by 2 (for dbf) move.w #$0100,($A11100).l ; request Z80 stop (ON) move.w #$0100,($A11200).l ; request Z80 reset (OFF) btst.b #$00,($A11100).l ; has the Z80 stopped yet? bne.s *-$08 ; if not, branch SM_LoadZ80: move.b (a0)+,(a1)+ ; dump Z80 data to Z80 space dbf d1,SM_LoadZ80 ; repeat til done SM_ClearZ80: move.b d0,(a1)+ ; clear remaining Z80 space dbf d2,SM_ClearZ80 ; repeat til done move.w #$0000,($A11200).l ; request Z80 reset (ON) moveq #$7F,d1 ; set repeat times dbf d1,* ; there's no way of checking for reset, so a manual delay is necessary move.w #$0000,($A11100).l ; request Z80 stop (OFF) move.w #$0100,($A11200).l ; request Z80 reset (OFF) rts ; return Replace it with: Code (Text): SoundDriverLoad: lea (Z80ROM).l,a0 ; load Z80 ROM data lea ($A00000).l,a1 ; load Z80 RAM space address move.w #(Z80ROM_End-Z80ROM)-$01,d1 ; set repeat times move.w #$0100,($A11100).l ; request Z80 stop (ON) move.w #$0100,($A11200).l ; request Z80 reset (OFF) btst.b #$00,($A11100).l ; has the Z80 stopped yet? bne.s *-$08 ; if not, branch SM_LoadZ80: move.b (a0)+,(a1)+ ; dump Z80 data to Z80 space dbf d1,SM_LoadZ80 ; repeat til done lea (StopSample).l,a0 ; load stop/mute sample address lea ($A00000+MuteSample).l,a1 ; load Z80 RAM space where the pointer is to be stored move.b (a0)+,(a1)+ ; copy pointer over into Z80 move.b (a0)+,(a1)+ ; '' move.b (a0)+,(a1)+ ; '' move.w #$0000,($A11200).l ; request Z80 reset (ON) moveq #$7F,d1 ; set repeat times dbf d1,* ; there's no way of checking for reset, so a manual delay is necessary move.w #$0000,($A11100).l ; request Z80 stop (OFF) move.w #$0100,($A11200).l ; request Z80 reset (OFF) rts ; return >> 12. Find this: Code (Text): Z80ROM: incbin "Dual PCM\Z80.bin" dcz80 SWF_StopSample Z80ROM_End: even Replace it with this: Code (Text): Z80ROM: incbin "Dual PCM\Z80.bin" Z80ROM_End: even Finished... I'll be updating the first post now.
I really, honestly doubted something like would be possible just because of how limited the Z80's power is, but I'm happy to eat my words. This is outstanding, MarkeyJester!
Thank god I didn't start with that yet :p. Also, I thought that Model 2 was worse in terms of sound? What did I miss now?
Alright ladies and gentlemen, I have some good news for you all! I rewrote the Z80 side of Dual PCM in order to support pitch control, this form of pitch control isn't done by delay however since a delay would cause both samples to pitch at the same time. Instead I'm using a mathematical fixed point 16 bit quotient 8 bit dividend to control the pitch of each PCM channel individually, this also means that a specific pitch can be played, any specific pitch that PCM playback can muster. This brought the sample playback down to about 13.000Hz unfortunately... However, after rearranging the time in which the samples are fused (doing on playback rather than collection), I was able to increase that sample rate to 14.400Hz. And with suggestions, support and encouragement from Natsumi, as well as a lot of shared ideas, we've managed to get a stable enough build to play back any two samples, any size, at any pitch, including looping sample support, as well as reversal playback. All at a sample rate of 14.400Hz, but with little to no chopping on hardware for that clean and crisp quality. http://mrjester.hapisan.com/01_Public/DualPCMPitchTest.mp3 (I posted this in the music thread.) However, I went a step further and wondered how well it'd perform by changing the number of bytes loaded into the buffer vs playback. Dual PCM has always been; "Load 20, Play 10". So after ripping the driver apart, and applying some careful buffer wrapping methods for non-POT (not power of two) sizes, I tried with; "Load 18, Play 10", and this raised the sample rate to 16.500Hz. I then went a step further and tried "Load 14, Play 10", and the sample rate is a nice healthy 18.000Hz playback! I've muted FM and PSG playback so you can hear just Dual PCM running (FM and PSG are still running in the background, they in quite mode): http://mrjester.hapisan.com/01_Public/DualPCMPitchTest2.mp3 I haven't tested this on hardware yet, and I suspect there may be a considerable delay, there's also a lot of testing and some clean-up to do, so stay tuned~ This is so exciting! =D
A small bump (nothing major yet). After some heavy drumbling, a lot of nudging and fudging, and some very precise instruction usage, and abusing undocumented quirks, I have managed to do the following so far: The sample rate playback is currently 20,500Hz and counting (might get it a little higher if you're lucky). The samples are now stored on the ROM in their native unsigned 8-bit format. The output is still the same, as in, you'll still have a 7-bit sample being played, but 8-bit unsigned is stored on the ROM. So you could in reality just include an unsigned 8-bit sample, without the use of the tool I've written. You just have to make sure that the byte "00" isn't used anywhere in the sample (I'm keeping the tool in of course, just for ease of use for noobs, but still...) I "might" include some volume control at the expense of some of that sample rate (let me know your opinion on that). The SMPS structure to DAC channels has changed significantly. Now you select a sample using the F5 flag (F5 XX, where XX is the instrument/sample ID), and you play the note you want from 81 to DF (for pitch/notes). The FA flag will turn on/off the "reverse playback" mode, use once to turn on, use again to turn off. The FB flag will turn on/off the "looping" mode, when the sample reaches the end it'll loop back and play again (will be useful for instruments/sounds that play a sound/note forever with no decay). The E1 and F0 (detune and modulation) flags affect the frequency/pitch of the samples, so you can make it vibrato, or bend or whatever... as well as the E9 flag for pitch changing. Reduced the wait time on the 68k side by a MASSIVE amount of time. All of the above is working with the (still) ability to play any sample, anywhere, any size, anytime, and at near perfect quality on hardware (due to Jester Stream Technology, and the YMCUE Listing System). I have a lot of clean up to do, but so long as you're all patient the coming weeks, you'll be thankful of the features and control available to you on release~ Also, I'm open to feature ideas related to the driver itself, if I can support those ideas, I will. But you have to speak up...
Thanks a lot for the hard-work you provide to us all. Some features? Well, I don't know how much it is possible, but the smooth PCM panning? LINK: http://www.sega-16.com/forum/showthread.php?20704-Undocumented-YM2612-register-2C-Smooth-PCM-panning!
Yeah, I know about that (in fact, my name is plastered all over the thread! Yay! =D). The issue though, is that by enabling this, you convert all FM channels into a DAC channel each, unfortunately, they reference from the same 8-bit port, meaning that they playback exactly the same sound, so there's no variation. But, in regards to the "panning" trick oerg pulled by manipulating the left/right speaker on/off of each channel. Is that, firstly, this will control all sounds flushed in so both PCM channels will be panned the same, rather than individually, though it's still better than absolute panning. But the second issue is a real problem, the fact that it disables FM operator playback means that you are sacrificing 5 FM channels, just for some smooth panning and a slightly louder playback. The third issue, is that you will only experience this quirk on hardware as it was an unknown quirk at the time of those many of public emulators out there being released. So no emulators will support this, this heavily hinders and limits the audience. If the two sounds being flushed could pan individually, then it may have more benefit, we could stuff the entire ROM with sample data, and flush out BGM through one, and SFX through the other, especially given that we potentially have unlimited memory on the cartridge, but again, it all comes down to emulators supporting DTAK on cartridge, to access the Mega CD and 32X memory map as cartridge space, and even memory mappers are limited on emulators. But the lack of individual channel panning (even if not smooth) would have been prefered. tl;dr, this causes more sacrifice than the benefits it's meant to give. Sorry =o