-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.s
2421 lines (1831 loc) · 71.3 KB
/
app.s
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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.include "./constants.s"
.include "./time.s"
.include "./pixel.s"
.include "./star.s"
.include "./line.s"
.include "./rectangle.s"
.include "./tetris_cell.s"
.include "./particle.s"
.include "./util.s"
.include "./rand.s"
.include "./background.s"
/* Choose a different BOARD_WIDTH and/or BOARD_HEIGHT to play tetris of any size! */
.equ BOARD_WIDTH, 10 // Original is 10, minimum 4.
.equ BOARD_HEIGHT, 20 // Original is 20, minimum 4.
.equ BOARD_SIZE, BOARD_WIDTH * BOARD_HEIGHT
.equ BOARD_SIZE_IN_BYTES, BOARD_SIZE * 1
.equ TETROMINO_ID_BIAS, 1
.equ TEST_BINARY_TO_DECIMAL_MAX, 10000
.equ TICKS_PER_SECOND, 60
.equ MS_PER_TICK, 1000 / TICKS_PER_SECOND // unused
.equ US_PER_TICK, 1000000 / TICKS_PER_SECOND
.equ MOVEMENT_PER_STEP, 16
.equ MS_PER_DROP, 400
.equ US_PER_AUTO_DROP, MS_PER_DROP * 1000
.equ DESTROY_PARTICLE_RADIUS, 10
.equ DESTROY_PARTICLE_COLOR, 0xFFFF0000
.equ BACK_BUFFER_ADDRESS, 0x1000000
.equ GPIO_BASE, 0x3f200000
.equ GPIO_GPFSEL0, 0x00
.equ GPIO_GPLEV0, 0x34
.equ GPIO_W, 0b000010 // (GPIO_1)
.equ GPIO_A, 0b000100 // (GPIO_2)
.equ GPIO_S, 0b001000 // (GPIO_3)
.equ GPIO_D, 0b010000 // (GPIO_4)
.equ GPIO_SPACE, 0b100000 // (GPIO_5)
.equ MS_PER_STEP, 250
.equ US_PER_STEP, MS_PER_STEP * 1000
/* Bit flags used for drawing digits. */
.equ DIGIT_SEGMENT_0, 1 << 0
.equ DIGIT_SEGMENT_1, 1 << 1
.equ DIGIT_SEGMENT_2, 1 << 2
.equ DIGIT_SEGMENT_3, 1 << 3
.equ DIGIT_SEGMENT_4, 1 << 4
.equ DIGIT_SEGMENT_5, 1 << 5
.equ DIGIT_SEGMENT_6, 1 << 6
.equ BACKGROUND_COLOR, 0xFF000000
BG_COLORS: .word 0x00FF0000, 0x00FFFF00, 0x0000FF00, 0x0000FFFF, 0x000000FF, 0x001E1E1E, 0x00000000
BG_COLOR: .dword 0xFFFFFFFF
PLAYER_COLOR: .dword 0xFFFFFFFF
.globl main
/* enum CollisionResult */
.equ COLLISION_RESULT_NO_COLLISION, 0b00
.equ COLLISION_RESULT_OUT_OF_BOUNDS, 0b01
.equ COLLISION_RESULT_EXISTING_BLOCK, 0b10
/* struct GameState */
.equ LAST_TICK, 0 // u32
.equ LAST_DROP, LAST_TICK + 4 // u32
.equ TICK_COUNTER, LAST_DROP + 4 // u64
.equ GAME_STATS, TICK_COUNTER + 8 // struct GameStats
.equ INPUT_STATE, GAME_STATS + STRUCT_GS_SIZE // struct InputState
.equ TETROMINO_STATE, INPUT_STATE + STRUCT_INPUT_STATE_SIZE // struct TetrominoState
.equ CHOOSER_BAG_STATE, TETROMINO_STATE + STRUCT_TSTATE_SIZE // struct ChooserBagState
.equ BOARD_STATE, CHOOSER_BAG_STATE + STRUCT_CBAG_STATE_SIZE // struct BoardState
.equ PARTICLE_MANAGER, BOARD_STATE + STRUCT_BSTATE_SIZE // struct ParticleManager
.equ STRUCT_GAME_STATE_SIZE, PARTICLE_MANAGER + STRUCT_PM_SIZE
/* struct GameStats */
.equ GS_SCORE, 0 // u64
.equ GS_COMPLETED_ROWS, GS_SCORE + 8 // u64
.equ STRUCT_GS_SIZE, GS_COMPLETED_ROWS + 8
/* struct InputState */
.equ PRESSED_STATE, 0 // u32
.equ ON_PRESS_STATE, PRESSED_STATE + 4 // u32
.equ STRUCT_INPUT_STATE_SIZE, ON_PRESS_STATE + 4
/* struct TetrominoState */
.equ TSTATE_ID, 0 // u64
.equ TSTATE_ROT, TSTATE_ID + 8 // u64
.equ TSTATE_POS_X, TSTATE_ROT + 8 // i64
.equ TSTATE_POS_Y, TSTATE_POS_X + 8 // i64
.equ STRUCT_TSTATE_SIZE, TSTATE_POS_Y + 8
/* struct ChooserBagState */
.equ CBAG_REMAINING, 0 // u8
.equ CBAG_VALUES, CBAG_REMAINING + 1 // u8[7] = u8[NUM_TETROMINOS]
.equ STRUCT_CBAG_STATE_SIZE, CBAG_VALUES + (NUM_TETROMINOS * 1)
/* struct BoardState */
.equ BSTATE_BOARD, 0 // u8[22][10] = u8[BOARD_HEIGHT][BOARD_WIDTH]
.equ BSTATE_DESTROYED_ROWS, BSTATE_BOARD + BOARD_SIZE_IN_BYTES // u8[4]
.equ STRUCT_BSTATE_SIZE, BSTATE_DESTROYED_ROWS + 4
/* struct DrawNumberInfo */
.equ DNI_POS_X, 0 // u64
.equ DNI_POS_Y, DNI_POS_X + 8 // u64
.equ DNI_VALUE, DNI_POS_Y + 8 // i64
.equ DNI_MAX_DIGITS, DNI_VALUE + 8 // u64
.equ DNI_DIGIT_WIDTH, DNI_MAX_DIGITS + 8 // u64
.equ DNI_DIGIT_THICKNESS, DNI_DIGIT_WIDTH + 8 // u64
.equ DNI_DIGIT_SPACING, DNI_DIGIT_THICKNESS + 8 // u64
.equ DNI_COLOR, DNI_DIGIT_SPACING + 8 // u32
.equ STRUCT_DNI_SIZE, DNI_COLOR + 4
/* Data for drawing decimal digits */
/* u32[10] */
DIGITS_FONT:
/* Digit 0: */
.word DIGIT_SEGMENT_0 | DIGIT_SEGMENT_1 | DIGIT_SEGMENT_2 | DIGIT_SEGMENT_4 | DIGIT_SEGMENT_5 | DIGIT_SEGMENT_6
/* Digit 1: */
.word DIGIT_SEGMENT_2 | DIGIT_SEGMENT_5
/* Digit 2: */
.word DIGIT_SEGMENT_0 | DIGIT_SEGMENT_1 | DIGIT_SEGMENT_3 | DIGIT_SEGMENT_5 | DIGIT_SEGMENT_6
/* Digit 3: */
.word DIGIT_SEGMENT_0 | DIGIT_SEGMENT_2 | DIGIT_SEGMENT_3 | DIGIT_SEGMENT_5 | DIGIT_SEGMENT_6
/* Digit 4: */
.word DIGIT_SEGMENT_2 | DIGIT_SEGMENT_3 | DIGIT_SEGMENT_4 | DIGIT_SEGMENT_5
/* Digit 5: */
.word DIGIT_SEGMENT_0 | DIGIT_SEGMENT_2 | DIGIT_SEGMENT_3 | DIGIT_SEGMENT_4 | DIGIT_SEGMENT_6
/* Digit 6: */
.word DIGIT_SEGMENT_0 | DIGIT_SEGMENT_1 | DIGIT_SEGMENT_2 | DIGIT_SEGMENT_3 | DIGIT_SEGMENT_4 | DIGIT_SEGMENT_6
/* Digit 7: */
.word DIGIT_SEGMENT_2 | DIGIT_SEGMENT_5 | DIGIT_SEGMENT_6
/* Digit 8: */
.word DIGIT_SEGMENT_0 | DIGIT_SEGMENT_1 | DIGIT_SEGMENT_2 | DIGIT_SEGMENT_3 | DIGIT_SEGMENT_4 | DIGIT_SEGMENT_5 | DIGIT_SEGMENT_6
/* Digit 9: */
.word DIGIT_SEGMENT_2 | DIGIT_SEGMENT_3 | DIGIT_SEGMENT_4 | DIGIT_SEGMENT_5 | DIGIT_SEGMENT_6
/* Digit - (minus) (i = 10): */
.word DIGIT_SEGMENT_3
.equ DIGIT_MINUS_INDEX, 10
/*
* x19: framebuffer address (could be x9-15 ?)
* x20: player x
* x21: player y
* x22: bg color index
* w23: bg color
* w24: player color
* x25: remaining columns of row
* x26: remaining rows
* x28 (const): beginning of framebuffer
*/
main:
sub sp, sp, 8
// TODO: clean framebuffer saving
str x0, [sp] // Save framebuffer to stack
// mov x28, x0 // Save framebuffer address to x28
ldr x28, =BACK_BUFFER_ADDRESS
// Set GPIOs 0..9 to read
mov x9, GPIO_BASE
str wzr, [x9, GPIO_GPFSEL0]
// Allocate and init struct GameState
mov x9, STRUCT_GAME_STATE_SIZE
sub sp, sp, x9
restart:
mov x0, sp
bl init_game_state
add x0, sp, PARTICLE_MANAGER
bl create_background_starfield
/*
* ************* *
* * GAME LOOP * *
* ************* *
*/
game_loop:
add x0, sp, INPUT_STATE
bl read_input
add x0, sp, INPUT_STATE
add x1, sp, TETROMINO_STATE
add x2, sp, BOARD_STATE
bl process_input
mov x0, sp
mov x1, x28
bl tetromino_fall
cbz x0, game_loop__keep_tetromino
/* We have just placed a tetromino, and we need to get a new one */
add x0, sp, TETROMINO_STATE
bl init_tetromino_state
add x0, sp, CHOOSER_BAG_STATE
bl choose_next_tetromino
sturb w0, [sp, TETROMINO_STATE + TSTATE_ID] // Start with new tetromino.
game_loop__keep_tetromino:
mov x0, x28
bl draw_canvas
add x0, sp, PARTICLE_MANAGER
mov x1, x28
bl particle_manager_render
mov x0, ((SCREEN_WIDTH / 2) - (TETRIS_CELL_SIZE * ((BOARD_WIDTH / 2) + 1)))
mov x1, ((SCREEN_HEIGH / 2) - (TETRIS_CELL_SIZE * ((BOARD_HEIGHT / 2) + 1)))
mov x19, x1
add x2, sp, BOARD_STATE
add x3, sp, TETROMINO_STATE
mov x4, x28
bl draw_board
mov x0, (SCREEN_WIDTH - 1)
sub x0, x0, x19
mov x1, x19
add x2, sp, GAME_STATS
mov x3, x28
bl draw_stats
mov x0, sp
mov x1, x28
bl draw_destroy_particles
/* Copy back buffer to real framebuffer */
mov x0, x28
mov x9, STRUCT_GAME_STATE_SIZE
ldr x1, [sp, x9] // restore original framebuffer address from stack
bl copy_framebuffer
game_loop__sleep:
ldr w0, [sp, LAST_TICK]
bl get_elapsed_time
mov w2, US_PER_TICK
cmp w0, w2
b.lt game_loop__sleep
ldr w0, [sp, LAST_TICK]
add w0, w0, w2
str w0, [sp, LAST_TICK]
ldr x0, [sp, TICK_COUNTER]
add x0, x0, 1
str x0, [sp, TICK_COUNTER]
b game_loop
game_over:
add x0, sp, INPUT_STATE
bl read_input
add x0, sp, INPUT_STATE
ldr w0, [x0, ON_PRESS_STATE]
add x1, sp, TETROMINO_STATE
bl handle_space
b game_over
/*
* Params:
* x0: in u32* <- src framebuffer
* x1: out u32* <- dst framebuffer
*/
copy_framebuffer:
stp lr, x19, [sp, -80]!
stp x20, x21, [sp, 16]
stp x22, x23, [sp, 32]
stp x24, x25, [sp, 48]
stp x26, x27, [sp, 64]
ldr x19, =SCREEN_PIXELS // x19 = width * height
lsl x19, x19, BYTES_PER_PIXEL_SHIFT
add x19, x0, x19
copy_framebuffer__next_pixel:
ldp x9, x10, [x0]
ldp x11, x12, [x0, 16]
ldp x13, x14, [x0, 32]
ldp x15, x16, [x0, 48]
ldp x20, x21, [x0, 64]
ldp x22, x23, [x0, 80]
ldp x24, x25, [x0, 96]
ldp x26, x27, [x0, 112]
stp x9, x10, [x1]
stp x11, x12, [x1, 16]
stp x13, x14, [x1, 32]
stp x15, x16, [x1, 48]
stp x20, x21, [x1, 64]
stp x22, x23, [x1, 80]
stp x24, x25, [x1, 96]
stp x26, x27, [x1, 112]
add x0, x0, 128
add x1, x1, 128
cmp x0, x19
b.lt copy_framebuffer__next_pixel
ldp x26, x27, [sp, 64]
ldp x24, x25, [sp, 48]
ldp x22, x23, [sp, 32]
ldp x20, x21, [sp, 16]
ldp lr, x19, [sp], 80
ret
/*
* Params:
* x0: out struct GameState* <- game_state
*/
init_game_state:
stp lr, x19, [sp, -16]!
mov x19, x0 // x19 <- game_state
bl get_time
str w0, [x19, LAST_TICK]
str w0, [x19, LAST_DROP]
str xzr, [x19, TICK_COUNTER]
add x0, x19, INPUT_STATE
bl init_input_state
add x0, x19, TETROMINO_STATE
bl init_tetromino_state
add x0, x19, CHOOSER_BAG_STATE
bl recreate_chooser_bag
add x0, x19, CHOOSER_BAG_STATE
bl choose_next_tetromino
stur x0, [x19, TETROMINO_STATE + TSTATE_ID] // Set first tetromino.
add x0, x19, PARTICLE_MANAGER
bl particle_manager_init
add x0, x19, BOARD_STATE
bl init_board_state
add x0, x19, GAME_STATS
bl init_game_stats
ldp lr, x19, [sp], 16
ret
/*
* Params:
* x0: out struct InputState*
*/
init_input_state:
stur wzr, [x0, PRESSED_STATE]
stur wzr, [x0, ON_PRESS_STATE]
ret
/*
* Params:
* x0: out struct TetrominoState*
*/
init_tetromino_state:
stur xzr, [x0, TSTATE_ID]
mov x9, ((BOARD_WIDTH / 2) - 2)
stur x9, [x0, TSTATE_POS_X]
mov x9, (BOARD_HEIGHT - 4)
stur x9, [x0, TSTATE_POS_Y]
stur xzr, [x0, TSTATE_ROT]
ret
/*
* Params:
* x0: out struct BoardState*
*/
init_board_state:
sub sp, sp, 8
stur x19, [sp]
mov x19, x0 // x19 <- board state
add x9, x19, BSTATE_BOARD // x9 <- u32* pointer to current cell
mov x10, BOARD_SIZE // x10 <- remaining cells
init_board_state__board_loop:
sturb wzr, [x9]
add x9, x9, 1 // Advance board pointer
sub x10, x10, 1 // Decrement remaining cells
cbnz x10, init_board_state__board_loop
add x9, x19, BSTATE_DESTROYED_ROWS // x9 <- pointer to destroyed rows array
mov x10, 4 // x10 <- remaining destroyed rows elements
mov w11, 0xFF
init_board_state__destroyed_rows_loop:
sturb w11, [x9]
add x9, x9, 1
sub x10, x10, 1
cbnz x10, init_board_state__destroyed_rows_loop
ldur x19, [sp]
add sp, sp, 8
ret
/*
* Params:
* x0: out struct GameStats*
*/
init_game_stats:
stur xzr, [x0, GS_SCORE]
stur xzr, [x0, GS_COMPLETED_ROWS]
ret
/*
* Params:
* x0: out u32* <- framebuffer
*/
draw_canvas:
stp lr, x19, [sp, -16]!
ldr w9, =BACKGROUND_COLOR // x9 <- background color
ldr x19, =SCREEN_PIXELS // x19 = width * height = remaining pixels
draw_canvas__next_pixel:
str w9, [x0] // Paint pixel with background color
add x0, x0, BYTES_PER_PIXEL // Advance framebuffer
subs x19, x19, 1 // Decrement remaining pixels
b.gt draw_canvas__next_pixel // Loop if pixels remain
ldp lr, x19, [sp], 16
ret
/*
* Params:
* x0: u64 <- bottom left x coordinate (game coordinates)
* x1: u64 <- bottom left y coordinate (game coordinates)
* x2: in struct BoardState* <- board state
* x3: in struct TetrominoState* <- tetromino state
* x4: in/out u32* <- framebuffer
*/
draw_board:
stp lr, x19, [sp, -48]!
stp x20, x21, [sp, 16]
stp x22, x23, [sp, 32]
mov x20, x0
mov x21, x1
mov x22, x2
mov x23, x3
mov x19, x4
add x0, x20, TETRIS_CELL_SIZE
add x1, x21, TETRIS_CELL_SIZE
mov x2, x22
mov x3, x23
mov x4, x19
bl draw_board_inner
mov x0, x20
mov x1, x21
mov x2, x19
bl draw_board_frame
ldp x22, x23, [sp, 32]
ldp x20, x21, [sp, 16]
ldp lr, x19, [sp], 48
ret
/*
* Params:
* x0: u64 <- bottom left x coordinate (game coordinates)
* x1: u64 <- bottom left y coordinate (game coordinates)
* x2: in struct BoardState* <- board state
* x3: in struct TetrominoState* <- tetromino state
* x4: in/out u32* <- framebuffer
*/
draw_board_inner:
stp lr, x19, [sp, -80]!
stp x20, x21, [sp, 16]
stp x22, x23, [sp, 32]
stp x24, x25, [sp, 48]
stp x26, x27, [sp, 64]
mov x20, x0 // x20 <- bottom left x coordinate
mov x21, x1 // x21 <- bottom left y coordinate
mov x22, x2 // x22 <- board state
mov x23, x3 // x23 <- tetromino state
mov x24, x4 // x24 <- framebuffer
mov x26, 0 // x26 <- row iterator
draw_board_inner__next_row:
mov x25, 0 // x25 <- column iterator
draw_board_inner__next_col:
mov x0, x25
mov x1, x26
mov x2, x22
mov x3, x23
bl get_cell_id_with_tetromino_at
cbz x0, draw_board_inner__skip_cell // skip if cell is air
sub x0, x0, TETROMINO_ID_BIAS // x0 <- normalized cell id
bl get_tetromino_by_index
mov x2, x0 // x2: in struct Tetromino* <- current tetromino
mov x9, TETRIS_CELL_SIZE
mul x0, x9, x25 // x0 <- x offset in coords to current cell
add x0, x20, x0 // x0 <- bottom left x of current cell
mul x1, x9, x26 // x1 <- y offset in coords to current cell
add x1, x21, x1 // x1 <- bottom left y of current cell
mov x3, x24
bl draw_tetris_cell
draw_board_inner__skip_cell:
add x25, x25, 1 // Advance to next column
cmp x25, BOARD_WIDTH // Check if we finished row
b.lt draw_board_inner__next_col
/* We are at the end of the row */
add x26, x26, 1 // Advance to next row
cmp x26, BOARD_HEIGHT // Check if we finished all rows
b.lt draw_board_inner__next_row
/* We finished drawing inner board */
ldp x26, x27, [sp, 64]
ldp x24, x25, [sp, 48]
ldp x22, x23, [sp, 32]
ldp x20, x21, [sp, 16]
ldp lr, x19, [sp], 80
ret
/*
* Params:
* x0: u64 <- bottom left x coordinate (game coordinates)
* x1: u64 <- bottom left y coordinate (game coordinates)
* x2: in/out u32* <- framebuffer
*/
draw_board_frame:
stp lr, x19, [sp, -64]!
stp x20, x21, [sp, 16]
stp x22, x23, [sp, 32]
stp x24, x25, [sp, 48]
mov x20, x0 // x20 <- bottom left x coordinate
mov x21, x1 // x21 <- bottom left y coordinate
mov x22, x2 // x22 <- framebuffer
/* Draw bottom and top rows */
mov x23, ((BOARD_HEIGHT + 1) * TETRIS_CELL_SIZE) // vertical offset for top row, in pixels
mov x24, 0 // x iterator
draw_board_frame__rows_next_col:
mov x9, TETRIS_CELL_SIZE
mul x25, x9, x24 // x25 <- x offset in coords to current cell
add x25, x20, x25 // x25 <- bottom left x of current cell
mov x0, x25
mov x1, x21 // x1 <- bottom left y of bottom row
ldr x2, =FRAME_DIFFUSE_COLOR
ldr x3, =FRAME_SPECULAR_COLOR
ldr x4, =FRAME_AMBIENT_COLOR
mov x5, x22
bl draw_tetris_cell_by_colors
mov x0, x25
add x1, x21, x23 // x1 <- bottom left y of top row
ldr x2, =FRAME_DIFFUSE_COLOR
ldr x3, =FRAME_SPECULAR_COLOR
ldr x4, =FRAME_AMBIENT_COLOR
mov x5, x22
bl draw_tetris_cell_by_colors
add x24, x24, 1
cmp x24, (BOARD_WIDTH + 2)
b.lt draw_board_frame__rows_next_col
/* Draw left and right columns */
mov x23, ((BOARD_WIDTH + 1) * TETRIS_CELL_SIZE) // horizontal offset for right column, in pixels
mov x24, 1 // y iterator (we start at 1 because 0 has already been drawn)
draw_board_frame__cols_next_row:
mov x9, TETRIS_CELL_SIZE
mul x25, x9, x24 // x25 <- y offset in coords to current cell
add x25, x21, x25 // x25 <- bottom left y of current cell
mov x0, x20
mov x1, x25
ldr x2, =FRAME_DIFFUSE_COLOR
ldr x3, =FRAME_SPECULAR_COLOR
ldr x4, =FRAME_AMBIENT_COLOR
mov x5, x22
bl draw_tetris_cell_by_colors
add x0, x20, x23 // x1 <- bottom left y of right column
mov x1, x25
ldr x2, =FRAME_DIFFUSE_COLOR
ldr x3, =FRAME_SPECULAR_COLOR
ldr x4, =FRAME_AMBIENT_COLOR
mov x5, x22
bl draw_tetris_cell_by_colors
add x24, x24, 1
cmp x24, (BOARD_HEIGHT + 1)
b.lt draw_board_frame__cols_next_row
ldp x24, x25, [sp, 48]
ldp x22, x23, [sp, 32]
ldp x20, x21, [sp, 16]
ldp lr, x19, [sp], 64
ret
/*
* Params:
* x0: u64 <- x coordinate 0 <= x < BOARD_WIDTH
* x1: u64 <- y coordinate 0 <= y < BOARD_HEIGHT
* x2: in struct BoardState* <- board state
* x3: in struct TetrominoState* <- tetromino state
* Returns:
* x0: u64 <- 0 or tetromino id + TETROMINO_ID_BIAS
*/
get_cell_id_with_tetromino_at:
stp lr, x19, [sp, -80]!
stp x20, x21, [sp, 16]
stp x22, x23, [sp, 32]
stp x24, x25, [sp, 48]
stp x26, x27, [sp, 64]
mov x20, x0 // x20 <- x board coordinate
mov x21, x1 // x21 <- y board coordinate
mov x22, x2 // x22 <- board state
mov x23, x3 // x23 <- tetromino state
ldr x24, [x3, TSTATE_POS_X] // x24 <- tetromino x
ldr x25, [x3, TSTATE_POS_Y] // x25 <- tetromino y
sub x24, x0, x24 // x24 <- tetromino x relative to cell x
cmp x24, 0
b.lt get_cell_id_with_tetromino_at__not_tetromino
cmp x24, TETROMINO_BOARD_WIDTH
b.ge get_cell_id_with_tetromino_at__not_tetromino
sub x25, x1, x25 // x25 <- tetromino y relative to cell y
cmp x25, 0
b.lt get_cell_id_with_tetromino_at__not_tetromino
cmp x25, TETROMINO_BOARD_HEIGHT
b.ge get_cell_id_with_tetromino_at__not_tetromino
/* Current cell is inside tetromino matrix, we should check that first */
mov x0, x3
bl get_tetromino // x0 <- tetromino data of current tetromino's rotation
mov x2, x0 // x2 <- current tetromino data
mov x0, x24 // x0 <- tetromino x in tetromino board
mov x1, x25 // x1 <- tetromino y in tetromino board
bl is_tetromino_cell_air
cbnz x0, get_cell_id_with_tetromino_at__not_tetromino
/* Current cell belongs to tetromino */
ldr x0, [x23, TSTATE_ID] // x0 <- cell id without bias
add x0, x0, TETROMINO_ID_BIAS // x0 <- cell id with bias
b get_cell_id_with_tetromino_at__end
get_cell_id_with_tetromino_at__not_tetromino:
mov x9, BOARD_WIDTH
mul x9, x21, x9
add x9, x9, x20
add x9, x9, BSTATE_BOARD // x9 <- offset to board cell
add x9, x22, x9 // x9 <- pointer to board cell
ldrb w0, [x9] // x0 <- cell id (already has bias)
b get_cell_id_with_tetromino_at__end
get_cell_id_with_tetromino_at__end:
ldp x26, x27, [sp, 64]
ldp x24, x25, [sp, 48]
ldp x22, x23, [sp, 32]
ldp x20, x21, [sp, 16]
ldp lr, x19, [sp], 80
ret
/*
* Params:
* x0: u64 <- tetromino index 0 <= i < NUM_TETROMINOS
* Returns:
* x0: in struct Tetromino* <- tetromino of current tetromino
*/
get_tetromino_by_index:
stp lr, x19, [sp, -16]!
mov x9, STRUCT_TETROMINO_SIZE // x0 <- size of tetromino in bytes
mul x9, x9, x0 // x9 <- offset to tetromino in bytes
ldr x0, =TETROMINOS // x0 <- pointer to first tetromino
add x0, x0, x9 // x0 <- pointer to current tetromino
ldp lr, x19, [sp], 16
ret
/*
* Params:
* x0: u64 <- x tetromino board coordinate 0 <= x < TETROMINO_BOARD_WIDTH
* x1: u64 <- y tetromino board coordinate 0 <= y < TETROMINO_BOARD_HEIGHT
* x2: in struct TetrominoData* <- tetromino data
* Returns:
* x0: bool <- is TetrominoData[y][x] air
*/
is_tetromino_cell_air:
stp lr, x19, [sp, -16]!
mov x9, TETROMINO_BOARD_WIDTH
mul x9, x1, x9 // x9 <- vertical offset
add x9, x9, x0 // x9 <- vertical offset + horizontal offset
add x9, x9, TDATA
add x9, x2, x9 // x9 <- pointer to cell at (x, y)
ldrb w9, [x9] // x9 <- cell at (x, y)
cmp x9, 0 // Check if cell is air
cset x0, eq // x0 = is_air() ? 1 : 0
ldp lr, x19, [sp], 16
ret
/*
* Params:
* x0: u64 <- bottom right x (game coordinates)
* x1: u64 <- bottom right y (game coordinates)
* x2: in struct GameStats* <- game stats
* x3: out u32* <- framebuffer
*/
draw_stats:
stp lr, x19, [sp, -16]!
sub sp, sp, STRUCT_DNI_SIZE
str x0, [sp, DNI_POS_X]
str x1, [sp, DNI_POS_Y]
ldr x9, [x2, GS_COMPLETED_ROWS]
str x9, [sp, DNI_VALUE]
mov x9, 5
str x9, [sp, DNI_MAX_DIGITS]
mov x9, 48
str x9, [sp, DNI_DIGIT_WIDTH]
mov x9, 12
str x9, [sp, DNI_DIGIT_THICKNESS]
mov x9, 12
str x9, [sp, DNI_DIGIT_SPACING]
ldr w9, =0xFFFFFFFF
str w9, [sp, DNI_COLOR]
mov x0, sp
mov x1, x3
bl draw_number
add sp, sp, STRUCT_DNI_SIZE
ldp lr, x19, [sp], 16
ret
/*
* Params:
* x0: struct GameState* <- game state
* x1: in/out u32* <- framebuffer
*/
draw_destroy_particles:
sub sp, sp, 72
stur lr, [sp]
stur x19, [sp, 8]
stur x20, [sp, 16]
stur x21, [sp, 24]
stur x22, [sp, 32]
stur x23, [sp, 40]
stur x24, [sp, 48]
stur x25, [sp, 56]
stur x26, [sp, 64]
add x19, x0, (BOARD_STATE + BSTATE_DESTROYED_ROWS)
mov x24, x0
add x0, x0, LAST_DROP
ldur w0, [x0]
mov x20, x0
mov x21, ( (SCREEN_WIDTH - BOARD_WIDTH * TETRIS_CELL_SIZE) / 2 )
sub x21, x21, TETRIS_CELL_SIZE
mov x22, ( (TETRIS_CELL_SIZE + SCREEN_HEIGH - BOARD_HEIGHT * TETRIS_CELL_SIZE) / 2 )
mov x23, x1
mov x2, 4
draw_destroy_particles__loop:
ldurb w3, [x19]
cmp x3, 0xff
b.eq draw_destroy_particles__end
sub sp, sp, 8
stur x2, [sp]
mov x0, x20
bl get_elapsed_time
lsl x0, x0, 5
ldr w2, =US_PER_AUTO_DROP
udiv x4, x0, x2
mov x5, 1
lsl x5, x5, 5
sub x5, x5, x4
mov x0, DESTROY_PARTICLE_RADIUS
mul x2, x5, x0
lsr x2, x2, 5
mov x10, x2
mov x25, x2
mov x2, 50
mul x4, x4, x2
lsr x4, x4, 5
mov x24, x4
mov x26, TETRIS_CELL_SIZE
mul x26, x26, x3
sub x0, x21, x24
add x1, x22, x26
mov x2, x23
mov x3, x25
ldr x4, =DESTROY_PARTICLE_COLOR
bl draw_circle_at
mov x0, SCREEN_WIDTH
sub x0, x0, x21
add x0, x0, x24
add x1, x22, x26
mov x2, x23
mov x3, x25
ldr x4, =DESTROY_PARTICLE_COLOR
bl draw_circle_at
ldur x2, [sp]
add sp, sp, 8
sub x2, x2, 1
add x19, x19, 1
cbnz x2, draw_destroy_particles__loop
b draw_destroy_particles__end
draw_destroy_particles__end:
ldur x26, [sp, 64]
ldur x25, [sp, 56]
ldur x24, [sp, 48]
ldur x23, [sp, 40]
ldur x22, [sp, 32]
ldur x21, [sp, 24]
ldur x20, [sp, 16]
ldur x19, [sp, 8]
ldur lr, [sp]
add sp, sp, 72
ret
/*
* Params:
* x0: in/out struct InputState*
*/
read_input:
stp lr, x19, [sp, -32]!
stp x20, x21, [sp, 16]
ldr w19, [x0, PRESSED_STATE] // Load last pressed state to compute on press state
mov x20, GPIO_BASE
ldr w20, [x20, GPIO_GPLEV0] // w20 = new pressed state
// Input in bit 'n' is "on press" if current pressed state is 1 and previous pressed state is 0
mvn w19, w19 // Bitwise NOT last pressed state
and w21, w20, w19 // w21 = new on press state
str w20, [x0, PRESSED_STATE]
str w21, [x0, ON_PRESS_STATE]
ldp x20, x21, [sp, 16]
ldp lr, x19, [sp], 32
ret
/*
* Params:
* x0: const struct InputState*
* x1: mut struct TetrominoState*
* x2: const struct BoardState* // Para chequear colision en los handle (MEJORARLO)
*/
process_input:
stp lr, x19, [sp, -32]!
stp x20, x21, [sp, 16]
mov x19, x0 // x19 = const struct InputState*
mov x20, x1 // x20 = mut struct TetrominoState*
mov x21, x2 // x21 = const struct BoardState*
ldr w0, [x19, ON_PRESS_STATE]
mov x1, x20
bl handle_w
ldr w0, [x19, ON_PRESS_STATE]
mov x1, x20
bl handle_a