基于汇编语言实现打字练习软件

简介: 基于汇编语言实现打字练习软件

一 需求分析


根据以下几部分来实现打字练习:

  • 随机显示字母,字母出现的位置随机
  • 字母自动落下
  • 从键盘输入的字母与落下字母相同则该字母消失,否则字母自动接着落下
  • 按下“Esc”键则程序返回主菜单
  • 字母下落过程中按空格键暂停
  • 在主界面按“E”则程序退出

打字练习的主要功能由以上六部分组成,每一部分之间的联系都是比较紧密的。对于以上及部分,最主要的部分就是中间的四个部分,这是打字练习的重点,需要详细设计其所需要的功能。


二 程序设计


主模块是打字游戏的核心模块,主要通过各个键盘符来控制各个子模块之间的协调,完成打字游戏的运行。

子模块主要包括:初始化子模块、速度设定子模块、显示时钟子模块、开始打字子模块,显示打字结果子模块。


  • 初始化子模块包括显示初始界面菜单,初始化程序参数,判断是否进入游戏
  • 速度设定子模块包括速度选择子程序和速度设置子程序
  • 显示时钟子模块包括取系统时钟和显示两个子程序
  • 开始打字子模块包括显示分数子程序,当敲入字符与下落相符时扬声器发声子程序,字母下落子程序,产生新的字母和新的位置子程序,延时子程序。这些程序有机的组合在一起,完成整个指法练习的程序


初始化子模块包括初始化程序参数,显示初始界面菜单,判断是否进入游戏。首先初始化字母出现的位置,初始化得分和各种标志的值,然后显示初始界面菜单,通过一个比较指令和堆栈操作来判断是否进入游戏。


2.1 系统总体框架



689c934d76c33e4e89850f11b944b75d.png


2.2 系统流程图

ec0592d181311eb600ec10e816063dc8.png



三 程序实现


3.1 实现环境

  • 硬件环境:IBM-PC机,硬盘40G以上,内存256M以上,打印机等
  • 软件环境:Windows 2000 Server或Windows XPServer操作系统,TC,QE等编辑软件,MASM汇编软件

3.2 关键代码说明

Init_game macro op1,op2,op3,op4,op5
  local ns
  mov cx,00h 
  mov dh,op1 
  mov dl,op2
  ns:mov ah,02h;设置光标位置
  mov bh,00h;页号为0 
  int 10h
  push cx
  mov ah,0ah;在当前光标位置写字符
  mov al,op3;al=字符的ascii码
  mov bh,00h;bh=页号bl=字符属性
  mov cx,01h;cx=写入字符重复次数
  int 10h
  pop cx;cx=0
  inc cx;cx=cx+1
  inc op4
  cmp cx,op5
  jne ns 
endm
clear_screen macro op1,op2,op3,op4 ;清屏宏定义 cx,屏幕的左上角,dx屏幕的右下角
  mov ah,06h 
  mov al,00h
  mov bh,0eh;改变行属性的色彩,字的色彩,bh空白行的属性/07就是正常的黑底白字
  mov ch,op1
  mov cl,op2
  mov dh,op3
  mov dl,op4
  int 10h
  mov ah,02h;设置光标的位置从0000开始
  mov bh,00h
  mov dh,00h
  mov dl,00h
  int 10h
endm
menu macro op1,op2,op3 ;菜单显示宏定义 
  mov ah,02h
  mov bh,00h
  mov dh,op1
  mov dl,op2
  int 10h
  mov ah,09h
  lea dx,op3
  int 21h
endm
data segment 
  ZK db "WELCOME TO PLAY$" 
  no db "date:2013/12/27$"
  meg db "press Enter key to continue.......$"
  meg1 db "when a letter is dropping,please hit it!$"
  meg2 db "press space key to pause!$"
  meg3 db "press ESC key to return main interface!$"
  meg4 db "press letter 'E' to exit!$"
  speed dw 600d
  letters_bak db "jwmilzoeucgpravskntxhdyqfb" 
        db "iytpkwnxlsvxrmofzhgaebudjq"
        db "nwimzoexrphysfqtvdcgljukda"
  letters db 78d dup(0)
  letter_counter db 0 
  life_flag db 78 dup(0)
  position_flag db 78 dup(0) 
  present_position db 1 
