MASM匯編筆記

這學(xué)期有了匯編課,但個(gè)人感覺這部分難度還是有一些的,所以寫了這篇簡(jiǎn)書當(dāng)做自己對(duì)于知識(shí)的復(fù)習(xí),同時(shí)也是希望能夠再次加深理解,雖然匯編語言在當(dāng)今用的比較少了,但是作為一名計(jì)算機(jī)專業(yè)的學(xué)生,我們還是應(yīng)該有所了解的。

我的這篇文章分為三個(gè)部分

第一部分:通過看MASM的默認(rèn)程序段,對(duì)一些基本概念略作了解

第二部分:通過解讀一道例題,了解簡(jiǎn)單的匯編指令(個(gè)人感覺跟高級(jí)語言還是有一些差異的)

第三部分:總結(jié)一下編寫匯編語言的大致思路



接下來進(jìn)入第一部分

正如高級(jí)語言有自己的編譯器一樣,匯編語言也有自己的一個(gè)編程環(huán)境,目測(cè)是有幾種不同的環(huán)境的。

我們學(xué)校課程選用的是MASM這個(gè)環(huán)境,我個(gè)人感覺還是不錯(cuò)的


首先打開編譯器是這個(gè)樣子的,這些都是默認(rèn)的

大致可以分成三個(gè)部分,data segment(數(shù)據(jù)段)、stacks segment(堆棧段)、codes segment(代碼段)

一般如果我們的程序里需要有pop和push這類的指令,那么我們要先寫堆棧段這部分(作為初學(xué)者就不用考慮這些了,先寫上,反正也不會(huì)有什么錯(cuò)。。。)


這里面 db 128? dup(0)表示的意思是開辟128個(gè)字節(jié)大?。ㄓ胐b表示)的空間,每一個(gè)空間的初始為0(有一點(diǎn)C語言數(shù)組的味道。。。)

基本上每一個(gè)匯編程序一開始都是寫這個(gè),但是為什么要這么寫,我個(gè)人認(rèn)為作為初學(xué)者暫且可以不去探究,只需要記住每次寫匯編之前,先寫上這么一段,以及知道這一段是在開辟一段空間,這段空間是作為堆棧使用的就可以

stack segment stack

db 128 dup(0)

stack segment


堆棧段就到此為止了,接下來我們進(jìn)入數(shù)據(jù)段的研究

數(shù)據(jù)段的初始是這個(gè)樣子的,我們要在其中定義我們需要用的數(shù)據(jù)

DATAS SEGMENT

?;此處輸入數(shù)據(jù)段代碼?

DATAS ENDS

舉個(gè)例子


比如下面這樣


目前來說我作為一個(gè)沒編過幾個(gè)程序的新手,我見到的大部分?jǐn)?shù)據(jù)定義格式都是這個(gè)樣子的

變量名? ? ? ? ?變量類型? ? ? ? ?初始值

num1? ? ? ? ? ? ?db? ? ? ? ? ? ? ? ? ?0

這一句就表示定義一個(gè)叫num1的變量,這個(gè)變量是字節(jié)形的,也就是說在內(nèi)存中為這個(gè)變量開辟一個(gè)8位的字節(jié)空間來保存這個(gè)變量的值

個(gè)人認(rèn)為需要注意這幾點(diǎn)

(a)變量名的定義不要和關(guān)鍵字沖突(比較好的方式就是每個(gè)變量都包含一個(gè)下劃線,這樣比較省事)

(b)變量類型不止db一種,還有有很多其他的,放一張網(wǎng)上的圖


除了以上這種變量的定義方法,字符串變量的定義也是很常見的,格式如下

變量名? ? ? ?db(字符串只能用db)? ? ? ?字符串


我覺得對(duì)于初學(xué)者,有必要解釋一下,這個(gè)字符串的定義在內(nèi)存中是如何對(duì)應(yīng)的

首先明確字符串由字符組成,字符包括字母、數(shù)字、一些特殊符號(hào),還有空格,回車,換行等等

每一個(gè)字符都有一個(gè)0-63之間的數(shù)字與之對(duì)應(yīng),這個(gè)對(duì)應(yīng)關(guān)系就是美國信息交換標(biāo)準(zhǔn)碼(ASCII碼)

接下來我們來梳理內(nèi)存的對(duì)應(yīng)關(guān)系,以上面這個(gè)定義為例

我個(gè)人目前的理解是,prompt1相當(dāng)于是一個(gè)指針,這個(gè)指針指向的是這個(gè)字符串的第一個(gè)字符,在這里面就是‘p’,之后在內(nèi)存中從低地址向高地址,依次放入‘l’、‘e’、‘a(chǎn)’、‘s’、‘e’........‘:’、‘$’

雖然我們?cè)趯懙臅r(shí)候?qū)懙氖亲址?,但是存在?nèi)存中存的就是字符對(duì)應(yīng)的ASCII碼值

由于一個(gè)存儲(chǔ)單位是8位,所以存一個(gè)63以內(nèi)的數(shù)字是綽綽有余的

這也就是為什么在定義字符串的時(shí)候都是用db,而不用dw之類的存儲(chǔ)類型

我理解的就是為了盡可能的節(jié)約內(nèi)存資源,畢竟就算計(jì)算機(jī)技術(shù)發(fā)展到了今天,內(nèi)存資源也不是隨隨便便就可以浪費(fèi)的。

還有一點(diǎn)需要關(guān)注的就是,整個(gè)字符串中的最后一個(gè)字符'$',這個(gè)是表示字符串的結(jié)尾,可以類比C語言字符串中的'/0'



上面我是用語言描述的方式表達(dá)了數(shù)據(jù)定義與內(nèi)存之間的關(guān)系

接下來再用圖來加深一下理解

首先是數(shù)據(jù)的

這里說了一點(diǎn)我之前沒提的,那就是在內(nèi)存中是用16進(jìn)制來表示數(shù)據(jù)的,而定義的時(shí)候,我們使用的是十進(jìn)制

接下來是字符的


就是個(gè)示意圖,里面有一些因素是不嚴(yán)謹(jǐn)?shù)模瑑H供參考



到此為止,已經(jīng)進(jìn)行了簡(jiǎn)單的數(shù)據(jù)段和堆棧段的定義方式,其中堆棧段的定義可以一開始就直接寫上,但是數(shù)據(jù)段的定義往往是在編程的過程中逐步完善的。

接下來,我們要學(xué)習(xí)一個(gè)詞MACRO

查查,有道詞典,就知道這是一個(gè)厲害的詞



“人如其名”,macro的功能也是強(qiáng)大的,接下來就學(xué)習(xí)一下匯編中的宏指令

先回答一個(gè)基本的問題

為什么要學(xué)習(xí)宏指令?

為了使程序更簡(jiǎn)潔呀?。。?!

為什么會(huì)使程序簡(jiǎn)潔呢??(我暫且留一個(gè)伏筆,稍后再說)



我們先以一個(gè)程序?yàn)槔?,介紹一下匯編中如何利用DOS功能調(diào)用來輸出一個(gè)字符串

比如說我們的目的是要輸出字符串到dos命令行窗口中(小黑框),我們要用匯編完成,我們會(huì)怎么做呢

我們會(huì)用DOS的功能調(diào)用,因?yàn)镈OS的功能表里有字符串輸出功能


這個(gè)DOS功能的使用要求我們要把數(shù)值9放到ah寄存器(ax寄存器的高八位)中,補(bǔ)一句,在調(diào)用DOS功能時(shí),AH寄存器中的值是用來區(qū)分調(diào)用哪一個(gè)DOS功能,9號(hào)就是字符串輸出功能。

接下來呢,我們會(huì)這么寫

lea dx, string(這個(gè)語句叫有效地址傳送指令,在這句話里,它將string這個(gè)字符串的首地址,放到了dx這個(gè)16位的寄存器中)

這里注意了?。。。?/p>


lea這個(gè)指令的介紹如下


這個(gè)指令是很友愛的,因?yàn)橹噶顚?duì)于oprd1和oprd2是沒有特殊要求的,這里所說的特殊要求是對(duì)于oprd并沒有指定只能使用哪一個(gè)寄存器,后面我們會(huì)看到有些指令對(duì)于操作數(shù)是有特殊要求的

但是?。。?!

我們這一條指令是必須要把oprd1寫成dx寄存器的

因?yàn)榍懊娴腄OS功能表告訴我們,9號(hào)功能會(huì)自動(dòng)根據(jù)DS:DX找到字符串并輸出

int 21h(這個(gè)語句先暫且理解為DOS的功能執(zhí)行,更深入的理解個(gè)人認(rèn)為暫且不需要,因?yàn)槲覀兠看味际菍戇@個(gè)。。。。。)



前面我們說了正常情況下輸出一個(gè)字符串我們?cè)撛趺醋觯ㄖ虚g我們插入了lea指令的介紹),但是有些語句我們是要在一個(gè)程序里反反復(fù)復(fù)用到的,比如字符輸入、字符輸出、或者是字符串輸入輸出這些

為了避免麻煩,我們希望能夠把這些功能打個(gè)包,想用的時(shí)候隨時(shí)用,并且不用總是寫那些重復(fù)的語句

這個(gè)包就是“宏(macro)”?。。。?!

這個(gè)包就是“宏(macro)”!?。。?!

這個(gè)包就是“宏(macro)”!?。。?!


好,接下來我們就看一下,如何進(jìn)行打包工作


打包大致就是定義一個(gè)宏的意思

定義宏的格式是

舉個(gè)例子——輸出字符串的“宏”

再舉個(gè)栗子


輸入字符的宏


原本是這個(gè)樣子的

mov ah 1

int 21h

寫成“宏”就是這個(gè)樣子的


一般我們會(huì)把宏定義寫在代碼段之前,數(shù)據(jù)段之后,寫在數(shù)據(jù)段之后是因?yàn)楹甓x中會(huì)用到數(shù)據(jù)段定義的變量,比如string,寫在代碼段之前是因?yàn)榇a段里會(huì)用到宏,所以需要事先定義

最后要說一下宏的缺點(diǎn)!?。。?!

宏的缺點(diǎn)就是形參只能是字符串或者是字符,如果是其他類型的變量的話就要寫成一個(gè)函數(shù)啦?。。。?/b>


接下來我們就要進(jìn)入最為驚心動(dòng)魄的code segment了!?。?/h1>


在MASM匯編環(huán)境下,新建一個(gè)文件,其中code segemnt的部分是這樣的


這里面有一些東西是寫好的,我們不需要自己動(dòng)手,但是還是要知道是什么意思,我個(gè)人認(rèn)為這也對(duì)我們后續(xù)的編程是有幫助的。

比如assume CS:CODES,DS:DATAS,SS:STACKS

其中CS、DS、SS是段寄存器(其實(shí)還有一個(gè)ES段寄存器,這里用不上)

ASSUME CS:CODES,DS:DATAS,SS:STACKS

這句話表明CODES這個(gè)段從現(xiàn)在開始就被程序認(rèn)為是段寄存器了,DATAS就被認(rèn)為是數(shù)據(jù)段寄存器了、而STACKS就被認(rèn)為是堆棧段寄存器了

或許我上面說的這些話有點(diǎn)兒像廢話(其實(shí)我也覺得有點(diǎn)兒)。。。。。。。


但還是有一些含義的,以下是我個(gè)人的理解,僅供參考

上面這些代碼表明:內(nèi)存是客觀的,但是對(duì)內(nèi)存的使用是人為的

也就是說我可以定義一個(gè)段CODES給CS作為代碼段

但是我如果高興的話,我也可以定義一個(gè)HAPPY段,然后寫CS:HAPPY(這是我個(gè)人理解,還沒有試驗(yàn)過。。。。),但是無論是HAPPY還是CODES他們發(fā)揮的功能都是存儲(chǔ)指令



接下來要解釋一下那個(gè)start是什么意思,要想理解為什么我們每一次寫代碼段的時(shí)候都要寫個(gè)start就要先理解,整個(gè)程序的最后那個(gè)END的含義。

END的含義表示程序的結(jié)束

