Ch 28 輸出入埠 (1) CMOS


概念

在往後的一、兩章堙A小木偶想談談輸出入埠,所謂輸出入埠,就是英文的 Input/Output port,簡稱為「I/O port」,他是電腦與周邊設備溝通的管道。

在 IBM PC 相容電腦堙A所有的周邊設備都是藉著輸出入埠和 CPU 相連,IBM PC 共有 65536 個輸出入埠,由編號 0000H 到 0FFFFH,其編排方式有點像一個區段的記憶體,但要寫入或讀取卻完全不同,有些輸出入埠只能讀不能寫,有些只能寫不能讀,有些可寫可讀,都跟和那一種周邊設備有關。每一種周邊設備使用數個輸出入埠與 CPU 相連,當然 PC 提供了六萬多個輸出入埠,而周邊設備不可能有這麼多,所以有許多輸出入埠是空下來的,並沒有使用。例如下表就是 PC/XT 常用的周邊裝置與相對應的輸出入埠。( PC/AT 與 PC/XT 的 I/O 埠稍微不同 )

PC/XT
I/O埠
周邊裝置 描 述
00∼0F 8237A-5 DMA 控制晶片 8237A-5 是用來加速記憶體和周邊裝置 ( 如硬碟 ) 之間的資料傳送 ( 註一 )
20∼21 8259A 中斷控制器 負責管理中斷及中斷請求 ( IRQ )
40∼43 8253-5 計時器 內含三個計時器,見下一章
60∼63 8255A-5 PPI 8255A 是一種可程式周邊界面晶片 ( Programmable Peripheral Interface,即 PPI ),通常鍵盤與喇叭接在這個晶片上。意即可經由程式控制鍵盤與喇叭。
200∼20F JOYSTICK 一般用來接搖桿,玩電玩用
278∼27F 第二個平行埠 即 PARALLEL 2 或 LPT 2,一般用來接 KEY PRO
2F8∼2FF 第二非同步埠 即 COM 2 或第二個 RS-232,一般用來接數據機或滑鼠
378∼37F 第一個平行埠 即 PARALLEL 1 或 LPT 1,一般用來接印表機
3F8∼3FF 第一非同步埠 即 COM 1 或第一個 RS-232,一般用來接數據機或滑鼠

回想以前,還未有 PnP 的時代堙A假如您想自己組裝電腦的話,一定要知道該周邊設備所使用的輸出入埠編,或者有些周邊裝置可以自行調整輸出入埠,使得所有的周邊裝置都有獨一無二的輸出入埠,這樣插入擴充槽後才能正常使用。假如有任何兩個周邊裝置使用相同的輸出入埠,或者有部份重疊,那麼下場只有兩種,不是不能開機,就是不能正常運作。

回歸正傳,寫程式時如果要在螢幕上顯示某一個字元,由印表機印出一份文件,甚至由鍵盤輸入一個字……時,我們怎麼做?大多數的人都是藉由 INT 中斷服務程式來替我們完成,但實際上這些中斷程式是藉由輸出入埠和周邊設備 ( 顯示器、印表機、鍵盤…… ) 溝通。這些都是因為 DOS 或 Win 9x 都已經寫好這些服務程式,我們只需拿來用就好了,假如我們有足夠的資料,我們也能自行控制這些輸出入埠,達到上述目的。

當我們改變某個輸出入埠內的資料,其實就是送某個資料到相對應的周邊設備去,而周邊設備就能依照我們的要求做動作,例如在螢幕上印出一個字。相反的假如要讀取某個周邊設備的狀態,也是利用輸出入埠把其內的資料傳回來。80X86 CPU 提供底下兩個指令用來溝通輸出入埠的。

OUT 指令

語法是:

out     dx,al
out     輸出入埠編號,al
out     dx,ax
out     輸出入埠編號,ax

OUT 指令是把一個位元組或一個字組長度的資料送到輸出入埠,所送出的資料放在 AL 或 AX 暫存器。送出到那一個輸出入埠則由 DX 暫存器指定,或者也可以直接用輸出入埠編號指定。

