- 指令格式:操作码 [操作数] [操作数]
~ 操作数:执行对象。第一个操作数 :目标操作数;第二个操作数:源操作数
~ []表示可以缺省 - 指令格式:
~ 零操作数指令:操作码
~ 单操作数指令:操作码 [操作数]
~ 双操作数指令:操作码 [操作数] [操作数] - 指令中的操作数
~ 立即数:是一个常数
~ 寄存器操作数:是一个地址,计算速度最快
~ 存储器操作数:是一个地址,计算速度最慢 - 立即数:立即数本身是参加操作的本身,可以是8位或者16位。只能作为源操作数
mov AX,1234H
mov BL,22H
- 寄存器操作数:参加运算的数存放在指令给出的寄存器中,可以是8位,16位。
mov AX,BX
mov DL,CH
- 存储器操作数:
~ 参与运算的数据存放在存储器的某一个或两个单元中
~ 表现形式:【 】:方括号里面是地址(偏移地址)
MOV AL,[1200H]
- 立即寻址
由指令直接给出运算数据。操作数为立即数 MOV AX,1200H 结果:AL=00H,AH=12H 常数1200H存放在代码段
- 寄存器寻址
参加的操作数在CPU的通用寄存器中 mov AX,BX
- 存储器操作数的寻址方式
- 直接寻址
方括号里面直接是偏移地址 MOV AX,[1200H]
- 寄存器间接寻址
偏移地址为通用寄存器中的内容,换句话说:偏移地址放在通用寄存器中
- 寄存器相对寻址
相对寻址主要用于一维数组的操作
- 基址,变址,相对寻址
操作数的偏移地址=基址寄存器中的数据+变址寄存器中的数据+偏移地址 主要运用二维数组
- 隐含寻址
操作数在默认的地址中 MUL BL 指令执行:AL*BL,结果放在AX中
- 代码生成器:将优化后的三地址码转化为目标码
- 目标码的三种形式:
~ 绝对指令代码:能够立即执行的二进制代码,所有地址已经定位
~ 可重新定位指令代码:待装配的机器语言模块,执行时由链接器把他们和某些程序连接起来,转换为可执行的二进制机器语言代码
~ 汇编指令代码:尚未经过汇编器汇编成二进制的汇编代码 - 代码生成主要考虑的问题:
~ 如何使生成目标代码较短
~ 如何充分利用寄存器,减少目标代码中访问存储单元次数
~ 如何充分利用不同cpu指令系统的特点 - 代码生成器的主要任务:
~ 指令选择:代码生成器将中间码转换成目标机器码。一个中间码可以有多种机器码转换,所以代码生成器负责选择指令。
~ 寄存器申请:程序执行过程中可能需要保存一系列值。目标机器架构可能不允许所有的值都保存在CPU内存或寄存器。代码生成器决定寄存器保存哪些值。同样,也决定寄存器保存哪些值。 ~ 指令顺序:一个代码生成器决定指令执行的顺序,它创建指令调度来执行它。 - 一个目标原型非常复杂,我们不可能描述出全部细节,所以我们通常会将其简化为一个简单目标机原型。
- 一个简单目标机原型:
~ 加载、保存、运算、跳转等操作
~ 内存按字节、寄存器、指针指向寻址或其他间接寻址
~ n个通用寄存器R0,R1,....,Rn-1
~ 所以运算分量都是整数
~ 指令之间可能有一个标号 - 目标通常一个机器指令有几十上百个指令,为了简化通常只选取一些典型指令:
~ 加载指令:LD r, x
~ 保存指令:ST x,r
~ 运算指令:OP dst,src1,src2
~ 无条件跳转指令:BR L
~ 条件跳转指令:Bcond r,L
~ 压栈操作指令:push #1 将数字1压栈 push TOP 将寄存器TOP压栈 push @sp 将指针sp指向的值压栈
~ 移动指令: ``` MOVE R0,R1 将寄存器R0的值移入到R1中 MOVE #1 R0 将数字1移入寄存器R0中 MOVE #1 @TOP 将数字1移到寄存器指向的位置 MOVE #1 @(TOP+4) 将数字1移到寄存器指向的位置基础上再加4位的位置 ```
~ 比较指令:CMP R0,R1 比较两个寄存器中值的大小 - 运算语句的三地址转目标代码:
三地址码: x=y-z 目标代码: LD R1,y //R1=Y LD R2,z //R2=Z SUB R1,R1,R2 //R1=R1-R2 ST x,R1 //X=R1 解析:优秀的代码生成器应该避免使用上面的全部4个指令,如果: ①所需的分量已经在寄存器中了 ②运算结果不需要存放内存