ti99: Again one indirection less; dropped the videowrp and moved the v9938 on the EVPC board.

This commit is contained in:
Michael Zapf 2016-04-19 22:03:27 +02:00
parent 9829c978c7
commit c7aeff7d6d
16 changed files with 968 additions and 775 deletions

View File

@ -2076,8 +2076,6 @@ if (BUSES["TI99X"]~=null) then
MAME_DIR .. "src/devices/bus/ti99x/mecmouse.cpp",
MAME_DIR .. "src/devices/bus/ti99x/mecmouse.h",
MAME_DIR .. "src/devices/bus/ti99x/ti99defs.h",
MAME_DIR .. "src/devices/bus/ti99x/videowrp.cpp",
MAME_DIR .. "src/devices/bus/ti99x/videowrp.h",
}
end

View File

@ -1,39 +1,99 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
SNUG Enhanced Video Processor Card (evpc)
based on v9938 (may also be equipped with v9958)
Can be used with TI-99/4A as an add-on card; internal VDP must be removed
SNUG Enhanced Video Processor Card (EVPC)
This is an expansion card with an own v9938 video processor on board.
Later releases (EVPC2) can also be equipped with a v9958.
The EVPC is intended to be used
1. with the TI-99/4A console
2. with the SGCPU
For option 1, the console-internal VDP (TMS9928A) must be removed. This,
however, raises a problem, because the video interrupt must now be send
from the EVPC in the Peripheral Expansion Box to the console, but there is
no line in the PEB cable for this purpose.
To solve this issue, a separate cable is led from the EVPC to the console
which delivers the video interrupt, and also the GROM clock which is also
lost due to the removal of the internal VDP.
The SGCPU requires this card, as it does not offer any video processor.
In this configuration, the video interrupt cable is not required.
Also, the SGCPU does not offer a socket for the sound chip of the TI
console, and accordingly, the EVPC also gives the sound chip a new home.
Thus we assume that in the TI console (option 1) the sound chip has
also been removed.
Important note: The DSR (firmware) of the EVPC expects a memory expansion
to be present; otherwise, the configuration (using CALL EVPC) will crash.
There is no warning if the 32K expansion is not present.
The SGCPU ("TI-99/4P") only runs with EVPC.
Michael Zapf
October 2010: Rewritten as device
February 2012: Rewritten as class
FIXME: Locks up on startup when HFDC is present. This can be avoided
by using another controller (like bwg) or doing a soft reset.
*****************************************************************************/
#include "evpc.h"
#define EVPC_CRU_BASE 0x1400
#define VERBOSE 1
#define LOG logerror
#define TRACE_ADDRESS 0
#define TRACE_CRU 0
#define TRACE_MEM 0
#define NOVRAM_SIZE 256
snug_enhanced_video_device::snug_enhanced_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: ti_expansion_card_device(mconfig, TI99_EVPC, "SNUG Enhanced Video Processor Card", tag, owner, clock, "ti99_evpc", __FILE__),
device_nvram_interface(mconfig, *this),
m_dsrrom(nullptr),
m_RAMEN(false),
m_dsr_page(0),
m_novram(nullptr)
m_inDsrArea(false),
m_novram_accessed(false),
m_palette_accessed(false),
m_RAMEN(false),
m_dsrrom(nullptr),
m_novram(nullptr),
m_video(*this, VDP_TAG),
m_sound(*this, TISOUNDCHIP_TAG)
{
}
SETADDRESS_DBIN_MEMBER( snug_enhanced_video_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;
if (TRACE_ADDRESS) logerror("set address %04x, %s\n", offset, (state==ASSERT_LINE)? "read" : "write");
m_address = offset;
bool reading = (state==ASSERT_LINE);
int offbase = (m_address & 0xfc01);
// Sound
m_sound_accessed = ((m_address & 0xff01)==0x8400) && !reading;
// Video space
// 8800 / 8802 / 8804 / 8806
// 8c00 / 8c02 / 8c04 / 8c06
//
// Bits 1000 1w00 0000 0xx0
// Mask 1111 1000 0000 0001
m_video_accessed = ((offbase==0x8800) && reading) || ((offbase==0x8c00) && !reading);
// Read a byte in evpc DSR space
// 0x4000 - 0x5eff DSR (paged)
// 0x5f00 - 0x5fef NOVRAM
// 0x5ff0 - 0x5fff Palette
m_inDsrArea = ((m_address & 0xe000)==0x4000);
m_novram_accessed = ((m_address & 0xff00)==0x5f00);
m_palette_accessed = ((m_address & 0xfff0)==0x5ff0);
// Note that we check the selection in reverse order so that the overlap is avoided
}
//-------------------------------------------------
// nvram_default - called to initialize NVRAM to
// its default state
@ -64,85 +124,80 @@ void snug_enhanced_video_device::nvram_write(emu_file &file)
file.write(m_novram.get(), NOVRAM_SIZE);
}
/*
Read a byte in evpc DSR space
Read a byte in evpc DSR space, NOVRAM, Palette, or video
0x4000 - 0x5eff DSR (paged)
0x5f00 - 0x5fef NOVRAM
0x5ff0 - 0x5fff Palette
0x5ff0 - 0x5fff Palette (5ff0, 5ff2, 5ff4, 5ff6)
*/
READ8Z_MEMBER(snug_enhanced_video_device::readz)
{
if (m_selected)
if (m_selected && m_inDsrArea)
{
if ((offset & m_select_mask)==m_select_value)
if (m_palette_accessed)
{
if ((offset & 0x1ff0)==0x1ff0) // Palette control
switch (m_address & 0x000f)
{
switch (offset & 0x000f)
{
case 0:
/* Palette Read Address Register */
*value = m_palette.write_index;
break;
case 0:
// Palette Read Address Register
*value = m_palette.write_index;
break;
case 2:
/* Palette Read Color Value */
if (m_palette.read)
{
switch (m_palette.state)
{
case 0:
*value = m_palette.color[m_palette.read_index].red;
break;
case 1:
*value = m_palette.color[m_palette.read_index].green;
break;
case 2:
*value = m_palette.color[m_palette.read_index].blue;
break;
}
m_palette.state++;
if (m_palette.state == 3)
{
m_palette.state = 0;
m_palette.read_index++;
}
}
break;
case 4:
/* Palette Read Pixel Mask */
*value = m_palette.mask;
break;
case 6:
/* Palette Read Address Register for Color Value */
if (m_palette.read)
*value = 0;
else
*value = 3;
break;
}
}
else
{
if ((offset & 0x1f00)==0x1f00)
case 2:
// Palette Read Color Value
if (m_palette.read)
{
if (m_RAMEN) // NOVRAM hides DSR
switch (m_palette.state)
{
*value = m_novram[offset & 0x00ff];
case 0:
*value = m_palette.color[m_palette.read_index].red;
break;
case 1:
*value = m_palette.color[m_palette.read_index].green;
break;
case 2:
*value = m_palette.color[m_palette.read_index].blue;
break;
}
else // DSR
m_palette.state++;
if (m_palette.state == 3)
{
*value = m_dsrrom[(offset&0x1fff) | (m_dsr_page<<13)];
m_palette.state = 0;
m_palette.read_index++;
}
}
break;
case 4:
// Palette Read Pixel Mask
*value = m_palette.mask;
break;
case 6:
// Palette Read Address Register for Color Value
if (m_palette.read)
*value = 0;
else
{
*value = m_dsrrom[(offset&0x1fff) | (m_dsr_page<<13)];
}
*value = 3;
break;
}
return;
}
if (m_novram_accessed && m_RAMEN)
{
// NOVRAM
*value = m_novram[offset & 0x00ff];
return;
}
// DSR space
*value = m_dsrrom[(offset & 0x1fff) | (m_dsr_page<<13)];
return;
}
if (m_video_accessed)
{
*value = m_video->read(space, m_address>>1);
}
}
@ -150,82 +205,87 @@ READ8Z_MEMBER(snug_enhanced_video_device::readz)
Write a byte in evpc DSR space
0x4000 - 0x5eff DSR (paged)
0x5f00 - 0x5fef NOVRAM
0x5ff0 - 0x5fff Palette
0x5ff0 - 0x5fff Palette (5ff8, 5ffa, 5ffc, 5ffe)
*/
WRITE8_MEMBER(snug_enhanced_video_device::write)
{
if (m_selected)
if (m_selected && m_inDsrArea)
{
if ((offset & m_select_mask)==m_select_value)
if (m_palette_accessed)
{
if ((offset & 0x1ff0)==0x1ff0)
// Palette
if (TRACE_MEM) logerror("palette write %04x <- %02x\n", offset&0xffff, data);
switch (m_address & 0x000f)
{
/* PALETTE */
if (VERBOSE>5) LOG("palette write %04x <- %02x\n", offset&0xffff, data);
switch (offset & 0x000f)
case 0x08:
// Palette Write Address Register
if (TRACE_MEM) logerror("EVPC palette address write (for write access)\n");
m_palette.write_index = data;
m_palette.state = 0;
m_palette.read = 0;
break;
case 0x0a:
// Palette Write Color Value
if (TRACE_MEM) logerror("EVPC palette color write\n");
if (!m_palette.read)
{
case 0x08:
/* Palette Write Address Register */
if (VERBOSE>5) LOG("EVPC palette address write (for write access)\n");
m_palette.write_index = data;
m_palette.state = 0;
m_palette.read = 0;
break;
case 0x0a:
/* Palette Write Color Value */
if (VERBOSE>5) LOG("EVPC palette color write\n");
if (!m_palette.read)
switch (m_palette.state)
{
switch (m_palette.state)
{
case 0:
m_palette.color[m_palette.write_index].red = data;
break;
case 1:
m_palette.color[m_palette.write_index].green = data;
break;
case 2:
m_palette.color[m_palette.write_index].blue = data;
break;
}
m_palette.state++;
if (m_palette.state == 3)
{
m_palette.state = 0;
m_palette.write_index++;
}
//evpc_palette.dirty = 1;
case 0:
m_palette.color[m_palette.write_index].red = data;
break;
case 1:
m_palette.color[m_palette.write_index].green = data;
break;
case 2:
m_palette.color[m_palette.write_index].blue = data;
break;
}
break;
case 0x0c:
/* Palette Write Pixel Mask */
if (VERBOSE>5) LOG("EVPC palette mask write\n");
m_palette.mask = data;
break;
case 0x0e:
/* Palette Write Address Register for Color Value */
if (VERBOSE>5) LOG("EVPC palette address write (for read access)\n");
m_palette.read_index = data;
m_palette.state = 0;
m_palette.read = 1;
break;
}
}
else
{
if ((offset & 0x1f00)==0x1f00)
{
if (m_RAMEN)
m_palette.state++;
if (m_palette.state == 3)
{
// NOVRAM
m_novram[offset & 0x00ff] = data;
m_palette.state = 0;
m_palette.write_index++;
}
//evpc_palette.dirty = 1;
}
break;
case 0x0c:
// Palette Write Pixel Mask
if (TRACE_MEM) logerror("EVPC palette mask write\n");
m_palette.mask = data;
break;
case 0x0e:
// Palette Write Address Register for Color Value
if (TRACE_MEM) logerror("EVPC palette address write (for read access)\n");
m_palette.read_index = data;
m_palette.state = 0;
m_palette.read = 1;
break;
}
return;
}
if (m_novram_accessed && m_RAMEN)
{
// NOVRAM
m_novram[offset & 0x00ff] = data;
return;
}
}
if (m_video_accessed)
{
m_video->write(space, m_address>>1, data);
}
if (m_sound_accessed)
{
m_sound->write(space, 0, data);
}
}
@ -273,7 +333,7 @@ WRITE8_MEMBER(snug_enhanced_video_device::cruwrite)
{
case 0:
m_selected = (data!=0);
if (VERBOSE>4) LOG("evpc: Map DSR = %d\n", m_selected);
if (TRACE_CRU) logerror("Map DSR = %d\n", m_selected);
break;
case 1:
@ -309,15 +369,23 @@ WRITE8_MEMBER(snug_enhanced_video_device::cruwrite)
}
}
/*
READY line for the sound chip
*/
WRITE_LINE_MEMBER( snug_enhanced_video_device::ready_line )
{
m_slot->set_ready(state);
}
void snug_enhanced_video_device::device_start()
{
m_dsrrom = memregion(DSRROM)->base();
m_novram = std::make_unique<UINT8[]>(NOVRAM_SIZE);
m_console_conn = downcast<evpc_clock_connector*>(machine().device(EVPC_CONN_TAG));
}
void snug_enhanced_video_device::device_reset()
{
if (VERBOSE>5) LOG("evpc: reset\n");
m_select_mask = 0x7e000;
m_select_value = 0x74000;
m_dsr_page = 0;
@ -330,11 +398,36 @@ void snug_enhanced_video_device::device_stop()
m_novram = nullptr;
}
/*
This is the extra cable running from the EVPC card right into the TI console.
It delivers the VDP interrupt and the GROM clock.
For the SGCPU, the signal is delivered by the LCP line.
*/
WRITE_LINE_MEMBER( snug_enhanced_video_device::video_interrupt_in )
{
if (m_console_conn != nullptr) m_console_conn->vclock_line(state);
else m_slot->lcp_line(state);
}
ROM_START( ti99_evpc )
ROM_REGION(0x10000, DSRROM, 0)
ROM_LOAD("evpcdsr.bin", 0, 0x10000, CRC(a062b75d) SHA1(6e8060f86e3bb9c36f244d88825e3fe237bfe9a9)) /* evpc DSR ROM */
ROM_END
MACHINE_CONFIG_FRAGMENT( ti99_evpc )
// video hardware
MCFG_V9938_ADD(VDP_TAG, SCREEN_TAG, 0x20000, XTAL_21_4772MHz) /* typical 9938 clock, not verified */
MCFG_V99X8_INTERRUPT_CALLBACK(WRITELINE(snug_enhanced_video_device, video_interrupt_in))
MCFG_V99X8_SCREEN_ADD_NTSC(SCREEN_TAG, VDP_TAG, XTAL_21_4772MHz)
// Sound hardware
MCFG_SPEAKER_STANDARD_MONO("sound_out")
MCFG_SOUND_ADD(TISOUNDCHIP_TAG, SN94624, 3579545/8) /* 3.579545 MHz */
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "sound_out", 0.75)
MCFG_SN76496_READY_HANDLER( WRITELINE(snug_enhanced_video_device, ready_line) )
MACHINE_CONFIG_END
/*
Input ports for the EPVC
*/
@ -370,4 +463,9 @@ ioport_constructor snug_enhanced_video_device::device_input_ports() const
return INPUT_PORTS_NAME(ti99_evpc);
}
machine_config_constructor snug_enhanced_video_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( ti99_evpc );
}
const device_type TI99_EVPC = &device_creator<snug_enhanced_video_device>;

