动态线条
动态线条end

中断及外部设备操作

移位指令

逻辑左移SHL ORP,CNT

逻辑右移SHR OPR,CNT

循环左移ROL OPR,CNT

循环右移ROR ORP,CNT

算数左移SAL OPR,CNT

算数右移SAR OPR,CNT

带进位循环左移RCL OPR,CNT

带进位循环右移RCR OPR,CNT

s,sh - shift l-left r-right a-arithmetic r,ro-rotate c-carry

位移操作需要注意:

如果移动的位数大于1的时候,必须用cl,否则移动的位数只能是1

mov al,01010001B
mov cl,3
shl al,cl

操作显存数据

屏幕上的内容 = 显存中的数据

image-20220113100937764

image-20220113101021587

其中B8000H ~ BFFFFH共32K的空间,是80*25彩色字符模式第0页的显示缓冲区

显示缓冲区的结构

image-20220113101425104

image-20220113101508629

由此可知,一列需要两个字节来存储数据,低位字节要显示符号的ASCII,高位字节显示属性字节(闪烁、背景、高亮、前景等)

高位字节

7 6 5 4 3 2 1 0
BL R G B I R G B
闪烁 背景 背景 背景 高亮 前景 前景 前景

显示信息的一种直接方式

在品目中间,白底蓝字,显示“welcome to masm”

assume cs:code,ds:data
data segment
	db 'welcome to masm'
data ends

code segment
main:
	;初始化寄存器
	mov ax,data
	mov ds,ax
	mov ax,0B800H
	mov ex,ax
	mov si,0
	mov di,160*12 + 80-16;定位到了第十二行的中间位置,并向左偏移16字节
	
	;显示字符串
	mov cx,16
l:
	mov al,[si]
	mov es:[di],al
	inc di
	mov al,71H
	mov es:[di],al
	inc si
	inc di
	loop l
	
code ends
end main

描述内存单元的标号

关于标号

1)代码段中的标号可以用来标记指令、段的起始地址

2)代码段中的数据也可以用标号

assume cs:code
code segment
	a:db 1,2,3,4,5,6,7,8
	b:dw 0
main:
	mov si,offset a
	mov bx,offset b
	mov cx,8
l:
	mov al,cs:[si]
	mov ah,0
	add cs:[bx],ax
	inc si
	loop l
	
	mov ax,4c00H
	int 21H
code ends
end main

去掉冒号的数据标号

assume cs:code
code segment
	a db 1,2,3,4,5,6,7,8
	b dw 0
main:
	mov si,0
	mov cx,8
l:
	mov al,a[si]	;类似高级语言的数组
	mov ah,0
	add b,ax
	inc si
	loop l
	
	mov ax,4c00H
	int 21H
code ends
end main

上述代码中的a,b被称为数据标号:

1)数据标号标记了存储数据的单元的地址和长度

2)数据标号不同于仅仅表示地址的地址标号

地址编号只能在代码段内使用

数据编号能在代码段和数据段内使用

扩展用法(类似于C语言的指针)

data segment
	a db 1,2,3,4,5,6,7,8
	b dw 0
	c dw a,b
data ends

等同于

data segment
	a db 1,2,3,4,5,6,7,8
	b dw 0
	c dw offset a,offset b
data ends

如果利用双字存储地址,那么会将段地址也进行存储

data segment
	a db 1,2,3,4,5,6,7,8
	b dw 0
	c dd a,b
data ends

等同于

data segment
	a db 1,2,3,4,5,6,7,8
	b dw 0
	c dw offset a,seg a,offset b,seg b
data ends

数据的直接定址表

直接定址表:通过查表的方式解决问题

问题:以十六进制的形式在屏幕上显示给定的byte型数据

分析:现将一个byte的高四位和低四位分开,显示对应的数码字符

方案一:逐个进行比较

     如果是0,那么将0打印在公屏上

     如果是1,那么将1打印在公屏上

     ....

缺点:比较次数多,过程书写繁琐

方法二:建立一张表,表中依次存储“0 ~ F”,我们可以通过数值0~15直接查找到对应的字符