格式就是 END 表達(dá)式(這里的表達(dá)式必須是程序段第一個(gè)可執(zhí)行指令的地址。這個(gè)很重要

為了能夠得到這個(gè)地址,我們就要給程序第一個(gè)指令一個(gè)標(biāo)號(hào),這個(gè)標(biāo)號(hào)的含義就是第一個(gè)指令的位置


接下來進(jìn)入第二部分:解讀一道例題,了解幾個(gè)最基本的匯編指令

我以我的課內(nèi)作業(yè)為例

題目如下:

求兩個(gè)數(shù)的平方差,若是負(fù)數(shù),要輸出負(fù)號(hào)。要求:由鍵盤輸入兩整數(shù)a 、b,中間空格隔開;Enter 鍵結(jié)束輸入,并換行顯示結(jié)果。

按照我們前面說的,首先是一些無腦操作(在理解之后就可以無腦了)


第一步:堆棧段開辟,并賦初始值0

stack segment stack

db 128 dup(0)

stack ends

數(shù)據(jù)段先空著,宏定義這部分也先空著,后面根據(jù)需要再補(bǔ)(有需求,才有編程。。。)


我們先開始寫代碼段,先寫上加粗的兩句,這兩句基本上也屬于常規(guī)操作,基本所有程序的代碼段上來都是這個(gè)(只是個(gè)人經(jīng)驗(yàn),其實(shí)總共也沒編幾道題。。。。)

CODES SEGMENT

ASSUME CS:CODES,DS:DATA,SS:STACKS

START:

MOV SS STACKS

MOV DS,DATAS

?MOV AH,4CH//DOS功能,表示代碼結(jié)束

?INT 21H

CODES ENDS

END START

原因就是匯編是對(duì)寄存器的操作,寄存器一共有16個(gè),而寄存器的種類是有劃分的,其中段寄存器是用來存放段基址的(為什么會(huì)有這個(gè)概念是有歷史原因的,個(gè)人認(rèn)為可以暫且忽略,只需要知道段寄存器中的段基址是需要一開始就寫好的)

通過看這張圖,我們雖然不能掌握每一個(gè)寄存器的應(yīng)用,但是我們可以知道CS和SS兩個(gè)寄存器是很特殊的,他們叫段寄存器,是用來存放段基址的

但是到這里大體上的意思是對(duì)了,但是還有一點(diǎn)小問題

那就是MOV指令是不能把段名傳給段寄存器的,所以需要改一下,先把段名(段基址)傳給一個(gè)通用寄存器,再傳給段寄存器

MOV AX STACKS

MOV SS AX

MOV AX DATAS

MOV DS,AX


接下來為了簡(jiǎn)潔美觀,我們需要輸出提示語,為了鍛煉我們用宏的能力,我們就用宏來寫輸出提示語這一部分

首先是定義宏,宏的格式如下:

putstr(宏名) macro(宏關(guān)鍵詞) string(傳入的參數(shù))

lea dx string//把字符串的首地址傳入9號(hào)功能指定的寄存器 ,這里的string是形式參數(shù)(這個(gè)lea指令前面說過,英文源于LOAD EFFECT ADDRESS)

mov ah 09h//這里要調(diào)用dos的輸出字符串功能,是9號(hào)功能

int 21h



接下里就是在代碼段里調(diào)用宏

CODES SEGMENT

ASSUME CS:CODES,DS:DATAS,SS:STACKS

START:

MOV AX STACKS

MOV SS AX

MOV AX DATAS

MOV DS,AX

putstr prompt1//注意這里的prompt1是實(shí)際參數(shù),而這個(gè)實(shí)際參數(shù)是需要用前面介紹的數(shù)據(jù)段定義字符串的方式來定義的

MOV AH,4CH//DOS功能,表示代碼結(jié)束

INT 21H

CODES ENDS

END START


接下來按照程序的需求,該從控制臺(tái)讀入數(shù)字了

雖然我們說是從控制臺(tái)讀數(shù)字,但實(shí)際上讀的是字符串,而字符串是按照ASCII碼存的,也就是說內(nèi)存中實(shí)際上存的是ASCII碼,所以我們需要把讀入的字符ASCII變?yōu)閿?shù)值(這點(diǎn)看看ASCII表就知道是要把數(shù)字字符對(duì)應(yīng)的ASCII減去48就可以了,但是匯編中記得用16進(jìn)制喲?。∷允?0h)

這是一個(gè)經(jīng)常要做的事,所以寫成一個(gè)函數(shù)(為什么不寫成一個(gè)宏?答:這是由前面說過的宏只能傳入字符串的局限所導(dǎo)致的),函數(shù)的功能就是傳入字符,可以得到相應(yīng)的數(shù)值,以后直接調(diào)用即可

