分页机制重新来过,完成时也和大家分享一下!

仅仅是打印了三个字符了,但确实是用心去练习了。

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
发布了159 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_39410618/article/details/103961538
今日推荐