【问题标题】:assembly bootloader won't jump to kernel程序集引导加载程序不会跳转到内核
【发布时间】:2017-11-17 18:37:59
【问题描述】:

我已经在汇编中制作了一个基本的引导加载程序,但它实际上并没有跳转到内核。它只是说“正在启动......”。我敢肯定这只是我犯的一些愚蠢的错误,比如跳错了地方。它应该显示类似“正在启动...已加载!”的输出。我也尝试在加载之前将 es 设置为 0,但即使这样也不起作用。这是我的代码:

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

感谢所有帮助。我会感谢任何可以帮助我的人。谢谢!

【问题讨论】:

  • 可能是因为你连内核都没有跳转?
  • 糟糕。一定是在将其复制到框中时不小心删除了。对不起。
  • 另外,您的 int 13h 调用显然位于错误的位置(甚至在设置参数之前???)并且它正在将数据加载到错误的位置。
  • int 13h 实际上应该被调用两次。一次重置驱动器,一次加载它。我修复了它,即使现在它仍然无法正常工作。
  • 您将段设置为7c0h,因此加载和跳转到的偏移量当然是200h 而不是7e00h

标签: assembly x86 operating-system bootloader 16-bit


【解决方案1】:

对于高级语言,有很多关于程序员想要在结构循环中包含什么、如何选择变量名、定义/枚举等的线索;而且不用 cmets 也很容易编写可维护的代码。

对于汇编语言,没有精心选择的变量名称和变量类型(例如,ax 不会告诉读者它是指向字符串的指针还是长颈鹿的高度或...),指令通常不会显示意图(例如,lea 可能用于乘以一个常数,并且可能不用于加载有效地址),控制流更加灵活(例如,do(condition1) { } while(condition2) 之类的东西非常好)和gotojmpjc 等条件分支都被大量使用。

因此,编写良好/可维护的汇编语言使用了大量的 cmets。更具体地说,您将在右侧使用 cmets 来描述您的意图。这允许您通过阅读 cmets 检查意图是否正确,然后通过将每行的指令与其注释进行比较来检查意图是否正确实现。它使避免错误变得更加容易,并且更容易找到错误。

这是您的 cmets 代码的前半部分:

;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

这是使您的错误显而易见的部分:

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

基本上,如果您正确地注释了您的代码,您会注意到将KERNEL_SEGMENT 加载到ax 会覆盖BIOS 功能编号(位于ax 的最高8 位中)。这会导致这段代码调用 BIOS“重置磁盘系统”功能,并且根本不从磁盘加载任何内容。当它跳转到稍后应该加载(但未加载)内核的位置时,该内存可能仍然充满零,因为它尚未被使用,但充满零的内存被解码为add 指令CPU,所以 CPU 很高兴地执行 add 指令很长时间了。

注意:还有另一个(不相关的)错误 - 您打印字符串的代码使用 lodsb,这取决于方向标志;但是您没有执行cld 指令来设置方向标志,因此根据BIOS 留下此标志的(未定义)状态,它可能会打印垃圾。

【讨论】:

  • 0x07C0:0x0000 = 0x00007C00 = load address -> 不,取决于 BIOS。 0000:7C00 更为常见,但仍然足够强大的引导加载程序总是从远跳转开始,以将cs:ip“正常化”到预期状态。
  • 那么,我需要做什么?我尝试在 int 13h 调用之前直接设置 ah,但即使这样也不起作用。
  • @Ped7g:是的,也不是。此代码中的任何内容实际上都取决于CS(如果BIOS 改用0x0000:0x7C00,它应该仍然可以正常工作);并且该注释(和%define)记录了曾经/用于数据的意图(例如DS,它已加载并用于打印字符串)。不过,jmp LOAD_SEGMENT: start 会很好(并且可以避免未来的错误)。
  • @programmer:在int 0x13 应该工作之前立即将“读取扇区”功能编号加载到ah,但我认为您之前遇到过jmp KERNEL_SEGMENT:KERNEL_OFFSET 的问题 - 多个错误如果您只修复其中一个错误,类似的症状会让人看起来没有任何改变。
  • 我只需要一个引导加载程序来启动我的内核。有人可以告诉我在哪里可以找到一些有效的引导加载程序代码。
猜你喜欢
  • 2014-10-24
  • 2015-12-18
  • 2014-04-23
  • 2020-09-10
  • 2015-01-19
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 2019-10-24
相关资源
最近更新 更多