day5
harib02d
c语言结构体的一些解释
struct BOOTINFO {
char cyls, leds, vmode, reserve;
short scrnx, scrny;
char *vram;
};
//最开始的struct命令只是把一串变量声明集中起来,统一叫做“struct BOOTINFO”。
//最初是1字节的变量cyls,接着是1字节的变量leds,照此下去,最后是vram。这一
//串变量一共是12字节。
//定义一个结构体指针
struct BOOTINFO *binfo;
//为结构体指针赋初始值
binfo = (struct BOOTINFO *)0x0ff0;
//为结构体指针里的变量赋值
xsize = (*binfo).scrnx;//必须要写括号
//要不然会认为是(*binfo.scrnx)
//xsize = (*binfo).scrnx;”写成“xsize = binfo—>scrnx;
harib02e
关于汇编中变量内存地址到c语言结构体变量的连接
- 汇编代码设置内存布局 (
asmhead.nas
):
; 内存地址定义
CYLS EQU 0x0ff0 ; 柱面数
LEDS EQU 0x0ff1 ; 键盘LED状态
VMODE EQU 0x0ff2 ; 显示模式
SCRNX EQU 0x0ff4 ; 屏幕X分辨率
SCRNY EQU 0x0ff6 ; 屏幕Y分辨率
VRAM EQU 0x0ff8 ; 显存地址
- C代码对应结构体 (
bootpack.h
):
struct BOOTINFO { // 严格对应汇编中的内存布局char cyls; // 0x0ff0char leds; // 0x0ff1char vmode; // 0x0ff2char reserve;// 0x0ff3 (填充字节)short scrnx; // 0x0ff4-0x0ff5short scrny; // 0x0ff6-0x0ff7char* vram; // 0x0ff8-0x0ffb (32位指针)
}; // 共占用16字节 (0x0ff0-0x0fff)
- 地址强制映射:
#define ADR_BOOTINFO 0x00000ff0
// 通过强制类型转换将物理地址映射为结构体指针
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
这种硬编码的内存布局是早期操作系统开发中常见的硬件信息传递方式,通过汇编代码收集硬件参数后,C代码直接访问固定内存地址来获取信息。
主要就是地址强制映射
这个vram中的地址空间填入的数据和显示的关系
在这个代码中,vram
是显存的起始地址,显存用于存储屏幕上每个像素的颜色值。显存中的数据与屏幕上的显示内容直接相关,具体关系如下:
-
显存地址与屏幕像素的映射:
- 显存是一个线性地址空间,每个地址对应屏幕上的一个像素。
- 假设屏幕的分辨率为
scrnx x scrny
,显存的第一个字节(vram[0]
)对应屏幕左上角的第一个像素。 - 显存的第
y * scrnx + x
个字节对应屏幕上(x, y)
坐标的像素。
-
显存中的数据表示颜色:
- 每个字节存储一个颜色值,颜色值是一个索引,对应调色板中的颜色。
- 调色板通过
init_palette
和set_palette
函数初始化,将颜色索引映射到实际的 RGB 值。
-
绘制像素的过程:
-
通过直接操作显存,可以改变屏幕上对应像素的颜色。例如:这里,
c
是颜色索引,xsize
是屏幕的宽度,x
和y
是像素的坐标。vram[y * xsize + x] = c;
-
-
示例:绘制矩形:
boxfill8
函数通过遍历指定区域的像素坐标,将对应的显存地址填充为指定的颜色值,从而在屏幕上绘制矩形。
-
示例:显示字符:
putfont8
函数通过读取点阵字体数据,将字符的像素逐行写入显存,从而在屏幕上显示字符。
总结
显存中的数据直接决定屏幕上显示的内容。通过操作显存,可以控制屏幕上每个像素的颜色,从而实现图形绘制和字符显示等功能。
如何实现像素颜色更新
在这个代码中,像素颜色的更新是通过直接操作显存 (vram
) 来实现的。以下是实现像素颜色更新的步骤:
1. 确定像素的显存地址
显存是一个线性数组,每个字节对应屏幕上的一个像素。要更新某个像素的颜色,需要计算该像素在显存中的地址。
公式:
vram_address = vram + y * xsize + x;
vram
是显存的起始地址。xsize
是屏幕的宽度(像素数)。(x, y)
是像素的坐标。
2. 设置像素的颜色值
显存中的每个字节存储一个颜色索引值,该值对应调色板中的颜色。通过将颜色索引写入显存,可以更新像素的颜色。
示例代码:
vram[y * xsize + x] = color_index;
color_index
是颜色的索引值(如COL8_FFFFFF
表示白色)。
3. 示例:更新单个像素
假设要将屏幕上 (100, 50)
坐标的像素设置为白色:
vram[50 * scrnx + 100] = COL8_FFFFFF;
4. 示例:更新矩形区域
通过遍历矩形区域的每个像素,可以更新整个区域的颜色。例如,boxfill8
函数实现了矩形填充:
void boxfill8(unsigned char *vram, int xsize, unsigned char c,
int x0, int y0, int x1, int y1)
{
int x, y;
for (y = y0; y <= y1; y++) {
for (x = x0; x <= x1; x++) {
vram[y * xsize + x] = c; // 更新像素颜色
}
}
}
x0, y0
是矩形左上角坐标。x1, y1
是矩形右下角坐标。c
是颜色索引。
5. 示例:更新字符显示
通过点阵数据更新多个像素,可以显示字符。例如,putfont8
函数根据点阵数据逐行更新像素:
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
int i;
char *p, d;
for (i = 0; i < 16; i++) { // 遍历字符的16行
p = vram + (y + i) * xsize + x; // 计算当前行的显存地址
d = font[i]; // 获取点阵数据
if ((d & 0x80) != 0) p[0] = c; // 更新每个像素
if ((d & 0x40) != 0) p[1] = c;
if ((d & 0x20) != 0) p[2] = c;
if ((d & 0x10) != 0) p[3] = c;
if ((d & 0x08) != 0) p[4] = c;
if ((d & 0x04) != 0) p[5] = c;
if ((d & 0x02) != 0) p[6] = c;
if ((d & 0x01) != 0) p[7] = c;
}
}
总结
像素颜色更新的核心是通过计算显存地址并写入颜色索引值来实现的。可以更新单个像素、矩形区域,甚至通过点阵数据更新字符显示。
找到具有 1 个许可证类型的类似代码 - 查看匹配项
(y + i) * xsize + x
在代码中,(y + i) * xsize + x
是用来计算显存中某个像素的地址偏移量的。以下是详细解释:
- 显存的二维布局
显存(vram
)是一个一维数组,但它实际上表示屏幕上的一个二维像素网格。
xsize
:屏幕的宽度(以像素为单位)。y
:当前字符的起始行位置(纵坐标)。x
:当前字符的起始列位置(横坐标)。
显存的布局可以看作是一个二维数组,像素 (x, y)
的地址在显存中可以通过以下公式计算:
地址偏移量 = y * xsize + x
其中:
y * xsize
表示第y
行的起始地址。x
表示第y
行中第x
列的偏移量。
- 逐行绘制字符
在 putfont8
函数中,字符的点阵数据是逐行绘制的:
for (i = 0; i < 16; i++) {p = vram + (y + i) * xsize + x;d = font[i];...
}
i
:当前绘制的行号(从 0 到 15,共 16 行)。y + i
:表示字符当前绘制的行在屏幕上的纵坐标。(y + i) * xsize
:计算当前行在显存中的起始地址。+ x
:加上横坐标x
,得到当前行中第一个像素的地址。
- 为什么表示像素坐标
(y + i) * xsize + x
的作用是将二维坐标 (x, y + i)
转换为显存中的一维地址偏移量:
y + i
:表示字符当前绘制的行在屏幕上的纵坐标。x
:表示字符在屏幕上的横坐标。xsize
:屏幕的宽度,用于将二维坐标映射到显存的一维地址。
显存的地址是线性排列的,因此需要通过行号乘以屏幕宽度(xsize
)来跳过前面的行,再加上列号(x
)来定位具体的像素。
- 示例
假设屏幕宽度 xsize = 320
,字符起始位置 (x, y) = (8, 8)
,当前绘制到字符的第 5 行(i = 4
):
(y + i) * xsize + x = (8 + 4) * 320 + 8 = 12 * 320 + 8 = 3848
显存中偏移量为 3848
的位置就是字符第 5 行第一个像素的地址。
- 总结
(y + i) * xsize + x
是通过屏幕的宽度(xsize
)和字符的起始位置 (x, y)
计算显存中像素的地址偏移量。它将二维坐标 (x, y + i)
转换为显存的一维地址,用于正确绘制字符的点阵数据到屏幕上。
sprintf和printf的区别
printf
和 sprintf
都是 C 标准库中的格式化输出函数,但它们的用途和行为有所不同:
printf
-
功能:将格式化的字符串输出到标准输出(通常是屏幕)。
-
原型:
int printf(const char *format, ...);
-
参数:
format
:格式化字符串。...
:可变参数,用于替换格式化字符串中的占位符。
-
返回值:成功时返回输出的字符数,失败时返回负值。
-
示例:
输出到屏幕:Hello, Alice! You are 25 years old.
printf("Hello, %s! You are %d years old.\\n", "Alice", 25);
sprintf
-
功能:将格式化的字符串写入到指定的字符数组中,而不是输出到屏幕。
-
原型:
int sprintf(char *str, const char *format, ...);
-
参数:
str
:目标字符数组,用于存储格式化后的字符串。format
:格式化字符串。...
:可变参数,用于替换格式化字符串中的占位符。
-
返回值:成功时返回写入的字符数(不包括终止符
\\0
),失败时返回负值。 -
示例:
输出到屏幕:Hello, Alice! You are 25 years old.
(字符串先写入buffer
,然后通过printf
输出)char buffer[50]; sprintf(buffer, "Hello, %s! You are %d years old.", "Alice", 25); printf("%s\\n", buffer);
- 主要区别
特性 | printf | sprintf |
---|---|---|
输出位置 | 标准输出(屏幕) | 字符数组 |
返回值 | 输出的字符数 | 写入的字符数(不包括 \\0 ) |
用途 | 用于直接显示信息 | 用于生成字符串供后续处理 |
安全性 | 不涉及缓冲区溢出 | 需要确保目标数组有足够的空间 |
- 注意事项
-
使用
sprintf
时,必须确保目标数组足够大以容纳格式化后的字符串,否则可能导致缓冲区溢出。 -
如果需要更安全的版本,可以使用
snprintf
,它允许指定目标数组的最大长度,避免溢出:snprintf(buffer, sizeof(buffer), "Hello, %s!", "Alice");
总结
printf
:直接输出到屏幕,适合显示信息。sprintf
:将格式化字符串写入数组,适合需要进一步处理字符串的场景。
自个总结
就是sprintf可以写入内存,然后后面自己决定如何处理
harib02i
GDT和IDT
分段和GDT
- 表示一个段
- 段的大小
- 段的起始地址
- 段的管理属性(禁止写入,禁止执行,系统专用等)
一共8个字节
- 段寄存器
- 低3位不可以用
- 一共13位,可以表示有8192个段
所以这些段要8192*865536个字节64kB
这个64kb存到内存里,称为GDT,是“global(segment)descriptor table”的缩写,意思是全局段号记录表
然后将这个内存的起始地址和有效设定个数放在CPU内称作GDTR(global (segment) descriptor table register)的特殊寄存器中
GDT 的用途
- 切换任务时加载不同的段。
- 定义内核和用户模式的分段。
- 配合 TSS(Task State Segment)实现任务切换。
IDT
IDT是“interrupt descriptor table”的缩写,中断记录表
IDT记录了0-255的中断号码和调用函数的对应关系,比如发生了123号中断,就调用0x函数
IDT 的用途
- 硬件中断:如键盘输入、鼠标移动、定时器中断。
- 软件中断:通过
int
指令触发。 - 异常处理:如非法指令、页错误等