; 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 . :start LOADUI R15 $stack ; Put stack at end of program ;; We will be using R14 for our condition codes ;; We will be using R13 for storage of Head LOADUI R12 0x4000 ; We will be using R12 for the HEAP ;; Main program ;; Reads contents of Tape_01 and writes desired contents onto Tape_02 ;; Accepts no arguments and HALTS when done :main ;; Prep TAPE_01 LOADUI R0 0x1100 FOPEN_READ ;; Prepare to Read File FALSE R14 FALSE R1 CALLI R15 @ReadFile ;; Done reading File LOADUI R0 0x1100 ; Close TAPE_01 FCLOSE ;; Enter Editor Loop MOVE R13 R1 ; Set R13 to Head CALLI R15 @EditorLoop ;; And We are Done HALT ;; Readfile function ;; Receives pointer to head in R1 ;; Creates Nodes and imports text until EOF ;; Alters R0 R1 R14 ;; Returns to whatever called it :ReadFile ;; Allocate another Node COPY R0 R12 ; Copy Current free into R0 ADDUI R12 R12 12 ; Allocate Node ;; Get another line into list PUSHR R1 R15 LOADUI R1 0x1100 ; Read from tape_01 CALLI R15 @Readline POPR R1 R15 SWAP R0 R1 CALLI R15 @addline SWAP R0 R1 ;; Loop if not reached EOF JUMP.Z R14 @ReadFile RET R15 ;; Readline function ;; Receives Pointer to node in R0 ;; And Input in R1 ;; Allocates Text segment on Heap ;; Sets node's pointer to Text segment ;; Sets R14 to True if EOF reached ;; Returns to whatever called it :Readline ;; Preserve registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 PUSHR R4 R15 ;; Initialize MOVE R4 R0 COPY R0 R12 ; Get where space is free MOVE R2 R0 FALSE R3 :Readline_0 FGETC ; Read a Char ;; Flag if reached EOF CMPSKIPI.GE R0 0 TRUE R14 ;; Stop if EOF CMPSKIPI.GE R0 0 JUMP @Readline_2 ;; Handle Backspace CMPSKIPI.E R0 127 JUMP @Readline_1 ;; Move back 1 character if R3 > 0 CMPSKIPI.LE R3 0 SUBUI R3 R3 1 ;; Hopefully they keep typing JUMP @Readline_0 :Readline_1 ;; Replace all CR with LF CMPSKIPI.NE R0 13 LOADUI R0 10 ;; Store the Byte STOREX8 R0 R2 R3 ;; Prep for next loop ADDUI R3 R3 1 ;; Check for EOL CMPSKIPI.NE R0 10 JUMP @Readline_2 ;; Otherwise loop JUMP @Readline_0 :Readline_2 ;; Set Text pointer CMPSKIPI.E R3 0 ; Don't bother for Empty strings STORE32 R2 R4 8 ;; Correct Malloc ADD R12 R12 R3 ; Ensure actually allocates exactly the amount of space required ;; Restore Registers POPR R4 R15 POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; addline Function ;; Receives pointers in R0 R1 ;; Alters R0 if NULL ;; Appends nodes together ;; Returns to whatever called it :addline ;; Preserve Registers PUSHR R2 R15 PUSHR R1 R15 PUSHR R0 R15 ;; Handle if Head is NULL JUMP.NZ R0 @addline_0 POPR R0 R15 PUSHR R1 R15 JUMP @addline_2 :addline_0 ;; Handle if Head->next is NULL LOAD32 R2 R0 0 JUMP.NZ R2 @addline_1 ;; Set head->next = p STORE32 R1 R0 0 ;; Set p->prev = head STORE32 R0 R1 4 JUMP @addline_2 :addline_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 @addline_1 ; Move to the next node and try again JUMP @addline_0 ; Else simply act as if we got this node ; in the first place :addline_2 ;; Restore registers POPR R0 R15 POPR R1 R15 POPR R2 R15 RET R15 ;; Editor Loop ;; Provides user interaction ;; Requires R13 to be pointer to Head ;; Internally loops ;; Returns nothing :EditorLoop FALSE R1 ; Read from tty FGETC ; Read a Char ;; Quit if q CMPSKIPI.NE R0 113 RET R15 ;; Print if p CMPUI R14 R0 112 JUMP.NE R14 @EditorLoop_0 LOAD32 R0 R13 8 FALSE R1 CALLI R15 @PrintLine JUMP @EditorLoop :EditorLoop_0 ;; Move forward if f CMPUI R14 R0 102 JUMP.NE R14 @EditorLoop_1 LOAD32 R0 R13 0 ; Load head->next ;; If head->next isn't null make it the new head CMPSKIPI.E R0 0 MOVE R13 R0 JUMP @EditorLoop :EditorLoop_1 ;; Move backward if b CMPUI R14 R0 98 JUMP.NE R14 @EditorLoop_2 LOAD32 R0 R13 4 ; Load head->prev ;; If head->prev isn't null make it the new head CMPSKIPI.E R0 0 MOVE R13 R0 JUMP @EditorLoop :EditorLoop_2 ;; Edit Line if e CMPUI R14 R0 101 JUMP.NE R14 @EditorLoop_3 ;; Change Head's Text COPY R0 R13 FALSE R1 ; Read from tty CALLI R15 @Readline JUMP @EditorLoop :EditorLoop_3 ;; Writeout to tape_02 if w CMPUI R14 R0 119 JUMP.NE R14 @EditorLoop_4 ;; Prep TAPE_02 LOADUI R0 0x1101 FOPEN_WRITE COPY R0 R13 LOADUI R1 0x1101 CALLI R15 @GetRoot CALLI R15 @PrintAll LOADUI R0 0x1101 ; Close TAPE_02 FCLOSE JUMP @EditorLoop :EditorLoop_4 ;; Append node if a CMPUI R14 R0 97 JUMP.NE R14 @EditorLoop_5 COPY R0 R13 CALLI R15 @AppendLine JUMP @EditorLoop :EditorLoop_5 ;; Insert node if i CMPUI R14 R0 105 JUMP.NE R14 @EditorLoop_6 COPY R0 R13 CALLI R15 @InsertLine JUMP @EditorLoop :EditorLoop_6 ;; Delete node if d CMPUI R14 R0 100 JUMP.NE R14 @EditorLoop_7 COPY R0 R13 CALLI R15 @RemoveLine MOVE R13 R0 JUMP @EditorLoop :EditorLoop_7 JUMP @EditorLoop ;; GetRoot function ;; Walks backwards through nodes until beginning ;; Receives node pointer in R0 and Returns result in R0 ;; Returns to whatever called it :GetRoot ;; Preserve registers PUSHR R1 R15 :GetRoot_0 ;; Get Head->Prev LOAD32 R1 R0 4 CMPSKIPI.NE R1 0 JUMP @GetRoot_1 MOVE R0 R1 JUMP @GetRoot_0 :GetRoot_1 ;; Restore registers POPR R1 R15 RET R15 ;; Printall Function ;; Prints all lines to Interface in R1 ;; Starting at node in R0 ;; Does not alter registers ;; Returns to whatever called it :PrintAll ;; Preserve registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 :PrintAll_0 LOAD32 R2 R0 0 ; Store Head->Next in R2 LOAD32 R0 R0 8 ; Set R0 to Head->Text CALLI R15 @PrintLine ; Prints Head->Text CMPSKIPI.NE R2 0 ; If Head->Next is NULL JUMP @PrintAll_1 ; Stop Looping MOVE R0 R2 ; Otherwise Move to Next Node JUMP @PrintAll_0 ; And Loop :PrintAll_1 ;; Restore registers POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; Printline function ;; Receives a string pointer in R0 ;; Prints string interface specified in R1 ;; Does not alter registers ;; Returns to whatever called it :PrintLine ;; Preserve registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 PUSHR R3 R15 ;; Initialize MOVE R2 R0 FALSE R3 ;; Deal with NULL Pointer CMPSKIPI.NE R2 0 JUMP @PrintLine_1 :PrintLine_0 LOADXU8 R0 R2 R3 ; Load char from string ;; Don't print NULLs CMPSKIPI.NE R0 0 JUMP @PrintLine_1 FPUTC ; Print the char ADDUI R3 R3 1 ; Prep for next loop JUMP @PrintLine_0 :PrintLine_1 ;; Restore registers POPR R3 R15 POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; AppendLine Function ;; Receives a Node in R0 ;; Creates a new Node and appends it ;; Does not alter registers ;; Returns to whatever calls it :AppendLine ;; Preserve registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 ;; Initialize MOVE R1 R0 ;; Allocate another Node COPY R0 R12 ADDUI R12 R12 12 ;; Check if head->Next is null LOAD32 R2 R1 0 CMPSKIPI.E R2 0 ; If head->Next is something STORE32 R0 R2 4 ; Set head->next->prev to p ;; Setup p and head STORE32 R2 R0 0 ; p->next = head->next STORE32 R1 R0 4 ; p->prev = head STORE32 R0 R1 0 ; head->next = p ;; Restore Registers POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; InsertLine Function ;; Receives a Node in R0 ;; Creates a new Node and prepends it ;; Does not alter registers ;; Returns to whatever called it :InsertLine ;; Preserve Registers PUSHR R0 R15 PUSHR R1 R15 PUSHR R2 R15 ;; Initialize MOVE R1 R0 ;; Allocate another Node COPY R0 R12 ADDUI R12 R12 12 ;; Check if Head->Prev is Null LOAD32 R2 R1 4 CMPSKIPI.E R2 0 ; If head->prev is something STORE32 R0 R2 0 ; Set head->prev->next to p ;; Setup p and head STORE32 R2 R0 4 ; p->prev = head->prev STORE32 R1 R0 0 ; p->next = head STORE32 R0 R1 4 ; head->prev = p ;; Restore Registers POPR R2 R15 POPR R1 R15 POPR R0 R15 RET R15 ;; RemoveLine Function ;; Receives Node in R0 ;; Returns replacement node in R0 ;; Returns to whatever called it :RemoveLine ;; Preserve Registers PUSHR R1 R15 PUSHR R2 R15 ;; Initialize MOVE R1 R0 LOAD32 R0 R1 4 ; put p->prev in R0 LOAD32 R2 R1 0 ; put p->next in R2 ;; Keep links CMPSKIPI.E R0 0 ; If p->prev is not null STORE32 R2 R0 0 ; p->prev->next = p->next CMPSKIPI.E R2 0 ; If p->next is not null STORE32 R0 R2 4 ; p->next->prev = p->prev ;; Attempt to save what is left of the list CMPSKIPI.NE R0 0 ; If p->prev is null MOVE R0 R2 ; return p->next ;; Restore Registers POPR R2 R15 POPR R1 R15 RET R15 ;; Where our stack begins :stack