mirror of
https://github.com/holub/mame
synced 2025-04-25 09:50:04 +03:00
(MESS) Using the split addressing (setaddress ... read/write)
This commit is contained in:
parent
cda0ac67fb
commit
419b6f0158
@ -240,6 +240,8 @@ public:
|
||||
DECLARE_WRITE_LINE_MEMBER(video_wait_states);
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(clock_out);
|
||||
DECLARE_WRITE_LINE_MEMBER(dbin_line);
|
||||
|
||||
DECLARE_WRITE8_MEMBER(external_operation);
|
||||
|
||||
DECLARE_WRITE8_MEMBER(tms9901_interrupt);
|
||||
@ -284,7 +286,7 @@ private:
|
||||
*/
|
||||
|
||||
static ADDRESS_MAP_START(memmap, AS_PROGRAM, 8, geneve_state)
|
||||
AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(GMAPPER_TAG, geneve_mapper_device, readm, writem)
|
||||
AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(GMAPPER_TAG, geneve_mapper_device, readm, writem) AM_DEVSETOFFSET(GMAPPER_TAG, geneve_mapper_device, setoffset)
|
||||
ADDRESS_MAP_END
|
||||
|
||||
/*
|
||||
@ -604,7 +606,7 @@ WRITE_LINE_MEMBER( geneve_state::intb )
|
||||
|
||||
WRITE_LINE_MEMBER( geneve_state::ext_ready )
|
||||
{
|
||||
if (VERBOSE>6) LOG("ti99_8: READY level (ext) =%02x\n", state);
|
||||
if (VERBOSE>6) LOG("geneve: READY level (ext) = %02x\n", state);
|
||||
m_ready_line = state;
|
||||
m_cpu->set_ready((m_ready_line == ASSERT_LINE && m_ready_line1 == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE);
|
||||
}
|
||||
@ -670,13 +672,21 @@ WRITE_LINE_MEMBER( geneve_state::clock_out )
|
||||
m_mapper->clock_in(state);
|
||||
}
|
||||
|
||||
/*
|
||||
DBIN line from the CPU. Used to control wait state generation.
|
||||
*/
|
||||
WRITE_LINE_MEMBER( geneve_state::dbin_line )
|
||||
{
|
||||
m_mapper->dbin(state);
|
||||
}
|
||||
|
||||
static TMS9995_CONFIG( geneve_processor_config )
|
||||
{
|
||||
DEVCB_DRIVER_MEMBER(geneve_state, external_operation),
|
||||
DEVCB_NULL, // Instruction acquisition
|
||||
DEVCB_DRIVER_LINE_MEMBER(geneve_state, clock_out),
|
||||
DEVCB_NULL, // wait
|
||||
DEVCB_NULL, // HOLDA
|
||||
DEVCB_DRIVER_LINE_MEMBER(geneve_state, dbin_line), // DBIN
|
||||
INTERNAL_RAM, // use internal RAM
|
||||
NO_OVERFLOW_INT // The generally available versions of TMS9995 have a deactivated overflow interrupt
|
||||
};
|
||||
|
@ -89,6 +89,7 @@ public:
|
||||
DECLARE_READ8_MEMBER( interrupt_level );
|
||||
DECLARE_READ_LINE_MEMBER( ready_connect );
|
||||
DECLARE_WRITE_LINE_MEMBER( clock_out );
|
||||
DECLARE_WRITE_LINE_MEMBER( dbin_line );
|
||||
|
||||
DECLARE_INPUT_CHANGED_MEMBER( load_interrupt );
|
||||
TIMER_DEVICE_CALLBACK_MEMBER(ti99_4ev_hblank_interrupt);
|
||||
@ -601,6 +602,14 @@ WRITE_LINE_MEMBER( ti99_4x_state::clock_out )
|
||||
m_datamux->clock_in(state);
|
||||
}
|
||||
|
||||
/*
|
||||
Data bus in (DBIN) line from the CPU.
|
||||
*/
|
||||
WRITE_LINE_MEMBER( ti99_4x_state::dbin_line )
|
||||
{
|
||||
m_datamux->dbin_in(state);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
@ -822,7 +831,8 @@ static TMS99xx_CONFIG( ti99_cpuconf )
|
||||
DEVCB_NULL, // Instruction acquisition
|
||||
DEVCB_DRIVER_LINE_MEMBER(ti99_4x_state, clock_out),
|
||||
DEVCB_NULL, // wait
|
||||
DEVCB_NULL // Hold acknowledge
|
||||
DEVCB_NULL, // Hold acknowledge
|
||||
DEVCB_DRIVER_LINE_MEMBER(ti99_4x_state, dbin_line) // data bus in
|
||||
};
|
||||
|
||||
static JOYPORT_CONFIG( joyport4_60 )
|
||||
|
@ -215,7 +215,7 @@ Known Issues (MZ, 2010-11-07)
|
||||
#include "machine/ti99/gromport.h"
|
||||
#include "machine/ti99/joyport.h"
|
||||
|
||||
#define VERBOSE 0
|
||||
#define VERBOSE 1
|
||||
#define LOG logerror
|
||||
|
||||
class ti99_8_state : public driver_device
|
||||
@ -630,6 +630,14 @@ WRITE_LINE_MEMBER( ti99_8_state::keyC3 )
|
||||
WRITE_LINE_MEMBER( ti99_8_state::CRUS )
|
||||
{
|
||||
m_mapper->CRUS_set(state==ASSERT_LINE);
|
||||
if (state==ASSERT_LINE)
|
||||
{
|
||||
m_gromport->set_grom_base(0x9800, 0xfbf1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_gromport->set_grom_base(0xf830, 0xfff1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -795,8 +803,8 @@ static TMS9995_CONFIG( ti99_8_processor_config )
|
||||
DEVCB_DRIVER_MEMBER(ti99_8_state, external_operation),
|
||||
DEVCB_NULL, // Instruction acquisition
|
||||
DEVCB_DRIVER_LINE_MEMBER(ti99_8_state, clock_out),
|
||||
DEVCB_NULL, // wait
|
||||
DEVCB_NULL, // HOLDA
|
||||
DEVCB_NULL, // DBIN
|
||||
NO_INTERNAL_RAM,
|
||||
NO_OVERFLOW_INT
|
||||
};
|
||||
@ -962,6 +970,7 @@ void ti99_8_state::machine_reset()
|
||||
|
||||
// But we assert the line here so that the system starts running
|
||||
m_ready_line = m_ready_line1 = ASSERT_LINE;
|
||||
m_gromport->set_grom_base(0x9800, 0xfff1);
|
||||
}
|
||||
|
||||
static MACHINE_CONFIG_START( ti99_8_60hz, ti99_8_state )
|
||||
|
@ -824,7 +824,8 @@ static TMS99xx_CONFIG( cpuconf )
|
||||
DEVCB_NULL, // Instruction acquisition
|
||||
DEVCB_NULL, // Clock out
|
||||
DEVCB_NULL, // wait
|
||||
DEVCB_NULL // Hold acknowledge
|
||||
DEVCB_NULL, // Hold acknowledge
|
||||
DEVCB_NULL // DBIN
|
||||
};
|
||||
|
||||
static MACHINE_CONFIG_START( tm990_189, tm990189_state )
|
||||
|
@ -120,54 +120,48 @@ void ti99_datamux_device::write_all(address_space& space, UINT16 addr, UINT8 val
|
||||
}
|
||||
}
|
||||
|
||||
void ti99_datamux_device::setaddress_all(address_space& space, UINT16 addr)
|
||||
{
|
||||
attached_device *dev = m_devices.first();
|
||||
while (dev != NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
|
||||
TODO: Check two-pass operation
|
||||
*/
|
||||
READ16_MEMBER( ti99_datamux_device::read )
|
||||
{
|
||||
UINT8 hbyte = 0;
|
||||
UINT16 addr = (offset << 1);
|
||||
|
||||
// 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_use32k)
|
||||
if (m_base32k != 0)
|
||||
{
|
||||
UINT16 base = 0;
|
||||
if ((addr & 0xe000)==0x2000) base = 0x1000;
|
||||
if (((addr & 0xe000)==0xa000) || ((addr & 0xc000)==0xc000)) base = 0x4000;
|
||||
|
||||
if (base != 0)
|
||||
{
|
||||
UINT16 reply = m_ram16b[offset-base];
|
||||
return reply & mem_mask;
|
||||
}
|
||||
UINT16 reply = m_ram16b[offset-m_base32k];
|
||||
return reply & mem_mask;
|
||||
}
|
||||
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 (VERBOSE>3) LOG("datamux: read even byte from address %04x -> %02x\n", m_addr_buf, hbyte);
|
||||
|
||||
// Read the odd address into the latch
|
||||
read_all(space, addr+1, &m_latch);
|
||||
|
||||
// Reading the even address now (addr)
|
||||
read_all(space, addr, &hbyte);
|
||||
|
||||
// Insert four wait states and let CPU enter wait state
|
||||
// The counter in the real console is implemented by a shift register
|
||||
// that is triggered on a memory access
|
||||
|
||||
// We cannot split the wait states between the read accesses before we
|
||||
// have a split-phase read access in the core
|
||||
m_waitcount = 6;
|
||||
m_ready(CLEAR_LINE);
|
||||
|
||||
// use the latch and the currently read byte and put it on the 16bit bus
|
||||
return ((hbyte<<8) | m_latch) & mem_mask;
|
||||
return ((hbyte<<8) | m_latch) & mem_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -175,57 +169,117 @@ READ16_MEMBER( ti99_datamux_device::read )
|
||||
*/
|
||||
WRITE16_MEMBER( ti99_datamux_device::write )
|
||||
{
|
||||
UINT16 addr = (offset << 1);
|
||||
|
||||
// Although MESS allows for using mem_mask to address parts of the
|
||||
// data bus, this is not used in the TMS implementation. In fact, all
|
||||
// accesses are true 16-bit accesses. We check for mem_mask always
|
||||
// being ffff.
|
||||
|
||||
// printf("write addr=%04x, value=%04x\n", addr, data);
|
||||
|
||||
// Handle the internal 32K expansion
|
||||
if (m_use32k)
|
||||
if (m_base32k != 0)
|
||||
{
|
||||
UINT16 base = 0;
|
||||
if ((addr & 0xe000)==0x2000) base = 0x1000;
|
||||
if (((addr & 0xe000)==0xa000) || ((addr & 0xc000)==0xc000)) base = 0x4000;
|
||||
|
||||
if (base != 0)
|
||||
{
|
||||
m_ram16b[offset-base] = data;
|
||||
return;
|
||||
}
|
||||
m_ram16b[offset-m_base32k] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise the datamux is in normal operation which means it puts
|
||||
// the even value into the latch and outputs the odd value now.
|
||||
m_latch = (data >> 8) & 0xff;
|
||||
|
||||
// write odd byte
|
||||
write_all(space, addr+1, data & 0xff);
|
||||
// write even byte
|
||||
write_all(space, addr, (data>>8) & 0xff);
|
||||
|
||||
// Insert four wait states and let CPU enter wait state
|
||||
m_waitcount = 6;
|
||||
m_ready(CLEAR_LINE);
|
||||
// write odd byte
|
||||
if (VERBOSE>3) LOG("datamux: write odd byte to address %04x <- %02x\n", m_addr_buf+1, data & 0xff);
|
||||
write_all(space, m_addr_buf+1, data & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the memory access starts by setting the address bus. From that
|
||||
point on, we suspend the CPU until all operations are done.
|
||||
*/
|
||||
SETOFFSET_MEMBER( ti99_datamux_device::setoffset )
|
||||
{
|
||||
if (VERBOSE>6) LOG("set address %04x\n", offset << 1);
|
||||
if (VERBOSE>6) LOG("datamux: set address %04x\n", offset << 1);
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Suspend the CPU if not using the 32K
|
||||
if (m_base32k == 0)
|
||||
{
|
||||
// propagate the setaddress operation
|
||||
// First the odd address
|
||||
setaddress_all(space, m_addr_buf+1);
|
||||
m_ready(CLEAR_LINE);
|
||||
}
|
||||
else m_waitcount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
The datamux is connected to the clock line in order to operate
|
||||
the wait state counter.
|
||||
the wait state counter and to read/write the bytes.
|
||||
*/
|
||||
void ti99_datamux_device::clock_in(int clock)
|
||||
{
|
||||
if (clock==ASSERT_LINE && m_waitcount!=0)
|
||||
// return immediately if the datamux is currently inactive
|
||||
if (m_waitcount>0)
|
||||
{
|
||||
m_waitcount--;
|
||||
if (m_waitcount==0) m_ready(ASSERT_LINE);
|
||||
if (VERBOSE>6) LOG("datamux: wait count %d\n", m_waitcount);
|
||||
if (m_read_mode)
|
||||
{
|
||||
// Reading
|
||||
if (clock==ASSERT_LINE)
|
||||
{ // raising edge
|
||||
m_waitcount--;
|
||||
if (m_waitcount==0) m_ready(ASSERT_LINE);
|
||||
if (m_waitcount==2)
|
||||
{
|
||||
// read odd byte
|
||||
read_all(*m_spacep, m_addr_buf+1, &m_latch);
|
||||
if (VERBOSE>3) LOG("datamux: read odd byte from address %04x -> %02x\n", m_addr_buf+1, m_latch);
|
||||
// do the setaddress for the even address
|
||||
setaddress_all(*m_spacep, m_addr_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (clock==ASSERT_LINE)
|
||||
{ // raising edge
|
||||
m_waitcount--;
|
||||
if (m_waitcount==0) m_ready(ASSERT_LINE);
|
||||
}
|
||||
else
|
||||
{ // falling edge
|
||||
if (m_waitcount==2)
|
||||
{
|
||||
// do the setaddress for the even address
|
||||
setaddress_all(*m_spacep, m_addr_buf);
|
||||
// write even byte
|
||||
if (VERBOSE>3) LOG("datamux: write even byte to address %04x <- %02x\n", m_addr_buf, m_latch);
|
||||
write_all(*m_spacep, m_addr_buf, m_latch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ti99_datamux_device::dbin_in(int state)
|
||||
{
|
||||
m_read_mode = (state==ASSERT_LINE);
|
||||
if (VERBOSE>6) LOG("datamux: data bus in = %d\n", m_read_mode? 1:0 );
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
DEVICE LIFECYCLE FUNCTIONS
|
||||
***************************************************************************/
|
||||
@ -308,6 +362,8 @@ void ti99_datamux_device::device_reset(void)
|
||||
|
||||
m_waitcount = 0;
|
||||
m_latch = 0;
|
||||
|
||||
m_read_mode = true;
|
||||
}
|
||||
|
||||
INPUT_PORTS_START( datamux )
|
||||
|
@ -73,6 +73,7 @@ public:
|
||||
DECLARE_SETOFFSET_MEMBER( setoffset );
|
||||
|
||||
void clock_in(int state);
|
||||
void dbin_in(int state);
|
||||
|
||||
protected:
|
||||
/* Constructor */
|
||||
@ -82,15 +83,27 @@ protected:
|
||||
virtual ioport_constructor device_input_ports() const;
|
||||
|
||||
private:
|
||||
// Keeps the address space pointer
|
||||
address_space* m_spacep;
|
||||
|
||||
// Common read routine
|
||||
void read_all(address_space& space, UINT16 addr, UINT8 *target);
|
||||
|
||||
// Common write routine
|
||||
void write_all(address_space& space, UINT16 addr, UINT8 value);
|
||||
|
||||
// Common set address method
|
||||
void setaddress_all(address_space& space, UINT16 addr);
|
||||
|
||||
// Ready line to the CPU
|
||||
devcb_resolved_write_line m_ready;
|
||||
|
||||
/* 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;
|
||||
|
||||
@ -106,6 +119,9 @@ private:
|
||||
/* 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;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -135,10 +135,12 @@ public:
|
||||
|
||||
DECLARE_READ8_MEMBER( readm );
|
||||
DECLARE_WRITE8_MEMBER( writem );
|
||||
DECLARE_SETOFFSET_MEMBER( setoffset );
|
||||
|
||||
DECLARE_INPUT_CHANGED_MEMBER( gm_changed );
|
||||
|
||||
void clock_in(int state);
|
||||
void dbin(int state);
|
||||
|
||||
protected:
|
||||
virtual void device_start();
|
||||
@ -157,6 +159,8 @@ private:
|
||||
bool m_video_waitstates;
|
||||
bool m_extra_waitstates;
|
||||
|
||||
bool m_read_mode;
|
||||
|
||||
// Mapper function
|
||||
bool m_geneve_mode;
|
||||
bool m_direct_mode;
|
||||
@ -166,6 +170,11 @@ private:
|
||||
bool m_cartridge7_writable;
|
||||
int m_map[8];
|
||||
|
||||
int m_mapdecode;
|
||||
|
||||
int m_offset;
|
||||
int m_physaddr;
|
||||
|
||||
// Genmod modifications
|
||||
bool m_turbo;
|
||||
bool m_genmod;
|
||||
|
@ -54,6 +54,7 @@ ti998_mapper_device::ti998_mapper_device(const machine_config &mconfig, const ch
|
||||
CRU access
|
||||
***************************************************************************/
|
||||
|
||||
#define HEXBUS_CRU_BASE 0x1700
|
||||
#define MAPPER_CRU_BASE 0x2700
|
||||
|
||||
void ti998_mapper_device::crureadz(offs_t offset, UINT8 *value)
|
||||
@ -83,6 +84,19 @@ void ti998_mapper_device::cruwrite(offs_t offset, UINT8 data)
|
||||
machine().schedule_soft_reset();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((offset & 0xff00)==HEXBUS_CRU_BASE)
|
||||
{
|
||||
if (VERBOSE>5) LOG("mapper8: Set CRU>%04x (Hexbus) to %d\n",offset,data);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((offset & 0xff00)>=0x0100)
|
||||
{
|
||||
if (VERBOSE>5) LOG("mapper8: Set CRU>%04x (unknown) to %d\n",offset,data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,6 +248,7 @@ void ti998_mapper_device::mapwrite(int offset, UINT8 data)
|
||||
int ptr = (bankindx << 6);
|
||||
m_pas_offset[i] = (m_sram[(i<<2) + ptr] << 24) | (m_sram[(i<<2)+ ptr+1] << 16)
|
||||
| (m_sram[(i<<2) + ptr+2] << 8) | (m_sram[(i<<2) + ptr+3]);
|
||||
if (VERBOSE>7) LOG("mapper8: load %d=%08x\n", i, m_pas_offset[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -247,6 +262,7 @@ void ti998_mapper_device::mapwrite(int offset, UINT8 data)
|
||||
m_sram[(i<<2) + ptr +1] = (m_pas_offset[i] >> 16)& 0xff;
|
||||
m_sram[(i<<2) + ptr +2] = (m_pas_offset[i] >> 8)& 0xff;
|
||||
m_sram[(i<<2) + ptr +3] = (m_pas_offset[i])& 0xff;
|
||||
if (VERBOSE>7) LOG("mapper8: save %d=%08x\n", i, m_pas_offset[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,6 +249,14 @@ WRITE8_MEMBER(peribox_device::write)
|
||||
}
|
||||
}
|
||||
|
||||
SETADDRESS_DBIN_MEMBER(peribox_device::setaddress_dbin)
|
||||
{
|
||||
for (int i=2; i <= 8; i++)
|
||||
{
|
||||
if (m_slot[i]!=NULL) m_slot[i]->setaddress_dbin(space, offset | m_address_prefix, state);
|
||||
}
|
||||
}
|
||||
|
||||
void peribox_device::crureadz(offs_t offset, UINT8 *value)
|
||||
{
|
||||
for (int i=2; i <= 8; i++)
|
||||
@ -579,6 +587,11 @@ WRITE8_MEMBER(peribox_slot_device::write)
|
||||
m_card->write(space, offset, data, mem_mask);
|
||||
}
|
||||
|
||||
SETADDRESS_DBIN_MEMBER(peribox_slot_device::setaddress_dbin)
|
||||
{
|
||||
m_card->setaddress_dbin(space, offset, state);
|
||||
}
|
||||
|
||||
void peribox_slot_device::crureadz(offs_t offset, UINT8 *value)
|
||||
{
|
||||
m_card->crureadz(offset, value);
|
||||
|
@ -49,9 +49,11 @@ public:
|
||||
peribox_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
peribox_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);
|
||||
|
||||
// Next six methods are called from the console
|
||||
// Next seven methods are called from the console
|
||||
DECLARE_READ8Z_MEMBER(readz);
|
||||
DECLARE_WRITE8_MEMBER(write);
|
||||
DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin);
|
||||
|
||||
void crureadz(offs_t offset, UINT8 *value);
|
||||
void cruwrite(offs_t offset, UINT8 value);
|
||||
DECLARE_WRITE_LINE_MEMBER(senila);
|
||||
@ -143,6 +145,8 @@ public:
|
||||
// Called from the box (direction to card)
|
||||
DECLARE_READ8Z_MEMBER(readz);
|
||||
DECLARE_WRITE8_MEMBER(write);
|
||||
DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin);
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(senila);
|
||||
DECLARE_WRITE_LINE_MEMBER(senilb);
|
||||
|
||||
|
@ -28,8 +28,6 @@
|
||||
#define VERBOSE 1
|
||||
#define LOG logerror
|
||||
|
||||
#define REAL_TIMING 0
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
ti_speech_synthesizer_device::ti_speech_synthesizer_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
@ -37,79 +35,23 @@ ti_speech_synthesizer_device::ti_speech_synthesizer_device(const machine_config
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
Comments on real timing in the TMS5200 emulation
|
||||
|
||||
Real timing means that the synthesizer clears the /READY line (puts high)
|
||||
whenever a read or write access is in progress. This is done by setting
|
||||
/RS and /WS lines, according to the read or write operation. The /READY line
|
||||
is asserted again after some time. Real timing is used once the tms5220_wsq_w
|
||||
and tms5220_rsq_w are called.
|
||||
|
||||
Within the TI systems, the /RS and /WS lines are controlled directly by the
|
||||
address bus. There is currently no way to insert wait states between
|
||||
the address setting and the data bus sampling, since this is an atomic
|
||||
operation in the emulator (read_byte). It would be necessary to somehow
|
||||
announce the pending read before it actually happens so that devices
|
||||
like this VSP may insert wait states before the read.
|
||||
|
||||
The TMS5220 implementation assumes that wait states are respected and
|
||||
therefore delivers bad values when queried too early. It uses a latch that
|
||||
gets the new value after some time has expired.
|
||||
|
||||
To fix this we have to modify the RS/WS methods in TMS5200 to immediately
|
||||
set the status (after updating the sound status, or the status will be
|
||||
outdated too early).
|
||||
|
||||
Also note that the /RS and /WS lines must be cleared (put to high) when the
|
||||
read is done. Again, this is not possible with the current implementation.
|
||||
So we do this in the ready callback.
|
||||
|
||||
On the bottom line we will stay with the not-REAL_TIMING for now and wait
|
||||
for the core to allow for split read accesses.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Memory read
|
||||
*/
|
||||
#if REAL_TIMING
|
||||
|
||||
// ====== This is the version with real timing =======
|
||||
READ8Z_MEMBER( ti_speech_synthesizer_device::readz )
|
||||
{
|
||||
if ((offset & m_select_mask)==m_select_value)
|
||||
{
|
||||
m_vsp->wsq_w(TRUE);
|
||||
m_vsp->rsq_w(FALSE);
|
||||
*value = m_vsp->read(offset) & 0xff;
|
||||
if (VERBOSE>4) LOG("spchsyn: read value = %02x\n", *value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Memory write
|
||||
*/
|
||||
WRITE8_MEMBER( ti_speech_synthesizer_device::write )
|
||||
{
|
||||
if ((offset & m_select_mask)==(m_select_value | 0x0400))
|
||||
{
|
||||
m_vsp->rsq_w(m_vsp, TRUE);
|
||||
m_vsp->wsq_w(m_vsp, FALSE);
|
||||
if (VERBOSE>4) LOG("spchsyn: write value = %02x\n", data);
|
||||
m_vsp->write(offset, data);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// ====== This is the version without real timing =======
|
||||
|
||||
READ8Z_MEMBER( ti_speech_synthesizer_device::readz )
|
||||
{
|
||||
if ((offset & m_select_mask)==m_select_value)
|
||||
{
|
||||
machine().device("maincpu")->execute().adjust_icount(-(18+3)); /* this is just a minimum, it can be more */
|
||||
*value = m_vsp->status_r(space, offset, 0xff) & 0xff;
|
||||
if (VERBOSE>4) LOG("spchsyn: read value = %02x\n", *value);
|
||||
// We should clear the lines at this point. The TI-99/4A clears the
|
||||
// lines by setting the address bus to a different value, but the
|
||||
// Geneve may behave differently. This may not 100% reflect the real
|
||||
// situation, but it ensures a safe processing.
|
||||
m_vsp->rsq_w(TRUE);
|
||||
m_vsp->wsq_w(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,26 +62,51 @@ WRITE8_MEMBER( ti_speech_synthesizer_device::write )
|
||||
{
|
||||
if ((offset & m_select_mask)==(m_select_value | 0x0400))
|
||||
{
|
||||
machine().device("maincpu")->execute().adjust_icount(-(54+3)); /* this is just an approx. minimum, it can be much more */
|
||||
|
||||
/* RN: the stupid design of the tms5220 core means that ready is cleared */
|
||||
/* when there are 15 bytes in FIFO. It should be 16. Of course, if */
|
||||
/* it were the case, we would need to store the value on the bus, */
|
||||
/* which would be more complex. */
|
||||
if (!m_vsp->readyq_r())
|
||||
{
|
||||
attotime time_to_ready = attotime::from_double(m_vsp->time_to_ready());
|
||||
int cycles_to_ready = machine().device<cpu_device>("maincpu")->attotime_to_cycles(time_to_ready);
|
||||
if (VERBOSE>8) LOG("spchsyn: time to ready: %f -> %d\n", time_to_ready.as_double(), (int) cycles_to_ready);
|
||||
|
||||
machine().device("maincpu")->execute().adjust_icount(-cycles_to_ready);
|
||||
machine().scheduler().timer_set(attotime::zero, FUNC_NULL);
|
||||
}
|
||||
if (VERBOSE>4) LOG("spchsyn: write value = %02x\n", data);
|
||||
m_vsp->data_w(space, offset, data);
|
||||
// Note that we must NOT clear the lines here. Find the lines in the
|
||||
// READY callback below.
|
||||
}
|
||||
}
|
||||
|
||||
SETADDRESS_DBIN_MEMBER( ti_speech_synthesizer_device::setaddress_dbin )
|
||||
{
|
||||
if ((offset & m_select_mask & ~0x0400)==m_select_value)
|
||||
{
|
||||
if (VERBOSE>4) LOG("spchsyn: set address = %04x, dbin = %d\n", offset, state);
|
||||
m_read_mode = (state==ASSERT_LINE);
|
||||
bool readop = (offset & 0x0400)==0;
|
||||
|
||||
if (m_read_mode != readop)
|
||||
{
|
||||
// reset all; this is not a valid access
|
||||
m_vsp->rsq_w(TRUE);
|
||||
m_vsp->wsq_w(TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (readop)
|
||||
{
|
||||
// Caution: We MUST first clear (TRUE) one line to avoid
|
||||
// both RS* and WS* be asserted (otherwise tms5220 will report "illegal")
|
||||
m_vsp->wsq_w(TRUE);
|
||||
m_vsp->rsq_w(FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vsp->rsq_w(TRUE);
|
||||
m_vsp->wsq_w(FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If other address, turn off RS* and WS* (negative logic!)
|
||||
m_vsp->rsq_w(TRUE);
|
||||
m_vsp->wsq_w(TRUE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@ -151,14 +118,12 @@ WRITE_LINE_MEMBER( ti_speech_synthesizer_device::speech_ready )
|
||||
m_slot->set_ready((state==0)? ASSERT_LINE : CLEAR_LINE);
|
||||
if (VERBOSE>5) LOG("spchsyn: READY = %d\n", (state==0));
|
||||
|
||||
#if REAL_TIMING
|
||||
// Need to do that here (see explanations above)
|
||||
if (state==0)
|
||||
if ((state==0) && !m_read_mode)
|
||||
{
|
||||
// Clear the lines only when we are done with writing.
|
||||
m_vsp->rsq_w(TRUE);
|
||||
m_vsp->wsq_w(TRUE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ti_speech_synthesizer_device::device_start()
|
||||
@ -172,6 +137,7 @@ void ti_speech_synthesizer_device::device_config_complete()
|
||||
|
||||
void ti_speech_synthesizer_device::device_reset()
|
||||
{
|
||||
if (VERBOSE>5) LOG("spchsyn: reset\n");
|
||||
if (m_genmod)
|
||||
{
|
||||
m_select_mask = 0x1ffc01;
|
||||
@ -182,6 +148,7 @@ void ti_speech_synthesizer_device::device_reset()
|
||||
m_select_mask = 0x7fc01;
|
||||
m_select_value = 0x79000;
|
||||
}
|
||||
m_read_mode = false;
|
||||
}
|
||||
|
||||
MACHINE_CONFIG_FRAGMENT( ti99_speech )
|
||||
|
@ -23,6 +23,7 @@ public:
|
||||
ti_speech_synthesizer_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
DECLARE_READ8Z_MEMBER(readz);
|
||||
DECLARE_WRITE8_MEMBER(write);
|
||||
DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin);
|
||||
|
||||
void crureadz(offs_t offset, UINT8 *value) { };
|
||||
void cruwrite(offs_t offset, UINT8 value) { };
|
||||
@ -42,6 +43,7 @@ protected:
|
||||
|
||||
private:
|
||||
cd2501e_device *m_vsp;
|
||||
bool m_read_mode;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -49,6 +49,15 @@
|
||||
#define READ8Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED UINT8 *value, ATTR_UNUSED UINT8 mem_mask)
|
||||
#define DECLARE_READ8Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED UINT8 *value, ATTR_UNUSED UINT8 mem_mask = 0xff)
|
||||
|
||||
/*
|
||||
For almost all applications of setoffset, we also need the data bus
|
||||
direction. This line is called DBIN on the TI CPUs, but as we do not assume
|
||||
that this is a general rule, we use new macros here which contain the
|
||||
DBIN setting.
|
||||
*/
|
||||
#define SETADDRESS_DBIN_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED int state)
|
||||
#define DECLARE_SETADDRESS_DBIN_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED int state)
|
||||
|
||||
#define GENMOD 0x01
|
||||
|
||||
/*
|
||||
@ -65,6 +74,7 @@ public:
|
||||
: device_t(mconfig, type, name, tag, owner, clock, shortname, source) { }
|
||||
virtual DECLARE_READ8Z_MEMBER(readz) =0;
|
||||
virtual DECLARE_WRITE8_MEMBER(write) =0;
|
||||
virtual DECLARE_SETADDRESS_DBIN_MEMBER( setaddress_dbin ) { };
|
||||
};
|
||||
|
||||
class bus16z_device : device_t
|
||||
@ -72,6 +82,7 @@ class bus16z_device : device_t
|
||||
public:
|
||||
virtual DECLARE_READ16Z_MEMBER(read16z) =0;
|
||||
virtual DECLARE_WRITE16_MEMBER(write16) =0;
|
||||
virtual DECLARE_SETADDRESS_DBIN_MEMBER( setaddress_dbin ) { };
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
Loading…
Reference in New Issue
Block a user