30天自制操作系统:第三天

it2023-02-10  60

对第二天代码进行了修改,只打印hello ,uos没一点意思。

读取磁盘上10个柱面的1-18个扇区,(目前ssd已经没有柱面这个概念了)。读出来的数据放入内存0x8200起始的地方。

启动区放在0x8000内存扇区中。为什么要放在0x8000以后的内存中的呢? 只是因为这块内存没有用而已。

为什么要将启动程序(img的0扇区)放在0x7c00处? 这是ibm规定的。

向一个空软盘保存文件时:

​ 1.文件名会写在0x002600以后的地方。

​ 2.文件内容会写在0x004200以后的地方。

所以若想启动位于0x004200的程序,就得让引导程序运行完成后跳转到0x8000+0x4200=0xc200处。

作者这一块写的很混乱啊,咱们整理一下为啥是0xc200地址,看下图:

bios会把磁盘0位置的内容拷贝到内存0x7c00这个位置然后跳过去执行, 也就是把A拷贝到了内存,然后执行A A会把磁盘后面的内容从内存0x8200开始拷贝 这样的话磁盘0x4200位置就会对应到内存0xC200 一个扇区512字节,十六进制表示就是0x200

程序的执行情况:

1.bios读取磁盘0扇区到0x7c00处。

2.bios跳转到0x7c00处开始执行,该处为ipl10.nas程序,该程序功能为加载磁盘[1-最后]扇区到内存的0x8200处。并跳转到0xc200处执行。

3.0xc200处为asmhead.nas程序,该程序功能为,调用显卡bios,设置画面模式,调用操作系统代码。

4.操作系统代码目前就一个功能就是让cpu睡眠,开始进入c程序。

下面根据功能修改代码:

1.修改ipl.nas

ipl10.nas:

; haribote-ipl ; TAB=4 CYLS EQU 10 ; 声明CYLS=10 ORG 0x7c00 ; 指明程序装载地址 ; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code JMP entry DB 0x90 DB "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 ; 不使用分区,必须是0 DD 2880 ; 重写一次磁盘大小 DB 0,0,0x29 ; 意义不明(固定) DD 0xffffffff ; (可能是)卷标号码 DB "HARIBOTEOS " ; 磁盘的名称(必须为11字?,不足填空格) DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格) RESB 18 ; 先空出18字节 ; 程序主体 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX ; 读取磁盘 MOV AX,0x0820 MOV ES,AX MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 readloop: MOV SI,0 ; 记录失败次数寄存器 retry: MOV AH,0x02 ; AH=0x02 : 读入磁盘 MOV AL,1 ; 1个扇区 MOV BX,0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JNC next ; 没出错则跳转到next ADD SI,1 ; 往SI加1 CMP SI,5 ; 比较SI与5 JAE error ; SI >= 5 跳转到error MOV AH,0x00 MOV DL,0x00 ; A驱动器 INT 0x13 ; 重置驱动器 JMP retry next: MOV AX,ES ; 把内存地址后移0x200(512/16十六进制转换) ADD AX,0x0020 MOV ES,AX ; ADD ES,0x020因为没有ADD ES,只能通过AX进行 ADD CL,1 ; 往CL里面加1 CMP CL,18 ; 比较CL与18 JBE readloop ; CL <= 18 跳转到readloop MOV CL,1 ADD DH,1 CMP DH,2 JB readloop ; DH < 2 跳转到readloop MOV DH,0 ADD CH,1 CMP CH,CYLS JB readloop ; CH < CYLS 跳转到readloop ; 读取完毕,跳转到haribote.sys执行! MOV [0x0ff0],CH ; 记下IPL读到哪里了 JMP 0xc200 error: MOV SI,msg putloop: MOV AL,[SI] ADD SI,1 ; 给SI加1 CMP AL,0 JE fin MOV AH,0x0e ; 显示一个文字 MOV BX,15 ; 指定字符颜色 INT 0x10 ; 调用显卡BIOS JMP putloop fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 msg: DB 0x0a, 0x0a ; 换行两次 DB "load error" DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00直到0x001fe DB 0x55, 0xaa

我们在使用段寄存器时,以ES:BX这种方式表示地址,写成"MOV AL, [ES:BX]"她代表ES×16+BX内存地址。

2.修改asmhead.nas

