汇编语言学习笔记

 

恋恋风辰

https://www.cnblogs.com/secondtonone1/p/6617884.html

汇编语言学习笔记(一)

一:变量类型

汇编语言变量基本类型如下:

sdword :表示32位整数

dword:表示32位无符号整数

sword:表示16位整数

word:表示16位无符号整数

sbyte:表示8位整数

byte:用于表示字节,大小为8位

变量的表示和定义:

C语言中

int num3 = 5;

汇编中

num3 sdword 5;

C语言中

char name1;

char name2=‘A’;

汇编中:

name1 byte ?

name2 byte ‘A’;

C语言中:

char name[] = "iloveu";

汇编中:

name byte ‘iloveu’,0 //字符串结汇通常带有二进制的0,占用一个字节。

二:立即数的存储

在汇编语言中将一个常量存储在内存空间需要通过mov操作,立即数为一个常量。

将5存储在变量num1中

num1 sdword ?;

mov num1 5;

mov操作后面两个参数为内存或寄存器,mov两个参数可以为以下关系:

mov mem , imm 将立即数存储到内存中

mov reg, mem 将内存单元的数据存储到寄存器中

mov mem, reg 将寄存器中的内容存储到内存单元中

mov reg, imm 将立即数存储在寄存器单元中

mov reg, reg 将第二个寄存器中的内容存储到第一个寄存器中

但是不能mov mem, mem, 不能将一个内存中的内容存储到另一个内存中

需要通过mov reg, mem1; mov mem2 reg;可达到目的。

 三:寄存器

数据不能直接从一个内存单元中移动到另一个内存单元中,需通过寄存器做中转。

内存单元数据放在随机存储器(RAM)中,RAM将内存单元中的数据拷贝到CPU的寄存器中,

然后将CPU的寄存器中的数据copy到另一个存储单元中。

将内存单元的内容复制到寄存器中的动作成为装载,将cpu中寄存器的数据copy到内存中成为存储。

不是所有的寄存器都可以被开发者使用,提供给程序员的寄存器为通用寄存器。

16位通用寄存器为ax,bx,cx,dx

32位通用寄存器为eax,ebx,ecx,edx

以eax举例,eax低16位为ax寄存器,ax寄存器低8位为al寄存器,高8位为ah寄存器。如图所示:

eax为累加器,主要用于参与算术逻辑

ebx为基址寄存器,用于处理数组

ecx为计数器,用于循环操作

edx为数据寄存器,用于算术指令。

除此之外还有eflags表示状态的寄存器,esp堆栈指针指向栈顶部,ebp基址指针指向栈底部。

 四:输入输出

 字符串的输出

C语言版:

#include<stdio.h>
int main()
{
     printf("%s\n"," Hello World!");  
     return 0;
}

用汇编实现为:

复制代码

            .386
            .model flat ,c
            .stack 100h
printf      PROTO arg1:Ptr Byte, printlist:VARARG
            .data
msg1fmt     byte "%s", 0Ah, 0
msg1        byte "Hello World", 0
            .code
main        proc
            INVOKE printf, ADDR msg1fmt, ADDR msg1
            ret
main        endp
            end            

复制代码

 .386是一个汇编指令,它指明程序可以被汇编为在Intel386系列或更高级的计算机运行,

.model flat表明程序使用保护模式,.model flat ,c表示该程序可以与C或C++程序进行连接,且需要运行在Visual C++环境中。

.stack 100h 表示堆栈的大小应该达到十六进制100个字节

printf 名称后面的 PROTO表示printf为函数原型, arg1表示printf第一个参数为指向Byte类型的Ptr指针。

第二个参数为VARARG类型可变参数printlist

.data表示数据段,.code表示代码段。

msg1fmt 为byte类型的字符串,0Ah为16进制表示\n,0表示字符串结尾\0

msg1为byte类型的字符串,“Hello World”

proc汇编指令表示一个过程,过程名称为main

INVOKE 为汇编指令,调用函数printf完成输出,

格式为 INVOKE 函数名, 参数一,参数二

这里为msg1fmt字符串的首地址,以及msg1的首地址。

ADDR为取地址操作。

ret表示返回和C语言return 0一样。

main endp表示main proc结束

end表示整个汇编程序结束。

 

整数的输出和字符串类似,下面通过输入输出程序介绍汇编语言的输入和输出

复制代码

     .386
     .model flat c
     .stack 100h
printf  PROTO arg1:Ptr Byte, printlist:VARARG
scanf   PROTO arg2:Ptr Byte, inputlist:VARARG
     .data
in1fmt  byte "%d", 0
msg0fmt byte 0Ah, "%s", 0
msg1fmt byte 0Ah, "%s%d", 0Ah, 0Ah, 0
msg0    byte "Enter an integer for num1: ", 0
msg1    byte "The integer in num2 is: ",0
num1   sdword ?
num2    sdword ?
    .code
main   proc
    INVOKE printf, ADDR msg0fmt, ADDR msg0
       INVOKE scanf, ADDR in1fmt, ADDR num1
       mov eax, num1
       mov num2, eax
       INVOKE printf, ADDR msg1fmt, ADDR msg1, num2
      ret
main  endp
      end
       

复制代码

 输入和输出类似,C语言中输入函数为scanf

汇编程序通过

scanf   PROTO arg2:Ptr Byte, inputlist:VARARG 
PROTO表示scanf为函数原型, 第一个参数为指向Byte的指针
第二个参数为可变参数
scanf输入整数num1,需要取地址,所以会采用ADDR num1



将上述汇编转译为C语言

复制代码

#include<stdio.h>
int main()
{
    int num1, num2;
    printf("\n%s", "Enter an integer for num1: ");
    scanf("%d", &num1);
    num2 = num1;
    printf("\n%s%d\n\n", "The integer in num2 is: ", num2);
    return 0;

}

复制代码

  五:算数指令

加法指令

add mem, imm  将立即数imm和内存单元mem中数值相加存储在mem中

add reg, mem 将内存mem中的数值和寄存器reg中的数值相加,结果存储在reg中

add reg, imm 将立即数imm和寄存器reg中数据相加存储在reg中

add mem, reg 将寄存器中的数据和内存单元mem中数据相加存储在mem中

add reg, reg 将寄存器中的数据和寄存器相加,最后存储在第一个reg中

注意:不可add mem, mem 也不可 add imm, imm 两个立即数没法相加结果也没办法存储

两个内存单元也没办法相加,和mov mem, mem是一样的,都不可以直接操纵两个内存。

减法指令

sub mem, imm 将内存单元mem中数据减去立即数imm存储在mem中

sub reg, mem 将寄存器reg中数据减去mem中的数据,存储在reg中。

sub mem, reg 将内存单元mem中数据减去寄存器reg中数据,结果存储在mem中

sub reg ,imm 将寄存器reg中的数据减去立即数imm,将结果存储在reg中

sub reg, reg 将第一个reg数据减去第二个reg数据,最后结果存储在第一个reg中

注意:不可sub mem, mem 也不可 add imm, imm 两个立即数没法相减结果也没办法存储

两个内存单元也没办法相减,和mov mem, mem是一样的,都不可以直接操纵两个内存。

 

乘法指令imul

imul mem 将内存单元mem中的数据和寄存器eax中的数据相乘,结果存储在edx:eax中

imul reg 将寄存器reg中的数据和寄存器eax中的数据相乘,结果存储在edx:eax中

注意:imul 只有一个参数,这个参数只能为内存单元或者寄存器,不能是imm立即数

由于乘法指令imul可能会造成乘积位数大于乘数和被乘数的位数,所以将结果存储在edx:eax中

imul为有符号的乘法指令, mul为无符号的乘法指令, imul和mul参数只能为mem或reg,如果

想计算num = mem* 3;mem为内存单元数据,3为立即数

imul实现为

mov eax, 3

imul mem

mov num, eax

 

除法指令idiv

和乘法指令类似,idiv 为有符号除法, div为无符号除法

进行除法操作前将被除数放入edx:eax中,除法操作后edx存储的为余数,

eax存储的为商。举例 num = 13/,5;

汇编语言实现过程先将13放入eax中,

调用cdq 可以将eax内容扩展到edx:eax寄存器对中,将5放入内存单元或寄存器中,

调用idiv最终edx存储为3,eax存储为2

汇编语言实现如下:

mov eax, 13
mov ebx,5
cdq
idiv ebx
mov num , eax

 

 

cbw 指令可将字节转换为字,一个字为两个字节,即将al内容扩展到寄存器ax中

cwd 字转换为双字, 即将ax中的内容扩展到eax中

cdq 双字转换为4个字, 即将eax中内容扩充为edx:eax中。

只有cdq允许有符号位,所以进行idiv和div操作前执行cdq将eax中内容扩充到

edx:eax中。

注意:idiv和div 只有一个参数,这个参数只能为内存单元或者寄存器,不能是imm立即数

 

自增自减指令

dec reg    reg中的数据自减

dec mem  mem中的数据自减

inc reg reg中数据自增

inc mem mem中数据自增。

注意:dec 和inc  只有一个参数,这个参数只能为内存单元或者寄存器,不能是imm立即数

 

取反指令,也就是求一个数的补码

neg mem

neg imm

注意:neg  只有一个参数,这个参数只能为内存单元或者寄存器,不能是imm立即数

下面两个例子是综合计算

复制代码

; v = -w + m * 3 - y/(z-4) 
;w,v,m,y, z分别为内存单元

mov ebx, w
neg ebx
mov eax,3
imul m
add eax, ebx
mov v ,eax
mov ebx, z
sub ebx, 4
mov eax, y
cdq
idiv ebx
sub v, eax

复制代码

 

 

汇编语言基础第一部分记录到此为止,下一篇跟进记录

 

 

 

 

重剑无锋,大巧不工

分类: 汇编学习

https://www.cnblogs.com/secondtonone1/p/6652155.html

汇编语言学习笔记(二)

六、选择结构

if-then结构

C语言版本

 

if(count == 10)
{
   count --;
   i++;
}

 

MASM汇编

.if count==10
dec count
inc i
.endif

 

cmp指令,该指令用于比较两个参数大小

cmp mem, imm 比较内存mem和立即数imm大小

cmp reg, imm 比较寄存器reg和立即数imm大小

cmp reg, mem 比较寄存器reg和内存mem大小

cmp mem, reg 比较内存mem和寄存器reg大小

cmp imm, reg 比较立即数和寄存器reg大小

cmp imm, mem比较立即数和内存mem大小

cmp reg, reg比较两个寄存器数据大小

.if 内部是通过cmp实现的,.if和cmp都不能比较两个内存中数据大小 

 

条件跳转指令

je 相等时跳转

jne 不相等时跳转

je 和jne可以用于无符号数比较跳转,也可用于符号数比较跳转

 

符号数据跳转指令

jg  大于时跳转

jge 大于或等于时跳转

jl 小于是跳转

jle 小于或等于时跳转

 

jnle 不小于且不等于时跳转,即大于时跳转

jnl 不小于时跳转

jnge 不大于且不等于时跳转

jng 不大于时跳转

这类指令可用上面的jg,jge,jl,jle替代,尽量不要使用,

不容易理解。

 

.if-.end汇编指令可用上面的cmp指令和条件跳转指令替代

.if number == 0
dec number
.endif

 

 用条件跳转指令实现为:

if01: cmp number, 0
        jne  endif01
then01:  dec number
endif01: nop

 

 if-then-else结构

汇编指令的结构为

复制代码

.if 条件

    ;执行语句

.else

   ;执行语句

.endif

复制代码

 

举例说明,C语言

if (x>=y)
    x--;
else
    y--;

 

汇编语言

复制代码

if01:     mov eax,x
          cmp eax,y
          jl else01
then03: dec x
              jmp endif01
else01:  dec y
endif01:  nop

复制代码

采用汇编指令

mov eax , x
.if eax >= y
   dec x
.else
   dec y
.endif

 

 

 嵌套if结构

C语言

复制代码

if(x < 50)
  y++;
else
    if(x <= 100)
       y = 0;
   else
       y--;

复制代码

 

 汇编语言

采用汇编指令

复制代码

.if x < 50
   inc y
.elseif x <= 100
   mov y, 0
.else
   dec y
.endif 

复制代码

 

 通过跳转指令实现上述步骤

复制代码

if01: cmp eax, 50
        jge else01
then01: inc y
         jmp endif01
else01:    nop
if02:    cmp x, 100
           jg else02
then02:  mov y,0
           jmp endif01
else02:  dec y
endif01:  nop

复制代码

 

 case 结构

C语言

复制代码

switch(w)
{
   case 1: x++;
     break
  case 2:
  case 3: y++;
     break;
  default: z++;

}

