; 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 . :start ;; We will be using R13 for storage of Head ;; We will be using R14 for our condition codes LOADUI R15 $stack ; Put stack at end of program ;; Main program ;; Reads contents of Tape_01 and applies all Definitions ;; Writes results to Tape_02 ;; Accepts no arguments and HALTS when done :main ;; Prep TAPE_01 LOADUI R0 0x1100 FOPEN_READ FALSE R13 ; Head is NULL MOVE R1 R0 ; Read Tape_01 FALSE R14 ; We haven't yet reached EOF :main_0 CALLI R15 @Tokenize_Line ; Call Tokenize_Line JUMP.Z R14 @main_0 ; Until we reach EOF ;; Done reading File LOADUI R0 0x1100 ; Close TAPE_01 FCLOSE COPY R0 R13 ; Prepare for function CALLI R15 @Identify_Macros ; Tag all nodes that are macros CALLI R15 @Line_Macro ; Apply macros down nodes CALLI R15 @Process_String ; Convert string values to Hex16 CALLI R15 @Eval_Immediates ; Convert numbers to hex CALLI R15 @Preserve_Other ; Ensure labels/Pointers aren't lost ;; Prep TAPE_02 LOADUI R0 0x1101 FOPEN_WRITE CALLI R15 @Print_Hex ; Write Nodes to Tape_02 ;; Done writing File LOADUI R0 0x1101 ; Close TAPE_01 FCLOSE HALT ; We are Done ;; Primative malloc function ;; Recieves number of bytes to allocate in R0 ;; Returns pointer to block of that size in R0 ;; Returns to whatever called it :malloc ;; Preserve registers PUSHR R1 R15 ;; Get current malloc pointer LOADR R1 @malloc_pointer ;; Deal with special case CMPSKIPI.NE R1 0 ; If Zero set to our start of heap space LOADUI R1 0x4000 ;; update malloc pointer SWAP R0 R1 ADD R1 R0 R1 STORER R1 @malloc_pointer ;; Done ;; Restore registers POPR R1 R15 RET R15 ;; Our static value for malloc pointer :malloc_pointer NOP ;; Tokenize_Line function ;; Recieves pointer to Head in R0 and desired input in R1 ;; Alters R14 when EOF Reached ;; Returns to whatever called it :Tokenize_Line ;; Preserve registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 ;; Initialize COPY R4 R13 ; Get Head pointer out of the way :Tokenize_Line_0 FGETC ; Get a Char ;; Deal with lines comments starting with # CMPSKIPI.NE R0 35 JUMP @Purge_Line_Comment ;; Deal with Line comments starting with ; CMPSKIPI.NE R0 59 JUMP @Purge_Line_Comment ;; Deal with Tab CMPSKIPI.NE R0 9 JUMP @Tokenize_Line_0 ; Throw away byte and try again ;; Deal with New line CMPSKIPI.NE R0 10 JUMP @Tokenize_Line_0 ; Throw away byte and try again ;; Deal with space characters CMPSKIPI.NE R0 32 JUMP @Tokenize_Line_0 ; Throw away byte and try again ;; Flag if reached EOF CMPSKIPI.GE R0 0 TRUE R14 ;; Stop if EOF CMPSKIPI.GE R0 0 JUMP @Tokenize_Line_Done ;; Allocate a new Node MOVE R2 R0 ; Get Char out the way LOADUI R0 16 ; Allocate 16 Bytes CALLI R15 @malloc ; Get address of new Node SWAP R2 R0 ; Store Pointer in R2 ;; Deal with Strings wrapped in " CMPSKIPI.NE R0 34 JUMP @Store_String ;; Deal with Strings wrapped in ' CMPSKIPI.NE R0 39 JUMP @Store_String ;; Everything else is an atom store it CALLI R15 @Store_Atom :Tokenize_Line_Done MOVE R1 R2 ; Put Node pointer we are working on into R1 COPY R0 R13 ; Get current HEAD CALLI R15 @Add_Token ; Append new token to Head ;; Restore registers POPR R4 R15 POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 ;; Return since we are done RET R15 ;; Purge_Line_Comment Function ;; Recieves char in R0 and desired input in R1 ;; Modifies R0 ;; Returns to Tokenize_Line as if the entire line ;; Comment never existed :Purge_Line_Comment FGETC ; Get another Char CMPSKIPI.E R0 10 ; Stop When LF is reached JUMP @Purge_Line_Comment ; Otherwise keep looping JUMP @Tokenize_Line_0 ; Return as if this never happened ;; Store_String function ;; Recieves Char in R0, desired input in R1 ;; And node pointer in R2 ;; Modifies node Text to point to string and sets ;; Type to string. :Store_String ;; Preserve registers PUSHR R4 R15 PUSHR R5 R15 PUSHR R6 R15 ;; Initialize MOVE R6 R0 ; Get R0 out of the way CALLI R15 @malloc ; Get where space is free MOVE R4 R0 ; Put pointer someplace safe FALSE R5 ; Start at index 0 COPY R0 R6 ; Copy Char back into R0 ;; Primary Loop :Store_String_0 STOREX8 R0 R4 R5 ; Store the Byte FGETC ; Get next Byte ADDUI R5 R5 1 ; Prep for next loop CMPJUMPI.NE R0 R6 @Store_String_0 ; Loop if matching not found ;; Clean up STORE32 R4 R2 8 ; Set Text pointer ADDUI R0 R5 4 ; Correct Malloc CALLI R15 @malloc ; To the amount of space used LOADUI R0 2 ; Using type string STORE32 R0 R2 4 ; Set node type ;; Restore Registers POPR R6 R15 POPR R5 R15 POPR R4 R15 JUMP @Tokenize_Line_Done ;; Store_Atom function ;; Recieves Char in R0, desired input in R1 ;; And node pointer in R2 ;; Modifies node Text to point to string :Store_Atom ;; Preserve registers PUSHR R4 R15 PUSHR R5 R15 ;; Initialize MOVE R5 R0 ; Get R0 out of the way CALLI R15 @malloc ; Get where space is free MOVE R4 R0 ; Put pointer someplace safe MOVE R0 R5 ; Copy Char back and Set index to 0 ;; Primary Loop :Store_Atom_0 STOREX8 R0 R4 R5 ; Store the Byte FGETC ; Get next Byte ADDUI R5 R5 1 ; Prep for next loop CMPSKIPI.NE R0 9 ; If char is Tab JUMP @Store_Atom_Done ; Be done CMPSKIPI.NE R0 10 ; If char is LF JUMP @Store_Atom_Done ; Be done CMPSKIPI.NE R0 32 ; If char is Space JUMP @Store_Atom_Done ; Be done ;; Otherwise loop JUMP @Store_Atom_0 :Store_Atom_Done ;; Cleanup STORE32 R4 R2 8 ; Set Text pointer ADDUI R0 R5 1 ; Correct Malloc CALLI R15 @malloc ; To the amount of space used ;; Restore Registers POPR R5 R15 POPR R4 R15 RET R15 ;; Add_Token Function ;; Recieves pointers in R0 R1 ;; Alters R13 if R) is NULL ;; Appends nodes together ;; Returns to whatever called it :Add_Token ;; Preserve Registers PUSHR R2 R15 PUSHR R1 R15 PUSHR R0 R15 ;; Handle if Head is NULL JUMP.NZ R0 @Add_Token_0 COPY R13 R1 ; Fix head POPR R0 R15 ; Clean up register PUSHR R1 R15 ; And act like we passed the reverse JUMP @Add_Token_2 :Add_Token_0 ;; Handle if Head->next is NULL LOAD32 R2 R0 0 JUMP.NZ R2 @Add_Token_1 ;; Set head->next = p STORE32 R1 R0 0 JUMP @Add_Token_2 :Add_Token_1 ;; Handle case of Head->next not being NULL LOAD32 R0 R0 0 ; Move to next node LOAD32 R2 R0 0 ; Get node->next CMPSKIPI.E R2 0 ; If it is not null JUMP @Add_Token_1 ; Move to the next node and try again JUMP @Add_Token_0 ; Else simply act as if we got this node ; in the first place :Add_Token_2 ;; Restore registers POPR R0 R15 POPR R1 R15 POPR R2 R15 RET R15 ;; strcmp function ;; Recieves pointers to null terminated strings ;; In R0 and R1 ;; Returns if they are equal in R0 ;; Returns to whatever called it :strcmp ;; Preserve registers PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 ;; Setup registers MOVE R2 R0 ; Put R0 in a safe place MOVE R3 R1 ; Put R1 in a safe place LOADUI R4 0 ; Starting at index 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 ;; Identify_Macros Function ;; Recieves a pointer to a node in R0 ;; If the text stored in its Text segment matches ;; DEFINE, flag it and Collapse it down to a single Node ;; Loop until all nodes are checked ;; Return to whatever called it :Identify_Macros ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 ;; Main Loop :Identify_Macros_0 MOVE R2 R0 LOAD32 R1 R2 8 ; Get Pointer to Text LOADUI R0 $Identify_Macros_string CALLI R15 @strcmp MOVE R1 R2 JUMP.NE R0 @Identify_Macros_1 ;; It is a definition ;; Set p->Type = macro LOADUI R0 1 ; The Enum value for macro STORE32 R0 R1 4 ; Set node type ;; Set p->Text = p->Next->Text LOAD32 R2 R1 0 ; Get Next LOAD32 R0 R2 8 ; Get Next->Text STORE32 R0 R1 8 ; Set Text = Next->Text ;; Set p->Expression = p->next->next->Text LOAD32 R2 R2 0 ; Get Next->Next LOAD32 R0 R2 8 ; Get Next->Next->Text LOAD32 R3 R2 4 ; Get Next->Next->type CMPSKIPI.NE R3 2 ; If node is a string ADDUI R0 R0 1 ; Skip first char STORE32 R0 R1 12 ; Set Expression = Next->Next->Text ;; Set p->Next = p->Next->Next->Next LOAD32 R0 R2 0 ; Get Next->Next->Next STORE32 R0 R1 0 ; Set Next = Next->Next->Next :Identify_Macros_1 LOAD32 R0 R1 0 ; Get node->next CMPSKIPI.NE R0 0 ; If node->next is NULL JUMP @Identify_Macros_Done ; Be done ;; Otherwise keep looping JUMP @Identify_Macros_0 :Identify_Macros_Done ;; Restore registers POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 :Identify_Macros_string "DEFINE" ;; Line_Macro Function ;; Recieves a node pointer in R0 ;; Causes macros to be applied ;; Returns to whatever called it :Line_Macro ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 ;; Main loop :Line_Macro_0 LOAD32 R3 R0 4 ; Load Node type LOAD32 R2 R0 12 ; Load Expression pointer LOAD32 R1 R0 8 ; Load Text pointer LOAD32 R0 R0 0 ; Load Next pointer CMPSKIPI.NE R3 1 ; If a macro CALLI R15 @setExpression ; Apply to other nodes CMPSKIPI.E R0 0 ; If Next is Null JUMP @Line_Macro_0 ; Don't loop ;; Clean up POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; setExpression Function ;; Recieves a node pointer in R0 ;; A string pointer to compare against in R1 ;; A string pointer for replacement in R2 ;; Doesn't modify any registers ;; Returns to whatever called it :setExpression ;; Preserve registers PUSHR R0 R15 PUSHR R3 R15 PUSHR R4 R15 PUSHR R5 R15 ;; Initialize MOVE R4 R1 ; Put Macro Text in a safe place COPY R5 R0 ; Use R5 for Node pointer :setExpression_0 LOAD32 R3 R5 4 ; Load type into R3 CMPSKIPI.NE R3 1 ; Check if Macro JUMP @setExpression_1 ; Move to next if Macro LOAD32 R0 R5 8 ; Load Text pointer into R0 for Comparision COPY R1 R4 ; Put Macro Text for comparision CALLI R15 @strcmp ; compare Text and Macro Text JUMP.NE R0 @setExpression_1 ; Move to next if not Match STORE32 R2 R5 12 ; Set node->Expression = Exp :setExpression_1 LOAD32 R5 R5 0 ; Load Next JUMP.NZ R5 @setExpression_0 ; Loop if next isn't NULL :setExpression_Done ;; Restore registers POPR R5 R15 POPR R4 R15 POPR R3 R15 POPR R0 R15 RET R15 ;; Process_String Function ;; Recieves a Node in R0 ;; Doesn't modify registers ;; Returns back to whatever called it :Process_String ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 :Process_String_0 ;; Get node type LOAD32 R1 R0 4 ; Load Type CMPSKIPI.E R1 2 ; If not a string JUMP @Process_String_Done ; Just go to next ;; Its a string LOAD32 R1 R0 8 ; Get Text pointer LOAD8 R2 R1 0 ; Get first char of Text ;; Deal with ' CMPSKIPI.E R2 39 ; If char is not ' JUMP @Process_String_1 ; Move to next label ;; Simply use Hex strings as is ADDUI R1 R1 1 ; Move Text pointer by 1 STORE32 R1 R0 12 ; Set expression to Text + 1 JUMP @Process_String_Done ; And move on :Process_String_1 ;; Deal with " CALLI R15 @Hexify_String :Process_String_Done LOAD32 R0 R0 0 ; Load Next CMPSKIPI.E R0 0 ; If Next isn't NULL JUMP @Process_String_0 ; Recurse down list ;; Restore registers POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; Hexify_String Function ;; Recieves a node pointer in R0 ;; Converts Quoted text to Hex values ;; Pads values up to multiple of 4 bytes ;; Doesn't modify registers ;; Returns to whatever called it :Hexify_String ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 ;; Initialize MOVE R2 R0 ; Move R0 out of the way CALLI R15 @malloc ; Get address of new Node MOVE R1 R0 ; Prep For Hex32 STORE32 R1 R2 12 ; Set node expression pointer LOAD32 R2 R2 8 ; Load Text pointer into R2 ADDUI R2 R2 1 ; SKip leading " FALSE R4 ; Set counter for malloc to Zero ;; Main Loop :Hexify_String_0 LOAD32 R0 R2 0 ; Load 4 bytes into R0 from Text ANDI R3 R0 0xFF ; Preserve byte to check for NULL CALLI R15 @hex32 ; Convert to hex and store in Expression ADDUI R2 R2 4 ; Pointer Text pointer to next 4 bytes ADDUI R4 R4 8 ; Increment storage space required CMPSKIPI.E R3 0 ; If byte was NULL JUMP @Hexify_String_0 ;; Done ADDUI R0 R4 1 ; Lead space for NULL terminator CALLI R15 @malloc ; Correct malloc value ;; Restore Registers POPR R4 R15 POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; hex32 functionality ;; Accepts 32bit value in R0 ;; Require R1 to be a pointer to place to store hex16 ;; WILL ALTER R1 ! ;; Returns to whatever called it :hex32 PUSHR R0 R15 SR0I R0 16 ; Do high word first CALLI R15 @hex16 POPR R0 R15 :hex16 PUSHR R0 R15 SR0I R0 8 ; Do high byte first CALLI R15 @hex8 POPR R0 R15 :hex8 PUSHR R0 R15 SR0I R0 4 ; Do high nybble first CALLI R15 @hex4 POPR R0 R15 :hex4 ANDI R0 R0 0x000F ; isolate nybble ADDUI R0 R0 48 ; convert to ascii CMPSKIPI.LE R0 57 ; If nybble was greater than '9' ADDUI R0 R0 7 ; Shift it into 'A' range of ascii STORE8 R0 R1 0 ; Store Hex Char ADDUI R1 R1 1 ; Increment address pointer RET R15 ; Get next nybble or return if done ;; Eval_Immediates function ;; Recieves a node in R0 ;; Converts number into Hex ;; And write into Memory and fix pointer :Eval_Immediates ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 PUSHR R5 R15 PUSHR R6 R15 ;; Initialize COPY R0 R13 ; Start with Head FALSE R5 ; Zero for checking return of numerate_string ;; Process Text :Eval_Immediates_0 COPY R6 R0 ; Safely preserve pointer to node LOAD32 R4 R0 0 ; Load Node->Next LOAD32 R3 R0 4 ; Load Node type LOAD32 R2 R0 12 ; Load Expression pointer LOAD32 R1 R0 8 ; Load Text pointer JUMP.NZ R2 @Eval_Immediates_1 ; Don't do anything if Expression is set JUMP.NZ R3 @Eval_Immediates_1 ; Don't do anything if Typed COPY R0 R1 ; Put Text pointer into R0 CALLI R15 @numerate_string ; Convert to number in R0 LOAD8 R1 R1 0 ; Get first char of Text CMPSKIPI.E R1 48 ; Skip next comparision if '0' CMPJUMPI.E R0 R5 @Eval_Immediates_1 ; Don't do anything if string isn't a number MOVE R1 R0 ; Preserve number LOADUI R0 5 ; Allocate enough space for 4 hex and a null CALLI R15 @malloc ; Obtain the pointer the newly allocated Expression STORE R0 R6 12 ; Preserve pointer to expression SWAP R0 R1 ; Fix order for call to hex16 CALLI R15 @hex16 ; Shove our number into expression ;; Handle looping :Eval_Immediates_1 CMPJUMPI.E R4 R5 @Eval_Immediates_2 ; If null be done MOVE R0 R4 ; Prepare for next loop JUMP @Eval_Immediates_0 ; And loop ;; Clean up :Eval_Immediates_2 ;; Restore Registers POPR R6 R15 POPR R5 R15 POPR R4 R15 POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 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 ;; Preserve_Other function ;; Sets Expression pointer to Text pointer value ;; For all unset nodes :Preserve_Other ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 ;; Initialize COPY R0 R13 ; Start with HEAD ;; Process Node :Preserve_Other_0 LOAD32 R4 R0 0 ; Load Node->Next LOAD32 R3 R0 4 ; Load Node type LOAD32 R2 R0 12 ; Load Expression pointer LOAD32 R1 R0 8 ; Load Text pointer JUMP.NZ R2 @Preserve_Other_1 ; Don't do anything if Expression is set JUMP.NZ R3 @Preserve_Other_1 ; Don't do anything if Typed STORE32 R1 R0 12 ; Set Expression pointer to Text pointer ;; Loop through nodes :Preserve_Other_1 MOVE R0 R4 ; Prepare for next loop JUMP.NZ R0 @Preserve_Other_0 ;; Clean up :Preserve_Other_Done ;; Restore Registers POPR R4 R15 POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; Print_Hex Function ;; Print all of the expressions ;; Starting with HEAD :Print_Hex ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 ;; Initialize COPY R0 R13 ; Start with HEAD :Print_Hex_0 LOAD32 R2 R0 0 ; Load Node->Next LOAD32 R1 R0 4 ; Load Node type LOAD32 R0 R0 12 ; Load Expression pointer SUBI R1 R1 1 ; Check for Macros JUMP.Z R1 @Print_Hex_1 ; Don't print Macros LOADUI R1 0x1101 ; Write to Tape_02 CALLI R15 @Print_Line ; Print the Expression ;; Loop down the nodes :Print_Hex_1 MOVE R0 R2 ; Prepare for next loop JUMP.NZ R0 @Print_Hex_0 ; Keep looping if not NULL ;; Clean up :Print_Hex_Done ;; Restore Registers POPR R4 R15 POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; Print_Line Function ;; Receives a pointer to a string in R0 ;; And an interface in R1 ;; Writes all Chars in string ;; Then writes a New line character to interface :Print_Line ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 ;; Initialize MOVE R3 R0 ; Get Pointer safely out of the way FALSE R4 ; Start index at 0 :Print_Line_0 LOADXU8 R0 R3 R4 ; Get our first byte CMPSKIPI.NE R0 0 ; If the loaded byte is NULL JUMP @Print_Line_Done ; Be done FPUTC ; Otherwise print ADDUI R4 R4 1 ; Increment for next loop JUMP @Print_Line_0 ; And Loop ;; Clean up :Print_Line_Done LOADUI R0 10 ; Put in Newline char FPUTC ; Write it out ;; Restore Registers POPR R4 R15 POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; Where we are putting the start of our stack :stack