函數(shù)(匯編叫過程,從關(guān)鍵字proc(procedure就可以看出來))的標(biāo)準(zhǔn)格式是

接下來是具體的代碼,在這一部分只解釋代碼含義,不探討編程思維(也就是為什么這么寫的問題)

transform1(過程名) proc(這是關(guān)鍵字)

mov neg_flag,0//neg_flag是符號(hào)標(biāo)記(數(shù)據(jù)段要定義),規(guī)定0為正1為負(fù),初始neg_flag為0,相當(dāng)于是先默認(rèn)為正數(shù)

?可以跳過多余的空格,具體的操作就是利用循環(huán),跳過空格

skip_space://這是程序段的標(biāo)號(hào)(標(biāo)號(hào)我理解的就是一個(gè)類似于索引的東西,匯編可以很神奇地通過程序中的標(biāo)號(hào)找到標(biāo)號(hào)對(duì)應(yīng)程序的位置),用于跳轉(zhuǎn),可以跳過多余的空格,具體的操作就是利用循環(huán),跳過空格

getchar//由于經(jīng)常使用,所以定義一個(gè)宏,所以要定義一個(gè)宏,作用就是利用DOS功能01把輸入的字符對(duì)應(yīng)的ASCII碼值放到AL寄存器中

cmp al, ' ' ;//又是一個(gè)新指令,指令的作用就是通過前一個(gè)操作數(shù)減去后一個(gè)操作數(shù),之后根據(jù)運(yùn)算結(jié)果改變標(biāo)志位

je skip_space ;//又是一個(gè)新指令,這個(gè)指令英文:judge equal,原理就是看上一條指令運(yùn)算結(jié)果對(duì)于標(biāo)志位的改變,借此來判斷al是不是空格



這張圖沒有任何含義,我只是有點(diǎn)累了。。。。不想寫了。。。。。



在進(jìn)入下面這些語句的時(shí)候,AL中已經(jīng)有了值了,要么是數(shù)據(jù),要么是負(fù)號(hào),反正不是空格。。。

cmp al, '-';判斷有沒有負(fù)號(hào),跟空格的判斷一樣

jne next1;新指令(其實(shí)也不新,就是judge unequal,類比judge equal)若不相等,那么就是正數(shù),那么標(biāo)志位ZF=0,跳轉(zhuǎn)到next1標(biāo)號(hào)位置

mov neg_flag,1 ;這里是負(fù)數(shù)的標(biāo)記,如果上一條指令成立,那么不會(huì)走到這條指令,如果走到這里了,那就說明是負(fù)數(shù),于是就是neg_flag=1

getchar;獲取字符,此時(shí)應(yīng)該就是負(fù)數(shù)的數(shù)字了

next1:

mov di, 0 ;di來保存換成的數(shù)值,di本身是目的地址寄存器,但由于屬于通用寄存器,所以也可以用來存數(shù)據(jù),注意此時(shí)的di是一個(gè)16位的寄存器,這一點(diǎn)對(duì)于后面的程序是有影響的

input_loop:看這個(gè)樣子就知道是一個(gè)用于循環(huán)的程序標(biāo)號(hào)

sub al, 30h; (0-9)asc碼轉(zhuǎn) 數(shù)值,我們以3為例

mov cl, al ; 轉(zhuǎn)出的數(shù)值保存到cl(8位寄存器到8位寄存器),此時(shí)cl=3,al=3

mov ax, di; 此時(shí)ax=0,注意上一行al=3,但此時(shí)ax=0使得al也等于0了,因?yàn)閍x中包含al

mov bx, 10;bx=10

mul bx? ? ;又是一條新指令,mul做的是無符號(hào)的乘法,具體操作要看操作數(shù)是字還是字節(jié),如果操作數(shù)是字節(jié)操作數(shù),則把AL中的無符號(hào)數(shù)與SRC相乘得到16位結(jié)果送AX中,即:AX←(AL)*(SRC)。如果SRC是字操作數(shù),則把AX中的無符號(hào)數(shù)與SRC相乘得到32位結(jié)果送DX和AX中,DX存高16位,AX存低16位,我們這里是字操作數(shù)所以結(jié)果存到了DX:AX兩個(gè)寄存器中,此時(shí)DX、AX都是零

