三十天自制操作系统(10)

第22天

CPU中的IRQ中断是从0x20号开始的,0x20也就是定时器中断。0x0~0x1f都是CPU异常所使用的中断。0x00是除零异常;0x06是非法指令异常;之前介绍的0x0d是一般保护异常;0x0c是栈异常。

我们一开始先讲栈异常。所谓的栈异常,就是定义一个数组比如 a[100],但是却在程序中访问a[101],这就是栈异常。CPU不会因为应用程序访问超出定义的栈发出警告,但是如果应用程序访问超出操作系统定义的数据段或者代码段就会产生0x0c号中断。

int *inthandler0c(int *esp)
{
  struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
  struct TASK *task = task_now();
  char s[30];
  cons_putstr0(cons, "\nINT 0C :\n Stack Exception.\n");
  sprintf(s, "EIP = %08X\n", esp[11]);
  cons_putstr0(cons, s);
  return &(task->tss.esp0);
}

_asm_inthandler0c:
  STI
  PUSH  ES
  PUSH  DS
  PUSHAD
  MOV       EAX,ESP
  PUSH  EAX  
  MOV       AX,SS
  MOV       DS,AX
  MOV       ES,AX
  CALL  _inthandler0c
  CMP       EAX,0
  JNE       end_app
  POP       EAX
  POPAD
  POP       DS
  POP       ES
  ADD       ESP,4
  IRETD

0x0c异常的处理程序写好,再把asm_inthandler0c注册进idt里就可以了。下面的汇编代码中把esp的值push到栈里,当作参数传进inthandler0c函数中。在这个函数中把传进去的esp作为int数据类型的指针,并加上了11,现在看看加上11后表示什么值。esp[07]为_asm_inthandler0c中pushad压进去的值;esp[89]为_asm_inthandler0c中push进去的ds和es。esp[10~15]为异常产生时CPU自动push的结果,其中esp[11]为eip。

我们的操用系统已经能处理2种操作系统的异常了。为了完善操作系统我们再增加一种功能:用户按下某个按键时候,强制执行当前正在运行的应用程序。

我们现在有2个任务同时在运行,1个是一开机就运行的task_a,还有一个就是命令行任务task_console。如果task_console启一个应用程序,这个应用程序很复杂,要运行很久,但是过了一段时间之后,用户后悔了,不想再运行了,然后按下ctrl+f1结束应用程序。那么处理ctrl+f1就不能在task_console里执行,因为应用程序运行的时候,task_console就没有办法处理消息队列里的数据了,所以只能在task_a里处理。当a_task任务接收到ctrl+f1的时候察看task_console里是不是运行应用程序,如果在运行应用程序那么就把寄存器强制转换为task_console的寄存器就可以了。

我们已经实现了用C语言调用显示字符的api,接下来可以写显示字符串的api了。

_api_putstr0:   ; void api_putstr0(char *s);
PUSH    EBX
MOV     EDX,2
MOV     EBX,[ESP+8]     ; s
INT     0x40
POP     EBX
RET

但是编绎执行的时候程序无法正常运行。这里牵涉到可执行文件格式的问题,接下来讲讲编绎,链接之后可执行文件的格式了。

  • 0x0000:请求操作系统为应用程序准备的数据段的大小
  • 0x0004:“Hari”字符串
  • 0x0008:数据段内预备空间的大小
  • 0x000c:esp初始值和数据部分传送目的的地址
  • 0x0010:hrb文件内数据部分的大小
  • 0x0014:hrb文件内数据部分从哪里开始
  • 0x0018:0xe9000000
  • 0x001c:应用程序入口地址 - 0x20
  • 0x0020:malloc空间的起始地址

重点看0x000c中存放的esp的值,esp为栈地址,也就是说esp之前部分被认为是栈空间,要显示的字符串也会被放到栈里。0x0018这个地址中的数据是按双字存放的,以intel8086CPU存放数据的规定是高高低低,也就是说0x0018~0x0001b的内存分别是00,00,00,e9。而e9就是jmp指令的机器码。e9后面跟着的就是应用程序入口地址-0x20的值。如果我们把这个文件读入内存然后从0x001b开始执行的话,就可以正常jmp到程序的入口地址,然后就可以正常执行了,在正常执行之前把ss和sp,ds之类的寄存器设置好,使能正常指向程序的数据段就行了。下在是代码段。

