自制操作系统day4(c指针)(指针和数组)(CLI和STI)(EFLAGS)
day4
关于c指针的理解(结合汇编)
int i;
char *p;
p=(char *) i;
*p=i &0x0f;
p=(char *) i;
*p=i &0x0f;MOV ECX,i
MOV BYTE [ECX],(i &0x0f)
如果一开始不给p赋值,p的内存地址不确定,也就是这个ECX里的值不确定,
就导致指向了一个不确定的地址空间,即*P里的值不确定
上面的char *p;其实定义的只有一个p变量,p就是内存地址的值,p是地址变量
*p就是内存里p的地址空间里面的内容,相当于[p]
但是如果一开始没有声明这个 p,汇编就不知道这个p地址是什么类型(BYTE,WORD,DWORD),也就是不知道p这个这个开始的内存地址要往后用几个内存单元(多大的内存)
p=(char *) i;
*p=i &0x0f;相当于
*((char *)i)=i&0x0f;
指针和数组
p = (char *) 0xa0000; /*将地址赋值进去*/
for (i = 0; i <= 0xffff; i++) {
p[i] = i & 0x0f;
}
其实:p[i]和*(p+i)是一样的
所以p[0]和p是一样的又因为*(p+i)和*(i+p)是一样的
所以:p[i]和i[p]是一样的所以本质上和数组没有关系,p[i]和i[p]仅仅是一种省略写法
harib01f
调色板和显示的相关代码解释
在 bootpack.c 中,调色板数据存储在内存中的位置和 p 地址的作用如下:
- 调色板内存位置 :
- 调色板数据定义在 init_palette() 函数中,是一个静态数组 table_rgb
- 这个数组包含16种颜色,每种颜色由3个字节(RGB)组成,共48字节
- 通过 set_palette() 函数,这些颜色值被写入VGA调色板寄存器(端口0x03c8/0x03c9)
- p地址(0xa0000)的作用 :
- p = (char *) 0xa0000 这个地址是VGA显卡的显存起始地址
- 在VGA模式13h(320x200 256色)下,这个区域对应屏幕像素数据
- 代码中 p[i] = i & 0x0f 的作用是:
- 向显存写入测试图案(0-15的循环)
- i & 0x0f 确保只使用前16种颜色(0-15)
- 这样可以在屏幕上看到调色板设置的效果
- 整体流程 :
- 先初始化调色板( init_palette )
- 然后向显存写入测试图案
- 最后进入无限循环保持屏幕显示
这个代码是操作系统开发中常见的VGA图形初始化过程,通过直接操作硬件端口和显存来实现图形显示。
向显存写入测试图案(0-15的循环)
- i & 0x0f 确保只使用前16种颜色(0-15这个写入的图案和颜色是调色板的吗
是的,这段代码中的显存写入操作与调色板设置是直接关联的:
- 调色板关系 :
- i & 0x0f 确保只使用0-15这16种颜色索引,正好对应 init_palette() 中定义的16种颜色
- 每个像素值(p[i])实际上是一个调色板索引,不是直接的颜色值
- 写入图案 :
- 代码向显存连续写入0x0000-0xffff的索引值
- 由于 & 0x0f 操作,实际写入的是0,1,2,…,15,0,1,…的循环序列
- 这样会在屏幕上产生16种颜色的条纹图案
- 显示原理 :
- VGA硬件会根据显存中的索引值(0-15)
- 查找调色板寄存器中对应的RGB颜色值
- 最终显示出对应的颜色
- 颜色值处理 :
- 注意 set_palette() 中颜色值都除以4(右移2位)
- 这是因为VGA调色板寄存器只接受6位颜色值(0-63)
- 而代码中使用的是8位颜色值(0-255),所以需要缩放
这种设计是早期VGA图形模式的典型用法,通过调色板索引+显存写入来实现高效的颜色显示。
问题:这些颜色值被写入VGA调色板寄存器(端口0x03c8/0x03c9)颜色不是存在这里吗,0x0000-0xffff的索引值不在这里面呀
问题涉及VGA显示架构的几个关键概念,来澄清一下:
-
调色板寄存器(0x03c8/0x03c9) :
- 这里存储的是RGB颜色值(实际硬件存储的是6位值,0-63)
- 每个索引对应一组RGB值(如索引0=黑,1=亮红等)
- 代码中 set_palette() 函数正是通过OUT指令写入这些端口
-
显存(0xa0000) :
- 这里存储的是像素的调色板索引值(0-15)
- 每个字节代表一个像素点的颜色索引
- 硬件会自动根据索引值查找调色板寄存器中的实际颜色
-
颜色显示流程 :
显存(0xa0000)[索引值] → VGA控制器 → 调色板寄存器[RGB值] → 显示器
- 例如:显存某字节是5 → 查找调色板索引5 → 输出(0xff,0x00,0xff)/4的RGB
-
0x0000-0xffff的意义 :
- 这是显存地址范围(0xa0000-0xaffff)
- 每个地址存储1字节索引值,共65536像素(对应320x200分辨率)
- i & 0x0f 保证索引值不超过15(只用前16种颜色)
关键区别:调色板寄存器存储颜色定义,显存存储颜色索引。两者通过硬件自动关联。
如果我们读一读程序的话,就会发现突然蹦出了0x03c8、0x03c9之类的设备号码,
这些设备号码到底是如何获得的呢?随意写几个数字行不行呢?这些号码当然不是
能随便乱写的。否则,别的什么设备胡乱动作一下,会带来很严重的问题。所以事
先必须仔细调查。笔者的参考网页如下:
http://community.osdev.info/?VGA
网页的叙述太长了,不好意思(注:这一页也是笔者写的)。网页正中间那里,有
一个项目,叫做“video DA converter”,其中有以下记述。
调色板的访问步骤。
首先在一连串的访问中屏蔽中断(比如CLI)。
将想要设定的调色板号码写入0x03c8,紧接着,按R,G,B的顺序写入
0x03c9。如果还想继续设定下一个调色板,则省略调色板号码,再按照RGB的
顺序写入0x03c9就行了。
如果想要读出当前调色板的状态,首先要将调色板的号码写入0x03c7,再从
0x03c9读取3次。读出的顺序就是R,G,B。如果要继续读出下一个调色板,同
样也是省略调色板号码的设定,按RGB的顺序读出。
CLI和STI
首先是CLI和STI。所谓CLI,是将中断标志(interrupt flag)置为0的指令(clear
interrupt flag)。STI是要将这个中断标志置为1的指令(set interrupt flag)。而标
志,是指像以前曾出现过的进位标志一样的各种标志,也就是说在CPU中有多种多
样的标志。更改中断标志有什么好处呢?正如其名所示,它与CPU的中断处理有关
系。当CPU遇到中断请求时,是立即处理中断请求(中断标志为1),还是忽略中
断请求(中断标志为0),就由这个中断标志位来设定。
EFLAGS
下面再来介绍一下EFLAGS这一特别的寄存器。这是由名为FLAGS的16位寄存器扩
展而来的32位寄存器。FLAGS是存储进位标志和中断标志等标志的寄存器。进位标
志可以通过JC或JNC等跳转指令来简单地判断到底是0还是1。但对于中断标志,没
有类似的JI或JNI命令,所以只能读入EFLAGS,再检查第9位是0还是1。顺便说一
下,进位标志是EFLAGS的第0位。
空白位没有特殊意义(或许留给将来的CPU用?)
标志寄存器用来保存在一条指令执行完成后,CPU所处状态的信息及运算结果的特征。
16位标志寄存器 FLAGS
32位标志寄存器 EFLAGS
SF:若运算结果为负(即结果的最高位为1,因为机器内的数据都是以补码形式表示)则SF=1,否则SF=0
ZF:若运算结果为0,则ZF=1,否则ZF=0
OF:当将操作数作为有符号数看时,用该标志位判断运算结果是否溢出
加法:若同符号数相加,而结果符号与之相反,则OF=1.否则OF=0减法:被减数与减数异号,而结果的符号与减数相同则OF=1,否则OF=0
(比如5-(-3)的话,结果不应该与减数相同)
CF:将操作数作为无符号数时,用该标志位判断运算结果是否发生了进位或错位。
加法:若运算结果从字或字节的最高位向前产生了进位则CF=1,否则CF=0。减法:两数相减,若够减无借位,则CF=0,否则CF=1
PF:当运算结果( 只考虑低8位)中1的个数为偶数时,PF=1,否则PF=0。此标志位主要用于检测数据在传输过程中的错误
AF:标识做字节运算时低办字节向高半字节的进位和借位
DF:控制串操作指令的处理方向
DF=0,从低地址向高地址,即每次操作后SI、DI递增DF=1,从高地址向低地址,即每次操作后SI、DI递减
IF:控制CPU是否允许响应外设的中断请求
IF=0,关中断,CPU屏蔽外设的中断请求IF=1,开中断,CPU响应外设的中断请求
TF(Trap Flag):控制单步执行
TF=0,CPU连续工作TF=1,CPU单步执行
标志位操作指令
LAHF
功能:将标志寄存器的低8位—>AH
说明:无操作数,也不影响标志位
SAHF
功能:AH -à标志寄存器低8位
说明:标志寄存器原来低8位被冲掉,以AH中的内容取代。该指令不影响高字节内容,因为高字节主要是DF、IF、TF等控制标志,不可以随便改变
PUSHF
功能:标志寄存器内容压入堆栈,即
(SP)-2 -->SP (FLAGS) -->(SP)
PUSHFD
(ESP)-4-->ESP (EFLAGS) -->(ESP)
POPF/POPFD
功能:将栈顶内容送入标志寄存器
CLC
功能;置CF=0
CMC
功能:使CF取反
STC
功能:CF=1
CLD
功能:DF=0
STD
功能:DF=1
CLI
功能:IF=0
STI
功能:IF=1
文件详情
这些文件是用于构建一个简单的操作系统(haribote-os)的源代码和构建脚本。让我为您详细解释每个文件的作用:
naskfunc.nas
- 汇编语言编写的底层硬件操作函数
- 提供IO端口操作、中断控制等基础功能
- 被bootpack.c调用
Makefile
- 构建系统的核心配置文件
- 定义了从源代码到最终镜像的完整构建流程
- 包含编译、链接、打包等各个步骤
make.bat
- Windows下的构建脚本
- 调用Makefile执行构建
ipl10.nas
- 初始程序加载器(IPL)
- 负责从磁盘加载操作系统核心
- 包含FAT12文件系统支持
bootpack.c
- 操作系统核心代码
- 实现图形界面、调色板设置等功能
- 包含主循环和基本绘图函数
asmhead.nas
- 引导汇编代码
- 设置CPU模式(保护模式)
- 初始化内存和硬件环境
- 加载并跳转到bootpack
!cons_nt.bat
- Windows NT/2000/XP下的控制台启动脚本
- 简单地启动cmd.exe
整个项目是一个简单的操作系统开发练习,展示了从引导加载到图形界面显示的全过程。构建后会生成一个可启动的磁盘镜像(haribote.img)。
这是一个操作系统开发项目,我将详细解释每个文件的作用并添加代码注释。首先需要分析每个文件的功能,然后为关键代码添加中文注释,帮助理解操作系统启动流程、硬件交互和图形界面实现。
这是一个操作系统开发项目,我将详细解释每个文件的作用并添加中文注释:
- ipl10.nas - 初始程序加载器(IPL)
ipl10.nas
; 负责从磁盘加载操作系统核心到内存
; 设置FAT12文件系统参数
; 使用BIOS中断读取磁盘扇区
; 加载错误时会显示"load error"信息
- asmhead.nas - 汇编头部
asmhead.nas
应用
; 设置显示模式为320x200 8位色
; 初始化GDT(全局描述符表)
; 启用A20地址线以访问1MB以上内存
; 将bootpack从临时位置复制到最终内存地址
- naskfunc.nas - 硬件交互函数
naskfunc.nas
应用
; 提供底层硬件操作接口:
; - 端口输入输出(io_in8/io_out8等)
; - 中断控制(io_cli/io_sti)
; - EFLAGS寄存器操作
; 这些函数会被C语言调用
- bootpack.c - 操作系统核心
bootpack.c
// 主入口函数HariMain
// 初始化调色板(init_palette)
// 绘制简单的GUI界面(boxfill8)
// 进入无限循环等待中断
- Makefile - 构建脚本
Makefile
定义编译工具链路径
将各组件编译链接成最终镜像
支持img/run/install/clean等目标
- make.bat - 构建批处理
make.bat
:: 调用z_tools中的make.exe
:: 传递所有参数给make工具
- !cons_nt.bat/!cons_9x.bat - 控制台脚本
!cons_nt.bat
:: 根据不同Windows版本启动命令行
:: NT系列使用cmd.exe
:: 9x系列使用command.com
整个项目的工作流程:
- ipl10从磁盘加载asmhead和bootpack
- asmhead设置保护模式并初始化硬件
- bootpack绘制简单GUI界面
- 系统进入待机状态(HLT指令)