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

问题

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

环境

  • 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 [bx], 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 -> [q]uotient, [r]emainder
	; CyldNum = Q >> 1, HeadNum = Q & 1, StartSec = R + 1
	push bp
	mov bp, sp
	sub esp, 2
	mov byte [bp-2], 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 [bp-2]
		int 0x13
		jc .GoOnReading
		add esp, 2
		pop bp
		ret

问题原因

我的问题出在代码与存储设备格式不匹配。

简称 全称 中文 适用设备 读取方式(不多赘述,具体请查找文档教程)
LBA Logical Block Addressing 逻辑块 寻址 多用于硬盘 通过向设备写入地址命令,并检查硬盘可用性,即可读取
CHS Cylinder Head Sector 柱面-读头-扇区 寻址 多用于软盘 通过柱面读头扇区的三维坐标,调用系统中断,即可读取

通过bochsrc中的floppyafloppyb指定软盘,ata[0-3][-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 虚拟盘写入,大多类unixlinuxmac)自带

系统建议使用LinuxMac,对于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 [gs:0x00], 'O'
	mov byte [gs:0x01], 0x02
	mov byte [gs:0x02], 'S'
	mov byte [gs:0x03], 0x02

	jmp $

载入后会打印绿色OS字样。

mbr_lba.asm

%include "boot.inc"
StackBase equ 0x7c00
; load loader.asm into memory
section 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_ADDR

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 [bx], ax
		add bx, 2
		loop .read_loop
	ret

times 510 - ($ - $$) db 0x0
dw 0xaa55

mbr_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 [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

	; 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 -> [q]uotient, [r]emainder
	; CyldNum = Q >> 1, HeadNum = Q & 1, StartSec = R + 1
	push bp
	mov bp, sp
	sub esp, 2
	mov byte [bp-2], 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 [bp-2]
		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: sdl2

bochsrc_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: sdl2

bochs的启动配置文件。

准备

制作软盘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加载失败。

有错误望指正。

posted @ 2025-06-29 15:29  SueXY  阅读(30)  评论(0)    收藏  举报