吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9289|回复: 13
收起左侧

[其他转载] 汇编小白---解决一些常用汇编指令问题---简单通俗易懂

  [复制链接]
MuFen 发表于 2019-3-15 13:05
本帖最后由 MuFen 于 2019-4-30 15:18 编辑

下面将用一个简单的例子来讲解,保证百分百通俗易懂;


例子比较简单,对于小白,每一个错误都是一笔财富,在没有讲解之前,你大致看一下就会发现你还是非常地富裕...


不知道如何搞出源程序的汇编代码的童鞋可以先去看我的上一篇帖子:[https://www.52pojie.cn/thread-896166-1-1.html]

想下载本篇源码继续研究的也可以下载:---附件下载---


统一解释:H表示该数为16进制数,不知道的先把这句话背诵几万遍...,例如:88H就表示一个16进制数88,FDH就表示一个16进制数FD,如果还想知道FD是啥鬼东西的人,我建议你还是别看下面了,先学几天计算机再来看,因为我发现你除了会拆电脑,啥都不会...,还想来学汇编?当然我以后还是会补充这一部分内容的,因为对只会拆电脑的你,我脚的你也不知道我到底在说啥,其实说了一大堆,我也不知道我在说啥...(这就very尴尬辽)

main:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:        0c c0               rjmp        .+24             ; 0x1a <__ctors_end>
   2:        1f c0               rjmp        .+62             ; 0x42 <__bad_interrupt>
   4:        1e c0               rjmp        .+60             ; 0x42 <__bad_interrupt>
   6:        1d c0               rjmp        .+58             ; 0x42 <__bad_interrupt>
   8:        1c c0               rjmp        .+56             ; 0x42 <__bad_interrupt>
   a:        1b c0               rjmp        .+54             ; 0x42 <__bad_interrupt>
   c:        1a c0               rjmp        .+52             ; 0x42 <__bad_interrupt>
   e:        19 c0               rjmp        .+50             ; 0x42 <__bad_interrupt>
  10:        18 c0               rjmp        .+48             ; 0x42 <__bad_interrupt>
  12:        17 c0               rjmp        .+46             ; 0x42 <__bad_interrupt>
  14:        16 c0               rjmp        .+44             ; 0x42 <__bad_interrupt>
  16:        15 c0               rjmp        .+42             ; 0x42 <__bad_interrupt>
  18:        14 c0               rjmp        .+40             ; 0x42 <__bad_interrupt>

0000001a <__ctors_end>:
  1a:        11 24               eor        r1, r1
  1c:        1f be               out        0x3f, r1        ; 63
  1e:        cf e5               ldi        r28, 0x5F        ; 95
  20:        d2 e0               ldi        r29, 0x02        ; 2
  22:        de bf               out        0x3e, r29        ; 62
  24:        cd bf               out        0x3d, r28        ; 61

00000026 <__do_copy_data>:
  26:        10 e0               ldi        r17, 0x00        ; 0
  28:        a0 e6               ldi        r26, 0x60        ; 96
  2a:        b0 e0               ldi        r27, 0x00        ; 0
  2c:        ea e9               ldi        r30, 0x9A        ; 154
  2e:        f0 e0               ldi        r31, 0x00        ; 0
  30:        03 c0               rjmp        .+6              ; 0x38 <.do_copy_data_start>

00000032 <.do_copy_data_loop>:
  32:        c8 95               lpm
  34:        31 96               adiw        r30, 0x01        ; 1
  36:        0d 92               st        X+, r0

00000038 <.do_copy_data_start>:
  38:        aa 36               cpi        r26, 0x6A        ; 106
  3a:        b1 07               cpc        r27, r17
  3c:        d1 f7               brne        .-12             ; 0x32 <.do_copy_data_loop>
  3e:        0f d0               rcall        .+30             ; 0x5e <main>
  40:        2a c0               rjmp        .+84             ; 0x96 <_exit>

00000042 <__bad_interrupt>:
  42:        de cf               rjmp        .-68             ; 0x0 <__vectors>

//从这里开始,往下讲解,有兴趣的可以仔细研究一下在这之前的东西

00000044 <funA>:
int a = 268, b = 78;
char s[2] = {26, 66};
int k[2] = {280, 36};

要想了解其值如何变化,我们还是要先把这些变量赋值语句来搞定,我们用一张表简单的弄清楚这些东西:


地址16进制变量备注
0x00600ca的低位a=12
0x006101a的高位a=256
0x00624eb的低位b=78
0x006300b的高位b=0
0x00641as[0]s[0]=26
0x006542s[1]s[1]=66
0x006618k[0]低位k[0]=24
0x006701k[0]高位k[0]=256
0x006824k[1]低位k[1]=36
0x006900k[1]高位k[1]=0


看不懂上面这张表的童鞋可以留言,我会补充讲解;

int funA(int x, int y)
{
  44:        68 0f               add        r22, r24
//ADD是普通的加法指令,双字数据长度。
  46:        79 1f               adc        r23, r25
//ADC是带进位的加法指令,把值分割为多个双字数据元素,并且对每个元素执行独立的加法操作。
return x+y;
}

//MOV指令是数据传送指令,为方便理解,下面给出一些例子
//MOV AX,2000H;将16位数据2000H传送到AX寄存器
//MOV AL,20H;将8位数据20H传送到AL寄存器
//MOV AX,BX;将BX寄存器的16位数据传送到AX寄存器
//MOV AL,[2000H];将2000H单元的内容传送到AL寄存器


  48:        86 2f               mov        r24, r22
  4a:        97 2f               mov        r25, r23
  4c:        08 95               ret

//RET指令是子程序的最后一条指令,即恢复断点,返回主程序。 没有要求RET指令非要和哪一条指令要配对使用。
//RET是子程序返回指令,放在子程序的结尾,当子程序执行完后,靠该指令返回主程序。

//单独重点讲解一下过程:
//44:        68 0f add        r22, r24 ;这句r22和r24是寄存器,add是汇编指令,68 0f 是汇编后的机器码,其中r22是存储x低位的寄存器,r24是存储y低位的寄存器;这句话的意义是:r22和r24相加,结果存储在r22中
//46:        79 1f adc        r23, r25 ;这句r23和r25是寄存器,adc是汇编指令,79 1f 是汇编后的机器码,其中r23是存储x高位的寄存器,r25是存储y高位的寄存器;;这句话的意义是:r23和r25相加,结果存储在r23中
//上面两句是为了实现:X+Y
//下面是为了实现return
//48:        86 2f mov        r24, r22 ;这句r22和r24是寄存器,mov是汇编指令,86 2f 是汇编后的机器码;这句话的意义是:将r22寄存器的16位数据传送到r24寄存器
//4a:        97 2f mov        r25, r23 ;这句r23和r25是寄存器,mov是汇编指令,97 2f 是汇编后的机器码;这句话的意义是:将r23寄存器的16位数据传送到r25寄存器
//4c:        08 95 ret ;这句的解释稍微往上翻一下,你就看到了
//这里有人有疑问了,为什么这里的寄存器用的是r22-r25而不用别的,this why在main函数里面对funA(x,y)传递参数时,用的是r22-r25,main函数中调用funA()函数的语句是:d = funA(b, d); ,你往后翻,然后看d = funA(b, d); 下面的汇编语句,你就会发现b所用的寄存器就是这里x用的寄存器,d和y亦如此。
//还有人会问,为什么return的是r24和r25而不是r22和r23,因为这是AVR的约定,好似你不能把你老爸叫妈妈,不能把你老妈叫爸爸的约定(也可以说是规定)
//还有人会说,既然ADD AX,BX执行的是将BX寄存器的16位数据传送到AX寄存器,我们在return的时候却返回了BX,为什么不直接用ADD BX,AX就可以省去MOV  BX,AX的过程,如果你能想到这个问题,那么你一定是个智商距离250差不远的童鞋了,其实按照你这样做也是对的,但是AVR编译器却不是这么做的,那么当你以后自己写编译器的时候你大可这样优化你的编译过程。


0000004e <funB>:

int funB(int x, int y)
{
if(x > y)
  4e:        68 17               cp        r22, r24
//cp:比较,无进位
  50:        79 07               cpc        r23, r25
//cpc:考虑进位
  52:        14 f0               brlt        .+4              ; 0x58 <funB+0xa>
//brlt:若if条件为假则顺序执行,为真则跳转,根据解释0x58 <funB+0xa> ,这里我们可以看到,若跳转则跳转到了0x58位置
  54:        60 e0               ldi        r22, 0x00        ; 0
  56:        71 e0               ldi        r23, 0x01        ; 1

  //ldi 代码意义:装入一个8位立即数到指定寄存器16~31
  //下面结合另一段汇编加注释
  //ldi r31,-0x62 ;将16进制数62H的
补码装入寄存器r31
  //这里-0x62是一个带符号数,在汇编语言中是这个数的补码。
  //正数的补码就是它自身,负数补码就是将原码取反后+1。
  //0x62=0b01100010(原码)
  //-0x62=0b10011110=0x9e(补码)
  //综上所述执行这一段代码后,寄存器r31被装入0x9e。


  return y;
else  
  return 256;
}
  58:        86 2f               mov        r24, r22
  5a:        97 2f               mov        r25, r23
  5c:        08 95               ret

0000005e <main>:

int main(void)
{
int d = 326;

k[1] = 20;
  5e:        84 e1               ldi        r24, 0x14        ; 20
//上面这句的意思是将16进制数14H的补码装入寄存器r24
//正数的补码就是它自身,寄存器r24被装入0x14

  60:        90 e0               ldi        r25, 0x00        ; 0
  62:        90 93 69 00         sts        0x0069, r25
  66:        80 93 68 00         sts        0x0068, r24
//sts:直接存储到数据空间
a = k[0];
  6a:        80 91 66 00         lds        r24, 0x0066
  6e:        90 91 67 00         lds        r25, 0x0067
  72:        90 93 61 00         sts        0x0061, r25
  76:        80 93 60 00         sts        0x0060, r24

//这里重点讲一下传参(传递参数)的神奇过程:
//a = k[0];的意思是把k[0]的值赋值给a,
//6a:        80 91 66 00 lds        r24, 0x0066,这句中0x0066是k[0]的低位地址,r24是一个寄存器,lds是汇编指令,80 91 66 00 是汇编后的机器码;这句的意义是:将0x0066装入寄存器r24,也可以理解为将k[0]的低位地址装入寄存器r24(虽然并不能这么理解,但是暂时你就勉强这么理解吧!)
//6e:        90 91 67 00 lds        r25, 0x0067,这句中0x0067是k[0]的高位地址,r25是一个寄存器,lds是汇编指令,90 91 67 00 是汇编后的机器码;这句的意义是:将0x0067装入寄存器r25,也可以理解为将k[0]的高位地址装入寄存器r25(虽然并不能这么理解,但是暂时你就勉强这么理解吧!)
//72:        90 93 61 00 sts        0x0061, r25,这句中0x0061是a的高位地址,r25是一个寄存器,sts是汇编指令,90 63 61 00 是汇编后的机器码;这句话的意义是:将r25直接存储到0x0061这个地址
//76:        80 93 60 00 sts        0x0060, r24,这句中0x0060是a的低位地址,r24是一个寄存器,sts是汇编指令,80 93 60 00 是汇编后的机器码;这句话的意义是:将r24直接存储到0x0060这个地址
//大致的过程就是酱紫:k[0]的地址传给某个特定的寄存器,这个寄存器再存储到a的地址,过程就是这么个过程,道理就是这么个道理...

d = funA(b, d);
  7a:        80 91 62 00         lds        r24, 0x0062//低位
  7e:        90 91 63 00         lds        r25, 0x0063//高位
//lds:这条指令的功能是把后面指向的地址,高位存放在DS中,低位存放在reg中.
  82:        66 e4               ldi        r22, 0x46        ; 70
  84:        71 e0               ldi        r23, 0x01        ; 1
//这里我们会看到一个问题:有些汇编指令后面是8位2进制操作数例如 ldi r23, 0x01中的0x01,而有些是16位2进制操作数,如 lds   r25, 0x0063中的0x0063,首先这里我们要先明白, 7a:  80 91 62 00   lds    r24, 0x0062 和7e:   90 91 63 00     lds     r25, 0x0063是对参数b的操作,而  82:        66 e4               ldi        r22, 0x46  和 84:        71 e0               ldi        r23, 0x01   是对d进行操作,仔细观察,我们会发现b是全局变量,d是局部变量。
  86:        de df               rcall        .-68             ; 0x44 <funA>

  //RCALL指令属于ROM空间的相对寻址范畴,CALL属于ROM空间的直接寻址范畴
  //相对寻址,操作数是地址的偏移量;这时还要加上一个固定地址,才能得到绝对地址
  //绝对寻址,操作数是绝对地址。

//上述rcall便是如下操作:.-68为偏移量,这里的68是十进制数,68的十六进制表示为44H,这里的固定地址为88H,为什么为88H呢?我们看一下rcall前面的机器码de为86H,机器码df为87H,那么当df解析完之后,下一位就是88H了。

  88:        28 2f               mov        r18, r24
  8a:        39 2f               mov        r19, r25
  8c:        2d 5f               subi        r18, 0xFD        ; 253
//subi:subi rd k;操作:rd<— rd-k,
  8e:        3f 4f               sbci        r19, 0xFF        ; 255
//sbci:sbci rd k;操作:rd<— rd-k-c,c为进位
d += 3;

//上面的sub的原始解释为减法,而我们想要的是d=d+3,明明是加法,为什么要用减法汇编指令来执行呢?我可以很明确的告诉你:这鸟玩意儿我也不知道...,但是我们或许可以了解一下它是怎么把加法操作变成减法汇编的,仔细观察,你会发现,3跟253有这某种内在的联系,253+3刚好等于256,256为16的二次方,那么它怎么来的我们就能理解了,他把+3变成了-3的补码即-256(如果需要我再讲解一下原码、补码、反码等尼玛,可以留言,下一篇会补充)
//有人会问 [subi r18 0xFD] 中的FD是啥玩意儿,你猜一下这是啥玩意,熟悉10进制和16进制之间转换的人,都知道FDH是10进制的253,我就不告诉你这是啥玩意儿了。

return d;
}
  90:        82 2f               mov        r24, r18
  92:        93 2f               mov        r25, r19
  94:        08 95               ret

00000096 <_exit>:
  96:        f8 94               cli

00000098 <__stop_program>:
  98:        ff cf               rjmp        .-2              ; 0x98 <__stop_program>



选择在这里发帖的最重要的一个原因是:这里大佬多,小学生和喷子老哥比较少;
1234.jpg

main_S.txt

3.51 KB, 下载次数: 34, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 3吾爱币 +6 热心值 +3 收起 理由
Zeno___Lee + 2 + 1 感谢感谢感谢,重要的事情说三次
ws100200111 + 1 + 1 热心回复!
wushaominkk + 3 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

bachelor66 发表于 2019-3-15 13:39
虽然看不懂,但跟着尝试慢慢学。                             
 楼主| MuFen 发表于 2019-3-15 13:33
Reynard 发表于 2019-3-15 13:20
这方面不是很懂 帮顶一下 让大佬来

认真看一下,我觉得我还是说的很容易理解的
lichao521 发表于 2019-3-15 13:15
御花猫 发表于 2019-3-15 13:16
感谢分享,慢慢学习。
davidmao 发表于 2019-3-15 13:19
跟着学习。。。。
Reynard 发表于 2019-3-15 13:20
这方面不是很懂 帮顶一下 让大佬来
Reynard 发表于 2019-3-15 13:37
MuFen 发表于 2019-3-15 13:33
认真看一下,我觉得我还是说的很容易理解的

主要是我太菜了,不是大佬的关系……
明星家电维修部 发表于 2019-3-22 20:18
学习资料
seedhk 发表于 2019-8-29 14:26
感觉硬生生的搞复杂了。。。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-26 01:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表