/* zx-200a.c: ZENDEX single/double density disk adapter Copyright (c) 2010, William A. Beech Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of William A. Beech shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from William A. Beech. MODIFICATIONS: 28 Jun 16 - Original file. NOTES: This controller will mount 4 DD disk images on drives :F0: thru :F3: addressed at ports 078H to 07FH. It also will mount 2 SD disk images on :F4: and :F5: addressed at ports 088H to 08FH. These are on physical drives :F0: and :F1:. Registers: 078H - Read - Subsystem status bit 0 - ready status of drive 0 bit 1 - ready status of drive 1 bit 2 - state of channel's interrupt FF bit 3 - controller presence indicator bit 4 - DD controller presence indicator bit 5 - ready status of drive 2 bit 6 - ready status of drive 3 bit 7 - zero 079H - Read - Read result type (bits 2-7 are zero) 00 - I/O complete with error 01 - Reserved 10 - Result byte contains diskette ready status 11 - Reserved 079H - Write - IOPB address low byte. 07AH - Write - IOPB address high byte and start operation. 07BH - Read - Read result byte If result type is 00H bit 0 - deleted record bit 1 - CRC error bit 2 - seek error bit 3 - address error bit 4 - data overrun/underrun bit 5 - write protect bit 6 - write error bit 7 - not ready If result type is 02H and ready has changed bit 0 - zero bit 1 - zero bit 2 - zero bit 3 - zero bit 4 - drive 2 ready bit 5 - drive 3 ready bit 6 - drive 0 ready bit 7 - drive 1 ready else return 0 07FH - Write - Reset diskette system. Operations: NOP - 0x00 Seek - 0x01 Format Track - 0x02 Recalibrate - 0x03 Read Data - 0x04 Verify CRC - 0x05 Write Data - 0x06 Write Deleted Data - 0x07 IOPB - I/O Parameter Block Byte 0 - Channel Word bit 3 - data word length (=8-bit, 1=16-bit) bit 4-5 - interrupt control 00 - I/O complete interrupt to be issued 01 - I/O complete interrupts are disabled 10 - illegal code 11 - illegal code bit 6- randon format sequence Byte 1 - Diskette Instruction bit 0-2 - operation code 000 - no operation 001 - seek 010 - format track 011 - recalibrate 100 - read data 101 - verify CRC 110 - write data 111 - write deleted data bit 3 - data word length ( same as byte-0, bit-3) bit 4-5 - unit select 00 - drive 0 01 - drive 1 10 - drive 2 11 - drive 3 bit 6-7 - reserved (zero) Byte 2 - Number of Records Byte 4 - Track Address Byte 5 - Sector Address Byte 6 - Buffer Low Address Byte 7 - Buffer High Address u3 - u4 - u5 - u6 - fdd number. The ZX-200A appears to the multibus system as if there were an iSBC-201 installed addressed at 0x88-0x8f and an iSBC-202 installed addressed at 0x78-0x7F. The DD disks are drive 0 - 3. The SD disks are mapped over DD disks 0 - 1. Thus drive 0 - 1 can be SD or DD, but not both. Drive 2 - 3 are always DD. */ #include "system_defs.h" /* system header in system dir */ #define UNIT_V_WPMODE (UNIT_V_UF) /* Write protect */ #define UNIT_WPMODE (1 << UNIT_V_WPMODE) #define FDD_NUM 6 #define SECSIZ 128 //disk controoler operations #define DNOP 0x00 //disk no operation #define DSEEK 0x01 //disk seek #define DFMT 0x02 //disk format #define DHOME 0x03 //disk home #define DREAD 0x04 //disk read #define DVCRC 0x05 //disk verify CRC #define DWRITE 0x06 //disk write //status #define RDY0 0x01 //FDD 0 ready #define RDY1 0x02 //FDD 1 ready #define FDCINT 0x04 //FDC interrupt flag #define FDCPRE 0x08 //FDC board present #define FDCDD 0x10 //fdc is DD #define RDY2 0x20 //FDD 2 ready #define RDY3 0x40 //FDD 3 ready //result type #define ROK 0x00 //FDC error #define RCHG 0x02 //FDC OK OR disk changed // If result type is ROK then rbyte is #define RB0DR 0x01 //deleted record #define RB0CRC 0x02 //CRC error #define RB0SEK 0x04 //seek error #define RB0ADR 0x08 //address error #define RB0OU 0x10 //data overrun/underrun #define RB0WP 0x20 //write protect #define RB0WE 0x40 //write error #define RB0NR 0x80 //not ready // If result type is RCHG then rbyte is #define RB1RD2 0x10 //drive 2 ready #define RB1RD3 0x20 //drive 3 ready #define RB1RD0 0x40 //drive 0 ready #define RB1RD1 0x80 //drive 1 ready //disk geometry values #define MDSSD 256256 //single density FDD size #define MDSDD 512512 //double density FDD size #define MAXSECSD 26 //single density last sector #define MAXSECDD 52 //double density last sector #define MAXTRK 76 //last track #define zx200a_NAME "Zendex ZX-200A Floppy Disk Controller Board" /* external globals */ extern uint16 PCX; /* external function prototypes */ extern uint8 reg_dev(uint8 (*routine)(t_bool, uint8, uint8), uint16, uint16, uint8); extern uint8 unreg_dev(uint16); extern uint8 get_mbyte(uint16 addr); extern void put_mbyte(uint16 addr, uint8 val); /* internal function prototypes */ t_stat zx200a_set_port(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat zx200a_set_int(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat zx200a_set_verb(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat zx200a_show_param (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat zx200a_reset(DEVICE *dptr); void zx200a_reset_dev(void); t_stat zx200a_attach (UNIT *uptr, CONST char *cptr); t_stat zx200a_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc); uint8 zx200ar0SD(t_bool io, uint8 data, uint8 devnum); uint8 zx200ar0DD(t_bool io, uint8 data, uint8 devnum); uint8 zx200ar1SD(t_bool io, uint8 data, uint8 devnum); uint8 zx200ar1DD(t_bool io, uint8 data, uint8 devnum); uint8 zx200ar2SD(t_bool io, uint8 data, uint8 devnum); uint8 zx200ar2DD(t_bool io, uint8 data, uint8 devnum); uint8 zx200ar3(t_bool io, uint8 data, uint8 devnum); uint8 zx200ar7(t_bool io, uint8 data, uint8 devnum); void zx200a_diskio(void); /* globals */ int zx200a_onetime = 1; static const char* zx200a_desc(DEVICE *dptr) { return zx200a_NAME; } typedef struct { //FDD definition uint8 sec; uint8 cyl; uint8 dd; } FDDDEF; typedef struct { //FDC definition uint8 baseport; //FDC base port uint8 intnum; //interrupt number uint8 verb; //verbose flag uint16 iopb; //FDC IOPB uint8 DDstat; //FDC DD status uint8 SDstat; //FDC SD status uint8 rdychg; //FDC ready change uint8 rtype; //FDC result type uint8 rbyte0; //FDC result byte for type 00 uint8 rbyte1; //FDC result byte for type 10 uint8 intff; //fdc interrupt FF FDDDEF fdd[FDD_NUM]; //indexed by the FDD number } FDCDEF; FDCDEF zx200a; /* ZX-200A Standard I/O Data Structures */ UNIT zx200a_unit[] = { { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+UNIT_RO+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_FIX, MDSDD) }, { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+UNIT_RO+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_FIX, MDSDD) }, { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+UNIT_RO+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_FIX, MDSDD) }, { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+UNIT_RO+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_FIX, MDSDD) }, { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+UNIT_RO+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_FIX, MDSSD) }, { UDATA (0, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+UNIT_RO+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_FIX, MDSSD) }, { NULL } }; REG zx200a_reg[] = { { HRDATA (SSTAT0, zx200a.SDstat, 8) }, /* zx200a 0 SD status */ { HRDATA (DSTAT0, zx200a.DDstat, 8) }, /* zx200a 0 DD status */ { HRDATA (RTYP0, zx200a.rtype, 8) }, /* zx200a 0 result type */ { HRDATA (RBYT0A, zx200a.rbyte0, 8) }, /* zx200a 0 result byte 0 */ { HRDATA (RBYT0B, zx200a.rbyte1, 8) }, /* zx200a 0 result byte 1 */ { HRDATA (INTFF0, zx200a.intff, 8) }, /* zx200a 0 interrupt f/f */ { NULL } }; MTAB zx200a_mod[] = { { UNIT_WPMODE, 0, "RW", "RW", &zx200a_set_mode }, { UNIT_WPMODE, UNIT_WPMODE, "WP", "WP", &zx200a_set_mode }, // { UNIT_WPMODE, UNIT_WPMODE, "RO", "RO", &zx200a_set_mode }, { MTAB_XTD | MTAB_VDV, 0, NULL, "VERB", &zx200a_set_verb, NULL, NULL, "Sets the verbose mode for ZX-200A"}, { MTAB_XTD | MTAB_VDV, 0, NULL, "PORT", &zx200a_set_port, NULL, NULL, "Sets the base port for ZX-200A"}, { MTAB_XTD | MTAB_VDV, 0, NULL, "INT", &zx200a_set_int, NULL, NULL, "Sets the interrupt number for ZX-200A"}, { MTAB_XTD | MTAB_VDV, 0, "PARAM", NULL, NULL, &zx200a_show_param, NULL, "show configured parameters for ZX-200A" }, { 0 } }; DEBTAB zx200a_debug[] = { { "ALL", DEBUG_all }, { "FLOW", DEBUG_flow }, { "READ", DEBUG_read }, { "WRITE", DEBUG_write }, { "XACK", DEBUG_xack }, { NULL } }; /* address width is set to 16 bits to use devices in 8086/8088 implementations */ DEVICE zx200a_dev = { "ZX200A", //name zx200a_unit, //units zx200a_reg, //registers zx200a_mod, //modifiers FDD_NUM, //numunits 16, //aradix 16, //awidth 1, //aincr 16, //dradix 8, //dwidth NULL, //examine NULL, //deposit zx200a_reset, //reset NULL, //boot &zx200a_attach, //attach NULL, //detach NULL, //ctxt DEV_DEBUG+DEV_DISABLE+DEV_DIS, //flags 0, //dctrl zx200a_debug, //debflags NULL, //msize NULL, //lname NULL, //help routine NULL, //attach help routine NULL, //help context &zx200a_desc //device description }; /* Service routines to handle simulator functions */ /* zx200a set mode = Write protect */ t_stat zx200a_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { if (uptr == NULL) return SCPE_ARG; if (uptr->flags & UNIT_ATT) return sim_messagef (SCPE_ALATT, "%s is already attached to %s\n", sim_uname(uptr), uptr->filename); if (val & UNIT_WPMODE) { /* write protect */ uptr->flags |= val; if (zx200a.verb) sim_printf(" ZX200A: WP\n"); } else { /* read write */ uptr->flags &= ~val; if (zx200a.verb) sim_printf(" ZX200A: RW\n"); } return SCPE_OK; } // set base address parameter t_stat zx200a_set_port(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint32 size, result; if (uptr == NULL) return SCPE_ARG; result = sscanf(cptr, "%02x", &size); zx200a.baseport = size; // if (zx200a.verb) sim_printf("ZX200A: Installed at base port=%04X\n", zx200a.baseport); reg_dev(zx200ar0DD, zx200a.baseport, 0, 0); //read status reg_dev(zx200ar1DD, zx200a.baseport + 1, 0, 0); //read rslt type/write IOPB addr-l reg_dev(zx200ar2DD, zx200a.baseport + 2, 0, 0); //write IOPB addr-h and start reg_dev(zx200ar3, zx200a.baseport + 3, 0, 0); //read rstl byte reg_dev(zx200ar7, zx200a.baseport + 7, 0, 0); //write reset fdc202 reg_dev(zx200ar0SD, zx200a.baseport, 0, 0); //read status reg_dev(zx200ar1SD, zx200a.baseport + 1, 0, 0); //read rslt type/write IOPB addr-l reg_dev(zx200ar2SD, zx200a.baseport + 2, 0, 0); //write IOPB addr-h and start reg_dev(zx200ar3, zx200a.baseport + 3, 0, 0); //read rstl byte reg_dev(zx200ar7, zx200a.baseport + 7, 0, 0); //write reset fdc202 // if (zx200a.verb) sim_printf(" ZX200A: Enabled base port at 0%02XH Interrupt #=%02X %s\n", zx200a.baseport, zx200a.intnum, zx200a.verb ? "Verbose" : "Quiet" ); return SCPE_OK; } // set interrupt parameter t_stat zx200a_set_int(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint32 size, result; if (uptr == NULL) return SCPE_ARG; result = sscanf(cptr, "%02x", &size); zx200a.intnum = size; // if (zx200a.verb) sim_printf("ZX200A: Interrupt number=%04X\n", zx200a.intnum); return SCPE_OK; } t_stat zx200a_set_verb(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { if (uptr == NULL) return SCPE_ARG; if (cptr == NULL) return SCPE_ARG; if (strncasecmp(cptr, "OFF", 4) == 0) { zx200a.verb = 0; return SCPE_OK; } if (strncasecmp(cptr, "ON", 3) == 0) { zx200a.verb = 1; sim_printf(" ZX200A: zx200a.verb=%d\n", zx200a.verb); return SCPE_OK; } return SCPE_ARG; } // show configuration parameters t_stat zx200a_show_param (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { if (uptr == NULL) return SCPE_ARG; fprintf(st, "%s Base port at %04X Interrupt # is %i %s", ((zx200a_dev.flags & DEV_DIS) == 0) ? "Enabled" : "Disabled", zx200a.baseport, zx200a.intnum, zx200a.verb ? "Verbose" : "Quiet" ); return SCPE_OK; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. */ /* Hardware reset routine */ t_stat zx200a_reset(DEVICE *dptr) { int i; UNIT *uptr; if (dptr == NULL) return SCPE_ARG; if (zx200a_onetime) { // zx200a.baseport = ZX200A_BASE; //set default base // zx200a.intnum = ZX200A_INT; //set default interrupt zx200a.verb = 0; //set verb = 0 zx200a_onetime = 0; // one-time initialization for all FDDs for this FDC instance for (i = 0; i < FDD_NUM; i++) { uptr = zx200a_dev.units + i; uptr->u6 = i; //fdd unit number } } if ((dptr->flags & DEV_DIS) == 0) { // enabled reg_dev(zx200ar0DD, zx200a.baseport, 0, 0); //read status reg_dev(zx200ar0DD, zx200a.baseport + 1, 0, 0); //read rslt type/write IOPB addr-l reg_dev(zx200ar0DD, zx200a.baseport + 2, 0, 0); //write IOPB addr-h and start reg_dev(zx200ar3, zx200a.baseport + 3, 0, 0); //read rstl byte reg_dev(zx200ar7, zx200a.baseport + 7, 0, 0); //write reset zx200a reg_dev(zx200ar0SD, zx200a.baseport + 16, 0, 0); //read status reg_dev(zx200ar1SD, zx200a.baseport + 17, 0, 0); //read rslt type/write IOPB addr-l reg_dev(zx200ar2SD, zx200a.baseport + 18, 0, 0); //write IOPB addr-h and start reg_dev(zx200ar3, zx200a.baseport + 19, 0, 0); //read rstl byte reg_dev(zx200ar7, zx200a.baseport + 23, 0, 0); //write reset zx200a zx200a_reset_dev(); //software reset // if (zx200a.verb) sim_printf(" ZX200A: Enabled base port at 0%02XH Interrupt #=%02X %s\n", zx200a.baseport, zx200a.intnum, zx200a.verb ? "Verbose" : "Quiet" ); } else { unreg_dev(zx200a.baseport); //read status unreg_dev(zx200a.baseport + 1); //read rslt type/write IOPB addr-l unreg_dev(zx200a.baseport + 2); //write IOPB addr-h and start unreg_dev(zx200a.baseport + 3); //read rstl byte unreg_dev(zx200a.baseport + 7); //write reset fdc201 unreg_dev(zx200a.baseport + 16); //read status unreg_dev(zx200a.baseport + 17); //read rslt type/write IOPB addr-l unreg_dev(zx200a.baseport + 18); //write IOPB addr-h and start unreg_dev(zx200a.baseport + 19); //read rstl byte unreg_dev(zx200a.baseport + 23); //write reset fdc201 if (zx200a.verb) sim_printf(" ZX200A: Disabled\n"); } return SCPE_OK; } /* Software reset routine */ void zx200a_reset_dev(void) { int32 i; UNIT *uptr; zx200a.DDstat = 0; //clear the FDC DD status zx200a.SDstat = 0; //clear the FDC SD status for (i = 0; i < FDD_NUM; i++) { /* handle all units */ uptr = zx200a_dev.units + i; zx200a.DDstat |= FDCPRE | FDCDD; //set the FDC DD status zx200a.SDstat |= FDCPRE; //set the FDC SD status if (i <= 3 ) { //first 4 are DD, last 2 are SD zx200a.fdd[i].dd = 1; //set double density } else { zx200a.fdd[i].dd = 0; //set single density } zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error if (uptr->flags & UNIT_ATT) { /* if attached */ switch(i){ case 0: zx200a.DDstat |= RDY0; //set FDD 0 ready zx200a.rbyte1 |= RB1RD0; break; case 1: zx200a.DDstat |= RDY1; //set FDD 1 ready zx200a.rbyte1 |= RB1RD1; break; case 2: zx200a.DDstat |= RDY2; //set FDD 2 ready zx200a.rbyte1 |= RB1RD2; break; case 3: zx200a.DDstat |= RDY3; //set FDD 3 ready zx200a.rbyte1 |= RB1RD3; break; case 4: zx200a.SDstat |= RDY0; //set FDD 0 ready zx200a.rbyte1 |= RB1RD0; break; case 5: zx200a.SDstat |= RDY1; //set FDD 1 ready zx200a.rbyte1 |= RB1RD1; break; } zx200a.rdychg = 0; } } } /* zx200a attach - attach an .IMG file to a FDD */ t_stat zx200a_attach (UNIT *uptr, CONST char *cptr) { t_stat r; uint8 fddnum; sim_debug (DEBUG_flow, &zx200a_dev, " zx200a_attach: Entered with cptr=%s\n", cptr); if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { sim_printf(" zx200a_attach: Attach error %d\n", r); return r; } fddnum = uptr->u6; switch(fddnum){ case 0: zx200a.DDstat |= RDY0; //set FDD 0 ready zx200a.rbyte1 |= RB1RD0; break; case 1: zx200a.DDstat |= RDY1; //set FDD 1 ready zx200a.rbyte1 |= RB1RD1; break; case 2: zx200a.DDstat |= RDY2; //set FDD 2 ready zx200a.rbyte1 |= RB1RD2; break; case 3: zx200a.DDstat |= RDY3; //set FDD 3 ready zx200a.rbyte1 |= RB1RD3; break; case 4: zx200a.SDstat |= RDY0; //set FDD 0 ready zx200a.rbyte1 |= RB1RD0; break; case 5: zx200a.SDstat |= RDY1; //set FDD 1 ready zx200a.rbyte1 |= RB1RD1; break; } zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error return SCPE_OK; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. */ /* zx200a control port functions */ uint8 zx200ar0SD(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read ststus*/ return zx200a.SDstat; } return 0; } uint8 zx200ar0DD(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read ststus*/ return zx200a.DDstat; } return 0; } uint8 zx200ar1SD(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read operation */ zx200a.intff = 0; //clear interrupt FF zx200a.SDstat &= ~FDCINT; zx200a.rtype = ROK; return zx200a.rtype; } else { /* write control port */ zx200a.iopb = data; } return 0; } uint8 zx200ar1DD(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read operation */ zx200a.intff = 0; //clear interrupt FF zx200a.DDstat &= ~FDCINT; zx200a.rtype = ROK; return zx200a.rtype; } else { /* write control port */ zx200a.iopb = data; } return 0; } uint8 zx200ar2SD(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read data port */ ; } else { /* write data port */ zx200a.iopb |= (data << 8); zx200a_diskio(); if (zx200a.intff) zx200a.SDstat |= FDCINT; } return 0; } uint8 zx200ar2DD(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read data port */ ; } else { /* write data port */ zx200a.iopb |= (data << 8); zx200a_diskio(); if (zx200a.intff) zx200a.DDstat |= FDCINT; } return 0; } uint8 zx200ar3(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read data port */ if (zx200a.rtype == ROK) { return zx200a.rbyte0; } else { if (zx200a.rdychg) { return zx200a.rbyte1; } else { return zx200a.rbyte0; } } } else { /* write data port */ ; //stop diskette operation } return 0; } /* reset ZX-200A */ uint8 zx200ar7(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read data port */ ; } else { /* write data port */ zx200a_reset_dev(); } return 0; } // perform the actual disk I/O operation void zx200a_diskio(void) { uint8 cw, di, nr, ta, sa, data, nrptr; uint16 ba; uint32 dskoff; uint8 fddnum, fmtb; uint32 i; UNIT *uptr; uint8 *fbuf; //parse the IOPB cw = get_mbyte(zx200a.iopb); di = get_mbyte(zx200a.iopb + 1); nr = get_mbyte(zx200a.iopb + 2); ta = get_mbyte(zx200a.iopb + 3); sa = get_mbyte(zx200a.iopb + 4); ba = get_mbyte(zx200a.iopb + 5); ba |= (get_mbyte(zx200a.iopb + 6) << 8); fddnum = (di & 0x30) >> 4; uptr = zx200a_dev.units + fddnum; fbuf = (uint8 *) uptr->filebuf; if (zx200a.verb) sim_printf("\n zx200a: FDD %d - nr=%02XH ta=%02XH sa=%02XH IOPB=%04XH PCX=%04XH", fddnum, nr, ta, sa, zx200a.iopb, PCX); //check for not ready switch(fddnum) { case 0: if ((zx200a.DDstat & RDY0) == 0) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0NR; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Ready error on drive %d", fddnum); return; } break; case 1: if ((zx200a.DDstat & RDY1) == 0) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0NR; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Ready error on drive %d", fddnum); return; } break; case 2: if ((zx200a.DDstat & RDY2) == 0) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0NR; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Ready error on drive %d", fddnum); return; } break; case 3: if ((zx200a.DDstat & RDY3) == 0) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0NR; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Ready error on drive %d", fddnum); return; } break; case 4: if ((zx200a.SDstat & RDY0) == 0) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0NR; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Ready error on drive %d", fddnum); return; } break; case 5: if ((zx200a.SDstat & RDY1) == 0) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0NR; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Ready error on drive %d", fddnum); return; } break; } //check for address error if (zx200a.fdd[fddnum].dd == 1) { if ( ((di & 0x07) != DHOME) && ( (sa > MAXSECDD) || ((sa + nr) > (MAXSECDD + 1)) || (sa == 0) || (ta > MAXTRK) )) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0ADR; zx200a.intff = 1; //set interrupt FF sim_printf("\n ZX200A: FDD %d - Address error sa=%02X nr=%02X ta=%02X PCX=%04X", fddnum, sa, nr, ta, PCX); return; } } else { if ( ((di & 0x07) != DHOME) && ( (sa > MAXSECSD) || ((sa + nr) > (MAXSECSD + 1)) || (sa == 0) || (ta > MAXTRK) )) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0ADR; zx200a.intff = 1; //set interrupt FF sim_printf("\n ZX200A: FDD %d - Address error sa=%02X nr=%02X ta=%02X PCX=%04X", fddnum, sa, nr, ta, PCX); return; } } switch (di & 0x07) { case DNOP: zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error zx200a.intff = 1; //set interrupt FF break; case DSEEK: zx200a.fdd[fddnum].sec = sa; zx200a.fdd[fddnum].cyl = ta; zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error zx200a.intff = 1; //set interrupt FF break; case DHOME: zx200a.fdd[fddnum].sec = sa; zx200a.fdd[fddnum].cyl = 0; zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error zx200a.intff = 1; //set interrupt FF break; case DVCRC: zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error zx200a.intff = 1; //set interrupt FF break; case DFMT: //check for WP if(uptr->flags & UNIT_WPMODE) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0WP; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Write protect error 1 on drive %d", fddnum); return; } fmtb = get_mbyte(ba); //get the format byte if (zx200a.fdd[fddnum].dd == 1) { //calculate offset into DD disk image dskoff = ((ta * MAXSECDD) + (sa - 1)) * 128; for(i=0; i<=((uint32)(MAXSECDD) * 128); i++) { *(fbuf + (dskoff + i)) = fmtb; } } else { //calculate offset into SD disk image dskoff = ((ta * MAXSECSD) + (sa - 1)) * 128; for(i=0; i<=((uint32)(MAXSECSD) * 128); i++) { *(fbuf + (dskoff + i)) = fmtb; } } zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error zx200a.intff = 1; //set interrupt FF break; case DREAD: nrptr = 0; while(nrptr < nr) { //calculate offset into disk image if (zx200a.fdd[fddnum].dd == 1) { dskoff = ((ta * MAXSECDD) + (sa - 1)) * 128; } else { dskoff = ((ta * MAXSECSD) + (sa - 1)) * 128; } //copy sector from image to RAM for (i=0; i<128; i++) { data = *(fbuf + (dskoff + i)); put_mbyte(ba + i, data); } sa++; ba+=0x80; nrptr++; } zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error zx200a.intff = 1; //set interrupt FF break; case DWRITE: //check for WP if(uptr->flags & UNIT_WPMODE) { zx200a.rtype = ROK; zx200a.rbyte0 = RB0WP; zx200a.intff = 1; //set interrupt FF sim_printf("\n zx200a: Write protect error 2 on drive %d", fddnum); return; } nrptr = 0; while(nrptr < nr) { //calculate offset into disk image if (zx200a.fdd[fddnum].dd == 1) { dskoff = ((ta * MAXSECDD) + (sa - 1)) * 128; } else { dskoff = ((ta * MAXSECSD) + (sa - 1)) * 128; } for (i=0; i<128; i++) { //copy sector from image to RAM data = get_mbyte(ba + i); *(fbuf + (dskoff + i)) = data; } sa++; ba+=0x80; nrptr++; } zx200a.rtype = ROK; zx200a.rbyte0 = 0; //set no error zx200a.intff = 1; //set interrupt FF break; default: sim_printf("\n zx200a: zx200a_diskio bad di=%02X", di & 0x07); break; } } /* end of zx-200a.c */