Westone sound driver research

Discussion in 'Discussion & Q&A' started by Natsumi, Jun 2, 2017.

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

    Natsumi Phoenix egg Member

    Joined:
    Oct 7, 2011
    Messages:
    695
    Location:
    Long and dangerous river
    Me and MarkeyJester were working on documenting the Westone sound driver during our vacation. We documented most of the driver par a few confusing details, and I continued work on disassembling the tracker data and documenting the bank itself. Here is all the information we have about the driver. I realize this is quite a TL;DR topic, so I don't expect anyone to read this fully. What I suspect you'd be most interested in, is the sound test mod, source code, tracker data disassembler, and information about instruments.

    We have disassembled the Wonderboy in Monster World version of the driver, although I have noticed some improvements in Monster World IV and Mega Bomberman drivers; These versions of the drivers are still undocumented so not all changes are found more than likely. I will not explain the differences past few interesting tidbits. Also, all offsets and specific info will be based on the Wonderboy in Monster World version of the driver.

    The 68000 side of the driver is slightly interesting compared to many other drivers I've seen. The sound driver is loaded at $306, which is pretty standard-looking routine, with the exception it reads the size of the sound driver from the uncompressed sound driver binary itself. The main sound driver binary starts at $98000, which is also the data bank used in the game. The size of the sound driver is reported to be 1A77h bytes long, however last 2 bytes do not seem like they are used by the sound driver in any way.

    Music loading is an interesting bit; Instead of using the normal 2-4 bytes for sound ID's, it actually uses $10 bytes, with a rotating buffer. The music loading subroutine at $366 basically first checks if Z80 is accessing the sound queue. If so, it waits until Z80 is not accessing it. Then it reads the position of where Z80 is accessing the queue, adds the counter (number of sounds played that Z80 has not processed yet), wraps it around by $F, and then adds in the sound queue position. Then, it uses that as an index to Z80 memory, writes the ID, and increments the counter by 1.

    Complicated, I know, but it allows you to have a lot of sounds played during the same frame, and as the driver uses slotted channels instead of static ones, it can play more sounds generally.

    There is also a subroutine at $D9A2, which seems to manually control music volume. The purpose of this subroutine is still unknown.
    I have made a sound test hack for Wonderboy in Monster World, and along with it, extracted all the sound effects I am familiar with. Here is a list of those:
    Code:
    -Music-
    00 - Cave 1, Mushroom cave
    01 - Beginning music
    02 - Lilypad and Childam
    03 - Sky, before sky fortress
    04 - Desert
    05 - Castle and outside the castle in the beginning
    06 - Pyramid
    07 - Cave after Queen Eleanora
    08 - Generic boss theme used by Gragg & Glagg, Ice Bomber,
    09 - Jungle music before beating the tribe
    0A - Alsedo, and the underwater temple
    0B - Eleanora
    0C - Beach areas
    0D - Jungle, normal
    0E - Boss theme used by Tyrant Dragon
    0F - Begonia
    10 - Generic boss theme used by Mushroom boss, Sphinx, Almighty Demon King, and final boss
    11 - Sky castle
    12 - Underwater areas, ice areas
    13 - Unknown jingle
    14 - Title screen
    15 - Spaceship
    16 - End credits
    17 - Final dungeon
    
    -SFX-
    18 - Ocarina button A
    19 - Ocarina button B
    1A - Ocarina button C
    1B - Ocarina tune 1
    1C - Ocarina tune 2
    1D - Ocarina tune 3
    1E - Jump
    1F - Firestorm hit
    20 - Sword
    21 - Get magic(?)
    22 - Door close
    23 - Door open
    24 - Firestorm fire
    25 - Pause screen move selection
    26 - Pause screen select
    27 - Coin drop (from enemy, chest, etc)
    28 - Pick up an item (coin?)
    29 - Pick up a heart
    2A - Press a switch (such as right before Gragg and Glagg)
    2B - Dragon grandson fire
    2C - Found a heart from a chest
    2D - Found the ocarina
    2E - unknown
    2F - A big rock hitting the floor (such as in the dungeon near Lilypad)
    30 - Arrow hits a shield
    31 - unknown
    32 - A big door opens
    33 - Bounce on a jellyfish
    34 - unknown
    35 - Not enough money
    36 - Hurt
    37 - unknown
    38 - A sound that plays when you enter a boss room
    39 - unknown
    3A - unknown
    3B - unknown
    3C - Using vertical teleporters (like the one in the middle of Alsedo)
    3D - Using a potion
    3E - Empty sound effect
    3F - unknown
    40 - unknown
    41 - Elevator
    42 - Elevator stopped?
    43 - Maybe used by the green underwater enemies?
    44 - Getting shrunk or grown (Pygmy armour)
    45 - Longer version of 44
    46 - Splash
    47 - unknown
    48 - unknown
    49 - unknown
    4A - Swimming
    4B - Block falls (such as right before Gragg and Glagg)
    4C - Duplicate of 4B
    4D - unknown
    4E - unknown
    4F - Speaking
    51 - A "Dom" sound when the glass of the spaceship is put in or taken out
    52 - A sound played when your health has been restored and final boss starts
    53 - Game Over jingle
    54 - Inn jingle
    55 - Old Axe being repaired
    56 - Game being paused
    57 - Old man's little brother destroying a wall
    If you are curious about how I did the hack, I simply injected this piece of code into unused ROM memory ($A47C8)
    Code:
        moveq    #0,d6            ; D6 = Sound ID
        lea    $C00000.l,a5        ; VDP DATA
        lea    4(a5),a6        ; VDP CTRL
        lea    .hex(pc),a3        ; hex numbers array
    
        move.w    #$8164,(a6)        ; enable display
        move.l    #$C01E0000,(a6)        ; write pal0 entryF
        move.w    #$EEE,(a5)        ; white
    
        moveq    #0,d0
        move.l    #$54000003,(a6)        ; HSCROLL
        move.w    d0,(a5)
    
        move.l    #$40000010,(a6)        ; VSCROLL
        move.w    d0,(a5)
    
        move.l    #$41860003,(a6)        ; 3x3
        lea    .sndtst(pc),a0        ; sound test text
        moveq    #14-1,d7        ; 14 letters
    .write    move.b    (a0)+,d0        ; get letter
        move.w    d0,(a5)            ; write next letter
        dbf    d7,.write
    
    .loop    stop    #$2300            ; SYNC
    
        jsr    $51C6.w            ; read joy data
        move.b    $FFFF8A7F+2.w,d7    ; get button presses
        or.b    $FFFF8A82+2.w,d7    ; ''
    
        btst    #0,d7            ; check if pressed up
        beq.s    .noup            ; if not, branch
        subq.b    #1,d6            ; sub 1 from ID
    
        cmp.b    #$FA-1,d6        ; check if we are out of the commands area
        bne.s    .noup
        moveq    #$58,d6            ; force to max song ID
    
    .noup    btst    #1,d7            ; check if pressed down
        beq.s    .nodwn            ; if not, branch
        addq.b    #1,d6            ; add 1 to ID
    
        cmp.b    #$58+1,d6        ; check if we are out of the music ID's
        bne.s    .nodwn
        moveq    #$FFFFFFFA,d6        ; force to commands
    
    .nodwn    move.l    #$419E0003,(a6)        ; 3x15
        moveq    #0,d5
    
        move.b    d6,d5            ; copy num
        lsr.b    #4,d5            ; get 4 high bits
        move.b    (a3,d5.w),d5        ; get hex char
        move.w    d5,(a5)            ; save to VDP
    
        move.b    d6,d5            ; copy num
        and.w    #$F,d5            ; get 4 low bits
        move.b    (a3,d5.w),d5        ; get hex char
        move.w    d5,(a5)            ; save to VDP
    
        and.b    #$70,d7            ; check if A/B/C are pressed
        beq.s    .loop            ; if not, branch
    
        move.b    d6,d0
        pea    .loop(pc)        ; keep looping
        jmp    $366.w            ; but play a sound first
    
    .hex    dc.b "0123456789ABCDEF"
    .sndtst    dc.b "Sound Test: 00"
    Before we get to the juicy stuff, I want to talk about the data bank itself. The bank is nothing impressive, but it does contain something quite interesting. That is, pointers! At 9A77h, you can see a little pointer table, containing of following data in order:
    • Music tracker Z80 address
    • Instrument table Z80 address (in sound driver in Wonderboy in Monster World, in ROM in other games)
    • Instrument rate list (YM regs 5xh, 6xh, 7xh, and 8xh used by instruments) Z80 address
    • Envelope data Z80 address
    • Envelope "tracker" data Z80 address
    • Frequency modulation "tracker" data Z80 address
    • Volume modulation "tracker" data Z80 address
    Next, we have some null bytes. I do not think they are ever used and is merely padding. Next is a byte that denotes the number of entries for the next table. If instrument table is in ROM, the next 19*numentries bytes will be the instrument table. Curiously, instrument rate list is in Z80 RAM. The next bytes will likely be the drum mappings table.

    In any case. next byte again tells the number of entries for the next table, which is the music & sfx table! This time, it will just be a table of offsets to the music data. Then, there may be some unknown data at the end, and/or padding to end of bank.
    Ok, so lets dive head first into the tracker, which may be the most interesting portion (or not, a lot of stuff is of interest here!) So, in order to understand the format, we must start at the core; The music & sfx table. This table is simply Z80 word pointers to music and data. Nothing special. The header of each music consists of following;
    • A priority byte, that is higher than or 80h. Higher number determines a higher priority. This priority will be used particularly when finding hardware channels to play the music.
    • a Z80 word containing the initial frequency of the channel
    • Any number of Z80 words, containing the addresses of slot channels. However, be mindful that there is only 0Ah slot channels available.
    • a Z80 word that is 0h. This is the end token, and tells the driver to stop reading new channels.
    While, the header of each SFX consists of following;
    • A priority byte, that is lower than 80h. Higher number determines a higher priority. This priority will be used particularly when finding hardware channels to play the music.
    • a byte, containing the slot channel ID we intend to load to. No clue why they'd do this, instead of dynamically finding a free slot, but whatever. If this byte is 0FFh, it is an end token instead of being an ID. Stop trying to read new channels.
    • a Z80 word, that contains the address of the slot channel.
    • Last two may be repeated for any number of times, being mindful that there are only 0Ah slot channels available.
    The tracker data itself will be the same for each channel, regardless of type (with some exceptions listed appropriately). The following list all the possible bytes you may encounter:
    Code:
    $00-$5F - Play a note, and stop reading the tracker. We read tracker next time tempo counts down.
    $60-$BF - Play a note, but keep reading the tracker anyway.
    $C0-$CF - Read a value from table at 0E35h, multiply it by 3, a save it to the channel timer
    $D0-$D7 - Initializes a loop with counter of flag-$D0, and a Z80 word is supplied as an address to jump to each loop time, when paired with flag $FF
    $D8-$DF - Initializes a loop with counter of flag-$D8, and stores the next address as the loop back point, when paired with flag $FE
    $E0 - Enable frequency modulation. Also sets the speed of modulation.
    $E1 - Disables frequency modulation.
    $E2 - Set timer to a byte value
    $E3 - Set timer to a word value
    $E4 - Loads music tempo
    $E5 - Loads a sound ID (but not any special sounds). This is only available on Mega Bomberman.
    $E6-$EF - Not used.
    $F0 - Set instrument or envelope ID
    $F1 - set tempo divider of the channel
    $F2 - Sets channel frequency to word value
    $F3 - Adds a word value to channel frequency
    $F4 - Sets channel volume
    $F5 - Adds to channel volume
    $F6 - Selects channel type. $00 -> FM, $02 -> PSG, more than $02 -> PSG4
    $F7 - Select hardware channel mask. Basically, what channels can this slot play on?
    $F8 - Enable drum channel mode. Each note now instead sets the panning, tempo, and instrument. FM only.
    $F9 - Set panning. Only controls panning, no AMS/FMS
    $FA - Stop reading tracker. We read tracker next time tempo counts down.
    $FB - Stop hardware channel.
    $FC - Currently unknown. Seems to be a similar deal as the no attack mode in SMPS (Flag $E7)
    $FD - Jump to location pointer by a Z80 word
    $FE - If loop counter is not 0, loop back to the last $D8-$DF flag
    $FF - If loop counter is not 0, loop back to the location pointed by previous $D0-$D7. If there were no loop flags in the stack, instead stop slot channel (end of stream)
    Phew, that was a lot to type. Now, for your convenience, I have made a disassembler for the sound data, along with macros for ASM68K.

    Z80 sound driver
    Now we're getting somewhere! I decided to section this off, as there is so much to get into. As you are reading, I will refer to things inside the Z80 driver. In case you are interested, I have supplied a disassembly of the driver in idb and asm. However, do keep in mind, that there are some things that are named funnily. Hardware channel modulation is actually frequency modulation, and LFO is just volume/amplitude modulation. These both are software modulation and are named incorrectly during making the disassembly. I am simply too lazy to come up with better names and rename a lot of things.

    Without further ado, lets dig into it;
    Since, we have been talking about the tracker, lets segway into the slot channels! Slot channels read the tracker, and control hardware channels. Each slot channel is 20h bytes, and there are 0Ah channels in total. The first thing a slot channel does after being started, is initialize itself at 0BAEh. Thats right, music loading does not initialize channels, the channel does it itself. It does nothing special, just clears some flags and sets some others. After initialization, it always starts reading tracker instead of doing tempo.

    The tempo algorithm for slot channels works off of a tempo accumulator in the main loop. More specifically, music uses it, while SFX use a hardcoded value of 9. The tempo accumulator is a multiple of 3 of the tempo, as its incremented 3 times before slot channels are run. This means, hardware channels run 3 times as often as slot channels.

    The channel tempo timer is then decremented by the tempo accumulator, and if it is 0 or less, the tracker is read. The backup timer is not copied to main timer until we finish reading tracker. I have already touched quite bit on the tracker format, but I will give some complementary information. When the tracker read ends, the timer is saved, and it sets or clears bitfields bit 4 (no-attack?), according to if flag 1C1Ah is not clear. It also stores how many notes we have played that tracker read.

    Playing notes (0CA5h) themselves is a little more complex, so I will let you go into the dirty and complicated details, but here is an overview of the process. If the drum channel mode is set, each note will contain some information, such as the instrument ID and base frequency of that note. It also seems like frequency modulation is disabled too. This mode allows you to play drums with just playing notes, instead of setting up all these values each time you want to play a single drum(!). Since the sound driver does not support DAC, FM drums are used often.

    If the channel is in normal mode, the note ID is added to the high byte of frequency. Then the channel will either try to find the last note playing on hardware channel by this slot channel, and stop the channel, or find a new, free channel. If modulation, the frequency we calculated is the target frequency, and last frequency is current, and the difference is divided by a speed divider, resulting is the modulation speed. If we are not modulating, the calculated frequency will be the current frequency.TL levels or PSG volume will get updated, tempo will be divided by a tempo divider and saved to hardware channel, and hardware channel bits will be set accordingly, and the number of notes played will get incremented.

    Now, here is all the bytes of each slot channels RAM, for your convenience;
    Code:
    00 - bitfield.
    bit 00 - Drum channel bit
        01-03 - unused?
        04 - unknown (suspecting no-attack bit)
        05 - modulation bit
        06 - Initialized bit
        07 - Running bit
    01 - Loop offset
    02-03 - Tracker timer
    04-05 - Backup of timer
    06-07 - Tracker address
    08 - Section ID. 00 = FM, 02 = PSG, more than 02 = PSG4
    09 - Instrument or PSG volume envelope ID
    0A - Hardware channel mask
    0B - Tempo divider
    0C-0D - Frequency
    0E - Volume
    0F - Frequency modulation speed divider
    10 - Priority
    11 - Panning flags, also some other bits(?)
    12 - a note counter
    13 - num of notes played when reading tracker
    14-1F - Loop data. 4 total entries, 3 bytes per entry. First 2 bytes are address, third byte is loop counter.
    Hardware channels are a little bit more involved than slot channels, as such, I've created a few different sections to allow easier explanation of the processing of these channels.
    The very first thing a hardware channel does after being activated, is initialize itself, much like a slot channel. FM, PSG, and PSG4 channels have their own initialization routines. I will divide up FM and PSG as separate, because PSG4 is almost identical to PSG.
    FM channels use the instrument/envelope as an index to the instrument table. Each instrument is $13 bytes large, but do not get fooled, the instruments do not hold same data as SMPS does. Instead, here is their format;
    Code:
    00 - algorithm + feedback
    01 - Key on operators (can not key on operators individually!)
    02 - Panning + AMS/FMS. If Panning control is not 0, instrument does not have control over panning.
    03 - Total level op1
    04 - Detune + Multiple op1
    05 - ID in table with Attack rate + rate scale, decay rate 1, decay rate 2, decay level + release rate for op1
    06-0E - 03-05, for op3, op2, and op4 in that order
    0F-10 - modulation frequency offset
    11 - frequency modulation tracker ID
    12 - volume modulation tracker ID
    As these values are loaded, the game checks for last values, to avoid writing to YM if the values did not change. However, TL values are always written. They use slot operator system, much like SMPS, hence op2 and op3 are swapped. Last, the key on operators are loaded, and keyed on.
    PSG4 channels have a little bit before main PSG stuff; It mutes PSG3 volume and saves the previous hardware volume to mute too. PSG channels use the instrument/envelope as an index to the envelope data table. Each envelope data is 6 bytes. The format goes as follows;
    Code:
    00 - Destination volume
    01 - Volume envelope timer
    02 - Volume speed
    03 - Volume envelope tracker ID
    04 - Frequency modulation tracker ID
    05 - Volume modulation tracker ID
    
    When the PSG channel loads the envelope tracker address, it also reads and processes the first byte. The PSG volumes actually work on range of 00-7F instead of 00-0F like you'd expect. The 3 least significant bits are ignored, and as the volume is sent to the chip, the volume is shifted down thrice. This bigger range of volumes allows for smoother volume effects. Modulation frequency offset is set to -$1800 ($E800). After this, it will follow to a piece of code the FM will go at some point too, but instead PSG will terminate here.
    The tempo algorithm is almost the same as in slot channels, with the exception that the values are much smaller. The hardcoded value for SFX is only 1, and music uses another byte for tempo accumulation. If timer has timed out, PSG is told to start releasing, and FM is keyed off. Curiously, the code checks for FM twice, once it will just jump to key off, second time call. The second time should never be able to occur any way, but this is not fixed in later versions of the driver at all.
    If timer has not expired or we are a PSG channel, another timer is decremented. If its not 0, it jumps to do final modulation stuff. Else, the modulation frequency offset is added to the base frequency, and is saved back to the base frequency. The tracker timer is then checked for 0, and if so, no tracker reading can occur again. Else, it is decremented, and if its nonzero, tracker is not read. Else, it is, this is the format;
    Code:
    00 - Finish tracker read
    01-F7 - Set modulation frequency offset to a word value (next byte is high byte)
    F8 - Load modulation shift counter and a new timer (other method of modulating?)
    F9 - Load timer
    FA - Finish modulating
    FB - Load a new base frequency
    FC - Negates the modulation frequency offset, and loads a new timer
    FD - Loop initialization. Contains a loop counter
    FE - Decrement loop counter. If not 0, jump to address
    FF - Jump to address
    Next, we do more modulation code. The current modulation frequency is loaded, and modulation speed is added to it. Next, it checks if we finished the modulation, and if so, stop modulating completely. If mod shift counter is nonzero, it uses some very strange code to calculate the base frequency, which is otherwise loaded from RAM.
    These are then added together with frequency offset into a final modulation frequency value.
    We tried to figure out this piece of code (08A6h) with Markey, but we could not come up with any pattern or find out what it would be used for or how it would affect the frequency. It seems completely irregular, and always drifts off by -$80 each $100 values calculated. I do not know if it means anything, but sharing it here. Here is the piece of code I used to calculate the drift:
    Code:
    public static void main(String[] asd) throws IOException, InterruptedException {
            int a = 0x32, b = 0;
            for(int i = 0;i < 0x100;i ++){
                a = calc(a);
                b += ((a & 0x80) == 0 ? a : a | 0xFF00);
                System.out.println(String.format("%03X", i + 1) +" = "+ String.format("%04X", b & 0xFFFF));
            }
        }
    
        private static int calc(int i) {
            boolean[] x = { true, true, false, true, true, false, true, false, true, false };
            int a = i, c = i;
    
            for(int z = 0;z < x.length;z ++){
                if(x[z]){
                    c <<= 1;
                } else {
                    a += c;
                }
    
                a &= 0xFF;
                c &= 0xFF;
            }
    
            return (a + 0x7F) & 0xFF;
        }
    As seen on the code above, the frequency base number is multiplied by 75h, and 7Fh is added. My only guess it is to do something with a kind of harmonic sound, but I have no clue further than that.
    After processing frequency modulation, then volume modulation is processed right after it. Volume modulation uses a timer much like what frequency modulation does at the start. If timer has not expired, all the code is skipped. Otherwise, volume offset is added to the current volume, and saved to current volume. Then the tracker timer is checked for 0, and if it is, no tracker read may occur anymore. Else, it is decremented, and if is 0, tracker is read. The format is as follows;
    Code:
    00 - Finish tracker read
    01-F7 - Set modulation frequency offset to a word value (next byte is high byte)
    F9 - Load tempo
    FA - Finish modulating
    FB - Load a new base volume
    FC - Negates the modulation volume offset, and loads a new timer
    FD - Loop initialization. Contains a loop counter
    FE - Decrement loop counter. If not 0, jump to address
    FF - Jump to address
    Curiously, nothing special related to volume modulation happens before we update the volume later on.
    Whoop-dee-doo, still more tracker like systems to dive into. Fun, isn't it? This is a PSG-specific one though, and quite interestingly different. It is done in 2 parts; when note is on, the tracker format is read, when when note is off, it then does a little sequence of releasing. Lets first talk about note on.

    We use our familiar timer ritual here; if timer is initially 0, no read may ever occur, else it is decremented and checked for 0. Only if latter case holds true, then the tracker is read. The format goes as follows;
    Code:
    F0 - End marker, channel is stopped
    anything else - Lower 4 bits are the timer value, and upper 4 bits are shifted down once, main volume is added, and then it is saved to the current volume.
    At the end, the modulated volume is added to current volume, and saved as the final volume. After some time, the channel informs the note has released, and another process occurs inside this piece of code.

    When we initially start releasing, a release destination volume is checked against current volume, and if they match, and destination volume is nonzero, the release is immediately halted. After initialization, it is checked if the releasing is done, or if the timer had reached 0 or less. If so, the next step of release is executed.

    Current volume is added to the release volume speed. Destination volume is checked for 0, and if it is, volume is checked for a negative value. If it is, the hardware channel is stopped for good. Meanwhile, if destination volume was nonzero, the code checks if volume had reached its value, and the volume is adjusted to exactly it and releasing is stopped. Then finally, the modulated volume is added to current volume, and saved as the final volume.
    This code is still a mystery to me, and I could not unfortunately explain how it works. Would be great if someone has any ideas of how it would work, will update the post when an explanation is figured out! The code is located at 053Ch.
    Updating volume between FM and PSG is quite different, so I'll split them into two. PSG4 is basically same as PSG, except it does not check for last value, and uses another flag to store volume.
    Channel loads the slot operators bits, loads main volume and adds volume modulation volume. Then it checks if it is running a music track. If so, master volume is loaded, else 0 is loaded. That is added to the calculated volume, and limited to max of 7Fh. Next it enters a loop, to process op1, op3, op2 and op4 in that order.

    In the loop, TL level is loaded, and it is checked if this is a slot operator. If so, calculated volume is added to TL level, and is capped to 7Fh. The volume is compared to the last volume, and if they do not match, the volume is written to YM. Then it loops for all of the 4 ops before terminating.
    Channel checks if it is running a music track. If so, master volume is loaded, else 0 is loaded. Then, current volume is added, and limited to max of 7Fh. 3 least significant bits are cleared, and then is compared to last volume. If not same, volume is saved back, and shifted down onto its place (00-78 -> 00-0F). Next, the channel ID is shifted onto its place too, volume update bits and the volume is added, and the final value is put into PSG.
    That marks the end of our journey with hardware channels. What a mess it was, writing it took forever. Moving right along!
    We are almost through everything, finally! We are discuss the rest of the logic right here, separated into nice little sections. But first, the main loop. The first part is run thrice, to process hardware, and sound queue 3x faster than slot channels. The code first runs the timer B synchronization routine, to keep everything nice and tidy. Then, volume control (fading etc) is processed, and music/sounds are loaded. Then, main tempo and tempo accumulator's fraction is added together, and saved to tempo accumulator. After, the high byte of tempo accumulator, aka the quotient, is added to the tempo count variable. After which hardware channels get processed. After that is done 3 times, slot channels are processed and tempo count is cleared, main loop is started again.
    During driver initialization, Timer B is set to 0EDh. First, the driver checks a debug access flag, and if nonzero, it will in addition do some strange debug stuff. I will not explain what it does, because we have been unable to find the 68000 code controlling this. It seems completely unused code and does not seem to hold much purpose.

    There is a flag that controls how often the code will wait for Timer B. Usually it is set to 1, which means it will wait every single time. However, a bit of the pause control flag will also allow the timer to be set to 4, which would mean each 3 times Timer B is waited for. This features seems to be unused in Wonderboy in Monster World however. If the timer will be waited for, it will loop until YM tells the Timer has expired, and after it, it is reloaded.
    Volume control has 2 modes; User control, and fade control. In user control, the 68000 will dictate the volume to the driver, and it sets it as the master volume. In fade control mode, fade counter is checked for 0, and if not, the fading is in process. Fade amount and fade speed are added together, and the result's bit 7 is checked. If is set, the speed's bit 7 is checked. If set (aka fading out), all music is stopped. In both cases, the fade counter and master volume are cleared.

    If however, bit 7 was not set for result, the fade amount is stored back (updates master volume too!), and fade counter is decremented. If fade counter reached 0, fade list address is loaded, and a byte is read to the fade counter. Then speed is loaded and saved, along with the new list position.
    First, the 68000 access to the sound queue is blocked, and the queue count is checked for 0. If zero, 68000 access is granted. In the other case, queue count is decremented and the next byte is loaded from the list (much alike 68000, but queue count is not used for indexing). 68000 queue access is granted. Next, if the ID is a command, the appropriate routine for music commands are called. Here is a list of all music commands;
    Code:
    F9 - All music is stopped (Mega Bomberman only)
    FA - Slow type fading out is started
    FB - 68000 will control master volume
    FC - Pause music
    FD - Unpause music
    FE - Quick type fading out is started
    FF - All sounds are stopped
    Else, the game checks if the music ID is valid, and if it is, only then it will load the music/sound. First, its pointer is loaded from the bank, and then the priority is loaded. If it is positive, sound effect loading happens, else it loads music.

    First, lets check out music. Some fading stuff is cleared, and the main tempo is loaded. Tempo accumulator is cleared and all music channels are stopped. The tracker address is loaded, and if 0, loop is terminated. Else, tracker address is saved, channel is set running, and priority is saved. Then just loop.

    Next, sound effects. Channels with same priority are stopped (Don't know why exactly, would seem like they could just override the same sound effect anyway?). The channel ID is loaded, and if 0FFh, the loop is stopped. Then it checks if the channel is running or has lower priority. If not, the channel is not overridden. Else, the hardware channel is stopped if running, channel is set as active, priority and tracker address is saved, and loop is continued.
     
Thread Status:
Not open for further replies.