汇编语言:键盘中断

汇编语言:键盘中断

友情提示,本篇文章较长,如果仅需要代码可以点击此处下载源代码

一、实验目的

  1. 了解 Intel8086CPU的中断处理功能以及IBM-PC的中断结构
  2. 了解8259中断控制器的使用。
  3. 掌握键盘中断的编程,观察中断的执行情况。

二、实验任务

  1. 基本要求:每按下任意一个键就向CPU发出中断请求信号,该信号由8259的IRQ1引入,中断类型号为09, CPU响应中断后转入执行KEYINTS中断服务程序,并在屏幕上显示“OK!”,按下10次键后返回DOS。

  2. 附加功能

    1. 通过DOS系统功能调用的25H35H功能实现中断向量的设置和读取;
    2. 在显示”OK!”的前面增加显示按键次数
    3. 按键10次后,不等25行太阳图标显示完,立即返回DOS;
    4. 修改显示字符的属性,如,红底白字,蓝底黄字……

三、实验原理

    键盘与主机是通过5芯螺旋形的电缆相连的,其中包括数据线、时钟线、复位线、+5v电源线和地线(电缆插入系统板后部的插座)
   每当有键按下或释放时,键盘以串行方式向系统板的键盘接口电路传送数据,即扫描码。一个扫描码移位传送完,键盘接口电路便向主机发岀中断请求信号IRQ1(中断类型码为09H,此信号送到8259A产生中断请求。
   CPU响应中断请求时,查中断向量表,从09H×4开始的连续四个单元中取出中断向量(IRQ1中断服程序 KEYINTS的入口地址指针),转去执行中断服务程序 KEYINTS
  在键盘中断程序 KEYINTS中,保护现场、开中断之后,就通过8255APA口PA口地址为60H)读取键盘扫描码,接着从8255APB口PB口地址为61H)的PB7输出一个正脉冲(即PB7先输出髙电平,再输出低电平),先输出的高电平信号反相之后控制键盘状态触发器的清零端,使IRQI清零,撤消中断请求信号。再输出的低电平信号允许位寄存器输出数据,这样就为传递下一个键盘扫描码作好了准备。

以上内容是为了让大家对程序功能有一些基本认识,接下来开始正式编程

四、编写程序

首先先放几张程序运行时的动图

  1. 基本程序
    在这里插入图片描述
  2. 附加任务二(在显示”OK!”的前面增加显示按键次数):
    在这里插入图片描述
  3. 附加任务三(按键10次后,不等25行太阳图标显示完,立即返回DOS)
    在这里插入图片描述
  4. 附加任务四(修改显示字符的属性,如,红底白字,蓝底黄字……)
    在这里插入图片描述

看完程序运行动态图后,我们再通过程序流程图来观察程序是如何运行的。

在这里插入图片描述
通过流程图可以看出程序的基本结构,现在将其中几个重要的程序段单独呈现出来

  1. 延时程序
  DELAY PROC         ; 延时程序
      PUSH CX
      PUSH DX
      MOV DX,36H
 	  DL500:MOV CX, 0FFFFH
 	  DL10MS:LOOP DL10MS
      DEC DX
      JNZ DL500
      POP DX
      POP CX    
      RET
  DELAY ENDP

对于延时程序也可以使用我写的上一篇文章汇编语言:时钟实验中的延时程序,如果需要修改延时时间的话,直接修改DX或者CX的值即可

  1. 显示太阳字符
DISP1 PROC FAR     ; 显示太阳
       	PUSH AX
       	PUSH BX
       	PUSH CX
       	PUSH DX
       	MOV AH,15     ; 读当前显示状态
       	INT 10H
       	MOV AH,0      ; 设置显示方式
       	INT 10H
       	MOV DX,0      ; 行号为0,列号为0
	REPT: MOV AH,2      ; 设置光标位置
      	INT 10H  
       	MOV AL,0FH    ; OFH一太阳图形的ASCII码
        MOV CX,1      ; 显示字符个数
       	MOV AH,10     ; 写字符
       	INT 10H
       	CALL DELAY
       	SUB AL,AL
       	MOV AH,0      ; 清除原图形
       	INT 10H
       	INC DH        ; 行号+1
       	ADD DL,2      ; 列号+1
       	CMP DH,25     ; 判断是否到25行,不等继续显示太阳,相等返回
       	JNE REPT
       	POP DX
       	POP CX
       	POP BX
       	POP AX
       	RET
  DISP1 ENDP
  1. 显示OK!
