CPU是计算机的核心部件。它控制着整个计算机的运行和计算。指令和数据都存储在存储器中,也就是存储器。 CPU 没有内存就无法生存。内存中的指令和数据没有区别,都是二进制的。 CPU来识别是信息还是指令。
一个存储单元存储1Byte
CPU从内存中读取和写入书籍。它需要指定地址以及要执行的操作。 CPU通过总线与其他芯片连接,传输信息。
内存单元的地址(地址信息)——地址总线
设备选择、读或写命令(控制信息)——控制总线
读或写数据(数据信息)——数据总线
ima111ng 地址总线
一根导线有两个稳定状态,分别代表0和1,因此10根导线有2^10个不同的数据,从0到1023
地址总线的宽度决定了CPU的寻址能力
数据总线
8条数据总线可传输8位二进制数据8bits=1byte
数据总线的宽度决定了CPU与其他设备之间传输的数据量。
控制总线
控制总线的宽度决定了CPU控制外部设备的能力
主板上有CPU、内存等核心设备,CPU通过总线向接口卡发送指令,接口卡控制外设工作。
随机存取存储器RAM 只读存储器ROM
BIOS(基本输入/输入系统)
1CPU将各类内存视为逻辑内存,所有物理内存都视为由若干个存储单元组成的逻辑内存。每个物理内存在该逻辑内存中占用一个地址段,即地址空间。
ima11333ng 内存地址空间的大小受CPU 地址总线宽度的限制。不同的计算机系统有不同的内存地址分配
2.注册
CPU由运算器、控制器、寄存器等器件组成。设备之间通过内部总线连接,这与之前的总线(外部)不同。
寄存器程序员可以使用指令进行读写
8086CPU的14个寄存器
AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW
通用寄存器:
用于存储通用数据AX BX CX DX
使用AH AL可以将16位寄存器拆分为两个8位寄存器,高8位和低8位
1 两个字节构成一个字,一个字节的8位放在8位寄存器中。
组装说明
移动斧头,18
莫夫啊,78
添加斧头,8
移动ax,bx
添加斧头,bx
.
16位寄存器只能存储4位十六进制数据。 1044CH的最高位不能存储在ax中的004CH中。
如果单独使用AL 寄存器,则进位不会存储在AH 中。
数据传输和操作时,指令的两个操作数的位数应保持一致。
物理地址
所有内存单元组成的存储空间是一维线性空间,每个内存单元在该空间中都有唯一的地址。
CPU在向地址总线发送物理地址之前,必须在内部形成物理地址。
16位CPU可以同时处理、传输和临时存储16位地址
8086CPU有20位地址总线,内部使用两个16位地址合成一个20位物理地址。
1物理地址=段地址16(基地址)+偏移地址
段地址
X 左移1 位相当于X 相乘
1 因为内部结构是这样的,为了达到20位寻址能力,用两个16位地址就可以达到目的。
1后面是段地址,内存不分段,分段来自cpu
段地址
CPU可以使用不同的段地址和偏移地址组成同一个物理地址
8086的4个段寄存器分别是CS、DS、SS、ES。访问内存时,这四个段寄存器提供内存单元的段地址。
CS是代码段寄存器IP是指令指针寄存器
任意时刻,假设CS的内容为M,IP的内容为N,则8086CPU从M16+N的内存单元开始,读取指令并执行。
读取一条指令后,IP中的值自动递增(指令长度),以便CPU读取下一条指令
CPU将CS:IP指向的内存地址单元的内容视为一条指令。
要同时修改CS和IP的内容,可以使用jmp段地址:偏移地址
只能修改IP。 jmp 一个合法的寄存器(用寄存器中的值修改IP)?
在对8086机器进行编程时,可以根据需要将一组存储单元定义为一个段。
对于长度为N=64的代码,有一组地址连续且起始地址为16的倍数的存储单元作为一个代码段。
使用CS:IP指向的内容来执行代码段的内容。
3.寄存器(内存访问)
在内存中存储时,存储单元是字节单元,因此一个字Word必须存储在两个地址连续的存储单元中。低位字节存储在低地址单元中,高位字节存储在高地址单元中。
字单元:由两个地址连续的存储单元组成。起始地址为N的字单元称为N地址字单元。
10个地址字单元4E21H、1个地址字单元124EH……
DS 和[地址]
8086中有一个DS寄存器,通常用来存储要访问的数据的段地址。
mov bx,1000H
移动ds,bx
移动,[0]
将10000H(1000:0)中的数据读入al
这里[.]表示一个内存单元,括号内表示偏移地址。当指令执行时,8086 CPU自动读取ds中的数据作为内存单元的段地址。
8086 CPU不支持直接向段寄存器发送数据,因此必须使用寄存器来传输。
16位结构,有16根数线,所以一次可以传输16位数据,也就是一个字
mov add sub 是有两个操作数的指令,而jump 有一个操作数
编程时,根据需要定义数据段。在具体操作时可以使用ds来存储数据段的段地址,然后根据需要使用相关指令来访问数据段中的特定单元。
堆
栈是一个具有特殊访问方式的存储空间(最后进入该空间的数据最先出去)
11、栈的两个基本操作:入栈和出栈。入栈就是将一个新元素放入栈顶,出栈就是从栈顶取出一个元素。
栈的运算规则称为:LIFO(后进先出,后进先出)
编程时,可以使用一段内存作为栈
Push ax 将ax 中的数据压入堆栈
pop ax 从栈顶取出数据到ax
运算以字为单位进行
在8086 CPU中任何时刻,段寄存器SS(栈的段地址):寄存器SP(偏移地址)都指向栈顶元素。当执行入栈和出栈指令时,CPU从SS和SP中获取栈顶地址。
推斧两步
1SP=SP-2,SS:SP指向当前栈顶之前的单元作为新栈顶
2 将ax中的内容发送到SS:SP指向的内存单元。 SS:SP 现在指向新堆栈的顶部。
当压入堆栈时,堆栈顶部从高地址向低地址增长。
弹出斧头两步
1 将SS:SP指向的内存单元处的数据发送到ax中
2SP=SP+2,SS:SP指向当前栈顶以下的单元作为新的栈顶
值得注意的是,出栈后,出栈操作之前的栈顶元素仍然存在,但不再在栈上。再次推送后,新数据将被写入那里并被覆盖。
由于上面的数据不在栈中,我们可以思考栈顶越界的问题。
8086CPU并不能保证我们对栈的操作不会越界
当我们把一块内存当成栈空间时,当栈满时,我们对栈顶执行push,超出栈空间,栈空间外的数据就会被覆盖。当栈为空时,我们在栈顶再次执行pop,超过栈空间,超出区域的数据就会被覆盖。覆盖范围,需要注意
当使用堆栈暂存后需要恢复寄存器内容时,出栈和压入堆栈的顺序相反。
push和pop本质上都是内存传输指令
编程时可以根据需要定义堆栈段
段落摘要
对于数据段,将其段地址放入DS中。当使用mov、add、sub等指令访问内存单元时,CPU会访问我们定义为数据的数据段中的内容。
对于代码段,将其段地址放在CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就会执行我们定义的代码段中的指令。
对于栈段,将其地址放入SS,将栈顶单元的偏移地址放入SP。这样,当CPU进行堆栈操作时,就会使用我们定义为堆栈空间的堆栈段。
可见CPU对内存内容的处理是因为对应的段寄存器指向那里。
4.第一个程序
源程序从编写到执行的过程
第一步:编写汇编源程序
第二步:编译并链接源程序
使用汇编语言编译器对源程序文件中的源程序进行编译,生成目标文件;然后使用链接器链接目标文件,生成可以直接在操作系统中运行的可执行文件。
可执行文件包含两部分
1)程序(由源程序中的汇编指令翻译而来的机器代码)和数据(源程序定义的数据)
2)相关描述信息(例如程序有多大,占用多少内存空间等)
第三步:执行可执行文件中的程序
操作系统根据可执行文件的描述信息,将可执行文件中的机器代码和数据加载到内存中,开始相关的初始化,然后由CPU执行。
源程序
用汇编语言编写的源程序包括伪指令和汇编指令。伪指令由编译器处理。程序是指源程序中由计算机执行和处理的指令或数据。
指示:
1段和端部成对使用。它们的功能是定义一个段。段表示段的开始,结束表示段的结束。
格式: 段名段。
章节名称结束
2end 是汇编程序的结束标志。编译器遇到它就结束编译。注意区分目的。
3assume是假设的意思。它假设寄存器的某个段与程序中由段.结束定义的段相关联。
例如,代码段.代码结束定义了一个名为代码的段。
程序开始时使用假设cs:code将用作代码段的段代码与CPU中的段寄存器cs关联起来。
DOS(单任务操作系统)
如果程序p2在可执行文件中,则必须有一个正在运行的程序p1。将p2从可执行文件加载到内存中,并将CPU的控制权交给p2,以便p2可以运行。 p2开始运行后,p1停止运行。
当p2完成运行时,CPU控制权应返回给p1
这个过程称为:程序返回
任何通用操作系统都必须提供一个称为shell 的程序。用户使用该程序来操作计算机系统进行工作。
当DOS启动时,首先完成其他重要的初始化工作,然后运行command.com。运行command.com并执行其他相关任务后,屏幕上会提示当前盘符和当前路径。
例如,C:
用户输入的命令,如cd dir等,通过command执行
执行程序时,命令首先根据文件名找到可执行文件,然后将可执行文件加载到内存中,并设置CS:IP指向程序的入口。之后,命令暂停,CPU 运行程序。程序运行后,返回命令。该命令再次显示由当前盘符和当前路径组成的提示符,等待用户输入。
在DEBUG中,command将debug加载到内存中,debug将程序加载到内存中,所以程序结束后,返回到debug,Q可以返回到command。
移动斧头,4c00h
21小时内
这两条指令实现的是程序返回,用在程序结束时
编辑
编辑节目
马斯姆
汇编编译器接受默认的文件扩展名.asm,如果不是则写出文件扩展名
输入源程序文件名时,请指定路径,除非该路径位于当前路径中。
masm 1t.asm/masm 1t
简化流程并添加;最后忽略中间文件的生成
关联
链接器,接受默认文件扩展名.obj,如果不是,则写出文件扩展名
将编译生成的目标文件链接起来,得到可执行程序。输入目标文件名时指定路径,除非在当前路径中
链接1t.obj/链接1t
简化流程并添加;最后忽略中间文件的生成
学习汇编的主要目的是通过汇编语言编程,深入了解计算机的基本工作机制,从而达到随意控制计算机的目的。汇编语言编程所使用的工具都是运行在操作系统上的,所以我们暂时不做过多的深究。
1调试
Debug 中数据默认以十六进制表示。
遇到int 21h时,使用P命令执行
加载.EXE
在DOS系统中,程序被加载在.EXE文件中,程序的长度存储在cx中。
.exe加载到内存后,程序加载到内存的哪里呢?
15.[BX]与循环指令
1.bx
当用[0]表示内存单元时,0表示该单元的偏移地址。默认情况下,段地址位于ds 中。单元的长度(类型)可以由具体指令中的其他操作对象(如寄存器)指出。
[bx]也代表一个内存单元,它的偏移地址在bx中
inc bx的含义是将bx中的内容加1,执行后bx=2。
2.循环
因为它意味着循环
循环指令的格式为:循环标签
CPU执行循环指令时分两步(这里括号代表寄存器或内存单元的内容)
1(cx)=(cx)-1
2 确定cx 中的值。如果不为零,则转到标号处执行程序。如果为零,则向下执行。
通常我们使用loop指令来实现循环功能,循环次数存放在cx中
程序框架如下
mov cx,循环次数
s:
循环执行的程序段
循环
调试/执行程序时
十六进制数据A000H、A001H.FFFFH大于9FFFh写入时以字母开头,但在汇编源程序中,数据不能以字母开头,所以前面必须加0,如0A001H
当程序执行时,循环s中的标号s就变成了地址。
当我们只想跟踪循环过程时,可以使用DUBUG中的G命令来达到目的。标签之前的内容一次性执行。 g 0012表示执行程序到当前代码段的0012h(段地址在cs中)
进入循环后,我们要一次性执行循环。我们可以使用p命令来达到目的。 DEBUG会自动重复循环中的指令,直到(cx)=0。
调试和汇编编译器masm以不同的方式处理指令
Debug中,mov ax,[0]表示将ds:0处的数据发送到ax中
但在汇编源程序中,该指令被编译器处理为指令mov ax,0
Dubug将其解释为idata是一个内存单元
编译器将[idata] 解释为idata
目前的方法是将偏移地址送入bx寄存器,使用[bx]访问内存单元
但这样就比较麻烦了。另一种方式是在[ ]前面显式给出段地址所在的段寄存器。
段前缀
ds: cs: ss: es: 用于显式指定某个内存单元的段地址,成为汇编语言中的段前缀
例如,访问单位2000:0
移动斧头,2000小时
mov ds,ax
移动,ds:[0]
循环和[bx]的组合应用
在实际编程中,我们经常会遇到用同样的方法处理地址连续的存储单元中的数据的问题。每次循环时我们都需要以同样的方式改变要访问的内存单元的地址。
mov al,[bx]中的bx可以看作是代表内存单元地址的变量。我们可以通过改变bx中的值来改变访问的内存单元
计算以ffff:0~ffff:b为单位的数据之和,并将结果存储在ds中
1、求和运算的结果为字节数据,范围为0-255。 12个结果相加不会大于65535,dx可以放下。
2、ffff:0~ffff:b中的数据不能累加到ds中。这里的数据是8位的,不能直接加到16位寄存器dx中,也不能累加到dl中。 dl 将在进位中丢失。
解决方案
以16位寄存器为中介,将内存单元中的8位数据赋值给16位寄存器ax,然后将ax中的数据与dx相加,这样两个操作数的类型匹配,结果不超出边界
假设cs:代码
代码段
移动斧头,0ffffh
mov ds,ax
移动bx,0
移动dx,0
莫夫CS,12
s:
莫夫·阿尔,[bx]
移动啊,0
添加x,ax
xx
循环
移动斧头,4c00h
21小时内
代码结束
结束安全空间
在8086模式下,随意向空间写入内容是危险的。该空间可以存储重要的系统数据或代码。
Dos等合法程序一般不使用0:200~0:2ff这256字节空间。使用这个空间是安全的。谨慎起见,我们还可以调试查看。
6. 包含多个段的程序
在代码片段中使用数据
前面提到的安全空间只有256字节。如果我们需要超过256字节的空间怎么办?在操作系统环境中,通过操作系统合法获得的空间是安全的,因为操作系统不会让某个程序使用的空间与其他程序以及系统自身的空间发生冲突。
程序有两种方法获取所需的空间:
1.加载程序时分配给程序
2.程序执行时向系统申请
如果我们想让一个程序在加载时获得所需的空间,就必须在源程序中声明
当可执行文件中的程序被加载到内存中时,这些定义的数据也被加载到内存中。同时我们要处理的数据自然就获得了存储空间
dw 0123h,"dw"表示定义字体数据,即定义字
编程计算0123h、0456h、0789h、0abch、0defh、0cbah、0987h之和,结果存放在ax中
假设cs:代码
代码段
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
start: mov bx,0
移动斧头,0
莫夫CX,8
s: 添加ax,cs:[bx]
添加bx,2
循环
移动斧头,4c00h
21小时内
代码结束
标签end startstart 出现在end 之后。 end伪指令除了通知编译器程序结束外,还可以通知编译器程序的入口点在哪里。 end指令指定程序的入口在标号start处
指令end描述了程序的结束和程序的入口。经过编译和链接后,由"end start"指定的程序入口被转换为入口地址,并存储在可执行文件的描述信息中。
可以用这个方法来安排程序框架
假设cs:代码
代码段
数据
启动:
代码
代码结束
结束开始
在代码片段中使用堆栈
我们需要栈空间,当然必须由系统分配。正如上面定义的数据,可以将数据加载到内存中。因此,可以通过在程序中定义数据来获得一段空间,然后将这段空间用作堆栈空间。
深度0,0,0,0,0,0,0,0
适当设置栈顶的ss:sp后,这个空间就可以作为栈空间使用了。
所以在描述dw的作用时,我们可以说它是用来定义数据的,也可以说它是用来开辟内存空间的。
将数据、代码、栈放到不同的空间
以上内容放在一起,程序看起来很混乱,使用的堆栈空间很小,代码也不长。放在1段是没有问题的(8086模式下1段容量不能大于64kb)
所以考虑使用多个段来存储数据和代码
假设cs:code、ds:data、ss:stack
数据段
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
数据结束
堆栈段
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
堆栈结束
代码段
start: mov ax,堆栈
移动SS,斧头
移动速度,20小时
移动斧头,数据
mov ds,ax
移动bx,0
莫夫CX,8
s: 推[bx]
添加bx,2
循环
移动bx,0
莫夫CX,8
s0: 流行[bx]
添加bx,2
循环s0
移动斧头,4c00h
21小时内
代码结束
end start 定义多个段的方法与定义一个段的方法相同。
段地址的引用:在程序中,段名相当于一个标签,它代表了段地址。
我们定义了三个段,它们的含义和名字一样,但是计算机不知道
我们只需要设置代码段中的起始位置(cs:ip),ss:sp指向栈顶,ds指向数据段,其他寄存器如bx存储数据段中数据的偏移地址,我们可以根据我们的意思进行分段。
7、更灵活的内存地址定位方法
and指令:逻辑与指令,按位进行与运算(所有对应位均为1,否则为0)
例如说明
移动,01100011B
等人,00111011B
执行后al=00100011B
通过该指令可以将操作对象的相应位设置为0,而其他位保持不变。
or指令:逻辑或指令,进行按位或运算(对应位为1的为1,全为0的为0)
例如说明
移动,01100011B
或其他,00111011B
执行后al=01111011B
通过该指令可以将操作对象的相应位设置为1,而其他位保持不变。
如果我们想在计算机中存储人类能够理解的信息,就必须对其进行编码,转换成二进制信息进行存储。为了让计算机显示存储的信息,需要对其进行解码。
文本编辑过程包括按照ASCII编码规则进行编码和解码:
输入“a”,计算机将按照ASCII码规则对其进行编码,转换为61H并存储在内存的指定空间中;文本编辑软件会从内存中取出61H发送到显卡显存;工作在文本模式显卡使用ASCII码规则来解释显存中的数据。 61H 被视为字符“a”。显卡驱动显示器并在屏幕上绘制字符“a”图像。
汇编器中的"."方法表示数据以字符的形式给出,编译器将其转换为相应的ASCII码。
db "unix" 相当于db 75H,6EH,49H,58H
大小写转换问题
首先分析一下:每个小写字母的ASCII码值比大写字母的ASCII码值大20H。通过该方法可以将小写字母转换为大写字母。不过,这需要先判断字母本身是大写还是小写,但目前还没有达到这个程度。
重新分析:字母的二进制ASCII码,除了第五位数字(从0开始计数)外,大写字母和小写字母的其他数字是相同的。大写字母第五位为0,小写字母第五位为1
因此,可以使用and 和or 指令通过将第五位设置为0 或1 来更改大小写。
【《汇编语言基础教程》第四版:8086汇编语言入门指南】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
终于看到一本适合零基础学汇编的书啦!
有17位网友表示赞同!
想入门计算机领域的底层逻辑,看这本挺合适吧?
有13位网友表示赞同!
第四版了,应该内容都很完善了。
有10位网友表示赞同!
感觉汇编语言很神秘,这本书能让我入门吗?
有11位网友表示赞同!
对编程有兴趣的朋友们可以试试学习汇编,很有挑战性和成就感!
有7位网友表示赞同!
8086 汇编确实是一个难学的编程语言,但只要掌握了其中的逻辑,就能更好地理解计算机的运作机制。
有16位网友表示赞同!
这本书是零基础入门学习汇编语言的好选择吗?
有8位网友表示赞同!
看封面就觉得很有深度,期待能学到很多东西!
有9位网友表示赞同!
平时写代码都是用高层语言,想尝试一下底层的编程体验!
有13位网友表示赞同!
对计算机科学感兴趣的老铁们,这本书可不能错过。
有9位网友表示赞同!
已经开始学习汇编了,想看看这本书的讲解怎么样?
有16位网友表示赞同!
感觉学汇编能让我对计算机硬件有一个更深入的了解。
有5位网友表示赞同!
想要学习嵌入式开发,是不是应该先学汇编?
有15位网友表示赞同!
听说汇编语言是很考验逻辑思维能力的,我也想去试试手!
有12位网友表示赞同!
这本书会不会太难看懂了...
有8位网友表示赞同!
感觉汇编语言很实用,希望这本书能教会我实际操作。
有20位网友表示赞同!
学习汇编不是一件容易的事,需要坚持不懈的练习!
有8位网友表示赞同!
期待这本书能带给我宝贵知识!
有12位网友表示赞同!