Skip to content

Instantly share code, notes, and snippets.

@freem
Last active July 11, 2016 18:14
Show Gist options
  • Save freem/357e622263e08a9bf28f to your computer and use it in GitHub Desktop.
Save freem/357e622263e08a9bf28f to your computer and use it in GitHub Desktop.
notes on the SM1 driver
=======================
[Introduction]
The main purpose of this document is to figure out how the SM1 driver accepts
parameters for some of its commands.
Examples are:
* Command $0A: Fade Out
* Command $14: Stop ADPCM-A sound?
* Command $18: Start ADPCM-A sound?
* Command $19: Start ADPCM-B sound?
* routine sub_DDA
* routine sub_E1D
All of these set byte_FE58 to a nonzero value. This variable is used to tell
the NMI to skip its normal behavior and jump to loc_A8 instead.
================================================================================
[Variables]
So throughout all of this, we have a few variables to keep track of.
* processParam (0xFE2C): I originally thought this controlled param parsing.
It is related to it, but it seems to only be used in the loops where the
param is being waited for. A more accurate name would be "waitForParam"?
* byte_FE39: used in handleCommand and various param-taking commands.
Its value is sometimes written to byte_FE3A.
* byte_FE3A: used in pcmaCommandHandler, pcmbCommandHandler, ssgCommandHandler,
sub_498, loc_7E8, sub_881. At the beginning of the PCMA, PCMB, and SSG
handlers, byte_FE3A is set to the value in byte_FE39.
* byte_FE47: the current index into the command buffer? Used in the NMI, sub_1AD,
and sub_B66.
* byte_FE48-0xFE57: command buffer?
unsure of the final address in the buffer, but byte_FE48 is where it begins.
* byte_FE58: the overall "parse the next command as a parameter" var.
Checked in NMI, cleared in Main Loop. Set with command_18, command_19,
command_14, command_0A, sub_DDA, sub_E1D.
* byte_FE5C: related to command buffer index?
only used in Main Loop and sub_B66.
* byte_FE66: Infinite loop in RAM variable?
Set to 0 in main loop; set to 1 at loc_BF3.
Unrelated to sound driver parameter design.
================================================================================
[The NMI]
As in a typical sound driver, the command from the 68K is received and
checked for specific values. After those checks, the value in byte_FE58 is
compared. If it's nonzero, the execution jumps to loc_A8.
Otherwise...
sub_B66 is called with a = 0x80. The routine at sub_b66 "compares values at
0xFE5C and 0xFE47 (increments 0xFE47 if match)". Not sure what purpose this
serves.
After this, byte_FE47 is read into a, masked with 7, and used as an index
into byte_FE48 (Command buffer?). 0xFF is written to this location, followed
by the value in b. a is incremented, masked with 7, and stored into byte_FE47.
processParam is set to 0, and NMI_reply is jumped to.
(loc_A8)
loc_A8 copies b back to a (as the reverse was done just after the byte was
grabbed from the 68K), sets byte_FE39 to the value in a, then clears the
processParam (0xFE2C) variable. The NMI continues as usual (e.g. falls
through to NMI_reply).
(NMI_reply)
replyto68K is loaded (set in the main loop), the top bit is masked, and the
final value is written to ports 0x0C and 0x00.
================================================================================
[The Main Loop]
Understanding this one is tough.
First, byte_FE58 (parse next command as param) and byte_FE66 (used in slot
switching) are set to 0. Interrupts are enabled.
soundBufIndex (not the same as byte_FE47) is loaded into a, masked with 7,
and stored into byte_FE5C. This value is used as the index into the command
buffer (byte_FE48). soundBufIndex is updated after being added to the
command buffer, and a value from the buffer is read into a. handleCommand is
called if the command is nonzero.
Execution continues...
Interrupts are enabled again, byte_FE65 is read; if zero, execution begins at
the beginning of the loop again. If not, a random byte is written to the 68K,
the top bit is OR'd, and sent to port 0x0C. main loop continues from the top.
================================================================================
[handleCommand routine]
The handleCommand routine sets processParam to 0xFF, clears the command at the
current buffer position, reads the next value from the buffer, and sets
byte_FE39 to that value. That value is compared as though it was a sent command.
If the command was not handled, byte_FE3B and bye_FE39 are set to 0.
================================================================================
[Command Example: command $0A - Fade Out]
1) byte_FE58 is set to 0xFF
2) processParam is loaded into a; if nonzero, keep looping.
after the loop stops...
1) processParam is set to 0xFF
2) unk_FABE is loaded into iy
3) byte_FE39 is loaded into a
4) write a to 2(iy)
5) write 1 to byte_FE23
6) write 2 to byte_FE36, FE37
================================================================================
[Command Example: command $18 - Start ADPCM-A sound?]
1) byte_FE58 is set to 0xFF
2) processParam is loaded into a; if nonzero, keep looping.
after the loop stops...
1) processParam is set to 0xFF
2) byte_FE39 is loaded into a.
3) This is then compared to 0x10. If "C" is set, jump to loc_175.
4) If not, byte_4499 is loaded into hl, bc is pushed, and sub_c66 is called.
bc is then popped after this runs.
5) or A, return if zero.
6) load byte_FE39 into a, disable interrupts, and jump to loc_1CE.
[Addendum: loc_175]
loc_175 is handleCommand after the param stuff is dealt with. see above.
[Addendum: loc_1CE]
loc_1CE is the normal ADPCM-A command handler after param stuff is handled.
[Addendum: sub_c66]
1) load a into c
2) and with 7
3) load 3 into b (counter)
loc_c6B:
1) shift c right
2) djnz loc_C6B
after loop finishes...
3) add hl,bc
4) load a into b, 0x80 into c
5) or a, jump to loc_C7A if 0
loc_c76:
1) shift c right
2) djnz loc_c76
after loop finishes...
loc_C7A:
1) load c into a
2) and with value at hl and return
================================================================================
[Trying to put it all together]
The parts of this system:
* The NMI
* The Main Loop
* The Commands that accept params
<NMI>
The NMI is pretty simple.
In the Mr.Pac v1.5b driver, the required commands are checked before the param
waiting variable (byte_FE58). I am not sure if this precludes certain values
from being sent as parameters.
If byte_FE58 is nonzero, the parameter handling routine is run. The original
byte (still in b) gets copied to byte_FE39, and processParam is set to 0.
<Main Loop>
byte_FE58 is one of the two variables that get set to 0 at the beginning of
the main loop. When it comes to params, the main loop doesn't do much.
<Commands that accept Params>
Commands that accept parameters seem to perform the following steps:
1) Set byte_FE58 to 0xFF (making the NMI use the "handle params" code)
2) Load processParam into a and check if it's nonzero. If it is nonzero, loop
back to the beginning of the command (in this case, step 1).
3) After the loop ends, processParam is set to 0xFF again (since it will be
re-set to 0 in the NMI).
4) The parameter value sent from the 68K seems to be in byte_FE39.
Do with it what you wish.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment