mirror of
https://github.com/holub/mame
synced 2025-05-24 14:56:21 +03:00
518 lines
12 KiB
C
518 lines
12 KiB
C
/***************************************************************************
|
|
|
|
a7800.c
|
|
|
|
Machine file to handle emulation of the Atari 7800.
|
|
|
|
5-Nov-2003 npwoods Cleanups
|
|
|
|
14-May-2002 kubecj Fixed Fatal Run - adding simple riot timer helped.
|
|
maybe someone with knowledge should add full fledged
|
|
riot emulation?
|
|
|
|
13-May-2002 kubecj Fixed a7800_cart_type not to be too short ;-D
|
|
fixed for loading of bank6 cart (uh, I hope)
|
|
fixed for loading 64k supercarts
|
|
fixed for using PAL bios
|
|
cart not needed when in PAL mode
|
|
added F18 Hornet bank select type
|
|
added Activision bank select type
|
|
19-Feb-2010 DanB Added return values for TIA collision registers
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "includes/a7800.h"
|
|
#include "cpu/m6502/m6502.h"
|
|
|
|
|
|
|
|
/* local */
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
6532 RIOT
|
|
***************************************************************************/
|
|
|
|
READ8_MEMBER(a7800_state::riot_joystick_r)
|
|
{
|
|
return m_io_joysticks->read();
|
|
}
|
|
|
|
READ8_MEMBER(a7800_state::riot_console_button_r)
|
|
{
|
|
return m_io_console_buttons->read();
|
|
}
|
|
|
|
WRITE8_MEMBER(a7800_state::riot_button_pullup_w)
|
|
{
|
|
m_p1_one_button = data & 0x04; // pin 6 of the controller port is held high by the riot chip when reading two-button controllers (from schematic)
|
|
m_p2_one_button = data & 0x10;
|
|
}
|
|
|
|
const riot6532_interface a7800_r6532_interface =
|
|
{
|
|
DEVCB_DRIVER_MEMBER(a7800_state,riot_joystick_r),
|
|
DEVCB_DRIVER_MEMBER(a7800_state,riot_console_button_r),
|
|
DEVCB_NULL,
|
|
DEVCB_DRIVER_MEMBER(a7800_state,riot_button_pullup_w),
|
|
DEVCB_NULL
|
|
};
|
|
|
|
|
|
/***************************************************************************
|
|
DRIVER INIT
|
|
***************************************************************************/
|
|
|
|
void a7800_state::a7800_driver_init(int ispal, int lines)
|
|
{
|
|
address_space& space = m_maincpu->space(AS_PROGRAM);
|
|
m_ROM = m_region_maincpu->base();
|
|
m_ispal = ispal;
|
|
m_lines = lines;
|
|
m_p1_one_button = 1;
|
|
m_p2_one_button = 1;
|
|
|
|
/* standard banks */
|
|
m_bank5->set_base(&m_ROM[0x2040]); /* RAM0 */
|
|
m_bank6->set_base(&m_ROM[0x2140]); /* RAM1 */
|
|
m_bank7->set_base(&m_ROM[0x2000]); /* MAINRAM */
|
|
|
|
/* Brutal hack put in as a consequence of new memory system; fix this */
|
|
space.install_readwrite_bank(0x0480, 0x04FF,"bank10");
|
|
m_bank10 = membank("bank10");
|
|
m_bank10->set_base(m_ROM + 0x0480);
|
|
space.install_readwrite_bank(0x1800, 0x27FF, "bank11");
|
|
m_bank11 = membank("bank11");
|
|
m_bank11->set_base(m_ROM + 0x1800);
|
|
|
|
m_bios_bkup = NULL;
|
|
m_cart_bkup = NULL;
|
|
|
|
/* Allocate memory for BIOS bank switching */
|
|
m_bios_bkup = auto_alloc_array_clear(machine(), UINT8, 0x4000);
|
|
m_cart_bkup = auto_alloc_array(machine(), UINT8, 0x4000);
|
|
|
|
/* save the BIOS so we can switch it in and out */
|
|
memcpy( m_bios_bkup, m_ROM + 0xC000, 0x4000 );
|
|
|
|
/* Initialize cart area to "no data" */
|
|
memset( m_cart_bkup, 0xFF, 0x4000 );
|
|
|
|
/* defaults for PAL bios without cart */
|
|
m_cart_type = 0;
|
|
m_stick_type = 1;
|
|
}
|
|
|
|
|
|
DRIVER_INIT_MEMBER(a7800_state,a7800_ntsc)
|
|
{
|
|
a7800_driver_init(FALSE, 262);
|
|
}
|
|
|
|
|
|
DRIVER_INIT_MEMBER(a7800_state,a7800_pal)
|
|
{
|
|
a7800_driver_init(TRUE, 312);
|
|
}
|
|
|
|
|
|
void a7800_state::machine_reset()
|
|
{
|
|
UINT8 *memory;
|
|
address_space& space = m_maincpu->space(AS_PROGRAM);
|
|
|
|
m_ctrl_lock = 0;
|
|
m_ctrl_reg = 0;
|
|
m_maria_flag = 0;
|
|
|
|
/* set banks to default states */
|
|
memory = m_region_maincpu->base();
|
|
m_bank1->set_base(memory + 0x4000 );
|
|
m_bank2->set_base(memory + 0x8000 );
|
|
m_bank3->set_base(memory + 0xA000 );
|
|
m_bank4->set_base(memory + 0xC000 );
|
|
|
|
/* pokey cartridge */
|
|
if (m_cart_type & 0x01)
|
|
{
|
|
space.install_readwrite_handler(0x4000, 0x7FFF, read8_delegate(FUNC(pokey_device::read),(pokey_device*)m_pokey), write8_delegate(FUNC(pokey_device::write),(pokey_device*)m_pokey));
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
CARTRIDGE HANDLING
|
|
***************************************************************************/
|
|
|
|
#define MBANK_TYPE_ATARI 0x0000
|
|
#define MBANK_TYPE_ACTIVISION 0x0100
|
|
#define MBANK_TYPE_ABSOLUTE 0x0200
|
|
|
|
/* Header format
|
|
0 Header version - 1 byte
|
|
1..16 "ATARI7800 " - 16 bytes
|
|
17..48 Cart title - 32 bytes
|
|
49..52 data length - 4 bytes
|
|
53..54 cart type - 2 bytes
|
|
bit 0 0x01 - pokey cart
|
|
bit 1 0x02 - supercart bank switched
|
|
bit 2 0x04 - supercart RAM at $4000
|
|
bit 3 0x08 - additional state->m_ROM at $4000
|
|
|
|
bit 8-15 - Special
|
|
0 = Normal cart
|
|
1 = Absolute (F18 Hornet)
|
|
2 = Activision
|
|
|
|
55 controller 1 type - 1 byte
|
|
56 controller 2 type - 1 byte
|
|
0 = None
|
|
1 = Joystick
|
|
2 = Light Gun
|
|
57 0 = NTSC/1 = PAL
|
|
|
|
100..127 "ACTUAL CART DATA STARTS HERE" - 28 bytes
|
|
|
|
Versions:
|
|
Version 0: Initial release
|
|
Version 1: Added PAL/NTSC bit. Added Special cart byte.
|
|
Changed 53 bit 2, added bit 3
|
|
|
|
*/
|
|
void a7800_partialhash(hash_collection &dest, const unsigned char *data,
|
|
unsigned long length, const char *functions)
|
|
{
|
|
if (length <= 128)
|
|
return;
|
|
dest.compute(&data[128], length - 128, functions);
|
|
}
|
|
|
|
|
|
static int a7800_verify_cart(char header[128])
|
|
{
|
|
const char* tag = "ATARI7800";
|
|
|
|
if( strncmp( tag, header + 1, 9 ) )
|
|
{
|
|
logerror("Not a valid A7800 image\n");
|
|
return IMAGE_VERIFY_FAIL;
|
|
}
|
|
|
|
logerror("returning ID_OK\n");
|
|
return IMAGE_VERIFY_PASS;
|
|
}
|
|
|
|
|
|
struct a7800_pcb
|
|
{
|
|
const char *pcb_name;
|
|
UINT16 type;
|
|
};
|
|
|
|
// sketchy support for a7800 cart types
|
|
// TODO: proper emulation of the banking based on xml
|
|
// (and on the real cart layout!)
|
|
static const a7800_pcb pcb_list[] =
|
|
{
|
|
{ "ABSOLUTE", MBANK_TYPE_ABSOLUTE },
|
|
{ "ACTIVISION", MBANK_TYPE_ACTIVISION },
|
|
{ "TYPE-0", 0x0 },
|
|
{ "TYPE-1", 0x1 },
|
|
{ "TYPE-2", 0x2 },
|
|
{ "TYPE-3", 0x3 },
|
|
{ "TYPE-6", 0x6 },
|
|
{ "TYPE-A", 0xa },
|
|
{ 0 }
|
|
};
|
|
|
|
static UINT16 a7800_get_pcb_id(const char *pcb)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_LENGTH(pcb_list); i++)
|
|
{
|
|
if (!mame_stricmp(pcb_list[i].pcb_name, pcb))
|
|
return pcb_list[i].type;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEVICE_IMAGE_LOAD_MEMBER( a7800_state, a7800_cart )
|
|
{
|
|
UINT32 len = 0, start = 0;
|
|
unsigned char header[128];
|
|
UINT8 *memory = m_region_maincpu->base();
|
|
const char *pcb_name;
|
|
|
|
// detect cart type either from xml or from header
|
|
if (image.software_entry() == NULL)
|
|
{
|
|
/* Load and decode the header */
|
|
image.fread(header, 128);
|
|
|
|
/* Check the cart */
|
|
if( a7800_verify_cart((char *)header) == IMAGE_VERIFY_FAIL)
|
|
return IMAGE_INIT_FAIL;
|
|
|
|
len =(header[49] << 24) |(header[50] << 16) |(header[51] << 8) | header[52];
|
|
m_cart_size = len;
|
|
|
|
m_cart_type =(header[53] << 8) | header[54];
|
|
m_stick_type = header[55];
|
|
logerror("Cart type: %x\n", m_cart_type);
|
|
|
|
/* For now, if game support stick and gun, set it to stick */
|
|
if (m_stick_type == 3)
|
|
m_stick_type = 1;
|
|
}
|
|
else
|
|
{
|
|
len = image.get_software_region_length("rom");
|
|
m_cart_size = len;
|
|
// TODO: add stick/gun support to xml!
|
|
m_stick_type = 1;
|
|
if ((pcb_name = image.get_feature("pcb_type")) == NULL)
|
|
m_cart_type = 0;
|
|
else
|
|
m_cart_type = a7800_get_pcb_id(pcb_name);
|
|
}
|
|
|
|
if (m_cart_type == 0 || m_cart_type == 1)
|
|
{
|
|
/* Normal Cart */
|
|
start = 0x10000 - len;
|
|
m_cartridge_rom = memory + start;
|
|
if (image.software_entry() == NULL)
|
|
image.fread(m_cartridge_rom, len);
|
|
else
|
|
memcpy(m_cartridge_rom, image.get_software_region("rom"), len);
|
|
}
|
|
else if (m_cart_type & 0x02)
|
|
{
|
|
/* Super Cart */
|
|
/* Extra ROM at $4000 */
|
|
if (m_cart_type & 0x08)
|
|
{
|
|
if (image.software_entry() == NULL)
|
|
image.fread(memory + 0x4000, 0x4000);
|
|
else
|
|
memcpy(memory + 0x4000, image.get_software_region("rom"), 0x4000);
|
|
len -= 0x4000;
|
|
start = 0x4000;
|
|
}
|
|
|
|
m_cartridge_rom = memory + 0x10000;
|
|
if (image.software_entry() == NULL)
|
|
image.fread(m_cartridge_rom, len);
|
|
else
|
|
memcpy(m_cartridge_rom, image.get_software_region("rom") + start, len);
|
|
|
|
/* bank 0 */
|
|
memcpy(memory + 0x8000, memory + 0x10000, 0x4000);
|
|
|
|
/* last bank */
|
|
memcpy(memory + 0xC000, memory + 0x10000 + len - 0x4000, 0x4000);
|
|
|
|
/* fixed 2002/05/13 kubecj
|
|
there was 0x08, I added also two other cases.
|
|
Now, it loads bank n-2 to $4000 if it's empty.
|
|
*/
|
|
|
|
/* bank n-2 */
|
|
if (!(m_cart_type & 0x0d))
|
|
{
|
|
memcpy(memory + 0x4000, memory + 0x10000 + len - 0x8000, 0x4000);
|
|
}
|
|
}
|
|
else if (m_cart_type == MBANK_TYPE_ABSOLUTE)
|
|
{
|
|
/* F18 Hornet */
|
|
|
|
logerror("Cart type: %x Absolute\n",m_cart_type);
|
|
|
|
m_cartridge_rom = memory + 0x10000;
|
|
if (image.software_entry() == NULL)
|
|
image.fread(m_cartridge_rom, len);
|
|
else
|
|
memcpy(m_cartridge_rom, image.get_software_region("rom") + start, len);
|
|
|
|
/* bank 0 */
|
|
memcpy(memory + 0x4000, memory + 0x10000, 0x4000);
|
|
|
|
/* last bank */
|
|
memcpy(memory + 0x8000, memory + 0x18000, 0x8000);
|
|
}
|
|
else if (m_cart_type == MBANK_TYPE_ACTIVISION)
|
|
{
|
|
/* Activision */
|
|
|
|
logerror("Cart type: %x Activision\n",m_cart_type);
|
|
|
|
m_cartridge_rom = memory + 0x10000;
|
|
if (image.software_entry() == NULL)
|
|
image.fread(m_cartridge_rom, len);
|
|
else
|
|
memcpy(m_cartridge_rom, image.get_software_region("rom") + start, len);
|
|
|
|
/* bank 0 */
|
|
memcpy(memory + 0xa000, memory + 0x10000, 0x4000);
|
|
|
|
/* bank6 hi */
|
|
memcpy(memory + 0x4000, memory + 0x2a000, 0x2000);
|
|
|
|
/* bank6 lo */
|
|
memcpy(memory + 0x6000, memory + 0x28000, 0x2000);
|
|
|
|
/* bank7 hi */
|
|
memcpy(memory + 0x8000, memory + 0x2e000, 0x2000);
|
|
|
|
/* bank7 lo */
|
|
memcpy(memory + 0xe000, memory + 0x2c000, 0x2000);
|
|
|
|
}
|
|
|
|
memcpy(m_cart_bkup, memory + 0xc000, 0x4000);
|
|
memcpy(memory + 0xc000, m_bios_bkup, 0x4000);
|
|
return IMAGE_INIT_PASS;
|
|
}
|
|
|
|
|
|
WRITE8_MEMBER(a7800_state::a7800_RAM0_w)
|
|
{
|
|
m_ROM[0x2040 + offset] = data;
|
|
m_ROM[0x40 + offset] = data;
|
|
}
|
|
|
|
|
|
WRITE8_MEMBER(a7800_state::a7800_cart_w)
|
|
{
|
|
UINT8 *memory = m_region_maincpu->base();
|
|
|
|
if(offset < 0x4000)
|
|
{
|
|
if(m_cart_type & 0x04)
|
|
{
|
|
m_ROM[0x4000 + offset] = data;
|
|
}
|
|
else if(m_cart_type & 0x01)
|
|
{
|
|
m_pokey->write(space, offset, data);
|
|
}
|
|
else
|
|
{
|
|
logerror("Undefined write A: %x",offset + 0x4000);
|
|
}
|
|
}
|
|
|
|
if(( m_cart_type & 0x02 ) &&( offset >= 0x4000 ) )
|
|
{
|
|
/* fix for 64kb supercart */
|
|
if( m_cart_size == 0x10000 )
|
|
{
|
|
data &= 0x03;
|
|
}
|
|
else
|
|
{
|
|
data &= 0x07;
|
|
}
|
|
m_bank2->set_base(memory + 0x10000 + (data << 14));
|
|
m_bank3->set_base(memory + 0x12000 + (data << 14));
|
|
/* logerror("BANK SEL: %d\n",data); */
|
|
}
|
|
else if(( m_cart_type == MBANK_TYPE_ABSOLUTE ) &&( offset == 0x4000 ) )
|
|
{
|
|
/* F18 Hornet */
|
|
/*logerror( "F18 BANK SEL: %d\n", data );*/
|
|
if( data & 1 )
|
|
{
|
|
m_bank1->set_base(memory + 0x10000 );
|
|
}
|
|
else if( data & 2 )
|
|
{
|
|
m_bank1->set_base(memory + 0x14000 );
|
|
}
|
|
}
|
|
else if(( m_cart_type == MBANK_TYPE_ACTIVISION ) &&( offset >= 0xBF80 ) )
|
|
{
|
|
/* Activision */
|
|
data = offset & 7;
|
|
|
|
/*logerror( "Activision BANK SEL: %d\n", data );*/
|
|
|
|
m_bank3->set_base(memory + 0x10000 + ( data << 14 ) );
|
|
m_bank4->set_base(memory + 0x12000 + ( data << 14 ) );
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
TIA
|
|
***************************************************************************/
|
|
|
|
READ8_MEMBER(a7800_state::a7800_TIA_r)
|
|
{
|
|
switch(offset & 0x0f)
|
|
{
|
|
case 0x00:
|
|
case 0x01:
|
|
case 0x02:
|
|
case 0x03:
|
|
case 0x04:
|
|
case 0x05:
|
|
case 0x06:
|
|
case 0x07:
|
|
/* Even though the 7800 doesn't use the TIA graphics the collision registers should
|
|
still return a reasonable value */
|
|
return 0x00;
|
|
case 0x08:
|
|
return((m_io_buttons->read() & 0x02) << 6);
|
|
case 0x09:
|
|
return((m_io_buttons->read() & 0x08) << 4);
|
|
case 0x0A:
|
|
return((m_io_buttons->read() & 0x01) << 7);
|
|
case 0x0B:
|
|
return((m_io_buttons->read() & 0x04) << 5);
|
|
case 0x0c:
|
|
if(((m_io_buttons->read() & 0x08) ||(m_io_buttons->read() & 0x02)) && m_p1_one_button)
|
|
return 0x00;
|
|
else
|
|
return 0x80;
|
|
case 0x0d:
|
|
if(((m_io_buttons->read() & 0x01) ||(m_io_buttons->read() & 0x04)) && m_p2_one_button)
|
|
return 0x00;
|
|
else
|
|
return 0x80;
|
|
default:
|
|
logerror("undefined TIA read %x\n",offset);
|
|
|
|
}
|
|
return 0xFF;
|
|
}
|
|
|
|
|
|
WRITE8_MEMBER(a7800_state::a7800_TIA_w)
|
|
{
|
|
switch(offset) {
|
|
case 0x01:
|
|
if(data & 0x01)
|
|
{
|
|
m_maria_flag=1;
|
|
}
|
|
if(!m_ctrl_lock)
|
|
{
|
|
m_ctrl_lock = data & 0x01;
|
|
m_ctrl_reg = data;
|
|
|
|
if (data & 0x04)
|
|
memcpy( m_ROM + 0xC000, m_cart_bkup, 0x4000 );
|
|
else
|
|
memcpy( m_ROM + 0xC000, m_bios_bkup, 0x4000 );
|
|
}
|
|
break;
|
|
}
|
|
m_tia->tia_sound_w(space, offset, data);
|
|
m_ROM[offset] = data;
|
|
}
|