title: 微机与汇编接口实验
tags: [微机,汇编]
categories:
- 微机
date: 2021-07-05 19:47:58
微机与汇编接口实验
暑假小学期让做一个有关微机的小项目,看来也是为以后做嵌入式开发做准备。
设计
设计思路
设想的是使用led点阵做一个小型的贪吃蛇游戏,并且可以使用红外遥控器进行操控,并且配合实时的灯光和音效展现出一种复古的街机氛围。
设计项目要抓住项目的主要矛盾,做贪吃蛇就是做贪吃蛇,专心地先做出来贪吃蛇再想别的。
首先必须可以实时的控制led点阵,led点阵可以显示红绿两种颜色,将led点阵存放在red_buffer、green_buffer中,他们都是一个8字节地缓冲区,每一个字节存放了每一列中各行地明亮情况。下面说明一下程序的生命周期:程序开始运行,主菜单显示退出程序和开始贪吃蛇游戏,选择退出程序则退出程序,选择开始贪吃蛇游戏则开始贪吃蛇游戏。进入贪吃蛇游戏之后在led点阵上显示出图像,使用遥控器控制贪吃蛇进行移动吃食物,若撞到墙则从另一边出来,若撞到自己则失败回到主菜单。在游戏过程中实时播放音效和数码管计分。
功能设计
- 程序开始时展现出一个菜单,菜单分为:
- 退出程序
- 开始贪吃蛇游戏
- 贪吃蛇程序开始运行,在led点阵上面实时地显示。
- 使用数码管显示当前得分
- 在游戏中播放音乐
- 在游戏中根据贪吃蛇地状态进行灯光、音乐、文字等方面地提示。
- 游戏失败后返回主菜单。
模块设计
- show_led
- 负责根据led点阵缓冲区中地数据向上提供可靠的led点阵显示程序。
- 入口:led点阵缓冲区
- 出口:led点阵进行刷新一次
- delay
- 根据bx地值进行时长不等的延时。
- 入口:bx
- 出口:延时一定时间
- dispmenu
- 显示程序初始的菜单
- 入口:程序启动
- 出口:显示程序初始的菜单
- show_digital_tube
- 把bx中的10进制数据显示出来(0<=bx<=9999)
- 入口:bx
- 出口:数码管显示bx中的十进制数据
- buzzer_sound
- 根据bx中的频率,蜂鸣器响
- 入口:bx
- 出口:蜂鸣器响特定的频率
- infrared_input
- 红外线输入,接受红外线的输入并将红外线输入的信息存储到infrared_input_buffer中。
- 入口:红外线输入
- 出口:更新infrared_input_buffer
- keyboard_input
- 键盘输入,接受键盘的输入并将键盘输入的信息存储到keyboard_input_buffer中。
- 入口:键盘输入
- 出口:更新keyboard_input_buffer
- process_input
- 处理输入,根据infrared_input_buffer和keyboard_input_buffer和status_buffer更新程序的数据。相当于游戏逻辑控制。
- 入口:以上三个缓冲区
- 出口:根据游戏状态和逻辑更新游戏状态
- run
- 程序的状态机,程序的声明周期控制
- 入口:status_buffer
- 出口:status_buffer
详细模块设计
code
;点亮数码管 ********
;删除simple_input
;删除红外输入的有关内容
;键盘的BIOS调用 int 16h 入口: ah=0 出口 al=ascii 先使用 ah=01判断是否有输入
;注释掉 process_input中的 call keyboard_input 和snake_count_time 中的 call keyboard_input 并删除中断程序中对call keyboard_input的注释,验证使用中断输入是否可行
;删除snake中jmp snake_flush
;新增 delay_short
;尝试可不可以在任何时候按下9回到主菜单,做不到的话就删除主菜单中的这句话
;支持子程序库的简化段源程序格式
include io.inc
.model small ; 定义程序的存储模式
.stack ; 定义堆栈段(默认是1KB空间)
.data ;数据段
;存放每一列的信息 1为亮 两种颜色交替进行闪亮 要把每一个字节的信息传送给行,来控制某一行的亮
;比如 某一个字节为 1000_0010b 则最上面一行和倒数第二行的灯亮,此时只需要对应列全为1即可控制该列的8个灯的亮暗。
;红色 颜色是通过列线进行输出的
red_buffer byte 8 dup(0)
;绿色
green_buffer byte 8 dup(0)
;infrared_input_buffer 存储红外线输入的缓冲区 每一个bit可以自由定义其含义
infrared_input_buffer word 0
;keyboard_input_buffer 存储键盘输入的缓冲区 每一个bit可以自由定义其含义
keyboard_input_buffer word 0
;Status buffer 状态缓冲区,用来存放程序运行的各种状态
status_buffer word 0
;时间缓冲区,负责提供同步信息 由中断程序每隔10ms加一
sync_time_buffer word 0
;蛇身位置 不包含蛇头 蛇的每一个位置需要两个字节,8*8*2=128B
snake_body_buffer byte 128 dup(0)
;蛇身的长度,不包括蛇头,也是当前的分数
snake_body_length word 0
;蛇头位置
snake_head_position_x byte 0
snake_head_position_y byte 0
;蛇尾位置
snake_tail_position_x byte 0
snake_tail_position_y byte 0
;食物位置
food_x byte 0
food_y byte 0
;是否吃了食物 0:没有 1:吃了
food_eaten byte 0
;碰撞与否
collision byte 0
;蛇的前近方向 2:上 0:下 2;左 1:右
snake_direction byte 0
;保存原中断程序入口
int_0b_seg word 0
int_0b_off word 0
;蛇的时间量,每经过100个全局时间量 加一,即蛇每1s移动一次
snake_time word 0
;数码管
ledtb byte 3fh,06h,5bh,4fh,66h,6dh,7dh,07h,7fh,6fh
;得分(当前长度减去初始长度)存储 分别存储四个数码管的数字
lednum byte 4 dup(0)
byte 128 dup(0)
;main_menu 主菜单
main_menu byte '---------------main menu---------------',10,13
byte '----press corrsepnding key to continue----',10,13
byte '1. exit',10,13
byte '2. play snake_gluttony ',10,13
byte '*********************************************',10,13,0
;退出语句
abort_msg byte 'exit,thanks',10,13,0
msgstatus0 byte 'msgstatus0',10,13,0
msgstatus1 byte 'msgstatus1',10,13,0
msgsetint byte 'msgsetint',10,13,0
msgscankeyboard byte 'msgscankeyboard',10,13,0
msgintirq10 byte 'msgintirq10',10,13,0
snake_gluttony_running byte 'snake_gluttony is running',10,13,0
msg_direction byte 'now, the direction is ',10,13,0
msg_snake_head_position byte 'msg_snake_head_position is ',10,13,0
msg_snake_tail_position byte 'msg_snake_tail_position is ',10,13,0
msg_snake_body_position byte 'msg_snake_body_position is ',10,13,0
msg_snake_gluttony_game_over byte 'game over!!!',10,13,0
msg_snake_gluttony_game_start byte 'snake gluttony game start, have fun!!!',10,13,0
msg_int_test byte 'msg_int_test',10,13,0
.code ;代码段
start:
mov ax,@data
mov ds,ax
;设置中断
call set_int
call run
.exit 0
;状态机 负责处理所有的状态 程序的所有状态全部由status_buffer所体现
run proc
push ax
push bx
push cx
push dx
push si
push di
;程序开始运行,清空程序的所有缓冲区
run_begin:
call clear_digital_buffer
call clear_screen
call disp_main_menu
;清空缓冲区的数据
mov red_buffer,0
mov green_buffer,0
mov status_buffer,1 ;初始状态为1 主菜单
mov infrared_input_buffer,0
mov keyboard_input_buffer,0
;显示主菜单 没有收到遥控器的命令就一直进行查询
show_main_menu:
;所有的输入已经通过每10ms的中断传入到输入缓冲区了
call process_input ;改变程序状态 下面判断当前程序状态 000为退出
;判断是否有有效输入进行下一步
;状态0 退出
mov ax,status_buffer
and al,00000111B
cmp al,0
jz run_abort
;状态1
mov ax,status_buffer
and al,00000111B
cmp al,1
jz show_main_menu
;判断是否要进入贪吃蛇游戏 就是判断当前主状态为2
mov ax,status_buffer
and al,00000111B
cmp al,2
jz run_snake_gluttony
;判断是否退出
jz run_abort
jmp show_main_menu
;运行贪吃蛇
run_snake_gluttony:
snake_gluttony_game_start:
;每一个主状态都是一个自循环,除非中断迫使其退出
call snake_gluttony
;贪吃蛇由于某种原因运行完成退出 回到主菜单
snake_gluttony_game_over:
mov si,offset msg_snake_gluttony_game_over
call dispstr
mov bx,1000
call delay
jmp run_begin
;这里可以有很多程序和贪吃蛇并列,但是我并不想要。
run_abort:
;打印出一段退出程序的语句
mov si,offset abort_msg
call dispstr
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
run endp
;显示主菜单
disp_main_menu proc
push ax
push bx
push cx
push dx
push si
push di
mov ax,@data
mov ds,ax
mov si,offset main_menu
call dispstr
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
disp_main_menu endp
;处理输入结果 逻辑控制
process_input proc
push ax
push bx
push cx
push dx
push si
push di
;判断主状态
judge_main_status:
;状态0 ,退出
mov ax,status_buffer
and al,00000111B
cmp al,0
jz main_status_0 ;跳转到主状态0 退出
;判断是否为主状态1,即在主菜单
mov ax,status_buffer
and al,00000111B
cmp al,1
jz main_status_1 ;跳转到主状态1来执行
;判断是否为主状态2,即贪吃蛇游戏
mov ax,status_buffer
and al,00000111B ;只留下低三位
cmp al,2 ;状态1
jz main_status_2 ;跳转到主状态1,即执行贪吃蛇
;还要依次遍历很多别的状态
jmp process_input_done
;状态0 ,退出
main_status_0:
mov bx,status_buffer
and bl,11111000B
mov status_buffer,bx
jmp process_input_done
;主状态1 主菜单
main_status_1:
;将程序状态变为1
mov al,00000001B
mov bx,status_buffer
and bl,11111000B
or bl,al
mov status_buffer,bx
;主菜单一件事情,等待输入,将红外和键盘输入的对于处于主菜单的程序有效的输入存储在缓冲区内
;判断红外输入。 1:退出 2:开始贪吃蛇 9:回到主菜单
;键盘输入,1-9位对应输入
mov ax,keyboard_input_buffer
and al,00000010B ;判断第一位 退出
cmp al,00000010B
jz main_status_0
;键盘输入,1-9位对应输入
mov ax,keyboard_input_buffer
and al,00000100B ;判断第二位 贪吃蛇
cmp al,00000100B
jz main_status_2
jmp process_input_done
;主状态2 贪吃蛇
main_status_2:
;将程序状态变为2
mov al,00000010B
mov bx,status_buffer
and bl,11111000B
or bl,al
mov status_buffer,bx
;没有符合条件的状态,直接退出
jmp process_input_done
process_input_done:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
process_input endp
;Infrared input 红外线输入
infrared_input proc
push ax
push bx
push cx
push dx
push si
push di
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
infrared_input endp
;simple_input
simple_input proc
push ax
push bx
push cx
push dx
push si
push di
mov dx,282h ;读入 pc
in al,dx
mov ah,al
cmp al,0
jz simple_input_end
simple_input_1:
mov al,ah
and al,00000010B
jz simple_input_2
mov bx,keyboard_input_buffer
;or ax,0000 0000 0000 0010b
or bx,0002h
mov keyboard_input_buffer,bx
simple_input_2:
mov al,ah
and al,00000100B
jz simple_input_w
mov bx,keyboard_input_buffer
;or ax,0000 0000 0000 0100b
or bx,0004h
mov keyboard_input_buffer,bx
simple_input_w:
mov al,ah
and al,00001000B
jz simple_input_a
mov bx,keyboard_input_buffer
;or ax,0000 0100 0000 0000b
or bx,0400h
mov keyboard_input_buffer,bx
simple_input_a:
mov al,ah
and al,00010000B
jz simple_input_s
mov bx,keyboard_input_buffer
;or ax,0000 0100 0000 0000b
or bx,0800h
mov keyboard_input_buffer,bx
simple_input_s:
mov al,ah
and al,00100000B
jz simple_input_d
mov bx,keyboard_input_buffer
;or ax,0000 0100 0000 0000b
or bx,1000h
mov keyboard_input_buffer,bx
simple_input_d:
mov al,ah
and al,01000000B
jz simple_input_end
mov bx,keyboard_input_buffer
;or ax,0000 0100 0000 0000b
or bx,2000h
mov keyboard_input_buffer,bx
simple_input_end:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
simple_input endp
;键盘输入
keyboard_input proc
push ax
push bx
push cx
push dx
push si
push di
;mov si,offset msgscankeyboard
;call dispstr
;扫描输入
;键盘输入 al中存放键盘输入的ascii码
scan_keynoard:
;处理键盘的W A S D 10-13位存放 将对应位置1 输入小写
;call readkey
;测试 int 16h
mov ah,01h
int 16h
jz scan_keynoard_end ;若键盘缓冲区没有则结束
mov ah,00h ;键盘缓冲区中有则读取
int 16h
scan_keynoard_w:
cmp al,'w'
jnz scan_keynoard_a
mov ax,keyboard_input_buffer
;or ax,0000 0100 0000 0000b
or ax,0400h
mov keyboard_input_buffer,ax
jmp scan_keynoard_end
scan_keynoard_a:
cmp al,'a'
jnz scan_keynoard_s
mov ax,keyboard_input_buffer
;or ax,0000 1000 0000 0000b
or ax,0800h
mov keyboard_input_buffer,ax
jmp scan_keynoard_end
scan_keynoard_s:
cmp al,'s'
jnz scan_keynoard_d
mov ax,keyboard_input_buffer
;or ax,0001 0000 0000 0000b
or ax,1000h
mov keyboard_input_buffer,ax
jmp scan_keynoard_end
scan_keynoard_d:
cmp al,'d'
jnz scan_keynoard_num
mov ax,keyboard_input_buffer
;or ax,0010 0000 0000 0000b
or ax,2000h
mov keyboard_input_buffer,ax
jmp scan_keynoard_end
;扫描num num存在1-9位对应1-9
scan_keynoard_num:
sub al,48
cmp al,1 ;小于1不行
jb scan_keynoard_end
cmp al,9 ;大于9不行
ja scan_keynoard_end
mov bx,1
mov cl,al
shl bx,cl
mov ax,keyboard_input_buffer
or ax,bx
mov keyboard_input_buffer,ax
jmp scan_keynoard_end
;将来增加新功能
scan_keynoard_end:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
keyboard_input endp
;点亮数码管 用来记录分数等信息 使用pc
show_digital_tube proc
push ax
push bx
push cx
push dx
push si
push di
call clear_digital_buffer
;先得到每一个数码管应该显示的数字
mov ax,snake_body_length
mov si,offset lednum
sub ax,2
mov cx,4
get_one_tube_num:
mov bl,10
div bl
mov [si],ah
xor ah,ah
cmp al,0
jz get_one_tube_num_over
inc si
loop get_one_tube_num
get_one_tube_num_over:
;依次显示出来
mov si,offset lednum
mov ah,00000001B
mov cx,4
show_digital_tube_one_by_one:
xor bx,bx
mov bl,[si]
inc si
mov al,ledtb[bx] ;段码
;clear tube ;清空位码 灭掉所有的灯
push ax
mov al,0
mov dx,280h
out dx,al
mov dx,282h ;清空段码
mov al,0ffh
out dx,al
pop ax
;控制数字 段码
mov dx,282h
out dx,al
;控制哪一个tube去亮 位码
mov al,ah
mov dx,280h
out dx,al
shl ah,1
;mov bx,1
;call delay_short
loop show_digital_tube_one_by_one
;clear tube ;清空位码 灭掉所有的灯
push ax
mov al,0
mov dx,280h
out dx,al
mov dx,282h ;清空段码
mov al,0ffh
out dx,al
pop ax
;验证数字算的duibudui
;mov si,offset lednum
;add si,3
;mov cx,4
disp_led_num:
;mov al,[si]
;dec si
;call dispuib
;loop disp_led_num
;call dispcrlf
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
show_digital_tube endp
;clear
clear_digital_buffer proc
push ax
push bx
push cx
push dx
push si
push di
mov si,offset lednum
mov byte ptr [si],0
mov byte ptr [si+1],0
mov byte ptr [si+2],0
mov byte ptr [si+3],0
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
clear_digital_buffer endp
;蜂鸣器响 入口参数:响的频率
buzzer_sound proc
push ax
push bx
push cx
push dx
push si
push di
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
buzzer_sound endp
;写入红色灯亮,ah为行号,al为列号 注意这里 (0,0)在左下角
make_it_red proc
push ax
push bx
push cx
push dx
push si
push di
;ah,al hang lie
mov si,offset red_buffer
mov bx,ax
xor ah,ah
add si,ax
mov ax,bx
mov cl,ah
mov al,00000001B
shl al,cl
mov bl,[si]
or bl,al
mov [si],bl
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
make_it_red endp
make_it_green proc
push ax
push bx
push cx
push dx
push si
push di
;ah,al hang lie
mov si,offset green_buffer
mov bx,ax
xor ah,ah
add si,ax
mov ax,bx
mov cl,ah
mov al,00000001B
shl al,cl
mov bl,[si]
or bl,al
mov [si],bl
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
make_it_green endp
make_it_dark proc
push ax
push bx
push cx
push dx
push si
push di
;ah,al hang lie
mov si,offset red_buffer
mov bx,ax
xor ah,ah
add si,ax
mov ax,bx
mov cl,ah
mov al,11111110B
rol al,cl
mov bl,[si]
and bl,al
mov [si],bl
;ah,al hang lie
mov si,offset green_buffer
mov bx,ax
xor ah,ah
add si,ax
mov ax,bx
mov cl,ah
mov al,11111110B
rol al,cl
mov bl,[si]
and bl,al
mov [si],bl
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
make_it_dark endp
make_all_dark proc
push ax
push bx
push cx
push dx
push si
push di
mov si,offset red_buffer
mov cx,8
make_all_dark_again:
mov byte ptr [si],0
inc si
loop make_all_dark_again
make_all_dark_end:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
make_all_dark endp
clear_snake_body proc
push ax
push bx
push cx
push dx
push si
push di
mov si,offset snake_body_buffer
mov cx,128
clear_snake_body_again:
mov byte ptr [si],0
inc si
loop clear_snake_body_again
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
clear_snake_body endp
;逐列点亮所有的led灯阵 只向上提供可靠的点亮程序,其余不管(例如延时显示、定时改变颜色等)
show_led proc
push ax
push bx
push cx
push dx
push si
push di
;写入8255方式控制字
mov dx , 283h
mov al , 80h
out dx , al
;逐列刷新 红 si控制红行 di控制绿行
mov si,offset red_buffer ;控制每一列的红8行
mov di,offset green_buffer ;控制每一列的绿8行
mov cl,10000000b ;第一列 颜色通过列线进行输出,281h输出则为第零列为红,282h输出则为第零列为绿
again_show_led_col:
;红列 是控制哪一列是什么颜色的 红:281h 绿:282h 第0列为红色
mov al,cl
mov dx,281h
out dx,al
;每一列控制哪一行的红灯亮 这里有问题 对于控制行线来讲,若要红灯亮绿灯不亮则都不会亮(但是由于时间间隔很短,所以我在实验室的效果就是看起来不太亮)
mov al,[si] ;所以这里必须解决红绿灯互相影响的问题
add si,1 ;因为时按列亮灯,共用列,所以不可能在一列中同时显示红灯和绿灯。
mov dx,280h ;要不试试逐个点亮
out dx,al ;我觉得实验室器材不太好,必不能支持如此高频率的数据传送
;所以贪吃蛇只能做到食物和蛇一个颜色,让食物闪烁吧。
;mov bx ,10000
;call delay
;绿列
;mov al,cl
;mov dx,282h
;out dx,al
;每一列控制哪一行的绿灯亮
;mov al,[di]
;add di,1
;mov dx,280h
;out dx,al
;显示完每一列进行delay 以bx为参数
;mov bx,1
;call delay_short
;关闭所有的列显示
mov al,0
mov dx,281h
out dx,al
mov dx,280h
out dx,al
shr cl,1 ;下一列
cmp cl,0
jnz again_show_led_col
;趁这段时间列显示关闭,赶快将行信息输出到数码管(复用pa)
call show_digital_tube
show_led_end:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
show_led endp
;运行贪吃蛇的程序 所有按键信息由中断进行扫描,程序只管从缓冲区中读取即可
snake_gluttony proc
push ax
push bx
push cx
push dx
push si
push di
;初始化,灭掉所有的灯 并清空蛇的身体
call make_all_dark
call clear_snake_body
mov si,offset msg_snake_gluttony_game_start
call dispstr
mov bx,1000
call delay
;初始化贪吃蛇
;设置蛇的初始位置 head(3,3) tail(3,4) dir=left
mov snake_head_position_x,3
mov snake_head_position_y,3
mov ah,3
mov al,3
call make_it_red
;初始化一节身体 这里有问题
mov si,offset snake_body_buffer
mov byte ptr [si],03H ;横坐标 列 al
mov byte ptr [si+1],04H ;纵坐标 行 ah
mov ah,4
mov al,3
call make_it_red
mov byte ptr [si+2],03H
mov byte ptr [si+3],05H
mov snake_tail_position_x,3
mov snake_tail_position_y,5
mov ah,5
mov al,3
call make_it_red
;初始化一块食物
mov food_x,1
mov food_y,5
mov ah,5
mov al,1
call make_it_red
;初始化方向为left 蛇身长度为1,此时蛇身只有一个尾巴
mov snake_direction,2
mov snake_body_length,2
mov collision,0
snake_gluttony_begin:
snake_count_time:
;蛇的身体信息已经全部适配为灯亮的信息
call show_led
mov ax,sync_time_buffer
mov bl,10
div bl ;得到余数在ah中 商在al
xor ah,ah
cmp ax,[snake_time] ;只要和之前的时间不相等就刷新
jnz snake_flush
;为防止因为溢出而造成的错误判断
mov [snake_time],ax
jmp snake_count_time
;snake的时间到了,可以刷新 ;现在要解决的问题是蛇不会移动
snake_flush:
;ax中存放当前贪吃蛇时间,放入snake_time
mov [snake_time],ax
;判断按键,进行贪吃蛇的逻辑操作 ;蛇的前近方向 3:上 0:下 2:左 1:右
mov ax,keyboard_input_buffer
;处理键盘的W A S D 改变方向 10-13位存放 ;用开关连入 8255的pc 进行输入
mov cl,10
shr ax,cl
and al,00001111B
cmp al,00000001B
jz snake_fulsh_process_key_w
cmp al,00000010B
jz snake_fulsh_process_key_a
cmp al,00000100B
jz snake_fulsh_process_key_s
cmp al,00001000B
jz snake_fulsh_process_key_d
;没有任何按键按下则直接进行处理
jmp snake_flush_process
;处理按键w
snake_fulsh_process_key_w:
mov al,snake_direction
cmp al,0 ;当前方向不为下时改变方向为上
jz snake_flush_process
mov snake_direction,3
jmp snake_flush_process
;处理按键s
snake_fulsh_process_key_s:
mov al,snake_direction
cmp al,3 ;当前方向不为上时改变方向为下
jz snake_flush_process
mov snake_direction,0
jmp snake_flush_process
;处理按键a
snake_fulsh_process_key_a:
mov al,snake_direction
cmp al,1 ;当前方向不为右时改变方向为左
jz snake_flush_process
mov snake_direction,2
jmp snake_flush_process
;处理按键d
snake_fulsh_process_key_d:
mov al,snake_direction
cmp al,2 ;当前方向不为左时改变方向为右
jz snake_flush_process
mov snake_direction,1
jmp snake_flush_process
;按键处理完了,处理贪吃蛇的位置
snake_flush_process:
;现在已经得到了贪吃蛇的方向。输出检查是否成功
;将按键信息清零
snake_flush_process_clear_key:
mov ax,keyboard_input_buffer
;处理键盘的W A S D 都置为零
and ax,1100001111111111B
mov keyboard_input_buffer,ax
;位置刷新,根据上一次是否吃了食物决定身体更新策略
;若上一次刷新吃到了食物,则这一次蛇头变为蛇尾的一部分,然后蛇头向前走一步,若没有吃到,则从最后一节开始,每一节的位置时前一个的位置,第一个位置更新为蛇头的位置。
move_snake_body:
mov ax,@data
mov ds,ax
;每个位置需要两个字节
mov si,offset snake_body_buffer
mov di,snake_body_length ;其实有没有吃都从最后一节向前更新即可,无非是length的不同
dec di ;减一 方便指向每一节
shl di,1
add di,si ;指向最后一节 目标地址
mov si,di
sub si,2 ;源地址
;更新蛇的身体的每一节,第一节不更新
move_snake_body_every_section:
;先将旧的尾巴灭掉
mov ah,snake_tail_position_y
mov al,snake_tail_position_x
call make_it_dark
;更新蛇的尾巴信息 si所指向的
mov al,[si]
mov snake_tail_position_x,al
mov al,[si+1]
mov snake_tail_position_y,al
;串传送的个数 (word)第一节不传送
mov cx,snake_body_length
dec cx
mov ax,@data
mov ds,ax
mov es,ax
std ;反向传送
rep movsw
;更新第一节,将头的位置赋予即可
move_snake_body_first_section:
;高位存放纵坐标
mov si,offset snake_body_buffer
mov ah,snake_head_position_y
mov al,snake_head_position_x
mov [si],ax
;位置刷新,将贪吃蛇头朝对应方向移动一格
move_snake_head:
mov cl,snake_direction
cmp cl,3 ;上
jz move_snake_head_up
cmp cl,0 ;下
jz move_snake_head_down
cmp cl,2 ;左
jz move_snake_head_left
cmp cl,1 ;右
jz move_snake_head_right
;不可能哪个方向都不对
;jmp move_snake_head_direction_error
jmp move_snake_head_left
;下
move_snake_head_down:
mov ah,snake_head_position_y
sub ah,1
jnc snake_judge_conflict
mov ah,7 ;小于0,则从下面钻出来
jmp snake_judge_conflict
;上
move_snake_head_up:
mov ah,snake_head_position_y
add ah,1
cmp ah,8
jnz snake_judge_conflict
mov ah,0 ;等于8,超界,则从上面钻出来
jmp snake_judge_conflict
;左
move_snake_head_left:
mov al,snake_head_position_x
sub al,1
jnc snake_judge_conflict
mov al,7 ;小于0,则从下右面钻出来
jmp snake_judge_conflict
;右
move_snake_head_right:
mov al,snake_head_position_x
add al,1
cmp al,8
jnz snake_judge_conflict
mov al,0 ;等于8 ,则从左面钻出来
jmp snake_judge_conflict
;判断是否冲突
snake_judge_conflict:
;此时 蛇头坐标为(ah,al)(行列) ah中存放纵坐标,代表行值,al存放横坐标,代表列值
mov snake_head_position_y,ah ;行,纵坐标
mov snake_head_position_x,al
call show_snake_info
;首先判断是否蛇头和食物重合,若没有重合则蛇尾向前一步走,若重合则蛇尾不动,将蛇的身体信息全部向后移动一位
;清空上一次吃到食物的信息
;将当前食物位置存放在(bh,bl)中和(ah,al)比较
mov bh,food_y ;行
mov bl,food_x ;列
mov food_eaten,0
mov ah,snake_head_position_y
mov al,snake_head_position_x
cmp al,bl
jnz snake_judge_conflict_not_eat
cmp ah,bh
jnz snake_judge_conflict_not_eat
;下面时蛇吃了食物
mov food_eaten,1
add snake_body_length,1
;生成新的食物 位置为(7-当前蛇头x,7-当前蛇尾y)
snake_create_new_food:
;原来的食物不用灭,因为现在蛇头在这里
mov ah,food_y
mov al,food_x
call make_it_dark
mov ax,7
mov bl,snake_head_position_x
sub ax,bx
mov food_x,al
mov ax,7
mov bl,snake_tail_position_y
sub ax,bx
mov food_y,al
;蛇这一步没有吃到食物
snake_judge_conflict_not_eat:
mov food_eaten,0
;判断是否碰撞到自己 头坐标(al,ah) 身体坐标(bl,bh)
snake_judge_collision:
mov ah,snake_head_position_y
mov al,snake_head_position_x
mov si,offset snake_body_buffer
mov cx,snake_body_length
snake_judge_collision_again:
mov bl,[si]
mov bh,[si+1]
add si,2
cmp ah,bh
jnz snake_judge_collision_again_not_collision
cmp al,bl
jnz snake_judge_collision_again_not_collision
;碰撞了
snake_judge_collision_again_collision:
mov collision,1
jmp snake_gluttony_end
snake_judge_collision_again_not_collision:
loop snake_judge_collision_again
;在led点阵中点亮蛇阵 使用红色
snake_lighten_led_red:
;首先点亮食物
;写入红色灯亮,ah为行号,al为列号 注意这里 (0,0)在左下角 即ah为y al为x
mov ah,food_y
mov al,food_x
call make_it_red
;点亮头
mov ah,snake_head_position_y
mov al,snake_head_position_x
call make_it_red
;点亮身体 记得在更新身体的时候灭掉尾巴的灯
mov si,offset snake_body_buffer
mov cx,snake_body_length
dec cx
snake_lighten_led_red_body:
mov al,[si]
mov ah,[si+1]
call make_it_red
add si,2
loop snake_lighten_led_red_body
jmp snake_gluttony_begin
snake_gluttony_end:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
snake_gluttony endp
;延时 bx为传入的参数
delay proc
push ax
push bx
push cx
push dx
push si
push di
again1_delay:
mov cx , 0
again2_delay:
nop
loop again2_delay
dec bx
jnz again1_delay
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
delay endp
;短延时 bx为传入的参数 为了增加亮度
delay_short proc
push ax
push bx
push cx
push dx
push si
push di
again1_delay_short:
mov cx , 0ffh
again2_delay_short:
nop
loop again2_delay_short
dec bx
jnz again1_delay_short
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
delay_short endp
;8253 设置方式控制字为 00110100 计时器0先低后高方式二二进制 初值为 1000 连在1MHZ上 每10ms进行一次中断 out口接在主板irq上(主板irq已经固定在了irq10)
set_int proc
push ax
push bx
push cx
push dx
push si
push di
cli
;mov si,offset msgsetint
;call dispstr
;设置8253
mov dx,28bh
mov ax,00110110B
out dx,ax
mov dx,288h
mov ax,1000
out dx,al
mov al,ah
out dx,al
;设置中断处理程序 irq10使用0B号中断
mov ax,3572h
int 21h
mov int_0b_seg,es
mov int_0b_off,bx
push ds
mov ax,seg int_irq10
mov ds,ax
mov dx,offset int_irq10
mov ax,2572h
int 21h
pop ds
;imr
in al,0a1h
and al,0fbh
out 0a1h,al
;EOI
mov al,20h
out 0a0h,al
out 20h,al
sti
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
set_int endp
;中断处理程序 每一次中断时10ms,这10ms用来扫描输入和同步时间量
int_irq10 proc
push ax
push bx
push cx
push dx
push si
push di
;mov si,offset msgintirq10
;call dispstr
;设置时间量
add sync_time_buffer,1
;键盘输入 键盘可以用的话就用键盘吧
call keyboard_input
;红外输入
;call infrared_input
;EOI
mov al,20h
out 0a0h,al
out 20h,al
int_irq10_end:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
iret
int_irq10 endp
dispstr proc
push ax
push bx
push cx
push dx
push si
push di
dps1:
mov al,[si]
cmp al,0
jz dps2
mov bx,0
mov ah,0eh
int 10h
inc si
jmp dps1
dps2:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
dispstr endp
show_snake_info proc
push ax
push bx
push cx
push dx
push si
push di
mov si,offset msg_direction
call dispstr
mov al,snake_direction
call dispuib
call dispcrlf
mov si,offset msg_snake_body_position
call dispstr
;头
mov al,snake_head_position_x
call dispuib
mov al,snake_head_position_y
call dispuib
;身
mov si,offset snake_body_buffer
mov ax,snake_body_length
mov cl,1
shl ax,cl
mov cx,ax
snake_show_body_again:
mov al,[si]
inc si
call dispuib
loop snake_show_body_again
call dispcrlf
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
show_snake_info endp
;清屏
clear_screen proc
push ax
push bx
push cx
push dx
push si
push di
mov ax,0b800h
mov es,ax
mov cx,4000
xor si,si
clear_screen_again:
mov byte ptr es:[si],0
inc si
loop clear_screen_again
;入口参数:AH=02H
;BH=显示页码
;DH=行(Y坐标)
;DL= 列(X坐标)
;设置光标位置
mov ax,02h
mov bh,0
mov dx,0
int 10h
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
clear_screen endp
end start