使用bios进行键盘读写

Tutorial: 汇编基础 Category: C语言 Published: 2026-04-07 13:58:26 Views: 20 Likes: 0 Comments: 0
1. 键盘缓冲区
  • 15 个字型数据, 填满会报警
  • 高位:扫描码, 低位:ASCII 码
  • 按下 a: 1E61H
  • 按下 b: 3062H

BIOS 提供的 int 16H 中断 读取时:

  • ah => 扫描码
  • al => ASCII 码
假设缓冲区如下:1E61H,3062H

第1次时:
mov ah,0
int 16h

ah = 1E
al = 61

缓冲区剩下:3062H

第2次时:
mov ah,0
int 16h

ah = 30H
al = 62H

2. int 16h 做了什么
  • 检测缓冲区是否有数据
  • 没有的话,继续检测
  • 有的话,读取缓冲区第 1 个字型数据(键盘输入)
  • AH = 高位 = 扫描码, AL = 低位 = ASCII 码
  • 删除读取的字形数据
  • 与 int 9 互相配合
3. 接收用户键盘输入
; 接收用户键盘输入
; ======================================================
; 接收用户键盘输入,输入"r",将屏幕上的字符设置为红色;输入"g",将屏幕上的字符设置为绿色;输入"b",将屏幕上的字符设置为蓝色
; ======================================================

; 功能编号:0(ah读取)
;
; mov ah, 0
; int 16h
; 读取的 扫描码送入 ah,ASCII码送入 al
; ======================================================

assume cs:codesg, ss:stacksg
; ======================================================
stacksg segment stack
                db 128 dup (0)
stacksg ends
; ======================================================
codesg segment
        start: mov  ax, stacksg
               mov  ss, ax
               mov  sp, 128

               mov  ah, 0
               int  16h

               mov  ah, 1
               cmp  al, 'r'
               je   red

               cmp  al, 'g'
               je   green

               cmp  al, 'b'
               je   blue

               jmp  _ret

        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], 11110000B        ; 将低位字节(前景属性)全设为0
               or   es:[bx], ah
               add  bx, 2
               loop s

        _ret:  mov  ax, 4c00h
               int  21h

        ; =============================================
        ; 效果:因为int 16h的地方是从缓冲区读取,所以在输入对应字符以后才会执行
        ; 否则程序一直处于等待状态

codesg ends
end start
4. 字符串输入
; 字符串输入
; ======================================================

; 最基本的字符串输入程序,需要具备以下功能
; 1. 在输入的同时需要显示这个字符串
; 2. 一般在输入回车符后,字符串输入结束
; 3. 能够删除已经输入的字符串

; ======================================================

; 编写一个接收字符串输入的子程序,实现上面3个基本功能。因为在输入的过程中需要显示,子程序的参数如下
; (dh)、(dl) = 字符串在屏幕上显示的行、列位置
; ds:si 指向字符串的储存空间,字符串以0为结尾符

; ======================================================

; 功能编号:0(ah读取)
;
; mov ah, 0
; int 16h
; 读取的 扫描码送入 ah,ASCII码送入 al
; ======================================================
assume cs:codesg, ss:stacksg
; ======================================================
stacksg segment stack
                db 128 dup (0)
stacksg ends
; ======================================================
codesg segment
        start:     mov  ax, stacksg
                   mov  ss, ax
                   mov  sp, 128

                   call get_str

                   mov  ax, 4c00h
                   int  21h

        ; ===================================
        ; 子程序:接收字符串输入
        get_str:   push ax

        _getStrs:  mov  ah, 0
                   int  16h
                   cmp  al, 20h                          ; ASCII码小于20h说明不是字符
                   jb   nochar
                   mov  ah, 0
                   call char_stack                       ; 字符入栈
                   mov  ah, 2
                   call char_stack                       ; 显示栈中字符
                   jmp  _getStrs

        nochar:    cmp  ah, 0eh                          ; 退格键的扫描码
                   je   _backspace
                   cmp  ah, 1ch                          ; Enter 的扫描码
                   je   _enter
                   jmp  _getStrs

        _backspace:mov  ah, 1
                   call char_stack                       ; 字符出栈
                   mov  ah, 2
                   call char_stack
                   jmp  _getStrs

        _enter:    mov  al, 0
                   mov  ah, 0
                   call char_stack                       ; 0 入栈
                   mov  ah, 2
                   call char_stack                       ; 显示栈中字符串
                   pop  ax
                   ret


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

        _charStart:push bx
                   push dx
                   push di
                   push es


                   cmp  ah, 2
                   ja   _ret
                   mov  bl, ah
                   mov  bh, 0
                   add  bx, bx
                   jmp  word ptr table[bx]

        _push:     mov  bx, top
                   mov  [si][bx], al
                   inc  top
                   jmp  _ret

        _pop:      cmp  top, 0
                   je   _ret
                   dec  top
                   mov  bx, top
                   mov  al, [si][bx]
                   jmp  _ret

        _show:     mov  bx, 0b800h
                   mov  es, bx
                   mov  al, 160
                   mov  ah, 0
                   mul  dh
                   mov  di, ax
                   add  dl, dl
                   mov  ah, 0
                   add  di, ax

                   mov  bx, 0

        _shows:    cmp  bx, top
                   jne  _noempty
                   mov  byte ptr es:[di], ' '
                   jmp  _ret

        _noempty:  mov  al, [si][bx]
                   mov  es:[di], al
                   mov  byte ptr es:[di + 2], ' '
                   inc  bx
                   add  di, 2
                   jmp  _shows

        _ret:      pop  es
                   pop  di
                   pop  dx
                   pop  bx
                   ret

