Skip to content

TG100 sample ROMS

Technical discussion which is not directly related to VGM files. Talk about Hardware and Software.

Moderator: Staff

Post by vampirefrog »

grauw wrote:Vampirefrog, it’s the OPL4 linux driver, it’s existing for ages. Not sure if it came from an official vendor, or some Linux dev who got provided the official data, or retrieved the table data from the SoundEdge software or by disassembling the SoundEdge drivers, or just manually build the table, could be anything. Could try to find and disassemble the SoundEdge Windows driver if you want a second data point.
All I want is to decipher the bit fields in that element table at 0x176ab in the program ROM.

Post by vampirefrog »

Okay so here's how it works.

- The instrument table at 0x10410 has the following data (this is what I've figured out so far):
- Instrument uses two elements (bool)
- Level (volume) for element 1 and 2
- Detune for element 1 and 2
- Pan for element 1 and 2
- Name (8 chars, space padded)
- Region group ID for both elements

Each entry in the table starts with 16 bytes that hold Level, Detune, two elements bool, and other stuff I haven't figured out yet.
Next 8 bytes is the name
Next comes a pair of 36 bytes, one for each element
First two bytes use only the lower 4 bits to select the region group
Within these 36 byte chunks are also Pan bytes (pan goes from L7 to R7)

The Region group ID is an index into the table at 0x1841C (I might be off by a couple of bytes). That table has the start index into the region table at 0x176ab. The next index in the same table is the next start index, you can use that to compute the number of entries used.

So to view the samples used by an instrument, go to the instrument table at 0x10410, go to bytes 24 and 25 for element 1, and 60 and 61 for element 2 (if the first byte of the entry is 1, which means it uses the second element). Put those bytes together and you have an index into the table at 0x1841C. Read the value there (let's call it N) and count N entries, or N*9 bytes from the start of the region table at 0x176ab. You select the region you're interested in, depending on which key you're playing, and you have the sample number, which is an index into the sample rom table, which is at its beginning.

See this spreadsheet for more details.

Post by vampirefrog »

Here is a capture from the chip:

sync word is 0xeeee, every 9 words
data is samples (1/44100 of a second) then D0-D7 and A0-A3

It goes out of sync sometimes, so I'm not sure what's up. I'm still trying to figure it out. Capture was made with a 3000000 baud rate and a FT232 board. I am not sure what the results should look like, so can someone check if the data is good?

I've logged since turn on of the device up to playing the demo song until the end.
Attachments
foo.cap.gz
(1.32 MiB) Downloaded 278 times
  • ctr Offline
  • Posts: 492
  • Joined: 2013-07-17, 23:32:39

Post by ctr »

Not sure if I can make much sense of this unless /CS (/GEW8) and /WR is logged as well. I see there is 32 bytes of data between each 0xEE,0xEE sequence.

An example sequence looks like this (there are 8 four byte sequences per sync word and not 9):

Code: Select all

a6 03 ff f8
72 0b 00 fb
a2 03 ff f8
d6 07 94 eb
82 06 00 fb 
a4 03 ff f8
d0 0c 00 fb
a4 03 ff f8
So the "ff,f8" part in the first line is the data present on the bus?. I think that you could actually get rid of the "samples" part, as timing is not something we're too concerned about now, just the data instead.

Post by vampirefrog »

Here are the connections:
/WR - PD15
/CS - PE1
D0-D7 - PD0-PD7
A0-A3 - PD8-PD11
NC - PD13,PD14
/RD - PD12

I am triggering on /WR, and checking if /CS is low in the interrupt handler. Here is the ISR:

Code: Select all

static int mark = 0;
void exti15_10_isr(void) {
	if((GPIOE_IDR & GPIO1) == 0) { // Check /CS (PE1)
		uint16_t w = timer_get_counter(TIM1);
		TIM1_CNT = 0; // reset counter
		_write(1, (char *)&w, 2);

		w = GPIOD_IDR; // get the whole 16 bits
		_write(1, (char *)&w, 2);

		mark++;
		if(mark == 8) {
			w = 0xeeee;
			_write(1, (char *)&w, 2);
			mark = 0;
		}
	}
	exti_reset_request(EXTI15);
}

Post by vampirefrog »

Ok here's another capture, this time in binary and with a marker for every write. you'll notice that the second byte of data always has the 0x80 bit set, that's because it's /RD.

At the beginning it goes out of sync at some point then it seems to be ok.

Code: Select all

void exti15_10_isr(void) {         // trigger on /WR (PD15)
	uint16_t w;
	if((GPIOE_IDR & GPIO1) == 0) { // check if /CS is on
		w = 0xeeee;                // marker
		_write(1, (char *)&w, 2);  // send marker

		w = TIM1_CNT;              // get samples since last ISR call
		TIM1_CNT = 0;              // reset timer to zero
		_write(1, (char *)&w, 2);  // send samples

		w = GPIOD_IDR;             // get D0-D7, A0-A3, /RD, NC, NC, /WR
		_write(1, (char *)&w, 2);  // send data

	}
	exti_reset_request(EXTI15);
}
Edit: I've fixed the timing code. I was counting up to 3773 at the mcu's clock, which is 168MHz in this case. I've changed it to prescale by 3810 and count up to 65535. This way the timing should be for real. 168000000 / 3810 = 44094.5. Reattached another log. I've noticed that when I hit play on the demo song, it freezes up a bit, I assume the CPU is doing some intensive work and not sending any data to the YM chippo.
Edit 2: Perhaps if we can figure out which messages to ignore, I can reduce the log's size.
Edit 3: I can also log VGM data directly, not sure which commands though.
Attachments
foo.cap.gz
(309.75 KiB) Downloaded 267 times

Post by vampirefrog »

Perhaps we can ask that R. Belmont fella why MAME uses only 3 of the 4 address lines
  • User avatar
  • ValleyBell Offline
  • Posts: 4767
  • Joined: 2011-12-01, 20:20:07
  • Location: Germany

Post by ValleyBell »

It's actually emulating 3 of the 16 possible addresses.

I looked at your captured data and made a list of commands and how often they occoured:

Code: Select all

80 18	x3104
82 00	x2330
83 FE	x3104
84 03	x109044
86 00	x473003
86 01	x3107
86 AF	x3107
87 00	x2325
88 04	x3167
88 3D	x35
8B 03	x3104
8B 09	x954
8C 05	x227465
8D AF	x701
8D F2	x431
8E 02	x3107
8E 05	x3107
8F 09	x609
8F 8B	x3167
8F AF	x880
8F C4	x366
8F CC	x11
8F CE	x45
8F EE	x36
8F EF	x1524
The first byte is A0-A3/RD/WR, the second byte is D0-D7. The last column is how many times the commands were captured. (in decimal)
It doesn't make sense to be, though. I think there should be a more commands.
  • ctr Offline
  • Posts: 492
  • Joined: 2013-07-17, 23:32:39

Post by ctr »

I think it might be a good idea to enter the TG100 test program and do a capture while executing test #10. It will play a tone on all 28 channels sequentially.

And, yes, I made a similar program to analyze the log and get pretty much the same results as Valley Bell. It seems like there are only 26 unique commands in the log.

Post by vampirefrog »

Okay, so I thought that 0x80 bit being set was a little weird, since the code was meant to read it while it was low. It's actually /CS, not /RD. The ISR is a bit slow and reads just after the /CS and /WR lines have gone back to high state, so all that data was bogus. I'll make it so it uses a DMA to read the GPIO port instead, perhaps that'll make it more responsive. This is on an STM32F4 board, here's what it looks like: http://www.ebay.com/itm/STM32F4Discover ... SwOyJX-NcZ

If you look on the scope, you'll see the top signal, yellow, comes a bit after the middle one (cyan). Top is an output pin from the micro, middle is /CS and bottom is /WR.
IMG_0722.JPG
Attachments
IMG_0715.JPG
IMG_0721.JPG
IMG_0720.JPG
  • ctr Offline
  • Posts: 492
  • Joined: 2013-07-17, 23:32:39

Post by ctr »

So I made a small breakthrough in reversing the program ROM.

Looking at the MAME source, I found out that the YMW258/MultiPCM maps channels in a slightly unusual way. In MAME the channel map table looks like this

Code: Select all

const int32_t multipcm_device::VALUE_TO_CHANNEL[32] =
{
	0, 1, 2, 3, 4, 5, 6 , -1,
	7, 8, 9, 10,11,12,13, -1,
	14,15,16,17,18,19,20, -1,
	21,22,23,24,25,26,27, -1,
};
i found an equivalent table in the program rom at 0x14e10 to used to convert logical channel numbers to physical (how the chip recognizes them).

Code: Select all

ROM:94E10 ChannelMapTable:.byte 0, 1, 2, 3, 4, 5, 6, 8! 0
ROM:94E10                 .byte 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0x10, 0x11! 8
ROM:94E10                 .byte 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19, 0x1A! 0x10
ROM:94E10                 .byte 0x1B, 0x1C, 0x1D, 0x1E! 0x18
This led me to find the write to chip function

Code: Select all

ROM:81F6A GEW8GetChannelWrite: ! near             ! CODE XREF: sub_8264A+25p
ROM:81F6A                 mov:g.w r4, @-sp
ROM:81F6C
ROM:81F6C loc_81F6C:                              ! CODE XREF: GEW8GetChannelWrite+5j
ROM:81F6C                 btst.b  #0:8, @unk_800:8 ! check busy flag?
ROM:81F6F                 bne     loc_81F6C:8
ROM:81F71                 mov:g.b @unk_3957:16, r4
ROM:81F75                 extu.b  r4
ROM:81F77                 add:q.w #-2, sp
ROM:81F79                 stc.b   ep, @sp
ROM:81F7B                 ldc.b   #9:8, ep
ROM:81F7E                 ! assume ep:9
ROM:81F7E                 mov:g.b @(0x4E10:16,r4), r4 ! Convert channel number to physical
ROM:81F82                 ldc.b   @sp, ep
ROM:81F84                 ! assume ep:nothing
ROM:81F84                 add:q.w #2, sp
ROM:81F86                 mov:s.b r4, @unk_801:8  ! set channel
ROM:81F88                 jsr     SmallDelay:16
ROM:81F8B                 mov:s.b r0, @unk_802:8  ! set register number
ROM:81F8D                 jsr     SmallDelay:16
ROM:81F90                 mov:s.b r1, @unk_800:8  ! write data
ROM:81F92                 mov:g.w @sp+, r4
ROM:81F94                 rts
ROM:81F94 ! End of function GEW8GetChannelWrite
(There are also variations of this function to only set the channel and to write data to a register.)

Just like the MAME MultiPCM core, this only uses three addresses to set the channel number, register number and to write the data.

Post by vampirefrog »

ctr wrote:I think it might be a good idea to enter the TG100 test program and do a capture while executing test #10. It will play a tone on all 28 channels sequentially.
Okay, thanks for the tip. I've managed to enter that menu, it only works with the midi cable connected from in to out.

Post by vampirefrog »

Okay, I've managed to configure the DMA controller, here's the code for anyone curious, and a new capture. Unfortunately I'm still struggling to get timing information in there (trying to configure a second DMA stream triggered by the same signal to read a timer counter), but I think the data is probably less bogus this time. It starts before the demo song and ends after it.

The capture file format is now: 0xeeee, then 16 words of data. The wiring is now, from LSB to MSB: D0-D7, A0-A3, /RD, /CS, NC, NC. To read the file, ignore words with /CS high. These are captured by DMA, so they should be closer to the truth. I have no way of checking currently, would need a 16 channel logic analyzer.
Attachments
tg100.c
(4.32 KiB) Downloaded 238 times
foo.cap.gz
(2.62 MiB) Downloaded 245 times

Post by vampirefrog »

Here are some more captures, this time with timing. I am using one DMA transfer, without much buffering, but it seems to work well. I was hoping to use DMA entirely but I couldn't figure it out.

Included are test mode 10, startup, idle state and demo song.
Attachments
startup.cap.gz
(15.89 KiB) Downloaded 242 times
idle.cap.gz
(6.7 KiB) Downloaded 224 times
demosong.cap.gz
(309.28 KiB) Downloaded 228 times
testmode.cap.gz
(425 Bytes) Downloaded 220 times

Post by vampirefrog »

Here is another capture. It is a few midi notes. See if you can recognize the song.

With this one I've cleared the top bits of the second byte, so it's purely D0-D7 and A0-A3
Attachments
notes.cap.gz
(20.51 KiB) Downloaded 230 times
Post Reply