使用bios进行键盘读写
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