复制代码

 

 汇编语言中没有case switch对应的汇编指令,但是可以通过cmp指令和跳转指令实现

 

复制代码

switch01:    cmp  w, 1
                   je case1
                   cmp w,2
                   je case2
                   cmp w,3
                   je case2
                   jmp default01
case1:  inc x
            jmp endswitch01
case2: inc y
           jmp endswitch01
default01 : inc z
endswitch01: nop

复制代码

 

 无符号跳转指令

ja 高于跳转

jae 高于或等于跳转

jb 低于跳转

i = 1;
while(i <= 3)
{
     i++;
}

 

jbe 低于或等于时跳转

 

逻辑运算符综合实现

.if w==1 || x==2&&y == 3
  inc z
.endif

 

条件跳转指令实现上述逻辑

复制代码

if01:  cmp w,1
         jne or01
or01: cmp x, 2
          jne endif01
          cmp y, 3
          jne  endif01
then01:  inc z
endif01: nop

复制代码

 

 

 七、迭代结构

 1 前置循环结构while

C语言

i = 1;
 while(i <= 3)
{
    i++; 
}

 

汇编指令实现

mov i, 1
.while (i <= 3)
   inc i
.endw

 

 条件跳转指令实现

 

mov i, 1
while01: cmp i, 3
         jg endw01
         inc i
endw01: nop

 

 

 

2 后置循环结构 do while

C语言

 

i = 1;
do
{
   i++;

}while(i <= 3);

 

 

 

汇编指令 .repeat .until

 

mov i, 1
.repeat
   inc i
.until i > 3

 

 

 

.repeat-.until和C语言do -while一样,第一次先执行循环体内容,do-while是在每次运行循环体后判断条件不满足跳出循环,

.repeat-.until 是判断条件不满足则一直循环,直到.until后面条件成立则跳出循环。

通过汇编跳转指令实现

 

mov i, 1
repeat01:  nop
           inc i
           cmp i, 3
           jle repeat01
endrpt01: nop

 

 

 

3 固定循环结构 for

C语言

 

for(i=1; i <= 3; i++)
{
     //循环体
}

 

 汇编语言

mov ecx, 3
.repeat 
;  循环体
.untilcxz

 .repeat-.untilcxz 对应C语言的for循环固定循环结构,它和.repeat-.until都是后置循环结构。

都要先执行一次循环体,然后.untilcxz会将ecx中的数据值减1,接着判断ecx是否为0,ecx为0则跳出循环

ecx大于0则继续执行循环。所以执行.repeat-.untilcxz这个循环前保证ecx大于0

.repeat-.untilcxz 内部是loop实现的,所以同样可以通过loop实现该循环

mov ecx, 3
for01 : nop
         ; 循环体
         loop for01
endfor01: nop

 

 loop 和.untilcxz一样,都会将ecx中的值减1,然后判断ecx是否为0,为0则终止循环,大于0则继续循环。

loop指令最多只能向前跳转128字节处,也就是循环体内容不得超过128字节。同样.repeat-.untilcxz循环体也不得超过128字节。

与.if高级汇编指令一样,.while-end高级汇编指令和.repeat-.until高级汇编指令都基于cmp,不可直接比较两个内存变量。

使用loop指令和.repeat-untilcxz指令前,需要保证ecx大于0,在循环体中尽量不要更改ecx数值,如果需要更改则将ecx

数据先保存在内存中,执行完相关操作后将ecx数值还原,保证.untilcxz和loop操作ecx的准确性。

下面用汇编语言实现斐波那契数列

if n= 0, then 0

if n = 1, then 1

if n = 2, then 0+ 1 = 1

if n = 3, then 1+ 1 = 2

if n = 4, then 1+2 = 3

n 为任意非负数,计算n对应的数值sum。

 

用.while循环实现如下

复制代码

.if n==0
mov sum, 0
.endif
.if n==1
mov sum, 1
.endif
mov ecx, n
mov eax, 0
mov sum, 1
.while (ecx <= n)
mov ebx, sum
add sum, eax
mov eax, ebx
.endwhile

复制代码

 

 

到目前为止,循环和条件判断就介绍到这里,下一期介绍堆栈位移之类的指令。

 

https://www.cnblogs.com/secondtonone1/p/6664598.html

汇编语言学习笔记(三)

八、逻辑运算指令

数字在计算机中以二进制存储,每个位数字为0或者1,当两个二进制数字进行逻辑按位&,逻辑按位|,逻辑异或^操作,

可以采用汇编语言提供的逻辑运算指令and,or, xor,not等指令。

and指令:

C语言&操作,将0110&1101得出结果为0100

C语言代码为

if(flag & maskit)
   count++;

 

汇编语言高级指令可实现:

mov eax, flag
.if eax & maskit
inc count
.endif

不采用高级指令的情况下可采用如下代码:

if01:     mov eax, flag
          and eax, flag
          jz endif01
then01:   inc count
endif01:  nop

and指令和cmp指令一样,and运算执行后,eflags寄存器的响应比特位会被设置。

同样xor,not, or等指令也会设置eflags相应比特位。

jz 指令之前介绍过,当eflags寄存器相应比特位被设置为0后该指令将逻辑跳转到指定位置。

同样ZERO?也可以判断eflags寄存器相应比特位是否被设置为0,为零则为真,反之为假

 

mov eax, flag
and eax, maskit
.if !ZERO?
inc count
.endif

 

 or指令:

or指令和and指令用法一样

;flag = flag | maskit
mov eax, flag
or eax, maskit
mov flag, eax

 

 xor指令:

;flag = flag ^ maskit;
mov eax, flag
xor eax, maskit
mov flag, eax

 三种指令的两个参数类型限制是一样的

and mem, imm

and mem, reg

and reg, reg

and reg, imm

and reg, mem

 or, xor参数类型和上面and一样,三种命令都不可以直接操作两个内存变量。

 

 九、逻辑移位指令

shl reg, cl 将reg中数据向左移动cl寄存器中数值大小的位数。

shl reg, imm 将reg中数据向左移动imm立即数大小的位数。

举例:

mov cl , 3
shl reg, cl
;也可以采取立即数
shl reg, 3

 

上述代码都是将reg中数值向左移动三位,那为什么采用cl寄存器呢?因为在老式

的8086/8088处理器上,操作数中能使用的唯一立即数为1,如果调用shl reg,3

会出问题,所以通常的做法是mov cl, 3先将3移动到cl寄存器中,再调用

shl reg, cl可完成reg数值向左移动3位。

