minibfc/minibfc.asm

246 lines
4.1 KiB
NASM

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: