1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
;2006年新年公开我写的471字节俄罗斯方块汇编程序源代码及详细注释!
;471 bytes GAME! By Dwing
;Only for DOS/Win9x/WinME/DosBox(注意不能直接在Win2000以上系统运行)
.model tiny
.386
.code
$shape equ 008h
$backg equ 0dbh
$up equ 72
$left equ 75
$right equ 77
$down equ 80
org 100h
start: int 10h ;设置显示模式0(40*25*16色字符模式)
push 0b800h ;字符缓冲区段=b800h
pop ds ;ds=b800h
push ds
pop es ;es=b800h
xchg ax,di ;di=0000h
mov ax,0700h+$backg ;开始画边框,ax=字符(0dbh)及属性(灰色)
mov cx,ax ;cx=数量
rep stosw ;覆盖全屏
mov ax,0e30h ;开始画数码框,ax=字符(‘0‘)及属性(黄色)
mov cl,6 ;cl=数字个数(6)
mov di,2*(40*23+17) ;di=数字显示屏幕偏移位置
rep stosw ;显示数码
xor ax,ax ;开始画中间空框,ax=空字符
mov di,2*(40*2+15) ;di=空框第一行屏幕偏移位置
@nextbl:mov cl,10 ;cl=每行块数(10)
rep stosw ;画一行空格
add di,2*(15+15) ;计算下一行屏幕偏移位置
cmp di,2*(40*22+15) ;判断是否画完最后一行(共20行)
jb short @nextbl ;没画完则循环
@rernd: in al,40h ;开始随机选择方块类型,al=时间随机值
and al,0111b ;al只取0-7
jz short @rernd ;如果是0则重新选择(只选1-7,共7种)
dec ax ;1-7变为0-6
mov bx,2*(40*2+15+4) ;方块起始屏幕偏移位置
jnz short @t ;如果不是0(长条形方块需特殊处理)则跳转
mov bx,2*(40+15+4) ;长条形方块的起始屏幕偏移位置上移一行
@t: xchg ax,bp ;bp=方块起始屏幕偏移位置
call @isok ;判断新产生的方块能否放置
jz short @goon ;能放置则跳转
@end: call @dispb ;不能放置情况:先显示方块
push cs
pop ds ;ds=当前程序段
lea dx,msg1 ;dx="GAMEOVER"信息地址
mov ah,9 ;ah=9(显示字符串)
int 21h ;显示"GAMEOVER"
@esc: in al,60h ;读键盘
dec al ;"ESC"扫描码=1
jnz short @esc ;如果没有按"ESC"则跳回继续读键盘
mov ax,3 ;ax=DOS默认显示模式(3)
int 10h ;设置显示模式3(80*25*16色字符模式)
retn ;退出
@goon: mov cl,0ffh ;新方块能放置情况:先进入延时状态
cmp cl,40h ;cl=循环等待次数
jae short @wait ;确认cl不小于40
mov cl,40h
@wait: call @dispb ;显示当前新方块
push cx ;进入等待状态
xor cx,cx ;cx=等待时间(微秒)低字
mov dx,1000 ;dx=等待时间(微秒)高字
mov ah,86h
int 15h ;等待
pop cx ;退出等待状态
@t4: mov ah,1
int 16h ;判断键盘缓冲区是否有字符
jz short @loop ;没有按键则跳出键盘处理部分
xor ax,ax
call @disp ;清除新方块的显示
int 16h ;读取键盘缓冲区字符=>ah
mov al,ah ;al=ah
cmp al,$up ;判断是否是上方向键
jnz short @k1 ;不是则跳转
push bp ;保存当前新方块的摆放形状
movzx bp,cs:[bp+bkv] ;改变新方块的摆放形状
call @isok ;判断是否能放置
jz short @loop_ ;能放置则跳出键盘处理部分
pop bp ;不能放置则恢复新方块原来形状
loop @wait ;继续下一次等待
@k1: push bx ;保存当前新方块的位置
cmp al,$left ;判断是否是左方向键
jnz short @k2 ;不是则跳转
dec bx ;新方块左移一个位置(2个字节)
dec bx
@test: call @isok ;判断是否能放置
jz short @loop_ ;能放置则跳出键盘处理部分
pop bx ;不能放置则恢复新方块原来位置
loop @wait ;继续下一次等待
@k2: cmp al,$right ;判断是否是右方向键
jnz short @k3 ;不是则跳转
inc bx ;新方块右移一个位置(2个字节)
inc bx
jmp short @test ;剩下的处理同"左方向键"
@k3: pop bx ;恢复新方块原来位置
cmp al,1 ;判断是否是ESC键
jz short @end ;如果是则跳转到退出程序段
jmp short @ok ;如果是其他按键则跳出延时状态
@loop_: pop ax ;清除保存的新方块位置
@loop: loop @wait ;继续下一次等待
@ok: push ax ;保存按键扫描码
xor ax,ax ;延时过后进入方块下落部分
call @disp ;清除新方块的显示
add bx,2*40 ;新方块下移一个位置(2个字节)
call @isok ;判断是否能放置
pop ax ;恢复按键扫描码
jnz short @down ;如果不能放置新方块则跳转
cmp al,$down ;判断是否是下方向键
jz short @ok ;如果是则继续下落
jmp short @goon ;不是则进入下一次延时
@down: sub bx,2*40 ;恢复新方块原来位置
call @dispb ;显示新方块
xor ax,ax ;进入判断是否有一行已满
mov dx,ax ;ax=dx=0
mov si,2*(40*2+15) ;si=中间空框的起始屏幕偏移位置
@nextl: mov di,si ;di=当前判断的屏幕偏移位置
mov cl,11 ;判断10次(10+1)
repnz scasw ;扫描一行
jz short @skip ;如果有空位则跳出
pusha ;进入消除一行部分
mov si,di
sub si,2*40 ;si=上一行屏幕偏移位置
mov cx,si ;cx=移动字符个数
std
rep movsb ;移下一行
cld
mov di,2*(40*2+15) ;di=中间空框最上一行的屏幕偏移位置
mov cl,10 ;一行10个方块
rep stosw ;清除最上一行
popa ;退出消除一行部分
inc dx ;分数基值+1
add dh,dl ;累计当前分数
@skip: add si,2*40 ;下一行偏移位置
cmp si,2*(40*22) ;判断是否判断完所有行
jb short @nextl ;没有则继续下一行判断
and dx,dx ;判断是否有得到当前的分数
jz short @t_ ;没有则跳过
@t00: mov di,2*(40*23+21) ;数码位屏幕偏移位置(第2位)
@t0: mov byte ptr[di],30h ;置0
dec di ;进一位(倒退2个字节长度)
dec di
cmp di,2*(40*23+20) ;判断是否进入第3位
jnz short @t000 ;如果不是则跳过
dec byte ptr cs:[@goon+1];每100分等待次数减1(加速)
@t000: inc byte ptr[di] ;当前数码位+1
cmp byte ptr[di],3ah ;判断数码位是否超过9
jz short @t0 ;如果是则跳转(进位)
dec dh ;当前分数累计值-1
jnz short @t00 ;如果分数没加完则继续累加
@t_: jmp @rernd ;继续产生下一个新方块
@isok: mov si,bp ;判断是否能放置方块子模块
shl si,2 ;si=方块形状标号*4(占4个字节)
xor ax,ax
mov dx,ax ;ax=dx=0
add si,offset bks ;si=方块形状位置描述指针
push cx ;保存cx
mov cl,4 ;cl=方块数(4)
@nextb: db 2eh ;lodsb cs: (al<=cs:[si])
lodsb ;载入方块位置描述(位置偏移)
mov di,ax
or dx,[di+bx] ;判断小方块是否冲突
loop @nextb ;继续判断下一个位置描述
pop cx ;恢复cx
retn ;返回
@dispb: mov al,$shape ;显示方块子模块,al=方块形状标号
mov ah,cs:[bp+bkc] ;ah=方块颜色值
@disp: mov si,bp
shl si,2 ;si=方块形状标号*4(占4个字节)
push cx ;保存cx
mov cl,4 ;cl=方块数(4)
@nextb_:movzx di,cs:[si+bks] ;取方块描述
mov [di+bx],ax ;显示一个小方块
inc si ;si=下一个位置描述
loop @nextb_ ;继续画下一个小方块
pop cx ;恢复cx
retn ;返回
bkc db 2 ;方块颜色值
db 9,12,13,14, 11,10
db 9,9,9, 12,13
db 11,11,11, 10,10,10
db 2
bkv db 18 ;方块形状链表
db 7,10,11,4, 12,15
db 8,9,1, 2,3
db 13,14,5, 16,17,6
db 0 ;下面是方块形状描述
bks db 40*2,41*2,42*2,43*2 ; **** 0
db 1*2,40*2,41*2,42*2 ; * ** ** ** 基本形状*7
db 1*2, 2*2,40*2,41*2 ; *** ** ** ** 1-4
db 0*2, 1*2,41*2,42*2 ;
db 0*2, 1*2,40*2,41*2 ;
db 0*2,40*2,41*2,42*2 ; * *
db 2*2,40*2,41*2,42*2 ; *** *** 5-6
db 1*2,40*2,41*2,81*2 ; * * 扩展形状*12
db 40*2,41*2,42*2,81*2 ; ** *** **
db 1*2,41*2,42*2,81*2 ; * * * * * 7-9
db 0*2,40*2,41*2,81*2 ; ** **
db 1*2,40*2,41*2,80*2 ; * * 10-11
db 1*2,41*2,80*2,81*2 ; * **
db 40*2,41*2,42*2,82*2 ; * *** *
db 1*2, 2*2,41*2,81*2 ; ** * * 12-14
db 0*2, 1*2,41*2,81*2 ;* ** *
db 40*2,41*2,42*2,80*2 ;* * *** *
db 1*2,41*2,81*2,82*2 ;* * * ** 15-17
db 2*2,42*2,82*2,122*2 ;* 18
msg1 db 9,9,‘GAMEOVER‘,9,9,‘$‘
end start
|