S1-friendly improved Sonic 2 sound driver

Discussion in 'Showroom Archive' started by Clownacy, Apr 19, 2015.

Thread Status:
Not open for further replies.
  1. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,020
    EDIT: The forum update decided to butcher the post's formatting, and make it a monolithic pain in the arse to fix. Just check here for a readable version.

    =======================================

    Have you ever wanted to replace your Sonic 1 sound driver with Sonic 2's? Less buggy, doesn't need loads of (non-Z80) RAM, frees up a little of the 68k, and is more compatible with the original music and SFX than S3K's driver.

    It's certainly possible, but bumps in the road are plentiful, and soon, limitations of S2's driver will get in your way. The driver isn't the most usable: compression woes, the mess of porting the thing in the first place, there are only two music banks, one DAC bank, and a very limited number of music slots. Not to mention, the driver itself has very little room for additions to the code. There's also the total lack of support for Special SFX, which GHZ's waterfall uses, and the MZ push SFX behaviour.

    Over time, there have come guides that correct these, and it seems the only hurdle left is S1 support. The process is a little... involved, so, rather than a guide, I think something pre-made would be better. I might still eventually split some of this into guides, but that's for another time.

    I suppose I should bring up one interesting barrier, sound driver compression: Sonic 2's disasm assembles and compresses the driver at build time. The compression used is Saxman. S3K's and S1's disasms don't do this. Until recently, S1 didn't compress anything at all, but the addition of a disassembled Z80 DAC driver necessitated the addition of a compress-to-Kosinski function, which S3K also has. Changing this would require the user compile a modified s1p2bin. It would be easier to just make the driver settle for Kosinski compression, which I think is better, anyway.

    Anyhow, here's a features list.

    Features:

    • No Saxman-compressed music - Free up a lot of Z80 RAM
    • Driver is Kosinski-compressed - Better compression, so it seems. Also, S1's disasm supports this by default
    • Support for >2 music banks
    • Support for >1 DAC banks
    • Additional bugfixes
    • Smaller FM frequencies table - More RAM
    • S3K PSG frequency range
    • Track RAM optimisation - More RAM
    • Greater sound ID range - $01-$FF
    • No sndDriverInput - Writes are direct, a la S3K
    • Restored broken sound queue - Used for music
    • DAC auto-bankswitch - No need to worry about sample alignment
    • SMPS2ASM
    • Toggleable features - What you do need, and what you don'tSpecial SFX support
    • MZ's block push SFX support
    • CPZ's gloop SFX support
    • Spin dash rev SFX support
    • Sonic 2's PSG envelopes
    • Sonic 2's DAC samples
    Download (code) | Download (ROM)

    S1 VS S2:

    There's a very significant difference between Sonic 1's and 2's drivers: the Mega Drive has two processors - the 68k and the Z80. The 68k is responsible for just about everything; and the Z80 is just the sound processor. Sonic 1 used a driver (SMPS 68k) that was mostly handled by the 68k, which is great for freeing up the Z80, but the game never really took advantage of that, so all it served to do was make a game, that already had performance issues,

    slightly worse off. So, for Sonic 2, the driver was ported to the Z80.
    Despite this, Sonic 2's driver is not your usual SMPS Z80 (the same kind of driver S3K uses). SMPS 68k and SMPS Z80 do some things slightly different, and Sonic 2's driver, being a port of SMPS 68k, still behaves as SMPS 68k. This makes it more compatible with Sonic 1/Sonic 2 music and SFX than S3K's driver, while still being Z80-based. A 'best of both worlds' in some lights.

    Of course, if an S3K music/SFX collection is more your thing, then S3K's driver might be a better choice. The incompatibility goes two ways.
    Installation:

    Note: You'll need a copy of the

    S2 disassembly and flamewing's SMPS2ASM song pack (songs-sfx-v4a.7z). Also, your S1 disasm must be the AS Git branch
    Disassembly files

    s1.sounddriver.asm and the sound folder, then extract the new driver's 7z file to your disasm. Go into the new sound folder and extract the contents of the s1 folder from songs-sfx-v4a.7z into it. Following that, go into the sound folder of the S2 disasm, and copy across the DAC and PCM folders.

    We need to strip out the old driver's files, and replace them. Delete
    sonic.asm

    Open

    sonic.asm. Make sure that OptimiseSound is set to 0. Find the label 'SoundDriver', and replace the line with this:
    include "s2.sounddriver.stuff.asm"
    even
    Find and delete SoundDriverLoad. Our new file contains a replacement. Also, remove the 'bsr.w' to it in GM_Title, and change the 'bsr.w' to it above MainGameLoop to a 'jsr'.

    Remove both 'jsr's to UpdateMusic. These just updated the old driver, the new one will do that on its own.
    sub PlaySound.asm

    Now open

    _incobj/sub PlaySound.asm, and delete PlaySound_Unused. Replace PlaySound and PlaySound_Special with these:
    PlaySound:
    stopZ80 ; Stop the Z80 so the 68k can write to Z80 RAM
    waitZ80
    tst.b (Z80_RAM+zAbsVar.QueueToPlay).l ; If this (zQueueToPlay) isn't $00, the driver is processing a previous sound request.
    bne.s + ; So we'll put this sound in a backup queue
    move.b d0,(Z80_RAM+zAbsVar.QueueToPlay).l ; Queue sound
    startZ80 ; Start the Z80 back up again so the sound driver can continue functioning
    rts
    +
    move.b d0,(Z80_RAM+zAbsVar.SFXUnknown).l ; Queue sound
    startZ80 ; Start the Z80 back up again so the sound driver can continue functioning
    rts
    Code:
    PlaySound_Special:
    		stopZ80						; Stop the Z80 so the 68k can write to Z80 RAM
    		waitZ80
    		tst.b	(Z80_RAM+zAbsVar.SFXToPlay).l		; Is this queue occupied?
    		bne.s	+					; If so, we'll put this sound in a different queue
    		move.b	d0,(Z80_RAM+zAbsVar.SFXToPlay).l	; Queue sound
    		startZ80					; Start the Z80 back up again so the sound driver can continue functioning
    		rts
    +
    		move.b	d0,(Z80_RAM+zAbsVar.SFXStereoToPlay).l	; Queue sound
    		startZ80					; Start the Z80 back up again so the sound driver can continue functioning
    		rts
    
    PauseGame.asm

    Open

    _inc/PauseGame.asm. The new pause system is fairly different from both S1 and S2. Start by replacing this...
    move.b #1,(v_snddriver_ram+f_stopmusic).w ; pause music
    ...with this:

    stopZ80
    waitZ80
    move.b #MusID_Pause,(Z80_RAM+zAbsVar.StopMusic).l ; pause music
    startZ80
    And replace both of these...

    move.b #$80,(v_snddriver_ram+f_stopmusic).w
    ...with this:

    stopZ80
    waitZ80
    move.b #MusID_Unpause,(Z80_RAM+zAbsVar.StopMusic).l
    startZ80
    Constants.asm

    Open

    Constants.asm and remove the following now-useless constants:
    • Size_of_SegaPCM
    • Size_of_DAC_driver_guess
    • z80_dac3_pitch
    • z80_dac_status
    • z80_dac_sample
    Also remove the data under 'Sound driver constants'.

    Now we'll want to adjust the sound ID constants to read the new tables. Find '; Background music', and set bgm__First to $01, then perform the following replacements:

    • '4)' -> '3)'
    • 'MusicIndex' -> 'zMasterPlaylist'
    • 'ptr_musend' -> 'zMusIDPtr__End'
    Set sfx__First to 'bgm__Last+1', then perform the following replacements:

    • '4)' -> '2)'
    • 'ptr_sndend' -> 'SndPtr__End'
    Set spec__First to 'sfx__Last+1', then perform the following replacements:

    • '4)' -> '2)'
    • 'ptr_specend' -> 'SpecPtr__End'
    Replace everything from flg__First to flg__Last with this:

    flg__First equ $FA
    sfx_Stop: equ ((CmdPtr_StopSFX-zCommandIndex)/2)+flg__First
    bgm_Fade: equ ((CmdPtr_FadeOut-zCommandIndex)/2)+flg__First
    sfx_Sega: equ ((CmdPtr_SegaSound-zCommandIndex)/2)+flg__First
    bgm_Speedup: equ ((CmdPtr_SpeedUp-zCommandIndex)/2)+flg__First
    bgm_Slowdown: equ ((CmdPtr_SlowDown-zCommandIndex)/2)+flg__First
    bgm_Stop: equ ((CmdPtr_Stop-zCommandIndex)/2)+flg__First
    flg__Last: equ ((CmdPtr__End-zCommandIndex-2)/2)+flg__First
    Variables.asm

    Open

    Variables.asm, and delete everything from this...
    v_snddriver_ram: = $FFFFF000 ; start of RAM for the sound driver data ($5C0 bytes)
    ...to this:

    ; =================================================================================
    ; From here on, no longer relative to sound driver RAM
    ; =================================================================================
    That $600 bytes of RAM is now completely free.

    Fixing up the SEGA screen:

    Sonic 1 relied on the driver to occupy the 68k and produce good quality PCM playback. This Z80 driver won't be able to do that, so we'll have to alter the SEGA screen to handle things the S2 way, so the menu remains static for long enough, and does not make the chant sound awful.

    In sonic.asm, find Sega_WaitEnd. Change the write to v_vbla_routine above Sega_WaitEnd to #2, and the write below it to #$14. Also change the write to v_demolength to $B4, this will extend how long the screen is to remain idle, letting the SEGA chant play.

    Find VBla_14, and delete the label. After the 'rts', add this:

    VBla_14: ; XREF: VBla_Index
    move.b (v_vbla_count+3).w,d0
    andi.w #$F,d0
    bne.s .skipread

    stopZ80
    waitZ80
    bsr.w ReadJoypads
    startZ80

    .skipread:
    tst.w (v_demolength).w
    beq.w .end
    subq.w #1,(v_demolength).w

    .end:
    rts
    That should do it. As you can see, we're changing which V-Int routine to use, and adjusting it to still read controller input, while still being light on the Z80, so the playback isn't horribly scratchy. But we're not done yet...
    Correcting silence on screenmode change:

    Another change from S1 to S2: Check out S1's GM_Sega, and compare it to S2's SegaScreen. S1 uses the 'sfx' macro (which uses the SFX sound queue), while S2 uses PlayMusic (which uses the music sound queue). In S2's driver, while playing the SEGA chant, the driver will only stop if there's something in the first music queue. Now, this driver currently (v1/v1.1) accounts for both the first music and SFX queue, but, really, it would be better to just use the music queue exclusively.

    Go to the following code, and change the 'sfx' macro to a 'music' macro.

    • GM_Sega
    • GM_Title
    • GM_Ending
    Expanding sound test:

    We need to make the sound test span values $00-$FF. It also wouldn't hurt to add navigational features to buttons A and C.

    In sonic.asm, go to LevelSelect and change this...

    andi.b #btnABC+btnStart,(v_jpadpress1).w ; is A, B, C, or Start pressed?...into this:

    andi.b #btnB+btnStart,(v_jpadpress1).w ; is B or Start pressed?Then change this...

    bne.s LevSel_Level_SS ; if not, go to Level/SS subroutine
    ...into this:

    beq.s .checkB ; if so, branch
    andi.b #btnStart,(v_jpadpress1).w ; is start pressed?
    beq.s LevelSelect ; if not, branch
    bra.s LevSel_Level_SS ; if so, go to Level/SS subroutine

    .checkB:
    andi.b #btnB,(v_jpadpress1).w ; is B pressed?
    beq.s LevelSelect ; if not, branch

    .soundtest:
    After that, remove this line:

    addi.w #$80,d0
    Then delete these lines:

    ; This is a workaround for a bug, see Sound_ChkValue for more.
    ; Once you've fixed the bugs there, comment these four instructions out
    cmpi.w #bgm__Last+1,d0 ; is sound $80-$93 being played?
    blo.s LevSel_PlaySnd ; if yes, branch
    cmpi.w #sfx__First,d0 ; is sound $94-$9F being played?
    blo.s LevelSelect ; if yes, branch

    LevSel_PlaySnd:
    After that, go to LevSel_SndTest, and change this...

    andi.b #btnR+btnL,d1 ; is left/right pressed?
    ...into this:

    andi.b #btnA+btnC+btnR+btnL,d1 ; is left/right/A/C pressed?
    and remove this:

    bhs.s LevSel_Right
    moveq #$4F,d0 ; if sound test moves below 0, set to $4F
    And then change this...

    btst #bitR,d1 ; is right pressed?
    beq.s LevSel_Refresh2 ; if not, branch
    addq.w #1,d0 ; add 1 to sound test
    cmpi.w #$50,d0
    blo.s LevSel_Refresh2
    moveq #0,d0 ; if sound test moves above $4F, set to 0

    LevSel_Refresh2:
    ...into this:

    btst #bitR,d1 ; is right pressed?
    beq.s LevSel_ButtonA ; if not, branch
    addq.w #1,d0 ; add 1 to sound test

    LevSel_ButtonA:
    btst #bitA,d1 ; is A pressed?
    beq.s LevSel_ButtonC ; if not, branch
    addi.b #$10,d0 ; add $10 to sound test
    bcc.s LevSel_ButtonC ; did the addition overflow?
    moveq #0,d0 ; if so, set value to $00

    LevSel_ButtonC:
    btst #bitC,d1 ; is C pressed?
    beq.s LevSel_Refresh2 ; if not, branch
    subi.b #$10,d0 ; subtract $10 from sound test
    bcc.s LevSel_Refresh2
    cmpi.b #$F0,d0
    beq.s LevSel_Refresh2 ; do not set to 0 if already at 0
    moveq #0,d0 ; if the subtraction overflowed, set value to $00

    LevSel_Refresh2:
    Now go to LevSel_DrawSnd, and remove this line:

    addi.w #$80,d0
    Removing needless Z80 stops:

    I think Vladikcomper does well in explaining this one: Just look through sonic.asm for instances of 'stopZ80', 'waitZ80', and 'startZ80', and remove them. They should all be in the V-Int routines and JoypadInit. It just so happens that all instances in stock sonic.asm are needless. Transfer-related stuff, joypad-related stuff; the joypad-related stuff at least had a point, depending on the quality of ROM hardware.
    Changelog:

    v1.0 - 19/04/2015 (DD/MM/YYYY)

    - Followed every goddamn S2 driver guide under the sun

    - Further optimised DAC channel register setting

    - Removed music compression

    - Changed driver's compression to Kosinski

    - Ported some fixes from Sonic 1's driver

    - Optimised track RAM (zTrack.len = 24h)

    - Added SMPS2ASM

    - Restored Special SFX system

    - Restored MZ block push SFX

    - Made S2-exclusive features toggleable; off by default

    - Made some minor optimisations (ix-relative -> absolute)

    - Made Sega chant stop if SFX queue is occupied (S2 only used the music queue - S1 is just stupid)

    - Optimised Sega chant playback loop to make up for the above fix

    - Changed ensure1byteoffset to produce a message instead of a warning (that got annoying fast)

    v1.1 - 24/04/2015

    - Added a new bugfix (PSG fade)

    - Stripped down SMPS2ASM core

    - Added new driver optimisations (size)

    - Improved bugfix descriptions

    - Corrected (my) bug in 1-up music backup ('SFX overriding' bit was not cleared)

    - Optimised some Special SFX code

    - Improved cfOpF9 description

    - Added another bugfix (accidental delay on first frame of music playback)

    - Switched to S3K's tempo algorithm for the above fix

    - Made necessary changes to SMPS2ASM

    - Optimised zMasterPlaylist's entry size (3 bytes)

    - Moved zDACDecodeTbl to align better

    - Changed 'jr' to 'jp' in zWriteToDAC

    - Corrected (my) bug in gloop SFX code being skipped

    - Removed (my) redundant bankswitch under zloc_F1D

    - Renamed 's2.sounddriver.compatibility.asm' to 's2.sounddriver.stuff.asm'
    Credits:

    Valley Bell - Bugfixes, S2Beta4 driver disassembly (source of Push SFX support)

    Flamewing - SMPS2ASM, >$1F songs expansion, pointing out S3K's additional frequency values, improvement for DAC channel register setting

    Vladikcomper - Elements of the DAC-related improvements

    Clownacy - Making the driver good
     
    Last edited by a moderator: Jul 4, 2015
  2. ProjectFM

    ProjectFM Optimistic and self-dependent Member

    Joined:
    Oct 4, 2014
    Messages:
    912
    Location:
    Orono, Maine
    This driver really intrigues me, especially the extra RAM and the ability to have more than 1 DAC bank (I hate having me FAX sound effects cut short by the background music). Unfortunately, I'm using the Hivebrain 2005 disassembly and I have a few questions:


    -Can you convert this guide to Hivebrain?


    -Will this work with MegaPCM?


    -Will the songs I'm currently using with the Sonic 1 sound driver work?


    -Will TheRoboticOverlord (aka TheRoboticDeveloper)'s guide for porting Sonic 3K and 3D songs work with this?
     
  3. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,020
    That's not what a bank is; you're thinking of channels, which this doesn't affect. As far as hardware is concerned, unlike FM or PSG, there is only one DAC channel. So, logically, you can only play one sample at a time. Not that playing two samples at once is completely impossible; you can indeed play two samples, but they have to be played through the same channel. Ristar does this. It uses SMPS 68k, the same kind of driver S1 uses, which frees up the entire Z80, which exclusively handles a mixing DAC driver, which mixes two PCM samples together, and plays that through the lone DAC channel.

    A bank is simply an $8000 byte window that the Z80 has into ROM space, where stuff such as music and DAC samples are kept. Being restricted to one bank means that you can't have more than $8000 bytes' worth of DAC samples, which makes the use of all S3K DACs, or any large collection of custom DAC samples, impossible, as there is not enough room for them.

    As for Hivebrain...

    Even if it did, I'm not interested. When porting my Sonic 2 Clone Driver v2 to the Sonic 2 Xenowhirl disasm and Sonic 1 disassemblies, Hivebrain gave me the most trouble, and, in the end, didn't work anyway.

    Do you know what MegaPCM is? It's a Z80-based DAC driver that occupies the entire Z80. Likewise, Sonic 2's driver is a Z80-based FM/PSG/DAC driver that occupies the entire Z80. If you want to try Frankenstein-ing the two drivers together, be my guest. MegaPCM is designed for use with the 68k-based SMPS 68k, specifically Sonic 1's. Sonic 2's driver, even if it is just a port of S1's driver, is another beast entirely.

    Read up on SMPS2ASM. It allows music and SFXs to be broken down into a form that works with theoretically any driver, so long as the SMPS2ASM core is present, and supports the target sound driver. That is to say, the SMPS2ASM converter tool can process music designed for (stock!) Sonic 1's SMPS 68k sound driver, and the resultant ASM file can be added to this driver with hopefully no problems.

    No. That's designed for porting to a significantly modified version of Sonic 1's driver. S1's and S2's drivers, even unmodified, have different music and SFX formats. That guide also expects you to modify Sonic 1's driver, none of that will work on this driver.
     
    Last edited by a moderator: Apr 23, 2015
  4. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,020
    Update - v1.1

    - Added a new bugfix (PSG fade)
    - Stripped down SMPS2ASM core
    - Added new driver optimisations (size)
    - Improved bugfix descriptions
    - Corrected (my) bug in 1-up music backup ('SFX overriding' bit was not cleared)
    - Optimised some Special SFX code
    - Improved cfOpF9 description
    - Added another bugfix (accidental delay on first frame of music playback)
    - Switched to S3K's tempo algorithm for the above fix
    - Made necessary changes to SMPS2ASM
    - Optimised zMasterPlaylist's entry size (3 bytes)
    - Moved zDACDecodeTbl to align better
    - Changed 'jr' to 'jp' in zWriteToDAC
    - Corrected (my) bug in gloop SFX code being skipped
    - Removed (my) redundant bankswitch under zloc_F1D
    - Renamed 's2.sounddriver.compatibility.asm' to 's2.sounddriver.stuff.asm'

    I've also added a pair of guides: one detailing how to remove unnecessary Z80 stops, improving the Z80's performance; and another on using S2's method of stopping all sound on screenmode change.
     
    Last edited by a moderator: Apr 26, 2015
  5. SaaS

    SaaS Newcomer Trialist

    Joined:
    Mar 4, 2015
    Messages:
    6
    Location:
    NO
    If you do this to S3K, I bet LegendOfRenegade (A video game music remixer.) would be proud.

    Anyways... this is actually pretty nice.
     
    Last edited by a moderator: May 27, 2015
  6. Pacca

    Pacca Having an online identity crisis since 2019 Member

    Joined:
    Jul 5, 2014
    Messages:
    1,175
    Location:
    Limbo
    It probably wouldn't be too difficult to implement the S2 driver into S3K; like the S2 driver, S3Ks' is built solely on the Z80, and due to the fact that S3K was built off of S2, the ways in which the games reference the driver are likely quite similar.
     
  7. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,020
    ...how? Also, bump. Great.

    Anyway, Pacguy, it'd be simpler than this, that's for sure:



    Good luck finding $5C0 bytes of free RAM in S3K.
     
    Last edited by a moderator: Jul 9, 2015
  8. StephenUK

    StephenUK Working on a Quackshot disassembly Member

    Joined:
    Aug 5, 2007
    Messages:
    1,026
    I actually found that video pretty entertaining. The snailbot in particular made me laugh, as the sound effect for the bullets is a bit overkill yet somehow still fits.
     
Thread Status:
Not open for further replies.