IN 指令

語法是:

in      al,dx
in      al,輸出入埠編號
in      ax,dx
in      ax,輸出入埠編號

IN 指令和 OUT 指令恰好相反,是把輸出入埠的資料傳回 AL 或 AX 暫存器中。輸出入埠編號可以直接寫出來也可以放在 DX 暫存器中。

電腦的周邊設備很多,而每一種周邊設備的輸出入埠操作方法不同,而且有時候不當的數值會使周邊設備不正常運作,甚至當機,加上現在電腦產品的技術資料幾乎無法取得,因此輸出入埠的使用常蒙上一層神祕的陰影,小木偶就將手邊現有的資料與大家分享。( 小木偶參考民國 83 年 4 月份第三波雜誌林錦瑞先生的文章 (第 16 頁 )、 CMOS Registers 以及 Marc Feeley 的網頁。)


CMOS

CMOS 資料

CMOS ( complementary metal oxide semiconductor 的簡寫,是指互補金金氧化物半導體 ) 是一種耗電很低的記憶體,它是用來儲存電腦中的基本設備,例如硬碟數目及形式、軟碟數目及形式、記憶體大小、顯示器等等,還有像計時這種隨時改變的資料。CMOS 是靠電腦內的電池供應電流,所以即使電腦的電源關掉,其內的資料也不會流失,但是如果電池沒電,CMOS 的資料就後不正確,必須重設,幸好 CMOS 使用的電池是可充電的鋰電池,只要您常使用電腦,就能在使用時同時充電,所以不必擔心這種問題。

IBM PC/XT 等級的電腦是沒有 CMOS,到了 IBM PC/AT 電腦才配備了 CMOS,採用的是 MC146818A 晶片,可存放有 64 位元組的資料。而 386 機種通常是把它整合到其他晶片中 ( 例如 82C206 晶片 ),容量也擴充到 128 到 256 位元組。

CMOS 的資料看得見嗎?這是當然的,在電腦一開機時,會出現像底下的畫面,就是依據 CMOS 內的資料顯示於螢幕上的。

除了在開機時能看到外,您也可以在開機時尚未進入作業系統前,進入 BIOS 設定程式,也可以看到記錄於 CMOS 內的內容。在這一節堙A小木偶將要以自己製作的程式來讀取 CMOS,當然也可寫入 ( 設定 BIOS 時,其實就是修改 CMOS 的內容 ),但是這是很危險的,因為設定值不對很容易造成當機。

要寫程式顯示 CMOS 的資料,要先了解其內容。小木偶只找到前 64 個位元組的資料,如下表:

偏移位址意義
00H∼0DH 即時時鐘 ( realtime clock )
0EH 診斷狀態 ( diagnostic status )
0FH 開機狀態 ( reat code )
10H 軟碟機型式 ( diskette type )
11H 保留
12H 硬碟機型式 ( harddisk type )
13H 保留
14H 設備狀態 ( equipment status )
15H∼16H 傳統記憶體大小 ( base memory )
17H∼18H 延伸記憶體大小 ( extended memory )
19H 第一台硬碟型態 ( first HD type )
1AH 第二台硬碟型態 ( second HD type )
1BH∼2DH 保留
2EH∼2FH 記憶體之查核值 ( check sum )
30H∼31H 延伸記憶體大小 ( extended memory )
32H 世紀數 ( BCD 格式 )
33H 啟動旗標
34H∼3FH 保留

用 OUT/IN 指令寫入或讀取 CMOS

電腦是以 70H 與 71H 兩個輸出入埠與 CMOS 溝通,當要讀取 CMOS 的某個位址時,先把該位址填入埠 70H,然後再由埠 71H 讀取該位址之資料。舉例來說,要讀取『世紀數』,可以用下面程式:

        mov     al,32h
        out     70h,al
        in      al,71h

這樣,CMOS 的位址 32H 的數值就在 AL 暫存器堙C同理,要改寫世紀數,也是要把 CMOS 的位址填入埠 70H,再把要改寫的世紀值輸出到埠 71H,如下面程式

        mov     al,32h
        out     70h,al
        mov     al,20h
        out     71h,al

CMOS 內的一些欄位說明

小木偶就自己所收集到的資料,說明在 CMOS 內的資料意義。

位址 00H ∼ 0DH 表示即時時鐘,其內容請看下表:

位址意義 位址意義
00H07H
01H08H
02H09H
03H0AH
04H小時0BH
05H0CH
06H星期0DH

位址 0EH 表示診斷狀態,這個位元組各位元的意義是

位元 意義
7 電池狀態
6 查核值狀態
5 結構狀態
4 記憶體大小記錄狀態
3 硬碟狀態
2 即時時鐘狀態
1 保留
0 保留

當位元值為一時,表示該項正常。例如位元七為一,表示電池正常,若為零,表示電力不足。

位址 10H 表示軟碟機型式,這個位元組被分成兩部份,較高的四個位元表示軟碟機 A: 的型式;較低的四個位元表示軟碟機 B: 的型式。雖然每個軟碟機被分配到四個位元組,共可以表示 16 種不同型式的軟碟機,但事實上,市面上所販賣的磁碟機只有五種而已,如下表:

BIT 值  軟碟機型式
--------------
0000    未裝置該軟碟機
0001    5.25 吋 360K 軟碟機
0010    5.25 吋 1.2M 軟碟機
0011    3.5 吋 720K 軟碟機
0100    3.5 吋 1.44M 軟碟機
0101    3.5 吋 2.88M 軟碟機

位址 12H 是表示硬碟機的型態,同軟碟機類似,此位元組也分成兩部分,較高的四個位元組是表示第一台硬碟型式,較低的四個位元組是表示第二台硬碟型式,如果為 0000 表示該硬碟不存在,不為零,表示該硬碟型式。因為每台硬碟被分到四個位元組,故可表示 16 種型式的硬碟,但是硬碟發展遠較軟碟快速,從最先 IBM XT 所配備的 20MB 硬碟,到現在 80G 的硬碟,故可知這十六種型式,根本不夠用,所以這四個位元如果是『1111』的話,表示要到位址 19H 或 1AH 去取得真正的硬碟型式。但後來大概是真的發展太快,又加上不再使用磁軌、磁區、磁面來表示硬碟型式,都改用 TYPE 46,由使用者定義,後來更進一步由 BIOS 自己偵測。

位址 14H 是表示設備狀態,位元 7、6 表示軟碟機數目。位元 5、4 表示顯示卡種類:

位元5 位元4  顯示卡
------------
  0    0     EGA/VGA
  0    1     40 行 CGA
  1    0     80 行 CGA
  1    1     MGA ( 單色卡 )

位元 2、3 保留。位元 1 表示是否有浮點運算器,1 表示有,0 表示沒有。位元 0 表示是否有磁碟機 A:,1 表示有,0 表示沒有。

位址 15、16 這兩個位元組表示傳統記憶體大小,單位是 1KB,即 1024 個位元組為單位。舉例來說,若位址 15、16 的內容分別是 80H、02H,表示有 280HKB,換成十進位即 640KB。

位址 17、18 這兩個位元組表示延伸記憶體大小,單位是 1KB。

傾印 CMOS

底下這個程式是用來把 CMOS 的前 64 個位元組於螢幕上印出來,並顯示重要諸元:

tab     equ     09h
cr      equ     0dh
lf      equ     0ah
;***************************************
code    segment
        assume  cs:code,ds:code
        extrn   print_bl_hex:near
        org     100h
;---------------------------------------
start:  jmp     begin
cmos    db      64 dup (?)              ;011
str00   db      '第一台軟碟機:$'
str01   db      '第二台軟碟機:$'
str02   db      '第一台硬碟機:$'
str03   db      '第二台硬碟機:$'
str04   db      '浮點運算器:$'
str05   db      '傳統記憶體:$'
str06   db      'KB',cr,lf,'延伸記憶體:$'
str07   db      '顯示卡種類:$'
str08   db      '現在時間:2003年09月27日  06:40$'
str50   db      '存在$'
str51   db      '不存在$'
str52   db      '5.25 吋 360KB$'
str53   db      '5.25 吋 1.2MB$'
str54   db      '3.5 吋 720KB $'
str55   db      '3.5 吋 1.44MB$'
str56   db      '3.5 吋 2.88MB$'
str57   db      'VGA/EGA$'
str58   db      '40 行 CGA$'
str59   db      '80 行 CGA$'
str60   db      'MGA$'
pbcd    dt      ?                       ;032
display dw      str57,str58,str59,str60 ;033
begin:  mov     dl,tab          ;034 第038 到 044 行
        call    print_char      ;035 印出位址之個位數
        mov     cx,16
        sub     bx,bx           ;037 BX為十六進位之個位數
next1:  push    cx              ;038—┐
        call    print_bl_hex    ;039  │
        pop     cx              ;040  ├印出位址之十六進位
        mov     dl,' '          ;041  |個位數,即 BL 之值
        inc     bx              ;042  |
        call    print_char      ;043  |
        loop    next1           ;044—┘
        call    print_cr_lf     ;045 換行
        call    print_line      ;046 印出分隔線

        mov     di,offset cmos
        cld
        sub     bx,bx           ;050 BX=CMOS指標
        mov     cx,64           ;051 CX=共讀取64個位元組
next2:  mov     dx,70h          ;052—┐
        mov     al,bl           ;053  │
        out     dx,al           ;054  |
        mov     dx,71h          ;055  ├讀取CMOS內容64位元組,並
        in      al,dx           ;056  |存於第12行的CMOS變數內
        stosb                   ;057  |
        inc     bx              ;058  |
        loop    next2           ;059—┘

        sub     dx,dx           ;061 每執行一次小迴圈,DL就加10H
        mov     si,offset cmos  ;062 SI 指向CMOS變數內的每一個位址
next3:  mov     bl,dl           ;063—————————┐
        push    dx              ;064                  │
        call    print_bl_hex    ;065                  |
        mov     dl,'H'          ;066                  |
        call    print_char      ;067                  |
        mov     dl,tab          ;068                  |
        call    print_char      ;069                  |
        mov     ch,10h          ;070                  | 
next4:  mov     bl,[si]         ;071—┐              |
        call    print_bl_hex    ;072  │              ├四行CMOS    
        mov     dl,' '          ;073  |以16個位元組為|的迴圈
        call    print_char      ;074  ├一行,印出一行|
        inc     si              ;075  |CMOS內容的迴圈|
        dec     ch              ;076  |              |
        jnz     next4           ;077—┘              |

        call    print_cr_lf     ;079                  |
        pop     dx              ;080                  |
        add     dx,10h          ;081                  |
        cmp     dl,40h          ;082                  |
        jnz     next3           ;083—————————┘

        call    print_line
        call    print_cr_lf
        call    floppy_type     ;087 印出軟碟機型式

        call    print_cr_lf
        call    hd_type         ;090 印出硬碟機型式

        call    print_cr_lf
        call    equipment       ;093 印出設備

        call    print_cr_lf
        call    memory          ;096 印出記憶體大小

        call    print_cr_lf
        call    date_and_time   ;099 印出時間及日期

        mov     ax,4c00h        ;101 結束,返回 DOS
        int     21h
;---------------------------------------
;印出字串
;輸入:DX-字串位址
print_string    proc    near
        push    ax
        mov     ah,9
        int     21h
        pop     ax
        ret
print_string    endp
;---------------------------------------
print_line:                     ;114 印出分隔線
        mov     cx,58
        mov     dl,'-'
pnt_m:  call    print_char
        loop    pnt_m
;---------------------------------------
print_cr_lf:                    ;120 換行
        mov     dl,cr
        call    print_char
        mov     dl,lf
;---------------------------------------
print_char      proc    near    ;125 印出一個字元
        push    ax
        mov     ah,2
        int     21h
        pop     ax
        ret
print_char      endp
;---------------------------------------
equipment       proc    near
        mov     dx,offset str07 ;134 印出『顯示卡種類:』字串
        call    print_string
        mov     bx,offset cmos+14h
        mov     al,[bx]         ;137 取得 CMOS 的設備狀態
        push    ax
        mov     cl,4            ;139 由AL的第5、4位元,計算顯示卡種類
        shr     al,cl
        and     al,0ch
        mov     si,ax
        shl     si,1
        add     si,offset display
        mov     dx,[si]         ;145 DX指向STR57∼60四個字串的其中之一
        call    print_string    ;146 印出顯示卡種類
        call    print_cr_lf

        mov     dx,offset str04 ;149 150行到159行印出是否有FPU
        call    print_string
        pop     ax
        mov     dx,offset str50 ;152 指向『存在』字串
        and     al,2
        jnz     fpu_yes
        mov     dx,offset str51 ;155 指向『不存在』字串
fpu_yes:
        call    print_string
        call    print_cr_lf
        ret
equipment       endp
;---------------------------------------
hd_type proc    near            ;162 印出硬碟機型式副程式
        mov     bx,offset cmos+12h
        mov     al,[bx]
        mov     ch,al
        mov     bx,offset cmos+19h
        mov     cl,4
        mov     dx,offset str02 ;168 DX指向『第一台硬碟機:』字串
        shr     al,cl
        call    hd_c
        mov     al,ch
        mov     bx,offset cmos+1ah
        mov     dx,offset str03 ;173 DX指向『第二台硬碟機:』字串
        and     al,0fh

hd_c:   call    print_string    ;176 印出『第XX硬碟機:』的字串
        cbw
        or      ax,ax
        jz      no_hd           ;179 若AX=0,表示沒有該台硬碟機
        cmp     al,0fh          ;    若AL=0FH,表示硬碟機型式在 19H 或 1AH 處
        jne     hd_no
        mov     al,[bx]
hd_no:  sub     dx,dx           ;183 硬碟機型式存於 AX 中
        mov     di,offset pbcd  ;184 將 AX 之十六進位數變成聚集 BCD 數
        call    hex32_to_pbcd
        mov     dl,[di]
        shr     dl,cl
        call    h_digit         ;188 印出硬碟型式之十位數
        mov     dl,[di]
        call    l_digit         ;190 印出硬碟型式之個位數
        call    print_cr_lf
        ret
no_hd:  mov     dx,offset str51 ;193 印出『不存在』字串
        call    print_string
        ret
hd_type endp
;---------------------------------------
floppy_type     proc    near    ;198 印出軟碟機型式副程式
        mov     bx,offset cmos+10h
        mov     al,[bx]
        mov     cl,4
        mov     ah,al
        mov     dx,offset str00 ;203 指向『第一台軟碟機:』字串
        call    floppy_a
        mov     al,ah
        mov     dx,offset str01 ;206 指向『第二台軟碟機:』字串
        and     al,0fh
floppy_a:
        call    print_string    ;209 印出『第XX軟碟機:』字串
        mov     dx,offset str51 ;210 計算DX應指向的位址
        shr     al,cl
        jz      floppy_ok
        add     dx,7
        cmp     al,1
        je      floppy_ok
        dec     al
floppy_exist:
        add     dx,14
        dec     al
        jnz     floppy_exist
floppy_ok:
        call    print_string    ;222 DX指向軟碟機型式
        call    print_cr_lf
        ret
floppy_type     endp
;---------------------------------------
date_and_time   proc    near    ;227 印出時間、日期副程式

        mov     di,offset str08+10
        mov     bx,offset cmos+32h      ;230 BX指向『世紀數』
        mov     cl,4
        call    a_byte
        mov     bx,offset cmos+09h      ;233 BX指向年份
        call    a_byte

        inc     di
        inc     di
        dec     bx              ;238 BX指向月份
        call    a_byte

        inc     di
        inc     di
        dec     bx              ;243 BX指向日
        call    a_byte

        add     di,4
        sub     bx,3            ;247 BX指向小時
        call    a_byte

        inc     di
        inc     di
        dec     bx
        dec     bx              ;253 BX指向分
        call    a_byte

        call    print_cr_lf
        mov     dx,offset str08 ;257 印出時間
        call    print_string
        ret

;使BX所指位址的位元組(該位元組為一聚集BCD數)變成十進位
;數存於AX,AH為個位數,AL為十位數,再存於DI所指的位址
a_byte: mov     al,[bx]
        mov     ah,al
        and     ah,0fh
        shr     al,cl
        add     ax,3030h
        stosw
        ret
date_and_time   endp
;---------------------------------------
;把存於 DX:AX 內的十六進位數轉換成聚集 BCD 數,存於 ES:DI 所指的位址
;輸入:DX:AX 32 位元的十六進位數
;      ES:DI 6 個位元組長,聚集的 BCD 數
;輸出:ES:DI 已填好等值於 DX:AX 的聚集 BCD 數
;      DX:AX 會被破壞

hex32_to_pbcd   proc    near
        public  hex32_to_pbcd

        push    bx
        push    cx
        mov     bx,ax
        mov     cx,3
        xor     ax,ax
        repz    stosw
        sub     di,6
        mov     cx,20h  ;32 bits
n_bit:  shl     bx,1
        rcl     dx,1
        xchg    es:[di],ax
        call    decimal
        xchg    es:[di],ax

        xchg    es:[di+2],ax
        call    decimal
        xchg    es:[di+2],ax

        xchg    es:[di+4],ax
        call    decimal
        xchg    es:[di+4],ax
        adc     al,0
        loop    n_bit
        pop     cx
        pop     bx
        ret
decimal:
        adc     al,al
        daa
        xchg    al,ah
        adc     al,al
        daa
        xchg    al,ah
        ret
hex32_to_pbcd   endp
;---------------------------------------
l_digit:
        and     dl,0fh
h_digit:
        add     dl,'0'
        call    print_char
        ret
;---------------------------------------
memory  proc    near
        mov     dx,offset str05 ;325 DX指向『傳統記憶體:』字串
        call    print_string
        mov     bx,offset cmos+15h
        mov     ax,[bx]
        sub     dx,dx           ;329 DX:AX=傳統記憶體大小(以1K為單位)
        mov     di,offset pbcd
        call    hex32_to_pbcd   ;331 把DX:AX改成十進位
        mov     cl,4
        mov     dl,[di+1]
        call    l_digit         ;334 印出傳統記憶體大小的百位數
        mov     dl,[di]
        shr     dl,cl
        call    h_digit         ;337 印出傳統記憶體大小的十位數
        mov     dl,[di]
        call    l_digit         ;339 印出傳統記憶體大小的個位數

        mov     dx,offset str06
        call    print_string

        ret
memory  endp
;---------------------------------------
code    ends
;***************************************
        end     start

程式說明

這個程式在組譯、連結時,必須依以下方式:

H:\HomePage\SOURCE>masm dumpcmos; [Enter]
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.


  51362 + 386030 Bytes symbol space free

      0 Warning Errors
      0 Severe  Errors

H:\HomePage\SOURCE>link dumpcmos [Enter]

Microsoft (R) Personal Computer Linker  Version 2.40
Copyright (C) Microsoft Corp 1983, 1984, 1985.  All rights reserved.

Run File [DUMPCMOS.EXE]:[Enter]
List File [NUL.MAP]:[Enter]
Libraries [.LIB]: myasmlib [Enter]
Warning: no stack segment

H:\HomePage\SOURCE>exe2bin dumpcmos dumpcmos.com [Enter]

而執行結果,如下圖:

DumpCMOS.com 執行結果

