30天自制操作系统day5(vram和显存)(GDT和IDT)(c语言结构体)(汇编-c ...

打印 上一主题 下一主题

主题 1963|帖子 1963|积分 5889

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
day5

harib02d


c语言结构体的一些表明

  1. struct BOOTINFO {
  2. char cyls, leds, vmode, reserve;
  3. short scrnx, scrny;
  4. char *vram;
  5. };
  6. //最开始的struct命令只是把一串变量声明集中起来,统一叫做“struct BOOTINFO”。
  7. //最初是1字节的变量cyls,接着是1字节的变量leds,照此下去,最后是vram。这一
  8. //串变量一共是12字节。
  9. //定义一个结构体指针
  10. struct BOOTINFO *binfo;
  11. //为结构体指针赋初始值
  12. binfo = (struct BOOTINFO *)0x0ff0;
  13. //为结构体指针里的变量赋值
  14. xsize = (*binfo).scrnx;//必须要写括号
  15. //要不然会认为是(*binfo.scrnx)
  16. //xsize = (*binfo).scrnx;”写成“xsize = binfo—>scrnx;
复制代码

harib02e

关于汇编中变量内存地址到c语言结构体变量的连接


  • 汇编代码设置内存结构 (asmhead.nas):
  1. ; 内存地址定义
  2. CYLS    EQU     0x0ff0      ; 柱面数
  3. LEDS    EQU     0x0ff1      ; 键盘LED状态
  4. VMODE   EQU     0x0ff2      ; 显示模式
  5. SCRNX   EQU     0x0ff4      ; 屏幕X分辨率
  6. SCRNY   EQU     0x0ff6      ; 屏幕Y分辨率
  7. VRAM    EQU     0x0ff8      ; 显存地址
复制代码

  • C代码对应结构体 (bootpack.h):
  1. struct BOOTINFO { // 严格对应汇编中的内存布局
  2.     char  cyls;   // 0x0ff0
  3.     char  leds;   // 0x0ff1
  4.     char  vmode;  // 0x0ff2
  5.     char  reserve;// 0x0ff3 (填充字节)
  6.     short scrnx;  // 0x0ff4-0x0ff5
  7.     short scrny;  // 0x0ff6-0x0ff7
  8.     char* vram;   // 0x0ff8-0x0ffb (32位指针)
  9. }; // 共占用16字节 (0x0ff0-0x0fff)
复制代码

  • 地址强制映射
  1. #define ADR_BOOTINFO 0x00000ff0
  2. // 通过强制类型转换将物理地址映射为结构体指针
  3. 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 函数实现了矩形填充:
  1. void boxfill8(unsigned char *vram, int xsize, unsigned char c,
  2. int x0, int y0, int x1, int y1)
  3. {
  4. int x, y;
  5. for (y = y0; y <= y1; y++) {
  6. for (x = x0; x <= x1; x++) {
  7. vram[y * xsize + x] = c; // 更新像素颜色
  8. }
  9. }
  10. }
复制代码


  • x0, y0 是矩形左上角坐标。
  • x1, y1 是矩形右下角坐标。
  • c 是颜色索引。

5. 示例:更新字符表现
通过点阵数据更新多个像素,可以表现字符。例如,putfont8 函数根据点阵数据逐行更新像素:
  1. void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
  2. {
  3. int i;
  4. char *p, d;
  5. for (i = 0; i < 16; i++) { // 遍历字符的16行
  6. p = vram + (y + i) * xsize + x; // 计算当前行的显存地址
  7. d = font[i]; // 获取点阵数据
  8. if ((d & 0x80) != 0) p[0] = c; // 更新每个像素
  9. if ((d & 0x40) != 0) p[1] = c;
  10. if ((d & 0x20) != 0) p[2] = c;
  11. if ((d & 0x10) != 0) p[3] = c;
  12. if ((d & 0x08) != 0) p[4] = c;
  13. if ((d & 0x04) != 0) p[5] = c;
  14. if ((d & 0x02) != 0) p[6] = c;
  15. if ((d & 0x01) != 0) p[7] = c;
  16. }
  17. }
复制代码

总结
像素颜色更新的核心是通过计算显存地址并写入颜色索引值来实现的。可以更新单个像素、矩形地区,甚至通过点阵数据更新字符表现。
找到具有 1 个许可证类型的类似代码 - 查看匹配项
(y + i) * xsize + x

在代码中,(y + i) * xsize + x 是用来计算显存中某个像素的地址偏移量的。以下是详细表明:


  • 显存的二维结构
显存(vram)是一个一维数组,但它实际上表示屏幕上的一个二维像素网格。


  • xsize:屏幕的宽度(以像素为单位)。
  • y:当前字符的起始行位置(纵坐标)。
  • x:当前字符的起始列位置(横坐标)。
显存的结构可以看作是一个二维数组,像素 (x, y) 的地址在显存中可以通过以下公式计算:
  1. 地址偏移量 = y * xsize + x
复制代码
此中:


  • y * xsize 表示第 y 行的起始地址。
  • x 表示第 y 行中第 x 列的偏移量。


  • 逐行绘制字符
在 putfont8 函数中,字符的点阵数据是逐行绘制的:
  1. for (i = 0; i < 16; i++) {
  2.     p = vram + (y + i) * xsize + x;
  3.     d = font[i];
  4.     ...
  5. }
复制代码


  • 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):
  1. (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


  • 功能:将格式化的字符串输出到标准输出(通常是屏幕)。
  • 原型
    1. int printf(const char *format, ...);
    复制代码
  • 参数

    • format:格式化字符串。
    • ...:可变参数,用于替换格式化字符串中的占位符。

  • 返回值:成功时返回输出的字符数,失败时返回负值。
  • 示例
    输出到屏幕:Hello, Alice! You are 25 years old.
    1. printf("Hello, %s! You are %d years old.\\n", "Alice", 25);
    复制代码


  • sprintf


  • 功能:将格式化的字符串写入到指定的字符数组中,而不是输出到屏幕。
  • 原型
    1. int sprintf(char *str, const char *format, ...);
    复制代码
  • 参数

    • str:目标字符数组,用于存储格式化后的字符串。
    • format:格式化字符串。
    • ...:可变参数,用于替换格式化字符串中的占位符。

  • 返回值:成功时返回写入的字符数(不包罗终止符 \\0),失败时返回负值。
  • 示例
    输出到屏幕:Hello, Alice! You are 25 years old.(字符串先写入 buffer,然后通过 printf 输出)
    1. char buffer[50];
    2. sprintf(buffer, "Hello, %s! You are %d years old.", "Alice", 25);
    3. printf("%s\\n", buffer);
    复制代码


  • 重要区别
特性printfsprintf输出位置标准输出(屏幕)字符数组返回值输出的字符数写入的字符数(不包罗 \\0)用途用于直接表现信息用于生成字符串供后续处置惩罚安全性不涉及缓冲区溢出须要确保目标数组有充足的空间

  • 留意事项


  • 利用 sprintf 时,必须确保目标数组充足大以容纳格式化后的字符串,否则可能导致缓冲区溢出。
  • 如果须要更安全的版本,可以利用 snprintf,它允许指定目标数组的最大长度,避免溢出:
    1. 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 指令触发。
  • 异常处置惩罚:如非法指令、页错误等

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

铁佛

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表