微机与汇编接口实验

本文介绍了一个基于微机与汇编的接口实验项目,设计并实现了一个使用LED点阵和红外遥控器操控的贪吃蛇游戏。游戏在LED点阵上实时显示,具有音效和计分功能,玩家可通过红外遥控器控制贪吃蛇移动。文章详细阐述了程序的生命周期、模块设计和状态机控制,包括输入处理、显示逻辑和游戏逻辑。
摘要由CSDN通过智能技术生成

title: 微机与汇编接口实验
tags: [微机,汇编]
categories:

  • 微机
    date: 2021-07-05 19:47:58

微机与汇编接口实验

暑假小学期让做一个有关微机的小项目,看来也是为以后做嵌入式开发做准备。

设计

设计思路

设想的是使用led点阵做一个小型的贪吃蛇游戏,并且可以使用红外遥控器进行操控,并且配合实时的灯光和音效展现出一种复古的街机氛围。
设计项目要抓住项目的主要矛盾,做贪吃蛇就是做贪吃蛇,专心地先做出来贪吃蛇再想别的。

首先必须可以实时的控制led点阵,led点阵可以显示红绿两种颜色,将led点阵存放在red_buffer、green_buffer中,他们都是一个8字节地缓冲区,每一个字节存放了每一列中各行地明亮情况。下面说明一下程序的生命周期:程序开始运行,主菜单显示退出程序和开始贪吃蛇游戏,选择退出程序则退出程序,选择开始贪吃蛇游戏则开始贪吃蛇游戏。进入贪吃蛇游戏之后在led点阵上显示出图像,使用遥控器控制贪吃蛇进行移动吃食物,若撞到墙则从另一边出来,若撞到自己则失败回到主菜单。在游戏过程中实时播放音效和数码管计分。

功能设计

  1. 程序开始时展现出一个菜单,菜单分为:
    1. 退出程序
    2. 开始贪吃蛇游戏
  2. 贪吃蛇程序开始运行,在led点阵上面实时地显示。
  3. 使用数码管显示当前得分
  4. 在游戏中播放音乐
  5. 在游戏中根据贪吃蛇地状态进行灯光、音乐、文字等方面地提示。
  6. 游戏失败后返回主菜单。

模块设计

  1. show_led
    1. 负责根据led点阵缓冲区中地数据向上提供可靠的led点阵显示程序。
    2. 入口:led点阵缓冲区
    3. 出口:led点阵进行刷新一次
  2. delay
    1. 根据bx地值进行时长不等的延时。
    2. 入口:bx
    3. 出口:延时一定时间
  3. dispmenu
    1. 显示程序初始的菜单
    2. 入口:程序启动
    3. 出口:显示程序初始的菜单
  4. show_digital_tube
    1. 把bx中的10进制数据显示出来(0<=bx<=9999)
    2. 入口:bx
    3. 出口:数码管显示bx中的十进制数据
  5. buzzer_sound
    1. 根据bx中的频率,蜂鸣器响
    2. 入口:bx
    3. 出口:蜂鸣器响特定的频率
  6. infrared_input
    1. 红外线输入,接受红外线的输入并将红外线输入的信息存储到infrared_input_buffer中。
    2. 入口:红外线输入
    3. 出口:更新infrared_input_buffer
  7. keyboard_input
    1. 键盘输入,接受键盘的输入并将键盘输入的信息存储到keyboard_input_buffer中。
    2. 入口:键盘输入
    3. 出口:更新keyboard_input_buffer
  8. process_input
    1. 处理输入,根据infrared_input_buffer和keyboard_input_buffer和status_buffer更新程序的数据。相当于游戏逻辑控制。
    2. 入口:以上三个缓冲区
    3. 出口:根据游戏状态和逻辑更新游戏状态
  9. run
    1. 程序的状态机,程序的声明周期控制
    2. 入口:status_buffer
    3. 出口: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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值