Sonic 1 Mega PCM driver

Discussion in 'Tutorials Archive' started by vladikcomper, Jun 11, 2012.

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

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    08/05/2012 - Version 1.1 is out! Check you this post for info (clickie). Links in this post are also up to date.


    10/28/2012 - New guide: "How to increase playback quality with Mega PCM"


    * * *


    "Mega PCM" is my new DAC driver for Sonic 1, coded from the ground up. We already have JMan's and S1HL's custom drivers, which provided us with a lot of good possibilities over the native Sonic 1's DAC driver. I personally used JMan's driver all the time, finding it the best for my purposes. However, like most Z80 drivers, it had some limits and quirks, which were impossible to overpass. All these 'quirks' are actually caused by the hardware design: the Z80 is very limited in accessing ROM sections (this is done via a small 32 KB window) and the processor has to do all DAC timing itself, which adds many difficulties into programming it.


    In my driver, I attempted to get rid of some unpleasant limits most of Z80 drivers usually have. I also aimed to code it in the most optimized way to keep playback rates high, while the code is overloaded with a lot of logic I have to program to make overpassing limits possible. To my surprise, I succeded, Mega PCM currently plays sound faster than YM's maximum output frequency (~27 kHz), so there is some room remains for future additions ;)


    The features

    • Automatic bank-switching
      Forget about the banks, put your samples where you like, how you like. You no longer have to align samples on 32 KB boundary and care if they cross the boundary.

    • Unlimited sample size
      Samples are no more limited to 32 KB. "Mega PCM" is capable of playing samples of absolutely any size, as long as it can fit your ROM space.

    • Two sound formats supported
      These are 4-bit DPCM and 8-bit PCM.
      The first format was widely used by Sonic 1 to Sonic 3K for DAC samples as it takes only half of the space a normal PCM sound would. 8-bit PCM, however, is the 'native' format for Sega's YM2612 chip, it takes more space but provides a better sound quality.

    • Extended playback controls: Stop, Pause, Loop, Priority
      "Mega PCM" can pause and continue sample playback, so if you play a long sample it won't be cut off after you pause the game.
      You can also loop samples (good for DAC-songs) and can tell "Mega PCM" not to overwrite some samples (good for in-game voice clips).

    • DAC panning
      You can store your sample to play in Left or Right headphone only.

    • Up to $5F DAC samples allowed
      For instance, JMan's driver allowed $1F DAC sample due to its usage of 8-bit pointers in the code. However, in reality DAC table size is only limited by the RAM size, but the SMPS only allows up $5F different DAC samples (slots $81-$CF).



    How-to Install "Mega PCM"


    If you got interested in trying it out, here we go!


    In this guide, I'll tell you how to put Mega PCM driver into your hack, how to improve SMPS slightly to suit new driver's needs and go wild with including new DACs!


    This guide also includes bonus: "How-to play voices in-game" (see below)


    First off, download this: https://dl.dropbox.com/u/44757401/MegaPCM_v11_Bin.7z


    This is almost ready to use "Mega PCM" setup with Sonic 3 samples set (except for timpani, S3's doesn't fit well here). Copy all files from my archive into your hack's folder and we'll start.


    The guide is for Hivebrain's Disassembly.


    1. Replacing old driver and driver loading routine


    Open Sonic1.asm and find 'Kos_Z80' label, you'll see the following code:



    Kos_Z80: incbin soundz80_1.bin
    dc.w ((SegaPCM&$FF)<<8)+((SegaPCM&$FF00)>>8)


    dc.b $21


    dc.w (((EndOfRom-SegaPCM)&$FF)<<8)+(((EndOfRom-SegaPCM)&$FF00)>>8)


    incbin soundz80_2.bin


    even



    Delete everything, and put this instead:



    include 'MegaPCM.asm'



    Now, goto 'SoundDriverLoad' routine and replace all the code with this:



    SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
    nop


    move.w #$100,d0


    move.w d0,($A11100).l


    move.w d0,($A11200).l


    lea (MegaPCM).l,a0


    lea ($A00000).l,a1


    move.w #(MegaPCM_End-MegaPCM)-1,d1


    @Load: move.b (a0)+,(a1)+


    dbf d1,@Load


    moveq #0,d1


    move.w d1,($A11200).l


    nop


    nop


    nop


    nop


    move.w d0,($A11200).l


    move.w d1,($A11100).l


    rts


    ; End of function SoundDriverLoad



    2. Editing SMPS to work with the driver


    Find 'sub_71B4C' label and scroll down until you see this part:



    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:



    Delete or comment out this code.

    This code checked driver's status and didn't run SMPS engine until driver tells SMPS can interrupt him. "Mega PCM" doesn't need this trick anymore.


    Then, goto 'loc_71C88' label, find and delete these lines:



    btst #3,d0
    bne.s loc_71CAC



    This removes hardcoded Timpani tempo modifier for old driver. Certain sample numbers ($88-$8B) were hardcoded to play sample $83 (timpani) with different tempos. New driver doesn't need it, as you can set this stuff in the DAC table.


    Also, 'loc_71CAC' is now unused code, so you may want to delete it as well.


    Next, find 'loc_71C44' label and in the very beginning add this code:



    move.b ($A04000).l,d2
    btst #7,d2


    bne.s loc_71C44


    move.b #$2A,($A04000).l



    "Mega PCM" doesn't store YM register 2A before writing to port in playback loop, this is odd and getting rid of it gives a small speed-up. Register is stored before playback and driver relies that it won't be changed. However, SMPS does writes to YM, storing different registers. So, it must restore register 2A before releasing Z80 bus and continuing Z80 playback program.


    The following changes will make "Mega PCM" pause playback when the game pauses and continue it when the game unpauses.


    Find 'loc_71E7C' and at the very end, just before BRA, add this line:



    move.b #$7F,($A01FFF).l; pause DAC


    Playing sample $7F executes pause command.


    Next, find 'loc_71EDC' and replace all the code until label 'loc_71EFE' with this:



    loc_71EDC:
    adda.w d3,a5


    dbf d4,loc_71EC4


    lea $340(a6),a5


    btst #7,(a5)


    beq.s @UnpauseDAC


    btst #2,(a5)


    bne.s @UnpauseDAC


    move.b #-$4C,d0


    move.b $A(a5),d1


    jsr sub_72722(pc)


    @UnpauseDAC:


    move.b #0,($A01FFF).l ; unpause DAC



    Playing sample $00 cancels pause mode.


    And the last touch, let's make playback stop properly (not only mute) when SMPS stops the sound.


    Goto 'loc_725B6' (a part of 'Sound_E4' code) and before the 'bra.w sub_729B6' add this line:



    move.b #$80,($A01FFF).l ; stop DAC playback


    Playing sample $80 forces to stop playback


    That's it!


    How-to use "Mega PCM"


    Open MegaPCM.asm. This file includes compiled "Mega PCM" Z80-code and stores table of samples to play. To make working with driver easier, I added some constants and macros to setup table and include samples.


    Scroll down in the file until you see this:



    ; ---------------------------------------------------------------
    ; DAC Samples Table


    ; ---------------------------------------------------------------


    DAC_Entry $08, Kick, dpcm ; $81 - Kick


    DAC_Entry $08, Snare, dpcm ; $82 - Snare


    DAC_Entry $1B, Timpani, dpcm ; $83 - Timpani


    dc.l 0,0 ; $84 - <Free>


    dc.l 0,0 ; $85 - <Free>


    dc.l 0,0 ; $86 - <Free>


    dc.l 0,0 ; $87 - <Free>


    DAC_Entry $12, Timpani, dpcm ; $88 - Hi-Timpani


    DAC_Entry $15, Timpani, dpcm ; $89 - Mid-Timpani


    DAC_Entry $1B, Timpani, dpcm ; $8A - Mid-Low-Timpani


    DAC_Entry $1D, Timpani, dpcm ; $8B - Low-Timpani


    MegaPCM_End:


    ; ---------------------------------------------------------------


    ; DAC Samples Files


    ; ---------------------------------------------------------------


    IncludeDAC Kick, bin


    IncludeDAC Snare, bin


    IncludeDAC Timpani, bin


    even



    First block is a DAC samples table, storing what sample to play and how to play it. It will be loaded to Z80 memory along with "Mega PCM" code.


    Second block includes sound files into your ROM. 'IncludeDAC' macro searches for file with given name and extension in 'dac' folder, which you've added to your hack among with other files.


    For example,



    IncludeDAC Kick, bin



    will include 'dackick.bin' file.


    The macro includes all the files 'as is', except for files with 'wav' extension. For WAV-files, the macro cuts off the header, or you will hear a short noise when play them.


    BIN-files usually used for DPCM format. The fact "Mega PCM" plays this format means you may rip DACs from any Sonic game and play it without conversion to PCM, which makes file two times heavier, but doesn't help with the quality. If you wish to convert PCM sound to DPCM, you can use JMan's compressor: http://selbi.se.funpic.de/s1sndcomp.exe


    WAV-files should use "8-bit unsigned PCM" format. PCM sound can be also presented with 'RAW' extension (raw sound without header), some trackers support it.


    Sound frequency can be up to ~30 kHz for PCM and ~32 for DPCM (Yeah, suddenly the more complex format got a higher playback rate, seems my optimized code is way fast. Actually, the fact it processes 2 DAC writes per loop helps a lot.)


    But actually, YM2612 can't play DAC faster than ~27 kHz (it will lead to missed writes). However, emulators don't emulate this and if you don't care about playback quality on real hardware, go wild.


    DAC samples table uses 'DAC_Entry' macros to store sample, which has the following format:



    DAC_Entry <pitch>, <sample name>, <flags>



    The real 8-byte format used by "Mega PCM" you can see in the macro's definition.


    Pitch sets how fast the sample sound should play. The lower pitch is, the faster it will play.


    Sample Name refers to sample name as defined by 'IncludeDAC' macro.


    Flags field accepts the following flags:


    * pcm - selects PCM format


    * dpcm - selects DPCM format


    * loop - makes sample loop itself


    * pri - priority flag, playback won't be interrupted if new sample requested. Playback will respond only to stop/pause commands in this mode.


    * panL - play in Left headphone only


    * panR - play in Right headphone only


    * panLR - play in both headphones


    You can combine flags like this: dpcm+panL+loop+pri


    Note: if no pan flags were set, "Mega PCM" won't change DAC panning, it will leave setting by SMPS.


    Bonus: How-to play voices in game


    Here is a small example of using "Mega PCM" to play voice clips during the game.


    Download this clip: https://dl.dropbox.com/u/44757401/V_Hurt.wav


    Move it to 'dac' folder in you disassembly.


    In the very end of MegaPCM.asm file, just before the 'even', add:



    IncludeDAC V_Hurt,wav



    Now we should add it in one of DAC table's slot, take slot $84 for example, it's free. We'll need "Mega PCM" to play this as PCM sound with high priority (or else it can be interrupted by another DACs in BGM).


    Modify the table so it will look like this:



    DAC_Entry $08, Kick, dpcm ; $81 - Kick
    DAC_Entry $08, Snare, dpcm ; $82 - Snare


    DAC_Entry $1B, Timpani, dpcm ; $83 - Timpani


    DAC_Entry $07, V_Hurt, pcm+pri ; $84 - 'Hurt' voice


    dc.l 0,0 ; $85 - <Free>


    dc.l 0,0 ; $86 - <Free>


    dc.l 0,0 ; $87 - <Free>


    DAC_Entry $12, Timpani, dpcm ; $88 - Hi-Timpani


    DAC_Entry $15, Timpani, dpcm ; $89 - Mid-Timpani


    DAC_Entry $1B, Timpani, dpcm ; $8A - Mid-Low-Timpani


    DAC_Entry $1D, Timpani, dpcm ; $8B - Low-Timpani



    Done! Sample is now playable. However, we must program the game to play it. In order to do this, find 'PlaySound' routine and add a new routine above:



    ; ---------------------------------------------------------------------------
    ; Subroutine to play a DAC sample


    ; ---------------------------------------------------------------------------


    PlaySample:


    move.w #$100,($A11100).l ; stop the Z80


    @0 btst #0,($A11100).l


    bne.s @0


    move.b d0,$A01FFF


    move.w #0,($A11100).l


    rts



    This routine will allow you to play DAC samples in game. Our new sample is $84, let's play it when Sonic's hurt.


    Goto 'Hurt_Sound' and replace



    jsr (PlaySound_Special).l



    with



    moveq #$FFFFFF84,d0
    jsr PlaySample



    That's it. Enjoy!


    "Mega PCM" source code


    "Mega PCM" has an open source code, enjoy!


    Version 1.1 Source Code: https://dl.dropbox.com/u/44757401/MegaPCM_v11_SourceCode.7z


    Version 1.0 Source Code: https://dl.dropbox.com/u/44757401/MegaPCM_v10_SourceCode.7z


    I accept any ideas on improving driver's core, bug reports etc. You are free to modify driver for your purposes or create something new of it.
     
    Last edited by a moderator: Oct 28, 2012
    Clownacy likes this.
  2. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    I spoke to ValleyBell, as he's in the music department in S2R. We have most of these already, or better. One or two features we don't have though!


    This looks really good. You've obviously taken your time to get this working, and it's very nice of you to share this out, making people's music editing a lot easier, and taking it to the next level. Good work!
     
    Last edited by a moderator: Jun 11, 2012
  3. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    Thanks for your opinion.


    I saw ValleyBell's driver is capable of, and from what I've heard, I can say it's an awesome work, and set of features is pretty gorgeous.


    In my driver I aimed more for overall comfortability and freedom, than for set of playback effects. The engine was build around the idea of having support for two sound formats, and to archive it, I had to design a more complex core than an ordinary DAC driver would need. That was my main goal: to create a quite easy to use driver with all facilities.


    Anyways, Mega PCM's code still can accept more possibilities in sound playback, so I may improve it in the future =)


    * * *


    Also, small update:


    Just found a small bug in "How-to play voices in-game" section.


    Playback flags were not set correctly in my example.


    Fixed.
     
  4. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    I should've released it at the very beginning, but was lazy to do it...


    A clean Sonic 1 Disassembly with Mega PCM driver included:


    Download: https://dl.dropbox.com/u/44757401/Sonic_1_with_MegaPCM.7z


    If you're not in mood to deal with a massive guide yet, you can check out how it looks like and even play with the driver (it's easy).


    If you're not sure if you followed some steps of the guide correctly, you may also check yourself.


    Everything is done directly as the guide in the first post says, nothing more. Section 'How-to play voices in-game' is also applied.


    For your convenience, I marked all changed lines in SMPS code (section 2 of the guide) with '++' comment, so you will find them easily. All code to be deleted was just commented out.
     
  5. Dark Lips

    Dark Lips Well-Known Member Member

    Joined:
    Nov 14, 2008
    Messages:
    293
    Location:
    Wolverhampton UK
    This is very impressive - Good to see this kind of work going on :)
     
  6. EMK-20218

    EMK-20218 The Fuss Maker Exiled

    Joined:
    Aug 8, 2008
    Messages:
    1,067
    Location:
    Jardim Capelinha, São Paulo
    Vladik. I just love you for this! It's very flexible to add the samples. The only hard part I found was when I followed the guide to insert it. Great job, man!
     
  7. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    I finally decided to change the original Sonic 1 driver for this one :p I give it a try and I have to say it's great and I like it :D The only thing I will change is that you use the S3 DAC samples here, and my intention was to use the S1 ones, but that is not a problem and maybe I will kept the S3 ones, who knows (you did it great keeping the S1 timpani). Thanks for this excellent work and congratulations!

    Hard part? Are you kidding? This is one of the better guides I have seen in long time, it's impossible to get lost here. Vladikcomper did a great job explaining step by step the changes to do and he even tell us what every change does, it's excellent explained. It only took me less than 10 minutes to insert it and it worked fine at the first try :p
     
  8. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    Coming Soon:

    1. How to improve sample playback quality
    2. How to play digital songs with "Mega PCM"
    3. A new version of "Mega PCM" with few improvements and bug-fixes


    Note that you can play digital audio tracks even now, as "Mega PCM" can easily manage with these. First post contains enough info to get started, so if you're impatient, feel free to try this out =P


    Few words about upcoming 'How to improve sound quality' guide. Due to way Sonic 1 controls Z80, the processor is being stopped quite a lot which means, digital sound stops processing. This drops sound quality quite a lot. In my guide, I'll explain how to modify SMPS and a bit of game engine to reduce Z80 suspend times and achieve near-perfect quality results like in the video shown.
     
  9. Mike B Berry

    Mike B Berry A grandiose return Member

    Joined:
    Jun 6, 2012
    Messages:
    377
    Location:
    New places, newer motivation
    So you built it like a latched S1Mp3, but with the MegaPCM function?


    Also I'm using the JMans Z80 driver, could somebody post a step by step guide on how to switch. I can't import this without deleting all the music index pointers.


    EDIT: I back tracked and got my samples working again. So now the rest will come easier to me when The update to the driver is posted. Good luck on your hacks guys.
     
    Last edited by a moderator: Jul 13, 2012
  10. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    Mega PCM v.1.1

    • Added a small code-fix to prevent noise and memory access errors on playing empty/broken samples

      Now if the game attempts to play empty or not existing DAC sample slot, you won't hear anything. For example, if you imported an SMPS song and few DACs are missing in your driver. The previous Mega PCM version along with most of the drivers would give out random noise here due to playback errors.

      This also helps if a slot of random broken bytes somehow gets playing. Random offsets now won't cause errors on accessing prohibited memory areas, because they are fixed as well.

    • Fixed synchronization code issue in PCM and DPCM playback loops.

      While I've been counting every cycles in my code, I slightly miscalculated in calculating synchronization code cycle worth, which caused playback loop to work 4 cycles faster when the last bank reached. Therefore, sample's last bank played slightly faster then the rest. Despite 4 cycles (NOP opcode worth) being extremely small time period for Z80, playback speed up was noticeable on high sample rates (22 kHz and higher).

    • Mega PCM now supports up to 8 MB ROMs!

      The previous version was limited to 4 MB, but that wasn't the maximum of Mega PCM addressing capabilities though. I decided to push the limit to what the driver's bank switching system was really capable of, 8 MB. This means you can fit twice as much music and samples!

      I must warn though, very few emulators support big ROMs yet. But if you're still interested in this possibility, I'll try to clue you in (see below for the list of emulators):

      So, many people think that SMD is strictly limited to 4 MB in ROM storage. Actually, the console is capable of more. But the fact is, all the official game cartridges were designed with this limit in mind, their boards just weren't meant to address more than 4 MB of ROM-space, while the console allowed to do so. It was cartridges' limit, not the console's.

      For instance, some SMD flash-cartridges (like Mega Everdrive) support ROMs above 4 MB already, showing what the console is really capable of. However, emulators have very poor support for this.

      The matter is, that limit was tied by the official Genesis manual, where the memory map for 68K had only 4 MB of memory marked for ROM and the rest memory space up to 8 MB was marked as 'reserved'. It's interesting that in the earliest versions of this manual (1988-1989 years) the ROM limit was 1 MB. Many emulators' creators decided to strictly follow the manual and intentionally limited ROM-space to 4 MB. In principle, this makes sense and none of the official games suffered, but it would be rather good if more emulators got rid of this nasty limit, this will surely open up new horizons for SMD developing, bringing console a new life.

      As for now, there are quite few emulators with big ROMs support.
      Some of them are just hacked versions of popular emulators. You can find them here:
      http://umk3.hacking-cult.org/eng/download.htm



    How to update my Mega PCM to version 1.1


    Piece of cake.


    Download this: https://dl.dropbox.com/u/44757401/MegaPCM_v11_Bin.7z


    Replace MegaPCM.z80 file in your disassembly with a new one from the archive.


    That's it.


    Mega PCM v.1.1 Source code


    https://dl.dropbox.com/u/44757401/MegaPCM_v11_SourceCode.7z


    * * *


    If you don't have Mega PCM yet, but you decided to use it, just follow the first post's instructions. Links in the post and the info is up to date as well.


    * * *


    Still coming soon (guides):

    • How to improve sample playback quality
    • How to play digital songs with "Mega PCM"
     
  11. Sonic master

    Sonic master Well-Known Member Member

    Joined:
    Mar 27, 2010
    Messages:
    303
    When you say digital songs do you mean that it would be possible to store small portions of songs and then set an order that the samples play to create a longer song? if so that would be very cool and save alot of space I want to use sonic adventure music in my home-brew game and sonic adventure's music uses a similar technique the music is broken up into pieces and the pieces are used multiple times to create a longer song.
     
    Last edited by a moderator: Aug 15, 2012
  12. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    Unfortunately, not yet.


    I'm just going to make a guide about playing full song samples instead of normal SMPS music, and I'll try to do it the most handy way. By now, I don't know how it'll be exactly, I'll probably attach song samples to a particular SMPS sound ID to make it better usable.


    As for the idea you mentioned, I'm planning this too, but later.


    While this is not too hard to implement into the code, the current Mega PCM architecture is doesn't suit this purpose well. All the samples are stored in one table, and storing song parts into it as well will make things a bit messy for the user and make management less flexible. I'll go for storing sample parts for individual song in a separate table, along with the script telling what parts to play in what order. This requires to change quite a lot of things about how Mega PCM core works with data, so I'll leave it for the next version (2.0) where I plan to rewrite the core.
     
    Last edited by a moderator: Aug 26, 2012
  13. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    How to increase playback quality with Mega PCM


    We all know what the SEGA screen sounds like in the original Sonic 1 title:


    https://dl.dropbox.com/u/44757401/SEGA_Good.wav


    But what if we try playing it during game? When massive data transfers take place between different parts of the system, the same sample will sound like this instead:


    https://dl.dropbox.com/u/44757401/SEGA_Bad.wav


    This sample quality loss occurs with all samples during the game; this means that all of your kicks, snares, whatever, all playback scratchy.


    In this guide, I'll teach you how to solve this complex technical problem to achieve the best playback quality possible.


    There is also an attachment at the end of this post; a clean disassembly with this guide applied + a little bonus.


    After following this guide you can achieve impressive sample playback quality on Sega Genesis, like so:



    But first, in order to fix the quality loss, we must first understand why it occurs.


    The following text will contain a fair amount of theory and technical terminology. If you don't want to dive into incredible (!) shocking (!!) details, skip the next part and go straight to the second one.


    1. Theoretical explanation


    The problem here is not Mega PCM's fault, but Sonic 1's.


    As you might know, there are two processors in the Sega Genesis; the Motorola 68000 and the Zilog "Z80". The game engine itself is processed on the M68K which controls most of the system features, though the other processor (the Z80), is used in Sonic 1 for digital samples playback. My driver (Mega PCM) works solely on the Z80.


    As mentioned above, during the game, the engine has to communicate with different parts of the system, like the sound and video chips to update audio and video output. The console architecture, in turn, sets some limitations on accessing its own resources. For example, when accessing the YM2612 sound chip using the M68K, because the YM2612 is access within the Z80 memory space, the Z80 must be suspended to allow the M68K to access it.


    During the game, Sonic 1 tends to stop Z80 under the following conditions:

    1. When SMPS engine is running, as it needs access to the YM2612
    2. During DMA-transfers to VRAM or CRAM

    SMPS and DMA-transfers occur constantly at the end of every frame, and their execution can take some time. Hence, the Z80 is being stopped for quite a few times and for quite a while, and with this, Mega PCM execution stops, which means updating DAC (which is controlled entirely by Mega PCM) interrupts, causing distorted sound outputs.


    As an example, pretend that we're playing a simple sine wave. Normally, it would look like this:


    [​IMG]


    But in Sonic 1, the Z80 is being stopped quite a lot and during the stops Mega PCM isn't processing, so the wave isn't updated. If the sound chip does not receive a new wave signal, it will use the last one that was sent for playback, thus making the wave look like this instead:


    [​IMG]


    The grey areas here are the periods when the Z80 is stopped and the sound isn't being updated. If they were cut off in the graph, you'd get a proper sine wave like in the first graph, but long-lasting stops cause the wave to become deformed.


    This guide aims to shorten the times the Z80 is stopped. Unfortunately, there is no way to prevent the Z80 from being stopped, as you will need to stop it in order to allow the M68K to access the YM2612. This is why achieving a good playback quality of digital samples on a SEGA Genesis is a difficult task, that requires a lot of work.


    But here I have done everything for you, the only thing you need to do is follow my instructions below:


    We need to optimize these two mentioned factors the best way possible:

    1. SMPS
    2. DMA-transfers

    Let's start!


    2. Optimizing Z80 stops in SMPS


    Sonic 1 prefers to stop the Z80 as soon as SMPS starts and hold it stopped until the very end. This is understandable, since the sound engine may need access to the YM2612 or Z80 memory (to control Mega PCM) at anytime. However, this doesn't happen too often - most of the time SMPS is just updating its internal events and timers, as reading song files. Running their scripts takes considerably longer.


    So it tuns out that the Z80 is being stopped most of the time for no reason: access to the YM2612 occurs very rarely.


    If you stop Z80 at the very moment when certain SMPS routines need an access to YM or Z80 memory (but not for the whole time SMPS is running), you can get decent optimization. That's what this part of the guide is about.


    At first, you need to add some new macros into your disassembly. They noticeably simplify the process of changing the code, as they help to get rid of long parts of repeating code.




    ; =============================================================


    stopZ80 macro


    move.w #$100,($A11100).l


    nop


    nop


    nop


    @wait@: btst #0,($A11100).l


    bne.s @wait@


    endm


    ; =============================================================


    startZ80 macro


    move.w #0,($A11100).l ; start the Z80


    endm


    ; =============================================================


    waitYM macro


    @wait@: move.b ($A04000).l,d2


    btst #7,d2


    bne.s @wait@


    endm



    Add these at the beginning of Sonic1.asm or where you like to keep your macros.


    Now, here we go!


    Go to label "sub_71B4C". At the very beginning, you will see the following code:



    move.w #$100,($A11100).l ; stop the Z80
    nop


    nop


    nop



    This is the code to stop Z80. Thus, SMPS stops it at the beginning of its main routine execution and starts it at the end.


    You won't need this code anymore, so delete or comment it out.


    Now, go to label "loc_71C44":



    loc_71C44:
    move.b ($A04000).l,d2 ;++


    btst #7,d2 ;++


    bne.s loc_71C44 ;++


    move.b #$2A,($A04000).l ;++


    move.w #0,($A11100).l ; start the Z80


    rts



    Assuming you have installed Mega PCM already, this code will look as shown above. At the end there is a command to start Z80. The lines marked with (++) are the extra lines of code used for Mega PCM. It pulls a little trick required for Mega PCM (the description of the trick you may find in the first post). We'll need this one, but not here.


    Delete the whole code, except for the label name and an rts.


    From now on, SMPS no longer stops Z80 during its execution. But there are few places, where it's required to stop Z80.


    The first place is where SMPS sends Mega PCM a number of the sample to play. As I said, when M68K accesses Z80 memory, the Z80 itself must be stopped.


    Go to label "loc_71C88" and find the following line in the code:



    move.b d0,($A01FFF).l



    We need the Z80 to be stopped before this command executes and to be started directly afterwards. Macros will help us to do so easily. Just replace it with:



    stopZ80
    move.b d0,($A01FFF).l


    startZ80



    Another place is where SMPS commands Mege PCM to pause, unpause and stop playback.


    Go to the label "loc_71E7C" and find this line:



    move.b #$7F,($A01FFF).l; pause DAC



    Append this command in the same way, so it will look like this:



    stopZ80
    move.b #$7F,($A01FFF).l; pause DAC


    startZ80



    Now, find the label "loc_71EFE". Right above it, you'll see:



    move.b #0,($A01FFF).l ; unpause DAC



    Again, append this command as shown above.


    And finally, go find label "loc_725B6" and do this line:



    move.b #$80,($A01FFF).l ; stop DAC playback



    The last thing left to do regarding SMPS, are the routines that access the YM2612.


    This time, the code replacement will be a little larger, plus we're going to add that extra code for Mega PCM, we took out in the beginning.


    Find the routine "sub_7272E" and replace the whole code with this:




    sub_7272E: ; XREF: loc_71E6A


    stopZ80


    waitYM


    move.b d0,($A04000).l


    waitYM


    move.b d1,($A04001).l


    waitYM


    move.b #$2A,($A04000).l


    startZ80


    rts


    ; End of function sub_7272E



    Then, go to "sub_72764" and replace it with:



    sub_72764: ; XREF: loc_71E6A; Sound_ChkValue; sub_7256A; sub_72764
    stopZ80


    waitYM


    move.b d0,($A04002).l


    waitYM


    move.b d1,($A04003).l


    waitYM


    move.b #$2A,($A04000).l


    startZ80


    rts


    ; End of function sub_72764



    If you've followed the above correctly, then congratulations! You've just optimized SMPS.


    Make sure you've done everything right - build the ROM and test it in emulator. The sound and the DAC samples should play as normal.


    If you have problems, you can always check youself by comparing your edits to a clean disassembly with this guide applied. You can find it below, see Attachments section at the end.


    3. Optimizing VBlank routines


    Sonic 1 always tends to stop Z80 on DMA-transfers to VRAM or CRAM. But in fact, stopping processor isn't necessary in this case.


    When DMA-transfer executes, VDP freezes M68K, requests its bus and starts data transfer from any location (in RAM or ROM) to videomemory at high speeds. But the Z80 isn't affected by this as it can function normally. It is only when the Z80 wants to access 68K memory during this time (for example, if it's reading the ROM), it will freeze as well, waiting for 68K to respond. The 68K will only respond when DMA-trasfer is over and the processor continues working.


    However, even taking the case described above into account, forcing Z80 to stop doesn't make sense - it will stop itself when it's required. Moreover, the stop itself takes a while to execute, which means wasting processor cycles from both the Z80 and M68K side. But most importantly, emulators don't emulate Z80 stops during DMA. So, having excluded the forced stops that Sonic 1 performs, you'll get a huge optimization. On real hardware, you won't have optimization this big, however, you'll save some execution time.


    So, we are going to work with VBlank code. It starts from "loc_B10". Its code is quite big and includes number of routines.


    Find the label "loc_BC8", and you'll see this:




    move.w #$100,($A11100).l


    loc_BC8:


    btst #0,($A11100).l


    bne.s loc_BC8



    Do you recognize the Z80 stop code here? We've seen one quite a lot already.


    Completely delete this code (including the label name and one command above it)


    Now, go to "loc_C22". Find and delete this line:



    move.w #0,($A11100).l



    This command started Z80 after its being stopped.


    Go to "loc_C76". You'll see the Z80 stop code again:



    move.w #$100,($A11100).l ; stop the Z80

    loc_C76:


    btst #0,($A11100).l ; has Z80 stopped?


    bne.s loc_C76 ; if not, branch



    Delete it.


    Then, find "loc_D50" (it's few lines below). You'll see Z80 start command, after the stop in loc_C76.



    move.w #0,($A11100).l



    Delete this.


    Now, you know the deal. Find and delete the similar code in the following places:


    loc_DAE


    loc_E64 (move.w #0,($A11100).l is few lines above)


    loc_E7A


    loc_F54


    loc_FAE


    loc_1060 (few lines above)


    loc_1076


    loc_10D4 (at the end, just before an rts)


    That's it!


    This was a hard and continuous guide, and if you successfully finished it, you deserve good praise (and a cookie). From now on, you can play digital samples and songs in near-perfect quality.


    4. Attachment


    A clean disassembly with Mega PCM installed and this guide applied:


    https://dl.dropbox.c...h_MegaPCM_HQ.7z


    This one also contains a bonus: on the title screen, there plays a high-quality digital song from Sonic 1 Megahack: Ultra Edition!


    Enjoy!
     
    Last edited by a moderator: Oct 28, 2012
  14. Cinossu

    Cinossu A blend of secret herbs and spices Member

    Joined:
    Aug 14, 2007
    Messages:
    282
    Location:
    London, UK
    Just a quick note, your last attachment isn't a very clean disassembly; jump dash, edited palettes, changed Sonic 1 drums.. :p
     
  15. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    It's an S1 Hacking Studio 2 disassembly. And for an S1HS2 disassembly, it's clean =P Unfortunately, S1HS2 isn't released in English, but it's done in Russian.


    It's pretty much like the original Hivebrain's disassembly, but with few improvements, more annotated code etc. Jump Dash and Spin Dash are standard features included in this diasm, and S3 drums is what the Mega PCM installation guide offers you to replace the original samples with.


    But that said, there is no big differences between this one and Hivebrain's, and the places you should edit according to the guide are totally the same.
     
    Last edited by a moderator: Oct 28, 2012
  16. Mike B Berry

    Mike B Berry A grandiose return Member

    Joined:
    Jun 6, 2012
    Messages:
    377
    Location:
    New places, newer motivation
    Alright I have a question. Now that I have the samples running at near perfect quality; My MegaPCM contains Voice clips that are activated through the simplest of events... When I play a digital track during the stages and activate one of the voice clips, the voice clip stops the digital track from playing. Once the voice clip is finished, the digital never starts up again, even after the game resumes from pausing. Is there a way to fuse the Tracks and Voices without cancellation? As I said before, my game is whoring with in game voice clips, and it is starting to bug me.
     
  17. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    You can't play two samples at the same time, mainly because Sega Genesis has only one channel for DAC, meaning you can play only one sound stream.


    Doing so is quite a hard technical task. First off, you need to write a software sound mixer, that mixes two sound streams into one to be played in the only DAC channel. Moreover, considering Z80 can access only 32 KB of ROM at chosen location, this adds an unreal amount of restrictions for your samples. If the first and the second sample playing are not in the same bank (which is likely to happen with large voices and digital songs), this makes it almost impossible to be played at a decent speed (let alone the decent quality).


    Actually, I've developed a driver of this kind specially for Sonic 1 Megahack Ultra Edition, which allowed me to play both digital music and voice in the final boss, as well as digital sfx with music on "Sonic has passed" card. I started it as a Mega PCM add-on (as stated in the credits), but this turned out to be a separate driver that only shares the same DAC samples table with Mega PCM, but even the way it uses some values from the table is different.


    The driver holds two 8-bit PCM channels, with the maximum playback rate of 19 KHz (which is way fast actually, considering it processes two sound streams, mixes them, and even does bank switching). That said, it has a lot of limitations, it's not as easy to work with yet. I've been working on improving it's usability and optimizing it a bit more, but now it's frozen due to other projects. Anyways I don't plan to release it yet, and I can't say if it's ever released.
     
  18. DJaftershock

    DJaftershock TWO-DEE-ECKS-GOLD Member

    Joined:
    Nov 20, 2012
    Messages:
    96
    Location:
    The United Kingdom
    Do you wish for credit if used in a hack?
     
  19. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    I'm not demanding it from anyone, but a credit would be appreciated.


    As an author, I'm pleased to see my stuff credited, this shows some respect and gratitude to the work I've done, as well helps other people to know about Mega PCM and maybe try it out (I believe they won't be disappointed about it).
     
  20. DJaftershock

    DJaftershock TWO-DEE-ECKS-GOLD Member

    Joined:
    Nov 20, 2012
    Messages:
    96
    Location:
    The United Kingdom
    Alright, if I ever need this or want to put it in I'll credit you. I really love the stuff you do behind the scenes of these awesome hacks that come out.
     
Thread Status:
Not open for further replies.