組合語言的藝術(轉)

heying1229發表於2007-07-28
組合語言的藝術(組合語言的藝術)--準備工作: 第一節 系統

一、系統之選擇

只因真正瞭解電腦的人太少,迷信名氣的結果,使得 IBM PC/AT佔有了市場。為求電腦普及應用,我們唯有因陋就簡,針對 IBM PC/AT及其相容系統,作進一步的分析和說明。
儘管使用 80486 CPU的微電腦已問世,我個人認為以其造價及功能而言,並不符合效率法則。在從事藝術工作的立場,價值的高低,與所採用的材料沒有絕對的關係。更何況我們正要證明,利用組合語言的製作技巧,能夠使功能不高的微電腦發揮最大的邊際效益。故本書僅以8088 CPU指令為研討物件,至於程式應用在什麼機型,就不在本書的考慮範圍內了。

二、目錄及磁碟

假定讀者為有經驗的程式師,且電腦為 IBM PC/XT/AT。 則在著手製作程式之初,應該先將系統準備妥當。即所謂「工欲善其事,必先利其器」,如果未能注意到下面所提的細節,僅僅製作一、兩個程式尚可應付。待工作量一大,程式漸漸增多,不是某些程式找不到,就是找到了也不能一目瞭然。結果是心煩意躁,馬馬虎虎,另外再寫一個,勉強應付了事。
硬碟空間較大,應行注意的事相對的也較多。首先應妥善規劃好目錄及子目錄的分類及應用方式,否則每當要找一個檔案時,往往有大海撈針之嘆。再不然,在修改程式時,如果將不同的版本放在一起,一定會張冠李戴,錯誤百出。
每位程式師都應該有自己的目錄,而且每個不同的整套檔案都應有不同的子目錄,這樣方能明確的分辨以及應用。而最有效率的方法是,在每一子目錄中,檔案數不宜太多,且僅存放在同一工作專案下所需要的程式。
一旦硬碟壞了,所有貯存資料均將付諸流水。為了,一定要用軟盤備份。最好每天覆制一次,雖然每次複製,相當麻煩,但是萬一程式被毀壞,那種損失是無從估計的。
最好的方法,是製作一個專門複製的,一般用 .bat 應用程式,每天工作完畢,只要執行一次即可。

三、應用

程式寫作是為了控制電腦運作,應用則是將程式師的觀念和理解轉化為能夠連續執行的指令。因此,市面上的應用雖多,卻不見得對程式的寫作有所助益。下面列了幾種,只要運用純熟,絕對夠用了。

程式寫作:EDLIN ,PE II,WS等。

程式編譯:masm.exe
早期 (1.25) 版本容許較多的「外界標題」,即external label,但該版本僅限用於8088指令,較宜大型程式處理。
4.0 以前的版本,於檔案開始時,可用.xcref以產生標題及緩衝器的位址對照表。但後期的版本會發生錯誤,不能再用。

程式聯接:link.exe
早期的版本對 segment public 'xxxx' 有些規定,但 3.0以後,除了對code及 data 有效外,其他如 byte 及 word 等皆無作用。

程式偵錯:debug.exe 或 symdeb.exe 及 mapsym.exe
後兩者為套件,必須另購。其優點為在偵錯過程中,可以使用原程式中的標題名稱,且可設「斷點」等,偵錯方便,功能較強。

系統處理:pctools.exe 是 dos下很有用的,可以作檔案管理,也可用來直接修改程式機器碼。此外,連機時可以用ll.exe 或xtalk.exe等。尤其是系統程式的偵錯,經常需要用聯機的方式,以另一臺微電腦作為監視器。

這些應統一放在 dos 子目錄中,且須在系統之自動執行程式(autoexec.bat)裡,先設妥優先通道(path)如:

1:PATH=DOS;
是則,不論在哪一個子目錄中,前述之皆可使用。

四、簡化名稱