mov ch,0;讓ch(8位寄存器為)為0

add ax, cx ;之前的di的數(shù)值+ 轉(zhuǎn)后的數(shù)值,此時(shí)ax結(jié)果為3

mov di, ax ;保存結(jié)果到di,此時(shí)di=3,ax=3

getchar;接著獲取字符,字符的ASCII放到AL中

cmp al,0dh ;遇到回車,認(rèn)為輸入完一個(gè)整數(shù),在這部分由于DOS功能默認(rèn)選擇AL寄存器為輸入字符的存儲(chǔ)位置,因此破壞了之前AX=3

je int_quit

cmp al,' ' ;遇到空格

je int_quit? ?

jmp input_loop;相當(dāng)于是無條件的跳轉(zhuǎn)

int_quit:

mov ax,di ;save di to ax,這里恢復(fù)之前由DOS功能導(dǎo)致的AX值的改變,讓AX重新為結(jié)果3(這就告訴我們?nèi)绻覀兿胍袮X作為結(jié)果,那么中間一旦使用DOS的某些功能就一定要把AX中的值先進(jìn)行保護(hù),這里我們用的是di進(jìn)行了保護(hù))

mov bx, neg_flag;把符號(hào)標(biāo)記給bx

cmp bx, 1;看bx是否為正數(shù)

jne LN;若不是正數(shù),跳到LN

neg ax ;又是新指令,但并不復(fù)雜,就是取反而已,放在負(fù)數(shù)的判斷之后

LN:

ret

transform2

?endp

注意

(1)在這個(gè)過程中要注意對(duì)于寄存器高低位的使用,Ax=AH與AL拼接,所以即便AL之前有值,如果之后對(duì)AX賦值0,那么AL也將是0

(2)MUL這個(gè)指令做的是無符號(hào)的乘法,所以是絕對(duì)值相乘,結(jié)果都是正數(shù),如果是負(fù)數(shù)的話,之后要再用neg取反


現(xiàn)在通過這個(gè)函數(shù),數(shù)值3已經(jīng)放到了AX里了,同理,再調(diào)用一次另外一個(gè)數(shù)值,暫且定為4,也可以得到,由于兩次都是放在AX中,所以一定要定義其他的變量,以便保存數(shù)據(jù)

call transform1

mov num1, ax第一個(gè)數(shù)

call transform1

mov num2, ax第二個(gè)數(shù)

mov ax, num1由于后面要做平方,所以有一個(gè)數(shù)必須放到AX里,另外一個(gè)數(shù)可以隨便放

mov bx, num1此時(shí)放到了BX里

mul bx無符號(hào)乘法,字操作數(shù),所以結(jié)果放到DX:AX中

mov square_num1, ax但是在這里我們只取AX中的數(shù)據(jù),這就造成了超出一定的范圍之后,結(jié)果會(huì)錯(cuò)誤,不過100以內(nèi)的演示是沒有問題的

mov ax, num2

mov bx, num2

mul bx

mov square_num2, ax

mov ax, square_num1

sub ax, square_num2

現(xiàn)在我們已經(jīng)得到了結(jié)果了,接下來是把這個(gè)結(jié)果顯示到屏幕上,顯示的是字符,所以我們要把結(jié)果的數(shù)值變?yōu)閷?duì)應(yīng)的ASCII值

為了讓主程序簡(jiǎn)潔,所以我們就再寫一個(gè)函數(shù)

;整數(shù)值轉(zhuǎn)asc碼

transform2 proc

cmp ax, 0

jge LW;新的一條指令,當(dāng)大于等于時(shí)轉(zhuǎn)移

neg ax? ? ? ;如果是負(fù)數(shù),轉(zhuǎn)成正數(shù)

putchar '-' ;調(diào)用宏輸出負(fù)號(hào)標(biāo)記

LW:

mov cx, 0; cx清零待用,功能是看數(shù)直結(jié)果需要幾個(gè)字符來表示

?cal_loop:一看就是循環(huán)用的標(biāo)號(hào)

mov dx,0? ;dx清0,準(zhǔn)備除法

