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语言的目的。