admin 管理员组文章数量: 1184232
Cpu 启动时,如果在 bios 中设置了从软盘启动,则 bios 会自动把软盘的第一个扇区( 512 字节)搬移到 0x7c00 ,然后会从 0x7c00 开始运行,我们需要在这 512 字节的程序中实现把 boot 从软盘中搬移到内存中的功能。
此时 x86 系列的 cpu 还处在实模式中,可以利用 bios 提供的中断完成软盘读写,也就是著名的 int 13 。
1 软盘操作 1.1 软盘结构
以 1.44M 软盘为例,软盘有上、下两个盘面,每个盘面被划分为 80 个 track ,每个 track 被划分为 18 个 Sector ,每个 Sector 大小为 512 BYTES 。
2 × 80 × 18 × 512 = 1440 × 1024 = 1.44MB
如果是单纯的进行软盘读写操作,知道软盘的这些知识就够了。
1.2 软盘读写中断号 | 寄存器 | 作用 |
13h | ah=00h al= 驱动器号( 0 表示 A 盘) | 复位软驱 |
ah=02h al= 要读扇区数 ch= 磁道号 cl= 起始扇区号 dh= 磁头号 dl= 驱动器号 es:bx= 数据缓冲区 | 从 cl/ch/dh/dl 指向的扇区开始读取 al 个扇区的数据到 es:bx 指向的缓冲区 | |
ah=03h al= 要写扇区数 ch= 磁道号 cl= 起始扇区号 dh= 磁头号 dl= 驱动器号 es:bx= 数据缓冲区 | 从 es:bx 指向的缓冲区读取 al 个扇区的数据写入 cl/ch/dh/dl 指向的 al 个扇区 |
对于软盘来说, DL=0 ( 0 表示 A 盘)
CL 的取值范围是: 1—18
CH 的取值范围是: 0—79
DH 的取值范围是: 0—1
如果软盘的 CL , CH , DH 取值超过了范围,中断调用出错。
使用 int13 时对应扇区数的计算方法如下:
1.3 软盘编程
用软盘存放操作系统时, bios 只会把前 512 个字节(即第一个扇区,引导区)读入内存的 0x7c00 处执行。程序的长度也被限制在了 512 字节大小,一个出入保护模式的程序就会超出。
为了突破 512 字节的限制,需要在这个扇区内放置一段引导程序,用于把后面的程序加载到内存中。这也是第一个扇区被称为引导区的原因。
下面的就是一段简单的加载程序, reset 软驱后从 LABEL_SECTOR2 开始读入 17 个扇区。加上 0 号扇区(引导区),一共是 18 个扇区,也就是第一个磁道。这是因为 13 号中断不能跨磁道读取,所以这里只读入第一个磁道剩下的 17 个扇区。
; reset floppy xor ah, ah xor dl, dl int 13h ; read sector 2 to memory mov ah, 03h mov al, 11h mov ch, 00h mov cl, 03h mov dh, 00h mov dl, 00h mov bx, 0100h int 13h |
下面的程序用于保证除引导程序外的所有程序都在引导区之外,完成把磁盘内容拷贝入 memory 的操作后,就会跳入 LABEL_SECTOR2 。
jmp LABEL_SECTOR2 ; fill boot sector times 510 - ($ - $$) db 1 dw 0xaa55 ; Sector 2 LABEL_SECTOR2: |
下面是完整的程序代码
org 07c00h jmp LABEL_BEGIN LABEL_BEGIN: mov ax, cs mov ds, ax mov ss, ax mov es, ax ; reset floppy xor ah, ah xor dl, dl int 13h ; read sector 2 mov ah, 02h mov al, 01h mov ch, 00h mov cl, 02h mov dh, 00h mov dl, 00h mov bx, LABEL_SECTOR2 int 13h jmp LABEL_SECTOR2 ; fill boot sector uutimes 510 - ($ - $$) db 0 dw 0xaa55 ; Sector 2 jmp into loader ; org 0100h LABEL_SECTOR2 : mov ax, cs mov ds, ax mov es, ax call DispStr ; 调用显示字符串例程 jmp $ ; 无限循环 DispStr: mov ax, BootMessage mov bp, ax ; ES:BP = 串地址 mov cx, 16 ; CX = 串长度 mov ax, 01301h ; AH = 13, AL = 01h mov bx, 000ch ; 页号为 0(BH = 0) 黑底红字 (BL = 0Ch, 高亮 ) mov dl, 0 int 10h ; 10h 号中断 ret BootMessage: db "Hello, OS world!" ; fill floppy times (2 * 18 * 80 * 512 - ($ - $$)) db 0 |
软盘 ,INT 13H 和 IMG 文件
1: 软盘
软盘是以扇区为基本单位来进行操作的,每扇区 512 字节,共 2880 个扇区,空间大小为 1.44M.
这 2880 个扇区又可以用(磁头、磁道、扇区)这三个参数来描述。我们简单记(磁头、磁道、扇区)为( x,y,z ),那么
X 的取值范围是: 0—1
Y 的取值范围是: 0--79
Z 的取值范围是: 1—18
也就是说软盘有 2 磁头、每磁头有 80 磁道、每磁道有 18 扇区 , 共 2880 个扇区( 2880=2*80*18 )。
如果把 2880 个扇区从 0 开始编号,一直到 2879 结束,那么
编号为 i 的扇区和( X,Y,Z )的换算公式为: i=80*18*x+18*y+z-1
2:INT 13H
INT13H 是磁盘的 BOIS 中断 , 对于读写扇区操作,中断的完整调用参数如下 :
AL= 扇区数
AH= 中断子功能号 ;2= 读扇区, 3= 写扇区
CL 的 6 , 7 位 ,CH = 磁道号 ; 每磁头最多可以有 2^10=1024 个磁道
CL 的低 6 位 = 扇区号 ; 每磁道最多可以有 2^6=64 个扇区
DH = 磁头号 ; 最多可以有 256 个磁头
DL = 驱动器号 ;0= 软盘, 80H= 硬盘
ES:BX= 数据缓冲区的地址
其他:
1).
对于软盘来说,实际的 INT 13H 中 DL=0 ,而 CL , CH , DH 的取值范围也不可能取到上面的数值,根据 1: 中的数据,有
CL 的取值范围是: 1--18
CH 的取值范围是: 0-79
DH 的取值范围是: 0—1
如果软盘的 CL , CH , DH 取值超过了范围,中断调用出错。
AL 的取值也不是任意的,一次调用 INT13H 进行读写扇区的只可以在一个磁道内的扇区进行。如果超出了一个磁道,必须要更新 INT13H 的中寄存器,重复调用 INT13H 。
对于软盘来说 AL 必须要小于 19-CL 。
下面是一个从软盘的 (x,y,z) 扇区中连续读出 n 个扇区的内容到缓冲区 BUFFER 中的代码片段 ( 代码使用了一些 80386 的指令和 MASM6.0 才支持的伪指令 ) :
MOV AX,DS
MOV ES,AX
MOV BX,OFFSET
MOV BP, n
MOV CL , z
MOV CH , y
MOV DL , 0
MOV DH , x
.WHILE BP ;bp 记录的是还没有进行读操作的扇区数量
;WHILE 伪指令,和高级语言的 WHILE 一样理解就可以了,下面的。 IF 也一样
; 对 AL 的取值进行计算
MOV AL,19
SUB AL,CL ; 最多可以读 19-CL 个扇区
XOR AH,AH
.IF AX>BP
MOV AX,BP
XOR BP,BP
.ELSE
SUB BP,AX ;更新 BP
.ENDIF
MOV AH,2
INT 13H ; 读扇区
; 更新 CL , CH , DH
MOV CL,1
.IF CH==79
INC DH
XOR CH,CH
.ELSE
INC CH
.ENDIF
XOR AH,AH
SHL AL,9 ;AX=AL*512 ,等于已经处理的字节数
;SHL AL,9 和 SHL AX , 4 都是 80386+ 才支持的指令
SHR AX,4 ;AX=AX/16
ADD ES,AX ; 更新 ES : BX ,这里是更新了 ES ,也可以更新 BX 。
.endw
2).
INT13H 只理论上最多处理 2^24 个扇区 *512 字节 / 扇区 =8G 的磁盘空间,这对现在的硬盘来说,是远远不够的,于是后来对 INT13H 进行了扩展,用 AH=42H 、 AH=43H 分别对大硬盘进行操作,这里就不详细讨论了。
3:IMG 文件
IMG 文件是软盘的镜像文件,文件大小也是 1.44M ,它和软盘是一种线性的对应关系。
软盘上一个编号为 (x,y,z) 的 512 字节的扇区,对应 IMG 文件中的以 2400H*x+4800H*y+(z-1)*512 为基址的 512 个字节(注意 ,2400H,4800H 是 16 进制数)。
IMG 文件的字节和软盘的扇区对应关系也可以如下图所示 ( 注意那些是十进制,那些是 16 进制 ) 。
对于扩展 INT 13 中断,参数如下:
中断号 功能 调用寄存器 返回寄存器 备注
INT 13
AH=41H 检测扩展中断功能是否安装 AH = 41h
BX=55AAh
DL = 驱动器号 (80h 到 FFH)
失败: AH=1
CF 置位
成功: AH= 版本号
CF=0 BX=AA55H
INT 13
AH=42H 磁盘扩展读操作 AH = 42H
DL = 驱动器号
DS:SI= 指向 LBA 地址包的指针 失败: AH= 错误号
CF 置位
成功: AH=0
CF=0
地址包定义:
偏移 大小 描述
00H 字节 地址包大小
01H 字节 保留(为 0 )
02H 字 传输包个数
04H 双字 指向数据指针
08H 4 字 起始地址
其中 LBA=(( 柱面 * 磁头 / 柱面 + 磁头 )* 扇区 / 柱面 )+ 扇区 -1
INT 13
AH=43H 磁盘扩展写操作 AH=43H
AL= 写标志
DL = 驱动器号
DS:SI= 指向 LBA 地址包的指针 失败: AH= 错误号
CF 置位
成功: AH=0
CF =0 同上
INT 13
AH=48H 获取磁盘参数 AH=48H
DL= 驱动器号
DS:SI= 指向保存参数缓冲区的指针
失败: AH= 错误号
CF 置位
成功: AH=0
CF=0
参数缓冲区定义:
偏移 大小 描述
00H 字 缓冲区大小
02H 字 信息标志位
04H 双字 物理柱面数
08H 双字 物理磁头数
0CH 双字 物理每柱扇区数
10H 4 字 扇区总数
18H 字 每扇区字节数
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 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 |
0 | DRV_SEL0 |
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 | 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 | 未找到扇区地址标志 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 | SRT(单位2ms) | HUT(单位32ms) | 马达速度、磁头卸载时间 | |||||||
2 | HLT(单位4ms) | ND | 磁头加载时间,非 DMA模式 | |||||||
执行 | 设置控制器 | |||||||||
结果 | 无 | 无 | ||||||||
DIR寄存器只有D7位有效,用于表示软盘更换状态,其余用于硬盘控制器。
1.1.1.5 DCR:磁盘控制寄存器DCR仅是用户D0与D1位,用于表示数据传输率。
00-500kpbs, 01-300kpbs, 10-250kpbs。
1.1.2 保护模式下代码实现 1.1.2.1 初始化1) Reset
1) 设置磁盘数据传输速度
outb(FD_DCR, 0); // 500kpbs
2) Output_byte函数
用于 FDC命令的输出,FDC的每条命令需要确保上条命令已经完成
3) 设置驱动器参数
1.1.2.2 读扇区程序很清楚,不再多说,写命令于此类似。
唯一不清楚的是 SetDMA函数。
我们在设置 DOR时设置的DMA工作方式为enable,也就是说数据会通过DMA方式传送,因此必须设置DMA控制器。
1.1.2.3 DMA传输该函数由 linux0.11移植而来,可参照DMA控制器手册进行设置。不看也可以,注释写得很清楚,拿过来用就是了
版权声明:本文标题:软盘玩转新天地:X86保护模式下的无BIOS操作 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1773520100a3562873.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论