【读书笔记-《30天自制操作系统》-22】Day23

news2024/9/20 1:13:02

本篇内容比较简单,集中于显示问题。首先编写了应用程序使用的api_malloc,然后实现了在窗口中画点与画线的API与应用程序。有了窗口显示,还要实现关闭窗口的功能,于是在键盘输入API的基础上实现了按下按键关闭窗口。最后发现用上文的强制结束按键结束应用程序,程序的窗口还没有关闭,又增加了强制结束程序时关闭窗口的功能。
在这里插入图片描述

1. 实现api_malloc

在上一篇中,显示窗口定义了char buf[150 * 50]。编译之后,相当于增加了150*50个字节的0x00,导致编译后的应用程序大了很多。为了精简应用程序的大小,这里改为使用分配的内存空间。

为应用程序分配内存空间,不能直接使用操作系统管理内存的memman_alloc函数。因为应用程序不能直接读写操作系统管理的内存,这样会产生异常并强制结束应用程序。应用程序可以读写的仅仅是操作系统为其分配好的内存空间,位于数据段中。

这里我们在应用程序中写好需要使用的内存大小。指定内存大小的数值后,会和栈等的大小相累加,写入到.hrb文件最开头的4个字节中。这个内存空间在数据段中的开始位置,被保存在.hrb文件的0x0020处。
设计API如下:

menman初始化:

  • EDX = 8
  • EBX = memman的地址
  • EAX = memman所管理内存空间的起始地址
  • ECX = memman所管理的内存空间的字节数

malloc:

  • EDX = 9
  • EBX = memman的地址
  • ECX = 需要请求的字节数
  • EAX = 分配到的内存空间地址

free:

  • EDX = 10
  • EBX = memman的地址
  • EAX = 需要释放的内存空间地址
  • ECX = 需要释放的字节数

根据以上设计,在hrb_api中增加以下处理:

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
	int ds_base = *((int *) 0xfe8);
	struct TASK *task = task_now();
	struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	struct SHEET *sht;
	int *reg = &eax + 1;	
		/* reg[0] : EDI,   reg[1] : ESI,   reg[2] : EBP,   reg[3] : ESP */
		/* reg[4] : EBX,   reg[5] : EDX,   reg[6] : ECX,   reg[7] : 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) {
		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;
	} else if (edx == 6) {
		sht = (struct SHEET *) ebx;
		putfonts8_asc(sht->buf, sht->bxsize, esi, edi, eax, (char *) ebp + ds_base);
		sheet_refresh(sht, esi, edi, esi + ecx * 8, edi + 16);
	} else if (edx == 7) {
		sht = (struct SHEET *) ebx;
		boxfill8(sht->buf, sht->bxsize, ebp, eax, ecx, esi, edi);
		sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
	} else if (edx == 8) {
		memman_init((struct MEMMAN *) (ebx + ds_base));
		ecx &= 0xfffffff0;	/* 以16字节为单位 */
		memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
	} else if (edx == 9) {
		ecx = (ecx + 0x0f) & 0xfffffff0; /* 以16字节为单位进位取整 */
		reg[7] = memman_alloc((struct MEMMAN *) (ebx + ds_base), ecx);
	} else if (edx == 10) {
		ecx = (ecx + 0x0f) & 0xfffffff0; /* 以16字节为单位进位取整 */
		memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
	}
	return 0;
}

应用程序调用的api:

_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

应用程序:

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_initmalloc(void);
char *api_malloc(int size);
void api_end(void);

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();
}

运行程序之后可以实现同样的功能,但是应用程序文件大大减小了。
在这里插入图片描述

2. 画点、线与刷新窗口

接下来就进入图形处理,分别来描绘点和线。

1.1 描绘点

在窗口中画点的设计很简单:

  • EDX = 11
  • EBX = 窗口句柄
  • ESI = 显示位置的x坐标
  • EDI = 显示位置的y坐标
  • EAX = 色号

应用程序调用的api和应用程序的代码也比较简单。

应用程序调用API:

_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

操作系统API:

……
else if (edx == 11) 
{
	sht = (struct SHEET *) ebx;
	sht->buf[sht->bxsize * edi + esi] = eax;
	sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
}
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_point(int win, int x, int y, int col);
void api_end(void);

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();
}

