summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--README.md7
-rw-r--r--skullfuck.c53
-rw-r--r--test/hello_world.bf (renamed from examples/hello_world.bf)0
-rw-r--r--test/hello_world_commented.bf (renamed from examples/hello_world_commented.bf)0
-rw-r--r--test/rot13.bf (renamed from examples/rot13.bf)0
-rw-r--r--test/rot13_commented.bf (renamed from examples/rot13_commented.bf)0
7 files changed, 51 insertions, 13 deletions
diff --git a/.gitignore b/.gitignore
index 92436a3..ffee194 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
skullfuck
-test
-test.c
+run_tests
+run_tests.c
diff --git a/README.md b/README.md
index 0f8dc25..ad30ca6 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,12 @@
Skullfuck
=========
-Skullfuck is a dead simple, non-optimizing compiler for Brainfuck, contained in a single C source file. It outputs binaries targeting amd64 Linux.
+Skullfuck is a dead simple, non-optimizing compiler for Brainfuck, contained in a single C source file. It outputs binaries targeting x86_64 and i686 Linux.
It depends on GNU Binutils, or any compatible implementation of `as` and `ld`.
A good explanation of the history and design behind the language can be found in [the Wikipedia article.](https://en.wikipedia.org/wiki/Brainfuck)
-Undefined Behavior
-------------------
-The Brainfuck programming language is extremely limited in its specification, and some operations don't have a defined behavior - specifically, operations on an out-of-bounds data pointer are undefined. UB is allowed, but do not expect it to work equally on all platforms.
-
-
TODO
----
* *BSD targeted binaries.
diff --git a/skullfuck.c b/skullfuck.c
index 7d9e0a8..b40e903 100644
--- a/skullfuck.c
+++ b/skullfuck.c
@@ -29,9 +29,13 @@
#define ARCH "x86_64"
#define OS "Linux"
#define INC_DATA_PTR "\tincq %rsi\n"
+#define ADD_DATA_PTR "\taddq $0x%x, %%rsi\n"
#define DEC_DATA_PTR "\tdecq %rsi\n"
+#define SUB_DATA_PTR "\tsubq $0x%x, %%rsi\n"
#define INC_DATA "\tincb (%rsi)\n"
+#define ADD_DATA "\taddb $0x%x, (%%rsi)\n"
#define DEC_DATA "\tdecb (%rsi)\n"
+#define SUB_DATA "\tsubb $0x%x, (%%rsi)\n"
#define PRINT_DATA "\tmovq $0x01, %rax\n" \
"\tmovq $0x01, %rdi\n" \
"\tsyscall\n"
@@ -58,9 +62,13 @@
#define ARCH "i686"
#define OS "Linux"
#define INC_DATA_PTR "\tincl %ecx\n"
+#define ADD_DATA_PTR "\taddl $0x%x, %%ecx\n"
#define DEC_DATA_PTR "\tdecl %ecx\n"
+#define SUB_DATA_PTR "\tsubl $0x%x, %%ecx\n"
#define INC_DATA "\tincb (%ecx)\n"
+#define ADD_DATA "\taddb $0x%x, (%%ecx)\n"
#define DEC_DATA "\tdecb (%ecx)\n"
+#define SUB_DATA "\tsubb $0x%x, (%%ecx)\n"
#define PRINT_DATA "\tmovl $0x04, %eax\n" \
"\tmovl $0x01, %ebx\n" \
"\tint $0x80\n"
@@ -121,26 +129,57 @@ static int pop_loop_index(struct loop_stack *s) {
}
+/* Minor optimization subroutine. Increments the `in` pointer to beyond
+ the collection of adjacent `op` characters, and returns the number
+ of operations counted. */
+static int reduce(char **in, char op) {
+ int i;
+ for (i = 0; (*in)[i] == op; i++);
+ *in += i - 1;
+ return i;
+}
+
+
/* Converts the brainfuck instructions read from `in` to assembly
instructions and writes them to `out`. `in` is expected to be
null-terminated. */
static void write_instructions(FILE *out, char *in) {
- int next_loop = 0, cur_loop = 0;
+ int next_loop = 0, cur_loop = 0, op_count;
struct loop_stack *loops = calloc(sizeof(struct loop_stack), 1);
for (char *cur = in; *cur != '\0'; cur++) {
switch(*cur) {
case '>':
- fputs(INC_DATA_PTR, out);
+ op_count = reduce(&cur, '>');
+ if (op_count > 1) {
+ fprintf(out, ADD_DATA_PTR, op_count);
+ } else {
+ fputs(INC_DATA_PTR, out);
+ }
break;
case '<':
- fputs(DEC_DATA_PTR, out);
+ op_count = reduce(&cur, '<');
+ if (op_count > 1) {
+ fprintf(out, SUB_DATA_PTR, op_count);
+ } else {
+ fputs(DEC_DATA_PTR, out);
+ }
break;
case '+':
- fputs(INC_DATA, out);
+ op_count = reduce(&cur, '+');
+ if (op_count > 1) {
+ fprintf(out, ADD_DATA, op_count);
+ } else {
+ fputs(INC_DATA, out);
+ }
break;
case '-':
- fputs(DEC_DATA, out);
+ op_count = reduce(&cur, '-');
+ if (op_count > 1) {
+ fprintf(out, SUB_DATA, op_count);
+ } else {
+ fputs(DEC_DATA, out);
+ }
break;
case '.':
fputs(PRINT_DATA, out);
@@ -223,6 +262,10 @@ static void create_binary(struct params p) {
wait(NULL);
}
}
+
+ if (!p.compile_only)
+ remove("/tmp/skullfuck_tmp.s");
+ remove("/tmp/skullfuck_tmp.o");
}
diff --git a/examples/hello_world.bf b/test/hello_world.bf
index ea2b641..ea2b641 100644
--- a/examples/hello_world.bf
+++ b/test/hello_world.bf
diff --git a/examples/hello_world_commented.bf b/test/hello_world_commented.bf
index fff532c..fff532c 100644
--- a/examples/hello_world_commented.bf
+++ b/test/hello_world_commented.bf
diff --git a/examples/rot13.bf b/test/rot13.bf
index e9e82be..e9e82be 100644
--- a/examples/rot13.bf
+++ b/test/rot13.bf
diff --git a/examples/rot13_commented.bf b/test/rot13_commented.bf
index 85a97fa..85a97fa 100644
--- a/examples/rot13_commented.bf
+++ b/test/rot13_commented.bf