back to buchty.net Korg Zone up


Wavestation Samples dissected

So there was it, my new Wavestation. Of course, I had looked into the service manual way before the machine arrived to notice something interesting:

The TG88 tone generator can address a total of 16MW of sample memory as 16 banks per 1MW using a 4-bit wavebank WB[3:0] and a 20-bit wave address WA[19:0]. Much more than Korg ever equipped any WS with. So what's going on?

Step 1: Wave memory decoding

Instead of making full use of all banks, waveform memory is only split into two major banks:

  • WB3=0 signals access to PCM ROM, Cartridge and (on the AD) the external signal input.
  • WB2=0 signals access to the internal connectors CN16/CN17 only found in the WS and EX (but not the AD; there, WB2 is n/c). A potentially perfect space for hacking in own samples -- assuming that the OS really checks this area. If not, OS hacking is required.
Quite wasteful! Properly decoding this would give 4 pages per 4MW instead of just two.

A page is broken down with the help of WB[1:0] and WA19. Let's treat them in this very order as a 3-bit number and get the following page structure with each part being 512MW (1MB) in size:

PageOffset (W)Offset (B)PCM DataComment
00x00.00000x00.0000classic ROM #1 IC9 on WS/EX, IC8 on AD; MSB only
10x08.00000x10.0000classic ROM #2 IC8 on WS/EX, IC7 on AD; MSB only
20x10.00000x20.0000classic ROM #3 IC7/6 on WS/AD, IC6/5 on AD
30x18.00000x30.0000external signalWA18 separates left/right (only AD)
40x20.00000x40.0000PCM card only WA[17:0], no WA18
50x28.00000x50.0000EX ROM #1 IC5 on WS/EX, IC3 on AD; MSB only
60x30.00000x60.0000EX ROM #2 IC4 on WS/EX, IC2 on AD; MSB only
70x38.00000x70.0000EX ROM #3 IC3/2 on WS/EX, IC4/1 on AD
  • In order to save cost, Korg stored single-cycle and other low-dynamic waveforms just as 8-bit quantities. For these, the LSB value is always 0x00 by hardware.
  • Page 3 is only used in the AD, not the WS or EX (where it is empty). While WA18 separates the audio channels, there's seemingly no stereo wave signal processing in the other pages.

Step 2: PCM data decoding

Now the fun begins. How does the WS access its PCM data? Luckily, I was able to get hold of the EX ROM dumps, so I didn't have to desolder these. (Anyone having a dump of the classic ROMs, feel free to send me the dump for further analysis!).

In case you want to join in, here's the two EX ROM #1 images fused into a single file, my quick and dirty analyzer tool, and an example log.

So let's concentrate on EX ROM #3 which, unlike #1 and #2, is fully attached (all 16 data lines). And, indeed, there we find management data:

