From 50b20ddf23ea1b959732f6ad65d54f3949f9f068 Mon Sep 17 00:00:00 2001 From: Michael Zapf Date: Tue, 23 May 2017 23:20:52 +0200 Subject: ti99: Wrapped into namespaces; minor rearrangement --- scripts/src/bus.lua | 24 +- src/devices/bus/ti99/colorbus/busmouse.cpp | 7 +- src/devices/bus/ti99/colorbus/busmouse.h | 5 +- src/devices/bus/ti99/colorbus/colorbus.cpp | 10 +- src/devices/bus/ti99/colorbus/colorbus.h | 8 +- src/devices/bus/ti99/internal/998board.cpp | 2307 +++++++++++++++++++++ src/devices/bus/ti99/internal/998board.h | 562 +++++ src/devices/bus/ti99/internal/datamux.cpp | 645 ++++++ src/devices/bus/ti99/internal/datamux.h | 146 ++ src/devices/bus/ti99/internal/evpcconn.cpp | 45 + src/devices/bus/ti99/internal/evpcconn.h | 45 + src/devices/bus/ti99/internal/genboard.cpp | 2015 ++++++++++++++++++ src/devices/bus/ti99/internal/genboard.h | 206 ++ src/devices/bus/ti99/internal/gromport.cpp | 3040 ++++++++++++++++++++++++++++ src/devices/bus/ti99/internal/gromport.h | 468 +++++ src/devices/bus/ti99/joyport/handset.cpp | 10 +- src/devices/bus/ti99/joyport/handset.h | 7 +- src/devices/bus/ti99/joyport/joyport.cpp | 23 +- src/devices/bus/ti99/joyport/joyport.h | 18 +- src/devices/bus/ti99/joyport/mecmouse.cpp | 21 +- src/devices/bus/ti99/joyport/mecmouse.h | 9 +- src/devices/bus/ti99/peb/bwg.cpp | 6 +- src/devices/bus/ti99/peb/bwg.h | 6 +- src/devices/bus/ti99/peb/evpc.cpp | 8 +- src/devices/bus/ti99/peb/evpc.h | 13 +- src/devices/bus/ti99/peb/hfdc.cpp | 6 +- src/devices/bus/ti99/peb/hfdc.h | 6 +- src/devices/bus/ti99/peb/horizon.cpp | 6 +- src/devices/bus/ti99/peb/horizon.h | 6 +- src/devices/bus/ti99/peb/hsgpl.cpp | 6 +- src/devices/bus/ti99/peb/hsgpl.h | 6 +- src/devices/bus/ti99/peb/memex.cpp | 6 +- src/devices/bus/ti99/peb/memex.h | 6 +- src/devices/bus/ti99/peb/myarcmem.cpp | 6 +- src/devices/bus/ti99/peb/myarcmem.h | 6 +- src/devices/bus/ti99/peb/pcode.cpp | 6 +- src/devices/bus/ti99/peb/pcode.h | 6 +- src/devices/bus/ti99/peb/peribox.cpp | 18 +- src/devices/bus/ti99/peb/peribox.h | 29 +- src/devices/bus/ti99/peb/samsmem.cpp | 6 +- src/devices/bus/ti99/peb/samsmem.h | 6 +- src/devices/bus/ti99/peb/spchsyn.cpp | 6 +- src/devices/bus/ti99/peb/spchsyn.h | 6 +- src/devices/bus/ti99/peb/ti_32kmem.cpp | 6 +- src/devices/bus/ti99/peb/ti_32kmem.h | 6 +- src/devices/bus/ti99/peb/ti_fdc.cpp | 6 +- src/devices/bus/ti99/peb/ti_fdc.h | 6 +- src/devices/bus/ti99/peb/ti_rs232.cpp | 10 +- src/devices/bus/ti99/peb/ti_rs232.h | 12 +- src/devices/bus/ti99/peb/tn_ide.cpp | 7 +- src/devices/bus/ti99/peb/tn_ide.h | 6 +- src/devices/bus/ti99/peb/tn_usbsm.cpp | 7 +- src/devices/bus/ti99/peb/tn_usbsm.h | 5 +- src/devices/bus/ti99/ti99defs.h | 160 ++ src/devices/bus/ti99x/998board.cpp | 2308 --------------------- src/devices/bus/ti99x/998board.h | 559 ----- src/devices/bus/ti99x/datamux.cpp | 640 ------ src/devices/bus/ti99x/datamux.h | 142 -- src/devices/bus/ti99x/genboard.cpp | 2011 ------------------ src/devices/bus/ti99x/genboard.h | 202 -- src/devices/bus/ti99x/gromport.cpp | 3039 --------------------------- src/devices/bus/ti99x/gromport.h | 466 ----- src/devices/bus/ti99x/ti99defs.cpp | 17 - src/devices/bus/ti99x/ti99defs.h | 205 -- src/mame/drivers/geneve.cpp | 33 +- src/mame/drivers/ti99_4p.cpp | 4 +- src/mame/drivers/ti99_4x.cpp | 23 +- src/mame/drivers/ti99_8.cpp | 80 +- 68 files changed, 9989 insertions(+), 9768 deletions(-) create mode 100644 src/devices/bus/ti99/internal/998board.cpp create mode 100644 src/devices/bus/ti99/internal/998board.h create mode 100644 src/devices/bus/ti99/internal/datamux.cpp create mode 100644 src/devices/bus/ti99/internal/datamux.h create mode 100644 src/devices/bus/ti99/internal/evpcconn.cpp create mode 100644 src/devices/bus/ti99/internal/evpcconn.h create mode 100644 src/devices/bus/ti99/internal/genboard.cpp create mode 100644 src/devices/bus/ti99/internal/genboard.h create mode 100644 src/devices/bus/ti99/internal/gromport.cpp create mode 100644 src/devices/bus/ti99/internal/gromport.h create mode 100644 src/devices/bus/ti99/ti99defs.h delete mode 100644 src/devices/bus/ti99x/998board.cpp delete mode 100644 src/devices/bus/ti99x/998board.h delete mode 100644 src/devices/bus/ti99x/datamux.cpp delete mode 100644 src/devices/bus/ti99x/datamux.h delete mode 100644 src/devices/bus/ti99x/genboard.cpp delete mode 100644 src/devices/bus/ti99x/genboard.h delete mode 100644 src/devices/bus/ti99x/gromport.cpp delete mode 100644 src/devices/bus/ti99x/gromport.h delete mode 100644 src/devices/bus/ti99x/ti99defs.cpp delete mode 100644 src/devices/bus/ti99x/ti99defs.h diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 97b664f7d74..8008ac4e67a 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -2342,7 +2342,6 @@ end --@src/devices/bus/ti99x/990_dk.h,BUSES["TI99X"] = true --@src/devices/bus/ti99x/990_tap.h,BUSES["TI99X"] = true --@src/devices/bus/ti99x/990_hd.h,BUSES["TI99X"] = true ---@src/devices/bus/ti99x/genboard.h,BUSES["TI99X"] = true --------------------------------------------------- if (BUSES["TI99X"]~=null) then @@ -2353,16 +2352,6 @@ if (BUSES["TI99X"]~=null) then MAME_DIR .. "src/devices/bus/ti99x/990_hd.h", MAME_DIR .. "src/devices/bus/ti99x/990_tap.cpp", MAME_DIR .. "src/devices/bus/ti99x/990_tap.h", - MAME_DIR .. "src/devices/bus/ti99x/998board.cpp", - MAME_DIR .. "src/devices/bus/ti99x/998board.h", - MAME_DIR .. "src/devices/bus/ti99x/datamux.cpp", - MAME_DIR .. "src/devices/bus/ti99x/datamux.h", - MAME_DIR .. "src/devices/bus/ti99x/genboard.cpp", - MAME_DIR .. "src/devices/bus/ti99x/genboard.h", - MAME_DIR .. "src/devices/bus/ti99x/gromport.cpp", - MAME_DIR .. "src/devices/bus/ti99x/gromport.h", - MAME_DIR .. "src/devices/bus/ti99x/ti99defs.cpp", - MAME_DIR .. "src/devices/bus/ti99x/ti99defs.h", } end @@ -2371,10 +2360,23 @@ end --@src/devices/bus/ti99/colorbus/colorbus.h,BUSES["TI99"] = true --@src/devices/bus/ti99/joyport/joyport.h,BUSES["TI99"] = true --@src/devices/bus/ti99/peb/peribox.h,BUSES["TI99"] = true +--@src/devices/bus/ti99/internal/genboard.h,BUSES["TI99"] = true + --------------------------------------------------- if (BUSES["TI99"]~=null) then files { + MAME_DIR .. "src/devices/bus/ti99/ti99defs.h", + MAME_DIR .. "src/devices/bus/ti99/internal/998board.cpp", + MAME_DIR .. "src/devices/bus/ti99/internal/998board.h", + MAME_DIR .. "src/devices/bus/ti99/internal/datamux.cpp", + MAME_DIR .. "src/devices/bus/ti99/internal/datamux.h", + MAME_DIR .. "src/devices/bus/ti99/internal/evpcconn.cpp", + MAME_DIR .. "src/devices/bus/ti99/internal/evpcconn.h", + MAME_DIR .. "src/devices/bus/ti99/internal/genboard.cpp", + MAME_DIR .. "src/devices/bus/ti99/internal/genboard.h", + MAME_DIR .. "src/devices/bus/ti99/internal/gromport.cpp", + MAME_DIR .. "src/devices/bus/ti99/internal/gromport.h", MAME_DIR .. "src/devices/bus/ti99/colorbus/busmouse.cpp", MAME_DIR .. "src/devices/bus/ti99/colorbus/busmouse.h", MAME_DIR .. "src/devices/bus/ti99/colorbus/colorbus.cpp", diff --git a/src/devices/bus/ti99/colorbus/busmouse.cpp b/src/devices/bus/ti99/colorbus/busmouse.cpp index 305e1eba702..2dd31315e1e 100644 --- a/src/devices/bus/ti99/colorbus/busmouse.cpp +++ b/src/devices/bus/ti99/colorbus/busmouse.cpp @@ -14,6 +14,10 @@ #include "emu.h" #include "busmouse.h" +DEFINE_DEVICE_TYPE_NS(TI99_BUSMOUSE, bus::ti99::colorbus, geneve_busmouse_device, "ti99_busmouse", "Geneve Bus Mouse") + +namespace bus { namespace ti99 { namespace colorbus { + geneve_busmouse_device::geneve_busmouse_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, TI99_BUSMOUSE, tag, owner, clock), device_ti99_colorbus_interface(mconfig, *this) , m_buttons(*this, "MOUSEBUT"), m_xaxis(*this, "MOUSEX"), m_yaxis(*this, "MOUSEY") @@ -64,5 +68,4 @@ ioport_constructor geneve_busmouse_device::device_input_ports() const { return INPUT_PORTS_NAME( busmouse ); } - -DEFINE_DEVICE_TYPE(TI99_BUSMOUSE, geneve_busmouse_device, "ti99_busmouse", "Geneve Bus Mouse") +} } } // end namespace bus::ti99::colorbus diff --git a/src/devices/bus/ti99/colorbus/busmouse.h b/src/devices/bus/ti99/colorbus/busmouse.h index 15570ff2a4a..74d47e8e628 100644 --- a/src/devices/bus/ti99/colorbus/busmouse.h +++ b/src/devices/bus/ti99/colorbus/busmouse.h @@ -17,7 +17,7 @@ #include "colorbus.h" -DECLARE_DEVICE_TYPE(TI99_BUSMOUSE, geneve_busmouse_device) +namespace bus { namespace ti99 { namespace colorbus { class geneve_busmouse_device : public device_t, public device_ti99_colorbus_interface { @@ -35,5 +35,8 @@ private: int m_last_mx; int m_last_my; }; +} } } // end namespace bus::ti99::colorbus + +DECLARE_DEVICE_TYPE_NS(TI99_BUSMOUSE, bus::ti99::colorbus, geneve_busmouse_device) #endif // MAME_BUS_TI99_COLORBUS_BUSMOUSE_H diff --git a/src/devices/bus/ti99/colorbus/colorbus.cpp b/src/devices/bus/ti99/colorbus/colorbus.cpp index b9c938f8fde..e8e4c841c65 100644 --- a/src/devices/bus/ti99/colorbus/colorbus.cpp +++ b/src/devices/bus/ti99/colorbus/colorbus.cpp @@ -20,7 +20,11 @@ #include "emu.h" #include "colorbus.h" #include "busmouse.h" -#include "bus/ti99x/ti99defs.h" +#include "bus/ti99/ti99defs.h" + +DEFINE_DEVICE_TYPE_NS(TI99_COLORBUS, bus::ti99::colorbus, ti99_colorbus_device, "ti99_colorbus", "v9938 Color bus") + +namespace bus { namespace ti99 { namespace colorbus { ti99_colorbus_device::ti99_colorbus_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, TI99_COLORBUS, tag, owner, clock), @@ -58,8 +62,8 @@ void device_ti99_colorbus_interface::interface_config_complete() m_colorbus = dynamic_cast(device().owner()); } +} } } // end namespace bus::ti99::colorbus + SLOT_INTERFACE_START( ti99_colorbus_port ) SLOT_INTERFACE("busmouse", TI99_BUSMOUSE) SLOT_INTERFACE_END - -DEFINE_DEVICE_TYPE(TI99_COLORBUS, ti99_colorbus_device, "ti99_colorbus", "v9938 Color bus") diff --git a/src/devices/bus/ti99/colorbus/colorbus.h b/src/devices/bus/ti99/colorbus/colorbus.h index 74d939e77d7..7d7dea7620c 100644 --- a/src/devices/bus/ti99/colorbus/colorbus.h +++ b/src/devices/bus/ti99/colorbus/colorbus.h @@ -18,7 +18,9 @@ #include "video/v9938.h" -DECLARE_DEVICE_TYPE(TI99_COLORBUS, ti99_colorbus_device) +namespace bus { namespace ti99 { namespace colorbus { + +class ti99_colorbus_device; /******************************************************************** Common parent class of all devices attached to the color bus @@ -55,10 +57,14 @@ private: bool m_left_button_pressed; }; +} } } // end namespace bus::ti99::colorbus + SLOT_INTERFACE_EXTERN(ti99_colorbus_port); #define MCFG_COLORBUS_MOUSE_ADD( _tag ) \ MCFG_DEVICE_ADD(_tag, TI99_COLORBUS, 0) \ MCFG_DEVICE_SLOT_INTERFACE(ti99_colorbus_port, "busmouse", false) +DECLARE_DEVICE_TYPE_NS(TI99_COLORBUS, bus::ti99::colorbus, ti99_colorbus_device) + #endif // MAME_BUS_TI99_COLORBUS_COLORBUS_H diff --git a/src/devices/bus/ti99/internal/998board.cpp b/src/devices/bus/ti99/internal/998board.cpp new file mode 100644 index 00000000000..65383d012aa --- /dev/null +++ b/src/devices/bus/ti99/internal/998board.cpp @@ -0,0 +1,2307 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/*************************************************************************** + + TI-99/8 main board logic + + This component implements the address decoder and mapper logic from the + TI-99/8 console. + + The TI-99/8 defines a "logical address map" with 64 KiB (according to the + 16 address bits) and a "physical address map" with 16 MiB (according to the + 24 address bits of the mapper). Note that the mapper only uses 16 outgoing + address lines and multiplexes the address bytes. + + Note: The TI-99/8's internal codename was "Armadillo" + + + +-------+ +--------+ + | CPU |========LogAddrBus======| Mapper |====PhysAddrBus========== + | TMS | || | AMIGO | || + | 9995 | +----------+ | | +----------+ + | | | Logical | +--------+ | Physical | + +-------+ | space | | | space | + | decoder | | | decoder | + | VAQUERRO | | | MOFETTA | + +----------+ | +----------+ + | | | + +--------------------+ | +---------------------+ + | Devices | | | Devices | + | +-------+ | | | + | ROM0 | SRAM | | | DRAM (POLLO) | + | Video | ---- | | | ROM1 | + | Speech | Maps--+--+ | Cartridge port | + | GROM +-------+ | PEB | + | Sound | | Hexbus (OSO) | + +--------------------+ +---------------------+ + + Custom chips + ------------ + The chipset of the TI-99/8 consists of five specifically programmed chips. + All are nicknamed after some Spanish words (albeit sometimes misspelled) + + VAQUERRO: Logical Address Space decoder ("Vaquero" = "Cowboy") + MOFETTA : Physical Address Space decoder ("Mofeta" = "Skunk") + AMIGO : Mapper ("Amigo" = "Friend") + OSO : Hexbus adapter ("Oso" = "Bear") + POLLO : DRAM controller (Not emulated) ("Pollo" = "Chicken") + + See the comments for the respective chip implementation for details. + + + ROM contents + ------------ + The ROM0 chip is accessible at addresses 0000-1FFF in the logical address + space of the compatibility mode. It contains the GPL interpreter. In + native mode the ROM0 chip is invisible. + + ROM0 + offset Logical address Name + ----------------------------------- + 0000 0000-1FFF ROM0 + + + The ROM1 chip contains 32 KiB of various system software. It is located in + the physical address space, so it must be mapped into the logical address + space by defining an appropriate map. + + ROM1 + offset Physical address Name + ---------------------------------------------------------- + 0000 FFA000-FFDFFF ROM1 + 4000 FF4000-FF5FFF @CRU>2700 Text-to-speech ROM/DSR + 6000 FF4000-FF5FFF @CRU>1700 Hexbus DSR + + The DSR portions have to be selected via the CRU bits >1700 or >2700. + + + CRU map (I/O address space) + =========================== + 0000-003e: TMS9901 system interface (see ti99_8.c) + 1700-17fe: Hexbus + 2000-26fe: Future external devices + 2700-27fe: Additional ROM ("internal DSR") + 2702 : System reset (when set to 1) + 2800-3ffe: Future external devices + 4000-fffe: Future external devices + + The TMS9995 offers the full 15-bit CRU address space. Devices designed for + the TI-99/4A should only be accessed in the area 1000-1ffe. They will (by + design) incompletely decode the CRU address and be mirrored in the higher + areas. + + Note that the cartridge port of the TI-99/8 offers support for 16K ROM + cartridges, but lacks CRU support. + + Michael Zapf, October 2010 + February 2012: Rewritten as class + March 2016: Redesigned for custom chip emulation + + Informations taken from + [1] ARMADILLO PRODUCT SPECIFICATIONS + [2] TI-99/8 Graphics Programming Language interpreter + +***************************************************************************/ + +#include "emu.h" +#include "998board.h" + +DEFINE_DEVICE_TYPE_NS(TI99_MAINBOARD8, bus::ti99::internal, mainboard8_device, "ti998_mainboard", "TI-99/8 Mainboard") +DEFINE_DEVICE_TYPE_NS(TI99_VAQUERRO, bus::ti99::internal, vaquerro_device, "ti998_vaquerro", "TI-99/8 Logical Address Space Decoder") +DEFINE_DEVICE_TYPE_NS(TI99_MOFETTA, bus::ti99::internal, mofetta_device, "ti998_mofetta", "TI-99/8 Physical Address Space Decoder") +DEFINE_DEVICE_TYPE_NS(TI99_OSO, bus::ti99::internal, oso_device, "ti998_oso", "TI-99/8 Hexbus interface") +DEFINE_DEVICE_TYPE_NS(TI99_AMIGO, bus::ti99::internal, amigo_device, "ti998_amigo", "TI-99/8 Address space mapper") + +namespace bus { namespace ti99 { namespace internal { + +#define TRACE_CRU 0 +#define TRACE_ADDRESS 0 +#define TRACE_MAP 0 +#define TRACE_OSO 0 +#define TRACE_DECODE 0 +#define TRACE_READY 0 +#define TRACE_MEM 0 +#define TRACE_CLOCK 0 +#define TRACE_DETAIL 0 +#define TRACE_MOFETTA 0 +#define TRACE_AMIGO 0 +#define TRACE_WS 0 +#define TRACE_CPURY 0 +#define TRACE_GROM 0 +#define TRACE_PUNMAP 0 + +enum +{ + SGMSEL = 1, + TSGSEL = 2, + P8GSEL = 4, + P3GSEL = 8, + VIDSEL = 16 +}; + +mainboard8_device::mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, TI99_MAINBOARD8, tag, owner, clock), + m_A14_set(false), + m_pending_write(false), + m_speech_ready(true), + m_sound_ready(true), + m_pbox_ready(true), + m_ready(*this), + m_console_reset(*this), + m_hold_line(*this), + m_vaquerro(*this, VAQUERRO_TAG), + m_mofetta(*this, MOFETTA_TAG), + m_amigo(*this, AMIGO_TAG), + m_oso(*this, OSO_TAG), + m_video(*owner, VDP_TAG), // subdevice of main class + m_sound(*owner, TISOUNDCHIP_TAG), + m_speech(*owner, SPEECHSYN_TAG), + m_gromport(*owner, GROMPORT_TAG), + m_peb(*owner, PERIBOX_TAG), + m_sram(*owner, SRAM_TAG), + m_dram(*owner, DRAM_TAG), + m_sgrom_idle(true), + m_tsgrom_idle(true), + m_p8grom_idle(true), + m_p3grom_idle(true) +{ +} + +// Debugger support +// The memory accesses by the debugger are routed around the custom chip logic + +READ8_MEMBER( mainboard8_device::debugger_read ) +{ + int logical_address = offset; + bool compat_mode = (m_crus_debug==ASSERT_LINE); + + // Check whether the mapper itself is accessed + int mapaddr = compat_mode? 0x8810 : 0xf870; + bool mapper_accessed = ((offset & 0xfff1)==mapaddr); + + if (mapper_accessed) return 0; // do not allow the debugger to mess with the mapper + + // or SRAM + int sramaddr = compat_mode? 0x8000 : 0xf000; + + if ((offset & 0xf800)==sramaddr) + { + // SRAM access + return m_sram->pointer()[logical_address & 0x07ff]; + } + if ((offset & 0xe000)==0x0000 && compat_mode) + { + // ROM0 access + return m_rom0[logical_address & 0x1fff]; + } + + // Physical space + u8 value = 0; + int physical_address = m_amigo->get_physical_address_debug(offset); + + if ((physical_address & 0x00ff0000)==0x00000000) + { + // DRAM + return m_dram->pointer()[physical_address & 0xffff]; + } + if ((physical_address & 0x00ffc000)==0x00f00000) + { + // Pascal ROM 16K + return m_pascalrom[physical_address & 0x3fff]; + } + if ((physical_address & 0x00ffe000)==0x00ff4000) + { + // Internal DSR, Hexbus DSR, or PEB + if (m_mofetta->hexbus_access_debug()) return m_rom1[(physical_address & 0x1fff) | 0x6000]; + if (m_mofetta->intdsr_access_debug()) return m_rom1[(physical_address & 0x1fff) | 0x4000]; + m_peb->memen_in(ASSERT_LINE); + m_peb->readz(space, physical_address & 0xffff, &value); + m_peb->memen_in(CLEAR_LINE); + return value; + } + if ((physical_address & 0x00ffe000)==0x00ff6000) + { + // Cartridge space lower 8 + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->readz(space, physical_address & 0x1fff, &value); + m_gromport->romgq_line(CLEAR_LINE); + return value; + } + if ((physical_address & 0x00ffe000)==0x00ff8000) + { + // Cartridge space upper 8 + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->readz(space, (physical_address & 0x1fff) | 0x2000, &value); + m_gromport->romgq_line(CLEAR_LINE); + return value; + } + if ((physical_address & 0x00ffe000)==0x00ffa000) + { + // ROM1 lower 8 + return m_rom1[(physical_address & 0x1fff) | 0x0000]; + } + if ((physical_address & 0x00ffe000)==0x00ffc000) + { + // ROM1 upper 8 + return m_rom1[(physical_address & 0x1fff) | 0x2000]; + } + return 0; +} + +WRITE8_MEMBER( mainboard8_device::debugger_write ) +{ + int logical_address = offset; + bool compat_mode = (m_crus_debug==ASSERT_LINE); + + // Check whether the mapper itself is accessed + int mapaddr = compat_mode? 0x8810 : 0xf870; + bool mapper_accessed = ((offset & 0xfff1)==mapaddr); + + if (mapper_accessed) + { + // Allow for loading/saving mapper registers + m_amigo->mapper_access_debug(data); + return; + } + + // SRAM + int sramaddr = compat_mode? 0x8000 : 0xf000; + + if ((offset & 0xf800)==sramaddr) + { + // SRAM access + m_sram->pointer()[logical_address & 0x07ff] = data & 0xff; + return; + } + + // ROM0 (no write access) + if ((offset & 0xe000)==0x0000 && compat_mode) return; + + // Physical space + int physical_address = m_amigo->get_physical_address_debug(offset); + + if ((physical_address & 0x00ff0000)==0x00000000) + { + // DRAM + m_dram->pointer()[physical_address & 0xffff] = data & 0xff; + return; + } + + // Pascal ROM (no write) + if ((physical_address & 0x00ffc000)==0x00f00000) return; + + // Internal DSR, Hexbus DSR, or PEB + if ((physical_address & 0x00ffe000)==0x00ff4000) + { + if (m_mofetta->hexbus_access_debug()) return; + if (m_mofetta->intdsr_access_debug()) return; + m_peb->memen_in(ASSERT_LINE); + m_peb->write(space, physical_address & 0xffff, data & 0xff); + m_peb->memen_in(CLEAR_LINE); + return; + } + if ((physical_address & 0x00ffe000)==0x00ff6000) + { + // Cartridge space lower 8 + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->write(space, physical_address & 0x1fff, data & 0xff); + m_gromport->romgq_line(CLEAR_LINE); + return; + } + if ((physical_address & 0x00ffe000)==0x00ff8000) + { + // Cartridge space upper 8 + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->write(space, (physical_address & 0x1fff) | 0x2000, data & 0xff); + m_gromport->romgq_line(CLEAR_LINE); + return; + } + + // ROM1 not writable + if ((physical_address & 0x00ffe000)==0x00ffa000 || (physical_address & 0x00ffe000)==0x00ffc000) return; +} + +// =============== CRU bus access ================== + +READ8Z_MEMBER(mainboard8_device::crureadz) +{ + m_peb->crureadz(space, offset, value); +} + +/* + CRU handling. Mofetta is the only chip that bothers to handle it, beside the PEB +*/ +WRITE8_MEMBER(mainboard8_device::cruwrite) +{ + m_mofetta->cruwrite(space, offset, data); + m_peb->cruwrite(space, offset, data); +} + +// =============== Memory bus access ================== + +WRITE_LINE_MEMBER( mainboard8_device::dbin_in ) +{ + m_dbin_level = (line_state)state; +} + +SETOFFSET_MEMBER( mainboard8_device::setoffset ) +{ + if (TRACE_ADDRESS) logerror("set %s %04x\n", (m_dbin_level==ASSERT_LINE)? "R" : "W", offset); + + // No data is waiting on the data bus + m_pending_write = false; + + // Memory cycle begins + m_vaquerro->memen_in(ASSERT_LINE); + m_amigo->memen_in(ASSERT_LINE); + + // Save the logical address + m_logical_address = offset; + m_physical_address = 0; + + // In TI's bit order, A14 is the second line from the right side (2^1) + m_A14_set = ((m_logical_address & 2)!=0); // Needed for clock_in + + // Check for match in logical space + m_vaquerro->set_address(space, m_logical_address, m_dbin_level); + + // Select GROMs if addressed + select_groms(); + + // Speech select lines will always be asserted/cleared as soon as the address is available + m_speech->wsq_w((m_vaquerro->spwt_out() == ASSERT_LINE)? false : true); + m_speech->rsq_w((m_vaquerro->sprd_out() == ASSERT_LINE)? false : true); + + // If it is a logical space address, tell the mapper to stay inactive + line_state lasreq = (line_state)m_vaquerro->lascsq_out(); + m_amigo->lascs_in(lasreq); + m_mofetta->lascs_in(lasreq); + + // Need to set the address in any case so that the lines can be cleared + m_amigo->set_address(space, m_logical_address); + + // AMIGO is the one to control the READY line to the CPU + // MOFETTA does not contribute to READY + m_ready(m_amigo->cpury_out()); +} + +WRITE_LINE_MEMBER( mainboard8_device::reset_console ) +{ + m_console_reset(state); +} + +WRITE_LINE_MEMBER( mainboard8_device::hold_cpu ) +{ + m_hold_line(state); +} + +/* + HOLD Acknowledge from the CPU +*/ +WRITE_LINE_MEMBER( mainboard8_device::holda_line ) +{ + m_amigo->holda_in(state); +} + +/* + Clock line from the CPU. Forward to the custom chips. +*/ +WRITE_LINE_MEMBER( mainboard8_device::clock_in ) +{ + if (TRACE_CLOCK) logerror("CLKOUT = %d\n", state); + + // Propagate to Vaquerro; may change GGRDY (trailing edge) and the GROM select lines + m_vaquerro->clock_in((line_state)state); + + // Set the incoming ready line of Amigo (Mapper) before the clock + bool readycomb = ((m_vaquerro->ggrdy_out()==ASSERT_LINE) && m_speech_ready && m_sound_ready && m_pbox_ready); + m_amigo->srdy_in(readycomb? ASSERT_LINE : CLEAR_LINE); + + // This may change the incoming READY lines of Vaquerro + if (state==CLEAR_LINE) select_groms(); + + m_amigo->clock_in((line_state)state); + + // Mofetta only needs the clock to produce the GROM clock + m_mofetta->clock_in(state); + + m_mofetta->skdrcs_in(m_amigo->skdrcs_out()); + + int gromclk = m_mofetta->gromclk_out(); + + if (gromclk != m_gromclk) // when it changed, propagate to the GROMs + { + m_gromclk = gromclk; + + // Get some more performance. We only propagate the clock line to + // those GROMs that are not idle. + // Yields about 25% in bench (hoped for more, but well) + if (!m_sgrom_idle) + { + for (int i=0; i < 3; i++) m_sgrom[i]->gclock_in(gromclk); + m_gromport->gclock_in(gromclk); + m_sgrom_idle = m_sgrom[0]->idle(); + } + + if (!m_tsgrom_idle) + { + for (int i=0; i < 8; i++) m_tsgrom[i]->gclock_in(gromclk); + m_tsgrom_idle = m_tsgrom[0]->idle(); + } + if (!m_p8grom_idle) + { + for (int i=0; i < 8; i++) m_p8grom[i]->gclock_in(gromclk); + m_p8grom_idle = m_p8grom[0]->idle(); + } + + if (!m_p3grom_idle) + { + for (int i=0; i < 3; i++) m_p3grom[i]->gclock_in(gromclk); + m_p3grom_idle = m_p3grom[0]->idle(); + } + } + + // Check video for writing + if (m_pending_write && m_vaquerro->vdpwt_out()==ASSERT_LINE) + { + if (m_A14_set) m_video->register_write(*m_space, 0, m_latched_data); + else m_video->vram_write(*m_space, 0, m_latched_data); + m_pending_write = false; + if (TRACE_MEM) logerror("Write %04x (video) <- %02x\n", m_logical_address, m_latched_data); + cycle_end(); + return; + } + + // Propagate the READY signal + m_ready(m_amigo->cpury_out()); + + // In case we're reading, the CPU will now do the READ operation. + // Otherwise we must do the write operation now which we postponed before. + + if (m_pending_write && (state==CLEAR_LINE)) + { + if (m_amigo->skdrcs_out()==ASSERT_LINE) + { + m_dram->pointer()[m_physical_address & 0xffff] = m_latched_data; + m_pending_write = false; + if (TRACE_MEM) logerror("Write %04x (phys %06x, DRAM) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); + } + + if (m_mofetta->alccs_out()==ASSERT_LINE) + { + m_oso->write(*m_space, m_physical_address>>1, m_latched_data); + m_pending_write = false; + if (TRACE_MEM) logerror("Write %04x (phys %06x, OSO) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); + } + + if (m_mofetta->cmas_out()==ASSERT_LINE) + { + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->write(*m_space, m_physical_address & 0x3fff, m_latched_data); + m_pending_write = false; + if (TRACE_MEM) logerror("Write %04x (phys %06x, cartridge) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); + } + else + { + m_gromport->romgq_line(CLEAR_LINE); + } + + if (m_mofetta->dbc_out()==ASSERT_LINE) + { + m_peb->write(*m_space, m_physical_address, m_latched_data); + m_pending_write = false; + if (TRACE_MEM) logerror("Write %04x (phys %06x, PEB) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); + } + } + + if (m_dbin_level==CLEAR_LINE && !m_pending_write) // Memory cycle ends + cycle_end(); +} + +void mainboard8_device::select_groms() +{ + // Select the GROM libs + // Note that we must also deselect them again, so we have to visit each + // one of them + + int select = m_vaquerro->gromcs_out(); + + // Avoid to be called too often; this would have a bad penalty on emulation performance + // This simple check actually increases bench performance from 120% to 240% + if (select != m_prev_grom) + { + m_prev_grom = select; + int lines = (m_dbin_level==ASSERT_LINE)? GROM_M_LINE : 0; + if (m_A14_set) lines |= GROM_MO_LINE; + + if (select & SGMSEL) m_sgrom_idle = false; + if (select & TSGSEL) m_tsgrom_idle = false; + if (select & P8GSEL) m_p8grom_idle = false; + if (select & P3GSEL) m_p3grom_idle = false; + + for (int i=0; i < 3; i++) + m_sgrom[i]->set_lines(*m_space, lines, select & SGMSEL); + + for (int i=0; i < 8; i++) + m_tsgrom[i]->set_lines(*m_space, lines, select & TSGSEL); + + for (int i=0; i < 8; i++) + m_p8grom[i]->set_lines(*m_space, lines, select & P8GSEL); + + for (int i=0; i < 3; i++) + m_p3grom[i]->set_lines(*m_space, lines, select & P3GSEL); + + // Write to the cartridge port. The GROMs on cartridges are accesses as system GROMs + if (select & SGMSEL) m_gromport->romgq_line(CLEAR_LINE); + m_gromport->set_gromlines(*m_space, lines, select & SGMSEL); + } + + // If we're planning to write to the GROMs, let's do it right now + if (select !=0 && m_pending_write) + { + m_pending_write = false; + switch (select) + { + case SGMSEL: + for (int i=0; i < 3; i++) + { + m_sgrom[i]->write(*m_space, 0, m_latched_data); + } + if (TRACE_MEM) logerror("Write GS <- %02x\n", m_latched_data); + m_gromport->write(*m_space, 0, m_latched_data); + break; + + case TSGSEL: + for (int i=0; i < 8; i++) + { + m_tsgrom[i]->write(*m_space, 0, m_latched_data); + } + if (TRACE_MEM) logerror("Write GT <- %02x\n", m_latched_data); + break; + + case P8GSEL: + for (int i=0; i < 8; i++) + { + m_p8grom[i]->write(*m_space, 0, m_latched_data); + } + if (TRACE_MEM) logerror("Write G8 <- %02x\n", m_latched_data); + break; + + case P3GSEL: + for (int i=0; i < 3; i++) + { + m_p3grom[i]->write(*m_space, 0, m_latched_data); + } + if (TRACE_MEM) logerror("Write G3 <- %02x\n", m_latched_data); + break; + + default: + logerror("Error: Multiple GROM libs selected: SGM=%d TSG=%d P8G=%d P3G=%d\n", (select & SGMSEL)!=0, (select & TSGSEL)!=0, (select & P8GSEL)!=0, (select & P3GSEL)!=0); + break; + } + } +} + +void mainboard8_device::set_paddress(int address) +{ + // Keep this value as the current address + m_physical_address = (m_physical_address << 16) | address; + if (TRACE_DETAIL) logerror("Setting physical address %06x\n", m_physical_address); + + m_mofetta->set_address(*m_space, address, m_dbin_level); + m_peb->setaddress_dbin(*m_space, address, m_dbin_level); +} + +WRITE_LINE_MEMBER( mainboard8_device::msast_in ) +{ + if (TRACE_DETAIL) logerror("msast = %d\n", state); + + // Start physical space cycle on the trailing edge + if (state==CLEAR_LINE) + { + m_mofetta->pmemen_in(ASSERT_LINE); + m_peb->memen_in(ASSERT_LINE); + } + m_mofetta->msast_in(state); + m_peb->msast_in(state); +} + + +READ8_MEMBER( mainboard8_device::read ) +{ + uint8_t value = 0; + const char* what; + + if (machine().side_effect_disabled()) + { + return debugger_read(space, offset); + } + + // ================================================= + // Logical space + // ================================================= + if (m_amigo->mapper_accessed()) + { + value = m_amigo->read(space, 0); + what = "mapper"; + goto readdone; + } + + if (m_amigo->sramcs_out()==ASSERT_LINE) + { + value = m_sram->pointer()[m_logical_address & 0x07ff]; + what = "SRAM"; + goto readdone; + } + + if (m_vaquerro->lascsq_out()==ASSERT_LINE) + { + // VDP access + if (m_vaquerro->vdprd_out()==ASSERT_LINE) + { + value = m_A14_set? m_video->register_read(space, 0) : m_video->vram_read(space, 0); + what = "video"; + goto readdone; + } + + // System ROM0 + if (m_vaquerro->sromcs_out()==ASSERT_LINE) + { + value = m_rom0[m_logical_address & 0x1fff]; + what = "ROM0"; + goto readdone; + } + + // Speech + if (m_vaquerro->sprd_out()==ASSERT_LINE) + { + value = m_speech->status_r(space, 0) & 0xff; + what = "speech"; + goto readdone; + } + + // GROMs + switch (m_vaquerro->gromcs_out()) + { + case SGMSEL: + m_sgrom_idle = false; + for (int i=0; i < 3; i++) + { + m_sgrom[i]->readz(space, 0, &value); + } + m_gromport->readz(space, 0, &value); + if (TRACE_GROM && !m_A14_set) logerror("GS>%04x\n", m_sgrom[0]->debug_get_address()-1); + what = "system GROM"; + goto readdone; + + case TSGSEL: + m_tsgrom_idle = false; + for (int i=0; i < 8; i++) + { + m_tsgrom[i]->readz(space, 0, &value); + } + if (TRACE_GROM && !m_A14_set) logerror("GT>%04x\n", m_tsgrom[0]->debug_get_address()-1); + what = "TTS GROM"; + goto readdone; + + case P8GSEL: + m_p8grom_idle = false; + for (int i=0; i < 8; i++) + { + m_p8grom[i]->readz(space, 0, &value); + } + if (TRACE_GROM && !m_A14_set) logerror("G8>%04x\n", m_p8grom[0]->debug_get_address()-1); + what = "P8 GROM"; + goto readdone; + + case P3GSEL: + m_p3grom_idle = false; + for (int i=0; i < 3; i++) + { + m_p3grom[i]->readz(space, 0, &value); + } + if (TRACE_GROM && !m_A14_set) logerror("G3>%04x\n", m_p3grom[0]->debug_get_address()-1); + what = "P3 GROM"; + goto readdone; + default: + break; + } + + // These messages appear in fact every time that a GPL command writes + // an immediate value to a write-only address (like 9400) because the + // GPL interpreter always tries to load the value from the provided memory address first + + logerror("Read %04x (unmapped) ignored\n", m_logical_address); + + // Memory cycle ends + cycle_end(); + return 0; + } + else + { + // ================================================= + // Physical space + // ================================================= + if (m_amigo->skdrcs_out()==ASSERT_LINE) + { + value = m_dram->pointer()[m_physical_address & 0xffff]; + what = "DRAM"; + goto readdonephys; + } + + if (m_mofetta->rom1cs_out()==ASSERT_LINE) + { + int address = (m_physical_address & 0x1fff); + if (m_mofetta->rom1am_out()==ASSERT_LINE) address |= 0x4000; + if (m_mofetta->rom1al_out()==ASSERT_LINE) address |= 0x2000; + value = m_rom1[address]; + + if (TRACE_MEM) logerror("Read %04x (ROM1@%04x) -> %02x\n", m_logical_address, address, value); + cycle_end(); + return value; + } + + if (m_mofetta->alccs_out()==ASSERT_LINE) + { + value = m_oso->read(*m_space, m_physical_address>>1); + what = "OSO"; + goto readdonephys; + } + + if (m_mofetta->prcs_out()==ASSERT_LINE) + { + value = m_pascalrom[m_physical_address & 0x3fff]; + what = "PASCAL"; + goto readdonephys; + } + + if (m_mofetta->cmas_out()==ASSERT_LINE) + { + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->readz(*m_space, m_physical_address & 0x3fff, &value); + what = "Cartridge"; + goto readdonephys; + } + + if (m_mofetta->dbc_out()==ASSERT_LINE) + { + m_peb->readz(*m_space, m_physical_address & 0xffff, &value); + what = "PEB"; + goto readdonephys; + } + + if (TRACE_PUNMAP) logerror("Read %04x (phys %06x, unmapped) ignored\n", m_logical_address, m_physical_address); + + // Memory cycle ends + cycle_end(); + return 0; + } + + +readdone: + if (TRACE_MEM) logerror("Read %04x (%s) -> %02x\n", m_logical_address, what, value); + cycle_end(); + return value; + +readdonephys: + if (TRACE_MEM) logerror("Read %04x (phys %06x, %s) -> %02x\n", m_logical_address, m_physical_address, what, value); + cycle_end(); + return value; +} + +void mainboard8_device::cycle_end() +{ + // Memory cycle ends + m_vaquerro->memen_in(CLEAR_LINE); + m_amigo->memen_in(CLEAR_LINE); + m_mofetta->pmemen_in(CLEAR_LINE); + m_peb->memen_in(CLEAR_LINE); +} + +/* + When writing, the emulation relies on a push mechanism; the write + operation follows the setaddress operation immediately. + + If the READY line is pulled down due to the mapping process, we must + store the data bus value until the physical address is available. +*/ +WRITE8_MEMBER( mainboard8_device::write ) +{ + m_latched_data = data; + m_pending_write = true; + + if (machine().side_effect_disabled()) + { + return debugger_write(space, offset, data); + } + + // Some logical space devices can be written immediately + // GROMs and video must wait to be selected + if (m_amigo->mapper_accessed()) + { + if (TRACE_MEM) logerror("Write %04x (mapper) <- %02x\n", m_logical_address, data); + m_amigo->write(space, 0, data); + m_pending_write = false; + } + + // Sound ... we have to put it before SRAM because in compatibility mode the + // sound address lies within the SRAM area + if (m_vaquerro->sccs_out()==ASSERT_LINE) + { + if (TRACE_MEM) logerror("Write %04x (sound) <- %02x\n", m_logical_address, data); + m_sound->write(space, 0, data); // Sound chip will lower READY after this access + m_pending_write = false; + } + else + { + // SRAM + if (m_amigo->sramcs_out()==ASSERT_LINE) + { + if (TRACE_MEM) logerror("Write %04x (SRAM) <- %02x\n", m_logical_address, data); + m_sram->pointer()[m_logical_address & 0x07ff] = data; + m_pending_write = false; + } + } + + // Speech + if (m_vaquerro->spwt_out()==ASSERT_LINE) + { + if (TRACE_MEM) logerror("Write %04x (speech) <- %02x\n", m_logical_address, data); + m_speech->data_w(space, 0, data); + m_pending_write = false; + } + + if (!m_pending_write) + cycle_end(); + + // The remaining physical devices will respond as soon as the physical address is completely set +} + +/* + Set 99/4A compatibility mode (CRUS=1) +*/ +WRITE_LINE_MEMBER( mainboard8_device::crus_in ) +{ + if (TRACE_CRU) logerror("%s CRUS\n", (state==1)? "Assert" : "Clear"); + m_vaquerro->crus_in(state); + m_amigo->crus_in(state); + m_crus_debug = (line_state)state; +} + +/* + Set mapper /PTGEN line ("Pascal and Text-to-speech GROMs enable"). + Note that PTGEN is negative logic. +*/ +WRITE_LINE_MEMBER( mainboard8_device::ptgen_in ) +{ + if (TRACE_CRU) logerror("%s PTGEN*\n", (state==0)? "Assert" : "Clear"); + m_vaquerro->crusgl_in((state==0)? ASSERT_LINE : CLEAR_LINE); +} + + +/* + READY lines, to be combined +*/ +WRITE_LINE_MEMBER( mainboard8_device::system_grom_ready ) +{ + m_vaquerro->sgmry(state); +} + +WRITE_LINE_MEMBER( mainboard8_device::ptts_grom_ready ) +{ + m_vaquerro->tsgry(state); +} + +WRITE_LINE_MEMBER( mainboard8_device::p8_grom_ready ) +{ + m_vaquerro->p8gry(state); +} + +WRITE_LINE_MEMBER( mainboard8_device::p3_grom_ready ) +{ + m_vaquerro->p3gry(state); +} + +WRITE_LINE_MEMBER( mainboard8_device::sound_ready ) +{ + m_sound_ready = state; +} + +WRITE_LINE_MEMBER( mainboard8_device::speech_ready ) +{ + // The TMS5200 implementation uses true/false, not ASSERT/CLEAR semantics + m_speech_ready = (state==false)? ASSERT_LINE : CLEAR_LINE; +} + +WRITE_LINE_MEMBER( mainboard8_device::pbox_ready ) +{ + m_pbox_ready = state; +} + +WRITE_LINE_MEMBER( mainboard8_device::ggrdy_in ) +{ + m_amigo->srdy_in((state==ASSERT_LINE && m_speech_ready && m_sound_ready && m_pbox_ready)? ASSERT_LINE : CLEAR_LINE); +} + +static const char *const glib0[] = { SYSGROM0_TAG, SYSGROM1_TAG, SYSGROM2_TAG }; +static const char *const glib1[] = { GLIB10_TAG, GLIB11_TAG, GLIB12_TAG, GLIB13_TAG, GLIB14_TAG, GLIB15_TAG, GLIB16_TAG, GLIB17_TAG }; +static const char *const glib2[] = { GLIB20_TAG, GLIB21_TAG, GLIB22_TAG, GLIB23_TAG, GLIB24_TAG, GLIB25_TAG, GLIB26_TAG, GLIB27_TAG }; +static const char *const glib3[] = { GLIB30_TAG, GLIB31_TAG, GLIB32_TAG }; + +void mainboard8_device::device_start() +{ + logerror("Starting main board\n"); + // Lines going to the main driver class, then to the CPU + m_ready.resolve_safe(); // READY + m_console_reset.resolve_safe(); // RESET + m_hold_line.resolve_safe(); // HOLD + + // Setting up the links to the GROMs + for (int i=0; i < 8; i++) + { + if (i < 3) + { + m_sgrom[i] = downcast(machine().device(glib0[i])); + m_p3grom[i] = downcast(machine().device(glib3[i])); + } + m_tsgrom[i] = downcast(machine().device(glib1[i])); + m_p8grom[i] = downcast(machine().device(glib2[i])); + } + + m_rom0 = machine().root_device().memregion(ROM0_REG)->base(); + m_rom1 = machine().root_device().memregion(ROM1_REG)->base(); + m_pascalrom = machine().root_device().memregion(PASCAL_REG)->base(); + + // Register state variables + save_item(NAME(m_A14_set)); + save_item(NAME(m_logical_address)); + save_item(NAME(m_physical_address)); + save_item(NAME(m_pending_write)); + save_item(NAME(m_latched_data)); + save_item(NAME(m_gromclk)); + save_item(NAME(m_prev_grom)); + save_item(NAME(m_speech_ready)); + save_item(NAME(m_sound_ready)); + save_item(NAME(m_pbox_ready)); + save_item(NAME(m_dbin_level)); + save_item(NAME(m_last_ready)); + save_item(NAME(m_sgrom_idle)); + save_item(NAME(m_tsgrom_idle)); + save_item(NAME(m_p8grom_idle)); + save_item(NAME(m_p3grom_idle)); +} + +void mainboard8_device::device_reset() +{ + logerror("Resetting main board\n"); + m_last_ready = CLEAR_LINE; + m_speech_ready = true; + m_sound_ready = true; + m_pbox_ready = true; + m_pending_write = false; + m_prev_grom = 0; + m_A14_set = false; + // Configure RAM and AMIGO + m_amigo->connect_sram(m_sram->pointer()); + + // Get the pointer to the address space; we need it outside of the + // usual memory functions. + cpu_device* cpu = downcast(machine().device("maincpu")); + m_space = &cpu->space(AS_PROGRAM); +} + +MACHINE_CONFIG_START( ti998_mainboard ) + MCFG_DEVICE_ADD(VAQUERRO_TAG, TI99_VAQUERRO, 0) + MCFG_DEVICE_ADD(MOFETTA_TAG, TI99_MOFETTA, 0) + MCFG_DEVICE_ADD(AMIGO_TAG, TI99_AMIGO, 0) + MCFG_DEVICE_ADD(OSO_TAG, TI99_OSO, 0) +MACHINE_CONFIG_END + +machine_config_constructor mainboard8_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( ti998_mainboard ); +} + +/*************************************************************************** + ===== VAQUERRO: Logical Address Space decoder ===== + + Logical address space (LAS) + =========================== + The LAS is the address space as seen by the TMS 9995 CPU. It is 64 KiB large. + The LAS can be configured in two ways: + - the native (99/8) mode + - and the compatibility mode (99/4A) + + Both modes are selected by CRU bit 20 on base 0000 (named "CRUS"). + + The console starts up in compatibility mode. + + The compatibility mode organizes the LAS in a similar way as the TI-99/4A. + This means that machine language programs should run with no or only minor + changes. In particular, game cartridges work without problems. + + The native mode rearranges the address space and puts memory-mapped devices + to other positions. + + TI-99/4A compatibility mode (CRUS=1) + ------------------------------------ + 0000-1fff: 2 KiB ROM0 + 2000-7fff: Free area + 8000-87ff: 2 KiB SRAM + 8000-81ff: mapper files (8 files with 16*4 bytes each) + 8200-82ff: Free RAM + 8300-83ff: Scratch-pad RAM as in the 99/4A + 8400-840f: Sound chip + 8800-880f: VDP read port (data, status) + 8810-881f: Mapper access port + 8820-8bff: Free area + 8c00-8c0f: VDP write port (data, address) + 8c10-8fff: Free area + 9000-900f: Speech synthesizer read (on-board) + 9010-93ff: Free area + 9400-940f: Speech synthesizer write (on-board) + 9410-97ff: Free area + 9800-980f: System GROM read (data, address) + 9810-9bff: Free area + 9c00-9c0f: System GROM write (data, address) + 9c10-fffb: Free area + fffc-ffff: NMI vector + + TI-99/8 native mode (CRUS=0) + ---------------------------- + 0000-efff: Free area + f000-f7ff: 2 KiB SRAM + f000-f1ff: mapper files (8 files with 16*4 bytes each) + f200-f7ff: Free RAM + f800-f80f: Sound chip + f810-f81f: VDP read (data, status) and write (data, address) + f820-f82f: Speech synthesizer read/write + f830-f83f: System GROM read/write + f840-f86f: Free area + f870-f87f: Mapper access port + f880-fffb: Free area + fffc-ffff: NMI vector + + Note that ROM0 is not visible in the native mode. + + If CRU bit 21 (PTGEN*) is set to 0, Pascal GROMs appear in the LAS in either + mode. It is highly recommended to use native mode when turning on these + GROMs, because the area where they appear may be occupied by a program in + 99/4A mode. + + Pascal and Text-to-speech GROM enabled (PTGEN*=0) + ------------------------------------------------- + f840-f84f: Text-to-speech GROM read/write + f850-f85f: P-Code library #1 GROM read/write + f860-f86f: P-Code library #2 GROM read/write + + +***************************************************************************/ + +vaquerro_device::vaquerro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, TI99_VAQUERRO, tag, owner, clock), + m_crus(ASSERT_LINE), + m_crugl(ASSERT_LINE), + m_ggrdy(ASSERT_LINE) +{ +} + +SETADDRESS_DBIN_MEMBER( vaquerro_device::set_address ) +{ + // Do the decoding + // state = dbin, offset = address + + bool reading = (state==ASSERT_LINE); + bool sgfap = false; + bool tsgfap = false; + bool p8gfap = false; + bool p3gfap = false; + bool vfap = false; + + m_a14 = ((offset & 2)!=0)? ASSERT_LINE : CLEAR_LINE; // Needed for clock_in + + m_dbin_level = (line_state)state; + + int offbase = (offset & 0xfff1); + + // =================== TI (compatibility) mode ====================== + if (m_crus == ASSERT_LINE) + { + if (TRACE_DETAIL) logerror("Compatibility mode\n"); + + // SPRD (Speech read) + m_sprd = ((offbase==0x9000) && reading); + + // SPWT (Speech write) + m_spwt = ((offbase==0x9400) && !reading); + + // Sound + m_sccs = ((offbase==0x8400)&& !reading); + + // ROM0 + m_sromcs = (((offset & 0xe000)==0x0000) && reading); + + // Video select + vfap = ((offbase==0x8800) && reading) || ((offbase==0x8c00) && !reading); + + // System GROM + sgfap = ((offbase==0x9800) && reading) || ((offbase==0x9c00) && !reading); + } + // ====================== Native mode ====================== + else + { + if (TRACE_DETAIL) logerror("Native mode\n"); + + // SPRD (Speech read) + m_sprd = ((offbase==0xf820) && reading); + + // SPWT (Speech write) + m_spwt = ((offbase==0xf820) && !reading); + + // Sound + m_sccs = ((offbase==0xf800) && !reading); + + // Video + vfap = (offbase==0xf810); + + // System GROM (read and write) + sgfap = (offbase==0xf830); + } + + // These lines are not decoded for compatibility or native mode, only + // the line CRUGL determines whether they become visible. + tsgfap = (offbase==0xf840) && m_crugl; + p8gfap = (offbase==0xf850) && m_crugl; + p3gfap = (offbase==0xf860) && m_crugl; + + // The LASREQ line says whether Vaquerro does the job, or whether it is Mofetta's turn. + m_grom_or_video = sgfap || tsgfap || p8gfap || p3gfap || vfap ; + + m_lasreq = (m_sprd || m_spwt || m_sccs || m_sromcs || m_grom_or_video); + + if (TRACE_DETAIL) logerror("sgfap=%d tsgfap=%d p8gfap=%d p3gfap=%d vfap=%d\n", sgfap, tsgfap, p8gfap, p3gfap, vfap); + + // Pass the selection to the wait state generators + // and pick up the current select line states + m_sgmws.select_in(sgfap); + m_tsgws.select_in(tsgfap); + m_p8gws.select_in(p8gfap); + m_p3gws.select_in(p3gfap); + m_vidws.select_in(vfap); + + m_gromsel = m_sgmws.select_out() | m_tsgws.select_out() | m_p8gws.select_out() | m_p3gws.select_out(); + + m_vdprd = (reading && (m_vidws.select_out()!=0)); + m_vdpwt = (!reading && (m_vidws.select_out()!=0)); + + if (m_grom_or_video) + { + // We don't see the current selection now; only with next clock pulse. + m_mainboard->ggrdy_in(m_sry); + if (TRACE_READY) logerror("GGRDY = %d\n", m_sry); + } +} + +WRITE_LINE_MEMBER( vaquerro_device::crusgl_in ) +{ + m_crugl = (state==ASSERT_LINE); +} + +WRITE_LINE_MEMBER( vaquerro_device::crus_in ) +{ + m_crus = (line_state)state; +} + +WRITE_LINE_MEMBER( vaquerro_device::memen_in ) +{ + m_memen = (state==ASSERT_LINE); +} + +/* + Called by Mofetta +*/ +READ_LINE_MEMBER( vaquerro_device::lascsq_out ) +{ + return (m_lasreq && m_memen)? ASSERT_LINE : CLEAR_LINE; +} + +/* + Incoming ready lines from the GROM library +*/ +WRITE_LINE_MEMBER( vaquerro_device::sgmry ) +{ + if (TRACE_READY) logerror("Incoming SGMRY = %d\n", state); + m_sgmws.ready_in((line_state)state); +} + +WRITE_LINE_MEMBER( vaquerro_device::tsgry ) +{ + if (TRACE_READY) logerror("Incoming TSGRY = %d\n", state); + m_tsgws.ready_in((line_state)state); +} + +WRITE_LINE_MEMBER( vaquerro_device::p8gry ) +{ + if (TRACE_READY) logerror("Incoming 8GRY = %d\n", state); + m_p8gws.ready_in((line_state)state); +} + +WRITE_LINE_MEMBER( vaquerro_device::p3gry ) +{ + if (TRACE_READY) logerror("Incoming P3GRY = %d\n", state); + m_p3gws.ready_in((line_state)state); +} + +/* + Outgoing READY +*/ +READ_LINE_MEMBER( vaquerro_device::ggrdy_out ) +{ + if (TRACE_READY) logerror("GGRDY out = %d\n", m_ggrdy); + return m_ggrdy; +} + +/* + Select lines +*/ + +// ========================= + +int vaquerro_device::gromcs_out() +{ + return m_gromsel; +} + +// ========================= + +READ_LINE_MEMBER( vaquerro_device::vdprd_out ) +{ + return (m_vdprd && m_memen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( vaquerro_device::vdpwt_out ) +{ + return (m_vdpwt && m_memen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( vaquerro_device::sprd_out ) +{ + return (m_sprd && m_memen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( vaquerro_device::spwt_out ) +{ + return (m_spwt && m_memen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( vaquerro_device::sromcs_out ) +{ + return (m_sromcs && m_memen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( vaquerro_device::sccs_out ) +{ + return (m_sccs && m_memen)? ASSERT_LINE : CLEAR_LINE; +} + +/* + Incoming clock signal. + + The Vaquerro has a Wait State generation logic circuit for the video + processor and all 4 GROM libraries. Each one has its separate generator. + The GROMs get a 16 cycle wait period after their access, while the video + processors gets an 8 cycle wait period. If during that period another + access occurs, the system READY line will be cleared, triggering wait + states in the CPU. +*/ +WRITE_LINE_MEMBER( vaquerro_device::clock_in ) +{ + line_state level = (line_state)state; + + // Propagate to the wait state generators (note that we need both clock levels) + m_sgmws.clock_in(level); + m_tsgws.clock_in(level); + m_p8gws.clock_in(level); + m_p3gws.clock_in(level); + m_vidws.clock_in(level); + + // Collect the selections + // Each one has its own indication, defined at init time + m_gromsel = m_sgmws.select_out() | m_tsgws.select_out() | m_p8gws.select_out() | m_p3gws.select_out(); + + bool reading = (m_dbin_level==ASSERT_LINE); + + m_vdprd = (reading && (m_vidws.select_out()!=0)); + m_vdpwt = (!reading && (m_vidws.select_out()!=0)); + + // Get the READY levels from the GROMs + if (level==CLEAR_LINE) + { + m_sry = m_sgmws.ready_out() || m_tsgws.ready_out() || m_p8gws.ready_out() || m_p3gws.ready_out() || m_vidws.ready_out(); + if (TRACE_WS) logerror("ready_out = (%d, %d, %d, %d, %d)\n", m_sgmws.ready_out(), m_tsgws.ready_out(), m_p8gws.ready_out(), m_p3gws.ready_out(),m_vidws.ready_out()); + } + + // If the output gate is closed, propagate ASSERT_LINE (pulled up) + m_ggrdy = (!m_grom_or_video || m_sry)? ASSERT_LINE : CLEAR_LINE; +} + + +void vaquerro_device::device_start() +{ + logerror("Starting\n"); + m_mainboard = downcast(owner()); + m_sgmws.init(SGMSEL); + m_tsgws.init(TSGSEL); + m_p8gws.init(P8GSEL); + m_p3gws.init(P3GSEL); + m_vidws.init(VIDSEL); + + save_item(NAME(m_memen)); + save_item(NAME(m_video_wait)); + save_item(NAME(m_crus)); + save_item(NAME(m_crugl)); + save_item(NAME(m_lasreq)); + save_item(NAME(m_grom_or_video)); + save_item(NAME(m_spwt)); + save_item(NAME(m_sccs)); + save_item(NAME(m_sromcs)); + save_item(NAME(m_sprd)); + save_item(NAME(m_vdprd)); + save_item(NAME(m_vdpwt)); + save_item(NAME(m_gromsel)); + save_item(NAME(m_ggrdy)); + save_item(NAME(m_sry)); + save_item(NAME(m_a14)); + save_item(NAME(m_dbin_level)); + + // FIXME: In rare occasions, the saved state is invalid and restoring + // may crash the emulated 99/8 (e.g. with invalid opcodes) + // Saving the wait state logic does not affect the operation, as it seems, + // so we leave it out. +} + +void vaquerro_device::device_reset() +{ + m_ggrdy = ASSERT_LINE; + m_vdpwt = m_vdprd = CLEAR_LINE; + m_gromsel = 0; + m_sgmws.treset_in(ASSERT_LINE); + m_tsgws.treset_in(ASSERT_LINE); + m_p8gws.treset_in(ASSERT_LINE); + m_p3gws.treset_in(ASSERT_LINE); + m_vidws.treset_in(ASSERT_LINE); +} + +/* + Wait state generation logic inside Vaquerro + + Analysis of the logic diagram of the Vaquerro delivers the following + behavior (for the first GROM library; similar behavior applies for the + other libraries). Note that the CLKOUT line is inverted. + + 1. When the GROMs are unselected by address (SGFAP), the SRY line is Z + (System READY). The GROM ready line (SGMRY) has no effect. + 2. When SGFAP is asserted while the internal counter is off, the + READY line changes from Z to Low and the GROM select line (SGCS) is + asserted (both immediately, before the next tick edge). SGMRY has no effect. + The circuit state is constant during further clock ticks. + 3. After being selected, when SGMRY is asserted (GROM is ready), SRY + changes to High on the next trailing edge. This will allow the + CPU to complete the GROM access on the next cycle, + and the address bus will change, typically deselecting the GROMs. + Until this deselection, the circuit state remains constant + (SGCS asserted, READY=H). + 4. When the GROMs are deselected, SRY changes to Z, and SGCS is cleared + (immediately). A counter is started at 0 that is incremented on each clock + tick (leading edge). + 5. When SGFAP is asserted while the counter is less that 15, SRY changes + to Low immediately. SGCS remains cleared, so the GROMs are not selected. + While SGFAP stays cleared, the counter completes its way to 15, + then 0, and turns off. + 6. When the counter reaches 15, it returns to 0 on the next tick + (leading). On the following trailing edge, the GROM select line is + asserted, while the READY line remains Low. + 7. Continue at 3. +*/ +void vaquerro_device::waitstate_generator::select_in(bool addressed) +{ + m_addressed = addressed; +} + +int vaquerro_device::waitstate_generator::select_out() +{ + return (!m_counting && m_addressed)? m_selvalue : 0; +} + +/* + Should be low by default. +*/ +line_state vaquerro_device::waitstate_generator::ready_out() +{ + return (m_ready && !m_counting && m_generate)? ASSERT_LINE : CLEAR_LINE; +} + +bool vaquerro_device::waitstate_generator::is_counting() +{ + return m_counting; +} + +bool vaquerro_device::waitstate_generator::is_generating() +{ + return m_generate; +} + +bool vaquerro_device::waitstate_generator::is_ready() +{ + return m_ready; +} + +/* + READY in. This may only show an effect with the next trailing edge of CLKOUT. +*/ +void vaquerro_device::grom_waitstate_generator::ready_in(line_state ready) +{ + m_ready = (ready==ASSERT_LINE); +} + +void vaquerro_device::grom_waitstate_generator::clock_in(line_state clkout) +{ + if (clkout == ASSERT_LINE) + { + if (m_counting) m_counter++; + } + else + { + if (m_counting && m_counter==16) + { + m_counter = 0; + m_counting = false; + } + else + { + if (!m_addressed && m_generate) m_counting = true; + m_generate = ((m_addressed || m_counting) && (m_counter != 15)); + } + } +} + +void vaquerro_device::waitstate_generator::treset_in(line_state reset) +{ + if (reset==ASSERT_LINE) + { + m_counter = 0; + m_generate = m_counting = m_addressed = false; + } +} + +void vaquerro_device::video_waitstate_generator::clock_in(line_state clkout) +{ + if (clkout == ASSERT_LINE) + { + if (m_counting) m_counter++; + } + else + { + if (m_counting && m_counter==7) + { + m_counter = 0; + m_counting = false; + } + else + { + if (!m_addressed && m_generate) m_counting = true; + m_generate = ((m_addressed || m_counting) && (m_counter != 6)); + } + } +} + +/*************************************************************************** + ===== MOFETTA: Physical Address Space decoder ===== + + Physical address space (PAS) + ============================ + The PAS is 24 bits wide and accessed via the custom mapper chip nicknamed + "Amigo". The mapper exchanges map definitions with SRAM (see LAS). That + means, a map can be prepared in SRAM, and for activating it, the mapper + is accessed on its port, telling it to load or save a map. + + 000000-00ffff: 64 KiB console DRAM + 010000-efffff: undefined + + f00000-f03fff: PASCAL support ROM (not mentioned in [1]) + + f04000-feffff: undefined + ff0000 : unmapped (code for mapper) + ff0001-ff3fff: undefined + ff4000-ff5fff: DSR ROM in Peripheral Box, Hexbus DSR (CRU 1700) or additional ROM (CRU 2700) + ff6000-ff9fff: Cartridge ROM space + ffa000-ffdfff: 16 KiB ROM1 + ffe000-ffe00f: Interrupt level sense + ffe010-ffffff: undefined + + +***************************************************************************/ + +enum +{ + UNDEF=0, + DRAM, + PASCAL, + INTERNAL +}; + +mofetta_device::mofetta_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, TI99_MOFETTA, tag, owner, clock), + m_gotfirstword(false) +{ +} + +SETADDRESS_DBIN_MEMBER( mofetta_device::set_address ) +{ + if (!m_gotfirstword) + { + // Store the first word and wait for clearing of MSAST + if (TRACE_MOFETTA) logerror("Got the upper word of the address: %04x\n", offset); + m_address_latch = offset; + } + else + { + // Second part - now decode the address + if (TRACE_MOFETTA) logerror("Got the lower word of the address: %04x\n", offset); + + bool acs, tcs, rcs, acsx; + bool reading = (state==ASSERT_LINE); + int offbase = (offset & 0xe000); + + // PASCAL ROM select (16K) + m_prcs = (m_prefix == 0xf0) && ((offset & 0xc000) == 0x0000); + + // Hexbus select + acs = (m_prefix == 0xff) && (offbase == 0x4000) && m_alcpg; + + // Internal DSR select (ff4000-ff5fff @ CRU>2700) + tcs = (m_prefix == 0xff) && (offbase == 0x4000) && m_txspg; + + // Hexbus select (ff4000-ff5fef @ CRU>1700), excluding OSO + acsx = acs && ((offset & 0x1ff0)!=0x1ff0); + + // Upper 16K of ROM1 + m_rom1am = !((offbase == 0xa000) || (offbase == 0xc000)); + + // ROM select + rcs = (m_prefix == 0xff) && reading && !m_rom1am; + + // ROM1 select (containing 16K ROM, 8K TTS, 8K ACS) + m_rom1cs = tcs || rcs || acsx; + + // Accessing OSO (ff5ff0 @ CRU>1700) + m_alccs = acs && ((offset & 0x1ff0)==0x1ff0); + + // Second half of ROM or ACS + m_rom1al = reading && (m_prefix == 0xff) && ((offbase == 0xc000) || acs); + + // Cartridge port (ff6000-ff9fff) + m_cmas = (m_prefix == 0xff) && ((offbase == 0x6000) || (offbase == 0x8000)); + + m_gotfirstword = false; + } +} + +/* + Mofetta delivers the GROMCLK. In the 99/4A, this clock is produced by the VDP. + Apart from that, Mofetta does not need the CLKOUT. +*/ +WRITE_LINE_MEMBER( mofetta_device::clock_in ) +{ + if (state == CLEAR_LINE) // TODO: Correct edge? + { + m_gromclock_count++; + if (m_gromclock_count >=3) + { + m_gromclk_up = !m_gromclk_up; + m_gromclock_count = 0; + } + } +} + +READ_LINE_MEMBER( mofetta_device::alccs_out ) +{ + return (m_alccs && m_pmemen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( mofetta_device::gromclk_out ) +{ + return m_gromclk_up? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( mofetta_device::rom1cs_out ) +{ + return (m_rom1cs && m_pmemen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( mofetta_device::rom1am_out ) +{ + return (m_rom1am && m_pmemen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( mofetta_device::rom1al_out ) +{ + return (m_rom1al && m_pmemen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( mofetta_device::prcs_out ) +{ + return (m_prcs && m_pmemen)? ASSERT_LINE : CLEAR_LINE; +} + +READ_LINE_MEMBER( mofetta_device::cmas_out ) +{ + return (m_cmas && m_pmemen)? ASSERT_LINE : CLEAR_LINE; +} + +/* + Asserted when a PEB access occurs +*/ +READ_LINE_MEMBER( mofetta_device::dbc_out ) +{ + return (m_lasreq || m_cmas || m_rom1cs || m_skdrcs || !m_pmemen)? CLEAR_LINE : ASSERT_LINE; +} + +/* + Debugger support +*/ +bool mofetta_device::hexbus_access_debug() +{ + return m_alcpg; +} + +bool mofetta_device::intdsr_access_debug() +{ + return m_txspg; +} + +WRITE8_MEMBER(mofetta_device::cruwrite) +{ + if ((offset & 0xff00)==0x2700) + { + if ((offset & 0x0002)!=0) + { + // SWRST (Software reset) + // Value seems to be irrelevant + if (TRACE_CRU) logerror("Doing a software reset by SBO 2702\n"); + m_mainboard->reset_console(ASSERT_LINE); + m_mainboard->reset_console(CLEAR_LINE); + } + else + { + m_txspg = (data!=0); // CRU>2700 + if (TRACE_CRU) logerror("Turning %s CRU>2700\n", m_txspg? "on" : "off"); + } + } + else + { + if ((offset & 0xff00)==0x1700) + { + m_alcpg = (data!=0); // CRU>1700 + if (TRACE_CRU) logerror("Turning %s CRU>1700\n", m_alcpg? "on" : "off"); + } + } +} + +/* + Setting or clearing the MSAST line. +*/ +WRITE_LINE_MEMBER( mofetta_device::msast_in ) +{ + if (state == ASSERT_LINE) + { + if (m_msast == CLEAR_LINE) // Leading edge + { + m_gotfirstword = true; // Process first word + // We now have the first part, containing the flags and the upper byte. + m_prefix = m_address_latch & 0xff; + } + } + // TODO: Evaluate the first three bits + m_msast = (line_state)state; +} + +WRITE_LINE_MEMBER( mofetta_device::pmemen_in ) +{ + m_pmemen = (state==ASSERT_LINE); +} + +WRITE_LINE_MEMBER( mofetta_device::lascs_in ) +{ + m_lasreq = (state==ASSERT_LINE); +} + +WRITE_LINE_MEMBER( mofetta_device::skdrcs_in ) +{ + m_skdrcs = (state==ASSERT_LINE); +} + +void mofetta_device::device_start() +{ + logerror("Starting\n"); + m_mainboard = downcast(owner()); + + save_item(NAME(m_pmemen)); + save_item(NAME(m_lasreq)); + save_item(NAME(m_skdrcs)); + save_item(NAME(m_gromclk_up)); + save_item(NAME(m_gotfirstword)); + save_item(NAME(m_address_latch)); + save_item(NAME(m_prefix)); + save_item(NAME(m_alcpg)); + save_item(NAME(m_txspg)); + save_item(NAME(m_rom1cs)); + save_item(NAME(m_rom1am)); + save_item(NAME(m_rom1al)); + save_item(NAME(m_alccs)); + save_item(NAME(m_prcs)); + save_item(NAME(m_cmas)); + save_item(NAME(m_gromclock_count)); + save_item(NAME(m_msast)); +} + +void mofetta_device::device_reset() +{ + m_gotfirstword = false; + m_alcpg = false; + m_txspg = false; + m_prefix = 0; +} + +/*************************************************************************** + + ============================== + Mapper (codename "Amigo") + ============================== + + Unfortunately, we do not have logic diagrams for Amigo, so we have to + guess how it is actually working. + + Initial setting of mapper (as defined in the power-up routine, TI-99/4A mode) + + 0 00ff0000 -> Unmapped; logical address 0000...0fff = ROM0 + 1 00ff0000 -> Unmapped; logical address 1000...1fff = ROM0 + 2 00000800 -> DRAM; 2000 = 000800, 2fff = 0017ff + 3 00001800 -> DRAM; 3000 = 001800, 3fff = 0027ff + 4 00ff4000 -> DSR space (internal / ioport) + 5 00ff5000 -> DSR space (internal / ioport) + 6 00ff6000 -> Cartridge space (6000..6fff) + 7 00ff7000 -> Cartridge space (7000..7fff) + 8 00ff0000 -> Unmapped; device ports (VDP) and SRAM + 9 00ff0000 -> Unmapped; device ports (Speech, GROM) + A 00002800 -> DRAM; a000 = 002800, afff = 0037ff + B 00003800 -> DRAM; b000 = 003800, bfff = 0047ff + C 00004800 -> DRAM; c000 = 004800, cfff = 0057ff + D 00005800 -> DRAM; d000 = 005800, dfff = 0067ff + E 00006800 -> DRAM; e000 = 006800, efff = 0077ff + F 00007800 -> DRAM; f000 = 007800, ffff = 0087ff + + Format of map table entry + + +--+---+---+---+---+---+---+---+ +-----------+ +----------+ +---------+ + | W| X | R | 0 | 0 | 0 | 0 | 0 | | Upper (8) | | High (8) | | Low (8) | + +--+---+---+---+---+---+---+---+ +-----------+ +----------+ +---------+ + + W: Write protection if set to 1 + X: Execute protection if set to 1 + R: Read protection if set to 1 + + When a protection violation occurs, the tms9901 INT1* pin is pulled low + (active). The pin remains low until the mapper status register is read. + + Address handling + ---------------- + Physical address is (Upper * 2^16) + (High * 2^8) + Low + + The mapper calculates the actual physical address by looking up the + table entry from the first four bits of the logical address and then + *adding* the remaining 12 bits of the logical address on the map value. + + The value 0xff0000 is used to indicate a non-mapped area. + + Mapper control register + ----------------------- + The mapper control register is used to initiate a map load/save operation. + + +---+---+---+---+---+---+---+---+ + | 0 | 0 | 0 | 0 | Map File | RW| + +---+---+---+---+---+---+---+---+ + + The map file is a number from 0-7 indicating the set of map values for the + operation, which means the location in SRAM where the next 64 values are + loaded from or stored into. + + RW = 1: load from SRAM into mapper + RW = 0: store from mapper into SRAM + + When read, the mapper register returns the violation flags: + +---+---+---+---+---+---+---+---+ + | W | X | R | 0 | 0 | 0 | 0 | 0 | + +---+---+---+---+---+---+---+---+ + +***************************************************************************/ + +amigo_device::amigo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, TI99_AMIGO, tag, owner, clock), + m_logical_space(true), + m_crus(ASSERT_LINE) +{ +} + +enum +{ + IDLE = 0, + CREATE_PADDR, + ADDR_MSW, + ADDR_LSW, + SRAMLOAD, + SRAMSAVE +}; + +/* + Debugger support +*/ +int amigo_device::get_physical_address_debug(offs_t offset) +{ + return ((offset & 0x0fff) + m_base_register[(offset >> 12) & 0x000f]) & 0x00ffffff; +} + +/* + Incoming READY line (SRDY) +*/ +WRITE_LINE_MEMBER( amigo_device::srdy_in ) +{ + if (TRACE_READY) logerror("Incoming SRDY = %d\n", state); + m_srdy = (line_state)state; + + // If the access is going to logical space, pass through the READY line + if (m_logical_space) + { + if (TRACE_CPURY) logerror("Setting CPURY = %d (SRDY)\n", m_ready_out); + m_ready_out = m_srdy; + } +} + +WRITE_LINE_MEMBER( amigo_device::memen_in ) +{ + m_memen = (state==ASSERT_LINE); +} + +/* + Polled from the mainboard +*/ +READ_LINE_MEMBER( amigo_device::cpury_out ) +{ + return m_ready_out; +} + +/* + Polled from the mainboard +*/ +READ_LINE_MEMBER( amigo_device::sramcs_out ) +{ + return m_sram_accessed && m_memen? ASSERT_LINE : CLEAR_LINE; +} + +/* + SKDRCS line (maybe "Sixty-four Kilobyte DRam Chip Select"). We assume that + Amigo asserts the select line not before the whole address was written. + This is actually more than needed because we only have 64K DRAM (which + would only need a 16 bit address), and Amigo itself selects it. +*/ +READ_LINE_MEMBER( amigo_device::skdrcs_out ) +{ + return m_dram_accessed && (m_amstate == IDLE) && m_memen? ASSERT_LINE : CLEAR_LINE; +} + +/* + Incoming CRUS line. Needed to set the mapper config addresses. +*/ +WRITE_LINE_MEMBER( amigo_device::crus_in ) +{ + m_crus = (line_state)state; +} + +/* + Incoming LASCS line. +*/ +WRITE_LINE_MEMBER( amigo_device::lascs_in ) +{ + m_logical_space = (state==ASSERT_LINE); +} + +/* + The logical address bus has been set. The Amigo chip now has to map this + address to a physical address. There are three phases (3 clock ticks): + 1. Sample the logical address lines, determine the map register + (first four bits), create the physical address by adding the remaining + 12 bits and the map register contents + 2. Set the physical address bus with the first 16 bits of the physical + address. Assert the MSAST line. + 3. Set the physical address bus with the second 16 bits of the physical + address. Clear the MSAST line. Forward any incoming READY=0 to the CPU. +*/ +SETOFFSET_MEMBER( amigo_device::set_address ) +{ + // Check whether the mapper itself is accessed + int mapaddr = (m_crus==ASSERT_LINE)? 0x8810 : 0xf870; + m_mapper_accessed = ((offset & 0xfff1)==mapaddr); + + // or SRAM + int sramaddr = (m_crus==ASSERT_LINE)? 0x8000 : 0xf000; + m_sram_accessed = ((offset & 0xf800)==sramaddr); + + m_logical_space |= (m_mapper_accessed || m_sram_accessed); + + // Is the address not in the logical address space? + if (!m_logical_space) + { + if (TRACE_AMIGO) logerror("Amigo decoding; %04x is a physical address.\n", offset); + // Build the physical address + // The first three bits are the protection bits (Write, Execute, Read) + // Theoretically, the addition of the logical address could mess up those + // first three bits, but the physical address is only 24 bits wide, so we + // have a space of 5 zeros between the protection bits and the address. + // We should just clear those five bits after the addition. + + m_physical_address = ((offset & 0x0fff) + m_base_register[(offset >> 12) & 0x000f]) & 0x00ffffff; + + // TODO: Process flags + + // Is it DRAM? + m_dram_accessed = (m_physical_address & 0x00ff0000)==0; + + // This takes one clock pulse. + m_amstate = CREATE_PADDR; + + // Pull down READY + m_ready_out = CLEAR_LINE; + + if (TRACE_CPURY) logerror("Setting CPURY = %d (PAS)\n", m_ready_out); + } + else + { + // This was a logical space access. Pass through READY. + m_dram_accessed = false; + m_amstate = IDLE; + m_ready_out = m_srdy; + if (TRACE_CPURY) logerror("Setting CPURY = %d (LAS)\n", m_ready_out); + } +} + +/* + Read the mapper status bits +*/ +READ8_MEMBER( amigo_device::read ) +{ + // Read the protection status bits and reset them + uint8_t value = m_protflag; + m_protflag = 0; + return value; +} + +/* + Configure the mapper. This is the only reason to write to the AMIGO. +*/ +WRITE8_MEMBER( amigo_device::write ) +{ + // Load or save map file + if ((data & 0xf0)==0x00) + { + // Need to HOLD the CPU + m_amstate = ((data & 1)==1)? SRAMLOAD : SRAMSAVE; + m_sram_address = (data & 0x0e) << 5; + m_hold_acknowledged = false; + m_basereg = 0; + m_mapvalue = 0; + m_mainboard->hold_cpu(ASSERT_LINE); + } + else logerror("Invalid value written to Amigo: %02x\n", data); +} + +WRITE_LINE_MEMBER( amigo_device::clock_in ) +{ + if (state==CLEAR_LINE) + { + switch (m_amstate) + { + case IDLE: + break; + case CREATE_PADDR: + // Address has been created + m_amstate = ADDR_MSW; + break; + case ADDR_MSW: + // Transmit the first word (without the protection bits) + m_mainboard->set_paddress((m_physical_address >> 16) & 0x00ff); + m_amstate = ADDR_LSW; + break; + case ADDR_LSW: + m_mainboard->msast_in(ASSERT_LINE); // Pulse MSAST + m_mainboard->msast_in(CLEAR_LINE); + m_mainboard->set_paddress(m_physical_address & 0xffff); + m_amstate = IDLE; + m_ready_out = m_srdy; // Propagate incoming READY + break; + + case SRAMLOAD: + if (m_hold_acknowledged) mapper_load(); + break; + + case SRAMSAVE: + if (m_hold_acknowledged) mapper_save(); + break; + + default: + logerror("Invalid state in mapper: %d\n", m_amstate); + } + } +} + +void amigo_device::mapper_load() +{ + m_mapvalue = (m_mapvalue << 8) | m_sram[m_sram_address++]; + + if ((m_sram_address & 0x03)==0) + { + if (TRACE_MAP) logerror("Loaded basereg %02d = %08x\n", m_basereg, m_mapvalue); + m_base_register[m_basereg++] = m_mapvalue; + } + if (m_basereg == 16) + { + m_amstate = IDLE; + m_mainboard->hold_cpu(CLEAR_LINE); + } +} + +void amigo_device::mapper_save() +{ + if ((m_sram_address & 0x03)==0) + { + if (m_basereg == 16) + { + m_amstate = IDLE; + m_mainboard->hold_cpu(CLEAR_LINE); + return; + } + else + { + m_mapvalue = m_base_register[m_basereg]; + if (TRACE_MAP) logerror("Saving basereg %02d = %08x\n", m_basereg, m_mapvalue); + m_basereg++; + } + } + + m_sram[m_sram_address++] = (m_mapvalue >> 24) & 0xff; + m_mapvalue = m_mapvalue << 8; +} + +/* + Debugger support +*/ +void amigo_device::mapper_access_debug(int data) +{ + if ((data & 0xf0)==0x00) + { + int address = (data & 0x0e) << 5; + + if ((data & 1)==1) + { + for (int i=0; i < 64; i++) + { + // Load from SRAM + m_base_register[i/4] = (m_base_register[i/4] << 8) | (m_sram[address++] & 0xff); + } + } + else + { + for (int i=0; i < 16; i++) + { + // Save to SRAM + m_sram[address++] = (m_base_register[i] >> 24) & 0xff; + m_sram[address++] = (m_base_register[i] >> 16) & 0xff; + m_sram[address++] = (m_base_register[i] >> 8) & 0xff; + m_sram[address++] = m_base_register[i] & 0xff; + } + } + } +} + +WRITE_LINE_MEMBER( amigo_device::holda_in ) +{ + if (TRACE_MAP) logerror("HOLD acknowledged = %d\n", state); + m_hold_acknowledged = (state==ASSERT_LINE); +} + +void amigo_device::device_start() +{ + logerror("Starting\n"); + m_mainboard = downcast(owner()); + + save_item(NAME(m_memen)); + save_pointer(NAME(m_base_register),16); + save_item(NAME(m_logical_space)); + save_item(NAME(m_physical_address)); + save_item(NAME(m_srdy)); + save_item(NAME(m_ready_out)); + save_item(NAME(m_crus)); + save_item(NAME(m_amstate)); + save_item(NAME(m_protflag)); + save_item(NAME(m_sram_accessed)); + save_item(NAME(m_dram_accessed)); + save_item(NAME(m_mapper_accessed)); + save_item(NAME(m_hold_acknowledged)); + save_item(NAME(m_sram_address)); + save_item(NAME(m_basereg)); + save_item(NAME(m_mapvalue)); +} + +void amigo_device::device_reset() +{ + m_logical_space = true; +} + +/*************************************************************************** + + ===== OSO: Hexbus interface ===== + + The Hexbus is a 4-bit peripheral bus with master/slave coordination. Bytes + are written over the bus in two passes. Hexbus was the designated standard + peripheral bus for TI computers before TI left the home computer market. + + Existing devices are floppy drive, RS232 serial adapter, and + a "Wafertape" drive (kind of tape streamer) + + Registers: Read Write Bits of register + ---------------------------------------------------------------------------- + Data : 5FF8 - ADB3 ADB2 ADB1 ADB0 ADB3 ADB2 ADB1 ADB0 + Status : 5FFA - HSKWT HSKRD BAVIAS BAVAIS SBAV WBUSY RBUSY SHSK + Control : 5FFC 5FFA WIEN RIEN BAVIAEN BAVAIEN BAVC WEN REN CR7 + Xmit : 5FFE 5FF8 XDR0 XDR1 XDR2 XDR3 XDR4 XDR5 XDR6 XDR7 + + ADBx = Hexbus data bit X + HSKWT = Set when a byte has been sent over the bus and HSK has been asserted + HSKRD = Set when a byte has been received + BAVIAS = set when the BAV* signal (bus available) transits to active state + BAVAIS = set when the BAV* signal transits to inactive state (=1) + SBAV = set when BAV* = 0 (active) + WBUSY = set when a write action is in progress (two transfers @ 4 bits) + Reset when HSKWT is set + RBUSY = set when a read action is in progress (two transfers @ 4 bits) + Reset when HSKRD is set + SHSK = set when HSK* is active (0) + + WIEN = Enable interrupt for write completion + RIEN = Enable interrupt for read completion + BAVIAEN = BAVIA enable (slave mode) + BAVAIEN = BAVAI enable (slave mode) + BAVC = set BAV* line (0=active) + WEN = set write enable (byte is written from xmit reg) + REN = set read enable (latch HSK and read byte into data reg) + CR7 = future extension + XDRx = transmit register bit + + Hexbus connector (console) + +---+---+---+---+ + | 4 | 3 | 2 | 1 | 4 = L; 3 = BAV*; 2 = ADB1; 1 = ADB0 + +---+---+---+---+ + | 8 | 7 | 6 | 5 | 8 = ADB3; 7 = ADB2; 6 = nc; 5 = HSK* + +---+---+---+---+ + + TODO: This is just a preliminary implementation to satisfy the operating + system. When completed we can hopefully emulate a Hexbus floppy and + use it in Extended Basic II which refuses to work with the PEB cards. + The Hexbus should then be designed as a slot device. + +****************************************************************************/ + +/* Status register bits */ +enum +{ + HSKWT = 0x80, + HSKRD = 0x40, + BAVIAS = 0x20, + BAVAIS = 0x10, + SBAV = 0x08, + WBUSY = 0x04, + RBUSY = 0x02, + SHSK = 0x01 +}; + +oso_device::oso_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, TI99_OSO, tag, owner, clock), m_data(0), m_status(0), m_control(0), m_xmit(0) +{ +} + +READ8_MEMBER( oso_device::read ) +{ + int value = 0; + offset &= 0x03; + switch (offset) + { + case 0: + // read 5FF8: read data register + if (TRACE_OSO) logerror("Read data register = %02x\n", value); + value = m_data; + break; + case 1: + // read 5FFA: read status register + value = m_status; + if (TRACE_OSO) logerror("Read status %02x\n", value); + break; + case 2: + // read 5FFC: read control register + value = m_control; + if (TRACE_OSO) logerror("Read control register = %02x\n", value); + break; + case 3: + // read 5FFE: read transmit register + value = m_xmit; + if (TRACE_OSO) logerror("Read transmit register = %02x\n", value); + break; + } + return value; +} + +WRITE8_MEMBER( oso_device::write ) +{ + offset &= 0x03; + switch (offset) + { + case 0: + // write 5FF8: write transmit register + if (TRACE_OSO) logerror("Write transmit register %02x\n", data); + m_xmit = data; + // We set the status register directly in order to prevent lock-ups + // until we have a complete Hexbus implementation + m_status |= HSKWT; + break; + case 1: + // write 5FFA: write control register + if (TRACE_OSO) logerror("Write control register %02x\n", data); + m_control = data; + break; + default: + // write 5FFC, 5FFE: undefined + if (TRACE_OSO) logerror("Invalid write on %04x: %02x\n", (offset<<1) | 0x5ff0, data); + break; + } +} + +void oso_device::device_start() +{ + logerror("Starting\n"); + m_status = m_xmit = m_control = m_data = 0; + save_item(NAME(m_data)); + save_item(NAME(m_status)); + save_item(NAME(m_control)); + save_item(NAME(m_xmit)); +} + +} } } // end namespace bus::ti99::internal + diff --git a/src/devices/bus/ti99/internal/998board.h b/src/devices/bus/ti99/internal/998board.h new file mode 100644 index 00000000000..d89e0a736e8 --- /dev/null +++ b/src/devices/bus/ti99/internal/998board.h @@ -0,0 +1,562 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + TI-99/8 main board logic + + This component implements the address decoder and mapper logic from the + TI-99/8 console. + + See 998board.c for documentation + + Michael Zapf + +*****************************************************************************/ + +#ifndef MAME_BUS_TI99_INTERNAL_998BOARD_H +#define MAME_BUS_TI99_INTERNAL_998BOARD_H + +#pragma once + +#include "bus/ti99/ti99defs.h" +#include "gromport.h" + +#include "bus/ti99/peb/peribox.h" +#include "machine/ram.h" +#include "machine/tmc0430.h" +#include "sound/sn76496.h" +#include "sound/tms5220.h" +#include "video/tms9928a.h" + +namespace bus { namespace ti99 { namespace internal { + +#define VAQUERRO_TAG "vaquerro" +#define MOFETTA_TAG "mofetta" +#define AMIGO_TAG "amigo" +#define OSO_TAG "oso" + +class mainboard8_device; + +/* + Custom chip: Vaquerro +*/ +class vaquerro_device : public device_t +{ +public: + vaquerro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void device_start() override; + void device_reset() override; + + line_state ready(); + void treset(); + + DECLARE_READ8_MEMBER( read ); + DECLARE_SETADDRESS_DBIN_MEMBER( set_address ); + + DECLARE_READ_LINE_MEMBER( sprd_out ); + DECLARE_READ_LINE_MEMBER( spwt_out ); + DECLARE_READ_LINE_MEMBER( sccs_out ); + DECLARE_READ_LINE_MEMBER( sromcs_out ); + + // Collective select line query + int gromcs_out(); + + DECLARE_READ_LINE_MEMBER( vdprd_out ); + DECLARE_READ_LINE_MEMBER( vdpwt_out ); + DECLARE_READ_LINE_MEMBER( lascsq_out ); + DECLARE_READ_LINE_MEMBER( ggrdy_out ); + DECLARE_WRITE_LINE_MEMBER( hold_cpu ); + + DECLARE_WRITE_LINE_MEMBER( crus_in ); + DECLARE_WRITE_LINE_MEMBER( crusgl_in ); + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( memen_in ); + + DECLARE_WRITE_LINE_MEMBER( sgmry ); + DECLARE_WRITE_LINE_MEMBER( tsgry ); + DECLARE_WRITE_LINE_MEMBER( p8gry ); + DECLARE_WRITE_LINE_MEMBER( p3gry ); + +private: + /* + Wait state generator (part of Vaquerro) + */ + class waitstate_generator + { + public: + waitstate_generator() : + m_counting(false), + m_generate(false), + m_counter(0), + m_addressed(true), + m_ready(true) + { + } + + virtual ~waitstate_generator() { } + void select_in(bool addressed); + virtual void ready_in(line_state ready) = 0; + virtual void clock_in(line_state clkout) = 0; + void treset_in(line_state reset); + + int select_out(); + void init(int select_value) { m_selvalue = select_value; } + + line_state ready_out(); + + bool is_counting(); + bool is_generating(); + bool is_ready(); + + protected: + // Two flipflops + bool m_counting; + bool m_generate; + // Counter + int m_counter; + + // Select value (indicates selected line) + int m_selvalue; + + // Line state flags + bool m_addressed; + bool m_ready; + }; + + class grom_waitstate_generator : public waitstate_generator + { + public: + void ready_in(line_state ready) override; + void clock_in(line_state clkout) override; + }; + + class video_waitstate_generator : public waitstate_generator + { + public: + void ready_in(line_state ready) override { } + void clock_in(line_state clkout) override; + }; + + // Memory cycle state + bool m_memen; + + // Waiting for video + bool m_video_wait; + + // State of the CRUS line + int m_crus; + + // Are the GROM libraries turned on? + bool m_crugl; + + // Do we have a logical address space match? + bool m_lasreq = false; + + // Keep the decoding result (opens the SRY gate) + bool m_grom_or_video = false; + + // Select lines + bool m_spwt; + bool m_sccs; + bool m_sromcs; + bool m_sprd; + bool m_vdprd; + bool m_vdpwt; + + // Collective GROM select state + int m_gromsel; + + // Outgoing READY + int m_ggrdy; + + // Outgoing READY latch (common flipflop driving SRY) + bool m_sry; + + // Holds the A14 address line state. We need this for the clock_in method. + int m_a14; + + // Keeps the recent DBIN level + int m_dbin_level; + + // Wait state logic components + grom_waitstate_generator m_sgmws, m_tsgws, m_p8gws, m_p3gws; + video_waitstate_generator m_vidws; + + // Pointer to mainboard + mainboard8_device* m_mainboard; +}; + +/* + Custom chip: Mofetta +*/ +class mofetta_device : public device_t +{ +public: + mofetta_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + void device_start() override; + void device_reset() override; + + DECLARE_WRITE8_MEMBER( cruwrite ); + DECLARE_SETADDRESS_DBIN_MEMBER( set_address ); + + // Debugger support + bool hexbus_access_debug(); + bool intdsr_access_debug(); + + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( msast_in ); + DECLARE_WRITE_LINE_MEMBER( lascs_in ); + DECLARE_WRITE_LINE_MEMBER( pmemen_in ); + DECLARE_WRITE_LINE_MEMBER( skdrcs_in ); + + DECLARE_READ8_MEMBER( rom1cs_out ); + DECLARE_READ_LINE_MEMBER( gromclk_out ); + + DECLARE_READ_LINE_MEMBER( alccs_out ); + DECLARE_READ_LINE_MEMBER( prcs_out ); + DECLARE_READ_LINE_MEMBER( cmas_out ); + DECLARE_READ_LINE_MEMBER( dbc_out ); + + DECLARE_READ_LINE_MEMBER( rom1cs_out ); + DECLARE_READ_LINE_MEMBER( rom1am_out ); + DECLARE_READ_LINE_MEMBER( rom1al_out ); + +private: + // Memory cycle state + bool m_pmemen; + + // Logical access + bool m_lasreq; + + // DRAM access + bool m_skdrcs; + + // Indicates the UP level of the GROMCLK + bool m_gromclk_up; + + // Have we got the upper word of the address? + bool m_gotfirstword; + + // Address latch + int m_address_latch; + + // Most significant byte of the 24-bit address + int m_prefix; + + // CRU select of the 1700 device + bool m_alcpg; + + // CRU select of the 2700 device + bool m_txspg; + + // ROM1 select lines + bool m_rom1cs; + bool m_rom1am; + bool m_rom1al; + + // OSO select + bool m_alccs; + + // Pascal ROM select line + bool m_prcs; + + // Cartridge port select line + bool m_cmas; + + // GROM clock count (as frequency divider) + int m_gromclock_count; + + // Remember last msast state for edge detection + int m_msast; + + // Pointer to mainboard + mainboard8_device* m_mainboard; +}; + +/* + Custom chip: Amigo +*/ +class amigo_device : public device_t +{ +public: + amigo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void device_start() override; + void device_reset() override; + + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + DECLARE_SETOFFSET_MEMBER( set_address ); + + // Debugger support + int get_physical_address_debug(offs_t offset); + void mapper_access_debug(int data); + + DECLARE_WRITE_LINE_MEMBER( srdy_in ); + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( crus_in ); + DECLARE_WRITE_LINE_MEMBER( lascs_in ); + DECLARE_WRITE_LINE_MEMBER( memen_in ); + + DECLARE_WRITE_LINE_MEMBER( holda_in ); + + DECLARE_READ_LINE_MEMBER( cpury_out ); + DECLARE_READ_LINE_MEMBER( sramcs_out ); + DECLARE_READ_LINE_MEMBER( skdrcs_out ); + + void connect_sram(uint8_t* sram) { m_sram = sram; } + bool mapper_accessed() { return m_mapper_accessed; } + +private: + // Memory cycle state + bool m_memen; + + // DMA methods for loading/saving maps + void mapper_load(); + void mapper_save(); + + // Address mapper registers. Each offset is selected by the first 4 bits + // of the logical address. + uint32_t m_base_register[16]; + + // Indicates a logical space access + bool m_logical_space; + + // Physical address + uint32_t m_physical_address; + + // Pointer to SRAM where AMIGO needs to upload/download its map values + uint8_t* m_sram; + + // Pointer to mainboard + mainboard8_device* m_mainboard; + + // Keep the system ready state + int m_srdy; + + // Outgoing READY level + int m_ready_out; + + // Keep the CRUS setting + int m_crus; + + // State of the address creation + int m_amstate; + + // Protection flags + int m_protflag; + + // Accessing SRAM + bool m_sram_accessed; + + // Accessing DRAM + bool m_dram_accessed; + + // Accessing the mapper + bool m_mapper_accessed; + + // HOLDA flag + bool m_hold_acknowledged; + + // Address in SRAM during DMA + uint32_t m_sram_address; + + // Number of the currently loaded/save base register + int m_basereg; + + // Latched value for mapper DMA transfer + uint32_t m_mapvalue; +}; + +/* + Custom chip: OSO +*/ +class oso_device : public device_t +{ +public: + oso_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + void device_start() override; + +private: + uint8_t m_data; + uint8_t m_status; + uint8_t m_control; + uint8_t m_xmit; +}; + +class mainboard8_device : public device_t +{ +public: + mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // Memory space + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + DECLARE_SETOFFSET_MEMBER( setoffset ); + + // Memory space for debugger access + DECLARE_READ8_MEMBER( debugger_read ); + DECLARE_WRITE8_MEMBER( debugger_write ); + + // I/O space + DECLARE_READ8Z_MEMBER( crureadz ); + DECLARE_WRITE8_MEMBER( cruwrite ); + + // Control lines + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( dbin_in ); + DECLARE_WRITE_LINE_MEMBER( msast_in ); + DECLARE_WRITE_LINE_MEMBER( crus_in ); + DECLARE_WRITE_LINE_MEMBER( ptgen_in ); + DECLARE_WRITE_LINE_MEMBER( reset_console ); + DECLARE_WRITE_LINE_MEMBER( hold_cpu ); + DECLARE_WRITE_LINE_MEMBER( ggrdy_in ); + + DECLARE_WRITE_LINE_MEMBER( holda_line ); + + template static devcb_base &set_ready_wr_callback(device_t &device, _Object object) + { + return downcast(device).m_ready.set_callback(object); + } + + template static devcb_base &set_reset_wr_callback(device_t &device, _Object object) + { + return downcast(device).m_console_reset.set_callback(object); + } + + template static devcb_base &set_hold_wr_callback(device_t &device, _Object object) + { + return downcast(device).m_hold_line.set_callback(object); + } + + void set_paddress(int address); + + // Ready lines from GROMs + DECLARE_WRITE_LINE_MEMBER( system_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( ptts_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( p8_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( p3_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( sound_ready ); + DECLARE_WRITE_LINE_MEMBER( speech_ready ); + DECLARE_WRITE_LINE_MEMBER( pbox_ready ); + + // Emulation + // void set_gromport(gromport_device* dev) { m_gromport = dev; } + +protected: + void device_start() override; + void device_reset() override; + machine_config_constructor device_mconfig_additions() const override; + +private: + // Holds the state of the A14 line + bool m_A14_set; + + // Propagates the end of the memory cycle + void cycle_end(); + + // Original logical address. + int m_logical_address; + + // Mapped physical address. + int m_physical_address; + + // Hold the address space value so that we can use it in other methods. + address_space* m_space; + + // Indicates that a byte is waiting on the data bus (see m_latched_data) + bool m_pending_write; + + // Hold the value of the data bus. In a real machine, the data bus continues + // to show that value, but in this emulation we have a push mechanism. + uint8_t m_latched_data; + + // Hold the level of the GROMCLK line + int m_gromclk; + + // Selecting GROM libraries + void select_groms(); + + // Previous select state + int m_prev_grom; + + // Ready states + bool m_speech_ready; + bool m_sound_ready; + bool m_pbox_ready; + + // Keeps the recent DBIN level + int m_dbin_level; + + // Ready line to the CPU + devcb_write_line m_ready; + + // Reset line to the main system + devcb_write_line m_console_reset; + + // Hold line to the main system + devcb_write_line m_hold_line; + + // Custom chips + required_device m_vaquerro; + required_device m_mofetta; + required_device m_amigo; + required_device m_oso; + + // More devices + required_device m_video; + required_device m_sound; + required_device m_speech; + required_device m_gromport; + required_device m_peb; + required_device m_sram; + required_device m_dram; + + // Debugging + int m_last_ready; + line_state m_crus_debug; + + // System GROM library + tmc0430_device* m_sgrom[3]; + + // Text-to-speech GROM library + tmc0430_device* m_tsgrom[8]; + + // Pascal 8 GROM library + tmc0430_device* m_p8grom[8]; + + // Pascal 3 GROM library + tmc0430_device* m_p3grom[3]; + + // Idle flags for GROMs + bool m_sgrom_idle; + bool m_tsgrom_idle; + bool m_p8grom_idle; + bool m_p3grom_idle; + + // ROM area of the system. + uint8_t* m_rom0; + uint8_t* m_rom1; + uint8_t* m_pascalrom; +}; + +} } } // end namespace bus::ti99::internal + +#define MCFG_MAINBOARD8_READY_CALLBACK(_write) \ + devcb = &bus::ti99::internal::mainboard8_device::set_ready_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MAINBOARD8_RESET_CALLBACK(_write) \ + devcb = &bus::ti99::internal::mainboard8_device::set_reset_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MAINBOARD8_HOLD_CALLBACK(_write) \ + devcb = &bus::ti99::internal::mainboard8_device::set_hold_wr_callback(*device, DEVCB_##_write); + +DECLARE_DEVICE_TYPE_NS(TI99_MAINBOARD8, bus::ti99::internal, mainboard8_device) +DECLARE_DEVICE_TYPE_NS(TI99_VAQUERRO, bus::ti99::internal, vaquerro_device) +DECLARE_DEVICE_TYPE_NS(TI99_MOFETTA, bus::ti99::internal, mofetta_device) +DECLARE_DEVICE_TYPE_NS(TI99_AMIGO, bus::ti99::internal, amigo_device) +DECLARE_DEVICE_TYPE_NS(TI99_OSO, bus::ti99::internal, oso_device) + +#endif // MAME_BUS_TI99_INTERNAL_998BOARD_H diff --git a/src/devices/bus/ti99/internal/datamux.cpp b/src/devices/bus/ti99/internal/datamux.cpp new file mode 100644 index 00000000000..d937c639c27 --- /dev/null +++ b/src/devices/bus/ti99/internal/datamux.cpp @@ -0,0 +1,645 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/*************************************************************************** + + TI-99/4(A) databus multiplexer circuit + + The DMUX is used to convert the 16-bit databus of the TMS9900 into + an 8-bit databus. The processor writes a 16 bit word which is split + by this circuit into two bytes that are sent subsequently over the 8-bit bus. + In the opposite direction, one 16-bit read request from the CPU is + translated into two 8-bit read requests (odd address / even address) from + this datamux. Its 8-bit latch (LS373) holds the first (odd address) byte, + while the datamux puts the CPU on hold, gets the second byte, + and routes that second byte to the D0-D7 lines, while the latch now puts + the first byte on D8-D15. Since we get two memory accesses each time, + there are twice as many wait states than for a direct 16-bit access + (order LSB, MSB). + + In addition, since the TMS 9900 also supports byte operations, all write + operations are automatically preceded by a read operation, so this adds even + more delays. + + Within the TI-99/4(A) console, only the internal ROM and the small internal + RAM ("scratch pad RAM") are directly connected to the 16-bit bus. All other + devices (video, audio, speech, GROM, and the complete P-Box system are + connected to the datamux. + + The TMS9995 which is used in the Geneve has an internal multiplex, and + the byte order is reversed: MSB, LSB + + ROM = 4K * 16 bit (8 KiB) system ROM (kind of BIOS, plus the GPL interpreter) + RAM = 128 * 16 bit (256 byte) system RAM ("scratch pad") + + Many users (me too) used to solder a 16K * 16 bit (32 KiB) SRAM circuit into + the console, before the datamux, decoded to 0x2000-0x3fff and 0xa000-0xffff. + (This expansion was also called 0-waitstate, since it could be accessed + with the full databus width, and the datamux did not create waitstates.) + + +---+ +-------+ + | |===##========##== D0-D7 ==========##===============|TMS9918| Video + | | || || || +-------+ + | T | +-----+ +-----+ LS245 +----+ + | M | | ROM | | RAM | +----+ + | S | +-----+ +-----+ || | : + | |---||-||-----||-||----------------||-|---------------------: + | 9 | || || A0 - A14 || | A0 : Sound + | 9 |---||--------||-------------------||-|----------+ -A15 : GROM + | 0 | || || LS373 +-+ || | +----A15-+----------: Cartridges + | 0 | || || ##========|<|===## | | : Speech + | | || || || +-+ +-+ || | | : Expansion + | |===## D8-D15 ##===##==|>|=====|===##=|=|=========== D0-D7 =: cards + +---+ +-+ | | | : + ^ LS244| | | | + | | +--+---+-++ + | +--------| DMUX |---------------<--: READY + +--- READY -------------------------+------+ + + Databus width + :------------- 16 bit ---------------|---------- 8 bit -----: + + A0=MSB; A15=LSB + D0=MSB; D15=LSB + + We integrate the 16 bit memory expansion in this datamux component + (pretending that the memory expansion was soldered on top of the datamux) + + January 2012: Rewritten as class + +***************************************************************************/ + +#include "emu.h" +#include "datamux.h" + +DEFINE_DEVICE_TYPE_NS(TI99_DATAMUX, bus::ti99::internal, datamux_device, "ti99_datamux", "TI-99 Databus multiplexer") + +namespace bus { namespace ti99 { namespace internal { + +/* + Constructor +*/ +datamux_device::datamux_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, TI99_DATAMUX, tag, owner, clock), + m_video(*owner, VDP_TAG), + m_sound(*owner, TISOUNDCHIP_TAG), + m_peb(*owner, PERIBOX_TAG), + m_gromport(*owner, GROMPORT_TAG), + m_ram16b(*owner, EXPRAM_TAG), + m_padram(*owner, PADRAM_TAG), + m_spacep(nullptr), + m_ready(*this), + m_addr_buf(0), + m_dbin(CLEAR_LINE), + m_muxready(CLEAR_LINE), + m_sysready(CLEAR_LINE), + m_latch(0), + m_waitcount(0), + m_use32k(false), + m_base32k(0), + m_console_groms_present(false), + m_grom_idle(true) +{ +} + +#define TRACE_READY 0 +#define TRACE_ACCESS 0 +#define TRACE_ADDRESS 0 +#define TRACE_WAITCOUNT 0 +#define TRACE_SETUP 0 + +/*************************************************************************** + DEVICE ACCESSOR FUNCTIONS +***************************************************************************/ + +void datamux_device::read_all(address_space& space, uint16_t addr, uint8_t *value) +{ + // Valid access + bool validaccess = ((addr & 0x0400)==0); + + if (validaccess) + { + // GROM access + if ((addr & 0xf801)==0x9800) + { + if (m_console_groms_present) + { + for (int i=0; i < 3; i++) + { + m_grom[i]->readz(space, addr, value); + } + } + // GROMport (GROMs) + m_gromport->readz(space, addr, value); + m_grom_idle = false; + } + + // Video + if ((addr & 0xf801)==0x8800) + { + // Forward to VDP unless we have an EVPC + if (m_video != nullptr) *value = m_video->read(space, addr>>1); // A14 determines data or register read + } + } + + // GROMport (ROMs) + if ((addr & 0xe000)==0x6000) m_gromport->readz(space, addr, value); + + // PEB gets all accesses + m_peb->readz(space, addr, value); + m_peb->memen_in(CLEAR_LINE); +} + +void datamux_device::write_all(address_space& space, uint16_t addr, uint8_t value) +{ + // GROM access + if ((addr & 0xf801)==0x9800) + { + if (m_console_groms_present) + { + for (int i=0; i < 3; i++) + m_grom[i]->write(space, addr, value); + } + // GROMport + m_gromport->write(space, addr, value); + m_grom_idle = false; + } + + // Cartridge port and sound + if ((addr & 0xe000)==0x6000) m_gromport->write(space, addr, 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) + { + // Forward to VDP unless we have an EVPC + if (m_video != nullptr) m_video->write(space, addr>>1, value); // A14 determines data or register write + } + + // PEB gets all accesses + m_peb->write(space, addr, value); + m_peb->memen_in(CLEAR_LINE); +} + +void datamux_device::setaddress_all(address_space& space, uint16_t addr) +{ + line_state a14 = ((addr & 2)!=0)? ASSERT_LINE : CLEAR_LINE; + + // Valid access = not(DBIN and A5) + bool validaccess = (m_dbin==CLEAR_LINE || (addr & 0x0400)==0); + + // GROM access + bool isgrom = ((addr & 0xf801)==0x9800) && validaccess; + + // Cartridge ROM + bool iscartrom = ((addr & 0xe000)==0x6000); + + // Always deliver to GROM so that the select line may be cleared + int lines = (m_dbin==ASSERT_LINE)? 1 : 0; + if (a14==ASSERT_LINE) lines |= 2; + line_state select = isgrom? ASSERT_LINE : CLEAR_LINE; + + if (select) m_grom_idle = false; + + if (m_console_groms_present) + for (int i=0; i < 3; i++) + m_grom[i]->set_lines(space, lines, select); + + // GROMport (GROMs) + m_gromport->set_gromlines(space, lines, select); + + // Sound chip and video chip do not require the address to be set before access + + // GROMport (ROMs) + m_gromport->romgq_line(iscartrom? ASSERT_LINE : CLEAR_LINE); + + // PEB gets all accesses + m_peb->memen_in(ASSERT_LINE); + m_peb->setaddress_dbin(space, addr, m_dbin); +} + +/* + Special debugger access. The access is similar to the normal access, + but it bypasses the wait state circuitry. Also, access ports of memory- + mapped devices are excluded because their state would be changed + unpredictably by the debugger access. +*/ +uint16_t datamux_device::debugger_read(address_space& space, uint16_t addr) +{ + uint16_t addrb = addr << 1; + uint16_t value = 0; + + if ((addrb & 0xe000)==0x0000) value = m_consolerom[(addrb & 0x1fff)>>1]; + else + { + if ((addrb & 0xfc00)==0x8000) + value = (m_padram->pointer()[addrb & 0x00ff] << 8) | m_padram->pointer()[(addrb & 0x00ff)+1]; + else + { + int base32k = 0; + if (m_use32k) + { + if ((addrb & 0xe000)==0x2000) base32k = 0x2000; + if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000; + } + + if (base32k != 0) + { + value = (m_ram16b->pointer()[addrb-base32k] << 8) | m_ram16b->pointer()[addrb-base32k+1]; + } + else + { + uint8_t lval = 0; + uint8_t hval = 0; + + if ((addrb & 0xe000)==0x6000) + { + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->readz(space, addrb+1, &lval); + m_gromport->readz(space, addrb, &hval); + m_gromport->romgq_line(CLEAR_LINE); + } + m_peb->memen_in(ASSERT_LINE); + m_peb->readz(space, addrb+1, &lval); + m_peb->readz(space, addrb, &hval); + m_peb->memen_in(CLEAR_LINE); + value = ((hval << 8)&0xff00) | (lval & 0xff); + } + } + } + return value; +} + +void datamux_device::debugger_write(address_space& space, uint16_t addr, uint16_t data) +{ + uint16_t addrb = addr << 1; + + if ((addrb & 0xe000)==0x0000) return; + + if ((addrb & 0xfc00)==0x8000) + { + m_padram->pointer()[addrb & 0x00ff] = data >> 8; + m_padram->pointer()[(addrb & 0x00ff)+1] = data & 0xff; + } + else + { + int base32k = 0; + if (m_use32k) + { + if ((addrb & 0xe000)==0x2000) base32k = 0x2000; + if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000; + } + + if (base32k != 0) + { + m_ram16b->pointer()[addrb-base32k] = data >> 8; + m_ram16b->pointer()[(addrb-base32k)+1] = data & 0xff; + } + else + { + if ((addrb & 0xe000)==0x6000) + { + m_gromport->romgq_line(ASSERT_LINE); + m_gromport->write(space, addr+1, data & 0xff); + m_gromport->write(space, addr, (data>>8) & 0xff); + m_gromport->romgq_line(CLEAR_LINE); + } + + m_peb->memen_in(ASSERT_LINE); + m_peb->write(space, addr+1, data & 0xff); + m_peb->write(space, addr, (data>>8) & 0xff); + m_peb->memen_in(CLEAR_LINE); + } + } +} + +/* + Read access. We are using two loops because the delay between both + accesses must not occur within the loop. So we have one access on the bus, + a delay, and then the second access. + + mem_mask is always ffff on TMS processors (cannot control bus width) +*/ +READ16_MEMBER( datamux_device::read ) +{ + uint16_t value = 0; + + // Care for debugger + if (machine().side_effect_disabled()) + { + return debugger_read(space, offset); + } + + // Addresses below 0x2000 are ROM (no wait states) + if ((m_addr_buf & 0xe000)==0x0000) + { + value = m_consolerom[(m_addr_buf & 0x1fff)>>1]; + } + else + { + // Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM (no wait states) + if ((m_addr_buf & 0xfc00)==0x8000) + { + value = (m_padram->pointer()[m_addr_buf & 0x00ff] << 8) | m_padram->pointer()[(m_addr_buf & 0x00ff)+1]; + } + else + { + // Looks ugly, but this is close to the real thing. If the 16bit + // memory expansion is installed in the console, and the access hits its + // space, just respond to the memory access and don't bother the + // datamux in any way. In particular, do not make the datamux insert wait + // states. + + if (m_base32k != 0) + { + value = (m_ram16b->pointer()[m_addr_buf-m_base32k] << 8) | m_ram16b->pointer()[(m_addr_buf-m_base32k)+1]; + } + else + { + // The byte from the odd address has already been read into the latch + // Reading the even address now (addr) + uint8_t hbyte = 0; + read_all(space, m_addr_buf, &hbyte); + if (TRACE_ACCESS) logerror("Read even byte from address %04x -> %02x\n", m_addr_buf, hbyte); + + value = (hbyte<<8) | m_latch; + } + } + } + return value; +} + +/* + Write access. +*/ +WRITE16_MEMBER( datamux_device::write ) +{ + if (machine().side_effect_disabled()) + { + debugger_write(space, offset, data); + return; + } + + // Addresses below 0x2000 are ROM + if ((m_addr_buf & 0xe000)==0x0000) + { + return; + } + + // Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM + if ((m_addr_buf & 0xfc00)==0x8000) + { + m_padram->pointer()[(m_addr_buf & 0x00ff)] = data >> 8; + m_padram->pointer()[(m_addr_buf & 0x00ff)+1] = data & 0xff; + return; + } + + // Handle the internal 32K expansion + if (m_base32k != 0) + { + m_ram16b->pointer()[(m_addr_buf-m_base32k)] = data >> 8; + m_ram16b->pointer()[(m_addr_buf-m_base32k)+1] = data & 0xff; + } + 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 + if (TRACE_ACCESS) logerror("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( datamux_device::setoffset ) +{ + m_addr_buf = offset << 1; + m_waitcount = 0; + + if (TRACE_ADDRESS) logerror("set address %04x\n", m_addr_buf); + + if ((m_addr_buf & 0xe000) == 0x0000) + { + return; // console ROM + } + + if ((m_addr_buf & 0xfc00) == 0x8000) + { + return; // console RAM + } + + // Initialize counter + // 1 cycle for loading into the datamux + // 2 subsequent wait states (LSB) + // 2 subsequent wait states (MSB) + // clock cycle 6 is the nominal follower of the last wait state + m_waitcount = 5; + + m_base32k = 0; + if (m_use32k) + { + if ((m_addr_buf & 0xe000)==0x2000) m_base32k = 0x2000; + if (((m_addr_buf & 0xe000)==0xa000) || ((m_addr_buf & 0xc000)==0xc000)) m_base32k = 0x8000; + } + + // Suspend the CPU if not using the 32K + if (m_base32k == 0) + { + // propagate the setaddress operation + // First the odd address + setaddress_all(space, m_addr_buf+1); + m_muxready = CLEAR_LINE; + ready_join(); + } + else m_waitcount = 0; +} + +/* + The datamux is connected to the clock line in order to operate + the wait state counter and to read/write the bytes. +*/ +WRITE_LINE_MEMBER( datamux_device::clock_in ) +{ + // return immediately if the datamux is currently inactive + if (m_waitcount>0) + { + if (TRACE_WAITCOUNT) logerror("datamux: wait count %d\n", m_waitcount); + if (m_sysready==CLEAR_LINE) + { + if (TRACE_READY) 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 = ASSERT_LINE; + ready_join(); + } + if (m_waitcount==2) + { + // read odd byte + read_all(*m_spacep, m_addr_buf+1, &m_latch); + if (TRACE_ACCESS) logerror("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 (state==ASSERT_LINE) + { // raising edge + if (--m_waitcount==0) + { + m_muxready = ASSERT_LINE; + ready_join(); + } + } + 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 (TRACE_ACCESS) logerror("datamux: write even byte to address %04x <- %02x\n", m_addr_buf, m_latch); + write_all(*m_spacep, m_addr_buf, m_latch); + } + } + } + } +} + +/* + Combine the external (sysready) and the own (muxready) READY states. +*/ +void datamux_device::ready_join() +{ + m_ready((m_sysready==CLEAR_LINE || m_muxready==CLEAR_LINE)? CLEAR_LINE : ASSERT_LINE); +} + +WRITE_LINE_MEMBER( datamux_device::dbin_in ) +{ + m_dbin = (line_state)state; + if (TRACE_ADDRESS) logerror("data bus in = %d\n", (m_dbin==ASSERT_LINE)? 1:0 ); +} + +WRITE_LINE_MEMBER( datamux_device::ready_line ) +{ + if (TRACE_READY) + { + if (state != m_sysready) logerror("READY line from PBox = %d\n", state); + } + m_sysready = (line_state)state; + // Also propagate to CPU via driver + ready_join(); +} + +/* Called from VDP via console. */ +WRITE_LINE_MEMBER( datamux_device::gromclk_in ) +{ + // Don't propagate the clock in idle phase + if (m_grom_idle) return; + + // Propagate to the GROMs + if (m_console_groms_present) + { + for (int i=0; i < 3; i++) m_grom[i]->gclock_in(state); + m_grom_idle = m_grom[0]->idle(); + } + m_gromport->gclock_in(state); + + // Only ask the gromport when we don't have GROMs in the console + if (!m_console_groms_present) + m_grom_idle = m_gromport->is_grom_idle(); +} + +/*************************************************************************** + DEVICE LIFECYCLE FUNCTIONS +***************************************************************************/ + +void datamux_device::device_start(void) +{ + m_muxready = ASSERT_LINE; + m_ready.resolve(); + + // Register persistable state variables + save_item(NAME(m_addr_buf)); + save_item(NAME(m_dbin)); + save_item(NAME(m_muxready)); + save_item(NAME(m_sysready)); + save_item(NAME(m_latch)); + save_item(NAME(m_waitcount)); + save_item(NAME(m_use32k)); + save_item(NAME(m_base32k)); + save_item(NAME(m_console_groms_present)); + save_item(NAME(m_grom_idle)); +} + +void datamux_device::device_stop(void) +{ +} + +void datamux_device::device_reset(void) +{ + m_consolerom = (uint16_t*)owner()->memregion(CONSOLEROM)->base(); + m_use32k = (ioport("RAM")->read()==1); + m_console_groms_present = (ioport("GROMENA")->read()==1); + + m_sysready = ASSERT_LINE; + m_muxready = ASSERT_LINE; + ready_join(); + + m_waitcount = 0; + m_latch = 0; + + m_dbin = CLEAR_LINE; + + // Get the pointer to the address space already here, because we cannot + // save that pointer to a savestate, and we need it on restore + cpu_device* cpu = downcast(machine().device("maincpu")); + m_spacep = &cpu->space(AS_PROGRAM); +} + +void datamux_device::device_config_complete() +{ + m_grom[0] = downcast(owner()->subdevice(GROM0_TAG)); + m_grom[1] = downcast(owner()->subdevice(GROM1_TAG)); + m_grom[2] = downcast(owner()->subdevice(GROM2_TAG)); +} + + +INPUT_PORTS_START( datamux ) + PORT_START( "RAM" ) /* config */ + PORT_CONFNAME( 0x01, 0x00, "Console 32 KiB RAM upgrade (16 bit)" ) + PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) + PORT_CONFSETTING( 0x01, DEF_STR( On ) ) + + PORT_START( "GROMENA" ) + PORT_CONFNAME( 0x01, 0x01, "Console GROMs" ) + PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) + PORT_CONFSETTING( 0x01, DEF_STR( On ) ) + +INPUT_PORTS_END + +ioport_constructor datamux_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(datamux); +} + +} } } // end namespace bus::ti99::internal + diff --git a/src/devices/bus/ti99/internal/datamux.h b/src/devices/bus/ti99/internal/datamux.h new file mode 100644 index 00000000000..3231f72297d --- /dev/null +++ b/src/devices/bus/ti99/internal/datamux.h @@ -0,0 +1,146 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + TI-99/4(A) databus multiplexer circuit + See datamux.c for documentation + + Michael Zapf + + February 2012: Rewritten as class + +*****************************************************************************/ + +#ifndef MAME_BUS_TI99_INTERNAL_DATAMUX_H +#define MAME_BUS_TI99_INTERNAL_DATAMUX_H + +#pragma once + +#include "bus/ti99/ti99defs.h" +#include "machine/tmc0430.h" +#include "gromport.h" +#include "bus/ti99/peb/peribox.h" +#include "sound/sn76496.h" +#include "video/tms9928a.h" +#include "machine/ram.h" + +namespace bus { namespace ti99 { namespace internal { + +/* + Main class +*/ +class datamux_device : public device_t +{ +public: + datamux_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_READ16_MEMBER( read ); + DECLARE_WRITE16_MEMBER( write ); + DECLARE_SETOFFSET_MEMBER( setoffset ); + + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( dbin_in ); + DECLARE_WRITE_LINE_MEMBER( ready_line ); + + DECLARE_WRITE_LINE_MEMBER( gromclk_in ); + + template static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) + { + return downcast(device).m_ready.set_callback(std::forward(cb)); + } + +protected: + /* Constructor */ + void device_start() override; + void device_stop() override; + void device_reset() override; + void device_config_complete() override; + ioport_constructor device_input_ports() const override; + +private: + // Link to the video processor + optional_device m_video; + + // Link to the sound processor + optional_device m_sound; + + // Link to the peripheral expansion box + required_device m_peb; + + // Link to the cartridge port (aka GROM port) + required_device m_gromport; + + // Memory expansion (internal, 16 bit) + required_device m_ram16b; + + // Console RAM + required_device m_padram; + + // Keeps the address space pointer + address_space* m_spacep; + + // Console ROM + uint16_t* m_consolerom; + + // Console GROMs + tmc0430_device* m_grom[3]; + + // Common read routine + void read_all(address_space& space, uint16_t addr, uint8_t *target); + + // Common write routine + void write_all(address_space& space, uint16_t addr, uint8_t value); + + // Common set address method + void setaddress_all(address_space& space, uint16_t addr); + + // Debugger access + uint16_t debugger_read(address_space& space, uint16_t addr); + void debugger_write(address_space& space, uint16_t addr, uint16_t data); + + // Join own READY and external READY + void ready_join(); + + // Ready line to the CPU + devcb_write_line m_ready; + + // Address latch (emu). In reality, the address bus remains constant. + uint16_t m_addr_buf; + + // DBIN line + int m_dbin; + + // Own ready state. + int m_muxready; + + // Ready state. Needed to control wait state generation via inbound READY + int m_sysready; + + // Latch which stores the first (odd) byte + uint8_t m_latch; + + // Counter for the wait states. + int m_waitcount; + + // Use the memory expansion? + bool m_use32k; + + // Memory base for piggy-back 32K expansion. If 0, expansion is not used. + uint16_t m_base32k; + + // Console GROMs are available (the HSGPL expects them to be removed) + bool m_console_groms_present; + + // GROMs are idle, no need to propagate the clock + bool m_grom_idle; +}; + +/******************************************************************************/ + +#define MCFG_DMUX_READY_HANDLER( _intcallb ) \ + devcb = &bus::ti99::internal::datamux_device::static_set_ready_callback( *device, DEVCB_##_intcallb ); + +} } } // end namespace bus::ti99::internal + +DECLARE_DEVICE_TYPE_NS(TI99_DATAMUX, bus::ti99::internal, datamux_device) + +#endif // MAME_BUS_TI99_INTERNAL_DATAMUX_H diff --git a/src/devices/bus/ti99/internal/evpcconn.cpp b/src/devices/bus/ti99/internal/evpcconn.cpp new file mode 100644 index 00000000000..5aba788a9fe --- /dev/null +++ b/src/devices/bus/ti99/internal/evpcconn.cpp @@ -0,0 +1,45 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + Connector from EVPC + + We need this for the TI-99/4A console; the SGCPU uses a separate line + in the PEB. + + 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). + + May 2017, Michael Zapf + +****************************************************************************/ + +#include "emu.h" +#include "evpcconn.h" + +DEFINE_DEVICE_TYPE_NS(TI99_EVPCCONN, bus::ti99::internal, evpc_clock_connector, "ti99_evpc_clock", "EVPC clock connector") + +namespace bus { namespace ti99 { namespace internal { + +evpc_clock_connector::evpc_clock_connector(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, TI99_EVPCCONN, tag, owner, clock), + m_vdpint(*this) +{ +} + +WRITE_LINE_MEMBER( evpc_clock_connector::vclock_line ) +{ + m_vdpint(state); +} + +void evpc_clock_connector::device_start() +{ + m_vdpint.resolve(); +} +} } } // end namespace bus::ti99::internal + diff --git a/src/devices/bus/ti99/internal/evpcconn.h b/src/devices/bus/ti99/internal/evpcconn.h new file mode 100644 index 00000000000..2c8e4bc9a40 --- /dev/null +++ b/src/devices/bus/ti99/internal/evpcconn.h @@ -0,0 +1,45 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + Connector from EVPC + For details see evpcconn.cpp + +****************************************************************************/ +#ifndef MAME_BUS_TI99_INTERNAL_EVPCCONN_H +#define MAME_BUS_TI99_INTERNAL_EVPCCONN_H + +#pragma once + +#include "emu.h" +#include "bus/ti99/ti99defs.h" + +namespace bus { namespace ti99 { namespace internal { + +class evpc_clock_connector : public device_t +{ +public: + evpc_clock_connector(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template static devcb_base &static_set_vdpint_callback(device_t &device, _Object object) + { + return downcast(device).m_vdpint.set_callback(object); + } + + DECLARE_WRITE_LINE_MEMBER( vclock_line ); + void device_start() override; + +private: + // VDPINT line to the CPU + devcb_write_line m_vdpint; +}; + +} } } // end namespace bus::ti99::internal + +DECLARE_DEVICE_TYPE_NS(TI99_EVPCCONN, bus::ti99::internal, evpc_clock_connector) + +#define MCFG_ADD_EVPC_CONNECTOR( _tag, _vdpint ) \ + MCFG_DEVICE_ADD(_tag, TI99_EVPCCONN, 0) \ + devcb = &bus::ti99::internal::evpc_clock_connector::static_set_vdpint_callback( *device, DEVCB_##_vdpint ); + +#endif // MAME_BUS_TI99_INTERNAL_EVPCCONN_H diff --git a/src/devices/bus/ti99/internal/genboard.cpp b/src/devices/bus/ti99/internal/genboard.cpp new file mode 100644 index 00000000000..003ba81e08f --- /dev/null +++ b/src/devices/bus/ti99/internal/genboard.cpp @@ -0,0 +1,2015 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/*************************************************************************** + Geneve 9640 mapper and more components + + This file contains 2 classes: + - mapper: main function of the Gate Array on the Geneve board. Maps logical + memory accesses to a wider address space using map registers. + - keyboard: an implementation of a XT-style keyboard. This should be dropped + and replaced by a proper XT keyboard implementation. + + Onboard SRAM configuration: + There is an adjustable SRAM configuration on board, representing the + various enhancements by users. + + The standard memory configuration as reported by chkdsk (32 KiB): + 557056 bytes of total memory + + With 64 KiB SRAM: + 589824 bytes of total memory + + With 384 KiB SRAM: + 917504 bytes of total memory + + The original 32 KiB SRAM memory needs to be expanded to 64 KiB for + MDOS 2.50s and higher, or the system will lock up. Therefore the emulation + default is 64 KiB. + + The ultimate expansion is a 512 KiB SRAM circuit wired to the gate array + to provide 48 pages of fast static RAM. This also requires to build an + adapter for a larger socket. From the 512 KiB, only 384 KiB will be + accessed, since the higher pages are hidden behind the EPROM pages. + + === Address map === + p,q = page value bit (q = AMC, AMB, AMA) + c = address offset within 8 KiB page + + p pqqq pppc cccc cccc cccc + + 0 0... .... .... .... .... on-board dram 512 KiB + + 0 1... .... .... .... .... on-board future expansion 512 KiB or Memex with Genmod + + 1 00.. .... .... .... .... p-box AMA=0 (256 KiB) + 1 010. .... .... .... .... p-box AMA=1 AMB=0 (128 KiB) + 1 0110 .... .... .... .... p-box AMA=1 AMB=1 AMC=0 (64 KiB) + + 1 0111 00.. .... .... .... p-box address block 0xxx, 2xxx + 1 0111 010. .... .... .... p-box address block 4xxx (DSR) + 1 0111 011. .... .... .... p-box address block 6xxx + 1 0111 100. .... .... .... p-box address block 8xxx (Speech at 0x9000) + 1 0111 101. .... .... .... p-box address block axxx + 1 0111 11.. .... .... .... p-box address block cxxx, exxx + + 1 100. .... .... .... .... on-board sram (128K) -\ + 1 101. .... .... .... .... on-board sram (128K) --+- maximum SRAM expansion + 1 1100 .... .... .... .... on-board sram (64K) --/ + 1 1101 0... .... .... .... on-board sram (32K) - additional 32 KiB required for MDOS 2.50s and higher + 1 1101 1... .... .... .... on-board sram (32K) - standard setup + + 1 111. ..0. .... .... .... on-board boot1 + 1 111. ..1. .... .... .... on-board boot2 + + The TI console (or more precise, the Flex Cable Interface) sets the AMA/B/C + lines to 1. Most cards actually check for AMA/B/C=1. However, this decoding + was forgotten in third party cards which cause the card address space + to be mirrored. The usual DSR space at 4000-5fff which would be reachable + via page 0xba is then mirrored on a number of other pages: + + 10 xxx 010x = 82, 8a, 92, 9a, a2, aa, b2, ba + + Another block to take care of is 0xbc which covers 8000-9fff since this + area contains the speech synthesizer port at 9000/9400. + + For the standard Geneve, only prefix 10 is routed to the P-Box. The Genmod + modification wires these address lines to pins 8 and 9 in the P-Box as AMD and + AME. This requires all cards to be equipped with an additional selection logic + to detect AMD=0, AME=1. Otherwise these cards, although completely decoding the + 19-bit address, would reappear at 512 KiB distances. + + Genmod's double switch box is also emulated. There are two switches: + - Turbo mode: Activates or deactivates the wait state logic on the Geneve + board. This switch may be changed at any time. + - TI mode: Selects between the on-board memory, which is obviously required + for the GPL interpreter, and the external Memex memory. This switch + triggers a reset when changed. + + + =================== + Mapping + =================== + + Logical address space: 64 KiB + + Geneve mode: + + Video: F100, F102, F104, F106 (mirror: +8) + Mapper: F110 - F117 + Keyboard: F118 + Clock: F130 - F13F + Sound: F120 + + TI mode: + + Video: 8800, 8802, 8804, 8806 + Mapper: 8000 - 8007 + Keyboard: 8008 - 800F + Clock: 8010 - 801F + Speech: 9000 / 9400 + Grom: 9800 / 9802 + + Physical address space: 2 MiB + + Start End Banks + 000000 - 07FFFF 00-3F 512 KiB DRAM on-board + 080000 - 0FFFFF 40-7F 512 KiB on-board expansion (never used) + 100000 - 16FFFF 80-B7 448 KiB P-Box space (special cards, like MEMEX) + 170000 - 17FFFF B8-BF 64 KiB P-Box space (current cards) + 180000 - 1DFFFF C0-EF 384 KiB SRAM space on-board; stock Geneve comes with 32 KiB + 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors + + + GenMod modification: + + TI mode + 000000 - 07FFFF 00-3F 512 KiB DRAM on-board + 080000 - 1DFFFF 40-EF 1408 KiB P-Box space + 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors + + Non-TI mode + 000000 - 1DFFFF 00-EF 1920 KiB P-Box space + 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors + + Waitstate handling + ------------------ + Waitstates are caused by a cleared READY line of the TMS9995 processor + during an external memory cycle. That means that waitstates have no effect + for operations within the on-chip memory, and only when an access to the + external memory or other devices occurs, a delay will be noticed. + + The waitstates are generated by the custom Gate Array chip on the board + and the PAL 16R4, both lacking proper documentation. All of the following + numbers have been determined by experiments with the real machine. + + Waitstates are generated for: + - memory-mapped devices (mapper, clock, keyboard): 1 WS + - accesses to the peripheral expansion box: 1 WS + - accesses to on-board DRAM: 1 WS + - accesses to video: 15 WS + - accesses to sound: ~25 WS + - accesses to SRAM: 0 WS + + Additional waitstates are created when one of the CRU bits is set. In that + case, all delays are extended to 2 WS (including SRAM). + + Sound waitstates are somewhat unpredictable. It seems as if they depend + on the clock of the sound chip; the theory is that the READY line is + pulled down until the next clock pulse, which may take some value between + 18 CPU cycles and 30 CPU cycles. + + The gate array is able to create wait states for video accesses. However, + these wait states are effective after the video access has been completed. + Wait states are not effective when the execution is running in on-chip + RAM. Additional wait states are requested by m_video_waitstates = true. + Without additional wait states, the video access takes the usual 1 or 2 WS. + + Waitstate behavior (Nov 2013) + Almost perfect. Only video read access from code in DRAM is too fast by one WS + + ========================== + PFM expansion + ========================== + + The "Programmable Flash Memory expansion" is a replacement for the boot + EPROM. + + PFM: Original version, 128 KiB + PFM+: Expansion of the original version, piggybacked, adds another 128KiB + PFM512: Using an AT29C040 (not A), 512 KiB + + The PFM is visible as four banks in memory pages 0xF0 - 0xFF. + + Bank switching is done by four 9901 pins: + + 0028: LSB of bank number + 003A: MSB of bank number + + Bank 0 is the boot code, while banks 1-3 can be used as flash drives + + Michael Zapf, October 2011 + February 2012: rewritten as class, restructured + Aug 2015: PFM added + +***************************************************************************/ + +#include "emu.h" +#include "genboard.h" + +DEFINE_DEVICE_TYPE_NS(GENEVE_KEYBOARD, bus::ti99::internal, geneve_keyboard_device, "geneve_keyboard", "Geneve XT-style keyboard") +DEFINE_DEVICE_TYPE_NS(GENEVE_MAPPER, bus::ti99::internal, geneve_mapper_device, "geneve_mapper", "Geneve Gate Array") + +namespace bus { namespace ti99 { namespace internal { + +#define TRACE_READ 0 +#define TRACE_WRITE 0 +#define TRACE_DETAIL 0 +#define TRACE_KEYBOARD 0 +#define TRACE_CLOCK 0 +#define TRACE_LINES 0 +#define TRACE_SETTING 1 +#define TRACE_VIDEOWS 0 +#define TRACE_PFM 0 + +#define SRAM_PAR_TAG ":sram" +#define DRAM_PAR_TAG ":dram" + +geneve_mapper_device::geneve_mapper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, GENEVE_MAPPER, tag, owner, clock), m_gromwaddr_LSB(false), + m_gromraddr_LSB(false), + m_grom_address(0), + m_video_waitstates(false), + m_extra_waitstates(false), + m_ready_asserted(false), + m_read_mode(false), + m_debug_no_ws(false), + m_geneve_mode(false), + m_direct_mode(false), + m_cartridge_size(0), + m_cartridge_secondpage(false), + m_cartridge6_writable(false), + m_cartridge7_writable(false), + m_turbo(false), + m_genmod(false), + m_timode(false), + m_pfm_mode(0), + m_pfm_bank(0), + m_pfm_output_enable(false), + m_sram_mask(0), + m_sram_val(0), + m_ready(*this), + m_waitcount(0), + m_ext_waitcount(0), + m_clock(*owner, GCLOCK_TAG), + m_cpu(*owner, "maincpu"), + m_pfm512(*owner, PFM512_TAG), + m_pfm512a(*owner, PFM512A_TAG), + m_sound(*owner, TISOUNDCHIP_TAG), + m_keyboard(*owner, GKEYBOARD_TAG), + m_video(*owner, VDP_TAG), + m_peribox(*owner, PERIBOX_TAG), + m_sram(*this, SRAM_PAR_TAG), + m_dram(*this, DRAM_PAR_TAG) +{ + m_eprom = nullptr; +} + +INPUT_CHANGED_MEMBER( geneve_mapper_device::settings_changed ) +{ + int number = (int)((uint64_t)param&0x03); + int value = newval; + + switch (number) + { + case 1: + // Turbo switch. May be changed at any time. + if (TRACE_SETTING) logerror("Setting turbo flag to %d\n", value); + m_turbo = (value!=0); + break; + case 2: + // TIMode switch. Causes reset when changed. + if (TRACE_SETTING) logerror("Setting timode flag to %d\n", value); + m_timode = (value!=0); + machine().schedule_hard_reset(); + break; + case 3: + // Used when switching the boot ROMs during runtime, especially the PFM + set_boot_rom(value); + break; + default: + logerror("Unknown setting %d ignored\n", number); + } +} + +/**************************************************************************** + GROM simulation. The Geneve board simulated GROM circuits within its gate + array. +*****************************************************************************/ + +/* + Simulates GROM. The real Geneve does not use GROMs but simulates them + within the gate array. Unlike with real GROMs, no address wrapping occurs, + and the complete 64K space is available. +*/ +READ8_MEMBER( geneve_mapper_device::read_grom ) +{ + uint8_t reply; + if (offset & 0x0002) + { + // GROM address handling + m_gromwaddr_LSB = false; + + if (m_gromraddr_LSB) + { + reply = m_grom_address & 0xff; + m_gromraddr_LSB = false; + } + else + { + reply = (m_grom_address >> 8) & 0xff; + m_gromraddr_LSB = true; + } + } + else + { + // GROM data handling + // GROMs are stored in pages 38..3f + int page = 0x38; + reply = m_dram->pointer()[(page<<13) + m_grom_address]; + m_grom_address = (m_grom_address + 1) & 0xffff; + m_gromraddr_LSB = m_gromwaddr_LSB = false; + } + return reply; +} + +/* + Simulates GROM. The real Geneve does not use GROMs but simulates them + within the gate array. +*/ +WRITE8_MEMBER( geneve_mapper_device::write_grom ) +{ + if (offset & 0x0002) + { + // set address + m_gromraddr_LSB = false; + if (m_gromwaddr_LSB) + { + m_grom_address = (m_grom_address & 0xff00) | data; + m_grom_address = (m_grom_address + 1) & 0xffff; + m_gromwaddr_LSB = false; + } + else + { + m_grom_address = (m_grom_address & 0x00ff) | ((uint16_t)data<<8); + m_gromwaddr_LSB = true; + } + } + else + { // write GPL data + // The Geneve GROM simulator allows for GROM writing (verified with a real system) + int page = 0x38; + m_dram->pointer()[(page<<13) + m_grom_address] = data; + + m_grom_address = (m_grom_address + 1) & 0xffff; + m_gromraddr_LSB = m_gromwaddr_LSB = false; + } +} + +void geneve_mapper_device::set_wait(int min) +{ + if (m_debug_no_ws) return; + if (m_extra_waitstates && min < 2) min = 2; + + // if we still have video wait states, do not set this counter + // (or it will assert READY when expiring) + if (m_ext_waitcount > min) return; + + // need one more pass so that READY will be asserted again + m_waitcount = min + 1; + if (m_waitcount > 1) + { + if (TRACE_LINES) logerror("Pulling down READY line for %d cycles\n", min); + m_ready(CLEAR_LINE); + m_ready_asserted = false; + } +} + +void geneve_mapper_device::set_ext_wait(int min) +{ + if (m_debug_no_ws) return; + m_ext_waitcount = min; +} + +void geneve_mapper_device::set_boot_rom(int selection) +{ + switch (selection) + { + case GENEVE_098: + logerror("Using 0.98 boot eprom\n"); + m_eprom = machine().root_device().memregion("maincpu")->base() + 0x4000; + m_pfm_mode = 0; + break; + case GENEVE_100: + logerror("Using 1.00 boot eprom\n"); + m_eprom = machine().root_device().memregion("maincpu")->base(); + m_pfm_mode = 0; + break; + case GENEVE_PFM512: + logerror("Using PFM512 (AT29C040)\n"); + m_pfm_mode = 1; + break; + case GENEVE_PFM512A: + logerror("Using PFM512A (AT29C040A)\n"); + m_pfm_mode = 2; + break; + default: + logerror("Unknown boot ROM selection\n"); + } +} + +void geneve_mapper_device::set_geneve_mode(bool geneve) +{ + if (TRACE_SETTING) logerror("Setting Geneve mode = %d\n", geneve); + m_geneve_mode = geneve; +} + +void geneve_mapper_device::set_direct_mode(bool direct) +{ + if (TRACE_SETTING) logerror("Setting direct mode = %d\n", direct); + m_direct_mode = direct; +} + +void geneve_mapper_device::set_cartridge_size(int size) +{ + if (TRACE_SETTING) logerror("Setting cartridge size to %d\n", size); + m_cartridge_size = size; +} + +void geneve_mapper_device::set_cartridge_writable(int base, bool write) +{ + if (TRACE_SETTING) logerror("Cartridge %04x space writable = %d\n", base, write); + if (base==0x6000) m_cartridge6_writable = write; + else m_cartridge7_writable = write; +} + +void geneve_mapper_device::set_video_waitstates(bool wait) +{ + if (TRACE_VIDEOWS) logerror("Setting video waitstates = %d\n", wait); + m_video_waitstates = wait; +} + +void geneve_mapper_device::set_extra_waitstates(bool wait) +{ + if (TRACE_SETTING) logerror("Setting extra waitstates = %d\n", wait); + m_extra_waitstates = wait; +} + + +/************************************************************************ + Called by the address map +************************************************************************/ +/* + Constants for mapper decoding. Naming scheme: + M=mapper + L=Logical space; P=Physical space + G=Geneve mode; T=TI mode; GM=GenMod +*/ +enum +{ + MLGVIDEO=1, + MLGMAPPER, + MLGKEY, + MLGCLOCK, + MLGSOUND, + MLTMAPPER, + MLTKEY, + MLTCLOCK, + MLTVIDEO, + MLTSPEECH, + MLTGROM, + MLTSOUND, + MPGDRAM, + MPGEXP, + MPGEPROM, + MPGSRAM, + MPGBOX, + MPGMDRAM, + MPGMEPROM, + MPGMBOX +}; + +/* + Read a byte via the data bus. The decoding has already been done in the + SETOFFSET method, and we re-use the values stored there to quickly + access the appropriate component. +*/ +READ8_MEMBER( geneve_mapper_device::readm ) +{ + uint8_t value = 0; + + decdata *dec; + decdata debug; + + // For the debugger, do the decoding here with no wait states + if (machine().side_effect_disabled()) + { + if (m_cpu->is_onchip(offset)) return m_cpu->debug_read_onchip_memory(offset&0xff); + dec = &debug; + m_debug_no_ws = true; + decode(space, offset, true, dec); + } + else + { + // Use the values found in the setaddress phase + dec = &m_decoded; + m_debug_no_ws = false; + } + + switch (dec->function) + { + case MLGVIDEO: + if (!machine().side_effect_disabled()) + { + value = m_video->read(space, dec->offset>>1); + if (TRACE_READ) logerror("Read video %04x -> %02x\n", dec->offset, value); + // Video wait states are created *after* the access + // Accordingly, they have no effect when execution is in onchip RAM + if (m_video_waitstates) set_ext_wait(15); + } + break; + + case MLGMAPPER: + // mapper + value = m_map[dec->offset]; + if (TRACE_READ) logerror("read mapper %04x -> %02x\n", dec->offset, value); + break; + + case MLGKEY: + // key + if (!machine().side_effect_disabled()) value = m_keyboard->get_recent_key(); + if (TRACE_READ) logerror("Read keyboard -> %02x\n", value); + break; + + case MLGCLOCK: + // clock + // tests on the real machine showed that + // upper nibble is 0xf (probably because of the location at 0xf130?) + value = m_clock->read(space, dec->offset) | 0xf0; + if (TRACE_READ) logerror("Read clock %04x -> %02x\n", dec->offset, value); + break; + + case MLTMAPPER: + // mapper + value = m_map[dec->offset]; + if (TRACE_READ) logerror("Read mapper %04x -> %02x\n", dec->offset, value); + break; + + case MLTKEY: + // key + if (!machine().side_effect_disabled()) value = m_keyboard->get_recent_key(); + if (TRACE_READ) logerror("Read keyboard -> %02x\n", value); + break; + + case MLTCLOCK: + // clock + // upper nibble is 1, only last byte gets a 2 + // probably because of the location at 8010...8020? + // (TI mode used swapped byte order) + // unless we use a workspace at >F000, in which case we get 8x values + // Obscure, needs more investigation. We might as well ignore this, + // as the high nibble is obviously undefined and takes some past + // value floating around. + value = m_clock->read(space, dec->offset); + value |= (dec->offset==0x000f)? 0x20 : 0x10; + if (TRACE_READ) logerror("Read clock %04x -> %02x\n", dec->offset, value); + break; + + case MLTVIDEO: + // video + // ++++ ++-- ---- ---+ + // 1000 1000 0000 00x0 + if (!machine().side_effect_disabled()) + { + value = m_video->read(space, dec->offset>>1); + if (TRACE_READ) logerror("Read video %04x -> %02x\n", dec->offset, value); + // See above + if (m_video_waitstates) set_ext_wait(15); + } + break; + + case MLTSPEECH: + // speech + // ++++ ++-- ---- ---+ + // 1001 0000 0000 0000 + // We need to add the address prefix bits + m_peribox->readz(space, dec->offset, &value, 0xff); + m_peribox->memen_in(CLEAR_LINE); + if (TRACE_READ) logerror("Read speech -> %02x\n", value); + break; + + case MLTGROM: + // grom simulation + // ++++ ++-- ---- ---+ + // 1001 1000 0000 00x0 + if (!machine().side_effect_disabled()) value = read_grom(space, dec->offset, 0xff); + if (TRACE_READ) logerror("Read GROM %04x -> %02x\n", dec->offset, value); + break; + + case MLGSOUND: + case MLTSOUND: + value = 0; + break; + + + case MPGDRAM: + // DRAM. + value = m_dram->pointer()[dec->physaddr]; +// LOG("dram read physaddr = %06x logaddr = %04x value = %02x\n", dec->physaddr, dec->offset, value); + if (TRACE_READ) logerror("Read DRAM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); + break; + + case MPGEXP: + // On-board memory expansion for standard Geneve (never used) + if (TRACE_READ) logerror("Read unmapped area %06x\n", dec->physaddr); + value = 0; + break; + + case MPGEPROM: + // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) + // mirrored for f0, f2, f4, ...; f1, f3, f5, ... + if (m_pfm_mode == 0) + { + value = m_eprom[dec->physaddr & 0x003fff]; + if (TRACE_READ) logerror("Read EPROM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); + } + else value = read_from_pfm(space, dec->physaddr, 0xff); + + break; + + case MPGSRAM: + if ((dec->physaddr & m_sram_mask)==m_sram_val) + { + value = m_sram->pointer()[dec->physaddr & ~m_sram_mask]; + } + else value = 0; + // Return in any case +// LOG("sram read physaddr = %06x logaddr = %04x value = %02x\n", dec->physaddr, dec->offset, value); + if (TRACE_READ) logerror("Read SRAM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); + break; + + case MPGBOX: + // Route everything else to the P-Box + // 0x000000-0x07ffff for the stock Geneve (AMC,AMB,AMA,A0 ...,A15) + // 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15) + + m_peribox->readz(space, dec->physaddr, &value, 0xff); + m_peribox->memen_in(CLEAR_LINE); + if (TRACE_READ) logerror("Read P-Box %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); + break; + + case MPGMDRAM: + // DRAM. One wait state. + value = m_dram->pointer()[dec->physaddr]; + break; + + case MPGMEPROM: + // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) + // mirrored for f0, f2, f4, ...; f1, f3, f5, ... + if (m_pfm_mode == 0) + { + value = m_eprom[dec->physaddr & 0x003fff]; + if (TRACE_READ) logerror("Read EPROM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); + } + else value = read_from_pfm(space, dec->physaddr, 0xff); + break; + + case MPGMBOX: + // Route everything else to the P-Box + m_peribox->readz(space, dec->physaddr, &value, 0xff); + m_peribox->memen_in(CLEAR_LINE); + break; + } + return value; +} + +WRITE8_MEMBER( geneve_mapper_device::writem ) +{ + decdata *dec; + decdata debug; + + // For the debugger, do the decoding here with no wait states + if (machine().side_effect_disabled()) + { + dec = &debug; + m_debug_no_ws = true; + decode(space, offset, false, dec); + } + else + { + // Use the values found in the setaddress phase + m_debug_no_ws = false; + dec = &m_decoded; + } + + switch (dec->function) + { + case MLGVIDEO: + // video + // ++++ ++++ ++++ ---+ + // 1111 0001 0000 .cc0 + + if (!machine().side_effect_disabled()) + { + m_video->write(space, dec->offset>>1, data); + if (TRACE_WRITE) logerror("Write video %04x <- %02x\n", offset, data); + // See above + if (m_video_waitstates) set_ext_wait(15); + } + break; + + case MLGMAPPER: + // mapper + m_map[dec->offset] = data; + if (TRACE_WRITE) logerror("Write mapper %04x <- %02x\n", offset, data); + break; + + case MLGCLOCK: + // clock + // ++++ ++++ ++++ ---- + m_clock->write(space, dec->offset, data); + if (TRACE_WRITE) logerror("Write clock %04x <- %02x\n", offset, data); + break; + + case MLGSOUND: + // sound + // ++++ ++++ ++++ ---+ + m_sound->write(space, 0, data, 0xff); + if (TRACE_WRITE) logerror("Write sound <- %02x\n", data); + break; + + case MLTMAPPER: + // mapper + m_map[dec->offset] = data; + if (TRACE_WRITE) logerror("Write mapper %04x <- %02x\n", offset, data); + break; + + case MLTCLOCK: + // clock + m_clock->write(space, dec->offset, data); + if (TRACE_WRITE) logerror("Write clock %04x <- %02x\n", offset, data); + break; + + case MLTVIDEO: + // video + // ++++ ++-- ---- ---+ + // 1000 1100 0000 00c0 + // Initialize waitstate timer + if (!machine().side_effect_disabled()) + { + m_video->write(space, dec->offset>>1, data); + if (TRACE_WRITE) logerror("Write video %04x <- %02x\n", offset, data); + // See above + if (m_video_waitstates) set_ext_wait(15); + } + break; + + case MLTSPEECH: + // speech + // ++++ ++-- ---- ---+ + // 1001 0100 0000 0000 + // We need to add the address prefix bits + m_peribox->write(space, dec->offset, data, 0xff); + m_peribox->memen_in(CLEAR_LINE); + if (TRACE_WRITE) logerror("Write speech <- %02x\n", data); + break; + + case MLTGROM: + // grom simulation + // ++++ ++-- ---- ---+ + // 1001 1100 0000 00c0 + write_grom(space, dec->offset, data, 0xff); + if (TRACE_WRITE) logerror("Write GROM %04x <- %02x\n", offset, data); + break; + + case MLTSOUND: + // sound + // ++++ ++-- ---- ---+ + // 1000 0100 0000 0000 + m_sound->write(space, 0, data, 0xff); + if (TRACE_WRITE) logerror("Write sound <- %02x\n", data); + break; + + case MLTKEY: + case MLGKEY: + break; + + case MPGDRAM: + // DRAM write. One wait state. (only for normal Geneve) + m_dram->pointer()[dec->physaddr] = data; + if (TRACE_WRITE) logerror("Write DRAM %04x (%06x) <- %02x\n", offset, dec->physaddr, data); + break; + + case MPGEXP: + // On-board memory expansion for standard Geneve (never used) + if (TRACE_WRITE) logerror("Write unmapped area %06x\n", dec->physaddr); + break; + + case MPGEPROM: + // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) + // mirrored for f0, f2, f4, ...; f1, f3, f5, ... + // Ignore EPROM write (unless PFM) + if (m_pfm_mode != 0) write_to_pfm(space, dec->physaddr, data, 0xff); + else + logerror("Write EPROM %04x (%06x) <- %02x, ignored\n", offset, dec->physaddr, data); + break; + + case MPGSRAM: + if ((dec->physaddr & m_sram_mask)==m_sram_val) + { + m_sram->pointer()[dec->physaddr & ~m_sram_mask] = data; + } + if (TRACE_WRITE) logerror("Write SRAM %04x (%06x) <- %02x\n", offset, dec->physaddr, data); + break; + + case MPGBOX: + dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address + if (TRACE_WRITE) logerror("Write P-Box %04x (%06x) <- %02x\n", offset, dec->physaddr, data); + m_peribox->write(space, dec->physaddr, data, 0xff); + m_peribox->memen_in(CLEAR_LINE); + break; + + case MPGMDRAM: + // DRAM. One wait state. + m_dram->pointer()[dec->physaddr] = data; + break; + + case MPGMEPROM: + // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) + // mirrored for f0, f2, f4, ...; f1, f3, f5, ... + // Ignore EPROM write + if (m_pfm_mode != 0) write_to_pfm(space, dec->physaddr, data, 0xff); + else + logerror("Write EPROM %04x (%06x) <- %02x, ignored\n", offset, dec->physaddr, data); + break; + + case MPGMBOX: + // Route everything else to the P-Box + m_peribox->write(space, dec->physaddr, data, 0xff); + m_peribox->memen_in(CLEAR_LINE); + break; + } +} + +void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read_mode, geneve_mapper_device::decdata* dec) +{ + dec->function = 0; + dec->offset = offset; + dec->physaddr = 0; + + int page; + + if (read_mode) // got this from DBIN + { + // Logical addresses + if (m_geneve_mode) + { + // TODO: shortcut offset & 0xffc0 = 0xf100 + if ((offset & 0xfff5)==0xf100) + { + // video + // ++++ ++++ ++++ -+-+ + // 1111 0001 0000 0000 + // 1111 0001 0000 0010 + // 1111 0001 0000 1000 + // 1111 0001 0000 1010 + + dec->function = MLGVIDEO; + set_wait(1); + return; + } + if ((offset & 0xfff8)==0xf110) + { + // mapper + dec->function = MLGMAPPER; + dec->offset = dec->offset & 0x0007; + set_wait(1); + return; + } + if ((offset & 0xfff8) == 0xf118) + { + // key + dec->function = MLGKEY; + set_wait(1); + return; + } + if ((offset & 0xfff0)==0xf130) + { + // clock + // tests on the real machine showed that + // upper nibble is 0xf (probably because of the location at 0xf130?) + dec->function = MLGCLOCK; + dec->offset = dec->offset & 0x000f; + set_wait(1); + return; + } + } + else + { + if ((offset & 0xfff8)==0x8000) + { + // mapper + dec->function = MLTMAPPER; + dec->offset = dec->offset & 0x0007; + set_wait(1); + return; + } + if ((offset & 0xfff8)== 0x8008) + { + // key + dec->function = MLTKEY; + set_wait(1); + return; + } + if ((offset & 0xfff0)==0x8010) + { + // clock + dec->function = MLTCLOCK; + dec->offset = dec->offset & 0x000f; + set_wait(1); + return; + } + if ((offset & 0xfc01)==0x8800) + { + // video + // ++++ ++-- ---- ---+ + // 1000 1000 0000 00x0 + // 1 WS is always added; any pending video waitstates are canceled + dec->function = MLTVIDEO; + set_wait(1); + return; + } + if ((offset & 0xfc01)==0x9000) + { + // speech + // ++++ ++-- ---- ---+ + // 1001 0000 0000 0000 + // We need to add the address prefix bits + dec->function = MLTSPEECH; + dec->offset = offset | ((m_genmod)? 0x170000 : 0x070000); + m_peribox->memen_in(ASSERT_LINE); + m_peribox->setaddress_dbin(space, dec->offset, read_mode); + set_wait(1); + return; + } + if ((offset & 0xfc01)==0x9800) + { + // grom simulation + // ++++ ++-- ---- ---+ + // 1001 1000 0000 00x0 + dec->function = MLTGROM; + set_wait(1); + return; + } + } + // still here? Then go via mapping. + page = (offset & 0xe000) >> 13; + + // Determine physical address + if (m_direct_mode) + { + dec->physaddr = 0x1f0000; // points to boot eprom (page F8) + } + else + { + if (!m_geneve_mode && page==3) + { + if (m_cartridge_size==0x4000 && m_cartridge_secondpage) dec->physaddr = 0x06e000; + else dec->physaddr = 0x06c000; + } + else + { + dec->physaddr = (m_map[page] << 13); + } + } + dec->physaddr |= (offset & 0x1fff); + + if (!m_genmod) // Standard Geneve + { + if ((dec->physaddr & 0x180000)==0x000000) + { + // DRAM. + dec->physaddr = dec->physaddr & 0x07ffff; + dec->function = MPGDRAM; + set_wait(1); + return; + } + + if ((dec->physaddr & 0x180000)==0x080000) + { + // On-board memory expansion for standard Geneve (never used) + dec->function = MPGEXP; + set_wait(1); + return; + } + + if ((dec->physaddr & 0x1e0000)==0x1e0000) + { + // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) + // mirrored for f0, f2, f4, ...; f1, f3, f5, ... unless using PFM + dec->function = MPGEPROM; + set_wait(0); + return; + } + + if ((dec->physaddr & 0x180000)==0x180000) + { + dec->function = MPGSRAM; + set_wait(0); + return; + } + + // Route everything else to the P-Box + // 0x000000-0x07ffff for the stock Geneve (AMC,AMB,AMA,A0 ...,A15) + // 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15) + // Add a wait state + set_wait(1); + dec->function = MPGBOX; + + dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address (with AMA..AMC) + m_peribox->memen_in(ASSERT_LINE); + m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); + return; + } + else + { + // GenMod mode + if ((m_timode) && ((dec->physaddr & 0x180000)==0x000000)) + { + // DRAM. One wait state. + dec->function = MPGMDRAM; + dec->physaddr = dec->physaddr & 0x07ffff; + if (!m_turbo) set_wait(1); + return; + } + + if ((dec->physaddr & 0x1e0000)==0x1e0000) + { + // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) + // mirrored for f0, f2, f4, ...; f1, f3, f5, ... unless using PFM + dec->function = MPGMEPROM; + set_wait(0); + return; + } + + // Route everything else to the P-Box + dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod + dec->function = MPGMBOX; + + if (!m_turbo) set_wait(1); + // Check: Are waitstates completely turned off for turbo mode, or + // merely the waitstates for DRAM memory access and box access? + + m_peribox->memen_in(ASSERT_LINE); + m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); + return; + } + } + else + { // Write access + // Logical addresses + if (m_geneve_mode) + { + if ((offset & 0xfff1)==0xf100) + { + // 1 WS is always added; any pending video waitstates are canceled + dec->function = MLGVIDEO; + set_wait(1); + return; + } + if ((offset & 0xfff8)==0xf110) + { + dec->function = MLGMAPPER; + dec->offset = dec->offset & 0x0007; + set_wait(1); + return; + } + if ((offset & 0xfff1)==0xf120) + { + // Add 24 waitstates. This is an average value, as the + // waitstate generation seems to depend on an external timer of + // the sound chip + // TODO: do it properly with the use of READY + dec->function = MLGSOUND; + set_wait(24); + return; + } + if ((offset & 0xfff0)==0xf130) + { + dec->function = MLGCLOCK; + dec->offset = dec->offset & 0x00f; + set_wait(1); + return; + } + } + else + { + // TI mode + if ((offset & 0xfff8)==0x8000) + { + dec->function = MLTMAPPER; + dec->offset = dec->offset & 0x0007; + set_wait(1); + return; + } + if ((offset & 0xfff0)==0x8010) + { + dec->function = MLTCLOCK; + dec->offset = dec->offset & 0x00f; + set_wait(1); + return; + } + if ((offset & 0xfc01)==0x9c00) + { + dec->function = MLTGROM; + set_wait(1); + return; + } + if ((offset & 0xfc01)==0x8400) + { + // Add 24 waitstates. This is an approximation, as the + // waitstate generation seems to depend on an external timer of + // the sound chip + // TODO: do it properly with the use of READY- + dec->function = MLTSOUND; + set_wait(24); + return; + } + if ((offset & 0xfc01)==0x8c00) + { + // 1 WS is always added; any pending video waitstates are canceled + dec->function = MLTVIDEO; + set_wait(1); + return; + } + + if ((offset & 0xfc01)==0x9400) + { + dec->function = MLTSPEECH; + dec->offset = dec->offset | ((m_genmod)? 0x170000 : 0x070000); + m_peribox->memen_in(ASSERT_LINE); + m_peribox->setaddress_dbin(space, dec->offset, read_mode); + set_wait(1); + return; + } + } + + // Determine physical address + page = (dec->offset & 0xe000) >> 13; + + if (m_direct_mode) + { + dec->physaddr = 0x1e0000; // points to boot eprom + } + else + { + if (!m_geneve_mode && page==3) + { + if (m_cartridge_size==0x4000) + { + m_cartridge_secondpage = ((dec->offset & 0x0002)!=0); + if (TRACE_WRITE) logerror("Set cartridge page %02x\n", m_cartridge_secondpage); + set_wait(1); + return; + } + else + { + // writing into cartridge rom space (no bankswitching) + if ((((dec->offset & 0x1000)==0x0000) && !m_cartridge6_writable) + || (((dec->offset & 0x1000)==0x1000) && !m_cartridge7_writable)) + { + logerror("Writing to protected cartridge space %04x ignored\n", dec->offset); + return; + } + else + // TODO: Check whether secondpage is really ignored + dec->physaddr = 0x06c000; + } + } + else + dec->physaddr = (m_map[page] << 13); + } + + dec->physaddr |= dec->offset & 0x1fff; + + if (!m_genmod) + { + if ((dec->physaddr & 0x180000)==0x000000) + { + dec->function = MPGDRAM; + dec->physaddr = dec->physaddr & 0x07ffff; + set_wait(1); + return; + } + if ((dec->physaddr & 0x180000)==0x080000) + { + dec->function = MPGEXP; + set_wait(1); + return; + } + + if ((dec->physaddr & 0x1e0000)==0x1e0000) + { + dec->function = MPGEPROM; + set_wait(0); // EPROM + return; + } + if ((dec->physaddr & 0x180000)==0x180000) + { + dec->function = MPGSRAM; + set_wait(0); // SRAM + return; + } + + // Route everything else to the P-Box + // Add a wait state + + // only AMA, AMB, AMC are used; AMD and AME are not used + dec->function = MPGBOX; + dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address + m_peribox->memen_in(ASSERT_LINE); + m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); + set_wait(1); + } + else + { + // GenMod mode + if ((dec->physaddr & 0x1e0000)==0x1e0000) + { // EPROM, ignore (unless PFM) + dec->function = MPGMEPROM; + set_wait(0); + return; + } + + if (m_timode && ((dec->physaddr & 0x180000)==0x000000)) + { + dec->function = MPGMDRAM; + dec->physaddr = dec->physaddr & 0x07ffff; + if (!m_turbo) set_wait(1); + return; + } + + // Route everything else to the P-Box + dec->function = MPGMBOX; + dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod + m_peribox->memen_in(ASSERT_LINE); + m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); + if (!m_turbo) set_wait(1); + } + } +} + +/* + Read from PFM. +*/ +READ8_MEMBER( geneve_mapper_device::read_from_pfm ) +{ + uint8_t value; + if (!m_pfm_output_enable) return 0; + + int address = (offset & 0x01ffff) | (m_pfm_bank<<17); + + switch (m_pfm_mode) + { + case 1: + value = m_pfm512->read(space, address, mem_mask); + break; + case 2: + value = m_pfm512a->read(space, address, mem_mask); + break; + default: + logerror("Illegal mode for reading PFM: %d\n", m_pfm_mode); + return 0; + } + + if (TRACE_PFM) logerror("Reading from PFM at address %05x -> %02x\n", address, value); + return value; +} + +WRITE8_MEMBER( geneve_mapper_device::write_to_pfm ) +{ + // Nota bene: The PFM must be write protected on startup, or the RESET + // of the 9995 will attempt to write the return vector into the flash EEPROM + int address = (offset & 0x01ffff) | (m_pfm_bank<<17); + if (TRACE_PFM) logerror("Writing to PFM at address %05x <- %02x\n", address, data); + + switch (m_pfm_mode) + { + case 1: + m_pfm512->write(space, address, data, mem_mask); + break; + case 2: + m_pfm512a->write(space, address, data, mem_mask); + break; + default: + logerror("Illegal mode for writing to PFM: %d\n", m_pfm_mode); + } +} + + +/* + Accept the address passed over the address bus and decode it appropriately. + This decoding will later be used in the READ/WRITE member functions. Also, + we initiate wait state creation here. +*/ +SETOFFSET_MEMBER( geneve_mapper_device::setoffset ) +{ + if (TRACE_DETAIL) logerror("setoffset = %04x\n", offset); + m_debug_no_ws = false; + decode(space, offset, m_read_mode, &m_decoded); +} + +/* + The mapper is connected to the clock line in order to operate + the wait state counter. + The wait counter is decremented on each rising clock edge; when 0, the + READY line is asserted. However, there is a second counter which is used for + video wait states. + The READY line must be asserted when the wait counter reaches 0, but must be + cleared immediately again if the video counter has not reached 0. + (See comments at the file header: The additional video wait states do not + affect the video access itself but become effective after the access; if + the code runs on the chip, these wait states are ignored.) +*/ +WRITE_LINE_MEMBER( geneve_mapper_device::clock_in ) +{ + if (state==ASSERT_LINE) + { + // Rising edge + if (!m_ready_asserted) + { + if (m_waitcount > 0) + { + m_waitcount--; + if (m_waitcount == 0) + { + if (TRACE_CLOCK) logerror("clock, READY asserted\n"); + m_ready(ASSERT_LINE); + m_ready_asserted = true; + } + else + { + if (TRACE_CLOCK) logerror("clock\n"); + } + } + else + { + if (m_ext_waitcount > 0) + { + m_ext_waitcount--; + if (m_ext_waitcount == 0) + { + if (TRACE_CLOCK) logerror("clock, READY asserted after video\n"); + m_ready(ASSERT_LINE); + m_ready_asserted = true; + } + else + { + if (TRACE_CLOCK) logerror("vclock, ew=%d\n", m_ext_waitcount); + } + } + } + } + } + else + { + // Falling edge + // Do we have video wait states? In that case, clear the line again + if ((m_waitcount == 0) && (m_ext_waitcount > 0) && m_ready_asserted) + { + if (TRACE_CLOCK) logerror("clock, READY cleared for video\n"); + m_ready(CLEAR_LINE); + m_ready_asserted = false; + } + } +} + +/* + We need the DBIN line for the setoffset operation. +*/ +WRITE_LINE_MEMBER( geneve_mapper_device::dbin_in ) +{ + m_read_mode = (state==ASSERT_LINE); + if (TRACE_DETAIL) logerror("dbin = %02x\n", m_read_mode? 1:0); +} + +/* + PFM expansion: Setting the bank. +*/ +WRITE_LINE_MEMBER( geneve_mapper_device::pfm_select_lsb ) +{ + if (state==ASSERT_LINE) m_pfm_bank |= 1; + else m_pfm_bank &= 0xfe; + if (TRACE_PFM) logerror("Setting bank (l) = %d\n", m_pfm_bank); +} + +WRITE_LINE_MEMBER( geneve_mapper_device::pfm_select_msb ) +{ + if (state==ASSERT_LINE) m_pfm_bank |= 2; + else m_pfm_bank &= 0xfd; + if (TRACE_PFM) logerror("Setting bank (u) = %d\n", m_pfm_bank); +} + +WRITE_LINE_MEMBER( geneve_mapper_device::pfm_output_enable ) +{ + // Negative logic + m_pfm_output_enable = (state==CLEAR_LINE); + if (TRACE_PFM) logerror("PFM output %s\n", m_pfm_output_enable? "enable" : "disable"); +} + +//==================================================================== +// Common device lifecycle +//==================================================================== + +void geneve_mapper_device::device_start() +{ + m_ready.resolve(); + + m_geneve_mode = false; + m_direct_mode = true; + + // State registration + save_item(NAME(m_gromwaddr_LSB)); + save_item(NAME(m_gromraddr_LSB)); + save_item(NAME(m_grom_address)); + save_item(NAME(m_video_waitstates)); + save_item(NAME(m_extra_waitstates)); + save_item(NAME(m_ready_asserted)); + save_item(NAME(m_read_mode)); + save_item(NAME(m_debug_no_ws)); + save_item(NAME(m_geneve_mode)); + save_item(NAME(m_direct_mode)); + save_item(NAME(m_cartridge_size)); + save_item(NAME(m_cartridge_secondpage)); + save_item(NAME(m_cartridge6_writable)); + save_item(NAME(m_cartridge7_writable)); + save_pointer(NAME(m_map), 8); + save_item(NAME(m_decoded.function)); + save_item(NAME(m_decoded.offset)); + save_item(NAME(m_decoded.physaddr)); + save_item(NAME(m_turbo)); + save_item(NAME(m_genmod)); + save_item(NAME(m_timode)); + save_item(NAME(m_pfm_mode)); + save_item(NAME(m_pfm_bank)); + save_item(NAME(m_pfm_output_enable)); + save_item(NAME(m_sram_mask)); + save_item(NAME(m_sram_val)); + save_item(NAME(m_waitcount)); + save_item(NAME(m_ext_waitcount)); +} + +void geneve_mapper_device::device_reset() +{ + m_extra_waitstates = false; + m_video_waitstates = true; + m_read_mode = false; + m_waitcount = 0; + m_ext_waitcount = 0; + m_ready_asserted = true; + + m_geneve_mode =false; + m_direct_mode = true; + m_cartridge_size = 0x4000; + m_cartridge_secondpage = false; + m_cartridge6_writable = false; + m_cartridge7_writable = false; + m_grom_address = 0; + m_pfm_bank = 0; + m_pfm_output_enable = true; + + // Clear map + for (auto & elem : m_map) elem = 0; + + m_genmod = false; + + // Check which boot EPROM we are using (or PFM) + set_boot_rom(machine().root_device().ioport("BOOTROM")->read()); + + // Check for GenMod. We assume that GenMod can be combined with PFM. + if (machine().root_device().ioport("MODE")->read()!=0) + { + logerror("Using GenMod modification\n"); + m_eprom = machine().root_device().memregion("maincpu")->base() + 0x8000; + if (m_eprom[0] != 0xf0) + { + fatalerror("genboard: GenMod boot ROM missing\n"); + } + m_genmod = true; + m_turbo = ((machine().root_device().ioport("GENMODDIPS")->read() & GM_TURBO)!=0); + m_timode = ((machine().root_device().ioport("GENMODDIPS")->read() & GM_TIM)!=0); + } + + switch (machine().root_device().ioport("SRAM")->read()) + { +/* 1 100. .... .... .... .... on-board sram (128K) -+ + 1 101. .... .... .... .... on-board sram (128K) -+-- maximum SRAM expansion + 1 1100 .... .... .... .... on-board sram (64K) --+ + 1 1101 0... .... .... .... on-board sram (32K) - additional 32 KiB required for MDOS 2.50s and higher + 1 1101 1... .... .... .... on-board sram (32K) - standard setup +*/ + case 0: // 32 KiB + m_sram_mask = 0x1f8000; + m_sram_val = 0x1d8000; + break; + case 1: // 64 KiB + m_sram_mask = 0x1f0000; + m_sram_val = 0x1d0000; + break; + case 2: // 384 KiB (actually 512 KiB, but the EPROM masks the upper 128 KiB) + m_sram_mask = 0x180000; + m_sram_val = 0x180000; + break; + } +} + +/**************************************************************************** + Keyboard support +****************************************************************************/ + +static const uint8_t MF1_CODE[0xe] = +{ + /* extended keys that are equivalent to non-extended keys */ + 0x1c, /* keypad enter */ + 0x1d, /* right control */ + 0x38, /* alt gr */ + // extra codes are 0x5b for Left Windows, 0x5c for Right Windows, 0x5d + // for Menu, 0x5e for power, 0x5f for sleep, 0x63 for wake, but I doubt + // any Geneve program would take advantage of these. */ + + // extended key that is equivalent to a non-extended key + // with shift off + 0x35, /* pad slash */ + + // extended keys that are equivalent to non-extended keys + // with numlock off + 0x47, /* home */ + 0x48, /* up */ + 0x49, /* page up */ + 0x4b, /* left */ + 0x4d, /* right */ + 0x4f, /* end */ + 0x50, /* down */ + 0x51, /* page down */ + 0x52, /* insert */ + 0x53 /* delete */ +}; + +geneve_keyboard_device::geneve_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, GENEVE_KEYBOARD, tag, owner, clock), + m_interrupt(*this), + m_keys(*this, "KEY%u", 0), + m_key_reset(false), m_key_queue_length(0), m_key_queue_head(0), m_key_in_buffer(false), m_key_numlock_state(false), m_key_ctrl_state(0), m_key_alt_state(0), + m_key_real_shift_state(0), m_key_fake_shift_state(false), m_key_fake_unshift_state(false), m_key_autorepeat_key(0), m_key_autorepeat_timer(0), m_keep_keybuf(false), + m_keyboard_clock(false), m_timer(nullptr) +{ +} + +void geneve_keyboard_device::post_in_key_queue(int keycode) +{ + m_key_queue[(m_key_queue_head + m_key_queue_length) % KEYQUEUESIZE] = keycode; + m_key_queue_length++; + + if (TRACE_KEYBOARD) logerror("Posting keycode %02x\n", keycode); +} + +void geneve_keyboard_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + poll(); +} + +void geneve_keyboard_device::poll() +{ + uint32_t keystate; + uint32_t key_transitions; + int i, j; + int keycode; + int pressed; + if (TRACE_KEYBOARD) logerror("Poll keyboard\n"); + if (m_key_reset) return; + + /* Poll keyboard */ + for (i = 0; (i < 4) && (m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); i++) + { + keystate = m_keys[2*i]->read() | (m_keys[2*i + 1]->read() << 16); + key_transitions = keystate ^ m_key_state_save[i]; + if (key_transitions) + { + for (j = 0; (j < 32) && (m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); j++) + { + if ((key_transitions >> j) & 1) + { + keycode = (i << 5) | j; + pressed = ((keystate >> j) & 1); + if (pressed) + m_key_state_save[i] |= (1 << j); + else + m_key_state_save[i] &= ~ (1 << j); + + /* Update auto-repeat */ + if (pressed) + { + m_key_autorepeat_key = keycode; + m_key_autorepeat_timer = KEYAUTOREPEATDELAY+1; + } + else /*if (keycode == m_key_autorepeat_key)*/ + m_key_autorepeat_key = 0; + + // Release Fake Shift/Unshift if another key is pressed + // We do so if a key is released, though it is actually + // required only if it is a modifier key + /*if (pressed)*/ + //{ + if (m_key_fake_shift_state) + { + /* Fake shift release */ + post_in_key_queue(0xe0); + post_in_key_queue(0xaa); + m_key_fake_shift_state = false; + } + if (m_key_fake_unshift_state) + { + /* Fake shift press */ + post_in_key_queue(0xe0); + post_in_key_queue(0x2a); + m_key_fake_unshift_state = false; + } + //} + + /* update shift and numlock state */ + if ((keycode == 0x2a) || (keycode == 0x36)) + m_key_real_shift_state = m_key_real_shift_state + (pressed ? +1 : -1); + if ((keycode == 0x1d) || (keycode == 0x61)) + m_key_ctrl_state = m_key_ctrl_state + (pressed ? +1 : -1); + if ((keycode == 0x38) || (keycode == 0x62)) + m_key_alt_state = m_key_alt_state + (pressed ? +1 : -1); + if ((keycode == 0x45) && pressed) + m_key_numlock_state = !m_key_numlock_state; + + if ((keycode >= 0x60) && (keycode < 0x6e)) + { /* simpler extended keys */ + /* these keys are emulated */ + + if ((keycode >= 0x63) && pressed) + { + /* Handle shift state */ + if (keycode == 0x63) + { /* non-shifted key */ + if (m_key_real_shift_state!=0) + /* Fake shift unpress */ + m_key_fake_unshift_state = true; + } + else /*if (keycode >= 0x64)*/ + { /* non-numlock mode key */ + if (m_key_numlock_state & (m_key_real_shift_state==0)) + /* Fake shift press if numlock is active */ + m_key_fake_shift_state = true; + else if ((!m_key_numlock_state) & (m_key_real_shift_state!=0)) + /* Fake shift unpress if shift is down */ + m_key_fake_unshift_state = true; + } + + if (m_key_fake_shift_state) + { + post_in_key_queue(0xe0); + post_in_key_queue(0x2a); + } + + if (m_key_fake_unshift_state) + { + post_in_key_queue(0xe0); + post_in_key_queue(0xaa); + } + } + + keycode = MF1_CODE[keycode-0x60]; + if (!pressed) keycode |= 0x80; + post_in_key_queue(0xe0); + post_in_key_queue(keycode); + } + else if (keycode == 0x6e) + { /* emulate Print Screen / System Request (F13) key */ + /* this is a bit complex, as Alt+PrtScr -> SysRq */ + /* Additionally, Ctrl+PrtScr involves no fake shift press */ + if (m_key_alt_state!=0) + { + /* SysRq */ + keycode = 0x54; + if (!pressed) keycode |= 0x80; + post_in_key_queue(keycode); + } + else + { + /* Handle shift state */ + if (pressed && (m_key_real_shift_state==0) && (m_key_ctrl_state==0)) + { /* Fake shift press */ + post_in_key_queue(0xe0); + post_in_key_queue(0x2a); + m_key_fake_shift_state = true; + } + + keycode = 0x37; + if (!pressed) keycode |= 0x80; + post_in_key_queue(0xe0); + post_in_key_queue(keycode); + } + } + else if (keycode == 0x6f) + { // emulate pause (F15) key + // this is a bit complex, as Pause -> Ctrl+NumLock and + // Ctrl+Pause -> Ctrl+ScrLock. Furthermore, there is no + // repeat or release. + if (pressed) + { + if (m_key_ctrl_state!=0) + { + post_in_key_queue(0xe0); + post_in_key_queue(0x46); + post_in_key_queue(0xe0); + post_in_key_queue(0xc6); + } + else + { + post_in_key_queue(0xe1); + post_in_key_queue(0x1d); + post_in_key_queue(0x45); + post_in_key_queue(0xe1); + post_in_key_queue(0x9d); + post_in_key_queue(0xc5); + } + } + } + else + { + if (!pressed) keycode |= 0x80; + post_in_key_queue(keycode); + } + signal_when_key_available(); + } + } + } + } + + /* Handle auto-repeat */ + if ((m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)) && (m_key_autorepeat_key!=0) && (--m_key_autorepeat_timer == 0)) + { + if ((m_key_autorepeat_key >= 0x60) && (m_key_autorepeat_key < 0x6e)) + { + post_in_key_queue(0xe0); + post_in_key_queue(MF1_CODE[m_key_autorepeat_key-0x60]); + } + else if (m_key_autorepeat_key == 0x6e) + { + if (m_key_alt_state!=0) + post_in_key_queue(0x54); + else + { + post_in_key_queue(0xe0); + post_in_key_queue(0x37); + } + } + else if (m_key_autorepeat_key == 0x6f) + ; + else + { + post_in_key_queue(m_key_autorepeat_key); + } + signal_when_key_available(); + m_key_autorepeat_timer = KEYAUTOREPEATRATE; + } +} + +uint8_t geneve_keyboard_device::get_recent_key() +{ + if (m_key_in_buffer) return m_key_queue[m_key_queue_head]; + else return 0; +} + +void geneve_keyboard_device::signal_when_key_available() +{ + // if keyboard reset is not asserted, and key clock is enabled, and key + // buffer clear is disabled, and key queue is not empty. */ + if ((!m_key_reset) && (m_keyboard_clock) && (m_keep_keybuf) && (m_key_queue_length != 0)) + { + if (TRACE_KEYBOARD) logerror("Signalling key available\n"); + m_interrupt(ASSERT_LINE); + m_key_in_buffer = true; + } +} + +WRITE_LINE_MEMBER( geneve_keyboard_device::clock_control ) +{ + bool rising_edge = (!m_keyboard_clock && (state==ASSERT_LINE)); + m_keyboard_clock = (state==ASSERT_LINE); + if (TRACE_KEYBOARD) logerror("Keyboard clock_control state=%d\n", m_keyboard_clock); + if (rising_edge) + signal_when_key_available(); +} + +WRITE_LINE_MEMBER( geneve_keyboard_device::send_scancodes ) +{ + bool rising_edge = (!m_keep_keybuf && (state==ASSERT_LINE)); + bool falling_edge = (m_keep_keybuf && (state==CLEAR_LINE)); + m_keep_keybuf = (state==ASSERT_LINE); + + if (rising_edge) signal_when_key_available(); + else + { + if (falling_edge) + { + if (m_key_queue_length != 0) + { + m_key_queue_head = (m_key_queue_head + 1) % KEYQUEUESIZE; + m_key_queue_length--; + } + /* clear keyboard interrupt */ + m_interrupt(CLEAR_LINE); + m_key_in_buffer = false; + } + } +} + +WRITE_LINE_MEMBER( geneve_keyboard_device::reset_line ) +{ + m_key_reset = !(state==ASSERT_LINE); + + if (m_key_reset) + { + /* reset -> clear keyboard key queue, but not geneve key buffer */ + m_key_queue_length = (m_key_in_buffer)? 1 : 0; + m_key_queue_head = 0; + memset(m_key_state_save, 0, sizeof(m_key_state_save)); + m_key_numlock_state = false; + m_key_ctrl_state = 0; + m_key_alt_state = 0; + m_key_real_shift_state = 0; + m_key_fake_shift_state = false; + m_key_fake_unshift_state = false; + m_key_autorepeat_key = 0; + } +} + +void geneve_keyboard_device::device_start() +{ + m_timer = timer_alloc(0); + m_interrupt.resolve(); + + // State registration + save_item(NAME(m_key_reset)); + save_item(NAME(m_key_queue_length)); + save_item(NAME(m_key_queue_head)); + save_item(NAME(m_key_in_buffer)); + save_item(NAME(m_key_numlock_state)); + save_item(NAME(m_key_ctrl_state)); + save_item(NAME(m_key_alt_state)); + save_item(NAME(m_key_real_shift_state)); + save_item(NAME(m_key_fake_shift_state)); + save_item(NAME(m_key_fake_unshift_state)); + save_item(NAME(m_key_autorepeat_key)); + save_item(NAME(m_key_autorepeat_timer)); + save_item(NAME(m_keep_keybuf)); + save_item(NAME(m_keyboard_clock)); + save_pointer(NAME(m_key_queue),KEYQUEUESIZE); + save_pointer(NAME(m_key_state_save),4); +} + +void geneve_keyboard_device::device_reset() +{ + m_key_in_buffer = false; + reset_line(CLEAR_LINE); + m_key_queue_length = 0; + m_key_reset = true; + m_keyboard_clock = false; + m_keep_keybuf = false; + m_timer->adjust(attotime::from_usec(1), 0, attotime::from_hz(120)); +} + +INPUT_PORTS_START( genkeys ) + PORT_START("KEY0") /* IN3 */ + PORT_BIT ( 0x0001, 0x0000, IPT_UNUSED ) /* unused scancode 0 */ + PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) /* Esc 01 81 */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) /* 1 02 82 */ + PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) /* 2 03 83 */ + PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) /* 3 04 84 */ + PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) /* 4 05 85 */ + PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) /* 5 06 86 */ + PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) /* 6 07 87 */ + PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) /* 7 08 88 */ + PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) /* 8 09 89 */ + PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) /* 9 0A 8A */ + PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) /* 0 0B 8B */ + PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) /* - 0C 8C */ + PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) /* = 0D 8D */ + PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) /* Backspace 0E 8E */ + PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) /* Tab 0F 8F */ + + PORT_START("KEY1") /* IN4 */ + PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) /* Q 10 90 */ + PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) /* W 11 91 */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) /* E 12 92 */ + PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) /* R 13 93 */ + PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) /* T 14 94 */ + PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) /* Y 15 95 */ + PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) /* U 16 96 */ + PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) /* I 17 97 */ + PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) /* O 18 98 */ + PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) /* P 19 99 */ + PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) /* [ 1A 9A */ + PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) /* ] 1B 9B */ + PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) /* Enter 1C 9C */ + PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L-Ctrl") PORT_CODE(KEYCODE_LCONTROL) /* Left Ctrl 1D 9D */ + PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) /* A 1E 9E */ + PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) /* S 1F 9F */ + + PORT_START("KEY2") /* IN5 */ + PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) /* D 20 A0 */ + PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) /* F 21 A1 */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) /* G 22 A2 */ + PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) /* H 23 A3 */ + PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) /* J 24 A4 */ + PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) /* K 25 A5 */ + PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) /* L 26 A6 */ + PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) /* ; 27 A7 */ + PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("' \"") PORT_CODE(KEYCODE_QUOTE) /* ' 28 A8 */ + PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) /* ` 29 A9 */ + PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L-Shift") PORT_CODE(KEYCODE_LSHIFT) /* Left Shift 2A AA */ + PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) /* \ 2B AB */ + PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) /* Z 2C AC */ + PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) /* X 2D AD */ + PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) /* C 2E AE */ + PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) /* V 2F AF */ + + PORT_START("KEY3") /* IN6 */ + PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) /* B 30 B0 */ + PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) /* N 31 B1 */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) /* M 32 B2 */ + PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) /* , 33 B3 */ + PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) /* . 34 B4 */ + PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) /* / 35 B5 */ + PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-Shift") PORT_CODE(KEYCODE_RSHIFT) /* Right Shift 36 B6 */ + PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP * (PrtScr)") PORT_CODE(KEYCODE_ASTERISK ) /* Keypad * (PrtSc) 37 B7 */ + PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT) /* Left Alt 38 B8 */ + PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) /* Space 39 B9 */ + PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) /* Caps Lock 3A BA */ + PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) /* F1 3B BB */ + PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) /* F2 3C BC */ + PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) /* F3 3D BD */ + PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) /* F4 3E BE */ + PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) /* F5 3F BF */ + + PORT_START("KEY4") /* IN7 */ + PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) /* F6 40 C0 */ + PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) /* F7 41 C1 */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) /* F8 42 C2 */ + PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) /* F9 43 C3 */ + PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) /* F10 44 C4 */ + PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NumLock") PORT_CODE(KEYCODE_NUMLOCK) /* Num Lock 45 C5 */ + PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ScrLock (F14)") PORT_CODE(KEYCODE_SCRLOCK) /* Scroll Lock 46 C6 */ + PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 7 (Home)") PORT_CODE(KEYCODE_7_PAD ) /* Keypad 7 (Home) 47 C7 */ + PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 8 (Up)") PORT_CODE(KEYCODE_8_PAD ) /* Keypad 8 (Up arrow) 48 C8 */ + PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 9 (PgUp)") PORT_CODE(KEYCODE_9_PAD ) /* Keypad 9 (PgUp) 49 C9 */ + PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP -") PORT_CODE(KEYCODE_MINUS_PAD) /* Keypad - 4A CA */ + PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 4 (Left)") PORT_CODE(KEYCODE_4_PAD ) /* Keypad 4 (Left arrow) 4B CB */ + PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 5") PORT_CODE(KEYCODE_5_PAD) /* Keypad 5 4C CC */ + PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 6 (Right)") PORT_CODE(KEYCODE_6_PAD ) /* Keypad 6 (Right arrow) 4D CD */ + PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP +") PORT_CODE(KEYCODE_PLUS_PAD) /* Keypad + 4E CE */ + PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 1 (End)") PORT_CODE(KEYCODE_1_PAD ) /* Keypad 1 (End) 4F CF */ + + PORT_START("KEY5") /* IN8 */ + PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 2 (Down)") PORT_CODE(KEYCODE_2_PAD ) /* Keypad 2 (Down arrow) 50 D0 */ + PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 3 (PgDn)") PORT_CODE(KEYCODE_3_PAD ) /* Keypad 3 (PgDn) 51 D1 */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 0 (Ins)") PORT_CODE(KEYCODE_0_PAD ) /* Keypad 0 (Ins) 52 D2 */ + PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP . (Del)") PORT_CODE(KEYCODE_DEL_PAD ) /* Keypad . (Del) 53 D3 */ + PORT_BIT ( 0x0030, 0x0000, IPT_UNUSED ) + PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(84/102)\\") PORT_CODE(KEYCODE_BACKSLASH2) /* Backslash 2 56 D6 */ + PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)F11") PORT_CODE(KEYCODE_F11) /* F11 57 D7 */ + PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)F12") PORT_CODE(KEYCODE_F12) /* F12 58 D8 */ + PORT_BIT ( 0xfe00, 0x0000, IPT_UNUSED ) + + PORT_START("KEY6") /* IN9 */ + PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)KP Enter") PORT_CODE(KEYCODE_ENTER_PAD) /* PAD Enter 60 e0 */ + PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)R-Control") PORT_CODE(KEYCODE_RCONTROL) /* Right Control 61 e1 */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)ALTGR") PORT_CODE(KEYCODE_RALT) /* ALTGR 64 e4 */ + + PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)KP /") PORT_CODE(KEYCODE_SLASH_PAD) /* PAD Slash 62 e2 */ + + PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Home") PORT_CODE(KEYCODE_HOME) /* Home 66 e6 */ + PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Up") PORT_CODE(KEYCODE_UP) /* Up 67 e7 */ + PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Page Up") PORT_CODE(KEYCODE_PGUP) /* Page Up 68 e8 */ + PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Left") PORT_CODE(KEYCODE_LEFT) /* Left 69 e9 */ + PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Right") PORT_CODE(KEYCODE_RIGHT) /* Right 6a ea */ + PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)End") PORT_CODE(KEYCODE_END) /* End 6b eb */ + PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Down") PORT_CODE(KEYCODE_DOWN) /* Down 6c ec */ + PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Page Down") PORT_CODE(KEYCODE_PGDN) /* Page Down 6d ed */ + PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Insert") PORT_CODE(KEYCODE_INSERT) /* Insert 6e ee */ + PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Delete") PORT_CODE(KEYCODE_DEL) /* Delete 6f ef */ + + PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)PrtScr (F13)") PORT_CODE(KEYCODE_PRTSCR) /* Print Screen 63 e3 */ + PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Pause (F15)") PORT_CODE(KEYCODE_PAUSE) /* Pause 65 e5 */ + + PORT_START("KEY7") /* IN10 */ + PORT_BIT ( 0xffff, 0x0000, IPT_UNUSED ) +#if 0 + PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Print Screen") PORT_CODE(KEYCODE_PRTSCR) /* Print Screen alternate 77 f7 */ + PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Win") /* Left Win 7d fd */ + PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Win") /* Right Win 7e fe */ + PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Menu") /* Menu 7f ff */ +#endif +INPUT_PORTS_END + +ioport_constructor geneve_keyboard_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( genkeys ); +} + +} } } // end namespace bus::ti99::internal + diff --git a/src/devices/bus/ti99/internal/genboard.h b/src/devices/bus/ti99/internal/genboard.h new file mode 100644 index 00000000000..b2d22ac69b1 --- /dev/null +++ b/src/devices/bus/ti99/internal/genboard.h @@ -0,0 +1,206 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + Geneve main board components. + See genboard.c for documentation. + + Michael Zapf + + February 2012: Rewritten as class + +*****************************************************************************/ +#ifndef MAME_BUS_TI99_INTERNAL_GENBOARD_H +#define MAME_BUS_TI99_INTERNAL_GENBOARD_H + +#pragma once + +#include "bus/ti99/ti99defs.h" +#include "bus/ti99/peb/peribox.h" +#include "video/v9938.h" +#include "cpu/tms9900/tms9995.h" +#include "sound/sn76496.h" +#include "machine/mm58274c.h" +#include "machine/at29x.h" +#include "machine/ram.h" + +namespace bus { namespace ti99 { namespace internal { + +/*****************************************************************************/ + +class geneve_keyboard_device : public device_t +{ +public: + geneve_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_WRITE_LINE_MEMBER( reset_line ); + DECLARE_WRITE_LINE_MEMBER( send_scancodes ); + DECLARE_WRITE_LINE_MEMBER( clock_control ); + uint8_t get_recent_key(); + + template static devcb_base &static_set_int_callback(device_t &device, Object &&cb) { return downcast(device).m_interrupt.set_callback(std::forward(cb)); } + +protected: + void device_start() override; + void device_reset() override; + ioport_constructor device_input_ports() const override; + void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + devcb_write_line m_interrupt; // Keyboard interrupt to console + required_ioport_array<8> m_keys; + +private: + static constexpr unsigned KEYQUEUESIZE = 256; + static constexpr unsigned MAXKEYMSGLENGTH = 10; + static constexpr unsigned KEYAUTOREPEATDELAY = 30; + static constexpr unsigned KEYAUTOREPEATRATE = 6; + + void post_in_key_queue(int keycode); + void signal_when_key_available(); + void poll(); + + bool m_key_reset; + int m_key_queue_length; + uint8_t m_key_queue[KEYQUEUESIZE]; + int m_key_queue_head; + bool m_key_in_buffer; + uint32_t m_key_state_save[4]; + bool m_key_numlock_state; + + int m_key_ctrl_state; + int m_key_alt_state; + int m_key_real_shift_state; + + bool m_key_fake_shift_state; + bool m_key_fake_unshift_state; + + int m_key_autorepeat_key; + int m_key_autorepeat_timer; + + bool m_keep_keybuf; + bool m_keyboard_clock; + + emu_timer* m_timer; +}; + +#define MCFG_GENEVE_KBINT_HANDLER( _intcallb ) \ + devcb = &bus::ti99::internal::geneve_keyboard_device::static_set_int_callback( *device, DEVCB_##_intcallb ); + +/*****************************************************************************/ + +class geneve_mapper_device : public device_t +{ +public: + geneve_mapper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void set_geneve_mode(bool geneve); + void set_direct_mode(bool direct); + void set_cartridge_size(int size); + void set_cartridge_writable(int base, bool write); + void set_video_waitstates(bool wait); + void set_extra_waitstates(bool wait); + + DECLARE_READ8_MEMBER( readm ); + DECLARE_WRITE8_MEMBER( writem ); + DECLARE_SETOFFSET_MEMBER( setoffset ); + + DECLARE_INPUT_CHANGED_MEMBER( settings_changed ); + + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( dbin_in ); + + // PFM support + DECLARE_WRITE_LINE_MEMBER( pfm_select_lsb ); + DECLARE_WRITE_LINE_MEMBER( pfm_select_msb ); + DECLARE_WRITE_LINE_MEMBER( pfm_output_enable ); + + template static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) { return downcast(device).m_ready.set_callback(std::forward(cb)); } + +protected: + void device_start() override; + void device_reset() override; + +private: + // GROM simulation + bool m_gromwaddr_LSB; + bool m_gromraddr_LSB; + int m_grom_address; + DECLARE_READ8_MEMBER( read_grom ); + DECLARE_WRITE8_MEMBER( write_grom ); + + // wait states + void set_wait(int min); + void set_ext_wait(int min); + bool m_video_waitstates; + bool m_extra_waitstates; + bool m_ready_asserted; + + bool m_read_mode; + + bool m_debug_no_ws; + + // Mapper function + typedef struct + { + int function; + offs_t offset; + offs_t physaddr; + } decdata; + + bool m_geneve_mode; + bool m_direct_mode; + int m_cartridge_size; + bool m_cartridge_secondpage; + bool m_cartridge6_writable; + bool m_cartridge7_writable; + int m_map[8]; + + void decode(address_space& space, offs_t offset, bool read_mode, decdata* dec); + decdata m_decoded; + + // Genmod modifications + bool m_turbo; + bool m_genmod; + bool m_timode; + + // PFM mod (0 = none, 1 = AT29C040, 2 = AT29C040A) + DECLARE_READ8_MEMBER( read_from_pfm ); + DECLARE_WRITE8_MEMBER( write_to_pfm ); + void set_boot_rom(int selection); + int m_pfm_mode; + int m_pfm_bank; + bool m_pfm_output_enable; + + // SRAM access + int m_sram_mask; + int m_sram_val; + + // Ready line to the CPU + devcb_write_line m_ready; + + // Counter for the wait states. + int m_waitcount; + int m_ext_waitcount; + + // Devices + required_device m_clock; + required_device m_cpu; + required_device m_pfm512; + required_device m_pfm512a; + required_device m_sound; + + required_device m_keyboard; + required_device m_video; + required_device m_peribox; + uint8_t* m_eprom; + required_device m_sram; + required_device m_dram; +}; + +#define MCFG_GENEVE_READY_HANDLER( _intcallb ) \ + devcb = &bus::ti99::internal::geneve_mapper_device::static_set_ready_callback( *device, DEVCB_##_intcallb ); + +} } } // end namespace bus::ti99::internal + +DECLARE_DEVICE_TYPE_NS(GENEVE_KEYBOARD, bus::ti99::internal, geneve_keyboard_device) +DECLARE_DEVICE_TYPE_NS(GENEVE_MAPPER, bus::ti99::internal, geneve_mapper_device) + +#endif // MAME_BUS_TI99_INTERNAL_GENBOARD_H diff --git a/src/devices/bus/ti99/internal/gromport.cpp b/src/devices/bus/ti99/internal/gromport.cpp new file mode 100644 index 00000000000..b8cf3d95599 --- /dev/null +++ b/src/devices/bus/ti99/internal/gromport.cpp @@ -0,0 +1,3040 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/*************************************************************************** + GROM port - the cartridge port of the TI-99/4, TI-99/4A, and + TI-99/8 console. + + The name refers to the main intended application scenario, that is, + to host cartridges with GROMs. The second, wider port of the console is + called I/O port and connects to the Peripheral Expansion System + (see peribox.h). + + LEFT + + /RESET 1||2 GND + D7 3||4 CRUCLK + D6 5||6 CRUIN + D5 7||8 A15/CRUOUT + D4 9||10 A13 + D3 11||12 A12 + D2 13||14 A11 + D1 15||16 A10 + D0 17||18 A9 + +5V 19||20 A8 + /GS 21||22 A7 + A14 23||24 A3 + DBIN 25||26 A6 + GRMCLK 27||28 A5 + -5V 29||30 A4 + READY 31||32 /WE + GND 33||34 /ROMG + GND 35||36 GND + + RIGHT + + Address bus line ordering, according to TI convention, is A0 (MSB) ... A15 (LSB). + A0, A1, and A2 are not delivered to the port but decoded in the console: + + /ROMG is asserted for A0/A1/A2 = 011 (addresses 6000 - 7fff) + /GS is asserted for A0...A5 = 100110 (addresses 9800 - 9bff) + + This means that a maximum of 8 KiB of direct memory space can be accessed. + The /GS line is used to enable GROM circuits on the board (serial ROMs with + own address counter, see tmc0430.h). + + When a cartridge is inserted the /RESET line is pulled to ground, which + via a R/C component pulls down the /RESET input of the timer circuit for + a short time, which in turn resets the CPU. In order to dump cartridges, + a common procedure was to tape the /RESET line of the cartridge. However, + inserting a cartridge without resetting often caused so much data bus noise + that the console usually locked up. + + ---------------- + + The TI-99/4A computer was strictly designed for cartridge usage. The basic + console had only little directly accessible RAM and offered no ways to + write machine language programs; cartridges were intended to add various + capabilities, or just for running games. + + Beside the seemingly simple handling, Texas Instruments had own intentions + behind their cartridge strategy. With only 8 KiB of direct access memory, a + major part of the cartridge code had to be stored in GROMs, which had to be + licensed from Texas Instruments. Thus they kept firm control over all + software development. + + Over the years, and with the increasingly difficult market situations, + TI's policies seem to have changed. This may be the reason that the built-in + operating system actually allowed for running ROM-only cartridges until TI + clipped out this part in the OS, banning cartridges without GROMs. Consoles + with this modification were produced in 1983, TI's last year in the home + computer business. + + Although only 8 KiB were available for direct addressing, clever techniques + were invented by third-party manufacturers. The first extension was utilized + by TI themselves in the Extended Basic cartridge which offers two banks + of ROM contents. Switching between the banks is achieved by writing a value + to the ROM space at 6000 or 6002. Later, cartridges with much more memory + space were created, up to the Super Space II cartridge with 128 KiB of + buffered SRAM. + + ---------------- + + From the console case layout the GROM port was intended for a single + cartridge only. Although never officially released, the operating system + of the TI console supported a multi-cartridge extender with software + switching. There were also extenders based on hardware switching (like + the Navarone Widget). + + This emulation offers both variants as slot options: + + -gromport single : default single cartridge connector + -gromport multi : software-switchable 4-slot cartridge extender + -gromport gkracker : GRAM Kracker + + The last option enables another popular device, the GRAM Kracker. This is + a device to be plugged into the cartridge slot with five manual switches + at the front and an own cartridge slot at its top. It contains buffered + SRAM, a built-in ROM, and a GROM simulator which simulates GROM behaviour + when accessing the buffered RAM. Its main use is to provide editable + storage for the read-only cartridge contents. Cartridges can be plugged + into its slot; their contents can be read and written to disk, modified as + needed, and loaded into the buffered RAM. Even the console GROMs can be + copied into the device; despite running in parallel, the GROM simulator + is able to override the console GROMs, thus allowing the user to install + a customized OS. + +***************************************************************************/ +#include "emu.h" +#include "gromport.h" +#include "emuopts.h" +#include "image.h" +#include "softlist.h" +#include "unzip.h" +#include "xmlfile.h" + +DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT, bus::ti99::internal, gromport_device, "gromport", "TI-99 Cartridge port") +DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT_SINGLE, bus::ti99::internal, ti99_single_cart_conn_device, "ti99_scartconn", "TI-99 Standard cartridge connector") +DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT_MULTI, bus::ti99::internal, ti99_multi_cart_conn_device, "ti99_mcartconn", "TI-99 Multi-cartridge extender") +DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT_GK, bus::ti99::internal, ti99_gkracker_device, "ti99_gkracker", "Miller's Graphics GRAM Kracker") +DEFINE_DEVICE_TYPE_NS(TI99_CART, bus::ti99::internal, ti99_cartridge_device, "ti99cart", "TI-99 cartridge") + +namespace bus { namespace ti99 { namespace internal { + +#define TRACE_RPK 0 +#define TRACE_CHANGE 0 +#define TRACE_ILLWRITE 1 +#define TRACE_CONFIG 0 +#define TRACE_READ 0 +#define TRACE_WRITE 0 +#define TRACE_GROM 0 +#define TRACE_GKRACKER 0 +#define TRACE_CRU 0 +#define TRACE_BANKSWITCH 0 + +#define GROM3_TAG "grom3" +#define GROM4_TAG "grom4" +#define GROM5_TAG "grom5" +#define GROM6_TAG "grom6" +#define GROM7_TAG "grom7" + +#define CARTGROM_TAG "grom_contents" +#define CARTROM_TAG "rom_contents" +#define GKRACKER_ROM_TAG "gkracker_rom" +#define GKRACKER_NVRAM_TAG "gkracker_nvram" + +namespace { + +enum rpk_open_error +{ + RPK_OK, + RPK_NOT_ZIP_FORMAT, + RPK_CORRUPT, + RPK_OUT_OF_MEMORY, + RPK_XML_ERROR, + RPK_INVALID_FILE_REF, + RPK_ZIP_ERROR, + RPK_ZIP_UNSUPPORTED, + RPK_MISSING_RAM_LENGTH, + RPK_INVALID_RAM_SPEC, + RPK_UNKNOWN_RESOURCE_TYPE, + RPK_INVALID_RESOURCE_REF, + RPK_INVALID_LAYOUT, + RPK_MISSING_LAYOUT, + RPK_NO_PCB_OR_RESOURCES, + RPK_UNKNOWN_PCB_TYPE +}; + + +const char *const error_text[16] = +{ + "No error", + "Not a RPK (zip) file", + "Module definition corrupt", + "Out of memory", + "XML format error", + "Invalid file reference", + "Zip file error", + "Unsupported zip version", + "Missing RAM length", + "Invalid RAM specification", + "Unknown resource type", + "Invalid resource reference", + "layout.xml not valid", + "Missing layout", + "No pcb or resource found", + "Unknown pcb type" +}; + + +class rpk_exception +{ +public: + rpk_exception(rpk_open_error value): m_err(value), m_detail(nullptr) { } + rpk_exception(rpk_open_error value, const char* detail) : m_err(value), m_detail(detail) { } + + const char* to_string() + { + // FIXME: this leaks memory - in some cases it returns a new buffer, in other cases it returns a pointer to a static string, so the caller can't know whether it needs to be cleaned up + if (m_detail==nullptr) return error_text[(int)m_err]; + std::string errormsg = std::string(error_text[(int)m_err]).append(": ").append(m_detail); + return core_strdup(errormsg.c_str()); + } + +private: + rpk_open_error m_err; + const char* m_detail; +}; + +struct pcb_type +{ + int id; + const char* name; +}; + +} // anonymous namespace + + +/************************************************************************* + RPK support +*************************************************************************/ + +class ti99_cartridge_device::rpk_socket +{ +public: + rpk_socket(const char *id, int length, uint8_t *contents); + rpk_socket(const char *id, int length, uint8_t *contents, const char *pathname); + ~rpk_socket() {} + + const char* id() { return m_id; } + int get_content_length() { return m_length; } + uint8_t* get_contents() { return m_contents; } + bool persistent_ram() { return m_pathname != nullptr; } + const char* get_pathname() { return m_pathname; } + void cleanup() { if (m_contents != nullptr) global_free_array(m_contents); } + +private: + const char* m_id; + uint32_t m_length; + uint8_t* m_contents; + const char* m_pathname; +}; + +class ti99_cartridge_device::rpk_reader +{ +public: + rpk_reader(const pcb_type *types) : m_types(types) { } + + rpk *open(emu_options &options, const char *filename, const char *system_name); + +private: + int find_file(util::archive_file &zip, const char *filename, uint32_t crc); + std::unique_ptr load_rom_resource(util::archive_file &zip, util::xml::data_node const* rom_resource_node, const char* socketname); + std::unique_ptr load_ram_resource(emu_options &options, util::xml::data_node const* ram_resource_node, const char* socketname, const char* system_name); + const pcb_type* m_types; +}; + +class ti99_cartridge_device::rpk +{ + friend class rpk_reader; +public: + rpk(emu_options& options, const char* sysname); + ~rpk(); + + int get_type(void) { return m_type; } + uint8_t* get_contents_of_socket(const char *socket_name); + int get_resource_length(const char *socket_name); + void close(); + +private: + emu_options& m_options; // need this to find the path to the nvram files + int m_type; + //const char* m_system_name; // need this to find the path to the nvram files + std::unordered_map> m_sockets; + + void add_socket(const char* id, std::unique_ptr newsock); +}; + +gromport_device::gromport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : bus8z_device(mconfig, TI99_GROMPORT, tag, owner, clock), + device_slot_interface(mconfig, *this), + m_connector(nullptr), + m_reset_on_insert(true), + m_console_ready(*this), + m_console_reset(*this) +{ } + +/* + Reading via the GROM port. Only 13 address lines are passed through + on the TI-99/4A, and 14 lines on the TI-99/8. +*/ +READ8Z_MEMBER(gromport_device::readz) +{ + if (m_connector != nullptr) + { + m_connector->readz(space, offset & m_mask, value); + if (TRACE_READ) if (m_romgq) logerror("Read %04x -> %02x\n", offset | 0x6000, *value); + } +} + +/* + Writing via the GROM port. Only 13 address lines are passed through + on the TI-99/4A, and 14 lines on the TI-99/8. +*/ +WRITE8_MEMBER(gromport_device::write) +{ + if (m_connector != nullptr) + { + if (TRACE_WRITE) if (m_romgq) logerror("Write %04x <- %02x\n", offset | 0x6000, data); + m_connector->write(space, offset & m_mask, data); + } +} + +READ8Z_MEMBER(gromport_device::crureadz) +{ + if (m_connector != nullptr) + m_connector->crureadz(space, offset, value); +} + +WRITE8_MEMBER(gromport_device::cruwrite) +{ + if (m_connector != nullptr) + m_connector->cruwrite(space, offset, data); +} + +WRITE_LINE_MEMBER(gromport_device::ready_line) +{ + m_console_ready(state); +} + +/* + Asserted when the console addresses cartridge rom. +*/ +WRITE_LINE_MEMBER(gromport_device::romgq_line) +{ + m_romgq = state; + if (m_connector != nullptr) + m_connector->romgq_line(state); +} + +WRITE_LINE_MEMBER(gromport_device::gclock_in) +{ + if (m_connector != nullptr) + m_connector->gclock_in(state); +} + +/* + Combined GROM control lines. +*/ +WRITE8_MEMBER( gromport_device::set_gromlines ) +{ + if (m_connector != nullptr) + m_connector->set_gromlines(space, offset, data); +} + +void gromport_device::device_start() +{ + m_console_ready.resolve(); + m_console_reset.resolve(); + + save_item(NAME(m_romgq)); +} + +void gromport_device::device_reset() +{ + m_reset_on_insert = (ioport("CARTRESET")->read()==0x01); +} + +/* + Shall we reset the console when a cartridge has been inserted? + This is triggered by the cartridge by pulling down /RESET via a capacitor. + Accordingly, when we put a tape over the /RESET contact we can avoid the + reset, which is useful when we want to swap the cartridges while a program + is runnning. +*/ +void gromport_device::cartridge_inserted() +{ + if (m_reset_on_insert) + { + m_console_reset(ASSERT_LINE); + m_console_reset(CLEAR_LINE); + } +} + +/* + Find out whether the GROMs in the cartridge are idle. In that case, + cut the clock line. +*/ +bool gromport_device::is_grom_idle() +{ + if (m_connector != nullptr) + return m_connector->is_grom_idle(); + else + return false; +} + +void gromport_device::device_config_complete() +{ + m_connector = downcast(subdevices().first()); +} + +INPUT_PORTS_START(gromport) + PORT_START( "CARTRESET" ) + PORT_CONFNAME( 0x01, 0x01, "RESET on cartridge insert" ) + PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) + PORT_CONFSETTING( 0x01, DEF_STR( On ) ) +INPUT_PORTS_END + +ioport_constructor gromport_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(gromport); +} + +/*************************************************************************** + Different versions of cartridge connections + + single: the standard console connector, one cartridge + multi: a multi-cart expander, up to 4 cartridges with software selection + gkracker: GRAMKracker, a device with NVRAM which allows the user to copy + the contents of the cartridge plugged into its slot into the NVRAM + and to modify it. + +***************************************************************************/ + +cartridge_connector_device::cartridge_connector_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : bus8z_device(mconfig, type, tag, owner, clock), + m_gromport(nullptr) +{ +} + +WRITE_LINE_MEMBER( cartridge_connector_device::ready_line ) +{ + m_gromport->ready_line(state); +} + +void cartridge_connector_device::device_config_complete() +{ + m_gromport = static_cast(owner()); +} + +ti99_single_cart_conn_device::ti99_single_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : cartridge_connector_device(mconfig, TI99_GROMPORT_SINGLE, tag, owner, clock), + m_cartridge(nullptr) +{ +} + +READ8Z_MEMBER(ti99_single_cart_conn_device::readz) +{ + // Pass through + m_cartridge->readz(space, offset, value); +} + +WRITE8_MEMBER(ti99_single_cart_conn_device::write) +{ + // Pass through + m_cartridge->write(space, offset, data); +} + +READ8Z_MEMBER(ti99_single_cart_conn_device::crureadz) +{ + // Pass through + m_cartridge->crureadz(space, offset, value); +} + +WRITE8_MEMBER(ti99_single_cart_conn_device::cruwrite) +{ + // Pass through + m_cartridge->cruwrite(space, offset, data); +} + +WRITE_LINE_MEMBER(ti99_single_cart_conn_device::romgq_line) +{ + // Pass through + m_cartridge->romgq_line(state); +} + +/* + Combined select lines +*/ +WRITE8_MEMBER(ti99_single_cart_conn_device::set_gromlines) +{ + // Pass through + m_cartridge->set_gromlines(space, offset, data); +} + + +WRITE_LINE_MEMBER(ti99_single_cart_conn_device::gclock_in) +{ + // Pass through + m_cartridge->gclock_in(state); +} + +/* + Check whether the GROMs are idle. +*/ +bool ti99_single_cart_conn_device::is_grom_idle() +{ + return m_cartridge->is_grom_idle(); +} + +void ti99_single_cart_conn_device::device_start() +{ + m_cartridge = static_cast(subdevices().first()); +} + +void ti99_single_cart_conn_device::device_reset() +{ + m_cartridge->set_slot(0); +} + +static MACHINE_CONFIG_START( single_slot ) + MCFG_DEVICE_ADD("cartridge", TI99_CART, 0) +MACHINE_CONFIG_END + +machine_config_constructor ti99_single_cart_conn_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( single_slot ); +} + +/********************************************************/ + +/* + The multi-cartridge extender + + This is a somewhat mythical device which was never available for the normal + customer, but there are reports of the existence of such a device + in development labs or demonstrations. + + The interesting thing about this is that the OS of the console + fully supports this multi-cartridge extender, providing a selection + option on the screen to switch between different plugged-in + cartridges. + + The switching is possible by decoding address lines that are reserved + for GROM access. GROMs are accessed via four separate addresses + 9800, 9802, 9C00, 9C02. The addressing scheme looks like this: + + 1001 1Wxx xxxx xxM0 W = write(1), read(0), M = address(1), data(0) + + This leaves 8 bits (256 options) which are not decoded inside the + console. As the complete address is routed to the port, some circuit + just needs to decode the xxx lines and turn on the respective slot. + + One catch must be considered: Some cartridges contain ROMs which are + directly accessed and not via ports. This means that the ROMs must + be activated according to the slot that is selected. + + Another issue: Each GROM contains an own address counter and an ID. + According to the ID the GROM only delivers data if the address counter + is within the ID area (0 = 0000-1fff, 1=2000-3fff ... 7=e000-ffff). + Thus it is essential that all GROMs stay in sync with their address + counters. We have to route all address settings to all slots and their + GROMs, even when the slot has not been selected before. The selected + just shows its effect when data is read. In this case, only the + data from the selected slot will be delivered. + + This may be considered as a design flaw within the complete cartridge system + which eventually led to TI not manufacturing that device for the broad + market. +*/ + +#define AUTO -1 + +ti99_multi_cart_conn_device::ti99_multi_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : cartridge_connector_device(mconfig, TI99_GROMPORT_MULTI, tag, owner, clock), + m_active_slot(0), + m_fixed_slot(0), + m_next_free_slot(0) +{ +} + +/* + Activates a slot in the multi-cartridge extender. + Setting the slot is done by accessing the GROM ports using a + specific address: + + slot 0: 0x9800 (0x9802, 0x9c00, 0x9c02) : cartridge1 + slot 1: 0x9804 (0x9806, 0x9c04, 0x9c06) : cartridge2 + ... + slot 15: 0x983c (0x983e, 0x9c3c, 0x9c3e) : cartridge16 + + Scheme: + + 1001 1Wxx xxxx xxM0 (M=mode; M=0: data, M=1: address; W=write) + + The following addresses are theoretically available, but the + built-in OS does not use them; i.e. cartridges will not be + included in the selection list, and their features will not be + found by lookup, but they could be accessed directly by user + programs. + slot 16: 0x9840 (0x9842, 0x9c40, 0x9c42) + ... + slot 255: 0x9bfc (0x9bfe, 0x9ffc, 0x9ffe) + + Setting the GROM base should select one cartridge, but the ROMs in the + CPU space must also be switched. As there is no known special mechanism + we assume that by switching the GROM base, the ROM is automatically + switched. + + Caution: This means that cartridges which do not have at least one + GROM cannot be switched with this mechanism. + + We assume that the slot number is already calculated in the caller: + slotnumber>=0 && slotnumber<=255 + + NOTE: The OS will stop searching when it finds slots 1 and 2 empty. + Interestingly, cartridge subroutines are found nevertheless, even when + the cartridge is plugged into a higher slot. +*/ +void ti99_multi_cart_conn_device::set_slot(int slotnumber) +{ + if (TRACE_CHANGE) + if (m_active_slot != slotnumber) logerror("Setting cartslot to %d\n", slotnumber); + + if (m_fixed_slot==AUTO) + m_active_slot = slotnumber; + else + m_active_slot = m_fixed_slot; +} + +int ti99_multi_cart_conn_device::get_active_slot(bool changebase, offs_t offset) +{ + int slot; + if (changebase) + { + // GROM selected? + if (m_grom_selected) set_slot((offset>>2) & 0x00ff); + } + slot = m_active_slot; + return slot; +} + +void ti99_multi_cart_conn_device::insert(int index, ti99_cartridge_device* cart) +{ + if (TRACE_CHANGE) logerror("Insert slot %d\n", index); + m_cartridge[index] = cart; + m_gromport->cartridge_inserted(); +} + +void ti99_multi_cart_conn_device::remove(int index) +{ + if (TRACE_CHANGE) logerror("Remove slot %d\n", index); + m_cartridge[index] = nullptr; +} + +WRITE_LINE_MEMBER(ti99_multi_cart_conn_device::romgq_line) +{ + m_readrom = state; + + // Propagate to all slots + for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) + { + if (m_cartridge[i] != nullptr) + { + m_cartridge[i]->romgq_line(state); + } + } +} + +/* + Combined select lines +*/ +WRITE8_MEMBER(ti99_multi_cart_conn_device::set_gromlines) +{ + // GROM selected? + m_grom_selected = (data != 0); + + // Propagate to all slots + for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) + { + if (m_cartridge[i] != nullptr) + { + m_cartridge[i]->set_gromlines(space, offset, data); + } + } +} + +WRITE_LINE_MEMBER(ti99_multi_cart_conn_device::gclock_in) +{ + // Propagate to all slots + for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) + { + if (m_cartridge[i] != nullptr) + { + m_cartridge[i]->gclock_in(state); + } + } +} + +READ8Z_MEMBER(ti99_multi_cart_conn_device::readz) +{ + int slot = get_active_slot(true, offset); + + // If we have a GROM access, we need to send the read request to all + // attached cartridges so the slot is irrelevant here. Each GROM + // contains an internal address counter, and we must make sure they all stay in sync. + if (m_grom_selected) + { + for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) + { + if (m_cartridge[i] != nullptr) + { + uint8_t newval = *value; + m_cartridge[i]->readz(space, offset, &newval, 0xff); + if (i==slot) + { + *value = newval; + } + } + } + } + else + { + if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr) + { + m_cartridge[slot]->readz(space, offset, value, 0xff); + } + } +} + +WRITE8_MEMBER(ti99_multi_cart_conn_device::write) +{ + // Same issue as above (read) + // We don't have GRAM cartridges, anyway, so it's just used for setting the address. + if (m_grom_selected) + { + for (auto & elem : m_cartridge) + { + if (elem != nullptr) + { + elem->write(space, offset, data, 0xff); + } + } + } + else + { + int slot = get_active_slot(true, offset); + if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr) + { + // logerror("writing %04x (slot %d) <- %02x\n", offset, slot, data); + m_cartridge[slot]->write(space, offset, data, 0xff); + } + } +} + +READ8Z_MEMBER(ti99_multi_cart_conn_device::crureadz) +{ + int slot = get_active_slot(false, offset); + /* Sanity check. Higher slots are always empty. */ + if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) + return; + + if (m_cartridge[slot] != nullptr) + { + m_cartridge[slot]->crureadz(space, offset, value); + } +} + +WRITE8_MEMBER(ti99_multi_cart_conn_device::cruwrite) +{ + int slot = get_active_slot(true, offset); + + /* Sanity check. Higher slots are always empty. */ + if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) + return; + + if (m_cartridge[slot] != nullptr) + { + m_cartridge[slot]->cruwrite(space, offset, data); + } +} + +/* + Check whether the GROMs are idle. Just ask the currently + active cartridge. +*/ +bool ti99_multi_cart_conn_device::is_grom_idle() +{ + /* Sanity check. Higher slots are always empty. */ + if (m_active_slot >= NUMBER_OF_CARTRIDGE_SLOTS) + return false; + + if (m_cartridge[m_active_slot] != nullptr) + return m_cartridge[m_active_slot]->is_grom_idle(); + + return false; +} + +void ti99_multi_cart_conn_device::device_start() +{ + m_next_free_slot = 0; + m_active_slot = 0; + for (auto & elem : m_cartridge) + { + elem = nullptr; + } + save_item(NAME(m_readrom)); + save_item(NAME(m_active_slot)); + save_item(NAME(m_fixed_slot)); + save_item(NAME(m_next_free_slot)); +} + +void ti99_multi_cart_conn_device::device_reset(void) +{ + m_active_slot = 0; + m_fixed_slot = ioport("CARTSLOT")->read() - 1; + m_grom_selected = false; +} + +static MACHINE_CONFIG_START( multi_slot ) + MCFG_DEVICE_ADD("cartridge1", TI99_CART, 0) + MCFG_DEVICE_ADD("cartridge2", TI99_CART, 0) + MCFG_DEVICE_ADD("cartridge3", TI99_CART, 0) + MCFG_DEVICE_ADD("cartridge4", TI99_CART, 0) +MACHINE_CONFIG_END + +machine_config_constructor ti99_multi_cart_conn_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( multi_slot ); +} + +INPUT_CHANGED_MEMBER( ti99_multi_cart_conn_device::switch_changed ) +{ + if (TRACE_CHANGE) logerror("Slot changed %d - %d\n", (int)((uint64_t)param & 0x07), newval); + m_active_slot = m_fixed_slot = newval - 1; +} + +INPUT_PORTS_START(multi_slot) + PORT_START( "CARTSLOT" ) + PORT_DIPNAME( 0x0f, 0x00, "Multi-cartridge slot" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_multi_cart_conn_device, switch_changed, 0) + PORT_DIPSETTING( 0x00, "Auto" ) + PORT_DIPSETTING( 0x01, "Slot 1" ) + PORT_DIPSETTING( 0x02, "Slot 2" ) + PORT_DIPSETTING( 0x03, "Slot 3" ) + PORT_DIPSETTING( 0x04, "Slot 4" ) +INPUT_PORTS_END + +ioport_constructor ti99_multi_cart_conn_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(multi_slot); +} + +/************************************************************************** + + The GRAM Kracker was manufactured by Miller's Graphics and designed to + fit into the cartridge slot. + + It offers one own cartridge slot at the top side and a row of switches + at its front. It contains buffered SRAM circuits; the base version has + 56 KiB, and the extended version has 80 KiB. + + The operation of the GRAM Kracker is a bit complex and most likely + bound to fail when you have no manual. Accordingly, this emulation is + neither simpler nor more difficult to use. + + Concept of operation: + + Loader: The GRAM Kracker contains a small loader utility + which allows you to dump cartridges and to load the contents into the + SRAM of the GK. This loader utility is active when the switch 5 is put + into "Loader on" state. The activated loader hides the TI BASIC + interpreter in the console. + + Cartridges: When a cartridge is plugged into the GK the contents may be + dumped and saved to disk by the loader. They cannot be directly copied + into the GK because the memory locations are hidden by the cartridge. + + Loading the cartridge into the SRAM: With the cartridge unplugged, dumps + can be loaded into the SRAM using the loader. This is one major use case + of the GK, that is, to load dumps from disk, and in particular modified + dumps. (There is no checksum, so contents may be freely changed.) + + Console dump: The GK is also able to dump the console GROMs and also to + load them into the SRAM (only in the extended version). Due to a + peculiarity of the TI console design it is possible to override the + console GROMs with the contents in the cartridge slot. + + A standard procedure for use with the GK: + + Save cartridge: + + - Put switches to [Normal | OpSys | TI BASIC | W/P | Loader On] + - Insert a disk image into disk drive 1 + - Plug in a cartridge + - Reset the console (done automatically here) + - Visit the option screen, press 1 for GRAM KRACKER + - In the GK loader, select 2 for Save Module + - Follow the on-screen instructions. Switches are set via the dip switch menu. + - Enter a target file name + - Saving is complete when the Save operation has been unmarked. + + Load cartridge: + + - Put switches to [Normal | OpSys | TI BASIC | W/P | Loader On] + - Insert a disk image into disk drive 1 + - Make sure no cartridge is plugged in + - Press 1 for GRAM KRACKER + - Press 3 for Init Module space; follow instructions + - Press 1 for Load Module; specify file name on disk + - Loading is complete when the Load operation has been unmarked. + + Memory organisation: + + The console has three GROMs with 6 KiB size and occupying 8 KiB of address + space each. These are called GROMs 0, 1, and 2. GROM 0 contains the common + routines for the computer operation; GROMs 1 and 2 contain TI BASIC. + + Memory locations 6000-7fff are assigned to cartridge ROMs; in some + cartridges, a second ROM bank can be used by writing a value to a special + ROM access. This way, instead of 8 KiB we often have 16 KiB at these + locations. + + Each cartridge can host up to 5 GROMs (called GROM 3, 4, 5, 6, and 7). + As in the console, each one occupies 6 KiB in an 8 KiB window. + + The GRAM Kracker offers + + - a loader in an own GROM 1 (which hides the console GROM 1 when active, + so we have no BASIC anymore). The contents of the loader must be found + by the emulator in a file named ti99_gkracker.zip. + + - a complete set of 8 (simulated) GRAMs with full 8 KiB each (done by a + simple addressing circuit); the basic version only offered GRAMs 3-7 + + - 16 KiB of RAM memory space for the 6000-7fff area (called "bank 1" and "bank 2") + + Notes: + + - it is mandatory to turn off the loader when loading into GRAM 1, but only + after prompted in the on-screen instructions, or the loader will crash + - GRAM0 must be properly loaded if switch 2 is set to GRAM0 and the computer is reset + - Switch 4 must not be in W/P position (write protect) when loading data + into the GK (either other position will do). + + +***************************************************************************/ +enum +{ + GK_OFF = 0, + GK_NORMAL = 1, + GK_GRAM0 = 0, + GK_OPSYS = 1, + GK_GRAM12 = 0, + GK_TIBASIC = 1, + GK_BANK1 = 0, + GK_WP = 1, + GK_BANK2 = 2, + GK_LDON = 0, + GK_LDOFF = 1 +}; + +#define GKSWITCH1_TAG "GKSWITCH1" +#define GKSWITCH2_TAG "GKSWITCH2" +#define GKSWITCH3_TAG "GKSWITCH3" +#define GKSWITCH4_TAG "GKSWITCH4" +#define GKSWITCH5_TAG "GKSWITCH5" + +ti99_gkracker_device::ti99_gkracker_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : cartridge_connector_device(mconfig, TI99_GROMPORT_GK, tag, owner, clock), + device_nvram_interface(mconfig, *this), + m_romspace_selected(false), + m_ram_page(0), + m_grom_address(0), + m_ram_ptr(nullptr), + m_grom_ptr(nullptr), + m_waddr_LSB(false), + m_cartridge(nullptr) +{ +} + +WRITE_LINE_MEMBER(ti99_gkracker_device::romgq_line) +{ + m_romspace_selected = (state==ASSERT_LINE); + // Propagate to the guest + if (m_cartridge != nullptr) m_cartridge->romgq_line(state); +} + +/* + Combined select lines +*/ +WRITE8_MEMBER(ti99_gkracker_device::set_gromlines) +{ + m_grom_selected = (data != 0); + if (m_cartridge != nullptr) m_cartridge->set_gromlines(space, offset, data); +} + +WRITE_LINE_MEMBER(ti99_gkracker_device::gclock_in) +{ + if (m_cartridge != nullptr) m_cartridge->gclock_in(state); +} + +/* + Check whether the GROMs are idle. +*/ +bool ti99_gkracker_device::is_grom_idle() +{ + return (m_cartridge != nullptr)? m_cartridge->is_grom_idle() : false; +} + +READ8Z_MEMBER(ti99_gkracker_device::readz) +{ + if (m_grom_selected) + { + // Reads from the GRAM space of the GRAM Kracker. + int id = ((m_grom_address & 0xe000)>>13)&0x07; + + // The GK does not have a readable address counter, but the console + // GROMs and the GROMs of the guest cartridge will keep our address + // counter up to date. + if ((offset & 0x0002)==0) + { + // Reading data + if (((id==0) && (m_gk_switch[2]==GK_GRAM0)) + || ((id==1) && (m_gk_switch[5]==GK_LDOFF) && (m_gk_switch[3]==GK_GRAM12)) + || ((id==2) && (m_gk_switch[3]==GK_GRAM12)) + || ((id>=3) && (m_gk_switch[1]==GK_NORMAL))) + *value = m_ram_ptr[m_grom_address]; + + if ((id==1) && (m_gk_switch[5]==GK_LDON)) + *value = m_grom_ptr[m_grom_address & 0x1fff]; + + // The GK GROM emulation does not wrap at 8K boundaries. + m_grom_address = (m_grom_address + 1) & 0xffff; + + // Reset the write address flipflop. + m_waddr_LSB = false; + if (TRACE_GKRACKER) logerror("GROM read -> %02x\n", *value); + } + } + + if (m_romspace_selected) + { + // Reads from the RAM space of the GRAM Kracker. + + // RAM is stored behind the GRAM area + // Note that offset is 0000...1fff + // When switch in middle position (WP) do bank select according to page flag + if (m_gk_switch[1] == GK_NORMAL) + { + int base = ((m_gk_switch[4]==GK_BANK1) || ((m_gk_switch[4]==GK_WP) && (m_ram_page==0)))? 0x10000 : 0x12000; + *value = m_ram_ptr[offset | base]; + if (TRACE_GKRACKER) logerror("Read %04x -> %02x\n", offset | 0x6000, *value); + } + } + + // If the guest has GROMs or ROMs they will override the GK contents + if (m_cartridge != nullptr) + { + // For debugging + uint8_t val1 = *value; + + // Read from the guest cartridge. + m_cartridge->readz(space, offset, value, mem_mask); + if (TRACE_GKRACKER) + if (val1 != *value) logerror("Read (from guest) %04x -> %02x\n", offset, *value); + } +} + +WRITE8_MEMBER(ti99_gkracker_device::write) +{ + // write to the guest cartridge if present + if (m_cartridge != nullptr) + { + m_cartridge->write(space, offset, data, mem_mask); + } + + if (m_grom_selected) + { + // Write to the GRAM space of the GRAM Kracker. + if ((offset & 0x0002)==0x0002) + { + // Set address + if (m_waddr_LSB == true) + { + // Accept low address byte (second write) + m_grom_address = (m_grom_address & 0xff00) | data; + m_waddr_LSB = false; + if (TRACE_GKRACKER) logerror("Set GROM address %04x\n", m_grom_address); + } + else + { + // Accept high address byte (first write) + m_grom_address = (m_grom_address & 0x00ff) | (data << 8); + m_waddr_LSB = true; + } + } + else + { + // Write data byte to GRAM area. + if (TRACE_GKRACKER) logerror("GROM write %04x(%04x) <- %02x\n", offset, m_grom_address, data); + + // According to manual: + // Writing to GRAM 0: switch 2 set to GRAM 0 + Write protect switch (4) in 1 or 2 position + // Writing to GRAM 1: switch 3 set to GRAM 1-2 + Loader off (5); write prot has no effect + // Writing to GRAM 2: switch 3 set to GRAM 1-2 (write prot has no effect) + // Writing to GRAM 3-7: switch 1 set to GK_NORMAL, no cartridge inserted + // GK_NORMAL switch has no effect on GRAM 0-2 + + int id = ((m_grom_address & 0xe000)>>13)&0x07; + + if ((id==0 && m_gk_switch[2]==GK_GRAM0 && m_gk_switch[4]!=GK_WP) + || (id==1 && m_gk_switch[3]==GK_GRAM12 && m_gk_switch[5]==GK_LDOFF) + || (id==2 && m_gk_switch[3]==GK_GRAM12) + || (id>=3 && m_gk_switch[1]==GK_NORMAL)) + m_ram_ptr[m_grom_address] = data; + + // The GK GROM emulation does not wrap at 8K boundaries. + m_grom_address = (m_grom_address + 1) & 0xffff; + + // Reset the write address flipflop. + m_waddr_LSB = false; + } + } + + if (m_romspace_selected) + { + // Write to the RAM space of the GRAM Kracker + if (TRACE_GKRACKER) logerror("Write %04x <- %02x\n", offset | 0x6000, data); + + if (m_gk_switch[1] == GK_NORMAL) + { + if (m_gk_switch[4]==GK_BANK1) m_ram_ptr[offset | 0x10000] = data; + else if (m_gk_switch[4]==GK_BANK2) m_ram_ptr[offset | 0x12000] = data; + // Switch in middle position (WP, implies auto-select according to the page flag) + // This is handled like in Extended Basic (using addresses) + else m_ram_page = (offset >> 1) & 1; + } + } +} + +READ8Z_MEMBER( ti99_gkracker_device::crureadz ) +{ + if (m_cartridge != nullptr) m_cartridge->crureadz(space, offset, value); +} + +WRITE8_MEMBER( ti99_gkracker_device::cruwrite ) +{ + if (m_cartridge != nullptr) m_cartridge->cruwrite(space, offset, data); +} + +INPUT_CHANGED_MEMBER( ti99_gkracker_device::gk_changed ) +{ + if (TRACE_GKRACKER) logerror("Input changed %d - %d\n", (int)((uint64_t)param & 0x07), newval); + m_gk_switch[(uint64_t)param & 0x07] = newval; +} + +void ti99_gkracker_device::insert(int index, ti99_cartridge_device* cart) +{ + if (TRACE_CHANGE) logerror("Insert cartridge\n"); + m_cartridge = cart; + // Switch 1 has a third location for resetting. We do the reset by default + // here. It can be turned off in the configuration. + m_gromport->cartridge_inserted(); +} + +void ti99_gkracker_device::remove(int index) +{ + if (TRACE_CHANGE) logerror("Remove cartridge\n"); + m_cartridge = nullptr; +} + +void ti99_gkracker_device::gk_install_menu(const char* menutext, int len, int ptr, int next, int start) +{ + const int base = 0x0000; + m_ram_ptr[base + ptr] = (uint8_t)((next >> 8) & 0xff); + m_ram_ptr[base + ptr+1] = (uint8_t)(next & 0xff); + m_ram_ptr[base + ptr+2] = (uint8_t)((start >> 8) & 0xff); + m_ram_ptr[base + ptr+3] = (uint8_t)(start & 0xff); + + m_ram_ptr[base + ptr+4] = (uint8_t)(len & 0xff); + memcpy(m_ram_ptr + base + ptr+5, menutext, len); +} + +/* + Define the default for the GRAM Kracker device. The memory is preset with + some sample entries which shall indicate that the memory has been tested + by the manufacturer. +*/ +void ti99_gkracker_device::nvram_default() +{ + if (TRACE_GKRACKER) logerror("Creating default NVRAM\n"); + memset(m_ram_ptr, 0, 81920); + + m_ram_ptr[0x6000] = 0xaa; + m_ram_ptr[0x6001] = 0x01; + m_ram_ptr[0x6002] = 0x01; + + m_ram_ptr[0x6006] = 0x60; + m_ram_ptr[0x6007] = 0x20; + + gk_install_menu("GROM 3 OK", 9, 0x60e0, 0, 0x6100); + gk_install_menu("GROM 4 OK", 9, 0x60c0, 0x60e0, 0x6100); + gk_install_menu("GROM 5 OK", 9, 0x60a0, 0x60c0, 0x6100); + gk_install_menu("GROM 6 OK", 9, 0x6080, 0x60a0, 0x6100); + gk_install_menu("PROM OK", 9, 0x6060, 0x6080, 0x6100); + gk_install_menu("RAMS OK", 9, 0x6040, 0x6060, 0x6100); + gk_install_menu("OPTION GRAMS OK", 15, 0x6020, 0x6040, 0x6100); + + m_ram_ptr[0x6100] = 0x0b; // GPL EXIT +} + +void ti99_gkracker_device::nvram_read(emu_file &file) +{ + int readsize = file.read(m_ram_ptr, 81920); + if (TRACE_GKRACKER) logerror("Reading NVRAM\n"); + // If we increased the size, fill the remaining parts with 0 + if (readsize < 81920) + { + memset(m_ram_ptr + readsize, 0, 81920-readsize); + } +} + +void ti99_gkracker_device::nvram_write(emu_file &file) +{ + if (TRACE_GKRACKER) logerror("Writing NVRAM\n"); + file.write(m_ram_ptr, 81920); +} + +void ti99_gkracker_device::device_start() +{ + m_ram_ptr = memregion(GKRACKER_NVRAM_TAG)->base(); + m_grom_ptr = memregion(GKRACKER_ROM_TAG)->base(); + m_cartridge = nullptr; + for (int i=1; i < 6; i++) m_gk_switch[i] = 0; + save_pointer(NAME(m_gk_switch),6); + save_item(NAME(m_romspace_selected)); + save_item(NAME(m_ram_page)); + save_item(NAME(m_grom_address)); + save_item(NAME(m_waddr_LSB)); +} + +void ti99_gkracker_device::device_reset() +{ + m_gk_switch[1] = ioport(GKSWITCH1_TAG)->read(); + m_gk_switch[2] = ioport(GKSWITCH2_TAG)->read(); + m_gk_switch[3] = ioport(GKSWITCH3_TAG)->read(); + m_gk_switch[4] = ioport(GKSWITCH4_TAG)->read(); + m_gk_switch[5] = ioport(GKSWITCH5_TAG)->read(); + m_grom_address = 0; // for the GROM emulation + m_ram_page = 0; + m_waddr_LSB = false; + m_grom_selected = false; +} + +static MACHINE_CONFIG_START( gkracker_slot ) + MCFG_DEVICE_ADD("cartridge", TI99_CART, 0) +MACHINE_CONFIG_END + +/* + The GRAMKracker ROM +*/ +ROM_START( gkracker_rom ) + ROM_REGION(0x14000, GKRACKER_NVRAM_TAG, ROMREGION_ERASE00) + ROM_REGION(0x2000, GKRACKER_ROM_TAG, 0) + ROM_LOAD("gkracker.bin", 0x0000, 0x2000, CRC(86eaaf9f) SHA1(a3bd5257c63e190800921b52dbe3ffa91ad91113)) +ROM_END + +const tiny_rom_entry *ti99_gkracker_device::device_rom_region() const +{ + return ROM_NAME( gkracker_rom ); +} + +machine_config_constructor ti99_gkracker_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( gkracker_slot ); +} + +INPUT_PORTS_START(gkracker) + PORT_START( GKSWITCH1_TAG ) + PORT_DIPNAME( 0x01, 0x01, "GK switch 1" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 1) + PORT_DIPSETTING( 0x00, "GK Off" ) + PORT_DIPSETTING( 0x01, DEF_STR( Normal ) ) + + PORT_START( GKSWITCH2_TAG ) + PORT_DIPNAME( 0x01, 0x01, "GK switch 2" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 2) + PORT_DIPSETTING( 0x00, "GRAM 0" ) + PORT_DIPSETTING( 0x01, "Op Sys" ) + + PORT_START( GKSWITCH3_TAG ) + PORT_DIPNAME( 0x01, 0x01, "GK switch 3" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 3) + PORT_DIPSETTING( 0x00, "GRAM 1-2" ) + PORT_DIPSETTING( 0x01, "TI BASIC" ) + + PORT_START( GKSWITCH4_TAG ) + PORT_DIPNAME( 0x03, 0x01, "GK switch 4" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 4) + PORT_DIPSETTING( 0x00, "Bank 1" ) + PORT_DIPSETTING( 0x01, "W/P" ) + PORT_DIPSETTING( 0x02, "Bank 2" ) + + PORT_START( GKSWITCH5_TAG ) + PORT_DIPNAME( 0x01, 0x00, "GK switch 5" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 5) + PORT_DIPSETTING( 0x00, "Loader On" ) + PORT_DIPSETTING( 0x01, "Loader Off" ) +INPUT_PORTS_END + +ioport_constructor ti99_gkracker_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(gkracker); +} + +/*************************************************************************** + Cartridge implementation + + Every cartridge is an instance of ti99_cartridge_device, implementing the + device_image_interface. This means it is capable of loading cartridge + data into its memory locations. All memory locations are organised as + regions. + + The different cartridge versions are realised by different PCB instances. + All PCBs are subclassed from ti99_cartridge_pcb. + +***************************************************************************/ +enum +{ + PCB_STANDARD=1, + PCB_PAGED12K, + PCB_PAGED16K, + PCB_MINIMEM, + PCB_SUPER, + PCB_MBX, + PCB_PAGED379I, + PCB_PAGED378, + PCB_PAGED377, + PCB_PAGEDCRU, + PCB_GROMEMU +}; + +static const pcb_type pcbdefs[] = +{ + { PCB_STANDARD, "standard" }, + { PCB_PAGED12K, "paged12k" }, + { PCB_PAGED16K, "paged" }, + { PCB_MINIMEM, "minimem" }, + { PCB_SUPER, "super" }, + { PCB_MBX, "mbx" }, + { PCB_PAGED379I, "paged379i" }, + { PCB_PAGED378, "paged378" }, + { PCB_PAGED377, "paged377" }, + { PCB_PAGEDCRU, "pagedcru" }, + { PCB_GROMEMU, "gromemu" }, + { 0, nullptr} +}; + +// Softlists do not support the cartridges with RAM yet +static const pcb_type sw_pcbdefs[] = +{ + { PCB_STANDARD, "standard" }, + { PCB_PAGED12K, "paged12k" }, + { PCB_PAGED16K, "paged16k" }, + { PCB_MINIMEM, "minimem" }, + { PCB_SUPER, "super" }, + { PCB_MBX, "mbx" }, + { PCB_GROMEMU, "gromemu" }, + { 0, nullptr} +}; + +ti99_cartridge_device::ti99_cartridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +: bus8z_device(mconfig, TI99_CART, tag, owner, clock), + device_image_interface(mconfig, *this), + m_pcbtype(0), + m_slot(0), + m_pcb(nullptr), + m_connector(nullptr), + m_rpk(nullptr) +{ +} + +void ti99_cartridge_device::prepare_cartridge() +{ + int rom2_length; + + uint8_t* grom_ptr; + uint8_t* rom_ptr; + + memory_region *regg; + memory_region *regr; + + // Initialize some values. + m_pcb->m_rom_page = 0; + m_pcb->m_rom_ptr = nullptr; + m_pcb->m_ram_size = 0; + m_pcb->m_ram_ptr = nullptr; + m_pcb->m_ram_page = 0; + + for (int i=0; i < 5; i++) m_pcb->m_grom[i] = nullptr; + + m_pcb->m_grom_size = loaded_through_softlist() ? get_software_region_length("grom") : m_rpk->get_resource_length("grom_socket"); + if (TRACE_CONFIG) logerror("grom_socket.size=0x%04x\n", m_pcb->m_grom_size); + + if (m_pcb->m_grom_size > 0) + { + regg = memregion(CARTGROM_TAG); + grom_ptr = loaded_through_softlist() ? get_software_region("grom") : m_rpk->get_contents_of_socket("grom_socket"); + memcpy(regg->base(), grom_ptr, m_pcb->m_grom_size); + m_pcb->m_grom_ptr = regg->base(); // for gromemu + m_pcb->m_grom_address = 0; // for gromemu + + // Find the GROMs and keep their pointers + m_pcb->set_grom_pointer(0, subdevice(GROM3_TAG)); + if (m_pcb->m_grom_size > 0x2000) m_pcb->set_grom_pointer(1, subdevice(GROM4_TAG)); + if (m_pcb->m_grom_size > 0x4000) m_pcb->set_grom_pointer(2, subdevice(GROM5_TAG)); + if (m_pcb->m_grom_size > 0x6000) m_pcb->set_grom_pointer(3, subdevice(GROM6_TAG)); + if (m_pcb->m_grom_size > 0x8000) m_pcb->set_grom_pointer(4, subdevice(GROM7_TAG)); + } + + m_pcb->m_rom_size = loaded_through_softlist() ? get_software_region_length("rom") : m_rpk->get_resource_length("rom_socket"); + if (m_pcb->m_rom_size > 0) + { + if (TRACE_CONFIG) logerror("rom size=0x%04x\n", m_pcb->m_rom_size); + regr = memregion(CARTROM_TAG); + rom_ptr = loaded_through_softlist() ? get_software_region("rom") : m_rpk->get_contents_of_socket("rom_socket"); + memcpy(regr->base(), rom_ptr, m_pcb->m_rom_size); + // Set both pointers to the same region for now + m_pcb->m_rom_ptr = regr->base(); + } + + // Softlist uses only one ROM area, no second socket + if (!loaded_through_softlist()) + { + rom2_length = m_rpk->get_resource_length("rom2_socket"); + if (rom2_length > 0) + { + // sizes do not differ between rom and rom2 + // We use the large cartrom space for the second bank as well + regr = memregion(CARTROM_TAG); + rom_ptr = m_rpk->get_contents_of_socket("rom2_socket"); + memcpy(regr->base() + 0x2000, rom_ptr, rom2_length); + } + } + + // (NV)RAM cartridges + if (loaded_through_softlist()) + { + // Do we have NVRAM? + if (get_software_region("nvram")!=nullptr) + { + m_pcb->m_ram_size = get_software_region_length("nvram"); + m_pcb->m_nvram.resize(m_pcb->m_ram_size); + m_pcb->m_ram_ptr = &m_pcb->m_nvram[0]; + battery_load(m_pcb->m_ram_ptr, m_pcb->m_ram_size, 0xff); + } + + // Do we have RAM? + if (get_software_region("ram")!=nullptr) + { + m_pcb->m_ram_size = get_software_region_length("ram"); + m_pcb->m_ram.resize(m_pcb->m_ram_size); + m_pcb->m_ram_ptr = &m_pcb->m_ram[0]; + } + } + else + { + m_pcb->m_ram_size = m_rpk->get_resource_length("ram_socket"); + if (m_pcb->m_ram_size > 0) + { + // TODO: Consider to use a region as well. If so, do not forget to memcpy. + m_pcb->m_ram_ptr = m_rpk->get_contents_of_socket("ram_socket"); + } + } +} + +/* + Find the index of the cartridge name. We assume the format + , i.e. the number is the longest string from the right + which can be interpreted as a number. Subtract 1. +*/ +int ti99_cartridge_device::get_index_from_tagname() +{ + const char *mytag = tag(); + int maxlen = strlen(mytag); + int i; + + for (i=maxlen-1; i >=0; i--) + if (mytag[i] < 48 || mytag[i] > 57) break; + + if (i==maxlen-1) return 0; + return atoi(mytag+i+1)-1; +} + +image_init_result ti99_cartridge_device::call_load() +{ + // File name is in m_basename + // return true = error + if (TRACE_CHANGE) logerror("Loading %s in slot %s\n", basename()); + + if (loaded_through_softlist()) + { + if (TRACE_CONFIG) logerror("Using softlists\n"); + int i = 0; + const char* pcb = get_feature("pcb"); + do + { + if (strcmp(pcb, sw_pcbdefs[i].name)==0) + { + m_pcbtype = sw_pcbdefs[i].id; + break; + } + i++; + } while (sw_pcbdefs[i].id != 0); + if (TRACE_CONFIG) logerror("Cartridge type is %s (%d)\n", pcb, m_pcbtype); + m_rpk = nullptr; + } + else + { + auto reader = new rpk_reader(pcbdefs); + try + { + m_rpk = reader->open(machine().options(), filename(), machine().system().name); + m_pcbtype = m_rpk->get_type(); + } + catch (rpk_exception& err) + { + logerror("Failed to load cartridge '%s': %s\n", basename(), err.to_string()); + m_rpk = nullptr; + m_err = IMAGE_ERROR_INVALIDIMAGE; + return image_init_result::FAIL; + } + } + + switch (m_pcbtype) + { + case PCB_STANDARD: + if (TRACE_CONFIG) logerror("Standard PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_PAGED12K: + if (TRACE_CONFIG) logerror("Paged PCB 12K\n"); + m_pcb = std::make_unique(); + break; + case PCB_PAGED16K: + if (TRACE_CONFIG) logerror("Paged PCB 16K\n"); + m_pcb = std::make_unique(); + break; + case PCB_MINIMEM: + if (TRACE_CONFIG) logerror("Minimem PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_SUPER: + if (TRACE_CONFIG) logerror("Superspace PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_MBX: + if (TRACE_CONFIG) logerror("MBX PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_PAGED379I: + if (TRACE_CONFIG) logerror("Paged379i PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_PAGED378: + if (TRACE_CONFIG) logerror("Paged378 PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_PAGED377: + if (TRACE_CONFIG) logerror("Paged377 PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_PAGEDCRU: + if (TRACE_CONFIG) logerror("PagedCRU PCB\n"); + m_pcb = std::make_unique(); + break; + case PCB_GROMEMU: + if (TRACE_CONFIG) logerror("Grom Emulation PCB\n"); + m_pcb = std::make_unique(); + break; + } + + prepare_cartridge(); + m_pcb->set_cartridge(this); + m_pcb->set_tag(tag()); + m_slot = get_index_from_tagname(); + m_connector->insert(m_slot, this); + return image_init_result::PASS; +} + +void ti99_cartridge_device::call_unload() +{ + if (TRACE_CHANGE) logerror("Unload\n"); + if (m_rpk != nullptr) + { + m_rpk->close(); // will write NVRAM contents + delete m_rpk; + } + else + { + // Softlist + bool has_nvram = (get_software_region("nvram")!=nullptr); + if (has_nvram) + { + int nvsize = get_software_region_length("nvram"); + battery_save(m_pcb->m_ram_ptr, nvsize); + } + } + + m_pcb = nullptr; + m_connector->remove(m_slot); +} + +void ti99_cartridge_device::set_slot(int i) +{ + m_slot = i; +} + +READ8Z_MEMBER(ti99_cartridge_device::readz) +{ + if (m_pcb != nullptr) + m_pcb->readz(space, offset, value); +} + +WRITE8_MEMBER(ti99_cartridge_device::write) +{ + if (m_pcb != nullptr) + m_pcb->write(space, offset, data); +} + +READ8Z_MEMBER(ti99_cartridge_device::crureadz) +{ + if (m_pcb != nullptr) m_pcb->crureadz(space, offset, value); +} + +WRITE8_MEMBER(ti99_cartridge_device::cruwrite) +{ + if (m_pcb != nullptr) m_pcb->cruwrite(space, offset, data); +} + +WRITE_LINE_MEMBER( ti99_cartridge_device::ready_line ) +{ + m_connector->ready_line(state); +} + +WRITE_LINE_MEMBER( ti99_cartridge_device::romgq_line ) +{ + if (m_pcb != nullptr) + { + m_pcb->romgq_line(state); + m_readrom = state; + } +} + +/* + Combined select lines +*/ +WRITE8_MEMBER(ti99_cartridge_device::set_gromlines) +{ + if (m_pcb != nullptr) m_pcb->set_gromlines(space, offset, data); +} + +WRITE_LINE_MEMBER(ti99_cartridge_device::gclock_in) +{ + if (m_pcb != nullptr) m_pcb->gclock_in(state); +} + +bool ti99_cartridge_device::is_grom_idle() +{ + return (m_pcb != nullptr)? m_pcb->is_grom_idle() : false; +} + +void ti99_cartridge_device::device_config_complete() +{ + m_connector = static_cast(owner()); +} + +/* + 5 GROMs that may be contained in a cartridge +*/ +static MACHINE_CONFIG_START( ti99_cartridge ) + MCFG_GROM_ADD( GROM3_TAG, 3, CARTGROM_TAG, 0x0000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM4_TAG, 4, CARTGROM_TAG, 0x2000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM5_TAG, 5, CARTGROM_TAG, 0x4000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM6_TAG, 6, CARTGROM_TAG, 0x6000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM7_TAG, 7, CARTGROM_TAG, 0x8000, WRITELINE(ti99_cartridge_device, ready_line)) +MACHINE_CONFIG_END + +machine_config_constructor ti99_cartridge_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( ti99_cartridge ); +} + +/* + Memory area for one cartridge. For most cartridges we only need 8 KiB for + ROM contents, but cartridges of the "paged377" type have up to 2 MiB + organised as selectable banks, so we must be sure there is enough space. +*/ +ROM_START( cartridge_memory ) + ROM_REGION(0xa000, CARTGROM_TAG, ROMREGION_ERASE00) + ROM_REGION(0x200000, CARTROM_TAG, ROMREGION_ERASE00) +ROM_END + +const tiny_rom_entry *ti99_cartridge_device::device_rom_region() const +{ + return ROM_NAME( cartridge_memory ); +} + +/*************************************************************************** + Cartridge types + Cartridges differ by the circuits on their PCB which hosts the ROMs. + Some cartridges also have RAM, and some allow for switching between + ROMs. + + Standard cartridge + + GROM space + 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff + |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| + + + ROM space + 6000 7000 7fff + | | | + |========== ROM1 ============| + +***************************************************************************/ + +ti99_cartridge_pcb::ti99_cartridge_pcb() + : m_cart(nullptr), + m_grom_idle(false), + m_grom_size(0), + m_rom_size(0), + m_ram_size(0), + m_rom_ptr(nullptr), + m_ram_ptr(nullptr), + m_romspace_selected(false), + m_rom_page(0), + m_grom_ptr(nullptr), + m_grom_address(0), + m_ram_page(0), + m_tag(nullptr) +{ +} + +void ti99_cartridge_pcb::set_cartridge(ti99_cartridge_device *cart) +{ + m_cart = cart; +} + +READ8Z_MEMBER(ti99_cartridge_pcb::gromreadz) +{ + for (auto & elem : m_grom) + { + if (elem != nullptr) + { + elem->readz(space, offset, value, mem_mask); + } + } +} + +WRITE8_MEMBER(ti99_cartridge_pcb::gromwrite) +{ + for (auto & elem : m_grom) + { + if (elem != nullptr) + { + elem->write(space, offset, data, mem_mask); + } + } +} + +/* + TI-99/4A cartridges can only occupy 8 KiB of CPU RAM space. For TI-99/8 + cartridges with up to 16 KiB we need a new PCB type. Unfortunately, such + cartridges were never developed. +*/ +READ8Z_MEMBER(ti99_cartridge_pcb::readz) +{ + if (m_romspace_selected) + { + if (m_rom_ptr!=nullptr) + { + *value = m_rom_ptr[offset & 0x1fff]; + } + } + else + { + // Will not return anything when not selected (preceding gsq=ASSERT) + gromreadz(space, offset, value, mem_mask); + } +} + +WRITE8_MEMBER(ti99_cartridge_pcb::write) +{ + if (m_romspace_selected) + { + if (TRACE_ILLWRITE) m_cart->logerror("Cannot write to ROM space at %04x\n", offset); + } + else + { + // Will not change anything when not selected (preceding gsq=ASSERT) + gromwrite(space, offset, data, mem_mask); + } +} + +READ8Z_MEMBER(ti99_cartridge_pcb::crureadz) +{ +} + +WRITE8_MEMBER(ti99_cartridge_pcb::cruwrite) +{ +} + +void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev) +{ + m_grom[number] = static_cast(dev); +} + + +WRITE_LINE_MEMBER( ti99_cartridge_pcb::romgq_line ) +{ + m_romspace_selected = (state==ASSERT_LINE); +} + +// Propagate to all GROMs + +/* + Combined select lines +*/ +WRITE8_MEMBER(ti99_cartridge_pcb::set_gromlines) +{ + for (auto& elem : m_grom) + { + if (elem != nullptr) + { + elem->set_lines(space, offset, data); + if (data==ASSERT_LINE) m_grom_idle = false; + } + } +} + +WRITE_LINE_MEMBER(ti99_cartridge_pcb::gclock_in) +{ + for (auto& elem : m_grom) + { + if (elem != nullptr) + { + elem->gclock_in(state); + m_grom_idle = elem->idle(); + } + } +} + + +/***************************************************************************** + Cartridge type: Paged (12K, Extended Basic) + + The Extended Basic cartridge consists of several GROMs which are + treated in the usual way, and two ROMs (4K and 8K). + + GROM space + 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff + |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| + + ROM space + 6000 7000 7fff + | | | + | |==== ROM2a ===| Bank 0 write to 6000, 6004, ... 7ffc + |=== ROM1 ====| | + |==== ROM2b ===| Bank 1 write to 6002, 6006, ... 7ffe + + The 4K ROM is mapped into the 6000-6FFF space. + The 8K ROM is actually composed of two banks of 4K which are mapped into + the 7000-7FFF space. Bank 0 is visible after a write access to 6000 / 6004 / + 6008 ... , while bank 1 is visible after writing to 6002 / 6006 / 600A / ... + +******************************************************************************/ + +READ8Z_MEMBER(ti99_paged12k_cartridge::readz) +{ + if (m_romspace_selected) + { + // rom_ptr: 0000-0fff = rom1 + // 2000-2fff = rom2a + // 3000-3fff = rom2b + if ((offset & 0x1000)==0) + *value = m_rom_ptr[offset & 0x0fff]; + else + *value = m_rom_ptr[(offset & 0x0fff) | 0x2000 | (m_rom_page << 12)]; + } + else + { + // Will not return anything when not selected (preceding gsq=ASSERT) + gromreadz(space, offset, value, mem_mask); + } +} + +WRITE8_MEMBER(ti99_paged12k_cartridge::write) +{ + if (m_romspace_selected) + { + m_rom_page = (offset >> 1) & 1; + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + } + else + { + // Will not change anything when not selected (preceding gsq=ASSERT) + gromwrite(space, offset, data, mem_mask); + } +} + +/***************************************************************************** + Cartridge type: Paged (16K) + + GROM space + 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff + |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| + + ROM space + 6000 7000 7fff + | | | + |========== ROM1 ===========| Bank 0 write to 6000, 6004, ... 7ffc + | | | + |========== ROM2 ===========| Bank 1 write to 6002, 6006, ... 7ffe + + This cartridge consists of GROM memory and 2 pages of standard ROM. + The page is set by writing any value to a location in + the address area, where an even word offset sets the page to 0 and an + odd word offset sets the page to 1 (e.g. 6000 = bank 0, and + 6002 = bank 1). +******************************************************************************/ + +READ8Z_MEMBER(ti99_paged16k_cartridge::readz) +{ + if (m_romspace_selected) + { + *value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page << 13)]; + } + else + { + // Will not return anything when not selected (preceding gsq=ASSERT) + gromreadz(space, offset, value, mem_mask); + } +} + +WRITE8_MEMBER(ti99_paged16k_cartridge::write) +{ + if (m_romspace_selected) + { + m_rom_page = (offset >> 1) & 1; + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + } + else + { + // Will not change anything when not selected (preceding gsq=ASSERT) + gromwrite(space, offset, data, mem_mask); + } +} + +/***************************************************************************** + Cartridge type: Mini Memory + GROM: 6 KiB (occupies G>6000 to G>77ff) + ROM: 4 KiB (6000-6fff) + RAM: 4 KiB (7000-7fff, battery-backed) + + GROM space + 6000 77ff + |== GROM3 ==| + + ROM space + 6000 7000 7fff + | | | + |=== ROM1 ====| | + |=== NVRAM ===| + +******************************************************************************/ + +/* Read function for the minimem cartridge. */ +READ8Z_MEMBER(ti99_minimem_cartridge::readz) +{ + if (m_romspace_selected) + { + if ((offset & 0x1000)==0x0000) + { + if (m_rom_ptr!=nullptr) // Super-Minimem seems to have no ROM + { + *value = m_rom_ptr[offset & 0x0fff]; + } + } + else + { + *value = m_ram_ptr[offset & 0x0fff]; + } + } + else + { + gromreadz(space, offset, value, mem_mask); + } +} + +/* Write function for the minimem cartridge. */ +WRITE8_MEMBER(ti99_minimem_cartridge::write) +{ + if (m_romspace_selected) + { + if ((offset & 0x1000)==0x0000) + { + if (TRACE_ILLWRITE) m_cart->logerror("Write access to cartridge ROM at address %04x ignored", offset); + } + else + { + m_ram_ptr[offset & 0x0fff] = data; + } + } + else + { + gromwrite(space, offset, data, mem_mask); + } +} + +/***************************************************************************** + Cartridge type: SuperSpace II + + SuperSpace is intended as a user-definable blank cartridge containing + buffered RAM. It has an Editor/Assembler GROM which helps the user to load + the user program into the cartridge. If the user program has a suitable + header, the console recognizes the cartridge as runnable, and + assigns a number in the selection screen. Switching the RAM banks in this + cartridge is achieved by setting CRU bits (the system serial interface). + + GROM: Editor/Assembler GROM + ROM: none + RAM: 32 KiB (0x6000-0x7fff, 4 banks) + Banking: via CRU write + + GROM space + 6000 77ff + |==== GROM3 (Editor/Assm) ====| + + ROM space + 6000 7000 7fff + | | | + |======== NVRAM 0 ==========| Bank 0 CRU>0802 + | | | + |======== NVRAM 1 ==========| Bank 1 CRU>0806 + | | | + |======== NVRAM 2 ==========| Bank 2 CRU>080a + | | | + |======== NVRAM 3 ==========| Bank 3 CRU>080e + +******************************************************************************/ + +/* Read function for the super cartridge. */ +READ8Z_MEMBER(ti99_super_cartridge::readz) +{ + if (m_romspace_selected) + { + if (m_ram_ptr != nullptr) + { + *value = m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)]; + } + } + else + { + gromreadz(space, offset, value, mem_mask); + } +} + +/* Write function for the super cartridge. */ +WRITE8_MEMBER(ti99_super_cartridge::write) +{ + if (m_romspace_selected) + { + m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)] = data; + } + else + { + gromwrite(space, offset, data, mem_mask); + } +} + +READ8Z_MEMBER(ti99_super_cartridge::crureadz) +{ + // offset is the bit number. The CRU base address is already divided by 2. + + // ram_page contains the bank number. We have a maximum of + // 4 banks; the Super Space II manual says: + // + // Banks are selected by writing a bit pattern to CRU address >0800: + // + // Bank # Value + // 0 >02 = 0000 0010 + // 1 >08 = 0000 1000 + // 2 >20 = 0010 0000 + // 3 >80 = 1000 0000 + // + // With the bank number (0, 1, 2, or 3) in R0: + // + // BNKSW LI R12,>0800 Set CRU address + // LI R1,2 Load Shift Bit + // SLA R0,1 Align Bank Number + // JEQ BNKS1 Skip shift if Bank 0 + // SLA R1,0 Align Shift Bit + // BNKS1 LDCR R1,0 Switch Banks + // SRL R0,1 Restore Bank Number (optional) + // RT + + // Our implementation in MESS always gets 8 bits in one go. Also, the address + // is twice the bit number. That is, the offset value is always a multiple + // of 0x10. + + if ((offset & 0xfff0) == 0x0800) + { + if (TRACE_CRU) m_cart->logerror("CRU accessed at %04x\n", offset); + uint8_t val = 0x02 << (m_ram_page << 1); + *value = (val >> ((offset - 0x0800)>>1)) & 0xff; + } +} + +WRITE8_MEMBER(ti99_super_cartridge::cruwrite) +{ + if ((offset & 0xfff0) == 0x0800) + { + if (TRACE_CRU) m_cart->logerror("CRU accessed at %04x\n", offset); + if (data != 0) + { + m_ram_page = (offset-0x0802)>>2; + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set RAM page = %d (CRU address %04x)\n", m_ram_page, offset); + } + } +} + +/***************************************************************************** + Cartridge type: MBX + GROM: up to 5 GROMs (sockets for a maximum of 3 GROMs, but may be stacked) + ROM: up to 16 KiB (in up to 2 banks of 8KiB each) + RAM: 1022 B (0x6c00-0x6ffd, overrides ROM in that area) + ROM mapper: 6ffe + + TODO: Some MBX cartridges assume the presence of the MBX system + (special user interface box with speech input/output) + and will not run without it. This MBX hardware is not emulated yet. + + GROM space + 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff + |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| + + ROM space + 6000 6c00 7000 7fff + | | | | + | | |===== ROM bank 0 ====| 6ffe = 00 + | |= RAM =| | + |=== ROM bank 0 ==| |===== ROM bank 1 ====| 6ffe = 01 + | | + |===== ROM bank 3 ====| 6ffe = 02 + | | + |===== ROM bank 3 ====| 6ffe = 03 + + The 16K ROM is composed of four 4K banks, which can be selected by writing + the bank number to address 6ffe. This also affects the RAM so that the + bank number is stored in RAM and may also be read from there. + + The mapper does not decode the LSB of the address, so it changes value when + a write operation is done on 6FFF. Since the TI console always writes the + odd byte first, then the even byte, the last byte written is actually 6FFE. + + ROM bank 0 (ROM area 0000-0fff) is always visible in the space 6000-6bff. + + RAM is implemented by two 1024x4 RAM circuits and is not affected by banking. + +******************************************************************************/ + +/* Read function for the mbx cartridge. */ +READ8Z_MEMBER(ti99_mbx_cartridge::readz) +{ + if (m_romspace_selected) + { + if (m_ram_ptr != nullptr && (offset & 0x1c00)==0x0c00) + { + // Also reads the value of 6ffe + *value = m_ram_ptr[offset & 0x03ff]; + if (TRACE_READ) m_cart->logerror("%04x (RAM) -> %02x\n", offset + 0x6000, *value); + } + else + { + if (m_rom_ptr!=nullptr) + { + if ((offset & 0x1000)==0) // 6000 area + *value = m_rom_ptr[offset]; + else // 7000 area + *value = m_rom_ptr[(offset & 0x0fff) | (m_rom_page << 12)]; + + if (TRACE_READ) m_cart->logerror("%04x(%04x) -> %02x\n", offset + 0x6000, offset | (m_rom_page<<13), *value); + } + } + } + else + { + gromreadz(space, offset, value, mem_mask); + } +} + +/* Write function for the mbx cartridge. */ +WRITE8_MEMBER(ti99_mbx_cartridge::write) +{ + if (m_romspace_selected) + { + if ((offset & 0x1c00)==0x0c00) // RAM area + { + if ((offset & 0x0ffe) == 0x0ffe) // Mapper, backed by RAM; reacts to bots 6fff and 6ffe + { + // Valid values are 0, 1, 2, 3 + m_rom_page = data & 3; + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + } + + if (m_ram_ptr != nullptr) + m_ram_ptr[offset & 0x03ff] = data; + else + if (TRACE_ILLWRITE) m_cart->logerror("Write access to %04x but no RAM present\n", offset+0x6000); + } + } + else + { + gromwrite(space, offset, data, mem_mask); + } +} + +/***************************************************************************** + Cartridge type: paged379i + This cartridge consists of one 16 KiB, 32 KiB, 64 KiB, or 128 KiB EEPROM + which is organised in 2, 4, 8, or 16 pages of 8 KiB each. The complete + memory contents must be stored in one dump file. + The pages are selected by writing a value to some memory locations. Due to + using the inverted outputs of the LS379 latch, setting the inputs of the + latch to all 0 selects the highest bank, while setting to all 1 selects the + lowest. There are some cartridges (16 KiB) which are using this scheme, and + there are new hardware developments mainly relying on this scheme. + + Writing to selects page (16K/32K/64K/128K) + >6000 1 / 3 / 7 / 15 + >6002 0 / 2 / 6 / 14 + >6004 1 / 1 / 5 / 13 + >6006 0 / 0 / 4 / 12 + >6008 1 / 3 / 3 / 11 + >600A 0 / 2 / 2 / 10 + >600C 1 / 1 / 1 / 9 + >600E 0 / 0 / 0 / 8 + >6010 1 / 3 / 7 / 7 + >6012 0 / 2 / 6 / 6 + >6014 1 / 1 / 5 / 5 + >6016 0 / 0 / 4 / 4 + >6018 1 / 3 / 3 / 3 + >601A 0 / 2 / 2 / 2 + >601C 1 / 1 / 1 / 1 + >601E 0 / 0 / 0 / 0 + + The paged379i cartrige does not have any GROMs. + + ROM space + 6000 7000 7fff + | | | + |========== ROM 1 ==========| Bank 0 Write to 601e + | | | + |========== ROM 2 ==========| Bank 1 Write to 601c + | | | + | ... | + | | | + |========== ROM 16 =========| Bank 15 Write to 6000 + + +******************************************************************************/ + +/* Read function for the paged379i cartridge. */ +READ8Z_MEMBER(ti99_paged379i_cartridge::readz) +{ + if (m_romspace_selected) + *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; +} + +/* Write function for the paged379i cartridge. Only used to set the bank. */ +WRITE8_MEMBER(ti99_paged379i_cartridge::write) +{ + // Bits: 011x xxxx xxxb bbbx + // x = don't care, bbbb = bank + if (m_romspace_selected) + { + // This is emulation magic to automatically adapt to different ROM sizes + + // Each block has 8 KiB. We assume that m_rom_size is a power of 2. + // Thus the number of blocks is also a power of 2. + // To get the required number of address lines, we just have to subtract 1. + // The SN74LS379 only has four flipflops, so we limit the lines to 4. + int mask = ((m_rom_size / 8192) - 1) & 0x0f; + + // The page is determined by the inverted outputs. + m_rom_page = (~offset)>>1 & mask; + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + } +} + +/***************************************************************************** + Cartridge type: paged378 + This type is intended for high-capacity cartridges of up to 512 KiB + plus GROM space of 120KiB (not supported yet) + + Due to its huge GROM space it is also called the "UberGROM" + + The cartridge may also be used without GROM. + + ROM space + 6000 7000 7fff + | | | + |========== ROM 1 ==========| Bank 0 Write to 6000 + | | | + |========== ROM 2 ==========| Bank 1 Write to 6002 + | | | + | ... | + | | | + |========== ROM 64 =========| Bank 63 Write to 607e + +******************************************************************************/ + +/* Read function for the paged378 cartridge. */ +READ8Z_MEMBER(ti99_paged378_cartridge::readz) +{ + if (m_romspace_selected) + *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; +} + +/* Write function for the paged378 cartridge. Only used to set the bank. */ +WRITE8_MEMBER(ti99_paged378_cartridge::write) +{ + // Bits: 011x xxxx xbbb bbbx + // x = don't care, bbbb = bank + if (m_romspace_selected) + { + m_rom_page = ((offset >> 1)&0x003f); + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + } +} + +/***************************************************************************** + Cartridge type: paged377 + This type is intended for high-capacity cartridges of up to 2 MiB + + The paged379i cartrige does not have any GROMs. + + ROM space + 6000 7000 7fff + | | | + |========== ROM 1 ==========| Bank 0 Write to 6000 + | | | + |========== ROM 2 ==========| Bank 1 Write to 6002 + | | | + | ... | + | | | + |========== ROM 256 ========| Bank 255 Write to 61fe + + +******************************************************************************/ + +/* Read function for the paged377 cartridge. */ +READ8Z_MEMBER(ti99_paged377_cartridge::readz) +{ + if (m_romspace_selected) + *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; +} + +/* Write function for the paged377 cartridge. Only used to set the bank. */ +WRITE8_MEMBER(ti99_paged377_cartridge::write) +{ + // Bits: 011x xxxb bbbb bbbx + // x = don't care, bbbb = bank + if (m_romspace_selected) + { + m_rom_page = ((offset >> 1)&0x00ff); + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + } +} + +/***************************************************************************** + Cartridge type: pagedcru + This cartridge consists of one 16 KiB, 32 KiB, or 64 KiB EEPROM which is + organised in 2, 4, or 8 pages of 8 KiB each. We assume there is only one + dump file of the respective size. + The pages are selected by writing a value to the CRU. This scheme is + similar to the one used for the SuperSpace cartridge, with the exception + that we are using ROM only, and we can have up to 8 pages. + + Bank Value written to CRU>0800 + 0 >0002 = 0000 0000 0000 0010 + 1 >0008 = 0000 0000 0000 1000 + 2 >0020 = 0000 0000 0010 0000 + 3 >0080 = 0000 0000 1000 0000 + 4 >0200 = 0000 0010 0000 0000 + 5 >0800 = 0000 1000 0000 0000 + 6 >2000 = 0010 0000 0000 0000 + 7 >8000 = 1000 0000 0000 0000 + + No GROMs used in this type. + + ROM space + 6000 7000 7fff + | | | + |========= ROM 1 ==========| Bank 0 CRU>0802 + | | | + |========= ROM 2 ==========| Bank 1 CRU>0806 + | | | + ... + | | | + |========= ROM 8 ==========| Bank 7 CRU>081e + +******************************************************************************/ + +/* Read function for the pagedcru cartridge. */ +READ8Z_MEMBER(ti99_pagedcru_cartridge::readz) +{ + if (m_romspace_selected) + *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; +} + +/* Write function for the pagedcru cartridge. No effect. */ +WRITE8_MEMBER(ti99_pagedcru_cartridge::write) +{ + return; +} + +READ8Z_MEMBER(ti99_pagedcru_cartridge::crureadz) +{ + int page = m_rom_page; + if ((offset & 0xf800)==0x0800) + { + int bit = (offset & 0x001e)>>1; + if (bit != 0) + { + page = page-(bit/2); // 4 page flags per 8 bits + } + *value = 1 << (page*2+1); + } +} + +WRITE8_MEMBER(ti99_pagedcru_cartridge::cruwrite) +{ + if ((offset & 0xf800)==0x0800) + { + int bit = (offset & 0x001e)>>1; + if (data != 0 && bit > 0) + { + m_rom_page = (bit-1)/2; + if (TRACE_BANKSWITCH) m_cart->logerror("Set ROM page = %d (CRU address %d)\n", m_rom_page, offset); + } + } +} + +/***************************************************************************** + Cartridge type: GROM emulation/paged + + This cartridge offers GROM address space without real GROM circuits. The GROMs + are emulated by a normal EPROM with a circuit that mimics GROM behavior. + Each simulated GROM offers 8K (real GROMs only offer 6K). + + Some assumptions: + - No readable address counter. This means the parallel console GROMs + will deliver the address when reading. + - No wait states. Reading is generally faster than with real GROMs. + - No wrapping at 8K boundaries. + - Two pages of ROM at address 6000 + + If any of these fails, the cartridge will crash, so we'll see. + + Typical cartridges: Third-party cartridges + + For the sake of simplicity, we register GROMs like the other PCB types, but + we implement special access methods for the GROM space. + + Still not working: + rxb1002 (Set page to 1 (6372 <- 00), lockup) + rxb237 (immediate reset) + rxbv555 (repeating reset on Master Title Screen) + superxb (lockup, fix: add RAM at 7c00) + + Super-MiniMemory is also included here. We assume a RAM area at addresses + 7000-7fff for this cartridge. + + + GROM space + 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 ffff + |=========================== emulated GROM ================================| + + ROM space + 6000 7000 7fff + | | | + |========== ROM1 ===========| Bank 0 write to 6000, 6004, ... 7ffc + | | | + |========== ROM2 ===========| Bank 1 write to 6002, 6006, ... 7ffe + + +******************************************************************************/ + +WRITE8_MEMBER(ti99_gromemu_cartridge::set_gromlines) +{ + if (m_grom_ptr != nullptr) + { + m_grom_selected = (data != CLEAR_LINE); + m_grom_read_mode = ((offset & GROM_M_LINE)!=0); + m_grom_address_mode = ((offset & GROM_MO_LINE)!=0); + } +} + +READ8Z_MEMBER(ti99_gromemu_cartridge::readz) +{ + if (m_grom_selected) + { + if (m_grom_read_mode) gromemureadz(space, offset, value, mem_mask); + } + else + { + if (m_ram_ptr != nullptr) + { + // Variant of the cartridge which emulates MiniMemory. We don't introduce + // another type for this single cartridge. + if ((offset & 0x1000)==0x1000) + { + *value = m_ram_ptr[offset & 0x0fff]; + return; + } + } + + if (m_rom_ptr == nullptr) return; + *value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page << 13)]; + } +} + +WRITE8_MEMBER(ti99_gromemu_cartridge::write) +{ + if (m_romspace_selected) + { + if (m_ram_ptr != nullptr) + { + // Lines for Super-Minimem; see above + if ((offset & 0x1000)==0x1000) { + m_ram_ptr[offset & 0x0fff] = data; + } + return; // no paging + } + m_rom_page = (offset >> 1) & 1; + if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + } + else + { + // Will not change anything when not selected (preceding gsq=ASSERT) + if (m_grom_selected) + { + if (!m_grom_read_mode) gromemuwrite(space, offset, data, mem_mask); + } + } +} + +READ8Z_MEMBER(ti99_gromemu_cartridge::gromemureadz) +{ + // Similar to the GKracker implemented above, we do not have a readable + // GROM address counter but use the one from the console GROMs. + if (m_grom_address_mode) return; + + int id = ((m_grom_address & 0xe000)>>13)&0x07; + if (id > 2) + { + // Cartridge space (0x6000 - 0xffff) + *value = m_grom_ptr[m_grom_address-0x6000]; // use the GROM memory + } + + // The GROM emulation does not wrap at 8K boundaries. + m_grom_address = (m_grom_address + 1) & 0xffff; + + // Reset the write address flipflop. + m_waddr_LSB = false; +} + +WRITE8_MEMBER(ti99_gromemu_cartridge::gromemuwrite) +{ + // Set GROM address + if (m_grom_address_mode) + { + if (m_waddr_LSB == true) + { + // Accept low address byte (second write) + m_grom_address = (m_grom_address << 8) | data; + m_waddr_LSB = false; + } + else + { + // Accept high address byte (first write) + m_grom_address = data; + m_waddr_LSB = true; + } + } + else + { + if (TRACE_ILLWRITE) m_cart->logerror("Ignoring write to GROM area at address %04x\n", m_grom_address); + } +} + +/**************************************************************************** + + RPK loader + + RPK format support + + A RPK file ("rompack") contains a collection of dump files and a layout + file that defines the kind of circuit board (PCB) used in the cartridge + and the mapping of dumps to sockets on the board. + +Example: + + + + + + + + + + + + +DTD: + + + + + + + + + + + + + + + + + + + + +****************************************************************************/ + +/**************************************** + RPK class +****************************************/ +/* + Constructor. +*/ +ti99_cartridge_device::rpk::rpk(emu_options& options, const char* sysname) + :m_options(options), m_type(0) +//,m_system_name(sysname) +{ + m_sockets.clear(); +} + +ti99_cartridge_device::rpk::~rpk() +{ + if (TRACE_RPK) printf("gromport/RPK: Destroy RPK\n"); +} + +/* + Deliver the contents of the socket by name of the socket. +*/ +uint8_t* ti99_cartridge_device::rpk::get_contents_of_socket(const char *socket_name) +{ + auto socket = m_sockets.find(socket_name); + if (socket == m_sockets.end()) return nullptr; + return socket->second->get_contents(); +} + +/* + Deliver the length of the contents of the socket by name of the socket. +*/ +int ti99_cartridge_device::rpk::get_resource_length(const char *socket_name) +{ + auto socket = m_sockets.find(socket_name); + if (socket == m_sockets.end()) return 0; + return socket->second->get_content_length(); +} + +void ti99_cartridge_device::rpk::add_socket(const char* id, std::unique_ptr newsock) +{ + m_sockets.emplace(id, std::move(newsock)); +} + +/*------------------------------------------------- + rpk_close - closes a rpk + Saves the contents of the NVRAMs and frees all memory. +-------------------------------------------------*/ + +void ti99_cartridge_device::rpk::close() +{ + // Save the NVRAM contents + for(auto &socket : m_sockets) + { + if (socket.second->persistent_ram()) + { + // try to open the battery file and write it if possible + assert_always(socket.second->get_contents() && (socket.second->get_content_length() > 0), "Buffer is null or length is 0"); + + emu_file file(m_options.nvram_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + osd_file::error filerr = file.open(socket.second->get_pathname()); + if (filerr == osd_file::error::NONE) + file.write(socket.second->get_contents(), socket.second->get_content_length()); + + } + socket.second->cleanup(); + } +} + +/************************************************************** + RPK socket (location in the PCB where a chip is plugged in; + not a network socket) +***************************************************************/ + +ti99_cartridge_device::rpk_socket::rpk_socket(const char* id, int length, uint8_t* contents, const char *pathname) + : m_id(id), m_length(length), m_contents(contents), m_pathname(pathname) +{ +} + +ti99_cartridge_device::rpk_socket::rpk_socket(const char* id, int length, uint8_t* contents) + : rpk_socket(id, length, contents, nullptr) +{ +} + +/* + Locate a file in the ZIP container +*/ +int ti99_cartridge_device::rpk_reader::find_file(util::archive_file &zip, const char *filename, uint32_t crc) +{ + for (int header = zip.first_file(); header >= 0; header = zip.next_file()) + { + // Ignore directories + if (!zip.current_is_directory()) + { + // We don't check for CRC == 0. + if (crc != 0) + { + // if the CRC and name both match, we're good + // if the CRC matches and the name doesn't, we're still good + if (zip.current_crc() == crc) + return header; + } + else + { + if (core_stricmp(zip.current_name().c_str(), filename) == 0) + { + return header; + } + } + } + } + return -1; +} + +/* + Load a rom resource and put it in a pcb socket instance. +*/ +std::unique_ptr ti99_cartridge_device::rpk_reader::load_rom_resource(util::archive_file &zip, util::xml::data_node const* rom_resource_node, const char* socketname) +{ + const char* file; + const char* crcstr; + const char* sha1; + util::archive_file::error ziperr; + uint32_t crc; + int length; + uint8_t* contents; + int header; + + // find the file attribute (required) + file = rom_resource_node->get_attribute_string("file", nullptr); + if (file == nullptr) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a 'file' attribute"); + + if (TRACE_RPK) printf("gromport/RPK: Loading ROM contents for socket '%s' from file %s\n", socketname, file); + + // check for crc + crcstr = rom_resource_node->get_attribute_string("crc", nullptr); + if (crcstr==nullptr) + { + // no CRC, just find the file in the RPK + header = find_file(zip, file, 0); + } + else + { + crc = strtoul(crcstr, nullptr, 16); + header = find_file(zip, file, crc); + } + if (header < 0) throw rpk_exception(RPK_INVALID_FILE_REF, "File not found or CRC check failed"); + + length = zip.current_uncompressed_length(); + + // Allocate storage + contents = global_alloc_array_clear(length); + if (contents==nullptr) throw rpk_exception(RPK_OUT_OF_MEMORY); + + // and unzip file from the zip file + ziperr = zip.decompress(contents, length); + if (ziperr != util::archive_file::error::NONE) + { + if (ziperr == util::archive_file::error::UNSUPPORTED) throw rpk_exception(RPK_ZIP_UNSUPPORTED); + else throw rpk_exception(RPK_ZIP_ERROR); + } + + // check for sha1 + sha1 = rom_resource_node->get_attribute_string("sha1", nullptr); + if (sha1 != nullptr) + { + util::hash_collection actual_hashes; + actual_hashes.compute((const uint8_t *)contents, length, util::hash_collection::HASH_TYPES_CRC_SHA1); + + util::hash_collection expected_hashes; + expected_hashes.add_from_string(util::hash_collection::HASH_SHA1, sha1, strlen(sha1)); + + if (actual_hashes != expected_hashes) throw rpk_exception(RPK_INVALID_FILE_REF, "SHA1 check failed"); + } + + // Create a socket instance + return std::make_unique(socketname, length, contents); +} + +/* + Load a ram resource and put it in a pcb socket instance. +*/ +std::unique_ptr ti99_cartridge_device::rpk_reader::load_ram_resource(emu_options &options, util::xml::data_node const* ram_resource_node, const char* socketname, const char* system_name) +{ + const char* length_string; + const char* ram_type; + const char* ram_filename; + const char* ram_pname; + unsigned int length; + uint8_t* contents; + + // find the length attribute + length_string = ram_resource_node->get_attribute_string("length", nullptr); + if (length_string == nullptr) throw rpk_exception(RPK_MISSING_RAM_LENGTH); + + // parse it + char suffix = '\0'; + sscanf(length_string, "%u%c", &length, &suffix); + switch(tolower(suffix)) + { + case 'k': // kilobytes + length *= 1024; + break; + + case 'm': + /* megabytes */ + length *= 1024*1024; + break; + + case '\0': + break; + + default: // failed + throw rpk_exception(RPK_INVALID_RAM_SPEC); + } + + // Allocate memory for this resource + contents = global_alloc_array_clear(length); + if (contents==nullptr) throw rpk_exception(RPK_OUT_OF_MEMORY); + + if (TRACE_RPK) printf("gromport/RPK: Allocating RAM buffer (%d bytes) for socket '%s'\n", length, socketname); + + ram_pname = nullptr; + + // That's it for pure RAM. Now check whether the RAM is "persistent", i.e. NVRAM. + // In that case we must load it from the NVRAM directory. + // The file name is given in the RPK file; the subdirectory is the system name. + ram_type = ram_resource_node->get_attribute_string("type", nullptr); + if (ram_type != nullptr) + { + if (strcmp(ram_type, "persistent")==0) + { + // Get the file name (required if persistent) + ram_filename = ram_resource_node->get_attribute_string("file", nullptr); + if (ram_filename==nullptr) + { + global_free_array(contents); + throw rpk_exception(RPK_INVALID_RAM_SPEC, " must have a 'file' attribute"); + } + std::string ram_pathname = std::string(system_name).append(PATH_SEPARATOR).append(ram_filename); + ram_pname = core_strdup(ram_pathname.c_str()); + // load, and fill rest with 00 + if (TRACE_RPK) printf("gromport/RPK: Loading NVRAM contents from '%s'\n", ram_pname); + + // Load the NVRAM contents + int bytes_read = 0; + assert_always(contents && (length > 0), "Buffer is null or length is 0"); + + // try to open the battery file and read it if possible + emu_file file(options.nvram_directory(), OPEN_FLAG_READ); + osd_file::error filerr = file.open(ram_pname); + if (filerr == osd_file::error::NONE) + bytes_read = file.read(contents, length); + + // fill remaining bytes (if necessary) + memset(((char *) contents) + bytes_read, 0x00, length - bytes_read); + } + } + + // Create a socket instance + return std::make_unique(socketname, length, contents, ram_pname); +} + +/*------------------------------------------------- + rpk_open - open a RPK file + options - parameters from the settings; we need it only for the NVRAM directory + system_name - name of the driver (also just for NVRAM handling) +-------------------------------------------------*/ + +ti99_cartridge_device::rpk* ti99_cartridge_device::rpk_reader::open(emu_options &options, const char *filename, const char *system_name) +{ + util::archive_file::error ziperr; + + util::archive_file::ptr zipfile; + + std::vector layout_text; + util::xml::data_node *layout_xml = nullptr; + + int i; + + auto newrpk = new rpk(options, system_name); + + try + { + /* open the ZIP file */ + ziperr = util::archive_file::open_zip(filename, zipfile); + if (ziperr != util::archive_file::error::NONE) throw rpk_exception(RPK_NOT_ZIP_FORMAT); + + /* find the layout.xml file */ + if (find_file(*zipfile, "layout.xml", 0) < 0) throw rpk_exception(RPK_MISSING_LAYOUT); + + /* reserve space for the layout file contents (+1 for the termination) */ + layout_text.resize(zipfile->current_uncompressed_length() + 1); + + /* uncompress the layout text */ + ziperr = zipfile->decompress(&layout_text[0], zipfile->current_uncompressed_length()); + if (ziperr != util::archive_file::error::NONE) + { + if (ziperr == util::archive_file::error::UNSUPPORTED) throw rpk_exception(RPK_ZIP_UNSUPPORTED); + else throw rpk_exception(RPK_ZIP_ERROR); + } + + layout_text[zipfile->current_uncompressed_length()] = '\0'; // Null-terminate + + /* parse the layout text */ + layout_xml = util::xml::data_node::string_read(&layout_text[0], nullptr); + if (!layout_xml) throw rpk_exception(RPK_XML_ERROR); + + // Now we work within the XML tree + + // romset is the root node + util::xml::data_node const *const romset_node = layout_xml->get_child("romset"); + if (!romset_node) throw rpk_exception(RPK_INVALID_LAYOUT, "document element must be "); + + // resources is a child of romset + util::xml::data_node const *const resources_node = romset_node->get_child("resources"); + if (!resources_node) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a child"); + + // configuration is a child of romset; we're actually interested in ... + util::xml::data_node const *const configuration_node = romset_node->get_child("configuration"); + if (!configuration_node) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a child"); + + // ... pcb, which is a child of configuration + util::xml::data_node const *const pcb_node = configuration_node->get_child("pcb"); + if (!pcb_node) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a child"); + + // We'll try to find the PCB type on the provided type list. + char const *const pcb_type = pcb_node->get_attribute_string("type", nullptr); + if (!pcb_type) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a 'type' attribute"); + if (TRACE_RPK) printf("gromport/RPK: Cartridge says it has PCB type '%s'\n", pcb_type); + + i=0; + do + { + if (strcmp(pcb_type, m_types[i].name)==0) + { + newrpk->m_type = m_types[i].id; + break; + } + i++; + } while (m_types[i].id != 0); + + if (m_types[i].id==0) throw rpk_exception(RPK_UNKNOWN_PCB_TYPE); + + // Find the sockets and load their respective resource + for (util::xml::data_node const *socket_node = pcb_node->get_first_child(); socket_node != nullptr; socket_node = socket_node->get_next_sibling()) + { + if (strcmp(socket_node->get_name(), "socket")!=0) throw rpk_exception(RPK_INVALID_LAYOUT, " element has only children"); + char const *const id = socket_node->get_attribute_string("id", nullptr); + if (!id) throw rpk_exception(RPK_INVALID_LAYOUT, " must have an 'id' attribute"); + char const *const uses_name = socket_node->get_attribute_string("uses", nullptr); + if (!uses_name) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a 'uses' attribute"); + + bool found = false; + // Locate the resource node + for (util::xml::data_node const *resource_node = resources_node->get_first_child(); resource_node != nullptr; resource_node = resource_node->get_next_sibling()) + { + char const *const resource_name = resource_node->get_attribute_string("id", nullptr); + if (!resource_name) throw rpk_exception(RPK_INVALID_LAYOUT, "resource node must have an 'id' attribute"); + + if (strcmp(resource_name, uses_name)==0) + { + // found it + if (strcmp(resource_node->get_name(), "rom")==0) + { + newrpk->add_socket(id, load_rom_resource(*zipfile, resource_node, id)); + } + else + { + if (strcmp(resource_node->get_name(), "ram")==0) + { + newrpk->add_socket(id, load_ram_resource(options, resource_node, id, system_name)); + } + else throw rpk_exception(RPK_INVALID_LAYOUT, "resource node must be or "); + } + found = true; + } + } + if (!found) throw rpk_exception(RPK_INVALID_RESOURCE_REF, uses_name); + } + } + catch (rpk_exception &) + { + newrpk->close(); + if (layout_xml) layout_xml->file_free(); + + // rethrow the exception + throw; + } + + if (layout_xml) layout_xml->file_free(); + + return newrpk; +} + +} } } // end namespace bus::ti99::internal + +SLOT_INTERFACE_START( gromport4 ) + SLOT_INTERFACE("single", TI99_GROMPORT_SINGLE) + SLOT_INTERFACE("multi", TI99_GROMPORT_MULTI) + SLOT_INTERFACE("gkracker", TI99_GROMPORT_GK) +SLOT_INTERFACE_END + +SLOT_INTERFACE_START( gromport8 ) + SLOT_INTERFACE("single", TI99_GROMPORT_SINGLE) + SLOT_INTERFACE("multi", TI99_GROMPORT_MULTI) +SLOT_INTERFACE_END diff --git a/src/devices/bus/ti99/internal/gromport.h b/src/devices/bus/ti99/internal/gromport.h new file mode 100644 index 00000000000..87985ce08fa --- /dev/null +++ b/src/devices/bus/ti99/internal/gromport.h @@ -0,0 +1,468 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/*************************************************************************** + Gromport (Cartridge port) of the TI-99 consoles + For details see gromport.cpp + + Michael Zapf +***************************************************************************/ + +#ifndef MAME_BUS_TI99_INTERNAL_GROMPORT_H +#define MAME_BUS_TI99_INTERNAL_GROMPORT_H + +#pragma once + +#include "bus/ti99/ti99defs.h" +#include "machine/tmc0430.h" +#include "softlist_dev.h" + +namespace bus { namespace ti99 { namespace internal { + +class cartridge_connector_device; + +class gromport_device : public bus8z_device, public device_slot_interface +{ +public: + gromport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(crureadz); + DECLARE_WRITE8_MEMBER(cruwrite); + DECLARE_WRITE_LINE_MEMBER(ready_line); + + DECLARE_WRITE_LINE_MEMBER(romgq_line); + + // Combined GROM select lines + DECLARE_WRITE8_MEMBER(set_gromlines); + + DECLARE_WRITE_LINE_MEMBER(gclock_in); + + static void set_mask(device_t &device, int mask) { downcast(device).m_mask = mask; } + + template static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) { return downcast(device).m_console_ready.set_callback(std::forward(cb)); } + template static devcb_base &static_set_reset_callback(device_t &device, Object &&cb) { return downcast(device).m_console_reset.set_callback(std::forward(cb)); } + + void cartridge_inserted(); + bool is_grom_idle(); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_config_complete() override; + virtual ioport_constructor device_input_ports() const override; + +private: + cartridge_connector_device* m_connector; + bool m_reset_on_insert; + devcb_write_line m_console_ready; + devcb_write_line m_console_reset; + int m_mask; + int m_romgq; +}; + +/****************************************************************************/ + +class ti99_cartridge_pcb; + +class ti99_cartridge_device : public bus8z_device, public device_image_interface +{ +public: + ti99_cartridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(crureadz); + DECLARE_WRITE8_MEMBER(cruwrite); + + DECLARE_WRITE_LINE_MEMBER(ready_line); + DECLARE_WRITE_LINE_MEMBER(romgq_line); + DECLARE_WRITE8_MEMBER(set_gromlines); + + DECLARE_WRITE_LINE_MEMBER(gclock_in); + + bool is_available() { return m_pcb != nullptr; } + void set_slot(int i); + bool is_grom_idle(); + +protected: + virtual void device_start() override { } + virtual void device_config_complete() override; + virtual machine_config_constructor device_mconfig_additions() const override; + virtual const tiny_rom_entry* device_rom_region() const override; + + // Image handling: implementation of methods which are abstract in the parent + image_init_result call_load() override; + void call_unload() override; + virtual const software_list_loader &get_software_list_loader() const override { return rom_software_list_loader::instance(); } + + void prepare_cartridge(); + + // device_image_interface + iodevice_t image_type() const override { return IO_CARTSLOT; } + bool is_readable() const override { return true; } + bool is_writeable() const override { return false; } + bool is_creatable() const override { return false; } + bool must_be_loaded() const override { return false; } + bool is_reset_on_load() const override { return false; } + const char *image_interface() const override { return "ti99_cart"; } + const char *file_extensions() const override { return "rpk"; } + +private: + class rpk_socket; + class rpk_reader; + class rpk; + + bool m_readrom; + int m_pcbtype; + int m_slot; + int get_index_from_tagname(); + + std::unique_ptr m_pcb; // inbound + cartridge_connector_device* m_connector; // outbound + + // RPK which is associated to this cartridge + // When we close it, the contents are saved to NVRAM if available + rpk *m_rpk; +}; + +/****************************************************************************/ + +class cartridge_connector_device : public bus8z_device +{ +public: + virtual DECLARE_READ8Z_MEMBER(crureadz) = 0; + virtual DECLARE_WRITE8_MEMBER(cruwrite) = 0; + + virtual DECLARE_WRITE_LINE_MEMBER(romgq_line) = 0; + virtual DECLARE_WRITE8_MEMBER(set_gromlines) = 0; + + virtual DECLARE_WRITE_LINE_MEMBER(gclock_in) = 0; + + DECLARE_WRITE_LINE_MEMBER(ready_line); + + virtual void insert(int index, ti99_cartridge_device* cart) { m_gromport->cartridge_inserted(); } + virtual void remove(int index) { } + virtual bool is_grom_idle() = 0; + +protected: + cartridge_connector_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + virtual void device_config_complete() override; + + gromport_device* m_gromport; + bool m_grom_selected; +}; + +/* + Single cartridge connector. +*/ +class ti99_single_cart_conn_device : public cartridge_connector_device +{ +public: + ti99_single_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(crureadz) override; + DECLARE_WRITE8_MEMBER(cruwrite) override; + DECLARE_WRITE_LINE_MEMBER(romgq_line) override; + DECLARE_WRITE8_MEMBER(set_gromlines) override; + DECLARE_WRITE_LINE_MEMBER(gclock_in) override; + + bool is_grom_idle() override; + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual machine_config_constructor device_mconfig_additions() const override; + +private: + ti99_cartridge_device *m_cartridge; +}; + +/* + Multi cartridge connector. +*/ + +/* We set the number of slots to 4, although we may have up to 16. From a + logical point of view we could have 256, but the operating system only checks + the first 16 banks. */ + +class ti99_multi_cart_conn_device : public cartridge_connector_device +{ +public: + ti99_multi_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(crureadz) override; + DECLARE_WRITE8_MEMBER(cruwrite) override; + DECLARE_WRITE_LINE_MEMBER(romgq_line) override; + DECLARE_WRITE8_MEMBER(set_gromlines) override; + DECLARE_WRITE_LINE_MEMBER(gclock_in) override; + + void insert(int index, ti99_cartridge_device* cart) override; + void remove(int index) override; + DECLARE_INPUT_CHANGED_MEMBER( switch_changed ); + + bool is_grom_idle() override; + +protected: + static constexpr unsigned NUMBER_OF_CARTRIDGE_SLOTS = 4; + + virtual void device_start() override; + virtual void device_reset() override; + virtual machine_config_constructor device_mconfig_additions() const override; + virtual ioport_constructor device_input_ports() const override; + +private: + bool m_readrom; + int m_active_slot; + int m_fixed_slot; + int m_next_free_slot; + ti99_cartridge_device* m_cartridge[NUMBER_OF_CARTRIDGE_SLOTS]; + + void set_slot(int slotnumber); + int get_active_slot(bool changebase, offs_t offset); +}; + +/* + GRAM Kracker. +*/ +class ti99_gkracker_device : public cartridge_connector_device, public device_nvram_interface +{ +public: + ti99_gkracker_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(crureadz) override; + DECLARE_WRITE8_MEMBER(cruwrite) override; + DECLARE_WRITE_LINE_MEMBER(romgq_line) override; + DECLARE_WRITE8_MEMBER(set_gromlines) override; + DECLARE_WRITE_LINE_MEMBER(gclock_in) override; + + void insert(int index, ti99_cartridge_device* cart) override; + void remove(int index) override; + DECLARE_INPUT_CHANGED_MEMBER( gk_changed ); + + // We may have a cartridge plugged into the GK + bool is_grom_idle() override; + +protected: + virtual void device_start() override; + virtual void device_reset() override; + + virtual machine_config_constructor device_mconfig_additions() const override; + virtual const tiny_rom_entry* device_rom_region() const override; + virtual ioport_constructor device_input_ports() const override; + + // device_nvram_interface + void nvram_default() override; + void nvram_read(emu_file &file) override; + void nvram_write(emu_file &file) override; + +private: + int m_gk_switch[6]; // Used to cache the switch settings. + + bool m_romspace_selected; + int m_ram_page; + int m_grom_address; + uint8_t* m_ram_ptr; + uint8_t* m_grom_ptr; + + bool m_waddr_LSB; + + ti99_cartridge_device *m_cartridge; // guest cartridge + + // Just for proper initialization + void gk_install_menu(const char* menutext, int len, int ptr, int next, int start); +}; + +/****************************************************************************/ + +class ti99_cartridge_pcb +{ + friend class ti99_cartridge_device; +public: + ti99_cartridge_pcb(); + virtual ~ti99_cartridge_pcb() { } + +protected: + virtual DECLARE_READ8Z_MEMBER(readz); + virtual DECLARE_WRITE8_MEMBER(write); + virtual DECLARE_READ8Z_MEMBER(crureadz); + virtual DECLARE_WRITE8_MEMBER(cruwrite); + + DECLARE_WRITE_LINE_MEMBER(romgq_line); + virtual DECLARE_WRITE8_MEMBER(set_gromlines); + DECLARE_WRITE_LINE_MEMBER(gclock_in); + + DECLARE_READ8Z_MEMBER(gromreadz); + DECLARE_WRITE8_MEMBER(gromwrite); + inline void set_grom_pointer(int number, device_t *dev); + void set_cartridge(ti99_cartridge_device *cart); + const char* tag() { return m_tag; } + void set_tag(const char* tag) { m_tag = tag; } + bool is_grom_idle() { return m_grom_idle; } + + ti99_cartridge_device* m_cart; + tmc0430_device* m_grom[5]; + bool m_grom_idle; + int m_grom_size; + int m_rom_size; + int m_ram_size; + + uint8_t* m_rom_ptr; + uint8_t* m_ram_ptr; + bool m_romspace_selected; + int m_rom_page; // for some cartridge types + uint8_t* m_grom_ptr; // for gromemu + int m_grom_address; // for gromemu + int m_ram_page; // for super + const char* m_tag; + std::vector m_nvram; // for MiniMemory + std::vector m_ram; // for MBX +}; + +/******************** Standard cartridge ******************************/ + +class ti99_standard_cartridge : public ti99_cartridge_pcb +{ +public: +}; + +/*********** Paged cartridge (like Extended Basic) ********************/ + +class ti99_paged12k_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; +}; + +/*********** Paged cartridge (others) ********************/ + +class ti99_paged16k_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; +}; + +/********************** Mini Memory ***********************************/ + +class ti99_minimem_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; +}; + +/********************* Super Space II *********************************/ + +class ti99_super_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(crureadz) override; + DECLARE_WRITE8_MEMBER(cruwrite) override; +}; + +/************************* MBX ***************************************/ + +class ti99_mbx_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; +}; + +/********************** Paged 379i ************************************/ + +class ti99_paged379i_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; +private: + int get_paged379i_bank(int rompage); +}; + +/********************** Paged 378 ************************************/ + +class ti99_paged378_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; +}; + +/********************** Paged 377 ************************************/ + +class ti99_paged377_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; +}; + +/********************** Paged CRU ************************************/ + +class ti99_pagedcru_cartridge : public ti99_cartridge_pcb +{ +public: + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(crureadz) override; + DECLARE_WRITE8_MEMBER(cruwrite) override; +}; + +/********************** GROM emulation cartridge ************************************/ + +class ti99_gromemu_cartridge : public ti99_cartridge_pcb +{ +public: + ti99_gromemu_cartridge(): m_waddr_LSB(false), m_grom_selected(false), m_grom_read_mode(false), m_grom_address_mode(false) + { m_grom_address = 0; } + DECLARE_READ8Z_MEMBER(readz) override; + DECLARE_WRITE8_MEMBER(write) override; + DECLARE_READ8Z_MEMBER(gromemureadz); + DECLARE_WRITE8_MEMBER(gromemuwrite); + DECLARE_WRITE8_MEMBER(set_gromlines) override; + +private: + bool m_waddr_LSB; + bool m_grom_selected; + bool m_grom_read_mode; + bool m_grom_address_mode; +}; + +} } } // end namespace bus::ti99::internal + +SLOT_INTERFACE_EXTERN(gromport4); +SLOT_INTERFACE_EXTERN(gromport8); + +#define MCFG_GROMPORT4_ADD( _tag ) \ + MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \ + bus::ti99::internal::gromport_device::set_mask(*device, 0x1fff); \ + MCFG_DEVICE_SLOT_INTERFACE(gromport4, "single", false) + +#define MCFG_GROMPORT8_ADD( _tag ) \ + MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \ + bus::ti99::internal::gromport_device::set_mask(*device, 0x3fff); \ + MCFG_DEVICE_SLOT_INTERFACE(gromport8, "single", false) + +#define MCFG_GROMPORT_READY_HANDLER( _ready ) \ + devcb = &bus::ti99::internal::gromport_device::static_set_ready_callback( *device, DEVCB_##_ready ); + +#define MCFG_GROMPORT_RESET_HANDLER( _reset ) \ + devcb = &bus::ti99::internal::gromport_device::static_set_reset_callback( *device, DEVCB_##_reset ); + +DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT, bus::ti99::internal, gromport_device) +DECLARE_DEVICE_TYPE_NS(TI99_CART, bus::ti99::internal, ti99_cartridge_device) +DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_SINGLE, bus::ti99::internal, ti99_single_cart_conn_device) +DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_MULTI, bus::ti99::internal, ti99_multi_cart_conn_device) +DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_GK, bus::ti99::internal, ti99_gkracker_device) + +#endif // MAME_BUS_TI99_INTERNAL_GROMPORT_H diff --git a/src/devices/bus/ti99/joyport/handset.cpp b/src/devices/bus/ti99/joyport/handset.cpp index 8e2833e2c84..d638d169684 100644 --- a/src/devices/bus/ti99/joyport/handset.cpp +++ b/src/devices/bus/ti99/joyport/handset.cpp @@ -40,6 +40,11 @@ #include "handset.h" #include "machine/tms9901.h" +DEFINE_DEVICE_TYPE_NS(TI99_JOYSTICK, bus::ti99::joyport, ti99_twin_joystick_device, "ti99_twinjoy", "TI-99/4(A) Twin Joysticks") +DEFINE_DEVICE_TYPE_NS(TI99_HANDSET, bus::ti99::joyport, ti99_handset_device, "ti99_handset", "TI-99/4 IR handset") + +namespace bus { namespace ti99 { namespace joyport { + #define LOG logerror #define VERBOSE 1 @@ -469,8 +474,6 @@ ioport_constructor ti99_handset_device::device_input_ports() const return INPUT_PORTS_NAME( handset ); } -DEFINE_DEVICE_TYPE(TI99_HANDSET, ti99_handset_device, "ti99_handset", "TI-99/4 IR handset") - /****************************************************************************** Twin Joystick ******************************************************************************/ @@ -534,5 +537,4 @@ ioport_constructor ti99_twin_joystick_device::device_input_ports() const { return INPUT_PORTS_NAME( joysticks ); } - -DEFINE_DEVICE_TYPE(TI99_JOYSTICK, ti99_twin_joystick_device, "ti99_twinjoy", "TI-99/4(A) Twin Joysticks") +} } } // end namespace bus::ti99::joyport diff --git a/src/devices/bus/ti99/joyport/handset.h b/src/devices/bus/ti99/joyport/handset.h index 043c426e50c..bdeda83899c 100644 --- a/src/devices/bus/ti99/joyport/handset.h +++ b/src/devices/bus/ti99/joyport/handset.h @@ -21,7 +21,7 @@ #include "joyport.h" -DECLARE_DEVICE_TYPE(TI99_HANDSET, ti99_handset_device) +namespace bus { namespace ti99 { namespace joyport { class ti99_handset_device : public device_t, public device_ti99_joyport_interface { @@ -61,8 +61,6 @@ private: /****************************************************************************/ -DECLARE_DEVICE_TYPE(TI99_JOYSTICK, ti99_twin_joystick_device) - class ti99_twin_joystick_device : public device_t, public device_ti99_joyport_interface { public: @@ -81,6 +79,9 @@ private: // and then routed back to the port via the joystick int m_joystick; }; +} } } // end namespace bus::ti99::joyport +DECLARE_DEVICE_TYPE_NS(TI99_HANDSET, bus::ti99::joyport, ti99_handset_device) +DECLARE_DEVICE_TYPE_NS(TI99_JOYSTICK, bus::ti99::joyport, ti99_twin_joystick_device) #endif // MAME_BUS_TI99_JOYPORT_HANDSET_H diff --git a/src/devices/bus/ti99/joyport/joyport.cpp b/src/devices/bus/ti99/joyport/joyport.cpp index 3ad74068913..806d04e6df1 100644 --- a/src/devices/bus/ti99/joyport/joyport.cpp +++ b/src/devices/bus/ti99/joyport/joyport.cpp @@ -40,7 +40,11 @@ #include "handset.h" #include "mecmouse.h" -ti99_joyport_device::ti99_joyport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +DEFINE_DEVICE_TYPE_NS(TI99_JOYPORT, bus::ti99::joyport, joyport_device, "ti99_joyport", "TI-99 Joystick port") + +namespace bus { namespace ti99 { namespace joyport { + +joyport_device::joyport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, TI99_JOYPORT, tag, owner, clock), device_slot_interface(mconfig, *this), m_interrupt(*this), m_connected(nullptr) @@ -50,7 +54,7 @@ ti99_joyport_device::ti99_joyport_device(const machine_config &mconfig, const ch /* Reads a value from the port. */ -uint8_t ti99_joyport_device::read_port() +uint8_t joyport_device::read_port() { return m_connected->read_dev(); } @@ -59,7 +63,7 @@ uint8_t ti99_joyport_device::read_port() This is used to select the device at the port. The device should keep this value until read() is called. */ -void ti99_joyport_device::write_port(int data) +void joyport_device::write_port(int data) { m_connected->write_dev(data); } @@ -67,7 +71,7 @@ void ti99_joyport_device::write_port(int data) /* This is only used for the handset device of the TI-99/4. It is driven by the VDP interrupt. */ -void ti99_joyport_device::pulse_clock() +void joyport_device::pulse_clock() { m_connected->pulse_clock(); } @@ -76,17 +80,17 @@ void ti99_joyport_device::pulse_clock() Propagate the interrupt to the defined target. Only used for the handset at the prototype 99/4. */ -WRITE_LINE_MEMBER( ti99_joyport_device::set_interrupt ) +WRITE_LINE_MEMBER( joyport_device::set_interrupt ) { m_interrupt(state); } -void ti99_joyport_device::device_start() +void joyport_device::device_start() { m_interrupt.resolve(); } -void ti99_joyport_device::device_config_complete() +void joyport_device::device_config_complete() { m_connected = dynamic_cast(subdevices().first()); } @@ -95,9 +99,11 @@ void ti99_joyport_device::device_config_complete() void device_ti99_joyport_interface::interface_config_complete() { - m_joyport = dynamic_cast(device().owner()); + m_joyport = dynamic_cast(device().owner()); } +} } } // end namespace bus::ti99::joyport + SLOT_INTERFACE_START( ti99_joystick_port ) SLOT_INTERFACE("twinjoy", TI99_JOYSTICK) SLOT_INTERFACE("mecmouse", TI99_MECMOUSE) @@ -113,4 +119,3 @@ SLOT_INTERFACE_START( ti99_joystick_port_994 ) SLOT_INTERFACE("handset", TI99_HANDSET) SLOT_INTERFACE_END -DEFINE_DEVICE_TYPE(TI99_JOYPORT, ti99_joyport_device, "ti99_joyport", "TI-99 Joystick port") diff --git a/src/devices/bus/ti99/joyport/joyport.h b/src/devices/bus/ti99/joyport/joyport.h index 789732ec1dd..44afd9f5fea 100644 --- a/src/devices/bus/ti99/joyport/joyport.h +++ b/src/devices/bus/ti99/joyport/joyport.h @@ -22,8 +22,9 @@ #pragma once +namespace bus { namespace ti99 { namespace joyport { -DECLARE_DEVICE_TYPE(TI99_JOYPORT, ti99_joyport_device) +class joyport_device; /******************************************************************** Common parent class of all devices attached to the joystick port @@ -39,22 +40,22 @@ protected: using device_slot_card_interface::device_slot_card_interface; virtual void interface_config_complete() override; - ti99_joyport_device* m_joyport = nullptr; + joyport_device* m_joyport = nullptr; }; /******************************************************************** Joystick port ********************************************************************/ -class ti99_joyport_device : public device_t, public device_slot_interface +class joyport_device : public device_t, public device_slot_interface { public: - ti99_joyport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + joyport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); uint8_t read_port(); void write_port(int data); void set_interrupt(int state); void pulse_clock(); - template static devcb_base &static_set_int_callback(device_t &device, Object &&cb) { return downcast(device).m_interrupt.set_callback(std::forward(cb)); } + template static devcb_base &static_set_int_callback(device_t &device, Object &&cb) { return downcast(device).m_interrupt.set_callback(std::forward(cb)); } protected: void device_start() override; @@ -65,12 +66,16 @@ private: device_ti99_joyport_interface* m_connected; }; +} } } // end namespace bus::ti99::joyport + SLOT_INTERFACE_EXTERN(ti99_joystick_port); SLOT_INTERFACE_EXTERN(ti99_joystick_port_994); SLOT_INTERFACE_EXTERN(ti99_joystick_port_gen); +DECLARE_DEVICE_TYPE_NS(TI99_JOYPORT, bus::ti99::joyport, joyport_device) + #define MCFG_JOYPORT_INT_HANDLER( _intcallb ) \ - devcb = &ti99_joyport_device::static_set_int_callback( *device, DEVCB_##_intcallb ); + devcb = &bus::ti99::joyport::joyport_device::static_set_int_callback( *device, DEVCB_##_intcallb ); #define MCFG_GENEVE_JOYPORT_ADD( _tag ) \ MCFG_DEVICE_ADD(_tag, TI99_JOYPORT, 0) \ @@ -84,4 +89,5 @@ SLOT_INTERFACE_EXTERN(ti99_joystick_port_gen); MCFG_DEVICE_ADD(_tag, TI99_JOYPORT, 0) \ MCFG_DEVICE_SLOT_INTERFACE(ti99_joystick_port_994, "twinjoy", false) + #endif // MAME_BUS_TI99_JOYPORT_JOYPORT_H diff --git a/src/devices/bus/ti99/joyport/mecmouse.cpp b/src/devices/bus/ti99/joyport/mecmouse.cpp index 75466df7844..51be546a467 100644 --- a/src/devices/bus/ti99/joyport/mecmouse.cpp +++ b/src/devices/bus/ti99/joyport/mecmouse.cpp @@ -51,15 +51,19 @@ #include "emu.h" #include "mecmouse.h" +DEFINE_DEVICE_TYPE_NS(TI99_MECMOUSE, bus::ti99::joyport, mecmouse_device, "ti99_mecmouse", "TI-99 Mechatronics Mouse") + +namespace bus { namespace ti99 { namespace joyport { + #define POLL_TIMER 1 -ti99_mecmouse_device::ti99_mecmouse_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +mecmouse_device::mecmouse_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, TI99_MECMOUSE, tag, owner, clock), device_ti99_joyport_interface(mconfig, *this), m_last_select(0), m_read_y_axis(false), m_x(0), m_y(0), m_x_buf(0), m_y_buf(0), m_last_mx(0), m_last_my(0), m_poll_timer(nullptr) { } -uint8_t ti99_mecmouse_device::read_dev() +uint8_t mecmouse_device::read_dev() { int answer; int buttons = ioport("MOUSE0")->read() & 3; @@ -81,7 +85,7 @@ uint8_t ti99_mecmouse_device::read_dev() /* Used to select lines. data = 0x01 (Joy1), 0x02 (Joy2) */ -void ti99_mecmouse_device::write_dev(uint8_t data) +void mecmouse_device::write_dev(uint8_t data) { if (data == 0x02) { if (m_last_select == 0x01) { @@ -128,7 +132,7 @@ void ti99_mecmouse_device::write_dev(uint8_t data) } } -void ti99_mecmouse_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +void mecmouse_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { // Poll the movement int new_mx, new_my; @@ -157,7 +161,7 @@ void ti99_mecmouse_device::device_timer(emu_timer &timer, device_timer_id id, in m_y += delta_y; } -void ti99_mecmouse_device::device_start() +void mecmouse_device::device_start() { m_poll_timer = timer_alloc(POLL_TIMER); // The poll time cannot depend on the console settings, since the TI-99/4A @@ -175,7 +179,7 @@ void ti99_mecmouse_device::device_start() save_item(NAME(m_last_my)); } -void ti99_mecmouse_device::device_reset() +void mecmouse_device::device_reset() { m_poll_timer->enable(true); m_last_select = 0; @@ -199,9 +203,10 @@ INPUT_PORTS_START( mecmouse ) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Mouse Button 2") PORT_PLAYER(1) INPUT_PORTS_END -ioport_constructor ti99_mecmouse_device::device_input_ports() const +ioport_constructor mecmouse_device::device_input_ports() const { return INPUT_PORTS_NAME( mecmouse ); } -DEFINE_DEVICE_TYPE(TI99_MECMOUSE, ti99_mecmouse_device, "ti99_mecmouse", "TI-99 Mechatronics Mouse") +} } } // end namespace bus::ti99::joyport + diff --git a/src/devices/bus/ti99/joyport/mecmouse.h b/src/devices/bus/ti99/joyport/mecmouse.h index f73a86c5f53..6d859376cd7 100644 --- a/src/devices/bus/ti99/joyport/mecmouse.h +++ b/src/devices/bus/ti99/joyport/mecmouse.h @@ -16,12 +16,12 @@ #include "joyport.h" -DECLARE_DEVICE_TYPE(TI99_MECMOUSE, ti99_mecmouse_device) +namespace bus { namespace ti99 { namespace joyport { -class ti99_mecmouse_device : public device_t, public device_ti99_joyport_interface +class mecmouse_device : public device_t, public device_ti99_joyport_interface { public: - ti99_mecmouse_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + mecmouse_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); uint8_t read_dev() override; void write_dev(uint8_t data) override; @@ -44,5 +44,8 @@ private: emu_timer *m_poll_timer; }; +} } } // end namespace bus::ti99::joyport + +DECLARE_DEVICE_TYPE_NS(TI99_MECMOUSE, bus::ti99::joyport, mecmouse_device) #endif // MAME_BUS_TI99_JOYPORT_MECMOUSE_H diff --git a/src/devices/bus/ti99/peb/bwg.cpp b/src/devices/bus/ti99/peb/bwg.cpp index 4294fe4a916..0f6b6f962a7 100644 --- a/src/devices/bus/ti99/peb/bwg.cpp +++ b/src/devices/bus/ti99/peb/bwg.cpp @@ -46,6 +46,10 @@ #include "bwg.h" #include "formats/ti99_dsk.h" +DEFINE_DEVICE_TYPE_NS(TI99_BWG, bus::ti99::peb, snug_bwg_device, "ti99_bwg", "SNUG BwG Floppy Controller") + +namespace bus { namespace ti99 { namespace peb { + // ---------------------------------- // Flags for debugging @@ -726,4 +730,4 @@ const tiny_rom_entry *snug_bwg_device::device_rom_region() const return ROM_NAME( bwg_fdc ); } -DEFINE_DEVICE_TYPE(TI99_BWG, snug_bwg_device, "ti99_bwg", "SNUG BwG Floppy Controller") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/bwg.h b/src/devices/bus/ti99/peb/bwg.h index de202241519..3eed7dce771 100644 --- a/src/devices/bus/ti99/peb/bwg.h +++ b/src/devices/bus/ti99/peb/bwg.h @@ -22,7 +22,7 @@ #include "machine/wd_fdc.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_BWG, snug_bwg_device) +namespace bus { namespace ti99 { namespace peb { class snug_bwg_device : public ti_expansion_card_device { @@ -139,4 +139,8 @@ private: required_device m_clock; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_BWG, bus::ti99::peb, snug_bwg_device) + #endif // MAME_BUS_TI99_PED_BWG_H diff --git a/src/devices/bus/ti99/peb/evpc.cpp b/src/devices/bus/ti99/peb/evpc.cpp index 38f96403126..10a7e0b36bf 100644 --- a/src/devices/bus/ti99/peb/evpc.cpp +++ b/src/devices/bus/ti99/peb/evpc.cpp @@ -39,6 +39,10 @@ #include "evpc.h" #include "speaker.h" +DEFINE_DEVICE_TYPE_NS(TI99_EVPC, bus::ti99::peb, snug_enhanced_video_device, "ti99_evpc", "SNUG Enhanced Video Processor Card") + +namespace bus { namespace ti99 { namespace peb { + #define EVPC_CRU_BASE 0x1400 #define TRACE_ADDRESS 0 @@ -387,7 +391,7 @@ void snug_enhanced_video_device::device_start() { m_dsrrom = memregion(DSRROM)->base(); m_novram = std::make_unique(NOVRAM_SIZE); - m_console_conn = downcast(machine().device(EVPC_CONN_TAG)); + m_console_conn = downcast(machine().device(EVPC_CONN_TAG)); save_item(NAME(m_address)); save_item(NAME(m_dsr_page)); save_item(NAME(m_inDsrArea)); @@ -495,4 +499,4 @@ machine_config_constructor snug_enhanced_video_device::device_mconfig_additions( return MACHINE_CONFIG_NAME( ti99_evpc ); } -DEFINE_DEVICE_TYPE(TI99_EVPC, snug_enhanced_video_device, "ti99_evpc", "SNUG Enhanced Video Processor Card") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/evpc.h b/src/devices/bus/ti99/peb/evpc.h index bb53e285a28..71276c26b37 100644 --- a/src/devices/bus/ti99/peb/evpc.h +++ b/src/devices/bus/ti99/peb/evpc.h @@ -20,10 +20,11 @@ #include "peribox.h" #include "video/v9938.h" #include "sound/sn76496.h" -#include "bus/ti99x/ti99defs.h" +#include "bus/ti99/ti99defs.h" #include "bus/ti99/colorbus/colorbus.h" +#include "bus/ti99/internal/evpcconn.h" -DECLARE_DEVICE_TYPE(TI99_EVPC, snug_enhanced_video_device) +namespace bus { namespace ti99 { namespace peb { class snug_enhanced_video_device : public ti_expansion_card_device, public device_nvram_interface { @@ -82,8 +83,12 @@ private: evpc_palette m_palette; required_device m_video; required_device m_sound; - required_device m_colorbus; - evpc_clock_connector* m_console_conn; + required_device m_colorbus; + bus::ti99::internal::evpc_clock_connector* m_console_conn; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_EVPC, bus::ti99::peb, snug_enhanced_video_device) + #endif // MAME_BUS_TI99_PEB_EVPC_H diff --git a/src/devices/bus/ti99/peb/hfdc.cpp b/src/devices/bus/ti99/peb/hfdc.cpp index 19478a75068..45c346ccb46 100644 --- a/src/devices/bus/ti99/peb/hfdc.cpp +++ b/src/devices/bus/ti99/peb/hfdc.cpp @@ -59,6 +59,10 @@ #include "formats/mfm_hd.h" #include "formats/ti99_dsk.h" // Format +DEFINE_DEVICE_TYPE_NS(TI99_HFDC, bus::ti99::peb, myarc_hfdc_device, "ti99_hfdc", "Myarc Hard and Floppy Disk Controller") + +namespace bus { namespace ti99 { namespace peb { + #define BUFFER "ram" #define FDC_TAG "hdc9234" #define CLOCK_TAG "mm58274c" @@ -1112,4 +1116,4 @@ ioport_constructor myarc_hfdc_device::device_input_ports() const return INPUT_PORTS_NAME( ti99_hfdc ); } -DEFINE_DEVICE_TYPE(TI99_HFDC, myarc_hfdc_device, "ti99_hfdc", "Myarc Hard and Floppy Disk Controller") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/hfdc.h b/src/devices/bus/ti99/peb/hfdc.h index c1bec7e014b..42b9e7e6490 100644 --- a/src/devices/bus/ti99/peb/hfdc.h +++ b/src/devices/bus/ti99/peb/hfdc.h @@ -27,7 +27,7 @@ #include "machine/hdc92x4.h" #include "machine/ram.h" -extern const device_type TI99_HFDC; +namespace bus { namespace ti99 { namespace peb { /* Implementation for modern floppy system. @@ -187,4 +187,8 @@ private: int m_readyflags; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_HFDC, bus::ti99::peb, myarc_hfdc_device) + #endif // MAME_BUS_TI99_PEB_HFDC_H diff --git a/src/devices/bus/ti99/peb/horizon.cpp b/src/devices/bus/ti99/peb/horizon.cpp index a597e0d31bb..34dc1e47d14 100644 --- a/src/devices/bus/ti99/peb/horizon.cpp +++ b/src/devices/bus/ti99/peb/horizon.cpp @@ -53,6 +53,10 @@ #include "emu.h" #include "horizon.h" +DEFINE_DEVICE_TYPE_NS(TI99_HORIZON, bus::ti99::peb, horizon_ramdisk_device, "ti99_horizon", "Horizon 4000 Ramdisk") + +namespace bus { namespace ti99 { namespace peb { + #define RAMREGION "ram32k" #define ROSREGION "ros8k" #define NVRAMREGION "nvram" @@ -491,4 +495,4 @@ ioport_constructor horizon_ramdisk_device::device_input_ports() const return INPUT_PORTS_NAME(horizon); } -DEFINE_DEVICE_TYPE(TI99_HORIZON, horizon_ramdisk_device, "ti99_horizon", "Horizon 4000 Ramdisk") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/horizon.h b/src/devices/bus/ti99/peb/horizon.h index d3a5f23817c..0ea76ffdc3e 100644 --- a/src/devices/bus/ti99/peb/horizon.h +++ b/src/devices/bus/ti99/peb/horizon.h @@ -19,7 +19,7 @@ #include "peribox.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_HORIZON, horizon_ramdisk_device) +namespace bus { namespace ti99 { namespace peb { class horizon_ramdisk_device : public ti_expansion_card_device, public device_nvram_interface { @@ -66,4 +66,8 @@ private: bool m_use_rambo; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_HORIZON, bus::ti99::peb, horizon_ramdisk_device) + #endif // MAME_BUS_TI99_PEB_HORIZON_H diff --git a/src/devices/bus/ti99/peb/hsgpl.cpp b/src/devices/bus/ti99/peb/hsgpl.cpp index 59b7e375db6..e0e16b5896a 100644 --- a/src/devices/bus/ti99/peb/hsgpl.cpp +++ b/src/devices/bus/ti99/peb/hsgpl.cpp @@ -121,6 +121,10 @@ #include "emu.h" #include "hsgpl.h" +DEFINE_DEVICE_TYPE_NS(TI99_HSGPL, bus::ti99::peb, snug_high_speed_gpl_device, "ti99_hsgpl", "SNUG High-speed GPL card") + +namespace bus { namespace ti99 { namespace peb { + #define CRU_BASE 0x1B00 #define SUPERCART_BASE 0x0800 @@ -714,4 +718,4 @@ ioport_constructor snug_high_speed_gpl_device::device_input_ports() const return INPUT_PORTS_NAME(ti99_hsgpl); } -DEFINE_DEVICE_TYPE(TI99_HSGPL, snug_high_speed_gpl_device, "ti99_hsgpl", "SNUG High-speed GPL card") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/hsgpl.h b/src/devices/bus/ti99/peb/hsgpl.h index 33a6456b2d1..8c5759a5346 100644 --- a/src/devices/bus/ti99/peb/hsgpl.h +++ b/src/devices/bus/ti99/peb/hsgpl.h @@ -22,7 +22,7 @@ #include "machine/at29x.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_HSGPL, snug_high_speed_gpl_device) +namespace bus { namespace ti99 { namespace peb { class snug_high_speed_gpl_device : public ti_expansion_card_device { @@ -81,4 +81,8 @@ private: int m_grom_address; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_HSGPL, bus::ti99::peb, snug_high_speed_gpl_device) + #endif // MAME_BUS_TI99_PEB_HSGPL_H diff --git a/src/devices/bus/ti99/peb/memex.cpp b/src/devices/bus/ti99/peb/memex.cpp index 3f33017092c..ff163e9c213 100644 --- a/src/devices/bus/ti99/peb/memex.cpp +++ b/src/devices/bus/ti99/peb/memex.cpp @@ -15,6 +15,10 @@ #include "emu.h" #include "memex.h" +DEFINE_DEVICE_TYPE_NS(TI99_MEMEX, bus::ti99::peb, geneve_memex_device, "ti99_memex", "Geneve memory expansion card") + +namespace bus { namespace ti99 { namespace peb { + #define RAMREGION "ram2meg" #define TRACE_CONFIG 0 @@ -158,4 +162,4 @@ ioport_constructor geneve_memex_device::device_input_ports() const return INPUT_PORTS_NAME( memex ); } -DEFINE_DEVICE_TYPE(TI99_MEMEX, geneve_memex_device, "ti99_memex", "Geneve memory expansion card") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/memex.h b/src/devices/bus/ti99/peb/memex.h index 12a9d47b9cb..6ddf14de15c 100644 --- a/src/devices/bus/ti99/peb/memex.h +++ b/src/devices/bus/ti99/peb/memex.h @@ -18,7 +18,8 @@ #include "peribox.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_MEMEX, geneve_memex_device) +namespace bus { namespace ti99 { namespace peb { + class geneve_memex_device : public ti_expansion_card_device { @@ -41,5 +42,8 @@ private: required_device m_ram; uint8_t m_switches; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_MEMEX, bus::ti99::peb, geneve_memex_device) #endif // MAME_BUS_TI99_PEB_MEMEX_H diff --git a/src/devices/bus/ti99/peb/myarcmem.cpp b/src/devices/bus/ti99/peb/myarcmem.cpp index 94de8980ec8..d7ac49e54fe 100644 --- a/src/devices/bus/ti99/peb/myarcmem.cpp +++ b/src/devices/bus/ti99/peb/myarcmem.cpp @@ -30,6 +30,10 @@ #include "emu.h" #include "myarcmem.h" +DEFINE_DEVICE_TYPE_NS(TI99_MYARCMEM, bus::ti99::peb, myarc_memory_expansion_device, "ti99_myarcmem", "Myarc Memory expansion card MEXP-1") + +namespace bus { namespace ti99 { namespace peb { + /* This card has two CRU bases where it answers. */ #define MYARCMEM_CRU_BASE1 0x1000 #define MYARCMEM_CRU_BASE2 0x1900 @@ -211,4 +215,4 @@ ioport_constructor myarc_memory_expansion_device::device_input_ports() const return INPUT_PORTS_NAME(myarc_exp); } -DEFINE_DEVICE_TYPE(TI99_MYARCMEM, myarc_memory_expansion_device, "ti99_myarcmem", "Myarc Memory expansion card MEXP-1") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/myarcmem.h b/src/devices/bus/ti99/peb/myarcmem.h index e2cf05d8660..85cac4d9d9f 100644 --- a/src/devices/bus/ti99/peb/myarcmem.h +++ b/src/devices/bus/ti99/peb/myarcmem.h @@ -18,7 +18,7 @@ #include "peribox.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_MYARCMEM, myarc_memory_expansion_device) +namespace bus { namespace ti99 { namespace peb { class myarc_memory_expansion_device : public ti_expansion_card_device { @@ -45,4 +45,8 @@ private: int m_size; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_MYARCMEM, bus::ti99::peb, myarc_memory_expansion_device) + #endif // MAME_BUS_TI99_PEB_MYARCMEM_H diff --git a/src/devices/bus/ti99/peb/pcode.cpp b/src/devices/bus/ti99/peb/pcode.cpp index 1dbe024ffba..5762e7aa66d 100644 --- a/src/devices/bus/ti99/peb/pcode.cpp +++ b/src/devices/bus/ti99/peb/pcode.cpp @@ -75,6 +75,10 @@ #include "emu.h" #include "pcode.h" +DEFINE_DEVICE_TYPE_NS(TI99_P_CODE, bus::ti99::peb, ti_pcode_card_device, "ti99_pcode", "TI-99 P-Code Card") + +namespace bus { namespace ti99 { namespace peb { + #define PCODE_GROM_TAG "pcode_grom" #define PCODE_ROM_TAG "pcode_rom" @@ -373,4 +377,4 @@ ioport_constructor ti_pcode_card_device::device_input_ports() const return INPUT_PORTS_NAME( ti99_pcode ); } -DEFINE_DEVICE_TYPE(TI99_P_CODE, ti_pcode_card_device, "ti99_pcode", "TI-99 P-Code Card") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/pcode.h b/src/devices/bus/ti99/peb/pcode.h index 088ca1ae4b3..6e9fbe34a94 100644 --- a/src/devices/bus/ti99/peb/pcode.h +++ b/src/devices/bus/ti99/peb/pcode.h @@ -20,7 +20,7 @@ #include "peribox.h" #include "machine/tmc0430.h" -DECLARE_DEVICE_TYPE(TI99_P_CODE, ti_pcode_card_device) +namespace bus { namespace ti99 { namespace peb { class ti_pcode_card_device : public ti_expansion_card_device { @@ -64,4 +64,8 @@ private: int m_address; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_P_CODE, bus::ti99::peb, ti_pcode_card_device) + #endif // MAME_BUS_TI99_PEB_PCODE_H diff --git a/src/devices/bus/ti99/peb/peribox.cpp b/src/devices/bus/ti99/peb/peribox.cpp index efc8c148c61..29de4b6c432 100644 --- a/src/devices/bus/ti99/peb/peribox.cpp +++ b/src/devices/bus/ti99/peb/peribox.cpp @@ -196,6 +196,16 @@ CRUCLK* 51||52 DBIN #include "memex.h" #include "horizon.h" +DEFINE_DEVICE_TYPE_NS(PERIBOX, bus::ti99::peb, peribox_device, "peribox", "Peripheral expansion box") +DEFINE_DEVICE_TYPE_NS(PERIBOX_SLOT, bus::ti99::peb, peribox_slot_device, "peribox_slot", "TI P-Box slot") + +DEFINE_DEVICE_TYPE_NS(PERIBOX_EV, bus::ti99::peb, peribox_ev_device, "peribox_ev", "Peripheral expansion box EVPC") +DEFINE_DEVICE_TYPE_NS(PERIBOX_SG, bus::ti99::peb, peribox_sg_device, "peribox_sg", "Peripheral expansion box SGCPU") +DEFINE_DEVICE_TYPE_NS(PERIBOX_GEN, bus::ti99::peb, peribox_gen_device, "peribox_gen", "Peripheral expansion box Geneve") +DEFINE_DEVICE_TYPE_NS(PERIBOX_998, bus::ti99::peb, peribox_998_device, "peribox_998", "Peripheral expansion box 99/8") + +namespace bus { namespace ti99 { namespace peb { + /* Debugging flags. Set to 0 or 1. */ @@ -744,10 +754,4 @@ WRITE_LINE_MEMBER( peribox_slot_device::set_ready ) } /***************************************************************************/ -DEFINE_DEVICE_TYPE(PERIBOX, peribox_device, "peribox", "Peripheral expansion box") -DEFINE_DEVICE_TYPE(PERIBOX_SLOT, peribox_slot_device, "peribox_slot", "TI P-Box slot") - -DEFINE_DEVICE_TYPE(PERIBOX_EV, peribox_ev_device, "peribox_ev", "Peripheral expansion box EVPC") -DEFINE_DEVICE_TYPE(PERIBOX_SG, peribox_sg_device, "peribox_sg", "Peripheral expansion box SGCPU") -DEFINE_DEVICE_TYPE(PERIBOX_GEN, peribox_gen_device, "peribox_gen", "Peripheral expansion box Geneve") -DEFINE_DEVICE_TYPE(PERIBOX_998, peribox_998_device, "peribox_998", "Peripheral expansion box 99/8") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/peribox.h b/src/devices/bus/ti99/peb/peribox.h index 8bea48c7612..4ee0e454f8e 100644 --- a/src/devices/bus/ti99/peb/peribox.h +++ b/src/devices/bus/ti99/peb/peribox.h @@ -16,17 +16,12 @@ #pragma once -#include "bus/ti99x/ti99defs.h" +#include "bus/ti99/ti99defs.h" -DECLARE_DEVICE_TYPE(PERIBOX, peribox_device) -DECLARE_DEVICE_TYPE(PERIBOX_SLOT, peribox_slot_device) - -DECLARE_DEVICE_TYPE(PERIBOX_EV, peribox_ev_device) -DECLARE_DEVICE_TYPE(PERIBOX_SG, peribox_sg_device) -DECLARE_DEVICE_TYPE(PERIBOX_GEN, peribox_gen_device) -DECLARE_DEVICE_TYPE(PERIBOX_998, peribox_998_device) +namespace bus { namespace ti99 { namespace peb { class ti_expansion_card_device; +class peribox_slot_device; /***************************************************************************** The overall Peripheral Expansion Box. @@ -257,15 +252,25 @@ protected: MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _default, false) #define MCFG_PERIBOX_INTA_HANDLER( _inta ) \ - devcb = &peribox_device::static_set_inta_callback( *device, DEVCB_##_inta ); + devcb = &bus::ti99::peb::peribox_device::static_set_inta_callback( *device, DEVCB_##_inta ); #define MCFG_PERIBOX_INTB_HANDLER( _intb ) \ - devcb = &peribox_device::static_set_intb_callback( *device, DEVCB_##_intb ); + devcb = &bus::ti99::peb::peribox_device::static_set_intb_callback( *device, DEVCB_##_intb ); #define MCFG_PERIBOX_READY_HANDLER( _ready ) \ - devcb = &peribox_device::static_set_ready_callback( *device, DEVCB_##_ready ); + devcb = &bus::ti99::peb::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 ); + devcb = &bus::ti99::peb::peribox_device::static_set_lcp_callback( *device, DEVCB_##_lcp ); + +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(PERIBOX, bus::ti99::peb, peribox_device) +DECLARE_DEVICE_TYPE_NS(PERIBOX_SLOT, bus::ti99::peb, peribox_slot_device) + +DECLARE_DEVICE_TYPE_NS(PERIBOX_EV, bus::ti99::peb, peribox_ev_device) +DECLARE_DEVICE_TYPE_NS(PERIBOX_SG, bus::ti99::peb, peribox_sg_device) +DECLARE_DEVICE_TYPE_NS(PERIBOX_GEN, bus::ti99::peb, peribox_gen_device) +DECLARE_DEVICE_TYPE_NS(PERIBOX_998, bus::ti99::peb, peribox_998_device) #endif // MAME_BUS_TI99_PEB_PERIBOX_H diff --git a/src/devices/bus/ti99/peb/samsmem.cpp b/src/devices/bus/ti99/peb/samsmem.cpp index ba3f7240ef6..957012daa83 100644 --- a/src/devices/bus/ti99/peb/samsmem.cpp +++ b/src/devices/bus/ti99/peb/samsmem.cpp @@ -26,6 +26,10 @@ #include "emu.h" #include "samsmem.h" +DEFINE_DEVICE_TYPE_NS(TI99_SAMSMEM, bus::ti99::peb, sams_memory_expansion_device, "ti99_sams", "SuperAMS memory expansion card") + +namespace bus { namespace ti99 { namespace peb { + #define SAMS_CRU_BASE 0x1e00 sams_memory_expansion_device::sams_memory_expansion_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) @@ -133,4 +137,4 @@ void sams_memory_expansion_device::device_reset() for (auto & elem : m_mapper) elem = 0; } -DEFINE_DEVICE_TYPE(TI99_SAMSMEM, sams_memory_expansion_device, "ti99_sams", "SuperAMS memory expansion card") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/samsmem.h b/src/devices/bus/ti99/peb/samsmem.h index 0166ad66acb..3ce69fb1a64 100644 --- a/src/devices/bus/ti99/peb/samsmem.h +++ b/src/devices/bus/ti99/peb/samsmem.h @@ -20,7 +20,7 @@ #include "peribox.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_SAMSMEM, sams_memory_expansion_device) +namespace bus { namespace ti99 { namespace peb { class sams_memory_expansion_device : public ti_expansion_card_device { @@ -46,4 +46,8 @@ private: bool m_access_mapper; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_SAMSMEM, bus::ti99::peb, sams_memory_expansion_device) + #endif // MAME_BUS_TI99_PEB_SAMSMEM_H diff --git a/src/devices/bus/ti99/peb/spchsyn.cpp b/src/devices/bus/ti99/peb/spchsyn.cpp index dfa4eacddc2..7e27bb65e2d 100644 --- a/src/devices/bus/ti99/peb/spchsyn.cpp +++ b/src/devices/bus/ti99/peb/spchsyn.cpp @@ -28,6 +28,9 @@ #include "sound/wave.h" #include "speaker.h" +DEFINE_DEVICE_TYPE_NS(TI99_SPEECH, bus::ti99::peb, ti_speech_synthesizer_device, "ti99_speech", "TI-99 Speech synthesizer (on adapter card)") + +namespace bus { namespace ti99 { namespace peb { #define TRACE_MEM 0 #define TRACE_ADDR 0 @@ -175,4 +178,5 @@ const tiny_rom_entry *ti_speech_synthesizer_device::device_rom_region() const return ROM_NAME( ti99_speech ); } -DEFINE_DEVICE_TYPE(TI99_SPEECH, ti_speech_synthesizer_device, "ti99_speech", "TI-99 Speech synthesizer (on adapter card)") +} } } // end namespace bus::ti99::peb + diff --git a/src/devices/bus/ti99/peb/spchsyn.h b/src/devices/bus/ti99/peb/spchsyn.h index 5edf4bffc45..e6ddcde9f66 100644 --- a/src/devices/bus/ti99/peb/spchsyn.h +++ b/src/devices/bus/ti99/peb/spchsyn.h @@ -18,7 +18,7 @@ #include "peribox.h" #include "sound/tms5220.h" -DECLARE_DEVICE_TYPE(TI99_SPEECH, ti_speech_synthesizer_device) +namespace bus { namespace ti99 { namespace peb { class ti_speech_synthesizer_device : public ti_expansion_card_device { @@ -46,4 +46,8 @@ private: bool m_sbe; // Signal "Speech block enable" }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_SPEECH, bus::ti99::peb, ti_speech_synthesizer_device) + #endif // MAME_BUS_TI99_PEB_SPCHSYN_H diff --git a/src/devices/bus/ti99/peb/ti_32kmem.cpp b/src/devices/bus/ti99/peb/ti_32kmem.cpp index 79fc21e0a77..7001527ade6 100644 --- a/src/devices/bus/ti99/peb/ti_32kmem.cpp +++ b/src/devices/bus/ti99/peb/ti_32kmem.cpp @@ -31,6 +31,10 @@ #include "emu.h" #include "ti_32kmem.h" +DEFINE_DEVICE_TYPE_NS(TI99_32KMEM, bus::ti99::peb, ti_32k_expcard_device, "ti99_32kmem", "TI-99 32KiB memory expansion card") + +namespace bus { namespace ti99 { namespace peb { + #define RAMREGION "ram32k" ti_32k_expcard_device::ti_32k_expcard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) @@ -106,4 +110,4 @@ machine_config_constructor ti_32k_expcard_device::device_mconfig_additions() con return MACHINE_CONFIG_NAME( mem32k ); } -DEFINE_DEVICE_TYPE(TI99_32KMEM, ti_32k_expcard_device, "ti99_32kmem", "TI-99 32KiB memory expansion card") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/ti_32kmem.h b/src/devices/bus/ti99/peb/ti_32kmem.h index a8548bb9090..d8a366982cd 100644 --- a/src/devices/bus/ti99/peb/ti_32kmem.h +++ b/src/devices/bus/ti99/peb/ti_32kmem.h @@ -19,7 +19,7 @@ #include "peribox.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_32KMEM, ti_32k_expcard_device) +namespace bus { namespace ti99 { namespace peb { class ti_32k_expcard_device : public ti_expansion_card_device { @@ -39,4 +39,8 @@ private: required_device m_ram; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_32KMEM, bus::ti99::peb, ti_32k_expcard_device) + #endif // MAME_BUS_TI99_PEB_TI_32KMEM_H diff --git a/src/devices/bus/ti99/peb/ti_fdc.cpp b/src/devices/bus/ti99/peb/ti_fdc.cpp index 0087a18843a..59eb47badc7 100644 --- a/src/devices/bus/ti99/peb/ti_fdc.cpp +++ b/src/devices/bus/ti99/peb/ti_fdc.cpp @@ -16,6 +16,10 @@ #include "ti_fdc.h" #include "formats/ti99_dsk.h" +DEFINE_DEVICE_TYPE_NS(TI99_FDC, bus::ti99::peb, ti_fdc_device, "ti99_fdc", "TI-99 Standard DSSD Floppy Controller") + +namespace bus { namespace ti99 { namespace peb { + // ---------------------------------- // Flags for debugging @@ -478,4 +482,4 @@ const tiny_rom_entry *ti_fdc_device::device_rom_region() const return ROM_NAME( ti_fdc ); } -DEFINE_DEVICE_TYPE(TI99_FDC, ti_fdc_device, "ti99_fdc", "TI-99 Standard DSSD Floppy Controller") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/ti_fdc.h b/src/devices/bus/ti99/peb/ti_fdc.h index 4df43c7832e..e498ce2ba41 100644 --- a/src/devices/bus/ti99/peb/ti_fdc.h +++ b/src/devices/bus/ti99/peb/ti_fdc.h @@ -20,7 +20,7 @@ #include "machine/wd_fdc.h" #include "imagedev/floppy.h" -DECLARE_DEVICE_TYPE(TI99_FDC, ti_fdc_device) +namespace bus { namespace ti99 { namespace peb { class ti_fdc_device : public ti_expansion_card_device { @@ -112,4 +112,8 @@ private: bool m_debug_dataout; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_FDC, bus::ti99::peb, ti_fdc_device) + #endif // MAME_BUS_TI99_PEB_TI_FDC_H diff --git a/src/devices/bus/ti99/peb/ti_rs232.cpp b/src/devices/bus/ti99/peb/ti_rs232.cpp index e3b8634b172..7f2d6e4d987 100644 --- a/src/devices/bus/ti99/peb/ti_rs232.cpp +++ b/src/devices/bus/ti99/peb/ti_rs232.cpp @@ -105,6 +105,12 @@ #include "emu.h" #include "ti_rs232.h" +DEFINE_DEVICE_TYPE_NS(TI99_RS232, bus::ti99::peb, ti_rs232_pio_device, "ti99_rs232", "TI-99 RS232/PIO interface") +DEFINE_DEVICE_TYPE_NS(TI99_RS232_DEV, bus::ti99::peb, ti_rs232_attached_device, "ti99_rs232_atttached", "TI-99 Serial attached device") +DEFINE_DEVICE_TYPE_NS(TI99_PIO_DEV, bus::ti99::peb, ti_pio_attached_device, "ti99_pio_attached", "TI-99 Parallel attached device") + +namespace bus { namespace ti99 { namespace peb { + #define SENILA_0_BIT 0x80 #define SENILA_1_BIT 0x40 @@ -1145,6 +1151,4 @@ ioport_constructor ti_rs232_pio_device::device_input_ports() const return INPUT_PORTS_NAME(ti_rs232); } -DEFINE_DEVICE_TYPE(TI99_RS232, ti_rs232_pio_device, "ti99_rs232", "TI-99 RS232/PIO interface") -DEFINE_DEVICE_TYPE(TI99_RS232_DEV, ti_rs232_attached_device, "ti99_rs232_atttached", "TI-99 Serial attached device") -DEFINE_DEVICE_TYPE(TI99_PIO_DEV, ti_pio_attached_device, "ti99_pio_attached", "TI-99 Parallel attached device") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/ti_rs232.h b/src/devices/bus/ti99/peb/ti_rs232.h index 6f246363323..f6f70654ed8 100644 --- a/src/devices/bus/ti99/peb/ti_rs232.h +++ b/src/devices/bus/ti99/peb/ti_rs232.h @@ -18,10 +18,10 @@ #include "peribox.h" #include "machine/tms9902.h" -DECLARE_DEVICE_TYPE(TI99_RS232, ti_rs232_pio_device) -DECLARE_DEVICE_TYPE(TI99_RS232_DEV, ti_rs232_attached_device) -DECLARE_DEVICE_TYPE(TI99_PIO_DEV, ti_pio_attached_device) +namespace bus { namespace ti99 { namespace peb { +class ti_pio_attached_device; +class ti_rs232_attached_device; class ti_rs232_pio_device : public ti_expansion_card_device { @@ -159,4 +159,10 @@ protected: void call_unload() override; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_RS232, bus::ti99::peb, ti_rs232_pio_device) +DECLARE_DEVICE_TYPE_NS(TI99_RS232_DEV, bus::ti99::peb, ti_rs232_attached_device) +DECLARE_DEVICE_TYPE_NS(TI99_PIO_DEV, bus::ti99::peb, ti_pio_attached_device) + #endif // MAME_BUS_TI99_PEB_TI_RS232_H diff --git a/src/devices/bus/ti99/peb/tn_ide.cpp b/src/devices/bus/ti99/peb/tn_ide.cpp index 803fd9e9d21..9c766c113ef 100644 --- a/src/devices/bus/ti99/peb/tn_ide.cpp +++ b/src/devices/bus/ti99/peb/tn_ide.cpp @@ -30,6 +30,10 @@ #include "emu.h" #include "tn_ide.h" +DEFINE_DEVICE_TYPE_NS(TI99_IDE, bus::ti99::peb, nouspikel_ide_interface_device, "ti99_ide", "Nouspikel IDE interface card") + +namespace bus { namespace ti99 { namespace peb { + #define CRU_BASE 0x1000 #define RAMREGION "ram" @@ -382,4 +386,5 @@ ioport_constructor nouspikel_ide_interface_device::device_input_ports() const return INPUT_PORTS_NAME(tn_ide); } -DEFINE_DEVICE_TYPE(TI99_IDE, nouspikel_ide_interface_device, "ti99_ide", "Nouspikel IDE interface card") +} } } // end namespace bus::ti99::peb + diff --git a/src/devices/bus/ti99/peb/tn_ide.h b/src/devices/bus/ti99/peb/tn_ide.h index edef805b2f1..de2888771ff 100644 --- a/src/devices/bus/ti99/peb/tn_ide.h +++ b/src/devices/bus/ti99/peb/tn_ide.h @@ -21,7 +21,7 @@ #include "machine/ram.h" #include "machine/rtc65271.h" -DECLARE_DEVICE_TYPE(TI99_IDE, nouspikel_ide_interface_device) +namespace bus { namespace ti99 { namespace peb { class nouspikel_ide_interface_device : public ti_expansion_card_device { @@ -63,4 +63,8 @@ private: required_device m_ram; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_IDE, bus::ti99::peb, nouspikel_ide_interface_device) + #endif // MAME_BUS_TI99_PEB_TN_IDE_H diff --git a/src/devices/bus/ti99/peb/tn_usbsm.cpp b/src/devices/bus/ti99/peb/tn_usbsm.cpp index 9a68ad1d715..91fa72c6fc4 100644 --- a/src/devices/bus/ti99/peb/tn_usbsm.cpp +++ b/src/devices/bus/ti99/peb/tn_usbsm.cpp @@ -42,6 +42,10 @@ #include "emu.h" #include "tn_usbsm.h" +DEFINE_DEVICE_TYPE_NS(TI99_USBSM, bus::ti99::peb, nouspikel_usb_smartmedia_device, "ti99_usbsm", "Nouspikel USB/Smartmedia card") + +namespace bus { namespace ti99 { namespace peb { + #define STRATA_TAG "strata" #define RAM1_TAG "ram512k_lb" @@ -375,5 +379,4 @@ ioport_constructor nouspikel_usb_smartmedia_device::device_input_ports() const { return INPUT_PORTS_NAME(tn_usbsm); } - -DEFINE_DEVICE_TYPE(TI99_USBSM, nouspikel_usb_smartmedia_device, "ti99_usbsm", "Nouspikel USB/Smartmedia card") +} } } // end namespace bus::ti99::peb diff --git a/src/devices/bus/ti99/peb/tn_usbsm.h b/src/devices/bus/ti99/peb/tn_usbsm.h index 285533a572e..e30c1cc13f2 100644 --- a/src/devices/bus/ti99/peb/tn_usbsm.h +++ b/src/devices/bus/ti99/peb/tn_usbsm.h @@ -21,7 +21,7 @@ #include "machine/strata.h" #include "machine/ram.h" -DECLARE_DEVICE_TYPE(TI99_USBSM, nouspikel_usb_smartmedia_device) +namespace bus { namespace ti99 { namespace peb { class nouspikel_usb_smartmedia_device : public ti_expansion_card_device { @@ -59,5 +59,8 @@ private: required_device m_smartmedia; required_device m_flash; }; +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_USBSM, bus::ti99::peb, nouspikel_usb_smartmedia_device) #endif // MAME_BUS_TI99_PEB_TN_USBSM_H diff --git a/src/devices/bus/ti99/ti99defs.h b/src/devices/bus/ti99/ti99defs.h new file mode 100644 index 00000000000..87d43c5c38c --- /dev/null +++ b/src/devices/bus/ti99/ti99defs.h @@ -0,0 +1,160 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + Common definitions for TI family + Should be included in each component. + + Michael Zapf + +*****************************************************************************/ + +#ifndef MAME_BUS_TI99X_TI99DEFS_H +#define MAME_BUS_TI99X_TI99DEFS_H + +#pragma once + +// TI-99/4(A) +#define TISOUNDCHIP_TAG "soundchip" +#define GROMPORT_TAG "gromport" +#define PERIBOX_TAG "peb" +#define VDP_TAG "vdp" +#define TMS9901_TAG "tms9901" +#define SCREEN_TAG "screen" +#define GROM0_TAG "console_grom_0" +#define GROM1_TAG "console_grom_1" +#define GROM2_TAG "console_grom_2" +#define JOYPORT_TAG "joyport" +#define EVPC_CONN_TAG "evpc_conn" +#define DATAMUX_TAG "datamux_16_8" +#define PADRAM_TAG "scratchpad" +#define EXPRAM_TAG "internal_32k_mod" +#define DSRROM "dsrrom" +#define CONSOLEROM "console_rom" +#define CONSOLEGROM "cons_grom" + +#define VDPFREQ XTAL_10_738635MHz +#define GROMFREQ VDPFREQ/24 + +// TI-99/8 +#define SRAM_TAG "sram8" +#define DRAM_TAG "dram8" +#define MAPPER_TAG "mapper" +#define MAINBOARD8_TAG "mainboard8" +#define SPEECHSYN_TAG "speech" + +#define ROM0_REG "rom0_region" +#define ROM1_REG "rom1_region" +#define PASCAL_REG "pascal_region" +#define SYSGROM_REG "sysgrom_region" +#define GROMLIB1_REG "gromlib1_region" +#define GROMLIB2_REG "gromlib2_region" +#define GROMLIB3_REG "gromlib3_region" +#define SPEECHROM_REG "speech_region" + +#define GROMLIB_TAG "gromlib" +#define SYSGROM_TAG GROMLIB_TAG "0" +#define SYSGROM0_TAG SYSGROM_TAG "_0" +#define SYSGROM1_TAG SYSGROM_TAG "_1" +#define SYSGROM2_TAG SYSGROM_TAG "_2" + +#define GLIB1_TAG GROMLIB_TAG "1" +#define GLIB10_TAG GLIB1_TAG "_0" +#define GLIB11_TAG GLIB1_TAG "_1" +#define GLIB12_TAG GLIB1_TAG "_2" +#define GLIB13_TAG GLIB1_TAG "_3" +#define GLIB14_TAG GLIB1_TAG "_4" +#define GLIB15_TAG GLIB1_TAG "_5" +#define GLIB16_TAG GLIB1_TAG "_6" +#define GLIB17_TAG GLIB1_TAG "_7" + +#define GLIB2_TAG GROMLIB_TAG "2" +#define GLIB20_TAG GLIB2_TAG "_0" +#define GLIB21_TAG GLIB2_TAG "_1" +#define GLIB22_TAG GLIB2_TAG "_2" +#define GLIB23_TAG GLIB2_TAG "_3" +#define GLIB24_TAG GLIB2_TAG "_4" +#define GLIB25_TAG GLIB2_TAG "_5" +#define GLIB26_TAG GLIB2_TAG "_6" +#define GLIB27_TAG GLIB2_TAG "_7" + +#define GLIB3_TAG GROMLIB_TAG "3" +#define GLIB30_TAG GLIB3_TAG "_0" +#define GLIB31_TAG GLIB3_TAG "_1" +#define GLIB32_TAG GLIB3_TAG "_2" + +// v9938 +#define COLORBUS_TAG "colorbus" + +// Geneve +#define GKEYBOARD_TAG "gkeyboard" +#define GMAPPER_TAG "gmapper" +#define GMOUSE_TAG "gmouse" +#define GCLOCK_TAG "mm58274c" +#define PFM512_TAG "pfm512" +#define PFM512A_TAG "pfm512a" + +#define READ16Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint16_t *value, ATTR_UNUSED uint16_t mem_mask) +#define DECLARE_READ16Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint16_t *value, ATTR_UNUSED uint16_t mem_mask = 0xffff) +#define READ8Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint8_t *value, ATTR_UNUSED uint8_t mem_mask) +#define DECLARE_READ8Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint8_t *value, ATTR_UNUSED uint8_t 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 + +/* + Base class for all devices that have a special read method. This read + methods differs from the standard READ8/16 by allowing for a "high-impedance" + state "Z" which means that the bus lines are not changed. That way, we can + built busses with parallel devices as in the real machine, and only the + active device changes the bus lines. +*/ +class bus8z_device : public device_t +{ +protected: + bus8z_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) { } +public: + virtual DECLARE_READ8Z_MEMBER(readz) = 0; + virtual DECLARE_WRITE8_MEMBER(write) = 0; + virtual DECLARE_SETADDRESS_DBIN_MEMBER( setaddress_dbin ) { } +}; + +class bus16z_device : device_t +{ +protected: + bus16z_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) { } +public: + virtual DECLARE_READ16Z_MEMBER(read16z) = 0; + virtual DECLARE_WRITE16_MEMBER(write16) = 0; + virtual DECLARE_SETADDRESS_DBIN_MEMBER( setaddress_dbin ) { } +}; + +/**************************************************************************** + Constants +****************************************************************************/ + +enum +{ + GM_TURBO = 1, + GM_TIM = 2 +}; + +enum +{ + GENEVE_098 = 0, + GENEVE_100, + GENEVE_PFM512, + GENEVE_PFM512A +}; + +#endif // MAME_BUS_TI99X_TI99DEFS_H diff --git a/src/devices/bus/ti99x/998board.cpp b/src/devices/bus/ti99x/998board.cpp deleted file mode 100644 index b0986a8454a..00000000000 --- a/src/devices/bus/ti99x/998board.cpp +++ /dev/null @@ -1,2308 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/*************************************************************************** - - TI-99/8 main board logic - - This component implements the address decoder and mapper logic from the - TI-99/8 console. - - The TI-99/8 defines a "logical address map" with 64 KiB (according to the - 16 address bits) and a "physical address map" with 16 MiB (according to the - 24 address bits of the mapper). Note that the mapper only uses 16 outgoing - address lines and multiplexes the address bytes. - - Note: The TI-99/8's internal codename was "Armadillo" - - - +-------+ +--------+ - | CPU |========LogAddrBus======| Mapper |====PhysAddrBus========== - | TMS | || | AMIGO | || - | 9995 | +----------+ | | +----------+ - | | | Logical | +--------+ | Physical | - +-------+ | space | | | space | - | decoder | | | decoder | - | VAQUERRO | | | MOFETTA | - +----------+ | +----------+ - | | | - +--------------------+ | +---------------------+ - | Devices | | | Devices | - | +-------+ | | | - | ROM0 | SRAM | | | DRAM (POLLO) | - | Video | ---- | | | ROM1 | - | Speech | Maps--+--+ | Cartridge port | - | GROM +-------+ | PEB | - | Sound | | Hexbus (OSO) | - +--------------------+ +---------------------+ - - Custom chips - ------------ - The chipset of the TI-99/8 consists of five specifically programmed chips. - All are nicknamed after some Spanish words (albeit sometimes misspelled) - - VAQUERRO: Logical Address Space decoder ("Vaquero" = "Cowboy") - MOFETTA : Physical Address Space decoder ("Mofeta" = "Skunk") - AMIGO : Mapper ("Amigo" = "Friend") - OSO : Hexbus adapter ("Oso" = "Bear") - POLLO : DRAM controller (Not emulated) ("Pollo" = "Chicken") - - See the comments for the respective chip implementation for details. - - - ROM contents - ------------ - The ROM0 chip is accessible at addresses 0000-1FFF in the logical address - space of the compatibility mode. It contains the GPL interpreter. In - native mode the ROM0 chip is invisible. - - ROM0 - offset Logical address Name - ----------------------------------- - 0000 0000-1FFF ROM0 - - - The ROM1 chip contains 32 KiB of various system software. It is located in - the physical address space, so it must be mapped into the logical address - space by defining an appropriate map. - - ROM1 - offset Physical address Name - ---------------------------------------------------------- - 0000 FFA000-FFDFFF ROM1 - 4000 FF4000-FF5FFF @CRU>2700 Text-to-speech ROM/DSR - 6000 FF4000-FF5FFF @CRU>1700 Hexbus DSR - - The DSR portions have to be selected via the CRU bits >1700 or >2700. - - - CRU map (I/O address space) - =========================== - 0000-003e: TMS9901 system interface (see ti99_8.c) - 1700-17fe: Hexbus - 2000-26fe: Future external devices - 2700-27fe: Additional ROM ("internal DSR") - 2702 : System reset (when set to 1) - 2800-3ffe: Future external devices - 4000-fffe: Future external devices - - The TMS9995 offers the full 15-bit CRU address space. Devices designed for - the TI-99/4A should only be accessed in the area 1000-1ffe. They will (by - design) incompletely decode the CRU address and be mirrored in the higher - areas. - - Note that the cartridge port of the TI-99/8 offers support for 16K ROM - cartridges, but lacks CRU support. - - Michael Zapf, October 2010 - February 2012: Rewritten as class - March 2016: Redesigned for custom chip emulation - - Informations taken from - [1] ARMADILLO PRODUCT SPECIFICATIONS - [2] TI-99/8 Graphics Programming Language interpreter - -***************************************************************************/ - -#include "emu.h" -#include "998board.h" - -#define TRACE_CRU 0 -#define TRACE_ADDRESS 0 -#define TRACE_MAP 0 -#define TRACE_OSO 0 -#define TRACE_DECODE 0 -#define TRACE_READY 0 -#define TRACE_MEM 0 -#define TRACE_CLOCK 0 -#define TRACE_DETAIL 0 -#define TRACE_MOFETTA 0 -#define TRACE_AMIGO 0 -#define TRACE_WS 0 -#define TRACE_CPURY 0 -#define TRACE_GROM 0 -#define TRACE_PUNMAP 0 - -enum -{ - SGMSEL = 1, - TSGSEL = 2, - P8GSEL = 4, - P3GSEL = 8, - VIDSEL = 16 -}; - -mainboard8_device::mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, TI99_MAINBOARD8, tag, owner, clock), - m_A14_set(false), - m_pending_write(false), - m_speech_ready(true), - m_sound_ready(true), - m_pbox_ready(true), - m_ready(*this), - m_console_reset(*this), - m_hold_line(*this), - m_vaquerro(*this, VAQUERRO_TAG), - m_mofetta(*this, MOFETTA_TAG), - m_amigo(*this, AMIGO_TAG), - m_oso(*this, OSO_TAG), - m_video(*owner, VDP_TAG), // subdevice of main class - m_sound(*owner, TISOUNDCHIP_TAG), - m_speech(*owner, SPEECHSYN_TAG), - m_gromport(*owner, GROMPORT_TAG), - m_peb(*owner, PERIBOX_TAG), - m_sram(*owner, SRAM_TAG), - m_dram(*owner, DRAM_TAG), - m_sgrom_idle(true), - m_tsgrom_idle(true), - m_p8grom_idle(true), - m_p3grom_idle(true) -{ -} - -// Debugger support -// The memory accesses by the debugger are routed around the custom chip logic - -READ8_MEMBER( mainboard8_device::debugger_read ) -{ - int logical_address = offset; - bool compat_mode = (m_crus_debug==ASSERT_LINE); - - // Check whether the mapper itself is accessed - int mapaddr = compat_mode? 0x8810 : 0xf870; - bool mapper_accessed = ((offset & 0xfff1)==mapaddr); - - if (mapper_accessed) return 0; // do not allow the debugger to mess with the mapper - - // or SRAM - int sramaddr = compat_mode? 0x8000 : 0xf000; - - if ((offset & 0xf800)==sramaddr) - { - // SRAM access - return m_sram->pointer()[logical_address & 0x07ff]; - } - if ((offset & 0xe000)==0x0000 && compat_mode) - { - // ROM0 access - return m_rom0[logical_address & 0x1fff]; - } - - // Physical space - u8 value = 0; - int physical_address = m_amigo->get_physical_address_debug(offset); - - if ((physical_address & 0x00ff0000)==0x00000000) - { - // DRAM - return m_dram->pointer()[physical_address & 0xffff]; - } - if ((physical_address & 0x00ffc000)==0x00f00000) - { - // Pascal ROM 16K - return m_pascalrom[physical_address & 0x3fff]; - } - if ((physical_address & 0x00ffe000)==0x00ff4000) - { - // Internal DSR, Hexbus DSR, or PEB - if (m_mofetta->hexbus_access_debug()) return m_rom1[(physical_address & 0x1fff) | 0x6000]; - if (m_mofetta->intdsr_access_debug()) return m_rom1[(physical_address & 0x1fff) | 0x4000]; - m_peb->memen_in(ASSERT_LINE); - m_peb->readz(space, physical_address & 0xffff, &value); - m_peb->memen_in(CLEAR_LINE); - return value; - } - if ((physical_address & 0x00ffe000)==0x00ff6000) - { - // Cartridge space lower 8 - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->readz(space, physical_address & 0x1fff, &value); - m_gromport->romgq_line(CLEAR_LINE); - return value; - } - if ((physical_address & 0x00ffe000)==0x00ff8000) - { - // Cartridge space upper 8 - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->readz(space, (physical_address & 0x1fff) | 0x2000, &value); - m_gromport->romgq_line(CLEAR_LINE); - return value; - } - if ((physical_address & 0x00ffe000)==0x00ffa000) - { - // ROM1 lower 8 - return m_rom1[(physical_address & 0x1fff) | 0x0000]; - } - if ((physical_address & 0x00ffe000)==0x00ffc000) - { - // ROM1 upper 8 - return m_rom1[(physical_address & 0x1fff) | 0x2000]; - } - return 0; -} - -WRITE8_MEMBER( mainboard8_device::debugger_write ) -{ - int logical_address = offset; - bool compat_mode = (m_crus_debug==ASSERT_LINE); - - // Check whether the mapper itself is accessed - int mapaddr = compat_mode? 0x8810 : 0xf870; - bool mapper_accessed = ((offset & 0xfff1)==mapaddr); - - if (mapper_accessed) - { - // Allow for loading/saving mapper registers - m_amigo->mapper_access_debug(data); - return; - } - - // SRAM - int sramaddr = compat_mode? 0x8000 : 0xf000; - - if ((offset & 0xf800)==sramaddr) - { - // SRAM access - m_sram->pointer()[logical_address & 0x07ff] = data & 0xff; - return; - } - - // ROM0 (no write access) - if ((offset & 0xe000)==0x0000 && compat_mode) return; - - // Physical space - int physical_address = m_amigo->get_physical_address_debug(offset); - - if ((physical_address & 0x00ff0000)==0x00000000) - { - // DRAM - m_dram->pointer()[physical_address & 0xffff] = data & 0xff; - return; - } - - // Pascal ROM (no write) - if ((physical_address & 0x00ffc000)==0x00f00000) return; - - // Internal DSR, Hexbus DSR, or PEB - if ((physical_address & 0x00ffe000)==0x00ff4000) - { - if (m_mofetta->hexbus_access_debug()) return; - if (m_mofetta->intdsr_access_debug()) return; - m_peb->memen_in(ASSERT_LINE); - m_peb->write(space, physical_address & 0xffff, data & 0xff); - m_peb->memen_in(CLEAR_LINE); - return; - } - if ((physical_address & 0x00ffe000)==0x00ff6000) - { - // Cartridge space lower 8 - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->write(space, physical_address & 0x1fff, data & 0xff); - m_gromport->romgq_line(CLEAR_LINE); - return; - } - if ((physical_address & 0x00ffe000)==0x00ff8000) - { - // Cartridge space upper 8 - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->write(space, (physical_address & 0x1fff) | 0x2000, data & 0xff); - m_gromport->romgq_line(CLEAR_LINE); - return; - } - - // ROM1 not writable - if ((physical_address & 0x00ffe000)==0x00ffa000 || (physical_address & 0x00ffe000)==0x00ffc000) return; -} - -// =============== CRU bus access ================== - -READ8Z_MEMBER(mainboard8_device::crureadz) -{ - m_peb->crureadz(space, offset, value); -} - -/* - CRU handling. Mofetta is the only chip that bothers to handle it, beside the PEB -*/ -WRITE8_MEMBER(mainboard8_device::cruwrite) -{ - m_mofetta->cruwrite(space, offset, data); - m_peb->cruwrite(space, offset, data); -} - -// =============== Memory bus access ================== - -WRITE_LINE_MEMBER( mainboard8_device::dbin_in ) -{ - m_dbin_level = (line_state)state; -} - -SETOFFSET_MEMBER( mainboard8_device::setoffset ) -{ - if (TRACE_ADDRESS) logerror("set %s %04x\n", (m_dbin_level==ASSERT_LINE)? "R" : "W", offset); - - // No data is waiting on the data bus - m_pending_write = false; - - // Memory cycle begins - m_vaquerro->memen_in(ASSERT_LINE); - m_amigo->memen_in(ASSERT_LINE); - - // Save the logical address - m_logical_address = offset; - m_physical_address = 0; - - // In TI's bit order, A14 is the second line from the right side (2^1) - m_A14_set = ((m_logical_address & 2)!=0); // Needed for clock_in - - // Check for match in logical space - m_vaquerro->set_address(space, m_logical_address, m_dbin_level); - - // Select GROMs if addressed - select_groms(); - - // Speech select lines will always be asserted/cleared as soon as the address is available - m_speech->wsq_w((m_vaquerro->spwt_out() == ASSERT_LINE)? false : true); - m_speech->rsq_w((m_vaquerro->sprd_out() == ASSERT_LINE)? false : true); - - // If it is a logical space address, tell the mapper to stay inactive - line_state lasreq = (line_state)m_vaquerro->lascsq_out(); - m_amigo->lascs_in(lasreq); - m_mofetta->lascs_in(lasreq); - - // Need to set the address in any case so that the lines can be cleared - m_amigo->set_address(space, m_logical_address); - - // AMIGO is the one to control the READY line to the CPU - // MOFETTA does not contribute to READY - m_ready(m_amigo->cpury_out()); -} - -WRITE_LINE_MEMBER( mainboard8_device::reset_console ) -{ - m_console_reset(state); -} - -WRITE_LINE_MEMBER( mainboard8_device::hold_cpu ) -{ - m_hold_line(state); -} - -/* - HOLD Acknowledge from the CPU -*/ -WRITE_LINE_MEMBER( mainboard8_device::holda_line ) -{ - m_amigo->holda_in(state); -} - -/* - Clock line from the CPU. Forward to the custom chips. -*/ -WRITE_LINE_MEMBER( mainboard8_device::clock_in ) -{ - if (TRACE_CLOCK) logerror("CLKOUT = %d\n", state); - - // Propagate to Vaquerro; may change GGRDY (trailing edge) and the GROM select lines - m_vaquerro->clock_in((line_state)state); - - // Set the incoming ready line of Amigo (Mapper) before the clock - bool readycomb = ((m_vaquerro->ggrdy_out()==ASSERT_LINE) && m_speech_ready && m_sound_ready && m_pbox_ready); - m_amigo->srdy_in(readycomb? ASSERT_LINE : CLEAR_LINE); - - // This may change the incoming READY lines of Vaquerro - if (state==CLEAR_LINE) select_groms(); - - m_amigo->clock_in((line_state)state); - - // Mofetta only needs the clock to produce the GROM clock - m_mofetta->clock_in(state); - - m_mofetta->skdrcs_in(m_amigo->skdrcs_out()); - - int gromclk = m_mofetta->gromclk_out(); - - if (gromclk != m_gromclk) // when it changed, propagate to the GROMs - { - m_gromclk = gromclk; - - // Get some more performance. We only propagate the clock line to - // those GROMs that are not idle. - // Yields about 25% in bench (hoped for more, but well) - if (!m_sgrom_idle) - { - for (int i=0; i < 3; i++) m_sgrom[i]->gclock_in(gromclk); - m_gromport->gclock_in(gromclk); - m_sgrom_idle = m_sgrom[0]->idle(); - } - - if (!m_tsgrom_idle) - { - for (int i=0; i < 8; i++) m_tsgrom[i]->gclock_in(gromclk); - m_tsgrom_idle = m_tsgrom[0]->idle(); - } - if (!m_p8grom_idle) - { - for (int i=0; i < 8; i++) m_p8grom[i]->gclock_in(gromclk); - m_p8grom_idle = m_p8grom[0]->idle(); - } - - if (!m_p3grom_idle) - { - for (int i=0; i < 3; i++) m_p3grom[i]->gclock_in(gromclk); - m_p3grom_idle = m_p3grom[0]->idle(); - } - } - - // Check video for writing - if (m_pending_write && m_vaquerro->vdpwt_out()==ASSERT_LINE) - { - if (m_A14_set) m_video->register_write(*m_space, 0, m_latched_data); - else m_video->vram_write(*m_space, 0, m_latched_data); - m_pending_write = false; - if (TRACE_MEM) logerror("Write %04x (video) <- %02x\n", m_logical_address, m_latched_data); - cycle_end(); - return; - } - - // Propagate the READY signal - m_ready(m_amigo->cpury_out()); - - // In case we're reading, the CPU will now do the READ operation. - // Otherwise we must do the write operation now which we postponed before. - - if (m_pending_write && (state==CLEAR_LINE)) - { - if (m_amigo->skdrcs_out()==ASSERT_LINE) - { - m_dram->pointer()[m_physical_address & 0xffff] = m_latched_data; - m_pending_write = false; - if (TRACE_MEM) logerror("Write %04x (phys %06x, DRAM) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); - } - - if (m_mofetta->alccs_out()==ASSERT_LINE) - { - m_oso->write(*m_space, m_physical_address>>1, m_latched_data); - m_pending_write = false; - if (TRACE_MEM) logerror("Write %04x (phys %06x, OSO) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); - } - - if (m_mofetta->cmas_out()==ASSERT_LINE) - { - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->write(*m_space, m_physical_address & 0x3fff, m_latched_data); - m_pending_write = false; - if (TRACE_MEM) logerror("Write %04x (phys %06x, cartridge) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); - } - else - { - m_gromport->romgq_line(CLEAR_LINE); - } - - if (m_mofetta->dbc_out()==ASSERT_LINE) - { - m_peb->write(*m_space, m_physical_address, m_latched_data); - m_pending_write = false; - if (TRACE_MEM) logerror("Write %04x (phys %06x, PEB) <- %02x\n", m_logical_address, m_physical_address, m_latched_data); - } - } - - if (m_dbin_level==CLEAR_LINE && !m_pending_write) // Memory cycle ends - cycle_end(); -} - -void mainboard8_device::select_groms() -{ - // Select the GROM libs - // Note that we must also deselect them again, so we have to visit each - // one of them - - int select = m_vaquerro->gromcs_out(); - - // Avoid to be called too often; this would have a bad penalty on emulation performance - // This simple check actually increases bench performance from 120% to 240% - if (select != m_prev_grom) - { - m_prev_grom = select; - int lines = (m_dbin_level==ASSERT_LINE)? GROM_M_LINE : 0; - if (m_A14_set) lines |= GROM_MO_LINE; - - if (select & SGMSEL) m_sgrom_idle = false; - if (select & TSGSEL) m_tsgrom_idle = false; - if (select & P8GSEL) m_p8grom_idle = false; - if (select & P3GSEL) m_p3grom_idle = false; - - for (int i=0; i < 3; i++) - m_sgrom[i]->set_lines(*m_space, lines, select & SGMSEL); - - for (int i=0; i < 8; i++) - m_tsgrom[i]->set_lines(*m_space, lines, select & TSGSEL); - - for (int i=0; i < 8; i++) - m_p8grom[i]->set_lines(*m_space, lines, select & P8GSEL); - - for (int i=0; i < 3; i++) - m_p3grom[i]->set_lines(*m_space, lines, select & P3GSEL); - - // Write to the cartridge port. The GROMs on cartridges are accesses as system GROMs - if (select & SGMSEL) m_gromport->romgq_line(CLEAR_LINE); - m_gromport->set_gromlines(*m_space, lines, select & SGMSEL); - } - - // If we're planning to write to the GROMs, let's do it right now - if (select !=0 && m_pending_write) - { - m_pending_write = false; - switch (select) - { - case SGMSEL: - for (int i=0; i < 3; i++) - { - m_sgrom[i]->write(*m_space, 0, m_latched_data); - } - if (TRACE_MEM) logerror("Write GS <- %02x\n", m_latched_data); - m_gromport->write(*m_space, 0, m_latched_data); - break; - - case TSGSEL: - for (int i=0; i < 8; i++) - { - m_tsgrom[i]->write(*m_space, 0, m_latched_data); - } - if (TRACE_MEM) logerror("Write GT <- %02x\n", m_latched_data); - break; - - case P8GSEL: - for (int i=0; i < 8; i++) - { - m_p8grom[i]->write(*m_space, 0, m_latched_data); - } - if (TRACE_MEM) logerror("Write G8 <- %02x\n", m_latched_data); - break; - - case P3GSEL: - for (int i=0; i < 3; i++) - { - m_p3grom[i]->write(*m_space, 0, m_latched_data); - } - if (TRACE_MEM) logerror("Write G3 <- %02x\n", m_latched_data); - break; - - default: - logerror("Error: Multiple GROM libs selected: SGM=%d TSG=%d P8G=%d P3G=%d\n", (select & SGMSEL)!=0, (select & TSGSEL)!=0, (select & P8GSEL)!=0, (select & P3GSEL)!=0); - break; - } - } -} - -void mainboard8_device::set_paddress(int address) -{ - // Keep this value as the current address - m_physical_address = (m_physical_address << 16) | address; - if (TRACE_DETAIL) logerror("Setting physical address %06x\n", m_physical_address); - - m_mofetta->set_address(*m_space, address, m_dbin_level); - m_peb->setaddress_dbin(*m_space, address, m_dbin_level); -} - -WRITE_LINE_MEMBER( mainboard8_device::msast_in ) -{ - if (TRACE_DETAIL) logerror("msast = %d\n", state); - - // Start physical space cycle on the trailing edge - if (state==CLEAR_LINE) - { - m_mofetta->pmemen_in(ASSERT_LINE); - m_peb->memen_in(ASSERT_LINE); - } - m_mofetta->msast_in(state); - m_peb->msast_in(state); -} - - -READ8_MEMBER( mainboard8_device::read ) -{ - uint8_t value = 0; - const char* what; - - if (machine().side_effect_disabled()) - { - return debugger_read(space, offset); - } - - // ================================================= - // Logical space - // ================================================= - if (m_amigo->mapper_accessed()) - { - value = m_amigo->read(space, 0); - what = "mapper"; - goto readdone; - } - - if (m_amigo->sramcs_out()==ASSERT_LINE) - { - value = m_sram->pointer()[m_logical_address & 0x07ff]; - what = "SRAM"; - goto readdone; - } - - if (m_vaquerro->lascsq_out()==ASSERT_LINE) - { - // VDP access - if (m_vaquerro->vdprd_out()==ASSERT_LINE) - { - value = m_A14_set? m_video->register_read(space, 0) : m_video->vram_read(space, 0); - what = "video"; - goto readdone; - } - - // System ROM0 - if (m_vaquerro->sromcs_out()==ASSERT_LINE) - { - value = m_rom0[m_logical_address & 0x1fff]; - what = "ROM0"; - goto readdone; - } - - // Speech - if (m_vaquerro->sprd_out()==ASSERT_LINE) - { - value = m_speech->status_r(space, 0) & 0xff; - what = "speech"; - goto readdone; - } - - // GROMs - switch (m_vaquerro->gromcs_out()) - { - case SGMSEL: - m_sgrom_idle = false; - for (int i=0; i < 3; i++) - { - m_sgrom[i]->readz(space, 0, &value); - } - m_gromport->readz(space, 0, &value); - if (TRACE_GROM && !m_A14_set) logerror("GS>%04x\n", m_sgrom[0]->debug_get_address()-1); - what = "system GROM"; - goto readdone; - - case TSGSEL: - m_tsgrom_idle = false; - for (int i=0; i < 8; i++) - { - m_tsgrom[i]->readz(space, 0, &value); - } - if (TRACE_GROM && !m_A14_set) logerror("GT>%04x\n", m_tsgrom[0]->debug_get_address()-1); - what = "TTS GROM"; - goto readdone; - - case P8GSEL: - m_p8grom_idle = false; - for (int i=0; i < 8; i++) - { - m_p8grom[i]->readz(space, 0, &value); - } - if (TRACE_GROM && !m_A14_set) logerror("G8>%04x\n", m_p8grom[0]->debug_get_address()-1); - what = "P8 GROM"; - goto readdone; - - case P3GSEL: - m_p3grom_idle = false; - for (int i=0; i < 3; i++) - { - m_p3grom[i]->readz(space, 0, &value); - } - if (TRACE_GROM && !m_A14_set) logerror("G3>%04x\n", m_p3grom[0]->debug_get_address()-1); - what = "P3 GROM"; - goto readdone; - default: - break; - } - - // These messages appear in fact every time that a GPL command writes - // an immediate value to a write-only address (like 9400) because the - // GPL interpreter always tries to load the value from the provided memory address first - - logerror("Read %04x (unmapped) ignored\n", m_logical_address); - - // Memory cycle ends - cycle_end(); - return 0; - } - else - { - // ================================================= - // Physical space - // ================================================= - if (m_amigo->skdrcs_out()==ASSERT_LINE) - { - value = m_dram->pointer()[m_physical_address & 0xffff]; - what = "DRAM"; - goto readdonephys; - } - - if (m_mofetta->rom1cs_out()==ASSERT_LINE) - { - int address = (m_physical_address & 0x1fff); - if (m_mofetta->rom1am_out()==ASSERT_LINE) address |= 0x4000; - if (m_mofetta->rom1al_out()==ASSERT_LINE) address |= 0x2000; - value = m_rom1[address]; - - if (TRACE_MEM) logerror("Read %04x (ROM1@%04x) -> %02x\n", m_logical_address, address, value); - cycle_end(); - return value; - } - - if (m_mofetta->alccs_out()==ASSERT_LINE) - { - value = m_oso->read(*m_space, m_physical_address>>1); - what = "OSO"; - goto readdonephys; - } - - if (m_mofetta->prcs_out()==ASSERT_LINE) - { - value = m_pascalrom[m_physical_address & 0x3fff]; - what = "PASCAL"; - goto readdonephys; - } - - if (m_mofetta->cmas_out()==ASSERT_LINE) - { - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->readz(*m_space, m_physical_address & 0x3fff, &value); - what = "Cartridge"; - goto readdonephys; - } - - if (m_mofetta->dbc_out()==ASSERT_LINE) - { - m_peb->readz(*m_space, m_physical_address & 0xffff, &value); - what = "PEB"; - goto readdonephys; - } - - if (TRACE_PUNMAP) logerror("Read %04x (phys %06x, unmapped) ignored\n", m_logical_address, m_physical_address); - - // Memory cycle ends - cycle_end(); - return 0; - } - - -readdone: - if (TRACE_MEM) logerror("Read %04x (%s) -> %02x\n", m_logical_address, what, value); - cycle_end(); - return value; - -readdonephys: - if (TRACE_MEM) logerror("Read %04x (phys %06x, %s) -> %02x\n", m_logical_address, m_physical_address, what, value); - cycle_end(); - return value; -} - -void mainboard8_device::cycle_end() -{ - // Memory cycle ends - m_vaquerro->memen_in(CLEAR_LINE); - m_amigo->memen_in(CLEAR_LINE); - m_mofetta->pmemen_in(CLEAR_LINE); - m_peb->memen_in(CLEAR_LINE); -} - -/* - When writing, the emulation relies on a push mechanism; the write - operation follows the setaddress operation immediately. - - If the READY line is pulled down due to the mapping process, we must - store the data bus value until the physical address is available. -*/ -WRITE8_MEMBER( mainboard8_device::write ) -{ - m_latched_data = data; - m_pending_write = true; - - if (machine().side_effect_disabled()) - { - return debugger_write(space, offset, data); - } - - // Some logical space devices can be written immediately - // GROMs and video must wait to be selected - if (m_amigo->mapper_accessed()) - { - if (TRACE_MEM) logerror("Write %04x (mapper) <- %02x\n", m_logical_address, data); - m_amigo->write(space, 0, data); - m_pending_write = false; - } - - // Sound ... we have to put it before SRAM because in compatibility mode the - // sound address lies within the SRAM area - if (m_vaquerro->sccs_out()==ASSERT_LINE) - { - if (TRACE_MEM) logerror("Write %04x (sound) <- %02x\n", m_logical_address, data); - m_sound->write(space, 0, data); // Sound chip will lower READY after this access - m_pending_write = false; - } - else - { - // SRAM - if (m_amigo->sramcs_out()==ASSERT_LINE) - { - if (TRACE_MEM) logerror("Write %04x (SRAM) <- %02x\n", m_logical_address, data); - m_sram->pointer()[m_logical_address & 0x07ff] = data; - m_pending_write = false; - } - } - - // Speech - if (m_vaquerro->spwt_out()==ASSERT_LINE) - { - if (TRACE_MEM) logerror("Write %04x (speech) <- %02x\n", m_logical_address, data); - m_speech->data_w(space, 0, data); - m_pending_write = false; - } - - if (!m_pending_write) - cycle_end(); - - // The remaining physical devices will respond as soon as the physical address is completely set -} - -/* - Set 99/4A compatibility mode (CRUS=1) -*/ -WRITE_LINE_MEMBER( mainboard8_device::crus_in ) -{ - if (TRACE_CRU) logerror("%s CRUS\n", (state==1)? "Assert" : "Clear"); - m_vaquerro->crus_in(state); - m_amigo->crus_in(state); - m_crus_debug = (line_state)state; -} - -/* - Set mapper /PTGEN line ("Pascal and Text-to-speech GROMs enable"). - Note that PTGEN is negative logic. -*/ -WRITE_LINE_MEMBER( mainboard8_device::ptgen_in ) -{ - if (TRACE_CRU) logerror("%s PTGEN*\n", (state==0)? "Assert" : "Clear"); - m_vaquerro->crusgl_in((state==0)? ASSERT_LINE : CLEAR_LINE); -} - - -/* - READY lines, to be combined -*/ -WRITE_LINE_MEMBER( mainboard8_device::system_grom_ready ) -{ - m_vaquerro->sgmry(state); -} - -WRITE_LINE_MEMBER( mainboard8_device::ptts_grom_ready ) -{ - m_vaquerro->tsgry(state); -} - -WRITE_LINE_MEMBER( mainboard8_device::p8_grom_ready ) -{ - m_vaquerro->p8gry(state); -} - -WRITE_LINE_MEMBER( mainboard8_device::p3_grom_ready ) -{ - m_vaquerro->p3gry(state); -} - -WRITE_LINE_MEMBER( mainboard8_device::sound_ready ) -{ - m_sound_ready = state; -} - -WRITE_LINE_MEMBER( mainboard8_device::speech_ready ) -{ - // The TMS5200 implementation uses true/false, not ASSERT/CLEAR semantics - m_speech_ready = (state==false)? ASSERT_LINE : CLEAR_LINE; -} - -WRITE_LINE_MEMBER( mainboard8_device::pbox_ready ) -{ - m_pbox_ready = state; -} - -WRITE_LINE_MEMBER( mainboard8_device::ggrdy_in ) -{ - m_amigo->srdy_in((state==ASSERT_LINE && m_speech_ready && m_sound_ready && m_pbox_ready)? ASSERT_LINE : CLEAR_LINE); -} - -static const char *const glib0[] = { SYSGROM0_TAG, SYSGROM1_TAG, SYSGROM2_TAG }; -static const char *const glib1[] = { GLIB10_TAG, GLIB11_TAG, GLIB12_TAG, GLIB13_TAG, GLIB14_TAG, GLIB15_TAG, GLIB16_TAG, GLIB17_TAG }; -static const char *const glib2[] = { GLIB20_TAG, GLIB21_TAG, GLIB22_TAG, GLIB23_TAG, GLIB24_TAG, GLIB25_TAG, GLIB26_TAG, GLIB27_TAG }; -static const char *const glib3[] = { GLIB30_TAG, GLIB31_TAG, GLIB32_TAG }; - -void mainboard8_device::device_start() -{ - logerror("Starting main board\n"); - // Lines going to the main driver class, then to the CPU - m_ready.resolve_safe(); // READY - m_console_reset.resolve_safe(); // RESET - m_hold_line.resolve_safe(); // HOLD - - // Setting up the links to the GROMs - for (int i=0; i < 8; i++) - { - if (i < 3) - { - m_sgrom[i] = downcast(machine().device(glib0[i])); - m_p3grom[i] = downcast(machine().device(glib3[i])); - } - m_tsgrom[i] = downcast(machine().device(glib1[i])); - m_p8grom[i] = downcast(machine().device(glib2[i])); - } - - m_rom0 = machine().root_device().memregion(ROM0_REG)->base(); - m_rom1 = machine().root_device().memregion(ROM1_REG)->base(); - m_pascalrom = machine().root_device().memregion(PASCAL_REG)->base(); - - // Register state variables - save_item(NAME(m_A14_set)); - save_item(NAME(m_logical_address)); - save_item(NAME(m_physical_address)); - save_item(NAME(m_pending_write)); - save_item(NAME(m_latched_data)); - save_item(NAME(m_gromclk)); - save_item(NAME(m_prev_grom)); - save_item(NAME(m_speech_ready)); - save_item(NAME(m_sound_ready)); - save_item(NAME(m_pbox_ready)); - save_item(NAME(m_dbin_level)); - save_item(NAME(m_last_ready)); - save_item(NAME(m_sgrom_idle)); - save_item(NAME(m_tsgrom_idle)); - save_item(NAME(m_p8grom_idle)); - save_item(NAME(m_p3grom_idle)); -} - -void mainboard8_device::device_reset() -{ - logerror("Resetting main board\n"); - m_last_ready = CLEAR_LINE; - m_speech_ready = true; - m_sound_ready = true; - m_pbox_ready = true; - m_pending_write = false; - m_prev_grom = 0; - m_A14_set = false; - // Configure RAM and AMIGO - m_amigo->connect_sram(m_sram->pointer()); - - // Get the pointer to the address space; we need it outside of the - // usual memory functions. - cpu_device* cpu = downcast(machine().device("maincpu")); - m_space = &cpu->space(AS_PROGRAM); -} - -MACHINE_CONFIG_START( ti998_mainboard ) - MCFG_DEVICE_ADD(VAQUERRO_TAG, TI99_VAQUERRO, 0) - MCFG_DEVICE_ADD(MOFETTA_TAG, TI99_MOFETTA, 0) - MCFG_DEVICE_ADD(AMIGO_TAG, TI99_AMIGO, 0) - MCFG_DEVICE_ADD(OSO_TAG, TI99_OSO, 0) -MACHINE_CONFIG_END - -machine_config_constructor mainboard8_device::device_mconfig_additions() const -{ - return MACHINE_CONFIG_NAME( ti998_mainboard ); -} - -DEFINE_DEVICE_TYPE(TI99_MAINBOARD8, mainboard8_device, "ti998_mainboard", "TI-99/8 Mainboard") - - -/*************************************************************************** - ===== VAQUERRO: Logical Address Space decoder ===== - - Logical address space (LAS) - =========================== - The LAS is the address space as seen by the TMS 9995 CPU. It is 64 KiB large. - The LAS can be configured in two ways: - - the native (99/8) mode - - and the compatibility mode (99/4A) - - Both modes are selected by CRU bit 20 on base 0000 (named "CRUS"). - - The console starts up in compatibility mode. - - The compatibility mode organizes the LAS in a similar way as the TI-99/4A. - This means that machine language programs should run with no or only minor - changes. In particular, game cartridges work without problems. - - The native mode rearranges the address space and puts memory-mapped devices - to other positions. - - TI-99/4A compatibility mode (CRUS=1) - ------------------------------------ - 0000-1fff: 2 KiB ROM0 - 2000-7fff: Free area - 8000-87ff: 2 KiB SRAM - 8000-81ff: mapper files (8 files with 16*4 bytes each) - 8200-82ff: Free RAM - 8300-83ff: Scratch-pad RAM as in the 99/4A - 8400-840f: Sound chip - 8800-880f: VDP read port (data, status) - 8810-881f: Mapper access port - 8820-8bff: Free area - 8c00-8c0f: VDP write port (data, address) - 8c10-8fff: Free area - 9000-900f: Speech synthesizer read (on-board) - 9010-93ff: Free area - 9400-940f: Speech synthesizer write (on-board) - 9410-97ff: Free area - 9800-980f: System GROM read (data, address) - 9810-9bff: Free area - 9c00-9c0f: System GROM write (data, address) - 9c10-fffb: Free area - fffc-ffff: NMI vector - - TI-99/8 native mode (CRUS=0) - ---------------------------- - 0000-efff: Free area - f000-f7ff: 2 KiB SRAM - f000-f1ff: mapper files (8 files with 16*4 bytes each) - f200-f7ff: Free RAM - f800-f80f: Sound chip - f810-f81f: VDP read (data, status) and write (data, address) - f820-f82f: Speech synthesizer read/write - f830-f83f: System GROM read/write - f840-f86f: Free area - f870-f87f: Mapper access port - f880-fffb: Free area - fffc-ffff: NMI vector - - Note that ROM0 is not visible in the native mode. - - If CRU bit 21 (PTGEN*) is set to 0, Pascal GROMs appear in the LAS in either - mode. It is highly recommended to use native mode when turning on these - GROMs, because the area where they appear may be occupied by a program in - 99/4A mode. - - Pascal and Text-to-speech GROM enabled (PTGEN*=0) - ------------------------------------------------- - f840-f84f: Text-to-speech GROM read/write - f850-f85f: P-Code library #1 GROM read/write - f860-f86f: P-Code library #2 GROM read/write - - -***************************************************************************/ - -vaquerro_device::vaquerro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, TI99_VAQUERRO, tag, owner, clock), - m_crus(ASSERT_LINE), - m_crugl(ASSERT_LINE), - m_ggrdy(ASSERT_LINE) -{ -} - -SETADDRESS_DBIN_MEMBER( vaquerro_device::set_address ) -{ - // Do the decoding - // state = dbin, offset = address - - bool reading = (state==ASSERT_LINE); - bool sgfap = false; - bool tsgfap = false; - bool p8gfap = false; - bool p3gfap = false; - bool vfap = false; - - m_a14 = ((offset & 2)!=0)? ASSERT_LINE : CLEAR_LINE; // Needed for clock_in - - m_dbin_level = (line_state)state; - - int offbase = (offset & 0xfff1); - - // =================== TI (compatibility) mode ====================== - if (m_crus == ASSERT_LINE) - { - if (TRACE_DETAIL) logerror("Compatibility mode\n"); - - // SPRD (Speech read) - m_sprd = ((offbase==0x9000) && reading); - - // SPWT (Speech write) - m_spwt = ((offbase==0x9400) && !reading); - - // Sound - m_sccs = ((offbase==0x8400)&& !reading); - - // ROM0 - m_sromcs = (((offset & 0xe000)==0x0000) && reading); - - // Video select - vfap = ((offbase==0x8800) && reading) || ((offbase==0x8c00) && !reading); - - // System GROM - sgfap = ((offbase==0x9800) && reading) || ((offbase==0x9c00) && !reading); - } - // ====================== Native mode ====================== - else - { - if (TRACE_DETAIL) logerror("Native mode\n"); - - // SPRD (Speech read) - m_sprd = ((offbase==0xf820) && reading); - - // SPWT (Speech write) - m_spwt = ((offbase==0xf820) && !reading); - - // Sound - m_sccs = ((offbase==0xf800) && !reading); - - // Video - vfap = (offbase==0xf810); - - // System GROM (read and write) - sgfap = (offbase==0xf830); - } - - // These lines are not decoded for compatibility or native mode, only - // the line CRUGL determines whether they become visible. - tsgfap = (offbase==0xf840) && m_crugl; - p8gfap = (offbase==0xf850) && m_crugl; - p3gfap = (offbase==0xf860) && m_crugl; - - // The LASREQ line says whether Vaquerro does the job, or whether it is Mofetta's turn. - m_grom_or_video = sgfap || tsgfap || p8gfap || p3gfap || vfap ; - - m_lasreq = (m_sprd || m_spwt || m_sccs || m_sromcs || m_grom_or_video); - - if (TRACE_DETAIL) logerror("sgfap=%d tsgfap=%d p8gfap=%d p3gfap=%d vfap=%d\n", sgfap, tsgfap, p8gfap, p3gfap, vfap); - - // Pass the selection to the wait state generators - // and pick up the current select line states - m_sgmws.select_in(sgfap); - m_tsgws.select_in(tsgfap); - m_p8gws.select_in(p8gfap); - m_p3gws.select_in(p3gfap); - m_vidws.select_in(vfap); - - m_gromsel = m_sgmws.select_out() | m_tsgws.select_out() | m_p8gws.select_out() | m_p3gws.select_out(); - - m_vdprd = (reading && (m_vidws.select_out()!=0)); - m_vdpwt = (!reading && (m_vidws.select_out()!=0)); - - if (m_grom_or_video) - { - // We don't see the current selection now; only with next clock pulse. - m_mainboard->ggrdy_in(m_sry); - if (TRACE_READY) logerror("GGRDY = %d\n", m_sry); - } -} - -WRITE_LINE_MEMBER( vaquerro_device::crusgl_in ) -{ - m_crugl = (state==ASSERT_LINE); -} - -WRITE_LINE_MEMBER( vaquerro_device::crus_in ) -{ - m_crus = (line_state)state; -} - -WRITE_LINE_MEMBER( vaquerro_device::memen_in ) -{ - m_memen = (state==ASSERT_LINE); -} - -/* - Called by Mofetta -*/ -READ_LINE_MEMBER( vaquerro_device::lascsq_out ) -{ - return (m_lasreq && m_memen)? ASSERT_LINE : CLEAR_LINE; -} - -/* - Incoming ready lines from the GROM library -*/ -WRITE_LINE_MEMBER( vaquerro_device::sgmry ) -{ - if (TRACE_READY) logerror("Incoming SGMRY = %d\n", state); - m_sgmws.ready_in((line_state)state); -} - -WRITE_LINE_MEMBER( vaquerro_device::tsgry ) -{ - if (TRACE_READY) logerror("Incoming TSGRY = %d\n", state); - m_tsgws.ready_in((line_state)state); -} - -WRITE_LINE_MEMBER( vaquerro_device::p8gry ) -{ - if (TRACE_READY) logerror("Incoming 8GRY = %d\n", state); - m_p8gws.ready_in((line_state)state); -} - -WRITE_LINE_MEMBER( vaquerro_device::p3gry ) -{ - if (TRACE_READY) logerror("Incoming P3GRY = %d\n", state); - m_p3gws.ready_in((line_state)state); -} - -/* - Outgoing READY -*/ -READ_LINE_MEMBER( vaquerro_device::ggrdy_out ) -{ - if (TRACE_READY) logerror("GGRDY out = %d\n", m_ggrdy); - return m_ggrdy; -} - -/* - Select lines -*/ - -// ========================= - -int vaquerro_device::gromcs_out() -{ - return m_gromsel; -} - -// ========================= - -READ_LINE_MEMBER( vaquerro_device::vdprd_out ) -{ - return (m_vdprd && m_memen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( vaquerro_device::vdpwt_out ) -{ - return (m_vdpwt && m_memen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( vaquerro_device::sprd_out ) -{ - return (m_sprd && m_memen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( vaquerro_device::spwt_out ) -{ - return (m_spwt && m_memen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( vaquerro_device::sromcs_out ) -{ - return (m_sromcs && m_memen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( vaquerro_device::sccs_out ) -{ - return (m_sccs && m_memen)? ASSERT_LINE : CLEAR_LINE; -} - -/* - Incoming clock signal. - - The Vaquerro has a Wait State generation logic circuit for the video - processor and all 4 GROM libraries. Each one has its separate generator. - The GROMs get a 16 cycle wait period after their access, while the video - processors gets an 8 cycle wait period. If during that period another - access occurs, the system READY line will be cleared, triggering wait - states in the CPU. -*/ -WRITE_LINE_MEMBER( vaquerro_device::clock_in ) -{ - line_state level = (line_state)state; - - // Propagate to the wait state generators (note that we need both clock levels) - m_sgmws.clock_in(level); - m_tsgws.clock_in(level); - m_p8gws.clock_in(level); - m_p3gws.clock_in(level); - m_vidws.clock_in(level); - - // Collect the selections - // Each one has its own indication, defined at init time - m_gromsel = m_sgmws.select_out() | m_tsgws.select_out() | m_p8gws.select_out() | m_p3gws.select_out(); - - bool reading = (m_dbin_level==ASSERT_LINE); - - m_vdprd = (reading && (m_vidws.select_out()!=0)); - m_vdpwt = (!reading && (m_vidws.select_out()!=0)); - - // Get the READY levels from the GROMs - if (level==CLEAR_LINE) - { - m_sry = m_sgmws.ready_out() || m_tsgws.ready_out() || m_p8gws.ready_out() || m_p3gws.ready_out() || m_vidws.ready_out(); - if (TRACE_WS) logerror("ready_out = (%d, %d, %d, %d, %d)\n", m_sgmws.ready_out(), m_tsgws.ready_out(), m_p8gws.ready_out(), m_p3gws.ready_out(),m_vidws.ready_out()); - } - - // If the output gate is closed, propagate ASSERT_LINE (pulled up) - m_ggrdy = (!m_grom_or_video || m_sry)? ASSERT_LINE : CLEAR_LINE; -} - - -void vaquerro_device::device_start() -{ - logerror("Starting\n"); - m_mainboard = downcast(owner()); - m_sgmws.init(SGMSEL); - m_tsgws.init(TSGSEL); - m_p8gws.init(P8GSEL); - m_p3gws.init(P3GSEL); - m_vidws.init(VIDSEL); - - save_item(NAME(m_memen)); - save_item(NAME(m_video_wait)); - save_item(NAME(m_crus)); - save_item(NAME(m_crugl)); - save_item(NAME(m_lasreq)); - save_item(NAME(m_grom_or_video)); - save_item(NAME(m_spwt)); - save_item(NAME(m_sccs)); - save_item(NAME(m_sromcs)); - save_item(NAME(m_sprd)); - save_item(NAME(m_vdprd)); - save_item(NAME(m_vdpwt)); - save_item(NAME(m_gromsel)); - save_item(NAME(m_ggrdy)); - save_item(NAME(m_sry)); - save_item(NAME(m_a14)); - save_item(NAME(m_dbin_level)); - - // FIXME: In rare occasions, the saved state is invalid and restoring - // may crash the emulated 99/8 (e.g. with invalid opcodes) - // Saving the wait state logic does not affect the operation, as it seems, - // so we leave it out. -} - -void vaquerro_device::device_reset() -{ - m_ggrdy = ASSERT_LINE; - m_vdpwt = m_vdprd = CLEAR_LINE; - m_gromsel = 0; - m_sgmws.treset_in(ASSERT_LINE); - m_tsgws.treset_in(ASSERT_LINE); - m_p8gws.treset_in(ASSERT_LINE); - m_p3gws.treset_in(ASSERT_LINE); - m_vidws.treset_in(ASSERT_LINE); -} - -/* - Wait state generation logic inside Vaquerro - - Analysis of the logic diagram of the Vaquerro delivers the following - behavior (for the first GROM library; similar behavior applies for the - other libraries). Note that the CLKOUT line is inverted. - - 1. When the GROMs are unselected by address (SGFAP), the SRY line is Z - (System READY). The GROM ready line (SGMRY) has no effect. - 2. When SGFAP is asserted while the internal counter is off, the - READY line changes from Z to Low and the GROM select line (SGCS) is - asserted (both immediately, before the next tick edge). SGMRY has no effect. - The circuit state is constant during further clock ticks. - 3. After being selected, when SGMRY is asserted (GROM is ready), SRY - changes to High on the next trailing edge. This will allow the - CPU to complete the GROM access on the next cycle, - and the address bus will change, typically deselecting the GROMs. - Until this deselection, the circuit state remains constant - (SGCS asserted, READY=H). - 4. When the GROMs are deselected, SRY changes to Z, and SGCS is cleared - (immediately). A counter is started at 0 that is incremented on each clock - tick (leading edge). - 5. When SGFAP is asserted while the counter is less that 15, SRY changes - to Low immediately. SGCS remains cleared, so the GROMs are not selected. - While SGFAP stays cleared, the counter completes its way to 15, - then 0, and turns off. - 6. When the counter reaches 15, it returns to 0 on the next tick - (leading). On the following trailing edge, the GROM select line is - asserted, while the READY line remains Low. - 7. Continue at 3. -*/ -void vaquerro_device::waitstate_generator::select_in(bool addressed) -{ - m_addressed = addressed; -} - -int vaquerro_device::waitstate_generator::select_out() -{ - return (!m_counting && m_addressed)? m_selvalue : 0; -} - -/* - Should be low by default. -*/ -line_state vaquerro_device::waitstate_generator::ready_out() -{ - return (m_ready && !m_counting && m_generate)? ASSERT_LINE : CLEAR_LINE; -} - -bool vaquerro_device::waitstate_generator::is_counting() -{ - return m_counting; -} - -bool vaquerro_device::waitstate_generator::is_generating() -{ - return m_generate; -} - -bool vaquerro_device::waitstate_generator::is_ready() -{ - return m_ready; -} - -/* - READY in. This may only show an effect with the next trailing edge of CLKOUT. -*/ -void vaquerro_device::grom_waitstate_generator::ready_in(line_state ready) -{ - m_ready = (ready==ASSERT_LINE); -} - -void vaquerro_device::grom_waitstate_generator::clock_in(line_state clkout) -{ - if (clkout == ASSERT_LINE) - { - if (m_counting) m_counter++; - } - else - { - if (m_counting && m_counter==16) - { - m_counter = 0; - m_counting = false; - } - else - { - if (!m_addressed && m_generate) m_counting = true; - m_generate = ((m_addressed || m_counting) && (m_counter != 15)); - } - } -} - -void vaquerro_device::waitstate_generator::treset_in(line_state reset) -{ - if (reset==ASSERT_LINE) - { - m_counter = 0; - m_generate = m_counting = m_addressed = false; - } -} - -void vaquerro_device::video_waitstate_generator::clock_in(line_state clkout) -{ - if (clkout == ASSERT_LINE) - { - if (m_counting) m_counter++; - } - else - { - if (m_counting && m_counter==7) - { - m_counter = 0; - m_counting = false; - } - else - { - if (!m_addressed && m_generate) m_counting = true; - m_generate = ((m_addressed || m_counting) && (m_counter != 6)); - } - } -} - -DEFINE_DEVICE_TYPE(TI99_VAQUERRO, vaquerro_device, "ti998_vaquerro", "TI-99/8 Logical Address Space Decoder") - -/*************************************************************************** - ===== MOFETTA: Physical Address Space decoder ===== - - Physical address space (PAS) - ============================ - The PAS is 24 bits wide and accessed via the custom mapper chip nicknamed - "Amigo". The mapper exchanges map definitions with SRAM (see LAS). That - means, a map can be prepared in SRAM, and for activating it, the mapper - is accessed on its port, telling it to load or save a map. - - 000000-00ffff: 64 KiB console DRAM - 010000-efffff: undefined - - f00000-f03fff: PASCAL support ROM (not mentioned in [1]) - - f04000-feffff: undefined - ff0000 : unmapped (code for mapper) - ff0001-ff3fff: undefined - ff4000-ff5fff: DSR ROM in Peripheral Box, Hexbus DSR (CRU 1700) or additional ROM (CRU 2700) - ff6000-ff9fff: Cartridge ROM space - ffa000-ffdfff: 16 KiB ROM1 - ffe000-ffe00f: Interrupt level sense - ffe010-ffffff: undefined - - -***************************************************************************/ - -enum -{ - UNDEF=0, - DRAM, - PASCAL, - INTERNAL -}; - -mofetta_device::mofetta_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, TI99_MOFETTA, tag, owner, clock), - m_gotfirstword(false) -{ -} - -SETADDRESS_DBIN_MEMBER( mofetta_device::set_address ) -{ - if (!m_gotfirstword) - { - // Store the first word and wait for clearing of MSAST - if (TRACE_MOFETTA) logerror("Got the upper word of the address: %04x\n", offset); - m_address_latch = offset; - } - else - { - // Second part - now decode the address - if (TRACE_MOFETTA) logerror("Got the lower word of the address: %04x\n", offset); - - bool acs, tcs, rcs, acsx; - bool reading = (state==ASSERT_LINE); - int offbase = (offset & 0xe000); - - // PASCAL ROM select (16K) - m_prcs = (m_prefix == 0xf0) && ((offset & 0xc000) == 0x0000); - - // Hexbus select - acs = (m_prefix == 0xff) && (offbase == 0x4000) && m_alcpg; - - // Internal DSR select (ff4000-ff5fff @ CRU>2700) - tcs = (m_prefix == 0xff) && (offbase == 0x4000) && m_txspg; - - // Hexbus select (ff4000-ff5fef @ CRU>1700), excluding OSO - acsx = acs && ((offset & 0x1ff0)!=0x1ff0); - - // Upper 16K of ROM1 - m_rom1am = !((offbase == 0xa000) || (offbase == 0xc000)); - - // ROM select - rcs = (m_prefix == 0xff) && reading && !m_rom1am; - - // ROM1 select (containing 16K ROM, 8K TTS, 8K ACS) - m_rom1cs = tcs || rcs || acsx; - - // Accessing OSO (ff5ff0 @ CRU>1700) - m_alccs = acs && ((offset & 0x1ff0)==0x1ff0); - - // Second half of ROM or ACS - m_rom1al = reading && (m_prefix == 0xff) && ((offbase == 0xc000) || acs); - - // Cartridge port (ff6000-ff9fff) - m_cmas = (m_prefix == 0xff) && ((offbase == 0x6000) || (offbase == 0x8000)); - - m_gotfirstword = false; - } -} - -/* - Mofetta delivers the GROMCLK. In the 99/4A, this clock is produced by the VDP. - Apart from that, Mofetta does not need the CLKOUT. -*/ -WRITE_LINE_MEMBER( mofetta_device::clock_in ) -{ - if (state == CLEAR_LINE) // TODO: Correct edge? - { - m_gromclock_count++; - if (m_gromclock_count >=3) - { - m_gromclk_up = !m_gromclk_up; - m_gromclock_count = 0; - } - } -} - -READ_LINE_MEMBER( mofetta_device::alccs_out ) -{ - return (m_alccs && m_pmemen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( mofetta_device::gromclk_out ) -{ - return m_gromclk_up? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( mofetta_device::rom1cs_out ) -{ - return (m_rom1cs && m_pmemen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( mofetta_device::rom1am_out ) -{ - return (m_rom1am && m_pmemen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( mofetta_device::rom1al_out ) -{ - return (m_rom1al && m_pmemen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( mofetta_device::prcs_out ) -{ - return (m_prcs && m_pmemen)? ASSERT_LINE : CLEAR_LINE; -} - -READ_LINE_MEMBER( mofetta_device::cmas_out ) -{ - return (m_cmas && m_pmemen)? ASSERT_LINE : CLEAR_LINE; -} - -/* - Asserted when a PEB access occurs -*/ -READ_LINE_MEMBER( mofetta_device::dbc_out ) -{ - return (m_lasreq || m_cmas || m_rom1cs || m_skdrcs || !m_pmemen)? CLEAR_LINE : ASSERT_LINE; -} - -/* - Debugger support -*/ -bool mofetta_device::hexbus_access_debug() -{ - return m_alcpg; -} - -bool mofetta_device::intdsr_access_debug() -{ - return m_txspg; -} - -WRITE8_MEMBER(mofetta_device::cruwrite) -{ - if ((offset & 0xff00)==0x2700) - { - if ((offset & 0x0002)!=0) - { - // SWRST (Software reset) - // Value seems to be irrelevant - if (TRACE_CRU) logerror("Doing a software reset by SBO 2702\n"); - m_mainboard->reset_console(ASSERT_LINE); - m_mainboard->reset_console(CLEAR_LINE); - } - else - { - m_txspg = (data!=0); // CRU>2700 - if (TRACE_CRU) logerror("Turning %s CRU>2700\n", m_txspg? "on" : "off"); - } - } - else - { - if ((offset & 0xff00)==0x1700) - { - m_alcpg = (data!=0); // CRU>1700 - if (TRACE_CRU) logerror("Turning %s CRU>1700\n", m_alcpg? "on" : "off"); - } - } -} - -/* - Setting or clearing the MSAST line. -*/ -WRITE_LINE_MEMBER( mofetta_device::msast_in ) -{ - if (state == ASSERT_LINE) - { - if (m_msast == CLEAR_LINE) // Leading edge - { - m_gotfirstword = true; // Process first word - // We now have the first part, containing the flags and the upper byte. - m_prefix = m_address_latch & 0xff; - } - } - // TODO: Evaluate the first three bits - m_msast = (line_state)state; -} - -WRITE_LINE_MEMBER( mofetta_device::pmemen_in ) -{ - m_pmemen = (state==ASSERT_LINE); -} - -WRITE_LINE_MEMBER( mofetta_device::lascs_in ) -{ - m_lasreq = (state==ASSERT_LINE); -} - -WRITE_LINE_MEMBER( mofetta_device::skdrcs_in ) -{ - m_skdrcs = (state==ASSERT_LINE); -} - -void mofetta_device::device_start() -{ - logerror("Starting\n"); - m_mainboard = downcast(owner()); - - save_item(NAME(m_pmemen)); - save_item(NAME(m_lasreq)); - save_item(NAME(m_skdrcs)); - save_item(NAME(m_gromclk_up)); - save_item(NAME(m_gotfirstword)); - save_item(NAME(m_address_latch)); - save_item(NAME(m_prefix)); - save_item(NAME(m_alcpg)); - save_item(NAME(m_txspg)); - save_item(NAME(m_rom1cs)); - save_item(NAME(m_rom1am)); - save_item(NAME(m_rom1al)); - save_item(NAME(m_alccs)); - save_item(NAME(m_prcs)); - save_item(NAME(m_cmas)); - save_item(NAME(m_gromclock_count)); - save_item(NAME(m_msast)); -} - -void mofetta_device::device_reset() -{ - m_gotfirstword = false; - m_alcpg = false; - m_txspg = false; - m_prefix = 0; -} - - -DEFINE_DEVICE_TYPE(TI99_MOFETTA, mofetta_device, "ti998_mofetta", "TI-99/8 Physical Address Space Decoder") - -/*************************************************************************** - - ============================== - Mapper (codename "Amigo") - ============================== - - Unfortunately, we do not have logic diagrams for Amigo, so we have to - guess how it is actually working. - - Initial setting of mapper (as defined in the power-up routine, TI-99/4A mode) - - 0 00ff0000 -> Unmapped; logical address 0000...0fff = ROM0 - 1 00ff0000 -> Unmapped; logical address 1000...1fff = ROM0 - 2 00000800 -> DRAM; 2000 = 000800, 2fff = 0017ff - 3 00001800 -> DRAM; 3000 = 001800, 3fff = 0027ff - 4 00ff4000 -> DSR space (internal / ioport) - 5 00ff5000 -> DSR space (internal / ioport) - 6 00ff6000 -> Cartridge space (6000..6fff) - 7 00ff7000 -> Cartridge space (7000..7fff) - 8 00ff0000 -> Unmapped; device ports (VDP) and SRAM - 9 00ff0000 -> Unmapped; device ports (Speech, GROM) - A 00002800 -> DRAM; a000 = 002800, afff = 0037ff - B 00003800 -> DRAM; b000 = 003800, bfff = 0047ff - C 00004800 -> DRAM; c000 = 004800, cfff = 0057ff - D 00005800 -> DRAM; d000 = 005800, dfff = 0067ff - E 00006800 -> DRAM; e000 = 006800, efff = 0077ff - F 00007800 -> DRAM; f000 = 007800, ffff = 0087ff - - Format of map table entry - - +--+---+---+---+---+---+---+---+ +-----------+ +----------+ +---------+ - | W| X | R | 0 | 0 | 0 | 0 | 0 | | Upper (8) | | High (8) | | Low (8) | - +--+---+---+---+---+---+---+---+ +-----------+ +----------+ +---------+ - - W: Write protection if set to 1 - X: Execute protection if set to 1 - R: Read protection if set to 1 - - When a protection violation occurs, the tms9901 INT1* pin is pulled low - (active). The pin remains low until the mapper status register is read. - - Address handling - ---------------- - Physical address is (Upper * 2^16) + (High * 2^8) + Low - - The mapper calculates the actual physical address by looking up the - table entry from the first four bits of the logical address and then - *adding* the remaining 12 bits of the logical address on the map value. - - The value 0xff0000 is used to indicate a non-mapped area. - - Mapper control register - ----------------------- - The mapper control register is used to initiate a map load/save operation. - - +---+---+---+---+---+---+---+---+ - | 0 | 0 | 0 | 0 | Map File | RW| - +---+---+---+---+---+---+---+---+ - - The map file is a number from 0-7 indicating the set of map values for the - operation, which means the location in SRAM where the next 64 values are - loaded from or stored into. - - RW = 1: load from SRAM into mapper - RW = 0: store from mapper into SRAM - - When read, the mapper register returns the violation flags: - +---+---+---+---+---+---+---+---+ - | W | X | R | 0 | 0 | 0 | 0 | 0 | - +---+---+---+---+---+---+---+---+ - -***************************************************************************/ - -amigo_device::amigo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, TI99_AMIGO, tag, owner, clock), - m_logical_space(true), - m_crus(ASSERT_LINE) -{ -} - -enum -{ - IDLE = 0, - CREATE_PADDR, - ADDR_MSW, - ADDR_LSW, - SRAMLOAD, - SRAMSAVE -}; - -/* - Debugger support -*/ -int amigo_device::get_physical_address_debug(offs_t offset) -{ - return ((offset & 0x0fff) + m_base_register[(offset >> 12) & 0x000f]) & 0x00ffffff; -} - -/* - Incoming READY line (SRDY) -*/ -WRITE_LINE_MEMBER( amigo_device::srdy_in ) -{ - if (TRACE_READY) logerror("Incoming SRDY = %d\n", state); - m_srdy = (line_state)state; - - // If the access is going to logical space, pass through the READY line - if (m_logical_space) - { - if (TRACE_CPURY) logerror("Setting CPURY = %d (SRDY)\n", m_ready_out); - m_ready_out = m_srdy; - } -} - -WRITE_LINE_MEMBER( amigo_device::memen_in ) -{ - m_memen = (state==ASSERT_LINE); -} - -/* - Polled from the mainboard -*/ -READ_LINE_MEMBER( amigo_device::cpury_out ) -{ - return m_ready_out; -} - -/* - Polled from the mainboard -*/ -READ_LINE_MEMBER( amigo_device::sramcs_out ) -{ - return m_sram_accessed && m_memen? ASSERT_LINE : CLEAR_LINE; -} - -/* - SKDRCS line (maybe "Sixty-four Kilobyte DRam Chip Select"). We assume that - Amigo asserts the select line not before the whole address was written. - This is actually more than needed because we only have 64K DRAM (which - would only need a 16 bit address), and Amigo itself selects it. -*/ -READ_LINE_MEMBER( amigo_device::skdrcs_out ) -{ - return m_dram_accessed && (m_amstate == IDLE) && m_memen? ASSERT_LINE : CLEAR_LINE; -} - -/* - Incoming CRUS line. Needed to set the mapper config addresses. -*/ -WRITE_LINE_MEMBER( amigo_device::crus_in ) -{ - m_crus = (line_state)state; -} - -/* - Incoming LASCS line. -*/ -WRITE_LINE_MEMBER( amigo_device::lascs_in ) -{ - m_logical_space = (state==ASSERT_LINE); -} - -/* - The logical address bus has been set. The Amigo chip now has to map this - address to a physical address. There are three phases (3 clock ticks): - 1. Sample the logical address lines, determine the map register - (first four bits), create the physical address by adding the remaining - 12 bits and the map register contents - 2. Set the physical address bus with the first 16 bits of the physical - address. Assert the MSAST line. - 3. Set the physical address bus with the second 16 bits of the physical - address. Clear the MSAST line. Forward any incoming READY=0 to the CPU. -*/ -SETOFFSET_MEMBER( amigo_device::set_address ) -{ - // Check whether the mapper itself is accessed - int mapaddr = (m_crus==ASSERT_LINE)? 0x8810 : 0xf870; - m_mapper_accessed = ((offset & 0xfff1)==mapaddr); - - // or SRAM - int sramaddr = (m_crus==ASSERT_LINE)? 0x8000 : 0xf000; - m_sram_accessed = ((offset & 0xf800)==sramaddr); - - m_logical_space |= (m_mapper_accessed || m_sram_accessed); - - // Is the address not in the logical address space? - if (!m_logical_space) - { - if (TRACE_AMIGO) logerror("Amigo decoding; %04x is a physical address.\n", offset); - // Build the physical address - // The first three bits are the protection bits (Write, Execute, Read) - // Theoretically, the addition of the logical address could mess up those - // first three bits, but the physical address is only 24 bits wide, so we - // have a space of 5 zeros between the protection bits and the address. - // We should just clear those five bits after the addition. - - m_physical_address = ((offset & 0x0fff) + m_base_register[(offset >> 12) & 0x000f]) & 0x00ffffff; - - // TODO: Process flags - - // Is it DRAM? - m_dram_accessed = (m_physical_address & 0x00ff0000)==0; - - // This takes one clock pulse. - m_amstate = CREATE_PADDR; - - // Pull down READY - m_ready_out = CLEAR_LINE; - - if (TRACE_CPURY) logerror("Setting CPURY = %d (PAS)\n", m_ready_out); - } - else - { - // This was a logical space access. Pass through READY. - m_dram_accessed = false; - m_amstate = IDLE; - m_ready_out = m_srdy; - if (TRACE_CPURY) logerror("Setting CPURY = %d (LAS)\n", m_ready_out); - } -} - -/* - Read the mapper status bits -*/ -READ8_MEMBER( amigo_device::read ) -{ - // Read the protection status bits and reset them - uint8_t value = m_protflag; - m_protflag = 0; - return value; -} - -/* - Configure the mapper. This is the only reason to write to the AMIGO. -*/ -WRITE8_MEMBER( amigo_device::write ) -{ - // Load or save map file - if ((data & 0xf0)==0x00) - { - // Need to HOLD the CPU - m_amstate = ((data & 1)==1)? SRAMLOAD : SRAMSAVE; - m_sram_address = (data & 0x0e) << 5; - m_hold_acknowledged = false; - m_basereg = 0; - m_mapvalue = 0; - m_mainboard->hold_cpu(ASSERT_LINE); - } - else logerror("Invalid value written to Amigo: %02x\n", data); -} - -WRITE_LINE_MEMBER( amigo_device::clock_in ) -{ - if (state==CLEAR_LINE) - { - switch (m_amstate) - { - case IDLE: - break; - case CREATE_PADDR: - // Address has been created - m_amstate = ADDR_MSW; - break; - case ADDR_MSW: - // Transmit the first word (without the protection bits) - m_mainboard->set_paddress((m_physical_address >> 16) & 0x00ff); - m_amstate = ADDR_LSW; - break; - case ADDR_LSW: - m_mainboard->msast_in(ASSERT_LINE); // Pulse MSAST - m_mainboard->msast_in(CLEAR_LINE); - m_mainboard->set_paddress(m_physical_address & 0xffff); - m_amstate = IDLE; - m_ready_out = m_srdy; // Propagate incoming READY - break; - - case SRAMLOAD: - if (m_hold_acknowledged) mapper_load(); - break; - - case SRAMSAVE: - if (m_hold_acknowledged) mapper_save(); - break; - - default: - logerror("Invalid state in mapper: %d\n", m_amstate); - } - } -} - -void amigo_device::mapper_load() -{ - m_mapvalue = (m_mapvalue << 8) | m_sram[m_sram_address++]; - - if ((m_sram_address & 0x03)==0) - { - if (TRACE_MAP) logerror("Loaded basereg %02d = %08x\n", m_basereg, m_mapvalue); - m_base_register[m_basereg++] = m_mapvalue; - } - if (m_basereg == 16) - { - m_amstate = IDLE; - m_mainboard->hold_cpu(CLEAR_LINE); - } -} - -void amigo_device::mapper_save() -{ - if ((m_sram_address & 0x03)==0) - { - if (m_basereg == 16) - { - m_amstate = IDLE; - m_mainboard->hold_cpu(CLEAR_LINE); - return; - } - else - { - m_mapvalue = m_base_register[m_basereg]; - if (TRACE_MAP) logerror("Saving basereg %02d = %08x\n", m_basereg, m_mapvalue); - m_basereg++; - } - } - - m_sram[m_sram_address++] = (m_mapvalue >> 24) & 0xff; - m_mapvalue = m_mapvalue << 8; -} - -/* - Debugger support -*/ -void amigo_device::mapper_access_debug(int data) -{ - if ((data & 0xf0)==0x00) - { - int address = (data & 0x0e) << 5; - - if ((data & 1)==1) - { - for (int i=0; i < 64; i++) - { - // Load from SRAM - m_base_register[i/4] = (m_base_register[i/4] << 8) | (m_sram[address++] & 0xff); - } - } - else - { - for (int i=0; i < 16; i++) - { - // Save to SRAM - m_sram[address++] = (m_base_register[i] >> 24) & 0xff; - m_sram[address++] = (m_base_register[i] >> 16) & 0xff; - m_sram[address++] = (m_base_register[i] >> 8) & 0xff; - m_sram[address++] = m_base_register[i] & 0xff; - } - } - } -} - -WRITE_LINE_MEMBER( amigo_device::holda_in ) -{ - if (TRACE_MAP) logerror("HOLD acknowledged = %d\n", state); - m_hold_acknowledged = (state==ASSERT_LINE); -} - -void amigo_device::device_start() -{ - logerror("Starting\n"); - m_mainboard = downcast(owner()); - - save_item(NAME(m_memen)); - save_pointer(NAME(m_base_register),16); - save_item(NAME(m_logical_space)); - save_item(NAME(m_physical_address)); - save_item(NAME(m_srdy)); - save_item(NAME(m_ready_out)); - save_item(NAME(m_crus)); - save_item(NAME(m_amstate)); - save_item(NAME(m_protflag)); - save_item(NAME(m_sram_accessed)); - save_item(NAME(m_dram_accessed)); - save_item(NAME(m_mapper_accessed)); - save_item(NAME(m_hold_acknowledged)); - save_item(NAME(m_sram_address)); - save_item(NAME(m_basereg)); - save_item(NAME(m_mapvalue)); -} - -void amigo_device::device_reset() -{ - m_logical_space = true; -} - -DEFINE_DEVICE_TYPE(TI99_AMIGO, amigo_device, "ti998_amigo", "TI-99/8 Address space mapper") - -/*************************************************************************** - - ===== OSO: Hexbus interface ===== - - The Hexbus is a 4-bit peripheral bus with master/slave coordination. Bytes - are written over the bus in two passes. Hexbus was the designated standard - peripheral bus for TI computers before TI left the home computer market. - - Existing devices are floppy drive, RS232 serial adapter, and - a "Wafertape" drive (kind of tape streamer) - - Registers: Read Write Bits of register - ---------------------------------------------------------------------------- - Data : 5FF8 - ADB3 ADB2 ADB1 ADB0 ADB3 ADB2 ADB1 ADB0 - Status : 5FFA - HSKWT HSKRD BAVIAS BAVAIS SBAV WBUSY RBUSY SHSK - Control : 5FFC 5FFA WIEN RIEN BAVIAEN BAVAIEN BAVC WEN REN CR7 - Xmit : 5FFE 5FF8 XDR0 XDR1 XDR2 XDR3 XDR4 XDR5 XDR6 XDR7 - - ADBx = Hexbus data bit X - HSKWT = Set when a byte has been sent over the bus and HSK has been asserted - HSKRD = Set when a byte has been received - BAVIAS = set when the BAV* signal (bus available) transits to active state - BAVAIS = set when the BAV* signal transits to inactive state (=1) - SBAV = set when BAV* = 0 (active) - WBUSY = set when a write action is in progress (two transfers @ 4 bits) - Reset when HSKWT is set - RBUSY = set when a read action is in progress (two transfers @ 4 bits) - Reset when HSKRD is set - SHSK = set when HSK* is active (0) - - WIEN = Enable interrupt for write completion - RIEN = Enable interrupt for read completion - BAVIAEN = BAVIA enable (slave mode) - BAVAIEN = BAVAI enable (slave mode) - BAVC = set BAV* line (0=active) - WEN = set write enable (byte is written from xmit reg) - REN = set read enable (latch HSK and read byte into data reg) - CR7 = future extension - XDRx = transmit register bit - - Hexbus connector (console) - +---+---+---+---+ - | 4 | 3 | 2 | 1 | 4 = L; 3 = BAV*; 2 = ADB1; 1 = ADB0 - +---+---+---+---+ - | 8 | 7 | 6 | 5 | 8 = ADB3; 7 = ADB2; 6 = nc; 5 = HSK* - +---+---+---+---+ - - TODO: This is just a preliminary implementation to satisfy the operating - system. When completed we can hopefully emulate a Hexbus floppy and - use it in Extended Basic II which refuses to work with the PEB cards. - The Hexbus should then be designed as a slot device. - -****************************************************************************/ - -/* Status register bits */ -enum -{ - HSKWT = 0x80, - HSKRD = 0x40, - BAVIAS = 0x20, - BAVAIS = 0x10, - SBAV = 0x08, - WBUSY = 0x04, - RBUSY = 0x02, - SHSK = 0x01 -}; - -oso_device::oso_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, TI99_OSO, tag, owner, clock), m_data(0), m_status(0), m_control(0), m_xmit(0) -{ -} - -READ8_MEMBER( oso_device::read ) -{ - int value = 0; - offset &= 0x03; - switch (offset) - { - case 0: - // read 5FF8: read data register - if (TRACE_OSO) logerror("Read data register = %02x\n", value); - value = m_data; - break; - case 1: - // read 5FFA: read status register - value = m_status; - if (TRACE_OSO) logerror("Read status %02x\n", value); - break; - case 2: - // read 5FFC: read control register - value = m_control; - if (TRACE_OSO) logerror("Read control register = %02x\n", value); - break; - case 3: - // read 5FFE: read transmit register - value = m_xmit; - if (TRACE_OSO) logerror("Read transmit register = %02x\n", value); - break; - } - return value; -} - -WRITE8_MEMBER( oso_device::write ) -{ - offset &= 0x03; - switch (offset) - { - case 0: - // write 5FF8: write transmit register - if (TRACE_OSO) logerror("Write transmit register %02x\n", data); - m_xmit = data; - // We set the status register directly in order to prevent lock-ups - // until we have a complete Hexbus implementation - m_status |= HSKWT; - break; - case 1: - // write 5FFA: write control register - if (TRACE_OSO) logerror("Write control register %02x\n", data); - m_control = data; - break; - default: - // write 5FFC, 5FFE: undefined - if (TRACE_OSO) logerror("Invalid write on %04x: %02x\n", (offset<<1) | 0x5ff0, data); - break; - } -} - -void oso_device::device_start() -{ - logerror("Starting\n"); - m_status = m_xmit = m_control = m_data = 0; - save_item(NAME(m_data)); - save_item(NAME(m_status)); - save_item(NAME(m_control)); - save_item(NAME(m_xmit)); -} - -DEFINE_DEVICE_TYPE(TI99_OSO, oso_device, "ti998_oso", "TI-99/8 Hexbus interface") diff --git a/src/devices/bus/ti99x/998board.h b/src/devices/bus/ti99x/998board.h deleted file mode 100644 index 250e8c061f4..00000000000 --- a/src/devices/bus/ti99x/998board.h +++ /dev/null @@ -1,559 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/**************************************************************************** - - TI-99/8 main board logic - - This component implements the address decoder and mapper logic from the - TI-99/8 console. - - See 998board.c for documentation - - Michael Zapf - -*****************************************************************************/ - -#ifndef MAME_BUS_TI99X_998BOARD_H -#define MAME_BUS_TI99X_998BOARD_H - -#pragma once - -#include "ti99defs.h" -#include "gromport.h" - -#include "bus/ti99/peb/peribox.h" -#include "machine/ram.h" -#include "machine/tmc0430.h" -#include "sound/sn76496.h" -#include "sound/tms5220.h" -#include "video/tms9928a.h" - -DECLARE_DEVICE_TYPE(TI99_MAINBOARD8, mainboard8_device) - -#define VAQUERRO_TAG "vaquerro" -#define MOFETTA_TAG "mofetta" -#define AMIGO_TAG "amigo" -#define OSO_TAG "oso" - -DECLARE_DEVICE_TYPE(TI99_VAQUERRO, vaquerro_device) -DECLARE_DEVICE_TYPE(TI99_MOFETTA, mofetta_device) -DECLARE_DEVICE_TYPE(TI99_AMIGO, amigo_device) -DECLARE_DEVICE_TYPE(TI99_OSO, oso_device) - - -/* - Custom chip: Vaquerro -*/ -class vaquerro_device : public device_t -{ -public: - vaquerro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - void device_start() override; - void device_reset() override; - - line_state ready(); - void treset(); - - DECLARE_READ8_MEMBER( read ); - DECLARE_SETADDRESS_DBIN_MEMBER( set_address ); - - DECLARE_READ_LINE_MEMBER( sprd_out ); - DECLARE_READ_LINE_MEMBER( spwt_out ); - DECLARE_READ_LINE_MEMBER( sccs_out ); - DECLARE_READ_LINE_MEMBER( sromcs_out ); - - // Collective select line query - int gromcs_out(); - - DECLARE_READ_LINE_MEMBER( vdprd_out ); - DECLARE_READ_LINE_MEMBER( vdpwt_out ); - DECLARE_READ_LINE_MEMBER( lascsq_out ); - DECLARE_READ_LINE_MEMBER( ggrdy_out ); - DECLARE_WRITE_LINE_MEMBER( hold_cpu ); - - DECLARE_WRITE_LINE_MEMBER( crus_in ); - DECLARE_WRITE_LINE_MEMBER( crusgl_in ); - DECLARE_WRITE_LINE_MEMBER( clock_in ); - DECLARE_WRITE_LINE_MEMBER( memen_in ); - - DECLARE_WRITE_LINE_MEMBER( sgmry ); - DECLARE_WRITE_LINE_MEMBER( tsgry ); - DECLARE_WRITE_LINE_MEMBER( p8gry ); - DECLARE_WRITE_LINE_MEMBER( p3gry ); - -private: - /* - Wait state generator (part of Vaquerro) - */ - class waitstate_generator - { - public: - waitstate_generator() : - m_counting(false), - m_generate(false), - m_counter(0), - m_addressed(true), - m_ready(true) - { - } - - virtual ~waitstate_generator() { } - void select_in(bool addressed); - virtual void ready_in(line_state ready) = 0; - virtual void clock_in(line_state clkout) = 0; - void treset_in(line_state reset); - - int select_out(); - void init(int select_value) { m_selvalue = select_value; } - - line_state ready_out(); - - bool is_counting(); - bool is_generating(); - bool is_ready(); - - protected: - // Two flipflops - bool m_counting; - bool m_generate; - // Counter - int m_counter; - - // Select value (indicates selected line) - int m_selvalue; - - // Line state flags - bool m_addressed; - bool m_ready; - }; - - class grom_waitstate_generator : public waitstate_generator - { - public: - void ready_in(line_state ready) override; - void clock_in(line_state clkout) override; - }; - - class video_waitstate_generator : public waitstate_generator - { - public: - void ready_in(line_state ready) override { } - void clock_in(line_state clkout) override; - }; - - // Memory cycle state - bool m_memen; - - // Waiting for video - bool m_video_wait; - - // State of the CRUS line - int m_crus; - - // Are the GROM libraries turned on? - bool m_crugl; - - // Do we have a logical address space match? - bool m_lasreq = false; - - // Keep the decoding result (opens the SRY gate) - bool m_grom_or_video = false; - - // Select lines - bool m_spwt; - bool m_sccs; - bool m_sromcs; - bool m_sprd; - bool m_vdprd; - bool m_vdpwt; - - // Collective GROM select state - int m_gromsel; - - // Outgoing READY - int m_ggrdy; - - // Outgoing READY latch (common flipflop driving SRY) - bool m_sry; - - // Holds the A14 address line state. We need this for the clock_in method. - int m_a14; - - // Keeps the recent DBIN level - int m_dbin_level; - - // Wait state logic components - grom_waitstate_generator m_sgmws, m_tsgws, m_p8gws, m_p3gws; - video_waitstate_generator m_vidws; - - // Pointer to mainboard - mainboard8_device* m_mainboard; -}; - -/* - Custom chip: Mofetta -*/ -class mofetta_device : public device_t -{ -public: - mofetta_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - void device_start() override; - void device_reset() override; - - DECLARE_WRITE8_MEMBER( cruwrite ); - DECLARE_SETADDRESS_DBIN_MEMBER( set_address ); - - // Debugger support - bool hexbus_access_debug(); - bool intdsr_access_debug(); - - DECLARE_WRITE_LINE_MEMBER( clock_in ); - DECLARE_WRITE_LINE_MEMBER( msast_in ); - DECLARE_WRITE_LINE_MEMBER( lascs_in ); - DECLARE_WRITE_LINE_MEMBER( pmemen_in ); - DECLARE_WRITE_LINE_MEMBER( skdrcs_in ); - - DECLARE_READ8_MEMBER( rom1cs_out ); - DECLARE_READ_LINE_MEMBER( gromclk_out ); - - DECLARE_READ_LINE_MEMBER( alccs_out ); - DECLARE_READ_LINE_MEMBER( prcs_out ); - DECLARE_READ_LINE_MEMBER( cmas_out ); - DECLARE_READ_LINE_MEMBER( dbc_out ); - - DECLARE_READ_LINE_MEMBER( rom1cs_out ); - DECLARE_READ_LINE_MEMBER( rom1am_out ); - DECLARE_READ_LINE_MEMBER( rom1al_out ); - -private: - // Memory cycle state - bool m_pmemen; - - // Logical access - bool m_lasreq; - - // DRAM access - bool m_skdrcs; - - // Indicates the UP level of the GROMCLK - bool m_gromclk_up; - - // Have we got the upper word of the address? - bool m_gotfirstword; - - // Address latch - int m_address_latch; - - // Most significant byte of the 24-bit address - int m_prefix; - - // CRU select of the 1700 device - bool m_alcpg; - - // CRU select of the 2700 device - bool m_txspg; - - // ROM1 select lines - bool m_rom1cs; - bool m_rom1am; - bool m_rom1al; - - // OSO select - bool m_alccs; - - // Pascal ROM select line - bool m_prcs; - - // Cartridge port select line - bool m_cmas; - - // GROM clock count (as frequency divider) - int m_gromclock_count; - - // Remember last msast state for edge detection - int m_msast; - - // Pointer to mainboard - mainboard8_device* m_mainboard; -}; - -/* - Custom chip: Amigo -*/ -class amigo_device : public device_t -{ -public: - amigo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - void device_start() override; - void device_reset() override; - - DECLARE_READ8_MEMBER( read ); - DECLARE_WRITE8_MEMBER( write ); - DECLARE_SETOFFSET_MEMBER( set_address ); - - // Debugger support - int get_physical_address_debug(offs_t offset); - void mapper_access_debug(int data); - - DECLARE_WRITE_LINE_MEMBER( srdy_in ); - DECLARE_WRITE_LINE_MEMBER( clock_in ); - DECLARE_WRITE_LINE_MEMBER( crus_in ); - DECLARE_WRITE_LINE_MEMBER( lascs_in ); - DECLARE_WRITE_LINE_MEMBER( memen_in ); - - DECLARE_WRITE_LINE_MEMBER( holda_in ); - - DECLARE_READ_LINE_MEMBER( cpury_out ); - DECLARE_READ_LINE_MEMBER( sramcs_out ); - DECLARE_READ_LINE_MEMBER( skdrcs_out ); - - void connect_sram(uint8_t* sram) { m_sram = sram; } - bool mapper_accessed() { return m_mapper_accessed; } - -private: - // Memory cycle state - bool m_memen; - - // DMA methods for loading/saving maps - void mapper_load(); - void mapper_save(); - - // Address mapper registers. Each offset is selected by the first 4 bits - // of the logical address. - uint32_t m_base_register[16]; - - // Indicates a logical space access - bool m_logical_space; - - // Physical address - uint32_t m_physical_address; - - // Pointer to SRAM where AMIGO needs to upload/download its map values - uint8_t* m_sram; - - // Pointer to mainboard - mainboard8_device* m_mainboard; - - // Keep the system ready state - int m_srdy; - - // Outgoing READY level - int m_ready_out; - - // Keep the CRUS setting - int m_crus; - - // State of the address creation - int m_amstate; - - // Protection flags - int m_protflag; - - // Accessing SRAM - bool m_sram_accessed; - - // Accessing DRAM - bool m_dram_accessed; - - // Accessing the mapper - bool m_mapper_accessed; - - // HOLDA flag - bool m_hold_acknowledged; - - // Address in SRAM during DMA - uint32_t m_sram_address; - - // Number of the currently loaded/save base register - int m_basereg; - - // Latched value for mapper DMA transfer - uint32_t m_mapvalue; -}; - -/* - Custom chip: OSO -*/ -class oso_device : public device_t -{ -public: - oso_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - DECLARE_READ8_MEMBER( read ); - DECLARE_WRITE8_MEMBER( write ); - void device_start() override; - -private: - uint8_t m_data; - uint8_t m_status; - uint8_t m_control; - uint8_t m_xmit; -}; - -class mainboard8_device : public device_t -{ -public: - mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // Memory space - DECLARE_READ8_MEMBER( read ); - DECLARE_WRITE8_MEMBER( write ); - DECLARE_SETOFFSET_MEMBER( setoffset ); - - // Memory space for debugger access - DECLARE_READ8_MEMBER( debugger_read ); - DECLARE_WRITE8_MEMBER( debugger_write ); - - // I/O space - DECLARE_READ8Z_MEMBER( crureadz ); - DECLARE_WRITE8_MEMBER( cruwrite ); - - // Control lines - DECLARE_WRITE_LINE_MEMBER( clock_in ); - DECLARE_WRITE_LINE_MEMBER( dbin_in ); - DECLARE_WRITE_LINE_MEMBER( msast_in ); - DECLARE_WRITE_LINE_MEMBER( crus_in ); - DECLARE_WRITE_LINE_MEMBER( ptgen_in ); - DECLARE_WRITE_LINE_MEMBER( reset_console ); - DECLARE_WRITE_LINE_MEMBER( hold_cpu ); - DECLARE_WRITE_LINE_MEMBER( ggrdy_in ); - - DECLARE_WRITE_LINE_MEMBER( holda_line ); - - template static devcb_base &set_ready_wr_callback(device_t &device, _Object object) - { - return downcast(device).m_ready.set_callback(object); - } - - template static devcb_base &set_reset_wr_callback(device_t &device, _Object object) - { - return downcast(device).m_console_reset.set_callback(object); - } - - template static devcb_base &set_hold_wr_callback(device_t &device, _Object object) - { - return downcast(device).m_hold_line.set_callback(object); - } - - void set_paddress(int address); - - // Ready lines from GROMs - DECLARE_WRITE_LINE_MEMBER( system_grom_ready ); - DECLARE_WRITE_LINE_MEMBER( ptts_grom_ready ); - DECLARE_WRITE_LINE_MEMBER( p8_grom_ready ); - DECLARE_WRITE_LINE_MEMBER( p3_grom_ready ); - DECLARE_WRITE_LINE_MEMBER( sound_ready ); - DECLARE_WRITE_LINE_MEMBER( speech_ready ); - DECLARE_WRITE_LINE_MEMBER( pbox_ready ); - - // Emulation - // void set_gromport(gromport_device* dev) { m_gromport = dev; } - -protected: - void device_start() override; - void device_reset() override; - machine_config_constructor device_mconfig_additions() const override; - -private: - // Holds the state of the A14 line - bool m_A14_set; - - // Propagates the end of the memory cycle - void cycle_end(); - - // Original logical address. - int m_logical_address; - - // Mapped physical address. - int m_physical_address; - - // Hold the address space value so that we can use it in other methods. - address_space* m_space; - - // Indicates that a byte is waiting on the data bus (see m_latched_data) - bool m_pending_write; - - // Hold the value of the data bus. In a real machine, the data bus continues - // to show that value, but in this emulation we have a push mechanism. - uint8_t m_latched_data; - - // Hold the level of the GROMCLK line - int m_gromclk; - - // Selecting GROM libraries - void select_groms(); - - // Previous select state - int m_prev_grom; - - // Ready states - bool m_speech_ready; - bool m_sound_ready; - bool m_pbox_ready; - - // Keeps the recent DBIN level - int m_dbin_level; - - // Ready line to the CPU - devcb_write_line m_ready; - - // Reset line to the main system - devcb_write_line m_console_reset; - - // Hold line to the main system - devcb_write_line m_hold_line; - - // Custom chips - required_device m_vaquerro; - required_device m_mofetta; - required_device m_amigo; - required_device m_oso; - - // More devices - required_device m_video; - required_device m_sound; - required_device m_speech; - required_device m_gromport; - required_device m_peb; - required_device m_sram; - required_device m_dram; - - // Debugging - int m_last_ready; - line_state m_crus_debug; - - // System GROM library - tmc0430_device* m_sgrom[3]; - - // Text-to-speech GROM library - tmc0430_device* m_tsgrom[8]; - - // Pascal 8 GROM library - tmc0430_device* m_p8grom[8]; - - // Pascal 3 GROM library - tmc0430_device* m_p3grom[3]; - - // Idle flags for GROMs - bool m_sgrom_idle; - bool m_tsgrom_idle; - bool m_p8grom_idle; - bool m_p3grom_idle; - - // ROM area of the system. - uint8_t* m_rom0; - uint8_t* m_rom1; - uint8_t* m_pascalrom; -}; - -#define MCFG_MAINBOARD8_READY_CALLBACK(_write) \ - devcb = &mainboard8_device::set_ready_wr_callback(*device, DEVCB_##_write); - -#define MCFG_MAINBOARD8_RESET_CALLBACK(_write) \ - devcb = &mainboard8_device::set_reset_wr_callback(*device, DEVCB_##_write); - -#define MCFG_MAINBOARD8_HOLD_CALLBACK(_write) \ - devcb = &mainboard8_device::set_hold_wr_callback(*device, DEVCB_##_write); - - -#endif diff --git a/src/devices/bus/ti99x/datamux.cpp b/src/devices/bus/ti99x/datamux.cpp deleted file mode 100644 index 2ccf7055410..00000000000 --- a/src/devices/bus/ti99x/datamux.cpp +++ /dev/null @@ -1,640 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/*************************************************************************** - - TI-99/4(A) databus multiplexer circuit - - The DMUX is used to convert the 16-bit databus of the TMS9900 into - an 8-bit databus. The processor writes a 16 bit word which is split - by this circuit into two bytes that are sent subsequently over the 8-bit bus. - In the opposite direction, one 16-bit read request from the CPU is - translated into two 8-bit read requests (odd address / even address) from - this datamux. Its 8-bit latch (LS373) holds the first (odd address) byte, - while the datamux puts the CPU on hold, gets the second byte, - and routes that second byte to the D0-D7 lines, while the latch now puts - the first byte on D8-D15. Since we get two memory accesses each time, - there are twice as many wait states than for a direct 16-bit access - (order LSB, MSB). - - In addition, since the TMS 9900 also supports byte operations, all write - operations are automatically preceded by a read operation, so this adds even - more delays. - - Within the TI-99/4(A) console, only the internal ROM and the small internal - RAM ("scratch pad RAM") are directly connected to the 16-bit bus. All other - devices (video, audio, speech, GROM, and the complete P-Box system are - connected to the datamux. - - The TMS9995 which is used in the Geneve has an internal multiplex, and - the byte order is reversed: MSB, LSB - - ROM = 4K * 16 bit (8 KiB) system ROM (kind of BIOS, plus the GPL interpreter) - RAM = 128 * 16 bit (256 byte) system RAM ("scratch pad") - - Many users (me too) used to solder a 16K * 16 bit (32 KiB) SRAM circuit into - the console, before the datamux, decoded to 0x2000-0x3fff and 0xa000-0xffff. - (This expansion was also called 0-waitstate, since it could be accessed - with the full databus width, and the datamux did not create waitstates.) - - +---+ +-------+ - | |===##========##== D0-D7 ==========##===============|TMS9918| Video - | | || || || +-------+ - | T | +-----+ +-----+ LS245 +----+ - | M | | ROM | | RAM | +----+ - | S | +-----+ +-----+ || | : - | |---||-||-----||-||----------------||-|---------------------: - | 9 | || || A0 - A14 || | A0 : Sound - | 9 |---||--------||-------------------||-|----------+ -A15 : GROM - | 0 | || || LS373 +-+ || | +----A15-+----------: Cartridges - | 0 | || || ##========|<|===## | | : Speech - | | || || || +-+ +-+ || | | : Expansion - | |===## D8-D15 ##===##==|>|=====|===##=|=|=========== D0-D7 =: cards - +---+ +-+ | | | : - ^ LS244| | | | - | | +--+---+-++ - | +--------| DMUX |---------------<--: READY - +--- READY -------------------------+------+ - - Databus width - :------------- 16 bit ---------------|---------- 8 bit -----: - - A0=MSB; A15=LSB - D0=MSB; D15=LSB - - We integrate the 16 bit memory expansion in this datamux component - (pretending that the memory expansion was soldered on top of the datamux) - - January 2012: Rewritten as class - -***************************************************************************/ - -#include "emu.h" -#include "datamux.h" - -/* - Constructor -*/ -ti99_datamux_device::ti99_datamux_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, TI99_DATAMUX, tag, owner, clock), - m_video(*owner, VDP_TAG), - m_sound(*owner, TISOUNDCHIP_TAG), - m_peb(*owner, PERIBOX_TAG), - m_gromport(*owner, GROMPORT_TAG), - m_ram16b(*owner, EXPRAM_TAG), - m_padram(*owner, PADRAM_TAG), - m_spacep(nullptr), - m_ready(*this), - m_addr_buf(0), - m_dbin(CLEAR_LINE), - m_muxready(CLEAR_LINE), - m_sysready(CLEAR_LINE), - m_latch(0), - m_waitcount(0), - m_use32k(false), - m_base32k(0), - m_console_groms_present(false), - m_grom_idle(true) -{ -} - -#define TRACE_READY 0 -#define TRACE_ACCESS 0 -#define TRACE_ADDRESS 0 -#define TRACE_WAITCOUNT 0 -#define TRACE_SETUP 0 - -/*************************************************************************** - DEVICE ACCESSOR FUNCTIONS -***************************************************************************/ - -void ti99_datamux_device::read_all(address_space& space, uint16_t addr, uint8_t *value) -{ - // Valid access - bool validaccess = ((addr & 0x0400)==0); - - if (validaccess) - { - // GROM access - if ((addr & 0xf801)==0x9800) - { - if (m_console_groms_present) - { - for (int i=0; i < 3; i++) - { - m_grom[i]->readz(space, addr, value); - } - } - // GROMport (GROMs) - m_gromport->readz(space, addr, value); - m_grom_idle = false; - } - - // Video - if ((addr & 0xf801)==0x8800) - { - // Forward to VDP unless we have an EVPC - if (m_video != nullptr) *value = m_video->read(space, addr>>1); // A14 determines data or register read - } - } - - // GROMport (ROMs) - if ((addr & 0xe000)==0x6000) m_gromport->readz(space, addr, value); - - // PEB gets all accesses - m_peb->readz(space, addr, value); - m_peb->memen_in(CLEAR_LINE); -} - -void ti99_datamux_device::write_all(address_space& space, uint16_t addr, uint8_t value) -{ - // GROM access - if ((addr & 0xf801)==0x9800) - { - if (m_console_groms_present) - { - for (int i=0; i < 3; i++) - m_grom[i]->write(space, addr, value); - } - // GROMport - m_gromport->write(space, addr, value); - m_grom_idle = false; - } - - // Cartridge port and sound - if ((addr & 0xe000)==0x6000) m_gromport->write(space, addr, 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) - { - // Forward to VDP unless we have an EVPC - if (m_video != nullptr) m_video->write(space, addr>>1, value); // A14 determines data or register write - } - - // PEB gets all accesses - m_peb->write(space, addr, value); - m_peb->memen_in(CLEAR_LINE); -} - -void ti99_datamux_device::setaddress_all(address_space& space, uint16_t addr) -{ - line_state a14 = ((addr & 2)!=0)? ASSERT_LINE : CLEAR_LINE; - - // Valid access = not(DBIN and A5) - bool validaccess = (m_dbin==CLEAR_LINE || (addr & 0x0400)==0); - - // GROM access - bool isgrom = ((addr & 0xf801)==0x9800) && validaccess; - - // Cartridge ROM - bool iscartrom = ((addr & 0xe000)==0x6000); - - // Always deliver to GROM so that the select line may be cleared - int lines = (m_dbin==ASSERT_LINE)? 1 : 0; - if (a14==ASSERT_LINE) lines |= 2; - line_state select = isgrom? ASSERT_LINE : CLEAR_LINE; - - if (select) m_grom_idle = false; - - if (m_console_groms_present) - for (int i=0; i < 3; i++) - m_grom[i]->set_lines(space, lines, select); - - // GROMport (GROMs) - m_gromport->set_gromlines(space, lines, select); - - // Sound chip and video chip do not require the address to be set before access - - // GROMport (ROMs) - m_gromport->romgq_line(iscartrom? ASSERT_LINE : CLEAR_LINE); - - // PEB gets all accesses - m_peb->memen_in(ASSERT_LINE); - m_peb->setaddress_dbin(space, addr, m_dbin); -} - -/* - Special debugger access. The access is similar to the normal access, - but it bypasses the wait state circuitry. Also, access ports of memory- - mapped devices are excluded because their state would be changed - unpredictably by the debugger access. -*/ -uint16_t ti99_datamux_device::debugger_read(address_space& space, uint16_t addr) -{ - uint16_t addrb = addr << 1; - uint16_t value = 0; - - if ((addrb & 0xe000)==0x0000) value = m_consolerom[(addrb & 0x1fff)>>1]; - else - { - if ((addrb & 0xfc00)==0x8000) - value = (m_padram->pointer()[addrb & 0x00ff] << 8) | m_padram->pointer()[(addrb & 0x00ff)+1]; - else - { - int base32k = 0; - if (m_use32k) - { - if ((addrb & 0xe000)==0x2000) base32k = 0x2000; - if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000; - } - - if (base32k != 0) - { - value = (m_ram16b->pointer()[addrb-base32k] << 8) | m_ram16b->pointer()[addrb-base32k+1]; - } - else - { - uint8_t lval = 0; - uint8_t hval = 0; - - if ((addrb & 0xe000)==0x6000) - { - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->readz(space, addrb+1, &lval); - m_gromport->readz(space, addrb, &hval); - m_gromport->romgq_line(CLEAR_LINE); - } - m_peb->memen_in(ASSERT_LINE); - m_peb->readz(space, addrb+1, &lval); - m_peb->readz(space, addrb, &hval); - m_peb->memen_in(CLEAR_LINE); - value = ((hval << 8)&0xff00) | (lval & 0xff); - } - } - } - return value; -} - -void ti99_datamux_device::debugger_write(address_space& space, uint16_t addr, uint16_t data) -{ - uint16_t addrb = addr << 1; - - if ((addrb & 0xe000)==0x0000) return; - - if ((addrb & 0xfc00)==0x8000) - { - m_padram->pointer()[addrb & 0x00ff] = data >> 8; - m_padram->pointer()[(addrb & 0x00ff)+1] = data & 0xff; - } - else - { - int base32k = 0; - if (m_use32k) - { - if ((addrb & 0xe000)==0x2000) base32k = 0x2000; - if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000; - } - - if (base32k != 0) - { - m_ram16b->pointer()[addrb-base32k] = data >> 8; - m_ram16b->pointer()[(addrb-base32k)+1] = data & 0xff; - } - else - { - if ((addrb & 0xe000)==0x6000) - { - m_gromport->romgq_line(ASSERT_LINE); - m_gromport->write(space, addr+1, data & 0xff); - m_gromport->write(space, addr, (data>>8) & 0xff); - m_gromport->romgq_line(CLEAR_LINE); - } - - m_peb->memen_in(ASSERT_LINE); - m_peb->write(space, addr+1, data & 0xff); - m_peb->write(space, addr, (data>>8) & 0xff); - m_peb->memen_in(CLEAR_LINE); - } - } -} - -/* - Read access. We are using two loops because the delay between both - accesses must not occur within the loop. So we have one access on the bus, - a delay, and then the second access. - - mem_mask is always ffff on TMS processors (cannot control bus width) -*/ -READ16_MEMBER( ti99_datamux_device::read ) -{ - uint16_t value = 0; - - // Care for debugger - if (machine().side_effect_disabled()) - { - return debugger_read(space, offset); - } - - // Addresses below 0x2000 are ROM (no wait states) - if ((m_addr_buf & 0xe000)==0x0000) - { - value = m_consolerom[(m_addr_buf & 0x1fff)>>1]; - } - else - { - // Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM (no wait states) - if ((m_addr_buf & 0xfc00)==0x8000) - { - value = (m_padram->pointer()[m_addr_buf & 0x00ff] << 8) | m_padram->pointer()[(m_addr_buf & 0x00ff)+1]; - } - else - { - // Looks ugly, but this is close to the real thing. If the 16bit - // memory expansion is installed in the console, and the access hits its - // space, just respond to the memory access and don't bother the - // datamux in any way. In particular, do not make the datamux insert wait - // states. - - if (m_base32k != 0) - { - value = (m_ram16b->pointer()[m_addr_buf-m_base32k] << 8) | m_ram16b->pointer()[(m_addr_buf-m_base32k)+1]; - } - else - { - // The byte from the odd address has already been read into the latch - // Reading the even address now (addr) - uint8_t hbyte = 0; - read_all(space, m_addr_buf, &hbyte); - if (TRACE_ACCESS) logerror("Read even byte from address %04x -> %02x\n", m_addr_buf, hbyte); - - value = (hbyte<<8) | m_latch; - } - } - } - return value; -} - -/* - Write access. -*/ -WRITE16_MEMBER( ti99_datamux_device::write ) -{ - if (machine().side_effect_disabled()) - { - debugger_write(space, offset, data); - return; - } - - // Addresses below 0x2000 are ROM - if ((m_addr_buf & 0xe000)==0x0000) - { - return; - } - - // Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM - if ((m_addr_buf & 0xfc00)==0x8000) - { - m_padram->pointer()[(m_addr_buf & 0x00ff)] = data >> 8; - m_padram->pointer()[(m_addr_buf & 0x00ff)+1] = data & 0xff; - return; - } - - // Handle the internal 32K expansion - if (m_base32k != 0) - { - m_ram16b->pointer()[(m_addr_buf-m_base32k)] = data >> 8; - m_ram16b->pointer()[(m_addr_buf-m_base32k)+1] = data & 0xff; - } - 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 - if (TRACE_ACCESS) logerror("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 ) -{ - m_addr_buf = offset << 1; - m_waitcount = 0; - - if (TRACE_ADDRESS) logerror("set address %04x\n", m_addr_buf); - - if ((m_addr_buf & 0xe000) == 0x0000) - { - return; // console ROM - } - - if ((m_addr_buf & 0xfc00) == 0x8000) - { - return; // console RAM - } - - // Initialize counter - // 1 cycle for loading into the datamux - // 2 subsequent wait states (LSB) - // 2 subsequent wait states (MSB) - // clock cycle 6 is the nominal follower of the last wait state - m_waitcount = 5; - - m_base32k = 0; - if (m_use32k) - { - if ((m_addr_buf & 0xe000)==0x2000) m_base32k = 0x2000; - if (((m_addr_buf & 0xe000)==0xa000) || ((m_addr_buf & 0xc000)==0xc000)) m_base32k = 0x8000; - } - - // Suspend the CPU if not using the 32K - if (m_base32k == 0) - { - // propagate the setaddress operation - // First the odd address - setaddress_all(space, m_addr_buf+1); - m_muxready = CLEAR_LINE; - ready_join(); - } - else m_waitcount = 0; -} - -/* - The datamux is connected to the clock line in order to operate - the wait state counter and to read/write the bytes. -*/ -WRITE_LINE_MEMBER( ti99_datamux_device::clock_in ) -{ - // return immediately if the datamux is currently inactive - if (m_waitcount>0) - { - if (TRACE_WAITCOUNT) logerror("datamux: wait count %d\n", m_waitcount); - if (m_sysready==CLEAR_LINE) - { - if (TRACE_READY) 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 = ASSERT_LINE; - ready_join(); - } - if (m_waitcount==2) - { - // read odd byte - read_all(*m_spacep, m_addr_buf+1, &m_latch); - if (TRACE_ACCESS) logerror("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 (state==ASSERT_LINE) - { // raising edge - if (--m_waitcount==0) - { - m_muxready = ASSERT_LINE; - ready_join(); - } - } - 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 (TRACE_ACCESS) logerror("datamux: write even byte to address %04x <- %02x\n", m_addr_buf, m_latch); - write_all(*m_spacep, m_addr_buf, m_latch); - } - } - } - } -} - -/* - Combine the external (sysready) and the own (muxready) READY states. -*/ -void ti99_datamux_device::ready_join() -{ - m_ready((m_sysready==CLEAR_LINE || m_muxready==CLEAR_LINE)? CLEAR_LINE : ASSERT_LINE); -} - -WRITE_LINE_MEMBER( ti99_datamux_device::dbin_in ) -{ - m_dbin = (line_state)state; - if (TRACE_ADDRESS) logerror("data bus in = %d\n", (m_dbin==ASSERT_LINE)? 1:0 ); -} - -WRITE_LINE_MEMBER( ti99_datamux_device::ready_line ) -{ - if (TRACE_READY) - { - if (state != m_sysready) logerror("READY line from PBox = %d\n", state); - } - m_sysready = (line_state)state; - // Also propagate to CPU via driver - ready_join(); -} - -/* Called from VDP via console. */ -WRITE_LINE_MEMBER( ti99_datamux_device::gromclk_in ) -{ - // Don't propagate the clock in idle phase - if (m_grom_idle) return; - - // Propagate to the GROMs - if (m_console_groms_present) - { - for (int i=0; i < 3; i++) m_grom[i]->gclock_in(state); - m_grom_idle = m_grom[0]->idle(); - } - m_gromport->gclock_in(state); - - // Only ask the gromport when we don't have GROMs in the console - if (!m_console_groms_present) - m_grom_idle = m_gromport->is_grom_idle(); -} - -/*************************************************************************** - DEVICE LIFECYCLE FUNCTIONS -***************************************************************************/ - -void ti99_datamux_device::device_start(void) -{ - m_muxready = ASSERT_LINE; - m_ready.resolve(); - - // Register persistable state variables - save_item(NAME(m_addr_buf)); - save_item(NAME(m_dbin)); - save_item(NAME(m_muxready)); - save_item(NAME(m_sysready)); - save_item(NAME(m_latch)); - save_item(NAME(m_waitcount)); - save_item(NAME(m_use32k)); - save_item(NAME(m_base32k)); - save_item(NAME(m_console_groms_present)); - save_item(NAME(m_grom_idle)); -} - -void ti99_datamux_device::device_stop(void) -{ -} - -void ti99_datamux_device::device_reset(void) -{ - m_consolerom = (uint16_t*)owner()->memregion(CONSOLEROM)->base(); - m_use32k = (ioport("RAM")->read()==1); - m_console_groms_present = (ioport("GROMENA")->read()==1); - - m_sysready = ASSERT_LINE; - m_muxready = ASSERT_LINE; - ready_join(); - - m_waitcount = 0; - m_latch = 0; - - m_dbin = CLEAR_LINE; - - // Get the pointer to the address space already here, because we cannot - // save that pointer to a savestate, and we need it on restore - cpu_device* cpu = downcast(machine().device("maincpu")); - m_spacep = &cpu->space(AS_PROGRAM); -} - -void ti99_datamux_device::device_config_complete() -{ - m_grom[0] = downcast(owner()->subdevice(GROM0_TAG)); - m_grom[1] = downcast(owner()->subdevice(GROM1_TAG)); - m_grom[2] = downcast(owner()->subdevice(GROM2_TAG)); -} - - -INPUT_PORTS_START( datamux ) - PORT_START( "RAM" ) /* config */ - PORT_CONFNAME( 0x01, 0x00, "Console 32 KiB RAM upgrade (16 bit)" ) - PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) - PORT_CONFSETTING( 0x01, DEF_STR( On ) ) - - PORT_START( "GROMENA" ) - PORT_CONFNAME( 0x01, 0x01, "Console GROMs" ) - PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) - PORT_CONFSETTING( 0x01, DEF_STR( On ) ) - -INPUT_PORTS_END - -ioport_constructor ti99_datamux_device::device_input_ports() const -{ - return INPUT_PORTS_NAME(datamux); -} - -DEFINE_DEVICE_TYPE(TI99_DATAMUX, ti99_datamux_device, "ti99_datamux", "TI-99 Databus multiplexer") diff --git a/src/devices/bus/ti99x/datamux.h b/src/devices/bus/ti99x/datamux.h deleted file mode 100644 index ee0042d7ca9..00000000000 --- a/src/devices/bus/ti99x/datamux.h +++ /dev/null @@ -1,142 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/**************************************************************************** - - TI-99/4(A) databus multiplexer circuit - See datamux.c for documentation - - Michael Zapf - - February 2012: Rewritten as class - -*****************************************************************************/ - -#ifndef MAME_BUS_TI99X_DATAMUX_H -#define MAME_BUS_TI99X_DATAMUX_H - -#pragma once - -#include "ti99defs.h" -#include "machine/tmc0430.h" -#include "gromport.h" -#include "bus/ti99/peb/peribox.h" -#include "sound/sn76496.h" -#include "video/tms9928a.h" -#include "machine/ram.h" - -DECLARE_DEVICE_TYPE(TI99_DATAMUX, ti99_datamux_device) - -/* - Main class -*/ -class ti99_datamux_device : public device_t -{ -public: - ti99_datamux_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - DECLARE_READ16_MEMBER( read ); - DECLARE_WRITE16_MEMBER( write ); - DECLARE_SETOFFSET_MEMBER( setoffset ); - - DECLARE_WRITE_LINE_MEMBER( clock_in ); - DECLARE_WRITE_LINE_MEMBER( dbin_in ); - DECLARE_WRITE_LINE_MEMBER( ready_line ); - - DECLARE_WRITE_LINE_MEMBER( gromclk_in ); - - template static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) - { - return downcast(device).m_ready.set_callback(std::forward(cb)); - } - -protected: - /* Constructor */ - void device_start() override; - void device_stop() override; - void device_reset() override; - void device_config_complete() override; - ioport_constructor device_input_ports() const override; - -private: - // Link to the video processor - optional_device m_video; - - // Link to the sound processor - optional_device m_sound; - - // Link to the peripheral expansion box - required_device m_peb; - - // Link to the cartridge port (aka GROM port) - required_device m_gromport; - - // Memory expansion (internal, 16 bit) - required_device m_ram16b; - - // Console RAM - required_device m_padram; - - // Keeps the address space pointer - address_space* m_spacep; - - // Console ROM - uint16_t* m_consolerom; - - // Console GROMs - tmc0430_device* m_grom[3]; - - // Common read routine - void read_all(address_space& space, uint16_t addr, uint8_t *target); - - // Common write routine - void write_all(address_space& space, uint16_t addr, uint8_t value); - - // Common set address method - void setaddress_all(address_space& space, uint16_t addr); - - // Debugger access - uint16_t debugger_read(address_space& space, uint16_t addr); - void debugger_write(address_space& space, uint16_t addr, uint16_t data); - - // Join own READY and external READY - void ready_join(); - - // Ready line to the CPU - devcb_write_line m_ready; - - // Address latch (emu). In reality, the address bus remains constant. - uint16_t m_addr_buf; - - // DBIN line - int m_dbin; - - // Own ready state. - int m_muxready; - - // Ready state. Needed to control wait state generation via inbound READY - int m_sysready; - - // Latch which stores the first (odd) byte - uint8_t m_latch; - - // Counter for the wait states. - int m_waitcount; - - // Use the memory expansion? - bool m_use32k; - - // Memory base for piggy-back 32K expansion. If 0, expansion is not used. - uint16_t m_base32k; - - // Console GROMs are available (the HSGPL expects them to be removed) - bool m_console_groms_present; - - // GROMs are idle, no need to propagate the clock - bool m_grom_idle; -}; - -/******************************************************************************/ - -#define MCFG_DMUX_READY_HANDLER( _intcallb ) \ - devcb = &ti99_datamux_device::static_set_ready_callback( *device, DEVCB_##_intcallb ); - -#endif // MAME_BUS_TI99X_DATAMUX_H diff --git a/src/devices/bus/ti99x/genboard.cpp b/src/devices/bus/ti99x/genboard.cpp deleted file mode 100644 index 1eb494005c8..00000000000 --- a/src/devices/bus/ti99x/genboard.cpp +++ /dev/null @@ -1,2011 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/*************************************************************************** - Geneve 9640 mapper and more components - - This file contains 2 classes: - - mapper: main function of the Gate Array on the Geneve board. Maps logical - memory accesses to a wider address space using map registers. - - keyboard: an implementation of a XT-style keyboard. This should be dropped - and replaced by a proper XT keyboard implementation. - - Onboard SRAM configuration: - There is an adjustable SRAM configuration on board, representing the - various enhancements by users. - - The standard memory configuration as reported by chkdsk (32 KiB): - 557056 bytes of total memory - - With 64 KiB SRAM: - 589824 bytes of total memory - - With 384 KiB SRAM: - 917504 bytes of total memory - - The original 32 KiB SRAM memory needs to be expanded to 64 KiB for - MDOS 2.50s and higher, or the system will lock up. Therefore the emulation - default is 64 KiB. - - The ultimate expansion is a 512 KiB SRAM circuit wired to the gate array - to provide 48 pages of fast static RAM. This also requires to build an - adapter for a larger socket. From the 512 KiB, only 384 KiB will be - accessed, since the higher pages are hidden behind the EPROM pages. - - === Address map === - p,q = page value bit (q = AMC, AMB, AMA) - c = address offset within 8 KiB page - - p pqqq pppc cccc cccc cccc - - 0 0... .... .... .... .... on-board dram 512 KiB - - 0 1... .... .... .... .... on-board future expansion 512 KiB or Memex with Genmod - - 1 00.. .... .... .... .... p-box AMA=0 (256 KiB) - 1 010. .... .... .... .... p-box AMA=1 AMB=0 (128 KiB) - 1 0110 .... .... .... .... p-box AMA=1 AMB=1 AMC=0 (64 KiB) - - 1 0111 00.. .... .... .... p-box address block 0xxx, 2xxx - 1 0111 010. .... .... .... p-box address block 4xxx (DSR) - 1 0111 011. .... .... .... p-box address block 6xxx - 1 0111 100. .... .... .... p-box address block 8xxx (Speech at 0x9000) - 1 0111 101. .... .... .... p-box address block axxx - 1 0111 11.. .... .... .... p-box address block cxxx, exxx - - 1 100. .... .... .... .... on-board sram (128K) -\ - 1 101. .... .... .... .... on-board sram (128K) --+- maximum SRAM expansion - 1 1100 .... .... .... .... on-board sram (64K) --/ - 1 1101 0... .... .... .... on-board sram (32K) - additional 32 KiB required for MDOS 2.50s and higher - 1 1101 1... .... .... .... on-board sram (32K) - standard setup - - 1 111. ..0. .... .... .... on-board boot1 - 1 111. ..1. .... .... .... on-board boot2 - - The TI console (or more precise, the Flex Cable Interface) sets the AMA/B/C - lines to 1. Most cards actually check for AMA/B/C=1. However, this decoding - was forgotten in third party cards which cause the card address space - to be mirrored. The usual DSR space at 4000-5fff which would be reachable - via page 0xba is then mirrored on a number of other pages: - - 10 xxx 010x = 82, 8a, 92, 9a, a2, aa, b2, ba - - Another block to take care of is 0xbc which covers 8000-9fff since this - area contains the speech synthesizer port at 9000/9400. - - For the standard Geneve, only prefix 10 is routed to the P-Box. The Genmod - modification wires these address lines to pins 8 and 9 in the P-Box as AMD and - AME. This requires all cards to be equipped with an additional selection logic - to detect AMD=0, AME=1. Otherwise these cards, although completely decoding the - 19-bit address, would reappear at 512 KiB distances. - - Genmod's double switch box is also emulated. There are two switches: - - Turbo mode: Activates or deactivates the wait state logic on the Geneve - board. This switch may be changed at any time. - - TI mode: Selects between the on-board memory, which is obviously required - for the GPL interpreter, and the external Memex memory. This switch - triggers a reset when changed. - - - =================== - Mapping - =================== - - Logical address space: 64 KiB - - Geneve mode: - - Video: F100, F102, F104, F106 (mirror: +8) - Mapper: F110 - F117 - Keyboard: F118 - Clock: F130 - F13F - Sound: F120 - - TI mode: - - Video: 8800, 8802, 8804, 8806 - Mapper: 8000 - 8007 - Keyboard: 8008 - 800F - Clock: 8010 - 801F - Speech: 9000 / 9400 - Grom: 9800 / 9802 - - Physical address space: 2 MiB - - Start End Banks - 000000 - 07FFFF 00-3F 512 KiB DRAM on-board - 080000 - 0FFFFF 40-7F 512 KiB on-board expansion (never used) - 100000 - 16FFFF 80-B7 448 KiB P-Box space (special cards, like MEMEX) - 170000 - 17FFFF B8-BF 64 KiB P-Box space (current cards) - 180000 - 1DFFFF C0-EF 384 KiB SRAM space on-board; stock Geneve comes with 32 KiB - 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors - - - GenMod modification: - - TI mode - 000000 - 07FFFF 00-3F 512 KiB DRAM on-board - 080000 - 1DFFFF 40-EF 1408 KiB P-Box space - 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors - - Non-TI mode - 000000 - 1DFFFF 00-EF 1920 KiB P-Box space - 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors - - Waitstate handling - ------------------ - Waitstates are caused by a cleared READY line of the TMS9995 processor - during an external memory cycle. That means that waitstates have no effect - for operations within the on-chip memory, and only when an access to the - external memory or other devices occurs, a delay will be noticed. - - The waitstates are generated by the custom Gate Array chip on the board - and the PAL 16R4, both lacking proper documentation. All of the following - numbers have been determined by experiments with the real machine. - - Waitstates are generated for: - - memory-mapped devices (mapper, clock, keyboard): 1 WS - - accesses to the peripheral expansion box: 1 WS - - accesses to on-board DRAM: 1 WS - - accesses to video: 15 WS - - accesses to sound: ~25 WS - - accesses to SRAM: 0 WS - - Additional waitstates are created when one of the CRU bits is set. In that - case, all delays are extended to 2 WS (including SRAM). - - Sound waitstates are somewhat unpredictable. It seems as if they depend - on the clock of the sound chip; the theory is that the READY line is - pulled down until the next clock pulse, which may take some value between - 18 CPU cycles and 30 CPU cycles. - - The gate array is able to create wait states for video accesses. However, - these wait states are effective after the video access has been completed. - Wait states are not effective when the execution is running in on-chip - RAM. Additional wait states are requested by m_video_waitstates = true. - Without additional wait states, the video access takes the usual 1 or 2 WS. - - Waitstate behavior (Nov 2013) - Almost perfect. Only video read access from code in DRAM is too fast by one WS - - ========================== - PFM expansion - ========================== - - The "Programmable Flash Memory expansion" is a replacement for the boot - EPROM. - - PFM: Original version, 128 KiB - PFM+: Expansion of the original version, piggybacked, adds another 128KiB - PFM512: Using an AT29C040 (not A), 512 KiB - - The PFM is visible as four banks in memory pages 0xF0 - 0xFF. - - Bank switching is done by four 9901 pins: - - 0028: LSB of bank number - 003A: MSB of bank number - - Bank 0 is the boot code, while banks 1-3 can be used as flash drives - - Michael Zapf, October 2011 - February 2012: rewritten as class, restructured - Aug 2015: PFM added - -***************************************************************************/ - -#include "emu.h" -#include "genboard.h" - -#define TRACE_READ 0 -#define TRACE_WRITE 0 -#define TRACE_DETAIL 0 -#define TRACE_KEYBOARD 0 -#define TRACE_CLOCK 0 -#define TRACE_LINES 0 -#define TRACE_SETTING 1 -#define TRACE_VIDEOWS 0 -#define TRACE_PFM 0 - -#define SRAM_PAR_TAG ":sram" -#define DRAM_PAR_TAG ":dram" - -geneve_mapper_device::geneve_mapper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, GENEVE_MAPPER, tag, owner, clock), m_gromwaddr_LSB(false), - m_gromraddr_LSB(false), - m_grom_address(0), - m_video_waitstates(false), - m_extra_waitstates(false), - m_ready_asserted(false), - m_read_mode(false), - m_debug_no_ws(false), - m_geneve_mode(false), - m_direct_mode(false), - m_cartridge_size(0), - m_cartridge_secondpage(false), - m_cartridge6_writable(false), - m_cartridge7_writable(false), - m_turbo(false), - m_genmod(false), - m_timode(false), - m_pfm_mode(0), - m_pfm_bank(0), - m_pfm_output_enable(false), - m_sram_mask(0), - m_sram_val(0), - m_ready(*this), - m_waitcount(0), - m_ext_waitcount(0), - m_clock(*owner, GCLOCK_TAG), - m_cpu(*owner, "maincpu"), - m_pfm512(*owner, PFM512_TAG), - m_pfm512a(*owner, PFM512A_TAG), - m_sound(*owner, TISOUNDCHIP_TAG), - m_keyboard(*owner, GKEYBOARD_TAG), - m_video(*owner, VDP_TAG), - m_peribox(*owner, PERIBOX_TAG), - m_sram(*this, SRAM_PAR_TAG), - m_dram(*this, DRAM_PAR_TAG) -{ - m_eprom = nullptr; -} - -INPUT_CHANGED_MEMBER( geneve_mapper_device::settings_changed ) -{ - int number = (int)((uint64_t)param&0x03); - int value = newval; - - switch (number) - { - case 1: - // Turbo switch. May be changed at any time. - if (TRACE_SETTING) logerror("Setting turbo flag to %d\n", value); - m_turbo = (value!=0); - break; - case 2: - // TIMode switch. Causes reset when changed. - if (TRACE_SETTING) logerror("Setting timode flag to %d\n", value); - m_timode = (value!=0); - machine().schedule_hard_reset(); - break; - case 3: - // Used when switching the boot ROMs during runtime, especially the PFM - set_boot_rom(value); - break; - default: - logerror("Unknown setting %d ignored\n", number); - } -} - -/**************************************************************************** - GROM simulation. The Geneve board simulated GROM circuits within its gate - array. -*****************************************************************************/ - -/* - Simulates GROM. The real Geneve does not use GROMs but simulates them - within the gate array. Unlike with real GROMs, no address wrapping occurs, - and the complete 64K space is available. -*/ -READ8_MEMBER( geneve_mapper_device::read_grom ) -{ - uint8_t reply; - if (offset & 0x0002) - { - // GROM address handling - m_gromwaddr_LSB = false; - - if (m_gromraddr_LSB) - { - reply = m_grom_address & 0xff; - m_gromraddr_LSB = false; - } - else - { - reply = (m_grom_address >> 8) & 0xff; - m_gromraddr_LSB = true; - } - } - else - { - // GROM data handling - // GROMs are stored in pages 38..3f - int page = 0x38; - reply = m_dram->pointer()[(page<<13) + m_grom_address]; - m_grom_address = (m_grom_address + 1) & 0xffff; - m_gromraddr_LSB = m_gromwaddr_LSB = false; - } - return reply; -} - -/* - Simulates GROM. The real Geneve does not use GROMs but simulates them - within the gate array. -*/ -WRITE8_MEMBER( geneve_mapper_device::write_grom ) -{ - if (offset & 0x0002) - { - // set address - m_gromraddr_LSB = false; - if (m_gromwaddr_LSB) - { - m_grom_address = (m_grom_address & 0xff00) | data; - m_grom_address = (m_grom_address + 1) & 0xffff; - m_gromwaddr_LSB = false; - } - else - { - m_grom_address = (m_grom_address & 0x00ff) | ((uint16_t)data<<8); - m_gromwaddr_LSB = true; - } - } - else - { // write GPL data - // The Geneve GROM simulator allows for GROM writing (verified with a real system) - int page = 0x38; - m_dram->pointer()[(page<<13) + m_grom_address] = data; - - m_grom_address = (m_grom_address + 1) & 0xffff; - m_gromraddr_LSB = m_gromwaddr_LSB = false; - } -} - -void geneve_mapper_device::set_wait(int min) -{ - if (m_debug_no_ws) return; - if (m_extra_waitstates && min < 2) min = 2; - - // if we still have video wait states, do not set this counter - // (or it will assert READY when expiring) - if (m_ext_waitcount > min) return; - - // need one more pass so that READY will be asserted again - m_waitcount = min + 1; - if (m_waitcount > 1) - { - if (TRACE_LINES) logerror("Pulling down READY line for %d cycles\n", min); - m_ready(CLEAR_LINE); - m_ready_asserted = false; - } -} - -void geneve_mapper_device::set_ext_wait(int min) -{ - if (m_debug_no_ws) return; - m_ext_waitcount = min; -} - -void geneve_mapper_device::set_boot_rom(int selection) -{ - switch (selection) - { - case GENEVE_098: - logerror("Using 0.98 boot eprom\n"); - m_eprom = machine().root_device().memregion("maincpu")->base() + 0x4000; - m_pfm_mode = 0; - break; - case GENEVE_100: - logerror("Using 1.00 boot eprom\n"); - m_eprom = machine().root_device().memregion("maincpu")->base(); - m_pfm_mode = 0; - break; - case GENEVE_PFM512: - logerror("Using PFM512 (AT29C040)\n"); - m_pfm_mode = 1; - break; - case GENEVE_PFM512A: - logerror("Using PFM512A (AT29C040A)\n"); - m_pfm_mode = 2; - break; - default: - logerror("Unknown boot ROM selection\n"); - } -} - -void geneve_mapper_device::set_geneve_mode(bool geneve) -{ - if (TRACE_SETTING) logerror("Setting Geneve mode = %d\n", geneve); - m_geneve_mode = geneve; -} - -void geneve_mapper_device::set_direct_mode(bool direct) -{ - if (TRACE_SETTING) logerror("Setting direct mode = %d\n", direct); - m_direct_mode = direct; -} - -void geneve_mapper_device::set_cartridge_size(int size) -{ - if (TRACE_SETTING) logerror("Setting cartridge size to %d\n", size); - m_cartridge_size = size; -} - -void geneve_mapper_device::set_cartridge_writable(int base, bool write) -{ - if (TRACE_SETTING) logerror("Cartridge %04x space writable = %d\n", base, write); - if (base==0x6000) m_cartridge6_writable = write; - else m_cartridge7_writable = write; -} - -void geneve_mapper_device::set_video_waitstates(bool wait) -{ - if (TRACE_VIDEOWS) logerror("Setting video waitstates = %d\n", wait); - m_video_waitstates = wait; -} - -void geneve_mapper_device::set_extra_waitstates(bool wait) -{ - if (TRACE_SETTING) logerror("Setting extra waitstates = %d\n", wait); - m_extra_waitstates = wait; -} - - -/************************************************************************ - Called by the address map -************************************************************************/ -/* - Constants for mapper decoding. Naming scheme: - M=mapper - L=Logical space; P=Physical space - G=Geneve mode; T=TI mode; GM=GenMod -*/ -enum -{ - MLGVIDEO=1, - MLGMAPPER, - MLGKEY, - MLGCLOCK, - MLGSOUND, - MLTMAPPER, - MLTKEY, - MLTCLOCK, - MLTVIDEO, - MLTSPEECH, - MLTGROM, - MLTSOUND, - MPGDRAM, - MPGEXP, - MPGEPROM, - MPGSRAM, - MPGBOX, - MPGMDRAM, - MPGMEPROM, - MPGMBOX -}; - -/* - Read a byte via the data bus. The decoding has already been done in the - SETOFFSET method, and we re-use the values stored there to quickly - access the appropriate component. -*/ -READ8_MEMBER( geneve_mapper_device::readm ) -{ - uint8_t value = 0; - - decdata *dec; - decdata debug; - - // For the debugger, do the decoding here with no wait states - if (machine().side_effect_disabled()) - { - if (m_cpu->is_onchip(offset)) return m_cpu->debug_read_onchip_memory(offset&0xff); - dec = &debug; - m_debug_no_ws = true; - decode(space, offset, true, dec); - } - else - { - // Use the values found in the setaddress phase - dec = &m_decoded; - m_debug_no_ws = false; - } - - switch (dec->function) - { - case MLGVIDEO: - if (!machine().side_effect_disabled()) - { - value = m_video->read(space, dec->offset>>1); - if (TRACE_READ) logerror("Read video %04x -> %02x\n", dec->offset, value); - // Video wait states are created *after* the access - // Accordingly, they have no effect when execution is in onchip RAM - if (m_video_waitstates) set_ext_wait(15); - } - break; - - case MLGMAPPER: - // mapper - value = m_map[dec->offset]; - if (TRACE_READ) logerror("read mapper %04x -> %02x\n", dec->offset, value); - break; - - case MLGKEY: - // key - if (!machine().side_effect_disabled()) value = m_keyboard->get_recent_key(); - if (TRACE_READ) logerror("Read keyboard -> %02x\n", value); - break; - - case MLGCLOCK: - // clock - // tests on the real machine showed that - // upper nibble is 0xf (probably because of the location at 0xf130?) - value = m_clock->read(space, dec->offset) | 0xf0; - if (TRACE_READ) logerror("Read clock %04x -> %02x\n", dec->offset, value); - break; - - case MLTMAPPER: - // mapper - value = m_map[dec->offset]; - if (TRACE_READ) logerror("Read mapper %04x -> %02x\n", dec->offset, value); - break; - - case MLTKEY: - // key - if (!machine().side_effect_disabled()) value = m_keyboard->get_recent_key(); - if (TRACE_READ) logerror("Read keyboard -> %02x\n", value); - break; - - case MLTCLOCK: - // clock - // upper nibble is 1, only last byte gets a 2 - // probably because of the location at 8010...8020? - // (TI mode used swapped byte order) - // unless we use a workspace at >F000, in which case we get 8x values - // Obscure, needs more investigation. We might as well ignore this, - // as the high nibble is obviously undefined and takes some past - // value floating around. - value = m_clock->read(space, dec->offset); - value |= (dec->offset==0x000f)? 0x20 : 0x10; - if (TRACE_READ) logerror("Read clock %04x -> %02x\n", dec->offset, value); - break; - - case MLTVIDEO: - // video - // ++++ ++-- ---- ---+ - // 1000 1000 0000 00x0 - if (!machine().side_effect_disabled()) - { - value = m_video->read(space, dec->offset>>1); - if (TRACE_READ) logerror("Read video %04x -> %02x\n", dec->offset, value); - // See above - if (m_video_waitstates) set_ext_wait(15); - } - break; - - case MLTSPEECH: - // speech - // ++++ ++-- ---- ---+ - // 1001 0000 0000 0000 - // We need to add the address prefix bits - m_peribox->readz(space, dec->offset, &value, 0xff); - m_peribox->memen_in(CLEAR_LINE); - if (TRACE_READ) logerror("Read speech -> %02x\n", value); - break; - - case MLTGROM: - // grom simulation - // ++++ ++-- ---- ---+ - // 1001 1000 0000 00x0 - if (!machine().side_effect_disabled()) value = read_grom(space, dec->offset, 0xff); - if (TRACE_READ) logerror("Read GROM %04x -> %02x\n", dec->offset, value); - break; - - case MLGSOUND: - case MLTSOUND: - value = 0; - break; - - - case MPGDRAM: - // DRAM. - value = m_dram->pointer()[dec->physaddr]; -// LOG("dram read physaddr = %06x logaddr = %04x value = %02x\n", dec->physaddr, dec->offset, value); - if (TRACE_READ) logerror("Read DRAM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); - break; - - case MPGEXP: - // On-board memory expansion for standard Geneve (never used) - if (TRACE_READ) logerror("Read unmapped area %06x\n", dec->physaddr); - value = 0; - break; - - case MPGEPROM: - // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) - // mirrored for f0, f2, f4, ...; f1, f3, f5, ... - if (m_pfm_mode == 0) - { - value = m_eprom[dec->physaddr & 0x003fff]; - if (TRACE_READ) logerror("Read EPROM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); - } - else value = read_from_pfm(space, dec->physaddr, 0xff); - - break; - - case MPGSRAM: - if ((dec->physaddr & m_sram_mask)==m_sram_val) - { - value = m_sram->pointer()[dec->physaddr & ~m_sram_mask]; - } - else value = 0; - // Return in any case -// LOG("sram read physaddr = %06x logaddr = %04x value = %02x\n", dec->physaddr, dec->offset, value); - if (TRACE_READ) logerror("Read SRAM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); - break; - - case MPGBOX: - // Route everything else to the P-Box - // 0x000000-0x07ffff for the stock Geneve (AMC,AMB,AMA,A0 ...,A15) - // 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15) - - m_peribox->readz(space, dec->physaddr, &value, 0xff); - m_peribox->memen_in(CLEAR_LINE); - if (TRACE_READ) logerror("Read P-Box %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); - break; - - case MPGMDRAM: - // DRAM. One wait state. - value = m_dram->pointer()[dec->physaddr]; - break; - - case MPGMEPROM: - // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) - // mirrored for f0, f2, f4, ...; f1, f3, f5, ... - if (m_pfm_mode == 0) - { - value = m_eprom[dec->physaddr & 0x003fff]; - if (TRACE_READ) logerror("Read EPROM %04x (%06x) -> %02x\n", dec->offset, dec->physaddr, value); - } - else value = read_from_pfm(space, dec->physaddr, 0xff); - break; - - case MPGMBOX: - // Route everything else to the P-Box - m_peribox->readz(space, dec->physaddr, &value, 0xff); - m_peribox->memen_in(CLEAR_LINE); - break; - } - return value; -} - -WRITE8_MEMBER( geneve_mapper_device::writem ) -{ - decdata *dec; - decdata debug; - - // For the debugger, do the decoding here with no wait states - if (machine().side_effect_disabled()) - { - dec = &debug; - m_debug_no_ws = true; - decode(space, offset, false, dec); - } - else - { - // Use the values found in the setaddress phase - m_debug_no_ws = false; - dec = &m_decoded; - } - - switch (dec->function) - { - case MLGVIDEO: - // video - // ++++ ++++ ++++ ---+ - // 1111 0001 0000 .cc0 - - if (!machine().side_effect_disabled()) - { - m_video->write(space, dec->offset>>1, data); - if (TRACE_WRITE) logerror("Write video %04x <- %02x\n", offset, data); - // See above - if (m_video_waitstates) set_ext_wait(15); - } - break; - - case MLGMAPPER: - // mapper - m_map[dec->offset] = data; - if (TRACE_WRITE) logerror("Write mapper %04x <- %02x\n", offset, data); - break; - - case MLGCLOCK: - // clock - // ++++ ++++ ++++ ---- - m_clock->write(space, dec->offset, data); - if (TRACE_WRITE) logerror("Write clock %04x <- %02x\n", offset, data); - break; - - case MLGSOUND: - // sound - // ++++ ++++ ++++ ---+ - m_sound->write(space, 0, data, 0xff); - if (TRACE_WRITE) logerror("Write sound <- %02x\n", data); - break; - - case MLTMAPPER: - // mapper - m_map[dec->offset] = data; - if (TRACE_WRITE) logerror("Write mapper %04x <- %02x\n", offset, data); - break; - - case MLTCLOCK: - // clock - m_clock->write(space, dec->offset, data); - if (TRACE_WRITE) logerror("Write clock %04x <- %02x\n", offset, data); - break; - - case MLTVIDEO: - // video - // ++++ ++-- ---- ---+ - // 1000 1100 0000 00c0 - // Initialize waitstate timer - if (!machine().side_effect_disabled()) - { - m_video->write(space, dec->offset>>1, data); - if (TRACE_WRITE) logerror("Write video %04x <- %02x\n", offset, data); - // See above - if (m_video_waitstates) set_ext_wait(15); - } - break; - - case MLTSPEECH: - // speech - // ++++ ++-- ---- ---+ - // 1001 0100 0000 0000 - // We need to add the address prefix bits - m_peribox->write(space, dec->offset, data, 0xff); - m_peribox->memen_in(CLEAR_LINE); - if (TRACE_WRITE) logerror("Write speech <- %02x\n", data); - break; - - case MLTGROM: - // grom simulation - // ++++ ++-- ---- ---+ - // 1001 1100 0000 00c0 - write_grom(space, dec->offset, data, 0xff); - if (TRACE_WRITE) logerror("Write GROM %04x <- %02x\n", offset, data); - break; - - case MLTSOUND: - // sound - // ++++ ++-- ---- ---+ - // 1000 0100 0000 0000 - m_sound->write(space, 0, data, 0xff); - if (TRACE_WRITE) logerror("Write sound <- %02x\n", data); - break; - - case MLTKEY: - case MLGKEY: - break; - - case MPGDRAM: - // DRAM write. One wait state. (only for normal Geneve) - m_dram->pointer()[dec->physaddr] = data; - if (TRACE_WRITE) logerror("Write DRAM %04x (%06x) <- %02x\n", offset, dec->physaddr, data); - break; - - case MPGEXP: - // On-board memory expansion for standard Geneve (never used) - if (TRACE_WRITE) logerror("Write unmapped area %06x\n", dec->physaddr); - break; - - case MPGEPROM: - // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) - // mirrored for f0, f2, f4, ...; f1, f3, f5, ... - // Ignore EPROM write (unless PFM) - if (m_pfm_mode != 0) write_to_pfm(space, dec->physaddr, data, 0xff); - else - logerror("Write EPROM %04x (%06x) <- %02x, ignored\n", offset, dec->physaddr, data); - break; - - case MPGSRAM: - if ((dec->physaddr & m_sram_mask)==m_sram_val) - { - m_sram->pointer()[dec->physaddr & ~m_sram_mask] = data; - } - if (TRACE_WRITE) logerror("Write SRAM %04x (%06x) <- %02x\n", offset, dec->physaddr, data); - break; - - case MPGBOX: - dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address - if (TRACE_WRITE) logerror("Write P-Box %04x (%06x) <- %02x\n", offset, dec->physaddr, data); - m_peribox->write(space, dec->physaddr, data, 0xff); - m_peribox->memen_in(CLEAR_LINE); - break; - - case MPGMDRAM: - // DRAM. One wait state. - m_dram->pointer()[dec->physaddr] = data; - break; - - case MPGMEPROM: - // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) - // mirrored for f0, f2, f4, ...; f1, f3, f5, ... - // Ignore EPROM write - if (m_pfm_mode != 0) write_to_pfm(space, dec->physaddr, data, 0xff); - else - logerror("Write EPROM %04x (%06x) <- %02x, ignored\n", offset, dec->physaddr, data); - break; - - case MPGMBOX: - // Route everything else to the P-Box - m_peribox->write(space, dec->physaddr, data, 0xff); - m_peribox->memen_in(CLEAR_LINE); - break; - } -} - -void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read_mode, geneve_mapper_device::decdata* dec) -{ - dec->function = 0; - dec->offset = offset; - dec->physaddr = 0; - - int page; - - if (read_mode) // got this from DBIN - { - // Logical addresses - if (m_geneve_mode) - { - // TODO: shortcut offset & 0xffc0 = 0xf100 - if ((offset & 0xfff5)==0xf100) - { - // video - // ++++ ++++ ++++ -+-+ - // 1111 0001 0000 0000 - // 1111 0001 0000 0010 - // 1111 0001 0000 1000 - // 1111 0001 0000 1010 - - dec->function = MLGVIDEO; - set_wait(1); - return; - } - if ((offset & 0xfff8)==0xf110) - { - // mapper - dec->function = MLGMAPPER; - dec->offset = dec->offset & 0x0007; - set_wait(1); - return; - } - if ((offset & 0xfff8) == 0xf118) - { - // key - dec->function = MLGKEY; - set_wait(1); - return; - } - if ((offset & 0xfff0)==0xf130) - { - // clock - // tests on the real machine showed that - // upper nibble is 0xf (probably because of the location at 0xf130?) - dec->function = MLGCLOCK; - dec->offset = dec->offset & 0x000f; - set_wait(1); - return; - } - } - else - { - if ((offset & 0xfff8)==0x8000) - { - // mapper - dec->function = MLTMAPPER; - dec->offset = dec->offset & 0x0007; - set_wait(1); - return; - } - if ((offset & 0xfff8)== 0x8008) - { - // key - dec->function = MLTKEY; - set_wait(1); - return; - } - if ((offset & 0xfff0)==0x8010) - { - // clock - dec->function = MLTCLOCK; - dec->offset = dec->offset & 0x000f; - set_wait(1); - return; - } - if ((offset & 0xfc01)==0x8800) - { - // video - // ++++ ++-- ---- ---+ - // 1000 1000 0000 00x0 - // 1 WS is always added; any pending video waitstates are canceled - dec->function = MLTVIDEO; - set_wait(1); - return; - } - if ((offset & 0xfc01)==0x9000) - { - // speech - // ++++ ++-- ---- ---+ - // 1001 0000 0000 0000 - // We need to add the address prefix bits - dec->function = MLTSPEECH; - dec->offset = offset | ((m_genmod)? 0x170000 : 0x070000); - m_peribox->memen_in(ASSERT_LINE); - m_peribox->setaddress_dbin(space, dec->offset, read_mode); - set_wait(1); - return; - } - if ((offset & 0xfc01)==0x9800) - { - // grom simulation - // ++++ ++-- ---- ---+ - // 1001 1000 0000 00x0 - dec->function = MLTGROM; - set_wait(1); - return; - } - } - // still here? Then go via mapping. - page = (offset & 0xe000) >> 13; - - // Determine physical address - if (m_direct_mode) - { - dec->physaddr = 0x1f0000; // points to boot eprom (page F8) - } - else - { - if (!m_geneve_mode && page==3) - { - if (m_cartridge_size==0x4000 && m_cartridge_secondpage) dec->physaddr = 0x06e000; - else dec->physaddr = 0x06c000; - } - else - { - dec->physaddr = (m_map[page] << 13); - } - } - dec->physaddr |= (offset & 0x1fff); - - if (!m_genmod) // Standard Geneve - { - if ((dec->physaddr & 0x180000)==0x000000) - { - // DRAM. - dec->physaddr = dec->physaddr & 0x07ffff; - dec->function = MPGDRAM; - set_wait(1); - return; - } - - if ((dec->physaddr & 0x180000)==0x080000) - { - // On-board memory expansion for standard Geneve (never used) - dec->function = MPGEXP; - set_wait(1); - return; - } - - if ((dec->physaddr & 0x1e0000)==0x1e0000) - { - // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) - // mirrored for f0, f2, f4, ...; f1, f3, f5, ... unless using PFM - dec->function = MPGEPROM; - set_wait(0); - return; - } - - if ((dec->physaddr & 0x180000)==0x180000) - { - dec->function = MPGSRAM; - set_wait(0); - return; - } - - // Route everything else to the P-Box - // 0x000000-0x07ffff for the stock Geneve (AMC,AMB,AMA,A0 ...,A15) - // 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15) - // Add a wait state - set_wait(1); - dec->function = MPGBOX; - - dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address (with AMA..AMC) - m_peribox->memen_in(ASSERT_LINE); - m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); - return; - } - else - { - // GenMod mode - if ((m_timode) && ((dec->physaddr & 0x180000)==0x000000)) - { - // DRAM. One wait state. - dec->function = MPGMDRAM; - dec->physaddr = dec->physaddr & 0x07ffff; - if (!m_turbo) set_wait(1); - return; - } - - if ((dec->physaddr & 0x1e0000)==0x1e0000) - { - // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) - // mirrored for f0, f2, f4, ...; f1, f3, f5, ... unless using PFM - dec->function = MPGMEPROM; - set_wait(0); - return; - } - - // Route everything else to the P-Box - dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod - dec->function = MPGMBOX; - - if (!m_turbo) set_wait(1); - // Check: Are waitstates completely turned off for turbo mode, or - // merely the waitstates for DRAM memory access and box access? - - m_peribox->memen_in(ASSERT_LINE); - m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); - return; - } - } - else - { // Write access - // Logical addresses - if (m_geneve_mode) - { - if ((offset & 0xfff1)==0xf100) - { - // 1 WS is always added; any pending video waitstates are canceled - dec->function = MLGVIDEO; - set_wait(1); - return; - } - if ((offset & 0xfff8)==0xf110) - { - dec->function = MLGMAPPER; - dec->offset = dec->offset & 0x0007; - set_wait(1); - return; - } - if ((offset & 0xfff1)==0xf120) - { - // Add 24 waitstates. This is an average value, as the - // waitstate generation seems to depend on an external timer of - // the sound chip - // TODO: do it properly with the use of READY - dec->function = MLGSOUND; - set_wait(24); - return; - } - if ((offset & 0xfff0)==0xf130) - { - dec->function = MLGCLOCK; - dec->offset = dec->offset & 0x00f; - set_wait(1); - return; - } - } - else - { - // TI mode - if ((offset & 0xfff8)==0x8000) - { - dec->function = MLTMAPPER; - dec->offset = dec->offset & 0x0007; - set_wait(1); - return; - } - if ((offset & 0xfff0)==0x8010) - { - dec->function = MLTCLOCK; - dec->offset = dec->offset & 0x00f; - set_wait(1); - return; - } - if ((offset & 0xfc01)==0x9c00) - { - dec->function = MLTGROM; - set_wait(1); - return; - } - if ((offset & 0xfc01)==0x8400) - { - // Add 24 waitstates. This is an approximation, as the - // waitstate generation seems to depend on an external timer of - // the sound chip - // TODO: do it properly with the use of READY- - dec->function = MLTSOUND; - set_wait(24); - return; - } - if ((offset & 0xfc01)==0x8c00) - { - // 1 WS is always added; any pending video waitstates are canceled - dec->function = MLTVIDEO; - set_wait(1); - return; - } - - if ((offset & 0xfc01)==0x9400) - { - dec->function = MLTSPEECH; - dec->offset = dec->offset | ((m_genmod)? 0x170000 : 0x070000); - m_peribox->memen_in(ASSERT_LINE); - m_peribox->setaddress_dbin(space, dec->offset, read_mode); - set_wait(1); - return; - } - } - - // Determine physical address - page = (dec->offset & 0xe000) >> 13; - - if (m_direct_mode) - { - dec->physaddr = 0x1e0000; // points to boot eprom - } - else - { - if (!m_geneve_mode && page==3) - { - if (m_cartridge_size==0x4000) - { - m_cartridge_secondpage = ((dec->offset & 0x0002)!=0); - if (TRACE_WRITE) logerror("Set cartridge page %02x\n", m_cartridge_secondpage); - set_wait(1); - return; - } - else - { - // writing into cartridge rom space (no bankswitching) - if ((((dec->offset & 0x1000)==0x0000) && !m_cartridge6_writable) - || (((dec->offset & 0x1000)==0x1000) && !m_cartridge7_writable)) - { - logerror("Writing to protected cartridge space %04x ignored\n", dec->offset); - return; - } - else - // TODO: Check whether secondpage is really ignored - dec->physaddr = 0x06c000; - } - } - else - dec->physaddr = (m_map[page] << 13); - } - - dec->physaddr |= dec->offset & 0x1fff; - - if (!m_genmod) - { - if ((dec->physaddr & 0x180000)==0x000000) - { - dec->function = MPGDRAM; - dec->physaddr = dec->physaddr & 0x07ffff; - set_wait(1); - return; - } - if ((dec->physaddr & 0x180000)==0x080000) - { - dec->function = MPGEXP; - set_wait(1); - return; - } - - if ((dec->physaddr & 0x1e0000)==0x1e0000) - { - dec->function = MPGEPROM; - set_wait(0); // EPROM - return; - } - if ((dec->physaddr & 0x180000)==0x180000) - { - dec->function = MPGSRAM; - set_wait(0); // SRAM - return; - } - - // Route everything else to the P-Box - // Add a wait state - - // only AMA, AMB, AMC are used; AMD and AME are not used - dec->function = MPGBOX; - dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address - m_peribox->memen_in(ASSERT_LINE); - m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); - set_wait(1); - } - else - { - // GenMod mode - if ((dec->physaddr & 0x1e0000)==0x1e0000) - { // EPROM, ignore (unless PFM) - dec->function = MPGMEPROM; - set_wait(0); - return; - } - - if (m_timode && ((dec->physaddr & 0x180000)==0x000000)) - { - dec->function = MPGMDRAM; - dec->physaddr = dec->physaddr & 0x07ffff; - if (!m_turbo) set_wait(1); - return; - } - - // Route everything else to the P-Box - dec->function = MPGMBOX; - dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod - m_peribox->memen_in(ASSERT_LINE); - m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); - if (!m_turbo) set_wait(1); - } - } -} - -/* - Read from PFM. -*/ -READ8_MEMBER( geneve_mapper_device::read_from_pfm ) -{ - uint8_t value; - if (!m_pfm_output_enable) return 0; - - int address = (offset & 0x01ffff) | (m_pfm_bank<<17); - - switch (m_pfm_mode) - { - case 1: - value = m_pfm512->read(space, address, mem_mask); - break; - case 2: - value = m_pfm512a->read(space, address, mem_mask); - break; - default: - logerror("Illegal mode for reading PFM: %d\n", m_pfm_mode); - return 0; - } - - if (TRACE_PFM) logerror("Reading from PFM at address %05x -> %02x\n", address, value); - return value; -} - -WRITE8_MEMBER( geneve_mapper_device::write_to_pfm ) -{ - // Nota bene: The PFM must be write protected on startup, or the RESET - // of the 9995 will attempt to write the return vector into the flash EEPROM - int address = (offset & 0x01ffff) | (m_pfm_bank<<17); - if (TRACE_PFM) logerror("Writing to PFM at address %05x <- %02x\n", address, data); - - switch (m_pfm_mode) - { - case 1: - m_pfm512->write(space, address, data, mem_mask); - break; - case 2: - m_pfm512a->write(space, address, data, mem_mask); - break; - default: - logerror("Illegal mode for writing to PFM: %d\n", m_pfm_mode); - } -} - - -/* - Accept the address passed over the address bus and decode it appropriately. - This decoding will later be used in the READ/WRITE member functions. Also, - we initiate wait state creation here. -*/ -SETOFFSET_MEMBER( geneve_mapper_device::setoffset ) -{ - if (TRACE_DETAIL) logerror("setoffset = %04x\n", offset); - m_debug_no_ws = false; - decode(space, offset, m_read_mode, &m_decoded); -} - -/* - The mapper is connected to the clock line in order to operate - the wait state counter. - The wait counter is decremented on each rising clock edge; when 0, the - READY line is asserted. However, there is a second counter which is used for - video wait states. - The READY line must be asserted when the wait counter reaches 0, but must be - cleared immediately again if the video counter has not reached 0. - (See comments at the file header: The additional video wait states do not - affect the video access itself but become effective after the access; if - the code runs on the chip, these wait states are ignored.) -*/ -WRITE_LINE_MEMBER( geneve_mapper_device::clock_in ) -{ - if (state==ASSERT_LINE) - { - // Rising edge - if (!m_ready_asserted) - { - if (m_waitcount > 0) - { - m_waitcount--; - if (m_waitcount == 0) - { - if (TRACE_CLOCK) logerror("clock, READY asserted\n"); - m_ready(ASSERT_LINE); - m_ready_asserted = true; - } - else - { - if (TRACE_CLOCK) logerror("clock\n"); - } - } - else - { - if (m_ext_waitcount > 0) - { - m_ext_waitcount--; - if (m_ext_waitcount == 0) - { - if (TRACE_CLOCK) logerror("clock, READY asserted after video\n"); - m_ready(ASSERT_LINE); - m_ready_asserted = true; - } - else - { - if (TRACE_CLOCK) logerror("vclock, ew=%d\n", m_ext_waitcount); - } - } - } - } - } - else - { - // Falling edge - // Do we have video wait states? In that case, clear the line again - if ((m_waitcount == 0) && (m_ext_waitcount > 0) && m_ready_asserted) - { - if (TRACE_CLOCK) logerror("clock, READY cleared for video\n"); - m_ready(CLEAR_LINE); - m_ready_asserted = false; - } - } -} - -/* - We need the DBIN line for the setoffset operation. -*/ -WRITE_LINE_MEMBER( geneve_mapper_device::dbin_in ) -{ - m_read_mode = (state==ASSERT_LINE); - if (TRACE_DETAIL) logerror("dbin = %02x\n", m_read_mode? 1:0); -} - -/* - PFM expansion: Setting the bank. -*/ -WRITE_LINE_MEMBER( geneve_mapper_device::pfm_select_lsb ) -{ - if (state==ASSERT_LINE) m_pfm_bank |= 1; - else m_pfm_bank &= 0xfe; - if (TRACE_PFM) logerror("Setting bank (l) = %d\n", m_pfm_bank); -} - -WRITE_LINE_MEMBER( geneve_mapper_device::pfm_select_msb ) -{ - if (state==ASSERT_LINE) m_pfm_bank |= 2; - else m_pfm_bank &= 0xfd; - if (TRACE_PFM) logerror("Setting bank (u) = %d\n", m_pfm_bank); -} - -WRITE_LINE_MEMBER( geneve_mapper_device::pfm_output_enable ) -{ - // Negative logic - m_pfm_output_enable = (state==CLEAR_LINE); - if (TRACE_PFM) logerror("PFM output %s\n", m_pfm_output_enable? "enable" : "disable"); -} - -//==================================================================== -// Common device lifecycle -//==================================================================== - -void geneve_mapper_device::device_start() -{ - m_ready.resolve(); - - m_geneve_mode = false; - m_direct_mode = true; - - // State registration - save_item(NAME(m_gromwaddr_LSB)); - save_item(NAME(m_gromraddr_LSB)); - save_item(NAME(m_grom_address)); - save_item(NAME(m_video_waitstates)); - save_item(NAME(m_extra_waitstates)); - save_item(NAME(m_ready_asserted)); - save_item(NAME(m_read_mode)); - save_item(NAME(m_debug_no_ws)); - save_item(NAME(m_geneve_mode)); - save_item(NAME(m_direct_mode)); - save_item(NAME(m_cartridge_size)); - save_item(NAME(m_cartridge_secondpage)); - save_item(NAME(m_cartridge6_writable)); - save_item(NAME(m_cartridge7_writable)); - save_pointer(NAME(m_map), 8); - save_item(NAME(m_decoded.function)); - save_item(NAME(m_decoded.offset)); - save_item(NAME(m_decoded.physaddr)); - save_item(NAME(m_turbo)); - save_item(NAME(m_genmod)); - save_item(NAME(m_timode)); - save_item(NAME(m_pfm_mode)); - save_item(NAME(m_pfm_bank)); - save_item(NAME(m_pfm_output_enable)); - save_item(NAME(m_sram_mask)); - save_item(NAME(m_sram_val)); - save_item(NAME(m_waitcount)); - save_item(NAME(m_ext_waitcount)); -} - -void geneve_mapper_device::device_reset() -{ - m_extra_waitstates = false; - m_video_waitstates = true; - m_read_mode = false; - m_waitcount = 0; - m_ext_waitcount = 0; - m_ready_asserted = true; - - m_geneve_mode =false; - m_direct_mode = true; - m_cartridge_size = 0x4000; - m_cartridge_secondpage = false; - m_cartridge6_writable = false; - m_cartridge7_writable = false; - m_grom_address = 0; - m_pfm_bank = 0; - m_pfm_output_enable = true; - - // Clear map - for (auto & elem : m_map) elem = 0; - - m_genmod = false; - - // Check which boot EPROM we are using (or PFM) - set_boot_rom(machine().root_device().ioport("BOOTROM")->read()); - - // Check for GenMod. We assume that GenMod can be combined with PFM. - if (machine().root_device().ioport("MODE")->read()!=0) - { - logerror("Using GenMod modification\n"); - m_eprom = machine().root_device().memregion("maincpu")->base() + 0x8000; - if (m_eprom[0] != 0xf0) - { - fatalerror("genboard: GenMod boot ROM missing\n"); - } - m_genmod = true; - m_turbo = ((machine().root_device().ioport("GENMODDIPS")->read() & GM_TURBO)!=0); - m_timode = ((machine().root_device().ioport("GENMODDIPS")->read() & GM_TIM)!=0); - } - - switch (machine().root_device().ioport("SRAM")->read()) - { -/* 1 100. .... .... .... .... on-board sram (128K) -+ - 1 101. .... .... .... .... on-board sram (128K) -+-- maximum SRAM expansion - 1 1100 .... .... .... .... on-board sram (64K) --+ - 1 1101 0... .... .... .... on-board sram (32K) - additional 32 KiB required for MDOS 2.50s and higher - 1 1101 1... .... .... .... on-board sram (32K) - standard setup -*/ - case 0: // 32 KiB - m_sram_mask = 0x1f8000; - m_sram_val = 0x1d8000; - break; - case 1: // 64 KiB - m_sram_mask = 0x1f0000; - m_sram_val = 0x1d0000; - break; - case 2: // 384 KiB (actually 512 KiB, but the EPROM masks the upper 128 KiB) - m_sram_mask = 0x180000; - m_sram_val = 0x180000; - break; - } -} - -DEFINE_DEVICE_TYPE(GENEVE_MAPPER, geneve_mapper_device, "geneve_mapper", "Geneve Gate Array") - -/**************************************************************************** - Keyboard support -****************************************************************************/ - -static const uint8_t MF1_CODE[0xe] = -{ - /* extended keys that are equivalent to non-extended keys */ - 0x1c, /* keypad enter */ - 0x1d, /* right control */ - 0x38, /* alt gr */ - // extra codes are 0x5b for Left Windows, 0x5c for Right Windows, 0x5d - // for Menu, 0x5e for power, 0x5f for sleep, 0x63 for wake, but I doubt - // any Geneve program would take advantage of these. */ - - // extended key that is equivalent to a non-extended key - // with shift off - 0x35, /* pad slash */ - - // extended keys that are equivalent to non-extended keys - // with numlock off - 0x47, /* home */ - 0x48, /* up */ - 0x49, /* page up */ - 0x4b, /* left */ - 0x4d, /* right */ - 0x4f, /* end */ - 0x50, /* down */ - 0x51, /* page down */ - 0x52, /* insert */ - 0x53 /* delete */ -}; - -geneve_keyboard_device::geneve_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, GENEVE_KEYBOARD, tag, owner, clock), - m_interrupt(*this), - m_keys(*this, "KEY%u", 0), - m_key_reset(false), m_key_queue_length(0), m_key_queue_head(0), m_key_in_buffer(false), m_key_numlock_state(false), m_key_ctrl_state(0), m_key_alt_state(0), - m_key_real_shift_state(0), m_key_fake_shift_state(false), m_key_fake_unshift_state(false), m_key_autorepeat_key(0), m_key_autorepeat_timer(0), m_keep_keybuf(false), - m_keyboard_clock(false), m_timer(nullptr) -{ -} - -void geneve_keyboard_device::post_in_key_queue(int keycode) -{ - m_key_queue[(m_key_queue_head + m_key_queue_length) % KEYQUEUESIZE] = keycode; - m_key_queue_length++; - - if (TRACE_KEYBOARD) logerror("Posting keycode %02x\n", keycode); -} - -void geneve_keyboard_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - poll(); -} - -void geneve_keyboard_device::poll() -{ - uint32_t keystate; - uint32_t key_transitions; - int i, j; - int keycode; - int pressed; - if (TRACE_KEYBOARD) logerror("Poll keyboard\n"); - if (m_key_reset) return; - - /* Poll keyboard */ - for (i = 0; (i < 4) && (m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); i++) - { - keystate = m_keys[2*i]->read() | (m_keys[2*i + 1]->read() << 16); - key_transitions = keystate ^ m_key_state_save[i]; - if (key_transitions) - { - for (j = 0; (j < 32) && (m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); j++) - { - if ((key_transitions >> j) & 1) - { - keycode = (i << 5) | j; - pressed = ((keystate >> j) & 1); - if (pressed) - m_key_state_save[i] |= (1 << j); - else - m_key_state_save[i] &= ~ (1 << j); - - /* Update auto-repeat */ - if (pressed) - { - m_key_autorepeat_key = keycode; - m_key_autorepeat_timer = KEYAUTOREPEATDELAY+1; - } - else /*if (keycode == m_key_autorepeat_key)*/ - m_key_autorepeat_key = 0; - - // Release Fake Shift/Unshift if another key is pressed - // We do so if a key is released, though it is actually - // required only if it is a modifier key - /*if (pressed)*/ - //{ - if (m_key_fake_shift_state) - { - /* Fake shift release */ - post_in_key_queue(0xe0); - post_in_key_queue(0xaa); - m_key_fake_shift_state = false; - } - if (m_key_fake_unshift_state) - { - /* Fake shift press */ - post_in_key_queue(0xe0); - post_in_key_queue(0x2a); - m_key_fake_unshift_state = false; - } - //} - - /* update shift and numlock state */ - if ((keycode == 0x2a) || (keycode == 0x36)) - m_key_real_shift_state = m_key_real_shift_state + (pressed ? +1 : -1); - if ((keycode == 0x1d) || (keycode == 0x61)) - m_key_ctrl_state = m_key_ctrl_state + (pressed ? +1 : -1); - if ((keycode == 0x38) || (keycode == 0x62)) - m_key_alt_state = m_key_alt_state + (pressed ? +1 : -1); - if ((keycode == 0x45) && pressed) - m_key_numlock_state = !m_key_numlock_state; - - if ((keycode >= 0x60) && (keycode < 0x6e)) - { /* simpler extended keys */ - /* these keys are emulated */ - - if ((keycode >= 0x63) && pressed) - { - /* Handle shift state */ - if (keycode == 0x63) - { /* non-shifted key */ - if (m_key_real_shift_state!=0) - /* Fake shift unpress */ - m_key_fake_unshift_state = true; - } - else /*if (keycode >= 0x64)*/ - { /* non-numlock mode key */ - if (m_key_numlock_state & (m_key_real_shift_state==0)) - /* Fake shift press if numlock is active */ - m_key_fake_shift_state = true; - else if ((!m_key_numlock_state) & (m_key_real_shift_state!=0)) - /* Fake shift unpress if shift is down */ - m_key_fake_unshift_state = true; - } - - if (m_key_fake_shift_state) - { - post_in_key_queue(0xe0); - post_in_key_queue(0x2a); - } - - if (m_key_fake_unshift_state) - { - post_in_key_queue(0xe0); - post_in_key_queue(0xaa); - } - } - - keycode = MF1_CODE[keycode-0x60]; - if (!pressed) keycode |= 0x80; - post_in_key_queue(0xe0); - post_in_key_queue(keycode); - } - else if (keycode == 0x6e) - { /* emulate Print Screen / System Request (F13) key */ - /* this is a bit complex, as Alt+PrtScr -> SysRq */ - /* Additionally, Ctrl+PrtScr involves no fake shift press */ - if (m_key_alt_state!=0) - { - /* SysRq */ - keycode = 0x54; - if (!pressed) keycode |= 0x80; - post_in_key_queue(keycode); - } - else - { - /* Handle shift state */ - if (pressed && (m_key_real_shift_state==0) && (m_key_ctrl_state==0)) - { /* Fake shift press */ - post_in_key_queue(0xe0); - post_in_key_queue(0x2a); - m_key_fake_shift_state = true; - } - - keycode = 0x37; - if (!pressed) keycode |= 0x80; - post_in_key_queue(0xe0); - post_in_key_queue(keycode); - } - } - else if (keycode == 0x6f) - { // emulate pause (F15) key - // this is a bit complex, as Pause -> Ctrl+NumLock and - // Ctrl+Pause -> Ctrl+ScrLock. Furthermore, there is no - // repeat or release. - if (pressed) - { - if (m_key_ctrl_state!=0) - { - post_in_key_queue(0xe0); - post_in_key_queue(0x46); - post_in_key_queue(0xe0); - post_in_key_queue(0xc6); - } - else - { - post_in_key_queue(0xe1); - post_in_key_queue(0x1d); - post_in_key_queue(0x45); - post_in_key_queue(0xe1); - post_in_key_queue(0x9d); - post_in_key_queue(0xc5); - } - } - } - else - { - if (!pressed) keycode |= 0x80; - post_in_key_queue(keycode); - } - signal_when_key_available(); - } - } - } - } - - /* Handle auto-repeat */ - if ((m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)) && (m_key_autorepeat_key!=0) && (--m_key_autorepeat_timer == 0)) - { - if ((m_key_autorepeat_key >= 0x60) && (m_key_autorepeat_key < 0x6e)) - { - post_in_key_queue(0xe0); - post_in_key_queue(MF1_CODE[m_key_autorepeat_key-0x60]); - } - else if (m_key_autorepeat_key == 0x6e) - { - if (m_key_alt_state!=0) - post_in_key_queue(0x54); - else - { - post_in_key_queue(0xe0); - post_in_key_queue(0x37); - } - } - else if (m_key_autorepeat_key == 0x6f) - ; - else - { - post_in_key_queue(m_key_autorepeat_key); - } - signal_when_key_available(); - m_key_autorepeat_timer = KEYAUTOREPEATRATE; - } -} - -uint8_t geneve_keyboard_device::get_recent_key() -{ - if (m_key_in_buffer) return m_key_queue[m_key_queue_head]; - else return 0; -} - -void geneve_keyboard_device::signal_when_key_available() -{ - // if keyboard reset is not asserted, and key clock is enabled, and key - // buffer clear is disabled, and key queue is not empty. */ - if ((!m_key_reset) && (m_keyboard_clock) && (m_keep_keybuf) && (m_key_queue_length != 0)) - { - if (TRACE_KEYBOARD) logerror("Signalling key available\n"); - m_interrupt(ASSERT_LINE); - m_key_in_buffer = true; - } -} - -WRITE_LINE_MEMBER( geneve_keyboard_device::clock_control ) -{ - bool rising_edge = (!m_keyboard_clock && (state==ASSERT_LINE)); - m_keyboard_clock = (state==ASSERT_LINE); - if (TRACE_KEYBOARD) logerror("Keyboard clock_control state=%d\n", m_keyboard_clock); - if (rising_edge) - signal_when_key_available(); -} - -WRITE_LINE_MEMBER( geneve_keyboard_device::send_scancodes ) -{ - bool rising_edge = (!m_keep_keybuf && (state==ASSERT_LINE)); - bool falling_edge = (m_keep_keybuf && (state==CLEAR_LINE)); - m_keep_keybuf = (state==ASSERT_LINE); - - if (rising_edge) signal_when_key_available(); - else - { - if (falling_edge) - { - if (m_key_queue_length != 0) - { - m_key_queue_head = (m_key_queue_head + 1) % KEYQUEUESIZE; - m_key_queue_length--; - } - /* clear keyboard interrupt */ - m_interrupt(CLEAR_LINE); - m_key_in_buffer = false; - } - } -} - -WRITE_LINE_MEMBER( geneve_keyboard_device::reset_line ) -{ - m_key_reset = !(state==ASSERT_LINE); - - if (m_key_reset) - { - /* reset -> clear keyboard key queue, but not geneve key buffer */ - m_key_queue_length = (m_key_in_buffer)? 1 : 0; - m_key_queue_head = 0; - memset(m_key_state_save, 0, sizeof(m_key_state_save)); - m_key_numlock_state = false; - m_key_ctrl_state = 0; - m_key_alt_state = 0; - m_key_real_shift_state = 0; - m_key_fake_shift_state = false; - m_key_fake_unshift_state = false; - m_key_autorepeat_key = 0; - } -} - -void geneve_keyboard_device::device_start() -{ - m_timer = timer_alloc(0); - m_interrupt.resolve(); - - // State registration - save_item(NAME(m_key_reset)); - save_item(NAME(m_key_queue_length)); - save_item(NAME(m_key_queue_head)); - save_item(NAME(m_key_in_buffer)); - save_item(NAME(m_key_numlock_state)); - save_item(NAME(m_key_ctrl_state)); - save_item(NAME(m_key_alt_state)); - save_item(NAME(m_key_real_shift_state)); - save_item(NAME(m_key_fake_shift_state)); - save_item(NAME(m_key_fake_unshift_state)); - save_item(NAME(m_key_autorepeat_key)); - save_item(NAME(m_key_autorepeat_timer)); - save_item(NAME(m_keep_keybuf)); - save_item(NAME(m_keyboard_clock)); - save_pointer(NAME(m_key_queue),KEYQUEUESIZE); - save_pointer(NAME(m_key_state_save),4); -} - -void geneve_keyboard_device::device_reset() -{ - m_key_in_buffer = false; - reset_line(CLEAR_LINE); - m_key_queue_length = 0; - m_key_reset = true; - m_keyboard_clock = false; - m_keep_keybuf = false; - m_timer->adjust(attotime::from_usec(1), 0, attotime::from_hz(120)); -} - -INPUT_PORTS_START( genkeys ) - PORT_START("KEY0") /* IN3 */ - PORT_BIT ( 0x0001, 0x0000, IPT_UNUSED ) /* unused scancode 0 */ - PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) /* Esc 01 81 */ - PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) /* 1 02 82 */ - PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) /* 2 03 83 */ - PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) /* 3 04 84 */ - PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) /* 4 05 85 */ - PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) /* 5 06 86 */ - PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) /* 6 07 87 */ - PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) /* 7 08 88 */ - PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) /* 8 09 89 */ - PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) /* 9 0A 8A */ - PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) /* 0 0B 8B */ - PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) /* - 0C 8C */ - PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) /* = 0D 8D */ - PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) /* Backspace 0E 8E */ - PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) /* Tab 0F 8F */ - - PORT_START("KEY1") /* IN4 */ - PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) /* Q 10 90 */ - PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) /* W 11 91 */ - PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) /* E 12 92 */ - PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) /* R 13 93 */ - PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) /* T 14 94 */ - PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) /* Y 15 95 */ - PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) /* U 16 96 */ - PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) /* I 17 97 */ - PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) /* O 18 98 */ - PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) /* P 19 99 */ - PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) /* [ 1A 9A */ - PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) /* ] 1B 9B */ - PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) /* Enter 1C 9C */ - PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L-Ctrl") PORT_CODE(KEYCODE_LCONTROL) /* Left Ctrl 1D 9D */ - PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) /* A 1E 9E */ - PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) /* S 1F 9F */ - - PORT_START("KEY2") /* IN5 */ - PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) /* D 20 A0 */ - PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) /* F 21 A1 */ - PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) /* G 22 A2 */ - PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) /* H 23 A3 */ - PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) /* J 24 A4 */ - PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) /* K 25 A5 */ - PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) /* L 26 A6 */ - PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) /* ; 27 A7 */ - PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("' \"") PORT_CODE(KEYCODE_QUOTE) /* ' 28 A8 */ - PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) /* ` 29 A9 */ - PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L-Shift") PORT_CODE(KEYCODE_LSHIFT) /* Left Shift 2A AA */ - PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) /* \ 2B AB */ - PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) /* Z 2C AC */ - PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) /* X 2D AD */ - PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) /* C 2E AE */ - PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) /* V 2F AF */ - - PORT_START("KEY3") /* IN6 */ - PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) /* B 30 B0 */ - PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) /* N 31 B1 */ - PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) /* M 32 B2 */ - PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) /* , 33 B3 */ - PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) /* . 34 B4 */ - PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) /* / 35 B5 */ - PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-Shift") PORT_CODE(KEYCODE_RSHIFT) /* Right Shift 36 B6 */ - PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP * (PrtScr)") PORT_CODE(KEYCODE_ASTERISK ) /* Keypad * (PrtSc) 37 B7 */ - PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT) /* Left Alt 38 B8 */ - PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) /* Space 39 B9 */ - PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) /* Caps Lock 3A BA */ - PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) /* F1 3B BB */ - PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) /* F2 3C BC */ - PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) /* F3 3D BD */ - PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) /* F4 3E BE */ - PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) /* F5 3F BF */ - - PORT_START("KEY4") /* IN7 */ - PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) /* F6 40 C0 */ - PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) /* F7 41 C1 */ - PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) /* F8 42 C2 */ - PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) /* F9 43 C3 */ - PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) /* F10 44 C4 */ - PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NumLock") PORT_CODE(KEYCODE_NUMLOCK) /* Num Lock 45 C5 */ - PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ScrLock (F14)") PORT_CODE(KEYCODE_SCRLOCK) /* Scroll Lock 46 C6 */ - PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 7 (Home)") PORT_CODE(KEYCODE_7_PAD ) /* Keypad 7 (Home) 47 C7 */ - PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 8 (Up)") PORT_CODE(KEYCODE_8_PAD ) /* Keypad 8 (Up arrow) 48 C8 */ - PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 9 (PgUp)") PORT_CODE(KEYCODE_9_PAD ) /* Keypad 9 (PgUp) 49 C9 */ - PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP -") PORT_CODE(KEYCODE_MINUS_PAD) /* Keypad - 4A CA */ - PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 4 (Left)") PORT_CODE(KEYCODE_4_PAD ) /* Keypad 4 (Left arrow) 4B CB */ - PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 5") PORT_CODE(KEYCODE_5_PAD) /* Keypad 5 4C CC */ - PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 6 (Right)") PORT_CODE(KEYCODE_6_PAD ) /* Keypad 6 (Right arrow) 4D CD */ - PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP +") PORT_CODE(KEYCODE_PLUS_PAD) /* Keypad + 4E CE */ - PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 1 (End)") PORT_CODE(KEYCODE_1_PAD ) /* Keypad 1 (End) 4F CF */ - - PORT_START("KEY5") /* IN8 */ - PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 2 (Down)") PORT_CODE(KEYCODE_2_PAD ) /* Keypad 2 (Down arrow) 50 D0 */ - PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 3 (PgDn)") PORT_CODE(KEYCODE_3_PAD ) /* Keypad 3 (PgDn) 51 D1 */ - PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 0 (Ins)") PORT_CODE(KEYCODE_0_PAD ) /* Keypad 0 (Ins) 52 D2 */ - PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP . (Del)") PORT_CODE(KEYCODE_DEL_PAD ) /* Keypad . (Del) 53 D3 */ - PORT_BIT ( 0x0030, 0x0000, IPT_UNUSED ) - PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(84/102)\\") PORT_CODE(KEYCODE_BACKSLASH2) /* Backslash 2 56 D6 */ - PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)F11") PORT_CODE(KEYCODE_F11) /* F11 57 D7 */ - PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)F12") PORT_CODE(KEYCODE_F12) /* F12 58 D8 */ - PORT_BIT ( 0xfe00, 0x0000, IPT_UNUSED ) - - PORT_START("KEY6") /* IN9 */ - PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)KP Enter") PORT_CODE(KEYCODE_ENTER_PAD) /* PAD Enter 60 e0 */ - PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)R-Control") PORT_CODE(KEYCODE_RCONTROL) /* Right Control 61 e1 */ - PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)ALTGR") PORT_CODE(KEYCODE_RALT) /* ALTGR 64 e4 */ - - PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)KP /") PORT_CODE(KEYCODE_SLASH_PAD) /* PAD Slash 62 e2 */ - - PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Home") PORT_CODE(KEYCODE_HOME) /* Home 66 e6 */ - PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Up") PORT_CODE(KEYCODE_UP) /* Up 67 e7 */ - PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Page Up") PORT_CODE(KEYCODE_PGUP) /* Page Up 68 e8 */ - PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Left") PORT_CODE(KEYCODE_LEFT) /* Left 69 e9 */ - PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Right") PORT_CODE(KEYCODE_RIGHT) /* Right 6a ea */ - PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)End") PORT_CODE(KEYCODE_END) /* End 6b eb */ - PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Down") PORT_CODE(KEYCODE_DOWN) /* Down 6c ec */ - PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Page Down") PORT_CODE(KEYCODE_PGDN) /* Page Down 6d ed */ - PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Insert") PORT_CODE(KEYCODE_INSERT) /* Insert 6e ee */ - PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Delete") PORT_CODE(KEYCODE_DEL) /* Delete 6f ef */ - - PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)PrtScr (F13)") PORT_CODE(KEYCODE_PRTSCR) /* Print Screen 63 e3 */ - PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Pause (F15)") PORT_CODE(KEYCODE_PAUSE) /* Pause 65 e5 */ - - PORT_START("KEY7") /* IN10 */ - PORT_BIT ( 0xffff, 0x0000, IPT_UNUSED ) -#if 0 - PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Print Screen") PORT_CODE(KEYCODE_PRTSCR) /* Print Screen alternate 77 f7 */ - PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Win") /* Left Win 7d fd */ - PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Win") /* Right Win 7e fe */ - PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Menu") /* Menu 7f ff */ -#endif -INPUT_PORTS_END - -ioport_constructor geneve_keyboard_device::device_input_ports() const -{ - return INPUT_PORTS_NAME( genkeys ); -} - -DEFINE_DEVICE_TYPE(GENEVE_KEYBOARD, geneve_keyboard_device, "geneve_keyboard", "Geneve XT-style keyboard") diff --git a/src/devices/bus/ti99x/genboard.h b/src/devices/bus/ti99x/genboard.h deleted file mode 100644 index d175a3d2c93..00000000000 --- a/src/devices/bus/ti99x/genboard.h +++ /dev/null @@ -1,202 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/**************************************************************************** - - Geneve main board components. - See genboard.c for documentation. - - Michael Zapf - - February 2012: Rewritten as class - -*****************************************************************************/ -#ifndef MAME_BUS_TI99X_GENBOARD_H -#define MAME_BUS_TI99X_GENBOARD_H - -#pragma once - -#include "ti99defs.h" -#include "machine/mm58274c.h" -#include "video/v9938.h" -#include "cpu/tms9900/tms9995.h" -#include "machine/at29x.h" -#include "bus/ti99/peb/peribox.h" -#include "sound/sn76496.h" -#include "machine/ram.h" - -DECLARE_DEVICE_TYPE(GENEVE_KEYBOARD, geneve_keyboard_device) -DECLARE_DEVICE_TYPE(GENEVE_MAPPER, geneve_mapper_device) - -/*****************************************************************************/ - -class geneve_keyboard_device : public device_t -{ -public: - geneve_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - DECLARE_WRITE_LINE_MEMBER( reset_line ); - DECLARE_WRITE_LINE_MEMBER( send_scancodes ); - DECLARE_WRITE_LINE_MEMBER( clock_control ); - uint8_t get_recent_key(); - - template static devcb_base &static_set_int_callback(device_t &device, Object &&cb) { return downcast(device).m_interrupt.set_callback(std::forward(cb)); } - -protected: - void device_start() override; - void device_reset() override; - ioport_constructor device_input_ports() const override; - void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - - devcb_write_line m_interrupt; // Keyboard interrupt to console - required_ioport_array<8> m_keys; - -private: - static constexpr unsigned KEYQUEUESIZE = 256; - static constexpr unsigned MAXKEYMSGLENGTH = 10; - static constexpr unsigned KEYAUTOREPEATDELAY = 30; - static constexpr unsigned KEYAUTOREPEATRATE = 6; - - void post_in_key_queue(int keycode); - void signal_when_key_available(); - void poll(); - - bool m_key_reset; - int m_key_queue_length; - uint8_t m_key_queue[KEYQUEUESIZE]; - int m_key_queue_head; - bool m_key_in_buffer; - uint32_t m_key_state_save[4]; - bool m_key_numlock_state; - - int m_key_ctrl_state; - int m_key_alt_state; - int m_key_real_shift_state; - - bool m_key_fake_shift_state; - bool m_key_fake_unshift_state; - - int m_key_autorepeat_key; - int m_key_autorepeat_timer; - - bool m_keep_keybuf; - bool m_keyboard_clock; - - emu_timer* m_timer; -}; - -#define MCFG_GENEVE_KBINT_HANDLER( _intcallb ) \ - devcb = &geneve_keyboard_device::static_set_int_callback( *device, DEVCB_##_intcallb ); - -/*****************************************************************************/ - -class geneve_mapper_device : public device_t -{ -public: - geneve_mapper_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - void set_geneve_mode(bool geneve); - void set_direct_mode(bool direct); - void set_cartridge_size(int size); - void set_cartridge_writable(int base, bool write); - void set_video_waitstates(bool wait); - void set_extra_waitstates(bool wait); - - DECLARE_READ8_MEMBER( readm ); - DECLARE_WRITE8_MEMBER( writem ); - DECLARE_SETOFFSET_MEMBER( setoffset ); - - DECLARE_INPUT_CHANGED_MEMBER( settings_changed ); - - DECLARE_WRITE_LINE_MEMBER( clock_in ); - DECLARE_WRITE_LINE_MEMBER( dbin_in ); - - // PFM support - DECLARE_WRITE_LINE_MEMBER( pfm_select_lsb ); - DECLARE_WRITE_LINE_MEMBER( pfm_select_msb ); - DECLARE_WRITE_LINE_MEMBER( pfm_output_enable ); - - template static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) { return downcast(device).m_ready.set_callback(std::forward(cb)); } - -protected: - void device_start() override; - void device_reset() override; - -private: - // GROM simulation - bool m_gromwaddr_LSB; - bool m_gromraddr_LSB; - int m_grom_address; - DECLARE_READ8_MEMBER( read_grom ); - DECLARE_WRITE8_MEMBER( write_grom ); - - // wait states - void set_wait(int min); - void set_ext_wait(int min); - bool m_video_waitstates; - bool m_extra_waitstates; - bool m_ready_asserted; - - bool m_read_mode; - - bool m_debug_no_ws; - - // Mapper function - typedef struct - { - int function; - offs_t offset; - offs_t physaddr; - } decdata; - - bool m_geneve_mode; - bool m_direct_mode; - int m_cartridge_size; - bool m_cartridge_secondpage; - bool m_cartridge6_writable; - bool m_cartridge7_writable; - int m_map[8]; - - void decode(address_space& space, offs_t offset, bool read_mode, decdata* dec); - decdata m_decoded; - - // Genmod modifications - bool m_turbo; - bool m_genmod; - bool m_timode; - - // PFM mod (0 = none, 1 = AT29C040, 2 = AT29C040A) - DECLARE_READ8_MEMBER( read_from_pfm ); - DECLARE_WRITE8_MEMBER( write_to_pfm ); - void set_boot_rom(int selection); - int m_pfm_mode; - int m_pfm_bank; - bool m_pfm_output_enable; - - // SRAM access - int m_sram_mask; - int m_sram_val; - - // Ready line to the CPU - devcb_write_line m_ready; - - // Counter for the wait states. - int m_waitcount; - int m_ext_waitcount; - - // Devices - required_device m_clock; - required_device m_cpu; - required_device m_pfm512; - required_device m_pfm512a; - required_device m_sound; - - required_device m_keyboard; - required_device m_video; - required_device m_peribox; - uint8_t* m_eprom; - required_device m_sram; - required_device m_dram; -}; - -#define MCFG_GENEVE_READY_HANDLER( _intcallb ) \ - devcb = &geneve_mapper_device::static_set_ready_callback( *device, DEVCB_##_intcallb ); - -#endif // MAME_BUS_TI99X_GENBOARD_H diff --git a/src/devices/bus/ti99x/gromport.cpp b/src/devices/bus/ti99x/gromport.cpp deleted file mode 100644 index 61391a03862..00000000000 --- a/src/devices/bus/ti99x/gromport.cpp +++ /dev/null @@ -1,3039 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/*************************************************************************** - GROM port - the cartridge port of the TI-99/4, TI-99/4A, and - TI-99/8 console. - - The name refers to the main intended application scenario, that is, - to host cartridges with GROMs. The second, wider port of the console is - called I/O port and connects to the Peripheral Expansion System - (see peribox.h). - - LEFT - - /RESET 1||2 GND - D7 3||4 CRUCLK - D6 5||6 CRUIN - D5 7||8 A15/CRUOUT - D4 9||10 A13 - D3 11||12 A12 - D2 13||14 A11 - D1 15||16 A10 - D0 17||18 A9 - +5V 19||20 A8 - /GS 21||22 A7 - A14 23||24 A3 - DBIN 25||26 A6 - GRMCLK 27||28 A5 - -5V 29||30 A4 - READY 31||32 /WE - GND 33||34 /ROMG - GND 35||36 GND - - RIGHT - - Address bus line ordering, according to TI convention, is A0 (MSB) ... A15 (LSB). - A0, A1, and A2 are not delivered to the port but decoded in the console: - - /ROMG is asserted for A0/A1/A2 = 011 (addresses 6000 - 7fff) - /GS is asserted for A0...A5 = 100110 (addresses 9800 - 9bff) - - This means that a maximum of 8 KiB of direct memory space can be accessed. - The /GS line is used to enable GROM circuits on the board (serial ROMs with - own address counter, see tmc0430.h). - - When a cartridge is inserted the /RESET line is pulled to ground, which - via a R/C component pulls down the /RESET input of the timer circuit for - a short time, which in turn resets the CPU. In order to dump cartridges, - a common procedure was to tape the /RESET line of the cartridge. However, - inserting a cartridge without resetting often caused so much data bus noise - that the console usually locked up. - - ---------------- - - The TI-99/4A computer was strictly designed for cartridge usage. The basic - console had only little directly accessible RAM and offered no ways to - write machine language programs; cartridges were intended to add various - capabilities, or just for running games. - - Beside the seemingly simple handling, Texas Instruments had own intentions - behind their cartridge strategy. With only 8 KiB of direct access memory, a - major part of the cartridge code had to be stored in GROMs, which had to be - licensed from Texas Instruments. Thus they kept firm control over all - software development. - - Over the years, and with the increasingly difficult market situations, - TI's policies seem to have changed. This may be the reason that the built-in - operating system actually allowed for running ROM-only cartridges until TI - clipped out this part in the OS, banning cartridges without GROMs. Consoles - with this modification were produced in 1983, TI's last year in the home - computer business. - - Although only 8 KiB were available for direct addressing, clever techniques - were invented by third-party manufacturers. The first extension was utilized - by TI themselves in the Extended Basic cartridge which offers two banks - of ROM contents. Switching between the banks is achieved by writing a value - to the ROM space at 6000 or 6002. Later, cartridges with much more memory - space were created, up to the Super Space II cartridge with 128 KiB of - buffered SRAM. - - ---------------- - - From the console case layout the GROM port was intended for a single - cartridge only. Although never officially released, the operating system - of the TI console supported a multi-cartridge extender with software - switching. There were also extenders based on hardware switching (like - the Navarone Widget). - - This emulation offers both variants as slot options: - - -gromport single : default single cartridge connector - -gromport multi : software-switchable 4-slot cartridge extender - -gromport gkracker : GRAM Kracker - - The last option enables another popular device, the GRAM Kracker. This is - a device to be plugged into the cartridge slot with five manual switches - at the front and an own cartridge slot at its top. It contains buffered - SRAM, a built-in ROM, and a GROM simulator which simulates GROM behaviour - when accessing the buffered RAM. Its main use is to provide editable - storage for the read-only cartridge contents. Cartridges can be plugged - into its slot; their contents can be read and written to disk, modified as - needed, and loaded into the buffered RAM. Even the console GROMs can be - copied into the device; despite running in parallel, the GROM simulator - is able to override the console GROMs, thus allowing the user to install - a customized OS. - -***************************************************************************/ -#include "emu.h" -#include "gromport.h" -#include "emuopts.h" -#include "image.h" -#include "softlist.h" - -#define TRACE_RPK 0 -#define TRACE_CHANGE 0 -#define TRACE_ILLWRITE 1 -#define TRACE_CONFIG 0 -#define TRACE_READ 0 -#define TRACE_WRITE 0 -#define TRACE_GROM 0 -#define TRACE_GKRACKER 0 -#define TRACE_CRU 0 -#define TRACE_BANKSWITCH 0 - -#define GROM3_TAG "grom3" -#define GROM4_TAG "grom4" -#define GROM5_TAG "grom5" -#define GROM6_TAG "grom6" -#define GROM7_TAG "grom7" - -#define CARTGROM_TAG "grom_contents" -#define CARTROM_TAG "rom_contents" -#define GKRACKER_ROM_TAG "gkracker_rom" -#define GKRACKER_NVRAM_TAG "gkracker_nvram" - -namespace { - -enum rpk_open_error -{ - RPK_OK, - RPK_NOT_ZIP_FORMAT, - RPK_CORRUPT, - RPK_OUT_OF_MEMORY, - RPK_XML_ERROR, - RPK_INVALID_FILE_REF, - RPK_ZIP_ERROR, - RPK_ZIP_UNSUPPORTED, - RPK_MISSING_RAM_LENGTH, - RPK_INVALID_RAM_SPEC, - RPK_UNKNOWN_RESOURCE_TYPE, - RPK_INVALID_RESOURCE_REF, - RPK_INVALID_LAYOUT, - RPK_MISSING_LAYOUT, - RPK_NO_PCB_OR_RESOURCES, - RPK_UNKNOWN_PCB_TYPE -}; - - -const char *const error_text[16] = -{ - "No error", - "Not a RPK (zip) file", - "Module definition corrupt", - "Out of memory", - "XML format error", - "Invalid file reference", - "Zip file error", - "Unsupported zip version", - "Missing RAM length", - "Invalid RAM specification", - "Unknown resource type", - "Invalid resource reference", - "layout.xml not valid", - "Missing layout", - "No pcb or resource found", - "Unknown pcb type" -}; - - -class rpk_exception -{ -public: - rpk_exception(rpk_open_error value): m_err(value), m_detail(nullptr) { } - rpk_exception(rpk_open_error value, const char* detail) : m_err(value), m_detail(detail) { } - - const char* to_string() - { - // FIXME: this leaks memory - in some cases it returns a new buffer, in other cases it returns a pointer to a static string, so the caller can't know whether it needs to be cleaned up - if (m_detail==nullptr) return error_text[(int)m_err]; - std::string errormsg = std::string(error_text[(int)m_err]).append(": ").append(m_detail); - return core_strdup(errormsg.c_str()); - } - -private: - rpk_open_error m_err; - const char* m_detail; -}; - -struct pcb_type -{ - int id; - const char* name; -}; - -} // anonymous namespace - - -/************************************************************************* - RPK support -*************************************************************************/ - -class ti99_cartridge_device::rpk_socket -{ -public: - rpk_socket(const char *id, int length, uint8_t *contents); - rpk_socket(const char *id, int length, uint8_t *contents, const char *pathname); - ~rpk_socket() {} - - const char* id() { return m_id; } - int get_content_length() { return m_length; } - uint8_t* get_contents() { return m_contents; } - bool persistent_ram() { return m_pathname != nullptr; } - const char* get_pathname() { return m_pathname; } - void cleanup() { if (m_contents != nullptr) global_free_array(m_contents); } - -private: - const char* m_id; - uint32_t m_length; - uint8_t* m_contents; - const char* m_pathname; -}; - -class ti99_cartridge_device::rpk_reader -{ -public: - rpk_reader(const pcb_type *types) : m_types(types) { } - - rpk *open(emu_options &options, const char *filename, const char *system_name); - -private: - int find_file(util::archive_file &zip, const char *filename, uint32_t crc); - std::unique_ptr load_rom_resource(util::archive_file &zip, util::xml::data_node const* rom_resource_node, const char* socketname); - std::unique_ptr load_ram_resource(emu_options &options, util::xml::data_node const* ram_resource_node, const char* socketname, const char* system_name); - const pcb_type* m_types; -}; - -class ti99_cartridge_device::rpk -{ - friend class rpk_reader; -public: - rpk(emu_options& options, const char* sysname); - ~rpk(); - - int get_type(void) { return m_type; } - uint8_t* get_contents_of_socket(const char *socket_name); - int get_resource_length(const char *socket_name); - void close(); - -private: - emu_options& m_options; // need this to find the path to the nvram files - int m_type; - //const char* m_system_name; // need this to find the path to the nvram files - std::unordered_map> m_sockets; - - void add_socket(const char* id, std::unique_ptr newsock); -}; - -ti99_gromport_device::ti99_gromport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : bus8z_device(mconfig, TI99_GROMPORT, tag, owner, clock), - device_slot_interface(mconfig, *this), - m_connector(nullptr), - m_reset_on_insert(true), - m_console_ready(*this), - m_console_reset(*this) -{ } - -/* - Reading via the GROM port. Only 13 address lines are passed through - on the TI-99/4A, and 14 lines on the TI-99/8. -*/ -READ8Z_MEMBER(ti99_gromport_device::readz) -{ - if (m_connector != nullptr) - { - m_connector->readz(space, offset & m_mask, value); - if (TRACE_READ) if (m_romgq) logerror("Read %04x -> %02x\n", offset | 0x6000, *value); - } -} - -/* - Writing via the GROM port. Only 13 address lines are passed through - on the TI-99/4A, and 14 lines on the TI-99/8. -*/ -WRITE8_MEMBER(ti99_gromport_device::write) -{ - if (m_connector != nullptr) - { - if (TRACE_WRITE) if (m_romgq) logerror("Write %04x <- %02x\n", offset | 0x6000, data); - m_connector->write(space, offset & m_mask, data); - } -} - -READ8Z_MEMBER(ti99_gromport_device::crureadz) -{ - if (m_connector != nullptr) - m_connector->crureadz(space, offset, value); -} - -WRITE8_MEMBER(ti99_gromport_device::cruwrite) -{ - if (m_connector != nullptr) - m_connector->cruwrite(space, offset, data); -} - -WRITE_LINE_MEMBER(ti99_gromport_device::ready_line) -{ - m_console_ready(state); -} - -/* - Asserted when the console addresses cartridge rom. -*/ -WRITE_LINE_MEMBER(ti99_gromport_device::romgq_line) -{ - m_romgq = state; - if (m_connector != nullptr) - m_connector->romgq_line(state); -} - -WRITE_LINE_MEMBER(ti99_gromport_device::gclock_in) -{ - if (m_connector != nullptr) - m_connector->gclock_in(state); -} - -/* - Combined GROM control lines. -*/ -WRITE8_MEMBER( ti99_gromport_device::set_gromlines ) -{ - if (m_connector != nullptr) - m_connector->set_gromlines(space, offset, data); -} - -void ti99_gromport_device::device_start() -{ - m_console_ready.resolve(); - m_console_reset.resolve(); - - save_item(NAME(m_romgq)); -} - -void ti99_gromport_device::device_reset() -{ - m_reset_on_insert = (ioport("CARTRESET")->read()==0x01); -} - -/* - Shall we reset the console when a cartridge has been inserted? - This is triggered by the cartridge by pulling down /RESET via a capacitor. - Accordingly, when we put a tape over the /RESET contact we can avoid the - reset, which is useful when we want to swap the cartridges while a program - is runnning. -*/ -void ti99_gromport_device::cartridge_inserted() -{ - if (m_reset_on_insert) - { - m_console_reset(ASSERT_LINE); - m_console_reset(CLEAR_LINE); - } -} - -/* - Find out whether the GROMs in the cartridge are idle. In that case, - cut the clock line. -*/ -bool ti99_gromport_device::is_grom_idle() -{ - if (m_connector != nullptr) - return m_connector->is_grom_idle(); - else - return false; -} - -void ti99_gromport_device::device_config_complete() -{ - m_connector = downcast(subdevices().first()); -} - -SLOT_INTERFACE_START( gromport4 ) - SLOT_INTERFACE("single", TI99_GROMPORT_SINGLE) - SLOT_INTERFACE("multi", TI99_GROMPORT_MULTI) - SLOT_INTERFACE("gkracker", TI99_GROMPORT_GK) -SLOT_INTERFACE_END - -SLOT_INTERFACE_START( gromport8 ) - SLOT_INTERFACE("single", TI99_GROMPORT_SINGLE) - SLOT_INTERFACE("multi", TI99_GROMPORT_MULTI) -SLOT_INTERFACE_END - -INPUT_PORTS_START(gromport) - PORT_START( "CARTRESET" ) - PORT_CONFNAME( 0x01, 0x01, "RESET on cartridge insert" ) - PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) - PORT_CONFSETTING( 0x01, DEF_STR( On ) ) -INPUT_PORTS_END - -ioport_constructor ti99_gromport_device::device_input_ports() const -{ - return INPUT_PORTS_NAME(gromport); -} - -DEFINE_DEVICE_TYPE(TI99_GROMPORT, ti99_gromport_device, "ti99_gromport", "TI-99 Cartridge port") - -/*************************************************************************** - Different versions of cartridge connections - - single: the standard console connector, one cartridge - multi: a multi-cart expander, up to 4 cartridges with software selection - gkracker: GRAMKracker, a device with NVRAM which allows the user to copy - the contents of the cartridge plugged into its slot into the NVRAM - and to modify it. - -***************************************************************************/ - -DEFINE_DEVICE_TYPE(TI99_GROMPORT_SINGLE, ti99_single_cart_conn_device, "ti99_scartconn", "TI-99 Standard cartridge connector") -DEFINE_DEVICE_TYPE(TI99_GROMPORT_MULTI, ti99_multi_cart_conn_device, "ti99_mcartconn", "TI-99 Multi-cartridge extender") -DEFINE_DEVICE_TYPE(TI99_GROMPORT_GK, ti99_gkracker_device, "ti99_gkracker", "Miller's Graphics GRAM Kracker") - -ti99_cartridge_connector_device::ti99_cartridge_connector_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : bus8z_device(mconfig, type, tag, owner, clock), - m_gromport(nullptr) -{ -} - -WRITE_LINE_MEMBER( ti99_cartridge_connector_device::ready_line ) -{ - m_gromport->ready_line(state); -} - -void ti99_cartridge_connector_device::device_config_complete() -{ - m_gromport = static_cast(owner()); -} - -ti99_single_cart_conn_device::ti99_single_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : ti99_cartridge_connector_device(mconfig, TI99_GROMPORT_SINGLE, tag, owner, clock), - m_cartridge(nullptr) -{ -} - -READ8Z_MEMBER(ti99_single_cart_conn_device::readz) -{ - // Pass through - m_cartridge->readz(space, offset, value); -} - -WRITE8_MEMBER(ti99_single_cart_conn_device::write) -{ - // Pass through - m_cartridge->write(space, offset, data); -} - -READ8Z_MEMBER(ti99_single_cart_conn_device::crureadz) -{ - // Pass through - m_cartridge->crureadz(space, offset, value); -} - -WRITE8_MEMBER(ti99_single_cart_conn_device::cruwrite) -{ - // Pass through - m_cartridge->cruwrite(space, offset, data); -} - -WRITE_LINE_MEMBER(ti99_single_cart_conn_device::romgq_line) -{ - // Pass through - m_cartridge->romgq_line(state); -} - -/* - Combined select lines -*/ -WRITE8_MEMBER(ti99_single_cart_conn_device::set_gromlines) -{ - // Pass through - m_cartridge->set_gromlines(space, offset, data); -} - - -WRITE_LINE_MEMBER(ti99_single_cart_conn_device::gclock_in) -{ - // Pass through - m_cartridge->gclock_in(state); -} - -/* - Check whether the GROMs are idle. -*/ -bool ti99_single_cart_conn_device::is_grom_idle() -{ - return m_cartridge->is_grom_idle(); -} - -void ti99_single_cart_conn_device::device_start() -{ - m_cartridge = static_cast(subdevices().first()); -} - -void ti99_single_cart_conn_device::device_reset() -{ - m_cartridge->set_slot(0); -} - -static MACHINE_CONFIG_START( single_slot ) - MCFG_DEVICE_ADD("cartridge", TI99CART, 0) -MACHINE_CONFIG_END - -machine_config_constructor ti99_single_cart_conn_device::device_mconfig_additions() const -{ - return MACHINE_CONFIG_NAME( single_slot ); -} - -/********************************************************/ - -/* - The multi-cartridge extender - - This is a somewhat mythical device which was never available for the normal - customer, but there are reports of the existence of such a device - in development labs or demonstrations. - - The interesting thing about this is that the OS of the console - fully supports this multi-cartridge extender, providing a selection - option on the screen to switch between different plugged-in - cartridges. - - The switching is possible by decoding address lines that are reserved - for GROM access. GROMs are accessed via four separate addresses - 9800, 9802, 9C00, 9C02. The addressing scheme looks like this: - - 1001 1Wxx xxxx xxM0 W = write(1), read(0), M = address(1), data(0) - - This leaves 8 bits (256 options) which are not decoded inside the - console. As the complete address is routed to the port, some circuit - just needs to decode the xxx lines and turn on the respective slot. - - One catch must be considered: Some cartridges contain ROMs which are - directly accessed and not via ports. This means that the ROMs must - be activated according to the slot that is selected. - - Another issue: Each GROM contains an own address counter and an ID. - According to the ID the GROM only delivers data if the address counter - is within the ID area (0 = 0000-1fff, 1=2000-3fff ... 7=e000-ffff). - Thus it is essential that all GROMs stay in sync with their address - counters. We have to route all address settings to all slots and their - GROMs, even when the slot has not been selected before. The selected - just shows its effect when data is read. In this case, only the - data from the selected slot will be delivered. - - This may be considered as a design flaw within the complete cartridge system - which eventually led to TI not manufacturing that device for the broad - market. -*/ - -#define AUTO -1 - -ti99_multi_cart_conn_device::ti99_multi_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : ti99_cartridge_connector_device(mconfig, TI99_GROMPORT_MULTI, tag, owner, clock), - m_active_slot(0), - m_fixed_slot(0), - m_next_free_slot(0) -{ -} - -/* - Activates a slot in the multi-cartridge extender. - Setting the slot is done by accessing the GROM ports using a - specific address: - - slot 0: 0x9800 (0x9802, 0x9c00, 0x9c02) : cartridge1 - slot 1: 0x9804 (0x9806, 0x9c04, 0x9c06) : cartridge2 - ... - slot 15: 0x983c (0x983e, 0x9c3c, 0x9c3e) : cartridge16 - - Scheme: - - 1001 1Wxx xxxx xxM0 (M=mode; M=0: data, M=1: address; W=write) - - The following addresses are theoretically available, but the - built-in OS does not use them; i.e. cartridges will not be - included in the selection list, and their features will not be - found by lookup, but they could be accessed directly by user - programs. - slot 16: 0x9840 (0x9842, 0x9c40, 0x9c42) - ... - slot 255: 0x9bfc (0x9bfe, 0x9ffc, 0x9ffe) - - Setting the GROM base should select one cartridge, but the ROMs in the - CPU space must also be switched. As there is no known special mechanism - we assume that by switching the GROM base, the ROM is automatically - switched. - - Caution: This means that cartridges which do not have at least one - GROM cannot be switched with this mechanism. - - We assume that the slot number is already calculated in the caller: - slotnumber>=0 && slotnumber<=255 - - NOTE: The OS will stop searching when it finds slots 1 and 2 empty. - Interestingly, cartridge subroutines are found nevertheless, even when - the cartridge is plugged into a higher slot. -*/ -void ti99_multi_cart_conn_device::set_slot(int slotnumber) -{ - if (TRACE_CHANGE) - if (m_active_slot != slotnumber) logerror("Setting cartslot to %d\n", slotnumber); - - if (m_fixed_slot==AUTO) - m_active_slot = slotnumber; - else - m_active_slot = m_fixed_slot; -} - -int ti99_multi_cart_conn_device::get_active_slot(bool changebase, offs_t offset) -{ - int slot; - if (changebase) - { - // GROM selected? - if (m_grom_selected) set_slot((offset>>2) & 0x00ff); - } - slot = m_active_slot; - return slot; -} - -void ti99_multi_cart_conn_device::insert(int index, ti99_cartridge_device* cart) -{ - if (TRACE_CHANGE) logerror("Insert slot %d\n", index); - m_cartridge[index] = cart; - m_gromport->cartridge_inserted(); -} - -void ti99_multi_cart_conn_device::remove(int index) -{ - if (TRACE_CHANGE) logerror("Remove slot %d\n", index); - m_cartridge[index] = nullptr; -} - -WRITE_LINE_MEMBER(ti99_multi_cart_conn_device::romgq_line) -{ - m_readrom = state; - - // Propagate to all slots - for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) - { - if (m_cartridge[i] != nullptr) - { - m_cartridge[i]->romgq_line(state); - } - } -} - -/* - Combined select lines -*/ -WRITE8_MEMBER(ti99_multi_cart_conn_device::set_gromlines) -{ - // GROM selected? - m_grom_selected = (data != 0); - - // Propagate to all slots - for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) - { - if (m_cartridge[i] != nullptr) - { - m_cartridge[i]->set_gromlines(space, offset, data); - } - } -} - -WRITE_LINE_MEMBER(ti99_multi_cart_conn_device::gclock_in) -{ - // Propagate to all slots - for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) - { - if (m_cartridge[i] != nullptr) - { - m_cartridge[i]->gclock_in(state); - } - } -} - -READ8Z_MEMBER(ti99_multi_cart_conn_device::readz) -{ - int slot = get_active_slot(true, offset); - - // If we have a GROM access, we need to send the read request to all - // attached cartridges so the slot is irrelevant here. Each GROM - // contains an internal address counter, and we must make sure they all stay in sync. - if (m_grom_selected) - { - for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) - { - if (m_cartridge[i] != nullptr) - { - uint8_t newval = *value; - m_cartridge[i]->readz(space, offset, &newval, 0xff); - if (i==slot) - { - *value = newval; - } - } - } - } - else - { - if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr) - { - m_cartridge[slot]->readz(space, offset, value, 0xff); - } - } -} - -WRITE8_MEMBER(ti99_multi_cart_conn_device::write) -{ - // Same issue as above (read) - // We don't have GRAM cartridges, anyway, so it's just used for setting the address. - if (m_grom_selected) - { - for (auto & elem : m_cartridge) - { - if (elem != nullptr) - { - elem->write(space, offset, data, 0xff); - } - } - } - else - { - int slot = get_active_slot(true, offset); - if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr) - { - // logerror("writing %04x (slot %d) <- %02x\n", offset, slot, data); - m_cartridge[slot]->write(space, offset, data, 0xff); - } - } -} - -READ8Z_MEMBER(ti99_multi_cart_conn_device::crureadz) -{ - int slot = get_active_slot(false, offset); - /* Sanity check. Higher slots are always empty. */ - if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) - return; - - if (m_cartridge[slot] != nullptr) - { - m_cartridge[slot]->crureadz(space, offset, value); - } -} - -WRITE8_MEMBER(ti99_multi_cart_conn_device::cruwrite) -{ - int slot = get_active_slot(true, offset); - - /* Sanity check. Higher slots are always empty. */ - if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) - return; - - if (m_cartridge[slot] != nullptr) - { - m_cartridge[slot]->cruwrite(space, offset, data); - } -} - -/* - Check whether the GROMs are idle. Just ask the currently - active cartridge. -*/ -bool ti99_multi_cart_conn_device::is_grom_idle() -{ - /* Sanity check. Higher slots are always empty. */ - if (m_active_slot >= NUMBER_OF_CARTRIDGE_SLOTS) - return false; - - if (m_cartridge[m_active_slot] != nullptr) - return m_cartridge[m_active_slot]->is_grom_idle(); - - return false; -} - -void ti99_multi_cart_conn_device::device_start() -{ - m_next_free_slot = 0; - m_active_slot = 0; - for (auto & elem : m_cartridge) - { - elem = nullptr; - } - save_item(NAME(m_readrom)); - save_item(NAME(m_active_slot)); - save_item(NAME(m_fixed_slot)); - save_item(NAME(m_next_free_slot)); -} - -void ti99_multi_cart_conn_device::device_reset(void) -{ - m_active_slot = 0; - m_fixed_slot = ioport("CARTSLOT")->read() - 1; - m_grom_selected = false; -} - -static MACHINE_CONFIG_START( multi_slot ) - MCFG_DEVICE_ADD("cartridge1", TI99CART, 0) - MCFG_DEVICE_ADD("cartridge2", TI99CART, 0) - MCFG_DEVICE_ADD("cartridge3", TI99CART, 0) - MCFG_DEVICE_ADD("cartridge4", TI99CART, 0) -MACHINE_CONFIG_END - -machine_config_constructor ti99_multi_cart_conn_device::device_mconfig_additions() const -{ - return MACHINE_CONFIG_NAME( multi_slot ); -} - -INPUT_CHANGED_MEMBER( ti99_multi_cart_conn_device::switch_changed ) -{ - if (TRACE_CHANGE) logerror("Slot changed %d - %d\n", (int)((uint64_t)param & 0x07), newval); - m_active_slot = m_fixed_slot = newval - 1; -} - -INPUT_PORTS_START(multi_slot) - PORT_START( "CARTSLOT" ) - PORT_DIPNAME( 0x0f, 0x00, "Multi-cartridge slot" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_multi_cart_conn_device, switch_changed, 0) - PORT_DIPSETTING( 0x00, "Auto" ) - PORT_DIPSETTING( 0x01, "Slot 1" ) - PORT_DIPSETTING( 0x02, "Slot 2" ) - PORT_DIPSETTING( 0x03, "Slot 3" ) - PORT_DIPSETTING( 0x04, "Slot 4" ) -INPUT_PORTS_END - -ioport_constructor ti99_multi_cart_conn_device::device_input_ports() const -{ - return INPUT_PORTS_NAME(multi_slot); -} - -/************************************************************************** - - The GRAM Kracker was manufactured by Miller's Graphics and designed to - fit into the cartridge slot. - - It offers one own cartridge slot at the top side and a row of switches - at its front. It contains buffered SRAM circuits; the base version has - 56 KiB, and the extended version has 80 KiB. - - The operation of the GRAM Kracker is a bit complex and most likely - bound to fail when you have no manual. Accordingly, this emulation is - neither simpler nor more difficult to use. - - Concept of operation: - - Loader: The GRAM Kracker contains a small loader utility - which allows you to dump cartridges and to load the contents into the - SRAM of the GK. This loader utility is active when the switch 5 is put - into "Loader on" state. The activated loader hides the TI BASIC - interpreter in the console. - - Cartridges: When a cartridge is plugged into the GK the contents may be - dumped and saved to disk by the loader. They cannot be directly copied - into the GK because the memory locations are hidden by the cartridge. - - Loading the cartridge into the SRAM: With the cartridge unplugged, dumps - can be loaded into the SRAM using the loader. This is one major use case - of the GK, that is, to load dumps from disk, and in particular modified - dumps. (There is no checksum, so contents may be freely changed.) - - Console dump: The GK is also able to dump the console GROMs and also to - load them into the SRAM (only in the extended version). Due to a - peculiarity of the TI console design it is possible to override the - console GROMs with the contents in the cartridge slot. - - A standard procedure for use with the GK: - - Save cartridge: - - - Put switches to [Normal | OpSys | TI BASIC | W/P | Loader On] - - Insert a disk image into disk drive 1 - - Plug in a cartridge - - Reset the console (done automatically here) - - Visit the option screen, press 1 for GRAM KRACKER - - In the GK loader, select 2 for Save Module - - Follow the on-screen instructions. Switches are set via the dip switch menu. - - Enter a target file name - - Saving is complete when the Save operation has been unmarked. - - Load cartridge: - - - Put switches to [Normal | OpSys | TI BASIC | W/P | Loader On] - - Insert a disk image into disk drive 1 - - Make sure no cartridge is plugged in - - Press 1 for GRAM KRACKER - - Press 3 for Init Module space; follow instructions - - Press 1 for Load Module; specify file name on disk - - Loading is complete when the Load operation has been unmarked. - - Memory organisation: - - The console has three GROMs with 6 KiB size and occupying 8 KiB of address - space each. These are called GROMs 0, 1, and 2. GROM 0 contains the common - routines for the computer operation; GROMs 1 and 2 contain TI BASIC. - - Memory locations 6000-7fff are assigned to cartridge ROMs; in some - cartridges, a second ROM bank can be used by writing a value to a special - ROM access. This way, instead of 8 KiB we often have 16 KiB at these - locations. - - Each cartridge can host up to 5 GROMs (called GROM 3, 4, 5, 6, and 7). - As in the console, each one occupies 6 KiB in an 8 KiB window. - - The GRAM Kracker offers - - - a loader in an own GROM 1 (which hides the console GROM 1 when active, - so we have no BASIC anymore). The contents of the loader must be found - by the emulator in a file named ti99_gkracker.zip. - - - a complete set of 8 (simulated) GRAMs with full 8 KiB each (done by a - simple addressing circuit); the basic version only offered GRAMs 3-7 - - - 16 KiB of RAM memory space for the 6000-7fff area (called "bank 1" and "bank 2") - - Notes: - - - it is mandatory to turn off the loader when loading into GRAM 1, but only - after prompted in the on-screen instructions, or the loader will crash - - GRAM0 must be properly loaded if switch 2 is set to GRAM0 and the computer is reset - - Switch 4 must not be in W/P position (write protect) when loading data - into the GK (either other position will do). - - -***************************************************************************/ -enum -{ - GK_OFF = 0, - GK_NORMAL = 1, - GK_GRAM0 = 0, - GK_OPSYS = 1, - GK_GRAM12 = 0, - GK_TIBASIC = 1, - GK_BANK1 = 0, - GK_WP = 1, - GK_BANK2 = 2, - GK_LDON = 0, - GK_LDOFF = 1 -}; - -#define GKSWITCH1_TAG "GKSWITCH1" -#define GKSWITCH2_TAG "GKSWITCH2" -#define GKSWITCH3_TAG "GKSWITCH3" -#define GKSWITCH4_TAG "GKSWITCH4" -#define GKSWITCH5_TAG "GKSWITCH5" - -ti99_gkracker_device::ti99_gkracker_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : ti99_cartridge_connector_device(mconfig, TI99_GROMPORT_GK, tag, owner, clock), - device_nvram_interface(mconfig, *this), - m_romspace_selected(false), - m_ram_page(0), - m_grom_address(0), - m_ram_ptr(nullptr), - m_grom_ptr(nullptr), - m_waddr_LSB(false), - m_cartridge(nullptr) -{ -} - -WRITE_LINE_MEMBER(ti99_gkracker_device::romgq_line) -{ - m_romspace_selected = (state==ASSERT_LINE); - // Propagate to the guest - if (m_cartridge != nullptr) m_cartridge->romgq_line(state); -} - -/* - Combined select lines -*/ -WRITE8_MEMBER(ti99_gkracker_device::set_gromlines) -{ - m_grom_selected = (data != 0); - if (m_cartridge != nullptr) m_cartridge->set_gromlines(space, offset, data); -} - -WRITE_LINE_MEMBER(ti99_gkracker_device::gclock_in) -{ - if (m_cartridge != nullptr) m_cartridge->gclock_in(state); -} - -/* - Check whether the GROMs are idle. -*/ -bool ti99_gkracker_device::is_grom_idle() -{ - return (m_cartridge != nullptr)? m_cartridge->is_grom_idle() : false; -} - -READ8Z_MEMBER(ti99_gkracker_device::readz) -{ - if (m_grom_selected) - { - // Reads from the GRAM space of the GRAM Kracker. - int id = ((m_grom_address & 0xe000)>>13)&0x07; - - // The GK does not have a readable address counter, but the console - // GROMs and the GROMs of the guest cartridge will keep our address - // counter up to date. - if ((offset & 0x0002)==0) - { - // Reading data - if (((id==0) && (m_gk_switch[2]==GK_GRAM0)) - || ((id==1) && (m_gk_switch[5]==GK_LDOFF) && (m_gk_switch[3]==GK_GRAM12)) - || ((id==2) && (m_gk_switch[3]==GK_GRAM12)) - || ((id>=3) && (m_gk_switch[1]==GK_NORMAL))) - *value = m_ram_ptr[m_grom_address]; - - if ((id==1) && (m_gk_switch[5]==GK_LDON)) - *value = m_grom_ptr[m_grom_address & 0x1fff]; - - // The GK GROM emulation does not wrap at 8K boundaries. - m_grom_address = (m_grom_address + 1) & 0xffff; - - // Reset the write address flipflop. - m_waddr_LSB = false; - if (TRACE_GKRACKER) logerror("GROM read -> %02x\n", *value); - } - } - - if (m_romspace_selected) - { - // Reads from the RAM space of the GRAM Kracker. - - // RAM is stored behind the GRAM area - // Note that offset is 0000...1fff - // When switch in middle position (WP) do bank select according to page flag - if (m_gk_switch[1] == GK_NORMAL) - { - int base = ((m_gk_switch[4]==GK_BANK1) || ((m_gk_switch[4]==GK_WP) && (m_ram_page==0)))? 0x10000 : 0x12000; - *value = m_ram_ptr[offset | base]; - if (TRACE_GKRACKER) logerror("Read %04x -> %02x\n", offset | 0x6000, *value); - } - } - - // If the guest has GROMs or ROMs they will override the GK contents - if (m_cartridge != nullptr) - { - // For debugging - uint8_t val1 = *value; - - // Read from the guest cartridge. - m_cartridge->readz(space, offset, value, mem_mask); - if (TRACE_GKRACKER) - if (val1 != *value) logerror("Read (from guest) %04x -> %02x\n", offset, *value); - } -} - -WRITE8_MEMBER(ti99_gkracker_device::write) -{ - // write to the guest cartridge if present - if (m_cartridge != nullptr) - { - m_cartridge->write(space, offset, data, mem_mask); - } - - if (m_grom_selected) - { - // Write to the GRAM space of the GRAM Kracker. - if ((offset & 0x0002)==0x0002) - { - // Set address - if (m_waddr_LSB == true) - { - // Accept low address byte (second write) - m_grom_address = (m_grom_address & 0xff00) | data; - m_waddr_LSB = false; - if (TRACE_GKRACKER) logerror("Set GROM address %04x\n", m_grom_address); - } - else - { - // Accept high address byte (first write) - m_grom_address = (m_grom_address & 0x00ff) | (data << 8); - m_waddr_LSB = true; - } - } - else - { - // Write data byte to GRAM area. - if (TRACE_GKRACKER) logerror("GROM write %04x(%04x) <- %02x\n", offset, m_grom_address, data); - - // According to manual: - // Writing to GRAM 0: switch 2 set to GRAM 0 + Write protect switch (4) in 1 or 2 position - // Writing to GRAM 1: switch 3 set to GRAM 1-2 + Loader off (5); write prot has no effect - // Writing to GRAM 2: switch 3 set to GRAM 1-2 (write prot has no effect) - // Writing to GRAM 3-7: switch 1 set to GK_NORMAL, no cartridge inserted - // GK_NORMAL switch has no effect on GRAM 0-2 - - int id = ((m_grom_address & 0xe000)>>13)&0x07; - - if ((id==0 && m_gk_switch[2]==GK_GRAM0 && m_gk_switch[4]!=GK_WP) - || (id==1 && m_gk_switch[3]==GK_GRAM12 && m_gk_switch[5]==GK_LDOFF) - || (id==2 && m_gk_switch[3]==GK_GRAM12) - || (id>=3 && m_gk_switch[1]==GK_NORMAL)) - m_ram_ptr[m_grom_address] = data; - - // The GK GROM emulation does not wrap at 8K boundaries. - m_grom_address = (m_grom_address + 1) & 0xffff; - - // Reset the write address flipflop. - m_waddr_LSB = false; - } - } - - if (m_romspace_selected) - { - // Write to the RAM space of the GRAM Kracker - if (TRACE_GKRACKER) logerror("Write %04x <- %02x\n", offset | 0x6000, data); - - if (m_gk_switch[1] == GK_NORMAL) - { - if (m_gk_switch[4]==GK_BANK1) m_ram_ptr[offset | 0x10000] = data; - else if (m_gk_switch[4]==GK_BANK2) m_ram_ptr[offset | 0x12000] = data; - // Switch in middle position (WP, implies auto-select according to the page flag) - // This is handled like in Extended Basic (using addresses) - else m_ram_page = (offset >> 1) & 1; - } - } -} - -READ8Z_MEMBER( ti99_gkracker_device::crureadz ) -{ - if (m_cartridge != nullptr) m_cartridge->crureadz(space, offset, value); -} - -WRITE8_MEMBER( ti99_gkracker_device::cruwrite ) -{ - if (m_cartridge != nullptr) m_cartridge->cruwrite(space, offset, data); -} - -INPUT_CHANGED_MEMBER( ti99_gkracker_device::gk_changed ) -{ - if (TRACE_GKRACKER) logerror("Input changed %d - %d\n", (int)((uint64_t)param & 0x07), newval); - m_gk_switch[(uint64_t)param & 0x07] = newval; -} - -void ti99_gkracker_device::insert(int index, ti99_cartridge_device* cart) -{ - if (TRACE_CHANGE) logerror("Insert cartridge\n"); - m_cartridge = cart; - // Switch 1 has a third location for resetting. We do the reset by default - // here. It can be turned off in the configuration. - m_gromport->cartridge_inserted(); -} - -void ti99_gkracker_device::remove(int index) -{ - if (TRACE_CHANGE) logerror("Remove cartridge\n"); - m_cartridge = nullptr; -} - -void ti99_gkracker_device::gk_install_menu(const char* menutext, int len, int ptr, int next, int start) -{ - const int base = 0x0000; - m_ram_ptr[base + ptr] = (uint8_t)((next >> 8) & 0xff); - m_ram_ptr[base + ptr+1] = (uint8_t)(next & 0xff); - m_ram_ptr[base + ptr+2] = (uint8_t)((start >> 8) & 0xff); - m_ram_ptr[base + ptr+3] = (uint8_t)(start & 0xff); - - m_ram_ptr[base + ptr+4] = (uint8_t)(len & 0xff); - memcpy(m_ram_ptr + base + ptr+5, menutext, len); -} - -/* - Define the default for the GRAM Kracker device. The memory is preset with - some sample entries which shall indicate that the memory has been tested - by the manufacturer. -*/ -void ti99_gkracker_device::nvram_default() -{ - if (TRACE_GKRACKER) logerror("Creating default NVRAM\n"); - memset(m_ram_ptr, 0, 81920); - - m_ram_ptr[0x6000] = 0xaa; - m_ram_ptr[0x6001] = 0x01; - m_ram_ptr[0x6002] = 0x01; - - m_ram_ptr[0x6006] = 0x60; - m_ram_ptr[0x6007] = 0x20; - - gk_install_menu("GROM 3 OK", 9, 0x60e0, 0, 0x6100); - gk_install_menu("GROM 4 OK", 9, 0x60c0, 0x60e0, 0x6100); - gk_install_menu("GROM 5 OK", 9, 0x60a0, 0x60c0, 0x6100); - gk_install_menu("GROM 6 OK", 9, 0x6080, 0x60a0, 0x6100); - gk_install_menu("PROM OK", 9, 0x6060, 0x6080, 0x6100); - gk_install_menu("RAMS OK", 9, 0x6040, 0x6060, 0x6100); - gk_install_menu("OPTION GRAMS OK", 15, 0x6020, 0x6040, 0x6100); - - m_ram_ptr[0x6100] = 0x0b; // GPL EXIT -} - -void ti99_gkracker_device::nvram_read(emu_file &file) -{ - int readsize = file.read(m_ram_ptr, 81920); - if (TRACE_GKRACKER) logerror("Reading NVRAM\n"); - // If we increased the size, fill the remaining parts with 0 - if (readsize < 81920) - { - memset(m_ram_ptr + readsize, 0, 81920-readsize); - } -} - -void ti99_gkracker_device::nvram_write(emu_file &file) -{ - if (TRACE_GKRACKER) logerror("Writing NVRAM\n"); - file.write(m_ram_ptr, 81920); -} - -void ti99_gkracker_device::device_start() -{ - m_ram_ptr = memregion(GKRACKER_NVRAM_TAG)->base(); - m_grom_ptr = memregion(GKRACKER_ROM_TAG)->base(); - m_cartridge = nullptr; - for (int i=1; i < 6; i++) m_gk_switch[i] = 0; - save_pointer(NAME(m_gk_switch),6); - save_item(NAME(m_romspace_selected)); - save_item(NAME(m_ram_page)); - save_item(NAME(m_grom_address)); - save_item(NAME(m_waddr_LSB)); -} - -void ti99_gkracker_device::device_reset() -{ - m_gk_switch[1] = ioport(GKSWITCH1_TAG)->read(); - m_gk_switch[2] = ioport(GKSWITCH2_TAG)->read(); - m_gk_switch[3] = ioport(GKSWITCH3_TAG)->read(); - m_gk_switch[4] = ioport(GKSWITCH4_TAG)->read(); - m_gk_switch[5] = ioport(GKSWITCH5_TAG)->read(); - m_grom_address = 0; // for the GROM emulation - m_ram_page = 0; - m_waddr_LSB = false; - m_grom_selected = false; -} - -static MACHINE_CONFIG_START( gkracker_slot ) - MCFG_DEVICE_ADD("cartridge", TI99CART, 0) -MACHINE_CONFIG_END - -/* - The GRAMKracker ROM -*/ -ROM_START( gkracker_rom ) - ROM_REGION(0x14000, GKRACKER_NVRAM_TAG, ROMREGION_ERASE00) - ROM_REGION(0x2000, GKRACKER_ROM_TAG, 0) - ROM_LOAD("gkracker.bin", 0x0000, 0x2000, CRC(86eaaf9f) SHA1(a3bd5257c63e190800921b52dbe3ffa91ad91113)) -ROM_END - -const tiny_rom_entry *ti99_gkracker_device::device_rom_region() const -{ - return ROM_NAME( gkracker_rom ); -} - -machine_config_constructor ti99_gkracker_device::device_mconfig_additions() const -{ - return MACHINE_CONFIG_NAME( gkracker_slot ); -} - -INPUT_PORTS_START(gkracker) - PORT_START( GKSWITCH1_TAG ) - PORT_DIPNAME( 0x01, 0x01, "GK switch 1" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 1) - PORT_DIPSETTING( 0x00, "GK Off" ) - PORT_DIPSETTING( 0x01, DEF_STR( Normal ) ) - - PORT_START( GKSWITCH2_TAG ) - PORT_DIPNAME( 0x01, 0x01, "GK switch 2" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 2) - PORT_DIPSETTING( 0x00, "GRAM 0" ) - PORT_DIPSETTING( 0x01, "Op Sys" ) - - PORT_START( GKSWITCH3_TAG ) - PORT_DIPNAME( 0x01, 0x01, "GK switch 3" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 3) - PORT_DIPSETTING( 0x00, "GRAM 1-2" ) - PORT_DIPSETTING( 0x01, "TI BASIC" ) - - PORT_START( GKSWITCH4_TAG ) - PORT_DIPNAME( 0x03, 0x01, "GK switch 4" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 4) - PORT_DIPSETTING( 0x00, "Bank 1" ) - PORT_DIPSETTING( 0x01, "W/P" ) - PORT_DIPSETTING( 0x02, "Bank 2" ) - - PORT_START( GKSWITCH5_TAG ) - PORT_DIPNAME( 0x01, 0x00, "GK switch 5" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 5) - PORT_DIPSETTING( 0x00, "Loader On" ) - PORT_DIPSETTING( 0x01, "Loader Off" ) -INPUT_PORTS_END - -ioport_constructor ti99_gkracker_device::device_input_ports() const -{ - return INPUT_PORTS_NAME(gkracker); -} - -/*************************************************************************** - Cartridge implementation - - Every cartridge is an instance of ti99_cartridge_device, implementing the - device_image_interface. This means it is capable of loading cartridge - data into its memory locations. All memory locations are organised as - regions. - - The different cartridge versions are realised by different PCB instances. - All PCBs are subclassed from ti99_cartridge_pcb. - -***************************************************************************/ -enum -{ - PCB_STANDARD=1, - PCB_PAGED12K, - PCB_PAGED16K, - PCB_MINIMEM, - PCB_SUPER, - PCB_MBX, - PCB_PAGED379I, - PCB_PAGED378, - PCB_PAGED377, - PCB_PAGEDCRU, - PCB_GROMEMU -}; - -static const pcb_type pcbdefs[] = -{ - { PCB_STANDARD, "standard" }, - { PCB_PAGED12K, "paged12k" }, - { PCB_PAGED16K, "paged" }, - { PCB_MINIMEM, "minimem" }, - { PCB_SUPER, "super" }, - { PCB_MBX, "mbx" }, - { PCB_PAGED379I, "paged379i" }, - { PCB_PAGED378, "paged378" }, - { PCB_PAGED377, "paged377" }, - { PCB_PAGEDCRU, "pagedcru" }, - { PCB_GROMEMU, "gromemu" }, - { 0, nullptr} -}; - -// Softlists do not support the cartridges with RAM yet -static const pcb_type sw_pcbdefs[] = -{ - { PCB_STANDARD, "standard" }, - { PCB_PAGED12K, "paged12k" }, - { PCB_PAGED16K, "paged16k" }, - { PCB_MINIMEM, "minimem" }, - { PCB_SUPER, "super" }, - { PCB_MBX, "mbx" }, - { PCB_GROMEMU, "gromemu" }, - { 0, nullptr} -}; - -ti99_cartridge_device::ti99_cartridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) -: bus8z_device(mconfig, TI99CART, tag, owner, clock), - device_image_interface(mconfig, *this), - m_pcbtype(0), - m_slot(0), - m_pcb(nullptr), - m_connector(nullptr), - m_rpk(nullptr) -{ -} - -void ti99_cartridge_device::prepare_cartridge() -{ - int rom2_length; - - uint8_t* grom_ptr; - uint8_t* rom_ptr; - - memory_region *regg; - memory_region *regr; - - // Initialize some values. - m_pcb->m_rom_page = 0; - m_pcb->m_rom_ptr = nullptr; - m_pcb->m_ram_size = 0; - m_pcb->m_ram_ptr = nullptr; - m_pcb->m_ram_page = 0; - - for (int i=0; i < 5; i++) m_pcb->m_grom[i] = nullptr; - - m_pcb->m_grom_size = loaded_through_softlist() ? get_software_region_length("grom") : m_rpk->get_resource_length("grom_socket"); - if (TRACE_CONFIG) logerror("grom_socket.size=0x%04x\n", m_pcb->m_grom_size); - - if (m_pcb->m_grom_size > 0) - { - regg = memregion(CARTGROM_TAG); - grom_ptr = loaded_through_softlist() ? get_software_region("grom") : m_rpk->get_contents_of_socket("grom_socket"); - memcpy(regg->base(), grom_ptr, m_pcb->m_grom_size); - m_pcb->m_grom_ptr = regg->base(); // for gromemu - m_pcb->m_grom_address = 0; // for gromemu - - // Find the GROMs and keep their pointers - m_pcb->set_grom_pointer(0, subdevice(GROM3_TAG)); - if (m_pcb->m_grom_size > 0x2000) m_pcb->set_grom_pointer(1, subdevice(GROM4_TAG)); - if (m_pcb->m_grom_size > 0x4000) m_pcb->set_grom_pointer(2, subdevice(GROM5_TAG)); - if (m_pcb->m_grom_size > 0x6000) m_pcb->set_grom_pointer(3, subdevice(GROM6_TAG)); - if (m_pcb->m_grom_size > 0x8000) m_pcb->set_grom_pointer(4, subdevice(GROM7_TAG)); - } - - m_pcb->m_rom_size = loaded_through_softlist() ? get_software_region_length("rom") : m_rpk->get_resource_length("rom_socket"); - if (m_pcb->m_rom_size > 0) - { - if (TRACE_CONFIG) logerror("rom size=0x%04x\n", m_pcb->m_rom_size); - regr = memregion(CARTROM_TAG); - rom_ptr = loaded_through_softlist() ? get_software_region("rom") : m_rpk->get_contents_of_socket("rom_socket"); - memcpy(regr->base(), rom_ptr, m_pcb->m_rom_size); - // Set both pointers to the same region for now - m_pcb->m_rom_ptr = regr->base(); - } - - // Softlist uses only one ROM area, no second socket - if (!loaded_through_softlist()) - { - rom2_length = m_rpk->get_resource_length("rom2_socket"); - if (rom2_length > 0) - { - // sizes do not differ between rom and rom2 - // We use the large cartrom space for the second bank as well - regr = memregion(CARTROM_TAG); - rom_ptr = m_rpk->get_contents_of_socket("rom2_socket"); - memcpy(regr->base() + 0x2000, rom_ptr, rom2_length); - } - } - - // (NV)RAM cartridges - if (loaded_through_softlist()) - { - // Do we have NVRAM? - if (get_software_region("nvram")!=nullptr) - { - m_pcb->m_ram_size = get_software_region_length("nvram"); - m_pcb->m_nvram.resize(m_pcb->m_ram_size); - m_pcb->m_ram_ptr = &m_pcb->m_nvram[0]; - battery_load(m_pcb->m_ram_ptr, m_pcb->m_ram_size, 0xff); - } - - // Do we have RAM? - if (get_software_region("ram")!=nullptr) - { - m_pcb->m_ram_size = get_software_region_length("ram"); - m_pcb->m_ram.resize(m_pcb->m_ram_size); - m_pcb->m_ram_ptr = &m_pcb->m_ram[0]; - } - } - else - { - m_pcb->m_ram_size = m_rpk->get_resource_length("ram_socket"); - if (m_pcb->m_ram_size > 0) - { - // TODO: Consider to use a region as well. If so, do not forget to memcpy. - m_pcb->m_ram_ptr = m_rpk->get_contents_of_socket("ram_socket"); - } - } -} - -/* - Find the index of the cartridge name. We assume the format - , i.e. the number is the longest string from the right - which can be interpreted as a number. Subtract 1. -*/ -int ti99_cartridge_device::get_index_from_tagname() -{ - const char *mytag = tag(); - int maxlen = strlen(mytag); - int i; - - for (i=maxlen-1; i >=0; i--) - if (mytag[i] < 48 || mytag[i] > 57) break; - - if (i==maxlen-1) return 0; - return atoi(mytag+i+1)-1; -} - -image_init_result ti99_cartridge_device::call_load() -{ - // File name is in m_basename - // return true = error - if (TRACE_CHANGE) logerror("Loading %s in slot %s\n", basename()); - - if (loaded_through_softlist()) - { - if (TRACE_CONFIG) logerror("Using softlists\n"); - int i = 0; - const char* pcb = get_feature("pcb"); - do - { - if (strcmp(pcb, sw_pcbdefs[i].name)==0) - { - m_pcbtype = sw_pcbdefs[i].id; - break; - } - i++; - } while (sw_pcbdefs[i].id != 0); - if (TRACE_CONFIG) logerror("Cartridge type is %s (%d)\n", pcb, m_pcbtype); - m_rpk = nullptr; - } - else - { - auto reader = new rpk_reader(pcbdefs); - try - { - m_rpk = reader->open(machine().options(), filename(), machine().system().name); - m_pcbtype = m_rpk->get_type(); - } - catch (rpk_exception& err) - { - logerror("Failed to load cartridge '%s': %s\n", basename(), err.to_string()); - m_rpk = nullptr; - m_err = IMAGE_ERROR_INVALIDIMAGE; - return image_init_result::FAIL; - } - } - - switch (m_pcbtype) - { - case PCB_STANDARD: - if (TRACE_CONFIG) logerror("Standard PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_PAGED12K: - if (TRACE_CONFIG) logerror("Paged PCB 12K\n"); - m_pcb = std::make_unique(); - break; - case PCB_PAGED16K: - if (TRACE_CONFIG) logerror("Paged PCB 16K\n"); - m_pcb = std::make_unique(); - break; - case PCB_MINIMEM: - if (TRACE_CONFIG) logerror("Minimem PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_SUPER: - if (TRACE_CONFIG) logerror("Superspace PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_MBX: - if (TRACE_CONFIG) logerror("MBX PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_PAGED379I: - if (TRACE_CONFIG) logerror("Paged379i PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_PAGED378: - if (TRACE_CONFIG) logerror("Paged378 PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_PAGED377: - if (TRACE_CONFIG) logerror("Paged377 PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_PAGEDCRU: - if (TRACE_CONFIG) logerror("PagedCRU PCB\n"); - m_pcb = std::make_unique(); - break; - case PCB_GROMEMU: - if (TRACE_CONFIG) logerror("Grom Emulation PCB\n"); - m_pcb = std::make_unique(); - break; - } - - prepare_cartridge(); - m_pcb->set_cartridge(this); - m_pcb->set_tag(tag()); - m_slot = get_index_from_tagname(); - m_connector->insert(m_slot, this); - return image_init_result::PASS; -} - -void ti99_cartridge_device::call_unload() -{ - if (TRACE_CHANGE) logerror("Unload\n"); - if (m_rpk != nullptr) - { - m_rpk->close(); // will write NVRAM contents - delete m_rpk; - } - else - { - // Softlist - bool has_nvram = (get_software_region("nvram")!=nullptr); - if (has_nvram) - { - int nvsize = get_software_region_length("nvram"); - battery_save(m_pcb->m_ram_ptr, nvsize); - } - } - - m_pcb = nullptr; - m_connector->remove(m_slot); -} - -void ti99_cartridge_device::set_slot(int i) -{ - m_slot = i; -} - -READ8Z_MEMBER(ti99_cartridge_device::readz) -{ - if (m_pcb != nullptr) - m_pcb->readz(space, offset, value); -} - -WRITE8_MEMBER(ti99_cartridge_device::write) -{ - if (m_pcb != nullptr) - m_pcb->write(space, offset, data); -} - -READ8Z_MEMBER(ti99_cartridge_device::crureadz) -{ - if (m_pcb != nullptr) m_pcb->crureadz(space, offset, value); -} - -WRITE8_MEMBER(ti99_cartridge_device::cruwrite) -{ - if (m_pcb != nullptr) m_pcb->cruwrite(space, offset, data); -} - -WRITE_LINE_MEMBER( ti99_cartridge_device::ready_line ) -{ - m_connector->ready_line(state); -} - -WRITE_LINE_MEMBER( ti99_cartridge_device::romgq_line ) -{ - if (m_pcb != nullptr) - { - m_pcb->romgq_line(state); - m_readrom = state; - } -} - -/* - Combined select lines -*/ -WRITE8_MEMBER(ti99_cartridge_device::set_gromlines) -{ - if (m_pcb != nullptr) m_pcb->set_gromlines(space, offset, data); -} - -WRITE_LINE_MEMBER(ti99_cartridge_device::gclock_in) -{ - if (m_pcb != nullptr) m_pcb->gclock_in(state); -} - -bool ti99_cartridge_device::is_grom_idle() -{ - return (m_pcb != nullptr)? m_pcb->is_grom_idle() : false; -} - -void ti99_cartridge_device::device_config_complete() -{ - m_connector = static_cast(owner()); -} - -/* - 5 GROMs that may be contained in a cartridge -*/ -static MACHINE_CONFIG_START( ti99_cartridge ) - MCFG_GROM_ADD( GROM3_TAG, 3, CARTGROM_TAG, 0x0000, WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM4_TAG, 4, CARTGROM_TAG, 0x2000, WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM5_TAG, 5, CARTGROM_TAG, 0x4000, WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM6_TAG, 6, CARTGROM_TAG, 0x6000, WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM7_TAG, 7, CARTGROM_TAG, 0x8000, WRITELINE(ti99_cartridge_device, ready_line)) -MACHINE_CONFIG_END - -machine_config_constructor ti99_cartridge_device::device_mconfig_additions() const -{ - return MACHINE_CONFIG_NAME( ti99_cartridge ); -} - -/* - Memory area for one cartridge. For most cartridges we only need 8 KiB for - ROM contents, but cartridges of the "paged377" type have up to 2 MiB - organised as selectable banks, so we must be sure there is enough space. -*/ -ROM_START( cartridge_memory ) - ROM_REGION(0xa000, CARTGROM_TAG, ROMREGION_ERASE00) - ROM_REGION(0x200000, CARTROM_TAG, ROMREGION_ERASE00) -ROM_END - -const tiny_rom_entry *ti99_cartridge_device::device_rom_region() const -{ - return ROM_NAME( cartridge_memory ); -} - -DEFINE_DEVICE_TYPE(TI99CART, ti99_cartridge_device, "ti99cart", "TI-99 cartridge") - -/*************************************************************************** - Cartridge types - Cartridges differ by the circuits on their PCB which hosts the ROMs. - Some cartridges also have RAM, and some allow for switching between - ROMs. - - Standard cartridge - - GROM space - 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff - |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| - - - ROM space - 6000 7000 7fff - | | | - |========== ROM1 ============| - -***************************************************************************/ - -ti99_cartridge_pcb::ti99_cartridge_pcb() - : m_cart(nullptr), - m_grom_idle(false), - m_grom_size(0), - m_rom_size(0), - m_ram_size(0), - m_rom_ptr(nullptr), - m_ram_ptr(nullptr), - m_romspace_selected(false), - m_rom_page(0), - m_grom_ptr(nullptr), - m_grom_address(0), - m_ram_page(0), - m_tag(nullptr) -{ -} - -void ti99_cartridge_pcb::set_cartridge(ti99_cartridge_device *cart) -{ - m_cart = cart; -} - -READ8Z_MEMBER(ti99_cartridge_pcb::gromreadz) -{ - for (auto & elem : m_grom) - { - if (elem != nullptr) - { - elem->readz(space, offset, value, mem_mask); - } - } -} - -WRITE8_MEMBER(ti99_cartridge_pcb::gromwrite) -{ - for (auto & elem : m_grom) - { - if (elem != nullptr) - { - elem->write(space, offset, data, mem_mask); - } - } -} - -/* - TI-99/4A cartridges can only occupy 8 KiB of CPU RAM space. For TI-99/8 - cartridges with up to 16 KiB we need a new PCB type. Unfortunately, such - cartridges were never developed. -*/ -READ8Z_MEMBER(ti99_cartridge_pcb::readz) -{ - if (m_romspace_selected) - { - if (m_rom_ptr!=nullptr) - { - *value = m_rom_ptr[offset & 0x1fff]; - } - } - else - { - // Will not return anything when not selected (preceding gsq=ASSERT) - gromreadz(space, offset, value, mem_mask); - } -} - -WRITE8_MEMBER(ti99_cartridge_pcb::write) -{ - if (m_romspace_selected) - { - if (TRACE_ILLWRITE) m_cart->logerror("Cannot write to ROM space at %04x\n", offset); - } - else - { - // Will not change anything when not selected (preceding gsq=ASSERT) - gromwrite(space, offset, data, mem_mask); - } -} - -READ8Z_MEMBER(ti99_cartridge_pcb::crureadz) -{ -} - -WRITE8_MEMBER(ti99_cartridge_pcb::cruwrite) -{ -} - -void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev) -{ - m_grom[number] = static_cast(dev); -} - - -WRITE_LINE_MEMBER( ti99_cartridge_pcb::romgq_line ) -{ - m_romspace_selected = (state==ASSERT_LINE); -} - -// Propagate to all GROMs - -/* - Combined select lines -*/ -WRITE8_MEMBER(ti99_cartridge_pcb::set_gromlines) -{ - for (auto& elem : m_grom) - { - if (elem != nullptr) - { - elem->set_lines(space, offset, data); - if (data==ASSERT_LINE) m_grom_idle = false; - } - } -} - -WRITE_LINE_MEMBER(ti99_cartridge_pcb::gclock_in) -{ - for (auto& elem : m_grom) - { - if (elem != nullptr) - { - elem->gclock_in(state); - m_grom_idle = elem->idle(); - } - } -} - - -/***************************************************************************** - Cartridge type: Paged (12K, Extended Basic) - - The Extended Basic cartridge consists of several GROMs which are - treated in the usual way, and two ROMs (4K and 8K). - - GROM space - 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff - |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| - - ROM space - 6000 7000 7fff - | | | - | |==== ROM2a ===| Bank 0 write to 6000, 6004, ... 7ffc - |=== ROM1 ====| | - |==== ROM2b ===| Bank 1 write to 6002, 6006, ... 7ffe - - The 4K ROM is mapped into the 6000-6FFF space. - The 8K ROM is actually composed of two banks of 4K which are mapped into - the 7000-7FFF space. Bank 0 is visible after a write access to 6000 / 6004 / - 6008 ... , while bank 1 is visible after writing to 6002 / 6006 / 600A / ... - -******************************************************************************/ - -READ8Z_MEMBER(ti99_paged12k_cartridge::readz) -{ - if (m_romspace_selected) - { - // rom_ptr: 0000-0fff = rom1 - // 2000-2fff = rom2a - // 3000-3fff = rom2b - if ((offset & 0x1000)==0) - *value = m_rom_ptr[offset & 0x0fff]; - else - *value = m_rom_ptr[(offset & 0x0fff) | 0x2000 | (m_rom_page << 12)]; - } - else - { - // Will not return anything when not selected (preceding gsq=ASSERT) - gromreadz(space, offset, value, mem_mask); - } -} - -WRITE8_MEMBER(ti99_paged12k_cartridge::write) -{ - if (m_romspace_selected) - { - m_rom_page = (offset >> 1) & 1; - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); - } - else - { - // Will not change anything when not selected (preceding gsq=ASSERT) - gromwrite(space, offset, data, mem_mask); - } -} - -/***************************************************************************** - Cartridge type: Paged (16K) - - GROM space - 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff - |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| - - ROM space - 6000 7000 7fff - | | | - |========== ROM1 ===========| Bank 0 write to 6000, 6004, ... 7ffc - | | | - |========== ROM2 ===========| Bank 1 write to 6002, 6006, ... 7ffe - - This cartridge consists of GROM memory and 2 pages of standard ROM. - The page is set by writing any value to a location in - the address area, where an even word offset sets the page to 0 and an - odd word offset sets the page to 1 (e.g. 6000 = bank 0, and - 6002 = bank 1). -******************************************************************************/ - -READ8Z_MEMBER(ti99_paged16k_cartridge::readz) -{ - if (m_romspace_selected) - { - *value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page << 13)]; - } - else - { - // Will not return anything when not selected (preceding gsq=ASSERT) - gromreadz(space, offset, value, mem_mask); - } -} - -WRITE8_MEMBER(ti99_paged16k_cartridge::write) -{ - if (m_romspace_selected) - { - m_rom_page = (offset >> 1) & 1; - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); - } - else - { - // Will not change anything when not selected (preceding gsq=ASSERT) - gromwrite(space, offset, data, mem_mask); - } -} - -/***************************************************************************** - Cartridge type: Mini Memory - GROM: 6 KiB (occupies G>6000 to G>77ff) - ROM: 4 KiB (6000-6fff) - RAM: 4 KiB (7000-7fff, battery-backed) - - GROM space - 6000 77ff - |== GROM3 ==| - - ROM space - 6000 7000 7fff - | | | - |=== ROM1 ====| | - |=== NVRAM ===| - -******************************************************************************/ - -/* Read function for the minimem cartridge. */ -READ8Z_MEMBER(ti99_minimem_cartridge::readz) -{ - if (m_romspace_selected) - { - if ((offset & 0x1000)==0x0000) - { - if (m_rom_ptr!=nullptr) // Super-Minimem seems to have no ROM - { - *value = m_rom_ptr[offset & 0x0fff]; - } - } - else - { - *value = m_ram_ptr[offset & 0x0fff]; - } - } - else - { - gromreadz(space, offset, value, mem_mask); - } -} - -/* Write function for the minimem cartridge. */ -WRITE8_MEMBER(ti99_minimem_cartridge::write) -{ - if (m_romspace_selected) - { - if ((offset & 0x1000)==0x0000) - { - if (TRACE_ILLWRITE) m_cart->logerror("Write access to cartridge ROM at address %04x ignored", offset); - } - else - { - m_ram_ptr[offset & 0x0fff] = data; - } - } - else - { - gromwrite(space, offset, data, mem_mask); - } -} - -/***************************************************************************** - Cartridge type: SuperSpace II - - SuperSpace is intended as a user-definable blank cartridge containing - buffered RAM. It has an Editor/Assembler GROM which helps the user to load - the user program into the cartridge. If the user program has a suitable - header, the console recognizes the cartridge as runnable, and - assigns a number in the selection screen. Switching the RAM banks in this - cartridge is achieved by setting CRU bits (the system serial interface). - - GROM: Editor/Assembler GROM - ROM: none - RAM: 32 KiB (0x6000-0x7fff, 4 banks) - Banking: via CRU write - - GROM space - 6000 77ff - |==== GROM3 (Editor/Assm) ====| - - ROM space - 6000 7000 7fff - | | | - |======== NVRAM 0 ==========| Bank 0 CRU>0802 - | | | - |======== NVRAM 1 ==========| Bank 1 CRU>0806 - | | | - |======== NVRAM 2 ==========| Bank 2 CRU>080a - | | | - |======== NVRAM 3 ==========| Bank 3 CRU>080e - -******************************************************************************/ - -/* Read function for the super cartridge. */ -READ8Z_MEMBER(ti99_super_cartridge::readz) -{ - if (m_romspace_selected) - { - if (m_ram_ptr != nullptr) - { - *value = m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)]; - } - } - else - { - gromreadz(space, offset, value, mem_mask); - } -} - -/* Write function for the super cartridge. */ -WRITE8_MEMBER(ti99_super_cartridge::write) -{ - if (m_romspace_selected) - { - m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)] = data; - } - else - { - gromwrite(space, offset, data, mem_mask); - } -} - -READ8Z_MEMBER(ti99_super_cartridge::crureadz) -{ - // offset is the bit number. The CRU base address is already divided by 2. - - // ram_page contains the bank number. We have a maximum of - // 4 banks; the Super Space II manual says: - // - // Banks are selected by writing a bit pattern to CRU address >0800: - // - // Bank # Value - // 0 >02 = 0000 0010 - // 1 >08 = 0000 1000 - // 2 >20 = 0010 0000 - // 3 >80 = 1000 0000 - // - // With the bank number (0, 1, 2, or 3) in R0: - // - // BNKSW LI R12,>0800 Set CRU address - // LI R1,2 Load Shift Bit - // SLA R0,1 Align Bank Number - // JEQ BNKS1 Skip shift if Bank 0 - // SLA R1,0 Align Shift Bit - // BNKS1 LDCR R1,0 Switch Banks - // SRL R0,1 Restore Bank Number (optional) - // RT - - // Our implementation in MESS always gets 8 bits in one go. Also, the address - // is twice the bit number. That is, the offset value is always a multiple - // of 0x10. - - if ((offset & 0xfff0) == 0x0800) - { - if (TRACE_CRU) m_cart->logerror("CRU accessed at %04x\n", offset); - uint8_t val = 0x02 << (m_ram_page << 1); - *value = (val >> ((offset - 0x0800)>>1)) & 0xff; - } -} - -WRITE8_MEMBER(ti99_super_cartridge::cruwrite) -{ - if ((offset & 0xfff0) == 0x0800) - { - if (TRACE_CRU) m_cart->logerror("CRU accessed at %04x\n", offset); - if (data != 0) - { - m_ram_page = (offset-0x0802)>>2; - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set RAM page = %d (CRU address %04x)\n", m_ram_page, offset); - } - } -} - -/***************************************************************************** - Cartridge type: MBX - GROM: up to 5 GROMs (sockets for a maximum of 3 GROMs, but may be stacked) - ROM: up to 16 KiB (in up to 2 banks of 8KiB each) - RAM: 1022 B (0x6c00-0x6ffd, overrides ROM in that area) - ROM mapper: 6ffe - - TODO: Some MBX cartridges assume the presence of the MBX system - (special user interface box with speech input/output) - and will not run without it. This MBX hardware is not emulated yet. - - GROM space - 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 f7ff - |== GROM3 ==|...|== GROM4 ==|...|== GROM5 ==|...|== GROM6 ==|...|== GROM7 ==| - - ROM space - 6000 6c00 7000 7fff - | | | | - | | |===== ROM bank 0 ====| 6ffe = 00 - | |= RAM =| | - |=== ROM bank 0 ==| |===== ROM bank 1 ====| 6ffe = 01 - | | - |===== ROM bank 3 ====| 6ffe = 02 - | | - |===== ROM bank 3 ====| 6ffe = 03 - - The 16K ROM is composed of four 4K banks, which can be selected by writing - the bank number to address 6ffe. This also affects the RAM so that the - bank number is stored in RAM and may also be read from there. - - The mapper does not decode the LSB of the address, so it changes value when - a write operation is done on 6FFF. Since the TI console always writes the - odd byte first, then the even byte, the last byte written is actually 6FFE. - - ROM bank 0 (ROM area 0000-0fff) is always visible in the space 6000-6bff. - - RAM is implemented by two 1024x4 RAM circuits and is not affected by banking. - -******************************************************************************/ - -/* Read function for the mbx cartridge. */ -READ8Z_MEMBER(ti99_mbx_cartridge::readz) -{ - if (m_romspace_selected) - { - if (m_ram_ptr != nullptr && (offset & 0x1c00)==0x0c00) - { - // Also reads the value of 6ffe - *value = m_ram_ptr[offset & 0x03ff]; - if (TRACE_READ) m_cart->logerror("%04x (RAM) -> %02x\n", offset + 0x6000, *value); - } - else - { - if (m_rom_ptr!=nullptr) - { - if ((offset & 0x1000)==0) // 6000 area - *value = m_rom_ptr[offset]; - else // 7000 area - *value = m_rom_ptr[(offset & 0x0fff) | (m_rom_page << 12)]; - - if (TRACE_READ) m_cart->logerror("%04x(%04x) -> %02x\n", offset + 0x6000, offset | (m_rom_page<<13), *value); - } - } - } - else - { - gromreadz(space, offset, value, mem_mask); - } -} - -/* Write function for the mbx cartridge. */ -WRITE8_MEMBER(ti99_mbx_cartridge::write) -{ - if (m_romspace_selected) - { - if ((offset & 0x1c00)==0x0c00) // RAM area - { - if ((offset & 0x0ffe) == 0x0ffe) // Mapper, backed by RAM; reacts to bots 6fff and 6ffe - { - // Valid values are 0, 1, 2, 3 - m_rom_page = data & 3; - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); - } - - if (m_ram_ptr != nullptr) - m_ram_ptr[offset & 0x03ff] = data; - else - if (TRACE_ILLWRITE) m_cart->logerror("Write access to %04x but no RAM present\n", offset+0x6000); - } - } - else - { - gromwrite(space, offset, data, mem_mask); - } -} - -/***************************************************************************** - Cartridge type: paged379i - This cartridge consists of one 16 KiB, 32 KiB, 64 KiB, or 128 KiB EEPROM - which is organised in 2, 4, 8, or 16 pages of 8 KiB each. The complete - memory contents must be stored in one dump file. - The pages are selected by writing a value to some memory locations. Due to - using the inverted outputs of the LS379 latch, setting the inputs of the - latch to all 0 selects the highest bank, while setting to all 1 selects the - lowest. There are some cartridges (16 KiB) which are using this scheme, and - there are new hardware developments mainly relying on this scheme. - - Writing to selects page (16K/32K/64K/128K) - >6000 1 / 3 / 7 / 15 - >6002 0 / 2 / 6 / 14 - >6004 1 / 1 / 5 / 13 - >6006 0 / 0 / 4 / 12 - >6008 1 / 3 / 3 / 11 - >600A 0 / 2 / 2 / 10 - >600C 1 / 1 / 1 / 9 - >600E 0 / 0 / 0 / 8 - >6010 1 / 3 / 7 / 7 - >6012 0 / 2 / 6 / 6 - >6014 1 / 1 / 5 / 5 - >6016 0 / 0 / 4 / 4 - >6018 1 / 3 / 3 / 3 - >601A 0 / 2 / 2 / 2 - >601C 1 / 1 / 1 / 1 - >601E 0 / 0 / 0 / 0 - - The paged379i cartrige does not have any GROMs. - - ROM space - 6000 7000 7fff - | | | - |========== ROM 1 ==========| Bank 0 Write to 601e - | | | - |========== ROM 2 ==========| Bank 1 Write to 601c - | | | - | ... | - | | | - |========== ROM 16 =========| Bank 15 Write to 6000 - - -******************************************************************************/ - -/* Read function for the paged379i cartridge. */ -READ8Z_MEMBER(ti99_paged379i_cartridge::readz) -{ - if (m_romspace_selected) - *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; -} - -/* Write function for the paged379i cartridge. Only used to set the bank. */ -WRITE8_MEMBER(ti99_paged379i_cartridge::write) -{ - // Bits: 011x xxxx xxxb bbbx - // x = don't care, bbbb = bank - if (m_romspace_selected) - { - // This is emulation magic to automatically adapt to different ROM sizes - - // Each block has 8 KiB. We assume that m_rom_size is a power of 2. - // Thus the number of blocks is also a power of 2. - // To get the required number of address lines, we just have to subtract 1. - // The SN74LS379 only has four flipflops, so we limit the lines to 4. - int mask = ((m_rom_size / 8192) - 1) & 0x0f; - - // The page is determined by the inverted outputs. - m_rom_page = (~offset)>>1 & mask; - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); - } -} - -/***************************************************************************** - Cartridge type: paged378 - This type is intended for high-capacity cartridges of up to 512 KiB - plus GROM space of 120KiB (not supported yet) - - Due to its huge GROM space it is also called the "UberGROM" - - The cartridge may also be used without GROM. - - ROM space - 6000 7000 7fff - | | | - |========== ROM 1 ==========| Bank 0 Write to 6000 - | | | - |========== ROM 2 ==========| Bank 1 Write to 6002 - | | | - | ... | - | | | - |========== ROM 64 =========| Bank 63 Write to 607e - -******************************************************************************/ - -/* Read function for the paged378 cartridge. */ -READ8Z_MEMBER(ti99_paged378_cartridge::readz) -{ - if (m_romspace_selected) - *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; -} - -/* Write function for the paged378 cartridge. Only used to set the bank. */ -WRITE8_MEMBER(ti99_paged378_cartridge::write) -{ - // Bits: 011x xxxx xbbb bbbx - // x = don't care, bbbb = bank - if (m_romspace_selected) - { - m_rom_page = ((offset >> 1)&0x003f); - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); - } -} - -/***************************************************************************** - Cartridge type: paged377 - This type is intended for high-capacity cartridges of up to 2 MiB - - The paged379i cartrige does not have any GROMs. - - ROM space - 6000 7000 7fff - | | | - |========== ROM 1 ==========| Bank 0 Write to 6000 - | | | - |========== ROM 2 ==========| Bank 1 Write to 6002 - | | | - | ... | - | | | - |========== ROM 256 ========| Bank 255 Write to 61fe - - -******************************************************************************/ - -/* Read function for the paged377 cartridge. */ -READ8Z_MEMBER(ti99_paged377_cartridge::readz) -{ - if (m_romspace_selected) - *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; -} - -/* Write function for the paged377 cartridge. Only used to set the bank. */ -WRITE8_MEMBER(ti99_paged377_cartridge::write) -{ - // Bits: 011x xxxb bbbb bbbx - // x = don't care, bbbb = bank - if (m_romspace_selected) - { - m_rom_page = ((offset >> 1)&0x00ff); - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); - } -} - -/***************************************************************************** - Cartridge type: pagedcru - This cartridge consists of one 16 KiB, 32 KiB, or 64 KiB EEPROM which is - organised in 2, 4, or 8 pages of 8 KiB each. We assume there is only one - dump file of the respective size. - The pages are selected by writing a value to the CRU. This scheme is - similar to the one used for the SuperSpace cartridge, with the exception - that we are using ROM only, and we can have up to 8 pages. - - Bank Value written to CRU>0800 - 0 >0002 = 0000 0000 0000 0010 - 1 >0008 = 0000 0000 0000 1000 - 2 >0020 = 0000 0000 0010 0000 - 3 >0080 = 0000 0000 1000 0000 - 4 >0200 = 0000 0010 0000 0000 - 5 >0800 = 0000 1000 0000 0000 - 6 >2000 = 0010 0000 0000 0000 - 7 >8000 = 1000 0000 0000 0000 - - No GROMs used in this type. - - ROM space - 6000 7000 7fff - | | | - |========= ROM 1 ==========| Bank 0 CRU>0802 - | | | - |========= ROM 2 ==========| Bank 1 CRU>0806 - | | | - ... - | | | - |========= ROM 8 ==========| Bank 7 CRU>081e - -******************************************************************************/ - -/* Read function for the pagedcru cartridge. */ -READ8Z_MEMBER(ti99_pagedcru_cartridge::readz) -{ - if (m_romspace_selected) - *value = m_rom_ptr[(m_rom_page<<13) | (offset & 0x1fff)]; -} - -/* Write function for the pagedcru cartridge. No effect. */ -WRITE8_MEMBER(ti99_pagedcru_cartridge::write) -{ - return; -} - -READ8Z_MEMBER(ti99_pagedcru_cartridge::crureadz) -{ - int page = m_rom_page; - if ((offset & 0xf800)==0x0800) - { - int bit = (offset & 0x001e)>>1; - if (bit != 0) - { - page = page-(bit/2); // 4 page flags per 8 bits - } - *value = 1 << (page*2+1); - } -} - -WRITE8_MEMBER(ti99_pagedcru_cartridge::cruwrite) -{ - if ((offset & 0xf800)==0x0800) - { - int bit = (offset & 0x001e)>>1; - if (data != 0 && bit > 0) - { - m_rom_page = (bit-1)/2; - if (TRACE_BANKSWITCH) m_cart->logerror("Set ROM page = %d (CRU address %d)\n", m_rom_page, offset); - } - } -} - -/***************************************************************************** - Cartridge type: GROM emulation/paged - - This cartridge offers GROM address space without real GROM circuits. The GROMs - are emulated by a normal EPROM with a circuit that mimics GROM behavior. - Each simulated GROM offers 8K (real GROMs only offer 6K). - - Some assumptions: - - No readable address counter. This means the parallel console GROMs - will deliver the address when reading. - - No wait states. Reading is generally faster than with real GROMs. - - No wrapping at 8K boundaries. - - Two pages of ROM at address 6000 - - If any of these fails, the cartridge will crash, so we'll see. - - Typical cartridges: Third-party cartridges - - For the sake of simplicity, we register GROMs like the other PCB types, but - we implement special access methods for the GROM space. - - Still not working: - rxb1002 (Set page to 1 (6372 <- 00), lockup) - rxb237 (immediate reset) - rxbv555 (repeating reset on Master Title Screen) - superxb (lockup, fix: add RAM at 7c00) - - Super-MiniMemory is also included here. We assume a RAM area at addresses - 7000-7fff for this cartridge. - - - GROM space - 6000 77ff 8000 97ff a000 b7ff c000 d7ff e000 ffff - |=========================== emulated GROM ================================| - - ROM space - 6000 7000 7fff - | | | - |========== ROM1 ===========| Bank 0 write to 6000, 6004, ... 7ffc - | | | - |========== ROM2 ===========| Bank 1 write to 6002, 6006, ... 7ffe - - -******************************************************************************/ - -WRITE8_MEMBER(ti99_gromemu_cartridge::set_gromlines) -{ - if (m_grom_ptr != nullptr) - { - m_grom_selected = (data != CLEAR_LINE); - m_grom_read_mode = ((offset & GROM_M_LINE)!=0); - m_grom_address_mode = ((offset & GROM_MO_LINE)!=0); - } -} - -READ8Z_MEMBER(ti99_gromemu_cartridge::readz) -{ - if (m_grom_selected) - { - if (m_grom_read_mode) gromemureadz(space, offset, value, mem_mask); - } - else - { - if (m_ram_ptr != nullptr) - { - // Variant of the cartridge which emulates MiniMemory. We don't introduce - // another type for this single cartridge. - if ((offset & 0x1000)==0x1000) - { - *value = m_ram_ptr[offset & 0x0fff]; - return; - } - } - - if (m_rom_ptr == nullptr) return; - *value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page << 13)]; - } -} - -WRITE8_MEMBER(ti99_gromemu_cartridge::write) -{ - if (m_romspace_selected) - { - if (m_ram_ptr != nullptr) - { - // Lines for Super-Minimem; see above - if ((offset & 0x1000)==0x1000) { - m_ram_ptr[offset & 0x0fff] = data; - } - return; // no paging - } - m_rom_page = (offset >> 1) & 1; - if (TRACE_BANKSWITCH) if ((offset & 1)==0) m_cart->logerror("Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); - } - else - { - // Will not change anything when not selected (preceding gsq=ASSERT) - if (m_grom_selected) - { - if (!m_grom_read_mode) gromemuwrite(space, offset, data, mem_mask); - } - } -} - -READ8Z_MEMBER(ti99_gromemu_cartridge::gromemureadz) -{ - // Similar to the GKracker implemented above, we do not have a readable - // GROM address counter but use the one from the console GROMs. - if (m_grom_address_mode) return; - - int id = ((m_grom_address & 0xe000)>>13)&0x07; - if (id > 2) - { - // Cartridge space (0x6000 - 0xffff) - *value = m_grom_ptr[m_grom_address-0x6000]; // use the GROM memory - } - - // The GROM emulation does not wrap at 8K boundaries. - m_grom_address = (m_grom_address + 1) & 0xffff; - - // Reset the write address flipflop. - m_waddr_LSB = false; -} - -WRITE8_MEMBER(ti99_gromemu_cartridge::gromemuwrite) -{ - // Set GROM address - if (m_grom_address_mode) - { - if (m_waddr_LSB == true) - { - // Accept low address byte (second write) - m_grom_address = (m_grom_address << 8) | data; - m_waddr_LSB = false; - } - else - { - // Accept high address byte (first write) - m_grom_address = data; - m_waddr_LSB = true; - } - } - else - { - if (TRACE_ILLWRITE) m_cart->logerror("Ignoring write to GROM area at address %04x\n", m_grom_address); - } -} - -/**************************************************************************** - - RPK loader - - RPK format support - - A RPK file ("rompack") contains a collection of dump files and a layout - file that defines the kind of circuit board (PCB) used in the cartridge - and the mapping of dumps to sockets on the board. - -Example: - - - - - - - - - - - - -DTD: - - - - - - - - - - - - - - - - - - - - -****************************************************************************/ - -#include "unzip.h" -#include "xmlfile.h" - -/**************************************** - RPK class -****************************************/ -/* - Constructor. -*/ -ti99_cartridge_device::rpk::rpk(emu_options& options, const char* sysname) - :m_options(options), m_type(0) -//,m_system_name(sysname) -{ - m_sockets.clear(); -} - -ti99_cartridge_device::rpk::~rpk() -{ - if (TRACE_RPK) printf("gromport/RPK: Destroy RPK\n"); -} - -/* - Deliver the contents of the socket by name of the socket. -*/ -uint8_t* ti99_cartridge_device::rpk::get_contents_of_socket(const char *socket_name) -{ - auto socket = m_sockets.find(socket_name); - if (socket == m_sockets.end()) return nullptr; - return socket->second->get_contents(); -} - -/* - Deliver the length of the contents of the socket by name of the socket. -*/ -int ti99_cartridge_device::rpk::get_resource_length(const char *socket_name) -{ - auto socket = m_sockets.find(socket_name); - if (socket == m_sockets.end()) return 0; - return socket->second->get_content_length(); -} - -void ti99_cartridge_device::rpk::add_socket(const char* id, std::unique_ptr newsock) -{ - m_sockets.emplace(id, std::move(newsock)); -} - -/*------------------------------------------------- - rpk_close - closes a rpk - Saves the contents of the NVRAMs and frees all memory. --------------------------------------------------*/ - -void ti99_cartridge_device::rpk::close() -{ - // Save the NVRAM contents - for(auto &socket : m_sockets) - { - if (socket.second->persistent_ram()) - { - // try to open the battery file and write it if possible - assert_always(socket.second->get_contents() && (socket.second->get_content_length() > 0), "Buffer is null or length is 0"); - - emu_file file(m_options.nvram_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open(socket.second->get_pathname()); - if (filerr == osd_file::error::NONE) - file.write(socket.second->get_contents(), socket.second->get_content_length()); - - } - socket.second->cleanup(); - } -} - -/************************************************************** - RPK socket (location in the PCB where a chip is plugged in; - not a network socket) -***************************************************************/ - -ti99_cartridge_device::rpk_socket::rpk_socket(const char* id, int length, uint8_t* contents, const char *pathname) - : m_id(id), m_length(length), m_contents(contents), m_pathname(pathname) -{ -} - -ti99_cartridge_device::rpk_socket::rpk_socket(const char* id, int length, uint8_t* contents) - : rpk_socket(id, length, contents, nullptr) -{ -} - -/* - Locate a file in the ZIP container -*/ -int ti99_cartridge_device::rpk_reader::find_file(util::archive_file &zip, const char *filename, uint32_t crc) -{ - for (int header = zip.first_file(); header >= 0; header = zip.next_file()) - { - // Ignore directories - if (!zip.current_is_directory()) - { - // We don't check for CRC == 0. - if (crc != 0) - { - // if the CRC and name both match, we're good - // if the CRC matches and the name doesn't, we're still good - if (zip.current_crc() == crc) - return header; - } - else - { - if (core_stricmp(zip.current_name().c_str(), filename) == 0) - { - return header; - } - } - } - } - return -1; -} - -/* - Load a rom resource and put it in a pcb socket instance. -*/ -std::unique_ptr ti99_cartridge_device::rpk_reader::load_rom_resource(util::archive_file &zip, util::xml::data_node const* rom_resource_node, const char* socketname) -{ - const char* file; - const char* crcstr; - const char* sha1; - util::archive_file::error ziperr; - uint32_t crc; - int length; - uint8_t* contents; - int header; - - // find the file attribute (required) - file = rom_resource_node->get_attribute_string("file", nullptr); - if (file == nullptr) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a 'file' attribute"); - - if (TRACE_RPK) printf("gromport/RPK: Loading ROM contents for socket '%s' from file %s\n", socketname, file); - - // check for crc - crcstr = rom_resource_node->get_attribute_string("crc", nullptr); - if (crcstr==nullptr) - { - // no CRC, just find the file in the RPK - header = find_file(zip, file, 0); - } - else - { - crc = strtoul(crcstr, nullptr, 16); - header = find_file(zip, file, crc); - } - if (header < 0) throw rpk_exception(RPK_INVALID_FILE_REF, "File not found or CRC check failed"); - - length = zip.current_uncompressed_length(); - - // Allocate storage - contents = global_alloc_array_clear(length); - if (contents==nullptr) throw rpk_exception(RPK_OUT_OF_MEMORY); - - // and unzip file from the zip file - ziperr = zip.decompress(contents, length); - if (ziperr != util::archive_file::error::NONE) - { - if (ziperr == util::archive_file::error::UNSUPPORTED) throw rpk_exception(RPK_ZIP_UNSUPPORTED); - else throw rpk_exception(RPK_ZIP_ERROR); - } - - // check for sha1 - sha1 = rom_resource_node->get_attribute_string("sha1", nullptr); - if (sha1 != nullptr) - { - util::hash_collection actual_hashes; - actual_hashes.compute((const uint8_t *)contents, length, util::hash_collection::HASH_TYPES_CRC_SHA1); - - util::hash_collection expected_hashes; - expected_hashes.add_from_string(util::hash_collection::HASH_SHA1, sha1, strlen(sha1)); - - if (actual_hashes != expected_hashes) throw rpk_exception(RPK_INVALID_FILE_REF, "SHA1 check failed"); - } - - // Create a socket instance - return std::make_unique(socketname, length, contents); -} - -/* - Load a ram resource and put it in a pcb socket instance. -*/ -std::unique_ptr ti99_cartridge_device::rpk_reader::load_ram_resource(emu_options &options, util::xml::data_node const* ram_resource_node, const char* socketname, const char* system_name) -{ - const char* length_string; - const char* ram_type; - const char* ram_filename; - const char* ram_pname; - unsigned int length; - uint8_t* contents; - - // find the length attribute - length_string = ram_resource_node->get_attribute_string("length", nullptr); - if (length_string == nullptr) throw rpk_exception(RPK_MISSING_RAM_LENGTH); - - // parse it - char suffix = '\0'; - sscanf(length_string, "%u%c", &length, &suffix); - switch(tolower(suffix)) - { - case 'k': // kilobytes - length *= 1024; - break; - - case 'm': - /* megabytes */ - length *= 1024*1024; - break; - - case '\0': - break; - - default: // failed - throw rpk_exception(RPK_INVALID_RAM_SPEC); - } - - // Allocate memory for this resource - contents = global_alloc_array_clear(length); - if (contents==nullptr) throw rpk_exception(RPK_OUT_OF_MEMORY); - - if (TRACE_RPK) printf("gromport/RPK: Allocating RAM buffer (%d bytes) for socket '%s'\n", length, socketname); - - ram_pname = nullptr; - - // That's it for pure RAM. Now check whether the RAM is "persistent", i.e. NVRAM. - // In that case we must load it from the NVRAM directory. - // The file name is given in the RPK file; the subdirectory is the system name. - ram_type = ram_resource_node->get_attribute_string("type", nullptr); - if (ram_type != nullptr) - { - if (strcmp(ram_type, "persistent")==0) - { - // Get the file name (required if persistent) - ram_filename = ram_resource_node->get_attribute_string("file", nullptr); - if (ram_filename==nullptr) - { - global_free_array(contents); - throw rpk_exception(RPK_INVALID_RAM_SPEC, " must have a 'file' attribute"); - } - std::string ram_pathname = std::string(system_name).append(PATH_SEPARATOR).append(ram_filename); - ram_pname = core_strdup(ram_pathname.c_str()); - // load, and fill rest with 00 - if (TRACE_RPK) printf("gromport/RPK: Loading NVRAM contents from '%s'\n", ram_pname); - - // Load the NVRAM contents - int bytes_read = 0; - assert_always(contents && (length > 0), "Buffer is null or length is 0"); - - // try to open the battery file and read it if possible - emu_file file(options.nvram_directory(), OPEN_FLAG_READ); - osd_file::error filerr = file.open(ram_pname); - if (filerr == osd_file::error::NONE) - bytes_read = file.read(contents, length); - - // fill remaining bytes (if necessary) - memset(((char *) contents) + bytes_read, 0x00, length - bytes_read); - } - } - - // Create a socket instance - return std::make_unique(socketname, length, contents, ram_pname); -} - -/*------------------------------------------------- - rpk_open - open a RPK file - options - parameters from the settings; we need it only for the NVRAM directory - system_name - name of the driver (also just for NVRAM handling) --------------------------------------------------*/ - -ti99_cartridge_device::rpk* ti99_cartridge_device::rpk_reader::open(emu_options &options, const char *filename, const char *system_name) -{ - util::archive_file::error ziperr; - - util::archive_file::ptr zipfile; - - std::vector layout_text; - util::xml::data_node *layout_xml = nullptr; - - int i; - - auto newrpk = new rpk(options, system_name); - - try - { - /* open the ZIP file */ - ziperr = util::archive_file::open_zip(filename, zipfile); - if (ziperr != util::archive_file::error::NONE) throw rpk_exception(RPK_NOT_ZIP_FORMAT); - - /* find the layout.xml file */ - if (find_file(*zipfile, "layout.xml", 0) < 0) throw rpk_exception(RPK_MISSING_LAYOUT); - - /* reserve space for the layout file contents (+1 for the termination) */ - layout_text.resize(zipfile->current_uncompressed_length() + 1); - - /* uncompress the layout text */ - ziperr = zipfile->decompress(&layout_text[0], zipfile->current_uncompressed_length()); - if (ziperr != util::archive_file::error::NONE) - { - if (ziperr == util::archive_file::error::UNSUPPORTED) throw rpk_exception(RPK_ZIP_UNSUPPORTED); - else throw rpk_exception(RPK_ZIP_ERROR); - } - - layout_text[zipfile->current_uncompressed_length()] = '\0'; // Null-terminate - - /* parse the layout text */ - layout_xml = util::xml::data_node::string_read(&layout_text[0], nullptr); - if (!layout_xml) throw rpk_exception(RPK_XML_ERROR); - - // Now we work within the XML tree - - // romset is the root node - util::xml::data_node const *const romset_node = layout_xml->get_child("romset"); - if (!romset_node) throw rpk_exception(RPK_INVALID_LAYOUT, "document element must be "); - - // resources is a child of romset - util::xml::data_node const *const resources_node = romset_node->get_child("resources"); - if (!resources_node) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a child"); - - // configuration is a child of romset; we're actually interested in ... - util::xml::data_node const *const configuration_node = romset_node->get_child("configuration"); - if (!configuration_node) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a child"); - - // ... pcb, which is a child of configuration - util::xml::data_node const *const pcb_node = configuration_node->get_child("pcb"); - if (!pcb_node) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a child"); - - // We'll try to find the PCB type on the provided type list. - char const *const pcb_type = pcb_node->get_attribute_string("type", nullptr); - if (!pcb_type) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a 'type' attribute"); - if (TRACE_RPK) printf("gromport/RPK: Cartridge says it has PCB type '%s'\n", pcb_type); - - i=0; - do - { - if (strcmp(pcb_type, m_types[i].name)==0) - { - newrpk->m_type = m_types[i].id; - break; - } - i++; - } while (m_types[i].id != 0); - - if (m_types[i].id==0) throw rpk_exception(RPK_UNKNOWN_PCB_TYPE); - - // Find the sockets and load their respective resource - for (util::xml::data_node const *socket_node = pcb_node->get_first_child(); socket_node != nullptr; socket_node = socket_node->get_next_sibling()) - { - if (strcmp(socket_node->get_name(), "socket")!=0) throw rpk_exception(RPK_INVALID_LAYOUT, " element has only children"); - char const *const id = socket_node->get_attribute_string("id", nullptr); - if (!id) throw rpk_exception(RPK_INVALID_LAYOUT, " must have an 'id' attribute"); - char const *const uses_name = socket_node->get_attribute_string("uses", nullptr); - if (!uses_name) throw rpk_exception(RPK_INVALID_LAYOUT, " must have a 'uses' attribute"); - - bool found = false; - // Locate the resource node - for (util::xml::data_node const *resource_node = resources_node->get_first_child(); resource_node != nullptr; resource_node = resource_node->get_next_sibling()) - { - char const *const resource_name = resource_node->get_attribute_string("id", nullptr); - if (!resource_name) throw rpk_exception(RPK_INVALID_LAYOUT, "resource node must have an 'id' attribute"); - - if (strcmp(resource_name, uses_name)==0) - { - // found it - if (strcmp(resource_node->get_name(), "rom")==0) - { - newrpk->add_socket(id, load_rom_resource(*zipfile, resource_node, id)); - } - else - { - if (strcmp(resource_node->get_name(), "ram")==0) - { - newrpk->add_socket(id, load_ram_resource(options, resource_node, id, system_name)); - } - else throw rpk_exception(RPK_INVALID_LAYOUT, "resource node must be or "); - } - found = true; - } - } - if (!found) throw rpk_exception(RPK_INVALID_RESOURCE_REF, uses_name); - } - } - catch (rpk_exception &) - { - newrpk->close(); - if (layout_xml) layout_xml->file_free(); - - // rethrow the exception - throw; - } - - if (layout_xml) layout_xml->file_free(); - - return newrpk; -} diff --git a/src/devices/bus/ti99x/gromport.h b/src/devices/bus/ti99x/gromport.h deleted file mode 100644 index 59aff16bb83..00000000000 --- a/src/devices/bus/ti99x/gromport.h +++ /dev/null @@ -1,466 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/*************************************************************************** - Gromport (Cartridge port) of the TI-99 consoles - - For details see gromport.c -***************************************************************************/ - -#ifndef MAME_BUS_TI99X_GROMPORT_H -#define MAME_BUS_TI99X_GROMPORT_H - -#pragma once - -#include "ti99defs.h" -#include "machine/tmc0430.h" -#include "softlist_dev.h" - - -DECLARE_DEVICE_TYPE(TI99_GROMPORT, ti99_gromport_device) - -class ti99_cartridge_connector_device; - -class ti99_gromport_device : public bus8z_device, public device_slot_interface -{ -public: - ti99_gromport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(crureadz); - DECLARE_WRITE8_MEMBER(cruwrite); - DECLARE_WRITE_LINE_MEMBER(ready_line); - - DECLARE_WRITE_LINE_MEMBER(romgq_line); - - // Combined GROM select lines - DECLARE_WRITE8_MEMBER(set_gromlines); - - DECLARE_WRITE_LINE_MEMBER(gclock_in); - - static void set_mask(device_t &device, int mask) { downcast(device).m_mask = mask; } - - template static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) { return downcast(device).m_console_ready.set_callback(std::forward(cb)); } - template static devcb_base &static_set_reset_callback(device_t &device, Object &&cb) { return downcast(device).m_console_reset.set_callback(std::forward(cb)); } - - void cartridge_inserted(); - bool is_grom_idle(); - -protected: - virtual void device_start() override; - virtual void device_reset() override; - virtual void device_config_complete() override; - virtual ioport_constructor device_input_ports() const override; - -private: - ti99_cartridge_connector_device* m_connector; - bool m_reset_on_insert; - devcb_write_line m_console_ready; - devcb_write_line m_console_reset; - int m_mask; - int m_romgq; -}; - -SLOT_INTERFACE_EXTERN(gromport4); -SLOT_INTERFACE_EXTERN(gromport8); - -#define MCFG_GROMPORT4_ADD( _tag ) \ - MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \ - ti99_gromport_device::set_mask(*device, 0x1fff); \ - MCFG_DEVICE_SLOT_INTERFACE(gromport4, "single", false) - -#define MCFG_GROMPORT8_ADD( _tag ) \ - MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \ - ti99_gromport_device::set_mask(*device, 0x3fff); \ - MCFG_DEVICE_SLOT_INTERFACE(gromport8, "single", false) - -#define MCFG_GROMPORT_READY_HANDLER( _ready ) \ - devcb = &ti99_gromport_device::static_set_ready_callback( *device, DEVCB_##_ready ); - -#define MCFG_GROMPORT_RESET_HANDLER( _reset ) \ - devcb = &ti99_gromport_device::static_set_reset_callback( *device, DEVCB_##_reset ); - -/****************************************************************************/ - -class ti99_cartridge_pcb; - -class ti99_cartridge_device : public bus8z_device, public device_image_interface -{ -public: - ti99_cartridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(crureadz); - DECLARE_WRITE8_MEMBER(cruwrite); - - DECLARE_WRITE_LINE_MEMBER(ready_line); - DECLARE_WRITE_LINE_MEMBER(romgq_line); - DECLARE_WRITE8_MEMBER(set_gromlines); - - DECLARE_WRITE_LINE_MEMBER(gclock_in); - - bool is_available() { return m_pcb != nullptr; } - void set_slot(int i); - bool is_grom_idle(); - -protected: - virtual void device_start() override { } - virtual void device_config_complete() override; - virtual machine_config_constructor device_mconfig_additions() const override; - virtual const tiny_rom_entry* device_rom_region() const override; - - // Image handling: implementation of methods which are abstract in the parent - image_init_result call_load() override; - void call_unload() override; - virtual const software_list_loader &get_software_list_loader() const override { return rom_software_list_loader::instance(); } - - void prepare_cartridge(); - - // device_image_interface - iodevice_t image_type() const override { return IO_CARTSLOT; } - bool is_readable() const override { return true; } - bool is_writeable() const override { return false; } - bool is_creatable() const override { return false; } - bool must_be_loaded() const override { return false; } - bool is_reset_on_load() const override { return false; } - const char *image_interface() const override { return "ti99_cart"; } - const char *file_extensions() const override { return "rpk"; } - -private: - class rpk_socket; - class rpk_reader; - class rpk; - - bool m_readrom; - int m_pcbtype; - int m_slot; - int get_index_from_tagname(); - - std::unique_ptr m_pcb; // inbound - ti99_cartridge_connector_device* m_connector; // outbound - - // RPK which is associated to this cartridge - // When we close it, the contents are saved to NVRAM if available - rpk *m_rpk; -}; - -DECLARE_DEVICE_TYPE(TI99CART, ti99_cartridge_device) - -/****************************************************************************/ - -class ti99_cartridge_connector_device : public bus8z_device -{ -public: - virtual DECLARE_READ8Z_MEMBER(crureadz) = 0; - virtual DECLARE_WRITE8_MEMBER(cruwrite) = 0; - - virtual DECLARE_WRITE_LINE_MEMBER(romgq_line) = 0; - virtual DECLARE_WRITE8_MEMBER(set_gromlines) = 0; - - virtual DECLARE_WRITE_LINE_MEMBER(gclock_in) = 0; - - DECLARE_WRITE_LINE_MEMBER(ready_line); - - virtual void insert(int index, ti99_cartridge_device* cart) { m_gromport->cartridge_inserted(); } - virtual void remove(int index) { } - virtual bool is_grom_idle() = 0; - -protected: - ti99_cartridge_connector_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); - virtual void device_config_complete() override; - - ti99_gromport_device* m_gromport; - bool m_grom_selected; -}; - -/* - Single cartridge connector. -*/ -class ti99_single_cart_conn_device : public ti99_cartridge_connector_device -{ -public: - ti99_single_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(crureadz) override; - DECLARE_WRITE8_MEMBER(cruwrite) override; - DECLARE_WRITE_LINE_MEMBER(romgq_line) override; - DECLARE_WRITE8_MEMBER(set_gromlines) override; - DECLARE_WRITE_LINE_MEMBER(gclock_in) override; - - bool is_grom_idle() override; - -protected: - virtual void device_start() override; - virtual void device_reset() override; - virtual machine_config_constructor device_mconfig_additions() const override; - -private: - ti99_cartridge_device *m_cartridge; -}; - -/* - Multi cartridge connector. -*/ - -/* We set the number of slots to 4, although we may have up to 16. From a - logical point of view we could have 256, but the operating system only checks - the first 16 banks. */ - -class ti99_multi_cart_conn_device : public ti99_cartridge_connector_device -{ -public: - ti99_multi_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(crureadz) override; - DECLARE_WRITE8_MEMBER(cruwrite) override; - DECLARE_WRITE_LINE_MEMBER(romgq_line) override; - DECLARE_WRITE8_MEMBER(set_gromlines) override; - DECLARE_WRITE_LINE_MEMBER(gclock_in) override; - - void insert(int index, ti99_cartridge_device* cart) override; - void remove(int index) override; - DECLARE_INPUT_CHANGED_MEMBER( switch_changed ); - - bool is_grom_idle() override; - -protected: - static constexpr unsigned NUMBER_OF_CARTRIDGE_SLOTS = 4; - - virtual void device_start() override; - virtual void device_reset() override; - virtual machine_config_constructor device_mconfig_additions() const override; - virtual ioport_constructor device_input_ports() const override; - -private: - bool m_readrom; - int m_active_slot; - int m_fixed_slot; - int m_next_free_slot; - ti99_cartridge_device* m_cartridge[NUMBER_OF_CARTRIDGE_SLOTS]; - - void set_slot(int slotnumber); - int get_active_slot(bool changebase, offs_t offset); -}; - -/* - GRAM Kracker. -*/ -class ti99_gkracker_device : public ti99_cartridge_connector_device, public device_nvram_interface -{ -public: - ti99_gkracker_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(crureadz) override; - DECLARE_WRITE8_MEMBER(cruwrite) override; - DECLARE_WRITE_LINE_MEMBER(romgq_line) override; - DECLARE_WRITE8_MEMBER(set_gromlines) override; - DECLARE_WRITE_LINE_MEMBER(gclock_in) override; - - void insert(int index, ti99_cartridge_device* cart) override; - void remove(int index) override; - DECLARE_INPUT_CHANGED_MEMBER( gk_changed ); - - // We may have a cartridge plugged into the GK - bool is_grom_idle() override; - -protected: - virtual void device_start() override; - virtual void device_reset() override; - - virtual machine_config_constructor device_mconfig_additions() const override; - virtual const tiny_rom_entry* device_rom_region() const override; - virtual ioport_constructor device_input_ports() const override; - - // device_nvram_interface - void nvram_default() override; - void nvram_read(emu_file &file) override; - void nvram_write(emu_file &file) override; - -private: - int m_gk_switch[6]; // Used to cache the switch settings. - - bool m_romspace_selected; - int m_ram_page; - int m_grom_address; - uint8_t* m_ram_ptr; - uint8_t* m_grom_ptr; - - bool m_waddr_LSB; - - ti99_cartridge_device *m_cartridge; // guest cartridge - - // Just for proper initialization - void gk_install_menu(const char* menutext, int len, int ptr, int next, int start); -}; - -DECLARE_DEVICE_TYPE(TI99_GROMPORT_SINGLE, ti99_single_cart_conn_device) -DECLARE_DEVICE_TYPE(TI99_GROMPORT_MULTI, ti99_multi_cart_conn_device) -DECLARE_DEVICE_TYPE(TI99_GROMPORT_GK, ti99_gkracker_device) - -/****************************************************************************/ - -class ti99_cartridge_pcb -{ - friend class ti99_cartridge_device; -public: - ti99_cartridge_pcb(); - virtual ~ti99_cartridge_pcb() { } - -protected: - virtual DECLARE_READ8Z_MEMBER(readz); - virtual DECLARE_WRITE8_MEMBER(write); - virtual DECLARE_READ8Z_MEMBER(crureadz); - virtual DECLARE_WRITE8_MEMBER(cruwrite); - - DECLARE_WRITE_LINE_MEMBER(romgq_line); - virtual DECLARE_WRITE8_MEMBER(set_gromlines); - DECLARE_WRITE_LINE_MEMBER(gclock_in); - - DECLARE_READ8Z_MEMBER(gromreadz); - DECLARE_WRITE8_MEMBER(gromwrite); - inline void set_grom_pointer(int number, device_t *dev); - void set_cartridge(ti99_cartridge_device *cart); - const char* tag() { return m_tag; } - void set_tag(const char* tag) { m_tag = tag; } - bool is_grom_idle() { return m_grom_idle; } - - ti99_cartridge_device* m_cart; - tmc0430_device* m_grom[5]; - bool m_grom_idle; - int m_grom_size; - int m_rom_size; - int m_ram_size; - - uint8_t* m_rom_ptr; - uint8_t* m_ram_ptr; - bool m_romspace_selected; - int m_rom_page; // for some cartridge types - uint8_t* m_grom_ptr; // for gromemu - int m_grom_address; // for gromemu - int m_ram_page; // for super - const char* m_tag; - std::vector m_nvram; // for MiniMemory - std::vector m_ram; // for MBX -}; - -/******************** Standard cartridge ******************************/ - -class ti99_standard_cartridge : public ti99_cartridge_pcb -{ -public: -}; - -/*********** Paged cartridge (like Extended Basic) ********************/ - -class ti99_paged12k_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; -}; - -/*********** Paged cartridge (others) ********************/ - -class ti99_paged16k_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; -}; - -/********************** Mini Memory ***********************************/ - -class ti99_minimem_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; -}; - -/********************* Super Space II *********************************/ - -class ti99_super_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(crureadz) override; - DECLARE_WRITE8_MEMBER(cruwrite) override; -}; - -/************************* MBX ***************************************/ - -class ti99_mbx_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; -}; - -/********************** Paged 379i ************************************/ - -class ti99_paged379i_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; -private: - int get_paged379i_bank(int rompage); -}; - -/********************** Paged 378 ************************************/ - -class ti99_paged378_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; -}; - -/********************** Paged 377 ************************************/ - -class ti99_paged377_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; -}; - -/********************** Paged CRU ************************************/ - -class ti99_pagedcru_cartridge : public ti99_cartridge_pcb -{ -public: - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(crureadz) override; - DECLARE_WRITE8_MEMBER(cruwrite) override; -}; - -/********************** GROM emulation cartridge ************************************/ - -class ti99_gromemu_cartridge : public ti99_cartridge_pcb -{ -public: - ti99_gromemu_cartridge(): m_waddr_LSB(false), m_grom_selected(false), m_grom_read_mode(false), m_grom_address_mode(false) - { m_grom_address = 0; } - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - DECLARE_READ8Z_MEMBER(gromemureadz); - DECLARE_WRITE8_MEMBER(gromemuwrite); - DECLARE_WRITE8_MEMBER(set_gromlines) override; - -private: - bool m_waddr_LSB; - bool m_grom_selected; - bool m_grom_read_mode; - bool m_grom_address_mode; -}; - -#endif // MAME_BUS_TI99X_GROMPORT_H diff --git a/src/devices/bus/ti99x/ti99defs.cpp b/src/devices/bus/ti99x/ti99defs.cpp deleted file mode 100644 index b1928908ae8..00000000000 --- a/src/devices/bus/ti99x/ti99defs.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -#include "emu.h" -#include "ti99defs.h" - -DEFINE_DEVICE_TYPE(EVPC_CONN, evpc_clock_connector, "ti99_evpc_clock", "EVPC clock connector") - -evpc_clock_connector::evpc_clock_connector(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, EVPC_CONN, tag, owner, clock) - , m_vdpint(*this) -{ -} - -void evpc_clock_connector::device_start() -{ - m_vdpint.resolve(); -} diff --git a/src/devices/bus/ti99x/ti99defs.h b/src/devices/bus/ti99x/ti99defs.h deleted file mode 100644 index 4f25b6ca606..00000000000 --- a/src/devices/bus/ti99x/ti99defs.h +++ /dev/null @@ -1,205 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/**************************************************************************** - - Common definitions for TI family - Should be included in each component. - - Michael Zapf - -*****************************************************************************/ - -#ifndef MAME_BUS_TI99X_TI99DEFS_H -#define MAME_BUS_TI99X_TI99DEFS_H - -#pragma once - -// TI-99/4(A) -#define TISOUNDCHIP_TAG "soundchip" -#define GROMPORT_TAG "gromport" -#define PERIBOX_TAG "peb" -#define VDP_TAG "vdp" -#define TMS9901_TAG "tms9901" -#define SCREEN_TAG "screen" -#define GROM0_TAG "console_grom_0" -#define GROM1_TAG "console_grom_1" -#define GROM2_TAG "console_grom_2" -#define JOYPORT_TAG "joyport" -#define EVPC_CONN_TAG "evpc_conn" -#define DATAMUX_TAG "datamux_16_8" -#define PADRAM_TAG "scratchpad" -#define EXPRAM_TAG "internal_32k_mod" -#define DSRROM "dsrrom" -#define CONSOLEROM "console_rom" -#define CONSOLEGROM "cons_grom" - -#define VDPFREQ XTAL_10_738635MHz -#define GROMFREQ VDPFREQ/24 - -// TI-99/8 -#define SRAM_TAG "sram8" -#define DRAM_TAG "dram8" -#define MAPPER_TAG "mapper" -#define MAINBOARD8_TAG "mainboard8" -#define SPEECHSYN_TAG "speech" - -#define ROM0_REG "rom0_region" -#define ROM1_REG "rom1_region" -#define PASCAL_REG "pascal_region" -#define SYSGROM_REG "sysgrom_region" -#define GROMLIB1_REG "gromlib1_region" -#define GROMLIB2_REG "gromlib2_region" -#define GROMLIB3_REG "gromlib3_region" -#define SPEECHROM_REG "speech_region" - -#define GROMLIB_TAG "gromlib" -#define SYSGROM_TAG GROMLIB_TAG "0" -#define SYSGROM0_TAG SYSGROM_TAG "_0" -#define SYSGROM1_TAG SYSGROM_TAG "_1" -#define SYSGROM2_TAG SYSGROM_TAG "_2" - -#define GLIB1_TAG GROMLIB_TAG "1" -#define GLIB10_TAG GLIB1_TAG "_0" -#define GLIB11_TAG GLIB1_TAG "_1" -#define GLIB12_TAG GLIB1_TAG "_2" -#define GLIB13_TAG GLIB1_TAG "_3" -#define GLIB14_TAG GLIB1_TAG "_4" -#define GLIB15_TAG GLIB1_TAG "_5" -#define GLIB16_TAG GLIB1_TAG "_6" -#define GLIB17_TAG GLIB1_TAG "_7" - -#define GLIB2_TAG GROMLIB_TAG "2" -#define GLIB20_TAG GLIB2_TAG "_0" -#define GLIB21_TAG GLIB2_TAG "_1" -#define GLIB22_TAG GLIB2_TAG "_2" -#define GLIB23_TAG GLIB2_TAG "_3" -#define GLIB24_TAG GLIB2_TAG "_4" -#define GLIB25_TAG GLIB2_TAG "_5" -#define GLIB26_TAG GLIB2_TAG "_6" -#define GLIB27_TAG GLIB2_TAG "_7" - -#define GLIB3_TAG GROMLIB_TAG "3" -#define GLIB30_TAG GLIB3_TAG "_0" -#define GLIB31_TAG GLIB3_TAG "_1" -#define GLIB32_TAG GLIB3_TAG "_2" - -// v9938 -#define COLORBUS_TAG "colorbus" - -// Geneve -#define GKEYBOARD_TAG "gkeyboard" -#define GMAPPER_TAG "gmapper" -#define GMOUSE_TAG "gmouse" -#define GCLOCK_TAG "mm58274c" -#define PFM512_TAG "pfm512" -#define PFM512A_TAG "pfm512a" - -#define READ16Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint16_t *value, ATTR_UNUSED uint16_t mem_mask) -#define DECLARE_READ16Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint16_t *value, ATTR_UNUSED uint16_t mem_mask = 0xffff) -#define READ8Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint8_t *value, ATTR_UNUSED uint8_t mem_mask) -#define DECLARE_READ8Z_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED uint8_t *value, ATTR_UNUSED uint8_t 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 - -/* - Base class for all devices that have a special read method. This read - methods differs from the standard READ8/16 by allowing for a "high-impedance" - state "Z" which means that the bus lines are not changed. That way, we can - built busses with parallel devices as in the real machine, and only the - active device changes the bus lines. -*/ -class bus8z_device : public device_t -{ -protected: - bus8z_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock) { } -public: - virtual DECLARE_READ8Z_MEMBER(readz) = 0; - virtual DECLARE_WRITE8_MEMBER(write) = 0; - virtual DECLARE_SETADDRESS_DBIN_MEMBER( setaddress_dbin ) { } -}; - -class bus16z_device : device_t -{ -protected: - bus16z_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock) { } -public: - virtual DECLARE_READ16Z_MEMBER(read16z) = 0; - virtual DECLARE_WRITE16_MEMBER(write16) = 0; - 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 - - 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. - -****************************************************************************/ - -DECLARE_DEVICE_TYPE(EVPC_CONN, 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_t clock); - - template static devcb_base &static_set_vdpint_callback(device_t &device, Object &&cb) - { - return downcast(device).m_vdpint.set_callback(std::forward(cb)); - } - - WRITE_LINE_MEMBER( vclock_line ) { m_vdpint(state); } - -protected: - void device_start() override; - -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 -****************************************************************************/ - -enum -{ - GM_TURBO = 1, - GM_TIM = 2 -}; - -enum -{ - GENEVE_098 = 0, - GENEVE_100, - GENEVE_PFM512, - GENEVE_PFM512A -}; - -#endif // MAME_BUS_TI99X_TI99DEFS_H diff --git a/src/mame/drivers/geneve.cpp b/src/mame/drivers/geneve.cpp index 2eea30ac2af..fb21be489e1 100644 --- a/src/mame/drivers/geneve.cpp +++ b/src/mame/drivers/geneve.cpp @@ -1,7 +1,7 @@ // license:LGPL-2.1+ // copyright-holders:Michael Zapf /****************************************************************************** - MESS Driver for the Myarc Geneve 9640. + Myarc Geneve 9640. The Geneve has two operation modes. One is compatible with the TI-99/4a, the other is not. @@ -202,14 +202,13 @@ Rewritten 2012 by Michael Zapf ******************************************************************************/ - #include "emu.h" #include "cpu/tms9900/tms9995.h" #include "machine/tms9901.h" #include "machine/mm58274c.h" #include "sound/sn76496.h" -#include "bus/ti99x/genboard.h" +#include "bus/ti99/internal/genboard.h" #include "bus/ti99/colorbus/colorbus.h" #include "bus/ti99/joyport/joyport.h" @@ -263,11 +262,11 @@ public: required_device m_cpu; required_device m_tms9901; - required_device m_keyboard; - required_device m_mapper; - required_device m_peribox; - required_device m_joyport; - required_device m_colorbus; + required_device m_keyboard; + required_device m_mapper; + required_device m_peribox; + required_device m_joyport; + required_device m_colorbus; DECLARE_WRITE_LINE_MEMBER( inta ); DECLARE_WRITE_LINE_MEMBER( intb ); @@ -295,7 +294,7 @@ public: */ static ADDRESS_MAP_START(memmap, AS_PROGRAM, 8, geneve_state) - AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(GMAPPER_TAG, geneve_mapper_device, readm, writem) AM_DEVSETOFFSET(GMAPPER_TAG, geneve_mapper_device, setoffset) + AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, readm, writem) AM_DEVSETOFFSET(GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, setoffset) ADDRESS_MAP_END /* @@ -318,12 +317,12 @@ ADDRESS_MAP_END static INPUT_PORTS_START(geneve) PORT_START( "MODE" ) - PORT_CONFNAME( 0x01, 0x00, "Operating mode" ) PORT_CHANGED_MEMBER(PERIBOX_TAG, peribox_device, genmod_changed, 0) + PORT_CONFNAME( 0x01, 0x00, "Operating mode" ) PORT_CHANGED_MEMBER(PERIBOX_TAG, bus::ti99::peb::peribox_device, genmod_changed, 0) PORT_CONFSETTING( 0x00, "Standard" ) PORT_CONFSETTING( GENMOD, "GenMod" ) PORT_START( "BOOTROM" ) - PORT_CONFNAME( 0x03, GENEVE_098, "Boot ROM" ) PORT_CHANGED_MEMBER(GMAPPER_TAG, geneve_mapper_device, settings_changed, 3) + PORT_CONFNAME( 0x03, GENEVE_098, "Boot ROM" ) PORT_CHANGED_MEMBER(GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, settings_changed, 3) PORT_CONFSETTING( GENEVE_098, "Version 0.98" ) PORT_CONFSETTING( GENEVE_100, "Version 1.00" ) PORT_CONFSETTING( GENEVE_PFM512, "PFM 512" ) @@ -336,10 +335,10 @@ static INPUT_PORTS_START(geneve) PORT_CONFSETTING( 0x02, "384 KiB" ) PORT_START( "GENMODDIPS" ) - PORT_DIPNAME( GM_TURBO, 0x00, "Genmod Turbo mode") PORT_CONDITION( "MODE", 0x01, EQUALS, GENMOD ) PORT_CHANGED_MEMBER(GMAPPER_TAG, geneve_mapper_device, settings_changed, 1) + PORT_DIPNAME( GM_TURBO, 0x00, "Genmod Turbo mode") PORT_CONDITION( "MODE", 0x01, EQUALS, GENMOD ) PORT_CHANGED_MEMBER(GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, settings_changed, 1) PORT_CONFSETTING( 0x00, DEF_STR( Off )) PORT_CONFSETTING( GM_TURBO, DEF_STR( On )) - PORT_DIPNAME( GM_TIM, GM_TIM, "Genmod TI mode") PORT_CONDITION( "MODE", 0x01, EQUALS, GENMOD ) PORT_CHANGED_MEMBER(GMAPPER_TAG, geneve_mapper_device, settings_changed, 2) + PORT_DIPNAME( GM_TIM, GM_TIM, "Genmod TI mode") PORT_CONDITION( "MODE", 0x01, EQUALS, GENMOD ) PORT_CHANGED_MEMBER(GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, settings_changed, 2) PORT_CONFSETTING( 0x00, DEF_STR( Off )) PORT_CONFSETTING( GM_TIM, DEF_STR( On )) @@ -703,12 +702,12 @@ static MACHINE_CONFIG_START( geneve_60hz ) MCFG_TMS9901_P0_HANDLER( WRITELINE( geneve_state, peripheral_bus_reset) ) MCFG_TMS9901_P1_HANDLER( WRITELINE( geneve_state, VDP_reset) ) MCFG_TMS9901_P2_HANDLER( WRITELINE( geneve_state, joystick_select) ) - MCFG_TMS9901_P4_HANDLER( DEVWRITELINE( GMAPPER_TAG, geneve_mapper_device, pfm_select_lsb) ) // new for PFM - MCFG_TMS9901_P5_HANDLER( DEVWRITELINE( GMAPPER_TAG, geneve_mapper_device, pfm_output_enable) ) // new for PFM - MCFG_TMS9901_P6_HANDLER( DEVWRITELINE( GKEYBOARD_TAG, geneve_keyboard_device, reset_line) ) + MCFG_TMS9901_P4_HANDLER( DEVWRITELINE( GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, pfm_select_lsb) ) // new for PFM + MCFG_TMS9901_P5_HANDLER( DEVWRITELINE( GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, pfm_output_enable) ) // new for PFM + MCFG_TMS9901_P6_HANDLER( DEVWRITELINE( GKEYBOARD_TAG, bus::ti99::internal::geneve_keyboard_device, reset_line) ) MCFG_TMS9901_P7_HANDLER( WRITELINE( geneve_state, extbus_wait_states) ) MCFG_TMS9901_P9_HANDLER( WRITELINE( geneve_state, video_wait_states) ) - MCFG_TMS9901_P13_HANDLER( DEVWRITELINE( GMAPPER_TAG, geneve_mapper_device, pfm_select_msb) ) // new for PFM + MCFG_TMS9901_P13_HANDLER( DEVWRITELINE( GMAPPER_TAG, bus::ti99::internal::geneve_mapper_device, pfm_select_msb) ) // new for PFM MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( geneve_state, tms9901_interrupt) ) // Mapper diff --git a/src/mame/drivers/ti99_4p.cpp b/src/mame/drivers/ti99_4p.cpp index efa0632b5ef..304009a6139 100644 --- a/src/mame/drivers/ti99_4p.cpp +++ b/src/mame/drivers/ti99_4p.cpp @@ -191,8 +191,8 @@ private: required_device m_cpu; required_device m_tms9901; required_device m_cassette; - required_device m_peribox; - required_device m_joyport; + required_device m_peribox; + required_device m_joyport; required_device m_scratchpad; required_device m_amsram; diff --git a/src/mame/drivers/ti99_4x.cpp b/src/mame/drivers/ti99_4x.cpp index 0307aebe273..cfd2ee57201 100644 --- a/src/mame/drivers/ti99_4x.cpp +++ b/src/mame/drivers/ti99_4x.cpp @@ -46,9 +46,10 @@ #include "machine/tms9901.h" #include "imagedev/cassette.h" -#include "bus/ti99x/ti99defs.h" -#include "bus/ti99x/datamux.h" -#include "bus/ti99x/gromport.h" +#include "bus/ti99/ti99defs.h" +#include "bus/ti99/internal/datamux.h" +#include "bus/ti99/internal/gromport.h" +#include "bus/ti99/internal/evpcconn.h" #include "bus/ti99/joyport/joyport.h" #include "bus/ti99/peb/peribox.h" @@ -165,10 +166,10 @@ private: // Connected devices required_device m_cpu; required_device m_tms9901; - required_device m_gromport; - required_device m_peribox; - required_device m_joyport; - required_device m_datamux; + required_device m_gromport; + required_device m_peribox; + required_device m_joyport; + required_device m_datamux; optional_device m_video; required_device m_cassette1; required_device m_cassette2; @@ -207,7 +208,7 @@ enum */ static ADDRESS_MAP_START(memmap, AS_PROGRAM, 16, ti99_4x_state) ADDRESS_MAP_GLOBAL_MASK(0xffff) - AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(DATAMUX_TAG, ti99_datamux_device, read, write) AM_DEVSETOFFSET(DATAMUX_TAG, ti99_datamux_device, setoffset) + AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(DATAMUX_TAG, bus::ti99::internal::datamux_device, read, write) AM_DEVSETOFFSET(DATAMUX_TAG, bus::ti99::internal::datamux_device, setoffset) ADDRESS_MAP_END /* @@ -878,7 +879,7 @@ static MACHINE_CONFIG_START( ti99_4 ) MCFG_DEVICE_ADD( PERIBOX_TAG, PERIBOX, 0) MCFG_PERIBOX_INTA_HANDLER( WRITELINE(ti99_4x_state, extint) ) MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_4x_state, notconnected) ) - MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(DATAMUX_TAG, ti99_datamux_device, ready_line) ) + MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(DATAMUX_TAG, bus::ti99::internal::datamux_device, ready_line) ) // Sound hardware MCFG_SPEAKER_STANDARD_MONO("sound_out") @@ -998,7 +999,7 @@ static MACHINE_CONFIG_START( ti99_4a ) MCFG_DEVICE_ADD( PERIBOX_TAG, PERIBOX, 0) MCFG_PERIBOX_INTA_HANDLER( WRITELINE(ti99_4x_state, extint) ) MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_4x_state, notconnected) ) - MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(DATAMUX_TAG, ti99_datamux_device, ready_line) ) + MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(DATAMUX_TAG, bus::ti99::internal::datamux_device, ready_line) ) // Sound hardware MCFG_SPEAKER_STANDARD_MONO("sound_out") @@ -1162,7 +1163,7 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz ) MCFG_DEVICE_ADD( PERIBOX_TAG, PERIBOX_EV, 0) MCFG_PERIBOX_INTA_HANDLER( WRITELINE(ti99_4x_state, extint) ) MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_4x_state, notconnected) ) - MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(DATAMUX_TAG, ti99_datamux_device, ready_line) ) + MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(DATAMUX_TAG, bus::ti99::internal::datamux_device, ready_line) ) // Cassette drives MCFG_SPEAKER_STANDARD_MONO("cass_out") diff --git a/src/mame/drivers/ti99_8.cpp b/src/mame/drivers/ti99_8.cpp index 8ce0acb0f04..62c6cb9f15c 100644 --- a/src/mame/drivers/ti99_8.cpp +++ b/src/mame/drivers/ti99_8.cpp @@ -173,7 +173,7 @@ Known Issues (MZ, 2010-11-07) #include "emu.h" #include "cpu/tms9900/tms9995.h" -#include "bus/ti99x/ti99defs.h" +#include "bus/ti99/ti99defs.h" #include "sound/sn76496.h" #include "sound/wave.h" @@ -181,8 +181,8 @@ Known Issues (MZ, 2010-11-07) #include "machine/tmc0430.h" #include "imagedev/cassette.h" -#include "bus/ti99x/998board.h" -#include "bus/ti99x/gromport.h" +#include "bus/ti99/internal/998board.h" +#include "bus/ti99/internal/gromport.h" #include "bus/ti99/joyport/joyport.h" #include "bus/ti99/peb/peribox.h" @@ -275,10 +275,10 @@ private: // Connected devices required_device m_cpu; required_device m_tms9901; - required_device m_gromport; - required_device m_peribox; - required_device m_mainboard; - required_device m_joyport; + required_device m_gromport; + required_device m_peribox; + required_device m_mainboard; + required_device m_joyport; required_device m_cassette; }; @@ -287,7 +287,7 @@ private: job to the mapper completely. */ static ADDRESS_MAP_START(memmap, AS_PROGRAM, 8, ti99_8_state) - AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(MAINBOARD8_TAG, mainboard8_device, read, write) AM_DEVSETOFFSET(MAINBOARD8_TAG, mainboard8_device, setoffset) + AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, read, write) AM_DEVSETOFFSET(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, setoffset) ADDRESS_MAP_END /* @@ -722,7 +722,7 @@ static MACHINE_CONFIG_START( ti99_8 ) MCFG_TMS9995_EXTOP_HANDLER( WRITE8(ti99_8_state, external_operation) ) MCFG_TMS9995_CLKOUT_HANDLER( WRITELINE(ti99_8_state, clock_out) ) MCFG_TMS9995_DBIN_HANDLER( WRITELINE(ti99_8_state, dbin_line) ) - MCFG_TMS9995_HOLDA_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, holda_line) ) + MCFG_TMS9995_HOLDA_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, holda_line) ) MCFG_MACHINE_START_OVERRIDE(ti99_8_state, ti99_8 ) MCFG_MACHINE_RESET_OVERRIDE(ti99_8_state, ti99_8 ) @@ -734,8 +734,8 @@ static MACHINE_CONFIG_START( ti99_8 ) MCFG_TMS9901_P1_HANDLER( WRITELINE( ti99_8_state, keyC1) ) MCFG_TMS9901_P2_HANDLER( WRITELINE( ti99_8_state, keyC2) ) MCFG_TMS9901_P3_HANDLER( WRITELINE( ti99_8_state, keyC3) ) - MCFG_TMS9901_P4_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, mainboard8_device, crus_in) ) - MCFG_TMS9901_P5_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, mainboard8_device, ptgen_in) ) + MCFG_TMS9901_P4_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, crus_in) ) + MCFG_TMS9901_P5_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptgen_in) ) MCFG_TMS9901_P6_HANDLER( WRITELINE( ti99_8_state, cassette_motor) ) MCFG_TMS9901_P8_HANDLER( WRITELINE( ti99_8_state, audio_gate) ) MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_8_state, cassette_output) ) @@ -748,7 +748,7 @@ static MACHINE_CONFIG_START( ti99_8 ) MCFG_MAINBOARD8_HOLD_CALLBACK(WRITELINE(ti99_8_state, cpu_hold)) MCFG_GROMPORT8_ADD( GROMPORT_TAG ) - MCFG_GROMPORT_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready) ) + MCFG_GROMPORT_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, system_grom_ready) ) MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_8_state, console_reset) ) // RAM @@ -766,20 +766,20 @@ static MACHINE_CONFIG_START( ti99_8 ) MCFG_DEVICE_ADD( PERIBOX_TAG, PERIBOX_998, 0) MCFG_PERIBOX_INTA_HANDLER( WRITELINE(ti99_8_state, extint) ) MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_8_state, notconnected) ) - MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, pbox_ready) ) + MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, pbox_ready) ) // Sound hardware MCFG_SPEAKER_STANDARD_MONO("sound_out") MCFG_SOUND_ADD(TISOUNDCHIP_TAG, SN76496, 3579545) /* 3.579545 MHz */ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "sound_out", 0.75) - MCFG_SN76496_READY_HANDLER(DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, sound_ready)) + MCFG_SN76496_READY_HANDLER(DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, sound_ready)) // Speech hardware // Note: SPEECHROM uses its tag for referencing the region MCFG_DEVICE_ADD(SPEECHROM_REG, SPEECHROM, 0) MCFG_SPEAKER_STANDARD_MONO("speech_out") MCFG_SOUND_ADD(SPEECHSYN_TAG, CD2501ECD, 640000L) - MCFG_TMS52XX_READYQ_HANDLER(DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, speech_ready)) + MCFG_TMS52XX_READYQ_HANDLER(DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, speech_ready)) MCFG_TMS52XX_SPEECHROM(SPEECHROM_REG) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speech_out", 0.50) @@ -790,31 +790,31 @@ static MACHINE_CONFIG_START( ti99_8 ) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25) // GROM library - MCFG_GROM_ADD( SYSGROM0_TAG, 0, SYSGROM_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready)) - MCFG_GROM_ADD( SYSGROM1_TAG, 1, SYSGROM_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready)) - MCFG_GROM_ADD( SYSGROM2_TAG, 2, SYSGROM_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready)) - - MCFG_GROM_ADD( GLIB10_TAG, 0, GROMLIB1_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - MCFG_GROM_ADD( GLIB11_TAG, 1, GROMLIB1_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - MCFG_GROM_ADD( GLIB12_TAG, 2, GROMLIB1_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - MCFG_GROM_ADD( GLIB13_TAG, 3, GROMLIB1_REG, 0x6000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - MCFG_GROM_ADD( GLIB14_TAG, 4, GROMLIB1_REG, 0x8000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - MCFG_GROM_ADD( GLIB15_TAG, 5, GROMLIB1_REG, 0xa000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - MCFG_GROM_ADD( GLIB16_TAG, 6, GROMLIB1_REG, 0xc000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - MCFG_GROM_ADD( GLIB17_TAG, 7, GROMLIB1_REG, 0xe000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, ptts_grom_ready)) - - MCFG_GROM_ADD( GLIB20_TAG, 0, GROMLIB2_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - MCFG_GROM_ADD( GLIB21_TAG, 1, GROMLIB2_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - MCFG_GROM_ADD( GLIB22_TAG, 2, GROMLIB2_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - MCFG_GROM_ADD( GLIB23_TAG, 3, GROMLIB2_REG, 0x6000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - MCFG_GROM_ADD( GLIB24_TAG, 4, GROMLIB2_REG, 0x8000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - MCFG_GROM_ADD( GLIB25_TAG, 5, GROMLIB2_REG, 0xa000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - MCFG_GROM_ADD( GLIB26_TAG, 6, GROMLIB2_REG, 0xc000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - MCFG_GROM_ADD( GLIB27_TAG, 7, GROMLIB2_REG, 0xe000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p8_grom_ready)) - - MCFG_GROM_ADD( GLIB30_TAG, 0, GROMLIB3_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p3_grom_ready)) - MCFG_GROM_ADD( GLIB31_TAG, 1, GROMLIB3_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p3_grom_ready)) - MCFG_GROM_ADD( GLIB32_TAG, 2, GROMLIB3_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, p3_grom_ready)) + MCFG_GROM_ADD( SYSGROM0_TAG, 0, SYSGROM_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, system_grom_ready)) + MCFG_GROM_ADD( SYSGROM1_TAG, 1, SYSGROM_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, system_grom_ready)) + MCFG_GROM_ADD( SYSGROM2_TAG, 2, SYSGROM_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, system_grom_ready)) + + MCFG_GROM_ADD( GLIB10_TAG, 0, GROMLIB1_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + MCFG_GROM_ADD( GLIB11_TAG, 1, GROMLIB1_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + MCFG_GROM_ADD( GLIB12_TAG, 2, GROMLIB1_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + MCFG_GROM_ADD( GLIB13_TAG, 3, GROMLIB1_REG, 0x6000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + MCFG_GROM_ADD( GLIB14_TAG, 4, GROMLIB1_REG, 0x8000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + MCFG_GROM_ADD( GLIB15_TAG, 5, GROMLIB1_REG, 0xa000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + MCFG_GROM_ADD( GLIB16_TAG, 6, GROMLIB1_REG, 0xc000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + MCFG_GROM_ADD( GLIB17_TAG, 7, GROMLIB1_REG, 0xe000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, ptts_grom_ready)) + + MCFG_GROM_ADD( GLIB20_TAG, 0, GROMLIB2_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + MCFG_GROM_ADD( GLIB21_TAG, 1, GROMLIB2_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + MCFG_GROM_ADD( GLIB22_TAG, 2, GROMLIB2_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + MCFG_GROM_ADD( GLIB23_TAG, 3, GROMLIB2_REG, 0x6000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + MCFG_GROM_ADD( GLIB24_TAG, 4, GROMLIB2_REG, 0x8000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + MCFG_GROM_ADD( GLIB25_TAG, 5, GROMLIB2_REG, 0xa000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + MCFG_GROM_ADD( GLIB26_TAG, 6, GROMLIB2_REG, 0xc000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + MCFG_GROM_ADD( GLIB27_TAG, 7, GROMLIB2_REG, 0xe000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p8_grom_ready)) + + MCFG_GROM_ADD( GLIB30_TAG, 0, GROMLIB3_REG, 0x0000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p3_grom_ready)) + MCFG_GROM_ADD( GLIB31_TAG, 1, GROMLIB3_REG, 0x2000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p3_grom_ready)) + MCFG_GROM_ADD( GLIB32_TAG, 2, GROMLIB3_REG, 0x4000, DEVWRITELINE(MAINBOARD8_TAG, bus::ti99::internal::mainboard8_device, p3_grom_ready)) // Joystick port MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG ) -- cgit v1.2.3