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

本篇内容也比较简单。首先开发了蜂鸣器的API,然后增加更多的颜色显示。为了同时运行两个应用程序,又增加了一个命令行窗口。

在这里插入图片描述

1. 蜂鸣器

首先是增加蜂鸣器的功能。

蜂鸣器与定时器一样,是由PIT控制的。

蜂鸣器发声的控制:

音高操作:

  • AL = 0xb6; OUT(0x43, AL);
  • AL = 设定值的低位 8bit; OUT(0x42, AL);
  • AL = 设定值的高位 8bit; OUT(0x42, AL);
  • 设定值为0时当作65536处理
  • 发声的音高为时钟除以设定值

蜂鸣器ON/OFF:

  • 使用I/O端口 0x61控制
  • ON: IN(AL, 0x61); AL |= 0x03; AL &= 0x0f; OUT(0x61, AL);
  • OFF: IN(AL, 0x61); AL &= 0xd; OUT(0x61, AL);

据此可以编写API:

蜂鸣器发声:
EDX = 20;
EAX = 声音频率(单位是mHz),频率为0表示停止发声

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
……
else if (edx == 20) {if (eax == 0) {i = io_in8(0x61);io_out8(0x61, i & 0x0d);} 
……}return 0;
}
_api_beep:			; void api_beep(int tone);MOV		EDX,20MOV		EAX,[ESP+4]			; toneINT		0x40RET

API很简单,继续来完成应用程序:

void api_end(void);
int api_getkey(int mode);
int api_alloctimer(void);
void api_inittimer(int timer, int data);
void api_settimer(int timer, int time);
void api_beep(int tone);void HariMain(void)
{int i, timer;timer = api_alloctimer();api_inittimer(timer, 128);for (i = 20000000; i >= 20000; i -= i / 100) {/* 20KHz~20Hz : 人类可以听到的声音范围 *//* i以1%的速度递减*/api_beep(i);api_settimer(timer, 1);		/* 0.01秒 */if (api_getkey(1) != 128) {break;}}api_beep(0);api_end();
}

可以看出每0.01s就降低一次发声的频率,当声音的频率降低到20Hz以下或者用户按下任意键结束。
不过QEMU上无法模拟蜂鸣器,因此需要用真机测试才能听到。

2. 增加更多颜色

当前操作系统支持256中颜色,而实际上只使用了16种。接下来就来优化操作系统以显示更多的颜色。

这里通过给R/G/B分别定义6个色阶,这样就可以定义出666 = 216种颜色。

void init_palette(void)
{
……unsigned char table2[216 * 3];int r, g, b;set_palette(0, 15, table_rgb);for (b = 0; b < 6; b++) {for (g = 0; g < 6; g++) {for (r = 0; r < 6; r++) {table2[(r + g * 6 + b * 36) * 3 + 0] = r * 51;table2[(r + g * 6 + b * 36) * 3 + 1] = g * 51;table2[(r + g * 6 + b * 36) * 3 + 2] = b * 51;}}}set_palette(16, 231, table2);return;
}

这样就可以通过色号来指定某种颜色了。指定RGB = [51, 102, 153]可以使用色号137,计算公式为16+1+26+336。编写应用程序如下:

int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
int api_getkey(int mode);
void api_end(void);void HariMain(void)
{char *buf;int win, x, y, r, g, b;api_initmalloc();buf = api_malloc(144 * 164);win = api_openwin(buf, 144, 164, -1, "color");for (y = 0; y < 128; y++) {for (x = 0; x < 128; x++) {r = x * 2;g = y * 2;b = 0;buf[(x + 8) + (y + 28) * 144] = 16 + (r / 43) + (g / 43) * 6 + (b / 43) * 36;}}api_refreshwin(win, 8, 28, 136, 156);api_getkey(1); /* 等待按下任意键 */api_end();
}

运行之后显示如下:
在这里插入图片描述
通过两种颜色交替排列,看上去就像将这两种颜色混合起来一样。这样可以增加颜色的显示效果。

int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
int api_getkey(int mode);
void api_end(void);unsigned char rgb2pal(int r, int g, int b, int x, int y);void HariMain(void)
{char *buf;int win, x, y;api_initmalloc();buf = api_malloc(144 * 164);win = api_openwin(buf, 144, 164, -1, "color2");for (y = 0; y < 128; y++) {for (x = 0; x < 128; x++) {buf[(x + 8) + (y + 28) * 144] = rgb2pal(x * 2, y * 2, 0, x, y);}}api_refreshwin(win, 8, 28, 136, 156);api_getkey(1); /* 等待按下任意键 */api_end();
}unsigned char rgb2pal(int r, int g, int b, int x, int y)
{static int table[4] = { 3, 1, 0, 2 };int i;x &= 1; /* 判断是偶数还是奇数*/y &= 1;i = table[x + y * 2];	/* 用来生成中间色的常量 */r = (r * 21) / 256;	/* r为0-20 */g = (g * 21) / 256;b = (b * 21) / 256;r = (r + i) / 4;	/* r为0-5*/g = (g + i) / 4;b = (b + i) / 4;return 16 + r + g * 6 + b * 36;
}

