简体   繁体   English

程序集引导程序不会跳转到内核

[英]assembly bootloader won't jump to kernel

I have made a basic bootloader in assembly, but it doesn't actually jump to the kernel. 我已经在程序集中创建了一个基本的bootloader,但它实际上并没有跳转到内核。 It just says "Booting...". 它只是说“引导......”。 I'm sure it's just some silly mistake I made, like jumping to the wrong place. 我确定这只是我犯的一些愚蠢的错误,比如跳到错误的地方。 It should show an output like "Booting... Loaded!". 它应该显示像“Booting ... Loaded!”这样的输出。 I've also tried setting es to 0 before loading it, but even that doesn't work. 我也尝试在加载之前将es设置为0,但即使这样也行不通。 Here's my code: 这是我的代码:

mov ax, 9ch
mov ss, ax
mov sp, 4096d
mov ax, 7c0h
mov ds, ax
mov es, ax

xor ah, ah
int 13h

clc

mov si, msg2
call print

mov ah, 02h
xor ax, ax
mov es, ax
mov bx, 0x7E00
mov al, 1h
mov ch, 0
mov cl, 2h
mov dh, 0
int 13h

jc error

jmp 0x7E00

mov si, msg3
call print

error:
mov si, msg
call print
hlt

print:
lodsb
cmp al, 0
jz done
mov ah, 0eh
int 10h
jmp print
done:
ret

msg db "An error occured!!", 0
msg2 db "Booting...", 0
msg3 db "Did not jump to kernel correctly!"

times 510-($-$$) db 0
dw 0xAA55

mov si, msgloaded
call printl
jmp $

printl:
lodsb
cmp al, 0
jz donel
mov ah, 0eh
int 10h
jmp print
donel:
ret

msgloaded db "Loaded!", 0

times 0x400-($-$$) db 0

All help is appreciated. 所有帮助表示赞赏。 I will credit anyone who can help me. 我会相信任何可以帮助我的人。 Thanks! 谢谢!

For high level languages there's lots of clues about what the programmer intended contained in the structure loops, how variable names were chosen, defines/enums, etc; 对于高级语言,有很多线索关于程序员打算包含在结构循环中的内容,如何选择变量名,定义/枚举等等; and it's easy to write maintainable code without comments. 并且很容易编写没有注释的可维护代码。

For assembly language there's no well chosen variable names and no variable types (eg ax doesn't tell the reader if it's a pointer to a string or a giraffe's height or ...), instructions often don't show the intent (eg lea might be used to multiply by a constant and might not be used to load an effective address), control flow is far more flexible (eg something like a do(condition1) { } while(condition2) is perfectly fine) and goto (both jmp and conditional branches like jc ) are used a lot. 对于汇编语言,没有精心选择的变量名称和没有变量类型(例如, ax不告诉读者它是指向字符串或长颈鹿的高度的指针还是......),指令通常不显示意图(例如lea可能用于乘以一个常量而不能用于加载有效地址),控制流更灵活(例如do(condition1) { } while(condition2)完全正常)和goto (两者都是jmpjc条件分支经常被使用。

For this reason, well written/maintainable assembly language uses lots of comments. 出于这个原因,编写良好/可维护的汇编语言使用了大量注释。 More specifically, you'd use comments on the right hand side to describe your intentions. 更具体地说,您可以在右侧使用评论来描述您的意图。 This allows you to check if the intentions are correct by reading comments, and then check if the intention is implemented correctly by comparing the instruction on each line with it's comment. 这允许您通过阅读注释来检查意图是否正确,然后通过将每行上的指令与其注释进行比较来检查意图是否正确实现。 It makes it much easier to avoid bugs, and much easier to find bugs. 它使得更容易避免错误,并且更容易发现错误。

Here's the first half of your code with comments: 以下是带注释的代码的前半部分:

;Memory Layout
;
; 0x009C:0x1000 = 0x000019C0 = stack top
; 0x07C0:0x0000 = 0x00007C00 = load address
; 0x0000:0x7E00 = 0x00007E00 = kernel address

%define STACK_SEGMENT      0x009C
%define STACK_TOP_OFFSET   0x1000
%define LOAD_SEGMENT       0x07C0
%define KERNEL_SEGMENT     0x0000
%define KERNEL_OFFSET      0x7E00

;_______________________________________________

;Entry point
;
;Input
; dl = BIOS boot device number

    mov ax, STACK_SEGMENT
    mov ss, ax
    mov sp, STACK_TOP_OFFSET
    mov ax, LOAD_SEGMENT
    mov ds, ax
    mov es, ax

;Reset disk system
;
;Note: This should be completely unnecessary. We know the BIOS
;      disk services are working correctly and don't need
;      to be reset because the BIOS just used it successfully
;      to load this code into memory.

    xor ah, ah            ;ah = BIOS "reset disk system" function number
    int 13h               ;Call BIOS disk services
    clc                   ;Unnecessary

;Display welcome message

    mov si, msg2
    call print

;Load kernel from disk
; dl = BIOS boot device number

    mov ah, 02h           ;ah = BIOS "read sectors" function number
    xor ax, ax            ;ax = KERNEL_SEGMENT
    mov es, ax
    mov bx, KERNEL_OFFSET ;es:bx = address to load kernel
    mov al, 1h            ;al = number of sectors to read
    mov ch, 0             ;ch = cylinder number for first sector
    mov cl, 2h            ;cl = sector number for first sector
    mov dh, 0             ;dh = head number for first sector
    int 13h               ;Call BIOS disk services

    jc error              ;Handle error if there was one

;Pass control to "kernel"

    jmp KERNEL_SEGMENT:KERNEL_OFFSET

Here's the part that makes your bug obvious: 这是使你的bug显而易见的部分:

                          ;ah = BIOS "read sectors" function number
                          ;ax = KERNEL_SEGMENT

Essentially, if you commented your code properly, you would've noticed that loading KERNEL_SEGMENT into ax overwrites the BIOS function number (which was in the highest 8 bits of ax ). 基本上,如果您正确地评论了您的代码,您会注意到将KERNEL_SEGMENT加载到ax覆盖BIOS功能编号(位于ax的最高8位)。 This causes this piece of code to call the BIOS "reset disk system" function and not load anything from disk at all. 这导致这段代码调用BIOS“重置磁盘系统”功能,而根本不从磁盘加载任何东西。 When it jumps to where the kernel should've been loaded (but wasn't) later, that memory is probably still full of zeros because it hasn't been used, but memory full of zeros are decoded as add instructions by the CPU, so the CPU happily executes the add instructions for ages. 当它跳转到内核应该被加载(但之后没有)的地方时,该内存可能仍然充满零,因为它还没有被使用,但是内存满了零被CPU解码为add指令,所以CPU很乐意执行多年的add指令。

Note: There is another (unrelated) bug - your code to print strings uses lodsb which depends on the direction flag; 注意:还有另一个(无关的)错误 - 打印字符串的代码使用lodsb ,这取决于方向标志; but you don't do a cld instruction to set the direction flag, so depending on the (undefined) state the BIOS left this flag in, it could print garbage instead. 但你不做一个cld指令来设置方向标志,所以根据(未定义)状态BIOS保留此标志,它可能会打印垃圾。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM