《微機(jī)原理與接口技術(shù)》課件第4章_第1頁
《微機(jī)原理與接口技術(shù)》課件第4章_第2頁
《微機(jī)原理與接口技術(shù)》課件第4章_第3頁
《微機(jī)原理與接口技術(shù)》課件第4章_第4頁
《微機(jī)原理與接口技術(shù)》課件第4章_第5頁
已閱讀5頁,還剩204頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第4章

匯編語言程序設(shè)計(jì)4.1匯編語言程序格式4.2偽指令4.3匯編語言源程序結(jié)構(gòu)4.4匯編語言程序的上機(jī)過程4.5匯編語言程序設(shè)計(jì)4.6程序設(shè)計(jì)舉例習(xí)題4

4.1

匯編語言程序格式

4.1.1匯編語言的程序結(jié)構(gòu)

1)?指令語句

指令語句是功能性語句,由Intel8086/8088CPU提供的指令形成,實(shí)現(xiàn)一定的操作功能,能夠被編譯成機(jī)器代碼。

2)?偽指令語句

偽指令語句也叫指示性語句,只是為匯編程序在翻譯匯編語言源程序時(shí)提供有關(guān)信息,并不產(chǎn)生機(jī)器代碼。

程序中的語句:

BUF1DB34H

BUF2DB2AH

SUMDB ?

就是偽指令語句,其功能是在內(nèi)存中開辟3個(gè)名字分別為BUF1、BUF2、SUM的字節(jié)存儲單元,前兩個(gè)單元的初值分別為34H和2AH,SUM僅指定一個(gè)字節(jié)的空單元,并不定義確定的初值。

實(shí)際上,匯編語言源程序中還可出現(xiàn)宏指令語句或系統(tǒng)調(diào)用。宏指令語句就是由若干條指令語句形成的語句體,編譯時(shí)被展開。一條宏指令語句的功能相當(dāng)于若干條指令語句的功能。系統(tǒng)調(diào)用是直接調(diào)用操作系統(tǒng)提供的專用子程序。

4.1.2匯編語言的語句格式

指令語句和偽指令語句的格式是類似的,格式如下:

[名字]指令助記符,操作數(shù)[;注釋]

其中,帶方括號的項(xiàng)可以省略,注釋內(nèi)容以分號(;)引導(dǎo)。

1.名字

1)名字的標(biāo)識符

名字也就是由用戶按一定規(guī)則定義的標(biāo)識符,可由下列符號組成:

(1)英文字母(A~Z,a~z);

(2)數(shù)字(0~9);

(3)特殊符號(?、@、_等)。

2)名字的定義規(guī)則

(1)數(shù)字不能作為名字的第一個(gè)符號;

(2)單獨(dú)的問號(

??)不能作為名字;

(3)一個(gè)名字的最大有效長度為31,超過31的部分計(jì)算機(jī)不再識別;

(4)匯編語言中有特定含義的保留字,如操作碼、寄存器名等,不能作為名字使用。

為了便于記憶,名字應(yīng)該做到見名知意,如用BUFFER表示緩沖區(qū),用SUM表示累加和等。

3)名字的兩種主要形式

名字有標(biāo)號和變量兩種主要形式。

(1)標(biāo)號。標(biāo)號在代碼段中定義,后面跟著冒號“:”,它也可以用LABEL或EQU偽操作來定義。此外,它還可以作為子程序名定義,由于子程序由偽指令定義,因此子程序名不需冒號說明。標(biāo)號經(jīng)常在轉(zhuǎn)移指令或CALL指令的操作數(shù)字段出現(xiàn),用以表示轉(zhuǎn)向地址。

標(biāo)號有三種屬性:段基值、段內(nèi)偏移量(或相對地址)和類型屬性。

段基值(SEG)屬性:是標(biāo)號所在邏輯段的段基值,即段起始地址的前16位。此值必須在一個(gè)段寄存器中,而標(biāo)號的段則總是在CS寄存器中。

段內(nèi)偏移量(OFFSET)屬性:是標(biāo)號距離段起始地址的字節(jié)數(shù),對于16位段是16位無符號數(shù),對于32位段則是32位無符號數(shù)。

類型(TYPE)屬性:類型表示該標(biāo)號所代表的指令的轉(zhuǎn)移范圍,分為NEAR和FAR兩種。如果為NEAR型,則標(biāo)號只能在段內(nèi)引用;如果為FAR型,則標(biāo)號可以在段間引用。

(2)變量。變量在數(shù)據(jù)段、附加數(shù)據(jù)段或堆棧段中定義,后面不跟冒號。它也可以用LABEL或EQU偽操作來定義。變量經(jīng)常在操作數(shù)字段出現(xiàn)。它也有段、偏移及類型三種屬性。

段屬性:定義變量的段起始地址,此值必須在一個(gè)段寄存器中。

偏移屬性:變量的偏移地址是從段的起始地址到定義變量的位置之間的字節(jié)數(shù)。對于16位段,是16位無符號數(shù);對于32位段,則是32位無符號數(shù)。在當(dāng)前段內(nèi)給出變量的偏移值等于當(dāng)前地址計(jì)數(shù)器的值,當(dāng)前地址計(jì)數(shù)器的值可以用$來表示。

類型屬性:變量的類型屬性定義該變量所保留的字節(jié)數(shù),如BYTE(DB,1個(gè)字節(jié)長)、WORD(DW,2個(gè)字節(jié)長)、DWORD(DD,4個(gè)字節(jié)長)、FWORD(DF,6個(gè)字節(jié)長)、QWORD(DQ,8個(gè)字節(jié)長)、TBYTE(DT,10個(gè)字節(jié)長)。

在同一個(gè)程序中,同樣的標(biāo)號或變量的定義只允許出現(xiàn)一次,否則匯編程序會指示出錯(cuò)。

2.指令助記符

指令助記符用來指明不同的操作指令,如MOV、ADD等都是指令助記符。

3.操作數(shù)

指令中的操作數(shù)是指令執(zhí)行的對象。對于一般指令,可以有一個(gè)或兩個(gè)操作數(shù),也可以沒有操作數(shù);對于偽指令和宏指令,可以有多個(gè)操作數(shù)。當(dāng)操作數(shù)多于一個(gè)時(shí),操作數(shù)之間用逗號分開。操作數(shù)可以是常數(shù)或表達(dá)式。

1)常數(shù)

(1)數(shù)值常數(shù)。匯編語言中的數(shù)值常數(shù)可以是二進(jìn)制、八進(jìn)制、十進(jìn)制或十六進(jìn)制數(shù),書寫時(shí)用加后綴(如B、O或Q、D、H)的方式標(biāo)明即可。對于十進(jìn)制數(shù),可以省掉后綴。對于十六進(jìn)制數(shù),當(dāng)以A~F開頭時(shí),前面要加數(shù)字0,以避免和名字混淆,如十六進(jìn)制數(shù)A6H,應(yīng)該寫成0A6H,否則容易和名字A6H相混。

(2)字符串常數(shù)。包含在單引號中的若干個(gè)字符即形成字符串常數(shù)。字符串在計(jì)算機(jī)中存儲的是相應(yīng)字符的ASCII碼,如‘A’的值是41H,‘AB’的值是4142H等。

(3)符號常數(shù)。用符號名來代替的常數(shù)就是符號常數(shù),如COUNTEQU3或COUNT=3定義后COUNT就是一個(gè)符號常數(shù),與數(shù)值常數(shù)3等價(jià)。

2)表達(dá)式

由運(yùn)算對象和運(yùn)算符組成的合法式子就是表達(dá)式。表達(dá)式分為數(shù)值表達(dá)式、關(guān)系表達(dá)式、邏輯表達(dá)式和地址表達(dá)式等。

(1)算術(shù)運(yùn)算符。算術(shù)運(yùn)算符有:+

(加)、-

(減)、*?(乘)、/?(除)、MOD?(取余除)。

算術(shù)運(yùn)算符可以用于數(shù)值表達(dá)式和地址表達(dá)式中,用于計(jì)算數(shù)據(jù)或地址的結(jié)果。下面的兩條指令是正確的:

MOV

AL,4*8+5 ;數(shù)值表達(dá)式

MOV

SI,OFFSET

BUF+12 ;地址表達(dá)式

(2)邏輯運(yùn)算符。邏輯運(yùn)算符有:AND(與)、OR(或)、XOR(異或)、NOT(非)。

邏輯運(yùn)算符只能用于數(shù)值表達(dá)式中,不能用于地址表達(dá)式中,其運(yùn)算結(jié)果為“真”或“假”。邏輯運(yùn)算符和邏輯運(yùn)算指令是有區(qū)別的。邏輯運(yùn)算符的功能在匯編階段完成,邏輯運(yùn)算指令的功能在程序執(zhí)行階段完成。

在匯編階段,指令A(yù)ND AL,78HAND0FH等價(jià)于指令A(yù)ND

AL,08H。

(3)關(guān)系運(yùn)算符。關(guān)系運(yùn)算符有:EQ(相等)、LT(小于)、LE(小于等于)、GT(大于)、GE(大于等于)、NE(不等于)。