前述各種,在程式偵錯過程中使用頻繁,為了應用上的方便,如果能少輸入一些字元,不僅節省時間,且可避免錯誤。令用者在慣性反應下,得以集中精神,思考其他細節。
簡化名稱是最簡單的方法,最好只用一個字母,由於長度相等,其後面的檔案名稱,就可以利用系統所提供的“F3”「複製上行」功能,不必再行輸入。

是以:edlin.com 改為 e.com
masm.exe 改為 m.exe
link.exe 改為 l.exe
debug.exe 改為 d.exe
symdeb.exe 改為 s.exe
例如:在完成一個程式後,若要彙編,只要改第一個字母即可。
c:>e myfile.asm ( 原來在系統中輸入的字元 )
此時僅輸入“M”,再按“F3”鍵,立即變為:
c:>m myfile.asm
此外,如不需 .lst 等檔案,則再加一“;”即可。
c:>m myfile.asm;
“L”及“D”等同樣都可依循上面的方法,看來是雕蟲小技,但實際上所節省的時間及精力卻不同凡響。

五、參考資料

除專門性的資料外,程式師應該備妥各種有關輸出、入,各種周邊裝置的參考資料或手冊等,以便隨手查閱。
最重要的是軟體參考手冊 MS-DOS Software Reference,其中有各種中斷命令的使用規格。唯應注意的,是有一些中斷功能與IBM BIOS相同,我建議讀者不要使用這些中斷,因其呼叫過程中,要借道BIOS,會使速度減慢。
其次為 IBM Programmer's Manual 的BIOS中斷規格, 是處理所有周邊裝置必需的。
正因為它涉及周邊裝置,在設計中文系統時,輸出入、顯示及列印等功能都有待修改,這本手冊更須徹底瞭解。
此外還有鍵盤的輸入碼錶,也是程式設計必備的資料,本書附錄三即為簡要的碼錶介紹。
如果是編寫應用程式,上述的資料已足夠應付,但若涉及系統,則尚需瞭解系統的記憶空間安排,下表為 IBM微電腦的記憶區結構。各種廠牌設計理念雖有差別,但大同小異。
系統段 位 址 機 能 用 途
┌──────────────┐
0000 0000H │中斷呼叫表 │
├──────────────┤
0000 0500H │DOS 及系統操作區 │
├──────────────┤
0000 4C00H │周邊控制程式 │
├──────────────┤
0000 5400H │COMMAND 運用 │
├──────────────┤
0000 0E0A0H │使用者操作空間 │
├──────────────┤
000A 0000H │螢幕緩衝區(VGA) │
├──────────────┤
000B 0000H │螢幕緩衝區(CGA,EGA) │
├──────────────┤
000C 0000H │EMS 記憶體擴充區 │
├──────────────┤
000F 0000H │ROM BIOS 程式區 │
└──────────────┘
上述之系統段,調入段暫存器中時,原來的 000AH應該是0A00H,餘同。
又使用者操作空間的起始點,因各家設計而異。
第二節 應用的製作

一、聯接目標檔(OBJ Files)

程式完成以後,相互間的聯結,越大越難,有時是結構的改變,有時是檔名的變動。不論什麼因素,在在都需要加以維護,而且對工作效率關係重大。

因此,需先建立一個 do.bat 的執行檔,其內容為:
1:LINK/M @XXXX
此 XXXX 為一記錄檔,其中記載所有需聯結的目標檔名,如果有任何異動,直接更改此檔即可。
在聯結時,執行 DO 即可。

例如:要製作 draw.exe 檔,已有 dr1.obj .. drn.obj 以及dr-seg.obj 等檔,則此 xxxx 檔內容應為:
1: DR-SEG+
2: dr1+dr2+.. drn
3: DRAW,DRAW,,
在執行 do 後,如果沒有錯誤,所產生的 draw.exe 以及draw.sym 即可供測試。

