From 04d039d43164f48f5266d32e783d11f166b6161c Mon Sep 17 00:00:00 2001 From: Harley Travis Date: Sat, 23 May 2026 23:17:07 -0500 Subject: Initial commit (TOSFloppy-2025-04-17.zip) This is taken from the earliest Discord release of the TempleOS floppy disk driver, released on April 17, 2025. --- NewFloppy.HC | 477 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 NewFloppy.HC (limited to 'NewFloppy.HC') diff --git a/NewFloppy.HC b/NewFloppy.HC new file mode 100644 index 0000000..9e67467 --- /dev/null +++ b/NewFloppy.HC @@ -0,0 +1,477 @@ +/* + New, (hopefully) Less Messy Floppy Driver + Copyright (C) 2025 Yoshi128k. + Licensed under version 2 of the Do What The Fuck You Want To Public License + See COPYING for details. +*/ + +Bool FDCIrq = FALSE; + +U0 FDCDMAInit(U16 len) +{ + U64 /*buf_lo, buf_hi, page,*/ cnt_lo, cnt_hi; + +// buf_lo = &FDC_DMA & 0xFF; +// buf_hi = &FDC_DMA >> 8; +// page = &FDC_DMA >> 16; + cnt_lo = (len - 1) & 0xFF; + cnt_hi = (len - 1) >> 8; + + OutU8(0x0A, 6); // mask ch 0 and 2 + Sleep(1); + OutU8(0x0C, -1); // reset flip flop + Sleep(1); + OutU8(0x04, 0x75); // buf low byte + Sleep(1); + OutU8(0x04, 0x9F); // buf high byte + Sleep(1); + OutU8(0x0C, -1); // reset flip flop again + Sleep(1); + OutU8(0x05, cnt_lo); // cntr low byte + Sleep(1); + OutU8(0x05, cnt_hi); // cntr high byte + Sleep(1); + OutU8(0x81, 0); // page + Sleep(1); + OutU8(0x0A, 2); // unmask ch 0 and 2 + Sleep(1); +} + +U0 FDCDMAPrepWrite() +{ + OutU8(0x0A, 6); // mask ch 0 and 2 + Sleep(1); + OutU8(0x0B, 0x5A); // single xfer, addr inc, auto init, write, ch 2 + Sleep(1); + OutU8(0x0A, 2); // unmask ch 0 and 2 + Sleep(1); +} + +U0 FDCDMAPrepRead() +{ + OutU8(0x0A, 6); // mask ch 0 and 2 + Sleep(1); + OutU8(0x0B, 0x56); // single xfer, addr inc, auto init, read, ch 2 + Sleep(1); + OutU8(0x0A, 2); // unmask ch 0 and 2 + Sleep(1); +} + +interrupt U0 FDCIrqHandler() { + OutU8(0x20, 0x20); // EOI + FDCIrq = TRUE; +} + +U16 fdc_base = 0x03F0; + +static U8 *fd_types[8] = { + "none", + "360kB 5.25\"", + "1.2MB 5.25\"", + "720kB 3.5\"", + + "1.44MB 3.5\"", + "2.88MB 3.5\"", + "unknown type", + "unknown type" +}; + +U0 CMOSGetFloppyDrives() +{ + OutU8(0x70, 0x10); + Sleep(1); + + U64 drives = InU8(0x71); + + AdamLog("Floppy Drive 0: %s\n", fd_types[drives >> 4]); + AdamLog("Floppy Drive 1: %s\n", fd_types[drives & 0xF]); +} + +U0 FDCSendCmd(U16 base, U8 cmd) +{ + // Send a command to the floppy controller + + // 60 sec timeout + U64 i; + for (i = 0; i < 600; i++) { + Sleep(10); + if (0x80 & InU8(base + FDC_MSR_DSR)) { + OutU8(base + FDC_DATA, cmd); + Sleep(1); + return; + } + } + AdamErr("FDC Command TimeOut"); +} + +U8 FDCReadData(U16 base) +{ + // Read data from the floppy controller + + // 60 sec timeout + U64 i; + for (i=0;i<600;i++) { + Sleep(10); + if (0x80 & InU8(base + FDC_MSR_DSR)) { + return InU8(base + FDC_DATA); + } + } + AdamErr("FDC Read TimeOut"); +} + +U0 FDCCheckInt(U16 base, U8 *st0, U8 *cyl) +{ + FDCSendCmd(base, FDC_SENSE_INTR); + + *st0 = FDCReadData(base); + *cyl = FDCReadData(base); +} + +I16 fdc_mtr_ticks = 0; +U8 fdc_mtr_state = FDC_MOTOR_OFF; + +U0 FDCMotorOff(U16 base) +{ + OutU8(base + FDC_DOR, 0x0C); + fdc_mtr_state = FDC_MOTOR_OFF; +} + +U0 FDCMotorPwrOffTimer() +{ + // You're supposed to run this function as a separate CTask. It will exit when the motor + // exits the "wait" state (either because it has turned off or because something needs it to be on). + // The driver only spawns this when the motor is moved from "on" to "wait". + while (fdc_mtr_state == FDC_MOTOR_WAIT) { + // Sanity check for if something wants the motor on at the last second + if (fdc_mtr_state == FDC_MOTOR_ON) break; + + Sleep(500); + + fdc_mtr_ticks -= 50; + if (fdc_mtr_ticks <= 0) { + FDCMotorOff(fdc_base); + } + Yield; // Give control back to the scheduler + } +} + +U0 FDCMotorCtrl(U16 base, Bool onoff) +{ + U64 prev_mtr_state; + if (onoff) { + if (!fdc_mtr_state) { + // Turn on motor + OutU8(base + FDC_DOR, 0x1C); + Sleep(500); // Wait 500 ms to allow drive to spin up + } + fdc_mtr_state = FDC_MOTOR_ON; + } else { + prev_mtr_state = fdc_mtr_state; + if (fdc_mtr_state == FDC_MOTOR_WAIT) { + AdamLog("FDC: Motor PowerOff Already Pending\n"); + } + fdc_mtr_ticks = 300; // 3 sec timeout before motor turns off + fdc_mtr_state = FDC_MOTOR_WAIT; + // Only spawn the timer task of the motor is currently on; infinitely-looping tasks peg the CPU + if (prev_mtr_state != FDC_MOTOR_WAIT) Spawn(&FDCMotorPwrOffTimer,,"FDCMotorPwrOffTimer"); + } +} + +I8 FDCRecalibrate(U16 base) +{ + FDCIrq = FALSE; + + U64 i; + U8 st0, cyl; + + FDCMotorCtrl(base, FDC_MOTOR_ON); + + for (i=0;i<10;i++) { + FDCSendCmd(base, FDC_RECALIBRATE); + FDCSendCmd(base, 0); + + // Wait for an interrupt, then get the status + while (!FDCIrq) Yield; + FDCCheckInt(base, &st0, &cyl); + AdamLog("ST0: %d Cyl: %d\n",st0,cyl); + + if (st0 & 0xC0) { + static U8 * status[4] = {0, "error", "invalid", "drive"}; + AdamLog("Calibration Status: %s\n", status[st0 >> 6]); + } + + if (!cyl) { + FDCMotorCtrl(base, FDC_MOTOR_OFF); + return 0; + } + } + + AdamErr("FDC ReCalibrate TimeOut"); + FDCMotorCtrl(base, FDC_MOTOR_OFF); + return -1; +} + +I8 FDCReset(U16 base) +{ + FDCIrq = FALSE; + + OutU8(base + FDC_DOR, 0x00); + Sleep(1); + OutU8(base + FDC_DOR, 0x0C); + Sleep(1); + + while (!FDCIrq) Yield; + + // Ignore this + U64 i, lck; + U8 st0, cyl; + for (i=0;i<4;i++) { + FDCCheckInt(base, &st0, &cyl); + } + + // Set xfer rate to 500kbps + OutU8(base + FDC_CCR_DIR, 0x00); + Sleep(1); + + // Set the mechanical params and disable DMA (Terry didn't use it) + FDCSendCmd(base, FDC_SPECIFY); + FDCSendCmd(base, 0b10000000); // SRT = 8 (8 ms), HUT = 0 (256 ms) + FDCSendCmd(base, 0b00011110); // HLT = 15 (30 ms), NDMA = 0 (DMA enabled) + + // Configure the FIFO + FDCSendCmd(base, FDC_CONFIGURE); + FDCSendCmd(base, 0x00); // 1st param is a 0 + FDCSendCmd(base, 0b01011011); // 2nd param: Implied seek on, FIFO on, Drive polling disabled, threshold = 12 + FDCSendCmd(base, 0x00); // 3rd param: write precomp = 0 + + // Lock the configuration + FDCSendCmd(base, 0x94); + lck = FDCReadData(base); // Result: lock status + + // contingency + if (FDCRecalibrate(base)) return -1; + + return 0; +} + +I8 FDCSeek(U16 base, U8 cyli, U8 head) +{ + U64 i; + U8 st0, cyl; + FDCIrq = FALSE; + + FDCMotorCtrl(base, FDC_MOTOR_ON); + + for (i=0;i<10;i++) { + // Attempt to move to given cyl + // 1: X X X X X HD D1 D0 + // 2: Cyl No + FDCSendCmd(base, FDC_SEEK); + FDCSendCmd(base, head<<2); + FDCSendCmd(base, cyli); + + while (!FDCIrq) Yield; + FDCCheckInt(base, &st0, &cyl); + AdamLog("ST0: %d Cyl: %d\n",st0,cyl); + + if (st0 & 0xC0) { + static U8 * status[4] = {0, "error", "invalid", "drive"}; + AdamLog("Seek Status: %s\n", status[st0 >> 6]); + } + + if (cyl == cyli) { + FDCMotorCtrl(base, FDC_MOTOR_OFF); + return 0; + } + } + + AdamErr("FDC Seek TimeOut"); + FDCMotorCtrl(base, FDC_MOTOR_OFF); + return -1; +} + +U0 FDCRead(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen) +{ + // Read data from disk (single-track) + static U8 flags; + flags = 0x40; // Multi-track off, MFM modulation + static U64 cmd; + cmd = FDC_READ_DATA | flags; + FDCIrq = FALSE; + + U64 st0, st1, st2, rcyl, rhd, rsect, rsectsize; + + FDCMotorCtrl(base, FDC_MOTOR_ON); + + AdamLog("Cmd: %d\nParams: %d %d %d %d %d %d %d %d\n",cmd,head<<2,cyl,head,sect,2,trklen,0x1B,0xFF); + + // Prepare the DMA controller + AdamLog("Preparing DMA\n"); + FDCDMAInit(512*(trklen - sect + 1)); + FDCDMAPrepRead(); + + FDCSendCmd(base, cmd); + FDCSendCmd(base, head << 2); // Drive 0, specified head + FDCSendCmd(base, cyl); + FDCSendCmd(base, head); + FDCSendCmd(base, sect); + FDCSendCmd(base, 2); // Sector size = 2 = 512 bytes + FDCSendCmd(base, trklen); // Track Length/Max Sector No. + FDCSendCmd(base, 0x1B); // GAP3 Length = 27 (3.5") + FDCSendCmd(base, 0xFF); // Data Length (irrelevant) + + // Wait for xfer to complete + while (!FDCIrq) Yield; + + st0 = FDCReadData(base); + st1 = FDCReadData(base); + st2 = FDCReadData(base); + rcyl = FDCReadData(base); + rhd = FDCReadData(base); + rsect = FDCReadData(base); + rsectsize = FDCReadData(base); + + AdamLog("Read Results:\nST0: %d\nST1: %d\nST2: %d\nCyl: %d\nHead: %d\nSect: %d\nSect Size: %d\n",st0,st1,st2,rcyl,rhd,rsect,rsectsize); + + FDCMotorCtrl(base, FDC_MOTOR_OFF); +} + + +U0 FDCReadMulti(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen) +{ + // Read data from disk (multi-track) + static U8 flags; + flags = 0xC0; // Multi-track on, MFM modulation + static U64 cmd; + cmd = FDC_READ_DATA | flags; + FDCIrq = FALSE; + + U64 st0, st1, st2, rcyl, rhd, rsect, rsectsize; + + FDCMotorCtrl(base, FDC_MOTOR_ON); + + AdamLog("Cmd: %d\nParams: %d %d %d %d %d %d %d %d\n",cmd,head<<2,cyl,head,sect,2,trklen,0x1B,0xFF); + + // Prepare the DMA controller + AdamLog("Preparing DMA\n"); + FDCDMAInit(512*(trklen - sect + 1)*2); + FDCDMAPrepRead(); + + FDCSendCmd(base, cmd); + FDCSendCmd(base, head << 2); // Drive 0, specified head + FDCSendCmd(base, cyl); + FDCSendCmd(base, head); + FDCSendCmd(base, sect); + FDCSendCmd(base, 2); // Sector size = 2 = 512 bytes + FDCSendCmd(base, trklen); // Track Length/Max Sector No. + FDCSendCmd(base, 0x1B); // GAP3 Length = 27 (3.5") + FDCSendCmd(base, 0xFF); // Data Length (irrelevant) + + // Wait for the xfer to complete + while (!FDCIrq) Yield; + + st0 = FDCReadData(base); + st1 = FDCReadData(base); + st2 = FDCReadData(base); + rcyl = FDCReadData(base); + rhd = FDCReadData(base); + rsect = FDCReadData(base); + rsectsize = FDCReadData(base); + + AdamLog("Read Results:\nST0: %d\nST1: %d\nST2: %d\nCyl: %d\nHead: %d\nSect: %d\nSect Size: %d\n",st0,st1,st2,rcyl,rhd,rsect,rsectsize); + + FDCMotorCtrl(base, FDC_MOTOR_OFF); +} + +U0 FDCWrite(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen) +{ + // Write data to disk (single-track) + static U8 flags; + flags = 0x40; // Multi-track off, MFM modulation + static U64 cmd; + cmd = FDC_WRITE_DATA | flags; + FDCIrq = FALSE; + + U64 st0, st1, st2, rcyl, rhd, rsect, rsectsize; + + FDCMotorCtrl(base, FDC_MOTOR_ON); + + AdamLog("Cmd: %d\nParams: %d %d %d %d %d %d %d %d\n",cmd,head<<2,cyl,head,sect,2,trklen,0x1B,0xFF); + + // Prepare the DMA controller + AdamLog("Preparing DMA\n"); + FDCDMAInit(512*(trklen - sect + 1)); + FDCDMAPrepWrite(); + + FDCSendCmd(base, cmd); + FDCSendCmd(base, head << 2); // Drive 0, specified head + FDCSendCmd(base, cyl); + FDCSendCmd(base, head); + FDCSendCmd(base, sect); + FDCSendCmd(base, 2); // Sector size = 2 = 512 bytes + FDCSendCmd(base, trklen); // Track Length/Max Sector No. + FDCSendCmd(base, 0x1B); // GAP3 Length = 27 (3.5") + FDCSendCmd(base, 0xFF); // Data Length (irrelevant) + + // Wait for xfer to complete + while (!FDCIrq) Yield; + + st0 = FDCReadData(base); + st1 = FDCReadData(base); + st2 = FDCReadData(base); + rcyl = FDCReadData(base); + rhd = FDCReadData(base); + rsect = FDCReadData(base); + rsectsize = FDCReadData(base); + + AdamLog("Read Results:\nST0: %d\nST1: %d\nST2: %d\nCyl: %d\nHead: %d\nSect: %d\nSect Size: %d\n",st0,st1,st2,rcyl,rhd,rsect,rsectsize); + + FDCMotorCtrl(base, FDC_MOTOR_OFF); +} + + +U0 FDCWriteMulti(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen) +{ + // Write data to disk (multi-track) + static U8 flags; + flags = 0xC0; // Multi-track on, MFM modulation + static U64 cmd; + cmd = FDC_WRITE_DATA | flags; + FDCIrq = FALSE; + + U64 st0, st1, st2, rcyl, rhd, rsect, rsectsize; + + FDCMotorCtrl(base, FDC_MOTOR_ON); + + AdamLog("Cmd: %d\nParams: %d %d %d %d %d %d %d %d\n",cmd,head<<2,cyl,head,sect,2,trklen,0x1B,0xFF); + + // Prepare the DMA controller + AdamLog("Preparing DMA\n"); + FDCDMAInit(512*(trklen - sect + 1)*2); + FDCDMAPrepRead(); + + FDCSendCmd(base, cmd); + FDCSendCmd(base, head << 2); // Drive 0, specified head + FDCSendCmd(base, cyl); + FDCSendCmd(base, head); + FDCSendCmd(base, sect); + FDCSendCmd(base, 2); // Sector size = 2 = 512 bytes + FDCSendCmd(base, trklen); // Track Length/Max Sector No. + FDCSendCmd(base, 0x1B); // GAP3 Length = 27 (3.5") + FDCSendCmd(base, 0xFF); // Data Length (irrelevant) + + // Wait for the xfer to complete + while (!FDCIrq) Yield; + + st0 = FDCReadData(base); + st1 = FDCReadData(base); + st2 = FDCReadData(base); + rcyl = FDCReadData(base); + rhd = FDCReadData(base); + rsect = FDCReadData(base); + rsectsize = FDCReadData(base); + + AdamLog("Read Results:\nST0: %d\nST1: %d\nST2: %d\nCyl: %d\nHead: %d\nSect: %d\nSect Size: %d\n",st0,st1,st2,rcyl,rhd,rsect,rsectsize); + + FDCMotorCtrl(base, FDC_MOTOR_OFF); +} \ No newline at end of file -- cgit v1.2.3