首页 > 其他分享 >【读书笔记-《30天自制操作系统》-2】Day3

【读书笔记-《30天自制操作系统》-2】Day3

时间:2024-08-14 23:22:38浏览次数:18  
标签:读取 读书笔记 18 30 Day3 MOV 扇区 柱面 AX

第三天的内容主要在于IPL的实现,在完成IPL之后又为进入32位模式与导入C语言做了准备。
在这里插入图片描述

1. 磁盘结构

IPL的作用是读取操作系统程序,所以IPL最主要的工作就是读取磁盘,将程序读取到内存中。先来看一下磁盘的结构与读取方式。
在这里插入图片描述
磁盘的样子也类似于这样一个圆柱体,实际读取数据时是将磁盘划分为一圈一圈,每一圈称为一个柱面,每个柱面又被平均划分为扇区。磁盘的正反面都可以存储数据,一个柱面的正反面可以通过两个磁头分别进行读取。
本书使用的是软盘,对于软盘来说,柱面的编号为0-79,共80个。而每一个柱面又被平均分为18个扇区,每个扇区的大小是512字节,这样一张磁盘的字节数
80x18x512x2 = 1474560
换算为KB则恰好是1440KB。

2. 磁盘读取程序

2.1 读取一个扇区

了解了磁盘的结构,继续来看读取磁盘的程序。

		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			; 驱动器号
		INT		0x13			; 调用BIOS
		JC		error

核心的代码其实很简单,也是要通过调用BIOS。这段程序做的主要是两件事:
(1)设置内存地址用于保存硬盘中读出的内容;
(2)设置读盘参数,调用BIOS读盘;
如果我们用一个寄存器来表示地址,由于只有16位,则最多也只能表示65536个地址,也就是64K。为了扩展寻址范围,上一篇提到的ES寄存器也就派上用场了。
这里使用ES:BX的方式来确定地址,即
ES*16 + BX
这样寻址范围就扩大到了1024K,也就是1M。对于现在至少也有几个G内存的电脑来说仍然是小的可怜。不过当时的设计者们谁也没料到计算机能有今天的发展,1M的内存在当时就已经很够用了。

前几条指令将ES设置为了0x0820,将BX设置为0,即将读取出的数据保存在内存中0x8200地址开始的地方。接下来则是设置读盘参数,分别指定柱面、磁头、扇区的编号,以及读取扇区的个数。对于多个磁盘,还需要指定驱动器号,这里默认为0。参数设置好之后,既可以通过INT 0x13指令调用BIOS进行读盘了。
最后出现了一条新指令JC error。JC是jump if carry的缩写,即如果进位标志被置位,则进行跳转,是用来检测读取是否出错的。如果读取出错,会将进位标志置位。
2.2 增加重试机制
软盘读取出错的概率比较高,因此这里还增加了重试的机制。这里用一个寄存器SI来记录读取出错的次数。JNC代表jump if not carry,即进位标志没有置位,此时跳转到fin,表示读取成功,继续下一步处理,否则将SI增加1。
JAE代表jump if above or equal,即如果SI >= 5,则跳转到error,表示重试此时超过5次之后停止重试并报错。否则执行
MOV AH, 0x00
MOV DL, 0x00
INT 0x13
来复位软盘状态,并跳转回retry重新读取。

		MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			
		MOV		DH,0			 
		MOV		CL,2			

		MOV		SI,0			 ; 记录失败次数的寄存器
retry:
		MOV		AH,0x02			 
		MOV		AL,1			 
		MOV		BX,0
		MOV		DL,0x00			
		INT		0x13			
		JNC		fin				;读取成功,跳转到fin
		ADD		SI,1				;读取失败,次数加1
		CMP		SI,5				;比较失败次数
		JAE		error			;失败次数>=5,跳转到error
		MOV		AH,0x00		;
		MOV		DL,0x00		;
		INT		0x13				;这三条指令为复位软盘状态
		JMP		retry			;跳转回retry,继续重试读盘

2.3 读取18个扇区与10个柱面
实现了读取一个扇区的程序,继续读取也就顺理成章了,接下来先增加了读取多个扇区的程序,在以上程序的基础上增加了next后的代码代码:

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			 
		MOV		BX,0
		MOV		DL,0x00			
		INT		0x13			
		JNC		fin				;读取成功,跳转到fin
		ADD		SI,1				;读取失败,次数加1
		CMP		SI,5				;比较失败次数
		JAE		error			;失败次数>=5,跳转到error
		MOV		AH,0x00		;
		MOV		DL,0x00		;
		INT		0x13				;这三条指令为复位软盘状态
		JMP		retry			;跳转回retry,继续重试读盘
