找回密码
 立即注册
首页 业界区 科技 操作系统开发:文件读取寻址问题、解决方案及实验 ...

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

喙审 2025-6-29 17:52:44
问题

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


  • bochs 虚拟机
  • nasm 汇编
现象

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

这是LBA读文件的代码。
修改自从零开发操作系统
  1. load_section:
  2.         ; eax        Section to read from
  3.         ; bx        Memory address to write to
  4.         ; cx        Number of sections to read
  5.         ; backup eax & cx
  6.         mov esi, eax
  7.         mov di, cx
  8.         ; write arguments
  9.         ; Number of sections
  10.                 mov dx, 0x1f2
  11.                 mov al, cl
  12.                 out dx, al
  13.                 mov eax, esi
  14.         ; Section (LBA) addr
  15.         ; 7~0
  16.                 mov dx, 0x1f3
  17.                 out dx, al
  18.         ; 15~8
  19.                 mov cl, 8
  20.                 shr eax, cl
  21.                 mov dx, 0x1f4
  22.                 out dx, al
  23.         ; 23~16
  24.                 shr eax, cl
  25.                 mov dx, 0x1f5
  26.                 out dx, al
  27.         ; 27~24
  28.                 shr eax, cl
  29.                 and al, 0x0f
  30.                 ; 7~4==1110 => LBA mode
  31.                 or al, 0xe0
  32.                 mov dx, 0x1f6
  33.                 out dx, al
  34.         ; write command
  35.                 mov dx, 0x1f7
  36.                 mov al, 0x20
  37.                 out dx, al
  38.         ; wait until disk ready
  39.         .wait_disk:
  40.                 nop
  41.                 in al, dx
  42.                 ; 4th digit==1 => ready
  43.                 ; 7th digit==1 => busy
  44.                 ; if (not (4th==1 and 7th!=1)) then wait
  45.                 and al, 0x88
  46.                 cmp al, 0x08
  47.                 jnz .wait_disk
  48.         ; calc read times
  49.                 ; di = cx = sections to read
  50.                 mov ax, di
  51.                 ; 1 word (2 bytes) per time per section, one section 512 bytes
  52.                 ; total 256 times per section
  53.                 mov dx, 256
  54.                 ; sections to read * time to read per section
  55.                 ; mul dx => prod=dx*ax, dx=prod.high16, ax=prod.low16
  56.                 mul dx
  57.                 ; high16 ignored, cx=low16 for loop times
  58.                 mov cx, ax
  59.                 mov dx, 0x1f0
  60.         .read_loop:
  61.                 in ax, dx
  62.                 mov [bx], ax
  63.                 add bx, 2
  64.                 loop .read_loop
  65.         ret
复制代码
这是CHS读文件的代码。
修改自《自己动手写操作系统》
  1. BPB_SecPerTrk equ 18
  2. BS_DrvNum equ 0
  3. ReadSector:
  4.         ; ax        Section to read from
  5.         ; cl        Number of sectors to read
  6.         ; bx        Destination
  7.         ; 1.44MB = 2 * 80 * 18 * 512
  8.         ; (One head for each side)
  9.         ; (Side * TracksPerSide * SectorsPerTrack * BytesPerSector)
  10.         ; SecToRead / SecPerTrk -> [q]uotient, [r]emainder
  11.         ; CyldNum = Q >> 1, HeadNum = Q & 1, StartSec = R + 1
  12.         push bp
  13.         mov bp, sp
  14.         sub esp, 2
  15.         mov byte [bp-2], cl
  16.         push bx
  17.         mov bl, BPB_SecPerTrk
  18.         div bl
  19.         inc ah
  20.         mov cl, ah
  21.         shr al, 1
  22.         mov ch, al
  23.         and dh, 1
  24.         pop bx
  25.         mov dl, BS_DrvNum
  26.         .GoOnReading:
  27.                 ; int 0x13, ah=2 for reading to buffer
  28.                 ; al        number of sectors to read
  29.                 ; ch        cylinder number
  30.                 ; cl        first section number
  31.                 ; dh        header number
  32.                 ; dl        drive number (0 for drive A)
  33.                 ; es:bx        buffer
  34.                 mov ah, 2
  35.                 mov al, byte [bp-2]
  36.                 int 0x13
  37.                 jc .GoOnReading
  38.                 add esp, 2
  39.                 pop bp
  40.                 ret
