我正在为学校项目编写一个小型演示内核,并在我的内核中启用A20线,加载GDT,然后启用保护模式。当我注释掉保护模式部分时,一切正常,启用A20线并加载GDT,但是当我尝试跳转到保护模式时,屏幕(QEMU)开始闪烁。如果我将jmp注释掉,一切正常。 这是代码:
[bits 16] ;16-bit binary format
;KERNEL
os_main:
mov ax, cs ;CS is segment where we were loaded
cli
mov ss, ax
xor sp, sp
sti
cld
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov si, hello ;print welcome
call print
call A20_line
call load_gdt
call set_mode
cli
hlt
;jmp 08h:ENTER_PROTECTED_MODE
;SUBROUTINES
A20_line:
in al, 0xee
mov si, A20_enable_str
call print
ret
load_gdt:
cli
lgdt [toc]
sti
mov si, load_gdt_str
call print
ret
set_mode:
mov si, set_mode_str
call print
cli
mov eax, cr0
or al, 1
mov cr0, eax
ret
print:
mov ah, 0x0e
.next:
lodsb
cmp al,0
je .done
int 0x10
jmp .next
.done:
ret
;DATA
hello db 'Hello',13,10,0
A20_enable_str db 'A20 Line enabled',13,10,0
load_gdt_str db 'Loading GDT',13,10,0
set_mode_str db 'Entering Protected Mode',13,10,0
;GLOBAL DESCRIPTOR TABLE
gdt_data:
dd 0 ;null descriptor
dd 0
dw 0FFFFh ;code descriptor
dw 0
dw 0
db 10011010b
db 11001111b
db 0
dw 0FFFFh ;data descriptor
dw 0
dw 0
db 10011010b
db 11001111b
db 0
end_of_gdt:
toc:
dw end_of_gdt - gdt_data - 1
dd gdt_data
;PROTECTED MODE
[bits 32]
ENTER_PROTECTED_MODE:
;VIDMEM equ 0xB8000
;mov ax, 0x10
;mov es, ax
;mov ds, ax
;mov fs, ax
;mov gs, ax
;mov esp, cs
;mov eax, 0x0f54
;mov [VIDMEM], eax
cli
hlt
我注释掉了视频记忆和在保护模式下打印的部分,因为从jmp到保护模式似乎无法正常工作。
这是装载机:
%define BUFFER_OFF 0x8000
%define BUFFER_SEG 0x0000
%define LOAD_SEG 0x0000
%define LOAD_OFF 0x8000
[bits 16]
[org 0x7c00]
jmp short start
nop
;DISK DESCRIPTION(BIOS PARAMETER BLOCK)
OEMLabel db "BOOT "
BytesPerSector dw 512
SectorsPerCluster db 1
ReservedForBoot dw 1
NumberOfFats db 2
RootDirEntries dw 224 ; Number of entries in root dir
; (224 * 32 = 7168 = 14 sectors to read)
LogicalSectors dw 2880
MediumByte db 0F0h
SectorsPerFat dw 9
SectorsPerTrack dw 18 ; Sectors per track (36/cylinder)
Sides dw 2
HiddenSectors dd 0
LargeSectors dd 0
DriveNo dw 0
Signature db 0x29
VolumeID dd 00000000h
VolumeLabel db "myOS "
FileSystem db "FAT12 "
;BOOTLOADER
start:
xor ax, ax
mov ds, ax
cli
mov ss, ax
mov sp, 0x7c00
sti
cld
mov [drive], dl
mov si, BUFFER_SEG ; ES=buffer segment. Only has to be set once
mov es, si
mov bx, BUFFER_OFF
load_root:
mov ax, 19 ; Root directory starts at LBA 19
call lba_to_hts
mov ax, (2<<8) | 14 ; Root directory for this media fits in 14 sectors
; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 14'
int 13h
jc reset
mov si, load_root_str
call print
search_file:
mov di, BUFFER_OFF
mov cx, word [RootDirEntries]
xor ax, ax
.loop_search:
xchg cx, dx
mov si, filename
mov cx, 11
rep cmpsb
je file_found
add ax, 32
mov di, BUFFER_OFF
add di, ax
xchg dx, cx
loop .loop_search
jmp file_not_found
file_found:
mov ax, word [di+15] ; Buffer and Bootloader now in same segment DS
; Don't need ES:
mov [cluster], ax
mov ax, 1
call lba_to_hts
mov bx, BUFFER_OFF
mov ax, (2<<8) | 9 ; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 9'
load_FAT:
mov si, FAT_str
call print
int 13h
jnc load_file
call reset
jnc load_FAT
jmp disk_error
load_file:
mov si, load_file_str
call print
mov ax, LOAD_SEG ; ES=load segment for kernel
mov es, ax
load_sector:
mov ax, word [cluster] ; Get cluster number to read
add ax, 33-2 ; Add 31 to cluster since FAT data area
; starts at Logical Block Address (LBA) 33
; and we need to subtract 2 since valid
; cluster numbers start at 2
call lba_to_hts
xor bx, bx ; Always read a kernel sector to offset 0
mov ax, (2<<8) | 1 ; AH=2 is read, AL=1 read 1 sector
; Combine 2 moves (AH/AL) into one
; same as 'mov ah, 2' and 'mov al, 1'
int 13h
jnc next_cluster
call reset
jmp load_sector
next_cluster:
mov bx, [cluster] ; BX = current cluster number
mov ax, bx ; AX = copy of cluster number
shl bx, 1 ; BX = BX * 2
add bx, ax ; BX = BX + AX (BX now contains BX * 3)
shr bx, 1 ; Divide BX by 2
mov ax, [bx+BUFFER_OFF] ; Get cluster entry from FAT table
jnc .even ; If carry not set by SHR then result was even
.odd:
shr ax, 4 ; If cluster entry is odd then cluster number is AX >> 4
jmp short finish_load
.even:
and ah, 0Fh ; If cluster entry is even then cluster number is AX & 0fffh
; We just need to and AH with 0fh to achieve the same result
finish_load:
mov word [cluster], ax
cmp ax, 0FF8h
jae .jump_to_file
mov ax, es
add ax, 32 ; Increasing segment by 1 advances 16 bytes (paragraph)
; in memory. Adding 32 is same advancing 512 bytes (32*16)
mov es, ax ; Advance ES to point at next 512 byte block to read into
jmp load_sector ; Go back and load the next sector
.jump_to_file:
mov dl, byte [drive]
jmp LOAD_SEG:LOAD_OFF
;SUBROUTINES
file_not_found:
mov si, not_found_str
call print
jmp reboot
print:
pusha
mov ah, 0x0E
.next:
lodsb
cmp al,0
je .done
int 0x10
jmp .next
.done:
popa
ret
lba_to_hts:
div byte [SectorsPerTrack] ; 16-bit by 8-bit DIV : LBA / SPT
mov cl, ah ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
xor ah, ah ; Upper 8-bit of 16-bit value set to 0 for DIV
div byte [Sides] ; 16-bit by 8-bit DIV : (LBA / SPT) / HEADS
mov ch, al ; CH = C = (LBA / SPT) / HEADS
mov dh, ah ; DH = H = (LBA / SPT) mod HEADS
mov dl, [drive] ; boot device, not necessary to set but convenient
ret
reset:
mov ah, 0
int 13h ;reset disk
jc disk_error ;if failed jump to search fail
ret
disk_error:
mov si, disk_error_str
call print
reboot:
mov si, reboot_pmpt
call print
mov ax, 0
int 16h
mov ax, 0
int 19h
;DATA
load_root_str db 'Loading Root',13,10,0
disk_error_str db 'Disk Error!',13,10,0
reboot_pmpt db 'PRESS A KEY TO REBOOT',13,10,0
not_found_str db 'KERNEL NOT FOUND',13,10,0
FAT_str db 'Loading FAT',13,10,0
load_file_str db 'Loading KERNEL',13,10,0
drive dw 0
cluster dw 0
filename db 'KERNEL BIN',0
;PADDING AND SIGNATURE
times (510-($-$$)) db 0x00
dw 0AA55h
LOAD_SEG和LOAD_OFF设置为0x0000和0x8000,尽管相对于原始内核(检查注释)应该设置为0x1000和0x0000。
内部版本:(MacOS)
#! bin/bash
cd image
hdiutil create -fs MS-DOS -sectors 2880 floppy
cd ../system
nasm -f bin bootloader.asm -o boot.bin
nasm -f bin kernel.asm -o kernel.bin
cd ..
dd conv=notrunc if=system/boot.bin of=image/floppy.dmg
dev=`hdid -nomount image/floppy.dmg`
sudo mkdir tmp-loop
sudo mount -t msdos ${dev} tmp-loop
sudo cp system/kernel.bin tmp-loop/
diskutil umount tmp-loop
hdiutil detach ${dev}
sudo rm -rf tmp-loop
hdiutil convert image/floppy.dmg -format UDTO -o image/image.iso