diff options
author | 2012-08-21 10:41:19 +0000 | |
---|---|---|
committer | 2012-08-21 10:41:19 +0000 | |
commit | 7285b359d259b2ae0fdf85096571c386ec8c991a (patch) | |
tree | a027aff57f1a255f9ec6cfd3b68cabe4b6683998 /src/mess/drivers/ti99_8.c | |
parent | 67c425e90757876a6716b7867df30c0149912e74 (diff) |
Merge of MESS sources (no whatsnew)
Diffstat (limited to 'src/mess/drivers/ti99_8.c')
-rw-r--r-- | src/mess/drivers/ti99_8.c | 1082 |
1 files changed, 1082 insertions, 0 deletions
diff --git a/src/mess/drivers/ti99_8.c b/src/mess/drivers/ti99_8.c new file mode 100644 index 00000000000..fc19939445e --- /dev/null +++ b/src/mess/drivers/ti99_8.c @@ -0,0 +1,1082 @@ +/**************************************************************************** + + The MESS TI-99/8 emulation driver + + The TI-99/8 was the envisaged successor to the TI-99/4A but never passed + its prototype state. Only a few consoles were built. The ROMs were not + even finalized, so the few available consoles may have different + operating system versions. + + There is some preliminary info: + +Name: Texas Instruments Computer TI-99/8 (no "Home") + +References: + * machine room <http://...> + * TI99/8 user manual + * TI99/8 schematics + * TI99/8 ROM source code + * Message on TI99 yahoo group for CPU info + +General: + * a few dozen units were built in 1983, never released + * CPU is a custom variant of tms9995 (part code MP9537): the 16-bit RAM and + (presumably) the on-chip decrementer are disabled + * 220kb(?) of ROM, including monitor, GPL interpreter, TI-extended basic + II, and a P-code interpreter with a few utilities. More specifically: + - 32kb system ROM with GPL interpreter, TI-extended basic II and a few + utilities (no dump, but 90% of source code is available and has been + compiled) + - 18kb system GROMs, with monitor and TI-extended basic II (no dump, + but source code is available and has been compiled) + - 4(???)kb DSR ROM for hexbus (no dump) + - 32(?)kb speech ROM: contents are slightly different from the 99/4(a) + speech ROMs, due to the use of a tms5220 speech synthesizer instead of + the older tms0285 (no dump, but 99/4(a) speech ROMs should work mostly + OK) + - 12(???)kb ROM with PCode interpreter (no dump) + - 2(3???)*48kb of GROMs with PCode data files (no dump) + * 2kb SRAM (16 bytes of which are hidden), 64kb DRAM (expandable to almost + 16MBytes), 16kb vdp RAM + * tms9118 vdp (similar to tms9918a, slightly different bus interface and + timings) + * I/O + - 50-key keyboard, plus 2 optional joysticks + - sound and speech (both ti99/4(a)-like) + - Hex-Bus + - Cassette + * cartridge port on the top + * 50-pin(?) expansion port on the back + * Programs can enable/disable the ROM and memory mapped register areas. + +Mapper: + Mapper has 4kb page size (-> 16 pages per map file), 32 bits per page + entry. Address bits A0-A3 are the page index, whereas bits A4-A15 are the + offset in the page. Physical address space is 16Mbytes. All pages are 4 + kBytes in length, and they can start anywhere in the 24-bit physical + address space. The mapper can load any of 4 map files from SRAM by DMA. + Map file 0 is used by BIOS, file 1 by memory XOPs(?), file 2 by P-code + interpreter(???). + + Format of map table entry: + * bit 0: WTPROT: page is write protected if 1 + * bit 1: XPROT: page is execute protected if 1 + * bit 2: RDPROT: page is read protected if 1 + * bit 3: reserved, value is ignored + * bits 4-7: reserved, always forced to 0 + * bits 8-23: page base address in 24-bit virtual address space + + Format of mapper control register: + * bit 0-4: unused??? + * bit 5-6: map file to load/save (0 for file 0, 1 for file 1, etc.) + * bit 7: 0 -> load map file from RAM, 1 -> save map file to RAM + + Format of mapper status register (cleared by read): + * bit 0: WPE - Write-Protect Error + * bit 1: XCE - eXeCute Error + * bit 2: RPE - Read-Protect Error + * bits 3-7: unused??? + + Memory error interrupts are enabled by setting WTPROT/XPROT/RDPROT. When + an error occurs, the tms9901 INT1* pin is pulled low (active). The pin + remains low until the mapper status register is read. + +24-bit address map: + * >000000->00ffff: console RAM + * >010000->feffff: expansion? + * >ff0000->ff0fff: empty??? + * >ff1000->ff3fff: unused??? + * >ff4000->ff5fff: DSR space + * >ff6000->ff7fff: cartridge space + * >ff8000->ff9fff(???): >4000 ROM (normally enabled with a write to CRU >2700) + * >ffa000->ffbfff(?): >2000 ROM + * >ffc000->ffdfff(?): >6000 ROM + + +CRU map: + Since the tms9995 supports full 15-bit CRU addresses, the >1000->17ff + (>2000->2fff) range was assigned to support up to 16 extra expansion slot. + The good thing with using >1000->17ff is the fact that older expansion + cards that only decode 12 address bits will think that addresses + >1000->17ff refer to internal TI99 peripherals (>000->7ff range), which + suppresses any risk of bus contention. + * >0000->001f (>0000->003e): tms9901 + - P4: 1 -> MMD (Memory Mapped Devices?) at >8000, ROM enabled + - P5: 1 -> no P-CODE GROMs + * >0800->17ff (>1000->2ffe): Peripheral CRU space + * >1380->13ff (>2700->27fe): Internal DSR, with two output bits: + - >2700: Internal DSR select (parts of Basic and various utilities) + - >2702: SBO -> hardware reset + + +Memory map (TMS9901 P4 == 1): + When TMS9901 P4 output is set, locations >8000->9fff are ignored by mapper. + * >8000->83ff: SRAM (>8000->80ff is used by the mapper DMA controller + to hold four map files) (r/w) + * >8400: sound port (w) + * >8410->87ff: SRAM (r/w) + * >8800: VDP data read port (r) + * >8802: VDP status read port (r) + * >8810: memory mapper status and control registers (r/w) + * >8c00: VDP data write port (w) + * >8c02: VDP address and register write port (w) + * >9000: speech synthesizer read port (r) + * >9400: speech synthesizer write port (w) + * >9800 GPL data read port (r) + * >9802 GPL address read port (r) + * >9c00 GPL data write port -- unused (w) + * >9c02 GPL address write port (w) + + +Memory map (TMS9901 P5 == 0): + When TMS9901 P5 output is cleared, locations >f840->f8ff(?) are ignored by + mapper. + * >f840: data port for P-code grom library 0 (r?) + * >f880: data port for P-code grom library 1 (r?) + * >f8c0: data port for P-code grom library 2 (r?) + * >f842: address port for P-code grom library 0 (r/w?) + * >f882: address port for P-code grom library 1 (r/w?) + * >f8c2: address port for P-code grom library 2 (r/w?) + + +Cassette interface: + Identical to ti99/4(a), except that the CS2 unit is not implemented. + + +Keyboard interface: + The keyboard interface uses the console tms9901 PSI, but the pin assignment + and key matrix are different from both 99/4 and 99/4a. + - P0-P3: column select + - INT6*-INT11*: row inputs (int6* is only used for joystick fire) + +ROM file contents: + 0000-1fff ROM0 0x0000 (logical address) + 2000-3fff ROM1 0xffa000 - 0xffbfff + 4000-5fff DSR1 0xff4000 (TTS) + 6000-7fff ROM1a 0xffc000 - 0xffdfff + 8000-9fff DSR2 0xff4000 (missing; Hexbus?) + +=========================================================================== +Known Issues (MZ, 2010-11-07) + + KEEP IN MIND THAT TEXAS INSTRUMENTS NEVER RELEASED THE TI-99/8 AND THAT + THERE ARE ONLY A FEW PROTOTYPES OF THE TI-99/8 AVAILABLE. ALL SOFTWARE + MUST BE ASSUMED TO HAVE REMAINED IN A PRELIMINARY STATE. + +- Extended Basic II does not start when a floppy controller is present. This is + a problem of the prototypical XB II which we cannot solve. It seems as if only + hexbus devices are properly supported, but we currently do not have an + emulation for those. Thus you can currently only use cassette to load and + save programs. You MUST not plug in any floppy controller when you intend to + start XB II. Other cartridges (like Editor/Assembler) + seem to be not affected by this problem and can make use of the floppy + controllers. + Technical detail: The designers of XB II seem to have decided to put PABs + (Peripheral access block; contains pointers to buffers, the file name, and + the access modes) into CPU RAM instead of the traditional storage in VDP + RAM. The existing peripheral cards are hard-coded to interpret the given + pointer to the PAB as pointing to a VDP RAM address. That is, as soon as + the card is found, control is passed to the DSR (device service routine), + the file name will not be found, and control returns with an error. It seems + as if XB II does not properly handle this situation and may lock up + (sometimes it starts up, but file access is still not possible). + +- Multiple cartridges are not shown in the startup screen; only one + cartridge is presented. You have to manually select the cartridges with the + dip switch. + +- SAVE and OLD MINIMEM do not work properly in XB II. It seems as if the + mapper shadows the NVRAM of the cartridge. You will lose the contents when + you turn off the machine. + + Raphael Nabet, 2003. + + February 2012: Rewritten as class + Michael Zapf + +*****************************************************************************/ + + +#include "emu.h" +#include "cpu/tms9900/tms9995.h" + +#include "sound/sn76496.h" +#include "sound/wave.h" +#include "machine/tms9901.h" +#include "imagedev/cassette.h" + +#include "machine/ti99/videowrp.h" +#include "machine/ti99/speech8.h" + +#include "machine/ti99/peribox.h" +#include "machine/ti99/mapper8.h" +#include "machine/ti99/grom.h" +#include "machine/ti99/gromport.h" +#include "machine/ti99/joyport.h" + +#define VERBOSE 0 +#define LOG logerror + +class ti99_8 : public driver_device +{ +public: + ti99_8(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) { } + + // CRU (Communication Register Unit) handling + DECLARE_READ8_MEMBER(cruread); + DECLARE_WRITE8_MEMBER(cruwrite); + + DECLARE_WRITE8_MEMBER(external_operation); + + // Forwarding interrupts to the CPU or CRU + DECLARE_WRITE_LINE_MEMBER( console_ready ); + DECLARE_WRITE_LINE_MEMBER( console_ready_mapper ); + DECLARE_WRITE_LINE_MEMBER( console_reset ); + + DECLARE_WRITE_LINE_MEMBER( set_tms9901_INT2 ); + DECLARE_WRITE_LINE_MEMBER( extint ); + DECLARE_WRITE_LINE_MEMBER( notconnected ); + + // Connections with the system interface TMS9901 + DECLARE_READ8_MEMBER(read_by_9901); + DECLARE_WRITE_LINE_MEMBER(keyC0); + 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); + DECLARE_WRITE8_MEMBER(tms9901_interrupt); + + DECLARE_WRITE_LINE_MEMBER( clock_out ); + + // Some values to keep + tms9995_device *m_cpu; + tms9901_device *m_tms9901; + gromport_device *m_gromport; + peribox_device *m_peribox; + ti998_mapper_device *m_mapper; + joyport_device* m_joyport; + ti_video_device* m_video; + + int m_firstjoy; // First joystick. 14 for TI-99/8 + + int m_ready_line, m_ready_line1; + +private: + /* Keyboard support */ + void set_keyboard_column(int number, int data); + int m_keyboard_column; + int m_alphalock_line; +}; + +/* + Memory map. We have a configurable mapper, so we need to delegate the + job to the mapper completely. +*/ +static ADDRESS_MAP_START(memmap, AS_PROGRAM, 8, ti99_8) + AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(MAPPER_TAG, ti998_mapper_device, readm, writem ) +ADDRESS_MAP_END + +/* + CRU map - see description above + The TMS9901 is fully decoded according to the specification, so we only + have 32 bits for it; the rest goes to the CRU bus + (decoded by the "Vaquerro" chip, signal NNOICS*) +*/ + +static ADDRESS_MAP_START(crumap, AS_IO, 8, ti99_8) + AM_RANGE(0x0000, 0x0003) AM_DEVREAD(TMS9901_TAG, tms9901_device, read) + AM_RANGE(0x0000, 0x02ff) AM_READ(cruread) + + AM_RANGE(0x0000, 0x001f) AM_DEVWRITE(TMS9901_TAG, tms9901_device, write) + AM_RANGE(0x0000, 0x17ff) AM_WRITE(cruwrite) +ADDRESS_MAP_END + +/* ti99/8 : 54-key keyboard */ +static INPUT_PORTS_START(ti99_8) + + /* 16 ports for keyboard and joystick */ + PORT_START("COL0") /* col 0 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALPHA LOCK") PORT_CODE(KEYCODE_CAPSLOCK) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FCTN") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LSHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL1") /* col 1 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 ! DEL") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL2") /* col 2 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 @ INS") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s S (LEFT)") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x X (DOWN)") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL3") /* col 3 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e E (UP)") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d D (RIGHT)") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL4") /* col 4 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $ CLEAR") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL5") /* col 5 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 % BEGIN") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL6") /* col 6 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ^ PROC'D") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL7") /* col 7 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 & AID") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL8") /* col 8 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 * REDO") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL9") /* col 9 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 ( BACK") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL10") /* col 10 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL11") /* col 11 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= + QUIT") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RSHIFT") PORT_CODE(KEYCODE_RSHIFT) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL12") /* col 12 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(SPACE)") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("COL13") /* col 13 */ + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('`') PORT_CHAR('~') + PORT_BIT(0x07, IP_ACTIVE_LOW, IPT_UNUSED) + +INPUT_PORTS_END + +/***************************************************************************** + Components +******************************************************************************/ + +static GROM_CONFIG(grom0_config) +{ + false, 0, region_grom, 0x0000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ +}; + +static GROM_CONFIG(grom1_config) +{ + false, 1, region_grom, 0x2000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ +}; + +static GROM_CONFIG(grom2_config) +{ + false, 2, region_grom, 0x4000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ +}; + +/**************************************************** + PASCAL groms, 3 libraries @ 8 GROMs + Do some macro tricks to keep writing effort low +*****************************************************/ + +#define pascal0_region "pascal0_region" +#define pascal12_region "pascal12_region" + +#define MCFG_GROM_LIBRARY_ADD(_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 GROM_LIBRARY_CONFIG(_conf, _region) \ +static GROM_CONFIG(_conf##0) \ +{ false, 0, _region, 0x0000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; \ +static GROM_CONFIG(_conf##1) \ +{ false, 1, _region, 0x2000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; \ +static GROM_CONFIG(_conf##2) \ +{ false, 2, _region, 0x4000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; \ +static GROM_CONFIG(_conf##3) \ +{ false, 3, _region, 0x6000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; \ +static GROM_CONFIG(_conf##4) \ +{ false, 4, _region, 0x8000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; \ +static GROM_CONFIG(_conf##5) \ +{ false, 5, _region, 0xa000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; \ +static GROM_CONFIG(_conf##6) \ +{ false, 6, _region, 0xc000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; \ +static GROM_CONFIG(_conf##7) \ +{ false, 7, _region, 0xe000, 0x1800, DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), GROMFREQ }; + +GROM_LIBRARY_CONFIG(pascal0, pascal0_region) +GROM_LIBRARY_CONFIG(pascal1, pascal12_region) +GROM_LIBRARY_CONFIG(pascal2, pascal12_region) + +static GROMPORT_CONFIG(console_cartslot) +{ + DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_reset) +}; + +static PERIBOX_CONFIG( peribox_conf ) +{ + DEVCB_DRIVER_LINE_MEMBER(ti99_8, extint), // INTA + DEVCB_DRIVER_LINE_MEMBER(ti99_8, notconnected), // INTB + DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), // READY + 0x70000 // Address bus prefix (AMA/AMB/AMC) +}; + +READ8_MEMBER( ti99_8::cruread ) +{ +// if (VERBOSE>6) LOG("read access to CRU address %04x\n", offset << 4); + UINT8 value = 0; + + // Similar to the bus8z_devices, just let the mapper, the gromport, and the p-box + // decide whether they want to change the value at the CRU address + // Also, we translate the bit addresses to base addresses + m_mapper->crureadz(offset<<4, &value); + m_gromport->crureadz(offset<<4, &value); + m_peribox->crureadz(offset<<4, &value); + + if (VERBOSE>8) LOG("ti99_8: CRU %04x -> %02x\n", offset<<4, value); + return value; +} + +WRITE8_MEMBER( ti99_8::cruwrite ) +{ + if (VERBOSE>8) LOG("ti99_8: CRU %04x <- %x\n", offset<<1, data); + m_mapper->cruwrite(offset<<1, data); + m_gromport->cruwrite(offset<<1, data); + m_peribox->cruwrite(offset<<1, data); +} + +/*************************************************************************** + TI99/8-specific tms9901 I/O handlers + These methods are callbacks from the TMS9901 system interface. That is, + they deliver the values queried via the TMS9901, and they represent + console functions which are under control of the TMS9901 (like the + keyboard column selection.) +***************************************************************************/ + +static const char *const column[] = { + "COL0", "COL1", "COL2", "COL3", "COL4", "COL5", "COL6", "COL7", + "COL8", "COL9", "COL10", "COL11", "COL12", "COL13" +}; + +READ8_MEMBER( ti99_8::read_by_9901 ) +{ + int answer=0; + UINT8 joyst; + switch (offset & 0x03) + { + case TMS9901_CB_INT7: + // Read pins INT3*-INT7* of TI99's 9901. + // + // (bit 1: INT1 status) + // (bit 2: INT2 status) + // bits 3-4: unused? + // bit 5: ??? + // bit 6-7: keyboard status bits 0 through 1 + + // |K|K|-|-|-|I2|I1|C| + if (m_keyboard_column >= m_firstjoy) + { + // TI-99/8's wiring differs from the TI-99/4A + joyst = m_joyport->read_port(); + answer = (joyst & 0x01) | ((joyst & 0x10)>>3); + } + else + { + answer = ioport(column[m_keyboard_column])->read(); + } + answer = (answer << 6) & 0xc0; + + break; + + case TMS9901_INT8_INT15: + // Read pins INT8*-INT15* of TI99's 9901. + // + // bit 0-2: keyboard status bits 2 to 4 + // bit 3: tape input mirror + // bit 4: unused + // bit 5-7: weird, not emulated + + // |0|0|0|0|0|K|K|K| + + if (m_keyboard_column >= m_firstjoy) + { + joyst = m_joyport->read_port(); + answer = joyst << 1; + } + else + { + answer = ioport(column[m_keyboard_column])->read(); + } + answer = (answer >> 2) & 0x07; + break; + + case TMS9901_P0_P7: + // Read pins P0-P7 of TI99's 9901. None here. + break; + + case TMS9901_P8_P15: + // Read pins P8-P15 of TI99's 9901. (TI-99/8) + // + // bit 26: high + // bit 27: tape input + answer = 4; + if ((machine().device<cassette_image_device>(CASSETTE_TAG))->input() > 0) + answer |= 8; + break; + } + return answer; +} + +/* + WRITE key column select (P2-P4), TI-99/8 +*/ +void ti99_8::set_keyboard_column(int number, int data) +{ + if (data != 0) m_keyboard_column |= 1 << number; + else m_keyboard_column &= ~(1 << number); + + if (m_keyboard_column >= m_firstjoy) + { + m_joyport->write_port(m_keyboard_column - m_firstjoy + 1); + } +} + +WRITE_LINE_MEMBER( ti99_8::keyC0 ) +{ + set_keyboard_column(0, state); +} + +WRITE_LINE_MEMBER( ti99_8::keyC1 ) +{ + set_keyboard_column(1, state); +} + +WRITE_LINE_MEMBER( ti99_8::keyC2 ) +{ + set_keyboard_column(2, state); +} + +WRITE_LINE_MEMBER( ti99_8::keyC3 ) +{ + set_keyboard_column(3, state); +} + +/* + Set 99/4A compatibility mode (CRUS=1) +*/ +WRITE_LINE_MEMBER( ti99_8::CRUS ) +{ + m_mapper->CRUS_set(state==ASSERT_LINE); +} + +/* + Set mapper /PTGEN. This is negative logic; we use PTGE as the positive logic signal. +*/ +WRITE_LINE_MEMBER( ti99_8::PTGEN ) +{ + m_mapper->PTGE_set(state==CLEAR_LINE); +} + +/* + Control cassette tape unit motor (P6) +*/ +WRITE_LINE_MEMBER( ti99_8::cassette_motor ) +{ + cassette_image_device *img = machine().device<cassette_image_device>(CASSETTE_TAG); + img->change_state(state==ASSERT_LINE? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR); +} + +/* + Audio gate (P8) + Set to 1 before using tape: this enables the mixing of tape input sound + with computer sound. + We do not really need to emulate this as the tape recorder generates sound + on its own. +*/ +WRITE_LINE_MEMBER( ti99_8::audio_gate ) +{ +} + +/* + Tape output (P9) + I think polarity is correct, but don't take my word for it. +*/ +WRITE_LINE_MEMBER( ti99_8::cassette_output ) +{ + machine().device<cassette_image_device>(CASSETTE_TAG)->output(state==ASSERT_LINE? +1 : -1); +} + +WRITE8_MEMBER( ti99_8::tms9901_interrupt ) +{ + m_cpu->set_input_line(INPUT_LINE_99XX_INT1, data); +} + +const tms9901_interface tms9901_wiring_ti99_8 = +{ + TMS9901_INT1 | TMS9901_INT2 | TMS9901_INTC, + + // read handler + DEVCB_DRIVER_MEMBER(ti99_8, read_by_9901), + + // write handlers + { + DEVCB_DRIVER_LINE_MEMBER(ti99_8, keyC0), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, keyC1), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, keyC2), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, keyC3), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, CRUS), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, PTGEN), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, cassette_motor), + DEVCB_NULL, + DEVCB_DRIVER_LINE_MEMBER(ti99_8, audio_gate), + DEVCB_DRIVER_LINE_MEMBER(ti99_8, cassette_output), + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL + }, + + DEVCB_DRIVER_MEMBER(ti99_8, tms9901_interrupt) +}; + +/*****************************************************************************/ + +/* + set the state of TMS9901's INT2 (called by the tms9928 core) +*/ +WRITE_LINE_MEMBER( ti99_8::set_tms9901_INT2 ) +{ + if (VERBOSE>6) LOG("ti99_8: VDP int 2 on tms9901, level=%02x\n", state); + m_tms9901->set_single_int(2, state); +} + +/*********************************************************** + Links to external devices +***********************************************************/ + + +WRITE_LINE_MEMBER( ti99_8::console_ready ) +{ + if (VERBOSE>6) LOG("ti99_8: READY level=%02x\n", state); + m_ready_line = state; + + m_cpu->set_ready((m_ready_line == ASSERT_LINE && m_ready_line1 == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE); +} + +/* + The RESET line leading to a reset of the CPU. +*/ +WRITE_LINE_MEMBER( ti99_8::console_reset ) +{ + if (machine().phase() != MACHINE_PHASE_INIT) + { + m_cpu->set_input_line(INPUT_LINE_99XX_RESET, state); + m_video->reset_vdp(state); + } +} + +/* + Memory access over the mapper also operates + the READY line, and the mapper raises READY depending on the clock pulse. + So we must make sure this does not interfere. +*/ +WRITE_LINE_MEMBER( ti99_8::console_ready_mapper ) +{ + if (VERBOSE>6) LOG("ti99_8: READY level (mapper) = %02x\n", state); + m_ready_line1 = state; + m_cpu->set_ready((m_ready_line == ASSERT_LINE && m_ready_line1 == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE); +} + +WRITE_LINE_MEMBER( ti99_8::extint ) +{ + if (VERBOSE>6) LOG("ti99_8: EXTINT level = %02x\n", state); + if (m_tms9901 != NULL) + m_tms9901->set_single_int(1, state); +} + +WRITE_LINE_MEMBER( ti99_8::notconnected ) +{ + if (VERBOSE>6) LOG("ti99_8: Setting a not connected line ... ignored\n"); +} + +static TMS9928A_INTERFACE(ti99_8_tms9118a_interface) +{ + SCREEN_TAG, + 0x4000, + DEVCB_DRIVER_LINE_MEMBER(ti99_8, set_tms9901_INT2) +}; + +WRITE8_MEMBER( ti99_8::external_operation ) +{ + static const char* extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" }; + if (VERBOSE>1) LOG("External operation %s not implemented on TI-99 board\n", extop[offset]); +} + +/* + Clock line from the CPU. Used to control wait state generation. +*/ +WRITE_LINE_MEMBER( ti99_8::clock_out ) +{ + m_mapper->clock_in(state); +} + +/*****************************************************************************/ + +/* + MP9537 mask: This variant of the TMS9995 does not contain on-chip RAM + Also, the overflow interrupt is disabled; in the available documentation + this feature is said to be disabled and is announced for a later version. +*/ +static TMS9995_CONFIG( ti99_8_processor_config ) +{ + DEVCB_DRIVER_MEMBER(ti99_8, external_operation), + DEVCB_NULL, // Instruction acquisition + DEVCB_DRIVER_LINE_MEMBER(ti99_8, clock_out), + DEVCB_NULL, // wait + DEVCB_NULL, // HOLDA + NO_INTERNAL_RAM, + NO_OVERFLOW_INT +}; + +static TI_SOUND_CONFIG( sound_conf ) +{ + DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready) // READY +}; + +/* + 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. + + NOTE: The available system software contradicts the specification + concerning the Pascal ports. While the specs define f840 as "Text-to-speech + library" and f850 and f860 as two libraries for Pascal, the operating + system defines f840 as Pascal lib 0, f880 as Pascal lib 1, and f8c0 as + Pascal lib 2. + + 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 24 GROMs, + every eight of them on the same address). +*/ + +#define PASCAL_GROM_LIB(_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 } + + +static const mapper8_list_entry mapper_devices[] = +{ + // TI-99/4A mode (CRUS=1) + // 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, 0xfff0, 0x0000 }, // 8400-840f + { VIDEO_SYSTEM_TAG, TI99EM, STOP, 0x8800, 0xfffd, 0x0400 }, // 8800,8802 / 8c00,8c02 + { SPEECH_TAG, TI99EM, STOP, 0x9000, 0xfff0, 0x0400 }, // 9000-900f / 9400-940f + { SRAMNAME, TI99EM, STOP, 0x8000, 0xf800, 0x0000 }, // 8000-87ff; must follow the sound generator + { MAPPER_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 + { SRAMNAME, NATIVE, STOP, 0xf000, 0xf800, 0x0000 }, // f000-f7ff + { TISOUND_TAG, NATIVE, STOP, 0xf800, 0xfff0, 0x0000 }, // f800-f80f + { VIDEO_SYSTEM_TAG, NATIVE, STOP, 0xf810, 0xfffd, 0x0000 }, // f810,2 (unlike 99/4A, no different read/write ports) + { SPEECH_TAG, NATIVE, STOP, 0xf820, 0xfff0, 0x0000 }, // f820-f82f + { MAPPER_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_LIB("pascal0_grom", 0xf840), + PASCAL_GROM_LIB("pascal1_grom", 0xf880), // lib1 and 2 are zeroed. We don't have good dumps for them yet. + PASCAL_GROM_LIB("pascal2_grom", 0xf8c0), // Anyway, we keep them in order to check whether/when they are accessed. + + // 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 + { MAPPER_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 + { ROM1NAME, PHYSIC, STOP, 0xffa000, 0xffe000, 0x000000 }, // ffa000-ffbfff ROM1 + { ROM1ANAME, 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 + + { NULL, 0, 0, 0, 0, 0 } +}; + +static MAPPER8_CONFIG( mapper_conf ) +{ + DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready_mapper), // READY + mapper_devices +}; + +static SPEECH8_CONFIG( speech_config ) +{ + DEVCB_DRIVER_LINE_MEMBER(ti99_8, console_ready), // READY +}; + +static JOYPORT_CONFIG( joyport8_60 ) +{ + DEVCB_NULL, + 60 +}; + +static JOYPORT_CONFIG( joyport8_50 ) +{ + DEVCB_NULL, + 50 +}; + +MACHINE_START( ti99_8 ) +{ + ti99_8 *driver = machine.driver_data<ti99_8>(); + driver->m_cpu = static_cast<tms9995_device*>(machine.device("maincpu")); + driver->m_tms9901 = static_cast<tms9901_device*>(machine.device(TMS9901_TAG)); + driver->m_gromport = static_cast<gromport_device*>(machine.device(GROMPORT_TAG)); + driver->m_peribox = static_cast<peribox_device*>(machine.device(PERIBOX_TAG)); + driver->m_mapper = static_cast<ti998_mapper_device*>(machine.device(MAPPER_TAG)); + driver->m_joyport = static_cast<joyport_device*>(machine.device(JOYPORT_TAG)); + driver->m_video = static_cast<ti_video_device*>(machine.device(VIDEO_SYSTEM_TAG)); + + driver->m_peribox->senila(CLEAR_LINE); + driver->m_peribox->senilb(CLEAR_LINE); + driver->m_firstjoy = 14; +} + +MACHINE_RESET( ti99_8 ) +{ + ti99_8 *driver = machine.driver_data<ti99_8>(); + + driver->m_cpu->set_hold(CLEAR_LINE); + + // Pulling down the line on RESET configures the CPU to insert one wait + // state on external memory accesses + + // RN: enable automatic wait state generation + // in January 83 99/8 schematics sheet 9: the delay logic + // seems to keep READY low for one cycle when RESET* is + // asserted, but the timings are completely wrong this way + + driver->m_cpu->set_ready(CLEAR_LINE); + + // But we assert the line here so that the system starts running + driver->m_ready_line = driver->m_ready_line1 = ASSERT_LINE; +} + +static MACHINE_CONFIG_START( ti99_8_60hz, ti99_8 ) + /* basic machine hardware */ + /* TMS9995-MP9537 CPU @ 10.7 MHz */ + MCFG_TMS9995_ADD("maincpu", TMS9995, 10738635, memmap, crumap, ti99_8_processor_config) + + MCFG_MACHINE_START( ti99_8 ) + MCFG_MACHINE_RESET( ti99_8 ) + + /* Video hardware */ + MCFG_TI998_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9118, ti99_8_tms9118a_interface) + + /* Main board */ + MCFG_TMS9901_ADD( TMS9901_TAG, tms9901_wiring_ti99_8, 2684658.75 ) + MCFG_MAPPER8_ADD( MAPPER_TAG, mapper_conf ) + MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG, console_cartslot ) + + /* Peripheral expansion box */ + MCFG_PERIBOX_ADD( PERIBOX_TAG, peribox_conf ) + + /* Sound hardware */ + MCFG_TI_SOUND_76496_ADD( TISOUND_TAG, sound_conf ) + + /* Cassette drives */ + MCFG_SPEAKER_STANDARD_MONO("cass_out") + MCFG_CASSETTE_ADD( CASSETTE_TAG, default_cassette_interface ) + MCFG_SOUND_WAVE_ADD(WAVE_TAG, CASSETTE_TAG) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25) + + /* Console GROMs */ + MCFG_GROM_ADD( GROM0_TAG, grom0_config ) + MCFG_GROM_ADD( GROM1_TAG, grom1_config ) + MCFG_GROM_ADD( GROM2_TAG, grom2_config ) + + /* Pascal GROM libraries. */ + MCFG_GROM_LIBRARY_ADD(pascal0_grom, pascal0) + MCFG_GROM_LIBRARY_ADD(pascal1_grom, pascal1) + MCFG_GROM_LIBRARY_ADD(pascal2_grom, pascal2) + + /* Devices */ + MCFG_TISPEECH8_ADD(SPEECH_TAG, speech_config) + + // Joystick port + MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG, joyport8_60 ) +MACHINE_CONFIG_END + + +static MACHINE_CONFIG_START( ti99_8_50hz, ti99_8 ) + /* basic machine hardware */ + /* TMS9995-MP9537 CPU @ 10.7 MHz */ + MCFG_TMS9995_ADD("maincpu", TMS9995, 10738635, memmap, crumap, ti99_8_processor_config) + MCFG_MACHINE_START( ti99_8 ) + MCFG_MACHINE_RESET( ti99_8 ) + + /* Video hardware */ + MCFG_TI998_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9129, ti99_8_tms9118a_interface) + + /* Main board */ + MCFG_TMS9901_ADD( TMS9901_TAG, tms9901_wiring_ti99_8, 2684658.75 ) + MCFG_MAPPER8_ADD( MAPPER_TAG, mapper_conf ) + MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG, console_cartslot ) + + /* Peripheral expansion box */ + MCFG_PERIBOX_ADD( PERIBOX_TAG, peribox_conf ) + + /* Sound hardware */ + MCFG_TI_SOUND_76496_ADD( TISOUND_TAG, sound_conf ) + + /* Cassette drives */ + MCFG_SPEAKER_STANDARD_MONO("cass_out") + MCFG_CASSETTE_ADD( CASSETTE_TAG, default_cassette_interface ) + MCFG_SOUND_WAVE_ADD(WAVE_TAG, CASSETTE_TAG) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "cass_out", 0.25) + + /* Console GROMs */ + MCFG_GROM_ADD( GROM0_TAG, grom0_config ) + MCFG_GROM_ADD( GROM1_TAG, grom1_config ) + MCFG_GROM_ADD( GROM2_TAG, grom2_config ) + + /* Pascal GROMs libraries. */ + MCFG_GROM_LIBRARY_ADD(pascal0_grom, pascal0) + MCFG_GROM_LIBRARY_ADD(pascal1_grom, pascal1) + MCFG_GROM_LIBRARY_ADD(pascal2_grom, pascal2) + + /* Devices */ + MCFG_TISPEECH8_ADD(SPEECH_TAG, speech_config) + + // Joystick port + MCFG_TI_JOYPORT4A_ADD( JOYPORT_TAG, joyport8_50 ) +MACHINE_CONFIG_END + +/* + ROM loading +*/ +ROM_START(ti99_8) + /*CPU memory space*/ + ROM_REGION(0x8000,"maincpu",0) + ROM_LOAD("998rom.bin", 0x0000, 0x8000, CRC(b7a06ffd) SHA1(17dc8529fa808172fc47089982efb0bf0548c80c)) /* system ROMs */ + + /*GROM memory space*/ + ROM_REGION(0x10000, region_grom, 0) + ROM_LOAD("998grom.bin", 0x0000, 0x6000, CRC(c63806bc) SHA1(cbfa8b04b4aefbbd9a713c54267ad4dd179c13a3)) /* system GROMs */ + + /* Pascal GROMs. Sadly, P-System fails to start. */ + ROM_REGION(0x10000, pascal0_region, 0) + ROM_LOAD_OPTIONAL("998pascal.bin", 0x0000, 0x10000, CRC(1389589e) SHA1(42942b99ed355a2c091cc480b15f5329156e6b03)) + + // Still need good dumps; so far, stay with 0 + ROM_REGION(0x10000, pascal12_region, 0) + ROM_FILL(0x0000, 0x10000, 0x00) + + /* 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) +ROM_END + +#define rom_ti99_8e rom_ti99_8 + +/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */ +COMP( 1983, ti99_8, 0, 0, ti99_8_60hz,ti99_8, driver_device, 0, "Texas Instruments", "TI-99/8 Computer (US)" , 0) +COMP( 1983, ti99_8e, ti99_8, 0, ti99_8_50hz,ti99_8, driver_device, 0, "Texas Instruments", "TI-99/8 Computer (Europe)" , 0 ) |