mov bx,10

div bx? ? ;整數(shù)除以10,div指令是無符號(hào)除法,分為字節(jié)型與字型兩種,具體用哪一種取決于div reg中reg這個(gè)寄存器的位數(shù),其中字節(jié)除法的結(jié)果商在al中,余數(shù)在ah中,字除法商在ax中,余數(shù)在dx中,所以當(dāng)前這句話的余數(shù)在dx中

push dx? ? ;余數(shù)dx入棧

inc cx? ? ;新指令,就是相當(dāng)于i++,是一個(gè)自加的指令,指令每運(yùn)行一次cx加一計(jì),算需要幾位asc2碼 (前面清零是為了這里用)

cmp ax,0

jne cal_loop;只要ax不等于0,就一直取出余數(shù)

show_loop:

pop dx ;取出余數(shù)

add dl,30h; '0'把余數(shù)的數(shù)值對(duì)應(yīng)的ASCII碼算出來

putchar dl 調(diào)用宏打印字符

loop? show_loop;新指令,很重要,是循環(huán)語句的重要部分,之前我們的那種跳轉(zhuǎn)循環(huán)是利用自己的cmp指令來控制的(相當(dāng)于while),而現(xiàn)在可以用cx的計(jì)數(shù)來控制(相當(dāng)于for循環(huán)),loop每循環(huán)一次cx就會(huì)減一,當(dāng)cx=0時(shí),循環(huán)結(jié)束跳出

ret

WriteInt?

endp

最后是主函數(shù)

mov ax, stack

mov ss, ax

mov ax, data

mov ds, ax?

putstr prompt1

?;輸入第一個(gè)數(shù)?

?call transform1

mov num1, ax

;輸入第2個(gè)數(shù)?

call transform1

mov num2, ax

mov ax, num1

mov bx, num1

mul bx

mov square_num1, ax

mov ax, num2

mov bx, num2

mul bx

mov square_num2, ax

mov ax, square_num1

sub ax, square_num2

call transform2

?mov ah, 4ch ;返回dos

int 21h


到此我們把所有重要的部分都過了一遍了,代碼我基本也把注釋寫清楚了,只要自己找兩個(gè)數(shù)據(jù)自己人肉編譯一下,基本就理解了。

接下來是最后一部分,大致理解一下匯編思路

一般來說一個(gè)程序需求我們都是可以理解的,我感覺難點(diǎn)在于如何用匯編語言描述,這次這個(gè)例題中如何將一個(gè)字符,轉(zhuǎn)為對(duì)應(yīng)的數(shù)值比較難的,看似一個(gè)減48的操作并不難,但是由于要處理正負(fù)號(hào)、回車、空格的問題導(dǎo)致有些寄存器中的數(shù)據(jù)需要被保護(hù),進(jìn)而增加了程序的復(fù)雜性,所以在匯編中要時(shí)刻保持對(duì)于寄存器的敏銳,時(shí)刻關(guān)注指令對(duì)于寄存器的影響(尤其是那些有默認(rèn)寄存器的指令比如mul,甚至是DOS功能下的默認(rèn)寄存器使用都是需要注意的)



2018.5.8補(bǔ)

前面所寫的這些在正確性上或許會(huì)有不嚴(yán)謹(jǐn)甚至是有疏漏的地方,后續(xù)我還會(huì)再查一遍

今天想說的是,之前有幾點(diǎn)我沒有注意到,今天在我匯編老師的指導(dǎo)下才注意到

(1)程序中缺少“邊界限制”,這個(gè)限制指的就是對(duì)于用戶不合法的輸出,程序應(yīng)該能夠處理

舉個(gè)例子:

比如明明應(yīng)該輸入0-9的字符,結(jié)果輸入了一個(gè)‘#’,這個(gè)時(shí)候程序應(yīng)該給出一定的反饋,比如

input error這種提示語,并且讓用戶重新輸入才行

(2)第二個(gè)就是要通過測(cè)試來發(fā)現(xiàn)自己程序的缺陷,并且最好改正過來

舉例

比如你的程序只能輸出單一字符,不能輸出12 345這種,那我最好就要考慮如何解決這個(gè)問題(千萬不要只是完成功能就完了,那樣不太好。。。)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容