• 30天自制操作系统(第23天)


    23.1 编写malloc

    参考第22天的内容,在绘制窗口前先分配了150*50个字节大小的内存,所以导致该文件经编译后有7.6k左右,能否在其中使用指针呢?当需要开辟空间时,移动指针即可。在之前的章节中也有函数memman_alloc函数可分配内存空间,所以在该节中将都用到这个函数。
    按照下表对api的设计,编写a_nask.nas中的相关函数,以及增加console.c文件中对edx的判断。
    memman 初始化
    malloc
    free
    EDX=8
    EBX=memman 的地址
    EAX=memman 所管理的内存空间的起始地址
    ECX=memman 所管理的内存空间的字节数
    EDX=9
    EBX=memman 的地址
    ECX= 需要请求的字节数
    EAX= 分配到的内存空间地址
    EDX=10
    EBX=memman 的地址
    EAX= 需要释放的内存空间地址
    ECX= 需要释放的字节数
    1. _api_initmalloc: ; void api_initmalloc(void);
    2. PUSH EBX
    3. MOV EDX,8
    4. MOV EBX,[CS:0x0020] ; 初始化地址 P4940x0020 (DWORD) ……malloc空间的起始地址
    5. MOV EAX,EBX
    6. ADD EAX,32*1024 ;分配了32k内存
    7. MOV ECX,[CS:0x0000]
    8. SUB ECX,EAX ;包括前32位数据(文件信息)以及32k内存
    9. INT 0x40
    10. POP EBX
    11. RET
    12. _api_malloc: ;char *api_malloc(int size)
    13. PUSH EBX
    14. MOV EDX,9
    15. MOV EBX,[CS:0x0020]
    16. MOV ECX,[ESP+8] ;size
    17. INT 0x40
    18. POP EBX
    19. RET
    20. _api_free: ;void api_free(char *addr, int size)
    21. PUSH EBX
    22. MOV EDX,10
    23. MOV EBX,[CS:0x0020]
    24. MOV EAX,[ESP+8] ;addr
    25. MOV ECX,[ESP+12] ;size
    26. INT 0x40
    27. POP EBX
    28. RET

    23.2 画点

    该节实现绘制一个点的功能,下面先介绍一下API的设计:
    EDX =11
    EBX = 窗口句柄
    ESI = 显示位置的 x 坐标
    EDI = 显示位置的 y 坐标
    EAX = 色号
    1. /* a_nask.nas */
    2. _api_point: ;void api_point(int win, int x, int y, int col);
    3. PUSH EDI
    4. PUSH ESI
    5. PUSH EBX
    6. MOV EDX,11
    7. MOV EBX,[ESP+16] ;win
    8. MOV ESI,[ESP+20] ;x
    9. MOV EDI,[ESP+24] ;y
    10. MOV EAX,[ESP+28] ;col
    11. INT 0X40
    12. POP EBX
    13. POP ESI
    14. POP EDI
    15. RET
    16. /* console.c */
    17. if(edx == 11){//api_point
    18. sht = (struct SHEET *) ebx;
    19. sht->buf[sht->bxsize * edi + esi] = eax;//修改某个点的颜色
    20. sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
    21. }

    23.3 刷新窗口

    考虑到每次绘制一个点都需要进行一次刷新,不如将所有点画好后刷新一次画面来的快,所以该节实现 刷新窗口的功能,下面先介绍一下API的设计:
    EDX = 12
    EBX = 窗口句柄
    EAX = x0
    ECX = y0
    ESI = x1
    EDI = y1
    如下操作:sht = (struct SHEET *) (ebx&0xfffffffe);的目的为,窗口句柄归根到底是struct SHEET的地址,这一定是一个偶数,所以使ebx为字符串的地址进行运算。ebx&1 == 0是判断ebx作为字符串的地址,是否为空,如果为空则刷新图层。
    1. /* a_nask.nas */
    2. _api_refreshwin: ;void api_refreshwin(int win, int x0, int y0, int x1, int y1);
    3. PUSH EDI
    4. PUSH ESI
    5. PUSH EBX
    6. MOV EDX,12
    7. MOV EBX,[ESP+16] ;win
    8. MOV EAX,[ESP+20] ;x0
    9. MOV ECX,[ESP+24] ;y0
    10. MOV ESI,[ESP+28] ;x1
    11. MOV EDI,[ESP+32] ;y1
    12. INT 0X40
    13. POP EBX
    14. POP ESI
    15. POP EDI
    16. RET
    17. /* console.c */
    18. int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax){
    19. (中略)
    20. if(edx == 1)
    21. cons_putchar(cons, eax&0xff, 1);
    22. else if(edx == 2)
    23. cons_putstr0(cons, (char*)ebx+ds_base);
    24. else if(edx == 3)
    25. cons_putstr1(cons, (char*)ebx+ds_base, ecx);
    26. else if(edx == 4)
    27. return &(task->tss.esp0);
    28. else if(edx == 5){
    29. (中略)
    30. }else if(edx == 6){
    31. /*这里*/sht = (struct SHEET *) (ebx&0xfffffffe);
    32. (中略)
    33. /*这里*/if(ebx&1 == 0)
    34. /*这里*/ sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
    35. }else if(edx == 8){//memman初始化
    36. (中略)
    37. }else if(edx == 9){//malloc
    38. (中略)
    39. }else if(edx == 10){//free
    40. ecx = (ecx+0x0f)&0xfffffff0;//所释放的内存空间的字节数
    41. memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
    42. }else if(edx == 11){//api_point
    43. /*这里*/ sht = (struct SHEET *) (ebx&0xfffffffe);
    44. sht->buf[sht->bxsize * edi + esi] = eax;
    45. /*这里*/ if(ebx&1 == 0)//当ebx的最低位为0,也就是没有字符串需要打印时,刷新图层
    46. /*这里*/ sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
    47. }else if(edx == 12){
    48. /*这里*/ sht = (struct SHEET *) ebx;
    49. /*这里*/ sheet_refresh(sht, eax, ecx, esi, edi);
    50. }
    51. return 0;
    52. }

    23.4 画直线

    在窗口上画直线: EDX = 13   EBX = 窗口句柄   EAX = x0   ECX = y0   ESI = x1   EDI = y1   EBP = 色号。考虑画的是直线,如果点间隙较大则会变成虚线,较小则会可能出现在一个坐标上多次花点浪费时间,所以选择将坐标跨度较大的间隙分为1024,直接看代码。
    1. /* a_nash.nas */
    2. _api_linewin: ;void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
    3. PUSH EDI
    4. PUSH ESI
    5. PUSH EBP
    6. PUSH EBX
    7. MOV EDX,13
    8. MOV EBX,[ESP+20] ;win
    9. MOV EAX,[ESP+24] ;x0
    10. MOV ECX,[ESP+28] ;y0
    11. MOV ESI,[ESP+32] ;x1
    12. MOV EDI,[ESP+36] ;y1
    13. MOV EBP,[ESP+40] ;col
    14. INT 0X40
    15. POP EBX
    16. POP EBP
    17. POP ESI
    18. POP EDI
    19. RET
    20. /* console.c */
    21. if(edx == 13){
    22. sht = (struct SHEET *) (ebx&0xfffffffe);
    23. hrb_api_linewin(sht, eax, ecx, esi, edi, ebp);
    24. if(ebx&1 == 0)//当ebx的最低位为0,也就是没有字符串需要打印时,刷新图层
    25. sheet_refresh(sht, eax, ecx, esi, edi);
    26. }
    27. void hrb_api_linewin(struct SHEET *sht, int x0, int y0, int x1, int y1,int col){
    28. int i, x, y, len, dx, dy;
    29. dx = x1-x0;
    30. dy = y1-y0;
    31. x = x0<<10;
    32. y = y0<<10;
    33. if(dx < 0)dx = -dx;
    34. if(dy < 0)dy = -dy;
    35. if(dx >= dy){
    36. len = dx+1;
    37. if(x0 > x1)dx = -1024;
    38. else dx = 1024;
    39. if(y0 <= y1)dy = ((y1-y0+1)<<10)/len;
    40. else dy = ((y1-y0-1)<<10)/len;//-(y0-y1+1) = y1-y0-1
    41. }else{
    42. len = dy+1;
    43. if(y0 > y1)dy = -1024;
    44. else dy = 1024;
    45. if(x0 <= x1)dx = ((x1-x0+1)<<10)/len;
    46. else dx = ((x1-x0-1)<<10)/len;
    47. }
    48. for(i = 0; i < len; i++){
    49. sht->buf[(y>>10)*sht->bxsize+(x>>10)] = col;
    50. x += dx;
    51. y += dy;
    52. }
    53. return;
    54. }

    23.5 关闭窗口

    关闭窗口:EDX=14   EBX= 窗口句柄。sheet_free()函数是释放已使用的图层。
    1. /* a_nask.nas */
    2. _api_closewin: ;void api_closewin(int win);
    3. PUSH EBX
    4. MOV EDX,14
    5. MOV EBX,[ESP+8] ;win
    6. INT 0X40
    7. POP EBX
    8. RET
    9. /* console.c */
    10. if(edx == 14){//关闭窗口
    11. sheet_free((struct SHEET *) ebx);
    12. }

    23.6 键盘输入API

    该节主要实现的功能为:当按下回车键时关闭lines窗口。
    键盘输入:EDX = 15   EAX = 0…… 没有键盘输入时返回 -1 ,不休眠
                                                = 1……休眠直到发生键盘输入
                                        EAX = 输入的字符编码
    参照console_task()函数编写函数,当eax!=0时则进入休眠状态,否则没有键盘输入返回值eax为-1(reg[7]存放的是寄存器eax)。当输入键盘数据时,则更新eax寄存器中的值。
    为了设置定时器我们需要 timer的地址,不过这是 console_task 中的变量,hrb_api是无法获取的,所以在CONSOLE结构体中增加元素struct TIMER *timer; 因此还修改了 console_task ,去掉 timer 变量,以 cons.timer 取而代之。
    1. /* a_nask.nas */
    2. _api_getkey: ;int api_getkey(int mode);
    3. MOV EDX,15
    4. MOV EAX,[ESP+4]
    5. INT 0X40
    6. RET
    7. /* console.c */
    8. if(edx == 15){
    9. for(;;){
    10. io_cli();
    11. if(fifo32_status(&task->fifo) == 0){
    12. if(eax != 0)task_sleep(task);//休眠
    13. else{//没有键盘输入时返回-1,不休眠
    14. io_sti();
    15. reg[7] = -1;
    16. return 0;
    17. }
    18. }
    19. i = fifo32_get(&task->fifo);
    20. if(i <= 1){//光标
    21. /*应用程序运行时不需要显示光标,因此总是将下次显示用的值置为1*/
    22. timer_init(cons->timer, &task->fifo, 1);
    23. timer_settime(cons->timer, 50);
    24. }
    25. if(i == 2)cons->cur_c = COL8_FFFFFF;/*光标ON */
    26. if(i == 3)cons->cur_c = -1;/*光标OFF */
    27. if(256 <= i && i <= 511){
    28. reg[7] = i-256;
    29. return 0;
    30. }
    31. }
    32. }

    23.7 强制结束并关闭窗口

    在运行 walk.hrb lines.hrb 时,如果不按回车键结束,而是按Shift+F1强制结束程序的话,窗口就会残留在画面上。
    首先,在 struct SHEET 中添加一个用来存放 task的成员,当应用程序结束时,查询所有的图层,如果图层的 task为将要结束的应用程序任务,则关闭该图层。
    1. int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline){
    2. int segsiz, datsiz, esp, dathrb;
    3. /*这里*/struct SHEET *sht;
    4. /*这里*/struct SHTCTL *shtctl;
    5. (中略)
    6. if (finfo != 0) {
    7. /* 找到了与字符串相同的文件 */
    8. p = (char *) memman_alloc_4k(memman, finfo->size);
    9. file_loadfile(finfo->clustno, finfo->size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
    10. if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00){
    11. (中略)
    12. start_app(0x1b, 1003 * 8, esp, 1004*8, &(task->tss.esp0));
    13. /*从这里开始*/shtctl = (struct SHTCTL *) *((int *) 0x0fe4);/*从这里开始*/
    14. for(i = 0; i < MAX_SHEETS; i++){
    15. sht = &(shtctl->sheets0[i]);
    16. if(sht->flags != 0 && sht->task == task)
    17. sheet_free(sht);
    18. /*到这里结束*/}
    19. memman_free_4k(memman, (int) q, segsiz);
    20. }else
    21. cons_putstr0(cons, ".hrb file format error.\n");
    22. memman_free_4k(memman, (int) p, finfo->size);
    23. cons_newline(cons);
    24. return 1;
    25. }
    26. return 0;
    27. }
  • 相关阅读:
    Redis的持久化策略(RDB、AOF、RDB-AOF混合持久化)
    element-ui+vue上传图片和评论现成完整html页面
    去掉实体类String类型字段的值前后空格
    c# 单元测试
    Servlet的生命周期与HTTP协议
    node.js的模块
    几分钟查询快递单号物流,一分钟筛选出未签收单号
    《21天精通TypeScript-6》-数组类型
    ArrayList 与 顺序表 (附洗牌算法)!
    Effective Modern C++[实践]->auto类型推导不符合要求时,使用强转
  • 原文地址:https://blog.csdn.net/Amnesiac_seven/article/details/136392987