/* * Copyright (c) 2023 Anders Magnusson. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "sim_defs.h" #include "nd100_defs.h" /* * Floppy and Streamer Controller 3112. * ND documentation ND-11.021.1 * * Also support Floppy Controller 3027 (Z80 version). * ND documentation ND-11.015.1 * * Currently only floppies implemented (no streamer). * * The device uses eight IOX addresses, but the transfer commands * are given in a command block of 12 words in memory. * * ND uses three floppy formats (but in theory can handle a lot of formats). * - SS/SD 8" (type 0): 77*8*512= 315392 bytes. * - DS/DD 8" (type 017): 77*8*1024*2= 1261568 bytes. * - DS/DD 5 1/4 (type 017?): 80*8*1024*2= 1261568 bytes. */ t_stat floppy_svc(UNIT *uptr); t_stat floppy_reset (DEVICE *dptr); t_stat floppy_boot (int32 unitno, DEVICE *dptr); t_stat floppy_attach (UNIT *uptr, CONST char *cptr); static int floppy_excmd(void); static int floppy_test(UNIT *up); static int dtomem(FILE *fp, int daddr, int maddr, int wcnt, int how); static int getmval(void); #define FL_NTR 80 /* # tracks/side */ #define FL_NSC 8 /* # sectors/track */ #define FL_NSD 2 /* # sides */ #define FL_NBY 1024 /* # bytes/sector */ #define FL_SZ (FL_NTR*FL_NTR*FL_NSD*FL_NBY) /* hardware status reg flags */ #define FL_ST_IE 0000002 /* interrupt enable (RFT) */ #define FL_ST_ACT 0000004 /* device active */ #define FL_ST_RDY 0000010 /* device ready for transfer (RFT) */ #define FL_ST_ERR 0000020 /* OR of errors */ #define FL_ST_HE 0000100 /* Hard error (DMA) */ #define FL_ST_DENS 0140000 /* Dual density ctlr */ /* hardware control word */ #define FL_CW_IE 0000002 /* interrupt enable (RFT) */ #define FL_CW_AUTO 0000004 /* Activate autoload */ #define FL_CW_TEST 0000010 /* Test mode */ #define FL_CW_CLR 0000020 /* Device clear */ #define FL_CW_ENSTR 0000040 /* Enable streamer */ #define FL_CW_FCE 0000400 /* Fetch Command and Execute */ static int fl_rdata; /* devno + 0, read data */ static int fl_rstatus; /* devno + 2 (+4) read status */ static int fl_lcw; /* devno + 3 load control word */ static int fl_lph; /* devno + 5 load pointer high */ static int fl_lpl; /* devno + 7 load pointer low */ /* * The command block (CB) is DMAed from ND100 memory. * Word 0-5 are the command part, 06-13 are the status part. * * 15 8 7 0 * +---------------------------------------------+ * 0 | Command word | * +---------------------------------------------+ * 1 | Device address bit 15-0 | * +----------------------+----------------------+ * 2 | Device addr bit 23-16| Memory addr bit 23-16| * +----------------------+----------------------+ * 3 | Memory addr bit 15-0 | * +----------------------+----------------------+ * 4 | Options | Word count bit 23-16 | * +----------------------+----------------------+ * 5 | Word count (or record count) bit 15-0 | * +---------------------------------------------+ * 6 | Status 1 | * +---------------------------------------------+ * 7 | Status 2 | * +---------------------------------------------+ * 10 | Empty | Last addr 23-16 | * +---------------------------------------------+ * 11 | last memory address 15-0 | * +---------------------------------------------+ * 12 | Empty | Rem. words 23-16 | * +---------------------------------------------+ * 13 | Remaining words 15-0 | * +---------------------------------------------+ * */ /* CB offsets */ #define CB_CW 000 #define CB_DAL 001 #define CB_DAHMAH 002 #define CB_MAL 003 #define CB_OPTWCH 004 #define CB_WCL 005 #define CB_ST1 006 #define CB_ST2 007 #define CB_LAH 010 #define CB_LAL 011 #define CB_REMWH 012 #define CB_REMWL 013 /* Options word (004) */ #define CB_OPT_WC 0100000 /* set if word count in 4/5, else record */ /* Command word (000) */ #define CW_FL_RD 0000000 /* Read data */ #define CW_FL_WR 0000001 /* Write data */ #define CW_FL_EXTST 0000036 /* Read extended status */ #define CW_FL_RDFMT 0000042 /* Read format */ #define CW_FL_CMDMSK 077 /* mask for command */ #define CW_FL_SELSH 6 /* shift for unit */ #define CW_FL_1K 0001400 /* 1K sectors */ #define CW_FL_DS 0002000 /* Double sided */ #define CW_FL_DD 0004000 /* Double density */ /* Status 2 */ #define ST2_FL_BS1K 0000003 /* 1k sectors */ #define ST2_FL_DS 0000004 /* Double sided */ #define ST2_FL_DD 0000010 /* Double density */ #define ST2_FL_514 0000020 /* 5 1/4" floppy */ /* soft data structures (for simh) */ #define state u3 /* current unit state */ #define U_RDY 00 /* unit idling */ #define U_READ 01 /* unit reading */ #define U_WRITE 02 /* unit writing */ #define U_RDFMT 03 /* Read format */ #define U_EXTST 04 /* Read extended status */ #define devaddr u4 /* unit offset (in words) */ #define wcnt u5 /* word count */ #define memaddr u6 /* place in memory */ UNIT floppy_unit[] = { { UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, FL_SZ) }, { UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, FL_SZ) }, { UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, FL_SZ) }, { UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, FL_SZ) }, { 0 } }; REG floppy_reg[] = { { ORDATA (RDATA, fl_rdata, 16) }, { ORDATA (RSTATUS, fl_rstatus, 16) }, { ORDATA (LCW, fl_lcw, 16) }, { ORDATA (LPH, fl_lph, 16) }, { ORDATA (LPL, fl_lpl, 16) }, { NULL } }; MTAB floppy_mod[] = { { MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED", &set_writelock, &show_writelock, NULL, "Write enable floppy drive" }, { 0 } }; DEVICE floppy_dev = { "FLOPPY", floppy_unit, floppy_reg, floppy_mod, 1, 8, 12, 1, 8, 16, NULL, NULL, &floppy_reset, &floppy_boot, NULL, NULL, NULL, DEV_DISABLE }; struct intr floppy0_int = { 0, 021 }; /* * Floppy called via iox instruction. */ int iox_floppy(int addr) { int n; int rv = 0; switch (addr & 07) { case 0: /* read data */ regA = 0; break; case 2: regA = fl_rstatus; break; case 3: n = regA; if (n & FL_CW_TEST) { rv = floppy_test(&floppy_unit[0]); break; } if (n & FL_CW_FCE) { rv = floppy_excmd(); break; } if (n & FL_CW_IE) { /* Interrupt enable */ if ((fl_rstatus & (FL_CW_IE|FL_ST_RDY)) == FL_ST_RDY) extint(11, &floppy0_int); fl_rstatus |= FL_ST_IE; break; } if (n & FL_CW_CLR) { /* reset */ break; } return STOP_UNHIOX; break; case 5: fl_lph = regA; break; case 7: fl_lpl = regA; break; default: if ((addr & 1) == 0) regA = 0; break; /* Unused IOXes are ignored */ } return rv; } t_stat floppy_reset(DEVICE *dptr) { fl_rstatus = FL_ST_DENS | FL_ST_RDY; return 0; } /* * Estimate floppy format based on disk size and return status2 word. * - SS/SD = 315392 bytes, c/h/s 77/1/8 (512b sectors) * - DS/DD = 1261568 bytes, c/h/s 77/2/8 (1024b sectors) * - DS/DD 5 1/4" = 1310720 bytes, c/h/s 80/2/8 (1024b sectors) */ static int readfmt(UNIT *up) { if (sim_fseek(up->fileref, 0, SEEK_END) < 0) return -1; switch (sim_ftell(up->fileref)) { case 315392: return 0; case 1261568: return ST2_FL_DD|ST2_FL_DS|ST2_FL_BS1K; case 1310720: return /* ST2_FL_514| */ST2_FL_DD|ST2_FL_DS|ST2_FL_BS1K; default: break; } return -1; } t_stat floppy_svc(UNIT *uptr) { int cbaddr = getmval(); int lah = 0, lal = 0; int status2 = 0; int remwh = 0, remwl = 0; if ((fl_rstatus & FL_ST_ACT) == 0) return STOP_UNHIOX; switch (uptr->state) { case U_READ: dtomem(uptr->fileref, uptr->devaddr, uptr->memaddr, uptr->wcnt, PM_DMA); lah = (uptr->memaddr + uptr->wcnt) >> 16; lal = (uptr->memaddr + uptr->wcnt) & 0177777; break; case U_RDFMT: if ((status2 = readfmt(uptr)) < 0) goto err; break; case U_EXTST: /* XXX no docs found...? */ lah = (uptr->memaddr + uptr->wcnt) >> 16; lal = (uptr->memaddr + uptr->wcnt) & 0177777; remwh = 0100000; /* XXX */ remwl = 1; /* XXX */ pwrmem(uptr->memaddr, 06201, PM_DMA); /* XXX */ break; case U_WRITE: default: return STOP_UNHIOX; } pwrmem(cbaddr+CB_ST1, FL_ST_RDY, PM_DMA); pwrmem(cbaddr+CB_ST2, status2, PM_DMA); /* set after error or rdfmt */ pwrmem(cbaddr+CB_LAH, lah, PM_DMA); pwrmem(cbaddr+CB_LAL, lal, PM_DMA); pwrmem(cbaddr+CB_REMWH, remwh, PM_DMA); pwrmem(cbaddr+CB_REMWL, remwl, PM_DMA); fl_rstatus &= ~FL_ST_ACT; fl_rstatus |= FL_ST_RDY; if (fl_rstatus & FL_ST_IE) extint(11, &floppy0_int); return SCPE_OK; err: return STOP_UNHIOX; } /* * Get physical memory address for command block. */ static int getmval(void) { return fl_lpl + ((fl_lph & 0377) << 8); } /* * Read a block from file fp, position daddr (bytes) to ND100 memory maddr * wcnt words. * Return 0 or fail. */ static int dtomem(FILE *fp, int daddr, int maddr, int wcnt, int how) { unsigned char *wp; int bcnt = wcnt *2; int i; wp = malloc(wcnt * 2); /* bounce buffer */ if (sim_fseek(fp, daddr, SEEK_SET) < 0) return STOP_UNHIOX; if (sim_fread(wp, bcnt, 1, fp) < 0) return STOP_UNHIOX; for (i = 0; i < bcnt; i += 2) wrmem(maddr++, (wp[i] << 8) | wp[i+1], how); free(wp); return SCPE_OK; } t_stat floppy_boot(int32 unitno, DEVICE *dptr) { printf("floppy_boot \n"); return 1; } /* * It seems that some of the boot programs uses the Z80 test programs * on the controller card. We try to mimic the behaviour. * * This data structure (in ND100) is used in test T13 and T14. * * 15 8 7 0 * +---------------------------------------------+ * 0 | ND100 Load address | * +---------------------------------------------+ * 1 | Z80 address | * +---------------------------------------------+ * 2 | Byte count | * +---------------------------------------------+ */ static int floppy_test(UNIT *up) { int cbaddr = getmval(); int n100addr, z80addr, bcnt; int rv = 0; if ((regA & FL_CW_FCE) == 0) return STOP_UNHIOX; /* What to do? */ switch (regA >> 9) { case 016: /* T14, load from Z80 to ND100 */ n100addr = prdmem(cbaddr, PM_CPU); z80addr = prdmem(cbaddr+1, PM_CPU); bcnt = prdmem(cbaddr+2, PM_CPU); if (bcnt > 3584) return STOP_UNHIOX; printf("\r\n addr %06o nd100 %06o z80 %04x count %o\r\n", cbaddr, prdmem(cbaddr, PM_CPU), prdmem(cbaddr+1, PM_CPU), prdmem(cbaddr+2, PM_CPU)); dtomem(up->fileref, up->devaddr + z80addr - 0x2200, n100addr, bcnt/2, PM_CPU); break; default: return STOP_UNHIOX; } return rv; } /* * Execute. Fills in the unit local vars memaddr/wcnt/devaddr from the * command block and setup for interrupt later. */ static int floppy_excmd(void) { UNIT *unit; int cw, u, cmd, i; int cbaddr = getmval(); int status2, sectsz; cw = prdmem(cbaddr+CB_CW, PM_CPU); u = (cw >> CW_FL_SELSH) & 03; cmd = cw & CW_FL_CMDMSK; unit = &floppy_unit[u]; if ((unit->flags & UNIT_ATT) == 0) goto err; /* floppy not inserted */ status2 = readfmt(unit); sectsz = (status2 & ST2_FL_BS1K) == 0 ? 512 : 1024; /* XXX check disk size, word count etc... */ unit->memaddr = ((prdmem(cbaddr+CB_DAHMAH, PM_CPU) & 0377) << 16) | prdmem(cbaddr+CB_MAL, PM_CPU); i = prdmem(cbaddr+CB_OPTWCH, PM_CPU); unit->wcnt = ((i & 0377) << 16) | prdmem(cbaddr+CB_WCL, PM_CPU); if ((i & CB_OPT_WC) == 0) unit->wcnt *= (sectsz/2); unit->devaddr = (((prdmem(cbaddr+CB_DAHMAH, PM_CPU) & 0177400) << 8) | prdmem(cbaddr+CB_DAL, PM_CPU)) * sectsz; switch (cmd) { case CW_FL_RD: unit->state = U_READ; break; case CW_FL_WR: goto err; /* floppy write protected */ case CW_FL_EXTST: unit->state = U_EXTST; break; case CW_FL_RDFMT: unit->state = U_RDFMT; break; default: goto err; } sim_activate(&floppy_unit[u], 10); fl_rstatus &= ~FL_ST_RDY; fl_rstatus |= FL_ST_ACT; return SCPE_OK; err: return STOP_UNHIOX; }