《汇编语言》- 读书笔记 - 第17章-使用 BIOS 进行键盘输入和磁盘读写
- 17.1 int9 中断例程对键盘输入的处理
- 键盘缓冲区
- 17.2 使用 int 16h 中断例程读取键盘缓冲区
- 编程
- 检测点 17.1
- 17.3 字符串的输入
- 编程:字符串输入程序
- 需求
- 分析
- 处理过程
- 子程序
- 完整代码
- 17.4 应用 int 13h 中断例程对磁盘进行读写
- 3.5 英寸 1.44兆 软盘
- INT 13H 直接磁盘服务 Direct Disk Service
- 功能 02H 读扇区
- 功能 03H 写扇区
- 编程:将当前屏幕的内容保存在磁盘上
- 实验 17 编写包含多个功能子程序的中断例程
- 课程设计 2
17.1 int9 中断例程对键盘输入的处理
键盘缓冲区
输入: A、B、C、D、E、Shift_A、A
- 写入顺序从左➡️右
- | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
写入前 | |||||||||||||||
输入A | 1E61 | ||||||||||||||
输入B | 1E61 | 3062 | |||||||||||||
输入C | 1E61 | 3062 | 2E62 | ||||||||||||
输入D | 1E61 | 3062 | 2E62 | 2064 | |||||||||||
输入E | 1E61 | 3062 | 2E62 | 2064 | 1265 | ||||||||||
输入Shift_A | 1E61 | 3062 | 2E62 | 2064 | 1265 | 1E41 | |||||||||
输入A | 1E61 | 3062 | 2E62 | 2064 | 1265 | 1E41 | 1E61 |
17.2 使用 int 16h 中断例程读取键盘缓冲区
从键盘缓冲区中读取一个键盘输入,并且将其从缓冲区中删除:
mov ah,0
int 16h
结果: (ah
)=扫描码,(al
)=ASCII 码。
- | 读取字符 | ah | al | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
读取前 | 1E61 | 3062 | 2E62 | 2064 | 1265 | 1E41 | 1E61 | |||||||||||
执行1次 | A | 1E | 61 | 3062 | 2E62 | 2064 | 1265 | 1E41 | 1E61 | |||||||||
执行2次 | B | 30 | 62 | 2E62 | 2064 | 1265 | 1E41 | 1E61 | ||||||||||
执行3次 | C | 2E | 62 | 2064 | 1265 | 1E41 | 1E61 | |||||||||||
执行4次 | D | 20 | 64 | 1265 | 1E41 | 1E61 | ||||||||||||
执行5次 | E | 12 | 65 | 1E41 | 1E61 | |||||||||||||
执行6次 | Shift_A | 1E | 41 | 1E61 | ||||||||||||||
执行7次 | A | 1E | 61 |
从上面我们可以看出,int 16h 中断例程的0号功能,进行如下的工作。
- 检测键盘缓冲区中是否有数据;
- 没有则继续做第1步;
- 读取缓冲区第一个字单元中的键盘输入;
- 将读取的扫描码送入 ah,ASCII码送入 al;
- 将已读取的键盘输入从缓冲区中删除。
BIOS 的 int 9中断例程
和 int l6h中断例程
配合工作。
按键触发 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
— 划 — 线 —— | 说 明 —————————— | 闪烁 | 背景红 | 背景绿 | 背景蓝 | 高亮 | 前景红 | 前景绿 | 前景蓝 |
---|---|---|---|---|---|---|---|---|---|
mov ah,1 | 设置颜色为 1,对应蓝色 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
red: shl ah,1 | 左移一位后成为绿色 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
green: shl ah,1 | 再左移一位后成为红色 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
- 最开始给
ah
默认值是0000 0001b
蓝色。 - 如果是按
r
跳到标号red
向下执行到blue
总共左移2
次。成为红色 - 如果是按
g
跳到标号green
向下执行到blue
总共左移1
次。成为绿色 - 如果是按
b
跳到标号blue
无需左移。直接使用蓝色
r | g | b |
---|---|---|
检测点 17.1
《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 17.1
17.3 字符串的输入
编程:字符串输入程序
需求
- 在输入的同时需要显示这个字符串;
- 一般在输入回车符后,字符串输入结束;
- 能够删除已经输入的字符。
参考命令行输入场景。
分析
处理过程
子程序
- charstack 入栈、出栈和显示
我们将上图中的几块业务,拆为子程序来实现。
子程序 | 字符栈的入栈、出栈和显示。 |
---|---|
参数 | (ah )=功能号,0=入栈,1=出栈,2=显示 |
– | 0 号功能(入栈): (al )=入栈的字符1 号功能(出栈): ( al )=返回的字符2 号功能(显示): ( dh )=行、(dl )=列 |
参数 | ds:si 指向字符栈空间 |
- getstr 获取字符串
接收字符串输入,判断后调用:入栈、出栈和显示
完整代码
assume cs:code,ds:data
data segment
mystr db 255 dup(0) ; 声明字符栈
data ends
code segment
start: mov ax,data ; 设置字符栈所在段地址
mov ds,ax
mov ax,offset mystr ; 设置字符栈偏移地址
mov di,ax
call getstr ; 调用子程序开始字符串输入
ok: mov ax,4c00h
int 21h
; =======================================================
; ------------------- 子程序 -----------------
; 接收字符串输入,判断后调用:入栈、出栈和显示
; -------------------------------------------------------
; 参数:无
; 返回:无
; -------------------------------------------------------
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 ; 对比扫描码,如果是【退格键】跳 backspace
je backspace
cmp ah,1ch ; 对比扫描码,如果是【Enter】跳 enter
je enterkey
jmp getstrs ; 跳开头,重新开始获取输入字符
backspace:
mov ah,1 ; 调用子程序 charstack 的功能1 出栈一个字符
call charstack
mov ah,2 ; 调用子程序 charstack 的功能2 显示字符串
call charstack
jmp getstrs ; 跳开头,重新开始获取输入字符
enterkey:
mov ah,0 ; 调用子程序 charstack 的功能0 入栈一个字符 cs:38
mov al,0 ; 入栈的字符 = 0
call charstack
mov ah,2 ; 调用子程序 charstack 的功能2 显示字符串
call charstack
pop ax ; 还原寄存器
ret
; -------------------- 子程序 -----------------
; =======================================================
; =======================================================
; ------------------- 子程序 -----------------
; 字符栈的入栈、出栈和显示
; -------------------------------------------------------
; 参数:(ah)=功能号,0=入栈,1=出栈,2=显示
; 0 号功能(入栈): (al)=入栈的字符
; 1 号功能(出栈): (al)=返回的字符
; 2 号功能(显示): (dh)=行、(dl)=列
; 参数:ds:si 指向字符栈空间
; -------------------------------------------------------
charstack:
jmp short charstart ; 直接跳过数据块,到代码开关
table dw charpush,charpop,charshow ; 子程序入口
top dw 0 ; 字符串长度
charstart:
push bx ; 备份寄存器
push dx
push di
push es
; 检查 ah 功能号:如果>2退出。否则按索引调用子程序
cmp ah,2
ja sret
mov bl,ah
mov bh,0
add bx,bx ; table 是 dw 类型,所以是bx*2
jmp word ptr table[bx] ; 调用子程序
charpush: ; 字符入栈
mov bx,top
mov [si][bx],al ; al中的字符,存入 ds:[si+bx]
inc top ; top + 1 对应字符串长度
jmp sret ; 跳结束
charpop: ; 上一个字符出栈
cmp top,0 ; 如果字符串空了,直接结束
je sret
dec top ; top - 1 对应字符串长度
mov bx,top
mov al,[si][bx] ; 从 ds:[si+bx] 取字符存入 al
jmp sret ; 跳结束
charshow: ; 显示字符(算出显示位置)
mov bx,0b800h ; 设置显存
mov es,bx
mov al,160 ; 每行160字节,用于计算行偏移
mov ah,0
mul dh ; 行偏移 = 每行字节数al * 行数dh
mov di,ax ; 行偏移存入 di
add dl,dl ; 列偏移 = 列号dl * 2
mov dh,0
add di,dx ; 显示位置di = 行偏移di + 列偏移dx
mov bx,0 ; 字符索引置 0 用作是否打印的判断条件
charshows:
cmp bx,top ; 循环类似于 for(bx=0; bx < top; bx++)
jne noempty ; bx == top 时 noempty
mov byte ptr es:[di],' ' ; 末尾多打个“空格”
; 用于覆盖掉被退格的字符
jmp sret ; 跳结束
noempty: ; 字符串非空,就打印出来
mov al,[si][bx] ; 从 ds:[si+bx] 取字符
mov es:[di],al ; 显示到 es:[di]
mov byte ptr es:[di+2],' ' ; 每个字符后插入一个空格
; 开路先锋,刷掉屏幕上原有字符,
; 让字符串看上去末尾总有个留
inc bx ; bx 指向下一个字符
add di,2 ; di 指向下一个显示字符的位置
jmp charshows ; 跳转 charshows 直到所有字符打印完成
sret:
pop es ; 还原寄存器
pop di
pop dx
pop bx
ret
; -------------------- 子程序 -----------------
; =======================================================
code ends
end start
17.4 应用 int 13h 中断例程对磁盘进行读写
3.5 英寸 1.44兆 软盘
面 | 磁道 | 扇区 | 字节 | 大小 |
---|---|---|---|---|
2 | 80 | 18 | 512 | 1440KB ≈ 4.44M |
INT 13H 直接磁盘服务 Direct Disk Service
功能 02H 读扇区
功能02H | 读扇区 |
---|---|
入口参数 | AH = 02H |
参数 | AL =扇区数(要读多少扇区)DL =驱动器,00H~7FH 软盘(0=A,1=B);80H~0FFH 硬盘(80h=C,81h=B) CH =柱面(磁道号)DH =磁头(面)CL =扇区ES:BX =缓冲区的地址(指向接收数据的内存区) |
返回 | 操作成功:ah =0; al =读取的扇区数操作失败: ah =错误代码 |
功能 03H 写扇区
功能03H | 写扇区 |
---|---|
入口参数 | AH = 03H |
参数 | AL =扇区数(要写多少扇区)DL =驱动器,00H~7FH 软盘(0=A,1=B);80H~0FFH 硬盘(80h=C,81h=B) CH =柱面(磁道号)DH =磁头(面)CL =扇区ES:BX =缓冲区的地址(指向要写入磁盘的数据) |
返回 | 操作成功:ah =0; al =写入的扇区数操作失败: ah =错误代码 |
编程:将当前屏幕的内容保存在磁盘上
分析: 1屏的内容占 4000个字节,需要8个扇区,用0面0道的1~8扇区存储显存中的内容。
1.44M软盘(3.5英寸高密度软盘)每个扇区的字节数是512字节。
assume cs:code
code segment
start: mov ax,0b800h ; es:bx 指向显示缓存第1页
mov es,ax
mov bx,0
mov al,8 ; 写入的扇区数
mov dl,0 ; A 盘
mov dh,0 ; 磁头
mOV ch,0 ; 磁道号
mov cl,1 ; 扇区号
mov ah,3 ; 功能03H 写扇区
int 13h ; 调用“直接磁盘服务”
ok: mov ax,4c00h
int 21h
code ends
end start
实验 17 编写包含多个功能子程序的中断例程
《汇编语言》- 读书笔记 - 第17章-实验17 编写包含多个功能子程序的中断例程
课程设计 2
《汇编语言》- 读书笔记 - 第17章-课程设计 2