OS专题-开始引导磁盘
OS专题-开始引导磁盘
因为MBR的大小只有512字节,其内存空间不足以存储一个完整的OS,所以我们需要让MBR去加载磁盘数据并运行操作系统。
还好我们不用去处理磁盘的磁头和盘片的控制,BIOS已经将对应的ISR中断指令写入了内存,我们只需要调用即可。为此,我们将AL
设置为0x02
(以及具有所需柱面、磁头和扇区的其他寄存器)并触发中断int 0x13
。
mov ax, 0xFFFF
add ax, 1 ; ax = 0x0000 and carry = 1
这里我们是第一次使用溢出位,这是每个寄存器上存在的一个额外位,用于存储操作溢出其当前容量的标识。溢出位不直接访问,只能被控制指令进行跳转处理,例如: jc
指令(如果设置了溢出位则跳转)
BIOS会以AL中的数据为参数读取指定数量的扇区数据。
原理地址:
https://stanislavs.org/helppc/int_13-2.html
https://stanislavs.org/helppc/int_13-a.html
因这两个地址不太好访问,所以我把它搬运过来了,如下:
INT 13,2 - Read Disk Sectors
AH = 02
AL = number of sectors to read (1-128 dec.)
CH = track/cylinder number (0-1023 dec., see below)
CL = sector number (1-17 dec.)
DH = head number (0-15 dec.)
DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
ES:BX = pointer to buffer
on return:
AH = status (see <A HREF="int_13-1.html">INT 13,STATUS</A>)
AL = number of sectors read
CF = 0 if successful
= 1 if error
- BIOS disk reads should be retried at least three times and the
controller should be reset upon error detection
- be sure ES:BX does not cross a 64K segment boundary or a
DMA boundary error will occur
- many programming references list only floppy disk register values
- only the disk number is checked for validity
- the parameters in CX change depending on the number of cylinders;
the track/cylinder number is a 10 bit value taken from the 2 high
order bits of CL and the 8 bits in CH (low order 8 bits of track):
|F|E|D|C|B|A|9|8|7|6|5-0| CX
| | | | | | | | | | `----- sector number
| | | | | | | | `--------- high order 2 bits of track/cylinder
`------------------------ low order 8 bits of track/cyl number
INT 13,A - Read Long Sector (XT & newer)
AH = 0A
AL = number of sectors (1-121 dec.)
CH = track number (0-1023 dec., see below)
CL = sector number (1-17 dec., see below)
DH = head number (0-15 dec.)
DL = fixed drive number (80h=drive 0, 81h=drive 1)
ES:BX = address of buffer
on return:
AH = status (see <A HREF="int_13-1.html">INT 13,STATUS</A>)
AL = number of sectors actually transferred
CF = 0 if successful
= 1 if error
- BIOS disk reads should be retried at least three times and the
controller should be reset upon error detection
- many good programming references indicate this function is only
available on the AT, PS/2 and later systems, but all hard disk
systems since the XT have this function available
- reads regular data sectors (128-1024 bytes) with an additional
4 byte ECC code included
- a DMA boundary error will occur if the buffer at ES:BX crosses
a 64K segment boundary
- only the disk number is checked for validity
- the parameters in CX change depending on the number of cylinders;
the track/cylinder number is a 10 bit value taken from the 2 high
order bits of CL and the 8 bits in CH (low order 8 bits of track):
|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0| CX
| | | | | | | | | | `-------------- sector number
| | | | | | | | `-------------- high order 2 bits of track number
`-------------------------- low order 8 bits of track number
接下来还是做个样例来看看吧,在lib
文件夹中新建disk.asm
文件,代码如下:
; lib/disk.asm
; 将dh扇区从驱动器dl加载到 ES:BX 位置
disk_load:
pusha
; 从磁盘读取需要在所有寄存器中设置新的值,所以会覆盖DX寄存器
; 这里让我们将它保存到堆栈中以备以后使用。
push dx
mov ah, 0x02 ; ah <- int 0x13 ISR. 0x02 = 'read'
mov al, dh ; al <- 读取的扇区数量 (0x01 .. 0x80)
mov cl, 0x02 ; cl <- 起始扇区 (0x01 .. 0x11)
; 0x01 是MBR扇区, 0x02 是真正的起始扇区
mov ch, 0x00 ; ch <- 柱面 (0x0 .. 0x3FF)
; dl <- 驱动器号。调用者将其设置为一个参数,并从BIOS获取它
; (0 = 软盘, 1 = 软盘2, 0x80 = 硬盘驱动器, 0x81 = 硬盘驱动器2)
mov dh, 0x00 ; dh <- 磁头数 (0x0 .. 0xF)
; [es:bx] <- 即将存入数据的内存地址
; 调用者已经设置了这个地址,它实际上是`int 13h`的标准位置
int 0x13 ; 触发ISR
jc disk_error ; 判断是否存在错误
pop dx
cmp al, dh ; 判断读取到的扇区数是否一致
jne sectors_error
popa
ret
disk_error:
mov bx, DISK_ERROR
call print
call print_nl
mov dh, ah ; ah = 错误代码, dl = 删除错误的磁盘驱动器
call print_hex ; 可以在这儿查看错误代码 http://stanislavs.org/helppc/int_13-1.html
jmp disk_loop
sectors_error:
mov bx, SECTORS_ERROR
call print
disk_loop:
jmp $
DISK_ERROR: db "Disk read error", 0
SECTORS_ERROR: db "Incorrect number of sectors read", 0
修改boot/mbr.asm
文件,代码如下:
[org 0x7c00]
mov bp, 0x8000 ; 设置数据存放位置
mov sp, bp
mov bx, 0x9000 ; es:bx = 0x0000:0x9000 = 0x09000
mov dh, 2 ; 读取两个扇区
; BIOS将'DL'设置为硬盘号(hdd0),此时我们使用的正是硬盘号
; 如果启动时出现问题,请使用'-fda'参数: 'qemu -fda file.bin'
call disk_load
mov dx, [0x9000] ; 打印第一个数据
call print_hex
call print_nl
mov dx, [0x9000 + 512] ; 答应第二个扇区的数据
call print_hex
jmp $
%include "print.asm"
%include "print_hex.asm"
%include "disk.asm"
times 510 - ($-$$) db 0
dw 0xaa55
; 引导扇区 = hdd0的0磁头0的0柱面1扇区
; 然后是2扇区
times 256 dw 0xdada ; sector 2 = 512 bytes
times 256 dw 0xface ; sector 3 = 512 bytes
这样我们就完成了磁盘的引导、加载并打印了数据。
OS专题-开始引导磁盘
https://blog.cikaros.top/doc/b5315bfb.html