我正在编写一个只加载内核的bootloader。我一直在关注一个教程,并对其汇编代码进行了一些调整,但是教程上的地址不再有效,因此内核未加载。我不知道寻址是如何工作的,以及我定义的附加字节和我添加的代码如何使教程中的地址无效。 我使用的教程是:http://inglorion.net/documents/tutorials/x86ostut/bootsector/
我应该怎么做以确保地址有效? 根据教程,我们应该可以在1000h时加载内核,但这似乎不会发生。
这是我的代码:
bootloader.asm 对于过多的评论感到抱歉
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将bootloader程序集编译为二进制文件 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
答案 0 :(得分:1)
您的bootloader.asm
可以使用以下修复程序:
int 13h; AH = 2h
扇区计数从1开始。您希望扇区2加载1。ES
设置这对我有用:
<强> bootloader.asm 强>
BITS 16 ; NASM directive for declaring the bit-mode. global start start: 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 ax, 0x07c0 mov ds, ax mov si, bootloader_status_message call bootloader_print_string ; begin reading kernel byte code from disk ; Uses interrupt 13h with AH = 2h ; Options: AL = sectors to read count ; CH = Cylinder to read, 0 to 1023 ; CL = Sector within Cylinder to read, 1-63 ; DH = Head (0 in our case) ; DL = Drive (also 0) ; ES:BX = Buffer address pointer mov ah, 2 mov al, KERNEL_BLOCK_SIZE push word KERNEL_SEGMENT pop es xor bx, bx ; reset bx to 0 mov cx, KERNEL_BLOCK_START + 1 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_print_string: mov ah, 0x0e ; print function mov al, [si] ; ascii char int 0x10 ; IO int inc si cmp byte [si], 0 jne bootloader_print_string ret 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 times 510 - ($ - $$) db 0 ; loop 510 times and pad with empty bytes dw 0xAA55 ; last 2 bytes are 55h and 0AAh