你好啊
目录- 基础知识
- 寄存器
- 第一个程序
- [BX]和 loop 指令
- 包含多个段的程序
- 更灵活的定位内存地址的方法
- 数据处理的两个基本问题
- 转移指令的原理
- CALL 和 RET 指令
- 标志寄存器
- 内中断
- int 指令
- 端口
- 外中断
- 直接定址表
- 使用 BIOS 进行键盘输入和磁盘读写
- 实验
- 问题与解决
- 学习资源
基础知识
机器语言是机器指令的集合
CPU(Central Processing Unit)中央处理单元
汇编语言是机器语言的助记符(伪代码)
汇编语言发展至今,有以下 3 类指令组成。
(1)汇编指令:机器码的助记符,有对应的机器码。
(2)伪指令:没有对应的机器码,由编译器执行,计算机并不执行。
(3) 其他符号:如+、-、*、/等,由编译器识别,没有对应的机器码。
汇编语言的核心是汇编指令,它决定了汇编语言的特性。
寄存器
一个 16 进制位=4 个二进制位,,,,4H=0100B
两个 16 进制位=8 个二进制位=1 个字节
通用寄存器 AX(AH+AL),BX(BH+BL),CX(CH+CL),DX(DH+DL)
段寄存器 CS,DS,SS,ES
mov x,y
add x,y
十六位结构的 CPU:
运算器一次最多可以处理 16 位的数据;
寄存器的最大宽度为 16 位;
寄存器和运算器之间的通路为 16 位。
段地址 x16+偏移地址=物理地址的本质含义是: CPU 在访问内存时,用一个基础地址(段地址 x16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。
内存没有段的概念,段的划分来自 CPU,CPU 根据自己的段找到物理地址
8086CPU 的工作过程:
(1)从 CS:IP 指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP=IP+所读取指令的长度,从而指向下一条指令;
(3)执行指令。转到步骤(1),重复这个过程。
在 8086CPU. 加电启动或复位后(即 CPU 刚开始工作时)CS 和 IP 被设置为 CS =FFFFH, IP=0000H, 即在 8086PC 机刚启动时,CPU 从内存 FFFF0H 单元中读取指令执行,FFFF0H 单元中的指令是 8086PC 机开机后执行的第一条指令。(BIOS)
CS:IP 同时修改:jmp 段地址:偏移地址
仅修改 IP:jmp 合法寄存器||用寄存器中的值修改 IP
8086 机中任意时刻,CPU 将 CS:IP 指向的内容当做指令执行
代码段:将一组内存单元定义为一个段,可以将长度为 N(N<=64KB)的一组代码,存在一组地址连续、起始地址为 16 的倍数的内存单元中
>内存中字的存储
8086CPU 是小端法,16 位机,所以 4E20H
将起始地址为 N 的字单元简称为 N 地址字单元
0 地址单元存储字节型数据是 20H,0 地址字单元存储的字型数据是 4E20H
>DS 和[address]
将10000H读入al中
mov ax,1000H
mov ds,ax
mov al,[0]
//mov ds,1000H ×,这对于DS来说是非法指令[设计缺陷]
需要找“媒人”
CS:IP 指向代码段
-a 更改代码
代码对数据段 DS 所指的内存单元进行操作
>mov add sub 指令
mov ax,6
mov bx,ax
mov ax,[0]
mov [0],ax
mov dx,ax
mov ax,ds
mov ds,[0]
段地址不能用于算术运算,add、sub 不行
>数据段
注意注意
AL 内存字节,AX 内存字
0001:0000 和 0000:0010 是等价的,0001:0001 和 0000:0011 等价,注意换算
>栈
现今的 CPU 都有栈的设计
push ax:将寄存器中的数据送入栈中
pop ax:将栈顶的数据送入 ax
栈的出栈和入栈操作都是以字为单位的===>SP+2
任意时刻,SS:SP 指向栈顶单元
如果将 10000H~1000FH 当做栈,初始栈的状态为空,SP=0010H,栈内一个元素,SP=00Eh,栈满 SP=0000H
硬盘格式化,数据还存在,只是改变了标签,指针(栈)
C 语言中局部变量,在函数之外的局部变量没有效果(栈)
栈顶超界问题
栈溢出(攻击方式)
CPU 不清楚栈的上界和下界在哪里
8086CPU 的工作机理,只考虑当前的情况:
当前栈顶在何处,当前要执行的指令是那一条
栈(C 语言自动分配)堆(线性表,必须释放)
栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。
后进先出:所以恢复寄存器内容的时候要先恢复后进的,寄存器入栈和出栈的顺序要相反
系统喜欢 XOR(异或)进行清零操作
push、pop 实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与 mov 指令不同的是,push 和 pop 指令访问的内存单元的地址不是在指令中给出的,而是由 SS:SP 指出的。
同时 push 和 pop 还要改变 SP
push、pop 等栈操作指令,修改的只是 SP,也就是说,栈顶的变化范围最大为:0~FFFFH。
将一段内存当作栈段,仅仅是我们在编程时的一种安排,CPU 并不会由于这种安排,就在执行 push、pop 等栈操作指令时就自动地将我们定义的栈段当作栈空间来访问。
10000H~1FFFFH 当做栈段,当栈为空时 SP 指向 0000H
一个栈段最大可设为多少?16 位 CPU,FFFFH,即为 2^16,64KB,栈空时 SP=0,不空不满 0<SP<FFFFH,栈满时 SP=0
堆栈的作用:
早期的 C 语言,保存函数的返回地址,调用函数时把寄存器的值都存放在栈里面
为了函数,面对过程而存在
代码段:CS:IP 数据段 DS 栈段:SS:SP
段是人为定义的,对于 CPU 来说都一样,只是执行的位置(指针)不同
一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么也不
是。关键在于 CPU 中寄存器的设置,即 CS、IP, SS, SP, DS 的指向。
第一个程序
>一个源程序从写出到执行的过程
伪指令被 CPU 所执行
伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。
汇编语言由多个段组成(至少有一个段)
汇编编源程序:
伪指令(编译器处理)
汇编指令( 编译为机器码)
程序:源程序中最终由计算机执行、处理的指令或数据。
程序(指令和数据)最先以汇编指令的形式存在源程序中,经编译、连接后转变为机器码,存储在可执行文件中。
assume CS:codesg(伪指令,编译时由编译器执行)
XXX segment
XXX ends//end segment
end//指令结束,释放内存??
>源程序>编辑源程序>编译>连接>以简化的方式进行编译和连接>.exe 的执行
DOS 是个单任务操作系统
一个程序 P2 在一个可执行文件中,P2 要运行,必须有一个正在运行的程序 P1,将 P2 从可执行文件中加载入内存后,将 CPU 的控制权交给 P2,P2 才能运行(P1 暂停运行)P2 运行完,CPU 控制权交给 P1
一个程序结束后,将 CPU 的控制权交还给使它得以运行的程序===>>程序返回
程序返回:(汇编指令,由 CPU 执行)
mov ax,4c00H
int 21H
逻辑错误:程序编译时不能表现出来的、在运行时发生的错误
连接的作用有以下几个:
当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序将它们连接到一起,生成一个可执行文件;
程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件;
一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这此内容处理为最终的可执行信息。所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必项用连接程序对目标文件进行处理,生成可执行文件。
>可执行文件中的程序装入内存并运行的原理
正在运行的 command(shell),将 1.exe 中的程序加载入内存
command 设置 CPU 的 CS:IP 指向程序的第一条指令(程序的入口),从而使程序得以运行
程序运行结束,返回到 command,CPU 继续运行 command
>程序执行过程的跟踪
Debug 将程序加载入内存,设置 CS:IP 指向程序的入口,但是 Debug 不放弃对 CPU 的控制,所以可以使用 debug 的命令进行调试
第三步:SA:0 和 SA+10H:0,而不是 SA:100H 是因为这种表述是在描述不同的段
psp 重定位, 程序的接口
程序加载后,ds 中存放着程序所在内存区的段地址,这个内存区的偏移地址为 0,则程序所在的内存区的地址为: ds:0 ;
这个内存区的前 256 个字节中存放的是 PSP,dos 用来和程序进行通信。
从 256 字节处向后的空间存放的是程序。
--------------------------------------总结-------------------------------------------
在内存管理中,与栈对应是堆。
对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;
对于栈来讲,它的生长方式是向下的,是向着内存地址减小的方向增长。
在内存中,“堆”和“栈”共用全部的自由空间,只不过各自的起始地址和增长方向不同,它们之间并没有一个固定的界限,如果在运行时,“堆”和 “栈”增长到发生了相互覆盖时,称为“栈堆冲突”,系统肯定垮台。
[BX]和 loop 指令
在 debug 之中写程序,-a 修改的[0][1]等偏移地址,是可行的
但是在 masm 写的程序中,是不可行的,会被当做 01 看待
>[BX]
>Loop 指令
包含多个段的程序
更灵活的定位内存地址的方法
数据处理的两个基本问题
转移指令的原理
CALL 和 RET 指令
标志寄存器
内中断
int 指令
端口
外中断
直接定址表
使用 BIOS 进行键盘输入和磁盘读写
实验
操作指令:
R:查看改变 CPU 寄存器的值,R CS,R
D:查看内存中的内容,d 1000:0,d 1000:0 9
e:改写内存中的内容,e 1000:0 0 1 2 3 4,e 1000:10 .....(写机器码)
e 命令写入机器码,u 命令查看内存中机器码的含义,t 命令执行 CS:IP 指向的机器码
a:以汇编形式在内存中写入机器指令,a 1000:0 mov ax,1.....(写汇编指令)
问题与解决
大端法和小端法压栈有什么不同