实现的功能也比较简单,只是在窗口中显示一个点:

在这里插入图片描述
还可以用随机数的方式在窗口中显示多个点:

int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_point(int win, int x, int y, int col);
void api_end(void);

int rand(void);		/* 0-32767之间的随机数 */

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();
}

在这里插入图片描述

在上面生成多个点的程序中,每生成一个点都会执行一次刷新窗口的操作。其实可以在描绘完所有点之后统一进行一次刷新。刷新窗口的句柄是struct SHEET的地址,是一个偶数。这样可以在所有窗口绘图命令中设置一个选项和一个仅用于刷新的API:

……
else if (edx == 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);
	}
}
else if (edx == 12) 
{
	sht = (struct SHEET *) ebx;
	sheet_refresh(sht, eax, ecx, esi, edi);
}
……

以上的处理,当ebx结尾为0,即偶数时,执行刷新窗口的操作。应用程序调用的执行刷新的API:

_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

修改后的应用程序:

int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_point(int win, int x, int y, int col);
void api_end(void);

int rand(void);		/* 0-32767的随机数 */

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();
}

1.2 描绘直线

描绘直线的基本方法:

for(i = 0; i < len; i++)
{
api_point(win, x, y, col);
x += dx;
y += dy;
}

len为线的长度,x,y为线的起始坐标,dx和dy表示线延伸的方向。
dx和dy既不能太大,导致线看起来像虚线,又不能太小,导致CPU多次在同一个坐标画点。
程序如下:

……
else if (edx == 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);
	}
}
……
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;
}

当前还不能处理小数。为了处理dx,dy更加精确,采用了将x和y预先扩大1024倍的方法,这样增加1024实际上就是增加1。
可以看出计算长度的方法比较粗糙,通过比较起点和终点的x,y坐标,将变化比较大的一方加上1作为长度len。这是因为当起点与终点相同时,需要在画面上显示一个点。而计算dx和dy时应用了一个小技巧:根据直线延伸的方向,将变化较大的一方设为1024或-1024,而将变化较小的一方的变化量除以len。这样的计算方式能够使描绘出的直线更平滑一些。
应用程序代码:

_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
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
void api_end(void);

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();
}

运行程序,绘制出了几条直线:
在这里插入图片描述

3. 键盘输入API

接下来我们来做通过键盘按键结束API的功能。

键盘输入

  • EDX = 15
  • EAX = 0——没有键盘输入时返回-1,不休眠
  • EAX = 1——休眠直到发生键盘输入
  • EAX = 输入的字符编码
else if (edx == 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;
			}
		}
	}

由于应用程序窗口的光标显示需要用到定时器,于是也将其包含到struct CONSOLE之中了。

struct CONSOLE {
	struct SHEET *sht;
	int cur_x, cur_y, cur_c;
	struct TIMER *timer;
};

键盘输入的API如下:

_api_getkey:		; int api_getkey(int mode);
		MOV		EDX,15
		MOV		EAX,[ESP+4]	; mode
		INT		0x40
		RET
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
void api_closewin(int win);
int api_getkey(int mode);
void api_end(void);

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();
}

运行程序,显示出直线;按下回车键,窗口消失。
在这里插入图片描述

接下来用键盘实现一个小程序:

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_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
void api_closewin(int win);
int api_getkey(int mode);
void api_end(void);

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();
}

运行程序,窗口中的’*'在按下2,4,6,8键时可以上下左右地移动。
在这里插入图片描述

4. 关闭窗口

最后还有一个问题,使用上一篇完成的Shift+F1按键结束程序时,程序结束后窗口仍然保留在屏幕上。因为上一篇的强制结束按键在结束程序后没有消除窗口,因此这一结果也在预料之中。

解决这个问题也很简单。在struct SHEET中添加一个用来存放task的成员,当应用程序结束时,查询所有的图层,找到图层中与结束的程序一样的task,将该图层关闭就可以了。

修改struct SHEET:

struct SHEET {
	unsigned char *buf;
	int bxsize, bysize, vx0, vy0, col_inv, height, flags;
	struct SHTCTL *ctl;
	struct TASK *task;
};

需要修改的程序代码:

