程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 匯編語言程序設計(五)

匯編語言程序設計(五)

編輯:匯編語言

四、橋式法:

橋式法是利用讀寫記憶體的特性,將程式中若干指令直接填入,作為臨時便橋,以改變此段程式的功能。
例如在顯示時,希望能提供多種變化,而又不願減低速度及增加太多的程式。最好的方法,便是利用橋式法,在同一位址,填入需要的指令。
橋式法用得好而又靈活時,對程式的效率極有助益。但是應該注意一點,就是只能用在可讀可寫的記憶區中,如若要制成「韌體」,即置入僅讀記憶體(ROM) 中的程式,絕不可使用此法。
下面的實例,即為螢幕顯示的橋式應用。首先,把架橋的「材料」設置在緩沖器中,如:
CDSPMOD DB 88H ;8805 = MOV [DI],AL
DB 30H ;3005 = XOR [DI],AL
DB 08H ;0805 = OR [DI],AL
DB 20H ;2005 = AND [DI],AL
CDSPMOD 即為緩沖器,其中有四個數據,分別為機器碼的相異部份,如分號後所注。因為四組機器碼皆有 05 ,不必再填。=右邊部份,即為該機器語言相對應的指令。
程式部份先設妥功能定義,利用一、所說的變數應用法,依序由0至3先載入暫存器BX中。根據 BX 值,將所需機器碼載入程式中。
10:CLOD:
11: MOV AL,CDSPMOD[BX] ;用BX取預存碼
12: MOV CS:CDSP2[1],AL ;載入CDSP2+1
13:CDSP:
14: SUB SI,SI ;資料由0起
15:CDSP1:
16: LODSB ;取資料
17:CDSP2 LABEL BYTE ;載入的位置
18: XOR ES:[DI],AL ;原碼26 30 05
19: INC DI ;須改 30 部份
20: LOOP CDSP1
21: RET

五、流水線法:

工業上的流水線生產作業,需要極為嚴格的規格限制,原器件分別研制完畢後,統一送到生產線上組裝。
程式亦可采用同樣的方法,只可惜一時手頭上找不到現成的、適用的例子,只得將方法概述如下:
先設定處理流程,凡是能用這種生產流程者,皆適用。
再設定處理流程中所采用的「生產線」,也就是緩沖器。因為流水線上所用的資料都需要由緩沖器提供。
此緩沖器的長度由流程決定,緩沖器中的資料則由各調用本流程的原程式載入。
各調用程式可視個別條件,將所需處理的資料,放在緩沖器內(全部或部份)。待調用後,再從原緩沖器中取出經過處理後的資料。

六、對應表法

凡是指根據某種需要,將經過整理的資料,以某種固定的格式,安排在一特定區域中。每當需要時,立刻可以按照排列的位置取出來使用的,皆可稱之為對應表。
這種對應表是我最喜歡利用的技巧,速度奇快不說,修改也極其容易。尤其是我做事一向不拘小節,寫起程式來,專出小錯。自從采用了表格對照法後,凡是適合這種形式的程式,只要想通了最理想的結構,幾個指令就把程式寫完了。
茲將附錄中所舉的例子,對字形放大所采用的查表法,在此作進一步的介紹。
假設有一組圖形,要在螢幕上左右放大一倍。一般程式師做這種題目,都是在暫存器內移來移去,每一個字元的資料,起碼要移八次之多,每次都要用借位作為轉換值。而轉換時,又要放進一個16位元的暫存器中,盡管可以用回路去做,時間的延誤相當大,讀者可參考附錄二以做比較。
當然,表格要占用空間,以本例而言,如果一次用256B,取足則要512B。
因此這種技術可以說是以空間換取時間。在第一章第三節「效率」的第四條定律下,我們知道鍵盤輸入速度,決定於人的操作速度,而人的反應遠遠不及電腦,故應以人的速度為時間邊際值,盡量設法節省。
目前,所涉及的是顯示時間,每個人在電腦前,都期望著立即得到結果。因此,顯示速度不僅要快,而且越快越好。所以,前述的空時交換應在可能范圍中,視實際的邊際效應,以作取捨。
現在看看資料分析,下面列舉的二進位資料,在左邊為原圖形點陣,在右邊則為放大一倍後的點陣:
原點陣 左右放大一倍
00000001 00000000 00000011
00000010 00000000 00001100
00000011 00000000 00001111
..
01010101 00110011 00110011
..
11111111 11111111 11111111
現在有兩個因素非常明顯,第一,不論什麼點陣,放大後長度加一倍,一字元有256 種。放大後點形種類不變,但字元數加倍為 512個。其次,由於放大後的 512個中,有一半皆相同,故仍可用256 種表示。
至於取前者或後者,當視情況而定。
決定以後,將之定義在緩沖器中,以原圖形的點陣資料作為索引值,即可采間接定址法,立即取得放大後點陣。
在制作對應表時,應養成良好的習慣,根據資料的規則,以等長度、固定的格式輸入。這樣不僅對表中的資料能一目瞭然,而且容易輸入、偵錯、修改,一舉數得。
如某表格為:
100 TBXXX DB 0,1,3,7,0FH,1FH,3FH,7FH,0FFH,2,6,0EH,1EH,3EH,7EH,0FEH
此表看去遠不如下表來得清楚、規律:
100 TBXXX DB 000H,001H,003H,007H,00FH,01FH,03FH,07FH
200 DB 0FFH,002H,006H,00EH,01EH,03EH,07EH,0FEH
從事程式寫作,規律的思考方式及追求,經常事半功倍。這種小技巧看似沒有多大作用。事實上,在輸入時,規則化的結構可以輕易地利用現有的功能,或復制,或修改。更有利的是能一眼看出該表的意義及正確性,在程式偵錯時,往往可以節省大量的時間。

