summaryrefslogtreecommitdiff
path: root/05/codegen.b
diff options
context:
space:
mode:
Diffstat (limited to '05/codegen.b')
-rw-r--r--05/codegen.b195
1 files changed, 195 insertions, 0 deletions
diff --git a/05/codegen.b b/05/codegen.b
new file mode 100644
index 0000000..1507e6d
--- /dev/null
+++ b/05/codegen.b
@@ -0,0 +1,195 @@
+; CALLING CONVENTION:
+; arguments are pushed onto the stack by the caller, from right to left
+; caller must also reserve space on stack for return value
+; so the function puts the return value at [rbp+8] (+8 for stored return address)
+
+
+
+global code_output
+global codegen_second_pass ; = 0 on first global pass, 1 on second global pass
+global functions_addresses ; ident list of addresses
+global functions_labels ; ident list of ident lists of label addresses
+global curr_function_labels ; ident list of labels for current function (written to in 1st pass, read from in 2nd pass)
+
+#define REG_RAX 0
+#define REG_RBX 3
+#define REG_RCX 1
+#define REG_RDX 2
+#define REG_RSP 4
+#define REG_RBP 5
+#define REG_RSI 6
+#define REG_RDI 7
+
+function emit_byte
+ argument byte
+ *1code_output = byte
+ code_output += 1
+ return
+
+function emit_bytes
+ argument bytes
+ argument count
+ memcpy(code_output, bytes, count)
+ code_output += count
+ return
+
+function emit_word
+ argument word
+ *2code_output = word
+ code_output += 2
+ return
+
+function emit_dword
+ argument word
+ *4code_output = word
+ code_output += 4
+ return
+
+function emit_qword
+ argument word
+ *8code_output = word
+ code_output += 8
+ return
+
+; e.g. emit_mov_reg(REG_RAX, REG_RBX) emits mov rax, rbx
+function emit_mov_reg
+ argument dest
+ argument src
+ local n
+
+ ;48 89 (DEST|SRC<<3|0xc0)
+ *2code_output = 0x8948
+ code_output += 2
+ n = 0xc0 | dest
+ n |= src < 3
+ *1code_output = n
+ code_output += 1
+ return
+
+
+function emit_sub_rsp_imm32
+ argument imm32
+ ;48 81 ec IMM32
+ *2code_output = 0x8148
+ code_output += 2
+ *1code_output = 0xec
+ code_output += 1
+ *4code_output = imm32
+ code_output += 4
+ return
+
+function emit_mov_qword_rsp_rbp
+ ; 48 89 2c 24
+ *4code_output = 0x242c8948
+ code_output += 4
+ return
+
+function emit_mov_rbp_qword_rsp
+ ; 48 8b 2c 24
+ *4code_output = 0x242c8b48
+ code_output += 4
+ return
+
+function emit_add_rsp_imm32
+ argument imm32
+ ;48 81 c4 IMM32
+ *2code_output = 0x8148
+ code_output += 2
+ *1code_output = 0xc4
+ code_output += 1
+ *4code_output = imm32
+ code_output += 4
+ return
+
+function emit_ret
+ *1code_output = 0xc3
+ code_output += 1
+ return
+
+; make sure you put the return value in the proper place before calling this
+function generate_return
+ emit_mov_reg(REG_RSP, REG_RBP)
+ emit_mov_rbp_qword_rsp()
+ emit_add_rsp_imm32(8)
+ emit_ret()
+ return
+
+function generate_statement
+ argument statement
+ ; @TODO
+ return
+
+function generate_function
+ argument function_name
+ argument function_statement
+ local out0
+
+ if codegen_second_pass != 0 goto genf_second_pass
+ curr_function_labels = ident_list_create(4000) ; ~ 200 labels per function should be plenty
+ ident_list_add(functions_labels, function_name, curr_function_labels)
+ goto genf_cont
+ :genf_second_pass
+ curr_function_labels = ident_list_lookup(functions_labels, function_name)
+ :genf_cont
+
+ ; prologue
+ emit_sub_rsp_imm32(8)
+ emit_mov_qword_rsp_rbp()
+ emit_mov_reg(REG_RBP, REG_RSP)
+
+ generate_statement(function_statement)
+
+ ; implicit return at end of function
+ generate_return()
+
+ return
+
+function generate_functions
+ local addr
+ local c
+ local p
+ local function_name
+
+ function_name = function_statements
+
+ :genfunctions_loop
+ if *1function_name == 0 goto genfunctions_loop_end
+ addr = code_output - output_file_data ; address of this function
+ if codegen_second_pass != 0 goto genfs_check_addr
+ ; first pass; record address of function
+ ident_list_add(functions_addresses, function_name, addr)
+ goto genfs_cont
+ :genfs_check_addr
+ c = ident_list_lookup(functions_addresses, function_name)
+ if c != addr goto function_addr_mismatch
+ goto genfs_cont
+ :genfs_cont
+ p = memchr(function_name, 0)
+ p += 1
+ generate_function(function_name, p)
+ function_name = p + 8
+ goto genfunctions_loop
+ :genfunctions_loop_end
+ return
+
+ :function_addr_mismatch
+ ; address of function on 2nd pass doesn't line up with 1st pass
+ fputs(2, .str_function_addr_mismatch)
+ fputs(2, function_name)
+ exit(1)
+ :str_function_addr_mismatch
+ string Function address on first pass doesn't match 2nd pass:
+ byte 32
+ byte 0
+
+function generate_code
+ local p_func
+ code_output = output_file_data + FUNCTIONS_ADDR
+ codegen_second_pass = 0
+ generate_functions()
+ code_output = output_file_data + FUNCTIONS_ADDR
+ codegen_second_pass = 1
+ generate_functions()
+ ; generate code at the entry point of the executable
+ ; @TODO
+ return