; haribote-os boot asm ; TAB=4 ;一些定义 BOTPAK EQU 0x00280000 ; bootpack�̃��[�h�� DSKCAC EQU 0x00100000 ; �f�B�X�N�L���b�V���̏ꏊ DSKCAC0 EQU 0x00008000 ; �f�B�X�N�L���b�V���̏ꏊ�i���A�����[�h�j ; 有关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显卡,320*320*8位彩色,调色板模式。 MOV AH,0x00 INT 0x10 ;调用显卡bios的函数,切换显示模式。 MOV BYTE [VMODE],8 ; 将画面模式信息保存到这些内存地址中。 MOV WORD [SCRNX],320 MOV WORD [SCRNY],200 MOV DWORD [VRAM],0x000a0000 ;VRAM指的时显卡内存,也就是用来显示画面的内存。这一块内存地址都对应着画面上的像素。 ;VRAM在0xa0000~0xaffff之间的64kb。 VRAM分布在内存分布图中的好几个不同的地方。 ; 用bios取得键盘上各种led灯的状态。 MOV AH,0x02 INT 0x16 ; keyboard BIOS MOV [LEDS],AL ;=====================================后边的留以后再看,这块时调用bootpack.c程序的====================== ; 防止PIC接受所有中断 ; AT兼容机的规范、PIC初始化 ; 然后之前在CLI不做任何事就挂起 ; PIC在同意后初始化 MOV AL,0xff OUT 0x21,AL NOP ; ; 不断执行OUT指令 OUT 0xa1,AL CLI ;进一步中断CPU ; ; 让CPU支持1M以上内存、设置A20GATE CALL waitkbdout MOV AL,0xd1 OUT 0x64,AL CALL waitkbdout MOV AL,0xdf ; enable A20 OUT 0x60,AL CALL waitkbdout ; 保护模式转换 [INSTRSET "i486p"] ; 说明使用486指令 LGDT [GDTR0] ;设置临时GDT MOV EAX,CR0 AND EAX,0x7fffffff ; 使用bit31(禁用分页) OR EAX,0x00000001 ; bit0到1转换(保护模式过渡) MOV CR0,EAX JMP pipelineflush pipelineflush: MOV AX,1*8 ; 写32bit的段 MOV DS,AX MOV ES,AX MOV FS,AX MOV GS,AX MOV SS,AX ;bootpack传递 MOV ESI,bootpack ; 源 MOV EDI,BOTPAK ; 目标 MOV ECX,512*1024/4 CALL memcpy ; 传输磁盘数据 ; 从引导区开始 MOV ESI,0x7c00 ; 源 MOV EDI,DSKCAC ; 目标 MOV ECX,512/4 CALL memcpy ; 剩余的全部 MOV ESI,DSKCAC0+512 ; 源 MOV EDI,DSKCAC+512 ; 目标 MOV ECX,0 MOV CL,BYTE [CYLS] IMUL ECX,512*18*2/4 ; 除以4得到字节数 SUB ECX,512/4 ; IPL偏移量 CALL memcpy ; 由于还需要asmhead才能完成 ; 完成其余的bootpack任务 ; bootpack启动 MOV EBX,BOTPAK MOV ECX,[EBX+16] ADD ECX,3 ; ECX += 3; SHR ECX,2 ; ECX /= 4; 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 waitkbdout: IN AL,0x64 AND AL,0x02 JNZ waitkbdout ; AND结果不为0跳转到waitkbdout RET memcpy: MOV EAX,[ESI] ADD ESI,4 MOV [EDI],EAX ADD EDI,4 SUB ECX,1 JNZ memcpy ; 运算结果不为0跳转到memcpy RET ; memcpy地址前缀大小 ALIGNB 16 GDT0: RESB 8 ; 初始值 DW 0xffff,0x0000,0x9200,0x00cf ; 写32bit位段寄存器 DW 0xffff,0x0000,0x9a28,0x0047 ; 可执行的文件的32bit寄存器(bootpack用) DW 0 GDTR0: DW 8*3-1 DD GDT0 ALIGNB 16 bootpack:

3.加入bootpack.c操作系统代码

这个操作系统实现了一个最简单的功能:让cpu睡眠

void io_hlt(void); void HariMain(void) { fin: io_hlt(); /* 执行naskfunc.nas里边的_io_hlt()函数。 */ goto fin; }

4.加入naskfunc.nas

加入这个文件是因为c程序不能调用HLT指令,所以使用c程序调用汇编程序,在汇编程序中用HLT让cpu睡眠。

; naskfunc ; TAB=4 [FORMAT "WCOFF"] ; 制作目标文件的模式 [BITS 32] ; 制作32位模式用的机器语言 ; 制作目标文件的信息。 [FILE "naskfunc.nas"] ; 源文件名信息 GLOBAL _io_hlt ; 程序中包含的函数名 ;实际的函数 [SECTION .text] ;目标中间中写了这些之后再写程序 _io_hlt: ; void io_hlt(void); 这个函数只执行了一个HLT命令,让cpu睡眠。 HLT RET

5.运行结果

本文代码下载地址:https://download.csdn.net/download/u011164819/12981508

最新回复(0)