Post view

組合語言新手上路篇:打造文字窗(1)

組合語言新手上路篇   打造文字窗(1)

作者:蘇言霖

藍寶堅尼、保時捷、法拉利這些名詞,會給您什麼樣的感覺,讓您想到什麼呢?電腦語言的保時捷─組合語言,不僅是部「超級跑車」而且是一部「超級工程車」,速度快且強而有力。
想要駕御「組合語言」這部旗艦級跑車,並不是很難的事。您只要有一般駕照,也就是具備簡單的BASIC或C語言基礎,就可以輕鬆考取職業賽車的駕照。
在新單元裡,我們將透過幾個輕快的話題,來聊聊組合語言這片天地。現在就讓我們從「如何打造一個文字窗」來認識組合語言吧。註1。


發動組合語言引擎


您會用BASIC秀出如圖一的文字窗嗎?那還不簡單,用程式一不就行了。啊!那也太簡單了吧?不不,正好相反!同樣的程式,用組合語言來寫,那可就不簡單了!沒關係,第一次發動車子引擎總是容易緊張。
首先,我們得寫出組合語言程式的基本架構。這個程式不必做什麼事,只要能正常啟動並回到DOS即可。程式二就是一個最簡單的迷你組合語言程式。


圖二是程式的編譯與執行結果。MASM會用ASSEMBLY.ASM翻譯成 ASSEMBLY.OBJ目的檔(ObjectFile),而LINK再用 .OBJ檔當材料做出我們要的ASSEMBLY.EXE執行檔。
這個程式有多大呢?請看圖三。哇 .EXE檔才522bytes,0.5K夠小吧! .OBJ檔是中繼處理的產物,對一般程式來說可刪掉這些 .OBJ檔,以節省磁碟空間。


ASSEMBLY.ASM的每個指令都是必要的基本指令。幾乎每個機制都是缺一不可的,底下就讓我們來看看各指令的作用。
程式的輔助說明
在程式加說明文字的方法很簡單,只要在註解前填上一個分號,就會使;到該行最後,統統會變成輔助說明。譬如:


;  File Name : assembly.asm     ← 說明本程式的檔名

程式的宣告區

任何組合語言程式前面,都會寫一些宣告指令,告訴MASM我們的程式要使用那些資源。
您要有這樣的想法:程式是寫給MASM譯者看的,自然要用MASM 看得懂的語意,才會把正確的意思轉譯成CPU看得懂的語意,程式也才能正常執行。因此,宣告指令是告訴MASM我們的程式打算架構成什麼樣子。於是我們宣告了:


        .286            ; 宣告與 286 以上的電腦相容
        .model  small   ; 宣告記憶體模式
        .stack          ; 宣告程式的堆疊區
        .data           ; 宣告程式的資料區
        .code           ; 宣告程式的程式區
main:                   ; 定義主程式標記
└─────────────────────┘ └─────────────┘
     組合語言指令            程式的輔助說明

.286指令的意思是告訴MASM,程式希望用286以上CPU提供的 pusha、popa等專屬指令。如果程式希望僅供386以上使用,則可以宣告為.386,某些Game就是如此。現在誰會為8088XT電腦寫程式呢?因此宣告.286應是必然的。


MASM設計了Tiny、Small、Medium、Compact、Large、Huge等六種記憶體操作模式。其中Tiny模式是.COM檔專用,其餘則是供.EXE 檔選用。Small是.EXE最小的模式,程式碼和資料碼可各達64K,執行檔最大可有128K,最適合初學者使用,所以才宣告為.modelsmall。


.STACK、.DATA、.CODE各是用來宣告程式的堆疊、資料與程式區。在Small模式,每個區域最多可有64K,因此程式執行時動態範圍可達192K。
在程式區開始之前,我們宣告了一個main標記,這個記號主要是搭配END指令使用的。告訴作業系統(MS-DOS或Windows)從這個標記開始執行程式。標記可以取任何的名稱,不一定要用main,比如 begin、start...均可。
對了,順便提醒您,組合語言的指令是不區分大小寫的。只要您喜歡怎麼寫都行,MASM可看懂即可。
主程式內文:


        指令    命令參數
       ┌─┐   ┌──┐
        mov     ax,@data  ; 把 DS 指向資料區
        mov     ds,ax

        mov     ax,4c00H  ; 打電話給 DOS 說要回家了
        int     21H
        end     main


任何程式執行前後都必須做以上兩件事,一是把CPU的資料區指標DS指向記憶體預定的資料區。另一件事是用INT指令打電話給 DOS,告訴DOS我們程式要結束回到DOS的C:\>提示號底下。


DOS在執行程式時會自動設好CS(程式區指標)和SS(堆疊區指標),但不會把DS指向資料區。註2。資料區實際的記憶體位址會放在@data預設變數裡。因此程式一開始就要把DS指到這塊資料區,以便能正確存取.DATA區定義的變數。請看圖四。


AX、DS、CS、SS是什麼呢?其實您只要當做「整數變數」,這些變數是2bytes,共16bits,範圍是0-65535即可。只不過CPU對不同的暫存器變數有不同的看法和用法。


其實把DS指到資料區,就是ds=@data。然而我們卻不能直接寫movds,@data。必須靠兩個mov指令才行!為什麼?這與CPU指令的「定址法」有關。


什麼是定址法?簡單的說,就是CPU指令參數「排列組合」的規則罷了。由於CPU不提供ds,@data這樣的組合方式,就只好透過兩個mov指令來完成了。有機會我們會詳加討論定址法,本期的重點不在這裡。


DOS的電話號碼是21H。數字後面加上H表示用16進制數值。「結束程式部門」的分機代號是轉4cH。因此我們只要打21H轉4cH 即可使程式結束並回到DOS。


這個工作的專業說詞是:把程式的傳回值0設給AL,並將AH設為代碼4cH,然後呼叫INT21H中斷服務程式,告訴DOS停掉程式。


要特別說明的是,最後的END指令並不能停掉程式回到DOS!那只不過是用來通知MASM程式到這裡寫完了,如此而已。如果沒有int21H 指令,程式會一直跑下去直到當機。


打造一個文字視窗


在組合語言列印字串,有幾種不同的方式,這裡介紹最簡單的一種,那就是透過DOS。我們只要把視窗用「列印字串」的方式印到螢幕上即可。「技術手冊(三)系統呼叫篇」一書告訴我們:


DOS 服務代號 09H

功能:列印字串。DOS 會把 DS:DX 所指的字串以一般「黑底白字」的方式印到螢幕(STDOUT)。字串必須以 $ 做為結束記號。

用法:
            .data
      字串  db    'Hi, Assembly.$'

            .code
      lea   dx,字串變數名稱
      mov   ah,09H   ; ah = 9
      int   21H      ; 叫用 DOS 的第 9 號功能

我們只要在.data資料區宣告字串變數,然後把dx指向這個字串,就可以打電話給DOS,叫DOS把dx所指的字串印出來。
db是DataByte的意思,表示該行後面的每個資料都以byte為單位。字串可用''或""括住。如果希望印出字串後換一列,則可以寫:


      字串  db    'Hi, Assembly.'10,13,'$'

ASCII碼的10和13字元,可以把游標移到下一列的最左邊。要多換幾列,只要多寫幾次10,13。
關於指標與指向
初學者最容易困惑的地方,就是指標與指向。假設底下有這樣一段程式:


            .data
      pc    dw     160

            .code
      mov   dx,pc        ; dx = 160
      lea   dx,pc        ; dx = 1000

又假設整數變數pc的記憶體位址在1000(詳細的說法是DS:1000),而變數內容為160。那麼movdx,pc就是把pc變數的內容搬給dx 暫存器。因此dx的值是160。請看圖五。
leadx,pc指令要的不是變數的值,而是變數所在的門牌號碼。因此會找出pc的位址,並把位址的號碼設給dx。因此dx的值是1000。


您要的WINDOW.ASM


好了,現在就可以開始寫我們所要的視窗了。啊,這麼快,心情還沒準備好耶!其實寫程式這玩意,「有點難又不會太難」,寫的方法很簡單,請先把標準的ASSEMBLY.ASM影印一份:


1.請輸入copy assembly.asm window.asm <Enter>
2.然後在WINDOW.ASM加一些資料和程式即可。請看程式三。比較粗的文字就是加上去的部份。
3.最後就可以用:


    C:\RUNPC>masm window; <Enter>     ← 編譯程式
    C:\RUNPC>link window; <Enter>     ← 連結程式


4.如果沒有任何錯誤,程式卻無法正常執行,請檢查是否輸入無誤。初學者最容易把09H打成08H或00H,也可能把ah,09H打成 bh,09H,還找不到問題點!不會吧?事實確是如此(根據我們多年的授課經驗)。
5.興奮的時刻終於來了,跑跑看WINDOW程式吧!


    C:\RUNPC>window <Enter>

只要會出現圖一的畫面並回到DOS底下,恭喜您成功啦!編譯的結果如圖六所示。如果希望用CodeView一步一步檢視程式的執行過程只要輸入:


    C:\RUNPC>masm /zi /zd window; <Enter>
    C:\RUNPC>link /co window; <Enter>
    C:\RUNPC>cv window; <Enter>

然後按F8鍵單步執行鍵和F4鍵切換DOS輸出畫面,或許對瞭解程式細節會有幫助。
WINDOW.ASM不必再解釋了吧。值得一提的是,我們利用「長字串」的技巧,把整個視窗寫在一個WINDOW字串變數裡頭。只要告訴DOS 印出整個WINODW字串即可。


好用的編譯工具


主菜之後,上一道特別的餐後點心請您嘗嘗。寫組合語言程式,常要重複修改和編譯程式,每次得下一堆的pe2、masm、link命令實在累人。因此大家都會介紹一個簡單的ASM.BAT(DOS批次檔)以簡化操作手續,我們也不例外。
只是筆者更懶,我們希望寫程式只要輸入:


C:\RUNPC>pe window <Enter>    ← 編寫程式
C:\RUNPC>asm <Enter>          ← 直接編譯與連結程式
C:\RUNPC>window <Enter>       ← 執行程式

執行後發現程式有誤或當機,重新開機後仍只要輸入:


C:\RUNPC>pe <Enter>           ← 編修 WINDOW.ASM 程式
C:\RUNPC>asm <Enter>          ← 組譯 WINDOW.ASM

如何?夠輕鬆簡單吧!這是怎麼做到的呢?其實這是PE.BAT和 ASM.BAT擁有記憶功能的緣故,就像電話的Redial重撥鍵。
我們只要輸入一次參數,PE和ASM就會永遠記住指定的檔名,所以下次只要直接輸入pe或asm重撥即可,記憶會保存到使用者再輸入另一個檔名為止,而且不受關機的影響。比如:


C:\RUNPC>asm assembly <Enter>

這套批次檔分別由PE.BAT、ASM.BAT、SETUP.BAT組成。製作的方法很簡單,請在C:\MASM\BIN目錄建好這3個批次檔。然後在AUTOEXEC.BAT 加上底下的指令:


    path c:\dos;... c:\masm\bin
    call c:\masm\bin\setup

再重新開機即可。如果您的MASM目錄不是放在C:\MASM\BIN,或希望在A:磁碟執行,請修改SETUP.BAT指定的目錄,比如A:\BIN。


我們知道就算是電腦玩家,要看懂ASM.BAT也不是件輕鬆的事。其實您不必瞭解這些批次檔!只要會用就行了。如果您對「DOS批次程式設計」有興趣,請參考「MS-DOS新時代利器」或「MS-DOS參考手冊」關於批次指令的說明。


認識組合語言


組合語言基本上是由「CPU指令」和「假指令」所「組合」而成的「語言」。
CPU指令就是直接控制CPU運作方式的命令,每個指令以1-6個 byte的數字所組成,我們稱為指令碼。比如mov、int、lea等都是CPU 指令。


假指令是MASM編譯器自定的指令,用來幫程式員輕鬆的架構出所需的組合語言程式。比如.286、.model、.code、proc、macro等都是 MASM的假指令。


簡單的說,CPU指令是.EXE會實際執行的指令,而假指令則僅供 MASM編譯時期使用。
CPU指令視386、486、Pentium種類而有所不同,假指令則視MASM 軟體版本而所有不同。基本上,新版本與舊版本仍維持向下相容。


更輕鬆的學習方式


CPU指令加MASM假指令共5-6百個命令,而且每代CPU每一版MASM仍不斷追加新指令,要是像學習英文單字般統統硬背下來,難怪消化不良。

舉個實例來說,一般主程式起始點都會寫:


    start:    mov   ax,@data
              mov   ds,ax

