• 汇编语言-18使用BIOS进行键盘输入和磁盘读写


    大多数有用的程序都需要处理用户的输入,键盘输入是最基本的输入。程序和数据通常需要长期存储,磁盘是最常用的存储设备。BIOS为这两种外设的I/O提供了最基本的中断例程。

    int 9中断例程对键盘输入的处理

    键盘输入将引发9号中断, BIOS提供了int 9中断例程。CPU在9号中断发生后,执行int 9中断例程,从60h端口读出扫描码,并将其转化为相应的ASCII码或状态信息,存储在内存的指定空间(键盘缓冲区或状态字节)中。

    一般的键盘输入,在CPU执行完int 9中断例程后,都放到了键盘缓冲区中。键盘缓冲区中有16个字单元,可以存储15个按键的扫描码和对应的ASCll码。

    A、B、C、D、E、Shift_A、A的输入过程,int 9中断例程对键盘输入的处理方法:

    (1) 初始状态下,没有键盘输入,键盘缓冲区空,此时没有任何元素。

    (2) 按下A键,引发键盘中断;CPU执行int 9中断例程,从60h端口读出A键的通码;然后检测状态字节, 看看是否有Shift 、Ctrl等切换键按下;发现没有切换键按下, 则将A键的扫描码1eh和对应的ASCII码,即字母"a"的ASCII码61h,写入键盘缓冲区。缓冲区的字单元中,高位字节存储扫描码,低位字节存储ASCII码。此时缓冲区中的内容如下:

    (3) 按下B键,引发键盘中断; CPU 执行int 9中断例程,从60h端口读出B键的通码;然后检测状态字节,看看是否有切换键按下;发现没有切换键按下,将B键的扫描码30h和对应的ASCII码,即字母"b"的ASCII码62h, 写入键盘缓冲区。此时缓冲区中的内容如下:

    (4) 按下C、D、E键后,缓冲区中的内容如下:

    (5) 按下左Shift键,引发键盘中断; int 9中断例程接收左Shift键的通码,设置0040:17处的状态字节的第1位为1, 表示左Shift 键按下。

    (6) 按下A键,引发键盘中断; CPU执行int 9中断例程,从60h端口读出A键的通码;检测状态字节,看看是否有切换键按下;发现左Shift 键被按下,则将A 键的扫描码1Eh和Shift_A对应的ASCII码,即字母"A"的ASCII码41h,写入键盘缓冲区。此时缓冲区中的内容如下:

    (7) 松开左Shift键,引发键盘中断;int 9中断例程接收左Shift键的断码,设置0040:17处的状态字节的第1位为0, 表示左Shift键松开。

    (8) 按下A键,引发键盘中断:CPU执行int 9中断例程,从60h端口读出A键的通码;然后检测状态字节,看看是否有切换键按下:发现没有切换键按下,则将A键的扫描码1Eh和A对应的ASCII码,即字母"a"的ASCII码61h,写入键盘缓冲区。此时缓冲区中的内容如下:

    使用int 16h中断例程读取键盘缓冲区

    BIOS提供了int 16h中断例程供程序员调用。int 16h中断例程中包含的一个最重要的功能是从键盘缓冲区中读取一个键盘输入,该功能的编号为0。下面的指令从键盘缓冲区中读取一个键盘输入,并且将其从缓冲区中删除:

    mov ah,0
    int 16h
    

    结果: (ah)=扫描码,(al) =ASCII码。

    int 16h读取键盘缓冲区:

    (1) 执行

    mov ah,0
    int 16h
    

    后,缓冲区中的内容如下:

    ah中的内容为1Eh,al中的内容为61h。

    (2) 执行

    mov ah,0
    int 16h
    

    后,缓冲区中的内容如下:

    ah中的内容为30h, al中的内容为62h。

    (3) 执行

    mov ah,0
    int 16h
    

    后,缓冲区中的内容如下:

    (4) 执行4次

    mov ah,0
    int 16h
    

    后,缓冲区空。

    (5) 执行

    mov ah,0
    int 16h
    

    int 16h中断例程检测键盘缓冲区,发现缓冲区空,则循环等待,直到缓冲区中有数据。

    (6) 按下A键后,缓冲区中的内容如下。

    (7) 循环等待的int 16h 中断例程检测到键盘缓冲区中有数据,将其读出,缓冲区又为空。

    ah中的内容为1Eh,al中的内容为61h。

    从上面可以看出,int 16h中断例程的0号功能,进行如下的工作。

    (1) 检测键盘缓冲区中是否有数据;
    (2) 没有则继续做第1步;
    (3) 读取缓冲区第一个字单元中的键盘输入;
    (4) 将读取的扫描码送入ah,ASCII码送入al;
    (5) 将已读取的键盘输入从缓冲区中删除。

    BIOS的int 9中断例程和int 16h中断例程是一对相互配合的程序, int 9中断例程向键盘缓冲区中写入, int 16h中断例程从缓冲区中读出。它们写入和读出的时机不同, int 9中断例程是在有键按下的时候向键盘缓冲区中写入数据;而int 16h中断例程是在应用程序对其进行调用的时候,将数据从键盘缓冲区中读出。

    编程,接收用户的键盘输入,输入"r"'将屏幕上的字符设置为红色;输入"g"将屏幕上的字符设置为绿色;输入"b"将屏幕上的字符设置为蓝色。

    assume cs:code
    
    code segment
    start: mov ah,0
        int 16h
    
        mov ah,1
        cmp al,'r'
        je red
        cmp al,'g'
        je green
        cmp al,'b'
        je blue
        jmp short sret
    
    red: shl ah,1
    
    green: shl ah,1
    
    blue:mov bx,0b800h
            mov es,bx
            mov bx,1
            mov cx,2000
        s: and byte ptr es:[bx],11111000b
            or es:[bx],ah
            add bx,2
            loop s
            
        sret: mov ax,4c00h
            int 21h
    
    code ends
    end start
    

    字符串的输入

    最基本的字符串输入程序,需要具备下面的功能。

    (1) 在输入的同时需要显示这个字符串;
    (2) 一般在输入回车符后,字符串输入结束;
    (3) 能够删除已经输入的字符。

    编写一个接收字符串输入的子程序,实现上面3 个基本功能。

    子程序的参数如下:

    (dh) 、(dl)=字符串在屏幕上显示的行、列位置;
    ds:si指向字符串的存储空间,字符串以0 为结尾符。

    (1) 字符的输入和删除
    每个新输入的字符都存储在前一个输入的字符之后,而删除是从最后面的字符进行的。

    空字符串:
    输入"a" : a
    输入"b" : ab
    输入"c" : abc
    输入"d" : abed
    删除一个字符: abc
    删除一个字符: ab
    删除一个字符: a
    删除一个字符:

    在字符串输入的过程中,字符的输入和输出是按照栈的访问规则进行的,即后进先出。这样就可以用栈的方式来管理字符串的存储空间,也就是说,字符串的存储空间实际上是一个字符栈。字符栈中的所有字符,从栈底到栈顶,组成一个字符串。

    (2) 在输入回车符后,字符串输入结束。
    输入回车符后,可以在字符串中加入0, 表示字符串结束。

    (3) 在输入的同时需要显示这个字符串。

    每次有新的字符输入和删除一个字符的时候,都应该重新显示字符串,即从字符栈的栈底到栈顶,显示所有的字符。

    (4) 程序的处理过程。

    1.调用int 16h读取键盘输入;
    2.如果是字符,进入字符栈,显示字符栈中的所有字符;继续执行0;
    3.如果是退格键,从字符栈中弹出一个字符,显示字符栈中的所有字符;继续执
    行1;
    4.如果是Enter键,向字符栈中压入0,返回。

    字符栈的入栈、出栈和显示栈中的内容,是需要在多处使用的功能,应该将它们写为子程序。

    子程序:字符栈的入栈、出栈和显示。
    参数说明: (ah)=功能号, 0表示入栈, 1表示出栈, 2表示显示;
    ds:si 指向字符栈空间;
    对于0 号功能: (al)=入栈字符;
    对于1 号功能: (al)=返回的字符;
    对于2 号功能: (dh) 、(di)=字符串在屏幕上显示的行、列位置。

    charstack:jmp short charstart
    
    table dw charpush,charpop,charshow
    top dw 0  ;栈顶
    
    charstart: push bx
            push dx
            push di
            push es
            
            cmp ah,2
            ja sret
            mov bl,ah
            mov bh,0
            add bx,bx
            jmp word ptr table[bx]
    
    charpush: mov bx,top
            mov [si][bx],al
            inc top
            jmp sret
    
    charpop:cmp top,0
            je sret
            dec top
            mov bx,top
            mov al,[si][bx]
            jmp sret
            
    charshow: mov bx,0b800h
            mov es,bx
            mov al,160
            mov ah,0
            mul dh
            mov di,ax
            add dl,dl
            mov dh,0
            add di,dx
    
            mov bx,0
    
    charshows: cmp bx,top
    		jne noempty
    		mov byte ptr es:[di],' '
    		jmp sret
    		
    noempty:mov al,[si][bx]
            mov es:[di],al
            mov byte ptr es:[di+2],' '
            inc bx
            add di,2
            jmp charshows
    
    sret: pop es
            pop di
            pop dx
            pop bx
            ret
    

    上面的子程序中,字符栈的访问规则如下:

    另外一个要注意的问题是,显示栈中字符的时候,要注意清除屏幕上上一次显示的内容。
    现在写出完整的接收字符串输入的子程序:

    getstr:push ax
    
    getstrs:mov ah,0
            int 16h
            cmp al,20h   
            jb nochar    ;ASCII码小于20h,说明不是字符
            mov ah,0
            call charstack   ;字符入栈
            mov ah,2
            call charstack    ;显示栈中的字符
            jmp getstrs
    
    nochar:cmp ah,0eh    ;退格键的扫描码
            je backspace
            cmp ah,lch    ;Enter键的扫描码
            je enter
            jmp getstrs
    
    backspace: mov ah,1
            call charstack    ;字符出栈
            mov ah,2
            call charstack    ;显示栈中的字符
            jmp getstrs
    
    enter: mov al,0
            mov ah,0
            call charstack    ;0入栈
            mov ah,2
            call charstack    ;显示栈中的字符
            pop ax
            ret
    

    应用int 13h中断例程对磁盘进行读写

    以3.5英寸软盘为例,3.5英寸软盘分为上下两面,每面有80个磁道,每个磁道又分为18个扇区,每个扇区的大小为512个字节。

    则: 2 面*80 磁道*18 扇区* 512字节= 1440KB = 1.44MB

    磁盘的实际访问由磁盘控制器进行,可以通过控制磁盘控制器来访问磁盘。只能以扇区为单位对磁盘进行读写。在读写扇区的时候,要给出面号、磁道号和扇区号。面号和磁道号从0 开始,而扇区号从1开始。

    如果通过直接控制磁盘控制器来访问磁盘, 则需要涉及许多硬件细节。BIOS提供了对扇区进行读写的中断例程,这些中断例程完成了许多复杂的和硬件相关的工作。通过调用BIOS中断例程来访问磁盘。

    BIOS提供的访问磁盘的中断例程为int 13h 。读取0面0道1扇区的内容到0:200的程序如下所示:

    mov ax,0
    mov es,ax
    mov bx,200h
    
    mov al,1
    mov ch,0
    mov cl,1
    mov dl,0
    mov dh,0
    mov ah,2
    int 13h
    

    入口参数:
    (ah)=int 13h的功能号(2表示读扇区)
    (al)=读取的扇区数
    (ch)=磁道号
    (cl)=扇区号
    (dh)=磁头号(对于软盘即面号,因为一个面用一个磁头来读写)
    (dl)=驱动器号 软驱从0开始,0:软驱A,1:软驱B;
    硬盘从80h开始,80h:硬盘C,81h:硬盘D
    es:bx指向接收从扇区读入数据的内存区

    返回参数:
    操作成功: (ah)=0, (al)=读入的扇区数
    操作失败: (ah)=出错代码
    将0:200中的内容写入0面0道1扇区。

    mov ax,O
    mov es,ax
    mov bx,200h
    
    mov al,1
    mov ch,O
    mov cl,1
    mov dl,O
    mov dh,O
    
    mov ah,3
    int 13h
    

    入口参数:
    (ah)=int 13h的功能号(3表示写扇区)
    (al)=写入的扇区数
    (ch)=磁道号
    (cl)=扇区号
    (dh)=磁头号(面)
    (dl)=驱动器号 软驱从0开始, 0:软驱A,1:软驱B;
    硬盘从80h 开始,80h:硬盘C,81h: 硬盘D
    es:bx指向将写入磁盘的数据

    返回参数:
    操作成功:(ah)=0,(al)=写入的扇区数
    操作失败:(ah)=出错代码

    注意,使用int 13h中断例程对软盘进行读写。直接向磁盘扇区写入数据是很危险的,很可能覆盖掉重要的数据。如果向软盘的0面0道1扇区中写入了数据,要使软盘在现有的操作系统下可以使用,必须要重新格式化。在编写相关的程序之前,必须要找一张空闲的软盘。在使用int 13h中断例程时一定要注意驱动器号是否正确,千万不要随便对硬盘中的扇区进行写入。

    编程:将当前屏幕的内容保存在磁盘上:

    分析: 1屏的内容占4000个字节,需要8个扇区,用0面0道的1~8扇区存储显存中的内容。程序如下。

    assume cs:code
    code segment
    	start: mov ax,0b800h
            mov es,ax
            mov bx,O
            
            mov al,8
            mov ch,0
            mov cl,1
            mov dl,O
            mov dh,O
            mov ah,3
            int 13h
            
            mov ax,4c00h
            int 21h
    code ends
    end start
    
  • 相关阅读:
    OCR文字识别【前端渲染,后端进行逻辑处理】
    前端vue实现高级检索小案例
    Pycharm安装中文语言插件Chinese【木鱼快速安装版】
    vue快速实现锚点功能【简单版与高级版】
    前端实现elpagination分页的两种业务场景,你知道吗?
    富文本编辑器应该这么用【博客文章发布、日常记录神器】
    vue实现按钮弹框【弹出图片、视频、表格、表单等】
    前端vue项目最强优化美化浏览器右侧滚动条样式与elscrollbar
    UI设计指南之可视化大屏【快速理解版】
    前端添加水印效果攻略【vue和原生js添加方式】
  • 原文地址:https://www.cnblogs.com/aeolian/p/13857944.html
Copyright © 2020-2023  润新知