DISP2 PROC FAR      ; 显示OK
        PUSH CX
       	PUSH BX
       	PUSH AX
     	  MOV CX,3
	      NEXTC: LODSB  ; 字符串"OK!"在数据段中定义,AL<—[SI]
	      MOV AH, 0EH   ; 写字符, 并移动鼠标
        MOV BX,01
	      INT 10H
        CALL DELAYL
        LOOP NEXTC
    	  POP AX
    	  POP BX
    	  POP CX
    	  RET
	DISP2 ENDP
  1. 中断程序
	KEYINT PROC FAR
       	PUSH AX
  	    PUSH SI  
  	    STI    
  	    IN AL,60H      ; 通过8255A的PA口(PA口地址为60H)读取键盘扫描码
  	    MOV AH,AL
      	IN AL,61H      ; 从8255APB口(PB口地址为61H)的PB7输出一个正脉冲(即PB7先输出高电平,再输出低电平)
       	OR AL,80H      ; PB7置1
  	    OUT 61H,AL
       	AND AL,7FH
       	OUT 61H,AL     ; PB7清零
       	TEST AH,80H    ; 相等时代表键被释放,开中断,显示字符
       	JNE GO         ; 不等,中断结束返回
       	STI  
       	INC KEY
       	MOV SI,OFFSET BUF   ; 初始化SI,在DISP2中不再对SI进行初始化
       	CALL DISP2
	    GO:MOV AL,20H
       	OUT 20H,AL
       	POP SI
       	POP AX
       	IRET
	KEYINT ENDP

1. 基本程序

完整的代码如下:

STACK  SEGMENT
	DW 200H DUP(?)
STACK ENDS
DATA SEGMENT
  	KEY DB ?           ; ?表示占用空间
  	BUF DB "OK!"
DATA ENDS
CODE SEGMENT
      ASSUME CS:CODE,SS:STACK,DS:DATA
  	DELAY PROC         ; 延时程序
     	PUSH CX
      	PUSH DX
      	MOV DX,36H
 	  	DL500:MOV CX, 0FFFFH
 	  	DL10MS:LOOP DL10MS
      	DEC DX
      	JNZ DL500
      	POP DX
      	POP CX    
      	RET
  	DELAY ENDP
 	DELAYL PROC
     	PUSH CX
      	PUSH DX
      	MOV DX,02H
      	DL500L:MOV CX, 0FFFFH
      	DL10MSL:LOOP DL10MSL
      	DEC DX
      	JNZ DL500L
      	POP DX
      	POP CX    
      	RET
  	DELAYL ENDP
  	DISP1 PROC FAR     ; 显示太阳
       	PUSH AX
       	PUSH BX
       	PUSH CX
       	PUSH DX
       	MOV AH,15     ; 读当前显示状态
       	INT 10H
       	MOV AH,0      ; 设置显示方式
       	INT 10H
       	MOV DX,0      ; 行号为0,列号为0
	REPT: MOV AH,2      ; 设置光标位置
      	INT 10H  
       	MOV AL,0FH    ; OFH一太阳图形的ASCII码
        MOV CX,1      ; 显示字符个数
       	MOV AH,10     ; 写字符
       	INT 10H
       	CALL DELAY
       	SUB AL,AL
       	MOV AH,0      ; 清除原图形
       	INT 10H
       	INC DH        ; 行号+1
       	ADD DL,2      ; 列号+1
       	CMP DH,25     ; 判断是否到25行,不等继续显示太阳,相等返回
       	JNE REPT
       	POP DX
       	POP CX
       	POP BX
       	POP AX
       	RET
  	DISP1 ENDP
	DISP2 PROC FAR      ; 显示OK
        PUSH CX
       	PUSH BX
       	PUSH AX
     	  MOV CX,3
	      NEXTC: LODSB  ; 字符串"OK!"在数据段中定义,AL<—[SI]
	      MOV AH, 0EH   ; 写字符, 并移动鼠标
        MOV BX,01
	      INT 10H
        CALL DELAYL
        LOOP NEXTC
    	  POP AX
    	  POP BX
    	  POP CX
    	  RET
	DISP2 ENDP
	KEYINT PROC FAR
       	PUSH AX
  	    PUSH SI  
  	    STI    
  	    IN AL,60H      ; 通过8255A的PA口(PA口地址为60H)读取键盘扫描码
  	    MOV AH,AL
      	IN AL,61H      ; 从8255APB口(PB口地址为61H)的PB7输出一个正脉冲(即PB7先输出高电平,再输出低电平)
       	OR AL,80H      ; PB7置1
  	    OUT 61H,AL
       	AND AL,7FH
       	OUT 61H,AL     ; PB7清零
       	TEST AH,80H    ; 相等时代表键被释放,开中断,显示字符
       	JNE GO         ; 不等,中断结束返回
       	STI  
       	INC KEY
       	MOV SI,OFFSET BUF   ; 初始化SI,在DISP2中不再对SI进行初始化
       	CALL DISP2
	    GO:MOV AL,20H
       	OUT 20H,AL
       	POP SI
       	POP AX
       	IRET
	KEYINT ENDP
	START:MOV AX,STACK
       	MOV SS,AX
       	MOV AX,DATA
       	MOV DS,AX
       	MOV AX,0          ; 将AX置0,后将其赋给ES,进行原09H中断向量的保存
       	MOV ES,AX
       	MOV AX,ES:[24H]   ; 09H*4=24H
       	PUSH AX
       	MOV AX,ES:[26H]   ; 
       	PUSH AX
       	CLI               ; 关中断,进行中断向量的设置
       	MOV AX,OFFSET KEYINT
       	MOV ES:[24H],AX
       	MOV AX,SEG KEYINT
       	MOV ES:[26H],AX    
       	STI               ; 同意下方程序接受中断
       	MOV  KEY,0
	AGAIN:CAll DISP1        ; 显示太阳
       	CMP KEY,10        ; 判断显示25行太阳后,是否已经按下了十次键
       	JB AGAIN
       	CLI               ; 禁止下方程序中断发生,保护代码运行
       	POP AX
       	MOV ES:[26H],AX
       	POP AX
       	MOV ES:[24H],AX
       	STI               ; 开中断
       	MOV AH,4CH
       	INT 21H
