Execute Brain****/x86 Assembly

Revision as of 09:27, 13 February 2016 by rosettacode>Rdebath (Not quite right.)

x86_64 Assembly

This example is incorrect. Please fix the code and remove this message.

Details: This example is not interpreted correctly.

>++++++++[-<+++++++++>]<.>[][<-]>+>-[+]++>++>+++[>[->+++<<+++>]<<]>-----.
>->+++..+++.>-.<<+[>[+>+]>>]<--------------.>>.+++.------.--------.>+.>+.

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

Implementation:

<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

.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
       movb    (%rbx), %al
       incq    %rbx
       movq    %rbx, inst_ptr
   decode:
       cmpb    $'+, %al
       je      inc_data
       cmpb    $'-, %al
       je      dec_data
       cmpb    $'>, %al
       je      dp_right
       cmpb    $'<, %al
       je      dp_left
       cmpb    $'[, %al
       je      brf
       cmpb    $'], %al
       je      brb
       cmpb    $'., %al
       je      out_data
       cmpb    $',, %al
       je      in_data
       jmp     fetch
   // 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
       decq    (%rbx)
       jmp     fetch
   inc_data:
       movq    data_ptr, %rbx
       incq    (%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 brack_smph: // Bracket matching semaphore

   .quad   0

junkChar: // Also used to check if input was used.

   .byte   0

err_val:

   .byte   0
   

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   30000

endtape:

   .zero   1

</lang>