1. 程式人生 > 其它 >彙編實驗:基於BIOS呼叫(10H)的多視窗輸出程式

彙編實驗:基於BIOS呼叫(10H)的多視窗輸出程式

彙編實驗報告-螢幕視窗程式實驗

1. 題目要求:自行編寫一個鍵盤輸入並且在螢幕輸出的程式,它可以完成鍵盤讀入並且在螢幕顯示出來。具體要求:

2. 執行環境:Windows11+MASM

3. 題目分析:

題目要求建立三個視窗用於回顯,利用BIOS呼叫的10H中的6號命令可以完成將螢幕中給定範圍的矩形區域上卷n行,因此我們可以使用該命令劃分出三個視窗的形狀。

此外,題目要求我們的所有輸入都要在最下方視窗顯示,預設也會在右視窗顯示,但是使用左右箭頭可以切換顯示螢幕。因此我們需要動態的置游標於指定位置,這個可以使用型別10H的0A號命令完成,只要給定游標所在的行列座標即可。

最後考慮左右箭頭切換螢幕的問題。由於左右箭頭不是可顯示字元,沒有ASCII碼,因此我們要利用鍵盤中斷獲取的掃描碼進行判斷。搜尋可知左右箭頭的掃描碼分別為4BH、4DH.取掃描碼可以使用BIOS呼叫的16H型別的0號命令,AH中即為掃描碼,AL中為ASCII碼(如果有)。並且判斷完之後,還可以直接利用AL儲存ASCII這一點進行顯示輸出。

4. 流程圖繪製:根據分析和題目要求繪製主函式的流程圖:

根據主函式流程圖,可以編寫主函式程式如下:

main proc 
    push ds
    mov ax,data
    mov ds,ax
    call clear
    scroll 11,win1_ulc,win1_ulr,win1_lrc,win1_lrr
    scroll 11,win2_ulc,win2_ulr,win2_lrc,win2_lrr
    scroll  1,win3_ulc,win3_ulr,win3_lrc,win3_lrr
myloop:
    call get_char
    call display
    jmp myloop
main endp

5. 各部分與功能的流程圖/程式:

  1. 資料段定義:當我們定義資料段時,我們要考慮的有以下幾方面因素:
  • 三個視窗的游標起始位置:因為回顯時使用的BIOS呼叫需要游標的x,y座標,而游標定位到三個視窗的左下角,因此我們需要分別設定三個視窗左下角的行列號。

  • 三個螢幕的位置座標:我們使用上卷的方式顯示三個視窗,而10h型別的6號命令需要提供視窗的左上角座標和右下角座標,因此我們需要分別定義win1\win2\win3的左上角、右下角的行號和列號。

  • 判斷游標在左還是右的flag:因為題目要求按下左右箭頭實現螢幕顯示的切換,因此我們的get_char函式中需要對左右視窗進行設定,而display函式則需要知道在左還是右視窗進行顯示。要實現這個功能,可以引入一個flag,為1則表示在右視窗,反之在左。

    資料段定義(較長,同類型的以...省略):

data segment
win1x db 15;左視窗游標
...
win3y db 15
win1_ulc db 10;win1左上列
...
win3_lrr db 22
isright db 1;flag,判斷是否在右視窗
data ends
  1. 置游標位置函式curse:
    這兩個函式主要執行的功能是根據給定的座標把游標置於該位置,使用10H中斷即可,實現比較簡單。不過由於該函式被diplay呼叫,此時AL中可能儲存著要輸出字元的ASCII,因此要保護一下。

    但由於本實驗多次反覆使用該函式,該函式需要游標位置的行、列號,但我們將行列號儲存在記憶體單元中(win1x,win1y,win2x......),則在每次呼叫子函式之前,都需要通過暫存器設定這兩個儲存,在curse中再使用暫存器讀取,比較麻煩。若使用巨集程式設計方式,可以直接使用形式引數接收主函式提供的實參,因此在呼叫時實參可以直接寫記憶體單元名win1x,win1y等等,更加方便。

  • 流程圖
  • 程式段程式碼如下:
curse  MACRO x,y ;置游標位置
  push ax
  mov dh,x
  mov dl,y
  mov bh,0
  mov ah,2
  int 10h
  pop ax
ENDM
  1. 螢幕上卷函式scroll

    該函式功能為將給定螢幕上卷指定行數,使用10H中斷的6號命令即可。與curse函式類似,由於本函式一共需要5個引數,如果編寫子程式需要用暫存器對這些地址單元依次存取,比較麻煩,因此也採用巨集的方式編寫,使用形式引數接收實參即可。同理,也要考慮保護AX暫存器。

  • 流程圖

  • 程式段程式碼如下:

scroll MACRO cnt,ulc,ulr,lrc,lrr  ;視窗上卷cnt行
    push ax;AX裡可能有字元
    mov al,cnt;卷cnt行
    mov ch,ulr
    mov cl,ulc
    mov dh,lrr
    mov dl,lrc
    mov ah,6
    mov bh,24h
    int 10h
    pop ax
ENDM
  1. 鍵盤輸入與控制字元判斷函式get_char

    該函式功能是利用鍵盤中斷獲取字元的掃描碼和ASCII碼,用掃描碼判斷是否為左右箭頭或ESC,若是則執行對應控制命令;將非控制字元的ASCII碼存在AL暫存器,留待display展示。

    • 流程圖

    • 程式碼段如下:

get_char proc near;獲取字元,判斷是否是左右箭頭和esc。注意我們這裡不在意現在游標在win1/win2
input:
    mov ah,0
    int 16h;BIOS,ah存掃描碼,al存ASCII碼
    cmp ah,4bh;左箭頭
    jnz no_left;若不是,繼續判斷
    mov isright,0;是,切換游標
    jmp input;不可輸出箭頭,需要再接收
no_left:
    cmp ah,4dh;右箭頭
    jnz no_right;也不是右箭頭
    mov isright,1
    jmp input;不可輸出箭頭,需要再接收
no_right:
    cmp ah,01;ESC
    jnz get_complete;說明不是控制字元,回主函式準備輸出
    mov ah,4ch
    int 21h;ESC,結束整個程式
get_complete:
    ret;回主函式,此時al儲存著字元
get_char endp
  1. 輸出與上卷判斷函式display

    該函式功能是將AL中字元輸出到下方視窗,以及左右視窗的其中一個,並在輸出完成後判斷是否應該上卷。(本函式呼叫的output1只是對BIOS呼叫10H的封裝,不再贅述)

    • 流程圖

    • 程式碼段較長,這裡不再附上,可參看附件中的源程式。

6.執行結果

  1. 輸入,預設在下視窗、右視窗顯示。可以看到當右側視窗游標進行到行末後,右側螢幕會上卷一行。

  2. 按下左箭頭,繼續輸入,可以看到左視窗和下視窗開始顯示內容。可以看到左、下視窗的上卷也成功實現。由於下視窗只有一行,所以上卷後只留下新一行的資訊。

  3. 當左/右視窗輸滿時,最上一行會上卷消失:

    可見功能均正常實現。