summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99x/990_tap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ti99x/990_tap.c')
-rw-r--r--src/devices/bus/ti99x/990_tap.c1021
1 files changed, 1021 insertions, 0 deletions
diff --git a/src/devices/bus/ti99x/990_tap.c b/src/devices/bus/ti99x/990_tap.c
new file mode 100644
index 00000000000..20ca2333f7b
--- /dev/null
+++ b/src/devices/bus/ti99x/990_tap.c
@@ -0,0 +1,1021 @@
+// license:GPL-2.0+
+// copyright-holders:Raphael Nabet
+/*
+ 990_tap.c: emulation of a generic ti990 tape controller, for use with
+ TILINE-based TI990 systems (TI990/10, /12, /12LR, /10A, Business system 300
+ and 300A).
+
+ This core will emulate the common feature set found in every tape controller.
+ Most controllers support additional features, but are still compatible with
+ the basic feature set. I have a little documentation on two specific
+ tape controllers (MT3200 and WD800/WD800A), but I have not tried to emulate
+ controller-specific features.
+
+
+ Long description: see 2234398-9701 and 2306140-9701.
+
+
+ Raphael Nabet 2002
+*/
+/*
+ Image encoding:
+
+
+ 2 bytes: record len - little-endian
+ 2 bytes: always 0s (length MSBs?)
+ len bytes: data
+ 2 bytes: record len - little-endian
+ 2 bytes: always 0s (length MSBs?)
+
+ 4 0s: EOF mark
+*/
+
+#include "emu.h"
+#include "990_tap.h"
+
+enum
+{
+ w0_offline = 0x8000,
+ w0_BOT = 0x4000,
+ w0_EOR = 0x2000,
+ w0_EOF = 0x1000,
+ w0_EOT = 0x0800,
+ w0_write_ring = 0x0400,
+ w0_tape_rewinding = 0x0200,
+ w0_command_timeout = 0x0100,
+
+ w0_rewind_status = 0x00f0,
+ w0_rewind_mask = 0x000f,
+
+ w6_unit0_sel = 0x8000,
+ w6_unit1_sel = 0x4000,
+ w6_unit2_sel = 0x2000,
+ w6_unit3_sel = 0x1000,
+ w6_command = 0x0f00,
+
+ w7_idle = 0x8000,
+ w7_complete = 0x4000,
+ w7_error = 0x2000,
+ w7_int_enable = 0x1000,
+ w7_PE_format = 0x0200,
+ w7_abnormal_completion = 0x0100,
+ w7_interface_parity_err = 0x0080,
+ w7_err_correction_enabled = 0x0040,
+ w7_hard_error = 0x0020,
+ w7_tiline_parity_err = 0x0010,
+ w7_tiline_timing_err = 0x0008,
+ w7_tiline_timeout_err = 0x0004,
+ /*w7_format_error = 0x0002,*/
+ w7_tape_error = 0x0001
+};
+
+static const UINT16 w_mask[8] =
+{
+ 0x000f, /* Controllers should prevent overwriting of w0 status bits, and I know
+ that some controllers do so. */
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xf3ff /* Don't overwrite reserved bits */
+};
+
+/*
+ Parse the tape select lines, and return the corresponding tape unit.
+ (-1 if none)
+*/
+int tap_990_device::cur_tape_unit()
+{
+ int reply;
+
+ if (m_w[6] & w6_unit0_sel)
+ reply = 0;
+ else if (m_w[6] & w6_unit1_sel)
+ reply = 1;
+ else if (m_w[6] & w6_unit2_sel)
+ reply = 2;
+ else if (m_w[6] & w6_unit3_sel)
+ reply = 3;
+ else
+ reply = -1;
+
+ if (reply >= MAX_TAPE_UNIT)
+ reply = -1;
+
+ return reply;
+}
+
+/*
+ Update interrupt state
+*/
+void tap_990_device::update_interrupt()
+{
+ bool level = (m_w[7] & w7_idle)
+ && (((m_w[7] & w7_int_enable) && (m_w[7] & (w7_complete | w7_error)))
+ || ((m_w[0] & ~(m_w[0] >> 4)) & w0_rewind_mask));
+ m_int_line(level);
+}
+
+/*
+ Handle the read binary forward command: read the next record on tape.
+*/
+void tap_990_device::cmd_read_binary_forward()
+{
+ UINT8 buffer[256];
+ int reclen;
+
+ int dma_address;
+ int char_count;
+ int read_offset;
+
+ int rec_count = 0;
+ int chunk_len;
+ int bytes_to_read;
+ int bytes_read;
+ int i;
+
+ int tap_sel = cur_tape_unit();
+
+ if (tap_sel == -1)
+ {
+ /* No idea what to report... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ return;
+ }
+ else if (!m_tape[tap_sel].img->exists())
+ { /* offline */
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#if 0
+ else if (0)
+ { /* rewind in progress */
+ m_w[0] |= 0x80 >> tap_sel;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#endif
+
+ m_tape[tap_sel].bot = 0;
+
+ dma_address = ((((int)m_w[6]) << 16) | m_w[5]) & 0x1ffffe;
+ char_count = m_w[4];
+ read_offset = m_w[3];
+
+ bytes_read = m_tape[tap_sel].img->fread(buffer, 4);
+ if (bytes_read != 4)
+ {
+ if (bytes_read == 0)
+ { /* legitimate EOF */
+ m_tape[tap_sel].eot = 1;
+ m_w[0] |= w0_EOT; /* or should it be w0_command_timeout? */
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ else
+ { /* illegitimate EOF */
+ /* No idea what to report... */
+ /* eject tape to avoid catastrophes */
+ logerror("Tape error\n");
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ }
+ reclen = (((int) buffer[1]) << 8) | buffer[0];
+ if (buffer[2] || buffer[3])
+ { /* no idea what these bytes mean */
+ logerror("Tape error\n");
+ logerror("Tape format looks gooofy\n");
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ /* test for EOF mark */
+ if (reclen == 0)
+ {
+ logerror("read binary forward: found EOF, requested %d\n", char_count);
+ m_w[0] |= w0_EOF;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ logerror("read binary forward: rec length %d, requested %d\n", reclen, char_count);
+
+ rec_count = reclen;
+
+ /* skip up to read_offset bytes */
+ chunk_len = (read_offset > rec_count) ? rec_count : read_offset;
+
+ if (m_tape[tap_sel].img->fseek(chunk_len, SEEK_CUR))
+ { /* eject tape */
+ logerror("Tape error\n");
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ rec_count -= chunk_len;
+ read_offset -= chunk_len;
+ if (read_offset)
+ {
+ m_w[0] |= w0_EOR;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto skip_trailer;
+ }
+
+ /* read up to char_count bytes */
+ chunk_len = (char_count > rec_count) ? rec_count : char_count;
+
+ for (; chunk_len>0; )
+ {
+ bytes_to_read = (chunk_len < sizeof(buffer)) ? chunk_len : sizeof(buffer);
+ bytes_read = m_tape[tap_sel].img->fread(buffer, bytes_to_read);
+
+ if (bytes_read & 1)
+ {
+ buffer[bytes_read] = 0xff;
+ }
+
+ /* DMA */
+ for (i=0; i<bytes_read; i+=2)
+ {
+ machine().device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, (((int) buffer[i]) << 8) | buffer[i+1]);
+ dma_address = (dma_address + 2) & 0x1ffffe;
+ }
+
+ rec_count -= bytes_read;
+ char_count -= bytes_read;
+ chunk_len -= bytes_read;
+
+ if (bytes_read != bytes_to_read)
+ { /* eject tape */
+ logerror("Tape error\n");
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ }
+
+ if (char_count)
+ {
+ m_w[0] |= w0_EOR;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto skip_trailer;
+ }
+
+ if (rec_count)
+ { /* skip end of record */
+ if (m_tape[tap_sel].img->fseek(rec_count, SEEK_CUR))
+ { /* eject tape */
+ logerror("Tape error\n");
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ }
+
+skip_trailer:
+ if (m_tape[tap_sel].img->fread(buffer, 4) != 4)
+ { /* eject tape */
+ logerror("Tape error\n");
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
+ { /* eject tape */
+ logerror("Tape error\n");
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ if (buffer[2] || buffer[3])
+ { /* no idea what these bytes mean */
+ logerror("Tape error\n");
+ logerror("Tape format looks gooofy\n");
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ if (! (m_w[7] & w7_error))
+ {
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+ }
+
+update_registers:
+ m_w[1] = rec_count & 0xffff;
+ m_w[2] = (rec_count >> 8) & 0xff;
+ m_w[3] = read_offset;
+ m_w[4] = char_count;
+ m_w[5] = (dma_address >> 1) & 0xffff;
+ m_w[6] = (m_w[6] & 0xffe0) | ((dma_address >> 17) & 0xf);
+}
+
+/*
+ Handle the record skip forward command: skip a specified number of records.
+*/
+void tap_990_device::cmd_record_skip_forward()
+{
+ UINT8 buffer[4];
+ int reclen;
+
+ int record_count;
+ int bytes_read;
+
+ int tap_sel = cur_tape_unit();
+
+ if (tap_sel == -1)
+ {
+ /* No idea what to report... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ return;
+ }
+ else if (! m_tape[tap_sel].img->exists())
+ { /* offline */
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#if 0
+ else if (0)
+ { /* rewind in progress */
+ m_w[0] |= 0x80 >> tap_sel;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#endif
+
+ record_count = m_w[4];
+
+ if (record_count)
+ m_tape[tap_sel].bot = 0;
+
+ while (record_count > 0)
+ {
+ bytes_read = m_tape[tap_sel].img->fread(buffer, 4);
+ if (bytes_read != 4)
+ {
+ if (bytes_read == 0)
+ { /* legitimate EOF */
+ m_tape[tap_sel].eot = 1;
+ m_w[0] |= w0_EOT; /* or should it be w0_command_timeout? */
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ else
+ { /* illegitimate EOF */
+ /* No idea what to report... */
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ }
+ reclen = (((int) buffer[1]) << 8) | buffer[0];
+ if (buffer[2] || buffer[3])
+ { /* no idea what these bytes mean */
+ logerror("Tape format looks gooofy\n");
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ /* test for EOF mark */
+ if (reclen == 0)
+ {
+ logerror("record skip forward: found EOF\n");
+ m_w[0] |= w0_EOF;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ /* skip record data */
+ if (m_tape[tap_sel].img->fseek(reclen, SEEK_CUR))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ if (m_tape[tap_sel].img->fread(buffer, 4) != 4)
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ if (buffer[2] || buffer[3])
+ { /* no idea what these bytes mean */
+ logerror("Tape format looks gooofy\n");
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ record_count--;
+ }
+
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+
+update_registers:
+ m_w[4] = record_count;
+}
+
+/*
+ Handle the record skip reverse command: skip a specified number of records backwards.
+*/
+void tap_990_device::cmd_record_skip_reverse()
+{
+ UINT8 buffer[4];
+ int reclen;
+
+ int record_count;
+
+ int bytes_read;
+
+ int tap_sel = cur_tape_unit();
+
+ if (tap_sel == -1)
+ {
+ /* No idea what to report... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ return;
+ }
+ else if (! m_tape[tap_sel].img->exists())
+ { /* offline */
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#if 0
+ else if (0)
+ { /* rewind in progress */
+ m_w[0] |= 0x80 >> tap_sel;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#endif
+
+ record_count = m_w[4];
+
+ if (record_count)
+ m_tape[tap_sel].eot = 0;
+
+ while (record_count > 0)
+ {
+ if (m_tape[tap_sel].img->ftell() == 0)
+ { /* bot */
+ m_tape[tap_sel].bot = 1;
+ m_w[0] |= w0_BOT;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ if (m_tape[tap_sel].img->fseek(-4, SEEK_CUR))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ bytes_read = m_tape[tap_sel].img->fread(buffer, 4);
+ if (bytes_read != 4)
+ {
+ /* illegitimate EOF */
+ /* No idea what to report... */
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ reclen = (((int) buffer[1]) << 8) | buffer[0];
+ if (buffer[2] || buffer[3])
+ { /* no idea what these bytes mean */
+ logerror("Tape format looks gooofy\n");
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ /* look for EOF mark */
+ if (reclen == 0)
+ {
+ logerror("record skip reverse: found EOF\n");
+ if (m_tape[tap_sel].img->fseek(-4, SEEK_CUR))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ m_w[0] |= w0_EOF;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ if (m_tape[tap_sel].img->fseek(-reclen-8, SEEK_CUR))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ if (m_tape[tap_sel].img->fread(buffer, 4) != 4)
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+ if (buffer[2] || buffer[3])
+ { /* no idea what these bytes mean */
+ logerror("Tape format looks gooofy\n");
+ /* eject tape to avoid catastrophes */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ if (m_tape[tap_sel].img->fseek(-4, SEEK_CUR))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ goto update_registers;
+ }
+
+ record_count--;
+ }
+
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+
+update_registers:
+ m_w[4] = record_count;
+}
+
+/*
+ Handle the rewind command: rewind to BOT.
+*/
+void tap_990_device::cmd_rewind()
+{
+ int tap_sel = cur_tape_unit();
+
+ if (tap_sel == -1)
+ {
+ /* No idea what to report... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ return;
+ }
+ else if (! m_tape[tap_sel].img->exists())
+ { /* offline */
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#if 0
+ else if (0)
+ { /* rewind in progress */
+ m_w[0] |= 0x80 >> tap_sel;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#endif
+
+ m_tape[tap_sel].eot = 0;
+
+ if (m_tape[tap_sel].img->fseek(0, SEEK_SET))
+ { /* eject tape */
+ m_tape[tap_sel].img->unload();
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ return;
+ }
+ m_tape[tap_sel].bot = 1;
+
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+}
+
+/*
+ Handle the rewind and offline command: disable the tape unit.
+*/
+void tap_990_device::cmd_rewind_and_offline()
+{
+ int tap_sel = cur_tape_unit();
+
+ if (tap_sel == -1)
+ {
+ /* No idea what to report... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ return;
+ }
+ else if (! m_tape[tap_sel].img->exists())
+ { /* offline */
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#if 0
+ else if (0)
+ { /* rewind in progress */
+ m_w[0] |= 0x80 >> tap_sel;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ return;
+ }
+#endif
+
+ /* eject tape */
+ m_tape[tap_sel].img->unload();
+
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+}
+
+/*
+ Handle the read transport status command: return the current tape status.
+*/
+void tap_990_device::read_transport_status()
+{
+ int tap_sel = cur_tape_unit();
+
+ if (tap_sel == -1)
+ {
+ /* No idea what to report... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ }
+ else if (! m_tape[tap_sel].img->exists())
+ { /* offline */
+ m_w[0] |= w0_offline;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ }
+#if 0
+ else if (0)
+ { /* rewind in progress */
+ m_w[0] |= /*...*/;
+ m_w[7] |= w7_idle | w7_error | w7_tape_error;
+ update_interrupt();
+ }
+#endif
+ else
+ { /* no particular error condition */
+ if (m_tape[tap_sel].bot)
+ m_w[0] |= w0_BOT;
+ if (m_tape[tap_sel].eot)
+ m_w[0] |= w0_EOT;
+ if (m_tape[tap_sel].wp)
+ m_w[0] |= w0_write_ring;
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+ }
+}
+
+/*
+ Parse command code and execute the command.
+*/
+void tap_990_device::execute_command()
+{
+ /* hack */
+ m_w[0] &= 0xff;
+
+ switch ((m_w[6] & w6_command) >> 8)
+ {
+ case 0x00:
+ case 0x0C:
+ case 0x0E:
+ /* NOP */
+ logerror("NOP\n");
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+ break;
+ case 0x01:
+ /* buffer sync: means nothing under emulation */
+ logerror("buffer sync\n");
+ m_w[7] |= w7_idle | w7_complete;
+ update_interrupt();
+ break;
+ case 0x02:
+ /* write EOF - not emulated */
+ logerror("write EOF\n");
+ /* ... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ break;
+ case 0x03:
+ /* record skip reverse - not fully tested */
+ logerror("record skip reverse\n");
+ cmd_record_skip_reverse();
+ break;
+ case 0x04:
+ /* read binary forward */
+ logerror("read binary forward\n");
+ cmd_read_binary_forward();
+ break;
+ case 0x05:
+ /* record skip forward - not tested */
+ logerror("record skip forward\n");
+ cmd_record_skip_forward();
+ break;
+ case 0x06:
+ /* write binary forward - not emulated */
+ logerror("write binary forward\n");
+ /* ... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ break;
+ case 0x07:
+ /* erase - not emulated */
+ logerror("erase\n");
+ /* ... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ break;
+ case 0x08:
+ case 0x09:
+ /* read transport status */
+ logerror("read transport status\n");
+ read_transport_status();
+ break;
+ case 0x0A:
+ /* rewind - not tested */
+ logerror("rewind\n");
+ cmd_rewind();
+ break;
+ case 0x0B:
+ /* rewind and offline - not tested */
+ logerror("rewind and offline\n");
+ cmd_rewind_and_offline();
+ break;
+ case 0x0F:
+ /* extended control and status - not emulated */
+ logerror("extended control and status\n");
+ /* ... */
+ m_w[7] |= w7_idle | w7_error | w7_hard_error;
+ update_interrupt();
+ break;
+ }
+}
+
+
+/*
+ Read one register in TPCS space
+*/
+READ16_MEMBER( tap_990_device::read )
+{
+ if (offset < 8)
+ return m_w[offset];
+ else
+ return 0;
+}
+
+/*
+ Write one register in TPCS space. Execute command if w7_idle is cleared.
+*/
+WRITE16_MEMBER( tap_990_device::write )
+{
+ if (offset < 8)
+ {
+ /* write protect if a command is in progress */
+ if (m_w[7] & w7_idle)
+ {
+ UINT16 old_data = m_w[offset];
+
+ /* Only write writable bits AND honor byte accesses (ha!) */
+ m_w[offset] = (m_w[offset] & ((~w_mask[offset]) | mem_mask)) | (data & w_mask[offset] & ~mem_mask);
+
+ if ((offset == 0) || (offset == 7))
+ update_interrupt();
+
+ if ((offset == 7) && (old_data & w7_idle) && ! (data & w7_idle))
+ { /* idle has been cleared: start command execution */
+ execute_command();
+ }
+ }
+ }
+}
+
+class ti990_tape_image_device : public device_t,
+ public device_image_interface
+{
+public:
+ // construction/destruction
+ ti990_tape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // image-level overrides
+ virtual iodevice_t image_type() const { return IO_MAGTAPE; }
+
+ virtual bool is_readable() const { return 1; }
+ virtual bool is_writeable() const { return 1; }
+ virtual bool is_creatable() const { return 1; }
+ virtual bool must_be_loaded() const { return 0; }
+ virtual bool is_reset_on_load() const { return 0; }
+ virtual const char *image_interface() const { return NULL; }
+ virtual const char *file_extensions() const { return "tap"; }
+ virtual const option_guide *create_option_guide() const { return NULL; }
+
+ virtual bool call_load();
+ virtual void call_unload();
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+private:
+ int tape_get_id();
+};
+
+const device_type TI990_TAPE = &device_creator<ti990_tape_image_device>;
+
+ti990_tape_image_device::ti990_tape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TI990_TAPE, "TI-990 Magnetic Tape", tag, owner, clock, "ti990_tape_image", __FILE__),
+ device_image_interface(mconfig, *this)
+{
+}
+
+void ti990_tape_image_device::device_config_complete()
+{
+ update_names();
+}
+
+void ti990_tape_image_device::device_start()
+{
+ tap_990_device* tpc = downcast<tap_990_device*>(owner());
+ tpc->set_tape(tape_get_id(), this, true, false, false);
+}
+
+int ti990_tape_image_device::tape_get_id()
+{
+ int drive =0;
+ if (strcmp(tag(), ":tape0") == 0) drive = 0;
+ if (strcmp(tag(), ":tape1") == 0) drive = 1;
+ if (strcmp(tag(), ":tape2") == 0) drive = 2;
+ if (strcmp(tag(), ":tape3") == 0) drive = 3;
+ return drive;
+}
+
+/*
+ Open a tape image
+*/
+bool ti990_tape_image_device::call_load()
+{
+ tap_990_device* tpc = downcast<tap_990_device*>(owner());
+ tpc->set_tape(tape_get_id(), this, true, false, is_readonly());
+
+ return IMAGE_INIT_PASS;
+}
+
+/*
+ Close a tape image
+*/
+void ti990_tape_image_device::call_unload()
+{
+ tap_990_device* tpc = downcast<tap_990_device*>(owner());
+ tpc->set_tape(tape_get_id(), this, false, false, true);
+}
+
+#define MCFG_TI990_TAPE_ADD(_tag) \
+ MCFG_DEVICE_ADD((_tag), TI990_TAPE, 0)
+
+
+static MACHINE_CONFIG_FRAGMENT( tap_990 )
+ MCFG_TI990_TAPE_ADD("tape0")
+ MCFG_TI990_TAPE_ADD("tape1")
+ MCFG_TI990_TAPE_ADD("tape2")
+ MCFG_TI990_TAPE_ADD("tape3")
+MACHINE_CONFIG_END
+
+const device_type TI990_TAPE_CTRL = &device_creator<tap_990_device>;
+
+tap_990_device::tap_990_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TI990_TAPE_CTRL, "Generic TI-990 Tape Controller", tag, owner, clock, "tap_990", __FILE__),
+ m_int_line(*this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void tap_990_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tap_990_device::device_start()
+{
+ m_int_line.resolve();
+ memset(m_w, 0, sizeof(m_w));
+
+ // The PE bit is always set for the MT3200 (but not MT1600)
+ // According to MT3200 manual, w7 bit #4 (reserved) is always set
+ m_w[7] = w7_idle /*| w7_PE_format*/ | 0x0800;
+
+ update_interrupt();
+}
+
+//-------------------------------------------------
+// device_mconfig_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor tap_990_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( tap_990 );
+}