關(guān)系運(yùn)算符要有兩個(gè)運(yùn)算對象。兩個(gè)運(yùn)算對象要么都是數(shù)值,要么都是同一個(gè)段內(nèi)的地址,其運(yùn)算結(jié)果為“真”或“假”。結(jié)果為真時(shí),表示為0FFFFH;運(yùn)算結(jié)果為假時(shí),表示為0000H。

指令MOVBX,32EQ45等價(jià)于MOVBX,0。

指令MOVBX,56GT30等價(jià)于MOVBX,0FFFFH。

4.注釋

注釋是語句的說明部分,用來說明一條指令或一段程序的功能,由分號(;)開始,適當(dāng)?shù)丶有┳⑨寖?nèi)容,可以增加程序的可讀性,便于閱讀、理解和修改程序。匯編源程序時(shí),注釋部分不產(chǎn)生機(jī)器代碼。

一條語句可以寫在多行上,續(xù)行符使用&。

4.1.3匯編語言的運(yùn)算符

1.分析運(yùn)算符

分析運(yùn)算符的運(yùn)算對象是存儲器操作數(shù),即由變量名或標(biāo)號形成的地址表達(dá)式,運(yùn)算結(jié)果是一個(gè)數(shù)值。運(yùn)算符的格式為

運(yùn)算符地址表達(dá)式

(1)?SEG和OFFSET運(yùn)算符。SEG運(yùn)算符返回變量或標(biāo)號所在段的段基值,OFFSET運(yùn)算符返回變量或標(biāo)號的段內(nèi)偏移量。

例如,若VAR是一個(gè)已經(jīng)定義的變量,它所在的邏輯段的段基址是3142H,它在該段的偏移量是120H,那么指令

MOVAX,SEGVAR

MOVBX,OFFSETVAR

就等價(jià)于:

MOVAX,3142H

MOVBX,120H或LEABX,VAR

(2)?TYPE運(yùn)算符。TYPE運(yùn)算符返回變量或標(biāo)號的類型屬性值。對于各種類型的變量和標(biāo)號,它們對應(yīng)的屬性值如表4-1所示。

(3)?LENGTH運(yùn)算符和SIZE運(yùn)算符。

LENGTH運(yùn)算符返回變量數(shù)據(jù)區(qū)分配的數(shù)據(jù)項(xiàng)總數(shù)。SIZE運(yùn)算符返回變量數(shù)據(jù)區(qū)分配的字節(jié)個(gè)數(shù)。例如,若有如下的數(shù)據(jù)定義:

DAT1DB20H,48

DAT2DW5DUP(2,4)

那么,對于下邊的指令語句,它所完成的操作如注釋所示。

MOVAL,TYPEDAT1 ;AL←1

MOVAH,LENGTHDAT1 ;AH←1

MOVBL,SIZEDAT1 ;BL←1

MOVBH,TYPEDAT2 ;BH←2

MOVCL,LENGTHDAT2 ;CL←5

MOVCH,SIZEDAT2 ;CH←20

2.組合運(yùn)算符

組合運(yùn)算符有PTR和THIS兩個(gè)運(yùn)算符。

(

1)?PTR運(yùn)算符。PTR運(yùn)算符的功能是對已分配的存儲器地址臨時(shí)賦予另一種類型屬性,但不改變操作數(shù)本身的類型屬性,同時(shí)保留存儲器地址的段基址和段內(nèi)偏移量的屬性。它的使用格式如下:

類型PTR地址表達(dá)式

其中,地址表達(dá)式部分可以是標(biāo)號、變量或各種尋址方式構(gòu)成的存儲器地址。對于標(biāo)號,可以設(shè)置的類型有NEAR和FAR;對于變量,可以設(shè)置的類型有BYTE、WORD和DWORD。

例如:

MOVWORDPTR[BX],AX ;

將BX所指存儲單元臨時(shí)設(shè)置為字類型

MOVBYTEPTRDAT,AL ;

將變量DAT臨時(shí)設(shè)置為字節(jié)類型

JMPFARPTRLPT ;

將標(biāo)號LPT臨時(shí)設(shè)置為遠(yuǎn)類型

(2)?THIS運(yùn)算符。THIS運(yùn)算符用來定義一個(gè)新類型的變量或標(biāo)號。但它只指定變量或標(biāo)號的類型屬性,并不為它分配存儲區(qū),它的段屬性和偏移屬性與下一條可分配地址的變量或標(biāo)號屬性相同。

格式:THIS類型

其類型選項(xiàng)與PTR運(yùn)算符相同。例如:

LABEQUTHISBYTE ;EQU是賦值偽指令,它將表達(dá)式的值賦給標(biāo)號或變量

LAWDW2341H

MOVBL,LAB ;(BL)←41H

MOVAX,LAW ;(AX)←2341H

在這里,變量LAB和LAW具有相同的段基址和偏移地址,但LAB是字節(jié)類型,而LAW是字類型。

3.分離運(yùn)算符

(1)?

LOW運(yùn)算符。

格式:LOW表達(dá)式

功能:取表達(dá)式的低字節(jié)返回。

(2)?

HIGH運(yùn)算符。

格式:HIGH表達(dá)式

功能:取表達(dá)式的高字節(jié)返回。

例如:

MOVAL,LOW2238H ;AL←38H

MOVAH,HIGH2238H ;AH←22H

4.2偽指令

匯編語言程序的語句除指令以外還可以由偽操作和宏指令組成。偽操作又稱為偽指令,它們不像機(jī)器指令那樣是在程序運(yùn)行期間由計(jì)算機(jī)來執(zhí)行的,而是在匯編程序?qū)υ闯绦騾R編期間由匯編程序處理的,它們可以完成如處理器選擇、定義程序模式、定義數(shù)據(jù)、分配存儲區(qū)、指示程序結(jié)束等功能。偽指令形式上與一般指令相似,但偽指令只是為匯編程序提供有關(guān)信息,不產(chǎn)生相應(yīng)的機(jī)器代碼。

4.2.1定義符號的偽指令

有時(shí)程序中多次出現(xiàn)同一個(gè)表達(dá)式,為方便起見,可以用賦值偽操作給表達(dá)式賦予一個(gè)名字。

1.等值偽指令EQU

格式:

<符號名>

EQU<表達(dá)式>

功能:給符號名定義一個(gè)值,賦予一個(gè)符號名、表達(dá)式或助記符。

此后,程序中凡需要用到該表達(dá)式之處,就可以用表達(dá)式名來代替了。可見,EQU的引入提高了程序的可讀性,也使其更加易于修改。上式中的表達(dá)式可以是任何有效的操作數(shù)格式,可以是任何可以求出常數(shù)值的表達(dá)式,也可以是任何有效的助記符。舉例如下:CONSTANT EQU 256;將數(shù)256賦以符號名CONSTANTDATA EQU HEIGHT+12;HEIGHT為一標(biāo)號,地址表達(dá)式賦以符號名DATAALPHA EQU 7BETA EQU ALPHA-2;這是一組賦值偽操作,把7-2=5賦以符號名BETAADDR EQU VAR+BETA ;將VAR+5賦以符號名ADDRB EQU [BP+8] ;變址引用賦以符號名BP8 EQU DS:[BP+8] ;加段前綴的變址引用賦以符號名P8

在EQU語句的表達(dá)式中,如果有變量或標(biāo)號的表達(dá)式,則在該語句前應(yīng)該先給出它們的定義。例如,語句

ABEQU

DATA_ONE+2

必須放在DATA_ONE的定義之后,否則匯編程序?qū)⒅甘境鲥e(cuò)。

2.等號偽指令“=”

還有一個(gè)與EQU相類似的“=”偽操作也可以作為賦值操作使用。它們之間的區(qū)別是:EQU偽操作中的表達(dá)式名是不允許重復(fù)定義的,而“=”偽操作則允許重復(fù)定義。

例如,EMP

=

6或EMPEQU6都可以使數(shù)6賦給符號名EMP,然而不允許兩者同時(shí)使用。但是,語句

EMP=7

EMP=EMP+1

在程序中是允許使用的,因?yàn)椤?”偽操作允許重復(fù)定義。這種情況下,在第一個(gè)語句后的指令中,EMP的值為7;而在第二個(gè)語句后的指令中,EMP的值為8。

3.解除定義偽指令PURGE

格式:

PURGE<符號1,符號2,…,符號N>

功能:解除指定符號的定義。解除符號定義后,可用EQU重新進(jìn)行定義。例如:

Y1 EQU 7 ;定義Y1的值為7

PURGE Y1 ;解除Y1的定義

Y1 EQU 36 ;重新定義Y1的值為36

4.2.2定義數(shù)據(jù)的偽指令

這一類偽指令的格式是:

[變量]助記符操作數(shù),…,操作數(shù)[;注釋]

功能:為操作數(shù)分配存儲單元,并用變量與存儲單元建立聯(lián)系。