七、模式法

所謂模式法,是指在程式的處理過程中,分析其規律,以期找到一種共同具有的「模式」。並用此模式,設計成為一個個程式單元,以追求最高效率。
這種模式,可用「概念」來代表,但最理想的表達方法,仍以視覺圖形為宜。也就是說,最好能把分析出來的模式,用圖形表示,並據以理解及設計程式。
茲以常用的功能「排序」為例,來說明模式法的應用,並設計成為程式。
先假定需要排序的資料結構為:
11每筆資料之長度固定為一字元。
12資料形式為 ASCII碼,16進位值,由 20H到 7EH。


13排序時,資料數值小者排在低位,大者排在高位。
14程式開始時參數設定為:
AL= 高位之資料。
AH= 低位之資料。
DS:SI=資料存貯處。
資料由低位開始檢查,並同時排序,直到全部查完為止。排序時,交換高位及低位之資料,以使
高位住址資料≧低位住址中之資料。
由於人類行為與視覺息息相關,故最有效的認知方式,是以作圖來說明。以下即為上一陳述之圖形說明。

│? │
模式一供檢查 ├─┤
AL,AH 之大小 ┌ AH <--│? │<-- AL ┐
模 │ ├─┤ │模
模式二交換資料 式 ┤ AL <--│? │<-- AH ├式
其中 AH>AL 一 │ ├─┤ │二
└ SI = │? │ = SI ┘
├─┤

由上圖可見在模式一中,AH為低位資料,AL為高位資料。比較 AL,AH 之大小,即可知是否符合序列規定。如符合,則繼續做下去,否則依模式二,將小值放進低位,大值放進高位住址中。程式只要設法保持此一處理之形式,即可簡單明瞭地完成任務。
1: COMPAR:
2: MOV AH,AL ;設AH為低位值
3: COMPAR1:
4: LODSB ;取資料
5: CMP AL,AH ;比大、小
6: JAE COMPAR ;高位大,不變
7: MOV [SI-2],AX ;交換AH,AL,排序
8: DEC SI ;向低位再查
9: MOV AH,[SI-2]
10: JMP COMPAR1

