|
| 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:
| Page | Offset (W) | Offset (B) | PCM Data | Comment |
| 0 | 0x00.0000 | 0x00.0000 | classic ROM #1 | IC9 on WS/EX, IC8 on AD; MSB only |
| 1 | 0x08.0000 | 0x10.0000 | classic ROM #2 | IC8 on WS/EX, IC7 on AD; MSB only |
| 2 | 0x10.0000 | 0x20.0000 | classic ROM #3 | IC7/6 on WS/AD, IC6/5 on AD |
| 3 | 0x18.0000 | 0x30.0000 | external signal | WA18 separates left/right (only AD) |
| 4 | 0x20.0000 | 0x40.0000 | PCM card | only WA[17:0], no WA18 |
| 5 | 0x28.0000 | 0x50.0000 | EX ROM #1 | IC5 on WS/EX, IC3 on AD; MSB only |
| 6 | 0x30.0000 | 0x60.0000 | EX ROM #2 | IC4 on WS/EX, IC2 on AD; MSB only |
| 7 | 0x38.0000 | 0x70.0000 | EX 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 |
| 00 | P/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].
|
| 02 | RBM | ROM-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)
|
| 04 | WS1 | Wave start pointer [15:0] |
| 06 | WS2 | Wave 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)
|
| 08 | LS1 | Loop start pointer [15:0] |
| 0a | LS2 | Loop 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.
|
| 0c | WE1 | Wave end pointer [15:0] (negated before writing) |
| 0e | WE2 | Wave end pointer [31:16] (negated before writing) |
| 10 | CFG | Crossfade 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
|
| 12 | CFS | Crossfade slew-rate
Slew rate for CFG as signed step (1/256)
0x8001 is maximum fall (used when muted, then CFG=0)
|
| 14 | RST | Reset/Wakeup -- presumed
(0xffff written here during init)
|
| 16 | n/a | unused |
| 18 | n/a | unused |
| 1a | VNE | Voice 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].
|
| 1c | n/a | unused |
| 1e | n/a | unused |
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 |
| 000 | AS |
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.
|
| 040 | AT |
Amplitude target (0-0x7fff)
On stop or release, output of the square-law level curve at ROM address 0xc02e62a multiplied by envelope and modulation
|
| 080 | n/a |
unused
|
| 0c0 | FC1 |
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.
|
| 100 | CS |
Cutoff slew (computation like AS)
|
| 140 | CT |
Cutoff target (0x7fff-0)
0 means fully open; includes key tracking and the zone byte 7.
|
| 180 | n/a |
unused
|
| 1c0 | FC2 |
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-2ff | n/a |
unused
|
| 300 | RSC |
Read-select command
Select amplitude or cutoff register to be read as 0x40+voice / 0xc0+voice.
|
| 3c0 | BSY |
Busy-flag (read)
to be polled until zero before value read can be applied
|
| 000 | VAL |
Result of read-select command
to be read after busy cleared. Used for polling current amplitude (0x040) and cutoff (0x0c0).
|
|
|