shl 的参数出去reg,还可以是mem内存

shl mem, cl

shl mem, imm

同样shr为向右移动,参数形式和shl一样。

 

如果10101010调用shl向左移动一位,那么所有比特位的数字依次左移,

第0个位置比特位数字填充0,第7个比特位数字1向左移动到CF进位标记里。

数字变为01010100。

 

同理,shr逻辑右移也是移动后第7个比特位补充0,第0个比特位数字放入CF标记中。

写一段代码测试每个比特位数字为1的位数。

 

复制代码

mov count, 0
mov ecx, 8
mov temp , al
.repeat
mov ah, al
and ah, 00000001b
.if !ZERO?
inc count
.endif
shr al, 1
.untilcxz
mov al, temp

复制代码

 

 由于and指令会导致第一个参数数值被修改,所以将al数值每次and操作前都

copy到ah中。最后处理完所有比特位,将temp数据copy回al中。

test命令可以避免and修改第一个参数数值的问题,同样test命令使用后

eflags寄存器相应的比特位也会被设置。

复制代码

mov count, 0
mov ecx, 8
mov temp, al
.repeat
test al, 00000001b
.if !ZERO?
inc count
.endif
shr al, 1
.untilcxz
mov al, temp

复制代码

 

 

 十、算术移位指令

算数左移

sal reg, cl

sal reg, imm

sal, mem, cl

sal reg, cl

参数和shl一样。移动效果和shl一样,都是将低比特位依次移动到高比特位,最高比特位

移动到CF标记里,空位补0。

移动示意图: 

 

如果符号位为0,那么左移操作和shl一样,没什么问题。

如果符号位也就是第七个比特位为1,如1111 0000,向左移动一位那么数据为

1110 0000,左移操作后数值为原数值乘以2,负数在计算机中以补码形式存储。

整数的补码为原码,负数的补码为整数原码按位取反末尾加1, 1111 0000-1为

1110 1111,按位取反后为0001 0000,该数值为十进制16,所以1111 0000为

-16,-16乘以2,也就是左移操作,为-32,那么-32在二进制中如何表示呢?

32用8位二进制表示为0010 0000,负数为正数原码按位取反末尾+1,0010 0000取反后

为1101 1111,末尾+1为1110 0000, 1110 0000恰好为1111 0000左移一位得到的数字。

考虑这样一个问题,如果是图中所示数字10101010左移会造成数字变为0101 0100,这样

最高位变为0怎么办?

这就是所谓的左移溢出,8位二进制能表示的二进制数为0111 11111~1111 1111 。

0111 1111 表示十进制127

1111 1111 表示十进制-1

1000 0000 是负数补码, 该数-1取反后得到1000 0000 该数值为128原码

所以1000 0000 表示-128,

那么8位二进制能表示的十进制数为127~-128

 10101010表示的负数为-86,超过-64了。

凡是超过-64都会引起左移位溢出,负数补码出去符号位,最高位为0,此时逻辑左移会导致数据溢出。

算数右移

算数右移保持符号位不变,其余比特位依次向右移动,空出的比特位补零,最右边的比特位移除后进入CF标记。

计算product = num * 8;可通过移位指令完成,速度更快

 

;product = num1 * 8;
mov eax, num1
sal eax, 3
mov product , eax

 

 乘以8,不是sal eax, 8,这样是乘以256,应该为移动三位。

answer =  amount /4 ;

;answer = amount/4;
mov eax, amount
shr eax, 2
mov answer, eax

 

 

 十一、循环移位指令

 

循环移位将末尾移除来的数据放入另一端空缺的比特位。

循环左移位

rol reg, cl

rol reg, imm

rol mem, cl

rol mem, imm

循环右移位

ror reg, cl

ror reg, imm

ror mem, cl

ror mem, imm

循环右移位

循环左移位

 

之前检测一个8位二进制数中比特位数字为1的比特位个数程序可通过循环右移完成,

因为循环右移8位后,移位的结果和初始数值一样。

复制代码

mov count , 0
mov ecx, 8
.repeat
test al, 0000 0001b
.if !ZERO?
inc count
.endif
ror al, 1
.untilcxz

复制代码

 

 十一、堆栈操作

push 指令

push reg

push mem

push imm

push指令为入栈指令,参数可以为寄存器reg, 内存mem, 立即数imm。

pop指令

pop mem

pop reg

pop出栈指令,参数只能为内存和寄存器。

将监测比特位数值为1的比特位数程序和栈操作指令结合

复制代码

push eax
push al
mov count, 0
mov ecx, 8
.repeat
test al, 0000 0001b
.if !ZERO?
inc count
.endif
shr al,1
.untilcxz
pop al
pop eax

复制代码

 十二、xchg交换指令

xchg交换指令用于交换两个地址空间数据,xchg参数可以为以下几种

xchg reg, reg

xchg reg, mem

xchg mem, reg

同样不可以操作两个内存。

实现两个数交换

temp = num1;
num1 = num2;
num2 = temp;

用mov指令实现为:

mov edx, num1
mov eax, num2
mov num1, eax
mov num2, edx

 

用push, pop指令实现为

push num1
push num2
pop   num1
pop   num2

 

用xchg指令实现为

mov eax, num1
xchg eax, num2
mov  num1, eax

三种办法的效率比较为mov指令最快,其次xchg指令,最后为push pop指令。

这一篇就介绍这里,下一篇讲述宏和过程。

https://www.cnblogs.com/secondtonone1/p/6679330.html

汇编语言学习笔记(四)

十三 过程

汇编语言的过程可以被理解为方法,过程调用采取如下形式

call pname

pname为过程名,call为调用指令

pname过程的格式为

 

pname proc
            ; 过程体
            ret
pname endp

 

proc 告知编译程序过程pname的开始,endp告诉编译程序过程pname的结束。ret指令表明何时返回到调用程序中,

ret和高级语言return不一样,ret不返回任何值给调用程序。ret是过程中必不可少的调用指令。

举个过程的实例:

复制代码

sample      proc
            .if eax == 0
            mov eax, 1
            .else 
            mov edx, 0
            .endif
            ret
sample   endp

复制代码

如果过程中使用到ecx,eax,ebx, edx等通用寄存器,而不确定外界是否也用到这些通用寄存器,

那么在过程中需要将外界用到的寄存器数据保存,可以使用临时变量,也可以使用入栈。

譬如ecx数据在调用程序中使用,同时过程mult中也用到ecx,那么使用ecx之前将ecx入栈。