當然,上面這段程式並不成立,因為沒有出口,永遠做不完。程式的終止有很多方法,一是用計數器,一是用位置來比較,也有用終止指令的,不一而足,各有長短。
首先,假設在DS:SI 中,有一長度值,茲以計數器的回路來試試看:
1: LODSW
2: MOV CX,AX ;似此,3B 18C
;若用 MOV CX,[SI]
; INC SI
; INC SI
;則需 6B,21T
3: SUB AL,AL ;先設最小值,備用
4: COMPAR:
5: MOV AH,AL ;設AH為低位值
6: COMPAR1:
7: LODSB ;取資料
8: CMP AL,AH ;比大、小
9: JB COMPAR2 ;低位大,需排序
10: LOOP COMPAR ;回路
11: RET ;完成
12: COMPAR2:
13: MOV [SI-2],AX ;交換AH,AL,排序
14: DEC SI ;向低位再查
15: MOV AH,[SI-2]
16: JMP COMPAR1
程式中的回路,對前面有一比較分支不太有利,因為回路每次要17T ,比較分支就是現成的回路,不利用形成浪費。
若把回路改為位置比較,程式即為:
1: MOV CX,SI
2: ADD CX,[SI]
3: INC SI
4: INC SI
5: SUB AL,AL ;先設為最小值,備用
6: COMPAR:
7: MOV AH,AL ;設AH為低位值
8: COMPAR1:
9: LODSB ;取資料
10: CMP SI,CX ;比位置到終點?
11: JAE COMRET ;完成
12: CMP AL,AH ;比大、小
13: JAE COMPAR ;高位大,再查
14: MOV [SI-2],AX ;交換AH,AL,排序
15: DEC SI ;向低位再查
16: MOV AH,[SI-2]
17: JMP COMPAR1
18: COMRET:
19: RET
如此,在分支時,在第13條指令做回路,10,11 則比較住址以決定是否完成。這一來,完成結束只有一次,需時 16T,其余所有執行時間皆為4T,較前一回路快了13T 之多。
再試用「終止指令」法,其必要條件為資料中有多余的組合可供選擇。一般多以 00H,0FFH 等極端值比較理想,下面且以0FFH作為終止指令,並置於資料終止處。
1: MOV CL,0FFH ;終止檢查用
2: SUB AL,AL ;先設為最小值,備用
3: COMPAR:
4: MOV AH,AL ;設AH為低位值
5: COMPAR1:
6: LODSB ;取資料
7: CMP AL,CL ;比是否終止指令?
8: JAE COMRET ;完成
9: CMP AL,AH ;比大、小
10: JAE COMPAR ;高位大,再查
11: MOV [SI-2],AX ;交換AH,AL,排序
12: DEC SI ;向低位再查
13: MOV AH,[SI-2]
14: JMP COMPAR1
15: COMRET:
16: RET
似此,程式較短,其他效果差不多。
這段程式,在處理速度上,還大有油水。因為已經檢查過的資料,因為回路關系,還會不斷地重復檢查,是否能夠避免這種情況呢?

事實上,當排序到某住址時,即表示由該住址起,上面已經檢查完畢。因此,只要記錄下來,下次再查時,將住址還原即可。
1: MOV CL,0FFH ;終止檢查用
2: COMPAR0:
3: SUB AL,AL ;先設為最小值,備用
4: COMPAR:
5: MOV AH,AL ;設AH為低位值
6: COMPAR1:
7: LODSB ;取資料
8: CMP AL,CL ;比是否終止指令?
9: JAE COMRET ;完成
10: CMP AL,AH ;比大、小
11: JAE COMPAR ;高位大,再查
12: MOV DI,SI ;暫時保存
13: COMPAR2:
14: MOV [SI-2],AX ;交換AH,AL,排序
15: DEC SI ;向低位再查
16: MOV AH,[SI-2]


17: LODSB ;取排序資料
18: CMP AL,AH ;比是否該排
19: JB COMPAR2 ;是
20: MOV SI,DI ;否,將原位址還原
21: JMP COMPAR0 ;從頭再做
22: COMRET:
23: RET
總而言之,程式的變化無窮無盡,尤其是用組合語言制作程式,更是靈活精妙。就像下圍棋一般,往往一兩個指令就足以將整個局勢扭轉過來。
程式的效率經常決定在回路上,讀者千萬不要以為一兩個時鐘脈沖算不了什麼。要知道,汪洋大海,也是由一點一滴的水珠累積而成的。
這段程式還有不少值得深思的,讀者們不妨自行研究吧!想得多了,自然會有生花妙筆。

