aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Rakefile59
-rw-r--r--boot/definitions.inc31
-rw-r--r--boot/stage1.s372
-rw-r--r--boot/stage2.elna604
-rw-r--r--boot/tokenizer.s20
-rw-r--r--rakelib/cross.rake335
-rw-r--r--rakelib/stage.rake57
-rw-r--r--tools/init.c204
8 files changed, 1042 insertions, 640 deletions
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
-
- 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
+ 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
- # 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
-
- 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
+ addi sp, sp, -32
+ sw ra, 28(sp)
+ sw s0, 24(sp)
+ addi s0, sp, 32
-.Lcompile_label_colon:
- # Write the whole line as is.
mv a0, s1
- call _write_out
-
- li t0, 0x3a # :
- sw t0, 4(sp)
- addi a0, sp, 4
- li a1, 1
- 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, '\n'
- 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
-
- 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 <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/reboot.h>
+
+#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;
+}