2841 lines
62 KiB
Python
2841 lines
62 KiB
Python
from vasm import *
|
|
from vcpu import VirtualCpu
|
|
import io
|
|
|
|
# vCPU register locations in memory
|
|
vPC = ADDR("vPC")
|
|
vAC = ADDR("vAC")
|
|
vLR = ADDR("vLR")
|
|
vSP = ADDR("vSP")
|
|
|
|
# SYS functions
|
|
sysFn = ADDR("sysFn")
|
|
sysArgs0 = ADDR("sysArgs0")
|
|
sysArgs1 = ADDR("sysArgs1")
|
|
sysArgs2 = ADDR("sysArgs2")
|
|
sysArgs3 = ADDR("sysArgs3")
|
|
sysArgs4 = ADDR("sysArgs4")
|
|
sysArgs5 = ADDR("sysArgs5")
|
|
sysArgs6 = ADDR("sysArgs6")
|
|
sysArgs7 = ADDR("sysArgs7")
|
|
|
|
SYS_VDrawBits_134 = ADDR("SYS_VDrawBits_134")
|
|
SYS_Random_34 = ADDR("SYS_Random_34")
|
|
|
|
# Extra variables in memory
|
|
memSize = ADDR("memSize")
|
|
romType = ADDR("romType")
|
|
romTypeValue_ROMv3 = ADDR("romTypeValue_ROMv2")
|
|
videoTable = ADDR("videoTable")
|
|
serialRaw = ADDR("serialRaw")
|
|
buttonState = ADDR("buttonState")
|
|
frameCount = ADDR("frameCount")
|
|
|
|
# Some constants
|
|
F_IMMD = 0x80 # Immediate flag
|
|
F_COMP = 0x40 # Compile only flag
|
|
F_HIDD = 0x20 # Hidden flag
|
|
F_MASK = 0x1F # Mask for the length of the word
|
|
rsp_top = 0x05FE # Top of the return stack
|
|
dsp_top = 0x06FE # Top of the data stack
|
|
buf_base = 0x0200 # Buffer for READ
|
|
auxbuf_end = 0x0320 # Buffer for other functions
|
|
segment_size = 96
|
|
buf_size = 0xF0
|
|
init1_base = 0x0200
|
|
init2_base = 0x0300
|
|
code_base = 0x0800
|
|
data_base = None # Automatically resolved
|
|
|
|
welcome_string = "GtForth v0.1\n"
|
|
free_bytes_string = "bytes free\n"
|
|
|
|
# User variables start from $30 in zero page
|
|
zp_base_addr = 0x30
|
|
variable_base = zp_base_addr
|
|
|
|
def zpSkip(nbytes=2):
|
|
global zp_base_addr
|
|
addr = zp_base_addr
|
|
zp_base_addr += nbytes
|
|
return addr
|
|
|
|
buf = zpSkip() # Current buffer position
|
|
inp = zpSkip() # The current input position
|
|
wlen = zpSkip() # The length of the current word
|
|
wbuf = zpSkip() # The current word
|
|
w = zpSkip() # Work register
|
|
adj1 = zpSkip() # Address adjust register 1
|
|
tmp1 = zpSkip() # Temporary 1
|
|
tmp2 = zpSkip() # Temporary 2
|
|
tmp3 = zpSkip() # Temporary 3
|
|
tmp4 = zpSkip() # Temporary 4
|
|
tmp5 = zpSkip() # Temporary 5
|
|
md1 = zpSkip() # Temporary 1 for multiplication / division
|
|
md2 = zpSkip() # Temporary 2 for multiplication / division
|
|
md3 = zpSkip() # Temporary 3 for multiplication / division
|
|
str1 = zpSkip() # Temporary 1 for find
|
|
str2 = zpSkip() # Temporary 2 for find
|
|
rsp = zpSkip() # Return stack pointer
|
|
dsp = zpSkip() # Data stack pointer
|
|
pc = zpSkip() # Program counter (points to the current word)
|
|
here = zpSkip() # The value of the memory
|
|
latest = zpSkip() # The latest defined word
|
|
base = zpSkip() # The base for number parsing / printing
|
|
state = zpSkip() # The state of the compiler
|
|
scr_pos = zpSkip() # The position on the screen
|
|
scr_color = zpSkip() # The current color
|
|
|
|
assert(zp_base_addr < 0x80)
|
|
|
|
# Addresses of auxiliary functions
|
|
zp_base_addr = 0x82
|
|
auxiliary_base = zp_base_addr
|
|
auxiliary_functions = []
|
|
|
|
def defAux(name):
|
|
globals()["%s_fn" % name] = zpSkip()
|
|
auxiliary_functions.append(name)
|
|
|
|
defAux("next_word") # The function to compute the next word
|
|
defAux("exec") # To jump to some other execution point
|
|
defAux("adjust_addr") # To adjust a general address (for packing)
|
|
defAux("forward") # Move forward the address by 2 bytes (and adjust)
|
|
defAux("cforward") # Move forward the address by 1 byte (and adjust)
|
|
defAux("align_addr") # To align a general address
|
|
defAux("push_data") # To push value on the data stack
|
|
defAux("pop_data") # To pop from the data stack
|
|
defAux("push_return") # To push value on the return stack
|
|
defAux("pop_return") # To pop from the return stack
|
|
defAux("multiply") # To multiply two numbers
|
|
defAux("udivide") # To divide two (unsigned) numbers
|
|
defAux("divide") # To divide two (signed) numbers
|
|
defAux("divide_aux") # Auriliary function to do division
|
|
defAux("unsigned_cmp") # Auxiliary function to compare unsigned numbers
|
|
defAux("signed_cmp") # Auxiliary function to compare signed numbers
|
|
defAux("setup_draw") # To setup for the SYS_VDrawBits_134
|
|
defAux("emit") # To print a character onto the screen
|
|
defAux("select_font") # To select the character font
|
|
defAux("draw_char") # To draw the current character
|
|
defAux("update_pos") # To update the current position
|
|
defAux("emit_scroll") # To scroll based on the last emit
|
|
defAux("clearline") # To clear a line
|
|
defAux("scroll") # To scroll the screen
|
|
defAux("read") # To read characters from keyboard
|
|
defAux("show_cursor") # To show the cursor
|
|
defAux("process_key") # To process an input key
|
|
defAux("process_bs") # To process the backspace key
|
|
defAux("parse_word") # To parse a word
|
|
defAux("number") # To parse a number
|
|
defAux("unumber") # To parse a unsigned number
|
|
defAux("digit") # To parse a single digit
|
|
defAux("memcpy") # To copy memory from one place to another
|
|
defAux("streq") # To compare two strings
|
|
defAux("find") # To find a word in the dictionary
|
|
defAux("match") # Auxiliary function to find the word
|
|
defAux("flags") # To obtain the flags of a word in the dictionary
|
|
defAux("cfa") # To obtain the code of a word in the dictionary
|
|
defAux("comma") # To compile a word
|
|
defAux("str_comma") # To compile a string
|
|
defAux("cv_number") # To convert a number to string
|
|
defAux("cv_unumber") # To convert an unsigned number to string
|
|
defAux("cv_digit") # To convert a digit to string
|
|
|
|
assert(zp_base_addr < 0xE0)
|
|
|
|
current_link = [ 0 ] # Current link for latest (at assembly time)
|
|
|
|
# ============================
|
|
# Auxiliary meta functions
|
|
# ============================
|
|
|
|
def get_link():
|
|
"""Get the link to the latest defined word.
|
|
|
|
Returns
|
|
-------
|
|
The link to the latest word (to be resolved later).
|
|
"""
|
|
fn = lambda x : ADDR(x[0])
|
|
return (fn, current_link)
|
|
|
|
def reference(label):
|
|
"""Places a reference to a label in the resulting GT1 file.
|
|
|
|
Parameters
|
|
----------
|
|
label: str
|
|
The label.
|
|
"""
|
|
# Use little-endian format here
|
|
BYTE((LO, label))
|
|
BYTE((HI, label))
|
|
|
|
return 0 # Bogus return value (for unary minus)
|
|
|
|
def place_string(s, include_length=True):
|
|
"""Places a string aligned to 2 bytes.
|
|
|
|
Parameters
|
|
----------
|
|
s: str
|
|
The string to place.
|
|
include_length: bool
|
|
To also place the length before the string bytes.
|
|
"""
|
|
if include_length:
|
|
BYTE(len(s))
|
|
|
|
for c in s:
|
|
BYTE(ord(c))
|
|
ALIGN(2)
|
|
|
|
return 0 # Bogus return value (for unary minus)
|
|
|
|
def define_word(name, flags, label=None, xt="xt_DOCOL"):
|
|
"""Defines a new word.
|
|
|
|
Parameters
|
|
----------
|
|
name: str
|
|
The name of the word.
|
|
flags: int
|
|
The flags used to create the word.
|
|
label: str
|
|
The label used for the assembler.
|
|
xt: str
|
|
The location of the code to execute this word.
|
|
"""
|
|
if label is None:
|
|
label = name
|
|
header_label = "word_" + label
|
|
L(header_label)
|
|
reference(current_link[0])
|
|
current_link[0] = header_label
|
|
|
|
BYTE(flags + len(name))
|
|
place_string(name, include_length=False)
|
|
L(label)
|
|
reference(xt)
|
|
|
|
def define_constant(name, value, label=None):
|
|
"""Defines a new constant word.
|
|
|
|
Parameters
|
|
----------
|
|
name: str
|
|
The name of the word.
|
|
value: int
|
|
The constant value.
|
|
label: str
|
|
The label used for the assembler.
|
|
"""
|
|
define_word(name, 0, label=label, xt="xt_DOCON")
|
|
reference(value)
|
|
|
|
def resolve_font_address(c):
|
|
"""Resolves the font address.
|
|
|
|
Parameters
|
|
----------
|
|
c: byte
|
|
The character to resolve.
|
|
|
|
Returns
|
|
-------
|
|
Tha address of the font for the character.
|
|
"""
|
|
if ord(c) < 82:
|
|
return ADDR("font32up") + 5 * (ord(c) - 32)
|
|
else:
|
|
return ADDR("font82up") + 5 * (ord(c) - 82)
|
|
|
|
def extend_callback(gt1, ins):
|
|
"""To extend the ORG segments page by page.
|
|
|
|
Parameters
|
|
----------
|
|
gt1: list
|
|
The list of segments of the GT1 file.
|
|
ins: tuple
|
|
The tuple of bytes to append.
|
|
"""
|
|
segment = gt1[-1]
|
|
base = segment[0]
|
|
size = segment[1]
|
|
contents = segment[3]
|
|
if len(contents) + len(ins) > size or len(contents) >= size:
|
|
new_segment = (base + 256, size, {}, [])
|
|
gt1.append(new_segment)
|
|
|
|
def pack_segments_factory(base, size):
|
|
"""To resolve and pack multiple ORG segments.
|
|
|
|
Parameters
|
|
----------
|
|
base: int
|
|
The base address of the segments.
|
|
size: int
|
|
The size of each segment (bin).
|
|
|
|
Returns
|
|
-------
|
|
A callback to pack the segments (to be used by END()).
|
|
"""
|
|
def do_packing(gt1, symbols):
|
|
cur_base = base
|
|
orig_gt1 = list(gt1)
|
|
used_pages = set()
|
|
|
|
gt1.clear()
|
|
new_segments = [ ]
|
|
for segment in orig_gt1:
|
|
seg_base = segment[0]
|
|
if seg_base is None:
|
|
continue
|
|
|
|
gt1.append(segment)
|
|
used_pages.add(seg_base // 256)
|
|
|
|
for segment in orig_gt1:
|
|
if segment[0] is not None:
|
|
continue
|
|
|
|
seg_size = len(segment[3])
|
|
if seg_size > size:
|
|
raise RuntimeError("segment too large: %d bytes" %\
|
|
seg_size)
|
|
|
|
selected = None
|
|
for s in new_segments:
|
|
if s[1] - len(s[3]) >= seg_size:
|
|
selected = s
|
|
break
|
|
|
|
if selected is None:
|
|
cur_page = cur_base // 256
|
|
if cur_page in used_pages:
|
|
raise RuntimeError("page overlap at 0x%04X" % cur_base)
|
|
|
|
new_segments.append([cur_base, size, {}, []])
|
|
selected = new_segments[-1]
|
|
cur_base += 256
|
|
|
|
s = selected
|
|
d = len(s[3])
|
|
|
|
# Update the labels within this segment
|
|
labels_for_id = []
|
|
for lbl, offset in segment[2].items():
|
|
if not lbl.startswith("."):
|
|
labels_for_id.append(lbl)
|
|
s[2][lbl] = d + offset
|
|
|
|
if seg_size > 55:
|
|
print("Section %r is large (%d bytes, resolved to 0x%04X)" %\
|
|
(labels_for_id, seg_size, s[0] + d))
|
|
s[3].extend(segment[3])
|
|
|
|
total_bytes = 0
|
|
for s in new_segments:
|
|
total_bytes += len(s[3])
|
|
gt1.append(s)
|
|
|
|
fraction = float(total_bytes) / (len(new_segments) * size)
|
|
print("Packed %d bytes into %d segments of %d bytes each (%.1f%%)" % \
|
|
(total_bytes, len(new_segments), size, 100 * fraction))
|
|
|
|
# Update the symbols
|
|
for segment in gt1:
|
|
for lbl, offset in segment[2].items():
|
|
symbols[lbl] = segment[0] + offset
|
|
|
|
segment = gt1[-1]
|
|
return segment[0]
|
|
|
|
return do_packing
|
|
|
|
# ============================
|
|
# Initialization
|
|
# ============================
|
|
|
|
# Note: Code in this segment will be destroyed after initialization
|
|
# This code occupies the same region of memory for the data and return
|
|
# stacks.
|
|
ORG(init1_base, size=variable_base)
|
|
L("start")
|
|
- LDWI("main")
|
|
- STW(tmp1)
|
|
- CALL(tmp1)
|
|
|
|
# Data initialization
|
|
ORG(init1_base + rsp)
|
|
L("init_data")
|
|
- reference(rsp_top) # rsp
|
|
- reference(dsp_top) # dsp
|
|
- reference("cold_start") # pc
|
|
- reference("here_loc") # here
|
|
- reference(get_link()) # latest
|
|
- reference(10) # base
|
|
- reference(0) # state
|
|
- reference(0x7662) # scr_pos
|
|
- reference(0x0B00) # scr_color
|
|
|
|
ORG(init1_base + 0x80)
|
|
BYTE(1)
|
|
|
|
ORG(init1_base + auxiliary_base)
|
|
for name in auxiliary_functions:
|
|
reference(name)
|
|
L("init_data_end")
|
|
|
|
ORG(init2_base)
|
|
L("main")
|
|
|
|
# Shift the video to not display the code
|
|
- LDWI(videoTable+1)
|
|
- STW(tmp1)
|
|
- LDI(96)
|
|
- POKE(tmp1)
|
|
|
|
# Copy initialization data
|
|
- LDWI("init_data")
|
|
- STW(tmp1)
|
|
- ANDI(0xFF)
|
|
- STW(tmp3)
|
|
- LDWI("init_data_end")
|
|
- STW(tmp2)
|
|
|
|
L(".copy_init_loop")
|
|
- LDW(tmp1)
|
|
- PEEK()
|
|
- POKE(tmp3)
|
|
- INC(tmp1)
|
|
- INC(tmp3)
|
|
- LDW(tmp1)
|
|
- SUBW(tmp2)
|
|
- BNE(".copy_init_loop")
|
|
|
|
# Start the interpreter
|
|
- CALL(next_word_fn)
|
|
|
|
# ============================
|
|
# Auxiliary functions
|
|
# ============================
|
|
|
|
# Processes the next forth word.
|
|
ORG(None)
|
|
L("next_word")
|
|
- LDW(pc)
|
|
- DEEK()
|
|
- STW(w)
|
|
|
|
# Increment the pc by 2 and adjust address.
|
|
- LDI(pc)
|
|
- CALL(forward_fn)
|
|
|
|
- LDW(w)
|
|
|
|
# Starts execution at a given address (in vAC)
|
|
L("exec")
|
|
- STW(w)
|
|
- DEEK()
|
|
- STW(vLR)
|
|
- RET()
|
|
|
|
# Adjusts the address to be within the valid region of RAM.
|
|
# The address of the memory location containing the address
|
|
# to adjust is given in vAC.
|
|
# Note: vAC must always be even!
|
|
ORG(None)
|
|
L("adjust_addr")
|
|
- STW(adj1)
|
|
- ADDI(1)
|
|
- PEEK()
|
|
- SUBI(8)
|
|
- BLT(".adjust_skip")
|
|
- LDW(adj1)
|
|
- DEEK()
|
|
- BLT(".adjust_skip")
|
|
- ANDI(0xFF)
|
|
- SUBI(segment_size & 0xFF)
|
|
- BLT(".adjust_skip")
|
|
- POKE(adj1)
|
|
- INC(adj1) # Can increment here because adj1 is always even
|
|
- LDW(adj1)
|
|
- PEEK()
|
|
- ADDI(1)
|
|
- POKE(adj1)
|
|
L(".adjust_skip")
|
|
- RET()
|
|
|
|
# Moves forward by two bytes and adjust the address.
|
|
# The input of this function is given in vAC (same as for
|
|
# adjust_addr).
|
|
ORG(None)
|
|
L("forward")
|
|
- PUSH()
|
|
- STW(adj1)
|
|
- DEEK()
|
|
- ADDI(2)
|
|
L(".forward_writeback")
|
|
- DOKE(adj1)
|
|
- LDW(adj1)
|
|
- CALL(adjust_addr_fn)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Moves forward by one byte and adjust the address.
|
|
# The input of this function is given in vAC (same as for
|
|
# adjust_addr).
|
|
L("cforward")
|
|
- PUSH()
|
|
- STW(adj1)
|
|
- DEEK()
|
|
- ADDI(1)
|
|
- BRA(".forward_writeback")
|
|
|
|
# Alignes an address to a multiple of two and
|
|
# then adjusts the address.
|
|
ORG(None)
|
|
L("align_addr")
|
|
- PUSH()
|
|
- STW(adj1)
|
|
- DEEK()
|
|
- ANDI(1)
|
|
- BEQ(".align_skip")
|
|
- LDW(adj1)
|
|
- DEEK()
|
|
- ADDI(1)
|
|
- DOKE(adj1)
|
|
L(".align_skip")
|
|
- LDW(adj1)
|
|
- CALL(adjust_addr_fn)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Pushes a value on the data stack.
|
|
ORG(None)
|
|
L("push_data")
|
|
- DOKE(dsp)
|
|
- LD(dsp)
|
|
- SUBI(2)
|
|
- ST(dsp)
|
|
- RET()
|
|
|
|
# Pops a value from the data stack.
|
|
ORG(None)
|
|
L("pop_data")
|
|
- INC(dsp) # Can increment because dsp stays in the same page
|
|
- INC(dsp)
|
|
- LDW(dsp)
|
|
- DEEK()
|
|
- RET()
|
|
|
|
# Pushes a value on the return stack.
|
|
ORG(None)
|
|
L("push_return")
|
|
- DOKE(rsp)
|
|
- LD(rsp)
|
|
- SUBI(2)
|
|
- ST(rsp)
|
|
- RET()
|
|
|
|
# Pops a value from the return stack.
|
|
ORG(None)
|
|
L("pop_return")
|
|
- INC(rsp)
|
|
- INC(rsp)
|
|
- LDW(rsp)
|
|
- DEEK()
|
|
- RET()
|
|
|
|
# To multiply two numbers.
|
|
# The inputs are given in md1 and md2.
|
|
# The result is in md3.
|
|
# Uses tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("multiply")
|
|
- LDI(0)
|
|
- STW(md3)
|
|
- LDI(1)
|
|
L(".multiply_loop")
|
|
- STW(tmp5)
|
|
- ANDW(md2)
|
|
- BEQ(".multiply_continue")
|
|
- LDW(md1)
|
|
- ADDW(md3)
|
|
- STW(md3)
|
|
L(".multiply_continue")
|
|
- LDW(md1)
|
|
- LSLW()
|
|
- STW(md1)
|
|
- LDW(tmp5)
|
|
- LSLW()
|
|
- BNE(".multiply_loop")
|
|
- RET()
|
|
|
|
# To divide two (unsigned) numbers.
|
|
# The inputs are given in md1 and md2 (dividend and divisor).
|
|
# The results are in md3 (quotient) and md1 (remainder).
|
|
# Note: if the divisor is zero, the quotient will be md1
|
|
# and the remainder will also be md1.
|
|
# Uses tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("udivide")
|
|
- PUSH()
|
|
- LDW(md1)
|
|
- STW(md3)
|
|
- LDW(md2)
|
|
- BEQ(".udivide_end")
|
|
- LDI(0)
|
|
- STW(md1)
|
|
L(".udivide_loop")
|
|
- STW(tmp5)
|
|
- LDW(md1)
|
|
- LSLW()
|
|
- STW(md1)
|
|
- LDW(md3)
|
|
- BGE(".udivide_no_increment1")
|
|
- INC(md1)
|
|
L(".udivide_no_increment1")
|
|
- LSLW()
|
|
- STW(md3)
|
|
- CALL(unsigned_cmp_fn)
|
|
- BLT(".udivide_no_increment2")
|
|
- LDW(md1)
|
|
- SUBW(md2)
|
|
- STW(md1)
|
|
- INC(md3)
|
|
L(".udivide_no_increment2")
|
|
- LDW(tmp5)
|
|
- ADDI(1)
|
|
- ANDI(15)
|
|
- BNE(".udivide_loop")
|
|
L(".udivide_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# To divide two (signed) numbers.
|
|
# The inputs are given in md1 and md2 (dividend and divisor).
|
|
# The results are in md3 (quotient) and md1 (remainder).
|
|
# Note: if the divisor is zero, the quotient will be md1
|
|
# and the remainder will also be md1.
|
|
# Uses tmp4 and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("divide")
|
|
- PUSH()
|
|
- LDI(0)
|
|
- SUBW(md1)
|
|
- BGT(".divide_neg1")
|
|
- CALL(divide_aux_fn)
|
|
- BRA(".divide_end")
|
|
L(".divide_neg1")
|
|
- STW(md1)
|
|
- CALL(divide_aux_fn)
|
|
- LDI(0)
|
|
- SUBW(md3)
|
|
- STW(md3)
|
|
- LDW(md1)
|
|
- BEQ(".divide_end")
|
|
- LDW(md2)
|
|
- SUBW(md1)
|
|
- STW(md1)
|
|
- LDW(tmp4)
|
|
- BLT(".divide_neg2")
|
|
- LDWI(-1)
|
|
- BRA(".divide_continue")
|
|
L(".divide_neg2")
|
|
- LDI(1)
|
|
L(".divide_continue")
|
|
- ADDW(md3)
|
|
- STW(md3)
|
|
L(".divide_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Auxiliary function divide two (signed) numbers.
|
|
# The inputs are given in md1 and md2 (dividend and divisor).
|
|
# The results are in md3 (quotient) and md1 (remainder).
|
|
# Note: This function assumes that md1 is non-negative and
|
|
# on the return, md2 is possibly changed in sign so that
|
|
# it is positive. In addition tmp4 keeps the original value
|
|
# of md2.
|
|
# Note: if the divisor is zero, the quotient will be md1
|
|
# and the remainder will also be md1.
|
|
# Uses tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("divide_aux")
|
|
- PUSH()
|
|
- LDW(md2)
|
|
- STW(tmp4)
|
|
- LDI(0)
|
|
- SUBW(md2)
|
|
- BLT(".divide_aux_no_adjust")
|
|
- STW(md2)
|
|
L(".divide_aux_no_adjust")
|
|
- CALL(udivide_fn)
|
|
- LDW(tmp4)
|
|
- BGE(".divide_aux_end")
|
|
- LDI(0)
|
|
- SUBW(md3)
|
|
- STW(md3)
|
|
L(".divide_aux_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
|
|
# Auxiliary function to compare md1 and md2
|
|
# as unsigned numbers.
|
|
# It returns a negative number if md1 < md2 (unsigned),
|
|
# a positive number if md1 > m2 (unsigned) and zero
|
|
# if md1 = md2.
|
|
ORG(None)
|
|
L("unsigned_cmp")
|
|
- LDW(md1)
|
|
- XORW(md2)
|
|
- BLT(".unsigned_cmp_diff")
|
|
- LDW(md1)
|
|
- SUBW(md2)
|
|
- RET()
|
|
L(".unsigned_cmp_diff")
|
|
- LDW(md2)
|
|
- ORI(1)
|
|
- RET()
|
|
|
|
# Auxiliary function to compare md1 and md2
|
|
# as signed numbers.
|
|
# It returns a negative number if md1 < md2 (signed),
|
|
# a positive number if md1 > md2 (signed), and zero
|
|
# if md1 = md2.
|
|
ORG(None)
|
|
L("signed_cmp")
|
|
- LDW(md1)
|
|
- XORW(md2)
|
|
- BLT(".signed_cmp_diff")
|
|
- LDW(md1)
|
|
- SUBW(md2)
|
|
- RET()
|
|
L(".signed_cmp_diff")
|
|
- LDW(md1)
|
|
- ORI(1)
|
|
- RET()
|
|
|
|
# Setup the register for invoking the SYS_VDrawBits_134 function.
|
|
ORG(None)
|
|
L("setup_draw")
|
|
- LDWI(SYS_VDrawBits_134)
|
|
- STW(sysFn)
|
|
- LDW(scr_color)
|
|
- STW(sysArgs0)
|
|
- RET()
|
|
|
|
# Prints a character onto the screen.
|
|
# The charater is in register vAC.
|
|
# It returns True if the character is printable or newline.
|
|
# This functions uses tmp3, tmp4, and tmp5 for temporary storage.
|
|
ORG(None)
|
|
L("emit")
|
|
- PUSH()
|
|
- STW(tmp5)
|
|
|
|
# Handle newline (line-feed)
|
|
- XORI(ord('\n'))
|
|
- BNE(".emit_no_newline")
|
|
# Move position to the new of the line
|
|
- LDI(0xF8)
|
|
- ST(scr_pos)
|
|
- BRA(".emit_update_pos")
|
|
L(".emit_no_newline")
|
|
- LDW(tmp5)
|
|
- CALL(select_font_fn)
|
|
- BEQ(".emit_end")
|
|
- LDW(scr_pos)
|
|
- STW(sysArgs4)
|
|
- CALL(draw_char_fn)
|
|
L(".emit_update_pos")
|
|
- CALL(update_pos_fn)
|
|
- CALL(emit_scroll_fn)
|
|
- LDI(1)
|
|
L(".emit_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Selects the font and returns True if it is a printable character.
|
|
# The charater is in vAC.
|
|
# The address of the font is stored in tmp5 on return.
|
|
# It uses tmp4 and tmp5 for temporary storage.
|
|
ORG(None)
|
|
L("select_font")
|
|
- SUBI(82)
|
|
- BLT(".select_font_32")
|
|
- STW(tmp5)
|
|
- SUBI(50)
|
|
- BGE(".select_font_end") # Skip unprintable characters
|
|
- LDWI("font82up")
|
|
- BRA(".select_font_continue")
|
|
L(".select_font_32")
|
|
- ADDI(50)
|
|
- BLT(".select_font_end")
|
|
- STW(tmp5)
|
|
- LDWI("font32up")
|
|
L(".select_font_continue")
|
|
- STW(tmp4)
|
|
- LDW(tmp5)
|
|
- LSLW()
|
|
- LSLW()
|
|
- ADDW(tmp5)
|
|
- ADDW(tmp4)
|
|
- STW(tmp5)
|
|
- LDI(1)
|
|
- RET()
|
|
L(".select_font_end")
|
|
- LDI(0)
|
|
- RET()
|
|
|
|
# Draws a character to screen.
|
|
# The address of the character is in tmp5.
|
|
# It uses tmp4 and tmp5 for temporary storage.
|
|
ORG(None)
|
|
L("draw_char")
|
|
- LDI(5)
|
|
L(".draw_char_loop")
|
|
- STW(tmp4)
|
|
- LDW(tmp5)
|
|
- LUP(0)
|
|
- ST(sysArgs2)
|
|
- SYS(134) # Call SYS_VDrawBits_134
|
|
- INC(tmp5)
|
|
- INC(sysArgs4)
|
|
- LDW(tmp4)
|
|
- SUBI(1)
|
|
- BGT(".draw_char_loop")
|
|
- RET()
|
|
|
|
# Updates scr_pos.
|
|
ORG(None)
|
|
L("update_pos")
|
|
- LDW(scr_pos)
|
|
- ADDI(6)
|
|
- STW(scr_pos)
|
|
- LD(scr_pos)
|
|
- SUBI(0xFE)
|
|
- BLT(".update_pos_end")
|
|
- LDWI(0x0964)
|
|
- ADDW(scr_pos)
|
|
- BGE(".update_pos_continue")
|
|
- LDWI(0x0862)
|
|
L(".update_pos_continue")
|
|
- STW(scr_pos)
|
|
L(".update_pos_end")
|
|
- RET()
|
|
|
|
# Checks if there is need to scroll.
|
|
# It uses tmp3, tmp4, and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("emit_scroll")
|
|
- PUSH()
|
|
# Check if started a new line
|
|
- LD(scr_pos)
|
|
- SUBI(0x62)
|
|
- BGT(".emit_scroll_end")
|
|
- LDW(scr_pos)
|
|
# Recall that there is a border of 2 pixels in scr_pos
|
|
- SUBI(2)
|
|
- CALL(clearline_fn)
|
|
# Check if would wrap around the screen
|
|
- LDWI(videoTable)
|
|
- PEEK()
|
|
- STW(tmp5)
|
|
- LDI(scr_pos + 1)
|
|
- PEEK()
|
|
- XORW(tmp5)
|
|
- BNE(".emit_scroll_end")
|
|
# Scroll by 10 lines to avoid the wrap around
|
|
- LDI(10)
|
|
- CALL(scroll_fn)
|
|
L(".emit_scroll_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Clears a line (for newline).
|
|
# This means that the screen is cleared for the current line.
|
|
# It uses tmp3 and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("clearline")
|
|
- PUSH()
|
|
- STW(sysArgs4)
|
|
- LDI(32)
|
|
L(".clearline_loop")
|
|
- STW(tmp3)
|
|
- LDWI(resolve_font_address(' '))
|
|
- STW(tmp5)
|
|
- CALL(draw_char_fn)
|
|
- LDW(tmp3)
|
|
- SUBI(1)
|
|
- BNE(".clearline_loop")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Code to implement scrolling.
|
|
# It uses tmp3, tmp4, and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("scroll")
|
|
- STW(tmp5) # The amount to scroll
|
|
- LDWI(120)
|
|
- STW(tmp3)
|
|
- LDWI(videoTable)
|
|
- STW(tmp4)
|
|
L(".scroll_loop")
|
|
- LDW(tmp4)
|
|
- PEEK()
|
|
- ADDW(tmp5)
|
|
- SUBI(8)
|
|
- BLT(".scroll_1")
|
|
- SUBI(120)
|
|
- BGE(".scroll_2")
|
|
L(".scroll_1")
|
|
- ADDI(120)
|
|
L(".scroll_2")
|
|
- ADDI(8)
|
|
- POKE(tmp4)
|
|
- LDW(tmp4)
|
|
- ADDI(2)
|
|
- STW(tmp4)
|
|
- LDW(tmp3)
|
|
- SUBI(1)
|
|
- STW(tmp3)
|
|
- BNE(".scroll_loop")
|
|
- RET()
|
|
|
|
# Code to implement reading from the keyboard.
|
|
# Uses tmp3, tmp4, and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("read")
|
|
- PUSH()
|
|
|
|
L(".read_loop1")
|
|
- LD(serialRaw)
|
|
- STW(tmp3)
|
|
- LD(frameCount) # Use the frameCount to implement the cursor
|
|
- STW(tmp4)
|
|
|
|
L(".read_loop2")
|
|
- LD(serialRaw)
|
|
- XORW(tmp3)
|
|
- BNE(".read_key")
|
|
- LD(frameCount)
|
|
- XORW(tmp4)
|
|
- BEQ(".read_loop2")
|
|
|
|
# Show the cursor
|
|
- LD(frameCount)
|
|
- ANDI(8)
|
|
- CALL(show_cursor_fn)
|
|
- LD(frameCount)
|
|
- STW(tmp4)
|
|
- BRA(".read_loop2")
|
|
|
|
L(".read_key")
|
|
- LD(serialRaw)
|
|
- CALL(process_key_fn)
|
|
- BNE(".read_loop1")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Shows the blinking cursor.
|
|
# If vAC is zero, shows a space, otherwise shows a full square.
|
|
# Uses tmp4 and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("show_cursor")
|
|
- PUSH()
|
|
- BEQ(".show_cursor_space")
|
|
- LDWI(resolve_font_address(chr(127)))
|
|
- BRA(".show_cursor_continue")
|
|
L(".show_cursor_space")
|
|
- LDWI(resolve_font_address(' '))
|
|
L(".show_cursor_continue")
|
|
- STW(tmp5)
|
|
- LDW(scr_pos)
|
|
- STW(sysArgs4)
|
|
- CALL(draw_char_fn)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Processes a key input from the keyboard.
|
|
# Uses tmp3, tmp4, and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("process_key")
|
|
- PUSH()
|
|
- STW(tmp5)
|
|
|
|
- LDI(ord('\n')) # Check for newlines
|
|
- XORW(tmp5)
|
|
- BNE(".process_key_continue")
|
|
- LDI(0)
|
|
- CALL(show_cursor_fn)
|
|
- LDI(0)
|
|
- BRA(".process_key_end")
|
|
|
|
L(".process_key_continue")
|
|
- LDI(127) # Now check for backspaces
|
|
- XORW(tmp5)
|
|
- BNE(".process_key_regular")
|
|
- CALL(process_bs_fn)
|
|
- BRA(".process_key_return_true")
|
|
|
|
L(".process_key_regular")
|
|
- LD(buf)
|
|
- SUBI(buf_size) # Check if the buffer is full
|
|
- BEQ(".process_key_return_true")
|
|
|
|
- LDW(tmp5)
|
|
- POKE(buf)
|
|
- CALL(emit_fn)
|
|
- BEQ(".process_key_return_true")
|
|
- INC(buf)
|
|
L(".process_key_return_true")
|
|
- LDI(1)
|
|
L(".process_key_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Processes the backspace key.
|
|
# Uses tmp4 and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("process_bs")
|
|
- PUSH()
|
|
- LD(scr_pos)
|
|
- SUBI(0x62)
|
|
- BLE(".process_bs_end")
|
|
- LD(buf)
|
|
- BEQ(".process_bs_end")
|
|
- LDI(0)
|
|
- CALL(show_cursor_fn)
|
|
- LDW(scr_pos)
|
|
- SUBI(6)
|
|
- STW(scr_pos)
|
|
- LDI(0)
|
|
- CALL(show_cursor_fn)
|
|
- LD(buf)
|
|
- SUBI(1)
|
|
- ST(buf)
|
|
L(".process_bs_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Processes a word.
|
|
# Input: vAC -> the character delimiter
|
|
# Uses tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("parse_word")
|
|
- PUSH()
|
|
- STW(tmp5)
|
|
- LDI(0)
|
|
- STW(wlen)
|
|
L(".parse_word_loop1")
|
|
- LDW(inp)
|
|
- XORW(buf)
|
|
- BEQ(".parse_word_end")
|
|
- LDW(inp)
|
|
- INC(inp)
|
|
- STW(wbuf)
|
|
- PEEK()
|
|
- XORW(tmp5)
|
|
- BEQ(".parse_word_loop1")
|
|
- INC(wlen)
|
|
L(".parse_word_loop2")
|
|
- LDW(inp)
|
|
- XORW(buf)
|
|
- BEQ(".parse_word_end")
|
|
- LDW(inp)
|
|
- PEEK()
|
|
- XORW(tmp5)
|
|
- INC(inp)
|
|
- BEQ(".parse_word_end")
|
|
- INC(wlen)
|
|
- BRA(".parse_word_loop2")
|
|
L(".parse_word_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# To parse a number.
|
|
# Input: str1 is the length of the string, and
|
|
# str2 is the pointer to the string.
|
|
# Output: tmp1 is the parsed number, and vAC
|
|
# indicates whether or not the parsing succeeded
|
|
# (non-positive number means error).
|
|
# This function uses tmp2, tmp3, tmp4 and tmp5 as
|
|
# temporary storage.
|
|
ORG(None)
|
|
L("number")
|
|
- PUSH()
|
|
- LDW(str2)
|
|
- STW(tmp3)
|
|
- LDW(str1)
|
|
- STW(tmp2)
|
|
- BEQ(".number_end")
|
|
- LDW(tmp3)
|
|
- PEEK()
|
|
- XORI(ord('-'))
|
|
- BEQ(".number_negative")
|
|
- CALL(unumber_fn)
|
|
L(".number_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
L(".number_negative")
|
|
- LDW(tmp2)
|
|
- SUBI(1)
|
|
- STW(tmp2)
|
|
- LDI(tmp3)
|
|
- CALL(cforward_fn)
|
|
- CALL(unumber_fn)
|
|
- STW(tmp2)
|
|
- LDI(0)
|
|
- SUBW(tmp1)
|
|
- STW(tmp1)
|
|
- LDW(tmp2)
|
|
- BRA(".number_end")
|
|
|
|
|
|
# To parse an unsigned number.
|
|
# Input: tmp2 is the length of the string, and
|
|
# tmp3 is the pointer to the string.
|
|
# Output: tmp1 is the parsed number, and vAC
|
|
# indicates whether or not the parsing succeeded
|
|
# (non-positive number means error).
|
|
# This function uses tmp4 and tmp5 as temporary storage.
|
|
ORG(None)
|
|
L("unumber")
|
|
- PUSH()
|
|
- LDI(0)
|
|
- STW(tmp1)
|
|
- LDW(base)
|
|
- STW(md2)
|
|
- LDW(tmp2)
|
|
- BEQ(".unumber_end")
|
|
L(".unumber_loop")
|
|
- LDW(tmp3)
|
|
- PEEK()
|
|
- STW(tmp4)
|
|
- LDI(tmp3)
|
|
- CALL(cforward_fn)
|
|
- CALL(digit_fn)
|
|
- BLE(".unumber_end")
|
|
- LDW(tmp1)
|
|
- STW(md1)
|
|
- CALL(multiply_fn)
|
|
- LDW(md3)
|
|
- ADDW(tmp4)
|
|
- STW(tmp1)
|
|
- LDW(tmp2)
|
|
- SUBI(1)
|
|
- STW(tmp2)
|
|
- BNE(".unumber_loop")
|
|
- LDI(1)
|
|
L(".unumber_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# To parse a single digit.
|
|
# The input digit is in tmp4 and the output is returned in tmp4 as well.
|
|
# On success this function should return a positive number in vAC.
|
|
ORG(None)
|
|
L("digit")
|
|
- LDW(tmp4)
|
|
- SUBI(ord('0'))
|
|
- BLT(".digit_error")
|
|
- SUBI(ord('9') - ord('0'))
|
|
- BGT(".digit_letter")
|
|
- ADDI(ord('9') - ord('0'))
|
|
- STW(tmp4)
|
|
- BRA(".digit_check")
|
|
|
|
L(".digit_letter")
|
|
- LDW(tmp4)
|
|
- SUBI(ord('A'))
|
|
- BLT(".digit_error")
|
|
- LDI(ord('Z'))
|
|
- SUBW(tmp4)
|
|
- BLT(".digit_error")
|
|
- LDW(tmp4)
|
|
- ADDI(10 - ord('A'))
|
|
- STW(tmp4)
|
|
|
|
L(".digit_check")
|
|
- LDW(base)
|
|
- SUBW(tmp4)
|
|
L(".digit_error")
|
|
- RET()
|
|
|
|
# To copy one memory region to another.
|
|
# Input: tmp1 -> number of bytes to copy;
|
|
# tmp2 -> source address;
|
|
# tmp3 -> destination address;
|
|
ORG(None)
|
|
L("memcpy")
|
|
- PUSH()
|
|
L(".memcpy_loop")
|
|
- LDW(tmp1)
|
|
- BEQ(".memcpy_end")
|
|
- SUBI(1)
|
|
- STW(tmp1)
|
|
- LDW(tmp2)
|
|
- PEEK()
|
|
- POKE(tmp3)
|
|
- LDI(tmp2)
|
|
- CALL(cforward_fn)
|
|
- LDI(tmp3)
|
|
- CALL(cforward_fn)
|
|
- BRA(".memcpy_loop")
|
|
L(".memcpy_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# To compare two strings.
|
|
# Input: tmp1 -> length of the first string;
|
|
# tmp2 -> address of the first string;
|
|
# tmp3 -> length of the second string;
|
|
# tmp4 -> address of the second string;
|
|
# Return: Zero if the strings are equal, non-zero otherwise.
|
|
ORG(None)
|
|
L("streq")
|
|
- PUSH()
|
|
- LDW(tmp1)
|
|
- SUBW(tmp3)
|
|
- BNE(".streq_end")
|
|
L(".streq_loop")
|
|
- LDW(tmp1)
|
|
- BEQ(".streq_end")
|
|
- SUBI(1)
|
|
- STW(tmp1)
|
|
- LDW(tmp2)
|
|
- PEEK()
|
|
- STW(tmp3)
|
|
- LDW(tmp4)
|
|
- PEEK()
|
|
- SUBW(tmp3)
|
|
- BNE(".streq_end")
|
|
- LDI(tmp2)
|
|
- CALL(cforward_fn)
|
|
- LDI(tmp4)
|
|
- CALL(cforward_fn)
|
|
- BRA(".streq_loop")
|
|
L(".streq_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# To find one word in the dictionary.
|
|
# Input: str1 -> length of the word to search;
|
|
# str2 -> address of the string to search;
|
|
# Output: vAC contains the address of the word in the dictionary.
|
|
# This function uses tmp1, tmp2, tmp3, tmp4, and tmp5
|
|
# as temporary storage.
|
|
ORG(None)
|
|
L("find")
|
|
- PUSH()
|
|
- LDW(latest)
|
|
L(".find_loop")
|
|
- STW(tmp5)
|
|
- BEQ(".find_end")
|
|
- CALL(match_fn)
|
|
- BEQ(".find_end")
|
|
- LDW(tmp5)
|
|
- DEEK()
|
|
- BRA(".find_loop")
|
|
L(".find_end")
|
|
- LDW(tmp5)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Auxiliary function to find one word in the dictionary.
|
|
# Input: str1 -> length of the word to search;
|
|
# str2 -> address of the string to search;
|
|
# vAC -> address of the current word;
|
|
# Output: vAC is zero if the word was found.
|
|
# This function uses tmp1, tmp2, tmp3 and tmp4 as temporary storage.
|
|
ORG(None)
|
|
L("match")
|
|
- PUSH()
|
|
- STW(tmp4)
|
|
- LDI(tmp4)
|
|
- CALL(forward_fn)
|
|
- LDW(tmp4)
|
|
- PEEK()
|
|
- ANDI(F_HIDD)
|
|
- BNE(".match_end")
|
|
- LDW(tmp4)
|
|
- INC(tmp4) # There is no need to adjust as tmp4 is even
|
|
- PEEK()
|
|
- ANDI(F_MASK)
|
|
- STW(tmp3)
|
|
- LDW(str1)
|
|
- STW(tmp1)
|
|
- LDW(str2)
|
|
- STW(tmp2)
|
|
- CALL(streq_fn)
|
|
L(".match_end")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Auxiliary function to extract the flags of a word in dictionary.
|
|
# Input: tmp1 -> address of the word in dictionary
|
|
# Output: vAC -> the flags (including the length)
|
|
# tmp1 -> the address of the flags byte.
|
|
ORG(None)
|
|
L("flags")
|
|
- PUSH()
|
|
- LDI(tmp1)
|
|
- CALL(forward_fn)
|
|
- LDW(tmp1)
|
|
- PEEK()
|
|
- POP()
|
|
- RET()
|
|
|
|
# Auxiliary function to extract the flags of a word in dictionary.
|
|
# Input: tmp1 -> address of the flags byte of the word in dictionary.
|
|
# Output: tmp1 -> the result;
|
|
# tmp2 -> the length of the name;
|
|
# tmp3 -> the pointer to the name string;
|
|
# Note: one should call "flags" before calling this function.
|
|
ORG(None)
|
|
L("cfa")
|
|
- PUSH()
|
|
- LDW(tmp1)
|
|
- INC(tmp1) # tmp1 is even here
|
|
- PEEK()
|
|
- ANDI(F_MASK)
|
|
- STW(tmp2)
|
|
- LDW(tmp1)
|
|
- STW(tmp3)
|
|
- ADDW(tmp2)
|
|
- STW(tmp1)
|
|
- LDI(tmp1)
|
|
- CALL(align_addr_fn)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Compiles one word.
|
|
# Input: vAC -> word to compile.
|
|
ORG(None)
|
|
L("comma")
|
|
- PUSH()
|
|
- DOKE(here)
|
|
- LDI(here)
|
|
- CALL(forward_fn)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Compiles one string.
|
|
# Input: tmp1 -> length of string;
|
|
# tmp2 -> address of string;
|
|
# Uses tmp3 as temporary variable.
|
|
ORG(None)
|
|
L("str_comma")
|
|
- PUSH()
|
|
- LDW(tmp1)
|
|
- POKE(here)
|
|
- INC(here)
|
|
- LDW(here)
|
|
- STW(tmp3)
|
|
- CALL(memcpy_fn)
|
|
- LDW(tmp3)
|
|
- STW(here)
|
|
- LDI(here)
|
|
- CALL(align_addr_fn)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Converts one number to string.
|
|
# Input: tmp1 -> number to be converted;
|
|
# tmp2 -> address of the buffer to write the number;
|
|
ORG(None)
|
|
L("cv_number")
|
|
- PUSH()
|
|
- LDI(0)
|
|
- SUBW(tmp1)
|
|
- BGT(".cv_number_negative")
|
|
- CALL(cv_unumber_fn)
|
|
- POP()
|
|
- RET()
|
|
L(".cv_number_negative")
|
|
- STW(tmp1)
|
|
- CALL(cv_unumber_fn)
|
|
- LDI(ord('-'))
|
|
- POKE(tmp2)
|
|
- LDW(tmp2)
|
|
- SUBI(1)
|
|
- STW(tmp2)
|
|
- POP()
|
|
- RET()
|
|
|
|
# Converts one unsigned number to string.
|
|
# Input: tmp1 -> number to be converted;
|
|
# tmp2 -> address of the buffer to write the number;
|
|
# Uses tmp5 as temporary storage;
|
|
ORG(None)
|
|
L("cv_unumber")
|
|
- PUSH()
|
|
- LDW(base)
|
|
- STW(md2)
|
|
- LDW(tmp1)
|
|
- STW(md3)
|
|
- BEQ(".cv_unumber_write")
|
|
L(".cv_unumber_loop")
|
|
- STW(md1)
|
|
- CALL(udivide_fn)
|
|
- LDW(md1)
|
|
L(".cv_unumber_write")
|
|
- CALL(cv_digit_fn)
|
|
- POKE(tmp2)
|
|
- LDW(tmp2)
|
|
- SUBI(1)
|
|
- STW(tmp2)
|
|
- LDW(md3)
|
|
- BNE(".cv_unumber_loop")
|
|
- POP()
|
|
- RET()
|
|
|
|
# Converts one digit to string.
|
|
# Input: vAC -> digit to be convert;
|
|
# Output: vAC -> the ascii code of the digit;
|
|
ORG(None)
|
|
L("cv_digit")
|
|
- SUBI(10)
|
|
- BGE(".cv_digit_letter")
|
|
- ADDI(ord('0') + 10)
|
|
- RET()
|
|
L(".cv_digit_letter")
|
|
- ADDI(ord('A'))
|
|
- RET()
|
|
|
|
# ============================
|
|
# Code of the words
|
|
# ============================
|
|
|
|
# Preamble of colon definitions.
|
|
# ( -- )
|
|
ORG(None)
|
|
L("xt_DOCOL")
|
|
- LDW(pc)
|
|
- CALL(push_return_fn)
|
|
- LDW(w) # Work register contains the pointer to the current word.
|
|
- STW(pc)
|
|
- LDI(pc)
|
|
- CALL(forward_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Code used to implement constants.
|
|
# ( -- n )
|
|
ORG(None)
|
|
L("xt_DOCON")
|
|
- LDI(w)
|
|
- CALL(forward_fn)
|
|
- LDW(w)
|
|
- DEEK() # Get the literal value
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Code for adjusting addresses.
|
|
# ( a -- a' )
|
|
ORG(None)
|
|
L("xt_ADJ")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- LDI(tmp1)
|
|
- CALL(adjust_addr_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Code for aligning addresses.
|
|
# ( a -- a' )
|
|
ORG(None)
|
|
L("xt_ALIGN")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- LDI(tmp1)
|
|
- CALL(align_addr_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Code for executing an instruction on the data stack.
|
|
# ( w -- )
|
|
ORG(None)
|
|
L("xt_EXECUTE")
|
|
- CALL(pop_data_fn)
|
|
- CALL(exec_fn)
|
|
|
|
# Code for the BRANCH instruction.
|
|
# The next cell contains a relative address of the branch.
|
|
# ( -- )
|
|
ORG(None)
|
|
L("xt_BRANCH")
|
|
- LDW(pc)
|
|
- DEEK() # Value in the slot after branch is the offset for the PC
|
|
- STW(pc)
|
|
- CALL(next_word_fn)
|
|
|
|
# Code for the 0BRANCH instruction (used to implement IF word).
|
|
# This will execute the branch if the number on top of the data
|
|
# stack is zero.
|
|
# ( n -- )
|
|
L("xt_0BRANCH")
|
|
- CALL(pop_data_fn)
|
|
- BEQ("xt_BRANCH")
|
|
- LDI(pc)
|
|
- CALL(forward_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Code to return from the current word.
|
|
# ( -- )
|
|
ORG(None)
|
|
L("xt_EXIT")
|
|
- CALL(pop_return_fn)
|
|
- STW(pc) # Pop pc from return stack
|
|
- CALL(next_word_fn)
|
|
|
|
# Pushes the literal in the next cell to the top of
|
|
# the data stack.
|
|
# ( -- n )
|
|
ORG(None)
|
|
L("xt_LIT")
|
|
- LDW(pc)
|
|
- DEEK() # Get the literal value
|
|
- CALL(push_data_fn)
|
|
- LDI(pc)
|
|
- CALL(forward_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Pushes the number of bytes and the address of the
|
|
# string onto the data stack.
|
|
# ( -- l a )
|
|
ORG(None)
|
|
L("xt_LITSTR")
|
|
- LDW(pc)
|
|
- PEEK() # Get the length
|
|
- STW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- INC(pc) # Can increment here because pc is even
|
|
- LDW(pc)
|
|
- CALL(push_data_fn)
|
|
- LDI(tmp1)
|
|
- CALL(adjust_addr_fn)
|
|
- LDW(pc)
|
|
- ADDW(tmp1)
|
|
- STW(pc)
|
|
- LDI(pc)
|
|
- CALL(align_addr_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Drops the current value in the data stack.
|
|
# ( n -- )
|
|
ORG(None)
|
|
L("xt_DROP")
|
|
- CALL(pop_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Drops the last two values in the data stack.
|
|
# ( n2 n1 -- )
|
|
ORG(None)
|
|
L("xt_2DROP")
|
|
- CALL(pop_data_fn)
|
|
- CALL(pop_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Swaps the last two values in the data stack.
|
|
# ( n2 n1 -- n1 n2 )
|
|
ORG(None)
|
|
L("xt_SWAP")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Duplicates the value in the (data) stack.
|
|
# ( n1 -- n1 n1 )
|
|
ORG(None)
|
|
L("xt_DUP")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Duplicates the last two values in the (data) stack.
|
|
# ( n2 n1 -- n2 n1 n2 n1 )
|
|
ORG(None)
|
|
L("xt_2DUP")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Duplicates the value in the (data) stack if the
|
|
# value is non-zero
|
|
# ( n1 -- n1 | n1 n1 )
|
|
ORG(None)
|
|
L("xt_?DUP")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp1)
|
|
- BEQ(".xt_?DUP_end")
|
|
- CALL(push_data_fn)
|
|
L(".xt_?DUP_end")
|
|
- CALL(next_word_fn)
|
|
|
|
# Pushes the 2nd element from the top to the data stack.
|
|
# ( n2 n1 -- n2 n1 n2 )
|
|
ORG(None)
|
|
L("xt_OVER")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Rotates the last 3 elements of the stack.
|
|
# ( n3 n2 n1 -- n2 n1 n3 )
|
|
ORG(None)
|
|
L("xt_ROT")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp3)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp3)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Rotates the last 3 elements of the stack in reverse order.
|
|
# ( n3 n2 n1 -- n1 n3 n2 )
|
|
ORG(None)
|
|
L("xt_RROT")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp3)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp3)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Transfers a value from the return stack to the data stack.
|
|
# ( -- r )
|
|
ORG(None)
|
|
L("xt_R>")
|
|
- CALL(pop_return_fn)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Transfers a value from the return stack to the data stack.
|
|
# ( r -- )
|
|
ORG(None)
|
|
L("xt_>R")
|
|
- CALL(pop_data_fn)
|
|
- CALL(push_return_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Gets the value of the (data) stack pointer (before the push).
|
|
# ( -- dsp )
|
|
ORG(None)
|
|
L("xt_DSP@")
|
|
- LDW(dsp)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Sets the value of the (data) stack pointer.
|
|
# ( dsp -- )
|
|
ORG(None)
|
|
L("xt_DSP!")
|
|
- CALL(pop_data_fn)
|
|
- STW(dsp)
|
|
- CALL(next_word_fn)
|
|
|
|
# Gets the value of the (return) stack pointer.
|
|
# ( -- rsp )
|
|
ORG(None)
|
|
L("xt_RSP@")
|
|
- LDW(rsp)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Sets the value of the (return) stack pointer.
|
|
# ( rsp -- )
|
|
ORG(None)
|
|
L("xt_RSP!")
|
|
- CALL(pop_data_fn)
|
|
- STW(rsp)
|
|
- CALL(next_word_fn)
|
|
|
|
# Stores a value into memory.
|
|
# ( n a -- )
|
|
ORG(None)
|
|
L("xt_!")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- DOKE(tmp1)
|
|
- CALL(next_word_fn)
|
|
|
|
# Fetches a value from memory.
|
|
# ( a -- n )
|
|
ORG(None)
|
|
L("xt_@")
|
|
- CALL(pop_data_fn)
|
|
- DEEK()
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Stores a character (byte).
|
|
# ( b a -- )
|
|
ORG(None)
|
|
L("xt_C!")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- POKE(tmp1)
|
|
- CALL(next_word_fn)
|
|
|
|
# Fetches a character.
|
|
# ( a -- b )
|
|
ORG(None)
|
|
L("xt_C@")
|
|
- CALL(pop_data_fn)
|
|
- PEEK()
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their sum.
|
|
# ( n2 n1 -- n1+n2 )
|
|
ORG(None)
|
|
L("xt_+")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- ADDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their difference.
|
|
# ( n2 n1 -- n2-n1 )
|
|
ORG(None)
|
|
L("xt_-")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- SUBW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their logical AND.
|
|
# ( n2 n1 -- n1&n2 )
|
|
ORG(None)
|
|
L("xt_AND")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- ANDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their logical OR.
|
|
# ( n2 n1 -- n1|n2 )
|
|
ORG(None)
|
|
L("xt_OR")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- ORW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their logical exclusive OR.
|
|
# ( n2 n1 -- n1^n2 )
|
|
ORG(None)
|
|
L("xt_XOR")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- XORW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the top of the stack with its logical NOT value.
|
|
# ( n1 -- ~n1 )
|
|
ORG(None)
|
|
L("xt_NOT")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- LDWI(-1)
|
|
- XORW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the top of the stack with its negative value.
|
|
# ( n1 -- ~n1 )
|
|
ORG(None)
|
|
L("xt_NEG")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- LDI(0)
|
|
- SUBW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their product.
|
|
# ( n2 n1 -- n2*n1 )
|
|
ORG(None)
|
|
L("xt_*")
|
|
- CALL(pop_data_fn)
|
|
- STW(md1)
|
|
- CALL(pop_data_fn)
|
|
- STW(md2)
|
|
- CALL(multiply_fn)
|
|
- LDW(md3)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their quotient and remainder.
|
|
# The two numbers n1 and n2 are treated as unsigned
|
|
# numbers.
|
|
# ( n2 n1 -- n2%n1 n2/n1 )
|
|
ORG(None)
|
|
L("xt_U/MOD")
|
|
- CALL(pop_data_fn)
|
|
- STW(md2)
|
|
- CALL(pop_data_fn)
|
|
- STW(md1)
|
|
- CALL(udivide_fn)
|
|
- LDW(md1)
|
|
- CALL(push_data_fn)
|
|
- LDW(md3)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Replaces the last two values on the data
|
|
# stack by their quotient and remainder.
|
|
# The two numbers n1 and n2 are treated as signed
|
|
# numbers.
|
|
# ( n2 n1 -- n2%n1 n2/n1 )
|
|
ORG(None)
|
|
L("xt_/MOD")
|
|
- CALL(pop_data_fn)
|
|
- STW(md2)
|
|
- CALL(pop_data_fn)
|
|
- STW(md1)
|
|
- CALL(divide_fn)
|
|
- LDW(md1)
|
|
- CALL(push_data_fn)
|
|
- LDW(md3)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Tests if the top of the stack equals to zero.
|
|
# ( n -- n==0 )
|
|
ORG(None)
|
|
L("xt_=0")
|
|
- CALL(pop_data_fn)
|
|
- BEQ(".xt_=0_satisfied")
|
|
- LDI(0)
|
|
- BRA(".xt_=0_end")
|
|
L(".xt_=0_satisfied")
|
|
- LDI(1)
|
|
L(".xt_=0_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Tests if the top of the stack is not zero.
|
|
# ( n -- n<>0 )
|
|
ORG(None)
|
|
L("xt_<>0")
|
|
- CALL(pop_data_fn)
|
|
- BNE(".xt_<>0_satisfied")
|
|
- LDI(0)
|
|
- BRA(".xt_<>0_end")
|
|
L(".xt_<>0_satisfied")
|
|
- LDI(1)
|
|
L(".xt_<>0_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Tests if the top of the stack is positive.
|
|
# ( n -- n>0 )
|
|
ORG(None)
|
|
L("xt_>0")
|
|
- CALL(pop_data_fn)
|
|
- BGT(".xt_>0_satisfied")
|
|
- LDI(0)
|
|
- BRA(".xt_>0_end")
|
|
L(".xt_>0_satisfied")
|
|
- LDI(1)
|
|
L(".xt_>0_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Tests if the top of the stack is negative.
|
|
# ( n -- n<0 )
|
|
ORG(None)
|
|
L("xt_<0")
|
|
- CALL(pop_data_fn)
|
|
- BLT(".xt_<0_satisfied")
|
|
- LDI(0)
|
|
- BRA(".xt_<0_end")
|
|
L(".xt_<0_satisfied")
|
|
- LDI(1)
|
|
L(".xt_<0_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Tests if the top of the stack is non-negative.
|
|
# ( n -- n>=0 )
|
|
ORG(None)
|
|
L("xt_>=0")
|
|
- CALL(pop_data_fn)
|
|
- BGE(".xt_>=0_satisfied")
|
|
- LDI(0)
|
|
- BRA(".xt_>=0_end")
|
|
L(".xt_>=0_satisfied")
|
|
- LDI(1)
|
|
L(".xt_>=0_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Tests if the top of the stack is non-positive.
|
|
# ( n -- n<=0 )
|
|
ORG(None)
|
|
L("xt_<=0")
|
|
- CALL(pop_data_fn)
|
|
- BLE(".xt_<=0_satisfied")
|
|
- LDI(0)
|
|
- BRA(".xt_<=0_end")
|
|
L(".xt_<=0_satisfied")
|
|
- LDI(1)
|
|
L(".xt_<=0_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Compares two numbers. That is, returns
|
|
# a number with the same sign as "n2 - n1" without the wrap
|
|
# around due to the 16-bit limit.
|
|
# ( n2 n1 -- cmp )
|
|
ORG(None)
|
|
L("xt_CMP")
|
|
- CALL(pop_data_fn)
|
|
- STW(md2)
|
|
- CALL(pop_data_fn)
|
|
- STW(md1)
|
|
- CALL(signed_cmp_fn)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Compares unsignedtwo numbers. That is, returns
|
|
# a number with the same sign as "n2 - n1" without the wrap
|
|
# around due to the 16-bit limit.
|
|
# ( n2 n1 -- ucmp )
|
|
ORG(None)
|
|
L("xt_UCMP")
|
|
- CALL(pop_data_fn)
|
|
- STW(md2)
|
|
- CALL(pop_data_fn)
|
|
- STW(md1)
|
|
- CALL(unsigned_cmp_fn)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Pushes a random value on top of the data stack.
|
|
# ( -- n )
|
|
ORG(None)
|
|
L("xt_RAND")
|
|
- LDWI(SYS_Random_34)
|
|
- STW(sysFn)
|
|
- SYS(34) # Call SYS_Random_34
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Gets a byte from the serial port.
|
|
# ( -- b )
|
|
ORG(None)
|
|
L("xt_SERIAL")
|
|
- LD(serialRaw)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Gets the button state.
|
|
# ( -- b )
|
|
ORG(None)
|
|
L("xt_BUTTON")
|
|
- LD(buttonState)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Gets a byte from the keyboard.
|
|
# ( -- b )
|
|
ORG(None)
|
|
L("xt_KEY")
|
|
- LD(serialRaw)
|
|
- STW(tmp1)
|
|
L(".xt_KEY_loop")
|
|
- LD(serialRaw)
|
|
- XORW(tmp1)
|
|
- BEQ(".xt_KEY_loop")
|
|
- LD(serialRaw)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Fills the input buffer.
|
|
# ( -- )
|
|
ORG(None)
|
|
L("xt_READ")
|
|
- CALL(setup_draw_fn)
|
|
- LDWI(buf_base)
|
|
- STW(buf)
|
|
- STW(inp)
|
|
- CALL(read_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Scrolls the screen by a specified number of lines.
|
|
# ( n -- )
|
|
ORG(None)
|
|
L("xt_SCROLL")
|
|
- CALL(pop_data_fn)
|
|
- CALL(scroll_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Puts a character onto the screen.
|
|
# ( b -- )
|
|
ORG(None)
|
|
L("xt_EMIT")
|
|
- CALL(setup_draw_fn)
|
|
- CALL(pop_data_fn)
|
|
- CALL(emit_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Prints a strings onto the screen.
|
|
# ( n addr -- )
|
|
ORG(None)
|
|
L("xt_TYPE")
|
|
- CALL(setup_draw_fn)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
|
|
L(".xt_TYPE_loop")
|
|
- LDW(tmp2)
|
|
- BEQ(".xt_TYPE_end")
|
|
- SUBI(1)
|
|
- STW(tmp2)
|
|
|
|
- LDW(tmp1)
|
|
- PEEK()
|
|
- STW(tmp3)
|
|
- LDI(tmp1)
|
|
- CALL(cforward_fn)
|
|
- LDW(tmp3)
|
|
- CALL(emit_fn)
|
|
- BRA(".xt_TYPE_loop")
|
|
L(".xt_TYPE_end")
|
|
- CALL(next_word_fn)
|
|
|
|
# Clears the screen with the background color.
|
|
# ( -- )
|
|
ORG(None)
|
|
L("xt_CLL")
|
|
- CALL(setup_draw_fn)
|
|
- LDW(scr_pos)
|
|
- CALL(clearline_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Clears the screen with the background color.
|
|
# ( -- )
|
|
ORG(None)
|
|
L("xt_CLS")
|
|
- CALL(setup_draw_fn)
|
|
- LDWI(0x0800)
|
|
- STW(tmp2)
|
|
- LDWI(0x0860)
|
|
- STW(tmp1)
|
|
L(".xt_CLS_loop")
|
|
- CALL(clearline_fn)
|
|
- LDW(tmp1)
|
|
- ADDW(tmp2)
|
|
- STW(tmp1)
|
|
- BLT(".xt_CLS_end")
|
|
- BRA(".xt_CLS_loop")
|
|
L(".xt_CLS_end")
|
|
- CALL(next_word_fn)
|
|
|
|
# Parses the current word.
|
|
# ( b -- n addr )
|
|
ORG(None)
|
|
L("xt_WORD")
|
|
- CALL(pop_data_fn)
|
|
- CALL(parse_word_fn)
|
|
- LD(wlen)
|
|
- CALL(push_data_fn)
|
|
- LDW(wbuf)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Parses a number.
|
|
# ( l a -- n 1 | 0 )
|
|
ORG(None)
|
|
L("xt_NUMBER")
|
|
- CALL(pop_data_fn)
|
|
- STW(str2)
|
|
- CALL(pop_data_fn)
|
|
- STW(str1)
|
|
- CALL(number_fn)
|
|
- BLE(".xt_NUMBER_fail")
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDI(1)
|
|
L(".xt_NUMBER_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
L(".xt_NUMBER_fail")
|
|
- LDI(0)
|
|
- BRA(".xt_NUMBER_end")
|
|
|
|
# Copies memory from one location to another
|
|
# (from a2 to a1, l bytes).
|
|
# ( l a2 a1 -- )
|
|
ORG(None)
|
|
L("xt_MEMCPY")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp3)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(memcpy_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Checks if two strings are equal.
|
|
# ( l2 a2 l1 a1 -- e )
|
|
ORG(None)
|
|
L("xt_S=")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp4)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp3)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(streq_fn)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Finds a word in the dictionary.
|
|
# ( l a -- n )
|
|
ORG(None)
|
|
L("xt_FIND")
|
|
- CALL(pop_data_fn)
|
|
- STW(str2)
|
|
- CALL(pop_data_fn)
|
|
- STW(str1)
|
|
- CALL(find_fn)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# To process the next token (word or number).
|
|
# ( b -- l a 0 | w 1 | n 2 )
|
|
ORG(None)
|
|
L("xt_TOKEN")
|
|
- CALL(pop_data_fn)
|
|
- CALL(parse_word_fn)
|
|
- LD(wlen)
|
|
- BEQ(".xt_TOKEN_error")
|
|
- STW(str1)
|
|
- LDW(wbuf)
|
|
- STW(str2)
|
|
- CALL(find_fn)
|
|
- BEQ(".xt_TOKEN_number")
|
|
- CALL(push_data_fn)
|
|
- LDI(1)
|
|
- BRA(".xt_TOKEN_end")
|
|
L(".xt_TOKEN_number")
|
|
- CALL(number_fn)
|
|
- BLE(".xt_TOKEN_error")
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- LDI(2)
|
|
- BRA(".xt_TOKEN_end")
|
|
L(".xt_TOKEN_error")
|
|
- LD(wlen)
|
|
- CALL(push_data_fn)
|
|
- LDW(wbuf)
|
|
- CALL(push_data_fn)
|
|
- LDI(0)
|
|
L(".xt_TOKEN_end")
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Verifies if the word is not compile only
|
|
# and we trying to execute it.
|
|
# ( a b -- a b | l a 0 )
|
|
# where ( a b ) = ( w 1 ) or ( n 2 )
|
|
ORG(None)
|
|
L("xt_VERIFY")
|
|
- LDW(state)
|
|
- BNE(".xt_VERIFY_end")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- SUBI(1)
|
|
- BEQ(".xt_VERIFY_word")
|
|
- LDW(tmp1)
|
|
L(".xt_VERIFY_push_end")
|
|
- CALL(push_data_fn)
|
|
L(".xt_VERIFY_end")
|
|
- CALL(next_word_fn)
|
|
L(".xt_VERIFY_word")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- STW(tmp1)
|
|
- CALL(flags_fn)
|
|
- ANDI(F_COMP)
|
|
- BNE(".xt_VERIFY_error")
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- LDI(1)
|
|
- BRA(".xt_VERIFY_push_end")
|
|
L(".xt_VERIFY_error")
|
|
- CALL(cfa_fn)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp3)
|
|
- CALL(push_data_fn)
|
|
- LDI(0)
|
|
- BRA(".xt_VERIFY_push_end")
|
|
|
|
# To execute the token.
|
|
# ( w 1 | n 2 -- )
|
|
ORG(None)
|
|
L("xt_DISPATCH")
|
|
- LDW(state)
|
|
- BNE(".xt_DISPATCH_compile")
|
|
- CALL(pop_data_fn)
|
|
- XORI(1)
|
|
- BNE(".xt_DISPATCH_end")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(flags_fn)
|
|
L(".xt_DISPATCH_execute_word")
|
|
- CALL(cfa_fn)
|
|
- LDW(tmp1)
|
|
- CALL(exec_fn)
|
|
L(".xt_DISPATCH_compile")
|
|
- CALL(pop_data_fn)
|
|
- XORI(1)
|
|
- BEQ(".xt_DISPATCH_compile_word")
|
|
- LDWI("LIT")
|
|
- CALL(comma_fn)
|
|
- CALL(pop_data_fn)
|
|
L(".xt_DISPATCH_compile_end")
|
|
- CALL(comma_fn)
|
|
L(".xt_DISPATCH_end")
|
|
- CALL(next_word_fn)
|
|
L(".xt_DISPATCH_compile_word")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(flags_fn)
|
|
- ANDI(F_IMMD)
|
|
- BNE(".xt_DISPATCH_execute_word")
|
|
- CALL(cfa_fn)
|
|
- LDW(tmp1)
|
|
- BRA(".xt_DISPATCH_compile_end")
|
|
|
|
# Returns the flags of a word.
|
|
# ( w -- flags )
|
|
ORG(None)
|
|
L("xt_FLAGS")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(flags_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Returns the name of a word.
|
|
# ( w -- l a )
|
|
ORG(None)
|
|
L("xt_NAME")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(flags_fn)
|
|
- CALL(cfa_fn)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- LDW(tmp3)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Returns the CFA of a word.
|
|
# ( w -- cfa )
|
|
ORG(None)
|
|
L("xt_>CFA")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(flags_fn)
|
|
- CALL(cfa_fn)
|
|
- LDW(tmp1)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Creates a new word.
|
|
# ( l a -- )
|
|
ORG(None)
|
|
L("xt_CREATE")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- LDI(here)
|
|
- CALL(align_addr_fn)
|
|
- LDW(latest)
|
|
- DOKE(here)
|
|
- LDW(here)
|
|
- STW(latest)
|
|
- LDI(here)
|
|
- CALL(forward_fn)
|
|
- LDW(here)
|
|
- STW(tmp4)
|
|
- CALL(str_comma_fn)
|
|
#- LDW(tmp4)
|
|
#- PEEK()
|
|
#- ORI(F_HIDD) # Hide the word
|
|
#- POKE(tmp4)
|
|
- CALL(next_word_fn)
|
|
|
|
# Compiles a word.
|
|
# ( n -- )
|
|
ORG(None)
|
|
L("xt_,")
|
|
- CALL(pop_data_fn)
|
|
- CALL(comma_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Compiles a string.
|
|
# ( l a -- )
|
|
ORG(None)
|
|
L("xt_S,")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp2)
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- CALL(str_comma_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Converts a number to string.
|
|
# ( n -- l a )
|
|
ORG(None)
|
|
L("xt_CONVERT")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- LDWI(auxbuf_end)
|
|
- STW(tmp2)
|
|
- CALL(cv_number_fn)
|
|
- LDWI(auxbuf_end)
|
|
- SUBW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- INC(tmp2)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Converts an unsigned number to string.
|
|
# ( n -- l a )
|
|
ORG(None)
|
|
L("xt_UCONVERT")
|
|
- CALL(pop_data_fn)
|
|
- STW(tmp1)
|
|
- LDWI(auxbuf_end)
|
|
- STW(tmp2)
|
|
- CALL(cv_unumber_fn)
|
|
- LDWI(auxbuf_end)
|
|
- SUBW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- INC(tmp2)
|
|
- LDW(tmp2)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
|
|
# Returns the number of free bytes.
|
|
# ( -- n )
|
|
ORG(None)
|
|
L("xt_FREE")
|
|
- LDI(0)
|
|
- STW(tmp3)
|
|
- LD(memSize)
|
|
- BNE(".xt_FREE_not_64k")
|
|
- LDWI(0x100)
|
|
L(".xt_FREE_not_64k")
|
|
- STW(tmp1)
|
|
- SUBI(128)
|
|
- BLE(".xt_FREE_32k")
|
|
- ST(tmp3+1)
|
|
- LDI(128)
|
|
- STW(tmp1)
|
|
L(".xt_FREE_32k")
|
|
- LD(here+1)
|
|
- STW(tmp2)
|
|
- LDW(tmp1)
|
|
- SUBW(tmp2)
|
|
- SUBI(9) # The first 8 pages are not usable
|
|
- STW(md1)
|
|
- LDWI(segment_size)
|
|
- STW(md2)
|
|
- CALL(multiply_fn)
|
|
- LD(here)
|
|
- STW(tmp2)
|
|
- LDW(md2)
|
|
- SUBW(tmp2)
|
|
- ADDW(md3)
|
|
- ADDW(tmp3)
|
|
- CALL(push_data_fn)
|
|
- CALL(next_word_fn)
|
|
|
|
# Trigger the callback to resolve the segments.
|
|
last_segment_addr = RESOLVE_SEGMENTS(
|
|
pack_segments_factory(base=code_base,
|
|
size=segment_size)
|
|
)
|
|
|
|
if data_base is None:
|
|
data_base = last_segment_addr + 256
|
|
print("Resolved data_base = 0x$%04X" % data_base)
|
|
|
|
# ============================
|
|
# Data segment
|
|
# ============================
|
|
|
|
ORG(data_base, size=segment_size, callback=extend_callback)
|
|
|
|
# The basic words
|
|
|
|
define_word("ADJ", 0, xt="xt_ADJ")
|
|
define_word("ALIGN", 0, xt="xt_ALIGN")
|
|
define_word("EXECUTE", 0, xt="xt_EXECUTE")
|
|
define_word("BRANCH", F_COMP, xt="xt_BRANCH")
|
|
define_word("0BRANCH", F_COMP, xt="xt_0BRANCH")
|
|
define_word("EXIT", F_COMP, xt="xt_EXIT")
|
|
define_word("LIT", F_COMP, xt="xt_LIT")
|
|
define_word("LITSTR", F_COMP, xt="xt_LITSTR")
|
|
define_word("DROP", 0, xt="xt_DROP")
|
|
define_word("2DROP", 0, xt="xt_2DROP")
|
|
define_word("SWAP", 0, xt="xt_SWAP")
|
|
define_word("DUP", 0, xt="xt_DUP")
|
|
define_word("2DUP", 0, xt="xt_2DUP")
|
|
define_word("?DUP", 0, xt="xt_?DUP")
|
|
define_word("OVER", 0, xt="xt_OVER")
|
|
define_word("ROT", 0, xt="xt_ROT")
|
|
define_word("RROT", 0, xt="xt_RROT")
|
|
define_word("R>", 0, xt="xt_R>")
|
|
define_word(">R", 0, xt="xt_>R")
|
|
define_word("DSP@", 0, xt="xt_DSP@")
|
|
define_word("DSP!", 0, xt="xt_DSP!")
|
|
define_word("RSP@", 0, xt="xt_RSP@")
|
|
define_word("RSP!", 0, xt="xt_RSP!")
|
|
define_word("!", 0, xt="xt_!")
|
|
define_word("@", 0, xt="xt_@")
|
|
define_word("C!", 0, xt="xt_C!")
|
|
define_word("C@", 0, xt="xt_C@")
|
|
define_word("+", 0, xt="xt_+")
|
|
define_word("-", 0, xt="xt_-")
|
|
define_word("AND", 0, xt="xt_AND")
|
|
define_word("OR", 0, xt="xt_OR")
|
|
define_word("XOR", 0, xt="xt_XOR")
|
|
define_word("NOT", 0, xt="xt_NOT")
|
|
define_word("NEG", 0, xt="xt_NEG")
|
|
define_word("*", 0, xt="xt_*")
|
|
define_word("U/MOD", 0, xt="xt_U/MOD")
|
|
define_word("/MOD", 0, xt="xt_/MOD")
|
|
define_word("=0", 0, xt="xt_=0")
|
|
define_word("<>0", 0, xt="xt_<>0")
|
|
define_word(">0", 0, xt="xt_>0")
|
|
define_word("<0", 0, xt="xt_<0")
|
|
define_word(">=0", 0, xt="xt_>=0")
|
|
define_word("<=0", 0, xt="xt_<=0")
|
|
define_word("CMP", 0, xt="xt_CMP")
|
|
define_word("UCMP", 0, xt="xt_UCMP")
|
|
define_word("RAND", 0, xt="xt_RAND")
|
|
define_word("SERIAL", 0, xt="xt_SERIAL")
|
|
define_word("BUTTON", 0, xt="xt_BUTTON")
|
|
define_word("KEY", 0, xt="xt_KEY")
|
|
define_word("READ", 0, xt="xt_READ")
|
|
define_word("SCROLL", 0, xt="xt_SCROLL")
|
|
define_word("EMIT", 0, xt="xt_EMIT")
|
|
define_word("TYPE", 0, xt="xt_TYPE")
|
|
define_word("CLL", 0, xt="xt_CLL")
|
|
define_word("CLS", 0, xt="xt_CLS")
|
|
define_word("WORD", 0, xt="xt_WORD")
|
|
define_word("NUMBER", 0, xt="xt_NUMBER")
|
|
define_word("MEMCPY", 0, xt="xt_MEMCPY")
|
|
define_word("S=", 0, xt="xt_S=")
|
|
define_word("FIND", 0, xt="xt_FIND")
|
|
define_word("TOKEN", 0, xt="xt_TOKEN")
|
|
define_word("VERIFY", 0, xt="xt_VERIFY")
|
|
define_word("DISPATCH", 0, xt="xt_DISPATCH")
|
|
define_word("FLAGS", 0, xt="xt_FLAGS")
|
|
define_word("NAME", 0, xt="xt_NAME")
|
|
define_word(">CFA", 0, xt="xt_>CFA")
|
|
define_word("CREATE", 0, xt="xt_CREATE")
|
|
define_word(",", 0, xt="xt_,")
|
|
define_word("S,", 0, xt="xt_S,")
|
|
define_word("CONVERT", 0, xt="xt_CONVERT")
|
|
define_word("UCONVERT", 0, xt="xt_UCONVERT")
|
|
define_word("FREE", 0, xt="xt_FREE")
|
|
|
|
# Some constants
|
|
define_constant("HERE", here)
|
|
define_constant("LATEST", latest)
|
|
define_constant("BASE", base)
|
|
define_constant("STATE", state)
|
|
define_constant("SCR_POS", scr_pos)
|
|
define_constant("SCR_COLOR", scr_color)
|
|
define_constant("F_IMMD", F_IMMD)
|
|
define_constant("F_COMP", F_COMP)
|
|
define_constant("F_HIDD", F_HIDD)
|
|
define_constant("F_MASK", F_MASK)
|
|
define_constant("DOCOL", "xt_DOCOL")
|
|
define_constant("DOCON", "xt_DOCON")
|
|
define_constant("RSP0", rsp_top)
|
|
define_constant("DSP0", dsp_top)
|
|
define_constant("SEGSIZE", segment_size)
|
|
define_constant("FALSE", 0)
|
|
define_constant("TRUE", 1)
|
|
define_constant("BL", ord(' '))
|
|
define_constant("\\n", ord('\n'))
|
|
|
|
define_word("CR", 0)
|
|
- reference("\\n")
|
|
- reference("EMIT")
|
|
- reference("EXIT")
|
|
|
|
define_word("SPACE", 0)
|
|
- reference("BL")
|
|
- reference("EMIT")
|
|
- reference("EXIT")
|
|
|
|
define_word(".", 0)
|
|
- reference("CONVERT")
|
|
- reference("TYPE")
|
|
- reference("SPACE")
|
|
- reference("EXIT")
|
|
|
|
define_word("U.", 0)
|
|
- reference("UCONVERT")
|
|
- reference("TYPE")
|
|
- reference("SPACE")
|
|
- reference("EXIT")
|
|
|
|
define_word("RESET", 0)
|
|
L("cold_start")
|
|
- reference("CLS")
|
|
- reference("LITSTR")
|
|
- place_string(welcome_string)
|
|
- reference("TYPE")
|
|
- reference("FREE")
|
|
- reference("U.")
|
|
- reference("LITSTR")
|
|
- place_string(free_bytes_string)
|
|
- reference("TYPE")
|
|
- reference("INTERPRET")
|
|
|
|
define_word("INTERPRET", 0)
|
|
- reference("RSP0")
|
|
- reference("RSP!")
|
|
- reference("DSP0")
|
|
- reference("DSP!")
|
|
L(".interpret_loop1")
|
|
- reference("LIT")
|
|
- reference(ord('>'))
|
|
- reference("STATE")
|
|
- reference("@")
|
|
- reference("0BRANCH")
|
|
- reference(".interpret_prompt")
|
|
- reference("DROP")
|
|
- reference("BL")
|
|
L(".interpret_prompt")
|
|
- reference("EMIT")
|
|
- reference("READ")
|
|
- reference("CR")
|
|
L(".interpret_loop2")
|
|
- reference("BL")
|
|
- reference("TOKEN")
|
|
- reference("?DUP")
|
|
- reference("0BRANCH")
|
|
- reference(".interpret_test_error")
|
|
- reference("VERIFY")
|
|
- reference("?DUP")
|
|
- reference("0BRANCH")
|
|
- reference(".interpret_error_compile_only")
|
|
- reference("DISPATCH")
|
|
- reference("BRANCH")
|
|
- reference(".interpret_loop2")
|
|
L(".interpret_test_error")
|
|
- reference("OVER")
|
|
- reference("=0")
|
|
- reference("0BRANCH")
|
|
- reference(".interpret_error_unknown_word")
|
|
- reference("2DROP")
|
|
- reference("BRANCH")
|
|
- reference(".interpret_loop1")
|
|
L(".interpret_error_unknown_word")
|
|
- reference("LIT")
|
|
- reference(ord('?'))
|
|
L(".interpret_continue")
|
|
- reference("EMIT")
|
|
- reference("SPACE")
|
|
- reference("TYPE")
|
|
- reference("CR")
|
|
- reference("BRANCH")
|
|
- reference(".interpret_loop1")
|
|
L(".interpret_error_compile_only")
|
|
- reference("LIT")
|
|
- reference(ord('!'))
|
|
- reference("BRANCH")
|
|
- reference(".interpret_continue")
|
|
|
|
define_word("'", F_IMMD)
|
|
- reference("BL")
|
|
- reference("WORD")
|
|
- reference("2DUP")
|
|
- reference("FIND")
|
|
- reference("?DUP")
|
|
- reference("0BRANCH")
|
|
- reference(".interpret_error_unknown_word")
|
|
- reference(">CFA")
|
|
- reference("RROT")
|
|
- reference("2DROP")
|
|
- reference("EXIT")
|
|
|
|
define_word("WORDS", 0)
|
|
- reference("LATEST")
|
|
L(".words_loop")
|
|
- reference("@")
|
|
- reference("?DUP")
|
|
- reference("0BRANCH")
|
|
- reference(".words_end")
|
|
- reference("DUP")
|
|
- reference("FLAGS")
|
|
- reference("C@")
|
|
- reference("F_HIDD")
|
|
- reference("AND")
|
|
- reference("=0")
|
|
- reference("0BRANCH")
|
|
- reference(".words_loop")
|
|
- reference("DUP")
|
|
- reference("NAME")
|
|
- reference("TYPE")
|
|
- reference("SPACE")
|
|
- reference("BRANCH")
|
|
- reference(".words_loop")
|
|
L(".words_end")
|
|
- reference("EXIT")
|
|
|
|
L("here_loc")
|
|
|
|
# ============================
|
|
# Emulation
|
|
# ============================
|
|
|
|
gt1_bytes = END("start", filename=None)
|
|
vcpu = VirtualCpu()
|
|
|
|
src_text = """
|
|
BL WORD [ CREATE DOCOL , ' FALSE , ' STATE , ' ! , ' EXIT ,
|
|
LATEST @ FLAGS DUP C@ F_HIDD NOT AND F_IMMD OR SWAP C!
|
|
|
|
BL WORD ] CREATE DOCOL , ' TRUE , ' STATE , ' ! , ' EXIT ,
|
|
LATEST @ FLAGS DUP C@ F_HIDD NOT AND SWAP C!
|
|
|
|
BL WORD UNHIDE CREATE DOCOL , ]
|
|
LATEST @ FLAGS DUP C@ F_HIDD NOT AND SWAP C! EXIT [
|
|
LATEST @ FLAGS DUP C@ F_HIDD NOT AND SWAP C!
|
|
|
|
BL WORD : CREATE DOCOL , ] BL WORD CREATE DOCOL , ] EXIT [ UNHIDE
|
|
|
|
: IMMEDIATE LATEST @ FLAGS DUP C@ F_IMMD OR SWAP C! EXIT [ UNHIDE
|
|
|
|
: ; LIT EXIT , UNHIDE [ ' [ , ] EXIT [ UNHIDE IMMEDIATE
|
|
|
|
: COMPILE-ONLY LATEST @ FLAGS DUP C@ F_COMP OR SWAP C! ;
|
|
|
|
: CONSTANT BL WORD CREATE DOCON , , ;
|
|
|
|
: VARIABLE BL WORD CREATE DOCON , HERE @ 0 , HERE @ SWAP ! 0 , ;
|
|
|
|
: DECIMAL 10 BASE ! ;
|
|
: HEX 16 BASE ! ;
|
|
|
|
: IF LIT 0BRANCH , HERE @ 0 , ; IMMEDIATE COMPILE-ONLY
|
|
: THEN HERE @ SWAP ! ; IMMEDIATE COMPILE-ONLY
|
|
: ELSE LIT BRANCH , HERE @ SWAP 0 ,
|
|
HERE @ SWAP ! ; IMMEDIATE COMPILE-ONLY
|
|
|
|
: BEGIN HERE @ ; IMMEDIATE COMPILE-ONLY
|
|
: UNTIL LIT 0BRANCH , , ; IMMEDIATE COMPILE-ONLY
|
|
: AGAIN LIT BRANCH , , ; IMMEDIATE COMPILE-ONLY
|
|
: WHILE LIT 0BRANCH , HERE @ 0 , ; IMMEDIATE COMPILE-ONLY
|
|
: REPEAT LIT BRANCH , SWAP , HERE @ SWAP ! ; IMMEDIATE COMPILE-ONLY
|
|
|
|
: CHAR BL WORD SWAP IF C@ ELSE 0 THEN ;
|
|
|
|
: ( LIT [ CHAR ) , ] WORD 2DROP ;
|
|
|
|
( We have comments! Not that I happen to use many comments anyway :-)
|
|
|
|
: = - =0 ;
|
|
: <> - <>0 ;
|
|
: > CMP >0 ;
|
|
: < CMP <0 ;
|
|
: >= CMP >=0 ;
|
|
: <= CMP <=0 ;
|
|
|
|
: / /MOD SWAP DROP ;
|
|
: MOD /MOD DROP ;
|
|
|
|
: S" LIT [ CHAR " , ] WORD STATE @
|
|
IF LIT LITSTR , S, THEN
|
|
; IMMEDIATE
|
|
|
|
: ." LIT [ CHAR " , ] WORD STATE @
|
|
IF LIT LITSTR , S, LIT TYPE , ELSE TYPE THEN
|
|
; IMMEDIATE
|
|
|
|
: FORGET BL WORD FIND DUP @ LATEST ! HERE ! INTERPRET ;
|
|
|
|
FREE .
|
|
"""
|
|
|
|
src = io.BytesIO(src_text.encode("ascii"))
|
|
out = io.BytesIO()
|
|
|
|
def emit_breakpoint(vcpu, addr):
|
|
c = vcpu.get_vAC()
|
|
out.write(bytes(tuple([ c ])))
|
|
|
|
def read_breakpoint(vcpu, addr):
|
|
if addr == ADDR(".read_loop1"):
|
|
vcpu.write_byte(serialRaw, 0xFF)
|
|
else:
|
|
b = src.read(1)
|
|
if len(b) == 0:
|
|
vcpu.halt()
|
|
else:
|
|
vcpu.write_byte(serialRaw, b[0])
|
|
|
|
breakpoints = {}
|
|
breakpoints[ADDR("emit")] = emit_breakpoint
|
|
breakpoints[ADDR(".read_loop1")] = read_breakpoint
|
|
breakpoints[ADDR(".read_loop2")] = read_breakpoint
|
|
|
|
here_before = ADDR("here_loc")
|
|
latest_before = ADDR(current_link[0])
|
|
|
|
|
|
print("\nRunning GtForth in emulated vCPU ...")
|
|
vcpu.load_gt1(gt1_bytes=gt1_bytes)
|
|
vcpu.write_byte(memSize, 128)
|
|
try:
|
|
vcpu.run(breakpoints=breakpoints)
|
|
finally:
|
|
out_text = out.getvalue().decode("ascii")
|
|
print(out_text)
|
|
|
|
here_current = vcpu.read_word(here)
|
|
latest_current = vcpu.read_word(latest)
|
|
print("HERE (before): 0x%04X" % here_before)
|
|
print("HERE (after): 0x%04X" % here_current)
|
|
print("LATEST (before): 0x%04X" % latest_before)
|
|
print("LATEST (after): 0x%04X\n" % latest_current)
|
|
|
|
assert(here_current < 0x8000)
|
|
|
|
# Inject the new code
|
|
print("Injecting new code into GT1 file ...")
|
|
addr = here_before
|
|
while addr < here_current:
|
|
b = vcpu.read_byte(addr)
|
|
BYTE(b)
|
|
addr += 1
|
|
if (addr & 0xFF) >= segment_size:
|
|
addr = (addr & ~0xFF) + 0x100
|
|
|
|
def inject_new_values_factory(new_values):
|
|
def do_inject(gt1, symbols):
|
|
resolved = set()
|
|
for segment in gt1:
|
|
base = segment[0]
|
|
contents = segment[3]
|
|
size = len(contents)
|
|
for addr, val in new_values.items():
|
|
if addr >= base and addr < base + size:
|
|
resolved.add(addr)
|
|
offset = addr - base
|
|
contents[offset] = val & 0xFF
|
|
contents[offset + 1] = (val >> 8) & 0xFF
|
|
|
|
return do_inject
|
|
|
|
RESOLVE_SEGMENTS(inject_new_values_factory({
|
|
(init1_base + here): here_current,
|
|
(init1_base + latest): latest_current
|
|
}))
|
|
|
|
# ============================
|
|
# GT1 file generation
|
|
# ============================
|
|
|
|
# Execution start address
|
|
END("start")
|
|
|
|
used_bytes = segment_size * (here_current >> 8) \
|
|
+ (here_current & 0xFF)
|
|
free_bytes = segment_size * (0x80 - 0x08) - used_bytes
|
|
print("used bytes = %d" % used_bytes)
|
|
print("free bytes for 32K RAM = %d" % free_bytes)
|