匯編語(yǔ)言-Emu8086使用指南_第1頁(yè)
匯編語(yǔ)言-Emu8086使用指南_第2頁(yè)
匯編語(yǔ)言-Emu8086使用指南_第3頁(yè)
匯編語(yǔ)言-Emu8086使用指南_第4頁(yè)
匯編語(yǔ)言-Emu8086使用指南_第5頁(yè)
已閱讀5頁(yè),還剩46頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

十進(jìn)制系統(tǒng)

目前使用最多的是十進(jìn)制.十進(jìn)制系統(tǒng)有10個(gè)數(shù)字0,1,2,3,4,5,6,7,8,9利用這些數(shù)字能表示任何數(shù)值,例如754這些數(shù)字是由每一位數(shù)字乘以“基數(shù)”的冪累加而成的〔上一個(gè)例子中基數(shù)是10因?yàn)槭M(jìn)制中有十個(gè)數(shù)字〕。

位置對(duì)于每一個(gè)數(shù)字是很重要的。例如,你將上一個(gè)例子中的“7”放到結(jié)尾:547

數(shù)值就成為:

特別提醒:任何數(shù)字的0次冪都是1,0的0次冪也是1

二進(jìn)制

計(jì)算機(jī)沒(méi)有人類(lèi)聰明〔至少現(xiàn)在是這樣〕,制造一個(gè)只有開(kāi)關(guān)或者稱(chēng)為0,1兩種狀態(tài)的電子機(jī)器很容易。計(jì)算機(jī)使用二進(jìn)制系統(tǒng),只有兩個(gè)數(shù)字0,1基地為2每一位二進(jìn)制數(shù)稱(chēng)作一位〔BIT〕,4BIT組成一個(gè)半字節(jié)〔NIBBLE〕,8BIT組成一個(gè)字節(jié)〔BYTE〕,兩個(gè)字節(jié)組成一個(gè)字〔WORD〕,兩個(gè)字組成一個(gè)雙字〔DOUBLEWORD〕〔很少使用〕:

習(xí)慣上在一串二進(jìn)制后面加上“b”,這樣,我們可以知道101b是二進(jìn)制表示十進(jìn)制的5。

二進(jìn)制10100101b表示十進(jìn)制的165,計(jì)算方法如下:

十六進(jìn)制系統(tǒng)

十六進(jìn)制系統(tǒng)使用16個(gè)數(shù)字0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F基底是16.十六進(jìn)制非常緊湊,便于閱讀。將二進(jìn)制轉(zhuǎn)換為十六進(jìn)制很容易,半字節(jié)〔4bits〕對(duì)應(yīng)一位十六進(jìn)制如下表Decimal

(base10)Binary

(base2)Hexadecimal

(base16)000000100011200102300113401004501015601106701117810008910019101010A111011B121100C131101D141110E151111F習(xí)慣上我們?cè)谝粋€(gè)十六進(jìn)制數(shù)的后面加上"H",以便和其他進(jìn)制區(qū)別,這樣我們就知道5Fh是一個(gè)十六進(jìn)制數(shù)表示十進(jìn)制的

95。習(xí)慣上,我們也在以字母開(kāi)頭〔從A到F〕的十六進(jìn)制數(shù)前面加上"0"例如:0E120h.十六進(jìn)制1234h

等于4660:

十進(jìn)制到另外進(jìn)制的換算

在換算中,將十進(jìn)制數(shù)不斷除以目標(biāo)進(jìn)制的基底,每一次都要記錄下商和余數(shù),直到商0。余數(shù)用來(lái)表示結(jié)果。

下面是一個(gè)十進(jìn)制39〔基底是10〕到十六進(jìn)制〔基底是16〕的換算:

結(jié)果為27H

上例中所有的余數(shù)都小于10,不必使用字母。再舉一個(gè)更復(fù)雜的例子:十進(jìn)制43868換算為十六進(jìn)制:

結(jié)果是0AB5Ch,使用上面提到的表將大于9的數(shù)字替換成字母。運(yùn)用同樣的原理,我們可以換算為二進(jìn)制〔用2作除數(shù)〕,或者是先換算成十六進(jìn)制,再用上面的表?yè)Q算成二進(jìn)制:

于是,得到二進(jìn)制:有符號(hào)數(shù)

對(duì)于十六進(jìn)制數(shù)0FFh無(wú)法確定它是正數(shù)還是負(fù)數(shù),因?yàn)樗梢员硎臼M(jìn)制的"255"或者"-1"。

8位可以表示256個(gè)狀態(tài),于是,我們可以假定前128?jìng)€(gè)表示正數(shù)(從0到127),接下來(lái)的128?jìng)€(gè)數(shù)(從128到256)表示負(fù)數(shù)。如果想表示"-5",我們從256中減去5,即256-5=251。用這種復(fù)雜的方法表示一個(gè)負(fù)數(shù)有著數(shù)學(xué)依據(jù)的,數(shù)學(xué)上"-5"加上"5"等于0。當(dāng)我們將兩個(gè)8位的數(shù)字5和251相加時(shí),結(jié)果超過(guò)255,溢出處理為0!

128到256高位始終是1,這個(gè)可以作為數(shù)字符號(hào)的標(biāo)記對(duì)于字〔16位〕,16位有65536個(gè)狀態(tài),頭32768?jìng)€(gè)狀態(tài)(從0到32767)用來(lái)表示正數(shù),下面的32768?jìng)€(gè)狀態(tài)(從32767到65535)表示負(fù)數(shù)Emu8086帶有數(shù)制轉(zhuǎn)換工具,也可以計(jì)算各種數(shù)值表達(dá)式。選擇菜單Math項(xiàng):

NumberConvertor〔數(shù)制轉(zhuǎn)換〕可以實(shí)現(xiàn)任意數(shù)制之間的轉(zhuǎn)換。在文本框中填寫(xiě)源數(shù)值,將自動(dòng)轉(zhuǎn)換到任意的數(shù)制。可以作8位或者16位轉(zhuǎn)換。ExpressionEvaluator〔表達(dá)式計(jì)算〕可以用來(lái)計(jì)算不同數(shù)制的計(jì)算以及從一個(gè)進(jìn)制到另一個(gè)進(jìn)制的轉(zhuǎn)換。輸入表達(dá)式,按下回車(chē),結(jié)果就會(huì)以你選定的進(jìn)制表示。最長(zhǎng)可以進(jìn)行32位的計(jì)算。當(dāng)在Signed打鉤選中時(shí)〔除了八進(jìn)制和雙字〕,最前面的一位將被認(rèn)作是符號(hào)位。這樣以來(lái),0FFFFFFFFh將被認(rèn)為是十進(jìn)制的-1。例如,你計(jì)算0FFFFh*10h+0FFFFh(8086CPU所能訪(fǎng)問(wèn)的最大內(nèi)存地址)。如果你選中Signed和Word選項(xiàng),結(jié)果是-17〔因?yàn)楸磉_(dá)式被認(rèn)為是(-1)*16+(-1)〕。如果想按照無(wú)符號(hào)數(shù)計(jì)算,請(qǐng)不要選擇Signed表達(dá)式為65535*16+65535計(jì)算結(jié)果將是1114095同樣你可以使用NumberConvertor將非十進(jìn)制換算為有符號(hào)的十進(jìn)制,然后根據(jù)十進(jìn)制計(jì)算。支持如下運(yùn)算:~not(invertsallbits).*multiply./divide.%modulus.+sum.-subtract(andunary-).<<shiftleft.>>shiftright.&bitwiseAND^bitwiseXOR.|bitwiseOR.二進(jìn)制必須有“b”作結(jié)尾,例如00011011b十六進(jìn)制必須有"h"作結(jié)尾,另外,當(dāng)?shù)匾晃皇亲帜笗r(shí),最前面必須加上0,例如:0ABCDh八進(jìn)制必須有"o"作結(jié)尾,例如:77o通用存放器

8086CPU有8個(gè)通用存放器,每一個(gè)存放器都有自己的名稱(chēng):AX累加存放器accumulatorregister〔分為AH/AL〕.BX基址存放器baseaddressregister〔分為BH/BL〕.CX計(jì)數(shù)存放器countregister〔分為CH/CL〕.DX數(shù)據(jù)存放器dataregister〔分為DH/DL〕.SI源變址存放器sourceindexregister.DI目的變址存放器destinationindexregister.BP基址指針存放器basepointer.SP堆棧存放器stackpointer.當(dāng)你修改其中任意8位值,整個(gè)16位存放器的值同樣改變。同樣對(duì)于其他的3個(gè)存放器,“H”表示高8位,“L”表示低8位。存放器在CPU內(nèi)部,訪(fǎng)問(wèn)中它們速度遠(yuǎn)遠(yuǎn)超過(guò)內(nèi)存。因?yàn)椋L(fǎng)問(wèn)內(nèi)存需要經(jīng)過(guò)系統(tǒng)總線(xiàn),所以時(shí)間要長(zhǎng)一些。而訪(fǎng)問(wèn)存放器中的數(shù)據(jù)幾乎不需要時(shí)間。于是,編程中,應(yīng)當(dāng)盡量在存放器中保存數(shù)據(jù)。雖然存放器很小,并且這些存放器都有具體用途,但他們依然是存放計(jì)算中臨時(shí)數(shù)據(jù)的好地方。段存放器CS代碼段存放器,用來(lái)存放當(dāng)前正在運(yùn)行的指令DS數(shù)據(jù)段存放器,用來(lái)存放當(dāng)前運(yùn)行程序所用的數(shù)據(jù)ES附加段存放器,由程序員決定用途SS堆棧段存放器,指出堆棧所在區(qū)域盡管容許在段存放器中存放任何數(shù)據(jù),但是這決不是一個(gè)好主意。段存放器有著非常特別的目的--指出可以訪(fǎng)問(wèn)內(nèi)存塊的地址。段存放器與通用存放器協(xié)同工作就可以訪(fǎng)問(wèn)任意的內(nèi)存區(qū)域。例如,如果我們打算訪(fǎng)問(wèn)物理地址是12345h〔十六進(jìn)制〕的內(nèi)存單元,我們應(yīng)設(shè)置DS=1230hSI=0045h這樣以來(lái),我們便能訪(fǎng)問(wèn)超過(guò)一個(gè)存放器〔16位〕所能表示的內(nèi)存地址的范圍。CPU計(jì)算物理地址的方法是將段存放器乘以10H在加上一個(gè)特定的通用存放器。(1230h*10h+45h=12345h):這種,由兩個(gè)存放器生成的地址被稱(chēng)為有效地址〔effectiveaddress〕默認(rèn)下,BX,SI及DI與DS協(xié)同工作,BPSP與SS存放器協(xié)同工作。其余的通用存放器不能形成有效地址!同樣,盡管BX可以形成有效地址,但是BHBL不能!控制存放IP指令指針存放器instructionpointer、FlagsRegister狀態(tài)標(biāo)志存放器IP始終同CS協(xié)同工作,指出當(dāng)前執(zhí)行的指令。FlagsRegister完成一次數(shù)學(xué)運(yùn)算后,由CPU自動(dòng)修改,通過(guò)它可以得到當(dāng)前結(jié)果類(lèi)型,也可以作為跳轉(zhuǎn)語(yǔ)句條件。通常你無(wú)法直接訪(fǎng)問(wèn)它們。尋址方式我們可以通過(guò)下面的四個(gè)存放器來(lái)尋址BX,SI,DI,BP.通過(guò)計(jì)算[]符號(hào)中的值,我們可以訪(fǎng)問(wèn)到不同內(nèi)存單元的值。具體組合請(qǐng)看下表:[BX+SI]

[BX+DI]

[BP+SI]

[BP+DI][SI]

[DI]

d16(variableoffsetonly)

[BX][BX+SI]+d8

[BX+DI]+d8

[BP+SI]+d8

[BP+DI]+d8[SI]+d8

[DI]+d8

[BP]+d8

[BX]+d8[BX+SI]+d16

[BX+DI]+d16

[BP+SI]+d16

[BP+DI]+d16[SI]+d16

[DI]+d16

[BP]+d16

[BX]+d16d8-表示8位偏移量d16-表示16位偏移量

偏移量可以是一個(gè)立即數(shù)或者是一個(gè)變量的偏移,或者二者兼?zhèn)?。這取決于編譯器如何計(jì)算單獨(dú)的立即數(shù)。偏移量可以在[]符號(hào)里面或者外面,這不影響編譯器生成相同的機(jī)器碼。偏移量是一個(gè)有符號(hào)數(shù),可以是正數(shù)或者負(fù)數(shù)。一般說(shuō)來(lái),8位或者16位,對(duì)于編譯后的結(jié)果是有影響的。例如,假定DS=100,BX=30,SI=70。如下尋址方式[BX+SI]+25計(jì)算物理地址為100*16+30+70+25=1725

默認(rèn)下,DS存放器應(yīng)用在除了BP存放器之外的所有物理地址計(jì)算中,存放器是和SS存放器一起工作的。用過(guò)下面的表,你可以和輕松記住誰(shuí)和誰(shuí)是關(guān)聯(lián)在一起使用的。

上表中,你可以從每一列中選擇一個(gè)或者忽略任意一個(gè)列。比方,可以看到,BX和BP始終不會(huì)選到一起。SI和DI不會(huì)選到一起。這是一個(gè)計(jì)算地址模式[BX+5]段存放器(CS,DS,SS,ES)中數(shù)值被稱(chēng)作"段偏移"。目的存放器(BX,SI,DI,BP)中數(shù)值被稱(chēng)作"偏移量"比方,ds中數(shù)值為1234h,si中數(shù)值為7890h,可以記作1234:7890物理地址為1234h*10h+7890h=19BD0h在編譯過(guò)程中使用如下聲明數(shù)據(jù)類(lèi)型BYTEPTR-表示字節(jié);WORDPTR-表示字〔2個(gè)字節(jié)〕

例如:BYTEPTR[BX];按字節(jié)訪(fǎng)問(wèn);WORDPTR[BX];按字訪(fǎng)問(wèn)

Emu8086容許使用如下更簡(jiǎn)潔的前綴

b.-等價(jià)于上面的BYTEPTR;w.-等價(jià)于上面的WORDPTR

有時(shí),編譯器可以自動(dòng)計(jì)算出數(shù)據(jù)類(lèi)型,但是如果一個(gè)參與運(yùn)算的數(shù)是立即數(shù),這種方法就不可靠了。

MOV指令將第二個(gè)操作數(shù)〔源〕拷貝到第一個(gè)操作數(shù)〔目的〕指定位值,源操作數(shù)可以是立即數(shù),通用存放器或者內(nèi)存單元,目的存放器可以是通用存放器或者內(nèi)存單元,源和目的必須是同樣大小,要么都是字節(jié)要么都是字操作類(lèi)型如下:MOVREG,memory

MOVmemory,REG

MOVREG,REG

MOVmemory,immediate

MOVREG,immediateREG:AX,BX,CX,DX,AH,AL,BL,BH,CH,CL,DH,DL,DI,SI,BP,SP.

memory:[BX],[BX+SI+7],變量,等等

immediate:5,-24,3Fh,10001101b,等等.mov指令只支持如下段存放器:MOVSREG,memory

MOVmemory,SREG

MOVREG,SREG

MOVSREG,REGSREG:DS,ES,SS,注意CS只能作操作源

REG:AX,BX,CX,DX,AH,AL,BL,BH,CH,CL,DH,DL,DI,SI,BP,SP.

memory:[BX],[BX+SI+7],variable,等等MOV指令不能用來(lái)設(shè)置CS和IP存放器的值。下面是一個(gè)使用MOV指令的例子:

#MAKE_COM#

;

表示,這個(gè)是一個(gè)com程序

ORG100h

;COM程序必須的

MOVAX,0B800h

;將ax設(shè)置為B800h.

MOVDS,AX

;將AX值拷貝到DS.

MOVCL,'A'

;將ASCII碼'A'的值傳送到cl,這個(gè)值是41h.

MOVCH,01011111b;將ch設(shè)置為二進(jìn)制的01011111b

MOVBX,15Eh

;

將BX設(shè)置成15Eh.

MOV[BX],CX

;將CX放到bx指出的內(nèi)存單元B800:015E

RET

;返回操作系統(tǒng)你可以將上面的程序貼入Emu8086代碼編輯器,接下來(lái)按下[complieandemulate](或者按F5)模擬窗口將顯示這個(gè)程序已經(jīng)調(diào)入,點(diǎn)擊[singlestep]觀(guān)察存放器數(shù)值變化,你可以猜到";"表示注釋?zhuān)幾g器忽略在";"后面的一切,程序結(jié)束后,你可以看到如下窗口

事實(shí)上,上面程序是將字符直接寫(xiě)入顯示內(nèi)存。

通過(guò)上面的例子,你可以發(fā)現(xiàn)MOV指令是非常有用的。變量變量是一個(gè)內(nèi)存地址。對(duì)于編程者來(lái)說(shuō),使用諸如名稱(chēng)為“var1”這樣的變量保存數(shù)據(jù)遠(yuǎn)遠(yuǎn)比使用5a73:235b這樣的地址容易的多。特別是當(dāng)你使用10個(gè)以上的變量的時(shí)侯。編譯器支持這兩種變量BYTE和WORD.〔字節(jié)和字〕聲明變量的方法:

nameDBvalue名稱(chēng)DB值

nameDWvalue名稱(chēng)DW值

DB-staysforDefineByte.

DW-staysforDefineWord.

name-可以是任何字母與數(shù)字構(gòu)成,但是必須由字母開(kāi)頭??梢酝ㄟ^(guò)不命名來(lái)聲明一個(gè)沒(méi)有名稱(chēng)的的變量〔這個(gè)變量只有地址,沒(méi)有名稱(chēng)〕value-可以是任何數(shù)值支持三種進(jìn)制(十六進(jìn)制,二進(jìn)制和十進(jìn)制),你可以使用"?"符號(hào)表示初始值沒(méi)有確定。你可能從第二章了解到,MOV指令是將數(shù)值從源拷貝到目的。讓我們?cè)倏匆粋€(gè)MOV指令的例子#MAKE_COM#ORG100hMOVAL,var1MOVBX,var2RET;stopstheprogram.VAR1DB7var2DW1234h將上面的代碼拷貝到emu8086源程序編輯器中,按下F5鍵編譯并在模擬器中執(zhí)行。你會(huì)看到如下畫(huà)面

從畫(huà)面可以看出,反編譯后的代碼同源程序很相似,不同的是變量被具體的內(nèi)存地址取代。當(dāng)編譯器生成機(jī)器代碼它會(huì)自動(dòng)將變量名稱(chēng)用該變量的廉價(jià)量代替。默認(rèn)情況下,DS存放器存放段偏移〔當(dāng)執(zhí)行com文件的時(shí)侯,DS存放器的值同CS存放器〔代碼段〕的值一樣〕。內(nèi)存第一列是偏移〔offset〕,第二列是一個(gè)十六進(jìn)制值〔hexadecimalvalue〕,第三列是十進(jìn)制〔decimalvalue〕,最后一列是ASCII字符。編譯器是非大小寫(xiě)敏感的,所以“VAR1”同“var1”都是同一個(gè)變量。VAR1變量的偏移是0108h,物理地址是0b56:0108

var2變量的偏移是0109h,物理地址是0b56:0109這個(gè)變量是字,它占用2字節(jié)。這里假定低字節(jié)存放在低地址,所以34h位于12h前面。

你可以看到,在RET指令后面還有一些指令,這樣是因?yàn)榉淳幾g工具無(wú)法判斷數(shù)據(jù)從什么地方開(kāi)始。同樣,你可以寫(xiě)出直接使用DB的程序.#MAKE_COM#ORG100hDB0A0hDB08hDB01hDB8BhDB1EhDB09hDB01hDB0C3hDB7DB34hDB12h將上面的代碼拷貝到emu8086原代碼編輯器,按下F5鍵編譯,并在模擬器中運(yùn)行,你可以看到同樣的反匯編結(jié)果,得到同樣的功能。根據(jù)上面,你可以猜想,編譯器將源程序轉(zhuǎn)化為一些字節(jié)的集合,這個(gè)集合被稱(chēng)作機(jī)器代碼(machinecode),處理器懂得他們,并且執(zhí)行它們。ORG100是一個(gè)編譯指令〔它告訴編譯器如何處理源代碼〕當(dāng)你使用變量的時(shí)侯,這條指令特別重要。它通知編譯器可執(zhí)行程序?qū)⒈徽{(diào)入偏移量是100h〔256字節(jié)〕的位置,有了它,編譯器就可以計(jì)算出所有變量的正確地址,然后用這些地址〔偏移量〕來(lái)代替變量名稱(chēng)。上面的這些指令不會(huì)真正的編譯為任何機(jī)器代碼。為何可執(zhí)行程序總是被裝入偏移量100h?操作系統(tǒng)在CS存放器(代碼段)存儲(chǔ)著程序信息,比方命令行方式下的參數(shù)等等。盡管上面只是一個(gè)COM文件的例子,EXE文件調(diào)入在偏移量0000的位置,他使用特定的段保存變量。我們?cè)谙旅鏁?huì)學(xué)習(xí)到關(guān)于EXE文件的知識(shí)。數(shù)組

數(shù)組可以看作是變量鏈。一個(gè)字符串是一個(gè)字節(jié)數(shù)組的例子,其中每一個(gè)字符都當(dāng)作一個(gè)ASCII碼的值〔0255〕下面是一些定義數(shù)組的例子aDB48h,65h,6Ch,6Ch,6Fh,00hbDB'Hello',0

b是一個(gè)數(shù)組,當(dāng)編譯器發(fā)現(xiàn)引用了字符串值后,會(huì)自動(dòng)將這些字符轉(zhuǎn)化為對(duì)應(yīng)的字節(jié)。下面圖表表示的就是聲明數(shù)組后在內(nèi)存中的分布:

你可以使用方括號(hào)做下標(biāo)直接訪(fǎng)問(wèn)到數(shù)組中的值,例如:

MOVAL,a[3]同樣,你還可以使用任意一個(gè)內(nèi)存索引存放器BX,SI,DI,BP,例如:MOVSI,3

MOVAL,a[SI]

如果你想聲明比擬復(fù)雜的數(shù)組,你可以使用DUP指令形式如下numberDUP(value(s))

number-重復(fù)的數(shù)量〔任意常數(shù)〕value-將要復(fù)制的表達(dá)式

例如:cDB5DUP(9)就相當(dāng)于如下定義:cDB9,9,9,9,9另外一個(gè)例子:dDB5DUP(1,2)等同于dDB1,2,1,2,1,2,1,2,1,2當(dāng)然,如果需要存放超過(guò)255或者小于-128的數(shù)值,你還可以使用DW來(lái)代替DB。但是DW不能用于聲明字符串。DUP命令展開(kāi)后不能超過(guò)1020個(gè)字符〔上一個(gè)例子中展開(kāi)之后是13個(gè)字符〕,如果需要聲明請(qǐng)將它們分成兩行〔這樣,內(nèi)存中得到的仍然是一個(gè)大數(shù)組〕。取得變量地址LEA指令〔LoadEffectiveAddress讀取有效地址〕或者OFFSET指令。OFFSET和LEA二者都能夠獲得變量的偏移量。LEA在使用中更有效,這是因?yàn)樗芊祷厮饕兞康牡刂贰H〉米兞康刂吩诤芏嗲闆r下是非常有用的,例如你打算向一個(gè)過(guò)程傳遞參數(shù)。注意:在編譯過(guò)程中使用如下聲明數(shù)據(jù)類(lèi)型BYTEPTR-表示字節(jié);WORDPTR-表示字〔2個(gè)字節(jié)〕

例如:

BYTEPTR[BX];按字節(jié)訪(fǎng)問(wèn);WORDPTR[BX];按字訪(fǎng)問(wèn)

Emu8086容許使用如下更簡(jiǎn)潔的前綴b.-等價(jià)于上面的BYTEPTR;w.-等價(jià)于上面的WORDPTR