其中,變量是可有可無的,它用符號地址表示,其作用與指令語句前的標(biāo)號相同,但它的后面不跟冒號。如果語句中有變量名,則匯編程序使其記為第一個(gè)字節(jié)的偏移地址。注釋字段用來說明該偽指令的功能,它也是可有可無的。偽指令格式中的助記符是用來說明所定義的數(shù)據(jù)類型。

這一類偽指令的助記符有以下幾種:

(1)?DB用來定義字節(jié),其后的每個(gè)操作數(shù)都占有一個(gè)字節(jié)(8位)的存儲單元。

(2)

DW用來定義字,其后的每個(gè)操作數(shù)占有一個(gè)字(16位,其低位字節(jié)在第一個(gè)字節(jié)地址中,高位字節(jié)在第二個(gè)字節(jié)地址中)。

(3)

DD用來定義雙字(4個(gè)字節(jié)),其后的每個(gè)操作數(shù)占有兩個(gè)字(32位)。

(4)

DF用來定義3字(6個(gè)字節(jié)),其后的每個(gè)操作數(shù)占有48位,可用來存放遠(yuǎn)地址。這一偽指令只能用于386及其后繼機(jī)型中。

(5)

DQ用來定義4字(8個(gè)字節(jié)),其后的每個(gè)操作數(shù)占有4個(gè)字(64位),可用來存放雙精度浮點(diǎn)數(shù)。

(6)?DT用來定義5字(10個(gè)字節(jié)),其后的每個(gè)操作數(shù)占有5個(gè)字,形成壓縮的BCD碼形式。

這些偽指令可以把其后跟著的數(shù)據(jù)存入指定的存儲單元,形成初始化數(shù)據(jù),或者只分配存儲空間而并不存入確定的數(shù)值,形成未初始化數(shù)據(jù)空間。DW和DD偽指令還可存儲地址,DF偽指令則可存儲由16位段地址及32位偏移地址組成的遠(yuǎn)地址指針。下面舉例說明。

【例4-2】操作數(shù)可以是常數(shù),或者是表達(dá)式(根據(jù)該表達(dá)式可以求得一個(gè)常數(shù)),如:

DATA_BYTEDB10,4,10H

DATA_WORDDW100,100H,-5

DATA_DWDD3C,0FFFDH

匯編程序可以在匯編期間在存儲器中存入數(shù)據(jù),匯編后的內(nèi)存分布如圖4.1所示。

圖4.1例4-2的匯編結(jié)果

【例4-3】操作數(shù)也可以是字符串,如:

MESSAGEDB‘HELLO’

存儲器存儲情況如圖4.2(a)所示,而DB'AB'?和DW'AB'?的存儲情況分別如圖4.2(b)和(c)所示。

(a)字符串的存儲;(b)DB‘AB’;(c)DW‘AB’圖4.2例4-3的匯編結(jié)果

【例4-4】操作數(shù)“?”可以保留存儲空間,但不存入數(shù)據(jù)。例如:

ABCDB0,?,?,0

DEFDW?,52,?

經(jīng)匯編后的存儲情況如圖4.3所示。

操作數(shù)還可以使用復(fù)制操作符(DUPLICATIONOPERATOR)來復(fù)制某個(gè)(或某些)操作數(shù)。其格式為

REPEAT_COUNTDUP(OPERAND,…,OPERAND)

其中,REPEAT_COUNT可以是一個(gè)表達(dá)式,它的值應(yīng)該是一個(gè)正整數(shù),用來指定括號中的操作數(shù)的重復(fù)次數(shù)。

圖4.3例4-4的匯編結(jié)果

【例4-5】使用DUP實(shí)現(xiàn)重復(fù)定義。

ARRAY1DB2DUP(0,1,2,?)

ARRAY2DB100DUP(?)

經(jīng)匯編后的存儲情況如圖4.4所示。

由圖可見,例4-5中的第一個(gè)語句展開后和語句ARRAY1

DB

0,1,2,?,0,1,2,?是等價(jià)的。

圖4.4例4-5的匯編結(jié)果

4.2.3定義程序開始和結(jié)束的偽指令

在程序的開始部分可以用NAME或TITLE為模塊命名。NAME的格式是:

NAMEMODULE_NAME

匯編程序?qū)⒁越o出的MODULE_NAME作為模塊的名字。如果程序中沒有使用NAME偽操作,則可使用TITLE偽操作,其格式為

TITLETEXT

TITLE偽操作可指定列表文件的每一頁上打印的標(biāo)題。同時(shí),如果程序中沒有使用NAME偽操作,則匯編程序?qū)⒂肨EXT中的前六個(gè)字符作為模塊名。TEXT中最多可有60個(gè)字符。如果程序中既無NAME又無TITLE偽操作,則將用源文件名作為模塊名。所以,NAME及TITLE偽操作并不是必要的,但一般經(jīng)常使用TITLE,以便在列表文件中能打印出標(biāo)題來。

表示源程序結(jié)束的偽操作的格式為

END[LABEL]

其中,標(biāo)號(LABEL)指示程序開始執(zhí)行的起始地址。如果多個(gè)程序模塊相連接,則只有主程序要使用標(biāo)號,其他子程序模塊只用END而不必指定標(biāo)號。匯編程序?qū)⒃谟龅紼ND時(shí)結(jié)束匯編,而程序則將從主模塊的第一個(gè)標(biāo)號處開始執(zhí)行。

4.2.4指令集選擇偽指令

由于80x86的所有處理器都支持8086/8088指令系統(tǒng),而且每一種高檔的機(jī)型又都增加了一些新的指令,因此,在編寫程序時(shí)要對所用處理器有一個(gè)確切的選擇。也就是說,要告訴匯編程序應(yīng)該選擇哪一種指令系統(tǒng)。這一組偽操作的功能就是確定指令系統(tǒng)。

此類偽操作主要有以下幾種:

(1)?.8086:選擇8086指令系統(tǒng)。

(2)?.286:選擇80286指令系統(tǒng)。

(3)?.286P:選擇保護(hù)方式下的80286指令系統(tǒng)。

(4)?.386:選擇80386指令系統(tǒng)。

(5)?.386P:選擇保護(hù)方式下的80386指令系統(tǒng)。

(6)?.486:選擇80486指令系統(tǒng)。

(7)?.486P:選擇保護(hù)方式下的80486指令系統(tǒng)。

(8)?.586:選擇Pentium指令系統(tǒng)。

(9)?.586P:選擇保護(hù)方式下的Pentium指令系統(tǒng)。

有關(guān)“選擇保護(hù)方式下的xxxx指令系統(tǒng)”的含義是指包括特權(quán)指令在內(nèi)的指令系統(tǒng)。此外,上述偽操作均支持相應(yīng)的協(xié)處理器指令。

這類偽操作一般放在整個(gè)程序的最前面,如不給出,則匯編程序默認(rèn)值為?.8086指令系統(tǒng)。它們可放在程序中,如程序中使用了一條80486所增加的指令,則可以在該指令的上一行加上?.486。

4.2.5地址計(jì)數(shù)器與對準(zhǔn)偽操作

1.地址計(jì)數(shù)器——$

在匯編程序?qū)υ闯绦騾R編的過程中,使用地址計(jì)數(shù)器(LOCATIONCOUNTER)來保存當(dāng)前正在匯編的指令的偏移地址。當(dāng)開始匯編或在每一段開始時(shí),把地址計(jì)數(shù)器初始化為零,以后在匯編過程中,每處理一條指令,地址計(jì)數(shù)器就增加一個(gè)值,此值為該指令所需要的字節(jié)數(shù)。地址計(jì)數(shù)器的值可用$來表示,匯編語言允許用戶直接用$來引用地址計(jì)數(shù)器的值,因此指令

JNE$+6

的轉(zhuǎn)向地址是JNE指令的首地址加上6。當(dāng)$用在指令中時(shí),它表示本條指令的第一個(gè)字節(jié)的地址。在這里,$+6必須是另一條指令的首地址;否則,匯編程序?qū)⒅甘境鲥e(cuò)信息。當(dāng)$用在偽操作的參數(shù)字段時(shí),和它用在指令中的情況不同,它所表示的是地址計(jì)數(shù)器的當(dāng)前值。

【例4-6】$?用法示例。

ARRAY

DW1,2,$+4,3,4,$+4

如匯編時(shí)ARRAY分配的偏移地址為0074,則匯編后的存儲區(qū)將如圖4.5所示。

注意:ARRAY數(shù)組中的兩個(gè)?$+4得到的結(jié)果是不同的,這是由于?$?的值是在不斷變化的緣故。當(dāng)在指令中用到?$?時(shí),它只代表該指令的首地址,而與?$?本身所在的字節(jié)無關(guān)。

圖4.5例4-6的匯編結(jié)果

2.ORG偽操作

ORG偽操作用來設(shè)置當(dāng)前地址計(jì)數(shù)器的值,其格式為

ORGCONSTANTEXPRESSION 如常數(shù)表達(dá)式的值為N,則ORG偽操作可以使下一個(gè)字節(jié)的地址成為常數(shù)表達(dá)式的值N。例如:

VECTORS SEGMENT

ORG10

VECT1DW47A5H

?ORG20

VECT2DW0C596H

