diff options
Diffstat (limited to 'src/devices/cpu/alto2/a2disk.c')
-rw-r--r-- | src/devices/cpu/alto2/a2disk.c | 1895 |
1 files changed, 1895 insertions, 0 deletions
diff --git a/src/devices/cpu/alto2/a2disk.c b/src/devices/cpu/alto2/a2disk.c new file mode 100644 index 00000000000..1a8736962bb --- /dev/null +++ b/src/devices/cpu/alto2/a2disk.c @@ -0,0 +1,1895 @@ +// license:BSD-3-Clause +// copyright-holders:Juergen Buchmueller +/***************************************************************************** + * + * Xerox AltoII disk interface + * + *****************************************************************************/ +#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 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 +} |