;----------------------------------------------------------------------------------
; 鼠标, 键盘和显示设备的汇编语言操作
;
; 在DOS系统或者命令行中运行 (在 win 下双击即可运行)
; 运行时跟踪鼠标的移动位置,捕获鼠标和键盘的击键情况
; (*)当按下 [ESC] 键时程序退出,其它键,蜂鸣
; (*)按下鼠标右键恢复初始显示方式,并发声
; (*)按下鼠标左键仅产生噪音
; (*)按下键盘上的 F1-F10 切换到不同的显示方式 .
;
; 编译器: [MASM] [ 欢迎到群里下载:28011342 ]
; (c) by 江南孤峰 Time: 2007--10--3 version: 1.0
; **************************************************************************
; **** 没有复杂的汇编语法,适合初学者完赏, 目前尚不完善,更新中……
; **************************************************************************
;----------------------------------------------------------------------------------
.model small
;----------------------------------------------------------------------------------
.data
msgWin db 'Mouse or Keyboard message show as follow: $'
msgHlpStar db ' ================= INFOMATION ==================$'
db ' * Press key [ESC] to exit $'
db ' * Hit right mouse key to normal mode $'
db ' * Hit left mouse key to make sound $'
db ' * Press key F1-F10 to change show mode $'
db ' (c) by JiangNanGuFeng Time: 2007 - 10 - 4 $'
db ' Email: lingdlz@163.com QQ: 403324669 $'
msgHlpEnd db ' ===============================================$'
msgLineLen dw $-msgHlpEnd
windowEdge db '**********************************************************$'
msgMouseMove db 'Mouse message : mouse move new pos: $'
msgMouseLeft db 'Mouse message : hit left key $'
msgMouseRight db 'Mouse message : hit right key $'
msgKeyBd db 'Keyboard message: hit key, code [$'
msgStart db 'Press any key to start ......$'
msgEnd db 'Press any key to exit ......$'
showMode db 03h,04h,06h,09h,0dh,0eh,0fh,10h,11h,13h,'$'
begMode dw 000fh ; 最初的显示方式
currentMode dw 6 ; 当前显示方式索引值,初始值对应 begMode
msgModePrompt db 'Mode Info :$'
msgMode db 'AL=03 WIDTH=80 HIGHT=25 COLOR=16 MODE=text $'
db 'AL=04 WIDTH=320 HIGHT=200 COLOR=4 MODE=graph $'
db 'AL=06 WIDTH=640 HIGHT=200 COLOR=w/b MODE=text $'
db 'AL=09 WIDTH=320 HIGHT=200 COLOR=16 MODE=graph\MC$'
db 'AL=0d WIDTH=320 HIGHT=200 COLOR=16 MODE=graph\EV$'
db 'AL=0e WIDTH=640 HIGHT=200 COLOR=16 MODE=graph\EV$'
db 'AL=0f WIDTH=640 HIGHT=350 COLOR=sig MODE=text\EV$'
db 'AL=10 WIDTH=640 HIGHT=350 COLOR=16 MODE=graph\EV$'
db 'AL=11 WIDTH=640 HIGHT=480 COLOR=w/b MODE=text $'
db 'AL=13 WIDTH=320 HIGHT=200 COLOR=256 MODE=graph\E$'
;--------------------------------------------------------------------------------
; msgMode 说明:AL 设置该显示方式时的调用参数; WIDTH/HIGHT 表示屏幕像素或者
; 字符行列数; COLOR 表示颜色数, 其中w/b表示黑白,sig 单色;
; MODE: text 表示文本模式, graph 图形模式, MC: MCGA,E: EGA, V: VGA
;--------------------------------------------------------------------------------
soundFreq dw 31,61,122,144,419,817,840,911
dw 62,144,446,799,819,1104,1715,2411
soundFreqSize db $-soundFreq ; 音符频率,键盘击键时发声
newLine db 13,10,'$'
savStat dw 0 ; 保存鼠标击键情况
savPosX dw 22 ; 鼠标的X坐标, 22 是初始位置
savPosY dw 62 ; 鼠标的Y坐标, 62 是初始位置
keyBdIn dw ? ; 保存最新的键盘按键
isScroll db 0 ; 窗口是否需要上滚,0 否,1 是
scroLine db ? ; 窗口滚动时,卷入行的属性
countColumn db ? ; 当前显示方式的字符列数
winTopRow dw 0c08h ; 窗口部分的最上边
winBottomRow dw 1408h ; 窗口部分的最下边,[04]最后会改为字符列数
rlt_asc db 10 dup(?)
temp_asc db 10 dup(?)
;----------------------------------------------------------------------------------
.code
main proc far
star: mov ax,@data
mov ds,ax
mov ax,begMode ; 初始化显示方式
int 10h
call InitScreen ; 初始化屏幕
call LocateScroll ; 定位光标,或者滚动窗口
Lea dx,msgStart ; 第一条消息
call PrintStr
call WaitHitKey ; 等待用户按键
again: call CheckKeyBd ; 检查鼠标消息,即是否有击键
cmp ax,4 ; 击键[ESC],退出程序
jz mainOver
cmp ax,2 ; 显示方式切换
jnz testMouse
call SwitchMode ; 切换显示方式
testMouse:
call CheckMouse ; 检查鼠标状态,是否发生变化
cmp al,0
jz again ; 鼠标状态没有改变
cmp al,3
jz mouseMove ; 移动
call MakeSound ; 单击了鼠标,发噪音
cmp al,1 ; 鼠标情况
jz hitLef ; 左击
cmp al,2
jz hitRig ; 右击
hitLef: call LocateScroll; 左击处理,这里可以调用某些函数
lea dx,msgMouseLeft
call PrintStr
jmp again
hitRig: call ResetMode ; 恢复至最初的显示状态
jmp again
mouseMove: ; 鼠标移动
call LocateScroll
lea dx,msgMouseMove
call PrintStr
mov dl,'('
call PrintChar
mov ax,savPosX ; 水平位置
call BinToHex ; 以十六进制形式打印出来
mov dl,','
call PrintChar
mov ax,savPosY ; 垂直位置
call BinToHex
mov dl,')'
call PrintChar
jmp again
mainOver:
call HideMouse ; 隐藏鼠标
call LocateScroll ; 重定位光标,并根据需要将窗口下滚一行
lea dx,msgEnd ; 提示信息
call PrintStr
call WaitHitKey ; 等待用户按键
mov ax,0003h ; 恢复至DOS默认的显示模式
int 10h
mov ax,4c00h
int 21h
main endp
;---------------------------------------------------------
; 初始化屏幕
;---------------------------------------------------------
InitScreen proc near
call HideCursor ; 隐藏光标 [无效] ??
call ShowProMsg ; 打印程序的有关信息
call ShowFramWin ; 打印屏幕小窗口
call ShowScrMode ; 打印当前显示方式的有关信息
mov ah,08h ; 读光标位置的字符和属性
mov bh,00h
int 10h
mov scroLine,al ; 保存字符属性,作为窗卷入行属性
mov ah,0fh ; 取当前显示方式
int 10h
cmp al,03h
jnz NotNormalDos ; 测试是否是DOS默认的显示模式
mov al,07h ; 普通卷入行属性 DOS
mov scroLine,al ; 卷入行属性
NotNormalDos:
mov countColumn,ah ; 保存字符列数
mov dx,winTopRow ; winTopRow 是标题所在行
inc dh
call LocateCursor ; 光标定位到窗口上边缘
mov al,0
mov isScroll,al ; 恢复滚动标记
call InitMouse ; 初始化鼠标
call ShowMouse ; 显示鼠标, 大部分模式下默认不能显示
ret
InitScreen endp
;---------------------------------------------------------
; 重定位光标,并根据需要将窗口下滚一行
;---------------------------------------------------------
LocateScroll proc near
cmp isScroll,1 ; 滚动标记已经设置
jz LocScroll
mov ah,03h ; 读当前光标位置
mov bh,00h ; 页号,0
int 10h
mov cx,winBottomRow
dec ch ; 修正
cmp dh,ch ; 是否到达窗口底部
jb LocAddRow ; 增加行号
mov al,1
mov isScroll,1
jmp LocScroll ; 下滚一行
LocAddRow:
mov cx,winTopRow
add cl,4 ; 修正,也就是缩进 4 格
mov dl,cl
inc dh ; 行号增 1
call LocateCursor
jmp LocScrOver
LocScroll:
mov ax,0701h ; ah 功能号,al 滚动行数
mov bh,scroLine ; 卷入行属性,普通
mov cx,winTopRow ; 窗口左上角
add ch,2 ; 内容滚动区修正
mov dx,winBottomRow ; 窗口右下角
dec dh ; 修正
mov dl,countColumn ; 字符列数
int 10h
mov dx,winTopRow ; 重定位光标到窗口顶部
add dh,2
add dl,4
call LocateCursor
LocScrOver:
ret
LocateScroll endp
;---------------------------------------------------------
; 检查是否有键盘击键情况,若有,则输出对应键的字符码和扫
; 描码, 若所击键为 [ESC],则 ax 设置为 4, 如果是显示方式
; 切换热键,则 ax 设置为 2
;---------------------------------------------------------
CheckKeyBd proc near
mov ah,01h
int 16h ; 取键盘缓冲区状态
jz CHKBEND ; 键盘无按键
mov keyBdIn,ax ; 保存 ax 中的内容
push ax
mov al,ah ; 按键发声,扫描码取模后作为索引
xor ah,ah
div soundFreqSize
xor bh,bh
mov bl,ah ; 取扫描码除soundFreqSize 后的余数
mov di,soundFreq[bx]
mov cx,0afffh ; 延迟
call GenMakeSound
pop ax
call IsModeChange ; 测试是否是显示方式切换的 "热键"
cmp ax,2
jz CHKBEND ; 是显示方式切换 "热键"
call LocateScroll ; 重定位光标,并根据需要将窗口下滚一行
call PrintKeyCode ; 以十六进制形式输出键盘码
mov ax,keyBdIn ; 测试是否是 [ESC] 键
cmp ax,011bh ; 011bh 是ESC的按键码
jnz CHKBEND
mov ax,4
CHKBEND:ret
CheckKeyBd endp
;---------------------------------------------------------
; 设置光标位置,页号皆为 0, DH/DL 已经相应存了 行/列 [SAFE]
;---------------------------------------------------------
LocateCursor proc near
push bx
push ax
mov bh,00h
mov ah,02h
int 10h
pop ax
pop bx
ret
LocateCursor endp
;---------------------------------------------------------
; 切换显示方式, 切换之后,重新运行所有初始化操作
;---------------------------------------------------------
SwitchMode proc near
mov bx,keyBdIn
sub bx,3b00h ; 3b00h 是F1的按键码
mov bl,bh
mov bh,0 ; 清0,使 bx 成为索引
mov al,byte ptr showMode[bx]
mov ah,00h
int 10h
mov currentMode,bx ; 设置索引
call InitScreen
call LocateScroll ; 光标处理,窗口下滚
call PrintKeyCode ; 输出按键码
ret
SwitchMode endp
;---------------------------------------------------------
; 检查鼠标状态,是否发生变化( 移动,或者击键 )
; 如果是击键则 ax 置为 1(左击)或者 2(右击)
; 如果是移动则将新位置存入 savPosX,savPosY, ax 置为 3
; 如果鼠标状态没有发生变化 ax 置为 0
;---------------------------------------------------------
CheckMouse proc near
mov ax,0003h ; 读取鼠标状态
int 33h
cmp cx,savPosX ; 是否移动了鼠标
jnz CKMOV
cmp dx,savPosY
jnz CKMOV
cmp bx,savStat ; 测试是否有鼠标按键
jz NOCHG ; 没有鼠标按键
mov savStat,bx
cmp bx,1 ; 左击
jz CKHITL
cmp bx,2 ; 右击
jz CKHITR
NOCHG: mov ax,0 ; 没有鼠标按键
jmp CKOVER
CKMOV: mov ax,3 ; 鼠标移动,保存新位置
mov savPosX,cx
mov savPosY,dx
jmp CKOVER
CKHITL: mov ax,1 ; 左击捕获
jmp CKOVER
CKHITR: mov ax,2 ; 右击捕获
CKOVER: ret
CheckMouse endp
;---------------------------------------------------------
; 设为DOS普通的显示模式
;---------------------------------------------------------
ResetMode proc near
mov al,03h ; DOS下的普通模式
mov ah,00h ; 恢复开始的显示方式
int 10h
mov ax,0
mov currentMode,ax ; 当前显示方式索引
call InitScreen
call LocateScroll
lea dx,msgMouseRight
call PrintStr
ret
ResetMode endp
;---------------------------------------------------------
; 声音制作,单击鼠标
;---------------------------------------------------------
MakeSound proc near
mov dx,0
MkSAgain:
mov bx,0
MkSNext:
mov di,soundFreq[bx]
mov cx,04fh
call GenMakeSound
inc bl
cmp bl,soundFreqSize
jb MkSNext
inc dx
cmp dx,0fh
jnz MkSAgain
ret
MakeSound endp
;---------------------------------------------------------
; 与CPU工作频率无关的延迟, 延迟时间 (cx*15.08 微秒) [BOOK:391]
;---------------------------------------------------------
GenWait proc near
inGenWait:
in al,61h
and al,10h
cmp al,ah
je inGenWait
mov ah,al
loop inGenWait
ret
GenWait endp
;---------------------------------------------------------
; 利用8253/8254/8255产生指定频率声音的通用发声程序
; 指定频率 Di (19-65535Hz) [BOOK:Pg389]
; CX 持续时间 (cx*15.08 微秒) [BOOK:391] [SAFE]
;---------------------------------------------------------
GenMakeSound proc near
push dx
push di
push ax
mov al,0b6h ; 初始化8253/8254的计数器2
out 43h,al
mov dx,12h
mov ax,348ch
div di ; 根据频率计算计数值 42h
out 42h,al
mov al,ah
out 42h,al ; 计数值送端口 42h,以建立声音频率
in al,61h ; 61h为8255(a)输出端口
mov ah,al ; 保存当前值
or al,03h
out 61h,al ; 打开声音开关
call GenWait ; 与CPU工作频率无关的延迟
mov al,ah ; 恢复 61h 端口状态
out 61h,al
pop ax
pop di
pop dx
ret
GenMakeSound endp
;---------------------------------------------------------
; 隐藏光标,该函数无效,不知道什么原因 ????
;---------------------------------------------------------
HideCursor proc near
mov cx,0ffffh ; ch 第4位为1时光标被隐藏
mov ah,01h ; 设置光标类型
int 10h
ret
HideCursor endp
;---------------------------------------------------------
; 等待按键
;---------------------------------------------------------
WaitHitKey proc near
mov ah,00h ; 等待按键
int 16h
ret
WaitHitKey endp
;---------------------------------------------------------
; 在屏幕特定位置显示帮助信息
;---------------------------------------------------------
ShowProMsg proc near
lea si,msgHlpEnd
mov bx,0
mov cx,010ah ; 用于设置光标位置CH/CL , 行/列
SSMsgNext:
inc ch ; 重设光标位置,行号增一
mov dx,cx
call LocateCursor
lea dx,msgHlpStar[bx]
call PrintStr
cmp dx,si
jnb SSMsgOver
add bx,msgLineLen
jmp SSMsgNext
SSMsgOver:
ret
ShowProMsg endp
;---------------------------------------------------------
; 打印屏幕上小窗口的框架
;---------------------------------------------------------
ShowFramWin proc near
mov dx,winTopRow ; 窗口标题文字
call LocateCursor
lea dx,msgWin
call PrintStr
mov dx,winTopRow
inc dh ; 窗口顶部边界
call LocateCursor
lea dx,windowEdge
call PrintStr
mov dx,winBottomRow ; 窗口底部边界
call LocateCursor
lea dx,windowEdge
call PrintStr
ret
ShowFramWin endp
;---------------------------------------------------------
; 在屏幕小窗口的下部,打印当前显示模式信息
;---------------------------------------------------------
ShowScrMode proc near
mov dx,winBottomRow
add dh,2
add dl,2
call LocateCursor ; 重设光标位置
lea dx,msgModePrompt
call PrintStr
mov ax,currentMode
mul msgLineLen
mov bx,ax
lea dx,msgMode[bx]
call PrintStr
ShowScrMode endp
;---------------------------------------------------------
; 设置光标位置,新位置在DX中,DH/DL=行/列, BH 页号
;---------------------------------------------------------
SetCursor proc near
mov ah,02h
int 10h
ret
SetCursor endp
;---------------------------------------------------------
; 打印键盘的击键信息(按键码)其中存于 keyBdIn 变量中
;---------------------------------------------------------
PrintKeyCode proc near
lea dx,msgKeyBd ; 打印十六进制的按键码
call PrintStr
mov ax,keyBdIn
call BinToHex ; 转为十六进制ASCLL码形式输出到屏幕
mov dl,']' ; 分割符
call PrintChar
mov dl,' '
call PrintChar
mov ah,0ch ; 调用DOS中断,清空键盘缓冲区
int 21h
mov dx,keyBdIn ; 打印字符
call PrintChar
ret
PrintKeyCode endp
;---------------------------------------------------------
; 将 ax 中的二进制转为 十六进制 ASCALL 码输出到屏幕
; 如果需要以小写形式输出则将 bx 设置为 4 [SAFE]
;---------------------------------------------------------
BinToHex proc near
push dx ; 保存寄存器
push cx
push bx
push ax
mov cx,0404h ; ch 控制循环次数,cl控制移位数
cmp bx,4
jz bhLowerCase
mov bh,07h ; 大写形式输出
jmp bhNext
bhLowerCase:
mov bh,27h ; 小写形式输出
bhNext: rol ax,cl
mov bl,al
and bl,0fh
add bl,30h
cmp bl,3ah
jb bhPrint
add bl,07h
bhPrint:
mov dl,bl
push ax
mov ah,02h
int 21h
dec ch
pop ax
jnz bhNext
pop ax ; 恢复寄存器
pop bx
pop cx
pop dx
ret
BinToHex endp
;---------------------------------------------------------
; 测试键盘按键是否是显示方式切换的 "热键" (F1-F10)
; 键码存于keyBdIn 中。 若是则 ax 置为 2
;---------------------------------------------------------
IsModeChange proc near
mov bx,3a00h ; 初始化,3b00h 是 F1
mov ax,keyBdIn
IsHotF: inc bh
cmp ax,bx
jz IsHotKey ; 是 热键 F1-F10
cmp bx,4400h ; 4400h 是 F10
jz IsMCOver
jmp IsHotF
IsHotKey:
mov ax,2
IsMCOver:
ret
IsModeChange endp
;---------------------------------------------------------
; 屏幕打印字符,字符已经存入 dl 中
;---------------------------------------------------------
PrintChar proc near
mov ah,02h
int 21h
ret
PrintChar endp
;---------------------------------------------------------
; 屏幕打印字符串,字符串偏移地址已经存入 dx 中
;---------------------------------------------------------
PrintStr proc near
mov ah,09h
int 21h
ret
PrintStr endp
;---------------------------------------------------------
; 鼠标初始化
;---------------------------------------------------------
InitMouse proc near
mov ax,0000h ; 鼠标复位
int 33h
call InitPos ; 初始化鼠标位置
ret
InitMouse endp
;---------------------------------------------------------
; 显示鼠标
;---------------------------------------------------------
ShowMouse proc near
mov ax,0001h
int 33h
ret
ShowMouse endp
;---------------------------------------------------------
; 隐藏鼠标
;---------------------------------------------------------
HideMouse proc near
mov ax,0002h
int 33h
ret
HideMouse endp
;---------------------------------------------------------
; 初始化鼠标位置
;---------------------------------------------------------
InitPos proc near
mov ax,0004h
mov cx,42 ; 水平位置
mov dx,18 ; 垂直位置
int 33h
ret
InitPos endp
;----------------------------------------------------------------------------------
end star
评论