-
Notifications
You must be signed in to change notification settings - Fork 7
/
brainfuck.c
212 lines (168 loc) · 5.28 KB
/
brainfuck.c
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
#include <stdio.h> /* printf(), fprintf() */
#include <string.h> /* memset() */
#include <lightning.h>
/* Max nesting of [] */
static const int maxLoopDepth = 32;
/* The code buffer can't be stack allocated.
I haven't checked why but I guess the stack can't
be made executable. */
static jit_insn codeBuffer[1024*64]; /* This is the max number of instructions. */
static int dataBuffer[30000];
/* Return the number of times *c is repeated.
On return *c will contain the next character. */
int opCount(FILE* fp, int* c) {
int n = 1; /* Seen it at least 1 time. */
int cr = *c;
while ((*c = fgetc(fp)) != EOF) {
/* Skip invalid characters. */
if ((*c == '>') ||
(*c == '<') ||
(*c == '+') ||
(*c == '-') ||
(*c == '.') ||
(*c == ',') ||
(*c == '[') ||
(*c == ']')) {
if (*c == cr) {
++n;
} else {
return n;
}
}
}
return n;
}
int main(int argc, char** argv) {
if (argc < 2) {
printf("Usage: %s [infile]\n", argv[0]);
return 0;
}
FILE* fp = fopen(argv[1], "r");
if (!fp) {
fprintf(stderr, "Error: could not open %s\n", argv[1]);
return 1;
}
memset(dataBuffer, 0, sizeof(dataBuffer));
/* We use these as a simple stack to keep track of the loops. */
jit_insn* loopStart[maxLoopDepth];
jit_insn* loopEnd[maxLoopDepth];
int loop = -1;
void (*code)();
/* Writing: code = (void (*)())jit_set_ip(codeBuffer).vptr;
would seem more natural, but the C99 standard leaves
casting from "void *" to a function pointer undefined.
The assignment used below is the POSIX.1-2003 (Technical
Corrigendum 1) workaround. */
*(void **)(&code) = jit_set_ip(codeBuffer).vptr;
/* Save the start of the code buffer. */
char* start = jit_get_ip().ptr;
/* Set up our stack frame and set V0 to our data buffer.
Lightning guarantees that the V* registers won't be overwritten
during function calls. We will use V0 to point to the current
location in our data buffer.
R1 will be used as general purpose register to perform operations on. */
jit_prolog(0);
jit_movi_p(JIT_V0, dataBuffer);
int n;
int c = fgetc(fp);
do {
if (jit_get_ip().ptr > (char*)(codeBuffer + sizeof(codeBuffer))) {
fprintf(stderr, "Error: the program is to big!\n");
return 4;
}
switch (c) {
case '>': {
/* We can't write jit_addi_i(JIT_V0, JIT_V0, opCount(fp, &c))
because the jit_addi_i() macro evaluates it's arguments
multiple times. */
n = opCount(fp, &c);
jit_addi_i(JIT_V0, JIT_V0, n); /* V0 += n */
continue;
}
case '<': {
n = opCount(fp, &c);
jit_subi_i(JIT_V0, JIT_V0, n); /* V0 -= n */
continue;
}
case '+': {
n = opCount(fp, &c);
jit_ldr_c(JIT_R1, JIT_V0); /* R1 = [V0] */
jit_addi_i(JIT_R1, JIT_R1, n); /* R1 += n */
jit_str_c(JIT_V0, JIT_R1); /* [V0] = R1 */
continue;
}
case '-': {
n = opCount(fp, &c);
jit_ldr_c(JIT_R1, JIT_V0); /* R1 = [V0] */
jit_subi_i(JIT_R1, JIT_R1, n); /* R1 -= n */
jit_str_c(JIT_V0, JIT_R1); /* [V0] = R1 */
continue;
}
case '.': {
jit_ldr_c(JIT_R1, JIT_V0); /* R1 = [V0] */
jit_prepare_i(1);
jit_pusharg_i(JIT_R1);
jit_finish(putchar); /* putchar(R1) */
break;
}
case ',': {
jit_prepare(0);
jit_finish(getchar);
jit_retval_c(JIT_R1); /* R1 = getchar() */
jit_str_c(JIT_V0, JIT_R1); /* [V0] = R1 */
break;
}
case '[': {
++loop;
if (loop >= maxLoopDepth) {
fprintf(stderr, "Error: nesting level to deep (max %d)!\n", maxLoopDepth);
return 2;
}
jit_ldr_c(JIT_R1, JIT_V0); /* R1 = [V0] */
/* if (R1 == 0) jump to the ] (which is unknown at this point)
We save the instruction location to patch it when we reach the ] */
loopEnd[loop] = jit_beqi_i(jit_forward(), JIT_R1, 0);
/* Save this location so we can jump to it from the ] */
loopStart[loop] = jit_get_label();
break;
}
case ']': {
if (loop < 0) {
fprintf(stderr, "Error: invalid ] (missing [)\n");
return 3;
}
jit_ldr_c(JIT_R1, JIT_V0); /* R1 = [V0] */
/* if (R1 != 0) jump to the instruction after the [ */
jit_bnei_i(loopStart[loop], JIT_R1, 0);
/* Patch the conditional jump in the [ */
jit_patch(loopEnd[loop]);
--loop;
break;
}
}
c = fgetc(fp);
} while (c != EOF);
fclose(fp);
if (loop > -1) {
fprintf(stderr, "Error: %d unclosed loops!\n", loop + 1);
return 5;
}
/* Unwind our stack frame. */
jit_ret();
/* Make sure the CPU hasn't cached any of the generated instructions. */
jit_flush_code(start, jit_get_ip().ptr);
#if 0
/* This code writes the generated instructions to a file
so we can disassemble it using ndisasm. */
int s = jit_get_ip().ptr - start;
fp = fopen("code.bin", "wb");
int i;
for (i = 0; i < s; ++i) {
fputc(start[i], fp);
}
fclose(fp);
#endif
/* Execute the generated code. */
code();
return 0;
}