《操作系统30天》-合川秀实-学习日志day3

制作真正的IPL:相关知识

关键代码:

  • MOV AX,0x0820
  • MOV ES,AX
  • MOV CH,0 ; 柱面0
  • MOV DH,0 ;磁头0
  • MOV CL,2 ; 扇区2
  • MOV AH,0x02 ; AH=0x02 : 读盘
  • MOV AL,1 ; 1个扇区
  • MOV BX,0
  • MOV DL,0x00 ; A驱动器
  • INT 0x13 ;调用磁盘BIOS
  • JC error

新指令JC:“jump if carry”意思是如果进位标志(carry flag)是1的话,就跳转。

INT  0x13表示调用BIOS的0x13号函数,根据BIOS网页:

  1. 磁盘读、写,扇区校验(verify),以及寻道(seek)
  • AH=0x02;(读盘) ---这次用的是这个功能
  • AH=0x03;(写盘)
  • AH=0x04;(校验)
  • AH=0x0c;(寻道)
  • AL=处理对象的扇区数;(只能同时处理连续的扇区)
  • CH=柱面号 & 0xff;
  • CL=扇区号(0-5位)|(柱面号&0x300)>>2;
  • DH=磁头号;
  • DL=驱动器号;
  • ES:BX=缓冲地址;(校验及寻道时不使用)

返回值:

FLACS:CF==0: 没有错误,AH==0

FLAGS.GF==1: 有错误,错误号码存入AH内(与重置(reset)功能一样)

进位标志:FLAGS.CF,调用此函数之后,如果没错,进位标志是0,如果有错,进位标志就是1。(本次是用来报告BIOS函数调用是否有错)

进位标志是一个只能存储1位信息的寄存器,除此之外CPU还有其他几个只有1位的寄存器,这种1位寄存器我们称为标志。

软盘磁盘:

 

在有多个软盘驱动器时,用磁盘驱动器号来指定从哪个驱动器的软盘上读取数据。现在的电脑基本都是只有1个软盘驱动器,以前一般是2个,只有一个就指定0号。软盘一共有80个柱面,有正反两个磁头分别是0号、1号(都可以记录数据)。在磁盘这个圆环上均匀等分了18个扇区,分别称扇区1,扇区2.......综上,一张软盘有80个柱面,2个磁头,18个扇区,且一个扇区有512个字节,所以一张软盘的容量是80×2×18×512=1474560Byte=1440KB

含有IPL的启动区位于C0-H0-SI(柱面0,磁头0,扇区1的缩写),下一个扇区是C0-H0-S2(本次要装载的扇区)。

缓冲区地址:这是一个内存地址,表明我们要把从软盘上读出的数据装载到内存的哪个位置。在设计BIOS时,CPU没有32位的寄存器,只设计了一个起辅助作用的段寄存器,在指定内存地址的时候可用这个段寄存器。

以ES:BX这种方式表示内存地址,写成“MOV  AL,[ES:BX]”,表示ES×16+BX的内存地址。可理解为先用ES指定一大块区域,再用BX指定一个具体地址。这种方法可以使用1M以内的内存。我们指定的是ES=0x0820,BX=0,因为0x8000-0x81ff这512个字节是留给启动区的。我们在使用内存地址时,都要同时指定段寄存器,如果省略就是默认”DS:”作为默认的段寄存器。“MOV AL,[SI]”=“MOV AL,[DS:SI]”。(注意:段寄存器要预先初始化为0,否则地址的值要加上这个数的16倍),下面运行查看结果:

试错:重点总结、关键代码及注释

因为有时候软盘不能读数据,需要多度几次,所以加入部分代码(读5次)

; 读磁盘

		MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			; 柱面0
		MOV		DH,0		; 磁头0
		MOV		CL,2			; 扇区2

		MOV		SI,0			; 记录失败次数的寄存器
retry:
		MOV		AH,0x02		; AH=0x02 : 读入磁盘
		MOV		AL,1			; 1个扇区
		MOV		BX,0
		MOV		DL,0x00		; A驱动器
		INT		0x13				; 调用磁盘BIOS
		JNC		fin				; 没出错的话跳转到fin
		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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

JNC是一个条件跳转指令:“Jump if not carry” 意思是进位标志是0的话就跳转;

JAE也是条件跳转:“Jump if above or equal” 意思是大于或等于时跳转。

重新读盘之前,需要做一次“系统复位”AH=0x00,DL=0x00,INT 0x13复位软盘状态,再读一次。