真要作大量的資料排序,還有更有效的方法,也是應用模式分析的原則,先找出資料的「型」。
假如以同樣性質的資料為例,為了避免資料一一查找,浪費時間。我們不妨研究一下,是否有可能,直截了當,就把資料依據大小,予以定位,一次排好?
電腦的好處,就在於資料的規律性,我們理應利用這種優點,來找出其排序的模式。
前例曾分為兩個模式,一是查找,一是搬移。如果我們把查找改為記錄,把搬移改為安排,則情形就大大的不同了。
記錄時利用間接定址技巧,每筆取到的資料,皆可視為一個數值,對應於一記錄的緩沖區。如果資料中每筆資料總值大於256 且小於 65536,則可以用二字元記錄之。再若資料為二進位值,可由 0至 255,即設 512個對應單位。
假定原資料在 DS:SI中,長度在CX中。
首先,設一記錄區為:
1: RECORD DB 512 DUP (0)
2: PUSH SI ;程式開始
3: CHECK:
4: LODSB ;取資料,AH永遠為0
5: MOV BX,AX ;利用BX間接定址
6: INC WORD PTR RECORD[BX]
7: LOOP CHECK
8: STORE:
9: MOV SI,OFFSET RECORD+512;指向最
;後記錄
10: MOV BP,OFFSET RECORD ;供檢查
11: POP DI ;資料貯存處
12: STORE1:
13: CMP SI,BP ;查是否完畢?
14: JE RECEND ;完成
15: DEC SI ;向上取
16: DEC SI
17: MOV CX,[SI] ;取記錄值
18: JCXZ STORE1 ;無記錄,重取
19: MOV AX,SI ;當前之位址
20: SUB AX,BP ;差值
21: SHR AX,1 ;原有值
22: STORE2:
23: REP STOSW ;重新載入
24: JMP STORE1 ;繼續
25: RECEND:
26: RET
程式的變化無窮無盡,尤其是用組合語言寫作程式,簡直沒有止境。只要稍稍用點心,加一點點變化因素,一個巧妙無比的程式,就會躍然而出。
寫程式的樂趣,就在於心智的投入。學者們不妨試著把這 式再加以改良,其中還有不少可以下手的地方,養成習慣以後,程式自然就會精簡了。

八、預置法

預置法適用於流程的安排,尤其是在不確定的情況下,有時需要作多項檢查,不僅浪費時間,對空間也不利。
例如有一段程式,其目的在於處理使用者所選擇的流程。由於使用者事先通過介面程式,選妥各項工作,現在必須依某一順序執行。
這是一項難度相當大的工作,要執行固定順序不難,下面的程式就可以達到目的。當然,一如既往,我們會嘗試著將程式一再改進。最後,我們再來討論如何能執行使用者所安排的順序。
設子流程有八種,使用者選用時,可令BX值等於子程式的代號。選用方式為「開關式」,即單數次為開,設定參數,復數次為關,取消設定。
設定後,因為共有八種程式,可以用八個位元來設置所需要執行的旗號。當然,這要看程式的多少而定,八位元正好用一個旗號FLAG:
1: SETUP:
2: CMP BX,MAXVAL ; 最大值檢查
3: JA SETRET ; 超過,無效
4: SHL BX,1 ; 參數乘2
5: JMP SUBTB[BX] ; 各種程式
6: SUBTB DW SUB1 ; 各種程式
7: DW SUB2 ; 程式中設定
8: .. ; flag
9: DW SUBN
10: ENTER:
11: SHR FLAG,1 ; 檢查FLAG
12: JNC ENTER1
13: CALL SUB1 ; 有設定
14: ENTER1:
15: SHR FLAG,1
16: JNC ENTER2
17: CALL SUB2
18: ENTER2:
19: .. ; 如此連續進行八次
顯然這種做法其笨無比,第十條以後,可用回路取代:
10: ENTER:
11: MOV CX,8
12: MOV AL,FLAG ; 暫存器較有效
13: OR AL,AL
14: JZ ENTRET ; 不必做
15: SUB BX,BX
16: LOOP0:
17: SHR AL,1
18: JNC LOOP1
19: PUSH AX
20: PUSH BX
21: PUSH CX
22: CALL SUBTB[BX]
23: POP CX
24: POP BX
25: POP AX
26: LOOP1:
27: INC BX
28: INC BX
29: LOOP LOOP0
30: ENTRET:
31: RET
這樣好得多了,可是,還能不能再加改進呢?組合語言的妙處就在於變化無窮,且看看是否還能變出花樣來。

從設置開始,方式稍微改變一下,旗號的觀念是供程式檢查用。在應用時,要占用一個暫存器,而暫存器有限,浪費了可惜。此外,八個不同的子程式,又要占用一個計數用的暫存器,最好能夠省掉。
因此,設置的重要性就顯而易見了,程式的好壞,並非僅僅在於指令的應用。原始的理念,及程式的規劃,經常在程式設計之前已經決定了。


