技巧的運用,關係組合語言的效率甚鉅,雖然其重要性比不上整體規劃,但也可以彌補規劃的不足。
由於「技巧」無法嚴格定義,茲將幾種較常用的技巧介紹如下:
一、變數法:
我非常反對在程式中採用「常數」的觀念,因為常數是固定的,無法靈活應用。例如在 IBM PC 的 BIOS 中,螢幕游標上、下、左、右位置固定設為 0,25,0,80 等常數值。每次移動都受到這四個值的限制,故而形成不變的「視區」。若將這些常數改為變數,且容許使用者自行改變,則立即有了可變「視窗」的功能。
也就是說,螢幕上、下、左、右四個位置,所圍起來的區域,就是我們視覺及資料所限制的「視窗」,所有資料顯示,在系統程式的控制下,無法超出此區。
如果此四個位置值是變數,則使用者可以隨意設定所需數值,如是則靈活方便,也就是所謂的視窗處理。
在下文三、虛擬法例中,CKFUN 該段程式即為用變數處理視窗的範例。下面這段程式,亦為變數法的一種應用, MAP87和MAP9A 中,均需調用子程式DYBPJ1,唯一不同者,是在該子程式中,又需分別調用不同的子程式。共用DYBPJ1的方法,是先將不同子程式的位址,放在BP中,再行調用。
1:MAP87:
2: MOV BP,OFFSET MAPF4
3: CALL MOVS211
4: CALL DYBPJ1
5: MOV DL,AH
6: ..
11:MAP9A:
12: MOV BP,OFFSET MAP46
13: CALL DYBPJ1
14: MOV AH,1
15: ..
20:DYBPJ1:
21: PUSH BX
22: PUSH DX
23: MOV DH,DL
24: CALL BP
25: POP AX
26: XCHG DH,AH
27: POP BX
28:DYBPJRT:
29: RET
二、對稱法:
本法實際上就是利用間接定址的指令,將原係對稱處理,或可以調整成為對稱型的程式,靈活調用。
下面這段程式,表面看來似乎毫不相干,但經過整理後,就有了眉目,然後再以暫存器間接定址,合併為一。像這種程式,規模越大,所節省的空間就越多。
1:ABCD:
2: CMP DX,BUFA
3: JB ABCD1
4: CMP CL,BUFD
5: JB ABCD1
6: MOV BUFC,CL
7: INC BUFE
8: MOV BUFB,DX
9: DEC BUFF
10: ABCD1:
11: ..
12: ..
與另一段程式:
20:EFGH:
21: CMP BX,BUFG
22: JB EFGH1
23: CMP CH,BUFH
24: JB EFGH1
25: INC BUFK
26: DEC BUFL
27: MOV BUFI,BX
28: MOV BUFJ,CH
29:EFGH1:
30: ..
31: ..
看來分別很大,先經過整理,得到左右兩組程式:
EFGH: ABCD:
CMP BX,BUFG CMP DX,BUFA
JB EFGH1 JB ABCD1
CMP CH,BUFH CMP CL,BUFD
JB EFGH1 JB ABCD1
INC BUFK INC BUFE
DEC BUFL DEC BUFF
MOV BUFI,BX MOV BUFB,DX
MOV BUFJ,CH MOV BUFC,CL
EFGH1: ABCD1:
.. ..
.. ..
對照之下可以看出,其所不同的,只是暫存器及緩衝器的分別而已。這種程式的出現,是由於事先規劃不當,未能通盤考慮,頭痛醫頭,腳痛醫腳。原可以把暫存器及緩衝器統一運用,現在木已成舟,想要變更很可能影響大局。
其次是寫作的風格及習慣沒有養成,在用指令時,信手拈來,以致兩段程式,兩種寫法!
現在唯一的補救方法,是利用間接定址法,先將兩組不同的緩衝器,照相對次序排列妥當,再改寫程式。
1:ABCD:
2: MOV SI,OFFSET BUFA
3: MOV DI,OFFSET BUFB
4: MOV BX,DX
5: MOV CH,CL
6: JMP SHORT EFGH1
7:EFGH:
8: MOV SI,OFFSET BUFG
9: MOV DI,OFFSET BUFI
10:EFGH1:
11: CMP BX,[SI]
12: JB EFGH2
13: CMP CH,[SI+2]
14: JB EFGH2
15: MOV [DI],BX
16: MOV [DI+2],CH
17: INC BYTE PTR[DI+3]
18: DEC BYTE PTR[DI+4]
19:EFGH2:
20: ..
其緩衝器的相對順序,如下所示:
35:BUFA DW 0
36:BUFD DB 0
37:BUFB DW 0
38:BUFC DB 0
39:BUFE DB 0
40:BUFF DB 0
41:..
65:BUFG DW 0
66:BUFH DB 0
67:BUFI DW 0
68:BUFJ DB 0
69:BUFK DB 0
70:BUFL DB 0
凡對稱形式或結構相同的程式,都可以採用這種技巧。
三、虛擬法:
對來處不同的資料,只要性質相同,都可以採用虛擬的技巧,將各種參數事先設妥,利用參數統一處理。
茲有一程式,係供螢幕畫圖之用,所有功能皆用游標完成之,特以此段處理游標的程式為例說明如後。
先虛擬各種功能及緩衝器:
區段位移: BBBLKMOV DB ? ;?= 位移值
十字游標: BBCROCSR DB ? ;?= 位移值
視框移動: BBFRMMOV DB ? ;?= 位移值
畫筆作圖: BBDRW DB ? ;?= 畫筆寬
橡皮擦: BBDEL DB ? ;?= 橡皮寬
閃動游標: BBCSR DB ? ;?= 游標寬
文字顯示: BBCHRDSP DB ? ;?= 字框值
游標移動值: BWMOV DW ? ;依當前功能先 ;設定
視框上限: BWTOP DW ? ;?= 設定值
視框下限: BWBTM DW ? ;?= 設定值
視框左限: BWLFT DW ? ;?= 設定值
視框右限: BWRGT DW ? ;?= 設定值
右界邊際值: BWADDX DW ? ;?= 字或圖寬
..
程式入口: CHKKEY DW CK47; 向左上移
DW CK48; 向上移
DW CK49; 向右上移
DW CRET; 無效
DW CK4B; 向左移
DW CRET; 無效
DW CK4D; 向右移
DW CRET; 無效
DW CK4F; 向左下移
DW CK50; 向下移
DW CK51; 向右下移
AX = 輸入游標鍵掃瞄碼
BP = 螢幕X向移動值
DX = 螢幕Y向移動值
ES:DI=螢幕記憶區位址
主流程: ( 已知 AL=0 AH=Scan-Code )
1:KEYIN:
2: SUB AH,47H ; =HOME
3: JB KEYRET ; 無效
4: CMP AH,11 ; ﹥掃瞄碼範圍
5: JA KEYRET
6: MOV BL,AH
7: SUB BH,BH
8: SHL BX,1
9: MOV AX,BWMOV ; 移動值
10: JMP CHKKEY[BX] ; 進入各處理程式
..
40:CK47:
41: SUB DX,AX ; Y 向
42:CK4B:
43: NEG AX ; 向左為負
44:CK49:
45: ADD BP,AX ; X 向
46: JMP SHORT CKFUN
47:CK4D:
48: SUB DX,AX
49: JMP CK49
50:CK51:
51: ADD DX,AX
52: JMP CK49
53:CK48:
54: NEG AX ; 向上為負
55:CK50:
56: ADD DX,AX
57:CKFUN: ; 用變數觀念檢查「視窗」
58: CMP DX,BWTOP ; 超出上限?
59: JNS CKFUN1
60: MOV DX,BWTOP ; 上限值
61:CKFUN1:
62: CMP DX,BWBTM ; 超出下限?
63: JBE CKFUN2
64: MOV DX,BWBTM ; 下限值
65:CKFUN2:
66: CMP BP,BWLFT ; 超出左限?
67: JNS CKFUN3
68: MOV BP,BWLFT ; 左限值
69:CKFUN3:
70: PUSH BP
71: ADD BP,BWADDX ; 右限+邊際值再比
72: CMP BP,BWRGT ; 超出右限?
73: POP BP
74: JBE CKFUN4
75: MOV BP,BWRGT ; 右限值
76:CKFUN4:
77: ..
CKFUN4以下為功能處理,由前面的功能參數決定。
用這種方法,多種功能可以共用一個入口,程式精簡且速度快。
四、橋式法:
橋式法是利用讀寫記憶體的特性,將程式中若干指令直接填入,作為臨時便橋,以改變此段程式的功能。
例如在顯示時,希望能提供多種變化,而又不願減低速度及增加太多的程式。最好的方法,便是利用橋式法,在同一位址,填入需要的指令。
橋式法用得好而又靈活時,對程式的效率極有助益。但是應該注意一點,就是只能用在可讀可寫的記憶區中,如若要製成「韌體」,即置入僅讀記憶體(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 ; 查取參數否
當然,真正可以應用的程式,還要考慮很多因素,但大致上,結構就這樣簡單。
寫程式和畫畫沒有兩樣,多看、多參考別人的程式,多想、多鑽研各種方法,最後則是要多多動手,除此之外,別無其他法門。