réplica de
https://github.com/Arnau478/quark.git
synced 2024-09-27 18:19:38 +02:00
259 líneas
6,6 KiB
C
259 líneas
6,6 KiB
C
#include <stdbool.h>
|
|
#include "fdc.h"
|
|
#include "isr.h"
|
|
#include "io.h"
|
|
#include "dma.h"
|
|
#include "../../lib/time.h"
|
|
#include "../../lib/debug.h"
|
|
|
|
static volatile bool g_fdc_irq = false;
|
|
static int g_curr_drive = 0;
|
|
|
|
static void i686_fdc_irq(registers *regs){
|
|
g_fdc_irq = true;
|
|
}
|
|
|
|
static inline void wait_irq(){
|
|
while(!g_fdc_irq); // Wait until g_fdc_irq is true
|
|
g_fdc_irq = false; // Then reset it to false
|
|
}
|
|
|
|
void i686_fdc_initialize_dma(){
|
|
i686_outb(0x0A, 0x06); // Mask DMA channel 2
|
|
i686_outb(0xD8, 0xFF); // Reset master flip-flop
|
|
i686_outw(0x04, i686_FDC_DMA_BUFFER); // Address = 0x1000
|
|
i686_outb(0xD8, 0xFF); // Reset master flip-flop
|
|
i686_outb(0x05, 0xFF); // Count to 0x23FF (bytes in 2.5" floppy disk track)
|
|
i686_outb(0x05, 0x23);
|
|
i686_outb(0x80, 0x00); // External page register = 0
|
|
i686_outb(0x0A, 0x02); // Unmask DMA channel 2
|
|
}
|
|
|
|
void i686_fdc_dma_read(){
|
|
i686_outb(0x0A, 0x06); // Mask DMA channel 2
|
|
i686_outb(0x0B, 0x56); // single transfer, address increment, autoinit, read, channel 2
|
|
i686_outb(0x0A, 0x02); // Unmask DMA channel 2
|
|
}
|
|
|
|
void i686_fdc_dma_write(){
|
|
i686_outb(0x0A, 0x06); // Mask DMA channel 2
|
|
i686_outb(0x0B, 0x5A); // single transfer, address increment, autoinit, write, channel 2
|
|
i686_outb(0x0A, 0x02); // Unmask DMA channel 2
|
|
}
|
|
|
|
uint8_t i686_fdc_msr_read(){
|
|
return i686_inb(i686_FDC_MSR);
|
|
}
|
|
|
|
void i686_fdc_send_cmd(uint8_t cmd){
|
|
while(!(i686_fdc_msr_read() & i686_FDC_MSR_DATAREG));
|
|
i686_outb(i686_FDC_FIFO, cmd);
|
|
}
|
|
|
|
uint8_t i686_fdc_read_data(){
|
|
while(!(i686_fdc_msr_read() & i686_FDC_MSR_DATAREG));
|
|
return i686_inb(i686_FDC_FIFO);
|
|
}
|
|
|
|
void i686_fdc_write_ccr(uint8_t v){
|
|
i686_outb(i686_FDC_CTRL, v);
|
|
}
|
|
|
|
void i686_fdc_drive_data(uint32_t stepr, uint32_t loadt, uint32_t unloadt, bool dma){
|
|
uint32_t data = 0;
|
|
|
|
i686_fdc_send_cmd(i686_FDC_CMD_SPECIFY);
|
|
|
|
data = ((stepr & 0xF) << 4) | (unloadt & 0xF);
|
|
i686_fdc_send_cmd(data);
|
|
|
|
data = loadt << 1 | dma ? 1 : 0; // Ensure dma is a 0/1 bool
|
|
i686_fdc_send_cmd(data);
|
|
}
|
|
|
|
void i686_fdc_check_int(uint32_t *st0, uint32_t *cyl){
|
|
i686_fdc_send_cmd(i686_FDC_CMD_CHECK_INT);
|
|
*st0 = i686_fdc_read_data();
|
|
*cyl = i686_fdc_read_data();
|
|
}
|
|
|
|
int i686_fdc_motor(bool b){
|
|
if(g_curr_drive > 3) return -2;
|
|
|
|
uint32_t motor = 0;
|
|
switch(g_curr_drive){
|
|
case 0: motor = i686_FDC_DOR_DRIVE0_MOTOR; break;
|
|
case 1: motor = i686_FDC_DOR_DRIVE1_MOTOR; break;
|
|
case 2: motor = i686_FDC_DOR_DRIVE2_MOTOR; break;
|
|
case 3: motor = i686_FDC_DOR_DRIVE3_MOTOR; break;
|
|
}
|
|
|
|
if(b) i686_outb(i686_FDC_DOR, g_curr_drive | motor | i686_FDC_DOR_RESET | i686_FDC_DOR_DMA);
|
|
else i686_outb(i686_FDC_DOR, i686_FDC_DOR_RESET);
|
|
|
|
// Give the motor some time
|
|
sleep(20);
|
|
}
|
|
|
|
void i686_fdc_read_sector_imp(uint8_t head, uint8_t track, uint8_t sector){
|
|
uint32_t st0, cyl;
|
|
|
|
// Initialize the DMA
|
|
i686_dma_initialize_floppy((uint8_t *)i686_FDC_DMA_BUFFER, 512);
|
|
|
|
// Set DMA for read
|
|
i686_fdc_dma_read();
|
|
|
|
// Read a sector
|
|
i686_fdc_send_cmd(i686_FDC_CMD_READ_SECT | i686_FDC_CMD_EXT_MULTITRACK | i686_FDC_CMD_EXT_SKIP | i686_FDC_CMD_EXT_DENSITY);
|
|
i686_fdc_send_cmd(head << 2 | g_curr_drive);
|
|
i686_fdc_send_cmd(track);
|
|
i686_fdc_send_cmd(head);
|
|
i686_fdc_send_cmd(sector);
|
|
i686_fdc_send_cmd(i686_FDC_SECTOR_DTL_512);
|
|
i686_fdc_send_cmd((sector + 1) >= i686_FDC_SECTORS_PER_TRACK ? i686_FDC_SECTORS_PER_TRACK : sector + 1);
|
|
i686_fdc_send_cmd(i686_FDC_GAP3_3_5);
|
|
i686_fdc_send_cmd(0xFF);
|
|
|
|
// Wait for IRQ
|
|
wait_irq();
|
|
|
|
// Read output
|
|
for(int i = 0; i < 7; i++) i686_fdc_read_data();
|
|
|
|
// Tell the FDC we are done
|
|
i686_fdc_check_int(&st0, &cyl);
|
|
}
|
|
|
|
static void lba_to_chs(int lba, int *head, int *track, int *sector){
|
|
*head = (lba % (i686_FDC_SECTORS_PER_TRACK * 2)) / i686_FDC_SECTORS_PER_TRACK;
|
|
*track = lba / (i686_FDC_SECTORS_PER_TRACK * 2);
|
|
*sector = lba % i686_FDC_SECTORS_PER_TRACK + 1;
|
|
}
|
|
|
|
uint8_t *i686_fdc_read_sector(int lba){
|
|
if(g_curr_drive > 3) return 0;
|
|
|
|
// Convert LBA to CHS
|
|
int head = 0, track = 0, sector = 1;
|
|
lba_to_chs(lba, &head, &track, §or);
|
|
|
|
// Turn on motor and seek
|
|
i686_fdc_motor(true);
|
|
if(i686_fdc_seek(track, head) != 0) return 0;
|
|
|
|
// Read and turn off motor
|
|
i686_fdc_read_sector_imp(head, track, sector);
|
|
i686_fdc_motor(false);
|
|
|
|
return (uint8_t *)0x1000;
|
|
}
|
|
|
|
int i686_fdc_calibrate(uint32_t drive){
|
|
uint32_t st0, cyl;
|
|
|
|
if(drive > 3) return -2;
|
|
|
|
i686_fdc_motor(true);
|
|
|
|
for(int i = 0; i < 10; i++){
|
|
// Send command
|
|
i686_fdc_send_cmd(i686_FDC_CMD_CALIBRATE);
|
|
i686_fdc_send_cmd(drive);
|
|
wait_irq();
|
|
i686_fdc_check_int(&st0, &cyl);
|
|
|
|
if(!cyl){
|
|
i686_fdc_motor(false);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
i686_fdc_motor(false);
|
|
return -1;
|
|
}
|
|
|
|
int i686_fdc_seek(uint32_t cyl, uint32_t head){
|
|
uint32_t st0, cyl0;
|
|
|
|
if(g_curr_drive > 3) return -2;
|
|
|
|
for(int i = 0; i < 10; i++){
|
|
// Send the command
|
|
i686_fdc_send_cmd(i686_FDC_CMD_SEEK);
|
|
i686_fdc_send_cmd(head << 2 | g_curr_drive);
|
|
i686_fdc_send_cmd(cyl);
|
|
|
|
// Wait for the results
|
|
wait_irq();
|
|
i686_fdc_check_int(&st0, &cyl0);
|
|
|
|
// Found?
|
|
if(cyl0 == cyl) return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void i686_fdc_disable(){
|
|
i686_outb(i686_FDC_DOR, 0);
|
|
}
|
|
|
|
void i686_fdc_enable(){
|
|
i686_outb(i686_FDC_DOR, i686_FDC_DOR_RESET | i686_FDC_DOR_DMA);
|
|
}
|
|
|
|
void i686_fdc_reset(){
|
|
uint32_t st0, cyl;
|
|
|
|
// Reset the controller
|
|
i686_fdc_disable();
|
|
i686_fdc_enable();
|
|
wait_irq();
|
|
|
|
// CHECK_INT to all drives
|
|
for(int i = 0; i < 4; i++) i686_fdc_check_int(&st0, &cyl);
|
|
|
|
// 500kb/s
|
|
i686_fdc_write_ccr(0);
|
|
|
|
// steprate = 3ms, unload time = 240ms, load time = 16ms, dma = true
|
|
i686_fdc_drive_data(3, 16, 240, true);
|
|
|
|
// Calibrate the disk
|
|
i686_fdc_calibrate(g_curr_drive);
|
|
}
|
|
|
|
void i686_fdc_set_working_drive(int drive){
|
|
if(drive < 4) g_curr_drive = drive;
|
|
}
|
|
|
|
void i686_fdc_initialize(){
|
|
debug_printf("[FDC] Initializing\n");
|
|
// Setup IRQ
|
|
i686_isr_register_handler(IRQ(6), i686_fdc_irq);
|
|
|
|
// Initialize DMA
|
|
i686_fdc_initialize_dma();
|
|
|
|
// Reset FDC
|
|
i686_fdc_reset();
|
|
|
|
// Set drive info
|
|
i686_fdc_drive_data(13, 1, 0x0F, true);
|
|
}
|
|
|
|
void i686_dma_initialize_floppy(uint8_t *buffer, unsigned length){
|
|
i686_dma_reset(1);
|
|
i686_dma_mask_channel(i686_FDC_DMA_CHANNEL);
|
|
i686_dma_reset_flipflop(1);
|
|
|
|
i686_dma_set_address(i686_FDC_DMA_CHANNEL, (unsigned)buffer);
|
|
i686_dma_reset_flipflop(1);
|
|
|
|
i686_dma_set_count(i686_FDC_DMA_CHANNEL, length);
|
|
i686_dma_set_read(i686_FDC_DMA_CHANNEL);
|
|
|
|
i686_dma_unmask_all(1);
|
|
}
|