Skip to content

day7FIFO与鼠标控制

鼠标数据的解读

c
for (;;) {
    io_cli();
    if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
        io_stihlt();
    } else {
        if (fifo8_status(&keyfifo) != 0) {
            //这里是键盘的处理程序
            i = fifo8_get(&keyfifo);
            io_sti();
            sprintf(s, "%02X", i);
            boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);
            putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
        } else if (fifo8_status(&mousefifo) != 0) {
            //获取第一个数据
            i = fifo8_get(&mousefifo);
            io_sti();
            //这一个变量是用来记录获得的数据的位数
            if (mouse_phase == 0) {
                /* 等待0xfa的状态标志位,这是初始化完成之后的返回值 */
                if (i == 0xfa) {
                    mouse_phase = 1;
                }
            } else if (mouse_phase == 1) {
                //初始化完成之后获得的第一个数据
                /* 获取第一字节的数据 */
                mouse_dbuf[0] = i;
                mouse_phase = 2;
            } else if (mouse_phase == 2) {
                /* 第二字节的数据 */
                mouse_dbuf[1] = i;
                mouse_phase = 3;
            } else if (mouse_phase == 3) {
                /* 第三字节的数据 */
                mouse_dbuf[2] = i;
                //把数据的位数重新恢复成1
                mouse_phase = 1;
                /* 获得三个数据之后把数据打印出来 */
                sprintf(s, "%02X %02X %02X", mouse_dbuf[0], mouse_dbuf[1], mouse_dbuf[2]);
                boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);
                putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
            }
        }
    }
}

整理

c
struct MOUSE_DEC {
    //三个存放数据的位置,以及一个标志位
	unsigned char buf[3], phase;
};

创建一个鼠标的结构体

c
void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	char s[40], mcursor[256], keybuf[32], mousebuf[128];
	int mx, my, i;
	struct MOUSE_DEC mdec;

	init_gdtidt();
	init_pic();
	io_sti(); /* 打开中断 */
	fifo8_init(&keyfifo, 32, keybuf);
	fifo8_init(&mousefifo, 128, mousebuf);
	io_out8(PIC0_IMR, 0xf9); /* 中断屏蔽 */
	io_out8(PIC1_IMR, 0xef); 

	init_keyboard();

	init_palette();
	init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
	mx = (binfo->scrnx - 16) / 2; /* 画面中央になるように座標計算 */
	my = (binfo->scrny - 28 - 16) / 2;
	init_mouse_cursor8(mcursor, COL8_008484);
	putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
	sprintf(s, "(%d, %d)", mx, my);
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
	//使能鼠标, 初始化鼠标的结构体
	enable_mouse(&mdec);

	for (;;) {
		io_cli();
		if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
			io_stihlt();
		} else {
			if (fifo8_status(&keyfifo) != 0) {
				i = fifo8_get(&keyfifo);
				io_sti();
				sprintf(s, "%02X", i);
				boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);
				putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
			} else if (fifo8_status(&mousefifo) != 0) {
				i = fifo8_get(&mousefifo);
				io_sti();
				if (mouse_decode(&mdec, i) != 0) {
					/* 有三个字节进行处理 */
					sprintf(s, "%02X %02X %02X", mdec.buf[0], mdec.buf[1], mdec.buf[2]);
					boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);
					putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
				}
			}
		}
	}
}
c
//鼠标数据的接受
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{
	if (mdec->phase == 0) {
		/* 等待初始化 */
		if (dat == 0xfa) {
			mdec->phase = 1;
		}
		return 0;
	}
	if (mdec->phase == 1) {
		/* 接受第一个数据 */
		mdec->buf[0] = dat;
		mdec->phase = 2;
		return 0;
	}
	if (mdec->phase == 2) {
		mdec->buf[1] = dat;
		mdec->phase = 3;
		return 0;
	}
	if (mdec->phase == 3) {
		mdec->buf[2] = dat;
		mdec->phase = 1;
		return 1;
	}
	return -1; /* 出错了 */
}

鼠标信息的解读

