理解分段的问题(尝试编写简单的操作系统)

时间:2017-07-12 12:46:31

标签: assembly operating-system memory-segmentation

我试图学习低级别的东西所以我认为可能学习操作系统如何工作将是最好的选择,我开始学习: https://github.com/cfenollosa/os-tutorial

这是代码:

main.asm中

[org 0x7c00]
mov bp, 0x9000
mov sp, bp
mov bx,0x1000
mov dh,1
call disk_load
call switch_to_pm

%include "print.asm"
%include "gdt.asm"
%include "32print.asm"
%include "32switch.asm"
%include "disk_func.asm"

[bits 32]
BEGIN_PM:
mov ebx, MSG_PROT_MODE
mov eax,0
mov ch,1
mov cl,0
call print_string
mov ch,0
call print_string
mov ebx,MSG_LOADED_Kernel
mov eax,30
mov cl,1
mov ch,0
call print_string
jmp 0x1000

MSG_PROT_MODE db "Test White ... ",0
MSG_LOADED_Kernel db " Test Red ... ",0
; bootsector
times 510-($-$$) db 0
dw 0xaa55

test_load:
mov ebx,MSG_PROMPT
sub ebx,test_load
add ebx,0x1000
mov cl,2
mov ch,0
mov eax,58
call CODE_SEG:print_string
jmp $

MSG_PROMPT db "Test Green ... ",0
times 512-($-test_load) db 0

print.asm

print:
pusha
mov ah,0x0e
start:
mov al,[bx]
cmp al,0
je done
int 0x10
add bx,1
jmp start
done:
popa
ret

disk_func.asm

disk_load:
pusha
mov ah,0x02 ;<= Read
mov al,dh ;<= Sectors to read
mov cl,0x02 ;<= Sector
mov ch,0x00 ;<= Cylinder
push edx
mov dh,0x00 ;<= Head number
int 0x13
pop edx
jc disk_error
cmp al, dh
jne sectors_error
popa
ret
disk_error:
mov ecx, DISK_ERROR
call print
mov dh, ah
jmp disk_loop

sectors_error:
mov ecx, SECTORS_ERROR
call print

disk_loop:
jmp $

DISK_ERROR:
db "Disk read error", 0

SECTORS_ERROR:
db "Incorrect number of sectors read", 0

gdt.asm

gdt_start:
dd 0x0
dd 0x0
;8 null bytes

; GDT for code segment. base = 0x00000000, length = 0xfffff
gdt_code:
dw 0xffff    ; segment length, bits 0-15
dw 0x0       ; segment base, bits 0-15
db 0x0       ; segment base, bits 16-23
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x0       ; segment base, bits 24-31

; GDT for data segment. base and length identical to code segment
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0

gdt_end:
gdt_desc:
dw gdt_end - gdt_start - 1 ; size (16 bit)
dd gdt_start ; address (32 bit)


CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

32switch.asm

[bits 16]
switch_to_pm:
cli
lgdt [gdt_desc]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm

[bits 32]
init_pm:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax

mov ebp, 0x90000
mov esp, ebp

call BEGIN_PM

32print.asm

[bits 32]

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
RED_ON_BLACK equ 0x0c
GREEN_ON_BLACK equ 0x0a

print_string:
pusha
cmp cl,1
je skip_print32_red
cmp cl,2
je skip_print32_red
mov cl,0
skip_print32_red:
mov edx, VIDEO_MEMORY
add edx,eax
cmp ch,1
je clear_scrn

print_string_loop:
mov al, [ebx] ; [ebx] is the address of our character
print32_color:
cmp cl,1
je print_with_red
cmp cl,2
je print_with_green
mov ah, WHITE_ON_BLACK
jmp print32_skip_red
print_with_red:
mov ah, RED_ON_BLACK
jmp print32_skip_red
print_with_green:
mov ah, GREEN_ON_BLACK
print32_skip_red:
cmp al, 0 ; check if end of string
je print_string_done
mov [edx], ax ; store character + attribute in video memory
add ebx, 1 ; next char
add edx, 2 ; next video memory position
jmp print_string_loop
print_string_done:
popa
ret

clear_scrn:
mov al, 0x20
mov ah, WHITE_ON_BLACK
mov [edx], ax ; store character + attribute in video memory
add edx, 2 ; next video memory position
cmp edx, 0xb8492
je print_string_done
jmp clear_scrn

make_32print_red:
mov cl, 1
jmp print32_color

在32switch.asm文件中我理解,因为init_pm以[位32]开始并使用32位寄存器,跳转到它需要有CODE_SEG,因为CODE_SEG启用32位但我不明白为什么它不需要调用BEGIN_PM CODE_SEG因为BEGIN_PM也使用32位寄存器。也调用BEGIN_PM中的print_string并跳转到test_load也不需要CODE_SEG但是在test_load中有必要使用CODE_SEG来调用print_string,否则它将不会做任何事情

&#34; test_load将从hdd读取到内存并在BEGIN_PM中跳转到0x1000是跳转到test_load&#34;

我真的很困惑,有人可以向我解释为什么在这些地方需要它而在其他地方不需要它? 以及为什么从test_load调用print字符串需要CODE_SEG但是从BEGIN_PM调用它不是吗?

我使用nasm进行汇编,使用qemu运行二进制文件

我对装配和低级别的东西很新,所以我可能已经理解了一切错误

编辑: 我试图以某种方式解决它并理解它,我发现在test_load中 如果我做

mov edx,print_string
call edx

它有效但

call print_string

犯规

1 个答案:

答案 0 :(得分:-1)

我明白了 因为test_load和print都组装在一起NASM编译它们好像它们会在执行时保持相同但是test_load实际上将从hdd中读取并放入内存中0x1000因此它们的距离将与NASM认为的不同 当我做的时候

call print_string

和NASM组装它,它使用这个

e8 21 fe ff ff          call   0xfffffe26

fffffe26代表-474从调用到print_string的距离,如果它们完全按照它们写入的方式放入内存中,它将完全正常工作 但由于test_load加载到0x1000,它应该是(-1000 + 512)+( - 474)所以

call print_string

不起作用

原因

mov edx
call edx

工作是因为它调用地址本身并且它不再关心距离 它与CODE_SEG的相同,因为它认为它是一个远跳,它不使用距离就调用地址