next:
		MOV		AX,ES			 
		ADD		AX,0x0020		 
		MOV		ES,AX			;  通过以上三条指令将内存地址增加0x200,即后移512字节,为读取下一个扇区做准备
		ADD		CL,1				;	将CL增加1
		CMP		CL,18			;  比较CL与18,读取18个扇区
		JBE		readloop		;  未读满18个扇区,继续读取

新增的程序也比较简单,每成功读取一个扇区之后,将内存地址向后移动512字节,为准备读入的下一个扇区腾空间,并用CL存储成功读取的扇区数,未达到想要读取的数量(这里设置为18)则继续循环读取。
说句题外话,在C语言里面除非不得已是不用go to这种跳转指令的,以上的实现用for循环会很简单。但是编译成机器语言之后,也会像汇编语言这样有很多跳转吧。
这里为什么不一次读入多个扇区而是选择一个一个扇区循环读取呢?因为BIOS的0x13功能有限制,读取时不能跨过多个磁道,一次读取也不能超过64KB。这对于以上的读取其实是没有影响的,但如果读取的内容比较多,或者恰好在两个相邻的柱面上,就会有问题了。作者这里应该是为了简化,预先把这个问题规避掉了。
在此基础上就是读取多个柱面了。在此基础上可以形成一个读取10个柱面的启动区的代码:

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			 
		MOV		BX,0
		MOV		DL,0x00			
		INT		0x13			
		JNC		fin				;读取成功,跳转到fin
		ADD		SI,1				;读取失败,次数加1
		CMP		SI,5				;比较失败次数
		JAE		error			;失败次数>=5,跳转到error
		MOV		AH,0x00		;
		MOV		DL,0x00		;
		INT		0x13				;这三条指令为复位软盘状态
		JMP		retry			;跳转回retry,继续重试读盘
next:
		MOV		AX,ES			 
		ADD		AX,0x0020		 
		MOV		ES,AX			;  通过以上三条指令将内存地址增加0x200,即后移512字节,为读取下一个扇区做准备
		ADD		CL,1				;	将CL增加1
		CMP		CL,18			;  比较CL与18,读取18个扇区
		JBE		readloop		;  未读满18个扇区,继续读取
		MOV		CL,1
		ADD		DH,1
		CMP		DH,2
		JB		readloop		; DH < 2
		MOV		DH,0
		ADD		CH,1
		CMP		CH,CYLS
		JB		readloop		; CH < CYLS

读完18个扇区之后,将CL重置为1,然后根据DH来判断是否完成一个柱面的读取。DH中存放的是磁头编号,0和1分别代表一个柱面的正反面。读完正面的18个扇区之后,将DH增加1,继续读取反面的数据。JB表示jump if below,如果DH达到了2,说明这一柱面的正反面都已读取完毕,可以继续读取下一个柱面。CH中存放的是柱面号,这只需将CH加1里去,即从下一个柱面开始读取。最终比较CH是否达到定义的柱面数CYLS即可。

这样启动区就完成了。

3. 进入32位模式前的准备工作
后续需要进入功能更为强大的32位模式,但32位模式与16位模式使用的是不同的机器语言,进入32位模式之后,以上16位的功能,包括BIOS都无法继续使用,因此在进入32位模式之前需要把通过BIOS进行的设置都完成。新增加的代码如下:

; 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

这里主要做了两件事。
(1)设置画面显示模式。通过INT 10调用BIOS,参数为AH = 0x00, AL = 0x13时,对应的是VGA图形模式,320 x 200 x 8位彩色模式,调色板模式。
(2)获取键盘上的指示灯状态。
在获取完以上信息之后,还将这些信息保存了起来。
VRAM指的是显卡内存,这一块内存用来在画面上显示图案。在上面这种画面模式下,对应的内存地址为0xa000-0xaffff,共64KB.

4. 导入C语言

完成以上准备工作之后,就可以切换到32位模式并导入C语言了。为了调用C语言,作者还写了大量的汇编代码,又怕读者当前无法理解,于是放在后面去讲解了,那么这一部分就在后面的内容讲到时再进行整理。这里讲的关于C语言的内容其实主要是在一个main函数中调用汇编语言函数,算是先开个头。
首先是汇编语言程序,用于实现HLT:

; naskfunc
; TAB=4

[FORMAT "WCOFF"]				; 制作目标文件的模式	
[BITS 32]						; 制作32位模式用的机器语言

;制作目标文件的信息

[FILE "naskfunc.nas"]			; 源文件名信息
		GLOBAL	_io_hlt			; 程序中包含的函数名


; 以下是实际的函数

[SECTION .text]		;目标文件中写了这些之后再写程序
_io_hlt:	; void io_hlt(void);
		HLT
		RET

