-
Notifications
You must be signed in to change notification settings - Fork 0
/
bfx86.asm
240 lines (199 loc) · 4.98 KB
/
bfx86.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
; Oh look, it's a brainfuck interpreter / compiler in x86 ASM. I didn't write
; this with much optimization in mind, to be quite honest.
section .data
; Constants, gotta love 'em. Call codes
SYS_read equ 0
SYS_write equ 1
SYS_open equ 2
SYS_close equ 3
SYS_exit equ 60
; Arguments for syscalls
EXIT_success equ 0
STDIN equ 0
STDOUT equ 1
O_RDONLY equ 000000q
; Interpreter constants
CELL_COUNT equ 30000
BUFFER_SIZE equ 100000
; Character constants
LF db 10
NULL equ 0
section .bss
; Uninitialized data. This is where we store interpreter cells and file data.
cells resb CELL_COUNT
fileBuffer resb BUFFER_SIZE
section .text
global _start
_start:
; The first thing we pop off of the stack is argc. We can use this value to see
; if we need to terminate immediately.
pop rsi
cmp rsi, 2
jne exit
; Okay, we have two arguments. Let's hope that the second one is actually a
; usable argument
pop rdi
skipFirstArg:
; Loads the current byte into rdi, increments rdi, and if it's zero, we move on.
mov al, byte[rdi]
inc rdi
test al, al
jnz skipFirstArg
; At this point, rsi contains the file that needs to be read.
openFile:
mov rax, SYS_open
mov rsi, O_RDONLY
syscall
; If negative, the resulting function should have OF as zero and SF as one But,
; we can't use jl since the jump would be too far, so we use jge instead
push rax
test rax, rax
jge readFile
jmp exit
readFile:
mov rax, SYS_read
pop rdi
lea rsi, byte[fileBuffer]
mov rdx, BUFFER_SIZE
syscall
; Again, check for errors
test rax, rax
jge interpreterStart
jmp exit
interpreterStart:
; We did it reddit! Move a NULL character at the end so that we don't have to
; worry about it later
mov rsi, fileBuffer
mov byte[rsi + rax], NULL
; Initialize all values in the cells array to zero.
mov rax, cells
mov rcx, CELL_COUNT
initializeCells:
mov byte[rax], 0
inc rax
loop initializeCells
; Setup our registers for interpreter action :) rdi contains the file text, rsi
; contains the cell buffer
xor eax, eax
lea rdi, byte[fileBuffer]
lea rsi, byte[cells]
; And now, we can interpret everything character by character
interpreter:
; First, we'll get the character that we intend to use.
mov al, byte[rdi]
; Before we do anything, we set up rcx as a scratch register.
xor ecx, ecx
; Move left?
cmp al, '<'
je move_left
; Move right
cmp al, '>'
je move_right
; Increment current cell?
cmp al, '+'
je increment
; Decrement current cell?
cmp al, '-'
je decrement
; Output current cell?
cmp al, '.'
je output
; Input to current cell
cmp al, ','
je input
; Beginning loop
cmp al, '['
je loop_left_bracket
; Ending loop
cmp al, ']'
je loop_right_bracket
; Unknown character, just go to the end
jmp interpreter_loop_end
move_left:
dec rsi
jmp interpreter_loop_end
move_right:
inc rsi
jmp interpreter_loop_end
increment:
inc byte[rsi]
jmp interpreter_loop_end
decrement:
dec byte[rsi]
jmp interpreter_loop_end
output:
push rdi
mov rax, SYS_write
mov rdi, STDOUT
mov rdx, 1
syscall
pop rdi
jmp interpreter_loop_end
input:
mov al, byte[rdi+1]
test al, al
jz interpreter_loop_end
push rdi
mov rax, SYS_read
mov rdi, STDIN
mov rdx, 1
syscall
pop rdi
jmp interpreter_loop_end
loop_left_bracket:
mov al, byte[rsi]
test al, al
jnz interpreter_loop_end
left_bracket_search:
; We want to find the *matching* right bracket. We use rcx as a counter - [
; increments, ] decrements If we find a ] with the counter equaling zero, that's
; our matching bracket.
inc rdi
mov al, byte[rdi]
cmp al, '['
jne left_bracket_search2
inc rcx
left_bracket_search2:
cmp al, ']'
jne left_bracket_search3
; First, if our counter is zero, we're done
test rcx, rcx
jz interpreter_loop_end
; Otherwise, decrement
dec rcx
left_bracket_search3:
; If we're here, just move on
jmp left_bracket_search
loop_right_bracket:
mov al, byte[rsi]
test al, al
jz interpreter_loop_end
right_bracket_search:
; We want to find the *matching* left bracket. We use rcx as a counter - ]
; increments, [ decrements If we find a [ with the counter equaling zero, that's
; our matching bracket.
dec rdi
mov al, byte[rdi]
cmp al, ']'
jne right_bracket_search2
inc rcx
right_bracket_search2:
cmp al, '['
jne right_bracket_search3
; First, if our counter is zero, we're done
test rcx, rcx
jz interpreter_loop_end
; Otherwise, decrement
dec rcx
right_bracket_search3:
; If we're here, just move on
jmp right_bracket_search
interpreter_loop_end:
inc rdi
mov al, byte[rdi]
test al, al
jnz interpreter
exit:
mov rax, SYS_exit
mov rdi, EXIT_success
syscall