/***************************************************************************** * SALTO - Xerox Alto I/II Simulator. * * Copyright (C) 2007 by Juergen Buchmueller * Partially based on info found in Eric Smith's Alto simulator: Altogether * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Disk I/O functions * * $Id: disk.h,v 1.1.1.1 2008/07/22 19:02:07 pm Exp $ *****************************************************************************/ #if !defined(_DISK_H_INCLUDED_) #define _DISK_H_INCLUDED_ /** @brief get sector number from address register */ #define GET_KADDR_SECTOR(kaddr) ALTO_GET(kaddr,16, 0, 3) /** @brief put sector number into address register */ #define PUT_KADDR_SECTOR(kaddr,val) ALTO_PUT(kaddr,16, 0, 3,val) /** @brief get cylinder number from address register */ #define GET_KADDR_CYLINDER(kaddr) ALTO_GET(kaddr,16, 4,12) /** @brief put cylinder number int address register */ #define PUT_KADDR_CYLINDER(kaddr,val) ALTO_PUT(kaddr,16, 4,12,val) /** @brief get head number from address register */ #define GET_KADDR_HEAD(kaddr) ALTO_GET(kaddr,16,13,13) /** @brief put head number into address register */ #define PUT_KADDR_HEAD(kaddr,val) ALTO_PUT(kaddr,16,13,13,val) /** @brief get drive (unit) number from address register */ #define GET_KADDR_DRIVE(kaddr) ALTO_GET(kaddr,16,14,14) /** @brief put drive (unit) number into address register */ #define PUT_KADDR_DRIVE(kaddr,val) ALTO_PUT(kaddr,16,14,14,val) /** @brief get restore flag from address register */ #define GET_KADDR_RESTORE(kaddr) ALTO_GET(kaddr,16,15,15) /** @brief putt restore flag into address register */ #define PUT_KADDR_RESTORE(kaddr,val) ALTO_PUT(kaddr,16,15,15,val) /** @brief get command seal from command register */ #define GET_KADR_SEAL(kadr) ALTO_GET(kadr,16, 0, 7) /** @brief put command seal into command register */ #define PUT_KADR_SEAL(kadr,val) ALTO_PUT(kadr,16, 0, 7,val) /** @brief get r/w/c for header from command register */ #define GET_KADR_HEADER(kadr) ALTO_GET(kadr,16, 8, 9) /** @brief put r/w/c for header from command register */ #define PUT_KADR_HEADER(kadr,val) ALTO_PUT(kadr,16, 8, 9,val) /** @brief get r/w/c for label from command register */ #define GET_KADR_LABEL(kadr) ALTO_GET(kadr,16,10,11) /** @brief put r/w/c for label into command register */ #define PUT_KADR_LABEL(kadr,val) ALTO_PUT(kadr,16,10,11,val) /** @brief get r/w/c for data from command register */ #define GET_KADR_DATA(kadr) ALTO_GET(kadr,16,12,13) /** @brief put r/w/c for data into command register */ #define PUT_KADR_DATA(kadr,val) ALTO_PUT(kadr,16,12,13,val) /** @brief get no transfer flag from command register */ #define GET_KADR_NOXFER(kadr) ALTO_GET(kadr,16,14,14) /** @brief put no transfer flag into command register */ #define PUT_KADR_NOXFER(kadr,val) ALTO_PUT(kadr,16,14,14,val) /** @brief get unused (drive?) flag from command register */ #define GET_KADR_UNUSED(kadr) ALTO_GET(kadr,16,15,15) /** @brief put unused (drive?) flag into command register */ #define PUT_KADR_UNUSED(kadr,val) ALTO_PUT(kadr,16,15,15,val) /** @brief get current sector number from status register */ #define GET_KSTAT_SECTOR(kstat) ALTO_GET(kstat,16,0,3) /** @brief put current sector number into status register */ #define PUT_KSTAT_SECTOR(kstat,val) ALTO_PUT(kstat,16,0,3,val) /** @brief get 'done' field from status register (017) */ #define GET_KSTAT_DONE(kstat) ALTO_GET(kstat,16,4,7) /** @brief put 'done' field int status register (017) */ #define PUT_KSTAT_DONE(kstat,val) ALTO_PUT(kstat,16,4,7,val) /** @brief get seek fail flag from status register */ #define GET_KSTAT_SEEKFAIL(kstat) ALTO_GET(kstat,16,8,8) /** @brief put seek fail flag into status register */ #define PUT_KSTAT_SEEKFAIL(kstat,val) ALTO_PUT(kstat,16,8,8,val) /** @brief get seek busy flag (strobe) from status register */ #define GET_KSTAT_SEEK(kstat) ALTO_GET(kstat,16,9,9) /** @brief put seek busy flag (strobe) into status register */ #define PUT_KSTAT_SEEK(kstat,val) ALTO_PUT(kstat,16,9,9,val) /** @brief get drive not ready flag from status register */ #define GET_KSTAT_NOTRDY(kstat) ALTO_GET(kstat,16,10,10) /** @brief put drive not ready flag into status register */ #define PUT_KSTAT_NOTRDY(kstat,val) ALTO_PUT(kstat,16,10,10,val) /** @brief get data late flag from status register */ #define GET_KSTAT_DATALATE(kstat) ALTO_GET(kstat,16,11,11) /** @brief put data late flag into status register */ #define PUT_KSTAT_DATALATE(kstat,val) ALTO_PUT(kstat,16,11,11,val) /** @brief get idle flag from status register (idle is a software flag) */ #define GET_KSTAT_IDLE(kstat) ALTO_GET(kstat,16,12,12) /** @brief put idle flag into status register (idle is a software flag) */ #define PUT_KSTAT_IDLE(kstat,val) ALTO_PUT(kstat,16,12,12,val) /** @brief get checksum flag from status register (checksum is a software flag; it is ORed when 0) */ #define GET_KSTAT_CKSUM(kstat) ALTO_GET(kstat,16,13,13) /** @brief put checksum flag into status register (checksum is a software flag; it is ORed when 0) */ #define PUT_KSTAT_CKSUM(kstat,val) ALTO_PUT(kstat,16,13,13,val) /** @brief get completion code from status register (completion is a 2-bit software latch) */ #define GET_KSTAT_COMPLETION(kstat) ALTO_GET(kstat,16,14,15) /** @brief put completion code into status register (completion is a 2-bit software latch) */ #define PUT_KSTAT_COMPLETION(kstat,val) ALTO_PUT(kstat,16,14,15,val) /** @brief get transfer off flag from controller command (hardware command register) */ #define GET_KCOM_XFEROFF(kcom) ALTO_GET(kcom,16,1,1) /** @brief put transfer off flag into controller command (hardware command register) */ #define PUT_KCOM_XFEROFF(kcom,val) ALTO_PUT(kcom,16,1,1,val) /** @brief get word task inhibit flag from controller command (hardware command register) */ #define GET_KCOM_WDINHIB(kcom) ALTO_GET(kcom,16,2,2) /** @brief put word task inhibit flag into controller command (hardware command register) */ #define PUT_KCOM_WDINHIB(kcom,val) ALTO_PUT(kcom,16,2,2,val) /** @brief get bit clock source flag from controller command (hardware command register) */ #define GET_KCOM_BCLKSRC(kcom) ALTO_GET(kcom,16,3,3) /** @brief put bit clock source flag into controller command (hardware command register) */ #define PUT_KCOM_BCLKSRC(kcom,val) ALTO_PUT(kcom,16,3,3,val) /** @brief get write fixed frequency oscillator flag from controller command (hardware command register) */ #define GET_KCOM_WFFO(kcom) ALTO_GET(kcom,16,4,4) /** @brief put write fixed frequency oscillator flag into controller command (hardware command register) */ #define PUT_KCOM_WFFO(kcom,val) ALTO_PUT(kcom,16,4,4,val) /** @brief get send address flag from controller command (hardware command register) */ #define GET_KCOM_SENDADR(kcom) ALTO_GET(kcom,16,5,5) /** @brief put send address flag into controller command (hardware command register) */ #define PUT_KCOM_SENDADR(kcom,val) ALTO_PUT(kcom,16,5,5,val) /** @brief completion codes (only for documentation, since this is microcode defined) */ typedef enum { STATUS_COMPLETION_GOOD, STATUS_COMPLETION_HARDWARE_ERROR, STATUS_COMPLETION_CHECK_ERROR, STATUS_COMPLETION_ILLEGAL_SECTOR } completion_t; /** @brief record numbers per sector in INCRECNO order */ typedef enum { RECNO_HEADER, RECNO_PAGENO, RECNO_LABEL, RECNO_DATA } recno_t; /** @brief read/write/check numbers */ typedef enum { RWC_READ, RWC_CHECK, RWC_WRITE, RWC_WRITE2 } rwc_t; /** * @brief enumeration of the inputs and outputs of a JK flip-flop type 74109 *
 * 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]
 * 
