diff options
-rw-r--r-- | scripts/src/bus.lua | 2 | ||||
-rw-r--r-- | src/devices/bus/ti99_peb/pcode.cpp | 208 | ||||
-rw-r--r-- | src/devices/bus/ti99_peb/pcode.h | 21 | ||||
-rw-r--r-- | src/devices/bus/ti99_peb/peribox.cpp | 2 | ||||
-rw-r--r-- | src/devices/bus/ti99x/998board.cpp | 2316 | ||||
-rw-r--r-- | src/devices/bus/ti99x/998board.h | 614 | ||||
-rw-r--r-- | src/devices/bus/ti99x/datamux.cpp | 397 | ||||
-rw-r--r-- | src/devices/bus/ti99x/datamux.h | 98 | ||||
-rw-r--r-- | src/devices/bus/ti99x/genboard.cpp | 14 | ||||
-rw-r--r-- | src/devices/bus/ti99x/genboard.h | 3 | ||||
-rw-r--r-- | src/devices/bus/ti99x/grom.cpp | 269 | ||||
-rw-r--r-- | src/devices/bus/ti99x/grom.h | 108 | ||||
-rw-r--r-- | src/devices/bus/ti99x/gromport.cpp | 464 | ||||
-rw-r--r-- | src/devices/bus/ti99x/gromport.h | 83 | ||||
-rw-r--r-- | src/devices/bus/ti99x/ti99defs.h | 48 | ||||
-rw-r--r-- | src/devices/bus/ti99x/videowrp.cpp | 17 | ||||
-rw-r--r-- | src/devices/bus/ti99x/videowrp.h | 30 | ||||
-rw-r--r-- | src/mame/drivers/ti99_4p.cpp | 8 | ||||
-rw-r--r-- | src/mame/drivers/ti99_4x.cpp | 155 | ||||
-rw-r--r-- | src/mame/drivers/ti99_8.cpp | 498 |
20 files changed, 3162 insertions, 2193 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 5423c224127..f52e4b03fe6 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -2029,8 +2029,6 @@ if (BUSES["TI99X"]~=null) then MAME_DIR .. "src/devices/bus/ti99x/datamux.h", MAME_DIR .. "src/devices/bus/ti99x/genboard.cpp", MAME_DIR .. "src/devices/bus/ti99x/genboard.h", - MAME_DIR .. "src/devices/bus/ti99x/grom.cpp", - MAME_DIR .. "src/devices/bus/ti99x/grom.h", MAME_DIR .. "src/devices/bus/ti99x/gromport.cpp", MAME_DIR .. "src/devices/bus/ti99x/gromport.h", MAME_DIR .. "src/devices/bus/ti99x/handset.cpp", diff --git a/src/devices/bus/ti99_peb/pcode.cpp b/src/devices/bus/ti99_peb/pcode.cpp index 57ee6462874..ff0d9b4c9d5 100644 --- a/src/devices/bus/ti99_peb/pcode.cpp +++ b/src/devices/bus/ti99_peb/pcode.cpp @@ -92,43 +92,87 @@ #define ACTIVE_TAG "ACTIVE" -#define LOG logerror -#define VERBOSE 1 +#define TRACE_ROM 0 +#define TRACE_GROM 0 +#define TRACE_CRU 0 +#define TRACE_SWITCH 0 ti_pcode_card_device::ti_pcode_card_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) -: ti_expansion_card_device(mconfig, TI99_P_CODE, "TI-99 P-Code Card", tag, owner, clock, "ti99_pcode", __FILE__), m_rom(nullptr), m_bank_select(0), m_switch(false) +: ti_expansion_card_device(mconfig, TI99_P_CODE, "TI-99 P-Code Card", tag, owner, clock, "ti99_pcode", __FILE__), + m_rom(nullptr), + m_bank_select(0), + m_active(false), + m_clock_count(0), + m_clockhigh(false), + m_inDsrArea(false), + m_isrom0(false), + m_isrom12(false), + m_isgrom(false), + m_address(0) { } +SETADDRESS_DBIN_MEMBER( ti_pcode_card_device::setaddress_dbin ) +{ + // Do not allow setaddress for the debugger. It will mess up the + // setaddress/memory access pairs when the CPU enters wait states. + if (space.debugger_access()) return; + + m_address = offset; + m_inDsrArea = ((m_address & m_select_mask)==m_select_value); + + line_state a14 = ((m_address & 2)!=0)? ASSERT_LINE : CLEAR_LINE; + + m_isrom0 = ((m_address & 0xf000)==0x4000); + m_isrom12 = ((m_address & 0xf000)==0x5000); + + // Valid access (GROM write with DBIN=0 or read with DBIN=1) + bool validaccess = (state==CLEAR_LINE || (m_address & 0x0400)==0); + + // GROM access 0101 1011 1111 1100 + m_isgrom = ((m_address & 0xfbfd)==0x5bfc) && validaccess; + + if (validaccess) + { + int lines = (state==ASSERT_LINE)? 1 : 0; + if (a14==ASSERT_LINE) lines |= 2; + line_state select = m_isgrom? ASSERT_LINE : CLEAR_LINE; + + // always deliver to GROM so that the select line may be cleared + for (int i=0; i < 8; i++) + m_grom[i]->set_lines(space, lines, select); + } +} + READ8Z_MEMBER( ti_pcode_card_device::readz ) { - if (m_switch && m_selected && ((offset & m_select_mask)==m_select_value)) + if (m_active && m_inDsrArea && m_selected) { - // GROM access - if ((offset & GROMMASK)==GROMREAD) + if (m_isrom0) { - for (auto & elem : m_grom) elem->readz(space, offset, value, mem_mask); - if (VERBOSE>5) LOG("ti99_pcode: read from grom %04x: %02x\n", offset&0xffff, *value); + *value = m_rom[m_address & 0x0fff]; + if (TRACE_ROM) logerror("Read from rom %04x: %02x\n", offset&0xffff, *value); } else { - if ((offset & 0x1000) == 0x0000) + if (m_isgrom) { - /* Accesses ROM 4732 (4K) */ - // 0000 xxxx xxxx xxxx - *value = m_rom[offset & 0x0fff]; - if (VERBOSE>5) LOG("ti99_pcode: read from rom %04x: %02x\n", offset&0xffff, *value); + for (auto& elem : m_grom) elem->readz(space, m_address, value); + if (TRACE_GROM) logerror("Read from grom %04x: %02x\n", m_address&0xffff, *value); } else { - // Accesses ROM 4764 (2*4K) - // We have two banks here which are activated according - // to the setting of CRU bit 4 - // Bank 0 is the ROM above - // 0001 xxxx xxxx xxxx Bank 1 - // 0010 xxxx xxxx xxxx Bank 2 - *value = m_rom[(m_bank_select<<12) | (offset & 0x0fff)]; - if (VERBOSE>5) LOG("ti99_pcode: read from rom %04x (%02x): %02x\n", offset&0xffff, m_bank_select, *value); + if (m_isrom12) + { + // Accesses ROM 4764 (2*4K) + // We have two banks here which are activated according + // to the setting of CRU bit 4 + // Bank 0 is the ROM above + // 0001 xxxx xxxx xxxx Bank 1 + // 0010 xxxx xxxx xxxx Bank 2 + *value = m_rom[(m_bank_select<<12) | (m_address & 0x0fff)]; + if (TRACE_ROM) logerror("Read from rom %04x (%02x): %02x\n", m_address&0xffff, m_bank_select, *value); + } } } } @@ -140,28 +184,14 @@ READ8Z_MEMBER( ti_pcode_card_device::readz ) */ WRITE8_MEMBER( ti_pcode_card_device::write ) { - if (m_switch && m_selected) + if (m_active && m_isgrom && m_selected) { - if ((offset & m_select_mask)==m_select_value) - { - if (VERBOSE>5) LOG("ti99_pcode: write to address %04x: %02x\n", offset & 0xffff, data); - // 0101 1111 1111 11x0 - if ((offset & GROMMASK) == GROMWRITE) - { - for (auto & elem : m_grom) elem->write(space, offset, data, mem_mask); - } - } + for (auto & elem : m_grom) elem->write(space, m_address, data); } } /* Common READY* line from the GROMs. - At this time we do not emulate GROM READY* since the CPU emulation does - not yet process READY*. If it did, however, we would have to do a similar - handling as in peribox (with INTA*): The common READY* line is a logical - AND of all single READY lines. If any GROM pulls it down, the line goes - down, and only if all GROMs release it, it pulls up again. We should think - about a general solution. */ WRITE_LINE_MEMBER( ti_pcode_card_device::ready_line ) { @@ -169,6 +199,22 @@ WRITE_LINE_MEMBER( ti_pcode_card_device::ready_line ) } /* + CLKOUT line from the CPU. This line is divided by 8 to generate a 375 Khz + clock input for the GROMs, which are thus running at a lower rate than + those in the console driven by the VDP (477 kHz). +*/ +WRITE_LINE_MEMBER( ti_pcode_card_device::clock_in) +{ + m_clock_count = (m_clock_count+1) & 0x03; // four pulses high, four pulses low + if (m_clock_count==0) + { + // Toggle + m_clockhigh = !m_clockhigh; + for (auto & elem : m_grom) elem->gclock_in(m_clockhigh? ASSERT_LINE : CLEAR_LINE); + } +} + +/* CRU read handler. The P-Code card does not offer CRU read lines, so we just ignore any request. (Note that CRU lines are not like memory; you may be able to write to them, but not necessarily read them again.) @@ -198,55 +244,22 @@ WRITE8_MEMBER(ti_pcode_card_device::cruwrite) if (addr==0x80) // Bit 4 is on address line 8 { m_bank_select = (data+1); // we're calling this bank 1 and bank 2 - if (VERBOSE>5) LOG("ti99_pcode: select rom bank %d\n", m_bank_select); + if (TRACE_CRU) logerror("Select rom bank %d\n", m_bank_select); } } } -static GROM_CONFIG(pgrom0_config) -{ - false, 0, PCODE_GROM_TAG, 0x0000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(pgrom1_config) -{ - false, 1, PCODE_GROM_TAG, 0x2000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(pgrom2_config) -{ - false, 2, PCODE_GROM_TAG, 0x4000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(pgrom3_config) -{ - false, 3, PCODE_GROM_TAG, 0x6000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(pgrom4_config) -{ - false, 4, PCODE_GROM_TAG, 0x8000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(pgrom5_config) -{ - false, 5, PCODE_GROM_TAG, 0xa000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(pgrom6_config) -{ - false, 6, PCODE_GROM_TAG, 0xc000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(pgrom7_config) -{ - false, 7, PCODE_GROM_TAG, 0xe000, 0x1800, GROMFREQ -}; - void ti_pcode_card_device::device_start() { m_cru_base = 0x1f00; - m_grom[0] = static_cast<ti99_grom_device*>(subdevice(PGROM0_TAG)); - m_grom[1] = static_cast<ti99_grom_device*>(subdevice(PGROM1_TAG)); - m_grom[2] = static_cast<ti99_grom_device*>(subdevice(PGROM2_TAG)); - m_grom[3] = static_cast<ti99_grom_device*>(subdevice(PGROM3_TAG)); - m_grom[4] = static_cast<ti99_grom_device*>(subdevice(PGROM4_TAG)); - m_grom[5] = static_cast<ti99_grom_device*>(subdevice(PGROM5_TAG)); - m_grom[6] = static_cast<ti99_grom_device*>(subdevice(PGROM6_TAG)); - m_grom[7] = static_cast<ti99_grom_device*>(subdevice(PGROM7_TAG)); + m_grom[0] = downcast<tmc0430_device*>(subdevice(PGROM0_TAG)); + m_grom[1] = downcast<tmc0430_device*>(subdevice(PGROM1_TAG)); + m_grom[2] = downcast<tmc0430_device*>(subdevice(PGROM2_TAG)); + m_grom[3] = downcast<tmc0430_device*>(subdevice(PGROM3_TAG)); + m_grom[4] = downcast<tmc0430_device*>(subdevice(PGROM4_TAG)); + m_grom[5] = downcast<tmc0430_device*>(subdevice(PGROM5_TAG)); + m_grom[6] = downcast<tmc0430_device*>(subdevice(PGROM6_TAG)); + m_grom[7] = downcast<tmc0430_device*>(subdevice(PGROM7_TAG)); m_rom = memregion(PCODE_ROM_TAG)->base(); } @@ -264,8 +277,15 @@ void ti_pcode_card_device::device_reset() } m_bank_select = 1; m_selected = false; + m_clock_count = 0; + m_clockhigh = false; + + m_active = ioport(ACTIVE_TAG)->read(); - m_switch = ioport(ACTIVE_TAG)->read(); + m_isrom0 = false; + m_isrom12 = false; + m_isgrom = false; + m_address = 0; } void ti_pcode_card_device::device_config_complete() @@ -274,28 +294,20 @@ void ti_pcode_card_device::device_config_complete() INPUT_CHANGED_MEMBER( ti_pcode_card_device::switch_changed ) { - if (VERBOSE>7) LOG("ti_pcode_card_device: switch changed to %d\n", newval); - m_switch = (newval != 0); + if (TRACE_SWITCH) logerror("Switch changed to %d\n", newval); + m_active = (newval != 0); } MACHINE_CONFIG_FRAGMENT( ti99_pcode ) - MCFG_GROM_ADD( PGROM0_TAG, pgrom0_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) - MCFG_GROM_ADD( PGROM1_TAG, pgrom1_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) - MCFG_GROM_ADD( PGROM2_TAG, pgrom2_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) - MCFG_GROM_ADD( PGROM3_TAG, pgrom3_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) - MCFG_GROM_ADD( PGROM4_TAG, pgrom4_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) - MCFG_GROM_ADD( PGROM5_TAG, pgrom5_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) - MCFG_GROM_ADD( PGROM6_TAG, pgrom6_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) - MCFG_GROM_ADD( PGROM7_TAG, pgrom7_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM0_TAG, 0, PCODE_GROM_TAG, 0x0000, WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM1_TAG, 1, PCODE_GROM_TAG, 0x2000, WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM2_TAG, 2, PCODE_GROM_TAG, 0x4000, WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM3_TAG, 3, PCODE_GROM_TAG, 0x6000, WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM4_TAG, 4, PCODE_GROM_TAG, 0x8000, WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM5_TAG, 5, PCODE_GROM_TAG, 0xa000, WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM6_TAG, 6, PCODE_GROM_TAG, 0xc000, WRITELINE(ti_pcode_card_device, ready_line)) + MCFG_GROM_ADD( PGROM7_TAG, 7, PCODE_GROM_TAG, 0xe000, WRITELINE(ti_pcode_card_device, ready_line)) MACHINE_CONFIG_END INPUT_PORTS_START( ti99_pcode ) diff --git a/src/devices/bus/ti99_peb/pcode.h b/src/devices/bus/ti99_peb/pcode.h index c2a81a1395c..71664682261 100644 --- a/src/devices/bus/ti99_peb/pcode.h +++ b/src/devices/bus/ti99_peb/pcode.h @@ -17,7 +17,7 @@ #include "emu.h" #include "peribox.h" -#include "bus/ti99x/grom.h" +#include "machine/tmc0430.h" extern const device_type TI99_P_CODE; @@ -29,6 +29,9 @@ public: DECLARE_WRITE8_MEMBER(write) override; DECLARE_READ8Z_MEMBER(crureadz) override; DECLARE_WRITE8_MEMBER(cruwrite) override; + DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin) override; + + DECLARE_WRITE_LINE_MEMBER(clock_in) override; DECLARE_WRITE_LINE_MEMBER( ready_line ); DECLARE_INPUT_CHANGED_MEMBER( switch_changed ); @@ -42,10 +45,22 @@ protected: virtual ioport_constructor device_input_ports() const override; private: - ti99_grom_device* m_grom[8]; + tmc0430_device* m_grom[8]; UINT8* m_rom; int m_bank_select; - bool m_switch; + bool m_active; + int m_clock_count; + bool m_clockhigh; + + // Address in card area + bool m_inDsrArea; + + bool m_isrom0; + bool m_isrom12; + bool m_isgrom; + + // Recent address + int m_address; }; #endif diff --git a/src/devices/bus/ti99_peb/peribox.cpp b/src/devices/bus/ti99_peb/peribox.cpp index a89f86d13ef..52274505533 100644 --- a/src/devices/bus/ti99_peb/peribox.cpp +++ b/src/devices/bus/ti99_peb/peribox.cpp @@ -259,7 +259,7 @@ WRITE8_MEMBER(peribox_device::write) SETADDRESS_DBIN_MEMBER(peribox_device::setaddress_dbin) { // Ignore the address when the TI-99/8 transmits the high-order 8 bits - // if (!m_memen) return; + if (!m_memen) return; for (int i=2; i <= 8; i++) { diff --git a/src/devices/bus/ti99x/998board.cpp b/src/devices/bus/ti99x/998board.cpp index 0d1f081ed1b..595ee5f77b1 100644 --- a/src/devices/bus/ti99x/998board.cpp +++ b/src/devices/bus/ti99x/998board.cpp @@ -14,71 +14,795 @@ Note: The TI-99/8's internal codename was "Armadillo" -============================== - Mapper (codename "Amigo") -============================== - Initial setting of mapper (as defined in the power-up routine, TI-99/4A mode) + +-------+ +--------+ + | 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. - 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 (not emulated) + 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 - +------+------+------+------+---+---+---+---------+----------+---------+ - | WProt| XProt| RProt| * | 0 | 0 | 0 | Upper | High | Low | - +------+------+------+------+---+---+---+---------+----------+---------+ + 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. - WProt: Write protection if set to 1 - XProt: Execute protection if set to 1 - RProt: Read protection if set to 1 + Note that the cartridge port of the TI-99/8 offers support for 16K ROM + cartridges, but lacks CRU support. - When a protection violation occurs, the tms9901 INT1* pin is pulled low - (active). The pin remains low until the mapper status register is read. + Michael Zapf, October 2010 + February 2012: Rewritten as class + March 2016: Redesigned for custom chip emulation - Address handling - ---------------- - Physical address is (Upper * 2^16) + (High * 2^8) + Low + Informations taken from + [1] ARMADILLO PRODUCT SPECIFICATIONS + [2] TI-99/8 Graphics Programming Language interpreter - 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. +// TODO: +// - PEB - Mapper control register - ----------------------- - The mapper control register is used to initiate a map load/save operation. +#include "998board.h" - +---+---+---+---+---+---+---+---+ - | 0 | 0 | 0 | 0 | Map File | RW| - +---+---+---+---+---+---+---+---+ +#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 - 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. +mainboard8_device::mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MAINBOARD8, "TI-99/8 Mainboard", tag, owner, clock, "ti998_mainboard", __FILE__), + 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) +{ +} - RW = 1: load from SRAM into mapper - RW = 0: store from mapper into SRAM +// =============== CRU bus access ================== - When read, the mapper register returns the violation flags: - +------+------+------+---+---+---+---+---+ - | WProt| XProt| RProt| 0 | 0 | 0 | 0 | 0 | - +------+------+------+---+---+---+---+---+ +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; + for (int i=0; i < 8; i++) + { + if (i < 3) + { + m_sgrom[i]->gclock_in(gromclk); + m_p3grom[i]->gclock_in(gromclk); + } + m_tsgrom[i]->gclock_in(gromclk); + m_p8grom[i]->gclock_in(gromclk); + } + m_gromport->gclock_in(gromclk); + } + + // 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[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; + + for (int i=0; i < 8; i++) + { + if (i < 3) + { + m_sgrom[i]->set_lines(*m_space, lines, select & SGMSEL); + m_p3grom[i]->set_lines(*m_space, lines, select & P3GSEL); + } + m_tsgrom[i]->set_lines(*m_space, lines, select & TSGSEL); + m_p8grom[i]->set_lines(*m_space, lines, select & P8GSEL); + } + // 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 value = 0; + const char* what; + + // ================================================= + // 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[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: + 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: + 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: + 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: + 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[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_space = &space; + m_pending_write = true; + + // 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[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); +} + +/* + 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<tmc0430_device*>(machine().device(glib0[i])); + m_p3grom[i] = downcast<tmc0430_device*>(machine().device(glib3[i])); + } + m_tsgrom[i] = downcast<tmc0430_device*>(machine().device(glib1[i])); + m_p8grom[i] = downcast<tmc0430_device*>(machine().device(glib2[i])); + } + + // Link to speech synthesizer + m_speech = downcast<cd2501ecd_device*>(machine().device(SPEECHSYN_TAG)); + + // Link to sound chip + m_sound = downcast<sn76496_base_device*>(machine().device(TISOUNDCHIP_TAG)); + + // Link to video + m_video = downcast<tms9118_device*>(machine().device(VDP_TAG)); + + // Link to cartridge port + m_gromport = downcast<gromport_device*>(machine().device(GROMPORT_TAG)); + + // Link to PEB + m_peb = downcast<peribox_device*>(machine().device(PERIBOX_TAG)); + + // Configure RAM and AMIGO + m_sram = std::make_unique<UINT8[]>(SRAM_SIZE); + m_dram = std::make_unique<UINT8[]>(DRAM_SIZE); + + m_amigo->connect_sram(m_sram.get()); + + 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(); +} + +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; +} + +MACHINE_CONFIG_FRAGMENT( ti998_mainboard ) + MCFG_DEVICE_ADD(VAQUERRO_TAG, VAQUERRO, 0) + MCFG_DEVICE_ADD(MOFETTA_TAG, MOFETTA, 0) + MCFG_DEVICE_ADD(AMIGO_TAG, AMIGO, 0) + MCFG_DEVICE_ADD(OSO_TAG, OSO, 0) +MACHINE_CONFIG_END + +machine_config_constructor mainboard8_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( ti998_mainboard ); +} +const device_type MAINBOARD8 = &device_creator<mainboard8_device>; + + +/*************************************************************************** + ===== VAQUERRO: Logical Address Space decoder ===== Logical address space (LAS) =========================== @@ -150,649 +874,976 @@ f850-f85f: P-Code library #1 GROM read/write f860-f86f: P-Code library #2 GROM read/write - 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: P-Code 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 +***************************************************************************/ - 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 +vaquerro_device::vaquerro_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) +: device_t(mconfig, VAQUERRO, "Logical Address Space Decoder", tag, owner, clock, "ti998_vaquerro", __FILE__), + m_crus(ASSERT_LINE), + m_crugl(ASSERT_LINE), + m_ggrdy(ASSERT_LINE) +{ +} - 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. +SETADDRESS_DBIN_MEMBER( vaquerro_device::set_address ) +{ + // Do the decoding + // state = dbin, offset = address - Michael Zapf, October 2010 - February 2012: Rewritten as class + bool reading = (state==ASSERT_LINE); + bool sgfap = false; + bool tsgfap = false; + bool p8gfap = false; + bool p3gfap = false; + bool vfap = false; - Informations taken from - [1] ARMADILLO PRODUCT SPECIFICATIONS - [2] TI-99/8 Graphics Programming Language interpreter + m_a14 = ((offset & 2)!=0)? ASSERT_LINE : CLEAR_LINE; // Needed for clock_in -***************************************************************************/ + m_dbin_level = (line_state)state; -#include "998board.h" + int offbase = (offset & 0xfff1); -#define TRACE_CRU 0 -#define TRACE_MEM 0 -#define TRACE_MAP 0 -#define TRACE_CONFIG 0 -#define TRACE_OSO 0 -#define TRACE_SPEECH 0 -#define TRACE_DETAIL 0 + // =================== TI (compatibility) mode ====================== + if (m_crus == ASSERT_LINE) + { + if (TRACE_DETAIL) logerror("Compatibility mode\n"); -mainboard8_device::mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : bus8z_device(mconfig, MAINBOARD8, "TI-99/8 Main board", tag, owner, clock, "ti998_mainboard", __FILE__), - m_ready(*this), m_dsr_selected(false), m_hexbus_selected(false), m_CRUS(false), m_PTGE(false), m_waitcount(0), m_sram(nullptr), m_dram(nullptr), m_rom0(nullptr), m_rom1(nullptr), m_pcode(nullptr), - m_oso(*this, OSO_TAG) - { } + // SPRD (Speech read) + m_sprd = ((offbase==0x9000) && reading); -/*************************************************************************** - CRU access -***************************************************************************/ + // SPWT (Speech write) + m_spwt = ((offbase==0x9400) && !reading); -#define HEXBUS_CRU_BASE 0x1700 -#define MAPPER_CRU_BASE 0x2700 + // Sound + m_sccs = ((offbase==0x8400)&& !reading); -READ8Z_MEMBER(mainboard8_device::crureadz) + // 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 ) { - if (TRACE_CRU) logerror("%s: read CRU %04x ignored\n", tag(), offset); - // Nothing here. + m_memen = (state==ASSERT_LINE); } /* - CRU handling. We handle the internal device at CRU address 0x2700 via - this mapper component. + Called by Mofetta */ -WRITE8_MEMBER(mainboard8_device::cruwrite) +READ_LINE_MEMBER( vaquerro_device::lascsq_out ) { - if ((offset & 0xff00)==MAPPER_CRU_BASE) - { - int bit = (offset & 0xff)>>1; - switch (bit) - { - case 0: - // Turn on/off the internal DSR - m_dsr_selected = (data!=0); - if (TRACE_CRU) logerror("%s: DSR select = %d\n", tag(), data); - break; - case 1: - if (TRACE_CRU) logerror("%s: System reset by CRU request\n", tag()); - machine().schedule_soft_reset(); - break; - } - return; - } + return (m_lasreq && m_memen)? ASSERT_LINE : CLEAR_LINE; +} - if ((offset & 0xff00)==HEXBUS_CRU_BASE) - { - int bit = (offset & 0xff)>>1; - switch (bit) - { - case 0: - // Turn on/off the Hexbus DSR - m_hexbus_selected = (data!=0); - if (TRACE_CRU) logerror("%s: Hexbus select = %d\n", tag(), data); - break; - default: - if (TRACE_CRU) logerror("%s: Set CRU>%04x (Hexbus) to %d\n", tag(), offset,data); - break; - } - return; - } +/* + 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); +} - if ((offset & 0xff00)>=0x0100) - { - if (TRACE_CRU) logerror("%s: Set CRU>%04x (unknown) to %d\n", tag(), offset,data); - return; - } +WRITE_LINE_MEMBER( vaquerro_device::tsgry ) +{ + if (TRACE_READY) logerror("Incoming TSGRY = %d\n", state); + m_tsgws.ready_in((line_state)state); } -void mainboard8_device::CRUS_set(bool state) +WRITE_LINE_MEMBER( vaquerro_device::p8gry ) { - if (TRACE_CRU) logerror("%s: set CRUS=%d\n", tag(), state); - m_CRUS = state; + 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); } /* - Note that PTGEN is negative logic. We invert these semantics here. + Outgoing READY */ -void mainboard8_device::PTGE_set(bool state) +READ_LINE_MEMBER( vaquerro_device::ggrdy_out ) { - if (TRACE_CRU) logerror("%s: set PTGEN=%d\n", tag(), state? 1:0); - m_PTGE = state; + if (TRACE_READY) logerror("GGRDY out = %d\n", m_ggrdy); + return m_ggrdy; } -/*************************************************************************** - Access by address map -***************************************************************************/ +/* + 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; +} /* - This method is called via the address map. + 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. */ -READ8_MEMBER( mainboard8_device::readm ) +WRITE_LINE_MEMBER( vaquerro_device::clock_in ) { - UINT8 value = 0; - bool found; - if (TRACE_DETAIL) logerror("%s: read from %04x\n", tag(), offset); - found = access_logical_r(space, offset, &value, mem_mask); - m_waitcount = 2; + line_state level = (line_state)state; - if (!found) - { - // In that case, the address decoder could not find a suitable device. - // This means the logical address is transformed by the mapper. - // NOTE: Use "+", not OR. The offset is not a prefix. - UINT32 pas_address = m_pas_offset[(offset & 0xf000)>>12] + (offset & 0xfff); + // 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); - // So now let's do the same as above with physical addresses - access_physical_r(space, pas_address, &value, mem_mask); + m_vdprd = (reading && (m_vidws.select_out()!=0)); + m_vdpwt = (!reading && (m_vidws.select_out()!=0)); - // The PAS area requires one more wait state, as the address bus - // is multiplexed - m_waitcount = 3; + // 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()); } - // Insert wait states and let CPU enter wait state - m_ready(CLEAR_LINE); + // If the output gate is closed, propagate ASSERT_LINE (pulled up) + m_ggrdy = (!m_grom_or_video || m_sry)? ASSERT_LINE : CLEAR_LINE; +} - return value; + +void vaquerro_device::device_start() +{ + logerror("Starting\n"); + m_mainboard = downcast<mainboard8_device*>(owner()); + m_sgmws.init(SGMSEL); + m_tsgws.init(TSGSEL); + m_p8gws.init(P8GSEL); + m_p3gws.init(P3GSEL); + m_vidws.init(VIDSEL); } -WRITE8_MEMBER( mainboard8_device::writem ) +void vaquerro_device::device_reset() { - bool found; + 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); +} - // Look for components responding to the logical address - found = access_logical_w(space, offset, data, mem_mask); - m_waitcount = 2; +/* + 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 waitstate_generator::select_in(bool addressed) +{ + m_addressed = addressed; +} - if (!found) - { - // In that case, the address decoder could not find a suitable device. - // This means the logical address is transformed by the mapper. - // NOTE: Use "+", not OR. The offset is not a prefix. - UINT32 pas_address = m_pas_offset[(offset & 0xf000)>>12] + (offset & 0xfff); +int waitstate_generator::select_out() +{ + return (!m_counting && m_addressed)? m_selvalue : 0; +} - // So now let's do the same as above with physical addresses - access_physical_w(space, pas_address, data, mem_mask); +/* + Should be low by default. +*/ +line_state waitstate_generator::ready_out() +{ + return (m_ready && !m_counting && m_generate)? ASSERT_LINE : CLEAR_LINE; +} - // The PAS area requires one more wait state, as the address bus - // is multiplexed - m_waitcount = 3; - } +bool waitstate_generator::is_counting() +{ + return m_counting; +} - // Insert wait states and let CPU enter wait state - m_ready(CLEAR_LINE); +bool waitstate_generator::is_generating() +{ + return m_generate; +} + +bool waitstate_generator::is_ready() +{ + return m_ready; } -/*************************************************************************** - Indirect calls (mapper calls itself) -***************************************************************************/ /* - This method is called by the mapper itself for - f870 (NATIVE): mapper: ignore - 8810 (TI99EM): mapper: ignore - ff4000 (PHYSIC): DSR + READY in. This may only show an effect with the next trailing edge of CLKOUT. */ -READ8Z_MEMBER( mainboard8_device::readz ) +void grom_waitstate_generator::ready_in(line_state ready) { - if ((offset & 0xffe000)==0xff4000) + m_ready = (ready==ASSERT_LINE); +} + +void grom_waitstate_generator::clock_in(line_state clkout) +{ + if (clkout == ASSERT_LINE) + { + if (m_counting) m_counter++; + } + else { - if (m_dsr_selected) + if (m_counting && m_counter==16) { - // Starts at 0x4000 in the image - *value = m_rom1[0x4000 | (offset & 0x1fff)]; - if (TRACE_MEM) logerror("%s: (intDSR) %04x -> %02x\n", tag(), offset, *value); + m_counter = 0; + m_counting = false; } else { - if (m_hexbus_selected) - { - if ((offset & 0x1ff0)==0x1ff0) - { - *value = m_oso->read(space, (offset>>1) & 0x0003); - } - else - { - // Starts at 0x6000 in the image - *value = m_rom1[0x6000 | (offset & 0x1fff)]; - if (TRACE_MEM) logerror("%s: (HexDSR) %04x -> %02x\n", tag(), offset, *value); - } - } + if (!m_addressed && m_generate) m_counting = true; + m_generate = ((m_addressed || m_counting) && (m_counter != 15)); } } +} + +void waitstate_generator::treset_in(line_state reset) +{ + if (reset==ASSERT_LINE) + { + m_counter = 0; + m_generate = m_counting = m_addressed = false; + } +} + +void video_waitstate_generator::clock_in(line_state clkout) +{ + if (clkout == ASSERT_LINE) + { + if (m_counting) m_counter++; + } else { - if (((offset & 0xfff0)==0xf870 && m_CRUS==false)||(((offset & 0xfff0)==0x8810 && m_CRUS==true))) + if (m_counting && m_counter==7) + { + m_counter = 0; + m_counting = false; + } + else { - if (TRACE_MEM) logerror("%s: read access to mapper ignored: %04x\n", tag(), offset); + if (!m_addressed && m_generate) m_counting = true; + m_generate = ((m_addressed || m_counting) && (m_counter != 6)); } } } +const device_type VAQUERRO = &device_creator<vaquerro_device>; + +/*************************************************************************** + ===== 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 clock) + : device_t(mconfig, MOFETTA, "Physical Address Space Decoder", tag, owner, clock, "ti998_mofetta", __FILE__), + 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; + } +} + /* - This method is called by the mapper itself for - ff4000 (PHYSIC): DSR. ignore + 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 */ -WRITE8_MEMBER( mainboard8_device::write ) +READ_LINE_MEMBER( mofetta_device::dbc_out ) +{ + return (m_lasreq || m_cmas || m_rom1cs || m_skdrcs || !m_pmemen)? CLEAR_LINE : ASSERT_LINE; +} + +WRITE8_MEMBER(mofetta_device::cruwrite) { - if ((offset & 0xffe000)==0xff4000) + if ((offset & 0xff00)==0x2700) { - if (m_hexbus_selected) + if ((offset & 0x0002)!=0) { - if ((offset & 0x1ff0)==0x1ff0) - { - m_oso->write(space, (offset>>1) & 0x0003, data); - } - else - { - logerror("%s: Write access to Hexbus DSR address %06x ignored\n", tag(), offset); - } + // 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 { - if (m_dsr_selected) - { - logerror("%s: Write access to internal DSR address %06x ignored\n", tag(), offset); - } - else - { - logerror("%s: Write access to unmapped DSR space at address %06x ignored\n", tag(), offset); - } + m_txspg = (data!=0); // CRU>2700 + if (TRACE_CRU) logerror("Turning %s CRU>2700\n", m_txspg? "on" : "off"); } } else { - if (((offset & 0xfff0)==0xf870 && m_CRUS==false)||(((offset & 0xfff0)==0x8810 && m_CRUS==true))) + if ((offset & 0xff00)==0x1700) { - mapwrite(offset, data); + m_alcpg = (data!=0); // CRU>1700 + if (TRACE_CRU) logerror("Turning %s CRU>1700\n", m_alcpg? "on" : "off"); } } } /* - Reconfigure mapper. Writing to this address copies the values in the - SRAM into the mapper and vice versa. - Format: - 0000 bbbl; bbb=bank, l=load - - TODO: Emulate properly, making use of HOLD + Setting or clearing the MSAST line. */ -void mainboard8_device::mapwrite(int offset, UINT8 data) +WRITE_LINE_MEMBER( mofetta_device::msast_in ) { - if ((data & 0xf0)==0x00) + if (state == ASSERT_LINE) { - int bankindx = (data & 0x0e)>>1; - if (data & 1) + if (m_msast == CLEAR_LINE) // Leading edge { - if (TRACE_MAP) logerror("%s: load mapper from SRAM, bank %d\n", tag(), bankindx); - // Load from SRAM - // In reality the CPU is put on HOLD during this transfer - for (int i=0; i < 16; i++) - { - int ptr = (bankindx << 6); - m_pas_offset[i] = (m_sram[(i<<2) + ptr] << 24) | (m_sram[(i<<2)+ ptr+1] << 16) - | (m_sram[(i<<2) + ptr+2] << 8) | (m_sram[(i<<2) + ptr+3]); - if (TRACE_MAP) logerror("%s: load %d=%08x\n", tag(), i, m_pas_offset[i]); - } - } - else - { - if (TRACE_MAP) logerror("%s: store mapper to SRAM, bank %d\n", tag(), bankindx); - // Store in SRAM - for (int i=0; i < 16; i++) - { - int ptr = (bankindx << 6); - m_sram[(i<<2) + ptr] = (m_pas_offset[i] >> 24)& 0xff; - m_sram[(i<<2) + ptr +1] = (m_pas_offset[i] >> 16)& 0xff; - m_sram[(i<<2) + ptr +2] = (m_pas_offset[i] >> 8)& 0xff; - m_sram[(i<<2) + ptr +3] = (m_pas_offset[i])& 0xff; - if (TRACE_MAP) logerror("%s: save %d=%08x\n", tag(), i, m_pas_offset[i]); - } + 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; } -/*************************************************************************** - Lookup methods. -***************************************************************************/ +WRITE_LINE_MEMBER( mofetta_device::pmemen_in ) +{ + m_pmemen = (state==ASSERT_LINE); +} -bool mainboard8_device::access_logical_r(address_space& space, offs_t offset, UINT8 *value, UINT8 mem_mask ) +WRITE_LINE_MEMBER( mofetta_device::lascs_in ) { - bool found = false; - logically_addressed_device *ldev = m_logcomp.first(); - bus8z_device *bdev; + m_lasreq = (state==ASSERT_LINE); +} - if (TRACE_DETAIL) logerror("%s: offset=%04x; CRUS=%d, PTGEN=%d\n", tag(), offset, m_CRUS? 1:0, m_PTGE? 0:1); - while (ldev != nullptr) - { - if (TRACE_DETAIL) logerror("%s: checking node=%s\n", tag(), ldev->m_config->name); - // Check the mode - if (((ldev->m_config->mode == NATIVE) && (m_CRUS==false)) - || ((ldev->m_config->mode == TI99EM) && (m_CRUS==true)) - || ((ldev->m_config->mode == PATGEN) && (m_PTGE==true))) - { - if ((offset & ldev->m_config->address_mask)==ldev->m_config->select_pattern) - { - switch (ldev->m_kind) - { - case MAP8_SRAM: - *value = m_sram[offset & ~ldev->m_config->address_mask]; - if (TRACE_MEM) logerror("%s: (SRAM) %04x -> %02x\n", tag(), offset, *value); - break; - case MAP8_ROM0: - // Starts at 0000 - *value = m_rom0[offset & ~ldev->m_config->address_mask]; - if (TRACE_MEM) logerror("%s: (ROM0) %04x -> %02x\n", tag(), offset, *value); - break; - case MAP8_DEV: - // device - bdev = static_cast<bus8z_device*>(ldev->m_device); - bdev->readz(space, offset, value, mem_mask); - if (TRACE_MEM) logerror("%s: (dev %s) %04x -> %02x\n", tag(), ldev->m_config->name, offset, *value); - break; - default: - if (TRACE_MEM) logerror("%s: Invalid kind for read access: %d\n", tag(), ldev->m_kind); - } - found = true; - if (ldev->m_config->stop==STOP) break; - } - } - ldev = ldev->m_next; - } - return found; +WRITE_LINE_MEMBER( mofetta_device::skdrcs_in ) +{ + m_skdrcs = (state==ASSERT_LINE); } -bool mainboard8_device::access_logical_w(address_space& space, offs_t offset, UINT8 data, UINT8 mem_mask ) +void mofetta_device::device_start() { - bool found = false; - logically_addressed_device *ldev = m_logcomp.first(); - bus8z_device *bdev; + logerror("Starting\n"); + m_mainboard = downcast<mainboard8_device*>(owner()); +} - while (ldev != nullptr) - { - // Check the mode - if (((ldev->m_config->mode == NATIVE) && (m_CRUS==false)) - || ((ldev->m_config->mode == TI99EM) && (m_CRUS==true)) - || ((ldev->m_config->mode == PATGEN) && (m_PTGE==true))) - { - if ((offset & ldev->m_config->address_mask)==(ldev->m_config->select_pattern | ldev->m_config->write_select)) - { - switch (ldev->m_kind) - { - case MAP8_SRAM: - m_sram[offset & ~ldev->m_config->address_mask] = data; - if (TRACE_MEM) logerror("%s: (SRAM) %04x <- %02x\n", tag(), offset, data); - break; - case MAP8_ROM0: - if (TRACE_MEM) logerror("%s: (ROM0) %04x <- %02x (ignored)\n", tag(), offset, data); - break; - case MAP8_DEV: - // device - bdev = static_cast<bus8z_device*>(ldev->m_device); - bdev->write(space, offset, data, mem_mask); - if (TRACE_MEM) logerror("%s: (dev %s) %04x <- %02x\n", tag(), ldev->m_config->name, offset, data); - break; - default: - if (TRACE_MEM) logerror("%s: Invalid kind for write access: %d\n", tag(), ldev->m_kind); - } - found = true; - if (ldev->m_config->stop==STOP) break; - } - } - ldev = ldev->m_next; - } - return found; +void mofetta_device::device_reset() +{ + m_gotfirstword = false; + m_alcpg = false; + m_txspg = false; + m_prefix = 0; +} + + +const device_type MOFETTA = &device_creator<mofetta_device>; + +/*************************************************************************** + + ============================== + 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 clock) +: device_t(mconfig, AMIGO, "Address space mapper", tag, owner, clock, "ti998_amigo", __FILE__), + m_logical_space(true), + m_crus(ASSERT_LINE) +{ } +enum +{ + IDLE = 0, + CREATE_PADDR, + ADDR_MSW, + ADDR_LSW, + SRAMLOAD, + SRAMSAVE +}; -void mainboard8_device::access_physical_r( address_space& space, offs_t pas_address, UINT8 *value, UINT8 mem_mask ) +/* + Incoming READY line (SRDY) +*/ +WRITE_LINE_MEMBER( amigo_device::srdy_in ) { - physically_addressed_device *pdev = m_physcomp.first(); - bus8z_device *bdev; + if (TRACE_READY) logerror("Incoming SRDY = %d\n", state); + m_srdy = (line_state)state; - while (pdev != nullptr) + // If the access is going to logical space, pass through the READY line + if (m_logical_space) { - if ((pas_address & pdev->m_config->address_mask)==pdev->m_config->select_pattern) - { - switch (pdev->m_kind) - { - case MAP8_DRAM: - *value = m_dram[pas_address & ~pdev->m_config->address_mask]; - if (TRACE_MEM) logerror("%s: (DRAM) %06x -> %02x\n", tag(), pas_address, *value); - break; - case MAP8_ROM1A0: - // Starts at 0000 in the image, 8K - *value = m_rom1[pas_address & 0x1fff]; - if (TRACE_MEM) logerror("%s: (ROM) %06x -> %02x\n", tag(), pas_address, *value); - break; - case MAP8_ROM1C0: - // Starts at 2000 in the image, 8K - *value = m_rom1[0x2000 | (pas_address & 0x1fff)]; - if (TRACE_MEM) logerror("%s: (ROM) %06x -> %02x\n", tag(), pas_address, *value); - break; - case MAP8_PCODE: - *value = m_pcode[pas_address & 0x3fff]; - if (TRACE_MEM) logerror("%s: (PCODE) %06x -> %02x\n", tag(), pas_address, *value); - break; - case MAP8_INTS: - // Interrupt sense - logerror("%s: ILSENSE not implemented.\n", tag()); - break; - case MAP8_DEV: - // devices - bdev = static_cast<bus8z_device*>(pdev->m_device); - bdev->readz(space, pas_address, value, mem_mask); - if (TRACE_MEM) logerror("%s: (dev %s) %06x -> %02x\n", tag(), pdev->m_config->name, pas_address, *value); - break; - default: - logerror("%s: Invalid kind for physical read access: %d\n", tag(), pdev->m_kind); - } - if (pdev->m_config->stop==STOP) break; - } - pdev = pdev->m_next; + if (TRACE_CPURY) logerror("Setting CPURY = %d (SRDY)\n", m_ready_out); + m_ready_out = m_srdy; } } -void mainboard8_device::access_physical_w( address_space& space, offs_t pas_address, UINT8 data, UINT8 mem_mask ) +WRITE_LINE_MEMBER( amigo_device::memen_in ) { - physically_addressed_device *pdev = m_physcomp.first(); - bus8z_device *bdev; + m_memen = (state==ASSERT_LINE); +} - while (pdev != nullptr) - { - if ((pas_address & pdev->m_config->address_mask)==(pdev->m_config->select_pattern | pdev->m_config->write_select)) - { - switch (pdev->m_kind) - { - case MAP8_DRAM: - m_dram[pas_address & ~pdev->m_config->address_mask] = data; - if (TRACE_MEM) logerror("%s: (DRAM) %06x <- %02x\n", tag(), pas_address, data); - break; - case MAP8_ROM1A0: - case MAP8_ROM1C0: - if (TRACE_MEM) logerror("%s: (ROM1) %06x <- %02x (ignored)\n", tag(), pas_address, data); - break; - case MAP8_PCODE: - if (TRACE_MEM) logerror("%s: (PCODE) %06x <- %02x (ignored)\n", tag(), pas_address, data); - break; - case MAP8_INTS: - // Interrupt sense - logerror("%s: write to ilsense ignored\n", tag()); - break; - case MAP8_DEV: - // devices - bdev = static_cast<bus8z_device*>(pdev->m_device); - if (TRACE_MEM) logerror("%s: (dev %s) %06x <- %02x\n", tag(), pdev->m_config->name, pas_address, data); - bdev->write(space, pas_address, data, mem_mask); - break; - default: - logerror("%s: Invalid kind for physical write access: %d\n", tag(), pdev->m_kind); - } - if (pdev->m_config->stop==STOP) break; - } - pdev = pdev->m_next; - } +/* + Polled from the mainboard +*/ +READ_LINE_MEMBER( amigo_device::cpury_out ) +{ + return m_ready_out; } /* - The mapper is connected to the clock line in order to operate - the wait state counter. + Polled from the mainboard */ -void mainboard8_device::clock_in(int clock) +READ_LINE_MEMBER( amigo_device::sramcs_out ) { - if (clock==ASSERT_LINE && m_waitcount!=0) - { - m_waitcount--; - if (m_waitcount==0) m_ready(ASSERT_LINE); - } + 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; +} -/*************************************************************************** - DEVICE LIFECYCLE FUNCTIONS -***************************************************************************/ /* - We need to do all of the configuration in device_start since we don't have all - required links earlier. + Incoming CRUS line. Needed to set the mapper config addresses. +*/ +WRITE_LINE_MEMBER( amigo_device::crus_in ) +{ + m_crus = (line_state)state; +} - Note that device_reset is too late; the initial context switch occurs earlier. +/* + Incoming LASCS line. */ -void mainboard8_device::device_start() +WRITE_LINE_MEMBER( amigo_device::lascs_in ) { - logerror("%s: Starting mapper\n", tag()); + 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; - // String values of the pseudo constants, used in the configuration. - const char *const pseudodev[7] = { SRAMNAME, ROM0NAME, ROM1A0NAME, ROM1C0NAME, DRAMNAME, PCODENAME, INTSNAME }; + // TODO: Process flags - const mapper8_config *conf = reinterpret_cast<const mapper8_config *>(static_config()); + // Is it DRAM? + m_dram_accessed = (m_physical_address & 0x00ff0000)==0; - const mapper8_list_entry *entry = conf->devlist; - m_ready.resolve_safe(); + // This takes one clock pulse. + m_amstate = CREATE_PADDR; - m_sram = machine().root_device().memregion(SRAM_TAG)->base(); - m_dram = machine().root_device().memregion(DRAM_TAG)->base(); - m_rom0 = machine().root_device().memregion(ROM0_TAG)->base(); - m_rom1 = machine().root_device().memregion(ROM1_TAG)->base(); - m_pcode = machine().root_device().memregion(PCODEROM_TAG)->base(); + // 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 value = m_protflag; + m_protflag = 0; + return value; +} - // Clear the lists - m_logcomp.reset(); - m_physcomp.reset(); +/* + 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); +} - // Now building the list of active devices at this mapper. - // Coyping partly from datamux.c. - if ( entry != nullptr ) +WRITE_LINE_MEMBER( amigo_device::clock_in ) +{ + if (state==CLEAR_LINE) { - bool done = false; - for (int i=0; !done; i++) + switch (m_amstate) { - if (entry[i].name == nullptr) - { - done = true; - } - else - { - device_t *dev = nullptr; - mapper8_device_kind kind = MAP8_UNDEF; - - for (int j=1; (j < 8) && (kind == MAP8_UNDEF); j++) - { - // Pseudo devices are enumerated as 1 ... 6 (see MAP8_SRAM etc.) - if (strcmp(entry[i].name, pseudodev[j-1])==0) kind = (mapper8_device_kind)j; - } - if (kind==MAP8_UNDEF) - { - // This entry points to a "real" device, not to a special constant - kind = MAP8_DEV; - dev = machine().device(entry[i].name); - } - if (kind != MAP8_DEV || dev != nullptr) - { - if (entry[i].mode != PHYSIC) - { - auto ad = new logically_addressed_device(kind, (device_t*)dev, entry[i]); - m_logcomp.append(*ad); - if (TRACE_CONFIG) logerror("%s: Device %s mounted into logical address space.\n", tag(), entry[i].name); - } - else - { - auto ad = new physically_addressed_device(kind, (device_t*)dev, entry[i]); - m_physcomp.append(*ad); - if (TRACE_CONFIG) logerror("%s: Device %s mounted into physical address space.\n", tag(), entry[i].name); - } - } - else - { - if (TRACE_CONFIG) logerror("%s: Device %s not found.\n", tag(), entry[i].name); - } - } + 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); } } - if (TRACE_CONFIG) logerror("%s: Mapper logical device count = %d\n", tag(), m_logcomp.count()); - if (TRACE_CONFIG) logerror("%s: Mapper physical device count = %d\n", tag(), m_physcomp.count()); +} - m_dsr_selected = false; - m_CRUS = true; - m_PTGE = false; +void amigo_device::mapper_load() +{ + m_mapvalue = (m_mapvalue << 8) | m_sram[m_sram_address++]; - // Clean mapper - for (auto & elem : m_pas_offset) elem = 0; + 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 mainboard8_device::device_reset() +void amigo_device::mapper_save() { - m_dsr_selected = false; - m_CRUS = true; - m_PTGE = false; - m_waitcount = 0; - m_hexbus_selected = false; + 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++; + } + } - // Clean mapper - for (auto & elem : m_pas_offset) elem = 0; + m_sram[m_sram_address++] = (m_mapvalue >> 24) & 0xff; + m_mapvalue = m_mapvalue << 8; +} - m_ready(ASSERT_LINE); +WRITE_LINE_MEMBER( amigo_device::holda_in ) +{ + if (TRACE_MAP) logerror("HOLD acknowledged = %d\n", state); + m_hold_acknowledged = (state==ASSERT_LINE); } -MACHINE_CONFIG_FRAGMENT( ti998_mainboard ) - MCFG_DEVICE_ADD(OSO_TAG, OSO, 0) -MACHINE_CONFIG_END +void amigo_device::device_start() +{ + logerror("Starting\n"); + m_mainboard = downcast<mainboard8_device*>(owner()); +} -machine_config_constructor mainboard8_device::device_mconfig_additions() const +void amigo_device::device_reset() { - return MACHINE_CONFIG_NAME( ti998_mainboard ); + m_logical_space = true; } -const device_type MAINBOARD8 = &device_creator<mainboard8_device>; +const device_type AMIGO = &device_creator<amigo_device>; /*************************************************************************** - Custom chips of the TI-99/8 - ===== OSO: Hexbus interface ===== The Hexbus is a 4-bit peripheral bus with master/slave coordination. Bytes @@ -858,12 +1909,12 @@ enum SHSK = 0x01 }; -ti998_oso_device::ti998_oso_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) -: device_t(mconfig, OSO, "OSO Hexbus interface", tag, owner, clock, "ti998_oso", __FILE__), m_data(0), m_status(0), m_control(0), m_xmit(0) +oso_device::oso_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) +: device_t(mconfig, OSO, "Hexbus interface", tag, owner, clock, "oso", __FILE__), m_data(0), m_status(0), m_control(0), m_xmit(0) { } -READ8_MEMBER( ti998_oso_device::read ) +READ8_MEMBER( oso_device::read ) { int value = 0; offset &= 0x03; @@ -871,36 +1922,36 @@ READ8_MEMBER( ti998_oso_device::read ) { case 0: // read 5FF8: read data register - if (TRACE_OSO) logerror("%s: Read data register = %02x\n", tag(), value); + 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("%s: Read status %02x\n", tag(), value); + if (TRACE_OSO) logerror("Read status %02x\n", value); break; case 2: // read 5FFC: read control register value = m_control; - if (TRACE_OSO) logerror("%s: Read control register = %02x\n", tag(), value); + 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("%s: Read transmit register = %02x\n", tag(), value); + if (TRACE_OSO) logerror("Read transmit register = %02x\n", value); break; } return value; } -WRITE8_MEMBER( ti998_oso_device::write ) +WRITE8_MEMBER( oso_device::write ) { offset &= 0x03; switch (offset) { case 0: // write 5FF8: write transmit register - if (TRACE_OSO) logerror("%s: Write transmit register %02x\n", tag(), data); + 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 @@ -908,169 +1959,20 @@ WRITE8_MEMBER( ti998_oso_device::write ) break; case 1: // write 5FFA: write control register - if (TRACE_OSO) logerror("%s: Write control register %02x\n", tag(), data); + if (TRACE_OSO) logerror("Write control register %02x\n", data); m_control = data; break; default: // write 5FFC, 5FFE: undefined - if (TRACE_OSO) logerror("%s: Invalid write on %04x: %02x\n", tag(), (offset<<1) | 0x5ff0, data); + if (TRACE_OSO) logerror("Invalid write on %04x: %02x\n", (offset<<1) | 0x5ff0, data); break; } } -void ti998_oso_device::device_start() +void oso_device::device_start() { + logerror("Starting\n"); m_status = m_xmit = m_control = m_data = 0; } -const device_type OSO = &device_creator<ti998_oso_device>; - - -// ======================================================================== - -/**************************************************************************** - - TI-99/8 Speech synthesizer subsystem - - The TI-99/8 contains a speech synthesizer inside the console, so we cannot - reuse the spchsyn implementation of the P-Box speech synthesizer. - Accordingly, this is not a ti_expansion_card_device. - - For comments on real timing see ti99/spchsyn.c - - Note that before the REAL_TIMING can be used we must first establish - the set_address logic in 998board. - -*****************************************************************************/ - -#define TMS5220_ADDRESS_MASK 0x3FFFFUL /* 18-bit mask for tms5220 address */ -#define SPEECHSYN_TAG "speechsyn" -#define REAL_TIMING 0 - -ti998_spsyn_device::ti998_spsyn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) -: bus8z_device(mconfig, SPEECH8, "TI-99/8 Onboard Speech synthesizer", tag, owner, clock, "ti998_speech", __FILE__), m_vsp(nullptr), - m_ready(*this) -{ -} - -/* - Memory read -*/ -#if REAL_TIMING -// ====== This is the version with real timing ======= -READ8Z_MEMBER( ti998_spsyn_device::readz ) -{ - m_vsp->wsq_w(TRUE); - m_vsp->rsq_w(FALSE); - *value = m_vsp->read(offset) & 0xff; - if (TRACE_SPEECH) logerror("%s: read value = %02x\n", tag(), *value); -} - -/* - Memory write -*/ -WRITE8_MEMBER( ti998_spsyn_device::write ) -{ - m_vsp->rsq_w(m_vsp, TRUE); - m_vsp->wsq_w(m_vsp, FALSE); - if (TRACE_SPEECH) logerror("%s: write value = %02x\n", tag(), data); - m_vsp->write(offset, data); -} - -#else -// ====== This is the version without real timing ======= - -READ8Z_MEMBER( ti998_spsyn_device::readz ) -{ - machine().device("maincpu")->execute().adjust_icount(-(18+3)); /* this is just a minimum, it can be more */ - *value = m_vsp->status_r(space, offset, 0xff) & 0xff; - if (TRACE_SPEECH) logerror("%s: read value = %02x\n", tag(), *value); -} - -/* - Memory write -*/ -WRITE8_MEMBER( ti998_spsyn_device::write ) -{ - machine().device("maincpu")->execute().adjust_icount(-(54+3)); /* this is just an approx. minimum, it can be much more */ - - /* RN: the stupid design of the tms5220 core means that ready is cleared */ - /* when there are 15 bytes in FIFO. It should be 16. Of course, if */ - /* it were the case, we would need to store the value on the bus, */ - /* which would be more complex. */ - if (!m_vsp->readyq_r()) - { - attotime time_to_ready = attotime::from_double(m_vsp->time_to_ready()); - int cycles_to_ready = machine().device<cpu_device>("maincpu")->attotime_to_cycles(time_to_ready); - if (TRACE_SPEECH && TRACE_DETAIL) logerror("%s: time to ready: %f -> %d\n", tag(), time_to_ready.as_double(), (int) cycles_to_ready); - - machine().device("maincpu")->execute().adjust_icount(-cycles_to_ready); - machine().scheduler().timer_set(attotime::zero, FUNC_NULL); - } - if (TRACE_SPEECH) logerror("%s: write value = %02x\n", tag(), data); - m_vsp->data_w(space, offset, data); -} -#endif - -/**************************************************************************/ - -WRITE_LINE_MEMBER( ti998_spsyn_device::speech8_ready ) -{ - // The TMS5200 implementation uses TRUE/FALSE, not ASSERT/CLEAR semantics - m_ready((state==0)? ASSERT_LINE : CLEAR_LINE); - if (TRACE_SPEECH) logerror("%s: READY = %d\n", tag(), (state==0)); - -#if REAL_TIMING - // Need to do that here (see explanations in spchsyn.c) - if (state==0) - { - m_vsp->rsq_w(TRUE); - m_vsp->wsq_w(TRUE); - } -#endif -} - -void ti998_spsyn_device::device_start() -{ - m_ready.resolve_safe(); - m_vsp = subdevice<tms5220_device>(SPEECHSYN_TAG); - speechrom_device* mem = subdevice<speechrom_device>("vsm"); - mem->set_reverse_bit_order(true); -} - -void ti998_spsyn_device::device_reset() -{ - if (TRACE_SPEECH) logerror("%s: reset\n", tag()); -} - -// Unlike the TI-99/4A, the 99/8 uses the CD2501ECD -// The CD2501ECD is a tms5200/cd2501e with the rate control from the tms5220c added in. -// (it's probably actually a tms5220c die with the cd2501e/tms5200 lpc rom masked onto it) -MACHINE_CONFIG_FRAGMENT( ti998_speech ) - MCFG_DEVICE_ADD("vsm", SPEECHROM, 0) - - MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD(SPEECHSYN_TAG, CD2501ECD, 640000L) - MCFG_TMS52XX_READYQ_HANDLER(WRITELINE(ti998_spsyn_device, speech8_ready)) - MCFG_TMS52XX_SPEECHROM("vsm") - MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) -MACHINE_CONFIG_END - -/* Verified on a real machine: TI-99/8 uses the same speech rom contents - as the TI speech synthesizer. */ -ROM_START( ti998_speech ) - ROM_REGION(0x8000, "vsm", 0) - ROM_LOAD("cd2325a.vsm", 0x0000, 0x4000, CRC(1f58b571) SHA1(0ef4f178716b575a1c0c970c56af8a8d97561ffe)) - ROM_LOAD("cd2326a.vsm", 0x4000, 0x4000, CRC(65d00401) SHA1(a367242c2c96cebf0e2bf21862f3f6734b2b3020)) -ROM_END - -machine_config_constructor ti998_spsyn_device::device_mconfig_additions() const -{ - return MACHINE_CONFIG_NAME( ti998_speech ); -} - -const rom_entry *ti998_spsyn_device::device_rom_region() const -{ - return ROM_NAME( ti998_speech ); -} -const device_type SPEECH8 = &device_creator<ti998_spsyn_device>; +const device_type OSO = &device_creator<oso_device>; diff --git a/src/devices/bus/ti99x/998board.h b/src/devices/bus/ti99x/998board.h index 9231d9be647..0a1d1523db7 100644 --- a/src/devices/bus/ti99x/998board.h +++ b/src/devices/bus/ti99x/998board.h @@ -18,113 +18,368 @@ #include "emu.h" #include "ti99defs.h" +#include "machine/tmc0430.h" +#include "video/tms9928a.h" +#include "sound/sn76496.h" #include "sound/tms5220.h" +#include "gromport.h" +#include "bus/ti99_peb/peribox.h" extern const device_type MAINBOARD8; -extern const device_type OSO; -extern const device_type SPEECH8; +#define VAQUERRO_TAG "vaquerro" +#define MOFETTA_TAG "mofetta" +#define AMIGO_TAG "amigo" #define OSO_TAG "oso" -#define SPEECHSYN_TAG "speechsyn" - -#define NATIVE 0 -#define TI99EM 1 -#define PATGEN 2 -#define PHYSIC 3 -#define CONT 0 -#define STOP 1 - -#define SRAMNAME "SRAM" -#define ROM0NAME "ROM0" -#define ROM1A0NAME "ROM1A" -#define ROM1C0NAME "ROM1C" -#define INTSNAME "INTS" -#define DRAMNAME "DRAM" -#define PCODENAME "PCODE" #define SRAM_SIZE 2048 #define DRAM_SIZE 65536 -// We use these constants in the read/write functions. -enum mapper8_device_kind +class mainboard8_device; +extern const device_type VAQUERRO; +extern const device_type MOFETTA; +extern const device_type AMIGO; +extern const device_type OSO; + + +enum { - MAP8_UNDEF = 0, - MAP8_SRAM, - MAP8_ROM0, - MAP8_ROM1A0, - MAP8_ROM1C0, - MAP8_DRAM, - MAP8_PCODE, - MAP8_INTS, - MAP8_DEV // device by name + SGMSEL = 1, + TSGSEL = 2, + P8GSEL = 4, + P3GSEL = 8, + VIDSEL = 16 }; -struct mapper8_list_entry +/* + Wait state generator (part of Vaquerro) +*/ +class waitstate_generator { - const char* name; // Name of the device (used for looking up the device) - int mode; // Mode of the system which applies to this entry - int stop; // Mapper shall stop searching for a matching device when this entry applies - UINT32 select_pattern; // State of the address line bits when addressing this device - UINT32 address_mask; // Bits of the address bus used to address this device - UINT32 write_select; // Additional bits set when doing write accesses to this device -}; +public: + waitstate_generator() : + m_counting(false), + m_generate(false), + m_counter(0), + m_addressed(true), + m_ready(true) { }; + void select_in(bool addressed); + virtual void ready_in(line_state ready) =0; + virtual void clock_in(line_state clkout) =0; + void treset_in(line_state reset); + + 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(); -#define MAPPER8_CONFIG(name) \ - const mapper8_config(name) = +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; +}; -#define MCFG_MAINBOARD8_READY_CALLBACK(_write) \ - devcb = &mainboard8_device::set_ready_wr_callback(*device, DEVCB_##_write); +class grom_waitstate_generator : public waitstate_generator +{ +public: + void ready_in(line_state ready) override; + void clock_in(line_state clkout) override; +}; -struct mapper8_config +class video_waitstate_generator : public waitstate_generator { - const mapper8_list_entry *devlist; +public: + void ready_in(line_state ready) override { }; + void clock_in(line_state clkout) override; }; /* - Device list of the mapper. + Custom chip: Vaquerro */ -class logically_addressed_device +class vaquerro_device : public device_t { - friend class simple_list<logically_addressed_device>; - friend class mainboard8_device; - public: - logically_addressed_device(mapper8_device_kind kind, device_t *busdevice, const mapper8_list_entry &entry) - : m_next(nullptr), m_kind(kind), m_device(busdevice), m_config(&entry) { }; + vaquerro_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + void device_start() override; + void device_reset() override; + + line_state ready(); + void treset(); + + DECLARE_READ8_MEMBER( read ); + DECLARE_SETADDRESS_DBIN_MEMBER( set_address ); + + DECLARE_READ_LINE_MEMBER( sprd_out ); + DECLARE_READ_LINE_MEMBER( spwt_out ); + DECLARE_READ_LINE_MEMBER( sccs_out ); + DECLARE_READ_LINE_MEMBER( sromcs_out ); + + // Collective select line query + int gromcs_out(); + + DECLARE_READ_LINE_MEMBER( vdprd_out ); + DECLARE_READ_LINE_MEMBER( vdpwt_out ); + DECLARE_READ_LINE_MEMBER( lascsq_out ); + DECLARE_READ_LINE_MEMBER( ggrdy_out ); + DECLARE_WRITE_LINE_MEMBER( hold_cpu ); + + DECLARE_WRITE_LINE_MEMBER( crus_in ); + DECLARE_WRITE_LINE_MEMBER( crusgl_in ); + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( memen_in ); + + DECLARE_WRITE_LINE_MEMBER( sgmry ); + DECLARE_WRITE_LINE_MEMBER( tsgry ); + DECLARE_WRITE_LINE_MEMBER( p8gry ); + DECLARE_WRITE_LINE_MEMBER( p3gry ); private: - logically_addressed_device *m_next; // needed for simple_list - mapper8_device_kind m_kind; // named device or predefined - device_t *m_device; // the actual device - const mapper8_list_entry *m_config; + // Memory cycle state + bool m_memen; + + // Waiting for video + bool m_video_wait; + + // State of the CRUS line + line_state m_crus; + + // Are the GROM libraries turned on? + bool m_crugl; + + // Do we have a logical address space match? + bool m_lasreq = false; + + // Keep the decoding result (opens the SRY gate) + bool m_grom_or_video = false; + + // Select lines + bool m_spwt; + bool m_sccs; + bool m_sromcs; + bool m_sprd; + bool m_vdprd; + bool m_vdpwt; + + // Collective GROM select state + int m_gromsel; + + // Outgoing READY + line_state m_ggrdy; + + // Outgoing READY latch (common flipflop driving SRY) + bool m_sry; + + // Holds the A14 address line state. We need this for the clock_in method. + line_state m_a14; + + // Keeps the recent DBIN level + line_state m_dbin_level; + + // Wait state logic components + grom_waitstate_generator m_sgmws, m_tsgws, m_p8gws, m_p3gws; + video_waitstate_generator m_vidws; + + // Pointer to mainboard + mainboard8_device* m_mainboard; }; /* - Device list of the mapper. + Custom chip: Mofetta */ -class physically_addressed_device +class mofetta_device : public device_t { - friend class simple_list<physically_addressed_device>; - friend class mainboard8_device; +public: + mofetta_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + void device_start() override; + void device_reset() override; + + DECLARE_WRITE8_MEMBER( cruwrite ); + DECLARE_SETADDRESS_DBIN_MEMBER( set_address ); + + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( msast_in ); + DECLARE_WRITE_LINE_MEMBER( lascs_in ); + DECLARE_WRITE_LINE_MEMBER( pmemen_in ); + DECLARE_WRITE_LINE_MEMBER( skdrcs_in ); + + DECLARE_READ8_MEMBER( rom1cs_out ); + DECLARE_READ_LINE_MEMBER( gromclk_out ); + + DECLARE_READ_LINE_MEMBER( alccs_out ); + DECLARE_READ_LINE_MEMBER( prcs_out ); + DECLARE_READ_LINE_MEMBER( cmas_out ); + DECLARE_READ_LINE_MEMBER( dbc_out ); + + DECLARE_READ_LINE_MEMBER( rom1cs_out ); + DECLARE_READ_LINE_MEMBER( rom1am_out ); + DECLARE_READ_LINE_MEMBER( rom1al_out ); + +private: + // Memory cycle state + bool m_pmemen; + + // Logical access + bool m_lasreq; + // DRAM access + bool m_skdrcs; + + // Holds the decoding result; essentially names the selected line + bool m_gromclk_up; + + // Have we got the upper word of the address? + bool m_gotfirstword; + + // Address latch + int m_address_latch; + + // Most significant byte of the 24-bit address + int m_prefix; + + // CRU select of the 1700 device + bool m_alcpg; + + // CRU select of the 2700 device + bool m_txspg; + + // ROM1 select lines + bool m_rom1cs; + bool m_rom1am; + bool m_rom1al; + + // OSO select + bool m_alccs; + + // Pascal ROM select line + bool m_prcs; + + // Cartridge port select line + bool m_cmas; + + // GROM clock count (as frequency divider) + int m_gromclock_count; + + // Remember last msast state for edge detection + line_state m_msast; + + // Pointer to mainboard + mainboard8_device* m_mainboard; +}; + +/* + Custom chip: Amigo +*/ +class amigo_device : public device_t +{ public: - physically_addressed_device(mapper8_device_kind kind, device_t *busdevice, const mapper8_list_entry &entry) - : m_next(nullptr), m_kind(kind), m_device(busdevice), m_config(&entry) { }; + amigo_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + void device_start() override; + void device_reset() override; + + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + DECLARE_SETOFFSET_MEMBER( set_address ); + + DECLARE_WRITE_LINE_MEMBER( srdy_in ); + DECLARE_WRITE_LINE_MEMBER( clock_in ); + DECLARE_WRITE_LINE_MEMBER( crus_in ); + DECLARE_WRITE_LINE_MEMBER( lascs_in ); + DECLARE_WRITE_LINE_MEMBER( memen_in ); + + DECLARE_WRITE_LINE_MEMBER( holda_in ); + + DECLARE_READ_LINE_MEMBER( cpury_out ); + DECLARE_READ_LINE_MEMBER( sramcs_out ); + DECLARE_READ_LINE_MEMBER( skdrcs_out ); + + void connect_sram(UINT8* sram) { m_sram = sram; } + bool mapper_accessed() { return m_mapper_accessed; } private: - physically_addressed_device *m_next; // needed for simple_list - mapper8_device_kind m_kind; // named device or predefined - device_t *m_device; // the actual device - const mapper8_list_entry *m_config; + // Memory cycle state + bool m_memen; + + // DMA methods for loading/saving maps + void mapper_load(); + void mapper_save(); + + // Address mapper registers. Each offset is selected by the first 4 bits + // of the logical address. + UINT32 m_base_register[16]; + + // Indicates a logical space access + bool m_logical_space; + + // Physical address + UINT32 m_physical_address; + + // Pointer to SRAM where AMIGO needs to upload/download its map values + UINT8* m_sram; + + // Pointer to mainboard + mainboard8_device* m_mainboard; + + // Keep the system ready state + line_state m_srdy; + + // Outgoing READY level + line_state m_ready_out; + + // Keep the CRUS setting + line_state m_crus; + + // State of the address creation + int m_amstate; + + // Protection flags + int m_protflag; + + // Accessing SRAM + bool m_sram_accessed; + + // Accessing DRAM + bool m_dram_accessed; + + // Accessing the mapper + bool m_mapper_accessed; + + // Doing a DMA access + bool m_sram_dma; + + // HOLDA flag + bool m_hold_acknowledged; + + // Address in SRAM during DMA + UINT32 m_sram_address; + + // Number of the currently loaded/save base register + int m_basereg; + + // Latched value for mapper DMA transfer + UINT32 m_mapvalue; }; /* Custom chip: OSO */ -class ti998_oso_device : public device_t +class oso_device : public device_t { public: - ti998_oso_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + oso_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); DECLARE_READ8_MEMBER( read ); DECLARE_WRITE8_MEMBER( write ); void device_start() override; @@ -136,139 +391,176 @@ private: UINT8 m_xmit; }; -/* - Speech support -*/ -#define MCFG_SPEECH8_READY_CALLBACK(_write) \ - devcb = &ti998_spsyn_device::set_ready_wr_callback(*device, DEVCB_##_write); - -class ti998_spsyn_device : public bus8z_device +class mainboard8_device : public device_t { public: - ti998_spsyn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - - template<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object) { return downcast<ti998_spsyn_device &>(device).m_ready.set_callback(object); } - - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - - DECLARE_READ8Z_MEMBER(crureadz) { }; - DECLARE_WRITE8_MEMBER(cruwrite) { }; + mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - DECLARE_WRITE_LINE_MEMBER( speech8_ready ); + // Memory space + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + DECLARE_SETOFFSET_MEMBER( setoffset ); + + // 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<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object) + { + return downcast<mainboard8_device &>(device).m_ready.set_callback(object); + } + + template<class _Object> static devcb_base &set_reset_wr_callback(device_t &device, _Object object) + { + return downcast<mainboard8_device &>(device).m_console_reset.set_callback(object); + } + + template<class _Object> static devcb_base &set_hold_wr_callback(device_t &device, _Object object) + { + return downcast<mainboard8_device &>(device).m_hold_line.set_callback(object); + } + + void set_paddress(int address); + + // Ready lines from GROMs + DECLARE_WRITE_LINE_MEMBER( system_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( ptts_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( p8_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( p3_grom_ready ); + DECLARE_WRITE_LINE_MEMBER( sound_ready ); + DECLARE_WRITE_LINE_MEMBER( speech_ready ); + DECLARE_WRITE_LINE_MEMBER( pbox_ready ); + + // Emulation + // void set_gromport(gromport_device* dev) { m_gromport = dev; } protected: - virtual void device_start() override; - virtual void device_reset(void) override; - virtual const rom_entry *device_rom_region() const override; - virtual machine_config_constructor device_mconfig_additions() const override; + void device_start(void) override; + void device_reset(void) override; + machine_config_constructor device_mconfig_additions() const override; private: - tms5220_device *m_vsp; -// UINT8 *m_speechrom; // pointer to speech ROM data -// int m_load_pointer; // which 4-bit nibble will be affected by load address -// int m_rombits_count; // current bit position in ROM -// UINT32 m_sprom_address; // 18 bit pointer in ROM -// UINT32 m_sprom_length; // length of data pointed by speechrom_data, from 0 to 2^18 + // Propagates the end of the memory cycle + void cycle_end(); - // Ready line to the CPU - devcb_write_line m_ready; -}; + // Original logical address. + int m_logical_address; -#define MCFG_TISPEECH8_ADD(_tag, _conf) \ - MCFG_DEVICE_ADD(_tag, TI99_SPEECH8, 0) \ - MCFG_DEVICE_CONFIG(_conf) + // 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; -/* - Main class -*/ -class mainboard8_device : public bus8z_device -{ -public: - mainboard8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + // Indicates that a byte is waiting on the data bus (see m_latched_data) + bool m_pending_write; - template<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object) { return downcast<mainboard8_device &>(device).m_ready.set_callback(object); } + // Hold the value of the data bus. In a real machine, the data bus continues + // to show that value, but in this emulation we have a push mechanism. + UINT8 m_latched_data; - DECLARE_READ8_MEMBER( readm); // used from address map - DECLARE_WRITE8_MEMBER( writem ); // used from address map + // Hold the level of the GROMCLK line + int m_gromclk; - DECLARE_READ8Z_MEMBER( readz ) override; - DECLARE_WRITE8_MEMBER( write ) override; + // Selecting GROM libraries + void select_groms(); - DECLARE_READ8Z_MEMBER(crureadz); - DECLARE_WRITE8_MEMBER(cruwrite); + // Previous select state + int m_prev_grom; - void CRUS_set(bool state); - void PTGE_set(bool state); + // Ready states + bool m_speech_ready; + bool m_sound_ready; + bool m_pbox_ready; - void clock_in(int state); + // Holds the A14 address line state. We need this for the clock_in method. + bool m_A14_set; -protected: - void device_start(void) override; - void device_reset(void) override; - machine_config_constructor device_mconfig_additions() const override; + // 99/4A compatibility mode. Name is taken from the spec. If asserted, 99/4A compatibility is active. + line_state m_crus; -private: - bool access_logical_r(address_space& space, offs_t offset, UINT8 *value, UINT8 mem_mask ); - bool access_logical_w(address_space& space, offs_t offset, UINT8 data, UINT8 mem_mask ); - void access_physical_r(address_space& space, offs_t offset, UINT8 *value, UINT8 mem_mask ); - void access_physical_w(address_space& space, offs_t offset, UINT8 data, UINT8 mem_mask ); - void mapwrite(int offset, UINT8 data); + // P-Code mode, negative logic. Name is taken from the spec. If asserted, P-Code libraries are available. + // May be read as "Pascal and Text-to-speech GROM libraries ENable" + line_state m_ptgen; + + // Keeps the recent DBIN level + line_state m_dbin_level; // Ready line to the CPU devcb_write_line m_ready; - // All devices that are attached to the 16-bit address bus. - simple_list<logically_addressed_device> m_logcomp; + // Reset line to the main system + devcb_write_line m_console_reset; + + // Hold line to the main system + devcb_write_line m_hold_line; - // All devices that are attached to the 24-bit mapped address bus. - simple_list<physically_addressed_device> m_physcomp; + // Custom chips + required_device<vaquerro_device> m_vaquerro; + required_device<mofetta_device> m_mofetta; + required_device<amigo_device> m_amigo; + required_device<oso_device> m_oso; - // Select bit for the internal DSR. - bool m_dsr_selected; + // Debugging + line_state m_last_ready; - // Select bit for the Hexbus DSR. - bool m_hexbus_selected; + // Video processor + tms9118_device* m_video; - // 99/4A compatibility mode. Name is taken from the spec. If 1, 99/4A compatibility is active. - bool m_CRUS; + // Sound generator + sn76496_base_device* m_sound; - // P-Code mode. Name is taken from the spec. If 0, P-Code libraries are available. - // May be read as "Pascal and Text-to-speech GROM libraries Enable (Negative)" - // Note: this is negative logic. GROMs are present only for PTGEN=0 - // We use PTGE as the inverse signal. - bool m_PTGE; + // Speech processor + cd2501ecd_device* m_speech; - // Counter for the wait states. - int m_waitcount; + // System GROM library + tmc0430_device* m_sgrom[3]; - // Address mapper registers. Each offset is selected by the first 4 bits - // of the logical address. - UINT32 m_pas_offset[16]; + // Text-to-speech GROM library + tmc0430_device* m_tsgrom[8]; - // SRAM area of the system. Directly connected to the address decoder. - UINT8 *m_sram; + // Pascal 8 GROM library + tmc0430_device* m_p8grom[8]; - // DRAM area of the system. Connected to the mapped address bus. - UINT8 *m_dram; + // Pascal 3 GROM library + tmc0430_device* m_p3grom[3]; - // ROM area of the system. Directly connected to the logical address decoder. - UINT8 *m_rom0; + // Gromport (cartridge port) + gromport_device* m_gromport; - // ROM area of the system. Directly connected to the physical address decoder. - UINT8 *m_rom1; + // Peripheral box + peribox_device* m_peb; - // P-Code ROM area of the system. Directly connected to the physical address decoder. - UINT8 *m_pcode; + // Memory + std::unique_ptr<UINT8[]> m_sram; + std::unique_ptr<UINT8[]> m_dram; - // Custom chips - required_device<ti998_oso_device> m_oso; + // ROM area of the system. + UINT8* m_rom0; + UINT8* m_rom1; + UINT8* m_pascalrom; }; +#define MCFG_MAINBOARD8_READY_CALLBACK(_write) \ + devcb = &mainboard8_device::set_ready_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MAINBOARD8_RESET_CALLBACK(_write) \ + devcb = &mainboard8_device::set_reset_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MAINBOARD8_HOLD_CALLBACK(_write) \ + devcb = &mainboard8_device::set_hold_wr_callback(*device, DEVCB_##_write); -#define MCFG_MAINBOARD8_ADD(_tag, _devices) \ - MCFG_DEVICE_ADD(_tag, MAINBOARD8, 0) \ - MCFG_DEVICE_CONFIG( _devices ) #endif diff --git a/src/devices/bus/ti99x/datamux.cpp b/src/devices/bus/ti99x/datamux.cpp index 1b8ec8de02f..da971c4ceec 100644 --- a/src/devices/bus/ti99x/datamux.cpp +++ b/src/devices/bus/ti99x/datamux.cpp @@ -75,9 +75,20 @@ Constructor */ ti99_datamux_device::ti99_datamux_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) -: device_t(mconfig, DATAMUX, "Databus multiplexer", tag, owner, clock, "ti99_datamux", __FILE__), m_spacep(nullptr), - m_ready(*this), m_muxready(), m_sysready(), m_addr_buf(0), m_read_mode(false), m_latch(0), m_waitcount(0), m_ram16b(nullptr), m_use32k(false), m_base32k(0), m_cpu(nullptr) -{ } + : device_t(mconfig, DATAMUX, "Databus multiplexer", tag, owner, clock, "ti99_datamux", __FILE__), + m_spacep(nullptr), + m_ready(*this), + m_addr_buf(0), + m_dbin(CLEAR_LINE), + m_muxready(CLEAR_LINE), + m_sysready(CLEAR_LINE), + m_latch(0), + m_waitcount(0), + m_ram16b(nullptr), + m_use32k(false), + m_base32k(0), + m_console_groms_present(false) + { } #define TRACE_READY 0 #define TRACE_ACCESS 0 @@ -89,140 +100,236 @@ ti99_datamux_device::ti99_datamux_device(const machine_config &mconfig, const ch DEVICE ACCESSOR FUNCTIONS ***************************************************************************/ -void ti99_datamux_device::read_all(address_space& space, UINT16 addr, UINT8 *target) +void ti99_datamux_device::read_all(address_space& space, UINT16 addr, UINT8 *value) { - attached_device *dev = m_devices.first(); + // Valid access + bool validaccess = ((addr & 0x0400)==0); - // Reading the odd address first (addr+1) - while (dev != nullptr) + if (validaccess) { - if (dev->m_config->write_select != 0xffff) // write-only + // GROM access + if ((addr & 0xf801)==0x9800) { - if ((addr & dev->m_config->address_mask)==dev->m_config->select) + if (m_console_groms_present) { - // Cast to the bus8z_device (see ti99defs.h) - bus8z_device *devz = static_cast<bus8z_device *>(dev->m_device); - devz->readz(space, addr, target); + for (int i=0; i < 3; i++) + { + m_grom[i]->readz(space, addr, value); + } } - // hope we don't have two devices answering... - // consider something like a logical OR and maybe some artificial smoke + // GROMport (GROMs) + m_gromport->readz(space, addr, value); } - dev = dev->m_next; + + // Video + if ((addr & 0xf801)==0x8800) m_video->readz(space, addr, value); } + + // GROMport (ROMs) + if ((addr & 0xe000)==0x6000) m_gromport->readz(space, addr, value); + + // PEB gets all accesses + m_peb->readz(space, addr, value); + m_peb->memen_in(CLEAR_LINE); } void ti99_datamux_device::write_all(address_space& space, UINT16 addr, UINT8 value) { - attached_device *dev = m_devices.first(); - while (dev != nullptr) + // GROM access + if ((addr & 0xf801)==0x9800) { - if ((addr & dev->m_config->address_mask)==(dev->m_config->select | dev->m_config->write_select)) + if (m_console_groms_present) { - bus8z_device *devz = static_cast<bus8z_device *>(dev->m_device); - devz->write(space, addr, value); + for (int i=0; i < 3; i++) + m_grom[i]->write(space, addr, value); } - dev = dev->m_next; + // GROMport + m_gromport->write(space, addr, value); } + + // Other devices + if ((addr & 0xe000)==0x6000) m_gromport->write(space, addr, value); + if ((addr & 0xfc01)==0x8400) m_sound->write(space, addr, value); + if ((addr & 0xf801)==0x8800) m_video->write(space, addr, value); + + // PEB gets all accesses + m_peb->write(space, addr, value); + m_peb->memen_in(CLEAR_LINE); } void ti99_datamux_device::setaddress_all(address_space& space, UINT16 addr) { - attached_device *dev = m_devices.first(); - while (dev != nullptr) + line_state a14 = ((addr & 2)!=0)? ASSERT_LINE : CLEAR_LINE; + + // Valid access = not(DBIN and A5) + bool validaccess = (m_dbin==CLEAR_LINE || (addr & 0x0400)==0); + + // GROM access + bool isgrom = ((addr & 0xf801)==0x9800) && validaccess; + + // Cartridge ROM + bool iscartrom = ((addr & 0xe000)==0x6000); + + // Always deliver to GROM so that the select line may be cleared + int lines = (m_dbin==ASSERT_LINE)? 1 : 0; + if (a14==ASSERT_LINE) lines |= 2; + line_state select = isgrom? ASSERT_LINE : CLEAR_LINE; + + if (m_console_groms_present) + for (int i=0; i < 3; i++) + m_grom[i]->set_lines(space, lines, select); + + // GROMport (GROMs) + m_gromport->set_gromlines(space, lines, select); + + if (validaccess) { - if ((addr & dev->m_config->address_mask)==(dev->m_config->select | dev->m_config->write_select)) - { - bus8z_device *devz = static_cast<bus8z_device *>(dev->m_device); - devz->setaddress_dbin(space, addr, m_read_mode? ASSERT_LINE : CLEAR_LINE); - } - dev = dev->m_next; + // Other devices + if ((addr & 0xfc01)==0x8400) m_sound->setaddress_dbin(space, addr, m_dbin); + if ((addr & 0xf801)==0x8800) m_video->setaddress_dbin(space, addr, m_dbin); } + + // GROMport (ROMs) + m_gromport->romgq_line(iscartrom? ASSERT_LINE : CLEAR_LINE); + + // PEB gets all accesses + m_peb->memen_in(ASSERT_LINE); + m_peb->setaddress_dbin(space, addr, m_dbin); } /* - Special debugger access; these routines have no influence on the wait - state generation. + Special debugger access. The access is similar to the normal access, + but it bypasses the wait state circuitry. Also, access ports of memory- + mapped devices are excluded because their state would be changed + unpredictably by the debugger access. */ UINT16 ti99_datamux_device::debugger_read(address_space& space, UINT16 addr) { - UINT16 base32k = 0; - UINT8 lval, hval; - UINT16 addrb = addr << 1; - if (m_use32k) - { - if ((addrb & 0xe000)==0x2000) base32k = 0x1000; - if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x4000; - } - if (base32k != 0) - { - return m_ram16b[addr - base32k]; - } + UINT16 value = 0; + + if ((addrb & 0xe000)==0x0000) value = m_consolerom[(addrb & 0x1fff)>>1]; else { - lval = hval = 0; - read_all(space, addrb+1, &lval); - read_all(space, addrb, &hval); - return ((hval << 8)&0xff00) | (lval & 0xff); + if ((addrb & 0xfc00)==0x8000) value = m_padram[(addrb & 0x00ff)>>1]; + else + { + int base32k = 0; + if (m_use32k) + { + if ((addrb & 0xe000)==0x2000) base32k = 0x2000; + if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000; + } + + if (base32k != 0) value = m_ram16b[(addrb-base32k)>>1]; + else + { + UINT8 lval = 0; + UINT8 hval = 0; + + if ((addr & 0xe000)==0x6000) + { + m_gromport->readz(space, addrb+1, &lval); + m_gromport->readz(space, addrb, &hval); + } + m_peb->memen_in(ASSERT_LINE); + m_peb->readz(space, addrb+1, &lval); + m_peb->readz(space, addrb, &hval); + m_peb->memen_in(CLEAR_LINE); + value = ((hval << 8)&0xff00) | (lval & 0xff); + } + } } + return value; } void ti99_datamux_device::debugger_write(address_space& space, UINT16 addr, UINT16 data) { - UINT16 base32k = 0; - UINT16 addrb = addr << 1; - if (m_use32k) - { - if ((addrb & 0xe000)==0x2000) base32k = 0x1000; - if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x4000; - } - if (base32k != 0) - { - m_ram16b[addr - base32k] = data; - } + + if ((addrb & 0xe000)==0x0000) return; + + if ((addrb & 0xfc00)==0x8000) m_padram[(addrb & 0x00ff)>>1] = data; else { - write_all(space, addrb+1, data & 0xff); - write_all(space, addrb, (data >> 8) & 0xff); + int base32k = 0; + if (m_use32k) + { + if ((addrb & 0xe000)==0x2000) base32k = 0x2000; + if (((addrb & 0xe000)==0xa000) || ((addrb & 0xc000)==0xc000)) base32k = 0x8000; + } + + if (base32k != 0) m_ram16b[(addrb-base32k)>>1] = data; + else + { + if ((addr & 0xe000)==0x6000) + { + m_gromport->write(space, addr+1, data & 0xff); + m_gromport->write(space, addr, (data>>8) & 0xff); + } + + m_peb->memen_in(ASSERT_LINE); + m_peb->write(space, addr+1, data & 0xff); + m_peb->write(space, addr, (data>>8) & 0xff); + m_peb->memen_in(CLEAR_LINE); + } } } /* Read access. We are using two loops because the delay between both accesses must not occur within the loop. So we have one access on the bus, - a delay, and then the second access (each one with possibly many attached - devices) + a delay, and then the second access. + + mem_mask is always ffff on TMS processors (cannot control bus width) */ READ16_MEMBER( ti99_datamux_device::read ) { + UINT16 value = 0; + // Care for debugger if (space.debugger_access()) { return debugger_read(space, offset); } - // Looks ugly, but this is close to the real thing. If the 16bit - // memory expansion is installed in the console, and the access hits its - // space, just respond to the memory access and don't bother the - // datamux in any way. In particular, do not make the datamux insert wait - // states. - - if (m_base32k != 0) + // Addresses below 0x2000 are ROM (no wait states) + if ((m_addr_buf & 0xe000)==0x0000) { - UINT16 reply = m_ram16b[offset-m_base32k]; - return reply & mem_mask; + value = m_consolerom[(m_addr_buf & 0x1fff)>>1]; } else { - // The byte from the odd address has already been read into the latch - // Reading the even address now (addr) - UINT8 hbyte = 0; - read_all(space, m_addr_buf, &hbyte); - if (TRACE_ACCESS) logerror("datamux: read even byte from address %04x -> %02x\n", m_addr_buf, hbyte); + // Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM (no wait states) + if ((m_addr_buf & 0xfc00)==0x8000) + { + value = m_padram[(m_addr_buf & 0x00ff)>>1]; + } + else + { + // Looks ugly, but this is close to the real thing. If the 16bit + // memory expansion is installed in the console, and the access hits its + // space, just respond to the memory access and don't bother the + // datamux in any way. In particular, do not make the datamux insert wait + // states. + + if (m_base32k != 0) + { + value = m_ram16b[(m_addr_buf-m_base32k)>>1]; + } + else + { + // The byte from the odd address has already been read into the latch + // Reading the even address now (addr) + UINT8 hbyte = 0; + read_all(space, m_addr_buf, &hbyte); + if (TRACE_ACCESS) logerror("Read even byte from address %04x -> %02x\n", m_addr_buf, hbyte); - return ((hbyte<<8) | m_latch) & mem_mask; + value = (hbyte<<8) | m_latch; + } + } } + return value; } /* @@ -230,21 +337,29 @@ READ16_MEMBER( ti99_datamux_device::read ) */ WRITE16_MEMBER( ti99_datamux_device::write ) { - // Addresses below 0x2000 are ROM and should be handled in the address map - // by the ROM entry, but as the write handler for ROM is not mapped, we end up - // here when there are invalid accesses, and this will mess up everything. - if (offset < 0x1000) return; - if (space.debugger_access()) { debugger_write(space, offset, data); return; } + // Addresses below 0x2000 are ROM + if ((m_addr_buf & 0xe000)==0x0000) + { + return; + } + + // Addresses from 8300-83ff (mirrors at 8000, 8100, 8200) are console RAM + if ((m_addr_buf & 0xfc00)==0x8000) + { + m_padram[(m_addr_buf & 0x00ff)>>1] = data; + return; + } + // Handle the internal 32K expansion if (m_base32k != 0) { - m_ram16b[offset-m_base32k] = data; + m_ram16b[(m_addr_buf-m_base32k)>>1] = data; } else { @@ -264,21 +379,34 @@ WRITE16_MEMBER( ti99_datamux_device::write ) */ SETOFFSET_MEMBER( ti99_datamux_device::setoffset ) { - if (TRACE_ADDRESS) logerror("datamux: set address %04x\n", offset << 1); + m_addr_buf = offset << 1; + m_waitcount = 0; + + if (TRACE_ADDRESS) logerror("set address %04x\n", m_addr_buf); + + if ((m_addr_buf & 0xe000) == 0x0000) + { + return; // console ROM + } + + if ((m_addr_buf & 0xfc00) == 0x8000) + { + return; // console RAM + } + // Initialize counter // 1 cycle for loading into the datamux // 2 subsequent wait states (LSB) // 2 subsequent wait states (MSB) // clock cycle 6 is the nominal follower of the last wait state m_waitcount = 5; - m_addr_buf = offset << 1; m_spacep = &space; m_base32k = 0; if (m_use32k) { - if ((m_addr_buf & 0xe000)==0x2000) m_base32k = 0x1000; - if (((m_addr_buf & 0xe000)==0xa000) || ((m_addr_buf & 0xc000)==0xc000)) m_base32k = 0x4000; + if ((m_addr_buf & 0xe000)==0x2000) m_base32k = 0x2000; + if (((m_addr_buf & 0xe000)==0xa000) || ((m_addr_buf & 0xc000)==0xc000)) m_base32k = 0x8000; } // Suspend the CPU if not using the 32K @@ -308,13 +436,13 @@ WRITE_LINE_MEMBER( ti99_datamux_device::clock_in ) if (TRACE_READY) logerror("datamux: stalled due to external READY=0\n"); return; } - if (m_read_mode) + + if (m_dbin==ASSERT_LINE) { // Reading if (state==ASSERT_LINE) { // raising edge - m_waitcount--; - if (m_waitcount==0) + if (--m_waitcount==0) { m_muxready = ASSERT_LINE; ready_join(); @@ -333,8 +461,7 @@ WRITE_LINE_MEMBER( ti99_datamux_device::clock_in ) { if (state==ASSERT_LINE) { // raising edge - m_waitcount--; - if (m_waitcount==0) + if (--m_waitcount==0) { m_muxready = ASSERT_LINE; ready_join(); @@ -365,21 +492,31 @@ void ti99_datamux_device::ready_join() WRITE_LINE_MEMBER( ti99_datamux_device::dbin_in ) { - m_read_mode = (state==ASSERT_LINE); - if (TRACE_ADDRESS) logerror("datamux: data bus in = %d\n", m_read_mode? 1:0 ); + m_dbin = (line_state)state; + if (TRACE_ADDRESS) logerror("data bus in = %d\n", (m_dbin==ASSERT_LINE)? 1:0 ); } WRITE_LINE_MEMBER( ti99_datamux_device::ready_line ) { if (TRACE_READY) { - if (state != m_sysready) logerror("datamux: READY line from PBox = %d\n", state); + if (state != m_sysready) logerror("READY line from PBox = %d\n", state); } m_sysready = (line_state)state; // Also propagate to CPU via driver ready_join(); } +WRITE_LINE_MEMBER( ti99_datamux_device::gromclk_in ) +{ + // Propagate to the GROMs + if (m_console_groms_present) + { + for (int i=0; i < 3; i++) m_grom[i]->gclock_in(state); + } + m_gromport->gclock_in(state); +} + /*************************************************************************** DEVICE LIFECYCLE FUNCTIONS ***************************************************************************/ @@ -398,15 +535,9 @@ void ti99_datamux_device::device_stop(void) void ti99_datamux_device::device_reset(void) { - const datamux_config *conf = reinterpret_cast<const datamux_config *>(static_config()); - - const dmux_device_list_entry *list = conf->devlist; - - m_cpu = machine().device("maincpu"); - // m_space = &m_cpu->memory().space(AS_PROGRAM); - - m_devices.reset(); // clear the list + m_consolerom = (UINT16*)owner()->memregion(CONSOLEROM)->base(); m_use32k = (ioport("RAM")->read()==1); + m_console_groms_present = (ioport("GROMENA")->read()==1); // better use a region? if (m_ram16b==nullptr) @@ -414,51 +545,6 @@ void ti99_datamux_device::device_reset(void) m_ram16b = make_unique_clear<UINT16[]>(32768/2); } - // Now building the list of active devices at this databus multiplex. - // We allow for turning off devices according to configuration switch settings. - // In particular, the HSGPL card cannot function unless the console GROMs are - // removed. - if ( list != nullptr ) - { - bool done = false; - for (int i=0; !done; i++) - { - if (list[i].name == nullptr) - { - done = true; - } - else - { - UINT32 set; - bool active_device = true; - if (list[i].setting!=nullptr) - { - set = ioport(list[i].setting)->read(); - active_device = ((set & list[i].set)==list[i].set) && ((set & list[i].unset)==0); - } - if (active_device) - { - device_t *dev = machine().device(list[i].name); - if (dev != nullptr) - { - auto ad = new attached_device(dev, list[i]); - m_devices.append(*ad); - if (TRACE_SETUP) logerror("datamux: Device %s mounted at index %d.\n", list[i].name, i); - } - else - { - if (TRACE_SETUP) logerror("datamux: Device %s not found.\n", list[i].name); - } - } - else - { - if (TRACE_SETUP) logerror("datamux: Device %s not mounted due to configuration setting %s.\n", list[i].name, list[i].setting); - } - } - } - } - if (TRACE_SETUP) logerror("datamux: Device count = %d\n", m_devices.count()); - m_sysready = ASSERT_LINE; m_muxready = ASSERT_LINE; ready_join(); @@ -466,9 +552,22 @@ void ti99_datamux_device::device_reset(void) m_waitcount = 0; m_latch = 0; - m_read_mode = true; + m_dbin = CLEAR_LINE; } +void ti99_datamux_device::device_config_complete() +{ + m_video = downcast<bus8z_device*>(owner()->subdevice(VIDEO_SYSTEM_TAG)); + m_sound = downcast<ti_sound_sn94624_device*>(owner()->subdevice(TISOUND_TAG)); + m_gromport = downcast<gromport_device*>(owner()->subdevice(GROMPORT_TAG)); + m_peb = downcast<peribox_device*>(owner()->subdevice(PERIBOX_TAG)); + m_grom[0] = downcast<tmc0430_device*>(owner()->subdevice(GROM0_TAG)); + m_grom[1] = downcast<tmc0430_device*>(owner()->subdevice(GROM1_TAG)); + m_grom[2] = downcast<tmc0430_device*>(owner()->subdevice(GROM2_TAG)); + m_padram = make_unique_clear<UINT16[]>(256/2); +} + + INPUT_PORTS_START( datamux ) PORT_START( "RAM" ) /* config */ PORT_CONFNAME( 0x01, 0x00, "Console 32 KiB RAM upgrade (16 bit)" ) diff --git a/src/devices/bus/ti99x/datamux.h b/src/devices/bus/ti99x/datamux.h index 604dfb2e62f..eaad45b7238 100644 --- a/src/devices/bus/ti99x/datamux.h +++ b/src/devices/bus/ti99x/datamux.h @@ -15,54 +15,14 @@ #define __DMUX__ #include "ti99defs.h" +#include "videowrp.h" +#include "machine/tmc0430.h" +#include "gromport.h" +#include "bus/ti99_peb/peribox.h" extern const device_type DATAMUX; /* - Device that is attached to this datamux. - The configuration setting is used for certain configurations - where devices may only be used if others are turned off. In particular, - if the HGSPL expansion card is used, the GROMs in the console must be - removed. -*/ -struct dmux_device_list_entry -{ - const char *name; // Name of the device (used for looking up the device) - UINT16 select; // State of the address line bits when addressing this device - UINT16 address_mask; // Bits of the address bus used to address this device - UINT16 write_select; // Bits set when doing write accesses to this device (ffff = write-only) - const char *setting; // configuration switch that may have an effect for the presence of this device - UINT8 set; // bits that must be set for this switch so that this device is present - UINT8 unset; // bits that must be reset for this switch so that this device is present -}; - -#define DMUX_CONFIG(name) \ - const datamux_config(name) = - -struct datamux_config -{ - const dmux_device_list_entry *devlist; -}; - -/* - Device list of this datamux. -*/ -class attached_device -{ - friend class simple_list<attached_device>; - friend class ti99_datamux_device; - -public: - attached_device(device_t *busdevice, const dmux_device_list_entry &entry) - : m_next(nullptr), m_device(busdevice), m_config(&entry) { }; - -private: - attached_device *m_next; - device_t *m_device; // the actual device - const dmux_device_list_entry *m_config; -}; - -/* Main class */ class ti99_datamux_device : public device_t @@ -77,6 +37,8 @@ public: DECLARE_WRITE_LINE_MEMBER( dbin_in ); DECLARE_WRITE_LINE_MEMBER( ready_line ); + DECLARE_WRITE_LINE_MEMBER( gromclk_in ); + template<class _Object> static devcb_base &static_set_ready_callback(device_t &device, _Object object) { return downcast<ti99_datamux_device &>(device).m_ready.set_callback(object); @@ -87,9 +49,22 @@ protected: void device_start() override; void device_stop() override; void device_reset() override; + void device_config_complete() override; ioport_constructor device_input_ports() const override; private: + // Link to the video processor + bus8z_device* m_video; + + // Link to the sound processor + ti_sound_sn94624_device* m_sound; + + // Link to the peripheral expansion box + peribox_device* m_peb; + + // Link to the cartridge port (aka GROM port) + gromport_device* m_gromport; + // Keeps the address space pointer address_space* m_spacep; @@ -112,21 +87,18 @@ private: // Ready line to the CPU devcb_write_line m_ready; + // Address latch (emu). In reality, the address bus remains constant. + UINT16 m_addr_buf; + + // DBIN line + line_state m_dbin; + // Own ready state. line_state m_muxready; // Ready state. Needed to control wait state generation via inbound READY line_state m_sysready; - /* Address latch (emu). In reality, the address bus remains constant. */ - UINT16 m_addr_buf; - - /* Stores the state of the DBIN line. */ - bool m_read_mode; - - /* All devices that are attached to the 8-bit bus. */ - simple_list<attached_device> m_devices; - /* Latch which stores the first (odd) byte */ UINT8 m_latch; @@ -136,22 +108,28 @@ private: /* Memory expansion (internal, 16 bit). */ std::unique_ptr<UINT16[]> m_ram16b; + // Console RAM + std::unique_ptr<UINT16[]> m_padram; + + // Console ROM + UINT16* m_consolerom; + + // Console GROMs + tmc0430_device* m_grom[3]; + /* Use the memory expansion? */ bool m_use32k; /* Memory base for piggy-back 32K expansion. If 0, expansion is not used. */ UINT16 m_base32k; - /* Reference to the CPU; avoid lookups. */ - device_t *m_cpu; + // Console GROMs are available (the HSGPL expects them to be removed) + bool m_console_groms_present; }; /******************************************************************************/ -#define MCFG_DMUX_ADD(_tag, _devices) \ - MCFG_DEVICE_ADD(_tag, DATAMUX, 0) \ - MCFG_DEVICE_CONFIG( _devices ) -#endif - #define MCFG_DMUX_READY_HANDLER( _intcallb ) \ devcb = &ti99_datamux_device::static_set_ready_callback( *device, DEVCB_##_intcallb ); + +#endif diff --git a/src/devices/bus/ti99x/genboard.cpp b/src/devices/bus/ti99x/genboard.cpp index 9d6aeef7e76..1abdd7c6762 100644 --- a/src/devices/bus/ti99x/genboard.cpp +++ b/src/devices/bus/ti99x/genboard.cpp @@ -535,6 +535,7 @@ READ8_MEMBER( geneve_mapper_device::readm ) // 1001 0000 0000 0000 // We need to add the address prefix bits m_peribox->readz(space, dec->offset, &value, 0xff); + m_peribox->memen_in(CLEAR_LINE); if (TRACE_READ) logerror("%s: Read speech -> %02x\n", tag(), value); break; @@ -594,6 +595,7 @@ READ8_MEMBER( geneve_mapper_device::readm ) // 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15) m_peribox->readz(space, dec->physaddr, &value, 0xff); + m_peribox->memen_in(CLEAR_LINE); if (TRACE_READ) logerror("%s: Read P-Box %04x (%06x) -> %02x\n", tag(), dec->offset, dec->physaddr, value); break; @@ -616,6 +618,7 @@ READ8_MEMBER( geneve_mapper_device::readm ) case MPGMBOX: // Route everything else to the P-Box m_peribox->readz(space, dec->physaddr, &value, 0xff); + m_peribox->memen_in(CLEAR_LINE); break; } return value; @@ -701,6 +704,7 @@ WRITE8_MEMBER( geneve_mapper_device::writem ) // 1001 0100 0000 0000 // We need to add the address prefix bits m_peribox->write(space, dec->offset, data, 0xff); + m_peribox->memen_in(CLEAR_LINE); if (TRACE_WRITE) logerror("%s: Write speech <- %02x\n", tag(), data); break; @@ -756,6 +760,7 @@ WRITE8_MEMBER( geneve_mapper_device::writem ) dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address if (TRACE_WRITE) logerror("%s: Write P-Box %04x (%06x) <- %02x\n", tag(), offset, dec->physaddr, data); m_peribox->write(space, dec->physaddr, data, 0xff); + m_peribox->memen_in(CLEAR_LINE); break; case MPGMDRAM: @@ -775,6 +780,7 @@ WRITE8_MEMBER( geneve_mapper_device::writem ) case MPGMBOX: // Route everything else to the P-Box m_peribox->write(space, dec->physaddr, data, 0xff); + m_peribox->memen_in(CLEAR_LINE); break; } } @@ -875,6 +881,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read // We need to add the address prefix bits dec->function = MLTSPEECH; dec->offset = offset | ((m_genmod)? 0x170000 : 0x070000); + m_peribox->memen_in(ASSERT_LINE); m_peribox->setaddress_dbin(space, dec->offset, read_mode); set_wait(1); return; @@ -954,6 +961,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read dec->function = MPGBOX; dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address (with AMA..AMC) + m_peribox->memen_in(ASSERT_LINE); m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); return; } @@ -986,6 +994,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read // Check: Are waitstates completely turned off for turbo mode, or // merely the waitstates for DRAM memory access and box access? + m_peribox->memen_in(ASSERT_LINE); m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); return; } @@ -1072,6 +1081,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read { dec->function = MLTSPEECH; dec->offset = dec->offset | ((m_genmod)? 0x170000 : 0x070000); + m_peribox->memen_in(ASSERT_LINE); m_peribox->setaddress_dbin(space, dec->offset, read_mode); set_wait(1); return; @@ -1151,6 +1161,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read // only AMA, AMB, AMC are used; AMD and AME are not used dec->function = MPGBOX; dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address + m_peribox->memen_in(ASSERT_LINE); m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); set_wait(1); } @@ -1175,6 +1186,7 @@ void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read // Route everything else to the P-Box dec->function = MPGMBOX; dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod + m_peribox->memen_in(ASSERT_LINE); m_peribox->setaddress_dbin(space, dec->physaddr, read_mode); if (!m_turbo) set_wait(1); } @@ -1346,7 +1358,7 @@ WRITE_LINE_MEMBER( geneve_mapper_device::pfm_output_enable ) void geneve_mapper_device::device_start() { // Get pointers - m_peribox = machine().device<bus8z_device>(PERIBOX_TAG); + m_peribox = machine().device<peribox_device>(PERIBOX_TAG); m_keyboard = machine().device<geneve_keyboard_device>(GKEYBOARD_TAG); m_video = machine().device<bus8z_device>(VIDEO_SYSTEM_TAG); m_sound = machine().device<bus8z_device>(TISOUND_TAG); diff --git a/src/devices/bus/ti99x/genboard.h b/src/devices/bus/ti99x/genboard.h index 2248c64f9fe..8f49cb6906e 100644 --- a/src/devices/bus/ti99x/genboard.h +++ b/src/devices/bus/ti99x/genboard.h @@ -19,6 +19,7 @@ #include "video/v9938.h" #include "cpu/tms9900/tms9995.h" #include "machine/at29x.h" +#include "bus/ti99_peb/peribox.h" extern const device_type GENEVE_MOUSE; extern const device_type GENEVE_KEYBOARD; @@ -207,7 +208,7 @@ private: geneve_keyboard_device* m_keyboard; bus8z_device* m_video; - bus8z_device* m_peribox; + peribox_device* m_peribox; bus8z_device* m_sound; UINT8* m_eprom; UINT8* m_sram; diff --git a/src/devices/bus/ti99x/grom.cpp b/src/devices/bus/ti99x/grom.cpp deleted file mode 100644 index 1ecb0168d80..00000000000 --- a/src/devices/bus/ti99x/grom.cpp +++ /dev/null @@ -1,269 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/*************************************************************************** - - GROM emulation (aka TMC0430) - - +----+--+----+ - AD7 |1 G 16| Vss - AD6 |2 R 15| GR - AD5 |3 O 14| Vdd - AD4 |4 M 13| GRC - AD3 |5 12| M - AD2 |6 11| MO - AD1 |7 10| GS* - AD0 |8 9| Vcc - +------------+ - - GR = GROM Ready. Should be connected to processor's READY/HOLD*. - GRC = GROM clock. Typically in the range of 400-500 kHz. - M = Direction. 1=read, 0=write - MO = Mode. 1=address counter access, 0=data access - GS* = GROM select. 0=select, 1=deselect - - GROMs are slow ROM devices, which are - interfaced via a 8-bit data bus, and include an internal address pointer - which is incremented after each read. This implies that accesses are - faster when reading consecutive bytes, although the address pointer can be - read and written at any time. - - GROMs are generally used to store programs written in GPL (Graphic Programming - Language): a proprietary, interpreted language. The GPL interpreter takes - most space of the TI-99/4A system ROMs. - - Both TI-99/4 and TI-99/4A include three GROMs, with some start-up code, - system routines and TI-Basic. TI99/4 includes an additional Equation - Editor. According to the preliminary schematics found on ftp.whtech.com, - TI-99/8 includes the three standard GROMs and 16 GROMs for the UCSD - p-system. TI99/2 does not include GROMs at all, and was not designed to - support any, although it should be relatively easy to create an expansion - card with the GPL interpreter and a /4a cartridge port. - - Communication with GROM is done by writing and reading data over the - AD0-AD7 lines. Within the TI-99 systems, the address bus is decoded for - the M, GS*, and MO lines: Writing a byte to address 9c02 asserts the GS* and - MO line, and clears the M line, which means the transmitted byte is put into - the internal address register. Two bytes must be written to set up the - complete address. - - It was obviously planned to offer GRAM circuits as well, since the - programming manuals refer to writing to a special address, clearing the MO - line. Although the TI-99 systems reserve a port in the memory space, no one - has ever seen a GRAM circuit in the wild. However, third-party products like - HSGPL or GRAM Kracker simulate GRAMs using conventional RAM with some - addressing circuitry, usually in a custom chip. - - Each GROM is logically 8 KiB long. Original TI-built GROM are 6 KiB long; - the extra 2kb can be read, and follow the following formula: - - GROM[0x1800+offset] = GROM[0x0800+offset] | GROM[0x1000+offset]; - - (sounds like address decoding is incomplete - we are lucky we don't burn - any silicon when doing so... Needless to say, some hackers simulated 8kb - GRAMs and GROMs with normal RAM/PROM chips and glue logic.) - - The address pointer is incremented after each GROM operation, but it will - always remain within the bounds of the currently selected GROM (e.g. after - 0x3fff comes 0x2000). - - Since address are 16-bit long, you can have up to 8 GROMs. Accordingly, - a cartridge may include up to 5 GROMs. - - Every GROM has an internal ID which represents the high-order three - address bits. The address counter can be set to any value from 0 - to 0xffff; the GROM will only react when selected and when the current - address counter's high-order bits match the ID of the chip. - Example: When the ID is 6, the GROM will react when the address - counter contains a value from 0xc000 to 0xdfff. - - CHECK: Reading the address increases the counter only once. The first access - returns the MSB, the second (and all following accesses) return the LSB. - - Michael Zapf, August 2010 - January 2012: rewritten as class - -***************************************************************************/ - -#include "emu.h" -#include "grom.h" - -#define TRACE_ADDRESS 0 - -/* - Constructor. -*/ -ti99_grom_device::ti99_grom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) -: bus8z_device(mconfig, GROM, "TI-99 GROM device", tag, owner, clock, "ti99_grom", __FILE__), m_writable(false), m_ident(0), m_size(0), - m_gromready(*this), m_clockrate(0), m_address(0), m_buffer(0), m_raddr_LSB(false), m_waddr_LSB(false), m_memptr(nullptr), m_timer(nullptr) -{ -} - -/* - Reading from the chip. Represents an access with M=1, GS*=0. The MO bit is - defined by the offset (0 or 1). This is the enhanced read function with - Z state. -*/ -READ8Z_MEMBER( ti99_grom_device::readz ) -{ - // Prevent debugger access - if (space.debugger_access()) return; - - if (offset & 2) - { - // GROMs generally answer the address read request - // (important if GROM simulators do not serve the request but rely on - // the console GROMs) so we don't check the ident - - /* When reading, reset the hi/lo flag byte for writing. */ - /* TODO: Verify this with a real machine. */ - m_waddr_LSB = false; - - /* Address reading is done in two steps; first, the high byte */ - /* is transferred, then the low byte. */ - if (m_raddr_LSB) - { - /* second pass */ - *value = m_address & 0x00ff; - m_raddr_LSB = false; - } - else - { - /* first pass */ - *value = (m_address & 0xff00)>>8; - m_raddr_LSB = true; - } - } - else - { - if (((m_address >> 13)&0x07)==m_ident) - { - // GROMs are buffered. Data is retrieved from a buffer, - // while the buffer is replaced with the next cell content. - if (TRACE_ADDRESS) if (m_ident==0) logerror("grom0: %04x = %02x\n", m_address-1, m_buffer); - *value = m_buffer; - // Get next value, put it in buffer. Note that the GROM - // wraps at 8K boundaries. - UINT16 addr = m_address-(m_ident<<13); - - if (m_size == 0x1800 && ((m_address&0x1fff)>=0x1800)) - m_buffer = m_memptr[addr-0x1000] | m_memptr[addr-0x0800]; - else - m_buffer = m_memptr[addr]; - } - // Note that all GROMs update their address counter. - // TODO: Check this on a real console - m_address = (m_address & 0xE000) | ((m_address + 1)&0x1FFF); - - // Reset the read and write address flipflops. - m_raddr_LSB = m_waddr_LSB = false; - - // Maybe the timer is also required for address reading/setting, but - // we don't have such technical details on GROMs. - clear_ready(); - } -} - -/* - Writing to the chip. Represents an access with M=0, GS*=0. The MO bit is - defined by the offset (0 or 1). -*/ -WRITE8_MEMBER( ti99_grom_device::write ) -{ - // Prevent debugger access - if (space.debugger_access()) return; - - if (offset & 2) - { - /* write GROM address */ - /* see comments above */ - m_raddr_LSB = false; - - /* Implements the internal flipflop. */ - /* The Editor/Assembler manuals says that the current address */ - /* plus one is returned. This effect is properly emulated */ - /* by using a read-ahead buffer. */ - if (m_waddr_LSB) - { - /* Accept low byte (2nd write) */ - m_address = (m_address & 0xFF00) | data; - /* Setting the address causes a new prefetch */ - if (is_selected()) - { - m_buffer = m_memptr[m_address-(m_ident<<13)]; - } - m_waddr_LSB = false; - if (TRACE_ADDRESS) if (m_ident==0) logerror("grom0: %04x\n", m_address); - } - else - { - /* Accept high byte (1st write). Do not advance the address conter. */ - m_address = (data << 8) | (m_address & 0xFF); - m_waddr_LSB = true; - return; - } - } - else - { - /* write GRAM data */ - if ((((m_address >> 13)&0x07)==m_ident) && m_writable) - { - UINT16 write_addr; - // We need to rewind by 1 because the read address has already advanced. - // However, do not change the address counter! - write_addr = (m_address & 0xE000) | ((m_address - 1)&0x1FFF); - - // UINT16 addr = m_address-(m_ident<<13); - if (m_size > 0x1800 || ((m_address&0x1fff)<0x1800)) - m_memptr[write_addr-(m_ident<<13)] = data; - } - m_raddr_LSB = m_waddr_LSB = false; - clear_ready(); - } - m_address = (m_address & 0xE000) | ((m_address + 1)&0x1FFF); -} - -/* - Timing. We assume that each data read results in READY going down for - one cycle at the given frequency. -*/ -void ti99_grom_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - m_gromready(ASSERT_LINE); -} - -void ti99_grom_device::clear_ready() -{ - m_gromready(CLEAR_LINE); - m_timer->adjust(attotime::from_hz(m_clockrate)); -} - -/*************************************************************************** - DEVICE FUNCTIONS -***************************************************************************/ - -void ti99_grom_device::device_start(void) -{ - const ti99grom_config *conf = reinterpret_cast<const ti99grom_config *>(static_config()); - - m_memptr = owner()->memregion(conf->regionname)->base(); - assert (m_memptr!=nullptr); - m_memptr += conf->offset_reg; - - m_size = conf->size; - m_clockrate = conf->clockrate; - m_writable = conf->writable; - m_ident = conf->ident; - m_gromready.resolve_safe(); - - m_timer = timer_alloc(0); -} - -void ti99_grom_device::device_reset(void) -{ - m_address = 0; - m_raddr_LSB = false; - m_waddr_LSB = false; - m_buffer = 0; -} - -const device_type GROM = &device_creator<ti99_grom_device>; diff --git a/src/devices/bus/ti99x/grom.h b/src/devices/bus/ti99x/grom.h deleted file mode 100644 index a3129b6997f..00000000000 --- a/src/devices/bus/ti99x/grom.h +++ /dev/null @@ -1,108 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/*************************************************************************** - - GROM emulation - See grom.c for documentation, - - Michael Zapf - - February 2012: Rewritten as class - -***************************************************************************/ -#ifndef __TI99GROM__ -#define __TI99GROM__ - -#include "ti99defs.h" - -struct ti99grom_config -{ - bool writable; - int ident; - const char *regionname; - offs_t offset_reg; - int size; - int clockrate; -}; - -#define GROM_CONFIG(name) \ - const ti99grom_config(name) = - -#define MCFG_GROM_READY_CALLBACK(_write) \ - devcb = &ti99_grom_device::set_ready_wr_callback(*device, DEVCB_##_write); - -extern const device_type GROM; - -/* - ti99_grom. For bus8z_device see ti99defs.h -*/ -class ti99_grom_device : public bus8z_device, ti99grom_config -{ -public: - ti99_grom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - - template<class _Object> static devcb_base &set_ready_wr_callback(device_t &device, _Object object) { return downcast<ti99_grom_device &>(device).m_gromready.set_callback(object); } - - DECLARE_READ8Z_MEMBER(readz) override; - DECLARE_WRITE8_MEMBER(write) override; - -private: - // Is this a GRAM (never seen actually, but obviously planned) - bool m_writable; - - // Identification of this GROM (0-7) - int m_ident; - - // If the GROM has only 6 KiB, the remaining 2 KiB are filled with a - // specific byte pattern which is created by a logical OR of lower - // regions - int m_size; - - // Ready callback. This line is usually connected to the READY pin of the CPU. - devcb_write_line m_gromready; - - // Frequency of the incoming GROM clock. In most application cases the - // GROM gets its clock from the video display processor (TMS9918) - int m_clockrate; - - /* Address pointer. */ - // This value is always expected to be in the range 0x0000 - 0xffff, even - // when this GROM is not addressed. - UINT16 m_address; - - /* GROM data buffer. */ - UINT8 m_buffer; - - /* Internal flip-flop. Used when retrieving the address counter. */ - bool m_raddr_LSB; - - /* Internal flip-flops. Used when writing the address counter.*/ - bool m_waddr_LSB; - - /* Pointer to the memory region contained in this GROM. */ - UINT8 *m_memptr; - - // Timer for READY line operation - emu_timer *m_timer; - - /* Indicates whether this device will react on the next read/write data access. */ - inline int is_selected() - { - return (((m_address >> 13)&0x07)==m_ident); - } - - // Calling this method causes the READY line to be cleared, which puts the - // CPU into wait state mode. A timer is set to raise READY again. - void clear_ready(); - - virtual void device_start(void) override; - virtual void device_reset(void) override; - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; -}; - - -#define MCFG_GROM_ADD(_tag, _config) \ - MCFG_DEVICE_ADD(_tag, GROM, 0) \ - MCFG_DEVICE_CONFIG(_config) - -#endif diff --git a/src/devices/bus/ti99x/gromport.cpp b/src/devices/bus/ti99x/gromport.cpp index 33ef82ae2a7..9a836f970d1 100644 --- a/src/devices/bus/ti99x/gromport.cpp +++ b/src/devices/bus/ti99x/gromport.cpp @@ -40,7 +40,7 @@ This means that a maximum of 8 KiB of direct memory space can be accessed. The /GS line is used to enable GROM circuits on the board (serial ROMs with - own address counter, see grom.h). + own address counter, see tmc0430.h). When a cartridge is inserted the /RESET line is pulled to ground, which via a R/C component pulls down the /RESET input of the timer circuit for @@ -119,6 +119,7 @@ #define TRACE_ILLWRITE 0 #define TRACE_CONFIG 0 #define TRACE_READ 0 +#define TRACE_WRITE 0 #define TRACE_GROM 0 #define TRACE_GKRACKER 0 #define TRACE_CRU 0 @@ -186,20 +187,33 @@ gromport_device::gromport_device(const machine_config &mconfig, const char *tag, m_connector(nullptr), m_reset_on_insert(true), m_console_ready(*this), - m_console_reset(*this), m_grombase(0), m_grommask(0) + m_console_reset(*this) // , m_grombase(0), m_grommask(0) { } -/* Only called for addresses 6000-7fff and GROM addresses (see datamux config) */ +/* + Reading via the GROM port. Only 13 address lines are passed through + on the TI-99/4A, and 14 lines on the TI-99/8. +*/ READ8Z_MEMBER(gromport_device::readz) { if (m_connector != nullptr) - m_connector->readz(space, offset, value); + { + m_connector->readz(space, offset & m_mask, value); + if (TRACE_READ) if (m_romgq) logerror("Read %04x -> %02x\n", offset, *value); + } } +/* + Writing via the GROM port. Only 13 address lines are passed through + on the TI-99/4A, and 14 lines on the TI-99/8. +*/ WRITE8_MEMBER(gromport_device::write) { if (m_connector != nullptr) - m_connector->write(space, offset, data); + { + if (TRACE_WRITE) if (m_romgq) logerror("Write %04x <- %02x\n", offset, data); + m_connector->write(space, offset & m_mask, data); + } } READ8Z_MEMBER(gromport_device::crureadz) @@ -219,6 +233,31 @@ WRITE_LINE_MEMBER(gromport_device::ready_line) m_console_ready(state); } +/* + Asserted when the console addresses cartridge rom. +*/ +WRITE_LINE_MEMBER(gromport_device::romgq_line) +{ + m_romgq = state; + if (m_connector != nullptr) + m_connector->romgq_line(state); +} + +WRITE_LINE_MEMBER(gromport_device::gclock_in) +{ + if (m_connector != nullptr) + m_connector->gclock_in(state); +} + +/* + Combined GROM control lines. +*/ +WRITE8_MEMBER( gromport_device::set_gromlines ) +{ + if (m_connector != nullptr) + m_connector->set_gromlines(space, offset, data); +} + void gromport_device::device_start() { m_console_ready.resolve(); @@ -230,11 +269,11 @@ void gromport_device::device_reset() m_reset_on_insert = (ioport("CARTRESET")->read()==0x01); } -void gromport_device::set_grom_base(UINT16 grombase, UINT16 grommask) -{ - m_grombase = grombase; - m_grommask = grommask; -} +// void gromport_device::set_grom_base(UINT16 grombase, UINT16 grommask) +// { + // m_grombase = grombase; + // m_grommask = grommask; +// } /* Shall we reset the console when a cartridge has been inserted? @@ -255,15 +294,18 @@ void gromport_device::cartridge_inserted() void gromport_device::device_config_complete() { m_connector = static_cast<ti99_cartridge_connector_device*>(subdevices().first()); - set_grom_base(0x9800, 0xf800); } -SLOT_INTERFACE_START( gromport ) +SLOT_INTERFACE_START( gromport4 ) SLOT_INTERFACE("single", GROMPORT_SINGLE) SLOT_INTERFACE("multi", GROMPORT_MULTI) SLOT_INTERFACE("gkracker", GROMPORT_GK) SLOT_INTERFACE_END +SLOT_INTERFACE_START( gromport8 ) + SLOT_INTERFACE("single", GROMPORT_SINGLE) + SLOT_INTERFACE("multi", GROMPORT_MULTI) +SLOT_INTERFACE_END INPUT_PORTS_START(gromport) PORT_START( "CARTRESET" ) @@ -310,16 +352,6 @@ void ti99_cartridge_connector_device::device_config_complete() m_gromport = static_cast<gromport_device*>(owner()); } -UINT16 ti99_cartridge_connector_device::grom_base() -{ - return m_gromport->get_grom_base(); -} - -UINT16 ti99_cartridge_connector_device::grom_mask() -{ - return m_gromport->get_grom_mask(); -} - single_conn_device::single_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : ti99_cartridge_connector_device(mconfig, GROMPORT_SINGLE, "Standard cartridge connector", tag, owner, clock, "single", __FILE__), m_cartridge(nullptr) @@ -350,6 +382,28 @@ WRITE8_MEMBER(single_conn_device::cruwrite) m_cartridge->cruwrite(space, offset, data); } +WRITE_LINE_MEMBER(single_conn_device::romgq_line) +{ + // Pass through + m_cartridge->romgq_line(state); +} + +/* + Combined select lines +*/ +WRITE8_MEMBER(single_conn_device::set_gromlines) +{ + // Pass through + m_cartridge->set_gromlines(space, offset, data); +} + + +WRITE_LINE_MEMBER(single_conn_device::gclock_in) +{ + // Pass through + m_cartridge->gclock_in(state); +} + void single_conn_device::device_start() { m_cartridge = static_cast<ti99_cartridge_device*>(subdevices().first()); @@ -475,10 +529,8 @@ int multi_conn_device::get_active_slot(bool changebase, offs_t offset) int slot; if (changebase) { - if ((offset & grom_mask()) == grom_base()) - { - set_slot((offset>>2) & 0x00ff); - } + // GROM selected? + if (m_gsel != 0) set_slot((offset>>2) & 0x00ff); } slot = m_active_slot; return slot; @@ -497,6 +549,50 @@ void multi_conn_device::remove(int index) m_cartridge[index] = nullptr; } +WRITE_LINE_MEMBER(multi_conn_device::romgq_line) +{ + m_readrom = state; + + // Propagate to all slots + for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) + { + if (m_cartridge[i] != nullptr) + { + m_cartridge[i]->romgq_line(state); + } + } +} + +/* + Combined select lines +*/ +WRITE8_MEMBER(multi_conn_device::set_gromlines) +{ + // GROM selected? + m_gsel = data; + + // Propagate to all slots + for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) + { + if (m_cartridge[i] != nullptr) + { + m_cartridge[i]->set_gromlines(space, offset, data); + } + } +} + +WRITE_LINE_MEMBER(multi_conn_device::gclock_in) +{ + // Propagate to all slots + for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) + { + if (m_cartridge[i] != nullptr) + { + m_cartridge[i]->gclock_in(state); + } + } +} + READ8Z_MEMBER(multi_conn_device::readz) { int slot = get_active_slot(true, offset); @@ -504,14 +600,14 @@ READ8Z_MEMBER(multi_conn_device::readz) // If we have a GROM access, we need to send the read request to all // attached cartridges so the slot is irrelevant here. Each GROM // contains an internal address counter, and we must make sure they all stay in sync. - if ((offset & grom_mask()) == grom_base()) + if (m_gsel != 0) { for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) { if (m_cartridge[i] != nullptr) { UINT8 newval = *value; - m_cartridge[i]->readz(space, offset, &newval, mem_mask); + m_cartridge[i]->readz(space, offset, &newval, 0xff); if (i==slot) { *value = newval; @@ -523,33 +619,32 @@ READ8Z_MEMBER(multi_conn_device::readz) { if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr) { - m_cartridge[slot]->readz(space, offset, value, mem_mask); + m_cartridge[slot]->readz(space, offset, value, 0xff); } } } WRITE8_MEMBER(multi_conn_device::write) { - int slot = get_active_slot(true, offset); - // Same issue as above (read) // We don't have GRAM cartridges, anyway, so it's just used for setting the address. - if ((offset & grom_mask()) == grom_base()) + if (m_gsel != 0) { for (auto & elem : m_cartridge) { if (elem != nullptr) { - elem->write(space, offset, data, mem_mask); + elem->write(space, offset, data, 0xff); } } } else { + int slot = get_active_slot(true, offset); if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr) { - // logerror("%s: try it on slot %d\n", tag(), slot); - m_cartridge[slot]->write(space, offset, data, mem_mask); + // logerror("writing %04x (slot %d) <- %02x\n", offset, slot, data); + m_cartridge[slot]->write(space, offset, data, 0xff); } } } @@ -595,6 +690,7 @@ void multi_conn_device::device_reset(void) { m_active_slot = 0; m_fixed_slot = ioport("CARTSLOT")->read() - 1; + m_gsel = 0; } static MACHINE_CONFIG_FRAGMENT( multi_slot ) @@ -751,9 +847,40 @@ gkracker_device::gkracker_device(const machine_config &mconfig, const char *tag, { } +WRITE_LINE_MEMBER(gkracker_device::romgq_line) +{ + if (m_cartridge != nullptr) + { + // Propagate to the guest + m_cartridge->romgq_line(state); + } +} + +/* + Combined select lines +*/ +WRITE8_MEMBER(gkracker_device::set_gromlines) +{ + m_gsel = data; + if (m_cartridge != nullptr) + { + // Propagate to the guest + m_cartridge->set_gromlines(space, offset, data); + } +} + +WRITE_LINE_MEMBER(gkracker_device::gclock_in) +{ + if (m_cartridge != nullptr) + { + // Propagate to the guest + m_cartridge->gclock_in(state); + } +} + READ8Z_MEMBER(gkracker_device::readz) { - if ((offset & grom_mask()) == grom_base()) + if (m_gsel != 0) { // Reads from the GRAM space of the GRAM Kracker. @@ -863,7 +990,7 @@ WRITE8_MEMBER(gkracker_device::write) m_cartridge->write(space, offset, data, mem_mask); } - if ((offset & grom_mask()) == grom_base()) + if (m_gsel != 0) { // Write to the GRAM space of the GRAM Kracker. if ((offset & 0x0002)==0x0002) @@ -1056,6 +1183,7 @@ void gkracker_device::device_reset() m_grom_address = 0; // for the GROM emulation m_ram_page = 0; m_waddr_LSB = false; + m_gsel = 0; } static MACHINE_CONFIG_FRAGMENT( gkracker_slot ) @@ -1270,15 +1398,15 @@ bool ti99_cartridge_device::has_grom() return m_pcb->m_grom_size>0; } -UINT16 ti99_cartridge_device::grom_base() -{ - return m_connector->grom_base(); -} - -UINT16 ti99_cartridge_device::grom_mask() -{ - return m_connector->grom_mask(); -} +// UINT16 ti99_cartridge_device::grom_base() +// { + // return m_connector->grom_base(); +// } +// +// UINT16 ti99_cartridge_device::grom_mask() +// { + // return m_connector->grom_mask(); +// } bool ti99_cartridge_device::call_load() { @@ -1401,12 +1529,14 @@ bool ti99_cartridge_device::call_softlist_load(software_list_device &swlist, con READ8Z_MEMBER(ti99_cartridge_device::readz) { - if (m_pcb != nullptr) m_pcb->readz(space, offset, value); + if (m_pcb != nullptr) + m_pcb->readz(space, offset, value); } WRITE8_MEMBER(ti99_cartridge_device::write) { - if (m_pcb != nullptr) m_pcb->write(space, offset, data); + if (m_pcb != nullptr) + m_pcb->write(space, offset, data); } READ8Z_MEMBER(ti99_cartridge_device::crureadz) @@ -1424,48 +1554,44 @@ WRITE_LINE_MEMBER( ti99_cartridge_device::ready_line ) m_connector->ready_line(state); } -void ti99_cartridge_device::device_config_complete() +WRITE_LINE_MEMBER( ti99_cartridge_device::romgq_line ) { - update_names(); - m_softlist = false; - m_connector = static_cast<ti99_cartridge_connector_device*>(owner()); + if (m_pcb != nullptr) + { + m_pcb->romgq_line(state); + m_readrom = state; + } } -static GROM_CONFIG(grom3_config) -{ - false, 3, CARTGROM_TAG, 0x0000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(grom4_config) -{ - false, 4, CARTGROM_TAG, 0x2000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(grom5_config) +/* + Combined select lines +*/ +WRITE8_MEMBER(ti99_cartridge_device::set_gromlines) { - false, 5, CARTGROM_TAG, 0x4000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(grom6_config) + if (m_pcb != nullptr) m_pcb->set_gromlines(space, offset, data); +} + +WRITE_LINE_MEMBER(ti99_cartridge_device::gclock_in) { - false, 6, CARTGROM_TAG, 0x6000, 0x1800, GROMFREQ -}; -static GROM_CONFIG(grom7_config) + if (m_pcb != nullptr) m_pcb->gclock_in(state); +} + +void ti99_cartridge_device::device_config_complete() { - false, 7, CARTGROM_TAG, 0x8000, 0x1800, GROMFREQ -}; + update_names(); + m_softlist = false; + m_connector = static_cast<ti99_cartridge_connector_device*>(owner()); +} /* 5 GROMs that may be contained in a cartridge */ static MACHINE_CONFIG_FRAGMENT( ti99_cartridge ) - MCFG_GROM_ADD( GROM3_TAG, grom3_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM4_TAG, grom4_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM5_TAG, grom5_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM6_TAG, grom6_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line)) - MCFG_GROM_ADD( GROM7_TAG, grom7_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM3_TAG, 3, CARTGROM_TAG, 0x0000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM4_TAG, 4, CARTGROM_TAG, 0x2000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM5_TAG, 5, CARTGROM_TAG, 0x4000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM6_TAG, 6, CARTGROM_TAG, 0x6000, WRITELINE(ti99_cartridge_device, ready_line)) + MCFG_GROM_ADD( GROM7_TAG, 7, CARTGROM_TAG, 0x8000, WRITELINE(ti99_cartridge_device, ready_line)) MACHINE_CONFIG_END machine_config_constructor ti99_cartridge_device::device_mconfig_additions() const @@ -1499,19 +1625,31 @@ const device_type TI99CART = &device_creator<ti99_cartridge_device>; Unlike in the previous implementation we do not model it as a full device. ***************************************************************************/ -ti99_cartridge_pcb::ti99_cartridge_pcb(): m_cart(nullptr), m_grom_size(0), m_rom_size(0), m_ram_size(0), m_rom_ptr(nullptr), m_ram_ptr(nullptr), m_rom_page(0), m_grom_ptr(nullptr), m_grom_address(0), m_ram_page(0), m_tag(nullptr) -{ -} - -UINT16 ti99_cartridge_pcb::grom_base() +ti99_cartridge_pcb::ti99_cartridge_pcb() + : m_cart(nullptr), + m_grom_size(0), + m_rom_size(0), + m_ram_size(0), + m_rom_ptr(nullptr), + m_ram_ptr(nullptr), + m_access_cartspace(false), + m_rom_page(0), + m_grom_ptr(nullptr), + m_grom_address(0), + m_ram_page(0), + m_tag(nullptr) { - return m_cart->grom_base(); } -UINT16 ti99_cartridge_pcb::grom_mask() -{ - return m_cart->grom_mask(); -} +// UINT16 ti99_cartridge_pcb::grom_base() +// { + // return m_cart->grom_base(); +// } +// +// UINT16 ti99_cartridge_pcb::grom_mask() +// { + // return m_cart->grom_mask(); +// } void ti99_cartridge_pcb::set_cartridge(ti99_cartridge_device *cart) { @@ -1540,31 +1678,38 @@ WRITE8_MEMBER(ti99_cartridge_pcb::gromwrite) } } +/* + TI-99/4A cartridges can only occupy 8 KiB of CPU RAM space. For TI-99/8 + cartridges with up to 16 KiB we need a new PCB type. Unfortunately, such + cartridges were never developed. +*/ READ8Z_MEMBER(ti99_cartridge_pcb::readz) { - if ((offset & grom_mask())==grom_base()) - gromreadz(space, offset, value, mem_mask); - else + if (m_access_cartspace) { if (m_rom_ptr!=nullptr) { - // For TI-99/8 we should plan for 16K cartridges. However, none was ever produced. - // Well, forget about that. *value = m_rom_ptr[offset & 0x1fff]; - // logerror("%s: read cartridge rom space %04x = %02x\n", tag(), offset, *value); } } + else + { + // Will not return anything when not selected (preceding gsq=ASSERT) + gromreadz(space, offset, value, mem_mask); + } } WRITE8_MEMBER(ti99_cartridge_pcb::write) { - // logerror("%s: write standard\n", tag()); - if ((offset & grom_mask())==grom_base()) - gromwrite(space, offset, data, mem_mask); - else + if (m_access_cartspace) { if (TRACE_ILLWRITE) space.device().logerror("%s: Cannot write to ROM space at %04x\n", tag(), offset); } + else + { + // Will not change anything when not selected (preceding gsq=ASSERT) + gromwrite(space, offset, data, mem_mask); + } } READ8Z_MEMBER(ti99_cartridge_pcb::crureadz) @@ -1575,11 +1720,35 @@ WRITE8_MEMBER(ti99_cartridge_pcb::cruwrite) { } -inline void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev) +void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev) { - m_grom[number] = static_cast<ti99_grom_device*>(dev); + m_grom[number] = static_cast<tmc0430_device*>(dev); } + +WRITE_LINE_MEMBER( ti99_cartridge_pcb::romgq_line ) +{ + m_access_cartspace = (state==ASSERT_LINE); +} + +// Propagate to all GROMs + +/* + Combined select lines +*/ +WRITE8_MEMBER(ti99_cartridge_pcb::set_gromlines) +{ + for (auto& elem : m_grom) + if (elem != nullptr) elem->set_lines(space, offset, data); +} + +WRITE_LINE_MEMBER(ti99_cartridge_pcb::gclock_in) +{ + for (auto& elem : m_grom) + if (elem != nullptr) elem->gclock_in(state); +} + + /***************************************************************************** Cartridge type: Paged (Extended Basic) This cartridge consists of GROM memory and 2 pages of standard ROM. @@ -1591,23 +1760,28 @@ inline void ti99_cartridge_pcb::set_grom_pointer(int number, device_t *dev) READ8Z_MEMBER(ti99_paged_cartridge::readz) { - if ((offset & grom_mask())==grom_base()) - gromreadz(space, offset, value, mem_mask); - else + if (m_access_cartspace) { *value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page << 13)]; } + else + { + // Will not return anything when not selected (preceding gsq=ASSERT) + gromreadz(space, offset, value, mem_mask); + } } WRITE8_MEMBER(ti99_paged_cartridge::write) { - // logerror("%s: write standard\n", tag()); - if ((offset & grom_mask())==grom_base()) - gromwrite(space, offset, data, mem_mask); - - else { + if (m_access_cartspace) + { m_rom_page = (offset >> 1) & 1; } + else + { + // Will not change anything when not selected (preceding gsq=ASSERT) + gromwrite(space, offset, data, mem_mask); + } } /***************************************************************************** @@ -1620,10 +1794,7 @@ WRITE8_MEMBER(ti99_paged_cartridge::write) /* Read function for the minimem cartridge. */ READ8Z_MEMBER(ti99_minimem_cartridge::readz) { - if ((offset & grom_mask())==grom_base()) - gromreadz(space, offset, value, mem_mask); - - else + if (m_access_cartspace) { if ((offset & 0x1000)==0x0000) { @@ -1637,16 +1808,16 @@ READ8Z_MEMBER(ti99_minimem_cartridge::readz) *value = m_ram_ptr[offset & 0x0fff]; } } + else + { + gromreadz(space, offset, value, mem_mask); + } } /* Write function for the minimem cartridge. */ WRITE8_MEMBER(ti99_minimem_cartridge::write) { - // logerror("%s: write standard\n", tag()); - if ((offset & grom_mask())==grom_base()) - gromwrite(space, offset, data, mem_mask); - - else + if (m_access_cartspace) { if ((offset & 0x1000)==0x0000) { @@ -1657,6 +1828,10 @@ WRITE8_MEMBER(ti99_minimem_cartridge::write) m_ram_ptr[offset & 0x0fff] = data; } } + else + { + gromwrite(space, offset, data, mem_mask); + } } /***************************************************************************** @@ -1678,26 +1853,30 @@ WRITE8_MEMBER(ti99_minimem_cartridge::write) /* Read function for the super cartridge. */ READ8Z_MEMBER(ti99_super_cartridge::readz) { - if ((offset & grom_mask())==grom_base()) - gromreadz(space, offset, value, mem_mask); - else + if (m_access_cartspace) { if (m_ram_ptr != nullptr) { *value = m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)]; } } + else + { + gromreadz(space, offset, value, mem_mask); + } } /* Write function for the super cartridge. */ WRITE8_MEMBER(ti99_super_cartridge::write) { - if ((offset & grom_mask())==grom_base()) - gromwrite(space, offset, data, mem_mask); - else + if (m_access_cartspace) { m_ram_ptr[(m_ram_page << 13) | (offset & 0x1fff)] = data; } + else + { + gromwrite(space, offset, data, mem_mask); + } } READ8Z_MEMBER(ti99_super_cartridge::crureadz) @@ -1763,9 +1942,7 @@ WRITE8_MEMBER(ti99_super_cartridge::cruwrite) /* Read function for the mbx cartridge. */ READ8Z_MEMBER(ti99_mbx_cartridge::readz) { - if ((offset & grom_mask())==grom_base()) - gromreadz(space, offset, value, mem_mask); - else + if (m_access_cartspace) { if ((offset & 0x1c00)==0x0c00) { @@ -1781,14 +1958,16 @@ READ8Z_MEMBER(ti99_mbx_cartridge::readz) *value = m_rom_ptr[(offset & 0x1fff) | (m_rom_page<<13)]; } } + else + { + gromreadz(space, offset, value, mem_mask); + } } /* Write function for the mbx cartridge. */ WRITE8_MEMBER(ti99_mbx_cartridge::write) { - if ((offset & grom_mask())==grom_base()) - gromwrite(space, offset, data, mem_mask); - else + if (m_access_cartspace) { if (offset == 0x6ffe) { @@ -1802,6 +1981,10 @@ WRITE8_MEMBER(ti99_mbx_cartridge::write) m_ram_ptr[offset & 0x03ff] = data; } } + else + { + gromwrite(space, offset, data, mem_mask); + } } /***************************************************************************** @@ -2032,9 +2215,15 @@ WRITE8_MEMBER(ti99_pagedcru_cartridge::cruwrite) ******************************************************************************/ +WRITE_LINE_MEMBER(ti99_gromemu_cartridge::gsq_line) +{ + m_grom_space = (state==ASSERT_LINE); +} + + READ8Z_MEMBER(ti99_gromemu_cartridge::readz) { - if ((offset & grom_mask())==grom_base()) + if (m_grom_space) gromemureadz(space, offset, value, mem_mask); else { @@ -2055,8 +2244,7 @@ READ8Z_MEMBER(ti99_gromemu_cartridge::readz) WRITE8_MEMBER(ti99_gromemu_cartridge::write) { - // logerror("%s: write standard\n", tag()); - if ((offset & grom_mask())==grom_base()) + if (m_grom_space) gromemuwrite(space, offset, data, mem_mask); else { @@ -2179,7 +2367,7 @@ rpk::rpk(emu_options& options, const char* sysname) rpk::~rpk() { - //if (TRACE_RPK) logerror("gromport/RPK: Destroy RPK\n"); + if (TRACE_RPK) printf("gromport/RPK: Destroy RPK\n"); } /* @@ -2290,7 +2478,7 @@ rpk_socket* rpk_reader::load_rom_resource(util::archive_file &zip, xml_data_node file = xml_get_attribute_string(rom_resource_node, "file", nullptr); if (file == nullptr) throw rpk_exception(RPK_INVALID_LAYOUT, "<rom> must have a 'file' attribute"); - //if (TRACE_RPK) logerror("gromport/RPK: Loading ROM contents for socket '%s' from file %s\n", socketname, file); + if (TRACE_RPK) printf("gromport/RPK: Loading ROM contents for socket '%s' from file %s\n", socketname, file); // check for crc crcstr = xml_get_attribute_string(rom_resource_node, "crc", nullptr); @@ -2378,7 +2566,7 @@ rpk_socket* rpk_reader::load_ram_resource(emu_options &options, xml_data_node* r contents = global_alloc_array_clear<UINT8>(length); if (contents==nullptr) throw rpk_exception(RPK_OUT_OF_MEMORY); - //if (TRACE_RPK) logerror("gromport/RPK: Allocating RAM buffer (%d bytes) for socket '%s'\n", length, socketname); + if (TRACE_RPK) printf("gromport/RPK: Allocating RAM buffer (%d bytes) for socket '%s'\n", length, socketname); ram_pname = nullptr; @@ -2400,7 +2588,7 @@ rpk_socket* rpk_reader::load_ram_resource(emu_options &options, xml_data_node* r std::string ram_pathname = std::string(system_name).append(PATH_SEPARATOR).append(ram_filename); ram_pname = core_strdup(ram_pathname.c_str()); // load, and fill rest with 00 - //if (TRACE_RPK) logerror("gromport/RPK: Loading NVRAM contents from '%s'\n", ram_pname); + if (TRACE_RPK) printf("gromport/RPK: Loading NVRAM contents from '%s'\n", ram_pname); image_battery_load_by_name(options, ram_pname, contents, length, 0x00); } } @@ -2488,7 +2676,7 @@ rpk* rpk_reader::open(emu_options &options, const char *filename, const char *sy // We'll try to find the PCB type on the provided type list. pcb_type = xml_get_attribute_string(pcb_node, "type", nullptr); if (pcb_type==nullptr) throw rpk_exception(RPK_INVALID_LAYOUT, "<pcb> must have a 'type' attribute"); - //if (TRACE_RPK) logerror("gromport/RPK: Cartridge says it has PCB type '%s'\n", pcb_type); + if (TRACE_RPK) printf("gromport/RPK: Cartridge says it has PCB type '%s'\n", pcb_type); i=0; do diff --git a/src/devices/bus/ti99x/gromport.h b/src/devices/bus/ti99x/gromport.h index 36900c40045..8f49a7f98d4 100644 --- a/src/devices/bus/ti99x/gromport.h +++ b/src/devices/bus/ti99x/gromport.h @@ -13,7 +13,7 @@ #include "emu.h" #include "ti99defs.h" -#include "grom.h" +#include "machine/tmc0430.h" extern const device_type GROMPORT; @@ -29,13 +29,22 @@ public: DECLARE_WRITE8_MEMBER(cruwrite); DECLARE_WRITE_LINE_MEMBER(ready_line); + DECLARE_WRITE_LINE_MEMBER(romgq_line); + + // Combined GROM select lines + DECLARE_WRITE8_MEMBER(set_gromlines); + + DECLARE_WRITE_LINE_MEMBER(gclock_in); + + static void set_mask(device_t &device, int mask) { downcast<gromport_device &>(device).m_mask = mask; } + template<class _Object> static devcb_base &static_set_ready_callback(device_t &device, _Object object) { return downcast<gromport_device &>(device).m_console_ready.set_callback(object); } template<class _Object> static devcb_base &static_set_reset_callback(device_t &device, _Object object) { return downcast<gromport_device &>(device).m_console_reset.set_callback(object); } void cartridge_inserted(); - void set_grom_base(UINT16 grombase, UINT16 grommask); - UINT16 get_grom_base() { return m_grombase; } - UINT16 get_grom_mask() { return m_grommask; } + // void set_grom_base(UINT16 grombase, UINT16 grommask); + // UINT16 get_grom_base() { return m_grombase; } + // UINT16 get_grom_mask() { return m_grommask; } protected: virtual void device_start() override; @@ -48,15 +57,24 @@ private: bool m_reset_on_insert; devcb_write_line m_console_ready; devcb_write_line m_console_reset; - UINT16 m_grombase; - UINT16 m_grommask; + int m_mask; + int m_romgq; + // UINT16 m_grombase; + // UINT16 m_grommask; }; -SLOT_INTERFACE_EXTERN(gromport); +SLOT_INTERFACE_EXTERN(gromport4); +SLOT_INTERFACE_EXTERN(gromport8); -#define MCFG_TI99_GROMPORT_ADD( _tag ) \ +#define MCFG_GROMPORT4_ADD( _tag ) \ MCFG_DEVICE_ADD(_tag, GROMPORT, 0) \ - MCFG_DEVICE_SLOT_INTERFACE(gromport, "single", false) + gromport_device::set_mask(*device, 0x1fff); \ + MCFG_DEVICE_SLOT_INTERFACE(gromport4, "single", false) + +#define MCFG_GROMPORT8_ADD( _tag ) \ + MCFG_DEVICE_ADD(_tag, GROMPORT, 0) \ + gromport_device::set_mask(*device, 0x3fff); \ + MCFG_DEVICE_SLOT_INTERFACE(gromport8, "single", false) #define MCFG_GROMPORT_READY_HANDLER( _ready ) \ devcb = &gromport_device::static_set_ready_callback( *device, DEVCB_##_ready ); @@ -80,11 +98,16 @@ public: DECLARE_WRITE8_MEMBER(cruwrite); DECLARE_WRITE_LINE_MEMBER(ready_line); + DECLARE_WRITE_LINE_MEMBER(romgq_line); + DECLARE_WRITE8_MEMBER(set_gromlines); + + DECLARE_WRITE_LINE_MEMBER(gclock_in); + bool is_available() { return m_pcb != nullptr; } bool has_grom(); void set_slot(int i); - UINT16 grom_base(); - UINT16 grom_mask(); + // UINT16 grom_base(); + // UINT16 grom_mask(); protected: virtual void device_start() override { }; @@ -111,6 +134,7 @@ protected: const option_guide *create_option_guide() const override { return nullptr; } private: + bool m_readrom; bool m_softlist; int m_pcbtype; int m_slot; @@ -136,18 +160,24 @@ public: virtual DECLARE_READ8Z_MEMBER(crureadz) = 0; virtual DECLARE_WRITE8_MEMBER(cruwrite) = 0; + virtual DECLARE_WRITE_LINE_MEMBER(romgq_line) =0; + virtual DECLARE_WRITE8_MEMBER(set_gromlines) =0; + + virtual DECLARE_WRITE_LINE_MEMBER(gclock_in) =0; + DECLARE_WRITE_LINE_MEMBER(ready_line); virtual void insert(int index, ti99_cartridge_device* cart) { m_gromport->cartridge_inserted(); }; virtual void remove(int index) { }; - UINT16 grom_base(); - UINT16 grom_mask(); + // UINT16 grom_base(); + // UINT16 grom_mask(); protected: ti99_cartridge_connector_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); virtual void device_config_complete() override; gromport_device* m_gromport; + int m_gsel; }; /* @@ -162,6 +192,9 @@ public: DECLARE_WRITE8_MEMBER(write) override; DECLARE_READ8Z_MEMBER(crureadz) override; DECLARE_WRITE8_MEMBER(cruwrite) override; + DECLARE_WRITE_LINE_MEMBER(romgq_line) override; + DECLARE_WRITE8_MEMBER(set_gromlines) override; + DECLARE_WRITE_LINE_MEMBER(gclock_in) override; protected: virtual void device_start() override; @@ -190,6 +223,9 @@ public: DECLARE_WRITE8_MEMBER(write) override; DECLARE_READ8Z_MEMBER(crureadz) override; DECLARE_WRITE8_MEMBER(cruwrite) override; + DECLARE_WRITE_LINE_MEMBER(romgq_line) override; + DECLARE_WRITE8_MEMBER(set_gromlines) override; + DECLARE_WRITE_LINE_MEMBER(gclock_in) override; void insert(int index, ti99_cartridge_device* cart) override; void remove(int index) override; @@ -201,6 +237,7 @@ protected: virtual ioport_constructor device_input_ports() const override; private: + bool m_readrom; int m_active_slot; int m_fixed_slot; int m_next_free_slot; @@ -222,6 +259,9 @@ public: DECLARE_WRITE8_MEMBER(write) override; DECLARE_READ8Z_MEMBER(crureadz) override; DECLARE_WRITE8_MEMBER(cruwrite) override; + DECLARE_WRITE_LINE_MEMBER(romgq_line) override; + DECLARE_WRITE8_MEMBER(set_gromlines) override; + DECLARE_WRITE_LINE_MEMBER(gclock_in) override; void insert(int index, ti99_cartridge_device* cart) override; void remove(int index) override; @@ -275,23 +315,28 @@ protected: virtual DECLARE_READ8Z_MEMBER(crureadz); virtual DECLARE_WRITE8_MEMBER(cruwrite); + DECLARE_WRITE_LINE_MEMBER(romgq_line); + DECLARE_WRITE8_MEMBER(set_gromlines); + DECLARE_WRITE_LINE_MEMBER(gclock_in); + DECLARE_READ8Z_MEMBER(gromreadz); DECLARE_WRITE8_MEMBER(gromwrite); inline void set_grom_pointer(int number, device_t *dev); void set_cartridge(ti99_cartridge_device *cart); - UINT16 grom_base(); - UINT16 grom_mask(); + // UINT16 grom_base(); + // UINT16 grom_mask(); const char* tag() { return m_tag; } void set_tag(const char* tag) { m_tag = tag; } ti99_cartridge_device* m_cart; - ti99_grom_device* m_grom[5]; + tmc0430_device* m_grom[5]; int m_grom_size; int m_rom_size; int m_ram_size; UINT8* m_rom_ptr; UINT8* m_ram_ptr; + bool m_access_cartspace; int m_rom_page; // for some cartridge types UINT8* m_grom_ptr; // for gromemu int m_grom_address; // for gromemu @@ -398,15 +443,19 @@ public: class ti99_gromemu_cartridge : public ti99_cartridge_pcb { public: - ti99_gromemu_cartridge(): m_waddr_LSB(false) + ti99_gromemu_cartridge(): m_waddr_LSB(false), m_grom_space(false) { m_grom_address = 0; } ~ti99_gromemu_cartridge() { }; DECLARE_READ8Z_MEMBER(readz) override; DECLARE_WRITE8_MEMBER(write) override; DECLARE_READ8Z_MEMBER(gromemureadz); DECLARE_WRITE8_MEMBER(gromemuwrite); + + DECLARE_WRITE_LINE_MEMBER(gsq_line); + private: bool m_waddr_LSB; + bool m_grom_space; }; diff --git a/src/devices/bus/ti99x/ti99defs.h b/src/devices/bus/ti99x/ti99defs.h index 130c0cfbc40..8d137e6599f 100644 --- a/src/devices/bus/ti99x/ti99defs.h +++ b/src/devices/bus/ti99x/ti99defs.h @@ -32,6 +32,7 @@ #define JOYPORT_TAG "joyport" #define VDP_TAG "vdp" #define DSRROM "dsrrom" +#define CONSOLEROM "consolerom" #define VDPFREQ XTAL_10_738635MHz #define GROMFREQ VDPFREQ/24 @@ -41,10 +42,49 @@ #define DRAM_TAG "dram8" #define MAPPER_TAG "mapper" #define MAINBOARD8_TAG "mainboard8" -#define SPEECH_TAG "speech" -#define ROM0_TAG "rom0" -#define ROM1_TAG "rom1" -#define PCODEROM_TAG "pcode" +#define SPEECHSYN_TAG "speech" + +#define ROM0_REG "rom0_region" +#define ROM1_REG "rom1_region" +#define PASCAL_REG "pascal_region" +#define SYSGROM_REG "sysgrom_region" +#define GROMLIB1_REG "gromlib1_region" +#define GROMLIB2_REG "gromlib2_region" +#define GROMLIB3_REG "gromlib3_region" +#define SPEECHROM_REG "speech_region" + +#define GROMLIB_TAG "gromlib" +#define SYSGROM_TAG GROMLIB_TAG "0" +#define SYSGROM0_TAG SYSGROM_TAG "_0" +#define SYSGROM1_TAG SYSGROM_TAG "_1" +#define SYSGROM2_TAG SYSGROM_TAG "_2" + +#define GLIB1_TAG GROMLIB_TAG "1" +#define GLIB10_TAG GLIB1_TAG "_0" +#define GLIB11_TAG GLIB1_TAG "_1" +#define GLIB12_TAG GLIB1_TAG "_2" +#define GLIB13_TAG GLIB1_TAG "_3" +#define GLIB14_TAG GLIB1_TAG "_4" +#define GLIB15_TAG GLIB1_TAG "_5" +#define GLIB16_TAG GLIB1_TAG "_6" +#define GLIB17_TAG GLIB1_TAG "_7" + +#define GLIB2_TAG GROMLIB_TAG "2" +#define GLIB20_TAG GLIB2_TAG "_0" +#define GLIB21_TAG GLIB2_TAG "_1" +#define GLIB22_TAG GLIB2_TAG "_2" +#define GLIB23_TAG GLIB2_TAG "_3" +#define GLIB24_TAG GLIB2_TAG "_4" +#define GLIB25_TAG GLIB2_TAG "_5" +#define GLIB26_TAG GLIB2_TAG "_6" +#define GLIB27_TAG GLIB2_TAG "_7" + +#define GLIB3_TAG GROMLIB_TAG "3" +#define GLIB30_TAG GLIB3_TAG "_0" +#define GLIB31_TAG GLIB3_TAG "_1" +#define GLIB32_TAG GLIB3_TAG "_2" + + // Geneve #define GKEYBOARD_TAG "gkeyboard" diff --git a/src/devices/bus/ti99x/videowrp.cpp b/src/devices/bus/ti99x/videowrp.cpp index 6f09da722e7..387872bba5d 100644 --- a/src/devices/bus/ti99x/videowrp.cpp +++ b/src/devices/bus/ti99x/videowrp.cpp @@ -39,7 +39,9 @@ ti_std_video_device::ti_std_video_device(const machine_config &mconfig, const ch } ti_exp_video_device::ti_exp_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : ti_video_device(mconfig, V9938VIDEO, "TI99 EXP Video subsystem", tag, owner, clock, "v9938_video", __FILE__), m_v9938(nullptr) + : ti_video_device(mconfig, V9938VIDEO, "TI99 EXP Video subsystem", tag, owner, clock, "v9938_video", __FILE__), + m_v9938(nullptr), + m_out_gromclk_cb(*this) { } @@ -129,6 +131,14 @@ WRITE16_MEMBER( ti_exp_video_device::write16 ) } } +void ti_exp_video_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + // Pulse it + m_out_gromclk_cb(ASSERT_LINE); + m_out_gromclk_cb(CLEAR_LINE); +} + + /******************************************************************************/ /* @@ -194,10 +204,13 @@ void ti_video_device::device_start(void) void ti_exp_video_device::device_start(void) { m_v9938 = static_cast<v9938_device*>(machine().device(VDP_TAG)); + m_out_gromclk_cb.resolve(); + m_gromclk_timer = timer_alloc(0); } -void ti_video_device::device_reset(void) +void ti_exp_video_device::device_reset(void) { + if (!m_out_gromclk_cb.isnull()) m_gromclk_timer->adjust(attotime::zero, 0, attotime::from_hz(XTAL_10_738635MHz/24)); } /**************************************************************************/ diff --git a/src/devices/bus/ti99x/videowrp.h b/src/devices/bus/ti99x/videowrp.h index cb339e9dd60..c59069db13b 100644 --- a/src/devices/bus/ti99x/videowrp.h +++ b/src/devices/bus/ti99x/videowrp.h @@ -30,7 +30,7 @@ protected: /* Constructor */ ti_video_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); virtual void device_start(void) override; - virtual void device_reset(void) override; + virtual void device_reset(void) override { }; virtual DECLARE_READ8Z_MEMBER(readz) override { }; virtual DECLARE_WRITE8_MEMBER(write) override { }; }; @@ -55,6 +55,8 @@ class ti_exp_video_device : public ti_video_device { public: ti_exp_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + template<class _Object> static devcb_base &set_out_gromclk_callback(device_t &device, _Object object) { return downcast<ti_exp_video_device &>(device).m_out_gromclk_cb.set_callback(object); } + void video_update_mouse(int delta_x, int delta_y, int buttons); DECLARE_READ8Z_MEMBER(readz) override; DECLARE_WRITE8_MEMBER(write) override; @@ -63,10 +65,20 @@ public: void reset_vdp(int state) override { m_v9938->reset_line(state); } protected: - virtual void device_start(void) override; - v9938_device *m_v9938; + virtual void device_start(void) override; + virtual void device_reset(void) override; + +private: + void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + v9938_device *m_v9938; + devcb_write_line m_out_gromclk_cb; // GROMCLK line is optional; if present, pulse it by XTAL/24 rate + emu_timer *m_gromclk_timer; }; +#define MCFG_ADD_GROMCLK_CB(_devcb) \ + devcb = &ti_exp_video_device::set_out_gromclk_callback(*device, DEVCB_##_devcb); + + extern const device_type TI99VIDEO; extern const device_type V9938VIDEO; @@ -131,35 +143,39 @@ protected: /****************************************************************************/ -#define MCFG_TI_TMS991x_ADD_NTSC(_tag, _chip, _vsize, _class, _int) \ +#define MCFG_TI_TMS991x_ADD_NTSC(_tag, _chip, _vsize, _class, _int, _gclk) \ MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \ MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \ MCFG_TMS9928A_VRAM_SIZE(_vsize) \ MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \ + MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \ MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) \ MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update ) -#define MCFG_TI_TMS991x_ADD_PAL(_tag, _chip, _vsize, _class, _int) \ +#define MCFG_TI_TMS991x_ADD_PAL(_tag, _chip, _vsize, _class, _int, _gclk) \ MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \ MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \ MCFG_TMS9928A_VRAM_SIZE(_vsize) \ MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \ + MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \ MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) \ MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update ) -#define MCFG_TI998_ADD_NTSC(_tag, _chip, _vsize, _class, _int) \ +#define MCFG_TI998_ADD_NTSC(_tag, _chip, _vsize, _class, _int, _gclk) \ MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \ MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \ MCFG_TMS9928A_VRAM_SIZE(_vsize) \ MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \ + MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \ MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) \ MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update ) -#define MCFG_TI998_ADD_PAL(_tag, _chip, _vsize, _class, _int) \ +#define MCFG_TI998_ADD_PAL(_tag, _chip, _vsize, _class, _int, _gclk) \ MCFG_DEVICE_ADD(_tag, TI99VIDEO, 0) \ MCFG_DEVICE_ADD( VDP_TAG, _chip, XTAL_10_738635MHz / 2 ) \ MCFG_TMS9928A_VRAM_SIZE(_vsize) \ MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(_class,_int)) \ + MCFG_TMS9928A_OUT_GROMCLK_CB(WRITELINE(_class,_gclk)) \ MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) \ MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update ) diff --git a/src/mame/drivers/ti99_4p.cpp b/src/mame/drivers/ti99_4p.cpp index 0f48e57772b..7da4302262b 100644 --- a/src/mame/drivers/ti99_4p.cpp +++ b/src/mame/drivers/ti99_4p.cpp @@ -439,12 +439,16 @@ READ16_MEMBER( ti99_4p_state::datamux_read ) UINT8 hbyte = 0; UINT16 addroff = (offset << 1); + m_peribox->memen_in(ASSERT_LINE); + m_peribox->readz(space, addroff+1, &m_latch, mem_mask); m_lowbyte = m_latch; m_peribox->readz(space, addroff, &hbyte, mem_mask); m_highbyte = hbyte; + m_peribox->memen_in(CLEAR_LINE); + // use the latch and the currently read byte and put it on the 16bit bus // printf("read address = %04x, value = %04x, memmask = %4x\n", addroff, (hbyte<<8) | sgcpu->latch, mem_mask); @@ -467,11 +471,15 @@ WRITE16_MEMBER( ti99_4p_state::datamux_write ) // read more about the datamux in datamux.c // Write to the PEB + m_peribox->memen_in(ASSERT_LINE); + m_peribox->write(space, addroff+1, data & 0xff); // Write to the PEB m_peribox->write(space, addroff, (data>>8) & 0xff); + m_peribox->memen_in(CLEAR_LINE); + // Insert four wait states and let CPU enter wait state m_waitcount = 6; console_ready_dmux(CLEAR_LINE); diff --git a/src/mame/drivers/ti99_4x.cpp b/src/mame/drivers/ti99_4x.cpp index b8fcb75b3cb..107f3a466e8 100644 --- a/src/mame/drivers/ti99_4x.cpp +++ b/src/mame/drivers/ti99_4x.cpp @@ -48,7 +48,6 @@ #include "bus/ti99x/videowrp.h" #include "bus/ti99x/datamux.h" -#include "bus/ti99x/grom.h" #include "bus/ti99x/gromport.h" #include "bus/ti99x/joyport.h" @@ -103,6 +102,9 @@ public: DECLARE_WRITE_LINE_MEMBER( console_reset ); DECLARE_WRITE_LINE_MEMBER( notconnected ); + // GROM clock + DECLARE_WRITE_LINE_MEMBER( gromclk_in ); + // Connections with the system interface chip 9901 DECLARE_WRITE_LINE_MEMBER( extint ); DECLARE_WRITE_LINE_MEMBER( video_interrupt_in ); @@ -177,16 +179,10 @@ enum }; /* - Memory map. - Most of the work is done in the datamux (see datamux.c). We only keep ROM - and the small 256 byte PAD RAM here because they are directly connected - to the 16bit bus, and the wait state logic is not active during their - accesses. + Memory map. All of the work is done in the datamux (see datamux.c). */ static ADDRESS_MAP_START(memmap, AS_PROGRAM, 16, ti99_4x_state) ADDRESS_MAP_GLOBAL_MASK(0xffff) - AM_RANGE(0x0000, 0x1fff) AM_ROM - AM_RANGE(0x8000, 0x80ff) AM_MIRROR(0x0300) AM_RAM AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(DATAMUX_TAG, ti99_datamux_device, read, write) AM_DEVSETOFFSET(DATAMUX_TAG, ti99_datamux_device, setoffset) ADDRESS_MAP_END @@ -365,21 +361,6 @@ INPUT_PORTS_END Components ******************************************************************************/ -static GROM_CONFIG(grom0_config) -{ - false, 0, region_grom, 0x0000, 0x1800, GROMFREQ -}; - -static GROM_CONFIG(grom1_config) -{ - false, 1, region_grom, 0x2000, 0x1800, GROMFREQ -}; - -static GROM_CONFIG(grom2_config) -{ - false, 2, region_grom, 0x4000, 0x1800, GROMFREQ -}; - READ8_MEMBER( ti99_4x_state::cruread ) { // if (TRACE_CRU) logerror("read access to CRU address %04x\n", offset << 4); @@ -631,6 +612,7 @@ READ8_MEMBER( ti99_4x_state::interrupt_level ) WRITE_LINE_MEMBER( ti99_4x_state::clock_out ) { m_datamux->clock_in(state); + m_peribox->clock_in(state); } /* @@ -641,6 +623,15 @@ WRITE_LINE_MEMBER( ti99_4x_state::dbin_line ) m_datamux->dbin_in(state); } +/* + GROMCLK from VDP, propagating to datamux +*/ +WRITE_LINE_MEMBER( ti99_4x_state::gromclk_in ) +{ + m_datamux->gromclk_in(state); +} + + /*****************************************************************************/ /* @@ -708,6 +699,7 @@ void ti99_4x_state::console_ready_join(int id, int state) */ WRITE_LINE_MEMBER( ti99_4x_state::console_ready_grom ) { + if (TRACE_READY) logerror("GROM ready = %d\n", state); console_ready_join(READY_GROM, state); } @@ -757,52 +749,6 @@ WRITE_LINE_MEMBER( ti99_4x_state::notconnected ) if (TRACE_INTERRUPTS) logerror("ti99_4x: Setting a not connected line ... ignored\n"); } -/*****************************************************************************/ - -/* - Devices attached to the databus multiplexer. We cannot solve this with - the common address maps since the multiplexer also inserts wait states - that we want to emulate properly. Also, devices may reside on the same - memory locations (like GROMs) and select themselves according to some - inner state (e.g. GROMs have an own address counter and a given address - area). -*/ -static const dmux_device_list_entry dmux_devices[] = -{ - { VIDEO_SYSTEM_TAG, 0x8800, 0xfc01, 0x0400, nullptr, 0, 0 }, - { GROM0_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 }, - { GROM1_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 }, - { GROM2_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 }, - { TISOUND_TAG, 0x8400, 0xfc01, 0x0000, nullptr, 0, 0 }, - { GROMPORT_TAG, 0x9800, 0xfc01, 0x0400, nullptr, 0, 0 }, - { GROMPORT_TAG, 0x6000, 0xe000, 0x0000, nullptr, 0, 0 }, - { PERIBOX_TAG, 0x0000, 0x0000, 0x0000, nullptr, 0, 0 }, // Peribox needs all addresses - { nullptr, 0, 0, 0, nullptr, 0, 0 } -}; - -static const dmux_device_list_entry dmux_devices_ev[] = -{ - { VIDEO_SYSTEM_TAG, 0x8800, 0xfc01, 0x0400, nullptr, 0, 0 }, - { GROM0_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 }, - { GROM1_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 }, - { GROM2_TAG, 0x9800, 0xfc01, 0x0400, "GROMENA", 0x01, 0x00 }, - { TISOUND_TAG, 0x8400, 0xfc01, 0x0000, nullptr, 0, 0 }, - { GROMPORT_TAG, 0x9800, 0xfc01, 0x0400, nullptr, 0, 0 }, - { GROMPORT_TAG, 0x6000, 0xe000, 0x0000, nullptr, 0, 0 }, - { PERIBOX_TAG, 0x0000, 0x0000, 0x0000, nullptr, 0, 0 }, // Peribox needs all addresses - { nullptr, 0, 0, 0, nullptr, 0, 0 } -}; - -static DMUX_CONFIG( datamux_conf ) -{ - dmux_devices -}; - -static DMUX_CONFIG( datamux_conf_ev ) -{ - dmux_devices_ev -}; - /****************************************************************************** Machine definitions ******************************************************************************/ @@ -852,9 +798,10 @@ static MACHINE_CONFIG_START( ti99_4, ti99_4x_state ) MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_4x_state, cassette_output) ) MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_4x_state, tms9901_interrupt) ) - MCFG_DMUX_ADD( DATAMUX_TAG, datamux_conf ) + MCFG_DEVICE_ADD( DATAMUX_TAG, DATAMUX, 0) MCFG_DMUX_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_dmux) ) - MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG ) + + MCFG_GROMPORT4_ADD( GROMPORT_TAG ) MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_cart) ) MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_4x_state, console_reset) ) @@ -879,13 +826,10 @@ static MACHINE_CONFIG_START( ti99_4, ti99_4x_state ) MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25) - /* GROM devices */ - MCFG_GROM_ADD( GROM0_TAG, grom0_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) - MCFG_GROM_ADD( GROM1_TAG, grom1_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) - MCFG_GROM_ADD( GROM2_TAG, grom2_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) + // GROM devices + MCFG_GROM_ADD( GROM0_TAG, 0, region_grom, 0x0000, WRITELINE(ti99_4x_state, console_ready_grom)) + MCFG_GROM_ADD( GROM1_TAG, 1, region_grom, 0x2000, WRITELINE(ti99_4x_state, console_ready_grom)) + MCFG_GROM_ADD( GROM2_TAG, 2, region_grom, 0x4000, WRITELINE(ti99_4x_state, console_ready_grom)) // Joystick port MCFG_TI_JOYPORT4_ADD( JOYPORT_TAG ) @@ -896,14 +840,14 @@ MACHINE_CONFIG_END US version: 60 Hz, NTSC */ static MACHINE_CONFIG_DERIVED( ti99_4_60hz, ti99_4 ) - MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918, 0x4000, ti99_4x_state, video_interrupt_in) + MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in) MACHINE_CONFIG_END /* European version: 50 Hz, PAL */ static MACHINE_CONFIG_DERIVED( ti99_4_50hz, ti99_4 ) - MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929, 0x4000, ti99_4x_state, video_interrupt_in) + MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in) MACHINE_CONFIG_END /********************************************************************** @@ -951,9 +895,10 @@ static MACHINE_CONFIG_START( ti99_4a, ti99_4x_state ) MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_4x_state, cassette_output) ) MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_4x_state, tms9901_interrupt) ) - MCFG_DMUX_ADD( DATAMUX_TAG, datamux_conf ) + MCFG_DEVICE_ADD( DATAMUX_TAG, DATAMUX, 0) MCFG_DMUX_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_dmux) ) - MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG ) + + MCFG_GROMPORT4_ADD( GROMPORT_TAG ) MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_cart) ) MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_4x_state, console_reset) ) @@ -978,13 +923,10 @@ static MACHINE_CONFIG_START( ti99_4a, ti99_4x_state ) MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25) - /* GROM devices */ - MCFG_GROM_ADD( GROM0_TAG, grom0_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) - MCFG_GROM_ADD( GROM1_TAG, grom1_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) - MCFG_GROM_ADD( GROM2_TAG, grom2_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) + // GROM devices + MCFG_GROM_ADD( GROM0_TAG, 0, region_grom, 0x0000, WRITELINE(ti99_4x_state, console_ready_grom)) + MCFG_GROM_ADD( GROM1_TAG, 1, region_grom, 0x2000, WRITELINE(ti99_4x_state, console_ready_grom)) + MCFG_GROM_ADD( GROM2_TAG, 2, region_grom, 0x4000, WRITELINE(ti99_4x_state, console_ready_grom)) // Joystick port MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG ) @@ -994,14 +936,14 @@ MACHINE_CONFIG_END US version: 60 Hz, NTSC */ static MACHINE_CONFIG_DERIVED( ti99_4a_60hz, ti99_4a ) - MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in) + MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in) MACHINE_CONFIG_END /* European version: 50 Hz, PAL */ static MACHINE_CONFIG_DERIVED( ti99_4a_50hz, ti99_4a ) - MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in) + MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in) MACHINE_CONFIG_END /************************************************************************ @@ -1029,14 +971,14 @@ MACHINE_CONFIG_END US version: 60 Hz, NTSC */ static MACHINE_CONFIG_DERIVED( ti99_4qi_60hz, ti99_4qi ) - MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in) + MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in) MACHINE_CONFIG_END /* European version: 50 Hz, PAL */ static MACHINE_CONFIG_DERIVED( ti99_4qi_50hz, ti99_4qi ) - MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in) + MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, 0x4000, ti99_4x_state, video_interrupt_in, gromclk_in) MACHINE_CONFIG_END /************************************************************************ @@ -1056,6 +998,10 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state ) /* video hardware */ MCFG_DEVICE_ADD(VIDEO_SYSTEM_TAG, V9938VIDEO, 0) + // Removing the TMS9928a requires to add a replacement for the GROMCLK. + // In the real hardware this is a circuit (REPL99x) that fits into the VDP socket + MCFG_ADD_GROMCLK_CB( WRITELINE(ti99_4x_state, gromclk_in) ) + MCFG_V9938_ADD(VDP_TAG, SCREEN_TAG, 0x20000, XTAL_21_4772MHz) /* typical 9938 clock, not verified */ MCFG_V99X8_INTERRUPT_CALLBACK(WRITELINE(ti99_4x_state, video_interrupt_in)) MCFG_V99X8_SCREEN_ADD_NTSC(SCREEN_TAG, VDP_TAG, XTAL_21_4772MHz) @@ -1073,9 +1019,9 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state ) MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_4x_state, cassette_output) ) MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_4x_state, tms9901_interrupt) ) - MCFG_DMUX_ADD( DATAMUX_TAG, datamux_conf_ev ) + MCFG_DEVICE_ADD( DATAMUX_TAG, DATAMUX, 0) MCFG_DMUX_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_dmux) ) - MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG ) + MCFG_GROMPORT4_ADD( GROMPORT_TAG ) MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_4x_state, console_ready_cart) ) MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_4x_state, console_reset) ) @@ -1100,13 +1046,10 @@ static MACHINE_CONFIG_START( ti99_4ev_60hz, ti99_4x_state ) MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25) - /* GROM devices */ - MCFG_GROM_ADD( GROM0_TAG, grom0_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) - MCFG_GROM_ADD( GROM1_TAG, grom1_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) - MCFG_GROM_ADD( GROM2_TAG, grom2_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_4x_state, console_ready_grom)) + // GROM devices + MCFG_GROM_ADD( GROM0_TAG, 0, region_grom, 0x0000, WRITELINE(ti99_4x_state, console_ready_grom)) + MCFG_GROM_ADD( GROM1_TAG, 1, region_grom, 0x2000, WRITELINE(ti99_4x_state, console_ready_grom)) + MCFG_GROM_ADD( GROM2_TAG, 2, region_grom, 0x4000, WRITELINE(ti99_4x_state, console_ready_grom)) // Joystick port MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG ) @@ -1124,7 +1067,7 @@ MACHINE_CONFIG_END ROM_START(ti99_4) // CPU memory space - ROM_REGION16_BE(0x2000, "maincpu", 0) + ROM_REGION16_BE(0x2000, CONSOLEROM, 0) ROM_LOAD16_BYTE("u610.bin", 0x0000, 0x1000, CRC(6fcf4b15) SHA1(d085213c64701d429ae535f9a4ac8a50427a8343)) /* CPU ROMs high */ ROM_LOAD16_BYTE("u611.bin", 0x0001, 0x1000, CRC(491c21d1) SHA1(7741ae9294c51a44a78033d1b77c01568a6bbfb9)) /* CPU ROMs low */ @@ -1137,7 +1080,7 @@ ROM_END ROM_START(ti99_4a) // CPU memory space - ROM_REGION16_BE(0x2000, "maincpu", 0) + ROM_REGION16_BE(0x2000, CONSOLEROM, 0) ROM_LOAD16_WORD("994arom.bin", 0x0000, 0x2000, CRC(db8f33e5) SHA1(6541705116598ab462ea9403c00656d6353ceb85)) /* system ROMs */ // GROM memory space @@ -1147,7 +1090,7 @@ ROM_END ROM_START(ti99_4qi) // CPU memory space - ROM_REGION16_BE(0x2000, "maincpu", 0) + ROM_REGION16_BE(0x2000, CONSOLEROM, 0) ROM_LOAD16_WORD("994qirom.bin", 0x0000, 0x2000, CRC(db8f33e5) SHA1(6541705116598ab462ea9403c00656d6353ceb85)) /* system ROMs */ // GROM memory space @@ -1160,7 +1103,7 @@ ROM_END ROM_START(ti99_4ev) /*CPU memory space*/ - ROM_REGION16_BE(0x2000, "maincpu", 0) + ROM_REGION16_BE(0x2000, CONSOLEROM, 0) ROM_LOAD16_WORD("994arom.bin", 0x0000, 0x2000, CRC(db8f33e5) SHA1(6541705116598ab462ea9403c00656d6353ceb85)) /* system ROMs */ /*GROM memory space*/ diff --git a/src/mame/drivers/ti99_8.cpp b/src/mame/drivers/ti99_8.cpp index 5e3acc85ff6..9b661f71e82 100644 --- a/src/mame/drivers/ti99_8.cpp +++ b/src/mame/drivers/ti99_8.cpp @@ -86,33 +86,6 @@ - Native mode (Armadillo mode): Devices are located at positions above 0xF000 that allow for a contiguous usage of memory. - - ROM contents - ------------ - The ROM0 chip is accessible at addresses 0000-1FFF in the logical address - space of the compatibility mode. It contains the GPL interpreter. In - native mode the ROM0 chip is invisible. - - ROM0 - offset Logical address Name - ----------------------------------- - 0000 0000-1FFF ROM0 - - - The ROM1 chip contains 32 KiB of various system software. It is located in - the physical address space, so it must be mapped into the logical address - space by defining an appropriate map. - - ROM1 - offset Physical address Name - ---------------------------------------------------------- - 0000 FFA000-FFDFFF ROM1 - 4000 FF4000-FF5FFF @CRU>2700 Text-to-speech ROM/DSR - 6000 FF4000-FF5FFF @CRU>1700 Hexbus DSR - - The DSR portions have to be selected via the CRU bits >1700 or >2700. - - Mapper ------ The mapper uses 4K pages (unlike the Geneve mapper with 8K pages) which @@ -200,22 +173,27 @@ Known Issues (MZ, 2010-11-07) #include "emu.h" #include "cpu/tms9900/tms9995.h" +#include "bus/ti99x/ti99defs.h" + #include "sound/sn76496.h" #include "sound/wave.h" #include "machine/tms9901.h" +#include "machine/tmc0430.h" #include "imagedev/cassette.h" #include "bus/ti99x/998board.h" #include "bus/ti99x/videowrp.h" -#include "bus/ti99x/grom.h" #include "bus/ti99x/gromport.h" #include "bus/ti99x/joyport.h" #include "bus/ti99_peb/peribox.h" +#include "softlist.h" + // Debugging #define TRACE_READY 0 #define TRACE_INTERRUPTS 0 +#define TRACE_RESET 0 #define TRACE_CRU 0 /* @@ -228,7 +206,8 @@ enum READY_PBOX = 4, READY_SOUND = 8, READY_CART = 16, - READY_SPEECH = 32 + READY_SPEECH = 32, + READY_MAINBOARD = 64 }; class ti99_8_state : public driver_device @@ -242,8 +221,7 @@ public: m_peribox(*this, PERIBOX_TAG), m_mainboard(*this, MAINBOARD8_TAG), m_joyport(*this, JOYPORT_TAG), - m_video(*this, VIDEO_SYSTEM_TAG), - m_cassette(*this, "cassette") { } + m_cassette(*this, "cassette") { }; // Machine management DECLARE_MACHINE_START(ti99_8); @@ -254,17 +232,17 @@ public: DECLARE_WRITE8_MEMBER( cruwrite ); DECLARE_WRITE8_MEMBER( external_operation ); DECLARE_WRITE_LINE_MEMBER( clock_out ); + DECLARE_WRITE_LINE_MEMBER( dbin_line ); // Connections from outside towards the CPU (callbacks) - DECLARE_WRITE_LINE_MEMBER( console_ready_mapper ); - DECLARE_WRITE_LINE_MEMBER( console_ready_sound ); - DECLARE_WRITE_LINE_MEMBER( console_ready_pbox ); - DECLARE_WRITE_LINE_MEMBER( console_ready_cart ); - DECLARE_WRITE_LINE_MEMBER( console_ready_grom ); - DECLARE_WRITE_LINE_MEMBER( console_ready_speech ); + DECLARE_WRITE_LINE_MEMBER( console_ready ); DECLARE_WRITE_LINE_MEMBER( console_reset ); + DECLARE_WRITE_LINE_MEMBER( cpu_hold ); DECLARE_WRITE_LINE_MEMBER( notconnected ); + // GROM clock (coming from Vaquerro) + DECLARE_WRITE_LINE_MEMBER( gromclk_in ); + // Connections with the system interface chip 9901 DECLARE_WRITE_LINE_MEMBER( extint ); DECLARE_WRITE_LINE_MEMBER( video_interrupt ); @@ -275,8 +253,6 @@ public: DECLARE_WRITE_LINE_MEMBER(keyC1); DECLARE_WRITE_LINE_MEMBER(keyC2); DECLARE_WRITE_LINE_MEMBER(keyC3); - DECLARE_WRITE_LINE_MEMBER(CRUS); - DECLARE_WRITE_LINE_MEMBER(PTGEN); DECLARE_WRITE_LINE_MEMBER(audio_gate); DECLARE_WRITE_LINE_MEMBER(cassette_output); DECLARE_WRITE_LINE_MEMBER(cassette_motor); @@ -288,9 +264,7 @@ private: int m_keyboard_column; // READY handling - int m_nready_combined; - int m_nready_prev; - void console_ready_join(int id, int state); + line_state m_ready_old; // Latch for 9901 INT2, INT1 lines line_state m_int1; @@ -303,7 +277,6 @@ private: required_device<peribox_device> m_peribox; required_device<mainboard8_device> m_mainboard; required_device<joyport_device> m_joyport; - required_device<ti_video_device> m_video; required_device<cassette_image_device> m_cassette; }; @@ -312,7 +285,7 @@ private: job to the mapper completely. */ static ADDRESS_MAP_START(memmap, AS_PROGRAM, 8, ti99_8_state) - AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(MAINBOARD8_TAG, mainboard8_device, readm, writem ) + AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(MAINBOARD8_TAG, mainboard8_device, read, write) AM_DEVSETOFFSET(MAINBOARD8_TAG, mainboard8_device, setoffset) ADDRESS_MAP_END /* @@ -432,89 +405,6 @@ static INPUT_PORTS_START(ti99_8) INPUT_PORTS_END -/***************************************************************************** - Components -******************************************************************************/ -#define region_sysgrom "sysgrom" - -static GROM_CONFIG(grom0_config) -{ - false, 0, region_sysgrom, 0x0000, 0x1800, GROMFREQ -}; - -static GROM_CONFIG(grom1_config) -{ - false, 1, region_sysgrom, 0x2000, 0x1800, GROMFREQ -}; - -static GROM_CONFIG(grom2_config) -{ - false, 2, region_sysgrom, 0x4000, 0x1800, GROMFREQ -}; - -/**************************************************** - PASCAL groms, 3 libraries @ 8 GROMs - Do some macro tricks to keep writing effort low -*****************************************************/ - -#define region_gromlib1 "gromlib1" -#define region_gromlib2 "gromlib2" -#define region_gromlib3 "gromlib3" - -#define MCFG_GROM_LIBRARY_ADD8(_tag, _config) \ - MCFG_DEVICE_ADD(#_tag "0", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##0) \ - MCFG_DEVICE_ADD(#_tag "1", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##1) \ - MCFG_DEVICE_ADD(#_tag "2", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##2) \ - MCFG_DEVICE_ADD(#_tag "3", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##3) \ - MCFG_DEVICE_ADD(#_tag "4", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##4) \ - MCFG_DEVICE_ADD(#_tag "5", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##5) \ - MCFG_DEVICE_ADD(#_tag "6", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##6) \ - MCFG_DEVICE_ADD(#_tag "7", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##7) - -#define MCFG_GROM_LIBRARY_ADD3(_tag, _config) \ - MCFG_DEVICE_ADD(#_tag "0", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##0) \ - MCFG_DEVICE_ADD(#_tag "1", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##1) \ - MCFG_DEVICE_ADD(#_tag "2", GROM, 0) \ - MCFG_DEVICE_CONFIG(_config##2) - -#define GROM_LIBRARY_CONFIG8(_conf, _region) \ -static GROM_CONFIG(_conf##0) \ -{ false, 0, _region, 0x0000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##1) \ -{ false, 1, _region, 0x2000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##2) \ -{ false, 2, _region, 0x4000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##3) \ -{ false, 3, _region, 0x6000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##4) \ -{ false, 4, _region, 0x8000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##5) \ -{ false, 5, _region, 0xa000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##6) \ -{ false, 6, _region, 0xc000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##7) \ -{ false, 7, _region, 0xe000, 0x1800, GROMFREQ }; - -#define GROM_LIBRARY_CONFIG3(_conf, _region) \ -static GROM_CONFIG(_conf##0) \ -{ false, 0, _region, 0x0000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##1) \ -{ false, 1, _region, 0x2000, 0x1800, GROMFREQ }; \ -static GROM_CONFIG(_conf##2) \ -{ false, 2, _region, 0x4000, 0x1800, GROMFREQ }; -GROM_LIBRARY_CONFIG8(pascal1, region_gromlib1) -GROM_LIBRARY_CONFIG8(pascal2, region_gromlib2) -GROM_LIBRARY_CONFIG3(pascal3, region_gromlib3) READ8_MEMBER( ti99_8_state::cruread ) { @@ -661,33 +551,6 @@ WRITE_LINE_MEMBER( ti99_8_state::keyC3 ) } /* - Set 99/4A compatibility mode (CRUS=1) -*/ -WRITE_LINE_MEMBER( ti99_8_state::CRUS ) -{ - m_mainboard->CRUS_set(state==ASSERT_LINE); - - // In Armadillo mode, GROMs are located at f830; accordingly, the - // gromport must be reconfigured - if (state==ASSERT_LINE) - { - m_gromport->set_grom_base(0x9800, 0xfbf1); - } - else - { - m_gromport->set_grom_base(0xf830, 0xfff1); - } -} - -/* - Set mapper /PTGEN. This is negative logic; we use PTGE as the positive logic signal. -*/ -WRITE_LINE_MEMBER( ti99_8_state::PTGEN ) -{ - m_mainboard->PTGE_set(state==CLEAR_LINE); -} - -/* Control cassette tape unit motor (P6) */ WRITE_LINE_MEMBER( ti99_8_state::cassette_motor ) @@ -723,11 +586,11 @@ WRITE8_MEMBER( ti99_8_state::tms9901_interrupt ) /*****************************************************************************/ /* - set the state of TMS9901's INT2 (called by the tms9928 core) + set the state of TMS9901's INT2 (called by the VDP) */ WRITE_LINE_MEMBER( ti99_8_state::video_interrupt ) { - if (TRACE_INTERRUPTS) logerror("ti99_8: VDP int 2 on tms9901, level=%02x\n", state); + if (TRACE_INTERRUPTS) logerror("VDP int 2 on tms9901, level=%02x\n", state); m_int2 = (line_state)state; m_tms9901->set_single_int(2, state); } @@ -737,82 +600,59 @@ WRITE_LINE_MEMBER( ti99_8_state::video_interrupt ) ***********************************************************/ /* - We combine the incoming READY signals and propagate them to the CPU. - An alternative would be to let the CPU get the READY state, but this would - be a much higher overhead, as this happens in each clock tick. + Propagate READY signals to the CPU. */ -void ti99_8_state::console_ready_join(int id, int state) +void ti99_8_state::console_ready(int state) { - if (state==CLEAR_LINE) - m_nready_combined |= id; - else - m_nready_combined &= ~id; - if (TRACE_READY) { - if (m_nready_prev != m_nready_combined) logerror("ti99_8: READY bits = %04x\n", ~m_nready_combined); + if (m_ready_old != state) logerror("READY = %d\n", state); } - m_nready_prev = m_nready_combined; - m_cpu->ready_line(m_nready_combined==0); + m_ready_old = (line_state)state; + m_cpu->ready_line(state); } /* - Connections to the READY line. This might look a bit ugly; we need an - implementation of a "Wired AND" device. + Enqueue a RESET signal. */ -WRITE_LINE_MEMBER( ti99_8_state::console_ready_grom ) -{ - console_ready_join(READY_GROM, state); -} - -WRITE_LINE_MEMBER( ti99_8_state::console_ready_mapper ) -{ - console_ready_join(READY_MAPPER, state); -} - -WRITE_LINE_MEMBER( ti99_8_state::console_ready_pbox ) -{ - console_ready_join(READY_PBOX, state); -} - -WRITE_LINE_MEMBER( ti99_8_state::console_ready_sound ) +WRITE_LINE_MEMBER( ti99_8_state::console_reset ) { - console_ready_join(READY_SOUND, state); -} + if (TRACE_RESET) logerror("Incoming RESET line = %d\n", state); + if (machine().phase() != MACHINE_PHASE_INIT) + { + // RESET the 9901 + m_tms9901->rst1_line(state); -WRITE_LINE_MEMBER( ti99_8_state::console_ready_cart ) -{ - console_ready_join(READY_CART, state); -} + // Pull up the CRUS and PTGEN lines (9901 outputs have been deactivated, pull-up resistors on the board show effect) + m_mainboard->crus_in(TRUE); // assert + m_mainboard->ptgen_in(TRUE); // clear -WRITE_LINE_MEMBER( ti99_8_state::console_ready_speech ) -{ - console_ready_join(READY_SPEECH, state); + // Setting ready to false so that automatic wait states are enabled + m_cpu->ready_line(CLEAR_LINE); + m_cpu->reset_line(ASSERT_LINE); + } } /* - The RESET line leading to a reset of the CPU. + The HOLD line leading to the CPU entering the HOLD state. */ -WRITE_LINE_MEMBER( ti99_8_state::console_reset ) +WRITE_LINE_MEMBER( ti99_8_state::cpu_hold ) { - if (machine().phase() != MACHINE_PHASE_INIT) - { - m_cpu->set_input_line(INT_9995_RESET, state); - m_video->reset_vdp(state); - } + if (TRACE_INTERRUPTS) logerror("Incoming HOLD line = %d\n", state); + m_cpu->hold_line(state); } WRITE_LINE_MEMBER( ti99_8_state::extint ) { - if (TRACE_READY) logerror("ti99_8: EXTINT level = %02x\n", state); + if (TRACE_INTERRUPTS) logerror("EXTINT level = %02x\n", state); m_int1 = (line_state)state; m_tms9901->set_single_int(1, state); } WRITE_LINE_MEMBER( ti99_8_state::notconnected ) { - if (TRACE_READY) logerror("ti99_8: Setting a not connected line ... ignored\n"); + if (TRACE_INTERRUPTS) logerror("Setting a not connected line ... ignored\n"); } WRITE8_MEMBER( ti99_8_state::external_operation ) @@ -821,7 +661,7 @@ WRITE8_MEMBER( ti99_8_state::external_operation ) if (offset == IDLE_OP) return; else { - logerror("ti99_4x: External operation %s not implemented on TI-99/8 board\n", extop[offset]); + logerror("External operation %s not implemented on TI-99/8 board\n", extop[offset]); } } @@ -833,119 +673,23 @@ WRITE_LINE_MEMBER( ti99_8_state::clock_out ) m_mainboard->clock_in(state); } -/*****************************************************************************/ - /* - Format: - Name, mode, stop, mask, select, write, read8z function, write8 function - - Multiple devices may have the same select pattern; as in the real hardware, - care must be taken that only one device actually responds. In the case of - GROMs, each chip has an internal address counter and an ID, and the chip - only responds when the ID and the most significant 3 bits match. - - NATIVE <-> CRUS=0 - TI99EM <-> CRUS=1 - - PATGEN <-> PTGEN=1 - - CONT: Mapper continues iterating through devices - STOP: Mapper stops iterating when found - - Access to the mapper registers is done directly in the mapper, not via - this list. - - TODO: This should (must) be improved in terms of performance. Every single - memory access goes through the mapper. Either we use an ordered search list, - or we order the entries according to their frequency. - (I did this right now, putting the Pascal GROMs at the end.) - We should think about a set entry where devices with the same address - are collected as one single entry (think about the Pascal lib with 21 GROMs, - twice eight and once three of them on the same address). + Data bus in (DBIN) line from the CPU. */ - -#define PASCAL_GROM_LIB8(_tag, _addr) \ - { _tag "0", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "1", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "2", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "3", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "4", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "5", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "6", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "7", PATGEN, CONT, _addr, 0xfff1, 0x0000 } - -#define PASCAL_GROM_LIB3(_tag, _addr) \ - { _tag "0", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "1", PATGEN, CONT, _addr, 0xfff1, 0x0000 }, \ - { _tag "2", PATGEN, CONT, _addr, 0xfff1, 0x0000 } - - -static const mapper8_list_entry mapper_devices[] = -{ - // TI-99/4A mode (CRUS=1) - // Full/partial decoding has been verified on a real machine - // GROMs: According to the spec, the 99/8 supports up to 4 GROM libraries - // (99/4A supports 256 libraries) - // at 9800, 9804, 9808, 980c. Address counter access is at 9802,6,a,e. Write access +0400. - { ROM0NAME, TI99EM, STOP, 0x0000, 0xe000, 0x0000 }, // 0000-1fff - { TISOUND_TAG, TI99EM, STOP, 0x8400, 0xfff1, 0x0000 }, // 8400-840f - { VIDEO_SYSTEM_TAG, TI99EM, STOP, 0x8800, 0xfff1, 0x0400 }, // 8800,8802 / 8c00,8c02 - { SPEECH_TAG, TI99EM, STOP, 0x9000, 0xfff1, 0x0400 }, // 9000-900f / 9400-940f - { SRAMNAME, TI99EM, STOP, 0x8000, 0xf800, 0x0000 }, // 8000-87ff; must follow the sound generator - { MAINBOARD8_TAG, TI99EM, STOP, 0x8810, 0xfff0, 0x0000 }, - - { GROM0_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // 9800,2,4,...e/9c00,2,4,...e - { GROM1_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // dto. - { GROM2_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // dto. (GROMs are connected in parallel, - { GROMPORT_TAG, TI99EM, CONT, 0x9800, 0xfff1, 0x0400 }, // dto. use internal address counter and id) - - // TI-99/8 mode - // Full/partial decoding has been verified on a real machine - // Sound ports are at f800, f802, f804, ..., f80e - // VDP ports are (f810,f812), (f814,f816), (f818,f81a), (f81c,f81e) - // Note that unmapped GROM accesses (odd addresses like F831) return FF, - // not 00 as in our emulation, so that is not quite consistent, but tolerable ... I guess - - { SRAMNAME, NATIVE, STOP, 0xf000, 0xf800, 0x0000 }, // f000-f7ff - { TISOUND_TAG, NATIVE, STOP, 0xf800, 0xfff1, 0x0000 }, // f800-f80e (even addresses) - { VIDEO_SYSTEM_TAG, NATIVE, STOP, 0xf810, 0xfff1, 0x0000 }, // f810,2 (unlike 99/4A, no different read/write ports) - { SPEECH_TAG, NATIVE, STOP, 0xf820, 0xfff1, 0x0000 }, // f820-f82f - { MAINBOARD8_TAG, NATIVE, STOP, 0xf870, 0xfff0, 0x0000 }, - - { GROM0_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 }, // f830-f83e (4 banks), no different read/write ports - { GROM1_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 }, - { GROM2_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 }, - { GROMPORT_TAG, NATIVE, CONT, 0xf830, 0xfff1, 0x0000 }, - - PASCAL_GROM_LIB8("pascal1_grom", 0xf840), - PASCAL_GROM_LIB8("pascal2_grom", 0xf850), - PASCAL_GROM_LIB3("pascal3_grom", 0xf860), - - // Physical (need to pack this in here as well to keep config simple) - // but these lines will be put into a separate list - { DRAMNAME, PHYSIC, STOP, 0x000000, 0xff0000, 0x000000 }, // 000000-00ffff 64 KiB DRAM - { PCODENAME, PHYSIC, STOP, 0xf00000, 0xffc000, 0x000000 }, // f00000-f03fff P-Code ROM - { MAINBOARD8_TAG, PHYSIC, CONT, 0xff4000, 0xffe000, 0x000000 }, // ff4000-ff5fff Internal DSR - { GROMPORT_TAG, PHYSIC, STOP, 0xff6000, 0xffe000, 0x000000 }, // ff6000-ff7fff Cartridge ROM space - { GROMPORT_TAG, PHYSIC, STOP, 0xff8000, 0xffe000, 0x000000 }, // ff8000-ff9fff Cartridge ROM space - { ROM1A0NAME, PHYSIC, STOP, 0xffa000, 0xffe000, 0x000000 }, // ffa000-ffbfff ROM1 - { ROM1C0NAME, PHYSIC, STOP, 0xffc000, 0xffe000, 0x000000 }, // ffc000-ffdfff ROM1 - { INTSNAME, PHYSIC, STOP, 0xffe000, 0xfffff0, 0x000000 }, // ffe000-ffe00f Interrupt level sense - { PERIBOX_TAG, PHYSIC, STOP, 0x000000, 0x000000, 0x000000 }, // Peripheral Expansion Box - - { nullptr, 0, 0, 0, 0, 0 } -}; - -static MAPPER8_CONFIG( mapper_conf ) +WRITE_LINE_MEMBER( ti99_8_state::dbin_line ) { - mapper_devices -}; + m_mainboard->dbin_in(state); +} MACHINE_START_MEMBER(ti99_8_state,ti99_8) { - m_nready_combined = 0; m_peribox->senila(CLEAR_LINE); m_peribox->senilb(CLEAR_LINE); + // m_mainboard->set_gromport(m_gromport); + + // Need to configure the speech ROM for inverse bit order + speechrom_device* mem = subdevice<speechrom_device>(SPEECHROM_REG); + mem->set_reverse_bit_order(true); } MACHINE_RESET_MEMBER(ti99_8_state, ti99_8) @@ -956,9 +700,7 @@ MACHINE_RESET_MEMBER(ti99_8_state, ti99_8) // state on external memory accesses m_cpu->ready_line(CLEAR_LINE); - // But we assert the line here so that the system starts running - m_nready_combined = 0; - m_gromport->set_grom_base(0x9800, 0xfff1); + // m_gromport->set_grom_base(0x9800, 0xfff1); // Clear INT1 and INT2 latch m_int1 = CLEAR_LINE; @@ -972,62 +714,92 @@ static MACHINE_CONFIG_START( ti99_8, ti99_8_state ) MCFG_TMS99xx_ADD("maincpu", TMS9995_MP9537, XTAL_10_738635MHz, memmap, crumap) MCFG_TMS9995_EXTOP_HANDLER( WRITE8(ti99_8_state, external_operation) ) MCFG_TMS9995_CLKOUT_HANDLER( WRITELINE(ti99_8_state, clock_out) ) + MCFG_TMS9995_DBIN_HANDLER( WRITELINE(ti99_8_state, dbin_line) ) + MCFG_TMS9995_HOLDA_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, holda_line) ) MCFG_MACHINE_START_OVERRIDE(ti99_8_state, ti99_8 ) MCFG_MACHINE_RESET_OVERRIDE(ti99_8_state, ti99_8 ) - /* Main board */ + // 9901 configuration MCFG_DEVICE_ADD(TMS9901_TAG, TMS9901, XTAL_10_738635MHz/4.0) MCFG_TMS9901_READBLOCK_HANDLER( READ8(ti99_8_state, read_by_9901) ) MCFG_TMS9901_P0_HANDLER( WRITELINE( ti99_8_state, keyC0) ) MCFG_TMS9901_P1_HANDLER( WRITELINE( ti99_8_state, keyC1) ) MCFG_TMS9901_P2_HANDLER( WRITELINE( ti99_8_state, keyC2) ) MCFG_TMS9901_P3_HANDLER( WRITELINE( ti99_8_state, keyC3) ) - MCFG_TMS9901_P4_HANDLER( WRITELINE( ti99_8_state, CRUS) ) - MCFG_TMS9901_P5_HANDLER( WRITELINE( ti99_8_state, PTGEN) ) + MCFG_TMS9901_P4_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, mainboard8_device, crus_in) ) + MCFG_TMS9901_P5_HANDLER( DEVWRITELINE( MAINBOARD8_TAG, mainboard8_device, ptgen_in) ) MCFG_TMS9901_P6_HANDLER( WRITELINE( ti99_8_state, cassette_motor) ) MCFG_TMS9901_P8_HANDLER( WRITELINE( ti99_8_state, audio_gate) ) MCFG_TMS9901_P9_HANDLER( WRITELINE( ti99_8_state, cassette_output) ) MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( ti99_8_state, tms9901_interrupt) ) - MCFG_MAINBOARD8_ADD( MAINBOARD8_TAG, mapper_conf ) - MCFG_MAINBOARD8_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_mapper)) - MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG ) - MCFG_GROMPORT_READY_HANDLER( WRITELINE(ti99_8_state, console_ready_cart) ) + // Mainboard with custom chips + MCFG_DEVICE_ADD(MAINBOARD8_TAG, MAINBOARD8, 0) + MCFG_MAINBOARD8_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready)) + MCFG_MAINBOARD8_RESET_CALLBACK(WRITELINE(ti99_8_state, console_reset)) + MCFG_MAINBOARD8_HOLD_CALLBACK(WRITELINE(ti99_8_state, cpu_hold)) + + MCFG_GROMPORT8_ADD( GROMPORT_TAG ) + MCFG_GROMPORT_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, system_grom_ready) ) MCFG_GROMPORT_RESET_HANDLER( WRITELINE(ti99_8_state, console_reset) ) - /* Peripheral expansion box */ + /* Software list */ + MCFG_SOFTWARE_LIST_ADD("cart_list_ti99", "ti99_cart") + + // Peripheral expansion box MCFG_DEVICE_ADD( PERIBOX_TAG, PERIBOX_998, 0) MCFG_PERIBOX_INTA_HANDLER( WRITELINE(ti99_8_state, extint) ) MCFG_PERIBOX_INTB_HANDLER( WRITELINE(ti99_8_state, notconnected) ) - MCFG_PERIBOX_READY_HANDLER( WRITELINE(ti99_8_state, console_ready_pbox) ) - - /* Sound hardware */ - MCFG_TI_SOUND_76496_ADD( TISOUND_TAG ) - MCFG_TI_SOUND_READY_HANDLER( WRITELINE(ti99_8_state, console_ready_sound) ) - - /* Cassette drives */ + MCFG_PERIBOX_READY_HANDLER( DEVWRITELINE(MAINBOARD8_TAG, 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)) + + // Speech hardware + // Note: SPEECHROM uses its tag for referencing the region + MCFG_DEVICE_ADD(SPEECHROM_REG, SPEECHROM, 0) + MCFG_SPEAKER_STANDARD_MONO("speech_out") + MCFG_SOUND_ADD(SPEECHSYN_TAG, CD2501ECD, 640000L) + MCFG_TMS52XX_READYQ_HANDLER(DEVWRITELINE(MAINBOARD8_TAG, mainboard8_device, speech_ready)) + MCFG_TMS52XX_SPEECHROM(SPEECHROM_REG) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speech_out", 0.50) + + // Cassette drive MCFG_SPEAKER_STANDARD_MONO("cass_out") MCFG_CASSETTE_ADD( "cassette" ) MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25) - /* Console GROMs */ - MCFG_GROM_ADD( GROM0_TAG, grom0_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_grom)) - MCFG_GROM_ADD( GROM1_TAG, grom1_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_grom)) - MCFG_GROM_ADD( GROM2_TAG, grom2_config ) - MCFG_GROM_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_grom)) - - /* Pascal GROM libraries. */ - MCFG_GROM_LIBRARY_ADD8(pascal1_grom, pascal1) - MCFG_GROM_LIBRARY_ADD8(pascal2_grom, pascal2) - MCFG_GROM_LIBRARY_ADD3(pascal3_grom, pascal3) - - /* Devices */ - MCFG_DEVICE_ADD(SPEECH_TAG, SPEECH8, 0) - MCFG_SPEECH8_READY_CALLBACK(WRITELINE(ti99_8_state, console_ready_speech)) + // 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)) // Joystick port MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG ) @@ -1037,14 +809,24 @@ MACHINE_CONFIG_END TI-99/8 US version (NTSC, 60 Hz) */ static MACHINE_CONFIG_DERIVED( ti99_8_60hz, ti99_8 ) - MCFG_TI998_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9118, 0x4000, ti99_8_state, video_interrupt) + // Video hardware + MCFG_DEVICE_ADD( VDP_TAG, TMS9118, XTAL_10_738635MHz / 2 ) + MCFG_TMS9928A_VRAM_SIZE(0x4000) + MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_8_state, video_interrupt)) + MCFG_TMS9928A_SCREEN_ADD_NTSC( SCREEN_TAG ) + MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update ) MACHINE_CONFIG_END /* TI-99/8 European version (PAL, 50 Hz) */ static MACHINE_CONFIG_DERIVED( ti99_8_50hz, ti99_8 ) - MCFG_TI998_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9129, 0x4000, ti99_8_state, video_interrupt) + // Video hardware + MCFG_DEVICE_ADD( VDP_TAG, TMS9129, XTAL_10_738635MHz / 2 ) + MCFG_TMS9928A_VRAM_SIZE(0x4000) + MCFG_TMS9928A_OUT_INT_LINE_CB(WRITELINE(ti99_8_state,video_interrupt)) + MCFG_TMS9928A_SCREEN_ADD_PAL( SCREEN_TAG ) + MCFG_SCREEN_UPDATE_DEVICE( VDP_TAG, tms9928a_device, screen_update ) MACHINE_CONFIG_END /* @@ -1055,11 +837,11 @@ MACHINE_CONFIG_END */ ROM_START(ti99_8) // Logical (CPU) memory space: ROM0 - ROM_REGION(0x2000, ROM0_TAG, 0) + ROM_REGION(0x2000, ROM0_REG, 0) ROM_LOAD("u4_rom0.bin", 0x0000, 0x2000, CRC(901eb8d6) SHA1(13190c5e834baa9c0a70066b566cfcef438ed88a)) // Physical memory space (mapped): ROM1 - ROM_REGION(0x8000, ROM1_TAG, 0) + ROM_REGION(0x8000, ROM1_REG, 0) ROM_LOAD("u25_rom1.bin", 0x0000, 0x8000, CRC(b574461a) SHA1(42c6aed44802cfabdd26b565d6e5ddfcd689f11e)) // Physical memory space (mapped): P-Code ROM @@ -1069,20 +851,20 @@ ROM_START(ti99_8) // the required select line for this ROM on the available schematics, so // they seem to be from the earlier version. The location in the address // space was determined by ROM disassembly. - ROM_REGION(0x8000, PCODEROM_TAG, 0) + ROM_REGION(0x8000, PASCAL_REG, 0) ROM_LOAD("u25a_pas.bin", 0x0000, 0x4000, CRC(d7ed6dd6) SHA1(32212ce6426ceccbff73d342d4a3ef699c0ae1e4)) // System GROMs. 3 chips @ f830 // The schematics do not enumerate the circuits but only talk about // "circuits on board" (COB) so we name the GROMs as gM_N.bin where M is the // ID (0-7) and N is the access port in the logical address space. - ROM_REGION(0x6000, region_sysgrom, 0) + ROM_REGION(0x6000, SYSGROM_REG, 0) ROM_LOAD("g0_f830.bin", 0x0000, 0x1800, CRC(1026db60) SHA1(7327095bf4f390476e69d9fd8424e98ea1f2325a)) ROM_LOAD("g1_f830.bin", 0x2000, 0x1800, CRC(93a43d65) SHA1(19be8a07d674bc7554c2bc9c7a5725d81e888e6e)) ROM_LOAD("g2_f830.bin", 0x4000, 0x1800, CRC(06f2b901) SHA1(f65e0fcb2c63e230b4a9563c72f91259b94ce955)) // TTS & Pascal library. 8 chips @ f840 - ROM_REGION(0x10000, region_gromlib1, 0) + ROM_REGION(0x10000, GROMLIB1_REG, 0) ROM_LOAD("g0_f840.bin", 0x0000, 0x1800, CRC(44501071) SHA1(4b5ef7f1aa43a87e7ae4f02090944be5c39b1f26)) ROM_LOAD("g1_f840.bin", 0x2000, 0x1800, CRC(5a271d9e) SHA1(bb95befa2ffba2cc17ac437386e069e8ff621248)) ROM_LOAD("g2_f840.bin", 0x4000, 0x1800, CRC(d52502df) SHA1(17063e33ee8709d0df8030f38bb92c4322d55e1e)) @@ -1093,7 +875,7 @@ ROM_START(ti99_8) ROM_LOAD("g7_f840.bin", 0xE000, 0x1800, CRC(3a9d20df) SHA1(1e6f9f8ec7df4b997a7579be742d0a7d54bc8763)) // Pascal library. 8 chips @ f850 - ROM_REGION(0x10000, region_gromlib2, 0) + ROM_REGION(0x10000, GROMLIB2_REG, 0) ROM_LOAD("g0_f850.bin", 0x0000, 0x1800, CRC(2d948672) SHA1(cf15912d6dae5a450e0cfd796aa36ea5e521dc56)) ROM_LOAD("g1_f850.bin", 0x2000, 0x1800, CRC(7d64a842) SHA1(d5884bb2af21c8027311478ee506beac6f46203d)) ROM_LOAD("g2_f850.bin", 0x4000, 0x1800, CRC(e5ed8900) SHA1(03826882ce10fb5a6b3a9ccc85d3d1fe51979d0b)) @@ -1104,17 +886,15 @@ ROM_START(ti99_8) ROM_LOAD("g7_f850.bin", 0xE000, 0x1800, CRC(71534098) SHA1(75e87123efde885e27dd749e07cb189eb2cc45a8)) // Pascal library. 3 chips @ f860 - ROM_REGION(0x6000, region_gromlib3, 0) + ROM_REGION(0x6000, GROMLIB3_REG, 0) ROM_LOAD("g0_f860.bin", 0x0000, 0x1800, CRC(0ceef210) SHA1(b89957fbff094b758746391a69dea6907c66b950)) ROM_LOAD("g1_f860.bin", 0x2000, 0x1800, CRC(fc87de25) SHA1(4695b7f979f59a01ec16c55e4587c3379482b658)) ROM_LOAD("g2_f860.bin", 0x4000, 0x1800, CRC(e833e350) SHA1(6ffe501981a1112be1af596a489d96e287fc6be5)) - // Built-in RAM - ROM_REGION(SRAM_SIZE, SRAM_TAG, 0) - ROM_FILL(0x0000, SRAM_SIZE, 0x00) - - ROM_REGION(DRAM_SIZE, DRAM_TAG, 0) - ROM_FILL(0x0000, DRAM_SIZE, 0x00) + // Speech ROM + ROM_REGION(0x8000, SPEECHROM_REG, 0) + ROM_LOAD("cd2325a.vsm", 0x0000, 0x4000, CRC(1f58b571) SHA1(0ef4f178716b575a1c0c970c56af8a8d97561ffe)) + ROM_LOAD("cd2326a.vsm", 0x4000, 0x4000, CRC(65d00401) SHA1(a367242c2c96cebf0e2bf21862f3f6734b2b3020)) ROM_END #define rom_ti99_8e rom_ti99_8 |