[NKU]sweet @ Google && TopCoder && CodeForces

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            33 Posts :: 1 Stories :: 15 Comments :: 0 Trackbacks

          2012年1月26日 #

          應(yīng)刁哥多年前的約稿,外加結(jié)合編譯原理系列連載,整理一下上一篇文章,寫一個(gè)簡(jiǎn)易的匯編教程……
          盡管謬誤,疏漏若干,但是本文的目的不在于培養(yǎng)學(xué)霸,而在于讓不會(huì)匯編的人簡(jiǎn)單看看就能寫出個(gè)基本能用的匯編程序……
          在程序設(shè)計(jì)中,我們知道一項(xiàng)基本原理:語(yǔ)言越高級(jí),一般來(lái)講容易編寫,形式簡(jiǎn)潔,可移植性強(qiáng),效率低……
          反過(guò)來(lái),語(yǔ)言越低級(jí),越難編寫,代碼越容易是又臭又長(zhǎng)還看不懂,越難移植,但是效率會(huì)很高……
          匯編就是比較低級(jí)的語(yǔ)言了,由于其不太好移植,匯編版本又多種多樣
          因此,本著活學(xué)活用,到時(shí)候不會(huì)就查手冊(cè)的原則,本文不會(huì)太著重強(qiáng)調(diào)語(yǔ)法,語(yǔ)法以intel語(yǔ)法為主……
          Chapter1:一些基本知識(shí)
          首先,CPU有若干寄存器,在32位CPU中,形如eax,ebx,ecx,edx……64位CPU則為rax,rbx……
          雖然寄存器這個(gè)名字很NB,其實(shí)它的本質(zhì)上就是int,為了向下兼容的需要,一些老的程序,也有可能能在新型的CPU上運(yùn)行(詳見DOS老游戲,有的能玩,有的不能玩)
          以64位舉例:
          64位寄存器rax,低32位是eax,eax低16位是ax,ax高8位叫ah,低8位叫al……
          CPU只能直接對(duì)寄存器進(jìn)行運(yùn)算……因?yàn)殡娐方Y(jié)構(gòu)就是那樣的……因此,所有的操作,都需要經(jīng)過(guò)寄存器
          譬如賦值:A = 1,實(shí)際上是 AX = 1; A = AX; 譬如 A = A + B,應(yīng)該是 AX = A; AX += B; A = AX; 這樣
          我是比較不求甚解的,99%功能(運(yùn)算,判斷,指針)等,使用eax,ebx,ecx,edx都能搞定……于是咱們也就不說(shuō)別的了……
          Chapter2:匯編的基本語(yǔ)法:
          匯編語(yǔ)言大概分為四個(gè)段,其中比較需要記住的就是代碼段和數(shù)據(jù)段……
          用類似高級(jí)語(yǔ)言的思路來(lái)講,數(shù)據(jù)段用于定義一些全局變量,代碼段用于寫程序……
          來(lái)看一個(gè)簡(jiǎn)單的 Hello World(環(huán)境: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:
          mov rax, 0
          mov rdi, ctrlout
          mov rsi, hw
          call printf
                  mov rax, 0
          ret
          其中,剛開始global main 表示程序入口,由于這里直接調(diào)用了Linux的函數(shù)printf,之后需要gcc編譯,所以叫做main,如果只使用中斷輸出的話,可以搞成_start
          之后extern意義和C++類似,表示這個(gè)函數(shù)在別處,叫編譯器以為他有就行……
          section .data  表示數(shù)據(jù)段,后面是一些定義,以及初始化
          注意這里定義的東西類型都像是指針一樣,ctrlout,hw 實(shí)際上是地址……
          初始化必須搞的干凈一點(diǎn),有一次調(diào)了半天不對(duì),就是因?yàn)橐粋€(gè)64位int,前4位沒有初始化成0……
          之后 section .text 表示代碼段,這里調(diào)用C語(yǔ)言函數(shù),網(wǎng)上說(shuō),64位匯編中,參數(shù)傳遞方式有了修改,大概是有4~5個(gè)寄存器專門保存參數(shù),如果參數(shù)過(guò)多,再放入棧,rax存放棧中的參數(shù)個(gè)數(shù)……我們想printf("%s","Hello World"),就要調(diào)用兩個(gè)參數(shù),于是,我們把第一個(gè)參數(shù),字符串ctrlout的指針mov進(jìn)rdi,hw mov進(jìn)rsi,rax賦值為0 ,然后,調(diào)用……
          (實(shí)現(xiàn)時(shí),需要手冊(cè)自行查閱一下本機(jī)怎么搞)
          函數(shù)的返回值在rax,于是return 0
          然后 yasm -f elf64 XXX.asm
          gcc -o XXX XXX.o
          ./XXX
          一個(gè)嶄新的hello world 出現(xiàn)了……
          Chapter3:匯編的一些簡(jiǎn)單指令:
          由于這是速成教程,選取一部分指令……要記住,CPU只能直接對(duì)寄存器進(jìn)行運(yùn)算……
          mov:相當(dāng)于賦值, mov eax,b  相當(dāng)于eax = b;
          注意:mov 后面的兩個(gè)東西至少要有一個(gè)是寄存器……
          同時(shí),和高級(jí)語(yǔ)言一樣,不能搞什么 mov 10,eax……
          add:add eax,b 相當(dāng)于eax+=b
          sub:基本同上……
          mul || imul:前面是無(wú)符號(hào)的乘,后面是有符號(hào)的乘,默認(rèn)被乘數(shù)在AX,于是指令形如:mul BX,如果有溢出,則高位溢出到DX,記得備份DX的東西……
          div  ||  idiv:前面是無(wú)符號(hào)整除,后面是有符號(hào)整除,默認(rèn)被除數(shù)是DX(高16位)和AX(低16位),因此,除法之前記得把DX清零,否則數(shù)會(huì)不對(duì)……
          之后商在AX,余數(shù)在DX
          指令形如:div BX
          push/pop:每個(gè)程序都有棧,或者是在程序中定義堆棧段,或者使用默認(rèn)棧
          push eax,表示把eax放進(jìn)棧里,pop ebx,表示取出棧頂,放在ebx中……
          push和pop異常重要,一個(gè)重要作用就是保護(hù)寄存器,譬如DX中有內(nèi)容,但是現(xiàn)在要做乘法,沒準(zhǔn)會(huì)破壞DX(見上),于是,先PUSH DX,然后,乘法,然后POP DX,又好比你要調(diào)用一個(gè)函數(shù),但是寄存器里有有用的信息,不保證這個(gè)函數(shù)不會(huì)破壞,于是,把所有寄存器先push進(jìn)去,運(yùn)行函數(shù),然后再pop出來(lái),這是常用技巧……
          另外也可以用來(lái)給函數(shù)傳參數(shù),傳時(shí)倒序壓入,用時(shí)順序取出,棧,先進(jìn)后出,你們懂的……
          Chapter4:if以及循環(huán)
          首先我們要記得,被Dijkstra老爺子罵的一文不值的goto……
          jmp語(yǔ)法和C++里的goto基本一樣
          start:
          XXX
          jmp start
          然后,匯編沒有if then,但是有條件goto
          有個(gè)語(yǔ)句叫做cmp
          cmp X,Y (老原則,這兩個(gè)得有一個(gè)寄存器……如果和常數(shù)比較,常數(shù)必須在后面)
          傳說(shuō)中具體實(shí)現(xiàn)是減法……
          這個(gè)的效果是可能改變?nèi)舾蓸?biāo)識(shí)位的值……譬如:0標(biāo)識(shí),進(jìn)位標(biāo)識(shí),溢出標(biāo)識(shí)……等等……
          然后,有若干指令
          jl XX(l==less) 相當(dāng)于 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,各種運(yùn)算,我們就能做出來(lái)&&,||的邏輯
          有了if then,我們也能做出循環(huán)……
          很多的功能就有了……
          Chapter5:實(shí)戰(zhàn)
          看看
          int main() {
              int a,b,c;
              input(a);
              input(b);
              if (a < b) {
                  c = a;
              } else {
                  c = b;
              }
              print(c);
          }
          用匯編翻譯出來(lái)啥樣:
          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:
          mov rax, 0
          mov rdi, ctrlin
          mov rsi, _a
          call scanf
          mov rax, 0
          mov rdi, ctrlin
          mov rsi, _b
          call scanf
          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:
          mov rax, 0
          mov rdi, ctrlout
          mov rsi, [_c]
          call printf
          mov rax, 0
          ret
          Chapter6:其它
          由于現(xiàn)實(shí)生活中,我需要寫匯編時(shí)候?qū)嵲谑巧伲虼艘矝]啥經(jīng)驗(yàn)……盡管借助手冊(cè),可以對(duì)付著寫一點(diǎn)匯編代碼,但是那是不科學(xué)的……對(duì)我來(lái)講,匯編告訴我們的一些底層的東西更有價(jià)值,可以在高級(jí)語(yǔ)言中有所體現(xiàn):譬如一些表達(dá)式的寫法,可以考慮寫的更科學(xué)一些,譬如 c = a / b, q = a % b,記得寫成 c = a / b; q = a - c * b,譬如靈活使用switch,等等……
          posted @ 2012-01-26 22:42 sweetsc 閱讀(688) | 評(píng)論 (0)編輯 收藏

          主站蜘蛛池模板: 广宁县| 邵阳县| 中阳县| 龙山县| 仲巴县| 永丰县| 吉隆县| 旌德县| 沅陵县| 香河县| 阜新市| 台前县| 公安县| 东兴市| 德江县| 临漳县| 固安县| 龙胜| 饶阳县| 万荣县| 溧阳市| 辽阳市| 墨脱县| 镇原县| 南漳县| 牙克石市| 河东区| 鹤峰县| 虎林市| 策勒县| 荣成市| 梨树县| 都匀市| 遵化市| 开封市| 布拖县| 且末县| 来安县| 蓝山县| 大英县| 天津市|