aboutsummaryrefslogtreecommitdiff
path: root/examples/Tetris.dasm16
blob: fc7cb21f80f8dffdd20861cc67780be5ac20261c (plain)
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
; retrieve some entropy...
  SET A, 0x8000 + 32 * 1 + 2
  SET B, entropy_str1
  JSR print
  SET A, 0x8000 + 32 * 2 + 2
  SET B, entropy_str2
  JSR print

  SET I, 0
:cyc4
  ADD I, 1
  JSR next_key
  IFE A, 0
    SET PC, cyc4
  MUL [rand_seed], 36313
  ADD [rand_seed], A
  MUL [rand_seed], 36313
  ADD [rand_seed], I
  
  SET A, 0x8000              ; clear screen
:cyc3
  SET [A], 0
  ADD A, 1
  IFG 0x8000 + 32*12, A
    SET PC, cyc3

; init
  SET A, 0x8000
  SET B, 0
:cyc1
  SET [A], 0x0700
  ADD A, 1
  ADD B, 1
  IFG 10, B
    SET PC, jmp1
  SET B, 0
  ADD A, 32 - 10
:jmp1
  IFG 0x8000 + 32*12, A
    SET PC, cyc1

  SET C, 0xf700
  SET A, 0x8000 + 32 * 1 + 1 ; (1, 1)
  SET B, score_str
  JSR print
  SET A, 0x8000 + 32 * 3 + 1 ; (1, 3)
  SET B, level_str
  JSR print
  SET A, 0x8000 + 32 * 5 + 1 ; (1, 5)
  SET B, next_str
  JSR print
  SET A, 0x8000
:cyc2
  SET [A + 10], 0x0100
  SET [A + 31], 0x0100
  ADD A, 32
  IFG 0x8000 + 32*12, A
    SET PC, cyc2

  JSR update_score
  JSR update_level
  JSR select_next_piece

SET PC, main_loop

;;;;;;;;;;;;;;;
; SUBROUTINES ;
;;;;;;;;;;;;;;;

:print                    ; takes coord at A, string to output at B, style at C
  IFE [B], 0
    SET PC, POP
  SET [A], [B]
  BOR [A], C
  ADD A, 1
  ADD B, 1
  SET PC, print

:next_rand                ; takes no arguments, returns random word in A
  MUL [rand_seed], 10061
  ADD [rand_seed], 1
  SET A, [rand_seed]
  SET PC, POP
:rand_seed
  DAT 0xC00F              ; TODO: collect entropy at start to randomize game

