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
作者
Cikaros
发布于
2022年9月23日
许可协议