【操作系统】30天自制操作系统--(27)文件操作

        本章主要介绍了对 _alloca 函数的兼容,日文的显示,以及着重介绍了文件系统操作。

一 对_alloca的支持

首先作者写了一个小应用程序,功能是找出并打印1000以内的质数:

#include <stdio.h>
#include "apilib.h"

#define MAX		1000

void HariMain(void)
{
	char flag[MAX], s[8];
	int i, j;
	for (i = 0; i < MAX; i++) {
		flag[i] = 0;
	}
	for (i = 2; i < MAX; i++) {
		if (flag[i] == 0) {
			/*没有标记的为质数*/
			sprintf(s, "%d ", i);
			api_putstr0(s);
			for (j = i * 2; j < MAX; j += i) {
				flag[j] = 1;	/*给它的倍数做上标记*/
			}
		}
	}
	api_end();
}

        当我们需要显示10000以内的质数,将MAX置为10000时,问题就出现了,编译出现一条警告“Warning: can't link __alloca”。出现问题的原因在于,C语言编译器规定,如果栈中的变量超过4Kb,则需要调用 __alloca 函数(该函数的主要功能是根据操作系统的规格来获取栈中的空间),而操作系统中并未对此做特殊支持

        为解决这个问题,我们需要编写一个 __alloca 函数,只对ESP进行减法运算,而不做其他任何多余的操作

        为了理解上面的操作,下面列举几种错误示范:

【1】错误示范一:

SUB 	ESP, EAX
RET

        这个程序是无法运行的,因为 RET 返回的地址保存在了 ESP 中,而 ESP 的值被改变了,于是会读取到错误的返回值(RET 其实相当于 POP EIP)。

【2】错误示范二:

SUB 	ESP, EAX
JMP 	DWORD [ESP + EAX]

        这样貌似不错,ESP 似乎又回到了 SUB 之前的值。但是在相当于 RET 指令的 POP EIP 指令中,实际上又包含两条指令:

MOV		EIP, [ESP]
ADD		ESP, 4

        也就是说我们忘了给ESP + 4。

【3】错误示范三:

SUB		ESP, EAX
JMP		DWPRD[ESP + EAX]	; replace RET
ADD		ESP, 4

        这个程序的问题在于ADD指令的位置,将ADD指令放在了JMP指令的后面,所以是不可能被执行的,因此也失败了。

【4】正确示范:

        结合上面失败的教训,编写如下的 alloca.nas 文件,将其放在 apilib 中:

__alloca:
        ADD     ESP, -4
        SUB     ESP, EAX            ; 要执行的操作从栈中分配EAX个字节的内存空间
        JMP     DWORD [ESP + EAX]   ; replace RET

        这样便兼容了 __alloca 函数。        

二 文件系统操作

        在任何操作系统中,文件系统FS的操作无疑是很重要的一部分。文件系统操作主要分为打开(open)、定位(seek)、读取(read)、写入(write)、关闭(close)。目前操作系统还不支持对磁盘的写入操作,所以先设计能读取文件内容的API。

【1】设计API:

 

【2】依据此修改 console.c 与 bootpack.h:

/* bootpack.h */
struct TASK {
    // ...
	struct FILEHANDLE *fhandle;  //用来存放应用程序所打开文件的信息
	int *fat;
    // ...
};

struct FILEHANDLE {
	char *buf;
	int size;
	int pos;
};

 

/* console.c */
void console_task(struct SHEET *sheet, int memtotal) {
	// ...
    struct FILEHANDLE fhandle[8];
    // ...
    for (i = 0; i < 8; i++) {
		fhandle[i].buf = 0; /*未使用标记*/
	}
	task->fhandle = fhandle;
	task->fat = fat;
    // ...
}

void cmd_app(...) {
	// ...
	for (i = 0; i < 8; i++) { /*将未关闭的文件关闭*/ 
        if (task->fhandle[i].buf != 0) {
            memman_free_4k(memman, (int) task->fhandle[i].buf, task->fhandle[i].size);
            task->fhandle[i].buf = 0;
        }
    }
    // ...
}

