30天自制操作系统 - 用INT 0x13/AH=0x42h读磁盘

背景

基本上,每个硬盘除了每扇(Sector)大小依旧是512字节外,柱面(Cylinder)数、磁头(Header)数都不一致。

用磁盘来取代原作者的软盘后,沿用原来的INT 0x13/AH=2h读磁盘的话会非常蹩脚并且非常不方便。参考Windows 7也是用INT 0x13/AH=0x42h读磁盘。用该方法读磁盘只需要关注到扇区即可,不需要关注到柱面跟磁头数,很直观。

调试

Bochs

这次采用了bochs2.6.9来支持。这虚拟机对编写操作系统的人来说非常有帮助!贴出本次用到的配置文件如下。对应目录则对应修改成本地的,这里不在另外赘述。

###############################################################
# Configuration file for Bochs
###############################################################

# how much memory the emulated machine will have
megs: 32

# 对应真实机器的BIOS和VGA BIOS
romimage: file="C:\Program Files (x86)\Bochs-2.6.9\BIOS-bochs-latest"
vgaromimage: file="C:\Program Files (x86)\Bochs-2.6.9\VGABIOS-lgpl-latest"

# 设置bochs使用的磁盘,软盘使用关键字floppya,硬盘使用disk
# 若有多个软盘,可写floppya,floppyb
#floppya: 1_44=a.img, status=inserted

# choose the boot disk.
# 默认是软盘,注释掉,改为disk
#boot: floppy
boot: disk
# where do we send log messages?
log: D:\Documents\bochs\bochsout.txt
# disable the mouse
mouse: enabled=0

# enable key mapping, using US layout as default.
keyboard: keymap="C:\Program Files (x86)\Bochs-2.6.9\keymaps\x11-pc-us.map"

ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14

#这一句是根据bximage生成的,后面会解释。
ata0-master: type=disk, path="D:\Documents\bochs\c.img", mode=flat, cylinders=20, heads=16, spt=63

magic_break: enabled=1    

通过以下命令进行调试。bochsdbg.exe可以进行调试,但是bochs.exe则不行,需要注意。

bochsdbg -f "D:\Documents\bochs\bochs.disk"

magic_break开启后,虚拟机会把

XCHG    BX, BX

当成断点停下来,方便调试。

错误

如果INT 0x13/AH=0x42h调用时用了错误的参数,除了CF位一定会置1外,还可能会

  • AH=42h
  • AH=1h

我在真机上试验到是AH莫名其妙还是42h,在bochs的虚拟机里则是1h。所以基本上不要真的把AH当成错误号来查询,你基本上是查不到什么有效信息的。只要LBA测试到是支持,那么INT 0x13/AH=0x42h肯定是支持的,如果还调用错误,那肯定是参数设置不对,需要仔细排查一下。

方案

是否支持

并非所有BIOS都支持INT 0x13/AH=0x42h,但是基本上目前绝大多数都支持。但是为了兼容性,还是最好判断一下。

        MOV        BX, 0x55AA
        MOV        AH, 0x41
        INT        0x13
        JC        _10_stand_bios
        CMP        BX, 0xAA55
        JNE        _10_stand_bios
        TEST    CL, 1        
        JZ        _10_stand_bios

可以通过以上方法简单判断到是否支持LBA。

如果不幸BIOS不支持,可以考虑回归之前的读取方式。不过我没做兼容。

修改点

由于我改写的ipl10.nas只支持nasm编译,所以在对应代码层级的Makefile文件里,需要做出一些变更。替换原来ipl10.bin为以下编译方式(自行设定好nasm的所在目录):

ipl10.bin : ipl10.nas Makefile
    nasm ipl10.nas
    ren ipl10 ipl10.bin

另外,由于没有采用原书代码里把最后读到的柱面数写入:

MOV        [0x0ff0],CH

所以在asmhead.nas里相应的要自己填上大致读到的Sector数。由于不断深入开发,haribote.sys的大小必定越来越大,所以镜像生成后要多关注一下0x4200开始haribote.sys的内容到哪里结束。假设到0x6200结束,那么大致读上来的扇区数目就是:

0x6200 / 512

在asmhead.nas相应修改如下:

; 残り全部

        MOV        ESI,DSKCAC0+512    ; 転送元
        MOV        EDI,DSKCAC+512    ; 転送先
        ;MOV        ECX,0
        MOV        ECX, 512 * 50 / 4 ; read by INT 0x13/AH = 42h
        ;MOV        CL,BYTE [CYLS]
        ;IMUL    ECX,512*18*2/4    ; シリンダ数からバイト数/4に変換
        ;SUB        ECX,512/4        ; IPLの分だけ差し引く
        CALL    memcpy

由于没有做太多的测试,重新编译的时候可能还是需要手动把ipl10.bin删除,各位可以自己修改一下对应的命令。

LoadSectors: ; buffer = EBX, size = ECX, begin sector = EDI
        PUSH     EBP
        MOV        EBP, ESP
        SUB        ESP, 0x10              ; size of DAP
        MOV        AX, 0x4200            ; reads sectors into memory
        MOV        EDX, ECX
        SAR        ECX, 6                ; / 64
        AND        EDX, 0x8000003F      ; % 64
        MOV        BYTE[ESP], 0x10        ; size of DAP
        MOV        BYTE[ESP + 1], 0    ; reserved for 0
        MOV        WORD[ESP + 2], 64   ; 64 sectors
        MOV        DWORD[ESP + 12], 0  ; always 0
        MOV        ESI, ESP            ; DAP addr
        PUSH      EDX
        MOV        DL, 0x80            ; drive number
_8_loop:
        PUSH    ECX
        MOV        ECX, EBX
        AND        CX, 0xF
        MOV        WORD[EBP - 0x10 + 4], CX    ; offset
        MOV        ECX, EBX
        SHR        ECX, 4
        MOV        WORD[EBP - 0x10 + 6], CX  ; segment
        POP        ECX
        MOV        DWORD[EBP - 0x10 + 8], EDI ; begin sector
        CMP        ECX, 0
        JLE        _6_left
        INT        0x13
        JC        _9_error        
        ADD        EDI, 64                ; sector
        ADD        EBX, 512 * 64        ; bytes
        DEC        ECX
        JMP        _8_loop
_6_left:
        POP        EDX
        CMP        EDX, 0
        JLE        _7_leave
        MOV        WORD[ESP + 2], DX   ; left sectors
        MOV        DL, 0x80
        INT      0x13
        JC        _9_error        
        JMP        _7_leave
_9_error:
        MOV        AL, 0
        PUSH    AX
        MOV        CX, 2
        MOV        SI, SP
        CALL    print_byte_str
        ;PUSH    0
        ;PUSH    'rr'
        ;PUSH    're'               ; error
        ;MOV        SI, SP
        ;CALL    print_string        
_7_leave:
        MOV        ESP, EBP
        POP        EBP
        RET

下载

几乎把对应源码改了个遍,基本上找不到太多原来ipl10.nas的影子了。

里面LoadSectors是本篇的核心:ipl10.rar

你可能感兴趣的:(30天自制操作系统)