复制代码

mult   proc
          push ecx
          mov eax, 0
          .if x != 0
          mov ecx, 1
          .while ecx <= y
          add eax, x
          inc ecx
          .endw
          .endif
          pop ecx
          ret
mult   endp

复制代码

出了过程中明显能从代码中看到一些寄存器会被修改,还有一些指令会导致寄存器数据被修改。

INVOKE指令会导致eax,ecx,edx内容被修改

imul会导致eax,edx被修改。所以可以通过pushad和popad指令保存和恢复这些寄存器的内容

举例

复制代码

blankln   proc
              pushad
              .repeat
              INVOKE printf, ADDR blnkfmt
              dec ebx
              .until ebx <= 0
              popad
              ret
blacnkln  endp    

复制代码

过程的声明都是放在main主程序之后。而且过程更看重的是节约空间。

 

十三 宏

1   宏和过程都可以避免编写同样功能代码,宏的速度比过程快,但是倾向于浪费一些空间达到提高效率的目的。

宏的声明放在main函数之前。

 宏的结构如下:

mname macro
      ; 宏体
      endm

 

 宏的结构和过程的结构不同,宏内部没有ret,并且macro 表示宏的开始 macro前边为宏的名字。

endm为宏的结束,但是endm之前没有宏的名字。另外宏的调用不需要使用call指令,直接写宏的名字即可。

 可以通过查看汇编列表文件看看宏扩展是什么,以及宏被插入到程序段是什么指令,下面为某段代码调用两次swap宏

展开后:

 

88 1D 00000046 表示指令mov ebx , num1,该指令在程序地址为00000000处。

另外需要注意的是宏扩展后,注释也会扩展在指令后边。虽然会占用一定内存,但是注释可以帮助程序人员排除逻辑错误。

如果想使用注释,仅仅在宏过程中可见那么采取;;替代;,这样注释不会出现在宏扩展中,用户可以通过宏的声明看到注释。

调用宏的时候,调用几次宏就在相应的调用位置扩展几次,而过程无论调用多少次,仅仅扩展一次。这就是过程节省空间的

原因。

另外宏内部尽量不要使用寄存器,如果使用寄存器也不需要保存和恢复寄存器内容,这些操作放在调用宏的程序里,因为保存和恢复

操作一则浪费空间,二则会减少宏执行的效率。

2  带参数的宏

swap macro p1, p2
     mov ebx, p1
     xchg ebx, p2
     mov p1, ebx
     endm

p1,p2为宏的参数,可以理解为高级语言宏的两个参数,调用程序调用swap时会将实参替代形参完成宏调用。

调用swap时会用num1替代p1,num2替代p2,

用x替代p1, y替代p2

如果开发人员只传入一个参数怎么办?

比如 swap num1?

这样宏展开就出现问题。可以通过:REQ语句指明参数是必须传入不可缺少的。

swap macro p1:REQ, p2:REQ
     mov ebx,p1
     xchg ebx, p2
     mov  p1, ebx
     endm

 这样要求调用swap必须传入两个参数,缺少参数则提示报错。

考虑这样一个问题,如果调用程序调用swap num1,1怎么办?

xchg ebx,1出错。

另外如果 swap ebx, ebx 怎么办?这种没必要调用swap,虽然调用swap不会出错,

但是造成了空间的浪费。这些问题都可以通过条件汇编解决。

 

十四  条件汇编


条件汇编和条件指令不同,条件汇编控制编译程序,而不是控制程序的执行,

条件汇编根据不同的条件将不同的指令或代码包含到程序中。

条件汇编指令if

if和之前介绍的高级汇编指令.if不一样,if后边的参数为 eq, ne, lt, le, gt, ge, or, and等。

举例:

      if num eq 3

     ;该条件满足将指令插入程序

      endif

除此之外

ifb  判断如果为空,则执行if下边的逻辑。

ifnb 判断如果不为空

ifidn 判断如果相同

ifidni  不区分大小写,如果相同

ifdif  如果不相同

ifdifi  不区分大小写,如果不同

这类指令后边要用<参数形式>

举例:

复制代码

addacc  macro parm
        ifb <parm>
        inc eax
        else
        add eax, parm
        endif
 endm

复制代码

在程序中通过几种方式调用addacc

.lst文件内容如下所示,根据不同条件扩展为不同的宏

 

根据不同的条件生成了不同的机器指令,达到了节约空间和控制编译的目的。

仅仅在一部分地址和机器指令行包含了汇编指令,其余没有机器指令和地址的汇编代码不会

被包含在程序中。

使用条件汇编将之前的swap设计的更完善

复制代码

swap   macro  p1:REQ, p2:REQ
       ifidni <ebx>,<p2>
       xchg p1, ebx
       elseifidni <p1>, <ebx>
       xchg ebx, p2
       else
       mov ebx, p1
       xchg ebx, p2
       mov p1, ebx
       endif
       endm

复制代码

通过条件汇编,使swap功能更健全和高效。

 

十五  过程和宏对比和总结

1    过程在被调用的时候只有一份程序副本出现,而宏在被调用的时候,每一次对宏的调用都会出现一次宏代码的副本。

2    过程通常会保存和恢复寄存器的内容,而宏通常不会去保存和恢复寄存器的内容。

3    过程倾向于节省内存空间,而宏倾向于节省运行时间

4   调用过程的时候,使用call指令,后面跟着过程的名字,而调用宏的时候直接写宏的名字。

5   过程中必须包含ret指令,但是宏中一定不能写ret

6   把过程名字放到endp语句之前的标记字段,但是endm之前的标记字段不需要写宏的名字。

7   如果要求调用宏必须传入参数,可在参数后加:REQ

8   条件编译if在使用or或and逻辑时需要将两个参数用括号括起来,如if(x lt 0)or(y gt 1)

 

https://www.cnblogs.com/secondtonone1/p/6700449.html

恋恋风辰

汇编语言学习笔记(五)

十六、数组

数组的基本表示方法

numary sdword 2,5,7

numary数组中有三个元素,为sdword类型,分别为2,5,7

empary sdword ?, ?,?

empary数组为sdword类型元素,未初始化。

如果数组元素很多可通过

zeroary sdword 100 dup(0)

zeroary数组中有100个0

empary sdword 3 dup(?)

empary 数组中有3个未初始化的sdword类型数据

mov eax, numary+8; 表示把数组numary第3个元素放入eax中

