diff options
Diffstat (limited to 'src/mess/machine/ti99/p_code.c')
-rw-r--r-- | src/mess/machine/ti99/p_code.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/mess/machine/ti99/p_code.c b/src/mess/machine/ti99/p_code.c new file mode 100644 index 00000000000..baec75cab96 --- /dev/null +++ b/src/mess/machine/ti99/p_code.c @@ -0,0 +1,325 @@ +/**************************************************************************** + + TI-99 P-Code Card emulation. + + The P-Code card is part of the UCSD p-System support for the TI-99 + computer family. This system is a comprehensive development system for + creating, running, and debugging programs written in UCSD Pascal. + + The complete system consists of + - P-Code card, plugged into the Peripheral Expansion Box (PEB) + - Software on disk: + + PHD5063: UCSD p-System Compiler + + PHD5064: UCSD p-System Assembler/Linker + + PHD5065: UCSD p-System Editor/Filer (2 disks) + + The card has a switch on the circuit board extending outside the PEB + which allows to turn off the card without removing it. Unlike other + expansion cards for the TI system, the P-Code card immediately takes + over control after the system is turned on. + + When the p-System is booted, the screen turns cyan and remains empty. + There are two beeps, a pause for about 15 seconds, another three beeps, + and then a welcome text is displayed with a one-line menu at the screen + top. (Delay times seem unrealistically short; the manual says + 30-60 seconds. To be checked.) + Many of the functions require one of the disks be inserted in one + of the disk drives. You can leave the p-System by waiting for the menu + to appear, and typing H (halt). This returns you to the Master Title + Screen, and the card is inactive until the system is reset. + + The P-Code card contains the P-Code interpreter which is somewhat + comparable to today's Java virtual machine. Programs written for the + p-System are interchangeable between different platforms. + + On the P-Code card we find 12 KiB of ROM, visible in the DSR memory area + (>4000 to >5FFF). The first 4 KiB (>4000->4FFF) are from the 4732 ROM, + the second and third 4 KiB (>5000->5FFF) are from a 4764 ROM, switched + by setting the CRU bit 4 to 1 on the CRU base >1F00. + + CRU base >1F00 + Bit 0: Activate card + Bit 4: Select bank 2 of the 4764 ROM (0 = bank 1) + Bit 7: May be connected to an indicator LED which is by default + wired to bit 0 (on the PCB) + + The lines are used in a slightly uncommon way: the three bits of the + CRU bit address are A8, A13, and A14 (A15=LSB). Hence, bit 4 is at + address >1F80, and bit 7 is at address >1F86. These bits are purely + write-only. + + Moreover, the card contains 48 KiB of GROM memory, occupying the address + space from G>0000 to G>FFFF in portions of 6KiB at every 8KiB boundary. + + Another specialty of the card is that the GROM contents are accessed via + another GROM base address than what is used in the console: + - >5BFC = read GROM data + - >5BFE = read GROM address + - >5FFC = write GROM data + - >5FFE = write GROM address + + This makes the GROM memory "private" to the card; together with the + rest of the ROM space the ports become invisible when the card is + deactivated. + + Michael Zapf + + July 2009: First version + September 2010: Rewritten as device + February 2012: Rewritten as class + +*****************************************************************************/ + +#include "p_code.h" + +#define PCODE_GROM_TAG "pcode_grom" +#define PCODE_ROM_TAG "pcode_rom" + +#define PGROM0_TAG "grom0" +#define PGROM1_TAG "grom1" +#define PGROM2_TAG "grom2" +#define PGROM3_TAG "grom3" +#define PGROM4_TAG "grom4" +#define PGROM5_TAG "grom5" +#define PGROM6_TAG "grom6" +#define PGROM7_TAG "grom7" + +#define GROMMASK 0x1ffd +#define GROMREAD 0x1bfc +#define GROMWRITE 0x1ffc + +#define ACTIVE_TAG "ACTIVE" + +#define LOG logerror +#define VERBOSE 1 + +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) +{ + m_shortname = "ti99_pcode"; +} + +READ8Z_MEMBER( ti_pcode_card_device::readz ) +{ + if (m_switch && m_selected && ((offset & m_select_mask)==m_select_value)) + { + // GROM access + if ((offset & GROMMASK)==GROMREAD) + { + for (int i=0; i < 8; i++) m_grom[i]->readz(space, offset, value, mem_mask); + if (VERBOSE>5) LOG("ti99_pcode: read from grom %04x: %02x\n", offset&0xffff, *value); + } + else + { + if ((offset & 0x1000) == 0x0000) + { + /* 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); + } + 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); + } + } + } +} + +/* + Write a byte in P-Code ROM space. This is only used for setting the + GROM address. +*/ +WRITE8_MEMBER( ti_pcode_card_device::write ) +{ + if (m_switch && 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 (int i=0; i < 8; i++) m_grom[i]->write(space, offset, data, mem_mask); + } + } + } +} + +/* + Common READY* line from the GROMs. + At this time we do not emulate GROM READY* since the CPU emulation does + not yet process READY*. If it did, however, we would have to do a similar + handling as in peribox (with INTA*): The common READY* line is a logical + AND of all single READY lines. If any GROM pulls it down, the line goes + down, and only if all GROMs release it, it pulls up again. We should think + about a general solution. +*/ +WRITE_LINE_MEMBER( ti_pcode_card_device::ready_line ) +{ + m_slot->set_ready(state); +} + +/* + 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.) +*/ +void ti_pcode_card_device::crureadz(offs_t offset, UINT8 *value) +{ + // Nothing here. + return; +} + +/* + The CRU write handler. + Bit 0 = activate card + Bit 4 = select second bank of high ROM. + + Somewhat uncommon, the CRU address is created from address lines + A8, A13, and A14 so bit 0 is at 0x1f00, but bit 4 is at 0x1f80. Accordingly, + bit 7 would be 0x1f86 but it is not used. +*/ +void ti_pcode_card_device::cruwrite(offs_t offset, UINT8 data) +{ + if ((offset & 0xff00)==m_cru_base) + { + int addr = offset & 0x00ff; + + if (addr==0) + m_selected = (data != 0); + + 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); + } + } +} + +static GROM_CONFIG(pgrom0_config) +{ + false, 0, PCODE_GROM_TAG, 0x0000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), GROMFREQ +}; +static GROM_CONFIG(pgrom1_config) +{ + false, 1, PCODE_GROM_TAG, 0x2000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), GROMFREQ +}; +static GROM_CONFIG(pgrom2_config) +{ + false, 2, PCODE_GROM_TAG, 0x4000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), GROMFREQ +}; +static GROM_CONFIG(pgrom3_config) +{ + false, 3, PCODE_GROM_TAG, 0x6000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), GROMFREQ +}; +static GROM_CONFIG(pgrom4_config) +{ + false, 4, PCODE_GROM_TAG, 0x8000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), GROMFREQ +}; +static GROM_CONFIG(pgrom5_config) +{ + false, 5, PCODE_GROM_TAG, 0xa000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), GROMFREQ +}; +static GROM_CONFIG(pgrom6_config) +{ + false, 6, PCODE_GROM_TAG, 0xc000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), GROMFREQ +}; +static GROM_CONFIG(pgrom7_config) +{ + false, 7, PCODE_GROM_TAG, 0xe000, 0x1800, DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, ti_pcode_card_device, ready_line), 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_rom = memregion(PCODE_ROM_TAG)->base(); +} + +void ti_pcode_card_device::device_reset() +{ + if (m_genmod) + { + m_select_mask = 0x1fe000; + m_select_value = 0x174000; + } + else + { + m_select_mask = 0x7e000; + m_select_value = 0x74000; + } + m_bank_select = 1; + m_selected = false; + + m_switch = ioport(ACTIVE_TAG)->read(); +} + +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); +} + + +MACHINE_CONFIG_FRAGMENT( ti99_pcode ) + MCFG_GROM_ADD( PGROM0_TAG, pgrom0_config ) + MCFG_GROM_ADD( PGROM1_TAG, pgrom1_config ) + MCFG_GROM_ADD( PGROM2_TAG, pgrom2_config ) + MCFG_GROM_ADD( PGROM3_TAG, pgrom3_config ) + MCFG_GROM_ADD( PGROM4_TAG, pgrom4_config ) + MCFG_GROM_ADD( PGROM5_TAG, pgrom5_config ) + MCFG_GROM_ADD( PGROM6_TAG, pgrom6_config ) + MCFG_GROM_ADD( PGROM7_TAG, pgrom7_config ) +MACHINE_CONFIG_END + +INPUT_PORTS_START( ti99_pcode ) + PORT_START( ACTIVE_TAG ) + PORT_DIPNAME( 0x01, 0x00, "P-Code activation switch" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti_pcode_card_device, switch_changed, 0) + PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x01, DEF_STR( On ) ) +INPUT_PORTS_END + +ROM_START( ti99_pcode ) + ROM_REGION(0x10000, PCODE_GROM_TAG, 0) + ROM_LOAD("pcode_g0.bin", 0x0000, 0x10000, CRC(541b3860) SHA1(7be77c216737334ae997753a6a85136f117affb7)) /* TI P-Code card groms */ + ROM_REGION(0x3000, PCODE_ROM_TAG, 0) + ROM_LOAD("pcode_r0.bin", 0x0000, 0x1000, CRC(3881d5b0) SHA1(a60e0468bb15ff72f97cf6e80979ca8c11ed0426)) /* TI P-Code card rom4732 */ + ROM_LOAD("pcode_r1.bin", 0x1000, 0x2000, CRC(46a06b8b) SHA1(24e2608179921aef312cdee6f455e3f46deb30d0)) /* TI P-Code card rom4764 */ +ROM_END + +machine_config_constructor ti_pcode_card_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( ti99_pcode ); +} + +const rom_entry *ti_pcode_card_device::device_rom_region() const +{ + return ROM_NAME( ti99_pcode ); +} + +ioport_constructor ti_pcode_card_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( ti99_pcode ); +} + +const device_type TI99_P_CODE = &device_creator<ti_pcode_card_device>; + |