-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathothello.asm
1326 lines (1057 loc) · 21 KB
/
othello.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
; Copyright 2024 - wowvain-dev (Bogdan Stanciu)
; MIT License
section .bss
board resb 64
input_buffer resb 2
section .data
COLOR_BLACK db 0
COLOR_RED db 1
COLOR_GREEN db 2
COLOR_YELLOW db 3
COLOR_BLUE db 4
COLOR_MAGENTA db 5
COLOR_CYAN db 6
COLOR_WHITE db 7
white_score db 0
black_score db 0
; the number of consecutive "forfeits" that took place. if we reach two, its game over since nobody is able to move
skips db 0
current_move db 2
opponent_move db 1
directions db -1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, -1, -1, 1, 1, 1
; Left, Right, Up, Down, Top-Left, Top-Right Bottom-left Bottom-right
coords db "%d %d", 0
walls db "| | | | | | | | |", 0
row_separator db "+---------+---------+---------+---------+---------+---------+---------+---------+", 0
black_piece db "X", 0
white_piece db "O", 0
int_format db "%d", 0
char_format db "%c", 0
string_format db "%s", 0
title db "Othello", 0
nl db "\n", 0
score db "Score:", 0
b_count db "Black pieces: %d", 0
w_count db "White pieces: %d", 0
keybinds db "Keybinds:", 0
f1_help db "P - Help", 0
f2_about db "O - About", 0
i_enter db "I - Enter Move", 0
q_exit db "Q - Exit", 0
player db "CURRENT PLAYER: %s", 0
player_black db "BLACK(X)", 0
player_white db "WHITE(O)", 0
over db " _____________", 0
over2 db "/ \", 0
over3 db "| GAME OVER! |", 0
over4 db "\_____________/", 0
over_black db "BLACK won", 0
over_white db "WHITE won", 0
over_draw db "DRAW", 0
over_prompt db "Press any key.", 0
sure db "Are you sure you want to exit? [y/n]"
sure_end db 0 ; used to get sure string length
input db "Enter your move (ex: a3, ONLY LOWERCASE, ESC to cancel): __", 0
input_end db 0 ; used to get input string length
bad_move_input db "BAD_MOVE: Input, X coord = (a-h); y coord = (1-8). Enter to input again", 0
already_placed db "BAD_MOVE: There is already a placed piece at your coords. Enter to input again", 0
invalid_move db "BAD_MOVE: Coordinates are correct, but they don't respect the game's rules. Check <HELP>. Enter to input again.", 0
help_title db "HELP MENU", 0
about_title db "ABOUT", 0
about_app db "This program has been developed for the CSE1400 Lab Game Assignment by Bogdan Stanciu. It uses ncurses as a backend for the terminal user interface and libc because ncurses requires it.", 0
about_app_2 db "It was been programmed using the NASM language.", 0
about_app_3 db "Copyright (c) 2024 - Bogdan Stanciu (wowvain-dev)", 0
help_link db "For a more complete description of the game, I recommend https://www.worldothello.org/about/about-othello/othello-rules/official-rules/english", 0
help_info_1 db "A move consists of placing a disc corresponding to your assigned color and flipping all the discs between the placed ones and the first disc that already has your color in any of the 8 directions (you cannot skip over blank cells).", 0
help_info_2 db "1. Black always moves first", 0
help_info_3 db "2. If on your turn you cannot flip at least one opposing disc, your turn is forfeited and your opponent moves again. If there is any move available, you are not allowed to skip your turn.", 0
help_info_4 db "3. Players may not skip over their own color discs in order to outflank an opposing disc. (so you only flip the discs until the FIRST disc of your color in every direction)", 0
help_info_5 db "4. Discs may only be outflanked as a direct result of a move, so you don't recursively check for each disk you flipped if they also outflank other discs."
help_info_6 db "5. All outflanked discs must be flipped, even if it could be in the player's disadvantage.", 0
help_info_7 db "6. Once a disc is placed, it can never be moved, only flipped.", 0
help_info_8 db "7. When neither player has a legal move left, the game ends.", 0
help_info_9 db "NOTE: It is possible (and likely) for a game to end before all 64 squares are filled.", 0
menu_controls db "Q - Go back to game", 0
section .text
global main ; Define the entry point as 'main'
extern initscr ; NCurses function to initialize screen
extern printw ; NCurses function to print text
extern refresh ; NCurses function to refresh screen
extern getch ; NCurses function to wait for input
extern endwin ; NCurses function to end NCurses mode
extern mvprintw
extern move
extern clrtoeol
extern clear
extern nonl
extern noecho
extern start_color
extern COLOR_PAIR
extern attron
extern attroff
extern init_pair
main:
push rbp
mov rbp, rsp
; Initialize NCurses
call initscr
call start_color
; general color setup
mov rdi, 1
movzx rsi, byte [COLOR_YELLOW]
movzx rdx, byte [COLOR_BLACK]
call init_pair
; color setup for black pieces
mov rdi, 2
movzx rsi, byte [COLOR_MAGENTA]
movzx rdx, byte [COLOR_BLACK]
call init_pair
; color setup for white pieces
mov rdi, 3
movzx rsi, byte [COLOR_BLUE]
movzx rdx, byte [COLOR_BLACK]
call init_pair
mov rdi, 4
movzx rsi, byte [COLOR_CYAN]
movzx rdx, byte [COLOR_BLACK]
call init_pair
mov rdi, 5
movzx rsi, byte [COLOR_CYAN]
movzx rdx, byte [COLOR_BLACK]
call init_pair
; Clearing the board buffer
call clear_board
call game_loop
; End NCurses mode
call endwin
mov rsp, rbp
pop rbp
; Exit the program
mov rax, 60 ; syscall: exit
xor rdi, rdi ; status: 0
syscall
draw_walls:
push rbp
mov rbp, rsp
mov rdx, walls
call mvprintw
mov rsp, rbp
pop rbp
ret
draw_separator:
push rbp
mov rbp, rsp
mov rdx, row_separator
call mvprintw
mov rsp, rbp
pop rbp
ret
draw_board:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push r15
mov r12, 2
mov r13, 2
mov rdi, 1
call COLOR_PAIR
mov rsi, rax
call attron
.loop:
mov rdi, r12
mov rsi, r13
call draw_separator
add r12, 1
mov rdi, r12
mov rsi, r13
call draw_walls
add r12, 1
mov rdi, r12
mov rsi, r13
call draw_walls
add r12, 1
mov rdi, r12
mov rsi, r13
call draw_walls
add r12, 1
cmp r12, 34
jl draw_board.loop
mov rdi, 34
mov rsi, 2
call draw_separator
mov r14, 4
mov r13, 1
mov r12, 7
.loop_coords:
mov rdi, r14
mov rsi, 0
mov rdx, int_format
mov rcx, r13
call mvprintw
mov rsi, r12
mov rdi, 1
mov rdx, char_format
mov r8, r13
add r8, 64
mov rcx, r8
call mvprintw
add r13, 1
add r12, 10
add r14, 4
cmp r13, 8
jle draw_board.loop_coords
mov rdi, 35
mov rsi, 2
mov rdx, player
cmp byte [current_move], 2
je draw_board.black_move
cmp byte [current_move], 1
je draw_board.white_move
.black_move:
mov rcx, player_black
call mvprintw
jmp draw_board.go_pieces
.white_move:
mov rcx, player_white
call mvprintw
jmp draw_board.go_pieces
.go_pieces:
mov rdi, 1
call COLOR_PAIR
mov rsi, rax
call attroff
call draw_pieces
pop r15
pop r14
pop r13
pop r12
mov rsp, rbp
pop rbp
ret
draw_pieces:
push rbp
mov rbp, rsp
push r12
push r13
mov r12, 0
.loop_rows:
mov r13, 0
.loop_cols:
mov rdi, r13
mov rsi, r12
call draw_cell
add r13, 1
cmp r13, 8
jl .loop_cols
add r12, 1
cmp r12, 8
jl .loop_rows
call draw_menu
pop r13
pop r12
mov rsp, rbp
pop rbp
ret
clear_board:
push rbp
mov rbp, rsp
mov rcx, 0
.loop:
mov byte [board + rcx], 0
add rcx, 1
cmp rcx, 64
jl clear_board.loop
mov rsp, rbp
pop rbp
ret
; The function that draws the value of a specific cell
; params:
; - Y_coord (RDI)
; - X_coord (RSI)
draw_cell:
push rbp
mov rbp, rsp
push r12
push r13
mov rax, rdi
shl rax, 3
add rax, rsi
movzx r12, byte [board + rax]
cmp r12, 0
je draw_cell.epilogue
mov rax, 4
mul rdi
mov rdi, rax
mov rax, 10
mul rsi
mov rsi, rax
add rsi, 7
add rdi, 4
cmp r12, 1
je draw_cell.white
cmp r12, 2
je draw_cell.black
.white:
push rdi
push rsi
push rdx
mov rdi, 3
call COLOR_PAIR
mov rsi, rax
call attron
pop rdx
pop rsi
pop rdi
mov rdx, white_piece
call mvprintw
push rdi
push rsi
push rdx
mov rdi, 3
call COLOR_PAIR
mov rsi, rax
call attroff
pop rdx
pop rsi
pop rdi
jmp draw_cell.print
.black:
push rdi
push rsi
push rdx
mov rdi, 2
call COLOR_PAIR
mov rsi, rax
call attron
pop rdx
pop rsi
pop rdi
mov rdx, black_piece
call mvprintw
push rdi
push rsi
push rdx
mov rdi, 3
call COLOR_PAIR
mov rsi, rax
call attroff
pop rdx
pop rsi
pop rdi
call mvprintw
jmp draw_cell.print
.print:
.epilogue:
pop r13
pop r12
mov rsp, rbp
pop rbp
ret
draw_menu:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push r15
mov rdi, 4
call COLOR_PAIR
mov rsi, rax
call attron
mov rdi, 3
mov rsi, 93
mov rdx, title
call mvprintw
mov rdi, 6
mov rsi, 88
mov rdx, keybinds
call mvprintw
mov rdi, 7
mov rsi, 90
mov rdx, i_enter
call mvprintw
mov rdi, 8
mov rsi, 90
mov rdx, f1_help
call mvprintw
mov rdi, 9
mov rsi, 90
mov rdx, f2_about
call mvprintw
mov rdi, 10
mov rsi, 90
mov rdx, q_exit
call mvprintw
mov rdi, 13
mov rsi, 88
mov rdx, score
call mvprintw
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15
.score_loop:
movzx r14, byte [board + r15]
cmp r14, 1
je draw_menu.white
cmp r14, 2
je draw_menu.black
jmp draw_menu.end
.white:
add r12, 1
jmp draw_menu.end
.black:
add r13, 1
jmp draw_menu.end
.end:
inc r15
cmp r15, 64
jl draw_menu.score_loop
mov byte [white_score], r12b
mov byte [black_score], r13b
mov rdi, 14
mov rsi, 90
call move
call clrtoeol
mov rdi, 14
mov rsi, 90
mov rdx, w_count
xor rcx, rcx
movzx rcx, r12b
call mvprintw
mov rdi, 15
mov rsi, 90
call move
call clrtoeol
mov rdi, 15
mov rsi, 90
mov rdx, b_count
xor rcx, rcx
movzx rcx, r13b
call mvprintw
mov rdi, 4
call COLOR_PAIR
mov rsi, rax
call attroff
pop r15
pop r14
pop r13
pop r12
mov rsp, rbp
pop rbp
ret
game_loop:
push rbp
mov rbp, rsp
; Initial board state
mov byte [board + (3*8+3)], 1
mov byte [board + (3*8+4)], 2
mov byte [board + (4*8+3)], 2
mov byte [board + (4*8+4)], 1
.loop:
; Draw the Othello board
call draw_board
; Refresh the screen to show the board
call refresh
mov r8b, byte [white_score]
mov r9b, byte [black_score]
add r8b, r9b
cmp r8b, 64
jge game_loop.over
call valid_moves
cmp rax, 0
jne game_loop.keep_round
inc byte [skips]
cmp byte [skips], 2,
jge game_loop.over
call switch_player
jmp game_loop.loop
.keep_round:
mov rdi, 36
mov rsi, 2
call move
; Wait for a key press
call getch
cmp rax, 80
jne game_loop.help
call help_menu
jmp game_loop.loop
.help:
cmp rax, 112
jne game_loop.help_skip
call help_menu
jmp game_loop.loop
.help_skip:
cmp rax, 79
jne game_loop.about
call about_menu
jmp game_loop.loop
.about:
cmp rax, 111
jne game_loop.skip_menu
call about_menu
jmp game_loop.loop
.skip_menu:
cmp rax, 73
je game_loop.input_move
cmp rax, 105
je game_loop.input_move
cmp rax, 81
je game_loop.ask_exit
cmp rax, 113
je game_loop.ask_exit
jmp game_loop.loop
.over:
mov rdi, 5
call COLOR_PAIR
mov rsi, rax
call attron
mov rdi, 19
mov rsi, 89
mov rdx, over
call mvprintw
mov rdi, 20
mov rsi, 89
mov rdx, over2
call mvprintw
mov rdi, 21
mov rsi, 89
mov rdx, over3
call mvprintw
mov rdi, 22
mov rsi, 89
mov rdx, over4
call mvprintw
xor r12, r12
xor r13, r13
mov r12b, byte [black_score]
mov r13b, byte [white_score]
mov r10, over_black
mov r11, over_white
mov r8, over_draw
mov rdi, 23
mov rsi, 92
cmp r12b, r13b
cmovl rdx, r11
cmovg rdx, r10
cmove rdx, r8
call mvprintw
mov rdi, 25
mov rsi, 89
mov rdx, over_prompt
call mvprintw
mov rdi, 5
call COLOR_PAIR
mov rsi, rax
call attroff
call getch
jmp game_loop.exit
.input_move:
call get_input
jmp game_loop.loop
.ask_exit:
mov rdi, 36
mov rsi, 2
mov rdx, sure
call mvprintw
.repeat_exit:
mov rdi, 36
mov rsi, sure_end - sure
add rsi, 2
call move
call getch
; y / Y
cmp rax, 121
je game_loop.exit
cmp rax, 089
je game_loop.exit
; n / N
cmp rax, 110
jne game_loop.lower_n
mov rdi, 36
mov rsi, 0
call move
call clrtoeol
jmp game_loop.loop
.lower_n:
cmp rax, 78
jne game_loop.repeat_exit
mov rdi, 36
mov rsi, 0
call move
call clrtoeol
jmp game_loop.loop
.exit:
mov rsp, rbp
pop rbp
ret
get_input:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push r15
.retry:
mov rdi, 36
mov rsi, 0
call move
call clrtoeol
mov rdi, 37
mov rsi, 0
call move
call clrtoeol
mov rdi, 38
mov rsi, 0
call move
call clrtoeol
mov rdi, 36
mov rsi, 2
mov rdx, input
call mvprintw
mov rdi, 36
mov rsi, input_end - input - 1
call move
call getch
cmp rax, 27
je get_input.exit
cmp rax, 97
jl get_input.bad_input
cmp rax, 104
jg get_input.bad_input
; converting coord to number
sub rax, 97
mov r12, rax
call getch
cmp rax, 27
je get_input.exit
cmp rax, 49
jl get_input.bad_input
cmp rax, 56
jg get_input.bad_input
; converting coord to number
sub rax, 49
mov r13, rax
jmp get_input.good_input
.bad_input:
mov rdi, 37
mov rsi, 2
mov rdx, bad_move_input
call mvprintw
call getch
cmp rax, 10
je get_input.retry
jmp get_input.bad_input
.already_placed:
mov rdi, 37
mov rsi, 2
mov rdx, already_placed
call mvprintw
call getch
cmp rax, 10
je get_input.retry
jmp get_input.already_placed
.invalid_move:
mov rdi, 37
mov rsi, 2
mov rdx, invalid_move
call mvprintw
call getch
cmp rax, 10
je get_input.retry
jmp get_input.invalid_move
.good_input:
xor r14, r14
movzx r14, byte [board+(r13*8 + r12)]
cmp r14, 0
jne get_input.already_placed
; at this point we are sure the input is valid
; from a coordinate or pre-placement perspective,
; now lets check if is a valid move
mov rdi, r12
mov rsi, r13
call validate_move
cmp rax, 0
je get_input.invalid_move
cmp rax, 1
jne get_input.switch_player
mov r14b, byte [current_move]
mov byte[board + (rsi*8 + rdi)], r14b
.switch_player:
call switch_player
.exit:
mov rdi, 36
mov rsi, 0
call move
call clrtoeol
pop r15
pop r14
pop r13
pop r12
mov rsp, rbp
pop rbp
ret
; Input: col (rdi), row (rsi), direction_index (rdx)
; Output: valid_move (rax), if valid move found, flips pieces along the way
check_direction:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push r15
mov r15, 0 ; start with invalid assumption
; Load directions
movzx r12, byte [directions + rdx * 2]
movzx r13, byte [directions + (rdx * 2 + 1)]
mov r8, rsi ; current row
mov r9, rdi ; current col
.check_loop:
cmp r13, 0xff
je check_direction.neg_r13
add r8, r13 ; next row in the given direction
jmp check_direction.r12
.neg_r13:
sub r8, 1
.r12:
cmp r12, 0xff
je check_direction.neg_r12
add r9, r12 ; next col in the given direction
jmp check_direction.borders
.neg_r12:
sub r9, 1
.borders:
; checking if we reach the end of the board
cmp r8, 0
jl check_direction.end_check
cmp r8, 7
jg check_direction.end_check
cmp r9, 0
jl check_direction.end_check
cmp r9, 7
jg check_direction.end_check
movzx r14, byte [board + (r8*8 + r9)]
cmp r14b, byte [current_move]
je check_direction.flip_pieces
mov r10, 0
cmp r14b, 0
cmove r15, r10
je check_direction.end_check
mov r11, 1
cmp r14b, byte [opponent_move]
cmove r15, r11
je check_direction.check_loop
jmp check_direction.end_check
.flip_pieces:
cmp r13, 0xff
je check_direction.neg_r13_2
sub r8, r13
jmp check_direction.r12_2
.neg_r13_2:
add r8, 1
.r12_2:
cmp r12, 0xff
je check_direction.neg_r12_2
sub r9, r12
jmp check_direction.borders_2
.neg_r12_2:
add r9, 1
.borders_2:
cmp r8, rsi
jne check_direction.flip
cmp r9, rdi
jne check_direction.flip
;xor r14, r14
;movzx r14, byte [current_move]
;mov [board + (r8*8 + r9)], r14b
jmp check_direction.end_check
.flip:
xor r14, r14
movzx r14, byte [current_move]
mov [board + (r8*8 + r9)], r14b
jmp check_direction.flip_pieces
.end_check:
mov rax, r15
pop r15
pop r14
pop r13
pop r12
mov rsp, rbp
pop rbp
ret
; Input: col (rdi), row (rsi)
; Output: valid_move (rax), if valid move found, flips pieces along the way
validate_move:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push r15
mov rdx, 0 ; the direction index
mov r15, 0 ; assume invalid move
.check_next_dir:
call check_direction
cmp rax, 1
cmove r15, rax
inc rdx
cmp rdx, 8
jl validate_move.check_next_dir
mov rax, r15
pop r15
pop r14
pop r13
pop r12