有時(shí),編譯器可以自動(dòng)計(jì)算出數(shù)據(jù)類(lèi)型,但是如果一個(gè)參與運(yùn)算的數(shù)是立即數(shù),這種方法就不可靠了。第一個(gè)例子:ORG100hMOVAL,VAR1;將變量var1的數(shù)值放入al以便檢查L(zhǎng)EABX,VAR1;將var1的地址存入BX.MOVBYTEPTR[BX],44h;修改變量var1的內(nèi)容MOVAL,VAR1;將變量VAR1的數(shù)值放入AL以便檢查RETVAR1DB22hEND下面是另外一個(gè)例子,用OFFSET指令代替LEA:ORG100hMOVAL,VAR1;將變量VAR1的值放入AL以便檢查.MOVBX,OFFSETVAR1;將變量VAR1的地址放入BX.MOVBYTEPTR[BX],44h;修改變量VAR1內(nèi)容MOVAL,VAR1;將變量VAR1的值放入AL以便檢查.RETVAR1DB22hEND上面例子的功能相同。

這些語(yǔ)句:

LEABX,VAR1

MOVBX,OFFSETVAR1

都將生成同樣的機(jī)器代碼:MOVBX,num,num是16位變量偏移請(qǐng)注意,只有這些存放器可以放入方括號(hào)中〔作為內(nèi)存指針〕BX,SI,DI,BP(請(qǐng)參考本教程前述章節(jié))

常量

常量同變量很相似,但是它一直存在。定義一個(gè)變量之后,它的值不會(huì)改變。使用EQU定義常量:nameequ<任意表達(dá)式>例如:kEQU5

MOVAX,k上面的例子等同于如下代碼:MOVAX,5在程序執(zhí)行過(guò)程中你可以選擇模擬器"View"菜單下的"Variables"

你可以點(diǎn)一個(gè)變量然后設(shè)置Elements屬性為數(shù)組大小來(lái)查看數(shù)組。匯編語(yǔ)言對(duì)于數(shù)據(jù)類(lèi)型并不嚴(yán)格,這樣以來(lái)所有的變量都可以被看作是數(shù)組。變量可以顯示為以下進(jìn)制HEX-十六進(jìn)制hexadecimal(基底16).BIN-二進(jìn)制(基底2).OCT-八進(jìn)制(基底8).SIGNED-有符號(hào)十進(jìn)制(基底10).UNSIGNED-無(wú)符號(hào)十進(jìn)制(基底10).CHAR-ASCII碼(一共有256個(gè)符號(hào),其中一些符號(hào)是不可見(jiàn)的).程序運(yùn)行的時(shí)侯,你可以通過(guò)雙擊它來(lái)編輯變量值,或者選中之后點(diǎn)Edit按鈕。十六進(jìn)制數(shù)值以"h"結(jié)尾,二進(jìn)制以"b"結(jié)尾,八進(jìn)制以"o"結(jié)尾,十進(jìn)制沒(méi)有結(jié)尾。字符串用這樣的方式表示:'helloworld',0

(結(jié)尾以0表示)數(shù)組按照如下輸入:1,2,3,4,5〔數(shù)組可以是一組字節(jié)或者字,這取決于你想以字節(jié)還是字的方式編輯〕

表達(dá)式會(huì)自動(dòng)計(jì)算,例如,輸入如下表達(dá)式5+2會(huì)自動(dòng)計(jì)算為7。等等中斷

中斷是一系列功能調(diào)用。這些功能調(diào)用使得編程更加容易。比方,你想在打印機(jī)上輸出一個(gè)字符,你只需要簡(jiǎn)單的調(diào)用中斷,它將幫你完成所有的事情。另外還有控制磁盤(pán)和其他硬件工作的中斷。我們將這些功能調(diào)用稱(chēng)作軟件中斷。不同的硬件同樣可以觸發(fā)中斷,這些中斷稱(chēng)作硬件中斷。這里,我們只介紹軟件中斷(softwareinterrupts)。觸發(fā)一個(gè)軟件中斷,需要使用INT指令,它的使用方式非常簡(jiǎn)單:INTvalue上面value的取值范圍是從0到255〔或者0到0ffh〕,通常我們使用十六進(jìn)制。你也許猜想只有256個(gè)中斷調(diào)用,但是這是不正確的。因?yàn)槊恳粋€(gè)中斷都有子功能。在調(diào)用一個(gè)中斷的子功能之前,需要設(shè)置AH存放器。每一個(gè)中斷最多可以擁有256個(gè)子功能〔于是,我們有256*256=65536個(gè)功能調(diào)用〕。一般情況下使用AH存放器,但是一些情況下可能使用另外的存放器。通常,其他的存放器是用來(lái)傳遞數(shù)據(jù)和參數(shù)的。下面的例子調(diào)用了INT10h中斷0Eh子功能輸出字符串‘Hello!'。這個(gè)功能作用是在屏幕上顯示一個(gè)字符,然后光標(biāo)進(jìn)一,如果需要還滾屏。#MAKE_COM#;生成com文件的指令ORG100h;我們使用的這個(gè)子功能沒(méi)有返回值,;所以我們只用設(shè)置就可以了。MOVAH,0Eh;選擇子功能;int10h/0eh子功能,輸出放在;AL存放器中的ASCII碼對(duì)應(yīng)的字符MOVAL,'H';ASCII碼:72INT10h;輸出MOVAL,'e';ASCII碼:101INT10h;輸出MOVAL,'l';ASCII碼:108INT10h;輸出MOVAL,'l';ASCII碼:108INT10h;輸出MOVAL,'o';ASCII碼:111INT10h;輸出MOVAL,'!';ASCII碼:33INT10h;輸出RET;返回操作系統(tǒng)將上述程序拷貝粘貼到Emu8086代碼編輯器,點(diǎn)擊[CompileandEmulate]按鈕,運(yùn)行!常用函數(shù)庫(kù)-emu8086.inc通過(guò)引用一些常用函數(shù),可以使你編程更加方便。在你的程序中使用其他文件中的函數(shù)的方法是

INCLUDE后面接上你要引用的文件名。編譯器

會(huì)自動(dòng)在你源程序所在的文件夾中查找你引用的文件,如果沒(méi)有找到,它將搜索Inc文件夾。通常你無(wú)法完全理解emu8086.inc〔位于Inc文件夾〕但是這沒(méi)有關(guān)系,你只用知道它能做什么就足夠了。要使用emu8086.inc中的函數(shù),你應(yīng)當(dāng)在你程序的開(kāi)頭加上

include'emu8086.inc'emu8086.inc定義了如下的宏:PUTCchar-將一個(gè)ascii字符輸出到光標(biāo)當(dāng)前位值,只有一個(gè)參數(shù)的宏GOTOXYcol,row-設(shè)置當(dāng)前光標(biāo)位置,有兩個(gè)參數(shù)PRINTstring-輸出字符串,一個(gè)參數(shù)PRINTNstring-輸出字符串,一個(gè)參數(shù)。與print功能相同,不同在于輸出之后自動(dòng)回車(chē)CURSOROFF-關(guān)閉文本光標(biāo)CURSORON-翻開(kāi)文本光標(biāo)使用上述宏的方法是:在你需要的位值寫(xiě)上宏名稱(chēng)加上參數(shù)。例如:ORG100hPRINT'HelloWorld!'GOTOXY10,5PUTC65;65-ASCII碼的'A'PUTC'B'RET;返回操作系統(tǒng)END;停止編譯器當(dāng)編譯器運(yùn)行你的代碼時(shí),它首先找到聲明中的emu8086.inc文件,然后將代碼中的宏用實(shí)際的代碼替換掉。通常來(lái)說(shuō),宏都是比擬小的代碼段,經(jīng)常使用宏會(huì)使得你的可執(zhí)行程序特別大〔對(duì)于降低文件大小來(lái)說(shuō)使用過(guò)程更好〕emu8086.inc同樣定義了如下過(guò)程:PRINT_STRING-在當(dāng)前光標(biāo)位置輸出一個(gè)字符串字符串地址由DS:SI存放器給出使用時(shí),需要在END前面聲明DEFINE_PRINT_STRING才能使用.PTHIS-在當(dāng)前光標(biāo)位置輸出一個(gè)字符串〔同PRINT_STRING〕一樣,不同之處在于它從堆棧接收字符串。字符串終止符應(yīng)在call之后定義。例如CALLPTHIS

db'HelloWorld!',0使用時(shí),需要在END前面聲明DEFINE_PTHIS。GET_STRING-從用戶(hù)輸入得到一個(gè)字符串,輸入的字符串寫(xiě)入DS:DI指出的緩沖,緩沖區(qū)的大小由DX設(shè)置?;剀?chē)作為輸入結(jié)束。使用時(shí),需要在END前面聲明