对于汇编语言编译成的目标文件,需要链接到C语言编译成的目标文件,才能形成可执行文件。需要注意几点,文件中需要先声明源文件名的信息,再声明下面的函数名。需要链接的函数名前面需要加上"_"与GLOBAL编号。这样在C文件中可以这样调用:


void io_hlt(void);


void HariMain(void)
{

fin:
	io_hlt(); /*执行naskfunc.nas中的_io_hlt函数 */
	goto fin;

}

了解了如何在C语言中调用汇编语言的函数,在第四天的内容中作者将使用C语言实现画面显示,敬请期待。

标签:读取,读书笔记,18,30,Day3,MOV,扇区,柱面,AX
From: https://blog.csdn.net/Ocean1994/article/details/141112786

相关文章

  • CF1530D Secret Santa 题解
    ProblemSolution每个人初始不会给自己送礼物。如果每人要送礼的人都不一样,答案即为\(n\)。如果有两个或以上的人要送给同一个人礼物,假设有\(x\)个人要给同一个人送礼物,那么必有\(x-1\)个人要更改送礼的人,并将礼物送个\(x-1\)个没有礼物收的人。然而这样送礼物可能会导......
  • CF830E Perpetual Motion Machine
    一堆CornnerCase的大分讨,我们全队一边写一边补情况,WA了五发终于干过去了首先当图中存在某个环时,我们只要给环上的所有点赋值为\(1\)即可;又因为图连通所以只要考虑树的情况即可考虑如果存在一个度数\(\ge4\)的点,将其赋值为\(2\)并将其周围的四个点赋值为\(1\)即可......
  • 图像识别,训练数据集---GPU篇 3090免费使用
     大家一般都在window上训练yolo等一些深度学习模型,会发现很慢或者说GPU显存不够用而云GPU平台则解决了一系列该问题深度学习目标检测交流群1:985499650yolo2D3D目标检测行为识别交流群2:782537412接下来以该平台为例---有一些免费的3090,4090可以用(24G):https://cloud.lanyun.n......
  • 0230-TCP 发送和接收
    环境Time2022-11-25WSL-Ubuntu22.04Rust1.65.0前言说明参考:https://doc.rust-lang.org/std/net/struct.TcpListener.html目标之前认识了TCP报文头,这里去除报文头的细节,直接通信。main.rsTCP由标准库直接支持,可以直接使用。连接上去后,服务器返回一个hello。us......
  • 附038.Kubernetes_v1.30.3高可用部署架构二
    部署组件该Kubernetes部署过程中,对于部署环节,涉及多个组件,主要有kubeadm、kubelet、kubectl。kubeadm介绍Kubeadm为构建Kubernetes提供了便捷、高效的“最佳实践”,该工具提供了初始化完整Kubernetes过程所需的组件,其主要命令及功能有:kubeadminit:用于搭建Kuberne......
  • Cisco Firepower 9300 Series FTD Software 7.4.2 & ASA Software 9.20.3 发布下载 -
    CiscoFirepower9300SeriesFTDSoftware7.4.2&ASASoftware9.20.3发布下载-思科防火墙系统软件FirepowerThreatDefense(FTD)Software请访问原文链接:https://sysin.org/blog/cisco-firepower-9300/,查看最新版。原创作品,转载请保留出处。为什么选择CiscoSecure......
  • vos3000 8.0安装注册机
    下载得文件解压后将压缩中得vos30002180.tar.gz上传至root目录进入系统得root目录下面,使用下面得命令解压tarzxvfvos30002180.tar.gz进入解压后的目录cdvos30002180删除原来的执行文件shred-uinstall.sh然后将用压缩包中得install.sh替换掉原来得install.sh给vos3......
  • CentOS 7 停服后(2024-06-30)升级最新的Linux 内核
     1、CentOS7更新 USTC的源sudosed-i.bak\-e's|^mirrorlist=|#mirrorlist=|g'\-e's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g'\/etc/yum.repos.d/CentOS-Base.repo 2......
  • 齐聚深中通道∣全国300余专家现场体验三思“黑科技”
    为深入贯彻落实中共中央、国务院决策部署,推进高速公路隧道数字化转型、智慧化升级,探讨新技术在高速公路隧道建设与运营中的应用,有效提升隧道的安全通行和管控能力。8月9-10日,由中国公路学会和深中通道管理中心指导、《中国公路》杂志社主办、北京交科公路勘察设计研究院、中铁......
  • HDU-ACM 2024 Day3
    T1004游戏(HDU7460)注意到对于两个人,他们\(t\)轮后能力值相同的概率只与他们初始时的能力差有关,所以我们先\(\text{FFT}\)求出\(|a_i-a_j|=k\)的\((i,j)\)对数。构造多项式\(F(x)=(p_1x^2+p_2+p_3x)\),其中\(p_1,p_2,p_3\),分别表示在一轮中两个人相对......