stage0/stage2/lisp.s

2623 lines
79 KiB
ArmAsm

; Copyright (C) 2016 Jeremiah Orians
; This file is part of stage0.
;
; stage0 is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; stage0 is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with stage0. If not, see <http://www.gnu.org/licenses/>.
;; A simple lisp with a precise garbage collector for cells
;; Cells are in the following form:
;; Type (0), CAR (4), CDR (8), ENV (12)
;; Each being the length of a register [32bits]
;;
;; Type maps to the following values
;; FREE = 1, MARKED = (1 << 1),INT = (1 << 2),SYM = (1 << 3),
;; CONS = (1 << 4),PROC = (1 << 5),PRIMOP = (1 << 6),ASCII = (1 << 7)
;; CONS space: End of program -> 1MB (0x100000)
;; HEAP space: 1MB -> 1.5MB (0x180000)
;; STACK space: 1.5MB -> End of Memory (2MB (0x200000))
;; Start function
:start
LOADR R15 @stack_start ; Put stack after CONS and HEAP
;; We will be using R14 for our condition codes
;; We will be using R13 for which Input we will be using
;; We will be using R12 for which Output we will be using
;; Initialize
CALLI R15 @garbage_init
CALLI R15 @init_sl3
;; Prep TAPE_01
LOADUI R0 0x1100
FOPEN_READ
;; We first read Tape_01 until completion
LOADUI R13 0x1100
;; Main loop
:main
CALLI R15 @garbage_collect ; Clean up unused cells
CALLI R15 @Readline ; Read another S-expression
JUMP.Z R1 @main ; Don't allow empty strings
CALLI R15 @parse ; Convert into tokens
LOADR R1 @top_env ; Get TOP_ENV
CALLI R15 @eval ; Evaluate tokens
CALLI R15 @writeobj ; Print result
LOADUI R0 10 ; Use LF
COPY R1 R12 ; And desired Output
FPUTC ; Write Line Feed
FALSE R0 ; Clear R0
FALSE R1 ; Clear R1
JUMP @main ; Loop forever
HALT ; If broken get the fuck out now
:stack_start
'00180000'
;; Append_Cell
;; Adds a cell to the end of a CDR chain
;; Recieves HEAD in R0 and Tail in R1
;; Returns HEAD if not NULL
:append_Cell
CMPSKIPI.NE R0 0 ; If HEAD is NULL
MOVE R0 R1 ; Swap TAIL and HEAD
PUSHR R3 R15 ; Protect R3
PUSHR R0 R15 ; Preserve HEAD
:append_Cell_loop
LOAD32 R3 R0 8 ; Load HEAD->CDR
CMPSKIPI.NE R3 0 ; If HEAD->CDR is NULL
JUMP @append_Cell_done ; Append and call it done
;; Walk down list
MOVE R0 R3 ; Make HEAD->CDR the new HEAD
JUMP @append_Cell_loop ; And try again
:append_Cell_done
STORE32 R1 R0 8 ; Store HEAD->CDR = Tail
POPR R0 R15 ; Ensure we are returning HEAD of list
POPR R3 R15 ; Restore R3
RET R15
;; Tokenize
;; Converts a string into a list of tokens
;; Recieves HEAD in R0, Pointer to String in R1 and Size of string in R2
;; Returns HEAD of list in R0
:tokenize
;; Deal with Edge case
CMPSKIPI.NE R2 0 ; If remaining is 0
RET R15 ; Just return
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
FALSE R4 ; Set Counter to 0
;; Try to find whitespace Char
:tokenize_loop
LOADXU8 R3 R1 R4 ; Get char
CMPSKIPI.G R3 32 ; If control character or SPACE
JUMP @tokenize_append ; Stop
;; Walk further down string
ADDUI R4 R4 1 ; Next char
JUMP @tokenize_loop ; And try again
:tokenize_append
FALSE R3 ; NULL terminate
STOREX8 R3 R1 R4 ; Found Token
COPY R3 R1 ; Preserve pointer to string
CMPSKIPI.NE R4 0 ; If empty
JUMP @tokenize_iterate ; Don't bother to append
;; Make string token and append
SWAP R0 R1 ; Need to send string in R0 for call
CALLI R15 @make_sym ; Convert string to token
SWAP R0 R1 ; Put HEAD and Tail in proper order
CALLI R15 @append_Cell ; Append Token to HEAD
;; Loop down string until end, appending tokens along the way
:tokenize_iterate
ADDUI R4 R4 1 ; Move past NULL
ADD R1 R3 R4 ; Update string pointer
SUB R2 R2 R4 ; Decrement by size used
FALSE R4 ; Reset Counter
CMPSKIPI.LE R2 0 ; If NOT end of string
JUMP @tokenize_loop ; try to append another token
;; Clean up
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
RET R15
;; is_integer
;; Recieves pointer to string in R0
;; Returns TRUE or FALSE in R0
:is_integer
PUSHR R1 R15 ; Protect R1
LOADU8 R1 R0 0 ; Read first Char
CMPSKIPI.NE R1 45 ; If starts with -
LOADU8 R1 R0 1 ; Get Second Char
FALSE R0 ; Assume FALSE
CMPSKIPI.GE R1 48 ; If below '0'
JUMP @is_integer_done ; Return FALSE
CMPSKIPI.G R1 57 ; If 0 to 9
TRUE R0 ; Set to TRUE
:is_integer_done
POPR R1 R15 ; Restore R1
RET R15
;; numerate_string function
;; Recieves pointer To string in R0
;; Returns number in R0 equal to value of string
;; Or Zero in the event of invalid string
:numerate_string
;; Preserve Registers
PUSHR R1 R15
PUSHR R2 R15
PUSHR R3 R15
PUSHR R4 R15
;; Initialize
MOVE R1 R0 ; Get Text pointer out of the way
FALSE R2 ; Set Negative flag to false
FALSE R3 ; Set current count to Zero
LOAD8 R0 R1 1 ; Get second byte
CMPSKIPI.NE R0 120 ; If the second byte is x
JUMP @numerate_string_hex ; treat string like hex
;; Deal with Decimal input
LOADUI R4 10 ; Multiply by 10
LOAD8 R0 R1 0 ; Get a byte
CMPSKIPI.NE R0 45 ; If - toggle flag
TRUE R2 ; So that we know to negate
CMPSKIPI.E R2 0 ; If toggled
ADDUI R1 R1 1 ; Move to next
:numerate_string_dec
LOAD8 R0 R1 0 ; Get a byte
CMPSKIPI.NE R0 0 ; If NULL
JUMP @numerate_string_done ; Be done
MUL R3 R3 R4 ; Shift counter by 10
SUBI R0 R0 48 ; Convert ascii to number
CMPSKIPI.GE R0 0 ; If less than a number
JUMP @numerate_string_done ; Terminate NOW
CMPSKIPI.L R0 10 ; If more than a number
JUMP @numerate_string_done ; Terminate NOW
ADDU R3 R3 R0 ; Don't add to the count
ADDUI R1 R1 1 ; Move onto next byte
JUMP @numerate_string_dec
;; Deal with Hex input
:numerate_string_hex
LOAD8 R0 R1 0 ; Get a byte
CMPSKIPI.E R0 48 ; All hex strings start with 0x
JUMP @numerate_string_done ; Be done if not a match
ADDUI R1 R1 2 ; Move to after leading 0x
:numerate_string_hex_0
LOAD8 R0 R1 0 ; Get a byte
CMPSKIPI.NE R0 0 ; If NULL
JUMP @numerate_string_done ; Be done
SL0I R3 4 ; Shift counter by 16
SUBI R0 R0 48 ; Convert ascii number to number
CMPSKIPI.L R0 10 ; If A-F
SUBI R0 R0 7 ; Shove into Range
CMPSKIPI.L R0 16 ; If a-f
SUBI R0 R0 32 ; Shove into Range
ADDU R3 R3 R0 ; Add to the count
ADDUI R1 R1 1 ; Get next Hex
JUMP @numerate_string_hex_0
;; Clean up
:numerate_string_done
CMPSKIPI.E R2 0 ; If Negate flag has been set
NEG R3 R3 ; Make the number negative
MOVE R0 R3 ; Put number in R0
;; Restore Registers
POPR R4 R15
POPR R3 R15
POPR R2 R15
POPR R1 R15
RET R15
;; atom
;; Converts tokens into native forms
;; Aka numbers become numbers and everything else is a symbol
;; Recieves a pointer to Token in R0
;; Returns a pointer to a Cell in R0
:atom
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOAD32 R1 R0 4 ; Get CAR
LOADU8 R2 R1 0 ; Get first Char
CMPSKIPI.E R2 39 ; If Not Quote Char
JUMP @atom_integer ; Move to next type
;; When dealing with a quote
ADDUI R1 R1 1 ; Move past quote Char
STORE32 R1 R0 4 ; And write to CAR
LOADUI R1 $NIL ; Using NIL
CALLI R15 @make_cons ; Make a cons with the token
MOVE R1 R0 ; Put the resulting CONS in R1
LOADUI R0 $s_quote ; Using S_QUOTE
CALLI R15 @make_cons ; Make a CONS with the CONS
MOVE R1 R0 ; Put What is being returned into R1
JUMP @atom_done ; We are done
:atom_integer
COPY R2 R1 ; Preserve String pointer
SWAP R0 R1 ; Put string Pointer in R0
CALLI R15 @is_integer ; Determine if it is an integer
JUMP.Z R0 @atom_functions ; If Not an integer move on
LOADUI R0 4 ; Using INT
STORE32 R0 R1 0 ; Set type to Integer
MOVE R0 R2 ; Using String pointer
CALLI R15 @numerate_string ; Convert to Number
STORE32 R0 R1 4 ; Store result in CAR
JUMP @atom_done ; We are done (Result is in R1)
:atom_functions
COPY R0 R2 ; Using String pointer
CALLI R15 @findsym ; Lookup Symbol
LOADUI R3 $NIL ; Using NIL
CMPSKIP.NE R0 R3 ; If NIL was Returned
JUMP @atom_new ; Make a new Symbol
LOAD32 R1 R0 4 ; Make OP->CAR our result
JUMP @atom_done ; We are done (Result is in R1)
:atom_new
LOADR32 R0 @all_symbols ; Get pointer to all symbols
SWAP R0 R1 ; Put pointers in correct order
COPY R3 R0 ; Protect A
CALLI R15 @make_cons ; Make a CONS out of Token and all_symbols
STORER32 R0 @all_symbols ; Update all_symbols
MOVE R1 R3 ; Put result in correct register
:atom_done
MOVE R0 R1 ; Put our result in R0
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
:token_stack
NOP ; Pointer to Unparsed Tokens
;; readobj
;; Breaks up tokens on the token_stack until its empty
;; Recieves Nothing
;; Returns a Cell in R0
:readobj
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
LOADR32 R0 @token_stack ; Get HEAD
LOAD32 R1 R0 8 ; Get HEAD->CDR
STORER32 R1 @token_stack ; Update Token Stack
FALSE R1 ; Using NULL
STORE32 R1 R0 8 ; Set HEAD->CDR
LOAD32 R1 R0 4 ; Get HEAD->CAR
LOADU8 R1 R1 0 ; Get First Char of HEAD->CAR
CMPSKIPI.E R1 40 ; If NOT (
JUMP @readobj_0 ; Atomize HEAD
CALLI R15 @readlist ; Otherwise we want the result of readlist
JUMP @readobj_done
:readobj_0
CALLI R15 @atom ; Let Atom process HEAD for us
:readobj_done
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; readlist
;; CONS up Rest of elements until ) is found
;; Recieves nothing
;; Returns A Cell in R0
:readlist
PUSHR R1 R15 ; Protect R1
LOADR32 R0 @token_stack ; Get HEAD
LOAD32 R1 R0 4 ; Get HEAD->CAR
LOADU8 R1 R1 0 ; Get first Char of HEAD->CAR
CMPSKIPI.E R1 41 ; If NOT )
JUMP @readlist_0 ; CONS up elements
LOAD32 R1 R0 8 ; Get HEAD->CDR
STORER32 R1 @token_stack ; Update token stack
LOADUI R0 $NIL ; Use NIL (Result in R0)
JUMP @readlist_done
:readlist_0
CALLI R15 @readobj ; Have readobj do its work
MOVE R1 R0 ; Put the result in a safe place
CALLI R15 @readlist ; Recursively call self
SWAP R0 R1 ; Put results in proper order
CALLI R15 @make_cons ; Make into a CONS (Result in R0)
:readlist_done
POPR R1 R15 ; Restore R1
RET R15
;; parse
;; Starts the recursive tokenizing and atomizing of input
;; Recieves a string in R0 and its length in R1
;; Returns a list of Cells in R0
:parse
PUSHR R2 R15 ; Protect R2
MOVE R2 R1 ; Put Size in the correct place
MOVE R1 R0 ; Put string pointer in the correct place
CALLI R15 @tokenize ; Get a list of tokens from string
STORER32 R0 @token_stack ; Shove list to token_stack
JUMP.NZ R0 @parse_0 ; If not a NULL list atomize
LOADUI R0 $NIL ; Otherwise we return NIL
JUMP @parse_done ; Result in R0
:parse_0
CALLI R15 @readobj ; Start the atomization (Result in R0)
:parse_done
POPR R2 R15 ; Restore R2
RET R15
;; Our simple malloc function
;; Recieves A number of bytes to allocate in R0
;; Returns a pointer to Segment in R0
:malloc
PUSHR R1 R15 ; Protect R1
LOADR R1 @malloc_pointer ; Get current malloc pointer
;; update malloc pointer
SWAP R0 R1
ADD R1 R0 R1
STORER R1 @malloc_pointer
;; Done
POPR R1 R15 ; Restore R1
RET R15
;; Our static value for malloc pointer
;; Starting at 64KB
:malloc_pointer
'00100000'
;; Switch_Input
;; If R13 is TTY, HALT
;; Else Set input to TTY
:Switch_Input
CMPSKIPI.NE R13 0 ; IF TTY
HALT ; Simply Done
FALSE R13 ; Otherwise switch to TTY
RET R15
;; Readline
;; Using IO source in R13 read a FULL S-expression
;; Returns String pointer in R0 and Length in R1
:Readline
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
PUSHR R5 R15 ; Protect R5
FALSE R0 ; Get where space is free
CALLI R15 @malloc
MOVE R2 R0 ; Preserve pointer
FALSE R3 ; Set index to 0
FALSE R4 ; Set Depth to 0
COPY R1 R13 ; Set desired IO
LOADUI R5 32 ; Keep SPACE for ()
;; Main Loop
:Readline_loop
FGETC ; Get a Byte
CMPSKIPI.G R0 4 ; If EOF
CALLI R15 @Switch_Input ; Do the correct thing
CMPSKIPI.NE R0 13 ; If CR
LOADUI R0 10 ; Replace with LF
CMPSKIPI.NE R13 0 ; Don't display unless TTY
FPUTC ; Display the Char we just pressed
CMPSKIPI.G R0 32 ; If SPACE or below
JUMP @Readline_1
CMPSKIPI.NE R0 59 ; If LINE Comment (;)
JUMP @Readline_0 ; Drop until the end of Line
CMPSKIPI.NE R0 40 ; If (
JUMP @Readline_2 ; Deal with depth and spacing
CMPSKIPI.NE R0 41 ; If )
JUMP @Readline_2 ; Deal with depth and spacing
STOREX8 R0 R2 R3 ; Append to String
ADDUI R3 R3 1 ; Increment Size
JUMP @Readline_loop ; Keep Reading
;; Deal with Line comments
:Readline_0
FGETC ; Get another Byte
CMPSKIPI.NE R0 13 ; Deal with CR
LOADUI R0 10 ; Convert to LF
CMPSKIPI.NE R0 10 ; If LF
JUMP @Readline_loop ; Resume
JUMP @Readline_0 ; Otherwise Keep Looping
;; Deal with Whitespace and Control Chars
:Readline_1
CMPSKIPI.NE R4 0 ; IF Depth 0
JUMP @Readline_done ; We made it to the end
LOADUI R0 32 ; Otherwise convert to SPACE
STOREX8 R0 R2 R3 ; Append to String
ADDUI R3 R3 1 ; Increment Size
JUMP @Readline_loop ; Keep Looping
;; Deal with ()
:Readline_2
CMPSKIPI.NE R0 40 ; If (
ADDUI R4 R4 1 ; Increment Depth
CMPSKIPI.NE R0 41 ; If )
SUBUI R4 R4 1 ; Decrement Depth
STOREX8 R5 R2 R3 ; Put in leading SPACE
ADDUI R3 R3 1 ; Increment Size
STOREX8 R0 R2 R3 ; Put in Char
ADDUI R3 R3 1 ; Increment Size
STOREX8 R5 R2 R3 ; Put in Trailing SPACE
ADDUI R3 R3 1 ; Increment Size
JUMP @Readline_loop ; Resume
;; Clean up
:Readline_done
ADDUI R0 R3 4 ; Pad with 4 NULLs
CALLI R15 @malloc ; Correct Malloc
MOVE R1 R3 ; Put Size in R1
POPR R5 R15 ; Restore R5
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
RET R15
;; Write_Int
;; Writes desired integer to desired IO
;; Recieves Integer in R0 and IO in R1
;; Returns Nothing
:Max_Decimal
'3B9ACA00'
:Write_Int
PUSHR R0 R15 ; Preserve R0
PUSHR R1 R15 ; Preserve R1
PUSHR R2 R15 ; Preserve R2
PUSHR R3 R15 ; Preserve R3
PUSHR R4 R15 ; Preserve R4
PUSHR R5 R15 ; Preserve R5
MOVE R3 R0 ; Move Integer out of the way
JUMP.Z R3 @Write_Int_ZERO ; Deal with Special case of ZERO
JUMP.P R3 @Write_Int_Positive
LOADUI R0 45 ; Using -
FPUTC ; Display leading -
NOT R3 R3 ; Flip into positive
ADDUI R3 R3 1 ; Adjust twos
:Write_Int_Positive
LOADR R2 @Max_Decimal ; Starting from the Top
LOADUI R5 10 ; We move down by 10
FALSE R4 ; Flag leading Zeros
:Write_Int_0
DIVIDE R0 R3 R3 R2 ; Break off top 10
CMPSKIPI.E R0 0 ; If Not Zero
TRUE R4 ; Flip the Flag
JUMP.Z R4 @Write_Int_1 ; Skip leading Zeros
ADDUI R0 R0 48 ; Shift into ASCII
FPUTC ; Print Top
:Write_Int_1
DIV R2 R2 R5 ; Look at next 10
CMPSKIPI.E R2 0 ; If we reached the bottom STOP
JUMP @Write_Int_0 ; Otherwise keep looping
:Write_Int_done
;; Cleanup
POPR R5 R15 ; Restore R5
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
:Write_Int_ZERO
LOADUI R0 48 ; Using Zero
FPUTC ; Display
JUMP @Write_Int_done ; Be done
;; Print_String
;; Prints the string pointed in R0 to IO in R1
;; Recieves string pointer in R0 and IO in R1
;; Returns nothing
:Print_String
PUSHR R0 R15 ; Protect R0
PUSHR R2 R15 ; Protect R2
MOVE R2 R0 ; Get pointer out of the way
:Print_String_loop
LOADU8 R0 R2 0 ; Get Char
CMPSKIPI.NE R0 0 ; If NULL
JUMP @Print_String_done ; Call it done
FPUTC ; Otherwise write the Char
ADDUI R2 R2 1 ; Increment to next Char
JUMP @Print_String_loop ; And Keep looping
:Print_String_done
POPR R2 R15 ; Restore R2
POPR R0 R15 ; Restore R0
RET R15
;; writeobj
;; Outputs to the IO in R12
;; Recieves a Cell list in R0
;; Returns nothing
:writeobj
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
COPY R3 R0 ; Protect HEAD
LOAD32 R2 R0 0 ; Load HEAD->Type
COPY R1 R12 ; Using desired output
CMPSKIPI.NE R2 4 ; If INT
JUMP @writeobj_INT ; Print it and be done
CMPSKIPI.NE R2 8 ; If SYM
JUMP @writeobj_SYM ; Print its string
CMPSKIPI.NE R2 16 ; If CONS
JUMP @writeobj_CONS ; Print it all recursively
CMPSKIPI.NE R2 32 ; If PROC
JUMP @writeobj_PROC ; Print Label
CMPSKIPI.NE R2 64 ; If PRIMOP
JUMP @writeobj_PRIMOP ; Print Label
CMPSKIPI.NE R2 128 ; If ASCII
JUMP @writeobj_ASCII ; Print the Char
;; What the hell is that???
LOADUI R0 $writeobj_Error
FALSE R1
CALLI R15 @Print_String
HALT
:writeobj_Error
"What the fuck was that?"
:writeobj_INT
LOAD32 R0 R0 4 ; Get HEAD->CAR
CALLI R15 @Write_Int ; Write it output
JUMP @writeobj_done ; Be done
:writeobj_CONS
LOADUI R0 40 ; Using (
FPUTC ; Write to desired output
:writeobj_CONS_0
LOAD32 R0 R3 4 ; Get HEAD->CAR
CALLI R15 @writeobj ; Recurse on HEAD->CAR
LOAD32 R3 R3 8 ; Set HEAD to HEAD->CDR
LOADUI R0 $NIL ; Using NIL
CMPJUMPI.E R0 R3 @writeobj_CONS_1
LOAD32 R0 R3 0 ; Get HEAD->type
CMPSKIPI.E R0 16 ; if Not CONS
JUMP @writeobj_CONS_2 ; Deal with inner case
LOADUI R0 32 ; Using SPACE
FPUTC ; Write out desired space
JUMP @writeobj_CONS_0 ; Keep looping
;; Deal with case of op->cdr == nil
:writeobj_CONS_1
LOADUI R0 41 ; Using )
FPUTC ; Write to desired output
JUMP @writeobj_done ; Be Done
:writeobj_CONS_2
COPY R0 R3 ; Using HEAD
CALLI R15 @writeobj ; Recurse
LOADUI R0 41 ; Using )
FPUTC ; Write to desired output
JUMP @writeobj_done ; Be Done
:writeobj_SYM
LOAD32 R0 R3 4 ; Get HEAD->CAR
CALLI R15 @Print_String ; Write it to output
JUMP @writeobj_done ; Be Done
:PRIMOP_String
"#<PRIMOP>"
:writeobj_PRIMOP
LOADUI R0 $PRIMOP_String ; Using the desired string
CALLI R15 @Print_String ; Write it to output
JUMP @writeobj_done ; Be Done
:PROC_String
"#<PROC>"
:writeobj_PROC
LOADUI R0 $PROC_String ; Using the desired string
CALLI R15 @Print_String ; Write it to output
JUMP @writeobj_done ; Be Done
:writeobj_ASCII
LOADU8 R0 R3 7 ; Using bottom 8 bits of HEAD->CAR
FPUTC ; We write our desired output
:writeobj_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; strcmp
;; A simple string compare function
;; Recieves string pointers in R0 and R1
;; Returns result of comparision in R0
:strcmp
;; Preserve registers
PUSHR R2 R15
PUSHR R3 R15
PUSHR R4 R15
;; Setup registers
MOVE R2 R0
MOVE R3 R1
LOADUI R4 0
:cmpbyte
LOADXU8 R0 R2 R4 ; Get a byte of our first string
LOADXU8 R1 R3 R4 ; Get a byte of our second string
ADDUI R4 R4 1 ; Prep for next loop
CMP R1 R0 R1 ; Compare the bytes
CMPSKIPI.E R0 0 ; Stop if byte is NULL
JUMP.E R1 @cmpbyte ; Loop if bytes are equal
;; Done
MOVE R0 R1 ; Prepare for return
;; Restore registers
POPR R4 R15
POPR R3 R15
POPR R2 R15
RET R15
;; findsym
;; Attempts to find a symbol in a CONS list
;; Recieves a string in R0
;; Returns Cell or NIL in R0
:findsym
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
COPY R3 R0 ; Protect String
LOADR R2 @all_symbols ; Get all_symbols
:findsym_loop
LOADUI R0 $NIL ; Using NIL
CMPSKIP.NE R0 R2 ; Check if we reached the end
JUMP @findsym_done ; Use NIL as our result
LOAD32 R0 R2 4 ; Get symlist->CAR
LOAD32 R0 R0 4 ; Get symlist->CAR->CAR
COPY R1 R3 ; Prepare string to find
CALLI R15 @strcmp ; See if we have a match
JUMP.E R0 @findsym_found ; We have a match
LOAD32 R2 R2 8 ; symlist = symlist->CDR
JUMP @findsym_loop ; Keep looping
:findsym_found
MOVE R0 R2 ; We want symlist as our result
:findsym_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; intern
;; Either find symbol or make it
;; Recieves string pointer in R0
;; Returns a Cell pointer in R0
:intern
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
COPY R1 R0 ; Protect String
CALLI R15 @findsym ; Lookup Symbol
CMPSKIPI.NE R0 $NIL ; Determine if Symbol was found
JUMP @intern_found ; And if so, use it
MOVE R0 R1 ; Using our string
CALLI R15 @make_sym ; Make a SYM
COPY R2 R0 ; Protect Cell
LOADR32 R1 @all_symbols ; Get all_symbols
CALLI R15 @make_cons ; CONS together
STORER32 R0 @all_symbols ; Update all_symbols
MOVE R0 R2 ; Restore Cell
JUMP @intern_done ; R0 has our result
:intern_found
LOAD32 R0 R0 4 ; Use op->CAR as our result
:intern_done
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; extend
;; CONS up symbols with an environment
;; Recieves an environment in R0, symbol in R1 and Value in R2
;; Returns a CONS of CONS in R0
:extend
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
SWAP R2 R0 ; Protect the env until we need it
SWAP R0 R1 ; Put Symbol and Value in Correct Order
CALLI R15 @make_cons ; Make inner CONS
MOVE R1 R2 ; Get env now that we need it
CALLI R15 @make_cons ; Make outter CONS
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; multiple_extend
;; Recieves an environment in R0, symbol in R1 and Values in R2
;; Returns an extended environment in R0
:multiple_extend
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
PUSHR R5 R15 ; Protect R5
LOADUI R5 $NIL ; We will need NIL
:multiple_extend_0
CMPJUMPI.E R1 R5 @multiple_extend_done
LOAD32 R3 R1 8 ; Protect SYMS->CDR
LOAD32 R4 R2 8 ; Protect VALS->CDR
LOAD32 R1 R1 4 ; Using SYMS->CAR
LOAD32 R2 R2 4 ; Using VALS->CAR
CALLI R15 @extend ; Extend Environment
MOVE R1 R3 ; USING SYMS->CDR
MOVE R2 R4 ; VALS->CDR
JUMP @multiple_extend_0 ; Iterate until fully extended
:multiple_extend_done
POPR R5 R15 ; Restore R5
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; extend_top
;; Recieves a Symbol in R0 and a Value in R1
;; Returns Value in R0 after extending top
:extend_top
PUSHR R1 R15 ; Protect Val
PUSHR R2 R15 ; Protect R2
CALLI R15 @make_cons ; Make a cons of SYM and VAL
LOADR32 R2 @top_env ; Get TOP_ENV
LOAD32 R1 R2 8 ; Using TOP_ENV->CDR
CALLI R15 @make_cons ; Make final CONS
STORE32 R0 R2 8 ; TOP_ENV->CDR = CONS
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore Val
COPY R0 R1 ; Return Val
RET R15
;; assoc
;; Recieves a Key in R0 and an alist in R1
;; Returns Value if Found or NIL in R0
:assoc
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
LOADUI R4 $NIL ; Using NIL
:assoc_0
CMPJUMPI.E R1 R4 @assoc_done
LOAD32 R2 R1 4 ; ALIST->CAR
LOAD32 R3 R2 4 ; ALIST->CAR->CAR
LOAD32 R1 R1 8 ; ALIST = ALIST->CDR
CMPSKIP.E R0 R3 ; If ALIST->CAR->CAR != KEY
JUMP @assoc_0 ; Iterate using ALIST->CDR
;; Found KEY
MOVE R4 R2 ; Set ALIST->CAR as our return value
:assoc_done
MOVE R0 R4 ; Use whatever in R4 as our return
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; evlis
;; Recieves Expressions in R0 and an Environment in R1
;; Returns the result of Evaluation of those Expressions
;; in respect to the given Environment in R0
:evlis
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
COPY R3 R1 ; Protect ENV
LOAD32 R2 R0 4 ; Protect EXPRS->CAR
LOAD32 R0 R0 8 ; Using EXPRS->CDR
CALLI R15 @evlis ; Recursively Call self Down Expressions
SWAP R0 R2 ; Using EXPRS->CDR
MOVE R1 R3 ; Restore ENV
CALLI R15 @eval ; EVAL
MOVE R1 R2 ; Using result of EVAL and EVLIS
CALLI R15 @make_cons ; Make a CONS of it all
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; progn
;; Recieves Expressions in R0 and an Environment in R1
;; Returns the result of Evaluation of those Expressions
;; in respect to the given Environment in R0
:progn
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
:progn_0
LOAD32 R2 R0 8 ; Protect EXPS->CDR
LOAD32 R0 R0 4 ; Using EXPS->CAR
CALLI R15 @eval ; EVAL
CMPSKIP.E R2 R3 ; If EXPS->CDR NOT NIL
MOVE R0 R2 ; Use EXPS->CDR for next loop
JUMP.Z R2 @progn_0 ; Keep looping if EXPS->CDR isn't NIL
;; Finally broke out of loop
;; Desired result is in R0
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; Apply
;; Recieves a Procedure in R0 and Values in R1
;; Applies the procedure to the values and returns the result in R0
:apply
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOAD32 R3 R0 0 ; Get PROC->TYPE
;; Deal with PRIMOPs
CMPSKIPI.E R3 64 ; If Not PRIMOP
JUMP @apply_0 ; Check NEXT
LOAD32 R3 R0 4 ; Using PROC->CAR
MOVE R0 R1 ; Apply to VALs
CALL R3 R15 ; Call PROC->CAR with VALs
JUMP @apply_done ; Simply Pass the results
;; Deal with Procedures
:apply_0
CMPSKIPI.E R3 32 ; If Not PROC
JUMP @apply_1 ; Abort with FIRE
LOAD32 R3 R0 8 ; Protect PROC->CDR
MOVE R2 R1 ; Put Values in right place
LOAD32 R1 R0 4 ; Using PROC->CAR
LOAD32 R0 R0 12 ; Using PROC->ENV
CALLI R15 @multiple_extend ; Multiple_extend
MOVE R1 R0 ; Put Extended_Env in the right place
MOVE R0 R3 ; Using PROC->CDR
CALLI R15 @progn ; PROGN
JUMP @apply_done ; Simply Pass the results
;; Deal with unknown shit
:apply_1
LOADUI R0 $apply_error ; Using designated Error Message
FALSE R1 ; Using TTY
CALLI R15 @Print_String ; Write Message
HALT ; And bring the FIRE
:apply_error
"Bad argument to apply"
;; Clean up and return
:apply_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; evcond
;; Recieves an Expression in R0 and an Environment in R1
;; Walks down conditions until true one is found and return
;; Desired expression's result in R0
;; if none of the conditions are true, and the result of
;; the COND is undefined
:evcond
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
LOADUI R4 $TEE ; Using TEE
;; Truth Evaluation
:evcond_0
LOAD32 R3 R0 8 ; Protect EXP->CDR
LOAD32 R2 R0 4 ; Protect EXP->CAR
LOAD32 R0 R2 4 ; Using EXP->CAR->CAR
CALLI R15 @eval ; EVAL
CMPJUMPI.E R0 R4 @evcond_1 ; Its true !
MOVE R0 R3 ; Using EXP->CDR
CALLI R15 @evcond ; Recurse
JUMP @evcond_done ; Bail with just NIL
;; Expression Evaluation
:evcond_1
LOAD32 R0 R2 8 ; Get EXP->CAR->CDR
LOAD32 R0 R0 4 ; Using EXP->CAR->CDR->CAR
CALLI R15 @eval ; EVAL
;; Clean up and return
:evcond_done
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; eval
;; Recieves an Expression in R0 and an Environment in R1
;; Evaluates the expression in the given environment and returns
;; The result in R0
:eval
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
LOAD32 R4 R0 0 ; Get EXP->TYPE
;; Deal with special case of Integers
CMPSKIPI.NE R4 4 ; If EXP->TYPE is Integer
JUMP @eval_done ; Simply return what was given
;; Deal with special case of Symbols
CMPSKIPI.E R4 8 ; If EXP->TYPE is NOT Symbol
JUMP @eval_cons ; Move onto next Case
CALLI R15 @process_sym ; process the symbol
JUMP @eval_done ; Return it
;; Deal with special cases of CONS
:eval_cons
CMPSKIPI.E R4 16 ; If EXP->TYPE is NOT CONS
JUMP @eval_proc ; Move onto next Case
CALLI R15 @process_cons ; Deal with all CONS
JUMP @eval_done ; Simply return the result
:eval_proc
CMPSKIPI.E R4 32 ; If EXP->TYPE is NOT PROC
JUMP @eval_primop ; Move onto next Case
JUMP @eval_done
:eval_primop
CMPSKIPI.E R4 64 ; If EXP->TYPE is NOT PRIMOP
JUMP @eval_ascii ; Move onto next Case
:eval_ascii
CMPSKIPI.E R4 128 ; If EXP->TYPE is NOT ASCII
JUMP @eval_error ; Move onto next Case
JUMP @eval_done
:eval_error
LOADUI R0 $eval_error_Message ; Use a specific message to aid debugging
FALSE R1 ; Written to TTY
CALLI R15 @Print_String ; Write NOW
HALT
:eval_error_Message
"EVAL Recieved unknown Object"
;; Result must be in R0 by this point
;; Simply Clean up and return result in R0
:eval_done
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; process_sym
;; Recieves Expression in R0 and an Environment in R1
;; Returns symbol in R0
:process_sym
CALLI R15 @assoc ; ASSOC to get tmp
CMPSKIPI.NE R0 $NIL ; If NIL is returned
JUMP @process_bad_Symbol ; Burn with FIRE
LOAD32 R0 R0 8 ; Return tmp->CDR
RET R15
:process_bad_Symbol
LOADUI R0 $sym_unbound ; Using the designated Error message
FALSE R1 ; Using TTY
CALLI R15 @Print_String ; Written for the user
HALT ; Simply toss the rest into the fire
:sym_unbound
"Unbound symbol"
;; process_if
;; Recieves Expression in R0 and an Environment in R1
;; Returns the evaluation of the expression if true in R0
;; Or the evaluation of the CDR of the expression
:process_if
PUSHR R2 R15 ; Protect R2
LOAD32 R2 R0 8 ; Protect EXP->CDR
LOAD32 R0 R2 4 ; Using EXP->CDR->CAR
CALLI R15 @eval ; Recurse to get truth
CMPSKIPI.NE R0 $NIL ; If Result was NOT NIL
LOAD32 R2 R2 8 ; Update to EXP->CDR->CDR
LOAD32 R0 R2 8 ; Get EXP->CDR->CDR
LOAD32 R0 R0 4 ; Using EXP->CDR->CDR->CAR
CALLI R15 @eval ; Recurse to get result
POPR R2 R15 ; Restore R2
RET R15
;; process_setb
;; Recieves Expression in R0 and an Environment in R1
;; Sets the desired variable to desired value/type
;; Returns the value/type in R0
:process_setb
PUSHR R2 R15 ; Protect R2
LOAD32 R2 R0 8 ; Protect EXP->CDR
LOAD32 R0 R2 8 ; Get EXP->CDR->CDR
LOAD32 R0 R0 4 ; Using EXP->CDR->CDR->CAR
CALLI R15 @eval ; Recurse to get New value
SWAP R0 R2 ; Protect New Value
LOAD32 R0 R0 4 ; Using EXP->CDR->CAR
CALLI R15 @assoc ; Get the associated Symbol
STORE32 R2 R0 8 ; SET Pair->CDR to New Value
MOVE R0 R2 ; Using New Value
POPR R2 R15 ; Restore R2
RET R15
;; process_cons
;; Recieves Expression in R0 and an Environment in R1
;; Returns the evaluation of whatever special used or
;; The application of the evaluation in R0
:process_cons
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
LOAD32 R4 R0 4 ; Using EXP->CAR
LOADUI R3 $s_if ; Using s_if
CMPJUMPI.NE R4 R3 @process_cons_cond
CALLI R15 @process_if ; deal with special case of If statements
JUMP @process_cons_done ; Return it
:process_cons_cond
LOADUI R3 $s_cond ; Using s_cond
CMPJUMPI.NE R4 R3 @process_cons_begin
;; Deal with special case of COND statements
LOAD32 R0 R0 8 ; Using EXP->CDR
CALLI R15 @evcond ; EVCOND
JUMP @process_cons_done ; Simply use it's result
:process_cons_begin
LOADUI R3 $s_begin ; Using s_begin
CMPJUMPI.NE R4 R3 @process_cons_lambda
;; Deal with special case of BEGIN statements
LOAD32 R0 R0 8 ; Using EXP->CDR
CALLI R15 @progn ; PROGN
JUMP @process_cons_done ; Simply use it's result
:process_cons_lambda
LOADUI R3 $s_lambda ; Using s_lambda
CMPJUMPI.NE R4 R3 @process_cons_quote
;; Deal with special case of lambda statements
MOVE R2 R1 ; Put ENV in the right place
LOAD32 R1 R0 8 ; Get EXP->CDR
LOAD32 R0 R1 4 ; Using EXP->CDR->CAR
LOAD32 R1 R1 8 ; Using EXP->CDR->CDR
CALLI R15 @make_proc ; MAKE_PROC
JUMP @process_cons_done ; Simply return its result
:process_cons_quote
LOADUI R3 $s_quote ; Using s_quote
CMPJUMPI.NE R4 R3 @process_cons_define
;; Deal with special case of quote statements
LOAD32 R0 R0 8 ; Get EXP->CDR
LOAD32 R0 R0 4 ; Using EXP->CDR->CAR
JUMP @process_cons_done ; Simply use it as the result
:process_cons_define
LOADUI R3 $s_define ; Using s_define
CMPJUMPI.NE R4 R3 @process_cons_set
;; Deal with special case of Define statements
LOAD32 R2 R0 8 ; Using EXP->CDR
LOAD32 R0 R2 8 ; Get EXP->CDR->CDR
LOAD32 R0 R0 4 ; Using EXP->CDR->CDR->CAR
CALLI R15 @eval ; Recurse to figure out what it is
MOVE R1 R0 ; Change Environment
LOAD32 R0 R2 4 ; Using EXP->CDR->CAR
CALLI R15 @extend_top ; EXTEND_TOP
JUMP @process_cons_done ; Simply use what was returned
:process_cons_set
LOADUI R3 $s_setb ; Using s_setb
CMPJUMPI.NE R4 R3 @process_cons_apply
CALLI R15 @process_setb ; Deal with special case of SET statements
JUMP @process_cons_done ; Simply Return Result
:process_cons_apply
;; Deal with the last option for a CONS, APPLY
LOAD32 R2 R0 4 ; Protect EXP->CAR
LOAD32 R0 R0 8 ; Using EXP->CDR
CALLI R15 @evlis ; EVLIS
SWAP R0 R2 ; Protect EVLIS result
CALLI R15 @eval ; Recurse to figure out what to APPLY
MOVE R1 R2 ; Put EVLIS result in right place
CALLI R15 @apply ; Apply what was found to the EVLIS result
:process_cons_done
POPR R4 R15 ; Restore R2
POPR R3 R15 ; Restore R2
POPR R2 R15 ; Restore R2
RET R15
;; prim_sum
;; Recieves a list in R0
;; Adds all values and returns a Cell with result in R0
:prim_sum_String
"+"
:prim_sum
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
FALSE R2 ; Initialize our SUM at 0
:prim_sum_0
CMPJUMPI.E R0 R3 @prim_sum_done
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
ADD R2 R2 R1 ; sum = sum + value
JUMP @prim_sum_0 ; Go to next list item
:prim_sum_done
MOVE R0 R2 ; Put SUM in right spot
CALLI R15 @make_int ; Get our Cell
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_sub
;; Recieves a list in R0
;; Subtracts all of the values and returns a Cell with the result in R0
:prim_sub_String
"-"
:prim_sub
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting SUM
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_sub_0
CMPJUMPI.E R0 R3 @prim_sub_done
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
SUB R2 R2 R1 ; sum = sum - value
JUMP @prim_sub_0 ; Go to next list item
:prim_sub_done
MOVE R0 R2 ; Put SUM in right spot
CALLI R15 @make_int ; Get our Cell
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_prod
;; Recieves a list in R0
;; Multiplies all of the values and returns a Cell with the result in R0
:prim_prod_String
"*"
:prim_prod
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOADUI R2 1 ; Initialize our Product at 1
:prim_prod_0
CMPJUMPI.E R0 R3 @prim_prod_done
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
MUL R2 R2 R1 ; sum = sum + value
JUMP @prim_prod_0 ; Go to next list item
:prim_prod_done
MOVE R0 R2 ; Put SUM in right spot
CALLI R15 @make_int ; Get our Cell
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_div
;; Recieves a list in R0
;; Divides all of the values and returns a Cell with the result in R0
:prim_div_String
"/"
:prim_div
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting SUM
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_div_0
CMPJUMPI.E R0 R3 @prim_div_done
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
DIV R2 R2 R1 ; sum = sum - value
JUMP @prim_div_0 ; Go to next list item
:prim_div_done
MOVE R0 R2 ; Put result in right spot
CALLI R15 @make_int ; Get our Cell
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_mod
;; Recieves a list in R0
;; Remainders all of the values and returns a Cell with the result in R0
:prim_mod_String
"mod"
:prim_mod
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting SUM
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_mod_0
CMPJUMPI.E R0 R3 @prim_mod_done
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
MOD R2 R2 R1 ; sum = sum - value
JUMP @prim_mod_0 ; Go to next list item
:prim_mod_done
MOVE R0 R2 ; Put result in right spot
CALLI R15 @make_int ; Get our Cell
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_and
;; Recieves a list in R0
;; ANDs all of the values and returns a Cell with the result in R0
:prim_and_String
"and"
:prim_and
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
LOADUI R4 $TEE ; Using TEE
LOADUI R3 $NIL ; Using NIL
:prim_and_0
CMPJUMPI.E R0 R3 @prim_and_done
LOAD32 R2 R0 4 ; Get ARGS->CAR
CMPJUMPI.NE R2 R4 @prim_and_1
LOAD32 R0 R0 8 ; Get ARGS->CDR
JUMP @prim_and_0 ; Go to next list item
:prim_and_1
COPY R2 R3 ; Return NIL
:prim_and_done
MOVE R0 R2 ; Put result in correct location
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_or
;; Recieves a list in R0
;; ORs all of the values and returns a Cell with the result in R0
:prim_or_String
"or"
:prim_or
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
LOADUI R4 $TEE ; Using TEE
LOADUI R3 $NIL ; Using NIL
:prim_or_0
CMPJUMPI.E R0 R3 @prim_or_1
LOAD32 R2 R0 4 ; Get ARGS->CAR
CMPJUMPI.E R2 R4 @prim_or_done
LOAD32 R0 R0 8 ; Get ARGS->CDR
JUMP @prim_or_0 ; Go to next list item
:prim_or_1
COPY R2 R3 ; Couldn't find a true
:prim_or_done
MOVE R0 R2 ; Put result in correct location
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_not
;; Recieves a list in R0
;; NOTs first of the values and returns a Cell with the result in R0
:prim_not_String
"not"
:prim_not
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
LOAD32 R0 R0 4 ; Get ARGS->CAR
CMPSKIPI.E R0 $TEE ; If not TEE
JUMP @prim_not_0 ; Return TEE
LOADUI R0 $NIL ; Otherwise return NIL
JUMP @prim_not_done ; Return our NIL
:prim_not_0
LOADUI R0 $TEE ; Make TEE
:prim_not_done
RET R15
;; prim_numgt
;; Recieves a list in R0
;; Compares values and returns a Cell with the result in R0
:prim_numgt_String
">"
:prim_numgt
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting Value
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_numgt_0
CMPJUMPI.E R0 R3 @prim_numgt_1
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
CMPJUMPI.LE R2 R1 @prim_numgt_2
MOVE R2 R1 ; Prepare for next loop
JUMP @prim_numgt_0 ; Go to next list item
:prim_numgt_1
LOADUI R0 $TEE ; Return TEE
JUMP @prim_numgt_done ; Be done
:prim_numgt_2
LOADUI R0 $NIL ; Return NIL
:prim_numgt_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_numge
;; Recieves a list in R0
;; Compares values and returns a Cell with the result in R0
:prim_numge_String
">="
:prim_numge
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting Value
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_numge_0
CMPJUMPI.E R0 R3 @prim_numge_1
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
CMPJUMPI.L R2 R1 @prim_numge_2
MOVE R2 R1 ; Prepare for next loop
JUMP @prim_numge_0 ; Go to next list item
:prim_numge_1
LOADUI R0 $TEE ; Return TEE
JUMP @prim_numge_done ; Be done
:prim_numge_2
LOADUI R0 $NIL ; Return NIL
:prim_numge_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_numeq
;; Recieves a list in R0
;; Compares values and returns a Cell with the result in R0
:prim_numeq_String
"="
:prim_numeq
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting Value
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_numeq_0
CMPJUMPI.E R0 R3 @prim_numeq_1
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
CMPJUMPI.NE R2 R1 @prim_numeq_2
MOVE R2 R1 ; Prepare for next loop
JUMP @prim_numeq_0 ; Go to next list item
:prim_numeq_1
LOADUI R0 $TEE ; Return TEE
JUMP @prim_numge_done ; Be done
:prim_numeq_2
LOADUI R0 $NIL ; Return NIL
:prim_numeq_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_numle
;; Recieves a list in R0
;; Compares values and returns a Cell with the result in R0
:prim_numle_String
"<="
:prim_numle
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting Value
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_numle_0
CMPJUMPI.E R0 R3 @prim_numle_1
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
CMPJUMPI.G R2 R1 @prim_numle_2
MOVE R2 R1 ; Prepare for next loop
JUMP @prim_numle_0 ; Go to next list item
:prim_numle_1
LOADUI R0 $TEE ; Return TEE
JUMP @prim_numle_done ; Be done
:prim_numle_2
LOADUI R0 $NIL ; Return NIL
:prim_numle_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_numlt
;; Recieves a list in R0
;; Compares values and returns a Cell with the result in R0
:prim_numlt_String
"<"
:prim_numlt
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
LOAD32 R2 R0 4 ; Get ARGS->CAR
LOAD32 R2 R2 4 ; Using ARGS->CAR->CAR as starting Value
LOAD32 R0 R0 8 ; Using ARGS->CDR as args
:prim_numlt_0
CMPJUMPI.E R0 R3 @prim_numlt_1
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R1 R1 4 ; Get ARGS->CAR->CAR
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
CMPJUMPI.GE R2 R1 @prim_numlt_2
MOVE R2 R1 ; Prepare for next loop
JUMP @prim_numlt_0 ; Go to next list item
:prim_numlt_1
LOADUI R0 $TEE ; Return TEE
JUMP @prim_numlt_done ; Be done
:prim_numlt_2
LOADUI R0 $NIL ; Return NIL
:prim_numlt_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
RET R15
;; prim_listp
;; Recieves a list in R0
;; Compares values and returns a Cell with the result in R0
:prim_listp_String
"list?"
:prim_listp
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
LOAD32 R0 R0 4 ; Get ARGS->CAR
LOAD32 R0 R0 0 ; Get ARGS->CAR->TYPE
CMPSKIPI.NE R0 16 ; If CONS
JUMP @prim_listp_0 ; Return TEE
LOADUI R0 $NIL ; Otherwise return NIL
JUMP @prim_listp_done ; Return our NIL
:prim_listp_0
LOADUI R0 $TEE ; Make TEE
:prim_listp_done
RET R15
;; prim_display
;; Recieves argslist in R0
;; Outputs to TTY and returns TEE
:prim_display_String
"display"
:prim_display
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
PUSHR R4 R15 ; Protect R4
LOADUI R4 $NIL ; Using NIL
FALSE R1 ; Set to use TTY
:prim_display_0
CMPJUMPI.E R0 R4 @prim_display_done
LOAD32 R3 R0 4 ; Get ARGS->CAR
LOAD32 R2 R3 0 ; Get ARGS->CAR->TYPE
SWAP R0 R3 ; Protect ARGS
CMPSKIPI.NE R2 4 ; If INT
CALLI R15 @prim_display_INT ; Print the value
CMPSKIPI.NE R2 8 ; If SYM
CALLI R15 @prim_display_SYM ; Print the string
CMPSKIPI.NE R2 16 ; If CONS
CALLI R15 @prim_display ; Recurse
CMPSKIPI.NE R2 128 ; If ASCII
CALLI R15 @prim_display_ASCII ; Just print the last Char
LOAD32 R0 R3 8 ; Get ARGS->CDR
JUMP @prim_display_0 ; Loop until we hit NIL
:prim_display_done
POPR R4 R15 ; Restore R4
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
LOADUI R0 $TEE ; Return TEE
RET R15
;; prim_display_INT
;; Recieves an INT CELL in R0 and desired Output in R1
;; Outputs value and returns
:prim_display_INT
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
LOAD32 R0 R0 4 ; Get ARG->CAR
CALLI R15 @Write_Int ; Write it
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; prim_display_SYM
;; Recieves a SYM CELL in R0 and desired Output in R1
;; Outputs string and returns
:prim_display_SYM
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
LOAD32 R0 R0 4 ; Get ARG->CAR
CALLI R15 @Print_String ; Print the string
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; prim_display_ASCII
;; Recieves an ASCII CELL in R0 and desired Output in R1
;; Outputs Last CHAR and returns
:prim_display_ASCII
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
LOADU8 R0 R0 7 ; Get ARG->CAR [bottom 8 bits]
FPUTC ; Display desired CHAR
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; prim_freecell
;; Recieves either NIL or a list in R0
;; If NIL displays header, otherwise just returns number of free cells in R0
:prim_freecell_String
"free_mem"
:prim_freecell
PUSHR R1 R15 ; Protect R1
CMPSKIPI.E R0 $NIL ; If NOT NIL
JUMP @prim_freecell_0 ; Skip message
LOADUI R0 $prim_freecell_Message
COPY R1 R12 ; Using Selected Output
CALLI R15 @Print_String ; Display our header
:prim_freecell_0
CALLI R15 @cells_remaining ; Get number of remaining Cells
CALLI R15 @make_int ; Convert integer in R0 to a Cell
:prim_freecell_done
POPR R1 R15 ; Restore R1
RET R15
:prim_freecell_Message
"Remaining Cells: "
;; prim_ascii
;; Recieves a list in R0
;; Converts all integers to ASCII
:prim_ascii_String
"ascii!"
:prim_ascii
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADUI R3 $NIL ; Using NIL
:prim_ascii_0
CMPJUMPI.E R0 R3 @prim_ascii_done
LOAD32 R1 R0 4 ; Get ARGS->CAR
LOAD32 R2 R1 0 ; Get ARGS->CAR->TYPE
LOAD32 R0 R0 8 ; Set ARGS to ARGS->CDR
CMPSKIPI.NE R2 4 ; If Type is INT
JUMP @prim_ascii_1 ; Convert to ASCII
JUMP @prim_ascii_0 ; Go to next list item
:prim_ascii_1
LOADUI R2 128 ; Using Type ASCII
STORE32 R2 R1 0 ; Update ARGS->CAR->TYPE
JUMP @prim_ascii_0 ; Keep looping
:prim_ascii_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; prim_halt
;; Simply HALTS
:prim_halt_String
"HALT"
:prim_halt
HALT
;; prim_list
;; Simply returns the argument list passed to it in R0
:prim_list_String
"list"
:prim_list
RET R15
;; prim_cons
;; Recieves an arglist in R0 and returns a CONS in R0
:prim_cons_String
"cons"
:prim_cons
PUSHR R1 R15 ; Protect R1
LOAD32 R1 R0 8 ; Get ARGS->CDR
LOAD32 R1 R1 4 ; Use ARGS->CDR->CAR
LOAD32 R0 R0 4 ; Use ARGS->CAR
CALLI R15 @make_cons ; MAKE_CONS
POPR R1 R15 ; Restore R1
RET R15
;; prim_car
;; Recieves an arglist in R0 and returns the CAR in R0
:prim_car_String
"car"
:prim_car
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
LOAD32 R0 R0 4 ; Get ARGS->CAR
LOAD32 R0 R0 4 ; Using ARGS->CAR->CAR
RET R15
;; prim_cdr
;; Recieves an arglist in R0 and returns the CDR in R0
:prim_cdr_String
"cdr"
:prim_cdr
CMPSKIPI.NE R0 $NIL ; If NIL Expression
RET R15 ; Just get the Hell out
LOAD32 R0 R0 4 ; Get ARGS->CAR
LOAD32 R0 R0 8 ; Using ARGS->CAR->CDR
RET R15
;; spinup
;; Recieves a symbol in R0 and a primitive in R1
;; Returns nothing but CONS both to all_symbols and top_env
:spinup
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
COPY R3 R0 ; Protect SYM
MOVE R2 R1 ; Put PRIM in right Spot
LOADR R1 @all_symbols ; Get ALL_SYMBOLS
CALLI R15 @make_cons ; MAKE_CONS
STORER R0 @all_symbols ; Update ALL_SYMBOLS
MOVE R1 R3 ; Restore SYM
LOADR R0 @top_env ; Get TOP_ENV
CALLI R15 @extend ; EXTEND
STORER R0 @top_env ; Update TOP_ENV
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; Special symbols
;; NIL Object
:NIL
'00000008' ; A Symbol
&NIL_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:NIL_String
"nil"
;; TEE Object
:TEE
'00000008' ; A Symbol
&TEE_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:TEE_String
"#t"
;; Quote Object
:s_quote
'00000008' ; A Symbol
&s_quote_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:s_quote_String
"quote"
;; IF Object
:s_if
'00000008' ; A Symbol
&s_if_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:s_if_String
"if"
;; COND Object
:s_cond
'00000008' ; A Symbol
&s_cond_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:s_cond_String
"cond"
;; Lambda Object
:s_lambda
'00000008' ; A Symbol
&s_lambda_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:s_lambda_String
"lambda"
;; Define Object
:s_define
'00000008' ; A Symbol
&s_define_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:s_define_String
"define"
;; SET Object
:s_setb
'00000008' ; A Symbol
&s_setb_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:s_setb_String
"set!"
;; Begin Object
:s_begin
'00000008' ; A Symbol
&s_begin_String ; Pointer to string
'00000000' ; NUL CDR
'00000000' ; NUL ENV
:s_begin_String
"begin"
;; Globals of interest
:all_symbols
&all_symbols_init
:all_symbols_init
'00000010' ; A CONS
&NIL ; Pointer to NIL
&NIL ; Pointer to NIL
'00000000' ; NULL
:top_env
&top_env_init_1
:top_env_init_0
'00000010' ; A CONS
&NIL ; Pointer to NIL
&NIL ; Pointer to NIL
'00000000' ; NULL
:top_env_init_1
'00000010' ; A CONS
&top_env_init_0 ; Pointer to CONS of NIL
&NIL ; Pointer to NIL
'00000000' ; NULL
:free_cells
NOP ; Start with NULL
;; Global init function
;; Recieves nothing
;; Returns nothing
;; sets up all_symbols and top_env
:init_sl3
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
;; Add Eval Specials
LOADUI R0 $TEE ; Get TEE
COPY R1 R0 ; Duplicate TEE
CALLI R15 @spinup ; SPINUP
LOADUI R0 $s_quote ; Get s_quote
COPY R1 R0 ; Duplicate s_quote
CALLI R15 @spinup ; SPINUP
LOADUI R0 $s_if ; Get s_if
COPY R1 R0 ; Duplicate s_if
CALLI R15 @spinup ; SPINUP
LOADUI R0 $s_cond ; Get s_cond
COPY R1 R0 ; Duplicate s_cond
CALLI R15 @spinup ; SPINUP
LOADUI R0 $s_lambda ; Get s_lambda
COPY R1 R0 ; Duplicate s_lambda
CALLI R15 @spinup ; SPINUP
LOADUI R0 $s_define ; Get s_define
COPY R1 R0 ; Duplicate s_define
CALLI R15 @spinup ; SPINUP
LOADUI R0 $s_setb ; Get s_setb
COPY R1 R0 ; Duplicate s_setb
CALLI R15 @spinup ; SPINUP
LOADUI R0 $s_begin ; Get s_begin
COPY R1 R0 ; Duplicate s_if
CALLI R15 @spinup ; SPINUP
;; Add Primitive Specials
LOADUI R0 $prim_sum ; Using PRIM_SUM
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_sum_String ; Using PRIM_SUM_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_sub ; Using PRIM_SUB
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_sub_String ; Using PRIM_SUB_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_prod ; Using PRIM_PROD
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_prod_String ; Using PRIM_PROD_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_div ; Using PRIM_DIV
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_div_String ; Using PRIM_DIV_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_mod ; Using PRIM_MOD
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_mod_String ; Using PRIM_MOD_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_and ; Using PRIM_AND
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_and_String ; Using PRIM_AND_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_or ; Using PRIM_OR
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_or_String ; Using PRIM_OR_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_not ; Using PRIM_NOT
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_not_String ; Using PRIM_NOT_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_numgt ; Using PRIM_NUMGT
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_numgt_String ; Using PRIM_NUMGT_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_numge ; Using PRIM_NUMGE
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_numge_String ; Using PRIM_NUMGE_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_numeq ; Using PRIM_NUMEQ
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_numeq_String ; Using PRIM_NUMEQ_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_numle ; Using PRIM_NUMLE
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_numle_String ; Using PRIM_NUMLE_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_numlt ; Using PRIM_NUMLT
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_numlt_String ; Using PRIM_NUMLT_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_listp ; Using PRIM_LISTP
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_listp_String ; Using PRIM_LISTP_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_display ; Using PRIM_DISPLAY
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_display_String ; Using PRIM_DISPLAY_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_freecell ; Using PRIM_FREECELL
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_freecell_String ; Using PRIM_FREECELL_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_ascii ; Using PRIM_ASCII
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_ascii_String ; Using PRIM_ASCII_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_halt ; Using PRIM_HALT
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_halt_String ; Using PRIM_HALT_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_list ; Using PRIM_list
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_list_String ; Using PRIM_LIST_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_cons ; Using PRIM_CONS
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_cons_String ; Using PRIM_CONS_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_car ; Using PRIM_CAR
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_car_String ; Using PRIM_CAR_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
LOADUI R0 $prim_cdr ; Using PRIM_CDR
CALLI R15 @make_prim ; MAKE_PRIM
MOVE R1 R0 ; Put Primitive in correct location
LOADUI R0 $prim_cdr_String ; Using PRIM_CDR_STRING
CALLI R15 @make_sym ; MAKE_SYM
CALLI R15 @spinup ; SPINUP
;; Clean up
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; Left_to_take
;; The number of cells_remaining
:left_to_take
NOP
;; cells_remaining
;; Recieves nothing and returns number of remaining cells in R0
:cells_remaining
LOADR R0 @left_to_take ; Get number of cells left
RET R15
;; update_remaining
;; Recieves nothing
;; Returns nothing
;; Updates left_to_take via counting
:update_remaining
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
LOADR R0 @free_cells ; Get FREE_CELLS
FALSE R1 ; Set Count to 0
:update_remaining_0
JUMP.Z R0 @update_remaining_done
ADDUI R1 R1 1 ; Increment by 1
LOAD32 R0 R0 8 ; get I->CDR
JUMP @update_remaining_0 ; Keep looping til NULL
:update_remaining_done
STORER R1 @left_to_take ; update left_to_take
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; gc_block_start
:gc_block_start
&Start_CONS
;; gc_block_end
:gc_block_end
'000FFFF0'
;; reclaim_marked
;; Recieves nothing
;; Returns nothing
;; Reclaims and updates free_cells
:reclaim_marked
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADR R0 @gc_block_start ; Using GC_BLOCK_START
LOADR R1 @gc_block_end ; Using GC_BLOCK_END
:reclaim_marked_0
CMPJUMPI.GE R0 R1 @reclaim_marked_done
LOAD32 R2 R0 0 ; Get I->TYPE
ANDI R2 R2 2 ; AND with MARKED
JUMP.Z R2 @reclaim_marked_1 ; Deal with MARKED CELLS or jump on NULL
;; Deal with Marked
LOADUI R2 1 ; Using FREE
STORE32 R2 R0 0 ; Set I->TYPE to FREE
FALSE R2 ; USING NULL
LOADR R3 @free_cells ; Get FREE_CELLS
STORE32 R2 R0 4 ; SET I->CAR to NULL
STORE32 R3 R0 8 ; SET I->CDR to FREE_CELLS
STORE32 R2 R0 12 ; SET I->ENV to NULL
STORER R0 @free_cells ; Update FREE_CELLS to I
;; Deal with unmarked
:reclaim_marked_1
ADDUI R0 R0 16 ; Increment I by the size of a CELL
JUMP @reclaim_marked_0 ; Iterate on next CELL
:reclaim_marked_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; mark_all_cells
;; Recieves nothing
;; Returns nothing
;; Marks all unfree cells
:mark_all_cells
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
PUSHR R3 R15 ; Protect R3
LOADR R0 @gc_block_start ; Using GC_BLOCK_START
LOADR R1 @gc_block_end ; Using GC_BLOCK_END
:mark_all_cells_0
CMPJUMPI.GE R0 R1 @mark_all_cells_done
LOAD32 R2 R0 0 ; Get I->TYPE
CMPSKIPI.NE R2 1 ; If NOT FREE
JUMP @mark_all_cells_1 ; Move onto the Next
;; Mark non-free cell
ORI R2 R2 2 ; Add MARK
STORE32 R2 R0 0 ; Write out MARK
:mark_all_cells_1
ADDUI R0 R0 16 ; Increment I by the size of a CELL
JUMP @mark_all_cells_0 ; Iterate on next CELL
:mark_all_cells_done
POPR R3 R15 ; Restore R3
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; unmark_cells
;; Recieves a List in R0
;; Returns nothing
;; Unmarks all connected Cells
:unmark_cells
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
PUSHR R2 R15 ; Protect R2
LOADUI R2 2 ; GET MARKED
NOT R2 R2 ; Use ~MARKED
:unmark_cells_0
JUMP.Z R0 @unmark_cells_done
LOAD32 R1 R0 0 ; Get I->TYPE
AND R1 R1 R2 ; Remove MARK
STORE32 R1 R0 0 ; Store the cleaned type
;; Deal with CONS
CMPSKIPI.NE R1 16 ; If A CONS
JUMP @unmark_cells_proc ; Deal with it
;; Deal with PROC
CMPSKIPI.NE R1 32 ; If A PROC
JUMP @unmark_cells_proc ; Deal with it
;; Everything else
JUMP @unmark_cells_1 ; Move onto NEXT
:unmark_cells_proc
LOAD32 R1 R0 4 ; Using list->CAR
SWAP R0 R1 ; Protect list
CALLI R15 @unmark_cells ; Recurse until the ends
SWAP R0 R1 ; Put list back
:unmark_cells_1
LOAD32 R0 R0 8 ; Get list->CDR
JUMP @unmark_cells_0 ; Keep going down list
:unmark_cells_done
POPR R2 R15 ; Restore R2
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; garbage_collect
;; Recieves nothing
;; Returns nothing
;; The Core of Garbage Collection
:garbage_collect
PUSHR R0 R15 ; Protect R0
CALLI R15 @mark_all_cells ; MARK_ALL_CELLS
LOADR R0 @all_symbols ; Using ALL_SYMBOLS
CALLI R15 @unmark_cells ; UNMARK ALL_SYMBOLS
LOADR R0 @top_env ; Using TOP_ENV
CALLI R15 @unmark_cells ; UNMARK TOP_ENV
CALLI R15 @reclaim_marked ; RECLAIM_MARKED
CALLI R15 @update_remaining ; Fix the Count
POPR R0 R15 ; Restore R0
RET R15
;; garbage_init
;; Recieves nothing
;; Returns nothing
;; Initializes Garbage Heap
:garbage_init
PUSHR R0 R15 ; Protect R0
PUSHR R1 R15 ; Protect R1
LOADR R0 @gc_block_start ; Get Starting Offset
ANDI R0 R0 0xF ; We only need the buttom 4 Bits
LOADR R1 @gc_block_end ; Get End Address
ADD R1 R1 R0 ; Add the Offset
SUBUI R1 R1 16 ; Shift Back Down
STORER R1 @gc_block_end ; Update Block End
CALLI R15 @mark_all_cells ; MARK_ALL_CELLS
CALLI R15 @reclaim_marked ; RECLAIM_MARKED
CALLI R15 @update_remaining ; Fix the Count
POPR R1 R15 ; Restore R1
POPR R0 R15 ; Restore R0
RET R15
;; pop_cons
;; Recieves nothing
;; Returns a Free CONS in R0
;; Updates left_to_take
:pop_cons
PUSHR R1 R15 ; Protect R1
LOADR R0 @free_cells ; Get First Free Cell
JUMP.Z R0 @pop_cons_error ; If NULL BURN with FIRE
LOAD32 R1 R0 8 ; Get I->CDR
STORER R1 @free_cells ; Update FREE_CELLS
FALSE R1 ; Using NULL
STORE32 R1 R0 8 ; SET I->CDR to NULL
LOADR R1 @left_to_take ; Get LEFT_TO_TAKE
SUBUI R1 R1 1 ; Decrement by 1
STORER R1 @left_to_take ; Update LEFT_TO_TAKE
POPR R1 R15 ; Restore R1
RET R15
:pop_cons_error
LOADUI R0 $pop_cons_Message ; Using Message
FALSE R1 ; Using TTY
CALLI R15 @Print_String ; Display ERROR
HALT ; Burn with FIRE
:pop_cons_Message
"OOOPS we ran out of cells"
;; make_int
;; Recieves an Integer in R0
;; Returns a CELL in R0
:make_int
PUSHR R1 R15 ; Protect R1
MOVE R1 R0 ; Protect Integer
CALLI R15 @pop_cons ; Get a CELL
STORE32 R1 R0 4 ; Set C->CAR
LOADUI R1 4 ; Using INT
STORE32 R1 R0 0 ; Set C->TYPE
POPR R1 R15 ; Restore R1
RET R15
;; make_sym
;; Recieves a string pointer in R0
;; Returns a Cell in R0
:make_sym
PUSHR R1 R15 ; Protect R1
MOVE R1 R0 ; Protect String Pointer
CALLI R15 @pop_cons ; Get a CELL
STORE32 R1 R0 4 ; Set C->CAR
LOADUI R1 8 ; Using SYM
STORE32 R1 R0 0 ; Set C->TYPE
POPR R1 R15 ; Restore R1
RET R15
;; make_cons
;; Recieves a Cell in R0 and R1
;; Returns a combined Cell in R0
:make_cons
PUSHR R2 R15 ; Protect R2
MOVE R2 R0 ; Protect CELL A
CALLI R15 @pop_cons ; Get a CELL
STORE32 R2 R0 4 ; Set C->CAR
STORE32 R1 R0 8 ; SET C->CDR
LOADUI R2 16 ; Using CONS
STORE32 R2 R0 0 ; Set C->TYPE
POPR R2 R15 ; Restore R2
RET R15
;; make_proc
;; Recieves Cells in R0, R1 and R2
;; Returns a combined Cell in R0
:make_proc
PUSHR R3 R15 ; Protect R3
MOVE R3 R0 ; Protect CELL
CALLI R15 @pop_cons ; Get a CELL
STORE32 R3 R0 4 ; Set C->CAR
STORE32 R1 R0 8 ; Set C->CDR
STORE32 R2 R0 12 ; Set C->ENV
LOADUI R3 32 ; Using PROC
STORE32 R3 R0 0 ; Set C->TYPE
POPR R3 R15 ; Restore R3
RET R15
;; make_prim
;; Recieves pointer to function in R0
;; Returns a Cell in R0
:make_prim
PUSHR R1 R15 ; Protect R1
MOVE R1 R0 ; Protect Function Pointer
CALLI R15 @pop_cons ; Get a CELL
STORE32 R1 R0 4 ; Set C->CAR
LOADUI R1 64 ; Using PRIMOP
STORE32 R1 R0 0 ; Set C->TYPE
POPR R1 R15 ; Restore R1
RET R15
;; CONS starts at the end of the program
:Start_CONS