zoukankan      html  css  js  c++  java
  • 编译原理入门以及战大作业心得(2)汇编简易入门 康某

    应刁哥多年前的约稿,外加结合编译原理系列连载,整理一下上一篇文章,写一个简易的汇编教程……
    尽管谬误,疏漏若干,但是本文的目的不在于培养学霸,而在于让不会汇编的人简单看看就能写出个基本能用的汇编程序……
     
    在程序设计中,我们知道一项基本原理:语言越高级,一般来讲容易编写,形式简洁,可移植性强,效率低……
    反过来,语言越低级,越难编写,代码越容易是又臭又长还看不懂,越难移植,但是效率会很高……
    汇编就是比较低级的语言了,由于其不太好移植,汇编版本又多种多样
    因此,本着活学活用,到时候不会就查手册的原则,本文不会太着重强调语法,语法以intel语法为主……
     
    Chapter1:一些基本知识
    首先,CPU有若干寄存器,在32位CPU中,形如eax,ebx,ecx,edx……64位CPU则为rax,rbx……
    虽然寄存器这个名字很NB,其实它的本质上就是int,为了向下兼容的需要,一些老的程序,也有可能能在新型的CPU上运行(详见DOS老游戏,有的能玩,有的不能玩)
    以64位举例:
    64位寄存器rax,低32位是eax,eax低16位是ax,ax高8位叫ah,低8位叫al……
    CPU只能直接对寄存器进行运算……因为电路结构就是那样的……因此,所有的操作,都需要经过寄存器
    譬如赋值:A = 1,实际上是 AX = 1; A = AX; 譬如 A = A + B,应该是 AX = A; AX += B; A = AX; 这样
    我是比较不求甚解的,99%功能(运算,判断,指针)等,使用eax,ebx,ecx,edx都能搞定……于是咱们也就不说别的了……
     
    Chapter2:汇编的基本语法:
    汇编语言大概分为四个段,其中比较需要记住的就是代码段和数据段……
    用类似高级语言的思路来讲,数据段用于定义一些全局变量,代码段用于写程序……
    来看一个简单的 Hello World(环境:yasm,64位)
     
    global main 
    extern printf 
     
    section .data 
    ctrlout   db      '%s', 10, 0 
    hw    db      'Hello World', 0 
     
    ; this is a comment
     
    section .text
    main:
    movrax, 0
    movrdi, ctrlout
    movrsi, hw
    callprintf
            mov rax, 0
    ret
     
    其中,刚开始global main 表示程序入口,由于这里直接调用了Linux的函数printf,之后需要gcc编译,所以叫做main,如果只使用中断输出的话,可以搞成_start
    之后extern意义和C++类似,表示这个函数在别处,叫编译器以为他有就行……
     
    section .data  表示数据段,后面是一些定义,以及初始化
    注意这里定义的东西类型都像是指针一样,ctrlout,hw 实际上是地址……
    初始化必须搞的干净一点,有一次调了半天不对,就是因为一个64位int,前4位没有初始化成0……
     
    之后 section .text 表示代码段,这里调用C语言函数,网上说,64位汇编中,参数传递方式有了修改,大概是有4~5个寄存器专门保存参数,如果参数过多,再放入栈,rax存放栈中的参数个数……我们想printf("%s","Hello World"),就要调用两个参数,于是,我们把第一个参数,字符串ctrlout的指针mov进rdi,hw mov进rsi,rax赋值为0 ,然后,调用……
    (实现时,需要手册自行查阅一下本机怎么搞)
    函数的返回值在rax,于是return 0
     
    然后 yasm -f elf64 XXX.asm
    gcc -o XXX XXX.o
    ./XXX
    一个崭新的hello world 出现了……
     
     
    Chapter3:汇编的一些简单指令:
     
    由于这是速成教程,选取一部分指令……要记住,CPU只能直接对寄存器进行运算……
    mov:相当于赋值, mov eax,b  相当于eax = b;
    注意:mov 后面的两个东西至少要有一个是寄存器……
    同时,和高级语言一样,不能搞什么 mov 10,eax……
     
    add:add eax,b 相当于eax+=b
    sub:基本同上……
    mul || imul:前面是无符号的乘,后面是有符号的乘,默认被乘数在AX,于是指令形如:mul BX,如果有溢出,则高位溢出到DX,记得备份DX的东西……
    div  ||  idiv:前面是无符号整除,后面是有符号整除,默认被除数是DX(高16位)和AX(低16位),因此,除法之前记得把DX清零,否则数会不对……
    之后商在AX,余数在DX
    指令形如:div BX
     
    push/pop:每个程序都有栈,或者是在程序中定义堆栈段,或者使用默认栈
    push eax,表示把eax放进栈里,pop ebx,表示取出栈顶,放在ebx中……
    push和pop异常重要,一个重要作用就是保护寄存器,譬如DX中有内容,但是现在要做乘法,没准会破坏DX(见上),于是,先PUSH DX,然后,乘法,然后POP DX,又好比你要调用一个函数,但是寄存器里有有用的信息,不保证这个函数不会破坏,于是,把所有寄存器先push进去,运行函数,然后再pop出来,这是常用技巧……
    另外也可以用来给函数传参数,传时倒序压入,用时顺序取出,栈,先进后出,你们懂的……
     
    Chapter4:if以及循环
    首先我们要记得,被Dijkstra老爷子骂的一文不值的goto……
    jmp语法和C++里的goto基本一样
     
    start:
    XXX
    jmp start
     
    然后,汇编没有if then,但是有条件goto
    有个语句叫做cmp
    cmp X,Y (老原则,这两个得有一个寄存器……如果和常数比较,常数必须在后面)
    传说中具体实现是减法……
    这个的效果是可能改变若干标识位的值……譬如:0标识,进位标识,溢出标识……等等……
    然后,有若干指令
    jl XX(l==less) 相当于 if (x<y) goto XX; 下同
    jg XX(g==greater)
    jle XX(ne==less or equal)
    jge XX(ge==greter or equal)
    jnle XX(n==no->nle==g)
    jnge XX(n==no->nge==l)
     
    有了if then,各种运算,我们就能做出来&&,||的逻辑
    有了if then,我们也能做出循环……
     
    很多的功能就有了……
     
    Chapter5:实战
     
    看看
     
    int main() {
        int a,b,c;
        input(a);
        input(b);
        if (a < b) {
            c = a;
        } else {
            c = b;
        }
        print(c);
    }
     
    用汇编翻译出来啥样:
     
    global main 
    extern printf 
    extern scanf 
     
    section .data 
    ctrlout   db      '%lld', 10, 0 
    ctrlin    db      '%lld', 0 
    _a db 0,0,0,0,0,0,0,0
    _b db 0,0,0,0,0,0,0,0
    _c db 0,0,0,0,0,0,0,0
     
     
    section .text
    main:
    movrax, 0
    movrdi, ctrlin
    movrsi, _a
    callscanf
    movrax, 0
    movrdi, ctrlin
    movrsi, _b
    callscanf
    MOV rax, [_a]
    CMP rax, [_b]
    jl @0
    jmp @2
    @0:
    MOV rax, [_a]
    MOV [_c], rax
    jmp @1
    @2:
    MOV rax, [_b]
    MOV [_c], rax
    @1:
    movrax, 0
    movrdi, ctrlout
    movrsi, [_c]
    callprintf
    mov rax, 0
    ret
     
    Chapter6:其它
    由于现实生活中,我需要写汇编时候实在是少,因此也没啥经验……尽管借助手册,可以对付着写一点汇编代码,但是那是不科学的……对我来讲,汇编告诉我们的一些底层的东西更有价值,可以在高级语言中有所体现:譬如一些表达式的写法,可以考虑写的更科学一些,譬如 c = a / b, q = a % b,记得写成 c = a / b; q = a - c * b,譬如灵活使用switch,等等……
  • 相关阅读:
    jQuery使用(十一):jQuery实例遍历与索引
    jQuery使用(十):jQuery实例方法之位置、坐标、图形(BOM)
    BOM:浏览器对象模型之浏览器剖析入门
    源码来袭:bind手写实现
    源码来袭:call、apply手写实现与应用
    浏览器UI多线程及JavaScript单线程运行机制的理解
    jQuery使用(九):队列及实现原理、基于队列模拟实现animate()
    原生JavaScript运动功能系列(五):定时定点运动
    原生JavaScript运动功能系列(四):多物体多值链式运动
    原生JavaScript运动功能系列(三):多物体多值运动
  • 原文地址:https://www.cnblogs.com/sweetsc/p/2578677.html
Copyright © 2011-2022 走看看