*/ typedef enum { /** @brief clock signal */ JKFF_CLK = 0001, /** @brief J input */ JKFF_J = 0002, /** @brief K' input */ JKFF_K = 0004, /** @brief S' input */ JKFF_S = 0010, /** @brief C' input */ JKFF_C = 0020, /** @brief Q output */ JKFF_Q = 0040, /** @brief Q' output */ JKFF_Q0 = 0100 } jkff_t; /** * @brief structure of the disk controller context */ typedef struct { /** @brief selected drive from KADDR[14] (written to data out with SENDADR) */ int drive; /** @brief A[0-15] disk hardware address (sector, cylinder, head, drive, restore) */ int kaddr; /** @brief C[0-15] with read/write/check modes for header, label and data */ int kadr; /** @brief S[0-15] disk status */ int kstat; /** @brief disk command (5 bits kcom[1-5]) */ int kcom; /** @brief record number (2 bits indexing header, label, data, -/-) */ recno_t krecno; /** @brief input shift register */ int shiftin; /** @brief output shift register */ int shiftout; /** @brief disk data in latch */ int datain; /** @brief disk data out latch */ int dataout; /** @brief read/write/check for current record */ rwc_t krwc; /** @brief disk fatal error signal state */ int kfer; /** @brief disk word task enable (active low) */ int wdtskena; /** @brief disk word task init at the early microcycle */ int wdinit0; /** @brief disk word task init at the late microcycle*/ int wdinit; /** @brief strobe (still) active */ int strobe; /** @brief current bitclk state (either crystal clock, or rdclk from the drive) */ int bitclk; /** @brief bit clock timer id */ int bitclk_id; /** @brief current datin drom the drive */ int datin; /** @brief bit counter */ int bitcount; /** @brief carry output of the bitcounter */ int carry; /** @brief sector late (monoflop output) */ int seclate; /** @brief sector late timer id */ int seclate_id; /** @brief seekok state (SKINC' & LAI' & ff_44a.Q') */ int seekok; /** @brief ok to run signal (set to 1 some time after reset ?) */ int ok_to_run; /** @brief ready monoflop 31a */ int ready_mf31a; /** @brief seclate monoflop 31b */ int seclate_mf31b; /** * @brief JK flip-flop 21a (sector task) * CLK SECT4 * J WAKEST' * K' 1 * S' ERRWAKE' * C' WAKEST' * Q to seclate monoflop */ jkff_t ff_21a; jkff_t ff_21a_old; /** * @brief JK flip-flop 21b (sector task) * CLK SYSCLKB' * J from 21a Q * K' 1 * S' 1 * C' WAKEST' * Q to 22a J */ jkff_t ff_21b; /** * @brief JK flip-flop 22a (sector task) * CLK SYSCLKB' * J from 21b Q * K' 1 * S' 1 * C' WAKEST' * Q to 22b J */ jkff_t ff_22a; /** * @brief JK flip-flop 22b (sector task) * CLK SYSCLKB' * J from 22a Q * K' (BLOCK & STSKACT)' * S' 1 (really it's RESET') * C' 1 * Q STSKENA; Q' WAKEKST' */ jkff_t ff_22b; /** * @brief JK flip-flop 43b (word task) * CLK WDDONE' * J 1 * K' 1 * S' 1 * C' WDTSKENA * Q to 53a J */ jkff_t ff_43b; /** * @brief JK flip-flop 53a (word task) * CLK SYSCLKB' * J from 43b Q * K' (BLOCK & WDTSKACT)' * S' 1 * C' WDALLOW * Q to 43a J */ jkff_t ff_53a; /** * @brief JK flip-flop 43a (word task) * CLK SYSCLKA' * J from 53a Q * K' from 53a Q * S' 1 * C' WDALLOW * Q WDTSKENA', Q' WDTSKENA */ jkff_t ff_43a; /** * @brief JK flip-flop 53b (word task) * CLK SYSCLKB' * J 0 * K' (BLOCK & WDTSKACT)' * S' WDALLOW * C' 1 * Q WDINIT */ jkff_t ff_53b; /** * @brief JK flip-flop 44a (LAI' clocked) * CLK (LAI')' * J 1 * K' 1 * S' 1 * C' CLRSTAT' * Q to seekok */ jkff_t ff_44a; /** * @brief JK flip-flop 44b (CKSUM) * CLK SYSCLKA' * J BUS[13] on KSTAT<- * K' 1 * S' 1 * C' 1 * Q n.c.; Q' inverted to BUS[13] on <-KSTAT */ jkff_t ff_44b; /** * @brief JK flip-flop 45a (ready latch) * CLK SYSCLKA' * J READY' from drive * K' 1 * S' 1 * C' CLRSTAT' * Q RDYLAT' */ jkff_t ff_45a; /** * @brief JK flip-flop 45b (seqerr latch) * CLK SYSCLKA' * J 1 * K' SEQERR' * S' CLRSTAT' * C' 1 * Q to KSTAT[11] DATALATE */ jkff_t ff_45b; } disk_t; /** * @brief the disk controller context */ extern disk_t dsk; /** * @brief update the frontend's drive image name status * * Note: There is no such indicator in the real Alto. This is just * to keep the user informed about drive activity. */ extern void disk_show_image_name(int unit); /** * @brief update the frontend's drive activity indicators * * Note: There is no such indicator in the real Alto. This is just * to keep the user informed about drive activity. */ extern void disk_show_indicator(int unit, int n, int val); /** * @brief update the frontend's drive activity indicators * * Note: There is no such indicator in the real Alto. This is just * to keep the user informed about drive activity. */ extern void disk_show_indicators(int unit); /** * @brief called if one of the disk tasks blocks * * @param task task that blocks (either task_ksec or task_kwd) */ extern void disk_block(int task); /** * @brief bus driven by disk status register KSTAT */ extern void bs_read_kstat_0(void); /** * @brief bus driven by disk data register KDATA input */ extern void bs_read_kdata_0(void); /** * @brief 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. */ extern void f1_strobe_1(void); /** * @brief load disk status register * * KSTAT[12-15] are loaded from BUS[12-15], except that BUS[13] is * ORed into KSTAT[13]. */ extern void f1_load_kstat_1(void); /** * @brief load data out register * * KDATA_OUT is loaded from BUS. */ extern void f1_load_kdata_1(void); /** * @brief 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. * * Sheet 10, shifters 36 and 37 (74195) * * 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 Vcc on both * Both shifters are loaded with KADR<- * * The QA outputs are 36 -> RECNO(0) and 37 -> RECNO(1) * * QA of 36 is inverted and goes to J/K' of 37 * QA of 37 goes to J/K' of 36 * */ extern void f1_increcno_1(void); /** * @brief reset all error latches * * Causes all error latches in the disk controller hardware to reset, * clears KSTAT[13]. */ extern void f1_clrstat_1(void); /** * @brief load the KCOM register from bus * * 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 disk. * (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 transmitted * to disk unit as track address. SENDADR = 0 inhibits such * transmission. */ extern void f1_load_kcom_1(void); /** * @brief 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]. * */ extern void f1_load_kadr_1(void); /** * @brief branch on disk word task active and init * * NEXT <- NEXT OR (WDTASKACT && WDINIT ? 037 : 0) */ extern void f2_init_1(void); /** * @brief branch on read/write/check state of the current record * * NEXT <- NEXT OR (current record to be written ? 3 : * current record to be checked ? 2 : 0); * */ extern void f2_rwc_1(void); /** * @brief branch on the current record number by a lookup table * * NEXT <- NEXT OR MAP (current record number) where * MAP(0) = 0 * MAP(1) = 2 * MAP(2) = 3 * MAP(3) = 1 */ extern void f2_recno_1(void); /** * @brief branch on the data transfer state * * NEXT <- NEXT OR (if current command wants data transfer ? 1 : 0) */ extern void f2_xfrdat_1(void); /** * @brief branch on the disk ready signal * * NEXT <- NEXT OR (if disk not ready to accept command ? 1 : 0) */ extern void f2_swrnrdy_1(void); /** * @brief branch on the disk fatal error condition * * NEXT <- NEXT OR (if fatal error in latches ? 0 : 1) */ extern void f2_nfer_1(void); /** * @brief branch on the seek busy status * * NEXT <- NEXT OR (if seek strobe still on ? 1 : 0) */ extern void f2_strobon_1(void); /** * @brief pass down command line arguments to the disk emulation * * @param arg command line argument * @result returns 0 if this was a disk image, -1 on error */ extern int disk_args(const char *arg); /** * @brief initialize the disk context and insert a disk wort timer * * @result returns 0 on success, fatal() on error */ extern int disk_init(void); #endif /* !defined(_DISK_H_INCLUDED_) */