From 32673d5872a2c77c2cc70a14a1e50cd440bc0180 Mon Sep 17 00:00:00 2001 From: Harley Travis Date: Wed, 27 May 2026 18:04:13 -0500 Subject: 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. --- ChangeLog.TXT | 9 + DskImg.HC | 73 -------- Examples/DskImg.HC | 73 ++++++++ Install.HC | 19 ++ KFloppy.HC | 482 --------------------------------------------------- KFloppyA.HH | 137 --------------- KFloppyB.HH | 25 --- KFloppyDMA.HC | 7 - Kernel/KFloppy.HC | 482 +++++++++++++++++++++++++++++++++++++++++++++++++++ Kernel/KFloppyA.HH | 137 +++++++++++++++ Kernel/KFloppyB.HH | 25 +++ Kernel/KFloppyDMA.HC | 7 + 12 files changed, 752 insertions(+), 724 deletions(-) delete mode 100644 DskImg.HC create mode 100644 Examples/DskImg.HC create mode 100644 Install.HC delete mode 100644 KFloppy.HC delete mode 100644 KFloppyA.HH delete mode 100644 KFloppyB.HH delete mode 100644 KFloppyDMA.HC create mode 100644 Kernel/KFloppy.HC create mode 100644 Kernel/KFloppyA.HH create mode 100644 Kernel/KFloppyB.HH create mode 100644 Kernel/KFloppyDMA.HC diff --git a/ChangeLog.TXT b/ChangeLog.TXT index 01b9808..ab636b5 100644 --- a/ChangeLog.TXT +++ b/ChangeLog.TXT @@ -1,3 +1,12 @@ +2026-05-27 (#2): +I have restructured the source tree in preparation for future work: + +* The kernel code (KFloppy*) has been moved into /Kernel. +* The disk dumper example has been moved into /Examples. + +I have also written an installer script that will get all of the files in +place. It also has instructions on what #includes need to be added. + 2026-05-27: I, Harley Travis, as the copyright holder of all current source code written as part of this TempleOS floppy disk controller project, have made the diff --git a/DskImg.HC b/DskImg.HC deleted file mode 100644 index 7aec469..0000000 --- a/DskImg.HC +++ /dev/null @@ -1,73 +0,0 @@ -#define CYLINDERS 80 -#define SECTORS 18 -#define BASE 0x03F0 -#define IMG_SIZE 1474560 - -/* - Copyright (C) 2025-2026 Harley Travis . - This software (including source code) is licensed under the BSD Zero Clause - License. See the Copying.TXT file for details. -*/ - -/* - - Make sure IRQ6 is on and - pointed to the correct handler! - -*/ - -U8 DskImg() -{ - U64 i; - - // Open image file for writing - CFile *img = FOpen("Floppy.IMG","w",IMG_SIZE/BLK_SIZE); - - // Initialize the controller - FDCReset(BASE); - Sleep(100); - - /* - - Loop through each sector, - reading each into the buffer, - then writing it into the file - - */ - - for (i = 0; i < CYLINDERS; i++) { - "Track %d\n", i; - if (FDCSeek(BASE, i, 0)) { - FDCReset(BASE); - FClose(img); - return 1; - } - if (FDCSeek(BASE, i, 1)) { - FDCReset(BASE); - FClose(img); - return 1; - } - FDCReadMulti(BASE, i, 0, 1, 18); - FBlkWrite(img, &FDC_DMA, 36 * i, 36); - } - - // Return to track 0 - "Returning to track 0...\n"; - if (FDCSeek(BASE, 0, 0)) { - FDCReset(BASE); - FClose(img); - return 1; - } - if (FDCSeek(BASE, 0, 1)) { - FDCReset(BASE); - FClose(img); - return 1; - } - - // Close the image file - FClose(img); - - return 0; -} - -DskImg; diff --git a/Examples/DskImg.HC b/Examples/DskImg.HC new file mode 100644 index 0000000..7aec469 --- /dev/null +++ b/Examples/DskImg.HC @@ -0,0 +1,73 @@ +#define CYLINDERS 80 +#define SECTORS 18 +#define BASE 0x03F0 +#define IMG_SIZE 1474560 + +/* + Copyright (C) 2025-2026 Harley Travis . + This software (including source code) is licensed under the BSD Zero Clause + License. See the Copying.TXT file for details. +*/ + +/* + + Make sure IRQ6 is on and + pointed to the correct handler! + +*/ + +U8 DskImg() +{ + U64 i; + + // Open image file for writing + CFile *img = FOpen("Floppy.IMG","w",IMG_SIZE/BLK_SIZE); + + // Initialize the controller + FDCReset(BASE); + Sleep(100); + + /* + + Loop through each sector, + reading each into the buffer, + then writing it into the file + + */ + + for (i = 0; i < CYLINDERS; i++) { + "Track %d\n", i; + if (FDCSeek(BASE, i, 0)) { + FDCReset(BASE); + FClose(img); + return 1; + } + if (FDCSeek(BASE, i, 1)) { + FDCReset(BASE); + FClose(img); + return 1; + } + FDCReadMulti(BASE, i, 0, 1, 18); + FBlkWrite(img, &FDC_DMA, 36 * i, 36); + } + + // Return to track 0 + "Returning to track 0...\n"; + if (FDCSeek(BASE, 0, 0)) { + FDCReset(BASE); + FClose(img); + return 1; + } + if (FDCSeek(BASE, 0, 1)) { + FDCReset(BASE); + FClose(img); + return 1; + } + + // Close the image file + FClose(img); + + return 0; +} + +DskImg; diff --git a/Install.HC b/Install.HC new file mode 100644 index 0000000..00aa318 --- /dev/null +++ b/Install.HC @@ -0,0 +1,19 @@ +/* + Copyright (C) 2026 Harley Travis . + This software (including source code) is licensed under the BSD Zero Clause + License. See the Copying.TXT file for details. +*/ + +// This program semi-automates the floppy driver install process. + +CopyTree("./Kernel/", "::/Kernel/") + +"The driver files have now been copied to your Kernel directory\n"; +"To complete the installation:\n\n"; +"in ::/Kernel/Kernel.PRJ:\n" +"* Include \"KFloppyDMA\" after \"KStart32\".\n"; +"* Include \"KFloppyA.HH\" and \"KFloppy\" (in that order) after \"KMain\"\n\n"; +"in ::/StartOS.HC:\n" +"* Include \"KFloppyB.HH\" after \"CompilerB.HH\"\n\n"; +"Then, rebuild the kernel with BootHDIns().\n\n"; +"Refer to ReadMe.TXT for further instructions. Good luck!\n"; diff --git a/KFloppy.HC b/KFloppy.HC deleted file mode 100644 index 16b6bd3..0000000 --- a/KFloppy.HC +++ /dev/null @@ -1,482 +0,0 @@ -/* - New, (hopefully) Less Messy Floppy Driver - Copyright (C) 2025-2026 Harley Travis . - 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); -} diff --git a/KFloppyA.HH b/KFloppyA.HH deleted file mode 100644 index e7065b6..0000000 --- a/KFloppyA.HH +++ /dev/null @@ -1,137 +0,0 @@ -/* - Copyright (C) 2025-2026 Harley Travis . - This software (including source code) is licensed under the BSD Zero Clause - License. See the Copying.TXT file for details. -*/ - -// FDC uses IRQ6 = int 26h -#define I_FDC 0x26 -#define IRQ_FDC 6 - -// FDC regs (primary ctrlr) -#define FDC_STAT_A 0x0 -#define FDC_STAT_B 0x1 -#define FDC_DOR 0x2 -#define FDC_TAPE 0x3 -#define FDC_MSR_DSR 0x4 -#define FDC_DATA 0x5 -#define FDC_CCR_DIR 0x7 - -// FDC cmds -#define FDC_READ_TRACK 0x02 -#define FDC_SPECIFY 0x03 -#define FDC_SENSE_STAT 0x04 -#define FDC_WRITE_DATA 0x05 -#define FDC_READ_DATA 0x06 -#define FDC_RECALIBRATE 0x07 -#define FDC_SENSE_INTR 0x08 -#define FDC_WRITE_DEL 0x09 -#define FDC_READ_ID 0x0A -#define FDC_READ_DEL 0x0C -#define FDC_FMT_TRACK 0x0D -#define FDC_DUMP_REG 0x0E -#define FDC_SEEK 0x0F -#define FDC_VERSION 0x10 -#define FDC_SCAN_EQL 0x11 -#define FDC_PERP_MODE 0x12 -#define FDC_CONFIGURE 0x13 -#define FDC_LOCK 0x14 -#define FDC_VERIFY 0x16 -#define FDC_SCAN_LO_EQL 0x19 -#define FDC_SCAN_HI_EQL 0x1D - -// Motor stuff -#define FDC_MOTOR_OFF 0 -#define FDC_MOTOR_ON 1 -#define FDC_MOTOR_WAIT 2 - -// Read/write directions -#define FDC_DIR_READ 0 -#define FDC_DIR_WRITE 1 - -// Digital Output Register (DOR) flags - -// DOR motor control flags: 1 = on, 0 = off -#define FDC_DOR_MOTD 0x80 // Drive D -#define FDC_DOR_MOTC 0x40 // Drive C -#define FDC_DOR_MOTB 0x20 // Drive B -#define FDC_DOR_MOTA 0x10 // Drive A - -// other DOR flags -#define FDC_DOR_DMA 0x08 // DMA2/IRQ6 On/Off -#define FDC_DOR_REST 0x04 // Controller Reset: this is active low; - // 1 = normal, 0 = reset - -// DOR drive select (DOR & 0x03): selects drive 0-3 - -// Main Status Register (MSR) flags -#define FDC_MSR_RQM 0x80 // Main Request: 1 = ready, 0 = not ready -#define FDC_MSR_DIO 0x40 // Data In/Out: 1 = FDC -> PC, 0 = PC -> FDC -#define FDC_MSR_NDMA 0x20 // Non-DMA: 1 = DMA off, 0 = DMA on -#define FDC_MSR_BUSY 0x10 // Device busy: self-explanatory - -// MSR drive seek flags: 1 = seeking, 0 = idle -#define FDC_MSR_ACTD 0x08 // Drive D -#define FDC_MSR_ACTC 0x04 // Drive C -#define FDC_MSR_ACTB 0x02 // Drive B -#define FDC_MSR_ACTA 0x01 // Drive A - -// Status Register 0 (ST0) flags - -/* ST0 interrupt code (ST0 & 0xC0): - 00 = normal termination - 01 = abnormal termination - 10 = invalid cmd - 11 = abnormal termination by poilling (drive became not ready) -*/ - -#define FDC_ST0_SEEK_END 0x20 // Seek End: seek/calibration completed -#define FDC_ST0_UNIT_CHK 0x10 // Unit Check: drive encountered a fault - // or recalibration failed -#define FDC_ST0_NOT_RDY 0x08 // Not Ready: self-explanatory -#define FDC_ST0_HEAD 0x04 // Active Head - -// ST0 unit select (ST0 & 0x03): same fmt as DOR drive select - -// Status Register 1 (ST1) flags -#define FDC_ST1_END_CYL 0x80 // End of Cylinder: - // set when sector count > sectors on track -// 0x40 is unused -#define FDC_ST1_DATA_ERR 0x20 // Data Error: set when an error is - // detected in a sector's data or ID fields -#define FDC_ST1_TIMEOUT 0x10 // Timeout: set when a data overrun or - // underrun occurs (i.e. the system is not - // reading data fast enough) -// 0x08 is unused -#define FDC_ST1_NO_DATA 0x04 // No Data: set when either a sector cannot - // be read, or an ID cannot be successfully - // read, or if the sector sequence cannot - // be determined -#define FDC_ST1_NO_WRITE 0x02 // Not Writable: set when attempting to - // write to a write-protected disk -#define FDC_ST1_NO_ID 0x01 // No Addr. Mark: set when an ID/(deleted) - // data address mark cannot be found. - -// Status Register 2 (ST2) flags -// 0x80 is unused -#define FDC_ST2_DEL_ADDR_MRK 0x40 // Deleted Addr. Mark: set when a deleted - // DAM or valid DAM is detected during RS - // or RDS, respectively -#define FDC_ST2_CRC_ERR 0x20 // CRC Error: self explanatory -#define FDC_ST2_WRONG_CYL 0x10 // Wrong cyl.: set when the ctrlr cyl does - // not match that in the ID addr mrk -// 0x08 and 0x04 are unused -#define FDC_ST2_BAD_CYL 0x02 // Bad Cylinder: set when track addr in - // sector does not match that on the ctrlr - // and equals 0xFF (meaning it is bad) -#define FDC_ST2_MISSING_DATA 0x01 // Missing Data: set when no DAM or DDAM - // can be found - -// Status Register 3 (ST3) flags -// 0x80 is unused -#define FDC_ST3_W_PROT 0x40 // Write Protected: self explanatory -// 0x20 is unused -#define FDC_ST3_TRK_0 0x10 // Track 0: ditto -// 0x08 is unused -#define FDC_ST3_HEAD_ADDR 0x04 // Head Address: ditto -// 0x02 and 0x01 are drive select diff --git a/KFloppyB.HH b/KFloppyB.HH deleted file mode 100644 index 76f6859..0000000 --- a/KFloppyB.HH +++ /dev/null @@ -1,25 +0,0 @@ -/* - Copyright (C) 2025-2026 Harley Travis . - This software (including source code) is licensed under the BSD Zero Clause - License. See the Copying.TXT file for details. -*/ - -// headers for floppy driver -public extern Bool FDCIrq; -public extern U16 fdc_base; -public extern U0 CMOSGetFloppyDrives(); -public extern U0 FDCSendCmd(U16 base, U8 cmd); -public extern U8 FDCReadData(U16 base); -public extern U0 FDCCheckInt(U16 base, U8 *st0, U8 *cyl); -public extern I16 fdc_mtr_ticks; -public extern U8 fdc_mtr_state; -public extern U0 FDCMotorOff(U16 base); -public extern U0 FDCMotorPwrOffTimer(); -public extern U0 FDCMotorCtrl(U16 base, Bool onoff); -public extern I8 FDCRecalibrate(U16 base); -public extern I8 FDCReset(U16 base); -public extern I8 FDCSeek(U16 base, U8 cyli, U8 head); -public extern U0 FDCRead(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); -public extern U0 FDCReadMulti(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); -public extern U0 FDCWrite(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); -public extern U0 FDCWriteMulti(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); diff --git a/KFloppyDMA.HC b/KFloppyDMA.HC deleted file mode 100644 index 12f4953..0000000 --- a/KFloppyDMA.HC +++ /dev/null @@ -1,7 +0,0 @@ -/* - Copyright (C) 2025-2026 Harley Travis . - This software (including source code) is licensed under the BSD Zero Clause - License. See the Copying.TXT file for details. -*/ - -U8 FDC_DMA[0x4800]; // Floppy DMA buffer 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 . + 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); +} diff --git a/Kernel/KFloppyA.HH b/Kernel/KFloppyA.HH new file mode 100644 index 0000000..e7065b6 --- /dev/null +++ b/Kernel/KFloppyA.HH @@ -0,0 +1,137 @@ +/* + Copyright (C) 2025-2026 Harley Travis . + This software (including source code) is licensed under the BSD Zero Clause + License. See the Copying.TXT file for details. +*/ + +// FDC uses IRQ6 = int 26h +#define I_FDC 0x26 +#define IRQ_FDC 6 + +// FDC regs (primary ctrlr) +#define FDC_STAT_A 0x0 +#define FDC_STAT_B 0x1 +#define FDC_DOR 0x2 +#define FDC_TAPE 0x3 +#define FDC_MSR_DSR 0x4 +#define FDC_DATA 0x5 +#define FDC_CCR_DIR 0x7 + +// FDC cmds +#define FDC_READ_TRACK 0x02 +#define FDC_SPECIFY 0x03 +#define FDC_SENSE_STAT 0x04 +#define FDC_WRITE_DATA 0x05 +#define FDC_READ_DATA 0x06 +#define FDC_RECALIBRATE 0x07 +#define FDC_SENSE_INTR 0x08 +#define FDC_WRITE_DEL 0x09 +#define FDC_READ_ID 0x0A +#define FDC_READ_DEL 0x0C +#define FDC_FMT_TRACK 0x0D +#define FDC_DUMP_REG 0x0E +#define FDC_SEEK 0x0F +#define FDC_VERSION 0x10 +#define FDC_SCAN_EQL 0x11 +#define FDC_PERP_MODE 0x12 +#define FDC_CONFIGURE 0x13 +#define FDC_LOCK 0x14 +#define FDC_VERIFY 0x16 +#define FDC_SCAN_LO_EQL 0x19 +#define FDC_SCAN_HI_EQL 0x1D + +// Motor stuff +#define FDC_MOTOR_OFF 0 +#define FDC_MOTOR_ON 1 +#define FDC_MOTOR_WAIT 2 + +// Read/write directions +#define FDC_DIR_READ 0 +#define FDC_DIR_WRITE 1 + +// Digital Output Register (DOR) flags + +// DOR motor control flags: 1 = on, 0 = off +#define FDC_DOR_MOTD 0x80 // Drive D +#define FDC_DOR_MOTC 0x40 // Drive C +#define FDC_DOR_MOTB 0x20 // Drive B +#define FDC_DOR_MOTA 0x10 // Drive A + +// other DOR flags +#define FDC_DOR_DMA 0x08 // DMA2/IRQ6 On/Off +#define FDC_DOR_REST 0x04 // Controller Reset: this is active low; + // 1 = normal, 0 = reset + +// DOR drive select (DOR & 0x03): selects drive 0-3 + +// Main Status Register (MSR) flags +#define FDC_MSR_RQM 0x80 // Main Request: 1 = ready, 0 = not ready +#define FDC_MSR_DIO 0x40 // Data In/Out: 1 = FDC -> PC, 0 = PC -> FDC +#define FDC_MSR_NDMA 0x20 // Non-DMA: 1 = DMA off, 0 = DMA on +#define FDC_MSR_BUSY 0x10 // Device busy: self-explanatory + +// MSR drive seek flags: 1 = seeking, 0 = idle +#define FDC_MSR_ACTD 0x08 // Drive D +#define FDC_MSR_ACTC 0x04 // Drive C +#define FDC_MSR_ACTB 0x02 // Drive B +#define FDC_MSR_ACTA 0x01 // Drive A + +// Status Register 0 (ST0) flags + +/* ST0 interrupt code (ST0 & 0xC0): + 00 = normal termination + 01 = abnormal termination + 10 = invalid cmd + 11 = abnormal termination by poilling (drive became not ready) +*/ + +#define FDC_ST0_SEEK_END 0x20 // Seek End: seek/calibration completed +#define FDC_ST0_UNIT_CHK 0x10 // Unit Check: drive encountered a fault + // or recalibration failed +#define FDC_ST0_NOT_RDY 0x08 // Not Ready: self-explanatory +#define FDC_ST0_HEAD 0x04 // Active Head + +// ST0 unit select (ST0 & 0x03): same fmt as DOR drive select + +// Status Register 1 (ST1) flags +#define FDC_ST1_END_CYL 0x80 // End of Cylinder: + // set when sector count > sectors on track +// 0x40 is unused +#define FDC_ST1_DATA_ERR 0x20 // Data Error: set when an error is + // detected in a sector's data or ID fields +#define FDC_ST1_TIMEOUT 0x10 // Timeout: set when a data overrun or + // underrun occurs (i.e. the system is not + // reading data fast enough) +// 0x08 is unused +#define FDC_ST1_NO_DATA 0x04 // No Data: set when either a sector cannot + // be read, or an ID cannot be successfully + // read, or if the sector sequence cannot + // be determined +#define FDC_ST1_NO_WRITE 0x02 // Not Writable: set when attempting to + // write to a write-protected disk +#define FDC_ST1_NO_ID 0x01 // No Addr. Mark: set when an ID/(deleted) + // data address mark cannot be found. + +// Status Register 2 (ST2) flags +// 0x80 is unused +#define FDC_ST2_DEL_ADDR_MRK 0x40 // Deleted Addr. Mark: set when a deleted + // DAM or valid DAM is detected during RS + // or RDS, respectively +#define FDC_ST2_CRC_ERR 0x20 // CRC Error: self explanatory +#define FDC_ST2_WRONG_CYL 0x10 // Wrong cyl.: set when the ctrlr cyl does + // not match that in the ID addr mrk +// 0x08 and 0x04 are unused +#define FDC_ST2_BAD_CYL 0x02 // Bad Cylinder: set when track addr in + // sector does not match that on the ctrlr + // and equals 0xFF (meaning it is bad) +#define FDC_ST2_MISSING_DATA 0x01 // Missing Data: set when no DAM or DDAM + // can be found + +// Status Register 3 (ST3) flags +// 0x80 is unused +#define FDC_ST3_W_PROT 0x40 // Write Protected: self explanatory +// 0x20 is unused +#define FDC_ST3_TRK_0 0x10 // Track 0: ditto +// 0x08 is unused +#define FDC_ST3_HEAD_ADDR 0x04 // Head Address: ditto +// 0x02 and 0x01 are drive select diff --git a/Kernel/KFloppyB.HH b/Kernel/KFloppyB.HH new file mode 100644 index 0000000..76f6859 --- /dev/null +++ b/Kernel/KFloppyB.HH @@ -0,0 +1,25 @@ +/* + Copyright (C) 2025-2026 Harley Travis . + This software (including source code) is licensed under the BSD Zero Clause + License. See the Copying.TXT file for details. +*/ + +// headers for floppy driver +public extern Bool FDCIrq; +public extern U16 fdc_base; +public extern U0 CMOSGetFloppyDrives(); +public extern U0 FDCSendCmd(U16 base, U8 cmd); +public extern U8 FDCReadData(U16 base); +public extern U0 FDCCheckInt(U16 base, U8 *st0, U8 *cyl); +public extern I16 fdc_mtr_ticks; +public extern U8 fdc_mtr_state; +public extern U0 FDCMotorOff(U16 base); +public extern U0 FDCMotorPwrOffTimer(); +public extern U0 FDCMotorCtrl(U16 base, Bool onoff); +public extern I8 FDCRecalibrate(U16 base); +public extern I8 FDCReset(U16 base); +public extern I8 FDCSeek(U16 base, U8 cyli, U8 head); +public extern U0 FDCRead(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); +public extern U0 FDCReadMulti(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); +public extern U0 FDCWrite(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); +public extern U0 FDCWriteMulti(U16 base, U8 cyl, U8 head, U8 sect, U8 trklen); diff --git a/Kernel/KFloppyDMA.HC b/Kernel/KFloppyDMA.HC new file mode 100644 index 0000000..12f4953 --- /dev/null +++ b/Kernel/KFloppyDMA.HC @@ -0,0 +1,7 @@ +/* + Copyright (C) 2025-2026 Harley Travis . + This software (including source code) is licensed under the BSD Zero Clause + License. See the Copying.TXT file for details. +*/ + +U8 FDC_DMA[0x4800]; // Floppy DMA buffer -- cgit v1.2.3