BITS 64 org 0x400000 elf_header: db 0x7f, "ELF" db 2 ; 64 bit db 1 ; little-endian db 1 ; header version 1 db 0 ; SysV ABI times 8 db 0 ; padding dw 2 ; executable dw 0x3e ; amd64 dd 1 ; ELF version 1 dq _start ; entry point dq program_headers - $$ ; program header table offset dq 0 ; section header table offset, not needed in executables dd 0 ; flags, unsused on x86 dw elf_header_length ; header length dw program_header_size ; size of one entry in program header table dw 1 ; 1 entry dw 0 ; size of one entry in section header table dw 0 ; number of entries in the section header table dw 0 ; section name index in section header table elf_header_length equ $ - elf_header program_headers: dd 1 ; loadable segment dd 4 + 1 ; readable + executable dq 0 ; offset of contents dq $$ ; location in virtual memory dq 0 ; undefined fsize_offset equ $ - $$ dq _end - $$ ; size of segment in file msize_offset equ $ - $$ dq _end - $$ ; size of segment in memory dq 0x1000 ; 4KiB alignment program_header_size equ $ - program_headers headers_length equ $ - elf_header _start: sub rsp, 0x10000 ; 64KiB of scratch space mov rdi, rsp .copy_header: mov esi, $$ mov ecx, headers_length rep movsb .copy_init: mov esi, init_code mov ecx, init_code_length rep movsb mov rbp, rdi mainloop: ; Read one byte xor eax, eax xor edi, edi mov rsi, rbp xor edx, edx inc edx syscall .was_eof: test eax, eax jz end mov al, [rbp] mov ecx, 8 mov ebx, command_table xor edx, edx .table_find: mov dl, [ebx+1] cmp al, [ebx] je .table_found add dl, 2 add ebx, edx loop .table_find xor edx, edx .table_found: lea esi, [ebx + 2] mov rdi, rbp mov ecx, edx rep movsb mov rbp, rdi jmp mainloop end: .copy_end_code: mov esi, end_code mov rdi, rbp mov ecx, end_code_length rep movsb mov rbp, rdi .adjust_header_sizes: mov rcx, rdi sub rcx, rsp mov [rsp + fsize_offset], rcx mov [rsp + msize_offset], rcx mov rsi, rsp add rsi, headers_length .fixup_loop: cmp rsi, rbp je .fixup_done lodsb cmp al, 0xe9 ; Relative jump jne .fixup_loop lodsb .is_forwards: test al, al jnz .is_backwards ; Push our current location (one byte after the start of the displacement) push rsi jmp .fixup_loop .is_backwards: cmp al, 1 jnz .fixup_loop ; Get matching ['s location pop rdi ; Distance between the locations = distance between jumps = displacement when jumping forwards mov rax, rsi sub rax, rdi ; Store in ['s displacement field dec rdi stosd ; Jumping backwards, we need to invert the displacement and then account for the size of the [ neg eax xor ebx, ebx mov byte bl, [while_length] sub eax, ebx ; Store in our displacement field mov rdi, rsi dec rdi stosd ; Move to next instruction mov rsi, rdi jmp .fixup_loop .fixup_done: xor ebx, ebx .output_loop: mov rsi, rsp add rsi, rbx cmp rsi, rbp je .exit xor eax, eax inc eax mov edi, eax mov edx, eax syscall inc ebx jmp .output_loop .exit: mov eax, 60 xor edi, edi syscall command_table: plus: db '+', minus - $ - 2 inc byte [rbx] minus: db '-', right - $ - 2 dec byte [rbx] right: db '>', left - $ - 2 inc rbx left: db '<', while - $ - 2 dec rbx while: db '[' while_length: db wend - $ - 1 cmp byte [rbx], 0 jne .skip ; Reserve space for jump forwards, tag with 0x00000000 db 0xe9 times 4 db 0 .skip: wend: db ']', getc - $ - 2 ; Reserve space for jump backwards, tag with 0x01000000 db 0xe9 db 1 times 3 db 0 getc: db ',', putc - $ - 2 mov byte [rbx], 0 xor eax, eax xor edi, edi mov rsi, rbx xor edx, edx inc edx syscall putc: db '.', ._end - $ - 2 xor eax, eax inc eax mov edi, eax mov rsi, rbx mov edx, eax syscall ._end: init_code: mov ecx, 0x10000 ; 64KiB of memory ; Clear out the memory sub rsp, rcx mov rdi, rsp xor al, al rep stosb ; Initialize tape head mov rbx, rsp init_code_length equ $ - init_code end_code: mov rax, 60 xor edi, edi syscall end_code_length equ $ - end_code _end: