Romhacking.net

Romhacking => Newcomer's Board => Topic started by: Hop on February 13, 2016, 12:11:58 pm

Title: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 13, 2016, 12:11:58 pm
I'm trying to save memory from sf2hfj from the "gfx" region of memory using the MAME debugger. I can save memory from the ':maincpu' region easily e.g.

save mem.bin,0,20000

This saves the memory as show in the Memory Window with Region: ':maincpu' selected.
 
If I change the dropdown from "Region ':maincpu' " to "Region: ':gfx' " then I see the memory I want to save, but I don't know how to specify that I want to save that memory in the command line. I've tried

save gfx.bin,0,20000,:gfx

but this fails with:

Unable to find CPU ':gfx'

Does anybody know how to perform this task?
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: mz on February 13, 2016, 02:53:50 pm
The fourth parameter seems to be for CPU, not memory region. Unless this game uses multiple CPUs, you shouldn't put anything there.

Now, I would look into the sf2hfj driver source code and see how's the memory map of this game. So, if the "gfx" region of memory is at 60000h or something, you would then use "save mem.bin, 60000, 20000".

Maybe... I'm just speculating here, since I've never used this feature. :D
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 13, 2016, 06:23:43 pm
Thanks for the response. I agree that the fourth parameter would appear to be a CPU id of some sort. The reason I thought that the save command might accept a "memory region"(?) id is because of the this:

>saved test,0,10
No matching memory space found for CPU ':maincpu'

This made me think that ':gfx' might also be a "CPU" name.


I have been looking at mame\src\mame\drivers\cps1.cpp  but I can't see how the gfx roms map into address space. This is what I have figured out (with my added comments)

/* B-Board 91634B-2 */
ROM_START( sf2hfj )
   // 3 x 512KiB code ROM files
   // 0x80000 = 524288 = 512KiB
   // 3 x 0x80000 = 0x180000 bytes of code from address 0 in "maincpu" address space
   ROM_REGION( CODE_SIZE, "maincpu", 0 )      /* 68000 code */
   ROM_LOAD16_WORD_SWAP( "s2tj_23.8f", 0x000000, 0x80000, CRC(ea73b4dc) SHA1(efbc73277d00bac86505755db35225e14ea25a36) )
   ROM_LOAD16_WORD_SWAP( "s2tj_22.7f", 0x080000, 0x80000, CRC(aea6e035) SHA1(ce5fe961b2c1c95d231d1235bfc03b47de489f2a) )   // == s2te_22.7f
   ROM_LOAD16_WORD_SWAP( "s2tj_21.6f", 0x100000, 0x80000, CRC(fd200288) SHA1(3817b67ab77c7b3d4a573a63f18671bea6905e26) )   // == s2te_21.6f
   
   // gfx ROMS
   // ========
   // length = 0x600000 = 6.00MB (12x512KB) of graphics ROM in "gfx" address space
   // The gfx roms are loaded into memory in sets of 4, interleaved by word (2 bytes). NOT word swapped.
   // This can be seen by looking at the offsets which are in groups of 4:
   //      {0x000000, 0x000002, 0x000004, 0x000006}
   //      {0x200000, 0x200002, 0x200004, 0x200006}
   //      {0x400000, 0x400002, 0x400004, 0x400006}
   // So if for example
   //   s92_01.3a is full of words 1122
   //   s92_02.4a is full of words 3344
   //   s92_03.5a is full of words 5566
   //   s92_04.6a is full of words 7788
   // then gfx memory will look like: 11 22 33 44 55 66 77 88 11 22 33 44 55 66 77 88 all the way up to 0x1fffff at which point the next 4 files
   // will start off in the same interleaved pattern
   ROM_REGION( 0x600000, "gfx", 0 )
   ROMX_LOAD( "s92_01.3a",  0x000000, 0x80000, CRC(03b0d852) SHA1(f370f25c96ad2b94f8c53d6b7139100285a25bef) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-1m.3a
   ROMX_LOAD( "s92_02.4a",  0x000002, 0x80000, CRC(840289ec) SHA1(2fb42a242f60ba7e74009b5a90eb26e035ba1e82) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-3m.5a
   ROMX_LOAD( "s92_03.5a",  0x000004, 0x80000, CRC(cdb5f027) SHA1(4c7d944fef200fdfcaf57758b901b5511188ed2e) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-2m.4a
   ROMX_LOAD( "s92_04.6a",  0x000006, 0x80000, CRC(e2799472) SHA1(27d3796429338d82a8de246a0ea06dd487a87768) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-4m.6a

   ROMX_LOAD( "s92_05.7a",  0x200000, 0x80000, CRC(ba8a2761) SHA1(4b696d66c51611e43522bed752654314e76d33b6) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-5m.7a
   ROMX_LOAD( "s92_06.8a",  0x200002, 0x80000, CRC(e584bfb5) SHA1(ebdf1f5e2638eed3a65dda82b1ed9151a355f4c9) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-7m.9a
   ROMX_LOAD( "s92_07.9a",  0x200004, 0x80000, CRC(21e3f87d) SHA1(4a4961bb68c3a1ce15f9d393d9c03ecb2466cc29) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-6m.8a
   ROMX_LOAD( "s92_08.10a", 0x200006, 0x80000, CRC(befc47df) SHA1(520390420da3a0271ba90b0a933e65143265e5cf) , ROM_GROUPWORD | ROM_SKIP(6) )    // == s92-8m.10a

   ROMX_LOAD( "s2t_10.3c",  0x400000, 0x80000, CRC(3c042686) SHA1(307e1ca8ad0b11f3265b7e5467ba4c90f90ec97f) , ROM_GROUPWORD | ROM_SKIP(6) )
   ROMX_LOAD( "s2t_11.4c",  0x400002, 0x80000, CRC(8b7e7183) SHA1(c8eaedfbddbf0b83311d2dbb9e19a1efef0dffa9) , ROM_GROUPWORD | ROM_SKIP(6) )
   ROMX_LOAD( "s2t_12.5c",  0x400004, 0x80000, CRC(293c888c) SHA1(5992ea9aa90fdd8b9dacca9d2a1fdaf25ac2cb65) , ROM_GROUPWORD | ROM_SKIP(6) )
   ROMX_LOAD( "s2t_13.6c",  0x400006, 0x80000, CRC(842b35a4) SHA1(35864a140a0c8d76501e69b2e01bc4ad76f27909) , ROM_GROUPWORD | ROM_SKIP(6) )
   
   ROM_REGION( 0x18000, "audiocpu", 0 ) /* 64k for the audio CPU (+banks) */
   ROM_LOAD( "s92_09.12a",  0x00000, 0x08000, CRC(08f6b60e) SHA1(8258fcaca4ac419312531eec67079b97f471179c) )   // == s92_09.11a
   ROM_CONTINUE(            0x10000, 0x08000 )

   ROM_REGION( 0x40000, "oki", 0 ) /* Samples */
   ROM_LOAD( "s92_18.11c",  0x00000, 0x20000, CRC(7f162009) SHA1(346bf42992b4c36c593e21901e22c87ae4a7d86d) )
   ROM_LOAD( "s92_19.12c",  0x20000, 0x20000, CRC(beade53f) SHA1(277c397dc12752719ec6b47d2224750bd1c07f79) )

   ROM_REGION( 0x0200, "aboardplds", 0 )
   ROM_LOAD( "buf1",         0x0000, 0x0117, CRC(eb122de7) SHA1(b26b5bfe258e3e184f069719f9fd008d6b8f6b9b) )
   ROM_LOAD( "ioa1",         0x0000, 0x0117, CRC(59c7ee3b) SHA1(fbb887c5b4f5cb8df77cec710eaac2985bc482a6) )
   ROM_LOAD( "prg1",         0x0000, 0x0117, CRC(f1129744) SHA1(a5300f301c1a08a7da768f0773fa0fe3f683b237) )
   ROM_LOAD( "rom1",         0x0000, 0x0117, CRC(41dc73b9) SHA1(7d4c9f1693c821fbf84e32dd6ef62ddf14967845) )
   ROM_LOAD( "sou1",         0x0000, 0x0117, CRC(84f4b2fe) SHA1(dcc9e86cc36316fe42eace02d6df75d08bc8bb6d) )

   ROM_REGION( 0x0200, "bboardplds", 0 )
   ROM_LOAD( "s9263b.1a",    0x0000, 0x0117, CRC(0a7ecfe0) SHA1(f75e7eed4604fcf68273197fe3dd7f0d7a313ada) )
   ROM_LOAD( "iob1.12d",     0x0000, 0x0117, CRC(3abc0700) SHA1(973043aa46ec6d5d1db20dc9d5937005a0f9f6ae) )
   ROM_LOAD( "bprg1.11d",    0x0000, 0x0117, CRC(31793da7) SHA1(400fa7ac517421c978c1ee7773c30b9ed0c5d3f3) )

   ROM_REGION( 0x0200, "cboardplds", 0 )
   ROM_LOAD( "ioc1.ic7",     0x0000, 0x0117, CRC(0d182081) SHA1(475b3d417785da4bc512cce2b274bb00d4cc6792) )
   ROM_LOAD( "c632.ic1",     0x0000, 0x0117, CRC(0fbd9270) SHA1(d7e737b20c44d41e29ca94be56114b31934dde81) )
ROM_END


This file doesn't seem to give the addresses that the gfx roms load into. I've wrote some C code to load the 3 x 4 sets of graphics ROM files and build the final buffer in memory, which matches the contents of the MAME debugger Memory: Region ':gfx' exactly. (Ref http://www.romhacking.net/forum/index.php?topic=13172.0 (http://www.romhacking.net/forum/index.php?topic=13172.0)), but I think there simply must be a way to save/dump the memory from :gfx from within the debugger.


I've also looked in the filemame\src\mame\video\cps1.cpp which shows:

#define mapper_S9263B   { 0x8000, 0x8000, 0x8000, 0 }, mapper_S9263B_table
static const struct gfx_range mapper_S9263B_table[] =
{
   // verified from PAL dump:
   // FIXME there is some problem with this dump since pin 14 is never enabled
   // instead of being the same as pin 15 as expected
   // bank0 = pin 19 (ROMs 1,3) & pin 18 (ROMs 2,4)
   // bank1 = pin 17 (ROMs 5,7) & pin 16 (ROMs 6,8)
   // bank2 = pin 15 (ROMs 10,12) & pin 14 (ROMs 11,13)
   // pins 12 and 13 are the same as 14 and 15

   /* type            start    end      bank */
   { GFXTYPE_SPRITES, 0x00000, 0x07fff, 0 },

   { GFXTYPE_SPRITES, 0x08000, 0x0ffff, 1 },

   { GFXTYPE_SPRITES, 0x10000, 0x11fff, 2 },
   { GFXTYPE_SCROLL3, 0x02000, 0x03fff, 2 },
   { GFXTYPE_SCROLL1, 0x04000, 0x04fff, 2 },
   { GFXTYPE_SCROLL2, 0x05000, 0x07fff, 2 },
   { 0 }
};


static const struct CPS1config cps1_config_table[]={
   ...
   {"sf2hfj",      CPS_B_21_DEF, mapper_S9263B, 0x36 },
}


But I'm not sure how to interpret this.

Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: mz on February 13, 2016, 09:41:57 pm
This file doesn't seem to give the addresses that the gfx roms load into.
Have you tried this one already?
ROM_REGION( 0x600000, "gfx", 0 )
That looks like it... So I would try with "save mem.bin, 0x600000, 20000", if you haven't already.

(It's also surprisingly similar to what I randomly said in my previous post...)
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 14, 2016, 03:24:12 am
Sorry for not answering directly before. The command "save mem.bin, 600000, 200000"  completes but the output file is completely full of zeroes. This is what I'd expect because the contents of "M68000 ':maincpu' program space memory" in the Memory Window is zero from 0x600000 to to 0x800000. From the definitions


#define ROM_START(name)                             static const rom_entry ROM_NAME(name)[] = {
#define ROM_REGION(length,tag,flags)                { tag, NULL, 0, length, ROMENTRYTYPE_REGION | (flags) },
#define ROM_END                                     { NULL, NULL, 0, 0, ROMENTRYTYPE_END } };


it would look like ROM_REGION( 0x600000, "gfx", 0 ) defines a region with length 0x600000 rather than address 0x600000.
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: mz on February 14, 2016, 03:35:30 am
it would look like ROM_REGION( 0x600000, "gfx", 0 ) defines a region with length 0x600000 rather than address 0x600000.
It does look like that.

In that case, the line "ROM_REGION( CODE_SIZE, "maincpu", 0 )" should give you the "gfx" address. Just find what that CODE_SIZE constant is, and replace it in "save mem.bin, CODE_SIZE, 20000".

(If this is still wrong, maybe I should just set up MAME and get that game, but I'm kind of busy right now.  :()
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 14, 2016, 04:00:01 am
In that case, the line "ROM_REGION( CODE_SIZE, "maincpu", 0 )" should give you the "gfx" address. Just find what that CODE_SIZE constant is, and replace it in "save mem.bin, CODE_SIZE, 20000".

It would make sense if the regions are mapped contiguously in address space.

#define CODE_SIZE 0x400000

But looking at or saving memory memory at this address it is all zeroes. It would seem that the "gfx" region ( and all of the other regions: e.g. "audiocpu", "oki") have their own address space.
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: mz on February 14, 2016, 04:10:48 am
Last suggestion: copy the first few bytes of the gfx region, for example 0xabcdef0123456789 or something, then use the cheat search functionality to find those bytes in memory. That should give you the address you're looking for...

(If the cheat search functionality doesn't let you search for enough bytes or within that region, dump all the memory and search with a hex editor.)
Title: Re: Saving gfx memory with MAME Debugger - \"Unable to find CPU \':gfx\'
Post by: Hop on February 14, 2016, 04:29:11 am
I think I've tried this by running a "find". Is this equivalent?

The gfx region starts with a load of FF FF FF.. but at address 0x160 we have something searchable: 03 63 63 03 ...  but

f 0,800000,d.03636303            ---> Not Found
f 900000,700000,d.03636303  ---> Not Found

Note that running a find or save in the region from 800000 crashes MAME. It looks like this is because it is trying to read from a memory mapped IO write only address.

-----------------------------------------------------
Exception at EIP=000000000251F0A3 (ioport_port::read()+0x0023): ACCESS VIOLATION
While attempting to read memory at 0000000000000008
-----------------------------------------------------
RAX=0000000000000000 RBX=0000000000000008 RCX=0000000000000000 RDX=0000000000000006
RSI=000000001275AEA0 RDI=0000000000000000 RBP=00000000002267E0 RSP=0000000000226760
 R8=1F96B0E5106A9E57  R9=C6A4A7935BD1E995 R10=C6A4A7935BD1E995 R11=00000000002267B0
R12=0000000000000000 R13=0000000000226960 R14=0000000000000001 R15=0000000000800140
-----------------------------------------------------
Stack crawl:
  0000000000226780: 000000000251F0A3 (ioport_port::read()+0x0023)
...


February 14, 2016, 04:36:58 am - (Auto Merged - Double Posts are not allowed before 7 days.)
As an experiment I tried changing the first 2 words of Region: ':maincpu' in the memory window to 1234 5678 then running f 0,100000,d.12345678 gives "Found at 000000"

However changing the first 4 bytes of Region: ':gfx' in the memory window to 12 34 56 78 then running f 0,800000,d.12345678 or f 900000,700000,d.12345678 both return"Not found"
Title: Re: Saving gfx memory with MAME Debugger - \"Unable to find CPU \':gfx\'
Post by: mz on February 14, 2016, 04:43:33 am
As an experiment I tried changing the first 2 words of Region: ':maincpu' in the memory window to 1234 5678 then running f 0,100000,d.12345678 gives "Found at 000000"

However changing the first 4 bytes of Region: ':gfx' in the memory window to 12 34 56 78 then running f 0,800000,d.12345678 or f 900000,700000,d.12345678 both return"Not found"
I see. Well, I don't know much about MAME and not a lot of people use it here, since you can't run hacked ROMs on it...

I'd now recommend the obvious stuff, such as asking in some official MAME or MESS forums.

Also, the people at http://www.mamecheat.co.uk/forums/ should have some experience with finding stuff in memory.

If you ever find an answer, please share it with us!
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 14, 2016, 04:44:56 am
Thanks for your help. I'll report back when I've figured this out.
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: AWJ on February 17, 2016, 02:51:43 am
MAME developer here. Unfortunately, what you're trying to do isn't supported by MAME. There's no debugger command to dump an arbitrary ROM region to a file. You can only dump CPU address spaces, and the graphics ROMs on CPS1 (like most arcade hardware) aren't mapped into any CPU's address space. Graphics ROMs in arcade machines are like CHR ROM on the NES: the CPU doesn't need to (and usually can't) read them, they're directly connected to the graphics hardware.

If you want the graphics ROMs concatenated into a single file so you can load it up in a tile editor, just write a python script or something to do it. Actually, I see you've already figured that out :)
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 17, 2016, 04:36:46 am
Thanks for the information. I'm slowly beginning to understand how it all works.

Am I correct in thinking that after the game is up and running in MAME, editing the gfx region of memory in the debugger will have no effect on the actual in-game graphics because at that point the data has already been decoded into another (MAME) buffer? If so, is there a way to ask MAME to re-decode?
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: AWJ on February 17, 2016, 05:03:19 am
Thanks for the information. I'm slowly beginning to understand how it all works.

Am I correct in thinking that after the game is up and running in MAME, editing the gfx region of memory in the debugger will have no effect on the actual in-game graphics because at that point the data has already been decoded into another (MAME) buffer? If so, is there a way to ask MAME to re-decode?

Yes, that's correct for drivers that use MAME's built-in sprite and tilemap primitives to emulate the graphics (which includes cps1, and every driver that has a block of GFXDECODE macros in the source and that lets you see the game's graphics by pressing F4) No, there isn't a debugger command to force the graphics to be re-decoded. And before you ask, there isn't any way to save changes you make to the ROMs using the debugger hex editor either. The MAME debugger is generally more targetted toward developing new drivers and fixing emulation bugs than at facilitating ROM hacking.
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 17, 2016, 05:09:01 am
I'm more interested in finding out how the data is stored and decoded that hacking the ROM. I'll step through the code that uses the data defined in the GFXDECODE macros to figure out how the data is stored/coded.  It would be helpful to be able to see any changes I make in realtime to confirm that my understanding is correct.

Can I ask: What defines where the 8x8, 16x16, 32x32 gfx data is stored?
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: AWJ on February 17, 2016, 05:54:15 am
I'm more interested in finding out how the data is stored and decoded that hacking the ROM. I'll step through the code that uses the data defined in the GFXDECODE macros to figure out how the data is stored/coded.

Oh, I can just explain that to you. I'll use CPS1's 16x16 layout (the sprites and one of the three scrolling layers) as an example and break down what each line in the gfx_layout struct means:

static const gfx_layout cps1_layout16x16 =
{
        16,16,
        RGN_FRAC(1,1),
        4,
        { 24, 16, 8, 0 },
        { STEP8(0, 1), STEP8(32, 1) },
        { STEP16(0, 4*16) },
        4*16*16
};


16,16, <- this is the size in pixels, i.e. 16x16

RGN_FRAC(1,1), <- this determines the total number of available tiles as a function of the ROM region size... don't worry about it

4 <- this is the bit depth, 4bpp (16 colors)

{ 24, 16, 8, 0 }, <- this is the bit position, relative to the start of a tile, where each bit plane starts. In this case the bit planes are 8 bits apart, meaning the graphics are planar like NES or SNES graphics. Packed pixels like Megadrive or GBA would be { 0, 1, 2, 3 }.

{ STEP8(0, 1), STEP8(32, 1) }, <- this is the bit position where each column of pixels starts. STEP8(x, y) is a macro that means "8 values starting at x and going up by y each time". So the first (leftmost) 8 columns are packed into the first byte (1 bit apart), and the second 8 columns come four bytes (32 bits) after and are likewise packed into a byte.

{ STEP16(0, 4*16) }, <- this is the bit position where each row starts. Unlike the columns, all the rows are evenly spaced, each eight bytes (4*16 bits) apart.

4*16*16 <- this is the distance in bits between consecutive tiles. Most often it's simply the size of a tile, in this case 4*16*16 bits.
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 17, 2016, 06:08:02 am
Wow. That's great. Many thanks. What defines where the memory for each of the different sets is starts? So for example knowing the 16x16 layout, where does the actual 16x16 data start in ROM/memory?
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: AWJ on February 17, 2016, 06:22:15 am
Wow. That's great. Many thanks. What defines where the memory for each of the different sets is starts? So for example knowing the 16x16 layout, where does the actual 16x16 data start in ROM/memory?

With CPS1, that's... apparently rather complicated and varies per game. Have a look at cps_state::gfxrom_bank_mapper().
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 18, 2016, 07:21:15 am
Thanks for the info. I've got some code up and running that loads the roms and displays the 8x8 gfx data. I'll extend it to the other sets later. I then came across MAME decode() function which does the same thing!

Quote
graphics ROMs on CPS1 (like most arcade hardware) aren't mapped into any CPU's address space. Graphics ROMs in arcade machines are like CHR ROM on the NES: the CPU doesn't need to (and usually can't) read them, they're directly connected to the graphics hardware.

This is very interesting. I'd never really thought that the CPU would not have access to the data. I guess the video hardware would have to be used to perform pixel perfect collision detection, and any tile metadata would have to be defined in the program address space.

I was also expecting to see palette data in the ROMs, but it looks like this is stored in the CODE and put in place as and when required - it seemed a little strange having the colour indices and the colours themselves in two different regions of memory, but I guess it gives allows for palette effects (like the fades I've seen in sf2).

Quote
The MAME debugger is generally more targetted toward developing new drivers and fixing emulation bugs than at facilitating ROM hacking.

Does this imply that any pull requests made that don't move MAME in this direction would be rejected?
 
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: AWJ on February 18, 2016, 01:42:45 pm
Thanks for the info. I've got some code up and running that loads the roms and displays the 8x8 gfx data. I'll extend it to the other sets later. I then came across MAME decode() function which does the same thing!

This is very interesting. I'd never really thought that the CPU would not have access to the data. I guess the video hardware would have to be used to perform pixel perfect collision detection, and any tile metadata would have to be defined in the program address space.

I was also expecting to see palette data in the ROMs, but it looks like this is stored in the CODE and put in place as and when required - it seemed a little strange having the colour indices and the colours themselves in two different regions of memory, but I guess it gives allows for palette effects (like the fades I've seen in sf2).

Did you notice how all the 8x8 tiles are duplicated twice? Once in the pair of ROMs that contains the left halves of the 16x16 tiles (and the first and third quarters of the 32x32 tiles) and once in the other pair of ROMs. That's why the MAME driver specifies two 8x8 layouts that are offset by 32 bits but otherwise identical. It's a quirk of the CPS1 background generator hardware--"odd 8-pixel columns come from one pair of ROMs, even 8-pixel columns come from another pair" even applies to 8x8 tiles, meaning the same tiles have to be duplicated in both pairs of ROMs, otherwise the game could only display a particular tile at odd screen positions or at even positions depending on which ROMs it was in.

CPS1 (and most arcade hardware) doesn't do hardware collision detection. Collision detection is done in software and it's done with bounding boxes, not at the pixel level. Yes, the "graphics" ROMs contain nothing but pixel patterns; the colors and the metadata for assembling the tiles into characters and backgrounds are in the "program" ROMs. Some arcade hardware actually does have the background tilemaps in ROM (e.g. 8-bit Capcom games like 1943 and Gunsmoke) but in that case it's still in a separate set of ROMs from the pixel patterns. And older arcade hardware (again, 8-bit Capcom games being an example) does often have the color palette in ROM rather than in RAM, but again it's in dedicated ROMs (typically tiny 256x4-bit BPROMs) that are separate from the graphics ROMs. Since the colors are in ROM, those older games can't do fade-ins or other dynamic color manipulation.

Are you familiar at all with 2D console hacking (NES, SNES, GBA...)? Most 2D arcade hardware (at least hardware from the Japanese makers and Atari) is similar to 2D consoles in general principles, or at least a lot more similar to 2D consoles than to modern gaming platforms that you might be tempted to compare with (PC, Flash, Unity, mobile OSes) Specifically, most 2D arcade hardware is like a NES game with CHR ROM. Everything is tile-based; the CPU says "draw character #1234 at position (125, 63)" and doesn't know or care about the individual pixels at all.

Quote
Does this imply that any pull requests made that don't move MAME in this direction would be rejected?

Not at all! It just means that the MAME debugger has a somewhat different feature set from console emulator debuggers, most of which were developed by and for ROM hackers.

Note that the features I listed (editing decoded graphics from the debugger, and saving modified data back to the ROMs) would be extremely difficult to implement in a way that worked with all drivers. The big obstacle to saving modified ROMs is that the ROM loading macros that say where each byte from each ROM goes don't always tell the whole story; the ROM data is often postprocessed in some way by the driver (usually to decrypt encrypted ROMs) In order to support saving back to the ROMs, every single driver that has a ROM decryption function would need to define an inverse function to reconstruct the encrypted data.
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 18, 2016, 05:52:59 pm
Quote
Did you notice how all the 8x8 tiles are duplicated twice?

No - I've only managed to extract the first 8x8 set so far. TBH I've been struggling to figure out how to calculate the start addresses for the other sets from the gfx_range mapper_S9263B_table, or perhaps I am looking in the wrong place? Do all of the sets start at address zero, but only have a valid range in the address space?

I've tried to confirm that all the 8x8 tiles are duplicated twice with a quick search of gfx memory in a hex editor. I expected every 4-byte aligned 4-byte sequence from the region near address zero to be seen elsewhere. This seems true for most, but not all sequences.

Quote
two 8x8 layouts that are offset by 32 bits but otherwise identical

I don't understand where the 32 bit offset comes from. From my understanding memory is built up by adding two bytes from ROM 1, then two from 2, then 3 then 4 then back to 1 and round again - each cycle will be 8 bytes. Each 8x8 element is 8x8x4 bits = 32 bytes and is contiguous in memory (albeit in short 1D bitplanes). I think this means that the data for each 8x8 element must be split between the 4 ROMs. This would mean that to draw 8x8 pixels data is required from all 4 roms. I must have misunderstood something.

EDIT: I think I've figured this out. In the 8x8 layouts the y spacing is 4*16 bits = 8 bytes, whereas each row is only 4 bytes. So the each pair of tiles in the pair of 8x8 sets are interleaved together row by row. Each row each tile from the first set uses 2 bytes from the ROM 1 and 2 bytes from ROM 2. Each row each tile from the second set uses 2 bytes from ROM 3 and 2 bytes from ROM4. This also explains why layout.charincrement is 2x the tile size in bytes.


Quote
otherwise the game could only display a particular tile at odd screen positions or at even positions depending on which ROMs it was in.

I might get this for backgrounds in which the tiles are drawn on a regular grid, but if an 8x8 sprite can be drawn at any pixel position then do you mean odd and even positions within a 2D array of tiles that make up an object?

Quote
Are you familiar at all with 2D console hacking (NES, SNES, GBA...)?

I've programmed the Amiga 500 and modern GPUs, but I missed out on the classic tile era and I'm enjoying learning how it all worked. I guess that old video hardware liked to have data fed to it in a particular format for performance. I actually find it quite hard to believe that data has to be duplicated to facilitate it in this case!
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: AWJ on February 18, 2016, 08:37:09 pm
No - I've only managed to extract the first 8x8 set so far. TBH I've been struggling to figure out how to calculate the start addresses for the other sets from the gfx_range mapper_S9263B_table, or perhaps I am looking in the wrong place? Do all of the sets start at address zero, but only have a valid range in the address space?

I've tried to confirm that all the 8x8 tiles are duplicated twice with a quick search of gfx memory in a hex editor. I expected every 4-byte aligned 4-byte sequence from the region near address zero to be seen elsewhere. This seems true for most, but not all sequences.

I don't understand where the 32 bit offset comes from. From my understanding memory is built up by adding two bytes from ROM 1, then two from 2, then 3 then 4 then back to 1 and round again - each cycle will be 8 bytes. Each 8x8 element is 8x8x4 bits = 32 bytes and is contiguous in memory (albeit in short 1D bitplanes). I think this means that the data for each 8x8 element must be split between the 4 ROMs. This would mean that to draw 8x8 pixels data is required from all 4 roms. I must have misunderstood something.

EDIT: I think I've figured this out. In the 8x8 layouts the y spacing is 4*16 bits = 8 bytes, whereas each row is only 4 bytes. So the each pair of tiles in the pair of 8x8 sets are interleaved together row by row. Each row each tile from the first set uses 2 bytes from the ROM 1 and 2 bytes from ROM 2. Each row each tile from the second set uses 2 bytes from ROM 3 and 2 bytes from ROM4. This also explains why layout.charincrement is 2x the tile size in bytes.

Yep, you've got it. I think it works that way because the hardware fetches data from all four ROMs in parallel, over a 64-bit data bus (each ROM is 16 bits wide). The ROM chips available in 1988 weren't very fast, so rendering 3 scrolling layers plus sprites at an 8MHz dot clock required a fair bit of parallelism.

Quote
I might get this for backgrounds in which the tiles are drawn on a regular grid, but if an 8x8 sprite can be drawn at any pixel position then do you mean odd and even positions within a 2D array of tiles that make up an object?

There are no 8x8 sprites on CPS1. There are four layers: a tilemap (scrollable character grid) of 8x8 tiles, a tilemap of 16x16 tiles, a tilemap of 32x32 tiles, and sprites that are made out of 16x16 cels. It will help you understand the MAME source if you know that "scroll 1" is the 8x8 tilemap, "scroll 2" is the 16x16 one, and "scroll 3" is the 32x32.

Okay, if you look at the mapper table you'll see that there are three "banks", each corresponding to one set of four ROMs. Bank 0 is the ROMs numbered 1, 2, 3 and 4; bank 1 is the ROMs numbered 5, 6, 7 and 8, and bank 2 is the ROMs numbered 10, 11, 12 and 13. Banks 0 and 1 are entirely used by sprites, while bank 2 contains the last few thousand sprites, then the 32x32 tiles (SCROLL3), then the 8x8 tiles (SCROLL1), then the 16x16 tiles (SCROLL2).

You can see in gfxrom_bank_mapper() that if the requested code is a sprite or a SCROLL2 tile it gets multiplied by 2 (left shifted by 1, same thing), if it is a SCROLL3 tile it gets multiplied by 8, and if it is a SCROLL1 tile it is left alone. So the "unit" that the ranges in the mapper table are measured in is one duplicated 8x8 tile (which is half a 16x16 tile, or one eighth of a 32x32 tile). Meaning 16 bytes in one ROM, or 64 bytes in the whole interleaved set.

So, to answer your original question: the 32x32 tiles are at 0x20000-0x3FFFF in ROMs 10, 11, 12 and 13 (that's the address in each individual ROM--multiply by 4 if you're working with them interleaved). The 8x8 tiles are at 0x40000-0x4FFFF in those same ROMs, and the 16x16 tiles are at 0x50000-0x7FFFF. The first 0x20000 bytes of ROMs 10, 11, 12, 13, and the entirety of the other eight ROMs, contain sprites (which are in exactly the same format as 16x16 tiles).

If you press F4 and look at the graphics in MAME, you'll see the entire region (all 12 ROMs) decoded in all three formats, meaning you have to page through lots and lots of garbage before you get to valid 8x8 or 32x32 tiles. That's because MAME uses the same GFXDECODE info for all CPS1 games, regardless of which mapper they use. Most 1980s sprite-and-tile arcade systems weren't as flexible as CPS1 with its single ROM bus and per-game mappers; in most systems each layer had an entirely separate bus to its own dedicated ROMs, and the MAME F4 graphics viewer was designed with that as the assumption.

ETA: I think the "8x8" graphics you think you've extracted may actually be the quarters of the 16x16 sprites. The 8x8 and 16x16 formats have the same row stride, so if you decode one of them as the other you'll still get recognizable graphics (whereas the 32x32 will look like complete garbage)
Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: Hop on February 26, 2016, 07:50:15 pm
Many thanks for all of the info.
Quote
There are no 8x8 sprites on CPS1 ...  sprites that are made out of 16x16 cels ... I think the "8x8" graphics you think you've extracted may actually be the quarters of the 16x16 sprites ...

Yes. I was getting confused by seeing pieces of sprite data in the first two 8x8 layout visualisations in the MAME GFX viewer. I can now see that the scroll 1 data is indeed duplicated. I've managed to implement MAME GFX viewer / sf2 test mode "character" viewer and I almost understand everything that's going on.

Quote
You can see in gfxrom_bank_mapper() that if the requested code is a sprite or a SCROLL2 tile it gets multiplied by 2 (left shifted by 1, same thing), if it is a SCROLL3 tile it gets multiplied by 8, and if it is a SCROLL1 tile it is left alone. So the "unit" that the ranges in the mapper table are measured in is one duplicated 8x8 tile (which is half a 16x16 tile, or one eighth of a 32x32 tile). Meaning 16 bytes in one ROM, or 64 bytes in the whole interleaved set.

This still confuses me a little. I've re-jigged the logic in the function into two functions that make sense to me:

Code: [Select]
void CalculateMinMaxCode(GfxType gfxType, unsigned int& minCode_out, unsigned int& maxCode_out)
{
int shift = 0;
switch (gfxType)
{
case kGfxType_Sprites:
shift = 1; // 16x16 = 2 units (2x 2x8x8 tiles)
break;
case kGfxType_Scroll1:
shift = 0; // 8x8 = 1 unit
break;
case kGfxType_Scroll2:
shift = 1;  // 16x16 = 2 units
break;
case kGfxType_Scroll3:
shift = 3;  // 32x32 = 8 units
break;
default:
FATAL_ERROR( "Unhandled case" );
}

unsigned int minUnitIndex = UINT32_MAX;
unsigned int maxUnitIndex = 0;
const struct gfx_range *range = mapper_table;
ASSERT(range);
bool bFoundRange = false;
while (range->gfxType != kGfxType_Invalid)
{
if (range->gfxType == gfxType)
{
minUnitIndex = Min(minUnitIndex, range->start);
maxUnitIndex = Max(maxUnitIndex, range->end);
bFoundRange = true;
}

++range;
ASSERT(range);
}

ASSERT(bFoundRange);
minCode_out = minUnitIndex >> shift;
maxCode_out = maxUnitIndex >> shift;
}

unsigned int FindGfxAddressInBytes( GfxType type, unsigned int code )
{
int shift = 0;
switch (type)
{
case kGfxType_Sprites:
shift = 1; // 16x16 = 2 units (2x 2x8x8 tiles)
break;
case kGfxType_Scroll1:
shift = 0; // 8x8 = 1 unit
break;
case kGfxType_Scroll2:
shift = 1;  // 16x16 = 2 units
break;
case kGfxType_Scroll3:
shift = 3;  // 32x32 = 8 units
break;
default:
FATAL_ERROR( "Unhandled case" );
}

// find range containing this code
unsigned int unitIndex = code << shift;
const struct gfx_range *range = mapper_table;
ASSERT(range);
while (range->gfxType != kGfxType_Invalid)
{
if( (range->gfxType == type) && (unitIndex >= range->start) && (unitIndex <= range->end) )
{
unsigned int bankBaseInUnits = range->bank * kBankSizesInUnits;
unsigned int unitIndexInBank = unitIndex & (kBankSizesInUnits - 1);
unsigned int unitIndexInRom = (bankBaseInUnits + unitIndexInBank);
unsigned int address = unitIndexInRom * kUnitSizeBytes;
ASSERT(address < kTotalGfxRomSizeBytes);
return address;
}

++range;
}

return kInvalidOffset;
}

CalculateMinMaxCode calculates the valid range of codes for each type, which can be fed into FindGfxAddressInBytes to find the address in gfx rom (interleaved) to allow the tile to be drawn. The min codes for each set match the min values I see in gfx RAM for the 4 types, which is good (sprite codes start at zero, scroll 1 starts at $4000, scroll 2 at $2800, scroll 3 at $400) 

So it seems that original code in cps_state::gfxrom_bank_mapper is designed take a tile code in (which is different for each set) and returns an index into gfx rom memory in units of the size of that set's element.


Title: Re: Saving gfx memory with MAME Debugger - "Unable to find CPU ':gfx'
Post by: AWJ on February 27, 2016, 06:41:38 pm
Many thanks for all of the info.
Yes. I was getting confused by seeing pieces of sprite data in the first two 8x8 layout visualisations in the MAME GFX viewer. I can now see that the scroll 1 data is indeed duplicated. I've managed to implement MAME GFX viewer / sf2 test mode "character" viewer and I almost understand everything that's going on.

This still confuses me a little. I've re-jigged the logic in the function into two functions that make sense to me:

Code: [Select]
void CalculateMinMaxCode(GfxType gfxType, unsigned int& minCode_out, unsigned int& maxCode_out)
{
int shift = 0;
switch (gfxType)
{
case kGfxType_Sprites:
shift = 1; // 16x16 = 2 units (2x 2x8x8 tiles)
break;
case kGfxType_Scroll1:
shift = 0; // 8x8 = 1 unit
break;
case kGfxType_Scroll2:
shift = 1;  // 16x16 = 2 units
break;
case kGfxType_Scroll3:
shift = 3;  // 32x32 = 8 units
break;
default:
FATAL_ERROR( "Unhandled case" );
}

unsigned int minUnitIndex = UINT32_MAX;
unsigned int maxUnitIndex = 0;
const struct gfx_range *range = mapper_table;
ASSERT(range);
bool bFoundRange = false;
while (range->gfxType != kGfxType_Invalid)
{
if (range->gfxType == gfxType)
{
minUnitIndex = Min(minUnitIndex, range->start);
maxUnitIndex = Max(maxUnitIndex, range->end);
bFoundRange = true;
}

++range;
ASSERT(range);
}

ASSERT(bFoundRange);
minCode_out = minUnitIndex >> shift;
maxCode_out = maxUnitIndex >> shift;
}

unsigned int FindGfxAddressInBytes( GfxType type, unsigned int code )
{
int shift = 0;
switch (type)
{
case kGfxType_Sprites:
shift = 1; // 16x16 = 2 units (2x 2x8x8 tiles)
break;
case kGfxType_Scroll1:
shift = 0; // 8x8 = 1 unit
break;
case kGfxType_Scroll2:
shift = 1;  // 16x16 = 2 units
break;
case kGfxType_Scroll3:
shift = 3;  // 32x32 = 8 units
break;
default:
FATAL_ERROR( "Unhandled case" );
}

// find range containing this code
unsigned int unitIndex = code << shift;
const struct gfx_range *range = mapper_table;
ASSERT(range);
while (range->gfxType != kGfxType_Invalid)
{
if( (range->gfxType == type) && (unitIndex >= range->start) && (unitIndex <= range->end) )
{
unsigned int bankBaseInUnits = range->bank * kBankSizesInUnits;
unsigned int unitIndexInBank = unitIndex & (kBankSizesInUnits - 1);
unsigned int unitIndexInRom = (bankBaseInUnits + unitIndexInBank);
unsigned int address = unitIndexInRom * kUnitSizeBytes;
ASSERT(address < kTotalGfxRomSizeBytes);
return address;
}

++range;
}

return kInvalidOffset;
}

CalculateMinMaxCode calculates the valid range of codes for each type, which can be fed into FindGfxAddressInBytes to find the address in gfx rom (interleaved) to allow the tile to be drawn. The min codes for each set match the min values I see in gfx RAM for the 4 types, which is good (sprite codes start at zero, scroll 1 starts at $4000, scroll 2 at $2800, scroll 3 at $400) 

So it seems that original code in cps_state::gfxrom_bank_mapper is designed take a tile code in (which is different for each set) and returns an index into gfx rom memory in units of the size of that set's element.

Yeah--the output of the MAME gfxrom_bank_mapper() function is in MAME "gfx_element" units, which start at the beginning of the first set of ROMs and are variable in size. An output of "0x8000" when a 32x32 tile is requested means "the 0x8000'th 32x32 tile if all the ROMs are bodged together and all decoded as 32x32". An output of "0x8000" when requesting an 8x8 tile means "the 0x8000'th 8x8 tile", which is not at the same address at all.

Like I said, the MAME 2d graphics subsystem assumes that each category of graphic data (16x16 sprites, 8x8 foreground tiles, 32x32 background tiles, etc.) resides in its own completely separate set of ROMs, which is how most arcade hardware works. Take a look at e.g. 1943, which is a board that fits MAME's assumptions very closely (and as a result the amount of code in its "video" module is tiny). Rather than a single "gfx" ROM region, 1943 has one region for each layer, and character codes in emulated VRAM directly map to MAME gfx_element indexes. CPS1 has to be massaged a bit in order to fit into the MAME system, and that massaging slightly obfuscates what's going on at the real hardware level.