assume cd:code
code segment
main:
	mov al,2BH
	call showbyte
	mov ax,4c00H
	int 21H
	
showbyte:
	jmp short show
	table db '0123456789ABCDEF'
show:
	push bx
	push es
	push cx
	
	mov ah,al
	mov cl,4
	shr ah,cl		;右移四位,ah中得到高四位的值
	and al,00001111b;al中获取低四位的值
	
	;用高四位的值作为table中的偏移量,获取对应字符并显示
	mov bl,ah
	mov bh,0
	mov ah,table[bx]
	
	mov bx,0b800H
	mov es,bx
	mov es:[160 * 12 + 40 * 2],ah
	
	;用低四位的值作为table中的偏移量,获取对应字符并显示
	mov bl,al
	mov bh,0
	mov al,table[bx]
	
	mov es:[160 * 12 + 40 * 2 + 2],al
	
	pop bx
	pop es
	pop cx
	
code ends
end main

代码直接定制表

直接定制表

用查表的方式,通过依据数据,直接计算出所要找的元素的位置

直接定制表分类

1)数据的直接定制表

2)代码的直接定制表

要解决的问题

实现一个子程序setscreen,为实现输出提供如下功能

1)清屏

;将显存当中的字符设置为空格符
sub1:
	push bx
	push cx
	push es
	
	mov bx,0b800H
	mov es,bx
	mov bx,0
	mov cx,2000
sub1s:
	mov byte ptr es:[bx],' '
	add bx,2
	loop sub1s
	
	pop es
	popcx
	pop bx
	
	ret		;sub1结束

2)设置前景

;设置前景色:显存中奇地址的属性字节的第0,1,2位
sub2:
	push bx
	push cx
	push es
	
	mov bx,0b800H
	mov es,bx
	mov bx,1
	mov cx,2000
	
sub2s:
	and byte ptr es:[bx],11111000b
	or es:[bx],al
	add bx,2
	loop sub2s
	
	pop es
	pop cx
	pop bx
	
	ret 	;sub2结束

3)设置背景

;设置第4,5,6位
sub3:
	push bx
	push cx
	push es
	mov cl,4
	shl al,cl
	mov bx,0b800H
	mov es,bx
	mov bx,1
	mov cx,2000
sub3s:
	and byte ptr es:[bx],10001111b
	or es:[bx],al
	add bx,2
	loop sub3s
	
	pop es
	pop cx
	pop bx
	ret		;sub3结束

4)向上滚动一行

;将n + 1行复制到n行处
sub4:
	push cx
	push si
	push di
	push es
	push ds
	
	;准备阶段
	mov si,0b800H
	mov es,si
	mov ds,si
	mov si,160		;将si指向n+1行
	mov di,0		;将di指向第n行
	cld
	mov cx,24		;共复制24行
	
	;复制24行
sub4s:
	push cx
	mov cx,160
	rep movsb
	pop cx
	loop sub4s

	mov cx,80
	mov si,0
sub4s1:
	mov byte ptr es:[160 * 34 + si],' '
	add si,2
	loop sub4s1
	
	pop ds
	pop es
	pop di
	pop si
	pop cx
	ret		;sub4结束

5)主程序

assume cs:code
code segment
main:
	mov ah,2
	mov al,5
	call setscreen
	moc ax,4c00H
	int 21H

setscreen:
	jmp short set
	table dw sub1,sub2,sub3,sub4
set:
	push bx
	cmp ah,3
	ja sret
	mov bl,ah
	mov bh,0
	add bx,bx
	call word ptr table[bx]
sret:
	pop bx
	ret
	
sub1:
	....
sub2:
	....
sub3:
	....
sub4:
	....
	
code ends
end main

解决方案

1)将四个功能写成四个子程序

2)将这些功能子程序的入口地址存储在一个表中,他们在表中的位置和功能号相对应

3)对应关系为:功能号 * 2 = 对应的功能子程序在地址表中的偏移

中断及其处理

中断概述

1)CPU不在接着刚刚执行的指令向下执行,而是转去处理中断信息

2)内中断:由CPU内部发生的事件引起的中断

3)外中断:由外部设备发生的事件引起的中断(键盘、显示器、打印机等引起)

内中断

1)除法错误,比如:执行div指令产生的除法溢出

2)单步执行

3)执行into指令

4)执行int指令

8086中断码类型

1)除法错误:0

2)单步执行:1

3)执行into指令:4

4)执行int n指令,立即数n为终端类型码

CPU接到中断信息怎么办?

执行中断处理程序

中断处理程序在哪里?

中断信息和其处理程序的入口地址之间有某种联系,CPU根据中断信息可以找到要执行的处理程序。

中断向量表

由中断类型码,查表得到中断处理程序的入口地址,从而定位中断处理程序。

(共256个中断程序,1024个字节,每四个字节为一组,低字节记录中断程序的IP,高字节激励中断程序的CS)

定位中断程序

IP = N * 4

CS = N * 4 + 2

中断过程

1)中断过程由CPU的硬件自动完成

2)用中断类型码找到中断向量,并用它设置CS和IP

8086CPU的中断过程

1)从中断信息中取得中断类型码

2)标志寄存器的值入栈--中断过程中要改变标志寄存器的值,需要先保护现场

3)设置标志寄存器的第八位TF和第九位IF的值为0

4)CS的内容入栈

5)IP的内容入栈

6)从中断向量表读取中断处理程序的入口地址

单步中断

程序的正常执行:取指令、改变CS:IP、执行指令、取指令

Debug提供了单步中断的中断处理程序,功能为显示所有寄存器中的内容后等待输入命令

CPU执行一条指令停下来的原理是什么

1)Debug利用了CPU提供的单步中断的功能

2)使用t命令时,Debug将TF标志设为1,使CPU工作在单步中断的方式下

单步中断的过程与处理

TF陷阱标志(Trap fial):用于调试时的单步方式操作。当TF = 1时,每条指令执行完后产生陷阱,由系统控制计算机,当TF = 0时,CPU正常工作,不产生陷阱。

IF中断标志(Interrupt flag):当IF = 1时,允许CPU响应可屏蔽中断请求,当IF = 0时,关闭中断。

CPU在执行完一条指令后,如果检测到标志寄存器的TF位为1,则产生单步中断(中断类型码为1),引发中断过程,执行中断处理程序。

中断过程

1)取得中断类型码

2)标志寄存器入栈,TF、IF设置为0

     中断处理程序也是由一条条指令组成的

     如果在执行中断程序之前,TF = 1,则CPU在执行完中断处理程序的第一条指令后,又要产生单步中断,转而去执行单步中断的中断处理程序的第一条指令

     上边的过程将陷入一个永远不能结束的循环,CPU永远执行单不中断处理程序的第一条指令

     所以,在进行中断处理程序之前,设置TF = 0

3)CS、IP入栈

4)IP = 1* 4,CS = 1 * 4 + 2

中断不响应的情况

1)一般情况下,CPU执行完当前指令后,如果检测到中断信息,就响应中断,引发中断过程

2)在有些情况下,CPU执行完当前指令后,即便发生中断,也不会响应

     如:在执行完向ss寄存器传送数据的指令后,即便是发生中断,CPU也不会响应,因为系统默认对ss寄存器与sp寄存器的初始化是连续的,如果进行中断的话,可能造成在二者初始化的间隙中,产生其他不可预测的指令。因此,将二者当成一个整体对待。(注意:是将ss赋值指令,与其下一条指令看做一个整体,下一条指令可能不是sp的赋值指令)

由int指令引发的中断

int n引发的中断

格式:int n,n为中断类型码

功能:引发中断过程

CPU执行int n指令,相当于引发一个n号中断的中断过程,执行过程如下:

1)取中断类型码n

2)标志寄存器入栈,IF = 0,TF = 0

3)CS、IP入栈

4)IP = n * 4,CS = n * 4 + 2

BIOS和DOS中断处理

BIOS,是在系统板的ROM中存放着一套程序

容量:8KB

地址:从统一编址的FE000H开始

BIOS中的主要内容

1)硬件系统的检测和初始化程序

2)外部中断和内部中断的中断例程

3)用于对硬件设备进行I/O操作的中断例程

4)其他和硬件系统相关的中断例程

优点:

1)使用BIOS功能调用,程序员不用了解硬件的操作细节,直接使用指令设置参数,并中断调用BIOS例程,即可完成相关工作

2)方便编程

3)能写出简介、可读性好、易于移植的程序

编程任务

在屏幕的5行12列显示三个红底高亮闪烁绿色的 ‘a’

用BIOS的10H中断

1)ah = 2时,调用10h中断例程的2号子程序,设置光标位置

2)ah = 9时,调用第10H中断例程的9号子程序,在光标位置显示字符

assume cs:code
code segment
	mov ah,2		;设置光标功能
	mov bh,0		;第0页
	mov dh,5		;dh中放行号
	mov dl.12		;dl中放列号
	int 10H
	
	mov ah,9		;显示字符功能
	mov al,'a'		;字符
	mov bl,11001010b;颜色属性
	mov bh,0		;第0页
	mov cx,3		;字符重复个数
	int 10H
	
	mov ax,4c00H
	int 21H
code ends
end

BIOS和DOS所提供的中断例程包含了很多子程序,这些子程序实现了程序猿在编程时常用到的功能

和硬件设备相关的DOS中断例程中,一般都调用了BIOS的中断例程

int 21H DOS中断例程的应用

4ch功能号:程序返回,功能号在ah中,返回结果在al中

用法:

mov ah,4cH
mov al,0

BIOS和DOS中断例程的安装过程

1)CPU一加电,初始化CS = 0FFFFH,IP = 0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的硬件检测和初始化程序

2)初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。

3)硬件系统检测和初始化完成后,调用int 19H进行操作系统的引导,从此将计算机交由操作系统控制。

4)DOS启动后,除完成其他工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量。

端口的读写

以端口访问外设:以发声为例

assume cs:code
code segment
main:
	mov al,08H	;设置声音频率
	out 42H,al
	out 42H,al
	in  al,61H	;读设备控制器端口原值
	mov ah,al	;保存原值
	or  al,3	;打开扬声器和定时器
	out 61H,al	;接通扬声器,发声
	mov cx,60000;延时
	
delay:
	nop
	loop delay
	mov al,ah	;恢复端口原值
	out 61H,al
	
	mov ax,4c00H
	int 21H
code ends
end main

CPU可直接访问的地方

1)CPU内部的寄存器

2)内存单元

3)端口:各种接口卡、网卡、显卡等,主板上的接口芯片,其他芯片

1)各个芯片工作的时候,都有一些寄存器由CPU读写

2)从CPU的角度,将各寄存器当端口并统一编址

3)CPU用统一的方法与各种设备通信

读写端口的指令

in :CPU从端口读数据

out:CPU往端口写入数据

访问端口的方法

in al,60H	;从60号端口读入一个字节

执行时与总线相关的操作

1)CPU通过地址线将地址信息60H发出

2)CPU通过控制线发出端口读命令,选中端口所在的芯片,并通知要从中读取数据

3)端口所在的芯片将60H端口中的数据通过数据总线送入CPU

对0~255以内的端口进行读写,端口号用立即数给出

in al,20H	;从20端口读入一个字节
out 21H,al	;往21端口写入一个字节

对256~65535的端口进行读写时,端口号放在dx中

mov dx,3f8H	;将端口号38f送入dx
in al,dx	;从38f端口读入一个字节
out dx,al	;从38f端口写入一个字节

操作CMOS RAM芯片

在开机的前几秒按F8或其他按键进入BIOS

CMOS RAM芯片

1)包含一个时钟和一个有128个存储单元的RAM存储器

2)128个字节的RAM中存储:内部时钟、系统配置信息、相关程序(用于开机时配置系统信息)

3)CMOS RAM芯片靠电池供电,关机后其内部的时钟仍可正常工作,RAM中的信息不丢失

4)该芯片内部有两个端口,端口地址为70H和71H,CPU通过这两个端口读写CMOS RAM

     70H:存放要访问的单元地址

     71H:存放从选定的单元中读取的数据,或要写入的数据

5)读写CMOS RAM的两个步骤

     将要读写的单元地址送入70H地址端口

     将数据写入71H地址端口

时间在CMOS RAM中的存储(用六个字节,分别存放时间信息中的秒、分、时、日、月、年)

内容
地址 00 01 02 03 04 05 06 07 08 09

读取月份,并将结果显示在屏幕中间

assume cs:code
code segment
main:
	mov al,8
	out 70H,al		;取得月份
	in al,71H
	
	mov ah,al		;分离月份的十、个位
	mov cl,4
	shr ah,cl
	and al,00001111b
	
	add ah,30H		;转换为ASCII码
	add al,30H
	
	mov bx,0b800H	;显示
	mov es,bx
	mov byte ptr es:[160 * 12 + 40 * 2],ah
	mov byte ptr es:[160 * 12 + 40 * 2 + 2],al
	
	
	mov ax,4c00H
	int 21H
code ends
end main 

外设连接与中断(外中断)

CPU在执行指令的过程中,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入。

可屏蔽中断

1)可屏蔽中断是CPU可以不响应的外中断

2)CPU是否响应可屏蔽中断,要看标志寄存器的IF位的设置

3)当CPU检测到可屏蔽中断信息时:

     如果 IF = 1,则CPU在执行过程中完成当前指令后响应中断,引发中断过程

     如果 IF = 0,则不响应可屏蔽中断

可屏蔽中断的中断过程

1)去中断类型码

     可屏蔽中断信息来自于CPU外部,中断类型码是通过数据总线送入CPU(对比内中断:中断类型是在CPU内部产生的)

2)标志寄存器入栈,IF = 0,TF = 0

     将IF置零的原因:进入中断处理程序后,禁止其他可屏蔽中断(如果在中断处理程序中需要处理可屏蔽中断,可以将IF置为1)

3)CS,IP入栈

4)IP = n * 4,CS = n * 4 + 2

不可屏蔽中断

1)CPU必须响应的中断

2)当CPU检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应,引发中断过程

3)对于8086CPU不可屏蔽中断的中断类型码固定为2

不可屏蔽中断的中断过程

1)标志寄存器入栈,IF = 0,TF = 0

2)CS,IP入栈

3)IP = 8,CS = 0AH

8086CPU提供的设置IF的指令

1)sti:用于设置IF = 1

2)cli:用于设置IF = 0

PC键盘的处理过程

输入键盘的字符如何保存

1)有BIOS键盘缓冲区

2)BIOS键盘缓冲区:是系统启动后,BIOS用于存放int 9中断例程所接收的键盘输入的内存区

3)BIOS键盘缓冲区:可以存储15个键盘输入,一个键盘输入用一个子单元存放,高位存放扫描码,低位存放字符码

键盘处理过程

1)键盘输入

     1)键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描

     2)按下一个键的操作

          开关接通,该芯片就产生一个扫描码,扫描码说明了按下的键在键盘上的位置

          扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60H

     3)松开按下的键时的操作

          产生一个扫描码,扫描码说明了松开的键在键盘上的位置

          松开按键时产生的扫描码也被送入60H端口中

     4)扫描码长度为一个字节的编码

          按下一个键时产生的扫描码 - 通码,通码的第八位为0

          松开一个键时产生的扫描码 - 断码,断码的第八位为1

2)引发九号中断

     1)键盘的输入到达60H端口时,相关芯片就会向CPU发出中断类型码为9的可屏蔽中断信息

     2)CPU检测到该中断信息后,如果IF = 1,则相应中断,引发中断过程,专区执行int 9中断例程

3)执行int 9中断例程

     1)独处60H端口的扫描码

     2)根据我扫描分情况对待

          如果是字符键的扫描码,将该扫描码和他对应字符码送入内存中的BIOS键盘缓冲区

          如果是控制键(Ctrl)和切换键(capslock),则将其转换为状态字节,写入内存中存储状态字节的单元

     3)对键盘系统进行相关的控制,如向相关芯片发出应答信息

posted @ 2022-01-17 09:12  v1v1v1  阅读(212)  评论(0)    收藏  举报