DEFINE_GET_STRING。CLEAR_SCREEN-清屏過(guò)程(滾過(guò)整個(gè)屏幕),然后將光標(biāo)設(shè)置在左上角.使用時(shí),需要在END前面聲明DEFINE_CLEAR_SCREEN

。

SCAN_NUM-取得用戶(hù)從鍵盤(pán)輸入的多位有符號(hào)數(shù),并將輸入存放在CX存放器。使用時(shí),需要在END前面聲明

DEFINE_SCAN_NUM。PRINT_NUM-輸出AX存放器中的有符號(hào)數(shù)。使用時(shí),需要在END前面聲明DEFINE_PRINT_NUM以及DEFINE_PRINT_NUM_UNS.PRINT_NUM_UNS-輸出AX存放器中的無(wú)符號(hào)數(shù)。使用時(shí),需要在END前面聲明DEFINE_PRINT_NUM_UNS.使用上述過(guò)程,必須在你源程序的底部〔但是在END之前!?。 陈暶鬟@些函數(shù),使用CALL指令后面接上過(guò)程名稱(chēng)來(lái)調(diào)用。例如:include'emu8086.inc'ORG100hLEASI,msg1;要求輸入數(shù)字CALLprint_string;CALLscan_num;讀取數(shù)字放入cxMOVAX,CX;CX存放數(shù)值拷貝到AX;輸入如下字符CALLpthisDB13,10,'Youhaveentered:',0CALLprint_num;輸出AX中的字符RET;返回操作系統(tǒng)msg1DB'Enterthenumber:',0DEFINE_SCAN_NUMDEFINE_PRINT_STRINGDEFINE_PRINT_NUMDEFINE_PRINT_NUM_UNS;print_num函數(shù)要求的DEFINE_PTHISEND;結(jié)束首先,編譯器運(yùn)行聲明〔對(duì)于宏只是展開(kāi)〕。當(dāng)編譯器遇到CALL指令,它將用過(guò)程聲明中的地址來(lái)替代過(guò)程名。程序在執(zhí)行過(guò)程中遇到這個(gè)過(guò)程,便會(huì)直接跳轉(zhuǎn)到過(guò)程。這是非常有用的,比方,即使在你的代碼中執(zhí)行100次一個(gè)過(guò)程,編譯后的可執(zhí)行文件也不會(huì)因此而增大多少。這樣看起來(lái)很劃算,是不是?后面你會(huì)學(xué)到更多的,現(xiàn)在只需要了解一點(diǎn)點(diǎn)根本原理。運(yùn)算與邏輯指令

大多數(shù)運(yùn)算與邏輯指令影響處理器的狀態(tài)標(biāo)記存放器。

從上圖可以看到,這是狀態(tài)標(biāo)記存放器是一個(gè)16位存放器,每一位稱(chēng)作一個(gè)標(biāo)志位,可以取值1或者0。

進(jìn)位標(biāo)志CarryFlag(CF)-出現(xiàn)無(wú)符號(hào)〔unsignedoverflow〕溢出該位設(shè)置成1。例如,計(jì)算255+1〔結(jié)果超出0...255〕。沒(méi)有溢出時(shí)該位為0。零標(biāo)志ZeroFlag(ZF)-當(dāng)結(jié)果為0時(shí)設(shè)置為1,結(jié)果不為0時(shí)設(shè)置為0。符號(hào)標(biāo)志SignFlag(SF)-

結(jié)果為負(fù)置1,結(jié)果為正置為0。事實(shí)上該位對(duì)于結(jié)果特別重要。溢出標(biāo)志OverflowFlag(OF)-

當(dāng)出現(xiàn)有符號(hào)數(shù)溢出設(shè)置為1。例如,計(jì)算100+50〔結(jié)果超出-128-127的范圍〕。奇偶標(biāo)志ParityFlag(PF)-當(dāng)結(jié)果操作數(shù)中1的個(gè)數(shù)為偶時(shí)置1,否那么為0注意,如果結(jié)果是一個(gè)字,該標(biāo)志只指示低8位。輔助進(jìn)位標(biāo)志AuxiliaryFlag(AF)-

低4位向上進(jìn)位時(shí)置1,否那么為0〔記錄運(yùn)算時(shí)第3位〔半個(gè)字節(jié)〕產(chǎn)生的進(jìn)位值。例如,執(zhí)行加法指令時(shí),最高有效位有進(jìn)位時(shí)置1,否那么置0

中斷標(biāo)志InterruptenableFlag(IF)-當(dāng)cpu容許中斷時(shí)為1,否那么為0DirectionFlag(DF)-方向標(biāo)志,在串處理指令中控制處理信息的方向用。當(dāng)DF為1時(shí),每次操作后使變址存放器SI和DI減量,這樣就使串處理從高地址向低地址方向處理。當(dāng)DF為0時(shí),那么使SI和DI增量,使串處理從低地址向高地址方向處理。這里有3組指令.第一組:ADD,SUB,CMP,AND,TEST,OR,XOR

支持如下操作數(shù):REG,memory

memory,REG

REG,REG

memory,immediate

REG,immediateREG〔存放器〕:AX,BX,CX,DX,AH,AL,BL,BH,CH,CL,DH,DL,DI,SI,BP,SP.

memory〔內(nèi)存〕:[BX],[BX+SI+7],變量,等等...

immediate〔立即數(shù)〕:5,-24,3Fh,10001101b,等等...

執(zhí)行之后,結(jié)果經(jīng)常存放在第一個(gè)操作數(shù)中。CMP和TEST指令只影響標(biāo)志位,并不返回?cái)?shù)值〔這兩條指令是用來(lái)在程序運(yùn)行中判斷的〕上述指令只影響如下標(biāo)志位

:CF,ZF,SF,OF,PF,AF.ADD-將第二個(gè)操作數(shù)加至第一個(gè)操作數(shù)上SUB-從第一個(gè)操作數(shù)中減去第二個(gè)操作數(shù)CMP-從第一個(gè)操作數(shù)中減去第二個(gè)操作數(shù),但只影響標(biāo)志位.AND-兩個(gè)操作數(shù)各個(gè)位邏輯與運(yùn)算。運(yùn)算法那么如下1AND1=11AND0=0

0AND1=00AND0=0只有當(dāng)兩個(gè)操作數(shù)都是1時(shí),運(yùn)算結(jié)果才是1。TEST-和上面的and操作一樣,但是只影響標(biāo)志位。OR-兩個(gè)操作數(shù)各個(gè)位邏輯或運(yùn)算。運(yùn)算法那么如下1OR1=11OR0=1

0OR1=10OR0=0如果操作數(shù)中有1那么結(jié)果一定是1。XOR-

兩個(gè)操作數(shù)各個(gè)位邏輯異或運(yùn)算。運(yùn)算法那么如下1XOR1=01XOR0=1

0XOR1=10XOR0=0當(dāng)兩個(gè)操作數(shù)不同時(shí),結(jié)果為1。第二組:MUL,IMUL,DIV,IDIV

支持如下操作數(shù):REG

memoryREG〔存放器〕:AX,BX,CX,DX,AH,AL,BL,BH,CH,CL,DH,DL,DI,SI,BP,SP.

memory〔內(nèi)存〕:[BX],[BX+SI+7],variable,etc...

MULandIMUL指令只影響CF,OF標(biāo)志位。

運(yùn)算后如果結(jié)果超出范圍,這些標(biāo)記位置1,如果沒(méi)有超過(guò)范圍,置0

DIV和IDIV指令對(duì)于標(biāo)志位無(wú)影響MUL-無(wú)符號(hào)乘:當(dāng)操作數(shù)是字節(jié)時(shí):AX=AL*操作數(shù).當(dāng)操作數(shù)是字時(shí):(DXAX)=AX*操作數(shù).IMUL-有符號(hào)乘法:當(dāng)操作數(shù)是字節(jié)時(shí):AX=AL*操作數(shù).當(dāng)操作數(shù)是字時(shí):(DXAX)=AX*

操作數(shù).DIV-無(wú)符號(hào)除法:當(dāng)操作數(shù)是字節(jié)時(shí):AL=AX/操作數(shù)AH=余數(shù)〔取模后的余數(shù)〕.當(dāng)操作數(shù)是字時(shí):