struct SHEET *sheet_alloc(struct SHTCTL *ctl)
{
	struct SHEET *sht;
	int i;
	for (i = 0; i < MAX_SHEETS; i++) {
		if (ctl->sheets0[i].flags == 0) {
			sht = &ctl->sheets0[i];
			sht->flags = SHEET_USE; /* 正在使用 */
			sht->height = -1; /* 不显示 */
			sht->task = 0;	/* 不使用自动关闭功能 */
			return sht;
		}
	}
	return 0;	
}
……
	} else if (edx == 5) {
		sht = sheet_alloc(shtctl);
		sht->task = task; //给sht->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);	
		reg[7] = (int) sht;
	}
……
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
……
	struct SHTCTL *shtctl;
	struct SHEET *sht;
……


	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;
}

这样在运行程序时,按下Shift+F1,结束程序的同时也可以关闭窗口了。
在这里插入图片描述

本篇内容比较简单,思路清晰,程序代码也不需要多作解释,大多还是利用前面完成的内容。下一篇中将继续开发窗口的功能,敬请期待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2147482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初学者笔记本电脑玩转大模型系列二:微调谷歌Gemma模型

之前分享了《初学者笔记本电脑玩转大模型系列一&#xff1a;利用ollama跑大模型》&#xff0c;这不&#xff0c;Google发布了Gemma开放模型&#xff0c;同等参数尺寸性能最好的大模型&#xff0c;那么言归正传&#xff0c;如何在笔记本电脑微调Gemma模型呢&#xff1f;我们接下…

阿里云CTO:通义稳居全球最强开源大模型,性能接近GPT-4o

来源&#xff1a;首席数智官 9月19日&#xff0c;在2024杭州云栖大会上&#xff0c;阿里云CTO周靖人表示&#xff0c;阿里云正在围绕AI时代&#xff0c;树立一个AI基础设施的新标准&#xff0c;全面升级从服务器到计算、存储、网络、数据处理、模型训练和推理平台的技术架构体…

Temu应用全球下载量突破7.35亿次!美国占27%,还是主要市场

据了解&#xff0c;随着购物者更多选择Temu作为实惠的在线购物目的地&#xff0c;其月访问量在今年第一季度跃升至5亿次以上&#xff0c;应用下载量也大幅增加。据外媒报道&#xff0c;根据Stocklytics的最新数据&#xff0c;Temu应用程序截至目前的下载量已超过7.35亿次。 最新…

动态线程池(二)

动态线程池 环境搭建 启动Nacos和redis 安装Node-Exporter 安装Prometheus 安装Grafana 源码解析

鸢尾花书实践和知识记录[数学要素3-2乘除]

书的作者 文章目录 算术乘除&#xff1a;先乘除&#xff0c;后加减&#xff0c;括号内先算基本的乘法运算计算阶乘基本除法 向量的乘法&#xff1a;标量乘法&#xff0c;向量内积&#xff0c;逐项积标量乘法向量的内积对于inner和dot的实现方式的探究逐项积dot的计算过程 逐项…

MAC如何获取文件数字签名和进程名称

1、安装需要查看数字签名和进程名称的软件包 2、打开终端命令行(Terminal) 3、查找数字签名 在终端命令行中输入: codesign -dvv 安装的软件路径 2>&1 | grep "Authority=" | head -n 1 | cut -d = -f2”

PDF使用虚拟列表技术做渲染和加载带来的问题

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

JavaFX实现视频播放功能

一、前言 最近使用javaFx写了个简单的视频播放功能&#xff0c;可以实现打开本地视频播放。 二、实现 1.使用jdk8自带的javaFx包实现&#xff0c;首先定义一个类VideoPlayer。 代码如下&#xff1a; import javafx.application.Application; import javafx.application.Platf…

LAN8720A-CP-TR-ABC QFN-24 以太网收发器芯片

功能&#xff1a; 高性能收发器&#xff1a;支持10BASE-T和100BASE-TX标准&#xff0c;能够自动协商最佳速度和双工模式。 小尺寸&#xff1a;是业界尺寸最小的解决方案&#xff0c;适合空间受限的应用场景。 低功耗&#xff1a;功耗比现有的Microchip收发器低40%&#x…

【Unity】URP Rendering总结