sdword为四字节,numary+0表示numary首地址,也是第一个元素地址,以此类推,numary+8为第三个元素首地址。

mov numary+0, eax; 将eax内容放入数组第一个元素中。

出了采用数组首地址+偏移地址方式,可以采用ebx基址寄存器进行数组索引。

访问numary第三个元素

mov ebx, 8;ebx存储8

mov eax, numary[ebx];访问numary偏移8字节的元素,也就是第三个元素,放入eax中。

举个例子C语言:

sum = 0
for(i = 0; i < 3; i++)
{
  sum +=numary[i];
}

汇编语言:

复制代码

mov sum, 0
mov ecx ,3
mov ebx, 0
.repeat
mov eax, numary[ebx]
add sum, eax
add ebx,4
.untilcxz

复制代码

 

除了使用基址寄存器ebx,还可以使用寄存器esi和寄存器edi进行索引,

esi为源索引寄存器,edi为目的索引寄存器。

第一种方法

mov ebx,4

mov eax,numary[ebx]

第二种方法

mov esi, offset numary+4

mov eax,[esi]

第二种方法先将numary+4的地址放入esi中,

然后[esi]取出esi指向的地址空间的数据,也就是numary+4地址空间里的数据

将数据放入eax中。

两种方法的效果图:

 

 

除了上述两种方法,还有第三种方法

lea esi, memory+4

mov eax, [esi]

lea 和offset的区别:

offset 是在编译时将地址写入esi

lea是动态写入,每次运行时将地址写入esi中。

去实现如下代码:

复制代码

j=n-1;
for(i=0; i < n/2; i++)
{
    temp=numary[i];
    numary[i] = numary[j];
    numary[j] = temp;
    j--;
}

复制代码

通过汇编实现:

复制代码

mov ecx,n
sar ecx,1
lea esi,numary+0
mov edi, esi
mov  eax,n
dec eax
sal eax,2
add edi, eax
.repeat
mov eax, [esi]
xchg eax, [edi]
mov [esi], eax
add esi,4
sub edi,4
.untilcxz

复制代码

 

数组还有两个指令,lengthof表示数组元素的个数

sizeof表示数组总共占用多少字节。

前面的代码可以通过这两个指令修改

复制代码

mov ecx, lengthof numary
sar ecx,1
lea esi, numary+0
mov edi, esi
mov eax, sizeof numary
sub eax,4
add edi, eax
.repeat
mov eax,[esi]
xchg eax, [edi]
mov [esi], eax
add esi, 4
sub edi,4
.untilcxz

复制代码

 

 

十七、数组总结

1 esi 为源索引寄存器,主要用于从esi指向地址空间取出数据

2 edi为目的索引寄存器,主要用于向edi指向地址空间写入数据

3 esi和edi存储的为地址,[esi]和[edi]为他们指向的地址空间存储的数据

4 可以通过mov edi, esi将esi寄存器存储的地址放入edi中,因为两个操作数都是寄存器

5 不可以使用mov [edi],[esi];因为两个操作数都为内存,这是汇编指令mov不允许的。

6 寄存器ebx为基址寄存器,可通过   数组名[ebx]取出数组首地址偏移ebx存储的字节数的元素。

7 offset操作符的mov指令,如mov eax, offset sumary+4 是将sumary首地址偏移4字节地址写入eax,

  此时eax存储的是第二个元素首地址,他是静态的获取地址,而lea是动态的获取地址

8 lengthof用于计算数组元素数量,sizeof用于计算数组总共占用多少字节。

 

数组的介绍到此为止,下一篇是字符串的介绍

 

恋恋风辰

https://www.cnblogs.com/secondtonone1/p/6710407.html

汇编语言学习笔记(六)

十八、字符串处理

前文介绍过字符串的处理,字符串是byte类型 的数组,现在实现一段代码,将字符串string1数据copy到字符串string2中

代码如下

复制代码

    .data
string1 byte "Hello World!", 0
string2 byte 12 dup(?), 0
    .code
mov ecx, 12
mov ebx,0
.repeat
mov al, string1[ebx]
mov string2[ebx], al
inc ebx
.untilcxz

复制代码

通过ecx递减,将字符串string1每个字符一次copy给string2中,其中用到了ebx基址寄存器。

 也可以通过esi和edi寄存器

复制代码

    .data
string1 byte "Hello World!"
stirng2 byte 12 dup(?), 0
    .code
mov ecx,12
lea esi, string1
lea edi, string2
.repeat
mov al,[esi]
mov [edi],al
inc edi
inc esi
.untilcxz

复制代码

 

这些代码可以通过字符串操作的其他指令替代,使代码更简洁

1 movsb指令

movsb是字符串移动指令,将esi指向的字符移动到edi指向的空间,并且将ecx减1,寄存器esi和edi内容增加1或者减少1

如果仅仅使用movsb指令作用不大,配合循环使用,功能很强大。cld指令表示方向标志值清0,调用movsb会使esi和edi内容增加1

std指令表示设置方向标志值,调用movsb会使esi和edi内容减少1

还是上面的需求,这次使用movsb完成目标

复制代码

mov ecx,12                ; 字符串大小为12
mov esi, offset string1   ; esi指向string1起始位置
mov edi, offset string2   ; edi指向string2起始位置
cld                       ; cld指令调用后 ,调用movsb会使esi和edi分别加1
.repeat
 movsb                    ; 先将esi指向的空间数据拷贝到edi指向的空间
                          ; 然后esi和edi分别加1,并且ecx值减少1
.untilcxz

复制代码

 

和movsb配合使用的几个前缀:

rep   重复操作,等于循环,直到ecx为0退出循环

repe  如果相等,则重复操作,如果不等,那么退出循环,或者ecx为0退出循环

repne  不相等,则重复操作,相等则退出循环,或者ecx为0退出循环

上面的代码可以使用 rep movsb更改,完成同样的目的

mov ecx, 12
lea esi, string1
lea edi, string2
cld
rep movsb   ;循环直到ecx为0结束

 

 

2  scasb指令

 

scasb指令用于在edi寄存器指定的空间中搜寻al所存储的字符。scasb指令操作流程为:将要查询的字符放入al寄存器中,

如果要在字符串string中查找匹配的字符,那么将edi存储string的首地址,调用scasb进行匹配,scasb指令调用后会将edi

存储数值加1,并且ecx值-1,为了完成字符串string中逐个字符匹配,需要在scasb前面加上repne指令,这样在不相等时继续

