计算机系统结构

博文末尾支持二维码赞赏哦 _

cpu内部:               
1. PC Program Counter指令指针寄存器指向下一条指令的地址EIP(X86-32)或者RIP(X86-64)
2. 寄存器与寄存器堆Registers在处理器CPU内部以名字来访问的快速存储单元
3. 条件状态码Condition Codes用于存储最近执行指令的结果状态信息用于条件指令的判断执行
内存单元Memory:以字节编码的连续存储空间存储程序代码、数据、运行栈stack 以及操作系统数据

汇编语言数据格式

c 语言          数据类型   汇编代码后缀  大小(字节为单位)
char              字节         b  byte         1
short       字           w  word         2
int        双字         l               4
long int      双字         l               4
long long int   无           无              4
char*        双字     l               4
float       单精度        s              4
double       双精度        l              8
long double    扩展精度      t             10/12

第一条汇编指令实例

c 语言代码:int t = x+y // 两个整数(32位)
汇编代码:addl 8(%ebp) %eax//l表示双字 8是位偏移量操作数:x: 寄存器 Register  eaxy: 内存    Memory   M[ebp+8]   ebp是函数栈基址寄存器t: 寄存器  Register eax结果t保存在寄存器eax中
类似于表达:x += y
或者:int eax;int* ebp;eax += ebp[2];//这里按字节

数据传送指令

movel 源地 目的地
将一个双字从源地移动到目的地
允许的操作数类型有:立即数Imm:常整数如: $0x400, $-533可以用1,2或4个字节来表示寄存器 Reg:8个通用寄存器之一%eax%ebx%ecx%edx%esi%edi%esp  栈顶%ebp  栈底存储器Mem:四个连续的字节汇编       类似C语言
立即数--->寄存器  movl $0x41, %eax     temp = 0x41;
立即数--->内存     movl $-43, (%eax)    *p   = -43;
寄存器--->寄存器   movl %eax, %edx      temp2 = temp;
寄存器--->内存     movl %eax, (%edx)    *p    = temp;
内存 --->寄存器   movl (%eax), %edx    temp  = *p;
不允许内存到内存  

简单得寻址模式

1. 间接寻址 (R)     Mem[Reg[R]]
寄存器R 指定得内存地址
movl (%ecx), %eax
2. 基址+偏移量 寻址 D(R)  Mem[Reg[R] + D]
寄存器R 指定内存的起始地址
常数D 给出偏移地址
movl 8(%ebp), %ecx

寻址模式使用示例

交换两个数
C语言
void swap(int* xp, int* yp){
int t0 = *xp;
int t1 = *yp;
*xp = t1;
*yp = t0;
}汇编语言分析
寄存器 变量:
%ecx   yp
%edx   xp
%eax   t1
%ebx   t0
对应汇编:
movl 12(%ebp),%ecx  # ecx = yp  是地址  放到ecx寄存器中
movl 8(%ebp), %edx  # edx = xp 是地址  放到edx寄存器中
movl (%ecx), %eax   # eax = *yp t1 值   取寄存器中地址指向的内存地址中的内容放入 寄存器eax中
movl (%edx), %ebx   # ebx = *xp t0 值  取寄存器中地址指向的内存地址中的内容放入 寄存器ebx中
movl %eax, (%edx)   # *xp = eax          交换内容放入原来内存指向得地址中
movl %ebx, (%ecx)   # *yp = ebxebp 是函数栈 基地址
ebp+8 的位置 存储 指针xp 指向内存的一个地址
ebp+12 的位置 存储 指针yp 指向内存的一个地址

变址寻址

常见形式:D(Rb,Ri,S) Mem[Reg[Rb] + S*Reg[Ri] + D]D:  常量(地址偏移量)Rb: 基址寄存器:8个通用寄存器之一Ri: 索引寄存器: %esp不作为索引寄存器一般%ebp也不做这个用途S: 比例因子, 1,2,4,8其他变形:D(Rb,Ri) Mem[Reg[Rb] + Reg[Ri] + D](Rb,Ri) Mem[ Reg[Rb] + Reg[Ri] ](Rb,Ri) Mem[Reg[Rb] + S*Reg[Ri]]

地址计算指令 leal    ,  lea +l

leal src, destsrc 是地址计算表达式子  D(Rb,Ri,S) ---> Reg[Rb] + S*Reg[Ri] + D计算出来得地址赋给 dest
使用实例:地址计算,无需访问内存 auto *p = &x[i];进行x+k*y这一类型得整数计算,k = 1,2,4,8

整数计算指令:

addl src,dest   #  dest = dest + src #加法
subl src,dest   #  dest = dest - src #减法
imull src,dest  #  dest = dest * src #乘法
sall src,dest   #  dest = dest << src #左移位 等价于shll
sarl src,dest   #  dest = dest >> src #算术右移位 补 被移动数的最高位
shrl src,dest   #  dest = dest >> src #逻辑右移位 左边单纯补 0
xorl src,dest   #  dest = dest ^ src  #按位异或
andl src,dest   #  dest = dest & src  #按位与
orl  src,dest   #  dest = dest | src  #按位或incl dest  #  dest = dest + 1  #++ 自增1
decl dest  #  dest = dest - 1  #-- 自减1
negl dest  #  dest = - dest # 取非
notl dest  #  dest = ~ dest #  取反

将leal指令用于计算

c语言:int temp(int x, int y){int t1 = x+y;int t2 = z+t1;int t3 = x+4;int t4 = y*48;int t5 = t3+t4;int ret = t2*t5return ret;}
汇编代码:movl 8(%ebp), %eax   # eax = xmovl 12(%ebp), %edx  # edx = yleal (%eax, %edx), %ecx # t1  = ecx = x+y addl 16(%ebp), %ecx     # t2 = ecx = z+t1leal (%edx,%edx,2), %edx # edx = 3*ysall $4, %edx            # t4 = edx = 2^4 * 3 * y =16*3*y = 48*yleal 4(%eax,%edx), %eax  # t5 = eax =4+x+t4imul %ecx, %eax          # ret = eax = t2*t5

逻辑运算示例

C语言:int logical_(int x, int y){int t1 = x^y;int t2 = t1>>17;int mask = (1<<13) - 7;int ret = t2 & mask;return ret;}
汇编语言:
movl 8(%ebp), %eax  # eax = x
xorl 12(%ebp), %eax # t1 = eax = x^y
sarl $17, %eax      # t2 = eax = t1 >> 12
andl $8185, %eax    # ret = eax = t2 & 8185  2^13-7 = 8185 这里编译器会算出来

x86-32 与 x86-64主要类型数据宽度的区别

             x86-32  x86-64long int     4       8char*        4       8   内存地址编号长度

使用 条件码(标志寄存器) 来进行控制 

  addl src,dest    t =a+bCF Carry Flag 进位标志  可用于无符号整数运算的溢出 SF Sign Flag  符号位标志 结果<0 的话,被设置为1ZF Zero Flag 0结果标志  结果为0 的话,被设置为1OF Overflow Flag 溢出标志 if a>0 & b>0 & t<0 被设置为1

比较指令 compare 被比较数 比较数 compare b,a a-b(但是不会改变a的值)

  compare b,a  a-bif a == b , ZF =1if a < b  , SF=1if (a>0 && b<0 && (a-b)<0)||(a<0 && b>0 && (a-b)>0) OF=1

测试指令 test 做与操作

  testl s2,s1testq s2,s1计算 s1 & s2并设置相应的条件码,不改变目的操作数testl b,a   计算a&bif a&b ==0,  ZF =1if a&b < 0,  SF =1CF=0,OF=0

setX指令 获取条件码状态

  sete  相等 结果为0 ZF =1时setne 不相等        ZF =0sets  结果<0      SF =1setns 结果>=0       SF=0
  有符号数:setg  大于      setge 大于等于setl  小于setle 小于等于
  无符号数:setb  小于 belowseta  大于  ablove

读取条件码实例

c语言:int gt(int x, int y){return x>y;}
汇编语言:
movl 12(%ebp), %eax    # eax = y
cmpl %eax, 8(%ebp)     # compare x:y  x-y
setg %al               # al寄存器是eax的低八位得名字 al = x>y
movzbl #al, %eax       # 8位扩展到16位 