Offset Size Data
0x00000 4 "KORG" marker
0x00004 2 Index marker (0x000f for EXROM, 0x001e for WS Classic, 0x001f for cartridges)
0x00006 10 Name (empty on ROMs / 0x00, seemingly only used on cartridge)
0x00010 8 User-wave reference #1
0x00018 8 User-wave reference #2
(presumed, empty in EX ROM)
0x00020 8 Wave-map reference #1
0x00028 8 Wave-pointer reference #1
0x00030 8 Wave-map reference #2
(copy of #1 in EX ROM)
0x00038 8 Wave-pointer reference #2
(copy of #1 in EX ROM)
0x00040 ... Data area, all samples are 16-bit signed
Offset/Size given in bytes

The 8-byte references hold the following information:

Offset Size Data
0x00 4 Page-based offset to management data
0x04 2 Number of entries in referenced table
0x06 2 Offset to first VS waveform in ROM
0x0083 / 0x0059 for classic/EX WaveROM reference (classic / EX set), 0x0000 otherwise

Comments:
  • Interestingly, the number entry only holds true for references to user-waves and wave maps. For wave-pointer references, not the number of actually present wave pointers is given, but the number of wavemaps is repeated.
  • Page-based offset assumes 16-bit data access, so it's only half of what you'd expect for a byte-sized dump.
  • No idea about the flags yet.

User waves are simple beasts, consisting of just the following:

Offset Size Data
0x00 2 Index to wave map table (start entry)
0x02 1 Wave type:
0x00 (multi-cycle waves) or
0x01 (single-cycle waves / VS waves)
0x03 1 Number of entries (zones) in wave-map table
(0x00 for VS waves)
0x04 10 Name as fixed-size ASCII char array
Offset/Size given in bytes.
Comments:
  • The VS waves don't have an entry in the wave-map or the wave-pointer list as they follow a simple mapping approach:
    • Single-cycle waves (VS waves) are marked with 0x00 entries in the map table.
    • They reside at the start of ROM #1.
    • Each VS waveset comprises 2427 samples containing 9 individual mipmapped waves of fixed size: 514, 364, 514, 364, 258, 183, 130, 66, and 34 samples; seemingly for the lower 3 octaves there's a half-octave stepping (2^0.5=1.41=514/364=258/183), for the upper 3 octaves there's only full-octave stepping.
    • Indexing hence is simple: zero-based wave no * 2427 + mipmap offset

Wave-map entries are already a little more obscure:

Offset Size Data
0x00 2 Index to wave pointer table
0x02 1 Zone top note as MIDI note number
0x03 1 always 0x05 (not read by OS)
0x04 1 Fine-tuning in cents as 8-bit signed quantity
(max values -75 and +97)
0x05 1 Reference key as MIDI note number
0x06 1 Level offset
0x07 1 Cutoff offset
0x08 2 always 0x0000 (not read by OS)
Offset/Size given in bytes.

  • Level offset is the base of a modulation chain that adds 0x7F00 (full scale) minus the summed modulation depths, clamps to 0-0x7FFF and takes the high byte as an index into a square-law curve at 0xC02E62. That curve is 32767*(i/127)2.
    The result is multiplied with the voice level and written to DF88 at @(0x40,R14) and @(0,R14).
    It is hence a level offset in steps of the 0-99 level parameter. For example, -12 is about -2.3 dB and -23 is about -4.6 dB, before modulation.
  • Cutoff offset is added to a chain built from a 0-99 base parameter (table at 0xC02618, 331 units per step), plus key-tracking depth * (note-64)*2.
    The clamped result is written to DF88 as the pair 0x7FFF-x at @(0x140,R14) and x-negated at @(0x100,R14).
    That reads as filter cutoff with key tracking; byte 7 in the structure is a cutoff offset of about one step per unit.

Wave-pointer entries, in term, are less of a mystery:

Offset Size Data
0x00 2 Waveform tuning in signed 1/128 semitones
+/-1536 (0x600) per octave
0x02 3 Waveform start address
0x05 1 unused, 0x00 (aligning)
0x06 3 Loop start address
0x09 1 Play/Loop mode: one-shot (0x00) or looped (0x04)
0x0a 3 Loop/sample end address
0x0d 1 unused, 0x00 (aligning)
Offset/Size given in bytes.
Comments:
  • For data cartridges, the above analysis holds true as well -- just that all management block data is word-aligned w/ only odd-address data being used as 8-bit quantity. Seemingly, the CPU treats cartridge memory differently from the ROM and allows only 16-bit accesses there.
  • With 16M address space, only three address bytes are needed. However, the TG88 can only do word accesses and hence has to fetch 2*2 bytes. Consequently, the 4th address byte contains flags such as the loop flag (0x04).
  • What also becomes obvious from looking at single-cycle wave lengths and loop-end vs. following sample-start addresses is an offset of two bytes (e.g. 514 byte wave-length instead of 512 or next sample starting 2 bytes after the previous sample's loop end). This most likely means that the TG88 does 3-point sample interpolation.

Step 4: TG88 considerations

Korg's TG88 aka MB87726 is another Fujitsu ASIC, like so many custom chips used in Japanese synthesizers (cf. also Casio). Looking at the schematics reveals the following:

  • The Audio interface has 20-bit output data OD[19:0] and 5-bit voice number VN[4:0] plus output waveform enable OWEF. This feeds the digital filter chip DF88 (MB87727).
  • It comprises a DRAM interface of four individual banks (4x RAS/CAS/WE). Not sure if this needs to be specifically activated by MMODE=1.
  • The CPU interface is implemented in true 16-bit, i.e. an 8-bit address space of (256) 16-bit registers. (DMODE=1)

Following the schematics, TG88 occupies a memory region of 1k. OS analysis confirms this, TG88 sits between 08.0000 to 08.03ff and is seemingly divided into 32 blocks of 32 bytes (16 words), which nicely matches the 32-osc architecture. Based on code analysis, the following register map sounds reasonable:
Offset Register Name
00P/S write: Pitch as semitone * fraction (4.12 fixed-point, 0x1000=1.0 sample)
read: status (bit0: address registers busy, bit1: control registers busy)
Pitch value taken from tables at 0xc26e0[semitone] and 0xc02842[fraction].
02RBMROM-bank mode
[3:0] = wave address[23:20]
[6] = mode flag (check use)
[7] = one-shot (taken from pointer's loop-flag byte; idle=0x11)
04WS1Wave start pointer [15:0]
06WS2Wave start pointer [19:16] / voice flag
[3:0] = wave address [19:16]
[7:4] = voice-flag nibble (check use)
read: Result of 0x60 command (see LS2 below)
08LS1Loop start pointer [15:0]
0aLS2Loop start pointer [19:16] / control
[3:0] = loop address [19:16]
[7:4] = further control (check), 0x60 triggers ROM read; result is read from 0x06.
0cWE1Wave end pointer [15:0] (negated before writing)
0eWE2Wave end pointer [31:16] (negated before writing)
10CFGCrossfade target gain (0-99)
one of 100 16-bit values [0:99] ranging 0000-7fff
taken from 0xC02618[x] (pitch-ramp time?)
partner voice gets complementary value
12CFSCrossfade slew-rate
Slew rate for CFG as signed step (1/256)
0x8001 is maximum fall (used when muted, then CFG=0)
14RSTReset/Wakeup -- presumed
(0xffff written here during init)
16n/aunused
18n/aunused
1aVNEVoice Number
[4:0] = voice number
[7] = enable/link to DF88
This register probably also drives the VNx pins via [4:0] and OWEF via [7].
1cn/aunused
1en/aunused

ROM readback uses voice 0. The OS writes +0x02 (bank | 0x10), +0x04, +0x06, then 0x60 to +0x0A. After a short wait it reads +0x06, rotated left by 1 bit. This is how all the management data is read, including the wave-map copying.


Step 5: DF88 considerations
The (unfortunately not resonating) digital filter DF88 has a block-wise access pattern. Each register type occupies 64 bytes (32 words), one word per voice. The presumed register map is as follows:

Offset Register Name
000AS Amplitude slew (write; see table bottom for read)
A release writes -(env>>3) or -1 here.
Computed as 0.625*(x/256) with a minimum of 1.
040AT Amplitude target (0-0x7fff)
On stop or release, output of the square-law level curve at ROM address 0xc02e62a multiplied by envelope and modulation
080n/a unused
0c0FC1 Filter control word 1
MSB: resonance parameter (scaled by 1.28, then *2)
LSB: b[1], b[5] and b[7] select the filter mode.
100CS Cutoff slew (computation like AS)
140CT Cutoff target (0x7fff-0)
0 means fully open; includes key tracking and the zone byte 7.
180n/a unused
1c0FC2 Filter control word 2
For a value k derived from velocity and key tracking, 0xC02404[k] is selected.
for special types, 0x0000, 0x00ff (min), 0xff00 (max), or 0xffff are applied. 0x0000 and 0xffff are outside the above curve table which has values 0x00ff-0xff00.
200-2ffn/a unused
300RSC Read-select command
Select amplitude or cutoff register to be read as 0x40+voice / 0xc0+voice.
3c0BSY Busy-flag (read)
to be polled until zero before value read can be applied
000VAL Result of read-select command
to be read after busy cleared. Used for polling current amplitude (0x040) and cutoff (0x0c0).