data ends
stack segment para stack 'stack'
  db 64 dup(0)
stack ends
code segment
  main proc far
    assume cs:code,ds:data,ss:stack
    start: mov ax,data
    mov ds,ax
    mov letter_counter,00h 
    mov present_position,1 
    lea si,position_flag; 
    mov ah,00h
    mov cx,00h
    lea di,letters;di的偏移地址为letters 
    lea si,letters_bak;si的偏移地址为letter_bak
    mov cx,00h;cx=0
    init_letters:
    mov ah,[si];ah=j
    mov [di],ah;ah的值放到letters里面;letters_bak的值放入letters里面
    inc si;si+1
    inc di;di+1
    inc cx;cx+1
    cmp cx,78d;
    jne init_letters;不为0就到init_letters,一直循环到letters里
    mov ah,00h
    lea si,life_flag;
    mov cx,00h
    init_life_flag:
    mov [si],ah
    inc si
    inc cx
    cmp cx,78d
    jne init_life_flag
    mov cx,00h ;ch=光标开始行,cl=光标结束行 根据CX给出光标的大小 
    mov ah,01h 
    or ch,00010000b;ch>20h,光标消失,cl>20h,覆盖字符
    int 10h
    clear_screen 00d,00d,24d,79d ;清屏,0000- 2479
    Init_game 00d,00d,0ah,dl,80d ;这个四个是初始化屏幕的上下左右的框框 
    Init_game 24d,00d,0ah,dl,80d
    Init_game 00d,00d,0ah,dh,25d
    Init_game 00d,79d,0ah,dh,25d
    menu 05d,15d,ZK ;菜单信息的宏调用,这五行是在屏幕上显示提示消息
    menu 07h,15d,no 
    menu 09d,15d,meg
    menu 11d,15d,meg1
    menu 13d,15d,meg2
    menu 15d,15d,meg3
    menu 17d,15d,meg4
    put: mov ah,02h ;设置光标位置
    mov bh,00h;设置页码
    mov dh,22d;dx行列坐标
    mov dl,33d
    int 10h
    mov ah,01h ;从键盘输入任意字符并回显示,al=输入字符
    int 21h
    cmp al,0dh;是否为换行符
    je speed3;如果是换行符则跳转到speed3处
    cmp al,45h;比较是否为e
    je exit;如果为e,转到exit
    exit: mov ah,4ch
    int 21h 
    speed3: 
    mov ax,speed+12
    mov speed,ax
    jmp begin
    begin: clear_screen 01d,01d,23d,78d ;清屏宏调用 
    ; clear_screen 01d,01d,23d,78d 
    Init_game 23d,01d,03h,dl,78d;23d01d行列坐标,初始化倒数第二行的字符
    mov ah,02h 
    mov bh,00h
    mov dh,01h
    mov dl,01h
    int 10h
    mov cx,00h 
    lea si,letters ;si的偏移地址是letters
    nextletter: 
    mov ah,02h ;显示字母
    mov dl,[si] ;把letters的字符放到dl里
    int 21h ;通过dos中断的2号功能项,把字符显示出来
    inc si
    inc cx
    cmp cx,78d 
    je nextcycle;全部显示完了后,跳到nextcycle
    jmp nextletter
    from_front: 
    sub present_position,78d ;当超过78个字时的处理方式 减去78 
    jmp gobackto_si;跑到gobackto_si这来
    find_zero:
    cmp letter_counter,78d ;letter_counter有78了,初始化
    je recycle;如果有跑到recycle
    cmp present_position,78d;如果present_position等于78d,
    je from_one
    mov ah,00h
    nextsi: add present_position,01h
    inc si
    cmp [si],ah
    je gobackto_di
    cmp present_position,78d
    je from_one
    jmp nextsi
    from_one:mov present_position,01h ;present_position=01 
    jmp gobackto_si
    recycle:mov letter_counter,00h;letter_counter=0
    mov present_position,01d;present_position=01
    lea si,position_flag;si=position_flag的偏移地址
    mov cx,00h
    mov ah,00h
    clearsi: 
    mov [si],ah;position_flag地址搞成0
    inc cx
    cmp cx,78d
    je nextcycle
    inc si
    jmp clearsi
    nextcycle: 
    lea di,letters;di的偏移地址是letters[字母]
    lea si,position_flag;si的偏移地址是position_flag
    add present_position,31d;31一跳,这个你可以随便设置
    cmp present_position,78d;;超过78个字节
    ja from_front
    gobackto_si:
    add si,word ptr present_position;si=si+present_position,si向后偏移
    dec si; 要不要都无所谓,只不过,因为开始就觉定了是要31一跳,所以这里减一个1位
    mov ah,[si];把position_flag放到ah里
    cmp ah,01h;看看position_flag里面有没有标志1
    je find_zero;如果ah为1转移,否则
    gobackto_di:
    mov ah,01h
    mov [si],ah
    add di,word ptr present_position
    dec di;因为列坐标是从0开始,而字符是从1开始,所以这里是32-1
    mov dl,present_position;
    mov ah,02h 
    mov bh,00h 
    mov dh,01h
    int 10h
    mov cx,00h
    nextrow: push cx 
    mov cx,00h
    out_cycle: ; 延迟
    push cx
    mov cx,00h
    in_cycle:
    add cx,01h
    cmp cx,1000 ;
    jne in_cycle ;zf=0转到标号处执行,
    push dx
    mov ah,06h ;从键盘输入字符,al等于字符
    mov dl,0ffh
    int 21h
    pop dx 
    jz pass 
    cmp al,1bh ;如果键入ESC,则返回主菜单
    je to_start1
    cmp al," " ;如果键入SPACE,则游戏暂停
    je pause
    cmp al,[di] ;输入字母正确!则字母消失
    je disappear
    pass: pop cx
    inc cx
    cmp cx,speed
    je print
    jmp out_cycle
    pause: push dx ;暂停处理 第一次知道暂停是这样的,循环空代码
    mov ah,06h
    mov dl,0ffh
    int 21h
    pop dx
    cmp al," "
    jne pause
    jmp pass
    to_start1: ;返回主菜单
    jmp start
    print: 
    mov ah,0ah ;在当前光标位置写空格 
    mov al," "
    mov bh,00h
    mov cx,01h
    int 10h
    inc dh
    mov ah,02h ;改变光标位置
    mov bh,00h
    int 10h
    mov ah,0ah ;在当前光标位置写字母 
    mov al,[di]
    mov bh,00h
    mov cx,01h
    int 10h
    pop cx
    inc cx
    cmp cx,21d
    je print_next_letter
    jmp nextrow ;下一行
    disappear: ;击中字母后输出空格
    pop cx
    pop cx
    mov ah,0ah;在光标处按原来属性显示字符
    mov al," "
    mov bh,00h
    mov cx,01h
    int 10h
    jmp hit 
    print_next_letter: 
    lea si,life_flag
    add si,word ptr present_position
    dec si
    mov ah,0ah;在当前光标处按原有属性显示字符
    mov al," ";最倒数第二排写入字符,意思是当掉下来的字符到倒数第二行的时候,自动变成空格消失
    mov bh,00h
    mov cx,01h
    int 10h
    inc dh ;这就是到了最后一行 
    mov ah,02h;2号中断,设置文本光标位置
    mov bh,00h
    int 10h
    mov ah,0ah;把最后一行的字符变成空格
    mov al," "
    mov bh,00h
    mov cx,01h;重复输出,这里的重复输出的意思就是输入一个空格
    int 10h
    mov ah,1;把life_flag变成1,这样下次就可以不在同一个位置掉字符下来
    mov [si],ah
    hit: mov ah,02h;设置光标
    mov bh,00h
    mov dh,01h;第一行
    mov dl,present_position;下一个字符的列
    int 10h
    mov al,[di] ; 出现下一个新字母的数法
    add al,7;di+7
    cmp al,7ah;z的ascii码就是7ah,所以当al大于7ah时转移
    ja convey_letter
    mov ah,0ah;在当前光标按原有属性显示字符,al=字符
    mov bh,00h
    mov cx,01h
    int 10h
    mov [di],al
    add letter_counter,01h;统计次数
    jmp nextcycle
    convey_letter: 
    sub al,7ah
    add al,61h;al等于要显示的字符,加61表示是小写字母
    mov ah,0ah
    mov bh,00h
    mov cx,01h
    int 10h
    mov [di],al
    add letter_counter,01h
    jmp nextcycle 
    clear_screen 01,01,23,78
    mov ah,02h
    mov bh,00h
    mov dh,11d
    mov dl,20d
    int 10h
    inc dh
    inc dh
    mov ah,02h
    mov bh,00h
    int 10h
    notkey: 
    mov ah,07h
    int 21h
    cmp al,0dh
    je to_start
    cmp al,1bh
    je over
    jmp notkey
    to_start: 
    clear_screen 00,00,24,79
    jmp start
    over: clear_screen 01,01,23,78 
    mov ah,02h
    mov bh,00h
    mov dh,11d
    mov dl,15h
    int 10h
    mov ah,02h
    mov bh,00h
    mov dh,13d
    mov dl,15h
    int 10h
    mov ah,07h
    int 21h
    mov ah,07h
    int 21h
    clear_screen 00,00,24,79 
    mov ax,4c00h
    int 21h
  main endp