也可在第三條後加入:
4: symdeb (或僅用 s)draw.exe draw.sym
似此,在發展初期,立即可以測試。如果不需再次聯結,將第四條另外作一 .bat 檔,隨時執行亦可。

另外有一點需要注意的是,為了要讓可執行的程式由指定的起點開始,應在原始檔 .asm 的結束格式標記‘end ’後,加一起點的標題名稱。
此標記不論有多少,聯接目標檔時,會以第一個找到的標題為唯一的入口。如果程式很多,參與工作的程式師也多,在分開測試時,經常使用自己的入口,一旦聯接在一起,就可能發生錯誤。
解決的方法是由負責聯接的程式師,事先準備好前述XXXX記錄檔,將帶有入口標記的程式放在最前面即可。當然,將其他程式標記後的標題刪除亦可,但不如前法效率高。

二、目標檔偵錯及

在使用 debug時,因為名稱簡短,用法也簡單。若使用「標題偵錯」symdeb.exe則不然,因為首先要生成.map檔,測試時,輸入文字也較多。對不斷使用的人來說,難免嫌煩。簡化的方法,是先製作執行檔:s.bat 及 sm.bat

s.bat 是專供指定程式偵錯用,內容為:
1:symdeb %1.sym %1.exe
這是指已經有了原檔的 .map 時,如果程式剛聯接完畢,且在聯接檔中,沒有生成 .map 的語句,則應制作 sm.bat 如下:
1:mapsym %1.map
使用「標題偵錯」有很多優點,其功能較 DEBUG強得多。但是使用者必須注意,需要測試的標題,應該在程式中宣告PUBLIC,否則無法直接跳到該處。
尚有些特殊效果的程式,也應備妥測試的,甚至以利用「監視器」的方式,用另外一臺電腦來控制。有一些通訊軟體,如 xtalk加上symdeb.exe,透過序列埠(serial port )可以聯機除錯,其手續如次:

1,先在待調程式的主機上輸入通訊指令:
C:>MODE COM1:9600,N,8,1
c:>symdeb myfile.exe

2,再於已與主機經序列埠聯接的副機上輸入:
C:>XTALK
進入指令輸入狀態,再輸入:
C:>SP
表示設定傳輸速度,一般多采用9600,但視主機而定。
C:>9600
最後,輸入操作指令:
C:>GO LO
表示用區域網路,即序列埠口。
這時等待聯機成功的訊號,見到螢幕上出現“-”,即可 開始偵錯。

3,在主機方面,尚要輸入:
C:>=COM1 ( 或COM2,端視雙方的通訊口而定 )

4,至於偵錯方式,與利用一臺電腦時相同,只是此時在主機上出現的是執行後的結果,而由副機控制偵錯步驟。
第三節 分類定義

一、段名(Segment Name)定義

對段名多於一個的程式,最好先有一個定義段的程式,以DRAW為例,假定程式為兩段,一為控制程式段,一為繪圖程式段。資料分為三段,一為應用資料,一為參考資料,以及製作資料。此外還有一資料索引段,合計有六個段。
茲建議,在通用的基礎上,簡化段名,將段分類如下:
程式段 (CODE SEGMENT) 定名為 CG
資料段 (DATA SEGMENT) 定名為 DG
索引段 (INDEX SEGMENT)定名為 IG
記憶段 (MEMORY SEGMENT) 名為 MG
特設段 (EXTRA SEGMENT)定名為 EG
堆疊段 (STACK SEGMENT)定名為 SG

當各段超過一組時,則再加數字以區分之。
如在 dr-seg.asm 中,可設為:
1: TITLE SEGMENT DEFINITION OF PROGRAM ‘DRAW’
2: CG1 SEGMENT PUBLIC
3: CG1 ENDS
4: CG2 SEGMENT PUBLIC
5: CG2 ENDS
6: DG1 SEGMENT PUBLIC
7: DG1 ENDS
8: DG2 SEGMENT PUBLIC
9: DG2 ENDS
10: DG3 SEGMENT PUBLIC
11: DG3 ENDS
12: IG SEGMENT PUBLIC
13: IG ENDS
14: END
在 SEGMENT PUBLIC 之後,有多種表示方式:
SEGMENT PUBLIC XXXX
XXXX=CODE 表示為程式段,在聯接時,屬程式的段與段前後銜接。兩段程式之間,以 000H 填充至「節」( 每十六個字元為一「節」 )之首位。
XXXX=DATA 表示資料段,在聯接時同上。
如果程式師為了某種原因,必須嚴格控制程式之位置及長度時,不宜使用上述兩種方式。
最簡單之陳述方式,即在 SEGMENT PUBLIC之後,保持空白。
XXXX=BYTE 表示程式聯接後,各程式之間緊密接合,不留空位。這種方法,有利於程式精簡。
XXXX=WORD 表示程式聯接後,各程式之間緊密接合,但在後面的程式必然由雙數位起。

又如在 XXXX 前後加以引號如:
SEGMENT PUBLIC 'XXXX'
此一宣告,用以通知彙編程式各段的順序及定義。因此在編寫程式時,只要使用的段名及定義與本檔相符,不管將各段安排在程式任一位置,都不致發生錯誤。
‘XXXX’與本段程式的排列順序有關,在聯接時,先將引號中的字串排序妥當,各程式即依此順序排列之。
也就是說,凡是使用了引號,則程式聯接的順序,即以在引號中字串,於聯接時出現先後為順序。
單一程式檔的錯誤不難測知,但若各段之間發生錯誤,對經驗不足的程式師,將有無從下手之虞,不得不慎!
一般說來,在聯接時,最令人頭痛的錯誤訊息為:
'fixup overflow at nnnn..'
不論其錯誤提示內容如何,此種錯誤的發生,多半是因為段與段之間的標題、緩衝器或是暫存器的使用發生了混淆,聯接程式得不到正確的資訊所致。
解決方法是在第一個錯誤訊息出現時,立刻以‘Ctrl_C’停止彙編,記下第一個訊息,再在原程式中,找到該位置,(多半為一標題位置)在此標題之前,一定會發現與「段」有關的錯誤。

二、原始檔(Source File)檔名定義

在共同設計大型模組時,程式師間的默契,全賴事先相互約定。否則程式越大,所面臨的困難將越多,經常耗時費事,甚至最後功敗垂成。
因此,在設計之初,必須妥善規劃,將一應有關的檔案、名稱、功能等,皆明確地加以定義。參與設計的程式師,更必須嚴格遵守,方能得心應手。
原始檔名的定義,其目的有三:
1,代表程式設計者:當製作的程式甚多時,一見檔名,就應能分辨出各個程式的設計人。再如某程式師所程式設計式在一 個以上,則應在其本人程式碼之後,加一數字編號。這樣,遇有任何問題,立即可以找到來處,進行追蹤。

2,代表程式功能:每當聯結後發生問題時,應能由檔名查知問題所在,故每種功能宜給予適當的名稱。

3,代表聯結的關係:除了功能外,有時尚須表示各程式之間的聯結關係。如某一程式必須安排在另一程式之前或後,亦應在檔名最後,以數字表示順序。
若參與的程式師不超過廿人,則以一字元為限,各人事先選定一個字母,作為檔案名稱的第一字元。第二字元則視該程式師是否編寫一個以上的程式而定,是則取一數字代表之,若無則免。
功能以三到六字元為宜,能統一長度將更為方便,餘下一字元留供數序用。
例:程式師代號為‘C’,本程式之編號為3,功能為‘DRAWS’,此外並無聯結關係,則其名應為:
cdraws3.asm

三、標題定義

