8259 鼠标移动实验
Last updated
Last updated
实验内容:
设定画面模式为为图形模式、320x200x8 位彩色,鼠标中断的中断类型码为 INT 2CH。以一个小矩形作为画面上的鼠标,当移动鼠标时,画面上的鼠标也跟着移动。
思路:
PS/2 鼠标连接在 8259 的从片 IRQ4
引脚上,即 IRQ12
。通过对 8259 初始化,将鼠标中断的中断类型码为设定为 INT 2CH,然后编写相应的中断处理程序。在中断处理程序中,获取鼠标的信息,根据内存中的记录鼠标左上角的横纵坐标完成鼠标的移动。
(说明:此程序可以正常运行,但在鼠标的端口方面还有一些疑问!)
Square_len equ 4
Background_color equ 0x0
Foreground_color equ 0x3
[org 0x7c00]
start:
mov ax,0x0013 ; 设置显示器模式为图形模式、320x200x8 位彩色
int 0x10
mov ax,0xa000 ; 设置 es 段基地址为显存区域 0xa0000 处
mov es,ax
; 填充背景色
mov di,(320*200)
fill_background_loop:
mov byte [es:di],Background_color
dec di
jnz fill_background_loop
; 绘制鼠标
mov al,Foreground_color
call fill_mouse
; 设置中断向量
cli ; 关中断
mov bx,0xb0 ; INT2C,中断向量表的偏移量 0xb0
mov word [cs:bx],new_int_0xb0 ; 自定义的中断处理程序的偏移地址
mov [cs:bx+2],cs ; 段地址
mov bx,0x84 ; INT21,中断向量表的偏移量 0x84
mov word [cs:bx],new_int_0x84 ; 自定义的中断处理程序的偏移地址
mov [cs:bx+2],cs ; 段地址
sti ; 开中断
; 初始化 8259
mov al,0xff ; 禁止主片和从片所有中断
out 0x21,al ; 主片 OCW1
out 0xa1,al ; 从片 OCW1
; 主片初始化
mov al,0b_000_1_0_0_0_1 ; 边沿触发,级联使用
out 0x20,al ; 主片,初始化命令字 ICW1
mov al,0x20 ; 设置 IRQ0-7 产生中断类型码 INT20-27
out 0x21,al ; 主片,初始化命令字 ICW2
mov al,(1<<2) ; PIC1 由 IRQ2 连接
out 0x21,al ; 主片,初始化命令字 ICW3
mov al,0b_000_0_00_0_1 ; 一般全嵌套方式,非缓冲方式,不自动结束中断,8259 工作在 8088/8086 系统中
out 0x21,al ; 主片,初始化命令字 ICW4
; 从片初始化
mov al,0b_000_1_0_0_0_1 ; 边沿触发,级联使用
out 0xa0,al ; 从片,初始化命令字 ICW1
mov al,0x28 ; 设置 IRQ8-15 产生中断类型码 INT28-2f
out 0xa1,al ; 从片,初始化命令字 ICW2
mov al,0x02 ; PIC1 连接主片 IRQ2
out 0xa1,al ; 从片,初始化命令字 ICW3
mov al,0b_000_0_00_0_1 ; 一般全嵌套方式,非缓冲方式,不自动结束中断,8259 工作在 8088/8086 系统中
out 0xa1,al ; 从片,初始化命令字 ICW4
mov al,0b_11111001 ; 禁止主片所有中断,只留键盘(因为似乎也要处理键盘中断?)
out 0x21,al ; 主片 OCW1
mov al,0b_11101111 ; 禁止除 IRQ12 外所有中断
out 0xa1,al ; 从片 OCW1
call kb_wait
mov al,0x60 ; 键盘写模式
out 0x64,al ; 8042 端口
call kb_wait
mov al,0x47 ; 键盘写模式
out 0x60,al ; 键盘数据端口
call kb_wait
mov al,0xd4 ; 操作鼠标
out 0x64,al
call kb_wait
mov al,0xf4 ; 激活鼠标
out 0x60,al
fin:
hlt
jmp fin
; 自定义的中断处理程序,鼠标
new_int_0xb0:
pusha
mov al,[mouse_read_state]
or al,al
jz .new_int_0xb0.read_state0
dec al
jz .new_int_0xb0.read_state1
dec al
jz .new_int_0xb0.read_state2
.new_int_0xb0.read_state3:
in al,0x64 ; 读 8042 的状态寄存器
test al,0x01 ; 检测第一位
jz .new_int_0xb0.fin ; 若无数据可读,结束
in al,0x60 ; 读出鼠标的纵坐标偏移量
neg al ; 需要取反
mov [square_pos_offset_y],al ; 将更改后的偏移量存入内存
mov byte [mouse_read_state],1 ; 本来应该跳到 0(接收 ACK 信号)的,结果不对,改成跳到 1 就好了
; 测试鼠标是否超出边界
mov bx,[square_pos_x]
mov al,[square_pos_offset_x]
cbw
add bx,ax
js .new_int_0xb0.fin
cmp bx,(320-Square_len)
jge .new_int_0xb0.fin
mov cx,[square_pos_y]
mov al,[square_pos_offset_y]
cbw
add cx,ax
js .new_int_0xb0.fin
cmp cx,(200-Square_len)
jge .new_int_0xb0.fin
; 清除原鼠标
mov al,Background_color
call fill_mouse
; 重绘鼠标
mov [square_pos_x],bx
mov [square_pos_y],cx
mov al,Foreground_color
call fill_mouse
.new_int_0xb0.fin:
; EOI
mov al,0b_011_00_100 ; IRQ12 对应 IR4
out 0xa0,al ; 从片 OCW2
mov al,0b_011_00_010 ; IRQ2 对应 IR2
out 0x20,al ; 主片 OCW2
popa
iret
.new_int_0xb0.read_state0:
in al,0x60 ; 等待鼠标 ACK 信号
cmp al,0xfa
jne .new_int_0xb0.fin ; 等待直到收到 ACK 信号
mov byte [mouse_read_state],1
jmp .new_int_0xb0.fin
.new_int_0xb0.read_state1:
in al,0x64 ; 读 8042 的状态寄存器
test al,0x01 ; 检测第一位
jz .new_int_0xb0.fin ; 若无数据可读,结束
in al,0x60 ; 有数据则读出
mov byte [mouse_read_state],2
jmp .new_int_0xb0.fin
.new_int_0xb0.read_state2:
in al,0x64 ; 读 8042 的状态寄存器
test al,0x01 ; 检测第一位
jz .new_int_0xb0.fin ; 若无数据可读,结束
in al,0x60 ; 读出鼠标的横坐标偏移量
mov [square_pos_offset_x],al ; 将更改后的偏移量存入内存
mov byte [mouse_read_state],3
jmp .new_int_0xb0.fin
; 自定义的中断处理程序,键盘
new_int_0x84:
call beep
; EOI
mov al,0b_011_00_001 ; IRQ1 对应 IR1
out 0x20,al ; 主片 OCW2
.new_int_0x84.kb_wait:
in al,0x64 ; 读 8042 的状态寄存器
test al,0x01 ; 检测第一位
jz .new_int_0x84.fin ; 若无数据可读,结束
in al,0x60 ; 有数据则读出
jmp .new_int_0x84.kb_wait
.new_int_0x84.fin:
iret
; Draw mouse
; al draw color
fill_mouse:
pusha
push ax ; 暂存 al
mov ax,[square_pos_y] ; 鼠标左上角纵坐标乘以 320 放入 di
mov di,ax
shl di,8 ; 先 256 倍
shl ax,6 ; 再加 64 倍
add di,ax
mov ax,[square_pos_x] ; 加上鼠标左上角横坐标
add di,ax
pop ax ; 恢复 al
mov cx,Square_len ; 绘制 Square_len 列
.fill_mouse.draw_y:
push cx ; 暂存外层循环的计数值
push di ; 暂存行绘制开始时的小方块的地址
mov cx,Square_len ; 绘制 Square_len 行
.fill_mouse.draw_x:
mov [es:di],al
inc di
loop .fill_mouse.draw_x
pop di ; 恢复行绘制开始时的小方块的地址指针
add di,320 ; 跳到下一行
pop cx ; 恢复外层循环的计数值
loop .fill_mouse.draw_y
popa
ret
; 等待 8042 键盘电路响应
kb_wait:
in al,0x64 ; 读键盘电路
test al,0x02 ; 检测 8042 状态寄存器的第二位
jnz kb_wait
ret
; Make a beep sound
beep:
pusha
mov al,0xb6
out 0x43,al ; 8254 CNT2
mov ax,0x0533 ; Sound frequency 896 Hz
out 0x42,al
mov al,ah
out 0x42,al
in al,0x61 ; 8255 PB
mov ah,al
or al,0x03 ; Enable 8254 CNT2
out 0x61,al
mov cx,0x0001 ; Last for some time
loop2:
push cx
mov cx,0xffff
loop1:
nop
loop loop1
pop cx
loop loop2
mov al,ah
out 0x61,al ; Restore 8255 PB
popa
ret
; 鼠标位置信息,初始值在屏幕中央
square_pos_x:
dw ((320-Square_len)>>1)
square_pos_y:
dw ((200-Square_len)>>1)
square_pos_offset_x:
db 0
square_pos_offset_y:
db 0
mouse_read_state:
db 0
signature:
%if $-$$>510
%fatal "stage1 code exceed 512 bytes."
%endif
times 510-($-$$) \
db 0
db 0x55,0xaa
参考资料: