diff options
| author | Harley Travis <harleytravis123@outlook.com> | 2026-05-27 18:04:13 -0500 |
|---|---|---|
| committer | Harley Travis <harleytravis123@outlook.com> | 2026-05-27 18:04:13 -0500 |
| commit | 32673d5872a2c77c2cc70a14a1e50cd440bc0180 (patch) | |
| tree | 568fb469e295bbb69d166765d4712c21f77568fc /Kernel/KFloppy.HC | |
| parent | b223d23c0165fccf014a4e8c0ed1a21425be7100 (diff) | |
| download | templeos-floppy-driver-32673d5872a2c77c2cc70a14a1e50cd440bc0180.tar.gz | |
Refactor source code and create an "installer"
I have moved some source files into subdirectories for ease of
installation. I have also added an installer script to automatically
place driver files into their respective system directories.
Diffstat (limited to 'Kernel/KFloppy.HC')
| -rw-r--r-- | Kernel/KFloppy.HC | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/Kernel/KFloppy.HC b/Kernel/KFloppy.HC new file mode 100644 index 0000000..16b6bd3 --- /dev/null +++ b/Kernel/KFloppy.HC @@ -0,0 +1,482 @@ +/* + New, (hopefully) Less Messy Floppy Driver + Copyright (C) 2025-2026 Harley Travis <yoshi128k@gmail.com>. + This software (including source code) is licensed under the BSD Zero Clause + License. See the Copying.TXT file 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, buf_lo); // buf low byte + Sleep(1); + OutU8(0x04, buf_hi); // 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 + // 2nd param: + // Implied seek on, FIFO on, Drive polling disabled, threshold = 12 + FDCSendCmd(base, 0b01011011); + 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); + 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 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); +} |
