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