30天自制操作系统(第28天)
28.1 alloca
__alloca 会在下述���况下被 C 语言的程序调用(采用 near-CALL 的方式)。 1、要执行的操作从栈中分配 EAX 个字节的内存空间( ESP -= EAX; ) 2、要遵守的规则不能改变 ECX 、 EDX 、 EBX 、 EBP 、 ESI 、 EDI的值(可以临时改变它们的值,但要使用 PUSH/POP 来复原) RET 返回的地址保存在了ESP(“RET”指令实际上相当于“POP EIP”,“POP EIP”实际上又相当于下面两条指令:MOV EIP,[ESP] ADD ESP,4)。为实现类似RET功能指令,所以编写了如下 __alloca函数指令。 __alloca: SUB ESP,EAX ADD ESP,4 JMP DWORD [ESP+EAX-4] ; 代替 RET 注:C语言中,在函数外部声明的变量和带static的变量一样,都会被解释为DB和RESB;在函数内部不带static声明的变量则会从栈中分配空间。28.2 文件操作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= 本次读取到的字节数(由操作系统返回) |
struct TASK { (中略) int ds_base, cons_stack; struct FILEHANDLE *fhandle; int *fat; }; struct FILEHANDLE { char *buf; int size; int pos; }; void console_task(struct SHEET *sheet, int memtotal) { (中略) struct FILEHANDLE fhandle[8]; (中略) for (i = 0; i fhandle = fhandle; task->fat = fat; (中略) } if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {//cmd_app代码片段 (中略) start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0)); (中略) for (i = 0; i fhandle[i].buf != 0) { memman_free_4k(memman, (int) task->fhandle[i].buf, task->fhandle[i].size); task->fhandle[i].buf = 0; } } (中略) } 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 fhandle[i].buf == 0) break; } fh = &task->fhandle[i]; reg[7] = 0; if(i 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);//与打开文件操作中的memman_alloc_4k对应 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 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){//文件读取,写入到ebx缓存区中 fh = (struct FILEHANDLE*)eax; for(i = 0; i pos == fh->size)break; *((char *) ebx + ds_base + i) = fh->buf[fh->pos]; fh->pos++; } reg[7] = i; } return 0; }
28.3 命令行API
需要实现的功能:在用户输入“type ***.nas”这样的命令时获取后面的文件名。 获取命令行 EDX=26 EBX= 存放命令行内容的地址 ECX= 最多可存放多少字节 EAX= 实际存放了多少字节(由操作系统返回)_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 struct TASK { (中略) char *cmdline; }; 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; }()()
The End