AX=(DXAX)/操作數(shù)DX=余數(shù)〔取模后的余數(shù)〕IDIV-有符號(hào)除法:當(dāng)操作數(shù)是字節(jié)時(shí):

AL=AX/操作數(shù)AH=余數(shù)〔取模后的余數(shù)〕當(dāng)操作數(shù)是字時(shí):

AX=(DXAX)/操作數(shù)DX=余數(shù)〔取模后的余數(shù)〕.第三組:INC,DEC,NOT,NEG支持如下操作數(shù):REG

memoryREG〔存放器〕:AX,BX,CX,DX,AH,AL,BL,BH,CH,CL,DH,DL,DI,SI,BP,SP.

memory〔內(nèi)存〕:[BX],[BX+SI+7],variable,etc...

INC,DEC指令只影響如下標(biāo)志位:

ZF,SF,OF,PF,AF.

NOT指令不影響任何標(biāo)志位!

NEGi指令只影響如下操作位:

CF,ZF,SF,OF,PF,AF.NOT-對(duì)與操作數(shù)每一位取反NEG-對(duì)操作數(shù)取反

實(shí)際上它對(duì)每一位取反然后在最后一位加1。例如5會(huì)變成-5,-2會(huì)變成2?!策@里所說(shuō)運(yùn)算應(yīng)當(dāng)是計(jì)算機(jī)內(nèi)部的補(bǔ)碼運(yùn)算〕程序控制轉(zhuǎn)移

對(duì)于編程來(lái)說(shuō)控制程序走向是非常重要的事情,它是你的程序根據(jù)條件作出判斷,跳轉(zhuǎn)到相應(yīng)的位值。

無(wú)條件跳轉(zhuǎn)

控制程序轉(zhuǎn)向的最根本的指令是JMP.

使用形式如下:JMPlabel在程序中聲明/label/的方法很簡(jiǎn)單,只要在它名字后面加上“:”,label可以由任何字符混合而成但是不能由數(shù)字開(kāi)頭,例如,下面是3個(gè)合法的labellabel1:

label2:

a:label可以在一條指令的前面聲明,例如:x1:

MOVAX,1

x2:MOVAX,2下面是一個(gè)JMP指令的例子:ORG100hMOVAX,5;將AX設(shè)置為5.MOVBX,2;將BX設(shè)置為2.JMPcalc;跳轉(zhuǎn)到'calc'.back:JMPstop;跳轉(zhuǎn)到'stop'.calc:ADDAX,BX;將BX加到AX.JMPback;返回'back'.stop:RET;返回操作系統(tǒng)END當(dāng)然有更簡(jiǎn)單的計(jì)算這兩個(gè)數(shù)字之和的方法,但是上面是一個(gè)JMP指令的很好的例子。從例子中可以看出,JMP可以控制向前和向后。它可以轉(zhuǎn)移到當(dāng)前代碼段的任意位置〔65535字節(jié)〕。短條件轉(zhuǎn)移與JMP這一無(wú)條件轉(zhuǎn)移指令不同,還有一些有條件跳轉(zhuǎn)指令〔只有在條件成立的時(shí)侯才跳轉(zhuǎn)〕。這些指令分為三組,第一組是只檢測(cè)單獨(dú)標(biāo)記位,第二組比擬有符號(hào)數(shù),第三組比擬無(wú)符號(hào)數(shù)。檢測(cè)單獨(dú)標(biāo)記位的轉(zhuǎn)移指令指令說(shuō)明條件相反指令JZ,JE如果為0(相等),轉(zhuǎn)移.ZF=1JNZ,JNEJC,JB,JNAE如果進(jìn)位(小于,不大于等于),轉(zhuǎn)移

CF=1JNC,JNB,JAEJS如果是負(fù)數(shù),轉(zhuǎn)移

SF=1JNSJO如果溢出,轉(zhuǎn)移

OF=1JNOJPE,JP如果是偶數(shù),轉(zhuǎn)移

PF=1JPOJNZ,JNE如果不為0〔不相等〕,轉(zhuǎn)移

ZF=0JZ,JEJNC,JNB,JAE如果沒(méi)有進(jìn)位(大于,大于等于),轉(zhuǎn)移

CF=0JC,JB,JNAEJNS如果不是負(fù)數(shù),轉(zhuǎn)移

SF=0JSJNO如果沒(méi)有溢出,轉(zhuǎn)移

OF=0JOJPO,JNP如果不是偶數(shù),轉(zhuǎn)移

PF=0JPE,JP可以看到一些指令功能相同,對(duì),他們編譯之后生成相同機(jī)器碼所以很容易理解為什么你編譯JE指令而反編譯得到的卻是JZ.使用不同的名稱(chēng)是為了使程序更容易理解。比擬有符號(hào)數(shù)的轉(zhuǎn)移指令指令說(shuō)明條件相反指令JE,JZ如果等于(=),如果為0,跳轉(zhuǎn)ZF=1JNE,JNZJNE,JNZ如果不等于(<>),如果不等于0,跳轉(zhuǎn)ZF=0JE,JZJG,JNLE如果大于(>)如果不小于等于(not<=),跳轉(zhuǎn)ZF=0

SF=OFJNG,JLEJL,JNGE如果小與JumpifLess(<)如果不大于等于(not>=),跳轉(zhuǎn)SF<>OFJNL,JGEJGE,JNL如果大于等于(>=),如果不小于(not<),跳轉(zhuǎn)SF=OFJNGE,JLJLE,JNG如果小于等于(<=),如果不大于(not>),跳轉(zhuǎn)ZF=1或者SF<>OFJNLE,JG

<>-符號(hào)表示不等于.比擬無(wú)符號(hào)數(shù)轉(zhuǎn)移指令指令說(shuō)明條件相反指令JE,JZ如果等于(=).,如果為0,跳轉(zhuǎn)ZF=1JNE,JNZJNE,JNZ如果不等于(<>),如果不為0,跳轉(zhuǎn)ZF=0JE,JZJA,JNBE如果大于(>),如果不小于等于(not<=),跳轉(zhuǎn)CF=0

and

ZF=0JNA,JBEJB,JNAE,JC如果小于(<),如果不大于等于(not>=),如果進(jìn)位,跳轉(zhuǎn)CF=1JNB,JAE,JNCJAE,JNB,JNC如果大于等于(>=),如果不小于(not<),如果沒(méi)有進(jìn)位,跳轉(zhuǎn)CF=0JNAE,JBJBE,JNA如果小于或者等于(<=),如果不大于(not>),跳轉(zhuǎn)CF=1or

ZF=1JNBE,JA一般來(lái)說(shuō),需要使用CMP指令來(lái)比擬數(shù)值〔該指令與SUB(減法)指令相近,只不過(guò)不保存結(jié)果,而只修改標(biāo)值位〕上面說(shuō)法的意思是,例如:需要比擬5和2,5-2=3結(jié)果不是0〔0標(biāo)值位設(shè)置為0〕另一個(gè)例子比擬7和77-7=0結(jié)果為0!(0標(biāo)值位設(shè)置為1。JZ或者JE會(huì)轉(zhuǎn)移).

下面是一個(gè)CMP指令和條件轉(zhuǎn)移指令的例子:ORG100hMOVAL,25;設(shè)置AL為25.MOVBL,10;設(shè)置BL為10.CMPAL,BL;比擬AL-BL.JEequal;如果AL=BL(ZF=1)跳轉(zhuǎn)PUTC'N';如果到這里,說(shuō)明AL<>BL,JMPstop;打印'N',跳轉(zhuǎn)到結(jié)束equal:;如果到這里PUTC'Y';那么AL=BL,打印'Y'.stop:RETEND請(qǐng)用用不同的數(shù)字試驗(yàn)取代上述AL和BL,點(diǎn)擊[FLAGS]鍵翻開(kāi)標(biāo)志,使用[SingleStep]觀(guān)察發(fā)生了什么,不要忘記每一次修改之后重新編譯運(yùn)行〔快捷鍵F5〕。全部的條件轉(zhuǎn)移指令都有一個(gè)很大的限制,就是與JMP指令不同,他們只能向前跳轉(zhuǎn)127字節(jié)或者向后跳轉(zhuǎn)128字節(jié)〔注意大多數(shù)指令編譯之后是3個(gè)或者更多字節(jié)〕我們可以用如下小技巧解決這一問(wèn)題:

