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

匯編語言套裝軟件制作(1)

編輯:匯編語言

程式寫完後,還要加工成為可執行的套裝軟件(Package),一般說來,即使是可以執行的程式,一點錯誤都沒有,離套裝軟件的程度,卻還有一段距離。
當然,程式偵錯也是必經過程之一,有時偵錯與程式寫作可以同時進行。但有經驗的程式師,對全面有了充份的認識,往往會等到程式聯接後再行偵錯。
程式完成後的全面偵錯,最好不要依靠寫程式的人。因為程式師經常不是使用者,他們僅在自己設計的條件下,依其理念進行偵錯。當然這種錯誤必須更正,但最容易發生的錯誤,卻是使用者不小心在輸入時,或運用指令時,違背了程式師的理念。這種錯誤的發生,是不能原諒的,程式本來就是為使用者設計的,如果令使用者不便,程式就失去了應有的價值。
程式的品管,就在於檢測程式是否符合使用者的需求。一般說來,應有專人負責,也有讓寫作手冊的人,兼做品管的工作。這樣可以同時對照手冊所描述的功能及操作方法,檢查兩者之間是否一致。
還有一種常見的品管方式,是在產品完成時,交給完全沒有參與程式設計的第三者,在客觀的立場,作全面的試用,並提出測試報告或建議書。
品管合格了,才是包裝、手冊等最後的工作。這並不是說包裝和手冊要最後才做,相反的,尤其是使用手冊,經常要在程式設計的同時,准備妥當,如此程式師才不會任意所之,脫離主題。
第一節 測試偵錯

不論使用什麼工具,偵錯時一定要有清晰的頭腦,根據所設定的方式,一步一步地查看流程及指令。
每個人都會有獨特的習慣性錯誤,最好每次將自己發生的錯誤記下來,不僅錯誤會漸漸減少,且在錯誤發生時,很容易就能找到。
測試偵錯是非常重要的手段,有人認為第一流的程式師不應該犯錯,即使犯錯,也錯得很少。我的看法不一樣,並非因為我經常犯錯,而且錯得離譜。真正的理由是為了適時掌握正確的思考方向,在編程時,有意無意地忽略一些細節,這樣反而能一氣呵成,不致於再而竭三而衰。
這就像畫圖一樣,有人喜歡先作草圖,有人則習慣由細部畫起。不論個人的風格如何,重要的是最後的成果。
我在寫程式之前,首先考慮全體的結構,再把各處的支架備妥,然後考慮有共同特性之處,便一口氣寫完。而且寫時力求快速,以免失去當時的感覺。等到結構大體完成了,最後才去填補一些不太重要的細節。至於正確與否,則全靠測試偵錯來修正、彌補。
這種寫法要有很強的整體觀念,且對每段程式的性質及功能皆瞭解從何下手。
我由系統程式到應用工具,大大小小的程式寫了不少,對這種寫作方法有很深的體會。唯一的缺點是,程式如果太大,超過20KB,我的記憶力就難以負擔。(年輕人或許不致如此)但其優點則是結構精簡,制作時間極短。以我們的中文系統程式而言,8KB 的程式,連偵錯在內,只花了兩個月的功夫。
然而,在訓練程式師的過程中,我發現到還是循序漸進較好。每次寫完了一段程式,立刻偵錯,此段程式正確了,再寫下一段。除非有絕對的把握,自信沒有問題,否則千萬不要等全部程式都寫完了,再來調試。到那時,如果發現問題,在各段錯綜復雜的程式中,要找到錯誤所在,那是大海撈針了。
程式發生「當機」的情況,常常是 PUSH 及 POP不平衡所致,也有在做回路時,計數器為負值,以致鎖在其中。如果程式分「段」太多,則要特別注意各「段」改變的情況。
諸如這些細節,最好隨時把編程時的假想值記錄下來,不要太過相信自己的記憶力,時間一長,程式一大,就什麼都忘記了。
還有一點,在偵錯時千萬要養成習慣,記錄追蹤的過程。因為偵錯是一種很瑣碎的工作,很難一次就發現問題所在,第一次應該是第二次改進的經驗。如果不詳加記錄,每次都要從頭做起,將是一種極為痛苦的事。
不妨把偵錯視為猜謎,一種智力的挑戰,在遵守一定的規則下,應該是一種有趣的享受。
第二節 研究改進

想要把程式寫好,一定要不斷地研究、改進,由錯誤中學習,由改進中得到經驗,培養出敏銳的觀察能力和良好的寫作習慣。
在開始時,這種過程需要付出不少時間,但對一位程式師來說,寫程式是終身職業,能不精益求精嗎?
以下舉兩個實例,以說明如何研究改進已完成的程式。
1,指令的運用:
以下面這段通訊處理程式而論,不僅語法及指令完全正確,執行時也毫無錯誤,是不是還可以加以改進呢?
1-1 按照前面規定,說明項中已用簡化的字串:
SND-傳送 RCV-接收 LET-左 
RGT-右 VER-直 HOR-橫
1-2 程式師代號為'C'。
1-3 段名省略。

