喙审 发表于 2025-6-29 17:52:44

操作系统开发:文件读取寻址问题、解决方案及实验

问题

记录下自己学习自制操作系统时写MBR遇到的文件读取问题及解决方案。
简单来说就是LBA和CHS地址读取方式没分清。
环境


[*]bochs 虚拟机
[*]nasm 汇编
现象

照着文档或教程写的代码,扇区号啥的也没错,但文件无法读取。
经调试器检查,存储器状态接口与读入接口皆为0x0。
代码示例

这是LBA读文件的代码。
修改自从零开发操作系统
load_section:
        ; eax        Section to read from
        ; bx        Memory address to write to
        ; cx        Number of sections to read

        ; backup eax & cx
        mov esi, eax
        mov di, cx

        ; write arguments
        ; Number of sections
                mov dx, 0x1f2
                mov al, cl
                out dx, al
                mov eax, esi

        ; Section (LBA) addr
        ; 7~0
                mov dx, 0x1f3
                out dx, al
        ; 15~8
                mov cl, 8
                shr eax, cl
                mov dx, 0x1f4
                out dx, al
        ; 23~16
                shr eax, cl
                mov dx, 0x1f5
                out dx, al
        ; 27~24
                shr eax, cl
                and al, 0x0f
                ; 7~4==1110 => LBA mode
                or al, 0xe0
                mov dx, 0x1f6
                out dx, al

        ; write command
                mov dx, 0x1f7
                mov al, 0x20
                out dx, al

        ; wait until disk ready
        .wait_disk:
                nop
                in al, dx
                ; 4th digit==1 => ready
                ; 7th digit==1 => busy
                ; if (not (4th==1 and 7th!=1)) then wait
                and al, 0x88
                cmp al, 0x08
                jnz .wait_disk

        ; calc read times
                ; di = cx = sections to read
                mov ax, di
                ; 1 word (2 bytes) per time per section, one section 512 bytes
                ; total 256 times per section
                mov dx, 256
                ; sections to read * time to read per section
                ; mul dx => prod=dx*ax, dx=prod.high16, ax=prod.low16
                mul dx
                ; high16 ignored, cx=low16 for loop times
                mov cx, ax
                mov dx, 0x1f0

        .read_loop:
                in ax, dx
                mov , ax
                add bx, 2
                loop .read_loop
        ret这是CHS读文件的代码。
修改自《自己动手写操作系统》
BPB_SecPerTrk equ 18
BS_DrvNum equ 0

ReadSector:
        ; ax        Section to read from
        ; cl        Number of sectors to read
        ; bx        Destination

        ; 1.44MB = 2 * 80 * 18 * 512
        ; (One head for each side)
        ; (Side * TracksPerSide * SectorsPerTrack * BytesPerSector)
        ; SecToRead / SecPerTrk -> uotient, emainder
        ; CyldNum = Q >> 1, HeadNum = Q & 1, StartSec = R + 1
        push bp
        mov bp, sp
        sub esp, 2
        mov byte , cl
        push bx
        mov bl, BPB_SecPerTrk
        div bl
        inc ah
        mov cl, ah
        shr al, 1

        mov ch, al
        and dh, 1
        pop bx
        mov dl, BS_DrvNum
        .GoOnReading:
                ; int 0x13, ah=2 for reading to buffer
                ; al        number of sectors to read
                ; ch        cylinder number
                ; cl        first section number
                ; dh        header number
                ; dl        drive number (0 for drive A)
                ; es:bx        buffer
                mov ah, 2
                mov al, byte
                int 0x13
                jc .GoOnReading
                add esp, 2
                pop bp
                ret问题原因

我的问题出在代码与存储设备格式不匹配。
简称全称中文适用设备读取方式(不多赘述,具体请查找文档教程)LBALogical Block Addressing逻辑块 寻址多用于硬盘通过向设备写入地址与命令,并检查硬盘可用性,即可读取CHSCylinder Head Sector柱面-读头-扇区 寻址多用于软盘通过柱面、读头、扇区的三维坐标,调用系统中断,即可读取通过bochsrc中的floppya、floppyb指定软盘,ata[-master/-slave]指定硬盘。boot参数指定启动设备。
例:
# 软盘
floppya: 1_44="img/floppy.img", status=inserted
boot: floppy# 硬盘
ata0-master: path="img/hd.img", type=disk, mode=flat
boot: disk解决方案

