《30天自制操作系统》笔记----Day8

1.鼠标解读

我们在移动鼠标时,会同时传来三个参数。
在这里插入图片描述如上图所示,屏幕中出现“38 FE FF”这3字节数字,其中“38”(即mouse_dbuf[0])其中“3”会在0~3范围内变化。同时,只有点击(左击,右击或中间滚轮时)鼠标会改变“8”那部分,变化范围为8 ~ F间变化。其中“FE”部分(mouse_dbuf[1])与鼠标左右移动有关。“FF“部分(mouse_dbuf[2])则与鼠标上下移动有关。
因此,我们在解读鼠标数据时,要注意三个部分(mouse_dbuf 这个数组)

struct MOUSE_DEC {
		unsigned char buf[3], phase;
		int x, y, btn;//其中x,y存放移动信息,btn存放鼠标按键状态。
};
/*******鼠标字节解码**********/
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) {
		/* 等待鼠标第一字节的阶段 */
				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;//0x07=0000 0111 &运算取出低三位,对应前文的“8”
				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; /* 应该不会到这儿来 */
}

将x和y的第8位及第8位以后全部都设成1,或全部都保留为0。这样就能正确地解读x和y。在解读处理的最后,对y的符号进行了取反的操作。这是因为,鼠标与屏幕的y方向正好相反,为了配合画面方向,就对y符号进行了取反操作。

mouse
btn
x
y
置1操作
y取反

2.移动鼠标指针

鼠标的解读工作完成,接下来的工作是让鼠标指针在屏幕上面动起来。

/* 鼠标指针的移动 */
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my +
15); /* 隐藏鼠标 */
mx += mdec.x;
my += mdec.y;
/*防止鼠标跑出去*/
if (mx < 0) {
mx = 0;
}
if (my < 0) {
my = 0;
}
if (mx > binfo->scrnx - 16) {
mx = binfo->scrnx - 16;
}
if (my > binfo->scrny - 16) {
my = binfo->scrny - 16;
}
sprintf(s, "(%3d, %3d)", mx, my);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15); /* 隐藏坐
标 */
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); /* 显示坐
标 */
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); /* 描画鼠标 */
}
}

运行的还行运行的还行。

3.通往32位模式之路

下面开始学习asmhead.nas程序:

; haribote-os boot asm   (声明语句)
; TAB=4
BOTPAK	EQU		0x00280000		; bootpack地址
DSKCAC	EQU		0x00100000		; 
DSKCAC0	EQU		0x00008000		; 
; BOOT_INFO信息
CYLS	EQU		0x0ff0			; 
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 
SCRNX	EQU		0x0ff4			; X方向解析度
SCRNY	EQU		0x0ff6			; y方向解析度
VRAM	EQU		0x0ff8			; VRAM 地址

		ORG		0xc200			; 程序开始的地方

; 画面设定
		MOV		AL,0x13			; VGA显示设置320x200x8bit
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	; 记录画面模式(参照C语言)
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000
; 传达BIOS键盘的LED状态
		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS
		MOV		[LEDS],AL

; PIC关闭一切中断
;	根据AT兼容机的规格,如果要初始化PIC,
; 必须在CLI之前进行,否则有时会挂起。
; 随后进行PIC的初始化。
		MOV		AL,0xff
		OUT		0x21,AL
		NOP						; 如果连续执行OUT指令,有些机种会无法正常运行
		OUT		0xa1,AL

		CLI						; 禁止CPU级别的中断

;  为了让CPU能够访问1MB以上的内存空间,设定**A20GATE**(具体参考下文的文字部分)
		CALL	waitkbdout
		MOV		AL,0xd1
		OUT		0x64,AL
		CALL	waitkbdout
		MOV		AL,0xdf			; enable A20
		OUT		0x60,AL
		CALL	waitkbdout
;切换到保护模式
[INSTRSET "i486p"]				; 即“想使用486指令”
		LGDT	[GDTR0]			; 设定临时GDT 
		MOV		EAX,CR0
		AND		EAX,0x7fffffff	; 设bit31为0(为了禁止分页)
		OR		EAX,0x00000001	; 设bit0为1(为了切换到保护模式)
		MOV		CR0,EAX     ;通过CR0切换到保护模式时,要马上执行JMP指令(下文有详细解释)
		JMP		pipelineflush
pipelineflush:
		MOV		AX,1*8			;  可读写的段 32bit
		MOV		DS,AX
		MOV		ES,AX
		MOV		FS,AX
		MOV		GS,AX
		MOV		SS,AX
; bootpack的转送
		MOV		ESI,bootpack	; 传送源
		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	;从柱面数变换为字节数/4
		SUB		ECX,512/4		;  减去 IPL
		CALL	memcpy ;memcpy(转送源地址, 转送目的地址, 转送数据的大小);
; 必须由asmhead来完成的工作,至此全部完毕
;	之后由bootpack来完成
; bootpack的启动
		MOV		EBX,BOTPAK
		MOV		ECX,[EBX+16]
		ADD		ECX,3			; ECX += 3;
		SHR		ECX,2			; ECX /= 4;
		JZ		skip			; 没有要传送的东西
		MOV		ESI,[EBX+20]	; 传送源
		ADD		ESI,EBX
		MOV		EDI,[EBX+12]	; 传送目的地
		CALL	memcpy
skip:
		MOV		ESP,[EBX+12]	; 栈初始值
		JMP		DWORD 2*8:0x0000001b

waitkbdout:
		IN		 AL,0x64
		AND		 AL,0x02
		IN      AL,0x60 ;空读(为了清空数据接收缓冲区中的垃圾数据)
		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 ;一直添加DBO知道凑成能被16整除的地址,方便向段寄存器复制
GDT0:
		RESB	8				; NULL selector
		DW		0xffff,0x0000,0x9200,0x00cf	; 可以读写的段(segment)32bit
		DW		0xffff,0x0000,0x9a28,0x0047	; 可以执行的段(segment)32bit(bootpack
用)
		DW		0
GDTR0:
		DW		8*3-1
		DD		GDT0

		ALIGNB	16
bootpack:

目前系统的内存分布图:

Class01 0x00268000 - 0x0026f7ff(空) 0x00400000 - (空) 0x00000000 - 0x000fffff(1MB) 0x00100000 - 0x00267fff(保存软盘内容) 0x0026f800 - 0x0026ffff(IDT) 0x00270000 - 0x0027ffff(GDT) 0x00280000 - 0x002fffff(bootpack.hrb) 0x00300000 - 0x003fffff(栈及其他) SYS_OS_ROM_MAP

新知识说明:

其中A20GATE这条信号线能使内存的1MB以上的部分变成可使用的状态。但为了兼容旧版的操作系统(16位),在执行激活指令之前,电路被限制为只能使用1MB内存。和
鼠标的情况很类似。A20GATE信号线正是用来使这个电路停止从而让所有内存都可以使用的东西。

INSTRSET指令:是为了能够使用386以后的LGDT,EAX,CR0等关键字。
LGDT指令:把随意准备的GDT给读进来。
保护模式:在这种模式下,应用程序既不能随便改变段的设定,又不能使用操作系统专用的段。操作系统受到CPU的保护。

**通过代入CR0而切换到保护模式时,要马上执行JMP指令。因为变成保护模式后,机器语言的解释要发生变化。CPU为了加快指令的执行速度而使用了管道(pipeline)这一机制,就是说,前一条指令还在执行的时候,就开始解释下一条甚至是再下一条指令。因为模式变了,就要重新解释一遍,所以加入了JMP指令。

okk结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃人的坤坤坤坤坤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值