MyOS 进入32位模式并导入C语言
在ubuntu下编译第三天的代码
修改Makefile
- 重新定义工具和参数
# 定义工具和参数
# Ubuntu下使用系统自带的工具,无需再指定z_tools路径
MAKE = make
NASM = nasm
GCC = gcc
LD = ld
OBJCOPY = objcopy
QEMU = qemu-system-i386
RM = rm -f
CP = cp# GCC编译选项:
# -m32: 生成32位代码 (Haribote运行在32位保护模式)
# -nostdlib: 不使用标准库 (我们是在写操作系统内核)
# -fno-pie: 关闭位置无关可执行文件 (内核需要固定地址)
# -I. : 头文件路径
CFLAGS = -m32 -nostdlib -fno-pie -I. -Os -Wall -Wextra# 链接选项:
# -m elf_i386: 指定生成32位的 elf 格式 (配合 -m32 使用)
# -Ttext 0xc200: 将代码段起始地址指定为0xc200,对应haribote的bootpack装载地址
LDFLAGS = -m elf_i386 -Ttext 0xc200# 默认动作
default :$(MAKE) img# 1. 编译IPL (启动区)
ipl10.bin : ipl10.nas$(NASM) ipl10.nas -o ipl10.bin -l ipl10.lst# 2. 编译汇编启动头 (asmhead)
asmhead.bin : asmhead.nas$(NASM) asmhead.nas -o asmhead.bin -l asmhead.lst# 3. 编译C语言内核文件 (bootpack)
# 将 .c 文件直接编译为 32位 的 .o 对象文件
bootpack.o : bootpack.c Makefile$(GCC) $(CFLAGS) -c bootpack.c -o bootpack.o# 4. 编译汇编函数文件
naskfunc.o : naskfunc.nas Makefile$(NASM) -f elf32 naskfunc.nas -o naskfunc.o -l naskfunc.lst# 5. 链接生成内核二进制文件
# 链接所有 .o 文件,并输出为 elf 格式,再用 objcopy 转为纯二进制
bootpack.bin : bootpack.o naskfunc.o Makefile$(LD) $(LDFLAGS) bootpack.o naskfunc.o -o bootpack.elf$(OBJCOPY) -O binary bootpack.elf bootpack.bin# 6. 合并启动头与内核,生成最终系统文件
haribote.sys : asmhead.bin bootpack.bincat asmhead.bin bootpack.bin > haribote.sys# 7. 制作磁盘镜像 (使用dd命令创建软盘镜像并写入文件)
haribote.img : ipl10.bin haribote.sys# 创建一个空的1440KB软盘镜像dd if=/dev/zero of=haribote.img bs=512 count=2880 2>/dev/null# 将ipl10.bin写入第一个扇区dd if=ipl10.bin of=haribote.img bs=512 count=1 conv=notrunc 2>/dev/null# 拷贝系统文件到镜像中 (这里借助mcopy工具,如果没有请先安装: sudo apt install mtools)# 或者你也可以利用后续的run指令直接通过QEMU挂载文件夹,不一定要打包进imgmcopy -i haribote.img haribote.sys ::# 8. 运行 (通过QEMU直接启动)
run : haribote.sys$(QEMU) -drive file=haribote.sys,format=raw,if=floppy -boot a# 9. 完整制作镜像
img :$(MAKE) haribote.img# 10. 清理临时文件
clean :$(RM) *.bin *.lst *.o *.elf *.sys *.img# 11. 彻底清理(包括镜像)
src_only :$(MAKE) clean$(RM) haribote.img
修改nas文件
- 修改 RESB 关键字
; ipl10.nas
; haribote-ipl
; TAB=4CYLS EQU 10 ; 声明CYLS=10ORG 0x7c00 ; 指明程序装载地址; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy codeJMP entryDB 0x90DB "HARIBOTE" ; 启动扇区名称(8字节)DW 512 ; 每个扇区(sector)大小(必须512字节)DB 1 ; 簇(cluster)大小(必须为1个扇区)DW 1 ; FAT起始位置(一般为第一个扇区)DB 2 ; FAT个数(必须为2)DW 224 ; 根目录大小(一般为224项)DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512)DB 0xf0 ; 磁盘类型(必须为0xf0)DW 9 ; FAT的长度(必??9扇区)DW 18 ; 一个磁道(track)有几个扇区(必须为18)DW 2 ; 磁头数(必??2)DD 0 ; 不使用分区,必须是0DD 2880 ; 重写一次磁盘大小DB 0,0,0x29 ; 意义不明(固定)DD 0xffffffff ; (可能是)卷标号码DB "HARIBOTEOS " ; 磁盘的名称(必须为11字?,不足填空格)DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格)RESB 18 ; 先空出18字节; 程序主体entry:MOV AX,0 ; 初始化寄存器MOV SS,AXMOV SP,0x7c00MOV DS,AX; 读取磁盘MOV AX,0x0820MOV ES,AXMOV CH,0 ; 柱面0MOV DH,0 ; 磁头0MOV CL,2 ; 扇区2readloop:MOV SI,0 ; 记录失败次数寄存器retry:MOV AH,0x02 ; AH=0x02 : 读入磁盘MOV AL,1 ; 1个扇区MOV BX,0MOV DL,0x00 ; A驱动器INT 0x13 ; 调用磁盘BIOSJNC next ; 没出错则跳转到finADD SI,1 ; 往SI加1CMP SI,5 ; 比较SI与5JAE error ; SI >= 5 跳转到errorMOV AH,0x00MOV DL,0x00 ; A驱动器INT 0x13 ; 重置驱动器JMP retry
next:MOV AX,ES ; 把内存地址后移0x200(512/16十六进制转换)ADD AX,0x0020MOV ES,AX ; ADD ES,0x020因为没有ADD ES,只能通过AX进行ADD CL,1 ; 往CL里面加1CMP CL,18 ; 比较CL与18JBE readloop ; CL <= 18 跳转到readloopMOV CL,1ADD DH,1CMP DH,2JB readloop ; DH < 2 跳转到readloopMOV DH,0ADD CH,1CMP CH,CYLSJB readloop ; CH < CYLS 跳转到readloop; 读取完毕,跳转到haribote.sys执行!MOV [0x0ff0],CH ; IPLがどこまで読んだのかをメモJMP 0xc200error:MOV SI,msgputloop:MOV AL,[SI]ADD SI,1 ; 给SI加1CMP AL,0JE finMOV AH,0x0e ; 显示一个文字MOV BX,15 ; 指定字符颜色INT 0x10 ; 调用显卡BIOSJMP putloopfin:HLT ; 让CPU停止,等待指令JMP fin ; 无限循环msg:DB 0x0a, 0x0a ; 换行两次DB "load error"DB 0x0a ; 换行DB 0; RESB 0x7dfe-$ ; 填写0x00直到0x001fetimes 0x1fe-($-$$) db 0DB 0x55, 0xaa
- 修改 asmhead.nas 文件
; haribote-os boot asm
; TAB=4BOTPAK EQU 0x00280000 ; 加载bootpack
DSKCAC EQU 0x00100000 ; 磁盘缓存的位置
DSKCAC0 EQU 0x00008000 ; 磁盘缓存的位置(实模式); BOOT_INFO相关
CYLS EQU 0x0ff0 ; 引导扇区设置
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色的信息
SCRNX EQU 0x0ff4 ; 分辨率X
SCRNY EQU 0x0ff6 ; 分辨率Y
VRAM EQU 0x0ff8 ; 图像缓冲区的起始地址ORG 0xc200 ; 这个的程序要被装载的内存地址; 画面モードを設定MOV AL,0x13 ; VGA显卡,320x200x8bitMOV AH,0x00INT 0x10MOV BYTE [VMODE],8 ; 屏幕的模式(参考C语言的引用)MOV WORD [SCRNX],320MOV WORD [SCRNY],200MOV DWORD [VRAM],0x000a0000; 通过BIOS获取指示灯状态MOV AH,0x02INT 0x16 ; keyboard BIOSMOV [LEDS],AL; 防止PIC接受所有中断
; AT兼容机的规范、PIC初始化
; 然后之前在CLI不做任何事就挂起
; PIC在同意后初始化MOV AL,0xffOUT 0x21,ALNOP ; 不断执行OUT指令OUT 0xa1,ALCLI ; 进一步中断CPU; 让CPU支持1M以上内存、设置A20GATECALL waitkbdoutMOV AL,0xd1OUT 0x64,ALCALL waitkbdoutMOV AL,0xdf ; enable A20OUT 0x60,ALCALL waitkbdout; 保护模式转换;[INSTRSET "i486p"] ; 说明使用486指令LGDT [GDTR0] ; 设置临时GDTMOV EAX,CR0AND EAX,0x7fffffff ; 使用bit31(禁用分页)OR EAX,0x00000001 ; bit0到1转换(保护模式过渡)MOV CR0,EAXJMP pipelineflush
pipelineflush:MOV AX,1*8 ; 写32bit的段MOV DS,AXMOV ES,AXMOV FS,AXMOV GS,AXMOV SS,AX; bootpack传递MOV ESI,bootpack ; 源MOV EDI,BOTPAK ; 目标MOV ECX,512*1024/4CALL memcpy; 传输磁盘数据; 从引导区开始MOV ESI,0x7c00 ; 源MOV EDI,DSKCAC ; 目标MOV ECX,512/4CALL memcpy; 剩余的全部MOV ESI,DSKCAC0+512 ; 源MOV EDI,DSKCAC+512 ; 目标MOV ECX,0MOV CL,BYTE [CYLS]IMUL ECX,512*18*2/4 ; 除以4得到字节数SUB ECX,512/4 ; IPL偏移量CALL memcpy; 由于还需要asmhead才能完成
; 完成其余的bootpack任务; bootpack启动MOV EBX,BOTPAKMOV ECX,[EBX+16]ADD ECX,3 ; ECX += 3;SHR ECX,2 ; ECX /= 4;JZ skip ; 传输完成MOV ESI,[EBX+20] ; 源ADD ESI,EBXMOV EDI,[EBX+12] ; 目标CALL memcpy
skip:MOV ESP,[EBX+12] ; 堆栈的初始化JMP DWORD 2*8:0x0000001bwaitkbdout:IN AL,0x64AND AL,0x02JNZ waitkbdout ; AND结果不为0跳转到waitkbdoutRETmemcpy:MOV EAX,[ESI]ADD ESI,4MOV [EDI],EAXADD EDI,4SUB ECX,1JNZ memcpy ; 运算结果不为0跳转到memcpyRET
; memcpy地址前缀大小ALIGNB 16
GDT0:
; RESB 8 ; 初始值times 8 db 0DW 0xffff,0x0000,0x9200,0x00cf ; 写32bit位段寄存器DW 0xffff,0x0000,0x9a28,0x0047 ; 可执行的文件的32bit寄存器(bootpack用)DW 0
GDTR0:DW 8*3-1DD GDT0align 16
bootpack:
- 修改nasfunc.nas文件
; [FORMAT "WCOFF"] ; ← 删除或注释掉,nasm不需要
; [BITS 32] ; ← 这个可以保留,但nasm会通过命令行参数指定
[BITS 32] ; 制作32位模式用的机器语言; 制作目标文件的信息
; [FILE "naskfunc.nas"] ; ← 删除或注释掉,nasm不需要GLOBAL _io_hlt ; 程序中包含的函数名; 以下是实际的函数实现
_io_hlt: ; void io_hlt(void);HLTRET
nasm支持以上内容,无需特意声明
详解 ipl.nas 文件
; haribote-ipl
; TAB=4CYLS EQU 10 ; 声明CYLS=10ORG 0x7c00 ; 指明程序装载地址; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy codeJMP entryDB 0x90DB "HARIBOTE" ; 启动扇区名称(8字节)DW 512 ; 每个扇区(sector)大小(必须512字节)DB 1 ; 簇(cluster)大小(必须为1个扇区)DW 1 ; FAT起始位置(一般为第一个扇区)DB 2 ; FAT个数(必须为2)DW 224 ; 根目录大小(一般为224项)DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512)DB 0xf0 ; 磁盘类型(必须为0xf0)DW 9 ; FAT的长度(必??9扇区)DW 18 ; 一个磁道(track)有几个扇区(必须为18)DW 2 ; 磁头数(必??2)DD 0 ; 不使用分区,必须是0DD 2880 ; 重写一次磁盘大小DB 0,0,0x29 ; 意义不明(固定)DD 0xffffffff ; (可能是)卷标号码DB "HARIBOTEOS " ; 磁盘的名称(必须为11字?,不足填空格)DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格)RESB 18 ; 先空出18字节; 程序主体entry:MOV AX,0 ; 初始化寄存器MOV SS,AXMOV SP,0x7c00MOV DS,AX; 读取磁盘MOV AX,0x0820MOV ES,AXMOV CH,0 ; 柱面0MOV DH,0 ; 磁头0MOV CL,2 ; 扇区2readloop:MOV SI,0 ; 记录失败次数寄存器retry:MOV AH,0x02 ; AH=0x02 : 读入磁盘MOV AL,1 ; 1个扇区MOV BX,0MOV DL,0x00 ; A驱动器INT 0x13 ; 调用磁盘BIOSJNC next ; 没出错则跳转到finADD SI,1 ; 往SI加1CMP SI,5 ; 比较SI与5JAE error ; SI >= 5 跳转到errorMOV AH,0x00MOV DL,0x00 ; A驱动器INT 0x13 ; 重置驱动器JMP retry
next:MOV AX,ES ; 把内存地址后移0x200(512/16十六进制转换)ADD AX,0x0020MOV ES,AX ; ADD ES,0x020因为没有ADD ES,只能通过AX进行ADD CL,1 ; 往CL里面加1CMP CL,18 ; 比较CL与18JBE readloop ; CL <= 18 跳转到readloopMOV CL,1ADD DH,1CMP DH,2JB readloop ; DH < 2 跳转到readloopMOV DH,0ADD CH,1CMP CH,CYLSJB readloop ; CH < CYLS 跳转到readloop; 读取完毕,跳转到haribote.sys执行!MOV [0x0ff0],CH ; IPLがどこまで読んだのかをメモJMP 0xc200error:MOV SI,msgputloop:MOV AL,[SI]ADD SI,1 ; 给SI加1CMP AL,0JE finMOV AH,0x0e ; 显示一个文字MOV BX,15 ; 指定字符颜色INT 0x10 ; 调用显卡BIOSJMP putloopfin:HLT ; 让CPU停止,等待指令JMP fin ; 无限循环msg:DB 0x0a, 0x0a ; 换行两次DB "load error"DB 0x0a ; 换行DB 0; RESB 0x7dfe-$ ; 填写0x00直到0x001fetimes 0x1fe-($-$$) db 0DB 0x55, 0xaa