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

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

时间:2024-09-12 16:54:14浏览次数:13  
标签:newline sheet COL8 读书笔记 ++ 18 30 cursor finfo

本篇内容涉及到文件与文件系统,以及应用程序的运行。首先实现type命令,读取文件并显示;接下来导入对FAT文件系统的支持,实现读取大小512字节以上,存放在不连续扇区中的文件。在此基础上,最终实现读取并运行应用程序。
在这里插入图片描述

1. type命令实现

type命令是Windows命令行中用于读取并显示文件内容的命令,对应Linux中的cat命令。为了获取文件信息,这里还是要用到上一篇提到的结构体:

struct FILEINFO {
	unsigned char name[8], ext[3], type;
	char reserve[10];
	unsigned short time, date, clustno;
	unsigned int size;
};

其中clustno这个成员表示文件从磁盘上的哪个扇区开始存放。对于当前的操作系统映像,文件的地址有如下公式:

地址 = clustno * 512 + 0x003e00

这样只需将文件的内容读取,并显示出来即可:

void console_task(struct SHEET *sheet, unsigned int memtotal)
{

	……
	char s[30], cmdline[30], *p;
	……

	for (;;) {
		io_cli();
		if (fifo32_status(&task->fifo) == 0) {
			task_sleep(task);
			io_sti();
		} else {
			……
			if (256 <= i && i <= 511) { 
				if (i == 8 + 256) {
					if (cursor_x > 16) {
						putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
						cursor_x -= 8;
					}
				} else if (i == 10 + 256) {
					/* Enter */
					putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
					cmdline[cursor_x / 8 - 2] = 0;
					cursor_y = cons_newline(cursor_y, sheet);
					if (strcmp(cmdline, "mem") == 0) {
						sprintf(s, "total   %dMB", memtotal / (1024 * 1024));
						putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
						cursor_y = cons_newline(cursor_y, sheet);
						sprintf(s, "free %dKB", memman_total(memman) / 1024);
						putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
						cursor_y = cons_newline(cursor_y, sheet);
						cursor_y = cons_newline(cursor_y, sheet);
					} else if (strcmp(cmdline, "cls") == 0) {
						for (y = 28; y < 28 + 128; y++) {
							for (x = 8; x < 8 + 240; x++) {
								sheet->buf[x + y * sheet->bxsize] = COL8_000000;
							}
						}
						sheet_refresh(sheet, 8, 28, 8 + 240, 28 + 128);
						cursor_y = 28;
					} else if (strcmp(cmdline, "dir") == 0) {
						for (x = 0; x < 224; x++) {
							if (finfo[x].name[0] == 0x00) {
								break;
							}
							if (finfo[x].name[0] != 0xe5) {
								if ((finfo[x].type & 0x18) == 0) {
									sprintf(s, "filename.ext   %7d", finfo[x].size);
									for (y = 0; y < 8; y++) {
										s[y] = finfo[x].name[y];
									}
									s[ 9] = finfo[x].ext[0];
									s[10] = finfo[x].ext[1];
									s[11] = finfo[x].ext[2];
									putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
									cursor_y = cons_newline(cursor_y, sheet);
								}
							}
						}
						cursor_y = cons_newline(cursor_y, sheet);
					} else if (cmdline[0] == 't' && cmdline[1] == 'y' && cmdline[2] == 'p' &&
							cmdline[3] == 'e' && cmdline[4] == ' ') {
						/* type命令 */
						/* 准备文件名 */
						for (y = 0; y < 11; y++) {
							s[y] = ' ';
						}
						y = 0;
						for (x = 5; y < 11 && cmdline[x] != 0; x++) {
							if (cmdline[x] == '.' && y <= 8) {
								y = 8;
							} else {
								s[y] = cmdline[x];
								if ('a' <= s[y] && s[y] <= 'z') {
									/* 将小写字母转换为大写字母 */
									s[y] -= 0x20;
								} 
								y++;
							}
						}
						/* 寻找文件 */
						for (x = 0; x < 224; ) {
							if (finfo[x].name[0] == 0x00) {
								break;
							}
							if ((finfo[x].type & 0x18) == 0) {
								for (y = 0; y < 11; y++) {
									if (finfo[x].name[y] != s[y]) {
										goto type_next_file;
									}
								}
								break; /* 找到文件 */
							}
		type_next_file:
							x++;
						}
						if (x < 224 && finfo[x].name[0] != 0x00) {
							/* 找到文件的情况 */
							y = finfo[x].size;
							p = (char *) (finfo[x].clustno * 512 + 0x003e00 + ADR_DISKIMG);
							cursor_x = 8;
							for (x = 0; x < y; x++) {
								/* 逐个字符输出 */
								s[0] = p[x];
								s[1] = 0;
								putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
								cursor_x += 8;
								if (cursor_x == 8 + 240) {/* 到达最右端后换行*/
									cursor_x = 8;
									cursor_y = cons_newline(cursor_y, sheet);
								}
							}
						} else {
							/* 没有找到文件的情况 */
							putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
							cursor_y = cons_newline(cursor_y, sheet);
						}
						cursor_y = cons_newline(cursor_y, sheet);
					} else if (cmdline[0] != 0) {
						putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "Bad command.", 12);
						cursor_y = cons_newline(cursor_y, sheet);
						cursor_y = cons_newline(cursor_y, sheet);
					}
					putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
					cursor_x = 16;
				} else {
					if (cursor_x < 240) {
						s[0] = i - 256;
						s[1] = 0;
						cmdline[cursor_x / 8 - 2] = i - 256;
						putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
						cursor_x += 8;
					}
				}
			}
			if (cursor_c >= 0) {
				boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, cursor_y, cursor_x + 7, cursor_y + 15);
			}
			sheet_refresh(sheet, cursor_x, cursor_y, cursor_x + 8, cursor_y + 16);
		}
	}
}

因为type命令后面会跟文件名,因此这里只比较前4个字符是否为“type”。而在准备文件名部分,会将命令行输入的文件名存入s[]数组中,并转换为大写字母。当前只考虑8个字节的文件名与3个字节的扩展名,因此数组的长度为11。找到文件名后,则将文件的内容逐个字符显示出来,否则显示"File not found"。

尝试用type命令打卡make.bat文件,显示很正常:

在这里插入图片描述

而尝试打开二进制文件时,则会显示乱码:

在这里插入图片描述

再尝试打开nas汇编代码文件时,效果如下:

在这里插入图片描述

可以看到有些字符如"DB","0x55"正常显示出来了,但还是存在大量的乱码。这是由于没有对换行符和制表符的字符编码进行处理导致的。接下来就来增加对换行符和制表符的支持。

制表符和换行符的字符编码如下:

  • 0x09: 制表符
  • 0x0a: 换行符

制表符是用来对齐字符显示位置的。制表符的功能是在当前位置到下一个制表位之间填充上空格。这里作者将制表位设置在0,4,8……等能被4整除的字符数的位置处。对程序做如下的改写:

else if (strncmp(cmdline, "type ", 5) == 0) 
{
	for (y = 0; y < 11; y++) 
	{
		s[y] = ' ';
	}
	y = 0;
	for (x = 5; y < 11 && cmdline[x] != 0; x++) 
	{
		if (cmdline[x] == '.' && y <= 8) 
		{
			y = 8;
		} else 
		{
			s[y] = cmdline[x];
			if ('a' <= s[y] && s[y] <= 'z') 
			{
				s[y] -= 0x20;
			} 
			y++;
		}
	}
	for (x = 0; x < 224; ) 
	{
		if (finfo[x].name[0] == 0x00) 
		{
			break;
		}
		if ((finfo[x].type & 0x18) == 0) 
		{
			for (y = 0; y < 11; y++) 
			{
				if (finfo[x].name[y] != s[y]) 
				{
					goto type_next_file;
				}
			}
			break; 
		}
type_next_file:
		x++;
	}
	if (x < 224 && finfo[x].name[0] != 0x00) 
	{
		/* 找到文件的情况 */
		y = finfo[x].size;
		p = (char *) (finfo[x].clustno * 512 + 0x003e00 + ADR_DISKIMG);
		cursor_x = 8;
		for (x = 0; x < y; x++) 
		{
			/* 逐字输出 */
			s[0] = p[x];
			s[1] = 0;
			if (s[0] == 0x09) {	/* 制表符 */
				for (;;) 
				{
					putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
					cursor_x += 8;
					if (cursor_x == 8 + 240) 
					{
						cursor_x = 8;
						cursor_y = cons_newline(cursor_y, sheet);
					}
					if (((cursor_x - 8) & 0x1f) == 0) 
					{
						break;	/* 能被32整除则break */
					}
				}
			} 
			else if (s[0] == 0x0a) 
			{	/* 换行 */
				cursor_x = 8;
				cursor_y = cons_newline(cursor_y, sheet);
			} 
			else if (s[0] == 0x0d) 
			{	/* 回车符 */
				/* 这里暂不进行任何操作 */
			} 
			else 
			{	/* 一般字符 */
				putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
				cursor_x += 8;
				if (cursor_x == 8 + 240) {
					cursor_x = 8;
					cursor_y = cons_newline(cursor_y, sheet);
				}
			}
		}

这里把strcmp换成了strncmp,只比较前5个字符,这样就不需要逐个比较了。

(cursor_x - 8) & 0x1f这一句,用横坐标减8,是去除边框的8个像素;而&0x1f用来判断能否整除32。每个制表符相隔4个字符,每个字符是8个像素,也就是制表符之间相隔32个像素,通过这种方式来判断制表符的位置。

在这里插入图片描述
从显示的内容看,仍然有些乱码。不过这是注释的日语部分,这里就暂时忽略了。

2. FAT支持

上面的type命令,其实只能展示512字节以内的文件。对于512字节以上的文件,显示可能会有问题。

这里涉及到Windows的磁盘管理。存放512字节以上的文件时,有时并不存入连续的扇区中,这样我们需要找到文件的下一个扇区。而这一信息,磁盘中是有记录的,找到这一记录就可以正确读取文件内容了。这个记录存放的位置为0柱面,0磁头,2扇区开始的9个扇区,在磁盘映像中的地址为0x000200-0x0013ff。这个记录被称为FAT(file allocation table)。

……

file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));

……
if (x < 224 && finfo[x].name[0] != 0x00) 
{
	p = (char *) memman_alloc_4k(memman, finfo[x].size);
	file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
	cursor_x = 8;
	for (y = 0; y < finfo[x].size; y++) 
	{
		s[0] = p[y];
		s[1] = 0;
		if (s[0] == 0x09) 
		{	
			for (;;) 
			{
				putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
				cursor_x += 8;
				if (cursor_x == 8 + 240) 
				{
					cursor_x = 8;
					cursor_y = cons_newline(cursor_y, sheet);
				}
				if (((cursor_x - 8) & 0x1f) == 0) 
				{
					break;	
				}
			}
		} else if (s[0] == 0x0a) 
		{	
			cursor_x = 8;
			cursor_y = cons_newline(cursor_y, sheet);
		} 
		else if (s[0] == 0x0d) 
		{	
		} 
		else 
		{	
			putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
			cursor_x += 8;
			if (cursor_x == 8 + 240) 
			{
				cursor_x = 8;
				cursor_y = cons_newline(cursor_y, sheet);
			}
		}
	}
	memman_free_4k(memman, (int) p, finfo[x].size);
	} 
	else 
	{
		putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
		cursor_y = cons_newline(cursor_y, sheet);
	}

由于磁盘映像中的FAT使用了微软公司的算法进行了压缩,首先需要解压,file_readfat函数即是实现解压的功能:

void file_readfat(int *fat, unsigned char *img)
{
	int i, j = 0;
	for (i = 0; i < 2880; i += 2) {
		fat[i + 0] = (img[j + 0]      | img[j + 1] << 8) & 0xfff;
		fat[i + 1] = (img[j + 1] >> 4 | img[j + 2] << 4) & 0xfff;
		j += 3;
	}
	return;
}

这里就是将算法转换为程序代码实现。在fat数组中,存放着文件下一个扇区的记录。当文件大小在512字节以上时,存放在多个扇区中。我们从fat数组的第一条记录中获取文件第一个扇区的位置,将这个扇区的内容读取出来;然后再从fat数组的第二条记录中获取文件第二个扇区的位置,再将这个扇区的内容读取出来;……以此类推,每读完一个扇区就去fat数组中查询下一条记录,直到记录中的值为FFF,这表示文件已经读完了。

