潘晶 發表於 2021-2-11 12:11:00

汇编语言 | 定制键盘输入的处理过程

<h1 id="汇编语言--定制键盘输入的处理过程">汇编语言 | 定制键盘输入的处理过程</h1>
<h2 id="一键盘输入的处理过程">一、键盘输入的处理过程</h2>
<ol>
<li>键盘产生扫描码</li>
<li>扫描码送入60h端口</li>
<li>引发9号中断</li>
<li>CPU执行int 9中断例程,处理键盘输入</li>
</ol>
<p>​      1.~3.由硬件系统用完成。4.中的int 9中断例程可以由DOS系统提供,也可以按照开发需求定制处理键盘的输入。</p>
<h2 id="二编程任务分析">二、编程任务分析</h2>
<ul>
<li>
<p>在屏幕中间依次显示'a'~'z',并可以让人看清</p>
</li>
<li>
<p>在显示的过程中按下Esc键后改变显示的颜色</p>
</li>
</ul>
<h2 id="三工作策略">三、工作策略</h2>
<ul>
<li>
<p>尽可能忽略硬件处理细节,充分利用BIOS提供的int 9中断例程处理硬件细节。</p>
</li>
<li>
<p>在改写后的中断例程中满足特定要求,并能调用BIOS的原int 9中断例程</p>
</li>
</ul>
<h2 id="四实现">四、实现</h2>
<h3 id="1依次显示az">(1)依次显示'a'~'z':</h3>
<pre><code class="language-assembly">        ; 显示'a'-'z'
        mov ax,0b800h
        mov es,ax
        mov ah,'a'
s:        mov es:,ah ; 操作显存,找到中间位置
        call delay
        inc ah
        cmp ah,'z'
        jna s
        mov ax,0
        mov es,ax
</code></pre>
<p>​                但是这样做字母切换太快,在屏幕上无法看清每个字母的显示。需要在每个字母显示后延时一段时间。</p>
<h3 id="2延时程序">(2)延时程序:</h3>
<p>​                用两个16位寄存器来存放32位的循环次数。一共循环10,0000h次。</p>
<pre><code class="language-assembly">        ; 定义延迟程序
        delay:
        push ax
        push dx
        mov dx,10h
        mov ax,0
s1:
        sub ax,1
        sbb dx,0
        cmp ax,0
        jne s1
        cmp dx,0
        jne s1
        pop dx
        pop ax
        ret
</code></pre>
<h3 id="3按下esc键后改变显示的颜色">(3)按下Esc键后改变显示的颜色</h3>
<p>​                键盘输入到达60h端口后会引发9号中断,CPU会转去执行int 9中断例程。</p>
<p>​                (1) 从60h端口读出键盘的输入</p>
<p>​                (2) 调用BIOS的int 9中断例程,处理硬件细节</p>
<ul>
<li>
<p>将中断向量表中的int 9中断例程的入口地址改为自编的中断处理程序的入口地址</p>
</li>
<li>
<p>在新中断例程中调用原有的int 9中断例程,因此仍需保存原来的int 9中断例程的地址</p>
<p>​         <strong>解决方案:将原来int 9中断例程的偏移地址和段地址保存在ds:和ds:单元中。</strong></p>
</li>
</ul>
<pre><code class="language-assembly">        ; 保存旧中断例程入口
        mov ax,0
        mov es,ax
        push es:
        pop ds:
        push es:
        pop ds:
      ; 设置新的中断向量的位置
        mov word ptr es:,offset int9
        mov es:,cs
</code></pre>
<p>​          调用原int 9指令的中断例程:</p>
<ol>
<li>
<p>标志寄存器入栈</p>
</li>
<li>
<p>IF=0, TF=0</p>
</li>
<li>
<p>CS、IP入栈</p>
</li>
<li>
<p>(IP) = ((ds)*16 + 0)</p>
<p>(CS) = ((ds)*16 + 2)</p>
</li>
</ol>
<pre><code class="language-assembly">        pushf    ; 标志寄存器入栈
        ; IF=0, TF=0
        pushf
        pop bx
        and bh,11111100b
        push bx
        popf
        ; CS、IP入栈 (IP) = ((ds)*16 + 0) (CS) = ((ds)*16 + 2)
        call dword ptr ds:
</code></pre>
<p>(3) 判断是否为Esc的扫描码,如果是则改变显示颜色。否则直接返回。</p>
<pre><code class="language-assembly">; 定义中断例程
int9:
        push ax
        push bx
        push es
        in al,60h
        pushf
        pushf
        pop bx
        and bh,11111100b
        push bx
        popf
        call dword ptr ds:

        cmp al,1       ; ESC扫描码1
        jne int9ret
        ; 改变颜色
        mov ax,0b800h
        mov es,ax
        inc byte ptr es:

int9ret:
        pop es
        pop bx
        pop ax
        iret
</code></pre>
<h2 id="五完整代码">五、完整代码</h2>
<pre><code class="language-assembly">; 编写int 9中断例程改变显示的颜色
; (1)从60h端口读出键盘的输入 in al,60h
; (2)调用BIOS的int 9中断例程, 处理硬件细节
; (3)判断是否为Esc扫描码 如果是则改变显示的颜色后返回
; 如果不是则直接返回
assume cs:code

stack segment
        db 128 dup (0)
stack ends

data segment
        dw 0,0
data ends

code segment
start:
        mov ax,stack
        mov ss,ax
        mov sp,128
        mov ax,data
        mov ds,ax
       
        ; 更改中断例程的入口地址
        mov ax,0
        mov es,ax
        push es:
        pop ds:
        push es:
        pop ds:
       
        mov word ptr es:,offset int9
        mov es:,cs
       
        ; 显示'a'-'z'
        mov ax,0b800h
        mov es,ax
        mov ah,'a'
s:        mov es:,ah
        call delay
        inc ah
        cmp ah,'z'
        jna s
        mov ax,0
        mov es,ax

        ; 恢复原来的地址
        push ds:
        pop es:
        push ds:
        pop es:
       
        mov ax,4c00h
        int 21h
       
        ; 定义延迟程序
        delay:
        push ax
        push dx
        mov dx,10h
        mov ax,0
s1:
        sub ax,1
        sbb dx,0
        cmp ax,0
        jne s1
        cmp dx,0
        jne s1
        pop dx
        pop ax
        ret

        ; 定义中断例程
int9:
        push ax
        push bx
        push es
        in al,60h
        pushf
        pushf
        pop bx
        and bh,11111100b
        push bx
        popf
        call dword ptr ds:

        cmp al,1       ; ESC扫描码为1
        jne int9ret
        ; 改变颜色
        mov ax,0b800h
        mov es,ax
        inc byte ptr es: ; 高8位存放的是字符的属性

int9ret:
        pop es
        pop bx
        pop ax
        iret
code ends

end start
</code></pre><br><br>
来源:https://www.cnblogs.com/tedukuri/p/14397572.html
頁: [1]
查看完整版本: 汇编语言 | 定制键盘输入的处理过程