summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2014-04-22 12:21:07 +0000
committer Miodrag Milanovic <mmicko@gmail.com>2014-04-22 12:21:07 +0000
commitc046734f76db73405b7f58663f5b44be327d33db (patch)
tree66fecdc57ac03045532d6380b1c09f4ed38b14ec /src
parent92d6b69712905fc7623301da72deb46613092018 (diff)
(MESS) New driver added: Xerox Alto 2 [Juergen Buchmueller]
out of whatsnew: Did not add Unicode support for debugger from branch yet, for now few special chars are converted to be plain ASCII
Diffstat (limited to 'src')
-rw-r--r--src/emu/cpu/alto2/a2curt.c90
-rw-r--r--src/emu/cpu/alto2/a2curt.h30
-rw-r--r--src/emu/cpu/alto2/a2dht.c69
-rw-r--r--src/emu/cpu/alto2/a2dht.h29
-rw-r--r--src/emu/cpu/alto2/a2disk.c1897
-rw-r--r--src/emu/cpu/alto2/a2disk.h109
-rw-r--r--src/emu/cpu/alto2/a2disp.c876
-rw-r--r--src/emu/cpu/alto2/a2disp.h303
-rw-r--r--src/emu/cpu/alto2/a2dvt.c48
-rw-r--r--src/emu/cpu/alto2/a2dvt.h27
-rw-r--r--src/emu/cpu/alto2/a2dwt.c64
-rw-r--r--src/emu/cpu/alto2/a2dwt.h27
-rw-r--r--src/emu/cpu/alto2/a2emu.c690
-rw-r--r--src/emu/cpu/alto2/a2emu.h71
-rw-r--r--src/emu/cpu/alto2/a2ether.c1382
-rw-r--r--src/emu/cpu/alto2/a2ether.h118
-rw-r--r--src/emu/cpu/alto2/a2hw.c424
-rw-r--r--src/emu/cpu/alto2/a2hw.h68
-rw-r--r--src/emu/cpu/alto2/a2jkff.h194
-rw-r--r--src/emu/cpu/alto2/a2kbd.c65
-rw-r--r--src/emu/cpu/alto2/a2kbd.h111
-rw-r--r--src/emu/cpu/alto2/a2ksec.c56
-rw-r--r--src/emu/cpu/alto2/a2ksec.h50
-rw-r--r--src/emu/cpu/alto2/a2kwd.c54
-rw-r--r--src/emu/cpu/alto2/a2kwd.h51
-rw-r--r--src/emu/cpu/alto2/a2mem.c879
-rw-r--r--src/emu/cpu/alto2/a2mem.h140
-rw-r--r--src/emu/cpu/alto2/a2mouse.c273
-rw-r--r--src/emu/cpu/alto2/a2mouse.h73
-rw-r--r--src/emu/cpu/alto2/a2mrt.c47
-rw-r--r--src/emu/cpu/alto2/a2mrt.h21
-rw-r--r--src/emu/cpu/alto2/a2part.c32
-rw-r--r--src/emu/cpu/alto2/a2part.h20
-rw-r--r--src/emu/cpu/alto2/a2ram.c440
-rw-r--r--src/emu/cpu/alto2/a2ram.h80
-rw-r--r--src/emu/cpu/alto2/a2roms.c198
-rw-r--r--src/emu/cpu/alto2/a2roms.h45
-rw-r--r--src/emu/cpu/alto2/alto2cpu.c2814
-rw-r--r--src/emu/cpu/alto2/alto2cpu.h948
-rw-r--r--src/emu/cpu/alto2/alto2dsm.c391
-rw-r--r--src/emu/cpu/cpu.mak109
-rw-r--r--src/emu/drivers/xtal.h1
-rw-r--r--src/emu/emu.mak1
-rw-r--r--src/emu/imagedev/diablo.c288
-rw-r--r--src/emu/imagedev/diablo.h100
-rw-r--r--src/emu/machine/diablo_hd.c1466
-rw-r--r--src/emu/machine/diablo_hd.h180
-rw-r--r--src/emu/machine/machine.mak8
-rw-r--r--src/emu/mame.c13
-rw-r--r--src/emu/mame.h1
-rw-r--r--src/mess/drivers/alto2.c304
-rw-r--r--src/mess/mess.lst1
-rw-r--r--src/mess/mess.mak3
53 files changed, 15778 insertions, 1 deletions
diff --git a/src/emu/cpu/alto2/a2curt.c b/src/emu/cpu/alto2/a2curt.c
new file mode 100644
index 00000000000..c721d83c328
--- /dev/null
+++ b/src/emu/cpu/alto2/a2curt.c
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII cursor task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+/**
+ * @brief disable the cursor task and set the curt_blocks flag
+ */
+void alto2_cpu_device::f1_early_curt_block()
+{
+ m_dsp.curt_blocks = true;
+ m_task_wakeup &= ~(1 << m_task);
+ LOG((LOG_CURT,2," BLOCK %s\n", task_name(m_task)));
+}
+
+/**
+ * @brief f2_load_xpreg late: load the x position register from BUS[6-15]
+ */
+void alto2_cpu_device::f2_late_load_xpreg()
+{
+ m_dsp.xpreg = X_RDBITS(m_bus,16,6,15);
+ LOG((LOG_CURT, 9," XPREG<- BUS[6-15] (%#o)\n", m_dsp.xpreg));
+}
+
+/**
+ * @brief f2_load_csr late: load the cursor shift register from BUS[0-15]
+ *
+ * Shift CSR to xpreg % 16 position to make it easier to
+ * to handle the word xor in unload_word().
+ * <PRE>
+ * xpreg % 16 cursor bits
+ * [ first word ][ second word ]
+ * ----------------------------------------------
+ * 0 xxxxxxxxxxxxxxxx0000000000000000
+ * 1 0xxxxxxxxxxxxxxxx000000000000000
+ * 2 00xxxxxxxxxxxxxxxx00000000000000
+ * ...
+ * 14 00000000000000xxxxxxxxxxxxxxxx00
+ * 15 000000000000000xxxxxxxxxxxxxxxx0
+ * </PRE>
+ */
+void alto2_cpu_device::f2_late_load_csr()
+{
+ m_dsp.csr = m_bus;
+ LOG((LOG_CURT, m_dsp.csr ? 2 : 9," CSR<- BUS (%#o)\n", m_dsp.csr));
+}
+
+/**
+ * @brief curt_activate: called by the CPU when the cursor task becomes active
+ */
+void alto2_cpu_device::activate_curt()
+{
+ m_task_wakeup &= ~(1 << m_task);
+ m_dsp.curt_wakeup = false;
+
+ int x = 01777 - m_dsp.xpreg;
+ UINT32 bits = m_dsp.csr << (16 - (x & 15));
+ m_dsp.cursor0 = static_cast<UINT16>(bits >> 16);
+ m_dsp.cursor1 = static_cast<UINT16>(bits);
+ m_dsp.curxpos = x / 16;
+}
+
+/** @brief initialize the cursor task F1 and F2 functions */
+void alto2_cpu_device::init_curt(int task)
+{
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_curt_block, 0);
+ set_f2(task, f2_curt_load_xpreg, 0, &alto2_cpu_device::f2_late_load_xpreg);
+ set_f2(task, f2_curt_load_csr, 0, &alto2_cpu_device::f2_late_load_csr);
+ m_active_callback[task] = &alto2_cpu_device::activate_curt;
+}
+
+void alto2_cpu_device::exit_curt()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_curt()
+{
+ m_dsp.curt_blocks = false;
+ m_dsp.xpreg = 0;
+ m_dsp.csr = 0;
+ m_dsp.curxpos = 0;
+ m_dsp.cursor0 = m_dsp.cursor1 = 0;
+}
diff --git a/src/emu/cpu/alto2/a2curt.h b/src/emu/cpu/alto2/a2curt.h
new file mode 100644
index 00000000000..3e2d840f1d4
--- /dev/null
+++ b/src/emu/cpu/alto2/a2curt.h
@@ -0,0 +1,30 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII cursor task (CURT)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2CURT_H_
+#define _A2CURT_H_
+
+//! F2 functions for cursor task
+enum {
+ f2_curt_load_xpreg = f2_task_10, //!< f2 10: load x position register
+ f2_curt_load_csr = f2_task_11, //!< f2 11: load cursor shift register
+};
+
+void f1_early_curt_block(); //!< f1_curt_block early: disable the cursor task and set the curt_blocks flag
+void f2_late_load_xpreg(); //!< f2_load_xpreg late: load the x position register from BUS[6-15]
+void f2_late_load_csr(); //!< f2_load_csr late: load the cursor shift register from BUS[0-15]
+void activate_curt(); //!< curt_activate: called by the CPU when the cursor task becomes active
+void init_curt(int task = task_curt); //!< initialize cursor task
+void exit_curt(); //!< deinitialize cursor task
+void reset_curt(); //!< reset cursor task
+#endif // _A2CURT_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2dht.c b/src/emu/cpu/alto2/a2dht.c
new file mode 100644
index 00000000000..61f63b6a74e
--- /dev/null
+++ b/src/emu/cpu/alto2/a2dht.c
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display horizontal task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+/**
+ * @brief f1_dht_block early: disable the display word task
+ */
+void alto2_cpu_device::f1_early_dht_block()
+{
+ m_dsp.dht_blocks = true;
+ // clear the wakeup for the display horizontal task
+ m_task_wakeup &= ~(1 << m_task);
+ LOG((LOG_DHT,2," BLOCK %s\n", task_name(m_task)));
+}
+
+/**
+ * @brief f2_dht_setmode late: set the next scanline's mode inverse and half clock and branch
+ *
+ * BUS[0] selects the pixel clock (0), or half pixel clock (1)
+ * BUS[1] selects normal mode (0), or inverse mode (1)
+ *
+ * The current BUS[0] drives the NEXT[09] line, i.e. branches to 0 or 1
+ */
+void alto2_cpu_device::f2_late_dht_setmode()
+{
+ UINT16 r = X_RDBITS(m_bus,16,0,0);
+ m_dsp.setmode = m_bus;
+ LOG((LOG_DHT,2," SETMODE<- BUS (%#o), branch on BUS[0] (%#o | %#o)\n", m_bus, m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief called by the CPU when the display horizontal task becomes active
+ */
+void alto2_cpu_device::activate_dht()
+{
+ m_task_wakeup &= ~(1 << m_task);
+}
+
+/**
+ * @brief initialize the display horizontal task
+ *
+ * @param task task number
+ */
+void alto2_cpu_device::init_dht(int task)
+{
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_dht_block, 0);
+ set_f2(task, f2_dht_evenfield, 0, &alto2_cpu_device::f2_late_evenfield);
+ set_f2(task, f2_dht_setmode, 0, &alto2_cpu_device::f2_late_dht_setmode);
+ m_active_callback[task] = &alto2_cpu_device::activate_dht;
+}
+
+void alto2_cpu_device::exit_dht()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_dht()
+{
+ m_dsp.dht_blocks = true;
+ m_dsp.setmode = 0;
+}
diff --git a/src/emu/cpu/alto2/a2dht.h b/src/emu/cpu/alto2/a2dht.h
new file mode 100644
index 00000000000..2415e9133f1
--- /dev/null
+++ b/src/emu/cpu/alto2/a2dht.h
@@ -0,0 +1,29 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display horizontal task (DHT)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2DHT_H_
+#define _A2DHT_H_
+
+//! F2 functions for display horizontal task
+enum {
+ f2_dht_evenfield = f2_task_10, //!< f2 10: load even field
+ f2_dht_setmode = f2_task_11, //!< f2 11: set mode
+};
+
+void f1_early_dht_block(); //!< F1 func: disable the display word task
+void f2_late_dht_setmode(); //!< F2 func: set the next scanline's mode inverse and half clock and branch
+void activate_dht(); //!< called by the CPU when the display horizontal task becomes active
+void init_dht(int task = task_dht); //!< initialize display horizontal task
+void exit_dht(); //!< deinitialize display horizontal task
+void reset_dht(); //!< reset the display horizontal task
+#endif // _A2DHT_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2disk.c b/src/emu/cpu/alto2/a2disk.c
new file mode 100644
index 00000000000..76872ba4f3f
--- /dev/null
+++ b/src/emu/cpu/alto2/a2disk.c
@@ -0,0 +1,1897 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII disk interface
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+#define GET_KADDR_SECTOR(kaddr) X_RDBITS(kaddr,16, 0, 3) //!< get sector number from address register
+#define PUT_KADDR_SECTOR(kaddr,val) X_WRBITS(kaddr,16, 0, 3,val) //!< put sector number into address register
+#define GET_KADDR_CYLINDER(kaddr) X_RDBITS(kaddr,16, 4,12) //!< get cylinder number from address register
+#define PUT_KADDR_CYLINDER(kaddr,val) X_WRBITS(kaddr,16, 4,12,val) //!< put cylinder number int address register
+#define GET_KADDR_HEAD(kaddr) X_RDBITS(kaddr,16,13,13) //!< get head number from address register
+#define PUT_KADDR_HEAD(kaddr,val) X_WRBITS(kaddr,16,13,13,val) //!< put head number into address register
+#define GET_KADDR_DRIVE(kaddr) X_RDBITS(kaddr,16,14,14) //!< get drive (unit) number from address register
+#define PUT_KADDR_DRIVE(kaddr,val) X_WRBITS(kaddr,16,14,14,val) //!< put drive (unit) number into address register
+#define GET_KADDR_RESTORE(kaddr) X_RDBITS(kaddr,16,15,15) //!< get restore flag from address register
+#define PUT_KADDR_RESTORE(kaddr,val) X_WRBITS(kaddr,16,15,15,val) //!< putt restore flag into address register
+
+#define GET_KADR_SEAL(kadr) X_RDBITS(kadr,16, 0, 7) //!< get command seal from command register
+#define PUT_KADR_SEAL(kadr,val) X_WRBITS(kadr,16, 0, 7,val) //!< put command seal into command register
+#define GET_KADR_HEADER(kadr) X_RDBITS(kadr,16, 8, 9) //!< get r/w/c for header from command register
+#define PUT_KADR_HEADER(kadr,val) X_WRBITS(kadr,16, 8, 9,val) //!< put r/w/c for header from command register
+#define GET_KADR_LABEL(kadr) X_RDBITS(kadr,16,10,11) //!< get r/w/c for label from command register
+#define PUT_KADR_LABEL(kadr,val) X_WRBITS(kadr,16,10,11,val) //!< put r/w/c for label into command register
+#define GET_KADR_DATA(kadr) X_RDBITS(kadr,16,12,13) //!< get r/w/c for data from command register
+#define PUT_KADR_DATA(kadr,val) X_WRBITS(kadr,16,12,13,val) //!< put r/w/c for data into command register
+#define GET_KADR_NOXFER(kadr) X_RDBITS(kadr,16,14,14) //!< get no transfer flag from command register
+#define PUT_KADR_NOXFER(kadr,val) X_WRBITS(kadr,16,14,14,val) //!< put no transfer flag into command register
+#define GET_KADR_UNUSED(kadr) X_RDBITS(kadr,16,15,15) //!< get unused (drive?) flag from command register
+#define PUT_KADR_UNUSED(kadr,val) X_WRBITS(kadr,16,15,15,val) //!< put unused (drive?) flag into command register
+
+#define GET_KSTAT_SECTOR(kstat) X_RDBITS(kstat,16,0,3) //!< get current sector number from status register
+#define PUT_KSTAT_SECTOR(kstat,val) X_WRBITS(kstat,16,0,3,val) //!< put current sector number into status register
+#define GET_KSTAT_DONE(kstat) X_RDBITS(kstat,16,4,7) //!< get 'done' field from status register (017)
+#define PUT_KSTAT_DONE(kstat,val) X_WRBITS(kstat,16,4,7,val) //!< put 'done' field int status register (017)
+#define GET_KSTAT_SEEKFAIL(kstat) X_RDBITS(kstat,16,8,8) //!< get seek fail flag from status register
+#define PUT_KSTAT_SEEKFAIL(kstat,val) X_WRBITS(kstat,16,8,8,val) //!< put seek fail flag into status register
+#define GET_KSTAT_SEEK(kstat) X_RDBITS(kstat,16,9,9) //!< get seek busy flag (strobe) from status register
+#define PUT_KSTAT_SEEK(kstat,val) X_WRBITS(kstat,16,9,9,val) //!< put seek busy flag (strobe) into status register
+#define GET_KSTAT_NOTRDY(kstat) X_RDBITS(kstat,16,10,10) //!< get drive not ready flag from status register
+#define PUT_KSTAT_NOTRDY(kstat,val) X_WRBITS(kstat,16,10,10,val) //!< put drive not ready flag into status register
+#define GET_KSTAT_DATALATE(kstat) X_RDBITS(kstat,16,11,11) //!< get data late flag from status register
+#define PUT_KSTAT_DATALATE(kstat,val) X_WRBITS(kstat,16,11,11,val) //!< put data late flag into status register
+#define GET_KSTAT_IDLE(kstat) X_RDBITS(kstat,16,12,12) //!< get idle flag from status register (idle is a software flag)
+#define PUT_KSTAT_IDLE(kstat,val) X_WRBITS(kstat,16,12,12,val) //!< put idle flag into status register (idle is a software flag)
+#define GET_KSTAT_CKSUM(kstat) X_RDBITS(kstat,16,13,13) //!< get checksum flag from status register (checksum is a software flag; it is ORed when 0)
+#define PUT_KSTAT_CKSUM(kstat,val) X_WRBITS(kstat,16,13,13,val) //!< put checksum flag into status register (checksum is a software flag; it is ORed when 0)
+#define GET_KSTAT_COMPLETION(kstat) X_RDBITS(kstat,16,14,15) //!< get completion code from status register (completion is a 2-bit software latch)
+#define PUT_KSTAT_COMPLETION(kstat,val) X_WRBITS(kstat,16,14,15,val) //!< put completion code into status register (completion is a 2-bit software latch)
+
+#define GET_KCOM_XFEROFF(kcom) X_RDBITS(kcom,16,1,1) //!< get transfer off flag from controller command (hardware command register)
+#define PUT_KCOM_XFEROFF(kcom,val) X_WRBITS(kcom,16,1,1,val) //!< put transfer off flag into controller command (hardware command register)
+#define GET_KCOM_WDINHIB(kcom) X_RDBITS(kcom,16,2,2) //!< get word task inhibit flag from controller command (hardware command register)
+#define PUT_KCOM_WDINHIB(kcom,val) X_WRBITS(kcom,16,2,2,val) //!< put word task inhibit flag into controller command (hardware command register)
+#define GET_KCOM_BCLKSRC(kcom) X_RDBITS(kcom,16,3,3) //!< get bit clock source flag from controller command (hardware command register)
+#define PUT_KCOM_BCLKSRC(kcom,val) X_WRBITS(kcom,16,3,3,val) //!< put bit clock source flag into controller command (hardware command register)
+#define GET_KCOM_WFFO(kcom) X_RDBITS(kcom,16,4,4) //!< get write fixed frequency oscillator flag from controller command (hardware command register)
+#define PUT_KCOM_WFFO(kcom,val) X_WRBITS(kcom,16,4,4,val) //!< put write fixed frequency oscillator flag into controller command (hardware command register)
+#define GET_KCOM_SENDADR(kcom) X_RDBITS(kcom,16,5,5) //!< get send address flag from controller command (hardware command register)
+#define PUT_KCOM_SENDADR(kcom,val) X_WRBITS(kcom,16,5,5,val) //!< put send address flag into controller command (hardware command register)
+
+#if defined(ALTO2_FAKE_STATUS_H) && (ALTO2_FAKE_STATUS_H > 0)
+#define STATUS_DP0 48
+#define STATUS_DP1 78
+#define STATUS_RGATE 0, "%c"
+#define STATUS_WGATE 1, "%c"
+#define STATUS_KWRC 2, "%c"
+#define STATUS_CYL 4, "C%-3d"
+#define STATUS_HEAD 9, "H%d"
+#define STATUS_SECT 12, "S%-2d"
+#define STATUS_PAGE 16, "[%-4d]"
+#define FAKE_STATUS(_unit,_which,...) do { \
+ int x = (_unit) ? STATUS_DP1 : STATUS_DP0; \
+ fake_status_printf(x + _which, __VA_ARGS__); \
+} while (0)
+#endif
+
+/** @brief completion codes (only for documentation, since this is microcode defined) */
+enum {
+ STATUS_COMPLETION_GOOD,
+ STATUS_COMPLETION_HARDWARE_ERROR,
+ STATUS_COMPLETION_CHECK_ERROR,
+ STATUS_COMPLETION_ILLEGAL_SECTOR
+};
+
+/** @brief record numbers per sector in INCRECNO order */
+enum {
+ RECNO_HEADER,
+ RECNO_NOTHING,
+ RECNO_LABEL,
+ RECNO_DATA
+};
+
+
+/** @brief read/write/check numbers */
+enum {
+ RWC_READ,
+ RWC_CHECK,
+ RWC_WRITE,
+ RWC_WRITE2
+};
+
+#if ALTO2_DEBUG
+/** @brief human readable names for the KADR<- modes */
+static const char *rwc_name[4] = {"read", "check", "write", "write2"};
+static const char* raise_lower[2] = {"/","\\"};
+#endif
+
+/**
+ * <PRE>
+ * SECTOR, ERROR WAKEUPS
+ *
+ *
+ * Monoflop pulse duration:
+ * tW = K * Rt * Cext * (1 + 0.7/Rt)
+ * K = 0.28 for 74123
+ * Rt = kOhms
+ * Cext = pF
+ *
+ * +------+
+ * CLRSTAT' >---------oS' | 15k, .47uF (=470000pF)
+ * | MONO | 2066120ns ~= 2ms
+ * | FLOP |
+ * | | Q' +----+
+ * READY' >---------oC' o--------|NAND| ERRWAKE'
+ * +------+ | o----+
+ * RDYLAT' >-------------------------| | |
+ * +----+ |
+ * |
+ * |
+ * .---------------------+
+ * |
+ * +---o--+ Q +------+
+ * +-----|J S' |----+---------|S | 30k, .01uF (=10000pF)
+ * | | | | | MONO | 85960ns ~= 86us
+ * SECT[4] >---|-----|CLK | | | FLOP |
+ * | | 21a| | | | Q'
+ * | 1 >-|K' C' | | 1 >-|C' |--------------------> SECLATE
+ * | +---o--+ | +------+
+ * | | |
+ * +---------+-------|-----------------------------------.
+ * | |
+ * +---------------+ |
+ * | |
+ * | 1 1 RESET' >------+ |
+ * | | | | |
+ * | +---o--+ Q +---o--+ Q +---o--+ Q |
+ * +---|J S' |----------|J S' |----------|J S' |------> STSKENA
+ * | | | | | | |
+ * SYSCLKB' >--+------|CLK | .-------|CLK | +-------|CLK | |
+ * | | 21b| | | 22a| | | 22b| Q' |
+ * | 1 >-|K' C' | | 1 >-|K' C' | | +---|K' C' |----+-> WAKEST'
+ * | +---o--+ | +---o--+ | | +---o--+ |
+ * | | | | | | 1 |
+ * | +-----|-----------+-----|---|---------------+
+ * | | | |
+ * +----------------+-----------------+ |
+ * |
+ * +----+ |
+ * BLOCK >-------------------------|NAND| |
+ * | o----------+
+ * STSKACT >-------------------------| |
+ * +----+
+ *
+ * A CLRSTAT starts the monoflop, and READY', i.e. the ready signal from the disk
+ * drive, clears it. The Q' output is thus 0 for some time after CLRSTAT, and as
+ * long as the disk signals being ready.
+ *
+ * If the disk is not ready, i.e. the Q' being 1, and if RDYLAT' - the READY' state
+ * latched at the most recent CLRSTAT - is also 1, the ERRWAKE' signal will go 0.
+ *
+ * Each new sector (SECT[4]' going 1) will clock the FF 21a, which changes
+ * its Q output depending on WAKEST' (K' is always 1):
+ * if J and K' are both 1, sets its Q to 1.
+ * if J is 0, and K' is 1, keeps Q as is.
+ * So Q becomes 0 by WAKEST' going 0, and it becomes 1 with the next sector, if
+ * WAKEST' is 1.
+ *
+ * The mono-flop to the right will generate a SECLATE signal, if WAKEST' was
+ * not 0 when the disk signalled a new sector.
+ *
+ * The three J-K FFs at the bottom are all clocked with the rising edge of
+ * SYSCLKB' (i.e falling edge of SYSCLKB).
+ *
+ * The left JK-FF propagates the current state of the upper JK-FF's Q output
+ * to its own Q. The middle propagates the previous state of the left one,
+ * and the JK-FF to the right delays the wandering Q for a third SYSCLKB'
+ * rising edge, but only in one case:
+ * 1) if J and K' are both 1, set its Q to 1.
+ * 2) if J is 1, and K' is 0, toggle Q.
+ * 3) if J is 0, and K' is 1, keep Q as is.
+ * 4) if J and K' are both 0, set its Q to 0.
+ *
+ * The right FF's K' is 0 whenever the BLOCK signal (see DISK WORD TIMING)
+ * and the sector task active signal (STSKACT) are 1 at the same time.
+ *
+ * Case 1) is the normal case, and it wakes the KSECT on the third SYSCLKB'
+ * positive edge. It resets at that same time the left, middle, and upper
+ * J-K FFs .
+ *
+ * Case 2) is due, when the sector task is already active the moment
+ * the BLOCK signal arrives. This toggles the output, i.e. removes the
+ * wake.
+ *
+ * Case 3) is for an active sector task without a new sector.
+ *
+ * And finally case 4) happens when an active sector task sees no new
+ * sector, and BLOCK rises.
+ *
+ * (This is like the video timing's dwt_blocks and dht_blocks signals)
+ * </PRE>
+ */
+
+/**
+ * @brief monoflop 31a pulse duration
+ * Rt = 15k, Cext = .47uF (=470000pF) => 2066120ns (~2ms)
+ */
+#define TW_READY 2066120
+
+/**
+ * @brief monoflop 31b pulse duration
+ * Rt = 30k, Cext = .01uF (=10000pF) => 86960ns (~85us)
+ *
+ * There's something wrong with this, or the KSEC would never ever
+ * be able to commence the KWD. The SECLATE monoflop ouput has to go
+ * high some time into the KSEC task microcode, before the sequence
+ * error state is checked.
+ *
+ * TW_SECLATE (85960 nsec)
+ * TW_SECLATE (46*ALTO2_UCYCLE)
+ * TW_SECLATE 8596
+ */
+#define TW_SECLATE 8596
+
+/** @brief monoflop 52b pulse duration
+ * Rt = 20k, Cext = 0.01uF (=10000pF) => 57960ns (~58us)
+ */
+#define TW_STROBON 57960
+
+/**
+ * <PRE>
+ * DISK WORD TIMING
+ *
+ *
+ * SECLATE ----+ +-+-+-+---< 1
+ * | | | | |
+ * +--o-----------+ CARRY +---+
+ * | CLR 1 2 4 8 |-------|INVo-----> WDDONE'
+ * +---+ BITCLK' | | +---+
+ * BITCLK >----|INVo----+------|CLK/ 74161 |
+ * +---+ | +----o---------+
+ * | |LOAD'
+ * +----------+ |
+ * | +----+ |
+ * +--|NAND| |
+ * | o----+ |
+ * HIORDBIT >-----| | | |
+ * +----+ | |
+ * | |
+ * +---o--+ Q |
+ * BUS[4] >--------+--|J S' |------+
+ * | | | +----+
+ * LDCOM' >--------|--|CLK | +------|NAND|
+ * | | 67b| | | o----> WAKEWDT'
+ * +--|K' C' | | +--| |
+ * +---o--+ | | +----+
+ * | | |
+ * 1 | +----------+
+ * | |
+ * OK TO RUN >---------------+ 1 | 1 |
+ * (1 in AltoI) | | | | |
+ * +---o--+ Q +---o--+ Q | +---o--+ Q |
+ * 1 >-|J S' |----------|J S' |------+---|J S' |---+
+ * | | | | | | |
+ * WDDONE' >-----------|CLK | +-------|CLK | .--|---|CLK |
+ * | 43b| | | 53a| | | | 43a|
+ * 1 >-|K' C' | | +-----|K' C' | | `---|K' C' o---+
+ * +---o--+ | | +---o--+ | +---o--+ Q'|
+ * | | | | | | |
+ * +-----|-|---------|------|----------|------+
+ * | | | | |
+ * SYSCLKA' >---------------------|-|---------|------+ |
+ * | | | |
+ * WDALLOW >---------------------|-|---------+-----------------+
+ * | | |
+ * SYSCLKB' >---------------------+ | +---o--+ Q
+ * | | 0 >-|J S' |-------------> WDINIT
+ * | | | |
+ * +----+ +-|-----|CLK |
+ * BLOCK >--|NAND| | | 53b|
+ * | o---------------+-----|K C' |
+ * WDTSKACT >--| | +---o--+
+ * +----+ |
+ * 1
+ *
+ *
+ * If SECLATE is 0, WDDONE' never goes low (counter's clear has precedence).
+ *
+ * If SECLATE is 1, WDDONE', the counter will count:
+ *
+ * If HIORDBIT is 1 at the falling edge of BITCLK, it sets the J-K flip-flop 67b, and
+ * thus takes away the LOAD' assertion from the counter. It has been loaded with
+ * 15, so it counts to 16 on the next rising edge and makes WDDONE' go to 0.
+ *
+ * If HIORDBIT is 0 at the falling edge of BITCLK, counting continues as it was
+ * preset with BUS[4] at the last KCOM<- load:
+ *
+ * If BUS[4] was 1, both J and K' of the FF (74109) will be 1 at the rising edge
+ * of LDCOM' (at the end of KCOM<-) and Q will be 1 => LOAD' is deasserted.
+ *
+ * If BUS[4] was 0, both J and K' will be 0, and Q will be 0 => LOAD' asserted.
+ *
+ * WDDONE' going from 0 to 1 will make the Q output of FF 43b go to 1.
+ * The FF is also set, as long as OK TO RUN is 0.
+ *
+ * The FF 53a is clocked with falling edge of SYSCLKB (rising of SYSCLKB'),
+ * and will:
+ * if J and K' are both 1, set its Q to 1.
+ * if J is 1, and K' is 0, toggle Q.
+ * if J is 0, and K' is 1, keep Q as is.
+ * if J and K' are both 0, set its Q to 0.
+ * J is = Q of the FF 43b.
+ * K is = 0, if both BLOCK and WDTASKACT are 1, and 1 otherwise.
+ *
+ * The FF 43a is clocked with falling edge of SYSCLKA (rising of SYSCLKA').
+ * Its J and K' inputs are the Q output of the previous (middle) FF, thus
+ * it will propagate the middle FF's Q to its own Q when SYSCLKA goes 0.
+ *
+ * If Q (53a) and Q (43a) are both 1, the WAKEKWDT' is 0 and the
+ * word task wakeup signal is sent.
+ *
+ * WDALLOW going 0 asynchronously resets the 53a and 43a FFs, and thus
+ * deasserts the WAKEKWD'. It also asynchronously sets the FF 53b, and
+ * its output Q is the WDINIT signal.
+ *
+ * WDINIT is also deasserted with SYSCLKB going high, whenever both BLOCK
+ * and WDTSKACT are 1.
+ *
+ * Whoa there! :-)
+ * </PRE>
+ */
+#define WDALLOW (!GET_KCOM_WDINHIB(m_dsk.kcom))
+#define WDINIT ((m_dsk.ff_53b & JKFF_Q) ? 1 : 0)
+#define RDYLAT ((m_dsk.ff_45a & JKFF_Q) ? 1 : 0)
+#define SEQERR ((m_task == task_ksec && m_dsk.seclate == 0) || (m_task == task_kwd && m_dsk.bitcount == 15))
+#define ERRWAKE (RDYLAT | m_dsk.ready_mf31a)
+#define SEEKOK (m_dsk.seekok)
+
+/**
+ * @brief disk word timing
+ *
+ * Implement the FIFOs and gates in the description above.
+ *
+ * @param bitclk the current bitclk level
+ * @param datin the level of the bit read from the disk
+ * @param block contains the task number of a blocking task, or 0 otherwise
+ */
+void alto2_cpu_device::kwd_timing(int bitclk, int datin, int block)
+{
+ diablo_hd_device* dhd = m_drive[m_dsk.drive];
+ int wddone = m_dsk.wddone; // get previous state of word-done
+ int i;
+ UINT8 s0, s1;
+
+ LOG((LOG_DISK,9," *** KWD timing bitclk:%d datin:%d block:%d\n", bitclk, datin, block));
+ if (0 == m_dsk.seclate)
+ {
+ // if SECLATE is 0, WDDONE' never goes low (counter's clear has precedence).
+ if (m_dsk.bitcount) {
+ LOG((LOG_DISK,7," SECLATE:0 clears bitcount:0\n"));
+ m_dsk.bitcount = 0;
+ }
+ }
+ else
+ {
+ // SECLATE is 1
+ if (m_dsk.bitclk && !bitclk)
+ {
+ // on the falling edge of bitclk the counter will count or be loaded
+ if ((m_dsk.shiftin & (1 << 16)) && !GET_KCOM_WFFO(m_dsk.kcom)) {
+ /*
+ * If HIORDBIT is 1 at the falling edge of BITCLK, it sets the
+ * JK-FF 67b, and thus takes away the LOAD' assertion from the
+ * counter. It has been loaded with 15, so it counts to 16 on
+ * the next rising edge and makes WDDONE' go to 0.
+ */
+ LOG((LOG_DISK,7," HIORDBIT:1 sets WFFO:1\n"));
+ PUT_KCOM_WFFO(m_dsk.kcom, 1);
+ // TODO: show disk indicators
+ }
+ /*
+ * Falling edge of BITCLK, counting continues as it was preset
+ * with BUS[4] (WFFO) at the last KCOM<- load, or as set by a
+ * 1 bit being read in HIORDBIT.
+ */
+ if (GET_KCOM_WFFO(m_dsk.kcom)) {
+ /*
+ * If BUS[4] (WFFO) was 1, both J and K' of the FF (74109) will
+ * be 1 at the rising edge of LDCOM' (at the end of KCOM<-)
+ * and Q will be 1. LOAD' is deassterted: count on clock.
+ */
+ m_dsk.bitcount = (m_dsk.bitcount + 1) % 16;
+ LOG((LOG_DISK,6," WFFO:1 count bitcount:%2d\n", m_dsk.bitcount));
+ } else {
+ /*
+ * If BUS[4] (WFFO) was 0, both J and K' will be 0, and Q
+ * will be 0. LOAD' is asserted and will load on rising bitclock (now)
+ */
+ m_dsk.bitcount = 15;
+ LOG((LOG_DISK,6," WFFO:0 load bitcount:%2d\n", m_dsk.bitcount));
+ }
+ }
+ if (!m_dsk.bitclk && bitclk) {
+ // rising edge of bitclk
+ m_dsk.shiftin = (m_dsk.shiftin << 1) | datin; // clock the input shift register
+ m_dsk.shiftout = m_dsk.shiftout << 1; // and the output shift register too
+ }
+ }
+
+ if (m_dsk.wddone != wddone) {
+ LOG((LOG_DISK,8," WDDONE':%d->%d\n", m_dsk.wddone, wddone));
+ }
+
+ if (15 == m_dsk.bitcount) {
+ /* CARRY = 1 -> WDDONE' = 0 */
+ wddone = 0;
+ if (m_dsk.wddone == 0) {
+ /*
+ * Latch a new data word while WDDONE is 0
+ * Note: The shifter outputs for bits 0 to 14 are connected
+ * to the latches inputs 1 to 15, while input bit 0 comes
+ * from the current datin.
+ * Shifter output 15 is the HIORDBIT signal.
+ */
+ m_dsk.datain = m_dsk.shiftin & 0177777;
+ /* load the output shift register */
+ m_dsk.shiftout = m_dsk.dataout;
+ LOG((LOG_DISK,8," LATCH in:%06o (0x%04x) out:%06o (0x%04x)\n", m_dsk.datain, m_dsk.datain, m_dsk.dataout, m_dsk.dataout));
+ }
+ } else {
+ /* CARRY = 0 -> WDDONE' = 1 */
+ wddone = 1;
+ }
+
+ // remember previous state of word-done
+ m_dsk.wddone = wddone;
+
+ /**
+ * JK flip-flop 43b (word task)
+ * <PRE>
+ * CLK WDDONE'
+ * J 1
+ * K' 1
+ * S' 1
+ * C' WDTSKENA
+ * Q to 53a J
+ * </PRE>
+ */
+ s0 = m_dsk.ff_43b;
+ s1 = wddone ? JKFF_CLK : JKFF_0;
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ if (m_dsk.ok_to_run)
+ s1 |= JKFF_S;
+ if (!(m_dsk.ff_43a & JKFF_Q))
+ s1 |= JKFF_C;
+ m_dsk.ff_43b = update_jkff(s0, s1, "43b KWD ");
+
+ // loop over the 4 stages of sysclka and sysclkb transitions
+ for (i = 0; i < 4; i++) {
+#if ALTO2_DEBUG
+ if (m_sysclka0[i] != m_sysclka1[i]) {
+ LOG((LOG_DISK,9," SYSCLKA' %s\n", raise_lower[m_sysclka1[i]]));
+ }
+ if (m_sysclkb0[i] != m_sysclkb1[i]) {
+ LOG((LOG_DISK,9," SYSCLKB' %s\n", raise_lower[m_sysclkb1[i]]));
+ }
+#endif
+
+ /**
+ * JK flip-flop 53b (word task)
+ * <PRE>
+ * CLK SYSCLKB'
+ * J 0
+ * K' (BLOCK & WDTSKACT)'
+ * S' WDALLOW
+ * C' 1
+ * Q WDINIT
+ * </PRE>
+ */
+ s0 = m_dsk.ff_53b;
+ s1 = m_sysclkb1[i];
+ if (block != task_kwd)
+ s1 |= JKFF_K; // (BLOCK & WDTSKACT)'
+ if (WDALLOW)
+ s1 |= JKFF_S;
+ s1 |= JKFF_C;
+ m_dsk.ff_53b = update_jkff(s0, s1, "53b KWD ");
+
+ /**
+ * JK flip-flop 53a (word task)
+ * <PRE>
+ * CLK SYSCLKB'
+ * J from 43b Q
+ * K' (BLOCK & WDTSKACT)'
+ * S' 1
+ * C' WDALLOW
+ * Q to 43a J and K'
+ * </PRE>
+ */
+ s0 = m_dsk.ff_53a;
+ s1 = m_sysclkb1[i];
+ if (m_dsk.ff_43b & JKFF_Q)
+ s1 |= JKFF_J;
+ if (block != task_kwd)
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ if (WDALLOW)
+ s1 |= JKFF_C;
+ m_dsk.ff_53a = update_jkff(s0, s1, "53a KWD ");
+
+ /**
+ * JK flip-flop 43a (word task)
+ * <PRE>
+ * CLK SYSCLKA'
+ * J from 53a Q
+ * K' from 53a Q
+ * S' 1
+ * C' WDALLOW
+ * Q WDTSKENA', Q' WDTSKENA
+ * </PRE>
+ */
+ s0 = m_dsk.ff_43a;
+ s1 = m_sysclka1[i];
+ if (m_dsk.ff_53a & JKFF_Q)
+ s1 |= JKFF_J;
+ if (m_dsk.ff_53a & JKFF_Q)
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ if (WDALLOW)
+ s1 |= JKFF_C;
+ m_dsk.ff_43a = update_jkff(s0, s1, "43a KWD ");
+
+ /**
+ * JK flip-flop 45a (ready latch)
+ * <PRE>
+ * CLK SYSCLKA'
+ * J READY' from drive
+ * K' 1
+ * S' 1
+ * C' CLRSTAT'
+ * Q RDYLAT'
+ * </PRE>
+ */
+ s0 = m_dsk.ff_45a;
+ s1 = m_sysclka1[i];
+ if (dhd->get_ready_0())
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ s1 |= JKFF_C; // FIXME: CLRSTAT' ?
+ m_dsk.ff_45a = update_jkff(s0, s1, "45a RDYLAT");
+
+ /**
+ * sets the seqerr flip-flop 45b (Q' is SEQERR)
+ * JK flip-flop 45b (seqerr latch)
+ * <PRE>
+ * CLK SYSCLKA'
+ * J 1
+ * K' SEQERR'
+ * S' CLRSTAT'
+ * C' 1
+ * Q to KSTAT[11] DATALATE
+ * </PRE>
+ */
+ s0 = m_dsk.ff_45b;
+ s1 = m_sysclka1[i];
+ s1 |= JKFF_J;
+ if (SEQERR)
+ s1 |= JKFF_K;
+ s1 |= JKFF_S; // FIXME: CLRSTAT' ?
+ s1 |= JKFF_C;
+ m_dsk.ff_45b = update_jkff(s0, s1, "45b SEQERR");
+
+ /**
+ * JK flip-flop 22b (sector task)
+ * <PRE>
+ * CLK SYSCLKB'
+ * J from 22a Q
+ * K' (BLOCK & STSKACT)'
+ * S' 1 (really it's RESET')
+ * C' 1
+ * Q STSKENA; Q' WAKEKST'
+ * </PRE>
+ */
+ s0 = m_dsk.ff_22b;
+ s1 = m_sysclkb1[i];
+ if (m_dsk.ff_22a & JKFF_Q)
+ s1 |= JKFF_J;
+ if (block != task_ksec)
+ s1 |= JKFF_K;
+ s1 |= JKFF_S; // FIXME: RESET' ?
+ s1 |= JKFF_C;
+ m_dsk.ff_22b = update_jkff(s0, s1, "22b KSEC ");
+
+ /**
+ * JK flip-flop 22a (sector task)
+ * <PRE>
+ * CLK SYSCLKB'
+ * J from 21b Q
+ * K' 1
+ * S' 1
+ * C' WAKEST'
+ * Q to 22b J
+ * </PRE>
+ */
+ s0 = m_dsk.ff_22a;
+ s1 = m_sysclkb1[i];
+ if (m_dsk.ff_21b & JKFF_Q)
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ if (!(m_dsk.ff_22b & JKFF_Q))
+ s1 |= JKFF_C;
+ m_dsk.ff_22a = update_jkff(s0, s1, "22a KSEC ");
+
+ /**
+ * JK flip-flop 21b (sector task)
+ * <PRE>
+ * CLK SYSCLKB'
+ * J from 21a Q
+ * K' 1
+ * S' 1
+ * C' WAKEST'
+ * Q to 22a J
+ * </PRE>
+ */
+ s0 = m_dsk.ff_21b;
+ s1 = m_sysclkb1[i];
+ if (m_dsk.ff_21a & JKFF_Q)
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ if (!(m_dsk.ff_22b & JKFF_Q))
+ s1 |= JKFF_C;
+ m_dsk.ff_21b = update_jkff(s0, s1, "21b KSEC ");
+ }
+
+ // The 53b FF Q output is the WDINIT signal.
+ if (WDINIT != m_dsk.wdinit) {
+ m_dsk.wdinit0 = m_dsk.wdinit;
+ // rising edge immediately
+ if ((m_dsk.wdinit = WDINIT) == 1)
+ m_dsk.wdinit0 = 1;
+ LOG((LOG_DISK,8," WDINIT:%d\n", m_dsk.wdinit));
+ }
+
+ /*
+ * If Q (53a) and Q (43a) are both 1, the WAKEKWDT'
+ * output is 0 and the disk word task wakeup signal is asserted.
+ */
+ if ((m_dsk.ff_53a & JKFF_Q) && (m_dsk.ff_43a & JKFF_Q)) {
+ if (m_dsk.wdtskena == 1) {
+ LOG((LOG_DISK,2," WDTSKENA':0 and WAKEKWDT':0 wake KWD\n"));
+ m_dsk.wdtskena = 0;
+ m_task_wakeup |= 1 << task_kwd;
+ }
+ } else if (m_dsk.ff_43a & JKFF_Q) {
+ /*
+ * If Q (43a) is 1, the WDTSKENA' signal is deasserted.
+ */
+ if (m_dsk.wdtskena == 0) {
+ LOG((LOG_DISK,2," WDTSKENA':1\n"));
+ m_dsk.wdtskena = 1;
+ m_task_wakeup &= ~(1 << task_kwd);
+ }
+ }
+
+ if (0 != m_dsk.kfer) {
+ // no fatal error: ready AND not seqerr AND seekok
+ if (!RDYLAT && !SEQERR && SEEKOK) {
+ LOG((LOG_DISK,6," reset KFER\n"));
+ m_dsk.kfer = 0;
+ }
+ } else {
+ // fatal error: not ready OR seqerr OR not seekok
+ if (RDYLAT) {
+ LOG((LOG_DISK,6," RDYLAT sets KFER\n"));
+ m_dsk.kfer = 1;
+ }
+ if (SEQERR) {
+ LOG((LOG_DISK,6," SEQERR sets KFER\n"));
+ m_dsk.kfer = 1;
+ }
+ if (!SEEKOK) {
+ LOG((LOG_DISK,6," not SEEKOK sets KFER\n"));
+ m_dsk.kfer = 1;
+ }
+ }
+
+ /*
+ * The FF 22b Q output is the STSKENA (sector task enable)
+ * signal, the Q' is the WAKEKST' signal.
+ */
+ if (m_dsk.ff_22b & JKFF_Q) {
+ if (0 == (m_task_wakeup & (1 << task_ksec))) {
+ LOG((LOG_DISK,6," STSKENA:1; WAKEST':0 wake KSEC\n"));
+ m_task_wakeup |= 1 << task_ksec;
+ }
+ } else {
+ if (0 != (m_task_wakeup & (1 << task_ksec))) {
+ LOG((LOG_DISK,6," STSKENA:0; WAKEST':1\n"));
+ m_task_wakeup &= ~(1 << task_ksec);
+ }
+ }
+
+ /**
+ * JK flip-flop 21a (sector task)
+ * <PRE>
+ * CLK SECT4 (inverted sector mark from drive)
+ * J WAKEST'
+ * K' 1
+ * S' ERRWAKE'
+ * C' WAKEST'
+ * Q to seclate monoflop
+ * </PRE>
+ */
+ s0 = m_dsk.ff_21a;
+ s1 = dhd->get_sector_mark_0() ? JKFF_CLK : JKFF_0;
+ if (!(m_dsk.ff_22b & JKFF_Q))
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ if (!ERRWAKE)
+ s1 |= JKFF_S;
+ if (!(m_dsk.ff_22b & JKFF_Q))
+ s1 |= JKFF_C;
+ m_dsk.ff_21a = update_jkff(s0, s1, "21a KSEC ");
+
+ // If the KSEC FF 21a Q goes 1, pulse the SECLATE signal for some time.
+ if (!(m_dsk.ff_21a_old & JKFF_Q) && (m_dsk.ff_21a & JKFF_Q)) {
+ m_dsk.seclate_timer->adjust(attotime::from_nsec(TW_SECLATE), 1);
+ if (m_dsk.seclate) {
+ m_dsk.seclate = 0;
+ LOG((LOG_DISK,6," SECLATE -> 0 pulse until cycle %lld\n", cycle() + TW_SECLATE / ALTO2_UCYCLE));
+ }
+ }
+
+ // check if write and erase gate, or read gate are changed
+ if ((m_task_wakeup & (1 << task_ksec)) || GET_KCOM_XFEROFF(m_dsk.kcom) || m_dsk.kfer) {
+#if ALTO2_DEBUG
+ if (0 == m_dsk.egate || 0 == m_dsk.wrgate || 0 == m_dsk.rdgate) {
+ // log the reason why gates are deasserted
+ LOG((LOG_DISK,6," deassert gates because of"));
+ if (m_task_wakeup & (1 << task_ksec)) {
+ LOG((LOG_DISK,6," KSECWAKE"));
+ }
+ if (GET_KCOM_XFEROFF(m_dsk.kcom)) {
+ LOG((LOG_DISK,6," XFEROFF"));
+ }
+ if (m_dsk.kfer) {
+ LOG((LOG_DISK,6," KFER"));
+ }
+ LOG((LOG_DISK,6,"\n"));
+ }
+#endif
+ // sector task is active OR xferoff is set OR fatal error
+ dhd->set_egate(m_dsk.egate = 1);
+ dhd->set_wrgate(m_dsk.wrgate = 1);
+ dhd->set_rdgate(m_dsk.rdgate = 1);
+ FAKE_STATUS(m_dsk.drive, STATUS_WGATE, '-');
+ FAKE_STATUS(m_dsk.drive, STATUS_RGATE, '-');
+ } else {
+ if (m_dsk.krwc & RWC_WRITE) {
+ if (m_dsk.ok_to_run) {
+#if ALTO2_DEBUG
+ if (1 == m_dsk.egate || 1 == m_dsk.wrgate) {
+ LOG((LOG_DISK,6," assert "));
+ if (m_dsk.egate) {
+ LOG((LOG_DISK,6," EGATE"));
+ }
+ if (m_dsk.wrgate) {
+ LOG((LOG_DISK,6," WRGATE"));
+ }
+ LOG((LOG_DISK,6,"\n"));
+ }
+#endif
+ // assert erase and write gates
+ dhd->set_egate(m_dsk.egate = 0);
+ dhd->set_wrgate(m_dsk.wrgate = 0);
+ FAKE_STATUS(m_dsk.drive, STATUS_WGATE, 'W');
+ }
+ } else {
+#if ALTO2_DEBUG
+ if (1 == m_dsk.rdgate) {
+ LOG((LOG_DISK,6," assert RDGATE\n"));
+ }
+#endif
+ // assert read gate
+ dhd->set_rdgate(m_dsk.rdgate = 0);
+ FAKE_STATUS(m_dsk.drive, STATUS_RGATE, 'R');
+ }
+ }
+
+ m_dsk.ff_21a_old = m_dsk.ff_21a;
+ m_dsk.bitclk = bitclk;
+ m_dsk.datin = datin;
+}
+
+
+/**
+ * @brief timer callback to take away the SECLATE pulse (monoflop)
+ * @param ptr some unused pointer
+ * @param arg contains the seclate value
+ */
+void alto2_cpu_device::disk_seclate(void* ptr, INT32 arg)
+{
+ (void)ptr;
+ LOG((LOG_DISK,2," SECLATE -> %d\n", arg));
+ m_dsk.seclate = arg;
+ m_dsk.seclate_timer->enable(false);
+}
+
+/**
+ * @brief timer callback to take away the OK TO RUN pulse (reset)
+ * @param ptr some unused pointer
+ * @param arg contains the ok_to_run value
+ */
+void alto2_cpu_device::disk_ok_to_run(void* ptr, INT32 arg)
+{
+ (void)ptr;
+ LOG((LOG_DISK,2," OK TO RUN -> %d\n", arg));
+ m_dsk.ok_to_run = arg;
+ m_dsk.ok_to_run_timer->enable(false);
+}
+
+/**
+ * @brief timer callback to pulse the STROBE' signal to the drive
+ *
+ * STROBE' pulses are sent to the drive at a rate that depends on
+ * the monoflop 52b external resistor and capacitor.
+ *
+ * The drive compares the cylinder number that is presented on
+ * its inputs against the current cylinder, and if they don't
+ * match steps into the corresponding direction.
+ *
+ * On the falling edge of a strobe, the drive sets the log_addx_interlock
+ * flag 0 (LAI, active low). On the rising edge of the strobe the drive then
+ * indicates seek completion by setting addx_acknowledge to 0 (ADDRACK, active low).
+ * If the seek is not yet complete, it instead keeps the seek_incomplete
+ * flag 0 (SKINC, active low). If the seek would go beyond the last cylinder,
+ * the drive deasserts seek_incomplete, but does not assert the addx_acknowledge.
+ *
+ * @param ptr some unused pointer
+ * @param arg contains the drive, cylinder, and restore flag
+ */
+void alto2_cpu_device::disk_strobon(void* ptr, INT32 arg)
+{
+ (void)ptr;
+ int unit = arg % 2;
+ int restore = (arg / 2) % 2;
+ int cylinder = arg / 4;
+
+ diablo_hd_device* dhd = m_drive[unit];
+ LOG((LOG_DISK,2," STROBE #%d restore:%d cylinder:%d dhd:%p\n", unit, restore, cylinder, dhd));
+
+ dhd->set_cylinder(cylinder);
+ dhd->set_restore(restore);
+ // This is really monoflop 52a generating a very short 0 pulse
+ for (int strobe = 0; strobe < 2; strobe++) {
+ UINT8 s0, s1;
+ dhd->set_strobe(strobe); // pulse the strobe signal to the unit
+
+ int lai = dhd->get_log_addx_interlock_0();
+ LOG((LOG_DISK,6," LAI':%d\n", lai));
+ /**
+ * JK flip-flop 44a (LAI' clocked)
+ * <PRE>
+ * CLK LAI
+ * J 1
+ * K' 1
+ * S' 1
+ * C' CLRSTAT' (not now)
+ * Q to seekok
+ * </PRE>
+ */
+ s0 = m_dsk.ff_44a;
+ s1 = lai ? JKFF_CLK : JKFF_0;
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ s1 |= JKFF_C;
+ m_dsk.ff_44a = update_jkff(s0, s1, "44a LAI ");
+ if (dhd->get_addx_acknowledge_0() == 0 && (m_dsk.ff_44a & JKFF_Q)) {
+ /* if address is acknowledged, and Q' of FF 44a, clear the strobe */
+ m_dsk.strobe = 0;
+ }
+ }
+
+ if (dhd->get_addx_acknowledge_0()) {
+ /* no acknowledge yet */
+
+ } else {
+ /* clear the monoflop 52b, i.e. no timer restart */
+ LOG((LOG_DISK,2," STROBON:%d\n", m_dsk.strobe));
+ /* update the seekok status: SKINC' && LAI' && Q' of FF 44a */
+ int seekok = dhd->get_seek_incomplete_0();
+ if (seekok != m_dsk.seekok) {
+ m_dsk.seekok = seekok;
+ LOG((LOG_DISK,2," SEEKOK:%d\n", m_dsk.seekok));
+ }
+ }
+
+ LOG((LOG_DISK,2," current cylinder:%d\n", dhd->get_cylinder()));
+
+ /* if the strobe is still set, restart the timer */
+ if (m_dsk.strobe) {
+ m_dsk.strobon_timer->adjust(attotime::from_nsec(TW_STROBON), arg);
+ } else {
+ m_dsk.strobon_timer->reset();
+ }
+ FAKE_STATUS(unit, STATUS_CYL, dhd->get_cylinder());
+ FAKE_STATUS(unit, STATUS_HEAD, dhd->get_head());
+}
+
+/** @brief timer callback to change the READY monoflop 31a */
+void alto2_cpu_device::disk_ready_mf31a(void* ptr, INT32 arg)
+{
+ diablo_hd_device* dhd = m_drive[m_dsk.drive];
+ m_dsk.ready_mf31a = arg & dhd->get_ready_0();
+ /* log the not ready result with level 0, else 2 */
+ LOG((LOG_DISK,m_dsk.ready_mf31a ? 0 : 2," mf31a:%d %sready\n", m_dsk.ready_mf31a, m_dsk.ready_mf31a ? "not " : ""));
+}
+
+/**
+ * @brief called if one of the disk tasks (task_kwd or task_ksec) blocks
+ *
+ * @param task task that blocks (either task_ksec or task_kwd)
+ */
+void alto2_cpu_device::disk_block(int task)
+{
+ kwd_timing(m_dsk.bitclk, m_dsk.datin, task);
+}
+
+/**
+ * @brief bs_read_kstat early: bus driven by disk status register KSTAT
+ * <PRE>
+ * Part of the KSTAT register is made of two 4 bit latches S8T10 (Signetics).
+ * The signals BUS[8-11] are the current state of:
+ * BUS[0-3] SECT[0-3]; from the Winchester drive (inverted)
+ * BUS[8] SEEKOK'
+ * BUS[9] SRWRDY' from the Winchester drive
+ * BUS[10] RDYLAT' (latched READY' at last CLRSTAT, FF 45a output Q)
+ * BUS[11] SEQERR (latched SEQERR at last CLRSTAT, FF 45b output Q')
+ * The signals BUS[12,14-15] are just as they were loaded at KSTAT<- time.
+ * BUS[13] CHSEMERROR (FF 44b output Q' inverted)
+ * </PRE>
+ */
+void alto2_cpu_device::bs_early_read_kstat()
+{
+ diablo_hd_device* dhd = m_drive[m_dsk.drive];
+ UINT16 r;
+
+ /* KSTAT[4-7] bus is open */
+ PUT_KSTAT_DONE(m_dsk.kstat, 017);
+
+ /* KSTAT[8] latch the inverted seekok status */
+ PUT_KSTAT_SEEKFAIL(m_dsk.kstat, m_dsk.seekok ? 0 : 1);
+
+ /* KSTAT[9] latch the drive seek/read/write status */
+ PUT_KSTAT_SEEK(m_dsk.kstat, dhd->get_seek_read_write_0());
+
+ /* KSTAT[10] latch the latched (FF 45a at CLRSTAT) ready status (Q) */
+ PUT_KSTAT_NOTRDY(m_dsk.kstat, m_dsk.ff_45a & JKFF_Q ? 1 : 0);
+
+ /* KSTAT[11] latch the latched (FF 45b at CLRSTAT) seqerr status (Q') */
+ PUT_KSTAT_DATALATE(m_dsk.kstat, m_dsk.ff_45b & JKFF_Q ? 0 : 1);
+
+ /* KSTAT[13] latch the latched (FF 44b at CLRSTAT/KSTAT<-) checksum status */
+ PUT_KSTAT_CKSUM(m_dsk.kstat, m_dsk.ff_44b & JKFF_Q ? 1 : 0);
+
+ r = m_dsk.kstat;
+
+ LOG((LOG_DISK,1," <-KSTAT; BUS &= %#o\n", r));
+ LOG((LOG_DISK,2," SECTOR : %#o\n", GET_KSTAT_SECTOR(m_dsk.kstat)));
+ LOG((LOG_DISK,2," DONE : %#o\n", GET_KSTAT_DONE(m_dsk.kstat)));
+ LOG((LOG_DISK,2," SEEKFAIL : %d\n", GET_KSTAT_SEEKFAIL(m_dsk.kstat)));
+ LOG((LOG_DISK,2," SEEK : %d\n", GET_KSTAT_SEEK(m_dsk.kstat)));
+ LOG((LOG_DISK,2," NOTRDY : %d\n", GET_KSTAT_NOTRDY(m_dsk.kstat)));
+ LOG((LOG_DISK,2," DATALATE : %d\n", GET_KSTAT_DATALATE(m_dsk.kstat)));
+ LOG((LOG_DISK,2," IDLE : %d\n", GET_KSTAT_IDLE(m_dsk.kstat)));
+ LOG((LOG_DISK,2," CKSUM : %d\n", GET_KSTAT_CKSUM(m_dsk.kstat)));
+ LOG((LOG_DISK,2," COMPLETION : %#o\n", GET_KSTAT_COMPLETION(m_dsk.kstat)));
+
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_read_kdata early: bus driven by disk data register KDATA input
+ *
+ * The input data register is a latch that latches the contents of
+ * the lower 15 bits of a 16 bit shift register in its more significant
+ * 15 bits, and the current read data bit is the least significant
+ * bit. This is handled in kwd_timing.
+ */
+void alto2_cpu_device::bs_early_read_kdata()
+{
+ UINT16 r;
+ /* get the current word from the drive */
+ r = m_dsk.datain;
+ LOG((LOG_DISK,1," <-KDATA (%#o)\n", r));
+ m_bus &= r;
+}
+
+/**
+ * @brief f1_strobe late: initiates a disk seek
+ *
+ * Initiates a disk seek operation. The KDATA register must have
+ * been loaded previously, and the SENDADR bit of the KCOM
+ * register previously set to 1.
+ */
+void alto2_cpu_device::f1_late_strobe()
+{
+ if (GET_KCOM_SENDADR(m_dsk.kcom)) {
+ LOG((LOG_DISK,1," STROBE (SENDADR:1)\n"));
+ /* Set the STROBON flag and start the STROBON monoflop */
+ m_dsk.strobe = 1;
+ disk_strobon(0,
+ 4 * GET_KADDR_CYLINDER(m_dsk.kaddr) +
+ 2 * GET_KADDR_RESTORE(m_dsk.kaddr) +
+ m_dsk.drive);
+ } else {
+ LOG((LOG_DISK,1," STROBE (w/o SENDADR)\n"));
+ /* FIXME: what to do if SENDADR isn't set? */
+ }
+}
+
+/**
+ * @brief f1_load_kstat late: load disk status register
+ *
+ * KSTAT[12-15] are loaded from BUS[12-15], except that BUS[13] is
+ * ORed into KSTAT[13].
+ *
+ * NB: The 4 bits are just software, not changed by hardware
+ */
+void alto2_cpu_device::f1_late_load_kstat()
+{
+ LOG((LOG_DISK,1," KSTAT<-; BUS[12-15] %#o\n", m_bus));
+ LOG((LOG_DISK,2," IDLE : %d\n", GET_KSTAT_IDLE(m_bus)));
+ LOG((LOG_DISK,2," CKSUM : %d\n", GET_KSTAT_CKSUM(m_bus)));
+ LOG((LOG_DISK,2," COMPLETION : %#o\n", GET_KSTAT_COMPLETION(m_bus)));
+
+ /* KSTAT[12] is just taken from BUS[12] */
+ PUT_KSTAT_IDLE(m_dsk.kstat, GET_KSTAT_IDLE(m_bus));
+
+ /* KSTAT[14-15] are just taken from BUS[14-15] */
+ PUT_KSTAT_COMPLETION(m_dsk.kstat, GET_KSTAT_COMPLETION(m_bus));
+
+ /* May set the the CKSUM flip-flop 44b
+ * JK flip-flop 44b (KSTAT<- clocked)
+ * CLK SYSCLKA'
+ * J !BUS[13]
+ * K' 1
+ * S' 1
+ * C' CLRSTAT' (not now)
+ * Q Q' inverted to BUS[13] on <-KSTAT
+ */
+ for (int i = 0; i < 2; i++) {
+ UINT8 s0, s1;
+ s0 = m_dsk.ff_44b;
+ s1 = i ? JKFF_CLK : JKFF_0;
+ if (!GET_KSTAT_CKSUM(m_bus))
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ s1 |= JKFF_C;
+ m_dsk.ff_44b = update_jkff(s0, s1, "44b CKSUM ");
+ }
+}
+
+/**
+ * @brief f1_load_kdata late: load data out register, or the disk address register
+ *
+ * KDATA is loaded from BUS.
+ */
+void alto2_cpu_device::f1_late_load_kdata()
+{
+ m_dsk.dataout = m_bus;
+ if (GET_KCOM_SENDADR(m_dsk.kcom)) {
+ PUT_KADDR_SECTOR(m_dsk.kaddr, GET_KADDR_SECTOR(m_bus));
+ PUT_KADDR_CYLINDER(m_dsk.kaddr, GET_KADDR_CYLINDER(m_bus));
+ PUT_KADDR_HEAD(m_dsk.kaddr, GET_KADDR_HEAD(m_bus));
+ PUT_KADDR_DRIVE(m_dsk.kaddr, GET_KADDR_DRIVE(m_bus));
+ PUT_KADDR_RESTORE(m_dsk.kaddr, GET_KADDR_RESTORE(m_bus));
+ PUT_KADDR_DRIVE(m_dsk.kaddr, GET_KADDR_DRIVE(m_bus));
+ m_dsk.drive = GET_KADDR_DRIVE(m_dsk.kaddr);
+
+ LOG((LOG_DISK,1," KDATA<-; BUS (%#o) (drive:%d restore:%d %d/%d/%02d)\n",
+ m_bus,
+ GET_KADDR_DRIVE(m_dsk.kaddr),
+ GET_KADDR_RESTORE(m_dsk.kaddr),
+ GET_KADDR_CYLINDER(m_dsk.kaddr),
+ GET_KADDR_HEAD(m_dsk.kaddr),
+ GET_KADDR_SECTOR(m_dsk.kaddr)));
+#if 0
+ /* printing changes in the disk address */
+ {
+ static int last_kaddr;
+ if (m_dsk.kaddr != last_kaddr) {
+ int c = GET_KADDR_CYLINDER(m_dsk.kaddr);
+ int h = GET_KADDR_HEAD(m_dsk.kaddr);
+ int s = GET_KADDR_SECTOR(m_dsk.kaddr);
+ int page = DRIVE_PAGE(c,h,s);
+ last_kaddr = m_dsk.kaddr;
+ printf(" unit:%d restore:%d %d/%d/%02d page:%d\n",
+ GET_KADDR_DRIVE(m_dsk.kaddr),
+ GET_KADDR_RESTORE(m_dsk.kaddr),
+ c, h, s, page);
+ }
+ }
+#endif
+ } else {
+ LOG((LOG_DISK,1," KDATA<-; BUS %#o (%#x)\n", m_bus, m_bus));
+ }
+}
+
+/**
+ * @brief f1_increcno late: advances shift registers holding KADR
+ *
+ * Advances the shift registers holding the KADR register so that they
+ * present the number and read/write/check status of the next record
+ * to the hardware.
+ *
+ * <PRE>
+ * Sheet 10, shifter (74195) parts #36 and #37
+ *
+ * Vcc, BUS[08], BUS[10], BUS[12] go to #36 A,B,C,D
+ * Vcc, BUS[09], BUS[11], BUS[13] go to #37 A,B,C,D
+ * A is connected to ground on both chips;
+ * both shifters are loaded with KADR<-
+ *
+ * The QA outputs are #36 -> RECNO(0) and #37 -> RECNO(1)
+ *
+ * RECNO(0) (QA of #37) goes to J and K' of #36
+ * RECNO(1) (QA of #36) is inverted and goes to J and K' of #37
+ *
+ * shift/ RECNO(0) RECNO(1) R/W/C presented
+ * load #37 #36 to the drive
+ * ---------------------------------------------------
+ * load 0 0 HEADER
+ * 1st shift 1 0 LABEL
+ * 2nd shift 1 1 DATA
+ * 3rd shift 0 1 (none) 0 = read
+ * [ 4th 0 0 (none) 2 = write ]
+ * [ 5th 1 0 (none) 3 = write ]
+ * [ 6th 1 1 (none) 1 = check ]
+ * </PRE>
+ */
+void alto2_cpu_device::f1_late_increcno()
+{
+ switch (m_dsk.krecno) {
+ case RECNO_HEADER:
+ m_dsk.krecno = RECNO_LABEL;
+ m_dsk.krwc = GET_KADR_LABEL(m_dsk.kadr);
+ LOG((LOG_DISK,2," INCRECNO; HEADER -> LABEL (%o, rwc:%o)\n", m_dsk.krecno, m_dsk.krwc));
+ break;
+ case RECNO_NOTHING:
+ m_dsk.krecno = RECNO_HEADER;
+ m_dsk.krwc = GET_KADR_HEADER(m_dsk.kadr);
+ LOG((LOG_DISK,2," INCRECNO; NOTHING -> HEADER (%o, rwc:%o)\n", m_dsk.krecno, m_dsk.krwc));
+ break;
+ case RECNO_LABEL:
+ m_dsk.krecno = RECNO_DATA;
+ m_dsk.krwc = GET_KADR_DATA(m_dsk.kadr);
+ LOG((LOG_DISK,2," INCRECNO; LABEL -> DATA (%o, rwc:%o)\n", m_dsk.krecno, m_dsk.krwc));
+ break;
+ case RECNO_DATA:
+ m_dsk.krecno = RECNO_NOTHING;
+ m_dsk.krwc = 0; /* read (?) */
+ LOG((LOG_DISK,2," INCRECNO; DATA -> NOTHING (%o, rwc:%o)\n", m_dsk.krecno, m_dsk.krwc));
+ break;
+ }
+ // TODO: show disk indicator
+ FAKE_STATUS(m_dsk.drive, STATUS_KWRC, "HPLD"[m_dsk.krecno]);
+}
+
+/**
+ * @brief f1_clrstat late: reset all error latches
+ *
+ * Causes all error latches in the disk controller hardware to reset,
+ * clears KSTAT[13].
+ *
+ * NB: IDLE (KSTAT[12]) and COMPLETION (KSTAT[14-15]) are not cleared
+ */
+void alto2_cpu_device::f1_late_clrstat()
+{
+ diablo_hd_device* dhd = m_drive[m_dsk.drive];
+ UINT8 s0, s1;
+
+ /* clears the LAI clocked flip-flop 44a
+ * JK flip-flop 44a (LAI' clocked)
+ * CLK (LAI')'
+ * J 1
+ * K' 1
+ * S' 1
+ * C' CLRSTAT'
+ * Q to seekok
+ */
+ s0 = m_dsk.ff_44a;
+ s1 = m_dsk.ff_44a & JKFF_CLK;
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ s1 &= ~JKFF_C;
+ m_dsk.ff_44a = update_jkff(s0, s1, "44a LAI ");
+
+ /* clears the CKSUM flip-flop 44b
+ * JK flip-flop 44b (KSTAT<- clocked)
+ * CLK SYSCLKA' (not used here, just clearing)
+ * J 1 (BUS[13] during KSTAT<-)
+ * K' 1
+ * S' 1
+ * C' CLRSTAT'
+ * Q to seekok
+ */
+ s0 = m_dsk.ff_44b;
+ s1 = m_dsk.ff_44b & JKFF_CLK;
+ s1 |= m_dsk.ff_44b & JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ s1 &= ~JKFF_C;
+ m_dsk.ff_44b = update_jkff(s0, s1, "44b CKSUM ");
+
+ /* clears the rdylat flip-flop 45a
+ * JK flip-flop 45a (ready latch)
+ * CLK SYSCLKA'
+ * J READY' from drive
+ * K' 1
+ * S' 1
+ * C' CLRSTAT'
+ * Q RDYLAT'
+ */
+ s0 = m_dsk.ff_45a;
+ s1 = m_dsk.ff_45a & JKFF_CLK;
+ if (dhd->get_ready_0())
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ s1 &= ~JKFF_C;
+ m_dsk.ff_45a = update_jkff(s0, s1, "45a RDYLAT");
+
+ /* sets the seqerr flip-flop 45b (Q' is SEQERR)
+ * JK flip-flop 45b (seqerr latch)
+ * CLK SYSCLKA'
+ * J 1
+ * K' SEQERR'
+ * S' CLRSTAT'
+ * C' 1
+ * Q to KSTAT[11] DATALATE
+ */
+ s0 = m_dsk.ff_45b;
+ s1 = m_dsk.ff_45b & JKFF_CLK;
+ s1 |= JKFF_J;
+ if (!SEQERR)
+ s1 |= JKFF_K;
+ s1 &= ~JKFF_S;
+ s1 |= JKFF_C;
+ m_dsk.ff_45b = update_jkff(s0, s1, "45b SEQERR");
+
+ /* set or reset monoflop 31a, depending on drive READY' */
+ m_dsk.ready_mf31a = dhd->get_ready_0();
+
+ /* start monoflop 31a, which resets ready_mf31a */
+ m_dsk.ready_timer->adjust(attotime::from_nsec(TW_READY), 1);
+
+ LOG((LOG_DISK,1," CLRSTAT (44a:%d 44b:%d 45a:%d 45b:%d 31a:%d)\n",
+ m_dsk.ff_44a & JKFF_Q ? 1 : 0, m_dsk.ff_44b & JKFF_Q ? 1 : 0,
+ m_dsk.ff_45a & JKFF_Q ? 1 : 0, m_dsk.ff_45b & JKFF_Q ? 1 : 0,
+ m_dsk.ready_mf31a));
+}
+
+/**
+ * @brief f1_load_kcom late: load the KCOM register from bus
+ * <PRE>
+ * This causes the KCOM register to be loaded from BUS[1-5]. The
+ * KCOM register has the following interpretation:
+ * (1) XFEROFF = 1 inhibits data transmission to/from the m_dsk.
+ * (2) WDINHIB = 1 prevents the disk word task from awakening.
+ * (3) BCLKSRC = 0 takes bit clock from disk input or crystal clock, as appropriate.
+ * BCLKSRC = 1 force use of crystal clock.
+ * (4) WFFO = 0 holds the disk bit counter at -1 until a 1 bit is read.
+ * WFFO = 1 allows the bit counter to proceed normally.
+ * (5) SENDADR = 1 causes KDATA[4-12] and KDATA[15] to be signalled to disk unit as track address.
+ * SENDADR = 0 inhibits such signalling.
+ * </PRE>
+ */
+void alto2_cpu_device::f1_late_load_kcom()
+{
+ UINT16 change = m_dsk.kcom ^ m_bus;
+ m_dsk.kcom = m_bus;
+ LOG((LOG_DISK,2," KCOM<-; BUS %06o\n", m_dsk.kcom));
+ LOG((LOG_DISK,2," XFEROFF : %d\n", GET_KCOM_XFEROFF(m_dsk.kcom)));
+ LOG((LOG_DISK,2," WDINHIB : %d\n", GET_KCOM_WDINHIB(m_dsk.kcom)));
+ LOG((LOG_DISK,2," BCLKSRC : %d\n", GET_KCOM_BCLKSRC(m_dsk.kcom)));
+ LOG((LOG_DISK,2," WFFO : %d\n", GET_KCOM_WFFO(m_dsk.kcom)));
+ LOG((LOG_DISK,2," SENDADR : %d\n", GET_KCOM_SENDADR(m_dsk.kcom)));
+ if (GET_KCOM_WDINHIB(change)) {
+ // WDALLOW going 0: should asynchronously reset 43a and 53a and set 53b
+ if (m_task == task_kwd) {
+ UINT8 s0, s1;
+ /**
+ * JK flip-flop 53b (word task)
+ * <PRE>
+ * CLK SYSCLKB'
+ * J 0
+ * K' (BLOCK & WDTSKACT)'
+ * S' WDALLOW
+ * C' 1
+ * Q WDINIT
+ * </PRE>
+ */
+ s0 = m_dsk.ff_53b;
+ s1 = JKFF_0;
+ if (WDALLOW)
+ s1 |= JKFF_S;
+ s1 |= JKFF_C;
+ m_dsk.ff_53b = update_jkff(s0, s1, "53b KWD ");
+
+ /**
+ * JK flip-flop 53a (word task)
+ * <PRE>
+ * CLK SYSCLKB'
+ * J from 43b Q
+ * K' (BLOCK & WDTSKACT)'
+ * S' 1
+ * C' WDALLOW
+ * Q to 43a J and K'
+ * </PRE>
+ */
+ s0 = m_dsk.ff_53a;
+ s1 = JKFF_0;
+ if (m_dsk.ff_43b & JKFF_Q)
+ s1 |= JKFF_J;
+ s1 |= JKFF_S;
+ if (WDALLOW)
+ s1 |= JKFF_C;
+ m_dsk.ff_53a = update_jkff(s0, s1, "53a KWD ");
+
+ /**
+ * JK flip-flop 43a (word task)
+ * <PRE>
+ * CLK SYSCLKA'
+ * J from 53a Q
+ * K' from 53a Q
+ * S' 1
+ * C' WDALLOW
+ * Q WDTSKENA', Q' WDTSKENA
+ * </PRE>
+ */
+ s0 = m_dsk.ff_43a;
+ s1 = JKFF_0;
+ if (m_dsk.ff_53a & JKFF_Q)
+ s1 |= JKFF_J;
+ if (m_dsk.ff_53a & JKFF_Q)
+ s1 |= JKFF_K;
+ s1 |= JKFF_S;
+ if (WDALLOW)
+ s1 |= JKFF_C;
+ m_dsk.ff_43a = update_jkff(s0, s1, "43a KWD ");
+ }
+ }
+ // TODO: show disk indicator in the GUI?
+}
+
+/**
+ * @brief f1_load_kadr late: load the KADR register from bus
+ *
+ * The KADR register is loaded from BUS[8-14]. This register has the format
+ * of word C in section 6.0 above. In addition, it causes the head address
+ * bit to be loaded from KDATA[13].
+ *
+ * NB: the record numer RECNO(0) and RECNO(1) is reset to 0
+ */
+void alto2_cpu_device::f1_late_load_kadr()
+{
+ /* store into the separate fields of KADR */
+ PUT_KADR_SEAL(m_dsk.kadr, GET_KADR_SEAL(m_bus));
+ PUT_KADR_HEADER(m_dsk.kadr, GET_KADR_HEADER(m_bus));
+ PUT_KADR_LABEL(m_dsk.kadr, GET_KADR_LABEL(m_bus));
+ PUT_KADR_DATA(m_dsk.kadr, GET_KADR_DATA(m_bus));
+ PUT_KADR_NOXFER(m_dsk.kadr, GET_KADR_NOXFER(m_bus));
+ PUT_KADR_UNUSED(m_dsk.kadr, GET_KADR_UNUSED(m_bus));
+
+ int unit = GET_KADDR_DRIVE(m_dsk.kaddr); // get selected drive from DATA[14] output (FF 67a really)
+ int head = GET_KADDR_HEAD(m_dsk.dataout); // latch head from DATA[13]
+ PUT_KADDR_HEAD(m_dsk.kaddr, head); // store in KADDR
+
+ // select drive unit
+ diablo_hd_device* dhd = m_drive[unit];
+ dhd->select(unit);
+ // set selected head
+ dhd->set_head(head);
+
+ // On KDAR<- load bit 0 of parts #36 and #37 is reset to 0, i.e. recno = 0
+ m_dsk.krecno = 0;
+ // current read/write/check is that for the header
+ m_dsk.krwc = GET_KADR_HEADER(m_dsk.kadr);
+
+ LOG((LOG_DISK,1," KADR<-; BUS[8-14] #%o\n", m_dsk.kadr));
+ LOG((LOG_DISK,2," SEAL : %d\n", GET_KADR_SEAL(m_dsk.kadr)));
+ LOG((LOG_DISK,2," HEADER : %s (%#o)\n", rwc_name[GET_KADR_HEADER(m_dsk.kadr)], GET_KADR_HEADER(m_dsk.kadr)));
+ LOG((LOG_DISK,2," LABEL : %s (%#o)\n", rwc_name[GET_KADR_LABEL(m_dsk.kadr)], GET_KADR_LABEL(m_dsk.kadr)));
+ LOG((LOG_DISK,2," DATA : %s (%#o)\n", rwc_name[GET_KADR_DATA(m_dsk.kadr)], GET_KADR_DATA(m_dsk.kadr)));
+ LOG((LOG_DISK,2," NOXFER : %d\n", GET_KADR_NOXFER(m_dsk.kadr)));
+ LOG((LOG_DISK,2," unused : %d (drive?)\n", GET_KADR_UNUSED(m_dsk.kadr)));
+ // TODO: show disk indicator in the GUI?
+}
+
+/**
+ * @brief branch on disk word task active and init
+ *
+ * NEXT <- NEXT OR (WDTASKACT && WDINIT ? 037 : 0)
+ */
+void alto2_cpu_device::f2_late_init()
+{
+ // INIT = current task == KWD and WDINIT
+ UINT16 r = (m_task == task_kwd && m_dsk.wdinit0) ? 037 : 0;
+ LOG((LOG_DISK,1," INIT; %sbranch (%#o | %#o)\n", r ? "" : "no ", m_next2, r));
+ m_next2 |= r;
+ m_dsk.wdinit0 = 0;
+}
+
+/**
+ * @brief branch on read/write/check state of the current record
+ * <PRE>
+ * NEXT <- NEXT OR (current record to be written ? 3 : current record to be checked ? 2 : 0);
+ *
+ * NB: note how krecno counts 0,2,3,1 ... etc.
+ * on 0: it presents the RWC for HEADER
+ * on 2: it presents the RWC for LABEL
+ * on 3: it presents the RWC for DATA
+ * on 1: it presents the RWC 0, i.e. READ
+ *
+ * -NEXT[08] = -CHECK = RWC[0] | RWC[1]
+ * -NEXT[09] = W/R = RWC[0]
+ *
+ * rwc | -next
+ * -------+------
+ * 0 0 | 0
+ * 0 1 | 2
+ * 1 0 | 3
+ * 1 1 | 3
+ * </PRE>
+ */
+void alto2_cpu_device::f2_late_rwc()
+{
+ UINT16 r;
+ UINT16 init = (m_task == task_kwd && m_dsk.wdinit0) ? 037 : 0;
+
+ switch (m_dsk.krwc & 3) {
+ case 0: // read
+ r = 0;
+ break;
+ case 1: // check
+ r = 2;
+ break;
+ default: // write
+ r = 3;
+ }
+
+ switch (m_dsk.krecno) {
+ case RECNO_HEADER:
+ LOG((LOG_DISK,1," RWC; %sbranch header(%d):%s (%#o|%#o|%#o)\n",
+ (r | init) ? "" : "no ", m_dsk.krecno,
+ rwc_name[m_dsk.krwc], m_next2, r, init));
+ break;
+ case RECNO_NOTHING:
+ LOG((LOG_DISK,1," RWC; %sbranch pageno(%d):%s (%#o|%#o|%#o)\n",
+ (r | init) ? "" : "no ", m_dsk.krecno,
+ rwc_name[m_dsk.krwc], m_next2, r, init));
+ break;
+ case RECNO_LABEL:
+ LOG((LOG_DISK,1," RWC; %sbranch label(%d):%s (%#o|%#o|%#o)\n",
+ (r | init) ? "" : "no ", m_dsk.krecno,
+ rwc_name[m_dsk.krwc], m_next2, r, init));
+ break;
+ case RECNO_DATA:
+ LOG((LOG_DISK,1," RWC; %sbranch data(%d):%s (%#o|%#o|%#o)\n",
+ (r | init) ? "" : "no ", m_dsk.krecno,
+ rwc_name[m_dsk.krwc], m_next2, r, init));
+ break;
+ }
+ m_next2 |= r | init;
+ m_dsk.wdinit0 = 0;
+}
+
+/**
+ * @brief f2_recno late: branch on the current record number by a lookup table
+ * <PRE>
+ * NEXT <- NEXT OR MAP (current record number) where
+ * MAP(0) = 0 (header)
+ * MAP(1) = 2 (label)
+ * MAP(2) = 3 (pageno)
+ * MAP(3) = 1 (data)
+ * </PRE>
+ * NB: The map isn't needed, because m_dsk.krecno counts exactly this way.
+ */
+void alto2_cpu_device::f2_late_recno()
+{
+ UINT16 r = m_dsk.krecno;
+ UINT16 init = (m_task == task_kwd && m_dsk.wdinit0) ? 037 : 0;
+ LOG((LOG_DISK,1," RECNO; %sbranch recno:%d (%#o|%#o|%#o)\n", (r | init) ? "" : "no ", m_dsk.krecno, m_next2, r, init));
+ m_next2 |= r | init;
+ m_dsk.wdinit0 = 0;
+}
+
+/**
+ * @brief branch on the data transfer state
+ *
+ * NEXT <- NEXT OR (if current command wants data transfer ? 1 : 0)
+ */
+void alto2_cpu_device::f2_late_xfrdat()
+{
+ UINT16 r = GET_KADR_NOXFER(m_dsk.kadr) ? 0 : 1;
+ UINT16 init = (m_task == task_kwd && m_dsk.wdinit0) ? 037 : 0;
+ LOG((LOG_DISK,1," XFRDAT; %sbranch (%#o|%#o|%#o)\n", (r | init) ? "" : "no ", m_next2, r, init));
+ m_next2 |= r | init;
+ m_dsk.wdinit0 = 0;
+}
+
+/**
+ * @brief branch on the disk ready signal
+ *
+ * NEXT <- NEXT OR (if disk not ready to accept command ? 1 : 0)
+ */
+void alto2_cpu_device::f2_late_swrnrdy()
+{
+ diablo_hd_device* dhd = m_drive[m_dsk.drive];
+ UINT16 r = dhd->get_seek_read_write_0();
+ UINT16 init = (m_task == task_kwd && m_dsk.wdinit0) ? 037 : 0;
+
+ LOG((LOG_DISK,1," SWRNRDY; %sbranch (%#o|%#o|%#o)\n", (r | init) ? "" : "no ", m_next2, r, init));
+ m_next2 |= r | init;
+ m_dsk.wdinit0 = 0;
+}
+
+/**
+ * @brief branch on the disk fatal error condition
+ *
+ * NEXT <- NEXT OR (if fatal error in latches ? 0 : 1)
+ */
+void alto2_cpu_device::f2_late_nfer()
+{
+ UINT16 r = m_dsk.kfer ? 0 : 1;
+ UINT16 init = (m_task == task_kwd && m_dsk.wdinit0) ? 037 : 0;
+
+ LOG((LOG_DISK,1," NFER; %sbranch (%#o|%#o|%#o)\n", (r | init) ? "" : "no ", m_next2, r, init));
+ m_next2 |= r | init;
+ m_dsk.wdinit0 = 0;
+}
+
+/**
+ * @brief f2_strobon late: branch on the seek busy status
+ *
+ * NEXT <- NEXT OR (if seek strobe still on ? 1 : 0)
+ * <PRE>
+ * The STROBE signal is elongated with the help of two monoflops.
+ * The first one has a rather short pulse duration:
+ * tW = K * Rt * Cext * (1 + 0.7/Rt)
+ * K = 0.28 for 74123
+ * Rt = kOhms
+ * Cext = pF
+ * Rt = 20k, Cext = 150pf => 870ns
+ *
+ * The first one triggers the second, which will be cleared
+ * by ADDRACK' from the drive going 0.
+ * Its duration is:
+ * Rt = 20k, Cext = 0.01uF (=10000pF) => 57960ns (~= 58us)
+ * </PRE>
+ */
+void alto2_cpu_device::f2_late_strobon()
+{
+ UINT16 r = m_dsk.strobe;
+ UINT16 init = (m_task == task_kwd && m_dsk.wdinit0) ? 037 : 0;
+
+ LOG((LOG_DISK,2," STROBON; %sbranch (%#o|%#o|%#o)\n", (r | init) ? "" : "no ", m_next2, r, init));
+ m_next2 |= r | init;
+ m_dsk.wdinit0 = 0;
+}
+
+/**
+ * @brief update the disk controller with a new bitclk
+ *
+ * @param id timer id
+ * @param arg bit number
+ */
+void alto2_cpu_device::disk_bitclk(void* ptr, INT32 arg)
+{
+ (void)ptr;
+ diablo_hd_device* dhd = m_drive[m_dsk.drive];
+ int clk = arg & 1;
+ int bit = 0;
+
+ /**
+ * The source for BITCLK and DATAIN depends on disk controller part #65
+ * <PRE>
+ * BCLKSRC W/R | BITCLK | DATAIN
+ * --------------+--------+---------
+ * 0 0 | RDCLK | RDDATA
+ * 0 1 | CLK/2 | DATOUT
+ * 1 0 | CLK/2 | RDDATA
+ * 1 1 | CLK/2 | DATOUT
+ * </PRE>
+ */
+ if (m_dsk.krwc & RWC_WRITE) {
+ if (GET_KCOM_XFEROFF(m_dsk.kcom)) {
+ /* do anything, if the transfer is off? */
+ kwd_timing(clk, 1, 0);
+ } else {
+ bit = (m_dsk.shiftout >> 15) & 1;
+ kwd_timing(clk, bit, 0);
+ LOG((LOG_DISK,8," BITCLK#%d bit:%d (write) @%lldns\n", arg, bit, ntime()));
+ if (clk)
+ dhd->wr_data(arg, bit);
+ else
+ dhd->wr_data(arg, 1);
+ }
+ } else if (GET_KCOM_BCLKSRC(m_dsk.kcom)) {
+ /* always select the crystal clock */
+ bit = dhd->rd_data(arg);
+ LOG((LOG_DISK,8," BITCLK#%d bit:%d (read, crystal) @%lldns\n", arg, bit, ntime()));
+ kwd_timing(clk, bit, 0);
+ } else {
+ /* if XFEROFF is set, keep the bit at 1 (RDGATE' is high) */
+ if (GET_KCOM_XFEROFF(m_dsk.kcom)) {
+ bit = 1;
+ } else {
+ clk = dhd->rd_clock(arg);
+ bit = dhd->rd_data(arg);
+ LOG((LOG_DISK,8," BITCLK#%d bit:%d (read, driveclk) @%lldns\n", arg, bit, ntime()));
+ }
+ kwd_timing(clk, bit, 0);
+ }
+
+#if USE_BITCLK_TIMER
+ /* more bits to clock? */
+ if (++arg < dhd->bits_per_sector()) {
+ m_dsk.bitclk_timer->adjust(dhd->bit_time(), arg);
+ } else {
+ m_dsk.bitclk_timer->reset();
+ }
+#else
+ if (++arg < dhd->bits_per_sector()) {
+ m_bitclk_time += m_dsk.bitclk_time[m_dsk.drive];
+ m_bitclk_index = arg;
+ } else {
+ // stop the bitclock timer
+ m_bitclk_time = -1;
+ }
+#endif
+}
+
+/**
+ * @brief callback is called by the drive timer whenever a new sector starts
+ *
+ * @param unit the unit number
+ */
+void alto2_cpu_device::next_sector(int unit)
+{
+ diablo_hd_device* dhd = m_drive[unit];
+ LOG((LOG_DISK,0,"%s dhd=%p\n", __FUNCTION__, dhd));
+ // get bit time in pico seconds
+ m_dsk.bitclk_time[unit] = static_cast<int>(dhd->bit_time().as_attoseconds() / 1000000);
+#if USE_BITCLK_TIMER
+ LOG((LOG_DISK,0," unit #%d stop bitclk\n", unit));
+ m_dsk.bitclk_timer->enable(false);
+#else
+ if (m_bitclk_time >= 0) {
+ LOG((LOG_DISK,0," unit #%d stop bitclk\n", unit));
+ m_bitclk_time = -1;
+ m_bitclk_index = -1;
+ }
+#endif
+
+ /* KSTAT[0-3] update the current sector in the kstat field */
+ PUT_KSTAT_SECTOR(m_dsk.kstat, dhd->get_sector());
+
+ /* clear input and output shift registers (?) */
+ m_dsk.shiftin = 0;
+ m_dsk.shiftout = 0;
+
+ LOG((LOG_DISK,1," unit #%d sector %d start\n", unit, GET_KSTAT_SECTOR(m_dsk.kstat)));
+
+#if USE_BITCLK_TIMER
+ // HACK: no command, no bit clock
+ if (debug_read_mem(0521))
+ /* start a timer chain for the bit clock */
+ disk_bitclk(0, 0);
+#else
+ // TODO: verify current sector == requested sector and only then run the bitclk?
+ // HACK: no command, no bit clock
+ if (debug_read_mem(0521))
+ {
+ // Make the CPU execution loop call disk_bitclk
+ m_bitclk_time = 0;
+ m_bitclk_index = 0;
+ }
+#endif
+#if defined(ALTO2_FAKE_STATUS_H) && (ALTO2_FAKE_STATUS_H > 0)
+ if (debug_read_mem(0521) && unit == GET_KADDR_DRIVE(debug_read_mem(0523)))
+ {
+ FAKE_STATUS(unit, STATUS_SECT, dhd->get_sector());
+ FAKE_STATUS(unit, STATUS_PAGE, dhd->get_page());
+ }
+#endif
+}
+
+/**
+ * @brief initialize the disk context and insert a disk wort timer
+ *
+ * @result returns 0 on success, fatal() on error
+ */
+void alto2_cpu_device::init_disk()
+{
+ memset(&m_dsk, 0, sizeof(m_dsk));
+ save_item(NAME(m_dsk.drive));
+ save_item(NAME(m_dsk.kaddr));
+ save_item(NAME(m_dsk.kadr));
+ save_item(NAME(m_dsk.kstat));
+ save_item(NAME(m_dsk.kcom));
+ save_item(NAME(m_dsk.krecno));
+ save_item(NAME(m_dsk.shiftin));
+ save_item(NAME(m_dsk.shiftout));
+ save_item(NAME(m_dsk.datain));
+ save_item(NAME(m_dsk.dataout));
+ save_item(NAME(m_dsk.krwc));
+ save_item(NAME(m_dsk.kfer));
+ save_item(NAME(m_dsk.wdtskena));
+ save_item(NAME(m_dsk.wdinit0));
+ save_item(NAME(m_dsk.wdinit));
+ save_item(NAME(m_dsk.strobe));
+ save_item(NAME(m_dsk.bitclk));
+ save_item(NAME(m_dsk.datin));
+ save_item(NAME(m_dsk.bitcount));
+ save_item(NAME(m_dsk.carry));
+ save_item(NAME(m_dsk.seclate));
+ save_item(NAME(m_dsk.seekok));
+ save_item(NAME(m_dsk.ok_to_run));
+ save_item(NAME(m_dsk.ready_mf31a));
+ save_item(NAME(m_dsk.seclate_mf31b));
+#if 0
+ save_item(NAME(m_dsk.ff_21a));
+ save_item(NAME(m_dsk.ff_21a_old));
+ save_item(NAME(m_dsk.ff_21b));
+ save_item(NAME(m_dsk.ff_22a));
+ save_item(NAME(m_dsk.ff_22b));
+ save_item(NAME(m_dsk.ff_43b));
+ save_item(NAME(m_dsk.ff_53a));
+ save_item(NAME(m_dsk.ff_43a));
+ save_item(NAME(m_dsk.ff_53b));
+ save_item(NAME(m_dsk.ff_44a));
+ save_item(NAME(m_dsk.ff_44b));
+ save_item(NAME(m_dsk.ff_45a));
+ save_item(NAME(m_dsk.ff_45b));
+#endif
+
+ /** @brief simulate previous sysclka */
+ m_sysclka0[0] = JKFF_CLK;
+ m_sysclka0[1] = JKFF_0;
+ m_sysclka0[2] = JKFF_0;
+ m_sysclka0[3] = JKFF_CLK;
+
+ /** @brief simulate current sysclka */
+ m_sysclka1[0] = JKFF_0;
+ m_sysclka1[1] = JKFF_0;
+ m_sysclka1[2] = JKFF_CLK;
+ m_sysclka1[3] = JKFF_CLK;
+
+ /** @brief simulate previous sysclkb */
+ m_sysclkb0[0] = JKFF_CLK;
+ m_sysclkb0[1] = JKFF_CLK;
+ m_sysclkb0[2] = JKFF_0;
+ m_sysclkb0[3] = JKFF_0;
+
+ /** @brief simulate current sysclkb */
+ m_sysclkb1[0] = JKFF_CLK;
+ m_sysclkb1[1] = JKFF_0;
+ m_sysclkb1[2] = JKFF_0;
+ m_sysclkb1[3] = JKFF_CLK;
+
+ m_dsk.wdtskena = 1;
+ m_dsk.egate = 1;
+ m_dsk.wrgate = 1;
+ m_dsk.rdgate = 1;
+
+ m_dsk.seclate = 0;
+ m_dsk.ok_to_run = 0;
+
+ m_dsk.kcom = 066000;
+
+#if USE_BITCLK_TIMER
+ m_dsk.bitclk_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::disk_bitclk),this));
+#endif
+
+ m_dsk.strobon_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::disk_strobon),this));
+ m_dsk.strobon_timer->reset();
+
+ m_dsk.seclate_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::disk_seclate),this));
+ m_dsk.seclate_timer->reset();
+
+ m_dsk.ok_to_run_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::disk_ok_to_run),this));
+ m_dsk.ok_to_run_timer->adjust(attotime::from_nsec(35 * ALTO2_UCYCLE / 1000), 1);
+
+ m_dsk.ready_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::disk_ready_mf31a),this));
+ m_dsk.ready_timer->reset();
+
+ m_dsk.bitclk_time[0] = static_cast<int>(attotime::from_nsec(300).as_attoseconds() / 1000000);
+ m_dsk.bitclk_time[1] = static_cast<int>(attotime::from_nsec(300).as_attoseconds() / 1000000);
+}
+
+/**
+ * @brief exit disk controller - free all timers?
+ */
+void alto2_cpu_device::exit_disk()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_disk()
+{
+ m_dsk.drive = 0;
+ m_dsk.kaddr = 0;
+ m_dsk.kadr = 0;
+ m_dsk.kstat = 0;
+ m_dsk.kcom = 066000;
+ m_dsk.krecno = 0;
+ m_dsk.egate = 1;
+ m_dsk.wrgate = 1;
+ m_dsk.rdgate = 1;
+ m_dsk.shiftin = 0;
+ m_dsk.shiftout = 0;
+ m_dsk.datain = 0;
+ m_dsk.dataout = 0;
+ m_dsk.krwc = 0;
+ m_dsk.kfer = 0;
+ m_dsk.wdtskena = 1;
+ m_dsk.wddone = 0;
+ m_dsk.wdinit0 = 0;
+ m_dsk.wdinit = 0;
+ m_dsk.strobe = 0;
+ m_dsk.strobon_timer->reset();
+ m_dsk.bitclk = 0;
+#if USE_BITCLK_TIMER
+ m_dsk.bitclk_timer->reset();
+#else
+ m_dsk.bitclk_time[0] = static_cast<int>(attotime::from_nsec(300).as_attoseconds() / 1000000);
+ m_dsk.bitclk_time[1] = static_cast<int>(attotime::from_nsec(300).as_attoseconds() / 1000000);
+#endif
+ m_dsk.datin = 0;
+ m_dsk.bitcount = 0;
+ m_dsk.seclate = 0;
+ m_dsk.seclate_timer->reset();
+ m_dsk.seekok = 0;
+ m_dsk.ok_to_run = 0;
+ m_dsk.ok_to_run_timer->adjust(attotime::from_nsec(35 * ALTO2_UCYCLE / 1000), 1);
+ m_dsk.ready_mf31a = 0;
+ m_dsk.ready_timer->reset();
+ m_dsk.seclate_mf31b = 0;
+ m_dsk.ff_21a = JKFF_0;
+ m_dsk.ff_21a_old = JKFF_0;
+ m_dsk.ff_21b = JKFF_0;
+ m_dsk.ff_22a = JKFF_0;
+ m_dsk.ff_22b = JKFF_0;
+ m_dsk.ff_43b = JKFF_0;
+ m_dsk.ff_53a = JKFF_0;
+ m_dsk.ff_43a = JKFF_0;
+ m_dsk.ff_53b = JKFF_0;
+ m_dsk.ff_44a = JKFF_0;
+ m_dsk.ff_44b = JKFF_0;
+ m_dsk.ff_45a = JKFF_0;
+ m_dsk.ff_45b = JKFF_0;
+
+#if defined(ALTO2_FAKE_STATUS_H) && (ALTO2_FAKE_STATUS_H > 0)
+ for (int unit = 0; unit < 2; unit++) {
+ FAKE_STATUS(unit, STATUS_RGATE, '-');
+ FAKE_STATUS(unit, STATUS_WGATE, '-');
+ FAKE_STATUS(unit, STATUS_KWRC, '-');
+ FAKE_STATUS(unit, STATUS_CYL, 0);
+ FAKE_STATUS(unit, STATUS_HEAD, 0);
+ FAKE_STATUS(unit, STATUS_SECT, 0);
+ FAKE_STATUS(unit, STATUS_PAGE, 0);
+ }
+#endif
+}
diff --git a/src/emu/cpu/alto2/a2disk.h b/src/emu/cpu/alto2/a2disk.h
new file mode 100644
index 00000000000..c1d44bd7a03
--- /dev/null
+++ b/src/emu/cpu/alto2/a2disk.h
@@ -0,0 +1,109 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII disk controller block
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2DISK_H_
+#define _A2DISK_H_
+
+diablo_hd_device* m_drive[2]; //!< two diablo_hd_device drives
+
+//! disk controller context
+struct {
+ UINT8 drive; //!< selected drive from KADDR[14] (written to data out with SENDADR)
+ UINT16 kaddr; //!< A[0-15] disk hardware address (sector, cylinder, head, drive, restore)
+ UINT16 kadr; //!< C[0-15] with read/write/check modes for header, label and data
+ UINT16 kstat; //!< S[0-15] disk status
+ UINT16 kcom; //!< disk command (5 bits kcom[1-5])
+ UINT8 krecno; //!< record number (2 bits indexing header, label, data, -/-)
+ UINT8 egate; //!< current erase gate signal to the DIABLO hd
+ UINT8 wrgate; //!< current write gate signal to the DIABLO hd
+ UINT8 rdgate; //!< current read gate signal to the DIABLO hd
+ UINT32 shiftin; //!< input shift register
+ UINT32 shiftout; //!< output shift register
+ UINT32 datain; //!< disk data in latch
+ UINT32 dataout; //!< disk data out latch
+ UINT8 krwc; //!< read/write/check for current record
+ UINT8 kfer; //!< disk fatal error signal state
+ UINT8 wdtskena; //!< disk word task enable (active low)
+ UINT8 wddone; //!< previous state of WDDONE
+ UINT8 wdinit0; //!< disk word task init at the early microcycle
+ UINT8 wdinit; //!< disk word task init at the late microcycle
+ UINT8 strobe; //!< strobe (still) active
+ emu_timer* strobon_timer; //!< set strobe on timer
+ UINT8 bitclk; //!< current bitclk state (either crystal clock, or rdclk from the drive)
+#if USE_BITCLK_TIMER
+ emu_timer* bitclk_timer; //!< bit clock timer
+#else
+ int bitclk_time[2]; //!< per drive time in clocks per bit
+#endif
+ UINT8 datin; //!< current datin from the drive
+ UINT8 bitcount; //!< bit counter
+ UINT8 carry; //!< carry output of the bitcounter
+ UINT8 seclate; //!< sector late (monoflop output)
+ emu_timer* seclate_timer; //!< sector late timer
+ UINT8 seekok; //!< seekok state (SKINC' & LAI' & ff_44a.Q')
+ UINT8 ok_to_run; //!< ok to run signal (set to 1 some time after reset)
+ emu_timer* ok_to_run_timer; //!< ok to run timer
+ UINT8 ready_mf31a; //!< ready monoflop 31a
+ emu_timer* ready_timer; //!< ready timer
+ UINT8 seclate_mf31b; //!< seclate monoflop 31b
+ jkff_t ff_21a; //!< JK flip-flop 21a (sector task)
+ jkff_t ff_21a_old; //!< -"- previous state
+ jkff_t ff_21b; //!< JK flip-flop 21b (sector task)
+ jkff_t ff_22a; //!< JK flip-flop 22a (sector task)
+ jkff_t ff_22b; //!< JK flip-flop 22b (sector task)
+ jkff_t ff_43b; //!< JK flip-flop 43b (word task)
+ jkff_t ff_53a; //!< JK flip-flop 53a (word task)
+ jkff_t ff_43a; //!< JK flip-flop 43a (word task)
+ jkff_t ff_53b; //!< brief JK flip-flop 53b (word task)
+ jkff_t ff_44a; //!< JK flip-flop 44a (LAI' clocked)
+ jkff_t ff_44b; //!< JK flip-flop 44b (CKSUM)
+ jkff_t ff_45a; //!< JK flip-flop 45a (ready latch)
+ jkff_t ff_45b; //!< JK flip-flop 45b (seqerr latch)
+} m_dsk;
+
+jkff_t m_sysclka0[4]; //!< simulate previous sysclka
+jkff_t m_sysclka1[4]; //!< simulate current sysclka
+jkff_t m_sysclkb0[4]; //!< simulate previous sysclkb
+jkff_t m_sysclkb1[4]; //!< simulate current sysclkb
+
+void kwd_timing(int bitclk, int datin, int block); //!< disk word timing
+TIMER_CALLBACK_MEMBER( disk_seclate ); //!< timer callback to take away the SECLATE pulse (monoflop)
+TIMER_CALLBACK_MEMBER( disk_ok_to_run ); //!< timer callback to take away the OK TO RUN pulse (reset)
+TIMER_CALLBACK_MEMBER( disk_strobon ); //!< timer callback to pulse the STROBE' signal to the drive
+TIMER_CALLBACK_MEMBER( disk_ready_mf31a ); //!< timer callback to change the READY monoflop 31a
+#if USE_BITCLK_TIMER
+TIMER_CALLBACK_MEMBER( disk_bitclk ); //!< callback to update the disk controller with a new bitclk
+#else
+void disk_bitclk(void *ptr, int arg); //!< function to update the disk controller with a new bitclk
+#endif
+void disk_block(int task); //!< called if one of the disk tasks (task_kwd or task_ksec) blocks
+void bs_early_read_kstat(); //!< bus source: bus driven by disk status register KSTAT
+void bs_early_read_kdata(); //!< bus source: bus driven by disk data register KDATA input
+void f1_late_strobe(); //!< F1 func: initiates a disk seek
+void f1_late_load_kstat(); //!< F1 func: load disk status register
+void f1_late_load_kdata(); //!< F1 func: load data out register, or the disk address register
+void f1_late_increcno(); //!< F1 func: advances shift registers holding KADR
+void f1_late_clrstat(); //!< F1 func: reset all error latches
+void f1_late_load_kcom(); //!< F1 func: load the KCOM register from bus
+void f1_late_load_kadr(); //!< F1 func: load the KADR register from bus
+void f2_late_init(); //!< F2 func: branch on disk word task active and init
+void f2_late_rwc(); //!< F2 func: branch on read/write/check state of the current record
+void f2_late_recno(); //!< F2 func: branch on the current record number by a lookup table
+void f2_late_xfrdat(); //!< F2 func: branch on the data transfer state
+void f2_late_swrnrdy(); //!< F2 func: branch on the disk ready signal
+void f2_late_nfer(); //!< f2_nfer late: branch on the disk fatal error condition
+void f2_late_strobon(); //!< f2_strobon late: branch on the seek busy status
+void init_disk(); //!< initialize the disk controller
+void exit_disk(); //!< deinitialize the disk controller
+void reset_disk(); //!< reset the disk controller
+#endif // _A2DISK_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2disp.c b/src/emu/cpu/alto2/a2disp.c
new file mode 100644
index 00000000000..60e3a4be6e8
--- /dev/null
+++ b/src/emu/cpu/alto2/a2disp.c
@@ -0,0 +1,876 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display interface
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+#include "a2roms.h"
+
+/**
+ * @brief PROM a38 contains the STOPWAKE' and MBEMBPTY' signals for the FIFO
+ * <PRE>
+ * The inputs to a38 are the UNLOAD counter RA[0-3] and the DDR<- counter
+ * WA[0-3], and the designer decided to reverse the address lines :-)
+ *
+ * a38 counter
+ * -------------
+ * A0 RA[0]
+ * A1 RA[1]
+ * A2 RA[2]
+ * A3 RA[3]
+ * A4 WA[0]
+ * A5 WA[1]
+ * A6 WA[2]
+ * A7 WA[3]
+ *
+ * Only two bits of a38 are used:
+ * O1 (002) = STOPWAKE'
+ * O3 (010) = MBEMPTY'
+ * </PRE>
+ */
+
+//! P3601 256x4 BPROM; display FIFO control: STOPWAKE, MBEMPTY
+static const prom_load_t pl_displ_a38 =
+{
+ "displ.a38",
+ 0,
+ "fd30beb7",
+ "65e4a19ba4ff748d525122128c514abedd55d866",
+ /* size */ 0400,
+ /* amap */ AMAP_REVERSE_0_7, // reverse address lines A0-A7
+ /* axor */ 0,
+ /* dxor */ 0,
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+//! PROM a38 bit O1 is STOPWAKE' (stop DWT if bit is zero)
+#define FIFO_STOPWAKE(a38) (0 == (a38 & disp_a38_STOPWAKE) ? true : false)
+
+//! PROM a38 bit O3 is MBEMPTY' (FIFO is empty if bit is zero)
+#define FIFO_MBEMPTY(a38) (0 == (a38 & disp_a38_MBEMPTY) ? true : false)
+
+/**
+ * @brief emulation of PROM a63 in the display schematics page 8
+ * <PRE>
+ * The PROM's address lines are driven by a clock CLK, which is
+ * pixel clock / 24, and an inverted half-scanline signal H[1]'.
+ *
+ * It is 32x8 bits and its output bits (B) are connected to the
+ * signals, as well as its own address lines (A) through a latch
+ * of the type SN74774 like this:
+ *
+ * PROM 174 A others
+ * ------------------------
+ * B0 D5 - HBLANK
+ * B1 D0 - HSYNC
+ * B2 D4 A0 -
+ * B3 D1 A1 -
+ * B4 D3 A2 -
+ * B5 D2 A3 -
+ * B6 - - SCANEND
+ * B7 - - HLCGATE
+ * ------------------------
+ * H[1]' - A4 -
+ *
+ * The display_state_machine() is called at a rate of pixelclock/24.
+ *
+ * Decoded states of this PROM:
+ *
+ * STATE PROM binary HBLANK HSYNC NEXT SCANEND HLCGATE
+ * ----------------------------------------------------------
+ * 000 0007 00000111 1 1 001 0 0
+ * 001 0013 00001011 1 1 002 0 0
+ * 002 0015 00001101 1 0 003 0 0
+ * 003 0021 00010001 1 0 004 0 0
+ * 004 0024 00010100 0 0 005 0 0
+ * 005 0030 00011000 0 0 006 0 0
+ * 006 0034 00011100 0 0 007 0 0
+ * 007 0040 00100000 0 0 010 0 0
+ * 010 0044 00100100 0 0 011 0 0
+ * 011 0050 00101000 0 0 012 0 0
+ * 012 0054 00101100 0 0 013 0 0
+ * 013 0060 00110000 0 0 014 0 0
+ * 014 0064 00110100 0 0 015 0 0
+ * 015 0070 00111000 0 0 016 0 0
+ * 016 0074 00111100 0 0 017 0 0
+ * 017 0200 10000000 0 0 000 0 1
+ * 020 0004 00000100 0 0 001 0 0
+ * 021 0010 00001000 0 0 002 0 0
+ * 022 0014 00001100 0 0 003 0 0
+ * 023 0020 00010000 0 0 004 0 0
+ * 024 0024 00010100 0 0 005 0 0
+ * 025 0030 00011000 0 0 006 0 0
+ * 026 0034 00011100 0 0 007 0 0
+ * 027 0040 00100000 0 0 010 0 0
+ * 030 0044 00100100 0 0 011 0 0
+ * 031 0050 00101000 0 0 012 0 0
+ * 032 0054 00101100 0 0 013 0 0
+ * 033 0060 00110000 0 0 014 0 0
+ * 034 0064 00110100 0 0 015 0 0
+ * 035 0070 00111000 0 0 016 0 0
+ * 036 0175 01111101 1 0 017 1 0
+ * 037 0203 10000011 1 1 000 0 1
+ * </PRE>
+ */
+
+//! 82S23 32x8 BPROM; display HBLANK, HSYNC, SCANEND, HLCGATE ...
+static const prom_load_t pl_displ_a63 =
+{
+ "displ.a63",
+ 0,
+ "82a20d60",
+ "39d90703568be5419ada950e112d99227873fdea",
+ /* size */ 0040,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 0,
+ /* width */ 8,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+//!< test the HBLANK (horizontal blanking) signal in PROM a63 being high
+#define A63_HBLANK(a) ((a & disp_a63_HBLANK) ? true : false)
+
+//!< test the HSYNC (horizontal synchonisation) signal in PROM a63 being high
+#define A63_HSYNC(a) ((a & disp_a63_HSYNC) ? true : false)
+
+//!< test the SCANEND (scanline end) signal in PROM a63 being high
+#define A63_SCANEND(a) ((a & disp_a63_SCANEND) ? true : false)
+
+//!< test the HLCGATE (horz. line counter gate) signal in PROM a63 being high
+#define A63_HLCGATE(a) ((a & disp_a63_HLCGATE) ? true : false)
+
+/**
+ * @brief PROM a66 is a 256x4 bit (type 3601)
+ * <PRE>
+ * Address lines are driven by H[1] to H[128] of the the horz. line counters.
+ * PROM is enabled when H[256] and H[512] are both 0.
+ *
+ * Q1 is VSYNC for the odd field (with H1024=0)
+ * Q2 is VSYNC for the even field (with H1024=1)
+ * Q3 is VBLANK for the odd field (with H1024=0)
+ * Q4 is VBLANK for the even field (with H1024=1)
+ * </PRE>
+ */
+
+//! P3601 256x4 BPROM; display VSYNC and VBLANK
+static const prom_load_t pl_displ_a66 =
+{
+ "displ.a66",
+ 0,
+ "9f91aad9",
+ "69b1d4c71f4e18103112e8601850c2654e9265cf",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 0,
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+//! test the VSYNC (vertical synchronisation) signal in PROM a66 being high
+#define A66_VSYNC(a) (a & (HLC1024 ? disp_a66_VSYNC_ODD : disp_a66_VSYNC_EVEN) ? false : true)
+
+//! test the VBLANK (vertical blanking) signal in PROM a66 being high
+#define A66_VBLANK(a) (a & (HLC1024 ? disp_a66_VBLANK_ODD : disp_a66_VBLANK_EVEN) ? false : true)
+
+/**
+ * @brief double the bits for a byte (left and right of display word) to a word
+ */
+static const UINT16 double_bits[256] = {
+ 0x0000,0x0003,0x000c,0x000f,0x0030,0x0033,0x003c,0x003f,
+ 0x00c0,0x00c3,0x00cc,0x00cf,0x00f0,0x00f3,0x00fc,0x00ff,
+ 0x0300,0x0303,0x030c,0x030f,0x0330,0x0333,0x033c,0x033f,
+ 0x03c0,0x03c3,0x03cc,0x03cf,0x03f0,0x03f3,0x03fc,0x03ff,
+ 0x0c00,0x0c03,0x0c0c,0x0c0f,0x0c30,0x0c33,0x0c3c,0x0c3f,
+ 0x0cc0,0x0cc3,0x0ccc,0x0ccf,0x0cf0,0x0cf3,0x0cfc,0x0cff,
+ 0x0f00,0x0f03,0x0f0c,0x0f0f,0x0f30,0x0f33,0x0f3c,0x0f3f,
+ 0x0fc0,0x0fc3,0x0fcc,0x0fcf,0x0ff0,0x0ff3,0x0ffc,0x0fff,
+ 0x3000,0x3003,0x300c,0x300f,0x3030,0x3033,0x303c,0x303f,
+ 0x30c0,0x30c3,0x30cc,0x30cf,0x30f0,0x30f3,0x30fc,0x30ff,
+ 0x3300,0x3303,0x330c,0x330f,0x3330,0x3333,0x333c,0x333f,
+ 0x33c0,0x33c3,0x33cc,0x33cf,0x33f0,0x33f3,0x33fc,0x33ff,
+ 0x3c00,0x3c03,0x3c0c,0x3c0f,0x3c30,0x3c33,0x3c3c,0x3c3f,
+ 0x3cc0,0x3cc3,0x3ccc,0x3ccf,0x3cf0,0x3cf3,0x3cfc,0x3cff,
+ 0x3f00,0x3f03,0x3f0c,0x3f0f,0x3f30,0x3f33,0x3f3c,0x3f3f,
+ 0x3fc0,0x3fc3,0x3fcc,0x3fcf,0x3ff0,0x3ff3,0x3ffc,0x3fff,
+ 0xc000,0xc003,0xc00c,0xc00f,0xc030,0xc033,0xc03c,0xc03f,
+ 0xc0c0,0xc0c3,0xc0cc,0xc0cf,0xc0f0,0xc0f3,0xc0fc,0xc0ff,
+ 0xc300,0xc303,0xc30c,0xc30f,0xc330,0xc333,0xc33c,0xc33f,
+ 0xc3c0,0xc3c3,0xc3cc,0xc3cf,0xc3f0,0xc3f3,0xc3fc,0xc3ff,
+ 0xcc00,0xcc03,0xcc0c,0xcc0f,0xcc30,0xcc33,0xcc3c,0xcc3f,
+ 0xccc0,0xccc3,0xcccc,0xcccf,0xccf0,0xccf3,0xccfc,0xccff,
+ 0xcf00,0xcf03,0xcf0c,0xcf0f,0xcf30,0xcf33,0xcf3c,0xcf3f,
+ 0xcfc0,0xcfc3,0xcfcc,0xcfcf,0xcff0,0xcff3,0xcffc,0xcfff,
+ 0xf000,0xf003,0xf00c,0xf00f,0xf030,0xf033,0xf03c,0xf03f,
+ 0xf0c0,0xf0c3,0xf0cc,0xf0cf,0xf0f0,0xf0f3,0xf0fc,0xf0ff,
+ 0xf300,0xf303,0xf30c,0xf30f,0xf330,0xf333,0xf33c,0xf33f,
+ 0xf3c0,0xf3c3,0xf3cc,0xf3cf,0xf3f0,0xf3f3,0xf3fc,0xf3ff,
+ 0xfc00,0xfc03,0xfc0c,0xfc0f,0xfc30,0xfc33,0xfc3c,0xfc3f,
+ 0xfcc0,0xfcc3,0xfccc,0xfccf,0xfcf0,0xfcf3,0xfcfc,0xfcff,
+ 0xff00,0xff03,0xff0c,0xff0f,0xff30,0xff33,0xff3c,0xff3f,
+ 0xffc0,0xffc3,0xffcc,0xffcf,0xfff0,0xfff3,0xfffc,0xffff
+};
+
+#define HLC1 ((m_dsp.hlc >> 0) & 1) //!< horizontal line counter bit 0 (mid of the scanline)
+#define HLC2 ((m_dsp.hlc >> 1) & 1) //!< horizontal line counter bit 1
+#define HLC4 ((m_dsp.hlc >> 2) & 1) //!< horizontal line counter bit 2
+#define HLC8 ((m_dsp.hlc >> 3) & 1) //!< horizontal line counter bit 3
+#define HLC16 ((m_dsp.hlc >> 4) & 1) //!< horizontal line counter bit 4
+#define HLC32 ((m_dsp.hlc >> 5) & 1) //!< horizontal line counter bit 5
+#define HLC64 ((m_dsp.hlc >> 6) & 1) //!< horizontal line counter bit 6
+#define HLC128 ((m_dsp.hlc >> 7) & 1) //!< horizontal line counter bit 7
+#define HLC256 ((m_dsp.hlc >> 8) & 1) //!< horizontal line counter bit 8
+#define HLC512 ((m_dsp.hlc >> 9) & 1) //!< horizontal line counter bit 9
+#define HLC1024 ((m_dsp.hlc >> 10) & 1) //!< horizontal line counter bit 10 (odd/even field)
+
+#define GET_SETMODE_SPEEDY(mode) X_RDBITS(mode,16,0,0) //!< get the pixel clock speed from a SETMODE<- bus value
+#define GET_SETMODE_INVERSE(mode) X_RDBITS(mode,16,1,1) //!< get the inverse video flag from a SETMODE<- bus value
+
+//!< helper to extract A3-A0 from a PROM a63 value
+#define A63_NEXT(n) ((n >> 2) & 017)
+
+//! update the internal bitmap to a byte array
+void alto2_cpu_device::update_bitmap_word(UINT16* bitmap, int x, int y, UINT16 word)
+{
+ // mixing with the cursor
+ if (x == m_dsp.curxpos + 0)
+ word ^= m_dsp.cursor0;
+ if (x == m_dsp.curxpos + 1)
+ word ^= m_dsp.cursor1;
+ // no change?
+ if (word == bitmap[x])
+ return;
+ bitmap[x] = word;
+ UINT8* pix = m_dsp.scanline[y] + x * 16;
+ *pix++ = (word >> 15) & 1;
+ *pix++ = (word >> 14) & 1;
+ *pix++ = (word >> 13) & 1;
+ *pix++ = (word >> 12) & 1;
+ *pix++ = (word >> 11) & 1;
+ *pix++ = (word >> 10) & 1;
+ *pix++ = (word >> 9) & 1;
+ *pix++ = (word >> 8) & 1;
+ *pix++ = (word >> 7) & 1;
+ *pix++ = (word >> 6) & 1;
+ *pix++ = (word >> 5) & 1;
+ *pix++ = (word >> 4) & 1;
+ *pix++ = (word >> 3) & 1;
+ *pix++ = (word >> 2) & 1;
+ *pix++ = (word >> 1) & 1;
+ *pix++ = (word >> 0) & 1;
+}
+
+/**
+ * @brief unload the next word from the display FIFO and shift it to the screen
+ */
+void alto2_cpu_device::unload_word()
+{
+ int x = m_unload_word;
+ int y = ((m_dsp.hlc - m_dsp.vblank) & ~02001) ^ HLC1024;
+
+ if (y < 0 || y >= ALTO2_DISPLAY_HEIGHT || x >= ALTO2_DISPLAY_VISIBLE_WORDS)
+ {
+ m_unload_time = -1;
+ return;
+ }
+ UINT16* bitmap = m_dsp.raw_bitmap + y * ALTO2_DISPLAY_SCANLINE_WORDS;
+ UINT16 word = m_dsp.inverse;
+ UINT8 a38 = m_disp_a38[m_dsp.ra * 16 + m_dsp.wa];
+ if (FIFO_MBEMPTY(a38))
+ {
+ LOG((LOG_DISPL,1, " DSP FIFO underrun y:%d x:%d\n", y, x));
+ }
+ else
+ {
+ word ^= m_dsp.fifo[m_dsp.ra];
+ m_dsp.ra = (m_dsp.ra + 1) % ALTO2_DISPLAY_FIFO;
+ LOG((LOG_DISPL,3, " DSP pull %04x from FIFO[%02o] y:%d x:%d\n",
+ word, (m_dsp.ra - 1) & (ALTO2_DISPLAY_FIFO - 1), y, x));
+ }
+
+ if (m_dsp.halfclock)
+ {
+ UINT16 word1 = double_bits[word / 256];
+ UINT16 word2 = double_bits[word % 256];
+ update_bitmap_word(bitmap, x, y, word1);
+ x++;
+ if (x < ALTO2_DISPLAY_VISIBLE_WORDS)
+ {
+ update_bitmap_word(bitmap, x, y, word2);
+ x++;
+ }
+ m_unload_time += ALTO2_DISPLAY_BITTIME(32);
+ }
+ else
+ {
+ update_bitmap_word(bitmap, x, y, word);
+ x++;
+ m_unload_time += ALTO2_DISPLAY_BITTIME(16);
+ }
+ if (x < ALTO2_DISPLAY_VISIBLE_WORDS)
+ m_unload_word = x;
+ else
+ m_unload_time = -1;
+}
+
+
+/**
+ * @brief function called by the CPU to enter the next display state
+ *
+ * There are 32 states per scanline and 875 scanlines per frame.
+ */
+void alto2_cpu_device::display_state_machine()
+{
+ LOG((LOG_DISPL,5,"DSP%03o:", m_dsp.state));
+ if (020 == m_dsp.state)
+ {
+ LOG((LOG_DISPL,2," HLC=%d", m_dsp.hlc));
+ }
+
+ UINT8 a63 = m_disp_a63[m_dsp.state];
+ if (A63_HLCGATE(a63))
+ {
+ // count horizontal line counters and wrap
+ m_dsp.hlc += 1;
+ if (m_dsp.hlc > ALTO2_DISPLAY_HLC_END)
+ m_dsp.hlc = ALTO2_DISPLAY_HLC_START;
+ // wake up the memory refresh task _twice_ on each scanline
+ m_task_wakeup |= 1 << task_mrt;
+ }
+ // PROM a66 is disabled, if any of HLC256 or HLC512 are high
+ UINT8 a66 = (HLC256 || HLC512) ? 017 : m_disp_a66[m_dsp.hlc & 0377];
+
+ // next address from PROM a63, use A4 from HLC1
+ UINT8 next = ((HLC1 ^ 1) << 4) | A63_NEXT(a63);
+
+ if (A66_VBLANK(a66))
+ {
+ // Rising edge of VBLANK: remember HLC[1-10] where the VBLANK starts
+ m_dsp.vblank = m_dsp.hlc & ~02000;
+
+ LOG((LOG_DISPL,1, " VBLANK"));
+
+ // VSYNC is always within VBLANK, thus we handle it only here
+ if (A66_VSYNC(a66))
+ {
+ if (!A66_VSYNC(m_dsp.a66))
+ {
+ LOG((LOG_DISPL,1, " VSYNC/ (wake DVT)"));
+ /*
+ * The display vertical task DVT is woken once per field
+ * at the beginning of vertical retrace.
+ */
+ m_task_wakeup |= 1 << task_dvt;
+ // TODO: upade odd or even field of the internal bitmap now?
+ }
+ }
+ }
+ else
+ {
+ // Falling edge of VBLANK?
+ if (A66_VBLANK(m_dsp.a66))
+ {
+ /*
+ * VBLANKPULSE:
+ * The display horizontal task DHT is woken once at the
+ * beginning of each field, and thereafter whenever the
+ * display word task blocks.
+ *
+ * The DHT can block itself, in which case neither it nor
+ * the word task can be woken until the start of the
+ * next field.
+ */
+ LOG((LOG_DISPL,1, " VBLANKPULSE (wake DHT)"));
+ m_dsp.dht_blocks = false;
+ m_dsp.dwt_blocks = false;
+ m_task_wakeup |= 1 << task_dht;
+ /*
+ * VBLANKPULSE also resets the cursor task block flip flop,
+ * which is built from two NAND gates a40c and a40d (74H01).
+ */
+ m_dsp.curt_blocks = false;
+ }
+ if (!A63_HBLANK(a63) && A63_HBLANK(m_dsp.a63))
+ {
+ // Falling edge of a63 HBLANK starts unloading of FIFO words
+ LOG((LOG_DISPL,1, " HBLANK\\ UNLOAD"));
+ m_unload_time = ALTO2_DISPLAY_BITTIME(m_dsp.halfclock ? 32 : 16);
+ m_unload_word = 0;
+ }
+ }
+
+ /*
+ * The wakeup request for the display word task (DWT) is controlled by
+ * the state of the 16 word FIFO. If DWT has not executed a BLOCK,
+ * if DHT is not blocked, and if the buffer is not full, DWT wakeups
+ * are generated.
+ */
+ UINT8 a38 = m_disp_a38[m_dsp.ra * 16 + m_dsp.wa];
+ if (!m_dsp.dwt_blocks && !m_dsp.dht_blocks && !FIFO_STOPWAKE(a38))
+ {
+ m_task_wakeup |= 1 << task_dwt;
+ LOG((LOG_DISPL,1, " (wake DWT)"));
+ }
+
+ // Stop waking up the DWT when SCANEND is active
+ if (A63_SCANEND(a63))
+ {
+ m_task_wakeup &= ~(1 << task_dwt);
+ LOG((LOG_DISPL,1, " SCANEND"));
+ }
+
+ LOG((LOG_DISPL,1, "%s", A63_HBLANK(a63) ? " HBLANK": ""));
+
+ if (A63_HSYNC(a63))
+ {
+ // Active HSYNC
+ if (!A63_HSYNC(m_dsp.a63))
+ {
+ // Rising edge of HSYNC => CLRBUF
+ LOG((LOG_DISPL,1, " HSYNC/ (CLRBUF)"));
+ /*
+ * The hardware sets the buffer empty and clears the DWT block
+ * flip-flop at the beginning of horizontal retrace for
+ * every scanline.
+ */
+ m_dsp.wa = 0;
+ m_dsp.ra = 0;
+ m_dsp.dwt_blocks = false;
+ // now take the new values from the last SETMODE<-
+ m_dsp.inverse = GET_SETMODE_INVERSE(m_dsp.setmode) ? 0xffff : 0x0000;
+ m_dsp.halfclock = GET_SETMODE_SPEEDY(m_dsp.setmode) ? true : false;
+ // stop the CPU execution loop from calling unload_word()
+ m_unload_time = -1;
+ }
+ else
+ {
+ LOG((LOG_DISPL,1, " HSYNC"));
+ }
+ }
+ else
+ // Falling edge of HSYNC?
+ if (A63_HSYNC(m_dsp.a63))
+ {
+ /*
+ * CLRBUF' also resets the 2nd cursor task block flip flop,
+ * which is built from two NAND gates a30c and a30d (74H00).
+ * If both flip flops are reset, the NOR gate a20d (74S02)
+ * decodes this as WAKECURT signal.
+ */
+ m_dsp.curt_wakeup = true;
+ if (!m_dsp.curt_blocks)
+ m_task_wakeup |= 1 << task_curt;
+ }
+
+ LOG((LOG_DISPL,1, " NEXT:%03o\n", next));
+
+ m_dsp.a63 = a63;
+ m_dsp.a66 = a66;
+ m_dsp.state = next;
+ m_dsp_time += ALTO2_DISPLAY_BITTIME(32);
+}
+
+/**
+ * @brief branch on evenfield
+ *
+ * NEXT(09) = even field ? 1 : 0
+ */
+void alto2_cpu_device::f2_late_evenfield()
+{
+ UINT16 r = HLC1024 ^ 1;
+ LOG((LOG_DISPL,2," EVENFIELD branch (%#o | %#o)\n", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief initialize the display context to useful values
+ *
+ * Zap the display context to all 0s.
+ * Allocate a bitmap array to save blitting to the screen when
+ * there is no change in the data words.
+ */
+void alto2_cpu_device::init_disp()
+{
+ memset(&m_dsp, 0, sizeof(m_dsp));
+ save_item(NAME(m_dsp.state));
+ save_item(NAME(m_dsp.hlc));
+ save_item(NAME(m_dsp.setmode));
+ save_item(NAME(m_dsp.inverse));
+ save_item(NAME(m_dsp.halfclock));
+ save_item(NAME(m_dsp.fifo));
+ save_item(NAME(m_dsp.wa));
+ save_item(NAME(m_dsp.ra));
+ save_item(NAME(m_dsp.a63));
+ save_item(NAME(m_dsp.a66));
+ save_item(NAME(m_dsp.dht_blocks));
+ save_item(NAME(m_dsp.dwt_blocks));
+ save_item(NAME(m_dsp.curt_blocks));
+ save_item(NAME(m_dsp.curt_wakeup));
+ save_item(NAME(m_dsp.vblank));
+ save_item(NAME(m_dsp.xpreg));
+ save_item(NAME(m_dsp.csr));
+ save_item(NAME(m_dsp.curxpos));
+ save_item(NAME(m_dsp.cursor0));
+ save_item(NAME(m_dsp.cursor1));
+
+ m_disp_a38 = prom_load(machine(), &pl_displ_a38, memregion("displ_a38")->base());
+ m_disp_a63 = prom_load(machine(), &pl_displ_a63, memregion("displ_a63")->base());
+ m_disp_a66 = prom_load(machine(), &pl_displ_a66, memregion("displ_a66")->base());
+
+ m_dsp.hlc = ALTO2_DISPLAY_HLC_START;
+
+ m_dsp.raw_bitmap = auto_alloc_array(machine(), UINT16, ALTO2_DISPLAY_HEIGHT * ALTO2_DISPLAY_SCANLINE_WORDS);
+ m_dsp.scanline = auto_alloc_array(machine(), UINT8*, ALTO2_DISPLAY_HEIGHT + ALTO2_FAKE_STATUS_H);
+ for (int y = 0; y < ALTO2_DISPLAY_HEIGHT + ALTO2_FAKE_STATUS_H; y++)
+ m_dsp.scanline[y] = auto_alloc_array(machine(), UINT8, ALTO2_DISPLAY_TOTAL_WIDTH);
+
+ m_dsp.bitmap = auto_bitmap_ind16_alloc(machine(), ALTO2_DISPLAY_WIDTH, ALTO2_DISPLAY_HEIGHT + ALTO2_FAKE_STATUS_H);
+ m_dsp.state = 0;
+}
+
+void alto2_cpu_device::exit_disp()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_disp()
+{
+ m_dsp.state = 0;
+ m_dsp.hlc = ALTO2_DISPLAY_HLC_START;
+ m_dsp.a63 = 0;
+ m_dsp.a66 = 0;
+ m_dsp.setmode = 0;
+ m_dsp.inverse = 0;
+ m_dsp.halfclock = false;
+ m_dsp.wa = 0;
+ m_dsp.ra = 0;
+ m_dsp.dht_blocks = false;
+ m_dsp.dwt_blocks = false;
+ m_dsp.curt_blocks = false;
+ m_dsp.curt_wakeup = false;
+ m_dsp.vblank = 0;
+ m_dsp.xpreg = 0;
+ m_dsp.csr = 0;
+ m_dsp.curxpos = 0;
+ m_dsp.cursor0 = 0;
+ m_dsp.cursor1 = 0;
+ memset(m_dsp.raw_bitmap, 0, sizeof(UINT16) * ALTO2_DISPLAY_HEIGHT * ALTO2_DISPLAY_SCANLINE_WORDS);
+ for (int y = 0; y < ALTO2_DISPLAY_HEIGHT; y++)
+ memset(m_dsp.scanline[y], 0, sizeof(UINT8) * ALTO2_DISPLAY_TOTAL_WIDTH);
+ m_dsp.odd_frame = false;
+
+ for (int y = ALTO2_DISPLAY_HEIGHT; y < ALTO2_DISPLAY_HEIGHT + ALTO2_FAKE_STATUS_H; y++)
+ memset(m_dsp.scanline[y], 1, sizeof(UINT8) * ALTO2_DISPLAY_TOTAL_WIDTH);
+ fake_status_printf(1, "* Fake Status *");
+}
+
+/* Video update */
+UINT32 alto2_cpu_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ pen_t palette_bw[2];
+ palette_bw[0] = screen.palette()->white_pen();
+ palette_bw[1] = screen.palette()->black_pen();
+ // copy even or odd field
+ for (int y = m_dsp.odd_frame ? 0 : 1; y < ALTO2_DISPLAY_HEIGHT; y += 2)
+ draw_scanline8(*m_dsp.bitmap, 0, y, ALTO2_DISPLAY_WIDTH, m_dsp.scanline[y], palette_bw);
+ // copy fake status scanlines
+ for (int y = ALTO2_DISPLAY_HEIGHT; y < ALTO2_DISPLAY_HEIGHT + ALTO2_FAKE_STATUS_H; y++)
+ draw_scanline8(*m_dsp.bitmap, 0, y, ALTO2_DISPLAY_WIDTH, m_dsp.scanline[y], palette_bw);
+ // copy bitmap
+ copybitmap(bitmap, *m_dsp.bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+void alto2_cpu_device::screen_eof(screen_device &screen, bool state)
+{
+ if (state)
+ m_dsp.odd_frame = !m_dsp.odd_frame;
+}
+
+/*****************************************************************************
+ *
+ * FAKE STATUS LINE
+ *
+ *****************************************************************************/
+
+typedef struct {
+ UINT8 code;
+ UINT8 bits[10];
+} bdf_6x10_t;
+
+/**
+ * STARTFONT 2.1
+ * COMMENT "$ucs-fonts: 6x10.bdf,v 1.34 2002-11-10 19:12:30+00 mgk25 Rel $"
+ * COMMENT "Send bug reports to Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/>"
+ * FONT -Misc-Fixed-Medium-R-Normal--10-100-75-75-C-60-ISO10646-1
+ * SIZE 10 75 75
+ * FONTBOUNDINGBOX 6 10 0 -2
+ * STARTPROPERTIES 22
+ * FONTNAME_REGISTRY ""
+ * FOUNDRY "Misc"
+ * FAMILY_NAME "Fixed"
+ * WEIGHT_NAME "Medium"
+ * SLANT "R"
+ * SETWIDTH_NAME "Normal"
+ * ADD_STYLE_NAME ""
+ * PIXEL_SIZE 10
+ * POINT_SIZE 100
+ * RESOLUTION_X 75
+ * RESOLUTION_Y 75
+ * SPACING "C"
+ * AVERAGE_WIDTH 60
+ * CHARSET_REGISTRY "ISO10646"
+ * CHARSET_ENCODING "1"
+ * FONT_ASCENT 8
+ * FONT_DESCENT 2
+ * DEFAULT_CHAR 0
+ * COPYRIGHT "Public domain terminal emulator font. Share and enjoy."
+ * _XMBDFED_INFO "Edited with xmbdfed 4.5."
+ * CAP_HEIGHT 7
+ * X_HEIGHT 5
+ * ENDPROPERTIES
+ * CHARS 1597
+ */
+static const bdf_6x10_t bdf_6x10[] = {
+/* space */ { 32, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* exclam */ { 33, {0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00 }},
+/* quotedbl */ { 34, {0x00, 0x50, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* numbersign */ { 35, {0x00, 0x50, 0x50, 0xF8, 0x50, 0xF8, 0x50, 0x50, 0x00, 0x00 }},
+/* dollar */ { 36, {0x00, 0x20, 0x70, 0xA0, 0x70, 0x28, 0x70, 0x20, 0x00, 0x00 }},
+/* percent */ { 37, {0x00, 0x48, 0xA8, 0x50, 0x20, 0x50, 0xA8, 0x90, 0x00, 0x00 }},
+/* ampersand */ { 38, {0x00, 0x40, 0xA0, 0xA0, 0x40, 0xA8, 0x90, 0x68, 0x00, 0x00 }},
+/* quotesingle */ { 39, {0x00, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* parenleft */ { 40, {0x00, 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00, 0x00 }},
+/* parenright */ { 41, {0x00, 0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40, 0x00, 0x00 }},
+/* asterisk */ { 42, {0x00, 0x00, 0x88, 0x50, 0xF8, 0x50, 0x88, 0x00, 0x00, 0x00 }},
+/* plus */ { 43, {0x00, 0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00, 0x00, 0x00 }},
+/* comma */ { 44, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x20, 0x40, 0x00 }},
+/* hyphen */ { 45, {0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* period */ { 46, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x70, 0x20, 0x00 }},
+/* slash */ { 47, {0x00, 0x08, 0x08, 0x10, 0x20, 0x40, 0x80, 0x80, 0x00, 0x00 }},
+/* zero */ { 48, {0x00, 0x20, 0x50, 0x88, 0x88, 0x88, 0x50, 0x20, 0x00, 0x00 }},
+/* one */ { 49, {0x00, 0x20, 0x60, 0xA0, 0x20, 0x20, 0x20, 0xF8, 0x00, 0x00 }},
+/* two */ { 50, {0x00, 0x70, 0x88, 0x08, 0x30, 0x40, 0x80, 0xF8, 0x00, 0x00 }},
+/* three */ { 51, {0x00, 0xF8, 0x08, 0x10, 0x30, 0x08, 0x88, 0x70, 0x00, 0x00 }},
+/* four */ { 52, {0x00, 0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10, 0x00, 0x00 }},
+/* five */ { 53, {0x00, 0xF8, 0x80, 0xB0, 0xC8, 0x08, 0x88, 0x70, 0x00, 0x00 }},
+/* six */ { 54, {0x00, 0x30, 0x40, 0x80, 0xB0, 0xC8, 0x88, 0x70, 0x00, 0x00 }},
+/* seven */ { 55, {0x00, 0xF8, 0x08, 0x10, 0x10, 0x20, 0x40, 0x40, 0x00, 0x00 }},
+/* eight */ { 56, {0x00, 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* nine */ { 57, {0x00, 0x70, 0x88, 0x98, 0x68, 0x08, 0x10, 0x60, 0x00, 0x00 }},
+/* colon */ { 58, {0x00, 0x00, 0x20, 0x70, 0x20, 0x00, 0x20, 0x70, 0x20, 0x00 }},
+/* semicolon */ { 59, {0x00, 0x00, 0x20, 0x70, 0x20, 0x00, 0x30, 0x20, 0x40, 0x00 }},
+/* less */ { 60, {0x00, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00 }},
+/* equal */ { 61, {0x00, 0x00, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00 }},
+/* greater */ { 62, {0x00, 0x40, 0x20, 0x10, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00 }},
+/* question */ { 63, {0x00, 0x70, 0x88, 0x10, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00 }},
+/* at */ { 64, {0x00, 0x70, 0x88, 0x98, 0xA8, 0xB0, 0x80, 0x70, 0x00, 0x00 }},
+/* A */ { 65, {0x00, 0x20, 0x50, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, 0x00 }},
+/* B */ { 66, {0x00, 0xF0, 0x48, 0x48, 0x70, 0x48, 0x48, 0xF0, 0x00, 0x00 }},
+/* C */ { 67, {0x00, 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70, 0x00, 0x00 }},
+/* D */ { 68, {0x00, 0xF0, 0x48, 0x48, 0x48, 0x48, 0x48, 0xF0, 0x00, 0x00 }},
+/* E */ { 69, {0x00, 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8, 0x00, 0x00 }},
+/* F */ { 70, {0x00, 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x00, 0x00 }},
+/* G */ { 71, {0x00, 0x70, 0x88, 0x80, 0x80, 0x98, 0x88, 0x70, 0x00, 0x00 }},
+/* H */ { 72, {0x00, 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88, 0x00, 0x00 }},
+/* I */ { 73, {0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* J */ { 74, {0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x90, 0x60, 0x00, 0x00 }},
+/* K */ { 75, {0x00, 0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88, 0x00, 0x00 }},
+/* L */ { 76, {0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8, 0x00, 0x00 }},
+/* M */ { 77, {0x00, 0x88, 0x88, 0xD8, 0xA8, 0x88, 0x88, 0x88, 0x00, 0x00 }},
+/* N */ { 78, {0x00, 0x88, 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x00, 0x00 }},
+/* O */ { 79, {0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* P */ { 80, {0x00, 0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80, 0x00, 0x00 }},
+/* Q */ { 81, {0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0xA8, 0x70, 0x08, 0x00 }},
+/* R */ { 82, {0x00, 0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88, 0x00, 0x00 }},
+/* S */ { 83, {0x00, 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70, 0x00, 0x00 }},
+/* T */ { 84, {0x00, 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }},
+/* U */ { 85, {0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* V */ { 86, {0x00, 0x88, 0x88, 0x88, 0x50, 0x50, 0x50, 0x20, 0x00, 0x00 }},
+/* W */ { 87, {0x00, 0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88, 0x00, 0x00 }},
+/* X */ { 88, {0x00, 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88, 0x00, 0x00 }},
+/* Y */ { 89, {0x00, 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }},
+/* Z */ { 90, {0x00, 0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8, 0x00, 0x00 }},
+/* bracketleft */ { 91, {0x00, 0x70, 0x40, 0x40, 0x40, 0x40, 0x40, 0x70, 0x00, 0x00 }},
+/* backslash */ { 92, {0x00, 0x80, 0x80, 0x40, 0x20, 0x10, 0x08, 0x08, 0x00, 0x00 }},
+/* bracketright */ { 93, {0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, 0x00 }},
+/* asciicircum */ { 94, {0x00, 0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* underscore */ { 95, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00 }},
+/* grave */ { 96, {0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* a */ { 97, {0x00, 0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00 }},
+/* b */ { 98, {0x00, 0x80, 0x80, 0xB0, 0xC8, 0x88, 0xC8, 0xB0, 0x00, 0x00 }},
+/* c */ { 99, {0x00, 0x00, 0x00, 0x70, 0x88, 0x80, 0x88, 0x70, 0x00, 0x00 }},
+/* d */ { 100, {0x00, 0x08, 0x08, 0x68, 0x98, 0x88, 0x98, 0x68, 0x00, 0x00 }},
+/* e */ { 101, {0x00, 0x00, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, 0x00 }},
+/* f */ { 102, {0x00, 0x30, 0x48, 0x40, 0xF0, 0x40, 0x40, 0x40, 0x00, 0x00 }},
+/* g */ { 103, {0x00, 0x00, 0x00, 0x78, 0x88, 0x88, 0x78, 0x08, 0x88, 0x70 }},
+/* h */ { 104, {0x00, 0x80, 0x80, 0xB0, 0xC8, 0x88, 0x88, 0x88, 0x00, 0x00 }},
+/* i */ { 105, {0x00, 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* j */ { 106, {0x00, 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x48, 0x48, 0x30 }},
+/* k */ { 107, {0x00, 0x80, 0x80, 0x88, 0x90, 0xE0, 0x90, 0x88, 0x00, 0x00 }},
+/* l */ { 108, {0x00, 0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* m */ { 109, {0x00, 0x00, 0x00, 0xD0, 0xA8, 0xA8, 0xA8, 0x88, 0x00, 0x00 }},
+/* n */ { 110, {0x00, 0x00, 0x00, 0xB0, 0xC8, 0x88, 0x88, 0x88, 0x00, 0x00 }},
+/* o */ { 111, {0x00, 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* p */ { 112, {0x00, 0x00, 0x00, 0xB0, 0xC8, 0x88, 0xC8, 0xB0, 0x80, 0x80 }},
+/* q */ { 113, {0x00, 0x00, 0x00, 0x68, 0x98, 0x88, 0x98, 0x68, 0x08, 0x08 }},
+/* r */ { 114, {0x00, 0x00, 0x00, 0xB0, 0xC8, 0x80, 0x80, 0x80, 0x00, 0x00 }},
+/* s */ { 115, {0x00, 0x00, 0x00, 0x70, 0x80, 0x70, 0x08, 0xF0, 0x00, 0x00 }},
+/* t */ { 116, {0x00, 0x40, 0x40, 0xF0, 0x40, 0x40, 0x48, 0x30, 0x00, 0x00 }},
+/* u */ { 117, {0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x98, 0x68, 0x00, 0x00 }},
+/* v */ { 118, {0x00, 0x00, 0x00, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, 0x00 }},
+/* w */ { 119, {0x00, 0x00, 0x00, 0x88, 0x88, 0xA8, 0xA8, 0x50, 0x00, 0x00 }},
+/* x */ { 120, {0x00, 0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88, 0x00, 0x00 }},
+/* y */ { 121, {0x00, 0x00, 0x00, 0x88, 0x88, 0x98, 0x68, 0x08, 0x88, 0x70 }},
+/* z */ { 122, {0x00, 0x00, 0x00, 0xF8, 0x10, 0x20, 0x40, 0xF8, 0x00, 0x00 }},
+/* braceleft */ { 123, {0x00, 0x18, 0x20, 0x10, 0x60, 0x10, 0x20, 0x18, 0x00, 0x00 }},
+/* bar */ { 124, {0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }},
+/* braceright */ { 125, {0x00, 0x60, 0x10, 0x20, 0x18, 0x20, 0x10, 0x60, 0x00, 0x00 }},
+/* asciitilde */ { 126, {0x00, 0x48, 0xA8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* space */ { 160, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* exclamdown */ { 161, {0x00, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }},
+/* cent */ { 162, {0x00, 0x00, 0x20, 0x78, 0xA0, 0xA0, 0xA0, 0x78, 0x20, 0x00 }},
+/* sterling */ { 163, {0x00, 0x30, 0x48, 0x40, 0xE0, 0x40, 0x48, 0xB0, 0x00, 0x00 }},
+/* currency */ { 164, {0x00, 0x00, 0x00, 0x88, 0x70, 0x50, 0x70, 0x88, 0x00, 0x00 }},
+/* yen */ { 165, {0x00, 0x88, 0x88, 0x50, 0x20, 0xF8, 0x20, 0x20, 0x20, 0x00 }},
+/* brokenbar */ { 166, {0x00, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x00, 0x00 }},
+/* section */ { 167, {0x00, 0x70, 0x80, 0xE0, 0x90, 0x48, 0x38, 0x08, 0x70, 0x00 }},
+/* dieresis */ { 168, {0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* copyright */ { 169, {0x00, 0x70, 0x88, 0xA8, 0xC8, 0xA8, 0x88, 0x70, 0x00, 0x00 }},
+/* ordfeminine */ { 170, {0x00, 0x38, 0x48, 0x58, 0x28, 0x00, 0x78, 0x00, 0x00, 0x00 }},
+/* guillemotleft */ { 171, {0x00, 0x00, 0x00, 0x24, 0x48, 0x90, 0x48, 0x24, 0x00, 0x00 }},
+/* logicalnot */ { 172, {0x00, 0x00, 0x00, 0x00, 0x78, 0x08, 0x00, 0x00, 0x00, 0x00 }},
+/* hyphen */ { 173, {0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* registered */ { 174, {0x00, 0x70, 0x88, 0xE8, 0xC8, 0xC8, 0x88, 0x70, 0x00, 0x00 }},
+/* macron */ { 175, {0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* degree */ { 176, {0x00, 0x20, 0x50, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* plusminus */ { 177, {0x00, 0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0xF8, 0x00, 0x00 }},
+/* twosuperior */ { 178, {0x30, 0x48, 0x10, 0x20, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* threesuperior */ { 179, {0x70, 0x08, 0x30, 0x08, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* acute */ { 180, {0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* mu */ { 181, {0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0xC8, 0xB0, 0x80, 0x00 }},
+/* paragraph */ { 182, {0x00, 0x78, 0xE8, 0xE8, 0x68, 0x28, 0x28, 0x28, 0x00, 0x00 }},
+/* periodcentered */ { 183, {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* cedilla */ { 184, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x20 }},
+/* onesuperior */ { 185, {0x20, 0x60, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+/* ordmasculine */ { 186, {0x00, 0x30, 0x48, 0x48, 0x30, 0x00, 0x78, 0x00, 0x00, 0x00 }},
+/* guillemotright */ { 187, {0x00, 0x00, 0x00, 0x90, 0x48, 0x24, 0x48, 0x90, 0x00, 0x00 }},
+/* onequarter */ { 188, {0x40, 0xC0, 0x40, 0x40, 0xE4, 0x0C, 0x14, 0x3C, 0x04, 0x00 }},
+/* onehalf */ { 189, {0x40, 0xC0, 0x40, 0x40, 0xE8, 0x14, 0x04, 0x08, 0x1C, 0x00 }},
+/* threequarters */ { 190, {0xC0, 0x20, 0x40, 0x20, 0xC8, 0x18, 0x28, 0x78, 0x08, 0x00 }},
+/* questiondown */ { 191, {0x00, 0x20, 0x00, 0x20, 0x20, 0x40, 0x88, 0x70, 0x00, 0x00 }},
+/* Agrave */ { 192, {0x40, 0x20, 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, 0x00 }},
+/* Aacute */ { 193, {0x10, 0x20, 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, 0x00 }},
+/* Acircumflex */ { 194, {0x20, 0x50, 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, 0x00 }},
+/* Atilde */ { 195, {0x48, 0xB0, 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, 0x00 }},
+/* Adieresis */ { 196, {0x50, 0x00, 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, 0x00 }},
+/* Aring */ { 197, {0x20, 0x50, 0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, 0x00 }},
+/* AE */ { 198, {0x00, 0x3C, 0x50, 0x90, 0x9C, 0xF0, 0x90, 0x9C, 0x00, 0x00 }},
+/* Ccedilla */ { 199, {0x00, 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70, 0x20, 0x40 }},
+/* Egrave */ { 200, {0x40, 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8, 0x00, 0x00 }},
+/* Eacute */ { 201, {0x10, 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8, 0x00, 0x00 }},
+/* Ecircumflex */ { 202, {0x20, 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8, 0x00, 0x00 }},
+/* Edieresis */ { 203, {0x50, 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8, 0x00, 0x00 }},
+/* Igrave */ { 204, {0x40, 0x20, 0x70, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* Iacute */ { 205, {0x10, 0x20, 0x70, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* Icircumflex */ { 206, {0x20, 0x50, 0x70, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* Idieresis */ { 207, {0x50, 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* Eth */ { 208, {0x00, 0xF0, 0x48, 0x48, 0xE8, 0x48, 0x48, 0xF0, 0x00, 0x00 }},
+/* Ntilde */ { 209, {0x28, 0x50, 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x00, 0x00 }},
+/* Ograve */ { 210, {0x40, 0x20, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Oacute */ { 211, {0x10, 0x20, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Ocircumflex */ { 212, {0x20, 0x50, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Otilde */ { 213, {0x28, 0x50, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Odieresis */ { 214, {0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* multiply */ { 215, {0x00, 0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88, 0x00, 0x00 }},
+/* Oslash */ { 216, {0x00, 0x70, 0x98, 0x98, 0xA8, 0xC8, 0xC8, 0x70, 0x00, 0x00 }},
+/* Ugrave */ { 217, {0x40, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Uacute */ { 218, {0x10, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Ucircumflex */ { 219, {0x20, 0x50, 0x00, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Udieresis */ { 220, {0x50, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* Yacute */ { 221, {0x10, 0x20, 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x00, 0x00 }},
+/* Thorn */ { 222, {0x00, 0x80, 0xF0, 0x88, 0xF0, 0x80, 0x80, 0x80, 0x00, 0x00 }},
+/* germandbls */ { 223, {0x00, 0x70, 0x88, 0x90, 0xA0, 0x90, 0x88, 0xB0, 0x00, 0x00 }},
+/* agrave */ { 224, {0x40, 0x20, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00 }},
+/* aacute */ { 225, {0x10, 0x20, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00 }},
+/* acircumflex */ { 226, {0x20, 0x50, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00 }},
+/* atilde */ { 227, {0x28, 0x50, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00 }},
+/* adieresis */ { 228, {0x00, 0x50, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00 }},
+/* aring */ { 229, {0x20, 0x50, 0x20, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00 }},
+/* ae */ { 230, {0x00, 0x00, 0x00, 0x78, 0x14, 0x7C, 0x90, 0x7C, 0x00, 0x00 }},
+/* ccedilla */ { 231, {0x00, 0x00, 0x00, 0x70, 0x88, 0x80, 0x88, 0x70, 0x20, 0x40 }},
+/* egrave */ { 232, {0x40, 0x20, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, 0x00 }},
+/* eacute */ { 233, {0x10, 0x20, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, 0x00 }},
+/* ecircumflex */ { 234, {0x20, 0x50, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, 0x00 }},
+/* edieresis */ { 235, {0x00, 0x50, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, 0x00 }},
+/* igrave */ { 236, {0x40, 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* iacute */ { 237, {0x20, 0x40, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* icircumflex */ { 238, {0x20, 0x50, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* idieresis */ { 239, {0x00, 0x50, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00 }},
+/* eth */ { 240, {0x00, 0xC0, 0x30, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* ntilde */ { 241, {0x28, 0x50, 0x00, 0xB0, 0xC8, 0x88, 0x88, 0x88, 0x00, 0x00 }},
+/* ograve */ { 242, {0x40, 0x20, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* oacute */ { 243, {0x10, 0x20, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* ocircumflex */ { 244, {0x20, 0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* otilde */ { 245, {0x28, 0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* odieresis */ { 246, {0x00, 0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00 }},
+/* divide */ { 247, {0x00, 0x00, 0x20, 0x00, 0xF8, 0x00, 0x20, 0x00, 0x00, 0x00 }},
+/* oslash */ { 248, {0x00, 0x00, 0x00, 0x78, 0x98, 0xA8, 0xC8, 0xF0, 0x00, 0x00 }},
+/* ugrave */ { 249, {0x40, 0x20, 0x00, 0x88, 0x88, 0x88, 0x98, 0x68, 0x00, 0x00 }},
+/* uacute */ { 250, {0x10, 0x20, 0x00, 0x88, 0x88, 0x88, 0x98, 0x68, 0x00, 0x00 }},
+/* ucircumflex */ { 251, {0x20, 0x50, 0x00, 0x88, 0x88, 0x88, 0x98, 0x68, 0x00, 0x00 }},
+/* udieresis */ { 252, {0x00, 0x50, 0x00, 0x88, 0x88, 0x88, 0x98, 0x68, 0x00, 0x00 }},
+/* yacute */ { 253, {0x00, 0x10, 0x20, 0x88, 0x88, 0x98, 0x68, 0x08, 0x88, 0x70 }},
+/* thorn */ { 254, {0x00, 0x00, 0x80, 0xF0, 0x88, 0x88, 0x88, 0xF0, 0x80, 0x80 }},
+/* ydieresis */ { 255, {0x00, 0x50, 0x00, 0x88, 0x88, 0x98, 0x68, 0x08, 0x88, 0x70 }},
+/* char0 */ { 0, {0x00, 0xA8, 0x00, 0x88, 0x00, 0x88, 0x00, 0xA8, 0x00, 0x00 }}
+};
+
+void alto2_cpu_device::fake_status_putch(int x, UINT8 ch)
+{
+ const bdf_6x10_t* pf = bdf_6x10;
+ while (pf->code != ch && pf->code != 0)
+ pf++;
+ int dx = 6 * x;
+ if (dx >= ALTO2_DISPLAY_WIDTH)
+ return;
+ for (int dy = 0; dy < 10; dy++)
+ {
+ UINT8* pix = m_dsp.scanline[ALTO2_DISPLAY_HEIGHT + 1 + dy] + dx;
+ UINT8 bits = ~pf->bits[dy];
+ pix[0] = (bits >> 7) & 1;
+ pix[1] = (bits >> 6) & 1;
+ pix[2] = (bits >> 5) & 1;
+ pix[3] = (bits >> 4) & 1;
+ pix[4] = (bits >> 3) & 1;
+ pix[5] = (bits >> 2) & 1;
+ }
+
+}
+
+void alto2_cpu_device::fake_status_printf(int x, const char* format, ...)
+{
+ static char buff[256];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buff, sizeof(buff), format, ap);
+ va_end(ap);
+ char* src = buff;
+ while (*src)
+ fake_status_putch(x++, *src++);
+}
diff --git a/src/emu/cpu/alto2/a2disp.h b/src/emu/cpu/alto2/a2disp.h
new file mode 100644
index 00000000000..1e0d5dd7d26
--- /dev/null
+++ b/src/emu/cpu/alto2/a2disp.h
@@ -0,0 +1,303 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display block
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+/**
+ * @brief start value for the horizontal line counter
+ *
+ * This value is loaded into the three 4 bit counters (type 9316)
+ * with numbers 65, 67, and 75.
+ * 65: A=0 B=1 C=1 D=0
+ * 67: A=1 B=0 C=0 D=1
+ * 75: A=0 B=0 C=0 D=0
+ *
+ * The value is 150
+ */
+#define ALTO2_DISPLAY_HLC_START (2+4+16+128)
+
+/**
+ * @brief end value for the horizontal line counter
+ *
+ * This is decoded by H30, an 8 input NAND gate.
+ * The value is 1899; horz. line count range 150...1899 = 1750.
+ *
+ * There are 1750 / 2 = 875 total scanlines.
+ */
+#define ALTO2_DISPLAY_HLC_END (1+2+8+32+64+256+512+1024)
+
+/**
+ * @brief display total height, including overscan (vertical blanking and synch)
+ *
+ * The display is interleaved in two fields, alternatingly drawing the even and odd
+ * scanlines to the monitor. The frame rate is 60Hz, which is actually the rate
+ * of the half-frames. The rate for full frames is thus 30Hz.
+ */
+#define ALTO2_DISPLAY_TOTAL_HEIGHT ((ALTO2_DISPLAY_HLC_END + 1 - ALTO2_DISPLAY_HLC_START) / 2)
+
+/**
+ * @brief display total width, including horizontal blanking
+ *
+ * Known facts:
+ *
+ * We have 606x808 visible pixels, and the pixel clock is said to be 50ns
+ * (20MHz), while the crystal in the schematics is labeled 20.16 MHz,
+ * so the pixel clock would actually be 49.6031ns.
+ *
+ * The total number of scanlines is, according to the docs, 875.
+ *
+ * 875 scanlines at 30 frames per second, thus the scanline rate is 26.250 kHz.
+ *
+ * If I divide 20.16 MHz by 26.250 kHz, I get 768 pixels for the total width
+ * of a scanline in pixels.
+ *
+ * The horizontal blanking period would then be 768 - 606 = 162 pixels, and
+ * thus 162 * 49.6031ns ~= 8036ns = 8.036us for the HBLANK time.
+ *
+ * In the display schematics there is a divide by 24 logic, and when
+ * dividing the 768 pixels per scanline by 24, we have 32 phases of a scanline.
+ *
+ * A S8223 PROM (a63) with 32x8 bits contains the status of the HBLANK and
+ * HSYNC signals for these phases, the SCANEND and HLCGATE signals, as well
+ * as its own next address A0-A3!
+ *
+ */
+#define ALTO2_DISPLAY_TOTAL_WIDTH 768
+
+
+#define ALTO2_DISPLAY_FIFO 16 //!< the display fifo has 16 words
+#define ALTO2_DISPLAY_SCANLINE_WORDS (ALTO2_DISPLAY_TOTAL_WIDTH/16) //!< words per scanline
+#define ALTO2_DISPLAY_HEIGHT 808 //!< number of visible scanlines per frame; 808 really, but there are some empty lines?
+#define ALTO2_DISPLAY_WIDTH 606 //!< visible width of the display; 38 x 16 bit words - 2 pixels
+#define ALTO2_DISPLAY_VISIBLE_WORDS ((ALTO2_DISPLAY_WIDTH+15)/16) //!< visible words per scanline
+#define ALTO2_DISPLAY_BITCLOCK 20160000ll //!< display bit clock in in Hertz (20.16MHz)
+#define ALTO2_DISPLAY_BITTIME(n) (U64(1000000000000)*(n)/ALTO2_DISPLAY_BITCLOCK) //!< display bit time in in pico seconds (~= 49.6031ns)
+#define ALTO2_DISPLAY_SCANLINE_TIME ALTO2_DISPLAY_BITTIME(ALTO2_DISPLAY_TOTAL_WIDTH)//!< time for a scanline in pico seconds (768 * 49.6031ns ~= 38095.1808ns)
+#define ALTO2_DISPLAY_VISIBLE_TIME ALTO2_DISPLAY_BITTIME(ALTO2_DISPLAY_WIDTH) //!< time of the visible part of a scanline in pico seconds (606 * 49.6031ns ~= 30059.4786ns)
+#define ALTO2_DISPLAY_WORD_TIME ALTO2_DISPLAY_BITTIME(16) //!< time for a word in pico seconds (16 pixels * 49.6031ns ~= 793.6496ns)
+#define ALTO2_DISPLAY_VBLANK_TIME ((ALTO2_DISPLAY_TOTAL_HEIGHT-ALTO2_DISPLAY_HEIGHT)*HZ_TO_ATTOSECONDS(26250)/2)
+
+#else // ALTO2_DEFINE_CONSTANTS
+/**
+ * @brief structure of the display context
+ *
+ * Schematics of the task clear and wakeup signal generators
+ * <PRE>
+ * A quote (') appended to a signal name means inverted signal.
+ *
+ * AND | NAND| NOR | FF | Q N174
+ * -----+--- -----+--- -----+--- -----+--- -----
+ * 0 0 | 0 0 0 | 1 0 0 | 1 S'\0| 1 delay
+ * 0 1 | 0 0 1 | 1 0 1 | 0 R'\0| 0
+ * 1 0 | 0 1 0 | 1 1 0 | 0
+ * 1 1 | 1 1 1 | 0 1 1 | 0
+ *
+ *
+ * DVTAC'
+ * >-------+ +-----+
+ * | | FF |
+ * VBLANK'+----+ DELVBLANK' +---+ DELVBLANK +----+ +--|S' |
+ * >------|N174|------+-----|inv|--------------|NAND| VBLANKPULSE | | WAKEDVT'
+ * +----+ | +---+ | o--+-----------|R' Q|---------------------->
+ * | +-| | | | |
+ * +----+ | DDELVBLANK' | +----+ | +-----+
+ * +-|N174|-----------------------------+ | +---+
+ * | +----+ | +------oAND|
+ * | | DSP07.01 | | o----------+
+ * +-------------+ >---------|------o | |
+ * | +---+ |
+ * | |
+ * | +-----+ |
+ * | | FF | | +-----+
+ * DHTAC' +---+ | | | | | FF |
+ * >--------------oNOR| *07.25 +----+ +-|S' | DHTBLK' | | |
+ * BLOCK' | |---------------|NAND| | Q|--+----------|--|S1' | WAKEDHT'
+ * >--------------o | DCSYSCLK | o--------|R' | | >--------|--|S2' Q|--------->
+ * +---+ >---------| | +-----+ | DHTAC' +--|R' |
+ * +----+ | +-----+
+ * +------------+
+ * |
+ * DWTAC' +---+ | +-----+
+ * >--------------oNOR| *07.26 +----+ | | FF |
+ * BLOCK' | |---------|NAND| DSP07.01 | | |
+ * >--------------o | DCSYSCLK| o----------|---|S1' | DWTCN' +---+ DWTCN
+ * +---+ >-------| | +---|S2' Q|--------|inv|-----------+----
+ * +----+ +---|R' | +---+ |
+ * | +-----+ |
+ * SCANEND +----+ | |
+ * >-------------|NAND| CLRBUF' | .----------------------+
+ * DCLK | o----------------+ |
+ * >-------------| | | +-----+
+ * +----+ +--| NAND|
+ * STOPWAKE' | |preWake +----+ WAKEDWT'
+ * >-----------| o--------|N174|--------->
+ * VBLANK' | | +----+
+ * >-----------| |
+ * +-----+
+ * a40c
+ * VBLANKPULSE +----+
+ * -------------|NAND|
+ * | o--+
+ * +--| | |
+ * | +----+ |
+ * +----------|-+
+ * +----------+ |
+ * CURTAC' +---+ | +----+ | a20d
+ * >--------------oNOR| *07.27 +----+ +--|NAND| | +----+
+ * BLOCK' | |---------|NAND| DSP07.07 | o----+----o NOR| preWK +----+ WAKECURT'
+ * >--------------o | DCSYSCLK| o-----------| | | |--------|N174|--------->
+ * +---+ >-------| | +----+ +----o | +----+
+ * +----+ a40d | +----+
+ * a30c |
+ * CURTAC' +----+ |
+ * >------------|NAND| DSP07.03 |
+ * | o--+------------+
+ * +--| | |
+ * | +----+ |
+ * +----------|-+
+ * +----------+ |
+ * | +----+ |
+ * +--|NAND| |
+ * CLRBUF' | o----+
+ * >------------| |
+ * +----+
+ * a30d
+ * </PRE>
+ */
+
+#ifndef _A2DISP_H_
+#define _A2DISP_H_
+struct {
+ UINT16 state; //!< current state of the display_state_machine()
+ UINT16 hlc; //!< horizontal line counter
+ UINT16 setmode; //!< value written by last SETMODE<-
+ UINT16 inverse; //!< set to 0xffff if line is inverse, 0x0000 otherwise
+ bool halfclock; //!< set 0 for normal pixel clock, 1 for half pixel clock
+ UINT16 fifo[ALTO2_DISPLAY_FIFO]; //!< display word fifo
+ UINT8 wa; //!< fifo input pointer (write address; 4-bit)
+ UINT8 ra; //!< fifo output pointer (read address; 4-bit)
+ UINT8 a63; //!< most recent value read from the PROM a63
+ UINT8 a66; //!< most recent value read from the PROM a66
+ bool dht_blocks; //!< set non-zero, if the DHT executed BLOCK
+ bool dwt_blocks; //!< set non-zero, if the DWT executed BLOCK
+ bool curt_blocks; //!< set non-zero, if the CURT executed BLOCK
+ bool curt_wakeup; //!< set non-zero, if CURT wakeups are generated
+ UINT16 vblank; //!< most recent HLC with VBLANK still high (11-bit)
+ UINT16 xpreg; //!< cursor cursor x position register (10-bit)
+ UINT16 csr; //!< cursor shift register (16-bit)
+ UINT32 curxpos; //!< helper: first cursor word in scanline
+ UINT16 cursor0; //!< helper: shifted cursor data for left word
+ UINT16 cursor1; //!< helper: shifted cursor data for right word
+ UINT16 *raw_bitmap; //!< array of words of the raw bitmap that is displayed
+ UINT8 **scanline; //!< array of scanlines with 1 byte per pixel
+ bitmap_ind16 *bitmap; //!< MAME bitmap with 16 bit indices
+ bool odd_frame; //!< true, if odd frame is drawn
+} m_dsp;
+
+/**
+ * @brief PROM a38 contains the STOPWAKE' and MBEMBPTY' signals for the FIFO
+ * <PRE>
+ * The inputs to a38 are the UNLOAD counter RA[0-3] and the DDR<- counter
+ * WA[0-3], and the designer decided to reverse the address lines :-)
+ *
+ * a38 counter FIFO counter
+ * --------------------------
+ * A0 RA[0] fifo_rd
+ * A1 RA[1]
+ * A2 RA[2]
+ * A3 RA[3]
+ * A4 WA[0] fifo_wr
+ * A5 WA[1]
+ * A6 WA[2]
+ * A7 WA[3]
+ *
+ * Only two bits of a38 are used:
+ * O1 (002) = STOPWAKE'
+ * O3 (010) = MBEMPTY'
+ * </PRE>
+ */
+UINT8* m_disp_a38;
+
+//! output bits of PROM A38
+enum {
+ disp_a38_STOPWAKE = (1 << 1),
+ disp_a38_MBEMPTY = (1 << 3)
+};
+
+/**
+ * @brief emulation of PROM a63 in the display schematics page 8
+ * <PRE>
+ * The PROM's address lines are driven by a clock CLK, which is
+ * pixel clock / 24, and an inverted half-scanline signal H[1]'.
+ *
+ * It is 32x8 bits and its output bits (B) are connected to the
+ * signals, as well as its own address lines (A) through a latch
+ * of the type SN74774 like this:
+ *
+ * B 174 A others
+ * ------------------------
+ * 0 5 - HBLANK
+ * 1 0 - HSYNC
+ * 2 4 0
+ * 3 1 1
+ * 4 3 2
+ * 5 2 3
+ * 6 - - SCANEND
+ * 7 - - HLCGATE
+ * ------------------------
+ * H[1]' - 4
+ *
+ * The display_state_machine() is called by the CPU at a rate of pixelclock/24,
+ * which happens to be very close to every 7th CPU micrcocycle.
+ * </PRE>
+ */
+UINT8* m_disp_a63;
+
+enum {
+ disp_a63_HBLANK = (1 << 0), //!< PROM a63 B0 is latched as HBLANK signal
+ disp_a63_HSYNC = (1 << 1), //!< PROM a63 B1 is latched as HSYNC signal
+ disp_a63_A0 = (1 << 2), //!< PROM a63 B2 is the latched next address bit A0
+ disp_a63_A1 = (1 << 3), //!< PROM a63 B3 is the latched next address bit A1
+ disp_a63_A2 = (1 << 4), //!< PROM a63 B4 is the latched next address bit A2
+ disp_a63_A3 = (1 << 5), //!< PROM a63 B5 is the latched next address bit A3
+ disp_a63_SCANEND = (1 << 6), //!< PROM a63 B6 SCANEND signal, which resets the FIFO counters
+ disp_a63_HLCGATE = (1 << 7) //!< PROM a63 B7 HLCGATE signal, which enables counting the HLC
+};
+
+/**
+ * @brief vertical blank and synch PROM
+ *
+ * PROM a66 is a 256x4 bit (type 3601), containing the vertical blank + synch.
+ * Address lines are driven by H[1] to H[128] of the the horz. line counters.
+ * The PROM is enabled whenever H[256] and H[512] are both 0.
+ */
+UINT8* m_disp_a66;
+
+enum {
+ disp_a66_VSYNC_ODD = (1 << 0), //!< Q1 (001) is VSYNC for the odd field (with H1024=1)
+ disp_a66_VSYNC_EVEN = (1 << 1), //!< Q2 (002) is VSYNC for the even field (with H1024=0)
+ disp_a66_VBLANK_ODD = (1 << 2), //!< Q3 (004) is VBLANK for the odd field (with H1024=1)
+ disp_a66_VBLANK_EVEN = (1 << 3) //!< Q4 (010) is VBLANK for the even field (with H1024=0)
+};
+
+void update_bitmap_word(UINT16* bitmap, int x, int y, UINT16 word); //!< update a word in the screen bitmap
+void unload_word(); //!< unload the next word from the display FIFO and shift it to the screen
+void display_state_machine(); //!< function called by the CPU to enter the next display state
+
+void f2_late_evenfield(void); //!< branch on the evenfield flip-flop
+
+void init_disp(); //!< initialize the display context
+void exit_disp(); //!< deinitialize the display context
+void reset_disp(); //!< reset the display context
+
+void fake_status_putch(int x, UINT8 ch);
+void fake_status_printf(int x, const char* format, ...);
+
+#endif // _A2DISP_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2dvt.c b/src/emu/cpu/alto2/a2dvt.c
new file mode 100644
index 00000000000..124acf3b606
--- /dev/null
+++ b/src/emu/cpu/alto2/a2dvt.c
@@ -0,0 +1,48 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display vertical task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+/**
+ * @brief f1_dvt_block early: disable the display word task
+ */
+void alto2_cpu_device::f1_early_dvt_block()
+{
+// m_task_wakeup &= ~(1 << m_task);
+ LOG((LOG_DVT,2," BLOCK %s\n", task_name(m_task)));
+}
+
+
+/**
+ * @brief called by the CPU when the display vertical task becomes active
+ */
+void alto2_cpu_device::activate_dvt()
+{
+ m_task_wakeup &= ~(1 << m_task);
+}
+
+/**
+ * @brief initialize display vertical task
+ */
+void alto2_cpu_device::init_dvt(int task)
+{
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_dvt_block, 0);
+ set_f2(task, f2_dvt_evenfield, 0, &alto2_cpu_device::f2_late_evenfield);
+ m_active_callback[task] = &alto2_cpu_device::activate_dvt;
+}
+
+void alto2_cpu_device::exit_dvt()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_dvt()
+{
+ // nothing to do yet
+}
diff --git a/src/emu/cpu/alto2/a2dvt.h b/src/emu/cpu/alto2/a2dvt.h
new file mode 100644
index 00000000000..1afe28fa53b
--- /dev/null
+++ b/src/emu/cpu/alto2/a2dvt.h
@@ -0,0 +1,27 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display vertical task (DVT)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2DVT_H_
+#define _A2DVT_H_
+
+//! F2 functions for display vertical task
+enum {
+ f2_dvt_evenfield = f2_task_10 //!< f2 10: load even field
+};
+
+void f1_early_dvt_block(); //!< F1 func: disable the display word task
+void activate_dvt(); //!< called by the CPU when the display vertical task becomes active
+void init_dvt(int task = task_dvt); //!< initialize the display vertical task
+void exit_dvt(); //!< deinitialize the display vertical task
+void reset_dvt(); //!< reset the display vertical task
+#endif // _A2DVT_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2dwt.c b/src/emu/cpu/alto2/a2dwt.c
new file mode 100644
index 00000000000..07c5f07ae65
--- /dev/null
+++ b/src/emu/cpu/alto2/a2dwt.c
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display word task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+//! PROM a38 bit O1 is STOPWAKE' (stop DWT if bit is zero)
+#define FIFO_STOPWAKE(a38) (0 == (a38 & disp_a38_STOPWAKE) ? true : false)
+
+/**
+ * @brief block the display word task
+ */
+void alto2_cpu_device::f1_early_dwt_block()
+{
+ m_dsp.dwt_blocks = true;
+
+ /* clear the wakeup for the display word task */
+ m_task_wakeup &= ~(1 << m_task);
+ LOG((LOG_DWT,2," BLOCK %s\n", task_name(m_task)));
+
+ /* wakeup the display horizontal task, if it didn't block itself */
+ if (!m_dsp.dht_blocks)
+ m_task_wakeup |= 1 << task_dht;
+}
+
+/**
+ * @brief load the display data register
+ */
+void alto2_cpu_device::f2_late_dwt_load_ddr()
+{
+ LOG((LOG_DWT,2," DDR<- BUS (%#o)\n", m_bus));
+ m_dsp.fifo[m_dsp.wa] = m_bus;
+ m_dsp.wa = (m_dsp.wa + 1) % ALTO2_DISPLAY_FIFO;
+ UINT8 a38 = m_disp_a38[m_dsp.ra * 16 + m_dsp.wa];
+ if (FIFO_STOPWAKE(a38))
+ m_task_wakeup &= ~(1 << task_dwt);
+ LOG((LOG_DWT,2, " DWT push %04x into FIFO[%02o]%s\n",
+ m_bus, (m_dsp.wa - 1) & (ALTO2_DISPLAY_FIFO - 1),
+ FIFO_STOPWAKE(a38) ? " STOPWAKE" : ""));
+}
+
+void alto2_cpu_device::init_dwt(int task)
+{
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_dwt_block, 0);
+ set_f2(task, f2_dwt_load_ddr, 0, &alto2_cpu_device::f2_late_dwt_load_ddr);
+}
+
+void alto2_cpu_device::exit_dwt()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_dwt()
+{
+ m_dsp.dwt_blocks = false;
+ memset(m_dsp.fifo, 0, sizeof(m_dsp.fifo));
+ m_dsp.wa = 0;
+ m_dsp.ra = 0;
+}
diff --git a/src/emu/cpu/alto2/a2dwt.h b/src/emu/cpu/alto2/a2dwt.h
new file mode 100644
index 00000000000..f4bdb91f890
--- /dev/null
+++ b/src/emu/cpu/alto2/a2dwt.h
@@ -0,0 +1,27 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII display word task (DWT)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2DWT_H_
+#define _A2DWT_H_
+
+//! F2 functions for display word task
+enum {
+ f2_dwt_load_ddr = f2_task_10 //!< f2 10: load display data register
+};
+
+void f1_early_dwt_block(); //!< F1 func: block the display word task
+void f2_late_dwt_load_ddr(); //!< F2 func: load the display data register
+void init_dwt(int task = task_dwt); //!< initialize the display word task
+void exit_dwt(); //!< deinitialize the display word task
+void reset_dwt(); //!< reset the display word task
+#endif // _A2DWT_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2emu.c b/src/emu/cpu/alto2/a2emu.c
new file mode 100644
index 00000000000..d62d6a6e523
--- /dev/null
+++ b/src/emu/cpu/alto2/a2emu.c
@@ -0,0 +1,690 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII emulator task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+/** @brief CTL2K_U3 address line for F2 function */
+#define CTL2K_U3(f2) (f2 == f2_emu_idisp ? 0x80 : 0x00)
+
+/**
+ * width,from,to of the 16 bit instruction register
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * =============================================================
+ * x - - - - - - - - - - - - - - - arithmetic operation
+ * 0 m m - - - - - - - - - - - - - memory function
+ * 0 0 0 - - - - - - - - - - - - - jump functions
+ * 0 0 1 d d - - - - - - - - - - - LDA dstAC
+ * 0 1 0 d d - - - - - - - - - - - STA dstAC
+ * 0 1 1 - - - - - - - - - - - - - augmented functions
+ * 1 s s - - - - - - - - - - - - - source accumulator (0-3)
+ * 1 - - d d - - - - - - - - - - - destination accumulator (0-3)
+ * 1 s s d d x x x - - - - - - - - accumulator function
+ * 1 s s d d 0 0 0 - - - - - - - - COM dstAC, srcAC
+ * 1 s s d d 0 0 1 - - - - - - - - NEG dstAC, srcAC
+ * 1 s s d d 0 1 0 - - - - - - - - MOV dstAC, srcAC
+ * 1 s s d d 0 1 1 - - - - - - - - INC dstAC, srcAC
+ * 1 s s d d 1 0 0 - - - - - - - - ADC dstAC, srcAC
+ * 1 s s d d 1 0 1 - - - - - - - - SUB dstAC, srcAC
+ * 1 s s d d 1 1 0 - - - - - - - - ADD dstAC, srcAC
+ * 1 s s d d 1 1 1 - - - - - - - - AND dstAC, srcAC
+ * 1 - - - - - - - x x - - - - - - shift operation
+ * 1 - - - - - - - 0 0 - - - - - - nothing
+ * 1 - - - - - - - 0 1 - - - - - - rotate left through carry
+ * 1 - - - - - - - 1 0 - - - - - - rotate right through carry
+ * 1 - - - - - - - 1 1 - - - - - - swap byte halves
+ * 1 - - - - - - - - - x x - - - - carry in mode
+ * 1 - - - - - - - - - 0 0 - - - - nothing
+ * 1 - - - - - - - - - 0 1 - - - - Z carry in is zero
+ * 1 - - - - - - - - - 1 0 - - - - O carry in is one
+ * 1 - - - - - - - - - 1 1 - - - - C carry in is complemented carry
+ * 1 - - - - - - - - - - - x - - - NL
+ * - - - - - - - - - - - - - x x x conditional execution
+ * - - - - - - - - - - - - - 0 0 0 NVR never skip
+ * - - - - - - - - - - - - - 0 0 1 SKP always skip
+ * - - - - - - - - - - - - - 0 1 0 SZC skip if carry result is zero
+ * - - - - - - - - - - - - - 0 1 1 SNC skip if carry result is non-zero
+ * - - - - - - - - - - - - - 1 0 0 SZR skip if 16 bit result is zero
+ * - - - - - - - - - - - - - 1 0 1 SNR skip if 16 bit result is non-zero
+ * - - - - - - - - - - - - - 1 1 0 SEZ skip if either result is zero
+ * - - - - - - - - - - - - - 1 1 1 SBN skip if both results are non-zero
+ */
+#define IR_ARITH(ir) X_RDBITS(ir,16, 0, 0)
+#define IR_SrcAC(ir) X_RDBITS(ir,16, 1, 2)
+#define IR_DstAC(ir) X_RDBITS(ir,16, 3, 4)
+#define IR_AFunc(ir) X_RDBITS(ir,16, 5, 7)
+#define IR_SH(ir) X_RDBITS(ir,16, 8, 9)
+#define IR_CY(ir) X_RDBITS(ir,16,10,11)
+#define IR_NL(ir) X_RDBITS(ir,16,12,12)
+#define IR_SK(ir) X_RDBITS(ir,16,13,15)
+
+#define IR_MFunc(ir) X_RDBITS(ir,16, 1, 2)
+#define IR_JFunc(ir) X_RDBITS(ir,16, 3, 4)
+#define IR_I(ir) X_RDBITS(ir,16, 5, 5)
+#define IR_X(ir) X_RDBITS(ir,16, 6, 7)
+#define IR_DISP(ir) X_RDBITS(ir,16, 8,15)
+#define IR_AUGFUNC(ir) X_RDBITS(ir,16, 3, 7)
+
+#define op_MFUNC_MASK 0060000 //!< instruction register memory function mask
+#define op_MFUNC_JUMP 0000000 //!< jump functions value
+#define op_JUMP_MASK 0014000 //!< jump functions mask
+#define op_JMP 0000000 //!< jump
+#define op_JSR 0004000 //!< jump to subroutine
+#define op_ISZ 0010000 //!< increment and skip if zero
+#define op_DSZ 0014000 //!< decrement and skip if zero
+#define op_LDA 0020000 //!< load accu functions value
+#define op_STA 0040000 //!< store accu functions value
+#define op_AUGMENTED 0060000 //!< store accu functions value
+#define op_AUGM_MASK 0077400 //!< mask covering all augmented functions
+#define op_AUGM_NODISP 0061000 //!< augmented functions w/o displacement
+#define op_AUGM_SUBFUNC 0000037 //!< mask for augmented subfunctions in DISP
+#define op_CYCLE 0060000 //!< cycle AC0
+#define op_NODISP 0061000 //!< NODISP: opcodes without displacement
+#define op_DIR 0061000 //!< disable interrupts
+#define op_EIR 0061001 //!< enable interrupts
+#define op_BRI 0061002 //!< branch and return from interrupt
+#define op_RCLK 0061003 //!< read clock to AC0, AC1
+#define op_SIO 0061004 //!< start I/O
+#define op_BLT 0061005 //!< block transfer
+#define op_BLKS 0061006 //!< block set value
+#define op_SIT 0061007 //!< start interval timer
+#define op_JMPRAM 0061010 //!< jump to microcode RAM (actually ROM, too)
+#define op_RDRAM 0061011 //!< read microcode RAM
+#define op_WRTRAM 0061012 //!< write microcode RAM
+#define op_DIRS 0061013 //!< disable interrupts, and skip, if already disabled
+#define op_VERS 0061014 //!< get microcode version in AC0
+#define op_DREAD 0061015 //!< double word read (Alto II)
+#define op_DWRITE 0061016 //!< double word write (Alto II)
+#define op_DEXCH 0061017 //!< double word exchange (Alto II)
+#define op_MUL 0061020 //!< unsigned multiply
+#define op_DIV 0061021 //!< unsigned divide
+#define op_DIAGNOSE1 0061022 //!< write two different accus in fast succession
+#define op_DIAGNOSE2 0061023 //!< write Hamming code and memory
+#define op_BITBLT 0061024 //!< bit-aligned block transfer
+#define op_XMLDA 0061025 //!< load accu AC0 from extended memory (Alto II/XM)
+#define op_XMSTA 0061026 //!< store accu AC0 to extended memory (Alto II/XM)
+#define op_JSRII 0064400 //!< jump to subroutine PC relative, doubly indirect
+#define op_JSRIS 0065000 //!< jump to subroutine AC2 relative, doubly indirect
+#define op_CONVERT 0067000 //!< convert bitmapped font to bitmap
+#define op_ARITH_MASK 0103400 //!< mask for arithmetic functions
+#define op_COM 0100000 //!< one's complement
+#define op_NEG 0100400 //!< two's complement
+#define op_MOV 0101000 //!< accu transfer
+#define op_INC 0101400 //!< increment
+#define op_ADC 0102000 //!< add one's complement
+#define op_SUB 0102400 //!< subtract by adding two's complement
+#define op_ADD 0103000 //!< add
+#define op_AND 0103400 //!< logical and
+
+#define ea_DIRECT 0000000 //!< effective address is direct
+#define ea_INDIRECT 0002000 //!< effective address is indirect
+#define ea_MASK 0001400 //!< mask for effective address modes
+#define ea_PAGE0 0000000 //!< e is page 0 address
+#define ea_PCREL 0000400 //!< e is PC + signed displacement
+#define ea_AC2REL 0001000 //!< e is AC2 + signed displacement
+#define ea_AC3REL 0001400 //!< e is AC3 + signed displacement
+
+
+#define sh_MASK 0000300 //!< shift mode mask (do novel shifts)
+#define sh_L 0000100 //!< rotate left through carry
+#define sh_R 0000200 //!< rotate right through carry
+#define sh_S 0000300 //!< swap byte halves
+
+#define cy_MASK 0000060 //!< carry in mode mask
+#define cy_Z 0000020 //!< carry in is zero
+#define cy_O 0000040 //!< carry in is one
+#define cy_C 0000060 //!< carry in is complemented carry
+
+#define nl_MASK 0000010 //!< no-load mask
+#define nl_NONE 0000010 //!< do not load DstAC nor carry
+
+#define sk_MASK 0000007 //!< skip mask
+#define sk_NVR 0000000 //!< never skip
+#define sk_SKP 0000001 //!< always skip
+#define sk_SZC 0000002 //!< skip if carry result is zero
+#define sk_SNC 0000003 //!< skip if carry result is non-zero
+#define sk_SZR 0000004 //!< skip if 16-bit result is zero
+#define sk_SNR 0000005 //!< skip if 16-bit result is non-zero
+#define sk_SEZ 0000006 //!< skip if either result is zero
+#define sk_SBN 0000007 //!< skip if both results are non-zero
+
+/**
+ * @brief register selection
+ *
+ * <PRE>
+ * From the schematics: 08_ALU, page 6 (PDF page 4)
+ *
+ * EMACT emulator task active
+ * F2[0-2]=111b <-ACSOURCE and F2_17
+ * F2[0-2]=101b DNS<- and ACDEST<-
+ *
+ * u49 (8 input NAND 74S30)
+ * ----------------------------------------------
+ * F2[0] & F2[2] & F2[1]' & IR[03]' & EMACT
+ *
+ * F2[0-2] IR[03] EMACT output u49pin8
+ * --------------------------------------
+ * 101 0 1 0
+ * all others 1
+ *
+ *
+ * u59 (8 input NAND 74S30)
+ * ----------------------------------------------
+ * F2[0] & F2[2] & F2[1] & IR[01]' & EMACT
+ *
+ * F2[0-2] IR[01] EMACT output u59pin8
+ * --------------------------------------
+ * 111 0 1 0
+ * all others 1
+ *
+ * u70d (2 input NOR 74S02 used as inverter)
+ * ---------------------------------------------
+ * RSEL3 -> RSEL3'
+ *
+ * u79b (3 input NAND 74S10)
+ * ---------------------------------------------
+ * u49pin8 u59pin8 RSEL3' output 6RA3
+ * -------------------------------------
+ * 1 1 1 0
+ * 0 x x 1
+ * x 0 x 1
+ * x x 0 1
+ *
+ *
+ * u60 (8 input NAND 74S30)
+ * ----------------------------------------------
+ * F2[0] & F2[2] & F2[1]' & IR[02]' & EMACT
+ *
+ * F2[0-2] IR[02] EMACT output u60pin8
+ * --------------------------------------
+ * 101 0 1 0
+ * all others 1
+ *
+ * u50 (8 input NAND 74S30)
+ * ----------------------------------------------
+ * F2[0] & F2[2] & F2[1] & IR[04]' & EMACT
+ *
+ * F2[0-2] IR[04] EMACT output u50pin8
+ * --------------------------------------
+ * 111 0 1 0
+ * all others 1
+ *
+ * u70c (2 input NOR 74S02 used as inverter)
+ * ---------------------------------------------
+ * RSEL4 -> RSEL4'
+ *
+ *
+ * u79c (3 input NAND 74S10)
+ * ---------------------------------------------
+ * u60pin8 u50pin8 RSEL4' output 8RA4
+ * -------------------------------------
+ * 1 1 1 0
+ * 0 x x 1
+ * x 0 x 1
+ * x x 0 1
+ *
+ * BUG?: schematics seem to have swapped IR(04)' and IR(02)' inputs for the
+ * RA4 decoding, because SrcAC is selected from IR[1-2]?
+ * </PRE>
+ */
+
+/**
+ * @brief bs_disp early: drive bus by IR[8-15], possibly sign extended
+ *
+ * The high order bits of IR cannot be read directly, but the
+ * displacement field of IR (8 low order bits) may be read with
+ * the <-DISP bus source. If the X field of the instruction is
+ * zero (i.e., it specifies page 0 addressing), then the DISP
+ * field of the instruction is put on BUS[8-15] and BUS[0-7]
+ * is zeroed. If the X field of the instruction is non-zero
+ * (i.e. it specifies PC-relative or base-register addressing)
+ * then the DISP field is sign-extended and put on the bus.
+ *
+ */
+void alto2_cpu_device::bs_early_emu_disp()
+{
+ UINT16 r = IR_DISP(m_emu.ir);
+ if (IR_X(m_emu.ir)) {
+ r = ((signed char)r) & 0177777;
+ }
+ LOG((LOG_EMU,2, " <-DISP (%06o)\n", r));
+ m_bus &= r;
+}
+
+/**
+ * @brief f1_block early: block task
+ *
+ * The task request for the active task is cleared
+ */
+void alto2_cpu_device::f1_early_emu_block()
+{
+#if 0
+ CPU_CLR_TASK_WAKEUP(m_task);
+ LOG((LOG_EMU,2, " BLOCK %02o:%s\n", m_task, task_name(m_task)));
+#elif 0
+ fatal(1, "Emulator task want's to BLOCK.\n" \
+ "%s-%04o: r:%02o af:%02o bs:%02o f1:%02o f2:%02o" \
+ " t:%o l:%o next:%05o next2:%05o cycle:%lld\n",
+ task_name(m_task), m_mpc,
+ m_rsel, m_daluf, m_dbs, m_df1, mdf2,
+ m_dloadt, m_dloatl, m_next, m_next2,
+ ntime() / CPU_MICROCYCLE_TIME);
+#else
+ /* just ignore (?) */
+#endif
+}
+
+/**
+ * @brief f1_load_rmr late: load the reset mode register
+ */
+void alto2_cpu_device::f1_late_emu_load_rmr()
+{
+ LOG((LOG_EMU,2," RMR<-; BUS (%#o)\n", m_bus));
+ m_reset_mode = m_bus;
+}
+
+/**
+ * @brief f1_load_esrb late: load the extended S register bank from BUS[12-14]
+ */
+void alto2_cpu_device::f1_late_emu_load_esrb()
+{
+ LOG((LOG_EMU,2," ESRB<-; BUS[12-14] (%#o)\n", m_bus));
+ m_s_reg_bank[m_task] = X_RDBITS(m_bus,16,12,14);
+}
+
+/**
+ * @brief f1_rsnf early: drive the bus from the Ethernet node ID
+ *
+ * TODO: move this to the Ethernet code? It's really a emulator
+ * specific function that is decoded by the Ethernet card.
+ */
+void alto2_cpu_device::f1_early_rsnf()
+{
+ UINT16 r = 0177400 | m_ether_id;
+ LOG((LOG_EMU,2," <-RSNF; (%#o)\n", r));
+ m_bus &= r;
+}
+
+/**
+ * @brief f1_startf early: defines commands for for I/O hardware, including Ethernet
+ * <PRE>
+ * (SIO) Start I/O is included to facilitate I/O control, It places the contents of
+ * AC0 on the processor bus and executes the STARTF function (F1 = 17B). By convention,
+ * bits of AC0 must be "1" in order to signal devices. See Appendix C for a summary of
+ * assigned bits.
+ * Bit 0 100000B Standard Alto: Software boot feature
+ * Bit 14 000002B Standard Alto: Ethernet
+ * Bit 15 000001B Standard Alto: Ethernet
+ * If bit 0 of AC0 is 1, and if an Ethernet board is plugged into the Alto, the machine
+ * will boot, just as if the "boot button" were pressed (see sections 3.4, 8.4 and 9.2.2
+ * for discussions of bootstrapping).
+ *
+ * SIO also returns a result in AC0. If the Ethernet hardware is installed, the serial
+ * number and/or Ethernet host address of the machine (0-377B) is loaded into AC0[8-15].
+ * (On Alto I, the serial number and Ethernet host address are equivalent; on Alto II,
+ * the value loaded into AC0 is the Ethernet host address only.) If Ethernet hardware
+ * is missing, AC0[8-15] = 377B. Microcode installed after June 1976, which this manual
+ * describes, returns AC0[0] = 0. Microcode installed prior to June 1976 returns
+ * AC0[0] = 1; this is a quick way to acquire the approximate vintage of a machine's
+ * microcode.
+ * </PRE>
+ *
+ * TODO: move this to the Ethernet code? It's really a emulator
+ * specific function that is decoded by the Ethernet card.
+ */
+void alto2_cpu_device::f1_early_startf()
+{
+ LOG((LOG_EMU,2," STARTF (BUS is %06o)\n", m_bus));
+ /* TODO: what do we do here? reset the CPU on bit 0? */
+ if (X_BIT(m_bus,16,0)) {
+ LOG((LOG_EMU,2,"**** Software boot feature\n"));
+ soft_reset();
+ } else {
+ LOG((LOG_EMU,2,"**** Ethernet start function\n"));
+ eth_startf();
+ }
+}
+
+/**
+ * @brief branch on odd bus
+ */
+void alto2_cpu_device::f2_late_busodd()
+{
+ UINT16 r = m_bus & 1;
+ LOG((LOG_EMU,2," BUSODD; %sbranch (%#o|%#o)\n", r ? "" : "no ", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief f2_magic late: shift and use T[0] or T[15] for bit 15 or 0
+ */
+void alto2_cpu_device::f2_late_magic()
+{
+ int XC;
+ switch (m_d_f1) {
+ case f1_l_lsh_1: // <-L MLSH 1
+ XC = (m_t >> 15) & 1;
+ m_shifter = (m_l << 1) | XC;
+ LOG((LOG_EMU,2," <-L MLSH 1 (shifer:%06o XC:%o)", m_shifter, XC));
+ break;
+ case f1_l_rsh_1: // <-L MRSH 1
+ XC = (m_t & 1) << 15;
+ m_shifter = (m_l >> 1) | XC;
+ LOG((LOG_EMU,2," <-L MRSH 1 (shifter:%06o XC:%o)", m_shifter, XC));
+ break;
+ case f1_l_lcy_8: // <-L LCY 8
+ m_shifter = (m_l >> 8) | (m_l << 8);
+ break;
+ default: // other
+ m_shifter = m_l;
+ break;
+ }
+}
+
+/**
+ * @brief do novel shifts: modify RESELECT with DstAC = (3 - IR[3-4])
+ */
+void alto2_cpu_device::f2_early_load_dns()
+{
+ X_WRBITS(m_rsel, 5, 3, 4, IR_DstAC(m_emu.ir) ^ 3);
+ LOG((LOG_EMU,2," DNS<-; rsel := DstAC (%#o %s)\n", m_rsel, r_name(m_rsel)));
+}
+
+/**
+ * @brief do novel shifts
+ *
+ * <PRE>
+ * New emulator carry is selected by instruction register
+ * bits CY = IR[10-11]. R register and emulator carry are
+ * loaded only if NL = IR[12] is 0 (NL = no load).
+ * SKIP is set according to SK = IR[13-15].
+ *
+ * CARRY = !m_emu.cy
+ * exorB = IR11 ^ IR10
+ * ORA = !(exorB | CARRY)
+ * = (exorB | CARRY) ^ 1
+ * exorC = ORA ^ !IR11
+ * = ORA ^ IR11 ^ 1
+ * exorD = exorC ^ LALUC0
+ * XC = !(!(DNS & exorD) & !(MAGIC & OUTza))
+ * = (DNS & exorD) | (MAGIC & OUTza)
+ * = exorD, because this is DNS
+ * NEWCARRY = [XC, L(00), L(15), XC] for F1 = no shift, <-L RSH 1, <-L LSH 1, LCY 8
+ * SHZERO = shifter == 0
+ * DCARRY = !((!IR12 & NEWCARRY) | (IR12 & CARRY))
+ * = (((IR12 ^ 1) & NEWCARRY) | (IR12 & CARRY)) ^ 1
+ * DSKIP = !((!NEWCARRY & IR14) | (SHZERO & IR13)) ^ !IR15
+ * = ((((NEWCARRY ^ 1) & IR14) | (SHZERO & IR13)) ^ 1) ^ (IR15 ^ 1)
+ * = (((NEWCARRY ^ 1) & IR14) | (SHZERO & IR13)) ^ IR15
+ * </PRE>
+ */
+void alto2_cpu_device::f2_late_load_dns()
+{
+ UINT8 IR10 = X_BIT(m_emu.ir,16,10);
+ UINT8 IR11 = X_BIT(m_emu.ir,16,11);
+ UINT8 IR12 = X_BIT(m_emu.ir,16,12);
+ UINT8 IR13 = X_BIT(m_emu.ir,16,13);
+ UINT8 IR14 = X_BIT(m_emu.ir,16,14);
+ UINT8 IR15 = X_BIT(m_emu.ir,16,15);
+ UINT8 exorB = IR11 ^ IR10;
+ UINT8 CARRY = m_emu.cy ^ 1;
+ UINT8 ORA = (exorB | CARRY) ^ 1;
+ UINT8 exorC = ORA ^ (IR11 ^ 1);
+ UINT8 exorD = exorC ^ m_laluc0;
+ UINT8 XC = exorD;
+ UINT8 NEWCARRY;
+ UINT8 DCARRY;
+ UINT8 DSKIP;
+ UINT8 SHZERO;
+
+ switch (m_d_f1) {
+ case f1_l_rsh_1: // <-L RSH 1
+ NEWCARRY = m_l & 1;
+ m_shifter = ((m_l >> 1) | (XC << 15)) & 0177777;
+ LOG((LOG_EMU,2," DNS; <-L RSH 1 (shifter:%06o XC:%o NEWCARRY:%o)", m_shifter, XC, NEWCARRY));
+ break;
+ case f1_l_lsh_1: // <-L LSH 1
+ NEWCARRY = (m_l >> 15) & 1;
+ m_shifter = ((m_l << 1) | XC) & 0177777;
+ LOG((LOG_EMU,2," DNS; <-L LSH 1 (shifter:%06o XC:%o NEWCARRY:%o)", m_shifter, XC, NEWCARRY));
+ break;
+ case f1_l_lcy_8: // <-L LCY 8
+ NEWCARRY = XC;
+ m_shifter = (m_l >> 8) | (m_l << 8);
+ LOG((LOG_EMU,2," DNS; (shifter:%06o NEWCARRY:%o)", m_shifter, NEWCARRY));
+ break;
+ default: // other
+ NEWCARRY = XC;
+ m_shifter = m_l;
+ LOG((LOG_EMU,2," DNS; (shifter:%06o NEWCARRY:%o)", m_shifter, NEWCARRY));
+ break;
+ }
+ SHZERO = (m_shifter == 0);
+ DCARRY = (((IR12 ^ 1) & NEWCARRY) | (IR12 & CARRY)) ^ 1;
+ DSKIP = (((NEWCARRY ^ 1) & IR14) | (SHZERO & IR13)) ^ IR15;
+
+ m_emu.cy = DCARRY; // DCARRY is latched as new m_emu.cy
+ m_emu.skip = DSKIP; // DSKIP is latched as new m_emu.skip
+
+ /* !(IR12 & DNS) -> WR' = 0 for the register file */
+ if (!IR12) {
+ m_r[m_rsel] = m_shifter;
+ }
+}
+
+/**
+ * @brief destiantion accu: modify RSELECT with DstAC = (3 - IR[3-4])
+ */
+void alto2_cpu_device::f2_early_acdest()
+{
+ X_WRBITS(m_rsel, 5, 3, 4, IR_DstAC(m_emu.ir) ^ 3);
+ LOG((LOG_EMU,2," ACDEST<-; mux (rsel:%#o %s)\n", m_rsel, r_name(m_rsel)));
+}
+
+#if ALTO2_DEBUG
+void alto2_cpu_device::bitblt_info()
+{
+ static const char *type_name[4] = {"bitmap","complement","and gray","gray"};
+ static const char *oper_name[4] = {"replace","paint","invert","erase"};
+ int bbt = m_r[rsel_ac2];
+ int val = debug_read_mem(bbt);
+
+ LOG((LOG_EMU,3," BITBLT AC1:%06o AC2:%06o\n", m_r[rsel_ac1], m_r[rsel_ac2]));
+ LOG((LOG_EMU,3," function : %06o\n", val));
+ LOG((LOG_EMU,3," src extRAM: %o\n", X_BIT(val,16,10)));
+ LOG((LOG_EMU,3," dst extRAM: %o\n", X_BIT(val,16,11)));
+ LOG((LOG_EMU,3," src type : %o (%s)\n", X_RDBITS(val,16,12,13), type_name[X_RDBITS(val,16,12,13)]));
+ LOG((LOG_EMU,3," operation : %o (%s)\n", X_RDBITS(val,16,14,15), oper_name[X_RDBITS(val,16,14,15)]));
+ val = debug_read_mem(bbt+1);
+ LOG((LOG_EMU,3," unused AC2: %06o (%d)\n", val, val));
+ val = debug_read_mem(bbt+2);
+ LOG((LOG_EMU,3," DBCA : %06o (%d)\n", val, val));
+ val = debug_read_mem(bbt+3);
+ LOG((LOG_EMU,3," DBMR : %06o (%d words)\n", val, val));
+ val = debug_read_mem(bbt+4);
+ LOG((LOG_EMU,3," DLX : %06o (%d bits)\n", val, val));
+ val = debug_read_mem(bbt+5);
+ LOG((LOG_EMU,3," DTY : %06o (%d scanlines)\n", val, val));
+ val = debug_read_mem(bbt+6);
+ LOG((LOG_EMU,3," DW : %06o (%d bits)\n", val, val));
+ val = debug_read_mem(bbt+7);
+ LOG((LOG_EMU,3," DH : %06o (%d scanlines)\n", val, val));
+ val = debug_read_mem(bbt+8);
+ LOG((LOG_EMU,3," SBCA : %06o (%d)\n", val, val));
+ val = debug_read_mem(bbt+9);
+ LOG((LOG_EMU,3," SBMR : %06o (%d words)\n", val, val));
+ val = debug_read_mem(bbt+10);
+ LOG((LOG_EMU,3," SLX : %06o (%d bits)\n", val, val));
+ val = debug_read_mem(bbt+11);
+ LOG((LOG_EMU,3," STY : %06o (%d scanlines)\n", val, val));
+ LOG((LOG_EMU,3," GRAY0-3 : %06o %06o %06o %06o\n",
+ debug_read_mem(bbt+12), debug_read_mem(bbt+13),
+ debug_read_mem(bbt+14), debug_read_mem(bbt+15)));
+}
+#endif /* DEBUG */
+
+/**
+ * @brief load instruction register IR and branch on IR[0,5-7]
+ *
+ * Loading the IR clears the skip latch.
+ */
+void alto2_cpu_device::f2_late_load_ir()
+{
+ UINT16 r = (X_BIT(m_bus,16,0) << 3) | X_RDBITS(m_bus,16,5,7);
+
+#if ALTO2_DEBUG
+ /* special logging of some opcodes */
+ switch (m_bus) {
+ case op_CYCLE:
+ LOG((LOG_EMU,3," CYCLE AC0:#o\n", m_r[rsel_ac0]));
+ break;
+ case op_CYCLE + 1: case op_CYCLE + 2: case op_CYCLE + 3: case op_CYCLE + 4:
+ case op_CYCLE + 5: case op_CYCLE + 6: case op_CYCLE + 7: case op_CYCLE + 8:
+ case op_CYCLE + 9: case op_CYCLE +10: case op_CYCLE +11: case op_CYCLE +12:
+ case op_CYCLE +13: case op_CYCLE +14: case op_CYCLE +15:
+ LOG((LOG_EMU,3," CYCLE %#o\n", m_bus - op_CYCLE));
+ break;
+ case op_BLT:
+ LOG((LOG_EMU,3," BLT dst:%#o src:%#o size:%#o\n",
+ (m_r[rsel_ac1] + m_r[rsel_ac3] + 1) & 0177777,
+ (m_r[rsel_ac0] + 1) & 017777, -m_r[rsel_ac3] & 0177777));
+ break;
+ case op_BLKS:
+ LOG((LOG_EMU,3," BLKS dst:%#o val:%#o size:%#o\n",
+ (m_r[rsel_ac1] + m_r[rsel_ac3] + 1) & 0177777,
+ m_r[rsel_ac0], -m_r[rsel_ac3] & 0177777));
+ break;
+ case op_DIAGNOSE1:
+ LOG((LOG_EMU,3," DIAGNOSE1 AC0:%06o AC1:%06o AC2:%06o AC3:%06o\n",
+ m_r[rsel_ac0], m_r[rsel_ac1],
+ m_r[rsel_ac2], m_r[rsel_ac3]));
+ break;
+ case op_DIAGNOSE2:
+ LOG((LOG_EMU,3," DIAGNOSE2 AC0:%06o AC1:%06o AC2:%06o AC3:%06o\n",
+ m_r[rsel_ac0], m_r[rsel_ac1],
+ m_r[rsel_ac2], m_r[rsel_ac3]));
+ break;
+ case op_BITBLT:
+ bitblt_info();
+ break;
+ case op_RDRAM:
+ LOG((LOG_EMU,3," RDRAM addr:%#o\n", m_r[rsel_ac1]));
+ break;
+ case op_WRTRAM:
+ LOG((LOG_EMU,3," WRTAM addr:%#o upper:%06o lower:%06o\n", m_r[rsel_ac1], m_r[rsel_ac0], m_r[rsel_ac3]));
+ break;
+ case op_JMPRAM:
+ LOG((LOG_EMU,3," JMPRAM addr:%#o\n", m_r[rsel_ac1]));
+ break;
+ case op_XMLDA:
+ LOG((LOG_EMU,3," XMLDA AC0 = [bank:%o AC1:#o]\n", m_bank_reg[m_task] & 3, m_r[rsel_ac1]));
+ break;
+ case op_XMSTA:
+ LOG((LOG_EMU,3," XMSTA [bank:%o AC1:#o] = AC0 (%#o)\n", m_bank_reg[m_task] & 3, m_r[rsel_ac1], m_r[rsel_ac0]));
+ break;
+ }
+#endif
+ m_emu.ir = m_bus;
+ m_emu.skip = 0;
+ m_next2 |= r;
+}
+
+
+/**
+ * @brief branch on: arithmetic IR_SH, others PROM ctl2k_u3[IR[1-7]]
+ */
+void alto2_cpu_device::f2_late_idisp()
+{
+ UINT16 r;
+
+ if (IR_ARITH(m_emu.ir)) {
+ /* 1xxxxxxxxxxxxxxx */
+ r = IR_SH(m_emu.ir) ^ 3; /* complement of SH */
+ LOG((LOG_EMU,2," IDISP<-; branch on SH^3 (%#o|%#o)\n", m_next2, r));
+ } else {
+ int addr = CTL2K_U3(f2_emu_idisp) + X_RDBITS(m_emu.ir,16,1,7);
+ /* 0???????xxxxxxxx */
+ r = m_ctl2k_u3[addr];
+ LOG((LOG_EMU,2," IDISP<-; IR (%#o) branch on PROM ctl2k_u3[%03o] (%#o|%#o)\n", m_emu.ir, addr, m_next2, r));
+ }
+ m_next2 |= r;
+}
+
+/**
+ * @brief source accu: modify RSELECT with SrcAC = (3 - IR[1-2])
+ */
+void alto2_cpu_device::f2_early_acsource()
+{
+ X_WRBITS(m_rsel, 5, 3, 4, IR_SrcAC(m_emu.ir) ^ 3);
+ LOG((LOG_EMU,2," <-ACSOURCE; rsel := SrcAC (%#o %s)\n", m_rsel, r_name(m_rsel)));
+}
+
+/**
+ * @brief branch on: arithmetic IR_SH, others PROM ctl2k_u3[IR[1-7]]
+ */
+void alto2_cpu_device::f2_late_acsource()
+{
+ UINT16 r;
+
+ if (IR_ARITH(m_emu.ir)) {
+ /* 1xxxxxxxxxxxxxxx */
+ r = IR_SH(m_emu.ir) ^ 3; /* complement of SH */
+ LOG((LOG_EMU,2," <-ACSOURCE; branch on SH^3 (%#o|%#o)\n", m_next2, r));
+ } else {
+ int addr = CTL2K_U3(f2_emu_acsource) + X_RDBITS(m_emu.ir,16,1,7);
+ /* 0???????xxxxxxxx */
+ r = m_ctl2k_u3[addr];
+ LOG((LOG_EMU,2," <-ACSOURCE; branch on PROM ctl2k_u3[%03o] (%#o|%#o)\n", addr, m_next2, r));
+ }
+ m_next2 |= r;
+}
+
+void alto2_cpu_device::init_emu(int task)
+{
+ memset(&m_emu, 0, sizeof(m_emu));
+ save_item(NAME(m_emu.ir));
+ save_item(NAME(m_emu.skip));
+ save_item(NAME(m_emu.cy));
+
+ init_ram(task);
+
+ set_bs(task, bs_emu_read_sreg, &alto2_cpu_device::bs_early_read_sreg, 0);
+ set_bs(task, bs_emu_load_sreg, &alto2_cpu_device::bs_early_load_sreg, &alto2_cpu_device::bs_late_load_sreg);
+ set_bs(task, bs_disp, &alto2_cpu_device::bs_early_emu_disp, 0);
+
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_emu_block, 0); // catch the emulator task trying to block (wrong branch)
+ set_f1(task, f1_emu_swmode, 0, &alto2_cpu_device::f1_late_swmode);
+ set_f1(task, f1_emu_wrtram, 0, &alto2_cpu_device::f1_late_wrtram);
+ set_f1(task, f1_emu_rdram, 0, &alto2_cpu_device::f1_late_rdram);
+ set_f1(task, f1_emu_load_rmr, 0, &alto2_cpu_device::f1_late_emu_load_rmr);
+ set_f1(task, f1_task_14, 0, 0); // F1 014 is undefined (?)
+ set_f1(task, f1_emu_load_esrb, 0, &alto2_cpu_device::f1_late_emu_load_esrb);
+ set_f1(task, f1_emu_rsnf, &alto2_cpu_device::f1_early_rsnf, 0);
+ set_f1(task, f1_emu_startf, &alto2_cpu_device::f1_early_startf, 0);
+
+ set_f2(task, f2_emu_busodd, 0, &alto2_cpu_device::f2_late_busodd);
+ set_f2(task, f2_emu_magic, 0, &alto2_cpu_device::f2_late_magic);
+ set_f2(task, f2_emu_load_dns, &alto2_cpu_device::f2_early_load_dns, &alto2_cpu_device::f2_late_load_dns);
+ set_f2(task, f2_emu_acdest, &alto2_cpu_device::f2_early_acdest, 0);
+ set_f2(task, f2_emu_load_ir, 0, &alto2_cpu_device::f2_late_load_ir);
+ set_f2(task, f2_emu_idisp, 0, &alto2_cpu_device::f2_late_idisp);
+ set_f2(task, f2_emu_acsource, &alto2_cpu_device::f2_early_acsource, &alto2_cpu_device::f2_late_acsource);
+}
+
+void alto2_cpu_device::exit_emu()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_emu()
+{
+ m_emu.ir = 0;
+ m_emu.skip = 0;
+ m_emu.cy = 0;
+}
diff --git a/src/emu/cpu/alto2/a2emu.h b/src/emu/cpu/alto2/a2emu.h
new file mode 100644
index 00000000000..d119e6538f4
--- /dev/null
+++ b/src/emu/cpu/alto2/a2emu.h
@@ -0,0 +1,71 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII emulator task (EMU)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2EMU_H_
+#define _A2EMU_H_
+
+//! BUS source for emulator task
+enum {
+ bs_emu_read_sreg = bs_task_3, //!< bus source: read S register
+ bs_emu_load_sreg = bs_task_4 //!< bus source: load S register from BUS
+};
+
+//! F1 functions for emulator task
+enum {
+ f1_emu_swmode = f1_task_10, //!< f1 (1000): switch mode; branch to ROM/RAM microcode
+ f1_emu_wrtram = f1_task_11, //!< f1 (1001): write microcode RAM cycle
+ f1_emu_rdram = f1_task_12, //!< f1 (1010): read microcode RAM cycle
+ f1_emu_load_rmr = f1_task_13, //!< f1 (1011): load reset mode register
+ //!< f1 (1100): undefined
+ f1_emu_load_esrb = f1_task_15, //!< f1 (1101): load extended S register bank
+ f1_emu_rsnf = f1_task_16, //!< f1 (1110): read serial number (Ethernet ID)
+ f1_emu_startf = f1_task_17 //!< f1 (1111): start I/O hardware (Ethernet)
+};
+
+//! F2 functions for emulator task
+enum {
+ f2_emu_busodd = f2_task_10, //!< f2 (1000): branch on bus odd
+ f2_emu_magic = f2_task_11, //!< f2 (1001): magic shifter (MRSH 1: shifter[15]=T[0], MLSH 1: shifter[015])
+ f2_emu_load_dns = f2_task_12, //!< f2 (1010): do novel shift (RSH 1: shifter[15]=XC, LSH 1: shifer[0]=XC)
+ f2_emu_acdest = f2_task_13, //!< f2 (1011): destination accu
+ f2_emu_load_ir = f2_task_14, //!< f2 (1100): load instruction register and branch
+ f2_emu_idisp = f2_task_15, //!< f2 (1101): load instruction displacement and branch
+ f2_emu_acsource = f2_task_16 //!< f2 (1110): source accu
+ //!< f2 (1111): undefined
+};
+
+struct {
+ UINT16 ir; //!< emulator instruction register
+ UINT8 skip; //!< emulator skip
+ UINT8 cy; //!< emulator carry
+} m_emu;
+void bs_early_emu_disp(); //!< bus source: drive bus by IR[8-15], possibly sign extended
+void f1_early_emu_block(); //!< F1 func: block task
+void f1_late_emu_load_rmr(); //!< F1 func: load the reset mode register
+void f1_late_emu_load_esrb(); //!< F1 func: load the extended S register bank from BUS[12-14]
+void f1_early_rsnf(); //!< F1 func: drive the bus from the Ethernet node ID
+void f1_early_startf(); //!< F1 func: defines commands for for I/O hardware, including Ethernet
+void f2_late_busodd(); //!< F2 func: branch on odd bus
+void f2_late_magic(); //!< F2 func: shift and use T
+void f2_early_load_dns(); //!< F2 func: modify RESELECT with DstAC = (3 - IR[3-4])
+void f2_late_load_dns(); //!< F2 func: do novel shifts
+void f2_early_acdest(); //!< F2 func: modify RSELECT with DstAC = (3 - IR[3-4])
+void bitblt_info(); //!< debug bitblt opcode
+void f2_late_load_ir(); //!< F2 func: load instruction register IR and branch on IR[0,5-7]
+void f2_late_idisp(); //!< F2 func: branch on: arithmetic IR_SH, others PROM ctl2k_u3[IR[1-7]]
+void f2_early_acsource(); //!< F2 func: modify RSELECT with SrcAC = (3 - IR[1-2])
+void f2_late_acsource(); //!< F2 func: branch on arithmetic IR_SH, others PROM ctl2k_u3[IR[1-7]]
+void init_emu(int task = task_emu); //!< initialize the emulator task
+void exit_emu(); //!< deinitialize the emulator task
+void reset_emu(); //!< reset the emulator task
+#endif // _A2EMU_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2ether.c b/src/emu/cpu/alto2/a2ether.c
new file mode 100644
index 00000000000..d0b2fc92686
--- /dev/null
+++ b/src/emu/cpu/alto2/a2ether.c
@@ -0,0 +1,1382 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII ethernet task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+#include "a2roms.h"
+
+#define DEBUG_PACKETS 1
+
+
+/**
+ * @brief BPROMs P3601-1; 256x4; enet.a41 "PE1" and enet.a42 "PE2"
+ *
+ * Phase encoder
+ *
+ * a41: P3601-1; 256x4; "PE1"
+ * a42: P3601-1; 256x4; "PE2"
+ *
+ * PE1/PE2 inputs
+ * ----------------
+ * A0 (5) OUTGO
+ * A1 (6) XDATA
+ * A2 (7) OSDATAG
+ * A3 (4) XCLOCK
+ * A4 (3) OCNTR0
+ * A5 (2) OCNTR1
+ * A6 (1) OCNTR2
+ * A7 (15) OCNTR3
+ *
+ * PE1 outputs
+ * ----------------
+ * D0 (12) OCNTR0
+ * D1 (11) OCNTR1
+ * D2 (10) OCNTR2
+ * D3 (9) OCNTR3
+ *
+ * PE2 outputs
+ * ----------------
+ * D0 (12) n.c.
+ * D1 (11) to OSLOAD flip flop J and K'
+ * D2 (10) XDATA
+ * D3 (9) XCLOCK
+ */
+static const prom_load_t pl_enet_a41 =
+{ /* P3601 256x4 BPROM; Ethernet phase encoder 1 "PE1" */
+ "enet.a41",
+ 0,
+ "d5de8d86",
+ "c134a4c898c73863124361a9b0218f7a7f00082a",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 0,
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+static const prom_load_t pl_enet_a42 =
+{ /* P3601 256x4 BPROM; Ethernet phase encoder 2 "PE2" */
+ "enet.a42",
+ 0,
+ "9d5c81bd",
+ "ac7e63332a3dad0bef7cd0349b24e156a96a4bf0",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 0,
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+/**
+ * @brief BPROM; P3601-1; 265x4 enet.a49 "AFIFO"
+ *
+ * FIFO control
+ *
+ * a49: P3601-1; 256x4; "AFIFO"
+ *
+ * inputs
+ * ----------------
+ * A0 (5) fifo_wr[0]
+ * A1 (6) fifo_wr[1]
+ * A2 (7) fifo_wr[2]
+ * A3 (4) fifo_wr[3]
+ * A4 (3) fifo_rd[0]
+ * A5 (2) fifo_rd[1]
+ * A6 (1) fifo_rd[2]
+ * A7 (15) fifo_rd[3]
+ *
+ * outputs active low
+ * ----------------------------
+ * D0 (12) BE' (buffer empty)
+ * D1 (11) BNE' (buffer nearly empty)
+ * D2 (10) BNNE' (buffer next nearly empty)
+ * D3 (9) BF' (buffer full)
+ */
+static const prom_load_t pl_enet_a49 =
+{ /* P3601 256x4 BPROM; Ethernet FIFO control "AFIFO" */
+ "enet.a49",
+ 0,
+ "4d2dcdb2",
+ "583327a7d70cd02702c941c0e43c1e9408ff7fd0",
+ /* size */ 0400,
+ /* amap */ AMAP_REVERSE_0_7, // reverse address lines A0-A7
+ /* axor */ 0,
+ /* dxor */ 0,
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+#define GET_ETH_WLF(st) X_BIT(st,16,4) //!< hardware status: write latch full/filled (? set by EODFCT)
+#define PUT_ETH_WLF(st,val) X_WRBITS(st,16,4,4,val)
+
+#define GET_ETH_OEOT(st) X_BIT(st,16,5) //!< hardware status: output end of transmission (set by EEFCT)
+#define PUT_ETH_OEOT(st,val) X_WRBITS(st,16,5,5,val)
+
+#define GET_ETH_IGONE(st) X_BIT(st,16,6) //!< hardware status: input gone
+#define PUT_ETH_IGONE(st,val) X_WRBITS(st,16,6,6,val)
+
+#define GET_ETH_IBUSY(st) X_BIT(st,16,7) //!< hardware status: input busy (set by EISFCT, bit isn't visible to microcode)
+#define PUT_ETH_IBUSY(st,val) X_WRBITS(st,16,7,7,val)
+
+#define GET_ETH_OGONE(st) X_BIT(st,16,8) //!< hardware status: output gone
+#define PUT_ETH_OGONE(st,val) X_WRBITS(st,16,8,8,val)
+
+#define GET_ETH_OBUSY(st) X_BIT(st,16,9) //!< hardware status: output busy (set by EOSFCT, bit isn't visible to microcode)
+#define PUT_ETH_OBUSY(st,val) X_WRBITS(st,16,9,9,val)
+
+#define GET_ETH_IDL(st) X_BIT(st,16,10) //!< hardware status: input data late
+#define PUT_ETH_IDL(st,val) X_WRBITS(st,16,10,10,val)
+
+#define GET_ETH_COLL(st) X_BIT(st,16,11) //!< hardware status: collision
+#define PUT_ETH_COLL(st,val) X_WRBITS(st,16,11,11,val)
+
+#define GET_ETH_CRC(st) X_BIT(st,16,12) //!< hardware status: CRC error
+#define PUT_ETH_CRC(st,val) X_WRBITS(st,16,12,12,val)
+
+#define GET_ETH_ICMD(st) X_BIT(st,16,13) //!< hardware status: input command (set from BUS[14] on SIO, reset by EPFCT)
+#define PUT_ETH_ICMD(st,val) X_WRBITS(st,16,13,13,val)
+
+#define GET_ETH_OCMD(st) X_BIT(st,16,14) //!< hardware status: output command (set from BUS[15] on SIO, reset by EPFCT)
+#define PUT_ETH_OCMD(st,val) X_WRBITS(st,16,14,14,val)
+
+#define GET_ETH_IT(st) X_BIT(st,16,15) //!< hardware status: IT flip flop & ISRFULL'
+#define PUT_ETH_IT(st,val) X_WRBITS(st,16,15,15,val)
+
+#define BE(a49) ((a49 & ether_a49_BE) ? 1 : 0) //! buffer empty
+#define BNE(a49) ((a49 & ether_a49_BNE) ? 1 : 0) //! buffer next empty
+#define BNNE(a49) ((a49 & ether_a49_BNNE) ? 1 : 0) //! buffer next next empty
+#define BF(a49) ((a49 & ether_a49_BF) ? 1 : 0) //! buffer full
+
+#define BREATHLEN ALTO2_ETHER_PACKET_SIZE //!< ethernet packet length
+#define BREATHADDR (0377<<8) //!< destination (0377) and source (0000)
+#define BREATHTYPE 0602 //!< ethernet packet type
+static const UINT16 breath_of_life_data[BREATHLEN] =
+{
+ BREATHADDR, /* 3MB destination and source */
+ BREATHTYPE, /* ether packet type */
+ /* the rest is the contents of a breath of life packet.
+ * see <altosource>etherboot.dm (etherboot.asm) for the
+ * Alto assembly code.
+ */
+ 0022574, 0100000, 0040437, 0102000, 0034431, 0164000,
+ 0061005, 0102460, 0024567, 0034572, 0061006, 0024565, 0034570, 0061006,
+ 0024564, 0034566, 0061006, 0020565, 0034565, 0061005, 0125220, 0046573,
+ 0020576, 0061004, 0123400, 0030551, 0041211, 0004416, 0000000, 0001000,
+ 0000026, 0000244, 0000000, 0000000, 0000000, 0000000, 0000004, 0000000,
+ 0000000, 0000020, 0177777, 0055210, 0025400, 0107000, 0045400, 0041411,
+ 0020547, 0041207, 0020544, 0061004, 0006531, 0034517, 0030544, 0051606,
+ 0020510, 0041605, 0042526, 0102460, 0041601, 0020530, 0061004, 0021601,
+ 0101014, 0000414, 0061020, 0014737, 0000773, 0014517, 0000754, 0020517,
+ 0061004, 0030402, 0002402, 0000000, 0000732, 0034514, 0162414, 0000746,
+ 0021001, 0024511, 0106414, 0000742, 0021003, 0163400, 0035005, 0024501,
+ 0106415, 0175014, 0000733, 0021000, 0042465, 0034457, 0056445, 0055775,
+ 0055776, 0101300, 0041400, 0020467, 0041401, 0020432, 0041402, 0121400,
+ 0041403, 0021006, 0041411, 0021007, 0041412, 0021010, 0041413, 0021011,
+ 0041406, 0021012, 0041407, 0021013, 0041410, 0015414, 0006427, 0012434,
+ 0006426, 0020421, 0024437, 0134000, 0030417, 0002422, 0177035, 0000026,
+ 0000415, 0000427, 0000567, 0000607, 0000777, 0177751, 0177641, 0177600,
+ 0000225, 0177624, 0001013, 0000764, 0000431, 0000712, 0000634, 0000735,
+ 0000611, 0000567, 0000564, 0000566, 0000036, 0000002, 0000003, 0000015,
+ 0000030, 0000377, 0001000, 0177764, 0000436, 0054731, 0050750, 0020753,
+ 0040745, 0102460, 0040737, 0020762, 0061004, 0020734, 0105304, 0000406,
+ 0020743, 0101014, 0014741, 0000772, 0002712, 0034754, 0167700, 0116415,
+ 0024752, 0021001, 0106414, 0000754, 0021000, 0024703, 0106414, 0000750,
+ 0021003, 0163400, 0024736, 0106405, 0000404, 0121400, 0101404, 0000740,
+ 0044714, 0021005, 0042732, 0024664, 0122405, 0000404, 0101405, 0004404,
+ 0000727, 0010656, 0034654, 0024403, 0120500, 0101404, 0000777, 0040662,
+ 0040664, 0040664, 0102520, 0061004, 0020655, 0101015, 0000776, 0106415,
+ 0001400, 0014634, 0000761, 0020673, 0061004, 0000400, 0061005, 0102000,
+ 0143000, 0034672, 0024667, 0166400, 0061005, 0004670, 0020663, 0034664,
+ 0164000, 0147000, 0061005, 0024762, 0132414, 0133000, 0020636, 0034416,
+ 0101015, 0156415, 0131001, 0000754, 0024643, 0044625, 0101015, 0000750,
+ 0014623, 0004644, 0020634, 0061004, 0002000, 0176764, 0001401, 0041002
+};
+
+#if DEBUG_PACKETS
+static void dump_ascii(const UINT16 *src, size_t size)
+{
+ logerror(" [");
+ for (size_t offs = 0; offs < size; offs++) {
+ char ch1 = src[offs] / 256;
+ char ch2 = src[offs] % 256;
+ logerror("%c", ch1 < 32 || ch1 > 126 ? '.' : ch1);
+ logerror("%c", ch2 < 32 || ch2 > 126 ? '.' : ch2);
+ }
+ logerror("]\n");
+}
+
+static void dump_packet(const char* name, const UINT16 *src, size_t addr, size_t size)
+{
+ size_t offs;
+ for (offs = 0; offs < size; offs++) {
+ UINT16 word = src[offs];
+ if (offs % 8) {
+ logerror(" %06o", word);
+ } else {
+ if (offs > 0)
+ dump_ascii(&src[offs-8], 8);
+ logerror("%s\t%05o: %06o", name, static_cast<unsigned>(addr + offs), word);
+ }
+ }
+ if (offs % 8) {
+ dump_ascii(&src[offs - (offs % 8)], offs % 8);
+ } else if (offs > 0) {
+ dump_ascii(&src[offs - 8], 8);
+ }
+}
+#endif
+
+/**
+ * @brief check for the various reasons to wakeup the ethernet task
+ */
+void alto2_cpu_device::eth_wakeup()
+{
+ register int st = m_eth.status;
+ LOG((LOG_ETH,0,"IBUSY=%d OBUSY=%d ", GET_ETH_IBUSY(st), GET_ETH_OBUSY(st)));
+ UINT8 busy = GET_ETH_IBUSY(st) | GET_ETH_OBUSY(st);
+ if (0 == busy) {
+ // if not busy, reset the FIFO read and write counters
+ m_eth.fifo_rd = 0;
+ m_eth.fifo_wr = 0;
+ }
+
+ /*
+ * POST conditions to wakeup the Ether task:
+ * input data late
+ * output command
+ * input command
+ * output gone
+ * input gone
+ */
+ if (GET_ETH_IDL(st)) {
+ LOG((LOG_ETH,0,"POST (input data late)\n"));
+ m_task_wakeup |= 1 << task_ether;
+ return;
+ }
+ if (GET_ETH_OCMD(st)) {
+ LOG((LOG_ETH,0,"POST (output command)\n"));
+ m_task_wakeup |= 1 << task_ether;
+ return;
+ }
+ if (GET_ETH_ICMD(st)) {
+ LOG((LOG_ETH,0,"POST (input command)\n"));
+ m_task_wakeup |= 1 << task_ether;
+ return;
+ }
+ if (GET_ETH_OGONE(st)) {
+ LOG((LOG_ETH,0,"POST (output gone)\n"));
+ m_task_wakeup |= 1 << task_ether;
+ return;
+ }
+ if (GET_ETH_IGONE(st)) {
+ LOG((LOG_ETH,0,"POST (input gone)\n"));
+ m_task_wakeup |= 1 << task_ether;
+ return;
+ }
+
+ /**
+ * IDR (input data ready) conditions to wakeup the Ether task
+ * signal meaining
+ * --------------------------------------
+ * IBUSY input busy
+ * BNNE buffer next nearly empty
+ * BNE buffer nearly empty
+ * ETAC ether task active
+ *
+ ************************************************************
+ * +----+
+ * BNE' >----|NAND| (i1) +----+
+ * | o-------|NAND| (i2) +----+
+ * ETAC' >----| | | o-------|NAND|
+ * +----+ +---| | | o-----> IDR'
+ * | +----+ +---| |
+ * +---+ | | +----+
+ * BNNE' >----|INVo----+ |
+ * +---+ |
+ * |
+ * IBUSY >--------------------------+
+ *
+ ************************************************************
+ */
+ UINT8 a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
+ UINT8 ETAC = m_task == task_ether ? 0 : 1;
+ UINT8 i1 = ~(BNE(a49) & ETAC);
+ UINT8 i2 = ~(~BNNE(a49) & i1);
+ UINT8 IDR = ~(GET_ETH_IBUSY(st) & i2);
+ if (0 == IDR) {
+ m_task_wakeup |= 1 << task_ether;
+ LOG((LOG_ETH,0,"IDR (input data ready)\n"));
+ return;
+ }
+
+ /**
+ * ODR (output data ready) conditions to wakeup the Ether task
+ * signal meaining
+ * --------------------------------------
+ * WLF write latch full(?)
+ * BF buffer (FIFO) full
+ * OEOT output end of transmission
+ * OBUSY output busy
+ ************************************************************
+ * +----+
+ * WLF' >----|NAND| (o1) +----+
+ * | o---------|NAND|
+ * BF' >----| | | |
+ * +----+ +------| o----> ODR'
+ * | | |
+ * | +---| |
+ * OEOT' >------------+ | +----+
+ * |
+ * |
+ * OBUSY >---------------+
+ *
+ ************************************************************
+ */
+ UINT8 o1 = ~(~GET_ETH_WLF(st) & BF(a49));
+ UINT8 ODR = ~(GET_ETH_OBUSY(st) & ~GET_ETH_OEOT(st) & o1);
+ if (0 == ODR) {
+ m_task_wakeup |= 1 << task_ether;
+ LOG((LOG_ETH,0,"ODR (output data ready)\n"));
+ return;
+ }
+
+ /*
+ * EWFCT (ether wake function) conditions to wakeup the Ether task:
+ * EWFCT flip flop set by the F1 EWFCT
+ * The task is activated by the display code together with the
+ * next wakeup of the memory refresh task (MRT).
+ */
+ if (m_ewfct) {
+ m_task_wakeup |= 1 << task_ether;
+ LOG((LOG_ETH,0,"EWFCT (ether wake function)\n"));
+ return;
+ }
+
+ // otherwise no more wakeups for the ether task
+ LOG((LOG_ETH,0,"stop wake\n"));
+ m_task_wakeup &= ~(1 << task_ether);
+}
+
+/**
+ * @brief F9401 CRC checker
+ * <PRE>
+ *
+ * The F9401 looks similiar to the SN74F401. However, in the schematics
+ * there is a connection from pin 9 (labeled D9) to pin 2 (labeled Q8).
+ * See below for the difference:
+ *
+ * SN74F401 F9401
+ * +---+-+---+ +---+-+---+
+ * | +-+ | | +-+ |
+ * CP' -|1 14|- Vcc CLK' -|1 14|- Vcc
+ * | | | |
+ * P' -|2 13|- ER P' -|2 13|- CRCZ'
+ * | | | |
+ * S0 -|3 12|- Q Z -|3 12|- CRCDATA
+ * | | | |
+ * MR -|4 11|- D MR -|4 11|- SDI
+ * | | | |
+ * S1 -|5 10|- CWE Y -|5 10|- SR
+ * | | | |
+ * NC -|6 9|- NC D1 -|6 9|- D9
+ * | | | |
+ * GND -|7 8|- S2 GND -|7 8|- X
+ * | | | |
+ * +---------+ +---------+
+ *
+ * Functional description (SN74F401)
+ *
+ * The 'F401 is a 16-bit programmable device which operates on serial data
+ * streams and provides a means of detecting transmission errors. Cyclic
+ * encoding and decoding schemes for error detection are based on polynomial
+ * manipulation in modulo arithmetic. For encoding, the data stream (message
+ * polynomial) is divided by a selected polynomial. This division results
+ * in a remainder which is appended to the message as check bits. For error
+ * checking, the bit stream containing both data and check bits is divided
+ * by the same selected polynomial. If there are no detectable errors, this
+ * division results in a zero remainder. Although it is possible to choose
+ * many generating polynomials of a given degree, standards exist that
+ * specify a small number of useful polynomials. The 'F401 implements the
+ * polynomials listed in Tabel I by applying the appropriate logic levels
+ * to the select pins S0, S1 and S2.
+ *
+ * Teh 'F401 consists of a 16-bit register, a Read Only Memory (ROM) and
+ * associated control circuitry as shown in the block diagram. The
+ * polynomial control code presented at inputs S0, S1 and S2 is decoded
+ * by the ROM, selecting the desired polynomial by establishing shift
+ * mode operation on the register with Exclusive OR gates at appropriate
+ * inputs. To generate check bits, the data stream is entered via the
+ * Data inputs (D), using the HIGH-to-LOW transition of the Clock input
+ * (CP'). This data is gated with the most significant output (Q) of
+ * the register, and controls the Exclusive OR gates (Figure 1). The
+ * Check Word Enable (CWE) must be held HIGH while the data is being
+ * entered. After the last data bit is entered, the CWE is brought LOW
+ * and the check bits are shifted out of the register and appended to
+ * the data bits using external gating (Figure 2).
+ *
+ * To check an incoming message for errors, both the data and check bits
+ * are entered through the D input with the CWE input held HIGH. The
+ * 'F401 is not in the data path, but only monitors the message. The
+ * Error output becomes valid after the last check bit has been entered
+ * into the 'F401 by a HIGH-to-LOW transition of CP'. If no detectable
+ * errors have occured during the transmission, the resultant internal
+ * register bits are all LOW and the Error Output (ER) is LOW.
+ * If a detectable error has occured, ER is HIGH.
+ *
+ * A HIGH on the Master Reset input (MR) asynchronously clears the
+ * register. A LOW on the Preset input (P') asynchronously sets the
+ * entire register if the control code inputs specify a 16-bit
+ * polynomial; in the case of 12- or 8-bit check polynomials only the
+ * most significant 12 or 8 register bits are set and the remaining
+ * bits are cleared.
+ *
+ * [Table I]
+ *
+ * S2 S1 S0 polynomial remarks
+ * ----------------------------------------------------------------
+ * L L L x^16+x^15+x^2+1 CRC16
+ * L L H x^16+x^14+x+1 CRC16 reverse
+ * L H L x^16+x^15+x^13+x^7+x^4+x^2+x+1 -/-
+ * L H H x^12+x^11+x^3+x^2+x+1 CRC-12
+ * H L L x^8+x^7+x^5+x^4+x+1 -/-
+ * H L H x^8+1 LRC-8
+ * H H L X^16+x^12+x^5+1 CRC-CCITT
+ * H H H X^16+x^11+x^4+1 CRC-CCITT reverse
+ *
+ * </PRE>
+ * The Alto ethernet interface seems to be using the last one of the polynomials,
+ * or perhaps something entirely different?
+ *
+ * TODO: verify polynomial generator; build a lookup table to make it faster.
+ *
+ * @param crc previous CRC value
+ * @param data 16 bit data
+ * @return new CRC value after 16 bits
+ */
+UINT32 f9401_7(UINT32 crc, UINT32 data)
+{
+ static const UINT32 XOR = (1 << 10) | (1 << 3) | (1 << 0);
+ crc ^= data;
+ for (int i = 0; i < 16; i++)
+ crc = (crc >> 1) ^ ((crc & 1) ? XOR : 0);
+ return crc & 0177777;
+}
+
+/**
+ * @brief HACK: pull the next word from the breath_of_life_data in the fifo
+ *
+ * This is probably lacking the updates to one or more of
+ * the status flip flops.
+ */
+void alto2_cpu_device::rx_breath_of_life(void* ptr, INT32 arg)
+{
+ UINT32 data;
+
+ if (arg == 0) {
+ // on the first word set the IBUSY flip flop
+ PUT_ETH_IBUSY(m_eth.status, 1);
+ m_eth.rx_count = 0;
+ }
+
+ if (arg >= BREATHLEN) {
+ // CRC after the data
+ data = m_eth.rx_crc;
+ arg++;
+ } else {
+ // next data word
+ data = breath_of_life_data[arg++];
+ }
+ m_eth.rx_crc = f9401_7(m_eth.rx_crc, data);
+ m_eth.fifo[m_eth.fifo_wr] = data;
+ m_eth.fifo_wr = (m_eth.fifo_wr + 1) % ALTO2_ETHER_FIFO_SIZE;
+
+ PUT_ETH_IT(m_eth.status, 1); // set IT (input shift register full ...)?
+
+ UINT8 a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
+ if (0 == BF(a49))
+ PUT_ETH_IDL(m_eth.status, 1); // fifo is overrun: set input data late flip flop
+
+ if (arg > BREATHLEN) {
+ /*
+ * TODO: if data comes from some other source,
+ * compare our CRC with the next word received
+ * and set the CRC error flag if they differ.
+ */
+ m_eth.rx_crc = 0;
+ PUT_ETH_IGONE(m_eth.status, 1); // set the IGONE flip flop
+ m_eth.rx_timer->adjust(attotime::from_seconds(m_eth.breath_of_life), 0);
+ } else {
+ // receive at a rate of 5.44us per word
+ m_eth.rx_timer->adjust(attotime::from_usec(5.44), arg);
+ }
+ eth_wakeup();
+}
+
+/**
+ * @brief transmit data from the FIFO to <nirvana for now>
+ *
+ * @param ptr unused pointer
+ * @param arg word count if >= 0, -1 if CRC is to be transmitted (last word)
+ */
+void alto2_cpu_device::tx_packet(void* ptr, INT32 arg)
+{
+ UINT32 data;
+
+ // the last word sent is the CRC
+ if (-1 == arg) {
+ m_eth.tx_timer->reset();
+ LOG((LOG_ETH,0," CRC:%06o\n", m_eth.tx_crc));
+ // TODO: send the CRC as final word of the packet
+ m_eth.tx_crc = 0;
+ PUT_ETH_OGONE(m_eth.status, 1); // set the OGONE flip flop
+ eth_wakeup();
+ return;
+ }
+
+ data = m_eth.fifo[m_eth.fifo_rd];
+ m_eth.tx_crc = f9401_7(m_eth.tx_crc, data);
+ m_eth.fifo_rd = (m_eth.fifo_rd + 1) % ALTO2_ETHER_FIFO_SIZE;
+
+ UINT8 a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
+ if (0 == BE(a49)) {
+ // the FIFO is empty now: clear the OBUSY and WLF flip flops
+ PUT_ETH_OBUSY(m_eth.status, 0);
+ PUT_ETH_WLF(m_eth.status, 0);
+ m_eth.tx_timer->adjust(attotime::from_usec(5.44), -1);
+ } else {
+ // transmit teh next word after 5.44us
+ m_eth.tx_timer->adjust(attotime::from_usec(5.44), arg + 1);
+ }
+ eth_wakeup();
+}
+
+/**
+ * @brief ethernet start function - called from the emulator task
+ */
+void alto2_cpu_device::eth_startf()
+{
+#if 0 // FIXME: does not yet work
+ for (int sysclk = 0; sysclk < 2; sysclk++)
+ update_sysclk(sysclk);
+ PUT_ETH_OCMD(m_eth.status, m_eth.ff_35a & JKFF_Q ? 1 : 0);
+ PUT_ETH_ICMD(m_eth.status, m_eth.ff_35b & JKFF_Q ? 1 : 0);
+#else
+ PUT_ETH_ICMD(m_eth.status, X_BIT(m_bus,16,14));
+ PUT_ETH_OCMD(m_eth.status, X_BIT(m_bus,16,15));
+#endif
+ LOG((LOG_ETH,3, " STARTF; ICMD=%u OCMD=%u\n", GET_ETH_ICMD(m_eth.status), GET_ETH_ICMD(m_eth.status)));
+ eth_wakeup();
+}
+
+/**
+ * @brief ethernet input data function
+ *
+ * Gates the contents of the FIFO to BUS[0-15], and increments
+ * the read pointer at the end of the cycle.
+ */
+void alto2_cpu_device::bs_early_eidfct()
+{
+ UINT16 r = m_eth.fifo[m_eth.fifo_rd];
+ LOG((LOG_ETH,3, " <-EIDFCT; pull %06o from FIFO[%02o]\n", r, m_eth.fifo_rd));
+ m_eth.fifo_rd = (m_eth.fifo_rd + 1) % ALTO2_ETHER_FIFO_SIZE;
+ m_bus &= r;
+
+#if DEBUG_PACKETS
+ if (m_eth.rx_count < ALTO2_ETHER_PACKET_SIZE)
+ m_eth.rx_packet[m_eth.rx_count] = r;
+ m_eth.rx_count++;
+ if (ALTO2_ETHER_PACKET_SIZE == m_eth.rx_count) {
+ dump_packet("RX", m_eth.rx_packet, 0, m_eth.rx_count);
+ m_eth.rx_count = 0;
+ }
+#endif
+ eth_wakeup();
+}
+
+/**
+ * @brief block the ethernet task
+ */
+void alto2_cpu_device::f1_early_eth_block()
+{
+ LOG((LOG_ETH,2," BLOCK %s\n", task_name(m_task)));
+ m_task_wakeup &= ~(1 << task_ether);
+}
+
+/**
+ * @brief ethernet input look function
+ *
+ * Gates the contents of the FIFO to BUS[0-15], but does not
+ * increment the read pointer
+ */
+void alto2_cpu_device::f1_early_eilfct()
+{
+ UINT16 r = m_eth.fifo[m_eth.fifo_rd];
+ LOG((LOG_ETH,3, " <-EILFCT; %06o at FIFO[%02o]\n", r, m_eth.fifo_rd));
+ m_bus &= r;
+}
+
+/**
+ * @brief ethernet post function
+ *
+ * Gates the interface status to BUS[8-15]. Resets the interface
+ * at the end of the function.
+ *
+ * The schematics suggest that just BUS[10-15] is modified.
+ *
+ * Also a comment from the microcode suggests this:
+ *<PRE>
+ * ;Ether Post Function - EPFCT. Gate the hardware status
+ * ;(LOW TRUE) to Bus [10:15], reset interface.
+ *</PRE>
+ */
+void alto2_cpu_device::f1_early_epfct()
+{
+ UINT16 r = 0177777;
+ UINT16 st = m_eth.status;
+ m_eth.status = 0;
+ m_eth.tx_count = 0;
+
+ X_WRBITS(r,16,10,10,~GET_ETH_IDL(st)); // BUS[10] = IDL (input data late)
+ X_WRBITS(r,16,11,11,~GET_ETH_COLL(st)); // BUS[11] = COLL (collision)
+ X_WRBITS(r,16,12,12,~GET_ETH_CRC(st)); // BUS[12] = CRC (CRC error)
+ X_WRBITS(r,16,13,13,~GET_ETH_ICMD(st)); // BUS[13] = ICMD (input command)
+ X_WRBITS(r,16,14,14,~GET_ETH_OCMD(st)); // BUS[13] = OCMD (output command)
+ X_WRBITS(r,16,15,15,~GET_ETH_IT(st)); // BUS[13] = IT (input ???)
+ m_bus &= r;
+
+ LOG((LOG_ETH,3, " <-EPFCT; BUS[8-15] = STATUS (%#o)\n", r));
+ LOG((LOG_ETH,5, " IDL' : %u\n", GET_ETH_IDL(r)));
+ LOG((LOG_ETH,5, " COLL' : %u\n", GET_ETH_COLL(r)));
+ LOG((LOG_ETH,5, " CRC' : %u\n", GET_ETH_CRC(r)));
+ LOG((LOG_ETH,5, " ICMD' : %u\n", GET_ETH_ICMD(r)));
+ LOG((LOG_ETH,5, " OCMD' : %u\n", GET_ETH_OCMD(r)));
+ LOG((LOG_ETH,5, " IT' : %u\n", GET_ETH_IT(r)));
+ eth_wakeup();
+}
+
+/**
+ * @brief ethernet countdown wakeup function
+ *
+ * Sets a flip flop in the interface that will cause a wakeup to the
+ * Ether task on the next tick of SWAKMRT (memory refresh task).
+ * This function must be issued in the instruction after a TASK.
+ * The resulting wakeup is cleared when the Ether task next runs.
+ */
+void alto2_cpu_device::f1_late_ewfct()
+{
+ /*
+ * Set a flag in the CPU to handle the next task switch
+ * to the task_mrt by also waking up the task_ether.
+ */
+ m_ewfct = m_ether_enable;
+}
+
+/**
+ * @brief ethernet output data function
+ *
+ * Loads the FIFO from BUS[0-15], then increments the write
+ * pointer at the end of the cycle.
+ *
+ * Comment from the micro code:
+ *<PRE>
+ * Ether Output Data Function - EODFCT. Copy the bus into the
+ * interface data buffer, increment the write pointer, clears wakeup
+ * request if the buffer is now nearly full (one slot available).
+ *</PRE>
+ */
+void alto2_cpu_device::f2_late_eodfct()
+{
+ LOG((LOG_ETH,3, " EODFCT<-; push %06o into FIFO[%02o]\n", m_bus, m_eth.fifo_wr));
+ m_eth.fifo[m_eth.fifo_wr] = m_bus;
+ m_eth.fifo_wr = (m_eth.fifo_wr + 1) % ALTO2_ETHER_FIFO_SIZE;
+
+#if DEBUG_PACKETS
+ if (m_eth.tx_count < ALTO2_ETHER_PACKET_SIZE)
+ m_eth.tx_packet[m_eth.tx_count] = m_bus;
+ m_eth.tx_count++;
+ if (ALTO2_ETHER_PACKET_SIZE == m_eth.tx_count) {
+ dump_packet("TX", m_eth.tx_packet, 0, m_eth.tx_count);
+ m_eth.tx_count = 0;
+ }
+#endif
+
+ PUT_ETH_WLF(m_eth.status, 1); // set WLF (write latch full)
+ PUT_ETH_OBUSY(m_eth.status, 1); // set OBUSY (output busy)
+ eth_wakeup();
+ // if the FIFO is full, stop wakeup and kick off the timer
+ UINT8 a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
+ if (0 == BF(a49)) {
+ m_task_wakeup &= ~(1 << task_ether);
+ m_eth.tx_timer->adjust(attotime::from_usec(5.44), 0);
+ }
+}
+
+/**
+ * @brief ethernet output start function
+ *
+ * Sets the OBUSY flip flop in the interface, starting data
+ * wakeups to fill the FIFO for output. When the FIFO is full,
+ * or EEFCT has been issued, the interface will wait for silence
+ * on the Ether and begin transmitting.
+ */
+void alto2_cpu_device::f2_late_eosfct()
+{
+ LOG((LOG_ETH,3, " EOSFCT\n"));
+ PUT_ETH_WLF(m_eth.status, 1);
+ PUT_ETH_OBUSY(m_eth.status, 1);
+ eth_wakeup();
+}
+
+/**
+ * @brief ethernet reset branch function
+ *
+ * This command dispatch function merges the ICMD and OCMD flip flops
+ * into NEXT[6-7]. These flip flops are the means of communication
+ * between the emulator task and the ethernet task. The emulator
+ * task sets them up from BUS[14-15] with the STARTF function,
+ * causing the ethernet task to wakeup, dispatch on them and then
+ * reset them with EPFCT.
+ */
+void alto2_cpu_device::f2_late_erbfct()
+{
+ UINT16 r = 0;
+ X_WRBITS(r,10,6,6,GET_ETH_ICMD(m_eth.status));
+ X_WRBITS(r,10,7,7,GET_ETH_OCMD(m_eth.status));
+ LOG((LOG_ETH,3, " ERBFCT; NEXT[6-7] = ICMD,OCMD (%#o | %#o)\n", m_next2, r));
+ m_next2 |= r;
+ eth_wakeup();
+}
+
+/**
+ * @brief ethernet end of transmission function
+ *
+ * This function is issued when all of the main memory output buffer
+ * has been transferred to the FIFO. EEFCT disables further data
+ * wakeups.
+ */
+void alto2_cpu_device::f2_late_eefct()
+{
+ PUT_ETH_OBUSY(m_eth.status, 1);
+ PUT_ETH_OEOT(m_eth.status, 1);
+ // end transmitting the packet
+ m_eth.tx_timer->adjust(attotime::from_usec(5.44), -1);
+ eth_wakeup();
+}
+
+/**
+ * @brief ethernet branch function
+ *
+ * ORs a 1 into NEXT[6] if a collision is detected.
+ * ORs a 1 into NEXT[7] if
+ * an input data late is detected,
+ * or a SIO with AC0[14-15] non-zero is issued (ICMD or OCMD),
+ * or if the receiver is gone (IGONE)
+ * or if the transmitter is gone (OGONE).
+ */
+void alto2_cpu_device::f2_late_ebfct()
+{
+ UINT16 r = 0;
+ X_WRBITS(r,10,6,6, GET_ETH_COLL(m_eth.status));
+ X_WRBITS(r,10,7,7,
+ GET_ETH_IDL(m_eth.status) |
+ GET_ETH_ICMD(m_eth.status) |
+ GET_ETH_OCMD(m_eth.status) |
+ GET_ETH_IGONE(m_eth.status) |
+ GET_ETH_OGONE(m_eth.status));
+ LOG((LOG_ETH,3, " EBFCT; NEXT ... (%#o | %#o)\n", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief ethernet countdown branch function
+ *
+ * The BE' (buffer empty) signal is output D0 of PROM a49
+ * ORs a one into NEXT[7] if the FIFO is not empty.
+ */
+void alto2_cpu_device::f2_late_ecbfct()
+{
+ UINT16 r = 0;
+ UINT8 a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
+ X_WRBITS(r,10,7,7,~BE(a49));
+ LOG((LOG_ETH,3, " ECBFCT; NEXT[7] = FIFO %sempty (%#o | %#o)\n", r ? "not " : "is ", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief ethernet input start function
+ *
+ * Sets the IBUSY flip flop in the interface, causing it to hunt
+ * for the beginning of a packet: silence on the Ether followed
+ * by a transition. When the interface has collected two words,
+ * it will begin generating data wakeups to the microcode.
+ */
+void alto2_cpu_device::f2_late_eisfct()
+{
+ LOG((LOG_ETH,3, " EISFCT\n"));
+ PUT_ETH_IBUSY(m_eth.status, 1);
+ eth_wakeup();
+}
+
+/** @brief called by the CPU when the ethernet task becomes active
+ *
+ * Reset the Ether wake flip flop
+ */
+void alto2_cpu_device::activate_eth()
+{
+ m_ewfct = 0;
+}
+
+/**
+ * @brief update the ethernet circuit JK flip-flops that depend on SYSCLK
+ * @param sysclk current SYSCLK level 0 or 1
+ */
+void alto2_cpu_device::update_sysclk(int sysclk)
+{
+ UINT8 s0, s1;
+
+ /*
+ * JK flip-flop 35a (SIO' and SYSCLK clocked)
+ * (Sheet 7)
+ *
+ * Note: SIO is the emulator F1 STARTF
+ *
+ * CLK (SIO & SYSCLK)'
+ * J BUS[15]
+ * K' 1
+ * S' 1
+ * C' ERESET'
+ * Q OCMD
+ * Q' OCMD'
+ */
+ s0 = m_eth.ff_35a;
+ s1 = (m_d_f1 == f1_emu_startf && sysclk) ? JKFF_CLK : JKFF_0;
+ if (X_BIT(m_bus,16,15))
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_C; // ERESET' not now
+ m_eth.ff_35a = update_jkff(s0, s1, "35a OCMD ");
+
+ /*
+ * JK flip-flop 35b (SIO' and SYSCLK clocked)
+ * (Sheet 7)
+ *
+ * Note: SIO is the emulator F1 STARTF
+ *
+ * CLK (SIO & SYSCLK)'
+ * J BUS[14]
+ * K' 1
+ * S' 1
+ * C' ERESET'
+ * Q ICMD
+ * Q' ICMD'
+ */
+ s0 = m_eth.ff_35b;
+ s1 = (m_d_f1 == f1_emu_startf && sysclk) ? JKFF_CLK : JKFF_0;
+ if (X_BIT(m_bus,16,14))
+ s1 |= JKFF_J;
+ s1 |= JKFF_K;
+ s1 |= JKFF_C; // ERESET' not now
+ m_eth.ff_35b = update_jkff(s0, s1, "35b ICMD ");
+
+ /*
+ * JK flip-flop 10a IBUSY (Sheet 13)
+ *
+ * CLK SYSCLK'
+ * J 0
+ * K' EISFCT'
+ * S' ERESET'
+ * C' 1
+ * Q IBUSY'
+ * Q' IBUSY
+ */
+ s0 = m_eth.ff_10a;
+ s1 = sysclk ? JKFF_CLK : JKFF_0;
+ if (m_d_f2 != f2_ether_eisfct)
+ s1 |= JKFF_K;
+ s1 |= JKFF_C;
+ m_eth.ff_10a = update_jkff(s0, s1, "10a IBUSY ");
+
+ /*
+ * DEMUX 74S157 76
+ * 1A (SYSCLK & EODFCT)'
+ * 2A OSLOAD'
+ * 3A OSLOAD
+ * 4A (SYSCLK & EODFCT)'
+ * 1B ISRFULL
+ * 2B (SYSCLK & EIDFCT)'
+ * 3B EILDFCTA = (EIDFCT' & EILFCT')'
+ * 4B WLF'
+ * SEL IBUSY
+ * 1Y WLLOAD
+ * 2Y RDCNT'
+ * 3Y RR
+ * 4Y WLL'
+ */
+ UINT8 WLLOAD;
+ UINT8 RDCNT0;
+ UINT8 RR;
+ UINT8 WLL0;
+ if (m_eth.ff_10a & JKFF_Q) {
+ WLLOAD = ~(sysclk & (m_d_f2 == f2_ether_eodfct)) & 1;
+ RDCNT0 = m_eth.ff_52b & JKFF_Q ? 1 : 0;
+ RR = m_eth.ff_52b & JKFF_Q0 ? 1 : 0;
+ WLL0 = ~(sysclk & (m_d_f2 == f2_ether_eodfct)) & 1;
+ } else {
+ // ISRFULL
+ WLLOAD = (m_eth.serin >> 1) & 1;
+ RDCNT0 = ~(sysclk & (m_d_bs == bs_ether_eidfct)) & 1;
+ RR = m_d_bs == bs_ether_eidfct || m_d_f1 == f1_ether_eilfct;
+ WLL0 = m_eth.ff_77b & JKFF_Q0 ? 1 : 0;
+ }
+ // TODO: use the signals
+ (void)RDCNT0;
+ (void)RR;
+ (void)WLL0;
+
+ /*
+ * JK flip-flop 10b OBUSY (Sheet 13)
+ *
+ * CLK SYSCLK'
+ * J 0
+ * K' EOSFCT'
+ * S' ERESET'
+ * C' 1
+ * Q OBUSY'
+ * Q' OBUSY
+ */
+ s0 = m_eth.ff_10b;
+ s1 = sysclk ? JKFF_CLK : JKFF_0;
+ if (m_d_f2 != f2_ether_eosfct)
+ s1 |= JKFF_K;
+ m_eth.ff_10b = update_jkff(s0, s1, "10b OBUSY ");
+
+ /*
+ * JK flip-flop 51a EWFCT latch (Sheet 19)
+ *
+ * CLK SYSCLK'
+ * J OCDW
+ * K' EWFCT'
+ * S' ERESET'
+ * C' 1
+ * Q EWFCT latch(?)
+ * Q' ---
+ */
+ s0 = m_eth.ff_51a;
+ s1 = sysclk ? JKFF_CLK : JKFF_0;
+ m_eth.ff_51a = update_jkff(s0, s1, "51a EWFCT_L ");
+
+ /*
+ * JK flip-flop 31b OEOT (Sheet 19)
+ *
+ * CLK SYSCLK'
+ * J 0
+ * K' EEFCT'
+ * S' ERESET'
+ * C' 1
+ * Q OEOT'
+ * Q' ---
+ */
+ s0 = m_eth.ff_31b;
+ s1 = sysclk ? JKFF_CLK : JKFF_0;
+ m_eth.ff_31b = update_jkff(s0, s1, "31b OEOT ");
+
+ /*
+ * JK flip-flop 69a IT (Sheet 14)
+ *
+ * CLK ARC'
+ * J (BNE & ILOC & IMID & WR')
+ * K' 1
+ * S' 1
+ * C' ERESET'
+ * Q INGONE
+ * Q' INGONE'
+ */
+ s0 = m_eth.ff_69a;
+ s1 = JKFF_CLK;
+ m_eth.ff_69a = update_jkff(s0, s1, "69a IT ");
+
+ /*
+ * JK flip-flop 65a IDL (Sheet 10)
+ *
+ * CLK ARC'
+ * J 65b Q
+ * K' 1
+ * S' 1
+ * C' ERESET'
+ * Q IDL
+ * Q' IDL'
+ */
+ s0 = m_eth.ff_65a;
+ s1 = JKFF_CLK;
+ m_eth.ff_65a = update_jkff(s0, s1, "65a IDL ");
+
+ /*
+ * JK flip-flop 65b IO (Sheet 10)
+ *
+ * CLK ISRFULL
+ * J WLF
+ * K' 1
+ * S' 1
+ * C' ERESET
+ * Q to 65a J
+ * Q' ---
+ */
+ s0 = m_eth.ff_65b;
+ s1 = JKFF_CLK;
+ m_eth.ff_65b = update_jkff(s0, s1, "65b IO ");
+
+ /*
+ * JK flip-flop 77b WLF (Sheet 10)
+ *
+ * CLK WLLOAD
+ * J 1
+ * K' 1
+ * S' 1
+ * C' (BUSY | WE')
+ * Q WLF
+ * Q' WLF'
+ */
+ s0 = m_eth.ff_77b;
+ s1 = WLLOAD ? JKFF_CLK : JKFF_0;
+ m_eth.ff_77b = update_jkff(s0, s1, "77b WLF ");
+
+ /*
+ * JK flip-flop 77a WR (Sheet 10)
+ *
+ * CLK ARC'
+ * J RW'
+ * K' (WLF & BF')'
+ * S' BUSY
+ * C' 1
+ * Q WR'
+ * Q' WR
+ */
+ s0 = m_eth.ff_77a;
+ s1 = JKFF_CLK;
+ if (m_eth.ff_77b)
+ m_eth.ff_77a = update_jkff(s0, s1, "77a WR ");
+
+ /*
+ * JK flip-flop 69b INON (Sheet 14)
+ *
+ * CLK ARC'
+ * J CARRIER'
+ * K' (IMID' & ILOC)'
+ * S' 1
+ * C' IBUSY
+ * Q INON
+ * Q' INON'
+ */
+ s0 = m_eth.ff_69b;
+ s1 = JKFF_CLK;
+ m_eth.ff_69b = update_jkff(s0, s1, "69b INON ");
+
+ /*
+ * JK flip-flop 70b ILOC (Sheet 14)
+ *
+ * CLK CARRIER'
+ * J 1
+ * K' 1
+ * S' 1
+ * C' INON
+ * Q ILOC
+ * Q' ILOC'
+ */
+ s0 = m_eth.ff_70b;
+ s1 = JKFF_CLK;
+ m_eth.ff_70b = update_jkff(s0, s1, "70b ILOC ");
+
+ /*
+ * JK flip-flop 51b OCDW (Sheet 19)
+ *
+ * CLK ARC'
+ * J (EWFCT latch | SWAKMRT')'
+ * K' ETAC'
+ * S' 1
+ * C' ERESET'
+ * Q OCDW
+ * Q' OCDW'
+ */
+ s0 = m_eth.ff_51b;
+ s1 = JKFF_CLK;
+ m_eth.ff_51b = update_jkff(s0, s1, "51b OCDW ");
+
+ /*
+ * JK flip-flop 21a OUTON (Sheet 19)
+ *
+ * CLK OTHER'
+ * J OUTON
+ * K' 1
+ * S' 1
+ * C' OBUSY
+ * Q to FF 21b J and K'
+ * Q' ---
+ */
+ s0 = m_eth.ff_21a;
+ s1 = JKFF_CLK;
+ m_eth.ff_21a = update_jkff(s0, s1, "21a OUTON ");
+
+ /*
+ * JK flip-flop 21b COLL (Sheet 19)
+ *
+ * CLK ARC'
+ * J from FF 21a Q
+ * K' dito
+ * S' 1
+ * C' OBUSY
+ * Q COLL
+ * Q' COLL'
+ */
+ s0 = m_eth.ff_21b;
+ s1 = JKFF_CLK;
+ m_eth.ff_21b = update_jkff(s0, s1, "21b COLL ");
+
+ /*
+ * JK flip-flop 31a OUTGONE (Sheet 19)
+ *
+ * CLK OUTON'
+ * J 1
+ * K' 1
+ * S' 1
+ * C' OBUSY
+ * Q OUTGONE
+ * Q' OUTGONE'
+ */
+ s0 = m_eth.ff_31a;
+ s1 = JKFF_CLK;
+ m_eth.ff_31a = update_jkff(s0, s1, "31a OUTGONE ");
+}
+
+/**
+ * @brief update the ethernet circuit JK flip-flops that depend on RCLK
+ * @param rclk current RCLK level 0 or 1
+ */
+void alto2_cpu_device::update_rclk(int rclk)
+{
+ UINT8 s0, s1;
+
+ /*
+ * JK flip-flop 70a IMID (Sheet 14)
+ *
+ * CLK RCLK
+ * J ISR00
+ * K' 1
+ * S' 1
+ * C' INON
+ * Q IMID
+ * Q' IMID'
+ */
+ s0 = m_eth.ff_70a;
+ s1 = rclk ? JKFF_CLK : JKFF_0;
+ m_eth.ff_70a = update_jkff(s0, s1, "70a IMID ");
+
+ /*
+ * JK flip-flop 47a OUTON (Sheet 15)
+ *
+ * CLK RCLK
+ * J (ISR15 | ISRFULL)'
+ * K' dito
+ * S' INON
+ * C' 1
+ * Q ---
+ * Q' ISR14
+ */
+ s0 = m_eth.ff_47a;
+ s1 = rclk ? JKFF_CLK : JKFF_0;
+ m_eth.ff_47a = update_jkff(s0, s1, "47a ISR14 ");
+
+ /*
+ * JK flip-flop 47b COLL (Sheet 15)
+ *
+ * CLK RCLK
+ * J RDATA
+ * K' dito
+ * S' 1
+ * C' INON
+ * Q ISR15
+ * Q' ---
+ */
+ s0 = m_eth.ff_47b;
+ s1 = rclk ? JKFF_CLK : JKFF_0;
+ m_eth.ff_47b = update_jkff(s0, s1, "47b ISR15 ");
+}
+
+/**
+ * @brief update the ethernet circuit JK flip-flops that depend on TCLK
+ * @param tclk current TCLK level 0 or 1
+ */
+void alto2_cpu_device::update_tclk(int tclk)
+{
+ UINT8 s0, s1;
+
+ /*
+ * JK flip-flop 52b OSLOAD (Sheet 17)
+ *
+ * CLK TCLK'
+ * J PROM a42 O2
+ * K' dito
+ * S' 1
+ * C' 1
+ * Q OSLOAD'
+ * Q' OSLOAD
+ */
+ s0 = m_eth.ff_52b;
+ s1 = tclk ? JKFF_0 : JKFF_CLK;
+ m_eth.ff_52b = update_jkff(s0, s1, "52b OSLOAD ");
+
+ /*
+ * JK flip-flop 61a CRCGO (Sheet 21)
+ *
+ * CLK TCLK'
+ * J (OSLOAD & BE)
+ * K' 1
+ * S' 1
+ * C' OUTEND'
+ * Q CRCGO
+ * Q' CRCGO'
+ */
+ s0 = m_eth.ff_61a;
+ s1 = tclk ? JKFF_0 : JKFF_CLK;
+ m_eth.ff_61a = update_jkff(s0, s1, "61a CRCGO ");
+
+ /*
+ * JK flip-flop 61b OUTRGO (Sheet 21)
+ *
+ * CLK TCLK'
+ * J OUTGO
+ * K' dito
+ * S' 1
+ * C' OUTEND'
+ * Q OUTRGO
+ * Q' ---
+ */
+ s0 = m_eth.ff_61b;
+ s1 = tclk ? JKFF_0 : JKFF_CLK;
+ m_eth.ff_61b = update_jkff(s0, s1, "61b OUTRGO ");
+
+ /*
+ * JK flip-flop 62a OUTGO (Sheet 21)
+ *
+ * CLK TCLK'
+ * J OUTON
+ * K' 1
+ * S' 1
+ * C' OUTEND'
+ * Q OUTGO
+ * Q' ---
+ */
+ s0 = m_eth.ff_62a;
+ s1 = tclk ? JKFF_0 : JKFF_CLK;
+ m_eth.ff_62a = update_jkff(s0, s1, "62a OUTGO ");
+
+ /*
+ * JK flip-flop 62b OUTON (Sheet 21)
+ *
+ * CLK TCLK'
+ * J (FEOT' | OOK')'
+ * K' (CRCGO & OSLOAD)'
+ * S' 1
+ * C' PESTOP'
+ * Q OUTON
+ * Q' OUTON'
+ */
+ s0 = m_eth.ff_62b;
+ s1 = tclk ? JKFF_0 : JKFF_CLK;
+ m_eth.ff_62b = update_jkff(s0, s1, "62b OUTON ");
+}
+
+
+/**
+ * @brief ethernet task slot initialization
+ */
+void alto2_cpu_device::init_ether(int task)
+{
+ // intialize all ethernet variables
+ memset(&m_eth, 0, sizeof(m_eth));
+ save_item(NAME(m_eth.fifo));
+ save_item(NAME(m_eth.fifo_rd));
+ save_item(NAME(m_eth.fifo_wr));
+ save_item(NAME(m_eth.status));
+ save_item(NAME(m_eth.rx_crc));
+ save_item(NAME(m_eth.tx_crc));
+ save_item(NAME(m_eth.rx_count));
+ save_item(NAME(m_eth.tx_count));
+ save_item(NAME(m_eth.breath_of_life));
+
+ m_ether_a41 = prom_load(machine(), &pl_enet_a41, memregion("ether_a41")->base());
+ m_ether_a42 = prom_load(machine(), &pl_enet_a42, memregion("ether_a42")->base());
+ m_ether_a49 = prom_load(machine(), &pl_enet_a49, memregion("ether_a49")->base());
+
+ set_bs(task, bs_ether_eidfct, &alto2_cpu_device::bs_early_eidfct, 0);
+
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_eth_block, 0);
+ set_f1(task, f1_ether_eilfct, &alto2_cpu_device::f1_early_eilfct, 0);
+ set_f1(task, f1_ether_epfct, &alto2_cpu_device::f1_early_epfct, 0);
+ set_f1(task, f1_ether_ewfct, 0, &alto2_cpu_device::f1_late_ewfct);
+
+ set_f2(task, f2_ether_eodfct, 0, &alto2_cpu_device::f2_late_eodfct);
+ set_f2(task, f2_ether_eosfct, 0, &alto2_cpu_device::f2_late_eosfct);
+ set_f2(task, f2_ether_erbfct, 0, &alto2_cpu_device::f2_late_erbfct);
+ set_f2(task, f2_ether_eefct, 0, &alto2_cpu_device::f2_late_eefct);
+ set_f2(task, f2_ether_ebfct, 0, &alto2_cpu_device::f2_late_ebfct);
+ set_f2(task, f2_ether_ecbfct, 0, &alto2_cpu_device::f2_late_ecbfct);
+ set_f2(task, f2_ether_eisfct, 0, &alto2_cpu_device::f2_late_eisfct);
+
+ m_active_callback[task] = &alto2_cpu_device::activate_eth;
+
+ m_eth.rx_packet = auto_alloc_array(machine(), UINT16, sizeof(UINT16)*ALTO2_ETHER_PACKET_SIZE);
+ m_eth.tx_packet = auto_alloc_array(machine(), UINT16, sizeof(UINT16)*ALTO2_ETHER_PACKET_SIZE);
+
+ m_eth.tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::tx_packet),this));
+ m_eth.tx_timer->reset();
+
+ m_eth.rx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::rx_breath_of_life),this));
+ m_eth.rx_timer->reset();
+}
+
+void alto2_cpu_device::exit_ether()
+{
+ // nothing to do yet
+}
+
+//! delay between two breath_of_lifes in seconds
+static const int breath_of_life_sec[8] = {
+ 0, 5, 10, 15, 30, 60, 90, 120
+};
+void alto2_cpu_device::reset_ether()
+{
+ memset(m_eth.fifo, 0, sizeof(m_eth.fifo));
+ m_eth.fifo_rd = 0;
+ m_eth.fifo_wr = 0;
+ m_eth.status = 0;
+ m_eth.rx_crc = 0;
+ m_eth.tx_crc = 0;
+ m_eth.rx_count = 0;
+ m_eth.tx_count = 0;
+ m_eth.breath_of_life = 0;
+ m_eth.rx_timer->reset();
+ m_eth.tx_timer->reset();
+ ioport_port* config = ioport(":CONFIG");
+ // config should be valid, unless the driver doesn't define it
+ if (config)
+ m_eth.breath_of_life = breath_of_life_sec[(config->read() >> 4) & 7];
+ logerror("Ethernet breath_of_life %d sec\n", m_eth.breath_of_life);
+ if (m_eth.breath_of_life)
+ m_eth.rx_timer->adjust(attotime::from_seconds(m_eth.breath_of_life), 0);
+}
diff --git a/src/emu/cpu/alto2/a2ether.h b/src/emu/cpu/alto2/a2ether.h
new file mode 100644
index 00000000000..2082801e205
--- /dev/null
+++ b/src/emu/cpu/alto2/a2ether.h
@@ -0,0 +1,118 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII ethernet task (ETHER)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#define ALTO2_ETHER_FIFO_SIZE 16 //!< number of words in the ethernet FIFO
+#define ALTO2_ETHER_PACKET_SIZE 0400 //!< size of a packet in words
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2ETHER_H_
+#define _A2ETHER_H_
+//! BUS source for ethernet task
+enum {
+ bs_ether_eidfct = bs_task_3 //!< ethernet task: Ethernet input data function
+};
+
+//! F1 functions for ethernet task
+enum {
+ f1_ether_eilfct = f1_task_13, //!< f1 (1011): ethernet input look function
+ f1_ether_epfct = f1_task_14, //!< f1 (1100): ethernet post function
+ f1_ether_ewfct = f1_task_15 //!< f1 (1101): ethernet countdown wakeup function
+};
+
+//! F2 functions for ethernet task
+enum {
+ f2_ether_eodfct = f2_task_10, //!< f2 (1000): ethernet output data function
+ f2_ether_eosfct = f2_task_11, //!< f2 (1001): ethernet output start function
+ f2_ether_erbfct = f2_task_12, //!< f2 (1010): ethernet reset branch function
+ f2_ether_eefct = f2_task_13, //!< f2 (1011): ethernet end of transmission function
+ f2_ether_ebfct = f2_task_14, //!< f2 (1100): ethernet branch function
+ f2_ether_ecbfct = f2_task_15, //!< f2 (1101): ethernet countdown branch function
+ f2_ether_eisfct = f2_task_16 //!< f2 (1110): ethernet input start function
+ //!< f2 (1111): undefined
+};
+
+UINT8* m_ether_a41; //!< BPROM; P3601-1; 256x4; enet.a41 "PE1"
+UINT8* m_ether_a42; //!< BPROM; P3601-1; 256x4; enet.a42 "PE2"
+UINT8* m_ether_a49; //!< BPROM; P3601-1; 265x4 enet.a49 "AFIFO"
+enum {
+ ether_a49_BE = (1 << 0), //!< buffer empty
+ ether_a49_BNE = (1 << 1), //!< buffer next empty
+ ether_a49_BNNE = (1 << 2), //!< buffer next next empty
+ ether_a49_BF = (1 << 3) //!< buffer full
+};
+
+struct {
+ UINT32 serin; //!< serial input shift registers 74164 #37 and #33
+ UINT16 fifo[ALTO2_ETHER_FIFO_SIZE]; //!< FIFO buffer
+ UINT16 fifo_rd; //!< FIFO input pointer
+ UINT16 fifo_wr; //!< FIFO output pointer
+ UINT16 status; //!< status word
+ UINT16 rx_crc; //!< receiver CRC
+ UINT16 tx_crc; //!< transmitter CRC
+ UINT32 rx_count; //!< received words count
+ UINT32 tx_count; //!< transmitted words count
+ UINT16* rx_packet; //!< buffer to collect received words
+ UINT16* tx_packet; //!< buffer to collect transmitted words
+ emu_timer* rx_timer; //!< receiver timer
+ emu_timer* tx_timer; //!< transmitter timer
+ jkff_t ff_10a; //!< JK flip-flop 10a IBUSY (Sheet 13)
+ jkff_t ff_10b; //!< JK flip-flop 10b OBUSY (Sheet 13)
+ jkff_t ff_21a; //!< JK flip-flop 21a OUTON (Sheet 19)
+ jkff_t ff_21b; //!< JK flip-flop 21b COLL (Sheet 19)
+ jkff_t ff_31a; //!< JK flip-flop 31a OUTGONE (Sheet 19)
+ jkff_t ff_31b; //!< JK flip-flop 31b OEOT (Sheet 19)
+ jkff_t ff_35a; //!< JK flip-flop 35a OCMD (Sheet 7)
+ jkff_t ff_35b; //!< JK flip-flop 35b ICMD (Sheet 7)
+ jkff_t ff_47a; //!< JK flip-flop 47a ISR14 (Sheet 15)
+ jkff_t ff_47b; //!< JK flip-flop 47b ISR15 (Sheet 15)
+ jkff_t ff_51a; //!< JK flip-flop 51a EWFCT latch (Sheet 19)
+ jkff_t ff_51b; //!< JK flip-flop 51b OCDW (Sheet 19)
+ jkff_t ff_52b; //!< JK flip-flop 52b OSLOAD (Sheet 17)
+ jkff_t ff_61a; //!< JK flip-flop 61a CRCGO (Sheet 21)
+ jkff_t ff_61b; //!< JK flip-flop 61b OUTRGO (Sheet 21)
+ jkff_t ff_62a; //!< JK flip-flop 62a OUTON (Sheet 21)
+ jkff_t ff_62b; //!< JK flip-flop 62b OUTGO (Sheet 21)
+ jkff_t ff_65a; //!< JK flip-flop 65a IDL (Sheet 10)
+ jkff_t ff_65b; //!< JK flip-flop 65b IO (Sheet 10)
+ jkff_t ff_69a; //!< JK flip-flop 69a IT (Sheet 14)
+ jkff_t ff_69b; //!< JK flip-flop 69b INON (Sheet 14)
+ jkff_t ff_70a; //!< JK flip-flop 70a IMID (Sheet 14)
+ jkff_t ff_70b; //!< JK flip-flop 70b ILOC (Sheet 14)
+ jkff_t ff_77a; //!< JK flip-flop 77a WR (Sheet 10)
+ jkff_t ff_77b; //!< JK flip-flop 77b WLF (Sheet 10)
+ int breath_of_life; //!< if non-zero, interval in seconds at which to broadcast the breath-of-life
+} m_eth;
+
+TIMER_CALLBACK_MEMBER( rx_breath_of_life ); //!< HACK: pull the next word from the breath-of-life in the fifo
+TIMER_CALLBACK_MEMBER( tx_packet ); //!< transmit data from the FIFO to <nirvana for now>
+void eth_wakeup(); //!< check for the various reasons to wakeup the Ethernet task
+void eth_startf(); //!< start input or output depending on m_bus
+void bs_early_eidfct(); //!< bus source: Ethernet input data function
+void f1_early_eth_block(); //!< F1 func: block the Ether task
+void f1_early_eilfct(); //!< F1 func: Ethernet input look function
+void f1_early_epfct(); //!< F1 func: Ethernet post function
+void f1_late_ewfct(); //!< F1 func: Ethernet countdown wakeup function
+void f2_late_eodfct(); //!< F2 func: Ethernet output data function
+void f2_late_eosfct(); //!< F2 func: Ethernet output start function
+void f2_late_erbfct(); //!< F2 func: Ethernet reset branch function
+void f2_late_eefct(); //!< F2 func: Ethernet end of transmission function
+void f2_late_ebfct(); //!< F2 func: Ethernet branch function
+void f2_late_ecbfct(); //!< F2 func: Ethernet countdown branch function
+void f2_late_eisfct(); //!< F2 func: Ethernet input start function
+void activate_eth(); //!< called by the CPU when the Ethernet task becomes active
+void update_sysclk(int sysclk); //!< update all JK flip-flops for one cycle of SYSCLK
+void update_rclk(int rclk); //!< update all JK flip-flops for one cycle of RCLK
+void update_tclk(int tclk); //!< update all JK flip-flops for one cycle of TCLK
+void init_ether(int task = task_ether); //!< initialize the ethernet task
+void exit_ether(); //!< deinitialize the ethernet task
+void reset_ether(); //!< reset the ethernet task
+#endif // _A2ETHER_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2hw.c b/src/emu/cpu/alto2/a2hw.c
new file mode 100644
index 00000000000..32c46cf1374
--- /dev/null
+++ b/src/emu/cpu/alto2/a2hw.c
@@ -0,0 +1,424 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII memory mapped I/O hardware
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+#include "a2roms.h"
+
+/**
+ * @brief read printer paper ready bit
+ * Paper ready bit. 0 when the printer is ready for a paper scrolling operation.
+ */
+READ16_MEMBER ( alto2_cpu_device::pprdy_r ) { return X_RDBITS(m_hw.utilin,16,0,0); }
+
+/**
+ * @brief read printer check bit
+ * Printer check bit bit.
+ * Should the printer find itself in an abnormal state, it sets this bit to 0
+ */
+READ16_MEMBER ( alto2_cpu_device::pcheck_r ) { return X_RDBITS(m_hw.utilin,16,1,1); }
+
+/**
+ * @brief read unused bit 2
+ */
+READ16_MEMBER ( alto2_cpu_device::unused2_r ) { return X_RDBITS(m_hw.utilin,16,2,2); }
+
+/**
+ * @brief read printer daisy ready bit
+ * Daisy ready bit. 0 when the printer is ready to print a character.
+ */
+READ16_MEMBER ( alto2_cpu_device::pchrdy_r ) { return X_RDBITS(m_hw.utilin,16,3,3); }
+
+/**
+ * @brief read printer carriage ready bit
+ * Carriage ready bit. 0 when the printer is ready for horizontal positioning.
+ */
+READ16_MEMBER ( alto2_cpu_device::parrdy_r ) { return X_RDBITS(m_hw.utilin,16,4,4); }
+
+/**
+ * @brief read printer ready bit
+ * Ready bit. Both this bit and the appropriate other ready bit (carriage,
+ * daisy, etc.) must be 0 before attempting any output operation.
+ */
+READ16_MEMBER ( alto2_cpu_device::pready_r ) { return X_RDBITS(m_hw.utilin,16,5,5); }
+
+/**
+ * @brief memory configuration switch
+ */
+READ16_MEMBER ( alto2_cpu_device::memconfig_r ) { return X_RDBITS(m_hw.utilin,16,6,6); }
+
+/**
+ * @brief get unused bit 7
+ */
+READ16_MEMBER ( alto2_cpu_device::unused7_r ) { return X_RDBITS(m_hw.utilin,16,7,7); }
+
+/**
+ * @brief get key set key 0
+ */
+READ16_MEMBER ( alto2_cpu_device::keyset_key0_r ) { return X_RDBITS(m_hw.utilin,16,8,8); }
+
+/**
+ * @brief get key set key 1
+ */
+READ16_MEMBER ( alto2_cpu_device::keyset_key1_r ) { return X_RDBITS(m_hw.utilin,16,9,9); }
+
+/**
+ * @brief get key set key 2
+ */
+READ16_MEMBER ( alto2_cpu_device::keyset_key2_r ) { return X_RDBITS(m_hw.utilin,16,10,10); }
+
+/**
+ * @brief get key set key 3
+ */
+READ16_MEMBER ( alto2_cpu_device::keyset_key3_r ) { return X_RDBITS(m_hw.utilin,16,11,11); }
+
+/**
+ * @brief get key set key 4
+ */
+READ16_MEMBER ( alto2_cpu_device::keyset_key4_r ) { return X_RDBITS(m_hw.utilin,16,12,12); }
+
+/**
+ * @brief get mouse red button bit
+ */
+READ16_MEMBER ( alto2_cpu_device::mouse_red_r ) { return X_RDBITS(m_hw.utilin,16,13,13); }
+
+/**
+ * @brief get mouse blue button bit
+ */
+READ16_MEMBER ( alto2_cpu_device::mouse_blue_r ) { return X_RDBITS(m_hw.utilin,16,14,14); }
+
+/**
+ * @brief get mouse yellow button bit
+ */
+READ16_MEMBER ( alto2_cpu_device::mouse_yellow_r ) { return X_RDBITS(m_hw.utilin,16,15,15); }
+
+/**
+ * @brief write printer paper ready bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::pprdy_w ) { X_WRBITS(m_hw.utilin,16,0,0,data); }
+
+/**
+ * @brief write printer check bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::pcheck_w ) { X_WRBITS(m_hw.utilin,16,1,1,data); }
+
+/**
+ * @brief read unused bit 2
+ */
+WRITE16_MEMBER( alto2_cpu_device::unused2_w ) { X_WRBITS(m_hw.utilin,16,2,2,data); }
+
+/**
+ * @brief write printer daisy ready bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::pchrdy_w ) { X_WRBITS(m_hw.utilin,16,3,3,data); }
+
+/**
+ * @brief write printer carriage ready bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::parrdy_w ) { X_WRBITS(m_hw.utilin,16,4,4,data); }
+
+/**
+ * @brief write printer ready bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::pready_w ) { X_WRBITS(m_hw.utilin,16,5,5,data); }
+
+/**
+ * @brief write memory configuration switch
+ */
+WRITE16_MEMBER( alto2_cpu_device::memconfig_w ) { X_WRBITS(m_hw.utilin,16,6,6,data); }
+
+/**
+ * @brief write unused bit 7
+ */
+WRITE16_MEMBER( alto2_cpu_device::unused7_w ) { X_WRBITS(m_hw.utilin,16,7,7,data); }
+
+/**
+ * @brief write key set key 0
+ */
+WRITE16_MEMBER( alto2_cpu_device::keyset_key0_w ) { X_WRBITS(m_hw.utilin,16,8,8,data); }
+
+/**
+ * @brief write key set key 1
+ */
+WRITE16_MEMBER( alto2_cpu_device::keyset_key1_w ) { X_WRBITS(m_hw.utilin,16,9,9,data); }
+
+/**
+ * @brief write key set key 2
+ */
+WRITE16_MEMBER( alto2_cpu_device::keyset_key2_w ) { X_WRBITS(m_hw.utilin,16,10,10,data); }
+
+/**
+ * @brief write key set key 3
+ */
+WRITE16_MEMBER( alto2_cpu_device::keyset_key3_w ) { X_WRBITS(m_hw.utilin,16,11,11,data); }
+
+/**
+ * @brief write key set key 4
+ */
+WRITE16_MEMBER( alto2_cpu_device::keyset_key4_w ) { X_WRBITS(m_hw.utilin,16,12,12,data); }
+
+/**
+ * @brief write mouse red button bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::mouse_red_w ) { X_WRBITS(m_hw.utilin,16,13,13,data); }
+
+/**
+ * @brief write mouse blue button bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::mouse_blue_w ) { X_WRBITS(m_hw.utilin,16,14,14,data); }
+
+/**
+ * @brief write mouse yellow button bit
+ */
+WRITE16_MEMBER( alto2_cpu_device::mouse_yellow_w ) { X_WRBITS(m_hw.utilin,16,15,15,data); }
+
+/**
+ * @brief write mouse buttons bits
+ */
+WRITE16_MEMBER( alto2_cpu_device::mouse_buttons_w ) { X_WRBITS(m_hw.utilin,16,13,15,data); }
+
+/**
+ * @brief printer paper strobe bit
+ * Paper strobe bit. Toggling this bit causes a paper scrolling operation.
+ */
+static inline UINT16 GET_PPPSTR(UINT16 utilout) { return X_RDBITS(utilout,16,0,0); }
+
+/**
+ * @brief printer retstore bit
+ * Restore bit. Toggling this bit resets the printer (including clearing
+ * the "check" condition if present) and moves the carriage to the
+ * left margin.
+ */
+static inline UINT16 GET_PREST(UINT16 utilout) { return X_RDBITS(utilout,16,1,1); }
+
+/**
+ * @brief printer ribbon bit
+ * Ribbon bit. When this bit is 1 the ribbon is up (in printing
+ * position); when 0, it is down.
+ */
+static inline UINT16 GET_PRIB(UINT16 utilout) { return X_RDBITS(utilout,16,2,2); }
+
+/**
+ * @brief printer daisy strobe bit
+ * Daisy strobe bit. Toggling this bit causes a character to be printed.
+ */
+static inline UINT16 GET_PCHSTR(UINT16 utilout) { return X_RDBITS(utilout,16,3,3); }
+
+/**
+ * @brief printer carriage strobe bit
+ * Carriage strobe bit. Toggling this bit causes a horizontal position operation.
+ */
+static inline UINT16 GET_PCARSTR(UINT16 utilout) { return X_RDBITS(utilout,16,4,4); }
+
+/**
+ * @brief printer data
+ * Argument to various output operations:
+ * 1. Printing characters. When the daisy bit is toggled bits 9-15 of this field
+ * are interpreted as an ASCII character code to be printed (it should be noted
+ * that all codes less than 040 print as lower case "w").
+ * 2. For paper and carriage operations the field is interpreted as a displacement
+ * (-1024 to +1023), in units of 1/48 inch for paper and 1/60 inch for carriage.
+ * Positive is down or to the right, negative up or to the left. The value is
+ * represented as sign-magnitude (i.e., bit 5 is 1 for negative numbers, 0 for
+ * positive; bits 6-15 are the absolute value of the number).
+ */
+static inline UINT16 GET_PDATA(UINT16 utilout) { return X_RDBITS(utilout,16,5,15); }
+
+/**
+ * @brief read the UTILIN port
+ *
+ * @param addr memory mapped I/O address to be read
+ * @return current value on the UTILIN port
+ */
+READ16_MEMBER( alto2_cpu_device::utilin_r )
+{
+ UINT16 data;
+ // FIXME: update the printer status
+ // printer_read();
+
+ data = m_hw.utilin;
+
+ if (!space.debugger_access()) {
+ LOG((LOG_HW,2," UTILIN rd %#o (%#o)\n", offset, data));
+ }
+ return data;
+}
+
+/**
+ * @brief read the XBUS port
+ *
+ * @param addr memory mapped I/O address to be read
+ * @return current value on the XBUS port latch
+ */
+READ16_MEMBER( alto2_cpu_device::xbus_r )
+{
+ UINT16 data = m_hw.xbus[offset & 3];
+
+ if (!space.debugger_access()) {
+ LOG((LOG_HW,2," XBUS[%d] rd %#o (%#o)\n", offset & 3, offset, data));
+ }
+ return data;
+}
+
+/**
+ * @brief write the XBUS port
+ *
+ * The actual outputs are active-low.
+ *
+ * @param addr memory mapped I/O address to be read
+ * @param data value to write to the XBUS port latch
+ */
+WRITE16_MEMBER( alto2_cpu_device::xbus_w )
+{
+ if (!space.debugger_access()) {
+ LOG((LOG_HW,2," XBUS[%d] wr %#o (%#o)\n", offset & 3, offset, data));
+ }
+ m_hw.xbus[offset&3] = data;
+}
+
+/**
+ * @brief read the UTILOUT port
+ *
+ * @param addr memory mapped I/O address to be read
+ * @return current value on the UTILOUT port latch
+ */
+READ16_MEMBER( alto2_cpu_device::utilout_r )
+{
+ UINT16 data = m_hw.utilout ^ 0177777;
+ if (!space.debugger_access()) {
+ LOG((0,2," UTILOUT rd %#o (%#o)\n", offset, data));
+ }
+ return data;
+}
+
+/**
+ * @brief write the UTILOUT port
+ *
+ * The actual outputs are active-low.
+ *
+ * @param addr memory mapped I/O address to be read
+ * @param data value to write to the UTILOUT port latch
+ */
+WRITE16_MEMBER( alto2_cpu_device::utilout_w )
+{
+ if (!space.debugger_access()) {
+ LOG((LOG_HW,2," UTILOUT wr %#o (%#o)\n", offset, data));
+ }
+ m_hw.utilout = data ^ 0177777;
+
+ // FIXME: write printer data
+ // printer_write();
+}
+
+/**
+ * <PRE>
+ * TODO: use madr.a65 and madr.a64 to determine the actual I/O address ranges
+ *
+ * madr.a65
+ * address line connected to
+ * -------------------------------
+ * A0 MAR[11]
+ * A1 KEYSEL
+ * A2 MAR[7-10] == 0
+ * A3 MAR[12]
+ * A4 MAR[13]
+ * A5 MAR[14]
+ * A6 MAR[15]
+ * A7 IOREF (MAR[0-6] == 1)
+ *
+ * output data connected to
+ * -------------------------------
+ * D0 IOSEL0
+ * D1 IOSEL1
+ * D2 IOSEL2
+ * D3 INTIO
+ *
+ * madr.a64
+ * address line connected to
+ * -------------------------------
+ * A0 STORE
+ * A1 MAR[11]
+ * A2 MAR[7-10] == 0
+ * A3 MAR[12]
+ * A4 MAR[13]
+ * A5 MAR[14]
+ * A6 MAR[15]
+ * A7 IOREF (MAR[0-6] == 1)
+ *
+ * output data connected to
+ * -------------------------------
+ * D0 & MISYSCLK -> SELP
+ * D1 ^ INTIO -> INTIOX
+ * " ^ 1 -> NERRSEL
+ * " & WRTCLK -> NRSTE
+ * D2 XREG'
+ * D3 & MISYSCLK -> LOADERC
+ * </PRE>
+ */
+
+static const prom_load_t pl_madr_a64 =
+{
+ "madr.a64",
+ 0,
+ "a66b0eda",
+ "4d9088f592caa3299e90966b17765be74e523144",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 017, // invert D0-D3
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+static const prom_load_t pl_madr_a65 =
+{
+ "madr.a65",
+ 0,
+ "ba37febd",
+ "82e9db1cb65f451755295f0d179e6f8fe3349d4d",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 017, // invert D0-D3
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+/**
+ * @brief clear all keys and install the mmio handler for KBDAD to KBDAD+3
+ */
+void alto2_cpu_device::init_hw()
+{
+ memset(&m_hw, 0, sizeof(m_hw));
+ m_madr_a64 = prom_load(machine(), &pl_madr_a64, memregion("madr_a64")->base());
+ m_madr_a65 = prom_load(machine(), &pl_madr_a65, memregion("madr_a65")->base());
+}
+
+void alto2_cpu_device::exit_hw()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_hw()
+{
+ m_hw.eia = 0;
+ m_hw.utilout = 0;
+ // open inputs on the XBUS (?)
+ m_hw.xbus[0] = 0177777;
+ m_hw.xbus[1] = 0177777;
+ m_hw.xbus[2] = 0177777;
+ m_hw.xbus[3] = 0177777;
+ // open inputs on UTILIN
+ m_hw.utilin = 0177777;
+}
diff --git a/src/emu/cpu/alto2/a2hw.h b/src/emu/cpu/alto2/a2hw.h
new file mode 100644
index 00000000000..1ec9a72e8d4
--- /dev/null
+++ b/src/emu/cpu/alto2/a2hw.h
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII memory mapped i/o stuff (HW)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2HW_H_
+#define _A2HW_H_
+//! miscellaneous hardware registers in the memory mapped I/O range
+struct {
+ UINT16 eia; //!< the EIA port at 0177001
+ UINT16 utilout; //!< the UTILOUT port at 0177016 (active-low outputs)
+ UINT16 xbus[4]; //!< the XBUS port at 0177020 to 0177023
+ UINT16 utilin; //!< the UTILIN port at 0177030 to 0177033 (same value on all addresses)
+} m_hw;
+
+DECLARE_READ16_MEMBER ( pprdy_r ); //!< read UTILIN[0] printer paper ready bit
+DECLARE_READ16_MEMBER ( pcheck_r ); //!< read UTILIN[1] printer check bit
+DECLARE_READ16_MEMBER ( unused2_r ); //!< read UTILIN[2] unused bit
+DECLARE_READ16_MEMBER ( pchrdy_r ); //!< read UTILIN[3] printer daisy ready bit
+DECLARE_READ16_MEMBER ( parrdy_r ); //!< read UTILIN[4] printer carriage ready bit
+DECLARE_READ16_MEMBER ( pready_r ); //!< read UTILIN[5] printer ready bit
+DECLARE_READ16_MEMBER ( memconfig_r ); //!< read UTILIN[6] memory config switch
+DECLARE_READ16_MEMBER ( unused7_r ); //!< read UTILIN[7] unused bit
+DECLARE_READ16_MEMBER ( keyset_key0_r ); //!< read UTILIN[8] keyset key #0
+DECLARE_READ16_MEMBER ( keyset_key1_r ); //!< read UTILIN[9] keyset key #1
+DECLARE_READ16_MEMBER ( keyset_key2_r ); //!< read UTILIN[10] keyset key #2
+DECLARE_READ16_MEMBER ( keyset_key3_r ); //!< read UTILIN[11] keyset key #3
+DECLARE_READ16_MEMBER ( keyset_key4_r ); //!< read UTILIN[12] keyset key #4
+DECLARE_READ16_MEMBER ( mouse_red_r ); //!< read UTILIN[13] mouse red button bit
+DECLARE_READ16_MEMBER ( mouse_blue_r ); //!< read UTILIN[14] mouse blue button bit
+DECLARE_READ16_MEMBER ( mouse_yellow_r ); //!< read UTILIN[15] mouse yellow button bit
+
+DECLARE_WRITE16_MEMBER( pprdy_w ); //!< write UTILIN[0] printer paper ready bit
+DECLARE_WRITE16_MEMBER( pcheck_w ); //!< write UTILIN[1] printer check bit
+DECLARE_WRITE16_MEMBER( unused2_w ); //!< write UTILIN[2] unused bit
+DECLARE_WRITE16_MEMBER( pchrdy_w ); //!< write UTILIN[3] printer daisy ready bit
+DECLARE_WRITE16_MEMBER( parrdy_w ); //!< write UTILIN[4] carriage ready bit
+DECLARE_WRITE16_MEMBER( pready_w ); //!< write UTILIN[5] printer ready bit
+DECLARE_WRITE16_MEMBER( memconfig_w ); //!< write UTILIN[6] memory config switch
+DECLARE_WRITE16_MEMBER( unused7_w ); //!< write UTILIN[7] unused bit
+DECLARE_WRITE16_MEMBER( keyset_key0_w ); //!< write UTILIN[8] keyset key #0
+DECLARE_WRITE16_MEMBER( keyset_key1_w ); //!< write UTILIN[9] keyset key #1
+DECLARE_WRITE16_MEMBER( keyset_key2_w ); //!< write UTILIN[10] keyset key #2
+DECLARE_WRITE16_MEMBER( keyset_key3_w ); //!< write UTILIN[11] keyset key #3
+DECLARE_WRITE16_MEMBER( keyset_key4_w ); //!< write UTILIN[12] keyset key #4
+DECLARE_WRITE16_MEMBER( mouse_red_w ); //!< write UTILIN[13] mouse red button bit
+DECLARE_WRITE16_MEMBER( mouse_blue_w ); //!< write UTILIN[14] mouse blue button bit
+DECLARE_WRITE16_MEMBER( mouse_yellow_w ); //!< write UTILIN[15] mouse yellow button bit
+DECLARE_WRITE16_MEMBER( mouse_buttons_w ); //!< write UTILIN[13-15] mouse buttons bits
+
+DECLARE_READ16_MEMBER ( utilin_r ); //!< read an UTILIN address
+DECLARE_READ16_MEMBER ( utilout_r ); //!< read the UTILOUT address
+DECLARE_WRITE16_MEMBER( utilout_w ); //!< write the UTILOUT address
+DECLARE_READ16_MEMBER ( xbus_r ); //!< read an XBUS address
+DECLARE_WRITE16_MEMBER( xbus_w ); //!< write an XBUS address (?)
+
+void init_hw(); //!< initialize miscellaneous hardware
+void exit_hw(); //!< deinitialize miscellaneous hardware
+void reset_hw(); //!< reset miscellaneous hardware
+#endif // _A2HW_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2jkff.h b/src/emu/cpu/alto2/a2jkff.h
new file mode 100644
index 00000000000..429a6454eb1
--- /dev/null
+++ b/src/emu/cpu/alto2/a2jkff.h
@@ -0,0 +1,194 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII Dual J/K flip-flop 74109 emulation
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#define JKFF_DEBUG 0 //!< define 1 to debug the transitions
+
+/**
+ * @brief enumeration of the inputs and outputs of a JK flip-flop type 74109
+ * <PRE>
+ * 74109
+ * Dual J-/K flip-flops with set and reset.
+ *
+ * +----------+ +-----------------------------+
+ * /1RST |1 +--+ 16| VCC | J |/K |CLK|/SET|/RST| Q |/Q |
+ * 1J |2 15| /2RST |---+---+---+----+----+---+---|
+ * /1K |3 14| 2J | X | X | X | 0 | 0 | 1 | 1 |
+ * 1CLK |4 74 13| /2K | X | X | X | 0 | 1 | 1 | 0 |
+ * /1SET |5 109 12| 2CLK | X | X | X | 1 | 0 | 0 | 1 |
+ * 1Q |6 11| /2SET | 0 | 0 | / | 1 | 1 | 0 | 1 |
+ * /1Q |7 10| 2Q | 0 | 1 | / | 1 | 1 | - | - |
+ * GND |8 9| /2Q | 1 | 0 | / | 1 | 1 |/Q | Q |
+ * +----------+ | 1 | 1 | / | 1 | 1 | 1 | 0 |
+ * | X | X |!/ | 1 | 1 | - | - |
+ * +-----------------------------+
+ *
+ * [This information is part of the GIICM]
+ * </PRE>
+ */
+typedef enum {
+ JKFF_0, //!< no inputs or outputs
+ JKFF_CLK = (1 << 0), //!< clock signal
+ JKFF_J = (1 << 1), //!< J input
+ JKFF_K = (1 << 2), //!< K' input
+ JKFF_S = (1 << 3), //!< S' input
+ JKFF_C = (1 << 4), //!< C' input
+ JKFF_Q = (1 << 5), //!< Q output
+ JKFF_Q0 = (1 << 6) //!< Q' output
+} jkff_t;
+
+#else // ALTO2_DEFINE_CONSTANTS
+
+#ifndef _A2JKFF_H_
+#define _A2JKFF_H_
+
+#if JKFF_DEBUG
+/**
+ * @brief simulate a 74109 J-K flip-flop with set and reset inputs
+ *
+ * @param s0 is the previous state of the FF's in- and outputs
+ * @param s1 is the next state
+ * @return returns the next state and probably modified Q output
+ */
+static inline jkff_t update_jkff(UINT8 s0, UINT8 s1, const char* jkff_name)
+{
+ switch (s1 & (JKFF_C | JKFF_S))
+ {
+ case JKFF_C | JKFF_S: /* C' is 1, and S' is 1 */
+ if (((s0 ^ s1) & s1) & JKFF_CLK) {
+ /* rising edge of the clock */
+ switch (s1 & (JKFF_J | JKFF_K))
+ {
+ case 0:
+ /* both J and K' are 0: set Q to 0, Q' to 1 */
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ if (s0 & JKFF_Q) {
+ LOG((LOG_DISK,9,"\t\t%s J:0 K':0 -> Q:0\n", jkff_name));
+ }
+ break;
+ case JKFF_J:
+ /* J is 1, and K' is 0: toggle Q */
+ if (s0 & JKFF_Q)
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ else
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ LOG((LOG_DISK,9,"\t\t%s J:0 K':1 flip-flop Q:%d\n", jkff_name, (s1 & JKFF_Q) ? 1 : 0));
+ break;
+ case JKFF_K:
+ if ((s0 ^ s1) & JKFF_Q) {
+ LOG((LOG_DISK,9,"\t\t%s J:0 K':1 keep Q:%d\n", jkff_name, (s1 & JKFF_Q) ? 1 : 0));
+ }
+ /* J is 0, and K' is 1: keep Q as is */
+ if (s0 & JKFF_Q)
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ else
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ break;
+ case JKFF_J | JKFF_K:
+ /* both J and K' are 1: set Q to 1 */
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ if (!(s0 & JKFF_Q)) {
+ LOG((LOG_DISK,9,"\t\t%s J:1 K':1 -> Q:1\n", jkff_name));
+ }
+ break;
+ }
+ } else {
+ /* keep Q */
+ s1 = (s1 & ~JKFF_Q) | (s0 & JKFF_Q);
+ }
+ break;
+ case JKFF_S:
+ /* S' is 1, C' is 0: set Q to 0, Q' to 1 */
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ if (s0 & JKFF_Q) {
+ LOG((LOG_DISK,9,"\t\t%s C':0 -> Q:0\n", jkff_name));
+ }
+ break;
+ case JKFF_C:
+ /* S' is 0, C' is 1: set Q to 1, Q' to 0 */
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ if (!(s0 & JKFF_Q)) {
+ LOG((LOG_DISK,9,"\t\t%s S':0 -> Q:1\n", jkff_name));
+ }
+ break;
+ case 0:
+ default:
+ /* unstable state (what to do?) */
+ s1 = s1 | JKFF_Q | JKFF_Q0;
+ LOG((LOG_DISK,9,"\t\t%s C':0 S':0 -> Q:1 and Q':1 <unstable>\n", jkff_name));
+ break;
+ }
+ return static_cast<jkff_t>(s1);
+}
+#else // JKFF_DEBUG
+/**
+ * @brief simulate a 74109 J-K flip-flop with set and reset inputs
+ *
+ * @param s0 is the previous state of the FF's in- and outputs
+ * @param s1 is the next state
+ * @return returns the next state and probably modified Q output
+ */
+static inline jkff_t update_jkff(UINT8 s0, UINT8 s1, const char*)
+{
+ switch (s1 & (JKFF_C | JKFF_S))
+ {
+ case JKFF_C | JKFF_S: /* C' is 1, and S' is 1 */
+ if (((s0 ^ s1) & s1) & JKFF_CLK) {
+ /* rising edge of the clock */
+ switch (s1 & (JKFF_J | JKFF_K))
+ {
+ case 0:
+ /* both J and K' are 0: set Q to 0, Q' to 1 */
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ break;
+ case JKFF_J:
+ /* J is 1, and K' is 0: toggle Q */
+ if (s0 & JKFF_Q)
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ else
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ break;
+ case JKFF_K:
+ /* J is 0, and K' is 1: keep Q as is */
+ if (s0 & JKFF_Q)
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ else
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ break;
+ case JKFF_J | JKFF_K:
+ /* both J and K' are 1: set Q to 1 */
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ break;
+ }
+ } else {
+ /* keep Q */
+ s1 = (s1 & ~JKFF_Q) | (s0 & JKFF_Q);
+ }
+ break;
+ case JKFF_S:
+ /* S' is 1, C' is 0: set Q to 0, Q' to 1 */
+ s1 = (s1 & ~JKFF_Q) | JKFF_Q0;
+ break;
+ case JKFF_C:
+ /* S' is 0, C' is 1: set Q to 1, Q' to 0 */
+ s1 = (s1 | JKFF_Q) & ~JKFF_Q0;
+ break;
+ case 0:
+ default:
+ /* unstable state (what to do?) */
+ s1 = s1 | JKFF_Q | JKFF_Q0;
+ break;
+ }
+ return static_cast<jkff_t>(s1);
+}
+#endif // JKFF_DEBUG
+
+#endif // _A2JKFF_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2kbd.c b/src/emu/cpu/alto2/a2kbd.c
new file mode 100644
index 00000000000..c677c48d0b9
--- /dev/null
+++ b/src/emu/cpu/alto2/a2kbd.c
@@ -0,0 +1,65 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII memory mapped I/O keyboard
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+/**
+ * @brief read the keyboard address matrix
+ *
+ * @param addr memory mapped I/O address to be read
+ * @return keyboard matrix value for address modulo 4
+ */
+READ16_MEMBER( alto2_cpu_device::kbd_ad_r )
+{
+ UINT16 data = 0177777;
+ switch (offset & 3) {
+ case 0:
+ data = machine().root_device().ioport("ROW0")->read();
+ break;
+ case 1:
+ data = machine().root_device().ioport("ROW1")->read();
+ break;
+ case 2:
+ data = machine().root_device().ioport("ROW2")->read();
+ break;
+ case 3:
+ data = machine().root_device().ioport("ROW3")->read();
+ break;
+ }
+ m_kbd.matrix[offset & 03] = data;
+ if (!space.debugger_access()) {
+ LOG((LOG_KBD,2," read KBDAD+%o (%#o)\n", offset & 3, data));
+ }
+ if (0 == (offset & 3) && (m_kbd.bootkey != 0177777)) {
+ if (!space.debugger_access()) {
+ LOG((0,2," boot keys (%#o & %#o)\n", data, m_kbd.bootkey));
+ }
+ data &= m_kbd.bootkey;
+ m_kbd.bootkey = 0177777;
+ }
+ return data;
+}
+
+void alto2_cpu_device::init_kbd(UINT16 bootkey)
+{
+ m_kbd.bootkey = bootkey;
+}
+
+void alto2_cpu_device::exit_kbd()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_kbd()
+{
+ m_kbd.matrix[0] = 0177777;
+ m_kbd.matrix[1] = 0177777;
+ m_kbd.matrix[2] = 0177777;
+ m_kbd.matrix[3] = 0177777;
+}
diff --git a/src/emu/cpu/alto2/a2kbd.h b/src/emu/cpu/alto2/a2kbd.h
new file mode 100644
index 00000000000..021b7b64333
--- /dev/null
+++ b/src/emu/cpu/alto2/a2kbd.h
@@ -0,0 +1,111 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII keyboard hardware (KBD)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+//! make an Xerox AltoII key bit mask
+#define MAKE_KEY(a,b) (1 << (b))
+
+#define A2_KEY_5 MAKE_KEY(0,017) //!< normal: 5 shifted: %
+#define A2_KEY_4 MAKE_KEY(0,016) //!< normal: 4 shifted: $
+#define A2_KEY_6 MAKE_KEY(0,015) //!< normal: 6 shifted: ~
+#define A2_KEY_E MAKE_KEY(0,014) //!< normal: e shifted: E
+#define A2_KEY_7 MAKE_KEY(0,013) //!< normal: 7 shifted: &
+#define A2_KEY_D MAKE_KEY(0,012) //!< normal: d shifted: D
+#define A2_KEY_U MAKE_KEY(0,011) //!< normal: u shifted: U
+#define A2_KEY_V MAKE_KEY(0,010) //!< normal: v shifted: V
+#define A2_KEY_0 MAKE_KEY(0,007) //!< normal: 0 shifted: )
+#define A2_KEY_K MAKE_KEY(0,006) //!< normal: k shifted: K
+#define A2_KEY_MINUS MAKE_KEY(0,005) //!< normal: - shifted: _
+#define A2_KEY_P MAKE_KEY(0,004) //!< normal: p shifted: P
+#define A2_KEY_SLASH MAKE_KEY(0,003) //!< normal: / shifted: ?
+#define A2_KEY_BACKSLASH MAKE_KEY(0,002) //!< normal: \ shifted: |
+#define A2_KEY_LF MAKE_KEY(0,001) //!< normal: LF
+#define A2_KEY_BS MAKE_KEY(0,000) //!< normal: BS
+
+#define A2_KEY_3 MAKE_KEY(1,017) //!< normal: 3 shifted: #
+#define A2_KEY_2 MAKE_KEY(1,016) //!< normal: 2 shifted: @
+#define A2_KEY_W MAKE_KEY(1,015) //!< normal: w shifted: W
+#define A2_KEY_Q MAKE_KEY(1,014) //!< normal: q shifted: Q
+#define A2_KEY_S MAKE_KEY(1,013) //!< normal: s shifted: S
+#define A2_KEY_A MAKE_KEY(1,012) //!< normal: a shifted: A
+#define A2_KEY_9 MAKE_KEY(1,011) //!< normal: 9 shifted: (
+#define A2_KEY_I MAKE_KEY(1,010) //!< normal: i shifted: I
+#define A2_KEY_X MAKE_KEY(1,007) //!< normal: x shifted: X
+#define A2_KEY_O MAKE_KEY(1,006) //!< normal: o shifted: O
+#define A2_KEY_L MAKE_KEY(1,005) //!< normal: l shifted: L
+#define A2_KEY_COMMA MAKE_KEY(1,004) //!< normal: , shifted: <
+#define A2_KEY_QUOTE MAKE_KEY(1,003) //!< normal: ' shifted: "
+#define A2_KEY_RBRACKET MAKE_KEY(1,002) //!< normal: ] shifted: }
+#define A2_KEY_BLANK_MID MAKE_KEY(1,001) //!< middle blank key
+#define A2_KEY_BLANK_TOP MAKE_KEY(1,000) //!< top blank key
+
+#define A2_KEY_1 MAKE_KEY(2,017) //!< normal: 1 shifted: !
+#define A2_KEY_ESCAPE MAKE_KEY(2,016) //!< normal: ESC shifted: ?
+#define A2_KEY_TAB MAKE_KEY(2,015) //!< normal: TAB shifted: ?
+#define A2_KEY_F MAKE_KEY(2,014) //!< normal: f shifted: F
+#define A2_KEY_CTRL MAKE_KEY(2,013) //!< CTRL
+#define A2_KEY_C MAKE_KEY(2,012) //!< normal: c shifted: C
+#define A2_KEY_J MAKE_KEY(2,011) //!< normal: j shifted: J
+#define A2_KEY_B MAKE_KEY(2,010) //!< normal: b shifted: B
+#define A2_KEY_Z MAKE_KEY(2,007) //!< normal: z shifted: Z
+#define A2_KEY_LSHIFT MAKE_KEY(2,006) //!< LSHIFT
+#define A2_KEY_PERIOD MAKE_KEY(2,005) //!< normal: . shifted: >
+#define A2_KEY_SEMICOLON MAKE_KEY(2,004) //!< normal: ; shifted: :
+#define A2_KEY_RETURN MAKE_KEY(2,003) //!< RETURN
+#define A2_KEY_LEFTARROW MAKE_KEY(2,002) //!< normal: <- shifted: ^ (caret?)
+#define A2_KEY_DEL MAKE_KEY(2,001) //!< normal: DEL
+#define A2_KEY_MSW_2_17 MAKE_KEY(2,000) //!< unused on Microswitch KDB
+
+#define A2_KEY_R MAKE_KEY(3,017) //!< normal: r shifted: R
+#define A2_KEY_T MAKE_KEY(3,016) //!< normal: t shifted: T
+#define A2_KEY_G MAKE_KEY(3,015) //!< normal: g shifted: G
+#define A2_KEY_Y MAKE_KEY(3,014) //!< normal: y shifted: Y
+#define A2_KEY_H MAKE_KEY(3,013) //!< normal: h shifted: H
+#define A2_KEY_8 MAKE_KEY(3,012) //!< normal: 8 shifted: *
+#define A2_KEY_N MAKE_KEY(3,011) //!< normal: n shifted: N
+#define A2_KEY_M MAKE_KEY(3,010) //!< normal: m shifted: M
+#define A2_KEY_LOCK MAKE_KEY(3,007) //!< LOCK
+#define A2_KEY_SPACE MAKE_KEY(3,006) //!< SPACE
+#define A2_KEY_LBRACKET MAKE_KEY(3,005) //!< normal: [ shifted: {
+#define A2_KEY_EQUALS MAKE_KEY(3,004) //!< normal: = shifted: +
+#define A2_KEY_RSHIFT MAKE_KEY(3,003) //!< RSHIFT
+#define A2_KEY_BLANK_BOT MAKE_KEY(3,002) //!< bottom blank key
+#define A2_KEY_MSW_3_16 MAKE_KEY(3,001) //!< unused on Microswitch KDB
+#define A2_KEY_MSW_3_17 MAKE_KEY(3,000) //!< unused on Microswitch KDB
+
+#define A2_KEY_FR2 MAKE_KEY(0,002) //!< ADL right function key 2
+#define A2_KEY_FL2 MAKE_KEY(0,001) //!< ADL left function key 1
+
+#define A2_KEY_FR4 MAKE_KEY(1,001) //!< ADL right funtion key 4
+#define A2_KEY_BW MAKE_KEY(1,000) //!< ADL BW (?)
+
+#define A2_KEY_FR3 MAKE_KEY(2,002) //!< ADL right function key 3
+#define A2_KEY_FL1 MAKE_KEY(2,001) //!< ADL left function key 1
+#define A2_KEY_FL3 MAKE_KEY(2,000) //!< ADL left function key 3
+
+#define A2_KEY_FR1 MAKE_KEY(3,002) //!< ADL right function key 4
+#define A2_KEY_FL4 MAKE_KEY(3,001) //!< ADL left function key 4
+#define A2_KEY_FR5 MAKE_KEY(3,000) //!< ADL right function key 5
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2KBD_H_
+#define _A2KBD_H_
+struct {
+ UINT16 bootkey; //!< boot key - key code pressed before power on
+ UINT16 matrix[4]; //!< a bit map of the keys pressed (ioports ROW0 ... ROW3)
+} m_kbd;
+
+DECLARE_READ16_MEMBER( kbd_ad_r ); //!< read the keyboard matrix
+
+void init_kbd(UINT16 bootkey = 0177777); //!< initialize the keyboard hardware, optinally set the boot key
+void exit_kbd(); //!< deinitialize the keyboard hardware
+void reset_kbd(); //!< reset the keyboard hardware
+#endif // _A2KBD_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2ksec.c b/src/emu/cpu/alto2/a2ksec.c
new file mode 100644
index 00000000000..53ed96e3a6d
--- /dev/null
+++ b/src/emu/cpu/alto2/a2ksec.c
@@ -0,0 +1,56 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII disk sector task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+//! f1_ksec_block early: block the disk sector task
+void alto2_cpu_device::f1_early_ksec_block()
+{
+ LOG((LOG_KSEC,2," BLOCK %s\n", task_name(m_task)));
+ disk_block(m_task);
+}
+
+//! disk sector task slot initialization
+void alto2_cpu_device::init_ksec(int task)
+{
+ set_bs(task, bs_ksec_read_kstat, &alto2_cpu_device::bs_early_read_kstat, 0);
+ set_bs(task, bs_ksec_read_kdata, &alto2_cpu_device::bs_early_read_kdata, 0);
+
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_ksec_block, 0);
+
+ set_f1(task, f1_task_10, 0, 0);
+ set_f1(task, f1_ksec_strobe, 0, &alto2_cpu_device::f1_late_strobe);
+ set_f1(task, f1_ksec_load_kstat, 0, &alto2_cpu_device::f1_late_load_kstat);
+ set_f1(task, f1_ksec_increcno, 0, &alto2_cpu_device::f1_late_increcno);
+ set_f1(task, f1_ksec_clrstat, 0, &alto2_cpu_device::f1_late_clrstat);
+ set_f1(task, f1_ksec_load_kcom, 0, &alto2_cpu_device::f1_late_load_kcom);
+ set_f1(task, f1_ksec_load_kadr, 0, &alto2_cpu_device::f1_late_load_kadr);
+ set_f1(task, f1_ksec_load_kdata, 0, &alto2_cpu_device::f1_late_load_kdata);
+
+ set_f2(task, f2_ksec_init, 0, &alto2_cpu_device::f2_late_init);
+ set_f2(task, f2_ksec_rwc, 0, &alto2_cpu_device::f2_late_rwc);
+ set_f2(task, f2_ksec_recno, 0, &alto2_cpu_device::f2_late_recno);
+ set_f2(task, f2_ksec_xfrdat, 0, &alto2_cpu_device::f2_late_xfrdat);
+ set_f2(task, f2_ksec_swrnrdy, 0, &alto2_cpu_device::f2_late_swrnrdy);
+ set_f2(task, f2_ksec_nfer, 0, &alto2_cpu_device::f2_late_nfer);
+ set_f2(task, f2_ksec_strobon, 0, &alto2_cpu_device::f2_late_strobon);
+ set_f2(task, f2_task_17, 0, 0);
+
+ m_task_wakeup |= 1 << task;
+}
+
+void alto2_cpu_device::exit_ksec()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_ksec()
+{
+ // nothing to do yet
+}
diff --git a/src/emu/cpu/alto2/a2ksec.h b/src/emu/cpu/alto2/a2ksec.h
new file mode 100644
index 00000000000..ffca9c44208
--- /dev/null
+++ b/src/emu/cpu/alto2/a2ksec.h
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII disk sector task (KSEC)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2KSEC_H_
+#define _A2KSEC_H_
+//! BUS source for disk sector task
+enum {
+ bs_ksec_read_kstat = bs_task_3, //!< bus source: read disk status register
+ bs_ksec_read_kdata = bs_task_4 //!< bus source: read disk data register
+};
+
+//! F1 functions for disk sector task
+enum {
+ //!< f1 10: undefined
+ f1_ksec_strobe = f1_task_11, //!< f1 11: strobe
+ f1_ksec_load_kstat = f1_task_12, //!< f1 12: load kstat register
+ f1_ksec_increcno = f1_task_13, //!< f1 13: increment record number
+ f1_ksec_clrstat = f1_task_14, //!< f1 14: clear status register
+ f1_ksec_load_kcom = f1_task_15, //!< f1 15: load kcom register
+ f1_ksec_load_kadr = f1_task_16, //!< f1 16: load kadr register
+ f1_ksec_load_kdata = f1_task_17 //!< f1 17: load kdata register
+};
+
+//! F2 functions for disk sector task
+enum {
+ f2_ksec_init = f2_task_10, //!< f2 10: branches NEXT[5-9] on WDTASKACT && WDINIT
+ f2_ksec_rwc = f2_task_11, //!< f2 11: branches NEXT[8-9] on READ/WRITE/CHECK for record
+ f2_ksec_recno = f2_task_12, //!< f2 12: branches NEXT[8-9] on RECNO[0-1]
+ f2_ksec_xfrdat = f2_task_13, //!< f2 13: branches NEXT[9] on !SEEKONLY
+ f2_ksec_swrnrdy = f2_task_14, //!< f2 14: branches NEXT[9] on !SWRDY
+ f2_ksec_nfer = f2_task_15, //!< f2 15: branches NEXT[9] on !KFER
+ f2_ksec_strobon = f2_task_16, //!< f2 16: branches NEXT[9] on STROBE
+ //!< f2 17: undefined
+};
+
+void f1_early_ksec_block(void); //!< block ksec task
+void init_ksec(int task = task_ksec); //!< initialize the disk sector task
+void exit_ksec(); //!< deinitialize the disk sector task
+void reset_ksec(); //!< reset the disk sector task
+#endif // _A2KSEC_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2kwd.c b/src/emu/cpu/alto2/a2kwd.c
new file mode 100644
index 00000000000..14beeb122b0
--- /dev/null
+++ b/src/emu/cpu/alto2/a2kwd.c
@@ -0,0 +1,54 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII disk word task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+//! f1_kwd_block early: block the disk word task
+void alto2_cpu_device::f1_early_kwd_block()
+{
+ LOG((LOG_KWD,2," BLOCK %s\n", task_name(m_task)));
+ disk_block(m_task);
+}
+
+//! disk word task slot initialization
+void alto2_cpu_device::init_kwd(int task)
+{
+ set_bs(task, bs_kwd_read_kstat, &alto2_cpu_device::bs_early_read_kstat, 0);
+ set_bs(task, bs_kwd_read_kdata, &alto2_cpu_device::bs_early_read_kdata, 0);
+
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_kwd_block, 0);
+
+ set_f1(task, f1_task_10, 0, 0);
+ set_f1(task, f1_kwd_strobe, 0, &alto2_cpu_device::f1_late_strobe);
+ set_f1(task, f1_kwd_load_kstat, 0, &alto2_cpu_device::f1_late_load_kstat);
+ set_f1(task, f1_kwd_increcno, 0, &alto2_cpu_device::f1_late_increcno);
+ set_f1(task, f1_kwd_clrstat, 0, &alto2_cpu_device::f1_late_clrstat);
+ set_f1(task, f1_kwd_load_kcom, 0, &alto2_cpu_device::f1_late_load_kcom);
+ set_f1(task, f1_kwd_load_kadr, 0, &alto2_cpu_device::f1_late_load_kadr);
+ set_f1(task, f1_kwd_load_kdata, 0, &alto2_cpu_device::f1_late_load_kdata);
+
+ set_f2(task, f2_kwd_init, 0, &alto2_cpu_device::f2_late_init);
+ set_f2(task, f2_kwd_rwc, 0, &alto2_cpu_device::f2_late_rwc);
+ set_f2(task, f2_kwd_recno, 0, &alto2_cpu_device::f2_late_recno);
+ set_f2(task, f2_kwd_xfrdat, 0, &alto2_cpu_device::f2_late_xfrdat);
+ set_f2(task, f2_kwd_swrnrdy, 0, &alto2_cpu_device::f2_late_swrnrdy);
+ set_f2(task, f2_kwd_nfer, 0, &alto2_cpu_device::f2_late_nfer);
+ set_f2(task, f2_kwd_strobon, 0, &alto2_cpu_device::f2_late_strobon);
+ set_f2(task, f2_task_17, 0, 0);
+}
+
+void alto2_cpu_device::exit_kwd()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_kwd()
+{
+ // nothing to do yet
+}
diff --git a/src/emu/cpu/alto2/a2kwd.h b/src/emu/cpu/alto2/a2kwd.h
new file mode 100644
index 00000000000..577f4e85a0f
--- /dev/null
+++ b/src/emu/cpu/alto2/a2kwd.h
@@ -0,0 +1,51 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII disk word task (KWD)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2KWD_H_
+#define _A2KWD_H_
+
+//! BUS source for disk word task
+enum {
+ bs_kwd_read_kstat = bs_task_3, //!< bus source: read disk status register
+ bs_kwd_read_kdata = bs_task_4 //!< bus source: read disk data register
+};
+
+//! F1 functions for disk word task
+enum {
+ //!< f1 10: undefined
+ f1_kwd_strobe = f1_task_11, //!< f1 11: strobe
+ f1_kwd_load_kstat = f1_task_12, //!< f1 12: load kstat register
+ f1_kwd_increcno = f1_task_13, //!< f1 13: increment record number
+ f1_kwd_clrstat = f1_task_14, //!< f1 14: clear status register
+ f1_kwd_load_kcom = f1_task_15, //!< f1 15: load kcom register
+ f1_kwd_load_kadr = f1_task_16, //!< f1 16: load kadr register
+ f1_kwd_load_kdata = f1_task_17, //!< f1 17: load kdata register
+};
+
+//! F2 functions for disk word task
+enum {
+ f2_kwd_init = f2_task_10, //!< f2 10: branches NEXT[5-9] on WDTASKACT && WDINIT
+ f2_kwd_rwc = f2_task_11, //!< f2 11: branches NEXT[8-9] on READ/WRITE/CHECK for record
+ f2_kwd_recno = f2_task_12, //!< f2 12: branches NEXT[8-9] on RECNO[0-1]
+ f2_kwd_xfrdat = f2_task_13, //!< f2 13: branches NEXT[9] on !SEEKONLY
+ f2_kwd_swrnrdy = f2_task_14, //!< f2 14: branches NEXT[9] on !SWRDY
+ f2_kwd_nfer = f2_task_15, //!< f2 15: branches NEXT[9] on !KFER
+ f2_kwd_strobon = f2_task_16, //!< f2 16: branches NEXT[9] on STROBE
+ //!< f2 17: undefined
+};
+
+void f1_early_kwd_block(); //!< F1 func: disable the disk word task
+void init_kwd(int task = task_kwd); //!< initialize the disk word task
+void exit_kwd(); //!< deinitialize the disk word task
+void reset_kwd(); //!< reset the disk word task
+#endif // _A2KWD_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2mem.c b/src/emu/cpu/alto2/a2mem.c
new file mode 100644
index 00000000000..e0ae8563821
--- /dev/null
+++ b/src/emu/cpu/alto2/a2mem.c
@@ -0,0 +1,879 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII memory interface
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+#define PUT_EVEN(dword,word) X_WRBITS(dword,32, 0,15,word)
+#define GET_EVEN(dword) X_RDBITS(dword,32, 0,15)
+#define PUT_ODD(dword,word) X_WRBITS(dword,32,16,31,word)
+#define GET_ODD(dword) X_RDBITS(dword,32,16,31)
+
+#define GET_MESR_HAMMING(mesr) X_RDBITS(mesr,16,0,5)
+#define PUT_MESR_HAMMING(mesr,val) X_WRBITS(mesr,16,0,5,val)
+#define GET_MESR_PERR(mesr) X_RDBITS(mesr,16,6,6)
+#define PUT_MESR_PERR(mesr,val) X_WRBITS(mesr,16,6,6,val)
+#define GET_MESR_PARITY(mesr) X_RDBITS(mesr,16,7,7)
+#define PUT_MESR_PARITY(mesr,val) X_WRBITS(mesr,16,7,7,val)
+#define GET_MESR_SYNDROME(mesr) X_RDBITS(mesr,16,8,13)
+#define PUT_MESR_SYNDROME(mesr,val) X_WRBITS(mesr,16,8,13,val)
+#define GET_MESR_BANK(mesr) X_RDBITS(mesr,16,14,15)
+#define PUT_MESR_BANK(mesr,val) X_WRBITS(mesr,16,14,15,val)
+
+#define GET_MECR_SPARE1(mecr,val) X_RDBITS(mecr,16,0,3)
+#define PUT_MECR_SPARE1(mecr,val) X_WRBITS(mecr,16,0,3,val)
+#define GET_MECR_TEST_CODE(mecr) X_RDBITS(mecr,16,4,10)
+#define PUT_MECR_TEST_CODE(mecr,val) X_WRBITS(mecr,16,4,10,val)
+#define GET_MECR_TEST_MODE(mecr) X_RDBITS(mecr,16,11,11)
+#define PUT_MECR_TEST_MODE(mecr,val) X_WRBITS(mecr,16,11,11,val)
+#define GET_MECR_INT_SBERR(mecr) X_RDBITS(mecr,16,12,12)
+#define PUT_MECR_INT_SBERR(mecr,val) X_WRBITS(mecr,16,12,12,val)
+#define GET_MECR_INT_DBERR(mecr) X_RDBITS(mecr,16,13,13)
+#define PUT_MECR_INT_DBERR(mecr,val) X_WRBITS(mecr,16,13,13,val)
+#define GET_MECR_ERRCORR(mecr) X_RDBITS(mecr,16,14,14)
+#define PUT_MECR_ERRCORR(mecr,val) X_WRBITS(mecr,16,14,14,val)
+#define GET_MECR_SPARE2(mecr) X_RDBITS(mecr,16,15,15)
+#define PUT_MECR_SPARE2(mecr,val) X_WRBITS(mecr,16,15,15,val)
+
+/**
+ * <PRE>
+ * AltoII Memory
+ *
+ * Address mapping
+ *
+ * The mapping of addresses to memory chips can be altered by the setting of
+ * the "memory configuration switch". This switch is located at the top of the
+ * backplane of the AltoII. If the switch is in the alternate position, the
+ * first and second 32K portions of memory are exchanged.
+ *
+ * The AltoII memory system is organized around 32-bit doublewords. Stored
+ * along with each doubleword is 6 bits of Hamming code and a Parity bit for
+ * a total of 39 bits:
+ *
+ * bits 0-15 even data word
+ * bits 16-31 odd data word
+ * bits 32-37 Hamming code
+ * bit 38 Parity bit
+ *
+ * Things are further complicated by the fact that two types of memory chips
+ * are used: 16K chips in machines with extended memory and 4K chips for all
+ * others.
+ *
+ * The bits in a 1-word deep slice of memory are called a group. A group
+ * contains 4K oder 16K doublewords, depending on the chip type. The bits of
+ * a group on a single board are called a subgroup. Thus a subgroup contains
+ * 10 of the 40 bits in a group. There are 8 subgroups on a memory board.
+ * Subgroups are numbered from the high 3 bits of the address; for 4K chips
+ * this means MAR[0-2]; for 16K chips (i.e., an Alto with extended memory)
+ * this means BANK,MAR[0]:
+ *
+ * Subgroup Chip Positions
+ * 7 81-90
+ * 6 71-80
+ * 5 61-70
+ * 4 51-60
+ * 3 41-50
+ * 2 31-40
+ * 1 21-30
+ * 0 11-20
+ *
+ * The location of the bits in group 0 is:
+ *
+ * CARD 1 CARD2 CARD3 CARD4
+ * 32 24 16 08 00 33 25 17 09 01 34 26 18 10 02 35 27 19 11 03
+ * 36 28 20 12 04 37 29 21 13 05 38 30 22 14 06 xx 31 23 25 07
+ *
+ * Chips 15, 25, 35, 45, 65, 75 and 85 on board 4 aren't used. If you are out
+ * of replacement memory chips, you can use one of these, but then the board
+ * with the missing chips will only work in Slot 4.
+ *
+ * o WORD = 16 BITS
+ * o ACCESS -> 2 WORDS AT A TIME
+ * o -> 32 BITS + 6 BITS EC + PARITY + SPARE = 40 BITS
+ * o 10 BITS/MODULE 80 DRAMS/MODULE
+ * o 4 MODULES/ALTO 320 DRAMS/ALTO
+ *
+ * ADDRESS A0-6, WE, CAS'
+ * | TO ALL DEVICES
+ * v
+ * +-----------------------------------------+
+ * | ^ 8 DEVICES (32K OR 128K FOR XM) |
+ * | | | CARD 1
+ * /| v <------------ DATA OUT ----------> |
+ * / | 0 1 2 3 4 5 6 7 8 9 |
+ * / +-----------------------------------------+
+ * | H4 H0 28 24 20 16 12 8 4 0
+ * |
+ * | +-----------------------------------------+
+ * | /| | CARD 2
+ * | / +-----------------------------------------+
+ * RAS H5 H1 29 25 21 17 13 9 5 1
+ * 0-7
+ * | \ +-----------------------------------------+
+ * | \| | CARD 3
+ * | +-----------------------------------------+
+ * | P H2 30 26 22 18 14 10 6 2
+ * \
+ * \ +-----------------------------------------+
+ * \| | CARD 4
+ * +-----------------------------------------+
+ * X H3 31 27 23 19 15 11 7 3
+ *
+ * [ ODD WORD ] [ EVEN WORD ]
+ *
+ * <HR>
+ *
+ * 32K x 10 STORAGE MODULE
+ *
+ * Table I
+ *
+ * +-------+-------+-------+---------------+-------+
+ * |CIRCUIT| INPUT | SIGNAL| INVERTER | |
+ * | NO. | PINS | NAME | DEF?? ??? |RESIST.|
+ * +-------+-------+-------+---------------+-------+
+ * | | 71 | RAS0 | A1 1 -> 2 | ?? R2 |
+ * | 1 +-------+-------+---------------+-------+
+ * | | 110 | CS0 | A1 3 -> 4 | ?? R3 |
+ * +-------+-------+-------+---------------+-------+
+ * | | 79 | RAS1 | A2 1 -> 2 | ?? R4 |
+ * | 2 +-------+-------+---------------+-------+
+ * | | 110 | CS1 | A2 3 -> 4 | ?? R5 |
+ * +-------+-------+-------+---------------+-------+
+ * | | 90 | RAS2 | A3 1 -> 2 | ?? R7 |
+ * | 3 +-------+-------+---------------+-------+
+ * | | 110 | CS2 | A3 3 -> 4 | ?? R8 |
+ * +-------+-------+-------+---------------+-------+
+ * | | 86 | RAS3 | A3 11 -> 10 | ?? R9 |
+ * | 4 +-------+-------+---------------+-------+
+ * | | 110 | CS3 | A4 11 -> 10 | ?? R7 |
+ * +-------+-------+-------+---------------+-------+
+ * | | 102 | RAS4 | A4 1 -> 2 | ?? R4 |
+ * | 5 +-------+-------+---------------+-------+
+ * | | 110 | CS4 | A3 13 -> 12 | ?? R5 |
+ * +-------+-------+-------+---------------+-------+
+ * | | 106 | RAS5 | A5 11 -> 10 | ?? R3 |
+ * | 6 +-------+-------+---------------+-------+
+ * | | 110 | CS5 | A5 3 -> 4 | ?? R2 |
+ * +-------+-------+-------+---------------+-------+
+ * | | 111 | RAS6 | A5 1 -> 2 | ?? R8 |
+ * | 7 +-------+-------+---------------+-------+
+ * | | 110 | CS6 | A5 13 -> 12 | ?? R9 |
+ * +-------+-------+-------+---------------+-------+
+ * | | 99 | RAS7 | A4 13 -> 12 | ?? R5 |
+ * | 8 +-------+-------+---------------+-------+
+ * | | 110 | CS7 | A4 3 -> 4 | ?? R5 |
+ * +-------+-------+-------+---------------+-------+
+ *
+ * Table II
+ *
+ * MEMORY CHIP REFERENCE DESIGNATOR
+ *
+ * CIRCUIT NO.
+ * ROW NO. 1 2 3 4 5 6 7 8
+ * +-------+-------+-------+-------+-------+-------+-------+-------+-------+
+ * | 1 | 15 20 | 25 30 | 35 40 | 45 50 | 55 60 | 65 70 | 75 80 | 85 90 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+-------+
+ * | 2 | 14 19 | 24 29 | 34 39 | 44 49 | 54 59 | 64 69 | 64 79 | 84 89 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+-------+
+ * | 3 | 13 18 | 23 28 | 33 38 | 43 48 | 53 58 | 63 68 | 73 78 | 83 88 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+-------+
+ * | 4 | 12 17 | 22 27 | 32 37 | 42 47 | 52 57 | 62 67 | 72 77 | 82 87 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+-------+
+ * | 5 | 11 16 | 21 26 | 31 36 | 41 46 | 52 56 | 61 66 | 71 76 | 81 86 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+-------+
+ *
+ *
+ * The Hamming code generator:
+ *
+ * WDxx is write data bit xx.
+ * H(x) is Hammming code bit x.
+ * HC(x) is generated Hamming code bit x.
+ * HC(x/y) is an intermediate value.
+ * HC(x)A and HC(x)B are also intermediate values.
+ *
+ * Chips used are:
+ * 74S280 9-bit parity generator (A-I inputs, even and odd outputs)
+ * 74S135 EX-OR/EX-NOR gates (5 inputs, 2 outputs)
+ * 74S86 EX-OR gates (2 inputs, 1 output)
+ *
+ * chip A B C D E F G H I even odd
+ * ---------------------------------------------------------------------------------
+ * a75: WD01 WD04 WD08 WD11 WD15 WD19 WD23 WD26 WD30 --- HC(0)A
+ * a76: WD00 WD03 WD06 WD10 WD13 WD17 WD21 WD25 WD28 HC(0B1) ---
+ * a86: WD02 WD05 WD09 WD12 WD16 WD20 WD24 WD27 WD31 HC(1)A ---
+ * a64: WD01 WD02 WD03 WD07 WD08 WD09 WD10 WD14 WD15 --- HC(2)A
+ * a85: WD16 WD17 WD22 WD23 WD24 WD25 WD29 WD30 WD31 HC(2)B ---
+ *
+ * H(0) ^ HC(0)A ^ HC(0B1) -> HC(0)
+ * H(1) ^ HC(1)A ^ HC(0B1) -> HC(1)
+ * HC(2)A ^ HC(2)B ^ H(2) -> HC(2)
+ * H(0) ^ H(1) ^ H(2) -> H(0/2)
+ *
+ * chip A B C D E F G H I even odd
+ * ---------------------------------------------------------------------------------
+ * a66: WD04 WD05 WD06 WD07 WD08 WD09 WD10 H(3) 0 --- HC(3)A
+ * a84: WD18 WD19 WD20 WD21 WD22 WD23 WD24 WD25 0 HC(3/4) HCPA
+ * a63: WD11 WD12 WD13 WD14 WD15 WD16 WD17 H(4) 0 --- HC(4)A
+ * a87: WD26 WD27 WD28 WD29 WD30 WD31 H(5) 0 0 HC(5) HCPB
+ *
+ * HC(3)A ^ HC(3/4) -> HC(3)
+ * HC(4)A ^ HC(3/4) -> HC(4)
+ *
+ * WD00 ^ WD01 -> XX01
+ *
+ * chip A B C D E F G H I even odd
+ * ---------------------------------------------------------------------------------
+ * a54: HC(3)A HC(4)A HCPA HCPB H(0/2) XX01 WD02 WD03 RP PERR ---
+ * a65: WD00 WD01 WD02 WD04 WD05 WD07 WD10 WD11 WD12 --- PCA
+ * a74: WD14 WD17 WD18 WD21 WD23 WD24 WD26 WD27 WD29 PCB ---
+ *
+ * PCA ^ PCB -> PC
+ *
+ * Whoa ;-)
+ * </PRE>
+ */
+#if USE_HAMMING_CHECK
+
+#define WD(x) (1ul<<(31-x))
+
+/* a75: WD01 WD04 WD08 WD11 WD15 WD19 WD23 WD26 WD30 --- HC(0)A */
+#define A75 (WD( 1)|WD( 4)|WD( 8)|WD(11)|WD(15)|WD(19)|WD(23)|WD(26)|WD(30))
+
+/* a76: WD00 WD03 WD06 WD10 WD13 WD17 WD21 WD25 WD29 HC(0B1) --- */
+#define A76 (WD( 0)|WD( 3)|WD( 6)|WD(10)|WD(13)|WD(17)|WD(21)|WD(25)|WD(28))
+
+/* a86: WD02 WD05 WD09 WD12 WD16 WD20 WD24 WD27 WD31 HC(1)A --- */
+#define A86 (WD( 2)|WD( 5)|WD( 9)|WD(12)|WD(16)|WD(20)|WD(24)|WD(27)|WD(31))
+
+/* a64: WD01 WD02 WD03 WD07 WD08 WD09 WD10 WD14 WD15 --- HC(2)A */
+#define A64 (WD( 1)|WD( 2)|WD( 3)|WD( 7)|WD( 8)|WD( 9)|WD(10)|WD(14)|WD(15))
+
+/* a85: WD16 WD17 WD22 WD23 WD24 WD25 WD29 WD30 WD31 HC(2)B --- */
+#define A85 (WD(16)|WD(17)|WD(22)|WD(23)|WD(24)|WD(25)|WD(29)|WD(30)|WD(31))
+
+/* a66: WD04 WD05 WD06 WD07 WD08 WD09 WD10 H(3) 0 --- HC(3)A */
+#define A66 (WD( 4)|WD( 5)|WD( 6)|WD( 7)|WD( 8)|WD( 9)|WD(10))
+
+/* a84: WD18 WD19 WD20 WD21 WD22 WD23 WD24 WD25 0 HC(3/4) HCPA */
+#define A84 (WD(18)|WD(19)|WD(20)|WD(21)|WD(22)|WD(23)|WD(24)|WD(25))
+
+/* a63: WD11 WD12 WD13 WD14 WD15 WD16 WD17 H(4) 0 --- HC(4)A */
+#define A63 (WD(11)|WD(12)|WD(13)|WD(14)|WD(15)|WD(16)|WD(17))
+
+/* a87: WD26 WD27 WD28 WD29 WD30 WD31 H(5) 0 0 HC(5) HCPB */
+#define A87 (WD(26)|WD(27)|WD(28)|WD(29)|WD(30)|WD(31))
+
+/* a54: HC(3)A HC(4)A HCPA HCPB H(0/2) XX01 WD02 WD03 P PERR --- */
+#define A54 (WD( 2)|WD( 3))
+
+/* a65: WD00 WD01 WD02 WD04 WD05 WD07 WD10 WD11 WD12 --- PCA */
+#define A65 (WD( 0)|WD( 1)|WD( 2)|WD( 4)|WD( 5)|WD( 7)|WD(10)|WD(11)|WD(12))
+
+/* a74: WD14 WD17 WD18 WD21 WD23 WD24 WD26 WD27 WD29 PCB --- */
+#define A74 (WD(14)|WD(17)|WD(18)|WD(21)|WD(23)|WD(24)|WD(26)|WD(27)|WD(29))
+
+#define H0(hpb) X_BIT(hpb,8,0) //!< get Hamming code bit 0 from hpb data (really bit 32)
+#define H1(hpb) X_BIT(hpb,8,1) //!< get Hamming code bit 1 from hpb data (really bit 33)
+#define H2(hpb) X_BIT(hpb,8,2) //!< get Hamming code bit 2 from hpb data (really bit 34)
+#define H3(hpb) X_BIT(hpb,8,3) //!< get Hamming code bit 3 from hpb data (really bit 35)
+#define H4(hpb) X_BIT(hpb,8,4) //!< get Hamming code bit 4 from hpb data (really bit 36)
+#define H5(hpb) X_BIT(hpb,8,5) //!< get Hamming code bit 5 from hpb data (really bit 37)
+#define RH(hpb) X_RDBITS(hpb,8,0,5) //!< get Hamming code from hpb data (bits 32 to 37)
+#define RP(hpb) X_BIT(hpb,8,6) //!< get parity bit from hpb data (really bit 38)
+
+/** @brief return even parity of a (masked) 32 bit value */
+static __inline UINT8 parity_even(UINT32 val)
+{
+ val -= ((val >> 1) & 0x55555555);
+ val = (((val >> 2) & 0x33333333) + (val & 0x33333333));
+ val = (((val >> 4) + val) & 0x0f0f0f0f);
+ val += (val >> 8);
+ val += (val >> 16);
+ return (val & 1);
+}
+
+/** @brief return odd parity of a (masked) 32 bit value */
+#define parity_odd(val) (parity_even(val)^1)
+
+/**
+ * @brief lookup table to convert a Hamming syndrome into a bit number to correct
+ */
+static const int hamming_lut[64] = {
+ -1, -1, -1, 0, -1, 1, 2, 3, /* A69: HR(5):0 HR(4):0 HR(3):0 */
+ -1, 4, 5, 6, 7, 8, 9, 10, /* A79: HR(5):0 HR(4):0 HR(3):1 */
+ -1, 11, 12, 13, 14, 15, 16, 17, /* A67: HR(5):0 HR(4):1 HR(3):0 */
+ -1, -1, -1, -1, -1, 1, -1, -1, /* non chip selected */
+ -1, 26, 27, 28, 29, 30, 31, -1, /* A68: HR(5):1 HR(4):0 HR(3):0 */
+ -1, -1, -1, -1, -1, 1, -1, -1, /* non chip selected */
+ 18, 19, 20, 21, 22, 23, 24, 25, /* A78: HR(5):1 HR(4):1 HR(3):0 */
+ -1, -1, -1, -1, -1, 1, -1, -1 /* non chip selected */
+};
+
+/**
+ * @brief read or write a memory double-word and caluclate its Hamming code
+ *
+ * Hamming code generation according to the schematics described above.
+ * It's certainly overkill to do this on a modern PC, but I think we'll
+ * need it for perfect emulation anyways (Hamming code hardware checking).
+ *
+ * @param write non-zero if this is a memory write (don't check for error)
+ * @param dw_addr the double-word address
+ * @param dw_data the double-word data to write
+ * @return dw_data
+ */
+UINT32 alto2_cpu_device::hamming_code(int write, UINT32 dw_addr, UINT32 dw_data)
+{
+ register UINT8 hpb = write ? 0 : m_mem.hpb[dw_addr];
+ register UINT8 hc_0_a;
+ register UINT8 hc_0b1;
+ register UINT8 hc_1_a;
+ register UINT8 hc_2_a;
+ register UINT8 hc_2_b;
+ register UINT8 hc_0;
+ register UINT8 hc_1;
+ register UINT8 hc_2;
+ register UINT8 h_0_2;
+ register UINT8 hc_3_a;
+ register UINT8 hc_3_4;
+ register UINT8 hcpa;
+ register UINT8 hc_4_a;
+ register UINT8 hc_3;
+ register UINT8 hc_4;
+ register UINT8 hc_5;
+ register UINT8 hcpb;
+ register UINT8 perr;
+ register UINT8 pca;
+ register UINT8 pcb;
+ register UINT8 pc;
+ register int syndrome;
+
+ /* a75: WD01 WD04 WD08 WD11 WD15 WD19 WD23 WD26 WD30 --- HC(0)A */
+ hc_0_a = parity_odd (dw_data & A75);
+ /* a76: WD00 WD03 WD06 WD10 WD13 WD17 WD21 WD25 WD29 HC(0B1) --- */
+ hc_0b1 = parity_even(dw_data & A76);
+ /* a86: WD02 WD05 WD09 WD12 WD16 WD20 WD24 WD27 WD31 HC(1)A --- */
+ hc_1_a = parity_even(dw_data & A86);
+ /* a64: WD01 WD02 WD03 WD07 WD08 WD09 WD10 WD14 WD15 --- HC(2)A */
+ hc_2_a = parity_odd (dw_data & A64);
+ /* a85: WD16 WD17 WD22 WD23 WD24 WD25 WD29 WD30 WD31 HC(2)B --- */
+ hc_2_b = parity_even(dw_data & A85);
+
+ hc_0 = H0(hpb) ^ hc_0_a ^ hc_0b1;
+ hc_1 = H1(hpb) ^ hc_1_a ^ hc_0b1;
+ hc_2 = hc_2_a ^ hc_2_b ^ H2(hpb);
+ h_0_2 = H0(hpb) ^ H1(hpb) ^ H2(hpb);
+
+ /* a66: WD04 WD05 WD06 WD07 WD08 WD09 WD10 H(3) 0 --- HC(3)A */
+ hc_3_a = parity_odd ((dw_data & A66) ^ H3(hpb));
+ /* a84: WD18 WD19 WD20 WD21 WD22 WD23 WD24 WD25 0 HC(3/4) HCPA */
+ hcpa = parity_odd (dw_data & A84);
+ hc_3_4 = hcpa ^ 1;
+ /* a63: WD11 WD12 WD13 WD14 WD15 WD16 WD17 H(4) 0 --- HC(4)A */
+ hc_4_a = parity_odd ((dw_data & A63) ^ H4(hpb));
+
+ /* a87: WD26 WD27 WD28 WD29 WD30 WD31 H(5) 0 0 HC(5) HCPB */
+ hcpb = parity_odd ((dw_data & A87) ^ H5(hpb));
+ hc_3 = hc_3_a ^ hc_3_4;
+ hc_4 = hc_4_a ^ hc_3_4;
+ hc_5 = hcpb ^ 1;
+
+ syndrome = (hc_0<<5)|(hc_1<<4)|(hc_2<<3)|(hc_3<<2)|(hc_4<<1)|(hc_5);
+
+ /*
+ * Note: Here I XOR all the non dw_data inputs into bit 0,
+ * which has the same effect as spreading them over some bits
+ * and then counting them... I hope ;-)
+ */
+ /* a54: HC(3)A HC(4)A HCPA HCPB H(0/2) XX01 WD02 WD03 P PERR --- */
+ perr = parity_even(
+ hc_3_a ^
+ hc_4_a ^
+ hcpa ^
+ hcpb ^
+ h_0_2 ^
+ (X_RDBITS(dw_data,32,0,0) ^ X_RDBITS(dw_data,32,1,1)) ^
+ (dw_data & A54) ^
+ RP(hpb) ^
+ 1);
+
+ /* a65: WD00 WD01 WD02 WD04 WD05 WD07 WD10 WD11 WD12 --- PCA */
+ pca = parity_odd (dw_data & A65);
+ /* a74: WD14 WD17 WD18 WD21 WD23 WD24 WD26 WD27 WD29 PCB --- */
+ pcb = parity_even(dw_data & A74);
+ pc = pca ^ pcb;
+
+ if (write) {
+ /* update the hamming code and parity bit store */
+ m_mem.hpb[dw_addr] = (syndrome << 2) | (pc << 1);
+ return dw_data;
+
+ }
+
+ /**
+ * <PRE>
+ * A22 (74H30) 8-input NAND to check for error
+ * input signal
+ * -------------------------
+ * 1 POK = PERR'
+ * 4 NER(08) = HC(0)'
+ * 3 NER(09) = HC(1)'
+ * 2 NER(10) = HC(2)'
+ * 6 NER(11) = HC(3)'
+ * 5 NER(12) = HC(4)'
+ * 12 NER(13) = HC(5)'
+ * 11 1 (VPUL3)
+ *
+ * output signal
+ * -------------------------
+ * 8 ERROR
+ *
+ * Remembering De Morgan this can be simplified:
+ * ERROR is 0, whenever all of PERR and HC(0) to HC(5) are 0.
+ * Or the other way round: any of perr or syndrome non-zero means ERROR=1.
+ * </PRE>
+ */
+ if (perr || syndrome) {
+ /* latch data on the first error */
+ if (!m_mem.error) {
+ m_mem.error = true;
+ PUT_MESR_HAMMING(m_mem.mesr, RH(hpb));
+ PUT_MESR_PERR(m_mem.mesr, perr);
+ PUT_MESR_PARITY(m_mem.mesr, RP(hpb));
+ PUT_MESR_SYNDROME(m_mem.mesr, syndrome);
+ PUT_MESR_BANK(m_mem.mesr, (dw_addr >> 15));
+ /* latch memory address register */
+ m_mem.mear = m_mem.mar & 0177777;
+ LOG((LOG_MEM,5," memory error at dword addr:%07o data:%011o check:%03o\n", dw_addr * 2, dw_data, hpb));
+ LOG((LOG_MEM,6," MEAR: %06o\n", m_mem.mear));
+ LOG((LOG_MEM,6," MESR: %06o\n", m_mem.mesr ^ 0177777));
+ LOG((LOG_MEM,7," Hamming code read : %#o\n", GET_MESR_HAMMING(m_mem.mesr)));
+ LOG((LOG_MEM,7," Parity error : %o\n", GET_MESR_PERR(m_mem.mesr)));
+ LOG((LOG_MEM,7," Memory parity bit : %o\n", GET_MESR_PARITY(m_mem.mesr)));
+ LOG((LOG_MEM,7," Hamming syndrome : %#o (bit #%d)\n", GET_MESR_SYNDROME(m_mem.mesr), hamming_lut[GET_MESR_SYNDROME(m_mem.mesr)]));
+ LOG((LOG_MEM,7," Memory bank : %#o\n", GET_MESR_BANK(m_mem.mesr)));
+ LOG((LOG_MEM,6," MECR: %06o\n", m_mem.mecr ^ 0177777));
+ LOG((LOG_MEM,7," Test Hamming code : %#o\n", GET_MECR_TEST_CODE(m_mem.mecr)));
+ LOG((LOG_MEM,7," Test mode : %s\n", GET_MECR_TEST_MODE(m_mem.mecr) ? "on" : "off"));
+ LOG((LOG_MEM,7," INT on single-bit err: %s\n", GET_MECR_INT_SBERR(m_mem.mecr) ? "on" : "off"));
+ LOG((LOG_MEM,7," INT on double-bit err: %s\n", GET_MECR_INT_DBERR(m_mem.mecr) ? "on" : "off"));
+ LOG((LOG_MEM,7," Error correction : %s\n", GET_MECR_ERRCORR(m_mem.mecr) ? "off" : "on"));
+ }
+ if (-1 == hamming_lut[syndrome]) {
+ /* double-bit error: wake task_part, if we're told so */
+ if (GET_MECR_INT_DBERR(m_mem.mecr))
+ m_task_wakeup |= 1 << task_part;
+ } else {
+ /* single-bit error: wake task_part, if we're told so */
+ if (GET_MECR_INT_SBERR(m_mem.mecr))
+ m_task_wakeup |= 1 << task_part;
+ /* should we correct the single bit error ? */
+ if (0 == GET_MECR_ERRCORR(m_mem.mecr)) {
+ LOG((LOG_MEM,0," correct bit #%d addr:%07o data:%011o check:%03o\n", hamming_lut[syndrome], dw_addr * 2, dw_data, hpb));
+ dw_data ^= 1ul << hamming_lut[syndrome];
+ }
+ }
+ }
+ return dw_data;
+}
+#endif /* USE_HAMMING_CHECK */
+
+/**
+ * @brief memory error address register read
+ *
+ * This register is a 'shadow MAR'; it holds the address of the
+ * first error since the error status was last reset. If no error
+ * has occured, MEAR reports the address of the most recent
+ * memory access. Note that MEAR is set whenever an error of
+ * _any kind_ (single-bit or double-bit) is detected.
+ */
+READ16_MEMBER( alto2_cpu_device::mear_r )
+{
+ int data = m_mem.error ? m_mem.mear : m_mem.mar;
+ if (!space.debugger_access()) {
+ LOG((LOG_MEM,2," MEAR read %07o\n", data));
+ }
+ return data;
+}
+
+/**
+ * @brief memory error status register read
+ *
+ * This register reports specifics of the first error that
+ * occured since MESR was last reset. Storing anything into
+ * this register resets the error logic and enables it to
+ * detect a new error. Bits are "low true", i.e. if the bit
+ * is 0, the conidition is true.
+ * <PRE>
+ * MESR[0-5] Hamming code reported from error
+ * MESR[6] Parity error
+ * MESR[7] Memory parity bit
+ * MESR[8-13] Syndrome bits
+ * MESR[14-15] Bank number in which error occured
+ * </PRE>
+ */
+READ16_MEMBER( alto2_cpu_device::mesr_r )
+{
+ UINT16 data = m_mem.mesr ^ 0177777;
+ if (!space.debugger_access()) {
+ LOG((LOG_MEM,2," MESR read %07o\n", data));
+ LOG((LOG_MEM,6," Hamming code read : %#o\n", GET_MESR_HAMMING(data)));
+ LOG((LOG_MEM,6," Parity error : %o\n", GET_MESR_PERR(data)));
+ LOG((LOG_MEM,6," Memory parity bit : %o\n", GET_MESR_PARITY(data)));
+#if USE_HAMMING_CHECK
+ LOG((LOG_MEM,6," Hamming syndrome : %#o (bit #%d)\n", GET_MESR_SYNDROME(data), hamming_lut[GET_MESR_SYNDROME(data)]));
+#else
+ LOG((LOG_MEM,6," Hamming syndrome : %#o\n", GET_MESR_SYNDROME(data)));
+#endif
+ LOG((LOG_MEM,6," Memory bank : %#o\n", GET_MESR_BANK(data)));
+ }
+ return data;
+}
+
+WRITE16_MEMBER( alto2_cpu_device::mesr_w )
+{
+ if (!space.debugger_access()) {
+ LOG((LOG_MEM,2," MESR write %07o (clear MESR; was %07o)\n", data, m_mem.mesr));
+ }
+ m_mem.mesr = 0; // set all bits to 0
+ m_mem.error = 0; // reset the error flag
+ m_task_wakeup &= ~(1 << task_part); // clear the task wakeup for the parity error task
+}
+
+/**
+ * @brief memory error control register write
+ *
+ * Storing into this register is the means for controlling
+ * the memory error logic. This register is set to all ones
+ * (disable all interrupts) when the alto is bootstrapped
+ * and when the parity error task first detects an error.
+ * When an error has occured, MEAR and MESR should be read
+ * before setting MECR. Bits are "low true", i.e. a 0 bit
+ * enables the condition.
+ *
+ * <PRE>
+ * MECR[0-3] Spare
+ * MECR[4-10] Test hamming code (used only for special diagnostics)
+ * MECR[11] Test mode (used only for special diagnostics)
+ * MECR[12] Cause interrupt on single-bit errors if zero
+ * MECR[13] Cause interrupt on double-bit errors if zero
+ * MECR[14] Do not use error correction if zero
+ * MECR[15] Spare
+ * </PRE>
+ */
+WRITE16_MEMBER( alto2_cpu_device::mecr_w )
+{
+ m_mem.mecr = data ^ 0177777;
+ X_WRBITS(m_mem.mecr,16, 0, 3,0);
+ X_WRBITS(m_mem.mecr,16,15,15,0);
+ if (!space.debugger_access()) {
+ LOG((LOG_MEM,2," MECR write %07o\n", data));
+ LOG((LOG_MEM,6," Test Hamming code : %#o\n", GET_MECR_TEST_CODE(m_mem.mecr)));
+ LOG((LOG_MEM,6," Test mode : %s\n", GET_MECR_TEST_MODE(m_mem.mecr) ? "on" : "off"));
+ LOG((LOG_MEM,6," INT on single-bit err: %s\n", GET_MECR_INT_SBERR(m_mem.mecr) ? "on" : "off"));
+ LOG((LOG_MEM,6," INT on double-bit err: %s\n", GET_MECR_INT_DBERR(m_mem.mecr) ? "on" : "off"));
+ LOG((LOG_MEM,6," Error correction : %s\n", GET_MECR_ERRCORR(m_mem.mecr) ? "off" : "on"));
+ }
+}
+
+/**
+ * @brief memory error control register read
+ */
+READ16_MEMBER( alto2_cpu_device::mecr_r )
+{
+ UINT16 data = m_mem.mecr ^ 0177777;
+ /* set all spare bits */
+ if (!space.debugger_access()) {
+ LOG((LOG_MEM,2," MECR read %07o\n", data));
+ LOG((LOG_MEM,6," Test Hamming code : %#o\n", GET_MECR_TEST_CODE(data)));
+ LOG((LOG_MEM,6," Test mode : %s\n", GET_MECR_TEST_MODE(data) ? "on" : "off"));
+ LOG((LOG_MEM,6," INT on single-bit err: %s\n", GET_MECR_INT_SBERR(data) ? "on" : "off"));
+ LOG((LOG_MEM,6," INT on double-bit err: %s\n", GET_MECR_INT_DBERR(data) ? "on" : "off"));
+ LOG((LOG_MEM,6," Error correction : %s\n", GET_MECR_ERRCORR(data) ? "off" : "on"));
+ }
+ return data;
+}
+
+//! read i/o space RAM
+READ16_MEMBER ( alto2_cpu_device::ioram_r )
+{
+ offs_t dword_addr = offset / 2;
+ return static_cast<UINT16>(offset & 1 ? GET_ODD(m_mem.ram[dword_addr]) : GET_EVEN(m_mem.ram[dword_addr]));
+}
+
+//! write i/o space RAM
+WRITE16_MEMBER( alto2_cpu_device::ioram_w )
+{
+ offs_t dword_addr = offset / 2;
+ if (offset & 1)
+ PUT_ODD(m_mem.ram[dword_addr], data);
+ else
+ PUT_EVEN(m_mem.ram[dword_addr], data);
+}
+
+/**
+ * @brief load the memory address register with some value
+ *
+ * @param rsel selected register (to detect refresh cycles)
+ * @param addr memory address
+ */
+void alto2_cpu_device::load_mar(UINT8 rsel, UINT32 addr)
+{
+ if (rsel == 037) {
+ /*
+ * starting a memory refresh cycle
+ * currently we don't do anything special
+ */
+ LOG((LOG_MEM,5, " MAR<-; refresh cycle @ %#o\n", addr));
+ m_mem.mar = addr;
+ m_mem.access = ALTO2_MEM_REFRESH;
+ m_mem.cycle = cycle();
+ return;
+ }
+
+ m_mem.mar = addr;
+ if (addr < m_mem.size) {
+ LOG((LOG_MEM,2, " MAR<-; mar = %#o\n", addr));
+ m_mem.access = ALTO2_MEM_RAM;
+ // fetch the memory double-word to the read/write latches
+ m_mem.rmdd = m_mem.wmdd = m_mem.ram[m_mem.mar/2];
+ // keep track of the current CPU cycle
+ m_mem.cycle = cycle();
+ } else {
+ m_mem.access = ALTO2_MEM_INVALID;
+ m_mem.rmdd = m_mem.wmdd = ~0;
+ }
+}
+
+/**
+ * @brief read memory or memory mapped I/O from the address in mar to md
+ *
+ * @result returns value from memory (RAM or MMIO)
+ */
+UINT16 alto2_cpu_device::read_mem()
+{
+ UINT32 base_addr;
+
+ if (ALTO2_MEM_NONE == m_mem.access) {
+ LOG((LOG_MEM,0," fatal: mem read with no preceding address\n"));
+ return 0177777;
+ }
+
+ if (cycle() > m_mem.cycle + 4) {
+ LOG((LOG_MEM,0," fatal: mem read (MAR %#o) too late (+%lld cyc)\n", m_mem.mar, cycle() - m_mem.cycle));
+ m_mem.access = ALTO2_MEM_NONE;
+ return 0177777;
+ }
+
+ base_addr = m_mem.mar & 0177777;
+ if (base_addr >= ALTO2_IO_PAGE_BASE && m_mem.mar < ALTO2_RAM_SIZE) {
+ m_mem.md = m_iomem->read_word(m_iomem->address_to_byte(base_addr));
+ LOG((LOG_MEM,6," MD = MMIO[%#o] (%#o)\n", base_addr, m_mem.md));
+ m_mem.access = ALTO2_MEM_NONE;
+#if ALTO2_DEBUG
+ watch_read(m_mem.mar, m_mem.md);
+#endif
+ return m_mem.md;
+ }
+
+#if USE_HAMMING_CHECK
+ /* check for errors on the first access */
+ if (!(m_mem.access & ALTO2_MEM_ODD))
+ m_mem.rmdd = hamming_code(0, m_mem.mar/2, m_mem.rmdd);
+#endif
+ m_mem.md = (m_mem.mar & ALTO2_MEM_ODD) ? GET_ODD(m_mem.rmdd) : GET_EVEN(m_mem.rmdd);
+ LOG((LOG_MEM,6," MD = RAM[%#o] (%#o)\n", m_mem.mar, m_mem.md));
+
+#if ALTO2_DEBUG
+ watch_read(m_mem.mar, m_mem.md);
+#endif
+
+ if (m_mem.access & ALTO2_MEM_ODD) {
+ // after reading the odd word, reset the access flag
+ m_mem.access = ALTO2_MEM_NONE;
+ } else {
+ // after reading the even word word, toggle access flag (and address) to the odd word
+ m_mem.mar ^= ALTO2_MEM_ODD;
+ m_mem.access ^= ALTO2_MEM_ODD;
+ // extend the read succeeds window by one cycle
+ m_mem.cycle++;
+ }
+ return m_mem.md;
+}
+
+/**
+ * @brief write memory or memory mapped I/O from md to the address in mar
+ *
+ * @param data data to write to RAM or MMIO
+ */
+void alto2_cpu_device::write_mem(UINT16 data)
+{
+ int base_addr;
+
+ m_mem.md = data & 0177777;
+ if (ALTO2_MEM_NONE == m_mem.access) {
+ LOG((LOG_MEM,0," fatal: mem write with no preceding address\n"));
+ return;
+ }
+
+ if (cycle() > m_mem.cycle + 4) {
+ LOG((LOG_MEM,0," fatal: mem write (MAR %#o, data %#o) too late (+%lld cyc)\n", m_mem.mar, data, cycle() - m_mem.cycle));
+ m_mem.access = ALTO2_MEM_NONE;
+ return;
+ }
+
+ base_addr = m_mem.mar & 0177777;
+ if (base_addr >= ALTO2_IO_PAGE_BASE && m_mem.mar < ALTO2_RAM_SIZE) {
+ m_iomem->write_word(m_iomem->address_to_byte(base_addr), m_mem.md);
+ LOG((LOG_MEM,6, " MMIO[%#o] = MD (%#o)\n", base_addr, m_mem.md));
+ m_mem.access = ALTO2_MEM_NONE;
+#if ALTO2_DEBUG
+ watch_write(m_mem.mar, m_mem.md);
+#endif
+ return;
+ }
+
+ LOG((LOG_MEM,6, " RAM[%#o] = MD (%#o)\n", m_mem.mar, m_mem.md));
+ if (m_mem.mar & ALTO2_MEM_ODD)
+ PUT_ODD(m_mem.wmdd, m_mem.md);
+ else
+ PUT_EVEN(m_mem.wmdd, m_mem.md);
+
+#if USE_HAMMING_CHECK
+ if (m_mem.access & ALTO2_MEM_RAM)
+ m_mem.ram[m_mem.mar/2] = hamming_code(1, m_mem.mar/2, m_mem.wmdd);
+#else
+ if (m_mem.access & ALTO2_MEM_RAM)
+ m_mem.ram[m_mem.mar/2] = m_mem.wmdd;
+#endif
+
+#if ALTO2_DEBUG
+ watch_write(m_mem.mar, m_mem.md);
+#endif
+ // Toggle the odd/even word access flag
+ // NB: don't reset mem.access to permit double word exchange
+ m_mem.mar ^= ALTO2_MEM_ODD;
+ m_mem.access ^= ALTO2_MEM_ODD;
+ // extend the write succeeds window by one cycle
+ m_mem.cycle++;
+}
+
+/**
+ * @brief debugger interface to read memory
+ *
+ * @param addr address to read
+ * @return memory contents at address (16 bits)
+ */
+UINT16 alto2_cpu_device::debug_read_mem(UINT32 addr)
+{
+ space(AS_2).set_debugger_access(true);
+ int base_addr = addr & 0177777;
+ int data = 0177777;
+ if (base_addr >= ALTO2_IO_PAGE_BASE && addr < ALTO2_RAM_SIZE) {
+ data = m_iomem->read_word(m_iomem->address_to_byte(base_addr));
+ } else {
+ data = (addr & ALTO2_MEM_ODD) ? GET_ODD(m_mem.ram[addr/2]) : GET_EVEN(m_mem.ram[addr/2]);
+ }
+ space(AS_2).set_debugger_access(false);
+ return data;
+}
+
+/**
+ * @brief debugger interface to write memory
+ *
+ * @param addr address to write
+ * @param data data to write (16 bits used)
+ */
+void alto2_cpu_device::debug_write_mem(UINT32 addr, UINT16 data)
+{
+ space(AS_2).set_debugger_access(true);
+ int base_addr = addr & 0177777;
+ if (base_addr >= ALTO2_IO_PAGE_BASE && addr < ALTO2_RAM_SIZE) {
+ m_iomem->write_word(m_iomem->address_to_byte(base_addr), data);
+ } else if (addr & ALTO2_MEM_ODD) {
+ PUT_ODD(m_mem.ram[addr/2], data);
+ } else {
+ PUT_EVEN(m_mem.ram[addr/2], data);
+ }
+ space(AS_2).set_debugger_access(false);
+}
+
+/**
+ * @brief initialize the memory system
+ *
+ * Zeroes the memory context, including RAM and installs dummy
+ * handlers for the memory mapped I/O area.
+ * Sets handlers for access to the memory error address, status,
+ * and control registers at 0177024 to 0177026.
+ */
+void alto2_cpu_device::init_memory()
+{
+ memset(&m_mem, 0, sizeof(m_mem));
+ save_item(NAME(m_mem.mar));
+ save_item(NAME(m_mem.rmdd));
+ save_item(NAME(m_mem.wmdd));
+ save_item(NAME(m_mem.md));
+ save_item(NAME(m_mem.cycle));
+ save_item(NAME(m_mem.access));
+ save_item(NAME(m_mem.error));
+ save_item(NAME(m_mem.mear));
+ save_item(NAME(m_mem.mecr));
+}
+
+void alto2_cpu_device::exit_memory()
+{
+ // no need for this since it free on exit by itself
+ // if (m_mem.ram) {
+ // auto_free(machine(), m_mem.ram);
+ // m_mem.ram = 0;
+ // }
+ // if (m_mem.hpb) {
+ // auto_free(machine(), m_mem.hpb);
+ // m_mem.hpb = 0;
+ // }
+}
+
+void alto2_cpu_device::reset_memory()
+{
+ if (m_mem.ram) {
+ auto_free(machine(), m_mem.ram);
+ m_mem.ram = 0;
+ }
+ if (m_mem.hpb) {
+ auto_free(machine(), m_mem.hpb);
+ m_mem.hpb = 0;
+ }
+ // allocate 64K or 128K words of main memory
+ ioport_port* config = ioport(":CONFIG");
+ // config should be valid, unless the driver doesn't define it
+ if (config)
+ m_mem.size = config->read() & 1 ? ALTO2_RAM_SIZE : 2 * ALTO2_RAM_SIZE;
+ else
+ m_mem.size = ALTO2_RAM_SIZE;
+ logerror("Main memory %u KiB\n", static_cast<UINT32>(sizeof(UINT16) * m_mem.size / 1024));
+
+ m_mem.ram = auto_alloc_array_clear(machine(), UINT32, sizeof(UINT16) * m_mem.size);
+ m_mem.hpb = auto_alloc_array_clear(machine(), UINT8, sizeof(UINT16) * m_mem.size);
+
+#if USE_HAMMING_CHECK
+ // Initialize the hamming codes and parity bit
+ for (UINT32 addr = 0; addr < ALTO2_IO_PAGE_BASE; addr++) {
+ hamming_code(1, addr, 0);
+ hamming_code(1, 0200000 + addr, 0);
+ }
+#endif
+ m_mem.mar = 0;
+ m_mem.rmdd = 0;
+ m_mem.wmdd = 0;
+ m_mem.md = 0;
+ m_mem.cycle = 0;
+ m_mem.access = 0;
+ m_mem.error = false;
+ m_mem.mear = 0;
+ m_mem.mesr = 0;
+ m_mem.mecr = 0;
+}
diff --git a/src/emu/cpu/alto2/a2mem.h b/src/emu/cpu/alto2/a2mem.h
new file mode 100644
index 00000000000..2e0e4fa896b
--- /dev/null
+++ b/src/emu/cpu/alto2/a2mem.h
@@ -0,0 +1,140 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII memory block (MEM)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#define ALTO2_RAM_SIZE 0200000 //!< size of main memory in words
+#define ALTO2_IO_PAGE_BASE 0177000 //!< base address of the memory mapped io range
+#define ALTO2_IO_PAGE_SIZE 0001000 //!< size of the memory mapped io range
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2MEM_H_
+#define _A2MEM_H_
+//! memory access mode
+enum {
+ ALTO2_MEM_NONE,
+ ALTO2_MEM_ODD = (1 << 0),
+ ALTO2_MEM_RAM = (1 << 1),
+ ALTO2_MEM_REFRESH = (1 << 2),
+ ALTO2_MEM_INVALID = (1 << 3)
+};
+
+struct {
+ UINT32 size; //!< main memory size (64K or 128K)
+ UINT32* ram; //!< main memory organized as double-words
+ UINT8* hpb; //!< Hamming Code bits (6) and Parity bits (1) per double word
+ UINT32 mar; //!< memory address register
+ UINT32 rmdd; //!< read memory data double-word
+ UINT32 wmdd; //!< write memory data double-word
+ UINT16 md; //!< memory data register
+ UINT64 cycle; //!< cycle when the memory address register was loaded
+
+ /**
+ * @brief memory access under the way if non-zero
+ * 0: no memory access (MEM_NONE)
+ * 1: invalid
+ * 2: memory access even word (MEM_RAM)
+ * 3: memory access odd word (MEM_RAM | MEM_ODD)
+ */
+ int access;
+ bool error; //!< non-zero after a memory error was detected
+ UINT32 mear; //!< memory error address register
+ UINT16 mesr; //!< memory error status register
+ UINT16 mecr; //!< memory error control register
+} m_mem;
+
+/**
+ * @brief check if memory address register load is yet possible
+ * suspend if accessing RAM and previous MAR<- was less than 5 cycles ago
+ *
+ * 1. MAR<- ANY
+ * 2. REQUIRED
+ * 3. MD<- whatever
+ * 4. SUSPEND
+ * 5. SUSPEND
+ * 6. MAR<- ANY
+ *
+ * @return false, if memory address can be loaded
+ */
+inline bool check_mem_load_mar_stall(UINT8 rsel) {
+ if (ALTO2_MEM_NONE == m_mem.access)
+ return false;
+ return cycle() < m_mem.cycle+5;
+}
+
+/**
+ * @brief check if memory read is yet possible
+ * MAR<- = cycle #1, earliest read at cycle #5, i.e. + 4
+ *
+ * 1. MAR<- ANY
+ * 2. REQUIRED
+ * 3. SUSPEND
+ * 4. SUSPEND
+ * 5. whereever <-MD
+ *
+ * @return false, if memory can be read without wait cycle
+ */
+inline bool check_mem_read_stall() {
+ if (ALTO2_MEM_NONE == m_mem.access)
+ return false;
+ return cycle() < m_mem.cycle+4;
+}
+
+/**
+ * @brief check if memory write is yet possible
+ * MAR<- = cycle #1, earliest write at cycle #3, i.e. + 2
+ *
+ * 1. MAR<- ANY
+ * 2. REQUIRED
+ * 3. OPTIONAL
+ * 4. MD<- whatever
+ *
+ * @return false, if memory can be written without wait cycle
+ */
+inline bool check_mem_write_stall() {
+ if (ALTO2_MEM_NONE == m_mem.access)
+ return false;
+ return cycle() < m_mem.cycle+2;
+}
+
+
+DECLARE_READ16_MEMBER ( mear_r ); //!< memory error address register read
+DECLARE_READ16_MEMBER ( mesr_r ); //!< memory error status register read
+DECLARE_WRITE16_MEMBER( mesr_w ); //!< memory error status register write (clear)
+DECLARE_READ16_MEMBER ( mecr_r ); //!< memory error control register read
+DECLARE_WRITE16_MEMBER( mecr_w ); //!< memory error control register write
+
+//! read or write a memory double-word and caluclate its Hamming code
+UINT32 hamming_code(int write, UINT32 dw_addr, UINT32 dw_data);
+
+//! load the memory address register with some value
+void load_mar(UINT8 rsel, UINT32 addr);
+
+//! read memory or memory mapped I/O from the address in mar to md
+UINT16 read_mem();
+
+//! write memory or memory mapped I/O from md to the address in mar
+void write_mem(UINT16 data);
+
+//! debugger interface to read memory
+UINT16 debug_read_mem(UINT32 addr);
+
+//! debugger interface to write memory
+void debug_write_mem(UINT32 addr, UINT16 data);
+
+#if ALTO2_DEBUG
+void watch_write(UINT32 addr, UINT32 data);
+void watch_read(UINT32 addr, UINT32 data);
+#endif
+
+void init_memory(); //!< initialize the memory system
+void exit_memory(); //!< deinitialize the memory system
+void reset_memory(); //!< reset the memory system
+#endif // _A2MEM_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2mouse.c b/src/emu/cpu/alto2/a2mouse.c
new file mode 100644
index 00000000000..5588063d5f0
--- /dev/null
+++ b/src/emu/cpu/alto2/a2mouse.c
@@ -0,0 +1,273 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII mouse interface
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+#include "a2roms.h"
+
+#define MOUSE_DIRTY_HACK 0
+
+enum {
+ MX1 = (1<<0), //!< MX1 signal is bit 0 (latch bit 1)
+ LMX1 = (1<<1),
+ MX2 = (1<<2), //!< MX2 signal is bit 2 (latch bit 3)
+ LMX2 = (1<<3),
+ MY1 = (1<<4), //!< MY1 signal is bit 4 (latch bit 5)
+ LMY1 = (1<<5),
+ MY2 = (1<<6), //!< MY2 signal is bit 6 (latch bit 7)
+ LMY2 = (1<<7),
+ MACTIVE = (MX1|MX2|MY1|MY2), //!< mask for the active bits
+ MLATCH = (LMX1|LMX2|LMY1|LMY2) //!< mask for the latched bits
+};
+
+/**
+ * <PRE>
+ * The mouse inputs from the shutters are connected to a quad
+ * 2/3 input RS flip flop (SN74279).
+ *
+ * 74279
+ * +---+--+---+
+ * | +--+ |
+ * R1 -|1 16|- Vcc
+ * | |
+ * S1a -|2 15|- S4
+ * | |
+ * S1b -|3 14|- R4
+ * | |
+ * Q1 -|4 13|- Q4
+ * | |
+ * R2 -|5 12|- S3a
+ * | |
+ * S2 -|6 11|- S3b
+ * | |
+ * Q2 -|7 10|- R3
+ * | |
+ * GND -|8 9|- Q3
+ * | |
+ * +----------+
+ *
+ * The 'Y' Encoder signals are connected to IC1:
+ * shutter pin(s) R/S output
+ * ------------------------------------
+ * 0 2,3 S1a,b Q1 MX2 -> 1
+ * 1 1 R1 Q1 MX2 -> 0
+ * 2 5 R2 Q2 MX1 -> 0
+ * 3 6 S2 Q2 MX1 -> 1
+ *
+ * The 'X' Encoder signals are connected to IC2:
+ * shutter pin(s) R/S output
+ * ------------------------------------
+ * 0 2,3 S1a,b Q1 MY2 -> 1
+ * 1 1 R1 Q1 MY2 -> 0
+ * 2 5 R2 Q2 MY1 -> 0
+ * 3 6 S2 Q2 MY1 -> 1
+ *
+ *
+ * The pulse train generated by a left or up rotation is:
+ *
+ * +---+ +---+ +---+
+ * MX1/MY1 | | | | | |
+ * ---+ +---+ +---+ +---
+ *
+ * +---+ +---+ +---+ +-
+ * MX2/MY2 | | | | | | |
+ * -+ +---+ +---+ +---+
+ *
+ *
+ * The pulse train generated by a right or down rotation is:
+ *
+ * +---+ +---+ +---+ +-
+ * MX1/MY1 | | | | | | |
+ * -+ +---+ +---+ +---+
+ *
+ * +---+ +---+ +---+
+ * MX2/MY2 | | | | | |
+ * ---+ +---+ +---+ +---
+ *
+ * In order to simulate the shutter sequence for the mouse motions
+ * we have to generate a sequence of pulses on MX1/MX2 and MY1/MY2
+ * that have their phases shifted by 90 degree.
+ * </PRE>
+ */
+
+
+#define MOVEX(x) ((((x) < 0) ? MY2 : ((x) > 0) ? MY1 : 0))
+#define MOVEY(y) ((((y) < 0) ? MX2 : ((y) > 0) ? MX1 : 0))
+#define SIGN(a) ((a) < 0 ? -1 : (a) > 0 ? 1 : 0)
+
+/**
+ * @brief return the mouse motion flags
+ *
+ * Advance the mouse x and y coordinates to the dx and dy
+ * coordinates by either toggling MX2 or MX1 first for a
+ * y movement, or MY2 or MY1 for x movement.
+ * There are four read phases counted by m_mouse.phase
+ *
+ * @return lookup value from madr_a32
+ */
+UINT16 alto2_cpu_device::mouse_read()
+{
+ UINT16 data;
+
+ m_mouse.latch = (m_mouse.latch << 1) & MLATCH;
+ data = m_madr_a32[m_mouse.latch];
+
+ switch (m_mouse.phase) {
+ case 0:
+ m_mouse.latch |= MOVEX(m_mouse.dx - m_mouse.x);
+ m_mouse.latch |= MOVEY(m_mouse.dy - m_mouse.y);
+ break;
+ case 1:
+ m_mouse.latch |= MACTIVE;
+ m_mouse.x -= SIGN(m_mouse.x - m_mouse.dx);
+ m_mouse.y -= SIGN(m_mouse.y - m_mouse.dy);
+ break;
+ case 2:
+ m_mouse.latch ^= MOVEX(m_mouse.dx - m_mouse.x);
+ m_mouse.latch ^= MOVEY(m_mouse.dy - m_mouse.y);
+ break;
+ default:
+ m_mouse.latch &= ~MACTIVE;
+ m_mouse.x -= SIGN(m_mouse.x - m_mouse.dx);
+ m_mouse.y -= SIGN(m_mouse.y - m_mouse.dy);
+ }
+ m_mouse.phase = (m_mouse.phase + 1) % 4;
+ return data;
+}
+
+/**
+ * @brief register a mouse motion in x direction
+ * @param ioport_field reference to the field
+ * @param param pointer passed in PORT_CHANGED_MEMBER last parameter
+ * @param oldval the old ioport_value
+ * @param newval the new ioport_value
+ */
+INPUT_CHANGED_MEMBER( alto2_cpu_device::mouse_motion_x )
+{
+ // set new destination (absolute) mouse x coordinate
+ INT32 x = m_mouse.dx + newval - oldval;
+ x = x < 0 ? 0 : x > 605 ? 605 : x;
+ m_mouse.dx = x;
+#if MOUSE_DIRTY_HACK
+ /* XXX: dirty, dirty, hack */
+#if USE_HAMMING_CHECK
+ m_mem.ram[0424/2] = hamming_code(1, 0424 / 2, (m_mouse.dx << 16) | m_mouse.dy);
+#else
+ m_mem.ram[0424/2] = (m_mouse.dx << 16) | m_mouse.dy;
+#endif
+#endif
+}
+
+/**
+ * @brief register a mouse motion in y direction
+ * @param ioport_field reference to the field
+ * @param param pointer passed in PORT_CHANGED_MEMBER last parameter
+ * @param oldval the old ioport_value
+ * @param newval the new ioport_value
+ */
+INPUT_CHANGED_MEMBER( alto2_cpu_device::mouse_motion_y )
+{
+ // set new destination (absolute) mouse y coordinate
+ INT32 y = m_mouse.dy + newval - oldval;
+ y = y < 0 ? 0 : y > 807 ? 807 : y;
+ m_mouse.dy = y;
+#if MOUSE_DIRTY_HACK
+ /* XXX: dirty, dirty, hack */
+#if USE_HAMMING_CHECK
+ m_mem.ram[0424/2] = hamming_code(1, 0424 / 2, (m_mouse.dx << 16) | m_mouse.dy);
+#else
+ m_mem.ram[0424/2] = (m_mouse.dx << 16) | m_mouse.dy;
+#endif
+#endif
+}
+
+/**
+ * @brief register a mouse button change
+ *
+ * convert button bit to UTILIN[13-15]
+ *
+ * @param ioport_field reference to the field
+ * @param param pointer passed in PORT_CHANGED_MEMBER last parameter
+ * @param oldval the old ioport_value
+ * @param newval the new ioport_value
+ */
+INPUT_CHANGED_MEMBER( alto2_cpu_device::mouse_button_0 )
+{
+ X_WRBITS(m_hw.utilin,16,13,13,newval);
+}
+
+INPUT_CHANGED_MEMBER( alto2_cpu_device::mouse_button_1 )
+{
+ X_WRBITS(m_hw.utilin,16,14,14,newval);
+}
+
+INPUT_CHANGED_MEMBER( alto2_cpu_device::mouse_button_2 )
+{
+ X_WRBITS(m_hw.utilin,16,15,15,newval);
+}
+
+static const prom_load_t pl_madr_a32 =
+{
+ "madr.a32",
+ 0,
+ "a0e3b4a7",
+ "24e50afdeb637a6a8588f8d3a3493c9188b8da2c",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 017, // invert D0-D3
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_REVERSE_0_3, // reverse D0-D3 to D3-D0
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+/**
+ * @brief initialize the mouse context to useful values
+ *
+ * From the Alto Hardware Manual:
+ * <PRE>
+ * The mouse is a hand-held pointing device which contains two encoders
+ * which digitize its position as it is rolled over a table-top. It also
+ * has three buttons which may be read as the three low order bits of
+ * memory location UTILIN (0177030), iin the manner of the keyboard.
+ * The bit/button correspondence in UTILIN are (depressed keys
+ * correspond to 0's in memory):
+ *
+ * UTILIN[13] TOP or LEFT button (RED)
+ * UTILIN[14] BOTTOM or RIGHT button (BLUE)
+ * UTILIN[15] MIDDLE button (YELLOW)
+ *
+ * The mouse coordinates are maintained by the MRT microcode in locations
+ * MOUSELOC(0424)=X and MOUSELOC+1(0425)=Y in page one of the Alto memory.
+ * These coordinates are relative, i.e., the hardware only increments and
+ * decrements them. The resolution of the mouse is approximately 100 points
+ * per inch.
+ * </PRE>
+ */
+void alto2_cpu_device::init_mouse()
+{
+ memset(&m_mouse, 0, sizeof(m_mouse));
+ m_madr_a32 = prom_load(machine(), &pl_madr_a32, memregion("madr_a32")->base());
+}
+
+void alto2_cpu_device::exit_mouse()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_mouse()
+{
+ m_mouse.x = 0;
+ m_mouse.y = 0;
+ m_mouse.dx = 0;
+ m_mouse.dy = 0;
+ m_mouse.latch = 0;
+ m_mouse.phase = 0;
+}
diff --git a/src/emu/cpu/alto2/a2mouse.h b/src/emu/cpu/alto2/a2mouse.h
new file mode 100644
index 00000000000..b7f56c29de6
--- /dev/null
+++ b/src/emu/cpu/alto2/a2mouse.h
@@ -0,0 +1,73 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII mouse hardware (MOUSE)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2MOUSE_H_
+#define _A2MOUSE_H_
+/**
+ * @brief PROM madr.a32 contains a lookup table to translate mouse motions
+ *
+ * <PRE>
+ * The 4 mouse motion signals MX1, MX2, MY1, and MY2 are connected
+ * to a 256x4 PROM's (3601, SN74387) address lines A0, A2, A4, and A6.
+ * The previous (latched) state of the 4 signals is connected to the
+ * address lines A1, A3, A5, and A7.
+ *
+ * SN74387
+ * +---+--+---+
+ * | +--+ |
+ * MY2 A6 -|1 16|- Vcc
+ * | |
+ * LMY1 A5 -|2 15|- A7 LMY2
+ * | |
+ * MY1 A4 -|3 14|- FE1' 0
+ * | |
+ * LMX2 A3 -|4 13|- FE2' 0
+ * | |
+ * MX1 A0 -|5 12|- D0 BUS[12]
+ * | |
+ * LMX1 A1 -|6 11|- D1 BUS[13]
+ * | |
+ * MX2 A2 -|7 10|- D2 BUS[14]
+ * | |
+ * GND -|8 9|- D3 BUS[15]
+ * | |
+ * +----------+
+ *
+ * A motion to the west will first toggle MX2, then MX1.
+ * sequence: 04 -> 0d -> 0b -> 02
+ * A motion to the east will first toggle MX1, then MX2.
+ * sequence: 01 -> 07 -> 0e -> 08
+ *
+ * A motion to the north will first toggle MY2, then MY1.
+ * sequence: 40 -> d0 -> b0 -> 20
+ * A motion to the south will first toggle MY1, then MY2.
+ * sequence: 10 -> 70 -> e0 -> 80
+ * </PRE>
+ */
+UINT8* m_madr_a32;
+
+//! mouse context
+struct {
+ int x; //!< current X coordinate
+ int y; //!< current Y coordinate
+ int dx; //!< destination X coordinate (real mouse X)
+ int dy; //!< destination Y coordinate (real mouse Y)
+ UINT8 latch; //!< current latch value
+ UINT8 phase; //!< current read latch phase
+} m_mouse;
+
+UINT16 mouse_read(); //!< return the mouse motion flags
+void init_mouse(); //!< initialize the mouse context
+void exit_mouse(); //!< deinitialize the mouse context
+void reset_mouse(); //!< reset the mouse context
+#endif // _A2MOUSE_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2mrt.c b/src/emu/cpu/alto2/a2mrt.c
new file mode 100644
index 00000000000..51d3a34b0bb
--- /dev/null
+++ b/src/emu/cpu/alto2/a2mrt.c
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII memory refresh task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+//! f1_mrt_block early: block the display word task
+void alto2_cpu_device::f1_early_mrt_block()
+{
+ /* clear the wakeup for the memory refresh task */
+ m_task_wakeup &= ~(1 << m_task);
+ LOG((LOG_MRT,2," BLOCK %s\n", task_name(m_task)));
+}
+
+//! called by the CPU when MRT becomes active
+void alto2_cpu_device::activate_mrt()
+{
+ m_task_wakeup &= ~(1 << m_task);
+ if (m_ewfct)
+ {
+ // The Ether task wants a wakeup, too
+ m_task_wakeup |= 1 << task_ether;
+ }
+}
+
+ //! memory refresh task slots initialization
+void alto2_cpu_device::init_mrt(int task)
+{
+ set_f1(task, f1_block, &alto2_cpu_device::f1_early_mrt_block, 0);
+ /* auto block */
+ m_active_callback[task] = &alto2_cpu_device::activate_mrt;
+}
+
+void alto2_cpu_device::exit_mrt()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_mrt()
+{
+ // nothing to do yet
+}
diff --git a/src/emu/cpu/alto2/a2mrt.h b/src/emu/cpu/alto2/a2mrt.h
new file mode 100644
index 00000000000..647f6e63666
--- /dev/null
+++ b/src/emu/cpu/alto2/a2mrt.h
@@ -0,0 +1,21 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII memory refresh task (MRT)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2MRT_H_
+#define _A2MRT_H_
+void f1_early_mrt_block(); //!< F1 func: block the display word task
+void activate_mrt(); //!< called by the CPU when MRT becomes active
+void init_mrt(int task = task_mrt); //!< initialize the memory refresh task
+void exit_mrt(); //!< deinitialize the memory refresh task
+void reset_mrt(); //!< reset the memory refresh task
+#endif // _A2MRT_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2part.c b/src/emu/cpu/alto2/a2part.c
new file mode 100644
index 00000000000..52c8f61d69f
--- /dev/null
+++ b/src/emu/cpu/alto2/a2part.c
@@ -0,0 +1,32 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII parity task
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+//! called by the CPU when the parity task becomes active
+void alto2_cpu_device::activate_part()
+{
+ m_task_wakeup &= ~(1 << m_task);
+}
+
+//! parity task slots initialization
+void alto2_cpu_device::init_part(int task)
+{
+ m_active_callback[task] = &alto2_cpu_device::activate_part;
+}
+
+void alto2_cpu_device::exit_part()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_part()
+{
+ // nothing to do yet
+}
diff --git a/src/emu/cpu/alto2/a2part.h b/src/emu/cpu/alto2/a2part.h
new file mode 100644
index 00000000000..510348eaddd
--- /dev/null
+++ b/src/emu/cpu/alto2/a2part.h
@@ -0,0 +1,20 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII parity task (PART)
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2PART_H_
+#define _A2PART_H_
+void activate_part();
+void init_part(int task = task_part); //!< initialize the parity task
+void exit_part(); //!< deinitialize the parity task
+void reset_part(); //!< reset the parity task
+#endif // _A2PART_H_
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2ram.c b/src/emu/cpu/alto2/a2ram.c
new file mode 100644
index 00000000000..d7f68343bcc
--- /dev/null
+++ b/src/emu/cpu/alto2/a2ram.c
@@ -0,0 +1,440 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII RAM related functions
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+#define DEBUG_WRTRAM 0 //!< define to 1 to printf disassembled CRAM writes
+
+//! direct read access to the microcode CRAM
+#define RD_CRAM(addr) (*reinterpret_cast<UINT32 *>(m_ucode_cram + addr * 4))
+
+//! direct write access to the microcode CRAM
+#define WR_CRAM(addr,data) do { \
+ *reinterpret_cast<UINT32 *>(m_ucode_cram + addr * 4) = data; \
+} while (0)
+
+/**
+ * @brief read the microcode ROM/RAM halfword
+ *
+ * Note: HALFSEL is selecting the even (0) or odd (1) half of the
+ * microcode RAM 32-bit word. Here's how the demultiplexers (74298)
+ * u8, u18, u28 and u38 select the bits:
+ *
+ * SN74298
+ * +---+-+---+
+ * | +-+ |
+ * B2 -|1 16|- Vcc
+ * | |
+ * A2 -|2 15|- QA
+ * | |
+ * A1 -|3 14|- QB
+ * | |
+ * B1 -|4 13|- QC
+ * | |
+ * C2 -|5 12|- QD
+ * | |
+ * D2 -|6 11|- CLK
+ * | |
+ * D1 -|7 10|- SEL
+ * | |
+ * GND -|8 9|- C1
+ * | |
+ * +---------+
+ *
+ * chip out pin BUS in pin HSEL=0 in pin HSEL=1
+ * --------------------------------------------------------------
+ * u8 QA 15 0 A1 3 DRSEL(0)' A2 2 DF2(0)
+ * u8 QB 14 1 B1 4 DRSEL(1)' B2 1 DF2(1)'
+ * u8 QC 13 2 C1 9 DRSEL(2)' C2 5 DF2(2)'
+ * u8 QD 12 3 D1 7 DRSEL(3)' D2 6 DF2(3)'
+ *
+ * u18 QA 15 4 A1 3 DRSEL(4)' A2 2 LOADT'
+ * u18 QB 14 5 B1 4 DALUF(0)' B2 1 LOADL
+ * u18 QC 13 6 C1 9 DALUF(1)' C2 5 NEXT(00)'
+ * u18 QD 12 7 D1 7 DALUF(2)' D2 6 NEXT(01)'
+ *
+ * u28 QA 15 8 A1 3 DALUF(3)' A2 2 NEXT(02)'
+ * u28 QB 14 9 B1 4 DBS(0)' B2 1 NEXT(03)'
+ * u28 QC 13 10 C1 9 DBS(1)' C2 5 NEXT(04)'
+ * u28 QD 12 11 D1 7 DBS(2)' D2 6 NEXT(05)'
+ *
+ * u38 QA 15 12 A1 3 DF1(0) A2 2 NEXT(06)'
+ * u38 QB 14 13 B1 4 DF1(1)' B2 1 NEXT(07)'
+ * u38 QC 13 14 C1 9 DF1(2)' C2 5 NEXT(08)'
+ * u38 QD 12 15 D1 7 DF1(3)' D2 6 NEXT(09)'
+ *
+ * The HALFSEL signal to the demultiplexers is the inverted bit BUS(5):
+ * BUS(5)=1, HALFSEL=0, A1,B1,C1,D1 inputs, upper half of the 32-bit word
+ * BUS(5)=0, HALFSEL=1, A2,B2,C2,D2 inputs, lower half of the 32-bit word
+ */
+void alto2_cpu_device::rdram()
+{
+ UINT32 addr, value;
+ UINT32 bank = GET_CRAM_BANKSEL(m_cram_addr);
+ UINT32 wordaddr = GET_CRAM_WORDADDR(m_cram_addr);
+
+ if (GET_CRAM_RAMROM(m_cram_addr)) {
+ /* read CROM 0 at current mpc */
+ addr = m_mpc & ALTO2_UCODE_PAGE_MASK;
+ LOG((LOG_CPU,0," rdram: ROM [%05o] ", addr));
+ } else {
+ /* read CRAM[bank] */
+ addr = bank * ALTO2_UCODE_PAGE_SIZE + wordaddr;
+ LOG((LOG_CPU,0," rdram: RAM%d [%04o] ", bank, wordaddr));
+ }
+
+ m_rdram_flag = false;
+ if (ALTO2_UCODE_RAM_BASE + addr >= ALTO2_UCODE_SIZE) {
+ value = 0177777; /* ??? */
+ LOG((LOG_CPU,0,"invalid address (%06o)\n", addr));
+ return;
+ }
+ value = RD_CRAM(addr) ^ ALTO2_UCODE_INVERTED;
+ if (GET_CRAM_HALFSEL(m_cram_addr)) {
+ value = value >> 16;
+ LOG((LOG_CPU,0,"upper:%06o\n", value & 0177777));
+ } else {
+ LOG((LOG_CPU,0,"lower:%06o\n", value & 0177777));
+ }
+ m_bus &= value;
+}
+
+/**
+ * @brief write the microcode RAM from M register and ALU
+ *
+ * Note: M is a latch (MYL, i.e. memory L) on the CRAM board that latches
+ * the ALU whenever LOADL and GOODTASK are met. GOODTASK is the Emulator
+ * task and something I have not yet found out about: TASKA' and TASKB'.
+ *
+ * There's also an undumped PROM u21 which is addressed by GOODTASK and
+ * 7 other signals...
+ */
+void alto2_cpu_device::wrtram()
+{
+ UINT32 bank = GET_CRAM_BANKSEL(m_cram_addr);
+ UINT32 wordaddr = GET_CRAM_WORDADDR(m_cram_addr);
+ UINT32 value = ((m_m << 16) | m_alu) ^ ALTO2_UCODE_INVERTED;
+
+ UINT32 addr = bank * ALTO2_UCODE_PAGE_SIZE + wordaddr; // write RAM 0,1,2
+ LOG((LOG_CPU,0," wrtram: RAM%d [%04o] upper:%06o lower:%06o", bank, wordaddr, m_m, m_alu));
+
+#if DEBUG_WRTRAM
+ char buff[128];
+ UINT8 oprom[4];
+ oprom[0] = m_m / 256;
+ oprom[1] = m_m % 256;
+ oprom[2] = m_m / 256;
+ oprom[3] = m_m % 256;
+ disasm_disassemble(buff, addr, oprom, oprom, 0);
+ printf("WR CRAM_BANKSEL=%d RAM%d [%04o] upper:%06o lower:%06o *** %s\n",
+ GET_CRAM_BANKSEL(m_cram_addr), bank, wordaddr, m_m, m_alu, buff);
+#endif
+
+ m_wrtram_flag = false;
+ if (ALTO2_UCODE_RAM_BASE + addr >= ALTO2_UCODE_SIZE) {
+ LOG((LOG_CPU,0," invalid address %06o\n", addr));
+ return;
+ }
+ LOG((LOG_CPU,0,"\n"));
+ WR_CRAM(addr, value);
+}
+
+/**
+ * @brief bs_read_sreg early: drive bus by S register or M (MYL), if rsel is = 0
+ *
+ * Note: RSEL == 0 can't be read, because it is decoded as
+ * access to the M register (MYL latch access, LREF' in the schematics)
+ */
+void alto2_cpu_device::bs_early_read_sreg()
+{
+ UINT16 r;
+
+ if (m_d_rsel) {
+ UINT8 bank = m_s_reg_bank[m_task];
+ r = m_s[bank][m_d_rsel];
+ LOG((LOG_RAM,2," <-S%02o; bus &= S[%o][%02o] (%#o)\n", m_d_rsel, bank, m_d_rsel, r));
+ } else {
+ r = m_m;
+ LOG((LOG_RAM,2," <-S%02o; bus &= M (%#o)\n", m_d_rsel, r));
+ }
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_load_sreg early: load S register puts garbage on the bus
+ */
+void alto2_cpu_device::bs_early_load_sreg()
+{
+ int r = 0; /* ??? */
+ LOG((LOG_RAM,2," S%02o<- BUS &= garbage (%#o)\n", m_d_rsel, r));
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_load_sreg late: load S register from M
+ */
+void alto2_cpu_device::bs_late_load_sreg()
+{
+ UINT8 bank = m_s_reg_bank[m_task];
+ m_s[bank][m_d_rsel] = m_m;
+ LOG((LOG_RAM,2," S%02o<- S[%o][%02o] := %#o\n", m_d_rsel, bank, m_d_rsel, m_m));
+}
+
+/**
+ * @brief branch to ROM page
+ */
+void alto2_cpu_device::branch_ROM(const char *from, int page)
+{
+ (void)from;
+ m_next2 = (m_next2 & ALTO2_UCODE_PAGE_MASK) + page * ALTO2_UCODE_PAGE_SIZE;
+ LOG((LOG_RAM,2," SWMODE: branch from %s to ROM%d (%#o)\n", from, page, m_next2));
+}
+
+/**
+ * @brief branch to RAM page
+ */
+void alto2_cpu_device::branch_RAM(const char *from, int page)
+{
+ (void)from;
+ m_next2 = (m_next2 & ALTO2_UCODE_PAGE_MASK) + ALTO2_UCODE_RAM_BASE + page * ALTO2_UCODE_PAGE_SIZE;
+ LOG((LOG_RAM,2," SWMODE: branch from %s to RAM%d\n", from, page, m_next2));
+}
+
+/**
+ * @brief f1_swmode early: switch to micro program counter BUS[6-15] in other bank
+ *
+ * Note: Jumping to uninitialized CRAM
+ *
+ * When jumping to uninitialized RAM, which, because of the inverted bits of the
+ * microcode words F1(0), F2(0) and LOADL, it is then read as F1=010 (SWMODE),
+ * F2=010 (BUSODD) and LOADL=1, loading the M register (MYL latch), too.
+ * This causes control to go back to the Emulator task at 0, because the
+ * NEXT[0-9] of uninitialized RAM is 0.
+ *
+ */
+void alto2_cpu_device::f1_late_swmode()
+{
+ /* currently executing in what page? */
+ UINT16 current = m_mpc / ALTO2_UCODE_PAGE_SIZE;
+
+#if (ALTO2_UCODE_ROM_PAGES == 1 && ALTO2_UCODE_RAM_PAGES == 1)
+ switch (current) {
+ case 0:
+ branch_RAM("ROM0", 0);
+ break;
+ case 1:
+ branch_ROM("RAM0", 0);
+ break;
+ default:
+ fatal(1, "Impossible current mpc %d\n", current);
+ }
+#endif
+#if (ALTO2_UCODE_ROM_PAGES == 2 && ALTO2_UCODE_RAM_PAGES == 1)
+ UINT16 next = X_RDBITS(m_next2,10,1,1);
+
+ switch (current) {
+ case 0: /* ROM0 to RAM0 or ROM1 */
+ switch (next) {
+ case 0:
+ branch_RAM("ROM0", 0);
+ break;
+ case 1:
+ branch_ROM("ROM0", 1);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 1: /* ROM1 to ROM0 or RAM0 */
+ switch (next) {
+ case 0:
+ branch_ROM("ROM1", 0);
+ break;
+ case 1:
+ branch_RAM("ROM1", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 2: /* RAM0 to ROM0 or ROM1 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM0", 0);
+ break;
+ case 1:
+ branch_ROM("RAM0", 1);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ default:
+ fatal(1, "Impossible current mpc %d\n", current);
+ }
+#endif
+#if (ALTO2_UCODE_ROM_PAGES == 1 && ALTO2_UCODE_RAM_PAGES == 3)
+ UINT16 next = X_RDBITS(m_next2,10,1,2);
+
+ switch (current) {
+ case 0: /* ROM0 to RAM0, RAM2, RAM1, RAM0 */
+ switch (next) {
+ case 0:
+ branch_RAM("ROM0", 0);
+ break;
+ case 1:
+ branch_RAM("ROM0", 2);
+ break;
+ case 2:
+ branch_RAM("ROM0", 1);
+ break;
+ case 3:
+ branch_RAM("ROM0", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 1: /* RAM0 to ROM0, RAM2, RAM1, RAM1 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM0", 0);
+ break;
+ case 1:
+ branch_RAM("RAM0", 2);
+ break;
+ case 2:
+ branch_RAM("RAM0", 1);
+ break;
+ case 3:
+ branch_RAM("RAM0", 1);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 2: /* RAM1 to ROM0, RAM2, RAM0, RAM0 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM1", 0);
+ break;
+ case 1:
+ branch_RAM("RAM1", 2);
+ break;
+ case 2:
+ branch_RAM("RAM1", 0);
+ break;
+ case 3:
+ branch_RAM("RAM1", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 3: /* RAM2 to ROM0, RAM1, RAM0, RAM0 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM2", 0);
+ break;
+ case 1:
+ branch_RAM("RAM2", 1);
+ break;
+ case 2:
+ branch_RAM("RAM2", 0);
+ break;
+ case 3:
+ branch_RAM("RAM2", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ default:
+ fatal(1, "Impossible current mpc %d\n", current);
+ }
+#else
+ fatal(1, "Impossible control ROM/RAM combination %d/%d\n", ALTO2_UCODE_ROM_PAGES, ALTO2_UCODE_RAM_PAGES);
+#endif
+}
+
+/**
+ * @brief f1_wrtram late: start WRTRAM cycle
+ */
+void alto2_cpu_device::f1_late_wrtram()
+{
+ m_wrtram_flag = true;
+ LOG((LOG_RAM,2," WRTRAM\n"));
+}
+
+/**
+ * @brief f1_rdram late: start RDRAM cycle
+ */
+void alto2_cpu_device::f1_late_rdram()
+{
+ m_rdram_flag = true;
+ LOG((LOG_RAM,2," RDRAM\n"));
+}
+
+#if (ALTO2_UCODE_RAM_PAGES == 3)
+
+/**
+ * @brief f1_load_rmr late: load the reset mode register
+ *
+ * F1=013 corresponds to RMR<- in the emulator. In Altos with the 3K
+ * RAM option, F1=013 performs RMR<- in all RAM-related tasks, including
+ * the emulator.
+ */
+void alto2_cpu_device::f1_late_load_rmr()
+{
+ LOG((LOG_RAM,2," RMR<-; BUS (%#o)\n", m_bus));
+ m_reset_mode = m_bus;
+}
+#else // ALTO2_UCODE_RAM_PAGES != 3
+/**
+ * @brief f1_load_srb late: load the S register bank from BUS[12-14]
+ */
+void alto2_cpu_device::f1_late_load_srb()
+{
+ m_s_reg_bank[m_task] = X_RDBITS(m_bus,16,12,14) % ALTO2_SREG_BANKS;
+ LOG((LOG_RAM,2," SRB<-; srb[%d] := %#o\n", m_task, m_s_reg_bank[m_task]));
+}
+#endif
+
+/**
+ * @brief RAM related task slots initialization
+ */
+void alto2_cpu_device::init_ram(int task)
+{
+ m_ram_related[task] = true;
+
+ set_bs(task, bs_ram_read_slocation, &alto2_cpu_device::bs_early_read_sreg, 0);
+ set_bs(task, bs_ram_load_slocation, &alto2_cpu_device::bs_early_load_sreg, &alto2_cpu_device::bs_late_load_sreg);
+
+ set_f1(task, f1_ram_swmode, 0, &alto2_cpu_device::f1_late_swmode);
+ set_f1(task, f1_ram_wrtram, 0, &alto2_cpu_device::f1_late_wrtram);
+ set_f1(task, f1_ram_rdram, 0, &alto2_cpu_device::f1_late_rdram);
+#if (ALTO2_UCODE_RAM_PAGES == 3)
+ set_f1(task, f1_ram_load_rmr, 0, &alto2_cpu_device::f1_late_load_rmr);
+#else // ALTO2_UCODE_RAM_PAGES != 3
+ set_f1(task, f1_ram_load_srb, 0, &alto2_cpu_device::f1_late_load_srb);
+#endif
+}
+
+void alto2_cpu_device::exit_ram()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_ram()
+{
+ m_rdram_flag = false;
+ m_wrtram_flag = false;
+ m_m = 0;
+ memset(m_s, 0, sizeof(m_s));
+}
diff --git a/src/emu/cpu/alto2/a2ram.h b/src/emu/cpu/alto2/a2ram.h
new file mode 100644
index 00000000000..e9f6cce327a
--- /dev/null
+++ b/src/emu/cpu/alto2/a2ram.h
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII RAM related tasks
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifdef ALTO2_DEFINE_CONSTANTS
+
+#if (ALTO2_CRAM_CONFIG==1)
+#define ALTO2_UCODE_ROM_PAGES 1 //!< number of microcode ROM pages
+#define ALTO2_UCODE_RAM_PAGES 1 //!< number of microcode RAM pages
+#elif (ALTO2_CRAM_CONFIG==2)
+#define ALTO2_UCODE_ROM_PAGES 2 //!< number of microcode ROM pages
+#define ALTO2_UCODE_RAM_PAGES 1 //!< number of microcode RAM pages
+#elif (ALTO2_CRAM_CONFIG==3)
+#define ALTO2_UCODE_ROM_PAGES 1 //!< number of microcode ROM pages
+#define ALTO2_UCODE_RAM_PAGES 3 //!< number of microcode RAM pages
+#else
+#error "Undefined CROM/CRAM configuration"
+#endif
+
+/**
+ * \brief number of S register banks
+ * This depends on the number of RAM pages
+ * 8 pages in 3K CRAM configuration
+ * 1 page in 1K CRAM configurations
+ */
+#if (ALTO2_UCODE_RAM_PAGES == 3)
+#define ALTO2_SREG_BANKS 8
+#else
+#define ALTO2_SREG_BANKS 1
+#endif
+
+#define ALTO2_UCODE_PAGE_SIZE 02000 //!< number of words of microcode
+#define ALTO2_UCODE_PAGE_MASK (ALTO2_UCODE_PAGE_SIZE-1) //!< mask for microcode ROM/RAM address
+#define ALTO2_UCODE_SIZE ((ALTO2_UCODE_ROM_PAGES + ALTO2_UCODE_RAM_PAGES) * ALTO2_UCODE_PAGE_SIZE) //!< total number of words of microcode
+#define ALTO2_UCODE_RAM_BASE (ALTO2_UCODE_ROM_PAGES * ALTO2_UCODE_PAGE_SIZE) //!< base offset for the RAM page(s)
+
+#else // ALTO2_DEFINE_CONSTANTS
+#ifndef _A2RAM_H_
+#define _A2RAM_H_
+//! BUS source for RAM related tasks
+enum {
+ bs_ram_read_slocation= bs_task_3, //!< ram related: read S register
+ bs_ram_load_slocation= bs_task_4 //!< ram related: load S register
+};
+
+//!< F1 functions for RAM related tasks
+enum {
+ f1_ram_swmode = f1_task_10, //!< f1 10: switch mode to CROM/CRAM in same page
+ f1_ram_wrtram = f1_task_11, //!< f1 11: start WRTRAM cycle
+ f1_ram_rdram = f1_task_12, //!< f1 12: start RDRAM cycle
+#if (ALTO2_UCODE_RAM_PAGES == 3)
+ f1_ram_load_rmr = f1_task_13, //!< f1 13: load the reset mode register
+#else // ALTO2_UCODE_RAM_PAGES != 3
+ f1_ram_load_srb = f1_task_13 //!< f1 14: load the S register bank from BUS[12-14]
+#endif
+};
+
+void bs_early_read_sreg(); //!< bus source: drive bus by S register or M (MYL), if rsel is = 0
+void bs_early_load_sreg(); //!< bus source: load S register puts garbage on the bus
+void bs_late_load_sreg(); //!< bus source: load S register from M
+void branch_ROM(const char *from, int page); //!< branch to ROM page
+void branch_RAM(const char *from, int page); //!< branch to RAM page
+void f1_late_swmode(); //!< F1 func: switch to micro program counter BUS[6-15] in other bank
+void f1_late_wrtram(); //!< F1 func: start WRTRAM cycle
+void f1_late_rdram(); //!< F1 func: start RDRAM cycle
+#if (ALTO2_UCODE_RAM_PAGES == 3)
+void f1_late_load_rmr(); //!< F1 func: load the reset mode register
+#else // ALTO2_UCODE_RAM_PAGES != 3
+void f1_late_load_srb(); //!< F1 func: load the S register bank from BUS[12-14]
+#endif
+void init_ram(int task); //!< called by RAM related tasks
+void exit_ram(); //!< deinitialize the RAM related tasks
+void reset_ram(); //!< reset the RAM related tasks
+#endif // A2RAM_H
+#endif // ALTO2_DEFINE_CONSTANTS
diff --git a/src/emu/cpu/alto2/a2roms.c b/src/emu/cpu/alto2/a2roms.c
new file mode 100644
index 00000000000..8b9b2777a1c
--- /dev/null
+++ b/src/emu/cpu/alto2/a2roms.c
@@ -0,0 +1,198 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII PROM loading and decoding
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+#include "a2roms.h"
+
+#define DEBUG_PROM_LOAD 0 //!< define to 1 to dump PROMs after loading
+
+/**
+ * @brief return number of 1 bits in a 32 bit value
+ *
+ * 32-bit recursive reduction using SWAR,
+ * but first step is mapping 2-bit values
+ * into sum of 2 1-bit values in sneaky way.
+ */
+static UINT32 ones_u32(UINT32 val)
+{
+ val -= ((val >> 1) & 0x55555555);
+ val = (((val >> 2) & 0x33333333) + (val & 0x33333333));
+ val = (((val >> 4) + val) & 0x0f0f0f0f);
+ val += (val >> 8);
+ val += (val >> 16);
+ return (val & 0x0000003f);
+}
+
+/**
+ * @brief return the log2 of an integer value
+ */
+static UINT32 log2_u32(UINT32 val)
+{
+ val |= (val >> 1);
+ val |= (val >> 2);
+ val |= (val >> 4);
+ val |= (val >> 8);
+ val |= (val >> 16);
+ return ones_u32(val >> 1);
+}
+
+/**
+ * @brief map a number of data or address lines using a lookup table
+ *
+ * @param map pointer to an array of values, or NULL for default
+ * @param lines number of data or address lines
+ * @param val value to map
+ * @result returns the remapped value, or just val, if map was NULL
+ */
+static UINT32 map_lines(const UINT8 *map, int lines, UINT32 val)
+{
+ if (NULL == map)
+ return val;
+
+ UINT32 res = 0;
+ for (int i = 0; i < lines; i++)
+ if (val & (1 << i))
+ res |= 1 << map[i];
+ return res;
+}
+
+/**
+ * @brief write to a ROM base + address of type 'type', ANDing with and, ORing with or
+ *
+ * @param base ROM base address in memory
+ * @param type one of 1 for UINT8, 2 for UINT16, 4 for UINT32
+ * @param addr address offset into base
+ * @param dand value to AND to contents before XORing
+ * @param dxor value to XOR before writing back
+ */
+static void write_type_and_xor(void *base, int type, UINT32 addr, UINT32 dand, UINT32 dxor)
+{
+ switch (type) {
+ case sizeof(UINT8):
+ {
+ UINT8 *base8 = reinterpret_cast<UINT8 *>(base);
+ base8[addr] = (base8[addr] & dand) ^ dxor;
+ }
+ break;
+ case sizeof(UINT16):
+ {
+ UINT16 *base16 = reinterpret_cast<UINT16 *>(base);
+ base16[addr] = (base16[addr] & dand) ^ dxor;
+ }
+ break;
+ case sizeof(UINT32):
+ {
+ UINT32 *base32 = reinterpret_cast<UINT32 *>(base);
+ base32[addr] = (base32[addr] & dand) ^ dxor;
+ }
+ break;
+ default:
+ fatalerror("write_type_and_xor() invalid type size (%d) in ROM definitions\n", type);
+ }
+}
+
+/**
+ * @brief load a PROM from a (list of) source region(s) shifting, swapping and inverting address and data bits
+ * @param prom PROM loading definition
+ * @param src source ROM region where to load data from
+ * @param pages number of pages of definitions
+ * @param segments number of segments in one page of the result
+ * @return pointer to the newly allocated memory filled with source bits
+ */
+UINT8* prom_load(running_machine& machine, const prom_load_t* prom, const UINT8* src, int pages, int segments)
+{
+ void* array = 0;
+ size_t type = prom->type;
+ size_t size = prom->size;
+#if DEBUG_PROM_LOAD
+ UINT8 width = prom->width;
+#endif
+
+ switch (type) {
+ case sizeof(UINT8):
+ array = auto_alloc_array(machine, UINT8, pages * size);
+ break;
+ case sizeof(UINT16):
+ array = auto_alloc_array(machine, UINT16, pages * size);
+ break;
+ case sizeof(UINT32):
+ array = auto_alloc_array(machine, UINT32, pages * size);
+ break;
+ }
+
+ UINT8* dst = reinterpret_cast<UINT8*>(array);
+ for (int page = 0; page < pages; page++)
+ {
+ for (int segment = 0; segment < segments; segment++, prom++)
+ {
+ for (UINT32 src_addr = 0; src_addr < prom->size; src_addr++)
+ {
+ // map destination address lines
+ UINT32 dst_addr = map_lines(prom->amap, log2_u32(prom->size) + 1, src_addr);
+ // fetch data bits
+ UINT32 data = src[src_addr ^ prom->axor] ^ prom->dxor;
+ // mask width bits
+ data = data & ((1 << prom->width) - 1);
+ // map destination data lines
+ data = map_lines(prom->dmap, prom->width, data);
+ // shift to destination position
+ data = data << prom->shift;
+ // and destination width dand then xor data
+ write_type_and_xor(dst, prom->type, dst_addr, prom->dand, data);
+ }
+ src += prom->size;
+ }
+ dst += prom->type * prom->size;
+ }
+
+#if DEBUG_PROM_LOAD
+ switch (type) {
+ case sizeof(UINT8):
+ {
+ UINT8* data = reinterpret_cast<UINT8*>(array);
+ for (int addr = 0; addr < pages*size; addr++) {
+ if (0 == (addr % 16))
+ printf("%04x:", addr);
+ if (width <= 4)
+ printf(" %x", data[addr]);
+ else
+ printf(" %02x", data[addr]);
+ if (15 == (addr % 16))
+ printf("\n");
+ }
+ }
+ break;
+ case sizeof(UINT16):
+ {
+ UINT16* data = reinterpret_cast<UINT16*>(array);
+ for (int addr = 0; addr < pages*size; addr++) {
+ if (0 == (addr % 8))
+ printf("%04x:", addr);
+ printf(" %04x", data[addr]);
+ if (7 == (addr % 8))
+ printf("\n");
+ }
+ }
+ break;
+ case sizeof(UINT32):
+ {
+ UINT32* data = reinterpret_cast<UINT32*>(array);
+ for (int addr = 0; addr < pages*size; addr++) {
+ if (0 == (addr % 4))
+ printf("%04x:", addr);
+ printf(" %08x", data[addr]);
+ if (3 == (addr % 4))
+ printf("\n");
+ }
+ }
+ break;
+ }
+#endif
+ return reinterpret_cast<UINT8 *>(array);
+}
diff --git a/src/emu/cpu/alto2/a2roms.h b/src/emu/cpu/alto2/a2roms.h
new file mode 100644
index 00000000000..438d5c67edf
--- /dev/null
+++ b/src/emu/cpu/alto2/a2roms.h
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII RAM PROM loading and decoding
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifndef _CPU_A2ROMS_H_
+#define _CPU_A2ROMS_H_
+
+#include "emu.h"
+
+/**
+ * @brief structure to define a ROM's or PROM's loading options
+ */
+typedef struct {
+ const char *name; //!< default filename of the ROM image
+ const char *altname; //!< alternate filename of the ROM image
+ const char *crc32; //!< CRC32 hash of the file
+ const char *sha1; //!< SHA1 hash of the file
+ size_t size; //!< size of the file, and elements in destination memory
+ const UINT8 amap[16]; //!< address bit mapping
+ UINT32 axor; //!< address XOR mask (applied to source address)
+ UINT32 dxor; //!< data XOR mask (applied before shifting and mapping)
+ UINT8 width; //!< width in bits
+ UINT8 shift; //!< left shift in bits
+ const UINT8 dmap[16]; //!< data bit mapping
+ UINT32 dand; //!< ANDing destination with this value, before XORing the data
+ size_t type; //!< type of the destination, i.e. sizeof(type)
+} prom_load_t;
+
+#define ZERO 0
+#define KEEP ~0
+
+#define AMAP_DEFAULT {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
+#define AMAP_CONST_PROM {3,2,1,4,5,6,7,0,}
+#define AMAP_REVERSE_0_7 {7,6,5,4,3,2,1,0,}
+
+#define DMAP_DEFAULT {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
+#define DMAP_REVERSE_0_3 {3,2,1,0,}
+
+extern UINT8* prom_load(running_machine& machine, const prom_load_t* prom, const UINT8* src, int pages = 1, int segments = 1);
+#endif // _CPU_A2ROMS_H_
diff --git a/src/emu/cpu/alto2/alto2cpu.c b/src/emu/cpu/alto2/alto2cpu.c
new file mode 100644
index 00000000000..81f15630af5
--- /dev/null
+++ b/src/emu/cpu/alto2/alto2cpu.c
@@ -0,0 +1,2814 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII CPU core
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+#include "a2roms.h"
+
+#define DEBUG_UCODE_CONST_DATA 0 //!< define to 1 to dump decoded micro code and constants
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type ALTO2 = &device_creator<alto2_cpu_device>;
+
+//**************************************************************************
+// LOGGING AND DEBUGGING
+//**************************************************************************
+#if ALTO2_DEBUG
+int g_log_types = LOG_DISK | LOG_ETH;
+int g_log_level = 8;
+bool g_log_newline = true;
+
+void logprintf(int type, int level, const char* format, ...)
+{
+ static const char* type_name[] = {
+ "[CPU]",
+ "[EMU]",
+ "[T01]",
+ "[T02]",
+ "[T03]",
+ "[KSEC]",
+ "[T05]",
+ "[T06]",
+ "[ETH]",
+ "[MRT]",
+ "[DWT]",
+ "[CURT]",
+ "[DHT]",
+ "[DVT]",
+ "[PART]",
+ "[KWD]",
+ "[T17]",
+ "[MEM]",
+ "[RAM]",
+ "[DRIVE]",
+ "[DISK]",
+ "[DISPL]",
+ "[MOUSE]",
+ "[HW]",
+ "[KBD]"
+ };
+ if (!(g_log_types & type))
+ return;
+ if (level > g_log_level)
+ return;
+ if (g_log_newline) {
+ // last line had a \n - print type name
+ for (int i = 0; i < sizeof(type_name)/sizeof(type_name[0]); i++)
+ if (type & (1 << i))
+ logerror("%-7s ", type_name[i]);
+ }
+ va_list ap;
+ va_start(ap, format);
+ vlogerror(format, ap);
+ va_end(ap);
+ g_log_newline = format[strlen(format) - 1] == '\n';
+}
+#endif
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+DEVICE_ADDRESS_MAP_START( ucode_map, 32, alto2_cpu_device )
+ AM_RANGE(0, ALTO2_UCODE_RAM_BASE - 1) AM_READ ( crom_r )
+ AM_RANGE(ALTO2_UCODE_RAM_BASE, ALTO2_UCODE_SIZE - 1) AM_READWRITE( cram_r, cram_w )
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START( const_map, 16, alto2_cpu_device )
+ AM_RANGE(0, ALTO2_CONST_SIZE - 1) AM_READ ( const_r )
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START( iomem_map, 16, alto2_cpu_device )
+ AM_RANGE(0, ALTO2_IO_PAGE_BASE - 1) AM_READWRITE( ioram_r, ioram_w )
+ // page 0376
+ AM_RANGE(0177000, 0177015) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177016, 0177016) AM_READWRITE( utilout_r, utilout_w ) // UTILOUT register
+ AM_RANGE(0177017, 0177017) AM_READWRITE( noop_r, noop_w ) // unused range
+ AM_RANGE(0177020, 0177023) AM_READWRITE( xbus_r, xbus_w ) // XBUS[0-3] registers
+ AM_RANGE(0177024, 0177024) AM_READ ( mear_r ) // MEAR (memory error address register)
+ AM_RANGE(0177025, 0177025) AM_READWRITE( mesr_r, mesr_w ) // MESR (memory error status register)
+ AM_RANGE(0177026, 0177026) AM_READWRITE( mecr_r, mecr_w ) // MECR (memory error control register)
+ AM_RANGE(0177027, 0177027) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177030, 0177033) AM_READ ( utilin_r ) // UTILIN register
+ AM_RANGE(0177034, 0177037) AM_READ ( kbd_ad_r ) // KBD_AD[0-3] matrix
+ AM_RANGE(0177040, 0177057) AM_READWRITE( bank_reg_r, bank_reg_w ) // BANK[0-17] registers (4 bit)
+ AM_RANGE(0177060, 0177077) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177100, 0177101) AM_READWRITE( noop_r, noop_w ) // { Summagraphics tablet X, Y }
+ AM_RANGE(0177102, 0177137) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177140, 0177157) AM_READWRITE( noop_r, noop_w ) // { Organ keyboard }
+ AM_RANGE(0177160, 0177177) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177200, 0177204) AM_READWRITE( noop_r, noop_w ) // { PROM programmer }
+ AM_RANGE(0177205, 0177233) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177234, 0177237) AM_READWRITE( noop_r, noop_w ) // { Experimental cursor control }
+ AM_RANGE(0177240, 0177257) AM_READWRITE( noop_r, noop_w ) // { Alto-II debugger }
+// AM_RANGE(0177244, 0177247) AM_READWRITE( noop_r, noop_w ) // { Graphics keyboard }
+ AM_RANGE(0177260, 0177377) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ // page 0377
+// AM_RANGE(0177400, 0177405) AM_READWRITE( noop_r, noop_w ) // { Maxc2 maintenance interface }
+ AM_RANGE(0177400, 0177400) AM_READWRITE( noop_r, noop_w ) // { Alto DLS input }
+ AM_RANGE(0177401, 0177417) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177420, 0177420) AM_READWRITE( noop_r, noop_w ) // { "" }
+ AM_RANGE(0177421, 0177437) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177440, 0177440) AM_READWRITE( noop_r, noop_w ) // { "" }
+ AM_RANGE(0177441, 0177457) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177460, 0177460) AM_READWRITE( noop_r, noop_w ) // { "" }
+ AM_RANGE(0177461, 0177577) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177600, 0177677) AM_READWRITE( noop_r, noop_w ) // { Alto DLS output }
+ AM_RANGE(0177700, 0177700) AM_READWRITE( noop_r, noop_w ) // { EIA interface output bit }
+ AM_RANGE(0177701, 0177701) AM_READWRITE( noop_r, noop_w ) // { EIA interface input bit }
+ AM_RANGE(0177702, 0177717) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177720, 0177737) AM_READWRITE( noop_r, noop_w ) // { TV camera interface }
+ AM_RANGE(0177740, 0177763) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177764, 0177773) AM_READWRITE( noop_r, noop_w ) // { Redactron tape drive }
+ AM_RANGE(0177774, 0177775) AM_READWRITE( noop_r, noop_w ) // UNUSED RANGE
+ AM_RANGE(0177776, 0177776) AM_READWRITE( noop_r, noop_w ) // { Digital-Analog Converter, Joystick }
+ AM_RANGE(0177777, 0177777) AM_READWRITE( noop_r, noop_w ) // { Digital-Analog Converter, Joystick }
+
+ AM_RANGE(0200000, 0377777) AM_READWRITE( ioram_r, ioram_w )
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// alto2_cpu_device - constructor
+//-------------------------------------------------
+
+alto2_cpu_device::alto2_cpu_device(const machine_config& mconfig, const char* tag, device_t* owner, UINT32 clock) :
+ cpu_device(mconfig, ALTO2, "Xerox Alto-II", tag, owner, clock, "alto2", __FILE__),
+ m_ucode_config("ucode", ENDIANNESS_BIG, 32, 12, -2 ),
+ m_const_config("const", ENDIANNESS_BIG, 16, 8, -1 ),
+ m_iomem_config("iomem", ENDIANNESS_BIG, 16, 17, -1 ),
+ m_ucode_crom(0),
+ m_const_data(0),
+ m_icount(0),
+ m_task_mpc(),
+ m_task_next2(),
+ m_task(0),
+ m_next_task(0),
+ m_next2_task(0),
+ m_mpc(0),
+ m_mir(0),
+ m_rsel(0),
+ m_d_rsel(0),
+ m_d_aluf(0),
+ m_d_bs(0),
+ m_d_f1(0),
+ m_d_f2(0),
+ m_d_loadt(0),
+ m_d_loadl(0),
+ m_next(0),
+ m_next2(0),
+ m_r(),
+ m_s(),
+ m_bus(0),
+ m_t(0),
+ m_alu(0),
+ m_aluc0(0),
+ m_l(0),
+ m_shifter(0),
+ m_laluc0(0),
+ m_m(0),
+ m_cram_addr(0),
+ m_task_wakeup(0),
+ m_active_callback(),
+ m_reset_mode(0xffff),
+ m_rdram_flag(false),
+ m_wrtram_flag(false),
+ m_s_reg_bank(),
+ m_bank_reg(),
+ m_ether_enable(false),
+ m_ewfct(false),
+ m_dsp_time(0),
+ m_unload_time(0),
+ m_unload_word(0),
+ m_bitclk_time(0),
+ m_bitclk_index(0),
+ m_ctl2k_u3(0),
+ m_ctl2k_u38(0),
+ m_ctl2k_u76(0),
+ m_cram3k_a37(0),
+ m_madr_a64(0),
+ m_madr_a65(0),
+ m_madr_a90(0),
+ m_madr_a91(0),
+ m_bs(),
+ m_f1(),
+ m_f2(),
+ m_ram_related(),
+ m_cycle(0),
+ m_ether_id(0),
+ m_hw(),
+ m_mouse(),
+ m_drive(),
+ m_dsk(),
+ m_sysclka0(),
+ m_sysclka1(),
+ m_sysclkb0(),
+ m_sysclkb1(),
+ m_dsp(),
+ m_disp_a38(0),
+ m_disp_a63(0),
+ m_disp_a66(0),
+ m_mem(),
+ m_emu(),
+ m_ether_a41(0),
+ m_ether_a42(0),
+ m_ether_a49(0),
+ m_eth()
+{
+ m_is_octal = true;
+}
+
+alto2_cpu_device::~alto2_cpu_device()
+{
+ // call all subdevice's exit code
+ exit_kwd();
+ exit_part();
+ exit_dvt();
+ exit_dht();
+ exit_curt();
+ exit_dwt();
+ exit_mrt();
+ exit_ether();
+ exit_ksec();
+ exit_emu();
+ exit_hw();
+ exit_mouse();
+ exit_kbd();
+ exit_disp();
+ exit_disk();
+ exit_memory();
+}
+
+//-------------------------------------------------
+// driver interface to set diablo_hd_device
+//-------------------------------------------------
+
+void alto2_cpu_device::set_diablo(int unit, diablo_hd_device* ptr)
+{
+ logerror("%s: unit=%d diablo_hd_device=%p\n", __FUNCTION__, unit, ptr);
+ m_drive[unit] = ptr;
+}
+
+//-------------------------------------------------
+// device_rom_region - device-specific (P)ROMs
+//-------------------------------------------------
+
+ROM_START( alto2_cpu )
+ ROM_REGION( 16 * 02000, "ucode_proms", 0 )
+ ROM_LOAD( "55x.3", 0*02000, 0x400, CRC(de870d75) SHA1(2b98cc769d8302cb39948711424d987d94e4159b) ) //!< 00000-01777 RSEL(0)',RSEL(1)',RSEL(2)',RSEL(3)'
+ ROM_LOAD( "64x.3", 1*02000, 0x400, CRC(51b444c0) SHA1(8756e51f7f3253a55d75886465beb7ee1be6e1c4) ) //!< 00000-01777 RSEL(4)',ALUF(0)',ALUF(1)',ALUF(2)'
+ ROM_LOAD( "65x.3", 2*02000, 0x400, CRC(741d1437) SHA1(01f7cf07c2173ac93799b2475180bfbbe7e0149b) ) //!< 00000-01777 ALUF(3)',BS(0)',BS(1)',BS(2)'
+ ROM_LOAD( "63x.3", 3*02000, 0x400, CRC(f22d5028) SHA1(c65a42baef702d4aff2d9ad8e363daec27de6801) ) //!< 00000-01777 F1(0),F1(1)',F1(2)',F1(3)'
+ ROM_LOAD( "53x.3", 4*02000, 0x400, CRC(3c89a740) SHA1(95d812d489b2bde03884b2f126f961caa6c8ec45) ) //!< 00000-01777 F2(0),F2(1)',F2(2)',F2(3)'
+ ROM_LOAD( "60x.3", 5*02000, 0x400, CRC(a35de0bf) SHA1(7fa4aead44dcf5393bbfd1706c0ada24aa6fd3ac) ) //!< 00000-01777 LOADT',LOADL,NEXT(0)',NEXT(1)'
+ ROM_LOAD( "61x.3", 6*02000, 0x400, CRC(f25bcb2d) SHA1(acb57f3104a8dc4ba750dd1bf22ccc81cce9f084) ) //!< 00000-01777 NEXT(2)',NEXT(3)',NEXT(4)',NEXT(5)'
+ ROM_LOAD( "62x.3", 7*02000, 0x400, CRC(1b20a63f) SHA1(41dc86438e91c12b0fe42ffcce6b2ac2eb9e714a) ) //!< 00000-01777 NEXT(6)',NEXT(7)',NEXT(8)',NEXT(9)'
+
+ // extended memory Mesa 5.1 micro code PROMs, 8 x 4bit
+ ROM_LOAD( "xm51.u54", 8*02000, 02000, CRC(11086ae9) SHA1(c394e3fadbfb91801ddc1a70cb25dc6f606c4f76) ) //!< 00000-01777 RSEL(0)',RSEL(1)',RSEL(2)',RSEL(3)'
+ ROM_LOAD( "xm51.u74", 9*02000, 02000, CRC(be8224f2) SHA1(ea9abcc3832b26a094319796901237e1e3f238b6) ) //!< 00000-01777 RSEL(4)',ALUF(0)',ALUF(1)',ALUF(2)'
+ ROM_LOAD( "xm51.u75", 10*02000, 02000, CRC(dfe3e3ac) SHA1(246fd29f92150a5d5d7627fbb4f2504c7b6cd5ec) ) //!< 00000-01777 ALUF(3)',BS(0)',BS(1)',BS(2)'
+ ROM_LOAD( "xm51.u73", 11*02000, 02000, CRC(6c20fa46) SHA1(a054330c65048011f12209aaed5c6da73d95f029) ) //!< 00000-01777 F1(0),F1(1)',F1(2)',F1(3)'
+ ROM_LOAD( "xm51.u52", 12*02000, 02000, CRC(0a31eec8) SHA1(4e2ad5daa5e6a6f2143ee4de00c7b625d096fb02) ) //!< 00000-01777 F2(0),F2(1)',F2(2)',F2(3)'
+ ROM_LOAD( "xm51.u70", 13*02000, 02000, CRC(5c64ee54) SHA1(0eb16d1b5e5967be7c1bf8c8ef6efdf0518a752c) ) //!< 00000-01777 LOADT',LOADL,NEXT(0)',NEXT(1)'
+ ROM_LOAD( "xm51.u71", 14*02000, 02000, CRC(7283bf71) SHA1(819fdcc407ed0acdd8f12b02db6efbcab7bec19a) ) //!< 00000-01777 NEXT(2)',NEXT(3)',NEXT(4)',NEXT(5)'
+ ROM_LOAD( "xm51.u72", 15*02000, 02000, CRC(a28e5251) SHA1(44dd8ad4ad56541b5394d30ce3521b4d1d561394) ) //!< 00000-01777 NEXT(6)',NEXT(7)',NEXT(8)',NEXT(9)'
+
+ // constant PROMs, 4 x 4bit
+ // UINT16 src = BITS(addr, 3,2,1,4,5,6,7,0);
+ ROM_REGION( 4 * 0400, "const_proms", 0 )
+ ROM_LOAD( "madr.a6", 0*00400, 00400, CRC(c2c196b2) SHA1(8b2a599ac839ec2a070dbfef2f1626e645c858ca) ) //!< 0000-0377 C(00)',C(01)',C(02)',C(03)'
+ ROM_LOAD( "madr.a5", 1*00400, 00400, CRC(42336101) SHA1(c77819cf40f063af3abf66ea43f17cc1a62e928b) ) //!< 0000-0377 C(04)',C(05)',C(06)',C(07)'
+ ROM_LOAD( "madr.a4", 2*00400, 00400, CRC(b957e490) SHA1(c72660ad3ada4ca0ed8697c6bb6275a4fe703184) ) //!< 0000-0377 C(08)',C(09)',C(10)',C(11)'
+ ROM_LOAD( "madr.a3", 3*00400, 00400, CRC(e0992757) SHA1(5c45ea824970663cb9ee672dc50861539c860249) ) //!< 0000-0377 C(12)',C(13)',C(14)',C(15)'
+
+ // extended memory Mesa 4.1 (?) micro code PROMs, 8 x 4bit (unused)
+ ROM_REGION( 8 * 02000, "xm_mesa_4.1", 0 )
+ ROM_LOAD( "xm654.41", 0*02000, 02000, CRC(beace302) SHA1(0002fea03a0261f57365095c4b87385d833f7063) ) //!< 00000-01777 RSEL(0)',RSEL(1)',RSEL(2)',RSEL(3)'
+ ROM_LOAD( "xm674.41", 1*02000, 02000, CRC(7db5c097) SHA1(364bc41951baa3ad274031bd49abec1cf5b7a980) ) //!< 00000-01777 RSEL(4)',ALUF(0)',ALUF(1)',ALUF(2)'
+ ROM_LOAD( "xm675.41", 2*02000, 02000, CRC(26eac1e7) SHA1(9220a1386afae8de96bdb2cf084afbadeeb61d42) ) //!< 00000-01777 ALUF(3)',BS(0)',BS(1)',BS(2)'
+ ROM_LOAD( "xm673.41", 3*02000, 02000, CRC(8173d7e3) SHA1(7fbacf6dccb60dfe9cef88a248c3a1660efddcf4) ) //!< 00000-01777 F1(0),F1(1)',F1(2)',F1(3)'
+ ROM_LOAD( "xm652.41", 4*02000, 02000, CRC(ddfa94bb) SHA1(38625e269400aaf38cd07b5dbf36c0087a0f1b92) ) //!< 00000-01777 F2(0),F2(1)',F2(2)',F2(3)'
+ ROM_LOAD( "xm670.41", 5*02000, 02000, CRC(1cd187f3) SHA1(0fd5eff7c6b5c2383aa20148a795b80286554675) ) //!< 00000-01777 LOADT',LOADL,NEXT(0)',NEXT(1)'
+ ROM_LOAD( "xm671.41", 6*02000, 02000, CRC(f21b1ad7) SHA1(1e18bdb35de7802892ac373c128f900786d40886) ) //!< 00000-01777 NEXT(2)',NEXT(3)',NEXT(4)',NEXT(5)'
+ ROM_LOAD( "xm672.41", 7*02000, 02000, CRC(110ee075) SHA1(bb72fceba5ce9e5e8c8a0024915006bdd011a3f3) ) //!< 00000-01777 NEXT(6)',NEXT(7)',NEXT(8)',NEXT(9)'
+
+ ROM_REGION( 0400, "2kctl_u3", 0 )
+ ROM_LOAD( "2kctl.u3", 00000, 00400, CRC(5f8d89e8) SHA1(487cd944ab074290aea73425e81ef4900d92e250) ) //!< 3601-1 256x4 BPROM; Emulator address modifier
+
+ ROM_REGION( 0400, "2kctl_u38", 0 )
+ ROM_LOAD( "2kctl.u38", 00000, 00040, CRC(fc51b1d1) SHA1(e36c2a12a5da377394264899b5ae504e2ffda46e) ) //!< 82S23 32x8 BPROM; task priority and initial address
+
+ ROM_REGION( 0400, "2kctl_u76", 0 )
+ ROM_LOAD( "2kctl.u76", 00000, 00400, CRC(1edef867) SHA1(928b8a15ac515a99109f32672441832173883b81) ) //!< 3601-1 256x4 BPROM; 2KCTL replacement for u51 (1KCTL)
+
+ ROM_REGION( 0040, "alu_a10", 0 )
+ ROM_LOAD( "alu.a10", 00000, 00040, CRC(e0857892) SHA1(dcd389767139f0acc1f87cf074459115abc5b90b) )
+
+ ROM_REGION( 0400, "3kcram_a37", 0 )
+ ROM_LOAD( "3kcram.a37", 00000, 00400, CRC(9417360d) SHA1(bfcdbc56ee4ffafd0f2f672c0c869a55d6dd194b) )
+
+ ROM_REGION( 0400, "madr_a32", 0 )
+ ROM_LOAD( "madr.a32", 00000, 00400, CRC(a0e3b4a7) SHA1(24e50afdeb637a6a8588f8d3a3493c9188b8da2c) ) //! P3601 256x4 BPROM; mouse motion signals MX1, MX2, MY1, MY2
+
+ ROM_REGION( 0400, "madr_a64", 0 )
+ ROM_LOAD( "madr.a64", 00000, 00400, CRC(a66b0eda) SHA1(4d9088f592caa3299e90966b17765be74e523144) ) //! P3601 256x4 BPROM; memory addressing
+
+ ROM_REGION( 0400, "madr_a65", 0 )
+ ROM_LOAD( "madr.a65", 00000, 00400, CRC(ba37febd) SHA1(82e9db1cb65f451755295f0d179e6f8fe3349d4d) ) //! P3601 256x4 BPROM; memory addressing
+
+ ROM_REGION( 0400, "madr_a90", 0 )
+ ROM_LOAD( "madr.a90", 00000, 00400, CRC(7a2d8799) SHA1(c3760dba147740729d33b9b88e59088a4cc7437a) )
+
+ ROM_REGION( 0400, "madr_a91", 0 )
+ ROM_LOAD( "madr.a91", 00000, 00400, CRC(dd556aeb) SHA1(900f333a091e3ccde0843019c25f25fba62e6023) )
+
+ ROM_REGION( 0400, "displ_a38", 0 )
+ ROM_LOAD( "displ.a38", 00000, 00400, CRC(fd30beb7) SHA1(65e4a19ba4ff748d525122128c514abedd55d866) ) //!< P3601 256x4 BPROM; display FIFO control: STOPWAKE, MBEMPTY
+
+ ROM_REGION( 0040, "displ_a63", 0 )
+ ROM_LOAD( "displ.a63", 00000, 00040, CRC(82a20d60) SHA1(39d90703568be5419ada950e112d99227873fdea) ) //!< 82S23 32x8 BPROM; display HBLANK, HSYNC, SCANEND, HLCGATE ...
+
+ ROM_REGION( 0400, "displ_a66", 0 )
+ ROM_LOAD( "displ.a66", 00000, 00400, CRC(9f91aad9) SHA1(69b1d4c71f4e18103112e8601850c2654e9265cf) ) //!< P3601 256x4 BPROM; display VSYNC and VBLANK
+
+ ROM_REGION( 0400, "ether_a41", 0 )
+ ROM_LOAD( "enet.a41", 00000, 00400, CRC(d5de8d86) SHA1(c134a4c898c73863124361a9b0218f7a7f00082a) )
+
+ ROM_REGION( 0400, "ether_a42", 0 )
+ ROM_LOAD( "enet.a42", 00000, 00400, CRC(9d5c81bd) SHA1(ac7e63332a3dad0bef7cd0349b24e156a96a4bf0) )
+
+ ROM_REGION( 0400, "ether_a49", 0 )
+ ROM_LOAD( "enet.a49", 00000, 00400, CRC(4d2dcdb2) SHA1(583327a7d70cd02702c941c0e43c1e9408ff7fd0) )
+ROM_END
+
+const rom_entry *alto2_cpu_device::device_rom_region() const
+{
+ return ROM_NAME( alto2_cpu );
+}
+
+/**
+ * @brief list of microcode PROM loading options
+ */
+static const prom_load_t pl_ucode[] = {
+ { // 0000-01777 RSEL(0)',RSEL(1)',RSEL(2)',RSEL(3)'
+ "55x.3",
+ 0,
+ "de870d75",
+ "2b98cc769d8302cb39948711424d987d94e4159b",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 28,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ ZERO,
+/* type */ sizeof(UINT32)
+ },
+ { // 0000-01777 RSEL(4)',ALUF(0)',ALUF(1)',ALUF(2)'
+ "64x.3",
+ 0,
+ "51b444c0",
+ "8756e51f7f3253a55d75886465beb7ee1be6e1c4",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 24,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 0000-01777 ALUF(3)',BS(0)',BS(1)',BS(2)'
+ "65x.3",
+ 0,
+ "741d1437",
+ "01f7cf07c2173ac93799b2475180bfbbe7e0149b",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 20,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 0000-01777 F1(0),F1(1)',F1(2)',F1(3)'
+ "63x.3",
+ 0,
+ "f22d5028",
+ "c65a42baef702d4aff2d9ad8e363daec27de6801",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 007, // keep D0, invert D1-D3
+/* width */ 4,
+/* shift */ 16,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 0000-01777 F2(0),F2(1)',F2(2)',F2(3)'
+ "53x.3",
+ 0,
+ "3c89a740",
+ "95d812d489b2bde03884b2f126f961caa6c8ec45",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 007, // keep D0, invert D1-D3
+/* width */ 4,
+/* shift */ 12,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 0000-01777 LOADT',LOADL,NEXT(0)',NEXT(1)'
+ "60x.3",
+ 0,
+ "a35de0bf",
+ "7fa4aead44dcf5393bbfd1706c0ada24aa6fd3ac",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 013, // invert D0 and D2-D3
+/* width */ 4,
+/* shift */ 8,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 0000-01777 NEXT(2)',NEXT(3)',NEXT(4)',NEXT(5)'
+ "61x.3",
+ 0,
+ "f25bcb2d",
+ "acb57f3104a8dc4ba750dd1bf22ccc81cce9f084",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 4,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 0000-01777 NEXT(6)',NEXT(7)',NEXT(8)',NEXT(9)'
+ "62x.3",
+ 0,
+ "1b20a63f",
+ "41dc86438e91c12b0fe42ffcce6b2ac2eb9e714a",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 0,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ }
+
+#if (ALTO2_UCODE_ROM_PAGES > 1)
+ ,
+ { // 02000-03777 RSEL(0)',RSEL(1)',RSEL(2)',RSEL(3)'
+ "xm51.u54",
+ 0,
+ "11086ae9",
+ "c394e3fadbfb91801ddc1a70cb25dc6f606c4f76",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 28,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ ZERO,
+/* type */ sizeof(UINT32)
+ },
+ { // 02000-03777 RSEL(4)',ALUF(0)',ALUF(1)',ALUF(2)'
+ "xm51.u74",
+ 0,
+ "be8224f2",
+ "ea9abcc3832b26a094319796901237e1e3f238b6",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 24,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 02000-03777 ALUF(3)',BS(0)',BS(1)',BS(2)'
+ "xm51.u75",
+ 0,
+ "dfe3e3ac",
+ "246fd29f92150a5d5d7627fbb4f2504c7b6cd5ec",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 20,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 02000-03777 F1(0),F1(1)',F1(2)',F1(3)'
+ "xm51.u73",
+ 0,
+ "6c20fa46",
+ "a054330c65048011f12209aaed5c6da73d95f029",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 007, // keep D0, invert D1-D3
+/* width */ 4,
+/* shift */ 16,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 02000-03777 F2(0),F2(1)',F2(2)',F2(3)'
+ "xm51.u52",
+ 0,
+ "0a31eec8",
+ "4e2ad5daa5e6a6f2143ee4de00c7b625d096fb02",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 007, // keep D0, invert D1-D3
+/* width */ 4,
+/* shift */ 12,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 02000-03777 LOADT',LOADL,NEXT(0)',NEXT(1)'
+ "xm51.u70",
+ 0,
+ "5c64ee54",
+ "0eb16d1b5e5967be7c1bf8c8ef6efdf0518a752c",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 013, // invert D0 and D2-D3
+/* width */ 4,
+/* shift */ 8,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 02000-03777 NEXT(2)',NEXT(3)',NEXT(4)',NEXT(5)'
+ "xm51.u71",
+ 0,
+ "7283bf71",
+ "819fdcc407ed0acdd8f12b02db6efbcab7bec19a",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 4,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ },
+ { // 02000-03777 NEXT(6)',NEXT(7)',NEXT(8)',NEXT(9)'
+ "xm51.u72",
+ 0,
+ "a28e5251",
+ "44dd8ad4ad56541b5394d30ce3521b4d1d561394",
+/* size */ ALTO2_UCODE_PAGE_SIZE,
+/* amap */ AMAP_DEFAULT,
+/* axor */ ALTO2_UCODE_PAGE_MASK,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 0,
+/* dmap */ DMAP_DEFAULT,
+/* dand */ KEEP,
+/* type */ sizeof(UINT32)
+ }
+#endif // (UCODE_ROM_PAGES > 1)
+};
+
+/**
+ * @brief list of constant PROM loading options
+ */
+static const prom_load_t pl_const[] = {
+ { // constant prom D0-D3
+ "madr.a6",
+ "c3.3",
+ "c2c196b2",
+ "8b2a599ac839ec2a070dbfef2f1626e645c858ca",
+/* size */ ALTO2_CONST_SIZE,
+/* amap */ AMAP_CONST_PROM, // descramble constant address
+/* axor */ 0,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 0,
+/* dmap */ DMAP_REVERSE_0_3, // reverse D0-D3 to D3-D0
+/* dand */ ZERO,
+/* type */ sizeof(UINT16)
+ },
+ { // constant prom D4-D7
+ "madr.a5",
+ "c2.3",
+ "42336101",
+ "c77819cf40f063af3abf66ea43f17cc1a62e928b",
+/* size */ ALTO2_CONST_SIZE,
+/* amap */ AMAP_CONST_PROM, // descramble constant address
+/* axor */ 0,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 4,
+/* dmap */ DMAP_REVERSE_0_3, // reverse D0-D3 to D3-D0
+/* dand */ KEEP,
+/* type */ sizeof(UINT16)
+ },
+ { // constant prom D8-D11
+ "madr.a4",
+ "c1.3",
+ "b957e490",
+ "c72660ad3ada4ca0ed8697c6bb6275a4fe703184",
+/* size */ ALTO2_CONST_SIZE,
+/* amap */ AMAP_CONST_PROM, // descramble constant address
+/* axor */ 0,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 8,
+/* dmap */ DMAP_REVERSE_0_3, // reverse D0-D3 to D3-D0
+/* dand */ KEEP,
+/* type */ sizeof(UINT16)
+ },
+ { // constant PROM D12-D15
+ "madr.a3",
+ "c0.3",
+ "e0992757",
+ "5c45ea824970663cb9ee672dc50861539c860249",
+/* size */ ALTO2_CONST_SIZE,
+/* amap */ AMAP_CONST_PROM, // descramble constant address
+/* axor */ 0,
+/* dxor */ 017, // invert D0-D3
+/* width */ 4,
+/* shift */ 12,
+/* dmap */ DMAP_REVERSE_0_3, // reverse D0-D3 to D3-D0
+/* dand */ KEEP,
+/* type */ sizeof(UINT16)
+ }
+};
+
+//! 3601-1 256x4 BPROM; Emulator address modifier
+static const prom_load_t pl_2kctl_u3 =
+{
+ "2kctl.u3",
+ 0,
+ "5f8d89e8",
+ "487cd944ab074290aea73425e81ef4900d92e250",
+ /* size */ 0400,
+ /* amap */ AMAP_REVERSE_0_7, // reverse address lines A0-A7
+ /* axor */ 0377, // invert address lines A0-A7
+ /* dxor */ 017, // invert data lines D0-D3
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+//! 82S23 32x8 BPROM; task priority and initial address
+static const prom_load_t pl_2kctl_u38 =
+{
+ "2kctl.u38",
+ 0,
+ "fc51b1d1",
+ "e36c2a12a5da377394264899b5ae504e2ffda46e",
+ /* size */ 0040,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 0,
+ /* width */ 8,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+//! 3601-1 256x4 BPROM; 2KCTL replacement for u51 (1KCTL)
+static const prom_load_t pl_2kctl_u76 =
+{
+ "2kctl.u76",
+ 0,
+ "1edef867",
+ "928b8a15ac515a99109f32672441832173883b81",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0077, // invert address lines A0-A5
+ /* dxor */ 0,
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+//! ALUF to ALU 741818 functions and carry in mapper
+static const prom_load_t pl_alu_a10 =
+{
+ "alu.a10",
+ 0,
+ "e0857892",
+ "dcd389767139f0acc1f87cf074459115abc5b90b",
+ /* size */ 0040,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 0372, // invert D7-D3 and D1
+ /* width */ 8,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+static const prom_load_t pl_3kcram_a37 =
+{
+ "3kcram.a37",
+ 0,
+ "9417360d",
+ "bfcdbc56ee4ffafd0f2f672c0c869a55d6dd194b",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 017, // invert D0-D3
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+static const prom_load_t pl_madr_a90 =
+{
+ "madr.a90",
+ 0,
+ "7a2d8799",
+ "c3760dba147740729d33b9b88e59088a4cc7437a",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 017, // invert D0-D3
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+static const prom_load_t pl_madr_a91 =
+{
+ "madr.a91",
+ 0,
+ "dd556aeb",
+ "900f333a091e3ccde0843019c25f25fba62e6023",
+ /* size */ 0400,
+ /* amap */ AMAP_DEFAULT,
+ /* axor */ 0,
+ /* dxor */ 017, // invert D0-D3
+ /* width */ 4,
+ /* shift */ 0,
+ /* dmap */ DMAP_DEFAULT,
+ /* dand */ ZERO,
+ /* type */ sizeof(UINT8)
+};
+
+//-------------------------------------------------
+// device_memory_interface overrides
+//-------------------------------------------------
+
+const address_space_config*alto2_cpu_device::memory_space_config(address_spacenum spacenum) const
+{
+ if (AS_0 == spacenum)
+ return &m_ucode_config;
+ if (AS_1 == spacenum)
+ return &m_const_config;
+ if (AS_2 == spacenum)
+ return &m_iomem_config;
+ return NULL;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void alto2_cpu_device::device_start()
+{
+ // get a pointer to the IO address space
+ m_iomem = &space(AS_2);
+
+ // decode ALTO2_UCODE_PAGES = 1 or 2 pages of micro code PROMs to CROM
+ m_ucode_crom = prom_load(machine(), pl_ucode, memregion("ucode_proms")->base(), ALTO2_UCODE_ROM_PAGES, 8);
+
+ // allocate micro code CRAM
+ m_ucode_cram = auto_alloc_array(machine(), UINT8, sizeof(UINT32) * ALTO2_UCODE_RAM_PAGES * ALTO2_UCODE_PAGE_SIZE);
+ // fill with the micro code inverted bits value
+ for (offs_t offset = 0; offset < ALTO2_UCODE_RAM_PAGES * ALTO2_UCODE_PAGE_SIZE; offset++)
+ *reinterpret_cast<UINT32 *>(m_ucode_cram + offset * 4) = ALTO2_UCODE_INVERTED;
+
+ // decode constant PROMs to m_const_data
+ m_const_data = prom_load(machine(), pl_const, memregion("const_proms")->base(), 1, 4);
+
+ m_ctl2k_u3 = prom_load(machine(), &pl_2kctl_u3, memregion("2kctl_u3")->base());
+ m_ctl2k_u38 = prom_load(machine(), &pl_2kctl_u38, memregion("2kctl_u38")->base());
+ m_ctl2k_u76 = prom_load(machine(), &pl_2kctl_u76, memregion("2kctl_u76")->base());
+ m_alu_a10 = prom_load(machine(), &pl_alu_a10, memregion("alu_a10")->base());
+ m_cram3k_a37 = prom_load(machine(), &pl_3kcram_a37, memregion("3kcram_a37")->base());
+ m_madr_a90 = prom_load(machine(), &pl_madr_a90, memregion("madr_a90")->base());
+ m_madr_a91 = prom_load(machine(), &pl_madr_a91, memregion("madr_a91")->base());
+
+#if 0 // dump ALU a10 PROM after loading
+ for (UINT8 i = 0; i < 32; i++) {
+ UINT8 a = m_alu_a10[i];
+ printf("%03o: S3-S0:%u%u%u%u M:%u CI:%u T:%u ?:%u\n",
+ i, (a >> 7) & 1, (a >> 6) & 1, (a >> 5) & 1, (a >> 4) & 1,
+ (a >> 3) & 1, (a >> 2) & 1, (a >> 1) & 1, (a >> 0) & 1);
+ }
+#endif
+ save_item(NAME(m_task_mpc));
+ save_item(NAME(m_task_next2));
+ save_item(NAME(m_task));
+ save_item(NAME(m_next_task));
+ save_item(NAME(m_next2_task));
+ save_item(NAME(m_mpc));
+ save_item(NAME(m_mir));
+ save_item(NAME(m_rsel));
+ save_item(NAME(m_next));
+ save_item(NAME(m_next2));
+ save_item(NAME(m_r));
+ save_item(NAME(m_s));
+ save_item(NAME(m_bus));
+ save_item(NAME(m_t));
+ save_item(NAME(m_alu));
+ save_item(NAME(m_aluc0));
+ save_item(NAME(m_l));
+ save_item(NAME(m_shifter));
+ save_item(NAME(m_laluc0));
+ save_item(NAME(m_m));
+ save_item(NAME(m_cram_addr));
+ save_item(NAME(m_task_wakeup));
+ save_item(NAME(m_reset_mode));
+ save_item(NAME(m_rdram_flag));
+ save_item(NAME(m_wrtram_flag));
+ save_item(NAME(m_s_reg_bank));
+ save_item(NAME(m_bank_reg));
+ save_item(NAME(m_ether_enable));
+ save_item(NAME(m_ewfct));
+ save_item(NAME(m_dsp_time));
+ save_item(NAME(m_unload_time));
+ save_item(NAME(m_unload_word));
+#if (USE_BITCLK_TIMER == 0)
+ save_item(NAME(m_bitclk_time));
+ save_item(NAME(m_bitclk_index));
+#endif
+ save_item(NAME(m_mouse.x));
+ save_item(NAME(m_mouse.y));
+ save_item(NAME(m_mouse.dx));
+ save_item(NAME(m_mouse.dy));
+ save_item(NAME(m_mouse.latch));
+
+ hard_reset();
+
+ state_add( A2_TASK, "TASK", m_task).callimport().formatstr("%6s");
+ state_add( A2_MPC, "MPC", m_mpc).formatstr("%06O");
+ state_add( A2_NEXT, "NEXT", m_next).formatstr("%06O");
+ state_add( A2_NEXT2, "NEXT2", m_next2).formatstr("%06O");
+ state_add( A2_BUS, "BUS", m_bus).formatstr("%06O");
+ state_add( A2_T, "T", m_t).formatstr("%06O");
+ state_add( A2_ALU, "ALU", m_alu).formatstr("%06O");
+ state_add( A2_ALUC0, "ALUC0", m_aluc0).mask(1);
+ state_add( A2_L, "L", m_l).formatstr("%06O");
+ state_add( A2_SHIFTER, "SHIFTER", m_shifter).formatstr("%06O");
+ state_add( A2_LALUC0, "LALUC0", m_laluc0).mask(1);
+ state_add( A2_M, "M", m_m).formatstr("%06O");
+ state_add_divider(-1);
+ state_add( A2_AC3, "AC(3)", m_r[000]).formatstr("%06O");
+ state_add( A2_AC2, "AC(2)", m_r[001]).formatstr("%06O");
+ state_add( A2_AC1, "AC(1)", m_r[002]).formatstr("%06O");
+ state_add( A2_AC0, "AC(0)", m_r[003]).formatstr("%06O");
+ state_add( A2_R04, "R04", m_r[004]).formatstr("%06O");
+ state_add( A2_R05, "R05", m_r[005]).formatstr("%06O");
+ state_add( A2_PC, "PC", m_r[006]).formatstr("%06O");
+ state_add( A2_R07, "R07", m_r[007]).formatstr("%06O");
+ state_add( A2_R10, "R10", m_r[010]).formatstr("%06O");
+ state_add( A2_R11, "R11", m_r[011]).formatstr("%06O");
+ state_add( A2_R12, "R12", m_r[012]).formatstr("%06O");
+ state_add( A2_R13, "R13", m_r[013]).formatstr("%06O");
+ state_add( A2_R14, "R14", m_r[014]).formatstr("%06O");
+ state_add( A2_R15, "R15", m_r[015]).formatstr("%06O");
+ state_add( A2_R16, "R16", m_r[016]).formatstr("%06O");
+ state_add( A2_R17, "R17", m_r[017]).formatstr("%06O");
+ state_add( A2_R20, "R20", m_r[020]).formatstr("%06O");
+ state_add( A2_R21, "R21", m_r[021]).formatstr("%06O");
+ state_add( A2_R22, "R22", m_r[022]).formatstr("%06O");
+ state_add( A2_R23, "R23", m_r[023]).formatstr("%06O");
+ state_add( A2_R24, "R24", m_r[024]).formatstr("%06O");
+ state_add( A2_R25, "R25", m_r[025]).formatstr("%06O");
+ state_add( A2_R26, "R26", m_r[026]).formatstr("%06O");
+ state_add( A2_R27, "R27", m_r[027]).formatstr("%06O");
+ state_add( A2_R30, "R30", m_r[030]).formatstr("%06O");
+ state_add( A2_R31, "R31", m_r[031]).formatstr("%06O");
+ state_add( A2_R32, "R32", m_r[032]).formatstr("%06O");
+ state_add( A2_R33, "R33", m_r[033]).formatstr("%06O");
+ state_add( A2_R34, "R34", m_r[034]).formatstr("%06O");
+ state_add( A2_R35, "R35", m_r[035]).formatstr("%06O");
+ state_add( A2_R36, "R36", m_r[036]).formatstr("%06O");
+ state_add( A2_R37, "R37", m_r[037]).formatstr("%06O");
+ state_add_divider(-1);
+ state_add( A2_S00, "R40", m_s[0][000]).formatstr("%06O");
+ state_add( A2_S01, "R41", m_s[0][001]).formatstr("%06O");
+ state_add( A2_S02, "R42", m_s[0][002]).formatstr("%06O");
+ state_add( A2_S03, "R43", m_s[0][003]).formatstr("%06O");
+ state_add( A2_S04, "R44", m_s[0][004]).formatstr("%06O");
+ state_add( A2_S05, "R45", m_s[0][005]).formatstr("%06O");
+ state_add( A2_S06, "R46", m_s[0][006]).formatstr("%06O");
+ state_add( A2_S07, "R47", m_s[0][007]).formatstr("%06O");
+ state_add( A2_S10, "R50", m_s[0][010]).formatstr("%06O");
+ state_add( A2_S11, "R51", m_s[0][011]).formatstr("%06O");
+ state_add( A2_S12, "R52", m_s[0][012]).formatstr("%06O");
+ state_add( A2_S13, "R53", m_s[0][013]).formatstr("%06O");
+ state_add( A2_S14, "R54", m_s[0][014]).formatstr("%06O");
+ state_add( A2_S15, "R55", m_s[0][015]).formatstr("%06O");
+ state_add( A2_S16, "R56", m_s[0][016]).formatstr("%06O");
+ state_add( A2_S17, "R57", m_s[0][017]).formatstr("%06O");
+ state_add( A2_S20, "R60", m_s[0][020]).formatstr("%06O");
+ state_add( A2_S21, "R61", m_s[0][021]).formatstr("%06O");
+ state_add( A2_S22, "R62", m_s[0][022]).formatstr("%06O");
+ state_add( A2_S23, "R63", m_s[0][023]).formatstr("%06O");
+ state_add( A2_S24, "R64", m_s[0][024]).formatstr("%06O");
+ state_add( A2_S25, "R65", m_s[0][025]).formatstr("%06O");
+ state_add( A2_S26, "R66", m_s[0][026]).formatstr("%06O");
+ state_add( A2_S27, "R67", m_s[0][027]).formatstr("%06O");
+ state_add( A2_S30, "R70", m_s[0][030]).formatstr("%06O");
+ state_add( A2_S31, "R71", m_s[0][031]).formatstr("%06O");
+ state_add( A2_S32, "R72", m_s[0][032]).formatstr("%06O");
+ state_add( A2_S33, "R73", m_s[0][033]).formatstr("%06O");
+ state_add( A2_S34, "R74", m_s[0][034]).formatstr("%06O");
+ state_add( A2_S35, "R75", m_s[0][035]).formatstr("%06O");
+ state_add( A2_S36, "R76", m_s[0][036]).formatstr("%06O");
+ state_add( A2_S37, "R77", m_s[0][037]).formatstr("%06O");
+ state_add_divider(-1);
+ state_add( A2_DRIVE, "DRIVE", m_dsk.drive).formatstr("%1u");
+ state_add( A2_KADDR, "KADDR", m_dsk.kaddr).formatstr("%06O");
+ state_add( A2_KADR, "KADR", m_dsk.kadr).formatstr("%06O");
+ state_add( A2_KSTAT, "KSTAT", m_dsk.kstat).formatstr("%06O");
+ state_add( A2_KCOM, "KCOM", m_dsk.kcom).formatstr("%06O");
+ state_add( A2_KRECNO, "KRECNO", m_dsk.krecno).formatstr("%02O");
+ state_add( A2_SHIFTIN, "SHIFTIN", m_dsk.shiftin).formatstr("%06O");
+ state_add( A2_SHIFTOUT,"SHIFTOUT",m_dsk.shiftout).formatstr("%06O");
+ state_add( A2_DATAIN, "DATAIN", m_dsk.datain).formatstr("%06O");
+ state_add( A2_DATAOUT, "DATAOUT", m_dsk.dataout).formatstr("%06O");
+ state_add( A2_KRWC, "KRWC", m_dsk.krwc).formatstr("%1u");
+ state_add( A2_KFER, "KFER", m_dsk.kfer).formatstr("%1u");
+ state_add( A2_WDTSKENA,"WDTSKENA",m_dsk.wdtskena).formatstr("%1u");
+ state_add( A2_WDINIT0, "WDINIT0", m_dsk.wdinit0).formatstr("%1u");
+ state_add( A2_WDINIT, "WDINIT", m_dsk.wdinit).formatstr("%1u");
+ state_add( A2_STROBE, "STROBE", m_dsk.strobe).formatstr("%1u");
+ state_add( A2_BITCLK, "BITCLK", m_dsk.bitclk).formatstr("%1u");
+ state_add( A2_DATIN, "DATIN", m_dsk.datin).formatstr("%06O");
+ state_add( A2_BITCNT, "BITCNT", m_dsk.bitcount).formatstr("%02O");
+ state_add( A2_CARRY, "CARRY", m_dsk.carry).formatstr("%1u");
+ state_add( A2_SECLATE, "SECLATE", m_dsk.seclate).formatstr("%1u");
+ state_add( A2_SEEKOK, "SEEKOK", m_dsk.seekok).formatstr("%1u");
+ state_add( A2_OKTORUN, "OKTORUN", m_dsk.ok_to_run).formatstr("%1u");
+ state_add( A2_READY, "READY", m_dsk.kstat).formatstr("%1u");
+
+ state_add(STATE_GENPC, "curpc", m_mpc).formatstr("%03X").noshow();
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_aluc0).formatstr("%5s").noshow();
+
+ m_icountptr = &m_icount;
+}
+
+//-------------------------------------------------
+// state_string_export - export state as a string
+// for the debugger
+//-------------------------------------------------
+
+void alto2_cpu_device::state_string_export(const device_state_entry &entry, astring &string)
+{
+ switch (entry.index())
+ {
+ case A2_TASK:
+ string.printf("%s", task_name(m_task));
+ break;
+ case STATE_GENFLAGS:
+ string.printf("%s%s%s%s",
+ m_aluc0 ? "C":"-",
+ m_laluc0 ? "c":"-",
+ m_shifter == 0 ? "0":"-",
+ static_cast<INT16>(m_shifter) < 0 ? "<":"-");
+ break;
+ }
+}
+
+//! read microcode CROM
+READ32_MEMBER ( alto2_cpu_device::crom_r )
+{
+ return *reinterpret_cast<UINT32 *>(m_ucode_crom + offset * 4);
+}
+
+//! read microcode CRAM
+READ32_MEMBER ( alto2_cpu_device::cram_r )
+{
+ return *reinterpret_cast<UINT32 *>(m_ucode_cram + offset * 4);
+}
+
+//! write microcode CRAM
+WRITE32_MEMBER( alto2_cpu_device::cram_w )
+{
+ *reinterpret_cast<UINT32 *>(m_ucode_cram + offset * 4) = data;
+}
+
+//! read constants PROM
+READ16_MEMBER ( alto2_cpu_device::const_r )
+{
+ return *reinterpret_cast<UINT16 *>(m_const_data + offset * 2);
+}
+
+//! direct read access to the microcode CROM or CRAM
+#define RD_UCODE(addr) (addr < ALTO2_UCODE_RAM_BASE ? \
+ *reinterpret_cast<UINT32 *>(m_ucode_crom + addr * 4) : \
+ *reinterpret_cast<UINT32 *>(m_ucode_cram + (addr - ALTO2_UCODE_RAM_BASE) * 4))
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void alto2_cpu_device::device_reset()
+{
+ soft_reset();
+ // get the configured ethernet id
+ ioport_port* etherid = ioport(":ETHERID");
+ if (etherid)
+ m_ether_id = etherid->read() & 0377;
+ // call all sub-devices' reset_...
+ reset_memory();
+ reset_disp();
+ reset_disk();
+ reset_hw();
+ reset_kbd();
+ reset_mouse();
+
+ reset_emu();
+ reset_ksec();
+ reset_ether();
+ reset_mrt();
+ reset_dwt();
+ reset_curt();
+ reset_dht();
+ reset_dvt();
+ reset_part();
+ reset_kwd();}
+
+/**
+ * @brief callback is called by the drive timer whenever a new sector starts
+ *
+ * @param unit the unit number
+ */
+static void disk_sector_start(void* cookie, int unit)
+{
+ alto2_cpu_device* cpu = reinterpret_cast<alto2_cpu_device *>(cookie);
+ cpu->next_sector(unit);
+}
+
+void alto2_cpu_device::interface_post_reset()
+{
+ // set the disk unit sector callbacks
+ for (int unit = 0; unit < diablo_hd_device::DIABLO_UNIT_MAX; unit++) {
+ diablo_hd_device* dhd = m_drive[unit];
+ dhd->set_sector_callback(this, &disk_sector_start);
+ }
+}
+
+//-------------------------------------------------
+// execute_set_input - act on a changed input/
+// interrupt line
+//-------------------------------------------------
+
+// FIXME
+void alto2_cpu_device::execute_set_input(int inputnum, int state)
+{
+}
+
+void alto2_cpu_device::fatal(int exitcode, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ emu_fatalerror error(exitcode, format, ap);
+ va_end(ap);
+}
+
+/** @brief task names */
+const char* alto2_cpu_device::task_name(int task)
+{
+ switch (task) {
+ case 000: return "emu";
+ case 001: return "task01";
+ case 002: return "task02";
+ case 003: return "task03";
+ case 004: return "ksec";
+ case 005: return "task05";
+ case 006: return "task06";
+ case 007: return "ether";
+ case 010: return "mrt";
+ case 011: return "dwt";
+ case 012: return "curt";
+ case 013: return "dht";
+ case 014: return "dvt";
+ case 015: return "part";
+ case 016: return "kwd";
+ case 017: return "task17";
+ }
+ return "???";
+}
+
+/** @brief register names (as used by the microcode) */
+const char* alto2_cpu_device::r_name(UINT8 reg)
+{
+ switch (reg) {
+ case 000: return "ac(3)";
+ case 001: return "ac(2)";
+ case 002: return "ac(1)";
+ case 003: return "ac(0)";
+ case 004: return "nww";
+ case 005: return "r05";
+ case 006: return "pc";
+ case 007: return "r07";
+ case 010: return "xh";
+ case 011: return "r11";
+ case 012: return "ecntr";
+ case 013: return "epntr";
+ case 014: return "r14";
+ case 015: return "r15";
+ case 016: return "r16";
+ case 017: return "r17";
+ case 020: return "curx";
+ case 021: return "curdata";
+ case 022: return "cba";
+ case 023: return "aecl";
+ case 024: return "slc";
+ case 025: return "mtemp";
+ case 026: return "htab";
+ case 027: return "ypos";
+ case 030: return "dwa";
+ case 031: return "kwdctw";
+ case 032: return "cksumrw";
+ case 033: return "knmarw";
+ case 034: return "dcbr";
+ case 035: return "dwax";
+ case 036: return "mask";
+ case 037: return "r37";
+ }
+ return "???";
+}
+
+/** @brief ALU function names */
+const char* alto2_cpu_device::aluf_name(UINT8 aluf)
+{
+ switch (aluf) {
+ case 000: return "bus";
+ case 001: return "t";
+ case 002: return "bus or t";
+ case 003: return "bus and t";
+ case 004: return "bus xor t";
+ case 005: return "bus + 1";
+ case 006: return "bus - 1";
+ case 007: return "bus + t";
+ case 010: return "bus - t";
+ case 011: return "bus - t - 1";
+ case 012: return "bus + t + 1";
+ case 013: return "bus + skip";
+ case 014: return "bus, t";
+ case 015: return "bus and not t";
+ case 016: return "0 (undef)";
+ case 017: return "0 (undef)";
+ }
+ return "???";
+}
+
+/** @brief BUS source names */
+const char* alto2_cpu_device::bs_name(UINT8 bs)
+{
+ switch (bs) {
+ case 000: return "read_r";
+ case 001: return "load_r";
+ case 002: return "no_source";
+ case 003: return "task_3";
+ case 004: return "task_4";
+ case 005: return "read_md";
+ case 006: return "mouse";
+ case 007: return "disp";
+ }
+ return "???";
+}
+
+/** @brief F1 function names */
+const char* alto2_cpu_device::f1_name(UINT8 f1)
+{
+ switch (f1) {
+ case 000: return "nop";
+ case 001: return "load_mar";
+ case 002: return "task";
+ case 003: return "block";
+ case 004: return "l_lsh_1";
+ case 005: return "l_rsh_1";
+ case 006: return "l_lcy_8";
+ case 007: return "const";
+ case 010: return "task_10";
+ case 011: return "task_11";
+ case 012: return "task_12";
+ case 013: return "task_13";
+ case 014: return "task_14";
+ case 015: return "task_15";
+ case 016: return "task_16";
+ case 017: return "task_17";
+ }
+ return "???";
+}
+
+/** @brief F2 function names */
+const char* alto2_cpu_device::f2_name(UINT8 f2)
+{
+ switch (f2) {
+ case 000: return "nop";
+ case 001: return "bus=0";
+ case 002: return "shifter<0";
+ case 003: return "shifter=0";
+ case 004: return "bus";
+ case 005: return "alucy";
+ case 006: return "load_md";
+ case 007: return "const";
+ case 010: return "task_10";
+ case 011: return "task_11";
+ case 012: return "task_12";
+ case 013: return "task_13";
+ case 014: return "task_14";
+ case 015: return "task_15";
+ case 016: return "task_16";
+ case 017: return "task_17";
+ }
+ return "???";
+}
+
+#if ALTO2_DEBUG
+void alto2_cpu_device::watch_read(UINT32 addr, UINT32 data)
+{
+ LOG((LOG_MEM,0,"mem: rd[%06o] = %06o\n", addr, data));
+}
+
+void alto2_cpu_device::watch_write(UINT32 addr, UINT32 data)
+{
+ LOG((LOG_MEM,0,"mem: wr[%06o] = %06o\n", addr, data));
+}
+#endif
+
+/** @brief fatal exit on unitialized dynamic phase BUS source */
+void alto2_cpu_device::fn_bs_bad_0()
+{
+ fatal(9,"fatal: bad early bus source pointer for task %s, mpc:%05o bs:%s\n",
+ task_name(m_task), m_mpc, bs_name(m_d_bs));
+}
+
+/** @brief fatal exit on unitialized latching phase BUS source */
+void alto2_cpu_device::fn_bs_bad_1()
+{
+ fatal(9,"fatal: bad late bus source pointer for task %s, mpc:%05o bs: %s\n",
+ task_name(m_task), m_mpc, bs_name(m_d_bs));
+}
+
+/** @brief fatal exit on unitialized dynamic phase F1 function */
+void alto2_cpu_device::fn_f1_bad_0()
+{
+ fatal(9,"fatal: bad early f1 function pointer for task %s, mpc:%05o f1: %s\n",
+ task_name(m_task), m_mpc, f1_name(m_d_f1));
+}
+
+/** @brief fatal exit on unitialized latching phase F1 function */
+void alto2_cpu_device::fn_f1_bad_1()
+{
+ fatal(9,"fatal: bad late f1 function pointer for task %s, mpc:%05o f1: %s\n",
+ task_name(m_task), m_mpc, f1_name(m_d_f1));
+}
+
+/** @brief fatal exit on unitialized dynamic phase F2 function */
+void alto2_cpu_device::fn_f2_bad_0()
+{
+ fatal(9,"fatal: bad early f2 function pointer for task %s, mpc:%05o f2: %s\n",
+ task_name(m_task), m_mpc, f2_name(m_d_f2));
+}
+
+/** @brief fatal exit on unitialized latching phase F2 function */
+void alto2_cpu_device::fn_f2_bad_1()
+{
+ fatal(9,"fatal: bad late f2 function pointer for task %s, mpc:%05o f2: %s\n",
+ task_name(m_task), m_mpc, f2_name(m_d_f2));
+}
+
+#if ALTO2_DEBUG
+typedef struct {
+ UINT16 first, last;
+ const char* name;
+} memory_range_name_t;
+
+memory_range_name_t memory_range_name_table[] = {
+ {0177016, 0177017, "UTILOUT Printer output (Std. Hardware)"},
+ {0177020, 0177023, "XBUS Utility input bus (Alto II Std. Hardware)"},
+ {0177024, 0177024, "MEAR Memory Error Address Register (Alto II Std. Hardware)"},
+ {0177025, 0177025, "MESR Memory error status register (Alto II Std. Hardware)"},
+ {0177026, 0177026, "MECR Memory error control register (Alto II Std. Hardware)"},
+ {0177030, 0177033, "UTILIN Printer status, mouse, keyset (all 4 locations return same thing)"},
+ {0177034, 0177037, "KBDAD Undecoded keyboard (Std. Hardware)"},
+ {0177740, 0177757, "BANKREGS Extended memory option bank registers"},
+ {0177100, 0177100, "- Sumagraphics tablet X"},
+ {0177101, 0177101, "- Sumagraphics tablet Y"},
+ {0177140, 0177157, "- Organ keyboard"},
+ {0177200, 0177204, "- PROM programmer"},
+ {0177234, 0177237, "- Experimental ursor control"},
+ {0177240, 0177257, "- Alto II debugger"},
+ {0177244, 0177247, "- Graphics keyboard"},
+ {0177400, 0177405, "- Maxc2 maintenance interface"},
+ {0177400, 0177400, "- Alto DLS input (0)"},
+ {0177420, 0177420, "- Alto DLS input (1)"},
+ {0177440, 0177440, "- Alto DLS input (2)"},
+ {0177460, 0177460, "- Alto DLS input (3)"},
+ {0177600, 0177677, "- Alto DLS output"},
+ {0177700, 0177700, "- EIA interface output bit"},
+ {0177701, 0177701, "EIALOC EIA interface input bit"},
+ {0177720, 0177737, "- TV Camera Interface"},
+ {0177764, 0177773, "- Redactron tape drive"},
+ {0177776, 0177776, "- Digital-Analog Converter, Joystick"},
+ {0177777, 0177777, "- Digital-Analog Converter, Joystick"}
+};
+
+static const char* memory_range_name(offs_t offset)
+{
+ int _min = 0;
+ int _max = sizeof(memory_range_name_table) / sizeof(memory_range_name_table[0]) - 1;
+ int _mid;
+
+ offset %= ALTO2_IO_PAGE_SIZE;
+ offset += ALTO2_IO_PAGE_BASE;
+
+ /* binary search in table of memory ranges */
+ while (_max >= _min)
+ {
+ _mid = (_min + _max) / 2;
+ if (memory_range_name_table[_mid].last < offset)
+ _min = _mid + 1;
+ else if (memory_range_name_table[_mid].first > offset)
+ _max = _mid - 1;
+ else if (memory_range_name_table[_mid].first <= offset &&
+ memory_range_name_table[_mid].last >= offset)
+ return memory_range_name_table[_mid].name;
+ }
+ return "- UNUSED";
+}
+
+#endif
+
+/**
+ * @brief read the open bus for unused MMIO range
+ */
+READ16_MEMBER( alto2_cpu_device::noop_r )
+{
+ LOG((LOG_CPU,0," MMIO rd %s\n", memory_range_name(offset)));
+ return 0177777;
+}
+
+/**
+ * @brief write nowhere for unused MMIO range
+ */
+WRITE16_MEMBER( alto2_cpu_device::noop_w )
+{
+ LOG((LOG_CPU,0," MMIO wr %s\n", memory_range_name(offset)));
+}
+
+/**
+ * @brief read bank register in memory mapped I/O range
+ *
+ * The bank registers are stored in a 16x4-bit RAM 74S189.
+ */
+READ16_MEMBER( alto2_cpu_device::bank_reg_r )
+{
+ int task = offset & 017;
+ int bank = m_bank_reg[task] | 0177760;
+ return bank;
+}
+
+/**
+ * @brief write bank register in memory mapped I/O range
+ *
+ * The bank registers are stored in a 16x4-bit RAM 74S189.
+ */
+WRITE16_MEMBER( alto2_cpu_device::bank_reg_w )
+{
+ int task = offset & 017;
+ m_bank_reg[task] = data & 017;
+ LOG((LOG_CPU,0," write bank[%02o]=%#o normal:%o extended:%o (%s)\n",
+ task, data,
+ GET_BANK_NORMAL(data),
+ GET_BANK_EXTENDED(data),
+ task_name(task)));
+}
+
+/**
+ * @brief bs_read_r early: drive bus by R register
+ */
+void alto2_cpu_device::bs_early_read_r()
+{
+ UINT16 r = m_r[m_rsel];
+ LOG((LOG_CPU,2," <-R%02o; %s (%#o)\n", m_rsel, r_name(m_rsel), r));
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_load_r early: load R places 0 on the BUS
+ */
+void alto2_cpu_device::bs_early_load_r()
+{
+ UINT16 r = 0;
+ LOG((LOG_CPU,2," R%02o<-; %s (BUS&=0)\n", m_rsel, r_name(m_rsel)));
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_load_r late: load R from SHIFTER
+ */
+void alto2_cpu_device::bs_late_load_r()
+{
+ if (m_d_f2 != f2_emu_load_dns) {
+ m_r[m_rsel] = m_shifter;
+ LOG((LOG_CPU,2," R%02o<-; %s = SHIFTER (%#o)\n", m_rsel, r_name(m_rsel), m_shifter));
+#if 0
+ /* HACK: programs writing r37 with xxx3 make the cursor
+ * display go nuts. Until I found the real reason for this
+ * obviously buggy display, I just clear the two
+ * least significant bits of r37 if they are set at once.
+ */
+ if (m_rsel == 037 && ((m_shifter & 3) == 3)) {
+ printf("writing r37 = %#o\n", m_shifter);
+ m_r[037] &= ~3;
+ }
+#endif
+ }
+}
+
+/**
+ * @brief bs_read_md early: drive BUS from read memory data
+ */
+void alto2_cpu_device::bs_early_read_md()
+{
+#if ALTO2_DEBUG
+ UINT32 mar = m_mem.mar;
+#endif
+ UINT16 md = read_mem();
+ LOG((LOG_CPU,2," <-MD; BUS&=MD (%#o=[%#o])\n", md, mar));
+ m_bus &= md;
+}
+
+/**
+ * @brief bs_mouse early: drive bus by mouse
+ */
+void alto2_cpu_device::bs_early_mouse()
+{
+ UINT16 r = mouse_read();
+ LOG((LOG_CPU,2," <-MOUSE; BUS&=MOUSE (%#o)\n", r));
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_disp early: drive bus by displacement (which?)
+ */
+void alto2_cpu_device::bs_early_disp()
+{
+ UINT16 r = 0177777;
+ LOG((LOG_CPU,0,"BS <-DISP not handled by task %s mpc:%04x\n", task_name(m_task), m_mpc));
+ LOG((LOG_CPU,2," <-DISP; BUS&=DISP ?? (%#o)\n", r));
+ m_bus &= r;
+}
+
+/**
+ * @brief f1_load_mar late: load memory address register
+ *
+ * Load memory address register from the ALU output;
+ * start main memory reference (see section 2.3).
+ */
+void alto2_cpu_device::f1_late_load_mar()
+{
+ UINT8 bank = m_bank_reg[m_task];
+ UINT32 msb;
+ if (m_d_f2 == f2_load_md) {
+ msb = GET_BANK_EXTENDED(bank) << 16;
+ LOG((LOG_CPU,7, " XMAR %#o\n", msb | m_alu));
+ } else {
+ msb = GET_BANK_NORMAL(bank) << 16;
+
+ }
+ load_mar(m_rsel, msb | m_alu);
+}
+
+#if USE_PRIO_F9318
+/** @brief F9318 input lines */
+typedef enum {
+ PRIO_IN_EI = (1<<8),
+ PRIO_IN_I7 = (1<<7),
+ PRIO_IN_I6 = (1<<6),
+ PRIO_IN_I5 = (1<<5),
+ PRIO_IN_I4 = (1<<4),
+ PRIO_IN_I3 = (1<<3),
+ PRIO_IN_I2 = (1<<2),
+ PRIO_IN_I1 = (1<<1),
+ PRIO_IN_I0 = (1<<0),
+ /* masks */
+ PRIO_I7 = PRIO_IN_I7,
+ PRIO_I6_I7 = (PRIO_IN_I6 | PRIO_IN_I7),
+ PRIO_I5_I7 = (PRIO_IN_I5 | PRIO_IN_I6 | PRIO_IN_I7),
+ PRIO_I4_I7 = (PRIO_IN_I4 | PRIO_IN_I5 | PRIO_IN_I6 | PRIO_IN_I7),
+ PRIO_I3_I7 = (PRIO_IN_I3 | PRIO_IN_I4 | PRIO_IN_I5 | PRIO_IN_I6 | PRIO_IN_I7),
+ PRIO_I2_I7 = (PRIO_IN_I2 | PRIO_IN_I3 | PRIO_IN_I4 | PRIO_IN_I5 | PRIO_IN_I6 | PRIO_IN_I7),
+ PRIO_I1_I7 = (PRIO_IN_I1 | PRIO_IN_I2 | PRIO_IN_I3 | PRIO_IN_I4 | PRIO_IN_I5 | PRIO_IN_I6 | PRIO_IN_I7),
+ PRIO_I0_I7 = (PRIO_IN_I0 | PRIO_IN_I1 | PRIO_IN_I2 | PRIO_IN_I3 | PRIO_IN_I4 | PRIO_IN_I5 | PRIO_IN_I6 | PRIO_IN_I7),
+} f9318_in_t;
+
+/** @brief F9318 output lines */
+typedef enum {
+ PRIO_OUT_Q0 = (1<<0),
+ PRIO_OUT_Q1 = (1<<1),
+ PRIO_OUT_Q2 = (1<<2),
+ PRIO_OUT_EO = (1<<3),
+ PRIO_OUT_GS = (1<<4),
+ /* masks */
+ PRIO_OUT_QZ = (PRIO_OUT_Q0 | PRIO_OUT_Q1 | PRIO_OUT_Q2)
+} f9318_out_t;
+
+/**
+ * @brief F9318 priority encoder 8 to 3-bit
+ *
+ * Emulation of the F9318 chip (pin compatible with 74348).
+ *
+ * <PRE>
+ * F9318
+ * +---+-+---+
+ * | +-+ | +---------------------------------+----------------+
+ * I4' -|1 16|- Vcc | input | output |
+ * | | +---------------------------------+----------------+
+ * I5' -|2 15|- EO' | EI I0 I1 I2 I3 I4 I5 I6 I7 | GS Q0 Q1 Q2 EO |
+ * | | +---------------------------------+----------------+
+ * I6' -|3 14|- GS' | (a) H x x x x x x x x | H H H H H |
+ * | | | (b) L H H H H H H H H | H H H H L |
+ * I7' -|4 13|- I3' +---------------------------------+----------------+
+ * | | | (c) L x x x x x x x L | L L L L H |
+ * EI' -|5 12|- I2' | (d) L x x x x x x L H | L H L L H |
+ * | | | (e) L x x x x x L H H | L L H L H |
+ * Q2' -|6 11|- I1' | (f) L x x x x L H H H | L H H L H |
+ * | | | (g) L x x x L H H H H | L L L H H |
+ * Q1' -|7 10|- I0' | (h) L x x L H H H H H | L H L H H |
+ * | | | (i) L x L H H H H H H | L L H H H |
+ * GND -|8 9|- Q0' | (j) L L H H H H H H H | L H H H H |
+ * | | +---------------------------------+----------------+
+ * +---------+
+ * </PRE>
+ */
+static __inline f9318_out_t f9318(f9318_in_t in)
+{
+ f9318_out_t out;
+
+ if (in & PRIO_IN_EI) {
+ out = PRIO_OUT_EO | PRIO_OUT_GS | PRIO_OUT_QZ;
+ LOG((LOG_CPU,2," f9318 case (a) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (0 == (in & PRIO_I7)) {
+ out = PRIO_OUT_EO;
+ LOG((LOG_CPU,2," f9318 case (c) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (PRIO_I7 == (in & PRIO_I6_I7)) {
+ out = PRIO_OUT_EO | PRIO_OUT_Q0;
+ LOG((LOG_CPU,2," f9318 case (d) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (PRIO_I6_I7 == (in & PRIO_I5_I7)) {
+ out = PRIO_OUT_EO | PRIO_OUT_Q1;
+ LOG((LOG_CPU,2," f9318 case (e) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (PRIO_I5_I7 == (in & PRIO_I4_I7)) {
+ out = PRIO_OUT_EO | PRIO_OUT_Q0 | PRIO_OUT_Q1;
+ LOG((LOG_CPU,2," f9318 case (f) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (PRIO_I4_I7 == (in & PRIO_I3_I7)) {
+ out = PRIO_OUT_EO | PRIO_OUT_Q2;
+ LOG((LOG_CPU,2," f9318 case (g) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (PRIO_I3_I7 == (in & PRIO_I2_I7)) {
+ out = PRIO_OUT_EO | PRIO_OUT_Q0 | PRIO_OUT_Q2;
+ LOG((LOG_CPU,2," f9318 case (h) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (PRIO_I2_I7 == (in & PRIO_I1_I7)) {
+ out = PRIO_OUT_EO | PRIO_OUT_Q1 | PRIO_OUT_Q2;
+ LOG((LOG_CPU,2," f9318 case (i) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ if (PRIO_I1_I7 == (in & PRIO_I0_I7)) {
+ out = PRIO_OUT_EO | PRIO_OUT_Q0 | PRIO_OUT_Q1 | PRIO_OUT_Q2;
+ LOG((LOG_CPU,2," f9318 case (j) in:%#o out:%#o\n", in, out));
+ return out;
+ }
+
+ out = PRIO_OUT_QZ | PRIO_OUT_GS;
+ LOG((LOG_CPU,2," f9318 case (b) in:%#o out:%#o\n", in, out));
+ return out;
+}
+#endif
+
+/**
+ * @brief f1_task early: task switch
+ *
+ * The priority encoder finds the highest task requesting service
+ * and switches the task number after the next cycle.
+ *
+ * <PRE>
+ * CT PROM NEXT' RDCT'
+ * 1 2 4 8 DATA 6 7 8 9 1 2 4 8
+ * ---------------------------------
+ * 0 0 0 0 0367 1 1 1 1 0 1 1 1
+ * 1 0 0 0 0353 1 1 1 0 1 0 1 1
+ * 0 1 0 0 0323 1 1 0 1 0 0 1 1
+ * 1 1 0 0 0315 1 1 0 0 1 1 0 1
+ * 0 0 1 0 0265 1 0 1 1 0 1 0 1
+ * 1 0 1 0 0251 1 0 1 0 1 0 0 1
+ * 0 1 1 0 0221 1 0 0 1 0 0 0 1
+ * 1 1 1 0 0216 1 0 0 0 1 1 1 0
+ * 0 0 0 1 0166 0 1 1 1 0 1 1 0
+ * 1 0 0 1 0152 0 1 1 0 1 0 1 0
+ * 0 1 0 1 0122 0 1 0 1 0 0 1 0
+ * 1 1 0 1 0114 0 1 0 0 1 1 0 0
+ * 0 0 1 1 0064 0 0 1 1 0 1 0 0
+ * 1 0 1 1 0050 0 0 1 0 1 0 0 0
+ * 0 1 1 1 0020 0 0 0 1 0 0 0 0
+ * 1 1 1 1 0017 0 0 0 0 1 1 1 1
+ *
+ * The various task wakeups are encoded using two 8:3-bit priority encoders F9318,
+ * which are pin-compatible to the 74348 (inverted inputs and outputs).
+ * Their part numbers are U1 and U2.
+ * The two encoders are chained (EO of U1 goes to EI of U2):
+ *
+ * The outputs are fed into some NAND gates (74H10 and 74H00) to decode
+ * the task number to latch (CT1-CT4) after a F1 TASK. The case where all
+ * of RDCT1' to RDCT8' are high (1) is decoded as RESET'.
+ *
+ * signal function
+ * --------------------------------------------------
+ * CT1 (U1.Q0' & U2.Q0' & RDCT1')'
+ * CT2 (U1.Q1' & U2.Q1' & RDCT2')'
+ * CT4 (U1.Q2' & U2.Q2' & RDCT4')'
+ * CT8 (U1.GS' & RDCT8')'
+ * RESET' RDCT1' & RDCT2' & RDCT4' & RDCT8'
+ *
+ * In the tables below "x" is RDCTx' of current task
+ *
+ * signal input output, if first 0 CT1 CT2 CT4 CT8
+ * ----------------------------------------------------------------------------------------
+ * WAKE17' (T19?) 4 I7 Q2:0 Q1:0 Q0:0 GS:0 EO:1 1 1 1 1
+ * WAKEKWDT' 3 I6 Q2:0 Q1:0 Q0:1 GS:0 EO:1 x 1 1 1
+ * WAKEPART' 2 I5 Q2:0 Q1:1 Q0:0 GS:0 EO:1 1 x 1 1
+ * WAKEDVT' 1 I4 Q2:0 Q1:1 Q0:1 GS:0 EO:1 x x 1 1
+ * WAKEDHT' 13 I3 Q2:1 Q1:0 Q0:0 GS:0 EO:1 1 1 x 1
+ * WAKECURT' 12 I2 Q2:1 Q1:0 Q0:1 GS:0 EO:1 x 1 x 1
+ * WAKEDWT' 11 I1 Q2:1 Q1:1 Q0:0 GS:0 EO:1 1 x x 1
+ * WAKEMRT' 10 I0 Q2:1 Q1:1 Q0:1 GS:0 EO:1 x x x 1
+ * otherwise Q2:1 Q1:1 Q0:1 GS:1 EO:0 x x x x
+ *
+ * signal input output, if first 0
+ * ----------------------------------------------------------------------------------------
+ * WAKEET' 4 I7 Q2:0 Q1:0 Q0:0 GS:0 EO:1 1 1 1 x
+ * WAKE6' 3 I6 Q2:0 Q1:0 Q0:1 GS:0 EO:1 x 1 1 x
+ * WAKE5' 2 I5 Q2:0 Q1:1 Q0:0 GS:0 EO:1 1 x 1 x
+ * WAKEKST' 1 I4 Q2:0 Q1:1 Q0:1 GS:0 EO:1 x x 1 x
+ * WAKE3' (T23?) 13 I3 Q2:1 Q1:0 Q0:0 GS:0 EO:1 1 1 x x
+ * WAKE2' 12 I2 Q2:1 Q1:0 Q0:1 GS:0 EO:1 x 1 x x
+ * WAKE1' 11 I1 Q2:1 Q1:1 Q0:0 GS:0 EO:1 1 x x x
+ * 0 (GND) 10 I0 Q2:1 Q1:1 Q0:1 GS:0 EO:1 x x x x
+ * </PRE>
+ */
+void alto2_cpu_device::f1_early_task()
+{
+#if USE_PRIO_F9318
+ /* Doesn't work yet */
+ register f9318_in_t wakeup_hi;
+ register f9318_out_t u1;
+ register f9318_in_t wakeup_lo;
+ register f9318_out_t u2;
+ register int addr = 017;
+ register int rdct1, rdct2, rdct4, rdct8;
+ register int ct1, ct2, ct4, ct8;
+ register int wakeup, ct;
+
+ LOG((LOG_CPU,2, " TASK %02o:%s\n", m_task, task_name(m_task)));
+
+ if (m_task > task_emu && (m_task_wakeup & (1 << m_task)))
+ addr = m_task;
+ LOG((LOG_CPU,2," ctl2k_u38[%02o] = %04o\n", addr, ctl2k_u38[addr] & 017));
+
+ rdct1 = (ctl2k_u38[addr] >> U38_RDCT1) & 1;
+ rdct2 = (ctl2k_u38[addr] >> U38_RDCT2) & 1;
+ rdct4 = (ctl2k_u38[addr] >> U38_RDCT4) & 1;
+ rdct8 = (ctl2k_u38[addr] >> U38_RDCT8) & 1;
+
+ /* wakeup signals are active low */
+ wakeup = ~m_task_wakeup;
+
+ /* U1
+ * task wakeups 017 to 010 on I7 to I0
+ * EI is 0 (would be 1 at reset)
+ */
+ wakeup_hi = (wakeup >> 8) & PRIO_I0_I7;
+ u1 = f9318(wakeup_hi);
+
+ /* U2
+ * task wakeups 007 to 001 on I7 to I1, I0 is 0
+ * EO of U1 chained to EI
+ */
+ wakeup_lo = wakeup & PRIO_I0_I7;
+ if (u1 & PRIO_OUT_EO)
+ wakeup_lo |= PRIO_IN_EI;
+ u2 = f9318(wakeup_lo);
+
+ /* CT1 = (U1.Q0' & U2.Q0' & RDCT1')' */
+ ct1 = !((u1 & PRIO_OUT_Q0) && (u2 & PRIO_OUT_Q0) && rdct1);
+ LOG((LOG_CPU,2," CT1:%o U1.Q0':%o U2.Q0':%o RDCT1':%o\n",
+ ct1, (u1 & PRIO_OUT_Q0)?1:0, (u2 & PRIO_OUT_Q0)?1:0, rdct1));
+ /* CT2 = (U1.Q1' & U2.Q1' & RDCT2')' */
+ ct2 = !((u1 & PRIO_OUT_Q1) && (u2 & PRIO_OUT_Q1) && rdct2);
+ LOG((LOG_CPU,2," CT2:%o U1.Q1':%o U2.Q1':%o RDCT2':%o\n",
+ ct2, (u1 & PRIO_OUT_Q1)?1:0, (u2 & PRIO_OUT_Q1)?1:0, rdct2));
+ /* CT4 = (U1.Q2' & U2.Q2' & RDCT4')' */
+ ct4 = !((u1 & PRIO_OUT_Q2) && (u2 & PRIO_OUT_Q2) && rdct4);
+ LOG((LOG_CPU,2," CT4:%o U1.Q2':%o U2.Q2':%o RDCT4':%o\n",
+ ct4, (u1 & PRIO_OUT_Q2)?1:0, (u2 & PRIO_OUT_Q2)?1:0, rdct4));
+ /* CT8 */
+ ct8 = !((u1 & PRIO_OUT_GS) && rdct8);
+ LOG((LOG_CPU,2," CT8:%o U1.GS':%o RDCT8':%o\n",
+ ct8, (u1 & PRIO_OUT_GS)?1:0, rdct8));
+
+ ct = 8*ct8 + 4*ct4 + 2*ct2 + ct1;
+
+ if (ct != m_next_task) {
+ LOG((LOG_CPU,2, " switch to %02o\n", ct));
+ m_next2_task = ct;
+ } else {
+ LOG((LOG_CPU,2, " no switch\n"));
+ }
+#else /* USE_PRIO_F9318 */
+ LOG((LOG_CPU,2, " TASK %02o:%s", m_task, task_name(m_task)));
+ for (int i = 15; i >= 0; i--) {
+ if (m_task_wakeup & (1 << i)) {
+ m_next2_task = i;
+ if (m_next2_task != m_next_task) {
+ LOG((LOG_CPU,2, " switch to %02o:%s\n", m_next2_task, task_name(m_next2_task)));
+ } else {
+ LOG((LOG_CPU,2, " no switch\n"));
+ }
+ return;
+ }
+ }
+ fatal(3, "no tasks requesting service\n");
+#endif /* !USE_PRIO_F9318 */
+}
+
+/**
+ * @brief block task
+ *
+ * The task wakeup for the active task is cleared
+ */
+void alto2_cpu_device::f1_early_block()
+{
+ m_task_wakeup &= ~(1 << m_task);
+ LOG((LOG_CPU,2, " BLOCK %02o:%s\n", m_task, task_name(m_task)));
+}
+
+/**
+ * @brief SHIFTER = L shifted left once
+ */
+void alto2_cpu_device::f1_late_l_lsh_1()
+{
+ m_shifter = m_l << 1;
+ LOG((LOG_CPU,2," SHIFTER <-L LSH 1 (%#o := %#o<<1)\n", m_shifter, m_l));
+}
+
+/**
+ * @brief SHIFTER = L shifted right once
+ */
+void alto2_cpu_device::f1_late_l_rsh_1()
+{
+ m_shifter = m_l >> 1;
+ LOG((LOG_CPU,2," SHIFTER <-L RSH 1 (%#o := %#o>>1)\n", m_shifter, m_l));
+}
+
+/**
+ * @brief SHIFTER = L cycled 8 times (byte swap)
+ */
+void alto2_cpu_device::f1_late_l_lcy_8()
+{
+ m_shifter = (m_l >> 8) | (m_l << 8);
+ LOG((LOG_CPU,2," SHIFTER <-L LCY 8 (%#o := bswap %#o)\n", m_shifter, m_l));
+}
+
+/**
+ * @brief f2_bus_eq_zero late: branch on bus equals zero
+ */
+void alto2_cpu_device::f2_late_bus_eq_zero()
+{
+ UINT16 r = m_bus == 0 ? 1 : 0;
+ LOG((LOG_CPU,2, " BUS=0; %sbranch (%#o|%#o)\n", r ? "" : "no ", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief branch on shifter less than zero
+ */
+void alto2_cpu_device::f2_late_shifter_lt_zero()
+{
+ UINT16 r = (m_shifter & 0100000) ? 1 : 0;
+ LOG((LOG_CPU,2, " SH<0; %sbranch (%#o|%#o)\n", r ? "" : "no ", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief branch on shifter equals zero
+ */
+void alto2_cpu_device::f2_late_shifter_eq_zero()
+{
+ UINT16 r = m_shifter == 0 ? 1 : 0;
+ LOG((LOG_CPU,2, " SH=0; %sbranch (%#o|%#o)\n", r ? "" : "no ", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief f2_bus late: branch on bus bits BUS[6-15]
+ */
+void alto2_cpu_device::f2_late_bus()
+{
+ UINT16 r = X_RDBITS(m_bus,16,6,15);
+ LOG((LOG_CPU,2, " BUS; %sbranch (%#o|%#o)\n", r ? "" : "no ", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief f2_alucy late: branch on latched ALU carry
+ */
+void alto2_cpu_device::f2_late_alucy()
+{
+ UINT16 r = m_laluc0;
+ LOG((LOG_CPU,2, " ALUCY; %sbranch (%#o|%#o)\n", r ? "" : "no ", m_next2, r));
+ m_next2 |= r;
+}
+
+/**
+ * @brief f2_load_md late: load memory data
+ *
+ * Deliver BUS data to memory.
+ */
+void alto2_cpu_device::f2_late_load_md()
+{
+#if ALTO2_DEBUG
+ UINT16 mar = m_mem.mar;
+#endif
+ if (m_d_f1 == f1_load_mar) {
+ /* part of an XMAR */
+ LOG((LOG_CPU,2, " XMAR %#o (%#o)\n", mar, m_bus));
+ } else {
+ write_mem(m_bus);
+ LOG((LOG_CPU,2, " MD<- BUS ([%#o]=%#o)\n", mar, m_bus));
+ }
+}
+
+#if USE_ALU_74181
+/**
+ * <PRE>
+ * Functional description of the 4-bit ALU 74181
+ *
+ * The 74181 is a 4-bit high speed parallel Arithmetic Logic Unit (ALU).
+ * Controlled by four Function Select inputs (S0-S3) and the Mode Control
+ * input (M), it can perform all the 16 possible logic operations or 16
+ * different arithmetic operations on active HIGH or active LOW operands.
+ * The Function Table lists these operations.
+ *
+ * When the Mode Control input (M) is HIGH, all internal carries are
+ * inhibited and the device performs logic operations on the individual
+ * bits as listed. When the Mode Control input is LOW, the carries are
+ * enabled and the device performs arithmetic operations on the two 4-bit
+ * words. The device incorporates full internal carry lookahead and
+ * provides for either ripple carry between devices using the Cn+4 output,
+ * or for carry lookahead between packages using the signals P' (Carry
+ * Propagate) and G' (Carry Generate). In the ADD mode, P' indicates that
+ * F' is 15 or more, while G' indicates that F' is 16 or more. In the
+ * SUBTRACT mode, P' indicates that F' is zero or less, while G' indicates
+ * that F' is less than zero. P' and G' are not affected by carry in.
+ * When speed requirements are not stringent, it can be used in a simple
+ * ripple carry mode by connecting the Carry output (Cn+4) signal to the
+ * Carry input (Cn) of the next unit. For high speed operation the device
+ * is used in conjunction with the 74182 carry lookahead circuit. One
+ * carry lookahead package is required for each group of four 74181 devices.
+ * Carry lookahead can be provided at various levels and offers high speed
+ * capability over extremely long word lengths.
+ *
+ * The A=B output from the device goes HIGH when all four F' outputs are
+ * HIGH and can be used to indicate logic equivalence over four bits when
+ * the unit is in the subtract mode. The A=B output is open collector and
+ * can be wired-AND with other A=B outputs to give a comparison for more
+ * than four bits. The A=B signal can also be used with the Cn+4 signal
+ * to indicated A>B and A<B.
+ *
+ * The Function Table lists the arithmetic operations that are performed
+ * without a carry in. An incoming carry adds a one to each operation.
+ * Thus, select code 0110 generates A minus B minus 1 (2's complement
+ * notation) without a carry in and generates A minus B when a carry is
+ * applied. Because subtraction is actually performed by the complementary
+ * addition (1's complement), a carry out means borrow; thus a carry is
+ * generated when there is no underflow and no carry is generated when
+ * there is underflow. As indicated, this device can be used with either
+ * active LOW inputs producing active LOW outputs or with active HIGH
+ * inputs producing active HIGH outputs. For either case the table lists
+ * the operations that are performed to the operands labeled inside the
+ * logic symbol.
+ *
+ * The AltoI/II use four 74181s and a 74182 carry lookahead circuit,
+ * and the inputs and outputs are all active HIGH.
+ *
+ * Active HIGH operands:
+ *
+ * +-------------------+-------------+------------------------+------------------------+
+ * | Mode Select | Logic | Arithmetic w/o carry | Arithmetic w/ carry |
+ * | Inputs | | | |
+ * | S3 S2 S1 S0 | (M=1) | (M=0) (Cn=1) | (M=0) (Cn=0) |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 0 0 0 | A' | A | A + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 0 0 1 | A' | B' | A | B | (A | B) + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 0 1 0 | A' & B | A | B' | (A | B') + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 0 1 1 | logic 0 | - 1 | -1 + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 1 0 0 | (A & B)' | A + (A & B') | A + (A & B') + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 1 0 1 | B' | (A | B) + (A & B') | (A | B) + (A & B') + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 1 1 0 | A ^ B | A - B - 1 | A - B - 1 + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 0 1 1 1 | A & B' | (A & B) - 1 | (A & B) - 1 + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 0 0 0 | A' | B | A + (A & B) | A + (A & B) + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 0 0 1 | A' ^ B' | A + B | A + B + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 0 1 0 | B | (A | B') + (A & B) | (A | B') + (A & B) + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 0 1 1 | A & B | (A & B) - 1 | (A & B) - 1 + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 1 0 0 | logic 1 | A + A | A + A + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 1 0 1 | A | B' | (A | B) + A | (A | B) + A + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 1 1 0 | A | B | (A | B') + A | (A | B') + A + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * | 1 1 1 1 | A | A - 1 | A - 1 + 1 |
+ * +-------------------+-------------+------------------------+------------------------+
+ * </PRE>
+ */
+
+//! S function, M flag and C carry in
+#define SMC(s3,s2,s1,s0,m,ci) (s3*A10_ALUS3 + s2*A10_ALUS2 + s1*A10_ALUS1 + s0*A10_ALUS0 + m*A10_ALUM + ci*A10_ALUCI)
+
+/**
+ * @brief Compute the 74181 ALU operation smc for inputs a and b
+ *
+ * The function, arithmetic / logic flag and carry in define the
+ * ALU operation. The carry in is irrelevant for the logic operations.
+ * The result is 17 bit, where bit #16 is the carry out.
+ *
+ * @param smc S function [0-15], M arithmetic/logic flag, C carry
+ * @return resulting ALU output
+ */
+#if 1
+UINT32 alto2_cpu_device::alu_74181(UINT32 a, UINT32 b, UINT8 smc)
+{
+ register UINT32 f;
+ register const UINT32 cout = 1 << 16;
+
+ switch (smc & A10_ALUIN) {
+ case SMC(0,0,0,0, 0, 0): // 0000: A + 1
+ f = a + 1;
+ break;
+
+ case SMC(0,0,0,0, 0, 1): // 0000: A
+ f = a;
+ break;
+
+ case SMC(0,0,0,0, 1, 0): // 0000: A'
+ case SMC(0,0,0,0, 1, 1):
+ f = (~a) | cout;
+ break;
+
+ case SMC(0,0,0,1, 0, 0): // 0001: (A | B) + 1
+ f = (a | b) + 1;
+ break;
+
+ case SMC(0,0,0,1, 0, 1): // 0001: A | B
+ f = a | b;
+ break;
+
+ case SMC(0,0,0,1, 1, 0): // 0001: A' | B'
+ case SMC(0,0,0,1, 1, 1):
+ f = (~a | ~b) | cout;
+ break;
+
+ case SMC(0,0,1,0, 0, 0): // 0010: (A | B') + 1
+ f = (a | ~b) + 1;
+ break;
+
+ case SMC(0,0,1,0, 0, 1): // 0010: A | B'
+ f = a | ~b;
+ break;
+
+ case SMC(0,0,1,0, 1, 0): // 0010: A' & B
+ case SMC(0,0,1,0, 1, 1):
+ f = (~a & b) | cout;
+ break;
+
+ case SMC(0,0,1,1, 0, 0): // 0011: -1 + 1
+ f = (-1 + 1) | cout;
+ break;
+
+ case SMC(0,0,1,1, 0, 1): // 0011: -1
+ f = (-1) | cout;
+ break;
+
+ case SMC(0,0,1,1, 1, 0): // 0011: logic 0
+ case SMC(0,0,1,1, 1, 1):
+ f = cout;
+ break;
+
+ case SMC(0,1,0,0, 0, 0): // 0100: A + (A & B') + 1
+ f = a + (a & ~b) + 1;
+ break;
+
+ case SMC(0,1,0,0, 0, 1): // 0100: A + (A & B')
+ f = a + (a & ~b);
+ break;
+
+ case SMC(0,1,0,0, 1, 0): // 0100: (A & B)'
+ case SMC(0,1,0,0, 1, 1):
+ f = ~(a & b) | cout;
+ break;
+
+ case SMC(0,1,0,1, 0, 0): // 0101: (A | B) + (A & B') + 1
+ f = (a | b) + (a & ~b) + 1;
+ break;
+
+ case SMC(0,1,0,1, 0, 1): // 0101: (A | B) + (A & B')
+ f = (a | b) + (a & ~b);
+ break;
+
+ case SMC(0,1,0,1, 1, 0): // 0101: B'
+ case SMC(0,1,0,1, 1, 1):
+ f = (~b) | cout;
+ break;
+
+ case SMC(0,1,1,0, 0, 0): // 0110: A - B - 1 + 1
+ f = (a - b - 1 + 1) ^ cout;
+ break;
+
+ case SMC(0,1,1,0, 0, 1): // 0110: A - B - 1
+ f = (a - b - 1) ^ cout;
+ break;
+
+ case SMC(0,1,1,0, 1, 0): // 0110: A ^ B
+ case SMC(0,1,1,0, 1, 1):
+ f = (a ^ b) | cout;
+ break;
+
+ case SMC(0,1,1,1, 0, 0): // 0111: (A & B) - 1 + 1
+ f = ((a & b) - 1 + 1) ^ cout;
+ break;
+
+ case SMC(0,1,1,1, 0, 1): // 0111: (A & B) - 1
+ f = ((a & b) - 1) ^ cout;
+ break;
+
+ case SMC(0,1,1,1, 1, 0): // 0111: A & B'
+ case SMC(0,1,1,1, 1, 1):
+ f = (a & ~b) | cout;
+ break;
+
+ case SMC(1,0,0,0, 0, 0): // 1000: A + (A & B) + 1
+ f = a + (a & b) + 1;
+ break;
+
+ case SMC(1,0,0,0, 0, 1): // 1000: A + (A & B)
+ f = a + (a & b);
+ break;
+
+ case SMC(1,0,0,0, 1, 0): // 1000: A' | B
+ case SMC(1,0,0,0, 1, 1):
+ f = (~a | b) | cout;
+ break;
+
+ case SMC(1,0,0,1, 0, 0): // 1001: A + B + 1
+ f = a + b + 1;
+ break;
+
+ case SMC(1,0,0,1, 0, 1): // 1001: A + B
+ f = a + b;
+ break;
+
+ case SMC(1,0,0,1, 1, 0): // 1001: A' ^ B'
+ case SMC(1,0,0,1, 1, 1):
+ f = (~a ^ ~b) | cout;
+ break;
+
+ case SMC(1,0,1,0, 0, 0): // 1010: (A | B') + (A & B) + 1
+ f = (a | ~b) + (a & b) + 1;
+ break;
+
+ case SMC(1,0,1,0, 0, 1): // 1010: (A | B') + (A & B)
+ f = (a | ~b) + (a & b);
+ break;
+
+ case SMC(1,0,1,0, 1, 0): // 1010: B
+ case SMC(1,0,1,0, 1, 1):
+ f = (b) | cout;
+ break;
+
+ case SMC(1,0,1,1, 0, 0): // 1011: (A & B) - 1 + 1
+ f = ((a & b) - 1 + 1) ^ cout;
+ break;
+
+ case SMC(1,0,1,1, 0, 1): // 1011: (A & B) - 1
+ f = ((a & b) - 1) ^ cout;
+ break;
+
+ case SMC(1,0,1,1, 1, 0): // 1011: A & B
+ case SMC(1,0,1,1, 1, 1):
+ f = (a & b) | cout;
+ break;
+
+ case SMC(1,1,0,0, 0, 0): // 1100: A + A + 1
+ f = a + a + 1;
+ break;
+
+ case SMC(1,1,0,0, 0, 1): // 1100: A + A
+ f = a + a;
+ break;
+
+ case SMC(1,1,0,0, 1, 0): // 1100: logic 1
+ case SMC(1,1,0,0, 1, 1):
+ f = (~0) | cout;
+ break;
+
+ case SMC(1,1,0,1, 0, 0): // 1101: (A | B) + A + 1
+ f = (a | b) + a + 1;
+ break;
+
+ case SMC(1,1,0,1, 0, 1): // 1101: (A | B) + A
+ f = (a | b) + a;
+ break;
+
+ case SMC(1,1,0,1, 1, 0): // 1101: A | B'
+ case SMC(1,1,0,1, 1, 1):
+ f = (a | ~b) | cout;
+ break;
+
+ case SMC(1,1,1,0, 0, 0): // 1110: (A | B') + A + 1
+ f = (a | ~b) + a + 1;
+ break;
+
+ case SMC(1,1,1,0, 0, 1): // 1110: (A | B') + A
+ f = (a | ~b) + a;
+ break;
+
+ case SMC(1,1,1,0, 1, 0): // 1110: A | B
+ case SMC(1,1,1,0, 1, 1):
+ f = (a | b) | cout;
+ break;
+
+ case SMC(1,1,1,1, 0, 0): // 1111: A - 1 + 1
+ f = (a - 1 + 1) ^ cout;
+ break;
+
+ case SMC(1,1,1,1, 0, 1): // 1111: A - 1
+ f = (a - 1) ^ cout;
+ break;
+
+ case SMC(1,1,1,1, 1, 0): // 1111: A
+ case SMC(1,1,1,1, 1, 1):
+ f = (a) | cout;
+ break;
+ }
+ return f;
+}
+#else
+
+#define DO_74181(ci,mp,s0,s1,s2,s3,a,b,_b0,_b1,_b2,_b3,f,co) do { \
+ int a0 = BIT(a,_b0), a1 = BIT(a,_b1), a2 = BIT(a,_b2), a3 = BIT(a,_b3); \
+ int b0 = BIT(b,_b0), b1 = BIT(b,_b1), b2 = BIT(b,_b2), b3 = BIT(b,_b3); \
+ int ap0 = !(a0 | (b0 & s0) | (s1 & !b0)); \
+ int bp0 = !(((!b0) & s2 & a0) | (a0 & b0 & s3)); \
+ int ap1 = !(a1 | (b1 & s0) | (s1 & !b1)); \
+ int bp1 = !(((!b1) & s2 & a1) | (a1 & b1 & s3)); \
+ int ap2 = !(a2 | (b2 & s0) | (s1 & !b2)); \
+ int bp2 = !(((!b2) & s2 & a2) | (a2 & b2 & s3)); \
+ int ap3 = !(a3 | (b3 & s0) | (s1 & !b3)); \
+ int bp3 = !(((!b3) & s2 & a3) | (a3 & b3 & s3)); \
+ int fp0 = !(ci & mp) ^ ((!ap0) & bp0); \
+ int fp1 = (!((mp & ap0) | (mp & bp0 & ci))) ^ ((!ap1) & bp1); \
+ int fp2 = (!((mp & ap1) | (mp & ap0 & bp1) | (mp & ci & bp0 & bp1))) ^ ((!ap2) & bp2); \
+ int fp3 = (!((mp & ap2) | (mp & ap1 & bp2) | (mp & ap0 & bp1 & bp2) | (mp & ci & bp0 & bp1 & bp2))) ^ ((!ap3) & bp3); \
+ f |= (fp0 << _b0) | (fp1 << _b1) | (fp2 << _b2) | (fp3 << _b3); \
+ int g = !((ap0 & bp1 & bp2 & bp3) | (ap1 & bp2 & bp3) | (ap2 & bp3) | ap3); \
+ co = (!(ci & bp0 & bp1 & bp2 & bp3)) | g; \
+} while (0)
+
+
+UINT32 alto2_cpu_device::alu_74181(UINT32 a, UINT32 b, UINT8 smc)
+{
+ // inputs
+ int ci = !BIT(smc, 2);
+ int mp = !BIT(smc, 3);
+ int s0 = !BIT(smc, 4), s1 = !BIT(smc, 5), s2 = !BIT(smc, 6), s3 = !BIT(smc, 7);
+
+ // outputs
+ UINT32 f = 0;
+ int cn_x;
+ DO_74181(ci, mp,s0,s1,s2,s3,a,b, 0, 1, 2, 3,f,cn_x); // 74181 #1
+ int cn_y;
+ DO_74181(cn_x,mp,s0,s1,s2,s3,a,b, 4, 5, 6, 7,f,cn_y); // 74181 #2
+ int cn_z;
+ DO_74181(cn_y,mp,s0,s1,s2,s3,a,b, 8, 9,10,11,f,cn_z); // 74181 #3
+ int co;
+ DO_74181(cn_z,mp,s0,s1,s2,s3,a,b,12,13,14,15,f,co); // 74181 #4
+ f |= co << 16;
+ return f;
+}
+#endif // 0
+#endif
+
+/** @brief flag that tells whether to load the T register from BUS or ALU */
+#define TSELECT A10_TSELECT
+
+/** @brief flag that tells wheter operation was 0: arithmetic (M=0) or 1: logic (M=1) */
+#define ALUM A10_ALUM
+
+/** @brief execute the CPU for at most nsecs nano seconds */
+void alto2_cpu_device::execute_run()
+{
+ m_next = m_task_mpc[m_task]; // get current task's next mpc and address modifier
+ m_next2 = m_task_next2[m_task];
+
+ do {
+ int do_bs, flags;
+
+ m_mpc = m_next; // next instruction's micro program counter
+ m_mir = RD_UCODE(m_mpc); // fetch the micro code
+
+ // extract the bit fields
+ m_d_rsel = m_rsel = X_RDBITS(m_mir, 32, DRSEL0, DRSEL4);
+ m_d_aluf = X_RDBITS(m_mir, 32, DALUF0, DALUF3);
+ m_d_bs = X_RDBITS(m_mir, 32, DBS0, DBS2);
+ m_d_f1 = X_RDBITS(m_mir, 32, DF1_0, DF1_3);
+ m_d_f2 = X_RDBITS(m_mir, 32, DF2_0, DF2_3);
+ m_d_loadt = X_BIT(m_mir, 32, DLOADT);
+ m_d_loadl = X_BIT(m_mir, 32, DLOADL);
+
+ debugger_instruction_hook(this, m_mpc);
+ m_cycle++;
+
+
+ if (m_d_f1 == f1_load_mar && check_mem_load_mar_stall(m_rsel)) {
+ LOG((LOG_CPU,3, " MAR<- stall\n"));
+ continue;
+ }
+ if (m_d_f2 == f2_load_md && check_mem_write_stall()) {
+ LOG((LOG_CPU,3, " MD<- stall\n"));
+ continue;
+ }
+ /*
+ * Bus source decoding is not performed if f1 == f1_const
+ * or f2 == f2_const. These functions use the MIR BS field to
+ * provide a part of the address to the constant ROM instead.
+ */
+ do_bs = !(m_d_f1 == f1_const || m_d_f2 == f2_const);
+ if (do_bs && m_d_bs == bs_read_md && check_mem_read_stall()) {
+ LOG((LOG_CPU,3, " <-MD stall\n"));
+ continue;
+ }
+ // now read the next instruction field from the MIR and modify it
+ m_next = X_RDBITS(m_mir, 32, NEXT0, NEXT9) | m_next2;
+ // prefetch the next instruction's next field as next2
+ m_next2 = X_RDBITS(RD_UCODE(m_next), 32, NEXT0, NEXT9) | (m_next2 & ~ALTO2_UCODE_PAGE_MASK);
+ LOG((LOG_CPU,2,"%s-%04o: %011o r:%02o aluf:%02o bs:%02o f1:%02o f2:%02o t:%o l:%o next:%05o next2:%05o\n",
+ task_name(m_task), m_mpc, m_mir, m_rsel, m_d_aluf, m_d_bs, m_d_f1, m_d_f2, m_d_loadt, m_d_loadl, m_next, m_next2));
+
+ // BUS is all ones at the start of each cycle
+ m_bus = 0177777;
+
+ if (m_rdram_flag)
+ rdram();
+
+ // The constant memory is gated to the bus by F1 == f1_const, F2 == f2_const, or BS >= 4
+ if (!do_bs || m_d_bs >= bs_task_4) {
+ UINT32 addr = 8 * m_rsel + m_d_bs;
+ // FIXME: is the format of m_const_data endian safe?
+ UINT16 data = m_const_data[2*addr] | (m_const_data[2*addr+1] << 8);
+ m_bus &= data;
+ LOG((LOG_CPU,2," %#o; BUS &= %#o CONST[%03o]\n", m_bus, data, addr));
+ }
+
+ /*
+ * early F2 function has to be called before early BS,
+ * because the emulator task F2 acsource or acdest may
+ * change the m_rsel
+ */
+ ((*this).*m_f2[0][m_task][m_d_f2])();
+
+ // early BS function can be done now
+ if (do_bs)
+ ((*this).*m_bs[0][m_task][m_d_bs])();
+
+ // early F1 function
+ ((*this).*m_f1[0][m_task][m_d_f1])();
+
+#if USE_ALU_74181
+ /**
+ * The ALU a10 PROM address lines are
+ * A4:SKIP A3:ALUF0 A2:ALUF1 A1:ALUF2 A0:ALUF3
+ * The PROM output lines are
+ * B0: unused B1: TSELECT B2: ALUCI' B3: ALUM'
+ * B4: ALUS0' B5: ALUS1' B6: ALUS2' B7: ALUS3'
+ *
+ * B1 and B3-B7 are inverted on loading the PROM
+ */
+ UINT8 a10 = m_alu_a10[(m_emu.skip << 4) | m_d_aluf];
+ UINT32 alu = alu_74181(m_bus, m_t, a10);
+ m_aluc0 = (alu >> 16) & 1;
+ flags = a10 & (TSELECT | ALUM);
+ m_alu = static_cast<UINT16>(alu);
+#else
+ UINT32 alu;
+ /* compute the ALU function */
+ switch (m_d_aluf) {
+ /**
+ * 00: ALU <- BUS
+ * PROM data for S3-0:1111 M:1 C:0 T:0
+ * 74181 function F=A
+ * T source is BUS
+ */
+ case aluf_bus__alut:
+ alu = m_bus;
+ m_aluc0 = 1;
+ flags = ALUM;
+ LOG((LOG_CPU,2," ALU<- BUS (%#o := %#o)\n", alu, m_bus));
+ break;
+
+ /**
+ * 01: ALU <- T
+ * PROM data for S3-0:1010 M:1 C:0 T:0
+ * 74181 function F=B
+ * T source is BUS
+ */
+ case aluf_treg:
+ alu = m_t;
+ m_aluc0 = 1;
+ flags = ALUM;
+ LOG((LOG_CPU,2," ALU<- T (%#o := %#o)\n", alu, m_t));
+ break;
+
+ /**
+ * 02: ALU <- BUS | T
+ * PROM data for S3-0:1110 M:1 C:0 T:1
+ * 74181 function F=A|B
+ * T source is ALU
+ */
+ case aluf_bus_or_t__alut:
+ alu = m_bus | m_t;
+ m_aluc0 = 1;
+ flags = ALUM | TSELECT;
+ LOG((LOG_CPU,2," ALU<- BUS OR T (%#o := %#o | %#o)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 03: ALU <- BUS & T
+ * PROM data for S3-0:1011 M:1 C:0 T:0
+ * 74181 function F=A&B
+ * T source is BUS
+ */
+ case aluf_bus_and_t:
+ alu = m_bus & m_t;
+ m_aluc0 = 1;
+ flags = ALUM;
+ LOG((LOG_CPU,2," ALU<- BUS AND T (%#o := %#o & %#o)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 04: ALU <- BUS ^ T
+ * PROM data for S3-0:0110 M:1 C:0 T:0
+ * 74181 function F=A^B
+ * T source is BUS
+ */
+ case aluf_bus_xor_t:
+ alu = m_bus ^ m_t;
+ m_aluc0 = 1;
+ flags = ALUM;
+ LOG((LOG_CPU,2," ALU<- BUS XOR T (%#o := %#o ^ %#o)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 05: ALU <- BUS + 1
+ * PROM data for S3-0:0000 M:0 C:0 T:1
+ * 74181 function F=A+1
+ * T source is ALU
+ */
+ case aluf_bus_plus_1__alut:
+ alu = m_bus + 1;
+ m_aluc0 = (alu >> 16) & 1;
+ flags = TSELECT;
+ LOG((LOG_CPU,2," ALU<- BUS + 1 (%#o := %#o + 1)\n", alu, m_bus));
+ break;
+
+ /**
+ * 06: ALU <- BUS - 1
+ * PROM data for S3-0:1111 M:0 C:1 T:1
+ * 74181 function F=A-1
+ * T source is ALU
+ */
+ case aluf_bus_minus_1__alut:
+ alu = m_bus + 0177777;
+ m_aluc0 = (~alu >> 16) & 1;
+ flags = TSELECT;
+ LOG((LOG_CPU,2," ALU<- BUS - 1 (%#o := %#o - 1)\n", alu, m_bus));
+ break;
+
+ /**
+ * 07: ALU <- BUS + T
+ * PROM data for S3-0:1001 M:0 C:1 T:0
+ * 74181 function F=A+B
+ * T source is BUS
+ */
+ case aluf_bus_plus_t:
+ alu = m_bus + m_t;
+ m_aluc0 = (alu >> 16) & 1;
+ flags = 0;
+ LOG((LOG_CPU,2," ALU<- BUS + T (%#o := %#o + %#o)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 10: ALU <- BUS - T
+ * PROM data for S3-0:0110 M:0 C:0 T:0
+ * 74181 function F=A-B
+ * T source is BUS
+ */
+ case aluf_bus_minus_t:
+ alu = m_bus + ~m_t + 1;
+ m_aluc0 = (~alu >> 16) & 1;
+ flags = 0;
+ LOG((LOG_CPU,2," ALU<- BUS - T (%#o := %#o - %#o)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 11: ALU <- BUS - T - 1
+ * PROM data for S3-0:0110 M:0 C:1 T:0
+ * 74181 function F=A-B-1
+ * T source is BUS
+ */
+ case aluf_bus_minus_t_minus_1:
+ alu = m_bus + ~m_t;
+ m_aluc0 = (~alu >> 16) & 1;
+ flags = 0;
+ LOG((LOG_CPU,2," ALU<- BUS - T - 1 (%#o := %#o - %#o - 1)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 12: ALU <- BUS + T + 1
+ * PROM data for S3-0:1001 M:0 C:0 T:1
+ * 74181 function F=A+B+1
+ * T source is ALU
+ */
+ case aluf_bus_plus_t_plus_1__alut:
+ alu = m_bus + m_t + 1;
+ m_aluc0 = (alu >> 16) & 1;
+ flags = TSELECT;
+ LOG((LOG_CPU,2," ALU<- BUS + T + 1 (%#o := %#o + %#o + 1)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 13: ALU <- BUS + SKIP
+ * PROM data for S3-0:0000 M:0 C:SKIP T:1
+ * 74181 function F=A (SKIP=1) or F=A+1 (SKIP=0)
+ * T source is ALU
+ */
+ case aluf_bus_plus_skip__alut:
+ alu = m_bus + m_emu.skip;
+ m_aluc0 = (alu >> 16) & 1;
+ flags = TSELECT;
+ LOG((LOG_CPU,2," ALU<- BUS + SKIP (%#o := %#o + %#o)\n", alu, m_bus, m_emu.skip));
+ break;
+
+ /**
+ * 14: ALU <- BUS,T
+ * PROM data for S3-0:1011 M:1 C:0 T:1
+ * 74181 function F=A&B
+ * T source is ALU
+ */
+ case aluf_bus_and_t__alut:
+ alu = m_bus & m_t;
+ m_aluc0 = 1;
+ flags = ALUM | TSELECT;
+ LOG((LOG_CPU,2," ALU<- BUS,T (%#o := %#o & %#o)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 15: ALU <- BUS & ~T
+ * PROM data for S3-0:0111 M:1 C:0 T:0
+ * 74181 function F=A&~B
+ * T source is BUS
+ */
+ case aluf_bus_and_not_t:
+ alu = m_bus & ~m_t;
+ m_aluc0 = 1;
+ flags = ALUM;
+ LOG((LOG_CPU,2," ALU<- BUS AND NOT T (%#o := %#o & ~%#o)\n", alu, m_bus, m_t));
+ break;
+
+ /**
+ * 16: ALU <- BUS
+ * PROM data for S3-0:1111 M:1 C:0 T:1
+ * 74181 function F=A
+ * T source is ALU
+ */
+ case aluf_undef_16:
+ alu = m_bus;
+ m_aluc0 = 1;
+ flags = ALUM | TSELECT;
+ LOG((LOG_CPU,0," ALU<- 0 (illegal aluf in task %s, mpc:%05o aluf:%02o)\n", task_name(m_task), m_mpc, m_d_aluf));
+ break;
+
+ /**
+ * 17: ALU <- BUS
+ * PROM data for S3-0:1111 M:1 C:0 T:1
+ * 74181 function F=A
+ * T source is ALU
+ */
+ case aluf_undef_17:
+ default:
+ alu = m_bus;
+ m_aluc0 = 1;
+ flags = ALUM | TSELECT;
+ LOG((LOG_CPU,0," ALU<- 0 (illegal aluf in task %s, mpc:%05o aluf:%02o)\n", task_name(m_task), m_mpc, m_d_aluf));
+ }
+ m_alu = static_cast<UINT16>(alu);
+#endif
+
+ // WRTRAM must happen now before L is changed
+ if (m_wrtram_flag)
+ wrtram();
+
+ // shifter passes L, if F1 is not one of L LSH 1, L RSH 1 or L LCY 8
+ m_shifter = m_l;
+
+ // late F1 function call now
+ ((*this).*m_f1[1][m_task][m_d_f1])();
+
+ // late F2 function call now
+ ((*this).*m_f2[1][m_task][m_d_f2])();
+
+ // late BS function call now, if no constant was put on the bus
+ if (do_bs)
+ ((*this).*m_bs[1][m_task][m_d_bs])();
+
+ // update T register, if LOADT is set
+ if (m_d_loadt) {
+ m_cram_addr = m_alu; // latch CRAM address
+ if (flags & TSELECT) {
+ m_t = m_alu; // T source is ALU
+ LOG((LOG_CPU,2, " T<- ALU (%#o)\n", m_alu));
+ } else {
+ m_t = m_bus; // T source is BUS
+ LOG((LOG_CPU,2, " T<- BUS (%#o)\n", m_bus));
+ }
+ }
+
+ // update L register and LALUC0 if LOADL is set
+ if (m_d_loadl) {
+ m_l = m_alu; // load L from ALU
+ if (flags & ALUM) {
+ m_laluc0 = 0; // logic operation - put 0 into latched carry
+ LOG((LOG_CPU,2, " L<- ALU (%#o); LALUC0<- %o\n", m_alu, 0));
+ } else {
+ m_laluc0 = m_aluc0; // arithmethic operation - put ALU carry into latched carry
+ LOG((LOG_CPU,2, " L<- ALU (%#o); LALUC0<- ALUC0 (%o)\n", m_alu, m_aluc0));
+ }
+ // update M (MYL) register, if a RAM related task is active
+ if (m_ram_related[m_task]) {
+ m_m = m_alu; // load M from ALU, if 'GOODTASK'
+ m_s[m_s_reg_bank[m_task]][0] = m_alu; // also writes to S[bank][0], which can't be read
+ LOG((LOG_CPU,2, " M<- ALU (%#o)\n", m_alu));
+ }
+ }
+
+ // handle task switching
+ if (m_task != m_next2_task) {
+ // switch now?
+ if (m_task == m_next_task) {
+ // one more microinstruction
+ m_next_task = m_next2_task;
+ } else {
+ // save this task's next and next2
+ m_task_mpc[m_task] = m_next;
+ m_task_next2[m_task] = m_next2;
+ m_task = m_next_task;
+ LOG((LOG_CPU,1, "task switch to %02o:%s (cycle %lld)\n", m_task, task_name(m_task), cycle()));
+ m_next = m_task_mpc[m_task]; // get new task's mpc
+ m_next2 = m_task_next2[m_task]; // get address modifier after task switch (needed?)
+
+ // let the task know it becomes active now and (most probably) reset the wakeup
+ ((*this).*m_active_callback[m_task])();
+ }
+ }
+
+ /**
+ * Subtract the microcycle time from the display time accu.
+ * If it underflows, call the display state machine and add
+ * the time for 32(!) pixel clocks to the accu.
+ * This is very close to every seventh CPU cycle (really?)
+ */
+ if (m_dsp_time >= 0) {
+ m_dsp_time -= ALTO2_UCYCLE;
+ if (m_dsp_time < 0)
+ display_state_machine();
+ }
+ if (m_unload_time >= 0) {
+ /**
+ * Subtract the microcycle time from the unload time accu.
+ * If it underflows, call the unload word function which adds
+ * the time for 16 or 32 pixel clocks to the accu, or ends
+ * the unloading by leaving m_unload_time at -1.
+ */
+ m_unload_time -= ALTO2_UCYCLE;
+ if (m_unload_time < 0)
+ unload_word();
+ }
+#if (USE_BITCLK_TIMER == 0)
+ if (m_bitclk_time >= 0) {
+ /*
+ * Subtract the microcycle time from the bitclk time accu.
+ * If it underflows, call the disk bitclk function which adds
+ * the time for one bit as clocks to the accu, or ends
+ * the bitclk sequence by leaving m_bitclk_time at -1.
+ */
+ m_bitclk_time -= ALTO2_UCYCLE;
+ disk_bitclk(0, m_bitclk_index);
+ }
+#endif
+ } while (m_icount-- > 0);
+
+ /* save this task's mpc and address modifier */
+ m_task_mpc[m_task] = m_next;
+ m_task_next2[m_task] = m_next2;
+}
+
+/** @brief reset the various registers */
+void alto2_cpu_device::hard_reset()
+{
+ /* all tasks start in ROM0 */
+ m_reset_mode = 0xffff;
+
+ memset(&m_ram_related, 0, sizeof(m_ram_related));
+
+ // install standard handlers in all tasks
+ for (int task = 0; task < ALTO2_TASKS; task++) {
+ // every task starts at mpc = task number, in either ROM0 or RAM0
+ m_task_mpc[task] = (m_ctl2k_u38[task] >> 4) ^ 017;
+ m_active_callback[task] = &alto2_cpu_device::noop;
+ if (0 == (m_reset_mode & (1 << task)))
+ m_task_mpc[task] |= ALTO2_UCODE_RAM_BASE;
+
+ set_bs(task, bs_read_r, &alto2_cpu_device::bs_early_read_r, 0);
+ set_bs(task, bs_load_r, &alto2_cpu_device::bs_early_load_r, &alto2_cpu_device::bs_late_load_r);
+ set_bs(task, bs_no_source, 0, 0);
+ set_bs(task, bs_task_3, &alto2_cpu_device::fn_bs_bad_0, &alto2_cpu_device::fn_bs_bad_1); // task specific
+ set_bs(task, bs_task_4, &alto2_cpu_device::fn_bs_bad_0, &alto2_cpu_device::fn_bs_bad_1); // task specific
+ set_bs(task, bs_read_md, &alto2_cpu_device::bs_early_read_md, 0);
+ set_bs(task, bs_mouse, &alto2_cpu_device::bs_early_mouse, 0);
+ set_bs(task, bs_disp, &alto2_cpu_device::bs_early_disp, 0);
+
+ set_f1(task, f1_nop, 0, 0);
+ set_f1(task, f1_load_mar, 0, &alto2_cpu_device::f1_late_load_mar);
+ set_f1(task, f1_task, &alto2_cpu_device::f1_early_task, 0);
+ set_f1(task, f1_block, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // not all tasks have the f1_block
+ set_f1(task, f1_l_lsh_1, 0, &alto2_cpu_device::f1_late_l_lsh_1);
+ set_f1(task, f1_l_rsh_1, 0, &alto2_cpu_device::f1_late_l_rsh_1);
+ set_f1(task, f1_l_lcy_8, 0, &alto2_cpu_device::f1_late_l_lcy_8);
+ set_f1(task, f1_const, 0, 0);
+ set_f1(task, f1_task_10, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+ set_f1(task, f1_task_11, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+ set_f1(task, f1_task_12, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+ set_f1(task, f1_task_13, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+ set_f1(task, f1_task_14, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+ set_f1(task, f1_task_15, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+ set_f1(task, f1_task_16, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+ set_f1(task, f1_task_17, &alto2_cpu_device::fn_f1_bad_0, &alto2_cpu_device::fn_f1_bad_1); // f1_task_10 to f1_task_17 are task specific
+
+ set_f2(task, f2_nop, 0, 0);
+ set_f2(task, f2_bus_eq_zero, 0, &alto2_cpu_device::f2_late_bus_eq_zero);
+ set_f2(task, f2_shifter_lt_zero,0, &alto2_cpu_device::f2_late_shifter_lt_zero);
+ set_f2(task, f2_shifter_eq_zero,0, &alto2_cpu_device::f2_late_shifter_eq_zero);
+ set_f2(task, f2_bus, 0, &alto2_cpu_device::f2_late_bus);
+ set_f2(task, f2_alucy, 0, &alto2_cpu_device::f2_late_alucy);
+ set_f2(task, f2_load_md, 0, &alto2_cpu_device::f2_late_load_md);
+ set_f2(task, f2_const, 0, 0);
+ set_f2(task, f2_task_10, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ set_f2(task, f2_task_11, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ set_f2(task, f2_task_12, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ set_f2(task, f2_task_13, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ set_f2(task, f2_task_14, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ set_f2(task, f2_task_15, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ set_f2(task, f2_task_16, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ set_f2(task, f2_task_17, &alto2_cpu_device::fn_f2_bad_0, &alto2_cpu_device::fn_f2_bad_1); // f2_task_10 to f2_task_17 are task specific
+ }
+
+ init_memory();
+ init_disk();
+ init_disp();
+ init_kbd();
+ init_mouse();
+ init_hw();
+
+ init_emu();
+ init_ksec();
+ init_ether();
+ init_mrt();
+ init_dwt();
+ init_curt();
+ init_dht();
+ init_dvt();
+ init_part();
+ init_kwd();
+
+ m_dsp_time = 0; // reset the display state timing
+ m_task = task_emu; // start with task 0 (emulator)
+ m_task_wakeup |= 1 << task_emu; // set wakeup flag
+}
+
+/** @brief software initiated reset (STARTF) */
+void alto2_cpu_device::soft_reset()
+{
+ for (int task = 0; task < ALTO2_TASKS; task++) {
+ // every task starts at mpc = task number, in either ROM0 or RAM0
+ m_task_mpc[task] = (m_ctl2k_u38[task] >> 4) ^ 017;
+ if (0 == (m_reset_mode & (1 << task)))
+ m_task_mpc[task] |= ALTO2_UCODE_RAM_BASE;
+ }
+ m_next2_task = task_emu; // switch to task 0 (emulator)
+ m_reset_mode = 0xffff; // all tasks start in ROM0 again
+ m_task = task_emu; // set current task to emulator
+ m_task_wakeup = 1 << task_emu; // set only the emulator task wakeup flag
+
+ m_dsp_time = 0; // reset the display state machine timing accu
+ m_unload_time = 0; // reset the word unload timing accu
+#if (USE_BITCLK_TIMER == 0)
+ m_bitclk_time = 0; // reset the bitclk timing accu
+#endif
+}
diff --git a/src/emu/cpu/alto2/alto2cpu.h b/src/emu/cpu/alto2/alto2cpu.h
new file mode 100644
index 00000000000..50d206695d6
--- /dev/null
+++ b/src/emu/cpu/alto2/alto2cpu.h
@@ -0,0 +1,948 @@
+/*****************************************************************************
+ *
+ * Xerox AltoII CPU core interface
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ *
+ *****************************************************************************/
+#ifndef _CPU_ALTO2_H_
+#define _CPU_ALTO2_H
+
+#define ALTO2_TAG "alto2"
+
+#include "emu.h"
+#include "debugger.h"
+#include "machine/diablo_hd.h"
+
+/**
+ * \brief AltoII register names
+ */
+enum {
+ // micro code task, micro program counter, next and next2
+ A2_TASK, A2_MPC, A2_NEXT, A2_NEXT2,
+ // BUS, ALU, temp, latch, memory latch and carry flags
+ A2_BUS, A2_T, A2_ALU, A2_ALUC0, A2_L, A2_SHIFTER, A2_LALUC0, A2_M,
+ A2_R, // 32 R registers
+ A2_AC3 = A2_R, A2_AC2, A2_AC1, A2_AC0, A2_R04, A2_R05, A2_PC, A2_R07,
+ A2_R10, A2_R11, A2_R12, A2_R13, A2_R14, A2_R15, A2_R16, A2_R17,
+ A2_R20, A2_R21, A2_R22, A2_R23, A2_R24, A2_R25, A2_R26, A2_R27,
+ A2_R30, A2_R31, A2_R32, A2_R33, A2_R34, A2_R35, A2_R36, A2_R37,
+ A2_S, // 32 S registers
+ A2_S00 = A2_S, A2_S01, A2_S02, A2_S03, A2_S04, A2_S05, A2_S06, A2_S07,
+ A2_S10, A2_S11, A2_S12, A2_S13, A2_S14, A2_S15, A2_S16, A2_S17,
+ A2_S20, A2_S21, A2_S22, A2_S23, A2_S24, A2_S25, A2_S26, A2_S27,
+ A2_S30, A2_S31, A2_S32, A2_S33, A2_S34, A2_S35, A2_S36, A2_S37,
+ // DISK controller registers
+ A2_DRIVE, A2_KADDR, A2_KADR, A2_KSTAT, A2_KCOM, A2_KRECNO,
+ A2_SHIFTIN, A2_SHIFTOUT, A2_DATAIN, A2_DATAOUT, A2_KRWC,
+ A2_KFER, A2_WDTSKENA, A2_WDINIT0, A2_WDINIT, A2_STROBE,
+ A2_BITCLK, A2_DATIN, A2_BITCNT, A2_CARRY, A2_SECLATE,
+ A2_SEEKOK, A2_OKTORUN, A2_READY
+};
+
+#ifndef ALTO2_DEBUG
+#define ALTO2_DEBUG 1 //!< define to 1 to enable logerror() output
+#endif
+
+#ifndef ALTO2_CRAM_CONFIG
+#define ALTO2_CRAM_CONFIG 2 //!< use default CROM/CRAM configuration 2
+#endif
+
+#define ALTO2_FAKE_STATUS_H 12 //!< number of extra scanlines to display some status info
+
+#define USE_PRIO_F9318 0 //!< define to 1 to use the F9318 priority encoder code
+#define USE_ALU_74181 1 //!< define to 1 to use the SN74181 ALU code
+#define USE_BITCLK_TIMER 0 //!< define to 1 to use a very high rate timer for the disk bit clock
+#define USE_HAMMING_CHECK 1 //!< define to 1 to use the Hamming code and Parity check in a2mem
+
+#define ALTO2_TASKS 16 //!< 16 task slots
+#define ALTO2_REGS 32 //!< 32 16-bit words in the R register file
+#define ALTO2_ALUF 16 //!< 16 ALU functions (74181)
+#define ALTO2_BUSSRC 8 //!< 8 bus sources
+#define ALTO2_F1MAX 16 //!< 16 F1 functions
+#define ALTO2_F2MAX 16 //!< 16 F2 functions
+#define ALTO2_UCYCLE 169542 //!< time in pico seconds for a CPU micro cycle: 29.4912MHz/5 -> 5.898240Hz ~= 169.542ns/clock
+
+#define ALTO2_CONST_SIZE 256 //!< number words in the constant ROM
+
+//! inverted bits in the micro instruction 32 bit word
+#define ALTO2_UCODE_INVERTED ((1 << 10) | (1 << 15) | (1 << 19))
+
+/********************************************************************************
+ * Bit field primitives
+ * These are some macros to make it easier to access variable by the bit-
+ * reversed notation that the Xerox Alto documents use all over the place.
+ * Bit number 0 is the most significant there,
+ * and bit number (width - 1) is the least significant.
+ * The X_ is for Xerox and to avoid collisions with MAME generic macros.
+ ********************************************************************************/
+
+//! get the left shift required to access bit %to in a word of %width bits
+#define X_BITSHIFT(width,to) ((width) - 1 - (to))
+
+//! build a least significant bit mask for bits %from to %to (inclusive)
+#define X_BITMASK(from,to) ((1ul << ((to) + 1 - (from))) - 1)
+
+//! get a single bit number %bit value from %reg, a word of %width bits
+#define X_BIT(reg,width,bit) (((reg) >> X_BITSHIFT(width,bit)) & 1)
+
+//! get a bit field from %reg, a word of %width bits, starting at bit %from until bit %to
+#define X_RDBITS(reg,width,from,to) (((reg) >> X_BITSHIFT(width,to)) & X_BITMASK(from,to))
+
+//! put a value %val into %reg, a word of %width bits, starting at bit %from until bit %to
+#define X_WRBITS(reg,width,from,to,val) do { \
+ UINT32 mask = X_BITMASK(from,to) << X_BITSHIFT(width,to); \
+ reg = ((reg) & ~mask) | (((val) << X_BITSHIFT(width,to)) & mask); \
+} while (0)
+
+#if ALTO2_DEBUG
+ enum LOG_TYPE_ENUM {
+ LOG_0,
+ LOG_CPU = (1 << 0),
+ LOG_EMU = (1 << 1),
+ LOG_T01 = (1 << 2),
+ LOG_T02 = (1 << 3),
+ LOG_T03 = (1 << 4),
+ LOG_KSEC = (1 << 5),
+ LOG_T05 = (1 << 6),
+ LOG_T06 = (1 << 7),
+ LOG_ETH = (1 << 8),
+ LOG_MRT = (1 << 9),
+ LOG_DWT = (1 << 10),
+ LOG_CURT = (1 << 11),
+ LOG_DHT = (1 << 12),
+ LOG_DVT = (1 << 13),
+ LOG_PART = (1 << 14),
+ LOG_KWD = (1 << 15),
+ LOG_T17 = (1 << 16),
+ LOG_MEM = (1 << 17),
+ LOG_RAM = (1 << 18),
+ LOG_DRIVE = (1 << 19),
+ LOG_DISK = (1 << 20),
+ LOG_DISPL = (1 << 21),
+ LOG_MOUSE = (1 << 22),
+ LOG_HW = (1 << 23),
+ LOG_KBD = (1 << 24),
+ LOG_ALL = ((1 << 25) - 1)
+ };
+ extern int m_log_types;
+ extern int m_log_level;
+ extern bool m_log_newline;
+ void logprintf(int type, int level, const char* format, ...);
+# define LOG(x) logprintf x
+#else
+# define LOG(x)
+#endif
+
+//*******************************************
+// define constants from the sub-devices
+//*******************************************
+#define ALTO2_DEFINE_CONSTANTS 1
+#include "a2jkff.h"
+#include "a2ram.h"
+#include "a2hw.h"
+#include "a2kbd.h"
+#include "a2mouse.h"
+#include "a2disk.h"
+#include "a2disp.h"
+#include "a2mem.h"
+#include "a2emu.h"
+#include "a2ksec.h"
+#include "a2ether.h"
+#include "a2mrt.h"
+#include "a2dwt.h"
+#include "a2curt.h"
+#include "a2dht.h"
+#include "a2dvt.h"
+#include "a2part.h"
+#include "a2dwt.h"
+#include "a2kwd.h"
+#undef ALTO2_DEFINE_CONSTANTS
+
+class alto2_cpu_device : public cpu_device
+{
+public:
+ // construction/destruction
+ alto2_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~alto2_cpu_device();
+
+ //! driver interface to set diablo_hd_device
+ void set_diablo(int unit, diablo_hd_device* ptr);
+
+ //! call in for the next sector callback
+ void next_sector(int unit);
+
+ //! update the screen bitmap
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+ //! screen VBLANK handler
+ void screen_eof(screen_device &screen, bool state);
+
+ DECLARE_ADDRESS_MAP( ucode_map, 32 );
+ DECLARE_ADDRESS_MAP( const_map, 16 );
+ DECLARE_ADDRESS_MAP( iomem_map, 16 );
+
+ //! register a mouse motion in x direction
+ DECLARE_INPUT_CHANGED_MEMBER( mouse_motion_x );
+ //! register a mouse motion in y direction
+ DECLARE_INPUT_CHANGED_MEMBER( mouse_motion_y );
+ //! register a mouse button change
+ DECLARE_INPUT_CHANGED_MEMBER( mouse_button_0 );
+ DECLARE_INPUT_CHANGED_MEMBER( mouse_button_1 );
+ DECLARE_INPUT_CHANGED_MEMBER( mouse_button_2 );
+
+protected:
+ //! device-level override for start
+ virtual void device_start();
+ //! device-level override for reset
+ virtual void device_reset();
+
+ //! device-level override for post reset
+ void interface_post_reset();
+
+ //! device_execute_interface overrides
+ virtual UINT32 execute_min_cycles() const { return 1; }
+ virtual UINT32 execute_max_cycles() const { return 1; }
+ virtual UINT32 execute_input_lines() const { return 1; }
+ virtual void execute_run();
+ virtual void execute_set_input(int inputnum, int state);
+
+ //! device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ //! device (P)ROMs
+ virtual const rom_entry *device_rom_region() const;
+ //! device_state_interface overrides
+ void state_string_export(const device_state_entry &entry, astring &string);
+
+ //! device_disasm_interface overrides
+ virtual UINT32 disasm_min_opcode_bytes() const { return 4; }
+ virtual UINT32 disasm_max_opcode_bytes() const { return 4; }
+ virtual offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options);
+
+private:
+
+ void fatal(int level, const char *format, ...);
+
+ address_space_config m_ucode_config;
+ address_space_config m_const_config;
+ address_space_config m_iomem_config;
+
+ address_space* m_iomem;
+
+ UINT8* m_ucode_crom;
+ UINT8* m_ucode_cram;
+ UINT8* m_const_data;
+
+ //! read microcode CROM
+ DECLARE_READ32_MEMBER ( crom_r );
+
+ //! read microcode CRAM
+ DECLARE_READ32_MEMBER ( cram_r );
+
+ //! write microcode CRAM
+ DECLARE_WRITE32_MEMBER( cram_w );
+
+ //! read constants PROM
+ DECLARE_READ16_MEMBER ( const_r );
+
+ //! read i/o space RAM
+ DECLARE_READ16_MEMBER ( ioram_r );
+
+ //!< write i/o space RAM
+ DECLARE_WRITE16_MEMBER( ioram_w );
+
+ //!< read memory mapped i/o
+ DECLARE_READ16_MEMBER ( mmio_r );
+
+ //!< write memory mapped i/o
+ DECLARE_WRITE16_MEMBER( mmio_w );
+
+ int m_icount;
+
+ typedef void (alto2_cpu_device::*a2func)();
+
+ //! task numbers
+ enum {
+ task_emu, //!< emulator task
+ task_1, //!< unused
+ task_2, //!< unused
+ task_3, //!< unused
+ task_ksec, //!< disk sector task
+ task_5, //!< unused
+ task_6, //!< unused
+ task_ether, //!< ethernet task
+ task_mrt, //!< memory refresh task
+ task_dwt, //!< display word task
+ task_curt, //!< cursor task
+ task_dht, //!< display horizontal task
+ task_dvt, //!< display vertical task
+ task_part, //!< parity task
+ task_kwd, //!< disk word task
+ task_17 //!< unused task slot 017
+ };
+
+ //! register select values accessing R (Note: register numbers are octal)
+ enum {
+ rsel_ac3, //!< AC3 used by emulator as accu 3. Also used by Mesa emulator to keep bytecode to execute after breakpoint
+ rsel_ac2, //!< AC2 used by emulator as accu 2. Also used by Mesa emulator as x register for xfer
+ rsel_ac1, //!< AC1 used by emulator as accu 1. Also used by Mesa emulator as r-temporary for return indices and values
+ rsel_ac0, //!< AC0 used by emulator as accu 0. Also used by Mesa emulator as new field bits for WF and friends
+ rsel_r04, //!< NWW state of the interrupt system
+ rsel_r05, //!< SAD. Also used by Mesa emulator as scratch R-register for counting
+ rsel_pc, //!< PC used by emulator as program counter
+ rsel_r07, //!< XREG. Also used by Mesa emulator as task hole, i.e. pigeonhole for saving things across tasks.
+ rsel_r10, //!< XH. Also used by Mesa emulator as instruction byte register
+ rsel_r11, //!< CLOCKTEMP - used in the MRT
+ rsel_r12, //!< ECNTR remaining words in buffer - ETHERNET
+ rsel_r13, //!< EPNTR points BEFORE next word in buffer - ETHERNET
+ rsel_r14,
+ rsel_r15, //!< MPC. Used by the Mesa emulator as program counter
+ rsel_r16, //!< STKP. Used by the Mesa emulator as stack pointer [0-10] 0 empty, 10 full
+ rsel_r17, //!< XTSreg. Used by the Mesa emulator to xfer trap state
+ rsel_r20, //!< CURX. Holds cursor X; used by the cursor task
+ rsel_r21, //!< CURDATA. Holds the cursor data; used by the cursor task
+ rsel_r22, //!< CBA. Holds the address of the currently active DCB+1
+ rsel_r23, //!< AECL. Holds the address of the end of the current scanline's bitmap
+ rsel_r24, //!< SLC. Holds the number of scanlines remaining in currently active DCB
+ rsel_r25, //!< MTEMP. Holds the temporary cell
+ rsel_r26, //!< HTAB. Holds the number of tab words remaining on current scanline
+ rsel_r27, //!< YPOS
+ rsel_r30, //!< DWA. Holds the address of the bit map doubleword currently being fetched for transmission to the hardware buffer.
+ rsel_r31, //!< KWDCT. Used by the disk tasks as word counter
+ rsel_r32, //!< CKSUMR. Used by the disk tasks as checksum register (and *amble counter?)
+ rsel_r33, //!< KNMAR. Used by the disk tasks as transfer memory address register
+ rsel_r34, //!< DCBR. Used by the disk tasks to keep the current device control block
+ rsel_r35, //!< TEMP. Used by the Mesa emulator, and also by BITBLT
+ rsel_r36, //!< TEMP2. Used by the Mesa emulator, and also by BITBLT
+ rsel_r37 //!< CLOCKREG. Low order bits of the real time clock
+ };
+
+ //! ALU function numbers
+ enum {
+ /**
+ * \brief 00: ALU <- BUS
+ * PROM data for S3-0,M,C,T: 1111/1/0/0
+ * function F=A
+ * T source is ALU
+ */
+ aluf_bus__alut,
+ /**
+ * \brief 01: ALU <- T
+ * PROM data for S3-0,M,C,T: 1010/1/0/0
+ * function F=B
+ * T source is BUS
+ */
+ aluf_treg,
+ /**
+ * \brief 02: ALU <- BUS | T
+ * PROM data for S3-0,M,C,T: 1110/1/0/1
+ * function F=A|B
+ * T source is ALU
+ */
+ aluf_bus_or_t__alut,
+ /**
+ * \brief 03: ALU <- BUS & T
+ * PROM data for S3-0,M,C,T: 1011/1/0/0
+ * function F=A&B
+ * T source is BUS
+ */
+ aluf_bus_and_t,
+ /**
+ * \brief 04: ALU <- BUS ^ T
+ * PROM data for S3-0,M,C,T: 0110/1/0/0
+ * function F=A^B
+ * T source is BUS
+ */
+ aluf_bus_xor_t,
+ /**
+ * \brief 05: ALU <- BUS + 1
+ * PROM data for S3-0,M,C,T: 0000/0/0/1
+ * function F=A+1
+ * T source is ALU
+ */
+ aluf_bus_plus_1__alut,
+ /**
+ * \brief 06: ALU <- BUS - 1
+ * PROM data for S3-0,M,C,T: 1111/0/1/1
+ * function F=A-1
+ * T source is ALU
+ */
+ aluf_bus_minus_1__alut,
+ /**
+ * \brief 07: ALU <- BUS + T
+ * PROM data for S3-0,M,C,T: 1001/0/1/0
+ * function F=A+B
+ * T source is BUS
+ */
+ aluf_bus_plus_t,
+ /**
+ * \brief 10: ALU <- BUS - T
+ * PROM data for S3-0,M,C,T: 0110/0/0/0
+ * function F=A-B
+ * T source is BUS
+ */
+ aluf_bus_minus_t,
+ /**
+ * \brief 11: ALU <- BUS - T - 1
+ * PROM data for S3-0,M,C,T: 0110/0/1/0
+ * function F=A-B-1
+ * T source is BUS
+ */
+ aluf_bus_minus_t_minus_1,
+ /**
+ * \brief 12: ALU <- BUS + T + 1
+ * PROM data for S3-0,M,C: 1001/0/0
+ * function F=A+B+1
+ * T source is ALU
+ */
+ aluf_bus_plus_t_plus_1__alut,
+ /**
+ * \brief 13: ALU <- BUS + SKIP
+ * PROM data for S3-0,M,C,T: 0000/0/SKIP/1
+ * function F=A (SKIP=1) or F=A+1 (SKIP=0)
+ * T source is ALU
+ */
+ aluf_bus_plus_skip__alut,
+ /**
+ * \brief 14: ALU <- BUS & T
+ * PROM data for S3-0,M,C,T: 1011/1/0/1
+ * function F=A&B
+ * T source is ALU
+ */
+ aluf_bus_and_t__alut,
+ /**
+ * \brief 15: ALU <- BUS & ~T
+ * PROM data for S3-0,M,C,T: 0111/1/0/0
+ * function F=A&~B
+ * T source is BUS
+ */
+ aluf_bus_and_not_t,
+ /**
+ * \brief 16: ALU <- BUS
+ * PROM data for S3-0,M,C,T: 1111/1/0/1
+ * function F=A
+ * T source is ALU
+ */
+ aluf_undef_16,
+ /**
+ * \brief 17: ALU <- BUS
+ * PROM data for S3-0,M,C,T: 1111/1/0/1
+ * function F=A
+ * T source is ALU
+ */
+ aluf_undef_17
+ };
+
+ //! BUS source selection numbers
+ enum {
+ bs_read_r, //!< BUS source is R register
+ bs_load_r, //!< load R register from BUS
+ bs_no_source, //!< BUS is open (0177777)
+ bs_task_3, //!< BUS source is task specific
+ bs_task_4, //!< BUS source is task specific
+ bs_read_md, //!< BUS source is memory data
+ bs_mouse, //!< BUS source is mouse data
+ bs_disp //!< BUS source displacement (emulator task)
+ };
+
+ //! Function 1 numbers
+ enum {
+ f1_nop, //!< f1 00 no operation
+ f1_load_mar, //!< f1 01 load memory address register
+ f1_task, //!< f1 02 task switch
+ f1_block, //!< f1 03 task block
+ f1_l_lsh_1, //!< f1 04 left shift L once
+ f1_l_rsh_1, //!< f1 05 right shift L once
+ f1_l_lcy_8, //!< f1 06 cycle L 8 times
+ f1_const, //!< f1 07 constant from PROM
+
+ f1_task_10, //!< f1 10 task specific
+ f1_task_11, //!< f1 11 task specific
+ f1_task_12, //!< f1 12 task specific
+ f1_task_13, //!< f1 13 task specific
+ f1_task_14, //!< f1 14 task specific
+ f1_task_15, //!< f1 15 task specific
+ f1_task_16, //!< f1 16 task specific
+ f1_task_17 //!< f1 17 task specific
+ };
+
+ //! Function 2 numbers
+ enum {
+ f2_nop, //!< f2 00 no operation
+ f2_bus_eq_zero, //!< f2 01 branch on bus equals 0
+ f2_shifter_lt_zero, //!< f2 02 branch on shifter less than 0
+ f2_shifter_eq_zero, //!< f2 03 branch on shifter equals 0
+ f2_bus, //!< f2 04 branch on BUS[6-15]
+ f2_alucy, //!< f2 05 branch on (latched) ALU carry
+ f2_load_md, //!< f2 06 load memory data
+ f2_const, //!< f2 07 constant from PROM
+
+ f2_task_10, //!< f2 10 task specific
+ f2_task_11, //!< f2 11 task specific
+ f2_task_12, //!< f2 12 task specific
+ f2_task_13, //!< f2 13 task specific
+ f2_task_14, //!< f2 14 task specific
+ f2_task_15, //!< f2 15 task specific
+ f2_task_16, //!< f2 16 task specific
+ f2_task_17 //!< f2 17 task specific
+ };
+
+ //! enumeration of the micro code word bits
+ //! Note: The Alto documents enumerate bits from left (MSB = 0) to right (LSB = 31)
+ enum {
+ DRSEL0, DRSEL1, DRSEL2, DRSEL3, DRSEL4,
+ DALUF0, DALUF1, DALUF2, DALUF3,
+ DBS0, DBS1, DBS2,
+ DF1_0, DF1_1, DF1_2, DF1_3,
+ DF2_0, DF2_1, DF2_2, DF2_3,
+ DLOADT,
+ DLOADL,
+ NEXT0, NEXT1, NEXT2, NEXT3, NEXT4, NEXT5, NEXT6, NEXT7, NEXT8, NEXT9
+ };
+
+ //! get the normally accessed bank number from a bank register
+ static inline UINT16 GET_BANK_NORMAL(UINT16 breg) { return X_RDBITS(breg,16,12,13); }
+
+ //! get the extended bank number (accessed via XMAR) from a bank register
+ static inline UINT16 GET_BANK_EXTENDED(UINT16 breg) { return X_RDBITS(breg,16,14,15); }
+
+ //! get an ignored bit field from a control RAM address
+ static inline UINT16 GET_CRAM_IGNORE(UINT16 addr) { return X_RDBITS(addr,16,0,1); }
+
+ //! get the bank select bit field from a control RAM address
+ static inline UINT16 GET_CRAM_BANKSEL(UINT16 addr) { return X_RDBITS(addr,16,2,3); }
+
+ //! get the ROM/RAM flag from a control RAM address
+ static inline UINT16 GET_CRAM_RAMROM(UINT16 addr) { return X_RDBITS(addr,16,4,4); }
+
+ //! get the half select flag from a control RAM address
+ static inline UINT16 GET_CRAM_HALFSEL(UINT16 addr) { return X_RDBITS(addr,16,5,5); }
+
+ //! get the word address bit field from a control RAM address
+ static inline UINT16 GET_CRAM_WORDADDR(UINT16 addr) { return X_RDBITS(addr,16,6,15); }
+
+ UINT16 m_task_mpc[ALTO2_TASKS]; //!< per task micro program counter
+ UINT16 m_task_next2[ALTO2_TASKS]; //!< per task address modifier
+ UINT8 m_task; //!< active task
+ UINT8 m_next_task; //!< next micro instruction's task
+ UINT8 m_next2_task; //!< next but one micro instruction's task
+ UINT16 m_mpc; //!< micro program counter
+ UINT32 m_mir; //!< micro instruction register
+
+ /**
+ * \brief current micro instruction's register selection
+ * The emulator F2s ACSOURCE and ACDEST modify this.
+ * Note: The S registers are addressed by the original RSEL[0-4],
+ * even when the the emulator modifies this.
+ */
+ UINT8 m_rsel;
+ UINT8 m_d_rsel; //!< decoded RSEL[0-4]
+ UINT8 m_d_aluf; //!< decoded ALUF[0-3] function
+ UINT8 m_d_bs; //!< decoded BS[0-2] bus source
+ UINT8 m_d_f1; //!< decoded F1[0-3] function
+ UINT8 m_d_f2; //!< decoded F2[0-3] function
+ UINT8 m_d_loadt; //!< decoded LOADT flag
+ UINT8 m_d_loadl; //!< decoded LOADL flag
+ UINT16 m_next; //!< current micro instruction's next
+ UINT16 m_next2; //!< next micro instruction's next
+ UINT16 m_r[ALTO2_REGS]; //!< R register file
+ UINT16 m_s[ALTO2_SREG_BANKS][ALTO2_REGS]; //!< S register file(s)
+ UINT16 m_bus; //!< wired-AND bus
+ UINT16 m_t; //!< T register
+ UINT16 m_alu; //!< the current ALU
+ UINT16 m_aluc0; //!< the current ALU carry output
+ UINT16 m_l; //!< L register
+ UINT16 m_shifter; //!< shifter output
+ UINT16 m_laluc0; //!< the latched ALU carry output
+ UINT16 m_m; //!< M register of RAM related tasks (MYL latch in the schematics)
+ UINT16 m_cram_addr; //!< constant RAM address
+ UINT16 m_task_wakeup; //!< task wakeup: bit 1<<n set if task n requesting service
+ a2func m_active_callback[ALTO2_TASKS]; //!< task activation callbacks
+
+ UINT16 m_reset_mode; //!< reset mode register: bit 1<<n set if task n starts in ROM
+ bool m_rdram_flag; //!< set by rdram, action happens on next cycle
+ bool m_wrtram_flag; //!< set by wrtram, action happens on next cycle
+
+ UINT8 m_s_reg_bank[ALTO2_TASKS]; //!< active S register bank per task
+ UINT8 m_bank_reg[ALTO2_TASKS]; //!< normal and extended RAM banks per task
+ bool m_ether_enable; //!< set to true, if the ethernet should be simulated
+ bool m_ewfct; //!< set by Ether task when it want's a wakeup at switch to task_mrt
+ int m_dsp_time; //!< display_state_machine() time accu
+ int m_unload_time; //!< unload word time accu
+ int m_unload_word; //!< unload word number
+#if (USE_BITCLK_TIMER == 0)
+ int m_bitclk_time; //!< bitclk call time accu
+ int m_bitclk_index; //!< bitclk index (bit number)
+#endif
+
+ static const char *task_name(int task); //!< human readable task names
+ static const char *r_name(UINT8 reg); //!< human readable register names
+ static const char *aluf_name(UINT8 aluf); //!< human readable ALU function names
+ static const char *bs_name(UINT8 bs); //!< human readable bus source names
+ static const char *f1_name(UINT8 f1); //!< human readable F1 function names
+ static const char *f2_name(UINT8 f2); //!< human readable F2 function names
+
+ /**
+ * @brief 2KCTL PROM u3 - 256x4
+ * <PRE>
+ * PROM u3 is 256x4 type 3601-1, looks like SN74387, and it
+ * controls NEXT[6-9]', i.e. the outputs are wire-AND to NEXT
+ *
+ * SN74387
+ * +---+-+---+
+ * | +-+ |
+ * A6 -|1 16|- Vcc
+ * | |
+ * A5 -|2 15|- A7
+ * | |
+ * A4 -|3 14|- FE1'
+ * | |
+ * A3 -|4 13|- FE2'
+ * | |
+ * A0 -|5 12|- D0
+ * | |
+ * A1 -|6 11|- D1
+ * | |
+ * A2 -|7 10|- D2
+ * | |
+ * GND -|8 9|- D3
+ * | |
+ * +---------+
+ *
+ *
+ * It is enabled whenever the Emulator task is active and:
+ * both F2[0] and F2[1] are 1 F2 functions 014, 015, 016, 017
+ * F2=14 is 0 not for F2 = 14 (load IR<-)
+ * IR[0] is 0 not for arithmetic group
+ *
+ * This means it controls the F2 functions 015:IDISP<- and 016:<-ACSOURCE
+ *
+ * Its address lines are:
+ * line pin connected to load swap
+ * -------------------------------------------------------------------
+ * A0 5 F2[2] (i.e. MIR[18]) IR[07]
+ * A1 6 IR[01] IR[06]
+ * A2 7 IR[02] IR[05]
+ * A3 4 IR[03] IR[04]
+ * A4 3 IR[04] IR[03]
+ * A5 2 IR[05] IR[02]
+ * A6 1 IR[06] IR[01]
+ * A7 15 IR[07] F2[2]
+ *
+ * Its data lines are:
+ * line pin connected to load
+ * -------------------------------------------------------------------
+ * D3 9 NEXT[06]' NEXT[06]
+ * D2 10 NEXT[07]' NEXT[07]
+ * D1 11 NEXT[08]' NEXT[08]
+ * D0 12 NEXT[09]' NEXT[09]
+ *
+ * Its address lines are reversed at load time to make it easier to
+ * access it. Also both, address and data lines, are inverted.
+ * </PRE>
+ */
+ UINT8* m_ctl2k_u3;
+
+ /**
+ * @brief 2KCTL PROM u38; 82S23; 32x8 bit
+ * <PRE>
+ *
+ * 82S23
+ * +---+-+---+
+ * | +-+ |
+ * B0 -|1 16|- Vcc
+ * | |
+ * B1 -|2 15|- EN'
+ * | |
+ * B2 -|3 14|- A4
+ * | |
+ * B3 -|4 13|- A3
+ * | |
+ * B4 -|5 12|- A2
+ * | |
+ * B5 -|6 11|- A1
+ * | |
+ * B6 -|7 10|- A0
+ * | |
+ * GND -|8 9|- B7
+ * | |
+ * +---------+
+ *
+ * Task priority encoder
+ *
+ * line pin signal
+ * -------------------------------
+ * A0 10 CT1 (current task LSB)
+ * A1 11 CT2
+ * A2 12 CT4
+ * A3 13 CT8 (current task MSB)
+ * A4 14 0 (GND)
+ *
+ * line pin signal
+ * -------------------------------
+ * B0 1 RDCT8'
+ * B1 2 RDCT4'
+ * B2 3 RDCT2'
+ * B3 4 RDCT1'
+ * B4 5 NEXT[09]'
+ * B5 6 NEXT[08]'
+ * B6 7 NEXT[07]'
+ * B7 9 NEXT[06]'
+ * </PRE>
+ */
+ UINT8* m_ctl2k_u38;
+
+ //! output lines of the 2KCTL U38 PROM
+ enum {
+ U38_RDCT8,
+ U38_RDCT4,
+ U38_RDCT2,
+ U38_RDCT1,
+ U38_NEXT09,
+ U38_NEXT08,
+ U38_NEXT07,
+ U38_NEXT06
+ };
+
+ /**
+ * @brief 2KCTL PROM u76; P3601-1; 256x4; PC0I and PC1I decoding
+ * <PRE>
+ * Replacement for u51, which is used in 1KCTL
+ *
+ * SN74387
+ * +---+-+---+
+ * | +-+ |
+ * A6 -|1 16|- Vcc
+ * | |
+ * A5 -|2 15|- A7
+ * | |
+ * A4 -|3 14|- FE1'
+ * | |
+ * A3 -|4 13|- FE2'
+ * | |
+ * A0 -|5 12|- D0
+ * | |
+ * A1 -|6 11|- D1
+ * | |
+ * A2 -|7 10|- D2
+ * | |
+ * GND -|8 9|- D3
+ * | |
+ * +---------+
+ *
+ * input line signal
+ * ----------------------------
+ * A7 15 EMACT'
+ * A6 1 F1(0)
+ * A5 2 F1(1)'
+ * A4 3 F1(2)'
+ * A3 4 F1(3)'
+ * A2 7 0 (GND)
+ * A1 6 PC1O
+ * A0 5 PC0O
+ *
+ * output line signal
+ * ----------------------------
+ * D0 12 PC1T
+ * D1 11 PC1F
+ * D2 10 PC0T
+ * D3 9 PC0F
+ *
+ * The outputs are connected to a dual 4:1 demultiplexer 74S153, so that
+ * depending on NEXT01' and RESET the following signals are passed through:
+ *
+ * RESET NEXT[01]' PC0I PC1I
+ * --------------------------------------
+ * 0 0 PC0T PC1T
+ * 0 1 PC0F PC1F
+ * 1 0 PC0I4 T14 (?)
+ * 1 1 -"- -"-
+ *
+ * This selects the microcode "page" to jump to on SWMODE (F1 = 010)
+ * depending on the current NEXT[01]' level.
+ * </PRE>
+ */
+ UINT8* m_ctl2k_u76;
+
+ /**
+ * @brief 3k CRAM PROM a37
+ */
+ UINT8* m_cram3k_a37;
+
+ /**
+ * @brief memory addressing PROM a64
+ */
+ UINT8* m_madr_a64;
+
+ /**
+ * @brief memory addressing PROM a65
+ */
+ UINT8* m_madr_a65;
+
+ /**
+ * @brief unused PROM a90
+ * Data sheet 05a_AIM.pdf page 14
+ * inputs A0-A7 from R0-R7 (?)
+ * output signal
+ * -------------------
+ * Q0 KP3
+ * Q1 KP4
+ * Q2 KP5
+ * Q3 unused
+ *
+ * I haven't found yet where KP3-KP5 are used
+ */
+ UINT8* m_madr_a90;
+
+ /**
+ * @brief unused PROM a91
+ * Data sheet 05a_AIM.pdf page 14
+ * inputs A0-A7 from R0-R7 (?)
+ *
+ * Output Signal
+ * -------------------
+ * Q0 KP0
+ * Q1 KP1
+ * Q2 KP2
+ * Q3 unused
+ * KP0-KP3 are decoded using 7442 a78 to select
+ * the keyboard row enable
+ *
+ * Enable Key[0] Key[1] Key[2] Key[3] Key[4] Key[5] Key[6] Key[7]
+ * ------------------------------------------------------------------------
+ * KE(0) KB(R) KB(1) KB(3) KB(5) KB(T) KB(ESC) KB(2) KB(4)
+ * KE(1) KB(G) KB(TAB) KB(W) KB(6) KB(Y) KB(F) KB(0) KB(E)
+ * KE(2) KB(H) KB(CTL) KB(S) KB(7) KB(8) KB(C) KB(A) KB(D)
+ * KE(3) KB(N) KB(J) KB(9) KB(U) KB(M) KB(B) KB(I) KB(V)
+ * KE(4) KB(LCK) KB(Z) KB(X) KB(Q) KB(SPC) KB(^R) KB(O) KB(K)
+ * KE(5) KB([) KB(.) KB(L) KB(-) KB(+) KB(;) KB(,) KB(P)
+ * KE(6) KB(^L) KB(RTN) KB(") KB(/) KB(S3) KB(<-) KB(]) KB(\)
+ * KE(7) KB(S1) KB(DEL) KB(S2) KB(LF) KB(S4) KB(S5) KB(BW) KB(BS)
+ */
+ UINT8* m_madr_a91;
+
+ /**
+ * @brief ALU function to 74181 operation lookup PROM
+ */
+ UINT8* m_alu_a10;
+
+ //! output lines of the ALU a10 PROM
+ enum {
+ A10_UNUSED = (1 << 0),
+ A10_TSELECT = (1 << 1),
+ A10_ALUCI = (1 << 2),
+ A10_ALUM = (1 << 3),
+ A10_ALUS0 = (1 << 4),
+ A10_ALUS1 = (1 << 5),
+ A10_ALUS2 = (1 << 6),
+ A10_ALUS3 = (1 << 7),
+ A10_ALUIN = (A10_ALUM|A10_ALUCI|A10_ALUS0|A10_ALUS1|A10_ALUS2|A10_ALUS3)
+ };
+
+ //! no operating function to put in the m_bs, m_f1 and m_f2 slots
+ void noop() {}
+
+ //! per task bus source function pointers, early (0) and late (1)
+ a2func m_bs[2][ALTO2_TASKS][ALTO2_BUSSRC];
+ void set_bs(UINT8 task, UINT8 fn, a2func f0, a2func f1) {
+ m_bs[0][task][fn] = f0 ? f0 : &alto2_cpu_device::noop;
+ m_bs[1][task][fn] = f1 ? f1 : &alto2_cpu_device::noop;
+ }
+
+ //! per task f1 function pointers, early (0) and late (1)
+ a2func m_f1[2][ALTO2_TASKS][ALTO2_F1MAX];
+ void set_f1(UINT8 task, UINT8 fn, a2func f0, a2func f1) {
+ m_f1[0][task][fn] = f0 ? f0 : &alto2_cpu_device::noop;
+ m_f1[1][task][fn] = f1 ? f1 : &alto2_cpu_device::noop;
+ }
+
+ //! per task f2 function pointers, early (0) and late (1)
+ a2func m_f2[2][ALTO2_TASKS][ALTO2_F2MAX];
+ void set_f2(UINT8 task, UINT8 fn, a2func f0, a2func f1) {
+ m_f2[0][task][fn] = f0 ? f0 : &alto2_cpu_device::noop;
+ m_f2[1][task][fn] = f1 ? f1 : &alto2_cpu_device::noop;
+ }
+
+ bool m_ram_related[ALTO2_TASKS]; //!< set when task is RAM related
+
+ UINT64 m_cycle; //!< number of cycles executed in the current slice
+
+ UINT64 cycle() { return m_cycle; } //!< return the current CPU cycle
+ UINT64 ntime() { return m_cycle*ALTO2_UCYCLE/1000; } //!< return the current nano seconds
+
+ void hard_reset(); //!< reset the various registers
+ void soft_reset(); //!< soft reset
+
+ void fn_bs_bad_0(); //! bs dummy early function
+ void fn_bs_bad_1(); //! bs dummy late function
+
+ void fn_f1_bad_0(); //! f1 dummy early function
+ void fn_f1_bad_1(); //! f1 dummy late function
+
+ void fn_f2_bad_0(); //! f2 dummy early function
+ void fn_f2_bad_1(); //! f2 dummy late function
+
+ DECLARE_READ16_MEMBER( noop_r ); //!< read open bus (0177777)
+ DECLARE_WRITE16_MEMBER( noop_w ); //!< write open bus
+
+ DECLARE_READ16_MEMBER( bank_reg_r ); //!< read bank register in memory mapped I/O range
+ DECLARE_WRITE16_MEMBER( bank_reg_w ); //!< write bank register in memory mapped I/O range
+
+ void bs_early_read_r(); //!< bus source: drive bus by R register
+ void bs_early_load_r(); //!< bus source: load R places 0 on the BUS
+ void bs_late_load_r(); //!< bus source: load R from SHIFTER
+ void bs_early_read_md(); //!< bus source: drive BUS from read memory data
+ void bs_early_mouse(); //!< bus source: drive bus by mouse
+ void bs_early_disp(); //!< bus source: drive bus by displacement (which?)
+ void f1_early_block(); //!< F1 func: block active task
+ void f1_late_load_mar(); //!< F1 func: load memory address register
+ void f1_early_task(); //!< F1 func: task switch
+ void f1_late_l_lsh_1(); //!< F1 func: SHIFTER = left shift L once
+ void f1_late_l_rsh_1(); //!< F1 func: SHIFTER = right shift L once
+ void f1_late_l_lcy_8(); //!< F1 func: SHIFTER = byte swap L
+ void f2_late_bus_eq_zero(); //!< F2 func: branch on bus equals zero
+ void f2_late_shifter_lt_zero(); //!< F2 func: branch on shifter less than zero
+ void f2_late_shifter_eq_zero(); //!< F2 func: branch on shifter equals zero
+ void f2_late_bus(); //!< F2 func: branch on bus bits BUS[6-15]
+ void f2_late_alucy(); //!< F2 func: branch on latched ALU carry
+ void f2_late_load_md(); //!< F2 func: load memory data
+
+#if USE_ALU_74181
+ UINT32 alu_74181(UINT32 a, UINT32 b, UINT8 smc);
+#endif
+ void rdram(); //!< read the microcode ROM/RAM halfword
+ void wrtram(); //!< write the microcode RAM from M register and ALU
+
+ UINT8 m_ether_id; //!< configured Ethernet ID for this machine
+
+//*******************************************
+// inline the sub-devices
+//*******************************************
+#include "a2jkff.h"
+#include "a2ram.h"
+#include "a2hw.h"
+#include "a2kbd.h"
+#include "a2mouse.h"
+#include "a2disk.h"
+#include "a2disp.h"
+#include "a2mem.h"
+#include "a2emu.h"
+#include "a2ksec.h"
+#include "a2ether.h"
+#include "a2mrt.h"
+#include "a2dwt.h"
+#include "a2curt.h"
+#include "a2dht.h"
+#include "a2dvt.h"
+#include "a2part.h"
+#include "a2dwt.h"
+#include "a2kwd.h"
+};
+
+extern const device_type ALTO2;
+
+
+#endif /* _CPU_ALTO2_H_ */
diff --git a/src/emu/cpu/alto2/alto2dsm.c b/src/emu/cpu/alto2/alto2dsm.c
new file mode 100644
index 00000000000..8c3796f9845
--- /dev/null
+++ b/src/emu/cpu/alto2/alto2dsm.c
@@ -0,0 +1,391 @@
+/**********************************************************
+ * Xerox AltoII disassembler
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ **********************************************************/
+#include "alto2cpu.h"
+
+#define loc_DASTART 0000420 // display list header
+#define loc_DVIBITS 0000421 // display vertical field interrupt bitword
+#define loc_ITQUAN 0000422 // interval timer stored quantity
+#define loc_ITBITS 0000423 // interval timer bitword
+#define loc_MOUSEX 0000424 // mouse X coordinate
+#define loc_MOUSEY 0000425 // mouse Y coordinate
+#define loc_CURSORX 0000426 // cursor X coordinate
+#define loc_CURSORY 0000427 // cursor Y coordinate
+#define loc_RTC 0000430 // real time clock
+#define loc_CURMAP 0000431 // cursor bitmap (16 words up to 00450)
+#define loc_WW 0000452 // interrupt wakeups waiting
+#define loc_ACTIVE 0000453 // active interrupt bitword
+#define loc_MASKTAB 0000460 // mask table for convert
+#define loc_PCLOC 0000500 // saved interrupt PC
+#define loc_INTVEC 0000501 // interrupt transfer vector (15 words up to 00517)
+#define loc_KBLK 0000521 // disk command block address
+#define loc_KSTAT 0000522 // disk status at start of current sector
+#define loc_KADDR 0000523 // disk address of latest disk command
+#define loc_KSIBITS 0000524 // sector interrupt bit mask
+#define loc_ITTIME 0000525 // interval timer timer
+#define loc_TRAPPC 0000527 // trap saved PC
+#define loc_TRAPVEC 0000530 // trap vectors (up to 0567)
+#define loc_TIMERDATA 0000570 // timer data (OS; up to 0577)
+#define loc_EPLOC 0000600 // ethernet post location
+#define loc_EBLOC 0000601 // ethernet interrupt bitmask
+#define loc_EELOC 0000602 // ethernet ending count
+#define loc_ELLOC 0000603 // ethernet load location
+#define loc_EICLOC 0000604 // ethernet input buffer count
+#define loc_EIPLOC 0000605 // ethernet input buffer pointer
+#define loc_EOCLOC 0000606 // ethernet output buffer count
+#define loc_EOPLOC 0000607 // ethernet output buffer pointer
+#define loc_EHLOC 0000610 // ethernet host address
+#define loc_ERSVD 0000611 // reserved for ethernet expansion (up to 00612)
+#define loc_ALTOV 0000613 // Alto I/II indication that microcode caninterrogate (0 = Alto I, -1 = Alto II)
+#define loc_DCBR 0000614 // posted by parity task (main memory parity error)
+#define loc_KNMAR 0000615 // -"-
+#define loc_DWA 0000616 // -"-
+#define loc_CBA 0000617 // -"-
+#define loc_PC 0000620 // -"-
+#define loc_SAD 0000621 // -"-
+#define loc_SWATR 0000700 // saved registers (Swat; up to 00707)
+#define loc_UTILOUT 0177016 // printer output (up to 177017)
+#define loc_XBUS 0177020 // untility input bus (up to 177023)
+#define loc_MEAR 0177024 // memory error address register
+#define loc_MESR 0177025 // memory error status register
+#define loc_MECR 0177026 // memory error control register
+#define loc_UTILIN 0177030 // printer status, mouse keyset
+#define loc_KBDAD 0177034 // undecoded keyboard (up to 177037)
+#define loc_BANKREGS 0177740 // extended memory option bank registers
+
+/**
+ * @brief Microcode and constants PROM size
+ */
+#define MCODE_PAGE 1024
+#define MCODE_SIZE (2*MCODE_PAGE) /* Alto II may have 2 pages (or even 4?) */
+#define MCODE_MASK (MCODE_SIZE - 1)
+#define PROM_SIZE 256
+
+/**
+ * @brief short names for the 16 tasks
+ */
+static const char *taskname[16] = {
+ "EMU", // emulator task
+ "T01",
+ "T02",
+ "T03",
+ "DSC", // disk sector task
+ "T05",
+ "T06",
+ "ETH", // ethernet task
+ "MRT", // memory refresh task
+ "DWT", // display word task
+ "CUR", // cursor task
+ "DHT", // display horizontal task
+ "DVT", // display vertical task
+ "PAR", // parity task
+ "DWD", // disk word task
+ "T17"
+};
+
+/**
+ * @brief names for the 32 R registers
+ */
+static const char *regname[32] = {
+ "AC(3)", // emulator accu 3
+ "AC(2)", // emulator accu 2
+ "AC(1)", // emulator accu 1
+ "AC(0)", // emulator accu 0
+ "R04",
+ "R05",
+ "PC", // emulator program counter
+ "R07",
+ "R10",
+ "R11",
+ "R12",
+ "R13",
+ "R14",
+ "R15",
+ "R16",
+ "R17",
+ "R20",
+ "R21",
+ "CBA", // address of the currently active DCB+1
+ "AECL", // address of end of current scanline's bitmap
+ "SLC", // scan line count
+ "HTAB", // number of tab words remaining on current scanline
+ "DWA", // address of the bit map double word being fetched
+ "MTEMP", // temporary cell
+ "R30",
+ "R31",
+ "R32",
+ "R33",
+ "R34",
+ "R35",
+ "R36",
+ "R37"
+};
+
+//! for ALUF which is the value loaded into T, if t flags is set
+static const char* t_bus_alu[16] = {
+ "ALU",
+ "BUS",
+ "ALU",
+ "BUS",
+ "BUS",
+ "ALU",
+ "ALU",
+ "BUS",
+ "BUS",
+ "BUS",
+ "ALU",
+ "ALU",
+ "ALU",
+ "BUS",
+ "BUS",
+ "BUS",
+};
+
+/**
+ * @brief copy of the constant PROM, which this disassembler may not have access to
+ */
+static UINT16 const_prom[PROM_SIZE] = {
+ /* 0000 */ 0x0000, 0x0001, 0x0002, 0xfffe, 0xffff, 0xffff, 0x000f, 0xffff,
+ /* 0008 */ 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0xfff8, 0xfff8,
+ /* 0010 */ 0x0010, 0x001f, 0x0020, 0x003f, 0x0040, 0x007f, 0x0080, 0x0007,
+ /* 0018 */ 0x00ff, 0xff00, 0x0400, 0x0100, 0x0110, 0x0151, 0x0114, 0x000f,
+ /* 0020 */ 0x0116, 0x0118, 0x0ffa, 0xf000, 0x4000, 0xfffc, 0xfff6, 0xffeb,
+ /* 0028 */ 0x4800, 0x6c00, 0x0800, 0x1000, 0xfe00, 0x7fff, 0x7fe0, 0x7f00,
+ /* 0030 */ 0xffbd, 0x0f00, 0x0f0f, 0xf0f0, 0x6048, 0x3000, 0x7159, 0x2109,
+ /* 0038 */ 0x6a3c, 0x4213, 0xa5a5, 0xfe1c, 0x3f00, 0xffc0, 0x012a, 0x0140,
+ /* 0040 */ 0x8000, 0xffe0, 0x00bf, 0xfff9, 0xfff0, 0xfffd, 0x0970, 0x5d20,
+ /* 0048 */ 0x3844, 0x6814, 0xfc00, 0xfe20, 0xfe22, 0x0083, 0x00f0, 0xff80,
+ /* 0050 */ 0xf800, 0xe000, 0xc000, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff,
+ /* 0058 */ 0x3fff, 0x0200, 0x2000, 0xfff1, 0x0156, 0x0157, 0x0138, 0x0c00,
+ /* 0060 */ 0x0130, 0x1813, 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185,
+ /* 0068 */ 0x0186, 0x0187, 0x0188, 0x018a, 0x0112, 0x0113, 0x0102, 0xfff0,
+ /* 0070 */ 0x0153, 0x0154, 0xffef, 0xffe4, 0xfffb, 0x000a, 0xffc1, 0x001f,
+ /* 0078 */ 0x0e00, 0x007e, 0xff7e, 0x0018, 0x000d, 0x03f8, 0x83f9, 0xffe0,
+ /* 0080 */ 0xfbff, 0x0009, 0x000b, 0x000c, 0x000e, 0x0030, 0x01fe, 0xff7f,
+ /* 0088 */ 0x81ff, 0xffbf, 0xffcc, 0x0557, 0x0041, 0x0198, 0x0199, 0x01a2,
+ /* 0090 */ 0xfe72, 0xfe58, 0x0012, 0x0014, 0x00dd, 0x02ff, 0x0101, 0x0001,
+ /* 0098 */ 0x0401, 0x0011, 0x0013, 0x0015, 0x0016, 0x0017, 0x0019, 0x0003,
+ /* 00a0 */ 0x03bd, 0x01de, 0xfe50, 0x00c0, 0x0c01, 0x6200, 0x6300, 0x0008,
+ /* 00a8 */ 0x6400, 0x6500, 0x6e00, 0x6700, 0x6900, 0x6d00, 0x6600, 0x000c,
+ /* 00b0 */ 0x6b00, 0x6b01, 0x6b02, 0x6b03, 0x6b04, 0x6b05, 0x6b06, 0x0010,
+ /* 00b8 */ 0x6b07, 0x6b08, 0x6b09, 0x6b0a, 0x6b0b, 0x6b0c, 0x6b0d, 0x0020,
+ /* 00c0 */ 0x6b0e, 0x6b0f, 0xfff3, 0xfe14, 0xfe15, 0xfe16, 0x0ffc, 0x0040,
+ /* 00c8 */ 0x04ff, 0x05ff, 0x06ff, 0x013f, 0x017e, 0xfe7d, 0xffff, 0x0080,
+ /* 00d0 */ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00c0,
+ /* 00d8 */ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00ff,
+ /* 00e0 */ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ /* 00e8 */ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ /* 00f0 */ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ /* 00f8 */ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+};
+
+/**
+ * @brief print a symbolic name for an mpc address
+ *
+ * @param a microcode address (mpc)
+ * @return pointer to const string with the address or symbolic name
+ */
+static const char *addrname(int a)
+{
+ static char buffer[4][32];
+ static int which = 0;
+ char *dst;
+
+ which = (which + 1) % 4;
+ dst = buffer[which];
+
+ if (a < 020) {
+ // start value for mpc per task is the task number
+ snprintf(dst, sizeof(buffer[0]), "*%s", taskname[a]);
+ } else {
+ snprintf(dst, sizeof(buffer[0]), "%04o", a);
+ }
+ return dst;
+}
+
+offs_t alto2_cpu_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
+{
+ size_t len = 128;
+
+ UINT32 mir = (static_cast<UINT32>(oprom[0]) << 24) |
+ (static_cast<UINT32>(oprom[1]) << 16) |
+ (static_cast<UINT32>(oprom[2]) << 8) |
+ (static_cast<UINT32>(oprom[3]) << 0);
+ UINT8 rsel = static_cast<UINT8>((mir >> 27) & 31);
+ UINT8 aluf = static_cast<UINT8>((mir >> 23) & 15);
+ UINT8 bs = static_cast<UINT8>((mir >> 20) & 7);
+ UINT8 f1 = static_cast<UINT8>((mir >> 16) & 15);
+ UINT8 f2 = static_cast<UINT8>((mir >> 12) & 15);
+ UINT8 t = static_cast<UINT8>((mir >> 11) & 1);
+ UINT8 l = static_cast<UINT8>((mir >> 10) & 1);
+ offs_t next = static_cast<offs_t>(mir & 1023);
+ const UINT8* src = oprom - 4 * pc + 4 * next;
+ UINT32 next2 = (static_cast<UINT32>(src[0]) << 24) |
+ (static_cast<UINT32>(src[1]) << 16) |
+ (static_cast<UINT32>(src[2]) << 8) |
+ (static_cast<UINT32>(src[3]) << 0);
+ UINT16 prefetch = next2 & 1023;
+ char *dst = buffer;
+ offs_t result = 1 | DASMFLAG_SUPPORTED;
+ UINT8 pa;
+
+ if (next != pc + 1)
+ result |= DASMFLAG_STEP_OUT;
+
+ if (t)
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "T<-%s ", t_bus_alu[aluf]);
+ if (l)
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "L<- ");
+ if (bs == 1)
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "%s<- ", regname[rsel]);
+ switch (aluf) {
+ case 0: // T?: BUS
+ // this is somehow redundant and just wasting space
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS) ");
+ break;
+ case 1: // : T
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(T) ");
+ break;
+ case 2: // T?: BUS OR T
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS|T) ");
+ break;
+ case 3: // : BUS AND T
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS&T) ");
+ break;
+ case 4: // : BUS XOR T
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS^T) ");
+ break;
+ case 5: // T?: BUS + 1
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS+1) ");
+ break;
+ case 6: // T?: BUS - 1
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS-1) ");
+ break;
+ case 7: // : BUS + T
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS+T) ");
+ break;
+ case 8: // : BUS - T
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS-T) ");
+ break;
+ case 9: // : BUS - T - 1
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS-T-1) ");
+ break;
+ case 10: // T?: BUS + T + 1
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS+T+1) ");
+ break;
+ case 11: // T?: BUS + SKIP
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS+SKIP) ");
+ break;
+ case 12: // T?: BUS, T (AND)
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS,T) ");
+ break;
+ case 13: // : BUS AND NOT T
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "ALUF(BUS&~T) ");
+ break;
+ case 14: // : undefined
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "*ALUF(BUS) ");
+ break;
+ case 15: // : undefined
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "*ALUF(BUS) ");
+ break;
+ }
+
+ switch (bs) {
+ case 0: // read R
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-%s ", regname[rsel]);
+ break;
+ case 1: // load R from shifter output
+ // dst += snprintf(dst, len - (size_t)(dst - buffer), "; %s<-", regname[rsel]);
+ break;
+ case 2: // enables no source to the BUS, leaving it all ones
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-177777 ");
+ break;
+ case 3: // performs different functions in different tasks
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-BS3 ");
+ break;
+ case 4: // performs different functions in different tasks
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-BS4 ");
+ break;
+ case 5: // memory data
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-MD ");
+ break;
+ case 6: // BUS[3-0] <- MOUSE; BUS[15-4] <- -1
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-MOUSE ");
+ break;
+ case 7: // IR[7-0], possibly sign extended
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-DISP ");
+ break;
+ }
+
+
+ switch (f1) {
+ case 0: // no operation
+ break;
+ case 1: // load MAR from ALU output; start main memory reference
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "MAR<-ALU ");
+ break;
+ case 2: // switch tasks if higher priority wakeup is pending
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "TASK ");
+ break;
+ case 3: // disable the current task until re-enabled by a hardware-generated condition
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BLOCK ");
+ break;
+ case 4: // SHIFTER output will be L shifted left one place
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "SHIFTER<-L(LSH1) ");
+ break;
+ case 5: // SHIFTER output will be L shifted right one place
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "SHIFTER<-L(RSH1) ");
+ break;
+ case 6: // SHIFTER output will be L rotated left 8 places
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "SHIFTER<-L(LCY8) ");
+ break;
+ case 7: // put the constant from PROM (RSELECT,BS) on the bus
+ pa = (rsel << 3) | bs;
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-%05o CONST[%03o]", const_prom[pa], pa);
+ break;
+ default:
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "F1_%02o ", f1);
+ break;
+ }
+
+ switch (f2) {
+ case 0: // no operation
+ break;
+ case 1: // NEXT <- NEXT OR (BUS==0 ? 1 : 0)
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "[BUS==0 ? %s:%s] ",
+ addrname((prefetch | 1) & MCODE_MASK), addrname(prefetch & MCODE_MASK));
+ break;
+ case 2: // NEXT <- NEXT OR (SHIFTER==0 ? 1 : 0)
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "[SH==0 ? %s:%s] ",
+ addrname((prefetch | 1) & MCODE_MASK), addrname(prefetch & MCODE_MASK));
+ break;
+ case 3: // NEXT <- NEXT OR (SHIFTER<0 ? 1 : 0)
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "[SH<0 ? %s:%s] ",
+ addrname((prefetch | 1) & MCODE_MASK), addrname(prefetch & MCODE_MASK));
+ break;
+ case 4: // NEXT <- NEXT OR BUS
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "NEXT<-BUS ");
+ break;
+ case 5: // NEXT <- NEXT OR ALUC0. ALUC0 is the carry produced by last L loading microinstruction.
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "[ALUC0 ? %s:%s] ",
+ addrname((prefetch | 1) & MCODE_MASK), addrname(prefetch & MCODE_MASK));
+ break;
+ case 6: // deliver BUS data to memory
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "MD<-BUS ");
+ break;
+ case 7: // put on the bus the constant from PROM (RSELECT,BS)
+ if (f1 != 7) {
+ pa = 8 * rsel + bs;
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-%05o CONST[%03o]", const_prom[pa], pa);
+ }
+ break;
+ default:
+ dst += snprintf(dst, len - (size_t)(dst - buffer), "BUS<-F2_%02o ", f2);
+ break;
+ }
+ return result;
+}
diff --git a/src/emu/cpu/cpu.mak b/src/emu/cpu/cpu.mak
index 52ec3e78b24..be4aad6c067 100644
--- a/src/emu/cpu/cpu.mak
+++ b/src/emu/cpu/cpu.mak
@@ -2388,3 +2388,112 @@ $(CPUOBJ)/score/score.o: $(CPUSRC)/score/score.c \
$(CPUOBJ)/score/scoredsm.o: $(CPUSRC)/score/scoredsm.c \
$(CPUSRC)/score/scorem.h
+
+#-------------------------------------------------
+# Xerox Alto-II
+#@src/emu/cpu/alto2/alto2cpu.h,CPUS += ALTO2
+#-------------------------------------------------
+
+ifneq ($(filter ALTO2,$(CPUS)),)
+OBJDIRS += $(CPUOBJ)/alto2
+CPUOBJS += $(CPUOBJ)/alto2/alto2cpu.o \
+ $(CPUOBJ)/alto2/a2disk.o \
+ $(CPUOBJ)/alto2/a2disp.o \
+ $(CPUOBJ)/alto2/a2curt.o \
+ $(CPUOBJ)/alto2/a2dht.o \
+ $(CPUOBJ)/alto2/a2dvt.o \
+ $(CPUOBJ)/alto2/a2dwt.o \
+ $(CPUOBJ)/alto2/a2emu.o \
+ $(CPUOBJ)/alto2/a2ether.o \
+ $(CPUOBJ)/alto2/a2hw.o \
+ $(CPUOBJ)/alto2/a2kbd.o \
+ $(CPUOBJ)/alto2/a2ksec.o \
+ $(CPUOBJ)/alto2/a2kwd.o \
+ $(CPUOBJ)/alto2/a2mem.o \
+ $(CPUOBJ)/alto2/a2mouse.o \
+ $(CPUOBJ)/alto2/a2mrt.o \
+ $(CPUOBJ)/alto2/a2part.o \
+ $(CPUOBJ)/alto2/a2ram.o \
+ $(CPUOBJ)/alto2/a2roms.o
+
+DASMOBJS += $(CPUOBJ)/alto2/alto2dsm.o
+endif
+
+$(CPUOBJ)/alto2/alto2cpu.o: $(CPUSRC)/alto2/alto2cpu.c \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2disk.o: $(CPUSRC)/alto2/a2disk.c \
+ $(CPUSRC)/alto2/a2disk.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2disp.o: $(CPUSRC)/alto2/a2disp.c \
+ $(CPUSRC)/alto2/a2disp.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2curt.o: $(CPUSRC)/alto2/a2curt.c \
+ $(CPUSRC)/alto2/a2curt.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2dht.o: $(CPUSRC)/alto2/a2dht.c \
+ $(CPUSRC)/alto2/a2dht.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2dvt.o: $(CPUSRC)/alto2/a2dvt.c \
+ $(CPUSRC)/alto2/a2dvt.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2dwt.o: $(CPUSRC)/alto2/a2dwt.c \
+ $(CPUSRC)/alto2/a2dwt.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2emu.o: $(CPUSRC)/alto2/a2emu.c \
+ $(CPUSRC)/alto2/a2emu.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2ether.o: $(CPUSRC)/alto2/a2ether.c \
+ $(CPUSRC)/alto2/a2ether.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2hw.o: $(CPUSRC)/alto2/a2hw.c \
+ $(CPUSRC)/alto2/a2hw.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2kbd.o: $(CPUSRC)/alto2/a2kbd.c \
+ $(CPUSRC)/alto2/a2kbd.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2ksec.o: $(CPUSRC)/alto2/a2ksec.c \
+ $(CPUSRC)/alto2/a2ksec.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2kwd.o: $(CPUSRC)/alto2/a2kwd.c \
+ $(CPUSRC)/alto2/a2kwd.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2mem.o: $(CPUSRC)/alto2/a2mem.c \
+ $(CPUSRC)/alto2/a2mem.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2mouse.o: $(CPUSRC)/alto2/a2mouse.c \
+ $(CPUSRC)/alto2/a2mouse.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2mrt.o: $(CPUSRC)/alto2/a2mrt.c \
+ $(CPUSRC)/alto2/a2mrt.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2part.o: $(CPUSRC)/alto2/a2part.c \
+ $(CPUSRC)/alto2/a2part.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2ram.o: $(CPUSRC)/alto2/a2ram.c \
+ $(CPUSRC)/alto2/a2ram.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/a2roms.o: $(CPUSRC)/alto2/a2roms.c \
+ $(CPUSRC)/alto2/a2roms.h \
+ $(CPUSRC)/alto2/alto2cpu.h
+
+$(CPUOBJ)/alto2/alto2dsm.o: $(CPUSRC)/alto2/alto2dsm.c \
+ $(CPUSRC)/alto2/alto2cpu.h
+
diff --git a/src/emu/drivers/xtal.h b/src/emu/drivers/xtal.h
index b490b69d8cf..6aeba8670e9 100644
--- a/src/emu/drivers/xtal.h
+++ b/src/emu/drivers/xtal.h
@@ -176,6 +176,7 @@ enum
XTAL_28_48MHz = 28480000, /* Chromatics CGC-7900 */
XTAL_28_63636MHz = 28636363, /* Later Leland games and Atari GT, Amiga NTSC, Raiden2 h/w (8x NTSC subcarrier)*/
XTAL_28_7MHz = 28700000,
+ XTAL_29_4912MHz = 29491200, /* Xerox Alto-II system clock (tagged 29.4MHz in the schematics) */
XTAL_30MHz = 30000000, /* Impera Magic Card */
XTAL_30_4761MHz = 30476100, /* Taito JC */
XTAL_30_8MHz = 30800000, /* 15IE-00-013 */
diff --git a/src/emu/emu.mak b/src/emu/emu.mak
index 777644d5ff0..32aa695a3bd 100644
--- a/src/emu/emu.mak
+++ b/src/emu/emu.mak
@@ -178,6 +178,7 @@ EMUIMAGEDEVOBJS = \
$(EMUIMAGEDEV)/cartslot.o \
$(EMUIMAGEDEV)/cassette.o \
$(EMUIMAGEDEV)/chd_cd.o \
+ $(EMUIMAGEDEV)/diablo.o \
$(EMUIMAGEDEV)/flopdrv.o \
$(EMUIMAGEDEV)/floppy.o \
$(EMUIMAGEDEV)/harddriv.o \
diff --git a/src/emu/imagedev/diablo.c b/src/emu/imagedev/diablo.c
new file mode 100644
index 00000000000..269b819688d
--- /dev/null
+++ b/src/emu/imagedev/diablo.c
@@ -0,0 +1,288 @@
+/**********************************************************
+ * DIABLO drive image to hard disk interface
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ **********************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+#include "harddisk.h"
+#include "diablo.h"
+
+
+static OPTION_GUIDE_START(dsk_option_guide)
+ OPTION_INT('C', "cylinders", "Cylinders")
+ OPTION_INT('H', "heads", "Heads")
+ OPTION_INT('S', "sectors", "Sectors")
+ OPTION_INT('L', "sectorlength", "Sector Words")
+ OPTION_INT('K', "hunksize", "Hunk Bytes")
+OPTION_GUIDE_END
+
+static const char *dsk_option_spec =
+ "C1-[203]-1024;H1/[2]/4/8;S1-[12]-64;L267;K6408";
+
+
+// device type definition
+const device_type DIABLO = &device_creator<diablo_image_device>;
+
+//-------------------------------------------------
+// diablo_image_device - constructor
+//-------------------------------------------------
+
+diablo_image_device::diablo_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, DIABLO, "Diablo", tag, owner, clock, "diablo_image", __FILE__),
+ device_image_interface(mconfig, *this),
+ m_chd(NULL),
+ m_hard_disk_handle(NULL),
+ m_device_image_load(device_image_load_delegate()),
+ m_device_image_unload(device_image_func_delegate())
+{
+}
+
+//-------------------------------------------------
+// diablo_image_device - destructor
+//-------------------------------------------------
+
+diablo_image_device::~diablo_image_device()
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void diablo_image_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const diablo_interface *intf = reinterpret_cast<const diablo_interface *>(static_config());
+ if (intf != NULL)
+ *static_cast<diablo_interface *>(this) = *intf;
+
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_interface, 0, sizeof(m_interface));
+ memset(&m_device_displayinfo, 0, sizeof(m_device_displayinfo));
+ }
+
+ m_formatlist.append(*global_alloc(image_device_format("chd", "CHD Hard drive", "chd,dsk", dsk_option_spec)));
+
+ // set brief and instance name
+ update_names();
+}
+
+const option_guide *diablo_image_device::create_option_guide() const
+{
+ return dsk_option_guide;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void diablo_image_device::device_start()
+{
+ m_chd = NULL;
+
+ // try to locate the CHD from a DISK_REGION
+ chd_file *handle = get_disk_handle(machine(), tag());
+ if (handle != NULL)
+ {
+ m_hard_disk_handle = hard_disk_open(handle);
+ }
+ else
+ {
+ m_hard_disk_handle = NULL;
+ }
+}
+
+void diablo_image_device::device_stop()
+{
+ if (m_hard_disk_handle)
+ hard_disk_close(m_hard_disk_handle);
+}
+
+bool diablo_image_device::call_load()
+{
+ int our_result;
+
+ our_result = internal_load_dsk();
+ /* Check if there is an image_load callback defined */
+ if (!m_device_image_load.isnull())
+ {
+ /* Let the override do some additional work/checks */
+ our_result = m_device_image_load(*this);
+ }
+ return our_result;
+
+}
+
+bool diablo_image_device::call_create(int create_format, option_resolution *create_args)
+{
+ int err;
+ UINT32 sectorsize, hunksize;
+ UINT32 cylinders, heads, sectors, totalsectors;
+ astring metadata;
+
+ cylinders = option_resolution_lookup_int(create_args, 'C');
+ heads = option_resolution_lookup_int(create_args, 'H');
+ sectors = option_resolution_lookup_int(create_args, 'S');
+ sectorsize = option_resolution_lookup_int(create_args, 'L') * sizeof(UINT16);
+ hunksize = option_resolution_lookup_int(create_args, 'K');
+
+ totalsectors = cylinders * heads * sectors;
+
+ /* create the CHD file */
+ chd_codec_type compression[4] = { CHD_CODEC_NONE };
+ err = m_origchd.create(*image_core_file(), (UINT64)totalsectors * (UINT64)sectorsize, hunksize, sectorsize, compression);
+ if (err != CHDERR_NONE)
+ goto error;
+
+ /* if we created the image and hence, have metadata to set, set the metadata */
+ metadata.format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sectorsize);
+ err = m_origchd.write_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
+ m_origchd.close();
+
+ if (err != CHDERR_NONE)
+ goto error;
+
+ return internal_load_dsk();
+
+error:
+ return IMAGE_INIT_FAIL;
+}
+
+void diablo_image_device::call_unload()
+{
+ /* Check if there is an image_unload callback defined */
+ if ( !m_device_image_unload.isnull() )
+ {
+ m_device_image_unload(*this);
+ }
+
+ if (m_hard_disk_handle != NULL)
+ {
+ hard_disk_close(m_hard_disk_handle);
+ m_hard_disk_handle = NULL;
+ }
+
+ m_origchd.close();
+ m_diffchd.close();
+ m_chd = NULL;
+}
+
+/*-------------------------------------------------
+ open_disk_diff - open a DISK diff file
+-------------------------------------------------*/
+
+static chd_error open_disk_diff(emu_options &options, const char *name, chd_file &source, chd_file &diff_chd)
+{
+ astring fname(name, ".dif");
+
+ /* try to open the diff */
+ //printf("Opening differencing image file: %s\n", fname.cstr());
+ emu_file diff_file(options.diff_directory(), OPEN_FLAG_READ | OPEN_FLAG_WRITE);
+ file_error filerr = diff_file.open(fname);
+ if (filerr == FILERR_NONE)
+ {
+ astring fullpath(diff_file.fullpath());
+ diff_file.close();
+
+ //printf("Opening differencing image file: %s\n", fullpath.cstr());
+ return diff_chd.open(fullpath, true, &source);
+ }
+
+ /* didn't work; try creating it instead */
+ //printf("Creating differencing image: %s\n", fname.cstr());
+ diff_file.set_openflags(OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ filerr = diff_file.open(fname);
+ if (filerr == FILERR_NONE)
+ {
+ astring fullpath(diff_file.fullpath());
+ diff_file.close();
+
+ /* create the CHD */
+ //printf("Creating differencing image file: %s\n", fullpath.cstr());
+ chd_codec_type compression[4] = { CHD_CODEC_NONE };
+ chd_error err = diff_chd.create(fullpath, source.logical_bytes(), source.hunk_bytes(), compression, source);
+ if (err != CHDERR_NONE)
+ return err;
+
+ return diff_chd.clone_all_metadata(source);
+ }
+
+ return CHDERR_FILE_NOT_FOUND;
+}
+
+int diablo_image_device::internal_load_dsk()
+{
+ astring tempstring;
+ chd_error err = CHDERR_NONE;
+
+ m_chd = NULL;
+
+ if (m_hard_disk_handle)
+ hard_disk_close(m_hard_disk_handle);
+
+ /* open the CHD file */
+ if (software_entry() != NULL)
+ {
+ m_chd = get_disk_handle(device().machine(), device().subtag(tempstring,"harddriv"));
+ }
+ else
+ {
+ err = m_origchd.open(*image_core_file(), true);
+ if (err == CHDERR_NONE)
+ {
+ m_chd = &m_origchd;
+ }
+ else if (err == CHDERR_FILE_NOT_WRITEABLE)
+ {
+ err = m_origchd.open(*image_core_file(), false);
+ if (err == CHDERR_NONE)
+ {
+ err = open_disk_diff(device().machine().options(), basename_noext(), m_origchd, m_diffchd);
+ if (err == CHDERR_NONE)
+ {
+ m_chd = &m_diffchd;
+ }
+ }
+ }
+ }
+
+ if (m_chd != NULL)
+ {
+ /* open the hard disk file */
+ m_hard_disk_handle = hard_disk_open(m_chd);
+ if (m_hard_disk_handle != NULL)
+ return IMAGE_INIT_PASS;
+ }
+
+ /* if we had an error, close out the CHD */
+ m_origchd.close();
+ m_diffchd.close();
+ m_chd = NULL;
+ seterror(IMAGE_ERROR_UNSPECIFIED, chd_file::error_string(err));
+
+ return IMAGE_INIT_FAIL;
+}
+
+/*************************************
+ *
+ * Get the CHD file (from the src/chd.c core)
+ * after an image has been opened with the hd core
+ *
+ *************************************/
+
+chd_file *diablo_image_device::get_chd_file()
+{
+ chd_file *result = NULL;
+ hard_disk_file *hd_file = get_hard_disk_file();
+ if (hd_file)
+ result = hard_disk_get_chd(hd_file);
+ return result;
+}
diff --git a/src/emu/imagedev/diablo.h b/src/emu/imagedev/diablo.h
new file mode 100644
index 00000000000..bf064990452
--- /dev/null
+++ b/src/emu/imagedev/diablo.h
@@ -0,0 +1,100 @@
+/**********************************************************
+ * DIABLO drive image to hard disk interface
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ **********************************************************/
+
+#ifndef _IMAGEDEV_DIABLO_H_
+#define _IMAGEDEV_DIABLO_H_
+
+#include "harddisk.h"
+
+#define DIABLO_TAG(_id) "diablo"#_id
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+// ======================> diablo_interface
+
+struct diablo_interface
+{
+ const char * m_interface;
+ device_image_display_info_func m_device_displayinfo;
+};
+
+// ======================> diablo_image_device
+
+class diablo_image_device : public device_t,
+ public diablo_interface,
+ public device_image_interface
+{
+public:
+ // construction/destruction
+ diablo_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual ~diablo_image_device();
+
+ void set_device_load(device_image_load_delegate _load) { m_device_image_load = _load; }
+ void set_device_unload(device_image_func_delegate _unload) { m_device_image_unload = _unload; }
+
+ // image-level overrides
+ virtual bool call_load();
+ virtual bool call_create(int create_format, option_resolution *create_args);
+ virtual void call_unload();
+ virtual void call_display_info() { if (m_device_displayinfo) m_device_displayinfo(*this); }
+ virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) { load_software_part_region(*this, swlist, swname, start_entry ); return TRUE; }
+
+ virtual iodevice_t image_type() const { return IO_HARDDISK; }
+
+ virtual bool is_readable() const { return 1; }
+ virtual bool is_writeable() const { return 1; }
+ virtual bool is_creatable() const { return 0; }
+ virtual bool must_be_loaded() const { return 0; }
+ virtual bool is_reset_on_load() const { return 0; }
+ virtual const char *image_interface() const { return m_interface; }
+ virtual const char *file_extensions() const { return "chd,dsk"; }
+ virtual const option_guide *create_option_guide() const;
+
+ // specific implementation
+ hard_disk_file *get_hard_disk_file() { return m_hard_disk_handle; }
+ chd_file *get_chd_file();
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_stop();
+
+ int internal_load_dsk();
+
+ chd_file *m_chd;
+ chd_file m_origchd; /* handle to the original CHD */
+ chd_file m_diffchd; /* handle to the diff CHD */
+ hard_disk_file *m_hard_disk_handle;
+
+ device_image_load_delegate m_device_image_load;
+ device_image_func_delegate m_device_image_unload;
+};
+
+// device type definition
+extern const device_type DIABLO;
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_DIABLO_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, DIABLO, 0)
+#define MCFG_DIABLO_CONFIG_ADD(_tag,_config) \
+ MCFG_DEVICE_ADD(_tag, DIABLO, 0) \
+ MCFG_DEVICE_CONFIG(_config)
+
+#define MCFG_DIABLO_LOAD(_class,_load) \
+ static_cast<diablo_image_device *>(device)->set_device_load( DEVICE_IMAGE_LOAD_DELEGATE(_class,_load));
+
+#define MCFG_DIABLO_UNLOAD(_class,_unload) \
+ static_cast<diablo_image_device *>(device)->set_device_unload( DEVICE_IMAGE_UNLOAD_DELEGATE(_class,_unload));
+
+#endif /* _IMAGEDEV_DIABLO_H_ */
diff --git a/src/emu/machine/diablo_hd.c b/src/emu/machine/diablo_hd.c
new file mode 100644
index 00000000000..a9caa6d2cc9
--- /dev/null
+++ b/src/emu/machine/diablo_hd.c
@@ -0,0 +1,1466 @@
+/**********************************************************
+ * DIABLO31 and DIABLO44 hard drive support
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ **********************************************************/
+#include "diablo_hd.h"
+
+/**
+ *
+ * Just for completeness' sake:
+ * The mapping of disk controller connector P2 pins to the
+ * Winchester disk drive signals (see drive.h)
+ * <PRE>
+ * Alto Controller Winchester
+ * P2 signal disk bus
+ * -----------------------------------------------
+ * 1 GND D_GROUND
+ * 2 RDCLK' A_READ_CLOCK
+ * 3 WRDATA' B_WRITE_DATA_AND_CLOCK
+ * 4 SRWRDY' F_S_R_W
+ * 5 DISK L_SELECT_LINE_UNIT_1
+ * 6 CYL(7)' N_CYL_7
+ * 7 DISK' R_SELECT_LINE_UNIT_2
+ * 8 CYL(2)' T_CYL_2
+ * 9 ??? V_SELECT_LINE_UNIT_3
+ * 10 CYL(4)' X_CYL_4
+ * 11 CYL(0)' Z_CYL_0
+ * 12 CYL(1)' BB_CYL_1
+ * 13 CYL(3)' FF_CYL_3
+ * 14 ??? KK_BIT_2
+ * 15 CYL(8)' LL_CYL_8
+ * 16 ADRACK' NN_ADDX_ACKNOWLEDGE
+ * 17 SKINC' TT_SEEK_INCOMPLETE
+ * 18 LAI' XX_LOG_ADDX_INTERLOCK
+ * 19 CYL(6)' RR_CYL_6
+ * 20 RESTOR' VV_RESTORE
+ * 21 ??? UU_BIT_16
+ * 22 STROBE' SS_STROBE
+ * 23 ??? MM_BIT_8
+ * 24 ??? KK_BIT_4
+ * 25 ??? HH_WRITE_CHK
+ * 26 WRTGATE' EE_WRITE_GATE
+ * 27 ??? CC_BIT_SECTOR_ADDX
+ * 28 HEAD' AA_HEAD_SELECT
+ * 29 ??? Y_INDEX_MARK
+ * 30 SECT(4)' W_SECTOR_MARK
+ * 31 READY' U_FILE_READY
+ * 32 ??? S_PSEUDO_SECTOR_MARK
+ * 33 ??? P_WRITE_PROTECT_IND
+ * 34 ??? H_WRITE_PROTECT_INPUT_ATTENTION
+ * 35 ERGATE' K_ERASE_GATE
+ * 36 ??? M_HIGH_DENSITY
+ * 37 CYL(5)' J_CYL_5
+ * 38 RDDATA' C_READ_DATA
+ * 39 RDGATE' E_READ_GATE
+ * 40 GND ??
+ * </PRE>
+ */
+
+diablo_hd_device::diablo_hd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, DIABLO_HD, "Diablo Disk", tag, owner, clock, "diablo_hd", __FILE__),
+#if DIABLO_DEBUG
+ m_log_level(8),
+#endif
+ m_diablo31(true),
+ m_unit(0),
+ m_description(),
+ m_packs(1),
+ m_rotation_time(),
+ m_sector_time(),
+ m_sector_mark_0_time(),
+ m_sector_mark_1_time(),
+ m_bit_time(),
+ m_s_r_w_0(1),
+ m_ready_0(1),
+ m_sector_mark_0(1),
+ m_addx_acknowledge_0(1),
+ m_log_addx_interlock_0(1),
+ m_seek_incomplete_0(1),
+ m_egate_0(1),
+ m_wrgate_0(1),
+ m_rdgate_0(1),
+ m_cylinders(DIABLO_CYLINDERS),
+ m_pages(DIABLO_PAGES),
+ m_seekto(0),
+ m_restore(0),
+ m_cylinder(-1),
+ m_head(-1),
+ m_sector(-1),
+ m_page(-1),
+ m_cache(0),
+ m_bits(0),
+ m_rdfirst(-1),
+ m_rdlast(-1),
+ m_wrfirst(-1),
+ m_wrlast(-1),
+ m_sector_callback_cookie(0),
+ m_sector_callback(0),
+ m_timer(0),
+ m_image(0),
+ m_handle(0),
+ m_disk(0)
+{
+}
+
+/**
+ * @brief diablo_hd_device destructor
+ * Free all m_cache and m_bits pages and the arrays
+ */
+diablo_hd_device::~diablo_hd_device()
+{
+}
+
+#if DIABLO_DEBUG
+void diablo_hd_device::logprintf(int level, const char* format, ...)
+{
+ if (level > m_log_level)
+ return;
+ va_list ap;
+ va_start(ap, format);
+ vlogerror(format, ap);
+ va_end(ap);
+}
+#endif
+
+void diablo_hd_device::set_sector_callback(void *cookie, void (*callback)(void *, int))
+{
+ if (m_sector_callback_cookie == cookie && m_sector_callback == callback)
+ return;
+ LOG_DRIVE((0,"[DHD%u] cookie=%p callback=%p\n", m_unit, cookie, callback));
+ m_sector_callback_cookie = cookie;
+ m_sector_callback = callback;
+}
+
+#define DIABLO31_ROTATION_TIME attotime::from_usec(39900) //!< DIABLO 31 rotation time is approx. 40ms
+#define DIABLO31_SECTOR_TIME attotime::from_usec(39900/12) //!< DIABLO 31 sector time
+/**
+ * @brief DIABLO 31 bit clock is 3330kHz ~= 300ns per bit
+ * ~= 133333 bits/track (?)
+ * ~= 11111 bits/sector
+ * ~= 347 words/sector
+ */
+#define DIABLO31_BIT_TIME(bits) attotime::from_nsec(300*(bits))
+#define DIABLO31_SECTOR_BITS 10432
+#define DIABLO31_SECTOR_WORDS 347 //!< DIABLO 31 possible sector words
+#define DIABLO31_SECTOR_MARK_PULSE_PRE DIABLO31_BIT_TIME(16) //!< pulse width of sector mark before the next sector begins
+#define DIABLO31_SECTOR_MARK_PULSE_POST DIABLO31_BIT_TIME(16) //!< pulse width of sector mark after the next sector began
+
+#define DIABLO44_ROTATION_TIME attotime::from_usec(25000) //!< DIABLO 44 rotation time is approx. 25ms
+#define DIABLO44_SECTOR_TIME attotime::from_usec(25000/12) //!< DIABLO 44 sector time
+/**
+ * @brief DIABLO 44 bit clock is 5000kHz ~= 200ns per bit
+ * ~= 125184 bits/track (?)
+ * ~= 10432 bits/sector
+ * ~= 325 words/sector
+ */
+#define DIABLO44_BIT_TIME(bits) attotime::from_nsec(200*(bits))
+#define DIABLO44_SECTOR_BITS 10432
+#define DIABLO44_SECTOR_WORDS 325 //!< DIABLO 44 possible sector words
+#define DIABLO44_SECTOR_MARK_PULSE_PRE DIABLO44_BIT_TIME(16) //!< pulse width of sector mark before the next sector begins
+#define DIABLO44_SECTOR_MARK_PULSE_POST DIABLO44_BIT_TIME(16) //!< pulse width of sector mark after the next sector began
+
+#define MFROBL 34 //!< from the microcode: disk header preamble is 34 words
+#define MFRRDL 21 //!< from the microcode: disk header read delay is 21 words
+#define MIRRDL 4 //!< from the microcode: interrecord read delay is 4 words
+#define MIROBL 3 //!< from the microcode: disk interrecord preamble is 3 words
+#define MRPAL 3 //!< from the microcode: disk read postamble length is 3 words
+#define MWPAL 5 //!< from the microcode: disk write postamble length is 5 words
+
+#define GUARD_ZONE_BITS (16*32) //!< end of the guard zone at the beginning of a sector (wild guess!)
+
+/**
+ * @brief description of the sector layout (reverse engineered)
+ * <PRE>
+ *
+ * xx.x msec sector mark pulses
+ * -+ +-------------------------------------------------------------------------------+ +--
+ * | | | |
+ * +---+ +---+
+ *
+ * | |
+ *
+ * +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
+ * | PRE- |SYNC|HEADER|CKSUM| PRE- |SYNC| LABEL |CKSUM| PRE- |SYNC| DATA |CKSUM| POST |
+ * |AMBLE1| 1 | | 1 |AMBLE2| 2 | | 2 |AMBLE3| 3 | | 3 |AMBLE |
+ * +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
+ *
+ * | |
+ *
+ * +-----------------------------------------------------------------------------------+
+ * | |
+ * ---+ +----
+ * FORMAT WRITE GATE FOR INITIALIZING
+ * | |
+ *
+ * | +------------------------------+
+ * | |
+ * ---|----------------------------------------------------+ +----
+ * WRITE GATE FOR DATA XFER (*)
+ * | |
+ *
+ * | +-----------------------+-+------------------------------+
+ * | | | may be continuous (?) |
+ * ------------------------------+ +-+ +----
+ * ??? WRITE GATE FOR LABEL AND DATA XFER (*)
+ * | |
+ *
+ * | +--------------------+ +---------------------+ +----------------------------+
+ * | | | | | |
+ * -------+ +---+ +---+ +----
+ * READ GATE FOR INITIALIZING OR DATA XFER (**)
+ *
+ *
+ * (*) Enable should be delayed 1 byte/word time from last bit of checks sum.
+ * (**) Read Gate should be enabled half way through the preamble area. This
+ * ensures reading a zero field for data separator synchronization.
+ *
+ * </PRE>
+ */
+
+#define DIABLO_PAGENO_WORDS 1 //!< number of words in a page number (this doesn't really belong here)
+#define DIABLO_HEADER_WORDS 2 //!< number of words in a header (this doesn't really belong here)
+#define DIABLO_LABEL_WORDS 8 //!< number of words in a label (this doesn't really belong here)
+#define DIABLO_DATA_WORDS 256 //!< number of data words (this doesn't really belong here)
+#define DIABLO_CKSUM_WORDS 1 //!< number of words for a checksum (this doesn't really belong here)
+
+/**
+ * @brief format of the cooked disk image sectors, i.e. pure data
+ *
+ * The available images are a multiple of 267 words (534 bytes) per sector,
+ * 1 word page number
+ * 2 words header
+ * 8 words label
+ * 256 words data
+ */
+typedef struct {
+ UINT8 pageno[2*DIABLO_PAGENO_WORDS]; //!< sector page number
+ UINT8 header[2*DIABLO_HEADER_WORDS]; //!< sector header words
+ UINT8 label[2*DIABLO_LABEL_WORDS]; //!< sector label words
+ UINT8 data[2*DIABLO_DATA_WORDS]; //!< sector data words
+} diablo_sector_t;
+
+/**
+ * @brief write a bit into an array of UINT32
+ * @param bits pointer to array of bits
+ * @param dst destination index
+ * @param bit bit value
+ * @return next destination index
+ */
+static inline size_t WRBIT(UINT32* bits, size_t dst, int bit)
+{
+ if (bit) {
+ bits[(dst)/32] |= 1 << ((dst) % 32);
+ } else {
+ bits[(dst)/32] &= ~(1 << ((dst) % 32));
+ }
+ return ++dst;
+}
+
+/**
+ * @brief read a bit from an array of UINT32
+ * @param bits pointer to array of bits
+ * @param src source index
+ * @param bit reference to the bit to set
+ * @return next source index
+ */
+static inline size_t RDBIT(UINT32* bits, size_t src, int& bit)
+{
+ bit = (bits[src/32] >> (src % 32)) & 1;
+ return ++src;
+}
+
+/**
+ * @brief calculate the sector from the logical block address and read it
+ *
+ * Modifies drive's page by calculating the logical
+ * block address from cylinder, head, and sector.
+ */
+void diablo_hd_device::read_sector()
+{
+ /* If there's no drive, just reset the page number */
+ if (!m_image) {
+ LOG_DRIVE((0,"[DHD%u] CHS:%03d/%d/%02d => no image\n", m_unit, m_cylinder, m_head, m_sector));
+ m_page = -1;
+ return;
+ }
+ if (m_cylinder < 0 || m_cylinder >= m_cylinders) {
+ LOG_DRIVE((0,"[DHD%u] CHS:%03d/%d/%02d => invalid cylinder\n", m_unit, m_cylinder, m_head, m_sector));
+ m_page = -1;
+ return;
+ }
+ if (m_head < 0 || m_head >= DIABLO_HEADS) {
+ LOG_DRIVE((0,"[DHD%u] CHS:%03d/%d/%02d => invalid head\n", m_unit, m_cylinder, m_head, m_sector));
+ m_page = -1;
+ return;
+ }
+ if (m_sector < 0 || m_sector >= DIABLO_SPT) {
+ LOG_DRIVE((0,"[DHD%u] CHS:%03d/%d/%02d => invalid sector\n", m_unit, m_cylinder, m_head, m_sector));
+ m_page = -1;
+ return;
+ }
+ /* calculate the new disk relative sector offset */
+ m_page = DIABLO_PAGE(m_cylinder, m_head, m_sector);
+
+ // already have the sector image?
+ if (m_cache[m_page]) {
+ LOG_DRIVE((9,"[DHD%u] CHS:%03d/%d/%02d => page:%d is cached\n", m_unit, m_cylinder, m_head, m_sector, m_page));
+ return;
+ }
+
+ if (m_disk) {
+ // allocate a buffer for this page
+ m_cache[m_page] = auto_alloc_array(machine(), UINT8, sizeof(diablo_sector_t));
+ // and read the page from the hard_disk image
+ if (hard_disk_read(m_disk, m_page, m_cache[m_page])) {
+ LOG_DRIVE((2,"[DHD%u] CHS:%03d/%d/%02d => page:%d loaded\n", m_unit, m_cylinder, m_head, m_sector, m_page));
+ } else {
+ LOG_DRIVE((0,"[DHD%u] CHS:%03d/%d/%02d => page:%d read failed\n", m_unit, m_cylinder, m_head, m_sector, m_page));
+ auto_free(machine(), m_cache[m_page]);
+ m_cache[m_page] = 0;
+ }
+ } else {
+ LOG_DRIVE((2,"[DHD%u] no disk\n", m_unit));
+ }
+}
+
+/**
+ * @brief compute the checksum of a record
+ *
+ * @param src pointer to a record (header, label, data)
+ * @param size size of the record in bytes
+ * @param start start value for the checksum
+ * @return returns the checksum of the record
+ */
+int diablo_hd_device::cksum(UINT8 *src, size_t size, int start)
+{
+ int sum = start;
+ /* compute XOR of all words */
+ for (size_t offs = 0; offs < size; offs += 2) {
+ int word = src[size - 2 - offs] + 256 * src[size - 2 - offs + 1];
+ sum ^= word;
+ }
+ return sum;
+}
+
+/**
+ * @brief expand a series of clock bits and 0 data bits
+ *
+ * @param bits pointer to the sector bits
+ * @param dst destination offset into bits (bit number)
+ * @param size number of words to write
+ * @return offset to next destination bit
+ */
+size_t diablo_hd_device::expand_zeroes(UINT32 *bits, size_t dst, size_t size)
+{
+ for (size_t offs = 0; offs < 32 * size; offs += 2) {
+ dst = WRBIT(bits, dst, 1); // write the clock bit
+ dst = WRBIT(bits, dst, 0); // write the 0 data bit
+ }
+ return dst;
+}
+
+/**
+ * @brief expand a series of 0 words and write a final sync bit
+ *
+ * @param bits pointer to the sector bits
+ * @param dst destination offset into bits (bit number)
+ * @param size number of words to write
+ * @return offset to next destination bit
+ */
+size_t diablo_hd_device::expand_sync(UINT32 *bits, size_t dst, size_t size)
+{
+ for (size_t offs = 0; offs < 32 * size - 2; offs += 2) {
+ dst = WRBIT(bits, dst, 1); // write the clock bit
+ dst = WRBIT(bits, dst, 0); // write the 0 data bit
+ }
+ dst = WRBIT(bits, dst, 1); // write the final clock bit
+ dst = WRBIT(bits, dst, 1); // write the 1 data bit
+ return dst;
+}
+
+/**
+ * @brief expand a record of words into a array of bits at dst
+ *
+ * @param bits pointer to the sector bits
+ * @param dst destination offset into bits (bit number)
+ * @param field pointer to the record data (bytes)
+ * @param size size of the record in bytes
+ * @return offset to next destination bit
+ */
+size_t diablo_hd_device::expand_record(UINT32 *bits, size_t dst, UINT8 *field, size_t size)
+{
+ for (size_t offs = 0; offs < size; offs += 2) {
+ int word = field[size - 2 - offs] + 256 * field[size - 2 - offs + 1];
+ for (size_t bit = 0; bit < 16; bit++) {
+ dst = WRBIT(bits, dst, 1); // write the clock bit
+ dst = WRBIT(bits, dst, (word >> 15) & 1); // write the data bit
+ word <<= 1;
+ }
+ }
+ return dst;
+}
+
+/**
+ * @brief expand a record's checksum word to 32 bits
+ *
+ * @param bits pointer to the sector bits
+ * @param dst destination offset into bits (bit number)
+ * @param field pointer to the record data (bytes)
+ * @param size size of the record in bytes
+ * @return offset to next destination bit
+ */
+size_t diablo_hd_device::expand_cksum(UINT32 *bits, size_t dst, UINT8 *field, size_t size)
+{
+ int word = cksum(field, size, 0521);
+ for (size_t bit = 0; bit < 32; bit += 2) {
+ dst = WRBIT(bits, dst, 1); // write the clock bit
+ dst = WRBIT(bits, dst, (word >> 15) & 1); // write the data bit
+ word <<= 1;
+ }
+ return dst;
+}
+
+/**
+ * @brief expand a sector into an array of clock and data bits
+ *
+ * @param page page number (0 to DRIVE_PAGES-1)
+ * @return pointer to the newly allocated array of bits
+ */
+UINT32* diablo_hd_device::expand_sector()
+{
+ size_t dst;
+
+ if (!m_bits)
+ return NULL;
+ /* already expanded this sector? */
+ if (m_bits[m_page])
+ return m_bits[m_page];
+
+ /* allocate a sector buffer */
+ if (!m_cache[m_page]) {
+ LOG_DRIVE((0,"[DHD%u] no image for page #%d\n", m_unit, m_page));
+ return NULL;
+ }
+ diablo_sector_t *s = reinterpret_cast<diablo_sector_t *>(m_cache[m_page]);
+
+ /* allocate a bits image */
+ UINT32 *bits = auto_alloc_array_clear(machine(), UINT32, 400);
+
+ if (m_diablo31) {
+ /* write sync bit after (MFROBL-MRPAL) words - 1 bit */
+ dst = expand_sync(bits, 0, (MFROBL - MRPAL));
+ dst = expand_record(bits, dst, s->header, sizeof(s->header));
+ dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
+
+ /* write sync bit after 2 * MWPAL + 1 words - 1 bit */
+ dst = expand_sync(bits, dst, 2 * MWPAL);
+ dst = expand_record(bits, dst, s->label, sizeof(s->label));
+ dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
+
+ /* write sync bit after 2 * MWPAL + 1 words - 1 bit */
+ dst = expand_sync(bits, dst, 2 * MWPAL);
+ dst = expand_record(bits, dst, s->data, sizeof(s->data));
+ dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
+
+ /* fill MWPAL words of clock and 0 data bits */
+ dst = expand_zeroes(bits, dst, MWPAL);
+ } else {
+ /* write sync bit after (MFROBL - MRPAL) words - 1 bit */
+ dst = expand_sync(bits, 0, (MFROBL - MRPAL));
+ dst = expand_record(bits, dst, s->header, sizeof(s->header));
+ dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
+
+ /* write sync bit after 2 * MWPAL words - 1 bit */
+ dst = expand_sync(bits, dst, 2 * MWPAL);
+ dst = expand_record(bits, dst, s->label, sizeof(s->label));
+ dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
+
+ /* write sync bit after 2 * MWPAL words - 1 bit */
+ dst = expand_sync(bits, dst, 2 * MWPAL);
+ dst = expand_record(bits, dst, s->data, sizeof(s->data));
+ dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
+
+ /* fill MWPAL words of clock and 0 data bits */
+ dst = expand_zeroes(bits, dst, MWPAL);
+ }
+ m_bits[m_page] = bits;
+
+ LOG_DRIVE((0,"[DHD%u] CHS:%03d/%d/%02d #%5d bits\n", m_unit, m_cylinder, m_head, m_sector, dst));
+#if DIABLO_DEBUG
+ dump_record(s->pageno, 0, sizeof(s->pageno), "pageno", 0);
+ dump_record(s->header, 0, sizeof(s->header), "header", 0);
+ dump_record(s->label, 0, sizeof(s->label), "label", 0);
+ dump_record(s->data, 0, sizeof(s->data), "data", 1);
+#endif
+ return bits;
+}
+
+#if DIABLO_DEBUG
+void diablo_hd_device::dump_ascii(UINT8 *src, size_t size)
+{
+ size_t offs;
+ LOG_DRIVE((0," ["));
+ for (offs = 0; offs < size; offs++) {
+ char ch = (char)src[offs ^ 1];
+ LOG_DRIVE((0, "%c", ch < 32 || ch > 126 ? '.' : ch));
+ }
+ LOG_DRIVE((0,"]\n"));
+}
+
+
+/**
+ * @brief dump a record's contents
+ *
+ * @param src pointer to a record (header, label, data)
+ * @param size size of the record in bytes
+ * @param name name to print before the dump
+ */
+size_t diablo_hd_device::dump_record(UINT8 *src, size_t addr, size_t size, const char *name, int cr)
+{
+ size_t offs;
+ LOG_DRIVE((0,"%s:", name));
+ for (offs = 0; offs < size; offs += 2) {
+ int word = src[offs] + 256 * src[offs + 1];
+ if (offs % 16) {
+ LOG_DRIVE((0," %06o", word));
+ } else {
+ if (offs > 0)
+ dump_ascii(&src[offs-16], 16);
+ LOG_DRIVE((0,"\t%05o: %06o", (addr + offs) / 2, word));
+ }
+ }
+ if (offs % 16) {
+ dump_ascii(&src[offs - (offs % 16)], offs % 16);
+ } else {
+ dump_ascii(&src[offs-16], 16);
+ }
+ if (cr) {
+ LOG_DRIVE((0,"\n"));
+ }
+ return size;
+}
+#endif
+
+/**
+ * @brief find a sync bit in an array of clock and data bits
+ *
+ * @param bits pointer to the sector's bits
+ * @param src source index into bits (bit number)
+ * @param size number of words to scan for a sync word
+ * @return next source index for reading
+ */
+size_t diablo_hd_device::squeeze_sync(UINT32 *bits, size_t src, size_t size)
+{
+ UINT32 accu = 0;
+ /* hunt for the first 0x0001 word */
+ for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
+ /*
+ * accumulate clock and data bits until we are
+ * on the clock bit boundary
+ */
+ int bit;
+ src = RDBIT(bits,src,bit);
+ accu = (accu << 1) | bit;
+ /*
+ * look for 15 alternating clocks and 0-bits
+ * and the 16th clock with a 1-bit
+ */
+ if (accu == 0xaaaaaaab)
+ return src;
+ if (++bitcount == 32) {
+ bitcount = 0;
+ offs++;
+ }
+ }
+ /* return if no sync found within size*32 clock and data bits */
+ LOG_DRIVE((0,"[DHD%u] no sync within %d words\n", m_unit, size));
+ return src;
+}
+
+/**
+ * @brief find a 16 x 0 bits sequence in an array of clock and data bits
+ *
+ * @param bits pointer to the sector's bits
+ * @param src source index into bits (bit number)
+ * @param size number of words to scan for a sync word
+ * @return next source index for reading
+ */
+size_t diablo_hd_device::squeeze_unsync(UINT32 *bits, size_t src, size_t size)
+{
+ UINT32 accu = 0;
+ /* hunt for the first 0 word (16 x 0 bits) */
+ for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
+ /*
+ * accumulate clock and data bits until we are
+ * on the clock bit boundary
+ */
+ int bit;
+ src = RDBIT(bits,src,bit);
+ accu = (accu << 1) | bit;
+ /*
+ * look for 16 alternating clocks and 0 data bits
+ */
+ if (accu == 0xaaaaaaaa)
+ return src;
+ if (++bitcount == 32) {
+ bitcount = 0;
+ offs++;
+ }
+ }
+ /* return if no sync found within size*32 clock and data bits */
+ LOG_DRIVE((0,"[DHD%u] no unsync within %d words\n", m_unit, size));
+ return src;
+}
+
+/**
+ * @brief squeeze an array of clock and data bits into a sector's record
+ *
+ * @param bits pointer to the sector's bits
+ * @param src source index into bits (bit number)
+ * @param field pointer to the record data (bytes)
+ * @param size size of the record in bytes
+ * @return next source index for reading
+ */
+size_t diablo_hd_device::squeeze_record(UINT32 *bits, size_t src, UINT8 *field, size_t size)
+{
+ UINT32 accu = 0;
+ for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
+ int bit;
+ src = RDBIT(bits,src,bit); // skip clock
+ assert(bit == 1);
+ src = RDBIT(bits,src,bit); // get data bit
+ accu = (accu << 1) | bit;
+ bitcount += 2;
+ if (bitcount == 32) {
+ /* collected a word */
+ field[size - 2 - offs + 0] = accu % 256;
+ field[size - 2 - offs + 1] = accu / 256;
+ offs += 2;
+ bitcount = 0;
+ }
+ }
+ return src;
+}
+
+/**
+ * @brief squeeze an array of 32 clock and data bits into a checksum word
+ *
+ * @param bits pointer to the sector's bits
+ * @param src source index into bits (bit number)
+ * @param cksum pointer to an int to receive the checksum word
+ * @return next source index for reading
+ */
+size_t diablo_hd_device::squeeze_cksum(UINT32 *bits, size_t src, int *cksum)
+{
+ UINT32 accu = 0;
+
+ for (size_t bitcount = 0; bitcount < 32; bitcount += 2) {
+ int bit;
+ src = RDBIT(bits,src,bit); // skip clock
+ assert(bit == 1);
+ src = RDBIT(bits,src,bit); // get data bit
+ accu = (accu << 1) | bit;
+ }
+
+ /* set the cksum to the extracted word */
+ *cksum = accu;
+ return src;
+}
+
+/**
+ * @brief squeeze a array of clock and data bits into a sector's data
+ *
+ * Find and squeeze header, label and data fields and verify for
+ * zero checksums, starting with a value of 0521.
+ * Write the page back to the media and free the bitmap
+ */
+void diablo_hd_device::squeeze_sector()
+{
+ diablo_sector_t *s;
+ size_t src;
+ int cksum_header, cksum_label, cksum_data;
+
+ if (m_rdfirst >= 0) {
+ LOG_DRIVE((0, "[DHD%u] READ CHS:%03d/%d/%02d bit#%d ... bit#%d\n",
+ m_unit, m_cylinder, m_head, m_sector, m_rdfirst, m_rdlast));
+ }
+ m_rdfirst = -1;
+ m_rdlast = -1;
+
+ /* not written to, just drop it now */
+ if (m_wrfirst < 0) {
+ m_wrfirst = -1;
+ m_wrlast = -1;
+ return;
+ }
+
+ /* did write into the next sector (?) */
+ if (m_wrlast > m_wrfirst && m_wrlast < 256) {
+ m_wrfirst = -1;
+ m_wrlast = -1;
+ return;
+ }
+
+ if (m_wrfirst >= 0) {
+ LOG_DRIVE((0, "[DHD%u] WRITE CHS:%03d/%d/%02d bit#%d ... bit#%d\n",
+ m_unit, m_cylinder, m_head, m_sector, m_wrfirst, m_wrlast));
+ }
+ m_wrfirst = -1;
+ m_wrlast = -1;
+
+ if (m_page < 0 || m_page >= m_pages) {
+ LOG_DRIVE((0,"[DHD%u] page not set\n", m_unit));
+ return;
+ }
+
+ if (!m_cache[m_page]) {
+ LOG_DRIVE((0,"[DHD%u] no image\n", m_unit));
+ return;
+ }
+
+ /* no bits to write? */
+ if (!m_bits[m_page]) {
+ LOG_DRIVE((0,"[DHD%u] no bits\n", m_unit));
+ return;
+ }
+ UINT32 *bits = m_bits[m_page];
+
+ // pointer to sector buffer
+ s = reinterpret_cast<diablo_sector_t *>(m_cache[m_page]);
+
+ // zap the sector first
+ memset(s, 0, sizeof(*s));
+
+ src = MFRRDL * 32;
+ src = squeeze_unsync(bits, src, 40); // skip first words and garbage until 0 bits are coming in
+ src = squeeze_sync(bits, src, 40); // sync on header preamble
+ LOG_DRIVE((0,"[DHD%u] header sync bit #%5d\n", m_unit, src));
+ src = squeeze_record(bits, src, s->header, sizeof(s->header));
+ LOG_DRIVE((0,"[DHD%u] header CRC bit #%5d\n", m_unit, src));
+ src = squeeze_cksum(bits, src, &cksum_header);
+#if DIABLO_DEBUG
+ dump_record(s->header, 0, sizeof(s->header), "header", 0);
+#endif
+
+ src = squeeze_unsync(bits, src, 40); // skip garbage until 0 bits are coming in
+ src = squeeze_sync(bits, src, 40); // sync on label preamble
+ LOG_DRIVE((0,"[DHD%u] label sync bit #%5d\n", m_unit, src));
+ src = squeeze_record(bits, src, s->label, sizeof(s->label));
+ LOG_DRIVE((0,"[DHD%u] label CRC bit #%5d\n", m_unit, src));
+ src = squeeze_cksum(bits, src, &cksum_label);
+#if DIABLO_DEBUG
+ dump_record(s->label, 0, sizeof(s->label), "label", 0);
+#endif
+
+ src = squeeze_unsync(bits, src, 40); // skip garbage until 0 bits are coming in
+ src = squeeze_sync(bits, src, 40); // sync on data preamble
+ LOG_DRIVE((0,"[DHD%u] data sync bit #%5d\n", m_unit, src));
+ src = squeeze_record(bits, src, s->data, sizeof(s->data));
+ LOG_DRIVE((0,"[DHD%u] data CRC bit #%5d\n", m_unit, src));
+ src = squeeze_cksum(bits, src, &cksum_data);
+#if DIABLO_DEBUG
+ dump_record(s->data, 0, sizeof(s->data), "data", 1);
+#endif
+ LOG_DRIVE((0,"[DHD%u] postamble bit #%5d\n", m_unit, src));
+
+ /* The checksum start value always seems to be 0521 */
+ cksum_header ^= cksum(s->header, sizeof(s->header), 0521);
+ cksum_label ^= cksum(s->label, sizeof(s->label), 0521);
+ cksum_data ^= cksum(s->data, sizeof(s->data), 0521);
+
+ if (cksum_header || cksum_label || cksum_data) {
+#if DIABLO_DEBUG
+ LOG_DRIVE((0,"[DHD%u] cksum check - header:%06o label:%06o data:%06o\n", m_unit, cksum_header, cksum_label, cksum_data));
+#endif
+ }
+ auto_free(machine(), m_bits[m_page]);
+ m_bits[m_page] = 0;
+
+ if (m_disk) {
+ if (!hard_disk_write(m_disk, m_page, m_cache[m_page])) {
+ LOG_DRIVE((0,"[DHD%u] write failed for page #%d\n", m_unit, m_page));
+ }
+ } else {
+ LOG_DRIVE((2,"[DHD%u] no disk\n", m_unit));
+ }
+}
+
+/**
+ * @brief return number of bit clocks for a sector (clock and data)
+ * @return number of bitclks for a sector
+ */
+int diablo_hd_device::bits_per_sector() const
+{
+ return m_diablo31 ? DIABLO31_SECTOR_BITS : DIABLO44_SECTOR_BITS;
+}
+
+/**
+ * @brief return a pointer to a drive's description
+ * @return a pointer to the string description
+ */
+const char* diablo_hd_device::description() const
+{
+ return m_description;
+}
+
+/**
+ * @brief return the number of a drive unit
+ * @return the unit number of this instance
+ */
+int diablo_hd_device::unit() const
+{
+ return m_unit;
+}
+
+/**
+ * @brief return the time for a full rotation
+ * @return the time for a full track rotation in atto seconds
+ */
+attotime diablo_hd_device::rotation_time() const
+{
+ return m_rotation_time;
+}
+
+/**
+ * @brief return the time for a sector
+ * @return the time for a sector in atto seconds
+ */
+attotime diablo_hd_device::sector_time() const
+{
+ return m_sector_time;
+}
+
+/**
+ * @brief return the time for a data bit
+ * @return the time in atto seconds per bit clock
+ */
+attotime diablo_hd_device::bit_time() const
+{
+ return m_bit_time;
+}
+
+/**
+ * @brief return the seek/read/write status of a drive
+ * @return the seek/read/write status for the drive unit (0:active 1:inactive)
+ */
+int diablo_hd_device::get_seek_read_write_0() const
+{
+ return m_s_r_w_0;
+}
+
+/**
+ * @brief return the ready status of a drive
+ * @return the ready status for the drive unit (0:ready 1:not ready)
+ */
+int diablo_hd_device::get_ready_0() const
+{
+ return m_ready_0;
+}
+
+/**
+ * @brief return the current sector mark status of a drive
+ *
+ * The sector mark is derived from the offset into the current sector.
+ * It is deasserted except for a short time (a few micro seconds)
+ * around each new sector.
+ *
+ * @return the current sector mark for the drive (0:active 1:inactive)
+ */
+int diablo_hd_device::get_sector_mark_0() const
+{
+ /* no sector marks while seeking (?) */
+ if (m_s_r_w_0)
+ return 1;
+
+ /* return the sector mark */
+ return m_sector_mark_0;
+}
+
+/**
+ * @brief return the address acknowledge state
+ * @return address acknowledge state (0:active 1:inactive)
+ */
+int diablo_hd_device::get_addx_acknowledge_0() const
+{
+ return m_addx_acknowledge_0;
+}
+
+/**
+ * @brief return the log address interlock state
+ * @return log address interlock state (0:active 1:inactive)
+ */
+int diablo_hd_device::get_log_addx_interlock_0() const
+{
+ return m_log_addx_interlock_0;
+}
+
+/**
+ * @brief return the seek incomplete state
+ * @return address acknowledge state (0:active 1:inactive)
+ */
+int diablo_hd_device::get_seek_incomplete_0() const
+{
+ return m_seek_incomplete_0;
+}
+
+/**
+ * @brief return the current cylinder of a drive unit
+ *
+ * This is a convenience function.
+ * There is no such signal on the BUS.
+ *
+ * Note: The bus lines are active low
+ * The value on the BUS needs an XOR with DIABLO_CYLINDER_MASK
+ * to resemble the physical line levels.
+ *
+ * @return current cylinder number for the drive
+ */
+int diablo_hd_device::get_cylinder() const
+{
+ return m_cylinder;
+}
+
+/**
+ * @brief return the current head of a drive unit
+ *
+ * This is a convenience function.
+ * There is no such signal on the BUS.
+ *
+ * Note: The bus lines are active low
+ * The value on the BUS needs an XOR with DIABLO_HEAD_MASK
+ * to resemble the physical line levels.
+ *
+ * @return currently selected head for the drive
+ */
+int diablo_hd_device::get_head() const
+{
+ return m_head;
+}
+
+/**
+ * @brief return the current sector of a drive unit
+ *
+ * The current sector number is derived from the time since the
+ * most recent track rotation started.
+ * It counts modulo DIABLO_SPT (12).
+ *
+ * Note: The bus lines are active low
+ * The value on the BUS needs an XOR with DIABLO_SECTOR_MASK
+ * to resemble the physical line levels.
+ *
+ * @return current sector for the drive
+ */
+int diablo_hd_device::get_sector() const
+{
+ return m_sector;
+}
+
+/**
+ * @brief return the current page of a drive unit
+ *
+ * This is a convenience function.
+ * There is no such signal on the BUS.
+ *
+ * The current page number is derived from the cylinder,
+ * head, and sector numbers.
+ *
+ * @return the current page for the drive
+ */
+int diablo_hd_device::get_page() const
+{
+ return m_page;
+}
+
+/**
+ * @brief select a drive unit
+ *
+ * Selecting a drive unit updates the ready status
+ *
+ * @param unit unit number
+ */
+void diablo_hd_device::select(int unit)
+{
+ assert(unit == m_unit); // this drive is selected
+
+ if (m_disk) {
+ m_ready_0 = 0; // it is ready
+ m_s_r_w_0 = 0; // and can take seek/read/write commands
+ m_addx_acknowledge_0 = 0; // assert address acknowledge (?)
+ m_log_addx_interlock_0 = 1; // deassert log address interlock (?)
+ LOG_DRIVE((1,"[DHD%u] select unit:%d ready\n", m_unit, unit));
+ read_sector();
+ } else {
+ m_ready_0 = 1; // it is not ready (?)
+ m_s_r_w_0 = 1; // can't take seek/read/write commands (?)
+ m_addx_acknowledge_0 = 0; // assert address acknowledge (?)
+ m_log_addx_interlock_0 = 1; // deassert log address interlock (?)
+ LOG_DRIVE((1,"[DHD%u] select unit:%d not ready (no image)\n", m_unit, unit));
+ }
+}
+
+/**
+ * @brief set the selected head
+ * @param head head number
+ */
+void diablo_hd_device::set_head(int head)
+{
+ if ((head & DIABLO_HEAD_MASK) != m_head) {
+ m_head = head & DIABLO_HEAD_MASK;
+ LOG_DRIVE((0,"[DHD%u] select head:%d\n", m_unit, m_head));
+ }
+}
+
+/**
+ * @brief set the cylinder number to seek to
+ *
+ * This defines the cylinder to seek when the
+ * STROBE line is pulsed.
+ *
+ * @param cylinder cylinder number (bus lines CYL[0-9])
+ */
+void diablo_hd_device::set_cylinder(int cylinder)
+{
+ if ((cylinder & DIABLO_CYLINDER_MASK) != m_seekto) {
+ m_seekto = cylinder & DIABLO_CYLINDER_MASK;
+ LOG_DRIVE((0,"[DHD%u] seek to cylinder:%d\n", m_unit, m_seekto));
+ }
+}
+
+/**
+ * @brief set the restore line
+ *
+ * If the restore line is asserted when the
+ * STROBE line is pulsed, the drive seeks
+ * towards cylinder 0.
+ *
+ * @param restore state of the restore line
+ */
+void diablo_hd_device::set_restore(int restore)
+{
+ if ((restore & 1) != m_restore) {
+ m_restore = restore & 1;
+ LOG_DRIVE((0,"[DHD%u] restore:%d\n", m_unit, m_restore));
+ }
+}
+
+/**
+ * @brief strobe a seek operation
+ *
+ * Seek to the specified cylinder m_seekto,
+ * or restore to cylinder 0, if m_restore is set.
+ *
+ * @param strobe current level of the strobe signal (for edge detection)
+ */
+void diablo_hd_device::set_strobe(int strobe)
+{
+ int seekto = m_restore ? 0 : m_seekto;
+ if (strobe) {
+ LOG_DRIVE((1,"[DHD%u] STROBE end of interlock\n", m_unit));
+ // deassert the log address interlock
+ m_log_addx_interlock_0 = 1;
+ return;
+ }
+
+ // assert the log address interlock
+ m_log_addx_interlock_0 = 0;
+
+ if (seekto == m_cylinder) {
+ LOG_DRIVE((1,"[DHD%u] STROBE to cylinder %d acknowledge\n", m_unit, seekto));
+ m_addx_acknowledge_0 = 0; // address acknowledge, if cylinder is reached
+ m_seek_incomplete_0 = 1; // reset seek incomplete
+ return;
+ }
+ // assert the seek-read-write signal
+ m_s_r_w_0 = 0;
+
+ bool complete = true;
+ if (seekto < m_cylinder) {
+ m_cylinder--; // previous cylinder
+ if (m_cylinder < 0) {
+ m_cylinder = 0;
+ complete = false;
+ }
+ }
+ if (seekto > m_cylinder) {
+ /* increment cylinder */
+ m_cylinder++;
+ if (m_cylinder >= m_cylinders) {
+ m_cylinder = m_cylinders - 1;
+ complete = false;
+ }
+ }
+ if (complete) {
+ LOG_DRIVE((1,"[DHD%u] STROBE to cylinder %d (now %d) - interlock\n", m_unit, seekto, m_cylinder));
+ m_addx_acknowledge_0 = 1; // deassert address acknowledge signal
+ m_seek_incomplete_0 = 1; // deassert seek incomplete signal
+ read_sector();
+ } else {
+ m_log_addx_interlock_0 = 0; // deassert the log address interlock signal
+ m_seek_incomplete_0 = 1; // deassert seek incomplete signal
+ m_addx_acknowledge_0 = 0; // assert address acknowledge signal
+ LOG_DRIVE((1,"[DHD%u] STROBE to cylinder %d incomplete\n", m_unit, seekto));
+ }
+}
+
+/**
+ * @brief set the drive erase gate
+ * @param gate value of erase gate
+ */
+void diablo_hd_device::set_egate(int gate)
+{
+ m_egate_0 = gate & 1;
+}
+
+/**
+ * @brief set the drive write gate
+ * @param gate value of write gate
+ */
+void diablo_hd_device::set_wrgate(int gate)
+{
+ m_wrgate_0 = gate & 1;
+}
+
+/**
+ * @brief set the drive read gate
+ * @param gate value of read gate
+ */
+void diablo_hd_device::set_rdgate(int gate)
+{
+ m_rdgate_0 = gate & 1;
+}
+
+/**
+ * @brief write the sector relative bit at index
+ *
+ * The disk controller writes a combined clock and data pulse to one output
+ * <PRE>
+ * Encoding of binary 01011
+ *
+ * clk data clk data clk data clk data clk data
+ * 0 1 2 3 4 5 6 7 8 9
+ * +--+ +--+ +--+ +--+ +--+ +--+ +--+ +--+ +--
+ * | | | | | | | | | | | | | | | | |
+ * --+ +--------+ +--+ +--+ +--------+ +--+ +--+ +--+ +--+
+ * </PRE>
+ *
+ * @param index relative index of bit/clock into sector
+ * @param wrdata write data clock or bit
+ */
+void diablo_hd_device::wr_data(int index, int wrdata)
+{
+ if (m_wrgate_0) {
+ LOG_DRIVE((0,"[DHD%u] index=%d wrgate not asserted\n", m_unit, index));
+ return; // write gate is not asserted (active 0)
+ }
+
+ if (index < 0 || index >= bits_per_sector()) {
+ LOG_DRIVE((0,"[DHD%u] index=%d out of range\n", m_unit, index));
+ return; // don't write before or beyond the sector
+ }
+
+ if (-1 == m_page) {
+ LOG_DRIVE((0,"[DHD%u] invalid page\n", m_unit));
+ return; // invalid page
+ }
+
+ UINT32 *bits = expand_sector();
+ if (!bits) {
+ LOG_DRIVE((0,"[DHD%u] no bits\n", m_unit));
+ return; // invalid unit
+ }
+
+ if (-1 == m_wrfirst)
+ m_wrfirst = index;
+
+ LOG_DRIVE((9,"[DHD%u] CHS:%03d/%d/%02d index #%d bit:%d\n", m_unit, m_cylinder, m_head, m_sector, index, wrdata));
+
+ if (index < GUARD_ZONE_BITS) {
+ /* don't write in the guard zone (?) */
+ } else {
+ WRBIT(bits,index,wrdata);
+ }
+ m_wrlast = index;
+}
+
+/**
+ * @brief read the sector relative bit at index
+ *
+ * Note: this is a gross hack to allow the controller pulling bits
+ * at its will, rather than clocking them with the drive's RDCLK-
+ *
+ * @param index is the sector relative bit index
+ * @return returns the sector's bit by index
+ */
+int diablo_hd_device::rd_data(int index)
+{
+ int bit = 0;
+
+ if (m_rdgate_0) {
+ LOG_DRIVE((1,"[DHD%u] index=%d rdgate not asserted\n", m_unit, index));
+ return 0; // read gate is not asserted (active 0)
+ }
+
+ if (index < 0 || index >= bits_per_sector()) {
+ LOG_DRIVE((0,"[DHD%u] index=%d out of range\n", m_unit, index));
+ return 1; // don't read before or beyond the sector
+ }
+
+ if (0 == m_sector_mark_0) {
+ LOG_DRIVE((0,"[DHD%u] read while sector mark is asserted\n", m_unit));
+ return 1; // no data while sector mark is asserted
+ }
+
+ if (-1 == m_page) {
+ LOG_DRIVE((0,"[DHD%u] invalid page\n", m_unit));
+ return 1; // invalid unit
+ }
+
+ UINT32 *bits = expand_sector();
+ if (!bits) {
+ LOG_DRIVE((0,"[DHD%u] no bits\n", m_unit));
+ return 1; // invalid page
+ }
+
+ if (-1 == m_rdfirst)
+ m_rdfirst = index;
+
+ RDBIT(bits,index,bit);
+ LOG_DRIVE((9,"[DHD%u] CHS:%03d/%d/%02d index #%d bit:%d\n", m_unit, m_cylinder, m_head, m_sector, index, bit));
+ m_rdlast = index;
+ return bit;
+}
+
+/**
+ * @brief get the sector relative clock at index
+ *
+ * Note: this is a gross hack to allow the controller pulling bits
+ * at its will, rather than clocking them with the drive's RDCLK-
+ *
+ * @param index is the sector relative bit index
+ * @return returns the sector's clock bit by index
+ */
+int diablo_hd_device::rd_clock(int index)
+{
+ int clk = 0;
+
+ if (index < 0 || index >= bits_per_sector()) {
+ LOG_DRIVE((0,"[DHD%u] index out of range (%d)\n", m_unit, index));
+ return 1; // don't read before or beyond the sector
+ }
+
+ if (0 == m_sector_mark_0) {
+ LOG_DRIVE((0,"[DHD%u] read while sector mark is asserted\n", m_unit));
+ return 1; // no clock while sector mark is low (?)
+ }
+
+ if (-1 == m_page) {
+ LOG_DRIVE((0,"[DHD%u] invalid page\n", m_unit));
+ return 1; // invalid page
+ }
+
+ UINT32 *bits = expand_sector();
+ if (!bits) {
+ LOG_DRIVE((0,"[DHD%u] no bits\n", m_unit));
+ return 1; // invalid unit
+ }
+
+ if (-1 == m_rdfirst)
+ m_rdfirst = index;
+
+ if (index & 1) {
+ // clock bits are on even bit positions only
+ clk = 0;
+ } else if (bits) {
+ RDBIT(bits,index,clk);
+ } else {
+ clk = 0;
+ }
+ LOG_DRIVE((9,"[DHD%u] CHS:%03d/%d/%02d index #%d clk:%d\n", m_unit, m_cylinder, m_head, m_sector, index, clk));
+ m_rdlast = index;
+ return clk ^ 1;
+}
+
+/**
+ * @brief deassert the sector mark
+ *
+ */
+void diablo_hd_device::sector_mark_1()
+{
+ LOG_DRIVE((9,"[DHD%u] CHS:%03d/%d/%02d sector_mark_0=1\n", m_unit, m_cylinder, m_head, m_sector));
+ m_sector_mark_0 = 1; // deassert sector mark (set to 1)
+}
+
+/**
+ * @brief assert the sector mark and read the next sector
+ *
+ * Assert the sector mark and reset the read and write
+ * first and last bit indices.
+ * Increment the sector number, wrap and read the
+ * next sector from the media.
+ */
+void diablo_hd_device::sector_mark_0()
+{
+ LOG_DRIVE((9,"[DHD%u] CHS:%03d/%d/%02d sector_mark_0=0\n", m_unit, m_cylinder, m_head, m_sector));
+
+ // HACK: deassert wrgate
+ // m_wrgate_0 = 1;
+
+ squeeze_sector(); // squeeze previous sector bits, if it was written to
+ m_sector_mark_0 = 0; // assert sector mark (set to 0)
+ // reset read and write bit locations
+ m_rdfirst = -1;
+ m_rdlast = -1;
+ m_wrfirst = -1;
+ m_wrlast = -1;
+
+ // count up the sector number
+ m_sector = (m_sector + 1) % DIABLO_SPT;
+ read_sector();
+}
+
+void diablo_hd_device::device_start()
+{
+ m_image = static_cast<diablo_image_device *>(subdevice("drive"));
+
+ m_packs = 1; // FIXME: get from configuration?
+ m_unit = strstr(m_image->tag(), "diablo0") ? 0 : 1;
+ m_timer = timer_alloc(1, 0);
+}
+
+void diablo_hd_device::device_reset()
+{
+ // free previous page cache
+ if (m_cache) {
+ for (int page = 0; page < m_pages; page++)
+ if (m_cache[page])
+ auto_free(machine(), m_cache[page]);
+ auto_free(machine(), m_cache);
+ m_cache = 0;
+ }
+ // free previous bits cache
+ if (m_bits) {
+ for (int page = 0; page < m_pages; page++)
+ if (m_bits[page])
+ auto_free(machine(), m_bits[page]);
+ auto_free(machine(), m_bits);
+ m_bits = 0;
+ }
+ m_handle = m_image->get_chd_file();
+ m_diablo31 = true; // FIXME: get from m_handle meta data?
+ m_disk = m_image->get_hard_disk_file();
+ if (m_diablo31) {
+ snprintf(m_description, sizeof(m_description), "DIABLO31");
+ m_rotation_time = DIABLO31_ROTATION_TIME;
+ m_sector_time = DIABLO31_ROTATION_TIME / DIABLO_SPT;
+ m_sector_mark_0_time = DIABLO31_SECTOR_MARK_PULSE_PRE;
+ m_sector_mark_1_time = DIABLO31_SECTOR_MARK_PULSE_PRE;
+ m_bit_time = DIABLO31_BIT_TIME(1);
+ m_cylinders = DIABLO_CYLINDERS;
+ m_pages = DIABLO_PAGES;
+ } else {
+ snprintf(m_description, sizeof(m_description), "DIABLO44");
+ m_rotation_time = DIABLO44_ROTATION_TIME;
+ m_sector_time = DIABLO44_ROTATION_TIME / DIABLO_SPT;
+ m_sector_mark_0_time = DIABLO44_SECTOR_MARK_PULSE_PRE;
+ m_sector_mark_1_time = DIABLO44_SECTOR_MARK_PULSE_PRE;
+ m_bit_time = DIABLO44_BIT_TIME(1);
+ m_cylinders = 2 * DIABLO_CYLINDERS;
+ m_pages = 2 * DIABLO_PAGES;
+ }
+ LOG_DRIVE((0,"[DHD%u] m_handle : %p\n", m_unit, m_handle));
+ LOG_DRIVE((0,"[DHD%u] m_disk : %p\n", m_unit, m_disk));
+ LOG_DRIVE((0,"[DHD%u] rotation time : %.0fns\n", m_unit, m_rotation_time.as_double() * ATTOSECONDS_PER_NANOSECOND));
+ LOG_DRIVE((0,"[DHD%u] sector time : %.0fns\n", m_unit, m_sector_time.as_double() * ATTOSECONDS_PER_NANOSECOND));
+ LOG_DRIVE((0,"[DHD%u] sector mark 0 time : %.0fns\n", m_unit, m_sector_mark_0_time.as_double() * ATTOSECONDS_PER_NANOSECOND));
+ LOG_DRIVE((0,"[DHD%u] sector mark 1 time : %.0fns\n", m_unit, m_sector_mark_1_time.as_double() * ATTOSECONDS_PER_NANOSECOND));
+ LOG_DRIVE((0,"[DHD%u] bit time : %.0fns\n", m_unit, m_bit_time.as_double() * ATTOSECONDS_PER_NANOSECOND));
+
+ m_s_r_w_0 = 1; // deassert seek/read/write ready
+ m_ready_0 = 1; // deassert drive ready
+ m_sector_mark_0 = 1; // deassert sector mark
+ m_addx_acknowledge_0 = 1; // deassert drive address acknowledge
+ m_log_addx_interlock_0 = 1; // deassert drive log address interlock
+ m_seek_incomplete_0 = 1; // deassert drive seek incomplete
+
+ // reset the disk drive's strobe info
+ m_seekto = 0;
+ m_restore = 0;
+ // reset the disk drive's address
+ m_cylinder = 0;
+ m_head = 0;
+ m_sector = 0;
+ m_page = 0;
+
+ // disable the erase, write and read gates
+ m_egate_0 = 1;
+ m_wrgate_0 = 1;
+ m_rdgate_0 = 1;
+
+ // reset read and write first and last indices
+ m_wrfirst = -1;
+ m_wrlast = -1;
+ m_rdfirst = -1;
+ m_rdlast = -1;
+
+ if (!m_handle)
+ return;
+ // for units with a CHD assigned to them start the timer
+ m_cache = auto_alloc_array_clear(machine(), UINT8*, m_pages);
+ m_bits = auto_alloc_array_clear(machine(), UINT32*, m_pages);
+ timer_set(m_sector_time - m_sector_mark_0_time, 1, 0);
+ read_sector();
+}
+
+/**
+ * @brief timer callback that is called thrice per sector in the rotation
+ *
+ * The timer is called three times at the events:
+ * 0: sector mark goes active
+ * 1: sector mark goes inactive
+ * 2: in the middle of the active phase
+ *
+ * @param id timer id
+ * @param arg argument supplied to timer_insert (unused)
+ */
+void diablo_hd_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ LOG_DRIVE((9,"[DHD%u] TIMER id=%d param=%d ptr=%p @%.0fns\n", m_unit, id, param, ptr, timer.elapsed().as_double() * ATTOSECONDS_PER_NANOSECOND));
+ if (!m_disk)
+ return;
+
+ switch (param) {
+ case 0:
+ // assert sector mark
+ sector_mark_0();
+ // next sector timer event is in the middle between sector_mark going 0 and back to 1
+ timer.adjust(m_sector_mark_0_time, 1);
+ break;
+ case 1:
+ /* call the sector_callback, if any */
+ if (m_sector_callback)
+ (void)(*m_sector_callback)(m_sector_callback_cookie, m_unit);
+ // next sector timer event is deassert of sector_mark_0 (set to 1)
+ timer.adjust(m_sector_mark_1_time, 2);
+ break;
+ case 2:
+ // deassert sector mark
+ sector_mark_1();
+ // next sector timer event is sector_mark_0 for next sector
+ timer.adjust(m_sector_time - m_sector_mark_0_time, 0);
+ break;
+ }
+}
+
+MACHINE_CONFIG_FRAGMENT( diablo_drive )
+ MCFG_DIABLO_ADD("drive")
+MACHINE_CONFIG_END
+
+machine_config_constructor diablo_hd_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( diablo_drive );
+}
+
+const device_type DIABLO_HD = &device_creator<diablo_hd_device>;
diff --git a/src/emu/machine/diablo_hd.h b/src/emu/machine/diablo_hd.h
new file mode 100644
index 00000000000..0de3970b5c9
--- /dev/null
+++ b/src/emu/machine/diablo_hd.h
@@ -0,0 +1,180 @@
+/**********************************************************
+ * DIABLO31 and DIABLO44 hard drive support
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ **********************************************************/
+
+#if !defined(_DIABLO_HD_DEVICE_)
+#define _DIABLO_HD_DEVICE_
+
+#include "emu.h"
+#include "imagedev/diablo.h"
+
+#ifndef DIABLO_DEBUG
+#define DIABLO_DEBUG 1 //!< set to 1 to enable debug log output
+#endif
+
+#define DIABLO_HD_0 "diablo0"
+#define DIABLO_HD_1 "diablo1"
+
+extern const device_type DIABLO_HD;
+
+class diablo_hd_device : public device_t
+{
+public:
+ diablo_hd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~diablo_hd_device();
+
+ static const int DIABLO_UNIT_MAX = 2; //!< max number of drive units
+ static const int DIABLO_CYLINDERS = 203; //!< number of cylinders per drive
+ static const int DIABLO_CYLINDER_MASK = 0777; //!< bit maks for cylinder number (9 bits)
+ static const int DIABLO_SPT = 12; //!< number of sectors per track
+ static const int DIABLO_SECTOR_MASK = 017; //!< bit maks for sector number (4 bits)
+ static const int DIABLO_HEADS = 2; //!< number of heads per drive
+ static const int DIABLO_HEAD_MASK = 1; //!< bit maks for head number (1 bit)
+ static const int DIABLO_PAGES = 203*2*12; //!< number of pages per drive
+ //! convert a cylinder/head/sector to a logical block address (page)
+ static inline int DIABLO_PAGE(int c, int h, int s) { return (c * DIABLO_HEADS + h) * DIABLO_SPT + s; }
+
+ void set_sector_callback(void* cookie, void(*callback)(void*, int));
+
+ int bits_per_sector() const;
+ const char* description() const;
+ int unit() const;
+ attotime rotation_time() const;
+ attotime sector_time() const;
+ attotime bit_time() const;
+
+ int get_seek_read_write_0() const;
+ int get_ready_0() const;
+ int get_sector_mark_0() const;
+ int get_addx_acknowledge_0() const;
+ int get_log_addx_interlock_0() const;
+ int get_seek_incomplete_0() const;
+ int get_cylinder() const;
+ int get_head() const;
+ int get_sector() const;
+ int get_page() const;
+ void select(int unit);
+ void set_head(int head);
+ void set_cylinder(int cylinder);
+ void set_restore(int restore);
+ void set_strobe(int strobe);
+ void set_egate(int gate);
+ void set_wrgate(int gate);
+ void set_rdgate(int gate);
+ void wr_data(int index, int wrdata);
+ int rd_data(int index);
+ int rd_clock(int index);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+private:
+#if DIABLO_DEBUG
+ int m_log_level;
+ void logprintf(int level, const char* format, ...);
+# define LOG_DRIVE(x) logprintf x
+
+#else
+# define LOG_DRIVE(x)
+#endif
+ bool m_diablo31; //!< true, if this is a DIABLO31 drive
+ int m_unit; //!< drive unit number (0 or 1)
+ char m_description[32]; //!< description of the drive(s)
+ int m_packs; //!< number of packs in drive (1 or 2)
+ attotime m_rotation_time; //!< rotation time in atto seconds
+ attotime m_sector_time; //!< sector time in atto seconds
+ attotime m_sector_mark_0_time; //!< sector mark going 0 before sector pulse time
+ attotime m_sector_mark_1_time; //!< sector mark going 1 after sector pulse time
+ attotime m_bit_time; //!< bit time in atto seconds
+ int m_s_r_w_0; //!< drive seek/read/write signal (active 0)
+ int m_ready_0; //!< drive ready signal (active 0)
+ int m_sector_mark_0; //!< sector mark (0 if new sector)
+ int m_addx_acknowledge_0; //!< address acknowledge, i.e. seek successful (active 0)
+ int m_log_addx_interlock_0; //!< log address interlock, i.e. seek in progress (active 0)
+ int m_seek_incomplete_0; //!< seek incomplete, i.e. seek in progress (active 0)
+ int m_egate_0; //!< erase gate
+ int m_wrgate_0; //!< write gate
+ int m_rdgate_0; //!< read gate
+ int m_cylinders; //!< total number of cylinders
+ int m_pages; //!< total number of pages
+ int m_seekto; //!< seek to cylinder number
+ int m_restore; //!< restore to cylinder 0 flag
+ int m_cylinder; //!< current cylinder number
+ int m_head; //!< current head (track) number on cylinder
+ int m_sector; //!< current sector number in track
+ int m_page; //!< current page (derived from cylinder, head and sector)
+ UINT8** m_cache; //!< pages raw bytes
+ UINT32** m_bits; //!< pages expanded to bits
+ int m_rdfirst; //!< set to first bit of a sector that is read from
+ int m_rdlast; //!< set to last bit of a sector that was read from
+ int m_wrfirst; //!< set to non-zero if a sector is written to
+ int m_wrlast; //!< set to last bit of a sector that was written to
+ void *m_sector_callback_cookie; //!< cookie to pass to callback
+ void (*m_sector_callback)(void*,int); //!< callback to call at the start of each sector
+ emu_timer* m_timer; //!< sector timer
+ diablo_image_device* m_image; //!< diablo_image_device interfacing the CHD
+ chd_file* m_handle; //!< underlying CHD handle
+ hard_disk_file* m_disk; //!< underlying hard disk file
+
+ //! translate C/H/S to a page and read the sector
+ void read_sector();
+
+ //! compute the checksum of a record
+ int cksum(UINT8 *src, size_t size, int start);
+
+ //! expand a series of clock bits and 0 data bits
+ size_t expand_zeroes(UINT32 *bits, size_t dst, size_t size);
+
+ //! expand a series of 0 words and write a final sync bit
+ size_t expand_sync(UINT32 *bits, size_t dst, size_t size);
+
+ //! expand a record of words into a array of bits at dst
+ size_t expand_record(UINT32 *bits, size_t dst, UINT8 *field, size_t size);
+
+ //! expand a record's checksum word to 32 bits
+ size_t expand_cksum(UINT32 *bits, size_t dst, UINT8 *field, size_t size);
+
+ //! expand a sector into an array of clock and data bits
+ UINT32* expand_sector();
+
+#if DIABLO_DEBUG
+ //! dump a number of words as ASCII characters
+ void dump_ascii(UINT8 *src, size_t size);
+
+ //! dump a record's contents
+ size_t dump_record(UINT8 *src, size_t addr, size_t size, const char *name, int cr);
+#endif
+
+ //! find a sync bit in an array of clock and data bits
+ size_t squeeze_sync(UINT32 *bits, size_t src, size_t size);
+
+ //! find a 16 x 0 bits sequence in an array of clock and data bits
+ size_t squeeze_unsync(UINT32 *bits, size_t src, size_t size);
+
+ //! squeeze an array of clock and data bits into a sector's record
+ size_t squeeze_record(UINT32 *bits, size_t src, UINT8 *field, size_t size);
+
+ //! squeeze an array of 32 clock and data bits into a checksum word
+ size_t squeeze_cksum(UINT32 *bits, size_t src, int *cksum);
+
+ //! squeeze a array of clock and data bits into a sector's data
+ void squeeze_sector();
+
+ //! deassert the sector mark
+ void sector_mark_1();
+
+ //! assert the sector mark and read the next sector
+ void sector_mark_0();
+};
+
+#define MCFG_DIABLO_DRIVES_ADD() \
+ MCFG_DEVICE_ADD(DIABLO_HD_0, DIABLO_HD, ATTOSECONDS_TO_HZ(attotime::from_nsec(300).as_double())) \
+ MCFG_DEVICE_ADD(DIABLO_HD_1, DIABLO_HD, ATTOSECONDS_TO_HZ(attotime::from_nsec(300).as_double()))
+#endif // !defined(_DIABLO_HD_DEVICE_)
diff --git a/src/emu/machine/machine.mak b/src/emu/machine/machine.mak
index ac8f2b7d790..a34c90aa97e 100644
--- a/src/emu/machine/machine.mak
+++ b/src/emu/machine/machine.mak
@@ -1829,3 +1829,11 @@ ifneq ($(filter WOZFDC,$(MACHINES)),)
MACHINEOBJS += $(MACHINEOBJ)/wozfdc.o
endif
+#-------------------------------------------------
+#
+#@src/emu/machine/diablo_hd.h,MACHINES += DIABLO_HD
+#-------------------------------------------------
+ifneq ($(filter DIABLO_HD,$(MACHINES)),)
+MACHINEOBJS += $(MACHINEOBJ)/diablo_hd.o
+endif
+
diff --git a/src/emu/mame.c b/src/emu/mame.c
index c54d578ebea..72430034ed9 100644
--- a/src/emu/mame.c
+++ b/src/emu/mame.c
@@ -219,8 +219,19 @@ void CLIB_DECL logerror(const char *format, ...)
{
va_list arg;
va_start(arg, format);
+ vlogerror(format, arg);
+ va_end(arg);
+}
+
+
+/*-------------------------------------------------
+ vlogerror - log to the debugger and any other
+ OSD-defined output streams
+-------------------------------------------------*/
+
+void CLIB_DECL vlogerror(const char *format, va_list arg)
+{
if (global_machine != NULL)
global_machine->vlogerror(format, arg);
- va_end(arg);
}
diff --git a/src/emu/mame.h b/src/emu/mame.h
index 9bc19d29e6d..b7cdb7941d4 100644
--- a/src/emu/mame.h
+++ b/src/emu/mame.h
@@ -101,6 +101,7 @@ void CLIB_DECL popmessage(const char *format, ...) ATTR_PRINTF(1,2);
// log to the standard error.log file
void CLIB_DECL logerror(const char *format, ...) ATTR_PRINTF(1,2);
+void CLIB_DECL vlogerror(const char *format, va_list arg);
#endif /* __MAME_H__ */
diff --git a/src/mess/drivers/alto2.c b/src/mess/drivers/alto2.c
new file mode 100644
index 00000000000..85792cd38c6
--- /dev/null
+++ b/src/mess/drivers/alto2.c
@@ -0,0 +1,304 @@
+/***************************************************************************
+ * Xerox AltoII driver for MESS
+ *
+ * Copyright Juergen Buchmueller <pullmoll@t-online.de>
+ *
+ * Licenses: MAME, GPLv2
+ ***************************************************************************/
+
+#include "emu.h"
+#include "rendlay.h"
+#include "cpu/alto2/alto2cpu.h"
+#include "machine/diablo_hd.h"
+
+class alto2_state : public driver_device
+{
+public:
+ alto2_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_io_row0(*this, "ROW0"),
+ m_io_row1(*this, "ROW1"),
+ m_io_row2(*this, "ROW2"),
+ m_io_row3(*this, "ROW3"),
+ m_io_row4(*this, "ROW4"),
+ m_io_row5(*this, "ROW5"),
+ m_io_row6(*this, "ROW6"),
+ m_io_row7(*this, "ROW7"),
+ m_io_config(*this, "CONFIG")
+ { }
+
+ DECLARE_DRIVER_INIT(alto2);
+ DECLARE_MACHINE_RESET(alto2);
+
+protected:
+ required_device<cpu_device> m_maincpu;
+ required_ioport m_io_row0;
+ required_ioport m_io_row1;
+ required_ioport m_io_row2;
+ required_ioport m_io_row3;
+ required_ioport m_io_row4;
+ required_ioport m_io_row5;
+ required_ioport m_io_row6;
+ required_ioport m_io_row7;
+ optional_ioport m_io_config;
+};
+
+/* Input Ports */
+
+#define PORT_KEY(_bit,_code,_char1,_char2,_name) \
+ PORT_BIT(_bit, IP_ACTIVE_LOW, IPT_KEYBOARD) \
+ PORT_CODE(_code) PORT_NAME(_name) \
+ PORT_CHAR(_char1) PORT_CHAR(_char2)
+#define SPACING " "
+
+static INPUT_PORTS_START( alto2 )
+ PORT_START("ROW0")
+ PORT_KEY(A2_KEY_5, KEYCODE_5, '5', '%', "5" SPACING "%") //!< normal: 5 shifted: %
+ PORT_KEY(A2_KEY_4, KEYCODE_4, '4', '$', "4" SPACING "$") //!< normal: 4 shifted: $
+ PORT_KEY(A2_KEY_6, KEYCODE_6, '6', '~', "6" SPACING "~") //!< normal: 6 shifted: ~
+ PORT_KEY(A2_KEY_E, KEYCODE_E, 'e', 'E', "e" SPACING "E") //!< normal: e shifted: E
+ PORT_KEY(A2_KEY_7, KEYCODE_7, '7', '&', "7" SPACING "&") //!< normal: 7 shifted: &
+ PORT_KEY(A2_KEY_D, KEYCODE_D, 'd', 'D', "d" SPACING "D") //!< normal: d shifted: D
+ PORT_KEY(A2_KEY_U, KEYCODE_U, 'u', 'U', "u" SPACING "U") //!< normal: u shifted: U
+ PORT_KEY(A2_KEY_V, KEYCODE_V, 'v', 'V', "v" SPACING "V") //!< normal: v shifted: V
+ PORT_KEY(A2_KEY_0, KEYCODE_0, '0', ')', "0" SPACING ")") //!< normal: 0 shifted: )
+ PORT_KEY(A2_KEY_K, KEYCODE_K, 'k', 'K', "k" SPACING "K") //!< normal: k shifted: K
+ PORT_KEY(A2_KEY_MINUS, KEYCODE_MINUS, '-', '_', "-" SPACING "_") //!< normal: - shifted: _
+ PORT_KEY(A2_KEY_P, KEYCODE_P, 'p', 'P', "p" SPACING "P") //!< normal: p shifted: P
+ PORT_KEY(A2_KEY_SLASH, KEYCODE_SLASH, '/', '?', "/" SPACING "?") //!< normal: / shifted: ?
+ PORT_KEY(A2_KEY_BACKSLASH, KEYCODE_BACKSLASH, '\\', '|', "\\" SPACING "|") //!< normal: \ shifted: |
+ PORT_KEY(A2_KEY_LF, KEYCODE_DOWN, 10, 10, "LF" ) //!< normal: LF shifted: ?
+ PORT_KEY(A2_KEY_BS, KEYCODE_BACKSPACE, 8, 8, "BS" ) //!< normal: BS shifted: ?
+
+ PORT_START("ROW1")
+ PORT_KEY(A2_KEY_3, KEYCODE_3, '3', '#', "3" SPACING "#") //!< normal: 3 shifted: #
+ PORT_KEY(A2_KEY_2, KEYCODE_2, '2', '@', "2" SPACING "@") //!< normal: 2 shifted: @
+ PORT_KEY(A2_KEY_W, KEYCODE_W, 'w', 'W', "w" SPACING "W") //!< normal: w shifted: W
+ PORT_KEY(A2_KEY_Q, KEYCODE_Q, 'q', 'Q', "q" SPACING "Q") //!< normal: q shifted: Q
+ PORT_KEY(A2_KEY_S, KEYCODE_S, 's', 'S', "s" SPACING "S") //!< normal: s shifted: S
+ PORT_KEY(A2_KEY_A, KEYCODE_A, 'a', 'A', "a" SPACING "A") //!< normal: a shifted: A
+ PORT_KEY(A2_KEY_9, KEYCODE_9, '9', '(', "9" SPACING "(") //!< normal: 9 shifted: (
+ PORT_KEY(A2_KEY_I, KEYCODE_I, 'i', 'I', "i" SPACING "I") //!< normal: i shifted: I
+ PORT_KEY(A2_KEY_X, KEYCODE_X, 'x', 'X', "x" SPACING "X") //!< normal: x shifted: X
+ PORT_KEY(A2_KEY_O, KEYCODE_O, 'o', 'O', "o" SPACING "O") //!< normal: o shifted: O
+ PORT_KEY(A2_KEY_L, KEYCODE_L, 'l', 'L', "l" SPACING "L") //!< normal: l shifted: L
+ PORT_KEY(A2_KEY_COMMA, KEYCODE_COMMA, ',', '<', "," SPACING "<") //!< normal: , shifted: <
+ PORT_KEY(A2_KEY_QUOTE, KEYCODE_QUOTE, 39, 34, "'" SPACING "\"") //!< normal: ' shifted: "
+ PORT_KEY(A2_KEY_RBRACKET, KEYCODE_CLOSEBRACE, ']', '}', "]" SPACING "}") //!< normal: ] shifted: }
+ PORT_KEY(A2_KEY_BLANK_MID, KEYCODE_END, 0, 0, "MID" ) //!< middle blank key
+ PORT_KEY(A2_KEY_BLANK_TOP, KEYCODE_PGUP, 0, 0, "TOP" ) //!< top blank key
+
+ PORT_START("ROW2")
+ PORT_KEY(A2_KEY_1, KEYCODE_1, '1', '!', "1" SPACING "!") //!< normal: 1 shifted: !
+ PORT_KEY(A2_KEY_ESCAPE, KEYCODE_ESC, 27, 0, "ESC" ) //!< normal: ESC shifted: ?
+ PORT_KEY(A2_KEY_TAB, KEYCODE_TAB, 9, 0, "TAB" ) //!< normal: TAB shifted: ?
+ PORT_KEY(A2_KEY_F, KEYCODE_F, 'f', 'F', "f" SPACING "F") //!< normal: f shifted: F
+ PORT_KEY(A2_KEY_CTRL, KEYCODE_LCONTROL, 0, 0, "CTRL" ) //!< CTRL
+ PORT_KEY(A2_KEY_C, KEYCODE_C, 'c', 'C', "c" SPACING "C") //!< normal: c shifted: C
+ PORT_KEY(A2_KEY_J, KEYCODE_J, 'j', 'J', "j" SPACING "J") //!< normal: j shifted: J
+ PORT_KEY(A2_KEY_B, KEYCODE_B, 'b', 'B', "b" SPACING "B") //!< normal: b shifted: B
+ PORT_KEY(A2_KEY_Z, KEYCODE_Z, 'z', 'Z', "z" SPACING "Z") //!< normal: z shifted: Z
+ PORT_KEY(A2_KEY_LSHIFT, KEYCODE_LSHIFT, UCHAR_SHIFT_1, 0, "LSHIFT" ) //!< LSHIFT
+ PORT_KEY(A2_KEY_PERIOD, KEYCODE_STOP, '.', '>', "." SPACING ">") //!< normal: . shifted: >
+ PORT_KEY(A2_KEY_SEMICOLON, KEYCODE_COLON, ';', ':', ";" SPACING ":") //!< normal: ; shifted: :
+ PORT_KEY(A2_KEY_RETURN, KEYCODE_ENTER, 13, 0, "RETURN" ) //!< RETURN
+ PORT_KEY(A2_KEY_LEFTARROW, KEYCODE_LEFT, 0, 0, "???" SPACING "???") //!< normal: left arrow shifted: up arrow (caret)
+ PORT_KEY(A2_KEY_DEL, KEYCODE_DEL, UCHAR_MAMEKEY(DEL), 0, "DEL" ) //!< normal: DEL shifted: ?
+ PORT_KEY(A2_KEY_MSW_2_17, KEYCODE_MENU, 0, 0, "MSW2/17" ) //!< unused on Microswitch KDB
+
+ PORT_START("ROW3")
+ PORT_KEY(A2_KEY_R, KEYCODE_R, 'r', 'R', "r" SPACING "R") //!< normal: r shifted: R
+ PORT_KEY(A2_KEY_T, KEYCODE_T, 't', 'T', "t" SPACING "T") //!< normal: t shifted: T
+ PORT_KEY(A2_KEY_G, KEYCODE_G, 'g', 'G', "g" SPACING "G") //!< normal: g shifted: G
+ PORT_KEY(A2_KEY_Y, KEYCODE_Y, 'y', 'Y', "y" SPACING "Y") //!< normal: y shifted: Y
+ PORT_KEY(A2_KEY_H, KEYCODE_H, 'h', 'H', "h" SPACING "H") //!< normal: h shifted: H
+ PORT_KEY(A2_KEY_8, KEYCODE_8, '8', '*', "8" SPACING "*") //!< normal: 8 shifted: *
+ PORT_KEY(A2_KEY_N, KEYCODE_N, 'n', 'N', "n" SPACING "N") //!< normal: n shifted: N
+ PORT_KEY(A2_KEY_M, KEYCODE_M, 'm', 'M', "m" SPACING "M") //!< normal: m shifted: M
+ PORT_KEY(A2_KEY_LOCK, KEYCODE_SCRLOCK, 0, 0, "LOCK" ) //!< LOCK
+ PORT_KEY(A2_KEY_SPACE, KEYCODE_SPACE, 32, 0, "SPACE" ) //!< SPACE
+ PORT_KEY(A2_KEY_LBRACKET, KEYCODE_OPENBRACE, '[', '{', "[" SPACING "{") //!< normal: [ shifted: {
+ PORT_KEY(A2_KEY_EQUALS, KEYCODE_EQUALS, '=', '+', "=" SPACING "+") //!< normal: = shifted: +
+ PORT_KEY(A2_KEY_RSHIFT, KEYCODE_RSHIFT, UCHAR_SHIFT_2, 0, "RSHIFT" ) //!< RSHIFT
+ PORT_KEY(A2_KEY_BLANK_BOT, KEYCODE_PGDN, 0, 0, "BOT" ) //!< bottom blank key
+ PORT_KEY(A2_KEY_MSW_3_16, KEYCODE_HOME, 0, 0, "MSW3/16" ) //!< unused on Microswitch KDB
+ PORT_KEY(A2_KEY_MSW_3_17, KEYCODE_INSERT, 0, 0, "MSW3/17" ) //!< unused on Microswitch KDB
+
+ PORT_START("ROW4")
+ PORT_KEY(A2_KEY_FR2, KEYCODE_F6, 0, 0, "FR2" ) //!< ADL right function key 2
+ PORT_KEY(A2_KEY_FL2, KEYCODE_F2, 0, 0, "FL2" ) //!< ADL left function key 2
+
+ PORT_START("ROW5")
+ PORT_KEY(A2_KEY_FR4, KEYCODE_F8, 0, 0, "FR4" ) //!< ADL right funtion key 4
+ PORT_KEY(A2_KEY_BW, KEYCODE_F10, 0, 0, "BW" ) //!< ADL BW (?)
+
+ PORT_START("ROW6")
+ PORT_KEY(A2_KEY_FR3, KEYCODE_F7, 0, 0, "FR3" ) //!< ADL right function key 3
+ PORT_KEY(A2_KEY_FL1, KEYCODE_F1, 0, 0, "FL1" ) //!< ADL left function key 1
+ PORT_KEY(A2_KEY_FL3, KEYCODE_F3, 0, 0, "FL3" ) //!< ADL left function key 3
+
+ PORT_START("ROW7")
+ PORT_KEY(A2_KEY_FR1, KEYCODE_F5, 0, 0, "FR1" ) //!< ADL right function key 1
+ PORT_KEY(A2_KEY_FL4, KEYCODE_F4, 0, 0, "FL4" ) //!< ADL left function key 4
+ PORT_KEY(A2_KEY_FR5, KEYCODE_F9, 0, 0, "FR5" ) //!< ADL right function key 5
+
+ PORT_START("mouseb0") // Mouse button 0
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Mouse RED (left)") PORT_PLAYER(1) PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER( ":maincpu", alto2_cpu_device, mouse_button_0, 0 )
+ PORT_START("mouseb1") // Mouse button 1
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("Mouse BLUE (right)") PORT_PLAYER(1) PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER( ":maincpu", alto2_cpu_device, mouse_button_1, 0 )
+ PORT_START("mouseb2") // Mouse button 2
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("Mouse YELLOW (middle)") PORT_PLAYER(1) PORT_CODE(MOUSECODE_BUTTON3) PORT_CHANGED_MEMBER( ":maincpu", alto2_cpu_device, mouse_button_2, 0 )
+
+ PORT_START("mousex") // Mouse - X AXIS
+ PORT_BIT( 0xffff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CHANGED_MEMBER( ":maincpu", alto2_cpu_device, mouse_motion_x, 0 )
+
+ PORT_START("mousey") // Mouse - Y AXIS
+ PORT_BIT( 0xffff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(1) PORT_CHANGED_MEMBER( ":maincpu", alto2_cpu_device, mouse_motion_y, 0 )
+
+ PORT_START("CONFIG") /* Memory switch on AIM board */
+ PORT_CONFNAME( 0x01, 0x01, "Memory switch")
+ PORT_CONFSETTING( 0x00, "on")
+ PORT_CONFSETTING( 0x01, "off")
+ PORT_CONFNAME( 0x70, 0x00, "Ethernet breath-of-life")
+ PORT_CONFSETTING( 0x00, "off")
+ PORT_CONFSETTING( 0x10, "5 seconds")
+ PORT_CONFSETTING( 0x20, "10 seconds")
+ PORT_CONFSETTING( 0x30, "15 seconds")
+ PORT_CONFSETTING( 0x40, "30 seconds")
+ PORT_CONFSETTING( 0x50, "60 seconds")
+ PORT_CONFSETTING( 0x60, "90 seconds")
+ PORT_CONFSETTING( 0x70, "120 seconds")
+
+ PORT_START("ETHERID")
+ PORT_DIPNAME( 0377, 0042, "Ethernet ID")
+ PORT_DIPSETTING( 0000, "No ether") PORT_DIPSETTING( 0001, "ID 001") PORT_DIPSETTING( 0002, "ID 002") PORT_DIPSETTING( 0003, "ID 003")
+ PORT_DIPSETTING( 0004, "ID 004") PORT_DIPSETTING( 0005, "ID 005") PORT_DIPSETTING( 0006, "ID 006") PORT_DIPSETTING( 0007, "ID 007")
+ PORT_DIPSETTING( 0010, "ID 010") PORT_DIPSETTING( 0011, "ID 011") PORT_DIPSETTING( 0012, "ID 012") PORT_DIPSETTING( 0013, "ID 013")
+ PORT_DIPSETTING( 0014, "ID 014") PORT_DIPSETTING( 0015, "ID 015") PORT_DIPSETTING( 0016, "ID 016") PORT_DIPSETTING( 0017, "ID 017")
+ PORT_DIPSETTING( 0020, "ID 020") PORT_DIPSETTING( 0021, "ID 021") PORT_DIPSETTING( 0022, "ID 022") PORT_DIPSETTING( 0023, "ID 023")
+ PORT_DIPSETTING( 0024, "ID 024") PORT_DIPSETTING( 0025, "ID 025") PORT_DIPSETTING( 0026, "ID 026") PORT_DIPSETTING( 0027, "ID 027")
+ PORT_DIPSETTING( 0030, "ID 030") PORT_DIPSETTING( 0031, "ID 031") PORT_DIPSETTING( 0032, "ID 032") PORT_DIPSETTING( 0033, "ID 033")
+ PORT_DIPSETTING( 0034, "ID 034") PORT_DIPSETTING( 0035, "ID 035") PORT_DIPSETTING( 0036, "ID 036") PORT_DIPSETTING( 0037, "ID 037")
+ PORT_DIPSETTING( 0040, "ID 040") PORT_DIPSETTING( 0041, "ID 041") PORT_DIPSETTING( 0042, "ID 042") PORT_DIPSETTING( 0043, "ID 043")
+ PORT_DIPSETTING( 0044, "ID 044") PORT_DIPSETTING( 0045, "ID 045") PORT_DIPSETTING( 0046, "ID 046") PORT_DIPSETTING( 0047, "ID 047")
+ PORT_DIPSETTING( 0050, "ID 050") PORT_DIPSETTING( 0051, "ID 051") PORT_DIPSETTING( 0052, "ID 052") PORT_DIPSETTING( 0053, "ID 053")
+ PORT_DIPSETTING( 0054, "ID 054") PORT_DIPSETTING( 0055, "ID 055") PORT_DIPSETTING( 0056, "ID 056") PORT_DIPSETTING( 0057, "ID 057")
+ PORT_DIPSETTING( 0060, "ID 060") PORT_DIPSETTING( 0061, "ID 061") PORT_DIPSETTING( 0062, "ID 062") PORT_DIPSETTING( 0063, "ID 063")
+ PORT_DIPSETTING( 0064, "ID 064") PORT_DIPSETTING( 0065, "ID 065") PORT_DIPSETTING( 0066, "ID 066") PORT_DIPSETTING( 0067, "ID 067")
+ PORT_DIPSETTING( 0070, "ID 070") PORT_DIPSETTING( 0071, "ID 071") PORT_DIPSETTING( 0072, "ID 072") PORT_DIPSETTING( 0073, "ID 073")
+ PORT_DIPSETTING( 0074, "ID 074") PORT_DIPSETTING( 0075, "ID 075") PORT_DIPSETTING( 0076, "ID 076") PORT_DIPSETTING( 0077, "ID 077")
+ PORT_DIPSETTING( 0100, "ID 100") PORT_DIPSETTING( 0101, "ID 101") PORT_DIPSETTING( 0102, "ID 102") PORT_DIPSETTING( 0103, "ID 103")
+ PORT_DIPSETTING( 0104, "ID 104") PORT_DIPSETTING( 0105, "ID 105") PORT_DIPSETTING( 0106, "ID 106") PORT_DIPSETTING( 0107, "ID 107")
+ PORT_DIPSETTING( 0110, "ID 110") PORT_DIPSETTING( 0111, "ID 111") PORT_DIPSETTING( 0112, "ID 112") PORT_DIPSETTING( 0113, "ID 113")
+ PORT_DIPSETTING( 0114, "ID 114") PORT_DIPSETTING( 0115, "ID 115") PORT_DIPSETTING( 0116, "ID 116") PORT_DIPSETTING( 0117, "ID 117")
+ PORT_DIPSETTING( 0120, "ID 120") PORT_DIPSETTING( 0121, "ID 121") PORT_DIPSETTING( 0122, "ID 122") PORT_DIPSETTING( 0123, "ID 123")
+ PORT_DIPSETTING( 0124, "ID 124") PORT_DIPSETTING( 0125, "ID 125") PORT_DIPSETTING( 0126, "ID 126") PORT_DIPSETTING( 0127, "ID 127")
+ PORT_DIPSETTING( 0130, "ID 130") PORT_DIPSETTING( 0131, "ID 131") PORT_DIPSETTING( 0132, "ID 132") PORT_DIPSETTING( 0133, "ID 133")
+ PORT_DIPSETTING( 0134, "ID 134") PORT_DIPSETTING( 0135, "ID 135") PORT_DIPSETTING( 0136, "ID 136") PORT_DIPSETTING( 0137, "ID 137")
+ PORT_DIPSETTING( 0140, "ID 140") PORT_DIPSETTING( 0141, "ID 141") PORT_DIPSETTING( 0142, "ID 142") PORT_DIPSETTING( 0143, "ID 143")
+ PORT_DIPSETTING( 0144, "ID 144") PORT_DIPSETTING( 0145, "ID 145") PORT_DIPSETTING( 0146, "ID 146") PORT_DIPSETTING( 0147, "ID 147")
+ PORT_DIPSETTING( 0150, "ID 150") PORT_DIPSETTING( 0151, "ID 151") PORT_DIPSETTING( 0152, "ID 152") PORT_DIPSETTING( 0153, "ID 153")
+ PORT_DIPSETTING( 0154, "ID 154") PORT_DIPSETTING( 0155, "ID 155") PORT_DIPSETTING( 0156, "ID 156") PORT_DIPSETTING( 0157, "ID 157")
+ PORT_DIPSETTING( 0160, "ID 160") PORT_DIPSETTING( 0161, "ID 161") PORT_DIPSETTING( 0162, "ID 162") PORT_DIPSETTING( 0163, "ID 163")
+ PORT_DIPSETTING( 0164, "ID 164") PORT_DIPSETTING( 0165, "ID 165") PORT_DIPSETTING( 0166, "ID 166") PORT_DIPSETTING( 0167, "ID 167")
+ PORT_DIPSETTING( 0170, "ID 170") PORT_DIPSETTING( 0171, "ID 171") PORT_DIPSETTING( 0172, "ID 172") PORT_DIPSETTING( 0173, "ID 173")
+ PORT_DIPSETTING( 0174, "ID 174") PORT_DIPSETTING( 0175, "ID 175") PORT_DIPSETTING( 0176, "ID 176") PORT_DIPSETTING( 0177, "ID 177")
+ PORT_DIPSETTING( 0200, "ID 200") PORT_DIPSETTING( 0201, "ID 201") PORT_DIPSETTING( 0202, "ID 202") PORT_DIPSETTING( 0203, "ID 203")
+ PORT_DIPSETTING( 0204, "ID 204") PORT_DIPSETTING( 0205, "ID 205") PORT_DIPSETTING( 0206, "ID 206") PORT_DIPSETTING( 0207, "ID 207")
+ PORT_DIPSETTING( 0210, "ID 210") PORT_DIPSETTING( 0211, "ID 211") PORT_DIPSETTING( 0212, "ID 212") PORT_DIPSETTING( 0213, "ID 213")
+ PORT_DIPSETTING( 0214, "ID 214") PORT_DIPSETTING( 0215, "ID 215") PORT_DIPSETTING( 0216, "ID 216") PORT_DIPSETTING( 0217, "ID 217")
+ PORT_DIPSETTING( 0220, "ID 220") PORT_DIPSETTING( 0221, "ID 221") PORT_DIPSETTING( 0222, "ID 222") PORT_DIPSETTING( 0223, "ID 223")
+ PORT_DIPSETTING( 0224, "ID 224") PORT_DIPSETTING( 0225, "ID 225") PORT_DIPSETTING( 0226, "ID 226") PORT_DIPSETTING( 0227, "ID 227")
+ PORT_DIPSETTING( 0230, "ID 230") PORT_DIPSETTING( 0231, "ID 231") PORT_DIPSETTING( 0232, "ID 232") PORT_DIPSETTING( 0233, "ID 233")
+ PORT_DIPSETTING( 0234, "ID 234") PORT_DIPSETTING( 0235, "ID 235") PORT_DIPSETTING( 0236, "ID 236") PORT_DIPSETTING( 0237, "ID 237")
+ PORT_DIPSETTING( 0240, "ID 240") PORT_DIPSETTING( 0241, "ID 241") PORT_DIPSETTING( 0242, "ID 242") PORT_DIPSETTING( 0243, "ID 243")
+ PORT_DIPSETTING( 0244, "ID 244") PORT_DIPSETTING( 0245, "ID 245") PORT_DIPSETTING( 0246, "ID 246") PORT_DIPSETTING( 0247, "ID 247")
+ PORT_DIPSETTING( 0250, "ID 250") PORT_DIPSETTING( 0251, "ID 251") PORT_DIPSETTING( 0252, "ID 252") PORT_DIPSETTING( 0253, "ID 253")
+ PORT_DIPSETTING( 0254, "ID 254") PORT_DIPSETTING( 0255, "ID 255") PORT_DIPSETTING( 0256, "ID 256") PORT_DIPSETTING( 0257, "ID 257")
+ PORT_DIPSETTING( 0260, "ID 260") PORT_DIPSETTING( 0261, "ID 261") PORT_DIPSETTING( 0262, "ID 262") PORT_DIPSETTING( 0263, "ID 263")
+ PORT_DIPSETTING( 0264, "ID 264") PORT_DIPSETTING( 0265, "ID 265") PORT_DIPSETTING( 0266, "ID 266") PORT_DIPSETTING( 0267, "ID 267")
+ PORT_DIPSETTING( 0270, "ID 270") PORT_DIPSETTING( 0271, "ID 271") PORT_DIPSETTING( 0272, "ID 272") PORT_DIPSETTING( 0273, "ID 273")
+ PORT_DIPSETTING( 0274, "ID 274") PORT_DIPSETTING( 0275, "ID 275") PORT_DIPSETTING( 0276, "ID 276") PORT_DIPSETTING( 0277, "ID 277")
+ PORT_DIPSETTING( 0300, "ID 300") PORT_DIPSETTING( 0301, "ID 301") PORT_DIPSETTING( 0302, "ID 302") PORT_DIPSETTING( 0303, "ID 303")
+ PORT_DIPSETTING( 0304, "ID 304") PORT_DIPSETTING( 0305, "ID 305") PORT_DIPSETTING( 0306, "ID 306") PORT_DIPSETTING( 0307, "ID 307")
+ PORT_DIPSETTING( 0310, "ID 310") PORT_DIPSETTING( 0311, "ID 311") PORT_DIPSETTING( 0312, "ID 312") PORT_DIPSETTING( 0313, "ID 313")
+ PORT_DIPSETTING( 0314, "ID 314") PORT_DIPSETTING( 0315, "ID 315") PORT_DIPSETTING( 0316, "ID 316") PORT_DIPSETTING( 0317, "ID 317")
+ PORT_DIPSETTING( 0320, "ID 320") PORT_DIPSETTING( 0321, "ID 321") PORT_DIPSETTING( 0322, "ID 322") PORT_DIPSETTING( 0323, "ID 323")
+ PORT_DIPSETTING( 0324, "ID 324") PORT_DIPSETTING( 0325, "ID 325") PORT_DIPSETTING( 0326, "ID 326") PORT_DIPSETTING( 0327, "ID 327")
+ PORT_DIPSETTING( 0330, "ID 330") PORT_DIPSETTING( 0331, "ID 331") PORT_DIPSETTING( 0332, "ID 332") PORT_DIPSETTING( 0333, "ID 333")
+ PORT_DIPSETTING( 0334, "ID 334") PORT_DIPSETTING( 0335, "ID 335") PORT_DIPSETTING( 0336, "ID 336") PORT_DIPSETTING( 0337, "ID 337")
+ PORT_DIPSETTING( 0340, "ID 340") PORT_DIPSETTING( 0341, "ID 341") PORT_DIPSETTING( 0342, "ID 342") PORT_DIPSETTING( 0343, "ID 343")
+ PORT_DIPSETTING( 0344, "ID 344") PORT_DIPSETTING( 0345, "ID 345") PORT_DIPSETTING( 0346, "ID 346") PORT_DIPSETTING( 0347, "ID 347")
+ PORT_DIPSETTING( 0350, "ID 350") PORT_DIPSETTING( 0351, "ID 351") PORT_DIPSETTING( 0352, "ID 352") PORT_DIPSETTING( 0353, "ID 353")
+ PORT_DIPSETTING( 0354, "ID 354") PORT_DIPSETTING( 0355, "ID 355") PORT_DIPSETTING( 0356, "ID 356") PORT_DIPSETTING( 0357, "ID 357")
+ PORT_DIPSETTING( 0360, "ID 360") PORT_DIPSETTING( 0361, "ID 361") PORT_DIPSETTING( 0362, "ID 362") PORT_DIPSETTING( 0363, "ID 363")
+ PORT_DIPSETTING( 0364, "ID 364") PORT_DIPSETTING( 0365, "ID 365") PORT_DIPSETTING( 0366, "ID 366") PORT_DIPSETTING( 0367, "ID 367")
+ PORT_DIPSETTING( 0370, "ID 370") PORT_DIPSETTING( 0371, "ID 371") PORT_DIPSETTING( 0372, "ID 372") PORT_DIPSETTING( 0373, "ID 373")
+ PORT_DIPSETTING( 0374, "ID 374") PORT_DIPSETTING( 0375, "ID 375")
+INPUT_PORTS_END
+
+/* ROM */
+ROM_START( alto2 )
+ // dummy region for the maincpu - this is not used in any way
+ ROM_REGION( 0400, "maincpu", 0 )
+ ROM_FILL(0, 0400, ALTO2_UCODE_INVERTED)
+ROM_END
+
+//**************************************************************************
+// ADDRESS MAPS
+//**************************************************************************
+
+ADDRESS_MAP_START( alto2_ucode_map, AS_0, 32, alto2_state )
+ AM_RANGE(0, ALTO2_UCODE_SIZE-1) AM_DEVICE32( "maincpu", alto2_cpu_device, ucode_map, 0xffffffffUL )
+ADDRESS_MAP_END
+
+ADDRESS_MAP_START( alto2_const_map, AS_1, 16, alto2_state )
+ AM_RANGE(0, ALTO2_CONST_SIZE-1) AM_DEVICE16( "maincpu", alto2_cpu_device, const_map, 0xffffU )
+ADDRESS_MAP_END
+
+ADDRESS_MAP_START( alto2_iomem_map, AS_2, 16, alto2_state )
+ AM_RANGE(0, 2*ALTO2_RAM_SIZE-1) AM_DEVICE16( "maincpu", alto2_cpu_device, iomem_map, 0xffffU )
+ADDRESS_MAP_END
+
+static MACHINE_CONFIG_START( alto2, alto2_state )
+ /* basic machine hardware */
+ // SYSCLK is Display Control part A51 (tagged 29.4MHz) divided by 5(?)
+ // 5.8MHz according to de.wikipedia.org/wiki/Xerox_Alto
+ MCFG_CPU_ADD("maincpu", ALTO2, XTAL_29_4912MHz/5)
+ MCFG_CPU_PROGRAM_MAP(alto2_ucode_map)
+ MCFG_CPU_DATA_MAP(alto2_const_map)
+ MCFG_CPU_IO_MAP(alto2_iomem_map)
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_20_16MHz,
+ ALTO2_DISPLAY_TOTAL_WIDTH, 0, ALTO2_DISPLAY_WIDTH,
+ ALTO2_DISPLAY_TOTAL_HEIGHT, 0, ALTO2_DISPLAY_HEIGHT + ALTO2_FAKE_STATUS_H)
+ MCFG_SCREEN_REFRESH_RATE(60) // two interlaced fields
+ MCFG_SCREEN_VBLANK_TIME(ALTO2_DISPLAY_VBLANK_TIME)
+ MCFG_SCREEN_UPDATE_DEVICE("maincpu", alto2_cpu_device, screen_update)
+ MCFG_SCREEN_VBLANK_DEVICE("maincpu", alto2_cpu_device, screen_eof)
+ MCFG_SCREEN_PALETTE("palette")
+
+ MCFG_DEFAULT_LAYOUT( layout_vertical )
+
+ MCFG_PALETTE_ADD_WHITE_AND_BLACK("palette")
+
+ MCFG_DIABLO_DRIVES_ADD()
+MACHINE_CONFIG_END
+
+/* Driver Init */
+
+DRIVER_INIT_MEMBER( alto2_state, alto2 )
+{
+ // make the diablo drives known to the CPU core
+ alto2_cpu_device* cpu = downcast<alto2_cpu_device *>(m_maincpu.target());
+ cpu->set_diablo(0, downcast<diablo_hd_device *>(machine().device(DIABLO_HD_0)));
+ cpu->set_diablo(1, downcast<diablo_hd_device *>(machine().device(DIABLO_HD_1)));
+}
+
+/* Game Drivers */
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP( 1977, alto2, 0, 0, alto2, alto2, alto2_state, alto2, "Xerox", "Alto-II", GAME_NO_SOUND )
diff --git a/src/mess/mess.lst b/src/mess/mess.lst
index bd12a519582..fc7760cf07b 100644
--- a/src/mess/mess.lst
+++ b/src/mess/mess.lst
@@ -2362,3 +2362,4 @@ mx2178
hunter2
amust
fc100
+alto2
diff --git a/src/mess/mess.mak b/src/mess/mess.mak
index bc035305f39..115c32f0ce6 100644
--- a/src/mess/mess.mak
+++ b/src/mess/mess.mak
@@ -128,6 +128,7 @@ CPUS += ES5510
CPUS += SCUDSP
CPUS += IE15
CPUS += 8X300
+CPUS += ALTO2
#-------------------------------------------------
# specify available sound cores; some of these are
@@ -515,6 +516,7 @@ MACHINES += TI99_HD
MACHINES += STRATA
MACHINES += CORVUSHD
MACHINES += WOZFDC
+MACHINES += DIABLO_HD
#-------------------------------------------------
# specify available bus cores
@@ -1992,6 +1994,7 @@ $(MESSOBJ)/wavemate.a: \
$(MESSOBJ)/xerox.a: \
$(MESS_DRIVERS)/xerox820.o \
$(MESS_DRIVERS)/bigbord2.o \
+ $(MESS_DRIVERS)/alto2.o \
$(MESSOBJ)/xussrpc.a: \
$(MESS_DRIVERS)/ec184x.o \