大家就把這兩個指令硬記下來,也從不思考為何如此,直到有一天看到了:


    start:    mov   dx,@data
              mov   ds,dx

就有許多學員興奮的舉手說:寫錯了!應該是ax才對!天哪?完全沒概念到了不可思議的程度!別說是dx,就算用cx、si、di幫忙搬資料絕無問題。相信您一定沒試過,做做看吧!
其實寫程式,只是把我們所想的「觀念」用程式寫出來罷了,只要有基本的程式設計觀念,用什麼「語言」表現並不是問題。


比方說,用任何一個高階語言都可以寫出ax=bx+cx;這樣的式子,然而用組合語言來做,卻得寫成下列的二個指令:


    mov ax,bx
    add ax,cx

初學者常會硬記movax,bx是把bx搬到ax,而不是將ax搬到 bx。add是把cx累加到ax,而不是將ax加上cx,結果放在dx。
與其硬背下來,何不加點調味料,改成自己喜歡的口味呢?比如:


    mov ax,bx   ; ax=bx
    add ax,cx   ; ax=ax+cx

哦,原來這兩個指令是ax=bx和ax=ax+cx。剛開始學組合語言時,最好在每個指令後面加一點註解,不見得要用文字說明,您可以加上 BASIC、C、dBASE或任何您會高階語言。


分號左邊的指令看不懂?沒關係,只要看看右邊的指令,這不就輕鬆多了。學組合語言太沉重?要是依傳統學習方式死K,不沉重也難。


另外要提醒您的是,限於螢幕的長度,常無法使思緒連貫。特別是寫一堆組合語言程式,才做完一件簡單的事。因此建議您每寫完幾段程式,就把程式印出來,把程式前前後後再思考一次。這對學習組合語言有很大的幫助。
關於編譯器


常用的組合語言編輯器有Microsoft公司出品的Macro Assembler 簡稱MASM。以及Borland公司出品的Turbo Assembler,簡稱TASM兩種。


MASM牌子老歷史悠久使用者最多,TASM與MASM相容,但比較年輕且功能強大,特別是全新的Ideal Mode,扭轉了MASM不合理的語法並提供更快的編譯速度。可惜這幾年來TASM一直推展得十分吃力。於是組合語言幾乎是MASM的天下。



1.我們的目的是希望能以一個全新觀點,輕鬆而快速帶您入門,並非巨細彌遺的訴說整個組合語言。建議您交互參考施先生的「組合語言實務」與「組合語言程式設計實例」,相信會事半功倍。
2.程式執行前DOS會把DS和ES指向程式自己的PSP程式表頭。

══════════════════════════════════════════

本文圖片與程式列表:


圖一:請參考列印圖片的「圖一」

pic01.png

圖二:
───────────────────────────────────────
C:\MASM\RUNPC>masm assembly; <Enter>         ← 編譯 ASSEMBLY.ASM 程式
Microsoft (R) Macro Assembler Version 5.10
Copyright (C) Microsoft Corp 1981, 1988.  All rights reserved.


  49680 + 271053 Bytes symbol space free

      0 Warning Errors        ← 沒有任何警告
      0 Severe  Errors        ← 也沒有錯誤,可做好 ASSEMBLY.OBJ 檔

C:\MASM\RUNPC>link assembly; <Enter>         ← 連結 ASSEMBLY.OBJ 程式

Microsoft (R) Overlay Linker  Version 3.64
Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.


C:\MASM\RUNPC>assembly <Enter>           ← 執行看看

C:\MASM\RUNPC>_                          ← 果然可以回到 DOS 而不當機!
───────────────────────────────────────

圖三:
───────────────────────────────────────
C:\MASM\RUNPC>dir assembly <Enter>

 Volume in drive C is BRENT
 Volume Serial Number is 2D10-1BE3
 Directory of C:\MASM\RUNPC

assembly asm           213 02-22-95  10:21p
assembly exe           522 03-06-95  12:29a
assembly obj           139 03-06-95  12:29a
        3 file(s)            874 bytes
                     249,552,896 bytes free
───────────────────────────────────────

圖四:請參考列印圖片

pic04.png

圖五:請參考列印圖片

pic05.png

圖六:
───────────────────────────────────────
C:\MASM\RUNPC>dir window <Enter>

 Volume in drive C is BRENT
 Volume Serial Number is 2D10-1BE3
 Directory of C:\MASM\RUNPC

assembly asm           213 02-22-95  10:21p
assembly exe           522 03-06-95  12:29a
assembly obj           139 03-06-95  12:29a
        3 file(s)            874 bytes
                     249,552,896 bytes free
───────────────────────────────────────

程式一:
───────────────────────────────────────
CLS
PRINT "奼迋迋迋迋迋迋迋迋迋迋迋芼"
PRINT "*                        *"
PRINT "*                        *"
PRINT "*                        *"
PRINT "*                        *"
PRINT "*                        *"
PRINT "*                        *"
PRINT "迋迋迋迋迋迋迋迋迋迋迋芞"
───────────────────────────────────────

程式二:
───────────────────────────────────────
;  File Name : assembly.asm

        .286                    ; 宣告與 286 以上的電腦相容
        .model  small           ; 宣告記憶體模式
        .stack
        .data                   ; 宣告資料區
        .code
main:
        mov     ax,@data        ; ax = @data
        mov     ds,ax           ; dx = ax
        mov     ax,4c00H        ; ax = 04c00H
        int     21H             ; 打電話給 DOS 說要回家了
        end     main
───────────────────────────────────────


程式三:
───────────────────────────────────────
;  File Name : window.asm

        .286
        .model  small
        .stack
        .data
window  db      "奼迋迋迋迋迋迋迋迋迋迋迋芼",10,13
        db      "*                        *",10,13
        db      "*                        *",10,13
        db      "*                        *",10,13
        db      "*                        *",10,13
        db      "*                        *",10,13
        db      "*                        *",10,13
        db      "迋迋迋迋迋迋迋迋迋迋迋芞",10,13,"$"

        .code
main:
        mov     dx,@data
        mov     ds,dx

        lea     dx,window
        mov     ah,09H
        int     21H

        mov     ax,4c00H
        int     21H
        end     main
───────────────────────────────────────


程式四:PE.BAT
───────────────────────────────────────
@echo off
if "%MASMDIR%" == "" call setup
if not "%1" == "" goto save
if "%argv1%" == "" call %masmdir%\savearg
pe2 %argv1%.asm
goto exit

:save
set argv1=%1
set argv2=%2
echo set argv1=%1>%masmdir%\savearg.bat
echo set argv2=%2>>%masmdir%\savearg.bat
pe2 %argv1%.asm
goto exit

:exit
───────────────────────────────────────

程式五:PE.BAT
───────────────────────────────────────
@echo off
if "%MASMDIR%" == "" call setup
if "%1" == "/?" goto help
if not "%1" == "" goto save
if "%argv1%" == "" call %masmdir%\savearg
if not exist %argv1%.asm goto help
if "%argv2%" == "" goto masm
goto asm

:save
if not exist %1.asm goto error
set argv1=%1
set argv2=%2
echo set argv1=%1>%masmdir%\savearg.bat
echo set argv2=%2>>%masmdir%\savearg.bat
if "%argv2%" == "" goto masm
goto asm

:masm
masm /zi /zd %argv1%,object\%argv1%;
if errorlevel 1 goto exit
goto link

:asm
masm /zi /zd %argv1%,object\%argv1%,%argv2%.txt;
if errorlevel 1 goto exit
echo.
echo Listing file = %argv2%.TXT
echo.
goto link

:link
link /co object\%argv1%;
goto exit

:error
echo %1.asm file not found!
goto exit

:help
echo ASM  [ ASSEMBLY(.ASM) ]   [ LISTFILE(.TXT) ]
echo.
echo for example : asm demo1 Enter
echo          or : asm demo1 demo1 Enter
echo          or : asm Enter
echo.
echo ==:   masm /zi /zd demo1,object\demo1,demo1.txt; Enter
echo ==:   link /co object\demo1; Enter
goto exit

:exit
───────────────────────────────────────

程式六:SETUP.BAT
───────────────────────────────────────
@echo off
break off
set masmdir=c:\masm\bin
set include=c:\masm\include
set lib=c:\masm\lib
call %masmdir%\SAVEARG
───────────────────────────────────────

蘇言霖 2013/09/10 0 8138
Comments
Order by: 
Per page:
 
  • There are no comments yet
Rate
0 votes
Post info
蘇言霖
「超級懶貓級」社群網站站長
2013/09/10 (3853 days ago)
Actions