CODE ENDS
  END START
  

完整程序中有一个程序段为DELAYL,该程序段的功能也是延时,但是是在显示0K!时使用的。

2. 附加任务1

通过DOS系统功能调用(INT 21H)的25H,35H功能实现中断向量的设置和读取

功能号(AH)功能调用参数返回参数
25H设置中断向量DS: 段地址 DX: 偏移地址
AL: 中断类型号
35H获取中断向量AL: 中断类型号ES: 段地址 BX: 偏移地址

只需要修改START中的设置和读取中断向量的程序段即可,具体修改如下:

START:MOV AX,STACK
       	MOV SS,AX
       	MOV AX,DATA
       	MOV DS,AX
       	; MOV AX,0          ; 将AX置0,后将其赋给ES,进行原09H中断向量的保存
       	; MOV ES,AX
       	; MOV AX,ES:[24H]   ; 中断向量
       	; PUSH AX
       	; MOV AX,ES:[26H]   ; 中断向量
       	; PUSH AX
        MOV AH, 35H
        MOV AL, 09H         ; 使用DOS系统功能实现中断向量的读取
        INT 21H
        PUSH BX
        PUSH ES
       	CLI               ; 关中断,进行中断向量的设置
       	; MOV AX,OFFSET KEYINT
       	; MOV ES:[24H],AX
       	; MOV AX,SEG KEYINT
       	; MOV ES:[26H],AX 
        PUSH DS
        MOV AX, SEG KEYINT   ; 使用DOS系统功能实现中断向量的设置
        MOV DS, AX
        MOV DX, OFFSET KEYINT
        MOV AH, 25H
        MOV AL, 09H
        INT 21H 
        pop DS
       	STI               ; 同意下方程序接受中断
       	MOV  KEY,0
	AGAIN:CAll DISP1      ; 显示太阳
       	CMP KEY,10        ; 判断显示25行太阳后,是否已经按下了十次键
       	JB AGAIN
       	CLI               ; 禁止下方程序中断发生,保护代码运行
       	POP DS
       	POP DX
        MOV AH, 25H
        MOV AL, 09H
        INT 21H
       	STI               ; 开中断
       	MOV AH,4CH
       	INT 21H
CODE ENDS
	END START

3. 附加任务2

在显示0K!前面加上次数

