1. 程式人生 > >視訊記憶體文字模式詳解 ———《x86組合語言:從真實模式到保護模式》讀書筆記補遺02

視訊記憶體文字模式詳解 ———《x86組合語言:從真實模式到保護模式》讀書筆記補遺02

文章修改記錄

修改日期 修改內容
2018-2-4 修改了一處錯別字;增加了表格的使用方法

今天我們討論如何程式設計以在螢幕上顯示出彩色的文字。

為了顯示文字,通常需要兩種硬體——顯示器和顯示卡。
顯示卡的作用是為顯示器提供要顯示的內容,並且控制顯示器的模式和狀態。
顯示器的作用是把那些內容以人們可見的方式呈現在螢幕上。

1.視訊記憶體

每個顯示卡都有自己的儲存器,因為它位於顯示卡上,所以稱為顯示儲存器,簡稱“視訊記憶體”。和其他儲存器一樣,視訊記憶體並沒有什麼特殊的地方,也是一個按位元組訪問的儲存器件。

2.顯示卡的兩種工作模式

顯示卡最基本的兩種工作模式是文字(也稱為文字)模式和圖形模式。在不同的模式下,顯示卡對視訊記憶體內容的解釋是不同的。要想設定顯示卡的顯示模式,可以用指令訪問顯示卡,也可以直接呼叫BIOSint 10h

中斷。

3.BIOS呼叫之設定顯示模式

功能號:AH = 00H
用 途:設定顯示模式
參 數:AL = 顯示模式號
調 用:INT 10H
返 回:無

AL的取值說明:

AL 文字/圖形 解析度 顏色
00H 文字 40*25 2
01H 文字 40*25 16
02H 文字 80*25 2
03H 文字 80*25 16
04H 圖形 320*200 2
05H 圖形 320*200 4
06H 圖形 640*200 2

需要說明的是:計算機在加電自檢後會自動初始化到AL=03H

的文字模式。在這種模式下,一螢幕可以顯示25行,每行80個字元,總共是80*25=2000個字元。

4.文字模式下,視訊記憶體到記憶體的對映

0xB80000xBFFFF這段實體地址被對映到視訊記憶體。也就是說,寫這些實體地址,就可以控制顯示內容。

視訊記憶體和每個字元(假入從0開始數,那就是0~1999)的對應關係,如下圖所示。

這裡寫圖片描述

5.關於屬性

bit [7] [6:4] [3:0]
含義 1:字閃爍;0:字不閃爍 背景色 前景色

5.1 背景色

因為[6:4]決定背景色,所以取值是0~7。根據我判識色彩的能力,總結如下。

[6:4]取值 0 1 2 3 4 5 6 7
顏色 深藍 粉紅 灰白

5.2 前景色

因為[3:0]決定前景色,所以取值是0x0~0xF。根據我判識色彩的能力,總結如下。

[3:0]取值 0 1 2 3 4 5 6 7 8 9 A B C D E F
顏色 深藍 粉紅 灰白 亮藍 亮綠 亮青 亮紅 亮粉紅 亮白

6.程式設計實踐——遍歷所有顏色

關於顏色,眼見為實。也許我眼中的青色,在你眼中就是藍色。不妨程式設計看看,顯示在螢幕上的到底是什麼顏色。

思路:在不考慮閃爍的情況下,前景色搭配背景色,共有16*8=128種可能,我們的目的是提供一個表格,每一行表示前景色的不同取值,每一列表示背景色的不同取值。

關於要顯示的內容,可以選擇一個字串(考慮到一行最多顯示80個字元,80/16=5,所以字串長度不宜超過5個),一共顯示128次。

6.1 通過呼叫BIOS中斷實現

    jmp near start

message db 'KARL '   ;字串任意,但是不要超過5個字元
                     ;取KARL是因為KARL是我徒弟的英文名


;int 10h (video service)
;AH=13h, 在teletype模式下顯示字串
;入口引數:
;   AL[1:0]=顯示方式
;     [0]: 0表示不移動游標,1表示移動游標
;     [1]: 0表示字串中僅包含字元,不包含屬性,屬性在BL中;1表示字串中包含屬性
;   BH=頁碼
;   BL=屬性
;   CX=字串長度
;   DH=行
;   DL=列
;   ES:BP=指向字串
;
;出口引數:無


start:
    mov ax,0x7c0    ;設定ES段的段地址 
    mov es,ax     
    mov bp,message  ;ES:BP指向字串
    mov ah,0x13     ;在teletype模式下顯示字串
    mov al,1   ;顯示方式,表示字串中僅包含字元,不包含屬性,屬性在BL中,移動游標
    mov bl,0   ;屬性初始值
    mov bh,0   ;頁碼
    mov dh,0   ;從0行開始


    mov cx,8   ;迴圈8次,從0行到7行

put_0_8: ;------------------------------------外層迴圈

    push cx   ;因為內層迴圈也要用CX控制迴圈次數,所以壓棧保護
    mov dl,0  ;從0列開始
    mov cx,16 ;迴圈16次,從0列到15列

put_0_F:         ;------------內層迴圈
    push cx   ;因為迴圈體中要用到CX,所以壓棧保護
    mov cx,5  ;設定字串長度
    int 0x10  ;BIOS中斷呼叫
    inc bl    ;改變屬性,屬性值增加1
    add dl,5  ;改變列,列值增加5 
    pop cx
    loop put_0_F ;------------內層迴圈

    pop cx
    inc dh    ;改變行,行增加1  
    loop put_0_8  ;----------------------------外層迴圈

    jmp near $   ;使陷入死迴圈


times 510-($-$$) db 0
                 db 0x55,0xaa

執行結果如下圖
這裡寫圖片描述

6.2 通過自己寫過程實現

    jmp near start

message db 'KARL '
        db 0       ;本程式的過程規定以0結尾

start:
    mov ax,0x7c0   ;設定資料段的段基地址 
    mov ds,ax

    mov bx,message ;使DS:BX指向字串
    mov al,0       ;屬性初始值

    mov dh,0       ;從0行開始
    mov cx,8       ;迴圈8次,從0行到7行

put_0_8:         ;----------------------------外層迴圈

    push cx
    mov dl,0     ;列的初始值
    mov cx,16    ;迴圈16次,從0列到15列

put_0_F:         ;--------內層迴圈

    call put_string
    inc al       ;改變屬性,屬性值增加1
    add dl,5     ;改變列,列值增加5 
    loop put_0_F ;--------內層迴圈

    pop cx
    inc dh       ;改變行,行增加1  
    loop put_0_8 ;----------------------------外層迴圈

    jmp near $ 
;-------------------------------------  
;功能:在某位置顯示字串      
;入口引數:
;   AL=屬性
;   DH=行
;   DL=列
;   DX:BX=指向字串,字串必須以0結尾
;出口引數:無         
put_string:

    push ax
    push bx
    push cx
    push dx
    push di
    push es

    push ax          ;AX中是屬性,因為下面要用AX,所以先進棧保護起來
    mov ax,0xb800
    mov es,ax
                     ; xy列,換算成偏移是:(x*80+y)*2
    mov al,80
    mul dh           ;ax=al*dh (計算出x*80,結果在ax中)
    xor dh,dh        ;dh清零
    add ax,dx        ;計算出(x*80+y),結果在ax中
    shl ax,1         ;計算出(x*80+y)*2,結果在ax中

    mov di,ax        ;用di儲存偏移
    pop ax           ;得到屬性
put_char:
    mov cl,[bx]      ;取要顯示的字元到cl中
    cmp cl,0         ;和0比較
    jz end           ;等於0則跳轉
    mov [es:di],cl   ;寫字元的ASCII碼到視訊記憶體
    inc di
    mov [es:di],al   ;寫字元的屬性到視訊記憶體
    inc di           ;di指向視訊記憶體中的下一個位置
    inc bx           ;bx指向下一個字元
    jmp put_char
 end:   
    pop es
    pop di
    pop dx
    pop cx
    pop bx
    pop ax

    ret              ;返回
;-------------------------------------------

times 510-($-$$) db 0
                 db 0x55,0xaa

6.3 將實驗結果製作成表格

這裡寫圖片描述

可以看到,當前景色和背景色取值相同時,就看不到字了。所以,屬性組合不是128種,而是128-8=120種。

查詢表格的時候,最左邊一列的數字表示背景色,最上面一行的數字表示前景色(即字的顏色)。

  • 舉例1:0x02——黑底綠字
  • 舉例2:0x04——黑底紅字
  • 舉例3:0x24——綠底紅字

7.使用LOOP要注意什麼

上面的程式碼使用了LOOP實現迴圈,對於初學者,用LOOP時需要注意的是:

  • 迴圈之前的初始化,比如迴圈次數(CX)、變數的初始值等
  • 標號的位置
  • 迴圈體內變數的自增/自減
  • 對於巢狀的LOOP,尤其要注意CX的壓棧出棧和其他暫存器(如果需要)的壓棧、出棧

【完】