:next_key                 ; reads next key to A from keyboard (based on Notch's code)
  SET B, [keypointer]
  ADD B, 0x9000
  SET A, [B]
  IFE A, 0
    SET PC, POP
  SET [B], 0
  ADD [keypointer], 1
  AND [keypointer], 15
  SET PC, POP
:keypointer
  DAT 0

:select_next_piece        ; selects next piece
  JSR next_rand
  MOD A, 7
  SET [cur_piece], [next_piece]
  SET [next_piece], A
  SET [piece_pos], 0x8000 + 17
  SET PC, POP
:cur_piece
  DAT 0
:cur_rot
  DAT 0
:next_piece
  DAT 0
:piece_pos
  DAT 0x8000 + 17

:update_score             ; display current score
  SET A, [score]
  SET C, 0x8000 + 32*2 + 8
:score_cyc1
  SET B, A
  MOD B, 10
  BOR B, 0xe730
  SET [C], B
  SUB C, 1
  DIV A, 10
  IFG A, 0
    SET PC, score_cyc1
  SET PC, POP

:update_level             ; display current level
  SET A, [level]
  SET C, 0x8000 + 32*4 + 8
:level_cyc1
  SET B, A
  MOD B, 10
  BOR B, 0xe730
  SET [C], B
  SUB C, 1
  DIV A, 10
  IFG A, 0
    SET PC, level_cyc1
  SET PC, POP

:show_cur_piece           ; display/clear/check current piece (A=0 - clear, A=1 - display, A=2 - check), if A=2 doesn't actually place anything, return B=1 is position is valid, 0 otherwise
  SET X, [piece_pos]      ; place block at [X] (display)
  SET Y, [cur_piece]      ; ...from [Y] (pieces array)
  SHL Y, 2
  BOR Y, [cur_rot]
  SHL Y, 4
  ADD Y, pieces
  SET I, 0                ; index
:piece_cyc1
  SET B, [Y]
  IFE B, 0
    SET PC, piece_jmp1
  IFG 2, A
    SET PC, piece_jmp2
    IFG X, 0x8000 + 32*12
      ADD PC, 3
    IFE [X], 0
      SET PC, piece_jmp1
    SET B, 0
    SET PC, POP
:piece_jmp2
  IFE A, 0
    SET B, 0
  SET [X], B
  SET [X + 1], B
:piece_jmp1
  ADD I, 1
  ADD X, 2
  ADD Y, 1
  SET B, 1
  IFE I, 16
    SET PC, POP
  IFB I, 3
    SET PC, piece_cyc1
  ADD X, 32 - 8
  SET PC, piece_cyc1   

:show_next_piece              ; redraw next piece
  SET X, 0x8000 + 32*7 + 1    ; place block at [X] (display)
  SET Y, [next_piece]         ; ...from [Y] (pieces array)
  SHL Y, 6
  ADD Y, pieces
  SET I, 0                    ; index
:npiece_cyc1
  SET B, [Y]
  IFE B, 0
    SET B, 0x0700
  SET [X], B
  SET [X + 1], B
:npiece_jmp1
  ADD I, 1
  ADD X, 2
  ADD Y, 1
  SET B, 1
  IFE I, 16
    SET PC, POP
  IFB I, 3
    SET PC, npiece_cyc1
  ADD X, 32 - 8
  SET PC, npiece_cyc1  

:scan_lines                   ; search for complete lines, remove them and move all other down; update score & level
  SET A, 0x8000 + 32*11 + 11  ; start of next line to fill
  SET B, A                    ; start of next line to check
  SET J, 0                    ; num of lines skipped
:scan_cyc2
  SET I, 0                    ; horizontal index
  SET X, B
:scan_cyc1
  IFE [X], 0
    SET PC, scan_jmp1
  ADD X, 2
  ADD I, 1
  IFG 10, I
    SET PC, scan_cyc1
  ADD J, 1                    ; no gaps found, increase num of complete rows
  SUB B, 32
  IFE J, 4
    SET PC, scan_jmp1
  IFG B, 0x8000
    SET PC, scan_cyc2
:scan_jmp1                    ; found a gap, or no more gaps can be found
  IFE A, B
    SET PC, scan_jmp2         ; no need to move anything, continue
  SET I, 0
:scan_cyc3
  SET [A], [B]
  ADD I, 1
  ADD A, 1
  ADD B, 1
  IFG 20, I
    SET PC, scan_cyc3
  SUB A, 20
  SUB B, 20
:scan_jmp2
  SUB A, 32
  IFG 0x8000, A
    SET PC, scan_end
  SUB B, 32
  IFE J, 4
    SET PC, scan_jmp1
  IFG 0x8000, B
    SET PC, scan_jmp1
  SET PC, scan_cyc2
:scan_end
  IFE J, 0
    SET PC, POP
  ADD [lines], J
  SET J, [lines_score + J]
  MUL J, [level]
  ADD [score], J
  JSR update_score
  IFG 10, [lines]
    SET PC, POP
  SET [lines], 0
  ADD [level], 1
  JSR update_level
  SET PC, POP

:main_loop
  JSR select_next_piece
  SET A, 2
  JSR show_cur_piece          ; check if we can drop next piece
  IFE B, 0
    SET PC, game_over
  JSR show_next_piece         ; redraw next piece
  SET A, [level]
  SET [cycle_num], [level_cycles + A]

:drop_loop
  SET Z, [cycle_num]
:wait_loop_redraw             ; "heavy" way, redraw current piece
  SET A, 1
  JSR show_cur_piece
:wait_loop                    ; "light" way, when no keys were pressed
  IFE Z, 0
    SET PC, drop_jmp
  SUB Z, 1
; read from keyboard
  JSR next_key
  IFE A, 1
    SET PC, key_left
  IFE A, 2
    SET PC, key_right
  IFE A, 3
    SET PC, key_up
  IFE A, 4
    SET PC, key_down
  SET PC, wait_loop

:key_left
  SET A, 0
  JSR show_cur_piece
  SUB [piece_pos], 2
  SET A, 2
  JSR show_cur_piece
  IFE B, 1
    SET PC, wait_loop_redraw
  ADD [piece_pos], 2
  SET PC, wait_loop_redraw

:key_right
  SET A, 0
  JSR show_cur_piece
  ADD [piece_pos], 2
  SET A, 2
  JSR show_cur_piece
  IFE B, 1
    SET PC, wait_loop_redraw
  SUB [piece_pos], 2
  SET PC, wait_loop_redraw

:key_down
  SET [cycle_num], 100
  SET Z, 100
  SET PC, wait_loop

:key_up
  SET A, 0
  JSR show_cur_piece
  ADD [cur_rot], 1
  AND [cur_rot], 3
  SET A, 2
  JSR show_cur_piece
  IFE B, 1
    SET PC, wait_loop_redraw
  SUB [cur_rot], 1
  AND [cur_rot], 3
  SET PC, wait_loop_redraw
  

:drop_jmp
; lower current piece
  SET A, 0
  JSR show_cur_piece
  ADD [piece_pos], 32
  SET A, 2
  JSR show_cur_piece
  IFE B, 1
    SET PC, drop_loop
  SUB [piece_pos], 32
  SET A, 1
  JSR show_cur_piece
  JSR scan_lines
  SET PC, main_loop

:game_over                    ; print blinking game over message
  SET C, 0xf480
  SET A, 0x8000 + 32 * 4 + 9 ; (9, 4)
  SET B, game_over_pad
  JSR print
  SET A, 0x8000 + 32 * 5 + 9 ; (9, 5)
  SET B, game_over_str
  JSR print
  SET A, 0x8000 + 32 * 6 + 9 ; (9, 6)
  SET B, game_over_pad
  JSR print
  SUB PC, 1

:cycle_num
  DAT 2500
:level_cycles
  DAT 2500, 2500, 2000, 1500, 1000, 800, 650, 500, 300, 200, 100, 75, 50, 30, 20, 10, 5, 3, 2, 1
:lines_score
  DAT 0, 2, 5, 15, 60
:lines
  DAT 0
:level
  DAT 1
:score
  DAT 0
:entropy_str1
  DAT "Press any key to", 0
:entropy_str2
  DAT " make game random:", 0
:score_str
  DAT "Score:", 0
:level_str
  DAT "Level:", 0
:next_str
  DAT "Next:", 0
:game_over_pad
  DAT "              ", 0
:game_over_str
  DAT "  Game Over!  ", 0
:pieces
; I
  DAT 0, 0x0c00, 0, 0,   0, 0x0c00, 0, 0,   0, 0x0c00, 0, 0,   0, 0x0c00, 0, 0
  DAT 0, 0, 0, 0,   0x0c00, 0x0c00, 0x0c00, 0x0c00,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0c00, 0, 0,   0, 0x0c00, 0, 0,   0, 0x0c00, 0, 0,   0, 0x0c00, 0, 0
  DAT 0, 0, 0, 0,   0x0c00, 0x0c00, 0x0c00, 0x0c00,   0, 0, 0, 0,   0, 0, 0, 0
; J
  DAT 0x0d00, 0, 0, 0,   0x0d00, 0x0d00, 0x0d00, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0d00, 0x0d00, 0,   0, 0x0d00, 0, 0,   0, 0x0d00, 0, 0,   0, 0, 0, 0
  DAT 0, 0, 0, 0,   0x0d00, 0x0d00, 0x0d00, 0,   0, 0, 0x0d00, 0,   0, 0, 0, 0
  DAT 0, 0x0d00, 0, 0,   0, 0x0d00, 0, 0,   0x0d00, 0x0d00, 0, 0,   0, 0, 0, 0
; L
  DAT 0, 0, 0x0e00, 0,   0x0e00, 0x0e00, 0x0e00, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0e00, 0, 0,   0, 0x0e00, 0, 0,   0, 0x0e00, 0x0e00, 0,   0, 0, 0, 0
  DAT 0, 0, 0, 0,   0x0e00, 0x0e00, 0x0e00, 0,   0x0e00, 0, 0, 0,   0, 0, 0, 0
  DAT 0x0e00, 0x0e00, 0, 0,   0, 0x0e00, 0, 0,   0, 0x0e00, 0, 0,   0, 0, 0, 0
; O
  DAT 0x0b00, 0x0b00, 0, 0,   0x0b00, 0x0b00, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0x0b00, 0x0b00, 0, 0,   0x0b00, 0x0b00, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0x0b00, 0x0b00, 0, 0,   0x0b00, 0x0b00, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0x0b00, 0x0b00, 0, 0,   0x0b00, 0x0b00, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0
; S
  DAT 0, 0x0900, 0x0900, 0,   0x0900, 0x0900, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0900, 0, 0,   0, 0x0900, 0x0900, 0,   0, 0, 0x0900, 0,   0, 0, 0, 0
  DAT 0, 0x0900, 0x0900, 0,   0x0900, 0x0900, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0900, 0, 0,   0, 0x0900, 0x0900, 0,   0, 0, 0x0900, 0,   0, 0, 0, 0
; T  
  DAT 0x0800, 0x0800, 0x0800, 0,   0, 0x0800, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0800, 0, 0,   0x0800, 0x0800, 0, 0,   0, 0x0800, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0800, 0, 0,   0x0800, 0x0800, 0x0800, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0x0800, 0, 0,   0, 0x0800, 0x0800, 0,   0, 0x0800, 0, 0,   0, 0, 0, 0
; Z
  DAT 0x0a00, 0x0a00, 0, 0,   0, 0x0a00, 0x0a00, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0, 0x0a00, 0,   0, 0x0a00, 0x0a00, 0,   0, 0x0a00, 0, 0,   0, 0, 0, 0
  DAT 0x0a00, 0x0a00, 0, 0,   0, 0x0a00, 0x0a00, 0,   0, 0, 0, 0,   0, 0, 0, 0
  DAT 0, 0, 0x0a00, 0,   0, 0x0a00, 0x0a00, 0,   0, 0x0a00, 0, 0,   0, 0, 0, 0