summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99/internal/992board.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ti99/internal/992board.cpp')
-rw-r--r--src/devices/bus/ti99/internal/992board.cpp364
1 files changed, 197 insertions, 167 deletions
diff --git a/src/devices/bus/ti99/internal/992board.cpp b/src/devices/bus/ti99/internal/992board.cpp
index 071526016f2..23274d08291 100644
--- a/src/devices/bus/ti99/internal/992board.cpp
+++ b/src/devices/bus/ti99/internal/992board.cpp
@@ -7,23 +7,29 @@
This component implements the custom video controller and interface chip
from the TI-99/2 console.
+ Also, we emulate the expansion port at the backside of the console; there
+ are no known expansions except for a RAM expansion that is mentioned
+ in the specifications.
+
May 2018
+ June 2020
***************************************************************************/
#include "emu.h"
+#include "992board.h"
-#define LOG_WARN (1U<<1) // Warnings
-#define LOG_CRU (1U<<2) // CRU logging
-#define LOG_CASSETTE (1U<<3) // Cassette logging
-#define LOG_HEXBUS (1U<<4) // Hexbus logging
-#define LOG_BANK (1U<<5) // Change ROM banks
-#define LOG_KEYBOARD (1U<<6) // Keyboard operation
+#define LOG_WARN (1U << 1) // Warnings
+#define LOG_CRU (1U << 2) // CRU logging
+#define LOG_CASSETTE (1U << 3) // Cassette logging
+#define LOG_HEXBUS (1U << 4) // Hexbus logging
+#define LOG_BANK (1U << 5) // Change ROM banks
+#define LOG_KEYBOARD (1U << 6) // Keyboard operation
+#define LOG_EXPRAM (1U << 7) // Expansion RAM
-#define VERBOSE ( LOG_WARN )
+#define VERBOSE (LOG_GENERAL | LOG_WARN)
#include "logmacro.h"
-#include "992board.h"
/*
Emulation of the CRT Gate Array of the TI-99/2
@@ -123,21 +129,23 @@
[2] VDC Controller CF40052
*/
-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")
-DEFINE_DEVICE_TYPE_NS(IO99224, bus::ti99::internal, io992_24_device, "io992_24", "TI-99/2 I/O controller 24K version")
-DEFINE_DEVICE_TYPE_NS(IO99232, bus::ti99::internal, io992_32_device, "io992_32", "TI-99/2 I/O controller 32K version")
+DEFINE_DEVICE_TYPE(VIDEO99224, bus::ti99::internal::video992_24_device, "video992_24", "TI-99/2 CRT Controller 24K version")
+DEFINE_DEVICE_TYPE(VIDEO99232, bus::ti99::internal::video992_32_device, "video992_32", "TI-99/2 CRT Controller 32K version")
+DEFINE_DEVICE_TYPE(IO99224, bus::ti99::internal::io992_24_device, "io992_24", "TI-99/2 I/O controller 24K version")
+DEFINE_DEVICE_TYPE(IO99232, bus::ti99::internal::io992_32_device, "io992_32", "TI-99/2 I/O controller 32K version")
+DEFINE_DEVICE_TYPE(TI992_EXPPORT, bus::ti99::internal::ti992_expport_device, "ti992_expport", "TI-99/2 Expansion Port")
+DEFINE_DEVICE_TYPE(TI992_RAM32K, bus::ti99::internal::ti992_expram_device, "ti992_ram32k", "TI-99/2 RAM Expansion 32K")
-namespace bus { namespace ti99 { namespace internal {
+namespace bus::ti99::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_videna(true)
+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, 0),
+ m_hold_cb(*this),
+ m_int_cb(*this),
+ m_videna(true)
{
}
@@ -153,34 +161,21 @@ video992_32_device::video992_32_device(const machine_config &mconfig, const char
m_beol = 0x7f;
}
-std::string video992_device::tts(attotime t)
+TIMER_CALLBACK_MEMBER(video992_device::hold_cpu)
{
- 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;
-}
+ int raw_vpos = screen().vpos();
+ LOG("release time: %s, diff: %s\n", machine().time().to_string(), (machine().time()-m_hold_time).to_string());
+ // 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));
+}
-void video992_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(video992_device::free_cpu)
{
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()));
+ LOG("hold time: %s\n", machine().time().to_string());
if (m_videna)
{
// Hold the CPU
@@ -194,31 +189,31 @@ void video992_device::device_timer(emu_timer &timer, device_timer_id id, int par
}
int vpos = raw_vpos * m_vertical_size / screen().height();
- uint32_t *p = &m_tmpbmp.pix32(vpos);
+ uint32_t *p = &m_tmpbmp.pix(vpos);
bool endofline = false;
int linelength = 0;
- // logerror("draw line %d\n", vpos);
+ LOG("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;
+ bool text_white = BIT(control, 2);
+ bool border_white = BIT(control, 1);
+ bool background_white = BIT(control, 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();
+ 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 )
+ if (y == 193)
{
- m_int_cb( ASSERT_LINE );
- m_int_cb( CLEAR_LINE );
+ m_int_cb(ASSERT_LINE);
+ m_int_cb(CLEAR_LINE);
}
}
else
@@ -226,12 +221,12 @@ void video992_device::device_timer(emu_timer &timer, device_timer_id id, int par
// Draw regular line
// Left border
for (int i = 0; i < HORZ_DISPLAY_START; i++)
- p[i] = border_white? rgb_t::white() : rgb_t::black();
+ 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)
+ for (int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+=8)
{
uint8_t charcode = 0;
uint8_t pattern = 0;
@@ -247,16 +242,16 @@ void video992_device::device_timer(emu_timer &timer, device_timer_id id, int par
if (!endofline && m_videna)
{
// Get the pattern
- int addrp = 0x1c00 | (charcode << 3) | (y%8);
+ int addrp = 0x1c00 | (charcode << 3) | (y & 7);
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();
+ if (BIT(pattern, 7))
+ p[x+i] = text_white ? rgb_t::white() : rgb_t::black();
else
- p[x+i] = background_white? rgb_t::white() : rgb_t::black();
+ p[x+i] = background_white ? rgb_t::white() : rgb_t::black();
pattern <<= 1;
}
@@ -265,27 +260,27 @@ void video992_device::device_timer(emu_timer &timer, device_timer_id id, int par
// Right border
for (int i = HORZ_DISPLAY_START + 256; i < TOTAL_HORZ; i++)
- p[i] = border_white? rgb_t::white() : rgb_t::black();
+ 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));
+ LOG("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 )
+uint32_t video992_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
- copybitmap( bitmap, m_tmpbmp, 0, 0, 0, 0, cliprect );
+ copybitmap(bitmap, m_tmpbmp, 0, 0, 0, 0, cliprect);
return 0;
}
/*
VIDENA pin, positive logic
*/
-WRITE_LINE_MEMBER( video992_device::videna )
+void video992_device::videna(int state)
{
- m_videna = (state==ASSERT_LINE);
+ m_videna = state;
}
void video992_device::device_start()
@@ -294,16 +289,12 @@ void video992_device::device_start()
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_hold_timer = timer_alloc(FUNC(video992_device::hold_cpu), this);
+ m_free_timer = timer_alloc(FUNC(video992_device::free_cpu), this);
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()
@@ -348,16 +339,13 @@ void video992_device::device_reset()
io992_device::io992_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: bus::hexbus::hexbus_chained_device(mconfig, type, tag, owner, clock),
- m_hexbus(*this, "^" TI_HEXBUS_TAG),
- m_cassette(*this, "^" TI_CASSETTE),
- m_videoctrl(*this, "^" TI992_VDC_TAG),
+ m_hexbus(*owner, TI992_HEXBUS_TAG),
+ m_cassette(*owner, TI992_CASSETTE),
+ m_videoctrl(*owner, TI992_VDC_TAG),
m_keyboard(*this, "LINE%u", 0U),
m_set_rom_bank(*this),
m_key_row(0),
- m_latch_out(0xd7),
- m_latch_in(0xd7),
- m_communication_disable(true),
- m_response_phase(false)
+ m_hsk_released(true)
{
}
@@ -452,59 +440,55 @@ void io992_device::device_start()
// Establish callback for inbound propagations
m_hexbus_outbound->set_chain_element(this);
-
- m_set_rom_bank.resolve();
}
uint8_t io992_device::cruread(offs_t offset)
{
- int address = offset << 1;
- uint8_t value = 0x7f; // All Hexbus lines high
+ offs_t address = offset << 1;
double inp = 0;
- int i;
- uint8_t bit = 1;
- switch (address & 0xf800)
+ // CRU E000-E7fE: Keyboard
+ // Read: 1110 0*** **** xxx0 (mirror 07f0)
+ if ((address & 0xf800) == 0xe000)
+ return BIT(m_keyboard[m_key_row]->read(), offset & 7);
+
+ // CRU E800-EFFE: Hexbus and other functions
+ // Read: 1110 1*** **** xxx0 (mirror 007f0)
+ switch (address & 0xf80e)
{
- case 0xe000:
- // CRU E000-E7fE: Keyboard
- // Read: 0000 1110 0*** **** (mirror 007f)
- value = m_keyboard[m_key_row]->read();
- break;
+ // Hexbus
case 0xe800:
- // CRU E800-EFFE: Hexbus and other functions
- // Read: 0000 1110 1*** **** (mirror 007f)
- for (i=0; i < 6; i++)
- {
- if ((m_latch_in & m_hexbval[i])==0) value &= ~bit;
- bit <<= 1;
- }
- // e80c (bit 6) seems to be a latch for the response phase
- if (!m_response_phase)
- value &= ~0x40;
+ case 0xe802:
+ case 0xe804:
+ case 0xe806:
+ return data_bit(offset & 3);
+ case 0xe808:
+ return (bus_hsk_level() == ASSERT_LINE) ? 0 : 1;
+ case 0xe80a:
+ return (bus_bav_level() == ASSERT_LINE) ? 0 : 1;
+
+ case 0xe80c:
+ // e80c (bit 6) seems to indicate that the HSK* line has been released
+ // and is now asserted again
+ return (m_hsk_released && (bus_hsk_level() == ASSERT_LINE)) ? 1 : 0;
+ case 0xe80e:
inp = m_cassette->input();
- if (inp > 0)
- value |= 0x80;
LOGMASKED(LOG_CASSETTE, "value=%f\n", inp);
- break;
+ return (inp > 0) ? 1 : 0;
+
default:
- LOGMASKED(LOG_CRU, "Unknown access to %04x\n", address);
+ LOGMASKED(LOG_CRU, "Invalid CRU access to %04x\n", address);
}
-
- LOGMASKED(LOG_CRU, "CRU %04x -> %02x\n", address, value);
-
- return BIT(value, offset & 7);
+ return 0;
}
void io992_device::cruwrite(offs_t offset, uint8_t data)
{
- int address = (offset << 1) & 0xf80e;
+ offs_t address = (offset << 1) & 0xf80e;
LOGMASKED(LOG_CRU, "CRU %04x <- %1x\n", address, data);
- uint8_t olddata = m_latch_out;
-
switch (address)
{
// Select the current keyboard row. Also, bit 0 is used to switch the
@@ -516,15 +500,15 @@ void io992_device::cruwrite(offs_t offset, uint8_t data)
if (m_have_banked_rom)
{
LOGMASKED(LOG_BANK, "set bank = %d\n", data);
- m_set_rom_bank(data==1);
+ m_set_rom_bank(data == 1);
}
- // no break
+ [[fallthrough]];
case 0xe002:
case 0xe004:
case 0xe006:
case 0xe008:
case 0xe00a:
- if (data == 0) m_key_row = offset&7;
+ if (data == 0) m_key_row = offset & 7;
break;
case 0xe00c:
LOGMASKED(LOG_WARN, "Unmapped CRU write to address e00c\n");
@@ -539,50 +523,26 @@ void io992_device::cruwrite(offs_t offset, uint8_t data)
case 0xe802: // ID1
case 0xe804: // ID2
case 0xe806: // ID3
- if (data != 0) m_latch_out |= m_hexbval[offset & 0x07];
- else m_latch_out &= ~m_hexbval[offset & 0x07];
- LOGMASKED(LOG_HEXBUS, "Hexbus latch out = %02x\n", m_latch_out);
+ set_data_latch(data, offset & 0x03);
break;
+
case 0xe80a: // BAV
- // Undocumented, but makes sense according to the ROM
- if (data==0)
- m_response_phase = false;
- // no break
- case 0xe808: // HSK
- if (data != 0) m_latch_out |= m_hexbval[offset & 0x07];
- else m_latch_out &= ~m_hexbval[offset & 0x07];
+ set_bav_line(data != 0 ? CLEAR_LINE : ASSERT_LINE);
+ break;
- if (m_latch_out != olddata)
- {
- LOGMASKED(LOG_HEXBUS, "%s %s\n", (address==0xe808)? "HSK*" : "BAV*", (data==0)? "assert" : "clear");
- if (m_communication_disable)
- {
- // This is not explicitly stated in the specs, but since they
- // also claim that communication is disabled on power-up,
- // we must turn it on here; the ROM fails to do it.
- LOGMASKED(LOG_HEXBUS, "Enabling Hexbus\n");
- m_communication_disable = false;
- }
- LOGMASKED(LOG_HEXBUS, "Writing to Hexbus: BAV*=%d, HSK*=%d, DATA=%01x\n", (m_latch_out&0x04)!=0, (m_latch_out&0x10)!=0, ((m_latch_out>>4)&0x0c)|(m_latch_out&0x03));
- hexbus_write(m_latch_out);
- // Check how the bus has changed. This depends on the states of all
- // connected peripherals
- m_latch_in = hexbus_read();
- }
+ case 0xe808: // HSK
+ set_hsk_line(data != 0 ? CLEAR_LINE : ASSERT_LINE);
+ m_hsk_released = (bus_hsk_level() == CLEAR_LINE);
break;
+
case 0xe80c:
- LOGMASKED(LOG_HEXBUS, "Hexbus inhibit = %d\n", data);
- if (data == 1)
- {
- m_latch_in = 0xd7;
- m_communication_disable = true;
- }
- else m_communication_disable = false;
+ set_communication_enabled(data == 0);
break;
+
case 0xe80e:
LOGMASKED(LOG_CRU, "Cassette output = %d\n", data);
// Tape output. See also ti99_4x.cpp.
- m_cassette->output((data==1)? +1 : -1);
+ m_cassette->output((data == 1) ? +1 : -1);
break;
}
}
@@ -601,29 +561,23 @@ void io992_device::hexbus_value_changed(uint8_t data)
{
// Only latch the incoming data when BAV* is asserted and the Hexbus
// is not inhibited
- bool bav_asserted = ((m_latch_out & bus::hexbus::HEXBUS_LINE_BAV)==0);
- if (!m_communication_disable && bav_asserted)
+ if (own_bav_level() == ASSERT_LINE)
{
- LOGMASKED(LOG_HEXBUS, "Hexbus changed and latched: %02x\n", data);
- m_latch_in = data;
-
- if ((data & bus::hexbus::HEXBUS_LINE_HSK)==0)
+ if (bus_hsk_level() == ASSERT_LINE)
{
- // If HSK* is lowered and we as the host have it up, this indicates
- // the response phase.
- if (!m_response_phase && (m_myvalue & bus::hexbus::HEXBUS_LINE_HSK)!=0)
- {
- LOGMASKED(LOG_HEXBUS, "Incoming response\n");
- m_response_phase = true;
- }
- // We cannot wait for the CPU explicitly latching HSK*
- // as designed, because this implies a true parallel execution
- LOGMASKED(LOG_HEXBUS, "Latching HSK*\n");
- m_myvalue &= ~bus::hexbus::HEXBUS_LINE_HSK;
+ // According to the Hexbus spec, the incoming HSK must be latched
+ // by hardware
+ LOGMASKED(LOG_HEXBUS, "Latching HSK*; got data %01x\n", (data >> 4) | (data & 3));
+ latch_hsk();
+ }
+ else
+ {
+ LOGMASKED(LOG_HEXBUS, "HSK* released\n");
+ m_hsk_released = true;
}
}
else
- LOGMASKED(LOG_HEXBUS, "Ignoring Hexbus change (to %02x), latch=%s, BAV*=%d\n", data, m_communication_disable? "inhibit":"enabled", bav_asserted? 0:1);
+ LOGMASKED(LOG_HEXBUS, "Ignoring Hexbus change (to %02x), BAV*=%d\n", data, (own_bav_level() == ASSERT_LINE) ? 0 : 1);
}
ioport_constructor io992_device::device_input_ports() const
@@ -631,6 +585,82 @@ ioport_constructor io992_device::device_input_ports() const
return INPUT_PORTS_NAME( keys992 );
}
-} } }
+/********************************************************************
+ Expansion port
+********************************************************************/
+
+ti992_expport_device::ti992_expport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, TI992_EXPPORT, tag, owner, clock),
+ device_single_card_slot_interface<ti992_expport_attached_device>(mconfig, *this),
+ m_connected(nullptr)
+{
+}
+
+void ti992_expport_device::readz(offs_t offset, uint8_t *value)
+{
+ if (m_connected != nullptr)
+ m_connected->readz(offset, value);
+}
+
+void ti992_expport_device::write(offs_t offset, uint8_t data)
+{
+ if (m_connected != nullptr)
+ m_connected->write(offset, data);
+}
+
+void ti992_expport_device::device_config_complete()
+{
+ m_connected = get_card_device();
+}
+
+/*
+ 32K Expansion cartridge
+ Maps at 6000 - DFFF
+ This is the only known expansion device
+*/
+ti992_expram_device::ti992_expram_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ ti992_expport_attached_device(mconfig, TI992_RAM32K, tag, owner, clock),
+ m_ram(*this, "ram32k")
+{
+}
+
+void ti992_expram_device::readz(offs_t offset, uint8_t *value)
+{
+ // 000 -> 100 100 -> 000
+ // 001 -> 101 101 -> 001
+ // 010 -> 110 110 -> 010
+ // 011 -> 011 111 -> 111
+ offs_t address = offset;
+ if ((offset & 0x6000) != 0x6000) address ^= 0x8000;
+ if ((address & 0x8000) == 0)
+ {
+ *value = m_ram->read(address);
+ LOGMASKED(LOG_EXPRAM, "expram %04x -> %02x\n", offset, *value);
+ }
+}
+void ti992_expram_device::write(offs_t offset, uint8_t value)
+{
+ offs_t address = offset;
+ if ((offset & 0x6000) != 0x6000) address ^= 0x8000;
+ if ((address & 0x8000) == 0)
+ {
+ m_ram->write(address, value);
+ LOGMASKED(LOG_EXPRAM, "expram %04x <- %02x\n", offset, value);
+ }
+}
+
+void ti992_expram_device::device_add_mconfig(machine_config &config)
+{
+ RAM(config, m_ram, 0);
+ m_ram->set_default_size("32k");
+ m_ram->set_default_value(0);
+}
+
+} // namespace bus::ti99::internal
+
+void ti992_expport_options(device_slot_interface &device)
+{
+ device.option_add("ram32k", TI992_RAM32K);
+}