ti99_8: Chipset low-level emulation; ti99_4x: using new GROMs

This commit is contained in:
Michael Zapf 2016-03-31 23:55:50 +02:00
parent 35eab35778
commit 13fce89d59
20 changed files with 3277 additions and 2308 deletions

View File

@ -2029,8 +2029,6 @@ if (BUSES["TI99X"]~=null) then
MAME_DIR .. "src/devices/bus/ti99x/datamux.h",
MAME_DIR .. "src/devices/bus/ti99x/genboard.cpp",
MAME_DIR .. "src/devices/bus/ti99x/genboard.h",
MAME_DIR .. "src/devices/bus/ti99x/grom.cpp",
MAME_DIR .. "src/devices/bus/ti99x/grom.h",
MAME_DIR .. "src/devices/bus/ti99x/gromport.cpp",
MAME_DIR .. "src/devices/bus/ti99x/gromport.h",
MAME_DIR .. "src/devices/bus/ti99x/handset.cpp",

View File

@ -92,43 +92,87 @@
#define ACTIVE_TAG "ACTIVE"
#define LOG logerror
#define VERBOSE 1
#define TRACE_ROM 0
#define TRACE_GROM 0
#define TRACE_CRU 0
#define TRACE_SWITCH 0
ti_pcode_card_device::ti_pcode_card_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: ti_expansion_card_device(mconfig, TI99_P_CODE, "TI-99 P-Code Card", tag, owner, clock, "ti99_pcode", __FILE__), m_rom(nullptr), m_bank_select(0), m_switch(false)
: ti_expansion_card_device(mconfig, TI99_P_CODE, "TI-99 P-Code Card", tag, owner, clock, "ti99_pcode", __FILE__),
m_rom(nullptr),
m_bank_select(0),
m_active(false),
m_clock_count(0),
m_clockhigh(false),
m_inDsrArea(false),
m_isrom0(false),
m_isrom12(false),
m_isgrom(false),
m_address(0)
{
}
SETADDRESS_DBIN_MEMBER( ti_pcode_card_device::setaddress_dbin )
{
// Do not allow setaddress for the debugger. It will mess up the
// setaddress/memory access pairs when the CPU enters wait states.
if (space.debugger_access()) return;
m_address = offset;
m_inDsrArea = ((m_address & m_select_mask)==m_select_value);
line_state a14 = ((m_address & 2)!=0)? ASSERT_LINE : CLEAR_LINE;
m_isrom0 = ((m_address & 0xf000)==0x4000);
m_isrom12 = ((m_address & 0xf000)==0x5000);
// Valid access (GROM write with DBIN=0 or read with DBIN=1)
bool validaccess = (state==CLEAR_LINE || (m_address & 0x0400)==0);
// GROM access 0101 1011 1111 1100
m_isgrom = ((m_address & 0xfbfd)==0x5bfc) && validaccess;
if (validaccess)
{
int lines = (state==ASSERT_LINE)? 1 : 0;
if (a14==ASSERT_LINE) lines |= 2;
line_state select = m_isgrom? ASSERT_LINE : CLEAR_LINE;
// always deliver to GROM so that the select line may be cleared
for (int i=0; i < 8; i++)
m_grom[i]->set_lines(space, lines, select);
}
}
READ8Z_MEMBER( ti_pcode_card_device::readz )
{
if (m_switch && m_selected && ((offset & m_select_mask)==m_select_value))
if (m_active && m_inDsrArea && m_selected)
{
// GROM access
if ((offset & GROMMASK)==GROMREAD)
if (m_isrom0)
{
for (auto & elem : m_grom) elem->readz(space, offset, value, mem_mask);
if (VERBOSE>5) LOG("ti99_pcode: read from grom %04x: %02x\n", offset&0xffff, *value);
*value = m_rom[m_address & 0x0fff];
if (TRACE_ROM) logerror("Read from rom %04x: %02x\n", offset&0xffff, *value);
}
else
{
if ((offset & 0x1000) == 0x0000)
if (m_isgrom)
{
/* Accesses ROM 4732 (4K) */
// 0000 xxxx xxxx xxxx
*value = m_rom[offset & 0x0fff];
if (VERBOSE>5) LOG("ti99_pcode: read from rom %04x: %02x\n", offset&0xffff, *value);
for (auto& elem : m_grom) elem->readz(space, m_address, value);
if (TRACE_GROM) logerror("Read from grom %04x: %02x\n", m_address&0xffff, *value);
}
else
{
// Accesses ROM 4764 (2*4K)
// We have two banks here which are activated according
// to the setting of CRU bit 4
// Bank 0 is the ROM above
// 0001 xxxx xxxx xxxx Bank 1
// 0010 xxxx xxxx xxxx Bank 2
*value = m_rom[(m_bank_select<<12) | (offset & 0x0fff)];
if (VERBOSE>5) LOG("ti99_pcode: read from rom %04x (%02x): %02x\n", offset&0xffff, m_bank_select, *value);
if (m_isrom12)
{
// Accesses ROM 4764 (2*4K)
// We have two banks here which are activated according
// to the setting of CRU bit 4
// Bank 0 is the ROM above
// 0001 xxxx xxxx xxxx Bank 1
// 0010 xxxx xxxx xxxx Bank 2
*value = m_rom[(m_bank_select<<12) | (m_address & 0x0fff)];
if (TRACE_ROM) logerror("Read from rom %04x (%02x): %02x\n", m_address&0xffff, m_bank_select, *value);
}
}
}
}
@ -140,34 +184,36 @@ READ8Z_MEMBER( ti_pcode_card_device::readz )
*/
WRITE8_MEMBER( ti_pcode_card_device::write )
{
if (m_switch && m_selected)
if (m_active && m_isgrom && m_selected)
{
if ((offset & m_select_mask)==m_select_value)
{
if (VERBOSE>5) LOG("ti99_pcode: write to address %04x: %02x\n", offset & 0xffff, data);
// 0101 1111 1111 11x0
if ((offset & GROMMASK) == GROMWRITE)
{
for (auto & elem : m_grom) elem->write(space, offset, data, mem_mask);
}
}
for (auto & elem : m_grom) elem->write(space, m_address, data);
}
}
/*
Common READY* line from the GROMs.
At this time we do not emulate GROM READY* since the CPU emulation does
not yet process READY*. If it did, however, we would have to do a similar
handling as in peribox (with INTA*): The common READY* line is a logical
AND of all single READY lines. If any GROM pulls it down, the line goes
down, and only if all GROMs release it, it pulls up again. We should think
about a general solution.
*/
WRITE_LINE_MEMBER( ti_pcode_card_device::ready_line )
{
m_slot->set_ready(state);
}
/*
CLKOUT line from the CPU. This line is divided by 8 to generate a 375 Khz
clock input for the GROMs, which are thus running at a lower rate than
those in the console driven by the VDP (477 kHz).
*/
WRITE_LINE_MEMBER( ti_pcode_card_device::clock_in)
{
m_clock_count = (m_clock_count+1) & 0x03; // four pulses high, four pulses low
if (m_clock_count==0)
{
// Toggle
m_clockhigh = !m_clockhigh;
for (auto & elem : m_grom) elem->gclock_in(m_clockhigh? ASSERT_LINE : CLEAR_LINE);
}
}
/*
CRU read handler. The P-Code card does not offer CRU read lines, so
we just ignore any request. (Note that CRU lines are not like memory; you
@ -198,55 +244,22 @@ WRITE8_MEMBER(ti_pcode_card_device::cruwrite)
if (addr==0x80) // Bit 4 is on address line 8
{
m_bank_select = (data+1); // we're calling this bank 1 and bank 2
if (VERBOSE>5) LOG("ti99_pcode: select rom bank %d\n", m_bank_select);
if (TRACE_CRU) logerror("Select rom bank %d\n", m_bank_select);
}
}
}
static GROM_CONFIG(pgrom0_config)
{
false, 0, PCODE_GROM_TAG, 0x0000, 0x1800, GROMFREQ
};
static GROM_CONFIG(pgrom1_config)
{
false, 1, PCODE_GROM_TAG, 0x2000, 0x1800, GROMFREQ
};
static GROM_CONFIG(pgrom2_config)
{
false, 2, PCODE_GROM_TAG, 0x4000, 0x1800, GROMFREQ
};
static GROM_CONFIG(pgrom3_config)
{
false, 3, PCODE_GROM_TAG, 0x6000, 0x1800, GROMFREQ
};
static GROM_CONFIG(pgrom4_config)
{
false, 4, PCODE_GROM_TAG, 0x8000, 0x1800, GROMFREQ
};
static GROM_CONFIG(pgrom5_config)
{
false, 5, PCODE_GROM_TAG, 0xa000, 0x1800, GROMFREQ
};
static GROM_CONFIG(pgrom6_config)
{
false, 6, PCODE_GROM_TAG, 0xc000, 0x1800, GROMFREQ
};
static GROM_CONFIG(pgrom7_config)
{
false, 7, PCODE_GROM_TAG, 0xe000, 0x1800, GROMFREQ
};
void ti_pcode_card_device::device_start()
{
m_cru_base = 0x1f00;
m_grom[0] = static_cast<ti99_grom_device*>(subdevice(PGROM0_TAG));
m_grom[1] = static_cast<ti99_grom_device*>(subdevice(PGROM1_TAG));
m_grom[2] = static_cast<ti99_grom_device*>(subdevice(PGROM2_TAG));
m_grom[3] = static_cast<ti99_grom_device*>(subdevice(PGROM3_TAG));
m_grom[4] = static_cast<ti99_grom_device*>(subdevice(PGROM4_TAG));
m_grom[5] = static_cast<ti99_grom_device*>(subdevice(PGROM5_TAG));
m_grom[6] = static_cast<ti99_grom_device*>(subdevice(PGROM6_TAG));
m_grom[7] = static_cast<ti99_grom_device*>(subdevice(PGROM7_TAG));
m_grom[0] = downcast<tmc0430_device*>(subdevice(PGROM0_TAG));
m_grom[1] = downcast<tmc0430_device*>(subdevice(PGROM1_TAG));
m_grom[2] = downcast<tmc0430_device*>(subdevice(PGROM2_TAG));
m_grom[3] = downcast<tmc0430_device*>(subdevice(PGROM3_TAG));
m_grom[4] = downcast<tmc0430_device*>(subdevice(PGROM4_TAG));
m_grom[5] = downcast<tmc0430_device*>(subdevice(PGROM5_TAG));
m_grom[6] = downcast<tmc0430_device*>(subdevice(PGROM6_TAG));
m_grom[7] = downcast<tmc0430_device*>(subdevice(PGROM7_TAG));
m_rom = memregion(PCODE_ROM_TAG)->base();
}
@ -264,8 +277,15 @@ void ti_pcode_card_device::device_reset()
}
m_bank_select = 1;
m_selected = false;
m_clock_count = 0;
m_clockhigh = false;
m_switch = ioport(ACTIVE_TAG)->read();
m_active = ioport(ACTIVE_TAG)->read();
m_isrom0 = false;
m_isrom12 = false;
m_isgrom = false;
m_address = 0;
}
void ti_pcode_card_device::device_config_complete()
@ -274,28 +294,20 @@ void ti_pcode_card_device::device_config_complete()
INPUT_CHANGED_MEMBER( ti_pcode_card_device::switch_changed )
{
if (VERBOSE>7) LOG("ti_pcode_card_device: switch changed to %d\n", newval);
m_switch = (newval != 0);
if (TRACE_SWITCH) logerror("Switch changed to %d\n", newval);
m_active = (newval != 0);
}
MACHINE_CONFIG_FRAGMENT( ti99_pcode )
MCFG_GROM_ADD( PGROM0_TAG, pgrom0_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM1_TAG, pgrom1_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM2_TAG, pgrom2_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM3_TAG, pgrom3_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM4_TAG, pgrom4_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM5_TAG, pgrom5_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM6_TAG, pgrom6_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM7_TAG, pgrom7_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM0_TAG, 0, PCODE_GROM_TAG, 0x0000, WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM1_TAG, 1, PCODE_GROM_TAG, 0x2000, WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM2_TAG, 2, PCODE_GROM_TAG, 0x4000, WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM3_TAG, 3, PCODE_GROM_TAG, 0x6000, WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM4_TAG, 4, PCODE_GROM_TAG, 0x8000, WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM5_TAG, 5, PCODE_GROM_TAG, 0xa000, WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM6_TAG, 6, PCODE_GROM_TAG, 0xc000, WRITELINE(ti_pcode_card_device, ready_line))
MCFG_GROM_ADD( PGROM7_TAG, 7, PCODE_GROM_TAG, 0xe000, WRITELINE(ti_pcode_card_device, ready_line))
MACHINE_CONFIG_END
INPUT_PORTS_START( ti99_pcode )

View File

@ -17,7 +17,7 @@
#include "emu.h"
#include "peribox.h"
#include "bus/ti99x/grom.h"
#include "machine/tmc0430.h"
extern const device_type TI99_P_CODE;
@ -29,6 +29,9 @@ public:
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin) override;
DECLARE_WRITE_LINE_MEMBER(clock_in) override;
DECLARE_WRITE_LINE_MEMBER( ready_line );
DECLARE_INPUT_CHANGED_MEMBER( switch_changed );
@ -42,10 +45,22 @@ protected:
virtual ioport_constructor device_input_ports() const override;
private:
ti99_grom_device* m_grom[8];
tmc0430_device* m_grom[8];
UINT8* m_rom;
int m_bank_select;
bool m_switch;
bool m_active;
int m_clock_count;
bool m_clockhigh;
// Address in card area
bool m_inDsrArea;
bool m_isrom0;
bool m_isrom12;
bool m_isgrom;
// Recent address
int m_address;
};
#endif

View File

@ -259,7 +259,7 @@ WRITE8_MEMBER(peribox_device::write)
SETADDRESS_DBIN_MEMBER(peribox_device::setaddress_dbin)
{
// Ignore the address when the TI-99/8 transmits the high-order 8 bits
// if (!m_memen) return;
if (!m_memen) return;
for (int i=2; i <= 8; i++)
{

File diff suppressed because it is too large Load Diff

View File

@ -18,113 +18,368 @@
#include "emu.h"
#include "ti99defs.h"
#include "machine/tmc0430.h"
#include "video/tms9928a.h"
#include "sound/sn76496.h"
#include "sound/tms5220.h"
#include "gromport.h"
#include "bus/ti99_peb/peribox.h"
extern const device_type MAINBOARD8;
extern const device_type OSO;
extern const device_type SPEECH8;
#define VAQUERRO_TAG "vaquerro"
#define MOFETTA_TAG "mofetta"
#define AMIGO_TAG "amigo"
#define OSO_TAG "oso"
#define SPEECHSYN_TAG "speechsyn"
#define NATIVE 0
#define TI99EM 1
#define PATGEN 2
#define PHYSIC 3
#define CONT 0
#define STOP 1
#define SRAMNAME "SRAM"
#define ROM0NAME "ROM0"
#define ROM1A0NAME "ROM1A"
#define ROM1C0NAME "ROM1C"
#define INTSNAME "INTS"
#define DRAMNAME "DRAM"
#define PCODENAME "PCODE"
#define SRAM_SIZE 2048
#define DRAM_SIZE 65536
// We use these constants in the read/write functions.
enum mapper8_device_kind
class mainboard8_device;
extern const device_type VAQUERRO;
extern const device_type MOFETTA;
extern const device_type AMIGO;
extern const device_type OSO;
enum
{
MAP8_UNDEF = 0,
MAP8_SRAM,
MAP8_ROM0,
MAP8_ROM1A0,
MAP8_ROM1C0,
MAP8_DRAM,
MAP8_PCODE,
MAP8_INTS,
MAP8_DEV // device by name
};
struct mapper8_list_entry
{
const char* name; // Name of the device (used for looking up the device)
int mode; // Mode of the system which applies to this entry
int stop; // Mapper shall stop searching for a matching device when this entry applies
UINT32 select_pattern; // State of the address line bits when addressing this device
UINT32 address_mask; // Bits of the address bus used to address this device
UINT32 write_select; // Additional bits set when doing write accesses to this device
};
#define MAPPER8_CONFIG(name) \
const mapper8_config(name) =
#define MCFG_MAINBOARD8_READY_CALLBACK(_write) \
devcb = &mainboard8_device::set_ready_wr_callback(*device, DEVCB_##_write);
struct mapper8_config
{
const mapper8_list_entry *devlist;
SGMSEL = 1,
TSGSEL = 2,
P8GSEL = 4,
P3GSEL = 8,
VIDSEL = 16
};
/*
Device list of the mapper.
Wait state generator (part of Vaquerro)
*/
class logically_addressed_device
class waitstate_generator
{
friend class simple_list<logically_addressed_device>;
friend class mainboard8_device;
public:
logically_addressed_device(mapper8_device_kind kind, device_t *busdevice, const mapper8_list_entry &entry)
: m_next(nullptr), m_kind(kind), m_device(busdevice), m_config(&entry) { };
waitstate_generator() :
m_counting(false),
m_generate(false),
m_counter(0),
m_addressed(true),
m_ready(true) { };
void select_in(bool addressed);
virtual void ready_in(line_state ready) =0;
virtual void clock_in(line_state clkout) =0;
void treset_in(line_state reset);
private:
logically_addressed_device *m_next; // needed for simple_list
mapper8_device_kind m_kind; // named device or predefined
device_t *m_device; // the actual device
const mapper8_list_entry *m_config;
int select_out();
void init(int select_value) { m_selvalue = select_value; }
line_state ready_out();
bool is_counting();
bool is_generating();
bool is_ready();
protected:
// Two flipflops
bool m_counting;
bool m_generate;
// Counter
int m_counter;
// Select value (indicates selected line)
int m_selvalue;
// Line state flags
bool m_addressed;
bool m_ready;
};
class grom_waitstate_generator : public waitstate_generator
{
public:
void ready_in(line_state ready) override;
void clock_in(line_state clkout) override;
};
class video_waitstate_generator : public waitstate_generator
{
public:
void ready_in(line_state ready) override { };
void clock_in(line_state clkout) override;
};
/*
Device list of the mapper.
Custom chip: Vaquerro
*/
class physically_addressed_device
class vaquerro_device : public device_t
{
friend class simple_list<physically_addressed_device>;
friend class mainboard8_device;
public:
physically_addressed_device(mapper8_device_kind kind, device_t *busdevice, const mapper8_list_entry &entry)
: m_next(nullptr), m_kind(kind), m_device(busdevice), m_config(&entry) { };
vaquerro_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
void device_start() override;
void device_reset() override;
line_state ready();
void treset();
DECLARE_READ8_MEMBER( read );
DECLARE_SETADDRESS_DBIN_MEMBER( set_address );
DECLARE_READ_LINE_MEMBER( sprd_out );
DECLARE_READ_LINE_MEMBER( spwt_out );
DECLARE_READ_LINE_MEMBER( sccs_out );
DECLARE_READ_LINE_MEMBER( sromcs_out );
// Collective select line query
int gromcs_out();
DECLARE_READ_LINE_MEMBER( vdprd_out );
DECLARE_READ_LINE_MEMBER( vdpwt_out );
DECLARE_READ_LINE_MEMBER( lascsq_out );
DECLARE_READ_LINE_MEMBER( ggrdy_out );
DECLARE_WRITE_LINE_MEMBER( hold_cpu );
DECLARE_WRITE_LINE_MEMBER( crus_in );
DECLARE_WRITE_LINE_MEMBER( crusgl_in );
DECLARE_WRITE_LINE_MEMBER( clock_in );
DECLARE_WRITE_LINE_MEMBER( memen_in );
DECLARE_WRITE_LINE_MEMBER( sgmry );
DECLARE_WRITE_LINE_MEMBER( tsgry );
DECLARE_WRITE_LINE_MEMBER( p8gry );
DECLARE_WRITE_LINE_MEMBER( p3gry );
private:
physically_addressed_device *m_next; // needed for simple_list
mapper8_device_kind m_kind; // named device or predefined
device_t *m_device; // the actual device
const mapper8_list_entry *m_config;
// Memory cycle state
bool m_memen;
// Waiting for video
bool m_video_wait;
// State of the CRUS line
line_state m_crus;
// Are the GROM libraries turned on?
bool m_crugl;
// Do we have a logical address space match?
bool m_lasreq = false;
// Keep the decoding result (opens the SRY gate)
bool m_grom_or_video = false;
// Select lines
bool m_spwt;
bool m_sccs;
bool m_sromcs;
bool m_sprd;
bool m_vdprd;
bool m_vdpwt;
// Collective GROM select state
int m_gromsel;
// Outgoing READY
line_state m_ggrdy;
// Outgoing READY latch (common flipflop driving SRY)
bool m_sry;
// Holds the A14 address line state. We need this for the clock_in method.
line_state m_a14;
// Keeps the recent DBIN level
line_state m_dbin_level;
// Wait state logic components
grom_waitstate_generator m_sgmws, m_tsgws, m_p8gws, m_p3gws;
video_waitstate_generator m_vidws;
// Pointer to mainboard
mainboard8_device* m_mainboard;
};
/*
Custom chip: Mofetta
*/
class mofetta_device : public device_t
{
public:
mofetta_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
void device_start() override;
void device_reset() override;
DECLARE_WRITE8_MEMBER( cruwrite );
DECLARE_SETADDRESS_DBIN_MEMBER( set_address );
DECLARE_WRITE_LINE_MEMBER( clock_in );
DECLARE_WRITE_LINE_MEMBER( msast_in );
DECLARE_WRITE_LINE_MEMBER( lascs_in );
DECLARE_WRITE_LINE_MEMBER( pmemen_in );
DECLARE_WRITE_LINE_MEMBER( skdrcs_in );
DECLARE_READ8_MEMBER( rom1cs_out );
DECLARE_READ_LINE_MEMBER( gromclk_out );
DECLARE_READ_LINE_MEMBER( alccs_out );
DECLARE_READ_LINE_MEMBER( prcs_out );
DECLARE_READ_LINE_MEMBER( cmas_out );
DECLARE_READ_LINE_MEMBER( dbc_out );
DECLARE_READ_LINE_MEMBER( rom1cs_out );
DECLARE_READ_LINE_MEMBER( rom1am_out );
DECLARE_READ_LINE_MEMBER( rom1al_out );
private:
// Memory cycle state
bool m_pmemen;
// Logical access
bool m_lasreq;
// DRAM access
bool m_skdrcs;
// Holds the decoding result; essentially names the selected line
bool m_gromclk_up;
// Have we got the upper word of the address?
bool m_gotfirstword;
// Address latch
int m_address_latch;
// Most significant byte of the 24-bit address
int m_prefix;
// CRU select of the 1700 device
bool m_alcpg;
// CRU select of the 2700 device
bool m_txspg;
// ROM1 select lines
bool m_rom1cs;
bool m_rom1am;
bool m_rom1al;
// OSO select
bool m_alccs;
// Pascal ROM select line
bool m_prcs;
// Cartridge port select line
bool m_cmas;
// GROM clock count (as frequency divider)
int m_gromclock_count;
// Remember last msast state for edge detection
line_state m_msast;
// Pointer to mainboard
mainboard8_device* m_mainboard;
};
/*
Custom chip: Amigo
*/
class amigo_device : public device_t
{
public:
amigo_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
void device_start() override;
void device_reset() override;
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
DECLARE_SETOFFSET_MEMBER( set_address );
DECLARE_WRITE_LINE_MEMBER( srdy_in );
DECLARE_WRITE_LINE_MEMBER( clock_in );
DECLARE_WRITE_LINE_MEMBER( crus_in );
DECLARE_WRITE_LINE_MEMBER( lascs_in );
DECLARE_WRITE_LINE_MEMBER( memen_in );
DECLARE_WRITE_LINE_MEMBER( holda_in );
DECLARE_READ_LINE_MEMBER( cpury_out );
DECLARE_READ_LINE_MEMBER( sramcs_out );
DECLARE_READ_LINE_MEMBER( skdrcs_out );
void connect_sram(UINT8* sram) { m_sram = sram; }
bool mapper_accessed() { return m_mapper_accessed; }
private:
// Memory cycle state
bool m_memen;
// DMA methods for loading/saving maps
void mapper_load();
void mapper_save();
// Address mapper registers. Each offset is selected by the first 4 bits
// of the logical address.
UINT32 m_base_register[16];
// Indicates a logical space access
bool m_logical_space;
// Physical address
UINT32 m_physical_address;
// Pointer to SRAM where AMIGO needs to upload/download its map values
UINT8* m_sram;
// Pointer to mainboard
mainboard8_device* m_mainboard;
// Keep the system ready state
line_state m_srdy;
// Outgoing READY level
line_state m_ready_out;
// Keep the CRUS setting
line_state m_crus;
// State of the address creation
int m_amstate;
// Protection flags
int m_protflag;
// Accessing SRAM
bool m_sram_accessed;
// Accessing DRAM
bool m_dram_accessed;
// Accessing the mapper
bool m_mapper_accessed;
// Doing a DMA access
bool m_sram_dma;
// HOLDA flag
bool m_hold_acknowledged;
// Address in SRAM during DMA
UINT32 m_sram_address;
// Number of the currently loaded/save base register
int m_basereg;
// Latched value for mapper DMA transfer
UINT32 m_mapvalue;
};
/*
Custom chip: OSO
*/
class ti998_oso_device : public device_t
class oso_device : public device_t
{
public:
ti998_oso_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
oso_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
void device_start() override;
@ -136,73 +391,60 @@ private:
UINT8 m_xmit;
};
/*
Speech support
*/
#define MCFG_SPEECH8_READY_CALLBACK(_write) \
devcb = &ti998_spsyn_device::set_ready_wr_callback(*device, DEVCB_##_write);
class ti998_spsyn_device : public bus8z_device
{
public:
ti998_spsyn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
template<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object) { return downcast<ti998_spsyn_device &>(device).m_ready.set_callback(object); }
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) { };
DECLARE_WRITE8_MEMBER(cruwrite) { };
DECLARE_WRITE_LINE_MEMBER( speech8_ready );
protected:
virtual void device_start() override;
virtual void device_reset(void) override;
virtual const rom_entry *device_rom_region() const override;
virtual machine_config_constructor device_mconfig_additions() const override;
private:
tms5220_device *m_vsp;
// UINT8 *m_speechrom; // pointer to speech ROM data
// int m_load_pointer; // which 4-bit nibble will be affected by load address
// int m_rombits_count; // current bit position in ROM
// UINT32 m_sprom_address; // 18 bit pointer in ROM
// UINT32 m_sprom_length; // length of data pointed by speechrom_data, from 0 to 2^18
// Ready line to the CPU
devcb_write_line m_ready;
};
#define MCFG_TISPEECH8_ADD(_tag, _conf) \
MCFG_DEVICE_ADD(_tag, TI99_SPEECH8, 0) \
MCFG_DEVICE_CONFIG(_conf)
/*
Main class
*/
class mainboard8_device : public bus8z_device
class mainboard8_device : public device_t
{
public:
mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
template<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object) { return downcast<mainboard8_device &>(device).m_ready.set_callback(object); }
// Memory space
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
DECLARE_SETOFFSET_MEMBER( setoffset );
DECLARE_READ8_MEMBER( readm); // used from address map
DECLARE_WRITE8_MEMBER( writem ); // used from address map
// I/O space
DECLARE_READ8Z_MEMBER( crureadz );
DECLARE_WRITE8_MEMBER( cruwrite );
DECLARE_READ8Z_MEMBER( readz ) override;
DECLARE_WRITE8_MEMBER( write ) override;
// Control lines
DECLARE_WRITE_LINE_MEMBER( clock_in );
DECLARE_WRITE_LINE_MEMBER( dbin_in );
DECLARE_WRITE_LINE_MEMBER( msast_in );
DECLARE_WRITE_LINE_MEMBER( crus_in );
DECLARE_WRITE_LINE_MEMBER( ptgen_in );
DECLARE_WRITE_LINE_MEMBER( reset_console );
DECLARE_WRITE_LINE_MEMBER( hold_cpu );
DECLARE_WRITE_LINE_MEMBER( ggrdy_in );
DECLARE_READ8Z_MEMBER(crureadz);
DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER( holda_line );
void CRUS_set(bool state);
void PTGE_set(bool state);
template<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object)
{
return downcast<mainboard8_device &>(device).m_ready.set_callback(object);
}
void clock_in(int state);
template<class _Object> static devcb_base &set_reset_wr_callback(device_t &device, _Object object)
{
return downcast<mainboard8_device &>(device).m_console_reset.set_callback(object);
}
template<class _Object> static devcb_base &set_hold_wr_callback(device_t &device, _Object object)
{
return downcast<mainboard8_device &>(device).m_hold_line.set_callback(object);
}
void set_paddress(int address);
// Ready lines from GROMs
DECLARE_WRITE_LINE_MEMBER( system_grom_ready );
DECLARE_WRITE_LINE_MEMBER( ptts_grom_ready );
DECLARE_WRITE_LINE_MEMBER( p8_grom_ready );
DECLARE_WRITE_LINE_MEMBER( p3_grom_ready );
DECLARE_WRITE_LINE_MEMBER( sound_ready );
DECLARE_WRITE_LINE_MEMBER( speech_ready );
DECLARE_WRITE_LINE_MEMBER( pbox_ready );
// Emulation
// void set_gromport(gromport_device* dev) { m_gromport = dev; }
protected:
void device_start(void) override;
@ -210,65 +452,115 @@ protected:
machine_config_constructor device_mconfig_additions() const override;
private:
bool access_logical_r(address_space& space, offs_t offset, UINT8 *value, UINT8 mem_mask );
bool access_logical_w(address_space& space, offs_t offset, UINT8 data, UINT8 mem_mask );
void access_physical_r(address_space& space, offs_t offset, UINT8 *value, UINT8 mem_mask );
void access_physical_w(address_space& space, offs_t offset, UINT8 data, UINT8 mem_mask );
void mapwrite(int offset, UINT8 data);
// Propagates the end of the memory cycle
void cycle_end();
// Original logical address.
int m_logical_address;
// Mapped physical address.
int m_physical_address;
// Hold the address space value so that we can use it in other methods.
address_space* m_space;
// Indicates that a byte is waiting on the data bus (see m_latched_data)
bool m_pending_write;
// Hold the value of the data bus. In a real machine, the data bus continues
// to show that value, but in this emulation we have a push mechanism.
UINT8 m_latched_data;
// Hold the level of the GROMCLK line
int m_gromclk;
// Selecting GROM libraries
void select_groms();
// Previous select state
int m_prev_grom;
// Ready states
bool m_speech_ready;
bool m_sound_ready;
bool m_pbox_ready;
// Holds the A14 address line state. We need this for the clock_in method.
bool m_A14_set;
// 99/4A compatibility mode. Name is taken from the spec. If asserted, 99/4A compatibility is active.
line_state m_crus;
// P-Code mode, negative logic. Name is taken from the spec. If asserted, P-Code libraries are available.
// May be read as "Pascal and Text-to-speech GROM libraries ENable"
line_state m_ptgen;
// Keeps the recent DBIN level
line_state m_dbin_level;
// Ready line to the CPU
devcb_write_line m_ready;
// All devices that are attached to the 16-bit address bus.
simple_list<logically_addressed_device> m_logcomp;
// Reset line to the main system
devcb_write_line m_console_reset;
// All devices that are attached to the 24-bit mapped address bus.
simple_list<physically_addressed_device> m_physcomp;
// Select bit for the internal DSR.
bool m_dsr_selected;
// Select bit for the Hexbus DSR.
bool m_hexbus_selected;
// 99/4A compatibility mode. Name is taken from the spec. If 1, 99/4A compatibility is active.
bool m_CRUS;
// P-Code mode. Name is taken from the spec. If 0, P-Code libraries are available.
// May be read as "Pascal and Text-to-speech GROM libraries Enable (Negative)"
// Note: this is negative logic. GROMs are present only for PTGEN=0
// We use PTGE as the inverse signal.
bool m_PTGE;
// Counter for the wait states.
int m_waitcount;
// Address mapper registers. Each offset is selected by the first 4 bits
// of the logical address.
UINT32 m_pas_offset[16];
// SRAM area of the system. Directly connected to the address decoder.
UINT8 *m_sram;
// DRAM area of the system. Connected to the mapped address bus.
UINT8 *m_dram;
// ROM area of the system. Directly connected to the logical address decoder.
UINT8 *m_rom0;
// ROM area of the system. Directly connected to the physical address decoder.
UINT8 *m_rom1;
// P-Code ROM area of the system. Directly connected to the physical address decoder.
UINT8 *m_pcode;
// Hold line to the main system
devcb_write_line m_hold_line;
// Custom chips
required_device<ti998_oso_device> m_oso;
required_device<vaquerro_device> m_vaquerro;
required_device<mofetta_device> m_mofetta;
required_device<amigo_device> m_amigo;
required_device<oso_device> m_oso;
// Debugging
line_state m_last_ready;
// Video processor
tms9118_device* m_video;
// Sound generator
sn76496_base_device* m_sound;
// Speech processor
cd2501ecd_device* m_speech;
// System GROM library
tmc0430_device* m_sgrom[3];
// Text-to-speech GROM library
tmc0430_device* m_tsgrom[8];
// Pascal 8 GROM library
tmc0430_device* m_p8grom[8];
// Pascal 3 GROM library
tmc0430_device* m_p3grom[3];
// Gromport (cartridge port)
gromport_device* m_gromport;
// Peripheral box
peribox_device* m_peb;
// Memory
std::unique_ptr<UINT8[]> m_sram;
std::unique_ptr<UINT8[]> m_dram;
// ROM area of the system.
UINT8* m_rom0;
UINT8* m_rom1;
UINT8* m_pascalrom;
};
#define MCFG_MAINBOARD8_READY_CALLBACK(_write) \
devcb = &mainboard8_device::set_ready_wr_callback(*device, DEVCB_##_write);
#define MCFG_MAINBOARD8_RESET_CALLBACK(_write) \
devcb = &mainboard8_device::set_reset_wr_callback(*device, DEVCB_##_write);
#define MCFG_MAINBOARD8_HOLD_CALLBACK(_write) \
devcb = &mainboard8_device::set_hold_wr_callback(*device, DEVCB_##_write);
#define MCFG_MAINBOARD8_ADD(_tag, _devices) \
MCFG_DEVICE_ADD(_tag, MAINBOARD8, 0) \
MCFG_DEVICE_CONFIG( _devices )
#endif

View File

@ -75,9 +75,20 @@
Constructor
*/
ti99_datamux_device::ti99_datamux_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, DATAMUX, "Databus multiplexer", tag, owner, clock, "ti99_datamux", __FILE__), m_spacep(nullptr),
m_ready(*this), m_muxready(), m_sysready(), m_addr_buf(0), m_read_mode(false), m_latch(0), m_waitcount(0), m_ram16b(nullptr), m_use32k(false), m_base32k(0), m_cpu(nullptr)
{ }
: device_t(mconfig, DATAMUX, "Databus multiplexer", tag, owner, clock, "ti99_datamux", __FILE__),
m_spacep(nullptr),
m_ready(*this),
m_addr_buf(0),
m_dbin(CLEAR_LINE),
m_muxready(CLEAR_LINE),
m_sysready(CLEAR_LINE),
m_latch(0),
m_waitcount(0),
m_ram16b(nullptr),
m_use32k(false),
m_base32k(0),
m_console_groms_present(false)
{ }
#define TRACE_READY 0
#define TRACE_ACCESS 0
@ -89,140 +100,236 @@ ti99_datamux_device::ti99_datamux_device(const machine_config &mconfig, const ch
DEVICE ACCESSOR FUNCTIONS
***************************************************************************/
void ti99_datamux_device::read_all(address_space& space, UINT16 addr, UINT8 *target)
void ti99_datamux_device::read_all(address_space& space, UINT16 addr, UINT8 *value)
{
attached_device *dev = m_devices.first();
// Valid access
bool validaccess = ((addr & 0x0400)==0);
// Reading the odd address first (addr+1)
while (dev != nullptr)
if (validaccess)
{
if (dev->m_config->write_select != 0xffff) // write-only
// GROM access
if ((addr & 0xf801)==0x9800)
{
if ((addr & dev->m_config->address_mask)==dev->m_config->select)
if (m_console_groms_present)
{
// Cast to the bus8z_device (see ti99defs.h)
bus8z_device *devz = static_cast<bus8z_device *>(dev->m_device);
devz->readz(space, addr, target);
for (int i=0; i < 3; i++)
{
m_grom[i]->readz(space, addr, value);
}
}
// hope we don't have two devices answering...
// consider something like a logical OR and maybe some artificial smoke
// GROMport (GROMs)
m_gromport->readz(space, addr, value);
}
dev = dev->m_next;
// Video
if ((addr & 0xf801)==0x8800) m_video->readz(space, addr, value);
}
// GROMport (ROMs)
if ((addr & 0xe000)==0x6000) m_gromport->readz(space, addr, value);
// PEB gets all accesses
m_peb->readz(space, addr, value);
m_peb->memen_in(CLEAR_LINE);
}
void ti99_datamux_device::write_all(address_space& space, UINT16 addr, UINT8 value)
{
attached_device *dev = m_devices.first();
while (dev != nullptr)
// GROM access
if ((addr & 0xf801)==0x9800)
{
if ((addr & dev->m_config->address_mask)==(dev->m_config->select | dev->m_config->write_select))
if (m_console_groms_present)
{
bus8z_device *devz = static_cast<bus8z_device *>(dev->m_device);
devz->write(space, addr, value);
for (int i=0; i < 3; i++)
m_grom[i]->write(space, addr, value);
}
dev = dev->m_next;
// GROMport
m_gromport->write(space, addr, value);
}
// Other devices
if ((addr & 0xe000)==0x6000) m_gromport->write(space, addr, value);
if ((addr & 0xfc01)==0x8400) m_sound->write(space, addr, value);
if ((addr & 0xf801)==0x8800) m_video->write(space, addr, value);
// PEB gets all accesses
m_peb->write(space, addr, value);
m_peb->memen_in(CLEAR_LINE);
}
void ti99_datamux_device::setaddress_all(address_space& space, UINT16 addr)
{
attached_device *dev = m_devices.first();
while (dev != nullptr)
line_state a14 = ((addr & 2)!=0)? ASSERT_LINE : CLEAR_LINE;
// Valid access = not(DBIN and A5)
bool validaccess = (m_dbin==CLEAR_LINE || (addr & 0x0400)==0);
// GROM access
bool isgrom = ((addr & 0xf801)==0x9800) && validaccess;
// Cartridge ROM
bool iscartrom = ((addr & 0xe000)==0x6000);
// Always deliver to GROM so that the select line may be cleared
int lines = (m_dbin==ASSERT_LINE)? 1 : 0;
if (a14==ASSERT_LINE) lines |= 2;
line_state select = isgrom? ASSERT_LINE : CLEAR_LINE;
if (m_console_groms_present)
for (int i=0; i < 3; i++)
m_grom[i]->set_lines(space, lines, select);
// GROMport (GROMs)
m_gromport->set_gromlines(space, lines, select);
if (validaccess)
{
if ((addr & dev->m_config->address_mask)==(dev->m_config->select | dev->m_config->write_select))
{
bus8z_device *devz = static_cast<bus8z_device *>(dev->m_device);
devz->setaddress_dbin(space, addr, m_read_mode? ASSERT_LINE : CLEAR_LINE);
}
dev = dev->m_next;
// Other devices
if ((addr & 0xfc01)==0x8400) m_sound->setaddress_dbin(space, addr, m_dbin);
if ((addr & 0xf801)==0x8800) m_video->setaddress_dbin(space, addr, m_dbin);
}
// GROMport (ROMs)
m_gromport->romgq_line(iscartrom? ASSERT_LINE : CLEAR_LINE);
// PEB gets all accesses
m_peb->memen_in(ASSERT_LINE);
m_peb->setaddress_dbin(space, addr, m_dbin);
}
/*
Special debugger access; these routines have no influence on the wait
state generation.
Special debugger access. The access is similar to the normal access,
but it bypasses the wait state circuitry. Also, access ports of memory-
mapped devices are excluded because their state would be changed
unpredictably by the debugger access.
*/
UINT16 ti99_datamux_device::debugger_read(address_space& space, UINT16 addr)
{
UINT16 base32k = 0;
UINT8 lval, hval;
UINT16 addrb = addr << 1;
if (m_use32k)
{
if ((addrb & 0xe000)==0x2000) base32k = 0x1000;
if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x4000;
}
if (base32k != 0)
{
return m_ram16b[addr - base32k];
}
UINT16 value = 0;
if ((addrb & 0xe000)==0x0000) value = m_consolerom[(addrb & 0x1fff)>>1];
else
{
lval = hval = 0;
read_all(space, addrb+1, &lval);
read_all(space, addrb, &hval);
return ((hval << 8)&0xff00) | (lval & 0xff);
if ((addrb & 0xfc00)==0x8000) value = m_padram[(addrb & 0x00ff)>>1];
else
{
int base32k = 0;
if (m_use32k)
{
if ((addrb & 0xe000)==0x2000) base32k = 0x2000;
if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000;
}
if (base32k != 0) value = m_ram16b[(addrb-base32k)>>1];
else
{
UINT8 lval = 0;
UINT8 hval = 0;
if ((addr & 0xe000)==0x6000)
{
m_gromport->readz(space, addrb+1, &lval);
m_gromport->readz(space, addrb, &hval);
}
m_peb->memen_in(ASSERT_LINE);
m_peb->readz(space, addrb+1, &lval);
m_peb->readz(space, addrb, &hval);
m_peb->memen_in(CLEAR_LINE);
value = ((hval << 8)&0xff00) | (lval & 0xff);
}
}
}
return value;
}
void ti99_datamux_device::debugger_write(address_space& space, UINT16 addr, UINT16 data)
{
UINT16 base32k = 0;
UINT16 addrb = addr << 1;
if (m_use32k)
{
if ((addrb & 0xe000)==0x2000) base32k = 0x1000;
if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x4000;
}
if (base32k != 0)
{
m_ram16b[addr - base32k] = data;
}
if ((addrb & 0xe000)==0x0000) return;
if ((addrb & 0xfc00)==0x8000) m_padram[(addrb & 0x00ff)>>1] = data;
else
{
write_all(space, addrb+1, data & 0xff);
write_all(space, addrb, (data >> 8) & 0xff);
int base32k = 0;
if (m_use32k)
{
if ((addrb & 0xe000)==0x2000) base32k = 0x2000;
if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000;
}
if (base32k != 0) m_ram16b[(addrb-base32k)>>1] = data;
else
{
if ((addr & 0xe000)==0x6000)
{
m_gromport->write(space, addr+1, data & 0xff);
m_gromport->write(space, addr, (data>>8) & 0xff);
}
m_peb->memen_in(ASSERT_LINE);
m_peb->write(space, addr+1, data & 0xff);
m_peb->write(space, addr, (data>>8) & 0xff);
m_peb->memen_in(CLEAR_LINE);
}
}
}
/*
Read access. We are using two loops because the delay between both
accesses must not occur within the loop. So we have one access on the bus,
a delay, and then the second access (each one with possibly many attached
devices)
a delay, and then the second access.
mem_mask is always ffff on TMS processors (cannot control bus width)
*/
READ16_MEMBER( ti99_datamux_device::read )
{
UINT16 value = 0;
// Care for debugger
if (space.debugger_access())
{
return debugger_read(space, offset);
}
// Looks ugly, but this is close to the real thing. If the 16bit
// memory expansion is installed in the console, and the access hits its
// space, just respond to the memory access and don't bother the
// datamux in any way. In particular, do not make the datamux insert wait
// states.
if (m_base32k != 0)
// Addresses below 0x2000 are ROM (no wait states)
if ((m_addr_buf & 0xe000)==0x0000)
{
UINT16 reply = m_ram16b[offset-m_base32k];
return reply & mem_mask;
value = m_consolerom[(m_addr_buf & 0x1fff)>>1];
}
else
{
// The byte from the odd address has already been read into the latch
// Reading the even address now (addr)
UINT8 hbyte = 0;
read_all(space, m_addr_buf, &hbyte);
if (TRACE_ACCESS) logerror("datamux: read even byte from address %04x -> %02x\n", m_addr_buf, hbyte);
// Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM (no wait states)
if ((m_addr_buf & 0xfc00)==0x8000)
{
value = m_padram[(m_addr_buf & 0x00ff)>>1];
}
else
{
// Looks ugly, but this is close to the real thing. If the 16bit
// memory expansion is installed in the console, and the access hits its
// space, just respond to the memory access and don't bother the
// datamux in any way. In particular, do not make the datamux insert wait
// states.
return ((hbyte<<8) | m_latch) & mem_mask;
if (m_base32k != 0)
{
value = m_ram16b[(m_addr_buf-m_base32k)>>1];
}
else
{
// The byte from the odd address has already been read into the latch
// Reading the even address now (addr)
UINT8 hbyte = 0;
read_all(space, m_addr_buf, &hbyte);
if (TRACE_ACCESS) logerror("Read even byte from address %04x -> %02x\n", m_addr_buf, hbyte);
value = (hbyte<<8) | m_latch;
}
}
}
return value;
}
/*
@ -230,21 +337,29 @@ READ16_MEMBER( ti99_datamux_device::read )
*/
WRITE16_MEMBER( ti99_datamux_device::write )
{
// Addresses below 0x2000 are ROM and should be handled in the address map
// by the ROM entry, but as the write handler for ROM is not mapped, we end up
// here when there are invalid accesses, and this will mess up everything.
if (offset < 0x1000) return;
if (space.debugger_access())
{
debugger_write(space, offset, data);
return;
}
// Addresses below 0x2000 are ROM
if ((m_addr_buf & 0xe000)==0x0000)
{
return;
}
// Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM
if ((m_addr_buf & 0xfc00)==0x8000)
{
m_padram[(m_addr_buf & 0x00ff)>>1] = data;
return;
}
// Handle the internal 32K expansion
if (m_base32k != 0)
{
m_ram16b[offset-m_base32k] = data;
m_ram16b[(m_addr_buf-m_base32k)>>1] = data;
}
else
{
@ -264,21 +379,34 @@ WRITE16_MEMBER( ti99_datamux_device::write )
*/
SETOFFSET_MEMBER( ti99_datamux_device::setoffset )
{
if (TRACE_ADDRESS) logerror("datamux: set address %04x\n", offset << 1);
m_addr_buf = offset << 1;
m_waitcount = 0;
if (TRACE_ADDRESS) logerror("set address %04x\n", m_addr_buf);
if ((m_addr_buf & 0xe000) == 0x0000)
{
return; // console ROM
}
if ((m_addr_buf & 0xfc00) == 0x8000)
{
return; // console RAM
}
// Initialize counter
// 1 cycle for loading into the datamux
// 2 subsequent wait states (LSB)
// 2 subsequent wait states (MSB)
// clock cycle 6 is the nominal follower of the last wait state
m_waitcount = 5;
m_addr_buf = offset << 1;
m_spacep = &space;
m_base32k = 0;
if (m_use32k)
{
if ((m_addr_buf & 0xe000)==0x2000) m_base32k = 0x1000;
if (((m_addr_buf & 0xe000)==0xa000) || ((m_addr_buf & 0xc000)==0xc000)) m_base32k = 0x4000;
if ((m_addr_buf & 0xe000)==0x2000) m_base32k = 0x2000;
if (((m_addr_buf & 0xe000)==0xa000) || ((m_addr_buf & 0xc000)==0xc000)) m_base32k = 0x8000;
}
// Suspend the CPU if not using the 32K
@ -308,13 +436,13 @@ WRITE_LINE_MEMBER( ti99_datamux_device::clock_in )
if (TRACE_READY) logerror("datamux: stalled due to external READY=0\n");
return;
}
if (m_read_mode)
if (m_dbin==ASSERT_LINE)
{
// Reading
if (state==ASSERT_LINE)
{ // raising edge
m_waitcount--;
if (m_waitcount==0)
if (--m_waitcount==0)
{
m_muxready = ASSERT_LINE;
ready_join();
@ -333,8 +461,7 @@ WRITE_LINE_MEMBER( ti99_datamux_device::clock_in )
{
if (state==ASSERT_LINE)
{ // raising edge
m_waitcount--;
if (m_waitcount==0)
if (--m_waitcount==0)
{
m_muxready = ASSERT_LINE;
ready_join();
@ -365,21 +492,31 @@ void ti99_datamux_device::ready_join()
WRITE_LINE_MEMBER( ti99_datamux_device::dbin_in )
{
m_read_mode = (state==ASSERT_LINE);
if (TRACE_ADDRESS) logerror("datamux: data bus in = %d\n", m_read_mode? 1:0 );
m_dbin = (line_state)state;
if (TRACE_ADDRESS) logerror("data bus in = %d\n", (m_dbin==ASSERT_LINE)? 1:0 );
}
WRITE_LINE_MEMBER( ti99_datamux_device::ready_line )
{
if (TRACE_READY)
{
if (state != m_sysready) logerror("datamux: READY line from PBox = %d\n", state);
if (state != m_sysready) logerror("READY line from PBox = %d\n", state);
}
m_sysready = (line_state)state;
// Also propagate to CPU via driver
ready_join();
}
WRITE_LINE_MEMBER( ti99_datamux_device::gromclk_in )
{
// Propagate to the GROMs
if (m_console_groms_present)
{
for (int i=0; i < 3; i++) m_grom[i]->gclock_in(state);
}
m_gromport->gclock_in(state);
}
/***************************************************************************
DEVICE LIFECYCLE FUNCTIONS
***************************************************************************/
@ -398,15 +535,9 @@ void ti99_datamux_device::device_stop(void)
void ti99_datamux_device::device_reset(void)
{
const datamux_config *conf = reinterpret_cast<const datamux_config *>(static_config());
const dmux_device_list_entry *list = conf->devlist;
m_cpu = machine().device("maincpu");
// m_space = &m_cpu->memory().space(AS_PROGRAM);
m_devices.reset(); // clear the list
m_consolerom = (UINT16*)owner()->memregion(CONSOLEROM)->base();
m_use32k = (ioport("RAM")->read()==1);
m_console_groms_present = (ioport("GROMENA")->read()==1);
// better use a region?
if (m_ram16b==nullptr)
@ -414,51 +545,6 @@ void ti99_datamux_device::device_reset(void)
m_ram16b = make_unique_clear<UINT16[]>(32768/2);
}
// Now building the list of active devices at this databus multiplex.
// We allow for turning off devices according to configuration switch settings.
// In particular, the HSGPL card cannot function unless the console GROMs are
// removed.
if ( list != nullptr )
{
bool done = false;
for (int i=0; !done; i++)
{
if (list[i].name == nullptr)
{
done = true;
}
else
{
UINT32 set;
bool active_device = true;
if (list[i].setting!=nullptr)
{
set = ioport(list[i].setting)->read();
active_device = ((set & list[i].set)==list[i].set) && ((set & list[i].unset)==0);
}
if (active_device)
{
device_t *dev = machine().device(list[i].name);
if (dev != nullptr)
{
auto ad = new attached_device(dev, list[i]);
m_devices.append(*ad);
if (TRACE_SETUP) logerror("datamux: Device %s mounted at index %d.\n", list[i].name, i);
}
else
{
if (TRACE_SETUP) logerror("datamux: Device %s not found.\n", list[i].name);
}
}
else
{
if (TRACE_SETUP) logerror("datamux: Device %s not mounted due to configuration setting %s.\n", list[i].name, list[i].setting);
}
}
}
}
if (TRACE_SETUP) logerror("datamux: Device count = %d\n", m_devices.count());
m_sysready = ASSERT_LINE;
m_muxready = ASSERT_LINE;
ready_join();
@ -466,9 +552,22 @@ void ti99_datamux_device::device_reset(void)
m_waitcount = 0;
m_latch = 0;
m_read_mode = true;
m_dbin = CLEAR_LINE;
}
void ti99_datamux_device::device_config_complete()
{
m_video = downcast<bus8z_device*>(owner()->subdevice(VIDEO_SYSTEM_TAG));
m_sound = downcast<ti_sound_sn94624_device*>(owner()->subdevice(TISOUND_TAG));
m_gromport = downcast<gromport_device*>(owner()->subdevice(GROMPORT_TAG));
m_peb = downcast<peribox_device*>(owner()->subdevice(PERIBOX_TAG));
m_grom[0] = downcast<tmc0430_device*>(owner()->subdevice(GROM0_TAG));
m_grom[1] = downcast<tmc0430_device*>(owner()->subdevice(GROM1_TAG));
m_grom[2] = downcast<tmc0430_device*>(owner()->subdevice(GROM2_TAG));
m_padram = make_unique_clear<UINT16[]>(256/2);
}
INPUT_PORTS_START( datamux )
PORT_START( "RAM" ) /* config */
PORT_CONFNAME( 0x01, 0x00, "Console 32 KiB RAM upgrade (16 bit)" )

View File

@ -15,53 +15,13 @@
#define __DMUX__
#include "ti99defs.h"
#include "videowrp.h"
#include "machine/tmc0430.h"
#include "gromport.h"
#include "bus/ti99_peb/peribox.h"
extern const device_type DATAMUX;
/*
Device that is attached to this datamux.
The configuration setting is used for certain configurations
where devices may only be used if others are turned off. In particular,
if the HGSPL expansion card is used, the GROMs in the console must be
removed.
*/
struct dmux_device_list_entry
{
const char *name; // Name of the device (used for looking up the device)
UINT16 select; // State of the address line bits when addressing this device
UINT16 address_mask; // Bits of the address bus used to address this device
UINT16 write_select; // Bits set when doing write accesses to this device (ffff = write-only)
const char *setting; // configuration switch that may have an effect for the presence of this device
UINT8 set; // bits that must be set for this switch so that this device is present
UINT8 unset; // bits that must be reset for this switch so that this device is present
};
#define DMUX_CONFIG(name) \
const datamux_config(name) =
struct datamux_config
{
const dmux_device_list_entry *devlist;
};
/*
Device list of this datamux.
*/
class attached_device
{
friend class simple_list<attached_device>;
friend class ti99_datamux_device;
public:
attached_device(device_t *busdevice, const dmux_device_list_entry &entry)
: m_next(nullptr), m_device(busdevice), m_config(&entry) { };
private:
attached_device *m_next;
device_t *m_device; // the actual device
const dmux_device_list_entry *m_config;
};
/*
Main class
*/
@ -77,6 +37,8 @@ public:
DECLARE_WRITE_LINE_MEMBER( dbin_in );
DECLARE_WRITE_LINE_MEMBER( ready_line );
DECLARE_WRITE_LINE_MEMBER( gromclk_in );
template<class _Object> static devcb_base &static_set_ready_callback(device_t &device, _Object object)
{
return downcast<ti99_datamux_device &>(device).m_ready.set_callback(object);
@ -87,9 +49,22 @@ protected:
void device_start() override;
void device_stop() override;
void device_reset() override;
void device_config_complete() override;
ioport_constructor device_input_ports() const override;
private:
// Link to the video processor
bus8z_device* m_video;
// Link to the sound processor
ti_sound_sn94624_device* m_sound;
// Link to the peripheral expansion box
peribox_device* m_peb;
// Link to the cartridge port (aka GROM port)
gromport_device* m_gromport;
// Keeps the address space pointer
address_space* m_spacep;
@ -112,21 +87,18 @@ private:
// Ready line to the CPU
devcb_write_line m_ready;
// Address latch (emu). In reality, the address bus remains constant.
UINT16 m_addr_buf;
// DBIN line
line_state m_dbin;
// Own ready state.
line_state m_muxready;
// Ready state. Needed to control wait state generation via inbound READY
line_state m_sysready;
/* Address latch (emu). In reality, the address bus remains constant. */
UINT16 m_addr_buf;
/* Stores the state of the DBIN line. */
bool m_read_mode;
/* All devices that are attached to the 8-bit bus. */
simple_list<attached_device> m_devices;
/* Latch which stores the first (odd) byte */
UINT8 m_latch;
@ -136,22 +108,28 @@ private:
/* Memory expansion (internal, 16 bit). */
std::unique_ptr<UINT16[]> m_ram16b;
// Console RAM
std::unique_ptr<UINT16[]> m_padram;
// Console ROM
UINT16* m_consolerom;
// Console GROMs
tmc0430_device* m_grom[3];
/* Use the memory expansion? */
bool m_use32k;
/* Memory base for piggy-back 32K expansion. If 0, expansion is not used. */
UINT16 m_base32k;
/* Reference to the CPU; avoid lookups. */
device_t *m_cpu;
// Console GROMs are available (the HSGPL expects them to be removed)
bool m_console_groms_present;
};
/******************************************************************************/
#define MCFG_DMUX_ADD(_tag, _devices) \
MCFG_DEVICE_ADD(_tag, DATAMUX, 0) \
MCFG_DEVICE_CONFIG( _devices )
#endif
#define MCFG_DMUX_READY_HANDLER( _intcallb ) \
devcb = &ti99_datamux_device::static_set_ready_callback( *device, DEVCB_##_intcallb );
#endif

View File

@ -535,6 +535,7 @@ READ8_MEMBER( geneve_mapper_device::readm )
// 1001 0000 0000 0000
// We need to add the address prefix bits
m_peribox->readz(space, dec->offset, &value, 0xff);
m_peribox->memen_in(CLEAR_LINE);
if (TRACE_READ) logerror("%s: Read speech -> %02x\n", tag(), value);
break;
@ -594,6 +595,7 @@ READ8_MEMBER( geneve_mapper_device::readm )
// 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15)
m_peribox->readz(space, dec->physaddr, &value, 0xff);
m_peribox->memen_in(CLEAR_LINE);
if (TRACE_READ) logerror("%s: Read P-Box %04x (%06x) -> %02x\n", tag(), dec->offset, dec->physaddr, value);
break;
@ -616,6 +618,7 @@ READ8_MEMBER( geneve_mapper_device::readm )
case MPGMBOX:
// Route everything else to the P-Box
m_peribox->readz(space, dec->physaddr, &value, 0xff);
m_peribox->memen_in(CLEAR_LINE);
break;
}
return value;
@ -701,6 +704,7 @@ WRITE8_MEMBER( geneve_mapper_device::writem )
// 1001 0100 0000 0000
// We need to add the address prefix bits
m_peribox->write(space, dec->offset, data, 0xff);
m_peribox->memen_in(CLEAR_LINE);
if (TRACE_WRITE) logerror("%s: Write speech <- %02x\n", tag(), data);
break;
@ -756,6 +760,7 @@ WRITE8_MEMBER( geneve_mapper_device::writem )
dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address
if (TRACE_WRITE) logerror("%s: Write P-Box %04x (%06x) <- %02x\n", tag(), offset, dec->physaddr, data);
m_peribox->write(space, dec->physaddr, data, 0xff);
m_peribox->memen_in(CLEAR_LINE);
break;
case MPGMDRAM:
@ -775,6 +780,7 @@ WRITE8_MEMBER( geneve_mapper_device::writem )
case MPGMBOX:
// Route everything else to the P-Box
m_peribox->write(space, dec->physaddr, data, 0xff);
m_peribox->memen_in(CLEAR_LINE);
break;
}
}
@ -875,6 +881,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read
// We need to add the address prefix bits
dec->function = MLTSPEECH;
dec->offset = offset | ((m_genmod)? 0x170000 : 0x070000);
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, dec->offset, read_mode);
set_wait(1);
return;
@ -954,6 +961,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read
dec->function = MPGBOX;
dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address (with AMA..AMC)
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
return;
}
@ -986,6 +994,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read
// Check: Are waitstates completely turned off for turbo mode, or
// merely the waitstates for DRAM memory access and box access?
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
return;
}
@ -1072,6 +1081,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read
{
dec->function = MLTSPEECH;
dec->offset = dec->offset | ((m_genmod)? 0x170000 : 0x070000);
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, dec->offset, read_mode);
set_wait(1);
return;
@ -1151,6 +1161,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read
// only AMA, AMB, AMC are used; AMD and AME are not used
dec->function = MPGBOX;
dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
set_wait(1);
}
@ -1175,6 +1186,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read
// Route everything else to the P-Box
dec->function = MPGMBOX;
dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
if (!m_turbo) set_wait(1);
}
@ -1346,7 +1358,7 @@ WRITE_LINE_MEMBER( geneve_mapper_device::pfm_output_enable )
void geneve_mapper_device::device_start()
{
// Get pointers
m_peribox = machine().device<bus8z_device>(PERIBOX_TAG);
m_peribox = machine().device<peribox_device>(PERIBOX_TAG);
m_keyboard = machine().device<geneve_keyboard_device>(GKEYBOARD_TAG);
m_video = machine().device<bus8z_device>(VIDEO_SYSTEM_TAG);
m_sound = machine().device<bus8z_device>(TISOUND_TAG);

View File

@ -19,6 +19,7 @@
#include "video/v9938.h"
#include "cpu/tms9900/tms9995.h"
#include "machine/at29x.h"
#include "bus/ti99_peb/peribox.h"
extern const device_type GENEVE_MOUSE;
extern const device_type GENEVE_KEYBOARD;
@ -207,7 +208,7 @@ private:
geneve_keyboard_device* m_keyboard;
bus8z_device* m_video;
bus8z_device* m_peribox;
peribox_device* m_peribox;
bus8z_device* m_sound;
UINT8* m_eprom;
UINT8* m_sram;

View File

@ -1,269 +0,0 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
GROM emulation (aka TMC0430)
+----+--+----+
AD7 |1 G 16| Vss
AD6 |2 R 15| GR
AD5 |3 O 14| Vdd
AD4 |4 M 13| GRC
AD3 |5 12| M
AD2 |6 11| MO
AD1 |7 10| GS*
AD0 |8 9| Vcc
+------------+
GR = GROM Ready. Should be connected to processor's READY/HOLD*.
GRC = GROM clock. Typically in the range of 400-500 kHz.
M = Direction. 1=read, 0=write
MO = Mode. 1=address counter access, 0=data access
GS* = GROM select. 0=select, 1=deselect
GROMs are slow ROM devices, which are
interfaced via a 8-bit data bus, and include an internal address pointer
which is incremented after each read. This implies that accesses are
faster when reading consecutive bytes, although the address pointer can be
read and written at any time.
GROMs are generally used to store programs written in GPL (Graphic Programming
Language): a proprietary, interpreted language. The GPL interpreter takes
most space of the TI-99/4A system ROMs.
Both TI-99/4 and TI-99/4A include three GROMs, with some start-up code,
system routines and TI-Basic. TI99/4 includes an additional Equation
Editor. According to the preliminary schematics found on ftp.whtech.com,
TI-99/8 includes the three standard GROMs and 16 GROMs for the UCSD
p-system. TI99/2 does not include GROMs at all, and was not designed to
support any, although it should be relatively easy to create an expansion
card with the GPL interpreter and a /4a cartridge port.
Communication with GROM is done by writing and reading data over the
AD0-AD7 lines. Within the TI-99 systems, the address bus is decoded for
the M, GS*, and MO lines: Writing a byte to address 9c02 asserts the GS* and
MO line, and clears the M line, which means the transmitted byte is put into
the internal address register. Two bytes must be written to set up the
complete address.
It was obviously planned to offer GRAM circuits as well, since the
programming manuals refer to writing to a special address, clearing the MO
line. Although the TI-99 systems reserve a port in the memory space, no one
has ever seen a GRAM circuit in the wild. However, third-party products like
HSGPL or GRAM Kracker simulate GRAMs using conventional RAM with some
addressing circuitry, usually in a custom chip.
Each GROM is logically 8 KiB long. Original TI-built GROM are 6 KiB long;
the extra 2kb can be read, and follow the following formula:
GROM[0x1800+offset] = GROM[0x0800+offset] | GROM[0x1000+offset];
(sounds like address decoding is incomplete - we are lucky we don't burn
any silicon when doing so... Needless to say, some hackers simulated 8kb
GRAMs and GROMs with normal RAM/PROM chips and glue logic.)
The address pointer is incremented after each GROM operation, but it will
always remain within the bounds of the currently selected GROM (e.g. after
0x3fff comes 0x2000).
Since address are 16-bit long, you can have up to 8 GROMs. Accordingly,
a cartridge may include up to 5 GROMs.
Every GROM has an internal ID which represents the high-order three
address bits. The address counter can be set to any value from 0
to 0xffff; the GROM will only react when selected and when the current
address counter's high-order bits match the ID of the chip.
Example: When the ID is 6, the GROM will react when the address
counter contains a value from 0xc000 to 0xdfff.
CHECK: Reading the address increases the counter only once. The first access
returns the MSB, the second (and all following accesses) return the LSB.
Michael Zapf, August 2010
January 2012: rewritten as class
***************************************************************************/
#include "emu.h"
#include "grom.h"
#define TRACE_ADDRESS 0
/*
Constructor.
*/
ti99_grom_device::ti99_grom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: bus8z_device(mconfig, GROM, "TI-99 GROM device", tag, owner, clock, "ti99_grom", __FILE__), m_writable(false), m_ident(0), m_size(0),
m_gromready(*this), m_clockrate(0), m_address(0), m_buffer(0), m_raddr_LSB(false), m_waddr_LSB(false), m_memptr(nullptr), m_timer(nullptr)
{
}
/*
Reading from the chip. Represents an access with M=1, GS*=0. The MO bit is
defined by the offset (0 or 1). This is the enhanced read function with
Z state.
*/
READ8Z_MEMBER( ti99_grom_device::readz )
{
// Prevent debugger access
if (space.debugger_access()) return;
if (offset & 2)
{
// GROMs generally answer the address read request
// (important if GROM simulators do not serve the request but rely on
// the console GROMs) so we don't check the ident
/* When reading, reset the hi/lo flag byte for writing. */
/* TODO: Verify this with a real machine. */
m_waddr_LSB = false;
/* Address reading is done in two steps; first, the high byte */
/* is transferred, then the low byte. */
if (m_raddr_LSB)
{
/* second pass */
*value = m_address & 0x00ff;
m_raddr_LSB = false;
}
else
{
/* first pass */
*value = (m_address & 0xff00)>>8;
m_raddr_LSB = true;
}
}
else
{
if (((m_address >> 13)&0x07)==m_ident)
{
// GROMs are buffered. Data is retrieved from a buffer,
// while the buffer is replaced with the next cell content.
if (TRACE_ADDRESS) if (m_ident==0) logerror("grom0: %04x = %02x\n", m_address-1, m_buffer);
*value = m_buffer;
// Get next value, put it in buffer. Note that the GROM
// wraps at 8K boundaries.
UINT16 addr = m_address-(m_ident<<13);
if (m_size == 0x1800 && ((m_address&0x1fff)>=0x1800))
m_buffer = m_memptr[addr-0x1000] | m_memptr[addr-0x0800];
else
m_buffer = m_memptr[addr];
}
// Note that all GROMs update their address counter.
// TODO: Check this on a real console
m_address = (m_address & 0xE000) | ((m_address + 1)&0x1FFF);
// Reset the read and write address flipflops.
m_raddr_LSB = m_waddr_LSB = false;
// Maybe the timer is also required for address reading/setting, but
// we don't have such technical details on GROMs.
clear_ready();
}
}
/*
Writing to the chip. Represents an access with M=0, GS*=0. The MO bit is
defined by the offset (0 or 1).
*/
WRITE8_MEMBER( ti99_grom_device::write )
{
// Prevent debugger access
if (space.debugger_access()) return;
if (offset & 2)
{
/* write GROM address */
/* see comments above */
m_raddr_LSB = false;
/* Implements the internal flipflop. */
/* The Editor/Assembler manuals says that the current address */
/* plus one is returned. This effect is properly emulated */
/* by using a read-ahead buffer. */
if (m_waddr_LSB)
{
/* Accept low byte (2nd write) */
m_address = (m_address & 0xFF00) | data;
/* Setting the address causes a new prefetch */
if (is_selected())
{
m_buffer = m_memptr[m_address-(m_ident<<13)];
}
m_waddr_LSB = false;
if (TRACE_ADDRESS) if (m_ident==0) logerror("grom0: %04x\n", m_address);
}
else
{
/* Accept high byte (1st write). Do not advance the address conter. */
m_address = (data << 8) | (m_address & 0xFF);
m_waddr_LSB = true;
return;
}
}
else
{
/* write GRAM data */
if ((((m_address >> 13)&0x07)==m_ident) && m_writable)
{
UINT16 write_addr;
// We need to rewind by 1 because the read address has already advanced.
// However, do not change the address counter!
write_addr = (m_address & 0xE000) | ((m_address - 1)&0x1FFF);
// UINT16 addr = m_address-(m_ident<<13);
if (m_size > 0x1800 || ((m_address&0x1fff)<0x1800))
m_memptr[write_addr-(m_ident<<13)] = data;
}
m_raddr_LSB = m_waddr_LSB = false;
clear_ready();
}
m_address = (m_address & 0xE000) | ((m_address + 1)&0x1FFF);
}
/*
Timing. We assume that each data read results in READY going down for
one cycle at the given frequency.
*/
void ti99_grom_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
m_gromready(ASSERT_LINE);
}
void ti99_grom_device::clear_ready()
{
m_gromready(CLEAR_LINE);
m_timer->adjust(attotime::from_hz(m_clockrate));
}
/***************************************************************************
DEVICE FUNCTIONS
***************************************************************************/
void ti99_grom_device::device_start(void)
{
const ti99grom_config *conf = reinterpret_cast<const ti99grom_config *>(static_config());
m_memptr = owner()->memregion(conf->regionname)->base();
assert (m_memptr!=nullptr);
m_memptr += conf->offset_reg;
m_size = conf->size;
m_clockrate = conf->clockrate;
m_writable = conf->writable;
m_ident = conf->ident;
m_gromready.resolve_safe();
m_timer = timer_alloc(0);
}
void ti99_grom_device::device_reset(void)
{
m_address = 0;
m_raddr_LSB = false;
m_waddr_LSB = false;
m_buffer = 0;
}
const device_type GROM = &device_creator<ti99_grom_device>;

View File

@ -1,108 +0,0 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
GROM emulation
See grom.c for documentation,
Michael Zapf
February 2012: Rewritten as class
***************************************************************************/
#ifndef __TI99GROM__
#define __TI99GROM__
#include "ti99defs.h"
struct ti99grom_config
{
bool writable;
int ident;
const char *regionname;
offs_t offset_reg;
int size;
int clockrate;
};
#define GROM_CONFIG(name) \
const ti99grom_config(name) =
#define MCFG_GROM_READY_CALLBACK(_write) \
devcb = &ti99_grom_device::set_ready_wr_callback(*device, DEVCB_##_write);
extern const device_type GROM;
/*
ti99_grom. For bus8z_device see ti99defs.h
*/
class ti99_grom_device : public bus8z_device, ti99grom_config
{
public:
ti99_grom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
template<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object) { return downcast<ti99_grom_device &>(device).m_gromready.set_callback(object); }
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
private:
// Is this a GRAM (never seen actually, but obviously planned)
bool m_writable;
// Identification of this GROM (0-7)
int m_ident;
// If the GROM has only 6 KiB, the remaining 2 KiB are filled with a
// specific byte pattern which is created by a logical OR of lower
// regions
int m_size;
// Ready callback. This line is usually connected to the READY pin of the CPU.
devcb_write_line m_gromready;
// Frequency of the incoming GROM clock. In most application cases the
// GROM gets its clock from the video display processor (TMS9918)
int m_clockrate;
/* Address pointer. */
// This value is always expected to be in the range 0x0000 - 0xffff, even
// when this GROM is not addressed.
UINT16 m_address;
/* GROM data buffer. */
UINT8 m_buffer;
/* Internal flip-flop. Used when retrieving the address counter. */
bool m_raddr_LSB;
/* Internal flip-flops. Used when writing the address counter.*/
bool m_waddr_LSB;
/* Pointer to the memory region contained in this GROM. */
UINT8 *m_memptr;
// Timer for READY line operation
emu_timer *m_timer;
/* Indicates whether this device will react on the next read/write data access. */
inline int is_selected()
{
return (((m_address >> 13)&0x07)==m_ident);
}
// Calling this method causes the READY line to be cleared, which puts the
// CPU into wait state mode. A timer is set to raise READY again.
void clear_ready();
virtual void device_start(void) override;
virtual void device_reset(void) override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
};
#define MCFG_GROM_ADD(_tag, _config) \
MCFG_DEVICE_ADD(_tag, GROM, 0) \
MCFG_DEVICE_CONFIG(_config)
#endif

View File

@ -40,7 +40,7 @@
This means that a maximum of 8 KiB of direct memory space can be accessed.
The /GS line is used to enable GROM circuits on the board (serial ROMs with
own address counter, see grom.h).
own address counter, see tmc0430.h).
When a cartridge is inserted the /RESET line is pulled to ground, which
via a R/C component pulls down the /RESET input of the timer circuit for
@ -119,6 +119,7 @@
#define TRACE_ILLWRITE 0
#define TRACE_CONFIG 0
#define TRACE_READ 0
#define TRACE_WRITE 0
#define TRACE_GROM 0
#define TRACE_GKRACKER 0
#define TRACE_CRU 0
@ -186,20 +187,33 @@ gromport_device::gromport_device(const machine_config &mconfig, const char *tag,
m_connector(nullptr),
m_reset_on_insert(true),
m_console_ready(*this),
m_console_reset(*this), m_grombase(0), m_grommask(0)
m_console_reset(*this) // , m_grombase(0), m_grommask(0)
{ }
/* Only called for addresses 6000-7fff and GROM addresses (see datamux config) */
/*
Reading via the GROM port. Only 13 address lines are passed through
on the TI-99/4A, and 14 lines on the TI-99/8.
*/
READ8Z_MEMBER(gromport_device::readz)
{
if (m_connector != nullptr)
m_connector->readz(space, offset, value);
{
m_connector->readz(space, offset & m_mask, value);
if (TRACE_READ) if (m_romgq) logerror("Read %04x -> %02x\n", offset, *value);
}
}
/*
Writing via the GROM port. Only 13 address lines are passed through
on the TI-99/4A, and 14 lines on the TI-99/8.
*/
WRITE8_MEMBER(gromport_device::write)
{
if (m_connector != nullptr)
m_connector->write(space, offset, data);
{
if (TRACE_WRITE) if (m_romgq) logerror("Write %04x <- %02x\n", offset, data);
m_connector->write(space, offset & m_mask, data);
}
}
READ8Z_MEMBER(gromport_device::crureadz)
@ -219,6 +233,31 @@ WRITE_LINE_MEMBER(gromport_device::ready_line)
m_console_ready(state);
}
/*
Asserted when the console addresses cartridge rom.
*/
WRITE_LINE_MEMBER(gromport_device::romgq_line)
{
m_romgq = state;
if (m_connector != nullptr)
m_connector->romgq_line(state);
}
WRITE_LINE_MEMBER(gromport_device::gclock_in)
{
if (m_connector != nullptr)
m_connector->gclock_in(state);
}
/*
Combined GROM control lines.
*/
WRITE8_MEMBER( gromport_device::set_gromlines )
{
if (m_connector != nullptr)
m_connector->set_gromlines(space, offset, data);
}
void gromport_device::device_start()
{
m_console_ready.resolve();
@ -230,11 +269,11 @@ void gromport_device::device_reset()
m_reset_on_insert = (ioport("CARTRESET")->read()==0x01);
}
void gromport_device::set_grom_base(UINT16 grombase, UINT16 grommask)
{
m_grombase = grombase;
m_grommask = grommask;
}
// void gromport_device::set_grom_base(UINT16 grombase, UINT16 grommask)
// {
// m_grombase = grombase;
// m_grommask = grommask;
// }
/*
Shall we reset the console when a cartridge has been inserted?
@ -255,15 +294,18 @@ void gromport_device::cartridge_inserted()
void gromport_device::device_config_complete()
{
m_connector = static_cast<ti99_cartridge_connector_device*>(subdevices().first());
set_grom_base(0x9800, 0xf800);
}
SLOT_INTERFACE_START( gromport )
SLOT_INTERFACE_START( gromport4 )
SLOT_INTERFACE("single", GROMPORT_SINGLE)
SLOT_INTERFACE("multi", GROMPORT_MULTI)
SLOT_INTERFACE("gkracker", GROMPORT_GK)
SLOT_INTERFACE_END
SLOT_INTERFACE_START( gromport8 )
SLOT_INTERFACE("single", GROMPORT_SINGLE)
SLOT_INTERFACE("multi", GROMPORT_MULTI)
SLOT_INTERFACE_END
INPUT_PORTS_START(gromport)
PORT_START( "CARTRESET" )
@ -310,16 +352,6 @@ void ti99_cartridge_connector_device::device_config_complete()
m_gromport = static_cast<gromport_device*>(owner());
}
UINT16 ti99_cartridge_connector_device::grom_base()
{
return m_gromport->get_grom_base();
}
UINT16 ti99_cartridge_connector_device::grom_mask()
{
return m_gromport->get_grom_mask();
}
single_conn_device::single_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: ti99_cartridge_connector_device(mconfig, GROMPORT_SINGLE, "Standard cartridge connector", tag, owner, clock, "single", __FILE__),
m_cartridge(nullptr)
@ -350,6 +382,28 @@ WRITE8_MEMBER(single_conn_device::cruwrite)
m_cartridge->cruwrite(space, offset, data);
}
WRITE_LINE_MEMBER(single_conn_device::romgq_line)
{
// Pass through
m_cartridge->romgq_line(state);
}
/*
Combined select lines
*/
WRITE8_MEMBER(single_conn_device::set_gromlines)
{
// Pass through
m_cartridge->set_gromlines(space, offset, data);
}
WRITE_LINE_MEMBER(single_conn_device::gclock_in)
{
// Pass through
m_cartridge->gclock_in(state);
}
void single_conn_device::device_start()
{
m_cartridge = static_cast<ti99_cartridge_device*>(subdevices().first());
@ -475,10 +529,8 @@ int multi_conn_device::get_active_slot(bool changebase, offs_t offset)
int slot;
if (changebase)
{
if ((offset & grom_mask()) == grom_base())
{
set_slot((offset>>2) & 0x00ff);
}
// GROM selected?
if (m_gsel != 0) set_slot((offset>>2) & 0x00ff);
}
slot = m_active_slot;
return slot;
@ -497,6 +549,50 @@ void multi_conn_device::remove(int index)
m_cartridge[index] = nullptr;
}
WRITE_LINE_MEMBER(multi_conn_device::romgq_line)
{
m_readrom = state;
// Propagate to all slots
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
m_cartridge[i]->romgq_line(state);
}
}
}
/*
Combined select lines
*/
WRITE8_MEMBER(multi_conn_device::set_gromlines)
{
// GROM selected?
m_gsel = data;
// Propagate to all slots
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
m_cartridge[i]->set_gromlines(space, offset, data);
}
}
}
WRITE_LINE_MEMBER(multi_conn_device::gclock_in)
{
// Propagate to all slots
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
m_cartridge[i]->gclock_in(state);
}
}
}
READ8Z_MEMBER(multi_conn_device::readz)
{
int slot = get_active_slot(true, offset);
@ -504,14 +600,14 @@ READ8Z_MEMBER(multi_conn_device::readz)
// If we have a GROM access, we need to send the read request to all
// attached cartridges so the slot is irrelevant here. Each GROM
// contains an internal address counter, and we must make sure they all stay in sync.
if ((offset & grom_mask()) == grom_base())
if (m_gsel != 0)
{
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
UINT8 newval = *value;
m_cartridge[i]->readz(space, offset, &newval, mem_mask);
m_cartridge[i]->readz(space, offset, &newval, 0xff);
if (i==slot)
{
*value = newval;
@ -523,33 +619,32 @@ READ8Z_MEMBER(multi_conn_device::readz)
{
if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr)
{
m_cartridge[slot]->readz(space, offset, value, mem_mask);
m_cartridge[slot]->readz(space, offset, value, 0xff);
}
}
}
WRITE8_MEMBER(multi_conn_device::write)
{
int slot = get_active_slot(true, offset);
// Same issue as above (read)
// We don't have GRAM cartridges, anyway, so it's just used for setting the address.
if ((offset & grom_mask()) == grom_base())
if (m_gsel != 0)
{
for (auto & elem : m_cartridge)
{
if (elem != nullptr)
{
elem->write(space, offset, data, mem_mask);
elem->write(space, offset, data, 0xff);
}
}
}
else
{
int slot = get_active_slot(true, offset);
if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr)
{
// logerror("%s: try it on slot %d\n", tag(), slot);
m_cartridge[slot]->write(space, offset, data, mem_mask);
// logerror("writing %04x (slot %d) <- %02x\n", offset, slot, data);
m_cartridge[slot]->write(space, offset, data, 0xff);
}
}
}
@ -595,6 +690,7 @@ void multi_conn_device::device_reset(void)
{
m_active_slot = 0;
m_fixed_slot = ioport("CARTSLOT")->read() - 1;
m_gsel = 0;
}
static MACHINE_CONFIG_FRAGMENT( multi_slot )
@ -751,9 +847,40 @@ gkracker_device::gkracker_device(const machine_config &mconfig, const char *tag,
{
}
WRITE_LINE_MEMBER(gkracker_device::romgq_line)
{
if (m_cartridge != nullptr)
{
// Propagate to the guest
m_cartridge->romgq_line(state);
}
}
/*
Combined select lines
*/
WRITE8_MEMBER(gkracker_device::set_gromlines)
{
m_gsel = data;
if (m_cartridge != nullptr)
{
// Propagate to the guest
m_cartridge->set_gromlines(space, offset, data);
}
}
WRITE_LINE_MEMBER(gkracker_device::gclock_in)
{
if (m_cartridge != nullptr)
{
// Propagate to the guest
m_cartridge->gclock_in(state);
}
}
READ8Z_MEMBER(gkracker_device::readz)
{
if ((offset & grom_mask()) == grom_base())
if (m_gsel != 0)
{
// Reads from the GRAM space of the GRAM Kracker.
@ -863,7 +990,7 @@ WRITE8_MEMBER(gkracker_device::write)
m_cartridge->write(space, offset, data, mem_mask);
}
if ((offset & grom_mask()) == grom_base())
if (m_gsel != 0)
{
// Write to the GRAM space of the GRAM Kracker.
if ((offset & 0x0002)==0x0002)
@ -1056,6 +1183,7 @@ void gkracker_device::device_reset()
m_grom_address = 0; // for the GROM emulation
m_ram_page = 0;
m_waddr_LSB = false;
m_gsel = 0;
}
static MACHINE_CONFIG_FRAGMENT( gkracker_slot )
@ -1270,15 +1398,15 @@ bool ti99_cartridge_device::has_grom()
return m_pcb->m_grom_size>0;
}
UINT16 ti99_cartridge_device::grom_base()
{
return m_connector->grom_base();
}
UINT16 ti99_cartridge_device::grom_mask()
{
return m_connector->grom_mask();
}
// UINT16 ti99_cartridge_device::grom_base()
// {
// return m_connector->grom_base();
// }
//
// UINT16 ti99_cartridge_device::grom_mask()
// {
// return m_connector->grom_mask();
// }
bool ti99_cartridge_device::call_load()
{
@ -1401,12 +1529,14 @@ bool ti99_cartridge_device::call_softlist_load(software_list_device &swlist, con
READ8Z_MEMBER(ti99_cartridge_device::readz)
{
if (m_pcb != nullptr) m_pcb->readz(space, offset, value);
if (m_pcb != nullptr)
m_pcb->readz(space, offset, value);
}
WRITE8_MEMBER(ti99_cartridge_device::write)
{
if (m_pcb != nullptr) m_pcb->write(space, offset, data);
if (m_pcb != nullptr)
m_pcb->write(space, offset, data);
}
READ8Z_MEMBER(ti99_cartridge_device::crureadz)
@ -1424,6 +1554,28 @@ WRITE_LINE_MEMBER( ti99_cartridge_device::ready_line )
m_connector->ready_line(state);
}
WRITE_LINE_MEMBER( ti99_cartridge_device::romgq_line )
{
if (m_pcb != nullptr)
{
m_pcb->romgq_line(state);
m_readrom = state;
}
}
/*
Combined select lines
*/
WRITE8_MEMBER(ti99_cartridge_device::set_gromlines)
{
if (m_pcb != nullptr) m_pcb->set_gromlines(space, offset, data);
}
WRITE_LINE_MEMBER(ti99_cartridge_device::gclock_in)
{
if (m_pcb != nullptr) m_pcb->gclock_in(state);
}
void ti99_cartridge_device::device_config_complete()
{
update_names();
@ -1431,41 +1583,15 @@ void ti99_cartridge_device::device_config_complete()
m_connector = static_cast<ti99_cartridge_connector_device*>(owner());
}
static GROM_CONFIG(grom3_config)
{
false, 3, CARTGROM_TAG, 0x0000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom4_config)
{
false, 4, CARTGROM_TAG, 0x2000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom5_config)
{
false, 5, CARTGROM_TAG, 0x4000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom6_config)
{
false, 6, CARTGROM_TAG, 0x6000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom7_config)
{
false, 7, CARTGROM_TAG, 0x8000, 0x1800, GROMFREQ
};
/*
5 GROMs that may be contained in a cartridge
*/
static MACHINE_CONFIG_FRAGMENT( ti99_cartridge )
MCFG_GROM_ADD( GROM3_TAG, grom3_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM4_TAG, grom4_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM5_TAG, grom5_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM6_TAG, grom6_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM7_TAG, grom7_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM3_TAG, 3, CARTGROM_TAG, 0x0000, WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM4_TAG, 4, CARTGROM_TAG, 0x2000, WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM5_TAG, 5, CARTGROM_TAG, 0x4000, WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM6_TAG, 6, CARTGROM_TAG, 0x6000, WRITELINE(ti99_cartridge_device, ready_line))
MCFG_GROM_ADD( GROM7_TAG, 7, CARTGROM_TAG, 0x8000, WRITELINE(ti99_cartridge_device, ready_line))
MACHINE_CONFIG_END
machine_config_constructor ti99_cartridge_device::device_mconfig_additions() const
@ -1499,19 +1625,31 @@ const device_type TI99CART = &device_creator<ti99_cartridge_device>;
Unlike in the previous implementation we do not model it as a full device.
***************************************************************************/
ti99_cartridge_pcb::ti99_cartridge_pcb(): m_cart(nullptr), m_grom_size(0), m_rom_size(0), m_ram_size(0), m_rom_ptr(nullptr), m_ram_ptr(nullptr), m_rom_page(0), m_grom_ptr(nullptr), m_grom_address(0), m_ram_page(0), m_tag(nullptr)
ti99_cartridge_pcb::ti99_cartridge_pcb()
: m_cart(nullptr),
m_grom_size(0),
m_rom_size(0),
m_ram_size(0),
m_rom_ptr(nullptr),
m_ram_ptr(nullptr),
m_access_cartspace(false),
m_rom_page(0),
m_grom_ptr(nullptr),
m_grom_address(0),
m_ram_page(0),
m_tag(nullptr)
{
}
UINT16 ti99_cartridge_pcb::grom_base()
{
return m_cart->grom_base();
}
UINT16 ti99_cartridge_pcb::grom_mask()
{
return m_cart->grom_mask();
}
// UINT16 ti99_cartridge_pcb::grom_base()
// {
// return m_cart->grom_base();
// }
//
// UINT16 ti99_cartridge_pcb::grom_mask()
// {
// return m_cart->grom_mask();
// }
void ti99_cartridge_pcb::set_cartridge(ti99_cartridge_device *cart)
{
@ -1540,31 +1678,38 @@ WRITE8_MEMBER(ti99_cartridge_pcb::gromwrite)
}
}
/*
TI-99/4A cartridges can only occupy 8 KiB of CPU RAM space. For TI-99/8
cartridges with up to 16 KiB we need a new PCB type. Unfortunately, such
cartridges were never developed.
*/
READ8Z_MEMBER(ti99_cartridge_pcb::readz)
{
if ((offset & grom_mask())==grom_base())
gromreadz(space, offset, value, mem_mask);
else
if (m_access_cartspace)
{
if (m_rom_ptr!=nullptr)
{
// For TI-99/8 we should plan for 16K cartridges. However, none was ever produced.
// Well, forget about that.
*value = m_rom_ptr[offset & 0x1fff];
// logerror("%s: read cartridge rom space %04x = %02x\n", tag(), offset, *value);
}
}
else
{
// Will not return anything when not selected (preceding gsq=ASSERT)
gromreadz(space, offset, value, mem_mask);
}
}
WRITE8_MEMBER(ti99_cartridge_pcb::write)
{
// logerror("%s: write standard\n", tag());
if ((offset & grom_mask())==grom_base())
gromwrite(space, offset, data, mem_mask);
else
if (m_access_cartspace)
{
if (TRACE_ILLWRITE) space.device().logerror("%s: Cannot write to ROM space at %04x\n", tag(), offset);
}
else
{
// Will not change anything when not selected (preceding gsq=ASSERT)
gromwrite(space, offset, data, mem_mask);
}
}
READ8Z_MEMBER(ti99_cartridge_pcb::crureadz)
@ -1575,11 +1720,35 @@ WRITE8_MEMBER(ti99_cartridge_pcb::cruwrite)
{
}
inline void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev)
void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev)
{
m_grom[number] = static_cast<ti99_grom_device*>(dev);
m_grom[number] = static_cast<tmc0430_device*>(dev);
}
WRITE_LINE_MEMBER( ti99_cartridge_pcb::romgq_line )
{
m_access_cartspace = (state==ASSERT_LINE);
}
// Propagate to all GROMs
/*
Combined select lines
*/
WRITE8_MEMBER(ti99_cartridge_pcb::set_gromlines)
{
for (auto& elem : m_grom)
if (elem != nullptr) elem->set_lines(space, offset, data);
}
WRITE_LINE_MEMBER(ti99_cartridge_pcb::gclock_in)
{
for (auto& elem : m_grom)
if (elem != nullptr) elem->gclock_in(state);
}
/*****************************************************************************
Cartridge type: Paged (Extended Basic)
This cartridge consists of GROM memory and 2 pages of standard ROM.
@ -1591,23 +1760,28 @@ inline void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev)
READ8Z_MEMBER(ti99_paged_cartridge::readz)
{
if ((offset & grom_mask())==grom_base())
gromreadz(space, offset, value, mem_mask);
else
if (m_access_cartspace)
{
*value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page << 13)];
}
else
{
// Will not return anything when not selected (preceding gsq=ASSERT)
gromreadz(space, offset, value, mem_mask);
}
}
WRITE8_MEMBER(ti99_paged_cartridge::write)
{
// logerror("%s: write standard\n", tag());
if ((offset & grom_mask())==grom_base())
gromwrite(space, offset, data, mem_mask);
else {
if (m_access_cartspace)
{
m_rom_page = (offset >> 1) & 1;
}
else
{
// Will not change anything when not selected (preceding gsq=ASSERT)
gromwrite(space, offset, data, mem_mask);
}
}
/*****************************************************************************
@ -1620,10 +1794,7 @@ WRITE8_MEMBER(ti99_paged_cartridge::write)
/* Read function for the minimem cartridge. */
READ8Z_MEMBER(ti99_minimem_cartridge::readz)
{
if ((offset & grom_mask())==grom_base())
gromreadz(space, offset, value, mem_mask);
else
if (m_access_cartspace)
{
if ((offset & 0x1000)==0x0000)
{
@ -1637,16 +1808,16 @@ READ8Z_MEMBER(ti99_minimem_cartridge::readz)
*value = m_ram_ptr[offset & 0x0fff];
}
}
else
{
gromreadz(space, offset, value, mem_mask);
}
}
/* Write function for the minimem cartridge. */
WRITE8_MEMBER(ti99_minimem_cartridge::write)
{
// logerror("%s: write standard\n", tag());
if ((offset & grom_mask())==grom_base())
gromwrite(space, offset, data, mem_mask);
else
if (m_access_cartspace)
{
if ((offset & 0x1000)==0x0000)
{
@ -1657,6 +1828,10 @@ WRITE8_MEMBER(ti99_minimem_cartridge::write)
m_ram_ptr[offset & 0x0fff] = data;
}
}
else
{
gromwrite(space, offset, data, mem_mask);
}
}
/*****************************************************************************
@ -1678,26 +1853,30 @@ WRITE8_MEMBER(ti99_minimem_cartridge::write)
/* Read function for the super cartridge. */
READ8Z_MEMBER(ti99_super_cartridge::readz)
{
if ((offset & grom_mask())==grom_base())
gromreadz(space, offset, value, mem_mask);
else
if (m_access_cartspace)
{
if (m_ram_ptr != nullptr)
{
*value = m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)];
}
}
else
{
gromreadz(space, offset, value, mem_mask);
}
}
/* Write function for the super cartridge. */
WRITE8_MEMBER(ti99_super_cartridge::write)
{
if ((offset & grom_mask())==grom_base())
gromwrite(space, offset, data, mem_mask);
else
if (m_access_cartspace)
{
m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)] = data;
}
else
{
gromwrite(space, offset, data, mem_mask);
}
}
READ8Z_MEMBER(ti99_super_cartridge::crureadz)
@ -1763,9 +1942,7 @@ WRITE8_MEMBER(ti99_super_cartridge::cruwrite)
/* Read function for the mbx cartridge. */
READ8Z_MEMBER(ti99_mbx_cartridge::readz)
{
if ((offset & grom_mask())==grom_base())
gromreadz(space, offset, value, mem_mask);
else
if (m_access_cartspace)
{
if ((offset & 0x1c00)==0x0c00)
{
@ -1781,14 +1958,16 @@ READ8Z_MEMBER(ti99_mbx_cartridge::readz)
*value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page<<13)];
}
}
else
{
gromreadz(space, offset, value, mem_mask);
}
}
/* Write function for the mbx cartridge. */
WRITE8_MEMBER(ti99_mbx_cartridge::write)
{
if ((offset & grom_mask())==grom_base())
gromwrite(space, offset, data, mem_mask);
else
if (m_access_cartspace)
{
if (offset == 0x6ffe)
{
@ -1802,6 +1981,10 @@ WRITE8_MEMBER(ti99_mbx_cartridge::write)
m_ram_ptr[offset & 0x03ff] = data;
}
}
else
{
gromwrite(space, offset, data, mem_mask);
}
}
/*****************************************************************************
@ -2032,9 +2215,15 @@ WRITE8_MEMBER(ti99_pagedcru_cartridge::cruwrite)
******************************************************************************/
WRITE_LINE_MEMBER(ti99_gromemu_cartridge::gsq_line)
{
m_grom_space = (state==ASSERT_LINE);
}
READ8Z_MEMBER(ti99_gromemu_cartridge::readz)
{
if ((offset & grom_mask())==grom_base())
if (m_grom_space)
gromemureadz(space, offset, value, mem_mask);
else
{
@ -2055,8 +2244,7 @@ READ8Z_MEMBER(ti99_gromemu_cartridge::readz)
WRITE8_MEMBER(ti99_gromemu_cartridge::write)
{
// logerror("%s: write standard\n", tag());
if ((offset & grom_mask())==grom_base())
if (m_grom_space)
gromemuwrite(space, offset, data, mem_mask);
else {
@ -2179,7 +2367,7 @@ rpk::rpk(emu_options& options, const char* sysname)
rpk::~rpk()
{
//if (TRACE_RPK) logerror("gromport/RPK: Destroy RPK\n");
if (TRACE_RPK) printf("gromport/RPK: Destroy RPK\n");
}
/*
@ -2290,7 +2478,7 @@ rpk_socket* rpk_reader::load_rom_resource(util::archive_file &zip, xml_data_node
file = xml_get_attribute_string(rom_resource_node, "file", nullptr);
if (file == nullptr) throw rpk_exception(RPK_INVALID_LAYOUT, "<rom> must have a 'file' attribute");
//if (TRACE_RPK) logerror("gromport/RPK: Loading ROM contents for socket '%s' from file %s\n", socketname, file);
if (TRACE_RPK) printf("gromport/RPK: Loading ROM contents for socket '%s' from file %s\n", socketname, file);
// check for crc
crcstr = xml_get_attribute_string(rom_resource_node, "crc", nullptr);
@ -2378,7 +2566,7 @@ rpk_socket* rpk_reader::load_ram_resource(emu_options &options, xml_data_node* r
contents = global_alloc_array_clear<UINT8>(length);
if (contents==nullptr) throw rpk_exception(RPK_OUT_OF_MEMORY);
//if (TRACE_RPK) logerror("gromport/RPK: Allocating RAM buffer (%d bytes) for socket '%s'\n", length, socketname);
if (TRACE_RPK) printf("gromport/RPK: Allocating RAM buffer (%d bytes) for socket '%s'\n", length, socketname);
ram_pname = nullptr;
@ -2400,7 +2588,7 @@ rpk_socket* rpk_reader::load_ram_resource(emu_options &options, xml_data_node* r
std::string ram_pathname = std::string(system_name).append(PATH_SEPARATOR).append(ram_filename);
ram_pname = core_strdup(ram_pathname.c_str());
// load, and fill rest with 00
//if (TRACE_RPK) logerror("gromport/RPK: Loading NVRAM contents from '%s'\n", ram_pname);
if (TRACE_RPK) printf("gromport/RPK: Loading NVRAM contents from '%s'\n", ram_pname);
image_battery_load_by_name(options, ram_pname, contents, length, 0x00);
}
}
@ -2488,7 +2676,7 @@ rpk* rpk_reader::open(emu_options &options, const char *filename, const char *sy
// We'll try to find the PCB type on the provided type list.
pcb_type = xml_get_attribute_string(pcb_node, "type", nullptr);
if (pcb_type==nullptr) throw rpk_exception(RPK_INVALID_LAYOUT, "<pcb> must have a 'type' attribute");
//if (TRACE_RPK) logerror("gromport/RPK: Cartridge says it has PCB type '%s'\n", pcb_type);
if (TRACE_RPK) printf("gromport/RPK: Cartridge says it has PCB type '%s'\n", pcb_type);
i=0;
do

View File

@ -13,7 +13,7 @@
#include "emu.h"
#include "ti99defs.h"
#include "grom.h"
#include "machine/tmc0430.h"
extern const device_type GROMPORT;
@ -29,13 +29,22 @@ public:
DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(ready_line);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
// Combined GROM select lines
DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
static void set_mask(device_t &device, int mask) { downcast<gromport_device &>(device).m_mask = mask; }
template<class _Object> static devcb_base &static_set_ready_callback(device_t &device, _Object object) { return downcast<gromport_device &>(device).m_console_ready.set_callback(object); }
template<class _Object> static devcb_base &static_set_reset_callback(device_t &device, _Object object) { return downcast<gromport_device &>(device).m_console_reset.set_callback(object); }
void cartridge_inserted();
void set_grom_base(UINT16 grombase, UINT16 grommask);
UINT16 get_grom_base() { return m_grombase; }
UINT16 get_grom_mask() { return m_grommask; }
// void set_grom_base(UINT16 grombase, UINT16 grommask);
// UINT16 get_grom_base() { return m_grombase; }
// UINT16 get_grom_mask() { return m_grommask; }
protected:
virtual void device_start() override;
@ -48,15 +57,24 @@ private:
bool m_reset_on_insert;
devcb_write_line m_console_ready;
devcb_write_line m_console_reset;
UINT16 m_grombase;
UINT16 m_grommask;
int m_mask;
int m_romgq;
// UINT16 m_grombase;
// UINT16 m_grommask;
};
SLOT_INTERFACE_EXTERN(gromport);
SLOT_INTERFACE_EXTERN(gromport4);
SLOT_INTERFACE_EXTERN(gromport8);
#define MCFG_TI99_GROMPORT_ADD( _tag ) \
#define MCFG_GROMPORT4_ADD( _tag ) \
MCFG_DEVICE_ADD(_tag, GROMPORT, 0) \
MCFG_DEVICE_SLOT_INTERFACE(gromport, "single", false)
gromport_device::set_mask(*device, 0x1fff); \
MCFG_DEVICE_SLOT_INTERFACE(gromport4, "single", false)
#define MCFG_GROMPORT8_ADD( _tag ) \
MCFG_DEVICE_ADD(_tag, GROMPORT, 0) \
gromport_device::set_mask(*device, 0x3fff); \
MCFG_DEVICE_SLOT_INTERFACE(gromport8, "single", false)
#define MCFG_GROMPORT_READY_HANDLER( _ready ) \
devcb = &gromport_device::static_set_ready_callback( *device, DEVCB_##_ready );
@ -80,11 +98,16 @@ public:
DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(ready_line);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
bool is_available() { return m_pcb != nullptr; }
bool has_grom();
void set_slot(int i);
UINT16 grom_base();
UINT16 grom_mask();
// UINT16 grom_base();
// UINT16 grom_mask();
protected:
virtual void device_start() override { };
@ -111,6 +134,7 @@ protected:
const option_guide *create_option_guide() const override { return nullptr; }
private:
bool m_readrom;
bool m_softlist;
int m_pcbtype;
int m_slot;
@ -136,18 +160,24 @@ public:
virtual DECLARE_READ8Z_MEMBER(crureadz) = 0;
virtual DECLARE_WRITE8_MEMBER(cruwrite) = 0;
virtual DECLARE_WRITE_LINE_MEMBER(romgq_line) =0;
virtual DECLARE_WRITE8_MEMBER(set_gromlines) =0;
virtual DECLARE_WRITE_LINE_MEMBER(gclock_in) =0;
DECLARE_WRITE_LINE_MEMBER(ready_line);
virtual void insert(int index, ti99_cartridge_device* cart) { m_gromport->cartridge_inserted(); };
virtual void remove(int index) { };
UINT16 grom_base();
UINT16 grom_mask();
// UINT16 grom_base();
// UINT16 grom_mask();
protected:
ti99_cartridge_connector_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
virtual void device_config_complete() override;
gromport_device* m_gromport;
int m_gsel;
};
/*
@ -162,6 +192,9 @@ public:
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
protected:
virtual void device_start() override;
@ -190,6 +223,9 @@ public:
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
void insert(int index, ti99_cartridge_device* cart) override;
void remove(int index) override;
@ -201,6 +237,7 @@ protected:
virtual ioport_constructor device_input_ports() const override;
private:
bool m_readrom;
int m_active_slot;
int m_fixed_slot;
int m_next_free_slot;
@ -222,6 +259,9 @@ public:
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
void insert(int index, ti99_cartridge_device* cart) override;
void remove(int index) override;
@ -275,23 +315,28 @@ protected:
virtual DECLARE_READ8Z_MEMBER(crureadz);
virtual DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
DECLARE_READ8Z_MEMBER(gromreadz);
DECLARE_WRITE8_MEMBER(gromwrite);
inline void set_grom_pointer(int number, device_t *dev);
void set_cartridge(ti99_cartridge_device *cart);
UINT16 grom_base();
UINT16 grom_mask();
// UINT16 grom_base();
// UINT16 grom_mask();
const char* tag() { return m_tag; }
void set_tag(const char* tag) { m_tag = tag; }
ti99_cartridge_device* m_cart;
ti99_grom_device* m_grom[5];
tmc0430_device* m_grom[5];
int m_grom_size;
int m_rom_size;
int m_ram_size;
UINT8* m_rom_ptr;
UINT8* m_ram_ptr;
bool m_access_cartspace;
int m_rom_page; // for some cartridge types
UINT8* m_grom_ptr; // for gromemu
int m_grom_address; // for gromemu
@ -398,15 +443,19 @@ public:
class ti99_gromemu_cartridge : public ti99_cartridge_pcb
{
public:
ti99_gromemu_cartridge(): m_waddr_LSB(false)
ti99_gromemu_cartridge(): m_waddr_LSB(false), m_grom_space(false)
{ m_grom_address = 0; }
~ti99_gromemu_cartridge() { };
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(gromemureadz);
DECLARE_WRITE8_MEMBER(gromemuwrite);
DECLARE_WRITE_LINE_MEMBER(gsq_line);
private:
bool m_waddr_LSB;
bool m_grom_space;
};

View File

@ -32,6 +32,7 @@
#define JOYPORT_TAG "joyport"
#define VDP_TAG "vdp"
#define DSRROM "dsrrom"
#define CONSOLEROM "consolerom"
#define VDPFREQ XTAL_10_738635MHz
#define GROMFREQ VDPFREQ/24
@ -41,10 +42,49 @@
#define DRAM_TAG "dram8"
#define MAPPER_TAG "mapper"
#define MAINBOARD8_TAG "mainboard8"
#define SPEECH_TAG "speech"
#define ROM0_TAG "rom0"
#define ROM1_TAG "rom1"
#define PCODEROM_TAG "pcode"
#define SPEECHSYN_TAG "speech"
#define ROM0_REG "rom0_region"
#define ROM1_REG "rom1_region"
#define PASCAL_REG "pascal_region"
#define SYSGROM_REG "sysgrom_region"
#define GROMLIB1_REG "gromlib1_region"
#define GROMLIB2_REG "gromlib2_region"
#define GROMLIB3_REG "gromlib3_region"
#define SPEECHROM_REG "speech_region"
#define GROMLIB_TAG "gromlib"
#define SYSGROM_TAG GROMLIB_TAG "0"
#define SYSGROM0_TAG SYSGROM_TAG "_0"
#define SYSGROM1_TAG SYSGROM_TAG "_1"
#define SYSGROM2_TAG SYSGROM_TAG "_2"
#define GLIB1_TAG GROMLIB_TAG "1"
#define GLIB10_TAG GLIB1_TAG "_0"
#define GLIB11_TAG GLIB1_TAG "_1"
#define GLIB12_TAG GLIB1_TAG "_2"
#define GLIB13_TAG GLIB1_TAG "_3"
#define GLIB14_TAG GLIB1_TAG "_4"
#define GLIB15_TAG GLIB1_TAG "_5"
#define GLIB16_TAG GLIB1_TAG "_6"
#define GLIB17_TAG GLIB1_TAG "_7"
#define GLIB2_TAG GROMLIB_TAG "2"
#define GLIB20_TAG GLIB2_TAG "_0"
#define GLIB21_TAG GLIB2_TAG "_1"
#define GLIB22_TAG GLIB2_TAG "_2"
#define GLIB23_TAG GLIB2_TAG "_3"
#define GLIB24_TAG GLIB2_TAG "_4"
#define GLIB25_TAG GLIB2_TAG "_5"
#define GLIB26_TAG GLIB2_TAG "_6"
#define GLIB27_TAG GLIB2_TAG "_7"
#define GLIB3_TAG GROMLIB_TAG "3"
#define GLIB30_TAG GLIB3_TAG "_0"
#define GLIB31_TAG GLIB3_TAG "_1"
#define GLIB32_TAG GLIB3_TAG "_2"
// Geneve
#define GKEYBOARD_TAG "gkeyboard"

View File

@ -39,7 +39,9 @@ ti_std_video_device::ti_std_video_device(const machine_config &mconfig, const ch
}
ti_exp_video_device::ti_exp_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: ti_video_device(mconfig, V9938VIDEO, "TI99 EXP Video subsystem", tag, owner, clock, "v9938_video", __FILE__), m_v9938(nullptr)
: ti_video_device(mconfig, V9938VIDEO, "TI99 EXP Video subsystem", tag, owner, clock, "v9938_video", __FILE__),
m_v9938(nullptr),
m_out_gromclk_cb(*this)
{
}
@ -129,6 +131,14 @@ WRITE16_MEMBER( ti_exp_video_device::write16 )
}
}
void ti_exp_video_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
// Pulse it
m_out_gromclk_cb(ASSERT_LINE);
m_out_gromclk_cb(CLEAR_LINE);
}
/******************************************************************************/
/*
@ -194,10 +204,13 @@ void ti_video_device::device_start(void)
void ti_exp_video_device::device_start(void)
{
m_v9938 = static_cast<v9938_device*>(machine().device(VDP_TAG));
m_out_gromclk_cb.resolve();
m_gromclk_timer = timer_alloc(0);
}
void ti_video_device::device_reset(void)
void ti_exp_video_device::device_reset(void)
{
if (!m_out_gromclk_cb.isnull()) m_gromclk_timer->adjust(attotime::zero, 0, attotime::from_hz(XTAL_10_738635MHz/24));
}
/**************************************************************************/

View File

@ -30,7 +30,7 @@ protected:
/* Constructor */
ti_video_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
virtual void device_start(void) override;
virtual void device_reset(void) override;
virtual void device_reset(void) override { };
virtual DECLARE_READ8Z_MEMBER(readz) override { };
virtual DECLARE_WRITE8_MEMBER(write) override { };
};
@ -55,6 +55,8 @@ class ti_exp_video_device : public ti_video_device
{
public:
ti_exp_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
template<class _Object> static devcb_base &set_out_gromclk_callback(device_t &device, _Object object) { return downcast<ti_exp_video_device &>(device).m_out_gromclk_cb.set_callback(object); }
void video_update_mouse(int delta_x, int delta_y, int buttons);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
@ -63,10 +65,20 @@ public:
void reset_vdp(int state) override { m_v9938->reset_line(state); }
protected:
virtual void device_start(void) override;
v9938_device *m_v9938;
virtual void device_start(void) override;
virtual void device_reset(void) override;
private:
void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
v9938_device *m_v9938;
devcb_write_line m_out_gromclk_cb; // GROMCLK line is optional; if present, pulse it by XTAL/24 rate
emu_timer *m_gromclk_timer;
};
#define MCFG_ADD_GROMCLK_CB(_devcb) \
devcb = &ti_exp_video_device::set_out_gromclk_callback(*device, DEVCB_##_devcb);
extern const device_type TI99VIDEO;
extern const device_type V9938VIDEO;
@ -131,35 +143,39 @@ protected:
/****************************************************************************/
#define MCFG_TI_TMS991x_ADD_NTSC(_tag, _chip, _vsize, _class, _int) \
#define MCFG_TI_TMS991x_ADD_NTSC(_tag, _chip, _vsize, _class, _int, _gclk) \
MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \
MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(_vsize) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \
MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
#define MCFG_TI_TMS991x_ADD_PAL(_tag, _chip, _vsize, _class, _int) \
#define MCFG_TI_TMS991x_ADD_PAL(_tag, _chip, _vsize, _class, _int, _gclk) \
MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \
MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(_vsize) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \
MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
#define MCFG_TI998_ADD_NTSC(_tag, _chip, _vsize, _class, _int) \
#define MCFG_TI998_ADD_NTSC(_tag, _chip, _vsize, _class, _int, _gclk) \
MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \
MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(_vsize) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \
MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
#define MCFG_TI998_ADD_PAL(_tag, _chip, _vsize, _class, _int) \
#define MCFG_TI998_ADD_PAL(_tag, _chip, _vsize, _class, _int, _gclk) \
MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \
MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(_vsize) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \
MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )

View File

@ -439,12 +439,16 @@ READ16_MEMBER( ti99_4p_state::datamux_read )
UINT8 hbyte = 0;
UINT16 addroff = (offset << 1);
m_peribox->memen_in(ASSERT_LINE);
m_peribox->readz(space, addroff+1, &m_latch, mem_mask);
m_lowbyte = m_latch;
m_peribox->readz(space, addroff, &hbyte, mem_mask);
m_highbyte = hbyte;
m_peribox->memen_in(CLEAR_LINE);
// use the latch and the currently read byte and put it on the 16bit bus
// printf("read address = %04x, value = %04x, memmask = %4x\n", addroff, (hbyte<<8) | sgcpu->latch, mem_mask);
@ -467,11 +471,15 @@ WRITE16_MEMBER( ti99_4p_state::datamux_write )
// read more about the datamux in datamux.c
// Write to the PEB
m_peribox->memen_in(ASSERT_LINE);
m_peribox->write(space, addroff+1, data & 0xff);
// Write to the PEB
m_peribox->write(space, addroff, (data>>8) & 0xff);
m_peribox->memen_in(CLEAR_LINE);
// Insert four wait states and let CPU enter wait state
m_waitcount = 6;
console_ready_dmux(CLEAR_LINE);

View File

@ -48,7 +48,6 @@
#include "bus/ti99x/videowrp.h"
#include "bus/ti99x/datamux.h"
#include "bus/ti99x/grom.h"
#include "bus/ti99x/gromport.h"
#include "bus/ti99x/joyport.h"
@ -103,6 +102,9 @@ public:
DECLARE_WRITE_LINE_MEMBER( console_reset );
DECLARE_WRITE_LINE_MEMBER( notconnected );
// GROM clock
DECLARE_WRITE_LINE_MEMBER( gromclk_in );
// Connections with the system interface chip 9901
DECLARE_WRITE_LINE_MEMBER( extint );
DECLARE_WRITE_LINE_MEMBER( video_interrupt_in );
@ -177,16 +179,10 @@ enum
};
/*
Memory map.
Most of the work is done in the datamux (see datamux.c). We only keep ROM
and the small 256 byte PAD RAM here because they are directly connected
to the 16bit bus, and the wait state logic is not active during their
accesses.
Memory map. All of the work is done in the datamux (see datamux.c).
*/
static ADDRESS_MAP_START(memmap, AS_PROGRAM, 16, ti99_4x_state)
ADDRESS_MAP_GLOBAL_MASK(0xffff)
AM_RANGE(0x0000, 0x1fff) AM_ROM
AM_RANGE(0x8000, 0x80ff) AM_MIRROR(0x0300) AM_RAM
AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(DATAMUX_TAG, ti99_datamux_device, read, write) AM_DEVSETOFFSET(DATAMUX_TAG, ti99_datamux_device, setoffset)
ADDRESS_MAP_END
@ -365,21 +361,6 @@ INPUT_PORTS_END
Components
******************************************************************************/
static GROM_CONFIG(grom0_config)
{
false, 0, region_grom, 0x0000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom1_config)
{
false, 1, region_grom, 0x2000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom2_config)
{
false, 2, region_grom, 0x4000, 0x1800, GROMFREQ
};
READ8_MEMBER( ti99_4x_state::cruread )
{
// if (TRACE_CRU) logerror("read access to CRU address %04x\n", offset << 4);
@ -631,6 +612,7 @@ READ8_MEMBER( ti99_4x_state::interrupt_level )
WRITE_LINE_MEMBER( ti99_4x_state::clock_out )
{
m_datamux->clock_in(state);
m_peribox->clock_in(state);
}
/*
@ -641,6 +623,15 @@ WRITE_LINE_MEMBER( ti99_4x_state::dbin_line )
m_datamux->dbin_in(state);
}
/*
GROMCLK from VDP, propagating to datamux
*/
WRITE_LINE_MEMBER( ti99_4x_state::gromclk_in )
{
m_datamux->gromclk_in(state);
}
/*****************************************************************************/
/*
@ -708,6 +699,7 @@ void ti99_4x_state::console_ready_join(int id, int state)
*/
WRITE_LINE_MEMBER( ti99_4x_state::console_ready_grom )
{
if (TRACE_READY) logerror("GROM ready = %d\n", state);
console_ready_join(READY_GROM, state);
}
@ -757,52 +749,6 @@ WRITE_LINE_MEMBER( ti99_4x_state::notconnected )
if (TRACE_INTERRUPTS) logerror("ti99_4x: Setting a not connected line ... ignored\n");
}
/*****************************************************************************/
/*
Devices attached to the databus multiplexer. We cannot solve this with
the common address maps since the multiplexer also inserts wait states
that we want to emulate properly. Also, devices may reside on the same
memory locations (like GROMs) and select themselves according to some
inner state (e.g. GROMs have an own address counter and a given address
area).
*/
static const dmux_device_list_entry dmux_devices[] =
{
{ VIDEO_SYSTEM_TAG, 0x8800, 0xfc01, 0x0400, nullptr, 0, 0 },
{ GROM0_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 },
{ GROM1_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 },
{ GROM2_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 },
{ TISOUND_TAG, 0x8400, 0xfc01, 0x0000, nullptr, 0, 0 },
{ GROMPORT_TAG, 0x9800, 0xfc01, 0x0400, nullptr, 0, 0 },
{ GROMPORT_TAG, 0x6000, 0xe000, 0x0000, nullptr, 0, 0 },
{ PERIBOX_TAG, 0x0000, 0x0000, 0x0000, nullptr, 0, 0 }, // Peribox needs all addresses
{ nullptr, 0, 0, 0, nullptr, 0, 0 }
};
static const dmux_device_list_entry dmux_devices_ev[] =
{
{ VIDEO_SYSTEM_TAG, 0x8800, 0xfc01, 0x0400, nullptr, 0, 0 },
{ GROM0_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 },
{ GROM1_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 },
{ GROM2_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 },
{ TISOUND_TAG, 0x8400, 0xfc01, 0x0000, nullptr, 0, 0 },
{ GROMPORT_TAG, 0x9800, 0xfc01, 0x0400, nullptr, 0, 0 },
{ GROMPORT_TAG, 0x6000, 0xe000, 0x0000, nullptr, 0, 0 },
{ PERIBOX_TAG, 0x0000, 0x0000, 0x0000, nullptr, 0, 0 }, // Peribox needs all addresses
{ nullptr, 0, 0, 0, nullptr, 0, 0 }
};
static DMUX_CONFIG( datamux_conf )
{
dmux_devices
};
static DMUX_CONFIG( datamux_conf_ev )
{
dmux_devices_ev
};
/******************************************************************************
Machine definitions
******************************************************************************/
@ -852,9 +798,10 @@ static MACHINE_CONFIG_START( ti99_4, ti99_4x_state )
MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_4x_state, cassette_output) )
MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_4x_state, tms9901_interrupt) )
MCFG_DMUX_ADD( DATAMUX_TAG, datamux_conf )
MCFG_DEVICE_ADD( DATAMUX_TAG, DATAMUX, 0)
MCFG_DMUX_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_dmux) )
MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG )
MCFG_GROMPORT4_ADD( GROMPORT_TAG )
MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_cart) )
MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_4x_state, console_reset) )
@ -879,13 +826,10 @@ static MACHINE_CONFIG_START( ti99_4, ti99_4x_state )
MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette")
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25)
/* GROM devices */
MCFG_GROM_ADD( GROM0_TAG, grom0_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM1_TAG, grom1_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM2_TAG, grom2_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
// GROM devices
MCFG_GROM_ADD( GROM0_TAG, 0, region_grom, 0x0000, WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM1_TAG, 1, region_grom, 0x2000, WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM2_TAG, 2, region_grom, 0x4000, WRITELINE(ti99_4x_state, console_ready_grom))
// Joystick port
MCFG_TI_JOYPORT4_ADD( JOYPORT_TAG )
@ -896,14 +840,14 @@ MACHINE_CONFIG_END
US version: 60 Hz, NTSC
*/
static MACHINE_CONFIG_DERIVED( ti99_4_60hz, ti99_4 )
MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918, 0x4000, ti99_4x_state, video_interrupt_in)
MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in)
MACHINE_CONFIG_END
/*
European version: 50 Hz, PAL
*/
static MACHINE_CONFIG_DERIVED( ti99_4_50hz, ti99_4 )
MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929, 0x4000, ti99_4x_state, video_interrupt_in)
MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in)
MACHINE_CONFIG_END
/**********************************************************************
@ -951,9 +895,10 @@ static MACHINE_CONFIG_START( ti99_4a, ti99_4x_state )
MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_4x_state, cassette_output) )
MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_4x_state, tms9901_interrupt) )
MCFG_DMUX_ADD( DATAMUX_TAG, datamux_conf )
MCFG_DEVICE_ADD( DATAMUX_TAG, DATAMUX, 0)
MCFG_DMUX_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_dmux) )
MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG )
MCFG_GROMPORT4_ADD( GROMPORT_TAG )
MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_cart) )
MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_4x_state, console_reset) )
@ -978,13 +923,10 @@ static MACHINE_CONFIG_START( ti99_4a, ti99_4x_state )
MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette")
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25)
/* GROM devices */
MCFG_GROM_ADD( GROM0_TAG, grom0_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM1_TAG, grom1_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM2_TAG, grom2_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
// GROM devices
MCFG_GROM_ADD( GROM0_TAG, 0, region_grom, 0x0000, WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM1_TAG, 1, region_grom, 0x2000, WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM2_TAG, 2, region_grom, 0x4000, WRITELINE(ti99_4x_state, console_ready_grom))
// Joystick port
MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG )
@ -994,14 +936,14 @@ MACHINE_CONFIG_END
US version: 60 Hz, NTSC
*/
static MACHINE_CONFIG_DERIVED( ti99_4a_60hz, ti99_4a )
MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in)
MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in)
MACHINE_CONFIG_END
/*
European version: 50 Hz, PAL
*/
static MACHINE_CONFIG_DERIVED( ti99_4a_50hz, ti99_4a )
MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in)
MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in)
MACHINE_CONFIG_END
/************************************************************************
@ -1029,14 +971,14 @@ MACHINE_CONFIG_END
US version: 60 Hz, NTSC
*/
static MACHINE_CONFIG_DERIVED( ti99_4qi_60hz, ti99_4qi )
MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in)
MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in)
MACHINE_CONFIG_END
/*
European version: 50 Hz, PAL
*/
static MACHINE_CONFIG_DERIVED( ti99_4qi_50hz, ti99_4qi )
MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in)
MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in)
MACHINE_CONFIG_END
/************************************************************************
@ -1056,6 +998,10 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state )
/* video hardware */
MCFG_DEVICE_ADD(VIDEO_SYSTEM_TAG, V9938VIDEO, 0)
// Removing the TMS9928a requires to add a replacement for the GROMCLK.
// In the real hardware this is a circuit (REPL99x) that fits into the VDP socket
MCFG_ADD_GROMCLK_CB( WRITELINE(ti99_4x_state, gromclk_in) )
MCFG_V9938_ADD(VDP_TAG, SCREEN_TAG, 0x20000, XTAL_21_4772MHz) /* typical 9938 clock, not verified */
MCFG_V99X8_INTERRUPT_CALLBACK(WRITELINE(ti99_4x_state, video_interrupt_in))
MCFG_V99X8_SCREEN_ADD_NTSC(SCREEN_TAG, VDP_TAG, XTAL_21_4772MHz)
@ -1073,9 +1019,9 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state )
MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_4x_state, cassette_output) )
MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_4x_state, tms9901_interrupt) )
MCFG_DMUX_ADD( DATAMUX_TAG, datamux_conf_ev )
MCFG_DEVICE_ADD( DATAMUX_TAG, DATAMUX, 0)
MCFG_DMUX_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_dmux) )
MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG )
MCFG_GROMPORT4_ADD( GROMPORT_TAG )
MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_cart) )
MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_4x_state, console_reset) )
@ -1100,13 +1046,10 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state )
MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette")
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25)
/* GROM devices */
MCFG_GROM_ADD( GROM0_TAG, grom0_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM1_TAG, grom1_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM2_TAG, grom2_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom))
// GROM devices
MCFG_GROM_ADD( GROM0_TAG, 0, region_grom, 0x0000, WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM1_TAG, 1, region_grom, 0x2000, WRITELINE(ti99_4x_state, console_ready_grom))
MCFG_GROM_ADD( GROM2_TAG, 2, region_grom, 0x4000, WRITELINE(ti99_4x_state, console_ready_grom))
// Joystick port
MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG )
@ -1124,7 +1067,7 @@ MACHINE_CONFIG_END
ROM_START(ti99_4)
// CPU memory space
ROM_REGION16_BE(0x2000, "maincpu", 0)
ROM_REGION16_BE(0x2000, CONSOLEROM, 0)
ROM_LOAD16_BYTE("u610.bin", 0x0000, 0x1000, CRC(6fcf4b15) SHA1(d085213c64701d429ae535f9a4ac8a50427a8343)) /* CPU ROMs high */
ROM_LOAD16_BYTE("u611.bin", 0x0001, 0x1000, CRC(491c21d1) SHA1(7741ae9294c51a44a78033d1b77c01568a6bbfb9)) /* CPU ROMs low */
@ -1137,7 +1080,7 @@ ROM_END
ROM_START(ti99_4a)
// CPU memory space
ROM_REGION16_BE(0x2000, "maincpu", 0)
ROM_REGION16_BE(0x2000, CONSOLEROM, 0)
ROM_LOAD16_WORD("994arom.bin", 0x0000, 0x2000, CRC(db8f33e5) SHA1(6541705116598ab462ea9403c00656d6353ceb85)) /* system ROMs */
// GROM memory space
@ -1147,7 +1090,7 @@ ROM_END
ROM_START(ti99_4qi)
// CPU memory space
ROM_REGION16_BE(0x2000, "maincpu", 0)
ROM_REGION16_BE(0x2000, CONSOLEROM, 0)
ROM_LOAD16_WORD("994qirom.bin", 0x0000, 0x2000, CRC(db8f33e5) SHA1(6541705116598ab462ea9403c00656d6353ceb85)) /* system ROMs */
// GROM memory space
@ -1160,7 +1103,7 @@ ROM_END
ROM_START(ti99_4ev)
/*CPU memory space*/
ROM_REGION16_BE(0x2000, "maincpu", 0)
ROM_REGION16_BE(0x2000, CONSOLEROM, 0)
ROM_LOAD16_WORD("994arom.bin", 0x0000, 0x2000, CRC(db8f33e5) SHA1(6541705116598ab462ea9403c00656d6353ceb85)) /* system ROMs */
/*GROM memory space*/

