系统的简单注解
来源:互联网 发布:小额贷款骗局知乎 编辑:程序博客网 时间:2024/06/09 15:51
该文件用于初始化鼠标中断、处理鼠标事件、在屏幕上绘制鼠标。
首先,我们来看看鼠标的控制方法。以下文字摘自哈尔滨工业大学的谢煜波:
根据PS/2协议,鼠标是由键盘的控制器(i8042)进行控制的,键盘控制器(i8042)总共有两个通道,一个通道由键盘使用,另一个通道由鼠标使用,我们对鼠标进行操作也是通过i8042芯片来完成的,因此,现在的重点就是了解并熟悉怎样对i8042进行编程,来完成对鼠标的控制。
i8042支持两种工作模式——AT模式及PS/2模式,这都是由IBM所定义的一些规范,i8042在计算机启动时会自动检测用户是否使用的支持PS/2协议的键盘及鼠标,以决定是否工作在PS/2模式下,现在我们假设我们使用的都是PS/2键盘及鼠标,因此,现在i8042工作在PS/2模式下(请记住这一点,即i8042可以工作在AT模式或者PS/2模式下,并且现在假设其工作在PS/2模式下,这在后面将会用到)。
与i8042有关的I/O端口共有两个,一个是0x60端口,一个是0x64端口,如果我们想获得i8042的状态,我们需要读0x64端口,这将返回i8042中状态寄存器的内容。如果我们想向i8042发送命令,我们需要先把命令发送到0x64端口,然后紧接着把这个命令的参数发送到0x60端口,然后我们可以通过读0x60端口,获得i8042返回给我们的数据。
下面我们就来看看,应当发送什么样的命令去控制鼠标,这涉及到下面几个需要发送给i8042的命令:
0xA8命令:许可i8042的鼠标通道,即允许鼠标操作。
0xD4命令:把发往0x60端口的参数数据发给鼠标。
0x60命令:把发往0x60端口的参数数据写入i8042的控制寄存器。
从上面的分析我们可以基本窥见怎样操作鼠标。首先,我们应向i8042的0x64端口发送0xA8命令,以许可i8042的鼠标通道,以便完成对鼠标的操作。其次我们应向i8042的0x64端口发送0xD4命令,以通知i8042我们需要控制鼠标,并把控制鼠标的命令发到i8042的0x60端口,再从i8042的0x60端口取回鼠标发给我们的数据,这一过程无疑是比较简单的,我们先来看看,我们应向鼠标发送什么样的控制命令,然后再看看实际的代码。
控制鼠标的命令非常之多,比如0xFF命令可以让鼠标复位;0xFE命令可以让鼠标重新发送上次的数据包;0xF3命令可以设置鼠标的采样率,也即鼠标滑动时的灵敏度;0xF4命令可以允许鼠标向主机发送数据包等。这里最重要的就是0xF4命令,而其它的设置鼠标的命令我们暂时可以不用理会,因为使用默认值已经能很好的完成本实验了。要理解0xF4命令有什么作用,我们需要先了解一下鼠标的四种工作模式:Reset模式,Stream模式,Remote模式及Wrap模式。
首先是Reset模式,当鼠标初次加电或收到0xFF命令之后,鼠标就处于此模式,然后鼠标将进行一系列的初始化及检测工作,包括设定默认的采样率等,完成初始化极检测之后,鼠标将进入Stream模式。
在Stream模式下,如果鼠标被移动,或者有键被按下,那么鼠标将向主机发送数据包,并提请一个中断请求,主机需要响应此中断,并在中断处理程序中取得鼠标发送的数据包。如果在Stream模式下,我们向鼠标发送0xF0命令,将使鼠标进入Remote模式。
Remote模式同Stream模式差不多,主要工作就是检测鼠标是否被移动及是否有键被按下,不过它与Stream模式的区别在于,它并不会主动的向主机提请中断请求,也即它不会主动的向主机发送数据包,而是被动的等待主机使用0xEB(读数据命令)后,再向主机提请中断请求,发送数据包。换句话说,如果在Remote模式下,你每次欲读数据时,均需要向鼠标发送0xEB命令,而如果是在Stream模式下,鼠标会自动向你发送数据。
Wrap模式主要用于检测鼠标与主机之间的连线是否正常,主机向它发送命令,它一般不会执行此命令,而只是简单的将此命令原封不同的回送给主机,主机可比较它发出的命令及接收到的命令是否一致,并以此来认定鼠标与主机之间的连线是否正常。
从上面的描述中我们可以看出,我们需要关心的只有Reset模式及Stream模式,但对于操作系统编写人员而非BIOS编写人员来说,真正需要关心的只有Stream模式,这是因为当计算机启动的时候,BIOS会自动检测鼠标,与鼠标进行通信,这个时候它会向鼠标发送0xFF(复位)命令,然后鼠标会自检,并通知主机自检是否正常,然后鼠标就将处于Stream模式,此时,鼠标已经开始检测鼠标是否移动及是否有键按下了,但是它不会立即就向主机发送数据,因为有可能主机还没有进入真正的操作系统,主机还正在启动中,因此,鼠标会等待主机的通知,直到主机给它发送0xF4命令后,它才开始向主机发送数据。
对于鼠标的中断控制字:
鼠标控制字为三个字节,分别代表了:命令、X轴的移动值、Y轴的移动值;
左键点击 09 00 00左键弹起 08 00 00
右键点击 0A 00 00左键弹起 08 00 00
下图是鼠标对应X/Y轴的方向,这个和我们在图形中使用的方向是不同的:Y轴的方向是反的。
当我们鼠标移动方向与上图方向不同时,得到的X的值或者Y的值是大于0x80的。
文件中的接口:
rtm_0x74_interrupt_handle:鼠标中断处理代码;
install_0x74_interrupt:安装0x74号中断(鼠标);
init_keyboard:初始化鼠标相关寄存器;
enable_mouse:启动鼠标;
move_mouse:从鼠标数据缓冲区获取数据,并在屏幕上移动鼠标;
redraw_mouse:绘制鼠标;
;===============================================================================
;=== 本程序包括了鼠标的主要功能: ===
;=== 1.鼠标中断初始化 ===
;=== 2.鼠标控制 ===
;===============================================================================
;-------------------------------------------------------------------------------
rtm_0x74_interrupt_handle: ;鼠标中断处理代码
pushad
mov al,0x64 ;中断结束命令EOI
out 0xa0,al ;向8259A从片发送
mov al,0x62
out 0x20,al ;向8259A主片发送
mov ebx, [mouse_buf_wpnt] ;写偏移
in al, 0x60 ;获取鼠标数据
cmp al, 0xfa ;判断是否是标志字0xfa
jnz _rtm_0x74_data_ok ;如果不是
mov edx, [mouse_enabled] ;再判断是否鼠标已经启动成功
cmp edx, 0x0 ;是否已经启动成功
jnz _rtm_0x74_data_ok ;已经启动成功
mov dword [mouse_enabled], 0x55 ;确实是启动成功标志,设置启动成功标志,跳转出去
jmp _rtm_0x74_out
_rtm_0x74_data_ok:
;将收到的数据放入缓冲区
mov [mouse_buf_addr + ebx], al ;数据写入鼠标缓冲区
inc ebx ;写指针加一
cmp ebx, mouse_buf_end ;判断写指针的位置
jb _rtm_0x74_out ;如果未到了缓冲区尾部,结束
xor ebx, ebx ;已到缓冲区尾部,写指针翻转
_rtm_0x74_out:
mov [mouse_buf_wpnt], ebx ;保存新的写指针
popad
iretd
;-------------------------------------------------------------------------------
install_0x74_interrupt: ;安装0x74号中断(鼠标)
;配置鼠标相关寄存器
call init_keyboard
;安装鼠标中断向量,开启鼠标中断
mov eax, rtm_0x74_interrupt_handle ;中断向量
mov ebx, 0x74 ;中断向量号
mov ecx, 0x0100 ;ch-1从片,cl-0对应bit0
call install_XXX_interrupt
ret
;-------------------------------------------------------------------------------
init_keyboard: ;初始化鼠标相关寄存器
_init_kb_check1:
in al,0x64 ;键盘缓冲区状态
and al, 0x02
jne _init_kb_check1
mov al, 0x60
out 0x64, al
_init_kb_check2:
in al,0x64 ;键盘缓冲区状态
and al, 0x02
jne _init_kb_check2
mov al, 0x47
out 0x60, al
ret
;-------------------------------------------------------------------------------
enable_mouse: ;启动鼠标
_en_mouse_check1:
in al,0x64 ;键盘缓冲区状态
and al, 0x02
jne _en_mouse_check1
mov al, 0xd4
out 0x64, al
_en_mouse_check2:
in al,0x64 ;键盘缓冲区状态
and al, 0x02
jne _en_mouse_check2
mov al, 0xf4
out 0x60, al
ret
;-------------------------------------------------------------------------------
move_mouse: ;从鼠标数据缓冲区获取数据,并在屏幕上移动鼠标
pushad
cli ;关中断
_mouse_check_loop:
;判断缓冲区是否有数据
mov eax, [mouse_buf_wpnt] ;获取写指针
mov ebx, [mouse_buf_rpnt] ;获取读指针
cmp eax, ebx ;读、写指针比较
jz _check_finished ;如果读、写指针相同,退出
;从缓冲区获取一个字符
xor cx, cx ;cx清零
mov cl, [mouse_buf_addr + ebx] ;从读指针处获取一个字符
inc ebx ;读指针后移
cmp ebx, mouse_buf_end ;读指针是否已经到缓冲区末尾
jb _rpnt_wrt ;未到末尾,直接回写新的读指针值
xor ebx, ebx ;读指针已经到了缓冲区末尾,清零
_rpnt_wrt:
mov [mouse_buf_rpnt], ebx ;保存新的读指针
;根据解析状态来处理字符
mov eax, [mouse_chk_state] ;当前处理状态
cmp eax, 1 ;是否是状态1
jz _handle_x ;处理x的值
cmp eax, 2 ;是否是状态2
jz _handle_y ;处理y的值
;处理鼠标命令字 [mouse_chk_state] = 0
mov byte [mouse_cmd], cl ;保存命令字
mov dword [mouse_chk_state], 1 ;状态变为1
jmp _move_mouse_out
;处理x的值 [mouse_chk_state] = 1
_handle_x:
xor eax, eax
mov ax, [mouse_x_pnt] ;获取原鼠标x轴的值
mov [mouse_x_old_pnt], ax ;保存原x轴的值
cmp cl, 0x80 ;x的值是否大于0x80。如果x的值大于0x80,鼠标向左移动
jb _x_mov_right
not cl ;取反加一
inc cl
cmp cx, ax ;要判断x轴是否移动到了屏幕最左端
jb _x_left_ok ;如果移动值小于原x轴的值
mov cx, ax ;鼠标已经移到屏幕最左侧了,调整减数,使鼠标停留在屏幕最左侧
_x_left_ok:
sub ax, cx ;原x轴的值减去鼠标左移的值,得到新的x轴的值
jmp _x_check_ok ;鼠标左移处理完毕,保存新值,并改变命令字状态
_x_mov_right: ;鼠标右移
add ax, cx ;原鼠标x轴的值加上右移步数
cmp ax, 318 ;是否已经超过屏幕右侧
jb _x_check_ok ;未超过,直接保存x轴的值即可
mov ax, 318 ;否则,让x的值保持在320
_x_check_ok:
mov [mouse_x_pnt], ax ;将新的x轴的值写入内存
mov dword [mouse_chk_state], 2
jmp _move_mouse_out
;处理y的值 [mouse_chk_state] = 2
_handle_y:
xor eax, eax
mov ax, [mouse_y_pnt] ;获取原鼠标y轴的值
mov [mouse_y_old_pnt], ax ;保存原y轴的值
cmp cl, 0x80 ;y的值是否大于0x80。如果y的值大于0x80,鼠标向下移动
jb _y_mov_up
not cl
inc cl
add ax, cx ;要判断y轴是否移动到了屏幕最下端
cmp ax, 198
jb _y_down_ok ;如果移动值小于原y轴的值
mov ax, 198 ;鼠标已经移到屏幕最上端了,调整减数,使鼠标停留在屏幕最上侧
_y_down_ok:
jmp _y_check_ok ;鼠标上移处理完毕,保存新值,并改变命令字状态
_y_mov_up: ;鼠标上移
cmp ax, cx ;原鼠标y轴的值加上下移步数
jae _y_up_ok ;是否已经超过屏幕最下端了
mov cx, ax
_y_up_ok:
sub ax, cx ;否则,让的值保持在199
_y_check_ok:
mov [mouse_y_pnt], ax ;将新的y轴的值写入内存
mov dword [mouse_chk_state], 0
;在屏幕上绘制鼠标
call redraw_mouse
;判断是否点击了鼠标左键
cmp byte [mouse_cmd], 0x09
jnz _move_mouse_out ;如果没有点击鼠标,继续判断后续
cmp word [mouse_y_pnt], 100 ;如果没有点击在屏幕的下半部分,退出
jb _move_mouse_out
cmp word [mouse_x_pnt], 160
jb _click_other_app ;x轴小于160,对应APP1,否则是APP0
mov byte [USR1_PROC + proc_ctrl.cmd], 0x55 ;APP0被点击了
jmp _move_mouse_out
_click_other_app:
mov byte [USR2_PROC + proc_ctrl.cmd], 0x55 ;APP1被点击了
_move_mouse_out:
jmp _mouse_check_loop ;在缓冲区中数据处理完毕之前,死循环
_check_finished:
sti ;开中断
popad
ret
;--------------------------------全局变量---------------------------------------
redraw_mouse: ;在屏幕上绘制鼠标
pushad
;覆盖旧的坐标点
xor eax, eax
mov bx, [mouse_y_old_pnt]
mov ax, 320
mul bx
xor ebx, ebx
mov bx, [mouse_x_old_pnt]
mov cl, [mouse_old_point]
mov byte [pic_mem_addr + eax + ebx], cl ;左上
mov cl, [mouse_old_point + 1]
mov byte [pic_mem_addr + eax + ebx + 1], cl ;右上
mov cl, [mouse_old_point + 2]
mov byte [pic_mem_addr + eax + ebx + 320], cl ;左下
mov cl, [mouse_old_point + 3]
mov byte [pic_mem_addr + eax + ebx + 321], cl ;右下
;按照新的坐标绘图
xor eax, eax
mov bx, [mouse_y_pnt]
mov ax, 320 ;y轴做乘法(y×320)
mul bx
xor ebx, ebx
mov bx, [mouse_x_pnt]
mov cl, [pic_mem_addr + eax + ebx] ;记录被覆盖点的颜色(左上)
mov [mouse_old_point], cl
mov cl, [mouse_draw_color]
mov byte [pic_mem_addr + eax + ebx], cl
mov cl, [pic_mem_addr + eax + ebx + 1] ;记录被覆盖点的颜色(右上)
mov [mouse_old_point + 1], cl
mov cl, [mouse_draw_color + 1]
mov byte [pic_mem_addr + eax + ebx + 1], cl
mov cl, [pic_mem_addr + eax + ebx + 320] ;记录被覆盖点的颜色(左下)
mov [mouse_old_point + 2], cl
mov cl, [mouse_draw_color + 2]
mov byte [pic_mem_addr + eax + ebx + 320], cl
mov cl, [pic_mem_addr + eax + ebx + 321] ;记录被覆盖点的颜色(右下)
mov [mouse_old_point + 3], cl
mov cl, [mouse_draw_color + 3]
mov byte [pic_mem_addr + eax + ebx + 321], cl ;被覆盖点用蓝色填写
;鼠标4个点的颜色互换,作出闪烁效果
mov dl, [mouse_draw_color]
mov dh, [mouse_draw_color + 1]
mov [mouse_draw_color], dh
mov dh, [mouse_draw_color + 2]
mov [mouse_draw_color + 1], dh
mov dh, [mouse_draw_color + 3]
mov [mouse_draw_color + 2], dh
mov [mouse_draw_color + 3], dl
popad
ret
;--------------------------------全局变量---------------------------------------
align 4
;下列变量用于鼠标控制
mouse_cmd db 0,0,0,0 ;鼠标命令字
mouse_old_point db 0,0,0,0 ;鼠标的显示是一个小正方形,4个点,需要4个字节保存
mouse_draw_color db 1,2,3,4 ;鼠标4个点的颜色(红绿黄蓝)
mouse_x_pnt dw 159 ;鼠标当前在屏幕上的x值
mouse_y_pnt dw 60 ;鼠标当前在屏幕上的y值
mouse_x_old_pnt dw 159 ;鼠标在原屏幕上的x值
mouse_y_old_pnt dw 60 ;鼠标在原屏幕上的y值
mouse_enabled dd 0x0 ;鼠标是否初始化成功
mouse_buf_wpnt dd 0x0 ;鼠标数据写指针
mouse_buf_rpnt dd 0x0 ;鼠标数据读指针
mouse_chk_state dd 0x0 ;处理数据数据时的状态标志
align 4
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 系统的简单注解
- 注解的简单示例
- 主攻ASP.NET MVC4.0之重生:ASP.NET MVC使用JSONP
- Java性能垫底的真相(别的测试你都…
- util.NestedServletException:&nbs…
- org.springframework.core.NestedI…
- Spring与Hibernate整合实现(SH框…
- 系统的简单注解
- 从最基本的Netbeans默认Servlet项…
- 利用MFC中的picture control 控件显示图像
- 从最基本的Netbeans默认Servlet项…
- java动态代理之cglib动态代理实现
- 满屏幕的线程Hibernate结合Servlet…
- SSH框架特例实战【一】(SpringMVC…
- 为何有DAO与Service层?为何先搞Da…
- SSH框架特例实战【二】关于Autowir…