接下来就通过file_loadfile函数将文件读取出来。从代码中可以看出,文件大小在512字节以上,则根据fat获取下一个扇区的位置,持续读取,直到剩余的内容在512字节以内,直接读出。

void file_loadfile(int clustno, int size, char *buf, int *fat, char *img)
{
	int i;
	for (;;) {
		if (size <= 512) {
			for (i = 0; i < size; i++) {
				buf[i] = img[clustno * 512 + i];
			}
			break;
		}
		for (i = 0; i < 512; i++) {
			buf[i] = img[clustno * 512 + i];
		}
		size -= 512;
		buf += 512;
		clustno = fat[clustno];
	}
	return;
}

3. 应用程序运行

到目前为止我们已经完成了读取文件的功能。应用程序也是一个文件,如何将其运行起来呢?

需要为应用程序创建一个内存段。将应用程序读入进来,跳转到该段中的程序,就可以开始运行了。跳转的方法,在之前多任务的部分已经讲过,是farjump指令。

……

else if (strcmp(cmdline, "hlt") == 0) 
{
	/* 启动hlt.hrb应用程序 */
	for (y = 0; y < 11; y++) 
	{
		s[y] = ' ';
	}
	s[0] = 'H';
	s[1] = 'L';
	s[2] = 'T';
	s[8] = 'H';
	s[9] = 'R';
	s[10] = 'B';
	for (x = 0; x < 224; ) 
	{
		if (finfo[x].name[0] == 0x00) 
		{
			break;
		}
		if ((finfo[x].type & 0x18) == 0) 
		{
			for (y = 0; y < 11; y++) 
			{
				if (finfo[x].name[y] != s[y]) 
				{
					goto hlt_next_file;
				}
			}
			break; /* 找到文件 */
		}
hlt_next_file:
		x++;
	}
	if (x < 224 && finfo[x].name[0] != 0x00) 
	{
		/* 找到文件的情况 */
		p = (char *) memman_alloc_4k(memman, finfo[x].size);
		file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
		set_segmdesc(gdt + 1003, finfo[x].size - 1, (int) p, AR_CODE32_ER);
		farjmp(0, 1003 * 8);
		memman_free_4k(memman, (int) p, finfo[x].size);
	} 
	else 
	{
		putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
		cursor_y = cons_newline(cursor_y, sheet);
	}
	cursor_y = cons_newline(cursor_y, sheet);
}
……

这里我们运行了一个hlt.hrb的应用程序。为了不与windows混淆,这里作者自定义了扩展名.hrb。

我们首先根据文件的大小,通过memman_alloc_4k函数分配一段内存,首地址保存在变量p中。然后调用file_loadfile将hlt.hrb文件读入到这段内存中。接下来,我们设置一个地址段,段号设置为1003(因为1-2号分配给了dsctbl.c使用,3-1002号分配给了前面的多任务mtask.c使用,因此这里使用1003号,当然使用之后的段号也是没问题的),段的首地址设置为刚才p变量中的地址。这样一切准备就绪,我们使用farjmp指令跳转到1003号段,程序就可以开始运行了。

这个hlt.hrb应用程序其实执行的就是HLT指令,因此执行后CPU进入睡眠状态,现象其实就是命令行窗口完全没有反应了。不过还是可以切换到任务A,或者通过鼠标移动任务A的窗口。

在这里插入图片描述
下一篇中将会实现应用程序对操作系统功能的调用。敬请期待。

标签:newline,sheet,COL8,读书笔记,++,18,30,cursor,finfo
From: https://blog.csdn.net/Ocean1994/article/details/142062917

