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

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

0x00 int9中断例程对键盘输入的处理

​ 键盘输入将引发9号中断,BIOS提供int9中断例程。cpu在9号中断发生后,执行int9中断例程,从60h端口读出扫描码,并将其转化成相应的ascii码或状态信息,储存在内存的指定空间(键盘缓冲区或状态字节)中

​ 一般的键盘输入,在执行完int9中断例程后,都放在了键盘缓冲区中。键盘缓冲区是一个大小为16字节的环形队列,可以存储15个按键的扫描码和对应的ascii码。

QQ截图20200610092415

QQ截图20200610092510

QQ截图20200610092544

​ 从上面可以看出 int9中断例例程会不断的向队列里加入一般按键的扫描码和对应的ascii码放在队列尾部

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

​ 如果int9中断一直向缓冲区写入,那缓冲区一直是满的,所以有读取并删除缓冲区队列尾部的int 16h中断例程,该功能的编号为0.

mov ah,0 int 16h 结果 ah=扫描码 al=ascii码

QQ截图20200610092942

QQ截图20200610093011

QQ截图20200610093039

​ 如图上所说,调用一次int 16h的0号功能,便从缓冲区尾部读取并删除一个纪律,如果没有,那么会一直等待新的按键记录写入缓冲区 再读取删除,那么int 16的中断例程的0号功能里,必然没有屏蔽外设的中断,说明IF被置1了(之前进入int 16被硬件自动置0了)

QQ截图20200610093422

QQ截图20200610093722

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

QQ截图20200610094823

​ 上述代码运行正确,利用比较和跳转巧妙的让不同输入执行不同的shl次数,让ah刚好是前景颜色

QQ截图20200610094958

​ 这个说法是对的,因为进入int 16 的过程中if tf都被设置为0,然而如果缓冲区没有记录可以读取了,int 16会等待新的记录进入,也就是说int 16中还可以响应int9中断例程,所以IF必然要被置1

0x02 字符串的输入

​ 最基本的字符串输入程序,需要具备下面的功能。

​ (1)在输入的同时显示这个字符串

​ (2)一般在输入回车符后,输入结束

​ (3)可以删除已经输入的字符

QQ截图20200610102003

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

QQ截图20200610110113

​ 上述代码运行正确,但是和书上给的代码写法不一样,主要是我没有进行更细的子程序划分

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字节为单位

QQ截图20200611112344

​ 写内存到硬盘

QQ截图20200611112537

​ 计算机上电后 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盘)

QQ图片20200611115159

​ 上述代码运行正确!