跳转指令 Jx

  jmp   无条件跳转je    相等jne   不相等js    结果<0jns   结果>=0有符号 jg      大于jge   大于等于jl   小于jle   小与等与无符号 ja   大于jb   小于

跳转指令 Jx 实例

c语言:int abs_(int x, int y){int rest;if(x>y){rest = x - y;}else{rest = y-x;}return rest;}
汇编语言:movl 8(%ebp), %edx  #  edx = xmovl 12(%ebp), %eax #  eax = ycmpl %eax, %edx     # x - yjle  .L7            # x <=y 跳转到.L7subl %eax, %edx     # x>y  ,  计算rest = x-ymovl %edx, %eax     # 结果放在 寄存器eax指向的地址.L8:leaveret.L7:subl %edx, %eax  # x <=y 跳转到.L7 计算 y-x jmp L8

循环的汇编语言表示

for循环:for(Init初始条件; 判断测试条件Test; 更新量Update)循环体Body;转换到 While-do结构:Init初始条件;while(判断测试条件Test){循环体Body;更新量Update;}再转换成go-to结构:Init初始条件;goto middle;  # 无条件跳转 jmp
loop:循环体Body;更新量Update;
middle:if(判断测试条件Test)goto loop;

switch case的汇编语言格式

c语言版本:long switch_eg(long x, long y, long z){long w = 1;switch(x){case 1:w = y*z;break;case 2:w = y/z;break;case 3:w +=z;break;case 5:   case 6:w -= z;break; defult:    // 这里 case 0 和 case 4 和其他情况w = 2;}return w;
}汇编语言版本:pushl %ebp      # 保存 寄存器老的 ebp的值
movl %esp, %ebp # 函数堆栈 栈顶指针 %esp 存放在%ebp
pushl %ebx      # 保存 寄存器ebx的值movl 8(%ebp), %ebx  # 函数堆栈距离栈顶 偏移8位处存放x的值
movl 12(%ebp), %eax # y
movl 16(%ebp), %ecx # z
cmpl $6, %ebx       # x - 6 
jbe .L11            # X <= 6 才进入正常的 case.L8:                # default 部分movl $2, %eax    # w = 2jmp .L12         # break.L11:jmp *.L7(,%ebx,4) %访问.L7段表 对应的 case block.section     .rodata
.L7:.long .L8   # case 0 情况 进入 default.long .L3   # case 1 情况 .long .L4   # case 2 情况 .long .L9   # case 3 情况 .long .L8   # case 4 情况 进入 default  .long .L6   # case 5 情况 case 6.long .L6   # case 6 情况 进入 default.L12:         # breakpopl %ebx  # 后进先出popl %ebp  # 先进后出ret        # 函数段返回.L3:                # case 1imull %ecx, %eax # w = w*zjmp .L12         # break.L4:                # case 2movl %eax, %edx  sarl $31, %edxidivl %ecx      # w =y/zaddl %ecx, %eax # case 3 w += z  jmp .L12        # break.L9:               # case 3movl %1, %eax   # w = 1addl %ecx, %eax # w +=zjmp .L12        # break.L6:               # case 6  case 5movl $1, %eax   # w = 1subl %ecx, %eax # w -= Zjmp .L12        # break

x86-32的程序栈

  栈---水桶结构的一块内存区域---->只有一个出口(栈底%ebp (桶口)高地址(计算机地址逆向生长))先进后出后进先出 FILO寄存器 %esp 存储栈顶地址(下部)寄存器 %ebp 存储栈底地址(上部)注意 %esp  %ebp  始终指向当前正在运行的活动的函数过程栈

存储的内容

  局部变量返回地址临时空间

栈帧的分配与释放

  1. 进入过程 先分配栈帧空间set-up code2. 过程返回时 释放栈帧空间finish code

pushl 压栈 把大象装进水桶的操作

  水面栈顶%esp会上什(步进单位为4个内存地址块)
pushl src  # 从src 取得操作数# %esp = %esp-4 栈顶上升 水面上什

popl 出栈操作 把大象从水桶中取出来

  水面栈顶%esp会下降
popl Dest # 读取栈顶数据(%esp) 放入 目标位置Dest# %esp = %esp+4 栈顶下降 水面下降

过程调用 call label 过程返回指令 ret

过程调用指令:
call label #将返回地址(call指令的下一条指令地址(%eip)压入栈),跳转至label继续执行
过程返回指令
ret  #跳转至栈顶的返回地址

函数调用时 寄存器的使用惯例

  8个寄存器:两个特殊寄存器:调用者和被调用者可能都需要保存始终指向当前活动的堆栈上两端%ebp 栈底指针%esp 栈顶指针三个由调用者保存:%eax  常用于保存过程的返回值的%ecx%edx三个由被调用者保存:%ebx   %esi%edi

递归调用实例

  c语言版本:
int rfact(int x){int rval;// 递归出口if(x<=1) return 1;// 递归调用rval = rfact(x-1);return rval * x;}
汇编语言版本:
调用者栈情况          前 %ebp       <-  %ebp 栈底前 %ebx变量x 父过程调用的参数返回地址 adr   <- %esp 栈顶
#  进入过程 先分配栈帧空间
被调用者(函数rfact)   老的 %ebp      第一步需要保存%ebp    pushl %ebp 指向 前%ebp   <- %esp 栈顶 栈顶自动调整第二步 movl %esp, %ebp                        <- %ebp 栈第指针也指向了栈顶 (栈基址)老的 %ebx      第三步 pushl %ebx                             <- %esp 栈顶 栈顶自动调整 (用来存储函数内的变量值,之前的值需要先保存)主体代码:
movl 8(%ebp), %ebx   # ebx = x
cmpl $1, %ebx        # compare x : 1  , 计算 x - 1
jle .L78             # x <= 1  直接跳转到递归出口 .L78
leal -1(%ebx), %eax  # eax = x-1  被调用者这里需要用到 %eax 作为过程的返回值的
pushl %eax           # push x-1  入栈保存 这里用作为调用者需要保存 %eax
call rfact           # rval = rfact(x-1), 函数返回值保存在 %eax中
imull %ebx, %eax     # rval * x
jmp .L79             # 跳转到 done
.L 78:movl $1, %eax     # return eax = 1# 返回代码  过程返回时 释放栈帧空间
.L79:movl -4(%ebp), %ebx # 复原老的 %ebx 的值movl %ebp, %esp     # 栈顶指针 %esp  也指向%ebp指向的位置popl %ebp           # 恢复老的 %ebp ret

x86-64的通用寄存器

  x86-32 有 8个32位的寄存器8个寄存器:两个特殊寄存器:调用者和被调用者可能都需要保存始终指向当前活动的堆栈上两端%ebp 栈底指针%esp 栈顶指针三个由调用者保存:%eax  常用于保存过程的返回值的  累加器 计算操作数和存放结果数据%ecx  计数寄存器%edx  数据寄存器三个由被调用者保存:%ebx  基础寄存器  指向DS数据段的数据指针%esi  源索引%edi  目的索引
x86-64 有 16个64位的寄存器:与 x86-32兼容的8个64位的寄存器%rax  低16位也叫 %eax     eax 低16位称为ax,ax的高8位称为ah,低8位称为al%rbx            %ebx     ebx 低16位称为bx,bx的高8位称为bh,低8位称为bl%rcx            %ecx     ecx 低16位称为cx,cx的高8位称为ch,低8位称为cl%rdx            %edx     edx 低16位称为dx,dx的高8位称为dh,低8位称为dl%rsi            %esi     esi 低16位称为si%rdi            %edi     edi 低16位称为di%rsp            %esp     esp 低16位称为sp%rbp            %ebp     ebp 低16位称为bp
新增加的8个64位的寄存器:%r8   低16位也叫 %r8d%r9             %r9d%r10            %r10d%r11            %r11d%r12            %r12d%r13            %r13d%r14            %r14d%r15            %r15d

x86-32 6个16位的段寄存器,定义内存中的段

        CS   代码段   存储指令和执行的地方DS, ES, FS, GS  数据段SS  堆栈段  当前程序存储堆栈的地方

数组的表示 访问 操作

访问:
c语言:
typedef int array_i_5[5];// 定义一个长度为5的整数数组类型 array_i_5
array_i_5 cmu = {1, 2, 5, 3, 4};// 定义一个数组cmu  函数五个整形变量
cum[index];//可以获取相应索引位置处的 元素汇编语言:
# %edx = cmu   数组首地址
# %eax = index 索引
movl (%edx, %eax, 4), %eax # cmu[index]  4为一个整形类型所占据的直接数量
数组循环遍历修改
c语言:
void temp(array_i_5 z){
int i;
for (i=0; i<5; i++)z[i]++;// 数组各元素自增1
}
汇编语言:
movl $0, %eax  # 循环变量 %eax = i
.L4:addl $1, (%edx, %eax, 4) # z[i]++addl $1, %eax            # i++cmpl $5, %eax            # compare i:5jne .L4                  # i < 5 循环

多维数组的表示

int arr_i_i[row_index][col_index];
一个int类型占据4个字节地址
则 arr_i_i[y][x] 的实际地址为 arr_i_i + y*row_index*col_index + 4*x
例如一个 arrii[4][5]的二维数组,每一行占有 4*5=20个字节
那么 arrii[i][j] 的地址为  arrii + i*20 + 4*jarrii + 4*(i+4*i) + 4*j
相关汇编代码:
先计算列地址
leal 0(,%ecx,4), %edx        #  4*j
leal (%eax, %eax, 4), %eax   # 5 *i
movl arrii(%edx, %eax, 4), %eax # arrii + 4*j + 4*(i+4*i) 

hello world程序

      //helloworld.c#include <stdlib.h>#include <stdio.h>int main(){printf("Hello World\n");exit(0);return 0;//这里就不会编译了}// gcc 编译gcc -S -O2 helloworld.c-S   生成汇编代码(与系统类型相同)  helloworld.s-m32 64位系统生成32位汇编-On  -O2 2级编译优化// asm      movl $LC0 (%esp)  #设置过程调用参数 字符串首地址 放在栈顶对应的地址call _putsmovl $0, (%esp)   #设置过程调用参数call _exit

标记符号

.text  #代码段
.p2align 4,,15 #对齐方式 按16 = 2^4字节对齐,填充,, 0, 最大填充15个字节
.section .rdata "dr"  #只读数据段
LC0:# 字符串的起始地址.ascii "Hello World\12\0"

linux 汇编命令

编译:

  //gcc -S -O2 helloworld.cas -o my-object-file.o helloworld.s# -gstabs 产生带调试信息的object文件# 64位环境下添加命令行 --32 生成 32位目标文件链接成执行程序ld -o my-exe-file my-object-file.o# 64位环境下添加命令行 -m elf_i386 生成 32位目标文件
helloworld.s
.data     #数据段
msg:      # 字符 首地址.ascii "Hello World\n"len = .-msg #  字符长度 . 表示当前地址
.text     # 代码段
.globl _start  # 汇编程序入口地址,如同C语言的main函数
_start:movl $len, %edx # 字符串长度 字节数量movl $msg, %ecx # 字符串存储位置的起始地址movl $1, %ebx   # 系统输出(write 系统调用1)movl $4, %eax   #int $0x80       # 中断 来指向系统调用# eax存放的是系统调用的功能号# ebx 为参数 为1时 是 write 显示器输出# edx 字符串长度 字节数量# ecx 字符串存储位置的起始地址movl $0, %ebx  # 系统调用(程序退出 0)movl $4, eaxint $0x80      # 中断 来指向系统调用

汇编代码入门 ATT指令格式相关推荐

  1. Java JVM 汇编代码入门 GitChat链接

    为什么 new Integer(151)=151?我来带你们一起学习下 JVM 汇编代码吧,窥探下神奇的 Java 中间语言到底什么样子的,能帮你更深入的理解 Java. 本文包含以下内容 工具介绍 ...

  2. 分析一个简单的汇编代码

    分析一个简单的汇编代码 部分常见的寄存器 寄存器 16位 32位 64位 累加寄存器 AX EAX RAX 基址寄存器 BX EBX RBX 计数寄存器 CX ECX RCX 数据寄存器 DX EDX ...

  3. 程序编码(机器级代码+汇编代码+C代码+反汇编)

    [-1]相关声明 本文总结于csapp: 了解详情,或有兴趣,建议看原版书籍: [0]程序编码 GCC调用了一系列程序,将源代码转化成可执行代码的流程如下: (1)C预处理器扩展源代码,插入所有用#i ...

  4. 【单片机】汇编指令入门学习|单片机启动代码汇编

     目录 汇编指令入门学习 [汇编指令]MSP430汇编指令 IAR下的汇编/单片机启动代码汇编 ARM 汇编&ARM 指令集 学习笔记 汇编指令入门学习   在进行汇编程序设计时,MSP430 ...

  5. Linux下的ATT语法(即GNU as 汇编语法)入门

    学习这么长时间,一直在C语言这一层面上钻研和打拼,日积月累,很多关于C的疑惑在书本和资料中都难以找到答案.程序员是追求完美的一个种群,其头 脑中哪怕是存在一点点的思维黑洞都会让其坐卧不宁.不久前在it ...

  6. 寄存器(3)KDB入门+MIPS汇编及汇编代码详解

    KDB入门和MIPS汇编 1. KDB 介绍及进入退出命令 1.1 KDB 介绍 1.2 进入KDB及退出KDB 2. KDB 调试 2.1 断点类 2.2 内存操作类 md 2.3 堆栈跟踪类 2. ...

  7. 51单片机开发系列一-51单片机开发环境搭建以及入门汇编代码

    51单片机开发系列一 51单片机开发环境搭建以及入门汇编代码 象棋小子    1048272975 1. 51单片机概述 51单片机是对所有兼容Intel 8031指令系统的单片机的统称.目前教科书基 ...

  8. ATT与Intel汇编代码格式

    ATT(根据"AT&T"命名的,AT&T是运营贝尔实验室多年的公司)格式汇编代码,是GCC.OBJDUMP和其他一些工具的默认格式.其他一些编程工具,包括Micro ...

  9. 郁金香汇编代码注入怎么写看雪_汇编语言入门五:流程控制(一)

    回顾 前面说到过这样几个内容: 几条简单的汇编指令 寄存器 内存访问 对应到C语言的学习过程中,无非就是这样几个内容: 超级简单的运算 变量 好了,到这里,我们继续接下来的话题,程序中的流程控制. 文 ...

最新文章

  1. 树的高度从零还是一开始数_数据结构与算法之1——树与二叉树
  2. 网络最大流的三种基础算法
  3. 三个球数求最大值c语言,C语言中一个简单的球3个数最大数的程序中,最后一步:printf(apos;apos;max=%d\napos;apos;,max);怎么理解...
  4. Uva 11077 Find the Permutation
  5. Spanning-tree Potocol(整理)
  6. [Android] Implementation vs API dependency
  7. Linux基础:find命令总结
  8. iPhone第二季度在美销量降23%,但iPhone SE有亮点
  9. mysql hang分析_mysql hang
  10. CSAPP-C1-计算机系统漫游
  11. Pandas基础(一)——Pandas基础
  12. iOS 开发的9个超有用小技巧
  13. 可视化大屏设计尺寸_大屏数据可视化设计规律
  14. 聚类分析与判别分析十题_数学建模系列
  15. 2022年最新BIM计费标准,涉及14省市
  16. (个人笔记)EDEM耦合Recurdyn流程
  17. 系统测试总结报告模板
  18. 【noip模拟题】华尔街的秘密
  19. 网站底部添加公安备案HTML代码
  20. js获取浏览器默认语言设置并自动跳转

热门文章

  1. 发布gridgame游戏包
  2. DIN14 IPO系列 一路输入四路输出 模拟信号隔离分配器 光耦隔离
  3. python汇率转换_python 实现汇率计算功能
  4. RNA-seq助力发现治疗多种肿瘤的潜在靶点
  5. 用windows公文包实现不同盘符两个文件文件夹文件同步
  6. element el-dropdown 水平居中
  7. 启闭机 闸门 液压坝生产厂家-邢台市金来利水利机械制造有限公司
  8. IPhone程序发布到应用程序商店的流程
  9. Ubuntu22.04中root用户下依然权限不够,执行不了可执行文件
  10. STM32的7位数据位支持