diff options
54 files changed, 15823 insertions, 1 deletions
diff --git a/.gitattributes b/.gitattributes index bf4072c1142..cb0b4de1ab0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1430,6 +1430,46 @@ src/emu/cpu/adsp2100/adsp2100.h svneol=native#text/plain src/emu/cpu/alph8201/8201dasm.c svneol=native#text/plain src/emu/cpu/alph8201/alph8201.c svneol=native#text/plain src/emu/cpu/alph8201/alph8201.h svneol=native#text/plain +src/emu/cpu/alto2/a2curt.c svneol=native#text/plain +src/emu/cpu/alto2/a2curt.h svneol=native#text/plain +src/emu/cpu/alto2/a2dht.c svneol=native#text/plain +src/emu/cpu/alto2/a2dht.h svneol=native#text/plain +src/emu/cpu/alto2/a2disk.c svneol=native#text/plain +src/emu/cpu/alto2/a2disk.h svneol=native#text/plain +src/emu/cpu/alto2/a2disp.c svneol=native#text/plain +src/emu/cpu/alto2/a2disp.h svneol=native#text/plain +src/emu/cpu/alto2/a2dvt.c svneol=native#text/plain +src/emu/cpu/alto2/a2dvt.h svneol=native#text/plain +src/emu/cpu/alto2/a2dwt.c svneol=native#text/plain +src/emu/cpu/alto2/a2dwt.h svneol=native#text/plain +src/emu/cpu/alto2/a2emu.c svneol=native#text/plain +src/emu/cpu/alto2/a2emu.h svneol=native#text/plain +src/emu/cpu/alto2/a2ether.c svneol=native#text/plain +src/emu/cpu/alto2/a2ether.h svneol=native#text/plain +src/emu/cpu/alto2/a2hw.c svneol=native#text/plain +src/emu/cpu/alto2/a2hw.h svneol=native#text/plain +src/emu/cpu/alto2/a2jkff.h svneol=native#text/plain +src/emu/cpu/alto2/a2kbd.c svneol=native#text/plain +src/emu/cpu/alto2/a2kbd.h svneol=native#text/plain +src/emu/cpu/alto2/a2ksec.c svneol=native#text/plain +src/emu/cpu/alto2/a2ksec.h svneol=native#text/plain +src/emu/cpu/alto2/a2kwd.c svneol=native#text/plain +src/emu/cpu/alto2/a2kwd.h svneol=native#text/plain +src/emu/cpu/alto2/a2mem.c svneol=native#text/plain +src/emu/cpu/alto2/a2mem.h svneol=native#text/plain +src/emu/cpu/alto2/a2mouse.c svneol=native#text/plain +src/emu/cpu/alto2/a2mouse.h svneol=native#text/plain +src/emu/cpu/alto2/a2mrt.c svneol=native#text/plain +src/emu/cpu/alto2/a2mrt.h svneol=native#text/plain +src/emu/cpu/alto2/a2part.c svneol=native#text/plain +src/emu/cpu/alto2/a2part.h svneol=native#text/plain +src/emu/cpu/alto2/a2ram.c svneol=native#text/plain +src/emu/cpu/alto2/a2ram.h svneol=native#text/plain +src/emu/cpu/alto2/a2roms.c svneol=native#text/plain +src/emu/cpu/alto2/a2roms.h svneol=native#text/plain +src/emu/cpu/alto2/alto2cpu.c svneol=native#text/plain +src/emu/cpu/alto2/alto2cpu.h svneol=native#text/plain +src/emu/cpu/alto2/alto2dsm.c svneol=native#text/plain src/emu/cpu/am29000/am29000.c svneol=native#text/plain src/emu/cpu/am29000/am29000.h svneol=native#text/plain src/emu/cpu/am29000/am29dasm.c svneol=native#text/plain @@ -2192,6 +2232,8 @@ src/emu/imagedev/cassette.c svneol=native#text/plain src/emu/imagedev/cassette.h svneol=native#text/plain src/emu/imagedev/chd_cd.c svneol=native#text/plain src/emu/imagedev/chd_cd.h svneol=native#text/plain +src/emu/imagedev/diablo.c svneol=native#text/plain +src/emu/imagedev/diablo.h svneol=native#text/plain src/emu/imagedev/flopdrv.c svneol=native#text/plain src/emu/imagedev/flopdrv.h svneol=native#text/plain src/emu/imagedev/floppy.c svneol=native#text/plain @@ -2352,6 +2394,8 @@ src/emu/machine/cs4031.c svneol=native#text/plain src/emu/machine/cs4031.h svneol=native#text/plain src/emu/machine/cs8221.c svneol=native#text/plain src/emu/machine/cs8221.h svneol=native#text/plain +src/emu/machine/diablo_hd.c svneol=native#text/plain +src/emu/machine/diablo_hd.h svneol=native#text/plain src/emu/machine/dp8390.c svneol=native#text/plain src/emu/machine/dp8390.h svneol=native#text/plain src/emu/machine/ds1204.c svneol=native#text/plain @@ -7534,6 +7578,7 @@ src/mess/drivers/alesis.c svneol=native#text/plain src/mess/drivers/alphasma.c svneol=native#text/plain src/mess/drivers/alphatro.c svneol=native#text/plain src/mess/drivers/altair.c svneol=native#text/plain +src/mess/drivers/alto2.c svneol=native#text/plain src/mess/drivers/altos5.c svneol=native#text/plain src/mess/drivers/amico2k.c svneol=native#text/plain src/mess/drivers/amiga.c svneol=native#text/plain 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 \ |