Day23
今天的话主要在提供各种窗口相关的API,以及最后对于强制结束程序的完善。
一些感想和问题
应用程序的malloc
这次做的是应用程序需要的malloc,目前的做法是从一个较大的应用程序数据段中拿一部分给应用程序,以后看起来还会改进
强制结束时释放资源
当应用程序通过API申请某些系统资源的时候,将其与任务绑定。若任务结束后,这些资源仍没被释放,则由操作系统进行释放
free去哪里了?
虽然api提供了free,但是却没有看到在后面的函数使用free。。不太明白为什么
今天主要是功能性的更新,所以开始的总结比较少
编写malloc
定义各种API
- memman初始化
EDX=8
EBX=memman的地址
EAX=memman所管理的内存空间的起始地址
ECX=memman所管理的内存空间的字节数 - malloc
EDX=9
EBX=memman的地址
ECX=需要请求的字节数
EAX=分配到的内存空间地址 - free
EDX=10
EBX=memman的地址
EAX=需要释放的内存空间地址
ECX=需要释放的字节数
case 8:
memman_init((struct MEMMAN *)(ebx + ds_base));
ecx &= 0xfffffff0; // 以16字节为单位
memman_free((struct MEMMAN *)(ebx + ds_base), eax, ecx);
break;
case 9:
ecx = (ecx + 0x0f) & 0xfffffff0; // 以16字节为单位进位取整
reg[7] = memman_alloc((struct MEMMAN *)(ebx + ds_base), ecx);
break;
case 10:
ecx = (ecx + 0x0f) & 0xfffffff0; // 以16字节为单位进位取整
memman_free((struct MEMMAN *)(ebx + ds_base), eax, ecx);
break;
_api_initmalloc: ; void api_initmalloc(void);
PUSH EBX
MOV EDX,8
MOV EBX,[CS:0x0020] ; malloc内存空间的地址
MOV EAX,EBX
ADD EAX,32*1024 ; 加上32KB
MOV ECX,[CS:0x0000] ; 数据段的大小
SUB ECX,EAX
INT 0x40
POP EBX
RET
_api_malloc: ; char *api_malloc(int size);
PUSH EBX
MOV EDX,9
MOV EBX,[CS:0x0020]
MOV ECX,[ESP+8] ; size
INT 0x40
POP EBX
RET
_api_free: ; void api_free(char *addr, int size);
PUSH EBX
MOV EDX,10
MOV EBX,[CS:0x0020]
MOV EAX,[ESP+ 8] ; addr
MOV ECX,[ESP+12] ; size
INT 0x40
POP EBX
RET
然后编写一下测试程序试试
void HariMain(void)
{
char *buf;
int win;
api_initmalloc();
buf = api_malloc(150 * 50);
win = api_openwin(buf, 150, 50, -1, "hello");
api_boxfilwin(win, 8, 36, 141, 43, 6 /* 浅蓝色 */);
api_putstrwin(win, 28, 28, 0 /* 黑色 */, 12, "hello, world");
api_end();
}
画点
harib20b
画点API
在窗口中画点
EDX=11
EBX=窗口句柄
ESI=显示位置的x坐标
EDI=显示位置的y坐标
EAX=色号
case 11:
sht = (struct SHEET *)ebx;
sht->buf[sht->bxsize * edi + esi] = eax;
sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
break;
_api_point: ; void api_point(int win, int x, int y, int col);
PUSH EDI
PUSH ESI
PUSH EBX
MOV EDX,11
MOV EBX,[ESP+16] ; win
MOV ESI,[ESP+20] ; x
MOV EDI,[ESP+24] ; y
MOV EAX,[ESP+28] ; col
INT 0x40
POP EBX
POP ESI
POP EDI
RET
测试程序
void HariMain(void)
{
char *buf;
int win;
api_initmalloc();
buf = api_malloc(150 * 100);
win = api_openwin(buf, 150, 100, -1, "star1");
api_boxfilwin(win, 6, 26, 143, 93, 0 /* 黑色 */);
api_point(win, 75, 59, 3 /* 黄色 */);
api_end();
}
void HariMain(void)
{
char *buf;
int win, i, x, y;
api_initmalloc();
buf = api_malloc(150 * 100);
win = api_openwin(buf, 150, 100, -1, "stars");
api_boxfilwin(win, 6, 26, 143, 93, 0 /* 黑色 */);
for(i = 0; i < 50; ++i)
{
x = (rand() % 137) + 6;
y = (rand() % 67) + 26;
api_point(win, x, y, 3 /* 黄色 */);
}
api_end();
}
在屏幕上点了好多个点。。
刷新窗口
harib20c
每次调用点都要重新刷新,太慢了
所以添加一个刷新的API,同时修改之前的API,可以指定是否刷新
刷新窗口
EDX=12
EBX=窗口句柄
EAX=x0
ECX=y0
ESI=x1
EDI=y1
case 6:
sht = (struct SHEET *)(ebx & 0xfffffffe);
putfonts8_asc(sht->buf, sht->bxsize, esi, edi, eax, (char *)ebp + ds_base);
if((ebx & 1) == 0)
{
sheet_refresh(sht, esi, edi, esi + ecx * 8, edi + 16);
}
break;
case 7:
sht = (struct SHEET *)(ebx & 0xfffffffe);
boxfill8(sht->buf, sht->bxsize, ebp, eax, ecx, esi, edi);
if((ebx & 1) == 0)
{
sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
}
break;
...
case 11:
sht = (struct SHEET *)(ebx & 0xfffffffe);
sht->buf[sht->bxsize * edi + esi] = eax;
if((ebx & 1) == 0)
{
sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
}
break;
case 12:
sht = (struct SHEET *)ebx;
sheet_refresh(sht, eax, ecx, esi, edi);
break;
通过偏移1位SHT,之后在判断是否需要刷新的时候,如果不是2的倍数,则ebx & 1就为0,不进行刷新。
_api_refreshwin: ; void api_refreshwin(int win, int x0, int y0, int x1, int y1);
PUSH EDI
PUSH ESI
PUSH EBX
MOV EDX,12
MOV EBX,[ESP+16] ; win
MOV EAX,[ESP+20] ; x0
MOV ECX,[ESP+24] ; y0
MOV ESI,[ESP+28] ; x1
MOV EDI,[ESP+32] ; y1
INT 0x40
POP EBX
POP ESI
POP EDI
RET
测试程序
void HariMain(void)
{
char *buf;
int win, i, x, y;
api_initmalloc();
buf = api_malloc(150 * 100);
win = api_openwin(buf, 150, 100, -1, "stars2");
api_boxfilwin(win + 1, 6, 26, 143, 93, 0 /* 黑色 */);
for(i = 0; i < 50; ++i)
{
x = (rand() % 137) + 6;
y = (rand() % 67) + 26;
api_point(win + 1, x, y, 3 /* 黄色 */);
}
api_refreshwin(win, 6, 26, 144, 94);
api_end();
}
画直线
harib20d
貌似现在还不能使用浮点数。作者提供的方法是预先放大坐标1024倍(凑个整)之后每次增加dx,dy
在窗口上画直线
EDX=13
EBX=窗口句柄
EAX=x0
ECX=y0
ESI=x1
EDI=y1
EBP=色号
case 13:
sht = (struct SHEET *)(ebx & 0xfffffffe);
hrb_api_linewin(sht, eax, ecx, esi, edi, ebp);
if((ebx & 1) == 0)
{
sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
}
break;
void hrb_api_linewin(struct SHEET *sht, int x0, int y0, int x1, int y1, int col)
{
int i, x, y, len, dx, dy;
dx = x1 - x0;
dy = y1 - y0;
x = x0 << 10;
y = y0 << 10;
if(dx < 0)
{
dx = -dx;
}
if(dy < 0)
{
dy = -dy;
}
if(dx >= dy)
{
len = dx + 1;
if(x0 > x1)
{
dx = -1024;
}
else
{
dx = 1024;
}
if(y0 <= y1)
{
dy = ((y1 - y0 + 1) << 10) / len;
}
else
{
dy = ((y1 - y0 - 1) << 10) / len;
}
}
else
{
len = dy + 1;
if(y0 > y1)
{
dy = -1024;
}
else
{
dy = 1024;
}
if(x0 <= x1)
{
dx = ((x1 - x0 + 1) << 10) / len;
}
else
{
dx = ((x1 - x0 - 1) << 10) / len;
}
}
for(i = 0; i < len; i++)
{
sht->buf[(y >> 10) * sht->bxsize + (x >> 10)] = col;
x += dx;
y += dy;
}
return;
}
总的来说,就是在变化量较小的方向上变化1个像素。其实图形学的书貌似也有这个算法)
然后是汇编调用
_api_linewin: ; void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
PUSH EDI
PUSH ESI
PUSH EBP
PUSH EBX
MOV EDX,13
MOV EBX,[ESP+20] ; win
MOV EAX,[ESP+24] ; x0
MOV ECX,[ESP+28] ; y0
MOV ESI,[ESP+32] ; x1
MOV EDI,[ESP+36] ; y1
MOV EBP,[ESP+40] ; col
INT 0x40
POP EBX
POP EBP
POP ESI
POP EDI
RET
最后是测试程序
void HariMain(void)
{
char *buf;
int win, i;
api_initmalloc();
buf = api_malloc(160 * 100);
win = api_openwin(buf, 160, 100, -1, "lines");
for(i = 0; i < 8; ++i)
{
api_linewin(win + 1, 8, 26, 77, i * 9 + 26, i);
api_linewin(win + 1, 88, 26, i * 9 + 88, 89, i);
}
api_refreshwin(win, 6, 26, 154, 90);
api_end();
}
没什么问题。。
关闭窗口
harib20e
虽然可以结束应用程序,但是窗口却没有被释放,所以仍然留在屏幕上。
添加一个关闭窗口的API
关闭窗口
EDX=14
EBX=窗口句柄
case 14:
sheet_free((struct SHEET *)ebx);
break;
汇编调用
_api_closewin: ; void api_closewin(int win);
PUSH EBX
MOV EDX,14
MOV EBX,[ESP+8] ; win
INT 0x40
POP EBX
RET
测试程序
void HariMain(void)
{
char *buf;
int win, i;
api_initmalloc();
buf = api_malloc(160 * 100);
win = api_openwin(buf, 160, 100, -1, "lines");
for(i = 0; i < 8; ++i)
{
api_linewin(win + 1, 8, 26, 77, i * 9 + 26, i);
api_linewin(win + 1, 88, 26, i * 9 + 88, 89, i);
}
api_refreshwin(win, 6, 26, 154, 90);
api_closewin(win);
api_end();
}
多了一个close,但是这样一开始就被关闭了。。更严格地说,应用程序一开始就结束了,留下了没被释放的窗口在屏幕上。所以我们其实应该等待一个。。输入再结束应用程序
键盘输入API
harib20f
当一个程序等待IO的时候,就让他顺便休眠吧。
键盘输入
EDX=15
EAX=0 没有键盘输入时返回-1,不休眠
EAX=1 休眠直到发生键盘输入
EAX=输入的字符编码
case 15:
for(;;)
{
io_cli();
if(fifo32_status(&task->fifo) == 0)
{
if(eax != 0)
{
task_sleep(task); // FIFO为空,休眠并等待
}
else
{
io_sti();
reg[7] = -1;
return 0;
}
}
i = fifo32_get(&task->fifo);
io_sti();
if(i <= 1) // 光标用定时器
{
// 应用程序运行时不需要显示光标,因此总是将下次显示用的位置置为1
timer_init(cons->timer, &task->fifo, 1); // 下次置为1
timer_settime(cons->timer, 50);
}
if(i == 2) // 光标ON
{
cons->cur_c = COL8_FFFFFF;
}
if(i == 3) // 光标OFF
{
cons->cur_c = -1;
}
if(256 <= i && i <= 511) // 键盘数据(通过任务A)
{
reg[7] = i - 256;
return 0;
}
}
break;
当接收到键盘数据,或者FIFO为空的时候,会结束掉这个循环。
在
timer_init(cons->timer, &task->fifo, 1);
这句中,timer加入到了cons里面,因此CONSOLE结构做了修改,同时console里的task的timer就去掉了。
然后是汇编接口
_api_getkey: ; int api_getkey(int mode);
MOV EDX,15
MOV EAX,[ESP+4] ; mode
INT 0x40
RET
测试程序
void HariMain(void)
{
char *buf;
int win, i;
api_initmalloc();
buf = api_malloc(160 * 100);
win = api_openwin(buf, 160, 100, -1, "lines");
for(i = 0; i < 8; ++i)
{
api_linewin(win + 1, 8, 26, 77, i * 9 + 26, i);
api_linewin(win + 1, 88, 26, i * 9 + 88, 89, i);
}
api_refreshwin(win, 6, 26, 154, 90);
for(;;)
{
if(api_getkey(1) == 0x0a)
{
break; // 按下回车键则break
}
}
api_closewin(win);
api_end();
}
这样在窗口关闭前,会等待一个enter的输入,之后才会关闭、结束
用键盘输入来消遣一下
harib20g
可以移动的*
void HariMain(void)
{
char *buf;
int win, i, x, y;
api_initmalloc();
buf = api_malloc(160 * 100);
win = api_openwin(buf, 160, 100, -1, "walk");
api_boxfilwin(win, 4, 24, 155, 95, 0 /* 黑色 */);
x = 76;
y = 56;
api_putstrwin(win, x, y, 3 /* 黄色 */, 1, "*");
for(;;)
{
i = api_getkey(1);
api_putstrwin(win, x, y, 0 /* 黑色 */, 1, "*"); // 用黑色擦除
if(i == '4' && x > 4) { x -= 8; }
if(i == '6' && x < 148) { x += 8; }
if(i == '8' && y > 24) { y -= 8; }
if(i == '2' && y < 80) { y += 8; }
if(i == 0x0a) { break; } // 按回车键结束
api_putstrwin(win, x, y, 3 /* 黄色 */, 1, "*");
}
api_closewin(win);
api_end();
}
强制结束并关闭窗口
harib20h
如果进行强制结束,程序会来不及执行关闭窗口的操作。
给出的一种解法是将图层和任务绑定,当有程序被强制结束时,若有图层是该任务的,则一同关闭。
首先在SHEET的结构体里添加task指针。在SHEET初始化的时候,默认task为0
当API申请图层的时候,将其与任务绑定
case 5:
sht = sheet_alloc(shtctl);
sht->task = task;
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); // 背景高度3位于task_a之上
reg[7] = (int)sht;
break;
在控制台start_app结束之后,增加查找SHEET的代码
shtctl = (struct SHTCTL *)*((int *) 0x0fe4);
for(i = 0; i < MAX_SHEETS; ++i)
{
sht = &(shtctl->sheets0[i]);
if(sht->flags != 0 && sht->task == task)
{
// 找到被应用程序遗留的窗口
sheet_free(sht); // 关闭
}
}
这样在忘记手动释放窗口的时候,也可以关闭窗口了。