程式在整理或測試時,最大的困擾,是尋找某一個標題的出處或功能。如果在同一檔中,尚可利用cref.exe,列印出一份對照表來;如果不知道出自哪一個檔案,在眾多的程式中,就只有望洋興嘆了。
至於功能,如名稱定得太長,不僅輸入、修改不便,而且會使得程式看來雜亂無章,令人眼花撩亂。如果定得太簡單,或各人任意定名,則難以理解。
所以,標題定名之重要性,不下於程式之寫作。尤其是在參與人數眾多時,標題不僅要統一,而且要能代表所有必備的訊息。
標題所代表的訊息有:
A x x x x x x x x
│└┬┘└┬┘└── 分支代號
│ │ └──── 延伸定義
│ └─────── 功能名稱
└───────── 程式出處

1,程式出處:如果很多人同時參加一個計劃,而某程式師僅寫了一個程式,此定義即為該程式師之程式碼。否則尚要附 加編號,以便隨時可查到。即使只有一個人寫作程式,程式可能不斷擴大,為了製作的方便,或為了模組的分割,常有必要將程式分到其他檔中。因此,一段程式究竟出自於哪一個檔案,全賴標題表明,以便能迅速地找到。
程式出處一般用一個字母即可,如前述第一個字母A代表此段程式來自編號為A的原始檔;或屬於以A為代號程式師的程式。

2,功能名稱:除若干已知會用到的功能可以事先定義外,其餘的多半是在發展過程中,針對需要而產生。對功能定義千萬不要掉以輕心,一個程式的再利用價值,常與其功能定義息息相關,明確的定義,可以令人對其功能及應用方式一目瞭然。
功能名稱長度以三個字母為宜,太長則輸入費時,尤其是國人不見得個個英文了得,與其寫錯,不如藏拙。
如:DSP 表示螢幕顯示功能
PRN 表示列印功能
KIN 表示鍵盤輸入功能

3,延伸定義:若功能相同的程式過多,2,中的定義方式難以分辨,則可再加一延伸定義。
如:DSPDOT表示顯示點陣
PRNCHI表示列印中文
KINASC表示輸入 ASCII 字元

4,分支代號:在程式中常有分支,分支代號最理想是由小而大,依序安排。但由於寫作時常難以預知後情,故宜先留空號,以便擴充。
如:DSPDOT10
PRNCHI06
KINASC24

四、緩衝器定義

B x x x x x x x
││└┬┘└┬┘
││ │ └─── 延伸定義
││ └────── 功能名稱
│└──────── 型別
└───────── 緩衝器保留字

第一個字母規定用‘B’,為緩衝器保留字。
第二個字母定義其型別:
Q=QWORD 如:BQxxxx 緩衝器長度為8字元。
D=DWORD 如:BDxxxx 長度為4字元。
W=WORD 如:BWxxxx 長度為2字元。
B=BYTE 如:BBxxxx 長度為1字元。
S=STRING 如:BSxxxx 不限長度,但限BYTE型。
O=ORIGINAL 如:BOxxxx 表不可破壞的原始資料。
F=FLAG 如:BFxxxx 用作旗號。
第二字母以後所採用功能或延伸定義,與三、2,3相同。此外,所有緩衝器皆應統一設在緩衝器專用程式中,若系臨時使用,或尚未正式聯接,亦應設在各程式之首,以便於查閱、修改。

第四節 統一格式

一、格式統一之優點

不論採用何種程式語言,凡是共同工作、須相互研討,或者在同一部門中,分擔成敗責任者,應該有統一的程式寫作方式。這樣不僅便於溝通,提高程式的品質,且可避免因某一程式師之離職或出缺所造成的後遺症。
良好的程式,應如一篇美妙的文學作品,其中有技巧,有意境。其功能不僅表現在執行的效率上,也可以作為同行間學習、欣賞的媒介,這些都必須透過統一的格式達成。
尤其是把組合程式寫作當作一種藝術創作,就必須經常相互觀摩,如果沒有共通的格式規定,則這樣的程式和「天書」可以說沒有多大的分別。不論註釋寫得再好,一般程式師自己都怕看自己的程式,更何況參閱他人的? 但若有統一的格式,養成習慣後,不僅易看易懂,而且尋找、修改方便,寫作也輕鬆省事。