VECTORS ENDS

則VECT1的偏移地址值為0AH,而VECT2的偏移地址值為14H。

常數(shù)表達(dá)式也可以表示從當(dāng)前已定義過的符號開始的位移量,或表示從當(dāng)前地址計(jì)數(shù)器值$開始的位移量。例如:

ORG$+8

可以表示跳過8個(gè)字節(jié)的存儲區(qū),亦即建立了一個(gè)8字節(jié)的未初始化的數(shù)據(jù)緩沖區(qū)。如程序中需要訪問該緩沖區(qū),則可用LABEL偽操作來定義該緩沖區(qū)的如下變量名:

BUFFERLABELBYTE

ORG$+8

當(dāng)然,其完成的功能和

BUFFER DB 8 DUP(?)

是一樣的。

3.EVEN偽操作

EVEN偽操作使下一個(gè)變量或指令開始于偶數(shù)字節(jié)地址。一個(gè)字的地址最好從偶地址開始,所以對于字類型數(shù)組,為保證其從偶地址開始,可以在其前用EVEN偽操作來達(dá)到這一目的。

例如:

DATA_SEG SEGMENT

EVEN ;保證地址從偶地址開始

WORD_ARRAYDW 100 DUP(?)

DATA_SEG ENDS

4.?ALIGN偽操作

ALIGN偽操作為保證雙字類型數(shù)組邊界從4的倍數(shù)開始創(chuàng)造了條件,其格式為

ALIGNBOUNDARY

其中,BOUNDARY必須是2的冪,例如:

.DATA

ALIGN 4

ARRAY DB 100 DUP(?)

就可保證ARRAY的值為4的倍數(shù)。當(dāng)然,ALIGN2和EVEN是等價(jià)的。

4.3匯編語言源程序結(jié)構(gòu)

1.完整段定義的程序結(jié)構(gòu)存儲器的物理地址是由段地址和偏移地址組合而成的,匯編程序在把源程序轉(zhuǎn)換為目標(biāo)程序時(shí),必須確定標(biāo)號和變量(代碼段和數(shù)據(jù)段的符號地址)的偏移地址,并且需要把有關(guān)信息通過目標(biāo)模塊傳送給連接程序,以便連接程序把不同的段和模塊連接在一起,形成一個(gè)可執(zhí)行程序。為此,需要用段定義偽操作,其格式如下:

SEGMENT_NAME SEGMENT

<語句體>

SEGMENT_NAME ENDS

其中,語句體部分對于數(shù)據(jù)段、附加段和堆棧段來說,一般是存儲單元的定義、分配等偽操作,對于代碼段則是指令及偽操作。

此外,在代碼段還必須明確段和段寄存器的關(guān)系,這可用ASSUME偽操作來實(shí)現(xiàn),其格式為

ASSUME<段寄存器名>:段名[,<段寄存器名>:段名,…]

ASSUME<段寄存器名>:NOTHING

其中,段寄存器名必須是CS、DS、ES和SS(對于386及其后繼機(jī)型還有FS和GS),而段名則必須是由SEGMENT定義的段中的段名。ASSUMENOTHING則可取消前面由ASSUME所指定的段寄存器的對應(yīng)關(guān)系。

由于ASSUME偽操作只是指定某個(gè)段分配給哪一個(gè)段寄存器,它并不能把段地址裝入段寄存器中,要把段地址裝入段寄存器中,就要求在代碼段中必須有對段地址裝入相應(yīng)的段寄存器中的指令。例如,在上面的程序中,分別用兩條MOV指令完成這一操作。如果程序中有堆棧段,也需要把段地址裝入SS中。但是,代碼段CS不需要這樣做,這一操作是在程序初始化時(shí)完成的。

為了對段定義作進(jìn)一步的控制,SEGMENT偽操作添加了類型及屬性說明,其格式如下:

<段名>

SEGMENT

[定位類型][組合類型][使用類型][類別]

<段名>

ENDS

在一般情況下,這些說明可以不用。但是,當(dāng)需要用連接程序把本程序與其他程序模塊相連接時(shí),就需要使用這些說明。

2.對段定義的進(jìn)一步說明

1)定位類型(ALIGN_TYPE)

定位類型用于說明段的起始地址應(yīng)有怎樣的邊界值,其取值可以是:

PARA:指定段的起始地址必須從小段邊界開始,即段起始地址最低位必須為0。這樣,偏移地址可以從0開始。

BYTE:該段可以從任何地址開始,這樣,起始偏移地址可能不是0。

WORD:該段必須從字的邊界開始,即段起始地址必須為偶數(shù)。

DWORD:該段必須從雙字邊界開始,即段起始地址的最低位必須為4的倍數(shù)。

PAGE:該段必須從負(fù)的邊界開始,即段起始地址的最低兩個(gè)十六進(jìn)制數(shù)位必須為0(該地址能被256整除)。

定位類型的默認(rèn)項(xiàng)是PARA,即未指定定位類型時(shí),匯編程序默認(rèn)為PARA。

2)組合類型(COMBINE_TYPE)

組合類型用于說明程序連接時(shí)段的合并方法,其取值可以是:

PRIVATE

:該段為私有段,在連接時(shí)不與其他模塊中的同名段合并。

PUBLIC:該段連接時(shí)可以把不同模塊中的同名段相連接而合并為一個(gè)段,其連接次序由連接命令指定。

COMMON:該段在連接時(shí)可以把不同模塊中的同名段重疊而形成一個(gè)段,由于各同名分段有相同的起始地址,因此會產(chǎn)生覆蓋。

ATEXPRESSTION:使段地址為表達(dá)式所計(jì)算出來的16位值,但它不能用來指定代碼段。

MEMORY:與PUBLIC同義。

STACK:把不同模塊中的同名段組合而形成一個(gè)堆棧段,該段的長度為原有各堆棧段長度的總和,原有各段之間并無PUBLIC所連接段中的間隙,而且棧頂可自動指向連接后形成的大堆棧段的棧頂。

組合類型的默認(rèn)項(xiàng)是PRIVATE。

3)使用類型(USE_TYPE)

使用類型只適用于386及其后繼機(jī)型,它用來說明是使用16位尋址方式還是使用32位尋址方式。其取值可以是:

USE16:使用16位尋址方式。

USE32:使用32位尋址方式。

當(dāng)使用16位尋址方式時(shí),段長不超過64?KB,地址的形式是16位段地址和16位偏移地址組合;當(dāng)使用32位尋址方式時(shí),段長可達(dá)4?GB,地址的形式是16位段地址和32位偏移地址組合。可以看出,在實(shí)模式下,應(yīng)該使用USE16。

使用類型的默認(rèn)項(xiàng)是USE16。

4)類別名(‘CLASS’)

在單引號中給出連接時(shí)組成段組的類型名。類別說明并不能把相同類別的段合并起來,但在連接后形成的裝入模塊中,可以把它們的位置靠在一起。

4.4匯編語言程序的上機(jī)過程

在計(jì)算機(jī)上運(yùn)行匯編語言程序的步驟是:

(1)用編輯程序建立?.ASM源文件;

(2)用MASM程序把?.ASM文件匯編成?.OBJ文件;

(3)用LINK程序把?.OBJ文件連接成.EXE文件;

(4)用DOS命令直接鍵入文件名就可執(zhí)行該程序。

目前常用的匯編程序有Microsoft公司推出的宏匯編程MASM(MACROASSEMBLER)和BORLAND公司推出的TASM(TURBOASSEMBLER)兩種。本書采用MASM5.0版來說明匯編程序所提供的偽操作和操作符,操作流程(匯編語言程序的建立及匯編過程)如圖4.6所示。

圖4.6匯編語言程序的建立及匯編過程

說明:圖4.6中□表示操作使用的工具;○表示操作得到的文件。

匯編程序的主要功能是:

(1)檢查源程序語法是否正確。

(2)測出源程序中的語法錯(cuò)誤,并給出出錯(cuò)信息。

(3)產(chǎn)生源程序的目標(biāo)程序,并可給出列表文件(同時(shí)列出匯編語言和機(jī)器語言的文件,稱為?.LST文件)。

(4)展開宏指令。

4.4.1建立匯編語言的工作環(huán)境

為運(yùn)行匯編語言程序,至少要在磁盤上提供以下文件:

(1)編輯程序,如EDIT.EXE;

(2)匯編程序,如MASM.EXE;

(3)鏈接程序,如LINK.EXE;

(4)調(diào)試程序,如DEBUG.COM。

必要時(shí),還要提供CREF.EXE、EXR2BIN.EXE等文件。

4.4.2匯編語言源程序上機(jī)過程

1.建立匯編源程序?.ASM文件

【例4-7】把40個(gè)字母A的字符串從源緩沖區(qū)傳送到目的緩沖區(qū)。

可以用編輯程序EDIT在磁盤上建立如下的源程序EXAM.ASM。

2.匯編產(chǎn)生?.OBJ文件

源文件建立后,就要用匯編程序?qū)υ次募R編,匯編后產(chǎn)生二進(jìn)制的目標(biāo)文件(.OBJ文件),其操作與匯編程序回答如下:

