luqingfei@C++

为中华之崛起而崛起!
兼听则明,偏听则暗。

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

 

使用BIOS进行键盘输入和磁盘读写

 

大多数有用的程序都需要处理用户的输入,键盘输入是最基本的输入。程序和数据通常需要长期存储,磁盘是最常用的存储设备。

BIOS为这两种外设的I/O提供了最基本的中断例程。下面将对它们的应用和相关问题进行学习。

 

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

键盘输入将引发9号中断,BIOS提供了int 9中断例程。

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

 

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

 

下面我们按照键盘缓冲区的逻辑结构,来看一下键盘输入的扫描码和对应的ASCII码是如何写入键盘缓冲区的。

 

注意:在我们的课程中,仅在逻辑结构的基础上,讨论BIOS键盘缓冲区的读写问题。其实键盘缓冲区是用环形队列结构管理的内存区,但我们不对队列和环形队列的实现进行讨论,因为那是另一门专业课《数据结构》的内容。

 

下面,我们通过下面几个键:

ABCDEshift_AA

的输入过程,简要地看一下int 9中断例程对键盘输入的处理方法:

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

1E61

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

1E61

3062

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4)按下CDE键后,缓冲区中的内容如下:

1E61

2062

2E63

2064

1265

 

 

 

 

 

 

 

 

 

 

 

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

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

1E61

2062

2E63

2064

1265

1E41

 

 

 

 

 

 

 

 

 

 

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

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

1E61

2062

2E63

2064

1265

1E41

1E61

 

 

 

 

 

 

 

 

 

 

 

 

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

BIOS提供了int 16h中断例程供程序员调用。int 16h中断例程中包含的一个最重要的功能是从键盘缓冲区中读取一个键盘输入,该功能的编号为0

下面的指令从键盘缓冲区中读取一个键盘输入,并将其从缓冲区中删除

mov ah,0

int 16h

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

 

int 16h中断例程的0号功能,进行如下的工作:

1)检测键盘缓冲区中是否有数据;

2)没有则继续做第1步;

3)读取缓冲区第一个字单元中的键盘输入;

4)将读取的扫描码送入ahASCII码送入al

5)将已读取的键盘输入从缓冲区中删除。

 

可见,BIOSint 9中断例程和int 16h中断例程是一对相互配合的程序,int 9中断例程向键盘缓冲区中写入,int 16h中断例程从缓冲区中读出。

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

 

我们在编写一般的处理键盘输入的程序的时候,可以调用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 cx,2000

      s:add 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)能够删除已经输入的字符。

 

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

子程序的参数如下:

(dh)(dl)=字符串在屏幕上显示的行、列位置;

ds:si指向字符串的存储空间,字符串以0为结尾符。

 

分析:

1)字符的输入和删除。

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

       看下面的过程:

       空字符串:

       输入“a”:a

       输入“b”:ab

       输入“c”:abc

       输入“d”:abcd

       删除一个字符:abc

       删除一个字符:ab

       删除一个字符:a

       删除一个字符:

 

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

 

2)在输入回车符后,字符串输入结束。

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

 

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

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

 

4)程序的处理过程。

       1)调用int 16h读取键盘输入;

       2)如果是字符,进入字符栈,显示字符栈中的所有字符;继续执行1

       3)如果是退格键,从字符栈中弹出一个字符,显示字符栈中的所有字符;继续执行1

       4)如果是Enter键,向字符栈中压入0,返回。

 

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

       子程序:字符栈的入栈、出栈和显示。

       参数说明:(ah)=功能号,0表示入栈,1表示出栈,2表示显示;

                            ds:si指向字符栈空间;

                            对于0号功能:(al)=入栈字符;

                            对于1号功能:(al)=返回字符;

                            对于2号功能:(dh)(dl)=字符串在屏幕上显示的行、列位置。

 

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,0b800b

                     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:[dl],’ ‘

                     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码小于0,说明不是字符

              mov ah,0

              call charstack         ;字符入栈

              mov ah,2

              call charstack         ;显示栈中的字符

              jmp getstrs

 nochar:cmp ah,0eh            ;退格键的扫描码

              je backspace

              cmp ah,1ch           ;回车键的扫描码

              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中断例程对磁盘进行读写

BIOS提供的访问磁盘的中断例程为int 13h

 

入口参数:

(ah)=int 13h的功能号(2表示读扇区、3表示写扇区)

(al)=读取的扇区数/写入的扇区数

(ch)=磁道号

(cl)=扇区号

(dh)=磁头号(对于软盘即面号,因为一个面用一个磁头来读写。)

(dl)=驱动器号       软驱从0开始,0:软驱A1:软驱B

                            硬盘从80h开始,80h:硬盘C81h:硬盘D

es:bx指向接收从扇区读入数据的内存区/将写入磁盘的数据

 

返回参数:

操作成功:(ah)=0(al)=读入的扇区数/写入的扇区数

操作失败:(ah)=出错代码

 

以下指令,读取001扇区的内容到内存单元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

 

以下指令,将0:200中的内容写入001扇区:

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,3

int 13h

 

 

 

 

PC机是如何进入操作系统的?

开机后,CPU自动进入到FFFF:0单元处执行,此处有一条转跳指令。CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。

 

初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。

 

硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。

如果设为从软盘启动操作系统,则int 19h将主要完成以下工作:

1)控制0号软驱,读取软盘001扇区的内容到0:7c00

2)将CS:IP指向0:7c00

 

软盘的001扇区中装有操作系统引导程序。int 19h将其装到0:7c00处后,设置CPU0:7c00开始执行此处的引导程序,操作系统被激活,控制计算机。

 

 

 

posted on 2010-08-04 16:50 luqingfei 阅读(5028) 评论(1)  编辑 收藏 引用 所属分类: 汇编语言基础学习

评论

# re: 汇编语言--使用BIOS进行键盘输入和磁盘读写 2011-06-13 19:22 abcgril

您好:
最近在写一个terminate and stay resident程式
从键盘接收"*"(shift+8)
然後会显示一段字串,如ABCD
但按其他键如"8"却没有反应

我已经写出大部分
可以传给你看看
目前程式是输入"*"和"8"都会显示一段ABCD
所以还没完成!
希望您可以帮忙
或是教我怎样写
不知道要怎样改了
谢谢  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2010年8月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

留言簿(6)

随笔分类(109)

随笔档案(105)

Blogers

Game

Life

NodeJs

Python

Useful Webs

大牛

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