/* zx-200a.c: Intel double density disk adapter 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 10H 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 07FH - Write - Reset diskette system. Operations: Recalibrate - Seek - Format Track - Write Data - Write Deleted Data - Read Data - Verify CRC - 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. */ #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 4 #define WP 0x40 /* Write protect */ #define RDY 0x20 /* Ready */ #define T0 0x10 /* Track 0 */ #define TS 0x08 /* Two sided */ /* internal function prototypes */ t_stat zx200a_svc (UNIT *uptr); uint8 zx200a0(t_bool io, uint8 data, uint8 devnum); uint8 zx200a1(t_bool io, uint8 data, uint8 devnum); uint8 zx200a2(t_bool io, uint8 data, uint8 devnum); uint8 zx200a3(t_bool io, uint8 data, uint8 devnum); uint8 zx200a7(t_bool io, uint8 data, uint8 devnum); t_stat zx200a_attach (UNIT *uptr, CONST char *cptr); t_stat zx200a_reset(DEVICE *dptr, uint16 base, uint8 devnum); void zx200a_reset1(void); /* external function prototypes */ extern uint8 reg_dev(uint8 (*routine)(t_bool, uint8, uint8), uint16 port, uint8 devnum); /* globals */ uint16 iopb; uint8 syssta = 0, rsttyp = 0, rstbyt1 = 0, rstbyt2 = 0, cmd = 0; uint8 *zx200a_buf[FDD_NUM] = { /* FDD buffer pointers */ NULL, NULL, NULL, NULL }; int32 fddst[FDD_NUM] = { // fdd status 0, // status of FDD 0 0, // status of FDD 1 0, // status of FDD 2 0 // status of FDD 3 }; int32 maxsec[FDD_NUM] = { // last sector 0, // status of FDD 0 0, // status of FDD 1 0, // status of FDD 2 0 // status of FDD 3 }; int8 maxcyl[FDD_NUM] = { 0, // last cylinder + 1 of FDD 0 0, // last cylinder + 1 of FDD 1 0, // last cylinder + 1 of FDD 2 0 // last cylinder + 1 of FDD 3 }; UNIT zx200a_unit[] = { { UDATA (&zx200a_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, { UDATA (&zx200a_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, { UDATA (&zx200a_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, { UDATA (&zx200a_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 } }; REG zx200a_reg[] = { { HRDATA (SUBSYSSTA, zx200a_unit[0].u3, 8) }, /* subsytem status */ { HRDATA (RSTTYP, zx200a_unit[0].u4, 8) }, /* result type */ { HRDATA (RSTBYT0, zx200a_unit[0].u5, 8) }, /* result byte 0 RSTTYP = 0*/ { HRDATA (RSTBYT1, zx200a_unit[0].u6, 8) }, /* result byte 1 RSTTYP = 10*/ { NULL } }; DEBTAB zx200a_debug[] = { { "ALL", DEBUG_all }, { "FLOW", DEBUG_flow }, { "READ", DEBUG_read }, { "WRITE", DEBUG_write }, { "XACK", DEBUG_xack }, { "LEV1", DEBUG_level1 }, { "LEV2", DEBUG_level2 }, { 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 NULL, //modifiers 1, //numunits 16, //aradix 16, //awidth 1, //aincr 16, //dradix 8, //dwidth NULL, //examine NULL, //deposit // &zx200a_reset, //reset NULL, //reset NULL, //boot NULL, //attach NULL, //detach NULL, //ctxt 0, //flags 0, //dctrl zx200a_debug, //debflags NULL, //msize NULL //lname }; /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. */ /* Service routines to handle simulator functions */ /* service routine - actually does the simulated disk I/O */ t_stat zx200a_svc (UNIT *uptr) { sim_activate(&zx200a_unit[uptr->u6], zx200a_unit[uptr->u6].wait); return SCPE_OK; } /* zx200a control port functions */ uint8 zx200a0(t_bool io, uint8 data, uint8 devnum) { int val; if (io == 0) { /* read operation */ val = zx200a_unit[0].u3; sim_printf(" zx200a0: read data=%02X devnum=%02X val=%02X\n", data, devnum, val); return val; } else { /* write control port */ sim_printf(" zx200a0: write data=%02X port=%02X\n", data, devnum); } } uint8 zx200a1(t_bool io, uint8 data, uint8 devnum) { int val; if (io == 0) { /* read operation */ val = zx200a_unit[0].u4; sim_printf(" zx200a1: read data=%02X devnum=%02X val=%02X\n", data, devnum, val); return val; } else { /* write control port */ iopb = data; sim_printf(" zx200a1: write data=%02X port=%02X iopb=%04X\n", data, devnum, iopb); } } uint8 zx200a2(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read operation */ sim_printf(" zx200a2: read data=%02X devnum=%02X\n", data, devnum); return 0x00; } else { /* write control port */ iopb |= (data << 8); sim_printf(" zx200a2: write data=%02X port=%02X iopb=%04X\n", data, devnum, iopb); } } uint8 zx200a3(t_bool io, uint8 data, uint8 devnum) { int val; if (io == 0) { /* read operation */ if (zx200a_unit[0].u4) val = zx200a_unit[0].u5; else val = zx200a_unit[0].u6; sim_printf(" zx200a3: read data=%02X devnum=%02X val=%02X\n", data, devnum, val); return val; } else { /* write control port */ sim_printf(" zx200a3: write data=%02X port=%02X\n", data, devnum); } } /* reset ZX-200A */ uint8 zx200a7(t_bool io, uint8 data, uint8 devnum) { if (io == 0) { /* read operation */ sim_printf(" zx200a7: read data=%02X devnum=%02X\n", data, devnum); return 0x00; } else { /* write control port */ sim_printf(" zx200a7: write data=%02X port=%02X\n", data, devnum); zx200a_reset(NULL, ZX200A_BASE_DD, 0); //for now } } /* zx200a attach - attach an .IMG file to a FDD */ t_stat zx200a_attach (UNIT *uptr, CONST char *cptr) { t_stat r; FILE *fp; int32 i, c = 0; long flen; sim_debug (DEBUG_flow, &zx200a_dev, " zx200a_attach: Entered with uptr=%08X cptr=%s\n", uptr, cptr); if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { sim_printf(" zx200a_attach: Attach error\n"); return r; } fp = fopen(uptr->filename, "rb"); if (fp == NULL) { sim_printf(" Unable to open disk image file %s\n", uptr->filename); sim_printf(" No disk image loaded!!!\n"); } else { sim_printf("zx200a: Attach\n"); fseek(fp, 0, SEEK_END); /* size disk image */ flen = ftell(fp); fseek(fp, 0, SEEK_SET); if (zx200a_buf[uptr->u6] == NULL) { /* no buffer allocated */ zx200a_buf[uptr->u6] = (uint8 *)malloc(flen); if (zx200a_buf[uptr->u6] == NULL) { sim_printf(" zx200a_attach: Malloc error\n"); return SCPE_MEM; } } uptr->capac = flen; i = 0; c = fgetc(fp); // copy disk image into buffer while (c != EOF) { *(zx200a_buf[uptr->u6] + i++) = c & 0xFF; c = fgetc(fp); } fclose(fp); switch(uptr->u6){ case 0: fddst[uptr->u6] |= 0x01; /* set unit ready */ break; case 1: fddst[uptr->u6] |= 0x02; /* set unit ready */ break; case 2: fddst[uptr->u6] |= 0x20; /* set unit ready */ break; case 3: fddst[uptr->u6] |= 0x40; /* set unit ready */ break; } if (flen == 256256) { /* 8" 256K SSSD */ maxcyl[uptr->u6] = 77; maxsec[uptr->u6] = 26; } else if (flen == 512512) { /* 8" 512K SSDD */ maxcyl[uptr->u6] = 77; maxsec[uptr->u6] = 52; } sim_printf(" Drive-%d: %d bytes of disk image %s loaded, fddst=%02X\n", uptr->u6, i, uptr->filename, fddst[uptr->u6]); } sim_debug (DEBUG_flow, &zx200a_dev, " zx200a_attach: Done\n"); return SCPE_OK; } /* Reset routine */ t_stat zx200a_reset(DEVICE *dptr, uint16 base, uint8 devnum) { reg_dev(zx200a0, base, devnum); reg_dev(zx200a1, base + 1, devnum); reg_dev(zx200a2, base + 2, devnum); reg_dev(zx200a3, base + 3, devnum); reg_dev(zx200a7, base + 7, devnum); zx200a_unit[devnum].u3 = 0x00; /* ipc reset */ sim_printf(" zx200a-%d: Reset\n", devnum); sim_printf(" zx200a-%d: Registered at %04X\n", devnum, base); if ((zx200a_dev.flags & DEV_DIS) == 0) zx200a_reset1(); return SCPE_OK; } void zx200a_reset1(void) { int32 i; UNIT *uptr; static int flag = 1; if (flag) sim_printf("ZX-200A: Initializing\n"); for (i = 0; i < FDD_NUM; i++) { /* handle all units */ uptr = zx200a_dev.units + i; if (uptr->capac == 0) { /* if not configured */ // sim_printf(" ZX-200A%d: Not configured\n", i); // if (flag) { // sim_printf(" ALL: \"set ZX-200A en\"\n"); // sim_printf(" EPROM: \"att ZX-200A0 \"\n"); // flag = 0; // } uptr->capac = 0; /* initialize unit */ uptr->u3 = 0; uptr->u4 = 0; uptr->u5 = 0; uptr->u6 = i; /* unit number - only set here! */ fddst[i] = WP + T0 + i; /* initial drive status */ uptr->flags |= UNIT_WPMODE; /* set WP in unit flags */ sim_activate (&zx200a_unit[uptr->u6], zx200a_unit[uptr->u6].wait); } else { fddst[i] = RDY + WP + T0 + i; /* initial attach drive status */ // sim_printf(" SBC208%d: Configured, Attached to %s\n", i, uptr->filename); } } cmd = 0; /* clear command */ flag = 0; } /* end of zx-200a.c */