1: CSND0:
2: MOV DX,03FDH ; 輸出埠
3: MOV AL,80H
4: OUT DX,AL ; 輸出指令
5: MOV DX,03F8H ; LSB 速度控制
6: MOV AL,06H ; 速度=19200/秒
7: OUT DX,AL
8: MOV DX,03F9H ; MSB 速度控制
9: MOV AL,0 ; 速度=19200/秒
10: OUT DX,AL
11: MOV DX,03FBH ; 行控制暫存器
12: MOV AL,03H ; NO PARITY,1
; STOP,8
13: OUT DX,AL
14: MOV DX,03FCH ; 通訊控制
15: OUT DX,AL
16: MOV DX,03F9H ; 中斷有效
17: MOV AL,0
18: OUT DX,AL
19: CSND1:
20: MOV DX,03FDH ; 狀態暫存器
21: IN AL,DX
22: TEST AL,10H ; 是否可接收?
23: JNZ CRCV0 ; 可
24: TEST AL,20H ; 通道已清否?
25: JZ CSND1 ; 8250未清
26: MOV AH,1 ; 鍵盤有輸入?
27: INT 16H
28: CMP AL,07H ; ='CTRL+G'
29: JE CEND ; 是,完畢
30: MOV DX,03F8H
31: OUT DX,AL ; 送輸入字符


32: JMP CSND1
33: CRCV0: ; 接收
34: MOV DX,03FCH ; 通訊控制
35: MOV AL,08H ; 暫停中斷
36: OUT DX,AL
37: MOV DX,3F8H
38: IN AL,DX ; 收字符
39: MOV AH,0EH
40: INT 10H ; 螢屏顯示
41: MOV DX,03FCH
42: MOV AL,0BH
43: OUT DX,AL ; 繼續接受
44: JMP CSND1 ; 循環工作
45: CEND:
46: RET ; 完成

本段程式共 84 個字元,非常精簡,但仍然有節省的余地,要點在DX的數值上。
DX值由 03F8H到 03FDH,可知 DH 之值不變,只需改變 DL 即可。每改變DX一次,需要三個字元,如僅變DL,只需兩個字元。這一指令共用了十一次,除第一次有必要外,其他十次就可以省下10個字元。
再要斤斤計較,還可以搾出二個字元來,在5至8條中,若用INC DX 只需要一個字元。

此外,31,32及43 ,44是浪費的作法,只要在第18條加一標號,就可以省卻兩個字元輸出的指令。另外,還有35及39兩條指令,應該合並,一次即將AX設妥,於是,又省下了一個字元。
先令 DH=3
1: CSEND0:
2: MOV DL,0FDH ; 輸出埠
3: MOV AL,80H
4: OUT DX,AL ; 輸出指令
5: MOV DL,0F8H ; LSB 速度控制
6: MOV AL,06H ; 速度=19200/秒
7: OUT DX,AL
8: INC DX ; MSB 速度控制
9: SUB AL,AL ; 速度=19200/秒
10: OUT DX,AL
11: MOV DL,0FBH ; 行控制暫存器
12: MOV AL,DH ;NO PARITY,1
; STOP,8
13: OUT DX,AL
14: INC DX ; 通訊控制
15: OUT DX,AL
16: MOV DL,0F9H ; 中斷有效
17: SUB AL,AL
18: CSNDA:
19: OUT DX,AL
20: CSND1:
21: MOV DL,0FDH ; 狀態暫存器
22: IN AL,DX
23: TEST AL,10H ; 是否可接收?
24: JNZ CRCV0 ; 可
25: TEST AL,20H ; 通道已清否?
26: JZ CSND1 ; 8250未清
27: MOV AH,1 ; 鍵盤有輸入?
28: INT 16H
29: CMP AL,07H ; ='CTRL+G'
30: JE CEND ; 是,完畢
31: MOV DL,0F8H
32: JMP CSNDA ; 送輸入字符
33: CRCV0: ; 接收
34: MOV DL,0FCH ; 通訊控制
35: MOV AX,0E08H ; 暫停中斷
36: OUT DX,AL ; 及顯示
37: MOV DL,0F8H
38: IN AL,DX ; 收字符
39: INT 10H ; 螢屏顯示
40: MOV DL,0FCH
41: MOV AL,0BH
42: JMP CSNDA ; 循環工作
43: CEND:
44: RET ; 完成
看來似乎這樣太小氣,可是所謂藝術,就要具備絲毫不苟且的態度,再說由84個字元變成66個字元,省了近百分之廿,而且,速度也快了。這種程式原本就很精簡,只有訓練有素,追求完美的程式師,才做得到。
另一種做法,便是將重復的過程寫成回路,約可節省廿幾個字元。但是,由於時間定律限制,通訊程式頗重時效,回路是否值得,尚要多方面分析,不可輕率決定。

