3 * - By John Hodge (thePowersGang)
13 #define MOTOR_ON_DELAY 500
14 #define MOTOR_OFF_DELAY 2000
24 FDC_DOR = 0x02, // Digital Output Register
25 FDC_MSR = 0x04, // Master Status Register (Read Only)
26 FDC_FIFO = 0x05, // FIFO port
27 FDC_CCR = 0x07 // Configuration Control Register (write only)
32 CMD_SPECIFY = 3, // Specify parameters
33 CMD_WRITE_DATA = 5, // Write Data
34 CMD_READ_DATA = 6, // Read Data
35 CMD_RECALIBRATE = 7, // Recalibrate a drive
36 CMD_SENSE_INTERRUPT = 8, // Sense (Ack) an interrupt
37 CMD_SEEK = 15, // Seek to a track
41 int FDD_SetupIO(void);
42 int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
43 int FDD_int_SeekToTrack(int Disk, int Track);
44 int FDD_int_Calibrate(int Disk);
45 int FDD_int_Reset(int Disk);
47 int FDD_int_WriteData(Uint16 Base, Uint8 Data);
48 int FDD_int_ReadData(Uint16 Base, Uint8 *Data);
49 void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl);
51 int FDD_int_StartMotor(int Disk);
52 int FDD_int_StopMotor(int Disk);
53 void FDD_int_StopMotorCallback(void *Ptr);
55 int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0);
56 Uint16 FDD_int_GetBase(int Disk, int *Drive);
58 void FDD_int_ClearIRQ(void);
59 int FDD_int_WaitIRQ(void);
60 void FDD_int_IRQHandler(int IRQ, void *Ptr);
64 * \brief Marker for IRQ6
65 * \todo Convert into a semaphore?
69 * \brief Protector for DMA and IRQ6
75 * \brief Set up FDC IO
76 * \return Boolean failure
78 * Registers the IRQ handler and resets the controller
82 // Install IRQ6 Handler
83 IRQ_AddHandler(6, FDD_int_IRQHandler, NULL);
87 // TODO: All controllers
91 * \brief Read/Write data from/to a disk
92 * \param Disk Global disk number
93 * \param Track Track number (Cyl*2+Head)
94 * \param bWrite Toggle write mode
95 * \param Buffer Destination/Source buffer
96 * \return Boolean failure
98 int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer)
102 Uint16 base = FDD_int_GetBase(Disk, &_disk);
103 int cyl = Track >> 1, head = Track & 1;
105 Mutex_Acquire( &gFDD_IOMutex );
107 // Initialise DMA for read/write
108 // TODO: Support non 1.44MiB FDs
109 DMA_SetChannel(2, BYTES_PER_TRACK, !bWrite);
113 cmd = CMD_WRITE_DATA | 0xC0;
115 cmd = CMD_READ_DATA | 0xC0;
118 if( FDD_int_SeekToTrack(Disk, Track) ) {
119 Mutex_Release( &gFDD_IOMutex );
123 for( i = 0; i < 20; i ++ )
125 FDD_int_StartMotor(Disk);
129 DMA_WriteData(2, BYTES_PER_TRACK, Buffer);
131 FDD_int_WriteData(base, cmd);
132 FDD_int_WriteData(base, (head << 2) | _disk);
133 FDD_int_WriteData(base, cyl);
134 FDD_int_WriteData(base, head);
135 FDD_int_WriteData(base, 1); // First Sector
136 FDD_int_WriteData(base, 2); // Bytes per sector (128*2^n)
137 FDD_int_WriteData(base, 18); // 18 tracks (full disk) - TODO: Non 1.44
138 FDD_int_WriteData(base, 0x1B); // Gap length - TODO: again
139 FDD_int_WriteData(base, 0xFF); // Data length - ?
143 // No Sense Interrupt
145 Uint8 st0=0, st1=0, st2=0, bps=0;
146 FDD_int_ReadData(base, &st0);
147 FDD_int_ReadData(base, &st1); // st1
148 FDD_int_ReadData(base, &st2); // st2
149 FDD_int_ReadData(base, NULL); // rcy - Mutilated Cyl
150 FDD_int_ReadData(base, NULL); // rhe - Mutilated Head
151 FDD_int_ReadData(base, NULL); // rse - Mutilated sector
152 FDD_int_ReadData(base, &bps); // bps - Should be the same as above
155 FDD_int_HandleST0Error(__func__, Disk, st0);
160 Log_Debug("FDD", "Disk %i is not writable", Disk);
161 Mutex_Release( &gFDD_IOMutex );
166 Log_Debug("FDD", "FDD_int_ReadWriteTrack: Drive not ready");
172 Log_Debug("FDD", "FDD_int_ReadWriteTrack: End of cylinder");
176 if( st1 & (0x20|0x10|0x04|0x01) ) {
177 Log_Debug("FDD", "FDD_int_ReadWriteTrack: st1 = 0x%x", st1);
181 if( st2 & (0x40|0x20|0x10|0x04|0x01) ) {
182 Log_Debug("FDD", "FDD_int_ReadWriteTrack: st2 = 0x%x", st2);
187 Log_Debug("FDD", "Wanted bps = 2 (512), got %i", bps);
193 DMA_ReadData(2, BYTES_PER_TRACK, Buffer);
195 FDD_int_StopMotor(Disk);
196 Mutex_Release( &gFDD_IOMutex );
200 Log_Debug("FDD", "%i retries exhausted", i);
201 FDD_int_StopMotor(Disk);
202 Mutex_Release( &gFDD_IOMutex );
207 * \brief Seek to a specific track
208 * \param Disk Global disk number
209 * \param Track Track number (Cyl*2+Head)
210 * \return Boolean failure
212 int FDD_int_SeekToTrack(int Disk, int Track)
214 Uint8 st0=0, res_cyl=0;
217 Uint16 base = FDD_int_GetBase(Disk, &_disk);;
222 FDD_int_StartMotor(Disk);
224 for( int i = 0; i < 10; i ++ )
227 FDD_int_WriteData(base, CMD_SEEK);
228 FDD_int_WriteData(base, (head << 2) + _disk);
229 FDD_int_WriteData(base, cyl);
232 FDD_int_SenseInterrupt(base, &st0, &res_cyl);
236 FDD_int_HandleST0Error(__func__, Disk, st0);
240 if( res_cyl == cyl ) {
241 FDD_int_StopMotor(Disk);
246 Log_Error("FDD", "FDD_int_SeekToTrack: 10 retries exhausted\n");
247 FDD_int_StopMotor(Disk);
252 * \brief Calibrate a drive
253 * \param Disk Global disk number
255 int FDD_int_Calibrate(int Disk)
258 Uint16 base = FDD_int_GetBase(Disk, &_disk);
259 FDD_int_StartMotor(Disk);
261 for( int i = 0; i < 10; i ++ )
263 Uint8 st0=0, cyl = -1;
266 FDD_int_WriteData(base, CMD_RECALIBRATE);
267 FDD_int_WriteData(base, _disk);
271 FDD_int_SenseInterrupt(base, &st0, NULL);
274 FDD_int_HandleST0Error(__func__, Disk, st0);
280 FDD_int_StopMotor(Disk);
285 Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted");
291 * \brief Reset a controller
292 * \param Base Controller base address
294 int FDD_int_Reset(int Disk)
298 Uint16 base = FDD_int_Reset(Disk, &_disk);
300 tmp = inb(base + FDC_DOR) & 0xF0;
301 outb( base + FDC_DOR, 0x00 );
303 outb( base + FDC_DOR, tmp | 0x0C );
305 FDD_int_SenseInterrupt(base, NULL, NULL);
307 outb(base + FDC_CCR, 0x00); // 500KB/s
309 FDD_int_WriteData(base, CMD_SPECIFY); // Step and Head Load Times
310 FDD_int_WriteData(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
311 FDD_int_WriteData(base, 0x02); // Head Load Time >> 1
313 // TODO: Recalibrate all present disks
314 FDD_int_Calibrate(Disk);
319 * \brief Write a byte to the FIFO
321 int FDD_int_WriteData(Uint16 Base, Uint8 Data)
323 for( int i = 0; i < 100; i ++ )
325 if( inb(Base + FDC_MSR) & 0x80 )
327 outb(Base + FDC_FIFO, Data);
332 Log_Error("FDD", "Write timeout");
337 * \brief Read a byte from the FIFO
339 int FDD_int_ReadData(Uint16 Base, Uint8 *Data)
341 for( int i = 0; i < 100; i ++ )
343 if( inb(Base + FDC_MSR) & 0x80 )
345 Uint8 tmp = inb(Base + FDC_FIFO);
346 if(Data) *Data = tmp;
351 Log_Error("FDD", "Read timeout");
356 * \brief Acknowledge an interrupt
357 * \param Base Controller base address
358 * \param ST0 Location to store the ST0 value
359 * \param Cyl Current cylinder
361 void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl)
363 FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT);
364 FDD_int_ReadData(Base, ST0);
365 FDD_int_ReadData(Base, Cyl);
369 * \brief Start the motor on a disk
371 int FDD_int_StartMotor(int Disk)
374 Uint16 base = FDD_int_GetBase(Disk, &_disk);
376 if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED )
379 // Clear the motor off timer
380 Time_RemoveTimer(gaFDD_Disks[Disk].Timer);
381 gaFDD_Disks[Disk].Timer = -1;
384 outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4)));
386 // Wait for it to reach speed
387 Time_Delay(MOTOR_ON_DELAY);
389 gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED;
395 * \brief Schedule the motor to stop
397 int FDD_int_StopMotor(int Disk)
399 if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED )
401 if( gaFDD_Disks[Disk].Timer != -1 )
404 gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk);
410 * \brief Actually stop the motor
411 * \param Ptr Actaully the global disk number
413 void FDD_int_StopMotorCallback(void *Ptr)
415 int Disk = (tVAddr)Ptr;
417 Uint16 base = FDD_int_GetBase(Disk, &_disk);
419 gaFDD_Disks[Disk].Timer = -1;
420 gaFDD_Disks[Disk].MotorState = MOTOR_OFF;
422 outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4)));
428 * \brief Converts a global disk number into a controller and drive
429 * \param Disk Global disk number
430 * \param Drive Destination for controller disk number
431 * \return Controller base address
433 Uint16 FDD_int_GetBase(int Disk, int *Drive)
435 if(Drive) *Drive = Disk & 3;
438 case 0: return 0x3F0;
439 case 1: return 0x370;
446 * \brief Convert a ST0 error value into a message
447 * \param Fcn Calling function name
448 * \parma Disk Global disk number
449 * \param ST0 ST0 Value
450 * \return Boolean failure
452 int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0)
454 static const char *status_type[] = {
455 0, "Error", "Invalid", "Drive Error"
458 Log_Debug("FDD", "%s: Disk %i ST0 Status = %s (0x%x & 0xC0 = 0x%x)",
459 Fcn, Disk, status_type[ST0 >> 6], ST0, ST0 & 0xC0
465 * \brief Clear the IRQ fired flag
467 void FDD_int_ClearIRQ(void)
473 * \brief Wait for an IRQ to fire
475 int FDD_int_WaitIRQ(void)
477 while(gbFDD_IRQ6Fired == 0)
484 * \param IRQ IRQ Number (unused)
485 * \param Ptr Data Pointer (unused)
487 void FDD_int_IRQHandler(int IRQ, void *Ptr)