计算机基础

汇编笔记

# .data段 包含了已经初始化的数据项的数据定义,已经初始化的数据在程序开始运行前就拥有了自己的值。这些值是可执行文件的一部分
#  当可执行文件被加载到内存中用于执行时,它们被加载到内存中。 定义的初始数据项越多,可执行文件就越大,需要更长的时间从磁盘加载到内存
.code32
.data
    msg: # 变量名 代表变量值的地址
        .ascii "hello" # 变量的值 字符串(h是最低有效元素,小端序存放结果为hello)
        len = . - msg
   snippet:
        .ascii "KANGAROO"
    x:
       .int 0x12345678 # 数字类型 1是最高有效位,小端序存放结果为78 56 34 12(4个字节)

# .bss段,当程序需要从磁盘上读取文件数据时,读取的数据需要一个缓冲区来存放,这样的缓冲区就是在.bss段中定义的。
# .bss段中定义数据项不会添加到可执行文件的大小中。
#.bss

# 真正组成程序的机器指令存放在.text段中,一般情况下,.text段中不进行数据项的定义
.text
    .global _start
        _start:
#1、 mov指令:复制,l:双字 4字节 w:字 2字节 b:单字节
#            movw $0x67fe,%ax # 立即数0x67fe存入ax中
#            movw %ax,%bx # ax的数存入bx中-
#            movb %bh,%cl # bx高8位67存入cx的低8位中
#            movb %bl,%ch # bx的低8位fe存入cx高8位中,cx的值:0xfe67
#            xchgb %cl,%ch #交换两个寄存器的值,cx的值:0x67fe
#            movl (msg),%eax #将变量中的内容放到寄存器中,从内存读始终是从低位开始读,寄存器右边低位左边高位,所以结果是:0x6c6c6568(lleh)
#            movb (msg),%al # 将内容存入al(ax低八位)中,将
#            movl (x),%eax # 值 0x12345678×(内存从低位开始读,先读78再读56...,寄存器右边低位左边高位,从右往作存78,56...)
#            movw (x),%dx # dx寄存器两个字节,结果为0x5678



#2、 加减指令
#            # inc指令:加1(一个操作数)
#            # dec指令: 减1(一个操作数)
#            movl $0xFFFFFFFF,%eax
#            movl $0x2D,%ebx
#            dec %ebx # 结果0x2C
#            inc %eax # 结果 0x0
#            movl $0x5,%eax # 设置eax的值为5
#     DoMore:dec %eax  # 减1 DoMore:是一个标号
#            jnz DoMore # 跳转到DoMore(如果eax的值不为0,当eax的值是0时,eflags寄存器的第6位(ZF,从右往左0开始),将会设置为1代表操作数为0了)
#                       # jnz指令每次循环都会判断ZF位
#


#3、 字符串转小写
#            movl $snippet,%ebx # 将字符串的地址(也是首字母的地址)放到ebx中
#            movl $0x8,%eax  # 循环次数8
#    DoMore2:addb $0x20,(%ebx) # 将地址中的字符(1个字节b)加32(十进制),变为小写字母
#            inc %ebx # 地址加1(指向下一个字符)
#            dec %eax # 减1 直到eax为0(循环8次)
#            jnz DoMore2 # 循环操作 最有snippet的所有字符变为小写
#            movl (snippet),%ebx # 将字符串(前4个字节)放到ebx中 结果 0x676e616b (gnak)


#4、负数补碼表示
#            movl $0x5,%eax  # 将0x5放入eax (有符号数:最高位(最左边)为1表示负数,为0表示正数)
#    DoMore3:dec %eax  # 递减
#            # jmp DoMore3  # 跳转 循环(注释掉,以免再次死循环) ,当eax减到-1时,为0xFFFFFFFF(负数的补碼。负数补碼:原码取反+1, 补码->原碼:减1取反)
#            #  cpu在晶体管逻辑层面上,并不真正需要减法,它只产生减数的补碼,并将其与被减数相加
#            #  如:3-2 = 3+(-2); -2以补碼的形式存放,直接按位相加即可。


#5、正数转负数
#            #neg指令 将一个数取负数(补碼形式)
#            movl $0xF,%eax  # 0xF 16
#            neg %eax  # 取负数 0xFFFFFFF1(-16的补碼)
#            addl $0xF,%eax  # 0

#6、带符号移动(复制)
#            movw $0xFFD6,%ax # -42放到ax(2个字节)中
#            movl %eax,%ebx # ebx(4字节) 还是 0x0000FFD6 (但表示的值是65494,原来的符号位变成了普通位)
#
#            # movsx指令:带符号一起移动
#            mov $0xFFD6,%ax
#            movsx %ax,%ebx # ebx (4字节) 0xFFFFFFD6(-42)
#

