forked from IvyTheGreat01/tetris_x64_assembly
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtetris.asm
1418 lines (1242 loc) · 30.2 KB
/
tetris.asm
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
; Ivan Bystrov
; 13 September 2020
;
; Simple Tetris game written in NASM assembly, using Xlib, for 64 bit Linux
; X11 functions
extern XOpenDisplay
extern XDefaultScreen
extern XBlackPixel
extern XWhitePixel
extern XDefaultRootWindow
extern XCreateSimpleWindow
extern XSelectInput
extern XMapWindow
extern XCheckWindowEvent
extern XDrawRectangle
extern XFillRectangle
extern XCreateGC
extern XDefaultColormap
extern XAllocNamedColor
extern XSetForeground
extern XCloseDisplay
extern XkbKeycodeToKeysym
; libc functions
extern clock
extern printf
extern getrandom
; ========== bss ==========
section .bss
; Xlib handles and ints
display: resb 8
screen: resb 4
black: resb 4
white: resb 4
r_win: resb 8
win: resb 8
gc_black: resb 8
gc_color: resb 8
colormap: resb 8
keysym: resb 8
; Xlib structures
xgcvals_white: resb 128
xgcvals_black: resb 128
xevent: resb 192
; Time values
clock_start: resb 8
clock_end: resb 8
; XColor structures
xcols:
struc xcol_struct
.teal: resb 16
.blue: resb 16
.orange: resb 16
.yellow: resb 16
.green: resb 16
.red: resb 16
.purple: resb 16
.temp: resb 16
endstruc
; xcols seems to overwrite into gamemap, past its last struct
; no idea why
buffer: resb 256
; Each byte represents one tile of the gameboard
; Must always be equal to [width] * [height]
gamemap: resb 220
; ========== data ==========
section .data
; Length of tiles in pixels
; ========== CHANGE THIS VALUE TO CHANGE THE GAME SIZE =========
tile_len: dd 60
; Number of milliseconds between next game state update
; When interval == 500, tetronimos drop one tile every 0.5 seconds
; ========== CHANGE THIS VALUE TO CHANGE THE GAME SPEED ========
interval: dd 500
; Width and height of the game board in tiles
; IF YOU CHANGE THESE VALUES YOU MUST CHANGE gamemap:
width: dd 10
height: dd 22
gamemap_len: dd 10 * 22
; Xlib constants
ExposureMask: dq 32768
KeyPressMask: dq 1
Expose: dd 12
gc_foreground: dq 4
KeyPress: dd 2
XK_Escape: dd 65307
XK_Left: dd 65361
XK_Right: dd 65363
XK_Up: dd 65362
XK_Down: dd 65364
XK_space: dd 32
; time.h constants
CLOCKS_PER_SEC: dq 1000000 ; 1 million
CLOCKS_PER_SEC_thousandths:
dq 1000
; Masks
colormask: db 11111110b
statusmask: db 00000001b
fallmask: db 00000001b
tealmask: db 00000010b
bluemask: db 00000100b
orangemask: db 00001000b
yellowmask: db 00010000b
greenmask: db 00100000b
redmask: db 01000000b
purplemask: db 10000000b
; Color names
teal: db "teal", 0
blue: db "blue", 0
orange: db "orange", 0
yellow: db "yellow", 0
green: db "green", 0
red: db "red", 0
purple: db "purple", 0
; Rotation vals
pivot_index: dd 0
old_indicies: dd 0, 0, 0, 0
new_indicies: dd 0, 0, 0, 0
old_vals db 0, 0, 0, 0
; Score values
total_score: dd 0
block_placed: dd 20
lineclear1: dd 100
lineclear2: dd 250
lineclear3: dd 500
lineclear4: dd 900
; Other vals (init to 0 each game)
softdrop: dd 0
quit_game: dd 0
wait_restart dd 0
; Randomizer values
stock_array: db 0, 1, 2, 3, 4, 5, 6
rand_array: db 0, 0, 0, 0, 0, 0, 0
rand_val: dd 0
global_index: dd 7
; Strings
string: db "hello", 10, 0
val_string: db "val: %u", 10
score_string: db "Score: %d", 10, 0
test_string: db "test: %d %d", 10, 0
start_string: db "============================", 10, "========== Tetris ==========", 10, 10, 0
end_string: db "============================", 10, 10, 0
; ========== text ==========
section .text
global main
; ---------- Spawns a new tetronimo ----------
; void SpawnTetronimo()
SpawnTetronimo:
push rbp
mov rbp, rsp
push rbx
; If global index of the rand array is 7 then re-randomize
mov r9d, [global_index] ; r9d = global index of rand array
xor r11d, r11d
cmp r9d, 7
cmove r9d, r11d
jl spawnNextTetronimo
; Store the stock_array in the rand array
xor ebx, ebx ; ebx = index into the arrays
initRandArrayStart:
mov cl, [stock_array + ebx]
mov [rand_array + ebx], cl
inc ebx
cmp ebx, 7
jl initRandArrayStart
; Randomize the array of tetronimo ids
xor ebx, ebx ; ebx = index into the arrays
randomizeArrayStart:
; getrandom(&rand_val, 4, 0)
push r9
mov rdi, rand_val
mov rsi, 4
xor rdx, rdx
call getrandom
pop r9
; rand_val now has some random int
mov eax, [rand_val]
mov r8d, 7
xor rdx, rdx
div r8d ; edx = rand_val % 7
mov cl, [rand_array + ebx] ; ecx = rand_array[index]
mov r8b, [rand_array + edx] ; r8d = rand_array[random_index]
mov [rand_array + edx], cl ; store val of current index at random index
mov [rand_array + ebx], r8b ; store val or random index at current index
; increment index
inc ebx
cmp ebx, 7
jl randomizeArrayStart
; Spawn the next tetronimo onto the gamemap
spawnNextTetronimo:
mov r8b, [rand_array + r9d]
; Spawn I tetronimo
cmp r8b, 0
jne endSpawnI
mov byte [gamemap + 3], 3
mov byte [gamemap + 4], 3
mov byte [gamemap + 5], 3
mov byte [gamemap + 6], 3
mov dword [pivot_index], 4
jmp returnSpawnTetronimo
endSpawnI:
; Spawn J tetronimo
cmp r8b, 1
jne endSpawnJ
mov byte [gamemap + 14], 5
mov byte [gamemap + 15], 5
mov byte [gamemap + 16], 5
mov byte [gamemap + 4], 5
mov dword [pivot_index], 15
jmp returnSpawnTetronimo
endSpawnJ:
; Spawn L tetronimo
cmp r8b, 2
jne endSpawnL
mov byte [gamemap + 14], 9
mov byte [gamemap + 15], 9
mov byte [gamemap + 16], 9
mov byte [gamemap + 6], 9
mov dword [pivot_index], 15
jmp returnSpawnTetronimo
endSpawnL:
; Spawn O tetronimo
cmp r8b, 3
jne endSpawnO
mov byte [gamemap + 4], 17
mov byte [gamemap + 5], 17
mov byte [gamemap + 14], 17
mov byte [gamemap + 15], 17
mov dword [pivot_index], -1
jmp returnSpawnTetronimo
endSpawnO:
; Spawn S tetronimo
cmp r8b, 4
jne endSpawnS
mov byte [gamemap + 5], 33
mov byte [gamemap + 6], 33
mov byte [gamemap + 14], 33
mov byte [gamemap + 15], 33
mov dword [pivot_index], 15
jmp returnSpawnTetronimo
endSpawnS:
; Spawn Z tetronimo
cmp r8b, 5
jne endSpawnZ
mov byte [gamemap + 4], 65
mov byte [gamemap + 5], 65
mov byte [gamemap + 15], 65
mov byte [gamemap + 16], 65
mov dword [pivot_index], 15
jmp returnSpawnTetronimo
endSpawnZ:
; Spawn T tetronimo
cmp r8b, 6
jne endSpawnT
mov byte [gamemap + 14], 129
mov byte [gamemap + 15], 129
mov byte [gamemap + 16], 129
mov byte [gamemap + 5], 129
mov dword [pivot_index], 15
jmp returnSpawnTetronimo
endSpawnT:
returnSpawnTetronimo:
; Increment the global index
inc r9d
mov [global_index], r9d
; Return
pop rbx
pop rbp
ret
; ---------- Spawns a new tetronimo ----------
; ---------- Rotates all falling tiles if possible ----------
; void Rotate(dir)
; dir = -1 rotate clockwise
; dir = 1 rotate counter clockwise
Rotate:
push rbp
mov rbp, rsp
push rbx
push r12
push r13
push r14
push r15
; If pivot == -1 don't rotate because its the O tetronimo
mov eax, [pivot_index] ; eax = pivot index
cmp eax, -1
je rotateReturn
; Get the global x, y coordinates of the pivot
xor rdx, rdx
mov r8d, [width] ; r8d = width
div r8d
mov r9d, edx ; r9d = global x of pivot
mov r10d, eax ; r10d = global y of pivot
xor r13d, r13d ; r13d = index into old/new indicies array
xor r14d, r14d ; r14d = index into old values array
; Loop over the buffer and calculate rotation for each falling tile
xor ebx, ebx ; ebx = index of current tile
calculateRotateStart:
; Check if this tile is falling
xor rcx, rcx
mov cl, [gamemap + ebx] ; cl = tile val
and cl, [statusmask]
cmp cl, 1
jne calculateRotateInc
; Calculate the global x, y coordinates of the current tile
xor rdx, rdx
mov eax, ebx
div r8d
; eax = index / width (global y coord current tile)
; edx = index % width (global x coord current tile)
; Calculate the local x, y coordinates of current tile with respect to pivot
sub edx, r9d
sub eax, r10d
mov r11d, edx ; r11d = local x coordinate of current tile
mov r12d, eax ; r12d = local y coordinate of current tile
; Calculate new local x, y coordinates of current tile after rotation
mov eax, r12d
mul edi
mov r15d, eax ; r15d = temp rotated local x coordinate
mov eax, r11d
push rdi
neg edi
mul edi
pop rdi
mov r12d, eax ; r12d = rotated local y coordinate of current tile
mov r11d, r15d ; r11d = rotated local x coordinate of current tile
; Calculate new global x, y coordinates of current tile after rotation
add r11d, r9d ; r11d = rotated global x coordinate of current tile
add r12d, r10d ; r12d = rotated global y coordinate of current tile
; Calculate new index of rotated tile
mov eax, r12d
mov edx, [width]
mul edx
add eax, r11d ; eax = rotated index of current tile
; Check if the rotated tile index is valid
cmp r11d, 0
jl rotateReturn ; don't rotate if rotated global x < 0
cmp r11d, [width]
jge rotateReturn ; don't rotate if rotated global x >= width
cmp r12d, 0
jl rotateReturn ; don't rotate if rotated global y < 0
cmp r12d, [height]
jge rotateReturn ; don't rotate if rotated global y >= height
; Don't rotate if rotated index is on a stopped tile
xor r15b, r15b
mov r15b, [gamemap + eax] ; r15b = current value of new tile
cmp r15b, 0
je canRotateTile ; rotate if new tile is empty
mov dl, r15b
and dl, [statusmask]
cmp dl, 1
je canRotateTile ; rotate if new tile is falling
jmp rotateReturn ; don't rotate if new tile is stopped
; This tile can be rotated so save original and new indicies for it
; as well as the old tile val
canRotateTile:
mov [old_indicies + r13d], ebx
mov [new_indicies + r13d], eax
add r13d, 4
xor rcx, rcx
mov cl, [gamemap + ebx]
mov [old_vals + r14d], cl
inc r14d
; Increment loop
calculateRotateInc:
inc ebx
cmp ebx, [gamemap_len]
jl calculateRotateStart
; Calculated all rotations so clear all old tiles
xor r13d, r13d ; index of old_indicies
clearOldTilesStart:
mov ebx, [old_indicies + r13d]
mov byte [gamemap + ebx], 0
add r13d, 4
cmp r13d, 4 * 4
jl clearOldTilesStart
; Set new rotated tiles
xor r13d, r13d ; index of new_indicies
xor r14d, r14d ; index of new tile val
setNewTilesStart:
mov ebx, [new_indicies + r13d]
add r13d, 4
mov r8b, [old_vals + r14d]
mov [gamemap + ebx], r8b
inc r14d
cmp r13d, 4 * 4
jl setNewTilesStart
; return
rotateReturn:
pop r15
pop r14
pop r13
pop r12
pop rbp
pop rbp
ret
; ---------- Rotates all falling tiles if possible ----------
; ---------- Moves all falling tiles left or right if possible ----------
; void MoveLeftRight(dir)
; dir = -1, move left if possible
; dir = 1, move right if possible
MoveLeftRight:
push rbp
mov rbp, rsp
push rbx
push r12
push r13
mov r13d, 1 ; used for cmov
; r8d = 0 if direction = -1, otherwise r8d = 1 (used for edge detection)
xor r8d, r8d
cmp edi, 1
cmove r8d, edi
; Check to see if any falling tiles can't move in the right direction
mov ecx, [gamemap_len]
xor rbx, rbx ; ebx = index
canMoveLeftRightStart:
mov r10b, [gamemap + ebx] ; r10b = value of tile at index
; Check if current tile is falling
and r10b, [statusmask]
cmp r10b, 1
jne canMoveLeftRightInc
; Current tile is falling so check if it can move
mov r10b, [gamemap + ebx]
mov eax, ebx
add eax, r8d ; eax = index or eax = index + 1
mov r11d, [width] ; r11d = width
xor rdx, rdx
div r11d ; edx = (index + dirmod) % width
cmp edx, 0
je returnMoveLeftRight ; tile can't move because its on incompatible edge
; Check if tile to the left/right of it is stopped
mov r11d, ebx
add r11d, edi
mov r10b, [gamemap + r11d] ; r10b = value of tile curr tile will move into
xor r11d, r11d
cmp r10b, 0
cmovne r11d, r13d ; r11d = 1 if side tile is not empty
and r10b, [statusmask]
xor r12, r12
cmp r10b, 0
cmove r12d, r13d ; r12d = 1 if side tile is not falling
and r11d, r12d ; r11d = 1 if side tile is stopped
cmp r11d, 1
je returnMoveLeftRight ; tile can't move because it will move on stopped tile
canMoveLeftRightInc:
inc ebx
cmp ebx, ecx
jl canMoveLeftRightStart
canMoveLeftRightEnd:
; All tiles can move so move them all
mov ebx, 1
mov r11d, 1
mov r9d, -1
cmp edi, 1
cmove ebx, ecx ; ebx = index
cmove r11d, r9d ; r9d = index incrementor or decrementor
dec ebx
; if moving right, index starts at end and decrements
; if moving left, index starts at start and increments
doMoveStart:
mov r10b, [gamemap + ebx] ; r10b = value of tile at index
; Check if current tile is falling
and r10b, [statusmask]
cmp r10b, 1
jne doMoveIncDec
; Current tile is falling so move it
mov r10b, [gamemap + ebx]
mov byte [gamemap + ebx], 0
add ebx, edi ; move the index left or right
mov byte [gamemap + ebx], r10b
doMoveIncDec:
add ebx, r11d
cmp ebx, ecx
je doMoveEnd
cmp ebx, -1
je doMoveEnd
jmp doMoveStart
doMoveEnd:
; All tiles moved so also move pivot if its non negative
mov ecx, [pivot_index]
cmp ecx, 0
jl returnMoveLeftRight
add ecx, edi
mov [pivot_index], ecx
; Return
returnMoveLeftRight:
pop r13
pop r12
pop rbx
pop rbp
ret
; ---------- Moves all falling tiles left or right if possible ----------
; ---------- Clears all rows that are full, and updates score ----------
; int ClearRows()
; return 1 if stopped tile found on top two rows (game end)
; else return 0
ClearRows:
push rbp
mov rbp, rsp
push rbx
push r12
push r13
push r14
push r15
mov r14d, 1 ; used for cmovs
; Loop over each row from bottom to top
xor r12d, r12d ;r12d = number of rows cleared
mov ebx, [height] ; ebx = row (checking rows loop)
dec ebx
loopRowsStart:
; Loop over each column in current row
xor ecx, ecx ; ecx = col
xor r8d, r8d ; r8d = number of stopped tiles per row
mov eax, [width]
mul ebx ; eax = (width * row)
loopColsStart:
; Get the value of the current tile
mov r9b, [gamemap + eax] ; r9b = value of current tile
inc eax
; Check if this tile is stopped
xor r10d, r10d
cmp r9b, 0
cmovne r10d, r14d ; r10d = 1 if tile is not empty
and r9b, [statusmask]
xor r11d, r11d
cmp r9b, 1
cmovne r11d, r14d ; r11d = 1 if tile is not falling
and r10d, r11d ; r10d = 1 if tile is stopped
cmp r10d, 1
jne loopColsInc
; Increment number of stopped tiles in this row if tile is stopped
inc r8d
; Return 1 if this tile is stopped on the top two rows
mov r15d, [width]
add r15d, r15d ; 15d = smallest non game ending stopped index
inc r15d
cmp eax, r15d
jge loopColsInc
mov eax, 1
jmp returnClearRows
loopColsInc:
inc ecx
cmp ecx, [width]
jl loopColsStart
; Check if stopped == width for this row
cmp r8d, [width]
jne loopRowsDec
; Increment number of rows cleared
inc r12d
; Clear current row
xor ecx, ecx
mov eax, [width]
mul ebx ; eax = (row * width)
clearRowStart:
mov byte [gamemap + eax], 0 ; clear this tile
inc eax
clearRowInc:
inc ecx
cmp ecx, [width]
jl clearRowStart
; Drop every tile of every row down by one, starting at row above current row
mov r13d, ebx ; r13d = row (dropping rows loop)
dec r13d
dropRowsStart:
; Loop over each column of this row and drop the tile down by one
xor ecx, ecx
mov eax, [width]
mul r13d ; eax = (row * width)
dropColsStart:
mov r9b, [gamemap + eax]
mov byte [gamemap + eax], 0 ; clear current tile
mov edx, eax
add edx, [width] ; edx = (row * width) + width ie. one tile down
mov [gamemap + edx], r9b ; drop current tile down one row
inc eax
dropColsInc:
inc ecx
cmp ecx, [width]
jl dropColsStart
dropRowsDec:
dec r13d
cmp r13d, 0
jge dropRowsStart
inc ebx ; check this row again because we dropped above row into it
loopRowsDec:
dec ebx
cmp ebx, 0
jge loopRowsStart
; Update score
mov ecx, [total_score]
xor edx, edx
; Add correct score if cleared no lines
cmp r12d, 0
mov ebx, [block_placed]
cmove edx, ebx
; Add correct score if cleared exactly one line
cmp r12d, 1
mov ebx, [lineclear1]
cmove edx, ebx
; Add correct score if cleared exactly two lines
cmp r12d, 2
mov ebx, [lineclear2]
cmove edx, ebx
; Add correct score if cleared exactly three lines
cmp r12d, 3
mov ebx, [lineclear3]
cmove edx, ebx
; Add correct score if cleared exactly four lines
cmp r12d, 4
mov ebx, [lineclear4]
cmove edx, ebx
; Add to the total score
add ecx, edx
mov [total_score], ecx
; Return
xor eax, eax
returnClearRows:
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
; ---------- Clears all rows that are full, and updates score ----------
; ---------- Stops all falling tiles if at least one should be stopped, otherwise drops all falling down one tile ----------
; int StopDropAllFalling()
; output = 1 if falling were stopped
; output = 0 if falling were not stopped
StopDropAllFalling:
push rbp
mov rbp, rsp
push rbx
; Loop over the buffer
xor r9d, r9d
mov ecx, [gamemap_len]
mov ebx, 0
mov eax, 1
checkFallingLoopStart:
mov dl, [gamemap + ebx]
and dl, [statusmask]
cmp dl, 0
je checkFallingLoopInc ; Next index if dl isn't a falling tile
; This is a falling tile
mov r8d, ecx
sub r8d, [width]
cmp r8d, ebx
cmovle r9d, eax ; Stop all falling if this tile is on last row
jl checkFallingLoopEnd
mov r10d, ebx
add r10d, [width]
mov r8b, [gamemap + r10d]
xor r10d, r10d
cmp r8b, 0
cmovne r10d, eax ; r10d = 1 if tile below is not empty
and r8b, [statusmask]
xor r11d, r11d
cmp r8b, 0
cmove r11d, eax ; r11d = 1 if tile below is not falling
and r10d, r11d ; r10d = 1 if tile below this is stopped
cmp r10d, 1
cmove r9d, eax
je checkFallingLoopEnd ; Stop all falling if there is stopped tile below this tile
checkFallingLoopInc:
inc ebx
cmp ebx, ecx
jne checkFallingLoopStart
checkFallingLoopEnd:
; If we must, loop over the gamemap and stop all falling tiles
cmp r9d, 0
je stopFallingLoopEnd
mov ebx, ecx
dec ebx
stopFallingLoopStart:
mov dl, [gamemap + ebx]
and dl, [statusmask]
cmp dl, 0
je stopFallingLoopDec ; Next index if dl is not falling
; This tile is falling, so stop it
mov dl, [gamemap + ebx]
dec dl
mov [gamemap + ebx], dl
stopFallingLoopDec:
dec ebx
cmp ebx, 0
jge stopFallingLoopStart
stopFallingLoopEnd:
; If we don't stop falling tiles, loop over gamemap (from bottom up) and drop all falling by one
cmp r9d, 1
je dropFallingLoopEnd
mov ebx, ecx
dec ebx
dropFallingLoopStart:
mov dl, [gamemap + ebx]
and dl, [statusmask]
cmp dl, 0
je dropFallingLoopDec ; Next index if dl is not falling
; This tile is falling so drop it
mov dl, [gamemap + ebx]
mov byte [gamemap + ebx], 0
mov eax, ebx
add eax, [width]
mov [gamemap + eax], dl
dropFallingLoopDec:
dec ebx
cmp ebx, 0
jge dropFallingLoopStart
dropFallingLoopEnd:
; Return
mov eax, r9d
pop rbx
pop rbp
ret
; ---------- Stops all falling tiles if at least one should be stopped, otherwise drops all falling down one tile ----------
; ---------- Allocates all XColor structs ----------
; void AllocateColors()
AllocateColors:
push rbp
mov rbp, rsp
; XAllocNamedColor(display, colormap, "teal", xcol_struct.teal, xcol_struct.temp)
mov rdi, [display]
mov rsi, [colormap]
mov rdx, teal
mov rcx, xcols
add rcx, xcol_struct.teal
mov r8, xcols
add r8, xcol_struct.temp
call XAllocNamedColor
; XAllocNamedColor(display, colormap, "blue", xcol_struct.blue, xcol_struct.temp)
mov rdi, [display]
mov rsi, [colormap]
mov rdx, blue
mov rcx, xcols
add rcx, xcol_struct.blue
mov r8, xcols
add r8, xcol_struct.temp
call XAllocNamedColor
; XAllocNamedColor(display, colormap, "orange", xcol_struct.orange, xcol_struct.temp)
mov rdi, [display]
mov rsi, [colormap]
mov rdx, orange
mov rcx, xcols
add rcx, xcol_struct.orange
mov r8, xcols
add r8, xcol_struct.temp
call XAllocNamedColor
; XAllocNamedColor(display, colormap, "yellow", xcol_struct.yellow, xcol_struct.temp)
mov rdi, [display]
mov rsi, [colormap]
mov rdx, yellow
mov rcx, xcols
add rcx, xcol_struct.yellow
mov r8, xcols
add r8, xcol_struct.temp
call XAllocNamedColor
; XAllocNamedColor(display, colormap, "green", xcol_struct.green, xcol_struct.temp)
mov rdi, [display]
mov rsi, [colormap]
mov rdx, green
mov rcx, xcols
add rcx, xcol_struct.green
mov r8, xcols
add r8, xcol_struct.temp
call XAllocNamedColor
; XAllocNamedColor(display, colormap, "red", xcol_struct.red, xcol_struct.temp)
mov rdi, [display]
mov rsi, [colormap]
mov rdx, red
mov rcx, xcols
add rcx, xcol_struct.red
mov r8, xcols
add r8, xcol_struct.temp
call XAllocNamedColor
; XAllocNamedColor(display, colormap, "purple", xcol_struct.purple, xcol_struct.temp)
mov rdi, [display]
mov rsi, [colormap]
mov rdx, purple
mov rcx, xcols
add rcx, xcol_struct.purple
mov r8, xcols
add r8, xcol_struct.temp
call XAllocNamedColor
; return
pop rbp
ret
; ---------- Allocates all XColor structs ----------
; ---------- Draws gamemap to screen ----------
; void DrawGamemap()
DrawGamemap:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push r15
push rbx
; constant r12d = length of gamemap
mov r12d, [gamemap_len]
; ebx = index (skips first two rows of gamemap)
mov ebx, [width]
add ebx, ebx
; Loop over the buffer and draw the correct colour at the right pixels
; Skip first two rows of gamemap because they are above the screen
drawingLoopStart:
; r13d = x_pxl
mov eax, [width]
mov ecx, [tile_len]
mul ecx
mov ecx, eax ; ecx = width * tile_len
mov eax, ebx
mov r8d, [tile_len]
mul r8d ; eax = index * tile_len
xor rdx, rdx
div ecx ; edx = (index * tile_len) % (width * tile_len)
mov r13d, edx ; r13d = (index * tile_len) % (width * tile_len)
; r14d = y_pxl
mov eax, 2
mov ecx, [tile_len]
mul ecx
mov ecx, eax ; ecx = 2 * tile_len
mov eax, ebx
mov r8d, [width]
xor rdx, rdx
div r8d ; eax = index / width
mov r8d, [tile_len]
mul r8d ; eax = (index / width) * tile_len
sub eax, ecx ; eax = ((index / width) * tile_len) - (2 * tile_len)
mov r14d, eax ; r14d = ((index / width) * tile_len) - (2 * tile_len)
; If gamemap[index] == 0 draw with gc_black, Else draw with gc_color
mov r15d, [gc_color]
mov cl, [gamemap + ebx]
cmp cl, 0
cmove r15d, [gc_black]
je draw
; If gamemap[index] != 0, set r11 to correct offset multiple into xcols struct
mov dl, cl
and dl, [tealmask]
cmp dl, 0
mov eax, 0
cmovne r11d, eax ; Set r11 to 0 if tile teal
jne setColor
mov dl, cl
and dl, [bluemask]
cmp dl, 0
mov eax, 1
cmovne r11d, eax ; Set r11 to 1 if tile blue
jne setColor
mov dl, cl
and dl, [orangemask]
cmp dl, 0
mov eax, 2
cmovne r11d, eax ; Set r11 to 2 if tile orange
jne setColor
mov dl, cl
and dl, [yellowmask]
cmp dl, 0
mov eax, 3
cmovne r11d, eax ; Set r11 to 3 if tile yellow
jne setColor
mov dl, cl
and dl, [greenmask]
cmp dl, 0
mov eax, 4
cmovne r11d, eax ; Set r11 to 4 if tile green
jne setColor
mov dl, cl
and dl, [redmask]
cmp dl, 0
mov eax, 5
cmovne r11d, eax ; Set r11 to 5 if tile red
jne setColor
mov dl, cl
and dl, [purplemask]
cmp dl, 0
mov eax, 6
cmovne r11d, eax ; Set r11 to 6 if tile purple
setColor:
; void XSetForeground(display, gc_draw, XColor.pixel)
mov eax, 16
mul r11d
mov rdi, [display]
mov rsi, r15
mov rdx, [xcols + eax] ; offsetof(XColor, pixel) == 0
sub rsp, 8
call XSetForeground
add rsp, 8
draw:
; void XDrawRectangle(display, window, gc, x_pxl, y_pxl, tile_len, tile_len)
mov rdi, [display]
mov rsi, [win]