我在测试我自己写的操作系统boot时遇到了个很奇怪的问题:
我的boot中有一个功能,将内核文件SYS读取到内存中。
因为是用FAT16,所以一个簇一个簇地读取(我把簇大小设成了512字节)
在中间我设计了个测试代码,每成功读取一个簇,就输出对应的簇号:
;输出测试信号
add al,48
mov byte gs:[si],al
mov byte gs:[si+1],0xc
sub al,48
add si,2
(gs的值是0xb800,al存储的是簇号,si是视频内存偏移地址,起始为0)
我用了qemu和bochs两个测试了同样的代码,结果:
bochs可以成功读取,然而qemu却莫名其妙地在读完第一个簇之后代码跑飞,然而我的boot代码并没有能让代码跑飞的部分。
(跑飞的代码。我这里根本没写任何东西,也没有任何跳转指令指向这个地方!)
更奇怪的是,如果我在qemu使用si命令一步步执行,它就不会跑飞,能够正确读取我的簇?
这究竟是什么问题?
附上关键代码:
.found:
;获取首簇号
sub di,8
add di,0x1a;偏移到首簇号
mov word ax,[di]
mov bx,0
mov si,0
.read_f:
mov cx,1
push si
push ax
add ax,CLUSTER_SEC_BALANCE
call read_disk
pop ax
pop si
;输出测试信号
add al,48
mov byte gs:[si],al
mov byte gs:[si+1],0xc
sub al,48
add si,2
;获取下一个簇号
mov di,0x8000
shl ax,1
add di,ax
mov ax,[di]
cmp ax,0xfff8
jb .read_f
go_to_kernel:
jmp 0:0
read_disk:
push ebp
mov ebp,esp
mov dx,0x1f7
;.not_ready1:
;nop ;nop相当于稍息 hlt相当于睡觉
;in al,dx ;读0x1f7端口
;and al,0xc0 ;第7位为1表示硬盘忙,第6位为1表示硬盘控制器已准备好,正在等待指令。
;cmp al,0x40 ;当第7位为0,且第6位为1,则进入下一个步。
;jne .not_ready1 ;若未准备好,则继续判断。
;xor ecx,ecx
;mov eax,[esp+8];lba
;mov ecx,[esp+12];counts(actually only data in cl are valid)
;mov ebx,[esp+16];mem_addr
mov esi,eax;start LBA
mov dx,0x1f2;设置读取的扇区数
mov al,cl
out dx,al
mov dx,0x1f3
mov eax,esi
out dx,al;0-7bit(LBA)
inc dx
shr eax,8
out dx,al;8-15bit
inc dx
shr eax,8
out dx,al;16-23bit
inc dx
shr ax,8
and al,0x0f
or al,0xe0
out dx,al;24-27bit and set some disk args
inc dx
mov al,0x20
out dx,al;read disk
.wait:
nop
in al,dx
;check errors
push ebx
mov bl,al
and bl,0x01
cmp bl,1
je .err_disk_reading
and al,0x88
cmp al,0x08
jne .wait
;0x88=0b10001000
;0x51=0b01010001
;pop ebx
mov di,dx
shl ecx,8;每次读取2byte,所以要读取512/2*cl次
mov dx,0x1f0
.read:
in ax,dx
mov word [ebx],ax
add ebx,2
loop .read
mov esp,ebp
pop ebp
mov eax,0
ret
.err_disk_reading:
mov dx,0x1f1
xor eax,eax
in ax,dx;ax=0xffff
;pop ebx
mov si,diskerror
mov di,0
call print_g
mov esp,ebp
pop ebp
ret