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
|
section .data
; array of snake - 500 bytes means max length is 250
; x of i-th part is at snake[2*i]
; y of i-th part is at snake[2*i+1]
snake times 500 db 0
snake_length db 3
; direction of the snake:
; 3
; 2 x 0
; 1
global direction
direction db 0
; delay of the game loop (0.25s in the beginning)
global timespec
timespec dq 0
dq 250000000
section .rodata
; width and height of the playable area
; (without the borders)
global width
global height
width db 25
height db 10
section .text
extern write_game_over_message
extern start_screen
extern fruit_x
extern fruit_y
extern spawn_fruit
extern draw_fruit
extern input
extern draw_border
extern write_byte
extern clear_screen
extern reset_cursor
extern move_cursor_right
extern move_cursor_down
extern hide_cursor
extern show_cursor
extern read_input
extern set_terminal_options
extern restore_terminal_options
global _start
global exit
spawn_tail:
inc byte [snake_length]
xor rdx, rdx
mov dl, byte [snake_length]
mov ch, byte [snake+(2*rdx)-4]
mov cl, byte [snake+(2*rdx)-3]
mov byte [snake+(2*rdx)-2], ch
mov byte [snake+(2*rdx)-1], cl
mov ah, byte [direction]
cmp ah, 0
jz _spawn_left
cmp ah, 2
jz _spawn_right
cmp ah, 3
jz _spawn_down
cmp ah, 1
jz _spawn_up
ret ; should not reach this line
_spawn_right:
inc byte [snake+(2*rdx)-2]
ret
_spawn_down:
inc byte [snake+(2*rdx)-1]
ret
_spawn_left:
dec byte [snake+(2*rdx)-2]
ret
_spawn_up:
dec byte [snake+(2*rdx)-1]
ret
draw_snake:
push rbx
;;push r12
xor rdx, rdx
push rdx
call reset_cursor
pop rdx
_12:
mov bh, byte [snake+(2*rdx)]
mov bl, byte [snake+(2*rdx)+1]
_10:push rdx
call move_cursor_right
pop rdx
dec bh
cmp bh, 0
jnz _10
_11:push rdx
call move_cursor_down
pop rdx
dec bl
cmp bl, 0
jnz _11
push rdx
mov rax, 'X'
call write_byte
call reset_cursor
pop rdx
inc rdx
cmp dl, byte [snake_length]
jnz _12
;;pop r12
pop rbx
ret
move_snake:
xor rdx, rdx
mov dl, byte [snake_length]
_move_body:
cmp dl, 1
jz _move_head
mov ah, byte [snake+(2*rdx)-4]
mov al, byte [snake+(2*rdx)-3]
mov byte [snake+(2*rdx)-2], ah
mov byte [snake+(2*rdx)-1], al
dec dl
jmp _move_body
_move_head:
mov ah, byte [width]
mov al, byte [height]
cmp byte [direction], 0
je _move_right
cmp byte [direction], 1
je _move_down
cmp byte [direction], 2
je _move_left
cmp byte [direction], 3
je _move_up
ret ; should not be accessed
_move_right:
cmp byte [snake], ah
jz _to_left_border
inc byte [snake]
ret
_move_down:
cmp byte [snake+1], al
jz _to_top_border
inc byte [snake+1]
ret
_move_left:
cmp byte [snake], 1
jz _to_right_border
dec byte [snake]
ret
_move_up:
cmp byte [snake+1], 1
jz _to_bottom_border
dec byte [snake+1]
ret
_to_left_border:
mov byte [snake], 1
ret
_to_right_border:
mov al, byte [width]
mov byte [snake], al
ret
_to_top_border:
mov byte [snake+1], 1
ret
_to_bottom_border:
mov al, byte [height]
mov byte [snake+1], al
ret
check_eat_fruit:
mov ah, byte [fruit_x]
mov al, byte [fruit_y]
cmp byte [snake], ah
jz _check_y
ret
_check_y:
cmp byte [snake+1], al
jz _same_position
ret
_same_position:
; get longer
call spawn_tail
call spawn_fruit
ret
check_loose:
xor rcx, rcx
mov cl, byte [snake_length]
mov ah, byte [snake]
mov al, byte [snake+1]
_loop_through_snake:
cmp rcx, 4
jle _loop_return
mov dh, byte [snake+(2*rcx)-2]
mov dl, byte [snake+(2*rcx)-1]
dec rcx
cmp ah, dh
jnz _loop_through_snake
cmp al, dl
jnz _loop_through_snake
jmp game_over
_loop_return:
ret
_start:
call set_terminal_options
call clear_screen
call draw_border
call hide_cursor
call start_screen
_start_game:
; set starting positions
mov byte [snake], 5
mov byte [snake+1], 5
mov byte [snake+2], 4
mov byte [snake+3], 5
mov byte [snake+4], 3
mov byte [snake+5], 5
mov byte [snake_length], 3
mov byte [direction], 0
call spawn_fruit
main_loop:
call clear_screen
call draw_border
call draw_snake
call draw_fruit
mov rax, 35
lea rdi, [timespec]
xor rsi, rsi
syscall
call input
call move_snake
call check_loose
call check_eat_fruit
jmp main_loop
game_over:
call write_game_over_message
mov rax, 35
lea rdi, [timespec]
xor rsi, rsi
syscall
call read_input ; writes input byte into al
cmp al, 32 ; space
je _start_game
cmp al, 'q' ; escape
je exit
jmp game_over
exit: ; exit syscall with return code 0
call show_cursor
call clear_screen
call restore_terminal_options
mov rax, 60
xor rdi, rdi
syscall
|