code ends
end start


四 运行测试

0378d270fc1ae7724ca6f396119c69da.png

307e1af93078e47f55b601e4c8a789ac.png


4f9e12a72a65b78a7abd9f308091b027.png


相关文章
|
6月前
|
小程序
续:将基于Nasm汇编的打字小游戏,移植到DOSBox
前情提要 上一篇:【编程实践】黑框框里的打字小游戏,但是汇编语言-CSDN博客 在上一篇文章中我对这个小程序进行了介绍,但由于运行环境的安装比较复杂,估计没有谁会将我的代码跑起来,可那样实在是太遗憾了。学习过汇编语言的你大概率是使用过DOSBox的,为此我献祭了2小时的宝贵生命,成功地将代码在DOS上跑起来了。 如果对具体移植过程不感兴趣,可以直接跳到运行体验部分。
100 0
|
6月前
|
存储 Unix 编译器
汇编语言----X86汇编指令
汇编语言----X86汇编指令
216 2
|
1月前
|
存储 移动开发 C语言
【ARM汇编速成】零基础入门汇编语言之指令集(三)
【ARM汇编速成】零基础入门汇编语言之指令集(三)
|
1月前
|
编译器 C语言 计算机视觉
【ARM汇编速成】零基础入门汇编语言之指令集(二)
【ARM汇编速成】零基础入门汇编语言之指令集(二)
130 0
|
6月前
|
存储 机器学习/深度学习 移动开发
汇编语言指令系列
汇编语言指令系列
618 0
几组汇编指令的比较
几组汇编指令的比较
|
4月前
|
存储 机器学习/深度学习 芯片
8086 汇编笔记(十二):int 指令 & 端口 & 直接定址表
8086 汇编笔记(十二):int 指令 & 端口 & 直接定址表
|
4月前
|
存储 算法 安全
深入理解汇编语言:基础语法和常用指令介绍
深入理解汇编语言:基础语法和常用指令介绍
|
5月前
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
8086 汇编笔记(九):call 指令 和 ret 指令
8086 汇编笔记(九):call 指令 和 ret 指令