; bf.asm - Tiny BrainFuck compiler (135 bytes)
;
; Robert Oestling
; http://www.robos.org/
;
; nasm -f bin bf.asm -o bf.com
; bf <file.bf >file.com
;

; Here are some other more or less tiny brainfuck compilers:
;
; Name              Size  Comments
; ==========================================================================
; Jeffry Johnston   123   '@' is EOF. Cheater.
; INT-E             136   16-bit words.
; Ben Olmstead      331   286 compatible.
; ==========================================================================


[section .text]

        org     0x100

main:
        mov     di,code_buffer+8        ; Where the compiled code goes.
.read_byte:
        xor     bx,bx                   ; stdin.
        mov     ah,0x3f                 ; Read from file.
        mov     cl,1                    ; One byte.
        mov     dx,di                   ; Right after the last compiled data
        int     0x21
        inc     bx                      ; stdout.
        dec     ax                      ; Not EOF? (i.e. one byte read?)
        jz      .not_eof
        mov     al,0xc3                 ; "ret"
        stosb                           ; Compile it.
        mov     ah,0x40                 ; Write to file.
        mov     dl,0x7f                 ; dx = code_buffer.
        lea     cx,[di+0xfe81]          ; -code_buffer
        ; Note, if anything doesn't work after you modified the file, check
        ; these numbers first!

        int     0x21                    ; Write and quit.
        ret
.not_eof:
        cmp     byte[di],'['
        jz      .loop_start
        cmp     byte[di],']'
        jz      .loop_end
        mov     si,list                 ; Load instruction table into si.
.next_try:
        lodsw
        xchg    ax,cx                   ; cl = size, ch = instruction.
        lodsb                           ; al = address (low 8 bits).
        mov     ah,1                    ; High 8 bits of address always 1.
        or      ch,ch                   ; Last entry?
        jz      .read_byte              ; Yes, unknown instruction (comment).
        cmp     ch,[di]                 ; Is it the one we're looking for?
        jnz     .next_try               ; Nope, try again.
.found:
        xchg    ax,si                   ; Yes, address of code into si,
        mov     ch,0                    ; cx = cl (length of code).
        rep     movsb                   ; Copy code.
        jmp     short .read_byte

.loop_start:
        mov     al,0xe9
        stosb                           ; Compile near jump, address filled
        push    di                      ; Save current address.
        stosw                           ; in later.
        jmp     short .read_byte
.loop_end:
        pop     ax                      ; Get address to patch+maybe jump to.
        push    ax                      ; Save it again.
        mov     si,code_loop            ; Conditional jump code.
        movsw                           ; Copy first word of it.
        movsw                           ; Copy last word.
        sub     ax,di                   ; Calculate relative jump back.
        stosw                           ; Copy relative jump address.
        not     ax
        sub     ax,5                    ; Relative jump _forward_ (for patch)
        pop     si                      ; si = address of jump to patch.
        mov     [si],ax                 ; Patch!
        jmp     short .read_byte

code_inc:
        inc     byte[bx]
code_dec:
        dec     byte[bx]
code_dot:
        mov     al,[bx]
        int     0x29

list:
        db      1,'>'
        db      0x0d
        db      1,'<'
        db      0x84
        db      2,'+'
        db      0x5b
        db      2,'-'
        db      0x5d
        db      4,'.'
        db      0x5f
        db      8,','
        db      0x75
code_comma:
        mov     ah,0x00                 ; 00 shows that the list is over.
        int     0x16
        mov     [bx],al
code_loop:
        cmp     [bx],ch                 ; ch is always 0 at run-time.
        db      0x0f,0x85               ; jnz ...

code_buffer:                            ; Runtime initialization code:
        mov     bh,0xa0                 ; bx = 0xa000.
        mov     di,bx                   ; di = 0xa000.
        mov     ch,0x4b                 ; 0x4b = dec bx.
        rep     stosb                   ; Zero memory area.