LBA寻址一般给硬盘使用,CHS一般给软盘使用。
根据存储设备选择读取方式。
实验验证

点击展开工具


[*]bochs 虚拟机
[*]nasm 汇编
[*]bximage 虚拟盘制作,bochs自带
[*]dd 虚拟盘写入,大多类unix(linux和mac)自带
系统建议使用Linux或Mac,对于Windows用户bochsrc.bxrc与一些命令可能需更改,本文不多赘述。
代码

模拟mbr将操作系统载入内存并执行的过程
os.asm
%include "boot.inc"
section MBR vstart=OS_BASE_ADDR
        mov ax, 0xb800
        mov gs, ax

        ; print msg
        mov byte , 'O'
        mov byte , 0x02
        mov byte , 'S'
        mov byte , 0x02

        jmp $载入后会打印绿色OS字样。
mbr_lba.asm
%include "boot.inc"StackBase equ 0x7c00; load loader.asm into memorysection MBR vstart=StackBase        ; init        mov ax, cs        mov ds, ax        mov es, ax        mov ss, ax        mov sp, StackBase        ; video        mov ax, 0xb800        mov gs, ax        ; display clear        mov ax, 0x0600        mov bx, 0x0700        mov cx, 0x0000        ; dl=80, dh=25        mov dx, 0x184f        int 0x10        ; print msg        mov byte , 'M'        mov byte , 0x04        mov byte , 'B'        mov byte , 0x04        mov byte , 'R'        mov byte , 0x04        mov byte , ' '        mov byte , 0x04        ; load loader        mov eax, OS_START_SEC        mov bx, OS_BASE_ADDR        mov cx, 1        call load_section        jmp OS_BASE_ADDRload_section:
        ; eax        Section to read from
        ; bx        Memory address to write to
        ; cx        Number of sections to read

        ; backup eax & cx
        mov esi, eax
        mov di, cx

        ; write arguments
        ; Number of sections
                mov dx, 0x1f2
                mov al, cl
                out dx, al
                mov eax, esi

        ; Section (LBA) addr
        ; 7~0
                mov dx, 0x1f3
                out dx, al
        ; 15~8
                mov cl, 8
                shr eax, cl
                mov dx, 0x1f4
                out dx, al
        ; 23~16
                shr eax, cl
                mov dx, 0x1f5
                out dx, al
        ; 27~24
                shr eax, cl
                and al, 0x0f
                ; 7~4==1110 => LBA mode
                or al, 0xe0
                mov dx, 0x1f6
                out dx, al

        ; write command
                mov dx, 0x1f7
                mov al, 0x20
                out dx, al

        ; wait until disk ready
        .wait_disk:
                nop
                in al, dx
                ; 4th digit==1 => ready
                ; 7th digit==1 => busy
                ; if (not (4th==1 and 7th!=1)) then wait
                and al, 0x88
                cmp al, 0x08
                jnz .wait_disk

        ; calc read times
                ; di = cx = sections to read
                mov ax, di
                ; 1 word (2 bytes) per time per section, one section 512 bytes
                ; total 256 times per section
                mov dx, 256
                ; sections to read * time to read per section
                ; mul dx => prod=dx*ax, dx=prod.high16, ax=prod.low16
                mul dx
                ; high16 ignored, cx=low16 for loop times
                mov cx, ax
                mov dx, 0x1f0

        .read_loop:
                in ax, dx
                mov , ax
                add bx, 2
                loop .read_loop
        rettimes 510 - ($ - $$) db 0x0dw 0xaa55mbr_chs.asm
%include "boot.inc"
StackBase equ 0x7c00
; load os.asm into memory
section MBR vstart=StackBase
        ; init
        mov ax, cs
        mov ds, ax
        mov es, ax
        mov ss, ax

        ; video
        mov ax, 0xb800
        mov gs, ax
        ; display clear
        mov ax, 0x0600
        mov bx, 0x0700
        mov cx, 0x0000
        ; dl=80, dh=25
        mov dx, 0x184f
        int 0x10
        ; print msg
        mov byte , 'M'
        mov byte , 0x04
        mov byte , 'B'
        mov byte , 0x04
        mov byte , 'R'
        mov byte , 0x04

        ; load os
        mov eax, OS_START_SEC
        mov bx, OS_BASE_ADDR
        mov cx, OS_SEC_CNT
        call ReadSector
        jmp OS_BASE_ADDR

