【发布时间】:2014-04-23 13:16:36
【问题描述】:
我正在编写一个简单地加载内核的引导加载程序。我一直在关注一个教程并稍微调整了它的汇编代码,但是教程上的地址不再有效,因此内核没有加载。我不知道寻址是如何工作的,以及我定义的附加字节和我添加的代码如何使教程中的地址无效。 我使用的教程在这里:http://inglorion.net/documents/tutorials/x86ostut/bootsector/
我应该怎么做才能确保地址有效? 根据教程,我们应该可以在 1000h 加载内核,但这似乎没有发生。
这是我的代码:
bootloader.asm 抱歉过多的 cmets
BITS 16 ; NASM directive for declaring the bit-mode.
ORG 0 ; Tells NASM that all locations we give
; will be based off of zero
bootloader_initialize_environment:
mov ax, 0x07C0 ; 07C0 = 1984, the location where BIOS looks for the
; bootloader on the floppy disk
mov ds, ax ; sets up data segment (ds)
mov es, ax ; sets up extra segment (es)
KERNEL_BLOCK_START equ 1 ; Starting disk block where kernel is written
KERNEL_BLOCK_SIZE equ 1 ; Number of blocks containing kernel
KERNEL_SEGMENT equ 1000h ; Kernel will be loaded at segment 4096
call load_kernel ; begin OS
load_kernel:
mov si, bootloader_status_message
call bootloader_print_string ; print status message
; begin reading kernel byte code from disk
; Uses interrupt 13h with AH = 2h
; Options: AL = sectors to read count
; CH = Cylinder to read
; CL = Sector within Cylinder to read
; DH = Head (0 in our case)
; DL = Drive (also 0)
; ES:BX = Buffer address pointer
mov ah, 2h
mov al, KERNEL_BLOCK_SIZE
push word KERNEL_SEGMENT
pop es
xor bx, bx ; reset bx to 0
mov ch, KERNEL_BLOCK_START
mov cl, 1h
mov dx, 0
int 13h ; call interrupt. Writes error to Carry flag
jnc jump_to_kernel ; loading success, no error in carry flag
mov si, bootloader_load_failed
call bootloader_print_string
jmp $ ; loop forever
jump_to_kernel :
mov si, bootloader_load_success
call bootloader_print_string
mov bl, 2 ; for debugging (I later look in gdb)
jmp KERNEL_SEGMENT:0
bootloader_status_message db 'bootloader: loading kernel...', 0x0D, 0x0A, 0
bootloader_load_failed db 'bootloader fatal: loading kernel failed. Go home.', 0x0D, 0x0A, 0
bootloader_load_success db 'bootloader: reading kernel success, jumping now...', 0x0D, 0x0A, 0
bootloader_print_string:
lodsb ; Takes one byte from SI and puts it to AL (maintains pointer to next byte)
or al, al ; All strings end with zero to indicate that the string has finished
; we use that to know when to stop printing characters from SI
; takes logical OR of AL by itself. Result is store in Carry Flag
jz .finish ; checks if the carry flag is zero and if so jumps to finish subroutine
; continue printing below
mov ah, 0x0E ; BIOS directive for Teletype printing
int 10h ; BIOS interrupt for video services. (AH=Teletype & AL=character)
jmp bootloader_print_string ; recursive call until all characters are printed
.finish: ; finished printing all characters
ret ; return to where this routine was called from
times 510 - ($ - $$) db 0 ; loop 510 times and pad with empty bytes
dw 0xAA55 ; last 2 bytes are 55h and 0AAh
kernel.asm - 相关部分 os_initialize_environment:
STACK_SEGMENT equ 09000h ; top of Conventional memory, 36864
STACK_SIZE equ 0ffffh ; stack length: 64K-1 bytes
SCREEN_SEGMENT equ 0b800h ; segment of memory where BIOS writes display data
SCREEN_SIZE_COLUMNS equ 80 ; 80 width
SCREEN_SIZE_ROWS equ 25 ; 25 height
mov bl, 1 ; debugging with gdb
mov sp, STACK_SEGMENT
mov ss, sp
mov sp, STACK_SIZE
push cs
pop ds
push word SCREEN_SEGMENT
pop es
mov al, fh
mov si, os_kernel_read_signal
call os_print_string
call os_start ; begin OS
; ----------------------------------------------------------
; Start of main program
; Available routines: os_print_string, os_get_user_input, os_compare_string
; ----------------------------------------------------------
os_start:
mov si, os_welcome_message ; move welcome message to input
call os_print_string
mov si, os_alive_signal ; move welcome message to input
call os_print_string
jmp shell_begin
; ---------------
; data
; ---------------
shell_cursor db '> ', 0
shell_command_help db 'help', 0
shell_error_wrong_command db 'Wrong input. Type help for help.', 0x0D, 0x0A, 0
os_welcome_message db 'SsOS is a Simple Operating System. Keep expectations low. The pessimist is never disappointed.', 0x0D, 0x0A, 0
os_alive_signal db 'Command prompt ready', 0x0D, 0x0A, 0
os_kernel_read_signal db 'Kernel reached from bootloader', 0x0D, 0x0A, 0
os_action_help db 'available commands: help', 0x0D, 0x0A, 0
os_waiting_for_input db 'Please provide input below', 0x0D, 0x0A, 0
buffer times 128 db 0
; ----------------------------------------------------------
; Routine: Begins shell
; ----------------------------------------------------------
shell_begin:
mov si, shell_cursor ; print > cursor
call os_print_string
mov di, buffer ; move buffer to destination output
call os_get_user_input ; wait for user input
mov si, buffer ; copy user input to SI
mov di, shell_command_help
call os_compare_string ; checks if user typed help command
jc .command_help
; command help (shell_command_help) selected
.command_help:
mov si, os_action_help
call os_print_string
jmp shell_begin ; reset shell
; wrong user input (command not recognized)
.wrong_input_error:
mov si, shell_error_wrong_command
call os_print_string
jmp shell_begin
; ----------------------------------------------------------
; Routine: Print String in SI
; Input 1. SI: string to be printed must be copied to SI
; ----------------------------------------------------------
os_print_string:
lodsb ; Takes one byte from SI and puts it to AL (maintains pointer to next byte)
; All strings end with zero to indicate that the string has finished
; we use that to know when to stop printing characters from SI
or al, al ; takes logical OR of AL by itself. Result is store in Carry Flag
jz .finish ; checks if the carry flag is zero and if so jumps to finish subroutine
; continue printing below
mov ah, 0x0E ; BIOS directive for Teletype printing
int 10h ; BIOS interrupt for video services. (AH=Teletype & AL=character)
jmp os_print_string ; recursive call until all characters are printed
.finish: ; finished printing all characters
ret ; return to where this routine was called from
; ----------------------------------------------------------
; Routine: Get String from User
; Waits for a complete string of user input and puts it in buffer.
; Sensitive for backspace and Enter buttons
; Input 1. Buffer in DI
; Output 2. Input char in buffer
; ----------------------------------------------------------
os_get_user_input:
xor cl, cl ; CL will be our counter that keeps track of the number of characters the user has entered.
; XORing cl by itself will set it to zero.
mov si, os_waiting_for_input
call os_print_string
.get_char_and_add_to_buffer:
mov ah, 0 ; We use bios interrupt 16h to capture user input.
; AH=0 is an option for 16h that tells the interrupt to read the user input character
int 16h ; call interrupt. Stores read character in AL
; backspace button listener
cmp al, 0x08 ; compares user input to the backspace button, stores result in Carry Flag
je .backspace_pressed ; if the results of the compare is 1, go to subroutine .backspace_pressed
; enter button listener
cmp al, 0x0D ; compares user input to enter button
je .enter_pressed ; go to appropriate subroutine for enter button
; input counter
cmp cl, 0x80 ; Has the user entered 128 bytes yet? (buffer limit is 128)
je .buffer_overflow
; User input is normal character
; print input
mov ah, 0x0E ; Teletype mode
int 10h ; Print interrupt
stosb ; puts character in buffer
inc cl ; increment counter
jmp .get_char_and_add_to_buffer ; recurse
; // Subroutines
.backspace_pressed:
cmp cl, 0 ; no point erasing anything if no input has been entered
je .get_char_and_add_to_buffer ; ignore backspace press
; Delete last input character from buffer
; When you use stosb, movsb or similar functions, the system implicitly uses the SI and DI registers.
dec di ; Therefore we need to decrement di to get to the last input character and erase it.
mov byte[di],0 ; Erases the byte at location [di]
dec cl ; decrement our counter
; Erase character from display
mov ah, 0x0E ; Teletype mode again
mov al, 0x08 ; Backspace character
int 10h
mov al, ' ' ; Empty character to print
int 10h
mov al, 0x08
int 10h
jmp .get_char_and_add_to_buffer ; go back to main routine
; enter button pressed. Jump to exit
.enter_pressed:
jmp .exit_routine
; buffer overflow (buffer is full). Don't accept any more chars and exit routine.
.buffer_overflow:
jmp .exit_routine
.exit_routine:
mov al, 0 ; end of user input signal
stosb
mov ah, 0x0E
mov al, 0x0D ; new line
int 0x10
mov al, 0x0A
int 0x10
ret ; exit entire routine
; ----------------------------------------------------------
; Routine: Compare equality of two strings
; Waits for a complete string of user input and puts it in buffer.
; Sensitive for backspace and Enter buttons
; Input 1. String1 in SI 2. String2 in DI
; Output 1. result in carry flag
; ----------------------------------------------------------
os_compare_string:
.compare_next_character: ; a loop that goes character by character
mov al, [si] ; focus on next byte in si
mov bl, [di] ; focus on next byte in di
cmp al, bl
jne .conclude_not_equal ; if not equal, conclude and exit
; we know: two bytes are equal
cmp al, 0 ; did we just compare two zeros?
je .conclude_equal ; if yes, we've reached the end of the strings. They are equal.
; increment counters for next loop
inc di
inc si
call .compare_next_character
.conclude_equal:
stc ; sets the carry flag (meaning that they ARE equal)
jmp .done
.conclude_not_equal:
clc ; clears the carry flag (meaning that they ARE NOT equal)
jmp .done
.done:
ret
编译和运行脚本(nasm 和 qemu) # 使用 NASM 将引导加载程序程序集编译为二进制文件 nasm bootloader.asm -o bootloader.bin
# Next, create a floppy disk image
dd if=/dev/zero of=boot-disk.bin bs=512 count=2880
# Write the boot sector to the disk image
dd if=bootloader.bin of=boot-disk.bin conv=notrunc
# Time to put in the kernel
# Compile kernel with NASM
nasm kernel.asm -o kernel.bin
# Write Kernel to disk in appropriate place
dd if=kernel.bin of=boot-disk.bin conv=notrunc bs=512 seek=1
# Emulator
qemu-system-i386 -s -fda boot-disk.bin -boot a
# GDB Debugger
#gdb target remote localhost:1234
【问题讨论】:
-
您确定要从气缸 1 读取数据吗?我本来期望气缸 0 扇区 2。(已经有一段时间了......)
-
分区扇区是柱面 0,磁头 0,扇区 1。引导扇区在表中,但第一个通常是柱面 0,磁头 1,扇区 1。Microsoft 分区引导代码在 0000:7c00h 加载,将自身移动到 0000:0600h,然后在 0000:7c00h (es=0, bx=7c00h) 处读取引导扇区。如果是多引导系统,此过程可能会再次重复。
-
我觉得缸数应该没问题。如果我在不添加我的部分的情况下编译教程中的代码,它就可以工作。啊,所以我最初不需要从 7c00h 启动,对吧?它是 7c0h 还是 7c00h?我不是在做多重引导,我只需要一些非常简单的东西
-
@nrz 用 8086 重新标记了这个问题。该标签编辑带来了一些问题,因为 8086 不支持此原始代码中的一条指令。直到将立即值推入堆栈才出现80186/80188 处理器。出于这个原因,我删除了 8086 标签。
-
@MichaelPetch 你是对的。感谢您纠正我的错误。 Push immed 在 8086 中不可用。
标签: assembly x86 operating-system bootloader