| /* 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 <filename>\"\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 */ |