Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mcpg committed Mar 9, 2019
0 parents commit 97c4248
Show file tree
Hide file tree
Showing 9 changed files with 457 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

.vscode/
build/
*.o
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 2.6)
project(omabfc C)

set(CMAKE_C_STANDARD 99)

file(GLOB_RECURSE SOURCES src/*.c)
include_directories(src)

add_executable(omabfc ${SOURCES})
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2019 Paweł Cholewa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Oh my... Another Brainfuck Compiler - omabfc
OMABFC is a Brainfuck compiler written in C that produces NASM-compatible
x64 Linux assembly code.

## Usage
OMABFC is a command-line tool. Available options:

* **--input-file | -i**: provides input Brainfuck file (required),
* **--output-file | -o**: provides output file for assembly code (by default code is outputted to stdout),
* **--version | -v**: prints OMABFC version to stdout.

## Actually building Brainfuck programs into executables
OMABFC only produces assembly code. This means, that it needs
to be manually assembled and linked. The process is fairly simple.

1. Compile a Brainfuck file: `omabfc -i program.bf -o program.asm`
2. Assemble it using nasm: `nasm -f elf64 program.asm -o program.o`
3. Link it: `ld program.o -o program`

And that's it.

## Building OMABFC
You need to get CMake and GCC. Go into root directory of the repostiory with
a terminal, make a directory called `build`, go to it and execute `cmake ..` from it.
Now run `make all`. After the compilation succeeds, `omabfc` file should appear
in the `build` directory.

## License
This project is licensed under the [MIT License](LICENSE).
175 changes: 175 additions & 0 deletions src/codegenx64.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#include "codegenx64.h"
#include <stdlib.h>

#define MEMSET_REGISTER "rax" /* Register used to 0 the tape */
#define TAPE_SIZE "10000" /* size of the tape (as string to directly embed the value in result code) */
#define TAPE_POINTER_REGISTER "rsi" /* pointer used to hold current tape element address */

static int loop_counter;
static void generate_symbol(struct symbol_data* data, FILE* output);

static void generate_vinc(FILE* output, unsigned int amount)
{
if (amount == 0)
return;

if (amount == 1)
fprintf(output, " inc byte [" TAPE_POINTER_REGISTER "]\n");
else
fprintf(output, " add " TAPE_POINTER_REGISTER ", %u\n", amount);
}

static void generate_vdec(FILE* output, unsigned int amount)
{
if (amount == 0)
return;

if (amount == 1)
fprintf(output, " dec byte [" TAPE_POINTER_REGISTER "]\n");
else
fprintf(output, " sub byte [" TAPE_POINTER_REGISTER "], %u\n", amount);
}

static void generate_pinc(FILE* output, unsigned int amount)
{
if (amount == 0)
return;

if (amount == 1)
fprintf(output, " inc " TAPE_POINTER_REGISTER "\n");
else
fprintf(output, " add " TAPE_POINTER_REGISTER ", %u\n", amount);
}

static void generate_pdec(FILE* output, unsigned int amount)
{
if (amount == 0)
return;

if (amount == 1)
fprintf(output, " dec " TAPE_POINTER_REGISTER "\n");
else
fprintf(output, " sub " TAPE_POINTER_REGISTER ", %u\n", amount);
}

static void generate_out(FILE* output, unsigned int amount)
{
if (amount == 0)
return;

fprintf(output, " mov rax, 1\n");
fprintf(output, " mov rdi, 1\n");
fprintf(output, " mov rdx, 1\n");
for (int i = 0; i < amount; i++)
{
fprintf(output, " syscall\n\n");
}
}

static void generate_in(FILE* output, unsigned int amount)
{
if (amount == 0)
return;

fprintf(output, " mov rax, 0\n");
fprintf(output, " mov rdi, 0\n");
fprintf(output, " mov rdx, 1\n");
for (int i = 0; i < amount; i++)
{
fprintf(output, " syscall\n\n");
}
}

static void generate_loop(FILE* output, struct symbol_data* loop_symbol)
{
int used_loop = loop_counter++;
fprintf(output, "loop%u:\n", used_loop);
fprintf(output, " cmp byte [" TAPE_POINTER_REGISTER "], 0\n");
fprintf(output, " je loopend%u\n", used_loop);

struct symbol_data* current = loop_symbol->loop_data.loop_start;
while (current)
{
generate_symbol(current, output);
current = current->next;
}

fprintf(output, " cmp byte [" TAPE_POINTER_REGISTER "], 0\n");
fprintf(output, " jne loop%u\n", used_loop);
fprintf(output, "loopend%u:\n", used_loop);
}

static void generate_entry(FILE* output)
{
fprintf(output, "; Generated by OMABFC\n\n");
fprintf(output, "section .bss\n");
fprintf(output, " tape resb " TAPE_SIZE "\n");
fprintf(output, "section .text\n");
fprintf(output, "global _start\n");
fprintf(output, "_start:\n");
fprintf(output, " mov " TAPE_POINTER_REGISTER ", tape\n");
fprintf(output, " mov " MEMSET_REGISTER ", " TAPE_SIZE "\n");
fprintf(output, "memset_loop:\n");
fprintf(output, " cmp " MEMSET_REGISTER ", 0\n");
fprintf(output, " je program\n");
fprintf(output, " mov byte [tape + " MEMSET_REGISTER "], 0\n");
fprintf(output, " dec " MEMSET_REGISTER "\n");
fprintf(output, " jmp memset_loop\n");
fprintf(output, "program:\n");
}

static void generate_exit(FILE* output)
{
fprintf(output, " mov rax, 60\n");
fprintf(output, " mov rdi, 0\n");
fprintf(output, " syscall\n\n");
}

static void generate_symbol(struct symbol_data* data, FILE* output)
{
switch (data->type)
{
case SYMBOL_IN:
generate_in(output, data->amount);
break;
case SYMBOL_OUT:
generate_out(output, data->amount);
break;
case SYMBOL_VINC:
generate_vinc(output, data->amount);
break;
case SYMBOL_VDEC:
generate_vdec(output, data->amount);
break;
case SYMBOL_PINC:
generate_pinc(output, data->amount);
break;
case SYMBOL_PDEC:
generate_pdec(output, data->amount);
break;
case SYMBOL_LSTART:
generate_loop(output, data);
break;
default:
fprintf(stderr, "ERROR: Unknown symbol %d (%c)\n", data->type, data->type);
exit(1);
break;
}
}

void generate_code(struct symbol_data* data, FILE* output)
{
loop_counter = 0;

/* Generate the initial code template */
generate_entry(output);

while (data)
{
generate_symbol(data, output);
data = data->next;
}

/* And the exit syscall... */
generate_exit(output);
}
9 changes: 9 additions & 0 deletions src/codegenx64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef __CODEGEN_H
#define __CODEGEN_H

#include <stdio.h>
#include "symtab.h"

void generate_code(struct symbol_data* data, FILE* output);

#endif
89 changes: 89 additions & 0 deletions src/omabfc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <stdio.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "symtab.h"
#include "codegenx64.h"

#define OMABFC_VERSION 10000

static struct option long_options[] =
{
{ "version", no_argument, NULL, 'v' },
{ "input-file", required_argument, NULL, 'i' },
{ "output-file", required_argument, NULL, 'o' }
};

static char* output_file_name = NULL;

static FILE* input_file;
static FILE* output_file;

static void cleanup()
{
if (input_file != NULL && input_file != stdin)
fclose(input_file);
if (output_file != NULL && output_file != stdout)
fclose(output_file);
}

static void error(const char* fmt, ...)
{
va_list list;
va_start(list, fmt);
vfprintf(stderr, fmt, list);
va_end(list);
cleanup();
exit(1);
}

int main(int argc, char* argv[])
{
output_file = stdout;

int option;
while ((option = getopt_long(argc, argv, "vi:o:", long_options, NULL)) != -1)
{
switch (option)
{
case 'v':
printf("omabfc version %06d\n", (int) OMABFC_VERSION);
cleanup();
exit(0);
case 'o':
output_file_name = malloc(strlen(optarg) + 1);
memset(output_file_name, 0, strlen(optarg) + 1);
strcpy(output_file_name, optarg);
break;
case 'i':
input_file = fopen(optarg, "r");
if (!input_file)
error("Couldn't open the input file!\n");
break;
}
}

if (!input_file)
error("Input file not specified\n");

if (output_file_name)
{
output_file = fopen(output_file_name, "w");
if (!output_file)
error("Couldn't open the output file!\n");
}

fseek(input_file, 0, SEEK_END);
size_t input_size = ftell(input_file);
fseek(input_file, 0, SEEK_SET);
char* input = malloc(input_size + 1);
input[input_size] = '\0';

if (fread(input, sizeof(char), input_size, input_file) != input_size)
error("Couldn't read from the input file!\n");

generate_code(parse(input), output_file);
free(input);
return 0;
}
Loading

0 comments on commit 97c4248

Please sign in to comment.