二、規格

1,統一用大寫字母,以利於閱讀。
2,空格處應採用 TAB,齊頭等距,一目瞭然。
3,標題之後立即改行輸入,以便閱讀。
4,每行不得超過70個字母,以免長度不等,畫面紊亂。
5,註記位於指令後,在第五個 TAB的起點,空間不足時,可換行,但位置齊一。
6,所有緩衝器統一定義在程式之前,不可夾雜在程式中間。
7,除了需要作超過程式段的FAR CALL使用‘PROC FAR’外,切勿使用‘PROC NEAR ’形式,因這種寫法毫無意義。
8,程式開始時,應有註記說明使用要點。

以下舉例說明統一格式:
1: PAGE 60,132
2: TITLE [程式名],[程式師代號],[編號],[功能說明]
3:
4: DG SEGMENT PUBLIC
5: extrn bwsca1:word,..
6: DG ENDS
7:
8: ..
9: ..
10: ..
11:
12: CG SEGMENT PUBLIC
13: extrn mmmm:near,..
14: ..
15: public xxxx,yyyy,..
16: ASSUME CS:CG,DS:DG,ES:DG
17: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
18: ; REM ………… ;
19: ; …………………. ;
20: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21: START:
22: CLD
23: MOV AX,DG
24: MOV DS,AX
25: ..
26: ..
27: ..
28:
29:CG ENDS
30: END START

第一條的 PAGE 是供列印用的「保留指令」,表示每頁所印的行數及列數。其後之 60,132 即為60行, 132個字元。
第二條的 TITLE 為供查閱之「保留指令」。
前述REM 註記欄中,應說明該段程式的功能、應用條件,以便其他程式師共同使用。

三、註記

註記的目的在於日後自己或他人能很容易地做程式修改、維護。因此所做描述當力求詳盡。不幸的是,不論多麼精彩的文句,總有不足之處。因此,註記時需把握重點,大體說來,程式需加以說明的,不外:

1,功能:做什麼工作,需要何種先決條件。
2,引數:設些什麼資料,以供本段程式處理。
3,緩衝器:用什麼緩衝器,其工作特性如何。
4,結果:執行後所產生的結果。
5,子程式呼叫:在本段程式中,所需呼叫的子程式名。
6,重要宣告:如暫存器是否被破壞,或其他注意的事項。
如:
【功能】:讀鍵盤緩衝區輸入碼,依性質返回ASCII+掃
瞄碼或中文內碼
【引數】: AH = 00H
【返回】: 11AL< 80H,AH = 掃瞄碼,AL = ASCII
12AL≧80H,AL = 四字元中文內碼之一
【破壞暫存器】:AX
【備註】:以本功能取中文內碼,須連取4次,依序得
到一中文4字元內碼。
當然,如果時間充足,最好能有程式的全部說明,以及製作、修改的細部記錄。
問題在於任何程式發展之初,一定乏善可陳。再加上不斷的修改、增補,其間變化複雜無比。程式師又要全神貫注,解決問題,又要考慮如何註記,一心兩用,必然談不到效率。
那麼怎樣做才對呢?我建議先讓程式師自由發揮,在初期有無註記無關緊要。待程式完成了,大致上沒有錯誤,這時,就應該停下來,從頭到尾,將程式徹底整理一番。
這樣不僅加註記容易,而且程式師有機會重新「欣賞」自己的心血結晶,該改的改,該合併的合併,該刪的刪。最後,程式師經過反思,研究,技術將會大幅度進步,程式更精簡,註記也正確無誤,工作也達到最高效率。[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928811/,如需轉載,請註明出處,否則將追究法律責任。

相關文章