实现该功能需要修改DISP2的程序

	DISP2 PROC FAR      ; 显示OK
        PUSH DX
        PUSH CX
       	PUSH BX
       	PUSH AX
        MOV DL, KEY
        ADD DL, 30H
        CMP DL, 3AH   ; 比较DL和3AH,即确定DL是不是10
        JAE EQUALTEN
        MOV AH,2
        INT 21H
     	MOV CX,3
	    NEXTC: LODSB  ; 字符串"OK!"在数据段中定义,AL<—[SI]
	    MOV AH, 0EH   ; 写字符, 并移动鼠标
        MOV BX,01
	    INT 10H
        CALL DELAYL
        LOOP NEXTC
    	POP AX
    	POP BX
    	POP CX
        POP DX
    	RET
        EQUALTEN: MOV DL, 31H     ; 当KEY>=10时
        MOV AH, 2
        INT 21H
        MOV DL, KEY
        ADD DL, 26H               ; 显示KEY的个位数
        MOV AH,2
        INT 21H
        MOV CX, 3
        NEXTCE: LODSB             ; 字符串"OK!"在数据段中定义,AL<—[SI]
        MOV AH, 0EH               ; 写字符, 并移动鼠标
        MOV BX,01
        INT 10H
        CALL DELAYL
        LOOP NEXTCE
        POP AX
        POP BX
        POP CX
        POP DX
        RET
	DISP2 ENDP

附加功能2中比较棘手的是需要在按键次数为10时对其进行正确显示,需要将其分为1和0来显示,这里我写的程序其实不够好,因为当按键次数超过20次后,又会出现字符显示错误的情况,不过由于只需要按下10次键盘就停止程序,也就没过多研究了。

4. 附加任务3

按键10次后,直接结束程序

这个功能容易实现,在附加任务2中我们在按键10次时跳转到EQUALTEN标号处,只需要在结尾加上返回DOS系统的命令即可

DISP2 PROC FAR      ; 显示OK
        PUSH DX
        PUSH CX
       	PUSH BX
       	PUSH AX
        MOV DL, KEY
        ADD DL, 30H
        CMP DL, 3AH
        JAE EQUALTEN
        MOV AH,2
        INT 21H
     	MOV CX,3
	    NEXTC: LODSB  ; 字符串"OK!"在数据段中定义,AL<—[SI]
	    MOV AH, 0EH   ; 写字符, 并移动鼠标
        MOV BX,01
	    INT 10H
        CALL DELAYL
        LOOP NEXTC
    	POP AX
    	POP BX
    	POP CX
        POP DX
    	RET
        EQUALTEN: MOV DL, 31h     ; 当KEY==10时
        MOV AH, 2
        INT 21H
        MOV DL, KEY
        ADD DL, 26H               ; 显示KEY的个位数
        MOV AH,2
        INT 21H
        MOV CX, 3
        NEXTCE: LODSB             ; 字符串"OK!"在数据段中定义,AL<—[SI]
        MOV AH, 0EH               ; 写字符, 并移动鼠标
        MOV BX,01
        INT 10H
        CALL DELAYL
        LOOP NEXTCE
        POP AX
        POP BX
        POP CX
        POP DX            ; 此时直接结束程序
        CLI               ; 禁止下方程序中断发生,保护代码运行
        POP DS
        POP DX
        MOV AH, 25H
        MOV AL, 09H
        INT 21H
        STI               ; 开中断
        MOV AH,4CH
        INT 21H
	DISP2 ENDP

与附加任务2中的代码进行比对后可以发现在DISP2的结尾删除了RET这一命令,加上了返回DOS系统的命令

5. 附加任务4

修改显示字符的属性,如红底白字,蓝底黄字…

个人感觉这个附加任务并不简单,也有可能是我理解错了,写得比较复杂。

首先先定义了一个宏过程(macro procedure),代码如下:

OUTPUTOK MACRO STR    ; 定义了一个宏,STR 为参数
        MOV AL, STR
        MOV BH, 0
        MOV BL, COLOROK
        MOV CX, 1
        MOV AH, 9
        INT 10H
        CALL Cursor
        CALL DELAYL
  ENDM

这里有一个子程序Cursor是之前没有接触过的,它的功能是为了移动光标,防止字符之间相互覆盖,这一子程序中通过BIOS系统功能调用(INT 10H)的02H,03H功能实现光标位置的设置和读取

功能号(AH)功能调用参数返回参数
02H设置光标位置BH: 显示页码
DH: 行 DL: 列
03H获取光标位置BH: 显示页码DH: 行 DL: 列

具体程序如下:

