3 文件操作API(harib25c)
好了,让我们进入今天的第一个主题——文件操作API吧。所谓文件操作API,就是可以指定文件,并能自由读写文件内容的API。现在我们的“纸娃娃系统”还不能对磁盘进行写入操作,因此只要能读取文件内容就可以了。
一般的操作系统中,输入输出文件的API基本上都有下面这些功能(当然,还有其他一些次要的功能)。
打开……open
定位……seek
读取……read
写入……write
关闭……close
打开和关闭API用来对要读写的文件进行打开和关闭的操作。一个文件必须先打开才能进行读写操作,因为在打开时,操作系统需要对读写文件进行准备工作,关闭时也要进行一些善后处理。
打开文件时需要指定文件名,如果打开成功,操作系统将返回文件句柄。在随后的操作中,只要提供这个文件句柄就可以进行读写操作了,操作结束后将文件关闭。
定位API的功能是指定下次读取、写入命令需要操作的目标位于文件中的哪个位置。说句题外话,表示定位API的英文单词seek原本是“检索”的意思,在文件中定位就和在磁盘中检索文件差不多,不过实际上的感觉更像是检索指定文件位置所在的扇区。
读取和写入API基本上需要指定需要读取(写入)的数据长度以及内存地址,文件的内容会被传送至内存(写入操作时是由内存传送至文件)。
■■■■■
根据以上内容,我们将API设计成下面这样。
打开文件
EDX=21
EBX=文件名
EAX=文件句柄(为0时表示打开失败)(由操作系统返回)
关闭文件
EDX=22
EAX=文件句柄
文件定位
EDX=23
EAX=文件句柄
ECX=定位模式
=0:定位的起点为文件开头
=1:定位的起点为当前的访问位置
=2:定位的起点为文件末尾
EBX=定位偏移量
获取文件大小
EDX=24
EAX=文件句柄
ECX=文件大小获取模式
=0:普通文件大小
=1:当前读取位置从文件开头起算的偏移量
=2:当前读取位置从文件末尾起算的偏移量
EAX=文件大小(由操作系统返回)
文件读取
EDX=25
EAX=文件句柄
EBX=缓冲区地址
ECX=最大读取字节数
EAX=本次读取到的字节数(由操作系统返回)
■■■■■
我们修改一下bootpack.h和console.c来添加这些API。
本次的bootpack.h节选
struct TASK {
int sel, flags; /* sel为GDT编号*/
int level, priority;
struct FIFO32 fifo;
struct TSS32 tss;
struct SEGMENT_DESCRIPTOR ldt[2];
struct CONSOLE *cons;
int ds_base, cons_stack;
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;
(中略)
}
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
(中略)
if (finfo != 0) {
/*找到文件的情况*/
(中略)
if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
(中略)
start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));
(中略)
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;
}
} /*到此结束*/
(中略)
} else {
(中略)
}
(中略)
}
(中略)
}
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
(中略)
struct FILEINFO *finfo;
struct FILEHANDLE *fh;
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
(中略)
} else if (edx == 21) {
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) {
fh = (struct FILEHANDLE *) eax;
memman_free_4k(memman, (int) fh->buf, fh->size);
fh->buf = 0;
} else if (edx == 23) {
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) {
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) {
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;
}
return 0;
}
我们在struct TASK中添加了fhandle和fat两个元素,这是为了让hrb_api也能够使用console_task中声明的变量(cmd_app也可以使用)。fhandle用来存放应用程序所打开文件的信息。在cmd_app中新添加的代码,是为了自动关闭应用程序没有关闭的文件句柄。
■■■■■
接下来我们来添加apilib的函数,以便C语言可以使用新的API。
api021.nas节选
_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_fclose: ; void api_fclose(int fhandle);
MOV EDX,22
MOV EAX,[ESP+4] ; fhandle
INT 0x40
RET
api023.nas节选
_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_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_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
这几个都很简单,就不详细讲解了。
■■■■■
最后我们编写一个测试用的应用程序,这个程序的功能是将ipl10.nas的内容type出来。
typeipl.c
#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();
}
正如大家所看到的,这是个非常简单的程序。
我们来“make run”试试看吧。哦哦,运行很成功,真不错。
显示出文件内容了哦
typeipl.hrb是程序,所以可以通过Shift+F1来强制结束,从这一点来看它比命令行窗口内置的type命令要好用一些。