Welcome!
Final Burn Neo => FBN Development => Topic started by: iq_132 on May 12, 2014, 08:40:59 PM
-
So I'm going to port this driver bit by bit:
http://git.redump.net/mame/plain/src/mame/drivers/gotya.c
You can follow along in this thread :)
-
Let's start with the cpu map
static ADDRESS_MAP_START( gotya_map, AS_PROGRAM, 8, gotya_state )
AM_RANGE(0x0000, 0x3fff) AM_ROM
AM_RANGE(0x5000, 0x5fff) AM_RAM
AM_RANGE(0x6000, 0x6000) AM_READ_PORT("P1")
AM_RANGE(0x6001, 0x6001) AM_READ_PORT("P2")
AM_RANGE(0x6002, 0x6002) AM_READ_PORT("DSW")
AM_RANGE(0x6004, 0x6004) AM_WRITE(gotya_video_control_w)
AM_RANGE(0x6005, 0x6005) AM_WRITE(gotya_soundlatch_w)
AM_RANGE(0x6006, 0x6006) AM_WRITEONLY AM_SHARE("scroll")
AM_RANGE(0x6007, 0x6007) AM_WRITE(watchdog_reset_w)
AM_RANGE(0xc000, 0xc7ff) AM_RAM_WRITE(gotya_videoram_w) AM_SHARE("videoram")
AM_RANGE(0xc800, 0xcfff) AM_RAM_WRITE(gotya_colorram_w) AM_SHARE("colorram")
AM_RANGE(0xd000, 0xd3df) AM_RAM AM_SHARE("videoram2")
AM_RANGE(0xd3e0, 0xd3ff) AM_RAM AM_SHARE("spriteram")
ADDRESS_MAP_END
MCFG_CPU_ADD("maincpu", Z80,18432000/6) /* 3.072 MHz ??? */
MCFG_CPU_PROGRAM_MAP(gotya_map)
With these two pieces of information, you can see that the main cpu of this game is a z80.
for fba, this translates to:
ZetInit(0); // initialize the first z80
ZetOpen(0);
ZetMapMemory(DrvZ80ROM, 0x0000, 0x3fff, ZET_ROM); // AM_RANGE(0x0000, 0x3fff) AM_ROM
ZetMapMemory(DrvZ80RAM, 0x5000, 0x5fff, ZET_RAM); // AM_RANGE(0x5000, 0x5fff) AM_RAM
ZetMapMemory(DrvVidRAM, 0xc000, 0xc7ff, ZET_RAM); // AM_RANGE(0xc000, 0xc7ff) AM_RAM_WRITE(gotya_videoram_w) AM_SHARE("videoram")
ZetMapMemory(DrvColRAM, 0xc800, 0xcfff, ZET_RAM); // AM_RANGE(0xc800, 0xcfff) AM_RAM_WRITE(gotya_colorram_w) AM_SHARE("colorram")
ZetMapMemory(DrvVidRAM2, 0xd000, 0xd3ff, ZET_RAM); // AM_RANGE(0xd000, 0xd3df) AM_RAM AM_SHARE("videoram2")
// this is tricky, fba does pages of 256 bytes, the spriteram is only 32 (0x20) at the end of video ram, since
// we can only do 0x100 pages, just use a pointer to sprite ram
DrvSprRAM = DrvVidRAM2 + 0x3e0;
ZetSetWriteHandler(gotya_write); // use a handler to handle anything that isn't rom or ram
ZetSetReadHandler(gotya_read); // ""
ZetClose();
static void __fastcall gotya_write(UINT16 address, UINT8 data)
{
switch (address)
{
case 0x6004: // AM_RANGE(0x6004, 0x6004) AM_WRITE(gotya_video_control_w)
scroll = (scroll & 0xff) | ((data & 0x01) << 8); // mame handles this as "bit 8", just skip ahead
flipscreen = data & 0x02;
return;
case 0x6005: // AM_RANGE(0x6005, 0x6005) AM_WRITE(gotya_soundlatch_w)
gotya_soundlatch(data); // handle this later
return;
case 0x6006: // AM_RANGE(0x6006, 0x6006) AM_WRITEONLY AM_SHARE("scroll")
scroll = (scroll & 0x100) | data;
return;
case 0x6007: // AM_RANGE(0x6007, 0x6007) AM_WRITE(watchdog_reset_w)
watchdog = 0;
return;
}
}
static UINT8 __fastcall gotya_read(UINT16 address)
{
switch (address)
{
case 0x6000: // AM_RANGE(0x6000, 0x6000) AM_READ_PORT("P1")
return DrvInputs[0]; // assemble later on...
// this dip/ input assembly is abit advanced now, but will be explained a bit later, just trust with me on this...
case 0x6001: // AM_RANGE(0x6001, 0x6001) AM_READ_PORT("P2")
return (DrvDips[0] & 0x10) | (DrvInputs[1] & 0xef); // assemble later on...
case 0x6002: // AM_RANGE(0x6002, 0x6002) AM_READ_PORT("DSW")
return (DrvDips[1] & 0xf2) | (DrvInputs[2] & 0x0b);
}
return 0;
}
-
Next, we'll do some basic setup for Init, Reset, and exit.
I really just copy and paste a lot of this from other drivers (I like 4enraya lol)
This line allows us to set up our sound hardware
MCFG_SAMPLES_ADD("samples", gotya_samples_interface)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
This gets me to this point:
static void __fastcall gotya_write(UINT16 address, UINT8 data)
{
switch (address)
{
case 0x6004: // AM_RANGE(0x6004, 0x6004) AM_WRITE(gotya_video_control_w)
scroll = (scroll & 0xff) | ((data & 0x01) << 8); // mame handles this as "bit 8", just skip ahead
flipscreen = data & 0x02;
return;
case 0x6005: // AM_RANGE(0x6005, 0x6005) AM_WRITE(gotya_soundlatch_w)
gotya_soundlatch(data); // handle this later
return;
case 0x6006: // AM_RANGE(0x6006, 0x6006) AM_WRITEONLY AM_SHARE("scroll")
scroll = (scroll & 0x100) | data;
return;
case 0x6007: // AM_RANGE(0x6007, 0x6007) AM_WRITE(watchdog_reset_w)
watchdog = 0;
return;
}
}
static UINT8 __fastcall gotya_read(UINT16 address)
{
switch (address)
{
case 0x6000: // AM_RANGE(0x6000, 0x6000) AM_READ_PORT("P1")
return DrvInputs[0]; // assemble later on...
// this dip/ input assembly is abit advanced now, but will be explained a bit later, just trust with me on this...
case 0x6001: // AM_RANGE(0x6001, 0x6001) AM_READ_PORT("P2")
return (DrvDips[0] & 0x10) | (DrvInputs[1] & 0xef); // assemble later on...
case 0x6002: // AM_RANGE(0x6002, 0x6002) AM_READ_PORT("DSW")
return (DrvDips[1] & 0xf2) | (DrvInputs[2] & 0x0b);
}
return 0;
}
static INT32 DrvDoReset()
{
memset (AllRam, 0, RamEnd - AllRam); // reset all our ram
ZetOpen(0); // open our z80
ZetReset(); // reset it
ZetClose(); // close it back up
BurnSampleReset();
// set these all to 0 otherwise the game will freak out
scroll = 0;
watchdog = 0;
flipscreen = 0;
return 0;
}
// put some stuff here later...
static INT32 DrvInit()
{
AllMem = NULL;
MemIndex();
INT32 nLen = MemEnd - (UINT8 *)0;
if ((AllMem = (UINT8 *)BurnMalloc(nLen)) == NULL) return 1;
memset(AllMem, 0, nLen);
MemIndex();
{
// load roms here
// decode tiles here
}
ZetInit(0); // initialize the first z80
ZetOpen(0);
ZetMapMemory(DrvZ80ROM, 0x0000, 0x3fff, ZET_ROM); // AM_RANGE(0x0000, 0x3fff) AM_ROM
ZetMapMemory(DrvZ80RAM, 0x5000, 0x5fff, ZET_RAM); // AM_RANGE(0x5000, 0x5fff) AM_RAM
ZetMapMemory(DrvVidRAM, 0xc000, 0xc7ff, ZET_RAM); // AM_RANGE(0xc000, 0xc7ff) AM_RAM_WRITE(gotya_videoram_w) AM_SHARE("videoram")
ZetMapMemory(DrvColRAM, 0xc800, 0xcfff, ZET_RAM); // AM_RANGE(0xc800, 0xcfff) AM_RAM_WRITE(gotya_colorram_w) AM_SHARE("colorram")
ZetMapMemory(DrvVidRAM2, 0xd000, 0xd3ff, ZET_RAM); // AM_RANGE(0xd000, 0xd3df) AM_RAM AM_SHARE("videoram2")
// this is tricky, fba does pages of 256 bytes, the spriteram is only 32 (0x20) at the end of video ram, since
// we can only do 0x100 pages, just use a pointer to sprite ram
DrvSprRAM = DrvVidRAM2 + 0x3e0;
ZetSetWriteHandler(gotya_write); // use a handler to handle anything that isn't rom or ram
ZetSetReadHandler(gotya_read); // ""
ZetClose();
BurnSampleInit(0); // start our sound hardware
BurnSampleSetAllRoutesAllSamples(1.00, BURN_SND_ROUTE_BOTH); // set our volume levels for channels
GenericTilesInit(); // always, this starts the tile drawing routines
DrvDoReset(); // always start with resetting the machine
return 0;
}
static INT32 DrvExit()
{
GenericTilesExit(); // always, this exits the tile drawing routines
ZetExit(); // exit the z80
BurnSampleExit(); // exit our sound hardware
BurnFree(AllMem); // free all ram
return 0;
}
-
Now let's do some more fba-specific stuff. We need to set up our "memindex", really we're just allocating a blob of ram and setting pointers to offsets inside of it. This just makes it easier to track how much ram we're allocating.
Here's a snippet
static INT32 MemIndex()
{
UINT8 *Next; Next = AllMem;
DrvZ80ROM = Next; Next += 0x004000; // size from ZetMapMemory(DrvZ80ROM, 0x0000, 0x3fff
// gfx0 size from ROM_REGION( 0x1000, "gfx1", 0 )
// also take this GFXDECODE_ENTRY( "gfx1", 0, charlayout, 0, 16 )
// which leads us to this in charlayout 2, /* 2 bits per pixel *
// logically you can do (0x1000 * 8) / 2
// this is (tile_length * 8 [bits per byte]) / 2 (bits per pixel)
DrvGfxROM0 = Next; Next += 0x004000;
// gfx1 size is the same ROM_REGION( 0x1000, "gfx2", 0 )
DrvGfxROM1 = Next; Next += 0x004000;
DrvColPROM = Next; Next += 0x000120; // ROM_REGION( 0x0120, "proms", 0 )
// palette size can be tricky, but in this instance, it is pretty straight-forward
// MCFG_PALETTE_ADD("palette", 16*4). always make it size of UINT32 (4 bytes, this allows 24-bit colors)
DrvPalette = (UINT32*)Next; Next += 0x0040 * sizeof(UINT32);
AllRam = Next; // start of ram, use this weith ramend to allow us to clear/save every bit of ram
DrvZ80RAM = Next; Next += 0x001000; // size from ZetMapMemory(DrvZ80RAM, 0x5000, 0x5fff
DrvVidRAM = Next; Next += 0x000800; // size from ZetMapMemory(DrvVidRAM, 0xc000, 0xc7ff
DrvColRAM = Next; Next += 0x000800; // size from ZetMapMemory(DrvColRAM, 0xc800, 0xcfff
DrvVidRAM2 = Next; Next += 0x000400; // size from ZetMapMemory(DrvVidRAM2, 0xd000, 0xd3ff
RamEnd = Next;
MemEnd = Next;
return 0;
}
static INT32 DrvInit()
{
AllMem = NULL;
MemIndex();
INT32 nLen = MemEnd - (UINT8 *)0;
if ((AllMem = (UINT8 *)BurnMalloc(nLen)) == NULL) return 1;
memset(AllMem, 0, nLen);
-
Now let's add our rom loading.
static INT32 DrvInit()
{
AllMem = NULL;
MemIndex();
INT32 nLen = MemEnd - (UINT8 *)0;
if ((AllMem = (UINT8 *)BurnMalloc(nLen)) == NULL) return 1;
memset(AllMem, 0, nLen);
MemIndex();
{
if (BurnLoadRom(DrvZ80ROM + 0x0000, 0, 1)) return 1;
// ROM_LOAD( "gb-06.bin", 0x0000, 0x1000, CRC(7793985a) SHA1(23aa8bd161e700bea59b92075423cdf55e9a26c3) )
if (BurnLoadRom(DrvZ80ROM + 0x1000, 1, 1)) return 1;
// ROM_LOAD( "gb-05.bin", 0x1000, 0x1000, CRC(683d188b) SHA1(5341c62f5cf384c73be0d7a0a230bb8cebfbe709) )
if (BurnLoadRom(DrvZ80ROM + 0x2000, 2, 1)) return 1;
// ROM_LOAD( "gb-04.bin", 0x2000, 0x1000, CRC(15b72f09) SHA1(bd941722ed1310d5c8ca8a44899368cba3815f3b) )
if (BurnLoadRom(DrvZ80ROM + 0x3000, 3, 1)) return 1;
// ROM_LOAD( "gb-03.bin", 0x3000, 0x1000, CRC(f34d90ab) SHA1(bec5f6a34a273f308083a280f2b425d9c273c69b) ) /* this is the only ROM that passes the ROM test */
// ROM_REGION( 0x1000, "gfx1", 0 ) /* characters */
if (BurnLoadRom(DrvGfxROM0 + 0x0000, 4, 1)) return 1;
// ROM_LOAD( "gb-12.bin", 0x0000, 0x1000, CRC(4993d735) SHA1(9e47876238a8af3659721191a5f75c33507ed1a5) )
// ROM_REGION( 0x1000, "gfx2", 0 ) /* sprites */
if (BurnLoadRom(DrvGfxROM1 + 0x0000, 5, 1)) return 1;
// ROM_LOAD( "gb-11.bin", 0x0000, 0x1000, CRC(5d5eca1b) SHA1(d7c6b5f4d398d5e33cc411ed593d6f53a9979493) )
// ROM_REGION( 0x0120, "proms", 0 )
if (BurnLoadRom(DrvColPROM + 0x0000, 6, 1)) return 1;
// ROM_LOAD( "prom.1a", 0x0000, 0x0020, CRC(4864a5a0) SHA1(5b49f60b085fa026d4e8d4a5ad28ee7037a8ff9c) ) /* color PROM */
if (BurnLoadRom(DrvColPROM + 0x0020, 7, 1)) return 1;
// ROM_LOAD( "prom.4c", 0x0020, 0x0100, CRC(4745b5f6) SHA1(02a7f759e9bc8089cbd9213a71bbe671f9641638) ) /* lookup table */
// these aren't used so ignore them
// ROM_REGION( 0x1000, "user1", 0 ) /* no idea what these are */
// ROM_LOAD( "gb-01.bin", 0x0000, 0x0800, CRC(c31dba64) SHA1(15ae54b7d475ca3f0a3acc45cd8da2916c5fdef2) )
// ROM_LOAD( "gb-02.bin", 0x0800, 0x0800, CRC(65a7e284) SHA1(91e9c34dcf20608863ad5475dc0c4309971c8eee) )
// ROM_REGION( 0x8000, "user2", 0 ) /* HD38880 code/samples? */
// ROM_LOAD( "gb-10.bin", 0x4000, 0x1000, CRC(8101915f) SHA1(c4d21b1938ea7e0d47c48e74037f005280ac101b) )
// ROM_LOAD( "gb-09.bin", 0x5000, 0x1000, CRC(619bba76) SHA1(2a2deffe6f058fc840329fbfffbc0c70a0147c14) )
// ROM_LOAD( "gb-08.bin", 0x6000, 0x1000, CRC(82f59528) SHA1(6bfa2329eb291040bfc229c56420865253b0132a) )
// ROM_LOAD( "gb-07.bin", 0x7000, 0x1000, CRC(92a9f8bf) SHA1(9231cd86f24f1e6a585c3a919add50c1f8e42a4c) )
// decode tiles here
}
ZetInit(0);
-
Now we need to decode our graphics. We need this because most games have the pixels stored packed into a single byte if the pixels are only 2 bpp or 1bpp or whatever. Sometimes just because they're in a weird order. Putting them in a simple order now may take up more ram, but allows for easier drawing later.
We start with this struct -- this is how mame figures out how to decode tiles
static const gfx_layout charlayout =
{
8,8, /* 8*8 characters */
256, /* 256 characters */
2, /* 2 bits per pixel */
{ 0, 4 }, /* the bitplanes are packed in one byte */
{ 0, 1, 2, 3, 8*8+0, 8*8+1, 8*8+2, 8*8+3 },
{ 7*8, 6*8, 5*8, 4*8, 3*8, 2*8, 1*8, 0*8 },
16*8 /* every char takes 16 consecutive bytes */
};
static const gfx_layout spritelayout =
{
16,16, /* 16*16 characters */
64, /* 64 characters */
2, /* 2 bits per pixel */
{ 0, 4 }, /* the bitplanes are packed in one byte */
{ 0, 1, 2, 3, 24*8+0, 24*8+1, 24*8+2, 24*8+3,
16*8+0, 16*8+1, 16*8+2, 16*8+3, 8*8+0, 8*8+1, 8*8+2, 8*8+3 },
{ 39*8, 38*8, 37*8, 36*8, 35*8, 34*8, 33*8, 32*8,
7*8, 6*8, 5*8, 4*8, 3*8, 2*8, 1*8, 0*8 },
64*8 /* every char takes 64 consecutive bytes */
};
static GFXDECODE_START( gotya )
GFXDECODE_ENTRY( "gfx1", 0, charlayout, 0, 16 )
GFXDECODE_ENTRY( "gfx2", 0, spritelayout, 0, 16 )
GFXDECODE_END
This ends up being this for fba
static INT32 DrvGfxDecode()
{
// set up charlayout
INT32 Plane0[4] = { 0, 4 }; // { 0, 4 }, /* the bitplanes are packed in one byte */
INT32 XOffs0[8] = { 0, 1, 2, 3, 8*8+0, 8*8+1, 8*8+2, 8*8+3 };
INT32 YOffs0[8] = { 7*8, 6*8, 5*8, 4*8, 3*8, 2*8, 1*8, 0*8 };
// set up spritelayout
INT32 Plane1[4] = { 0, 4 }; // { 0, 4 }, /* the bitplanes are packed in one byte */
INT32 XOffs1[16] = { 0, 1, 2, 3, 24*8+0, 24*8+1, 24*8+2, 24*8+3, 16*8+0, 16*8+1, 16*8+2, 16*8+3, 8*8+0, 8*8+1, 8*8+2, 8*8+3 };
INT32 YOffs1[16] = { 39*8, 38*8, 37*8, 36*8, 35*8, 34*8, 33*8, 32*8,7*8, 6*8, 5*8, 4*8, 3*8, 2*8, 1*8, 0*8 };
// make sure to allocate a tmp that is large enough for both graphics tile sizes
UINT8 *tmp = (UINT8*)BurnMalloc(0x1000);
if (tmp == NULL) {
return 1;
}
memcpy (tmp, DrvGfxROM0, 0x1000);
GfxDecode(((0x1000 * 8) / 2) / (8 * 8), 2, 8, 8, Plane0, XOffs0, YOffs0, 16*8 /* from charlayout*/, tmp, DrvGfxROM0);
memcpy (tmp, DrvGfxROM1, 0x1000);
GfxDecode(((0x1000 * 8) / 2) / (16 * 16), 2, 16, 16, Plane1, XOffs1, YOffs1, 64*8 /* from spritelayout*/, tmp, DrvGfxROM1);
BurnFree(tmp);
return 0;
}
-
Next, let's set up DrvFrame. This part can be a bit confusing/tricky. In this routine, we essentially emulate 1 frame of the game running.
In this, we have to assemble inputs, run the cpu, run the sound, ensure the colors are set up/current, and draw the frame.
for this part, we need this from MAME
MCFG_CPU_ADD("maincpu", Z80,18432000/6) /* 3.072 MHz ??? */
MCFG_CPU_PROGRAM_MAP(gotya_map)
MCFG_CPU_VBLANK_INT_DRIVER("screen", gotya_state, irq0_line_hold)
fba is much more complicated, but allows for a lot more control/bugs
static INT32 DrvFrame()
{
watchdog++;
if (watchdog >= 180) { // arbitrary count, the watchdg is triggered so that the game resets if it glitches, hopefully fixing the problem
DrvDoReset(); // reset the game, clear watchdog in reset
}
if (DrvReset) {
DrvDoReset(); // is the reset button pressed? reset!
}
{
// assemble inputs, just need to assemble the joystick/button parts, fba does the dips for us
// each button/joy input is one bit of one of these two bytes, the bits are 1 by default (IPT_ACTIVE_LOW) or 0 (IPT_ACTIVE_HIGH)
// for this game, they are all IPT_ACTIVE_LOW, so we start with 0xff for each input (confused?)
DrvInputs[0] = 0xff;
DrvInputs[1] = 0xff;
DrvInputs[2] = 0xff;
for (INT32 i = 0; i < 8; i++) {
DrvInputs[0] ^= (DrvJoy1[i] & 1) << i;
DrvInputs[1] ^= (DrvJoy2[i] & 1) << i;
DrvInputs[2] ^= (DrvJoy3[i] & 1) << i;
}
INT32 nCyclesTotal = (18432000/6) / 60; // MCFG_CPU_ADD("maincpu", Z80,18432000/6) / 60 (60 frames per second)
ZetOpen(0); // open the cpu
ZetRun(nCyclesTotal); // run this frame's worth of cycles (how much the cpu can process in the alotted time)
ZetSetIRQLine(0, ZET_IRQSTATUS_AUTO); // trigger and irq (these are weird!) they interrupt whatever the cpu is doing and makes
// it do something specifically set up in the cpu's program.
// this is the fba equiv to this MCFG_CPU_VBLANK_INT_DRIVER("screen", gotya_state, irq0_line_hold)
// call the irq at "vertical blank" to tell the cpu that it has completed a frame of drawing,
// start the next one!
ZetClose(); // close the cpu
if (pBurnSoundOut) { // make sure we're outputting sound
BurnSampleRender(pBurnSoundOut, nBurnSoundLen); // actually output sound
}
if (pBurnDraw) { // make sure we're actually allowing drawing
DrvDraw(); // call the drawing routine
}
return 0;
}
-
Next, let's set up the palette.
Mame does this with this routine in this particular game, this is a static palette, some do ram writes, others do weird things.
PALETTE_INIT_MEMBER(gotya_state, gotya)
{
const UINT8 *color_prom = memregion("proms")->base();
static const int resistances_rg[3] = { 1000, 470, 220 };
static const int resistances_b [2] = { 470, 220 };
double rweights[3], gweights[3], bweights[2];
int i;
/* compute the color output resistor weights */
compute_resistor_weights(0, 255, -1.0,
3, &resistances_rg[0], rweights, 0, 0,
3, &resistances_rg[0], gweights, 0, 0,
2, &resistances_b[0], bweights, 0, 0);
/* create a lookup table for the palette */
for (i = 0; i < 0x20; i++)
{
int bit0, bit1, bit2;
int r, g, b;
/* red component */
bit0 = (color_prom[i] >> 0) & 0x01;
bit1 = (color_prom[i] >> 1) & 0x01;
bit2 = (color_prom[i] >> 2) & 0x01;
r = combine_3_weights(rweights, bit0, bit1, bit2);
/* green component */
bit0 = (color_prom[i] >> 3) & 0x01;
bit1 = (color_prom[i] >> 4) & 0x01;
bit2 = (color_prom[i] >> 5) & 0x01;
g = combine_3_weights(gweights, bit0, bit1, bit2);
/* blue component */
bit0 = (color_prom[i] >> 6) & 0x01;
bit1 = (color_prom[i] >> 7) & 0x01;
b = combine_2_weights(bweights, bit0, bit1);
palette.set_indirect_color(i, rgb_t(r, g, b));
}
/* color_prom now points to the beginning of the lookup table */
color_prom += 32;
for (i = 0; i < 0x40; i++)
{
UINT8 ctabentry = color_prom[i] & 0x07;
palette.set_pen_indirect(i, ctabentry);
}
}
How these are set up is pretty interesting, MAME documents the electronics on how each color is set up and each component.
so this:
static const int resistances_rg[3] = { 1000, 470, 220 };
static const int resistances_b [2] = { 470, 220 };
double rweights[3], gweights[3], bweights[2];
compute_resistor_weights(0, 255, -1.0,
3, &resistances_rg[0], rweights, 0, 0,
3, &resistances_rg[0], gweights, 0, 0,
2, &resistances_b[0], bweights, 0, 0);
resistances_rg[3] = { 1000, 470, 220 };
this means that to calculate the red or green components, these take 3 sub-components each.
keep in mind that we're setting this up as a 24-bit color, red is 0-255 (0xff), same for green and blue
so we can take 1000, 470, 220 and get the sum -> 1690
next we take each sub component and calculate how much of 255 it takes up
(255 * 1000) / 1690 -> 150.89
(255 * 470) / 1690 -> 70.91
(255 * 220) / 1690 -> 33.19
now let's round these up/down
151, 71, 33 -> 255!
this doesn't give mame-perfect results, but they're close enough for our purposes.
now let's do the blue sub-components
static const int resistances_b [2] = { 470, 220 };
(255 * 470) / 690 -> 173.69
(255 * 220) / 690 -> 81.30
174, 81 -> 255!
Now let's go further, we've got each sub component set up, now let's check out how the color proms factor into this.
the first prom 32 bytes long actually selects which sub components we use for each color componet
/* red component */
bit0 = (color_prom[i] >> 0) & 0x01;
bit1 = (color_prom[i] >> 1) & 0x01;
bit2 = (color_prom[i] >> 2) & 0x01;
r = combine_3_weights(rweights, bit0, bit1, bit2);
/* green component */
bit0 = (color_prom[i] >> 3) & 0x01;
bit1 = (color_prom[i] >> 4) & 0x01;
bit2 = (color_prom[i] >> 5) & 0x01;
g = combine_3_weights(gweights, bit0, bit1, bit2);
/* blue component */
bit0 = (color_prom[i] >> 6) & 0x01;
bit1 = (color_prom[i] >> 7) & 0x01;
b = combine_2_weights(bweights, bit0, bit1);
remember 151, 71, 33 from above?
these factor in on these lines
r = combine_3_weights(rweights, bit0, bit1, bit2);
g = combine_3_weights(gweights, bit0, bit1, bit2);
r = (bit0 * 151) + (bit1 * 71) + (bit2 * 33);
g = (bit0 * 151) + (bit1 * 71) + (bit2 * 33);
now for our blue component
174, 81
b = combine_2_weights(bweights, bit0, bit1);
b = (bit 0 * 174) + (bit1 * 81)
This allows us to set up a color table, we have another color prom that selects these colors (color index/lookup)
static void DrvPaletteInit()
{
UINT32 pal[0x20];
UINT8 *color_prom = DrvColPROM;
for (INT32 i = 0; i < 0x20; i++)
{
int bit0, bit1, bit2;
int r, g, b;
/* red component */
bit0 = (color_prom[i] >> 0) & 0x01;
bit1 = (color_prom[i] >> 1) & 0x01;
bit2 = (color_prom[i] >> 2) & 0x01;
r = (bit0 * 151) + (bit1 * 71) + (bit2 * 33);
/* green component */
bit0 = (color_prom[i] >> 3) & 0x01;
bit1 = (color_prom[i] >> 4) & 0x01;
bit2 = (color_prom[i] >> 5) & 0x01;
g = (bit0 * 151) + (bit1 * 71) + (bit2 * 33);
/* blue component */
bit0 = (color_prom[i] >> 6) & 0x01;
bit1 = (color_prom[i] >> 7) & 0x01;
b = (bit0 * 174) + (bit1 * 81);
// palette.set_indirect_color(i, rgb_t(r, g, b));
pal[i] = BurnHighCol(r, g, b, 0); // this function sets the colors to be whatever depth we're using, be it 24bpp, 16bpp, etc
}
/* color_prom now points to the beginning of the lookup table */
color_prom += 32;
for (INT32 i = 0; i < 0x40; i++)
{
UINT8 ctabentry = color_prom[i] & 0x07;
// palette.set_pen_indirect(i, ctabentry);
DrvPalette[i] = pal[ctabentry]; // the color index prom selects from the color table that we've set up above
}
}
-
Next, let's start the drawing routines. This is for our background tiles.
TILE_GET_INFO_MEMBER(gotya_state::get_bg_tile_info)
{
int code = m_videoram[tile_index];
int color = m_colorram[tile_index] & 0x0f;
SET_TILE_INFO_MEMBER(0, code, color, 0);
}
TILEMAP_MAPPER_MEMBER(gotya_state::tilemap_scan_rows_thehand)
{
/* logical (col,row) -> memory offset */
row = 31 - row;
col = 63 - col;
return ((row) * (num_cols >> 1)) + (col & 31) + ((col >> 5) * 0x400);
}
void gotya_state::video_start()
{
m_bg_tilemap = &machine().tilemap().create(m_gfxdecode, tilemap_get_info_delegate(FUNC(gotya_state::get_bg_tile_info),this), tilemap_mapper_delegate(FUNC(gotya_state::tilemap_scan_rows_thehand),this), 8, 8, 64, 32);
}
static void draw_layer()
{
// first let's set up scrolling
// m_bg_tilemap->set_scrollx(0, -(*m_scroll + (m_scroll_bit_8 * 256)) - 2 * 8);
// the variable we're using is "scroll", the (m_scroll + (m_scroll_bit_8 * 256)) part is already handled
INT32 scrollx = (-(scroll) - 2 * 8) & 0x1ff;
// we use & 0x3ff because this wraps the screen around when scrolling since it is a maximum of
// 64 x 8 pixels wide (see below)
// get tilemap width / height from here
// m_bg_tilemap = &machine().tilemap().create(m_gfxdecode, tilemap_get_info_delegate(FUNC(gotya_state::get_bg_tile_info),this), tilemap_mapper_delegate(FUNC(gotya_state::tilemap_scan_rows_thehand),this), 8, 8, 64, 32);
for (INT32 offs = 0; offs < 64 * 32; offs++)
{
INT32 sy = (offs / 64) * 8;
INT32 sx = (offs & 0x3f) * 8;
sx -= scrollx;
if (sx < -7) sx += 0x200; // if we're a full tile off screen, wrap back around
// get these from tilemap_scan_rows_thehand
INT32 row = (offs / 64); // sy
INT32 col = (offs & 0x3f); // sx
row = 31 - row;
col = 63 - col;
INT32 ofst = ((row) * (64 >> 1)) + (col & 31) + ((col >> 5) * 0x400);
// get these from get_bg_tile_info
INT32 code = DrvVidRAM[ofst];
INT32 color = DrvColRAM[ofst] & 0x0f;
// generic tile drawing function (tiles_generic.h)
// this is drawing 8x8 tiles, and this has the possibility of drawing partially off-screen. we will have
// to clip the tile or we will get a crash!
// pTransDraw is our temporary drawing surface
// code is the offset to the tile we want to draw -> (code * 8 * 8)
// sx & sy are offsets on the screen where we're drawing this
// color is color offset inside the palette that we're using
// this is necessary since a 2 bpp tile can only use 4 colors!
Render8x8Tile_Clip(pTransDraw, code, sx, sy, color, 2, 0, DrvGfxROM0);
}
}
-
Next, let's handle this routine.
void gotya_state::draw_status_row( bitmap_ind16 &bitmap, const rectangle &cliprect, int sx, int col )
{
int row;
if (flip_screen())
{
sx = 35 - sx;
}
for (row = 29; row >= 0; row--)
{
int sy;
if (flip_screen())
sy = row;
else
sy = 31 - row;
m_gfxdecode->gfx(0)->opaque(bitmap,cliprect,
m_videoram2[row * 32 + col],
m_videoram2[row * 32 + col + 0x10] & 0x0f,
flip_screen_x(), flip_screen_y(),
8 * sx, 8 * sy);
}
}
static void draw_status_row(INT32 sx, INT32 col)
{
INT32 row;
if (flipscreen)
{
sx = 35 - sx;
}
for (row = 29; row >= 0; row--)
{
INT32 sy;
if (flipscreen)
sy = row;
else
sy = 32 - row;
INT32 code = DrvVidRAM2[row * 32 + col];
INT32 color = DrvVidRAM2[row * 32 + col + 0x10] & 0x0f;
if (flipscreen)
Render8x8Tile_FlipXY_Clip(pTransDraw, code, 8 * sx, 8 * sy, color, 2, 0, DrvGfxROM0);
else
Render8x8Tile_Clip(pTransDraw, code, 8 * sx, 8 * sy, color, 2, 0, DrvGfxROM0);
}
}
Pretty straight-forward, right?
-
Now let's handle the sprites!
void gotya_state::draw_sprites( bitmap_ind16 &bitmap, const rectangle &cliprect )
{
UINT8 *spriteram = m_spriteram;
int offs;
for (offs = 2; offs < 0x0e; offs += 2)
{
int code = spriteram[offs + 0x01] >> 2;
int color = spriteram[offs + 0x11] & 0x0f;
int sx = 256 - spriteram[offs + 0x10] + (spriteram[offs + 0x01] & 0x01) * 256;
int sy = spriteram[offs + 0x00];
if (flip_screen())
sy = 240 - sy;
m_gfxdecode->gfx(1)->transpen(bitmap,cliprect,
code, color,
flip_screen_x(), flip_screen_y(),
sx, sy, 0);
}
}
static void draw_sprites()
{
UINT8 *spriteram = DrvSprRAM;
int offs;
for (offs = 2; offs < 0x0e; offs += 2)
{
int code = spriteram[offs + 0x01] >> 2;
int color = spriteram[offs + 0x11] & 0x0f;
int sx = 256 - spriteram[offs + 0x10] + (spriteram[offs + 0x01] & 0x01) * 256;
int sy = spriteram[offs + 0x00];
if (flipscreen)
sy = 240 - sy;
// the sprite tiles are 16x16 and if the pixel is 0 (pixel can be 0, 1, 2, 3 with a 2bpp pixel), it is clear!
// this actually only allows 3 colors per pixel!
if (flipscreen)
Render16x16Tile_Mask_Clip(pTransDraw, code, sx, sy, color, 2, 0, 0, DrvGfxROM1);
else
Render16x16Tile_Mask_FlipXY_Clip(pTransDraw, code, sx, sy, color, 2, 0, 0, DrvGfxROM1);
}
}
-
Now we've got the actuall screen update function left as far as video.
Let's integrate the draw_status function too, this will just simplify things
void gotya_state::draw_status( bitmap_ind16 &bitmap, const rectangle &cliprect )
{
draw_status_row(bitmap, cliprect, 0, 1);
draw_status_row(bitmap, cliprect, 1, 0);
draw_status_row(bitmap, cliprect, 2, 2); /* these two are blank, but I dont' know if the data comes */
draw_status_row(bitmap, cliprect, 33, 13); /* from RAM or 'hardcoded' into the hardware. Likely the latter */
draw_status_row(bitmap, cliprect, 35, 14);
draw_status_row(bitmap, cliprect, 34, 15);
}
UINT32 gotya_state::screen_update_gotya(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
m_bg_tilemap->set_scrollx(0, -(*m_scroll + (m_scroll_bit_8 * 256)) - 2 * 8);
m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
draw_sprites(bitmap, cliprect);
draw_status(bitmap, cliprect);
return 0;
}
l
static INT32 DrvDraw()
{
if (DrvRecalc) {
DrvPaletteInit(); // called if the color depth is changed!
DrvRecalc = 0;
}
draw_layer();
draw_sprites();
draw_status_row(0, 1);
draw_status_row(1, 0);
draw_status_row(2, 2); /* these two are blank, but I dont' know if the data comes */
draw_status_row(33, 13); /* from RAM or 'hardcoded' into the hardware. Likely the latter */
draw_status_row(35, 14);
draw_status_row(34, 15);
// this takes pTransDraw and adds the palette colors to it, and puts it on the screen!
BurnTransferCopy(DrvPalette);
return 0;
}
-
Let's set up our inputs (I wrote a tool to do this, I find it tedious)
static INPUT_PORTS_START( gotya )
PORT_START("P1")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
PORT_SERVICE( 0x10, IP_ACTIVE_LOW )
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 Paper") PORT_PLAYER(1)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P1 Scissors") PORT_PLAYER(1)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Rock") PORT_PLAYER(1)
PORT_START("P2")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
PORT_DIPNAME( 0x10, 0x10, "Sound Test" )
PORT_DIPSETTING( 0x10, DEF_STR( Off ) )
PORT_DIPSETTING( 0x00, DEF_STR( On ) )
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P2 Paper") PORT_PLAYER(2)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("P2 Scissors") PORT_PLAYER(2)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P2 Rock") PORT_PLAYER(2)
PORT_START("DSW")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 )
PORT_DIPNAME( 0x02, 0x00, DEF_STR( Cabinet ) )
PORT_DIPSETTING( 0x00, DEF_STR( Upright ) )
PORT_DIPSETTING( 0x02, DEF_STR( Cocktail ) )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START1 )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 )
PORT_DIPNAME( 0x10, 0x00, DEF_STR( Difficulty ) )
PORT_DIPSETTING( 0x00, DEF_STR( Easy ) )
PORT_DIPSETTING( 0x10, DEF_STR( Hard ) )
PORT_DIPNAME( 0x20, 0x20, DEF_STR( Bonus_Life ) )
PORT_DIPSETTING( 0x00, DEF_STR( None ) )
PORT_DIPSETTING( 0x20, "15000" )
PORT_DIPNAME( 0x40, 0x00, DEF_STR( Lives ) )
PORT_DIPSETTING( 0x00, "3" )
PORT_DIPSETTING( 0x40, "5" )
PORT_DIPNAME( 0x80, 0x80, "Game Type" ) /* Manual Says: Before main switch on: Test Pattern */
PORT_DIPSETTING( 0x80, DEF_STR( Normal ) ) /* After main switch on: Endless game */
PORT_DIPSETTING( 0x00, "Endless" )
INPUT_PORTS_END
Please note the numbers to the far right! these are important for our dip switches!
static struct BurnInputInfo gotyaInputList[] = {
{"P1 Coin", BIT_DIGITAL, DrvJoy3 + 0, "p1 coin" }, // 0
{"P1 Start", BIT_DIGITAL, DrvJoy3 + 2, "p1 start" }, // 1
// PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
// BIT = (1 << 0) [0x01] -> DrvJoy + 0
{"P1 Left", BIT_DIGITAL, DrvJoy1 + 0, "p1 left" }, // 2
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
// BIT = (1 << 1) [0x02] -> DrvJoy + 1
{"P1 Right", BIT_DIGITAL, DrvJoy1 + 1, "p1 right" }, // 3
{"P1 Down", BIT_DIGITAL, DrvJoy1 + 2, "p1 down" }, // 4
{"P1 Up", BIT_DIGITAL, DrvJoy1 + 3, "p1 up" }, // 5
{"P1 Paper", BIT_DIGITAL, DrvJoy1 + 5, "p1 fire 1" }, // 6
{"P1 Scissors", BIT_DIGITAL, DrvJoy1 + 6, "p1 fire 2" }, // 7
{"P1 Rock", BIT_DIGITAL, DrvJoy1 + 7, "p1 fire 3" }, // 8
{"P2 Start", BIT_DIGITAL, DrvJoy3 + 3, "p2 start" }, // 9
{"P2 Left", BIT_DIGITAL, DrvJoy2 + 0, "p2 left" }, // 0x0a
{"P2 Right", BIT_DIGITAL, DrvJoy2 + 1, "p2 right" }, // 0x0b
{"P2 Down", BIT_DIGITAL, DrvJoy2 + 2, "p2 down" }, // 0x0c
{"P2 Up", BIT_DIGITAL, DrvJoy2 + 3, "p2 up" }, // 0x0d
{"P2 Paper", BIT_DIGITAL, DrvJoy2 + 5, "p2 fire 1" }, // 0x0e
{"P2 Scissors", BIT_DIGITAL, DrvJoy2 + 6, "p2 fire 2" }, // 0x0f
{"P2 Rock", BIT_DIGITAL, DrvJoy2 + 7, "p2 fire 3" }, // 0x10
{"Reset", BIT_DIGITAL, &DrvReset, "reset" }, // 0x11
{"Service", BIT_DIGITAL, DrvJoy1 + 4, "service" }, // 0x12
{"Dip A", BIT_DIPSWITCH, DrvDips + 0, "dip" }, // 0x13
{"Dip B", BIT_DIPSWITCH, DrvDips + 1, "dip" }, // 0x14
};
STDINPUTINFO(gotya)
static struct BurnDIPInfo gotyaDIPList[]=
{
// defaults!
// #0 is the offset in the input struct to where we're putting the dip info
// #1 and #2 are always the same
// #3 is the default for this dip!
// ignore the NULL
{0x13, 0xff, 0xff, 0x10, NULL },
{0x14, 0xff, 0xff, 0x6b, NULL },
// actual dips
// #0 is always 0
// #1 is always fe
// #2 is always 0
// #3 is how many dips for this selection
// #4 is the description of this switch
{0 , 0xfe, 0 , 2, "Sound Test" },
// #0 is the offset to the input struct where this dip setting is getting stored (DrvDips + 0 for this one)
// #1 is always 1
// #2 is the bit mask
// #3 is the actual value we're using
// #4 is the description
{0x13, 0x01, 0x10, 0x10, "Off" },
{0x13, 0x01, 0x10, 0x00, "On" },
PORT_START("DSW")
PORT_DIPNAME( 0x02, 0x00, DEF_STR( Cabinet ) )
PORT_DIPSETTING( 0x00, DEF_STR( Upright ) )
PORT_DIPSETTING( 0x02, DEF_STR( Cocktail ) )
PORT_DIPNAME( 0x10, 0x00, DEF_STR( Difficulty ) )
PORT_DIPSETTING( 0x00, DEF_STR( Easy ) )
PORT_DIPSETTING( 0x10, DEF_STR( Hard ) )
PORT_DIPNAME( 0x20, 0x20, DEF_STR( Bonus_Life ) )
PORT_DIPSETTING( 0x00, DEF_STR( None ) )
PORT_DIPSETTING( 0x20, "15000" )
PORT_DIPNAME( 0x40, 0x00, DEF_STR( Lives ) )
PORT_DIPSETTING( 0x00, "3" )
PORT_DIPSETTING( 0x40, "5" )
PORT_DIPNAME( 0x80, 0x80, "Game Type" ) /* Manual Says: Before main switch on: Test Pattern */
PORT_DIPSETTING( 0x80, DEF_STR( Normal ) ) /* After main switch on: Endless game */
PORT_DIPSETTING( 0x00, "Endless" )
// we're not setting up flipscreen, so disable this...
// {0 , 0xfe, 0 , 2, "Cabine" },
// {0x14, 0x01, 0x02, 0x00, "Upright" },
// {0x14, 0x01, 0x02, 0x02, "Cocktail" },
{0 , 0xfe, 0 , 2, "Difficulty" },
{0x0d, 0x01, 0x10, 0x00, "Easy" },
{0x0d, 0x01, 0x10, 0x10, "Hard" },
{0 , 0xfe, 0 , 2, "Bonus Life" },
{0x0d, 0x01, 0x20, 0x00, "None" },
{0x0d, 0x01, 0x20, 0x20, "15000" },
{0 , 0xfe, 0 , 2, "Lifes" },
{0x0d, 0x01, 0x40, 0x00, "3" },
{0x0d, 0x01, 0x40, 0x40, "5" },
{0 , 0xfe, 0 , 2, "Game Type" },
{0x0d, 0x01, 0x80, 0x80, "Endless" },
{0x0d, 0x01, 0x80, 0x00, "Normal" },
};
STDDIPINFO(gotya)
-
We're almost there! Now let's add all of our variables at the top of our file we've been working in.
static UINT8 *AllMem;
static UINT8 *MemEnd;
static UINT8 *AllRam;
static UINT8 *RamEnd;
static UINT8 *DrvZ80ROM;
static UINT8 *DrvGfxROM0;
static UINT8 *DrvGfxROM1;
static UINT8 *DrvColPROM;
static UINT8 *DrvZ80RAM;
static UINT8 *DrvVidRAM;
static UINT8 *DrvColRAM;
static UINT8 *DrvVidRAM2;
static UINT8 *DrvSprRAM;
static UINT32 *DrvPalette;
static UINT8 DrvRecalc;
static INT32 scroll;
static INT32 flipscreen;
static INT32 watchdog;
static UINT8 DrvJoy1[8];
static UINT8 DrvJoy2[8];
static UINT8 DrvJoy3[8];
static UINT8 DrvDips[2];
static UINT8 DrvInputs[3];
static UINT8 DrvReset;
above that, let's do our includes...
We always do this in fba
#include "tiles_generic.h"
this game uses a z80
#include "z80_intf.h"
this game uses samples
#include "samples.h"
-
Now let's do our rom struct
ROM_START( thehand )
ROM_REGION( 0x10000, "maincpu", 0 )
ROM_LOAD( "hand6.bin", 0x0000, 0x1000, CRC(a33b806c) SHA1(1e552af5362e7b003f55e78bb59589e1db55557c) )
ROM_LOAD( "hand5.bin", 0x1000, 0x1000, CRC(89bcde82) SHA1(d074bb6a1975160eb533d5fd9289170a68209046) )
ROM_LOAD( "hand4.bin", 0x2000, 0x1000, CRC(c6844a83) SHA1(84e220dce3f5ddee9dd0377f3bebdd4027fc9108) )
ROM_LOAD( "gb-03.bin", 0x3000, 0x1000, CRC(f34d90ab) SHA1(bec5f6a34a273f308083a280f2b425d9c273c69b) )
ROM_REGION( 0x1000, "gfx1", 0 ) /* characters */
ROM_LOAD( "hand12.bin", 0x0000, 0x1000, CRC(95773b46) SHA1(db8d7ace4eafd4c72edfeff6003ca6e96e0239b5) )
ROM_REGION( 0x1000, "gfx2", 0 ) /* sprites */
ROM_LOAD( "gb-11.bin", 0x0000, 0x1000, CRC(5d5eca1b) SHA1(d7c6b5f4d398d5e33cc411ed593d6f53a9979493) )
ROM_REGION( 0x0120, "proms", 0 )
ROM_LOAD( "prom.1a", 0x0000, 0x0020, CRC(4864a5a0) SHA1(5b49f60b085fa026d4e8d4a5ad28ee7037a8ff9c) ) /* color PROM */
ROM_LOAD( "prom.4c", 0x0020, 0x0100, CRC(4745b5f6) SHA1(02a7f759e9bc8089cbd9213a71bbe671f9641638) ) /* lookup table */
ROM_REGION( 0x1000, "user1", 0 ) /* no idea what these are */
ROM_LOAD( "hand1.bin", 0x0000, 0x0800, CRC(ccc537e0) SHA1(471fd49225aa14b91d085178e1b58b6c4ae76481) )
ROM_LOAD( "gb-02.bin", 0x0800, 0x0800, CRC(65a7e284) SHA1(91e9c34dcf20608863ad5475dc0c4309971c8eee) )
ROM_REGION( 0x8000, "user2", 0 ) /* HD38880 code/samples? */
ROM_LOAD( "gb-10.bin", 0x4000, 0x1000, CRC(8101915f) SHA1(c4d21b1938ea7e0d47c48e74037f005280ac101b) )
ROM_LOAD( "gb-09.bin", 0x5000, 0x1000, CRC(619bba76) SHA1(2a2deffe6f058fc840329fbfffbc0c70a0147c14) )
ROM_LOAD( "gb-08.bin", 0x6000, 0x1000, CRC(82f59528) SHA1(6bfa2329eb291040bfc229c56420865253b0132a) )
ROM_LOAD( "hand7.bin", 0x7000, 0x1000, CRC(fbf1c5de) SHA1(dd3181a8da1972e3c997678bb868256a10f33d04) )
ROM_END
GAME( 1981, thehand, 0, gotya, gotya, driver_device, 0, ROT270, "T.I.C.", "The Hand", GAME_IMPERFECT_SOUND | GAME_SUPPORTS_SAVE )
in fba we pull a lot of information to put together our struct
// The Hand
static struct BurnRomInfo thehandRomDesc[] = {
{ "hand6.bin", 0x1000, 0xa33b806c, 0 | BRF_PRG | BRF_ESS }, // 0 Z80 Code
{ "hand5.bin", 0x1000, 0x89bcde82, 0 | BRF_PRG | BRF_ESS }, // 1
{ "hand4.bin", 0x1000, 0xc6844a83, 0 | BRF_PRG | BRF_ESS }, // 2
{ "gb-03.bin", 0x1000, 0xf34d90ab, 0 | BRF_PRG | BRF_ESS }, // 3
{ "hand12.bin", 0x1000, 0x95773b46, 1 | BRF_GRA }, // 4 Background Tiles
{ "gb-11.bin", 0x1000, 0x5d5eca1b, 2 | BRF_GRA }, // 5 Sprites
{ "prom.1a", 0x0020, 0x4864a5a0, 3 | BRF_GRA }, // 6 Colors
{ "prom.4c", 0x0100, 0x4745b5f6, 3 | BRF_GRA }, // 7 Color Lookup table
{ "hand1.bin", 0x0800, 0xccc537e0, 4 | BRF_OPT }, // 8 ??
{ "gb-02.bin", 0x0800, 0x65a7e284, 4 | BRF_OPT }, // 9
{ "gb-10.bin", 0x1000, 0x8101915f, 4 | BRF_OPT }, // 10 HD38880 code
{ "gb-09.bin", 0x1000, 0x619bba76, 4 | BRF_OPT }, // 11
{ "gb-08.bin", 0x1000, 0x82f59528, 4 | BRF_OPT }, // 12
{ "hand7.bin", 0x1000, 0xfbf1c5de, 4 | BRF_OPT }, // 13
};
STD_ROM_PICK(thehand)
STD_ROM_FN(thehand)
struct BurnDriver BurnDrvThehand = {
"thehand", NULL, NULL, "thehand", "1981",
"The Hand\0", NULL, "T.I.C.", "MISCELLANEOUS",
NULL, NULL, NULL, NULL,
BDF_GAME_WORKING, 2, HARDWARE_MISC_PRE90S, GBF_PUZZLE, 0,
NULL, thehandRomInfo, thehandRomName, thehandSampleInfo, thehandSampleName, gotyaInputInfo, gotyaDIPInfo,
DrvInit, DrvExit, DrvFrame, DrvDraw, NULL, &DrvRecalc, 64,
288, 224, 4, 3 // MCFG_SCREEN_VISIBLE_AREA(0, 36*8-1, 2*8, 30*8-1)
};
-
This game uses samples. let's set those up too!
static const char *const sample_names[] =
{ // Address triggered at
"*thehand",
"01", /* game start tune */ // 075f
"02", /* coin in */ // 0074
"03", /* eat dot */ // 0e45
"05", /* eat dollar sign */ // 0e45
"06", /* door open */ // 19e1
"07", /* door close */ // 1965
"08", /* theme song */ // 0821
//"09" // 1569
/* one of these two is played after eating the last dot */
"0a", /* piccolo */ // 17af
"0b", /* tune */ // 17af
//"0f" // 08ee
"10", /* 'We're even. Bye Bye!' */ // 162a
"11", /* 'You got me!' */ // 1657
"12", /* 'You have lost out' */ // 085e
"13", /* 'Rock' */ // 14de
"14", /* 'Scissors' */ // 14f3
"15", /* 'Paper' */ // 1508
/* one of these is played when going by the girl between levels */
"16", /* 'Very good!' */ // 194a
"17", /* 'Wonderful!' */ // 194a
"18", /* 'Come on!' */ // 194a
"19", /* 'I love you!' */ // 194a
"1a", /* 'See you again!' */ // 194a
0
};
Just like the rom struct
static struct BurnSampleInfo thehandSampleDesc[] = {
{ "01", SAMPLE_NOLOOP }, /* game start tune */ // 075f
{ "02", SAMPLE_NOLOOP }, /* coin in */ // 0074
{ "03", SAMPLE_NOLOOP }, /* eat dot */ // 0e45
{ "05", SAMPLE_NOLOOP }, /* eat dollar sign */ // 0e45
{ "06", SAMPLE_NOLOOP }, /* door open */ // 19e1
{ "07", SAMPLE_NOLOOP }, /* door close */ // 1965
{ "08", SAMPLE_NOLOOP }, /* theme song */ // 0821
//"09" // 1569
/* one of these two is played after eating the last dot */
{ "0a", SAMPLE_NOLOOP }, /* piccolo */ // 17af
{ "0b", SAMPLE_NOLOOP }, /* tune */ // 17af
//"0f" // 08ee
{ "10", SAMPLE_NOLOOP }, /* 'We're even. Bye Bye!' */ // 162a
{ "11", SAMPLE_NOLOOP }, /* 'You got me!' */ // 1657
{ "12", SAMPLE_NOLOOP }, /* 'You have lost out' */ // 085e
{ "13", SAMPLE_NOLOOP }, /* 'Rock' */ // 14de
{ "14", SAMPLE_NOLOOP }, /* 'Scissors' */ // 14f3
{ "15", SAMPLE_NOLOOP }, /* 'Paper' */ // 1508
/* one of these is played when going by the girl between levels */
{ "16", SAMPLE_NOLOOP }, /* 'Very good!' */ // 194a
{ "17", SAMPLE_NOLOOP }, /* 'Wonderful!' */ // 194a
{ "18", SAMPLE_NOLOOP }, /* 'Come on!' */ // 194a
{ "19", SAMPLE_NOLOOP }, /* 'I love you!' */ // 194a
{ "1a", SAMPLE_NOLOOP }, /* 'See you again!' */ // 194a
{ "", 0 } // must have this at the end!
};
STD_SAMPLE_PICK(thehand)
STD_SAMPLE_FN(thehand)
-
Still a work in progress. That's it for tonight. ^^
-
really interesting, big thanks iq !
-
Cool posts :)
-
nice post...
my brain hurts. :confused:
-
Bump :wink:
Nice guide you created here IQ_132 was wondering if you get a spare moment could you explain what this code does
is it save state related
static INT32 DrvScan(INT32 nAction,INT32 *pnMin)
{
struct BurnArea ba;
if (pnMin) {
*pnMin = 0x029707;
}
if (nAction & ACB_VOLATILE) {
memset(&ba, 0, sizeof(ba));
ba.Data = AllRam;
ba.nLen = RamEnd - AllRam;
ba.szName = "All Ram";
BurnAcb(&ba);
ZetScan(nAction);
beast_scan(nAction);
BurnYM2203Scan(nAction, pnMin);
MSM6295Scan(0, nAction);
MSM6295Scan(1, nAction);
SCAN_VAR(nBankAddress0);
SCAN_VAR(nBankAddress1);
SCAN_VAR(nBankAddress2);
SCAN_VAR(videoreg);
SCAN_VAR(scrollx);
SCAN_VAR(scrolly);
}
if (nAction & ACB_WRITE)
{
ZetOpen(0);
cpu0_bankswitch(nBankAddress0);
ZetClose();
ZetOpen(1);
cpu1_bankswitch(nBankAddress1);
ZetClose();
ZetOpen(2);
cpu2_bankswitch(nBankAddress2);
ZetClose();
}
return 0;
}
Im still trying to get a certain game playable the sound is there it controls coins and starts it just has no graphics for some reason
just plodding away trying to track down a possible cause i just wondered what the above is for in the driver
-
Yes - the Scan() function is called when saving or loading a savestate.
The function tells the driver which modules need scanning, which variables and anything that needs doing on top (in this case bankswitching the CPUs after restoring the bankswitch address variables).
-
Yes - the Scan() function is called when saving or loading a savestate.
The function tells the driver which modules need scanning, which variables and anything that needs doing on top (in this case bankswitching the CPUs after restoring the bankswitch address variables).
Thanks for your reply im wondering if the fact FBL uses an older save state handling and this driver i assume uses a newer
type of save and loading routine could this affect the games graphics eg make them not work any input guys would be appreciated
im trying to rule this out before i focus again on the Zet handling as the cause of course there was some changes in pandora.cpp/h
so last question from me here has the generic tiles.cpp/h had any changes since the last FBA release and the addition of djboy
-
Soted the missing graphics in DJ Boy sometimes a lucky guess works better than weeks of studying code looking for the solution
i noticed this in Airbuster.cpp
ZetMapArea(0xc000, 0xcfff, 0, DrvSprRAM);
// ZetMapArea(0xc000, 0xcfff, 1, DrvSprRAM); // handler
ZetMapArea(0xc000, 0xcfff, 2, DrvSprRAM);
so noticed 2 of the defines had handler written after em in the dj boy driver so i thought id try this since air buster had this way
ZetMapArea(0xb000, 0xbfff, 0, DrvSprRAM);
// ZetMapArea(0xb000, 0xbfff, 1, DrvSprRAM); // handler...
ZetMapArea(0xb000, 0xbfff, 2, DrvSprRAM);
And
ZetMapArea(0xd000, 0xd8ff, 0, DrvPalRAM);
// ZetMapArea(0xd000, 0xd8ff, 1, DrvPalRAM); // handler...
ZetMapArea(0xd000, 0xd8ff, 2, DrvPalRAM);
I wish i tried the above weeks ago was gonna but assumed dj boy needed them enabled why the driver does in FBA
but doesn't in FBL will remain a mystery at least the game works now i played through level 1 everything seems fine
-
Soted the missing graphics in DJ Boy sometimes a lucky guess works better than weeks of studying code looking for the solution
i noticed this in Airbuster.cpp
so noticed 2 of the defines had handler written after em in the dj boy driver so i thought id try this since air buster had this way
I wish i tried the above weeks ago was gonna but assumed dj boy needed them enabled why the driver does in FBA
but doesn't in FBL will remain a mystery at least the game works now i played through level 1 everything seems fine
Ah! Your palette wasn't be calculated, I bet.
-
Ah! Your palette wasn't be calculated, I bet.
Im sure your right i had no idea it needed calculated ;) i assumed i could just drop the driver into FBL and it would work off the bat
thats why your a top developer and im a wee guesser LOL
-
Im sure your right i had no idea it needed calculated ;) i assumed i could just drop the driver into FBL and it would work off the bat
thats why your a top developer and im a wee guesser LOL
I definitely wouldn't say that! We're all just hobbyists looking for something to keep us busy. I think the only professional programmer on the team is Barry!
-
Another last roll of the dice and a lucky guess and ive finally found out what was causing the issues with the games in
the nemesis driver loading the wrong way up and being reversed in game again though it's a mystery to me why
this being disabled in the dips was causing this
// {0 , 0xfe, 0 , 2, "Flip Screen" },
// {0x16, 0x01, 0x01, 0x01, "Off" },
// {0x16, 0x01, 0x01, 0x00, "On" },
i take it from the above flip screen isn't available in the games but after enabling it salamander etc now load the correct way up
and are graphically 100% as i can disable the flip screen in the dips i assume the flip was get set to on by default for some reason
in FBL