Cursor PROC             ; 将光标向后移动一位
        PUSH DX
        PUSH CX
        PUSH BX
        PUSH AX
        MOV AH, 03H
        INT 10H
        ADD DL, 01H       ; 将列数加一后设置光标位置
        MOV AH, 02H
        INT 10H
        POP AX
        POP BX
        POP CX
        POP DX  
        RET
  Cursor ENDP 

最后就要修改显示的函数DISP1DISP2了,具体代码如下

	DISP1 PROC FAR     ; 显示太阳
       	PUSH AX
       	PUSH BX
       	PUSH CX
       	PUSH DX
       	MOV AH,15     ; 读当前显示状态
       	INT 10H
       	MOV AH,0      ; 设置显示方式
       	INT 10H
       	MOV DX,0      ; 行号为0,列号为0
	REPT: MOV AH,2      ; 设置光标位置
      	INT 10H  
       	MOV AL,0FH    ; OFH一太阳图形的ASCII码
        MOV BH, 0
        MOV BL, COLORSUN
        MOV CX,1      ; 显示字符个数
       	MOV AH,09     ; 写字符
       	INT 10H
       	CALL DELAY
       	SUB AL,AL
       	MOV AH,0      ; 清除原图形
       	INT 10H
       	INC DH        ; 行号+1
       	ADD DL,2      ; 列号+1
       	CMP DH,25     ; 判断是否到25行,不等继续显示太阳,相等返回
       	JNE REPT
       	POP DX
       	POP CX
       	POP BX
       	POP AX
       	RET
 	DISP1 ENDP
	DISP2 PROC FAR      ; 显示OK
        PUSH DX
        PUSH CX
       	PUSH BX
       	PUSH AX
        MOV DL, KEY
        ADD DL, 30H
        CMP DL, 3AH
        JAE EQUALTEN
        MOV BH, 0
        MOV AL, DL
        MOV BL, COLORNUM
        MOV CX, 1
        MOV AH,9
        INT 10H
        CAll Cursor
        OUTPUTOK 'O'
        OUTPUTOK 'K'
        OUTPUTOK '!'
    	POP AX
    	POP BX
    	POP CX
        POP DX
    	RET
        EQUALTEN: MOV CX, 1
        MOV AL, 31h     ; 当KEY==10时
        MOV AH, 09
        MOV BL, COLORNUM
        MOV BH, 0
        INT 10H
        CAll Cursor
        MOV AL, KEY
        ADD AL, 26H               ; 显示KEY的个位数
        MOV AH,09
        MOV BL, COLORNUM
        MOV BH, 0
        INT 10H
        CALL Cursor
        OUTPUTOK 'O'
        OUTPUTOK 'K'
        OUTPUTOK '!'
        POP AX
        POP BX
        POP CX
        POP DX            ; 此时直接结束程序
        CLI               ; 禁止下方程序中断发生,保护代码运行
        POP DS
        POP DX
        mov ah, 25H
        mov al, 09H
        int 21H
        STI               ; 开中断
        MOV AH,4CH
        INT 21H
	DISP2 ENDP

显示带颜色的字符需要使用BIOS系统(INT 21H)功能号为9的功能

功能号(AH)功能调用参数返回参数
09H在当前光标处
写字符和属性
BH: 页号 AL: 显示字符的ASCⅡ码
BL: 属性   CX: 显示次数

在程序中我重写了显示OK!的程序,因为如果使用原来的程序,OK!将很难显示属性(当然有可能是我的水平不够,还有更加简单的方法)
在上面的程序和宏过程中大家可能发现了COLOROKCOLORSUNCOLORNUM这三个参数,这三个参数需要提前在数据段中定义:

DATA SEGMENT
	KEY DB ?           ; ?表示占用空间
  	COLORSUN DB 42H    ; 太阳字符的属性
  	COLORNUM DB 57H    ; 按键次数的属性
  	COLOROK DB 32H     ; OK!的属性
DATA ENDS

对于字符属性,高四位代表文字颜色,低四位代表背景颜色。颜色对照表如下:

十六进制颜色十六进制颜色
0黑色 8灰色
1蓝色 9淡蓝色
2绿色 A淡绿色
3青色 B淡青色
4红色 C淡红色
5紫红色 D淡紫红色
6棕色 E淡棕色
7银色F白色

42H即文字颜色为红色,背景颜色为绿色


五、结语

本文较长,感谢大家可以看到最后,如果想要源代码,可以在文章开头下载,也可以在百度网盘 上下载,提取码:xhmc

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值