我编写了简单的第一阶段bootloader,它使用对bios的中断显示“Hello world”。现在作为编写第二阶段的下一个明显步骤,但是应该存在代码以及如何从第一阶段加载它?
这是第一阶段的程序
[BITS 16] ;Tells the assembler that its a 16 bit code
[ORG 0x7C00] ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded
MOV SI, HelloString ;Store string pointer to SI
CALL PrintString ;Call print string procedure
JMP $ ;Infinite loop, hang it here.
PrintCharacter: ;Procedure to print character on screen
;Assume that ASCII value is in register AL
MOV AH, 0x0E ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00 ;Page no.
MOV BL, 0x07 ;Text attribute 0x07 is lightgrey font on black background
INT 0x10 ;Call video interrupt
RET ;Return to calling procedure
PrintString: ;Procedure to print string on screen
;Assume that string starting pointer is in register SI
next_character: ;Lable to fetch next character from string
MOV AL, [SI] ;Get a byte from string and store in AL register
INC SI ;Increment SI pointer
OR AL, AL ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character ;Fetch next character from string
exit_function: ;End label
RET ;Return from procedure
;Data
HelloString db 'Hello World', 0 ;HelloWorld string ending with 0
TIMES 510 - ($ - $$) db 0 ;Fill the rest of sector with 0
DW 0xAA55 ;Add boot signature at the end of bootloader
答案 0 :(得分:6)
在x86上,您将执行以下操作(简化):
retf
)。更好的选择是在文件系统中搜索某个文件名(例如KERNEL.BIN) - 但是您需要知道文件系统类型(例如,如果您正在从软盘映像进行测试,则为FAT12)。EXTERN _mykernel
(例如)并调用该符号。mykernel
。好的,这是我几年前做过的简短概述(有很多来自互联网的复制和粘贴;)。如果这没有帮助,这里有一些关于OS开发的好的Web资源:
希望有所帮助^^
答案 1 :(得分:3)
加载第2阶段并跳转到第2阶段的最小可运行NASM BIOS示例
use16
org 0x7C00
; You should do further initializations here
; like setup the stack and segment registers.
; Load stage 2 to memory.
mov ah, 0x02
; Number of sectors to read.
mov al, 1
; This may not be necessary as many BIOS set it up as an initial state.
mov dl, 0x80
; Cylinder number.
mov ch, 0
; Head number.
mov dh, 0
; Starting sector number. 2 because 1 was already loaded.
mov cl, 2
; Where to load to.
mov bx, stage2
int 0x13
jmp stage2
; Magic bytes.
times ((0x200 - 2) - ($ - $$)) db 0x00
dw 0xAA55
stage2:
; Print 'a'.
mov ax, 0x0E61
int 0x10
cli
hlt
; Pad image to multiple of 512 bytes.
times ((0x400) - ($ - $$)) db 0x00
编译并运行:
nasm -f bin -o main.img main.asm
qemu-system-i386 main.img
预期结果:a
被打印到屏幕上,然后程序停止。
在Ubuntu 14.04上测试。
Saner GAS示例使用链接描述文件和更正确的初始化(段寄存器,堆栈)on my GitHub。
答案 2 :(得分:1)
在这里查看GRUB实现(第1阶段):
http://src.illumos.org/source/xref/illumos-gate/usr/src/grub/grub-0.97/stage1/stage1.S
首先注意到第一个扇区的起始点为0x7c00,结束签名为0xaa55。从反汇编中,你可以看到:
349 copy_buffer: 350 movw ABS(stage2_segment), %es 351 352 /* 353 * We need to save %cx and %si because the startup code in 354 * stage2 uses them without initializing them. 355 */ 356 pusha 357 pushw %ds 358 359 movw $0x100, %cx 360 movw %bx, %ds 361 xorw %si, %si 362 xorw %di, %di 363 364 cld 365 366 rep 367 movsw 368 369 popw %ds 370 popa 371 372 /* boot stage2 */ 373 jmp *(stage2_address) 374 375 /* END OF MAIN LOOP */ 376
本质上,逻辑是将第2阶段代码复制到内存的另一部分,然后直接跳转到那里,那就是“引导阶段2”。换句话说,“boot stage1”在将扇区加载到内存后从BIOS有效触发,而stage2则是你跳到那里的地方 - 它可以在任何地方。