Ch 03 ASCII Code


原理

這一次我們要談的是 ASCII 碼,什麼是 ASCII 碼呢?我們知道電腦是一種電器設備,只認分辨高電位與低電位,我們就把它定義成 0 和 1 兩種數字,例如高電位為 1,低電位則為 0;或者反過來亦無不可。雖然電腦只能分辨兩種數字,但我們將八個 0 或 1 排列在一起就可以表示很大的數字,例如:
0000 0000    表示 0
0000 0001    表示 1
0000 0010    表示 2(註一)
0000 0011    表示 3
0000 0100    表示 4
0000 0101    表示 5
這就是用一個 byte(位元組)來表示 0 到 255,總共 256 個整數(28=256),當然 2 個位元組,就相當於 16 個位元,就可以表示出 65536 個整數。那麼電腦上的英文字母又如何在螢幕上顯現出呢?原來有些聰明的人,早已經想好了方法。我們把一個整數代替一個字母或阿拉伯數字。例如 65 這個數值代表英文字母「A」,當電腦「看到」這個數值時,就用 65 這個數值經過一套運算方式,而得到「A」這個字的形狀,再顯示到螢光幕上。當然,必須每一個人都使用同一個數值來表示同一個字,如果某人以「65」表示「A」,但另一人以「61」表示「A」,豈不天下大亂。

所以美國有個機構制定了一個統一的標準,將 7 個位元(共有 27 也就是 128 種表示方法,從 0 到 127)來代表英文字母、阿拉伯數字及一些符號,稱為 ASCII 碼。後來 IBM 又制定了擴充的 ASCII,這是由 8 位元組成的,共可以表示 256 個文字,前面 128 個與前述的 ASCII 碼相同,後 128 個可以表示音標、框線及希臘字母。底下列出幾個 ASCII 碼,以供說明,其餘可參考附錄四

常見 ASCII 碼
十進位 十六進位 ASCII 十進位 十六進位 ASCII
32 20   33 21 !
34 22 " 35 23 #
36 24 # 37 25 %
65 41 A 66 42 B
97 61 a 98 62 b

上表中的第一個,十進位的數值 32 ( 等於十六進位的 20 ),它的 ASCII 碼所代表的字元空白;第二個,十進位的 33 ( 等於十六進位的 21 ),它的 ASCII 碼代表的字元是!,其餘依此類推。意思是說,如果程式想把某個記憶體的內容顯示在螢幕上,假如這個記憶體存的是 41h,那就會印出英文字母『A』來,而不會印出 41 或 65。


原始程式列表

底下我們就介紹一個程式,將 0 到 255 的 ASCII 碼印在螢幕上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;***************************************
code    SEGMENT
        ASSUME  cs:code,ds:code
        ORG     100h
;---------------------------------------
start:  mov     cx,256   ;有 256 個 ASCII 碼
        mov     dl,0
next:   mov     ah,2     ;Loop 迴圈開始處 ─┐
        int     21h      ;                │
        inc     dl       ;                │
        loop    next     ;Loop 迴圈結束處 ─┘
        mov     ax,4c00h
        int     21h
;---------------------------------------
code    ENDS
;***************************************
        END     start

經過組譯、連結、轉換成 COM 檔後,執行看看:

ascii.com 執行後結果

你會聽“嗶”一聲,然後印出一大堆字,又回到提示符號。這是因為 ASCII 碼的 7 就是代表嗶聲,其他還有一些控制碼,我們稍候會用到,然後看到電腦印出許多符號、阿拉伯數字、英文字等等。底下解釋這個程式如何運作。

印出字元的 DOS 服務中斷:AH=2/INT 21H

這個中斷服務程式會在螢幕上印出一個字元,要印出的字元所代表的 ASCII 碼放入 DL 暫存器中,再使 AH 設為二,最後呼叫 INT 21H 就可以讓 DOS 服務中斷幫我們印出字元來。

DOS 提供了許多服務程式,這些服務程式只要事先設好該有的適當數值,及該服務需要的資料,再呼叫中斷服務程式就可以了,像這堛

        mov     ah,2
        int     21h

是印出字元服務程式,而第一章的

        mov     ah,9
        int     21h

是印出字串服務程式。它們都是屬於 DOS 提供的服務程式,而 AH 內的數值則表示要使用那一種服務,這些服務非常多且繁雜,可以參考 Ralf Brown's Interrupt List,這是網際網路上最權威的文獻。

新的指令:INC

INC 指令的語法是

INC     暫存器或變數

INC 會使後面接的暫存器或變數的數值增加一,這暫存器可以是各種通用暫存器 ( 如 AX、AH、AL、BX、BH……),也可以是 SI、DI、BP、SP。要注意的是,如果 AL 已等於 0FFH,再執行

        inc     al

AL 會變成零。

新的指令:LOOP

這程式的主體部分是一個 LOOP 迴圈。LOOP 的語法是:

LOOP    標記

當 CPU 執行到 LOOP 時,CPU 會使 CX 內的數值減 1,如果減一後,CX 內的數值不為零,CPU 會跳躍到 LOOP 所指定的標記處繼續執行;如果減一後,CX 的數值變為零,那麼 CPU 就會跳到 LOOP 下一行的指令執行。因此如果有一段程式必須執行好幾次,就可以先把次數放在 CX 堙A然後把那一段程式放在標記與 LOOP 之間,CPU 就可以照我們的設計執行一定次數。像這樣子我們設好某些條件,當符合這條件時,程式就在某一段落重複執行直到不符合這個條件,這種流程稱為迴圈。您要小心的是,不要使得永遠都符合條件,這樣程式便一直在迴圈中執行,永遠無法跳出這個迴圈,於是就當機了。在 LOOP 的例子,CX 的作用好比是計數器,因此 CX 也叫計數暫存器,英文為 counter register。

程式第 6 行,小木偶先把 256 存入 CX 中 ( 因為有 256 個 ASCII 字元 ),然後第七行把 DL 設為零,這是第一個要印出來的字元。接下來程式就進入迴圈了,這個迴圈將連續執行 256 次,每次只有印出來的字元不同,由零逐漸增加到 255,其餘部分皆相同,所以在印出字元之後使 DL 增加一,再檢查是否該離開迴圈,還是要回到迴圈的開頭。

更詳細的說,第一次 DL 為 0,就印出 ASCII 碼是 0 的字元,這是一個空字元 ( null ),然後程式執行到

next:   mov     ah,2     ;08 Loop 迴圈開始處 ─┐
        int     21h      ;09                   │

時,印出空字元,接下來 DL 會增加一,然後執行到 LOOP 時,CPU 檢查 CX 是否為零,此刻是第一次執行,CX 仍是 256,所以 CPU 使 CX 減少一,再跳到 next 標記處執行。這時 DL 已經是 1 了,於是就印出一個笑臉 ( 因為笑臉的 ASCII 碼即為 1,參考附錄四 ),接著又是再使 DL 加一,執行 LOOP,檢查 CX 是否為零……。最後,誠如小木偶所說的,1 代表笑臉這個字,65 代表「a」這個字,0∼255 共 256 個數字,各代表不同的字元。


結論

在這一章最後做一些結論:

最後,你可能會對本章的程式不滿,希望能加以改良,最好能順便印出 ASCII 碼及其所代表的符號或文字,下一章我們就討論如何印出數字。


註一:因為只能用 0 和 1 表示,所以當 0000 0001 再增加一時就發生進位了,變成 0000 0010,這就是二進位數。就好像我們所熟悉的十進位一樣,十進位能用 0、1、2、3……8、9 等十個阿拉伯數字表示,所以當 9 再增加一發生進位時,就變成 10。
同理十六進位用十個阿拉伯數字表示外,還用 A 表示 10,B 表示 11,C 表示 12 等等,一直到 F 表示 15,當 F 再增加一時就變成 10,此時是表示十進位的 16。


回到首頁到第二章到第四章