3.鏈接產(chǎn)生?.EXE文件

匯編程序已產(chǎn)生出二進(jìn)制的目標(biāo)文件(.OBJ),但?.OBJ文件并不是系統(tǒng)可執(zhí)行的文件,因此還必須使用鏈接程序(LINK)把?.OBJ文件轉(zhuǎn)換為系統(tǒng)可執(zhí)行的?.EXE文件。當(dāng)然,如果一個(gè)程序是由多個(gè)模塊組成,也應(yīng)該通過LINK把它們鏈接在一起,操作方法及機(jī)器回答如下:

LINK程序有兩個(gè)輸入文件?.OBJ和?.LIB。.OBJ是我們需要鏈接的目標(biāo)文件,.LIB則是程序中需要用到的庫文件,如無特殊需要,則應(yīng)對[.LIB]:直接回車。LINK程序有兩個(gè)輸出文件,一個(gè)是?.EXE文件,這是我們所需要的,應(yīng)對[EXAM.EXE]:直接回車,這樣就在磁盤上建立了該可執(zhí)行文件。LINK的另一個(gè)輸出文件為?.MAP文件,它是鏈接程序的列表文件,又稱為鏈接映像(LINKMAP),它給出了每個(gè)段在存儲器中的分配情況。

鏈接程序給出的無堆棧段的警告性錯(cuò)誤并不影響程序的運(yùn)行。所以,到此為止,鏈接過程已經(jīng)結(jié)束,可以在操作系統(tǒng)下執(zhí)行EXAM程序了。

4.程序的調(diào)試和執(zhí)行

在建立了?.EXE文件后,就可以直接在操作系統(tǒng)中執(zhí)行程序,程序如下:

C:\>EXAM↙

C:\>_

程序運(yùn)行結(jié)束并返回DOS。如果用戶程序已直接把結(jié)果在終端上顯示出來,那么程序已經(jīng)運(yùn)行結(jié)束,結(jié)果也已經(jīng)得到。但是,如果EXAM程序并未顯示出結(jié)果,這就要使用調(diào)試程序進(jìn)行分析。常用的調(diào)試工具軟件為DEBUG,見附錄2。

5.生成?.COM文件

.COM文件也是一種可執(zhí)行文件,由程序本身的二進(jìn)制代碼組成,它沒有?.EXE文件所具有的包括有關(guān)文件信息的標(biāo)題區(qū)(HEADER),因此它占有的存儲空間比?.EXE文件要小。.COM文件不允許分段,它所占有的空間不允許超過64KB,因而只能用來編制較小的程序。由于其小而簡單,因此其裝入速度比?.EXE文件要快。

使用?.COM文件時(shí),程序不分段,其入口點(diǎn)(開始運(yùn)行的起始點(diǎn))必須是100H(其前的256個(gè)字節(jié)為程序段前綴所在地),且不必設(shè)置堆棧段。在程序裝入時(shí),由系統(tǒng)自動把SP建立在該段之末。對于所有的過程,則應(yīng)定義為NEAR。

用戶可以通過操作系統(tǒng)下的EXE2BIN程序來建立?.COM文件,操作方法如下:

C:\>EXE2BINFILENAMEFILENAME.COM↙

注意,上行中的第一個(gè)FILENAME給出了已形成的?.EXE文件的文件名,但不必給出文件擴(kuò)展名;第二個(gè)FILENAME即為所要求的?.COM文件的文件名,它必須帶有文件擴(kuò)展名?.COM,這樣就形成了所要的?.COM文件。

此外,.COM文件還可以直接在調(diào)試程序DEBUG中用A或E命令建立,對于一些短小的程序,這也是一種相當(dāng)方便的方法。

4.5匯編語言程序設(shè)計(jì)

4.5.1流程圖的組成借助于流程圖可以清晰地把程序思路表達(dá)出來,有助于編寫正確的程序。流程圖對程序設(shè)計(jì)人員,特別是初學(xué)者來說是一種非常有用的工具。流程圖用一些圖框表示各種操作,用圖形表示算法,直觀形象,易于理解。美國國家標(biāo)準(zhǔn)化協(xié)會ANSI(American

National

StandardInstitute)規(guī)定了一些常用的流程圖,已被世界各國程序工作者普遍采用。

流程圖一般由六種成分組成,如圖4.7所示。

圖4.7流程圖的組成成分

1)執(zhí)行框(矩形框)

執(zhí)行框的作用是表示一段程序或一個(gè)模塊的功能,對于結(jié)構(gòu)化程序,一個(gè)執(zhí)行框只有一個(gè)入口和一個(gè)出口。

2)判別框(菱形框)

判別框的作用是對一個(gè)給定的條件進(jìn)行判斷,根據(jù)給定的條件是否成立來決定如何執(zhí)行其后的操作。它有一個(gè)入口,兩個(gè)出口,表示比較、判斷條件。

3)開始框和結(jié)束框

開始框和結(jié)束框表示程序的起始和結(jié)束。

4)指向線

指向線表示程序執(zhí)行的順序。

5)連接點(diǎn)(小圓圈)

連接點(diǎn)用于將畫在不同地方的流程線連接起來。例如,圖4.8中,有兩個(gè)以①為標(biāo)志的連接點(diǎn),它表示這兩個(gè)點(diǎn)是互相連接在一起的。實(shí)際上它們是同一個(gè)點(diǎn),只是當(dāng)在紙張上畫不下時(shí)才分開來畫。使用連接點(diǎn)可以避免流程線的交叉或過長,使流程圖清晰。

圖4.8流程圖的繪制示意

可以看出,流程圖是表示算法的較好工具。一個(gè)流程圖包括以下幾部分:

(1)表示相應(yīng)操作的框;

(2)帶箭頭的流程線;

(3)框內(nèi)外必要的文字說明。

繪制流程線時(shí)不要忘記畫箭頭,因?yàn)樗从沉肆鞒虉?zhí)行的先后次序,如不畫出箭頭則難以判定各框的執(zhí)行次序。

4.5.2順序程序結(jié)構(gòu)

順序程序結(jié)構(gòu)是指完全按順序逐條執(zhí)行的指令序列,這種程序結(jié)構(gòu)在程序段中大量存在。順序結(jié)構(gòu)程序是最簡單的程序。在順序結(jié)構(gòu)程序中,指令按照先后順序一條一條執(zhí)行。

注意:

(1)本程序采用了DOS中斷調(diào)用的4CH號功能來退出程序段運(yùn)行,返回DOS現(xiàn)場。這是一種常用的執(zhí)行程序返回DOS現(xiàn)場的方法。

(2)本例可顯示輸出。

4.5.3分支程序設(shè)計(jì)

1.分支程序結(jié)構(gòu)形式

分支程序結(jié)構(gòu)可以有兩種形式,如圖4.9所示。它們分別相當(dāng)于高級語言中的IF_THEN_ELSE語句和CASE語句,適用于要求根據(jù)不同條件作不同處理的情況。IF_THEN_ELSE語句可以引出兩個(gè)分支,CASE語句則可以引出多個(gè)分支。不論哪一種形式,它們的共同特點(diǎn)是:運(yùn)行方向是向前的,在某一種特定條件下,只能執(zhí)行多個(gè)分支中的一個(gè)分支。

(a)?IF_THEN_ELSE結(jié)構(gòu)(b)?CASE結(jié)構(gòu)圖4.9分支程序的結(jié)構(gòu)形式

2.分支程序設(shè)計(jì)方法

程序的分支一般用條件轉(zhuǎn)移指令來產(chǎn)生,利用轉(zhuǎn)移指令不影響條件碼的特性,連續(xù)地使用條件轉(zhuǎn)移指令可使程序產(chǎn)生多個(gè)不同的分支。

【例4-9】TABLE是一字節(jié)數(shù)組的首地址,長度為100。統(tǒng)計(jì)此數(shù)組中正數(shù)、0及負(fù)數(shù)的個(gè)數(shù),并分別放在COUNT1、COUNT2和COUNT3變量中。其流程圖如圖4.10所示。

圖4.10例4-9流程圖

本程序段在開始時(shí)出現(xiàn)了兩次壓棧操作,即PUSHDS和PUSHAX((AX)

=

0)。本程序段是一個(gè)FAR屬性的子程序,在程序結(jié)束執(zhí)行RET時(shí)將引起兩次出棧操作,會使(CS)內(nèi)容等于未執(zhí)行本程序前的值,(IP)?=

0。在(CS):(IP)位置有一段程序,功能就是退出程序段運(yùn)行,返回DOS現(xiàn)場。這是第二種執(zhí)行程序返回DOS現(xiàn)場的方法。

4.5.4循環(huán)程序設(shè)計(jì)

1.循環(huán)程序結(jié)構(gòu)

循環(huán)程序結(jié)構(gòu)可以總結(jié)為兩種結(jié)構(gòu)形式,如圖4.11所示。一種是DOWHILE(當(dāng)形)結(jié)構(gòu)形式;另一種是DOUNTIL(直到形)結(jié)構(gòu)形式。