查找,直到查找到指定字符退出循环。此时edi指向的位置为字符串string中匹配字符位置的下一个位置。

举例实现:

复制代码

.data
name1  byte "Abe Lincoln"
.code
mov al, ' '                ; 在al寄存器中存储空格,为了在name1中查找该空格
mov ecx, lengthof name1    ;ecx存储name1字符串长度,为11
lea edi, name1             ;edi指向name1首地址
repne scasb                ;匹配edi指向空间的字符和空格是否相等,不相等则edi加1
                           ;ecx减1,直到相等或者ecx为0退出循环

复制代码

 

运行后ecx为7,edi执行字符L的位置

效果图如下:

 

 

3  stosb指令

stosb用于把寄存器al存储的数据放入寄存器edi所指向的空间,指令完成后edi增加1

stosb等价于:

mov [edi],al

inc edi

4  lodsb指令

lodsb用于把寄存器esi所指向的空间数据存储到al寄存器中,指令完成后esi增加1

lodsb等价于:

mov al, [esi]

inc esi

下面实现将字符串姓名倒置功能,并将名字和姓中间添加字符“ ”和“,”

如“Abe Lincoln”变为“Lincoln, Abe”

 

复制代码

    .data
name1 byte "Abe Lincoln"
name2 byte 12 dup (?)
    .code
mov al, ''
mov ecx, lengthof name1
lea edi, name1
repne scasb
push ecx
mov esi, edi
lea edi, name2
rep movsb
mov al, ' '
stosb 
mov al, ' '
stosb
mov ecx, lengthof name2
pop eax
sub ecx, eax
dec ecx
lea esi , name1
rep movsb

复制代码

 

5  cmpsb指令

字符串比较指令,该指令和movsb差不多,比较edi和esi指向空间数据大小,并且edi和esi增加1或者减少1,

可通过cld或std设置,并且ecx减少1

比较“James” 和 “James”

代码如下:

复制代码

.data
name1 byte "James"
name2 byte "James"
.code
mov ecx, lengthof name1
lea esi, name1
lea edi, name2
cld
repe cmpsb

复制代码

相等情况下ecx为0,edi和esi都指向字符串最后一个字符的下一个位置

如果比较的字符为“James”和“Jamie”,那么效果如下:

如果比较字符为“Marci”和“Marcy”,那么上述代码就没办法区分是否相等了,因为此时ecx也为0

如果ecx大于0,那么两个字符串肯定不相等的,如果ecx为0,那么将esi和edi都-1,得到最后一个元素,

再将esi和edi指向空间的数据比较即可。

下面将上述代码完善一下,比较两个长度相等的字符串name1和name2

复制代码

mov ecx, lengthof name1
lea esi, name1
lea edi, name2
cld 
repe cmpsb
.if ecx > 0
;输出不相等
.else 
  dec esi
  dec edi
  mov al, [esi]
  .if al != [edi]
   ; 输出不相等
  .else
    ;输出相等
  .endif
.endif

复制代码

 

十九、字符串总结

1  movsb 指令将寄存器esi指向的字节类型字符串的内容移动到寄存器edi指向的位置,通过cld或std设置esi和edi增加还是减少

2  cmpsb 指令对寄存器的esi指向的字符串中的一个字符和edi所指向的一个字符进行比较,通过cld或std设置esi和edi增加还是减少

3  movsb指令前边的rep前缀会让该指令重复执行,循环次数等于ecx的值,每一次循环ecx的值减1,直到为0停止。

4  cmpsb指令前面的repe前缀的作用类似于rep前缀,当ecx为0是,停止执行指令。或者当esi和edi所指向的两个空间的字节内容不同,循环也会停止。

    repne当esi和edi所指向的两个空间的字节内容相同时,停止。

5  scasb指令将一个字符串进行搜索,查找字符串中是否存在寄存器al中所存储的字符。如果找到该字符,那么edi指向的地址比该字符的地址靠后一个字节。

    stosb将al寄存器的内容存储在edi所指向的字符串的位置中。lodsb指令将把寄存器esi所指向的字符串的位置中的字符复制到al寄存器中。

 

我的微信公众号,谢谢关注:

 

恋恋风辰

https://www.cnblogs.com/secondtonone1/p/6729440.html

汇编基础最后一篇--机器语言指令

这是汇编语言基础最后一篇,以后还会更新更高级的汇编知识,并且这部分知识会应用到

逆向编程的环节,这一章介绍汇编基础--机器指令。

一个16比特位的汇编指令:

opcode操作码占用3个比特位,可以表示2的3次方为8种操作

寄存器占用2个比特位,可表示2的2次方为4种可用寄存器

地址空间为2的11次方为2048个可能的内存单元地址可用。

 

inc 指令和dec指令

 

 

从图中可以看出这些指令长度仅为一字节,因为每个指令地址相差为1字节。

inc eax 这条指令的机器码为40,  40为16进制表示,转为二进制为 0100 0000

inc ecx 这条指令机器码为41,41位16进制表示,转换为二进制为0100 0001

依次展开会发现机器码的规律,开头都为01000XXX

而XXX实际就是寄存器在机器中表示的二进制机器码。

下面是各个寄存器的机器码:

 

下面看下dec 寄存器指令图:

 

 

 

dec 寄存器指令的格式可以总结为

01001xxx, xxx为寄存器二进制指令格式。

 

 mov 指令

mov eax, reg指令图

 

每个指令长度为2字节,从地址偏移可以看出。

将上面指令机器码转为二进制

 

mov ecx, eax  十六进制为8B C8,二进制表示第二个字节 1100 1000

mov ecx, ecx  十六进制为 8B C9,二进制表示第二个字节为 1100 1001

通过对比二进制,发现第一个字节都为8B,第二个字节分别为 C0,C1,C2,C3...C9

mov reg, reg 指令格式为

10001011  11XXXYYY

XXX为目的寄存器, YYY为源寄存器

 

mov reg, imm 即将一个立即数移动到寄存器中指令的机器码会是什么样呢?

下图为将立即数移动到寄存器的图示:

 

mov eax, 1 指令机器码为 B8 00000001

mov ecx, 10 指令机器码为 B9 0000000A 

两条指令地址相差5个字节,每个字节8bit,可计算出每条指令为40bit长度。

即10个十六进制数表示。而 B8 00000001 和 B9 0000000A 恰巧为10个16进制数字组成。

机器码B8 00000001 从左向右数,去掉B8占用的一个字节,剩下的四个字节可以看出用来表示

