📌 汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。 本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。
我们前面讲过,各种存储器都和CPU的地址线、数据线、控制线相连。CPU在操控它们的时候,把它们都当作内存来对待,把它们总地看做一个由若干存储单元组成的逻辑存储器,这个逻辑存储器我们称其为内存地址空间。
在PC机系统中,和CPU通过总线相连的芯片除各种存储器外,还有以下3种芯片。
在这些芯片中,都有一组可以由CPU读写的寄存器。这些寄存器,它们在物理上可能处于不同的芯片中,但是它们在以下两点上相同。
可见,从CPU的角度,将这些寄存器都当作端口,对它们进行统一编址,从而建立了一个统一的端口地址空间。每一个端口在地址空间中都有一个地址。
CPU可以直接读写以下3个地方的数据。
本文,我们讨论端口的读写。
在访问端口的时候,CPU 通过端口地址来定位端口。因为端口所在的芯片和CPU通过总线相连,所以,端口地址和内存地址一样,通过地址总线来传送。
在PC系统中,CPU 最多可以定位 64KB个不同的端口。则端口地址的范围为0~65535。
对端口的读写不能用mov、push、pop等内存读写指令。 端口的读写指令只有两条:in 和 out,分别用于从端口读取数据和往端口写入数据。
我们看一下 CPU 执行内存访问指令和端口访问指令时候,总线上的信息:
(1)访问内存:
mov ax,ds:[8] ;假设执行前 (ds)=0
执行时,与总线相关的操作:
(2)访问端口:
in al,60h ;从60h号端口读入一个字节
执行时与总线相关的操作:
注意:在in和out 指令中,只能使用 ax 或 al 来存放从端口中读入的数据或要发送到端口中的数据。
访问8位端口时用 al ,访问 16 位端口时用ax 。
对0~255以内的端口进行读写:
in al,20h ;从20h端口读入一个字节
out 20h,al ;往20h端口写入一个字节
对256~65535的端口进行读写时,端口号放在dx中:
mov dx,3f8h ;将端口号3f8送入dx
in al,dx ;从3f8h端口读入一个字节
out dx,al ;向3f8h端口写入一个字节
下面的内容中,我们通过对CMOS RAM的读写来体会一下对端口的访问。
PC机中,有一个CMOS RAM 芯片,一般简称为CMOS。
此芯片的特征如下:
可见,CPU对CMOS RAM的读写分两步进行。
比如:读CMOS RAM的2号单元:
shl和shr 是逻辑移位指令,后面的学习中我们要用到移位指令,这里进行一下讲解。
shl 是逻辑左移指令,功能为:
例如有如下指令:
mov al,01001000b
shl al,1 ;将al中的数据左移一位
执行后(al)=10010000b,CF=0。
我们来看一下shl al,1
的操作过程。
(1)左移:
(2)将最后移出的一位写入CF中:
(3)最低位用0补充:
如果接着上面,继续执行一条shl al1
,则执行后:(al)=00100000b,CF=1。
shl指令的操作过程如下。
(1)左移:
(2)将最后移出的一位写入CF中:
(3)最低位用0补充:
如果移动位数大于1时,必须将移动位数放在cl中。
比如,指令:
mov al,01010001b
mov cl,3
shl al,cl
执行后(al)=10001000b。
那请问,CF的值是多少捏?
答:因为最后移出一位是0,所以CF=0。
可以看出,将X逻辑左移一位,相当于执行X=X*2。
比如:
shr 是逻辑右移指令,它和 shl 所进行的操作刚好相反。
功能为:
指令:
mov al,10000001b
shr al,1 ;将 al中的数据右移一位
执行后(al)=01000000b,CF=1。
如果接着上面,继续执行一条shr al,1
,则执行后:(al)=00100000b,CF=0。
如果移动位数大于1时,必须将移动位数放在cl中。
比如,指令:
mov al,01010001b
mov cl,3
shr al,cl
执行后(al)=00001010b,因为最后移出的一位是0,所以CF=0。
可以看出,将X逻辑右移一位,相当于执行X=X/2。
在CMOS RAM中,存放着当前的时间:年、月、日、时、分、秒。
这6个信息的长度都为1个字节,存放单元为:
秒:0 分:2 时:4 年:9 月:8 日:7
这些数据以BCD码的方式存放。
BCD 码是以4位二进制数表示十进制数码的编码方法,如下图所示。
例如:数值26,用BCD码表示为:0010 0110
可见,一个字节可表示两个BCD码。
则CMOS RAM存储时间信息的单元中,存储了用两个 BCD码表示的两位十进制数,高 4 位的BCD码表示十位,低4 位的BCD 码表示个位。
比如:00010100b表示14。
编程:在屏幕中间显示当前的月份。
可以看出,这个程序主要做两部分工作。
从CMOS RAM的8号单元读出当前月份的BCD码。
要读取 CMOS RAM的信息,我们首先要向地址端口70h写入要访问的单元的地址:
mov al,8
out 70h,al
然后从数据端口71h中取得指定单元中的数据:
in al,71h
将用BCD码表示的月份以十进制的形式显示到屏幕上。
我们可以看出 ,BCD 码值=十进制数码值, 则BCD码值+30h=十进制数对应的ASCII码。
我们需要进行以下两步工作:
(1)将从CMOS RAM的8号单元中读取的一个字节,分为两个表示BCD码值的数据。
mov ah,al ;al中为从CMOS RAM的8号单元中读出的数据
mov cl,4
shr ah,cl ;ah中为月份的十位数码值
and al,00001111b ;ah中为月份的个位数码值
(2)显示(ah)+30h 和 (al)+30 对应的ASCII码字符。
完整的程序如下:
assume cs:code
code segment
start:
mov al,8
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
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 start
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!
v bx,0b800h ;显存 mov es,bx mov byte ptr es:[16012+402],ah ;显示月份的十位数码 mov byte ptr es:[16012+402+2],al ;显示月份的个位数码
mov ax,4c00h
int 21h
code ends end start
# 结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以<font color="blue">三连</font>支持一下。
也可以点点关注,避免以后找不到我哦!
[Crossoads主页](https://blog.csdn.net/2301_80191662?type=lately)还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!
