[英]Boot loader always messes up the first two bytes of Int 13h call to load kernel
Here's my boot loader code, and here's a document showing pictures of the registers (in case that's of importance in what I'm doing wrong), what was in memory at location 0x10000 (where I told the boot loader to load the kernel), the source assembly of my kernel, and the screen output when Qemu is run. 这是我的引导加载程序代码, 这是一个文档,显示了寄存器的图片(以防我做错事情很重要),位置0x10000的内存中的内容(我告诉引导加载程序加载内核),我的内核的源程序集,以及运行Qemu时的屏幕输出。
kernelStub.bin has EB 1B (the right jump command) at the very beginning. kernelStub.bin的开头是EB 1B(右跳命令)。 hda.img has EB 1B right after 55 AA at the beginning of the second sector. hda.img在第二扇区的开头55 AA之后具有EB 1B。 The carry flag is clear in my load_mem subroutine indicating that it believes the load was good. 我的load_mem子例程中的进位标志很清楚,表明它认为负载很好。 All the bytes are right in memory except the first two are always 63 61. 除了前两个始终为63 61以外,所有字节均位于内存中。
Why would the load_mem routine always load the first two bytes of sector 2 into address 0x10000 wrong and then get the rest right? 为什么load_mem例程总是总是错误地将扇区2的前两个字节加载到地址0x10000中,然后正确处理其余部分?
The bootloader code: 引导程序代码:
Update: Changed jmp SYSADDR:0000
to jmp 0x1000:0x0000
per Matthew Slattery's correction. 更新:根据Matthew Slattery的更正,将jmp SYSADDR:0000
更改为jmp 0x1000:0x0000
。
;Very minimal boot loader
BITS 16 ;Tell assembler to use 16-bit mode
jmp start ;Jump over defines
SYSADDR dw 0x1000 ;Load system at 0x10000
DRIVENUM db 0x80 ;Variable for drive number
HEADNUM db 0
CYLNUM db 0 ;Low bits of cylinder number
SECTNUM db 2 ;Bits 6 and 7 high bits of cylinder number (0),
;Bits 0-5 starting sector number (2)
NUMKERNELSECTS db 0x01 ;Will Probably Change! Number of sectors
;to read from disk
load_msg db 'Loading OS', 0
msg_2 db 'carry flag not clear', 0
load_worked db 'Load worked', 0
start:
mov ax, 0x07C0 ;Set data segment to where BIOS loaded boot loader
mov ds, ax
mov si, load_msg ;Simple text string to indicate loading
call show_message
call load_mem ;Subroutine to load bytes from disk to location
;pointed to by es
jmp 0x1000:0x0000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Subroutines
;;;Show Message;;;
show_message:
mov ah, 0x0E ;int 0x10 print character to screen function
.repeat:
lodsb ;Get char pointed to by si, puts in al
cmp al, 0 ;see if char is 0 (null)
je .done ;null signifies done
int 0x10 ;If not null, print to screen
jmp .repeat ;Get next char
.done:
ret
;;;Load Memory;;;
load_mem:
xor ah, ah ;ah=0, reset drive
int 0x13 ;Call drive reset
mov ax, [SYSADDR]
mov es, ax ;Destination- es:bx
mov bx, 0
mov dl, [DRIVENUM]
mov dh, [HEADNUM]
mov al, [NUMKERNELSECTS]
mov ch, [CYLNUM]
mov cl, [SECTNUM]
mov ah, 0x02 ;ah=2, read drive
int 0x13 ;Call read interrupt
jnc exit ;If carry flag is clear, exit
exit:
ret
times 510 - ($-$$) db 0;Pad sector with 0
dw 0xAA55 ;Boot signature
SYSADDR dw 0x1000 ;Load system at 0x10000 ... jmp SYSADDR:0000
doesn't do what you want it to. 不执行您想要的操作。 SYSADDR
is the location of the value you want ( 0x0002
here), not the value itself. SYSADDR
是所需值的位置(此处为0x0002
),而不是值本身。
I reckon that executing whatever happens to be at 0x0002:0000
(which won't even be real code; it's part of the interrupt vector table) is causing your data to be scribbled over. 我认为执行在0x0002:0000
处发生的任何事情(甚至都不是真正的代码;它是中断向量表的一部分)都会导致您的数据被乱写。
(By the way, there is at least one more problem as well: the kernel stub code in your second linked doc isn't resetting ds
.) (顺便说一句,至少还有另外一个问题:第二个链接文档中的内核存根代码未重置ds
。)
EDIT: 编辑:
I see that you've got this working now, but for completeness, I have a full explanation for the mysterious values in those two bytes, as a result of the jmp SYSADDR:0000
bug: 我看到您现在可以正常工作了,但是为了完整jmp SYSADDR:0000
,由于jmp SYSADDR:0000
错误,我对这两个字节中的神秘值有完整的解释:
0002:0000
, ie address 0x20
, is the vector for INT 08h
. 0002:0000
(即地址0x20
)是INT 08h
的向量。
The traditional entry point address for the INT 08h
handler is f000:fea5
(BIOSes tend to maintain the tradition, for compatibility with code that uses the entry points directly). INT 08h
处理程序的传统入口点地址为f000:fea5
(BIOS倾向于保留该传统,以便与直接使用入口点的代码兼容)。 So the bytes at this address will almost certainly be a5 fe 00 f0 ...
. 因此,该地址处的字节几乎可以肯定是a5 fe 00 f0 ...
If you execute the first of those bytes, a5
, as code, it's a movsw
instruction, which copies two bytes from ds:si
to es:di
. 如果将其中的第一个字节a5
作为代码执行,则它是一条movsw
指令,它将ds:si
两个字节复制到es:di
。
ds
is still your boot loader's data segment ds
仍然是引导加载程序的数据段 si
points to the beginning of msg_2
(where your show_message
routine left it after printing load_msg
) si
指向msg_2
的开头(您的show_message
例程在打印load_msg
之后将show_message
离开) es
is still 0x1000
es
仍为0x1000
di
presumably contains 0 di
可能包含0 So the very first thing that happens after the jmp 0x0002:0000
is that the c
and a
from the start of msg_2
get copied to the first two bytes of the sector which was loaded. 因此,在jmp 0x0002:0000
之后发生的第一件事是,将c
和a
从msg_2
的开头复制到已加载扇区的前两个字节。
I found the critical detail regarding my question of getting the first jump of my kernel here . 我找到了有关在这里获得内核的第一跳的问题的关键细节。 I didn't know how necessary it was (with some BIOSes, at least) to follow short jmp
with nop
. 我不知道用nop
跟随简短的jmp
有多必要(至少使用某些BIOS)。
Once I put the nop
s in and set up my ds
register, it finally worked. 一旦将nop
放入并设置了ds
寄存器,它终于可以工作了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.