修改自某CSDN网友的博客,但是他也是转载,所以无法确定原作者
软盘控制器
通过以下几个控制寄存器操作FDC
I/O address Read or Write Register
0x3f2 Write DOR: Digital Output Register
0x3f4 Read FDC Status: Floppy Disk Status Register
0x3f5 Read/Write FDC Data: Floppy Disk Data Register
0x3f7 Read DIR: Digital Input Register
Write DCR: Disk Control Register
DOR:DOR是一个8位寄存器,它控制软盘驱动器马达的开启、驱动器选择、启动/复位FDC以及允许/禁止DMA请求
位 Name Description
7 MOT_EN3 Driver D motor:1-start;0-stop
6 MOT_EN2 Driver C motor:1-start;0-stop
5 MOT_EN1 Driver B motor:1-start;0-stop
4 MOT_EN0 Driver A motor:1-start;0-stop
3 DMA_INT DMA interrupt; 1 enable; 0-disable
2 RESET FDC Reset
1 DRV_SEL1 Select driver 1/0
0 DRV_SEL0
FDC status:FDC状态寄存器
FDC status用于反映软盘驱动器FDC的基本状态。通常,在CPU想FDC发送命令或从FDC获取结果前,都要读取FDC的状态为,以判断当前的FDC data寄存器是否就需,以及确定数据传输方向。
位 Name Description
7 RQM Data ready: FDD ready
6 DIO Direction: 1 - FDD to CPU; 0 – CPU to FDD
5 NDM DMA set: 1-not DMA; 0-DMA
4 CB Controller busy
3 DDB Driver D busy
2 DCB Driver C busy
1 DBB Driver B busy
0 DAB Driver A busy
FDC Data寄存器用于向FDC发送控制命令或从FDC读取状态,实现数据读写等。FDC的使用比较复杂,可支持多种命令。每个命令都通过一个命令序列实现:命令阶段、执行阶段和结果阶段。
1) 重新校正命令(FD_RECALIBRATE)软盘启动时调用
阶段 序 D7 D6 D5 D4 D3 D2 D1 D0 说明
cmd 0 0 0 0 0 0 1 1 1 0x07
1 0 0 0 0 0 0 US1 US2 Drive no.
执行 磁头移动到track0
结果 无 无
2) 磁头寻道命令(FD_SEEK)
把磁头定位到制定位置,在读写前执行
阶段 序 D7 D6 D5 D4 D3 D2 D1 D0 说明
cmd 0 0 0 0 0 0 1 1 1 0x0F
1 0 0 0 0 0 HD US1 US2 磁头号、驱动器号
2 C 磁道号
执行 磁头移动到制定磁道
结果 无 无
3) 读扇区数据命令(FD_READ)
阶段 序 D7 D6 D5 D4 D3 D2 D1 D0 说明
cmd 0 MT MF SK 0 0 1 1 0 0xE6(MT=MF=SK=1)
1 0 0 0 0 0 0 US1 US2 驱动器号
2 C 磁道号track
3 H 磁头号head
4 R 起始扇区号start sector
5 N 扇区字节数
6 EOT 磁道最大扇区号
7 GPL 扇区建间隔长度(3)
8 DTL N=0时,制定扇区字节书
执行 从软盘读取扇区
结果 1 ST0 状态字节0
2 ST1 状态字节1
3 ST2 状态字节2
4 C 磁道号track
5 H 磁头号head
6 R 起始扇区号
7 N 扇区字节数
注:
MT:多磁道操作。MT=1表示允许多磁道操作
MF:记录方式。MF=1表示选用MFM记录方式,否则是FM记录方式d
SK:是否跳过有删除标志的扇区。SK=1表示跳过。
返回的返回的状态ST0、ST1和ST2的含义如下:
ST0:
位 名称 说明
7-6 ST0_INTR 中断原因。00-正常结束;01-异常结束;10-命令无效;11-软盘驱动器状态改变
5 ST0_SE 寻道操作或重新校正操作结束(seek end)
4 ST0_ECE 设备检查错误(0磁道校正错误)(Equip. Check Error)
3 ST0_NR 软盘未就绪(Not Ready)
2 ST0_HA 磁头地址。中断时磁头地址(Head Address)
1-0 ST0_DS 驱动器号(Driver Select)
ST1:
位 名称 说明
7 ST1_EOC 范文超过磁道最大扇区号(End of Cylinder)
6 Reserve
5 ST1_CRC CRC校验出错
4 ST1_OR 数据传输超时(Over Run)
3 Reserve
2 ST1_ND 未找到制定扇区(No Data)
1 ST1_WP 写保护(Write Protect)
0 ST1_MAM 未找到扇区地址标志ID(Miss Address Mask)
ST2:
位 名称 说明
7 Reserve
6 ST2_CM SK=0时,读数据遇到删除标志(Control Mark)
5 ST2_CRC CRC校验出错
4 ST2_WC 扇区ID信息的磁道号C不不符(Wrong Cylinder)
3 ST2_SEH 检索条件满足(Scan Equal Hit)
2 ST2_SNS 检索条件不满足(Scan Not Satisfied)
1 ST2_BC 磁道坏(Bad Cylinder)
0 ST2_MAM 未找到扇区地址标志ID(Miss Address Mask)
4) 写扇区数据命令(FD_WRITE)
阶段 序 D7 D6 D5 D4 D3 D2 D1 D0 说明
cmd 0 MT MF 0 0 0 1 0 1 0xC5(MT=MF=1)
1 0 0 0 0 0 0 US1 US2 磁头号、驱动器号
2 C 磁道号track
3 H 磁头号head
4 R 起始扇区号start sector
5 N 扇区字节数
6 EOT 磁道最大扇区号
7 GPL 扇区建间隔长度(3)
8 DTL N=0时,制定扇区字节书
执行 向软盘写入扇区
结果 1 ST0 状态字节0
2 ST1 状态字节1
3 ST2 状态字节2
5) 检测中断状态命令(FD_SENSEI)
阶段 序 D7 D6 D5 D4 D3 D2 D1 D0 说明
cmd 0 0 0 0 0 0 1 1 1 0x08
执行
结果 1 ST0 状态字节0
2 磁头所在磁道号
6) 设定驱动器参数命令(FD_SPECIFY)
阶段 序 D7 D6 D5 D4 D3 D2 D1 D0 说明
cmd 0 0 0 0 0 0 1 1 1 0x03
1 4-7:SRT单位2ms,0-3:HUT单位32ms 马达速度、磁头卸载时间
2 1-7:HLT单位4ms,0:ND 磁头加载时间,非DMA模式
执行 设置控制器
结果 无 无
DIR:数字输入寄存器
DIR寄存器只有D7位有效,用于表示软盘更换状态,其余用于硬盘控制器。
DCR:磁盘控制寄存器
DCR仅是用户D0与D1位,用于表示数据传输率。
00-500kpbs, 01-300kpbs, 10-250kpbs。
1)复位-Reset
- outb(FLOPPY_REG_DOR, 0x08);// 重启
- for (i=0; i<100 ; i++)
- __asm__("nop");// 延时,保证重启完成
- outb(FLOPPY_REG_DOR, 0xc);// 选择DMA模式,选择软驱A
1) 设置磁盘数据传输速度
outb(FD_DCR, 0); // 500kpbs
2) Output_byte函数用于FDC命令的输出,FDC的每条命令需要确保上条命令已经完成
- static void output_byte(char byte)
- {
- int counter;
- unsigned char status;
- for(counter= 0 ; counter< 10000 ; counter++){
- status = inb(FD_STATUS)& (STATUS_READY| STATUS_DIR);
- if (status == STATUS_READY){
- outb(FD_DATA, byte);
- return;
- }
- }
- printf("Unable to send byte to FDC\r");
- }
3) 设置驱动器参数
- output_byte(FD_SPECIFY);
- output_byte(0xCF);/* 马达步进速度、磁头卸载时间=32ms*/
- output_byte(6);/* Head loadtime =6ms, DMA*/
读扇区
- typedef struct {
- unsigned int size, sect, head, track, stretch;
- unsigned char gap,rate,spec1;
- }floppy_struct;
- static floppy_struct floppy_type =
- {2880,18,2,80,0,0x1B,0x00,0xCF}; /* 1.44MB diskette*/
- static u32 current_dev = 0;
- /* (2 * 18 * 80* 512) */
- void FloppyReadSector(u32 sectNo, u8*buf)
- { u32 head, track, block, sector, seek_track;
- if (NULL== buf)
- { printf("FloppyReadSector para error.\r");
- return;
- }
- if (sectNo>= (floppy_type.head * floppy_type.track * floppy_type.sect))
- {
- printf("FloppyReadSector sectNo error: %x.\r", sectNo);
- return;
- }
- /* 计算参数*/
- sector = sectNo % floppy_type.sect + 1;
- block = sectNo / floppy_type.sect;
- track = block / floppy_type.head;
- head = block % floppy_type.head;
- seek_track = track << floppy_type.stretch;
- /* 软盘重新校正*/
- output_byte(FD_RECALIBRATE);
- output_byte(current_dev);
- /* 寻找磁道*/
- output_byte(FD_SEEK);
- output_byte(current_dev);
- output_byte(seek_track);
- /* 设置DMA,准备传送数据*/
- SetDMA(buf, FD_READ);
- /* 发送读扇区命令*/
- output_byte(FD_READ);/* command */
- output_byte(current_dev);/* driver no.*/
- output_byte(track);/* track no.*/
- output_byte(head);/* head */
- output_byte(sector);/* start sector*/
- output_byte(2);/* sector size= 512 */
- output_byte(floppy_type.sect);/* Max sector*/
- output_byte(floppy_type.gap);/* sector gap*/
- output_byte(0xFF);/* sector size(0xff when n!=0 ?)*/
- }
程序很清楚,不再多说,写命令于此类似。
唯一不清楚的是SetDMA函数。
我们在设置DOR时设置的DMA工作方式为enable,也就是说数据会通过DMA方式传送,因此必须设置DMA控制器。
1.1.2.3 DMA传输
- /* DMA commands*/
- #define DMA_READ 0x46
- #define DMA_WRITE 0x4A
- #define immoutb_p(val,port)\
- __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a"((char)(val)),"i"(port))
- void SetDMA(u8 *buf, u8 cmd)
- {
- long addr = (long)buf;
- Cli();
- /* mask DMA 2*/
- immoutb_p(4|2,10);
- /* output command byte. I don't know why, but everyone(minix, */
- /* sanches& canton) output this twice, firstto 12 then to 11 */
- __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"
- "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::
- "a" ((char)((cmd == FD_READ)?DMA_READ:DMA_WRITE)));
- /* 8 low bits of addr*/
- immoutb_p(addr,4);
- addr >>= 8;
- /* bits 8-15 of addr*/
- immoutb_p(addr,4);
- addr >>= 8;
- /* bits 16-19 of addr*/
- immoutb_p(addr,0x81);
- /* low 8 bits of count-1(1024-1=0x3ff)*/
- immoutb_p(0xff,5);
- /* high 8 bits of count-1*/
- immoutb_p(3,5);
- /* activate DMA 2*/
- immoutb_p(0|2,10);
- Sti();
- }
该函数由linux0.11移植而来,可参照DMA控制器手册进行设置。不看也可以,注释写得很清楚,拿过来用就是了