c
struct MOUSE_DEC {
	unsigned char buf[3], phase;
    //存放鼠标的位置信息,以及按键的属性
	int x, y, btn;
};
c
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{
	if (mdec->phase == 0) {
		/* マウスの0xfaを待っている段階 */
		if (dat == 0xfa) {
			mdec->phase = 1;
		}
		return 0;
	}
	if (mdec->phase == 1) {
		/* 添加了一个判断, 判断移动数据的位置信息以及按键信息是否符合标准
        1. 有移动反应的是否在0~3之间
        2. 第一字节的按键状态是否在8~f之间
        0xc8 = 11001000*/
		if ((dat & 0xc8) == 0x08) {
			/* 第一字节正确 */
			mdec->buf[0] = dat;
			mdec->phase = 2;
		}
		return 0;
	}
	if (mdec->phase == 2) {
		mdec->buf[1] = dat;
		mdec->phase = 3;
		return 0;
	}
	if (mdec->phase == 3) {
		mdec->buf[2] = dat;
		mdec->phase = 1;
        //对数据进行解读
        //获取表示状态的低三位
		mdec->btn = mdec->buf[0] & 0x07;
		mdec->x = mdec->buf[1];
		mdec->y = mdec->buf[2];
        //通过第一字节对数据进行处理
		if ((mdec->buf[0] & 0x10) != 0) {
			mdec->x |= 0xffffff00;
		}
		if ((mdec->buf[0] & 0x20) != 0) {
			mdec->y |= 0xffffff00;
		}
		mdec->y = - mdec->y; /* マウスではy方向の符号が画面と反対 */
		return 1;
	}
	return -1; /* ここに来ることはないはず */
}

鼠标按键的状态在第一数据的低三位, 并通过第一字节的地两位确定是否对xy的值高位设置为1

c
for (;;) {
    io_cli();
    if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
        io_stihlt();
    } else {
        if (fifo8_status(&keyfifo) != 0) {
            i = fifo8_get(&keyfifo);
            io_sti();
            sprintf(s, "%02X", i);
            boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);
            putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
        } else if (fifo8_status(&mousefifo) != 0) {
            i = fifo8_get(&mousefifo);
            io_sti();
            if (mouse_decode(&mdec, i) != 0) {
                /* 有按键按下的话修改显示的内容 */
                sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
                if ((mdec.btn & 0x01) != 0) {
                    s[1] = 'L';
                }
                if ((mdec.btn & 0x02) != 0) {
                    s[3] = 'R';
                }
                if ((mdec.btn & 0x04) != 0) {
                    s[2] = 'C';
                }
                boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
                putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
            }
        }
    }
}

之后进行显示的修改

通往32位的模式之路

assembly
; haribote-os boot asm
; TAB=4

BOTPAK	EQU		0x00280000		; bootpack文件起始地址设置地址
DSKCAC	EQU		0x00100000		
DSKCAC0	EQU		0x00008000		

; 记录一些数据
CYLS	EQU		0x0ff0			
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			
SCRNX	EQU		0x0ff4			
SCRNY	EQU		0x0ff6			
VRAM	EQU		0x0ff8			

		ORG		0xc200			;设置文件的地址

; 画面的一些设置

		MOV		AL,0x13			; 对画面进行设置
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	; 画面设置的记录
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000

; キーボードのLED状態をBIOSに教えてもらう

		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS获取键盘的数据
		MOV		[LEDS],AL

; PIC关闭所有的中断
;	在这里初始化PIC
;	必须在CLI之前,否则可能会挂起
;	之后践行初始化

		MOV		AL,0xff
		OUT		0x21,AL
		NOP						; 设置PIC禁止所有的中断,这里有一个停顿, 硬件需求
		OUT		0xa1,AL

		CLI						; 禁止CPU的所有中断

; 为了让CPU可以访问1MB以外的内存, 所以需要设定A20GATE, 实际上设置的是地址位使用16位以上的线路

		CALL	waitkbdout		; 这是一个循环等待硬件响应
		MOV		AL,0xd1	
		OUT		0x64,AL			; 键盘的一个附属端口
		CALL	waitkbdout
		MOV		AL,0xdf			; enable A20, 发送命令
		OUT		0x60,AL
		CALL	waitkbdout

; 切换到保护模式

