汇编实验报告-屏幕窗口程序实验

1. 题目要求:自行编写一个键盘输入并且在屏幕输出的程序,它可以完成键盘读入并且在屏幕显示出来。具体要求:

双屏不显示bios bios多屏显示_流程图

双屏不显示bios bios多屏显示_寄存器_02

2. 运行环境:Windows11+MASM

3. 题目分析:

题目要求建立三个窗口用于回显,利用BIOS调用的10H中的6号命令可以完成将屏幕中给定范围的矩形区域上卷n行,因此我们可以使用该命令划分出三个窗口的形状。

此外,题目要求我们的所有输入都要在最下方窗口显示,默认也会在右窗口显示,但是使用左右箭头可以切换显示屏幕。因此我们需要动态的置光标于指定位置,这个可以使用类型10H的0A号命令完成,只要给定光标所在的行列坐标即可。

最后考虑左右箭头切换屏幕的问题。由于左右箭头不是可显示字符,没有ASCII码,因此我们要利用键盘中断获取的扫描码进行判断。搜索可知左右箭头的扫描码分别为4BH、4DH.取扫描码可以使用BIOS调用的16H类型的0号命令,AH中即为扫描码,AL中为ASCII码(如果有)。并且判断完之后,还可以直接利用AL存储ASCII这一点进行显示输出。

4. 流程图绘制:根据分析和题目要求绘制主函数的流程图:

双屏不显示bios bios多屏显示_寄存器_03

根据主函数流程图,可以编写主函数程序如下:

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. 当左/右窗口输满时,最上一行会上卷消失:

可见功能均正常实现。