From fee1781a5bb097b28056a43138713095dfa47a4a Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 25 Apr 2025 23:12:36 +0200 Subject: Change label and jump (now goto) syntax --- boot/stage1.s | 1564 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1564 insertions(+) create mode 100644 boot/stage1.s (limited to 'boot/stage1.s') diff --git a/boot/stage1.s b/boot/stage1.s new file mode 100644 index 0000000..fbfa16d --- /dev/null +++ b/boot/stage1.s @@ -0,0 +1,1564 @@ +.global _start # Program entry point. +# s1 - Contains the current position in the source text. + +.equ SOURCE_BUFFER_SIZE, 40960 + +.section .rodata +section_rodata: .ascii ".section .rodata\n" +.equ SECTION_RODATA_SIZE, . - section_rodata +section_text: .ascii ".section .text\n" +.equ SECTION_TEXT_SIZE, . - section_text +section_bss: .ascii ".section .bss\n" +.equ SECTION_BSS_SIZE, . - section_bss +global_start: .ascii ".global _start\n" +.equ GLOBAL_START_SIZE, . - global_start +prologue: .ascii "addi sp, sp, -96\nsw ra, 92(sp)\nsw s0, 88(sp)\naddi s0, sp, 96\n" +.equ PROLOGUE_SIZE, . - prologue +epilogue: .ascii "lw ra, 92(sp)\nlw s0, 88(sp)\naddi sp, sp, 96\nret\n" +.equ EPILOGUE_SIZE, . - epilogue + +.section .bss +.type source_code, @object +.size source_code, SOURCE_BUFFER_SIZE +source_code: .zero SOURCE_BUFFER_SIZE + +.section .text + +# Ignores the import. +.type _compile_import, @function +_compile_import: + # Prologue. + addi sp, sp, -8 + sw ra, 4(sp) + sw s0, 0(sp) + addi s0, sp, 8 + + addi s1, s1, 6 + call _skip_spaces + call _read_token + add s1, s1, a0 # Skip the imported module name. + + # Epilogue. + lw ra, 4(sp) + lw s0, 0(sp) + addi sp, sp, 8 + ret + +.type _build_binary_expression, @function +_build_binary_expression: + # Prologue. + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 + + li a0, 0 + call _build_expression + + call _skip_spaces + call _read_token + sw a0, 20(sp) + + li t0, 0x26 # & + sw t0, 16(sp) + mv a0, s1 + lw a1, 20(sp) + addi a2, sp, 16 + call _token_compare + beqz a0, .L_build_binary_expression_and + + li t0, 0x3d # = + sw t0, 16(sp) + mv a0, s1 + lw a1, 20(sp) + addi a2, sp, 16 + call _token_compare + beqz a0, .L_build_binary_expression_equal + + /* DEBUG + mv a0, s1 + li a1, 8 + call _write_error */ + + j .Lbuild_binary_expression_end + +.L_build_binary_expression_equal: + addi s1, s1, 1 # Skip =. + li a0, 1 + call _build_expression + li t0, 0x0a3161 # a1\n + sw t0, 16(sp) + li t0, 0x202c3061 # a0,_ + sw t0, 12(sp) + li t0, 0x202c3061 # a0,_ + sw t0, 8(sp) + li t0, 0x20627573 # sub_ + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 15 + call _write_out + + li t0, 0x0a306120 # _a0\n + sw t0, 16(sp) + li t0, 0x2c306120 # _a0, + sw t0, 12(sp) + li t0, 0x7a716573 # seqz + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 12 + call _write_out + + j .Lbuild_binary_expression_end + +.L_build_binary_expression_and: + addi s1, s1, 1 # Skip &. + li a0, 1 + call _build_expression + li t0, 0x0a3161 # a1\n + sw t0, 16(sp) + li t0, 0x202c3061 # a0,_ + sw t0, 12(sp) + li t0, 0x202c3061 # a0,_ + sw t0, 8(sp) + li t0, 0x20646e61 # and_ + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 15 + call _write_out + + j .Lbuild_binary_expression_end + +.Lbuild_binary_expression_end: + # Epilogue. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 + ret + +# Evalutes an expression and saves the result in a0. +# +# a0 - X in aX, the register number to save the result. +.type _build_expression, @function +_build_expression: + # Prologue. + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 + + addi a0, a0, '0' # Make the register number to a character. + sw a0, 20(sp) # And save it. + + call _skip_spaces + call _read_token + sw s1, 16(sp) + sw a0, 12(sp) + + # Integer literal. + addi a0, s1, 0 + lbu a0, (a0) + call _is_digit + bnez a0, .Lbuild_expression_literal + + addi a0, s1, 0 + lbu a0, (a0) + li t0, 0x5f # _ + beq a0, t0, .Lbuild_expression_call + + li t0, 0x61636f6c # loca + sw t0, 8(sp) + mv a0, s1 + addi a1, sp, 8 + li a2, 4 + call _memcmp + beqz a0, .Lbuild_expression_identifier + + # Named identifier. + lw t1, 20(sp) + li t0, 0x00202c00 # \0,_ + or t0, t0, t1 + sw t0, 8(sp) + li t0, 0x6120616c # la a + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 7 + call _write_out + + lw a0, 16(sp) + lw a1, 12(sp) + call _write_out + + li t0, 0x0a # \n + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 1 + call _write_out + + j .Lbuild_expression_advance + +.Lbuild_expression_identifier: + lw t1, 20(sp) + li t0, 0x00202c00 # \0,_ + or t0, t0, t1 + sw t0, 8(sp) + li t0, 0x6120776c # lw a + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 7 + call _write_out + + lw a0, 16(sp) + lw a1, 12(sp) + addi a0, a0, 4 # Skip the "loca" variable prefix. + addi a1, a1, -4 # Skip the "loca" variable prefix. + call _write_out + + li t0, 0x0a # \n + sw t0, 8(sp) + li t0, 0x29707328 # (sp) + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 5 + call _write_out + + j .Lbuild_expression_advance + +.Lbuild_expression_call: + lw a0, 16(sp) + lw a1, 12(sp) + add s1, s1, a1 + addi s1, s1, 1 + call _compile_call + + j .Lbuild_expression_end + +.Lbuild_expression_literal: + lw t1, 20(sp) + li t0, 0x00202c00 # \0,_ + or t0, t0, t1 + sw t0, 8(sp) + li t0, 0x6120696c # li a + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 7 + call _write_out + + lw a0, 16(sp) + lw a1, 12(sp) + call _write_out + + li t0, 0x0a # \n + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 1 + call _write_out + + j .Lbuild_expression_advance + +.Lbuild_expression_advance: + lw a0, 12(sp) + add s1, s1, a0 + +.Lbuild_expression_end: + # Epilogue. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 + ret + +# Compiles a statement beginning with an identifier. +# +# Left values should be variables named "loca n", where n is the offset +# of the variable on the stack, like loca8 or loca4. +.type _compile_identifier, @function +_compile_identifier: + # Prologue. + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 + + call _read_token + + # Save the pointer to the identifier and its length on the stack. + sw s1, 20(sp) + sw a0, 16(sp) + + add s1, s1, a0 + call _skip_spaces + call _read_token + + # Save the pointer and the length of the token following the identifier. + sw s1, 12(sp) + sw a0, 8(sp) + + add s1, s1, a0 # Skip that token. + call _skip_spaces + + li t0, 0x3d3a # := + sw t0, 4(sp) + lw a0, 12(sp) + lw a1, 8(sp) + addi a2, sp, 4 + call _token_compare + beqz a0, .Lcompile_identifier_assign + + lw t0, 12(sp) + lbu t0, (t0) + li t1, 0x28 # ( + beq t0, t1, .Lcompile_identifier_call + + j .Lcompile_identifier_end + +.Lcompile_identifier_call: + lw a0, 20(sp) + lw a1, 16(sp) + call _compile_call + + j .Lcompile_identifier_end + +.Lcompile_identifier_assign: + call _build_binary_expression + + li t0, 0x202c30 # 0,_ + sw t0, 12(sp) + li t0, 0x61207773 # sw a + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 7 + call _write_out + + lw a0, 20(sp) + lw a1, 16(sp) + addi a0, a0, 4 # Skip the "loca" variable prefix. + addi a1, a1, -4 # Skip the "loca" variable prefix. + call _write_out + + li t0, 0x0a # \n + sw t0, 12(sp) + li t0, 0x29707328 # (sp) + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 5 + call _write_out + + j .Lcompile_identifier_end + +.Lcompile_identifier_end: + # Epilogue. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 + ret + +# Compiles a procedure call. Expects s1 to point to the first argument. +# a0 - Pointer to the procedure name. +# a1 - Length of the procedure name. +# +# Returns the procedure result in a0. +.type _compile_call, @function +_compile_call: + # Prologue. + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 + + sw a0, 20(sp) + sw a1, 16(sp) + sw zero, 12(sp) # Argument count for a procedure call. + +.Lcompile_call_paren: + call _skip_spaces + call _read_token + lbu t0, (s1) + li t1, 0x29 # ) + beq t0, t1, .Lcompile_call_complete + +.Lcompile_call_argument: + li a0, 0 + call _build_expression + + li t0, 0x202c30 # 0,_ + sw t0, 8(sp) + li t0, 0x61207773 # sw a + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 7 + call _write_out + + lw t0, 12(sp) # Argument count for a procedure call. + + # Only 6 arguments are supported with a0-a5. + # Save all arguments on the stack so they aren't overriden afterwards. + # The offset on the stack always has two digits in this case. + li t1, -4 + mul t1, t0, t1 + addi t1, t1, 60 + li t2, 10 + div t3, t1, t2 + rem t4, t1, t2 + addi t3, t3, '0' + addi t4, t4, '0' + + sw t3, 8(sp) + sw t4, 4(sp) + + addi a0, sp, 8 + li a1, 1 + call _write_out + + addi a0, sp, 4 + li a1, 1 + call _write_out + + li t0, 0x0a # \n + sw t0, 8(sp) + li t0, 0x29707328 # (sp) + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 5 + call _write_out + + call _skip_spaces + call _read_token + lbu t0, (s1) + li t1, ',' + bne t0, t1, .Lcompile_call_paren + + lw t0, 12(sp) # Argument count for a procedure call. + addi t0, t0, 1 + sw t0, 12(sp) + + addi s1, s1, 1 # Skip the comma between the arguments. + j .Lcompile_call_argument + +.Lcompile_call_complete: + sw zero, 12(sp) + +.Lcompile_call_restore: + # Just go through all a0-a5 registers and read them from stack. + # If this stack value contains garbage, the procedure just shouldn't use it. + lw t0, 12(sp) + li t1, 5 + bgt t0, t1, .Lcompile_call_perform + + li t0, 0x6120776c # lw a + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 4 + call _write_out + + lw t0, 12(sp) # Argument count for a procedure call. + + li t1, -4 + mul t1, t0, t1 + addi t1, t1, 60 + li t2, 10 + div t3, t1, t2 + rem t4, t1, t2 + addi t3, t3, '0' + addi t4, t4, '0' + addi t0, t0, '0' + + li t5, 0x0a # \n + sb t5, 11(sp) + li t5, 0x29707328 # (sp) + sw t5, 7(sp) + sb t4, 6(sp) + sb t3, 5(sp) + li t5, 0x202c # ,_ + sh t5, 3(sp) + sb t0, 2(sp) + + addi a0, sp, 2 + li a1, 10 + call _write_out + + lw t0, 12(sp) # Increment. + addi t0, t0, 1 + sw t0, 12(sp) + + j .Lcompile_call_restore + +.Lcompile_call_perform: + li t0, 0x20 + sw t0, 8(sp) + li t0, 0x6c6c6163 + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 5 + call _write_out + + lw a0, 20(sp) + lw a1, 16(sp) + call _write_out + + li t0, 0x0a # \n + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 1 + call _write_out + + call _skip_spaces + addi s1, s1, 1 # Skip the close paren. + + # Epilogue. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 + ret + +# Reads a token and returns its length in a0. +# _read_token doesn't change s1, it finds the length of the token s1 is pointing to. +.type _read_token, @function +_read_token: + # Prologue. + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + addi s0, sp, 16 + + lbu t0, (s1) # t0 = Current character. + sw zero, 4(sp) + + li t1, '.' + beq t0, t1, .Ltoken_character_single + + li t1, ',' + beq t0, t1, .Ltoken_character_single + + li t1, ':' + beq t0, t1, .Ltoken_character_colon + + li t1, ';' + beq t0, t1, .Ltoken_character_single + + li t1, '(' + beq t0, t1, .Ltoken_character_single + + li t1, ')' + beq t0, t1, .Ltoken_character_single + + li t1, '[' + beq t0, t1, .Ltoken_character_single + + li t1, ']' + beq t0, t1, .Ltoken_character_single + + li t1, '^' + beq t0, t1, .Ltoken_character_single + + li t1, '&' + beq t0, t1, .Ltoken_character_single + + li t1, '=' + beq t0, t1, .Ltoken_character_single +# Expect an identifier or a number. +.Ltoken_character_loop_do: + lw t6, 4(sp) + add t1, s1, t6 + lbu a0, (t1) # a0 = Current character. + + call _is_alnum + + beqz a0, .Ltoken_character_end + lw t6, 4(sp) + addi t6, t6, 1 + sw t6, 4(sp) + j .Ltoken_character_loop_do + +.Ltoken_character_single: + lw t6, 4(sp) + addi t6, t6, 1 + sw t6, 4(sp) + j .Ltoken_character_end + +.Ltoken_character_colon: + lbu t0, 1(s1) # t0 = The character after the colon. + lw t6, 4(sp) + addi t6, t6, 1 + sw t6, 4(sp) + + li t1, '=' + beq t0, t1, .Ltoken_character_single + j .Ltoken_character_end + +.Ltoken_character_end: + lw a0, 4(sp) + + # Epilogue. + lw ra, 12(sp) + lw s0, 8(sp) + addi sp, sp, 16 + ret + +# Skips the spaces till the next non space character. +.type _skip_spaces, @function +_skip_spaces: +.Lspace_loop_do: + lbu t0, (s1) # t0 = Current character. + + li t1, ' ' + beq t0, t1, .Lspace_loop_repeat + li t1, '\t' + beq t0, t1, .Lspace_loop_repeat + li t1, '\n' + beq t0, t1, .Lspace_loop_repeat + li t1, '\r' + beq t0, t1, .Lspace_loop_repeat + + j .Lspace_loop_end +.Lspace_loop_repeat: + addi s1, s1, 1 + j .Lspace_loop_do + +.Lspace_loop_end: + ret + +# Skips tabs at the line beginning. +.type _skip_indentation, @function +_skip_indentation: +.Lskip_indentation_do: + lbu t0, (s1) + + li t1, '\t' + beq t0, t1, .Lskip_indentation_skip + + j .Lskip_indentation_end + +.Lskip_indentation_skip: + addi s1, s1, 1 + j .Lskip_indentation_do + +.Lskip_indentation_end: + ret + +# Parameters: +# a0 - Line length. +.type _skip_comment, @function +_skip_comment: + add s1, s1, a0 + addi s1, s1, 1 # Skip the new line. + ret + +# Parameters: +# a0 - Line length. +.type _compile_assembly, @function +_compile_assembly: + # Prologue. + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + addi s0, sp, 16 + + sw a0, 4(sp) # a0 - Line length. + + # Write the source to the standard output. + mv a0, s1 + lw a1, 4(sp) + call _write_out + + lw t0, 4(sp) + add s1, s1, t0 + + li t0, '\n' + sb t0, 0(sp) + addi a0, sp, 0 + li a1, 1 + call _write_out + + addi s1, s1, 1 # Skip the new line. + + # Epilogue. + lw ra, 12(sp) + lw s0, 8(sp) + addi sp, sp, 16 + ret + +.type _compile_program, @function +_compile_program: + # Prologue. + addi sp, sp, -8 + sw ra, 4(sp) + sw s0, 0(sp) + addi s0, sp, 8 + + la a0, global_start + li a1, GLOBAL_START_SIZE + call _write_out + + addi s1, s1, 8 # program\n. + + # Epilogue. + lw ra, 4(sp) + lw s0, 0(sp) + addi sp, sp, 8 + ret + +.type _compile_constant_section, @function +_compile_constant_section: + # Prologue. + addi sp, sp, -8 + sw ra, 4(sp) + sw s0, 0(sp) + addi s0, sp, 8 + + la a0, section_rodata + li a1, SECTION_RODATA_SIZE + call _write_out + + addi s1, s1, 6 # const\n. + +.Lcompile_constant_section_item: + call _skip_spaces + lbu a0, (s1) + call _is_upper + beqz a0, .Lcompile_constant_section_end + + call _compile_constant + j .Lcompile_constant_section_item + +.Lcompile_constant_section_end: + # Epilogue. + lw ra, 4(sp) + lw s0, 0(sp) + addi sp, sp, 8 + ret + +.type _compile_constant, @function +_compile_constant: + # Prologue. + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + addi s0, sp, 16 + + call _read_token + + mv a1, a0 # The identifier length from _read_token should be in a1. + mv a0, s1 # Save the identifier pointer before advancing it. + add s1, s1, a1 + call _write_out + + call _skip_spaces + call _read_token + addi s1, s1, 2 # Skip the assignment sign. + + # : .long + li t0, 0x20676e6f # ong_ + sw t0, 4(sp) + li t0, 0x6c2e203a # : .l + sw t0, 0(sp) + mv a0, sp + li a1, 8 + call _write_out + + call _skip_spaces + call _read_token + + mv a1, a0 # The literal length from _read_token should be in a1. + mv a0, s1 # Save the literal pointer before advancing it. + add s1, s1, a1 + call _write_out + + li t0, '\n' + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 1 + call _write_out + + # Epilogue. + lw ra, 12(sp) + lw s0, 8(sp) + addi sp, sp, 16 + ret + +.type _compile_variable_section, @function +_compile_variable_section: + # Prologue. + addi sp, sp, -8 + sw ra, 4(sp) + sw s0, 0(sp) + addi s0, sp, 8 + + la a0, section_bss + li a1, SECTION_BSS_SIZE + call _write_out + + addi s1, s1, 4 # var\n. + +.Lcompile_variable_section_item: + call _skip_spaces + lbu a0, (s1) + call _is_lower + beqz a0, .Lcompile_variable_section_end + + call _compile_variable + j .Lcompile_variable_section_item + +.Lcompile_variable_section_end: + # Epilogue. + lw ra, 4(sp) + lw s0, 0(sp) + addi sp, sp, 8 + ret + +.type _compile_variable, @function +_compile_variable: + # Prologue. + addi sp, sp, -40 + sw ra, 36(sp) + sw s0, 32(sp) + addi s0, sp, 40 + + call _read_token + + # Save the identifier on the stack since it should emitted multiple times. + sw s1, 28(sp) + sw a0, 24(sp) + add s1, s1, a0 + + call _skip_spaces + addi s1, s1, 1 # Skip the colon in front of the type. + + call _skip_spaces + addi s1, s1, 1 # Skip the opening bracket. + + call _read_token + + # Save the array size on the stack since it has to be emitted multiple times. + sw s1, 20(sp) + sw a0, 16(sp) + add s1, s1, a0 + + call _skip_spaces + addi s1, s1, 1 # Skip the closing bracket. + + call _skip_spaces + call _read_token + add s1, s1, a0 # Skip the type. + + # .type identifier, @object + li t0, 0x2065 # e_ + sw t0, 12(sp) + li t0, 0x7079742e # .typ + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 6 + call _write_out + + lw a0, 28(sp) + lw a1, 24(sp) + call _write_out + + li t0, 0x0a74 # t\n + sw t0, 12(sp) + li t0, 0x63656a62 # bjec + sw t0, 8(sp) + li t0, 0x6f40202c # , @o + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 10 + call _write_out + + # .size identifier, size + li t0, 0x2065 # e_ + sw t0, 12(sp) + li t0, 0x7a69732e # .siz + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 6 + call _write_out + + lw a0, 28(sp) + lw a1, 24(sp) + call _write_out + + li t0, 0x202c # , + sw t0, 12(sp) + addi a0, sp, 12 + li a1, 2 + call _write_out + + lw a0, 20(sp) + lw a1, 16(sp) + call _write_out + + li t0, 0x0a # \n + sw t0, 12(sp) + addi a0, sp, 12 + li a1, 1 + call _write_out + + # identifier: .zero size + lw a0, 28(sp) + lw a1, 24(sp) + call _write_out + + li t0, 0x206f7265 # ero_ + sw t0, 12(sp) + li t0, 0x7a2e203a # : .z + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 8 + call _write_out + + lw a0, 20(sp) + lw a1, 16(sp) + call _write_out + + li t0, 0x0a # \n + sw t0, 12(sp) + addi a0, sp, 12 + li a1, 1 + call _write_out + + # Epilogue. + lw ra, 36(sp) + lw s0, 32(sp) + addi sp, sp, 40 + ret + +.type _compile_procedure, @function +_compile_procedure: + # Prologue. + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 + + addi s1, s1, 5 # Skip proc_ + call _read_token + sw s1, 20(sp) + sw a0, 16(sp) + add s1, s1, a0 + + # .type identifier, @function + li t0, 0x2065 # e_ + sw t0, 12(sp) + li t0, 0x7079742e # .typ + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 6 + call _write_out + + lw a0, 20(sp) + lw a1, 16(sp) + call _write_out + + li t0, 0x0a6e6f69 # ion\n + sw t0, 12(sp) + li t0, 0x74636e75 # unct + sw t0, 8(sp) + li t0, 0x6640202c # , @f + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 12 + call _write_out + + lw a0, 20(sp) + lw a1, 16(sp) + call _write_out + + li t0, 0x0a3a # :\n + sw t0, 12(sp) + addi a0, sp, 12 + li a1, 2 + call _write_out + + call _skip_spaces + addi s1, s1, 1 # Skip opening argument paren. + call _skip_spaces + addi s1, s1, 1 # Skip closing argument paren. + + li t0, 0x6e # n + sw t0, 12(sp) + li t0, 0x69676562 # begi + sw t0, 8(sp) + + # Skip all declarations until we find the "begin" keyword, denoting the + # beginning of the procedure body. +.Lcompile_procedure_begin: + call _skip_spaces + call _read_token + + mv a1, a0 + mv a0, s1 + addi a2, sp, 8 + add s1, s1, a1 + call _token_compare + + bnez a0, .Lcompile_procedure_begin + + # Generate the procedure prologue with a predefined stack size. + la a0, prologue + li a1, PROLOGUE_SIZE + call _write_out + + # Save passed arguments on the stack. + li t0, 0x0a29 # )\n + sw t0, 12(sp) + li t0, 0x70732834 # 4(sp + sw t0, 8(sp) + li t0, 0x38202c30 # 0, 8 + sw t0, 4(sp) + li t0, 0x61207773 # sw a + sw t0, 0(sp) + addi a0, sp, 0 + li a1, 14 + call _write_out + + li t0, '0' + sb t0, 8(sp) + li t0, 0x38202c31 # 1, 8 + sw t0, 4(sp) + addi a0, sp, 0 + li a1, 14 + call _write_out + + li t0, '6' + sb t0, 8(sp) + li t0, 0x37202c32 # 2, 7 + sw t0, 4(sp) + addi a0, sp, 0 + li a1, 14 + call _write_out + + li t0, '2' + sb t0, 8(sp) + li t0, 0x37202c33 # 3, 7 + sw t0, 4(sp) + addi a0, sp, 0 + li a1, 14 + call _write_out + + li t0, '8' + sb t0, 8(sp) + li t0, 0x36202c34 # 4, 6 + sw t0, 4(sp) + addi a0, sp, 0 + li a1, 14 + call _write_out + + li t0, '4' + sb t0, 8(sp) + li t0, 0x36202c35 # 5, 6 + sw t0, 4(sp) + addi a0, sp, 0 + li a1, 14 + call _write_out + + # Generate the body of the procedure. +.Lcompile_procedure_body: + call _skip_indentation + call _read_line + sw a0, 12(sp) + li t0, 0x0a646e65 # end\n + sw t0, 8(sp) + mv a0, s1 + addi a1, sp, 8 + li a2, 4 + call _memcmp + + beqz a0, .Lcompile_procedure_end + + lw a0, 12(sp) + call _compile_line + j .Lcompile_procedure_body + +.Lcompile_procedure_end: + add s1, s1, 4 # Skip end\n. + + # Generate the procedure epilogue with a predefined stack size. + la a0, epilogue + li a1, EPILOGUE_SIZE + call _write_out + + # Epilogue. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 + ret + +# Compares two string, which of one has a length, the other one is null-terminated. +# +# a0 - The address of the token string. +# a1 - The length of the string in a0. +# a2 - The address of the null-terminated string. +# +# If the strings match sets a0 to 0, otherwise sets it to 1. +.type _token_compare, @function +_token_compare: + addi t0, a0, 0 + addi t1, a1, 0 + addi t2, a2, 0 + +.Ltoken_compare_loop: + lbu t3, (t2) + + # Will only be 0 if the current character in the null terminated string is \0 and the remaining length of the + # another string is 0. + or t4, t3, t1 + beqz t4, .Ltoken_compare_equal + + beqz t1, .Ltoken_compare_not_equal + beqz t3, .Ltoken_compare_not_equal + + lbu t4, (t0) + bne t3, t4, .Ltoken_compare_not_equal + + addi t0, t0, 1 + addi t1, t1, -1 + addi t2, t2, 1 + j .Ltoken_compare_loop + +.Ltoken_compare_not_equal: + li a0, 1 + j .Ltoken_compare_end + +.Ltoken_compare_equal: + li a0, 0 + +.Ltoken_compare_end: + ret + +.type _compile_goto, @function +_compile_goto: + # Prologue. + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + addi s0, sp, 16 + + addi s1, s1, 4 # Skip the goto keyword. + + li t0, 0x206a # j_ + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 2 + call _write_out + + call _skip_spaces + sw s1, 8(sp) # We should be on dot the label is beginning with. + addi s1, s1, 1 + + call _read_token + add s1, s1, a0 + addi a1, a0, 1 # Label length and the dot. + lw a0, 8(sp) # Saved dot position. + call _write_out + + addi s1, s1, 1 # Skip the new line. + + li t0, 0x0a # \n + sw t0, 8(sp) + addi a0, sp, 8 + li a1, 1 + call _write_out + + # Epilogue. + lw ra, 12(sp) + lw s0, 8(sp) + addi sp, sp, 16 + ret + +# a0 - Line length. +.type _compile_label, @function +_compile_label: + # Prologue. + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + addi s0, sp, 16 + + sw a0, 0(sp) # Save the line length. + + # Write the whole line as is. + mv a0, s1 + lw a1, 0(sp) + call _write_out + + lw t0, 0(sp) # Line length. + mv t1, s1 # Line start. + + add t1, t1, t0 + addi t1, t1, -1 # Last character on the line. + + lbu t1, (t1) + li t2, ':' + beq t1, t2, .Lcompile_label_colon + + li t0, 0x3a # : + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 1 + call _write_out + +.Lcompile_label_colon: + li t0, 0x0a # \n + sw t0, 4(sp) + addi a0, sp, 4 + li a1, 1 + call _write_out + + lw a0, 0(sp) + addi a0, a0, 1 # Skip the new line as well. + add s1, s1, a0 # Skip the line. + + # Epilogue. + lw ra, 12(sp) + lw s0, 8(sp) + addi sp, sp, 16 + ret + +# Parameters: +# a0 - Line length. +# a1 - Whether the section header was already emitted. If not it should be +# emitted before any code is written. +# +# Returns 1 in a0 if the parsed line contained a text section element such a +# procedure or the program entry point. Otherwise sets a0 to 0. +.type _compile_line, @function +_compile_line: + # Prologue. + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 + + # Preserve passed arguments. + sw a0, 20(sp) + sw a1, 16(sp) + + beqz a0, .Lcompile_line_empty # Skip an empty line. + + lbu t0, (s1) + li t1, '#' + beq t0, t1, .Lcompile_line_comment + + li t0, 0x0a6d6172 # ram\n + sw t0, 12(sp) + li t0, 0x676f7270 # prog + sw t0, 8(sp) + mv a0, s1 + addi a1, sp, 8 + li a2, 8 + call _memcmp + beqz a0, .Lcompile_line_program + + li t0, 0x0a74 # t\n + sw t0, 12(sp) + li t0, 0x736e6f63 # cons + sw t0, 8(sp) + mv a0, s1 + addi a1, sp, 8 + li a2, 6 + call _memcmp + beqz a0, .Lcompile_line_const + + li t0, 0x0a726176 # var\n + sw t0, 12(sp) + mv a0, s1 + addi a1, sp, 12 + li a2, 4 + call _memcmp + beqz a0, .Lcompile_line_var + + li t0, 0x20 # _ + sw t0, 12(sp) + li t0, 0x636f7270 # proc + sw t0, 8(sp) + mv a0, s1 + addi a1, sp, 8 + li a2, 5 + call _memcmp + beqz a0, .Lcompile_line_procedure + + li t0, 0x0a6e # n\n + sw t0, 12(sp) + li t0, 0x69676562 # begi + sw t0, 8(sp) + mv a0, s1 + addi a1, sp, 8 + li a2, 6 + call _memcmp + beqz a0, .Lcompile_line_begin + + li t0, 0x2e646e65 # end. + sw t0, 12(sp) + mv a0, s1 + addi a1, sp, 12 + li a2, 4 + call _memcmp + beqz a0, .Lcompile_line_exit + + li t0, 0x61636f6c # loca + sw t0, 12(sp) + mv a0, s1 + addi a1, sp, 12 + li a2, 4 + call _memcmp + beqz a0, .Lcompile_line_identifier + + li t0, 0x7472 # rt + sw t0, 12(sp) + li t0, 0x6f706d69 # impo + sw t0, 8(sp) + mv a0, s1 + addi a1, sp, 8 + li a2, 6 + call _memcmp + beqz a0, .Lcompile_line_import + + li t0, 0x6f746f67 # goto + sw t0, 12(sp) + mv a0, s1 + addi a1, sp, 12 + li a2, 4 + call _memcmp + beqz a0, .Lcompile_line_goto + + lbu t0, (s1) + li t1, '.' + beq t0, t1, .Lcompile_line_label + li t1, '_' + beq t0, t1, .Lcompile_line_identifier + + j .Lcompile_line_unchanged # Else. + +.Lcompile_line_label: + lw a0, 20(sp) + call _compile_label + j .Lcompile_line_section + +.Lcompile_line_goto: + call _compile_goto + j .Lcompile_line_section + +.Lcompile_line_import: + call _compile_import + j .Lcompile_line_section + +.Lcompile_line_identifier: + call _compile_identifier + j .Lcompile_line_section + +.Lcompile_line_exit: + call _compile_exit + j .Lcompile_line_section + +.Lcompile_line_begin: + lw a1, 16(sp) + bnez a1, .Lcompile_line_compile_entry + call _compile_text_section +.Lcompile_line_compile_entry: + call _compile_entry_point + li a0, 1 + j .Lcompile_line_end + +.Lcompile_line_const: + call _compile_constant_section + j .Lcompile_line_section + +.Lcompile_line_procedure: + lw a1, 16(sp) + bnez a1, .Lcompile_line_compile_procedure + call _compile_text_section +.Lcompile_line_compile_procedure: + call _compile_procedure + li a0, 1 + j .Lcompile_line_end + +.Lcompile_line_var: + call _compile_variable_section + j .Lcompile_line_section + +.Lcompile_line_program: + call _compile_program + j .Lcompile_line_section + +.Lcompile_line_comment: + lw a0, 20(sp) + call _skip_comment + j .Lcompile_line_section + +.Lcompile_line_empty: + addi s1, s1, 1 + j .Lcompile_line_section + +.Lcompile_line_unchanged: + lw a0, 20(sp) + call _compile_assembly + j .Lcompile_line_section + +.Lcompile_line_section: + mv a0, zero + +.Lcompile_line_end: + # Epilogue. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 + ret + +# Prints ".section .text" and exits. +.type _compile_text_section, @function +_compile_text_section: + # Prologue. + addi sp, sp, -24 + sw ra, 20(sp) + sw s0, 16(sp) + addi s0, sp, 24 + + la a0, section_text + li a1, SECTION_TEXT_SIZE + call _write_out + + # Epilogue. + lw ra, 20(sp) + lw s0, 16(sp) + addi sp, sp, 24 + ret + +.type _compile_entry_point, @function +_compile_entry_point: + # Prologue. + addi sp, sp, -64 + sw ra, 60(sp) + sw s0, 56(sp) + addi s0, sp, 64 + + # .type _start, @function + li t0, 0x0a3a7472 # rt:\n + sw t0, 52(sp) + li t0, 0x6174735f # _sta + sw t0, 48(sp) + li t0, 0x0a6e6f69 # ion\n + sw t0, 44(sp) + li t0, 0x74636e75 # unct + sw t0, 40(sp) + li t0, 0x6640202c # , @f + sw t0, 36(sp) + li t0, 0x74726174 # tart + sw t0, 32(sp) + li t0, 0x735f2065 # e _s + sw t0, 28(sp) + li t0, 0x7079742e # .typ + sw t0, 24(sp) + addi a0, sp, 24 + li a1, 32 + call _write_out + + addi s1, s1, 6 # Skip begin\n. + + # Epilogue. + lw ra, 60(sp) + lw s0, 56(sp) + addi sp, sp, 64 + ret + +.type _compile_exit, @function +_compile_exit: + # Prologue. + addi sp, sp, -64 + sw ra, 60(sp) + sw s0, 56(sp) + addi s0, sp, 64 + + # li a0, 0 + # li a7, SYS_EXIT + # ecall + li t0, 0x0a # \n + sw t0, 52(sp) + li t0, 0x6c6c6163 # call + sw t0, 48(sp) + li t0, 0x650a3339 # 93\ne + sw t0, 44(sp) + li t0, 0x202c3761 # a7,_ + sw t0, 40(sp) + li t0, 0x20696c0a # \nli_ + sw t0, 36(sp) + li t0, 0x30202c30 # 0, 0 + sw t0, 32(sp) + li t0, 0x6120696c # li a + sw t0, 28(sp) + addi a0, sp, 28 + li a1, 25 + call _write_out + + addi s1, s1, 4 # Skip end. + call _skip_spaces # Read the possible new line at the end of the file. + + # Epilogue. + lw ra, 60(sp) + lw s0, 56(sp) + addi sp, sp, 64 + ret + +# Finds the end of the line and returns its length in a0. +.type _read_line, @function +_read_line: + mv t0, s1 # Local position in the source text. + +.Lread_line_do: + lbu t1, (t0) # t1 = Current character. + beqz t1, .Lread_line_end # Exit the loop on the NUL character. + li t2, '\n' + beq t1, t2, .Lread_line_end # Exit the loop on the new line. + + addi t0, t0, 1 + j .Lread_line_do + +.Lread_line_end: + sub a0, t0, s1 # Return the line length. + ret + +.type _compile, @function +_compile: + # Prologue. + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + addi s0, sp, 16 + + sw zero, 4(sp) # Whether the text section header was already emitted. + +.Lcompile_do: + lbu t0, (s1) # t0 = Current character. + beqz t0, .Lcompile_end # Exit the loop on the NUL character. + + call _skip_indentation + call _read_line + lw a1, 4(sp) + call _compile_line + + beqz a0, .Lcompile_do + # Update whether the text section header was already emitted. + lw t0, 4(sp) + or t0, t0, a0 + sw t0, 4(sp) + + j .Lcompile_do +.Lcompile_end: + + # Epilogue. + lw ra, 12(sp) + lw s0, 8(sp) + addi sp, sp, 16 + ret + +# Entry point. +.type _start, @function +_start: + # Read the source from the standard input. + la a0, source_code + li a1, SOURCE_BUFFER_SIZE # Buffer size. + call _read_file + + la s1, source_code # s1 = Source code position. + call _compile + + # Call exit. + li a0, 0 # Use 0 return code. + call exit -- cgit v1.2.3