View File

@ -17,6 +17,9 @@
#include "emu.h"
#include "peribox.h"
#include "video/v9938.h"
#include "sound/sn76496.h"
#include "bus/ti99x/ti99defs.h"
extern const device_type TI99_EVPC;
@ -35,28 +38,45 @@ public:
snug_enhanced_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin) override;
DECLARE_WRITE_LINE_MEMBER( ready_line );
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER( video_interrupt_in );
protected:
virtual void device_start(void) override;
virtual void device_reset(void) override;
virtual void device_stop(void) override;
void device_start(void) override;
void device_reset(void) override;
void device_stop(void) override;
virtual const rom_entry *device_rom_region() const override;
virtual ioport_constructor device_input_ports() const override;
machine_config_constructor device_mconfig_additions() const override;
void nvram_default() override;
void nvram_read(emu_file &file) override;
void nvram_write(emu_file &file) override;
private:
UINT8* m_dsrrom;
bool m_RAMEN;
int m_dsr_page;
std::unique_ptr<UINT8[]> m_novram; /* NOVRAM area */
evpc_palette m_palette;
int m_address;
int m_dsr_page;
bool m_inDsrArea;
bool m_novram_accessed;
bool m_palette_accessed;
bool m_RAMEN;
bool m_sound_accessed;
bool m_video_accessed;
UINT8* m_dsrrom;
std::unique_ptr<UINT8[]> m_novram; // NOVRAM area
evpc_palette m_palette;
required_device<v9938_device> m_video;
required_device<sn76496_base_device> m_sound;
evpc_clock_connector* m_console_conn;
};
#endif

View File

