forth-riscv

My forth
git clone git://git.electrosoup.com/forth-riscv
Log | Files | Refs

commit 2b483904003bbec46c3ce450406f96edefe40fe7
parent 46778b08a87e3a4fdf6b51d9cebd88ba3774ec06
Author: Christian Ermann <christianermann@gmail.com>
Date:   Fri, 15 Nov 2024 01:08:23 -0800

Switch from indirect threaded code to subroutine threaded code

Diffstat:
Mforth.s | 578+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
1 file changed, 418 insertions(+), 160 deletions(-)

diff --git a/forth.s b/forth.s @@ -30,10 +30,7 @@ .equ flag_immediate, 0x40 .macro next - load_cell w, 0(ip) - load_cell x, 0(w) - addi ip, ip, cell - jr x + ret .endm .macro push_addr addr @@ -94,9 +91,14 @@ name_\label: .byte \flags .balign cell + .globl code_\label +code_\label: + .int \label + + .section ".text" + .balign cell .globl \label \label: - .int \code .endm .macro defmeta name, label @@ -112,19 +114,19 @@ meta_\label: .macro defcode name, label, hash, flags=0 defmeta "\name", \label defhead "\name", \label, \hash, code_\label, flags=\flags - .section ".text" - .globl code_\label -code_\label: .endm .macro defword name, label, hash, flags=0 defmeta "\name", \label defhead "\name", \label, \hash, docol, flags=\flags + docol .endm .macro defvar name, label, hash, flags=0, initial=0 defmeta "\name", \label defhead "\name", \label, \hash, dovar, flags=\flags + push_ret ra + jal dovar .globl _\label _\label: .endm @@ -132,25 +134,31 @@ _\label: .macro defconst name, label, hash, flags=0, value=0 defmeta "\name", \label defhead "\name", \label, \hash, docon, flags=\flags + push_ret ra + jal docon .globl _\label _\label: .endm -docol: - push_ret ip - addi ip, w, cell - next +.macro docol + push_ret ra +.endm + +.macro exit + pop_ret ra + ret +.endm dovar: - addi w, w, cell - push w - next + push ra + pop_ret ra + ret docon: - addi w, w, cell - load_cell w, 0(w) + load_cell w, 0(ra) push w - next + pop_ret ra + ret dodoes: push_ret ip # save old ip @@ -159,8 +167,7 @@ dodoes: mv ip, ra # set new ip next -defcode "exit", exit, 0xCDED1A85 - pop_ret ip +defcode "break", break, 0xC9648178 next # ----------------------------------------------------------------------------- @@ -208,7 +215,9 @@ defvar "source-id", source_id, 0x965ED1E2 defcode "type", type, 0x5127F14D pop a1 # length pop a0 # address + push_ret ra jal uart_put_string + pop_ret ra next defcode "emit", emit, 0x2D88474A @@ -224,7 +233,9 @@ defcode "key", key, 0x6815C86C defcode "accept", accept, 0x08247E29 pop w # max len pop x # address + push_ret ra jal accept_impl + pop_ret ra push w # recv'd len next @@ -251,24 +262,49 @@ accept_impl: ret defword "refill", refill, 0x238BAA91 - .int source_id, fetch, q_branch, _refill_tib + jal source_id + jal fetch + jal q_branch + .int _refill_tib _refill_fib: - .int fib, read_line, q_branch, _refill_failed - .int fib, drop, swap, branch, _refill_success + jal fib + jal read_line + jal q_branch + .int _refill_failed + jal fib + jal drop + jal swap + jal branch + .int _refill_success _refill_tib: - .int tib, accept, dup, q_branch, _refill_failed - .int tib, drop, swap + jal tib + jal accept + jal dup + jal q_branch + .int _refill_failed + jal tib + jal drop + jal swap _refill_success: - .int tick_source, two_store - .int lit, 0, source_offset, store - .int lit, -1 - .int exit + jal tick_source + jal two_store + jal lit + .int 0 + jal source_offset + jal store + jal lit + .int -1 + exit _refill_failed: - .int drop, lit, 0 - .int exit + jal drop + jal lit + .int 0 + exit defword "source", source, 0x1BCF29D8 - .int tick_source, two_fetch, exit + jal tick_source + jal two_fetch + exit defcode "tib", tib, 0xC90B0194 # ( -- addr len ) @@ -291,46 +327,90 @@ defcode "fib", fib, 0xBCE49236 defword "skip-while", skip_while, 0xBBFD4B86 # ( addr1 len1 -- addr2 len2 ) _skip_while_loop: - .int dup, q_branch, _skip_while_done - .int over, char_fetch, delimiter, fetch - .int equal, q_branch, _skip_while_done - .int lit, 1, apply_offset - .int branch, _skip_while_loop + jal dup + jal q_branch + .int _skip_while_done + jal over + jal char_fetch + jal delimiter + jal fetch + jal equal + jal q_branch + .int _skip_while_done + jal lit + .int 1 + jal apply_offset + jal branch + .int _skip_while_loop _skip_while_done: - .int exit + exit defword "skip-until", skip_until, 0x661A5D67 # ( addr1 len1 -- addr2 len2 ) _skip_until_loop: - .int dup, q_branch, _skip_until_done - .int over, char_fetch, delimiter, fetch - .int not_equal, q_branch, _skip_until_done - .int lit, 1, apply_offset - .int branch, _skip_until_loop + jal dup + jal q_branch + .int _skip_until_done + jal over + jal char_fetch + jal delimiter + jal fetch + jal not_equal + jal q_branch + .int _skip_until_done + jal lit + .int 1 + jal apply_offset + jal branch + .int _skip_until_loop _skip_until_done: - .int exit + exit defword "apply-offset", apply_offset, 0x605143A5 # ( addr1 len1 offset -- addr2 len2 ) - .int tuck, minus, to_ret, plus, from_ret, exit + jal tuck + jal minus + jal to_ret + jal plus + jal from_ret + exit defword "parse-offset", parse_offset, 0x9E5C3F80 # ( addr1 len1 -- offset ) - .int lit, 1, min, plus - .int source, drop, minus - .int exit + jal lit + .int 1 + jal min + jal plus + jal source + jal drop + jal minus + exit defword "parse", parse, 0x423B42EC - .int delimiter, store - .int source, source_offset, fetch, apply_offset - .int skip_while, over, to_ret - .int skip_until - .int two_dup, parse_offset, source_offset, store - .int drop, from_ret, tuck, minus - .int exit + jal delimiter + jal store + jal source + jal source_offset + jal fetch + jal apply_offset + jal skip_while + jal over + jal to_ret + jal skip_until + jal two_dup + jal parse_offset + jal source_offset + jal store + jal drop + jal from_ret + jal tuck + jal minus + exit defword "parse-word", parse_word, 0xB218226F - .int bl, parse, exit + jal bl + jal parse + exit defcode ">number", to_number, 0x2F770E4C pop x # word len @@ -394,20 +474,36 @@ defcode "word>hash", word_to_hash, 0x50E0A245 next defword "find", find, 0xBDF0855A - .int hash - .int latest, fetch + jal hash + jal latest + jal fetch _find_loop: - .int dup, zero_equal, q_branch, _find_check_hidden - .int swap, drop, exit + jal dup + jal zero_equal + jal q_branch + .int _find_check_hidden + jal swap + jal drop + exit _find_check_hidden: - .int dup, hidden_q, zero_equal, q_branch, _find_next_word + jal dup + jal hidden_q + jal zero_equal + jal q_branch + .int _find_next_word _find_check_hash: - .int two_dup - .int word_to_hash, equal, q_branch, _find_next_word - .int swap, drop, exit + jal two_dup + jal word_to_hash + jal equal + jal q_branch + .int _find_next_word + jal swap + jal drop + exit _find_next_word: - .int fetch - .int branch, _find_loop + jal fetch + jal branch + .int _find_loop defcode ">cfa", to_cfa, 0x8CAC3233 pop w @@ -423,74 +519,121 @@ defcode "execute", execute, 0xA01E3D98 defword "interpret", interpret, 0x1F98C57A _interpret_start: - .int parse_word, q_dup, q_branch, _interpret_parse_area_empty - .int two_dup - .int find, q_dup, q_branch, _interpret_word_not_found - .int nip, nip, to_cfa - .int compiling_q, q_branch, _interpret_execute_word - .int dup - .int immediate_q, zero_equal, q_branch, _interpret_execute_word + jal parse_word + load_cell w, 0(psp) # dup, ?branch + beqz w, _interpret_parse_area_empty + jal two_dup + jal find + load_cell w, 0(psp) # dup, ?branch + beqz w, _interpret_word_not_found + addi psp, psp, 2 * cell # nip, nip + store_cell w, 0(psp) + jal to_cfa + la w, _state # compiling? ?branch + load_cell w, 0(w) + beqz w, _interpret_execute_word + load_cell w, 0(psp) # dup immediate? 0= branch? + addi w, w, flag_offset - code_offset + lb w, 0(w) + andi w, w, flag_immediate + bnez w, _interpret_execute_word _interpret_compile_word: - .int comma, branch, _interpret_start + jal fetch + jal jal_to + jal comma + j _interpret_start _interpret_execute_word: - .int execute, branch, _interpret_start + jal execute + jal branch + .int _interpret_start _interpret_word_not_found: - .int to_number, zero_equal, q_branch, _interpret_retry - .int compiling_q, q_branch, _interpret_start + addi psp, psp, cell + jal to_number + pop w + bnez w, _interpret_retry + jal compiling_q + jal q_branch + .int _interpret_start _interpret_compile_number: - .int lit, lit, comma, comma, branch, _interpret_start + jal lit + .int lit + jal comma + jal comma + jal branch + .int _interpret_start _interpret_retry: - .int drop, branch, _interpret_start + jal drop + jal branch + .int _interpret_start _interpret_parse_area_empty: - .int drop, exit + addi psp, psp, 2 * cell + exit defword "evaluate", evaluate, 0xACE4360A - .int lit, 1, source_id, store + jal lit + .int 1 + jal source_id + jal store _evaluate_loop: - .int refill, q_branch, _evaluate_done - .int interpret - .int branch, _evaluate_loop + jal refill + jal q_branch + .int _evaluate_done + jal interpret + jal branch + .int _evaluate_loop _evaluate_done: - .int lit, 0, source_id, store - .int exit + jal lit + .int 0 + jal source_id + jal store + exit defcode "branch", branch, 0xB6873945 - load_cell w, 0(ip) - mv ip, w + load_cell w, 0(ra) + mv ra, w next defcode "?branch", q_branch, 0x6AF3C1DE pop w bnez w, _branch_done - load_cell w, 0(ip) - mv ip, w + load_cell w, 0(ra) + mv ra, w next _branch_done: - addi ip, ip, cell + addi ra, ra, cell next defcode "prompt", prompt, 0xDFE6493B la w, _prompt la x, _prompt_len + push_ret ra call uart_put_string + pop_ret ra next defcode "okay", okay, 0xBA9EEB49 la w, _okay la x, _okay_len + push_ret ra call uart_put_string + pop_ret ra next defword "quit", quit, 0x47878736 -_quit_top: - .int rp_top, rp_store - .int prompt - .int refill, drop # TODO: What should happen when 'refill' fails? - .int interpret, okay - .int branch, _quit_top + jal rp_top + jal rp_store + jal prompt + jal refill + jal drop # TODO: What should happen when 'refill' fails? + jal interpret + jal okay + jal branch + .int quit defword "abort", abort, 0xA52BCAF9 - .int sp_top, sp_store, quit + jal sp_top + jal sp_store + jal quit # ----------------------------------------------------------------------------- # memory access @@ -570,16 +713,17 @@ defcode "cell+", cell_plus, 0xB14A8CBA # ----------------------------------------------------------------------------- defword "here", here, 0x213B65CB - .int dp, fetch - .int exit + jal dp + jal fetch + exit defcode "lit", lit, 0x404CD5B6 - load_cell w, 0(ip) - addi ip, ip, cell + load_cell w, 0(ra) + addi ra, ra, cell push w next -defcode "lit-string", lit_string, 0xC7BE567C +defcode "lit-string", lit_string, 0xC7BE567C # TODO: ip -> ra load_cell x, 0(ip) # len addi ip, ip, cell mv w, ip # addr @@ -592,25 +736,41 @@ defcode "lit-string", lit_string, 0xC7BE567C next defword "allot", allot, 0xADB1A69F - .int dp, add_store - .int exit + jal dp + jal add_store + exit -defword ",", comma, 0x290C95CB - .int here, store # store value - .int lit, cell, allot # increment 'here' - .int exit +defcode ",", comma, 0x290C95CB + pop w + la y, _dp + load_cell x, 0(y) + store_cell w, 0(x) + addi x, x, cell + store_cell x, 0(y) + next -defword "c,", char_comma, 0xA32A0A5A - .int here, char_store # store value - .int lit, 1, allot # increment 'here' - .int exit +defcode "c,", char_comma, 0xA32A0A5A + pop w + la y, _dp + load_cell x, 0(y) + sb w, 0(x) + addi x, x, 1 + store_cell x, 0(y) + next defword "align", align, 0x602C63DE - .int dp, dup, fetch - .int lit, 3, plus - .int lit, -4, and_ - .int swap, store - .int exit + jal dp + jal dup + jal fetch + jal lit + .int 3 + jal plus + jal lit + .int -4 + jal and_ + jal swap + jal store + exit # ----------------------------------------------------------------------------- # stack manipulation @@ -837,31 +997,120 @@ _less_than: next defword "min", min, 0xC98F4557 - .int two_dup, less_than, q_branch, _min_b + jal two_dup + jal less_than + jal q_branch + .int _min_b _min_a: - .int drop, exit + jal drop + exit _min_b: - .int nip, exit + jal nip + exit # ----------------------------------------------------------------------------- # compiler # ----------------------------------------------------------------------------- +defcode "jal-immed", jal_immed, 0xA914EF13 + # encode a value as an immediate for a 'jal' instruction + pop x + li y, 0x000FF000 # imm[19:12] + and w, x, y + li y, 0x00100000 # imm[20] + and y, x, y + slli y, y, 11 + add w, w, y + li y, 0x00000800 # imm[11] + and y, x, y + slli y, y, 9 + add w, w, y + andi y, x, 0x000007FE # imm[10:1] + slli y, y, 20 + add w, w, y + push w + next + +defword "jal-to", jal_to, 0xC38EF054 + # generate a 'jal ra, addr' instruction + pop w + la x, _dp + load_cell x, 0(x) + sub w, w, x + push w + jal jal_immed + pop w + addi w, w, 0x000000EF # jal ra, imm + push w + exit + +defword "jal-dovar", jal_dovar, 0x668658A5 + # generate a 'jal ra, dovar' instruction + la w, dovar + push w + jal jal_to + exit + +defword "dovar,", dovar_comma, 0x1F514E5B + jal jal_dovar + jal comma + exit + +defword "docol,", docol_comma, 0xFAE1EE9E + li w, 0xFE192E23 # sw ra, -4(s2) + push w + jal comma + li w, 0xFFC90913 # addi s2, s2, -4 + push w + jal comma + exit + +defword "exit,", exit_comma, 0xD540F80B + li w, 0x00092083 # lw ra, 0(s2) + push w + jal comma + li w, 0x00490913 # addi s2, s2, 4 + push w + jal comma + li w, 0x00008067 # ret + push w + jal comma + exit + defword "create", create, 0x26BB595D - .int parse_word - .int latest, fetch, comma # link - .int hash, comma # hash - .int lit, 0, comma # meta - .int lit, 0, char_comma # flags - .int align - .int here, lit, -code_offset, plus, latest, store # update 'latest' - .int lit, dovar, comma - .int exit + jal parse_word + jal latest # link + jal fetch + jal comma + jal hash # hash + jal comma + push_imm 0 # meta + jal comma + push_imm 0 # flags + jal char_comma + jal align + # update latest + la x, _dp + load_cell x, 0(x) + addi x, x, -code_offset + la y, _latest + store_cell x, 0(y) + # code field + la x, _dp + load_cell y, 0(x) + addi y, y, cell + store_cell y, -cell(y) + store_cell y, 0(x) + # append 'dovar' + jal dovar_comma + exit defcode "hash", hash, 0xEDBF0FE3 pop x # string length pop w # string address + push_ret ra jal hash_impl + pop_ret ra push w # string hash next @@ -907,18 +1156,24 @@ defcode "hidden?", hidden_q, 0x6F436C72 next defword ":", colon, 0x3F0CB86D - .int create # create header - .int lit, -cell, allot - .int lit, docol, comma # append 'docol' - .int latest, fetch, hidden # hide word - .int r_bracket # enter 'compile' mode - .int exit + jal create # create header + jal lit + .int -cell + jal allot + jal docol_comma # append 'docol' + jal latest + jal fetch + jal hidden # hide word + jal r_bracket # enter 'compile' mode + exit defword ";", semicolon, 0x3E0CB6DA, flags=flag_immediate - .int lit, exit, comma # append 'exit' - .int latest, fetch, hidden # show word - .int l_bracket # enter 'immediate' mode - .int exit + jal exit_comma + jal latest + jal fetch + jal hidden + jal l_bracket + exit defcode "compiling?", compiling_q, 0x94652AE2 la w, _state @@ -935,34 +1190,36 @@ defcode "immediate?", immediate_q, 0x89F23E9F push w next +# TODO: everything after this needs updated to work with subroutine threading + defword "postpone", postpone, 0x933F531F, flags=flag_immediate .int parse_word, find # TODO: was word actually found? .int to_cfa, dup .int immediate_q, q_branch, _postpone_compile _postpone_execute: # stores "<xt>" into the current definition - .int comma, exit + #.int comma, exit _postpone_compile: # stores "lit <xt> ," into the current definition - .int lit, lit, comma, comma, lit, comma, comma, exit + #.int lit, lit, comma, comma, lit, comma, comma, exit defword "immediate", immediate, 0xF232267A .int latest, fetch, lit, flag_offset, plus, dup .int fetch, lit, flag_immediate, or_ .int swap, store - .int exit + exit defword "variable", variable, 0x19385305 .int create # create header .int lit, 0, comma # initialize to zero - .int exit + exit defword "constant", constant, 0x0691EA25 .int create # create header .int lit, -cell, allot - .int lit, docon, comma # append 'docon' + #.int lit, docon, comma # append 'docon' .int comma # initialize to value on stack - .int exit + exit defcode "jal-dodoes", jal_dodoes, 0x4F82E787 pop w @@ -988,12 +1245,12 @@ defcode "jal-dodoes", jal_dodoes, 0x4F82E787 defword ";does", does, 0xF5BCD777, flags=flag_immediate .int lit, does_internal, comma .int here, jal_dodoes, comma - .int exit + exit defword "(;does)", does_internal, 0xED4B7678 .int from_ret .int latest, fetch, lit, code_offset, plus, store - .int exit + exit defcode "read-char", read_char, 0xF07E2044 jal _read_char_impl @@ -1024,22 +1281,22 @@ _read_char_eof: defword "buffer-open", buffer_open, 0x79AAD9CA # ( addr len -- addr end-addr cur-addr ) .int over, plus, over - .int exit + exit defword "buffer-close", buffer_close, 0x42B7429E # ( open-buffer -- len ) .int nip, swap, minus - .int exit + exit defword "buffer-emit", buffer_emit, 0xEA240555 # ( open-buffer char -- open-buffer ) .int over, store, lit, 1, plus - .int exit + exit defword "buffer-full?", buffer_full_q, 0x070A2E6C # ( open-buffer -- flag ) .int two_dup, greater_than # TODO: >= - .int exit + exit defword "read-line", read_line, 0xAF1308A2 .int buffer_open @@ -1051,20 +1308,21 @@ _read_line_loop: .int buffer_full_q, q_branch, _read_line_buffer_full .int branch, _read_line_loop _read_line_newline: - .int drop, buffer_close, lit, -1, exit + #.int drop, buffer_close, lit, -1, exit _read_line_eof: .int drop, buffer_close, dup, q_branch, _read_line_eof_fail - .int lit, -1, exit + #.int lit, -1, exit _read_line_eof_fail: - .int lit, 0, exit + #.int lit, 0, exit _read_line_buffer_full: - .int buffer_close, lit, 0, exit + #.int buffer_close, lit, 0, exit -.section ".rodata" +.section ".text" program: - .int type - .int quit + jal type + jal quit +.section ".rodata" version_string: .ascii "soup forth rv32\n" version_string_len = (. - version_string) @@ -1103,5 +1361,5 @@ start: push_addr version_string push_imm version_string_len - next + jal program