這個程式雖然看起來很大,但事實上並沒有什麼技巧,大部分都是在比對資料,看是屬於那一種類型的設備。我想值得一提的是第 33 行的 display 變數。這個變數的內容是四個位址的集合,分別指向 str57、str58、str59、str60 這四個字串,該四個字串是表示顯示卡種類的字串。我想,您應該時時記得,在組合語言中的變數,其實代表一個位址,所以 str57 表示一個位址,當小木偶用下面式子表示時

str57   db      'VGA/EGA$'
display dw      str57

代表著 display 的內容是 str57 所在位址,您只要找到 display 這個變數的內容,依此內容就可以找到 str57 所在。

如果寫成

display dw      str57,str58,str59,str60

那麼就表示 display 變數包含四個位址,分別表示 str57 ∼ str60 所在位址。每個位址都是一個字組的長度,所以距 display 所指位址的第零個位元組是 str57,距 display 所指位址的第二個是 str58,距 display 所指位址的第四個字組是 str59 ……。

CMOS 設備狀態資料,如果把第 4、5 兩個位元移到第 0、1 兩個位元,那麼由 0 到 3 分別代表不同的顯示卡,也就是 0 到 3 分別代表 str57 到 str60 這四個字串,但這四個字串長短不一,若把他換成位址,就一樣長了 ( 都是兩個位元組 ),所以很容易計算其位址。

        mov     al,[bx]         ;137 取得 CMOS 的設備狀態
        push    ax
        mov     cl,4            ;139 由AL的第5、4位元,計算顯示卡種類
        shr     al,cl
        and     al,0ch
        mov     si,ax
        shl     si,1
        add     si,offset display
        mov     dx,[si]         ;145 DX指向STR57∼60四個字串的其中之一

第 137 行取得設備狀態後,由第 140、141 行的位元移動使第 4、5 位元變成第 0、1 位元,然後再由第 143 行乘以 2,就得到由 display 所指的位址開始再偏移的位元組數。為何要乘以 2 呢?這是因為每個位址佔兩個位元組。

另外還值得一提的是 floppy_type 副程式。雖然說軟碟型式包含『沒安裝軟碟』,大概就只這六種。如果用『若 AL=0,則未裝軟碟』,『若 AL=1,則安裝5.25 吋 360K 軟碟機』……這樣的判斷方式雖也可以,但總覺不夠精簡。仔細觀察 str51∼str56 這幾個字串,您應該會發現除了 str51 長 7 個位元組外,其餘均長 14 個位元組,可說變化規則,所以小木偶用了一些小技巧簡化了程式。請自行參考第 210 到第 220 的程式碼,相信不太難懂。

副程式 hex32_to_ubcd 是用來把存在於 DX:AX 堛 32 位元長的十六進位數,轉換成聚集 BCD 數,例如 DX:AX=0200:ABCD 轉換後依位址由低而高是 13 84 59 33,也就是十進位的 33598413。這個副程式轉換的原理簡單講是由 DX:AX 的最低位元開始處理,存於 ES:DI 所指的位址,每向左處理一個位元,便使 ES:DI 之值乘以 2,然後再適當的調整使其變為在 0∼9 之間,如此處理 32 次就得到答案。

至於延伸記憶體大小,小木偶所找到的資料中,似乎在 64MB 以下的記憶體才正確,在 64MB 以上的記憶體不相符,還望諸位先進提供資料。



註一:

假如我們要把資料由硬碟讀到記憶體中,如果用 CPU 去一筆一筆的把資料讀進來,然後在把這些資料搬到指定的記憶體上,是很花時間的。我們的做法是 CPU 只要下指令告訴 8237 需要把那一些資料讀進記憶體然後要放在記憶體那兒即可,然後 CPU 就可以回過頭去執行下一道指令,而 8237 會負責把硬碟上的資料搬到指定的記憶體上去。

像這樣不須透過 CPU 而直接把資料搬運的過程,可以加速硬碟與記憶體之間資料的轉移。所以 8237 也稱為 DMA (Direct Memory Access) 控制器。


回到首頁到第二十七章到第二十九章