mirror of
https://github.com/holub/mame
synced 2025-06-30 16:00:01 +03:00
ti99_2: New WORKING driver. New and verified ROM dumps, re-implementation, fixes.
This commit is contained in:
parent
9e5971241e
commit
7adec69c08
@ -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",
|
||||
|
298
src/devices/bus/ti99/internal/992board.cpp
Normal file
298
src/devices/bus/ti99/internal/992board.cpp
Normal file
@ -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));
|
||||
}
|
||||
|
||||
} } }
|
||||
|
||||
|
115
src/devices/bus/ti99/internal/992board.h
Normal file
115
src/devices/bus/ti99/internal/992board.h
Normal file
@ -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
|
@ -1,266 +1,330 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Raphael Nabet
|
||||
// copyright-holders: Michael Zapf
|
||||
/*
|
||||
Experimental ti99/2 driver
|
||||
TI-99/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
|
||||
Drivers: ti99_224 - 24 KiB version
|
||||
ti99_232 - 32 KiB version
|
||||
|
||||
Raphael Nabet (who really has too much time to waste), december 1999, 2000
|
||||
*/
|
||||
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.
|
||||
|
||||
/*
|
||||
TI99/2 facts :
|
||||
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.
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
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.
|
||||
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)
|
||||
/*
|
||||
Memory map - see description above
|
||||
*/
|
||||
|
||||
void ti99_2_state::memmap(address_map &map)
|
||||
{
|
||||
m_maincpu->set_input_line(INT_9995_INT1, m_irq_state);
|
||||
m_irq_state = (m_irq_state == ASSERT_LINE) ? CLEAR_LINE : ASSERT_LINE;
|
||||
// 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));
|
||||
}
|
||||
|
||||
/*
|
||||
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::crumap(address_map &map)
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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.
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
uint32_t ti99_2_state::screen_update_ti99_2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
|
||||
WRITE8_MEMBER(ti99_2_state::write_kbd)
|
||||
{
|
||||
uint8_t *videoram = m_videoram;
|
||||
int i, sx, sy;
|
||||
// logerror("offset=%d, data=%d\n", offset, data);
|
||||
if (data == 0) m_keyRow = offset;
|
||||
|
||||
|
||||
sx = sy = 0;
|
||||
|
||||
for (i = 0; i < 768; i++)
|
||||
// Now, we handle ROM paging
|
||||
if (m_have_banked_ROM && offset == 0)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
void ti99_2_state::ti99_2_memmap(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) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
CRU map - see description above
|
||||
*/
|
||||
|
||||
/* current keyboard row */
|
||||
|
||||
/* write the current keyboard row */
|
||||
WRITE8_MEMBER(ti99_2_state::ti99_2_write_kbd)
|
||||
{
|
||||
offset &= 0x7; /* other address lines are not decoded */
|
||||
|
||||
if (offset <= 2)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
/* ti99/2 : 54-key keyboard */
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
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_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")
|
||||
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 )
|
||||
|
Loading…
Reference in New Issue
Block a user