Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@ build:

test: build
@echo "hello-world test"
./bfcc -run examples/hello-world.bf > x
diff examples/hello-world.out x && rm x || echo "Failed Hello-World"
@./bfcc -run examples/hello-world.bf > x
@diff examples/hello-world.out x && rm x || echo "Failed Hello-World"
@echo "hello-world test OK"

@echo "fibonacci test"
./bfcc -run examples/fibonacci.bf > x
diff examples/fibonacci.out x && rm x || echo "Failed Fibonacci"
@./bfcc -run examples/fibonacci.bf > x
@diff examples/fibonacci.out x && rm x || echo "Failed!"
@echo "fibonacci test: OK"

@echo "bizzfuzz test"
./bfcc -run examples/bizzfuzz.bf > x
diff examples/bizzfuzz.out x && rm x || echo "Failed BizzFuzz"
@./bfcc -run examples/bizzfuzz.bf > x
@diff examples/bizzfuzz.out x && rm x || echo "Failed!"
@echo "bizzfuzz test: OK"

@echo "quine test"
./bfcc -run examples/quine.bf > x
diff examples/quine.out x && rm x || echo "Failed Quine"
@./bfcc -run examples/quine.bf > x
@diff examples/quine.out x && rm x || echo "Failed!"
@echo "quine test: OK"

@echo "mandelbrot test"
./bfcc -run examples/mandelbrot.bf > x
diff examples/mandelbrot.out x && rm x || echo "Failed Mandelbrot"
@./bfcc -run examples/mandelbrot.bf > x
@diff examples/mandelbrot.out x && rm x || echo "Failed!"
@echo "mandelbrot test: OK"
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@ Finally if you prefer you can specify an output name for the compiled result:
$ bfcc [-run] ./examples/bizzfuzz.bf ./bf
$ ./bf

There are two backends included, one generates an assembly language source-file, and compiles with with `nasm`, and the other generates C-code, which is compiled via `gcc`.
There are two backends included, one generates an assembly language source-file, and compiles with `gcc`, and the other generates C-code which is also compiled via `gcc`.

By default the assembly-language backend is selected, because this is the thing that I was more interested in writing, due to that you **must** have `nasm` installed to compile the generated assembly language file.
By default the assembly-language backend is selected, because this is the thing that I was more interested in writing.

Use the `-backend` flag to specify the backend which you prefer to use:
You may use the `-backend` flag to specify the backend which you prefer to use:

$ bfcc -backend=c ./examples/mandelbrot.bf ./mb-c
$ bfcc -backend=asm ./examples/mandelbrot.bf ./mb-asm

You'll see slightly difference sizes in the two executable:

$ ls -lash mb-c mb-asm
76K -rwxr-xr-x 1 skx skx 73K Jun 15 10:11 mb-asm
36K -rwxr-xr-x 1 skx skx 34K Jun 15 10:11 mb-c
76K -rwxr-xr-x 1 skx skx 860K Jun 15 10:11 mb-asm
36K -rwxr-xr-x 1 skx skx 34K Jun 15 10:11 mb-c

But both should work identically; if they do not that's a bug in the generated C/assembly source files I've generated!

Expand All @@ -80,7 +80,7 @@ In the end it took me about four hours to get something I was happy with, and la
* Finally I cleaned up and improved the code.
* Implementing a separate lexer.
* Allowing the use of pluggable backends, so we could generate both C and Assembly Language output (but only one at a time).

* Started using `gcc` to compile our assembly, to drop the dependency upon `nasm`.



