program # s1 - Contains the current position in the source text. const SOURCE_BUFFER_SIZE := 20480 var source_code: [20480]Byte .section .text # 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. proc _read_token() begin 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 .Ltoken_character_loop_do: # Expect an identifier or a number. 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) end # Skips the spaces till the next non space character. proc _skip_spaces() begin .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: end # Skips tabs at the line beginning. proc _skip_indentation() begin .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: end # Parameters: # a0 - Line length. proc _skip_comment() begin add s1, s1, a0 addi s1, s1, 1 # Skip the new line. end # Parameters: # a0 - Line length. proc _compile_assembly() begin 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. end proc _compile_program() begin # .global _start li t0, 0x0a7472 # rt\n sw t0, 20(sp) li t0, 0x6174735f # _sta sw t0, 16(sp) li t0, 0x206c6162 # bal_ sw t0, 12(sp) li t0, 0x6f6c672e # .glo sw t0, 8(sp) addi a0, sp, 8 li a1, 15 call write_out addi s1, s1, 8 # program\n. end proc _compile_constant_section() begin # .section .rodata li t0, 0x0a # \n sw t0, 20(sp) li t0, 0x61746164 # data sw t0, 16(sp) li t0, 0x6f722e20 # _.ro sw t0, 12(sp) li t0, 0x6e6f6974 # tion sw t0, 8(sp) li t0, 0x6365732e # .sec sw t0, 4(sp) addi a0, sp, 4 li a1, 17 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: end proc _compile_constant() begin 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 end proc _compile_variable_section() begin # .section .bss li t0, 0x0a73 # s\n sw t0, 12(sp) li t0, 0x73622e20 # _.bs sw t0, 8(sp) li t0, 0x6e6f6974 # tion sw t0, 4(sp) li t0, 0x6365732e # .sec sw t0, 0(sp) addi a0, sp, 0 li a1, 14 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: end proc _compile_variable() begin 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 end proc _compile_procedure() begin 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. li t0, 0x69646461 # addi sw t0, 12(sp) addi a0, sp, 12 li a1, 4 call write_out li t0, 0x2c707320 # _sp, sw t0, 12(sp) addi a0, sp, 12 li a1, 4 call write_out addi a0, sp, 12 li a1, 4 call write_out li t0, 0x0a36392d # -96\n sw t0, 12(sp) addi a0, sp, 12 li a1, 4 call write_out li t0, 0x0a29 # )\n sw t0, 12(sp) li t0, 0x70732832 # 2(sp sw t0, 8(sp) li t0, 0x39202c61 # a, 9 sw t0, 4(sp) li t0, 0x72207773 # sw r sw t0, 0(sp) addi a0, sp, 0 li a1, 14 call write_out li t0, 0x0a29 # )\n sw t0, 12(sp) li t0, 0x70732838 # 2(sp sw t0, 8(sp) li t0, 0x38202c30 # 0, 8 sw t0, 4(sp) li t0, 0x73207773 # sw s sw t0, 0(sp) addi a0, sp, 0 li a1, 14 call write_out li t0, 0x0a363920 # _96\n sw t0, 12(sp) li t0, 0x2c707320 # _sp, sw t0, 8(sp) li t0, 0x2c307320 # _s0, sw t0, 4(sp) li t0, 0x69646461 # addi sw t0, 0(sp) addi a0, sp, 0 li a1, 16 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. li t0, 0x0a29 # )\n sw t0, 12(sp) li t0, 0x70732832 # 2(sp sw t0, 8(sp) li t0, 0x39202c61 # a, 9 sw t0, 4(sp) li t0, 0x7220776c # lw r sw t0, 0(sp) addi a0, sp, 0 li a1, 14 call write_out li t0, 0x0a29 # )\n sw t0, 12(sp) li t0, 0x70732838 # 2(sp sw t0, 8(sp) li t0, 0x38202c30 # 0, 8 sw t0, 4(sp) li t0, 0x7320776c # lw s sw t0, 0(sp) addi a0, sp, 0 li a1, 14 call write_out li t0, 0x69646461 # addi sw t0, 12(sp) addi a0, sp, 12 li a1, 4 call write_out li t0, 0x2c707320 # _sp, sw t0, 12(sp) addi a0, sp, 12 li a1, 4 call write_out addi a0, sp, 12 li a1, 4 call write_out li t0, 0x0a3639 # 96\n sw t0, 12(sp) addi a0, sp, 12 li a1, 3 call write_out li t0, 0x0a746572 # ret\n sw t0, 12(sp) addi a0, sp, 12 li a1, 4 call write_out end # 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. proc _token_compare() begin 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: end # Parameters: # a0 - Line length. # 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. proc _compile_line() begin sw a0, 20(sp) # a0 - Line length. 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, 16(sp) li t0, 0x676f7270 # prog sw t0, 12(sp) mv a0, s1 addi a1, sp, 12 li a2, 8 call memcmp beqz a0, .Lcompile_line_program li t0, 0x0a74 # t\n sw t0, 16(sp) li t0, 0x736e6f63 # cons sw t0, 12(sp) mv a0, s1 addi a1, sp, 12 li a2, 6 call memcmp beqz a0, .Lcompile_line_const li t0, 0x0a726176 # var\n sw t0, 16(sp) mv a0, s1 addi a1, sp, 16 li a2, 4 call memcmp beqz a0, .Lcompile_line_var li t0, 0x20 # _ sw t0, 16(sp) li t0, 0x636f7270 # proc sw t0, 12(sp) mv a0, s1 addi a1, sp, 12 li a2, 5 call memcmp beqz a0, .Lcompile_line_procedure li t0, 0x0a6e # n\n sw t0, 16(sp) li t0, 0x69676562 # begi sw t0, 12(sp) mv a0, s1 addi a1, sp, 12 li a2, 6 call memcmp beqz a0, .Lcompile_line_begin li t0, 0x2e646e65 # end. sw t0, 16(sp) mv a0, s1 addi a1, sp, 16 li a2, 4 call memcmp beqz a0, .Lcompile_line_exit j .Lcompile_line_unchanged # Else. .Lcompile_line_exit: call _compile_exit j .Lcompile_line_section .Lcompile_line_begin: 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: call _compile_procedure li a0, 1 j .Lcompile_line_end .Lcompile_line_var: /* DEBUG mv a0, s1 li a1, 20 call write_error */ 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: end proc _compile_text_section() begin # .section .text li t0, 0x0a7478 # xt\n sw t0, 12(sp) li t0, 0x65742e20 # _.te sw t0, 8(sp) li t0, 0x6e6f6974 # tion sw t0, 4(sp) li t0, 0x6365732e # .sec sw t0, 0(sp) addi a0, sp, 0 li a1, 15 call write_out end proc _compile_entry_point() begin # .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. end proc _compile_exit() begin # 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. end # Finds the end of the line and returns its length in a0. proc _read_line() begin 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. end proc _compile() begin .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 call _compile_line j .Lcompile_do .Lcompile_end: end # Entry point. begin # Read the source from the standard input. la a0, source_code la a1, SOURCE_BUFFER_SIZE # Buffer size. lw a1, (a1) call read_file la s1, source_code # s1 = Source code position. call _compile end.