(a)?DOWHILE結(jié)構(gòu)(b)?DOUNTIL結(jié)構(gòu)圖4.11循環(huán)程序的結(jié)構(gòu)形式

1)

DOWHILE結(jié)構(gòu)

DOWHILE結(jié)構(gòu)把對循環(huán)控制條件的判斷放在循環(huán)的入口,先判斷條件,滿足條件就執(zhí)行循環(huán)體,否則就退出循環(huán),如圖4.11(a)所示。

2)

DOUNTIL結(jié)構(gòu)

DOUNTIL結(jié)構(gòu)把對循環(huán)控制條件的判斷放在循環(huán)的出口,先執(zhí)行循環(huán)體,然后判斷控制條件,不滿足條件則繼續(xù)執(zhí)行循環(huán)操作,一旦滿足條件則退出循環(huán),如圖4.11(b)所示。

這兩種結(jié)構(gòu)可以根據(jù)具體情況選擇使用。如果有循環(huán)次數(shù)等于0的情況,則應(yīng)選擇DOWHILE結(jié)構(gòu),否則使用DOUNTIL結(jié)構(gòu)。不論哪一種結(jié)構(gòu)形式,循環(huán)程序都可由如下四部分組成:

(1)循環(huán)初始化。初始化完成設(shè)置循環(huán)次數(shù)的計(jì)數(shù)值或其他條件,設(shè)置循環(huán)初始地址,為循環(huán)體正常工作而建立初始狀態(tài)等。

(2)循環(huán)體。循環(huán)體是循環(huán)程序的主體,該部分是為完成程序功能而設(shè)計(jì)的主要程序段。

(3)循環(huán)修改。循環(huán)修改是調(diào)整計(jì)數(shù)值或內(nèi)存地址的部分,保證每一次重復(fù)(循環(huán))時(shí),參加執(zhí)行的信息能發(fā)生有規(guī)律的變化而建立的程序段。

(4)循環(huán)控制。循環(huán)控制本來應(yīng)該屬于循環(huán)體的一部分,由于它是循環(huán)程序設(shè)計(jì)的關(guān)鍵,因此要對它作專門的討論。每個(gè)循環(huán)程序必須選擇一個(gè)循環(huán)控制條件來控制循環(huán)的運(yùn)行和結(jié)束,而合理地選擇該控制條件就成為循環(huán)程序設(shè)計(jì)的關(guān)鍵問題。有時(shí)循環(huán)次數(shù)是已知的,此時(shí)可以用循環(huán)次數(shù)作為循環(huán)的控制條件,LOOP指令使這種循環(huán)程序設(shè)計(jì)能很容易地實(shí)現(xiàn)。

2.循環(huán)程序設(shè)計(jì)方法

【例4-10】設(shè)計(jì)一個(gè)程序,完成從1連加到100(即1

+

2

+

+

99

+

100)的操作,結(jié)果保存在數(shù)據(jù)段的SUM單元。

分析:這樣的問題如果采用順序程序設(shè)計(jì),至少要一百條指令,并且程序的結(jié)構(gòu)性和可讀性差,而采用循環(huán)程序設(shè)計(jì)就會簡潔明了。程序清單如下:

注意:

(1)本程序段采用了第三種退出方式,程序運(yùn)行結(jié)束將由于執(zhí)行HLT指令而進(jìn)入停機(jī)狀態(tài),當(dāng)鍵入Ctrl?+

Break組合鍵(鍵盤中斷)后,返回DOS現(xiàn)場。

(2)用DEBUG跟蹤,會發(fā)現(xiàn)(SUM)?=

13BAH。

【例4-11】試編制一個(gè)把SUM的二進(jìn)制數(shù)用十六進(jìn)制數(shù)的形式在屏幕上顯示的程序段,作為轉(zhuǎn)換輸出功能部分,插入到例4-10中程序的“;********”位置。重新運(yùn)行例4-10程序,就會在屏幕上顯示出運(yùn)算結(jié)果。

根據(jù)題意,將SUM內(nèi)容送到BX中,從左到右每四位為一組在屏幕上顯示出來,每次循環(huán)顯示一個(gè)十六進(jìn)制數(shù)位,計(jì)數(shù)初值為4。程序框圖如圖4.12所示。采用循環(huán)移位的方式把所要顯示的4位二進(jìn)制數(shù)移到最右面,以便做數(shù)字到字符的轉(zhuǎn)換工作。另外,由于數(shù)字0~9的ASCII值為30H~39H,而字母A~F的ASCII值為41H~46H,因此在把4位二進(jìn)制數(shù)加上30H后,還需作一次判斷,如果是字符A~F,則還應(yīng)加上7才能顯示出正確的十六進(jìn)制數(shù)。以BINIHEX.ASM為文件名建立“二進(jìn)制到十六進(jìn)制數(shù)轉(zhuǎn)換程序”源文件。

圖4.12二進(jìn)制到十六進(jìn)制數(shù)轉(zhuǎn)換程序的框圖

在程序中沒有使用LOOP指令,這是因?yàn)檠h(huán)移位指令要使用CL寄存器,而LOOP指令要使用CX寄存器,為了解決CX寄存器的沖突問題,這里用CH寄存器存放循環(huán)計(jì)數(shù)值,而用DEC及JNZ兩條指令完成LOOP指令的功能。這說明,使用計(jì)數(shù)值控制循環(huán)結(jié)束也可以不用LOOP指令。當(dāng)然也可以把計(jì)數(shù)值初始化為0,用每循環(huán)一次加1然后比較次數(shù)是否達(dá)到要求的方法來實(shí)現(xiàn),或者仍用LOOP指令,而用堆棧保存其中的一個(gè)信息(如計(jì)數(shù)值)來解決CX寄存器的沖突問題等??傊?,程序設(shè)計(jì)是很靈活的,只要算法和指令的使用沒有錯(cuò)誤,都可以達(dá)到目的。

二進(jìn)制到十六進(jìn)制轉(zhuǎn)換程序如下:

注意:由于上述匯編語言格式不完全,因此該程序段不能單獨(dú)運(yùn)行。

4.5.5子程序結(jié)構(gòu)形式與操作

1.子程序定義

可把具有獨(dú)立功能的程序段定義為子程序,供其他程序調(diào)用(類似于C語言的函數(shù))。子程序定義偽操作用在子程序的前后,使整個(gè)子程序形成清晰的、具有特定功能的代碼塊。

子程序定義的語法格式為

<子程序名>

PROC <屬性>

RET

<子程序名>ENDP

其中,子程序名為標(biāo)識符,它又是子程序入口的符號地址,它的寫法與標(biāo)號的寫法相同;屬性(Attribute)是指類型屬性,它可以是NEAR或FAR。

如前所述,CALL和RET指令都有NEAR和FAR的屬性,段內(nèi)調(diào)用使用NEAR屬性,但可以不顯示地寫出,段間調(diào)用使用FAR屬性。為了使用戶的工作更加方便,80x86的匯編程序用PROC偽操作的類型屬性來確定CALL和RET指令的屬性。也就是說,如果所定義的子程序是FAR屬性的,那么對它的調(diào)用和返回一定都是FAR屬性;如果所定義的子程序是NEAR屬性的,那么對它的調(diào)用和返回也一定是NEAR屬性。

這樣,用戶只需在定義子程序時(shí)考慮它的屬性,而CALL和RET的屬性可以由匯編程序來確定。用戶確定子程序?qū)傩缘脑瓌t很簡單,即

(1)如調(diào)用程序和子程序在同一個(gè)代碼段中,則使用NEAR屬性;

(2)如調(diào)用程序和子程序不在同一個(gè)代碼段中,則使用FAR屬性。

由于調(diào)用程序MAIN和子程序SUBR1在同一代碼段中,因此SUBR1定義為NEAR屬性。這樣,MAIN中對SUBR1的調(diào)用和SUBR1中的RET就都是NEAR屬性。但是一般來說,主程序MAIN應(yīng)定義為FAR屬性,這是由于匯編系統(tǒng)把MAIN程序也看做是DOS調(diào)用的一個(gè)子程序,因而DOS對MAIN的調(diào)用以及MAIN中的RET就是FRA屬性。當(dāng)然,CALL和RET的屬性是匯編程序確定的,用戶只需正確選擇PROC的屬性就可以了。

例4-12的情況也可以寫成如下程序:

也就是說,子程序定義也可以嵌套,一個(gè)子程序定義中可以包括多個(gè)子程序定義。

SUBT是一個(gè)子程序,它在兩處被調(diào)用,一處是與SEGX同在段內(nèi),另一處是在SEGY段內(nèi)。為此,SUBT必須具有FAR屬性以適應(yīng)SEGY段調(diào)用的需要。SUBT既然有FAR屬性,則不論在SEGX段還是SEGY段中,對SUBT的調(diào)用就都具有FAR屬性了,這樣不會發(fā)生什么錯(cuò)誤。反之,如果這里的SUBT使用了NEAR屬性,則在SEGY段內(nèi)對它的調(diào)用就會出錯(cuò)。

2.子程序的調(diào)用和返回

