Execute Brain****/x86 Assembly
x86_64 Assembly
GAS Syntax for Linux. Called it "Brainkrieg" after Teen Girl Squad. Operating principle is a little like a multi-cycle processor.
Implementation (using jump table):
<lang>// Assembly brainf*** interpreter // Alternative name: "ASSembly brainF*** interpretER" // Compiles with `gcc -nostdlib brainkrieg.sx -o brainkrieg` // Usage: ./brainkrieg "filename" // Return 0 if normal exit
- define SYS_READ $0
- define SYS_WRITE $1
- define SYS_OPEN $2
- define SYS_CLOSE $3
- define SYS_FSTAT $5
- define SYS_MMAP $9
- define SYS_MUNMAP $11
- define SYS_EXIT $60
// From experiments:
- define FSIZEOFF 48
- define STATSIZE 144
// From Linux source:
- define RDONLY $00
- define PROT_READ $0x1
- define MAP_PRIVATE $0x02
- define STDIN $0
- define STDOUT $1
- define STDERR $2
- define DEBUGMODE 0
.global _start .text
.macro ERRCHECK code
cmpq $\code, %rax je fs_error
.endm
/* Local stack notes:
0: int fd
- /
- define STACKSIZE $4
_start:
/* Entry Point: */ // Open: movq RDONLY, %rsi // Filename ptr is on stack currently as argv[1]: cmpq $1, (%rsp) // if argc is 1, error jnz open_file jmp fs_error open_file: movq 16(%rsp), %rdi // argc(8), argv0(8) => rsp+16. filename movq SYS_OPEN, %rax syscall ERRCHECK -1 subq STACKSIZE, %rsp // local stack movl %eax, (%rsp) // int fd = open(argv[1], RDONLY) // fstat to get filesize fstat: movq $statstruct, %rsi movl (%rsp), %edi // fd movq SYS_FSTAT, %rax syscall // fstat(fd, statstruct) ERRCHECK -1 // mmap - don't forget to munmap. mmap: movq $0, %r9 // offset movq (%rsp), %r8 // fd movq MAP_PRIVATE, %r10 movq PROT_READ, %rdx movq filesize, %rsi movq (%rsp), %rdi // vmemptr movq SYS_MMAP, %rax syscall ERRCHECK -1 // Set up machine: boot: movq %rax, head // head = mmap'd file start movq %rax, inst_ptr // inst_ptr = head addq filesize, %rax movq %rax, end // end = head+filesize movq $tape, data_ptr // data_ptr = tape xorq %rax, %rax /* Magic happens here: - Fetch symbol - Decode symbol - Execute opcode - Instruction out of range = halt - Data out of range = halt */ OS_start: fetch: movq inst_ptr, %rbx cmpq end, %rbx ja shutdown // End of code movzbq (%rbx), %rax incq %rbx movq %rbx, inst_ptr decode: #if DEBUGMODE movb %al, debugChar movq $1, %rdx movq $debugChar, %rsi movq STDERR, %rdi movq SYS_WRITE, %rax syscall ERRCHECK -1 movzbq debugChar, %rax #endif mov $branch_table, %rcx jmp *(%rcx,%rax,8) // execute: dp_left: cmpq $tape, data_ptr jz scram decq data_ptr jmp fetch dp_right: cmpq $endtape, data_ptr jz scram incq data_ptr jmp fetch dec_data: movq data_ptr, %rbx decb (%rbx) jmp fetch inc_data: movq data_ptr, %rbx incb (%rbx) jmp fetch out_data: movq $1, %rdx movq data_ptr, %rsi movq STDOUT, %rdi movq SYS_WRITE, %rax syscall ERRCHECK -1 jmp fetch in_data: movq $1, %rdx movq data_ptr, %rsi movq STDIN, %rdi movq SYS_READ, %rax syscall ERRCHECK -1 cmpb $'\n, (%rsi) je fetch movq %rax, junkChar jmp fetch brf: // branch if *dp=0 movq data_ptr, %rbx cmpb $0, (%rbx) jnz fetch matchfwd: movq inst_ptr, %rbx movb (%rbx), %al cmpb $'[, %al jne no_smph incq brack_smph cmpq end, %rbx je scram incq inst_ptr jmp matchfwd no_smph: // no semaphore needed cmpb $'], %al je check_smph cmpq end, %rbx je scram incq inst_ptr jmp matchfwd check_smph: // check if semaphore set cmpq $0, brack_smph jz donematch decq brack_smph cmpq end, %rbx je scram incq inst_ptr jmp matchfwd brb: // branch if *dp!=0 movq data_ptr, %rbx cmpb $0, (%rbx) jz fetch subq $2, inst_ptr // Branch taken, scan back. matchbwd: movq inst_ptr, %rbx movb (%rbx), %al cmpb $'], %al jne no_smph2 incq brack_smph cmpq head, %rbx je scram decq inst_ptr jmp matchbwd no_smph2: // no semaphore needed cmpb $'[, %al je check_smph2 cmpq head, %rbx je scram decq inst_ptr jmp matchbwd check_smph2: // check if semaphore set cmpq $0, brack_smph jz donematch2 decq brack_smph cmpq head, %rbx je scram decq inst_ptr jmp matchbwd donematch: incq inst_ptr donematch2: jmp fetch scram: // Memory breached movq $0x7f, err_val shutdown: // Consume rest of stdin if we used it. cmpb $0, junkChar jz skip_flush flush_stdin: movq SYS_READ, %rax movq STDIN, %rdi movq $junkChar, %rsi movq $1, %rdx syscall cmpq $0, %rax // EOF jz skip_flush cmpb $'\n, junkChar jne flush_stdin // munmap skip_flush: movq filesize, %rsi movq head, %rdi movq SYS_MUNMAP, %rax syscall // munmap(vmemptr, filesize) cmpq $-1, %rax je fs_error // close movl (%rsp), %edi movq SYS_CLOSE, %rax syscall // close(fd) ERRCHECK -1
exit:
movq SYS_EXIT, %rax movzbq err_val, %rdi syscall
fs_error:
movq SYS_EXIT, %rax movq $-1, %rdi syscall // exit(-1)
.data branch_table:
.rept 43 .quad fetch .endr .quad inc_data .quad in_data .quad dec_data .quad out_data .rept 13 .quad fetch .endr .quad dp_left .quad fetch .quad dp_right .rept 28 .quad fetch .endr .quad brf .quad fetch .quad brb .rept 162 .quad fetch .endr
brack_smph: // Bracket matching semaphore
.quad 0
junkChar: // Also used to check if input was used.
.byte 0
- if DEBUGMODE
debugChar:
.byte 0
- endif
err_val:
.byte 0
// fstat: statstruct: // This struct is 144 bytes. Only want size (+48)
.zero FSIZEOFF filesize: // 8 bytes. .quad 0 .zero STATSIZE-FSIZEOFF+8
// Program: head:
.quad 0
end:
.quad 0
inst_ptr:
.quad 0
// Data: data_ptr:
.quad 0
tape:
.zero (1<<20)
endtape:
.zero 1
</lang>