Sonic and Sega Retro Message Board: ValleyBell - Viewing Profile - Sonic and Sega Retro Message Board

Jump to content

Hey there, Guest!  (Log In · Register) Help

Group:
Tech Member: Tech Members
Active Posts:
208 (0.12 per day)
Most Active In:
Technical Discussion (89 posts)
Joined:
08-September 10
Profile Views:
16865
Last Active:
User is offline Jul 21 2015 02:08 AM
Currently:
Offline

My Information

Age:
24 years old
Birthday:
May 29, 1991
Gender:
Male Male

Contact Information

E-mail:
Private
Website:
Website  http://vgm.mdscene.net/

Previous Fields

Other Contact Info:
irc://irc.digibase.ca/vgmrips
Project:
researching SMPS sound drivers
National Flag:
de
Wiki edits:
10

Latest Visitors

Topics I've Started

  1. Various bugfixes for the Sonic 1 sound driver

    24 March 2015 - 03:30 PM

    With Clownacy posting lots of fixes for the Sonic 2 driver recently, I remembered that I wanted to post some fixes for the Sonic 1 sound driver.
    Actually I planned to post these fixes a lot earlier (the text file with the original notes dates back to 30 March 2013), but kept forgetting about it.
    I ran into most of these bugs when working on the S2 Clone Driver for Sonic 2 Recreation.
    For some of the bugs there are example files that demonstrate them. There are MP3s and VGMs, the latter ones contain slightly longer segments of the used songs.

    Note: Most of the bugs were fixed in the Sonic 2 driver, sometimes in a slightly different way. Those that aren't should be easily portable to the S2 driver though.

    A few general notes about the code style:
    The code excerpts are based on the sound driver I used in S2R, so it's a mix between the 2005 disassembly (actual numbers for offsets) and the SVN/Git disassembly (comments/labels). Labels from the 2005 disassembly are listed in the comments, either directly after the label or in [square brackets] for branches.
    The locret_xxx labels are called @locret in the SVN/Git disassembly.


    Fix modulation during rests
    (fixed in Sonic 2)
    You won't notice this bug unless you have an FM instrument with a long release rate and enable modulation in areas with rests in your song.
    But in these cases, you'll likely be confronted with "rumbling" in your songs.

    What causes this? Sonic 1 applies the modulation effect on rests where the frequency is set to 0, so it sort of corrupts the frequency.
    (It will apply the modulation effect to frequency 0, so it will send frequencies like 0, 2, 0, 7FE, 0 to the FM chip.
    Also, the "rest frequency" is 0 for FM only. It is -1 for PSG channels, but the bug has no audible effect on them.)
    DoModulation:	;sub_71DC6		; XREF: FMUpdateTrack; PSGUpdateTrack
    		addq.w	#4,sp		; Do not return to caller (but see below)
    		btst	#3,(a5)		; Is modulation active?
    		beq.s	locret_71E16	; Return if not
    

    The fix is quite easy and actually a backport from the Sonic 2 sound driver. Just paste these 2 lines right below the addq.w:
    		btst	#1,(a5)		; Is note playing?
    		bne.s	 locret_71E16	; no - return
    


    Fix PSG fading bug (1-up bug, part 1)
    (fixed in Sonic 2)
    After the 1-up ends, when the music fades in, you may randomly hear some short and loud PSG notes. Or even worse, a long PSG tone with rising pitch. (Example MP3/VGM)
    The tone ends as soon as another notes plays on the respective PSG channels. It can still be pretty annoying though.

    The problem is, that some parts of the sound driver call the SetPSGVolume subroutine without checking, if the PSG volume is between $0 and $F. And if it isn't, the calculation will overflow and it will write to the next channel's frequency register instead.
    (The problematic code is not in DoFadeIn, btw. It's in PSGDoVolFX. It is possible to easily reproduce the bug by setting the PSG channel volume to $10+ and its instrument to $00.)

    To fix this, we could either add an additional check at every point where SetPSGVolume is called. Or we could add it one time, in PSGSendVolume, and catch all cases - this is what we'll do.
    This is the code that sends the PSG volume:
    PSGSendVolume:	;loc_7297C
    		or.b	1(a5),d6	; Add in track selector bits
    		addi.b	#$10,d6		; Mark it as a volume command
    		move.b	d6,($C00011).l
    

    We now need to limit the volume, which is stored in d6, to [$0, $F] before sending it to the PSG chip.
    Just paste these 4 lines directly under the PSGSendVolume-label:
    		cmpi.b	#$10, d6	; Is volume $10 or higher?
    		blo.s	L_psgsendvol	; Branch if not
    		moveq	#$F, d6		; Limit to silence and fall through
    L_psgsendvol:
    

    No, we're not finished yet. Now the sound driver does the volume check 2 times at a few places. This is useless work and costs valuable CPU cycles, since we're within an interrupt routine.

    Remove useless PSG volume checks - step 1:
    PSGDoVolFX:	;loc_7292E		; XREF: PSGUpdateTrack
    	; ...
    L_gotflutter:	;loc_72960
    		add.w	d0,d6		; Add flutter to volume
    		cmpi.b	#$10,d6		; Is volume $10 or higher?
    		bcs.s	SetPSGVolume	; Branch if not [sub_7296A]
    		moveq	#$F,d6		; Limit to silence and fall through
    

    Those last 3 lines look familiar, don't they? PSGSendVolume is right below, so delete the ones here or comment them out.

    Remove useless PSG volume checks - step 2:
    DoFadeIn:	;sub_7267C:		; XREF: UpdateMusic
    	; ...
    L1_psgloop:	;loc_726B4
    		tst.b	(a5)		; Is track playing?
    		bpl.s	L1_nextpsg	; Branch if not [loc_726CC]
    		subq.b	#1,9(a5)	; Reduce volume attenuation
    		move.b	9(a5),d6	; Get value
    		cmpi.b	#$10,d6		; Is it is < $10?
    		bcs.s	L1_sendpsgvol	; Branch if yes
    		moveq	#$F,d6		; Limit to $F (maximum attenuation)
    
    L1_sendpsgvol:	;loc_726C8
    		jsr	SetPSGVolume(pc)
    

    Like above, you can remove the 3 lines from cmpi.b to moveq. You can also remove the sendpsgvol-label, because it isn't used anymore.


    Fix PSG noise bug (1-up bug, part 2)
    You probably never used the PSG's periodic noise, but it makes a nice sawtooth-like bass sound.
    Sonic 1 has a very annoying bug that occurs when fading back to a song that uses it: It doesn't set the noise mode after fading from the 1-up, so it plays white noise instead of a bass sound. (Example MP3/VGM)

    The solution is to restore the noise type in the cfFadeInToPrevious routine. (This is already done when an SFX ends, btw.)
    cfFadeInToPrevious:	;loc_72B14	; XREF: coordflagLookup
    		; ...
    		moveq	#2,d7
    
    L2_psgloop:	;loc_72B66
    		btst	#7,(a5)		; Is track playing?
    		beq.s	L2_nextpsg	; Branch if not
    		bset	#1,(a5)		; Set track at rest bit
    		jsr	PSGNoteOff(pc)	; [sub_729A0]
    		add.b	d6,9(a5)	; Apply current volume fade-in
    
    L2_nextpsg:	;loc_72B78
    		adda.w	#$30,a5
    		dbf	d7,L2_psgloop
    


    Directly above the L2_nextpsg label, paste these 3 lines:
    		cmpi.b	#$E0, 1(a5)	; is this the Noise Channel?
    		bne.s	L2_nextpsg	; no - skip
    		move.b	$1F(a5), ($C00011).l	; restore Noise setting
    

    Done - enjoy using the periodic noise.


    Fix for 0 FM/DAC channels
    (fixed in Sonic 2)
    Most of you will probably never make SMPS files that use only the 3 PSG channels, but if you like to experiment with SMPS music, you might encounter this bug.
    In general, the song might or might not load. The behaviour can be random, because it uses uninitialized registers.

    Let's look at some of the code in the Sound_PlayBGM routine:
    L_nospeedshoes:	;loc_72068
    		move.b	d0,2(a6)
    		move.b	d0,1(a6)
    		moveq	#0,d1
    		movea.l	a4,a3
    		addq.w	#6,a4		; Point past header
    		moveq	#0,d7
    		move.b	2(a3),d7	; load number of FM+DAC channels
    		beq.w	L_bgm_fmdone	; branch if zero [loc_72114]
    		subq.b	#1,d7
    		move.b	#$C0,d1		; Default AMS+FMS+Panning
    		move.b	4(a3),d4	; [!] load tempo dividing timing
    		moveq	#$30,d6		; [!] load F8 gosub coord. flag pointer
    		move.b	#1,d5		; [!] Note duration for first "note"
    		lea	$40(a6),a1
    

    You see the 3 lines marked with [!] - these load a few default values that are important for the driver setup.
    If there are 0 FM/DAC tracks, the beq.w L_bgm_fmdone skips them and the PSG tracks will be set up with garbage values.
    (Since you're lucky, an unmodded Sonic 1 driver just won't play anything at all. But it can also play the first note and hang then.)

    The fix is quite simple, just move these 3 lines a bit up somewhere above of the beq.w so that they get processed even if there are no FM/DAC channels.
    I recommend to move them between addq.w #6,a4 and moveq #0,d7.


    Fix the DAC 1-up bug
    (fixed in Sonic 2)
    You know how getting a 1-up in the Special Stage in Sonic 1 mutes the 6th FM channel when fading in?
    The reason is, that the 1-up enables the YM2612's DAC mode (replaces the 6th FM channel with 8-bit PCM), but when fading in it doesn't disable the DAC mode.
    The solution is really simple - just write value $00 to register $2B of YM2612's port 0 to disable the DAC and enable the 6th FM channel again. It doesn't do any harm to songs that use the DAC, because the Z80 DAC driver enables the DAC mode before playing any sound.* (Sonic 1 only disables the DAC when starting to play a song with exactly 7 FM/DAC channels, btw.)
    * Custom DAC drivers might behave differently.

    Now let's look at some code:
    cfFadeInToPrevious:	;loc_72B14	; XREF: coordflagLookup
    		movea.l	a6,a0
    		lea	$3A0(a6),a1
    		move.w	#$87,d0		; $220 bytes to restore
    
    L_restoreramloop:	;loc_72B1E
    		move.l	(a1)+,(a0)+
    		dbf	d0,L_restoreramloop
    
    		bset	#2,$40(a6)	; Set SFX overriding bit
    		movea.l	a5,a3
    		move.b	#$28,d6
    		sub.b	$26(a6),d6	; If fade already in progress, this adjusts track volume accordingly
    

    All you need to do is pasting the following 3 lines between the dbf and the bset commands. (You can actually paste them at other places, too, but I recommend this one.)
    		move.b	#$2B, d0	; Register: DAC mode (bit 7 = enable)
    		moveq	#$00, d1	; Value: DAC mode disable
    		jsr	WriteFMI(pc)	; Write to YM2612 Port 0 [sub_7272E]
    


    Note: There is also an alternative solution that isn't as simple, but would allow dynamic switching between DAC mode and 6 FM channels during a song:
    Keep track of the current DAC mode (driver init should set it to $00) by using a byte in sound RAM that is always the same as the last value written to YM2612 register $02B.
    Set the DAC mode to $80 when playing a DAC sound (at the end of @gotsampleduration/loc_71C88) and to $00 when playing a note on FM6. The latter requires an additional check in FMNoteOn to prevent it from disabling DAC mode when a note is played on FM 1-5.


    Fix the SEGA sound panning bug
    Does your Game Over/Continue song use panned DAC drums? If it does, you may've run into a bug where the SEGA chant is panned to the left or right speaker.

    The cause is, that the song's panning doesn't get reset when the SEGA chant is played, so the effect remains.
    (It should be noted that starting any song with 1-6 FM/DAC channels resets the panning.)

    Anyway, the solution is simple: We reset the panning bits before playing the SEGA chant. This is done using these 3 lines (the DAC channel uses the FM6 stereo mask):
    		move.b	#$B6, d0	; Register: FM3/6 Panning
    		move.b	#$C0, d1	; Value: Enable both channels
    		jsr	WriteFMII(pc)	; Write to YM2612 Port 1 (for FM6) [sub_72764]
    

    This should be very the first thing you do in PlaySega/Sound_E1.
  2. Valley Bell's SMPS Research

    31 December 2013 - 10:45 AM

    Most recent releases
    SMPS Research Pack v4: direct link part 1 / part 2, release post
    SMPSPlay 2.11: Win32/64 binary, source code archive / git repo, release post


    Original post
    Over the last two years I did a lot of research on SMPS drivers, both 68k and Z80.
    I ripped songs from 21 SMPS 68k and 35 SMPS Z80 games, did some research on preSMPS, especially Rent-A-Hero, and disassembled some sound drivers.

    And today I want to show you the results: the SMPS Research Pack.
    The probably most interesting thing inside are rips from almost 60 games.
    Except for the preSMPS games, every ripped game has not only the extracted SMPS files, but DAC and PSG data, too. (PSG data is stored in mid2smps' PSG list format.)

    There is also a file called "Pointers.txt" in every folder that lists the locations of pointer lists and other data, as well as the format of the DAC sound list. Later rips are more complete and have more notes than earlier ones.
    For two games, OutRunners and Virtua Racing (SVP), there is also table that explains the custom coordination flags.

    Some folders have songs patched to work with SMPSPlay. The original songs are marked with .org in this case.

    The folder for Rent-A-Hero contains files in SMPS 68k format. I converted them from preSMPS 68k to SMPS 68k using a small tool I wrote. The conversion is not perfect (missing tempo changes and DAC on/off flags), but pretty close.


    The programmer guys may be interested in the folder with disassembled SMPS drivers. (Included are original .bin, the .idb and an exported .asm file.)

    There are disassemblies of a fair number of DAC drivers. I also compared them and noted the differences in a separate .txt file.
    I also collected some SMPS Z80 drivers, sorted them into categories and disassembled a few of them. Please note that some of the older disassemblies (2012 and early 2013) contain mistakes.
    Finally there are also a few disassembled preSMPS drivers. I didn't get far with Phantasy Star 2, but the other two are labelled well. PS2 with its "chord mode" and Space Harrier 2 with a 2x 2-Operator FM drum are both pretty interesting.


    Finally, there is also a folder with a few .txt files with tables that tell you the playback speed of a few DAC drivers. These are DAC rate -> frequency in Hz conversion tables and the values are reused by SMPSPlay to determinate the playback speed of the DAC samples.


    You think that's all? No, it's not.

    In order to play all those rips, you probably want to download an updated version of SMPSPlay, an SMPS player for Windows.
    It includes a Win32 binary, as well as a 64-bit one.
    If you want to look at the source code, you can get it here. Be warned though, it's pretty messy right now and I plan to make some larger changes that make it easier to support various SMPS variants.

    Important notes about SMPS Z80:
    - The offset autodetection is unstable and doesn't work on some games or songs. It's pretty much impossible to guess if there is garbage data between header and actual song data or not.
    - For some songs you will need instrument sets that are shared between songs. You can edit data\config.ini to use the respective InsSet.bin files.
    - Some SMPS Z80 games use FM and PSG drums by playing notes on the DAC/Rhythm channel. SMPSPlay currently supports only DAC sounds on this channel, so I tried to replace them with DAC drums in a few folders. The only game where it worked well was Dyna Brothers 2 though.

    Now, enjoy and happy hacking!

    P.S.: It's not ready yet, but I will release an updated version of my SMPS Extractor soon, too. I used it to extract all songs and DAC sounds.

    EDIT: You can download a slightly updated SMPSPlay here.
  3. DAC Rate to Frequency Conversion

    21 January 2012 - 10:45 AM

    When working on SMPSPlay and DEPlay there was always one thing that bothered me: the DAC frequency
    It's something I couldn't just get right, so I decided to do some research.

    I startet with Oerg866's SMPS Player, which uses jman2050's DAC driver:
    At first I made an SMPS file that plays a few DAC sounds and made each sound play the same sample, but with different DAC rates.
    Then I used MESS to make a VGM of it. (MESS because MAME's timing system is extremely accurate.)
    After that I wrote a small tool in VB that reads that VGM and calculated the frequency of each sound.
    Finally I made another small VB tool that recalculated the formula FreqHz = BaseVal / (RateBase + DACRate) for the DAC rates I logged. With this tool I tested values for RateBase until I found a perfect one. (I made it recalculate BaseVal with the tested frequency of rate 01 and RateBase.)

    Then I did the same with plain Sonic 1, Sonic 2, and Barver Battle Saga (because I already had its pointers) for the Data East driver.

    And here are my results:
    (The columns are DACRate, logged DAC samples, length of the DAC sound in VGM ticks (44100 Hz), DAC samples per VGM sample, DAC frequency.)

    jman2050's DAC driver
    ---------------------
    01	8,238	 11,940	  1.45	30,426.784
    02	8,238	 13,275	  1.61	27,366.915
    03	8,238	 14,600	  1.77	24,883.274
    04	8,238	 15,937	  1.93	22,795.746
    05	8,238	 17,268	  2.10	21,038.673
    06	8,238	 18,602	  2.26	19,529.932
    07	8,238	 19,938	  2.42	18,221.276
    08	8,238	 21,265	  2.58	17,084.213
    0C	8,238	 26,598	  3.23	13,658.764
    10	8,238	 31,926	  3.88	11,379.308
    20	8,238	 53,243	  6.46	 6,823.353
    30	8,238	 74,561	  9.05	 4,872.464
    40	8,082	 94,060	 11.64	 3,789.243
    80	4,278	 94,054	 21.99	 2,005.867
    C0	2,908	 94,020	 32.33	 1,363.995
    FF	2,212	 94,038	 42.51	 1,037.338
    00	8,238	352,309	 42.77	 1,031.185
    -> FreqHz = 272624 / (7.96 + DACRate)
     Calculating with (8 + prim) is still a good approximation. (DACSmpl/VGMSmpl variance is <0.2 for rates <= 80)
    
    
    Sonic 1 DAC driver
    ------------------
    01	8,236	 15,517	  1.88	23,407.076
    02	8,236	 16,846	  2.05	21,560.465
    03	8,236	 18,190	  2.21	19,967.433
    04	8,236	 19,530	  2.37	18,597.419
    05	8,236	 20,866	  2.53	17,406.671
    06	8,236	 22,209	  2.70	16,354.073
    07	8,236	 23,550	  2.86	15,422.828
    08	8,236	 24,882	  3.02	14,597.203
    0C	8,236	 30,252	  3.67	12,006.069
    10	8,236	 35,602	  4.32	10,201.888
    20	8,236	 57,041	  6.93	 6,367.483
    40	8,236	 99,906	 12.13	 3,635.493
    80	8,236	185,658	 22.54	 1,956.326
    C0	5,710	188,159	 32.95	 1,338.288
    FF	4,354	188,077	 43.20	 1,020.919
    00	8,236	357,141	 43.36	 1,016.987
    -> FreqHz = 271053 / (10.58 + DACRate)
    
    
    Sonic 2 DAC driver
    ------------------
    [without gaps]
    01	5,994	 10,634	  1.77	24,857.570
    02	5,994	 11,593	  1.93	22,801.294
    03	5,994	 12,550	  2.09	21,062.582
    04	5,994	 13,505	  2.25	19,573.151
    05	5,994	 14,464	  2.41	18,275.401
    06	5,994	 15,442	  2.58	17,117.951
    07	5,994	 16,400	  2.74	16,118.012
    08	5,994	 17,349	  2.89	15,236.348
    0C	5,994	 21,209	  3.54	12,463.360
    10	5,994	 25,016	  4.17	10,566.653
    20	5,994	 40,424	  6.74	 6,539.071
    40	5,994	 71,112	 11.86	 3,717.170
    80	4,384	 96,853	 22.09	 1,996.163
    C0	3,014	 97,425	 32.32	 1,364.305
    FF	2,312	 98,042	 42.41	 1,039.954
    00	5,994	255,585	 42.64	 1,034.237
    -> FreqHz = 275173 / (10.07 + DACRate)
    
    [with gaps]
    01	5,994	 11,152	  1.86	23,702.959
    02	5,994	 12,146	  2.03	21,763.165
    03	5,994	 13,140	  2.19	20,116.849
    04	5,994	 14,163	  2.36	18,663.800
    05	5,994	 15,155	  2.53	17,442.125
    06	5,994	 16,181	  2.70	16,336.160
    07	5,994	 17,175	  2.87	15,390.707
    08	5,994	 18,178	  3.03	14,541.501
    0C	5,994	 22,193	  3.70	11,910.756
    10	5,994	 26,203	  4.37	10,087.982
    20	5,994	 42,327	  7.06	 6,245.078
    40	5,994	 74,027	 12.35	 3,570.797
    80	4,384	100,639	 22.96	 1,921.068
    C0	3,014	100,638	 33.39	 1,320.748
    FF	2,312	 99,898	 43.21	 1,020.633
    00	5,994	259,097	 43.23	 1,020.218
    -> FreqHz = 265473 / (10.20 + DACRate)
    
    
    Data East DAC driver
    --------------------
    01	5,309	 14,714	  2.77	15,911.846
    02	5,309	 15,768	  2.97	14,848.231
    03	5,309	 16,817	  3.17	13,922.037
    04	5,309	 17,866	  3.37	13,104.607
    05	5,309	 18,906	  3.56	12,383.735
    06	5,309	 19,951	  3.76	11,735.096
    07	5,309	 21,006	  3.96	11,145.716
    08	5,309	 22,077	  4.16	10,605.014
    0C	5,309	 26,261	  4.95	 8,915.384
    10	5,309	 30,457	  5.74	 7,687.129
    20	5,309	 47,284	  8.91	 4,951.504
    40	5,309	 80,941	 15.25	 2,892.562
    80	5,309	148,154	 27.91	 1,580.294
    C0	5,309	215,537	 40.60	 1,086.249
    FF	5,309	281,543	 53.03	   831.585
    00	5,309	282,774	 53.26	   827.965
    -> FreqHz = 222766 / (13 + prim)
    

    All formulas are accurate enough that the difference between tested and recauculated (DAC samples per VGM sample) is <= 0.2 for all rates. (except for Sonic 2 with gaps)

    The Sonic 2 driver does all work on the Z80, so it needs to interrupt the DAC stream every frame to do update the music engine. [without gaps] means, that I removed the small delays that occur because of this, [with gaps] means that I just took the approximate frequency including these delays.


    The next update of SMPSPlay and DEPlay with feature perfect DAC rates. (For SMPSPlay they're adjustable via ini-files.)
  4. C-based SMPS Playback Engine - updated

    23 December 2011 - 06:12 AM

    Do you rememer this SMPS player?

    Well, I worked a little on it. And although I didn't improve the actual smps playback a lot (aside from a few small bugfixes), I made a lot of other changes.

    At first, it can log v1.60 VGMs. (press V to enable/disable logging)
    Also all DACs sounds and PSG envelopes are dynamically loaded and are easy to change. (I included a small editor for the PSG envelope files.)
    And I removed Allegro (and made it a console app), so it continues playing if the program doesn't have the focus.
    Finally, there is a 32-bit version (compiled with MSVC6 for compatibility) and a 64-bit versions (compiled with MSVC2010, so you need .NET 4.0).

    Download

    Enjoy!

    EDIT: File updated, see this post
  5. S3K to S1 SMPS Converter

    07 December 2011 - 03:20 AM

    Crosspost from RetroHack and SSRG:

    Valley Bell, on 06 December 2011 - 05:22 PM, said:

    During the last week I worked on a tool to convert S3K/S3D/... SMPS files to the Sonic 1 format.
    There was a need for it, because MFP doesn't do its job very well. (It converts only a few coordination flags and sometimes changes pointers at places with no pointers at all.)

    You can get the tool here. (Source is included.)


    The tool does not only convert S3K songs to S1 fully automatically (that includes the missing-note fix), but it also allows you to change all DAC sounds and PSG instruments by simply editing two ini-files. That should make porting songs a piece of cake. :)
    (If you want to find matching PSG envelopes, you can use my PSG Envelope Editor. I used it to make the current PSGMap file.)

    Notes and bugs:
    - Just change the Z80 ROM bank offset if you're sure that the tool detects it wrong. Don't mess with it or you'll get bad conversions.
    - The tool prints a message for every ignored command. (Some of the commands may cause it to stop processing the current track. But I don't know any songs that have such ones.)
    - The conversion of the modulation/vibrato effect (F0) is still wrong. S3K seems to do that completely different to S1. (very noticable in the S&K spheres bonus stage)
    - I know that S3's LBZ1/2 don't really work. That's because LBZ1 has some data of LBZ2 and vice versa.

    If there are other bugs (or even crashes), please report it and I'll try to fix it.

    Thanks go to redhotsonic for beta-testing the tool.

    Enjoy!