汇编语言贪吃蛇、俄罗斯方块双任务设计实现详解(三)——俄罗斯方块详细设计

详细设计:

3.俄罗斯方块详细设计:下图所示,左下角为主程序的调用情况,左边上侧和右侧为俄罗斯方块相关数据在teris段中的存储情况

 下图所示,左侧为俄罗斯方块具体子程序,右侧为俄罗斯方块程序运行流程图。

 

 主程序中所调用的to_play_teris较为复杂,具体代码如下:

;功能:运行俄罗斯方块
;参数:无
;返回:无
to_play_teris:
push ax
push ds
	
begin_play_teris:
;生成一个俄罗斯方块
call make_a_teris

;检测是否已到达最顶部,若是,则游戏结束
mov ax,0100h
call try_to_move
cmp al,2ah
je to_game_over_teris
	
;显示当前俄罗斯方块
call show_this_teris
;当前俄罗斯方块下落,直到不能下落
call teris_drop
;当前俄罗斯方块落定后,检测并消除可以消除的行
call check_and_dispel_action
jmp begin_play_teris
	
to_game_over_teris:
;俄罗斯方块状态置位1
mov ax,teris
mov ds,ax
mov word ptr ds:[2],1
	
pop ds
pop ax
ret

子程序revolve_teris和reset_lower_left_quarter,配合实现俄罗斯方块的旋转。旋转的思路为:以俄罗斯方块的行的最大值、列的最小值为坐标的块单元为中心,此块单元也就是俄罗斯方块的左下角单元。以此单元为中心顺时针旋转90度。

首先介绍reset_lower_left_quarter,其功能为重置俄罗斯方块的左下角坐标,实现方法为,寻找teris段中四个方块坐标值中的行的最大值、列的最小值,得到的便是左下角坐标,用该值去更改teris段中左下角坐标数据,完成重置旋转后的新的俄罗斯方块的左下角坐标。      

revolve_teris程序的实现方法为:俄罗斯方块中每个块的坐标值减去俄罗斯方块左下角的坐标值,对得到的坐标值取绝对值,交换行与列值,然后加上俄罗斯方块左下角的坐标值,就是该块顺势针旋转90度后的坐标值,原理图如下:

 具体代码如下:

	;功能:旋转当前俄罗斯方块
	;参数:无
	;返回:无
	revolve_teris:
	push ax
	push ds
	push cx
	push bx
	push dx
	push si
	
	mov ax,teris
	mov ds,ax
	push ds:[4]
	push ds:[6]
	push ds:[8]
	push ds:[10]
	
	mov si,4
	mov cx,4
	revolue_check:
	mov ax,teris
	mov ds,ax
	mov ax,ds:[12]
	cmp word ptr ds:[si],ax
	jnb greater_than
	
	;行、列值小于左下角坐标时
	mov dx,ds:[si]
	sub ah,dh
	sub dl,al
	mov al,dl
	
	;坐标差值的绝对值旋转
	mov dl,ah
	mov ah,al
	mov al,dl
	add ax,ds:[12]
	mov word ptr ds:[si],ax
	
	mov ax,ds:[si]
	call get_offset_by_column_value
	cmp byte ptr ds:[bx],2ah
	je give_up_revolve
	jmp to_loop_revolue_check
	
	;行、列值大于或等于左下角坐标时
	greater_than:
	mov dx,ds:[si]
	sub dx,ax
	mov ax,dx
	
	mov dl,ah
	mov ah,al
	mov al,dl
	add ax,ds:[12]
	mov word ptr ds:[si],ax
	
	mov ax,ds:[si]
	call get_offset_by_column_value
	cmp byte ptr ds:[bx],2ah
	je give_up_revolve
	jmp to_loop_revolue_check
	
	to_loop_revolue_check:
	add si,2
	loop revolue_check
	jmp revolve_succeed
	
	give_up_revolve:
	mov ax,teris
	mov ds,ax
	pop ds:[10]
	pop ds:[8]
	pop ds:[6]
	pop ds:[4]
	jmp revolve_teris_end
	
	revolve_succeed:
	pop ax
	pop ax
	pop ax
	pop ax
	jmp revolve_teris_end
	
	revolve_teris_end:
	call reset_lower_left_quarter
	pop si
	pop dx
	pop bx
	pop cx
	pop ds
	pop ax
	ret
	
	;功能:重置左下角坐标
	;参数:无
	;返回:无
	reset_lower_left_quarter:
	push ax
	push ds
	push bx
	push cx
	
	mov ax,teris
	mov ds,ax
	mov bx,4
	mov ax,ds:[bx]
	add bx,2
	
	mov cx,3
	look_for_lower_left_quarter:
	
	look_for_col:
	cmp ds:[bx],al
	jnb look_for_row
	mov al,ds:[bx]
	
	look_for_row:
	cmp ds:[bx+1],ah
	jna go_on_look_for
	mov ah,ds:[bx+1]
	
	go_on_look_for:
	add bx,2
	loop look_for_lower_left_quarter
	
	reset_lower_left_quarter_end:
	mov ds:[12],ax
	pop cx
	pop bx
	pop ds
	pop ax
	ret

子程序make_a_teris功能为生成俄罗斯方块,将每个小块坐标存储到teris:[4]开始的四个字单元中,每个俄罗斯方块设定均有四个小块。每中形态俄罗斯方块均可以由下图中的五种旋转变化而来。具体如下图所示:

具体代码如下:

;功能:生成一个俄罗斯方块,将每个小块坐标存储到teris:[4]开始的四个字单元中,每个俄罗斯方块均有四个小块
;参数:无(根据teris:[0]字单元中的俄罗斯方块个数)
;返回:
make_a_teris:
push ax
push ds
push bx
push dx
	
mov ax,teris
mov ds,ax
mov bx,4
mov word ptr ds:[bx],0139h
mov word ptr ds:[bx+2],013ah
	
mov ax,ds:[0]
mov dl,5
div dl
mov al,ah
mov ah,0
	
cmp ax,0
jne block_1
mov word ptr ds:[bx+4],013bh
mov word ptr ds:[bx+6],013ch
mov word ptr ds:[bx+8],0139h
jmp near ptr make_a_teris_end
	
block_1:
cmp ax,1
jne block_2
mov word ptr ds:[bx+4],013bh
mov word ptr ds:[bx+6],023bh
mov word ptr ds:[bx+8],0239h
jmp near ptr make_a_teris_end
	
block_2:
cmp ax,2
jne block_3
mov word ptr ds:[bx+4],013bh
mov word ptr ds:[bx+6],023ah
mov word ptr ds:[bx+8],0239h
jmp near ptr make_a_teris_end
	
block_3:
cmp ax,3
jne block_4
mov word ptr ds:[bx+4],013bh
mov word ptr ds:[bx+6],0239h
mov word ptr ds:[bx+8],0239h
jmp near ptr make_a_teris_end
	
block_4:
mov word ptr ds:[bx+4],023ah
mov word ptr ds:[bx+6],0239h
mov word ptr ds:[bx+8],0239h
jmp near ptr make_a_teris_end
	
make_a_teris_end:
inc word ptr ds:[0]
	
pop dx
pop bx
pop ds
pop ax
ret

子程序try_to_move功能为:尝试向指定方向移动,检测在该方向上下一位置是否已被占用,若已被占用则表示无法再向下一位置移动。其实现方式为:根据参数(ax)=在某一方向移动时坐标需要偏移的量;(ax)=0100h表示下移,(ax)=00ffh表示左移,(ax)=0001h表示右移,对teris中四个方块的行列值向指定方向移动一个单元,若有任何一个方块行列值更改后的位置已被占用,就表示该位置已被占用。程序返回:(al)=2ah表示该位置已被占用。具体代码如下:

;功能:尝试向指定方向移动,检测在该方向上下一位置是否已被占用
;参数:(ax)=在某一方向移动时坐标需要偏移的量;(ax)=0100h表示下移,(ax)=00ffh表示左移,(ax)=0001h表示右移;
;返回:(al)=2ah表示该位置已被占用
try_to_move:
push ds
push bx
push cx
push dx
	
call clean_this_teris
	
mov dx,teris
mov ds,dx
	
is_down_move:
cmp ax,0100h
jne is_left_move
jmp to_try
	
is_left_move:
cmp ax,00ffh
jne is_right_move
jmp to_try
	
is_right_move:
cmp ax,0001h
jne try_to_move_end
	
to_try:
mov dx,ax
mov bx,4
mov cx,4
begin_to_try:
mov ax,dx
add al,ds:[bx]
add ah,ds:[bx+1]
call get_value_by_column_value
cmp al,2ah
je try_to_move_end
	
add bx,2
loop begin_to_try
	
try_to_move_end:
call show_this_teris
pop dx
pop cx
pop bx
pop ds
ret

