Execute Brain****/x86 Assembly: Difference between revisions

From Rosetta Code
Content added Content deleted
(Not quite right.)
Line 1: Line 1:
==x86_64 Assembly==
==x86_64 Assembly==

{{incorrect|X86_Assembly|This example is not interpreted correctly.<pre>
>++++++++[-<+++++++++>]<.>[][<-]>+>-[+]++>++>+++[>[->+++<<+++>]<<]>-----.
>->+++..+++.>-.<<+[>[+>+]>>]<--------------.>>.+++.------.--------.>+.>+.
</pre>}}


GAS Syntax for Linux. Called it "Brainkrieg" after Teen Girl Squad. Operating principle is a little like a multi-cycle processor.
GAS Syntax for Linux. Called it "Brainkrieg" after Teen Girl Squad. Operating principle is a little like a multi-cycle processor.


===Implementation:===
===Implementation (using jump table):===


<lang>// Assembly brainf*** interpreter
<lang>// Assembly brainf*** interpreter
Line 34: Line 29:
#define STDIN $0
#define STDIN $0
#define STDOUT $1
#define STDOUT $1
#define STDERR $2

#define DEBUGMODE 0


.global _start
.global _start
Line 104: Line 102:
cmpq end, %rbx
cmpq end, %rbx
ja shutdown // End of code
ja shutdown // End of code
movb (%rbx), %al
movzbq (%rbx), %rax
incq %rbx
incq %rbx
movq %rbx, inst_ptr
movq %rbx, inst_ptr
decode:
decode:
cmpb $'+, %al
#if DEBUGMODE
je inc_data
movb %al, debugChar
cmpb $'-, %al
movq $1, %rdx
je dec_data
movq $debugChar, %rsi
cmpb $'>, %al
movq STDERR, %rdi
je dp_right
movq SYS_WRITE, %rax
cmpb $'<, %al
syscall
je dp_left
ERRCHECK -1
cmpb $'[, %al
movzbq debugChar, %rax
je brf
#endif
cmpb $'], %al
mov $branch_table, %rcx
je brb
jmp *(%rcx,%rax,8)
cmpb $'., %al
je out_data
cmpb $',, %al
je in_data
jmp fetch
// execute:
// execute:
dp_left:
dp_left:
Line 138: Line 131:
dec_data:
dec_data:
movq data_ptr, %rbx
movq data_ptr, %rbx
decq (%rbx)
decb (%rbx)
jmp fetch
jmp fetch
inc_data:
inc_data:
movq data_ptr, %rbx
movq data_ptr, %rbx
incq (%rbx)
incb (%rbx)
jmp fetch
jmp fetch
out_data:
out_data:
Line 270: Line 263:


.data
.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
brack_smph: // Bracket matching semaphore
.quad 0
.quad 0
Line 275: Line 298:
junkChar: // Also used to check if input was used.
junkChar: // Also used to check if input was used.
.byte 0
.byte 0

#if DEBUGMODE
debugChar:
.byte 0
#endif


err_val:
err_val:
.byte 0
.byte 0
// fstat:
statstruct: // This struct is 144 bytes. Only want size (+48)
statstruct: // This struct is 144 bytes. Only want size (+48)
.zero FSIZEOFF
.zero FSIZEOFF
Line 297: Line 326:
.quad 0
.quad 0
tape:
tape:
.zero 30000
.zero (1<<20)
endtape:
endtape:
.zero 1
.zero 1

Revision as of 04:01, 14 February 2016

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

  1. define SYS_READ $0
  2. define SYS_WRITE $1
  3. define SYS_OPEN $2
  4. define SYS_CLOSE $3
  5. define SYS_FSTAT $5
  6. define SYS_MMAP $9
  7. define SYS_MUNMAP $11
  8. define SYS_EXIT $60

// From experiments:

  1. define FSIZEOFF 48
  2. define STATSIZE 144

// From Linux source:

  1. define RDONLY $00
  2. define PROT_READ $0x1
  3. define MAP_PRIVATE $0x02
  4. define STDIN $0
  5. define STDOUT $1
  6. define STDERR $2
  1. define DEBUGMODE 0

.global _start .text

.macro ERRCHECK code

   cmpq    $\code, %rax
   je      fs_error

.endm

/* Local stack notes:

   0: int fd
  • /
  1. 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
  1. if DEBUGMODE

debugChar:

   .byte   0
  1. 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>