Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1057605
  • 博文数量: 118
  • 博客积分: 3888
  • 博客等级: 中校
  • 技术积分: 2938
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-10 18:15
个人简介

一看二做三总结

文章分类

全部博文(118)

分类: 嵌入式

2011-08-03 22:37:01

本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接,严禁用于任何商业用途。
作者:fireaxe_hq@hotmail.com
博客:fireaxe.blog.chinaunix.net 
1.1      保护模式下实现软盘编程

要发挥x86芯片的功能,必须要进入保护模式。

系统启动时所加载的512字节的MBR区为bootloader区,用于加载真正的boot程序。在bootloader区中,cpu还运行于实模式,因此bootloader通过bios中断加载boot。进入boot区后,一般来说已经完成了切换入保护模式的动作。

在保护模式下,不能使用bios中断,需要通过读写软盘控制器芯片8237来完成。

1.1.1  软盘控制器

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

注:FDC为软盘控制器

1.1.1.1          DOR数字输出寄存器

DOR是一个8为寄存器,他控制驱动器马达的开启、驱动器选择、启动/复位FDC以及允许/禁止DMA请求

Name

Description

7

MOT_EN3

Driver D motor1-start0-stop

6

MOT_EN2

Driver C motor1-start0-stop

5

MOT_EN1

Driver B motor1-start0-stop

4

MOT_EN0

Driver A motor1-start0-stop

3

DMA_INT

DMA interrupt 1 enable; 0-disable

2

RESET

FDC Reset

1

DRV_SEL1

Select driver

0

DRV_SEL0

 

1.1.1.2          FDC StatusFDC状态寄存器

FDC status用于反映软盘驱动器FDC的基本状态。通常,在CPUFDC发送命令或从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

 

1.1.1.3          FDC DataFDC数据寄存器

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

0xE6MT=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表示跳过。

返回的返回的状态ST0ST1ST2的含义如下:

ST0

名称

说明

7

ST0_INTR

中断原因。00-正常结束;01-异常结束;10-命令无效;11-软盘驱动器状态改变

6

5

ST0_SE

