Assembly

不仅仅是汇编

CPU中的"内存"--寄存器

常用寄存器

  • AX: accumulator, 累加寄存器
  • CX: counter, 计数寄存器
  • DX: data, 数据寄存器
  • BX: base, 基址寄存器
  • SP: stack pointer, 栈指针寄存器
  • BP: base pointer, 基址指针寄存器
  • SI: source index, 源地址寄存器
  • DI: destination index, 目的变址寄存器

以上的寄存器都是16位(2个字节)的寄存器

在[A-D]X寄存器中, 每一个寄存器都是由两个8位寄存器组成的

AX: AH + AL

BX: BH + BL

CX: CH + CL

DX: DH + DL

其中H表示High, L表示Low, 左高右低是内存中地址的分布形式, 而X是eXtend中的X, 表示扩展, 因为16位寄存器确实是8位寄存器的扩展,
但是剩下的四个寄存去就没有High和Low之分了, 如果我们想要获取SP该16位寄存器中低8位的值, 需要将SP中的值MOV到AX之类的寄存器中, 在通过访问AL寄存器获取,
也许你会注意到, 为什么不直接放到AL中呢? 这是因为MOV指令的作用, 因为MOV指令操作的目标源和数据源需要时大小相同的容器(在C语言中就是变量, 其实CPU中的寄存器可以为C语言中的变量, 也就是容器)

扩展寄存器

  • EAX
  • ECX
  • EDX
  • EBX
  • ...
    通过名字我们可以判断出来, 他们与我们上面提到的常用寄存器中的AX等, 就是多了E, 其实这里的E就是从Extend中提取出来的, 表示对16位寄存器的再扩展, 成为了32位, 同上面AX与AL的关系
    一样, AX是含在EAX中的
  • 还是提一下, 任何时刻CPU执行的指令的地址在CS:IP表示的地址上, 通过公式计算可以得出对应的物理地址

段寄存器

这里不展开讲了, 一般段寄存器的名称是以S结尾的, 表示Sgement(段)

汇编语言

nasm命令行工具的使用

  • nasm -o
  • ndisasm: 反汇编工具

汇编语言的指令

  • NASM中所有的标签和变量都是地址, 而使用了[]表示其该地址中的值
  • DB: data byte --> 向当前的地址中的一个字节slot中传入值 usage: DB 8 注意: DB "Hello world!" 也是对的, DB命令会自动将每一个字符添加到指定的位置
  • DW: data word --> 向当前地址中的两个字节slot传入值, 就是比DB多了字节数而已, 其他性质一样 usage: DW 88888
  • DD: double word --> 向当前地址中的四个字节slot传入值, 就是比DW多了字节数而已, 其他性质一样 usage: DD 88888 789798
  • RESB: reserve byte --> 指定空余多少个连续的字节, 填充的值为0 usage: RESB 100 意思是在当前位置开始空出100个字节, 并将每一个字节的值填充为0, 执行完该指令之后, 我们程序的指令也在100字节之后了, 但是在NSAM中使用不来, 替代他的是times # db 0
  • ORG: original --> 指定了程序开始的地址, 该指令后面跟的值是在特定范围的值, 否则会对系统造成破坏 usage: ORG 0x7c00
  • JMP: jump --> 类似于C语言中的goto usage: JMP entry: 其中entry:是一个标签, 实质上就是一个地址, 使用JMP让CPU其对应的地址找指令运行

JMP指令还可以修改CS和IP这两个寄存器中的值, CS和IP中的值不能通过MOV指令实现, 因为CPU没有对应的指令, 格式: JMP 234H:222H, CS的值为234H, IP的值为222H

JMP 段地址:偏移地址

  • MOV: move --> 类似于C语言中的赋值语句 usage: MOV SS, AX 等同于SS = AX, 注意如果两边是寄存器的话, 需要保证寄存器是同位数的
  • ADD: add --> 类似C语言中的加法赋值 usage: ADD SS, AX 等同于SS = SS + AX
  • "$"变量: 在不同的情况下有不同的意思, 一般表示当前程序所在的内存地址, 而$$表示的一个section的地址
  • []中括号: 中括号锁定地址, MOV BYTE [123] SI 意思是找到地址为123的那个字节的slot, 将SI里面的值赋给那个字节, 同样MOV [BX] AX是找到BX寄存器中存放的值, 并根据该值找到地址编号为该值的slot, 将AX中的值赋给他, 注意: []中可以是数字, 可以是该有B或者I的寄存器(SI, BX, BH, BL, SP, DI等), 但是不可以是其他的了(AX, CX, DX, SS)
  • ORG 0x7c00: 一般出现在GRUB等bootloader中的第一行, 该指令告诉CPU将该程序加载到内存的0x7c00地址上, 并且ORG后面的值只能是一些固定的时, 在0x7c00地址之前的内存存放的是BIOS, 所以不能将bootloader加载到0x7c00之前
  • EQU: 用来为标识符定义一个整型常量,它的作用类似C语言中的#define
  • TIMES 510-($-$$) DB 0: 该指令指令经常出现在MBR中, 目的是为了除了最后的标志位0x55AA, 之前的填充为0

NSAM中的宏定义

  • 类似于C语言中的宏定义, 不过使用的是%号, 其他都是一样的: %include, %ifndef, %define, %ifdef,
  • 使用带有参数的宏定义
%macro macroname #(表示参数的个数)
mov [%1] ax         ; %1表示第一个参数, 以此类推, %2, %3

%endmarco

练习

  • 计算2的4次方

MOV al, 2

ADD al, al

ADD al, al

ADD al, al

实模式下CPU如何将线性地址转换为物理地址

  • 公式: 段地址(基地址) < 1 + 偏移地址
  • 在实模式下的地址如0x00123abc, 该线性地址是由段地址(这里内存并没有分段, 而是CPU为了方便管理内存采用段的机制)和偏移地址组成的, 0012为段地址, 3abc为偏移地址
  • 偏移地址的取值范围取决于CPU的寻址方位, 也就是地址总线的宽度
  • CPU访问内存时, 必须指定地址, 在8086CPU中, 采用内部段地址和内部偏移地址, 在内部加法器中通过公式计算出最终的物理地址
  • CPU中可以通过不同的段地址和偏移地址计算出同一个物理地址, 数学解方程知识

CPU是如何判断01代码是指令还是数据的

  • 我们知道不管是指令还是数据, 在计算机的层面上都是01的代码, CPU都会将其当做指令, 有时将其当做数据, CPU是如何办到的呢?
  • 其实根据我们上一部分的知识, 通过CS:IP(段地址:偏移地址), CS < 1 + IP 得出的物理地址中的内容会被当做指令, 而没有被计算到的就会被当做数据, 每执行一次指令, IP(指令指针寄存器)中的值就会增加, 加上的值就是上一次读取的指令加数据的长度(以byte为单位), 目的是计算出下一个指令的地址
  • CPU执行的指令取决于CS:IP, 如果我们希望CPU执行我们的代码, 可以使用JMP #:#执行我们的代码段
  • 8086CPU是不允许直接使用MOV指令将数据复制给段寄存器中的. 要从一个空间复制到段寄存器中

  • CPU读指令设置CS:IP
  • CPU读数据设置DS, [address]

猜你喜欢

转载自www.cnblogs.com/megachen/p/9555990.html