僅僅是打印了三個字符了,但確實是用心去練習了。
rem auto.cmd
@echo off
nasm -fbin -o boot.bin boot.asm
dd if=boot.bin of=c.img seek=0 count=1
echo.
nasm -fbin -o loader.bin loader.asm
dd if=loader.bin of=c.img seek=1 count=2
echo.
nasm -fbin -o kernel.bin kernel.asm
dd if=kernel.bin of=c.img seek=3 count=253
echo.
rem del *.bin
del c.img.lock
;boot. asm
bits 16
start:
mov ax, 0x7c0
mov ds, ax
cli
mov dx, 0x1f2
mov al, 2 ; 2 sector
out dx, al
mov dx, 0x1f3
mov al, 1 ; the second sector, from zero to ...
out dx, al
mov dx, 0x1f4
mov al, 0
out dx, al
mov dx, 0x1f5
mov al, 0
out dx, al
mov dx, 0x1f6
mov al, 0xe0 ; LBA mode
out dx, al
mov dx, 0x1f7
mov al, 0x20
out dx, al
mov dx, 0x1f7
.1:
in al, dx
and al, 0x88
cmp al, 0x08
jne .1
mov dx, 0x1f0
mov bx, 0x200
mov cx, 256
.2:
in ax, dx
mov [bx], ax
inc bx
inc bx
loop .2
lgdt [gdtr]
in al, 0x92
or al, 0000_0010b ; 0x2
out 0x92, al
mov eax, cr0
or eax, 0000_0000_0000_0000_0000_0000_0000_0001b ; 0x1
mov cr0, eax
jmp dword 1 * 8 : 0
gdt_start:
dq 0x0000_0000_0000_0000 ; 0 * 8
dq 0x00c0_9a00_7e00_ffff ; 1 * 8
dq 0x00c0_9200_7e00_ffff ; 2 * 8
dq 0x00c0_920b_8000_ffff ; 3 * 8
gdt_end:
gdtr:
dw gdt_end - gdt_start - 1
dd gdt_start + 0x7c00
times 510 - ($ - $$) db 0
dw 0xaa55
; loader.asm
bits 32
start:
mov ax, 3 * 8
mov gs, ax
mov ax, 2 * 8
mov ds, ax
; xor ebx, ebx
; mov byte [gs: ebx], 'P'
; inc ebx
; mov byte [gs: ebx], 0xc
; inc ebx
mov dx, 0x1f2
mov al, 253 ; 253 sectors
out dx, al
mov dx, 0x1f3
mov al, 3 ; the third sector
out dx, al
mov dx, 0x1f4
mov al, 0
out dx, al
mov dx, 0x1f5
mov al, 0
out dx, al
mov dx, 0x1f6
mov al, 0xe0 ; LBA mode
out dx, al
mov dx, 0x1f7
mov al, 0x20 ; read sector instruction
out dx, al
mov dx, 0x1f7
.1:
in al, dx
and al, 0x88
cmp al, 0x08
jnz .1
mov dx, 0x1f0
mov ebx, 0x100000 - 0x7e00
mov ecx, 253 * 256
.2:
in ax, dx
mov [ebx], ax
inc ebx
inc ebx
loop .2
lgdt [gdtr]
jmp dword 4 * 8 : 0
gdt_start:
dq 0x0000_0000_0000_0000 ; 0 * 8
dq 0x00c0_9a00_7e00_ffff ; 1 * 8
dq 0x00c0_9200_7e00_ffff ; 2 * 8
dq 0x00c0_920b_8000_ffff ; 3 * 8
dq 0x00cf_9a10_0000_ffff ; 4 * 8
dq 0x00cf_9210_0000_ffff ; 5 * 8
dq 0x00cf_9a00_0000_ffff ; 6 * 8
dq 0x00cf_9200_0000_ffff ; 7 * 8
gdt_end:
gdtr:
dw gdt_end - gdt_start - 1
dd gdt_start + 0x7e00
bits 32
%macro enter 0
push ebp
mov ebp, esp
%endmacro
%macro push_some 0
push ecx
push edx
push ebx
push esi
push edi
%endmacro
%macro pop_some 0
pop edi
pop esi
pop ebx
pop edx
pop ecx
%endmacro
;人爲的增加了遠返回的難度,因爲似乎沒有類似的指令, 因此在這裏用宏模擬了一個
%macro jmpf 2
; jmpf instruction(%1), format_address(%2)
mov eax, [%2]
mov [%%.0], eax
mov ax, [%2 + 4]
mov [%%.0 + 4], ax
db %1
%%.0:
dd 0
dw 0
%endmacro
start:
mov ax, 5 * 8
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, stack_top
mov ax, 3 * 8
mov gs, ax
sub esp, 20 * 4
call set_page
call 4 * 8 : cls ;使用遠轉移,返回時要使用指令retf,遠轉移的指令格式是 段選擇符的值:段內偏移地址
mov dword [esp + 0 * 4], 'P'
mov dword [esp + 1 * 4], 0xc
call 4 * 8 : _print_c
call 6 * 8 : DEMO
call set_page2
call 6 * 8 : DEMO
add esp, 20 * 4
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cls: ;void cls(void)
enter
push_some
mov eax, [ebp + 4] ;保存遠返回的地址
mov [ret_addr], eax
mov ax, [ebp + 2 * 4]
mov [ret_addr + 4], ax
xor ebx, ebx
mov al, ' '
mov ah, 0xf
mov ecx, 2000
.1:
mov [gs:ebx], ax
inc ebx
inc ebx
loop .1
pop_some
leave
;retf ;因爲我們的調用是遠調用,所以這裏也採用遠返回
;pop eax ;因爲沒有段地址,因此這樣返回會有問題
;ret
cli
add esp, 8
jmpf 0xea, ret_addr ; 這裏是故意複雜化了,其實只要用數據定義指令就好了
; db 0xea
ret_addr: dd 0
dw 0
set_page: ;void set_page(void) ;全對等的映射,也就是和物理地址和虛擬地址一一對應
pusha
mov ecx, 1024 ;共1024個頁目錄項目
mov edi, page_dir_start ;頁目錄表的偏移地址
mov eax, PAGE_TABLE_BASE + 3 ;要填充的是頁表的物理地址,+3的意思是頁屬性爲 0000000011b
.1:
stosd ;串傳送指令,每次將eax中的值,傳送到[es:edi]中的連續4個字節中
add eax, 4 * 1024 ;連續填充,也就是下一個頁目錄表項指向另外一個頁表,這些頁表也是連續存放的
loop .1
mov ecx, 5 * 1024 ; 可以使用20M的內存
mov edi, page_table_start ;取頁表的偏移地址
mov eax, 0 + 3 ;從物理頁的第0頁開始填充
.2:
stosd
add eax, 4 * 1024 ;連續填充,也就是指向下一個頁面,每個頁面的大小爲固定 4K
loop .2
mov eax, PAGE_DIR_BASE ;裝載頁目錄表基地址
mov cr3, eax
mov eax, cr0
or eax, 0x8000_0000
mov cr0, eax ;開啓分頁模式 ;1000_0000_0000_0000_0000_0000_0000_0000b,最高位置1
jmp short .page
.page: ;據說這樣可以刷新高速緩衝
popa
ret
set_page2:
pusha
mov ecx, 1024
mov edi, page_dir_start2
mov eax, PAGE_TABLE_BASE + 3
.1:
stosd
add eax, 4 * 1024
loop .1
mov ecx, 5 * 1024
mov edi, page_table_start
mov eax, 0 + 3
.2:
stosd
add eax, 4 * 1024
loop .2
mov eax, FUNC2 ; 這裏是func2的線性地址,也就是分段機制轉化得到的地址形式
mov ebx, eax ;保留一份副本
shr eax, 22 ;取得最高10位,得到在頁目錄表中的項目數
shl eax, 2 ; 乘以4,得到在頁目錄表中的偏移地址
add eax, page_dir_start2 ;加上頁目錄表的基地址,注意這裏不是物理地址,
;而是相對於此段的偏移,我們僅使用了一個段
and ebx, 0000_0000_0011_1111_1111_0000_0000_0000b ;屏蔽無用位
shr ebx, 12 ;取得中間10位,得到在某個頁表中的項目數
shl ebx, 2 ;乘以4,得到在此頁表中的偏移
mov ecx, [eax] ;取得頁表的基地址,注意這裏是頁表的物理地址
and ecx, 0xfffff000 ;去掉頁表屬性
add ecx, ebx ;得到在頁表項在頁表中的物理地址,如果在此處訪問,需要減去段長度
sub ecx, 0x100000 ;這裏減去段長度,得以訪問頁表項
mov dword [ecx], FUNC1 + 3 ;將函數的所在頁物理地址和頁屬性存入頁表項中,從而使改變了頁映射關係
;使func2的線性地址映射到func1
mov eax, PAGE_DIR_BASE2
mov cr3, eax
jmp short .page
.page:
popa
ret
print_c: ; void print_c(char c, int colour) ;字符打印的函數
enter
push_some
sub esp, 20 * 4
mov ebx, [cursor_pos]
mov al, [ebp + 2 * 4]
mov ah, [ebp + 3 * 4]
mov [gs:ebx], ax
inc ebx
inc ebx
mov [cursor_pos], ebx
add esp, 20 * 4
leave
ret
_print_c: ; void _print_c(char c, int colour) ;字符打印的函數
enter
push_some
sub esp, 20 * 4
mov ebx, [cursor_pos] ;遠調用壓入堆棧8個字節分別是段選擇符和偏移地址,所以這裏累計加到3 * 4
mov al, [ebp + 3 * 4]
mov ah, [ebp + 4 * 4]
mov [gs:ebx], ax
inc ebx
inc ebx
mov [cursor_pos], ebx
add esp, 20 * 4
leave
retf
cursor_pos: dd 0 ;光標位置,這裏未設置
;棧空間
stack_bottom:
times 1024 db 0
stack_top:
;頁目錄表1
align 4 * 1024 ; 4k
page_dir_start:
times 4 * 1024 db 0
page_dir_end:
PAGE_DIR_BASE equ 0x100000 + page_dir_start ;線性地址,等同於物理地址
;頁表(公用)
page_table_start:
times 5 * 4 * 1024 db 0 ; 20M
page_table_end:
PAGE_TABLE_BASE equ 0x100000 + page_table_start
;頁目錄表2
page_dir_start2:
times 4 * 1024 db 0
page_dir_end2:
PAGE_DIR_BASE2 equ 0x100000 + page_dir_start2
align 4096 ;頁對齊
;似乎必須用這樣的格式,???
demo:
mov eax, FUNC2
call eax
retf
DEMO equ 0x100000 + demo
align 4096
func2:
enter
push_some
sub esp, 20 * 4
mov dword [esp + 0 * 4], '2'
mov dword [esp + 1 * 4], 0x9
call 4 * 8 : _print_c
add esp, 20 * 4
leave
ret
FUNC2 equ 0x100000 + func2
align 4096
func1:
enter
push_some
sub esp, 20 * 4
mov dword [esp + 0 * 4], '1' ;在頁映射變換後,不能調用print(char c, int colour)這個函數,不知道什麼原因
mov dword [esp + 1 * 4], 0xa
call 4 * 8 : _print_c
; mov ebx, 160
; mov al, '1'
; mov ah, 0xf
; mov [gs:ebx], ax
add esp, 20 * 4
leave
ret
FUNC1 equ 0x100000 + func1
###############################################################
# bochsrc.txt file for DLX Linux disk image.
###############################################################
# how much memory the emulated machine will have
megs: 64
# filename of ROM images
romimage: file=../BIOS-bochs-latest
vgaromimage: file=../VGABIOS-lgpl-latest
# what disk images will be used
floppya: 1_44=a.img, status=inserted
##floppyb: 1_44=b.img, status=inserted
# hard disk
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="c.img", cylinders=306, heads=4, spt=17
# choose the boot disk.
boot: c
# default config interface is textconfig.
#config_interface: textconfig
#config_interface: wx
#display_library: x
# other choices: win32 sdl wx carbon amigaos beos macintosh nogui rfb term svga
# where do we send log messages?
#log: bochsout.txt
# disable the mouse, since DLX is text only
mouse: enabled=0
# set up IPS value and clock sync
cpu: ips=15000000
clock: sync=both
# enable key mapping, using US layout as default.
#
# NOTE: In Bochs 1.4, keyboard mapping is only 100% implemented on X windows.
# However, the key mapping tables are used in the paste function, so
# in the DLX Linux example I'm enabling keyboard_mapping so that paste
# will work. Cut&Paste is currently implemented on win32 and X windows only.
keyboard: keymap=../keymaps/x11-pc-us.map
#keyboard: keymap=../keymaps/x11-pc-fr.map
#keyboard: keymap=../keymaps/x11-pc-de.map
#keyboard: keymap=../keymaps/x11-pc-es.map