從上述表中找到一條相反條件的轉(zhuǎn)移指令,令其跳轉(zhuǎn)到label_x.用JMP指令跳轉(zhuǎn)到你想要的地方在JMP指令后面定義label_x:label_x:-可以是任意合法標(biāo)號(hào).

下面是一個(gè)例子:includeemuORG100hMOVAL,25;設(shè)置AL為25.MOVBL,10;設(shè)置BL為10.CMPAL,BL;比擬AL-BL.JNEnot_equal;如果AL<>BL(ZF=0),轉(zhuǎn)移JMPequalnot_equal:;假定這里還有編譯之后超過(guò)127字節(jié)的程序PUTC'N';如果執(zhí)行到這里,說(shuō)明AL<>BL,JMPstop;打印'N',轉(zhuǎn)移到程序結(jié)束。equal:;如果執(zhí)行到這里,PUTC'Y';說(shuō)明AL=BL,打印'Y'.stop:RET;上述都要執(zhí)行這一條END另外,可以使用立即數(shù)來(lái)代替標(biāo)號(hào)。立即數(shù)前使用“$”編譯器將直接得到偏移。例如:ORG100h;無(wú)條件向前轉(zhuǎn)移;跳過(guò)后面2字節(jié)JMP$2aDB3;1byte.bDB4;1byte.;JCC跳過(guò)7字節(jié):;(JMP本身占用2字節(jié))MOVBL,9DECBL;2bytes.CMPBL,0;3bytes.JNE$-7RETEND堆棧

堆棧是內(nèi)存中用于保存臨時(shí)數(shù)據(jù)的一片區(qū)域.當(dāng)使用CALL指令時(shí),堆棧用于保存過(guò)程的返回地址,RET指令能夠從堆棧中取得該地址并使程序返回到那里。當(dāng)使用INT指令,發(fā)生的也與此類(lèi)似。

堆棧保存標(biāo)志存放器,代碼段和偏移量。IRET指令用來(lái)從中斷返回。我們同樣可以使用堆棧保存任何數(shù)據(jù)。對(duì)于堆棧的操作只有兩條:

PUSH-將16位數(shù)值壓入堆棧.POP-將16位數(shù)值從堆棧中彈出PUSH指令的使用方法:PUSHREG

PUSHSREG

PUSHmemory

PUSHimmediateREG〔存放器〕:AX,BX,CX,DX,DI,SI,BP,SP.

SREG〔段存放器〕:DS,ES,SS,CS.

memory〔內(nèi)存〕:[BX],[BX+SI+7],16位變量,等等...

immediate〔立即數(shù)〕:5,-24,3Fh,10001101b,等等...POP指令的使用方法:POPREG

POPSREG

POPmemoryREG〔存放器〕:AX,BX,CX,DX,DI,SI,BP,SP.

SREG〔段存放器〕:DS,ES,SS,(除了CS).

memory〔內(nèi)存〕:[BX],[BX+SI+7],16位變量,等等...注意:PUSHandPOP都只操作16位數(shù)據(jù)!注意:在80186其極以后的CPU中才能使用PUSH立即數(shù)這樣的指令堆棧使用LIFO〔后進(jìn)先出〕算法,意思是:參加我們按照如下順序壓入數(shù)值:1,2,3,4,5

再使用POP指令彈出,結(jié)果將是54321

注意,有多少條PUSH指令就要對(duì)應(yīng)有多少條POP指令,否那么堆棧會(huì)被占用,無(wú)法正確返回操作系統(tǒng)。前面講過(guò)使用RET指令返回操作系統(tǒng),所以在程序開(kāi)始時(shí)會(huì)將返回地址壓入堆棧〔通常都是0000h〕I

PUSH和POP指令在我們存放器不夠用的時(shí)侯特別有用,我們有如下技巧:將存放器原始數(shù)值存入堆?!彩褂肞USH〕使用存放器從堆棧中彈出存放器原先數(shù)值再放入存放器〔使用POP〕下面是一個(gè)例子:ORG100hMOVAX,1234hPUSHAX;將AX存入堆棧.MOVAX,5678h;修改AX值POPAX;返回AX原先的值RETEND堆棧的另外一個(gè)作用是交換數(shù)值,下面是一個(gè)這樣的例子:ORG100hMOVAX,1212h;將1212h存入AX.MOVBX,3434h;將3434h存入BXPUSHAX;將AX數(shù)值存入堆棧.PUSHBX;將BX數(shù)值存入堆棧POPAX;BX原值存入AXPOPBX;AX原值存入BXRETEND之所以能這樣是因?yàn)槎褩J怯肔IFO〔后進(jìn)先出〕算法,當(dāng)我們壓入1212h和3434h之后,使用pop彈出我們首先得到的是3434h然后才是1212h堆棧的內(nèi)存區(qū)域由SS存放器〔堆棧段〕,SP存放器〔棧指針〕設(shè)置設(shè)置。一般來(lái)說(shuō)操作系統(tǒng)在程序開(kāi)始時(shí)會(huì)設(shè)置這些。"PUSH源"

指令做如下工作:將SP存放器減2將源的值寫(xiě)入內(nèi)存SS:SP地址處"POP目的"

指令做如下工作:內(nèi)存SS:SP地址處數(shù)值寫(xiě)入目的將SP存放器加2由SS:SP指出的地址稱(chēng)作堆棧頂

對(duì)于COM文件,堆棧段通常就是代碼段,堆棧指針設(shè)置為

0FFFEh.在地址SS:0FFFEh處存放程序結(jié)束時(shí)RET指令返回地址。你可以點(diǎn)擊[stack]按鈕直接觀(guān)察堆棧操作。堆棧頂由“<”符號(hào)標(biāo)記。宏宏與過(guò)程很相似,但并不是完全相似。宏看起來(lái)像過(guò)程,但是當(dāng)你的代碼編譯完成之后就消失了,取而代之的是真正的代碼。如果你聲明一個(gè)宏,而在代碼中從來(lái)沒(méi)有調(diào)用,編譯器在編譯過(guò)程中將忽略它。宏的定義:nameMACRO[參數(shù),]<指令>ENDM與過(guò)程不同,宏要求定義參數(shù)并使用。例如:MyMacroMACROp1,p2,p3MOVAX,p1MOVBX,p2MOVCX,p3ENDMORG100hMyMacro1,2,3MyMacro4,5,DXRET上述代碼在編譯過(guò)程中將展開(kāi)成:MOVAX,00001h

MOVBX,00002h

MOVCX,00003h

MOVAX,00004h

MOVBX,00005h

MOVCX,DX關(guān)于宏與過(guò)程需要注意如下要點(diǎn):當(dāng)你想使用一個(gè)過(guò)程,你應(yīng)該使用CALL指令,例如:CALLMyProc當(dāng)你想使用一個(gè)宏,你只需要輸入它的名稱(chēng)。例如:MyMacro過(guò)程是存在于內(nèi)存中某一特定位值的,即使你調(diào)用這個(gè)過(guò)程100次,cpu只是執(zhí)行內(nèi)存中這一段的代碼。在遇到

