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节选

  1. struct TASK {
  2. int sel, flags; /* sel为GDT编号*/
  3. int level, priority;
  4. struct FIFO32 fifo;
  5. struct TSS32 tss;
  6. struct SEGMENT_DESCRIPTOR ldt[2];
  7. struct CONSOLE *cons;
  8. int ds_base, cons_stack;
  9. struct FILEHANDLE *fhandle; /*从此开始*/
  10. int *fat; /*到此结束*/
  11. };
  12. struct FILEHANDLE { /*从此开始*/
  13. char *buf;
  14. int size;
  15. int pos;
  16. }; /*到此结束*/

本次的console.c节选

  1. void console_task(struct SHEET *sheet, int memtotal)
  2. {
  3. (中略)
  4. struct FILEHANDLE fhandle[8];
  5. (中略)
  6. for (i = 0; i < 8; i++) {
  7. fhandle[i].buf = 0; /*未使用标记*/
  8. }
  9. task->fhandle = fhandle;
  10. task->fat = fat;
  11. (中略)
  12. }
  13. int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
  14. {
  15. (中略)
  16. if (finfo != 0) {
  17. /*找到文件的情况*/
  18. (中略)
  19. if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
  20. (中略)
  21. start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));
  22. (中略)
  23. for (i = 0; i < 8; i++) { /*将未关闭的文件关闭*/ /*从此开始*/
  24. if (task->fhandle[i].buf != 0) {
  25. memman_free_4k(memman, (int) task->fhandle[i].buf, task->fhandle[i].size);
  26. task->fhandle[i].buf = 0;
  27. }
  28. } /*到此结束*/
  29. (中略)
  30. } else {
  31. (中略)
  32. }
  33. (中略)
  34. }
  35. (中略)
  36. }
  37. int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
  38. {
  39. (中略)
  40. struct FILEINFO *finfo;
  41. struct FILEHANDLE *fh;
  42. struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
  43. (中略)
  44. } else if (edx == 21) {
  45. for (i = 0; i < 8; i++) {
  46. if (task->fhandle[i].buf == 0) {
  47. break;
  48. }
  49. }
  50. fh = &task->fhandle[i];
  51. reg[7] = 0;
  52. if (i < 8) {
  53. finfo = file_search((char *) ebx + ds_base,
  54. (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
  55. if (finfo != 0) {
  56. reg[7] = (int) fh;
  57. fh->buf = (char *) memman_alloc_4k(memman, finfo->size);
  58. fh->size = finfo->size;
  59. fh->pos = 0;
  60. file_loadfile(finfo->clustno, finfo->size, fh->buf, task->fat, (char *)
  61. (ADR_DISKIMG + 0x003e00));
  62. }
  63. }
  64. } else if (edx == 22) {
  65. fh = (struct FILEHANDLE *) eax;
  66. memman_free_4k(memman, (int) fh->buf, fh->size);
  67. fh->buf = 0;
  68. } else if (edx == 23) {
  69. fh = (struct FILEHANDLE *) eax;
  70. if (ecx == 0) {
  71. fh->pos = ebx;
  72. } else if (ecx == 1) {
  73. fh->pos += ebx;
  74. } else if (ecx == 2) {
  75. fh->pos = fh->size + ebx;
  76. }
  77. if (fh->pos < 0) {
  78. fh->pos = 0;
  79. }
  80. if (fh->pos > fh->size) {
  81. fh->pos = fh->size;
  82. }
  83. } else if (edx == 24) {
  84. fh = (struct FILEHANDLE *) eax;
  85. if (ecx == 0) {
  86. reg[7] = fh->size;
  87. } else if (ecx == 1) {
  88. reg[7] = fh->pos;
  89. } else if (ecx == 2) {
  90. reg[7] = fh->pos - fh->size;
  91. }
  92. } else if (edx == 25) {
  93. fh = (struct FILEHANDLE *) eax;
  94. for (i = 0; i < ecx; i++) {
  95. if (fh->pos == fh->size) {
  96. break;
  97. }
  98. *((char *) ebx + ds_base + i) = fh->buf[fh->pos];
  99. fh->pos++;
  100. }
  101. reg[7] = i;
  102. }
  103. return 0;
  104. }

我们在struct TASK中添加了fhandle和fat两个元素,这是为了让hrb_api也能够使用console_task中声明的变量(cmd_app也可以使用)。fhandle用来存放应用程序所打开文件的信息。在cmd_app中新添加的代码,是为了自动关闭应用程序没有关闭的文件句柄。

■■■■■

接下来我们来添加apilib的函数,以便C语言可以使用新的API。

api021.nas节选

  1. _api_fopen: ; int api_fopen(char *fname);
  2. PUSH EBX
  3. MOV EDX,21
  4. MOV EBX,[ESP+8] ; fname
  5. INT 0x40
  6. POP EBX
  7. RET

api022.nas节选

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

api023.nas节选

  1. _api_fseek: ; void api_fseek(int fhandle, int offset, int mode);
  2. PUSH EBX
  3. MOV EDX,23
  4. MOV EAX,[ESP+8] ; fhandle
  5. MOV ECX,[ESP+16] ; mode
  6. MOV EBX,[ESP+12] ; offset
  7. INT 0x40
  8. POP EBX
  9. RET

api024.nas节选

  1. _api_fsize: ; int api_fsize(int fhandle, int mode);
  2. MOV EDX,24
  3. MOV EAX,[ESP+4] ; fhandle
  4. MOV ECX,[ESP+8] ; mode
  5. INT 0x40
  6. RET

api025.nas节选

  1. _api_fread: ; int api_fread(char *buf, int maxsize, int fhandle);
  2. PUSH EBX
  3. MOV EDX,25
  4. MOV EAX,[ESP+16] ; fhandle
  5. MOV ECX,[ESP+12] ; maxsize
  6. MOV EBX,[ESP+8] ; buf
  7. INT 0x40
  8. POP EBX
  9. RET

这几个都很简单,就不详细讲解了。

■■■■■

最后我们编写一个测试用的应用程序,这个程序的功能是将ipl10.nas的内容type出来。

typeipl.c

  1. #include "apilib.h"
  2. void HariMain(void)
  3. {
  4. int fh;
  5. char c;
  6. fh = api_fopen("ipl10.nas");
  7. if (fh != 0) {
  8. for (;;) {
  9. if (api_fread(&c, 1, fh) == 0) {
  10. break;
  11. }
  12. api_putchar(c);
  13. }
  14. }
  15. api_end();
  16. }

正如大家所看到的,这是个非常简单的程序。

我们来“make run”试试看吧。哦哦,运行很成功,真不错。

3 文件操作API(harib25c) - 图1

显示出文件内容了哦

typeipl.hrb是程序,所以可以通过Shift+F1来强制结束,从这一点来看它比命令行窗口内置的type命令要好用一些。