复制代码
问题原因

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

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

点击展开工具


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

模拟mbr将操作系统载入内存并执行的过程
os.asm
  1. %include "boot.inc"
  2. section MBR vstart=OS_BASE_ADDR
  3.         mov ax, 0xb800
  4.         mov gs, ax
  5.         ; print msg
  6.         mov byte [gs:0x00], 'O'
  7.         mov byte [gs:0x01], 0x02
  8.         mov byte [gs:0x02], 'S'
  9.         mov byte [gs:0x03], 0x02
  10.         jmp $
复制代码
载入后会打印绿色OS字样。
mbr_lba.asm
  1. %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 [gs:0x00], 'M'        mov byte [gs:0x01], 0x04        mov byte [gs:0x02], 'B'        mov byte [gs:0x03], 0x04        mov byte [gs:0x04], 'R'        mov byte [gs:0x05], 0x04        mov byte [gs:0x06], ' '        mov byte [gs:0x07], 0x04        ; load loader        mov eax, OS_START_SEC        mov bx, OS_BASE_ADDR        mov cx, 1        call load_section        jmp OS_BASE_ADDRload_section:
  2.         ; eax        Section to read from
  3.         ; bx        Memory address to write to
  4.         ; cx        Number of sections to read
  5.         ; backup eax & cx
  6.         mov esi, eax
  7.         mov di, cx
  8.         ; write arguments
  9.         ; Number of sections
  10.                 mov dx, 0x1f2
  11.                 mov al, cl
  12.                 out dx, al
  13.                 mov eax, esi
  14.         ; Section (LBA) addr
  15.         ; 7~0
  16.                 mov dx, 0x1f3
  17.                 out dx, al
  18.         ; 15~8
  19.                 mov cl, 8
  20.                 shr eax, cl
  21.                 mov dx, 0x1f4
  22.                 out dx, al
  23.         ; 23~16
  24.                 shr eax, cl
  25.                 mov dx, 0x1f5
  26.                 out dx, al
  27.         ; 27~24
  28.                 shr eax, cl
  29.                 and al, 0x0f
  30.                 ; 7~4==1110 => LBA mode
  31.                 or al, 0xe0
  32.                 mov dx, 0x1f6
  33.                 out dx, al
  34.         ; write command
  35.                 mov dx, 0x1f7
  36.                 mov al, 0x20
  37.                 out dx, al
  38.         ; wait until disk ready
  39.         .wait_disk:
  40.                 nop
  41.                 in al, dx
  42.                 ; 4th digit==1 => ready
  43.                 ; 7th digit==1 => busy
  44.                 ; if (not (4th==1 and 7th!=1)) then wait
  45.                 and al, 0x88
  46.                 cmp al, 0x08
  47.                 jnz .wait_disk
  48.         ; calc read times
  49.                 ; di = cx = sections to read
  50.                 mov ax, di
  51.                 ; 1 word (2 bytes) per time per section, one section 512 bytes
  52.                 ; total 256 times per section
  53.                 mov dx, 256
  54.                 ; sections to read * time to read per section
  55.                 ; mul dx => prod=dx*ax, dx=prod.high16, ax=prod.low16
  56.                 mul dx
  57.                 ; high16 ignored, cx=low16 for loop times
  58.                 mov cx, ax
  59.                 mov dx, 0x1f0
  60.         .read_loop:
  61.                 in ax, dx
  62.                 mov [bx], ax
  63.                 add bx, 2
  64.                 loop .read_loop
  65.         rettimes 510 - ($ - $$) db 0x0dw 0xaa55