2,回路的實例:
前面曾經討論過,程式的效率,經常決定於回路的處理方式及其技巧。其對空間上影響比較小,但是良好的設計理念,常使速度上有高達十倍,甚至百倍的差異,讀者想必已經知道,但是如何能應用已知的技巧,來改進設計的程式呢?
回路是利用計數器,反復進行相同的程序作業,這種程式,目的就是為了節省空間,相對地,時間上難免有所損失。
因此,在設計回路時,必須先行考慮清楚: 空間的節省與時間的交換是否值得? 其次,則要充份掌握回路的特色,要用得恰到好處,不可掉以輕心。
原則上,在回路中,指令要用得精簡,流程要非常明確,尤其重要的是,應力求避免在回路中使用緩沖器,最好充份利用暫存器。如果時間效率極為重要,則不妨放棄回路方式。
有一個顯示程式,目的是要將 16*16點陣字形送到螢幕上。對象是Hercules 640*400的圖形卡,計分四區交互傳送,這是另一個「高科技」界的新鮮奇事,在IBM PC推出時,最高密度的圖形態,只有 640X200點陣,那是遷就電視螢幕的掃描方式,先送單線的水平訊號,再送雙線,故分兩區。Hercules卡為了加高密度,應用Interlace 技術,又在單雙水平掃描線中各加了一行,遂成了四區。
Hercules很適宜中文的顯示,如用 16X16字形,正好顯示25行,每行40字,與英文完全兼容。若希望有一狀態顯示欄,則可用 15X15字形,留出24條線供做狀態欄。
遺憾的是在最需要中文的國內,卻偏愛CGA,EGA 等密度不足的顯示設備。不但售價偏高,功能也不足,弄得不倫不類。
最理想的還是VGA 顯示,計有 640X480之螢幕點陣,不僅空間大,在記憶體中,只有一區,應用非常靈活。
下面,我們先介紹 Hercules 的顯示方法,同時探討回路的處理方式。
1: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2: ;HERCULES 中文顯示處理程式。 ;
3: ;輸入參數:SI=點陣字形,DI=螢幕位置。 ;
4: ; DS =CG,ES= 0B800H(螢幕段)。 ;


5: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6: CDSP0:
7: MOV CX,16 ;高16點
8: CDSP1:
9: MOVSW ;移至螢幕上
10: ADD DI,1FFEH ;加一區,每區=2000H
11: JNS CDSP2 ;未超越區限,繼續
12: ADD DI,8050H ;超越區限,換區加行
13: CDSP2:
14: LOOP CDSP1
15: RET
程式到此結束,相當精簡,技巧在第10至12條區限的檢測方式。一般做法是在檢查區限時,用:
ADD DI,1FFEH ; 加區值
CMP DI,8000H ; 最大區限值
JB CDSP2 ; 未超過
SUB DI,8000H ; 減去區限
如此則多了一條4字元的指令,加上4個時鐘脈沖,做16次回路就損失64個時鐘脈沖值。在全螢幕顯示時,以1,000 個字來算,為數就不少了。
當然,取消了回路速度還可以加快,其結果,則要增加130 個字元,時間則快了 272個時鐘脈沖,是否值得,就要看實際需要而定了。
另一個方法,要增加2個字元,但可快上36個時鐘脈沖,其法在第11條上:
11: JS CDSP3
12:CDSP2:
13: LOOP CDSP1
14: RET
15:CDSP3:
16: ADD DI,8050H
17: JMP CDSP2
再換一個方法,如果先使 BX 為1FFEH,DX為8050H,則在原程式中,將第10條及12條分別改為:
10: ADD DI,BX
12: ADD DI,DX
這一來,時鐘脈沖快了2個,16次則快更多,如果再加上取消回路,其意義更大。空間原增加 130字元,現僅94字元,時間則省下 304個時鐘脈沖。如果全螢幕顯示了1,000 個字,在8MHZ頻率下,將會加快 1/25 秒的速度。
在回路中,如果講求時間效益,應極力避免使用PUSH及POP ,因PUSH需15個時鐘脈沖,而POP 則要12個,兩者相加是27個時鐘脈沖,非常不值得。
解決方法之一是:設法將欲保留之值貯存在沒有用到的暫存器中;再若是固定的常數,也不妨在每次要用時重新置入,祇不過是4個時鐘而已。最麻煩是變數值,除了在設計模組之前,妥當地安排外,別無良策。

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