; 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