p = (char *) memman_alloc_4k(memman, finfo->size);
file_loadfile(finfo->clustno, finfo->size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
  segsiz = *((int *) (p + 0x0000));
  esp    = *((int *) (p + 0x000c));
  datsiz = *((int *) (p + 0x0010));
  dathrb = *((int *) (p + 0x0014));
  q = (char *) memman_alloc_4k(memman, segsiz);
  *((int *) 0xfe8) = (int) q;
  set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
  set_segmdesc(gdt + 1004, segsiz - 1,      (int) q, AR_DATA32_RW + 0x60);
  for (i = 0; i < datsiz; i++) {
    q[esp + i] = p[dathrb + i];
  }
  start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
  memman_free_4k(memman, (int) q, segsiz);
  } else {
    cons_putstr0(cons, ".hrb file format error.\n");
  }
  memman_free_4k(memman, (int) p, finfo->size);
  cons_newline(cons);
  return 1;
}

这段代码先判断文件中的0x0004开始的4个字节是不是"Hari”字符串,如果不就认为不是可执行文件。一个可执行文件至少有36个字节,因为文件头就是36字节了,如果文件小于36字节肯定不是可执行文件。由于链接程序自动将数据段调整为4K的倍数 ,所以文件第1个字节肯定是0x00,所以也判断一下第1个字节,如果不为0的话肯定不是可执行文件。

接下来写显示窗口的应用程序。

  • edx = 5
  • ebx = 窗口缓冲区
  • esi = 窗口宽度
  • edi = 窗口高度
  • eax = 透明色
  • ecx = 窗口名称

返回值eax = 用于操作窗口的句柄。

我们先写应用程序

int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_end(void);
char buf[150 * 50];
void HariMain(void)
{
  int win;
  win = api_openwin(buf, 150, 50, -1, "hello");
  api_end();
 }

这里api_openwin函数返回的是用于操作窗口的句柄,其实就是SHEET类型的指针。

再来写api_openwin函数

_api_openwin:   ; int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
  PUSH  EDI
  PUSH  ESI
  PUSH  EBX
  MOV       EDX,5
  MOV       EBX,[ESP+16]    ; buf
  MOV       ESI,[ESP+20]    ; xsiz
  MOV       EDI,[ESP+24]    ; ysiz
  MOV       EAX,[ESP+28]    ; col_inv
  MOV       ECX,[ESP+32]    ; title
  INT       0x40
  POP       EBX
  POP       ESI
  POP       EDI
  RET

这程序很简单,就是根据我们之前设计的寄存器的功能,赋值,然后调用0x40中断

else if (edx == 5) {
  sht = sheet_alloc(shtctl);
  sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
  make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
  sheet_slide(sht, 100, 50);
  sheet_updown(sht, 3); 
  reg[7] = (int) sht;
}

我们默认把窗口放到(100,50)的位置。reg[7]就是中断返回时候eax的值。

设计在窗口上写字符串和画方块的api

显示字符串api

  • edx = 6
  • ebx = 窗口句柄
  • esi = x座标
  • edi = y座标
  • eax = 色号
  • ecx = 字符串长度
  • ebp = 字符串

描绘方块api

  • edx = 7
  • ebx = 窗口句柄
  • eax = x0
  • ecx = y0
  • esi = x1
  • edi = y1
  • ebp = 色号

接下来跟之前显示窗口的功能差不多。

写好之后应用程序就可以这么调用;

int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_end(void);

char buf[150 * 50];

void HariMain(void)
{
  int win;
  win = api_openwin(buf, 150, 50, -1, "hello");
  api_boxfilwin(win,  8, 36, 141, 43, 3 /*黄色 */);
  api_putstrwin(win, 28, 28, 0 /* �黑色 */, 12, "hello, world");
  api_end();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容