[INSTRSET "i486p"]				; 486命令使用的标志, 这是一个命令, 以后可以使用LGDT, EAX, CR0等, CR0是系统控制寄存器

		LGDT	[GDTR0]			; 设置一个临时的GDT, 参数是六位的, 分别是长度以及位置
		MOV		EAX,CR0
		AND		EAX,0x7fffffff	; bit31为0(禁止分页)
		OR		EAX,0x00000001	; bit0位1(切换为保护模式)
		MOV		CR0,EAX
		JMP		pipelineflush	; 在设置以后需要使用一次JMP命令
pipelineflush:
		MOV		AX,1*8			;  之后会把除了CS之外的段寄存器设置为GDT1(低三位无效, 从8开始计算)
		MOV		DS,AX
		MOV		ES,AX
		MOV		FS,AX
		MOV		GS,AX
		MOV		SS,AX

; bootpack的复制

		MOV		ESI,bootpack	; 传送的原地址, 这里设置的是在这个文件之后的位置, 从Makefile得知是C文件的位置
		MOV		EDI,BOTPAK		; 传送的目的地址
		MOV		ECX,512*1024/4
		CALL	memcpy

; 磁盘的数据存送到本来的位置

; 从启动区开支

		MOV		ESI,0x7c00		; 原位置
		MOV		EDI,DSKCAC		; 目的地
		MOV		ECX,512/4
		CALL	memcpy

; 剩下的所有

		MOV		ESI,DSKCAC0+512	; 原地址
		MOV		EDI,DSKCAC+512	; 目的地址
		MOV		ECX,0
		MOV		CL,BYTE [CYLS]
		IMUL	ECX,512*18*2/4	; 计算大小
		SUB		ECX,512/4		
		CALL	memcpy

; asmhead完成工作
;	以后交给bootpack完成

; bootpack启动

		MOV		EBX,BOTPAK
		MOV		ECX,[EBX+16]
		ADD		ECX,3			; ECX += 3;
		SHR		ECX,2			; ECX /= 4;
		JZ		skip			; 没有传送的东西的时候, 这些数字是根据bin文件得知的
		MOV		ESI,[EBX+20]	; 复制的原位置
		ADD		ESI,EBX
		MOV		EDI,[EBX+12]	; 目的位置
		CALL	memcpy
skip:
		MOV		ESP,[EBX+12]	; 初始化栈
		JMP		DWORD 2*8:0x0000001b	; 开始执行main函数

waitkbdout:
		IN		 AL,0x64
		AND		 AL,0x02
		JNZ		waitkbdout		; ANDの結果が0でなければwaitkbdoutへ
		RET

memcpy:
		MOV		EAX,[ESI]
		ADD		ESI,4
		MOV		[EDI],EAX
		ADD		EDI,4
		SUB		ECX,1
		JNZ		memcpy			; 引き算した結果が0でなければmemcpyへ
		RET
; memcpyはアドレスサイズプリフィクスを入れ忘れなければ、ストリング命令でも書ける

		ALIGNB	16
GDT0:
		; 由于低三位无效,这里设置的实际上是第一个段
		RESB	8				; 0段不可以存东西
		DW		0xffff,0x0000,0x9200,0x00cf	; 可以读写的段32bit
		DW		0xffff,0x0000,0x9a28,0x0047	;可以执行的段bootpack使用

		DW		0
GDTR0:
		DW		8*3-1	
		DD		GDT0

		ALIGNB	16		; 添加DB0到地址可以被16整除
bootpack:
  1. 禁止所有中断
  2. 转换为32位的模式
  3. 由于可以使用的内存增大, 所以把硬盘内容以及C文件的内容重新存储, 把bootpack文件移动到0x00280000位置, 启动区和其他的软盘内容dao0x10000000位置
  • 0x00000000 - 0x000fffff: 启动时候使用的1M内存
  • 0x00100000 - 0x00267fff: 保存软盘
  • 0x00268000 - 0x26f7ff : 空
  • 0x0026f800 - 0x0026ffff IDT
  • 0x00270000 - 0x0027ffff GDT
  • 0x00280000 - 0x002fffff bootpack.hrb
  • 0x00300000 - 0x003fffff 栈
  • 0x00400000 - 其他