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;
}
相关推荐
睿途低空新程10 小时前
面向城市治理的AI集群空域融合模型
人工智能·经验分享·其他·无人机
努力的搬砖人.1 天前
中国科学院大学计算机考研历年初试分数线分析以及计算机所考的科目有哪些?
其他
橙子家2 天前
GitLab CI/CD 的配置文件 .gitlab-ci.yml 简介
其他
老陈头聊SEO4 天前
SEO长尾词优化实战布局
其他
ye150127774555 天前
220V转直流非隔离传感器供电电源芯片WT5105
stm32·单片机·嵌入式硬件·其他·硬件工程
时空无限6 天前
虚无隧穿产生宇宙(true nothing tunneling) 是谁提出的
其他
张高兴8 天前
为什么要对程序进行调试
其他
这是我588 天前
awk命令——功能强大的文本处理工具
linux·其他·shell·awk·强大··
cwtlw11 天前
PhotoShop学习10
笔记·学习·其他·photoshop
stockmasterx11 天前
什么是ETF跟踪误差?场内基金佣金最低是多少?
经验分享·笔记·其他