汇编语言笔记17使用BIOS进行键盘输入和磁盘读写
汇编语言笔记17使用BIOS进行键盘输入和磁盘读写
0x00 int9中断例程对键盘输入的处理
键盘输入将引发9号中断,BIOS提供int9中断例程。cpu在9号中断发生后,执行int9中断例程,从60h端口读出扫描码,并将其转化成相应的ascii码或状态信息,储存在内存的指定空间(键盘缓冲区或状态字节)中
一般的键盘输入,在执行完int9中断例程后,都放在了键盘缓冲区中。键盘缓冲区是一个大小为16字节的环形队列,可以存储15个按键的扫描码和对应的ascii码。
从上面可以看出 int9中断例例程会不断的向队列里加入一般按键的扫描码和对应的ascii码放在队列尾部
0x01 使用int 16h中断例程读取键盘缓冲区
如果int9中断一直向缓冲区写入,那缓冲区一直是满的,所以有读取并删除缓冲区队列尾部的int 16h中断例程,该功能的编号为0.
mov ah,0 int 16h 结果 ah=扫描码 al=ascii码
如图上所说,调用一次int 16h的0号功能,便从缓冲区尾部读取并删除一个纪律,如果没有,那么会一直等待新的按键记录写入缓冲区 再读取删除,那么int 16的中断例程的0号功能里,必然没有屏蔽外设的中断,说明IF被置1了(之前进入int 16被硬件自动置0了)
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: shr ah,1
green: shr 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
上述代码运行正确,利用比较和跳转巧妙的让不同输入执行不同的shl次数,让ah刚好是前景颜色
这个说法是对的,因为进入int 16 的过程中if tf都被设置为0,然而如果缓冲区没有记录可以读取了,int 16会等待新的记录进入,也就是说int 16中还可以响应int9中断例程,所以IF必然要被置1
0x02 字符串的输入
最基本的字符串输入程序,需要具备下面的功能。
(1)在输入的同时显示这个字符串
(2)一般在输入回车符后,输入结束
(3)可以删除已经输入的字符
assume cs:code,ds:data
data segment
db 128 dup (0)
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0
mov dh,14
mov dl,40
call input_str
mov ax,4c00h
int 21h
input_str: jmp in_start
top dw 0;栈顶 也就是最后一个字符
in_start: push ax
push bx
push cx
push es
push di
mov ax,0b800h
mov es,ax
mov al,160
mul dh
add dl,dl
mov dh,0
add ax,dx
mov di,ax;di指向第一个字符要显示的位置
typing: mov ah,0
int 16h;ah扫描码,alascii码
cmp al,20h
jb not_show;小于20h不显示
mov bx,top
mov ds:[si+bx],al
add bx,bx
mov es:[di+bx],al
mov byte ptr es:[di+bx+1],2
inc top
jmp short typing
not_show: cmp al,8;看是不是退格
jne fin
cmp top,0;
je short typing;如果当前没有字符就不继续退了
dec top
mov bx,top
mov byte ptr ds:[si+bx],0
add bx,bx
mov byte ptr es:[di+bx],0
mov byte ptr es:[di+bx+1],0
jmp short typing
fin: cmp ah,1ch; 这次用的是扫描码而不是ascii 因为按下回车键后的ascii是个很神奇的东西
jne short typing
mov bx,top
mov byte ptr ds:[si+bx],0;以0结尾的字符串
pop di
pop es
pop cx
pop bx
pop ax
ret
code ends
end start
上述代码运行正确,但是和书上给的代码写法不一样,主要是我没有进行更细的子程序划分
0x03 应用int13h中断例程对磁盘读写
//2020-6-11修改 原来因没有软盘且是在DOSBOX虚拟8086模式下无法进行本章节学习,现在使用bochs运行msdos 加载虚拟软盘
3.5英寸软盘分为上下两面,每面80个磁道,每个磁道分为18扇区,每扇区512字节,则2 * 80 * 10 * 512 =1440 kb ≈ 1.44mb
硬盘结构类似,只不过硬盘有多个盘面,每个盘面一个磁头 heads 每个面上的磁道数称为柱面数cylinders 每个磁道(track)上的扇区(sector) 数即 SPT (sectors per track)
BIOS提供硬盘读写的中断例程
读一个扇区到内存,注意对硬盘的读写都是一个扇区,也就是连续的512字节为单位
写内存到硬盘
计算机上电后 cs ip指向0ffffh:0处,执行BIOS硬件自检和向量初始化等过程,然后根据BIOS设置,按顺序查找具有MBR(master boot record,主引导记录,也就是第一个扇区的最后两个字节是0x55和0xAA)的储存设备,读取该设备的第一个扇区到内存0:7c00处,并跳转到该处执行
对于软盘而言其主引导记录只有启动代码+0x55 0xAA结尾,而硬盘则是启动代码+64字节的分区记录表(dpt disk partition table)+0x55 0xAA结尾
因此,如果要让程序代替操作系统,开机执行,我们可以把程序写入到盘符为a的软盘或者盘符为c的硬盘 的第一扇区,程序如下:
assume cs:code
code segment
task: jmp short tstart
str1: db 'hello world',0
tstart: mov ax,0b800h
mov es,ax
mov di,2000
mov ax,0
mov ds,ax
mov si,offset str1-offset task+07c00h
s: mov al,ds:[si]
cmp al,0
je fin
mov es:[di],al
mov byte ptr es:[di+1],2
inc si
add di,2
jmp short s
fin: nop
jmp short fin;程序结束让cpu空转 防止cpu跑飞
start: mov ax,cs
mov es,ax
mov bx,0
mov word ptr es:[1FEH],0aa55h;添加mbr描述符
mov al,1
mov ch,0
mov cl,1;写到第一扇区
mov dh,0
mov dl,1;
mov ah,3
int 13h
mov ax,4c00h
int 21h
code ends
end start
这个程序在b盘符 运行,写到b盘符上,然后再bochs中吧两个img交换了,也就是b盘和a盘交换了内容,然后再从a盘启动即运行这个程序(为什么不直接写到a盘? a盘原来保存着dos系统,我还想继续用呢,所以利用交换img的形式 让b盘变成了a盘)
上述代码运行正确!