子程序teris_drop功能为俄罗斯方块下落一行,其实现为:延时一秒后,根据teris段中四个块的坐标检测当前俄罗斯方块下一行是否已经被占用,若是则表示此俄罗斯方块已经无法再向下移动,程序退出,去生成一个新的俄罗斯方块。若当前俄罗斯方块下一行没有被占用,则清除此俄罗斯方块在其屏幕上的显示,将teris段中四个块的坐标行值加一,然后显示行值被更改后的俄罗斯方块。此后又回到延时一秒处,如此向下循环。具体代码如下:

	;功能:俄罗斯方块下落
	;参数:无
	;返回:无
	teris_drop:
	push ax
	push ds
	push bx
	push cx
	
	drop_start:
	
	call delayed_one_second
	
	mov ax,0100h
	call try_to_move
	cmp al,2ah
	je teris_drop_end
	
	;清空正在下落的俄罗斯方块上一秒时的位置
	call clean_this_teris

	;整个俄罗斯方块坐标数据向下移一格
	mov ax,teris
	mov ds,ax
	mov bx,4
	mov cx,5
	to_drop_teris:
	add word ptr ds:[bx],0100h
	add bx,2
	loop to_drop_teris

	;显示坐标更改后的俄罗斯方块
	call show_this_teris
	
	jmp drop_start
	
	teris_drop_end:
	pop cx
	pop bx
	pop ds
	pop ax
	ret

子程序check_and_dispel_action功能为从最底部检测俄罗斯方块的每一行,查看是否有一行全为“*”,若没有则表示当前俄罗斯方块落定后,没有可以消除的行,接着去执行make_a_teris生成一个新的俄罗斯方块,向下执行。若有某一行全为“*”,则需要消除本行,实现方法为,将本行以上的所有行向下移动一行,顶部一行置位空,实现了消除此行,然后再去从最底部检测消除后并下落后新的窗口是否有一行全为“*”。具体代码如下:

        ;功能:俄罗斯方块检测并消除动作
	;参数:无
	;返回:无
	check_and_dispel_action:
	push ax
	push ds
	push cx
	
	mov ax,teris
	mov ds,ax
	
	begin_to_check:
	mov ah,23
	mov cx,23
	check_all_effective_row:
	call get_asterisk_num_in_a_row
	cmp al,38
	jne is_equals_zero
	
	;若当前行全为*,则分数加38,消除这一行,从底部重新开始检测
	add word ptr ds:[14],38
	call dispel_action
	
	;消除完一行后,从最底部重新扫描检测
	jmp begin_to_check
	
	;若当前行*的个数为0,则终止扫描
	is_equals_zero:
	cmp al,0
	jne to_check_all_effective_row
	jmp check_and_dispel_action_end
	
	;若当前行*的个数为大于零小于38,则去准备扫描其上面的一行
	to_check_all_effective_row:
	dec ah
	loop check_all_effective_row
	
	check_and_dispel_action_end:
	pop cx
	pop ds
	pop ax
	ret
	
	;功能:俄罗斯方块某一行的消除动作
	;参数:(ah)=需要消除的那一行的行数
	;返回:无
	dispel_action:
	push ax
	push ds
	push bx
	push cx
	push dx
	
	mov cl,ah
	dec cl
	mov ch,0
	
	to_dispel:
	call get_asterisk_num_in_a_row
	cmp al,0
	je dispel_action_end
	
	mov al,41
	push cx
	mov cx,38
	
	;消除一行
	dispel_a_row:
	dec ah
	call get_offset_by_column_value
	mov dx,ds:[bx]
	
	inc ah
	call get_offset_by_column_value
	mov ds:[bx],dx
	
	inc al
	loop dispel_a_row
	
	pop cx
	dec ah
	loop to_dispel
	
	dispel_action_end:
	pop dx
	pop cx
	pop bx
	pop ds
	pop ax
	ret
	
	;功能:得到俄罗斯方块区域某一行*的个数
	;参数:(ah)=行数(取值:0-24)
	;返回:(al)=该行中*的个数
	get_asterisk_num_in_a_row:
	push ds
	push bx
	push cx
	
	mov al,29h
	call get_offset_by_column_value
	
	mov cx,38
	mov al,0
	is_asterisk_check:
	cmp byte ptr ds:[bx],2ah
	jne to_is_asterisk_check
	inc al
	
	to_is_asterisk_check:
	add bx,2
	loop is_asterisk_check
	
	pop cx
	pop bx
	pop ds
	ret

如下图,通过按→键使该方块一直向右移动,直到撞到边框后,按右键再无响应,此时按↓键使方块加速下落,到达底部后,无法再下落,且没有可以消除的行,则去生成新的俄罗斯方块来下落:

如下两张图:蓝色俄罗斯方块掉落到底部时,消除最底下的两行:

在上图中的绿色方块下落时按下↑键实现该方块的旋转,如下图:

上一篇:汇编语言贪吃蛇、俄罗斯方块双任务设计实现详解(二)——贪吃蛇详细设计

下一篇:汇编语言贪吃蛇、俄罗斯方块双任务设计实现详解(四)——双任务调度与键盘中断(完结)

完整代码:https://download.csdn.net/download/gduyt_gduyt/10924302

  • 1
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值