相关文章

  • 18_四数之和
    18_四数之和【问题描述】给你一个由n个整数组成的数组nums,和一个目标值target。请你找出并返回满足下述全部条件且不重复的四元组[nums[a],nums[b],nums[c],nums[d]](若两个四元组元素一一对应,则认为两个四元组重复):0<=a,b,c,d<na、b、c和d互不相同nums[......
  • 通过LiveGBS实现GB28181接入不同网络监控摄像头时如何跟不同网络接入设置使用不同的收
    @目录1、背景2、设备接入播放2.1、查看通道2.2、直播播放3、默认收流地址配置4、其它网络设备收流配置5、搭建GB28181视频直播平台1、背景服务器部署的时候,可能有多个网卡多个网段。LiveGBS接入国标摄像头设备,或是下级平台的时候,可能来自于不同的网段。这时候,怎么把不同网络段的......
  • 通过LiveGBS实现安防监控摄像头GB28181转成WebRTC流实现web浏览器网页无插件低延迟直
    @目录1、WebRTC超低延时直播2、WebRTC延时对比3、LiveGBS的低延时的WebRTC流4、分屏页面如何选择默认播放流5、无法播放Webrtc6、搭建GB28181视频直播平台1、WebRTC超低延时直播需要低延时的视频流监控播放,之前可以用rtmp的低延时播放(1秒左右),随着浏览器对rtmp的禁用,无插件的低延......
  • MUR3040CT-ASEMI快恢复二极管MUR3040CT
    编辑:llMUR3040CT-ASEMI快恢复二极管MUR3040CT型号:MUR3040CT品牌:ASEMI封装:TO-220AB安装方式:插件批号:最新恢复时间:35ns最大平均正向电流(IF):30A最大循环峰值反向电压(VRRM):400V最大正向电压(VF):0.95V~1.90V工作温度:-50°C~150°C芯片个数:2芯片尺寸:mil正向浪涌电流(IFMS):300AM......
  • C2A:灾难场景中人体检测数据集(猫脸码客 第185期)
    亲爱的读者们,您是否在寻找某个特定的数据集,用于研究或项目实践?欢迎您在评论区留言,或者通过公众号私信告诉我,您想要的数据集的类型主题。小编会竭尽全力为您寻找,并在找到后第一时间与您分享。C2ADataset:HumanDetectioninDisasterScenarios在自然灾害和人为灾难的应......
  • Windows 计划任务程序 运行结果 0xC000013a 或 0x41301 隐藏bat的弹出
    Windows的计划任务配置定时调度任务时发现,点击运行任务时,任务运行结果不是成功,而是0xC000013a,如下图所示配置任务时:选择【不管用户是否登录都要运行】,上述错误消失选择【只在用户登录时运行】,上述错误重现; 另外:需要bat弹出需要选【只在用户登录时运行】 需要隐藏bat的弹......
  • 代码整洁之道--读书笔记(8)
    代码整洁之道简介:本书是编程大师“Bob大叔”40余年编程生涯的心得体会的总结,讲解要成为真正专业的程序员需要具备什么样的态度,需要遵循什么样的原则,需要采取什么样的行动。作者以自己以及身边的同事走过的弯路、犯过的错误为例,意在为后来者引路,助其职业生涯迈上更高台阶。本......
  • fastjson1.2.24反序列化漏洞复现 CVE-2017-18349
    1.准备:1.1复现环境漏洞环境:vulnhub靶场工具准备:jdk8,apache-maven-3.9.9,kali2024.1,MarshalSec1.2环境启动进入vulnhub目录下的fastjson目录,进入CVE-2017-18349目录cd/home/hbesljx/vulhub/fastjson/1.2.24-rcedocker-compoe启动漏洞环境docker-composeup-d访问靶机......
  • 181 Animation Basics & CSS Transitions
    实现点击Animate,上面的方块移动示例步骤1、为Animate按钮添加@click方法animateBlock<button@click="animateBlock">Animate</button>2、添加animatedBlock变量控制是否可以移动data() {    return {      animatedBlock: false,      dialogIsV......
  • 【有源码】2025年最全的计算机软件毕业设计选题大全:300个热门选题推荐!本科选题大全汇
    注意:该项目只展示部分功能,如需了解,文末咨询即可。本文目录1、前言2、视频简介3、300个毕设选题参考-微信小程序4、更多推荐1、前言在移动互联网时代,微信小程序凭借其便捷、轻量化的特点,已经成为开发者和用户关注的焦点。从电商平台到社交娱乐,微信小程序的应用场......