寻道操作或重新校正操作结束(seek end

4

ST0_ECE

设备检查错误(0磁道校正错误)(Equip. Check Error

3

ST0_NR

软盘未就绪(Not Ready

2

ST0_HA

磁头地址。中断时磁头地址(Head Address

1

ST0_DS

驱动器号(Driver Select

0

 

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

未找到扇区地址标志IDMiss 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

未找到扇区地址标志IDMiss Address Mask

 

4)  写扇区数据命令(FD_WRITE

阶段

D7

D6

D5

D4

D3

D2

D1

D0

说明

cmd

0

MT

MF

0

0

0

1

0

1

0xC5MT=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

SRT(单位2ms

HUT(单位32ms

马达速度、磁头卸载时间

2

HLT(单位4ms

ND

磁头加载时间,非DMA模式

执行

 

 

设置控制器

结果

 

 

1.1.1.4          DIR:数字输入寄存器

DIR寄存器只有D7位有效,用于表示软盘更换状态,其余用于硬盘控制器。

1.1.1.5          DCR:磁盘控制寄存器

DCR仅是用户D0D1位,用于表示数据传输率。

00-500kpbs, 01-300kpbs, 10-250kpbs

1.1.2  保护模式下代码实现 1.1.2.1          初始化

1)  Reset

 

  1. outb(FLOPPY_REG_DOR, 0x08); // 重启

  2. for (i=0 ; i<100 ; i++)

  3. __asm__("nop"); // 延时,保证重启完成

  4. outb(FLOPPY_REG_DOR, 0xc); // 选择DMA模式,选择软驱A

1)  设置磁盘数据传输速度

outb(FD_DCR, 0); // 500kpbs

2)  Output_byte函数

用于FDC命令的输出,FDC的每条命令需要确保上条命令已经完成

 

  1. static void output_byte(char byte)

  2. {

  3.      int counter;

  4.      unsigned char status;



  5.      for(counter = 0 ; counter < 10000 ; counter++) {

  6.             status = inb(FD_STATUS) & (STATUS_READY | STATUS_DIR);

  7.             if (status == STATUS_READY) {

  8.                    outb(FD_DATA, byte);

  9.                    return;

  10.             }

  11.      }

  12.      printf("Unable to send byte to FDC\r");

  13. }

3)  设置驱动器参数

 

  1. output_byte(FD_SPECIFY);

  2. output_byte(0xCF); /* 马达步进速度、磁头卸载时间=32ms */

  3. output_byte(6); /* Head load time =6ms, DMA */

 

1.1.2.2          读扇区

 

  1. typedef struct {

  2.        unsigned int size, sect, head, track, stretch;

  3.        unsigned char gap,rate,spec1;

  4. }floppy_struct;



  5. static floppy_struct floppy_type =

  6.     {2880,18,2,80,0,0x1B,0x00,0xCF }; /* 1.44MB diskette */



  7. static u32 current_dev = 0;



  8. /* (2 * 18 * 80 * 512) */

  9. void FloppyReadSector(u32 sectNo, u8 *buf)

  10. {

  11.     u32 head, track, block, sector, seek_track;



  12.     if (NULL == buf)

  13.     {

  14.         printf("FloppyReadSector para error.\r");

  15.         return;

  16.     }



  17.     if (sectNo >= (floppy_type.head * floppy_type.track * floppy_type.sect))

  18.     {

  19.         printf("FloppyReadSector sectNo error: %x.\r", sectNo);

  20.         return;

  21.     }



  22.     /* 计算参数 */

  23.     sector = sectNo % floppy_type.sect + 1;

  24.     block = sectNo / floppy_type.sect;

  25.     track = block / floppy_type.head;

  26.     head = block % floppy_type.head;

  27.     seek_track = track << floppy_type.stretch;

  28.     

  29.     /* 软盘重新校正 */

  30.     output_byte(FD_RECALIBRATE);

  31.     output_byte(current_dev);

  32.     

  33.     /* 寻找磁道 */

  34.     output_byte(FD_SEEK);

  35.     output_byte(current_dev);

  36.     output_byte(seek_track);

  37.     

  38.     /* 设置DMA,准备传送数据 */

  39.     SetDMA(buf, FD_READ);



  40.     /* 发送读扇区命令 */

  41.     output_byte(FD_READ); /* command */

  42.     output_byte(current_dev); /* driver no. */

  43.     output_byte(track); /* track no. */

  44.     output_byte(head); /* head */

  45.     output_byte(sector); /* start sector */

  46.     output_byte(2); /* sector size = 512 */

  47.     output_byte(floppy_type.sect); /* Max sector */

  48.     output_byte(floppy_type.gap); /* sector gap */

  49.     output_byte(0xFF); /* sector size (0xff when n!=0 ?) */

  50. }

 

程序很清楚,不再多说,写命令于此类似。

唯一不清楚的是SetDMA函数。

我们在设置DOR时设置的DMA工作方式为enable,也就是说数据会通过DMA方式传送,因此必须设置DMA控制器。

 

1.1.2.3          DMA传输
  1. /* DMA commands */

  2. #define DMA_READ 0x46

  3. #define DMA_WRITE 0x4A



  4. #define immoutb_p(val,port) \

  5. __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port))



  6. void SetDMA(u8 *buf, u8 cmd)

  7. {

  8.     long addr = (long)buf;



  9.     Cli();

  10.     /* mask DMA 2 */

  11.     immoutb_p(4|2,10);

  12.     /* output command byte. I don't know why, but everyone (minix, */

  13.     /* sanches & canton) output this twice, first to 12 then to 11 */

  14.     __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"

  15.     "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::

  16.     "a" ((char) ((cmd == FD_READ)?DMA_READ:DMA_WRITE)));

  17.     /* 8 low bits of addr */

  18.     immoutb_p(addr,4);

  19.     addr >>= 8;

  20.     /* bits 8-15 of addr */

  21.     immoutb_p(addr,4);

  22.     addr >>= 8;

  23.     /* bits 16-19 of addr */

  24.     immoutb_p(addr,0x81);

  25.     /* low 8 bits of count-1 (1024-1=0x3ff) */

  26.     immoutb_p(0xff,5);

  27.     /* high 8 bits of count-1 */

  28.     immoutb_p(3,5);

  29.     /* activate DMA 2 */

  30.     immoutb_p(0|2,10);

  31.     Sti();

  32. }

该函数由linux0.11移植而来,可参照DMA控制器手册进行设置。不看也可以,注释写得很清楚,拿过来用就是了。

 

本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接,严禁用于任何商业用途。
作者:fireaxe_hq@hotmail.com
博客:fireaxe.blog.chinaunix.net 
阅读(3780) | 评论(0) | 转发(1) |
0

上一篇:VC学习--编译错误

下一篇:从效率到能力

给主人留下些什么吧!~~