summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/BlkDev/DskFDC.HC314
-rw-r--r--Kernel/Patches/CBlkDevFDC.HC6
2 files changed, 320 insertions, 0 deletions
diff --git a/Kernel/BlkDev/DskFDC.HC b/Kernel/BlkDev/DskFDC.HC
new file mode 100644
index 0000000..8c08778
--- /dev/null
+++ b/Kernel/BlkDev/DskFDC.HC
@@ -0,0 +1,314 @@
+/*
+ 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.
+*/
+
+interrupt U0 FDCInt()
+{
+ // On IRQ6, set a semaphore for anything waiting for an FDC int
+ fdc_int_semaphore = TRUE;
+ OutU8(0x20,0x20); // Send EOI to PIC
+}
+
+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);
+}
+
+U8 FDCReadByte(CBlkDev *bd)
+{
+ // Read byte from FDC.
+
+ U8 byte; // The byte read
+ F64 timeout; // Timeout variable
+ timeout = tS()+3.0; // 3 second timeout
+
+ while (TRUE) {
+ if (InU8(bd->base0+FDC_MSR_DSR)>>6==3) {
+ byte=InU8(bd->base0+FDC_DATA);
+ return byte;
+ }
+ if (tS()>timeout) {
+ throw('BlkDev');
+ }
+ }
+}
+
+U0 FDCSendByte(CBlkDev *bd,U8 byte)
+{
+ // Send byte to FDC.
+
+ F64 timeout; // Timeout variable
+ timeout=tS()+3.0; // 3 second timeout
+
+ while (TRUE) {
+ if (InU8(bd->base0+FDC_MSR_DSR)>>6==2) {
+ OutU8(bd->base0+FDC_DATA,byte);
+ return;
+ }
+ if (tS()>timeout) {
+ throw('BlkDev');
+ }
+ }
+}
+
+U0 FDCReset(CBlkDev *bd)
+{
+ // Reset the FDC
+
+ fdc_int_semaphore=FALSE;
+
+ // Twiddle the reset bit in the DSR
+ OutU8(bd->base0+FDC_MSR_DSR,0x80|bd->bps);
+
+ // Wait for the int
+ while (!fdc_int_semaphore)
+ Yield;
+
+ // Send 4 SISes
+ for (i=0,i<4,i++) {
+ FDCSendByte(bd,FDC_SENSE_INTR)
+ FDCReadByte(bd);
+ FDCReadByte(bd);
+ }
+
+ // Send "CONFIGURE" to fix FDC cfg
+ FDCSendByte(bd,FDC_CONFIGURE);
+ FDCSendByte(bd,0); // Null byte
+ FDCSendByte(bd,0b01010111); // Implied Seek, FIFO, No Polling, Threshold 8
+ FDCSendByte(bd,0); // No Write Precomp
+
+ // Select our drive
+ FDCSelDrv(bd);
+}
+
+U0 FDCMotorTask(CBlkDev *bd)
+{
+ // Motor timeout task
+ F64 timeout = tS()+3.0; // 3 sec timeout
+ U8 dor;
+ while(tS() != timeout) {
+ if (bd->mtr==FDC_MOTOR_ON)
+ return;
+ Yield;
+ }
+ dor=InU8(bd->base0+FDC_DOR);
+ OutU8(bd->base0+FDC_DOR,dor^(1<<4+bd->unit));
+}
+
+U0 FDCMotor(CBlkDev *bd, Bool onoff)
+{
+ // Motor control
+ U8 dor=InU8(bd->base0+FDC_DOR);
+ if (onoff) {
+ OutU8(bd->base0+FDC_DOR,dor^(1<<4+bd->unit)|(1<<4+bd->unit));
+ bd->mtr=FDC_MOTOR_ON;
+ } else {
+ bd->mtr == FDC_MOTOR_WAIT;
+ Spawn(&FDCMotorTask,bd,"FDC Motor");
+ }
+}
+
+U0 FDCSelDrv(CBlkDev *bd)
+{
+ // Select the drive assigned to this blkdev
+
+ // Set the CCR appropriately
+ OutU8(bd->base0+FDC_CCR_DSR,bd->bps);
+
+ // Send a "SPECIFY" command
+ FDCSendByte(bd,FDC_SPECIFY);
+ FDCSendByte(bd,bd->srt<<4|bd->hut); // Step Rate Time, Head Unload Time
+ FDCSendByte(bd,bd->hlt<<1|!bd->dma); // Head Load Time, DMA
+
+ // Set drv sel in DOR (and enable DMA/IRQs)
+ OutU8(bd->base0+FDC_DOR,8|bd->unit);
+}
+
+U0 FDCInit(CBlkDev *bd)
+{
+ // Initialize the FDC assigned to this blkdev
+
+ U8 ver,st0,pcn;
+ Bool unlock=BlkDevLock(bd);
+
+ // Check if the controller is 82077AA-compatible
+ FDCSendByte(bd,FDC_VERSION);
+ ver=FDCReadByte(bd);
+
+ if (ver!=0x90) throw('BlkDev');
+
+ // Reset the controller
+ FDCReset(bd);
+
+ recalibrate:
+ // Recalibrate this drive
+ fdc_irq_semaphore=FALSE;
+ FDCSendByte(bd,FDC_RECALIBRATE);
+ FDCSendByte(bd,bd->unit); // Drive number
+
+ // Wait for an IRQ
+ while (!fdc_irq_semaphore) Yield;
+
+ // Get the result of the recalibration
+ FDCSendByte(bd,FDC_SENSE_INTR);
+ st0=FDCReadByte(bd);
+ pcn=FDCReadByte(bd);
+
+ if (st0>>6) throw('BlkDev');
+ if (!(st0&0x20)) goto recalibrate;
+
+ if (unlock) BlkDevUnlock(bd);
+}
+
+U0 FDCRBlks(CDrv *dv,U8 *buf,I64 blk,I64 cnt)
+{
+ I64 n;
+ CBlkDev *bd=dv->bd;
+ U8 s,mts;
+
+ while (cnt>0) {
+ s=(blk%(bd->spt))+1; // sector
+ mts=(blk%(bd->spt*2))+1; // multi-track sector
+ n=cnt;
+ if (bd->heads>1 && n>bd->max_reads-mts+1) {
+ n=bd->max_reads-mts+1;
+ } else if (n>bd->max_reads-s+1) {
+ n=bd->max_reads-s+1;
+ }
+ FDCReadBlks(bd,buf,blk,n);
+ buf+n<<BLK_SIZE_BITS;
+ blk+=n;
+ cnt-=n;
+ }
+}
+
+U8 FDCReadCyl(CBlkDev *bd,U8 cyl)
+{
+ U8 st0,st1,st2,pcn,hd,sect,size
+
+ fdc_irq_semaphore=FALSE;
+
+ FDCDMAInit((bd->spt*bd->heads)*bd->blk_size);
+ FDCDMAPrepWrite();
+
+ FDCSendByte(bd,FDC_WRITE_DATA|(bd->heads>1<<7)|mfm<<6|);
+ FDCSendByte(bd,cyl);
+ FDCSendByte(bd,0);
+ FDCSendByte(bd,1);
+ FDCSendByte(bd,bd->blk_size>>8);
+ FDCSendByte(bd,bd->gpl1);
+ FDCSendByte(bd,255);
+
+ while(!fdc_int_semaphore) Yield;
+
+ st0=FDCReadByte(bd);
+ st1=FDCReadByte(bd);
+ st2=FDCReadByte(bd);
+ pcn=FDCReadByte(bd);
+ hd=FDCReadByte(bd);
+ sect=FDCReadByte(bd);
+ size=FDCReadByte(bd);
+
+ return st0;
+}
+
+U8 FDCReadCyl(CBlkDev *bd,U8 cyl)
+{
+ U8 st0,st1,st2,pcn,hd,sect,size
+
+ fdc_irq_semaphore=FALSE;
+
+ FDCDMAInit((bd->spt*bd->heads)*bd->blk_size);
+ FDCDMAPrepRead();
+
+ FDCSendByte(bd,FDC_READ_DATA|(bd->heads>1<<7)|mfm<<6|);
+ FDCSendByte(bd,cyl);
+ FDCSendByte(bd,0);
+ FDCSendByte(bd,1);
+ FDCSendByte(bd,bd->blk_size>>8);
+ FDCSendByte(bd,bd->gpl1);
+ FDCSendByte(bd,255);
+
+ while(!fdc_int_semaphore) Yield;
+
+ st0=FDCReadByte(bd);
+ st1=FDCReadByte(bd);
+ st2=FDCReadByte(bd);
+ pcn=FDCReadByte(bd);
+ hd=FDCReadByte(bd);
+ sect=FDCReadByte(bd);
+ size=FDCReadByte(bd);
+
+ return st0;
+}
+
+U0 FDCReadBlks(CBlkDev *bd,U8 *buf,I64 blk,I64 cnt)
+{
+ I64 retries=3;
+ U8 c,h,s,st0,st1,st2,pcn,hd,sect,size;
+ Bool unlock=BlkDevLock(bd);
+
+ retry:
+ if (retries==0) throw('BlkDev');
+
+ FDCSelDrv(bd);
+
+ c=blk/(bd->heads*bd->spt);
+ s=(blk%bd->spt)+1;
+
+ FDCReadCyl(bd,c);
+
+ if (st0>>6) {
+ retries-=1;
+ goto retry;
+ }
+
+ MemCpy(buf,&FDC_DMA+(c*bd->blk_size),cnt);
+}
diff --git a/Kernel/Patches/CBlkDevFDC.HC b/Kernel/Patches/CBlkDevFDC.HC
new file mode 100644
index 0000000..0b194fa
--- /dev/null
+++ b/Kernel/Patches/CBlkDevFDC.HC
@@ -0,0 +1,6 @@
+U8 cyls,heads,spt,
+ srt,hut,hlt,
+ bps,gpl1,gpl2,
+ dtl,mtr;
+Bool mfm,dma;
+