Expand All @@ -98,6 +98,8 @@ In the end it took me about four hours to get something I was happy with, and la
* Adding a lexer in [#4](https://github.com/skx/bfcc/pull/4)
* Allowing the generation of either C or assembly in [#6](https://github.com/skx/bfcc/pull/6)
* Allow generating a breakpoint instruction when using the assembly-backend in [#7](https://github.com/skx/bfcc/pull/7).
* Switched to generating assembly to be compiled by `gcc` rather than `nasm` [#8](https://github.com/skx/bfcc/pull/8).



### Debugging the generated program
Expand Down Expand Up @@ -159,9 +161,7 @@ You can run `make test` to run all the scripts, and compare their generated outp

Mostly none.

It might be cute to convert the assembly, such that `gcc` could compile it. That would drop the `nasm` dependency, but it's not a big deal. Patches welcome if you want to have a stab at it.

Otherwise more backends might be nice, but I guess the two existing ones are the most obvious. Due to the way the code is structured adding a new one would be trivial though.
More backends might be nice, but I guess the two existing ones are the most obvious. Due to the way the code is structured adding a new one would be trivial.



Expand Down
74 changes: 34 additions & 40 deletions generators/generator_asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ type GeneratorASM struct {
func (g *GeneratorASM) generateSource() error {
var buff bytes.Buffer
var programStart = `
global _start
section .text
.intel_syntax noprefix

_start:
mov r8, stack
.global main


main:
lea %r8, stack
`
buff.WriteString(programStart)

Expand Down Expand Up @@ -80,30 +82,30 @@ _start:
switch tok.Type {

case lexer.GREATER:
buff.WriteString(fmt.Sprintf(" add r8, %d\n", tok.Repeat))
buff.WriteString(fmt.Sprintf(" add %%r8, %d\n", tok.Repeat))

case lexer.LESS:
buff.WriteString(fmt.Sprintf(" sub r8, %d\n", tok.Repeat))
buff.WriteString(fmt.Sprintf(" sub %%r8, %d\n", tok.Repeat))

case lexer.PLUS:
buff.WriteString(fmt.Sprintf(" add byte [r8], %d\n", tok.Repeat))
buff.WriteString(fmt.Sprintf(" add byte ptr [%%r8], %d\n", tok.Repeat))

case lexer.MINUS:
buff.WriteString(fmt.Sprintf(" sub byte [r8], %d\n", tok.Repeat))
buff.WriteString(fmt.Sprintf(" sub byte ptr [%%r8], %d\n", tok.Repeat))

case lexer.OUTPUT:
buff.WriteString(" mov rax, 1\n") // SYS_WRITE
buff.WriteString(" mov rdi, 1\n") // STDOUT
buff.WriteString(" mov rsi, r8\n") // data-comes-here
buff.WriteString(" mov rdx, 1\n") // one byte
buff.WriteString(" syscall\n") // Syscall
buff.WriteString(" mov %rax, 1\n") // SYS_WRITE
buff.WriteString(" mov %rdi, 1\n") // STDOUT
buff.WriteString(" mov %rsi, %r8\n") // data-comes-here
buff.WriteString(" mov %rdx, 1\n") // one byte
buff.WriteString(" syscall\n") // Syscall

case lexer.INPUT:
buff.WriteString(" mov rax, 0\n") // SYS_READ
buff.WriteString(" mov rdi, 0\n") // STDIN
buff.WriteString(" mov rsi, r8\n") // Dest
buff.WriteString(" mov rdx, 1\n") // one byte
buff.WriteString(" syscall\n") // syscall
buff.WriteString(" mov %rax, 0\n") // SYS_READ
buff.WriteString(" mov %rdi, 0\n") // STDIN
buff.WriteString(" mov %rsi, %r8\n") // Dest
buff.WriteString(" mov %rdx, 1\n") // one byte
buff.WriteString(" syscall\n") // syscall

case lexer.LOOPOPEN:

Expand All @@ -117,7 +119,7 @@ _start:
// loop so the label here is AFTER our condition
//
i++
buff.WriteString(" cmp byte [r8], 0\n")
buff.WriteString(" cmp byte ptr [%r8], 0\n")
buff.WriteString(fmt.Sprintf(" je close_loop_%d\n", i))
buff.WriteString(fmt.Sprintf("label_loop_%d:\n", i))
opens = append(opens, i)
Expand Down Expand Up @@ -168,7 +170,7 @@ _start:
// test at the start of the loop, because
// running it twice would be pointless.
//
buff.WriteString(" cmp byte [r8], 0\n")
buff.WriteString(" cmp byte ptr [r8], 0\n")

buff.WriteString(fmt.Sprintf(" jne label_loop_%d\n", last))
buff.WriteString(fmt.Sprintf("close_loop_%d:\n", last))
Expand All @@ -185,13 +187,15 @@ _start:
}

// terminate
buff.WriteString(" mov rax, 60\n")
buff.WriteString(" mov rdi, 0\n")
buff.WriteString(" mov %rax, 60\n")
buff.WriteString(" mov %rdi, 0\n")
buff.WriteString(" syscall\n")

// program-area
buff.WriteString("section .bss\n")
buff.WriteString("stack: resb 300000\n")
buff.WriteString(".bss\n")
buff.WriteString("stack:\n")
buff.WriteString(".rept 30000\n")
buff.WriteString(" .byte 0x0\n")
buff.WriteString(".endr\n")

// Output to a file
err := ioutil.WriteFile(g.output+".s", buff.Bytes(), 0644)
Expand All @@ -200,22 +204,12 @@ _start:

func (g *GeneratorASM) compileSource() error {

// nasm to compile to object-code
nasm := exec.Command("nasm", "-f", "elf64", "-o", g.output+".o", g.output+".s")
nasm.Stdout = os.Stdout
nasm.Stderr = os.Stderr

err := nasm.Run()
if err != nil {
return err
}

// ld to link to an executable
ld := exec.Command("ld", "-m", "elf_x86_64", "-o", g.output, g.output+".o")
ld.Stdout = os.Stdout
ld.Stderr = os.Stderr
// Use gcc to compile our object-code
gcc := exec.Command("gcc", "-o", g.output, "-static", g.output+".s")
gcc.Stdout = os.Stdout
gcc.Stderr = os.Stderr

err = ld.Run()
err := gcc.Run()
if err != nil {
return err
}
Expand Down