子程序的正確執(zhí)行是由子程序的正確調(diào)用和正確返回保證的,80x86的CALL和RET指令完成的就是調(diào)用和返回的功能。為保證其正確性,除PROC的屬性要正確選擇外,還應(yīng)該注意子程序運(yùn)行期間的堆棧狀態(tài)。由于執(zhí)行CALL時(shí)已使返回地址入棧,因此執(zhí)行RET時(shí)應(yīng)該使返回地址出棧,如果子程序中不能正確使用堆棧而造成執(zhí)行RET前SP并未指向進(jìn)入子程序時(shí)的返回地址,則必然會導(dǎo)致運(yùn)行出錯(cuò)。因此,子程序中對堆棧的使用應(yīng)該特別小心,以免發(fā)生錯(cuò)誤。

3.現(xiàn)場保護(hù)與現(xiàn)場恢復(fù)

由于主程序和子程序通常是分別編制的,因此它們所使用的寄存器往往會發(fā)生沖突。如果主程序調(diào)用子程序之前的某個(gè)寄存器內(nèi)容在從子程序返回后還有用,而子程序又恰好使用了同一個(gè)寄存器,這就破壞了該寄存器的原有內(nèi)容,因而會造成程序運(yùn)行錯(cuò)誤,這是不允許的。為避免這種錯(cuò)誤的發(fā)生,在進(jìn)入子程序后,就應(yīng)該把子程序所需要使用的寄存器內(nèi)容保存在堆棧中,此過程稱作現(xiàn)場保護(hù);而在退出子程序前把寄存器內(nèi)容恢復(fù)原狀,此過程稱作現(xiàn)場恢復(fù)?,F(xiàn)場保護(hù)與現(xiàn)場恢復(fù)分別使用壓棧和彈出指令實(shí)現(xiàn)。例如:

在設(shè)計(jì)子程序時(shí),應(yīng)仔細(xì)考慮哪些寄存器是必須保護(hù)的,哪些寄存器是不必要保護(hù)的。一般來說,子程序中用到的寄存器是應(yīng)該保護(hù)的。但是,如果使用寄存器在主程序和子程序之間傳送參數(shù)的話,則這種寄存器就不一定需要保護(hù),特別是用來向主程序回送結(jié)果的寄存器,就更不應(yīng)該因保存和恢復(fù)寄存器而破壞了應(yīng)該向主程序傳送的信息。

從80286

CPU開始使用的PUSHA/POPA指令以及從80386CPU開始的高檔微機(jī)使用的PUSHAD/POPAD指令為子程序中保存和恢復(fù)寄存器內(nèi)容提供了有力的支持。

4.子程序嵌套

主程序調(diào)用子程序,子程序還可以調(diào)用其他子程序,這就是子程序的嵌套調(diào)用,子程序可以多重嵌套調(diào)用。

【例4-14】設(shè)從BUF開始存放若干無符號字節(jié)數(shù)據(jù),找出其中的最小值并以十六進(jìn)制形式輸出。

分析:本題用子程序SEARCH來求最小數(shù)字節(jié)數(shù)并輸出,再調(diào)用一個(gè)子程序輸出1位十六進(jìn)制數(shù)。由于數(shù)據(jù)多,因此可以利用子程序的嵌套。

注意:本例有顯示輸出子程序部分,上機(jī)更直觀。

4.5.6BIOS中斷調(diào)用

1.

BIOS中斷調(diào)用概述

BIOS(BasicInput/OutputSystem)是IBM-PC的監(jiān)控程序,它固化在微機(jī)主板的ROM中,它的內(nèi)容主要有系統(tǒng)測試程序(POST)、初始化引導(dǎo)程序(BOOT)、I/O設(shè)備的基本驅(qū)動程序和許多常用程序模塊,它們一般以中斷服務(wù)程序的形式存在。

圖4.13是用戶程序和操作系統(tǒng)關(guān)系示意圖。由圖可見,BIOS程序直接建立在硬件基礎(chǔ)上,磁盤操作系統(tǒng)(DOS)和其他操作系統(tǒng)建立在BIOS基礎(chǔ)上,各種高級語言則建立在操作系統(tǒng)基礎(chǔ)上。用戶程序可以使用高級語言,也可以調(diào)用DOS或其他操作系統(tǒng),還可以調(diào)用BIOS,甚至直接指揮硬件設(shè)備。

圖4.13用戶程序和操作系統(tǒng)關(guān)系示意圖

通常,應(yīng)用程序調(diào)用DOS提供的系統(tǒng)功能完成輸入/輸出或其他操作,這樣做用戶可以少考慮硬件,實(shí)現(xiàn)起來容易。應(yīng)用程序直接對硬件編程的優(yōu)點(diǎn)是程序的效率高,缺點(diǎn)是需要程序員對硬件性能有較深的了解。

BIOS中斷程序處于DOS功能調(diào)用和硬件環(huán)境之間,和DOS功能調(diào)用相比,其優(yōu)點(diǎn)是效率高,缺點(diǎn)是編程相對復(fù)雜;和直接對硬件編程相比,優(yōu)點(diǎn)是實(shí)現(xiàn)相對容易,缺點(diǎn)是效率相對低。在下列情況下可考慮使用BIOS中斷。

(1)有些功能DOS沒有提供,但BIOS提供了;

(2)有些場合無法使用DOS功能調(diào)用;

2.

BIOS中斷調(diào)用方法

IOS的調(diào)用就是人們借用每一臺計(jì)算機(jī)中BIOS固有的I/O操作程序來方便地解決自己的問題,由于它已經(jīng)在計(jì)算機(jī)中了,因此人們不必再把它寫入自己的程序,只要指明它的操作位置就可以了。

1)

BIOS調(diào)用的基本操作

由于BIOS中的每一種功能調(diào)用往往包含不同的幾個(gè)操作細(xì)節(jié),所以調(diào)用時(shí)需要說明三部分,基本步驟如下:

(1)設(shè)置分功能號:按實(shí)現(xiàn)的操作功能的要求,給指定寄存器(通常為AH)送入分功能號。

(2)置入口參數(shù):按操作要求,給寄存器填寫相應(yīng)參數(shù)的內(nèi)容(某些調(diào)用無參數(shù))。

(3)使用中斷語句INTn:執(zhí)行調(diào)用的功能,其中n為中斷號。

(4)分析出口參數(shù)。

例如:

MOVAH,0 ;分功能號為0

MOV

AL,10H ;置入口參數(shù)

NT1AH ;1AH為中斷號,功能為讀時(shí)間計(jì)數(shù)器的值

注意:某些BIOS調(diào)用可能沒有出口參數(shù),這時(shí)省略第(4)步操作。

2)

BIOS打印功能調(diào)用

BIOSI7H中斷指令提供了由AH寄存器指定的三種不同的打印操作。

(1)?BIOS中斷17H的功能0是打印一個(gè)字符。將要打印輸出的字符放在AL中,打印機(jī)號放在DX中,BIOS最多允許連接三臺打印機(jī),機(jī)號分別為0,1和2。如果只有一臺打印機(jī),那么默認(rèn)是0號打印機(jī),打印機(jī)的狀態(tài)信息被回送到AH寄存器。

(2)?BIOS17H的功能1是初始化打印機(jī)。初始化打印機(jī)并回送打印機(jī)狀態(tài)到AH寄存器。如果把打印機(jī)開關(guān)關(guān)上然后又打開,則打印機(jī)各部分復(fù)位到初始值。此功能和打開打印機(jī)時(shí)的作用一樣。在每個(gè)程序的初始化部分可以用17H的功能1來初始化打印機(jī)。

(3)?BIOS17H的功能2是把狀態(tài)字節(jié)讀入AH寄存器中。打印機(jī)的狀態(tài)字節(jié)如圖4.14所示。

圖4.14打印機(jī)的狀態(tài)字節(jié)

3)

BIOS串行通信口功能

微機(jī)通過串行接口實(shí)現(xiàn)硬件連接提供了一種比較靈活的通信方法,即通過INT14H調(diào)用ROMBIOS串行通信口中斷服務(wù)程序。該中斷服務(wù)程序的功能包括將串行口初始化為指定的字節(jié)結(jié)構(gòu)和傳輸速率,檢查控制器的狀態(tài),讀/寫字符等。具體功能設(shè)置如表4-2所示。

INT14H的AH?=

0功能把指定的串行通信口初始化為希望的波特率、奇偶性、字長和終止位的位數(shù)。這些初始化參數(shù)設(shè)置在AL寄存器中,其各位的含義如圖4.15所示。

圖4.15串行通信口初始化參數(shù)

【例4-15】要求0號通信口的傳輸率為2400波特,字長為8位,終止位為1位,無奇偶校驗(yàn)。

MOVAH,0 ;串行通信口初始化

MOVAL,0A3H ;0A3H?=?10100011B

MOVDX,0 ;指向COM1

INT14H ;調(diào)用BIOS

返回參數(shù)中通信口狀態(tài)字節(jié)各位置1的含義如圖4.16所示。

圖4.16串行通信口狀態(tài)字節(jié)