#7、 乘法
#            # mul:乘法(无符号) div:除法(无符号)
#            # imul:乘法(有符号) idiv:除法(有符号)
#            movl $447,%eax
#            movl $1739,%ebx
#            mul %ebx # 乘法,显式因子(r/m8(16,32) 任意8位,16位,32为的寄存器或内存地址,不能是立即数),隐式因子(al,ax,eax 根据显式因子的位数而定)
#                     # 结果存放在eax中 777333, 并且edx为0(edx用于保存乘法运算的部分结果)同时eflags寄存器的进位标志位CF(第0位)是0,表示无进位
#
#8、乘法 进位
#            # 将数设大一点
#            movl $0xFFFFFFFF,%eax
#            movl $0x3B72,%ebx
#            mul %ebx  # 计算结果 eax:0xffffc48e edx:0x3b71(进位后高位部分) eflags CF位设置位1(有进位)

#9、 除法
#           movl $0x8,%eax # 隐式操作数 被除数(商的整数部分 al,ax,eax 对应的余数部分ah,dx,edx)
#           movl $0x3,%ebx # 显式操作数 除数(r/m8(16,32) )
#           div %ebx  # 结果 商为2保存在eax中,余数2保存在edx中

#10、push指令(将16位或32寄存器或内存值压入堆栈,64位linux编译不过,在代码顶部加上.code32) (其他push指令:pushf pushfd pusha pushad)
#            movl $0xaabbffee,%eax
#            pushl %eax # 将esp的值(栈顶元素的地址)减小4(堆栈增长方向从高到低,eax占4个字节),将eax寄存器中的数据压入堆栈(压入顺序:aa bb ff ee)
            #pushw %ax  # 将esp减小2(ax占2个字节),将ax的值压栈
# pop指令(弹栈,弹出的字节数由操作数决定,只能是16位或32位)(其他pop指令:popa,popad,popf,popfd)
#           popw %bx # 取出栈顶的2个字节(bx的大小是2个字节)放到bx中 取出顺序 ee ff (存入bx结果: ffee ,从右往左存)


#11、系统调用 (控制台输出hello)
 #Linux的系统调用通过int 80h实现,用系统调用号来区分入口函数。 操作系统实现系统调用的基本过程是:
 #应用程序调用库函数(API);
 #API将系统调用号存入EAX,然后通过中断调用使系统进入内核态;
 #内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
 #系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数;
 #中断处理函数返回到API中;
 #API将EAX返回给应用程序。

#           movl $len, %edx						#参数三 字符串长度 (edx)
#           movl $msg, %ecx						#参数二 要显示的字符串(ecx)
#           movl $1, %ebx						#参数一 文件描述符(1:标准输出 ebx)
#           movl $4, %eax						#系统调用号(sys_write eax)
#           int $0x80							#调用内核功能 int0x80指令首先把它后面的那条指令地址压入栈顶(记录系统调用后的返回地址),系统调用完成后再用IRET指令弹出继续执行
#           #退出程序
#           movl $0, %ebx						#参数一 退出代码(ebx)
#           movl $1, %eax						#系统调用号(sys_exit eax)
#           int $0x80							#调用内核功能

#12、位
    #and 与操作符 可以用来屏蔽一些不需要的位
            #movl $0xffffffff,%eax
            #movl $0x0000ffff,%ebx
            #andl %ebx, %eax  # 与运算 结果存放到目标操作数eax中
    # or 或操作
            #movl $0xffffffff,%eax
            #movl $0x0000ffff,%ebx
            #orl %ebx, %eax  # 结果存放到目标操作数eax中

    # xor 异或操作 可以用来清零(一个数与自身异或结果为0)
            #movl $0xffee00ff,%eax
            #xorl %eax, %eax  # 结果为0 存放到目标操作数eax中
    # not 非操作 取反
             #movl $0xffffff00,%eax
             #notl %eax  # 结果为0xff
    # shl 左移操作
             #movl $0xffffffff,%eax
             #shll $0x4,%eax  # 左移4位 结果为0xfffffff0
    # shr 右移操作
             movl $0xfffffff0,%eax
             shrl $0x4,%eax  # 左移4位 结果为0x0fffffff

关于作者

程序员,软件工程师,java, golang, rust, c, python,vue, Springboot, mybatis, mysql,elasticsearch, docker, maven, gcc, linux, ubuntu, centos, axum,llm, paddlepaddle, onlyoffice,minio,银河麒麟,中科方德,rpm