在这里插入图片描述
3. 增加命令行窗口

当前我们还只能运行一个应用程序,做不到同时运行两个应用程序。为了实现同时运行两个应用程序,先增加一个命令行窗口。

void HariMain(void)
{
……
unsigned char *buf_back, buf_mouse[256], *buf_win, *buf_cons[2];struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_cons[2];struct TASK *task_a, *task_cons[2];
……/* sht_cons */for (i = 0; i < 2; i++) {sht_cons[i] = sheet_alloc(shtctl);buf_cons[i] = (unsigned char *) memman_alloc_4k(memman, 256 * 165);sheet_setbuf(sht_cons[i], buf_cons[i], 256, 165, -1); /* 无透明色 */make_window8(buf_cons[i], 256, 165, "console", 0);make_textbox8(sht_cons[i], 8, 28, 240, 128, COL8_000000);task_cons[i] = task_alloc();task_cons[i]->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12;task_cons[i]->tss.eip = (int) &console_task;task_cons[i]->tss.es = 1 * 8;task_cons[i]->tss.cs = 2 * 8;task_cons[i]->tss.ss = 1 * 8;task_cons[i]->tss.ds = 1 * 8;task_cons[i]->tss.fs = 1 * 8;task_cons[i]->tss.gs = 1 * 8;*((int *) (task_cons[i]->tss.esp + 4)) = (int) sht_cons[i];*((int *) (task_cons[i]->tss.esp + 8)) = memtotal;task_run(task_cons[i], 2, 2); /* level=2, priority=2 */sht_cons[i]->task = task_cons[i];sht_cons[i]->flags |= 0x20;	/* 有光标 */}
……sheet_slide(sht_back,  0,  0);sheet_slide(sht_cons[1], 56,  6);sheet_slide(sht_cons[0],  8,  2);sheet_slide(sht_win,  64, 56);sheet_slide(sht_mouse, mx, my);sheet_updown(sht_back,     0);sheet_updown(sht_cons[1],  1);sheet_updown(sht_cons[0],  2);sheet_updown(sht_win,      3);sheet_updown(sht_mouse,    4);key_win = sht_win;……for (;;) {
……io_cli();if (fifo32_status(&fifo) == 0) {
……} else {
……if (256 <= i && i <= 511) { /* 键盘数据 */
……if (i == 256 + 0x3b && key_shift != 0 && task_cons[0]->tss.ss0 != 0) {	/* Shift+F1 */cons = (struct CONSOLE *) *((int *) 0x0fec);cons_putstr0(cons, "\nBreak(key) :\n");io_cli();	/* 强制结束处理时禁止任务切换 */task_cons[0]->tss.eax = (int) &(task_cons[0]->tss.esp0);task_cons[0]->tss.eip = (int) asm_end_app;io_sti();}
……} else if (512 <= i && i <= 767) { /*鼠标数据 */
……if ((mdec.btn & 0x01) != 0) {/* 按下左键时 */if (mmx < 0) {
……for (j = shtctl->top - 1; j > 0; j--) {
……if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
……if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {/* 点击「×」按钮 */if ((sht->flags & 0x10) != 0) {		/* 是否为应用程序窗口 */cons = (struct CONSOLE *) *((int *) 0x0fec);cons_putstr0(cons, "\nBreak(mouse) :\n");io_cli();	/*强制结束处理时禁止任务切换 */task_cons[0]->tss.eax = (int) &(task_cons[0]->tss.esp0);task_cons[0]->tss.eip = (int) asm_end_app;io_sti();}}break;}}}} else {
……}} else {
……}}} else if (i <= 1) {if (i != 0) {timer_init(timer, &fifo, 0); if (cursor_c >= 0) {
……}} else {
……}}timer_settime(timer, 50);if (cursor_c >= 0) {
……}}}}
}

这还只是初步的修改,还存在不少问题。先运行看看:

在这里插入图片描述

两个窗口出现了,也可以互相切换。在其中一个输入指令看看……结果出现在另一个窗口中了。

问题出在这里:

struct CONSOLE *cons = (struct CONSOLE*)*((int *) 0x0fec)