在接收和發(fā)送過程,錯(cuò)誤狀態(tài)位(1,2,3,4位)一旦被置為1,則讀入的接收數(shù)據(jù)已不是有效數(shù)據(jù),所以在串行通信應(yīng)用程序中,應(yīng)檢測數(shù)據(jù)傳輸是否出錯(cuò)。

奇偶錯(cuò):通信線上(尤其是用電話線傳輸時(shí))的噪音引起某些數(shù)據(jù)位的改變,產(chǎn)生奇偶錯(cuò)。通常檢測出奇偶錯(cuò)時(shí),要求正在接收的數(shù)據(jù)至少應(yīng)重新發(fā)送一段。

超越錯(cuò):在上一個(gè)字符還未被處理機(jī)取走,又有字符要傳送到數(shù)據(jù)寄存器里時(shí),就會引起超越錯(cuò)。如果處理機(jī)處理字符的速度小于單行通信口的波特率,則會產(chǎn)生這種錯(cuò)誤。

幀格式錯(cuò):當(dāng)接收/發(fā)送器未接收到一個(gè)字符數(shù)據(jù)的停止位時(shí),會引起幀格式錯(cuò)。這種錯(cuò)誤可能是由于通信線上的噪音引起停止位的丟失,或者是由于接收方和發(fā)送方初始化不匹配而造成的。

間斷:間斷有時(shí)候并不能算是一個(gè)錯(cuò)誤,而是為某些特殊的通信環(huán)境設(shè)置的“空格”狀態(tài)。當(dāng)間斷位為1時(shí),說明接收的“空格”狀態(tài)超過了一個(gè)完整的數(shù)據(jù)字傳輸時(shí)間。

PS/2以及所有的PC,AH?=

04功能允許程序員將波特率設(shè)置為19?200,數(shù)據(jù)位的長度可以設(shè)置為5、6、7或8位,而不像AH?=?0功能那樣只能設(shè)置成7或8位。

4.5.7

DOS功能調(diào)用

1.

DOS功能調(diào)用概述

8086/8088指令系統(tǒng)中,有一種軟中斷指令I(lǐng)NTn。每執(zhí)行一條軟中斷指令,就調(diào)用一個(gè)相應(yīng)的中斷服務(wù)程序。當(dāng)n?=

5~1FH時(shí),調(diào)用BIOS中的服務(wù)程序,一般稱作系統(tǒng)中斷調(diào)用;當(dāng)n?=

20~3FH時(shí),調(diào)用DOS中的服務(wù)程序,稱作功能調(diào)用。其中,INT21H是一個(gè)具有調(diào)用多種功能的服務(wù)程序的軟中斷指令,故稱其為DOS系統(tǒng)功能調(diào)用。

2.

DOS功能調(diào)用方法

1)

DOS軟中斷指令(INT20H~I(xiàn)NT27H)

DOS軟中斷功能、入口及出口參數(shù)見表4-3。表中的入口參數(shù)是指在執(zhí)行軟中斷指令前有關(guān)寄存器必須設(shè)置的值,出口參數(shù)記錄的是執(zhí)行軟中斷以后的結(jié)果及特征,供用戶分析使用。

DOS中斷的使用方法是:首先按照DOS中斷的規(guī)定,輸入入口參數(shù),然后執(zhí)行INT指令,最后分析出口參數(shù),如下所示:

表4-3中INT22H、INT23H和INT24H用戶不能直接調(diào)用。例如,INT23H只有當(dāng)同時(shí)按下Ctrl和Break鍵時(shí)才形成DOS的23H號調(diào)用,其功能是:終止正在運(yùn)行的程序,返回操作系統(tǒng)。INT25H為絕對讀盤,INT26H為絕對寫盤,這兩條軟中斷的調(diào)用需要用戶熟知磁盤結(jié)構(gòu),準(zhǔn)確指出讀/寫的扇區(qū)號、扇區(qū)數(shù)、磁盤驅(qū)動器號,還需要知道與磁盤交換信息的內(nèi)存緩沖區(qū)的首地址。因此,這種讀/寫磁盤的方式較落后,除特殊用途外,基本上已不采用。

INT20H是兩字節(jié)指令,它的作用是終止正在運(yùn)行的程序,返回操作系統(tǒng)。這種終止程序的方法只適用于?.COM文件,而不適用于?.EXE文件。

INT27H指令的作用是終止正在運(yùn)行的程序,返回操作系統(tǒng),被終止的程序駐留在內(nèi)存中作為DOS的一部分,它不會被其他程序覆蓋。在其他用戶程序中,可以利用軟中斷來調(diào)用這個(gè)駐留的程序。

2)

DOS系統(tǒng)功能調(diào)用(INT21H)

系統(tǒng)功能調(diào)用INT21H是一個(gè)有100多個(gè)子功能的中斷服務(wù)程序,這些子功能的編號稱為功能號。INT21H的功能大致可以分為四個(gè)方面:設(shè)備管理、目錄管理、文件管理和其他。設(shè)備管理主要包括鍵盤輸入、顯示器輸出、打印機(jī)輸出、串行設(shè)備輸入/輸出、初始化磁盤、選擇當(dāng)前磁盤、取剩余磁盤空間等。目錄管理主要包括查找目錄項(xiàng)、查找文件、置/取文件屬性、文件改名等。

系統(tǒng)功能調(diào)用(INT21H)的使用方法如下:

下面主要介紹INT21H的基本功能。

(1)鍵盤輸入的功能調(diào)用。IBM-PC及PC/XT鍵盤上的按鍵分為三種類型:

第一類是字符鍵,如字母、數(shù)字、字符等。按下此類鍵,即可輸入此鍵相應(yīng)的編碼。

第二類是功能鍵,如Backspace、Home、End、Delete、PgUp、PgDn、F1~F10等。按下此類鍵,可以產(chǎn)生一個(gè)動作。例如,按下Backspace可以使光標(biāo)向左移動一個(gè)位置。

第三類是組合鍵及雙態(tài)鍵,如Shift、Alt、Ctrl、Insert、NumLock、CapsLock、ScrollLock等。使用這些鍵能改變其他鍵所產(chǎn)生的字符碼。

①掃描碼與字符碼。

鍵的掃描碼——鍵盤的每一個(gè)鍵都有一對掃描碼,掃描碼用一個(gè)字節(jié)表示。低7位是掃描碼的數(shù)字編碼01~83,即01H~53H,最高位BIT7表示鍵的狀態(tài)。當(dāng)某鍵按下時(shí),掃描碼的BIT7

=

0,稱為通碼,當(dāng)此鍵放開時(shí),掃描碼的BIT7

=

1,稱為斷碼。通碼和斷碼的值相差80H。

鍵的字符碼——鍵的字符碼是鍵的ASCII碼或擴(kuò)充碼,見表1-4ASCII字符編碼表。

②檢查鍵盤狀態(tài)。DOS系統(tǒng)功能調(diào)用中的功能1、7、8、A、B、C等都與鍵盤有關(guān),包括單字符輸入、字符串輸入和鍵盤狀態(tài)檢驗(yàn)等。

DOS系統(tǒng)功能調(diào)用的0BH號功能可以檢查是否有字符鍵入。如果有鍵按下,使AL

=

FFH,否則AL

=

00H。這個(gè)調(diào)用十分有用,例如,有時(shí)要求程序保持運(yùn)行狀態(tài),而不是無限期等待鍵盤輸入,但又要靠用戶按任意一鍵使程序結(jié)束或退出循環(huán)時(shí),就必須使用0BH號調(diào)用。

【例4-16】檢查鍵盤狀態(tài)。

LOOP:

MOVAH,0BH

INT21H ;檢查鍵盤狀態(tài)

INCAL

JNZLOOP ;無鍵入字符,則循環(huán)

RET ;有鍵入字符,則停止循環(huán)返回

③單字符輸入。功能1、7、8都可以直接接收鍵入的字符。程序中常常利用這些功能,回答程序中的提示信息,或選擇菜單中的可選項(xiàng)以執(zhí)行不同的程序段。用戶還可以利用功能7、8不回顯的特性,鍵入需要保密的信息。

④字符串輸入。用戶程序經(jīng)常需要從鍵盤上接收一串字符。0AH號功能可以接收鍵入的字符串將其存入內(nèi)存中用戶定義的緩沖區(qū)。緩沖區(qū)結(jié)構(gòu)如圖4.17所示。緩沖區(qū)第一字節(jié)為用戶定義的最大鍵入字符數(shù),若用戶鍵入的字符數(shù)(包括回車符)大于此數(shù),則機(jī)器鈴響且光標(biāo)不再右移,直到鍵入回車符為止。緩沖區(qū)第二字節(jié)為實(shí)際鍵入的字符數(shù)(不包括回車符),由DOS自動填入。從第三字節(jié)開始存放鍵入的字符,顯然,緩沖區(qū)的大小等于最大字符數(shù)加2。具體實(shí)例如例4-18所示。

圖4.17用戶定義的緩沖區(qū)

(2)顯示器(CRT)輸出

溫馨提示

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

評論

0/150

提交評論