复制代码
mbr_chs.asm
  1. %include "boot.inc"
  2. StackBase equ 0x7c00
  3. ; load os.asm into memory
  4. section MBR vstart=StackBase
  5.         ; init
  6.         mov ax, cs
  7.         mov ds, ax
  8.         mov es, ax
  9.         mov ss, ax
  10.         ; video
  11.         mov ax, 0xb800
  12.         mov gs, ax
  13.         ; display clear
  14.         mov ax, 0x0600
  15.         mov bx, 0x0700
  16.         mov cx, 0x0000
  17.         ; dl=80, dh=25
  18.         mov dx, 0x184f
  19.         int 0x10
  20.         ; print msg
  21.         mov byte [gs:0x00], 'M'
  22.         mov byte [gs:0x01], 0x04
  23.         mov byte [gs:0x02], 'B'
  24.         mov byte [gs:0x03], 0x04
  25.         mov byte [gs:0x04], 'R'
  26.         mov byte [gs:0x05], 0x04
  27.         ; load os
  28.         mov eax, OS_START_SEC
  29.         mov bx, OS_BASE_ADDR
  30.         mov cx, OS_SEC_CNT
  31.         call ReadSector
  32.         jmp OS_BASE_ADDR
  33. ReadSector:
  34.         ; ax        Section to read from
  35.         ; cl        Number of sectors to read
  36.         ; bx        Destination
  37.         ; 1.44MB = 2 * 80 * 18 * 512
  38.         ; (One head for each side)
  39.         ; (Side * TracksPerSide * SectorsPerTrack * BytesPerSector)
  40.         ; SecToRead / SecPerTrk -> [q]uotient, [r]emainder
  41.         ; CyldNum = Q >> 1, HeadNum = Q & 1, StartSec = R + 1
  42.         push bp
  43.         mov bp, sp
  44.         sub esp, 2
  45.         mov byte [bp-2], cl
  46.         push bx
  47.         mov bl, BPB_SecPerTrk
  48.         div bl
  49.         inc ah
  50.         mov cl, ah
  51.         shr al, 1
  52.         mov ch, al
  53.         and dh, 1
  54.         pop bx
  55.         mov dl, BS_DrvNum
  56.         .GoOnReading:
  57.                 ; int 0x13, ah=2 for reading to buffer
  58.                 ; al        number of sectors to read
  59.                 ; ch        cylinder number
  60.                 ; cl        first section number
  61.                 ; dh        header number
  62.                 ; dl        drive number (0 for drive A)
  63.                 ; es:bx        buffer
  64.                 mov ah, 2
  65.                 mov al, byte [bp-2]
  66.                 int 0x13
  67.                 jc .GoOnReading
  68.                 add esp, 2
  69.                 pop bp
  70.                 ret
  71. times 510 - ($ - $$) db 0x0
  72. dw 0xaa55
复制代码
这两个文件前面先清屏输出红色的MBR文字,后读入os。若os.asm载入成功,则前两个字符应被绿色OS字覆盖。
创建include目录(文件夹),并写入
include/boot.inc
  1. OS_BASE_ADDR equ 0x900  ; os将被载入的内存地址
  2. OS_START_SEC equ 2      ; os被存储的起始扇区
  3. OS_SEC_CNT equ 1        ; os存储占用扇区数
  4. BPB_SecPerTrk equ 18    ; 存储设备单磁道扇区数
  5. BS_DrvNum equ 0         ; 存储设备号
复制代码
存储一些mbr和os共用公用的数据,方便修改。后两行仅mbr_chs.asm使用。
bochsrc_floppy.bxrc
  1. romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
  2. vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest.bin
  3. keyboard: keymap=/usr/local/share/bochs/keymaps/sdl2-pc-us.map
  4. floppya: 1_44="os_chs.img", status=inserted
  5. boot: floppy
  6. log: /dev/null
  7. mouse: enabled=0
  8. megs: 32
  9. display_library: sdl2
复制代码
bochsrc_disk.bxrc
  1. romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
  2. vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest.bin
  3. keyboard: keymap=/usr/local/share/bochs/keymaps/sdl2-pc-us.map
  4. ata0-master: type=disk, path="os_lba.img", mode=flat
  5. boot: disk
  6. log: /dev/null
  7. mouse: enabled=0
  8. megs: 32
  9. display_library: sdl2
复制代码
bochs的启动配置文件。
准备

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

先试试软盘
  1. bochs -f bochsrc_floppy.bxrc
复制代码
然后硬盘
  1. bochs -f bochsrc_disk.bxrc
复制代码
现象

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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册