我們稱之為「預置法」,把前述的設置方式改變一下,用一組緩沖區,先定義如下:
DB 0 ; 計數用
BUFER DW 8 DUP (0) ; 存程式入口用
DW 1 ; 終止信號
然後再設計程式,預置及執行如次:
1: SETUP:
2: CMP BX,MAXVAL ; 最大值檢查
3: JA SETRET ; 超過,無效
4: SHL BX,1 ; 參數乘2
5: ADD BX,SUBTB
6: JMP BX ; 各種程式
7: SUB3:
8: XOR BUUER,BX ; 設為第三組
9: JNZ SUB31 ; 開
10: SUB30:
11: DEC BUFER-1 ; 取消
12: RET
13: SUB31: INC BUFER_1 ; 計數
14: RET
15: SUBTB DW SUB1
16: DW SUB2
..
21: DW SUBN
22: ENTER:
23: MOV SI,OFFSET BUFER-1
24: LODSB ; 查是否需要
25: OR AL,AL ; 為0則無
26: JZ ENTRET
27: ENTER1:
28: LODSW ; 取程式資料
29: CMP AX,1 ; 查程式入口
30: JB ENTER1 ; 0表示不做
31: JZ ENTRET ; 1表示終止
32: PUSH SI
33: CALL AX ; 執行
34: POP SI
35: JMP ENTER1
36: ENTRET:
37: RET
前一段調用程式需要31個字元,而現在只要21個字元,速度也快得多。不僅如此,前段程式僅能提供八個子程式,最多用十六位元,不過十六個子程式。本程式則不然,只要預留的緩沖器夠,可提供的子程式可以說是無限。
更重要的功能,是程式執行的順序。除了這種預置法外,其他的方法,都受限於 SUBTB的安排次序,無法變更。但本方法則完全可任依使用者的需要,來決定子程式執行的順序,以及是否執行。
請注意在 SETUP時,BX的參數就同時代表了執行的順序。如果要想依照設定的次序決定順序,只要將緩沖區加大,再加一組預設程式即可,如下所示:
1: SETUP:
2: SHL AX,1 ; 輸入參數
3: ADD AX,OFFSET SUBTB ; 子程式入口
4: MOV BX,BUFER-2 ; 位置序數
5: SHL BX,1 ; 指向位置
6: MOV BUFDER[BX],AX ; 存入緩沖區
7: INC WORD PTR BUFER-2; 序數加一
8: RET
這一來,先調用的程式放在前面,後調用的放在後面,使用者只要知道子程式的代號,就可以隨意安排調用。
甚至於各子程式所需的參數,也可以用類似的方法,預先設置妥當,然後一次取出運用。
預置法最宜於「用戶」接口,而且作為應用程式,既簡單又容易,方便靈活。
比如有一些應用模組,即可應用此方法,分別歸類、編號後,書於手冊中,以提供使用者選擇、應用。
使用者選擇介面的方法,可以通過螢幕提示,將各種模組顯示在指定位置上。使用者利用游標,或其它選擇方法,以求得到正確的編號,再依序置於緩沖區中。
各種模組都可能需要輸入參數或資料,所以,另外要准備一個參數緩沖區,在選擇模組時,同時選擇參數。由於各模組會自動取用參數,故只要置入即可。
假設有一個「使用者自行設計程式」的工具套件,(“聚珍整合系統”就建立在這觀念上,惜因我們人手不足,產品可能要到1991年才能上市。)螢幕提示有介面、功能、共用等各類模組,使用者選完一類後,螢幕再度提示該模組的編號。
螢幕上的模組編號經過程式轉換,得到程式編號,將此編號存入緩沖區,再查是否需要輸入資料。即可按照原有流程設計,逐步執行下去,完全可以利用這種預置法。
1: GETMOD:
2: SUB AX,AX
3: INT 16H ; 使用者輸入
4: CALL GETSUB ; 轉換為代號
5: SETUP: ; 代號置於AX
6: SHL AX,1 ; 次序乘二
7: ADD AX,OFFSET SUBTB ; 子程式入口
8: MOV BX,BUFER-2
9: SHL BX,1
10: MOV BUFFER[BX],AX
11: INC WORD PTR BUFER-2
12: JMP GETVAR ; 查取參數否
當然,真正可以應用的程式,還要考慮很多因素,但大致上,結構就這樣簡單。
寫程式和畫畫沒有兩樣,多看、多參考別人的程式,多想、多鑽研各種方法,最後則是要多多動手,除此之外,別無其他法門。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved