summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine')
-rw-r--r--src/devices/machine/com8116.cpp42
-rw-r--r--src/devices/machine/com8116.h6
-rw-r--r--src/devices/machine/corvushd.cpp2
-rw-r--r--src/devices/machine/dmac.cpp39
-rw-r--r--src/devices/machine/dmac.h2
-rw-r--r--src/devices/machine/hp_taco.cpp1467
-rw-r--r--src/devices/machine/hp_taco.h169
-rw-r--r--src/devices/machine/i8271.cpp20
-rw-r--r--src/devices/machine/i8271.h1
-rw-r--r--src/devices/machine/k033906.cpp4
-rw-r--r--src/devices/machine/k033906.h4
-rw-r--r--src/devices/machine/pic8259.cpp5
-rw-r--r--src/devices/machine/ram.cpp13
-rw-r--r--src/devices/machine/ram.h4
14 files changed, 1740 insertions, 38 deletions
diff --git a/src/devices/machine/com8116.cpp b/src/devices/machine/com8116.cpp
index 46a0ce83986..f5c177fef62 100644
--- a/src/devices/machine/com8116.cpp
+++ b/src/devices/machine/com8116.cpp
@@ -25,17 +25,59 @@
// device type definition
const device_type COM8116 = &device_creator<com8116_device>;
+// Parts with T after the number do not have an internal oscillator and require an external clock source
+// The SMC/COM 5xxx parts are all dual 5v/12v parts, while the 8xxx parts are 5v only
+// SMC/COM5016(T) with no dash, aka 'STD' part on the datasheet
+// Also COM8116(T)/8126(T)/8136(T)/8146(T) and Synertek sy2661-3 and GI AY-5-8116(T)-000 and GI AY-5-8136(T)-000 and WD WD-1943-00 (aka WD8136-00)
+// SMC/COM8156(T) is the same chip but clocked twice as fast and 32x clocks per baud instead of 16x
+// baud rates are 50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 19200
const int com8116_device::divisors_16X_5_0688MHz[] =
{ 6336, 4224, 2880, 2355, 2112, 1056, 528, 264, 176, 158, 132, 88, 66, 44, 33, 16 };
+// SMC/COM8116-003
+// from http://www.vintagecomputer.net/fjkraan/comp/divcomp/doc/SMC_BaudGen.pdf page 283 (pdf page 20)
+// baud rates are 50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 9600, 19200
+const int com8116_device::divisors_16X_6_01835MHz[] =
+ { 7523, 5015, 3420, 2797, 2508, 1881, 1254, 627, 313, 209, 188, 157, 104, 78, 39, 20 };
+
+// SMC/COM5016(T)-5 and WD WD-1943-05; Synertek SY2661-1 and 2 are NOT the same as this despite using same clock speed, see below
+// SMC/COM8156(T)-5 is the same chip but clocked twice as fast and 32x clocks per baud instead of 16x
+// baud rates are 50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 19200
const int com8116_device::divisors_16X_4_9152MHz[] =
{ 6144, 4096, 2793, 2284, 2048, 1024, 512, 256, 171, 154, 128, 85, 64, 43, 32, 16 };
+// SMC/COM5016(T)-6 and WD WD-1943-06
+// baud rates are 50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 19200
const int com8116_device::divisors_32X_5_0688MHz[] =
{ 3168, 2112, 1440, 1177, 1056, 792, 528, 264, 132, 88, 66, 44, 33, 22, 16, 8 };
+// SMC/COM5016(T)-013 (from http://bitsavers.informatik.uni-stuttgart.de/pdf/dec/terminal/vt100/EK-VT100-TM-003_VT100_Technical_Manual_Jul82.pdf page 4-27 (pdf page 78))
+// and from http://www.vintagecomputer.net/fjkraan/comp/divcomp/doc/SMC_BaudGen.pdf page 283 (pdf page 20)
+// SMC/COM5106-013A is the same chip clocked twice as fast, but with 32x clocks per baud instead of 16x
+// baud rates are 50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 9600, 19200
+const int com8116_device::divisors_16X_2_7648MHz[] =
+ { 3456, 2304, 1571, 1285, 1152, 864, 576, 288, 144, 96, 86, 72, 48, 36, 18, 9 };
+
+// SMC/COM5026(T)-030 (non-standard serial rates, from http://bitsavers.informatik.uni-stuttgart.de/pdf/standardMicrosystems/_dataBooks/1979_StandardMicrosystems.pdf page 135)
+const int com8116_device::divisors_16X_5_0688MHz_030[] =
+ { 731, 733, 735, 737, 741, 743, 745, 751, 6970, 5569, 5433, 4752, 4269, 1920, 1584, 301 };
+
+// SMC/COM5036(T)-080 (from http://bitsavers.informatik.uni-stuttgart.de/pdf/standardMicrosystems/_dataBooks/1979_StandardMicrosystems.pdf page 135)
+const int com8116_device::divisors_16X_4_6080MHz[] =
+ { 5760, 3840, 2618, 2141, 1920, 960, 480, 240, 160, 144, 120, 80, 60, 40, 30, 15 };
+
+// COM8046 combines the -6 and STD tables into one device as a 32-entry table
+
+// Synertek SY2661-1 (from http://bitsavers.informatik.uni-stuttgart.de/pdf/synertek/_dataBooks/Synertek_1981-1982_Data_Catalog.pdf page 3-40 (pdf page 139))
+// baud rates are 50, 75, 110, 134.5, 150, 200, 300, 600, 1050, 1200, 1800, 2000, 2400, 4800, 9600, 19200
+const int com8116_device::divisors_16X_4_9152MHz_SY2661_1[] =
+ { 6144, 4096, 2793, 2284, 2048, 1536, 1024, 512, 292, 256, 171, 154, 128, 64, 32, 16 };
+// Synertek SY2661-2 (from http://bitsavers.informatik.uni-stuttgart.de/pdf/synertek/_dataBooks/Synertek_1981-1982_Data_Catalog.pdf page 3-40 (pdf page 139))
+// baud rates are 45.5, 50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2000, 2400, 4800, 9600, 19200, 38400
+const int com8116_device::divisors_16X_4_9152MHz_SY2661_2[] =
+ { 6752, 6144, 4096, 2793, 2284, 2048, 1024, 512, 256, 171, 154, 128, 64, 32, 16, 8 };
//**************************************************************************
// LIVE DEVICE
diff --git a/src/devices/machine/com8116.h b/src/devices/machine/com8116.h
index 119f40d4b3d..614be491318 100644
--- a/src/devices/machine/com8116.h
+++ b/src/devices/machine/com8116.h
@@ -63,8 +63,14 @@ public:
DECLARE_WRITE8_MEMBER( stt_w );
static const int divisors_16X_5_0688MHz[];
+ static const int divisors_16X_6_01835MHz[];
static const int divisors_16X_4_9152MHz[];
static const int divisors_32X_5_0688MHz[];
+ static const int divisors_16X_2_7648MHz[];
+ static const int divisors_16X_5_0688MHz_030[];
+ static const int divisors_16X_4_6080MHz[];
+ static const int divisors_16X_4_9152MHz_SY2661_1[];
+ static const int divisors_16X_4_9152MHz_SY2661_2[];
protected:
// device-level overrides
diff --git a/src/devices/machine/corvushd.cpp b/src/devices/machine/corvushd.cpp
index 558851c0b7a..08536344849 100644
--- a/src/devices/machine/corvushd.cpp
+++ b/src/devices/machine/corvushd.cpp
@@ -874,7 +874,7 @@ UINT8 corvus_hdc_t::corvus_read_firmware_block(UINT8 head, UINT8 sector) {
// Status of command
//
UINT8 corvus_hdc_t::corvus_write_firmware_block(UINT8 head, UINT8 sector, UINT8 *buffer) {
- UINT16 relative_sector; // Relative sector on drive for Physical Read
+ UINT16 relative_sector; // Relative sector on drive for Physical Write
UINT8 status;
relative_sector = head * m_sectors_per_track + sector;
diff --git a/src/devices/machine/dmac.cpp b/src/devices/machine/dmac.cpp
index 34992fc4f5c..ba1167eb2d5 100644
--- a/src/devices/machine/dmac.cpp
+++ b/src/devices/machine/dmac.cpp
@@ -145,6 +145,8 @@ void dmac_device::check_interrupts()
// any interrupts pending?
if (m_istr & ISTR_INT_MASK)
m_istr |= ISTR_INT_P;
+ else
+ m_istr &= ~ISTR_INT_P;
}
else
m_istr &= ~ISTR_INT_P;
@@ -202,18 +204,19 @@ READ16_MEMBER( dmac_device::register_read )
case 0x48:
case 0x49:
- case 0x50:
- case 0x58:
- case 0x59:
- case 0x5a:
- case 0x5b:
- case 0x5c:
- case 0x5e:
- case 0x5f:
data = m_scsi_read_handler(offset);
if (VERBOSE)
- logerror("%s('%s'): read scsi data @ %02x %04x [mask = %04x]\n", shortname(), basetag(), offset, data, mem_mask);
+ logerror("%s('%s'): read scsi register @ %02x %04x [mask = %04x]\n", shortname(), basetag(), offset, data, mem_mask);
+
+ break;
+
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ if (VERBOSE)
+ logerror("%s('%s'): read xt register @ %02x %04x [mask = %04x]\n", shortname(), basetag(), offset, data, mem_mask);
break;
@@ -306,20 +309,20 @@ WRITE16_MEMBER( dmac_device::register_write )
case 0x48:
case 0x49:
- case 0x50:
- case 0x58:
- case 0x59:
- case 0x5a:
- case 0x5b:
- case 0x5c:
- case 0x5e:
- case 0x5f:
if (VERBOSE)
- logerror("%s('%s'): write scsi data @ %02x %04x [mask = %04x]\n", shortname(), basetag(), offset, data, mem_mask);
+ logerror("%s('%s'): write scsi register @ %02x %04x [mask = %04x]\n", shortname(), basetag(), offset, data, mem_mask);
m_scsi_write_handler(offset, data, 0xff);
break;
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ if (VERBOSE)
+ logerror("%s('%s'): write xt register @ %02x %04x [mask = %04x]\n", shortname(), basetag(), offset, data, mem_mask);
+ break;
+
case 0x70:
if (VERBOSE)
logerror("%s('%s'): write dma start strobe %04x [mask = %04x]\n", shortname(), basetag(), data, mem_mask);
diff --git a/src/devices/machine/dmac.h b/src/devices/machine/dmac.h
index 2d86c427013..8580b4c86ab 100644
--- a/src/devices/machine/dmac.h
+++ b/src/devices/machine/dmac.h
@@ -128,7 +128,7 @@ private:
ISTR_FE_FLG = 0x001 // fifo-empty flag
};
- static const int ISTR_INT_MASK = 0x1fc;
+ static const int ISTR_INT_MASK = 0x1ec;
// callbacks
devcb_write_line m_cfgout_handler;
diff --git a/src/devices/machine/hp_taco.cpp b/src/devices/machine/hp_taco.cpp
new file mode 100644
index 00000000000..76203d31ee3
--- /dev/null
+++ b/src/devices/machine/hp_taco.cpp
@@ -0,0 +1,1467 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/*********************************************************************
+
+ hp_taco.cpp
+
+ HP TApe COntroller (5006-3012)
+
+*********************************************************************/
+
+// This device has been reverse engineered entirely through documents & study of HP software.
+// I had no access to the real device to experiment.
+// Available documentation on the internal working of TACO chip is close to nothing. The best
+// I could find is [1] (see below) where all that's described is a (too) brief summary of registers and little else.
+// In other words, no description of the commands that can be issued to TACO chips.
+// So, my main source of information was the careful study of HP software, especially the 9845 system test ROM (09845-66520).
+// The second half of this ROM holds a comprehensive set of tape drive tests.
+// The main shortcomings of my approach are:
+// * I could indentify only those TACO commands that are actually used by the software. I managed
+// to identify 17 out of 32 possible commands. The purpose of the rest of commands is anyone's guess.
+// * I could only guess the behavior of TACO chips in corner cases (especially behavior in various error/abnormal
+// conditions)
+//
+// Documentation I used:
+// [1] HP, manual 64940-90905, may 80 rev. - Model 64940A tape control & drive service manual
+// [2] US patent 4,075,679 describing HP9825 system (this system had a discrete implementation of tape controller). The
+// firmware listing was quite useful in identifying sequences of commands (for example how to find a specific sector etc.).
+// [3] http://www.hp9845.net site
+// [4] April 1978 issue of HP Journal. There is a one-page summary of TACO chip on page 20.
+
+// This is an overview of the TACO/CPU interface.
+//
+// Reg. | R/W | Content
+// =====================
+// R4 | R/W | Data register: words read/written to/from tape pass through this register
+// R5 | R/W | Command and status register (see below)
+// R6 | R/W | Tachometer register. Writing it sets a pulse counter that counts up on either tachometer pulses or IRGs, depending
+// | | on command. When the counter rolls over from 0xffff to 0 it typically ends the command. It's not clear to me
+// | | what value could be read from this register, if it's just the same value that was written last time or the internal
+// | | counter or something else entirely.
+// R7 | R | Checksum register. Reading it clears it.
+// R7 | W | Timing register. It controls somehow the encoding and decoding of bits. For now I completely ignore it because its
+// | | content it's totally unknown to me. It seems safe to do so, anyway. I can see that it's always set to 0x661d before
+// | | writing to tape and to 0x0635 before reading.
+//
+// Format of TACO command/status register (R5)
+// Bit R/W Content
+// ===============
+// 15 RW Tape direction (1 = forward)
+// 14..10 RW Command (see the "enum" below)
+// 9 RW ? Drive ON according to [1], the actual use seems to be selection of gap length
+// 8 RW ? Size of gaps according to [1], N/U in my opinion
+// 7 RW Speed of tape (1 = 90 ips, 0 = 22 ips)
+// 6 RW Option bit for various commands
+// 5 R Current track (1 = B)
+// 4 R Gap detected (1)
+// 3 R Write protection (1)
+// 2 R Servo failure (1)
+// 1 R Cartridge out (1)
+// 0 R Hole detected (1)
+
+// Here's a summary of the on-tape format of HP9845 systems.
+// * A tape has two independent tracks (A & B).
+// * Each track holds 426 sectors.
+// * Each sector has an header and 256 bytes of payload (see below)
+// * Sectors are separated by gaps of uniform magnetization called IRGs (Inter-Record Gaps)
+// * The basic unit of data I/O are 16-bit words
+// * Bits are encoded by different distances between magnetic flux reversals
+// * The structure of tracks is:
+// - Begin of tape holes
+// - The deadzone: 350x 0xffff words
+// - 1" of IRG
+// - Sector #0 (track A) or #426 (track B)
+// - 1" of IRG (2.5" on track A)
+// - Sector #1 (track A) or #427 (track B)
+// - 1" of IRG
+// - Sector #2 (track A) or #428 (track B)
+// - ...and so on up to sector #425/#851
+// - 6" of final gap
+// - Non-recorded tape
+// - End of tape holes
+// * Sector #0 is not used
+// * Sectors #1 and #2 hold the first copy of tape directory
+// * Sectors #3 and #4 hold the second/backup copy of tape directory
+// * User data are stored starting from sector #5
+// * There is no "fragmentation" map (like file allocation table in FAT filesystem): a file
+// spanning more than 1 sector always occupy a single block of contiguous sectors.
+//
+// A sector is structured like this:
+// Word 0: Invisible preamble word (always 0). Preamble comes from 9825, don't know if it's
+// actually there in TACO encoding. I assumed it is.
+// Word 1: Format/sector in use and other unidentified bits.
+// Word 2: Sector number
+// Word 3: Sector length and other bits
+// Word 4: Checksum (sum of words 1..3)
+// Words 5..132: Payload
+// Word 133: Checksum (sum of words 5..132)
+//
+// Physical encoding of words is borrowed from 9825 as I wasn't able
+// to gather any info on the actual encoding of TACO chips.
+// This is how 9825 encodes words on tape:
+// - the unit of encoding are 16-bit words
+// - each word is encoded from MSB to LSB
+// - each word has an extra invisible "1" encoded at the end
+// - tape is read/written at slow speed only (21.98 ips)
+// - a 0 is encoded with a distance between flux reversals of 1/35200 s
+// (giving a maximum density of about 1600 reversals per inch)
+// - a 1 is encoded with a distance that's 1.75 times that of a 0
+//
+// This driver is based on the following model of the actual TACO/tape system:
+// * Tape immediately reaches working speed (no spin-up time)
+// * Inversion of tape direction and change of speed are immediate as well
+// * Time & distance to stop the tape are modeled, though. Firmware is upset by
+// a tape with null braking time/distance.
+// * Speed of tape is exceptionally accurate. Real tape was controlled by a closed loop
+// with something like 1% accuracy on speed.
+// * Storage is modeled by one "map" data structure per track. Each map maps the tape position
+// to the 16-bit word stored at that position. Gaps are modeled by lack of data in the map.
+// There is no model of the physical encoding of bits (except to compute how long each word
+// is on tape).
+// * Read threshold is ignored. Real tapes could be read with either a low or high threshold.
+// * "Flag" bit is used as a busy/ready signal in real TACO. Here I assumed the device is
+// always ready, so Flag is always active.
+// * I tried to fill the (many) gaps on chip behavior with "sensible" solutions. I could only
+// validate my solutions by running the original firmware in MAME, though (no real hw at hand).
+//
+// TODOs/issues:
+// * Some code cleanup
+// * Handling of tape holes seems to be wrong: test "C" of test ROM only works partially
+// * Find out what is read from register R6
+// * Handle device_image_interface::call_display to show state of tape
+// * Find more info on TACO chips (does anyone with a working 9845 or access to internal HP docs want to
+// help me here, please?)
+//
+#include "emu.h"
+#include "hp_taco.h"
+
+// Debugging
+#define VERBOSE 1
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+#define VERBOSE_0 0
+#define LOG_0(x) do { if (VERBOSE_0) logerror x; } while (0)
+
+// Macros to clear/set single bits
+#define BIT_MASK(n) (1U << (n))
+#define BIT_CLR(w , n) ((w) &= ~BIT_MASK(n))
+#define BIT_SET(w , n) ((w) |= BIT_MASK(n))
+
+// Timers
+enum {
+ TAPE_TMR_ID,
+ HOLE_TMR_ID
+};
+
+// Constants
+#define CMD_REG_MASK 0xffc0 // Command register mask
+#define STATUS_REG_MASK 0x003f // Status register mask
+#define TACH_TICKS_PER_INCH 968 // Tachometer pulses per inch of tape movement
+#define TAPE_POS_FRACT 1024 // 10 bits of fractional part in tape_pos_t
+#define ONE_INCH_POS (TACH_TICKS_PER_INCH * TAPE_POS_FRACT) // Value in tape_pos_t representing 1 inch of tape
+#define TACH_FREQ_SLOW 21276 // Tachometer pulse frequency for slow speed (21.98 ips)
+#define TACH_FREQ_FAST 87196 // Tachometer pulse frequency for fast speed (90.08 ips)
+#define TAPE_LENGTH ((140 * 12 + 72 * 2) * ONE_INCH_POS) // Tape length: 140 ft of usable tape + 72" of punched tape at either end
+#define TAPE_INIT_POS (80 * ONE_INCH_POS) // Initial tape position: 80" from beginning (just past the punched part)
+#define ZERO_BIT_LEN 619 // Length of 0 bits at slow tape speed: 1/(35200 Hz)
+#define ONE_BIT_LEN 1083 // Length of 1 bits at slow tape speed: 1.75 times ZERO_BIT_LEN
+#define QUICK_CMD_USEC 25 // usec for "quick" command execution
+#define FAST_BRAKE_MSEC 73 // Braking time from fast speed to stop (2 ips) in msec (deceleration is 1200 in/s^2)
+#define SLOW_BRAKE_MSEC 17 // Braking time from slow speed to stop in msec
+#define FAST_BRAKE_DIST 3350450 // Braking distance at fast speed (~3.38 in)
+#define SLOW_BRAKE_DIST 197883 // Braking distance at slow speed (~0.2 in)
+#define PREAMBLE_WORD 0 // Value of preamble word
+#define END_GAP_LENGTH (6 * ONE_INCH_POS) // Length of final gap: 6"
+#define MIN_IRG_LENGTH ((tape_pos_t)(0.2 * ONE_INCH_POS)) // Minimum length of IRGs: 0.2" (from 9825, not sure about value in TACO)
+#define NULL_TAPE_POS ((tape_pos_t)-1) // Special value for invalid/unknown tape position
+#define NO_DATA_GAP (17 * ONE_BIT_LEN) // Minimum gap size to detect end of data: length of longest word (0xffff)
+#define FILE_MAGIC 0x4f434154 // Magic value at start of image file: "TACO"
+
+// Parts of command register
+#define CMD_CODE(reg) \
+ (((reg) >> 10) & 0x1f)
+#define DIR_FWD(reg) \
+ (BIT(reg , 15))
+#define SPEED_FAST(reg) \
+ (BIT(reg , 7))
+#define CMD_OPT(reg) \
+ (BIT(reg , 6))
+#define UNKNOWN_B9(reg) \
+ (BIT(reg , 9))
+#define DIR_FWD_MASK BIT_MASK(15) // Direction = forward
+#define SPEED_FAST_MASK BIT_MASK(7) // Speed = fast
+
+// Commands
+enum {
+ CMD_INDTA_INGAP, // 00: scan for data first then for gap
+ CMD_UNK_01, // 01: unknown
+ CMD_FINAL_GAP, // 02: write final gap
+ CMD_INIT_WRITE, // 03: write words for tape formatting
+ CMD_STOP, // 04: stop
+ CMD_UNK_05, // 05: unknown
+ CMD_SET_TRACK, // 06: set A/B track
+ CMD_UNK_07, // 07: unknown
+ CMD_UNK_08, // 08: unknown
+ CMD_UNK_09, // 09: unknown
+ CMD_MOVE, // 0a: move tape
+ CMD_UNK_0b, // 0b: unknown
+ CMD_INGAP_MOVE, // 0c: scan for gap then move a bit further (used to gain some margin when inverting tape movement)
+ CMD_UNK_0d, // 0d: unknown
+ CMD_CLEAR, // 0e: clear errors/unlatch status bits
+ CMD_UNK_0f, // 0f: unknown
+ CMD_NOT_INDTA, // 10: scan for end of data
+ CMD_UNK_11, // 11: unknown
+ CMD_UNK_12, // 12: unknown
+ CMD_UNK_13, // 13: unknown
+ CMD_UNK_14, // 14: unknown
+ CMD_UNK_15, // 15: unknown
+ CMD_WRITE_IRG, // 16: write inter-record gap
+ CMD_UNK_17, // 17: unknown
+ CMD_SCAN_RECORDS, // 18: scan records (count IRGs)
+ CMD_RECORD_WRITE, // 19: write record words
+ CMD_MOVE_INDTA, // 1a: move then scan for data
+ CMD_UNK_1b, // 1b: unknown (for now it seems harmless to handle it as NOP)
+ CMD_DELTA_MOVE_HOLE, // 1c: move tape a given distance, intr at end or first hole found (whichever comes first)
+ CMD_START_READ, // 1d: start record reading
+ CMD_DELTA_MOVE_IRG, // 1e: move tape a given distance, detect gaps in parallel
+ CMD_END_READ // 1f: stop reading
+};
+
+// Bits of status register
+#define STATUS_HOLE_BIT 0 // Hole detected
+#define STATUS_CART_OUT_BIT 1 // Cartridge out
+#define STATUS_SFAIL_BIT 2 // Servo failure
+#define STATUS_WPR_BIT 3 // Write protection
+#define STATUS_GAP_BIT 4 // Gap detected
+#define STATUS_TRACKB_BIT 5 // Track B selected
+#define STATUS_CART_OUT_MASK BIT_MASK(STATUS_CART_OUT_BIT) // Cartridge out
+#define STATUS_WPR_MASK BIT_MASK(STATUS_WPR_BIT) // Write protection
+#define STATUS_ERR_MASK (STATUS_CART_OUT_MASK) // Mask of errors in status reg.
+
+// *** Position of tape holes ***
+// At beginning of tape:
+// *START*
+// |<-----24"----->|<---12"--->|<---12"--->|<-----24"----->|
+// O O O O O O O
+// |<->| |<->| |<->|
+// 0.218" 0.218" 0.218"
+// At end of tape:
+// *END*
+// |<-----24"----->|<---12"--->|<---12"--->|<-----24"----->|
+// O O O O
+//
+static const hp_taco_device::tape_pos_t tape_holes[] = {
+ (hp_taco_device::tape_pos_t)(23.891 * ONE_INCH_POS), // 24 - 0.218 / 2
+ (hp_taco_device::tape_pos_t)(24.109 * ONE_INCH_POS), // 24 + 0.218 / 2
+ (hp_taco_device::tape_pos_t)(35.891 * ONE_INCH_POS), // 36 - 0.218 / 2
+ (hp_taco_device::tape_pos_t)(36.109 * ONE_INCH_POS), // 36 + 0.218 / 2
+ (hp_taco_device::tape_pos_t)(47.891 * ONE_INCH_POS), // 48 - 0.218 / 2
+ (hp_taco_device::tape_pos_t)(48.109 * ONE_INCH_POS), // 48 + 0.218 / 2
+ 72 * ONE_INCH_POS, // 72
+ 1752 * ONE_INCH_POS, // 1752
+ 1776 * ONE_INCH_POS, // 1776
+ 1788 * ONE_INCH_POS, // 1788
+ 1800 * ONE_INCH_POS // 1800
+};
+
+// Device type definition
+const device_type HP_TACO = &device_creator<hp_taco_device>;
+
+// Constructors
+hp_taco_device::hp_taco_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, __FILE__),
+ device_image_interface(mconfig , *this),
+ m_irq_handler(*this),
+ m_flg_handler(*this),
+ m_sts_handler(*this),
+ m_tape_pos(TAPE_INIT_POS),
+ m_image_dirty(false)
+{
+ clear_state();
+}
+
+hp_taco_device::hp_taco_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HP_TACO, "HP TACO", tag, owner, clock, "TACO", __FILE__),
+ device_image_interface(mconfig , *this),
+ m_irq_handler(*this),
+ m_flg_handler(*this),
+ m_sts_handler(*this),
+ m_tape_pos(TAPE_INIT_POS),
+ m_image_dirty(false)
+{
+ clear_state();
+}
+
+WRITE16_MEMBER(hp_taco_device::reg_w)
+{
+ LOG_0(("wr R%u = %04x\n", 4 + offset , data));
+
+ // Any I/O activity clears IRQ
+ irq_w(false);
+
+ switch (offset) {
+ case 0:
+ // Data register
+ m_data_reg = data;
+ m_data_reg_full = true;
+ break;
+
+ case 1:
+ // Command register
+ start_cmd_exec(data & CMD_REG_MASK);
+ break;
+
+ case 2:
+ // Tachometer register
+ m_tach_reg = data;
+ break;
+
+ case 3:
+ // Timing register
+ m_timing_reg = data;
+ break;
+ }
+}
+
+READ16_MEMBER(hp_taco_device::reg_r)
+{
+ UINT16 res = 0;
+
+ // Any I/O activity clears IRQ
+ irq_w(false);
+
+ switch (offset) {
+ case 0:
+ // Data register
+ res = m_data_reg;
+ break;
+
+ case 1:
+ // Command & status register
+ res = (m_cmd_reg & CMD_REG_MASK) | (m_status_reg & STATUS_REG_MASK);
+ break;
+
+ case 2:
+ // Tachometer register
+ res = m_tach_reg;
+ break;
+
+ case 3:
+ // Checksum register: it clears when read
+ res = m_checksum_reg;
+ m_checksum_reg = 0;
+ break;
+ }
+
+ LOG_0(("rd R%u = %04x\n", 4 + offset , res));
+
+ return res;
+}
+
+READ_LINE_MEMBER(hp_taco_device::flg_r)
+{
+ return m_flg;
+}
+
+READ_LINE_MEMBER(hp_taco_device::sts_r)
+{
+ return m_sts;
+}
+
+// device_config_complete
+void hp_taco_device::device_config_complete()
+{
+ LOG(("device_config_complete"));
+ update_names();
+}
+
+// device_start
+void hp_taco_device::device_start()
+{
+ LOG(("device_start"));
+ m_irq_handler.resolve_safe();
+ m_flg_handler.resolve_safe();
+ m_sts_handler.resolve_safe();
+
+ save_item(NAME(m_data_reg));
+ save_item(NAME(m_data_reg_full));
+ save_item(NAME(m_cmd_reg));
+ save_item(NAME(m_cmd_state));
+ save_item(NAME(m_status_reg));
+ save_item(NAME(m_tach_reg));
+ save_item(NAME(m_checksum_reg));
+ save_item(NAME(m_timing_reg));
+ save_item(NAME(m_irq));
+ save_item(NAME(m_flg));
+ save_item(NAME(m_sts));
+ save_item(NAME(m_tape_pos));
+ save_item(NAME(m_start_time));
+ save_item(NAME(m_tape_fwd));
+ save_item(NAME(m_tape_fast));
+ save_item(NAME(m_image_dirty));
+ save_item(NAME(m_tape_wr));
+ save_item(NAME(m_rw_pos));
+ save_item(NAME(m_next_word));
+ save_item(NAME(m_rd_it_valid));
+ save_item(NAME(m_gap_detect_start));
+
+ m_tape_timer = timer_alloc(TAPE_TMR_ID);
+ m_hole_timer = timer_alloc(HOLE_TMR_ID);
+}
+
+// device_stop
+void hp_taco_device::device_stop()
+{
+}
+
+// device_reset
+void hp_taco_device::device_reset()
+{
+ LOG(("device_reset"));
+ clear_state();
+
+ m_irq = false;
+ m_flg = true;
+
+ m_irq_handler(false);
+ m_flg_handler(true);
+ set_error(false);
+}
+
+void hp_taco_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (CMD_CODE(m_cmd_reg) != CMD_STOP) {
+ update_tape_pos();
+ }
+
+ switch (id) {
+ case TAPE_TMR_ID:
+ LOG_0(("Tape tmr @%g\n" , machine().time().as_double()));
+
+ tape_pos_t length;
+
+ switch (CMD_CODE(m_cmd_reg)) {
+ case CMD_INDTA_INGAP:
+ if (m_cmd_state == 0) {
+ m_cmd_state = 1;
+ tape_pos_t target = m_tape_pos;
+ if (next_n_gap(target, 1, MIN_IRG_LENGTH)) {
+ m_tape_timer->adjust(time_to_target(target));
+ }
+ return;
+ }
+ break;
+
+ case CMD_RECORD_WRITE:
+ if (m_cmd_state == 0) {
+ if (m_rd_it->second == PREAMBLE_WORD) {
+ LOG_0(("Got preamble\n"));
+ m_cmd_state = 1;
+ // m_rw_pos already at correct position
+ m_tape_timer->adjust(fetch_next_wr_word());
+ break;
+ } else {
+ adv_res_t res = adv_it(m_rd_it);
+ if (res != ADV_NO_MORE_DATA) {
+ m_tape_timer->adjust(time_to_rd_next_word(m_rw_pos));
+ }
+ // No IRQ
+ return;
+ }
+ }
+ // Intentional fall-through
+ case CMD_INIT_WRITE:
+ write_word(m_rw_pos , m_next_word , length);
+ pos_offset(m_rw_pos , length);
+ // Just to be sure..
+ m_tape_pos = m_rw_pos;
+ m_tape_timer->adjust(fetch_next_wr_word());
+ break;
+
+ case CMD_STOP:
+ move_tape_pos(m_tape_fast ? FAST_BRAKE_DIST : SLOW_BRAKE_DIST);
+ stop_tape();
+ break;
+
+ case CMD_INGAP_MOVE:
+ if (m_cmd_state == 0) {
+ m_cmd_state = 1;
+ m_tape_timer->adjust(time_to_tach_pulses());
+ return;
+ }
+ break;
+
+ case CMD_FINAL_GAP:
+ case CMD_WRITE_IRG:
+ write_gap(m_rw_pos , m_tape_pos);
+ m_hole_timer->reset();
+ break;
+
+ case CMD_SCAN_RECORDS:
+ if (m_cmd_state == 0) {
+ m_cmd_state = 1;
+ tape_pos_t target = m_tape_pos;
+ if (next_n_gap(target, 0x10000U - m_tach_reg, MIN_IRG_LENGTH)) {
+ LOG_0(("%u gaps @%d\n" , 0x10000U - m_tach_reg, target));
+ m_tape_timer->adjust(time_to_target(target));
+ }
+ return;
+ } else {
+ m_hole_timer->reset();
+ }
+ break;
+
+ case CMD_MOVE_INDTA:
+ if (m_cmd_state == 0) {
+ if (next_data(m_rd_it , m_tape_pos , true)) {
+ m_cmd_state = 1;
+ m_tape_timer->adjust(time_to_target(farthest_end(m_rd_it)));
+ }
+ // No IRQ
+ return;
+ }
+ // m_cmd_state == 1 -> IRQ & cmd end
+ break;
+
+ case CMD_DELTA_MOVE_HOLE:
+ case CMD_DELTA_MOVE_IRG:
+ // Interrupt at end of movement
+ m_hole_timer->reset();
+ break;
+
+ case CMD_START_READ:
+ {
+ bool set_intr = true;
+ // Just to be sure..
+ m_tape_pos = m_rw_pos;
+ if (m_cmd_state == 0) {
+ set_intr = false;
+ if (m_rd_it->second == PREAMBLE_WORD) {
+ m_cmd_state = 1;
+ }
+ LOG_0(("Got preamble\n"));
+ } else {
+ m_data_reg = m_rd_it->second;
+ m_checksum_reg += m_data_reg;
+ LOG_0(("RD %04x\n" , m_data_reg));
+ }
+ adv_res_t res = adv_it(m_rd_it);
+ LOG_0(("adv_it %d\n" , res));
+ if (res == ADV_NO_MORE_DATA) {
+ m_rd_it_valid = false;
+ } else {
+ if (res == ADV_DISCONT_DATA) {
+ // Hit a gap, restart preamble search
+ m_cmd_state = 0;
+ }
+ m_tape_timer->adjust(time_to_rd_next_word(m_rw_pos));
+ }
+ if (!set_intr) {
+ return;
+ }
+ }
+ break;
+
+ case CMD_END_READ:
+ {
+ m_tape_pos = m_rw_pos;
+ // Note: checksum is not updated
+ m_data_reg = m_rd_it->second;
+ LOG_0(("Final RD %04x\n" , m_data_reg));
+ adv_res_t res = adv_it(m_rd_it);
+ if (res == ADV_NO_MORE_DATA) {
+ m_rd_it_valid = false;
+ }
+ m_hole_timer->reset();
+ }
+ break;
+
+ default:
+ // Other commands: just raise irq
+ break;
+ }
+ irq_w(true);
+ break;
+
+ case HOLE_TMR_ID:
+ LOG_0(("Hole tmr @%g\n" , machine().time().as_double()));
+
+ BIT_SET(m_status_reg , STATUS_HOLE_BIT);
+
+ switch (CMD_CODE(m_cmd_reg)) {
+ case CMD_FINAL_GAP:
+ case CMD_WRITE_IRG:
+ write_gap(m_rw_pos , m_tape_pos);
+ m_rw_pos = m_tape_pos;
+ break;
+
+ case CMD_SCAN_RECORDS:
+ case CMD_DELTA_MOVE_HOLE:
+ // Cmds 18 & 1c are terminated at first hole
+ m_tape_timer->reset();
+ irq_w(true);
+ // No reloading of hole timer
+ return;
+
+ case CMD_DELTA_MOVE_IRG:
+ // TODO: update r6
+ m_hole_timer->adjust(time_to_next_hole());
+ // No IRQ at holes
+ return;
+
+ case CMD_START_READ:
+ case CMD_END_READ:
+ set_error(true);
+ break;
+
+ default:
+ // Other cmds: default processing (update tape pos, set IRQ, schedule timer for next hole)
+ break;
+ }
+
+ irq_w(true);
+ m_hole_timer->adjust(time_to_next_hole());
+ break;
+
+ default:
+ break;
+ }
+}
+
+void hp_taco_device::clear_state(void)
+{
+ m_data_reg = 0;
+ m_data_reg_full = false;
+ m_cmd_reg = 0;
+ m_status_reg = 0;
+ m_tach_reg = 0;
+ m_checksum_reg = 0;
+ m_timing_reg = 0;
+ m_cmd_state = 0;
+ // m_tape_pos is not reset, tape stays where it is
+ m_start_time = attotime::never;
+ m_tape_fwd = false;
+ m_tape_fast = false;
+ // m_image_dirty is not touched
+ m_tape_wr = false;
+ m_rw_pos = 0;
+ m_next_word = 0;
+ m_rd_it_valid = false;
+ m_gap_detect_start = NULL_TAPE_POS;
+
+ set_tape_present(false);
+ set_tape_present(is_loaded());
+}
+
+void hp_taco_device::irq_w(bool state)
+{
+ if (state != m_irq) {
+ m_irq = state;
+ m_irq_handler(state);
+ LOG_0(("IRQ = %d\n" , state));
+ }
+}
+
+void hp_taco_device::set_error(bool state)
+{
+ m_sts = !state;
+ m_sts_handler(m_sts);
+ LOG_0(("error = %d\n" , state));
+}
+
+unsigned hp_taco_device::speed_to_tick_freq(void) const
+{
+ return m_tape_fast ? TACH_FREQ_FAST * TAPE_POS_FRACT : TACH_FREQ_SLOW * TAPE_POS_FRACT;
+}
+
+bool hp_taco_device::pos_offset(tape_pos_t& pos , tape_pos_t offset) const
+{
+ if (offset == 0) {
+ return true;
+ }
+
+ if (!m_tape_fwd) {
+ offset = -offset;
+ }
+
+ pos += offset;
+
+ // In real life tape would unspool..
+ if (pos > TAPE_LENGTH) {
+ pos = TAPE_LENGTH;
+ return false;
+ } else if (pos < 0) {
+ pos = 0;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void hp_taco_device::move_tape_pos(tape_pos_t delta_pos)
+{
+ tape_pos_t tape_start_pos = m_tape_pos;
+ if (!pos_offset(m_tape_pos , delta_pos)) {
+ LOG(("Tape unspooled!\n"));
+ }
+ m_start_time = machine().time();
+ LOG_0(("Tape pos = %u\n" , m_tape_pos));
+ if (any_hole(tape_start_pos , m_tape_pos)) {
+ // Crossed one or more holes
+ BIT_SET(m_status_reg , STATUS_HOLE_BIT);
+ }
+}
+
+void hp_taco_device::update_tape_pos(void)
+{
+ if (m_start_time.is_never()) {
+ // Tape not moving
+ return;
+ }
+
+ attotime delta_time(machine().time() - m_start_time);
+ LOG_0(("delta_time = %g\n" , delta_time.as_double()));
+ // How many tachometer ticks has the tape moved?
+ tape_pos_t delta_tach = (tape_pos_t)(delta_time.as_ticks(speed_to_tick_freq()));
+ LOG_0(("delta_tach = %u\n" , delta_tach));
+
+ move_tape_pos(delta_tach);
+
+ // Gap detection
+ bool gap_detected = false;
+ if (m_gap_detect_start != NULL_TAPE_POS && abs(m_gap_detect_start - m_tape_pos) >= MIN_IRG_LENGTH) {
+ tape_pos_t tmp = m_tape_pos;
+ pos_offset(tmp , -MIN_IRG_LENGTH);
+ gap_detected = just_gap(tmp , m_tape_pos);
+ }
+ if (gap_detected) {
+ BIT_SET(m_status_reg, STATUS_GAP_BIT);
+ } else {
+ BIT_CLR(m_status_reg, STATUS_GAP_BIT);
+ }
+}
+
+void hp_taco_device::ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b)
+{
+ if (a > b) {
+ // Ensure A always comes before B
+ tape_pos_t tmp;
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+}
+
+// Is there any hole in a given section of tape?
+bool hp_taco_device::any_hole(tape_pos_t tape_pos_a , tape_pos_t tape_pos_b)
+{
+ ensure_a_lt_b(tape_pos_a , tape_pos_b);
+
+ for (tape_pos_t hole : tape_holes) {
+ if (tape_pos_a < hole && tape_pos_b >= hole) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Position of next hole tape will reach in a given direction
+hp_taco_device::tape_pos_t hp_taco_device::next_hole(void) const
+{
+ if (m_tape_fwd) {
+ for (tape_pos_t hole : tape_holes) {
+ if (hole > m_tape_pos) {
+ LOG_0(("next hole fwd @%u = %u\n" , m_tape_pos , hole));
+ return hole;
+ }
+ }
+ // No more holes: will hit end of tape
+ return TAPE_LENGTH;
+ } else {
+ for (int i = (sizeof(tape_holes) / sizeof(tape_holes[ 0 ])) - 1; i >= 0; i--) {
+ if (tape_holes[ i ] < m_tape_pos) {
+ LOG_0(("next hole rev @%u = %u\n" , m_tape_pos , tape_holes[ i ]));
+ return tape_holes[ i ];
+ }
+ }
+ // No more holes: will hit start of tape
+ return 0;
+ }
+}
+
+attotime hp_taco_device::time_to_distance(tape_pos_t distance) const
+{
+ // +1 for rounding
+ return attotime::from_ticks(distance + 1 , speed_to_tick_freq());
+}
+
+attotime hp_taco_device::time_to_target(tape_pos_t target) const
+{
+ return time_to_distance(abs(target - m_tape_pos));
+}
+
+bool hp_taco_device::start_tape_cmd(UINT16 cmd_reg , UINT16 must_be_1 , UINT16 must_be_0)
+{
+ m_cmd_reg = cmd_reg;
+
+ UINT16 to_be_tested = (m_cmd_reg & CMD_REG_MASK) | (m_status_reg & STATUS_REG_MASK);
+ // Bits in STATUS_ERR_MASK must always be 0
+ must_be_0 |= STATUS_ERR_MASK;
+
+ // It's not an error if the error state is already set (sts false)
+ if (((to_be_tested & (must_be_1 | must_be_0)) ^ must_be_1) != 0) {
+ set_error(true);
+ return false;
+ } else {
+ bool prev_tape_wr = m_tape_wr;
+ bool prev_tape_fwd = m_tape_fwd;
+ bool prev_tape_fast = m_tape_fast;
+ bool not_moving = m_start_time.is_never();
+
+ m_start_time = machine().time();
+ m_tape_wr = (must_be_0 & STATUS_WPR_MASK) != 0;
+ m_tape_fwd = DIR_FWD(m_cmd_reg);
+ m_tape_fast = SPEED_FAST(m_cmd_reg);
+ // TODO: remove?
+ BIT_CLR(m_status_reg, STATUS_HOLE_BIT);
+
+ if (m_tape_wr) {
+ // Write command: disable gap detector
+ m_gap_detect_start = NULL_TAPE_POS;
+ BIT_CLR(m_status_reg, STATUS_GAP_BIT);
+ m_image_dirty = true;
+ } else if (not_moving || prev_tape_wr != m_tape_wr || prev_tape_fwd != m_tape_fwd || prev_tape_fast != m_tape_fast) {
+ // Tape started right now, switched from writing to reading, direction changed or speed changed: (re)start gap detector
+ m_gap_detect_start = m_tape_pos;
+ BIT_CLR(m_status_reg, STATUS_GAP_BIT);
+ }
+ return true;
+ }
+}
+
+void hp_taco_device::stop_tape(void)
+{
+ m_start_time = attotime::never;
+ m_gap_detect_start = NULL_TAPE_POS;
+}
+
+hp_taco_device::tape_track_t& hp_taco_device::current_track(void)
+{
+ return m_tracks[ BIT(m_status_reg , STATUS_TRACKB_BIT) ];
+}
+
+// Return physical length of a 16-bit word on tape
+hp_taco_device::tape_pos_t hp_taco_device::word_length(tape_word_t w)
+{
+ unsigned zeros , ones;
+
+ // pop count of w
+ ones = (w & 0x5555) + ((w >> 1) & 0x5555);
+ ones = (ones & 0x3333) + ((ones >> 2) & 0x3333);
+ ones = (ones & 0x0f0f) + ((ones >> 4) & 0x0f0f);
+ ones = (ones & 0x00ff) + ((ones >> 8) & 0x00ff);
+
+ zeros = 16 - ones;
+
+ return zeros * ZERO_BIT_LEN + (ones + 1) * ONE_BIT_LEN;
+}
+
+hp_taco_device::tape_pos_t hp_taco_device::word_end_pos(const tape_track_t::iterator& it)
+{
+ return it->first + word_length(it->second);
+}
+
+void hp_taco_device::adjust_it(tape_track_t& track , tape_track_t::iterator& it , tape_pos_t pos)
+{
+ if (it != track.begin()) {
+ it--;
+ if (word_end_pos(it) <= pos) {
+ it++;
+ }
+ }
+}
+
+// Write a word on current tape track
+void hp_taco_device::write_word(tape_pos_t start , tape_word_t word , tape_pos_t& length)
+{
+ tape_track_t& track = current_track();
+ tape_track_t::iterator it_low = track.lower_bound(start);
+ adjust_it(track , it_low , start);
+ length = word_length(word);
+ tape_pos_t end_pos = start + length;
+ tape_track_t::iterator it_high = track.lower_bound(end_pos);
+
+ track.erase(it_low , it_high);
+
+ track.insert(it_high , std::make_pair(start, word));
+ LOG_0(("WR %04x @ T%u:%u\n" , word , BIT(m_status_reg , STATUS_TRACKB_BIT) , start));
+}
+
+// Write a gap on current track
+void hp_taco_device::write_gap(tape_pos_t a , tape_pos_t b)
+{
+ ensure_a_lt_b(a , b);
+ tape_track_t& track = current_track();
+ tape_track_t::iterator it_low = track.lower_bound(a);
+ adjust_it(track , it_low , a);
+ tape_track_t::iterator it_high = track.lower_bound(b);
+
+ track.erase(it_low, it_high);
+
+ LOG_0(("GAP on T%u:[%u,%u)\n" , BIT(m_status_reg , STATUS_TRACKB_BIT) , a , b));
+}
+
+bool hp_taco_device::just_gap(tape_pos_t a , tape_pos_t b)
+{
+ ensure_a_lt_b(a , b);
+ tape_track_t& track = current_track();
+ tape_track_t::iterator it_low = track.lower_bound(a);
+ tape_track_t::iterator it_high = track.lower_bound(b);
+
+ adjust_it(track, it_low, a);
+
+ return it_low == it_high;
+}
+
+hp_taco_device::tape_pos_t hp_taco_device::farthest_end(const tape_track_t::iterator& it) const
+{
+ if (m_tape_fwd) {
+ return word_end_pos(it);
+ } else {
+ return it->first;
+ }
+}
+
+bool hp_taco_device::next_data(tape_track_t::iterator& it , tape_pos_t pos , bool inclusive)
+{
+ tape_track_t& track = current_track();
+ it = track.lower_bound(pos);
+ if (m_tape_fwd) {
+ if (inclusive) {
+ adjust_it(track, it, pos);
+ }
+ return it != track.end();
+ } else {
+ // Never more than 2 iterations
+ do {
+ if (it == track.begin()) {
+ it = track.end();
+ return false;
+ }
+ it--;
+ } while (!inclusive && word_end_pos(it) > pos);
+ return true;
+ }
+}
+
+hp_taco_device::adv_res_t hp_taco_device::adv_it(tape_track_t::iterator& it)
+{
+ tape_track_t& track = current_track();
+ if (m_tape_fwd) {
+ tape_pos_t prev_pos = word_end_pos(it);
+ it++;
+ if (it == track.end()) {
+ return ADV_NO_MORE_DATA;
+ } else {
+ adv_res_t res = prev_pos == it->first ? ADV_CONT_DATA : ADV_DISCONT_DATA;
+ return res;
+ }
+ } else {
+ if (it == track.begin()) {
+ it = track.end();
+ return ADV_NO_MORE_DATA;
+ } else {
+ tape_pos_t prev_pos = it->first;
+ it--;
+ return prev_pos == word_end_pos(it) ? ADV_CONT_DATA : ADV_DISCONT_DATA;
+ }
+ }
+}
+
+attotime hp_taco_device::fetch_next_wr_word(void)
+{
+ if (m_data_reg_full) {
+ m_next_word = m_data_reg;
+ m_data_reg_full = false;
+ LOG_0(("next %04x (DR)\n" , m_next_word));
+ } else {
+ // When data register is empty, write checksum word
+ m_next_word = m_checksum_reg;
+ LOG_0(("next %04x (CS)\n" , m_next_word));
+ }
+ // Update checksum with new word
+ m_checksum_reg += m_next_word;
+
+ return time_to_distance(word_length(m_next_word));
+}
+
+attotime hp_taco_device::time_to_rd_next_word(tape_pos_t& word_rd_pos)
+{
+ if (m_rd_it_valid) {
+ word_rd_pos = farthest_end(m_rd_it);
+ return time_to_target(word_rd_pos);
+ } else {
+ return attotime::never;
+ }
+}
+
+/**
+ * Scan for next "n_gaps" gaps
+ *
+ * @param[in,out] pos Start position on input, start of gap on output
+ * @param it Pointer to data word where scan is to start
+ * @param n_gaps Number of gaps to scan
+ * @param min_gap Minimum gap size
+ *
+ * @return true if n_gaps gaps are found
+ */
+bool hp_taco_device::next_n_gap(tape_pos_t& pos , tape_track_t::iterator it , unsigned n_gaps , tape_pos_t min_gap)
+{
+ tape_track_t& track = current_track();
+ bool done = false;
+ tape_track_t::iterator prev_it;
+
+ if (m_tape_fwd) {
+ tape_pos_t next_pos;
+
+ while (1) {
+ if (it == track.end()) {
+ next_pos = TAPE_LENGTH;
+ done = true;
+ } else {
+ next_pos = it->first;
+ }
+ if (((next_pos - pos) >= min_gap && --n_gaps == 0) || done) {
+ break;
+ }
+ adv_res_t adv_res;
+ do {
+ prev_it = it;
+ adv_res = adv_it(it);
+ } while (adv_res == ADV_CONT_DATA);
+ pos = word_end_pos(prev_it);
+ }
+ } else {
+ tape_pos_t next_pos;
+
+ while (1) {
+ if (it == track.end()) {
+ next_pos = 0;
+ done = true;
+ } else {
+ next_pos = word_end_pos(it);
+ }
+ if (((pos - next_pos) >= min_gap && --n_gaps == 0) || done) {
+ break;
+ }
+ adv_res_t adv_res;
+ do {
+ prev_it = it;
+ adv_res = adv_it(it);
+ } while (adv_res == ADV_CONT_DATA);
+ pos = prev_it->first;
+ }
+ }
+
+ // Set "pos" where minimum gap size is met
+ pos_offset(pos , min_gap);
+
+ return n_gaps == 0;
+}
+
+bool hp_taco_device::next_n_gap(tape_pos_t& pos , unsigned n_gaps , tape_pos_t min_gap)
+{
+ tape_track_t::iterator it;
+ // First align with next data
+ next_data(it, pos, true);
+ // Then scan for n_gaps
+ return next_n_gap(pos, it, n_gaps, min_gap);
+}
+
+void hp_taco_device::clear_tape(void)
+{
+ for (unsigned track_n = 0; track_n < 2; track_n++) {
+ m_tracks[ track_n ].clear();
+ }
+}
+
+void hp_taco_device::dump_sequence(tape_track_t::const_iterator it_start , unsigned n_words)
+{
+ if (n_words) {
+ UINT32 tmp32;
+ UINT16 tmp16;
+
+ tmp32 = n_words;
+ fwrite(&tmp32 , sizeof(tmp32));
+ tmp32 = it_start->first;
+ fwrite(&tmp32 , sizeof(tmp32));
+
+ for (unsigned i = 0; i < n_words; i++) {
+ tmp16 = it_start->second;
+ fwrite(&tmp16 , sizeof(tmp16));
+ it_start++;
+ }
+ }
+}
+
+void hp_taco_device::save_tape(void)
+{
+ UINT32 tmp32;
+
+ fseek(0, SEEK_SET);
+
+ tmp32 = FILE_MAGIC;
+ fwrite(&tmp32 , sizeof(tmp32));
+
+ for (unsigned track_n = 0; track_n < 2; track_n++) {
+ const tape_track_t& track = m_tracks[ track_n ];
+ tape_pos_t next_pos = (tape_pos_t)-1;
+ unsigned n_words = 0;
+ tape_track_t::const_iterator it_start;
+ for (tape_track_t::const_iterator it = track.cbegin(); it != track.cend(); it++) {
+ if (it->first != next_pos) {
+ dump_sequence(it_start , n_words);
+ it_start = it;
+ n_words = 0;
+ }
+ next_pos = it->first + word_length(it->second);
+ n_words++;
+ }
+ dump_sequence(it_start , n_words);
+ // End of track
+ tmp32 = (UINT32)-1;
+ fwrite(&tmp32 , sizeof(tmp32));
+ }
+}
+
+bool hp_taco_device::load_track(tape_track_t& track)
+{
+ UINT32 tmp32;
+
+ track.clear();
+
+ while (1) {
+ if (fread(&tmp32 , sizeof(tmp32)) != sizeof(tmp32)) {
+ return false;
+ }
+
+ if (tmp32 == (UINT32)-1) {
+ return true;
+ }
+
+ unsigned n_words = tmp32;
+
+ if (fread(&tmp32 , sizeof(tmp32)) != sizeof(tmp32)) {
+ return false;
+ }
+
+ tape_pos_t pos = (tape_pos_t)tmp32;
+
+ for (unsigned i = 0; i < n_words; i++) {
+ UINT16 tmp16;
+
+ if (fread(&tmp16 , sizeof(tmp16)) != sizeof(tmp16)) {
+ return false;
+ }
+
+ track.insert(std::make_pair(pos , tmp16));
+ pos += word_length(tmp16);
+ }
+ }
+}
+
+bool hp_taco_device::load_tape(void)
+{
+ UINT32 magic;
+
+ if (fread(&magic , sizeof(magic)) != sizeof(magic) ||
+ magic != FILE_MAGIC) {
+ return false;
+ }
+
+ for (unsigned track_n = 0; track_n < 2; track_n++) {
+ if (!load_track(m_tracks[ track_n ])) {
+ LOG(("load_tape failed"));
+ clear_tape();
+ return false;
+ }
+ }
+
+ LOG(("load_tape done\n"));
+ return true;
+}
+
+void hp_taco_device::set_tape_present(bool present)
+{
+ if (present) {
+ if (is_readonly()) {
+ BIT_SET(m_status_reg, STATUS_WPR_BIT);
+ } else {
+ BIT_CLR(m_status_reg, STATUS_WPR_BIT);
+ }
+ // STATUS_CART_OUT_BIT is reset by CMD_CLEAR
+ } else {
+ BIT_SET(m_status_reg, STATUS_CART_OUT_BIT);
+ BIT_SET(m_status_reg, STATUS_WPR_BIT);
+ }
+}
+
+attotime hp_taco_device::time_to_next_hole(void) const
+{
+ return time_to_target(next_hole());
+}
+
+attotime hp_taco_device::time_to_tach_pulses(void) const
+{
+ return time_to_distance((tape_pos_t)(0x10000U - m_tach_reg) * TAPE_POS_FRACT);
+}
+
+void hp_taco_device::start_cmd_exec(UINT16 new_cmd_reg)
+{
+ LOG(("Cmd = %02x\n" , CMD_CODE(new_cmd_reg)));
+
+ update_tape_pos();
+
+ attotime cmd_duration = attotime::never;
+ attotime time_to_hole = attotime::never;
+
+ unsigned new_cmd_code = CMD_CODE(new_cmd_reg);
+
+ if (new_cmd_code != CMD_START_READ &&
+ new_cmd_code != CMD_END_READ &&
+ new_cmd_code != CMD_CLEAR) {
+ m_rd_it_valid = false;
+ }
+
+ switch (new_cmd_code) {
+ case CMD_INDTA_INGAP:
+ // Errors: CART OUT,FAST SPEED
+ if (start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK)) {
+ m_cmd_state = 0;
+ if (next_data(m_rd_it , m_tape_pos , true)) {
+ cmd_duration = time_to_target(farthest_end(m_rd_it));
+ }
+ }
+ break;
+
+ case CMD_FINAL_GAP:
+ // Errors: WP,CART OUT
+ if (start_tape_cmd(new_cmd_reg , 0 , STATUS_WPR_MASK)) {
+ m_rw_pos = m_tape_pos;
+ cmd_duration = time_to_distance(END_GAP_LENGTH);
+ time_to_hole = time_to_next_hole();
+ }
+ break;
+
+ case CMD_CLEAR:
+ set_error(false);
+ BIT_CLR(m_status_reg, STATUS_HOLE_BIT);
+ BIT_CLR(m_status_reg, STATUS_CART_OUT_BIT);
+ BIT_CLR(m_status_reg, STATUS_WPR_BIT);
+ set_tape_present(is_loaded());
+ // This is a special command: it doesn't raise IRQ at completion and it
+ // doesn't replace current command
+ return;
+
+ case CMD_NOT_INDTA:
+ // Errors: CART OUT,FAST SPEED
+ if (start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK)) {
+ tape_pos_t target = m_tape_pos;
+ if (next_n_gap(target, 1, NO_DATA_GAP)) {
+ LOG_0(("End of data @%d\n" , target));
+ cmd_duration = time_to_target(target);
+ }
+ // Holes detected?
+ }
+ break;
+
+ case CMD_INIT_WRITE:
+ // Errors: WP,CART OUT,fast speed,reverse
+ if (start_tape_cmd(new_cmd_reg , DIR_FWD_MASK , STATUS_WPR_MASK | SPEED_FAST_MASK)) {
+ m_next_word = PREAMBLE_WORD;
+ m_rw_pos = m_tape_pos;
+ cmd_duration = time_to_distance(word_length(m_next_word));
+ }
+ break;
+
+ case CMD_STOP:
+ if (CMD_CODE(m_cmd_reg) != CMD_STOP) {
+ if (m_start_time.is_never()) {
+ // Tape is already stopped
+ cmd_duration = attotime::from_usec(QUICK_CMD_USEC);
+ } else {
+ // Start braking timer
+ cmd_duration = attotime::from_msec(m_tape_fast ? FAST_BRAKE_MSEC : SLOW_BRAKE_MSEC);
+ }
+ m_cmd_reg = new_cmd_reg;
+ } else {
+ // TODO: check if ok
+ return;
+ }
+ break;
+
+ case CMD_SET_TRACK:
+ // Don't know if this command really starts the tape or not (probably it doesn't)
+ if (start_tape_cmd(new_cmd_reg , 0 , 0)) {
+ // When b9 is 0, set track A/B
+ // When b9 is 1, ignore command (in TACO chip it has an unknown purpose)
+ if (!UNKNOWN_B9(new_cmd_reg)) {
+ if (CMD_OPT(new_cmd_reg)) {
+ BIT_SET(m_status_reg, STATUS_TRACKB_BIT);
+ } else {
+ BIT_CLR(m_status_reg, STATUS_TRACKB_BIT);
+ }
+ }
+ cmd_duration = attotime::from_usec(QUICK_CMD_USEC);
+ }
+ break;
+
+ case CMD_MOVE:
+ if (start_tape_cmd(new_cmd_reg , 0 , 0)) {
+ time_to_hole = time_to_next_hole();
+ }
+ break;
+
+ case CMD_INGAP_MOVE:
+ // Errors: CART OUT,FAST SPEED
+ if (start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK)) {
+ m_cmd_state = 0;
+ tape_pos_t target = m_tape_pos;
+ if (next_n_gap(target, 1, MIN_IRG_LENGTH)) {
+ LOG_0(("IRG @%d\n" , target));
+ cmd_duration = time_to_target(target);
+ }
+ // Holes detected?
+ }
+ break;
+
+ case CMD_WRITE_IRG:
+ // Errors: WP,CART OUT
+ if (start_tape_cmd(new_cmd_reg , 0 , STATUS_WPR_MASK)) {
+ m_rw_pos = m_tape_pos;
+ cmd_duration = time_to_tach_pulses();
+ time_to_hole = time_to_next_hole();
+ }
+ break;
+
+ case CMD_SCAN_RECORDS:
+ // Errors: CART OUT
+ if (start_tape_cmd(new_cmd_reg , 0 , 0)) {
+ m_cmd_state = 0;
+ if (next_data(m_rd_it , m_tape_pos , true)) {
+ cmd_duration = time_to_target(farthest_end(m_rd_it));
+ }
+ time_to_hole = time_to_next_hole();
+ }
+ break;
+
+ case CMD_RECORD_WRITE:
+ // Errors: WP,CART OUT,fast speed,reverse
+ if (start_tape_cmd(new_cmd_reg , DIR_FWD_MASK , STATUS_WPR_MASK | SPEED_FAST_MASK)) {
+ // Search for preamble first
+ m_cmd_state = 0;
+ m_rd_it_valid = next_data(m_rd_it , m_tape_pos , false);
+ cmd_duration = time_to_rd_next_word(m_rw_pos);
+ // Holes detected?
+ }
+ break;
+
+ case CMD_MOVE_INDTA:
+ // Errors: CART OUT,FAST SPEED
+ if (start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK)) {
+ m_cmd_state = 0;
+ cmd_duration = time_to_tach_pulses();
+ // Holes detected?
+ }
+ break;
+
+ case CMD_UNK_1b:
+ if (start_tape_cmd(new_cmd_reg , 0 , 0)) {
+ // Unknown purpose, but make it a NOP (it's used in "T" test of test ROM)
+ cmd_duration = attotime::from_usec(QUICK_CMD_USEC);
+ }
+ break;
+
+ case CMD_DELTA_MOVE_HOLE:
+ case CMD_DELTA_MOVE_IRG:
+ if (start_tape_cmd(new_cmd_reg , 0 , 0)) {
+ cmd_duration = time_to_tach_pulses();
+ time_to_hole = time_to_next_hole();
+ }
+ break;
+
+ case CMD_START_READ:
+ // Yes, you can read tape backwards: test "C" does that!
+ // Because of this DIR_FWD_MASK is not in the "must be 1" mask.
+ if (start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK)) {
+ // TODO: check anche m_rw_pos sforato
+ if (!m_rd_it_valid) {
+ // Search for preamble first
+ m_cmd_state = 0;
+ m_rd_it_valid = next_data(m_rd_it , m_tape_pos , false);
+ }
+
+ cmd_duration = time_to_rd_next_word(m_rw_pos);
+ time_to_hole = time_to_next_hole();
+ }
+ break;
+
+ case CMD_END_READ:
+ // This command only makes sense after CMD_START_READ
+ if (CMD_CODE(m_cmd_reg) == CMD_START_READ && m_cmd_state == 1 &&
+ start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK)) {
+ LOG_0(("END_READ %d\n" , m_rd_it_valid));
+ cmd_duration = time_to_rd_next_word(m_rw_pos);
+ time_to_hole = time_to_next_hole();
+ }
+ break;
+
+ default:
+ LOG(("Unrecognized command\n"));
+ break;
+ }
+
+ m_tape_timer->adjust(cmd_duration);
+ m_hole_timer->adjust(time_to_hole);
+}
+
+bool hp_taco_device::call_load()
+{
+ LOG(("call_load %d\n" , has_been_created()));
+ if (has_been_created()) {
+ clear_tape();
+ save_tape();
+ } else if (!load_tape()) {
+ seterror(IMAGE_ERROR_INVALIDIMAGE , "Wrong format");
+ set_tape_present(false);
+ return IMAGE_INIT_FAIL;
+ }
+
+ m_image_dirty = false;
+
+ set_tape_present(true);
+ return IMAGE_INIT_PASS;
+}
+
+bool hp_taco_device::call_create(int format_type, option_resolution *format_options)
+{
+ LOG(("call_create %d\n" , has_been_created()));
+ return call_load();
+}
+
+void hp_taco_device::call_unload()
+{
+ LOG(("call_unload dirty=%d\n" , m_image_dirty));
+ if (m_image_dirty) {
+ save_tape();
+ m_image_dirty = false;
+ }
+
+ clear_tape();
+ set_tape_present(false);
+}
+
+const char *hp_taco_device::file_extensions() const
+{
+ return "hti";
+}
diff --git a/src/devices/machine/hp_taco.h b/src/devices/machine/hp_taco.h
new file mode 100644
index 00000000000..c8b794c79a2
--- /dev/null
+++ b/src/devices/machine/hp_taco.h
@@ -0,0 +1,169 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/*********************************************************************
+
+ hp_taco.h
+
+ HP TApe COntroller (5006-3012)
+
+*********************************************************************/
+
+#ifndef __HP_TACO_H__
+#define __HP_TACO_H__
+
+#include <map>
+
+#define MCFG_TACO_IRQ_HANDLER(_devcb) \
+ devcb = &hp_taco_device::set_irq_handler(*device , DEVCB_##_devcb);
+
+#define MCFG_TACO_FLG_HANDLER(_devcb) \
+ devcb = &hp_taco_device::set_flg_handler(*device , DEVCB_##_devcb);
+
+#define MCFG_TACO_STS_HANDLER(_devcb) \
+ devcb = &hp_taco_device::set_sts_handler(*device , DEVCB_##_devcb);
+
+class hp_taco_device : public device_t ,
+ public device_image_interface
+{
+public:
+ // construction/destruction
+ hp_taco_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname);
+ hp_taco_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<hp_taco_device &>(device).m_irq_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_flg_handler(device_t &device, _Object object) { return downcast<hp_taco_device &>(device).m_flg_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_sts_handler(device_t &device, _Object object) { return downcast<hp_taco_device &>(device).m_sts_handler.set_callback(object); }
+
+ // Register read/write
+ DECLARE_WRITE16_MEMBER(reg_w);
+ DECLARE_READ16_MEMBER(reg_r);
+
+ // Flag & status read
+ DECLARE_READ_LINE_MEMBER(flg_r);
+ DECLARE_READ_LINE_MEMBER(sts_r);
+
+ // device_image_interface overrides
+ virtual bool call_load() override;
+ virtual bool call_create(int format_type, option_resolution *format_options) override;
+ virtual void call_unload() override;
+ virtual iodevice_t image_type() const override { return IO_MAGTAPE; }
+ virtual bool is_readable() const override { return true; }
+ virtual bool is_writeable() const override { return true; }
+ virtual bool is_creatable() const override { return true; }
+ virtual bool must_be_loaded() const override { return false; }
+ virtual bool is_reset_on_load() const override { return false; }
+ virtual const char *file_extensions() const override;
+ virtual const option_guide *create_option_guide() const override { return nullptr; }
+
+ // Tape position, 1 unit = 1 inch / (968 * 1024)
+ typedef INT32 tape_pos_t;
+
+ // Words stored on tape
+ typedef UINT16 tape_word_t;
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete() override;
+ virtual void device_start() override;
+ virtual void device_stop() override;
+ virtual void device_reset() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ // Storage of tracks: mapping from a tape position to word stored there
+ typedef std::map<tape_pos_t , tape_word_t> tape_track_t;
+
+ devcb_write_line m_irq_handler;
+ devcb_write_line m_flg_handler;
+ devcb_write_line m_sts_handler;
+
+ // Registers
+ UINT16 m_data_reg;
+ bool m_data_reg_full;
+ UINT16 m_cmd_reg;
+ UINT16 m_status_reg;
+ UINT16 m_tach_reg;
+ UINT16 m_checksum_reg;
+ UINT16 m_timing_reg;
+
+ // State
+ bool m_irq;
+ bool m_flg;
+ bool m_sts;
+ UINT8 m_cmd_state;
+
+ // Tape position & motion
+ tape_pos_t m_tape_pos;
+ attotime m_start_time; // Tape moving if != never
+ bool m_tape_fwd;
+ bool m_tape_fast;
+
+ // Timers
+ emu_timer *m_tape_timer;
+ emu_timer *m_hole_timer;
+
+ // Content of tape tracks
+ tape_track_t m_tracks[ 2 ];
+ bool m_image_dirty;
+
+ // Reading & writing
+ bool m_tape_wr;
+ tape_pos_t m_rw_pos;
+ UINT16 m_next_word;
+ tape_track_t::iterator m_rd_it;
+ bool m_rd_it_valid;
+
+ // Gap detection
+ tape_pos_t m_gap_detect_start;
+
+ typedef enum {
+ ADV_NO_MORE_DATA,
+ ADV_CONT_DATA,
+ ADV_DISCONT_DATA
+ } adv_res_t;
+
+ void clear_state(void);
+ void irq_w(bool state);
+ void set_error(bool state);
+ unsigned speed_to_tick_freq(void) const;
+ bool pos_offset(tape_pos_t& pos , tape_pos_t offset) const;
+ void move_tape_pos(tape_pos_t delta_pos);
+ void update_tape_pos(void);
+ static void ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b);
+ static bool any_hole(tape_pos_t tape_pos_a , tape_pos_t tape_pos_b);
+ tape_pos_t next_hole(void) const;
+ attotime time_to_distance(tape_pos_t distance) const;
+ attotime time_to_target(tape_pos_t target) const;
+ bool start_tape_cmd(UINT16 cmd_reg , UINT16 must_be_1 , UINT16 must_be_0);
+ void start_tape(UINT16 cmd_reg);
+ void stop_tape(void);
+ tape_track_t& current_track(void);
+ static tape_pos_t word_length(tape_word_t w);
+ static tape_pos_t word_end_pos(const tape_track_t::iterator& it);
+ static void adjust_it(tape_track_t& track , tape_track_t::iterator& it , tape_pos_t pos);
+ void write_word(tape_pos_t start , tape_word_t word , tape_pos_t& length);
+ void write_gap(tape_pos_t a , tape_pos_t b);
+ bool just_gap(tape_pos_t a , tape_pos_t b);
+ tape_pos_t farthest_end(const tape_track_t::iterator& it) const;
+ bool next_data(tape_track_t::iterator& it , tape_pos_t pos , bool inclusive);
+ adv_res_t adv_it(tape_track_t::iterator& it);
+ attotime fetch_next_wr_word(void);
+ attotime time_to_rd_next_word(tape_pos_t& word_rd_pos);
+ bool next_n_gap(tape_pos_t& pos , tape_track_t::iterator it , unsigned n_gaps , tape_pos_t min_gap);
+ bool next_n_gap(tape_pos_t& pos , unsigned n_gaps , tape_pos_t min_gap);
+ void clear_tape(void);
+ void dump_sequence(tape_track_t::const_iterator it_start , unsigned n_words);
+ void save_tape(void);
+ bool load_track(tape_track_t& track);
+ bool load_tape(void);
+ void set_tape_present(bool present);
+ attotime time_to_next_hole(void) const;
+ attotime time_to_tach_pulses(void) const;
+ void start_cmd_exec(UINT16 new_cmd_reg);
+};
+
+// device type definition
+extern const device_type HP_TACO;
+
+#endif /* __HP_TACO_H__ */
diff --git a/src/devices/machine/i8271.cpp b/src/devices/machine/i8271.cpp
index cd8c0faf5bf..dfd098a5f82 100644
--- a/src/devices/machine/i8271.cpp
+++ b/src/devices/machine/i8271.cpp
@@ -88,6 +88,7 @@ void i8271_device::soft_reset()
flopi[i].live = false;
flopi[i].ready = get_ready(i);
}
+ hdl_cb(false);
set_irq(false);
set_drq(false);
command_pos = 0;
@@ -887,6 +888,7 @@ void i8271_device::command_end(floppy_info &fi, bool data_completion)
{
logerror("%s: command done (%s) - %02x\n", tag(), data_completion ? "data" : "seek", rr);
fi.main_state = fi.sub_state = IDLE;
+ idle_icnt = 0;
main_phase = PHASE_RESULT;
set_irq(true);
}
@@ -981,6 +983,7 @@ void i8271_device::seek_continue(floppy_info &fi)
void i8271_device::read_data_start(floppy_info &fi)
{
fi.main_state = READ_DATA;
+ hdl_cb(true);
fi.sub_state = HEAD_LOAD_DONE;
logerror("%s: command read%s data%s cmd=%02x crn=(%d, %d, %d) len=%02x rate=%d\n",
@@ -1010,6 +1013,7 @@ void i8271_device::read_data_start(floppy_info &fi)
void i8271_device::scan_start(floppy_info &fi)
{
fi.main_state = SCAN_DATA;
+ hdl_cb(true);
fi.sub_state = HEAD_LOAD_DONE;
logerror("%s: command scan%s data%s cmd=%02x crn=(%d, %d, %d) len=%02x rate=%d\n",
@@ -1041,6 +1045,7 @@ void i8271_device::scan_start(floppy_info &fi)
void i8271_device::verify_data_start(floppy_info &fi)
{
fi.main_state = VERIFY_DATA;
+ hdl_cb(true);
fi.sub_state = HEAD_LOAD_DONE;
logerror("%s: command verify%s data%s cmd=%02x crn=(%d, %d, %d) len=%02x rate=%d\n",
@@ -1098,6 +1103,7 @@ void i8271_device::read_data_continue(floppy_info &fi)
return;
case SEEK_WAIT_STEP_TIME_DONE:
+ hdl_cb(true);
do {
if(fi.pcn > command[1])
fi.pcn--;
@@ -1181,7 +1187,9 @@ void i8271_device::read_data_continue(floppy_info &fi)
void i8271_device::write_data_start(floppy_info &fi)
{
fi.main_state = WRITE_DATA;
+ hdl_cb(true);
fi.sub_state = HEAD_LOAD_DONE;
+
logerror("%s: command write%s data%s cmd=%02x crn=(%d, %d, %d) len=%02x rate=%d\n",
tag(),
command[0] & 0x04 ? " deleted" : "",
@@ -1238,6 +1246,7 @@ void i8271_device::write_data_continue(floppy_info &fi)
return;
case SEEK_WAIT_STEP_TIME_DONE:
+ hdl_cb(true);
do {
if(fi.pcn > command[1])
fi.pcn--;
@@ -1311,6 +1320,7 @@ int i8271_device::calc_sector_size(UINT8 size)
void i8271_device::format_track_start(floppy_info &fi)
{
fi.main_state = FORMAT_TRACK;
+ hdl_cb(true);
fi.sub_state = HEAD_LOAD_DONE;
logerror("%s: command format track c=%02x n=%02x sc=%02x gap3=%02x gap5=%02x gap1=%02x\n",
@@ -1364,6 +1374,7 @@ void i8271_device::format_track_continue(floppy_info &fi)
return;
case SEEK_WAIT_STEP_TIME_DONE:
+ hdl_cb(true);
do {
if(fi.pcn > command[1])
fi.pcn--;
@@ -1402,6 +1413,7 @@ void i8271_device::format_track_continue(floppy_info &fi)
void i8271_device::read_id_start(floppy_info &fi)
{
fi.main_state = READ_ID;
+ hdl_cb(true);
fi.sub_state = HEAD_LOAD_DONE;
logerror("%s: command read id, rate=%d\n",
@@ -1457,6 +1469,7 @@ void i8271_device::read_id_continue(floppy_info &fi)
return;
case SEEK_WAIT_STEP_TIME_DONE:
+ hdl_cb(true);
do {
if(fi.pcn > command[1])
fi.pcn--;
@@ -1553,6 +1566,13 @@ void i8271_device::index_callback(floppy_image_device *floppy, int state)
continue;
}
+ if (fi.main_state == IDLE) {
+ idle_icnt++;
+ if (icnt != 0x0f && idle_icnt >= icnt) {
+ hdl_cb(false);
+ }
+ }
+
switch(fi.sub_state) {
case IDLE:
case SEEK_MOVE:
diff --git a/src/devices/machine/i8271.h b/src/devices/machine/i8271.h
index e4409bed389..5c63393a43f 100644
--- a/src/devices/machine/i8271.h
+++ b/src/devices/machine/i8271.h
@@ -215,6 +215,7 @@ private:
UINT8 srate, hset, icnt, hload;
int sector_size;
int cur_rate;
+ int idle_icnt;
static std::string tts(attotime t);
std::string ttsn();
diff --git a/src/devices/machine/k033906.cpp b/src/devices/machine/k033906.cpp
index bd32fef29f1..d9953d2864f 100644
--- a/src/devices/machine/k033906.cpp
+++ b/src/devices/machine/k033906.cpp
@@ -33,7 +33,7 @@ k033906_device::k033906_device(const machine_config &mconfig, const char *tag, d
void k033906_device::device_start()
{
- m_voodoo = machine().device(m_voodoo_tag);
+ m_voodoo = (voodoo_device*)machine().device(m_voodoo_tag);
m_reg_set = 0;
@@ -95,7 +95,7 @@ void k033906_device::reg_w(int reg, UINT32 data)
case 0x10: // initEnable
{
- voodoo_set_init_enable(m_voodoo, data);
+ m_voodoo->voodoo_set_init_enable(data);
break;
}
diff --git a/src/devices/machine/k033906.h b/src/devices/machine/k033906.h
index aa2c685df99..438cc5796ab 100644
--- a/src/devices/machine/k033906.h
+++ b/src/devices/machine/k033906.h
@@ -12,7 +12,7 @@
#define __K033906_H__
#include "emu.h"
-
+#include "video/voodoo.h"
/***************************************************************************
@@ -58,7 +58,7 @@ private:
int m_reg_set; // 1 = access reg / 0 = access ram
const char *m_voodoo_tag;
- device_t *m_voodoo;
+ voodoo_device *m_voodoo;
UINT32 m_reg[256];
UINT32 m_ram[32768];
diff --git a/src/devices/machine/pic8259.cpp b/src/devices/machine/pic8259.cpp
index 40fe6fd916c..3064e212850 100644
--- a/src/devices/machine/pic8259.cpp
+++ b/src/devices/machine/pic8259.cpp
@@ -133,7 +133,10 @@ UINT32 pic8259_device::acknowledge()
}
}
}
- return 0;
+ logerror("Spurious IRQ\n");
+ if(m_is_x86)
+ return m_base + 7;
+ return 0xcd0000 + (m_vector_addr_high << 8) + m_vector_addr_low + (7 << (3-m_vector_size));
}
diff --git a/src/devices/machine/ram.cpp b/src/devices/machine/ram.cpp
index 92728b55303..7dd8f9250a4 100644
--- a/src/devices/machine/ram.cpp
+++ b/src/devices/machine/ram.cpp
@@ -1,5 +1,5 @@
-// license:GPL-2.0+
-// copyright-holders:Dirk Best
+// license: BSD-3-Clause
+// copyright-holders: Dirk Best
/*************************************************************************
RAM device
@@ -23,8 +23,6 @@
// device type definition
const device_type RAM = &device_creator<ram_device>;
-
-
//-------------------------------------------------
// ram_device - constructor
//-------------------------------------------------
@@ -38,8 +36,6 @@ ram_device::ram_device(const machine_config &mconfig, const char *tag, device_t
m_default_value = 0xCD;
}
-
-
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@@ -68,7 +64,6 @@ void ram_device::device_start()
save_item(NAME(m_pointer));
}
-
//-------------------------------------------------
// device_validity_check - device-specific validity
// checks
@@ -171,8 +166,6 @@ void ram_device::device_validity_check(validity_checker &valid) const
}
}
-
-
//-------------------------------------------------
// parse_string - convert a ram string to an
// integer value
@@ -210,8 +203,6 @@ UINT32 ram_device::parse_string(const char *s)
return ram;
}
-
-
//-------------------------------------------------
// default_size
//-------------------------------------------------
diff --git a/src/devices/machine/ram.h b/src/devices/machine/ram.h
index 7d3f054895f..5140309f825 100644
--- a/src/devices/machine/ram.h
+++ b/src/devices/machine/ram.h
@@ -1,5 +1,5 @@
-// license:GPL-2.0+
-// copyright-holders:Dirk Best
+// license: BSD-3-Clause
+// copyright-holders: Dirk Best
/*************************************************************************
RAM device