立即数 1。同样的道理,可以看出 B9 0000000A也是这个原理。

如果移动的为负数,怎么表示呢?

 

mov edx, -1 这个指令我们分析一下 -1在机器中的表现形式

负数在机器中以补码的形式表现,-1 的补码计算规则为:

1的源码为 0000 0000 0000 0000 0000  0000 0000 0001

按位取反 为 1111 1111 1111 1111 1111 1111 1111 1110

末尾+1 位 1111 1111 1111 1111 1111 1111 1111 1111

转换为十六进制为 F F F F F F F F 恰好就是 机器码的最后三个字节表示。

同样的道理适用于mov ebx, -10

下面分析前两个字节 BA, BB, B8, B9 分别有什么关联。

 

 

 

 

同步对比可以看出 前几个比特位是一样的,都为 10111 

后三个比特位分别为 000,  001, 010, 011,这四个二进制码恰好为

几个寄存器的二进制表示方法。所以

mov reg, imm 机器指令为

10111XXX  YYYYYYYY  YYYYYYYY YYYYYYYY YYYYYYYY

 

mov reg, mem 将内存数据移动到寄存器中 的机器指令怎么表示?

mov mem, reg   将寄存器中的数据移动到内存中,机器指令如何表示?

 

下图定义了变量num1, num2, num3

num1 地址为 0000 0000

num2 地址为 0000 0004

num3 地址为 0000 0008

下图为指令对应的机器码:

除去A1,A3开始的一个字节,剩下的4个字节分别为 十六进制4 和十六进制8,分别为num2的地址

和num3 的地址。

下面分析第一个字节A1和A3 规律:

可以得出结论,无论将内存数据移动到eax中,还是将eax中的数据移动到内存中,

最后的4个字节表示的都是内存的地址,第一个字节表示的不同,用来表示两种移动方式的区别。

总结规律如下:

 

mov eax, mem 10100001 YYYYYYYY  YYYYYYYY  YYYYYYYY  YYYYYYYY

mov mem , eax 10100011 YYYYYYYY  YYYYYYYY  YYYYYYYY  YYYYYYYY

 

 下面图表表示了ebx,ecx,edx三种寄存器和内存数据移动指令

可以看出其他寄存器(eax, ecx, edx)和内存之间移动数据的操作指令大小为6字节,多出的为第二字节,

0D,15,1D。

第一字节8B表示从内存移动数据到寄存器,89表示从寄存器移动到内存,如下图所示:

 

第二个字节图表如下:

 

 

通过二进制可以看出 前两位都为 00, 中间三位为 001 ,010, 011 分别表示ecx, edx, ebx

最后三位为101, 其实这个字节不仅仅用于表示移动,还可以表示很多操作,因为ecx为循环控制,

ebx为基址寄存器, edx可用于存余数等等,所以 前两位为00,且最后三位为101,这个组合表示移动

操作。中间三位表示操作的寄存器是什么。

该字节概括为如下图所示:

mod 字段为00, 且r/m字段值为101,它表示地址模式数据置换,也就是指向内存地址模式。

 

add 指令 sub 指令

 add指令很简单,给出图表读者自己分析。

字节转为二进制可以看出从右往左数第3到1位为第二个操作的寄存器,

从右往左数第6到4位表示第一个操作的寄存器, 两个寄存器操作模式为 第7~16位所表示。

 

movoffset 指令  lea 指令

 

num2 的值为5, 地址为 00000004, 分别将num2 的内容移动到esi和edi,再通过lea指令将num2地址

放入esi和edi

 

通过对比可以看出后四个字节都为 0000 0004 , 但是前两个字节是不一样的。将前两个字节展开

为二进制

由于mov esi, num2 是将num2数据存入esi,而 lea esi, num2 是将num2地址放入esi,所以

第一个字节的倒数第二位不同,第一个字节分别为10001011  , 10001101,mov和lea第二个字节是相同的。

下面对比两个lea指令前两个字节  ,第一个字节是相同的,第二个字节为 00 110 101  和 00 111 101

第二个字节中间三位不同,分别为110(esi), 111(edi)表示寄存器。

所以可以总结一下, mov 指令和 lea指令区别在于第一个字节,计算机用第一个字节区别mov和lea指令。

计算机用第二个字节中间三位区别lea指令操作的不同寄存器。

第二个字节和我们上面说过的:

 

 

mod 字段为00, 且r/m字段值为101,它表示地址模式数据置换,也就是指向内存地址模式。

 

下面看一下 mov esi, offset mem 和 mov edi, offset mem两条指令。

 

 

可以看出mov offset指令为5个字节,比 mov 和lea指令少了一个字节,因为mov offset仅仅在编译的时候加载地址,

所以不需要lea的第二个字节表示 数据移动操作。 mov offset是静态的。

mov offset 指令esi和 edi区别仅仅在第一个字节,展开后可以看到:

 

第一个字节的后三位 分别为 110(esi), 111(edi)。

可以得出结论mov offset 的指令第一个字节后三位区别esi还是edi,其余不变。

 

jmp指令

看一则jmp指令操作

 

 

jmp 指令 机器码为EB + 相对偏移地址 ,

如 jmp  around 为EB 04 ,通知计算机跳转到当前指令指针位置+4字节的位置,

需要普及一个知识,当程序运行的时候,指令指针或者CPU中的指令指针指向下一条将要取到CPU中

被后续执行的指令。当运行到 jump around时,指令指针实际指向了 地址000000D8,指令 above:nop的位置,

EB 04指向 为 000 000 DB 加上4个字节地址即为 000 000 DC, 恰好是 around:nop指令地址。

符合逻辑。

下面看下 jump above指令会跳转到哪里。

EB FC 指令 FC 为 1111 1100 , 该数值为某个负数的补码,负数补码的计算规则为

符号位不变,其他位按位取反末位+1。

同样的道理,负数补码转为原码,符号位不变,按位取反末位+1

1111 1100 符号位不变,按位取反 1000 0011, 末位+1,

变为 1000 0100 表示-4.

jmp above 机器指令 EB FC 跳转到 指令指针地址向前移动四个字节的位置。

jmp above 指令运行时,指令指针指向下一条将要取出的指令位置,即 000 000 DC,

000 000 DC - 4 为 000 000 08,即 above:nop 的位置。

 

到此为止机器指令的知识介绍完毕,以后会介绍高级汇编和反汇编的知识,

汇编基础介绍告一段落。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值