TGP:Index
Generalities
Usage of this CPU
The mb86232/86233/86234/fdsp-4, also known as the TGP, is used in Sega Model 1, 2 and 2a. There are two, one is used as a T&L unit, the other as a coprocessor. In model 1, both programs are internal to the cpus. In model 2/2a, the T&L program is internal while the coprocessor program is uploaded.
Model 2b uses a pair of sharcs to replace the TGPs, 2c a pair of TGPx4 (mb86235, fdsp-5 in spirit but not in name), which only has the name in common with the original TGP.
Known information
The datasheet is the best information we have at that point. Grab it, read it, it won't be repeated in here. The copy/paste eagerness of the original author that makes us miss the op-B operations is a pity though. Other sources:
- an IEEE article: "Implementation Of A 4.8 Kbps Voice Codec Based On Pitch-Synchronous DFT Coding".
- US patent 5,099,417 may be related to the chip. Type is correct (32 bits dsp), time is correct. fdsp-3 was 16 bits, and TGPx4 was 64bits. Patents are quite hard on the reading though.
The CPU seems to do everything on 32-bits words, so endianness is irrelevant. The main cpu of model2 (i960) is little-endian though, so the program are stored that way.
Known programs
One can use an emulator to get the uploaded tgp programs, but the simplest way is probably to get them from the roms themselves. Use the Mame source to check which files to use and the interleave. Sizes are in 32bits words. As the md5 shows, some programs are identical.
Game | Region | Address | Size | MD5 |
---|---|---|---|---|
zeroguna | CPU1 | 008cc4 | b28 | 3164f056a727dbd84966dabafa542040 |
vf2 | CPU1 | 003e90 | eda | 514f0ebd82cc03a5c99ff0c20227aa46 |
srallyc | CPU1 | 05ae94 | 682 | ca706c08f84f1c721d5401ea0e50988a |
manxtt | CPU1 | 0787e8 | 6b6 | 2a9f8b8e5cd4d71527d75b07eb5b45e0 |
skytargt | USER1 | e7114c | b2b | 12c49cd5e99ae89b7c1d7d8521ca3443 |
vcop2 | CPU1 | 05d480 | 6f2 | 59675510722a18b847eec08f75d55d05 |
dynamcop | CPU1 | 0b6084 | e80 | 2389379b14042147626cd83cfb98811f |
dyndeka2 | CPU1 | 0b6074 | e80 | 2389379b14042147626cd83cfb98811f |
pltkidsa | CPU1 | 01a378 | 94b | 5fbd0626f6cb73ced9026b420873a27b |
doaa | CPU1 | 009664 | b2b | 12c49cd5e99ae89b7c1d7d8521ca3443 |
daytona | USER1 | 800020 | 7e8 | 8b6f2d1decf8a6be6c4c9005d8361aa5 |
vcop | CPU1 | 0070c0 | 7e8 | dff894ca077cb02cb66e6d1dec8e9020 |
desert | CPU1 | 0016a0 | 7e8 | 8b6f2d1decf8a6be6c4c9005d8361aa5 |
Subset encodings
Registers
Register numbers are encoded in 3+6 bits. The first 3 bits are some kind of type.
Type 0 and 3 address the same registers. 3 adds a "hi, I'm the destination of the move" twist to it though. Here are the known registers:
Value | Register |
---|---|
00-0f | Register file |
10-12 | a, a.e, a.m |
13-15 | b, b.e, b.m |
16-18 | c, c.e, c.m |
19-1b | d, d.e, d.m |
1c | p |
21 | fifo_in |
22 | fifo_out |
Type 6 looks like special registers. Only value 21 is known as being some kind of indirect access register.
ALU op-A operations
ALU operations are encoded in 6 bits. According to the datasheet the shift operations are not supposed to be there but should be opcodes by themselves.
Value | Type | Operation | Value | Type | Operation | Value | Type | Operation | Value | Type | Operation |
---|---|---|---|---|---|---|---|---|---|---|---|
00 000000 | nop | 10 010000 | f32 | p = a * b | 20 100000 | ? | ? | 30 110000 | int | a = a >> c | |
01 000001 | nop | 11 010001 | f32 | p = a * b | 21 100001 | ? | ? | 31 110001 | int | a = a >> d | |
02 000010 | int | c = c & a | 12 010010 | f32 | c = c + p, p = a * b | 22 100010 | ? | ? | 32 110010 | ? | ? |
03 000011 | int | d = d & a | 13 010011 | f32 | d = d + p, p = a * b | 23 100011 | ? | ? | 33 110011 | ? | ? |
04 000100 | ? | ? | 14 010100 | f32 | c = c - p, p = a * b | 24 100100 | ? | ? | 34 110100 | i24 | c = c + a |
05 000101 | ? | ? | 15 010101 | f32 | d = d - p, p = a * b | 25 100101 | ? | ? | 35 110101 | i24 | d = d + a |
06 000110 | ? | ? | 16 010110 | ? | ? | 26 100110 | ? | ? | 36 110110 | i24 | c = c - a |
07 000111 | ? | ? | 17 010111 | ? | ? | 27 100111 | ? | ? | 37 110111 | i24 | d = d - a |
08 001000 | ? | ? | 18 011000 | f32 | c = a + b | 28 101000 | f32 | c = a - b | 38 111000 | ? | ? |
09 001001 | ? | ? | 19 011001 | f32 | d = a + b | 29 101001 | f32 | d = a - b | 39 111001 | ? | ? |
0a 001010 | ? | ? | 1a 011010 | f32 | c = p, p = a * b | 2a 101010 | ? | ? | 3a 111010 | ? | ? |
0b 001011 | ? | ? | 1b 011011 | f32 | d = p, p = a * b | 2b 101011 | ? | ? | 3b 111011 | ? | ? |
0c 001100 | f32 | c = c + a | 1c 011100 | ? | ? | 2c 101100 | ? | ? | 3c 111100 | ? | ? |
0d 001101 | f32 | d = d + a | 1d 011101 | ? | ? | 2d 101101 | ? | ? | 3d 111101 | ? | ? |
0e 001110 | f32 | c = c - a | 1e 011110 | ? | ? | 2e 101110 | int | a = a << c | 3e 111110 | ? | ? |
0f 001111 | f32 | d = d - a | 1f 011111 | ? | ? | 2f 101111 | int | a = a << d | 3f 111111 | ? | ? |
Known missing: xor, not, neg, abs, comparisons (sub). Operations 36 and 37 (at least) are known to set flags though.
ALU op-B operations
Unknown, should be somewhere in the "single move" and "dual move" pattern.
Known instructions
Single move
1f | 1e | 1d | 1c | 1b | 1a | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0f | 0e | 0d | 0c | 0b | 0a | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
000111 | op-A | ?? | source register | destination register |
'??' is usually, but not always, 11. The source can actually be the destination when its type is '3', cf registers description. There is supposed to be an op-B somewhere in there.
Dual move
1f | 1e | 1d | 1c | 1b | 1a | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0f | 0e | 0d | 0c | 0b | 0a | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
000000 | op-A | type? | register 1 | register 2 |
Possible types found in the datasheet are load A&B, load A&C, load A&D, load A store C, load A store D, load C store C, load D store D. 2 bits are not enough. Also, there should be an op-B somewhere.
Branches
1f | 1e | 1d | 1c | 1b | 1a | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0f | 0e | 0d | 0c | 0b | 0a | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1011111 | condition | type | 0 | destination |
Value | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
Type | branch absolute | branch indirect | bsr absolute | bsr indirect | ? | rts | ? | rti |
Value | 00 | 01 | 02 | 06 | 10 | 11 | 12 | 16 |
---|---|---|---|---|---|---|---|---|
Type | == | <= | => | never | != | > | < | always |
One or both of types 4/6 may be load conditional. For absolute branches, the destination is the 16-bits address. For the indirect one, encoding is unknown except for 40xx which designates a register. The conditional values are known wrong except for 6/16, they'll be fixed later when the code around them is sorted a little better. The top bit is the "if/unless" bit, inverting the condition.
Load 24-bits integer
1f | 1e | 1d | 1c | 1b | 1a | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0f | 0e | 0d | 0c | 0b | 0a | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
001110 | dest | value |
Value | 0 | 1 | 2 | 3 |
---|---|---|---|---|
Register | p | a | b | d |
Load floating point
1f | 1e | 1d | 1c | 1b | 1a | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0f | 0e | 0d | 0c | 0b | 0a | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
010110 | dest | value (8 bits only for exponent) |
Value | 0 | 1 | 2 | 3 |
---|---|---|---|---|
Type | c.exponent | c.mantissa | d.exponent | d.mantissa |