读到18扇区(harib00c)

往后多读几个扇区:

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

ADD AX,0x0020

MOV ES,AX ; 因为没有ADD ES,0x020 指令,所以这里绕个

ADD CL,1 ; 往CL里+1

CMP CL,18 ; 比较CL与18

JBE readloop ; 如果CL <= 18 跳转到readloop

出现的新指令:

JBE条件跳转指令:“jump if below or equal”表示小于等于时跳转。

新增内容:读下一个扇区,给CL+1,给ES+0x20 (512÷16的十六进制),或者给BX+512;最后是一个循环,表示循环读取2-18个扇区,这里可以直接设置AL的值为17,但是作者提到BIOS的“补充说明部分”:指定处理的扇区数,范围在0x01-0xff(指定0x02以上的数值时,要特别注意能够连续处理多个扇区的条件。如果是FD的话,似乎不能跨越多个磁道,也不能超过64KB的界限。)作者的解释是循序渐进,用循环较好,因为后面的程序如果这样写会出问题。

到了这一步我们已经把磁盘上C0-H--S2到C0-H0-S18的512x17=8704个字节的内容装载到内存的0x8200-0xa3ff处。

 

读入10个柱面(harib00d)

新增内容:

; 读磁盘

 

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

ADD AX,0x0020

MOV ES,AX ; 因为没有ADD ES,0x020 指令,所以这里绕个

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

新指令:

JB条件跳转指令:“jump if below”如果小于就跳转

EQU开头的新指令(CYLS EQU 10 ):“equal”相当于C语言中的#define命令,常用来声明常数。“CYLS EQU 10”的意思就是 CYLS=10。

到了这一步我们已经把软盘最初的10×2×18×512=184320byte=180KB内容完整得装到内存中,运行“make install”运行需要一定的时间,画面没有什么变化,但是这个程序已经用从软盘读出的数据填满了内存“0x08200-0x34fff”的地方。

在这里卡了很久:

弹出一个窗口提醒(因为我没有装到磁盘):说明我的电脑跟这个操作系统不兼容,但是会生成相关的文件.img,ipl.bin,ipllst。

着手开发操作系统

将下面这段程序保存为haribote.nas,用nask编译,输出成hanbote.sys,再保存到磁盘映像haribote.img里,程序里面就让它HLT。

Fin:

HLT

JMP fin

因为这一步用到软盘磁盘和兼容的Windows而我电脑不匹配也没有软盘,所以我没有具体操作,还是用之前的BZ工具来查看作者已经映像好的haribote.img文件。先复制harib00e文件到tolset文件夹中,打开命令输入make img指令做个映像文件,生成haribote.img,用二进制编辑器查看:

 

打开haribote.sys文件,恰好是三个字节

作者总结为:一般向一个空软盘保存文件时,①文件名会写在0x002600以后的地方;②文件的内容会写在0x004200的地方。

下面我们将操作系统本身的内容写到名为haribote.sys文件中,再保存到磁盘映像里,从启动区执行这个haribote.sys就行了。

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

从启动区开始,把磁盘上的内容装载到内存0x8000号地址,所以磁盘0x4200处的内容位于内存0x8000+0x4200=0xc200号地址。往haribote.nas里加上ORG 0xc200,然后在ipl.nas处理的最后加上JMP 0xc200这个指令。最后make run一下

确认操作系统的执行情况

本次的haribote.nas:

; haribote-os

; TAB=4

 

ORG 0xc200 ; 这个程序将要被装载到内存的什么地方

MOV AL,0x13 ; VGA显卡、320x200x8位彩色

MOV AH,0x00

INT 0x10

fin:

HLT

JMP fin

为什么ORG设为0xc200: 执行磁盘映像上位于0x004200号地址的程序,从启动区开始把磁盘内容装载到0x8000号地址所以0x4200处内容应该位于内存0x8000+0x4200=0xc200号地址。

设定AH=0x00,调用显卡BIOS的函数,可以切换显示模式:

  1. AH=0x00;
  2. AL=模式:(省略了一些不重要的画面模式)
  1. 0x13: 16色字符模式,80x25
  2. 0x12: VGA图形模式,640x480x4位彩色模式,独特的4面存储模式
  3. 0x13: VGA图形模式,320x200x8位彩色模式,调色板模式
  4. 0x6a: 扩展VGA图形模式,800x600x4位彩色模式,独特的4面存储模式(有的显 卡不支持这个模式)
  1. 返回值:无

这次选择的是0x13的画面模式,8位彩色模式可以用256种颜色,如果画面模式切换正常,画面应该是一片漆黑。图形模式光标会消失:

其他地方的修改:

  1. ipl.nas->ipl10.nas(说明这个程序只能读入10个柱面)
  2. 在JMP 0xc200之前加入一行命令,把cyls的值写入内存地址0x0ff0中,把磁盘装载内容的结束地址告诉给haribote.sys
  3. 现在这一步为止,我们把启动区里与haribote.sys没有关系的前后部分也读进来了。
  1. 32位模式前期准备

32位CPU模式优势:使用32位寄存器较16位CPU模式方便;可使用内存容量远远大于1MB;CPU的自我保护功能在32位下能用。

用32位模式就不能调用BIOS功能,因为BIOS是用16位机器语言写的,如果要用,就全部都放在开头先做。修改之后的haribote.nas:

; haribote-os

; TAB=4

 

; 有关BOOT_INFO

CYLS EQU 0x0ff0 ; 设定启动区

LEDS EQU 0x0ff1

VMODE EQU 0x0ff2 ; 关于颜色数目的信息。颜色的位数

SCRNX EQU 0x0ff4 ; 分辨率的X (screen X)

SCRNY EQU 0x0ff6 ; 分辨率的Y  (screen Y)

VRAM EQU 0x0ff8 ; 图像缓冲区的开始地址

 

ORG 0xc200 ; 这个程序将要被装载到内存的什么地方

MOV AL,0x13 ; VGA显卡、320x200x8位彩色

MOV AH,0x00

INT 0x10

MOV BYTE [VMODE],8 ; 记录画面模式

MOV WORD [SCRNX],320

MOV WORD [SCRNY],200

MOV DWORD [VRAM],0x000a0000

 

; 用BIOS取得键盘上各种LED指示灯的状态

 

MOV AH,0x02

INT 0x16 ; keyboard BIOS

MOV [LEDS],AL

 

fin:

HLT

JMP fin

设置画面模式之后,把画面模式保存在内存里,暂时将启动时的信息称为BOOT_INFO。[VRAM]里保存的是0xa0000,VRam指的是显卡内存,用来显示画面的内存,里面的各个地址对应着画面上的像素,参考(AT)BIOS支持网页在INT 0x10的说明最后写“VRam是0xa0000~0xaffff的64KB”,INT 0x16是记录LED灯的状态,我们把画面的像素数、颜色数、以及从BIOS取得的键盘信息都保存起来,位置在内存的0x0ff0附近。

开始导入C语言

修改内容:haribote.sys:前半部分是汇编语言,后半部分是C语言。Haribote.nas->asmhead.nas,为了调用C语言的程序,添加了100行左右的汇编代码。

C语言部分(bootpack.c):

void HariMain(void)

{

 

fin:

/*C语言中不能用HLT */

goto fin;

 

}

Goto指令相当于JMP指令。

Bootpack.c变成机器语言的过程:

首先,使用ccl.exe从bootpack.c生成bootpack.gas.

第二步,使用Gas2nask.exe从bootpack.gas生成 bootpack.nas.

第三步,使用Nask.exe从bootpack.nas生成bootpack.obj.

第四步,使用Obi2bim.exe从bootpack.obj生成bootpack.bim.

最后,使用Bim2hrb.exe从bootpack.bim生成bootpack.hrb.

这样就做成了机器语言,再使用copy指令将asmhead.bin与bootpack.hrb单纯结合到起来,就成了haribote.sys。

具体Makefile:

 

实现HLT(harib00j)

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

新指令:RET:相当于C中的return

用汇编写的函数,之后还要与bootpack.obj链接,所以也要编译成目标文件。在nask目标文件的模式下,必须设定文件名信息,然后再写明下面程序的函数名,注意要在函数名的前面加上“_”,否则就不能很好地与C语言函数链接,需要链接的函数名都要用GLOBAL指令声明,如上代码所示,先写一个与用GLOBAL声明的函数名相同的标号(label)再从此往下写代码。

Bootpack.c:

/* 告诉C编译器,有一个函数在别的文件里 */

 

void io_hlt(void);

 

/* 是函数声明却不用{ },而用;,这表示的意思是:函数是在别的文件中,你自己找一下 */

void HariMain(void)

{

fin:

io_hlt(); /* 执行naskfunc.nas里的_io_hlt */

goto fin;

}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值