cons变量用于判断向哪个命令行窗口输出内容,该变量保存在0x0fec地址中。无论哪个任务从中读取,读到的都是同一个变量,因此也就只能在一个固定的窗口中显示。为了解决这个问题,我们将cons变量包含在任务结构体TASK中:

struct TASK {int sel, flags; /* sel代表GDT编号 */int level, priority;struct FIFO32 fifo;struct TSS32 tss;struct CONSOLE *cons;int ds_base;
};

接下来只需要将原来用于保存cons变量和ds_base变量的地址0x0fec与0x0fe8修改为TASK结构体中的cons成员与ds_base成员就可以了。修改后运行:
在这里插入图片描述

这样就可以同时运行两个程序进行对比了。

接下来尝试按下回车键退出程序,却发现QEMU异常退出了……

这又是什么原因呢?原来时因为两个应用程序只分配了一个代码段与一个数据段,color2在运行的时候把color的段覆盖掉了,导致关闭的时候出现异常:

set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
set_segmdesc(gdt + 1004, segsiz - 1,      (int) q, AR_DATA32_RW + 0x60);
for (i = 0; i < datsiz; i++) 
{q[esp + i] = p[dathrb + i];
}
start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));

修改起来也容易,只要给它们分别设置自己的段就可以了。

set_segmdesc(gdt + task->sel / 8 + 1000, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
set_segmdesc(gdt + task->sel / 8 + 2000, segsiz - 1,      (int) q, AR_DATA32_RW + 0x60);
for (i = 0; i < datsiz; i++) 
{q[esp + i] = p[dathrb + i];
}
start_app(0x1b, task->sel + 1000 * 8, esp, task->sel + 2000 * 8, &(task->tss.esp0));

这里在创建段的时候,代码段的段号用的是task->sel/8 +1000,数据段的段号用的是task->sel/8 +2000,不同的任务就不会出现覆盖的现象了。这样就可以正常关闭程序了。
在这里插入图片描述
全书接近尾声,好像内容难度相比之前也有所下降了。下一篇继续命令行窗口的优化,敬请期待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149086.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Linux:Bash中的文件描述符详解

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 Linux中的所有进程&#xff0c;都拥有自己的文件描述符(File Descriptor, FD)&#xff0c;它是操作系统在管理进程和文件时的一种抽象概念。每个文件描述符由一个非负整…

开关电源自动测试系统的测试设备与特色

突破传统测试系统的操作维护困难等限制&#xff0c;NSAT-8000开关电源自动测试系统以其开放式架构和0代码模式&#xff0c;带来了不一样的开关电源自动化测试体验。 开关电源自动测试系统的测试设备 开关电源自动测试系统核心硬件包括&#xff1a;可编程交直流电源、电子负载、…

使用 HFD 加快 Hugging Face 模型和数据集的下载,解决443报错

Hugging Face 提供了丰富的预训练模型和数据集&#xff0c;而且使用 Hugging Face 提供的 from_pretrained() 方法可以轻松加载它们&#xff0c;但是&#xff0c;模型和数据集文件通常体积庞大&#xff0c;用默认方法下载起来非常花时间。 本文将指导你如何使用 HFD&#xff08…

小新 Pro13 + windows 11 家庭中文版(网络适配器及地址配置)

网络适配器位置及地址配置 网络适配器简介 计算机系统&#xff1a;网络适配器详解&#xff0c;全面剖析 网络适配器位置 不同于win11之前的版本&#xff0c;win11的网络适配器的位置如下&#xff1a; 1、右键 右下角的网络图标-》网络和internet设置-》高级网络设置-》可以…

冒泡排序bubble sort

冒泡排序&#xff08;bubble sort&#xff09;通过连续地比较与交换相邻元素实现排序。这个过程就像气泡从底部升到顶部一样&#xff0c;因此得名冒泡排序。 算法流程 def bubble_sort(nums: list[int]):"""冒泡排序"""n len(nums)# 外循环&…

46.哀家要长脑子了!

1.435. 无重叠区间 - 力扣&#xff08;LeetCode&#xff09; 方法一&#xff1a;动态规划 实际上本质就是找最长的无重叠子序列&#xff0c;那么我们可以遍历这个区间的集合&#xff0c;只要前一个区间的右端点是小于等于后一个区间的左端点&#xff0c;那么这两个区间就不是重…

如何将Excel表格嵌入Web网页在线预览、编辑并保存到自己服务器上?

猿大师办公助手作为一款专业级的网页编辑Office方案&#xff0c;不仅可以把微软Office、金山WPS和永中Office的Word文档内嵌到浏览器网页中实现在线预览、编辑保存等操作&#xff0c;还可以把微软Office、金山WPS和永中Office的Excel表格实现网页中在线预览、编辑并保存到服务器…

虚拟机:4、配置12.5的cuda和gromacs

前言&#xff1a;本机环境是win11&#xff0c;通过wsl2安装了ubuntu实例并已实现gpu直通&#xff0c;现在需要下载12.5的cuda 一、查看是否有gpu和合适的cuda版本 在ubuntu实例中输入 nvidia-smi输出如下&#xff1a; 说明该实例上存在gpu驱动&#xff0c;且适合的CUDA版本…

硬件测试(五):信号补偿

一、简介 高速信号的趋肤效应以及传输线的介质损耗&#xff0c;使信号在传输过程中衰减很大&#xff0c;导致最后得到的信号失真。为了在接收终端能得到比较好的波形&#xff0c;就需要对受损的信号进行补偿&#xff0c;常用的补偿技术有&#xff1a;预加重、去加重和均衡三种信…

思科安全网络解决方案

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…

告别xx搜索,我用这个AI工具...

点击“终码一生”&#xff0c;关注&#xff0c;置顶公众号 每日技术干货&#xff0c;第一时间送达&#xff01; 前段时间&#xff0c;逛 GitHub 的时候发现了一个评估报告&#xff0c;对AI搜索引擎进行了详细的准确性测试&#xff0c;覆盖6种主流语言和5类场景。 其中&#xf…

苍穹外卖上半部分总结

苍穹外卖一个很经典的项目 虽然已经烂大街&#xff0c;但项目依旧是很优秀&#xff0c;并且代码十分规范&#xff0c;很值得学习。 前置介绍 niginx反向代理 前端和后端的url请求不一致的原因&#xff1a;前端是请求到nginx服务器&#xff0c;再由nginx服务器转发到后端 ngi…

箭头与数字识别系统源码分享

箭头与数字识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

《CUDA编程》1.GPU硬件与CUDA环境搭建

1 GPU 介绍 GPU&#xff08;graphics processing unit&#xff09;&#xff0c;意为图形处理器&#xff0c;也被称为显卡&#xff08;graphics card&#xff09;。GPU的浮点数运算峰值就比同时期的CPU高一个量级&#xff1b;GPU的内存带宽峰值也比同时期的CPU高一个量级。 CP…

数据结构 - 排序算法

一.直接插入排序 /*** description: 直接插入排序算法* param - a : 要进行排序的数组的指针* return : 无 */ void Seqsort(int *a) {/* i 用于表示无序部分的第一个元素的下标 &#xff0c; j 用于表示有序部分的最后一个元素的下标 &…

如何登录通义灵码,快速开启AI编码之旅?

通义灵码个人版开发者可以使用阿里云账号登录通义灵码 IDE 端插件&#xff0c;本文介绍个人版开发者登录 IDE 端插件的操作指南。 登录通义灵码 步骤 1&#xff1a;准备工作 已成功注册阿里云账号&#xff0c;具体操作可参考&#xff1a;账号注册&#xff08;PC端&#xff09;…

15.多线程概述(下篇)

目录 1.进程与线程 2.实现多线程方式一&#xff1a;继承Thread类【应用】 3.实现多线程方式二&#xff1a;实现Runnable接口【应用】 4.实现多线程方式三&#xff1a;实现Callable接口【应用】 5.三种实现方式的对比与套路 6.设置和获取线程名称/线程对象【应用】 7.线程优先级…

【编程底层原理】Java常用读写锁的使用和原理

一、引言 在Java的并发世界中&#xff0c;合理地管理对共享资源的访问是至关重要的。读写锁&#xff08;ReadWriteLock&#xff09;正是一种能让多个线程同时读取共享资源&#xff0c;而写入资源时需要独占访问的同步工具。本文将带你了解读写锁的使用方法、原理以及它如何提高…

【重磅】考虑火电机组储热改造的电力系统低碳经济调度

目录 1 主要内容 储热改造原理 约束条件 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《考虑火电机组储热改造的电力系统低碳经济调度》&#xff0c;利用原文献火电机组储热改造方案建立模型&#xff0c;在传统火电机组的基础上加装热能存储系统&#xf…

【每天学个新注解】Day 3 Lombok注解简解(二)—@Log

Log 自动创建并初始化日志记录器 日志系列注解包括&#xff1a;CommonsLog、Flogger、JBossLog、Log、Log4j、Log4j2、Slf4j、XSlf4j、CustomLog&#xff0c;对应于不同的日志框架。每个注解都会在编译时生成一个名为 log 的静态字段&#xff0c;该字段被初始化为对应的日志框…