ReadSector:
        ; ax        Section to read from
        ; cl        Number of sectors to read
        ; bx        Destination

        ; 1.44MB = 2 * 80 * 18 * 512
        ; (One head for each side)
        ; (Side * TracksPerSide * SectorsPerTrack * BytesPerSector)
        ; SecToRead / SecPerTrk -> uotient, emainder
        ; CyldNum = Q >> 1, HeadNum = Q & 1, StartSec = R + 1
        push bp
        mov bp, sp
        sub esp, 2
        mov byte , cl
        push bx
        mov bl, BPB_SecPerTrk
        div bl
        inc ah
        mov cl, ah
        shr al, 1

        mov ch, al
        and dh, 1
        pop bx
        mov dl, BS_DrvNum
        .GoOnReading:
                ; int 0x13, ah=2 for reading to buffer
                ; al        number of sectors to read
                ; ch        cylinder number
                ; cl        first section number
                ; dh        header number
                ; dl        drive number (0 for drive A)
                ; es:bx        buffer
                mov ah, 2
                mov al, byte
                int 0x13
                jc .GoOnReading
                add esp, 2
                pop bp
                ret

times 510 - ($ - $$) db 0x0
dw 0xaa55这两个文件前面先清屏输出红色的MBR文字,后读入os。若os.asm载入成功,则前两个字符应被绿色OS字覆盖。
创建include目录(文件夹),并写入
include/boot.inc
OS_BASE_ADDR equ 0x900; os将被载入的内存地址
OS_START_SEC equ 2      ; os被存储的起始扇区
OS_SEC_CNT equ 1      ; os存储占用扇区数

BPB_SecPerTrk equ 18    ; 存储设备单磁道扇区数
BS_DrvNum equ 0         ; 存储设备号存储一些mbr和os共用公用的数据,方便修改。后两行仅mbr_chs.asm使用。
bochsrc_floppy.bxrc
romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest.bin
keyboard: keymap=/usr/local/share/bochs/keymaps/sdl2-pc-us.map

floppya: 1_44="os_chs.img", status=inserted
boot: floppy
log: /dev/null
mouse: enabled=0
megs: 32
display_library: sdl2bochsrc_disk.bxrc
romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest.bin
keyboard: keymap=/usr/local/share/bochs/keymaps/sdl2-pc-us.map

ata0-master: type=disk, path="os_lba.img", mode=flat
boot: disk
log: /dev/null
mouse: enabled=0
megs: 32
display_library: sdl2bochs的启动配置文件。
准备

制作软盘chs版本。
nasm -I include/ -f bin mbr_chs.asm -o mbr_chs.bin
nasm -I include/ -f bin os.asm -o os.bin
bximage -func=create -fd=1.44M -q os_chs.img
dd if=mbr_chs.bin of=os_chs.img bs=512 count=1 conv=notrunc
dd if=os.bin of=os_chs.img bs=512 count=1 conv=notrunc seek=2制作硬盘lba版本。
nasm -I include/ -f bin mbr_lba.asm -o mbr_lba.bin
nasm -I include/ -f bin os.asm -o os.bin
bximage -func=create -hd=10M -q os_lba.img
dd if=mbr_lba.bin of=os_lba.img bs=512 count=1 conv=notrunc
dd if=os.bin of=os_lba.img bs=512 count=1 conv=notrunc seek=2实验

先试试软盘
bochs -f bochsrc_floppy.bxrc然后硬盘
bochs -f bochsrc_disk.bxrc现象

不出意外的话,两次都应该出现OSR字样,原因在代码区有解释。
有兴趣可以将不同的mbr与不同的虚拟存储设备相结合。读取方式不匹配预期现象为显示MBR字样,说明OS加载失败。
有错误望指正。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 操作系统开发:文件读取寻址问题、解决方案及实验