summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Michael Zapf <Michael.Zapf@mizapf.de>2018-05-05 22:04:21 +0200
committer Michael Zapf <Michael.Zapf@mizapf.de>2018-05-05 22:26:39 +0200
commit7adec69c0873fa128ff669b574d8caa2e0cc602c (patch)
tree79af5176754ce0d45b22afa3435d1766d81683f6
parent9e5971241ef7ba44a91b4b7a1b5927e4fdac00cb (diff)
ti99_2: New WORKING driver. New and verified ROM dumps, re-implementation, fixes.
-rw-r--r--scripts/src/bus.lua2
-rw-r--r--src/devices/bus/ti99/internal/992board.cpp298
-rw-r--r--src/devices/bus/ti99/internal/992board.h115
-rw-r--r--src/mame/drivers/ti99_2.cpp703
4 files changed, 864 insertions, 254 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index 39edfe5d9ac..4114928de76 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -2561,6 +2561,8 @@ end
if (BUSES["TI99"]~=null) then
files {
MAME_DIR .. "src/devices/bus/ti99/ti99defs.h",
+ MAME_DIR .. "src/devices/bus/ti99/internal/992board.cpp",
+ MAME_DIR .. "src/devices/bus/ti99/internal/992board.h",
MAME_DIR .. "src/devices/bus/ti99/internal/998board.cpp",
MAME_DIR .. "src/devices/bus/ti99/internal/998board.h",
MAME_DIR .. "src/devices/bus/ti99/internal/datamux.cpp",
diff --git a/src/devices/bus/ti99/internal/992board.cpp b/src/devices/bus/ti99/internal/992board.cpp
new file mode 100644
index 00000000000..669b43798de
--- /dev/null
+++ b/src/devices/bus/ti99/internal/992board.cpp
@@ -0,0 +1,298 @@
+// license:LGPL-2.1+
+// copyright-holders:Michael Zapf
+/***************************************************************************
+
+ TI-99/2 main board custom circuits
+
+ This component implements the custom video controller and interface chip
+ from the TI-99/2 console.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "992board.h"
+
+/**
+ Emulation of the CRT Gate Array of the TI-99/2
+
+ Video display controller
+
+ RF-modulated, composite output
+ for standard black/white television
+ Selectable channel 3 or 4 VHF
+
+ 625 lines for US markets
+ 525 lines for European markets
+
+ Display: 24 rows, 32 columns
+
+ The controller accesses ROM and RAM space of the 9995 CPU. It makes use
+ of the HOLD line to gain access. Thus, the controller has DMA control
+ while producing each scan line. The CPU has a chance to execute instructions
+ in border time, horizontal retrace, vertical retrace.
+
+ In order to get more computing time, a special character (BEOL - bank end
+ of line) is used to indicate the last drawable character on the line. After
+ this character, the buses are released.
+
+ 24K version: BEOL = any character from 0x70 to 0xff
+ 32K version: BEOL = any character from 0x80 to 0xff
+
+ CRU Bit VIDENA: disables the scan line generation; blank white screen
+
+ Clock: 10.7 MHz
+ Divided by 2 for 9995 CLKIN
+
+ Scanline refresh:
+ - Pull down HOLD
+ - Wait for a short time (some dots)
+ - Use row, column, dot_line counters
+ - Get the value c at 0xEC00 + row*32+col
+ - Get the byte b from 0x1C00 + c*8 + (dot_line%8)
+ - Push the byte to the shift register
+ - Get the bits for the scanline from the register
+
+ EF00: Control byte
+ +--+--+--+--+--+--+--+--+
+ |- |- |- |- |- |T |B |S |
+ +--+--+--+--+--+--+--+--+
+
+ Fabrice's 99/2:
+ T: Text color (1=white)
+ B: border color (1=white)
+ S: Background color (1=text color, 0=inverted)
+
+ text border back
+ 0 0 0 b b w
+ 0 0 1 b b b
+ 0 1 0 b w w
+ 0 1 1 b w b
+ 1 0 0 w b b
+ 1 0 1 w b w
+ 1 1 0 w w b
+ 1 1 1 w w w
+
+ Counters:
+ dotline 9 bit
+ after reaching 261, resets to 0
+
+ 224..236: Top blanking
+ 237..261: Top border
+ 000..191: Display
+ 192..217: Bottom border
+ 218..220: Bottom blanking
+ 221..223: Vert sync
+
+ dotcolumn 9 bit
+ increments every clock tick until reaching 341, resets to 0, and incr dotline
+
+ 305..328: Left blanking
+ 329..341: Left border
+ 000..255: Display
+ 256..270: Right border
+ 271..278: Right blanking
+ 279..304: Hor sync
+ ------------------------------
+
+ Later versions define a "bitmap mode" [2]
+
+ EF00: Control byte
+ +--+--+--+--+--+--+--+--+
+ |- |- |- |- |- |C |B |M |
+ +--+--+--+--+--+--+--+--+
+
+ C: character color (1=white)
+ B: border color (1=white)
+ M: bitmap mode (1=bitmap)
+
+ [1] Ground Squirrel Personal Computer Product Specification
+ [2] VDC Controller CF40052
+*/
+
+#include "emu.h"
+#include "992board.h"
+
+DEFINE_DEVICE_TYPE_NS(VIDEO99224, bus::ti99::internal, video992_24_device, "video992_24", "TI-99/2 CRT Controller 24K version")
+DEFINE_DEVICE_TYPE_NS(VIDEO99232, bus::ti99::internal, video992_32_device, "video992_32", "TI-99/2 CRT Controller 32K version")
+
+namespace bus { namespace ti99 { namespace internal {
+
+video992_device::video992_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock),
+ device_video_interface(mconfig, *this),
+ m_mem_read_cb(*this),
+ m_hold_cb(*this),
+ m_int_cb(*this),
+ m_holdcpu(false),
+ m_videna(true)
+{
+}
+
+video992_24_device::video992_24_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock )
+ : video992_device(mconfig, VIDEO99224, tag, owner, clock)
+{
+ m_beol = 0x70;
+}
+
+video992_32_device::video992_32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock )
+ : video992_device(mconfig, VIDEO99232, tag, owner, clock)
+{
+ m_beol = 0x7f;
+}
+
+std::string video992_device::tts(attotime t)
+{
+ char buf[256];
+ const char *sign = "";
+ if(t.seconds() < 0) {
+ t = attotime::zero-t;
+ sign = "-";
+ }
+ int nsec = t.attoseconds() / ATTOSECONDS_PER_NANOSECOND;
+ sprintf(buf, "%s%04d.%03d,%03d,%03d", sign, int(t.seconds()), nsec/1000000, (nsec/1000)%1000, nsec % 1000);
+ return buf;
+}
+
+
+void video992_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int raw_vpos = screen().vpos();
+
+ if (id == HOLD_TIME)
+ {
+ // logerror("release time: %s, diff: %s\n", tts(machine().time()), tts(machine().time()-m_hold_time));
+ // We're holding the CPU; release it until the next start
+ m_hold_cb(CLEAR_LINE);
+ m_free_timer->adjust(screen().time_until_pos((raw_vpos+1) % screen().height(), HORZ_DISPLAY_START));
+ return;
+ }
+
+ // logerror("hold time: %s\n", tts(machine().time()));
+ if (m_videna)
+ {
+ // Hold the CPU
+ m_hold_time = machine().time();
+ m_hold_cb(ASSERT_LINE);
+ }
+
+ int vpos = raw_vpos * m_vertical_size / screen().height();
+ uint32_t *p = &m_tmpbmp.pix32(vpos);
+ bool endofline = false;
+
+ int linelength = 0;
+
+ // logerror("draw line %d\n", vpos);
+ // Get control byte
+ uint8_t control = m_mem_read_cb(0xef00);
+ bool text_white = ((control & 0x04)!=0);
+ bool border_white = ((control & 0x02)!=0);
+ bool background_white = ((control & 0x01)!=0)? text_white : !text_white;
+
+ int y = vpos - m_top_border;
+ if (y < 0 || y >= 192)
+ {
+ // Draw border colour
+ for (int i = 0; i < TOTAL_HORZ; i++)
+ p[i] = border_white? rgb_t::white() : rgb_t::black();
+
+ // vblank is set at the last cycle of the first inactive line
+ // not confirmed by the specs, just doing like 9928A.
+ if ( y == 193 )
+ {
+ m_int_cb( ASSERT_LINE );
+ m_int_cb( CLEAR_LINE );
+ }
+ }
+ else
+ {
+ // Draw regular line
+ // Left border
+ for (int i = 0; i < HORZ_DISPLAY_START; i++)
+ p[i] = border_white? rgb_t::white() : rgb_t::black();
+
+ int addr = ((y << 2) & 0x3e0) | 0xec00;
+
+ // Active display
+ for (int x = HORZ_DISPLAY_START; x<HORZ_DISPLAY_START+256; x+=8)
+ {
+ uint8_t charcode = 0;
+ uint8_t pattern = 0;
+ if (!endofline && m_videna)
+ {
+ // Get character code at the location
+ charcode = m_mem_read_cb(addr) & 0x7f;
+
+ // Is it the BEOL (blank end-of-line)?
+ if (charcode >= m_beol)
+ endofline = true;
+ }
+ if (!endofline && m_videna)
+ {
+ // Get the pattern
+ int addrp = 0x1c00 | (charcode << 3) | (y%8);
+ pattern = m_mem_read_cb(addrp);
+ linelength++;
+ }
+ for (int i = 0; i < 8; i++)
+ {
+ if ((pattern & 0x80)!=0)
+ p[x+i] = text_white? rgb_t::white() : rgb_t::black();
+ else
+ p[x+i] = background_white? rgb_t::white() : rgb_t::black();
+
+ pattern <<= 1;
+ }
+ addr++;
+ }
+
+ // Right border
+ for (int i = HORZ_DISPLAY_START + 256; i < TOTAL_HORZ; i++)
+ p[i] = border_white? rgb_t::white() : rgb_t::black();
+ }
+
+ // +1 for the minimum hold time
+ // logerror("line length: %d\n", linelength);
+ m_hold_timer->adjust(screen().time_until_pos(raw_vpos, HORZ_DISPLAY_START + linelength*8 + 1));
+}
+
+
+uint32_t video992_device::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
+{
+ copybitmap( bitmap, m_tmpbmp, 0, 0, 0, 0, cliprect );
+ return 0;
+}
+
+/*
+ VIDENA pin, negative logic
+*/
+WRITE_LINE_MEMBER( video992_device::videna )
+{
+ m_videna = (state==CLEAR_LINE);
+}
+
+void video992_device::device_start()
+{
+ m_top_border = VERT_DISPLAY_START_NTSC;
+ m_vertical_size = TOTAL_VERT_NTSC;
+ m_tmpbmp.allocate(TOTAL_HORZ, TOTAL_VERT_NTSC);
+
+ m_hold_timer = timer_alloc(HOLD_TIME);
+ m_free_timer = timer_alloc(FREE_TIME);
+
+ m_border_color = rgb_t::black();
+ m_background_color = rgb_t::white();
+ m_text_color = rgb_t::black();
+
+ m_mem_read_cb.resolve();
+ m_hold_cb.resolve();
+ m_int_cb.resolve();
+}
+
+void video992_device::device_reset()
+{
+ m_free_timer->adjust(screen().time_until_pos(0, HORZ_DISPLAY_START));
+}
+
+} } }
+
+
diff --git a/src/devices/bus/ti99/internal/992board.h b/src/devices/bus/ti99/internal/992board.h
new file mode 100644
index 00000000000..1d8f581f879
--- /dev/null
+++ b/src/devices/bus/ti99/internal/992board.h
@@ -0,0 +1,115 @@
+// license:LGPL-2.1+
+// copyright-holders:Michael Zapf
+/****************************************************************************
+
+ TI-99/2 main board logic
+
+ This component implements the custom video controller and interface chip
+ from the TI-99/2 console.
+
+ See 992board.cpp for documentation
+
+ Michael Zapf
+
+*****************************************************************************/
+
+#ifndef MAME_BUS_TI99_INTERNAL_992BOARD_H
+#define MAME_BUS_TI99_INTERNAL_992BOARD_H
+
+#pragma once
+
+#include "screen.h"
+
+namespace bus { namespace ti99 { namespace internal {
+
+class video992_device : public device_t,
+ public device_video_interface
+{
+public:
+ // Complete line (31.848 us)
+ static constexpr unsigned TOTAL_HORZ = 342;
+ static constexpr unsigned TOTAL_VERT_NTSC = 262;
+
+ template <class Object> devcb_base &set_readmem_callback(Object &&cb) { return m_mem_read_cb.set_callback(std::forward<Object>(cb)); }
+ template <class Object> devcb_base &set_hold_callback(Object &&cb) { return m_hold_cb.set_callback(std::forward<Object>(cb)); }
+ template <class Object> devcb_base &set_int_callback(Object &&cb) { return m_int_cb.set_callback(std::forward<Object>(cb)); }
+
+ // Delay(2) + ColorBurst(14) + Delay(8) + LeftBorder(13)
+ static constexpr unsigned HORZ_DISPLAY_START = 2 + 14 + 8 + 13;
+ // RightBorder(15) + Delay(8) + HorizSync(26)
+ static constexpr unsigned VERT_DISPLAY_START_NTSC = 13 + 27;
+
+ // update the screen
+ uint32_t screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect );
+
+ // Video enable
+ DECLARE_WRITE_LINE_MEMBER( videna );
+
+protected:
+ video992_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+ int m_beol;
+
+private:
+ static const device_timer_id HOLD_TIME = 0;
+ static const device_timer_id FREE_TIME = 1;
+
+ void device_start() override;
+ void device_reset() override;
+ void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+ std::string tts(attotime t);
+ devcb_read8 m_mem_read_cb; // Callback to read memory
+ devcb_write_line m_hold_cb;
+ devcb_write_line m_int_cb;
+
+ bitmap_rgb32 m_tmpbmp;
+ emu_timer *m_free_timer;
+ emu_timer *m_hold_timer;
+
+ int m_top_border;
+ int m_vertical_size;
+
+ rgb_t m_border_color;
+ rgb_t m_background_color;
+ rgb_t m_text_color;
+
+ bool m_holdcpu;
+ bool m_videna;
+
+ attotime m_hold_time;
+};
+
+/* Variant for TI-99/2 24K */
+class video992_24_device : public video992_device
+{
+public:
+ video992_24_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+/* Variant for TI-99/2 32K */
+class video992_32_device : public video992_device
+{
+public:
+ video992_32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+} } } // end namespace bus::ti99::internal
+
+#define MCFG_VIDEO992_SCREEN_ADD(_screen_tag) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_SCREEN_ADD( _screen_tag, RASTER ) \
+ MCFG_SCREEN_RAW_PARAMS( XTAL(10'738'635) / 2, bus::ti99::internal::video992_device::TOTAL_HORZ, bus::ti99::internal::video992_device::HORZ_DISPLAY_START-12, bus::ti99::internal::video992_device::HORZ_DISPLAY_START + 256 + 12, \
+ bus::ti99::internal::video992_device::TOTAL_VERT_NTSC, bus::ti99::internal::video992_device::VERT_DISPLAY_START_NTSC - 12, bus::ti99::internal::video992_device::VERT_DISPLAY_START_NTSC + 192 + 12 )
+
+#define MCFG_VIDEO992_MEM_ACCESS_CB(_devcb) \
+ devcb = &downcast<bus::ti99::internal::video992_device &>(*device).set_readmem_callback(DEVCB_##_devcb);
+
+#define MCFG_VIDEO992_HOLD_CB(_devcb) \
+ devcb = &downcast<bus::ti99::internal::video992_device &>(*device).set_hold_callback(DEVCB_##_devcb);
+
+#define MCFG_VIDEO992_INT_CB(_devcb) \
+ devcb = &downcast<bus::ti99::internal::video992_device &>(*device).set_int_callback(DEVCB_##_devcb);
+
+DECLARE_DEVICE_TYPE_NS(VIDEO99224, bus::ti99::internal, video992_24_device)
+DECLARE_DEVICE_TYPE_NS(VIDEO99232, bus::ti99::internal, video992_32_device)
+
+#endif // MAME_BUS_TI99_INTERNAL_992BOARD_H
diff --git a/src/mame/drivers/ti99_2.cpp b/src/mame/drivers/ti99_2.cpp
index e252d6f2f1e..7fd1d620d6b 100644
--- a/src/mame/drivers/ti99_2.cpp
+++ b/src/mame/drivers/ti99_2.cpp
@@ -1,266 +1,330 @@
// license:GPL-2.0+
-// copyright-holders:Raphael Nabet
+// copyright-holders: Michael Zapf
/*
- Experimental ti99/2 driver
-
-TODO :
- * find a TI99/2 ROM dump (some TI99/2 ARE in private hands)
- * test the driver !
- * understand the "viden" pin
- * implement cassette
- * implement Hex-Bus
-
- Raphael Nabet (who really has too much time to waste), december 1999, 2000
-*/
-
-/*
- TI99/2 facts :
-
-References :
-* TI99/2 main logic board schematics, 83/03/28-30 (on ftp.whtech.com, or just ask me)
- (Thanks to Charles Good for posting this)
-
-general :
-* prototypes in 1983
-* uses a 10.7MHz TMS9995 CPU, with the following features :
- - 8-bit data bus
- - 256 bytes 16-bit RAM (0xff00-0xff0b & 0xfffc-0xffff)
- - only available int lines are INT4 (used by vdp), INT1*, and NMI* (both free for extension)
- - on-chip decrementer (0xfffa-0xfffb)
- - Unlike tms9900, CRU address range is full 0x0000-0xFFFE (A0 is not used as address).
- This is possible because tms9995 uses d0-d2 instead of the address MSBits to support external
- opcodes.
- - quite more efficient than tms9900, and a few additional instructions and features
-* 24 or 32kb ROM (16kb plain (1kb of which used by vdp), 16kb split into 2 8kb pages)
-* 4kb 8-bit RAM, 256 bytes 16-bit RAM
-* custom vdp shares CPU RAM/ROM. The display is quite alike to tms9928 graphics mode, except
- that colors are a static B&W, and no sprite is displayed. The config (particularily the
- table base addresses) cannot be changed. Since TI located the pattern generator table in
- ROM, we cannot even redefine the char patterns (unless you insert a custom cartidge which
- overrides the system ROMs). VBL int triggers int4 on tms9995.
-* CRU is handled by one single custom chip, so the schematics don't show many details :-( .
-* I/O :
- - 48-key keyboard. Same as TI99/4a, without alpha lock, and with an additional break key.
- Note that the hardware can make the difference between the two shift keys.
- - cassette I/O (one unit)
- - ALC bus (must be another name for Hex-Bus)
-* 60-pin expansion/cartidge port on the back
-
-memory map :
-* 0x0000-0x4000 : system ROM (0x1C00-0x2000 (?) : char ROM used by vdp)
-* 0x4000-0x6000 : system ROM, mapped to either of two distinct 8kb pages according to the S0
- bit from the keyboard interface (!), which is used to select the first key row.
- [only on second-generation TI99/2 protos, first generation protos only had 24kb of ROM]
-* 0x6000-0xE000 : free for expansion
-* 0xE000-0xF000 : 8-bit "system" RAM (0xEC00-0xEF00 used by vdp)
-* 0xF000-0xF0FB : 16-bit processor RAM (on tms9995)
-* 0xF0FC-0xFFF9 : free for expansion
-* 0xFFFA-0xFFFB : tms9995 internal decrementer
-* 0xFFFC-0xFFFF : 16-bit processor RAM (provides the NMI vector)
-
-CRU map :
-* 0x0000-0x1EFC : reserved
-* 0x1EE0-0x1EFE : tms9995 flag register
-* 0x1F00-0x1FD8 : reserved
-* 0x1FDA : tms9995 MID flag
-* 0x1FDC-0x1FFF : reserved
-* 0x2000-0xE000 : unaffected
-* 0xE400-0xE40E : keyboard I/O (8 bits input, either 3 or 6 bit output)
-* 0xE80C : cassette I/O
-* 0xE80A : ALC BAV
-* 0xE808 : ALC HSK
-* 0xE800-0xE808 : ALC data (I/O)
-* 0xE80E : video enable (probably input - seems to come from the VDP, and is present on the
- expansion connector)
-* 0xF000-0xFFFE : reserved
-Note that only A15-A11 and A3-A1 (A15 = MSB, A0 = LSB) are decoded in the console, so the keyboard
-is actually mapped to 0xE000-0xE7FE, and other I/O bits to 0xE800-0xEFFE.
-Also, ti99/2 does not support external instructions better than ti99/4(a). This is crazy, it
-would just have taken three extra tracks on the main board and a OR gate in an ASIC.
+ TI-99/2 driver
+ --------------
+
+ Drivers: ti99_224 - 24 KiB version
+ ti99_232 - 32 KiB version
+
+ Codenamed "Ground Squirrel", the TI-99/2 was designed to be an extremely
+ low-cost, downstripped version of the TI-99/4A, competing with systems
+ like the ZX81 or the Times/Sinclair 1000. The targeted price was below $100.
+
+ The 99/2 was equipped with a TMS9995, the same CPU as used in the envisioned
+ flag ship 99/8 and later in the Geneve 9640. In the 99/2 it is clocked
+ at 5.35 MHz. Also, the CPU has on-chip RAM, unlike the version in the 99/8.
+
+ At many places, the tight price constraint shows up.
+ - 2 or 4 KiB of RAM
+ - Video memory is part of the CPU RAM
+ - Video controller is black/white only and has a fixed character set, no sprites
+ - No sound
+ - No GROMs (as the significant TI technology circuit)
+ - No P-Box connection
+
+ Main board
+ ----------
+ 1 CPU @ 5.35 MHz
+ 2 RAM circuits
+ 3 or 4 EPROMs
+ 1 TAL-004 custom gate array as the video controller
+ 1 TAL-004 custom gate array as the I/O controller
+ and six selector or latch circuits
+
+ Connectors
+ ----------
+ - Built-in RF modulator, switch allows for setting the channel to VHF 3 or 4
+ - Cassette connector (compatible to 99/4A), one unit only
+ - Hexbus connector
+ - System expansion slot for cartridges (ROM or RAM), 60 pins, on the back
+
+ Keyboard: 48 keys, no Alpha Lock, two separate Shift keys, additional break key.
+
+ Description
+ -----------
+ Prototypes were developed in 1983. Despite a late marketing campaign,
+ including well-known faces, no devices were ever sold. A few of them
+ survived in the hands of the engineers, and were later sold to private
+ users.
+
+ The Ground Squirrel underwent several design changes. In the initial
+ design, only 2 KiB RAM were planned, and the included ROM was 24 KiB.
+ Later, the 2 KiB were obviously expanded to 4 KiB, while the ROM remained
+ the same in size. This can be proved here, since the console crashes with
+ less than 4 KiB by an unmapped memory access.
+
+ The next version showed an additional 8 KiB of ROM. Possibly in order
+ to avoid taking away too much address space, two EPROMs are mapped to the
+ same address space block, selectable by a CRU bit. ROM may be added as
+ cartridges to be plugged into the expansion slot, and the same is true
+ for RAM. Actually, since the complete bus is available on that connector,
+ almost any kind of peripheral device could be added. Too bad, none were
+ developed.
+
+ However, the Hexbus seemed to have matured in the meantime, which became
+ the standard peripheral bus system for the TI-99/8, and for smaller
+ systems like the CC-40 and the TI-74. The TI-99/2 also offers a Hexbus
+ interface so that any kind of Hexbus device can be connected, like, for
+ example, the HX5102 floppy drive, a Wafertape drive, or the RS232 serial
+ interface.
+
+ The address space layout looks like this:
+
+ 0000 - 3FFF: ROM, unbanked
+ 4000 - 5FFF: ROM, banked for 32K version
+ 6000 - DFFF: ROM or RAM; ROM growing upwards, RAM growing downwards
+ E000 - EFFF: 4K RAM
+ EC00 - EEFF: Area used by video controller (24 rows, 32 columns)
+ EF00 : Control byte for colors (black/white) for backdrop/border
+ EF01 - EFFF: RAM
+ F000 - F0FB: CPU-internal RAM
+ F0FC - FFF9: empty
+ FFFA - FFFF: CPU-internal decrementer and NMI vector
+
+ RAM expansions may be offered via the cartridge port, but they must be
+ contiguous with the built-in RAM, which means that it must grow
+ downwards from E000. The space from F0FC up to FFF9 may also be covered
+ by expansion RAM.
+
+ From the other side, ROM or other memory-mapped devices may occupy
+ address space, growing up from 6000.
+
+ One peculiar detail is the memory-mapped video RAM. The video controller
+ shares an area of RAM with the CPU. To avoid congestion, the video
+ controller must HOLD the CPU while it accesses the video RAM area.
+
+ This decreases processing speed of the CPU, of course. For that reason,
+ a special character may be placed in every row after which the row will
+ be filled with blank pixels, and the CPU will be released early. This
+ means that with a screen full of characters, the processing speed is
+ definitely slower than with a clear screen.
+
+ To be able to temporarily get full CPU power, there is a pin VIDENA* at
+ the video controller which causes the screen to be blank when asserted,
+ and to be restored as before when cleared. This is used during cassette
+ transfer.
+
+ Technical details
+ -----------------
+ - HOLD is asserted in every scanline when characters are drawn that are
+ not the "Blank End of line" (BEOL). Once encountered, the remaining
+ scanline remains blank.
+ - When a frame has been completed, the INT4 interrupt of the 9995 is
+ triggered as a vblank interrupt.
+ - All CRU operations are handled by the second gate array. Unfortunately,
+ there is no known documentation about this circuit.
+ - The two banks of the last 16 KiB of ROM are selected with the same
+ line that is used for selecting keyboard row 0. This should mean that
+ you cannot read the keyboard from the second ROM bank.
+
+ I/O map (CRU map)
+ -----------------
+ 0000 - 1DFE: unmapped
+ 1E00 - 1EFE: TMS9995-internal CRU addresses
+ 1F00 - 1FD8: unmapped
+ 1FDA: TMS9995 MID flag
+ 1FDC - 1FFF: unmapped
+ 2000 - DFFE: unmapped
+ E000 - E00E: Read: Keyboard column input
+ E000 - E00A: Write: Keyboard row selection
+ E00C: Write: unmapped
+ E00E: Write: Video enable (VIDENA)
+ E010 - E7FE: Mirrors of the above
+ E800 - E80C: Hexbus
+ E800 - E806: Data lines
+ E808: HSK line
+ E80A: BAV line
+ E80C: Inhibit (Write only)
+ E80E: Cassette
+
+ ROM dumps
+ ---------
+ Hard to believe, but we have access to two people with one 24K and
+ one 32K version, and we were able to dumps the ROMs correctly.
+
+ The ROMs contain a stripped-down version of TI BASIC, but without
+ the specific graphics subprograms. Programs written on the 99/2 should
+ run on the 99/4A, but the opposite is not true.
+
+ TODO
+ ----
+ * Implement cassette
+ * Add Hexbus
+
+ Original implementation: Raphael Nabet; December 1999, 2000
+
+ Michael Zapf, May 2018
+
+ References :
+ [1] TI-99/2 main logic board schematics, 83/03/28-30
+ [2] BYTE magazine, June 1983, pp. 128-134
*/
#include "emu.h"
#include "machine/tms9901.h"
#include "cpu/tms9900/tms9995.h"
-#include "screen.h"
+#include "bus/ti99/internal/992board.h"
+#include "machine/ram.h"
+
+#define TI_VDC_TAG "vdc"
+#define TI_SCREEN_TAG "screen"
+#define TI992_ROM "rom_region"
+#define TI992_RAM_TAG "ram_region"
+
+#define LOG_WARN (1U<<1) // Warnings
+#define LOG_HEXBUS (1U<<2) // Hexbus operation
+#define LOG_KEYBOARD (1U<<3) // Keyboard operation
+#define LOG_SIGNALS (1U<<4) // Signals like HOLD/HOLDA
+#define LOG_CRU (1U<<5) // CRU activities
+#define LOG_BANK (1U<<6) // Change ROM banks
+
+// Minimum log should be config and warnings
+#define VERBOSE ( LOG_GENERAL | LOG_WARN )
+
+#include "logmacro.h"
class ti99_2_state : public driver_device
{
public:
ti99_2_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
- m_videoram(*this, "videoram"),
- m_ROM_paged(0),
- m_irq_state(0),
- m_keyRow(0),
m_maincpu(*this, "maincpu"),
- m_gfxdecode(*this, "gfxdecode"),
- m_palette(*this, "palette") { }
+ m_videoctrl(*this, "vdc"),
+ m_ram(*this, TI992_RAM_TAG),
+ m_have_banked_ROM(true),
+ m_otherbank(false),
+ m_keyRow(0),
+ m_rom(nullptr),
+ m_ram_start(0xf000),
+ m_first_ram_page(0)
+ { }
+
+
+ DECLARE_MACHINE_START( ti99_224 );
+ DECLARE_MACHINE_START( ti99_232 );
+ DECLARE_MACHINE_RESET( ti99_2 );
+
+ DECLARE_WRITE8_MEMBER(intflag_write);
+ DECLARE_WRITE8_MEMBER(write_kbd);
+ DECLARE_WRITE8_MEMBER(write_misc_cru);
+
+ DECLARE_READ8_MEMBER(read_kbd);
+ DECLARE_READ8_MEMBER(read_misc_cru);
+
+ DECLARE_READ8_MEMBER(mem_read);
+ DECLARE_WRITE8_MEMBER(mem_write);
+
+ DECLARE_WRITE_LINE_MEMBER(hold);
+ DECLARE_WRITE_LINE_MEMBER(holda);
+ DECLARE_WRITE_LINE_MEMBER(interrupt);
+
+ void crumap(address_map &map);
+ void memmap(address_map &map);
- required_shared_ptr<uint8_t> m_videoram;
- int m_ROM_paged;
- int m_irq_state;
- int m_keyRow;
- DECLARE_WRITE8_MEMBER(ti99_2_write_kbd);
- DECLARE_WRITE8_MEMBER(ti99_2_write_misc_cru);
- DECLARE_READ8_MEMBER(ti99_2_read_kbd);
- DECLARE_READ8_MEMBER(ti99_2_read_misc_cru);
- DECLARE_DRIVER_INIT(ti99_2_24);
- DECLARE_DRIVER_INIT(ti99_2_32);
- virtual void machine_reset() override;
- uint32_t screen_update_ti99_2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
- INTERRUPT_GEN_MEMBER(ti99_2_vblank_interrupt);
- required_device<cpu_device> m_maincpu;
- required_device<gfxdecode_device> m_gfxdecode;
- required_device<palette_device> m_palette;
void ti99_2(machine_config &config);
- void ti99_2_io(address_map &map);
- void ti99_2_memmap(address_map &map);
-};
+ void ti99_224(machine_config &config);
+ void ti99_232(machine_config &config);
+
+private:
+ required_device<tms9995_device> m_maincpu;
+ required_device<bus::ti99::internal::video992_device> m_videoctrl;
+ required_device<ram_device> m_ram;
+ bool m_have_banked_ROM;
+ bool m_otherbank;
+ int m_keyRow;
+ uint8_t* m_rom;
+ int m_ram_start;
+ int m_first_ram_page;
+};
-DRIVER_INIT_MEMBER(ti99_2_state,ti99_2_24)
+MACHINE_START_MEMBER(ti99_2_state, ti99_224)
{
- /* no ROM paging */
- m_ROM_paged = 0;
+ m_rom = memregion(TI992_ROM)->base();
+ m_ram_start = 0xf000 - m_ram->default_size();
+ m_first_ram_page = m_ram_start >> 12;
+ m_have_banked_ROM = false;
}
-DRIVER_INIT_MEMBER(ti99_2_state,ti99_2_32)
+MACHINE_START_MEMBER(ti99_2_state, ti99_232)
{
- /* ROM paging enabled */
- m_ROM_paged = 1;
+ m_rom = memregion(TI992_ROM)->base();
+ m_ram_start = 0xf000 - m_ram->default_size();
+ m_first_ram_page = m_ram_start >> 12;
+ m_have_banked_ROM = true;
}
-#define TI99_2_32_ROMPAGE0 (memregion("maincpu")->base()+0x4000)
-#define TI99_2_32_ROMPAGE1 (memregion("maincpu")->base()+0x10000)
-
-void ti99_2_state::machine_reset()
+MACHINE_RESET_MEMBER(ti99_2_state, ti99_2)
{
- m_irq_state = ASSERT_LINE;
- if (! m_ROM_paged)
- membank("bank1")->set_base(memregion("maincpu")->base()+0x4000);
- else
- membank("bank1")->set_base((memregion("maincpu")->base()+0x4000));
+ m_otherbank = false;
// Configure CPU to insert 1 wait state for each external memory access
// by lowering the READY line on reset
// TODO: Check with specs
- tms9995_device* cpu = static_cast<tms9995_device*>(machine().device("maincpu"));
- cpu->ready_line(CLEAR_LINE);
- cpu->reset_line(ASSERT_LINE);
+ m_maincpu->ready_line(CLEAR_LINE);
+ m_maincpu->reset_line(ASSERT_LINE);
}
-INTERRUPT_GEN_MEMBER(ti99_2_state::ti99_2_vblank_interrupt)
-{
- m_maincpu->set_input_line(INT_9995_INT1, m_irq_state);
- m_irq_state = (m_irq_state == ASSERT_LINE) ? CLEAR_LINE : ASSERT_LINE;
-}
-
-
/*
- TI99/2 vdp emulation.
-
- Things could not be simpler.
- We display 24 rows and 32 columns of characters. Each 8*8 pixel character pattern is defined
- in a 128-entry table located in ROM. Character code for each screen position are stored
- sequentially in RAM. Colors are a fixed Black on White.
-
- There is an EOL character that blanks the end of the current line, so that
- the CPU can get more bus time.
+ Memory map - see description above
*/
-
-
-uint32_t ti99_2_state::screen_update_ti99_2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+void ti99_2_state::memmap(address_map &map)
{
- uint8_t *videoram = m_videoram;
- int i, sx, sy;
-
-
- sx = sy = 0;
-
- for (i = 0; i < 768; i++)
- {
- /* Is the char code masked or not ??? */
- m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, videoram[i] & 0x7F, 0,
- 0, 0, sx, sy);
-
- sx += 8;
- if (sx == 256)
- {
- sx = 0;
- sy += 8;
- }
- }
-
- return 0;
+ // 3 or 4 ROMs of 8 KiB. The 32K version has two ROMs mapped into 4000-5fff
+ // Part of the ROM is accessed by the video controller for the
+ // character definitions (1c00-1fff)
+ map(0x0000, 0xffff).rw(this, FUNC(ti99_2_state::mem_read), FUNC(ti99_2_state::mem_write));
}
-static const gfx_layout ti99_2_charlayout =
-{
- 8,8, /* 8 x 8 characters */
- 128, /* 128 characters */
- 1, /* 1 bits per pixel */
- { 0 }, /* no bitplanes; 1 bit per pixel */
- /* x offsets */
- { 0,1,2,3,4,5,6,7 },
- /* y offsets */
- { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, },
- 8*8 /* every char takes 8 bytes */
-};
-
-static GFXDECODE_START( ti99_2 )
- GFXDECODE_ENTRY( "maincpu", 0x1c00, ti99_2_charlayout, 0, 1 )
-GFXDECODE_END
-
-
/*
- Memory map - see description above
+ CRU map - see description above
+ Note that the CRU address space has only even numbers, and the
+ read addresses in the emulation gather 8 bits in one go, so the address
+ is the bit number times 16.
*/
-
-void ti99_2_state::ti99_2_memmap(address_map &map)
+void ti99_2_state::crumap(address_map &map)
{
- map(0x0000, 0x3fff).rom(); /* system ROM */
- map(0x4000, 0x5fff).bankr("bank1"); /* system ROM, banked on 32kb ROMs protos */
- map(0x6000, 0xdfff).noprw(); /* free for expansion */
- map(0xe000, 0xebff).ram(); /* system RAM */
- map(0xec00, 0xeeff).ram().share("videoram");
- map(0xef00, 0xefff).ram(); /* system RAM */
- map(0xf000, 0xffff).noprw(); /* free for expansion (and internal processor RAM) */
+ // Mirror of CPU-internal flags (1ee0-1efe). Don't read. Write is OK.
+ map(0x01ee, 0x01ef).nopr();
+ map(0x0f70, 0x0F7F).w(this, FUNC(ti99_2_state::intflag_write));
+
+ // CRU E000-E7fE: Keyboard
+ // Read: 0000 1110 0*** **** (mirror 007f)
+ // Write: 0111 00** **** *XXX (mirror 03f8)
+ map(0x0e00, 0x0e00).mirror(0x007f).r(this, FUNC(ti99_2_state::read_kbd));
+ map(0x7000, 0x7007).mirror(0x03f8).w(this, FUNC(ti99_2_state::write_kbd));
+
+ // CRU E800-EFFE: Hexbus and other functions
+ // Read: 0000 1110 1*** **** (mirror 007f)
+ // Write: 0111 01** **** *XXX (mirror 03f8)
+ map(0x0e80, 0x0e80).mirror(0x007f).r(this, FUNC(ti99_2_state::read_misc_cru));
+ map(0x7400, 0x7407).mirror(0x03f8).w(this, FUNC(ti99_2_state::write_misc_cru));
}
/*
- CRU map - see description above
+ Select the current keyboard row. Also, bit 0 is used to switch the
+ ROM bank. Suppose that means we won't be able to read the keyboard
+ when processing that ROM area.
*/
-
-/* current keyboard row */
-
-/* write the current keyboard row */
-WRITE8_MEMBER(ti99_2_state::ti99_2_write_kbd)
+WRITE8_MEMBER(ti99_2_state::write_kbd)
{
- offset &= 0x7; /* other address lines are not decoded */
+ // logerror("offset=%d, data=%d\n", offset, data);
+ if (data == 0) m_keyRow = offset;
- if (offset <= 2)
+ // Now, we handle ROM paging
+ if (m_have_banked_ROM && offset == 0)
{
- /* this implementation is just a guess */
- if (data)
- m_keyRow |= 1 << offset;
- else
- m_keyRow &= ~ (1 << offset);
- }
- /* now, we handle ROM paging */
- if (m_ROM_paged)
- { /* if we have paged ROMs, page according to S0 keyboard interface line */
- membank("bank1")->set_base((m_keyRow == 0) ? TI99_2_32_ROMPAGE1 : TI99_2_32_ROMPAGE0);
+ LOGMASKED(LOG_BANK, "set bank = %d\n", data);
+ m_otherbank = (data==1);
}
}
-WRITE8_MEMBER(ti99_2_state::ti99_2_write_misc_cru)
+WRITE8_MEMBER(ti99_2_state::write_misc_cru)
{
- offset &= 0x7; /* other address lines are not decoded */
-
switch (offset)
{
case 0:
@@ -268,45 +332,156 @@ WRITE8_MEMBER(ti99_2_state::ti99_2_write_misc_cru)
case 2:
case 3:
/* ALC I/O */
+ LOGMASKED(LOG_CRU, "Hexbus I/O (%d) = %d\n", offset, data);
break;
case 4:
/* ALC HSK */
+ LOGMASKED(LOG_CRU, "Hexbus HSK = %d\n", data);
break;
case 5:
/* ALC BAV */
+ LOGMASKED(LOG_CRU, "Hexbus BAV = %d\n", data);
break;
case 6:
/* cassette output */
+ LOGMASKED(LOG_CRU, "Cassette output = %d\n", data);
break;
case 7:
- /* video enable */
+ LOGMASKED(LOG_CRU, "VIDENA = %d\n", data);
+ m_videoctrl->videna(data);
break;
}
}
/* read keys in the current row */
-READ8_MEMBER(ti99_2_state::ti99_2_read_kbd)
+READ8_MEMBER(ti99_2_state::read_kbd)
{
static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7" };
-
return ioport(keynames[m_keyRow])->read();
}
-READ8_MEMBER(ti99_2_state::ti99_2_read_misc_cru)
+READ8_MEMBER(ti99_2_state::read_misc_cru)
{
return 0;
}
-void ti99_2_state::ti99_2_io(address_map &map)
+/*
+ These CRU addresses are actually inside the 9995 CPU, but they are
+ propagated to the outside world, so we can watch the changes.
+*/
+WRITE8_MEMBER(ti99_2_state::intflag_write)
{
- map(0x0E00, 0x0E7f).r(this, FUNC(ti99_2_state::ti99_2_read_kbd));
- map(0x0E80, 0x0Eff).r(this, FUNC(ti99_2_state::ti99_2_read_misc_cru));
- map(0x7000, 0x73ff).w(this, FUNC(ti99_2_state::ti99_2_write_kbd));
- map(0x7400, 0x77ff).w(this, FUNC(ti99_2_state::ti99_2_write_misc_cru));
+ int addr = 0x1ee0 | (offset<<1);
+ switch (addr)
+ {
+ case 0x1ee0:
+ LOGMASKED(LOG_CRU, "Setting 9995 decrementer to %s mode\n", (data==1)? "event" : "timer");
+ break;
+ case 0x1ee2:
+ LOGMASKED(LOG_CRU, "Setting 9995 decrementer to %s\n", (data==1)? "enabled" : "disabled");
+ break;
+ case 0x1ee4:
+ if (data==0) LOGMASKED(LOG_CRU, "Clear INT1 latch\n");
+ break;
+ case 0x1ee6:
+ if (data==0) LOGMASKED(LOG_CRU, "Clear INT3 latch\n");
+ break;
+ case 0x1ee8:
+ if (data==0) LOGMASKED(LOG_CRU, "Clear INT4 latch\n");
+ break;
+ case 0x1eea:
+ LOGMASKED(LOG_CRU, "Setting FLAG5 to %d\n", data);
+ break;
+
+ default:
+ LOGMASKED(LOG_CRU, "Writing internal flag %04x = %d\n", addr, data);
+ break;
+ }
}
+/*
+ Called by CPU and video controller.
+*/
+READ8_MEMBER(ti99_2_state::mem_read)
+{
+ int page = offset >> 12;
+
+ if (page>=0 && page<4)
+ {
+ // ROM, unbanked
+ return m_rom[offset];
+ }
+ if (page>=4 && page<6)
+ {
+ // ROM, banked on 32K version
+ if (m_otherbank) offset = (offset & 0x1fff) | 0x10000;
+ return m_rom[offset];
+ }
+
+ if ((page >= m_first_ram_page) && (page < 15))
+ {
+ return m_ram->pointer()[offset - m_ram_start];
+ }
+
+ LOGMASKED(LOG_WARN, "Unmapped read access at %04x\n", offset);
+ return 0;
+}
+
+WRITE8_MEMBER(ti99_2_state::mem_write)
+{
+ int page = offset >> 12;
+
+ if (page>=0 && page<4)
+ {
+ // ROM, unbanked
+ LOGMASKED(LOG_WARN, "Writing to ROM at %04x ignored (data=%02x)\n", offset, data);
+ return;
+ }
+
+ if (page>=4 && page<6)
+ {
+ LOGMASKED(LOG_WARN, "Writing to ROM at %04x (bank %d) ignored (data=%02x)\n", offset, m_otherbank? 1:0, data);
+ return;
+ }
+
+ if ((page >= m_first_ram_page) && (page < 15))
+ {
+ m_ram->pointer()[offset - m_ram_start] = data;
+ return;
+ }
-/* ti99/2 : 54-key keyboard */
+ LOGMASKED(LOG_WARN, "Unmapped write access at %04x\n", offset);
+}
+
+/*
+ Called by the VDC as a vblank interrupt
+*/
+WRITE_LINE_MEMBER(ti99_2_state::interrupt)
+{
+ LOGMASKED(LOG_SIGNALS, "Interrupt: %d\n", state);
+ m_maincpu->set_input_line(INT_9995_INT4, state);
+}
+
+/*
+ Called by the VDC to HOLD the CPU
+*/
+WRITE_LINE_MEMBER(ti99_2_state::hold)
+{
+ LOGMASKED(LOG_SIGNALS, "HOLD: %d\n", state);
+ m_maincpu->hold_line(state);
+}
+
+/*
+ Called by the CPU to ack the HOLD
+*/
+WRITE_LINE_MEMBER(ti99_2_state::holda)
+{
+ LOGMASKED(LOG_SIGNALS, "HOLDA: %d\n", state);
+}
+
+/*
+ 54-key keyboard
+*/
static INPUT_PORTS_START(ti99_2)
PORT_START("LINE0") /* col 0 */
@@ -377,51 +552,71 @@ static INPUT_PORTS_START(ti99_2)
INPUT_PORTS_END
-MACHINE_CONFIG_START(ti99_2_state::ti99_2)
- // basic machine hardware
- // TMS9995, standard variant
- // We have no lines connected yet
- MCFG_TMS99xx_ADD("maincpu", TMS9995, 10700000, ti99_2_memmap, ti99_2_io)
-
- MCFG_DEVICE_VBLANK_INT_DRIVER("screen", ti99_2_state, ti99_2_vblank_interrupt)
-
- /* video hardware */
- /*MCFG_TMS9928A( &tms9918_interface )*/
- MCFG_SCREEN_ADD_MONOCHROME("screen", RASTER, rgb_t::white())
- MCFG_SCREEN_REFRESH_RATE(60)
- MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
- MCFG_SCREEN_SIZE(256, 192)
- MCFG_SCREEN_VISIBLE_AREA(0, 256-1, 0, 192-1)
- MCFG_SCREEN_UPDATE_DRIVER(ti99_2_state, screen_update_ti99_2)
- MCFG_SCREEN_PALETTE("palette")
-
- MCFG_GFXDECODE_ADD("gfxdecode", "palette", ti99_2)
- MCFG_PALETTE_ADD_MONOCHROME_INVERTED("palette")
+MACHINE_CONFIG_START(ti99_2_state::ti99_224)
+ ti99_2(config);
+ // Video hardware
+ MCFG_DEVICE_ADD( TI_VDC_TAG, VIDEO99224, XTAL(10'738'635) )
+ MCFG_VIDEO992_MEM_ACCESS_CB( READ8( *this, ti99_2_state, mem_read ) )
+ MCFG_VIDEO992_HOLD_CB( WRITELINE( *this, ti99_2_state, hold ) )
+ MCFG_VIDEO992_INT_CB( WRITELINE( *this, ti99_2_state, interrupt ) )
+ MCFG_VIDEO992_SCREEN_ADD( TI_SCREEN_TAG )
+ MCFG_SCREEN_UPDATE_DEVICE( TI_VDC_TAG, bus::ti99::internal::video992_device, screen_update )
+
+ MCFG_MACHINE_START_OVERRIDE(ti99_2_state, ti99_224 )
MACHINE_CONFIG_END
+MACHINE_CONFIG_START(ti99_2_state::ti99_232)
+ ti99_2(config);
+ // Video hardware
+ MCFG_DEVICE_ADD( TI_VDC_TAG, VIDEO99232, XTAL(10'738'635) )
+ MCFG_VIDEO992_MEM_ACCESS_CB( READ8( *this, ti99_2_state, mem_read ) )
+ MCFG_VIDEO992_HOLD_CB( WRITELINE( *this, ti99_2_state, hold ) )
+ MCFG_VIDEO992_INT_CB( WRITELINE( *this, ti99_2_state, interrupt ) )
+ MCFG_VIDEO992_SCREEN_ADD( TI_SCREEN_TAG )
+ MCFG_SCREEN_UPDATE_DEVICE( TI_VDC_TAG, bus::ti99::internal::video992_device, screen_update )
+ MCFG_MACHINE_START_OVERRIDE(ti99_2_state, ti99_232 )
+
+MACHINE_CONFIG_END
+
+MACHINE_CONFIG_START(ti99_2_state::ti99_2)
+ // TMS9995, standard variant
+ // There is a divider by 2 for the clock rate
+ MCFG_TMS99xx_ADD("maincpu", TMS9995, XTAL(10'738'635) / 2, memmap, crumap)
+ MCFG_TMS9995_HOLDA_HANDLER( WRITELINE(*this, ti99_2_state, holda) )
+
+ // RAM 4 KiB
+ // Early documents indicate 2 KiB RAM, but this does not work with
+ // either ROM version, so we have to assume that the 2 KiB were never
+ // sufficient, not even in the initial design
+ MCFG_RAM_ADD(TI992_RAM_TAG)
+ MCFG_RAM_DEFAULT_SIZE("4096")
+ MCFG_RAM_DEFAULT_VALUE(0)
+
+ MCFG_MACHINE_RESET_OVERRIDE(ti99_2_state, ti99_2 )
+MACHINE_CONFIG_END
/*
ROM loading
*/
ROM_START(ti99_224)
- /*CPU memory space*/
- ROM_REGION(0x10000,"maincpu",0)
- ROM_LOAD("992rom.bin", 0x0000, 0x6000, NO_DUMP) /* system ROMs */
+ // The 24K version is an early design; the PCB does not have any socket names
+ ROM_REGION(0x6000, TI992_ROM ,0)
+ ROM_LOAD("rom0000.bin", 0x0000, 0x2000, CRC(c57436f1) SHA1(71d9048fed0317cfcc4cd966dcbc3bc163080cf9))
+ ROM_LOAD("rom2000.bin", 0x2000, 0x2000, CRC(be22c6c4) SHA1(931931d61732bacdab1da227c01b8045ca860f0b))
+ ROM_LOAD("rom4000.bin", 0x4000, 0x2000, CRC(926ca20e) SHA1(91624a16aa2c62c7ebc23128308709efdebddca3))
ROM_END
ROM_START(ti99_232)
- /*64kb CPU memory space + 8kb to read the extra ROM page*/
- ROM_REGION(0x12000,"maincpu",0)
- ROM_LOAD("992rom32.bin", 0x0000, 0x6000, NO_DUMP) /* system ROM - 32kb */
- ROM_CONTINUE(0x10000,0x2000)
+ ROM_REGION(0x12000, TI992_ROM, 0)
+ // The 32K version is a more elaborate design; the ROMs for addresses 0000-1fff
+ // and the second bank for 4000-5fff are stacked on the socket U2
+ ROM_LOAD("rom0000.u2a", 0x0000, 0x2000, CRC(01b94f06) SHA1(ef2e0c5f0492d7d024ebfe3fad29c2b57ea849e1))
+ ROM_LOAD("rom2000.u12", 0x2000, 0x2000, CRC(0a32f80a) SHA1(32ed98481998be295e637eaa2117337cfa4a7984))
+ ROM_LOAD("rom4000a.u3", 0x4000, 0x2000, CRC(10c11fab) SHA1(d43e0952538e66e2cedc307b71b65cb388cbe8e3))
+ ROM_LOAD("rom4000b.u2b", 0x10000, 0x2000, CRC(34dd52ed) SHA1(e01892b1b110d7d592a7e7f1f39f9f46ea0818db))
ROM_END
-/* one expansion/cartridge port on the back */
-/* one cassette unit port */
-/* Hex-bus disk controller: supports up to 4 floppy disk drives */
-/* None of these is supported (tape should be easy to emulate) */
-
// YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME FLAGS
-COMP( 1983, ti99_224, 0, 0, ti99_2, ti99_2, ti99_2_state, ti99_2_24, "Texas Instruments", "TI-99/2 BASIC Computer (24kb ROMs)" , MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
-COMP( 1983, ti99_232, ti99_224, 0, ti99_2, ti99_2, ti99_2_state, ti99_2_32, "Texas Instruments", "TI-99/2 BASIC Computer (32kb ROMs)" , MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
+COMP( 1983, ti99_224, 0, 0, ti99_224, ti99_2, ti99_2_state, 0, "Texas Instruments", "TI-99/2 BASIC Computer (24 KiB ROM)" , MACHINE_NO_SOUND_HW )
+COMP( 1983, ti99_232, ti99_224, 0, ti99_232, ti99_2, ti99_2_state, 0, "Texas Instruments", "TI-99/2 BASIC Computer (32 KiB ROM)" , MACHINE_NO_SOUND_HW )