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= 需要释放的字节数 |

_api_initmalloc:			; void api_initmalloc(void);
	PUSH 	EBX
	MOV 	EDX,8
	MOV 	EBX,[CS:0x0020] ; 初始化地址 P494页 0x0020 (DWORD) ......malloc空间的起始地址
	MOV 	EAX,EBX 
	ADD		EAX,32*1024		;分配了32k内存
	MOV 	ECX,[CS:0x0000]
	SUB		ECX,EAX			;包括前32位数据(文件信息)以及32k内存
	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

23.2 画点

该节实现绘制一个点的功能,下面先介绍一下API的设计:
EDX =11
EBX = 窗口句柄
ESI = 显示位置的 x 坐标
EDI = 显示位置的 y 坐标
EAX = 色号

/*                a_nask.nas                */
_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

/*                console.c                */
if(edx == 11){//api_point
	sht = (struct SHEET *) ebx;
	sht->buf[sht->bxsize * edi + esi] = eax;//修改某个点的颜色
	sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
}

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作为字符串的地址,是否为空,如果为空则刷新图层。

/*                a_nask.nas                */
_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

/*                console.c                */
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax){
	(中略)
	if(edx == 1)
		cons_putchar(cons, eax&0xff, 1);
	else if(edx == 2)
		cons_putstr0(cons, (char*)ebx+ds_base);
	else if(edx == 3)
		cons_putstr1(cons, (char*)ebx+ds_base, ecx);
	else if(edx == 4)  
		return &(task->tss.esp0); 
	else if(edx == 5){
		(中略)
	}else if(edx == 6){
/*这里*/sht = (struct SHEET *) (ebx&0xfffffffe);
		(中略)
/*这里*/if(ebx&1 == 0)
/*这里*/	sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
	}else if(edx == 8){//memman初始化
		(中略)
	}else if(edx == 9){//malloc
		(中略)
	}else if(edx == 10){//free
        ecx = (ecx+0x0f)&0xfffffff0;//所释放的内存空间的字节数
        memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
	}else if(edx == 11){//api_point
/*这里*/ sht = (struct SHEET *) (ebx&0xfffffffe);
		 sht->buf[sht->bxsize * edi + esi] = eax;
/*这里*/ if(ebx&1 == 0)//当ebx的最低位为0,也就是没有字符串需要打印时,刷新图层
/*这里*/	sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
	}else if(edx == 12){
/*这里*/ sht = (struct SHEET *) ebx;
/*这里*/ sheet_refresh(sht, eax, ecx, esi, edi);
	}
	return 0;
}

23.4 画直线

在窗口上画直线: EDX = 13 EBX = 窗口句柄 EAX = x0 ECX = y0 ESI = x1 EDI = y1 **EBP =**色号。考虑画的是直线,如果点间隙较大则会变成虚线,较小则会可能出现在一个坐标上多次花点浪费时间,所以选择将坐标跨度较大的间隙分为1024,直接看代码。

/*                a_nash.nas                */
_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

/*                console.c                */
if(edx == 13){
	sht = (struct SHEET *) (ebx&0xfffffffe);
	hrb_api_linewin(sht, eax, ecx, esi, edi, ebp);
	if(ebx&1 == 0)//当ebx的最低位为0,也就是没有字符串需要打印时,刷新图层
		sheet_refresh(sht, eax, ecx, esi, edi);
}

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;//-(y0-y1+1) = y1-y0-1
	}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;
}

23.5 关闭窗口

关闭窗口:EDX=14 EBX= 窗口句柄。sheet_free()函数是释放已使用的图层。

/*                a_nask.nas                */
_api_closewin:				;void api_closewin(int win);
	PUSH	EBX
	MOV 	EDX,14
	MOV		EBX,[ESP+8]		;win
	INT		0X40
	POP		EBX
	RET

/*                console.c                */
if(edx == 14){//关闭窗口
	sheet_free((struct SHEET *) ebx);
}

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 取而代之。

/*                a_nask.nas                */
_api_getkey:				;int api_getkey(int mode);
	MOV		EDX,15
	MOV		EAX,[ESP+4]
	INT		0X40
	RET

/*                console.c                */
if(edx == 15){
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			if(eax != 0)task_sleep(task);//休眠
			else{//没有键盘输入时返回-1,不休眠
				io_sti();
				reg[7] = -1;
				return 0;
			}
		}
		i = fifo32_get(&task->fifo);
		if(i <= 1){//光标
			/*应用程序运行时不需要显示光标,因此总是将下次显示用的值置为1*/
			timer_init(cons->timer, &task->fifo, 1);
			timer_settime(cons->timer, 50);
		}
		if(i == 2)cons->cur_c = COL8_FFFFFF;/*光标ON */
		if(i == 3)cons->cur_c = -1;/*光标OFF */
		if(256 <= i && i <= 511){
			reg[7] = i-256;
			return 0;
		}
	}
}

23.7 强制结束并关闭窗口

在运行 walk.hrb 和 lines.hrb 时,如果不按回车键结束,而是按Shift+F1强制结束程序的话,窗口就会残留在画面上。
首先,在 struct SHEET 中添加一个用来存放 task的成员,当应用程序结束时,查询所有的图层,如果图层的 task为将要结束的应用程序任务,则关闭该图层。

int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline){
	int segsiz, datsiz, esp, dathrb;
/*这里*/struct SHEET *sht;
/*这里*/struct SHTCTL *shtctl;
	(中略)
	if (finfo != 0) {
		/* 找到了与字符串相同的文件 */
		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){
			(中略)
			start_app(0x1b, 1003 * 8, esp, 1004*8, &(task->tss.esp0));
/*从这里开始*/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);
/*到这里结束*/}
			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;
	}
	return 0;
}
相关推荐
这是我583 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
NullPointerZZZ12 小时前
vue3和element-plus笔记
其他
gavin_gxh1 天前
SAP PP ECN CSAP_MAT_BOM_MAINTAIN
运维·经验分享·其他
安建资小栗子4 天前
一般行业安全管理人员考试题库分享
其他
cwtlw4 天前
SpringMVC的使用
java·开发语言·笔记·学习·其他
渊鱼L4 天前
ABAQUS随机球体骨料细观混凝土三维圆柱试件轴压开裂
其他
cwtlw5 天前
CSS学习记录14
前端·css·笔记·学习·其他
烁月_o96 天前
《安全工程师自我防护指南:直面数字威胁的有效策略与实践》
运维·网络·经验分享·其他·网络安全
归零-li7 天前
采购网站收集站
其他
爱吃生蚝的于勒7 天前
计算机基础 原码反码补码问题
经验分享·笔记·计算机网络·其他·1024程序员节