RET指令后還會(huì)回到調(diào)用該過(guò)程的位值。這是通過(guò)使用堆棧保存返回地址來(lái)實(shí)現(xiàn)的。CALL指令占用3字節(jié),無(wú)論調(diào)用多少次過(guò)程,最終輸出的可執(zhí)行文件并不會(huì)因此而顯著增大。宏會(huì)在程序代碼中展開(kāi)。如果你使用相同的宏100次,輸出的可執(zhí)行文件將會(huì)變得越來(lái)越大,因?yàn)槊恳淮握{(diào)用宏中的指令都會(huì)插入到調(diào)用宏的位值。你可以使用堆棧或者通用存放器來(lái)向過(guò)程傳遞參數(shù)向宏傳遞參數(shù)的方法是在宏名稱(chēng)后面直接接上參數(shù)。例如:MyMacro1,2,3用ENDM指令結(jié)束宏就足夠了標(biāo)記過(guò)程結(jié)束,你需要在ENDP指令前加上過(guò)程名稱(chēng)宏會(huì)直接在代碼中展開(kāi),因此,如果你在宏中使用標(biāo)記,當(dāng)宏被調(diào)用2次或兩次以上的時(shí)侯就會(huì)出現(xiàn)

"Duplicatedeclaration"〔重復(fù)定義〕這一錯(cuò)誤。為了防止該錯(cuò)誤

在變量,標(biāo)記或者過(guò)程名稱(chēng)之前加上“l(fā)ocal”指令。例如:MyMacro2MACRO LOCALlabel1,label2 CMPAX,2 JElabel1 CMPAX,3 JElabel2 label1: INCAX label2: ADDAX,2ENDMORG100hMyMacro2MyMacro2RETI假設(shè)過(guò)你打算在很多程序中使用宏,將所有的宏存放在一個(gè)文件中不失為一個(gè)好方法。將那個(gè)文件放在INC目錄下,使用INCLUDE文件名就可以在你的程序中調(diào)用宏了。makingyourownoperatingsystemUsually,whenacomputerstartsitwilltrytoloadthefirst512-bytesector(that'sCylinder0,Head0,Sector1)fromanydisketteinyourA:drivetomemorylocation0000h:7C00handgiveitcontrol.Ifthisfails,theBIOStriestousetheMBRofthefirstharddriveinstead.

Thistutorialcoversbootingupfromafloppydrive,thesameprinciplesareusedtobootfromaharddrive.Butusingafloppydrivehasseveraladvantages:youcankeepyourexistingoperatingsystemintact(windows,dos,linux,unix,be-os...).itiseasyandsafetomodifythebootrecordofafloppydisk.

exampleofasimplefloppydiskbootprogram:;directivetocreateBOOTfile:#make_boot#;Bootrecordisloadedat0000:7C00,;soinformcompilertomakerequired;corrections:ORG7C00hPUSHCS;makesureDS=CSPOPDS;loadmessageaddressintoSIregister:LEASI,msg;teletypefunctionid:MOVAH,0Ehprint:MOVAL,[SI]CMPAL,0JZdoneINT10h;printusingteletype.INCSIJMPprint;waitfor'anykey':done:MOVAH,0INT16h;storemagicvalueat0040h:0072h:;0000h-coldboot.;1234h-warmboot.MOVAX,0040hMOVDS,AXMOVw.[0072h],0000h;coldboot.JMP 0FFFFh:0000h ;reboot!new_lineEQU13,10msgDB'HelloThisisMyFirstBootProgram!'DBnew_line,'Pressanykeytoreboot',0

copytheaboveexampletothesourceeditorandpressemulate.theemulatorautomaticallyloads.binfileto0000h:7C00h(itusessupplementary.binffiletoknowwheretoload).

youcanrunitjustlikearegularprogram,oryoucanusethevirtualdrivemenutowrite512bytesat7c00htobootsectorofavirtualfloppydrive(it's"FLOPPY_0"fileinEmulator'sfolder).afteryourprogramiswrittentothevirtualfloppydrive,youcanselectbootfromfloppyfromvirtualdrivemenu.

.binfilesforbootrecordsarelimitedto512bytes(sectorsize).ifyournewoperatingsystemisgoingtogrowoverthissize,youwillneedtouseabootprogramtoloaddatafromothersectors(justlikedoes).anexampleofatinyoperatingsystemcanbefoundinc:\emu8086\examples:

Tocreateextensionsforyouroperatingsystem(over512bytes),youcanuseadditionalsectorsofafloppydisk.It'srecommendedtouse".bin"filesforthispurpose(tocreate".bin"fileselect"BINTemplate"from"File"->"New"menu).

Towrite".bin"filetovirtualfloppy,select"Write.binfiletofloppy..."from"Virtualdrive"menuofemulator,youshouldwriteitanywherebutthebootsector(whichisCylinder:0,Head:0,Sector:1).

youcanusethisutilitytowrite.binfilestovirtualfloppydisk("FLOPPY_0"file),insteadof"write512bytesat7c00htobootsector"menu.however,youshouldrememberthat.binfilethatisdesignedtobeabootrecordshouldalwaysbewrittentocylinder:0,head:0,sector:1BootSectorLocation:Cylinder:0

Head:0

Sector:1

towrite.binfilestorealfloppydiskusewritebin.asm,justcompileittocomfileandrunitfromcommandprompt.towriteabootrecordtype:;towritekernelmoduletype:writebinkernel.bin/k/k-parametertellstheprogramtowritethefileatsector2insteadofsector1.itdoesnotmatterinwhatorderyouwritethefilesontofloppydrive,butitdoesmatterwhereyouwritethem.mote:thisbootrecordisnotMS-DOS/Windowscompatiblebootsector,it'snotevenLinuxorUnixcompatible,operatingsystemmaynotallowyoutoreadorwritefilesonthisdisketteuntilyoure-formatit,thereforemakesurethedisketteyouusedoesn'tcontainanyimportantinformation.howeveryoucanwriteandreadanythingtoandfromthisdiskusinglowleveldiskaccessinterrupts,it'sevenpossibletoprotectvaluableinformationfromtheothersthisway;evenifsomeonegetsthediskhewillprobablythinkthatit'semptyandwillreformatitbecauseit'sthedefaultoptioninwindowsoperatingsystem...suchagoodtypeofselfdestructingdatacarrier:)

idealizedfloppydriveanddiskettestructure:

fora1440kbdiskette:floppydiskhas2sides,andthereare2heads;oneforeachside(0..1),thedriveheadsmoveabovethesurfaceofthediskoneachside.eachsidehas80cylinders(numbered0..79).eachcylinderhas18sectors(1..18).eachsectorhas512bytes.totalsizeoffloppydiskis:2x80x18x512=1,474,560bytes.note:theMS-DOS(windows)formattedfloppydiskhasslightlylessfreespaceonit(byabout16,896bytes)becausetheoperatingsystemneedsplacetostorefilenamesanddirectorystructure(oftencalledFATorfilesystemallocationtable).morefilenames-lessdiskspace.themostefficientwaytostorefilesistowritethemdirectlytosectorsinsteadofusingfilesystem,andinsomecasesitisalsothemostreliableway,ifyouknowhowtouseit.

toreadsectorsfromfloppydriveuseINT13h/AH=02h.ControllingExternalDevices

Thereare7devicesattachedtotheemulator:trafficlights,stepper-motor,LEDdisplay,thermometer,printer,robotandsimpletestdevice.Youcanviewdeviceswhenyouclick"VirtualDevices"menuoftheemulator.

FortechnicalinformationseeI/Oportssectionofemu8086reference.

Ingeneral,itispossibletouseanyx86familyCPUtocontrolallkindofdevices,thedifferencemaybeinbaseI/Oportnumber,thiscanbealteredusingsometrickyelectronicequipment.Usuallythe".bin"fileiswrittenintotheReadOnlyMemory(ROM)chip,thesystemreadsprogramfromthatchip,loadsitinRAMmoduleandrunstheprogram.Thisprincipleisusedformanymoderndevicessuchasmicro-waveovensandetc...TrafficLights

Usuallytocontrolthetrafficlightsanarray(table)ofvaluesisused.Incertainperiodsoftimethevalueisreadfromthearrayandsenttoaport.Forexample:;controllingexternaldevicewith8086microprocessor.;realistictestforc:\emu8086\devices\Traffic_Lights.exe#start=Traffic_Lights.exe#name"traffic"movax,all_redout4,axmovsi,offsetsituationnext:movax,[si]out4,ax;wait5seconds(5millionmicroseconds)movcx,4Ch;004C4B40h=5,000,000movdx,4B40hmovah,86hint15haddsi,2;nextsituationcmpsi,sit_endjbnextmovsi,offsetsituationjmpnext;FEDC_BA98_7654_3210situationdw0000_0011_0000_1100bs1dw0000_0110_1001_1010bs2dw0000_1000_0110_0001bs3dw0000_1000_0110_0001bs4dw0000_0100_1101_0011bsit_end=$all_redequ0000_0010_0100_1001bStepper-Motor

Themotorcanbehalfsteppedbyturningonpairofmagnets,followedbyasingleandsoon.

Themotorcanbefullsteppedbyturningonpairofmagnets,followedbyanotherpairofmagnetsandintheendfollowedbyasinglemagnetandsoon.Thebestwaytomakefullstepistomaketwohalfsteps.

Halfstepisequaltodegrees.

Fullstepisequaltodegrees.

Themotorcanbeturnedbothclock-wiseandcounter-clock-w

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論