unity-urp-rendering 介绍 个人学习总结&#xff0c;不定期更新 仓库 Unity版本&#xff1a;2022.3.42 Unity URP渲染管线下相关的渲染demo和总结 1. GPUInstance 1.1 Graphics.DrawMeshInstanced 1.2 Graphics.DrawMeshInstancedIndirect 1.3 MeshRenderer.SetPropertyBlock…

MTK芯片机型的“工程固件” 红米note9 5G版资源预览 写入以及改写参数相关步骤解析

小米机型:小米5 小米5x 米6 米6x 米8 米9 米10系列 米11系列 米12系列 mix mix2 mix2s mix3 max max2 max3 note3 8se 9se cc9系列 米play 平板系列等分享 红米机型:红米note4 红米note4x 红米note5 红米note6 红米note7 红米note8 红米note8pro 红米s2 红米note7pro 红米…

【C++算法】模拟算法

替换所有的问号 题目链接 替换所有的问号https://leetcode.cn/problems/replace-all-s-to-avoid-consecutive-repeating-characters/description/ 算法原理 代码步骤 class Solution { public:string modifyString(string s) {int n s.size();for(int i 0; i < n; i){…

网络药理学:15、草稿暂存区

TCMSP 韦恩图在线网站 https://bioinfogp.cnb.csic.es/tools/venny/index.html String数据库参数详解&#xff1a;https://www.bilibili.com/video/BV1q64y1k7Zf?p16&vd_sourceaed4c634975918b14b7354ec93ce5389 David数据库可以用基因ID或者基因名。 KEGG数据库使用&am…

高效处理NPE!!

相信不少小伙伴已经被java的NPE(Null Pointer Exception)所谓的空指针异常搞的头昏脑涨,有大佬说过“防止 NPE&#xff0c;是程序员的基本修养。”但是修养归修养&#xff0c;也是我们程序员最头疼的问题之一&#xff0c;那么我们今天就要尽可能的利用Java8的新特性 Optional来…

视觉语言大模型模型介绍-CLIP学习

多模态学习领域通过结合图像和文本信息&#xff0c;为各种视觉语言任务提供了强大的支持。图像和文本的结合在人工智能领域具有重要的意义&#xff0c;它使得机器能够更全面地理解人类的交流方式。通过这种结合&#xff0c;模型能够处理包括图像描述、视觉问答、特征提取和图像…

Maya动画基础

Maya动画基础教程&#xff08;完整&#xff09;_哔哩哔哩_bilibili 第一集 动画基础设置 altv播放动画 选择撕下副本 右键---播放预览 第二集 k帧记录物体的空间信息 初始位置清零 删除历史记录 s键key帧 自动记录位置信息 删除帧&#xff0c;按住右键选择delete 按shif…

Apache subversion 编译流程

目录 1. 概述2. 依赖库简介2.1 Expat2.2 Apache apr2.3 Apache apr-iconv2.4 Apache apr-util2.5 Zlib2.6 OpenSSL2.7 Sqlite2.8 Apache Serf2.9 Apache subversion3. 编译3.1 Expat编译3.1.1 源码信息3.1.2 CMake-GUI3.1.3 编译步骤3.2 APR编译3.2.1 源码信息3.2.2 编译步骤3.…

详解c++菱形继承和多态---下

菱形继承 #include<iostream>using namespace std; class Animal { public:int m_Age; }; class Sheep : public Animal {}; class Tuo : public Animal {}; class SheepTuo : public Sheep, public Tuo {}; void test() {SheepTuo st;st.Sheep::m_Age 18;st.Tuo::m_Age…

数据结构-3.1.栈的基本概念

一.栈的定义&#xff1a; 栈和线性表的区别&#xff1a;栈只能在表尾一端进行插入或者删除的操作&#xff0c;而线性表可以在任意一个地方进行插入或者删除 二.有关栈的关键术语&#xff1a; 三.栈的基本操作&#xff1a; 1.回顾线性表的基本操作&#xff1a; 2.栈的基本操作&…

vue使用vue-i18n实现国际化

我使用的是vue2.6版本&#xff0c;具体使用其他版本可以进行修改 一、安装 npm install vue-i18n -D 二、配置 1、文件配置 ①在src下创建 i18n 目录 ②在 i18n 目录下创建 langs 文件夹 和 index.js文件&#xff0c;具体如下 2、index.js代码如下&#xff0c;这里使用了…