format ELF64 executable RIGHT = 3Eh LEFT = 3Ch PLUS = 2Bh MINUS = 2Dh DOT = 2Eh COMMA = 2Ch LBRAC = 5Bh RBRAC = 5Dh CHUNK_MAX = 20h CHUNK_SIZE = (CHUNK_MAX * 2) CHUNK_MASK = (CHUNK_SIZE - 1) MAX_CHUNKS = 20h TRANSLATION_MAX = 40h MEM = 100h entry $ xor r12,r12 ; Initialize chunk start .next_chunk: xor r13,r13 ; Initialize chunk offset .skip: call key .next_run: cmp rax,0 je interpret ; EOF -> finished loading chunks ;; Dispatch on command cmp rax,RIGHT je .run_init cmp rax,LEFT je .run_init cmp rax,PLUS je .run_init cmp rax,MINUS je .run_init cmp rax,DOT je .run_init cmp rax,COMMA je .run_init cmp rax,LBRAC je .lbrac cmp rax,RBRAC je .rbrac jmp .skip ; Skip non-command characters .run_init: mov r14,rax ; Save rax as run command mov r15,1 ; Initialize run count to 1 .run: call key cmp rax,r14 jne .run_write ; Write run if different command inc r15 ; Increment run count jmp .run .run_write: mov rbx,r12 add rbx,r13 mov byte [chunks+rbx],r14b ; Write command mov byte [chunks+rbx+1],r15b ; Write run count add r13,2 ; Increment chunk offset jmp .next_run .lbrac: mov rbx,r12 add rbx,CHUNK_SIZE push rbx ; Push start of next chunk mov rax,r12 add rax,r13 mov byte [chunks+rax],LBRAC ; Write command mov byte [chunks+rax+1],0 ; Zero run count field lea rbx,[chunks+rax+2] push rbx ; Push address of dest field jmp .end_chunk .rbrac: pop rax ; Pop address of lbrac's dest field mov rbx,r12 add rbx,CHUNK_SIZE mov word [rax],bx ; Write start of next chunk to lbrac's dest field mov rax,r12 add rax,r13 mov byte [chunks+rax],RBRAC ; Write command mov byte [chunks+rax+1],0 ; Zero run count field pop rbx ; Pop start of chunk after lbrac mov word [chunks+rax+2],bx ; Write to dest field jmp .end_chunk .end_chunk: mov word [chunks+rax+4],0 ; Write chunk terminator add r12,CHUNK_SIZE ; Bump chunk start to next chunk jmp .next_chunk interpret: xor r12,r12 ; Initialize code offset mov r13,mem ; Initialize head .opcode_dispatch: mov word ax,[chunks+r12] movzx ebx,ah cmp al,RIGHT je .right cmp al,LEFT je .left cmp al,PLUS je .plus cmp al,MINUS je .minus cmp al,DOT je .dot cmp al,COMMA je .comma cmp al,LBRAC je .lbrac cmp al,RBRAC je .rbrac jmp exit .right: add r13,rbx jmp .next .left: sub r13,rbx jmp .next .plus: add byte [r13],bl jmp .next .minus: sub byte [r13],bl jmp .next .dot: mov r14,rbx .dot_loop: cmp r14,0 je .next call emit dec r14 jmp .dot_loop .comma: ; TODO int3 jmp .next .lbrac: cmp byte [r13],0 jne .next_chunk mov word r12w,[chunks+r12+2] jmp .opcode_dispatch .rbrac: cmp byte [r13],0 je .next_chunk mov word r12w,[chunks+r12+2] jmp .opcode_dispatch .next: add r12,2 jmp .opcode_dispatch .next_chunk: and r12,(not CHUNK_MASK) add r12,CHUNK_SIZE jmp .opcode_dispatch exit: mov rdi,0 mov rax,60 syscall key: mov rdi,0 mov rsi,.ch mov rdx,1 mov rax,0 syscall cmp rax,0 je .eof mov byte al,[.ch] ret .eof: ret .ch: rb 1 emit: mov rdi,1 mov rsi,r13 mov rdx,1 mov rax,1 syscall ret chunks: rw (CHUNK_MAX * MAX_CHUNKS) translations: rb (TRANSLATION_MAX * MAX_CHUNKS) mem: db MEM dup 0