@ -127,6 +127,7 @@
#define TRACE_DSR 0
#define TRACE_BANKING 0
#define TRACE_CRU 0
#define TRACE_ADDRESS 0
#define TRACE_READ 0
#define TRACE_WRITE 0
#define TRACE_IGNORE 0
@ -165,7 +166,7 @@ WRITE8_MEMBER(snug_high_speed_gpl_device::cruwrite)
{
if (data != 0)
{
if (TRACE_CRU) logerror("%s: Supercart cru setting %04x\n", tag(), offset);
if (TRACE_CRU) logerror("Supercart cru setting %04x\n", offset);
m_current_bank = (offset-0x0802)>>2;
}
return;
@ -179,15 +180,15 @@ WRITE8_MEMBER(snug_high_speed_gpl_device::cruwrite)
{
case 0:
m_dsr_enabled = (data != 0);
if (TRACE_CRU) logerror("%s: Set dsr_enabled=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set dsr_enabled=%x\n", data);
break;
case 1:
m_gram_enabled = (data != 0);
if (TRACE_CRU) logerror("%s: Set gram_enabled=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set gram_enabled=%x\n", data);
break;
case 2:
m_bank_inhibit = (data != 0);
if (TRACE_CRU) logerror("%s: Set bank_inhibit=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set bank_inhibit=%x\n", data);
break;
case 3:
case 4:
@ -199,34 +200,34 @@ WRITE8_MEMBER(snug_high_speed_gpl_device::cruwrite)
m_dsr_page |= (1 << (bit-3));
else
m_dsr_page &= ~(1 << (bit-3));
if (TRACE_CRU) logerror("%s: Set dsr_page=%d\n", tag(), m_dsr_page);
if (TRACE_CRU) logerror("Set dsr_page=%d\n", m_dsr_page);
break;
case 9:
m_card_enabled = data;
if (TRACE_CRU) logerror("%s: Set card_enabled=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set card_enabled=%x\n", data);
break;
case 10:
m_write_enabled = data;
if (TRACE_CRU) logerror("%s: Set write_enabled=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set write_enabled=%x\n", data);
break;
case 11:
m_supercart_enabled = data;
// CHECK: Do we have to reset the bank?
if (TRACE_CRU) logerror("%s: Set supercart_enabled=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set supercart_enabled=%x\n", data);
break;
case 12:
m_led_on = data;
if (TRACE_CRU) logerror("%s: Set led_on=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set led_on=%x\n", data);
break;
case 13:
break;
case 14:
m_mbx_enabled = data;
if (TRACE_CRU) logerror("%s: Set mbx_enabled=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set mbx_enabled=%x\n", data);
break;
case 15:
m_ram_enabled = data;
if (TRACE_CRU) logerror("%s: Set ram_enabled=%x\n", tag(), data);
if (TRACE_CRU) logerror("Set ram_enabled=%x\n", data);
break;
}
}
@ -279,7 +280,7 @@ void snug_high_speed_gpl_device::dsrspace_readz(address_space& space, offs_t off
if (m_dsr_enabled)
{
*value = m_dsr_eeprom->read(space, (offset & 0x1fff) | (m_dsr_page<<13), mem_mask);
if (TRACE_READ) logerror("%s: read dsr %04x[%02x] -> %02x\n", tag(), offset, m_dsr_page, *value);
if (TRACE_READ) logerror("read dsr %04x[%02x] -> %02x\n", offset, m_dsr_page, *value);
}
}
@ -290,14 +291,14 @@ void snug_high_speed_gpl_device::cartspace_readz(address_space& space, offs_t of
{
if (!m_card_enabled || m_flash_mode)
{
if (TRACE_IGNORE) logerror("%s: cartridge space read ignored (enable=%d, flash_mode=%d)\n", tag(), m_card_enabled, m_flash_mode);
if (TRACE_IGNORE) logerror("cartridge space read ignored (enable=%d, flash_mode=%d)\n", m_card_enabled, m_flash_mode);
return;
}
if (m_module_bank < 16)
{
*value = m_rom6_eeprom->read(space, (offset & 0x1fff) | (m_current_bank<<13) | (m_current_grom_port<<15), mem_mask);
if (TRACE_READ) logerror("%s: cartridge space read %04x -> %02x\n", tag(), offset, *value);
if (TRACE_READ) logerror("cartridge space read %04x -> %02x\n", offset, *value);
}
else
{
@ -307,7 +308,7 @@ void snug_high_speed_gpl_device::cartspace_readz(address_space& space, offs_t of
}
else
{
logerror("%s: unknown 0x6000 port\n", tag());
logerror("unknown 0x6000 port\n");
}
}
}
@ -356,7 +357,7 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
{
*value = m_gram_memory[m_grom_address | (port<<16)];
m_module_bank = port + 16;
if (TRACE_PORT) if (bNew) logerror("%s: GRAM read access at %04x (GRMENA=1) - switch to bank %d\n", tag(), offset & 0xffff, m_module_bank);
if (TRACE_PORT) if (bNew) logerror("GRAM read access at %04x (GRMENA=1) - switch to bank %d\n", offset & 0xffff, m_module_bank);
}
else
{
@ -366,7 +367,7 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
{
*value = m_grom_a_eeprom->read(space, m_grom_address | (port<<16), mem_mask);
m_module_bank = port;
if (TRACE_PORT) if (bNew) logerror("%s: GROM read access at %04x - switch to bank %d\n", tag(), offset & 0xffff, m_module_bank);
if (TRACE_PORT) if (bNew) logerror("GROM read access at %04x - switch to bank %d\n", offset & 0xffff, m_module_bank);
}
}
else
@ -375,7 +376,7 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
{
*value = m_grom_b_eeprom->read(space, m_grom_address | ((port-8)<<16), mem_mask);
m_module_bank = port;
if (TRACE_PORT) if (bNew) logerror("%s: GROM read access at %04x - switch to bank %d\n", tag(), offset & 0xffff, m_module_bank);
if (TRACE_PORT) if (bNew) logerror("GROM read access at %04x - switch to bank %d\n", offset & 0xffff, m_module_bank);
}
else
{
@ -385,7 +386,7 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
// DSR banks 0-63 (8 KiB per bank, 8 banks per port)
*value = m_dsr_eeprom->read(space, m_grom_address | ((port-16)<<16), mem_mask);
// Don't change the module port
if (TRACE_DSR) if (bNew) logerror("%s: read access to DSR bank %d-%d (%04x)\n", tag(), (port-16)<<3, ((port-16)<<3)+7, offset);
if (TRACE_DSR) if (bNew) logerror("read access to DSR bank %d-%d (%04x)\n", (port-16)<<3, ((port-16)<<3)+7, offset);
}
else
{
@ -395,7 +396,7 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
// Each ROM6 is available as 4 (sub)banks (switchable via 6000, 6002, 6004, 6006)
// Accordingly, each port has two complete sets
*value = m_rom6_eeprom->read(space, m_grom_address | ((port-24)<<16), mem_mask);
if (TRACE_PORT) if (bNew) logerror("%s: ROM6 read access for module bank %d-%d (%04x)\n", tag(), (port-24)<<1, ((port-24)<<1)+1, offset & 0xffff);
if (TRACE_PORT) if (bNew) logerror("ROM6 read access for module bank %d-%d (%04x)\n", (port-24)<<1, ((port-24)<<1)+1, offset & 0xffff);
}
else
{
@ -404,7 +405,7 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
{
*value = m_gram_memory[m_grom_address | ((port-32)<<16)];
m_module_bank = port - 16;
if (TRACE_PORT) if (bNew) logerror("%s: GRAM read access at %04x - switch to bank %d\n", tag(), offset & 0xffff, m_module_bank);
if (TRACE_PORT) if (bNew) logerror("GRAM read access at %04x - switch to bank %d\n", offset & 0xffff, m_module_bank);
}
else
{
@ -412,11 +413,11 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
{
// *value = m_ram6_memory[m_grom_address];
*value = m_ram6_memory[m_grom_address | ((port-48)<<16)];
if (TRACE_PORT) if (bNew) logerror("%s: RAM read access at %04x\n", tag(), offset & 0xffff);
if (TRACE_PORT) if (bNew) logerror("RAM read access at %04x\n", offset & 0xffff);
}
else
{
logerror("%s: Attempt to read from undefined port 0x%0x; ignored.\n", tag(), port);
logerror("Attempt to read from undefined port 0x%0x; ignored.\n", port);
}
}
}
@ -427,6 +428,7 @@ void snug_high_speed_gpl_device::grom_readz(address_space& space, offs_t offset,
}
// The address auto-increment should be done even when the card is
// offline
if (TRACE_ADDRESS) logerror("HSGPL GROM address %04x\n", m_grom_address);
m_grom_address++;
m_raddr_LSB = m_waddr_LSB = false;
}
@ -439,19 +441,19 @@ void snug_high_speed_gpl_device::cartspace_write(address_space& space, offs_t of
{
if (!m_card_enabled || m_flash_mode)
{
if (TRACE_IGNORE) logerror("%s: write ignored: card_enabled=%d, flash_mode=%d\n", tag(), m_card_enabled, m_flash_mode);
if (TRACE_IGNORE) logerror("write ignored: card_enabled=%d, flash_mode=%d\n", m_card_enabled, m_flash_mode);
return;
}
if (TRACE_WRITE) logerror("%s: cartridge space write %04x <- %02x\n", tag(), offset, data);
if (TRACE_WRITE) logerror("cartridge space write %04x <- %02x\n", offset, data);
if (!m_bank_inhibit && (m_module_bank < 16))
{
if ((offset & 1) == 0)
{
if ((offset & 0x9ff0)!=0) logerror("%s: unplausible ROM6 write: %04x <- %02x\n", tag(), offset, data);
if ((offset & 0x9ff0)!=0) logerror("unplausible ROM6 write: %04x <- %02x\n", offset, data);
m_current_bank = (offset>>1) & 3;
if (TRACE_BANKING) logerror("%s: select bank %d\n", tag(), m_current_bank);
if (TRACE_BANKING) logerror("select bank %d\n", m_current_bank);
}
return; /* right??? */
}
@ -459,7 +461,7 @@ void snug_high_speed_gpl_device::cartspace_write(address_space& space, offs_t of
if ((m_mbx_enabled) && (offset==0x6ffe))
{ /* MBX: mapper at 0x6ffe */
m_current_bank = data & 0x03;
if (TRACE_BANKING) logerror("%s: select bank MBX %d\n", tag(), m_current_bank);
if (TRACE_BANKING) logerror("select bank MBX %d\n", m_current_bank);
return;
}
@ -477,7 +479,7 @@ void snug_high_speed_gpl_device::cartspace_write(address_space& space, offs_t of
// and !ram_enabled
if (m_module_bank < 16)
{
logerror("%s: invalid write %04x <- %02x\n", tag(), offset, data);
logerror("invalid write %04x <- %02x\n", offset, data);
// feeprom is normally written to using GPL ports, and I don't know
// whether writing through >6000 page is enabled
/*
@ -493,7 +495,7 @@ void snug_high_speed_gpl_device::cartspace_write(address_space& space, offs_t of
}
else
{
logerror("%s: unknown 0x6000 port\n", tag());
logerror("unknown 0x6000 port\n");
}
}
}
@ -543,7 +545,7 @@ void snug_high_speed_gpl_device::grom_write(address_space& space, offs_t offset,
{
m_gram_memory[m_grom_address | (port<<16)] = data;
m_module_bank = port + 16;
if (TRACE_PORT) if (bNew) logerror("%s: GRAM write access at %04x (GRMENA=1) - switch to bank %d\n", tag(), offset & 0xffff, port);
if (TRACE_PORT) if (bNew) logerror("GRAM write access at %04x (GRMENA=1) - switch to bank %d\n", offset & 0xffff, port);
}
else
{
@ -551,7 +553,7 @@ void snug_high_speed_gpl_device::grom_write(address_space& space, offs_t offset,
{
m_grom_a_eeprom->write(space, m_grom_address | (port<<16), data, mem_mask);
m_module_bank = port;
if (TRACE_PORT) if (bNew) logerror("%s: GROM write access at %04x - switch to bank %d\n", tag(), offset & 0xffff, port);
if (TRACE_PORT) if (bNew) logerror("GROM write access at %04x - switch to bank %d\n", offset & 0xffff, port);
}
else
{
@ -559,21 +561,21 @@ void snug_high_speed_gpl_device::grom_write(address_space& space, offs_t offset,
{
m_grom_b_eeprom->write(space, m_grom_address | ((port-8)<<16), data, mem_mask);
m_module_bank = port;
if (TRACE_PORT) if (bNew) logerror("%s: GROM write access at %04x - switch to bank %d\n", tag(), offset & 0xffff, port);
if (TRACE_PORT) if (bNew) logerror("GROM write access at %04x - switch to bank %d\n", offset & 0xffff, port);
}
else
{
if (port < 24)
{
m_dsr_eeprom->write(space, m_grom_address | ((port-16)<<16), data, mem_mask);
if (TRACE_DSR) if (bNew) logerror("%s: write access to DSR bank %d-%d (%04x)\n", tag(), (port-16)<<3, ((port-16)<<3)+7, offset);
if (TRACE_DSR) if (bNew) logerror("write access to DSR bank %d-%d (%04x)\n", (port-16)<<3, ((port-16)<<3)+7, offset);
}
else
{
if (port < 32)
{
m_rom6_eeprom->write(space, m_grom_address | ((port-24)<<16), data, mem_mask);
if (TRACE_PORT) if (bNew) logerror("%s: ROM6 write access for module bank %d-%d (%04x)\n", tag(), (port-24)<<1, ((port-24)<<1)+1,offset & 0xffff);
if (TRACE_PORT) if (bNew) logerror("ROM6 write access for module bank %d-%d (%04x)\n", (port-24)<<1, ((port-24)<<1)+1,offset & 0xffff);
}
else
{
@ -581,7 +583,7 @@ void snug_high_speed_gpl_device::grom_write(address_space& space, offs_t offset,
{
m_gram_memory[m_grom_address | ((port-32)<<16)] = data;
m_module_bank = port - 16;
if (TRACE_PORT) if (bNew) logerror("%s: GRAM write access at %04x - switch to bank %d\n", tag(), offset & 0xffff, m_module_bank);
if (TRACE_PORT) if (bNew) logerror("GRAM write access at %04x - switch to bank %d\n", offset & 0xffff, m_module_bank);
}
else
{
@ -589,11 +591,11 @@ void snug_high_speed_gpl_device::grom_write(address_space& space, offs_t offset,
{
// m_ram6_memory[m_grom_address] = data;
m_ram6_memory[m_grom_address | ((port-48)<<16)] = data;
if (TRACE_PORT) if (bNew) logerror("%s: RAM write access at %04x\n", tag(), offset & 0xffff);
if (TRACE_PORT) if (bNew) logerror("RAM write access at %04x\n", offset & 0xffff);
}
else
{
logerror("%s: Attempt to write to undefined port; ignored.\n", tag());
logerror("Attempt to write to undefined port; ignored.\n");
}
}
}
@ -619,7 +621,7 @@ void snug_high_speed_gpl_device::device_start()
void snug_high_speed_gpl_device::device_reset()
{
logerror("%s: reset\n", tag());
logerror("reset\n");
m_dsr_enabled = false;
m_gram_enabled = false;
m_bank_inhibit = false;

View File

@ -220,7 +220,9 @@ peribox_device::peribox_device(const machine_config &mconfig, const char *tag, d
: bus8z_device(mconfig, PERIBOX, "Peripheral expansion box", tag, owner, clock, "peribox", __FILE__),
m_console_inta(*this),
m_console_intb(*this),
m_datamux_ready(*this), m_inta_flag(0), m_intb_flag(0), m_ready_flag(0)
m_sgcpu_lcp(*this),
m_datamux_ready(*this),
m_inta_flag(0), m_intb_flag(0), m_lcp_flag(0), m_ready_flag(0)
{
for (int i=2; i <= 8; i++) m_slot[i] = nullptr;
// The address prefix is actually created by the "Flex cable interface"
@ -235,6 +237,7 @@ peribox_device::peribox_device(const machine_config &mconfig, device_type type,
: bus8z_device(mconfig, type, name, tag, owner, clock, shortname, source),
m_console_inta(*this),
m_console_intb(*this),
m_sgcpu_lcp(*this),
m_datamux_ready(*this), m_inta_flag(0), m_intb_flag(0), m_ready_flag(0), m_address_prefix(0), m_msast(false), m_memen(false)
{
for (int i=2; i <= 8; i++) m_slot[i] = nullptr;
@ -378,6 +381,17 @@ void peribox_device::intb_join(int slot, int state)
m_console_intb((m_intb_flag != 0)? ASSERT_LINE : CLEAR_LINE);
}
void peribox_device::lcp_join(int slot, int state)
{
if (TRACE_INT) logerror("%s: propagating LCP from slot %d to SGCPU: %d\n", tag(), slot, state);
if (state==ASSERT_LINE)
m_lcp_flag |= (1 << slot);
else
m_lcp_flag &= ~(1 << slot);
m_sgcpu_lcp((m_lcp_flag != 0)? ASSERT_LINE : CLEAR_LINE);
}
/*
When any device pulls down READY, READY goes down.
*/
@ -405,6 +419,7 @@ void peribox_device::device_start(void)
// Resolve the callback lines to the console
m_console_inta.resolve();
m_console_intb.resolve();
m_sgcpu_lcp.resolve();
m_datamux_ready.resolve();
if (TRACE_EMU)
@ -421,6 +436,7 @@ void peribox_device::device_config_complete()
{
m_inta_flag = 0;
m_intb_flag = 0;
m_lcp_flag = 0;
m_ready_flag = 0;
}
@ -721,6 +737,12 @@ WRITE_LINE_MEMBER( peribox_slot_device::set_intb )
peb->intb_join(m_slotnumber, state);
}
WRITE_LINE_MEMBER( peribox_slot_device::lcp_line )
{
peribox_device *peb = static_cast<peribox_device*>(owner());
peb->lcp_join(m_slotnumber, state);
}
WRITE_LINE_MEMBER( peribox_slot_device::set_ready )
{
peribox_device *peb = static_cast<peribox_device*>(owner());

View File

@ -42,6 +42,7 @@ public:
template<class _Object> static devcb_base &static_set_inta_callback(device_t &device, _Object object) { return downcast<peribox_device &>(device).m_console_inta.set_callback(object); }
template<class _Object> static devcb_base &static_set_intb_callback(device_t &device, _Object object) { return downcast<peribox_device &>(device).m_console_intb.set_callback(object); }
template<class _Object> static devcb_base &static_set_ready_callback(device_t &device, _Object object) { return downcast<peribox_device &>(device).m_datamux_ready.set_callback(object); }
template<class _Object> static devcb_base &static_set_lcp_callback(device_t &device, _Object object) { return downcast<peribox_device &>(device).m_sgcpu_lcp.set_callback(object); }
// Next eight methods are called from the console
DECLARE_READ8Z_MEMBER(readz) override;
@ -74,6 +75,7 @@ protected:
// Next three methods call back the console
devcb_write_line m_console_inta; // INTA line (Box to console)
devcb_write_line m_console_intb; // INTB line
devcb_write_line m_sgcpu_lcp; // For EVPC with SGCPU only
devcb_write_line m_datamux_ready; // READY line (to the datamux)
void set_slot_loaded(int slot, peribox_slot_device* slotdev);
@ -83,10 +85,12 @@ protected:
// if any one slot asserts the line, the joint line is asserted.
void inta_join(int slot, int state);
void intb_join(int slot, int state);
void lcp_join(int slot, int state);
void ready_join(int slot, int state);
int m_inta_flag;
int m_intb_flag;
int m_lcp_flag;
int m_ready_flag;
// The TI-99/4(A) Flex Cable Interface (slot 1) pulls up the AMA/AMB/AMC lines to 1/1/1.
@ -173,6 +177,7 @@ public:
// Called from the card (direction to box)
DECLARE_WRITE_LINE_MEMBER( set_inta );
DECLARE_WRITE_LINE_MEMBER( set_intb );
DECLARE_WRITE_LINE_MEMBER( lcp_line );
DECLARE_WRITE_LINE_MEMBER( set_ready );
DECLARE_READ8Z_MEMBER(crureadz);
@ -257,4 +262,7 @@ protected:
#define MCFG_PERIBOX_READY_HANDLER( _ready ) \
devcb = &peribox_device::static_set_ready_callback( *device, DEVCB_##_ready );
#define MCFG_PERIBOX_LCP_HANDLER( _lcp ) \
devcb = &peribox_device::static_set_lcp_callback( *device, DEVCB_##_lcp );
#endif /* __PBOX__ */

View File

@ -11,22 +11,26 @@
addresses at 4000, 4002, ..., 401e, which correspond to memory locations
0000-0fff, 1000-1fff, ..., f000-ffff.
Michael Zapf
According to a software distribution disk from the South West 99ers group,
the predecessor of this card was the Asgard Expanded Memory System (AEMS).
Although some documentation and software was available for it, it was never
built. Instead, a simpler memory card called the Asgard Memory System (AMS)
was built. The South West 99ers group built a better version of this card
called the Super AMS. Any documentation and software containing a reference
to the AEMS are applicable to either AMS or SAMS.
February 2012: Rewritten as class
Michael Zapf
*****************************************************************************/
#include "samsmem.h"
#define RAMREGION "ram"
#define SAMS_CRU_BASE 0x1e00
#define VERBOSE 1
#define LOG logerror
sams_memory_expansion_device::sams_memory_expansion_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: ti_expansion_card_device(mconfig, TI99_SAMSMEM, "SuperAMS memory expansion card", tag, owner, clock, "ti99_sams", __FILE__), m_ram(nullptr), m_map_mode(false), m_access_mapper(false)
: ti_expansion_card_device(mconfig, TI99_SAMSMEM, "SuperAMS memory expansion card", tag, owner, clock, "ti99_sams", __FILE__),
m_ram(*this, RAM_TAG),
m_map_mode(false), m_access_mapper(false)
{
}
@ -49,12 +53,12 @@ READ8Z_MEMBER(sams_memory_expansion_device::readz)
if (!m_map_mode)
{
// transparent mode
*value = m_ram[offset & 0xffff];
*value = m_ram->pointer()[offset & 0xffff];
}
else
{
base = (m_mapper[(offset & 0xf000)>>12] << 12);
*value = m_ram[base | (offset & 0x0fff)];
*value = m_ram->pointer()[base | (offset & 0x0fff)];
}
}
}
@ -73,12 +77,12 @@ WRITE8_MEMBER(sams_memory_expansion_device::write)
if (!m_map_mode)
{
// transparent mode
m_ram[offset & 0xffff] = data;
m_ram->pointer()[offset & 0xffff] = data;
}
else
{
base = (m_mapper[(offset & 0xf000)>>12] << 12);
m_ram[base | (offset & 0x0fff)] = data;
m_ram->pointer()[base | (offset & 0x0fff)] = data;
}
}
}
@ -97,37 +101,31 @@ WRITE8_MEMBER(sams_memory_expansion_device::cruwrite)
{
if ((offset & 0xff00)==SAMS_CRU_BASE)
{
if (VERBOSE>7) LOG("cru address %04x = %02x\n", offset&0xffff, data);
if ((offset & 0x000e)==0) m_access_mapper = (data!=0);
if ((offset & 0x000e)==2) m_map_mode = (data!=0);
}
}
MACHINE_CONFIG_FRAGMENT( sams_mem )
MCFG_RAM_ADD(RAM_TAG)
MCFG_RAM_DEFAULT_SIZE("1M")
MACHINE_CONFIG_END
ROM_START( sams_card )
ROM_REGION(0x100000, RAMREGION, 0)
ROM_FILL(0x0000, 0x100000, 0x00)
ROM_END
machine_config_constructor sams_memory_expansion_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( sams_mem );
}
void sams_memory_expansion_device::device_start()
{
if (VERBOSE>5) LOG("SuperAMS: start\n");
m_ram = memregion(RAMREGION)->base();
}
void sams_memory_expansion_device::device_reset()
{
if (VERBOSE>5) LOG("SuperAMS: reset\n");
// Resetting values
m_map_mode = false;
m_access_mapper = false;
for (auto & elem : m_mapper) elem = 0;
}
const rom_entry *sams_memory_expansion_device::device_rom_region() const
{
return ROM_NAME( sams_card );
}
const device_type TI99_SAMSMEM = &device_creator<sams_memory_expansion_device>;

View File

@ -17,6 +17,7 @@
#include "emu.h"
#include "peribox.h"
#include "machine/ram.h"
extern const device_type TI99_SAMSMEM;
@ -30,13 +31,15 @@ public:
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
machine_config_constructor device_mconfig_additions() const override;
protected:
virtual void device_start(void) override;
virtual void device_reset(void) override;
virtual const rom_entry *device_rom_region(void) const override;
void device_start(void) override;
void device_reset(void) override;
private:
UINT8* m_ram;
// Console RAM
required_device<ram_device> m_ram;
int m_mapper[16];
bool m_map_mode;
bool m_access_mapper;

View File

@ -124,7 +124,18 @@ void ti99_datamux_device::read_all(address_space& space, UINT16 addr, UINT8 *val
// Video
if ((addr & 0xf801)==0x8800)
{
m_video->readz(space, addr, value);
// Forward to VDP unless we have an EVPC
if (m_video != nullptr)
{
if ((addr & 2) != 0)
{ // read VDP status
*value = m_video->register_read(space, 0);
}
else
{ // read VDP RAM
*value = m_video->vram_read(space, 0);
}
}
}
}
@ -152,12 +163,28 @@ void ti99_datamux_device::write_all(address_space& space, UINT16 addr, UINT8 val
// Cartridge port and sound
if ((addr & 0xe000)==0x6000) m_gromport->write(space, addr, value);
if ((addr & 0xfc01)==0x8400) m_sound->write(space, 0, value);
// Only if the sound chip has not been removed
if ((addr & 0xfc01)==0x8400)
{
if (m_sound != nullptr) m_sound->write(space, 0, value);
}
// Video
if ((addr & 0xf801)==0x8800)
{
m_video->write(space, addr, value);
// Forward to VDP unless we have an EVPC
if (m_video != nullptr)
{
if ((addr & 2) != 0)
{ // write VDP address/register
m_video->register_write(space, 0, value);
}
else
{ // write VDP data
m_video->vram_write(space, 0, value);
}
}
}
// PEB gets all accesses
@ -560,7 +587,7 @@ void ti99_datamux_device::device_reset(void)
void ti99_datamux_device::device_config_complete()
{
m_video = downcast<bus8z_device*>(owner()->subdevice(VIDEO_SYSTEM_TAG));
m_video = downcast<tms9928a_device*>(owner()->subdevice(VDP_TAG));
m_sound = downcast<sn76496_base_device*>(owner()->subdevice(TISOUNDCHIP_TAG));
m_gromport = downcast<gromport_device*>(owner()->subdevice(GROMPORT_TAG));
m_peb = downcast<peribox_device*>(owner()->subdevice(PERIBOX_TAG));

View File

@ -20,7 +20,6 @@
#include "bus/ti99_peb/peribox.h"
#include "sound/sn76496.h"
#include "video/tms9928a.h"
#include "bus/ti99x/videowrp.h"
extern const device_type DATAMUX;
@ -56,7 +55,7 @@ protected:
private:
// Link to the video processor
bus8z_device* m_video;
tms9928a_device* m_video;
// Link to the sound processor
sn76496_base_device* m_sound;

View File

@ -18,7 +18,6 @@
#define TMS9901_TAG "tms9901"
#define TIBOARD_TAG "ti_board"
#define DATAMUX_TAG "datamux_16_8"
#define VIDEO_SYSTEM_TAG "video"
#define SCREEN_TAG "screen"
#define TISOUNDCHIP_TAG "soundchip"
#define TISOUND_TAG "tisound"
@ -31,6 +30,8 @@
#define HANDSET_TAG "handset"
#define JOYPORT_TAG "joyport"
#define VDP_TAG "vdp"
#define EVPC_CONN_TAG "evpc_conn"
#define DSRROM "dsrrom"
#define CONSOLEROM "consolerom"
@ -137,6 +138,42 @@ public:
virtual DECLARE_SETADDRESS_DBIN_MEMBER( setaddress_dbin ) { };
};
/****************************************************************************
Connector from EVPC
We need this for the TI-99/4A console as well as for the SGCPU, so we have
to use callbacks
****************************************************************************/
class ti99_4x_state;
class evpc_clock_connector;
const device_type EVPC_CONN = &device_creator<evpc_clock_connector>;
class evpc_clock_connector : public device_t
{
public:
evpc_clock_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, EVPC_CONN, "EVPC clock connector", tag, owner, clock, "ti99_evpc_clock", __FILE__),
m_vdpint(*this) { };
template<class _Object> static devcb_base &static_set_vdpint_callback(device_t &device, _Object object)
{
return downcast<evpc_clock_connector &>(device).m_vdpint.set_callback(object);
}
WRITE_LINE_MEMBER( vclock_line ) { m_vdpint(state); }
void device_start() override { m_vdpint.resolve(); }
private:
// VDPINT line to the CPU
devcb_write_line m_vdpint;
};
#define MCFG_ADD_EVPC_CONNECTOR( _tag, _vdpint ) \
MCFG_DEVICE_ADD(_tag, EVPC_CONN, 0) \
devcb = &evpc_clock_connector::static_set_vdpint_callback( *device, DEVCB_##_vdpint );
/****************************************************************************
Constants
****************************************************************************/

View File

@ -1,121 +0,0 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
TI-99/4A Video subsystem
This device actually wraps the naked video chip implementation
EVPC (Enhanced Video Processor Card) from SNUG
based on v9938 (may also be equipped with v9958)
Can be used with TI-99/4A as an add-on card; internal VDP must be removed
The SGCPU ("TI-99/4P") only runs with EVPC
Michael Zapf
*****************************************************************************/
#include "emu.h"
#include "videowrp.h"
/*
Constructors
*/
ti_video_device::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)
: bus8z_device(mconfig, type, name, tag, owner, clock, shortname, source),
m_tms9928a(nullptr)
{
}
ti_std_video_device::ti_std_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: ti_video_device(mconfig, TI99VIDEO, "TI99 STD Video subsystem", tag, owner, clock, "ti99_video", __FILE__)
{
}
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)
{
}
/*****************************************************************************/
/*
Accessing TMS9928A (TI-99/4A)
*/
READ8Z_MEMBER( ti_std_video_device::readz )
{
if (space.debugger_access()) return;
if (offset & 2)
{ /* read VDP status */
*value = m_tms9928a->register_read(space, 0);
}
else
{ /* read VDP RAM */
*value = m_tms9928a->vram_read(space, 0);
}
}
WRITE8_MEMBER( ti_std_video_device::write )
{
if (space.debugger_access()) return;
if (offset & 2)
{ /* write VDP address */
m_tms9928a->register_write(space, 0, data);
}
else
{ /* write VDP data */
m_tms9928a->vram_write(space, 0, data);
}
}
/*****************************************************************************/
/*
Accessing v9938 via 16 bit bus (SGCPU)
*/
READ16_MEMBER( ti_exp_video_device::read16 )
{
if (space.debugger_access()) return 0;
return (int)(m_v9938->read(space, offset)<<8);
}
WRITE16_MEMBER( ti_exp_video_device::write16 )
{
if (space.debugger_access()) return;
m_v9938->write(space, offset, (data>>8)&0xff);
}
/******************************************************************************/
/*
Accessing v9938 via 8 bit bus (EVPC)
*/
READ8Z_MEMBER( ti_exp_video_device::readz )
{
if (space.debugger_access()) return;
*value = m_v9938->read(space, offset>>1);
}
WRITE8_MEMBER( ti_exp_video_device::write )
{
if (space.debugger_access()) return;
m_v9938->write(space, offset>>1, data);
}
void ti_video_device::device_start(void)
{
m_tms9928a = static_cast<tms9928a_device*>(machine().device(VDP_TAG));
}
void ti_exp_video_device::device_start(void)
{
m_v9938 = static_cast<v9938_device*>(machine().device(VDP_TAG));
}
/**************************************************************************/
const device_type TI99VIDEO = &device_creator<ti_std_video_device>;
const device_type V9938VIDEO = &device_creator<ti_exp_video_device>;

View File

@ -1,93 +0,0 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
TI-99/4A / EVPC Video subsystem
See videowrp.c for documentation
Michael Zapf
October 2010
January 2012: Rewritten as class
*****************************************************************************/
#ifndef __TIVIDEO__
#define __TIVIDEO__
#include "video/tms9928a.h"
#include "video/v9938.h"
#include "ti99defs.h"
class ti_video_device : public bus8z_device
{
public:
virtual void reset_vdp(int state) =0;
protected:
tms9928a_device *m_tms9928a;
/* 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 DECLARE_READ8Z_MEMBER(readz) override { };
virtual DECLARE_WRITE8_MEMBER(write) override { };
};
/*
Used in the TI-99/4A and TI-99/8
*/
class ti_std_video_device : public ti_video_device
{
public:
ti_std_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
void reset_vdp(int state) override { m_tms9928a->reset_line(state); }
};
/*
Used in the EVPC
*/
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);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ16_MEMBER(read16);
DECLARE_WRITE16_MEMBER(write16);
void reset_vdp(int state) override { m_v9938->reset_line(state); }
protected:
virtual void device_start(void) override;
private:
v9938_device *m_v9938;
};
extern const device_type TI99VIDEO;
extern const device_type V9938VIDEO;
/****************************************************************************/
#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, _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 )
#endif /* __TIVIDEO__ */