void hrb_api(...) {
	 else if (edx == 21) { /* load file */
		for (i = 0; i < 8; i++) {
			if (task->fhandle[i].buf == 0) {
				break;
			}
		}
		fh = &task->fhandle[i];
		reg[7] = 0;
		if (i < 8) {
			finfo = file_search((char *) ebx + ds_base, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
			if (finfo != 0) {
				reg[7] = (int) fh;
				fh->buf = (char *) memman_alloc_4k(memman, finfo->size);
				fh->size = finfo->size;
				fh->pos = 0;
				file_loadfile(finfo->clustno, finfo->size, fh->buf, task->fat, (char *) (ADR_DISKIMG + 0x003e00));
			}
		}
	} else if (edx == 22) { /* close file */
		fh = (struct FILEHANDLE *) eax;
		memman_free_4k(memman, (int) fh->buf, fh->size);
		fh->buf = 0;
	} else if (edx == 23) { /* locate file */
		// ebx : offset
		fh = (struct FILEHANDLE *) eax;
		if (ecx == 0) {
			fh->pos = ebx;
		} else if (ecx == 1) {
			fh->pos += ebx;
		} else if (ecx == 2) {
			fh->pos = fh->size + ebx;
		}
		if (fh->pos < 0) {
			fh->pos = 0;
		}
		if (fh->pos > fh->size) {
			fh->pos = fh->size;
		}
	} else if (edx == 24) { /* get file's size */
		fh = (struct FILEHANDLE *) eax;
		if (ecx == 0) {
			reg[7] = fh->size;
		} else if (ecx == 1) {
			reg[7] = fh->pos;
		} else if (ecx == 2) {
			reg[7] = fh->pos - fh->size;
		}
	} else if (edx == 25) { /* read file */
    	fh = (struct FILEHANDLE *) eax;
		for (i = 0; i < ecx; i++) {
			if (fh->pos == fh->size) {
				break;
			}
			*((char *) ebx + ds_base + i) = fh->buf[fh->pos];
			fh->pos++;
		}
		reg[7] = i;
     }
}

【3】添加几个API的汇编:

;api021.nas节选(打开文件api)
_api_fopen:			; int api_fopen(char *fname);
		PUSH	EBX
		MOV		EDX,21
		MOV		EBX,[ESP+8]			; fname
		INT		0x40
		POP		EBX
		RET

;api022.nas节选(关闭文件api)

_api_fclose:		; void api_fclose(int fhandle);
		MOV		EDX,22
		MOV		EAX,[ESP+4]			; fhandle
		INT		0x40
		RET

;api023.nas节选(文件定位api)
_api_fseek:			; void api_fseek(int fhandle, int offset, int mode);
		PUSH	EBX
		MOV		EDX,23
		MOV		EAX,[ESP+8]			; fhandle
		MOV		ECX,[ESP+16]		; mode
		MOV		EBX,[ESP+12]		; offset
		INT		0x40
		POP		EBX
		RET

;api024.nas节选(获取文件大小api)
_api_fsize:			; int api_fsize(int fhandle, int mode);
		MOV		EDX,24
		MOV		EAX,[ESP+4]			; fhandle
		MOV		ECX,[ESP+8]			; mode
		INT		0x40
		RET

;api025.nas节选(文件读取api)
_api_fread:			; int api_fread(char *buf, int maxsize, int fhandle);
		PUSH	EBX
		MOV		EDX,25
		MOV		EAX,[ESP+16]		; fhandle
		MOV		ECX,[ESP+12]		; maxsize
		MOV		EBX,[ESP+8]			; buf
		INT		0x40
		POP		EBX
		RET

【4】最后是应用程序的实现,功能是通过上述的几个文件系统API,打开文件 ipl10.nas ,并将其内容打印出来:

#include "apilib.h"

void HariMain(void)
{
	int fh;
	char c;
	fh = api_fopen("ipl10.nas");
	if (fh != 0) {
		for (;;) {
			if (api_fread(&c, 1, fh) == 0) {
				break;
			}
			api_putchar(c);
		}
	}
	api_end();
}

        实现效果如下:

        

三 命令行API

         与之前文章中提到过的type命令有区别(【操作系统】30天自制操作系统--(18)应用程序),这边的typeipl是应用程序,而非命令行命令。之前的type命令,能够查找文件,并从对应的内存区间把文件内容拎出来。而我们这边的应用程序,不仅可以将文件内容打印出来,还可以通过 Shift + F1命令强制结束。所以,我们考虑用应用程序代替type命令。

        现在该应用程序只能显示ipl10.nas文件,要想做到通用,我们需要实现能任意指定文件名的功能。例如我们输入“type ipl10.nas”这样的命令时,获取后面的文件名,因此我们就要编写一个API来获取命令行。

【1】设计API:

【2】据此修改 console.c 和 bootpack.h:

//bootpack.h
struct TASK{
    //中略
    char *cmdline;
}

 

//console.c
void console_task(struct SHEET *sheet, int memtotal)
{
    //中略
    task->cons = &cons;
	task->cmdline = cmdline;  //这里
    //中略
}

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
    //中略
    } else if (edx == 26) {
		i = 0;
		for (;;) {
			*((char *) ebx + ds_base + i) =  task->cmdline[i];
			if (task->cmdline[i] == 0) {
				break;
			}
			if (i >= ecx) {
				break;
			}
			i++;
		}
		reg[7] = i;
	}
	return 0;
}

【3】API汇编:

_api_cmdline:		; int api_cmdline(char *buf, int maxsize);
		PUSH	EBX
		MOV		EDX,26
		MOV		ECX,[ESP+12]		; maxsize
		MOV		EBX,[ESP+8]			; buf
		INT		0x40
		POP		EBX
		RET

【4】最后是应用程序 type.c 的实现:

#include "apilib.h"

void HariMain(void)
{
	int fh;
	char c, cmdline[30], *p;

	api_cmdline(cmdline, 30);
	for (p = cmdline; *p > ' '; p++) { }	/*跳过之前的内容,直到遇到空格*/
	for (; *p == ' '; p++) { }	/*跳过空格*/
	fh = api_fopen(p);
	if (fh != 0) {
		for (;;) {
			if (api_fread(&c, 1, fh) == 0) {
				break;
			}
			api_putchar(c);
		}
	} else {
		api_putstr0("File not found.\n");
	}
	api_end();
}

        该应用程序可以自动识别命令后面跟着的文件名,查找该文件并读取其内容。命令行执行“type ipl10.nas”效果如下:

 

四 日文显示

        日文的显示没啥好看的,就不详细介绍了,有兴趣自己去看书好了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值