您的位置:首页 > 其它

读书笔记《30天自制操作系统》day03

2012-12-13 22:59 337 查看
0. 自己试着在win7下用NASM和minGW改写汇编和C混合编程,结果受挫了。还是先使用作者提供的工具构建吧。

1. 通过前2天的工作已经能使用NASM制作一个映像了,并且编写的汇编代码可以成为引导扇区代码。

2. 引导扇区代码中可以调用BIOS中断,读取软盘上其它扇区到内存中,根据FAT12文件系统格式得知,保存到软盘内的第一个文件的文件名一定从19逻辑扇区开始,且该文件的内容从逻辑扇区33开始(见day01,图一)。

3. 引导扇区可以将第一个保存的文件(asmhead.nas,功能是跳入保护模式并调用C语言编写的函数代码)读入内存并使之执行,找这个文件用到了一个技巧(如2描述),不然通过文件系统结构分析出文件位置,并加载码就太复杂了。

4. 引导扇区代码如下ipl10.asm

CYLS	EQU		10

ORG		0x7c00

JMP		entry
DB		0x90
DB		"HARIBOTE"
DW		512
DB		1
DW		1
DB		2
DW		224
DW		2880
DB		0xf0
DW		9
DW		18
DW		2
DD		0
DD		2880
DB		0,0,0x29
DD		0xffffffff
DB		"HARIBOTEOS "
DB		"FAT12   "
RESB	18

entry:
MOV		AX,0
MOV		SS,AX
MOV		SP,0x7c00
MOV		DS,AX

MOV		AX,0x0820  ;目的地址
MOV		ES,AX
MOV		CH,0	   ;柱面号
MOV		DH,0	   ;磁头号
MOV		CL,2	   ;扇区号
readloop:
MOV		SI,0
retry:
MOV		AH,0x02	   ;读磁盘
MOV		AL,1	   ;读1个扇区
MOV		BX,0       ;目的地址
MOV		DL,0x00	   ;驱动器号
INT		0x13	   ;来BIOS中断
JNC		next
ADD		SI,1
CMP		SI,5
JAE		error	   ;读五次还失败就放弃
MOV		AH,0x00    ;重置驱动器功能号
MOV		DL,0x00	   ;驱动器号
INT		0x13
JMP		retry
next:
MOV		AX,ES
ADD		AX,0x0020   ;保存位置向后移动512字节,0x0020是段地址加偏移量后成为0x0200了
MOV		ES,AX
ADD		CL,1
CMP		CL,18       ;读18个扇区
JBE		readloop
MOV		CL,1
ADD		DH,1
CMP		DH,2
JB		readloop
MOV		DH,0
ADD		CH,1
CMP		CH,CYLS
JB		readloop

MOV		[0x0ff0],CH
JMP		0xc200        ;跳到软盘kernel.sys(asmhead.nas+bootpack.c)文件内部执行
;该文件在加载软盘文件基址0x8000+Fat12文件系统中文件出现位置0x004200处=0xc200H
error:
MOV             SI,msg
putloop:
MOV		AL,[SI]
ADD		SI,1
CMP		AL,0
JE		fin
MOV		AH,0x0e
MOV		BX,15
INT		0x10
JMP		putloop
fin:
HLT
JMP		fin
msg:
DB		0x0a, 0x0a
DB		"load error"
DB		0x0a
DB		0;
RESB	        0x7dfe-$
times           510-($-$$) db 0
DB		0x55, 0xaa

BIOS 13中断说明(功能有磁盘的读、写、扇区校验、寻道)

AH=0x02 读盘/0x03写盘/0x04校验/0x0c寻道

AL=处理连续扇区数

CH=柱面号&0xff

CL=扇区号(0~5位)|(柱面号&0x300)>>2

DH=磁头号

DL=驱动器号

ES:BX=缓冲地址

返回值:FLAGS=0没有错误AH=0,FLAGS=1有错误AH保存错误码

5. 跳入保护模式代码如下asmhead.nas(1)准备GDT(2)用LGDT加载gdtr(3)打开A20(4)设置CR0的PE位(5)跳转进入保护模式

BOTPAK	EQU		0x00280000
DSKCAC	EQU		0x00100000
DSKCAC0	EQU		0x00008000

CYLS	EQU		0x0ff0
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2
SCRNX	EQU		0x0ff4
SCRNY	EQU		0x0ff6
VRAM	EQU		0x0ff8

ORG		0xc200	 ;让引导扇区加载后从这里开始运行,认为它们在一个段中所以能直接跳转过来

MOV		AL,0x13		;保存信息
MOV		AH,0x00
INT		0x10
MOV		BYTE [VMODE],8
MOV		WORD [SCRNX],320
MOV		WORD [SCRNY],200
MOV		DWORD [VRAM],0x000a0000

MOV		AH,0x02
INT		0x16 			; keyboard BIOS
MOV		[LEDS],AL

MOV		AL,0xff                 ;禁止PIC主从片的中断
OUT		0x21,AL
NOP					;不能有两个连续的OUT指令
OUT		0xa1,AL

CLI		                        ;禁止PIC工作要在CLI之前