View File

@ -2,53 +2,126 @@
// copyright-holders:Michael Zapf
/****************************************************************************
SNUG SGCPU (a.k.a. 99/4p) system
SNUG Second Generation CPU (SGCPU, aka TI-99/4P)
This system is a reimplementation of the old ti99/4a console. It is known
both as the 99/4p ("peripheral box", since the system is a card to be
inserted in the peripheral box, instead of a self contained console), and
as the SGCPU ("Second Generation CPU", which was originally the name used
in TI documentation to refer to either (or both) TI99/5 and TI99/8
projects).
This system is known both as the TI-99/4P ("Peripheral box", since the
system is a card to be inserted in the peripheral box, instead of a
self-contained console), and as the SGCPU ("Second Generation CPU",
which was originally the name used in TI documentation to refer to either
(or both) TI-99/5 and TI-99/8 projects).
The SGCPU was designed and built by the SNUG (System 99 Users Group),
namely by Michael Becker for the hardware part and Harald Glaab for the
software part. It has no relationship with TI.
The card is architectured around a 16-bit bus (vs. an 8-bit bus in every
other TI99 system). It includes 64kb of ROM, including a GPL interpreter,
an internal DSR ROM which contains system-specific code, part of the TI
extended Basic interpreter, and up to 1Mbyte of RAM. It still includes a
16-bit to 8-bit multiplexer in order to support extension cards designed
for TI99/4a, but it can support 16-bit cards, too. It does not include
GROMs, video or sound: instead, it relies on the HSGPL and EVPC cards to
do the job.
The card is a complete redesign of the original TI-99/4A mainboard to fit
on a peripheral card, thus replacing the console. It shows no original
circuits on its board; the concept is to cannibalize a TI-99/4A console,
moving its main circuits (TMS9900, TMS9901) into the sockets on this board.
IMPORTANT: The SGCPU card relies on a properly set up HSGPL flash memory
card; without, it will immediately lock up. It is impossible to set it up
from here (a bootstrap problem; you cannot start without the HSGPL).
The best chance is to start a ti99_4ev with a plugged-in HSGPL
and go through the setup process there. Copy the nvram files of the hsgpl into this
driver's nvram subdirectory. The contents will be directly usable for the SGCPU.
The sound chip is not plugged on the SGCPU but on the EVPC card which
provides the video processor for SGCPU card (see below).
The card offers a PC-style keyboard interface which adapts the keyboard
to the matrix organisation expected by the operating system of the TI.
All decoding and further features are implemented by a MACH chip, which
appears on many SNUG cards.
On the card, most circuits are directly accessed by a 16-bit data bus,
which ensures a significant speed-up compared to the original console. Only
when accessing external devices via the PEB, a databus multiplexer comes into
play which is implemented in the same way as the one in the original console,
also contained in the MACH.
The SGCPU offers a special connector at the back, containing the remaining
8 data bus lines; by this feature, expansion cards can be connected at
full 16 bit width. Only the HRD16 card (not yet emulated), which is a
RAMDisk card, actually uses it.
EPROM layout 64K
----------------
The memory region is shifted by 4000 in the EPROM address space
According to the designers, this is caused by the next-to most significant
address line (2^14) being locked to 1. This is done to allow for smaller
24pin EPROM to be used.
Area EPROM offset Mapped at
---------------------------------------
ROM0 4000 (0100) 0000
DSR C000 (1100) 4000
ROM6A 6000 (0110) 6000
ROM6B E000 (1110) 6000
System ROM
----------
The GPL interpreter is located in the EPROM as ROM0 (see above). The
SGCPU does not contain any GROM, which contain the actual TI operating
system and the BASIC interpreter. The GROMs are replaced by the HSGPL card.
==== CAUTION ====: This means that the HSGPL must be properly set up before
starting up the SGCPU. Otherwise, the emulation locks up immediately with a
BLACK SCREEN.
In the real environment, the HSGPL has usually been set up on delivery.
In MESS we have to create a suitable HSGPL memory content. Best practice
is to start the TI-99/4A console with EVPC support (driver ti99_4ev) with
a plugged-in HSGPL and to go through the setup process there.
Finally, the nvram files of the HSGPL must be copied into this driver's nvram
subdirectory. The contents will be directly usable for the SGCPU.
RAM: AEMS emulation
--------------------
The Asgard Expanded Memory System is a peripheral card whose successor
(Super AMS) is available in MESS. The AEMS card is emulated inside the MACH
chip of the SGCPU. For more information see samsmem.cpp.
The first four address lines are used to select one of 16 mapper values with
8 bits each. Instead of these first 4 lines, the 8 bits are prepended to
the remaining address, yielding a 20 bit address space.
The mapper values are mapped into the address space at 4000 by setting
CRU bit 1E00. Only the even addresses are used, so the first mapper byte is
at 4000, the second at 4002, the last one at 401E.
The mapping mode can be turned on and off by the CRU bit at address 1E02.
When turned off, the address is passed through to the RAM circuits.
Since the only RAM areas on the TI systems are at 2000-3FFF and A000-FFFF,
the typical usage is to use the AEMS as a 32K expansion in unmapped mode
(the remaining 32K of the address space is decoded earlier, and does not
affect the card), and to use it as paged memory in the 2000-3FFF and A000-FFFF
areas by setting the mapper appropriately. Mapper registers referring to
other memory areas have no effect.
Video and sound
---------------
The SGCPU relies on the EVPC or EVPC2 card to provide video capabilities.
This card (rel.1) is emulated in MESS and is based on the v9938 video
display processor.
In order to route the VDP interrupt to the SGCPU card, the previously
unused LCP* line in the Peripheral Expansion Box is used.
The sound chip requires the video clock, and therefore it is moved from the
console to the EVPC card.
Joystick and cassette
---------------------
The card features a 25-pin connector at the back which contains the lines
for the joysticks and one cassette input/output. An adapter must be built
to be able to use the common cables.
Michael Zapf
February 2012: Rewritten as class
*****************************************************************************/
#include "emu.h"
#include "cpu/tms9900/tms9900.h"
#include "sound/wave.h"
#include "sound/dac.h"
#include "sound/sn76496.h"
#include "machine/tms9901.h"
#include "imagedev/cassette.h"
#include "bus/ti99x/videowrp.h"
#include "bus/ti99x/joyport.h"
#include "bus/ti99_peb/peribox.h"
#define TMS9901_TAG "tms9901"
@ -57,6 +130,9 @@
#define TRACE_ILLWRITE 0
#define TRACE_READY 0
#define TRACE_INT 0
#define TRACE_ADDRESS 0
#define TRACE_MEM 0
#define TRACE_MUX 0
class ti99_4p_state : public driver_device
{
@ -65,28 +141,28 @@ public:
: driver_device(mconfig, type, tag),
m_cpu(*this, "maincpu"),
m_tms9901(*this, TMS9901_TAG),
m_sound(*this, TISOUNDCHIP_TAG),
m_video(*this, VIDEO_SYSTEM_TAG),
m_cassette(*this, "cassette"),
m_peribox(*this, PERIBOX_TAG),
m_joyport(*this, JOYPORT_TAG) { }
DECLARE_WRITE_LINE_MEMBER( console_ready );
DECLARE_WRITE_LINE_MEMBER( console_ready_dmux );
DECLARE_WRITE_LINE_MEMBER( ready_line );
DECLARE_WRITE_LINE_MEMBER( extint );
DECLARE_WRITE_LINE_MEMBER( notconnected );
DECLARE_READ8_MEMBER( interrupt_level );
DECLARE_SETOFFSET_MEMBER( setoffset );
DECLARE_READ16_MEMBER( memread );
DECLARE_WRITE16_MEMBER( memwrite );
DECLARE_WRITE_LINE_MEMBER( dbin_in );
DECLARE_READ16_MEMBER( samsmem_read );
DECLARE_WRITE16_MEMBER( samsmem_write );
DECLARE_WRITE8_MEMBER(external_operation);
DECLARE_WRITE_LINE_MEMBER( clock_out );
DECLARE_WRITE_LINE_MEMBER( dbin_line );
void clock_in(int clock);
void datamux_clock_in(int clock);
// CRU (Communication Register Unit) handling
DECLARE_READ8_MEMBER( cruread );
@ -103,12 +179,10 @@ public:
virtual void machine_start() override;
DECLARE_MACHINE_RESET(ti99_4p);
DECLARE_WRITE_LINE_MEMBER(set_tms9901_INT2_from_v9938);
DECLARE_WRITE_LINE_MEMBER(video_interrupt_in);
required_device<tms9900_device> m_cpu;
required_device<tms9901_device> m_tms9901;
required_device<sn76496_base_device> m_sound;
required_device<ti_exp_video_device> m_video;
required_device<cassette_image_device> m_cassette;
required_device<peribox_device> m_peribox;
required_device<joyport_device> m_joyport;
@ -116,15 +190,6 @@ public:
// Pointer to ROM0
UINT16 *m_rom0;
// Pointer to DSR ROM
UINT16 *m_dsr;
// Pointer to ROM6, first bank
UINT16 *m_rom6a;
// Pointer to ROM6, second bank
UINT16 *m_rom6b;
// AMS RAM (1 Mib)
std::vector<UINT16> m_ram;
@ -134,12 +199,14 @@ public:
// First joystick. 6 for TI-99/4A
int m_firstjoy;
// READY line
int m_ready_line, m_ready_line_dmux;
private:
DECLARE_READ16_MEMBER( datamux_read );
DECLARE_WRITE16_MEMBER( datamux_write );
int decode_address(int address);
DECLARE_READ16_MEMBER( debugger_read );
DECLARE_WRITE16_MEMBER( debugger_write );
void ready_join();
void set_keyboard_column(int number, int data);
int m_keyboard_column;
@ -163,6 +230,36 @@ private:
// TRUE when mapper registers are accessible
bool m_access_mapper;
// Value on address bus (after being set by setaddress)
int m_addr_buf;
// Address decoding result
int m_decode;
// Ready state of the databus multiplexer
bool m_muxready;
// Incoming Ready level
line_state m_sysready;
// Saves a pointer to the address space
address_space* m_spacep;
// Internal DSR mapped in
bool m_internal_dsr_active;
// Mapper visible in 4000 area
bool m_mapper_active;
// ROM6 visible in 6000
bool m_rom6_active;
// Upper bank of ROM6 selected
bool m_rom6_upper;
// State of the DBIN line
line_state m_dbin;
UINT8 m_lowbyte;
UINT8 m_highbyte;
UINT8 m_latch;
@ -170,6 +267,9 @@ private:
// Mapper registers
UINT8 m_mapper[16];
// Pointer to EPROM
UINT16 *m_rom;
// Latch for 9901 INT2, INT1 lines
int m_9901_int;
void set_9901_int(int line, line_state state);
@ -178,8 +278,16 @@ private:
};
enum
{
ROM0BASE = 0x4000,
DSRBASE = 0xc000,
ROM6LBASE = 0x6000,
ROM6UBASE = 0xe000
};
static ADDRESS_MAP_START(memmap, AS_PROGRAM, 16, ti99_4p_state)
AM_RANGE(0x0000, 0xffff) AM_READWRITE( memread, memwrite )
AM_RANGE(0x0000, 0xffff) AM_READWRITE( memread, memwrite ) AM_SETOFFSET( setoffset )
ADDRESS_MAP_END
static ADDRESS_MAP_START(cru_map, AS_IO, 8, ti99_4p_state)
@ -267,226 +375,309 @@ static INPUT_PORTS_START(ti99_4p)
INPUT_PORTS_END
enum
{
SGCPU_NONE = 0,
SGCPU_SYSROM,
SGCPU_RAM,
SGCPU_INTDSR,
SGCPU_MAPPER,
SGCPU_ROM6,
SGCPU_PADRAM,
SGCPU_PEB
};
int ti99_4p_state::decode_address(int address)
{
int dec = SGCPU_NONE;
switch (address & 0xe000)
{
case 0x0000:
dec = SGCPU_SYSROM;
break;
case 0x2000:
case 0xa000:
case 0xc000:
case 0xe000:
dec = SGCPU_RAM;
break;
case 0x4000:
if (m_internal_dsr_active) dec = SGCPU_INTDSR;
else if (m_mapper_active) dec = SGCPU_MAPPER;
break;
case 0x6000:
if (m_rom6_active) dec = SGCPU_ROM6;
break;
case 0x8000:
if ((m_addr_buf & 0x1c00)==0x0000) dec = SGCPU_PADRAM;
break;
default:
break;
}
return dec;
}
/*
Memory access
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_4p_state::setoffset )
{
m_addr_buf = offset << 1;
m_waitcount = 0;
if (TRACE_ADDRESS) logerror("set address %04x\n", m_addr_buf);
m_decode = SGCPU_NONE;
m_muxready = true;
m_spacep = &space;
m_decode = decode_address(m_addr_buf);
if (m_decode == SGCPU_NONE)
{
// not found - pass on to PEB, 8 bit access with wait states as in TI-99/4A console
// PEB gets remaining accesses
// HSGPL, EVPC, other devices
m_decode = SGCPU_PEB;
m_waitcount = 5;
m_muxready = false;
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, m_addr_buf+1, m_dbin);
}
ready_join();
}
READ16_MEMBER( ti99_4p_state::memread )
{
int addroff = offset << 1;
if (m_rom0 == nullptr) return 0; // premature access
int address = 0;
UINT8 hbyte = 0;
UINT16 zone = addroff & 0xe000;
UINT16 value = 0;
if (zone==0x0000)
int addr_off8k = m_addr_buf & 0x1fff;
// If we use the debugger, decode the address now (normally done in setaddress)
if (space.debugger_access())
{
// ROM0
value = m_rom0[(addroff & 0x1fff)>>1];
return value;
}
if (zone==0x2000 || zone==0xa000 || zone==0xc000 || zone==0xe000)
{
value = samsmem_read(space, offset, mem_mask);
return value;
m_addr_buf = offset << 1;
m_decode = decode_address(m_addr_buf);
}
if (zone==0x4000)
switch (m_decode)
{
if (m_internal_dsr)
{
value = m_dsr[(addroff & 0x1fff)>>1];
return value;
}
else
{
if (m_access_mapper && ((addroff & 0xffe0)==0x4000))
{
value = m_mapper[offset & 0x000f]<<8;
return value;
}
}
case SGCPU_SYSROM:
value = m_rom[(ROM0BASE | addr_off8k) >> 1];
break;
case SGCPU_RAM:
// Memory read. The AEMS emulation has two address areas: The memory is at locations
// 0x2000-0x3fff and 0xa000-0xffff, and the mapper area is at 0x4000-0x401e
// (only even addresses).
if (m_map_mode)
address = (m_mapper[(m_addr_buf & 0xf000)>>12] << 12) | (m_addr_buf & 0x0fff);
else // transparent mode
address = m_addr_buf;
value = m_ram[address>>1];
break;
case SGCPU_INTDSR:
value = m_rom[(DSRBASE | addr_off8k)>>1];
break;
case SGCPU_MAPPER:
value = (m_mapper[m_addr_buf & 0x000f]<<8) & 0xff00;
break;
case SGCPU_ROM6:
value = m_rom[((m_rom6_upper? ROM6UBASE : ROM6LBASE) | addr_off8k)>>1];
break;
case SGCPU_PADRAM:
// Scratch pad RAM (16 bit)
// 8000 ... 83ff (1K, 4 times the size of the internal RAM of the TI-99/4A)
value = m_scratchpad[(m_addr_buf & 0x03ff)>>1];
break;
case SGCPU_PEB:
if (space.debugger_access()) return debugger_read(space, offset);
// The byte from the odd address has already been read into the latch
// Reading the even address now
m_peribox->readz(space, m_addr_buf, &hbyte);
m_peribox->memen_in(CLEAR_LINE);
if (TRACE_MEM) logerror("Read even byte from address %04x -> %02x\n", m_addr_buf, hbyte);
value = (hbyte<<8) | m_latch;
}
if (zone==0x6000 && m_internal_rom6)
{
if (m_rom6_bank==0)
value = m_rom6a[(addroff & 0x1fff)>>1];
else
value = m_rom6b[(addroff & 0x1fff)>>1];
return value;
}
// Scratch pad RAM and sound
// speech is in peribox
// groms are in hsgpl in peribox
if (zone==0x8000)
{
if ((addroff & 0xfff0)==0x8400) // cannot read from sound
{
value = 0;
return value;
}
if ((addroff & 0xfc00)==0x8000)
{
value = m_scratchpad[(addroff & 0x03ff)>>1];
return value;
}
// Video: 8800, 8802
if ((addroff & 0xfffd)==0x8800)
{
value = m_video->read16(space, offset, mem_mask);
return value;
}
}
// If we are here, check the peribox via the datamux
// catch-all for unmapped zones
value = datamux_read(space, offset, mem_mask);
return value;
}
WRITE16_MEMBER( ti99_4p_state::memwrite )
{
// m_cpu->adjust_icount(-4);
int address = 0;
int addroff = offset << 1;
UINT16 zone = addroff & 0xe000;
if (zone==0x0000)
// If we use the debugger, decode the address now (normally done in setaddress)
if (space.debugger_access())
{
// ROM0
if (TRACE_ILLWRITE) logerror("Ignoring ROM write access at %04x\n", addroff);
return;
m_addr_buf = offset << 1;
m_decode = decode_address(m_addr_buf);
}
if (zone==0x2000 || zone==0xa000 || zone==0xc000 || zone==0xe000)
switch (m_decode)
{
samsmem_write(space, offset, data, mem_mask);
return;
}
case SGCPU_SYSROM:
if (TRACE_ILLWRITE) logerror("Ignoring ROM write access at %04x\n", m_addr_buf);
break;
if (zone==0x4000)
{
if (m_internal_dsr)
{
if (TRACE_ILLWRITE) logerror("Ignoring DSR write access at %04x\n", addroff);
return;
}
else
{
if (m_access_mapper && ((addroff & 0xffe0)==0x4000))
{
m_mapper[offset & 0x000f] = data;
return;
}
}
}
case SGCPU_RAM:
// see above
if (m_map_mode)
address = (m_mapper[(m_addr_buf & 0xf000)>>12] << 12) | (m_addr_buf & 0x0fff);
else // transparent mode
address = m_addr_buf;
if (zone==0x6000 && m_internal_rom6)
{
m_rom6_bank = offset & 0x0001;
return;
}
m_ram[address>>1] = data;
break;
// Scratch pad RAM and sound
// speech is in peribox
// groms are in hsgpl in peribox
if (zone==0x8000)
{
if ((addroff & 0xfff0)==0x8400) //sound write
{
m_sound->write(space, 0, (data >> 8) & 0xff);
return;
}
if ((addroff & 0xfc00)==0x8000)
{
m_scratchpad[(addroff & 0x03ff)>>1] = data;
return;
}
// Video: 8C00, 8C02
if ((addroff & 0xfffd)==0x8c00)
{
m_video->write16(space, offset, data, mem_mask);
return;
}
}
case SGCPU_INTDSR:
if (TRACE_ILLWRITE) logerror("Ignoring DSR write access at %04x\n", m_addr_buf);
break;
// If we are here, check the peribox via the datamux
// catch-all for unmapped zones
datamux_write(space, offset, data, mem_mask);
case SGCPU_MAPPER:
m_mapper[(m_addr_buf>>1) & 0x000f] = data; // writing both bytes, but only the first is accepted
break;
case SGCPU_ROM6:
// Writing to 6002 sets upper bank
m_rom6_upper = (m_addr_buf & 0x0002)!=0;
break;
case SGCPU_PADRAM:
// Scratch pad RAM (16 bit)
// 8000 ... 83ff (1K, 4 times the size of the internal RAM of the TI-99/4A)
m_scratchpad[(m_addr_buf & 0x03ff)>>1] = data;
break;
case SGCPU_PEB:
if (space.debugger_access()) { debugger_write(space, offset, data); return; }
// Writing the even address now (addr)
// The databus multplexer puts the even value into the latch and outputs the odd value now.
m_latch = (data >> 8) & 0xff;
// write odd byte
if (TRACE_MEM) logerror("datamux: write odd byte to address %04x <- %02x\n", m_addr_buf+1, data & 0xff);
m_peribox->write(space, m_addr_buf+1, data & 0xff);
m_peribox->memen_in(CLEAR_LINE);
}
}
/***************************************************************************
Internal datamux; similar to TI-99/4A. However, here we have just
one device, the peripheral box, so it is much simpler.
***************************************************************************/
/*
Used when the debugger is reading values from PEB cards.
*/
READ16_MEMBER( ti99_4p_state::debugger_read )
{
UINT8 lval = 0;
UINT8 hval = 0;
UINT16 addrb = offset << 1;
m_peribox->memen_in(ASSERT_LINE);
m_peribox->readz(space, addrb+1, &lval);
m_peribox->readz(space, addrb, &hval);
m_peribox->memen_in(CLEAR_LINE);
return ((hval << 8)&0xff00) | (lval & 0xff);
}
/*
Used when the debugger is writing values to PEB cards.
*/
WRITE16_MEMBER( ti99_4p_state::debugger_write )
{
int addrb = offset << 1;
m_peribox->memen_in(ASSERT_LINE);
m_peribox->write(space, addrb+1, data & 0xff);
m_peribox->write(space, addrb, (data>>8) & 0xff);
m_peribox->memen_in(CLEAR_LINE);
}
/*
Data bus in (DBIN) line from the CPU.
*/
WRITE_LINE_MEMBER( ti99_4p_state::dbin_line )
{
m_dbin = (line_state)state;
}
/*
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_4p_state::clock_in(int clock)
WRITE_LINE_MEMBER( ti99_4p_state::datamux_clock_in )
{
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) console_ready_dmux(ASSERT_LINE);
if (TRACE_MUX) logerror("datamux: wait count %d\n", m_waitcount);
if (m_sysready==CLEAR_LINE)
{
if (TRACE_MUX) logerror("datamux: stalled due to external READY=0\n");
return;
}
if (m_dbin==ASSERT_LINE)
{
// Reading
if (state==ASSERT_LINE)
{ // raising edge
if (--m_waitcount==0)
{
m_muxready = true;
ready_join();
}
if (m_waitcount==2)
{
// read odd byte
m_peribox->readz(*m_spacep, m_addr_buf+1, &m_latch);
m_peribox->memen_in(CLEAR_LINE);
if (TRACE_MEM) logerror("datamux: read odd byte from address %04x -> %02x\n", m_addr_buf+1, m_latch);
// do the setaddress for the even address
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(*m_spacep, m_addr_buf, m_dbin);
}
}
}
else // write access
{
if (state==ASSERT_LINE)
{ // raising edge
if (--m_waitcount==0)
{
m_muxready = true;
ready_join();
}
}
else
{ // falling edge
if (m_waitcount==2)
{
// do the setaddress for the even address
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(*m_spacep, m_addr_buf, m_dbin);
// write even byte
if (TRACE_MEM) logerror("datamux: write even byte to address %04x <- %02x\n", m_addr_buf, m_latch);
m_peribox->write(*m_spacep, m_addr_buf, m_latch);
m_peribox->memen_in(CLEAR_LINE);
}
}
}
}
}
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);
// Insert four wait states and let CPU enter wait state
m_waitcount = 6;
console_ready_dmux(CLEAR_LINE);
return (hbyte<<8) | m_latch ;
}
/*
Write access.
TODO: use the 16-bit expansion in the box for suitable cards
*/
WRITE16_MEMBER( ti99_4p_state::datamux_write )
{
UINT16 addroff = (offset << 1);
// printf("write address = %04x, value = %04x, memmask = %4x\n", addroff, data, mem_mask);
// 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);
}
/***************************************************************************
CRU interface
***************************************************************************/
@ -528,46 +719,6 @@ READ8_MEMBER( ti99_4p_state::cruread )
return value;
}
/***************************************************************************
AMS Memory implementation
***************************************************************************/
/*
Memory read. The SAMS card has two address areas: The memory is at locations
0x2000-0x3fff and 0xa000-0xffff, and the mapper area is at 0x4000-0x401e
(only even addresses).
*/
READ16_MEMBER( ti99_4p_state::samsmem_read )
{
UINT32 address = 0;
int addroff = offset << 1;
// select memory expansion
if (m_map_mode)
address = (m_mapper[(addroff>>12) & 0x000f] << 12) + (addroff & 0x0fff);
else // transparent mode
address = addroff;
return m_ram[address>>1];
}
/*
Memory write
*/
WRITE16_MEMBER( ti99_4p_state::samsmem_write )
{
UINT32 address = 0;
int addroff = offset << 1;
// select memory expansion
if (m_map_mode)
address = (m_mapper[(addroff>>12) & 0x000f] << 12) + (addroff & 0x0fff);
else // transparent mode
address = addroff;
m_ram[address>>1] = data;
}
/***************************************************************************
Keyboard/tape control
****************************************************************************/
@ -700,15 +851,11 @@ WRITE_LINE_MEMBER( ti99_4p_state::cassette_output )
****************************************************************************/
/*
We may have lots of devices pulling down this line; so we should use a AND
gate to do it right. On the other hand, when READY is down, there is just
no chance to make another device pull down the same line; the CPU just
won't access any other device in this time.
Combine the external (sysready) and the own (muxready) READY states.
*/
WRITE_LINE_MEMBER( ti99_4p_state::console_ready )
void ti99_4p_state::ready_join()
{
m_ready_line = state;
int combined = (m_ready_line == ASSERT_LINE && m_ready_line_dmux == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE;
int combined = (m_sysready == ASSERT_LINE && m_muxready)? ASSERT_LINE : CLEAR_LINE;
if (TRACE_READY)
{
@ -719,21 +866,17 @@ WRITE_LINE_MEMBER( ti99_4p_state::console_ready )
}
/*
The exception of the above rule. Memory access over the datamux also operates
the READY line, and the datamux raises READY depending on the clock pulse.
So we must make sure this does not interfere.
Incoming READY line from other cards in the Peripheral Expansion Box.
*/
WRITE_LINE_MEMBER( ti99_4p_state::console_ready_dmux )
WRITE_LINE_MEMBER( ti99_4p_state::ready_line )
{
m_ready_line_dmux = state;
int combined = (m_ready_line == ASSERT_LINE && m_ready_line_dmux == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE;
if (TRACE_READY)
{
if (m_ready_prev != combined) logerror("READY dmux level = %d\n", state);
if (state != m_sysready) logerror("READY line from PBox = %d\n", state);
}
m_ready_prev = combined;
m_cpu->set_ready(combined);
m_sysready = (line_state)state;
// Also propagate to CPU via driver
ready_join();
}
void ti99_4p_state::set_9901_int( int line, line_state state)
@ -760,7 +903,8 @@ WRITE_LINE_MEMBER( ti99_4p_state::notconnected )
*/
WRITE_LINE_MEMBER( ti99_4p_state::clock_out )
{
clock_in(state);
datamux_clock_in(state);
m_peribox->clock_in(state);
}
WRITE8_MEMBER( ti99_4p_state::tms9901_interrupt )
@ -781,7 +925,7 @@ READ8_MEMBER( ti99_4p_state::interrupt_level )
WRITE8_MEMBER( ti99_4p_state::external_operation )
{
static const char* extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" };
logerror("External operation %s not implemented on the SGCPU board\n", extop[offset]);
if (offset != IDLE_OP) logerror("External operation %s not implemented on the SGCPU board\n", extop[offset]);
}
/*****************************************************************************/
@ -796,19 +940,16 @@ void ti99_4p_state::machine_start()
m_firstjoy = 6;
m_ready_line = m_ready_line_dmux = ASSERT_LINE;
m_sysready = ASSERT_LINE;
m_muxready = true;
UINT16 *rom = (UINT16*)(memregion("maincpu")->base());
m_rom0 = rom + 0x2000;
m_dsr = rom + 0x6000;
m_rom6a = rom + 0x3000;
m_rom6b = rom + 0x7000;
m_rom = (UINT16*)(memregion("maincpu")->base());
}
/*
set the state of int2 (called by the v9938)
*/
WRITE_LINE_MEMBER(ti99_4p_state::set_tms9901_INT2_from_v9938)
WRITE_LINE_MEMBER(ti99_4p_state::video_interrupt_in)
{
set_9901_int(2, (line_state)state);
}
@ -836,12 +977,7 @@ static MACHINE_CONFIG_START( ti99_4p_60hz, ti99_4p_state )
MCFG_TMS99xx_EXTOP_HANDLER( WRITE8(ti99_4p_state, external_operation) )
MCFG_TMS99xx_INTLEVEL_HANDLER( READ8(ti99_4p_state, interrupt_level) )
MCFG_TMS99xx_CLKOUT_HANDLER( WRITELINE(ti99_4p_state, clock_out) )
/* video hardware */
MCFG_DEVICE_ADD(VIDEO_SYSTEM_TAG, V9938VIDEO, 0)
MCFG_V9938_ADD(VDP_TAG, SCREEN_TAG, 0x20000, XTAL_21_4772MHz) /* typical 9938 clock, not verified */
MCFG_V99X8_INTERRUPT_CALLBACK(WRITELINE(ti99_4p_state, set_tms9901_INT2_from_v9938))
MCFG_V99X8_SCREEN_ADD_NTSC(SCREEN_TAG, VDP_TAG, XTAL_21_4772MHz)
MCFG_TMS99xx_DBIN_HANDLER( WRITELINE(ti99_4p_state, dbin_line) )
// tms9901
MCFG_DEVICE_ADD(TMS9901_TAG, TMS9901, 3000000)
@ -858,13 +994,10 @@ static MACHINE_CONFIG_START( ti99_4p_60hz, ti99_4p_state )
MCFG_DEVICE_ADD( PERIBOX_TAG, PERIBOX_SG, 0)
MCFG_PERIBOX_INTA_HANDLER( WRITELINE(ti99_4p_state, extint) )
MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_4p_state, notconnected) )
MCFG_PERIBOX_READY_HANDLER( WRITELINE(ti99_4p_state, console_ready) )
MCFG_PERIBOX_READY_HANDLER( WRITELINE(ti99_4p_state, ready_line) )
// Sound hardware
MCFG_SPEAKER_STANDARD_MONO("sound_out")
MCFG_SOUND_ADD(TISOUNDCHIP_TAG, SN94624, 3579545/8) /* 3.579545 MHz */
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "sound_out", 0.75)
MCFG_SN76496_READY_HANDLER( WRITELINE(ti99_4p_state, console_ready) )
// The SGCPU actually makes use of this pin which was unused before
MCFG_PERIBOX_LCP_HANDLER( WRITELINE(ti99_4p_state, video_interrupt_in) )
// Cassette drives
MCFG_SPEAKER_STANDARD_MONO("cass_out")
@ -887,4 +1020,4 @@ ROM_START(ti99_4p)
ROM_END
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */
COMP( 1996, ti99_4p, 0, 0, ti99_4p_60hz, ti99_4p, driver_device, 0, "System 99 Users Group", "SGCPU (a.k.a. 99/4P)" , 0 )
COMP( 1996, ti99_4p, 0, 0, ti99_4p_60hz, ti99_4p, driver_device, 0, "System-99 User Group", "SGCPU (aka TI-99/4P)" , 0 )

View File

@ -46,7 +46,7 @@
#include "machine/tms9901.h"
#include "imagedev/cassette.h"
#include "bus/ti99x/videowrp.h"
#include "bus/ti99x/ti99defs.h"
#include "bus/ti99x/datamux.h"
#include "bus/ti99x/gromport.h"
#include "bus/ti99x/joyport.h"
@ -73,7 +73,7 @@ public:
m_peribox(*this, PERIBOX_TAG),
m_joyport(*this, JOYPORT_TAG),
m_datamux(*this, DATAMUX_TAG),
m_video(*this, VIDEO_SYSTEM_TAG),
m_video(*this, VDP_TAG),
m_cassette1(*this, "cassette"),
m_cassette2(*this, "cassette2")
{ }
@ -130,6 +130,7 @@ public:
DECLARE_INPUT_CHANGED_MEMBER( load_interrupt );
// Used by EVPC
DECLARE_WRITE_LINE_MEMBER( video_interrupt_evpc_in );
void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
private:
@ -157,7 +158,7 @@ private:
required_device<peribox_device> m_peribox;
required_device<joyport_device> m_joyport;
required_device<ti99_datamux_device> m_datamux;
required_device<ti_video_device> m_video;
optional_device<tms9928a_device> m_video;
required_device<cassette_image_device> m_cassette1;
required_device<cassette_image_device> m_cassette2;
@ -655,6 +656,13 @@ void ti99_4x_state::device_timer(emu_timer &timer, device_timer_id id, int param
/*****************************************************************************/
WRITE_LINE_MEMBER( ti99_4x_state::video_interrupt_evpc_in )
{
if (TRACE_INTERRUPTS) logerror("ti99_4x: VDP INT2 from EVPC on tms9901, level=%d\n", state);
m_int2 = (line_state)state;
m_tms9901->set_single_int(2, state);
}
/*
set the state of TMS9901's INT2 (called by the tms9928 core)
*/
@ -754,7 +762,7 @@ WRITE_LINE_MEMBER( ti99_4x_state::console_reset )
{
logerror("ti99_4x: Console reset line = %d\n", state);
m_cpu->set_input_line(INT_9900_RESET, state);
m_video->reset_vdp(state);
m_video->reset_line(state);
}
}
@ -770,6 +778,37 @@ WRITE_LINE_MEMBER( ti99_4x_state::notconnected )
if (TRACE_INTERRUPTS) logerror("ti99_4x: Setting a not connected line ... ignored\n");
}
#if 0
/*
External clock connector. This is actually a separate cable lead going from
the EPVC in the PEB to a pin inside the console. This cable sends the
video interrupt from the v9938 on the EVPC into the console.
This workaround must be done on the real system because the peripheral
box and its connector were not designed to deliver a video interrupt signal.
This was fixed with the EVPC2 which uses the external interrupt EXTINT
with a special firmware (DSR).
Emulation detail: We are using a separate device class in order to avoid
exposing the console class to the external class.
*/
evpc_clock_connector::evpc_clock_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, EVPC_CONN, "EVPC clock connector", tag, owner, clock, "ti99_evpc_clock", __FILE__)
{
}
void evpc_clock_connector::device_start()
{
m_console = downcast<ti99_4x_state*>(owner());
}
WRITE_LINE_MEMBER( evpc_clock_connector::vclock_line )
{
m_console->video_interrupt_in(state);
};
const device_type EVPC_CONN = &device_creator<evpc_clock_connector>;
#endif
/******************************************************************************
Machine definitions
******************************************************************************/
@ -863,14 +902,24 @@ 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, gromclk_in)
MCFG_DEVICE_ADD( VDP_TAG, TMS9918, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(0x4000) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_4x_state, video_interrupt_in)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(ti99_4x_state, gromclk_in)) \
MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
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, gromclk_in)
MCFG_DEVICE_ADD( VDP_TAG, TMS9929, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(0x4000) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_4x_state, video_interrupt_in)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(ti99_4x_state, gromclk_in)) \
MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
MACHINE_CONFIG_END
/**********************************************************************
@ -961,14 +1010,24 @@ 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, gromclk_in)
MCFG_DEVICE_ADD( VDP_TAG, TMS9918A, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(0x4000) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_4x_state, video_interrupt_in)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(ti99_4x_state, gromclk_in)) \
MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
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, gromclk_in)
MCFG_DEVICE_ADD( VDP_TAG, TMS9929A, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(0x4000) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_4x_state, video_interrupt_in)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(ti99_4x_state, gromclk_in)) \
MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
MACHINE_CONFIG_END
/************************************************************************
@ -996,19 +1055,32 @@ 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, gromclk_in)
MCFG_DEVICE_ADD( VDP_TAG, TMS9918A, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(0x4000) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_4x_state, video_interrupt_in)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(ti99_4x_state, gromclk_in)) \
MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
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, gromclk_in)
MCFG_DEVICE_ADD( VDP_TAG, TMS9929A, XTAL_10_738635MHz / 2 ) \
MCFG_TMS9928A_VRAM_SIZE(0x4000) \
MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_4x_state, video_interrupt_in)) \
MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(ti99_4x_state, gromclk_in)) \
MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) \
MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update )
MACHINE_CONFIG_END
/************************************************************************
TI-99/4A with 80-column support. Actually a separate expansion card (EVPC),
replacing the console video processor.
Note that the sound chip is also moved to this card, because the SGCPU,
which is intended to use the EVPC, does not have an own sound chip.
*************************************************************************/
MACHINE_START_MEMBER(ti99_4x_state, ti99_4ev)
@ -1043,12 +1115,6 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state )
MCFG_MACHINE_START_OVERRIDE(ti99_4x_state, ti99_4ev )
MCFG_MACHINE_RESET_OVERRIDE(ti99_4x_state, ti99_4ev )
/* video hardware */
MCFG_DEVICE_ADD(VIDEO_SYSTEM_TAG, V9938VIDEO, 0)
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)
/* Main board */
MCFG_DEVICE_ADD(TMS9901_TAG, TMS9901, 3000000)
MCFG_TMS9901_READBLOCK_HANDLER( READ8(ti99_4x_state, read_by_9901) )
@ -1068,6 +1134,9 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state )
MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_cart) )
MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_4x_state, console_reset) )
// EVPC connector
MCFG_ADD_EVPC_CONNECTOR( EVPC_CONN_TAG, WRITELINE( ti99_4x_state, video_interrupt_evpc_in ) )
/* Software list */
MCFG_SOFTWARE_LIST_ADD("cart_list_ti99", "ti99_cart")
@ -1077,12 +1146,6 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state )
MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_4x_state, notconnected) )
MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(DATAMUX_TAG, ti99_datamux_device, ready_line) )
// Sound hardware
MCFG_SPEAKER_STANDARD_MONO("sound_out")
MCFG_SOUND_ADD(TISOUNDCHIP_TAG, SN94624, 3579545/8) /* 3.579545 MHz */
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "sound_out", 0.75)
MCFG_SN76496_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_sound) )
/* Cassette drives */
MCFG_SPEAKER_STANDARD_MONO("cass_out")
MCFG_CASSETTE_ADD( "cassette" )

View File

@ -182,7 +182,6 @@ Known Issues (MZ, 2010-11-07)
#include "imagedev/cassette.h"
#include "bus/ti99x/998board.h"
#include "bus/ti99x/videowrp.h"
#include "bus/ti99x/gromport.h"
#include "bus/ti99x/joyport.h"