codesg ends
end start
5. int 13h 对磁盘进行读写

软盘:2 面, 1 面= 80 磁道, 1 磁道 = 18 扇区, 1 扇区 = 512 字节(Byte), 总共 1440KB,大约 1.44MB 面号:从 0 开始 磁道:从 0 开始 扇区:从 1 开始

; 读取
mov ax,0
mov es,ax
mov bx,200H ; es:[bx]

mov dl,0    ; 驱动器号
mov dh,0    ; 面号,磁头号
mov ch,0    ; 磁道号
mov cl,1    ; 扇区号


mov al,1    ; 读取的扇区数
mov ah,2    ; 表示读取
int 13H


; 写入
mov ax,0
mov es,ax
mov bx,200H ; es:[bx]

mov dl,0    ; 驱动器号
mov dh,0    ; 面号,磁头号
mov ch,0    ; 磁道号
mov cl,1    ; 扇区号


mov al,1    ; 写入的扇区数
mov ah,3    ; 表示写入
int 13H
6. 实验 17

安装一个新的 int 7ch 中断例程,实现通过逻辑扇区号对软盘进行读写

ah 传递功能号:0 表示读 1 表示写 dx 传递逻辑扇区号 es:bx 指向读出/写入数据的内存区 提示:可调用 int 13h 进行实际的读写

逻辑扇区号=(面号*80 + 磁道号)*18 + 扇区号-1
反过来
面号=int(逻辑扇区号/1440)
磁道号=int(rem(逻辑扇区号/1440)/18)
扇区号=rem(rem(逻辑扇区号/1440)/18)+1
int():取商
rem():取余数
assume cs:code

code segment
        start:     mov  ax,cs
                   mov  ds,ax
                   mov  si,offset int7cstart

                   mov  ax,0
                   mov  es,ax
                   mov  di,200h

                   mov  cx,offset int7cend-offset int7cstart
                   cld
                   rep  movsb

                   cli
                   mov  ax,0
                   mov  ds,ax
                   mov  word ptr ds:[4*7ch],200h
                   mov  word ptr ds:[4*7ch+2],0
                   sti

                   mov  ax,4c00h
                   int  21h

        int7cstart:cmp  ah,1
                   ja   ok                                          ; 违法的功能号

                   push ax                                          ; 保护现场
                   push bx
                   push cx
                   push dx

                   push ax                                          ; ah中存着功能号

                   mov  ax,dx                                       ; 传递逻辑扇区号
                   mov  dx,0
                   mov  cx,1440
                   div  cx
                   push ax                                          ; ax保存得到的商,即面号,入栈保存

                   mov  cx,18
                   mov  ax,dx                                       ; 所得结果的余数传递给ax,继续做除法
                   mov  dx,0
                   div  cx
                   push ax                                          ; ax保存得到的商,即磁道号
                   inc  dx                                          ; 计算出扇区号
                   push dx                                          ; 入栈保存

                   pop  ax
                   mov  cl,al                                       ; 扇区号
                   pop  ax
                   mov  ch,al                                       ; 磁道号
                   pop  ax
                   mov  dh,al                                       ; 面号
                   mov  dl,0                                        ; 驱动器号
                   pop  ax                                          ; 弹出ax,其中ah中存着功能号
                   mov  al,1                                        ; 扇区数

                   cmp  ah,0
                   je   read
                   cmp  ah,1
                   je   write

        read:      mov  ah,2
                   jmp  short func
        write:     mov  ah,3
                   jmp  short func
        func:      int  13h

                   pop  dx                                          ; 恢复现场
                   pop  cx
                   pop  bx
                   pop  ax

        ok:        iret

        int7cend:  nop

code ends
end start
Prev: 实验16 Next: 课程设计2