这个问题已经解决了!!!我已经更新了引导加载程序和内核代码,以匹配已修复的代码,以便任何遇到相同问题的操作系统制造商(可能很快会在内核中添加评论)。以下是 Nate Eldredge 编写的修复代码的解释。无关,但操作系统最初称为 POVESK。结果发现已经有这样的名字,所以现在在引导加载程序代码中进行了更改。
最近在做一个操作系统,在制作GDT的时候遇到了很大的问题
内核代码(这就是我拼写内核的方式):
[org 0x8000]
[bits 16]
jmp short start
%define ENDL 0x0D, 0x0A
gdt_start:
dq 0x0000000000000000
dq 0x00CF9A000000FFFF
dq 0x00CF92000000FFFF
dq 0x00CF92000000FFFF
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
print:
push si
push ax
push bx
.loop:
lodsb
or al, al
jz .done
mov ah, 0x0E
mov bh, 0
int 0x10
jmp .loop
.done:
pop bx
pop ax
pop si
ret
start:
mov si, start_sixteen
call print
in al, 0x64
.wait_ibe:
test al, 0x02
jnz .wait_ibe
mov al, 0xD1
out 0x64, al
.wait_ibe2:
in al, 0x64
test al, 0x02
jnz .wait_ibe2
mov al, 0xDF
out 0x60, al
first_sixteen:
mov ah, 0x00
mov al, 0x03
int 0x10
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword 0x08:.afterstart
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
.afterstart:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebx, start_thirty_two
call print32
cli
hlt
jmp short start_prog
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
start_prog:
mov al, 2
mov eax, 1004
mov [Xval], eax
mov eax, 50
mov [Yval], eax
mov eax, 10
mov [Wval], eax
mov eax, 20
mov [Hval], eax
lea esi, [PySpr]
call draw_spr
.halt:
cli
hlt
draw_spr:
xor edx, edx
mov ecx, [Hval]
draw_sprite:
mov eax, [Yval]
mov ebx, 1024
mul ebx
add eax, [Xval]
mov edi, eax
push ecx
mov ecx, [Wval]
draw_pixels:
mov al, [esi]
cmp al, 5
je skip_pixel_2
stosd
jmp skip_pixel
skip_pixel_2:
inc edi
skip_pixel:
inc esi
loop draw_pixels
pop ecx
mov eax, [Yval]
inc eax
mov [Yval], eax
loop draw_sprite
ret
start_sixteen: db 'STAGE ONE START', ENDL, 0
start_thirty_two: db 'STAGE TWO START', 0
Xval dd 0
Yval dd 0
Wval dd 0
Hval dd 0
PySpr db 5,5,5,2,2,2,2,5,5,5
db 5,5,2,2,2,2,2,2,5,5
db 5,2,2,2,2,2,2,2,2,5
db 2,2,2,2,2,2,2,2,2,2
db 5,8,8,8,8,8,8,8,8,5
db 5,8,15,0,8,8,0,15,8,5
db 5,8,8,8,8,8,8,8,8,5
db 5,8,8,0,0,0,0,8,8,5
db 5,5,8,8,8,8,8,8,5,5
db 2,2,2,2,2,2,2,2,2,2
db 2,2,2,2,2,2,2,2,2,2
db 2,5,2,2,2,2,2,2,5,2
db 2,5,2,2,2,2,2,2,5,2
db 2,5,2,2,2,2,2,2,5,2
db 8,5,2,2,2,2,2,2,5,8
db 5,5,6,6,6,6,6,6,5,5
db 5,5,6,6,5,5,6,6,5,5
db 5,5,6,6,5,5,6,6,5,5
db 5,5,6,6,5,5,6,6,5,5
db 5,7,7,7,5,5,7,7,7,5
引导加载程序代码(基本上是第 3 部分的 nanobyte os 引导加载程序,但略有改动):
[org 0x7C00]
[bits 16]
%define ENDL 0x0D, 0x0A
jmp short start
nop
bdb_oem: db 'CLIPIN..'
bdb_bytes_per_sector: dw 512
bdb_sectors_per_cluster: db 1
bdb_reserved_sectors: dw 1
bdb_fat_count: db 2
bdb_dir_entries_count: dw 0E0h
bdb_total_sectors: dw 2880
bdb_media_descriptor_type: db 0F0h
bdb_sectors_per_fat: dw 9
bdb_sectors_per_track: dw 18
bdb_heads: dw 2
bdb_hidden_sectors: dd 0
bdb_large_sector_count: dd 0
ebr_drive_number: db 0
db 0
ebr_signature: db 29h
ebr_volume_id: db 12h, 34h, 56h, 78h
ebr_volume_label: db 'CLIPIN.. OS'
ebr_system_id: db 'FAT12 '
start:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
push es
push word .after
retf
.after:
mov [ebr_drive_number], dl
mov si, msg_loading
call puts
push es
mov ah, 08h
int 13h
jc floppy_error
pop es
and cl, 0x3F
xor ch, ch
mov [bdb_sectors_per_track], cx
inc dh
mov [bdb_heads], dh
mov ax, [bdb_sectors_per_fat]
mov bl, [bdb_fat_count]
xor bh, bh
mul bx
add ax, [bdb_reserved_sectors]
push ax
mov ax, [bdb_dir_entries_count]
shl ax, 5
xor dx, dx
div word [bdb_bytes_per_sector]
test dx, dx
jz .root_dir_after
inc ax
.root_dir_after:
mov cl, al
pop ax
mov dl, [ebr_drive_number]
mov bx, buffer
call disk_read
xor bx, bx
mov di, buffer
.search_kernal:
mov si, file_kernal_bin
mov cx, 11
push di
repe cmpsb
pop di
je .found_kernal
add di, 32
inc bx
cmp bx, [bdb_dir_entries_count]
jl .search_kernal
jmp kernal_not_found_error
.found_kernal:
mov ax, [di + 26]
mov [kernal_cluster], ax
mov ax, [bdb_reserved_sectors]
mov bx, buffer
mov cl, [bdb_sectors_per_fat]
mov dl, [ebr_drive_number]
call disk_read
mov bx, kernal_LOAD_SEGMENT
mov es, bx
mov bx, kernal_LOAD_OFFSET
.load_kernal_loop:
mov ax, [kernal_cluster]
add ax, 31
mov cl, 1
mov dl, [ebr_drive_number]
call disk_read
add bx, [bdb_bytes_per_sector]
mov ax, [kernal_cluster]
mov cx, 3
mul cx
mov cx, 2
div cx
mov si, buffer
add si, ax
mov ax, [ds:si]
or dx, dx
jz .even
.odd:
shr ax, 4
jmp .next_cluster_after
.even:
and ax, 0x0FFF
.next_cluster_after:
cmp ax, 0x0FF8
jae .read_finish
mov [kernal_cluster], ax
jmp .load_kernal_loop
.read_finish:
mov dl, [ebr_drive_number]
mov ax, kernal_LOAD_SEGMENT
mov ds, ax
mov es, ax
jmp kernal_LOAD_SEGMENT:kernal_LOAD_OFFSET
jmp wait_key_and_reboot
cli
hlt
floppy_error:
mov si, msg_read_failed
call puts
jmp wait_key_and_reboot
kernal_not_found_error:
mov si, msg_kernal_not_found
call puts
jmp wait_key_and_reboot
wait_key_and_reboot:
xor ah, ah
int 16h
jmp 0FFFFh:0
.halt:
cli
hlt
puts:
push si
push ax
push bx
.loop:
lodsb
or al, al
jz .done
mov ah, 0x0E
xor bh, bh
int 0x10
jmp .loop
.done:
pop bx
pop ax
pop si
ret
lba_to_chs:
push ax
push dx
xor dx, dx
div word [bdb_sectors_per_track]
inc dx
mov cx, dx
xor dx, dx
div word [bdb_heads]
mov dh, dl
mov ch, al
shl ah, 6
or cl, ah
pop ax
mov dl, al
pop ax
ret
disk_read:
push ax
push bx
push cx
push dx
push di
push cx
call lba_to_chs
pop ax
mov ah, 02h
mov di, 3
.retry:
pusha
stc
int 13h
jnc .done
popa
call disk_reset
dec di
test di, di
jnz .retry
.fail:
jmp floppy_error
.done:
popa
pop di
pop dx
pop cx
pop bx
pop ax
ret
disk_reset:
pusha
xor ah, ah
stc
int 13h
jc floppy_error
popa
ret
msg_loading: db 'BOOT START', ENDL, 0
msg_read_failed: db 'FAIL READ DISK', ENDL, 0
msg_kernal_not_found: db 'KERNAL NOT FOUND', ENDL, 0
file_kernal_bin: db 'KERNAL BIN'
kernal_cluster: dw 0
kernal_LOAD_SEGMENT equ 0x0000
kernal_LOAD_OFFSET equ 0x8000
times 510-($-$$) db 0
dw 0AA55h
buffer:
问题出在内核代码中。我进行了一些调试,并确定这可能是由于三重错误(chatGPT 得出的结论)造成的,并且它通常发生在 jmp dword 0x08:.afterstart 中,但奇怪的是,在多次尝试修复此问题后(找到许多教程和项目并将它们修复到我的代码中),三重错误出现在许多不同的代码行中。我在这个主题上相当缺乏经验,所以我可能真的很笨,错过了一些显而易见的东西。
如果有人有解决方案请告诉我。提前谢谢!
三重失误jmp dword 0x08:.afterstart
,并已开始出现在许多其他线路中。
构建命令:
--WinCMD NASM
nasm scr/boot.asm -f bin -o build/boot.bin
nasm scr/kernal.asm -f bin -o build/kernal.bin
--WSL
dd if=/dev/zero of=v_004.img bs=512 count=2880
mkfs.fat -F 12 -n NBOS v_004.img
dd if=boot.bin of=v_004.img conv=notrunc
mcopy -i v_004.img kernal.bin ::kernal.bin
--QEMUCMD
qemu-system-x86_64 v_004.img
gdt_start
ecm 指出了需要 的错误+ kernal_LOAD_SEGMENT * 16
,但同样的情况也适用于kernal.asm
在 32 位模式下使用的每个标签。从 16 位引导加载程序,您将内核加载到线性地址 0x20000 处并远跳转到 0x2000:0x0000,因此在进入内核时 CS = 0x2000。在
org 0
文件顶部,汇编程序计算从地址 0 开始的所有标签的地址(偏移量)。当 CS = 0x2000 时,这在 16 位模式下是正确的,但只要您处于 32 位模式,并且 CS 是一个基数 = 0 的段,则不正确。因此第一个问题出现在
jmp dword 0x08:.afterstart
。汇编器给出了.afterstart
地址 0x6E,相对于文件开头和 16 位段 0x2000 来说是正确的,但相对于基址为 0 的 32 位段 0x08 来说肯定不正确。 的实际代码位于.afterstart
线性地址 0x2006E,但您跳转到线性地址 0x0006E,该地址位于 16 位中断向量表中的某个位置,其字节肯定不是什么有用的代码。 之后的某个时间,当这些字节解码为发生故障的指令时(由于您没有安装异常处理程序,导致三重故障),您会崩溃。您可以像以前
.afterstart
一样通过替换来修复它.afterstart + kernal_LOAD_SEGMENT * 16
,但您必须对 32 位模式下使用的每个其他标签执行相同的操作:start_thirty_two
,,,等等,等等。Xval
Yval
So a better solution might be to load your kernel in the low 64 KB of memory, and far-jump to it with a segment of 0. For instance, load it at 0x0000:0x8000 (linear address 0x8000), do a far jump from the bootloader to 0x0000:0x8000 (so that CS = 0), and then put
org 0x8000
at the top of your file. Then every label in your file has the correct address whether interpreted as an offset to 16-bit segment 0x0000, or as an offset to a 32-bit segment with base address 0. This is fine so long as your kernel fits in 32 KB; and once it exceeds that, you'll probably need a multi-stage bootloader in any case.Another bug is that
[bits 32]
needs to go right before.afterstart
, since that code is executing in 32-bit mode. As it stands,mov ax, 0x10
is given its 16-bit encoding, which in 32-bit mode decodes asmov eax, imm32
and uses an additional 2 bytes as the immediate. The result is that you load the high bits ofeax
with the bytes that make up the following instructionmov ds, ax
and don't actually execute it.After these fixes the
STAGE TWO START
message is successfully displayed.Next bug (minor): you don't want
ENDL
in thestart_thirty_two
string, because those bytes don't start a new line when writing directly to video memory; they just show up as funny looking characters.Finally,
draw_spr
is going to need some serious debugging work as well. I don't understand how the arithmetic there is supposed to work, but you end up with EDI = 0xcbec which is certainly not anywhere within video memory. And you are usingstosd
to store all 32 bits ofeax
but only the low 8 bits (al
) seems to have been initialized to anything meaningful; did you meanstosb
instead?More fundamentally, the label names suggest you're trying to draw pixels, but you haven't changed video modes, so you're still in text mode where bytes of video memory are interpreted as characters, not pixels. (Unless you're going for some ASCII art effect, where each "pixel" is a text-mode character cell?)
Given that you haven't commented your code at all, it's unclear what it is supposed to be doing, so you're on your own from here on out.
Get a debugger working! I found all these bugs by single-stepping your code in Bochs. It's a complete waste of time to try to develop any kind of assembly program unless you first make sure you have a debugger and know how to use it.