挑战30天写操作系统-day3-进入32位模式并导入C语言
目录

1.制作真正的IPL
IPL:启动区,启动程序装载器
完整代码:
; 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
这里有个JC指令,是有条件跳转指令
注意到上面有个INT 0x13,这是调用BIOS的0x13号函数
常用的有:


这个进位标志位是存储在标志寄存器里面。
为了明白今天新加的代码,需要了解磁盘的工作方式
一定记得,磁盘他是实体的,是三维立体的物体,这样就好理解了
IPL启动区就在C0-H0-S1(柱面0-磁头0-扇区1)
接下来,为了介绍缓冲区(cache)的概念,需要再次复习一下,CPU寻址方式:段基址+段内地址
上面的说法比较老,最新的指令格式是AT&T格式,但是,基本思想是一致的,可百度。
这次的运行结果将会一闪而过,大家获取源码之后可以自行体验。
这里有一个报错:
解决方式如下:
16位不兼容解决方案
最好重启一下!!!!!!!!!!
运行结果解释漆黑一片:
2.试错
软盘不可靠,读取失败的情况下,需要多次读取,考虑极端情况,需要限制读取次数

这里两个新的指令:
JNC:进位标志为0时跳转
JAE:大于等于时跳转
INT 0x13,功能是系统复位
3.读到18扇区


新的指令:
JBE:小于等于跳转
4.读入10个柱面

代码如下:
; 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
新加指令:
JB:如果小于,跳转
EQU:等价与#define

5.着手开发操作系统
上面已经完成了启动区的制作
下面,写一个最简单的操作系统






6.从启动区执行操作系统


7.确认操作系统的执行情况




全黑画面:
8.32位模式前期准备
接下来就要以C语言作为开发语言,汇编语言暂时不用了。
32位模式就不能调用BIOS函数了(16位编写的)
所以需要使用BIOS的地方,我们需要首先放到前面去加载,在asmhead.nas文件中
; haribote-os boot asm
; TAB=4
BOTPAK 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显卡,320x200x8bit
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 屏幕的模式(参考C语言的引用)
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 通过BIOS获取指示灯状态
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
; 防止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:


9.开始导入C语言
bootpack.c
/* 告诉C编译器,有一个函数在别的文件里 */
void io_hlt(void);
/* 是函数声明却不用{},而用;,这表示的意思是:
函数在别的文件中,你自己找一下 */
void HariMain(void)
{
fin:
io_hlt(); /* 执行naskfunc.nas中的_io_hlt函数 */
goto fin;
}
这里goto最终将会被编译成JMP指令
函数io_hlt()在naskfunc.nas中
; naskfunc
; TAB=4
[FORMAT "WCOFF"] ; 制作目标文件的模式
[BITS 32] ; 制作32位模式用的机器语言
; 制作目标文件的信息
[FILE "naskfunc.nas"] ; 源文件名信息
GLOBAL _io_hlt ; 程序中包含的函数名
; 以下是实际的函数
[SECTION .text] ; 目标文件中写了这些后再写程序
_io_hlt: ; void io_hlt(void);
HLT
RET

下面讲解汇编语言做目标文件的方法
10.实现HLT(harib00j)


RET:等价与return
11.微信关注公众号获取源码和电子书籍

挑战30天写操作系统-day3-进入32位模式并导入C语言的更多相关文章
- 30天自制操作系统(三)进入32位模式并导入C语言
1 制作真正的IPL IPL(Initial Program Loader),启动程序装载器,但是之前并没有实质性的装载任何程序,这次作者要开始装载程序了. 虽然现在开发的操作系统啥功能也没有,作者说 ...
- 挑战30天写操作系统-day4-C语言与画面显示的练习
目录 获取源码关注公众号<猿小龙> 1.用C语言实现内存写入(harib01a) C语言中如果使用了write_mem8函数,就会跳转到_write_mem8,此时参数指定的数字就存放在内 ...
- 挑战30天写操作系统-day1-从计算机结构到汇编程序入门
先动手操作 软盘映像文件制作:先采用二进制编辑器编辑我们所需要的映像文件helloos.img 二进制编辑器下载链接:Bz - c.mos (vcraft.jp) 制作好之后,可以选择写入软盘,通过软 ...
- 挑战30天写操作系统-day2-汇编语言学习与Makefile入门
1.介绍文本编辑器 这里,我们直接采用自己windows电脑自带的文本编辑器即可以完成制作要求 2.继续开发 下面先是对昨天使用的helloos.nas文件内容进行详细解释 ; hello-os ; ...
- 《30天自制操作系统》笔记(06)——CPU的32位模式
<30天自制操作系统>笔记(06)——CPU的32位模式 进度回顾 上一篇中实现了启用鼠标.键盘的功能.屏幕上会显示出用户按键.点击鼠标的情况.这是通过设置硬件的中断函数实现的,可以说硬件 ...
- 30天自制操作系统-day3
30天自制操作系统-day3 前2天我们分别使用了直接使用二进制编辑器和简单的汇编指令生成了img文件,今天我们尝试一下使用稍微复杂一点的汇编指令 os.asm文件内容如下: ; hello-os ; ...
- 微软的操作系统中让 32 位支持大于 4GB 的内存。
先给一个参考文献:The RAM reported by the System Properties dialog box and the System Information tool is les ...
- 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16
一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...
- 呃,如何使 .NET 程序,在 64位 系统 中,以 32位 模式运行。
其实最简单的方法就是在解决方案中,把平台设为 x86 就好了哈~ 但是今天遇到一个第三方的软件,它调用的一个 dll 是 32位 的,可能它没有测试过在 64位 系统下运行的情况,它在编译时是按默 ...
随机推荐
- Nessus简单介绍与安装
1.Nessus简单介绍与安装 1.Nessus简介 Nessus号称是世界上最流行的漏洞扫描程序,全世界有超过75000个组织在使用它.该工具提供完整的电脑漏洞扫描服务,并随时更新其漏洞数据库.Ne ...
- 灵感乍现!造了个与众不同的Dubbo注册中心扩展轮子
hello大家好呀,我是小楼. 作为一名基础组件开发,服务好每一位业务开发同学是我们的义务(KPI). 客服群里经常有业务开发同学丢来一段代码.一个报错,而我们,当然要微笑服务,耐心解答. 有的问题, ...
- 《Streaming Systems》第二章: 数据处理中的 What, Where, When, How
本章中,我们将通过对 What,Where,When,How 这 4 个问题的回答,逐步揭开流处理过程的全貌. What:计算什么结果? 也就是我们进行数据处理的目的,答案是转换(transforma ...
- 视网膜血管分割代码(Pytorch实现)
创建日期: 2021-12-24 17:00:00 update log(2021.12.24):B站视频删除了,回放看了一下,讲的不太行......2333,时间过得真快,转眼就是2022年了啊 2 ...
- Spring Ioc源码分析系列--前言
Spring Ioc源码分析系列--前言 为什么要写这个系列文章 首先这是我个人很久之前的一个计划,拖了很久没有实施,现在算是填坑了.其次,作为一个Java开发者,Spring是绕不开的课题.在Spr ...
- gol处理命令行参数 flag
os.Args获取命令行参数 os.Args是一个srting的切片,用来存储所有的命令行参数 package main import ( "fmt" "os" ...
- leetcode704二分查找
title: 二分查找 题目描述 题目链接:二分查找 解题思路 二分模板默写 int search(vector<int>& nums, int target) { int lef ...
- 中间件漏洞之IIS
IIS中间件漏洞 我们常见的中间件有IIS.Apache.Nginx,其中IIS中间件有什么漏洞呢? IIS 短文件名漏洞: 漏洞产生的原因是为了兼容MS-DOS程序,windows为文件名较长的文件 ...
- 【mq】从零开始实现 mq-07-负载均衡 load balance
前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ...
- 羽夏 MakeFile 简明教程
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.该文章根据 GNU Make Manual 进行汉化处理并作出自己的整理,一是我对 Make 的学习记录,二是对大家学习 MakeF ...