Sonic 2 Clone Driver v2

Discussion in 'Engineering & Reverse Engineering' started by Clownacy, Mar 31, 2014.

  1. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    v2.2.4
    • Fixed FM6 fade-in
    • Modified cfFadeToPrevious to work on FM and PSG tracks
    • Optimised some locrets (recycling!)
    • Annotated Vladikcomper's modified WriteFMI(I)
    • Removed a startZ80 that I missed back in v2.0
    • Renamed several constants to be more S1-like
    • Restored original local labels (With tweaks for compatibility)
    • Optimised lea Clone_Driver_RAM into .w
    • Optimised 68k version of PlaySega
    • Ported S3K's NoteFill
    • Optimised some 'bsr & bra' into 'pea & bra'
    • Further optimised the waitYM macro by using tst.b instead of btst
    • Optimised all btst #7 to tst
    • Even further optimised waitYM by moving ym2612_a0 to a register
    • Updated channel RAM addresses description
    • Optimised NoteFillUpdate

    Hey look, optimisations!

    Updating:
    You must overwrite _smps2asm_inc.asm with the new one. It contains a new feature.
     
  2. MarkeyJester

    MarkeyJester

    A D V A N C E Resident Jester
    2,099
    229
    43
    Japan
    Well, if no one else is gonna take a damn interest, I will...

    Congratulations on your fixes and optimisations, how close do you feel to reaching the final goal?
     
  3. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    Oh, don't worry, it gets interest. It's just usually behind-the-scenes. I'm sure people would be a bit more vocal if this was on SSRG. At least, the Q&A thread seems to point to that.

    I feel I reached my goal back in v2.2.1. That was when I replicated all of S2's features. Since then I've been optimising and fixing bugs (and trying to make S3K songs compatible, but that's never going to happen). My buggery with SMPS is far from over, though: I'm getting familiar with SMPS-PCM, and I'm hoping to rebase the Clone Driver to SMPS 68k type 2.
     
  4. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    v2.2.4.1
    • Optimised WriteFMI(I) with a nice trick I picked up from Ristar's driver
    • Optimised some writes to psg_input
    • Optimised all adda.x #x,aN to lea x(aN),aN
    • Optimised FM and PSG's .gotduration by replacing them with SetDuration_pea
    • Added a nop to WaitYM to avoid missed writes
    • Optimised PSG flags by reordering their checks in order of most-to-least common
    • Replaced a 'branch to rts' with an rts
    • Restored the music and SFXs to their 'as-of-2011' state. They shouldn't be modified in the first place
    • Added to _smps2asm_inc.asm to fix DAC typo

    Hey, I did it: SMPS 68k is now too fast! First there was the concern about the optimised PlaySega being too fast, and now, I managed to make WriteFMI fast enough to rocket ahead of the YM2612. Anyone still using 2.2.4, try running your hack on hardware to see what I mean. It's fixed here, but still...

    I've just about optimised this to the best of my abilities, so I thought I'd wrap these all together before I get to bug-fixing and feature-adding, which are gonna take much longer to do.

    Updating:
    You must overwrite _smps2asm_inc.asm with the new one. It contains a compatibility fix.
     
  5. redquebec

    redquebec

    Member
    39
    0
    6
    Thanks again and again Clownacy! :)
     
  6. Very exciting! :) Any planned optimizations? It's fantastic to see the Clone Driver made into something not so broken. I'll probably have a peek at it later when I get home.
     
  7. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    As I said, the driver's just about as optimised as I can get it. Not without overhauling major components, that is. But that'll have to wait for when I can effectively work with the hardware. It took me until now to realise how easy it is for the 68k to be too quick for the YM2612, so there's a lot of ground to cover.

    I've decided to start cracking down on the bugs in this driver inherited from S1's driver. Those have been nothing but grief since the beginning. For example, play sound $FB, fade music, and then $0A, DEZ's theme. The PSG goes mad. Silver Sonic's reving plays noise, too.
     
  8. Super Egg

    Super Egg

    Master of MS Paint. Member
    311
    0
    16
    Tomball, TEXAS
    Sonic 2 beta 3 hoax
    Funny, I don't have that issue on my S2B z80 driver =P

    Jokes aside, nice to see you finally finished this thing. Seems you've beat me in the race of "Who can release an alternative to the Original Clone Driver or Flamewing driver."
     
  9. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    Time for more messing around with Sonic 1's driver! There's been a comment in the Clone Driver v2 for some time mentioning this, but I never went into much detail.

    Standard SMPS 68k Type 1a/1b/2 has a smaller FM_Notes table (array?) than what's in Sonic 1. This is achieved through additions to FMSetFreq. You can backport this by doing the following:

    First, let's study a Type 2's FMSetFreq. Here's Super Shinobi II's (ValleyBell disasm):

    Code (ASM):
    1. GetFMFreq:              ; CODE XREF: ProcessFMTrack+20p
    2.  
    3. ; FUNCTION CHUNK AT 00069996 SIZE 00000008 BYTES
    4.  
    5.         subi.b  #$80,d5
    6.         beq.s   loc_69996
    7.         add.b   8(a5),d5
    8.         andi.l  #$7F,d5
    9.         divu.w  #$C,d5  ; new instruction
    10.         swap    d5  ; new instruction
    11.         lsl.w   #1,d5
    12.         lea FMFreqs,a0
    13.         move.w  (a0,d5.w),d6
    14.         swap    d5  ; new instruction
    15.         andi.w  #7,d5   ; new instruction
    16.         moveq   #$B,d0  ; new instruction
    17.         lsl.w   d0,d5   ; new instruction
    18.         or.w    d5,d6   ; new instruction
    19.         move.w  d6,$10(a5)
    20.         rts
    21. ; End of function GetFMFreq
    As you can see, there are several new instructions. These use FM_Notes/FMFreqs, not just as raw data, as Sonic 1 does, but as base data, to which the input note is added to, calculating the appropriate octave. Because of this, data for each and every octave is no longer needed, allowing you to get away with a much smaller table, which covers only the one.

    Let's see about fitting this in Sonic 1's driver (modified SMPS 68k Type 1b).

    Code (ASM):
    1. FMSetFreq:
    2.         subi.b  #$80,d5         ; Make it a zero-based index
    3.         beq.s   TrackSetRest
    4.         add.b   zTrackKeyOffset(a5),d5  ; Add track key displacement
    5.         andi.w  #$7F,d5         ; Clear high byte and sign bit
    6.         ; first set of new instructions goes here
    7.         lsl.w   #1,d5
    8.         lea FM_Notes(pc),a0
    9.         move.w  (a0,d5.w),d6
    10.         ; second set of new instructions goes here
    11.         move.w  d6,zTrackFreq(a5)   ; Store new frequency
    12.         rts
    13. ; End of function FMSetFreq
    Self explanatory. Next up is replacing Sonic 1's FM_Notes with Super Shinobi II's FMFreqs:

    Find FM_Notes and delete all but the first dc.b line. That's all.

    Note that this can be optimised a little, not the additions, but the stock code. For starters, the 'lsl.w #1,d5' can be replaced with 'add d5,d5', which is faster. Also, if you move FM_Notes closer to FMSetFreq, you can replace 'lea FM_Notes(pc),a0' and the line after it with 'move.w FM_Notes(pc,d5.w),d6' (in fact, if you didn't add the new code, you could make this line write straight to zTrackFreq(a5) instead of using d6 to do it).

    Because this adds cycles to a very central piece of code, I won't be adding this to the Clone Driver v2, which is why I've made this post; if you want to apply this change, you can use this post to do so yourself.

    EDIT: Huh. Turns out this isn't a Type 2-only feature. Type 1b and even Type 1a have this. It seems that Sonic Team deliberately made it this way. Weird. Guess they were trying to optimise the code.
     
  10. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    These rambling posts should probably be moved to their own topic... if any higher ups think so, don't let me stop you.

    A bug fix!

    Anyone who's tried to port VVZ act 1's music to S1 or S2's driver will be met with an unusual bug: Some channels lag behind (there's also a bug with notes and rests, but I won't cover that here). The cause is that some coord. flags are used incorrectly. Namely two FM-only flags on a PSG channel. The reason why S3K's driver doesn't suffer from any side-effects is because it has some additional code to deal with this. We'll be porting this code over to both of these drivers, creating a workaround for this error. You could correct the flags in the music itself, but we're adding this for 'catch all' sake.

    First, let's examine cfSetPSGTone...

    cfSetPSGTone

    S1 (modified early(?) SMPS 68k Type 1b):
    Code (ASM):
    1. ; loc_72E26:
    2. cfSetPSGTone:
    3.         move.b  (a4)+,zTrackVoiceIndex(a5)  ; Set current PSG tone
    4.         rts

    S2 (modified Z80 port of S1's driver):
    Code (ASM):
    1. ;zloc_F8E
    2. cfSetPSGTone:
    3.     ld  (ix+8),a        ; Set current PSG tone
    4.     ret

    S3K (modified SMPS Z80 Type 2):
    Code (ASM):
    1. ;loc_E58
    2. cfSetPSGTone:
    3.         bit 7, (ix+zTrackVoiceControl)      ; Is this a PSG track?
    4.         ret z                               ; Return if not
    5.  
    6. ;loc_E5D
    7. cfStoreNewVoice:
    8.         ld  (ix+zTrackVoiceIndex), a        ; Store voice
    9.         ret


    While S1 and S2 are largely the same, S3K has a check for what channel is using the flag. This needs porting.

    S1:
    Code (ASM):
    1. ; loc_72E26:
    2. cfSetPSGTone:
    3.         move.b  (a4)+,d0    ; Set current PSG tone
    4.         tst.b   zTrackVoiceControl(a5)      ; Is this a PSG track?
    5.         bpl.s   +               ; Return if not
    6.         move.b  d0,zTrackVoiceIndex(a5) ; Set current PSG tone
    7. +       rts

    S2:
    Code (ASM):
    1. ;zloc_F8E
    2. cfSetPSGTone:
    3.     bit 7, (ix+1)       ; Is this a PSG track?
    4.     ret z           ; Return if not
    5.     ld  (ix+8),a        ; Set current PSG tone
    6.     ret


    That's that done. But that's only one of them: we still need to add to the FM flag, cfSetVoice.

    cfSetVoice

    Again, here's a comparison of the three...

    S1:
    Code (ASM):
    1. ; loc_72C26:
    2. cfSetVoice:
    3.         moveq   #0,d0
    4.         move.b  (a4)+,d0        ; Get new voice
    5.         move.b  d0,zTrackVoiceIndex(a5) ; Store it
    6.         btst    #2,zTrackPlaybackControl(a5) ; Is SFX overriding this track?
    7.         bne.w   locret_72CAA        ; Return if yes
    8.         movea.l v_voice_ptr(a6),a1  ; Music voice pointer
    9.         tst.b   f_voice_selector(a6)    ; Are we updating a music track?
    10.         beq.s   SetVoice        ; If yes, branch
    11.         movea.l zTrackVoicePtr(a5),a1   ; SFX track voice pointer
    12.         tst.b   f_voice_selector(a6)    ; Are we updating a SFX track?
    13.         bmi.s   SetVoice        ; If yes, branch
    14.         movea.l v_special_voice_ptr(a6),a1 ; Special SFX voice pointer

    S2:
    Code (ASM):
    1. ;zloc_E03
    2. cfSetVoice:
    3.     ld  (ix+8),a            ; Set current voice
    4.     ld  c,a                 ; a -> c (saving for later, if we go to cfSetVoiceCont)
    5.     bit 2,(ix+0)            ; If "SFX is overriding this track" bit set...
    6.     ret nz                  ; .. return!
    7.     push    hl              ; Save 'hl'
    8.     call    cfSetVoiceCont  ; Set the new voice!
    9.     pop hl                  ; Restore 'hl'
    10.     ret

    S3K:
    Code (ASM):
    1. ;loc_D2E
    2. cfSetVoice:
    3.         bit 7, (ix+zTrackVoiceControl)      ; Is this a PSG track?
    4.         jr  nz, zSetVoicePSG                ; Branch if yes
    5.         call    zSetMaxRelRate              ; Set minimum D1L/RR for channel
    6.         ld  a, (de)                         ; Get voice index
    7.         ld  (ix+zTrackVoiceIndex), a        ; Store to track RAM
    8.         or  a                               ; Is it negative?
    9.         jp  p, zSetVoiceUpload              ; Branch if not
    10.         inc de                              ; Advance pointer
    11.         ld  a, (de)                         ; Get song ID whose bank is desired
    12.         ld  (ix+zTrackVoiceSongID), a       ; Store to track RAM and fall-through


    Once again, S3K has a check (and even a new subroutine, but we don't need that). Let's port this.

    S1:
    Code (ASM):
    1. ; loc_72C26:
    2. cfSetVoice:
    3.         moveq   #0,d0
    4.         move.b  (a4)+,d0        ; Get new voice
    5.         move.b  d0,zTrackVoiceIndex(a5) ; Store it
    6.         tst.b   zTrackVoiceControl(a5)  ; Is this a PSG track?
    7.         bmi.s   locret_72CAA        ; Return if yes
    8.         btst    #2,zTrackPlaybackControl(a5) ; Is SFX overriding this track?
    9.         bne.w   locret_72CAA        ; Return if yes
    10.         movea.l v_voice_ptr(a6),a1  ; Music voice pointer
    11.         tst.b   f_voice_selector(a6)    ; Are we updating a music track?
    12.         beq.s   SetVoice        ; If yes, branch
    13.         movea.l zTrackVoicePtr(a5),a1   ; SFX track voice pointer
    14.         tst.b   f_voice_selector(a6)    ; Are we updating a SFX track?
    15.         bmi.s   SetVoice        ; If yes, branch
    16.         movea.l v_special_voice_ptr(a6),a1 ; Special SFX voice pointer

    S2:
    Code (ASM):
    1. ;zloc_E03
    2. cfSetVoice:
    3.     ld  (ix+8),a            ; Set current voice
    4.     bit 7,(ix+1)            ; Is this a PSG track?
    5.     ret nz              ; Return if yes
    6.     ld  c,a                 ; a -> c (saving for later, if we go to cfSetVoiceCont)
    7.     bit 2,(ix+0)            ; If "SFX is overriding this track" bit set...
    8.     ret nz                  ; .. return!
    9.     push    hl              ; Save 'hl'
    10.     call    cfSetVoiceCont  ; Set the new voice!
    11.     pop hl                  ; Restore 'hl'
    12.     ret

    Done. Wouldn't be surprised if this affects other songs.
     
  11. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    More on using a smaller FM frequencies table, this time for S2's driver. Also, info on a strange bug in S3K's driver.

    Because of the nature of SMPS Z80 drivers, you may prioritise size over speed. If so, you may want to use a much smaller FM frequencies table, as seen in S3K's driver (modified SMPS Z80 Type 2). Despite being cousins in terms of relation (S2's driver is a Z80 port of an evolved SMPS 68k), S3K's code works near-perfectly in S2's driver, so we'll see about porting across this feature, using the very code S3K uses.

    First, you need to follow the driver-related instructions here to relocate the frequency tables, otherwise the DAC playback will be broken. Note that adding the S3K frequencies is optional, and not required for this.

    zFMSetFreq, in s2.sounddriver.asm. Above 'add a,a', add this code:

    Code (ASM):
    1.     ld  d, 8            ; Each octave above the first adds this to frequency high bits
    2.     ld  e, 0Ch          ; 12 notes per octave
    3.     ex  af, af'         ; Exchange af with af'
    4.     push    af
    5.     xor a           ; Clear a (which will clear a')
    6.  
    7. -   ex  af,af'          ; Exchange af with af'
    8.     sub e           ; Subtract 1 octave from the note
    9.     jr  c,+         ; If this is less than zero, we are done
    10.     ex  af,af'          ; Exchange af with af'
    11.     add a,d         ; One octave up
    12.     jr  -           ; Loop
    13.  
    14. +
    15.     add a,e         ; Add 1 octave back (so note index is positive)
    (You can find the unmodified version of this code in S3K's driver under zGetNextNote_cont)

    This is responsible for the bulk of the calculation. In S2's driver, af' is used by the DAC playback, so we'll need to preserve its contents, as done by the 'push af'.

    After 'ld de,(zFrequencies)', add in this code:

    Code (ASM):
    1.     ex  af, af'         ; Exchange af with af'
    2.     or  d           ; a = high bits of frequency (including octave bits, which were in a)
    3.     ld  d, a            ; h = high bits of frequency (including octave bits)
    4.     pop af
    5.     ex  af, af'         ; Exchange af with af'
    This code has seen significant modification from its source: using the correct register for the pointer, and restoring af'.

    Now navigate to zFrequencies, and remove all but the first line of values.

    You should now be clear to build and test. However, you will likely notice one strange bug: the spin dash release sound will play incorrectly. Analysis of S3K's copy of this sound shows that it has been modified from its S2 form. The most notable change is this:

    In s2.asm, go to Sound3C. The value '$9000' has been changed to '$0000'. Making this change to your sound will fix the bug. But we will not leave it at that. Let the value remain $9000.

    What that modification does is change the FM5 channel's pitch from $90 to $00. This pitch of $90 is unusual, as most other SFXs use values much closer to $00. It is possible that this value is a mistake.

    You see, $90 is $10 with the sign bit set. $10 being much closer to $00, and even sounds exactly the same if used instead of $90. What pushes the idea of this being an error further is that, in an unmodified zFMSetFreq, the sign bit would be lost in the calculations. Most obviously at 'add a,a', where the sign bit would be lost to overflow. Our problem is that the code we ported from S3K doesn't have a chance to remove the sign bit, so it counts the value's octave incorrectly, leading to the sound playing incorrectly. While we could change the pitch value of the sound itself, a safer, more effective solution would be to modify S3K's code to remove the sign bit of the value:

    Back in zFMSetFreq, above 'ld d, 8', add the instruction 'res 7,a'. This will remove the troublesome bit before the calculations are made. Test this new build to find that the bug has been fixed.

    Note that this bug exists in S3K's driver. This can be seen by importing S2's spin dash release sound to the driver, and trying to play it.
     
  12. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    A while ago, I pushed the v2.2.4.1b update, which added Sonic 1 support. It wasn't too great, so I kept quiet on it. I intended to quickly replace it with a better one, but got caught up on the whole 'lay off the tiny updates' thing. Two months later, still not much to show off. I guess the best part is better S3K compatibility.

    Anyway, v2.2.5:
    • Updated to latest Git (fixing a newly-pointed-out bug along the way)
    • Merged all compatibility layers into s1.sounddriver.compatibility.asm
    • Moved v2.2.4.1b's toggles to s1.sounddriver.compatibility.asm
    • Reverted some dangerous 'moveq's to 'move.b's
    • Optimised a branch under PSGUpdateVolFX
    • Optimised a branch under VolEnv_Reset
    • Removed a branch under VolEnv_Off by moving it above the branch's target
    • Size-optimised MegaPCM, reducing the uncompressed binary's size from 210h to 1E5h
    • Added another nop instruction to waitYM (the beginning of S2's title screen theme plays a goofy note in Regen with just the one)
    • Optimised FMSetFreq by making it write directly from FM_Notes to zTrackFreq
    • Replaced the 'clr.b's under bgmnot1up with a faster moveq/move.b combination
    • Removed the redundant 'clr.b v_sndprio'
    • Optimised Sound_PlayBGM and Sound_PlaySpecial by making them write their voice pointers directly to v_voice_ptr/v_special_voice_ptr (old leftover from v2.1's longword voice pointer hack)
    • Optimised SpeedUpMusic, SpeedUpMusic_1up, SlowDownMusic and SlowDownMusic_1up with some register usage
    • Optimised some zTrackVolume(a5) usage under DoFadeOut
    • Optimised .bgm_psgloadloop by using an addq to skip the 'redundant' byte
    • Optimised Sound_ChkValue by removing the redundant check for Sound Commands
    • bsr -> bra in .specfmdone
    • Renamed all s1.sounddriver files and moved them to the sound folder
    • Changed which v_sndprio clear is used in Sound_PlayBGM (Sonic 2 uses the last one)
    • Added new waitYMspec macro that uses parameter as target for tst
    • Used above modification to optimise WriteFMI
    • Optimised cfSetPSGNoise with better register use
    • Rearranged files at end of driver (Mega PCM, PSGs, music, sounds, others) to put many within 'SoundIndex(pc)' range
    • Fixed oversight in cfNoteFillS3K (d1 isn't loaded if tempo divider = 1), increasing compatibility with S3+ music
    • Added two missing S1 equates
    • Excluded nops from waitYM loop
    • Used more appropriate locret under FMNoteOn
    • Added S3K tweak to cfSetPSGTone and cfSetVoice, increasing compatibility with S3+ music

    It seems that for the Xeno disasm users, they'll need to update their copy of AS: when I tried building, I got errors about the local labels.

    Updating:
    You must overwrite _smps2asm_inc.asm with the new one. You'll need to rename some of your files, also:
    1. s1.sounddriver.music.asm - Sonic 2 Clone Driver v2 - Music.asm
    2. s1.sounddriver.sounds.asm - Sonic 2 Clone Driver v2 - Sounds.asm
    3. s1.sounddriver.other.asm - Sonic 2 Clone Driver v2 - Other.asm
    These will need moving to the sound folder. You can delete the other s1.sounddriver.whatever files.

    In s2.asm, find the 'include "s1.sounddriver.asm"', and replace it with 'include "sound\Sonic 2 Clone Driver v2 - Compatibility.asm"'.
     
  13. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    v2.2.5.1
    • Updated SMPS2ASM

    Yay, unintentional coattail riding!

    Anyway, this should do well in reversing some, if not all, of the damage done by expanding the PSG frequencies table way back when.

    Updating:
    You must overwrite _smps2asm_inc.asm with the new one; it's been updated.

    Some music and SFXs have been updated, also. Check the timestamp of them, and replace where necessary.

    As for updating your own music and SFXs... it seems the only change is the use of nMaxPSG and the sort. This should just amount to searching for, and equating, the correct notes. You can also assemble the files in S1's or one of the others' formats, and running it back through the new SMPS2ASM tool.
     
  14. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    v2.2.5.2
    • Added waitZ80 macro, for S1 compatibility
    • Added cfSendFMI
    • Replaced cfOpF9 with above
    • Added cfChanFMCommand
    • Added some flag documentation from S3K and flamewing's driver
    • Better-formatted Sonic 2 Clone Driver v2 - Compatibility.asm
    • Relocated DAC ID equates from _smps2asm_inc.asm to Sonic 2 Clone Driver v2 - Compatibility.asm
    • Made said IDs dynamic, in the same way as the music and SFXs
    • Corrected IDs to account for absent dHipHopHitKick3
    • Added id function to Sonic 2 Clone Driver v2 - Compatibility.asm

    Turns out the DAC equates have been broken for a while. Ditching the use of enums, and switching the system S2 uses for its music, should make it so that this never happens again. Also, despite having the same name, the stopZ80 macros in S1 and S2 do not do the same thing. Nice.

    Maybe I should actually get to fixing those bugs now...

    Updating:
    Replace...
    • _smps2asm_inc.asm
    • Sonic 2 Clone Driver v2.asm
    • Sonic 2 Clone Driver v2 - Constants.asm
    • Sonic 2 Clone Driver v2 - Compatibility.asm
    • MegaPCM.asm
     
  15. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    v2.2.6
    • Sonic 2-ifications
      • Added to FMUpdateTrack and PSGUpdateTrack the way S2's driver does
      • Made TrackSetRest no longer continue to FinishTrackUpdate
      • Fixed modulation on rests by adding a check to DoModulation (noticeable at start of ARZ's theme after it's looped)
      • Relocated a check in FMPrepareNote to occur a little earlier
      • Changed a reference to a locret in Sound_PlayBGM with one to .bgm_loadMusic, which should make it so that the extra life music can interrupt itself
      • Made .bgmnot1up clear v_fadeout_counter
      • .bmg_fmloadloop and .bgm_psgloadloop now set the 'track at rest' bit, avoiding hanging notes (noticable at start of DEZ's theme)
      • Optimised .silencefm6 using some S2 logic
      • Sound_PlaySFX no longer checks v_fadeout_counter
      • FadeOutMusic no longer calls FadeOutSFX
      • SpeedUpMusic and SlowDownMusic no longer set v_main_tempo_timeout
      • PSGSetVolume now corrects values that are >=$10
      • PSGSetFreq and .restpsg don't continue to FinishTrackUpdate
      • An 'add' in PSGSendVolume is replaced with an 'ori'
      • cfPanningAMSFMS has an additional check
      • Added a (commented out) call to FadeOutSFX in Sound_PlayBGM
    • .nospeedshoes no longer sets v_main_tempo_timeout to the header tempo. This is to avoid unintended tempo overflow on the first frame of playback, delaying the song by a frame. This also helps to prevent hanging notes (noticable at start of DEZ's theme)
    • Removed useless 'even' from under FMDACInitBytes
    • Removed last '0's from FMDACInitBytes and PSGInitBytes (were actually 'even's)

    Finally, the bugs are gone.

    Anyone who's used previous versions of this driver, even v2.0, may have noticed that the start of DEZ's theme has this strange tone. They might also have noticed that the start of ARZ's theme, after already looping once, has these extremely high-pitched noises in the right channel. With this version, they're finally gone. The result of a pair of bugs in S1's driver: modulation on rests, and hanging notes. In lieu of an actual explanation, the changelog should give you the pointers to where the fixes are.

    If you want to hear the bugs, check this ROM.

    Updating:
    Replace...
    • _smps2asm_inc.asm
    • Sonic 2 Clone Driver v2.asm
     
  16. DigitalDuck

    DigitalDuck

    Arriving four years late. Member
    5,114
    262
    63
    Lincs, UK
    TurBoa, S1RL
    Famous last words.
     
  17. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    Here's an update that's been collecting dust since April.

    v2.3
    • Made FadeOutSFX use d6 instead of d7, so that it doesn't conflict with Sound_PlayBGM
    • Optimised PSG envelopes with more efficient use of flags
    • Made previous version's bugs toggleable with Fix_DriverBugs
    • Added Valley Bell's Sega chant pan fix
    • Added Valley Bell's 0 FM track fix
    • Added Valley Bell's cfFadeInToPrevious PSG noise type fix
    • Added a (commented out) call to FadeOutSpecSFX in Sound_PlayBGM
    • Un-commented-out the calls under Sound_PlayBGM
    • S2-ified FadeOutSFX
    • Corrected StopSoundAndMusic comment
    • Improved cfFadeInToPrevious FM6 fix
    • Reduced RAM usage by employing a trick from S&K's driver: the SFX/special SFX share RAM with the 1up music backup
    • Switched to using 'STRUCT' to define track RAM
    • Defined driver RAM with phase/ds.b combo
    • Split v_1up_ram_copy into v_1up_ram_copy and v_1up_variables (variables now go after tracks)
    • As part of the SFX/track backup share, the playback control bytes of all tracks are separately backed-up
    • Made Sound_PlayBGM's extra life code clear the special SFX tracks' 'is playing' bit
    • Special SFX code above FMDACInitBytes now uses tst.b instead of tst.w
    • Moved v_special_voice_ptr back next to v_voice_ptr (RAM is dynamic now, so it can go here without wasting any RAM if unused)
    • Made continuous SFX RAM toggleable
    • Reduced zTrack size to $2E by changing LoopCounters from 4 bytes to 2 bytes, as S&K's driver has it
    • Reduced zTrack size to $2C with some track-specific RAM usage
    • FinishTrackUpdate no longer clears VolFlutter on non-PSG tracks
    • Removed now-useless label (loc_721B6)

    Anyhow, this is a major update because of the massive RAM improvement (and it's about time the v2.2.X line died off).

    Now, where's my feedback saying that I'm a god that should be worshipped?
     
  18. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    I didn't intend for this update to come December 1st. Merry Christmas, I guess.

    v2.4
    • Added Vladikcomper's fix for interrupt crash, and optimisation of horizontal interrupts
    • Made Mega PCM assemble with the rest of the driver
    • Merged DAC_Table into Mega PCM source code
    • Split 'Sonic 2 Clone Driver v2.asm' from 'Sonic 2 Clone Driver v2 - Compatibility.asm', because of how constants are now generated
    • Added DAC volume control
    • Removed some excessive optimisation
    • Removed needless 'Clownacy |' comments
    • Rearranged constants and flags, allowing DAC ID constants to be used in your hack's code
    • Switched back to S1's Sega chant sample (S2's is just S1's with a bit cut off at the end)
    • Fixed mistake in Mega PCM's code I made at some point (an iy+3 was changed to iy+2, breaking panning)
    • Made everything case-sensitive-friendly

    v2.3 didn't live very long, did it? Didn't even see a minor update. On that topic, I'm listing this as a major update because of the massive changes to the Mega PCM DAC driver. As you can see, DAC volume control has been added, at a fairly major cost of cycles in the DPCM loop. If only I had one more 16-bit reg... PCM playback wasn't very heavily affected (7 cycles, as opposed to DPCM's 19 :/), so there's that, at least.

    I got most of this feature's code from SMPS 32x, a variant of SMPS 68k that saw use on a few 32x games. Oddly, its DAC driver supported automatic bankswitching and volume control. There are some things, however, that have been changed from the original code. One thing is how DAC volume is actually reset when a new song starts. The 32x games had to force it manually with a coordination flag at the start of the song. Another change is how DAC volume only applies to DACs originating from a song's DAC track. In the original driver, if a song stylistically made its DACs quiet, then DAC SFX played in gameplay would go quiet too, and since the value that decides DAC volume is the volume byte of the DAC track RAM, it's pretty clear the volume was intended for music DAC alone.

    But, yeah, the DAC channel fades with the rest of the song, now... have fun!

    Oh, volume is also controllable with a coordination flag, smpsFMAlterVol/cfChangeFMVolume. No additional parameters or anything, just use it on a DAC track, and Bob's your uncle.

    One thing SMPS 32X does that this doesn't is mid-sample playback volume change. As an optimisation, I made the volume only update at the start of a sample.

    Updating:
    Files changed from v2.3:
    • _smps2asm_inc.asm
    • MegaPCM.asm (MegaPCM - 68k.asm)
    • MegaPCM - Z80.asm
    • Sonic 2 Clone Driver v2 - Compatibility.asm
    • Sonic 2 Clone Driver v2.asm

    I imagine updating from v2.3 to v2.4 is a colossal pain in the ass. Mega PCM is no longer pre-assembled, and depends on a modified s2p2bin.exe. Source and a binary are provided with the driver, so that shouldn't be too much of a problem. DAC_Table has been moved from Mega PCM.asm (now Mega PCM - 68k.asm) to Mega PCM's source (Mega PCM - Z80.asm), and custom DPCMs' pitches will have to be reduced by 2 thanks to the cycle cost of the volume control.

    Sonic 2 Clone Driver v2.asm, Sonic 2 Clone Driver v2 - Compatibility.asm, and Sonic 2 Clone Driver v2 - Constants.asm have all been split apart and rearranged, allowing DAC ID constants to be used in your hack's code, for use with PlaySample.

    At the start of s2.asm, after 'include "s2.macros.asm"', paste this:

    Code (ASM):
    1. ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    2. ; Clone Driver v2 flags and stuff
    3.  
    4. TargetDisasm    = 1
    5. ;   | 1 for Sonic 2 Git
    6. ;   | 2 for Sonic 2 Xenowhirl
    7. ;   | 3 for Sonic 1 Git
    8. ;
    9. SegaPCM_68k = 0
    10. ;   | If 0, the Z80 and Mega PCM handle the SEGA sample playback. If 1, the 68k handles it. I recommend Z80, as with the 68k version, if overclocked, the sound plays wrongly.
    11. ;
    12. EnableContSFX   = 0
    13. ;   | If 1, include S3K's continuous SFX system.
    14. ;
    15. First_ContSFX   = $BC
    16. ;   | Set this to the sound ID of your first continous SFX. This ID must be a higher number than any of your normal SFXes.
    17. ;   | (Default value is S&K's)
    18. ;
    19.     include "sound/Sonic 2 Clone Driver v2 - Compatibility.asm"
    20.     include "sound/Sonic 2 Clone Driver v2 - Constants.asm"


    If you're using Xenowhirl's 2007 disasm, place it after the 'include "s2.macrosetup.asm"'. For S1 Git, place it after 'include "Macros.asm"'.



    PlaySample also needs modifying. It needs to communicate with Mega PCM that the sample it's sending it is an SFX DAC, and not part of the music. Just change it to this:

    S2
    Code (ASM):
    1. PlaySample:
    2.     stopZ80
    3.     st.b    (z80_dac_type).l    ; This is a DAC SFX; ignore music DAC volume
    4.     move.b  d0,(z80_dac_sample).l
    5.     startZ80
    6.     rts
    S1
    Code (ASM):
    1. PlaySample:
    2.     stopZ80
    3.     waitZ80
    4.     st.b    (z80_dac_type).l    ; This is a DAC SFX; ignore music DAC volume
    5.     move.b  d0,(z80_dac_sample).l
    6.     startZ80
    7.     rts



    Also, because DAC_Table is assembled and compressed with the rest of the driver, SoundDriverLoad needs changing, too.

    S2:
    Code (ASM):
    1. JmpTo_SoundDriverLoad   ; Misnomer
    2. SoundDriverLoad:
    3.         move.w  #$100,(Z80_Bus_Request).l       ; stop the Z80
    4.         move.w  #$100,(Z80_Reset).l             ; reset the Z80
    5.  
    6.         ; load Mega PCM (kosinski-compressed)
    7.         lea     (MegaPCM).l,a0                  ; source
    8.         lea     (Z80_RAM).l,a1                  ; destination
    9.         bsr.w   KosDec
    10.  
    11.         moveq   #0,d1
    12.         move.w  d1,(Z80_Reset).l
    13.         nop
    14.         nop
    15.         nop
    16.         nop
    17.         move.w  #$100,(Z80_Reset).l             ; reset the Z80
    18.         move.w  d1,(Z80_Bus_Request).l          ; start the Z80
    19.         rts
    20. ; End of function SoundDriverLoad
    S1
    Code (ASM):
    1. SoundDriverLoad:
    2.                 stopZ80
    3.                 resetZ80
    4.  
    5.                 ; load Mega PCM (kosinski-compressed)
    6.                 lea     (MegaPCM).l,a0                  ; source
    7.                 lea     (z80_ram).l,a1                  ; destination
    8.                 bsr.w   KosDec
    9.  
    10.                 moveq   #0,d1
    11.                 move.w  d1,(z80_reset).l
    12.                 nop
    13.                 nop
    14.                 nop
    15.                 nop
    16.                 resetZ80
    17.                 move.w  d1,(z80_bus_request).l          ; start the Z80
    18.                 rts
    19. ; End of function SoundDriverLoad



    All the H-Int trickery has to be removed, too.

    S2
    Find VintRet and replace this...

    Code (ASM):
    1.         jsr     (UpdateMusic).l                 ; update Sonic 2 Clone Driver
    2.  
    3. VintRet_NoMusic:


    ...with this:

    Code (ASM):
    1.     move    #$2300,sr           ; enable interrupts (we can accept horizontal interrupts from now on)
    2.     bset    #0,(SMPS_running_flag).w    ; set "SMPS running flag"
    3.     bne.s   +               ; if it was set already, don't call another instance of SMPS
    4.     jsr (UpdateMusic).l         ; update Sonic 2 Clone Driver v2
    5.     clr.b   (SMPS_running_flag).w       ; reset "SMPS running flag"
    6. +


    Go to loc_748 and delete these lines:

    Code (ASM):
    1.     cmpi.b  #$5C,(Hint_counter_reserve+1).w
    2.     bhs.s   Do_Updates
    3.     move.b  #1,(Do_Updates_in_H_int).w
    4.     rts
    5.  
    6. ; ---------------------------------------------------------------------------
    7. ; Subroutine to run a demo for an amount of time
    8. ; ---------------------------------------------------------------------------
    9.  
    10. ; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
    11.  
    12. ; sub_7E6: Demo_Time:
    13. Do_Updates:


    Find PalToCRAM, and delete this:

    Code (ASM):
    1.     tst.b   (Do_Updates_in_H_int).w
    2.     bne.s   loc_1072


    ...and this:

    Code (ASM):
    1. loc_1072:
    2.     clr.b   (Do_Updates_in_H_int).w
    3.     movem.l d0-a6,-(sp)
    4.     bsr.w   Do_Updates
    5.     jsr (UpdateMusic).l
    6.     movem.l (sp)+,d0-a6
    7.     rte


    Then open s2.constants.asm, and rename 'Do_Updates_in_H_int' to 'SMPS_running_flag'
    S1
    Find VBla_Music and replace this...

    Code (ASM):
    1.         jsr (UpdateMusic).l


    ...with this:

    Code (ASM):
    1.         move    #$2300,sr       ; enable interrupts (we can accept horizontal interrupts from now on)
    2.         bset    #0,($FFFFF64F).w        ; set "SMPS running flag"
    3.         bne.s   VBla_Exit       ; if it was set already, don't call another instance of SMPS
    4.         jsr (UpdateMusic).l     ; update Sonic 2 Clone Driver v2
    5.         clr.b   ($FFFFF64F).w       ; reset "SMPS running flag"


    Go to VBla_08 and delete these lines:

    Code (ASM):
    1.         cmpi.b  #96,(v_hbla_line).w
    2.         bhs.s   Demo_Time
    3.         move.b  #1,($FFFFF64F).w
    4.         addq.l  #4,sp
    5.         bra.w   VBla_Exit
    6.  
    7. ; ---------------------------------------------------------------------------
    8. ; Subroutine to run a demo for an amount of time
    9. ; ---------------------------------------------------------------------------
    10.  
    11. ; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
    12.  
    13.  
    14. Demo_Time:


    Find HBlank, and delete this:

    Code (ASM):
    1.         tst.b   ($FFFFF64F).w
    2.         bne.s   loc_119E


    ...and this:

    Code (ASM):
    1. loc_119E:
    2.         clr.b   ($FFFFF64F).w
    3.         movem.l d0-a6,-(sp)
    4.         bsr.w   Demo_Time
    5.         jsr (UpdateMusic).l
    6.         movem.l (sp)+,d0-a6
    7.         rte
     
  19. redquebec

    redquebec

    Member
    39
    0
    6
    Thanks a lot again!

    I just tried to install the new driver in to a fresh AS Sonic 1, and after installing the level select extensions, it doesnt play music in the sound test. Does it happen on your side too? I might have missed something perhaps
     
  20. Clownacy

    Clownacy

    Tech Member
    889
    177
    43
    No, it doesn't happen on my end. I've updated the demonstration S2 ROM with the expanded level select, and added an S1 demonstration ROM.