CALL	waitkbdout                      ;等待键盘电路准备好,要设置A20使1MB以上内存能被访问
MOV		AL,0xd1
OUT		0x64,AL
CALL	waitkbdout
MOV		AL,0xdf			; enable A20
OUT		0x60,AL
CALL	waitkbdout

[INSTRSET "i486p"]

LGDT	[GDTR0]			        ;加载临时的GDT表首地址到GDTR寄存器
MOV		EAX,CR0
AND		EAX,0x7fffffff
OR		EAX,0x00000001
MOV		CR0,EAX                 ;设置CR0寄存器PE标志位
JMP		pipelineflush           ;设置标志位之后马上JMP
pipelineflush:                                          ;从此寻址方式变了
MOV		AX,1*8                  ;段值的设置,用GDT中那个段(GDT+1段)
MOV		DS,AX
MOV		ES,AX
MOV		FS,AX
MOV		GS,AX
MOV		SS,AX

MOV		ESI,bootpack	        ;将bootpack.c生成的目标代码移动到0x00280000,乾坤大挪移
MOV		EDI,BOTPAK
MOV		ECX,512*1024/4
CALL	memcpy

MOV		ESI,0x7c00              ;将启动扇区复制到1MB以后的内存
MOV		EDI,DSKCAC
MOV		ECX,512/4
CALL	memcpy

MOV		ESI,DSKCAC0+512	        ;将0x00008200数据复制到0x00100200
MOV		EDI,DSKCAC+512
MOV		ECX,0
MOV		CL,BYTE [CYLS]
IMUL	        ECX,512*18*2/4
SUB		ECX,512/4
CALL	memcpy
;至此内存中0x00100000部分与磁盘内容就一样了

;调用bootpack.c的初始化操作,解析bootpack.hrb的header并传入参数
MOV		EBX,BOTPAK
MOV		ECX,[EBX+16]
ADD		ECX,3
SHR		ECX,2
JZ		skip
MOV		ESI,[EBX+20]
ADD		ESI,EBX
MOV		EDI,[EBX+12]
CALL	memcpy
skip:
MOV		ESP,[EBX+12]
JMP		DWORD 2*8:0x0000001b          ;跳到指定段中跳过可执行文件头p421

waitkbdout:
IN		 AL,0x64
AND		 AL,0x02
JNZ		waitkbdout
RET

memcpy:
MOV		EAX,[ESI]
ADD		ESI,4
MOV		[EDI],EAX
ADD		EDI,4
SUB		ECX,1
JNZ		memcpy
RET

ALIGNB	16
GDT0:                                                            ;临时设计的GDT表
RESB	8
DW		0xffff,0x0000,0x9200,0x00cf
DW		0xffff,0x0000,0x9a28,0x0047

DW		0
GDTR0:                                                           ;临时设计的GDT选择子
DW		8*3-1
DD		GDT0

ALIGNB	16
bootpack:

6. C语言代码如下bootpack.c

void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{
int i;
for(i=0xa0000;i<=0xaffff;i++)
{
write_mem8(i,15);        /*向内存的0xa0000~0xaffff位置写入信息,这块内存是显存空间,15是白色*/
}
for(;;)
{
io_hlt();
}

}


7. C语言中调用的io_hlt和write_mem8函数放到了如下代码中func.asm

[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "naskfunc.nas"]
global _io_hlt,_write_mem8
[section .text]
;void io_hlt(void);
_io_hlt:
HLT
RET

;void write_mem8(int addr,int data);
_write_mem8:
MOV ECX,[ESP+4]
MOV AL,[ESP+8]
MOV [ECX],AL
RET

8. 在toolset文件夹内建立一个新文件夹,将上面所有的文件放在里边,编译链接接上面的文件,写个bat文件如下

nasm -o ipl10.bin ipl10.asm  ;生成引导扇区代码
nasm -o img.img img.asm      ;生成软盘镜像文件
..\z_tools\nask.exe asmhead.nas asmhead.bin
..\z_tools\cc1.exe -I..\z_tools\haribote\ -Os -Wall -quiet -o bootpack.gas bootpack.c
..\z_tools\gas2nask.exe -a bootpack.gas bootpack.nas
..\z_tools\nask.exe bootpack.nas bootpack.obj
..\z_tools\nask.exe func.asm func.obj
..\z_tools\obj2bim.exe @..\z_tools\haribote\haribote.rul out:bootpack.bim stack:3136k map:bootpack.map bootpack.obj func.obj
..\z_tools\bim2hrb.exe bootpack.bim bootpack.hrb 0
copy /B asmhead.bin+bootpack.hrb kernel.sys


9.这样除了中间文件外,生成img.img文件和kernel.sys文件。使用winImage打开img.img文件将kernel.sys文件加入到该img文件中。

10. 启动Bochs,呵呵看见屏幕白了,这可是从C代码里控制的啊!

11. asmhead中跳入保护模式的代码慢慢在深入掌握,不然会掉入细节里不能自拔了。

12. (这句很经典)asmhead和C代码是通过copy /B进行链接的其中asmhead代码最后留了个标号bootpack,在这个标号后面C的目标代码被砍去文件头直接将代码链接到了这里,所以能实现从汇编跳转到C语言的目的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: