diff --git a/.gitignore b/.gitignore index 0ee919a..0f4ebfc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /build/ a.out +/vendor/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..235744c --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gem 'term-ansicolor', '~> 1.2' +gem 'rake', '~> 13.2' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..3521a74 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,22 @@ +GEM + remote: https://rubygems.org/ + specs: + bigdecimal (3.1.9) + rake (13.2.1) + sync (0.5.0) + term-ansicolor (1.11.2) + tins (~> 1.0) + tins (1.38.0) + bigdecimal + sync + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + rake (~> 13.2) + term-ansicolor (~> 1.2) + +BUNDLED WITH + 2.6.7 diff --git a/Rakefile b/Rakefile index e04bff1..3fa065e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,8 @@ +# frozen_string_literal: true + require 'open3' require 'rake/clean' +require 'term/ansicolor' CLOBBER.include 'build' @@ -10,7 +13,7 @@ QEMU = 'qemu-riscv32' def assemble_stage(output, compiler, source) arguments = [QEMU, '-L', SYSROOT, *compiler] - puts(arguments * ' ') + puts Term::ANSIColor.green(arguments * ' ') puts Open3.popen2(*arguments) do |qemu_in, qemu_out| qemu_in.write File.read(*source) @@ -27,7 +30,7 @@ task default: ['build/stage2b', 'build/stage2b.s', 'boot/stage2.elna'] do |t| cat_arguments = ['cat', source] compiler_arguments = [QEMU, '-L', SYSROOT, exe] - diff_arguments = ['diff', '-Nur', previous_output, '-'] + diff_arguments = ['diff', '-Nur', '--text', previous_output, '-'] Open3.pipeline(cat_arguments, compiler_arguments, diff_arguments) end @@ -70,7 +73,8 @@ task :statistics do word.start_with?('(*') || word.start_with?('*)') || ('A'..'Z').include?(word[0]) || - /^[[:alpha:]][[:digit:]]$/.match(word) + /^[[:alpha:]][[:digit:]]$/.match(word) || + ['end', 'if'].include?(word) end lines = File.read('boot/stage2.elna') diff --git a/boot/common-boot.s b/boot/common-boot.s index c6fcb98..e8eba52 100644 --- a/boot/common-boot.s +++ b/boot/common-boot.s @@ -1,5 +1,6 @@ .global _is_alpha, _is_digit, _is_alnum, _is_upper, _is_lower -.global _write_out, _read_file, exit, _memcmp, _write_error, _put_char +.global _write_out, _read_file, _memcmp, _write_error, _put_char, _printi +.global _divide_by_zero_error, _exit .section .rodata @@ -119,6 +120,13 @@ _is_alpha: addi sp, sp, 16 ret +# Detects whether the passed character is a digit +# (a value between 0 and 9). +# +# Parameters: +# a0 - Exemined value. +# +# Sets a0 to 1 if it is a digit, to 0 otherwise. .type _is_digit, @function _is_digit: li t0, '0' - 1 @@ -201,10 +209,68 @@ _read_file: ret # Terminates the program. a0 contains the return code. +# +# Parameters: +# a0 - Status code. .type _exit, @function -exit: +_exit: li a7, SYS_EXIT ecall + # ret + +.type _divide_by_zero_error, @function +_divide_by_zero_error: + addi a7, zero, 172 # getpid + ecall + + addi a1, zero, 8 # SIGFPE + addi a7, zero, 129 # kill + ecall + ret + +# a0 - Whole number. +# t1 - Constant 10. +# a1 - Local buffer. +# t2 - Current character. +# t3 - Whether the number is negative. +.type printi, @function +_printi: + addi sp, sp, -16 + sw s0, 0(sp) + sw ra, 4(sp) + addi s0, sp, 16 + addi t1, zero, 10 + addi a1, s0, -1 + + addi t3, zero, 0 + bge a0, zero, .digit10 + addi t3, zero, 1 + sub a0, zero, a0 + +.digit10: + rem t2, a0, t1 + addi t2, t2, '0' + sb t2, 0(a1) + div a0, a0, t1 + addi a1, a1, -1 + bne zero, a0, .digit10 + + beq zero, t3, .write_call + addi t2, zero, '-' + sb t2, 0(a1) + addi a1, a1, -1 + +.write_call: + addi a0, zero, 1 + addi a1, a1, 1 + sub a2, s0, a1 + addi a7, zero, 64 # write + ecall + + lw s0, 0(sp) + lw ra, 4(sp) + addi sp, sp, 16 + ret # Writes a character from a0 into the standard output. .type _put_char, @function diff --git a/boot/stage1.s b/boot/stage1.s index 5989ac9..61dcdec 100644 --- a/boot/stage1.s +++ b/boot/stage1.s @@ -1,5 +1,8 @@ .global _start # Program entry point. + +# Global variables or registers. # s1 - Contains the current position in the source text. +# s2 - Label counter. .equ SOURCE_BUFFER_SIZE, 81920 @@ -29,10 +32,17 @@ asm_add_a0_a1: .ascii "add a0, a0, a1\n" .equ ASM_ADD_A0_A1_SIZE, . - asm_add_a0_a1 asm_sub_a0_a1: .ascii "sub a0, a0, a1\n" .equ ASM_SUB_A0_A1_SIZE, . - asm_sub_a0_a1 +asm_mul_a0_a1: .ascii "mul a0, a0, a1\n" +.equ ASM_MUL_A0_A1_SIZE, . - asm_mul_a0_a1 asm_seqz_a0: .ascii "seqz a0, a0\n" .equ ASM_SEQZ_A0_SIZE, . - asm_seqz_a0 +asm_neg_a0: .ascii "neg a0, a0\n" +.equ ASM_NEG_A0_SIZE, . - asm_neg_a0 asm_type: .ascii ".type " .equ ASM_TYPE_SIZE, . - asm_type +asm_restore_parameters: + .ascii "lw a0, 60(sp)\nlw a1, 56(sp)\nlw a2, 52(sp)\nlw a3, 48(sp)\nlw a4, 44(sp)\nlw a5, 40(sp)\n" +.equ ASM_RESTORE_PARAMETERS_SIZE, . - asm_restore_parameters .section .bss .type source_code, @object @@ -116,6 +126,14 @@ _build_binary_expression: call _token_compare beqz a0, .L_build_binary_expression_minus + li t0, '*' + sw t0, 16(sp) + mv a0, s1 + lw a1, 20(sp) + addi a2, sp, 16 + call _token_compare + beqz a0, .L_build_binary_expression_product + j .Lbuild_binary_expression_end .L_build_binary_expression_equal: @@ -172,6 +190,16 @@ _build_binary_expression: j .Lbuild_binary_expression_end +.L_build_binary_expression_product: + addi s1, s1, 1 # Skip *. + li a0, 1 + call _build_expression + la a0, asm_mul_a0_a1 + li a1, ASM_MUL_A0_A1_SIZE + call _write_out + + j .Lbuild_binary_expression_end + .Lbuild_binary_expression_end: # Epilogue. lw ra, 28(sp) @@ -198,18 +226,19 @@ _build_expression: sw s1, 24(sp) sw a0, 20(sp) - addi a0, s1, 0 - lbu a0, (a0) + lbu a0, (s1) + li t0, '-' + beq a0, t0, .Lbuild_expression_negate + + lbu a0, (s1) li t0, '@' beq a0, t0, .Lbuild_expression_address - addi a0, s1, 0 - lbu a0, (a0) + lbu a0, (s1) call _is_digit bnez a0, .Lbuild_expression_literal - addi a0, s1, 0 - lbu a0, (a0) + lbu a0, (s1) li t0, '_' beq a0, t0, .Lbuild_expression_call @@ -241,6 +270,17 @@ _build_expression: j .Lbuild_expression_advance +.Lbuild_expression_negate: + addi s1, s1, 1 # Skip the -. + mv a0, zero + call _build_expression + + la a0, asm_neg_a0 + li a1, ASM_NEG_A0_SIZE + call _write_out + + j .Lbuild_expression_advance + .Lbuild_expression_address: lw t1, 28(sp) li t0, 0x20 # _ @@ -464,26 +504,10 @@ _compile_call: # Only 6 arguments are supported with a0-a5. # Save all arguments on the stack so they aren't overriden afterwards. - # The offset on the stack always has two digits in this case. - li t1, -4 - mul t1, t0, t1 - addi t1, t1, 60 - li t2, 10 - div t3, t1, t2 - rem t4, t1, t2 - addi t3, t3, '0' - addi t4, t4, '0' - - sw t3, 8(sp) - sw t4, 4(sp) - - addi a0, sp, 8 - li a1, 1 - call _write_out - - addi a0, sp, 4 - li a1, 1 - call _write_out + li a0, -4 + mul a0, t0, a0 + addi a0, a0, 60 + call _printi li t0, '\n' sw t0, 8(sp) @@ -511,52 +535,14 @@ _compile_call: .Lcompile_call_restore: # Just go through all a0-a5 registers and read them from stack. # If this stack value contains garbage, the procedure just shouldn't use it. - lw t0, 12(sp) - li t1, 5 - bgt t0, t1, .Lcompile_call_perform - - li t0, 0x6120776c # lw a - sw t0, 4(sp) - addi a0, sp, 4 - li a1, 4 + la a0, asm_restore_parameters + li a1, ASM_RESTORE_PARAMETERS_SIZE call _write_out - lw t0, 12(sp) # Argument count for a procedure call. - - li t1, -4 - mul t1, t0, t1 - addi t1, t1, 60 - li t2, 10 - div t3, t1, t2 - rem t4, t1, t2 - addi t3, t3, '0' - addi t4, t4, '0' - addi t0, t0, '0' - - li t5, '\n' - sb t5, 11(sp) - li t5, 0x29707328 # (sp) - sw t5, 7(sp) - sb t4, 6(sp) - sb t3, 5(sp) - li t5, 0x202c # ,_ - sh t5, 3(sp) - sb t0, 2(sp) - - addi a0, sp, 2 - li a1, 10 - call _write_out - - lw t0, 12(sp) # Increment. - addi t0, t0, 1 - sw t0, 12(sp) - - j .Lcompile_call_restore - .Lcompile_call_perform: li t0, 0x20 sw t0, 8(sp) - li t0, 0x6c6c6163 + li t0, 0x6c6c6163 # call sw t0, 4(sp) addi a0, sp, 4 li a1, 5 @@ -566,11 +552,8 @@ _compile_call: lw a1, 16(sp) call _write_out - li t0, '\n' - sw t0, 8(sp) - addi a0, sp, 8 - li a1, 1 - call _write_out + li a0, '\n' + call _put_char call _skip_spaces addi s1, s1, 1 # Skip the close paren. @@ -633,6 +616,9 @@ _read_token: li t1, '-' beq t0, t1, .Ltoken_character_single + li t1, '*' + beq t0, t1, .Ltoken_character_single + li t1, '@' beq t0, t1, .Ltoken_character_single # Expect an identifier or a number. @@ -1325,6 +1311,97 @@ _compile_return: addi sp, sp, 16 ret +.type _compile_if, @function +_compile_if: + # Prologue. + addi sp, sp, -32 + sw ra, 28(sp) + sw s0, 24(sp) + addi s0, sp, 32 + + addi s1, s1, 2 # Skip the if. + call _skip_spaces + + call _build_binary_expression + + call _skip_spaces + addi s1, s1, 4 # Skip the then. + + # if end marker. + li t0, 0x00646e65 # end\0 + sw t0, 20(sp) + + # Label prefix. + li t0, 0x66694c2e # .Lif + sw t0, 16(sp) + + li t0, 0x202c3061 # a0,_ + sw t0, 12(sp) + li t0, 0x207a7165 # eqz_ + sw t0, 8(sp) + li t0, 0x62626262 # bbbb + sb t0, 7(sp) + + addi a0, sp, 7 + li a1, 13 + call _write_out + + # Write the label counter. + mv a0, s2 + call _printi + + li a0, '\n' + call _put_char + +.Lcompile_if_loop: + /* DEBUG + mv a0, s1 + li a1, 6 + call _write_error + call _divide_by_zero_error */ + + call _skip_spaces + call _read_token + + mv a1, a0 + mv a0, s1 + addi a2, sp, 20 + call _token_compare + + beqz a0, .Lcompile_if_end + + call _read_line + li a1, 1 + call _compile_line + + j .Lcompile_if_loop + +.Lcompile_if_end: + # Write the label prefix. + addi a0, sp, 16 + li a1, 4 + call _write_out + + # Write the label counter. + mv a0, s2 + call _printi + + # Finalize the label. + li t0, 0x0a3a0a3a # :\n:\n + sh t0, 12(sp) + addi a0, sp, 12 + li a1, 2 + call _write_out + + addi s2, s2, 1 # Increment the label counter. + addi s1, s1, 4 # Skip the end with newline. + + # Epilogue. + lw ra, 28(sp) + lw s0, 24(sp) + addi sp, sp, 32 + ret + # Parameters: # a0 - Line length. # a1 - Whether the section header was already emitted. If not it should be @@ -1442,6 +1519,14 @@ _compile_line: call _memcmp beqz a0, .Lcompile_line_return + li t0, 0x6669 # if + sw t0, 12(sp) + mv a0, s1 + addi a1, sp, 12 + li a2, 2 + call _memcmp + beqz a0, .Lcompile_line_if + lbu t0, (s1) li t1, '.' beq t0, t1, .Lcompile_line_label @@ -1450,16 +1535,16 @@ _compile_line: j .Lcompile_line_unchanged # Else. +.Lcompile_line_if: + call _compile_if + j .Lcompile_line_section + .Lcompile_line_label: lw a0, 20(sp) call _compile_label j .Lcompile_line_section .Lcompile_line_return: - /* DEBUG - mv a0, s1 - li a1, 6 - call _write_error */ call _compile_return j .Lcompile_line_section @@ -1659,9 +1744,10 @@ _start: li a1, SOURCE_BUFFER_SIZE # Buffer size. call _read_file + li s2, 1 la s1, source_code # s1 = Source code position. call _compile # Call exit. li a0, 0 # Use 0 return code. - call exit + call _exit diff --git a/boot/stage2.elna b/boot/stage2.elna index 3ee1f32..0eb713c 100644 --- a/boot/stage2.elna +++ b/boot/stage2.elna @@ -14,6 +14,9 @@ type checking. 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. *) program @@ -36,7 +39,7 @@ begin end proc _build_binary_expression() -var loca0, loca4, loca8: Word +var loca0, loca4, loca8, loca20: Word begin _build_expression(0) @@ -44,8 +47,7 @@ begin loca8 := 0x0a316120 (* _a1\n *) _skip_spaces() - call _read_token - sw a0, 20(sp) + loca20 := _read_token() li t0, 0x26 # & sw t0, 16(sp) @@ -87,6 +89,14 @@ begin call _token_compare beqz a0, .L_build_binary_expression_minus + li t0, 0x2a # * + sw t0, 16(sp) + mv a0, s1 + lw a1, 20(sp) + addi a2, sp, 16 + call _token_compare + beqz a0, .L_build_binary_expression_product + goto .Lbuild_binary_expression_end .L_build_binary_expression_equal @@ -146,6 +156,19 @@ begin _write_out(@loca4, 4) _write_out(@loca8, 4) + goto .Lbuild_binary_expression_end + + .L_build_binary_expression_product + _advance(1) (* Skip *. *) + _build_expression(1) + loca0 := 0x6c756d (* mul *) + _write_out(@loca0, 3) + _write_out(@loca4, 4) + _write_out(@loca4, 4) + _write_out(@loca8, 4) + + goto .Lbuild_binary_expression_end + .Lbuild_binary_expression_end end @@ -156,28 +179,30 @@ a0 - X in aX, the register number to save the result. *) proc _build_expression() var - loca20, loca28, loca8: Word - loca24: ^Byte + loca0, loca20, loca28, loca8: Word + loca24, loca4: ^Byte begin - addi a0, a0, '0' # Make the register number to a character. - sw a0, 28(sp) # And save it. + (* Make the register number to a character and save it. *) + loca28 := loca84 + 0x30 (* 0 *) _skip_spaces() loca20 := _read_token() loca24 := _current() - addi a0, s1, 0 - lbu a0, (a0) + lbu a0, (s1) + li t0, '-' + beq a0, t0, .Lbuild_expression_negate + + lbu a0, (s1) li t0, '@' beq a0, t0, .Lbuild_expression_address - addi a0, s1, 0 - lbu a0, (a0) - call _is_digit - bnez a0, .Lbuild_expression_literal + loca0 := _front() + if _is_digit(loca0) then + goto .Lbuild_expression_literal + end - addi a0, s1, 0 - lbu a0, (a0) + lbu a0, (s1) li t0, '_' beq a0, t0, .Lbuild_expression_call @@ -200,6 +225,19 @@ begin goto .Lbuild_expression_advance + .Lbuild_expression_negate + _advance(1) (* Skip the -. *) + _build_expression(0) + + loca8 := 0x2067656e (* neg_ *) + _write_out(@loca8, 4) + loca8 := 0x202c3061 (* a0,_ *) + _write_out(@loca8, 4) + loca8 := 0x0a3061 (* a0,_ *) + _write_out(@loca8, 3) + + goto .Lbuild_expression_advance + .Lbuild_expression_address loca8 := 0x69646461 (* addi *) _write_out(@loca8, 4) @@ -216,40 +254,28 @@ begin loca24 := _current() loca20 := _read_token() - lw a0, 24(sp) - lw a1, 20(sp) - addi a0, a0, 4 # Skip the "loca" variable prefix. - addi a1, a1, -4 # Skip the "loca" variable prefix. - call _write_out + (* Skip the "loca" variable prefix. *) + loca4 := loca24 + 4 + loca0 := loca20 - 4 + _write_out(loca4, loca0) _put_char(0xa) goto .Lbuild_expression_advance .Lbuild_expression_identifier - lw t1, 28(sp) - li t0, 0x00202c00 # \0,_ - or t0, t0, t1 - sw t0, 8(sp) - li t0, 0x6120776c # lw a - sw t0, 4(sp) - addi a0, sp, 4 - li a1, 7 - call _write_out + loca8 := 0x6120776c (* lw a *) + _write_out(@loca8, 4) + loca8 := 0x00202c00 or loca28 # \0,_ + _write_out(@loca8, 3) - lw a0, 24(sp) - lw a1, 20(sp) - addi a0, a0, 4 # Skip the "loca" variable prefix. - addi a1, a1, -4 # Skip the "loca" variable prefix. - call _write_out + loca4 := loca24 + 4 + loca0 := loca20 - 4 + _write_out(loca4, loca0) (* Skip the "loca" variable prefix. *) - li t0, 0x0a # \n - sw t0, 8(sp) - li t0, 0x29707328 # (sp) - sw t0, 4(sp) - addi a0, sp, 4 - li a1, 5 - call _write_out + loca8 := 0x29707328 (* (sp) *) + _write_out(@loca8, 4) + _put_char(0x0a) (* \n *) goto .Lbuild_expression_advance @@ -261,18 +287,13 @@ begin goto .Lbuild_expression_end .Lbuild_expression_literal - lw t1, 28(sp) - li t0, 0x00202c00 # \0,_ - or t0, t0, t1 - sw t0, 8(sp) - li t0, 0x6120696c # li a - sw t0, 4(sp) - addi a0, sp, 4 - li a1, 7 - call _write_out + loca8 := 0x6120696c (* li a *) + _write_out(@loca8, 4) + loca8 := 0x00202c00 or loca28 (* \0,_ *) + _write_out(@loca8, 3) _write_out(loca24, loca20) - _put_char(0x0a) # \n + _put_char(0x0a) (* \n *) goto .Lbuild_expression_advance @@ -335,11 +356,9 @@ begin loca0 := 0x202c30 (* 0,_ *) _write_out(@loca0, 3) - lw a0, 20(sp) - lw a1, 16(sp) - addi a0, a0, 4 # Skip the "loca" variable prefix. - addi a1, a1, -4 # Skip the "loca" variable prefix. - call _write_out + loca20 := loca20 + 4 + loca16 := loca16 - 4 + _write_out(loca20, loca16) loca0 := 0x29707328 (* (sp) *) _write_out(@loca0, 4) @@ -359,7 +378,7 @@ Returns the procedure result in a0. *) proc _compile_call(loca84: ^Byte, loca80: Word) var - loca0, loca12: Word + loca0, loca4, loca12: Word begin loca12 := 0 (* Argument count for a procedure call. *) @@ -377,49 +396,24 @@ begin loca0 := 0x202c30 (* 0,_ *) _write_out(@loca0, 3) - lw t0, 12(sp) # Argument count for a procedure call. - (* Only 6 arguments are supported with a0-a5. Save all arguments on the stack so they aren't overriden afterwards. - The offset on the stack always has two digits in this case. *) - li t1, -4 - mul t1, t0, t1 - addi t1, t1, 60 - li t2, 10 - div t3, t1, t2 - rem t4, t1, t2 - addi t3, t3, '0' - addi t4, t4, '0' + loca0 := -4 * loca12 + loca0 := loca0 + 60 + _printi(loca0) - sw t3, 8(sp) - sw t4, 4(sp) - - addi a0, sp, 8 - li a1, 1 - call _write_out - - addi a0, sp, 4 - li a1, 1 - call _write_out - - li t0, 0x0a # \n - sw t0, 8(sp) - li t0, 0x29707328 # (sp) - sw t0, 4(sp) - addi a0, sp, 4 - li a1, 5 - call _write_out + loca0 := 0x29707328 (* (sp) *) + _write_out(@loca0, 4) + _put_char(0x0a) (* \n *) _skip_spaces() lbu t0, (s1) li t1, ',' bne t0, t1, .Lcompile_call_paren - lw t0, 12(sp) # Argument count for a procedure call. - addi t0, t0, 1 - sw t0, 12(sp) + loca12 := loca12 + 1 (* Argument count for a procedure call. *) _advance(1) (* Skip the comma between the arguments. *) goto .Lcompile_call_argument @@ -432,47 +426,62 @@ 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. *) - lw t0, 12(sp) - li t1, 5 - bgt t0, t1, .Lcompile_call_perform + loca0 := 0x6120776c (* lw a *) + (* lw a0, 60(sp) *) + _write_out(@loca0, 4) + loca4 := 0x36202c30 (* 0, 6 *) + _write_out(@loca4, 4) + loca4 := 0x70732830 (* 0(sp *) + _write_out(@loca4, 4) + loca4 := 0x0a29 (* )\n *) + _write_out(@loca4, 2) - li t0, 0x6120776c # lw a - sw t0, 4(sp) - addi a0, sp, 4 - li a1, 4 - call _write_out + (* lw a1, 56(sp) *) + _write_out(@loca0, 4) + loca4 := 0x35202c31 (* 1, 5 *) + _write_out(@loca4, 4) + loca4 := 0x70732836 (* 6(sp *) + _write_out(@loca4, 4) + loca4 := 0x0a29 (* )\n *) + _write_out(@loca4, 2) - lw t0, 12(sp) # Argument count for a procedure call. + (* lw a2, 52(sp) *) + _write_out(@loca0, 4) + loca4 := 0x35202c32 (* 2, 5 *) + _write_out(@loca4, 4) + loca4 := 0x70732832 (* 2(sp *) + _write_out(@loca4, 4) + loca4 := 0x0a29 (* )\n *) + _write_out(@loca4, 2) - li t1, -4 - mul t1, t0, t1 - addi t1, t1, 60 - li t2, 10 - div t3, t1, t2 - rem t4, t1, t2 - addi t3, t3, '0' - addi t4, t4, '0' - addi t0, t0, '0' + (* lw a3, 48(sp) *) + _write_out(@loca0, 4) + loca4 := 0x34202c33 (* 3, 4 *) + _write_out(@loca4, 4) + loca4 := 0x70732838 (* 8(sp *) + _write_out(@loca4, 4) + loca4 := 0x0a29 (* )\n *) + _write_out(@loca4, 2) - li t5, 0x0a # \n - sb t5, 11(sp) - li t5, 0x29707328 # (sp) - sw t5, 7(sp) - sb t4, 6(sp) - sb t3, 5(sp) - li t5, 0x202c # ,_ - sh t5, 3(sp) - sb t0, 2(sp) + (* lw a4, 44(sp) *) + _write_out(@loca0, 4) + loca4 := 0x34202c34 (* 4, 4 *) + _write_out(@loca4, 4) + loca4 := 0x70732834 (* 4(sp *) + _write_out(@loca4, 4) + loca4 := 0x0a29 (* )\n *) + _write_out(@loca4, 2) - addi a0, sp, 2 - li a1, 10 - call _write_out + (* lw a5, 40(sp) *) + _write_out(@loca0, 4) + loca4 := 0x34202c35 (* 5, 4 *) + _write_out(@loca4, 4) + loca4 := 0x70732830 (* 0(sp *) + _write_out(@loca4, 4) + loca4 := 0x0a29 (* )\n *) + _write_out(@loca4, 2) - loca12 := loca12 + 1 - goto .Lcompile_call_restore - - .Lcompile_call_perform - loca0 := 0x6c6c6163 + loca0 := 0x6c6c6163 (* call *) _write_out(@loca0, 4) _put_char(0x20) (* _ *) @@ -532,6 +541,9 @@ begin li t1, '-' beq t0, t1, .Ltoken_character_single + li t1, '*' + beq t0, t1, .Ltoken_character_single + li t1, '@' beq t0, t1, .Ltoken_character_single (* Expect an identifier or a number. *) @@ -1125,6 +1137,61 @@ begin _build_binary_expression() end +proc _compile_if() +var + loca8, loca12, loca16, loca20: Word + loca4: ^Byte +begin + _advance(2) (* Skip the if. *) + _skip_spaces() + + _build_binary_expression() + + _skip_spaces() + _advance(4) (* Skip the then. *) + + loca20 := 0x00646e65 (* if end marker and newline. *) + loca16 := 0x66694c2e (* Label prefix ".Lif". *) + + loca12 := 0x7a716562 (* beqz *) + _write_out(@loca12, 4) + loca12 := 0x2c306120 (* _a0, *) + _write_out(@loca12, 4) + _put_char(0x20) (* _ *) + + (* Write the label *) + _write_out(@loca16, 4) + mv a0, s2 + call _printi + + _put_char(0x0a) (* \n *) + + .Lcompile_if_loop + _skip_spaces() + loca12 := _read_token() + + loca4 := _current() + loca8 := _token_compare(loca4, loca12, @loca20) + + if loca8 then + loca12 := _read_line() + _compile_line(loca12, 1) + + goto .Lcompile_if_loop + end + + (* Write the label *) + _write_out(@loca16, 4) + mv a0, s2 + call _printi + + loca12 := 0x0a3a0a3a (* :\n:\n *) + _write_out(@loca12, 2) + + addi s2, s2, 1 # Increment the label counter. + _advance(4) (* Skip the end with newline. *) +end + (* Parameters: a0 - Line length. @@ -1234,6 +1301,14 @@ begin call _memcmp beqz a0, .Lcompile_line_return + li t0, 0x6669 # if + sw t0, 12(sp) + mv a0, s1 + addi a1, sp, 12 + li a2, 2 + call _memcmp + beqz a0, .Lcompile_line_if + lbu t0, (s1) li t1, '.' beq t0, t1, .Lcompile_line_label @@ -1242,6 +1317,10 @@ begin goto .Lcompile_line_unchanged (* Else. *) + .Lcompile_line_if: + _compile_if() + goto .Lcompile_line_section + .Lcompile_line_label _compile_label(loca84) goto .Lcompile_line_section @@ -1409,7 +1488,9 @@ begin end proc _compile() -var loca0, loca4: Word +var + loca0, loca4: Word + loca8: Bool begin loca4 := 0 (* Whether the text section header was already emitted. *) @@ -1419,15 +1500,11 @@ begin _skip_indentation() loca0 := _read_line() - lw a0, 0(sp) - lw a1, 4(sp) - call _compile_line + loca8 := _compile_line(loca0, loca4) beqz a0, .Lcompile_do (* Update whether the text section header was already emitted. *) - lw t0, 4(sp) - or t0, t0, a0 - sw t0, 4(sp) + loca4 := loca4 or loca8 goto .Lcompile_do .Lcompile_end @@ -1460,5 +1537,6 @@ begin call _read_file la s1, source_code # s1 = Source code position. + li s2, 1 _compile() end.