diff --git a/Rakefile b/Rakefile index 71b3293..57d3784 100644 --- a/Rakefile +++ b/Rakefile @@ -7,28 +7,12 @@ require 'open3' require 'rake/clean' require 'term/ansicolor' -CLOBBER.include 'build' +CLEAN.include 'build/boot' -CROSS_GCC = '../riscv32-ilp32d--glibc/bin/riscv32-linux-gcc' -SYSROOT = '../riscv32-ilp32d--glibc/riscv32-buildroot-linux-gnu/sysroot' -QEMU = 'qemu-riscv32' - -def assemble_stage(output, compiler, source) - arguments = [QEMU, '-L', SYSROOT, *compiler] - - puts Term::ANSIColor.green(arguments * ' ') - puts - Open3.popen2(*arguments) do |qemu_in, qemu_out| - qemu_in.write File.read(*source) - qemu_in.close - - IO.copy_stream qemu_out, output - qemu_out.close - end -end +directory 'build/boot' desc 'Final stage' -task default: ['build/stage2b', 'build/stage2b.s', 'boot/stage2.elna'] do |t| +task default: ['build/boot/stage2b', 'build/boot/stage2b.s', 'boot/stage2.elna'] do |t| exe, previous_output, source = t.prerequisites cat_arguments = ['cat', source] @@ -36,40 +20,3 @@ task default: ['build/stage2b', 'build/stage2b.s', 'boot/stage2.elna'] do |t| diff_arguments = ['diff', '-Nur', '--text', previous_output, '-'] Open3.pipeline(cat_arguments, compiler_arguments, diff_arguments) end - -directory 'build' - -Dir.glob('boot/*.s').each do |assembly_source| - target_object = Pathname.new('build') + Pathname.new(assembly_source).basename.sub_ext('.o') - - file target_object.to_s => [assembly_source, 'build'] do |t| - sh CROSS_GCC, '-c', '-o', t.name, assembly_source - end -end - -desc 'Initial stage' -file 'build/stage1' => ['build/tokenizer.o', 'build/stage1.o', 'build/common-boot.o'] do |t| - sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites -end - -file 'build/stage2a.s' => ['build/stage1', 'boot/stage2.elna'] do |t| - source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' } - - File.open t.name, 'w' do |output| - assemble_stage output, exe, source - end -end - -['build/stage2a', 'build/stage2b'].each do |exe| - file exe => [exe.ext('.s'), 'build/common-boot.o'] do |t| - sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites - end -end - -file 'build/stage2b.s' => ['build/stage2a', 'boot/stage2.elna'] do |t| - source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' } - - File.open t.name, 'w' do |output| - assemble_stage output, exe, source - end -end diff --git a/boot/definitions.inc b/boot/definitions.inc index d61a5f6..97f6601 100644 --- a/boot/definitions.inc +++ b/boot/definitions.inc @@ -34,20 +34,21 @@ .equ TOKEN_IDENTIFIER, 27 # The constant should match the character index in the byte_keywords string. -.equ TOKEN_AND, 28 -.equ TOKEN_DOT, 29 -.equ TOKEN_COMMA, 30 -.equ TOKEN_COLON, 31 -.equ TOKEN_SEMICOLON, 32 -.equ TOKEN_LEFT_PAREN, 33 -.equ TOKEN_RIGHT_PAREN, 34 -.equ TOKEN_LEFT_BRACKET, 35 -.equ TOKEN_RIGHT_BRACKET, 36 -.equ TOKEN_HAT, 37 -.equ TOKEN_EQUALS, 38 -.equ TOKEN_PLUS, 39 -.equ TOKEN_MINUS, 40 -.equ TOKEN_ASTERISK, 41 -.equ TOKEN_AT, 42 +.equ TOKEN_AND, TOKEN_IDENTIFIER + 1 +.equ TOKEN_DOT, TOKEN_IDENTIFIER + 2 +.equ TOKEN_COMMA, TOKEN_IDENTIFIER + 3 +.equ TOKEN_COLON, TOKEN_IDENTIFIER + 4 +.equ TOKEN_SEMICOLON, TOKEN_IDENTIFIER + 5 +.equ TOKEN_LEFT_PAREN, TOKEN_IDENTIFIER + 6 +.equ TOKEN_RIGHT_PAREN, TOKEN_IDENTIFIER + 7 +.equ TOKEN_LEFT_BRACKET, TOKEN_IDENTIFIER + 8 +.equ TOKEN_RIGHT_BRACKET, TOKEN_IDENTIFIER + 9 +.equ TOKEN_HAT, TOKEN_IDENTIFIER + 10 +.equ TOKEN_EQUALS, TOKEN_IDENTIFIER + 11 +.equ TOKEN_PLUS, TOKEN_IDENTIFIER + 12 +.equ TOKEN_MINUS, TOKEN_IDENTIFIER + 13 +.equ TOKEN_ASTERISK, TOKEN_IDENTIFIER + 14 +.equ TOKEN_AT, TOKEN_IDENTIFIER + 15 .equ TOKEN_ASSIGN, 43 +.equ TOKEN_INTEGER, 44 diff --git a/boot/stage1.s b/boot/stage1.s index c86e872..b32815d 100644 --- a/boot/stage1.s +++ b/boot/stage1.s @@ -4,9 +4,29 @@ .global _start # Program entry point. +# # Registers used as global variables: # s1 - Contains the current position in the source text. # s2 - Label counter. +# +# - The compiler expects valid input, otherwise it will generate invalid +# assembly or hang. There is no error checking, no semantic analysis, no +# type checking. +# +# - Imports with only a module name without package, e.g. +# "import dummy", can be parsed, but are ignored. +# +# - No loops. Only labels and goto. +# +# - Only unsigned number literals are supported (in decimal or +# hexadecimal format). +# +# - Comments are accepted only at the end of a line. +# +# - Return can be used only as the last statement of a procedure. It +# doesn't actually return, but sets a0 to the appropriate value. +# +# - The lvalue of an assignment can only be an identifier. .include "boot/definitions.inc" @@ -73,9 +93,6 @@ _compile_import: addi s0, sp, 24 .Lcompile_import_loop: - call _skip_comment - call _skip_spaces - mv a0, s1 addi a1, sp, 0 call _tokenize_next @@ -106,7 +123,6 @@ _build_binary_expression: li a0, 0 call _build_expression - call _skip_spaces mv a0, s1 addi a1, sp, 12 @@ -114,26 +130,26 @@ _build_binary_expression: lw t0, 12(sp) li t1, TOKEN_AND - beq t0, t1, .L_build_binary_expression_and + beq t0, t1, .Lbuild_binary_expression_and li t1, TOKEN_OR - beq t0, t1, .L_build_binary_expression_or + beq t0, t1, .Lbuild_binary_expression_or li t1, TOKEN_PLUS - beq t0, t1, .L_build_binary_expression_plus + beq t0, t1, .Lbuild_binary_expression_plus li t1, TOKEN_EQUALS - beq t0, t1, .L_build_binary_expression_equal + beq t0, t1, .Lbuild_binary_expression_equal li t1, TOKEN_ASTERISK - beq t0, t1, .L_build_binary_expression_product + beq t0, t1, .Lbuild_binary_expression_product li t1, TOKEN_MINUS - beq t0, t1, .L_build_binary_expression_minus + beq t0, t1, .Lbuild_binary_expression_minus j .Lbuild_binary_expression_end -.L_build_binary_expression_equal: +.Lbuild_binary_expression_equal: mv s1, a0 # Skip =. li a0, 1 call _build_expression @@ -147,7 +163,7 @@ _build_binary_expression: j .Lbuild_binary_expression_end -.L_build_binary_expression_and: +.Lbuild_binary_expression_and: mv s1, a0 # Skip &. li a0, 1 call _build_expression @@ -157,7 +173,7 @@ _build_binary_expression: j .Lbuild_binary_expression_end -.L_build_binary_expression_or: +.Lbuild_binary_expression_or: mv s1, a0 # Skip or. li a0, 1 call _build_expression @@ -167,7 +183,7 @@ _build_binary_expression: j .Lbuild_binary_expression_end -.L_build_binary_expression_plus: +.Lbuild_binary_expression_plus: mv s1, a0 # Skip +. li a0, 1 call _build_expression @@ -177,7 +193,7 @@ _build_binary_expression: j .Lbuild_binary_expression_end -.L_build_binary_expression_minus: +.Lbuild_binary_expression_minus: mv s1, a0 # Skip -. li a0, 1 call _build_expression @@ -187,7 +203,7 @@ _build_binary_expression: j .Lbuild_binary_expression_end -.L_build_binary_expression_product: +.Lbuild_binary_expression_product: mv s1, a0 # Skip *. li a0, 1 call _build_expression @@ -448,7 +464,6 @@ _build_expression: call _write_out addi s1, s1, 1 # Skip @. - call _skip_spaces call _read_token sw s1, 32(sp) sw a0, 28(sp) @@ -654,7 +669,6 @@ _compile_call: sw zero, 12(sp) # Argument count for a procedure call. .Lcompile_call_paren: - call _skip_spaces lbu t0, (s1) li t1, 0x29 # ) beq t0, t1, .Lcompile_call_complete @@ -688,7 +702,6 @@ _compile_call: li a1, 5 call _write_out - call _skip_spaces lbu t0, (s1) li t1, ',' bne t0, t1, .Lcompile_call_paren @@ -726,7 +739,6 @@ _compile_call: li a0, '\n' call _put_char - call _skip_spaces addi s1, s1, 1 # Skip the close paren. # Epilogue. @@ -912,7 +924,6 @@ _compile_procedure_section: .Lcompile_procedure_section_loop: call _skip_spaces call _skip_comment - call _skip_spaces mv a0, s1 addi a1, sp, 4 @@ -945,7 +956,6 @@ _compile_module_declaration: call _write_out # Skip "program". - call _skip_comment mv a0, s1 addi a1, sp, 4 call _tokenize_next @@ -965,9 +975,6 @@ _compile_constant_section: sw s0, 16(sp) addi s0, sp, 24 - call _skip_comment - call _skip_spaces - mv a0, s1 addi a1, sp, 4 call _tokenize_next @@ -999,25 +1006,22 @@ _compile_constant_section: .type _compile_constant, @function _compile_constant: # Prologue. - addi sp, sp, -24 - sw ra, 20(sp) - sw s0, 16(sp) - addi s0, sp, 24 + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 mv a0, s1 - addi a1, sp, 4 + addi a1, sp, 12 call _tokenize_next - - sub a1, a0, s1 # The identifier end from _tokenize_next should be in a0. - mv a0, s1 - add s1, s1, a1 # Save the identifier pointer before advancing it. + addi a1, sp, 0 + call _tokenize_next # Skip the assignment sign. + mv s1, a0 + # Write identifier the identifier. + lw a0, 20(sp) + lw a1, 16(sp) call _write_out - mv a0, s1 - addi a1, sp, 4 - call _tokenize_next - mv s1, a0 # Skip the assignment sign. - # : .long li t0, 0x20676e6f # ong_ sw t0, 4(sp) @@ -1027,21 +1031,23 @@ _compile_constant: li a1, 8 call _write_out - call _skip_spaces - call _read_token + mv a0, s1 + addi a1, sp, 12 + call _tokenize_next + mv s1, a0 - 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 + lw a0, 20(sp) # Save the literal pointer before advancing it. + lw a1, 16(sp) # The literal length. call _write_out li a0, '\n' call _put_char + call _skip_spaces # Epilogue. - lw ra, 20(sp) - lw s0, 16(sp) - addi sp, sp, 24 + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 ret .type _compile_variable_section, @function @@ -1080,6 +1086,7 @@ _compile_variable_section: addi sp, sp, 24 ret +# Compile a global variable. .type _compile_variable, @function _compile_variable: # Prologue. @@ -1088,32 +1095,33 @@ _compile_variable: sw s0, 40(sp) addi s0, sp, 48 - call _read_token - # Save the identifier on the stack since it should emitted multiple times. - sw s1, 36(sp) - sw a0, 32(sp) - add s1, s1, a0 + mv a0, s1 + addi a1, sp, 28 + call _tokenize_next + addi a1, sp, 4 + call _tokenize_next # Skip the colon in front of the type. + addi a1, sp, 4 + call _tokenize_next # Skip the opening bracket. + addi a1, sp, 16 + call _tokenize_next # Save the array size on the stack since it has to be emitted multiple times. + addi a1, sp, 4 + call _tokenize_next # Skip the closing bracket. + addi a1, sp, 4 + call _tokenize_next # Skip the type. + mv 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, 28(sp) + /* DEBUG + lw a0, 24(sp) + add a0, a0, '0' sw a0, 24(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. + addi a0, sp, 24 + li a1, 1 + call _write_error + lw a0, 28(sp) + li a1, 8 + call _write_error + */ # .type identifier, @object la a0, asm_type @@ -1134,15 +1142,15 @@ _compile_variable: call _write_out li t0, 0x206f7265 # ero_ - sw t0, 20(sp) + sw t0, 12(sp) li t0, 0x7a2e203a # : .z - sw t0, 16(sp) - addi a0, sp, 16 + sw t0, 8(sp) + addi a0, sp, 8 li a1, 8 call _write_out - lw a0, 28(sp) - lw a1, 24(sp) + lw a0, 24(sp) + lw a1, 20(sp) call _write_out li a0, '\n' @@ -1215,9 +1223,6 @@ _compile_procedure: # Generate the body of the procedure. .Lcompile_procedure_body: - call _skip_spaces - call _read_line - sw a0, 12(sp) li t0, 0x0a646e65 # end\n sw t0, 8(sp) mv a0, s1 @@ -1227,7 +1232,6 @@ _compile_procedure: beqz a0, .Lcompile_procedure_end - lw a0, 12(sp) call _compile_statement j .Lcompile_procedure_body @@ -1245,111 +1249,95 @@ _compile_procedure: addi sp, sp, 32 ret +# Compiles a goto statement to an uncoditional jump. .type _compile_goto, @function _compile_goto: # Prologue. - addi sp, sp, -16 - sw ra, 12(sp) - sw s0, 8(sp) - addi s0, sp, 16 + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 - addi s1, s1, 4 # Skip the goto keyword. + mv a0, s1 + addi a1, sp, 0 + call _tokenize_next # Skip the goto keyword. + addi a1, sp, 0 + call _tokenize_next # We should be on dot the label is beginning with. + addi a1, sp, 0 + call _tokenize_next# Save the label name. + mv s1, a0 - li t0, 0x206a # j_ - sw t0, 8(sp) - addi a0, sp, 8 - li a1, 2 + li t0, 0x2e206a # j . + sw t0, 12(sp) + addi a0, sp, 12 + li a1, 3 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. + lw a1, 4(sp) call _write_out - addi s1, s1, 1 # Skip the new line. - li a0, '\n' call _put_char # Epilogue. - lw ra, 12(sp) - lw s0, 8(sp) - addi sp, sp, 16 + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 ret -# a0 - Line length. +# Rewrites a label to assembly. .type _compile_label, @function _compile_label: # Prologue. - addi sp, sp, -16 - sw ra, 12(sp) - sw s0, 8(sp) - addi s0, sp, 16 + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 - sw a0, 0(sp) # Save the line length. - mv a1, a0 # Argument for _write_out later. - - 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, ';' - bne t1, t2, .Lcompile_label_colon - - addi a1, a1, -1 - -.Lcompile_label_colon: - # Write the whole line as is. mv a0, s1 - call _write_out + addi a1, sp, 8 + call _tokenize_next # Dot starting the label. + addi a1, sp, 8 + call _tokenize_next + mv s1, a0 - li t0, 0x3a # : - sw t0, 4(sp) - addi a0, sp, 4 - li a1, 1 + li a0, '.' + call _put_char + lw a0, 16(sp) + lw a1, 12(sp) call _write_out - - li t0, '\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. + li a0, ':' + call _put_char + li a0, '\n' + call _put_char # Epilogue. - lw ra, 12(sp) - lw s0, 8(sp) - addi sp, sp, 16 + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 ret +# Just skips the return keyword and evaluates the return expression. .type _compile_return, @function _compile_return: # Prologue. - addi sp, sp, -16 - sw ra, 12(sp) - sw s0, 8(sp) - addi s0, sp, 16 + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 - addi s1, s1, 6 # Skip return. - call _skip_spaces + mv a0, s1 + addi a1, sp, 12 + call _tokenize_next + mv s1, a0 # Skip return. call _build_binary_expression # Epilogue. - lw ra, 12(sp) - lw s0, 8(sp) - addi sp, sp, 16 + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 ret .type _compile_if, @function @@ -1403,8 +1391,6 @@ _compile_if: li t1, TOKEN_END beq t0, t1, .Lcompile_if_end - call _read_line - li a1, 1 call _compile_statement j .Lcompile_if_loop @@ -1422,7 +1408,7 @@ _compile_if: call _printi # Finalize the label. - li t0, 0x0a3a # :\n:\n + li t0, 0x0a3a # :\n sh t0, 16(sp) addi a0, sp, 16 li a1, 2 @@ -1436,11 +1422,7 @@ _compile_if: addi sp, sp, 32 ret -# 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. +# Checks for the type of the current statement and compiles it. .type _compile_statement, @function _compile_statement: # Prologue. @@ -1449,42 +1431,17 @@ _compile_statement: sw s0, 24(sp) addi s0, sp, 32 - # Preserve passed arguments. - sw a0, 20(sp) - - mv a0, s1 - lw a1, 20(sp) - call _is_local_identifier - bnez a0, .Lcompile_statement_identifier - - mv a0, s1 - li a1, 2 - call _is_register_identifier - bnez a0, .Lcompile_statement_identifier - - li t0, 0x6f746f67 # goto - sw t0, 12(sp) - mv a0, s1 - addi a1, sp, 12 - li a2, 4 - call _memcmp - beqz a0, .Lcompile_statement_goto - - call _skip_comment - /* DEBUG - mv a0, s1 - li a1, 4 - call _write_error - mv a0, s1 - li a1, 4 - call _write_error - */ - mv a0, s1 addi a1, sp, 0 call _tokenize_next lw t0, 0(sp) + li t1, TOKEN_IDENTIFIER + beq t0, t1, .Lcompile_statement_identifier + + li t1, TOKEN_GOTO + beq t0, t1, .Lcompile_statement_goto + li t1, TOKEN_RETURN beq t0, t1, .Lcompile_statement_return @@ -1494,10 +1451,6 @@ _compile_statement: li t1, TOKEN_DOT beq t0, t1, .Lcompile_statement_label - lbu t0, (s1) - li t1, '_' - beq t0, t1, .Lcompile_statement_identifier - j .Lcompile_statement_empty # Else. .Lcompile_statement_if: @@ -1505,7 +1458,6 @@ _compile_statement: j .Lcompile_statement_end .Lcompile_statement_label: - lw a0, 20(sp) call _compile_label j .Lcompile_statement_end @@ -1527,7 +1479,6 @@ _compile_statement: .Lcompile_statement_end: sw a0, 12(sp) - call _skip_spaces call _skip_comment lw a0, 12(sp) @@ -1559,10 +1510,10 @@ _compile_text_section: .type _compile_entry_point, @function _compile_entry_point: # Prologue. - addi sp, sp, -8 - sw ra, 4(sp) - sw s0, 0(sp) - addi s0, sp, 8 + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 # .type _start, @function la a0, asm_start @@ -1581,7 +1532,6 @@ _compile_entry_point: li t1, TOKEN_END beq t0, t1, .Lcompile_entry_point_end - lw a0, 12(sp) call _compile_statement j .Lcompile_entry_point_body @@ -1593,27 +1543,9 @@ _compile_entry_point: call _write_out # Epilogue. - lw ra, 4(sp) - lw s0, 0(sp) - addi sp, sp, 8 - 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. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 ret .type _compile, @function diff --git a/boot/stage2.elna b/boot/stage2.elna index 366096e..50f1303 100644 --- a/boot/stage2.elna +++ b/boot/stage2.elna @@ -1,26 +1,3 @@ -(* -s1 - Contains the current position in the source text. -s2 - Label counter. - -- The compiler expects valid input, otherwise it will generate invalid -assembly or hang. There is no error checking, no semantic analysis, no -type checking. - -- Imports with only a module name without package, e.g. -"import dummy", can be parsed, but are ignored. - -- No loops. Only labels and goto. - -- Only unsigned number literals are supported (in decimal or -hexadecimal format). - -- Comments are accepted only at the end of a line. - -- Return can be used only as the last statement of a procedure. It -doesn't actually return, but sets a0 to the appropriate value. - -- The lvalue of an assignment can only be an identifier. -*) program import dummy @@ -38,7 +15,7 @@ begin _advance(6); _skip_spaces(); loca0 := _read_token(); - _advance(loca0) (* Skip the imported module name. *) + _advance(loca0) end proc _build_binary_expression() @@ -49,44 +26,44 @@ var begin _build_expression(0); - loca4 := 0x2c306120 (* _a0, *); - loca8 := 0x0a316120 (* _a1\n *); + loca4 := 0x2c306120; + loca8 := 0x0a316120; _skip_spaces(); loca20 := _read_token(); loca12 := _current(); - loca16 := 0x26 (* & *); + loca16 := 0x26; loca24 := _token_compare(loca12, loca20, @loca16); if loca24 = 0 then goto .L_build_binary_expression_and end; - loca16 := 0x726f (* or *); + loca16 := 0x726f; loca24 := _token_compare(loca12, loca20, @loca16); if loca24 = 0 then goto .L_build_binary_expression_or end; - loca16 := 0x3d (* = *); + loca16 := 0x3d; loca24 := _token_compare(loca12, loca20, @loca16); if loca24 = 0 then goto .L_build_binary_expression_equal end; - loca16 := 0x2b (* + *); + loca16 := 0x2b; loca24 := _token_compare(loca12, loca20, @loca16); if loca24 = 0 then goto .L_build_binary_expression_plus end; - loca16 := 0x2d (* - *); + loca16 := 0x2d; loca24 := _token_compare(loca12, loca20, @loca16); if loca24 = 0 then goto .L_build_binary_expression_minus end - loca16 := 0x2a (* * *); + loca16 := 0x2a; loca24 := _token_compare(loca12, loca20, @loca16); if loca24 = 0 then goto .L_build_binary_expression_product @@ -95,27 +72,27 @@ begin goto .Lbuild_binary_expression_end; .L_build_binary_expression_equal; - _advance(1) (* Skip =. *); + _advance(1); _build_expression(1); - loca0 := 0x627573(* sub *); + loca0 := 0x627573; _write_out(@loca0, 3); _write_out(@loca4, 4); _write_out(@loca4, 4); _write_out(@loca8, 4); - loca0 := 0x7a716573 (* seqz *); + loca0 := 0x7a716573; _write_out(@loca0, 4); _write_out(@loca4, 4); _write_out(@loca4, 3); - _put_char(0x0a) (* \n *); + _put_char(0x0a); goto .Lbuild_binary_expression_end; .L_build_binary_expression_and; - _advance(1) (* Skip &. *); + _advance(1); _build_expression(1); - loca0 := 0x646e61 (* and *); + loca0 := 0x646e61; _write_out(@loca0, 3); _write_out(@loca4, 4); _write_out(@loca4, 4); @@ -124,9 +101,9 @@ begin goto .Lbuild_binary_expression_end; .L_build_binary_expression_or; - _advance(2) (* Skip or. *); + _advance(2); _build_expression(1); - loca0 := 0x726f (* or *); + loca0 := 0x726f; _write_out(@loca0, 2); _write_out(@loca4, 4); _write_out(@loca4, 4); @@ -135,9 +112,9 @@ begin goto .Lbuild_binary_expression_end; .L_build_binary_expression_plus; - _advance(1) (* Skip +. *); + _advance(1); _build_expression(1); - loca0 := 0x646461 (* add *); + loca0 := 0x646461; _write_out(@loca0, 3); _write_out(@loca4, 4); _write_out(@loca4, 4); @@ -146,9 +123,9 @@ begin goto .Lbuild_binary_expression_end; .L_build_binary_expression_minus; - _advance(1) (* Skip -. *); + _advance(1); _build_expression(1); - loca0 := 0x627573 (* sub *); + loca0 := 0x627573; _write_out(@loca0, 3); _write_out(@loca4, 4); _write_out(@loca4, 4); @@ -157,9 +134,9 @@ begin goto .Lbuild_binary_expression_end; .L_build_binary_expression_product; - _advance(1) (* Skip *. *); + _advance(1); _build_expression(1); - loca0 := 0x6c756d (* mul *); + loca0 := 0x6c756d; _write_out(@loca0, 3); _write_out(@loca4, 4); _write_out(@loca4, 4); @@ -170,30 +147,25 @@ begin .Lbuild_binary_expression_end end -(* -Parameters: -a0 - Identifier length. -a1 - Register number as character. -*) proc _compile_identifier_expression(loca84: Word, loca80: Byte) begin loca24 := _current(); - loca0 := 0x61636f6c (* loca *); + loca0 := 0x61636f6c; loca0 := _memcmp(@loca0, loca24, 4); if loca0 = 0 then - loca8 := 0x6120776c (* lw a *); + loca8 := 0x6120776c; _write_out(@loca8, 4); - loca8 := 0x00202c00 or loca80 (* \0,_ *); + loca8 := 0x00202c00 or loca80; _write_out(@loca8, 3); loca4 := loca24 + 4; loca0 := loca84 - 4; - _write_out(loca4, loca0) (* Skip the "loca" variable prefix. *); + _write_out(loca4, loca0); - loca8 := 0x29707328 (* (sp) *); + loca8 := 0x29707328; _write_out(@loca8, 4); - _put_char(0x0a) (* \n *); + _put_char(0x0a); goto .Lcompile_identifier_expression_end end; @@ -201,18 +173,18 @@ begin loca8 := loca84 = 2; loca12 := loca0 = 0x73; if loca8 & loca12 then - loca8 := 0x6120766d (* mv a *); + loca8 := 0x6120766d; _write_out(@loca8, 4); - loca8 := 0x00202c00 or loca80 (* \0,_ *); + loca8 := 0x00202c00 or loca80; _write_out(@loca8, 3); _write_out(loca24, loca84); - _put_char(0x0a) (* \n *); + _put_char(0x0a); goto .Lcompile_identifier_expression_end end (* Global identifier. *); - loca8 := 0x6120616c (* la a *); + loca8 := 0x6120616c; _write_out(@loca8, 4); loca8 := 0x00202c00 or loca80; _write_out(@loca8, 3); @@ -221,14 +193,14 @@ begin _put_char(0x0a); if _is_upper(loca0) then - loca8 := 0x6120776c (* lw a *); + loca8 := 0x6120776c; _write_out(@loca8, 4); - loca8 := 0x28202c00 or loca28 (* \0, ( *); + loca8 := 0x28202c00 or loca28; _write_out(@loca8, 4); - _put_char(0x61) (* a *); + _put_char(0x61); _put_char(loca28); - _put_char(0x29) (* ) *); - _put_char(0x0a) (* \n *); + _put_char(0x29); + _put_char(0x0a); goto .Lcompile_identifier_expression_end end; @@ -246,20 +218,17 @@ var loca0, loca20, loca28, loca8: Word loca24, loca4: ^Byte begin - (* Make the register number to a character and save it. *) - loca28 := loca84 + 0x30 (* 0 *); + loca28 := loca84 + 0x30; _skip_spaces(); loca20 := _read_token(); loca24 := _current(); loca0 := _front(loca24); - (* - *) if loca0 = 0x2d then goto .Lbuild_expression_negate end; - (* @ *) if loca0 = 0x40 then goto .Lbuild_expression_address end; @@ -268,7 +237,6 @@ begin goto .Lbuild_expression_literal end; - (* _ *) if loca0 = 0x5f then goto .Lbuild_expression_call end; @@ -277,35 +245,34 @@ begin goto .Lbuild_expression_advance; .Lbuild_expression_negate; - _advance(1) (* Skip the -. *); + _advance(1); _build_expression(0); - loca8 := 0x2067656e (* neg_ *); + loca8 := 0x2067656e; _write_out(@loca8, 4); - loca8 := 0x202c3061 (* a0,_ *); + loca8 := 0x202c3061; _write_out(@loca8, 4); - loca8 := 0x0a3061 (* a0,_ *); + loca8 := 0x0a3061; _write_out(@loca8, 3); goto .Lbuild_expression_advance; .Lbuild_expression_address; - loca8 := 0x69646461 (* addi *); + loca8 := 0x69646461; _write_out(@loca8, 4); - loca8 := 0x6120 (* _a *); + loca8 := 0x6120; _write_out(@loca8, 2); _put_char(loca28); - loca8 := 0x7073202c (* , sp *); + loca8 := 0x7073202c; _write_out(@loca8, 4); - loca8 := 0x202c (* ,_ *); + loca8 := 0x202c; _write_out(@loca8, 2); - _advance(1) (* Skip @. *); + _advance(1); _skip_spaces(); loca24 := _current(); loca20 := _read_token(); - (* Skip the "loca" variable prefix. *) loca4 := loca24 + 4; loca0 := loca20 - 4; _write_out(loca4, loca0); @@ -322,13 +289,13 @@ begin goto .Lbuild_expression_end; .Lbuild_expression_literal; - loca8 := 0x6120696c (* li a *); + loca8 := 0x6120696c; _write_out(@loca8, 4); - loca8 := 0x00202c00 or loca28 (* \0,_ *); + loca8 := 0x00202c00 or loca28; _write_out(@loca8, 3); _write_out(loca24, loca20); - _put_char(0x0a) (* \n *); + _put_char(0x0a); goto .Lbuild_expression_advance; @@ -349,35 +316,35 @@ proc _compile_designator_expression(loca84: ^Byte, loca80: Word) var loca0: Word begin - loca0 := 0x61636f6c (* loca *); + loca0 := 0x61636f6c; loca4 := _memcmp(@loca0, loca84, 4); if loca4 = 0 then - loca0 := 0x61207773 (* sw a *); + loca0 := 0x61207773; _write_out(@loca0, 4); - loca0 := 0x202c30 (* 0,_ *); + loca0 := 0x202c30; _write_out(@loca0, 3); loca84 := loca84 + 4; loca80 := loca80 - 4; _write_out(loca84, loca80); - loca0 := 0x29707328 (* (sp) *); + loca0 := 0x29707328; _write_out(@loca0, 4); - _put_char(0x0a) (* \n *); + _put_char(0x0a); goto .Lcompile_designator_expression_end end; loca8 := _front(loca84); - loca12 := loca8 = 0x73 (* s *); + loca12 := loca8 = 0x73; loca16 := loca80 = 2; if loca12 & loca16 then - loca0 := 0x20766d (* mv_ *); + loca0 := 0x20766d; _write_out(@loca0, 3); _write_out(loca84, loca80); - loca0 := 0x3061202c (* , a0 *); + loca0 := 0x3061202c; _write_out(@loca0, 4); - _put_char(0x0a) (* \n *); + _put_char(0x0a); goto .Lcompile_designator_expression_end end; @@ -397,21 +364,19 @@ var loca20, loca12: ^Byte loca4: Bool begin - (* Save the pointer to the identifier and its length on the stack. *) loca20 := _current(); loca16 := _read_token(); _advance(loca16); _skip_spaces(); - (* Save the pointer and the length of the token following the identifier. *) loca12 := _current(); loca8 := _read_token(); - _advance(loca8) (* Skip that token. *); + _advance(loca8); _skip_spaces(); - loca0 := 0x3d3a (* := *); + loca0 := 0x3d3a; loca4 := _token_compare(loca12, loca8, @loca0); if loca4 = 0 then _build_binary_expression(); @@ -440,7 +405,7 @@ var loca0, loca4, loca12: Word loca8: ^Byte begin - loca12 := 0 (* Argument count for a procedure call. *); + loca12 := 0; .Lcompile_call_paren; _skip_spaces(); @@ -452,9 +417,9 @@ begin .Lcompile_call_argument; _build_expression(0); - loca0 := 0x61207773 (* sw a *); + loca0 := 0x61207773; _write_out(@loca0, 4); - loca0 := 0x202c30 (* 0,_ *); + loca0 := 0x202c30; _write_out(@loca0, 3); (* @@ -465,9 +430,9 @@ begin loca0 := loca0 + 60; _printi(loca0); - loca0 := 0x29707328 (* (sp) *); + loca0 := 0x29707328; _write_out(@loca0, 4); - _put_char(0x0a) (* \n *); + _put_char(0x0a); _skip_spaces(); loca8 := _current(); @@ -476,9 +441,9 @@ begin goto .Lcompile_call_paren end - loca12 := loca12 + 1 (* Argument count for a procedure call. *); + loca12 := loca12 + 1; - _advance(1) (* Skip the comma between the arguments. *); + _advance(1); goto .Lcompile_call_argument; .Lcompile_call_complete; @@ -489,70 +454,64 @@ begin 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. *) - loca0 := 0x6120776c (* lw a *); - (* lw a0, 60(sp) *) + loca0 := 0x6120776c; _write_out(@loca0, 4); - loca4 := 0x36202c30 (* 0, 6 *); + loca4 := 0x36202c30; _write_out(@loca4, 4); - loca4 := 0x70732830 (* 0(sp *); + loca4 := 0x70732830; _write_out(@loca4, 4); - loca4 := 0x0a29 (* )\n *); + loca4 := 0x0a29; _write_out(@loca4, 2); - (* lw a1, 56(sp) *) _write_out(@loca0, 4); - loca4 := 0x35202c31 (* 1, 5 *); + loca4 := 0x35202c31; _write_out(@loca4, 4); - loca4 := 0x70732836 (* 6(sp *); + loca4 := 0x70732836; _write_out(@loca4, 4); - loca4 := 0x0a29 (* )\n *); + loca4 := 0x0a29; _write_out(@loca4, 2); - (* lw a2, 52(sp) *) _write_out(@loca0, 4); - loca4 := 0x35202c32 (* 2, 5 *); + loca4 := 0x35202c32; _write_out(@loca4, 4); - loca4 := 0x70732832 (* 2(sp *); + loca4 := 0x70732832; _write_out(@loca4, 4); - loca4 := 0x0a29 (* )\n *); + loca4 := 0x0a29; _write_out(@loca4, 2); - (* lw a3, 48(sp) *) _write_out(@loca0, 4); - loca4 := 0x34202c33 (* 3, 4 *); + loca4 := 0x34202c33; _write_out(@loca4, 4); - loca4 := 0x70732838 (* 8(sp *); + loca4 := 0x70732838; _write_out(@loca4, 4); - loca4 := 0x0a29 (* )\n *); + loca4 := 0x0a29; _write_out(@loca4, 2); - (* lw a4, 44(sp) *) _write_out(@loca0, 4); - loca4 := 0x34202c34 (* 4, 4 *); + loca4 := 0x34202c34; _write_out(@loca4, 4); - loca4 := 0x70732834 (* 4(sp *); + loca4 := 0x70732834; _write_out(@loca4, 4); - loca4 := 0x0a29 (* )\n *); + loca4 := 0x0a29; _write_out(@loca4, 2); - (* lw a5, 40(sp) *) _write_out(@loca0, 4); - loca4 := 0x34202c35 (* 5, 4 *); + loca4 := 0x34202c35; _write_out(@loca4, 4); - loca4 := 0x70732830 (* 0(sp *); + loca4 := 0x70732830; _write_out(@loca4, 4); - loca4 := 0x0a29 (* )\n *); + loca4 := 0x0a29; _write_out(@loca4, 2); - loca0 := 0x6c6c6163 (* call *); + loca0 := 0x6c6c6163; _write_out(@loca0, 4); - _put_char(0x20) (* _ *); + _put_char(0x20); _write_out(loca84, loca80); - _put_char(0x0a) (* \n *); + _put_char(0x0a); _skip_spaces(); - _advance(1) (* Skip the close paren. *) + _advance(1) end (* @@ -565,85 +524,69 @@ var loca8: ^Byte begin loca8 := _current(); - loca0 := _front(loca8) (* t0 = Current character. *); + loca0 := _front(loca8); loca4 := 0; - (* . *) if loca0 = 0x2e then goto .Ltoken_character_single end; - (* , *) if loca0 = 0x2c then goto .Ltoken_character_single end; - (* : *) if loca0 = 0x3a then goto .Ltoken_character_colon end; - (* ; *) if loca0 = 0x3b then goto .Ltoken_character_single end; - (* ( *) if loca0 = 0x28 then goto .Ltoken_character_single end; - (* ) *) if loca0 = 0x29 then goto .Ltoken_character_single end; - (* [ *) if loca0 = 0x5b then goto .Ltoken_character_single end; - (* ] *) if loca0 = 0x5d then goto .Ltoken_character_single end; - (* ^ *) if loca0 = 0x5e then goto .Ltoken_character_single end; - (* & *) if loca0 = 0x26 then goto .Ltoken_character_single end; - (* = *) if loca0 = 0x3d then goto .Ltoken_character_single end; - (* + *) if loca0 = 0x2b then goto .Ltoken_character_single end; - (* - *) if loca0 = 0x2d then goto .Ltoken_character_single end; - (* * *) if loca0 = 0x2a then goto .Ltoken_character_single end; - (* @ *) if loca0 = 0x40 then goto .Ltoken_character_single end; - (* Expect an identifier or a number. *) .Ltoken_character_loop_do; loca0 := loca8 + loca4; loca0 := _front(loca0); @@ -658,12 +601,11 @@ begin .Ltoken_character_colon; loca0 := loca8 + 1; - loca0 := _front(loca0) (* t0 = The character after the colon. *); + loca0 := _front(loca0); loca4 := loca4 + 1; - (* = *) - if loca0 = 0x3d then - goto .Ltoken_character_single + if loca0 = 0x3d then + goto .Ltoken_character_single end end; .Ltoken_character_end; @@ -678,21 +620,17 @@ var begin .Lspace_loop_do; loca4 := _current(); - loca0 := _front(loca4) (* t0 = Current character. *); + loca0 := _front(loca4); - (* _ *) if loca0 = 0x20 then goto .Lspace_loop_repeat end; - (* \t *) if loca0 = 0x09 then goto .Lspace_loop_repeat end; - (* \n *) if loca0 = 0x0a then goto .Lspace_loop_repeat end; - (* \r *) if loca0 = 0x0d then goto .Lspace_loop_repeat end; @@ -717,8 +655,7 @@ var begin loca0 := _current(); - (* Check whether this is a comment. *) - loca4 := 0x2a28 (* ( and * *); + loca4 := 0x2a28; loca8 := _memcmp(loca0, @loca4, 2); if loca8 = 0 then goto .Lskip_comment_continue @@ -726,9 +663,9 @@ begin goto .Lskip_comment_end; .Lskip_comment_continue; - _advance(2) (* Skip (*. *); + _advance(2); - loca4 := 0x292a (* ( and * *); + loca4 := 0x292a; .Lskip_comment_loop; loca0 := _current(); @@ -742,7 +679,7 @@ begin goto .Lskip_comment_loop; .Lskip_comment_close; - _advance(2) (* Skip "*" and ")". *); + _advance(2); .Lskip_comment_end end @@ -754,31 +691,29 @@ Parameters: proc _compile_assembly(loca84: Word) var loca0: ^Byte begin - (* Write the source to the standard output. *) loca0 := _current(); _write_out(loca0, loca84); _advance(loca84); - _put_char(0xa) (* \n *); + _put_char(0xa); - _advance(1) (* Skip the new line. *) + _advance(1) end proc _compile_program() var loca0: Word begin - (* .global _start *) - loca0 := 0x6f6c672e (* .glo *); + loca0 := 0x6f6c672e; _write_out(@loca0, 4); - loca0 := 0x206c6162 (* bal_ *); + loca0 := 0x206c6162; _write_out(@loca0, 4); - loca0 := 0x6174735f (* _sta *); + loca0 := 0x6174735f; _write_out(@loca0, 4); - loca0 := 0x0a7472 (* rt\n *); + loca0 := 0x0a7472; _write_out(@loca0, 3); - _advance(8) (* program\n. *) + _advance(8) end proc _compile_constant_section() @@ -786,19 +721,18 @@ var loca0: Word loca4: ^Byte begin - (* .section .rodata *) - loca0 := 0x6365732e (* .sec *); + loca0 := 0x6365732e; _write_out(@loca0, 4); - loca0 := 0x6e6f6974 (* tion *); + loca0 := 0x6e6f6974; _write_out(@loca0, 4); - loca0 := 0x6f722e20 (* _.ro *); + loca0 := 0x6f722e20; _write_out(@loca0, 4); - loca0 := 0x61746164 (* data *); + loca0 := 0x61746164; _write_out(@loca0, 4); - loca0 := 0x0a (* \n *); + loca0 := 0x0a; _write_out(@loca0, 1); - _advance(6) (* const\n. *); + _advance(6); .Lcompile_constant_section_item; _skip_spaces(); @@ -818,28 +752,27 @@ var loca8: ^Byte begin loca4 := _read_token(); - loca8 := _current() (* Save the identifier pointer before advancing it. *); + loca8 := _current(); _write_out(loca8, loca4); _advance(loca4); _skip_spaces(); - _advance(2) (* Skip the assignment sign. *); + _advance(2); - (* : .long *) - loca0 := 0x6c2e203a (* : .l *); + loca0 := 0x6c2e203a; _write_out(@loca0, 4); - loca0 := 0x20676e6f (* ong_ *); + loca0 := 0x20676e6f; _write_out(@loca0, 4); _skip_spaces(); loca4 := _read_token(); - loca8 := _current() (* Save the literal pointer before advancing it. *); + loca8 := _current(); _write_out(loca8, loca4); _advance(loca4); - _put_char(0x0a) (* \n *) + _put_char(0x0a) end proc _compile_variable_section() @@ -847,17 +780,16 @@ var loca0: Word loca4: ^Byte begin - (* .section .bss *) - loca0 := 0x6365732e (* .sec *); + loca0 := 0x6365732e; _write_out(@loca0, 4); - loca0 := 0x6e6f6974 (* tion *); + loca0 := 0x6e6f6974; _write_out(@loca0, 4); - loca0 := 0x73622e20 (* _.bs *); + loca0 := 0x73622e20; _write_out(@loca0, 4); - loca0 := 0x0a73 (* s\n *); + loca0 := 0x0a73; _write_out(@loca0, 2); - _advance(4) (* var\n. *); + _advance(4); .Lcompile_variable_section_item; _skip_spaces(); @@ -878,70 +810,66 @@ var loca28, loca16: ^Byte loca0, loca24, loca20: Word begin - (* Save the identifier on the stack since it should emitted multiple times. *) loca24 := _read_token(); loca28 := _current(); _advance(loca24); _skip_spaces(); - _advance(1) (* Skip the colon in front of the type. *); + _advance(1); _skip_spaces(); - _advance(1) (* Skip the opening bracket. *); + _advance(1); - (* Save the array size on the stack since it has to be emitted multiple times. *) loca16 := _read_token(); loca20 := _current(); _advance(loca16); _skip_spaces(); - _advance(1) (* Skip the closing bracket. *); + _advance(1); _skip_spaces(); loca0 := _read_token(); - _advance(loca0) (* Skip the type. *); + _advance(loca0); - (* .type identifier, @object *) - loca0 := 0x7079742e (* .typ *); + loca0 := 0x7079742e; _write_out(@loca0, 4); - loca0 := 0x2065 (* e_ *); + loca0 := 0x2065; _write_out(@loca0, 2); _write_out(loca28, loca24); - loca0 := 0x6f40202c (* , @o *); + loca0 := 0x6f40202c; _write_out(@loca0, 4); - loca0 := 0x63656a62 (* bjec *); + loca0 := 0x63656a62; _write_out(@loca0, 4); - loca0 := 0x0a74 (* t\n *); + loca0 := 0x0a74; _write_out(@loca0, 2); (* .size identifier, size *); - loca0 := 0x7a69732e (* .siz *); + loca0 := 0x7a69732e; _write_out(@loca0, 4); - loca0 := 0x2065 (* e_ *); + loca0 := 0x2065; _write_out(@loca0, 2); _write_out(loca28, loca24); - loca0 := 0x202c (* ,_ *); + loca0 := 0x202c; _write_out(@loca0, 2); _write_out(loca20, loca16); - _put_char(0x0a) (* \n *); + _put_char(0x0a); - (* identifier: .zero size *) _write_out(loca28, loca24); - loca0 := 0x7a2e203a (* : .z *); + loca0 := 0x7a2e203a; _write_out(@loca0, 4); - loca0 := 0x206f7265 (* ero_ *); + loca0 := 0x206f7265; _write_out(@loca0, 4); _write_out(loca20, loca16); - _put_char(0x0a) (* \n *) + _put_char(0x0a) end proc _compile_procedure() @@ -949,38 +877,38 @@ var loca0, loca4, loca8, loca12, loca16: Word loca20, loca24: ^Byte begin - _advance(5) (* Skip proc_ *); + _advance(5); loca16 := _read_token(); loca20 := _current(); _advance(loca16); (* .type identifier, @function *); - loca0 := 0x7079742e (* .typ *); + loca0 := 0x7079742e; _write_out(@loca0, 4); - loca0 := 0x2065 (* e_ *); + loca0 := 0x2065; _write_out(@loca0, 2); _write_out(loca20, loca16); - loca0 := 0x6640202c (* , @f *); + loca0 := 0x6640202c; _write_out(@loca0, 4); - loca0 := 0x74636e75 (* unct *); + loca0 := 0x74636e75; _write_out(@loca0, 4); - loca0 := 0x0a6e6f69 (* ion\n *); + loca0 := 0x0a6e6f69; _write_out(@loca0, 4); _write_out(loca20, loca16); - loca0 := 0x0a3a (* :\n *); + loca0 := 0x0a3a; _write_out(@loca0, 2); _skip_spaces(); - _advance(1) (* Skip opening argument paren. *); + _advance(1); _skip_spaces(); - _advance(1) (* Skip closing argument paren. *); + _advance(1); - loca12 := 0x6e (* n *); - loca8 := 0x69676562 (* begi *); + loca12 := 0x6e; + loca8 := 0x69676562; (* Skip all declarations until we find the "begin" keyword, denoting the @@ -998,94 +926,91 @@ begin goto .Lcompile_procedure_begin end; - (* Generate the procedure prologue with a predefined stack size. *) - loca0 := 0x69646461 (* addi *); + loca0 := 0x69646461; _write_out(@loca0, 4); - loca0 := 0x2c707320 (* _sp, *); + loca0 := 0x2c707320; _write_out(@loca0, 4); _write_out(@loca0, 4); - loca0 := 0x0a36392d (* -96\n *); + loca0 := 0x0a36392d; _write_out(@loca0, 4); - loca0 := 0x72207773 (* sw r *); + loca0 := 0x72207773; _write_out(@loca0, 4); - loca0 := 0x39202c61 (* a, 9 *); + loca0 := 0x39202c61; _write_out(@loca0, 4); - loca0 := 0x70732832 (* 2(sp *); + loca0 := 0x70732832; _write_out(@loca0, 4); - loca0 := 0x0a29 (* )\n *); + loca0 := 0x0a29; _write_out(@loca0, 2); - loca0 := 0x73207773 (* sw s *); + loca0 := 0x73207773; _write_out(@loca0, 4); - loca0 := 0x38202c30 (* 0, 8 *); + loca0 := 0x38202c30; _write_out(@loca0, 4); - loca0 := 0x70732838 (* 8(sp *); + loca0 := 0x70732838; _write_out(@loca0, 4); - loca0 := 0x0a29 (* )\n *); + loca0 := 0x0a29; _write_out(@loca0, 2); - loca0 := 0x69646461 (* addi *); + loca0 := 0x69646461; _write_out(@loca0, 4); - loca0 := 0x2c307320 (* _s0, *); + loca0 := 0x2c307320; _write_out(@loca0, 4); - loca0 := 0x2c707320 (* _sp, *); + loca0 := 0x2c707320; _write_out(@loca0, 4); - loca0 := 0x0a363920 (* _96\n *); + loca0 := 0x0a363920; _write_out(@loca0, 4); - (* Save passed arguments on the stack. *) - loca0 := 0x61207773 (* sw a *); + loca0 := 0x61207773; _write_out(@loca0, 4); - loca4 := 0x38202c30 (* 0, 8 *); + loca4 := 0x38202c30; _write_out(@loca4, 4); - loca8 := 0x70732834 (* 4(sp *); + loca8 := 0x70732834; _write_out(@loca8, 4); - loca12 := 0x0a29 (* )\n *); + loca12 := 0x0a29; _write_out(@loca12, 2); _write_out(@loca0, 4); - loca4 := 0x38202c31 (* 1, 8 *); + loca4 := 0x38202c31; _write_out(@loca4, 4); - loca8 := 0x70732830 (* 0(sp *); + loca8 := 0x70732830; _write_out(@loca8, 4); _write_out(@loca12, 2); _write_out(@loca0, 4); - loca4 := 0x37202c32 (* 2, 7 *); + loca4 := 0x37202c32; _write_out(@loca4, 4); - loca8 := 0x70732836 (* 6(sp *); + loca8 := 0x70732836; _write_out(@loca8, 4); _write_out(@loca12, 2); _write_out(@loca0, 4); - loca4 := 0x37202c33 (* 3, 7 *); + loca4 := 0x37202c33; _write_out(@loca4, 4); - loca8 := 0x70732832 (* 2(sp *); + loca8 := 0x70732832; _write_out(@loca8, 4); _write_out(@loca12, 2); _write_out(@loca0, 4); - loca4 := 0x36202c34 (* 4, 6 *); + loca4 := 0x36202c34; _write_out(@loca4, 4); - loca8 := 0x70732838 (* 8(sp *); + loca8 := 0x70732838; _write_out(@loca8, 4); _write_out(@loca12, 2); _write_out(@loca0, 4); - loca4 := 0x36202c35 (* 5, 6 *); + loca4 := 0x36202c35; _write_out(@loca4, 4); - loca8 := 0x70732838 (* 4(sp *); + loca8 := 0x70732838; _write_out(@loca8, 4); _write_out(@loca12, 2); - (* Generate the body of the procedure. *) .Lcompile_procedure_body; _skip_spaces(); loca12 := _read_line(); - loca8 := 0x0a646e65 (* end\n *); + loca8 := 0x0a646e65; loca24 := _current(); loca8 := _memcmp(loca24, @loca8, 4); @@ -1097,38 +1022,37 @@ begin goto .Lcompile_procedure_body; .Lcompile_procedure_end; - _advance(4) (* Skip end\n. *); + _advance(4); - (* Generate the procedure epilogue with a predefined stack size. *) - loca0 := 0x7220776c (* lw r *); + loca0 := 0x7220776c; _write_out(@loca0, 4); - loca0 := 0x39202c61 (* a, 9 *); + loca0 := 0x39202c61; _write_out(@loca0, 4); - loca0 := 0x70732832 (* 2(sp *); + loca0 := 0x70732832; _write_out(@loca0, 4); - loca0 := 0x0a29 (* )\n *); + loca0 := 0x0a29; _write_out(@loca0, 2); - loca0 := 0x7320776c (* lw s *); + loca0 := 0x7320776c; _write_out(@loca0, 4); - loca0 := 0x38202c30 (* 0, 8 *); + loca0 := 0x38202c30; _write_out(@loca0, 4); - loca0 := 0x70732838 (* 8(sp *); + loca0 := 0x70732838; _write_out(@loca0, 4); - loca0 := 0x0a29 (* )\n *); + loca0 := 0x0a29; _write_out(@loca0, 2); - loca0 := 0x69646461 (* addi *); + loca0 := 0x69646461; _write_out(@loca0, 4); - loca0 := 0x2c707320 (* _sp, *); + loca0 := 0x2c707320; _write_out(@loca0, 4); _write_out(@loca0, 4); - loca0 := 0x0a3639 (* 96\n *); + loca0 := 0x0a3639; _write_out(@loca0, 4); - loca0 := 0x0a746572 (* ret\n *); + loca0 := 0x0a746572; _write_out(@loca0, 4) end @@ -1193,22 +1117,22 @@ var loca0: Word loca8: ^Byte begin - _advance(4) (* Skip the goto keyword. *); + _advance(4); - loca0 := 0x206a (* j_ *); + loca0 := 0x206a; _write_out(@loca0, 2); _skip_spaces(); - loca8 := _current() (* We should be on dot the label is beginning with. *); + loca8 := _current(); _advance(1); loca0 := _read_token(); _advance(loca0); - loca0 := loca0 + 1 (* Label length and the dot. *); + loca0 := loca0 + 1; _write_out(loca8, loca0); - _advance(1) (* Skip the new line. *); - _put_char(0x0a) (* \n *) + _advance(1); + _put_char(0x0a) end (* a0 - Line length. *) @@ -1216,11 +1140,10 @@ proc _compile_label(loca84: Word) var loca0: Word begin - (* Write the whole line as is. *) loca0 := _current(); loca0 := loca0 + loca84; - loca0 := loca0 - 1 (* Last character on the line. *); + loca0 := loca0 - 1; loca4 := loca84; loca0 := _front(loca0); @@ -1229,15 +1152,15 @@ begin end; _write_out(s1, loca4); - _put_char(0x3a) (* : *); - _put_char(0x0a) (* \n *); + _put_char(0x3a); + _put_char(0x0a); _advance(loca84) end proc _compile_return() begin - _advance(6) (* Skip return. *); + _advance(6); _skip_spaces(); _build_binary_expression(); end @@ -1247,28 +1170,28 @@ var loca8, loca12, loca16, loca20: Word loca4: ^Byte begin - _advance(2) (* Skip the if. *); + _advance(2); _skip_spaces(); _build_binary_expression(); _skip_spaces(); - _advance(4) (* Skip the then. *); + _advance(4); - loca20 := 0x00646e65 (* if end marker and newline. *); - loca16 := 0x66694c2e (* Label prefix ".Lif". *); + loca20 := 0x00646e65; + loca16 := 0x66694c2e; - loca12 := 0x7a716562 (* beqz *); + loca12 := 0x7a716562; _write_out(@loca12, 4); - loca12 := 0x2c306120 (* _a0, *); + loca12 := 0x2c306120; _write_out(@loca12, 4); - _put_char(0x20) (* _ *); + _put_char(0x20); (* Write the label *); _write_out(@loca16, 4); _printi(s2); - _put_char(0x0a) (* \n *); + _put_char(0x0a); .Lcompile_if_loop; _skip_spaces(); @@ -1284,16 +1207,15 @@ begin goto .Lcompile_if_loop end; - (* Write the label *) _write_out(@loca16, 4); _printi(s2); - loca12 := 0x0a3a0a3a (* :\n:\n *); + loca12 := 0x0a3a0a3a; _write_out(@loca12, 2); (* Increment the label counter. *); s2 := s2 + 1; - _advance(4) (* Skip the end with newline. *) + _advance(4) end (* @@ -1314,54 +1236,53 @@ var loca16: ^Byte begin if loca84 = 0 then - goto .Lcompile_line_empty (* Skip an empty line. *) + goto .Lcompile_line_empty end; loca16 := _current(); loca0 := _front(loca16); - (* ( *) if loca0 = 0x28 then goto .Lcompile_line_comment end; loca16 := _current(); - loca12 := 0x676f7270 (* prog *); + loca12 := 0x676f7270; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_program end; - loca12 := 0x736e6f63 (* cons *); + loca12 := 0x736e6f63; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_const end; - loca12 := 0x0a726176 (* var\n *); + loca12 := 0x0a726176; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_var end; - loca12 := 0x636f7270 (* proc *); + loca12 := 0x636f7270; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_procedure end; - loca12 := 0x69676562 (* begi *); + loca12 := 0x69676562; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_begin end; - loca12 := 0x2e646e65 (* end. *); + loca12 := 0x2e646e65; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_exit end; - loca12 := 0x61636f6c (* loca *); + loca12 := 0x61636f6c; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_identifier @@ -1371,39 +1292,37 @@ begin goto .Lcompile_line_identifier end; - loca12 := 0x6f706d69 (* impo *); + loca12 := 0x6f706d69; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_import end; - loca12 := 0x6f746f67 (* goto *); + loca12 := 0x6f746f67; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_goto end; - loca12 := 0x75746572 (* retu *); + loca12 := 0x75746572; loca4 := _memcmp(loca16, @loca12, 4); if loca4 = 0 then goto .Lcompile_line_return end; - loca12 := 0x6669 (* if *); + loca12 := 0x6669; loca4 := _memcmp(loca16, @loca12, 2); if loca4 = 0 then goto .Lcompile_line_if end; - (* . *) if loca0 = 0x2e then goto .Lcompile_line_label end; - (* _ *) if loca0 = 0x5f then goto .Lcompile_line_identifier end; - goto .Lcompile_line_unchanged (* Else. *); + goto .Lcompile_line_unchanged; .Lcompile_line_if; _compile_if(); @@ -1492,64 +1411,59 @@ end proc _compile_text_section() var loca0: Word begin - (* .section .text *) - loca0 := 0x6365732e (* .sec *); + loca0 := 0x6365732e; _write_out(@loca0, 4); - loca0 := 0x6e6f6974 (* tion *); + loca0 := 0x6e6f6974; _write_out(@loca0, 4); - loca0 := 0x65742e20 (* _.te *); + loca0 := 0x65742e20; _write_out(@loca0, 4); - loca0 := 0x0a7478 (* xt\n *); + loca0 := 0x0a7478; _write_out(@loca0, 3) end proc _compile_entry_point() var loca0: Word begin - (* .type _start, @function *) - loca0 := 0x7079742e (* .typ *); + loca0 := 0x7079742e; _write_out(@loca0, 4); - loca0 := 0x735f2065 (* e _s *); + loca0 := 0x735f2065; _write_out(@loca0, 4); - loca0 := 0x74726174 (* tart *); + loca0 := 0x74726174; _write_out(@loca0, 4); - loca0 := 0x6640202c (* , @f *); + loca0 := 0x6640202c; _write_out(@loca0, 4); - loca0 := 0x74636e75 (* unct *); + loca0 := 0x74636e75; _write_out(@loca0, 4); - loca0 := 0x0a6e6f69 (* ion\n *); + loca0 := 0x0a6e6f69; _write_out(@loca0, 4); - loca0 := 0x6174735f (* _sta *); + loca0 := 0x6174735f; _write_out(@loca0, 4); - loca0 := 0x0a3a7472 (* rt:\n *); + loca0 := 0x0a3a7472; _write_out(@loca0, 4); - _advance(6) (* Skip begin\n. *) + _advance(6) end proc _compile_exit() var loca0: Word begin - (* li a0, 0 *) - (* li a7, SYS_EXIT *) - (* ecall *) - loca0 := 0x6120696c (* li a *); + loca0 := 0x6120696c; _write_out(@loca0, 4); - loca0 := 0x30202c30 (* 0, 0 *); + loca0 := 0x30202c30; _write_out(@loca0, 4); - loca0 := 0x20696c0a (* \nli_ *); + loca0 := 0x20696c0a; _write_out(@loca0, 4); - loca0 := 0x202c3761 (* a7,_ *); + loca0 := 0x202c3761; _write_out(@loca0, 4); - loca0 := 0x650a3339 (* 93\ne *); + loca0 := 0x650a3339; _write_out(@loca0, 4); - loca0 := 0x6c6c6163 (* call *); + loca0 := 0x6c6c6163; _write_out(@loca0, 4); - loca0 := 0x0a (* \n *); + loca0 := 0x0a; _write_out(@loca0, 1); - _advance(4) (* Skip end. *); - _skip_spaces() (* Read the possible new line at the end of the file. *) + _advance(4); + _skip_spaces() end (* Finds the end of the line and returns its length in a0. *) @@ -1558,22 +1472,22 @@ var loca0: ^Byte loca4: Byte begin - loca0 := _current() (* Local position in the source text. *); + loca0 := _current(); .Lread_line_do; - loca4 := _front(loca0) (* t1 = Current character. *); + loca4 := _front(loca0); if loca4 = 0 then - goto .Lread_line_end (* Exit the loop on the NUL character. *) + goto .Lread_line_end end; if loca4 = 0x0a then - goto .Lread_line_end (* Exit the loop on the new line. *) + goto .Lread_line_end end; loca0 := loca0 + 1; goto .Lread_line_do; .Lread_line_end; loca4 := _current(); - return loca0 - loca4 (* Return the line length. *) + return loca0 - loca4 end proc _compile() @@ -1583,14 +1497,14 @@ var loca12: Char loca16: ^Byte begin - loca4 := 0 (* Whether the text section header was already emitted. *); + loca4 := 0; .Lcompile_do; loca16 := _current(); - loca12 := _front(loca16) (* t0 = Current character. *); + loca12 := _front(loca16); if loca12 = 0 then - goto .Lcompile_end (* Exit the loop on the NUL character. *) + goto .Lcompile_end end; _skip_spaces(); @@ -1600,7 +1514,6 @@ begin if loca8 = 0 then goto .Lcompile_do end; - (* Update whether the text section header was already emitted. *) loca4 := loca4 or loca8; goto .Lcompile_do; @@ -1631,7 +1544,6 @@ end proc _main() begin - (* Read the source from the standard input. *) _read_file(source_code, SOURCE_BUFFER_SIZE); s2 := 1 diff --git a/boot/tokenizer.s b/boot/tokenizer.s index e358b89..bf8e443 100644 --- a/boot/tokenizer.s +++ b/boot/tokenizer.s @@ -266,9 +266,9 @@ transitions: .word 0x05ff, 0x0102, 0x05ff, 0x0102, 0x0102, 0x0102, 0x05ff, 0x05ff .word 0x05ff, 0x05ff, 0x05ff, 0x05ff # 0x02 Identifier - .word 0x02ff, 0x0103, 0x00ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff - .word 0x02ff, 0x02ff, 0x02ff, 0x00ff, 0x0103, 0x02ff, 0x02ff, 0x02ff - .word 0x02ff, 0x02ff, 0x02ff, 0x02ff # 0x03 Integer + .word 0x08ff, 0x0103, 0x00ff, 0x08ff, 0x08ff, 0x08ff, 0x08ff, 0x08ff + .word 0x08ff, 0x00ff, 0x08ff, 0x00ff, 0x0103, 0x00ff, 0x08ff, 0x08ff + .word 0x08ff, 0x08ff, 0x08ff, 0x08ff # 0x03 Integer .word 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x04ff, 0x02ff, 0x02ff .word 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff @@ -518,6 +518,9 @@ _tokenize_next: li t0, 0x07 # An action for symbols containing multiple characters. beq t1, t0, .Ltokenize_next_composite + li t0, 0x08 # Integer action. + beq t1, t0, .Ltokenize_next_integer + j .Ltokenize_next_reject .Ltokenize_next_reject: @@ -588,6 +591,17 @@ _tokenize_next: j .Ltokenize_next_end +.Ltokenize_next_integer: + lw a1, 12(sp) + sub a0, s1, a1 + sw a0, 8(sp) + sw a0, 4(sp) + lw a0, 0(sp) + addi a1, sp, 4 + li a2, 12 + call _memcpy + j .Ltokenize_next_end + .Ltokenize_next_end: mv a0, s1 # Return the advanced text pointer. diff --git a/rakelib/cross.rake b/rakelib/cross.rake new file mode 100644 index 0000000..f90bb84 --- /dev/null +++ b/rakelib/cross.rake @@ -0,0 +1,335 @@ +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at https://mozilla.org/MPL/2.0/. -} +# frozen_string_literal: true + +require 'pathname' +require 'uri' +require 'net/http' +require 'rake/clean' +require 'open3' +require 'etc' + +GCC_VERSION = "15.1.0" +BINUTILS_VERSION = '2.44' +GLIBC_VERSION = '2.41' +KERNEL_VERSION = '5.15.181' + +CLOBBER.include 'build' + +class BuildTarget + attr_accessor(:build, :gcc, :target, :tmp) + + def gxx + @gcc.gsub 'c', '+' + end + + def sysroot + tmp + 'sysroot' + end + + def rootfs + tmp + 'rootfs' + end + + def tools + tmp + 'tools' + end +end + +def gcc_verbose(gcc_binary) + read, write = IO.pipe + sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write) + write.close + output = read.read + read.close + output +end + +def find_build_target(gcc_version, task) + gcc_binary = 'gcc' + output = gcc_verbose gcc_binary + + if output.start_with? 'Apple clang' + gcc_binary = "gcc-#{gcc_version.split('.').first}" + output = gcc_verbose gcc_binary + end + result = output + .lines + .each_with_object(BuildTarget.new) do |line, accumulator| + if line.start_with? 'Target: ' + accumulator.build = line.split(' ').last.strip + elsif line.start_with? 'COLLECT_GCC' + accumulator.gcc = line.split('=').last.strip + end + end + result.tmp = Pathname.new('./build') + task.with_defaults target: 'riscv32-unknown-linux-gnu' + result.target = task[:target] + result +end + +def download_and_unarchive(url, target) + case File.extname url.path + when '.bz2' + archive_type = '-j' + root_directory = File.basename url.path, '.tar.bz2' + when '.xz' + archive_type = '-J' + root_directory = File.basename url.path, '.tar.xz' + else + raise "Unsupported archive type #{url.path}." + end + + Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http| + request = Net::HTTP::Get.new url.request_uri + + http.request request do |response| + case response + when Net::HTTPRedirection + download_and_unarchive URI.parse(response['location']) + when Net::HTTPSuccess + Open3.popen2 'tar', '-C', target.to_path, archive_type, '-xv' do |stdin, stdout, wait_thread| + Thread.new do + stdout.each { |line| puts line } + end + + response.read_body do |chunk| + stdin.write chunk + end + stdin.close + + wait_thread.value + end + else + response.error! + end + end + end + target + root_directory +end + +namespace :cross do + desc 'Build cross binutils' + task :binutils, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + source_directory = download_and_unarchive( + URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"), + options.tools) + + cwd = source_directory.dirname + 'build-binutils' + cwd.mkpath + options.rootfs.mkpath + + env = { + 'CC' => options.gcc, + 'CXX' => options.gxx + } + configure_options = [ + "--prefix=#{options.rootfs.realpath}", + "--target=#{options.target}", + '--disable-nls', + '--enable-gprofng=no', + '--disable-werror', + '--enable-default-hash-style=gnu', + '--disable-libquadmath' + ] + configure = source_directory.relative_path_from(cwd) + 'configure' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'install', chdir: cwd.to_path + end + + desc 'Build stage 1 GCC' + task :gcc1, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + source_directory = download_and_unarchive( + URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"), + options.tools) + + cwd = source_directory.dirname + 'build-gcc' + cwd.mkpath + options.rootfs.mkpath + options.sysroot.mkpath + + sh 'contrib/download_prerequisites', chdir: source_directory.to_path + configure_options = [ + "--prefix=#{options.rootfs.realpath}", + "--with-sysroot=#{options.sysroot.realpath}", + '--enable-languages=c,c++', + '--disable-shared', + '--with-arch=rv32imafdc', + '--with-abi=ilp32d', + '--with-tune=rocket', + '--with-isa-spec=20191213', + '--disable-bootstrap', + '--disable-multilib', + '--disable-libmudflap', + '--disable-libssp', + '--disable-libquadmath', + '--disable-libsanitizer', + '--disable-threads', + '--disable-libatomic', + '--disable-libgomp', + '--disable-libvtv', + '--disable-libstdcxx', + '--disable-nls', + '--with-newlib', + '--without-headers', + "--target=#{options.target}", + "--build=#{options.build}", + "--host=#{options.build}" + ] + flags = '-O2 -fPIC' + env = { + 'CC' => options.gcc, + 'CXX' => options.gxx, + 'CFLAGS' => flags, + 'CXXFLAGS' => flags, + 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}" + } + configure = source_directory.relative_path_from(cwd) + 'configure' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'install', chdir: cwd.to_path + end + + desc 'Copy glibc headers' + task :headers, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + + source_directory = download_and_unarchive( + URI.parse("https://ftp.gnu.org/gnu/glibc/glibc-#{GLIBC_VERSION}.tar.xz"), + options.tools) + include_directory = options.tools + 'include' + + include_directory.mkpath + cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h') + end + + desc 'Build linux kernel' + task :kernel, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + options.tools.mkpath + + cwd = download_and_unarchive( + URI.parse("https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-#{KERNEL_VERSION}.tar.xz"), + options.tools) + + env = { + 'CROSS_COMPILE' => "#{options.target}-", + 'ARCH' => 'riscv', + 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}", + 'HOSTCFLAGS' => "-D_UUID_T -D__GETHOSTUUID_H -I#{options.tools.realpath + 'include'}" + } + sh env, 'make', 'rv32_defconfig', chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'headers', chdir: cwd.to_path + + user_directory = options.sysroot + 'usr' + + user_directory.mkpath + cp_r (cwd + 'usr/include'), (user_directory + 'include') + end + + desc 'Build glibc' + task :glibc, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + source_directory = options.tools + "glibc-#{GLIBC_VERSION}" + configure_options = [ + '--prefix=/usr', + "--host=#{options.target}", + "--target=#{options.target}", + "--build=#{options.build}", + "--enable-kernel=#{KERNEL_VERSION}", + "--with-headers=#{options.sysroot.realpath + 'usr/include'}", + '--disable-nscd', + '--disable-libquadmath', + '--disable-libitm', + '--disable-werror', + 'libc_cv_forced_unwind=yes' + ] + bin = options.rootfs.realpath + 'bin' + env = { + 'PATH' => "#{bin}:#{ENV['PATH']}", + 'MAKE' => 'make' # Otherwise it uses gnumake which can be different and too old. + } + cwd = source_directory.dirname + 'build-glibc' + cwd.mkpath + + configure = source_directory.relative_path_from(cwd) +'./configure' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path + end + + desc 'Build stage 2 GCC' + task :gcc2, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + source_directory = options.tools + "gcc-#{GCC_VERSION}" + cwd = options.tools + 'build-gcc' + + rm_rf cwd + cwd.mkpath + + configure_options = [ + "--prefix=#{options.rootfs.realpath}", + "--with-sysroot=#{options.sysroot.realpath}", + '--enable-languages=c,c++,lto', + '--enable-lto', + '--enable-shared', + '--with-arch=rv32imafdc', + '--with-abi=ilp32d', + '--with-tune=rocket', + '--with-isa-spec=20191213', + '--disable-bootstrap', + '--disable-multilib', + '--enable-checking=release', + '--disable-libssp', + '--disable-libquadmath', + '--enable-threads=posix', + '--with-default-libstdcxx-abi=new', + '--disable-nls', + "--target=#{options.target}", + "--build=#{options.build}", + "--host=#{options.build}" + + ] + flags = '-O2 -fPIC' + env = { + 'CFLAGS' => flags, + 'CXXFLAGS' => flags, + 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}" + } + configure = source_directory.relative_path_from(cwd) + 'configure' + sh env, configure.to_path, *configure_options, chdir: cwd.to_path + sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path + sh env, 'make', 'install', chdir: cwd.to_path + end + + task :init, [:target] do |_, args| + options = find_build_target GCC_VERSION, args + env = { + 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}" + } + sh env, 'riscv32-unknown-linux-gnu-gcc', + '-ffreestanding', '-static', + '-o', (options.tools + 'init').to_path, + 'tools/init.c' + end +end + +desc 'Build cross toolchain' +task cross: [ + 'cross:binutils', + 'cross:gcc1', + 'cross:headers', + 'cross:kernel', + 'cross:glibc', + 'cross:gcc2', + 'cross:init' +] do +end diff --git a/rakelib/stage.rake b/rakelib/stage.rake new file mode 100644 index 0000000..80f704d --- /dev/null +++ b/rakelib/stage.rake @@ -0,0 +1,57 @@ +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at https://mozilla.org/MPL/2.0/. -} +# frozen_string_literal: true + +CROSS_GCC = 'build/rootfs/bin/riscv32-unknown-linux-gnu-gcc' +SYSROOT = 'build/sysroot' +QEMU = 'qemu-riscv32' + +def assemble_stage(output, compiler, source) + arguments = [QEMU, '-L', SYSROOT, *compiler] + + puts Term::ANSIColor.green(arguments * ' ') + puts + Open3.popen2(*arguments) do |qemu_in, qemu_out| + qemu_in.write File.read(*source) + qemu_in.close + + IO.copy_stream qemu_out, output + qemu_out.close + end +end + +Dir.glob('boot/*.s').each do |assembly_source| + target_object = Pathname.new('build/boot') + Pathname.new(assembly_source).basename.sub_ext('.o') + + file target_object.to_s => [assembly_source, 'build/boot'] do |t| + sh CROSS_GCC, '-c', '-o', t.name, assembly_source + end +end + +desc 'Initial stage' +file 'build/boot/stage1' => ['build/boot/tokenizer.o', 'build/boot/stage1.o', 'build/boot/common-boot.o'] do |t| + sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites +end + +file 'build/boot/stage2a.s' => ['build/boot/stage1', 'boot/stage2.elna'] do |t| + source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' } + + File.open t.name, 'w' do |output| + assemble_stage output, exe, source + end +end + +['build/boot/stage2a', 'build/boot/stage2b'].each do |exe| + file exe => [exe.ext('.s'), 'build/boot/common-boot.o'] do |t| + sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites + end +end + +file 'build/boot/stage2b.s' => ['build/boot/stage2a', 'boot/stage2.elna'] do |t| + source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' } + + File.open t.name, 'w' do |output| + assemble_stage output, exe, source + end +end diff --git a/tools/init.c b/tools/init.c new file mode 100644 index 0000000..f463bcd --- /dev/null +++ b/tools/init.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include +#include + +#define FILENAME_BUFFER_SIZE 256 + +size_t read_command(int descriptor, char *command_buffer) +{ + ssize_t bytes_read = 0; + size_t read_so_far = 0; + + while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0) + { + read_so_far += bytes_read; + if (read_so_far >= FILENAME_BUFFER_SIZE - 1) + { + break; + } + } + command_buffer[read_so_far] = 0; + return read_so_far; +} + +enum status +{ + status_success, + status_failure, + status_warning, + status_fatal +}; + +unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension) +{ + unsigned int i = 0; + + for (; i < FILENAME_BUFFER_SIZE; i++) + { + if (directory[i] == 0) + { + break; + } + destination[i] = directory[i]; + } + for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) + { + if (filename[j] == 0) + { + break; + } + destination[i] = filename[j]; + } + if (extension == NULL) + { + goto done; + } + for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) + { + if (extension[j] == 0) + { + break; + } + destination[i] = extension[j]; + } +done: + destination[i] = 0; + + return i; +} + +enum status run_test(const char *file_entry_name) +{ + printf("Running %s. ", file_entry_name); + + char filename[FILENAME_BUFFER_SIZE]; + char command_buffer[FILENAME_BUFFER_SIZE]; + char file_buffer[256]; + int pipe_ends[2]; + + if (pipe(pipe_ends) == -1) + { + perror("pipe"); + return status_fatal; + } + make_path(filename, "./tests/", file_entry_name, NULL); + + int child_pid = fork(); + if (child_pid == -1) + { + return status_fatal; + } + else if (child_pid == 0) + { + close(STDIN_FILENO); + close(STDERR_FILENO); + close(pipe_ends[0]); // Close the read end. + + if (dup2(pipe_ends[1], STDOUT_FILENO) == -1) + { + perror("dup2"); + } + else + { + execl(filename, filename); + perror("execl"); + } + close(STDOUT_FILENO); + close(pipe_ends[1]); + _exit(1); + } + else + { + close(pipe_ends[1]); // Close the write end. + read_command(pipe_ends[0], command_buffer); + close(pipe_ends[0]); + + int wait_status = 0; + + make_path(filename, "./expectations/", file_entry_name, ".txt"); + + FILE *expectation_descriptor = fopen(filename, "r"); + + if (expectation_descriptor == NULL) + { + return status_warning; + } + size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor); + fclose(expectation_descriptor); + + file_buffer[read_from_file] = 0; + for (unsigned int i = 0; ; ++i) + { + if (command_buffer[i] == 0 && file_buffer[i] == 0) + { + fwrite("\n", 1, 1, stdout); + return status_success; + } + else if (command_buffer[i] != file_buffer[i]) + { + printf("Failed. Got:\n%s", command_buffer); + return status_failure; + } + } + } +} + +struct summary +{ + size_t total; + size_t failure; + size_t success; +}; + +void walk() +{ + DIR *directory_stream = opendir("./tests"); + struct dirent *file_entry; + + struct summary test_summary = { .total = 0, .failure = 0, .success = 0 }; + + while ((file_entry = readdir(directory_stream)) != NULL) + { + if (file_entry->d_name[0] == '.') + { + continue; + } + ++test_summary.total; + switch (run_test(file_entry->d_name)) + { + case status_failure: + ++test_summary.failure; + break; + case status_success: + ++test_summary.success; + break; + case status_warning: + break; + case status_fatal: + goto end_walk; + } + } + printf("Successful: %lu, Failed: %lu, Total: %lu.\n", + test_summary.success, test_summary.failure, test_summary.total); +end_walk: + closedir(directory_stream); +} + +int main() +{ + int dev_console = open("/dev/console", O_WRONLY); + if (dev_console != -1) + { + dup2(dev_console, STDOUT_FILENO); + walk(); + close(dev_console); + } + sync(); + reboot(RB_POWER_OFF); + + return 1; +}