View File

@ -86,33 +86,6 @@
- Native mode (Armadillo mode): Devices are located at positions above
0xF000 that allow for a contiguous usage of memory.
ROM contents
------------
The ROM0 chip is accessible at addresses 0000-1FFF in the logical address
space of the compatibility mode. It contains the GPL interpreter. In
native mode the ROM0 chip is invisible.
ROM0
offset Logical address Name
-----------------------------------
0000 0000-1FFF ROM0
The ROM1 chip contains 32 KiB of various system software. It is located in
the physical address space, so it must be mapped into the logical address
space by defining an appropriate map.
ROM1
offset Physical address Name
----------------------------------------------------------
0000 FFA000-FFDFFF ROM1
4000 FF4000-FF5FFF @CRU>2700 Text-to-speech ROM/DSR
6000 FF4000-FF5FFF @CRU>1700 Hexbus DSR
The DSR portions have to be selected via the CRU bits >1700 or >2700.
Mapper
------
The mapper uses 4K pages (unlike the Geneve mapper with 8K pages) which
@ -200,22 +173,27 @@ Known Issues (MZ, 2010-11-07)
#include "emu.h"
#include "cpu/tms9900/tms9995.h"
#include "bus/ti99x/ti99defs.h"
#include "sound/sn76496.h"
#include "sound/wave.h"
#include "machine/tms9901.h"
#include "machine/tmc0430.h"
#include "imagedev/cassette.h"
#include "bus/ti99x/998board.h"
#include "bus/ti99x/videowrp.h"
#include "bus/ti99x/grom.h"
#include "bus/ti99x/gromport.h"
#include "bus/ti99x/joyport.h"
#include "bus/ti99_peb/peribox.h"
#include "softlist.h"
// Debugging
#define TRACE_READY 0
#define TRACE_INTERRUPTS 0
#define TRACE_RESET 0
#define TRACE_CRU 0
/*
@ -228,7 +206,8 @@ enum
READY_PBOX = 4,
READY_SOUND = 8,
READY_CART = 16,
READY_SPEECH = 32
READY_SPEECH = 32,
READY_MAINBOARD = 64
};
class ti99_8_state : public driver_device
@ -242,8 +221,7 @@ public:
m_peribox(*this, PERIBOX_TAG),
m_mainboard(*this, MAINBOARD8_TAG),
m_joyport(*this, JOYPORT_TAG),
m_video(*this, VIDEO_SYSTEM_TAG),
m_cassette(*this, "cassette") { }
m_cassette(*this, "cassette") { };
// Machine management
DECLARE_MACHINE_START(ti99_8);
@ -254,17 +232,17 @@ public:
DECLARE_WRITE8_MEMBER( cruwrite );
DECLARE_WRITE8_MEMBER( external_operation );
DECLARE_WRITE_LINE_MEMBER( clock_out );
DECLARE_WRITE_LINE_MEMBER( dbin_line );
// Connections from outside towards the CPU (callbacks)
DECLARE_WRITE_LINE_MEMBER( console_ready_mapper );
DECLARE_WRITE_LINE_MEMBER( console_ready_sound );
DECLARE_WRITE_LINE_MEMBER( console_ready_pbox );
DECLARE_WRITE_LINE_MEMBER( console_ready_cart );
DECLARE_WRITE_LINE_MEMBER( console_ready_grom );
DECLARE_WRITE_LINE_MEMBER( console_ready_speech );
DECLARE_WRITE_LINE_MEMBER( console_ready );
DECLARE_WRITE_LINE_MEMBER( console_reset );
DECLARE_WRITE_LINE_MEMBER( cpu_hold );
DECLARE_WRITE_LINE_MEMBER( notconnected );
// GROM clock (coming from Vaquerro)
DECLARE_WRITE_LINE_MEMBER( gromclk_in );
// Connections with the system interface chip 9901
DECLARE_WRITE_LINE_MEMBER( extint );
DECLARE_WRITE_LINE_MEMBER( video_interrupt );
@ -275,8 +253,6 @@ public:
DECLARE_WRITE_LINE_MEMBER(keyC1);
DECLARE_WRITE_LINE_MEMBER(keyC2);
DECLARE_WRITE_LINE_MEMBER(keyC3);
DECLARE_WRITE_LINE_MEMBER(CRUS);
DECLARE_WRITE_LINE_MEMBER(PTGEN);
DECLARE_WRITE_LINE_MEMBER(audio_gate);
DECLARE_WRITE_LINE_MEMBER(cassette_output);
DECLARE_WRITE_LINE_MEMBER(cassette_motor);
@ -288,9 +264,7 @@ private:
int m_keyboard_column;
// READY handling
int m_nready_combined;
int m_nready_prev;
void console_ready_join(int id, int state);
line_state m_ready_old;
// Latch for 9901 INT2, INT1 lines
line_state m_int1;
@ -303,7 +277,6 @@ private:
required_device<peribox_device> m_peribox;
required_device<mainboard8_device> m_mainboard;
required_device<joyport_device> m_joyport;
required_device<ti_video_device> m_video;
required_device<cassette_image_device> m_cassette;
};
@ -312,7 +285,7 @@ private:
job to the mapper completely.
*/
static ADDRESS_MAP_START(memmap, AS_PROGRAM, 8, ti99_8_state)
AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(MAINBOARD8_TAG, mainboard8_device, readm, writem )
AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(MAINBOARD8_TAG, mainboard8_device, read, write) AM_DEVSETOFFSET(MAINBOARD8_TAG, mainboard8_device, setoffset)
ADDRESS_MAP_END
/*
@ -432,89 +405,6 @@ static INPUT_PORTS_START(ti99_8)
INPUT_PORTS_END
/*****************************************************************************
Components
******************************************************************************/
#define region_sysgrom "sysgrom"
static GROM_CONFIG(grom0_config)
{
false, 0, region_sysgrom, 0x0000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom1_config)
{
false, 1, region_sysgrom, 0x2000, 0x1800, GROMFREQ
};
static GROM_CONFIG(grom2_config)
{
false, 2, region_sysgrom, 0x4000, 0x1800, GROMFREQ
};
/****************************************************
PASCAL groms, 3 libraries @ 8 GROMs
Do some macro tricks to keep writing effort low
*****************************************************/
#define region_gromlib1 "gromlib1"
#define region_gromlib2 "gromlib2"
#define region_gromlib3 "gromlib3"
#define MCFG_GROM_LIBRARY_ADD8(_tag, _config) \
MCFG_DEVICE_ADD(#_tag "0", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##0) \
MCFG_DEVICE_ADD(#_tag "1", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##1) \
MCFG_DEVICE_ADD(#_tag "2", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##2) \
MCFG_DEVICE_ADD(#_tag "3", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##3) \
MCFG_DEVICE_ADD(#_tag "4", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##4) \
MCFG_DEVICE_ADD(#_tag "5", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##5) \
MCFG_DEVICE_ADD(#_tag "6", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##6) \
MCFG_DEVICE_ADD(#_tag "7", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##7)
#define MCFG_GROM_LIBRARY_ADD3(_tag, _config) \
MCFG_DEVICE_ADD(#_tag "0", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##0) \
MCFG_DEVICE_ADD(#_tag "1", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##1) \
MCFG_DEVICE_ADD(#_tag "2", GROM, 0) \
MCFG_DEVICE_CONFIG(_config##2)
#define GROM_LIBRARY_CONFIG8(_conf, _region) \
static GROM_CONFIG(_conf##0) \
{ false, 0, _region, 0x0000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##1) \
{ false, 1, _region, 0x2000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##2) \
{ false, 2, _region, 0x4000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##3) \
{ false, 3, _region, 0x6000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##4) \
{ false, 4, _region, 0x8000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##5) \
{ false, 5, _region, 0xa000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##6) \
{ false, 6, _region, 0xc000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##7) \
{ false, 7, _region, 0xe000, 0x1800, GROMFREQ };
#define GROM_LIBRARY_CONFIG3(_conf, _region) \
static GROM_CONFIG(_conf##0) \
{ false, 0, _region, 0x0000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##1) \
{ false, 1, _region, 0x2000, 0x1800, GROMFREQ }; \
static GROM_CONFIG(_conf##2) \
{ false, 2, _region, 0x4000, 0x1800, GROMFREQ };
GROM_LIBRARY_CONFIG8(pascal1, region_gromlib1)
GROM_LIBRARY_CONFIG8(pascal2, region_gromlib2)
GROM_LIBRARY_CONFIG3(pascal3, region_gromlib3)
READ8_MEMBER( ti99_8_state::cruread )
{
@ -660,33 +550,6 @@ WRITE_LINE_MEMBER( ti99_8_state::keyC3 )
set_keyboard_column(3, state);
}
/*
Set 99/4A compatibility mode (CRUS=1)
*/
WRITE_LINE_MEMBER( ti99_8_state::CRUS )
{
m_mainboard->CRUS_set(state==ASSERT_LINE);
// In Armadillo mode, GROMs are located at f830; accordingly, the
// gromport must be reconfigured
if (state==ASSERT_LINE)
{
m_gromport->set_grom_base(0x9800, 0xfbf1);
}
else
{
m_gromport->set_grom_base(0xf830, 0xfff1);
}
}
/*
Set mapper /PTGEN. This is negative logic; we use PTGE as the positive logic signal.
*/
WRITE_LINE_MEMBER( ti99_8_state::PTGEN )
{
m_mainboard->PTGE_set(state==CLEAR_LINE);
}
/*
Control cassette tape unit motor (P6)
*/
@ -723,11 +586,11 @@ WRITE8_MEMBER( ti99_8_state::tms9901_interrupt )
/*****************************************************************************/
/*
set the state of TMS9901's INT2 (called by the tms9928 core)
set the state of TMS9901's INT2 (called by the VDP)
*/
WRITE_LINE_MEMBER( ti99_8_state::video_interrupt )
{
if (TRACE_INTERRUPTS) logerror("ti99_8: VDP int 2 on tms9901, level=%02x\n", state);
if (TRACE_INTERRUPTS) logerror("VDP int 2 on tms9901, level=%02x\n", state);
m_int2 = (line_state)state;
m_tms9901->set_single_int(2, state);
}
@ -737,82 +600,59 @@ WRITE_LINE_MEMBER( ti99_8_state::video_interrupt )
***********************************************************/
/*
We combine the incoming READY signals and propagate them to the CPU.
An alternative would be to let the CPU get the READY state, but this would
be a much higher overhead, as this happens in each clock tick.
Propagate READY signals to the CPU.
*/
void ti99_8_state::console_ready_join(int id, int state)
void ti99_8_state::console_ready(int state)
{
if (state==CLEAR_LINE)
m_nready_combined |= id;
else
m_nready_combined &= ~id;
if (TRACE_READY)
{
if (m_nready_prev != m_nready_combined) logerror("ti99_8: READY bits = %04x\n", ~m_nready_combined);
if (m_ready_old != state) logerror("READY = %d\n", state);
}
m_nready_prev = m_nready_combined;
m_cpu->ready_line(m_nready_combined==0);
m_ready_old = (line_state)state;
m_cpu->ready_line(state);
}
/*
Connections to the READY line. This might look a bit ugly; we need an
implementation of a "Wired AND" device.
*/
WRITE_LINE_MEMBER( ti99_8_state::console_ready_grom )
{
console_ready_join(READY_GROM, state);
}
WRITE_LINE_MEMBER( ti99_8_state::console_ready_mapper )
{
console_ready_join(READY_MAPPER, state);
}
WRITE_LINE_MEMBER( ti99_8_state::console_ready_pbox )
{
console_ready_join(READY_PBOX, state);
}
WRITE_LINE_MEMBER( ti99_8_state::console_ready_sound )
{
console_ready_join(READY_SOUND, state);
}
WRITE_LINE_MEMBER( ti99_8_state::console_ready_cart )
{
console_ready_join(READY_CART, state);
}
WRITE_LINE_MEMBER( ti99_8_state::console_ready_speech )
{
console_ready_join(READY_SPEECH, state);
}
/*
The RESET line leading to a reset of the CPU.
Enqueue a RESET signal.
*/
WRITE_LINE_MEMBER( ti99_8_state::console_reset )
{
if (TRACE_RESET) logerror("Incoming RESET line = %d\n", state);
if (machine().phase() != MACHINE_PHASE_INIT)
{
m_cpu->set_input_line(INT_9995_RESET, state);
m_video->reset_vdp(state);
// RESET the 9901
m_tms9901->rst1_line(state);
// Pull up the CRUS and PTGEN lines (9901 outputs have been deactivated, pull-up resistors on the board show effect)
m_mainboard->crus_in(TRUE); // assert
m_mainboard->ptgen_in(TRUE); // clear
// Setting ready to false so that automatic wait states are enabled
m_cpu->ready_line(CLEAR_LINE);
m_cpu->reset_line(ASSERT_LINE);
}
}
/*
The HOLD line leading to the CPU entering the HOLD state.
*/
WRITE_LINE_MEMBER( ti99_8_state::cpu_hold )
{
if (TRACE_INTERRUPTS) logerror("Incoming HOLD line = %d\n", state);
m_cpu->hold_line(state);
}
WRITE_LINE_MEMBER( ti99_8_state::extint )
{
if (TRACE_READY) logerror("ti99_8: EXTINT level = %02x\n", state);
if (TRACE_INTERRUPTS) logerror("EXTINT level = %02x\n", state);
m_int1 = (line_state)state;
m_tms9901->set_single_int(1, state);
}
WRITE_LINE_MEMBER( ti99_8_state::notconnected )
{
if (TRACE_READY) logerror("ti99_8: Setting a not connected line ... ignored\n");
if (TRACE_INTERRUPTS) logerror("Setting a not connected line ... ignored\n");
}
WRITE8_MEMBER( ti99_8_state::external_operation )
@ -821,7 +661,7 @@ WRITE8_MEMBER( ti99_8_state::external_operation )
if (offset == IDLE_OP) return;
else
{
logerror("ti99_4x: External operation %s not implemented on TI-99/8 board\n", extop[offset]);
logerror("External operation %s not implemented on TI-99/8 board\n", extop[offset]);
}
}
@ -833,119 +673,23 @@ WRITE_LINE_MEMBER( ti99_8_state::clock_out )
m_mainboard->clock_in(state);
}
/*****************************************************************************/
/*
Format:
Name, mode, stop, mask, select, write, read8z function, write8 function
Multiple devices may have the same select pattern; as in the real hardware,
care must be taken that only one device actually responds. In the case of
GROMs, each chip has an internal address counter and an ID, and the chip
only responds when the ID and the most significant 3 bits match.
NATIVE <-> CRUS=0
TI99EM <-> CRUS=1
PATGEN <-> PTGEN=1
CONT: Mapper continues iterating through devices
STOP: Mapper stops iterating when found
Access to the mapper registers is done directly in the mapper, not via
this list.
TODO: This should (must) be improved in terms of performance. Every single
memory access goes through the mapper. Either we use an ordered search list,
or we order the entries according to their frequency.
(I did this right now, putting the Pascal GROMs at the end.)
We should think about a set entry where devices with the same address
are collected as one single entry (think about the Pascal lib with 21 GROMs,
twice eight and once three of them on the same address).
Data bus in (DBIN) line from the CPU.
*/
#define PASCAL_GROM_LIB8(_tag, _addr) \
{ _tag "0", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "1", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "2", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "3", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "4", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "5", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "6", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "7", PATGEN, CONT, _addr, 0xfff1, 0x0000 }
#define PASCAL_GROM_LIB3(_tag, _addr) \
{ _tag "0", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "1", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \
{ _tag "2", PATGEN, CONT, _addr, 0xfff1, 0x0000 }
static const mapper8_list_entry mapper_devices[] =
WRITE_LINE_MEMBER( ti99_8_state::dbin_line )
{
// TI-99/4A mode (CRUS=1)
// Full/partial decoding has been verified on a real machine
// GROMs: According to the spec, the 99/8 supports up to 4 GROM libraries
// (99/4A supports 256 libraries)
// at 9800, 9804, 9808, 980c. Address counter access is at 9802,6,a,e. Write access +0400.
{ ROM0NAME, TI99EM, STOP, 0x0000, 0xe000, 0x0000 }, // 0000-1fff
{ TISOUND_TAG, TI99EM, STOP, 0x8400, 0xfff1, 0x0000 }, // 8400-840f
{ VIDEO_SYSTEM_TAG, TI99EM, STOP, 0x8800, 0xfff1, 0x0400 }, // 8800,8802 / 8c00,8c02
{ SPEECH_TAG, TI99EM, STOP, 0x9000, 0xfff1, 0x0400 }, // 9000-900f / 9400-940f
{ SRAMNAME, TI99EM, STOP, 0x8000, 0xf800, 0x0000 }, // 8000-87ff; must follow the sound generator
{ MAINBOARD8_TAG, TI99EM, STOP, 0x8810, 0xfff0, 0x0000 },
{ GROM0_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // 9800,2,4,...e/9c00,2,4,...e
{ GROM1_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // dto.
{ GROM2_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // dto. (GROMs are connected in parallel,
{ GROMPORT_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // dto. use internal address counter and id)
// TI-99/8 mode
// Full/partial decoding has been verified on a real machine
// Sound ports are at f800, f802, f804, ..., f80e
// VDP ports are (f810,f812), (f814,f816), (f818,f81a), (f81c,f81e)
// Note that unmapped GROM accesses (odd addresses like F831) return FF,
// not 00 as in our emulation, so that is not quite consistent, but tolerable ... I guess
{ SRAMNAME, NATIVE, STOP, 0xf000, 0xf800, 0x0000 }, // f000-f7ff
{ TISOUND_TAG, NATIVE, STOP, 0xf800, 0xfff1, 0x0000 }, // f800-f80e (even addresses)
{ VIDEO_SYSTEM_TAG, NATIVE, STOP, 0xf810, 0xfff1, 0x0000 }, // f810,2 (unlike 99/4A, no different read/write ports)
{ SPEECH_TAG, NATIVE, STOP, 0xf820, 0xfff1, 0x0000 }, // f820-f82f
{ MAINBOARD8_TAG, NATIVE, STOP, 0xf870, 0xfff0, 0x0000 },
{ GROM0_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 }, // f830-f83e (4 banks), no different read/write ports
{ GROM1_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 },
{ GROM2_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 },
{ GROMPORT_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 },
PASCAL_GROM_LIB8("pascal1_grom", 0xf840),
PASCAL_GROM_LIB8("pascal2_grom", 0xf850),
PASCAL_GROM_LIB3("pascal3_grom", 0xf860),
// Physical (need to pack this in here as well to keep config simple)
// but these lines will be put into a separate list
{ DRAMNAME, PHYSIC, STOP, 0x000000, 0xff0000, 0x000000 }, // 000000-00ffff 64 KiB DRAM
{ PCODENAME, PHYSIC, STOP, 0xf00000, 0xffc000, 0x000000 }, // f00000-f03fff P-Code ROM
{ MAINBOARD8_TAG, PHYSIC, CONT, 0xff4000, 0xffe000, 0x000000 }, // ff4000-ff5fff Internal DSR
{ GROMPORT_TAG, PHYSIC, STOP, 0xff6000, 0xffe000, 0x000000 }, // ff6000-ff7fff Cartridge ROM space
{ GROMPORT_TAG, PHYSIC, STOP, 0xff8000, 0xffe000, 0x000000 }, // ff8000-ff9fff Cartridge ROM space
{ ROM1A0NAME, PHYSIC, STOP, 0xffa000, 0xffe000, 0x000000 }, // ffa000-ffbfff ROM1
{ ROM1C0NAME, PHYSIC, STOP, 0xffc000, 0xffe000, 0x000000 }, // ffc000-ffdfff ROM1
{ INTSNAME, PHYSIC, STOP, 0xffe000, 0xfffff0, 0x000000 }, // ffe000-ffe00f Interrupt level sense
{ PERIBOX_TAG, PHYSIC, STOP, 0x000000, 0x000000, 0x000000 }, // Peripheral Expansion Box
{ nullptr, 0, 0, 0, 0, 0 }
};
static MAPPER8_CONFIG( mapper_conf )
{
mapper_devices
};
m_mainboard->dbin_in(state);
}
MACHINE_START_MEMBER(ti99_8_state,ti99_8)
{
m_nready_combined = 0;
m_peribox->senila(CLEAR_LINE);
m_peribox->senilb(CLEAR_LINE);
// m_mainboard->set_gromport(m_gromport);
// Need to configure the speech ROM for inverse bit order
speechrom_device* mem = subdevice<speechrom_device>(SPEECHROM_REG);
mem->set_reverse_bit_order(true);
}
MACHINE_RESET_MEMBER(ti99_8_state, ti99_8)
@ -956,9 +700,7 @@ MACHINE_RESET_MEMBER(ti99_8_state, ti99_8)
// state on external memory accesses
m_cpu->ready_line(CLEAR_LINE);
// But we assert the line here so that the system starts running
m_nready_combined = 0;
m_gromport->set_grom_base(0x9800, 0xfff1);
// m_gromport->set_grom_base(0x9800, 0xfff1);
// Clear INT1 and INT2 latch
m_int1 = CLEAR_LINE;
@ -972,62 +714,92 @@ static MACHINE_CONFIG_START( ti99_8, ti99_8_state )
MCFG_TMS99xx_ADD("maincpu", TMS9995_MP9537, XTAL_10_738635MHz, memmap, crumap)
MCFG_TMS9995_EXTOP_HANDLER( WRITE8(ti99_8_state, external_operation) )
MCFG_TMS9995_CLKOUT_HANDLER( WRITELINE(ti99_8_state, clock_out) )
MCFG_TMS9995_DBIN_HANDLER( WRITELINE(ti99_8_state, dbin_line) )
MCFG_TMS9995_HOLDA_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, holda_line) )
MCFG_MACHINE_START_OVERRIDE(ti99_8_state, ti99_8 )
MCFG_MACHINE_RESET_OVERRIDE(ti99_8_state, ti99_8 )
/* Main board */
// 9901 configuration
MCFG_DEVICE_ADD(TMS9901_TAG, TMS9901, XTAL_10_738635MHz/4.0)
MCFG_TMS9901_READBLOCK_HANDLER( READ8(ti99_8_state, read_by_9901) )
MCFG_TMS9901_P0_HANDLER( WRITELINE( ti99_8_state, keyC0) )
MCFG_TMS9901_P1_HANDLER( WRITELINE( ti99_8_state, keyC1) )
MCFG_TMS9901_P2_HANDLER( WRITELINE( ti99_8_state, keyC2) )
MCFG_TMS9901_P3_HANDLER( WRITELINE( ti99_8_state, keyC3) )
MCFG_TMS9901_P4_HANDLER( WRITELINE( ti99_8_state, CRUS) )
MCFG_TMS9901_P5_HANDLER( WRITELINE( ti99_8_state, PTGEN) )
MCFG_TMS9901_P4_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, mainboard8_device, crus_in) )
MCFG_TMS9901_P5_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, mainboard8_device, ptgen_in) )
MCFG_TMS9901_P6_HANDLER( WRITELINE( ti99_8_state, cassette_motor) )
MCFG_TMS9901_P8_HANDLER( WRITELINE( ti99_8_state, audio_gate) )
MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_8_state, cassette_output) )
MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_8_state, tms9901_interrupt) )
MCFG_MAINBOARD8_ADD( MAINBOARD8_TAG, mapper_conf )
MCFG_MAINBOARD8_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_mapper))
MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG )
MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_8_state, console_ready_cart) )
// Mainboard with custom chips
MCFG_DEVICE_ADD(MAINBOARD8_TAG, MAINBOARD8, 0)
MCFG_MAINBOARD8_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready))
MCFG_MAINBOARD8_RESET_CALLBACK(WRITELINE(ti99_8_state, console_reset))
MCFG_MAINBOARD8_HOLD_CALLBACK(WRITELINE(ti99_8_state, cpu_hold))
MCFG_GROMPORT8_ADD( GROMPORT_TAG )
MCFG_GROMPORT_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready) )
MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_8_state, console_reset) )
/* Peripheral expansion box */
/* Software list */
MCFG_SOFTWARE_LIST_ADD("cart_list_ti99", "ti99_cart")
// Peripheral expansion box
MCFG_DEVICE_ADD( PERIBOX_TAG, PERIBOX_998, 0)
MCFG_PERIBOX_INTA_HANDLER( WRITELINE(ti99_8_state, extint) )
MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_8_state, notconnected) )
MCFG_PERIBOX_READY_HANDLER( WRITELINE(ti99_8_state, console_ready_pbox) )
MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, pbox_ready) )
/* Sound hardware */
MCFG_TI_SOUND_76496_ADD( TISOUND_TAG )
MCFG_TI_SOUND_READY_HANDLER( WRITELINE(ti99_8_state, console_ready_sound) )
// Sound hardware
MCFG_SPEAKER_STANDARD_MONO("sound_out")
MCFG_SOUND_ADD(TISOUNDCHIP_TAG, SN76496, 3579545) /* 3.579545 MHz */
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "sound_out", 0.75)
MCFG_SN76496_READY_HANDLER(DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, sound_ready))
/* Cassette drives */
// Speech hardware
// Note: SPEECHROM uses its tag for referencing the region
MCFG_DEVICE_ADD(SPEECHROM_REG, SPEECHROM, 0)
MCFG_SPEAKER_STANDARD_MONO("speech_out")
MCFG_SOUND_ADD(SPEECHSYN_TAG, CD2501ECD, 640000L)
MCFG_TMS52XX_READYQ_HANDLER(DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, speech_ready))
MCFG_TMS52XX_SPEECHROM(SPEECHROM_REG)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speech_out", 0.50)
// Cassette drive
MCFG_SPEAKER_STANDARD_MONO("cass_out")
MCFG_CASSETTE_ADD( "cassette" )
MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette")
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25)
/* Console GROMs */
MCFG_GROM_ADD( GROM0_TAG, grom0_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_grom))
MCFG_GROM_ADD( GROM1_TAG, grom1_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_grom))
MCFG_GROM_ADD( GROM2_TAG, grom2_config )
MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_grom))
// GROM library
MCFG_GROM_ADD( SYSGROM0_TAG, 0, SYSGROM_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready))
MCFG_GROM_ADD( SYSGROM1_TAG, 1, SYSGROM_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready))
MCFG_GROM_ADD( SYSGROM2_TAG, 2, SYSGROM_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready))
/* Pascal GROM libraries. */
MCFG_GROM_LIBRARY_ADD8(pascal1_grom, pascal1)
MCFG_GROM_LIBRARY_ADD8(pascal2_grom, pascal2)
MCFG_GROM_LIBRARY_ADD3(pascal3_grom, pascal3)
MCFG_GROM_ADD( GLIB10_TAG, 0, GROMLIB1_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
MCFG_GROM_ADD( GLIB11_TAG, 1, GROMLIB1_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
MCFG_GROM_ADD( GLIB12_TAG, 2, GROMLIB1_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
MCFG_GROM_ADD( GLIB13_TAG, 3, GROMLIB1_REG, 0x6000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
MCFG_GROM_ADD( GLIB14_TAG, 4, GROMLIB1_REG, 0x8000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
MCFG_GROM_ADD( GLIB15_TAG, 5, GROMLIB1_REG, 0xa000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
MCFG_GROM_ADD( GLIB16_TAG, 6, GROMLIB1_REG, 0xc000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
MCFG_GROM_ADD( GLIB17_TAG, 7, GROMLIB1_REG, 0xe000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready))
/* Devices */
MCFG_DEVICE_ADD(SPEECH_TAG, SPEECH8, 0)
MCFG_SPEECH8_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_speech))
MCFG_GROM_ADD( GLIB20_TAG, 0, GROMLIB2_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB21_TAG, 1, GROMLIB2_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB22_TAG, 2, GROMLIB2_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB23_TAG, 3, GROMLIB2_REG, 0x6000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB24_TAG, 4, GROMLIB2_REG, 0x8000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB25_TAG, 5, GROMLIB2_REG, 0xa000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB26_TAG, 6, GROMLIB2_REG, 0xc000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB27_TAG, 7, GROMLIB2_REG, 0xe000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready))
MCFG_GROM_ADD( GLIB30_TAG, 0, GROMLIB3_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p3_grom_ready))
MCFG_GROM_ADD( GLIB31_TAG, 1, GROMLIB3_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p3_grom_ready))
MCFG_GROM_ADD( GLIB32_TAG, 2, GROMLIB3_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p3_grom_ready))
// Joystick port
MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG )
@ -1037,14 +809,24 @@ MACHINE_CONFIG_END
TI-99/8 US version (NTSC, 60 Hz)
*/
static MACHINE_CONFIG_DERIVED( ti99_8_60hz, ti99_8 )
MCFG_TI998_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9118, 0x4000, ti99_8_state, video_interrupt)
// Video hardware
MCFG_DEVICE_ADD( VDP_TAG, TMS9118, XTAL_10_738635MHz / 2 )
MCFG_TMS9928A_VRAM_SIZE(0x4000)
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_8_state, video_interrupt))
MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG )
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
MACHINE_CONFIG_END
/*
TI-99/8 European version (PAL, 50 Hz)
*/
static MACHINE_CONFIG_DERIVED( ti99_8_50hz, ti99_8 )
MCFG_TI998_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9129, 0x4000, ti99_8_state, video_interrupt)
// Video hardware
MCFG_DEVICE_ADD( VDP_TAG, TMS9129, XTAL_10_738635MHz / 2 )
MCFG_TMS9928A_VRAM_SIZE(0x4000)
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_8_state,video_interrupt))
MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG )
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
MACHINE_CONFIG_END
/*
@ -1055,11 +837,11 @@ MACHINE_CONFIG_END
*/
ROM_START(ti99_8)
// Logical (CPU) memory space: ROM0
ROM_REGION(0x2000, ROM0_TAG, 0)
ROM_REGION(0x2000, ROM0_REG, 0)
ROM_LOAD("u4_rom0.bin", 0x0000, 0x2000, CRC(901eb8d6) SHA1(13190c5e834baa9c0a70066b566cfcef438ed88a))
// Physical memory space (mapped): ROM1
ROM_REGION(0x8000, ROM1_TAG, 0)
ROM_REGION(0x8000, ROM1_REG, 0)
ROM_LOAD("u25_rom1.bin", 0x0000, 0x8000, CRC(b574461a) SHA1(42c6aed44802cfabdd26b565d6e5ddfcd689f11e))
// Physical memory space (mapped): P-Code ROM
@ -1069,20 +851,20 @@ ROM_START(ti99_8)
// the required select line for this ROM on the available schematics, so
// they seem to be from the earlier version. The location in the address
// space was determined by ROM disassembly.
ROM_REGION(0x8000, PCODEROM_TAG, 0)
ROM_REGION(0x8000, PASCAL_REG, 0)
ROM_LOAD("u25a_pas.bin", 0x0000, 0x4000, CRC(d7ed6dd6) SHA1(32212ce6426ceccbff73d342d4a3ef699c0ae1e4))
// System GROMs. 3 chips @ f830
// The schematics do not enumerate the circuits but only talk about
// "circuits on board" (COB) so we name the GROMs as gM_N.bin where M is the
// ID (0-7) and N is the access port in the logical address space.
ROM_REGION(0x6000, region_sysgrom, 0)
ROM_REGION(0x6000, SYSGROM_REG, 0)
ROM_LOAD("g0_f830.bin", 0x0000, 0x1800, CRC(1026db60) SHA1(7327095bf4f390476e69d9fd8424e98ea1f2325a))
ROM_LOAD("g1_f830.bin", 0x2000, 0x1800, CRC(93a43d65) SHA1(19be8a07d674bc7554c2bc9c7a5725d81e888e6e))
ROM_LOAD("g2_f830.bin", 0x4000, 0x1800, CRC(06f2b901) SHA1(f65e0fcb2c63e230b4a9563c72f91259b94ce955))
// TTS & Pascal library. 8 chips @ f840
ROM_REGION(0x10000, region_gromlib1, 0)
ROM_REGION(0x10000, GROMLIB1_REG, 0)
ROM_LOAD("g0_f840.bin", 0x0000, 0x1800, CRC(44501071) SHA1(4b5ef7f1aa43a87e7ae4f02090944be5c39b1f26))
ROM_LOAD("g1_f840.bin", 0x2000, 0x1800, CRC(5a271d9e) SHA1(bb95befa2ffba2cc17ac437386e069e8ff621248))
ROM_LOAD("g2_f840.bin", 0x4000, 0x1800, CRC(d52502df) SHA1(17063e33ee8709d0df8030f38bb92c4322d55e1e))
@ -1093,7 +875,7 @@ ROM_START(ti99_8)
ROM_LOAD("g7_f840.bin", 0xE000, 0x1800, CRC(3a9d20df) SHA1(1e6f9f8ec7df4b997a7579be742d0a7d54bc8763))
// Pascal library. 8 chips @ f850
ROM_REGION(0x10000, region_gromlib2, 0)
ROM_REGION(0x10000, GROMLIB2_REG, 0)
ROM_LOAD("g0_f850.bin", 0x0000, 0x1800, CRC(2d948672) SHA1(cf15912d6dae5a450e0cfd796aa36ea5e521dc56))
ROM_LOAD("g1_f850.bin", 0x2000, 0x1800, CRC(7d64a842) SHA1(d5884bb2af21c8027311478ee506beac6f46203d))
ROM_LOAD("g2_f850.bin", 0x4000, 0x1800, CRC(e5ed8900) SHA1(03826882ce10fb5a6b3a9ccc85d3d1fe51979d0b))
@ -1104,17 +886,15 @@ ROM_START(ti99_8)
ROM_LOAD("g7_f850.bin", 0xE000, 0x1800, CRC(71534098) SHA1(75e87123efde885e27dd749e07cb189eb2cc45a8))
// Pascal library. 3 chips @ f860
ROM_REGION(0x6000, region_gromlib3, 0)
ROM_REGION(0x6000, GROMLIB3_REG, 0)
ROM_LOAD("g0_f860.bin", 0x0000, 0x1800, CRC(0ceef210) SHA1(b89957fbff094b758746391a69dea6907c66b950))
ROM_LOAD("g1_f860.bin", 0x2000, 0x1800, CRC(fc87de25) SHA1(4695b7f979f59a01ec16c55e4587c3379482b658))
ROM_LOAD("g2_f860.bin", 0x4000, 0x1800, CRC(e833e350) SHA1(6ffe501981a1112be1af596a489d96e287fc6be5))
// Built-in RAM
ROM_REGION(SRAM_SIZE, SRAM_TAG, 0)
ROM_FILL(0x0000, SRAM_SIZE, 0x00)
ROM_REGION(DRAM_SIZE, DRAM_TAG, 0)
ROM_FILL(0x0000, DRAM_SIZE, 0x00)
// Speech ROM
ROM_REGION(0x8000, SPEECHROM_REG, 0)
ROM_LOAD("cd2325a.vsm", 0x0000, 0x4000, CRC(1f58b571) SHA1(0ef4f178716b575a1c0c970c56af8a8d97561ffe))
ROM_LOAD("cd2326a.vsm", 0x4000, 0x4000, CRC(65d00401) SHA1(a367242c2c96cebf0e2bf21862f3f6734b2b3020))
ROM_END
#define rom_ti99_8e rom_ti99_8