Difference between revisions of "Andlabs/Time Trax"

From Sega Retro

Line 52: Line 52:
 
*$33 byte - something to do with scheduling key off
 
*$33 byte - something to do with scheduling key off
 
*xxxx
 
*xxxx
*$58..$xx - FM voice data (TODO what is this on PSG?)
+
*$58..$71 - FM voice data (TODO what is this on PSG?)
 
**$58 byte - algo/FB
 
**$58 byte - algo/FB
 
**$59 byte - AMS/FMS
 
**$59 byte - AMS/FMS

Revision as of 23:06, 14 July 2013

There are only two sound banks.

  • $F0000 - PCM samples
  • $F8000 - music and SFX

everything fits neatly into its bank

Commands

Commands are two bytes each, the first byte being the argument and the second byte being the command itself. There are $20 bytes granted for commands (so up to $10 commands at once), and another byte in RAM used to determine where the command write pointer is. (Another byte stores where it used to be; this is used to tell if we have new commands.)

Commands, where $xx indicates argument:

  • $00 - play sound $xx ($00..$05 music, $06..$nn SFX)
  • $01 - ??
  • $02 - ??
  • $03 - ??
  • $04 - ??
  • $05 - ??
  • $06 - ??

Sound Bank

The beginning of this bank consists of the following:

  • $0 byte - ???
  • $1 big-endian word - bank pointer to list of songs
  • $3 big-endian word - bank pointer to list of FM voices
  • ...

The list of songs consists of just a flat list of big-endian bank pointers.

Song Format

The first thing in each song is a list of n channel headers; their form is:

  • $00 byte - xxxxxxxxxxx; loaded to byte $01 of the in-RAM channel data structure, if $FF, end the list (and thus stop loading the sound, as there is nothing left to do)
  • $01 byte - channel number:
    • $00..$05 - FM1..FM6
    • $06 - PSG1
    • $07 - PSG2

If the value at $00 is xxxxxx than the value already loaded into the channel's $01h, then the following two bytes are merely skipped:

  • $02 little endian word - channel data pointer; laoded to byte $07 of the in-RAM channel data structure

Z80 RAM Channel Data Structure

  • $00 byte - xxxxx; if top bit is set, channel is not played
  • $01 byte - xxxxx; loaded from byte $00 of the sound
  • xxxx
  • $06 little endian word - channel data pointer; loaded from byte $03 of the sound
  • xxxx ($08)
  • $09 byte - xxxxx; note duration?
  • xxxx
  • $21 byte - song data panning value?
  • $22 byte - cached chip panning value?
  • xxxx
  • $31 byte - something to do with scheduling key off
  • $32 byte - something to do with scheduling key off
  • $33 byte - something to do with scheduling key off
  • xxxx
  • $58..$71 - FM voice data (TODO what is this on PSG?)
    • $58 byte - algo/FB
    • $59 byte - AMS/FMS
    • $5A, $5B, $5C, $5D byte - DT/MULT for op 1, 2, 3, 4 respectively
    • $5E, $5F, $60, $61 byte - TL for op 1, 2, 3, 4 respectively
    • $62, $63, $64, $65 byte - AR/RS for op 1, 2, 3, 4 respectively
    • $66, $67, $68, $69 byte - DR/AM for op 1, 2, 3, 4 respectively
    • $6A, $6B, $6C, $6D byte - SR for op 1, 2, 3, 4 respectively
    • $6E, $6F, $70, $71 byte - RR/SL for op 1, 2, 3, 4 respectively
  • xxxx
  • $74 byte - flag to reload panning?
  • $75 byte - some flags
    • bit 7 - if nonzero, reload FM voice (TODO PSG?)
  • xxxx
  • $79 byte - xxxxx; if top bit is set, channel is not played

PCM Sample Bank

The top of this bank contains pointer-length pairs for PCM samples. Pointers are relative to the Z80 memory map (so they are bank pointers). Pointers and lengths are big endian (this is NOT how things usually are done!)

Or in other words

  • $0 word - first sample pointer BIG ENDIAN
  • $2 word - first sample length BIG ENDIAN
  • $4 word - second sample pointer BIG ENDIAN
  • $6 word - second sample length BIG ENDIAN
  • $8 word - third sample pointer BIG ENDIAN
  • $A word - third sample length BIG ENDIAN

and so on until the first PCM data byte

PCM Sample Playback

PCM samples are played back through a buffer: the game reads $80 bytes of sample data, then plays back one byte of ample data every so often. Buffer filling is done all at once and in groups of 8 bytes, with another PCM data write after each group of 8 bytes.

Though the sample buffer is $80 bytes long, the game has two consecutive buffers, switching after one has been played through. Why he doesn't just use one $100-byte-long buffer is beyond me; maybe he wanted it to load to the second buffer while playing back from the first?

pcm-bug

There appears to be a bug in the buffer loading code:

ROM:0B18                 ld      hl, (PCMSampleLength)
ROM:0B1B                 ld      bc, 80h ; 'Ç'
ROM:0B1E                 sbc     hl, bc
ROM:0B20                 jp      m, loc_C67 ; stops sample playback
ROM:0B23                 ld      (PCMSampleLength), hl

if I am reading this correctly, the game will stop playing samples if it cannot fill a buffer completely, leaving the tail end of samples unplayed:

00f0000: 8024 166e 9692 048d 9b1f 0a70 a58f 0869  .$.n.......p...i
00f0010: adf8 156f c367 0975 ccdc 0579 d255 2080  ...o.g.u...y.U .
00f0020: f2d5 093b ____ ____ ____ ____ ____ ____  ...;____________

notice how none of those lengths (except one) are aligned