boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

M0.hex2 (50189B)


      1 ; Copyright (C) 2017 Jeremiah Orians
      2 ; Copyright (C) 2020 Sanne Wouda
      3 ; This file is part of stage0.
      4 ;
      5 ; stage0 is free software: you can redistribute it and/or modify
      6 ; it under the terms of the GNU General Public License as published by
      7 ; the Free Software Foundation, either version 3 of the License, or
      8 ; (at your option) any later version.
      9 ;
     10 ; stage0 is distributed in the hope that it will be useful,
     11 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 ; GNU General Public License for more details.
     14 ;
     15 ; You should have received a copy of the GNU General Public License
     16 ; along with stage0.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 
     19 	; Register usage:
     20 	; X0, X8, RSI, RDI => Temps
     21 	; X12 => MALLOC
     22 	; X13 => HEAD
     23 	; X14 => Output_file
     24 	; X15 => Input_file
     25 
     26 	; Struct format: (size 32)
     27 	; NEXT => 0
     28 	; TYPE => 8
     29 	; TEXT => 16
     30 	; EXPRESSION => 24
     31 
     32 	; Types
     33 	; None => 0
     34 	; MACRO => 1
     35 	; STRING => 2
     36 
     37 ; Where the ELF Header is going to hit
     38 ; Simply jump to _start
     39 ; Our main function
     40 :_start
     41 	e10b40f9                     # LDR_X1_[SP,16]                  ; Get the actual input name
     42 	600c8092                     # SET_X0_TO_FCNTL_H_AT_FDCWD      ; AT_FDCWD, relative to current working directory
     43 	020080d2                     # SET_X2_TO_0                     ; prepare read_only
     44 	080780d2                     # SET_X8_TO_SYS_OPENAT            ; The syscall number for openat(), aarch64 has no open()
     45 	010000d4                     # SYSCALL                         ; Now open that damn file
     46 	ef0300aa                     # SET_X15_FROM_X0                 ; Preserve the file pointer we were given
     47 
     48 	e10f40f9                     # LDR_X1_[SP,24]                  ; Get the output name
     49 	600c8092                     # SET_X0_TO_FCNTL_H_AT_FDCWD      ; AT_FDCWD, relative to current working directory
     50 	224880d2                     # SET_X2_TO_577                   ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
     51 	033080d2                     # SET_X3_TO_384                   ; Prepare file as RWX for owner only (600 in octal)
     52 	080780d2                     # SET_X8_TO_SYS_OPENAT            ; The syscall number for openat(), aarch64 has no open()
     53 	010000d4                     # SYSCALL                         ; Open file!
     54 	1f0000f1                     # CMP_X0_TO_0                     ; Check for missing output
     55 	4d000054                     # SKIP_INST_LE                    ; Have real output
     56 	^~_start_out 14              # ^~_start_out FBRANCH
     57 	200080d2                     # SET_X0_TO_1                     ; Use stdout
     58 
     59 :_start_out
     60 	ee0300aa                     # SET_X14_FROM_X0                 ; Preserve the file pointer we were given
     61 	c81a80d2                     # SET_X8_TO_SYS_BRK               ; the Syscall # for SYS_BRK
     62 	000080d2                     # SET_X0_TO_0                     ; Get current brk
     63 	010000d4                     # SYSCALL                         ; Let the kernel do the work
     64 	ec0300aa                     # SET_X12_FROM_X0                 ; Set our malloc pointer
     65 
     66 	^~Tokenize_Line 94           # ^~Tokenize_Line FCALL           ; Get all lines
     67 	e0030daa                     # SET_X0_FROM_X13                 ; prepare for Reverse_List
     68 	^~Reverse_List 94            # ^~Reverse_List FCALL            ; Correct order
     69 	ed0300aa                     # SET_X13_FROM_X0                 ; Update HEAD
     70 	^~Identify_Macros 94         # ^~Identify_Macros FCALL         ; Find the DEFINEs
     71 	^~Line_Macro 94              # ^~Line_Macro FCALL              ; Apply the DEFINEs
     72 	^~Process_String 94          # ^~Process_String FCALL          ; Handle strings
     73 	^~Eval_Immediates 94         # ^~Eval_Immediates FCALL         ; Handle Numbers
     74 	^~Preserve_Other 94          # ^~Preserve_Other FCALL          ; Collect the remaining
     75 	^~Print_Hex 94               # ^~Print_Hex FCALL               ; Output our results
     76 
     77 :Done
     78 	; program completed Successfully
     79 	000080d2                     # SET_X0_TO_0                     ; All is well
     80 	a80b80d2                     # SET_X8_TO_SYS_EXIT              ; put the exit syscall number in eax
     81 	010000d4                     # SYSCALL                         ; Call it a good day
     82 
     83 
     84 ; Tokenize_Line Function
     85 ; Using input file X15 and Head X13
     86 ; Creates a linked list of structs
     87 ; Uses X1 for in_set strings, X2 for Int C and X3 for Struct Token* p
     88 :Tokenize_Line
     89 	e10f1ff8                     # PUSH_X1                         ; Protect X1
     90 	e20f1ff8                     # PUSH_X2                         ; Protect X2
     91 	e30f1ff8                     # PUSH_X3                         ; Protect X3
     92 	fe0f1ff8                     # PUSH_LR
     93 :restart
     94 	^~fgetc 94                   # ^~fgetc FCALL                   ; Read a char
     95 	1f1000b1                     # CMP_X0_TO_MINUS_4               ; Check for EOF
     96 	41000054                     # SKIP_INST_NE                    ; File is collected
     97 	^~done 14                    # ^~done FBRANCH
     98 
     99 	001c4092                     # AND_X0_X0_0xFF                  ; We have to zero extend it to use it
    100 	e20300aa                     # SET_X2_FROM_X0                  ; Protect C
    101 
    102 	41000018                     # LOAD_W1_AHEAD                   ; Get pointer to ";;"
    103 	02000014                     # SKIP_32_DATA
    104 	&comments                    # &comments
    105 	^~In_Set 94                  # ^~In_Set FCALL                  ; Check for comments
    106 	1f0400f1                     # CMP_X0_TO_1                     ; If comments
    107 	41000054                     # SKIP_INST_NE                    ; try again
    108 	^~Purge_LineComment 14       # ^~Purge_LineComment FBRANCH
    109 
    110 	e00302aa                     # SET_X0_FROM_X2                  ; put C in place for check
    111 	41000018                     # LOAD_W1_AHEAD                   ; Get pointer to "\n\t "
    112 	02000014                     # SKIP_32_DATA
    113 	&terminators                 # &terminators
    114 	^~In_Set 94                  # ^~In_Set FCALL                  ; Check for terminators
    115 	1f0400f1                     # CMP_X0_TO_1                     ; If terminator
    116 	41000054                     # SKIP_INST_NE                    ; try again
    117 	^~restart 17                 # ^~restart RBRANCH
    118 
    119 	000480d2                     # SET_X0_TO_32                    ; Malloc the struct P
    120 	^~malloc 94                  # ^~malloc FCALL                  ; Get pointer to P
    121 	e30300aa                     # SET_X3_FROM_X0                  ; Protect P
    122 	6d0000f9                     # STR_X13_[X3]                    ; P->NEXT = HEAD
    123 	ed0303aa                     # SET_X13_FROM_X3                 ; HEAD = P
    124 
    125 	e00302aa                     # SET_X0_FROM_X2                  ; put C in place for check
    126 	41000018                     # LOAD_W1_AHEAD                   ; Get pointer to "\"'"
    127 	02000014                     # SKIP_32_DATA
    128 	&string_char                 # &string_char
    129 	^~In_Set 94                  # ^~In_Set FCALL                  ; Check for string chars
    130 	1f0400f1                     # CMP_X0_TO_1                     ; If string char
    131 	41000054                     # SKIP_INST_NE                    ; Get string
    132 	^~Store_String 14            # ^~Store_String FBRANCH
    133 
    134 	^~Store_Atom 94              # ^~Store_Atom FCALL              ; Get whole token
    135 	^~restart 17                 # ^~restart RBRANCH
    136 
    137 :done
    138 	fe0741f8                     # POP_LR
    139 	e30741f8                     # POP_X3                          ; Restore X3
    140 	e20741f8                     # POP_X2                          ; Restore X2
    141 	e10741f8                     # POP_X1                          ; Restore X1
    142 	c0035fd6                     # RETURN
    143 
    144 
    145 ; fgetc function
    146 ; Receives FILE* in X15
    147 ; Returns -4 (EOF) or char in X0
    148 :fgetc
    149 	e10f1ff8                     # PUSH_X1
    150 	e20f1ff8                     # PUSH_X2
    151 	60008092                     # SET_X0_TO_MINUS_4               ; Put EOF in x0
    152 	e00f1ff8                     # PUSH_X0                         ; Assume bad (If nothing read, value will remain EOF)
    153 	e1030091                     # SET_X1_FROM_SP                  ; Get stack addresss
    154 	e0030faa                     # SET_X0_FROM_X15                 ; Where are we reading from
    155 	e80780d2                     # SET_X8_TO_SYS_READ              ; the syscall number for read
    156 	220080d2                     # SET_X2_TO_1                     ; set the size of chars we want
    157 
    158 	010000d4                     # SYSCALL                         ; call the Kernel
    159 
    160 	e00741f8                     # POP_X0                          ; Get either char or EOF
    161 	e20741f8                     # POP_X2
    162 	e10741f8                     # POP_X1
    163 	c0035fd6                     # RETURN
    164 
    165 
    166 ; Malloc isn't actually required if the program being built fits in the initial memory
    167 ; However, it doesn't take much to add it.
    168 ; Requires X12 to be initialized and X0 to have the number of desired bytes
    169 :malloc
    170 	e10f1ff8                     # PUSH_X1
    171 	00000c8b                     # ADD_X0_X0_X12                   ; Request the number of desired bytes
    172 	c81a80d2                     # SET_X8_TO_SYS_BRK               ; the Syscall # for SYS_BRK
    173 	010000d4                     # SYSCALL                         ; call the Kernel
    174 
    175 	e1030caa                     # SET_X1_FROM_X12                 ; save Return pointer
    176 	ec0300aa                     # SET_X12_FROM_X0                 ; Update pointer
    177 	e00301aa                     # SET_X0_FROM_X1                  ; return pointer
    178 	e10741f8                     # POP_X1
    179 	c0035fd6                     # RETURN
    180 
    181 
    182 ; Purge_LineComment function
    183 ; Reads chars until LF and jumps to restart
    184 :Purge_LineComment
    185 	^~fgetc 97                   # ^~fgetc RCALL                   ; Get a char
    186 	001c4092                     # AND_X0_X0_0xFF                  ; Zero extend
    187 	1f2800f1                     # CMP_X0_TO_10                    ; While not LF
    188 	40000054                     # SKIP_INST_EQ                    ; Keep reading
    189 	^~Purge_LineComment 17       # ^~Purge_LineComment RBRANCH
    190 	^~restart 17                 # ^~restart RBRANCH
    191 
    192 
    193 ; Store_String Function
    194 ; Receives C in X2, HEAD in X3 and Input file in X14
    195 ; Uses X1 for terminator, X2 for C and X3 for string
    196 :Store_String
    197 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    198 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    199 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    200 
    201 	400080d2                     # SET_X0_TO_2                     ; Using TYPE STRING
    202 	600400f9                     # STR_X0_[X3,8]                   ; HEAD->TYPE = STRING
    203 	004080d2                     # SET_X0_TO_512                   ; Malloc the string
    204 	^~malloc 97                  # ^~malloc RCALL                  ; Get pointer to P
    205 	600800f9                     # STR_X0_[X3,16]                  ; HEAD->TEXT = STRING
    206 	e10302aa                     # SET_X1_FROM_X2                  ; Protect terminator
    207 	e30300aa                     # SET_X3_FROM_X0                  ; Protect string pointer
    208 :Store_String_Loop
    209 	62140038                     # STR_BYTE_W2_[X3]_1
    210 	^~fgetc 97                   # ^~fgetc RCALL                   ; read next char
    211 	001c4092                     # AND_X0_X0_0xFF                  ; Zero extend it
    212 	e20300aa                     # SET_X2_FROM_X0                  ; Update C
    213 	5f0001eb                     # CMP_X2_X1                       ; See if we hit terminator
    214 	40000054                     # SKIP_INST_EQ                    ; Otherwise keep looping
    215 	^~Store_String_Loop 17       # ^~Store_String_Loop RBRANCH
    216 
    217 	e30741f8                     # POP_X3                          ; Restore X3
    218 	e20741f8                     # POP_X2                          ; Restore X2
    219 	e10741f8                     # POP_X1                          ; Restore X1
    220 	e00303aa                     # SET_X0_FROM_X3                  ; Return HEAD
    221 	^~restart 17                 # ^~restart RBRANCH
    222 
    223 
    224 ; Store_Atom Function
    225 ; Receives C in X2, HEAD in X3 and Input file in X15
    226 ; Uses X1 for in_set strings, X2 for C and X3 for string
    227 :Store_Atom
    228 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    229 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    230 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    231 	fe0f1ff8                     # PUSH_LR
    232 
    233 	002080d2                     # SET_X0_TO_256                   ; Malloc the string
    234 	^~malloc 97                  # ^~malloc RCALL                  ; Get pointer to P
    235 	600800f9                     # STR_X0_[X3,16]                  ; HEAD->TEXT = STRING
    236 	41000018                     # LOAD_W1_AHEAD                   ; Get pointer to "\n\t "
    237 	02000014                     # SKIP_32_DATA
    238 	&terminators                 # &terminators
    239 	e30300aa                     # SET_X3_FROM_X0                  ; Protect string pointer
    240 :Store_Atom_loop
    241 	62140038                     # STR_BYTE_W2_[X3]_1              ; write byte
    242 	^~fgetc 97                   # ^~fgetc RCALL                   ; read next char
    243 	001c4092                     # AND_X0_X0_0xFF                  ; Zero extend it
    244 	e20300aa                     # SET_X2_FROM_X0                  ; Update C
    245 	^~In_Set 94                  # ^~In_Set FCALL                  ; Check for terminators
    246 	1f0000f1                     # CMP_X0_TO_0                     ; Check for "\n\t "
    247 	41000054                     # SKIP_INST_NE                    ; Loop otherwise
    248 	^~Store_Atom_loop 17         # ^~Store_Atom_loop RBRANCH
    249 
    250 	fe0741f8                     # POP_LR
    251 	e30741f8                     # POP_X3                          ; Restore X3
    252 	e20741f8                     # POP_X2                          ; Restore X2
    253 	e10741f8                     # POP_X1                          ; Restore X1
    254 	e00303aa                     # SET_X0_FROM_X3                  ; Return HEAD
    255 	c0035fd6                     # RETURN
    256 
    257 
    258 ; In_Set function
    259 ; Receives Char C in X0 and CHAR* in X1
    260 ; Returns 1 if true, zero if false in X0
    261 :In_Set
    262 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    263 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    264 :In_Set_loop
    265 	22144038                     # LDR_BYTE_W2_[X1]_1              ; Read char
    266 
    267 	1f0002eb                     # CMP_X0_X2                       ; See if they match
    268 	41000054                     # SKIP_INST_NE                    ; return true
    269 	^~In_Set_True 14             # ^~In_Set_True FBRANCH
    270 
    271 	5f0000f1                     # CMP_X2_TO_0                     ; Check for NULL
    272 	41000054                     # SKIP_INST_NE                    ; return false
    273 	^~In_Set_False 14            # ^~In_Set_False FBRANCH
    274 
    275 	^~In_Set_loop 17             # ^~In_Set_loop RBRANCH           ; Keep looping
    276 
    277 :In_Set_True
    278 	200080d2                     # SET_X0_TO_1                     ; Set True
    279 	e20741f8                     # POP_X2                          ; Restore X2
    280 	e10741f8                     # POP_X1                          ; Restore X1
    281 	c0035fd6                     # RETURN
    282 
    283 :In_Set_False
    284 	000080d2                     # SET_X0_TO_0                     ; Set FALSE
    285 	e20741f8                     # POP_X2                          ; Restore X2
    286 	e10741f8                     # POP_X1                          ; Restore X1
    287 	c0035fd6                     # RETURN
    288 
    289 ; Char sets
    290 :terminators
    291 	0A 09 20 00                  # "\n\t "
    292 
    293 :comments
    294 	23 3B 00                     # "#;"
    295 
    296 :string_char
    297 	22 27 00                     # '22 27 00'
    298 
    299 	00 00                        # '00 00'                         ; .p2align 2
    300 
    301 ; Reverse_List function
    302 ; Receives List in X0
    303 ; Returns the list reversed in X0
    304 :Reverse_List
    305 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    306 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    307 	e10300aa                     # SET_X1_FROM_X0                  ; Set HEAD
    308 	000080d2                     # SET_X0_TO_0                     ; ROOT = NULL
    309 :Reverse_List_Loop
    310 	3f0000f1                     # CMP_X1_TO_0                     ; WHILE HEAD != NULL
    311 	41000054                     # SKIP_INST_NE                    ; Stop otherwise
    312 	^~Reverse_List_Done 14       # ^~Reverse_List_Done FBRANCH
    313 
    314 	220040f9                     # LDR_X2_[X1]                     ; NEXT = HEAD->NEXT
    315 	200000f9                     # STR_X0_[X1]                     ; HEAD->NEXT = ROOT
    316 	e00301aa                     # SET_X0_FROM_X1                  ; ROOT = HEAD
    317 	e10302aa                     # SET_X1_FROM_X2                  ; HEAD = NEXT
    318 	^~Reverse_List_Loop 17       # ^~Reverse_List_Loop RBRANCH     ; Keep Going
    319 
    320 :Reverse_List_Done
    321 	e20741f8                     # POP_X2                          ; Restore X2
    322 	e10741f8                     # POP_X1                          ; Restore X1
    323 	c0035fd6                     # RETURN
    324 
    325 
    326 ; Identify_Macros function
    327 ; Receives List in X0
    328 ; Updates the list in place; does not modify registers
    329 ; Uses X1 for DEFINE, X2 for I
    330 :Identify_Macros
    331 	e00f1ff8                     # PUSH_X0                         ; Protect X0
    332 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    333 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    334 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    335 	fe0f1ff8                     # PUSH_LR
    336 
    337 	41000018                     # LOAD_W1_AHEAD                   ; Setup define string
    338 	02000014                     # SKIP_32_DATA
    339 	&DEFINE_str                  # &DEFINE_str
    340 	e20300aa                     # SET_X2_FROM_X0                  ; I = HEAD
    341 :Identify_Macros_Loop
    342 	400840f9                     # LDR_X0_[X2,16]                  ; I->TEXT
    343 	^~match 94                   # ^~match FCALL                   ; IF "DEFINE" == I->TEXT
    344 	1f0000f1                     # CMP_X0_TO_0                     ; Check if match
    345 	40000054                     # SKIP_INST_EQ                    ; Skip the work
    346 	^~Identify_Macros_Next 14    # ^~Identify_Macros_Next FBRANCH
    347 
    348 	; Deal with MACRO
    349 	200080d2                     # SET_X0_TO_1                     ; Using MACRO
    350 	400400f9                     # STR_X0_[X2,8]                   ; I->TYPE = MACRO
    351 
    352 	400040f9                     # LDR_X0_[X2]                     ; I->NEXT
    353 	000840f9                     # LDR_X0_[X0,16]                  ; I->NEXT->TEXT
    354 	400800f9                     # STR_X0_[X2,16]                  ; I->TEXT = I->NEXT->TEXT
    355 
    356 	400040f9                     # LDR_X0_[X2]                     ; I->NEXT
    357 	000040f9                     # LDR_X0_[X0]                     ; I->NEXT->NEXT
    358 	000840f9                     # LDR_X0_[X0,16]                  ; I->NEXT->NEXT->TEXT
    359 	400c00f9                     # STR_X0_[X2,24]                  ; I->EXPRESSION = I->NEXT->NEXT->TEXT
    360 
    361 	400040f9                     # LDR_X0_[X2]                     ; I->NEXT
    362 	000040f9                     # LDR_X0_[X0]                     ; I->NEXT->NEXT
    363 	000040f9                     # LDR_X0_[X0]                     ; I->NEXT->NEXT->NEXT
    364 	400000f9                     # STR_X0_[X2]                     ; I->NEXT = I->NEXT->NEXT->NEXT
    365 
    366 :Identify_Macros_Next
    367 	420040f9                     # LDR_X2_[X2]                     ; I = I->NEXT
    368 	5f0000f1                     # CMP_X2_TO_0                     ; Check for NULL
    369 	40000054                     # SKIP_INST_EQ                    ; Keep looping otherwise
    370 	^~Identify_Macros_Loop 17    # ^~Identify_Macros_Loop RBRANCH
    371 
    372 	fe0741f8                     # POP_LR
    373 	e30741f8                     # POP_X3                          ; Restore X3
    374 	e20741f8                     # POP_X2                          ; Restore X2
    375 	e10741f8                     # POP_X1                          ; Restore X1
    376 	e00741f8                     # POP_X0                          ; Restore X0
    377 	c0035fd6                     # RETURN
    378 
    379 :DEFINE_str
    380 	44 45 46 49 4E 45 00         # "DEFINE"
    381 
    382 	00                           # '00'                            ; .p2align 2
    383 
    384 ; match function
    385 ; Receives CHAR* in X0 and CHAR* in X1
    386 ; Returns 0 (TRUE) or 1 (FALSE) in X0
    387 :match
    388 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    389 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    390 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    391 	fe0f1ff8                     # PUSH_LR
    392 
    393 	e20300aa                     # SET_X2_FROM_X0                  ; S1 in place
    394 	e30301aa                     # SET_X3_FROM_X1                  ; S2 in place
    395 :match_Loop
    396 	40144038                     # LDR_BYTE_W0_[X2]_1              ; S1[0]
    397 	61144038                     # LDR_BYTE_W1_[X3]_1              ; S2[0]
    398 	1f0001eb                     # CMP_X0_X1                       ; See if they match
    399 	40000054                     # SKIP_INST_EQ                    ; If not
    400 	^~match_False 14             # ^~match_False FBRANCH
    401 
    402 	1f0000f1                     # CMP_X0_TO_0                     ; If reached end of string
    403 	41000054                     # SKIP_INST_NE                    ; Perfect match
    404 	^~match_Done 14              # ^~match_Done FBRANCH
    405 	^~match_Loop 17              # ^~match_Loop RBRANCH            ; Otherwise keep looping
    406 
    407 :match_False
    408 	200080d2                     # SET_X0_TO_1                     ; Return false
    409 :match_Done
    410 	fe0741f8                     # POP_LR
    411 	e30741f8                     # POP_X3                          ; Restore X3
    412 	e20741f8                     # POP_X2                          ; Restore X2
    413 	e10741f8                     # POP_X1                          ; Restore X1
    414 	c0035fd6                     # RETURN
    415 
    416 
    417 ; Line_Macro function
    418 ; Receives List in X0
    419 ; Updates the list in place; does not modify registers
    420 ; Uses X0 for I, X1 for I->TEXT, X2 for I->EXPRESSION
    421 :Line_Macro
    422 	e00f1ff8                     # PUSH_X0                         ; Protect X0
    423 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    424 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    425 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    426 	fe0f1ff8                     # PUSH_LR
    427 :Line_Macro_Loop
    428 	010440f9                     # LDR_X1_[X0,8]                   ; I->TYPE
    429 	3f0400f1                     # CMP_X1_TO_1                     ; IF MACRO == I->TYPE
    430 	40000054                     # SKIP_INST_EQ                    ; Otherwise move on
    431 	^~Line_Macro_Next 14         # ^~Line_Macro_Next FBRANCH
    432 
    433 	; Is a macro apply
    434 	010840f9                     # LDR_X1_[X0,16]                  ; I->TEXT
    435 	020c40f9                     # LDR_X2_[X0,24]                  ; I->EXPRESSION
    436 	000040f9                     # LDR_X0_[X0]                     ; I->NEXT
    437 	^~Set_Expression 94          # ^~Set_Expression FCALL          ; Apply it
    438 	^~Line_Macro_Loop 17         # ^~Line_Macro_Loop RBRANCH       ; Move on to next
    439 
    440 :Line_Macro_Next
    441 	000040f9                     # LDR_X0_[X0]                     ; I->NEXT
    442 	1f0000f1                     # CMP_X0_TO_0                     ; Check for NULL
    443 	40000054                     # SKIP_INST_EQ                    ; Keep going
    444 	^~Line_Macro_Loop 17         # ^~Line_Macro_Loop RBRANCH
    445 
    446 	fe0741f8                     # POP_LR
    447 	e30741f8                     # POP_X3                          ; Restore X3
    448 	e20741f8                     # POP_X2                          ; Restore X2
    449 	e10741f8                     # POP_X1                          ; Restore X1
    450 	e00741f8                     # POP_X0                          ; Restore X0
    451 	c0035fd6                     # RETURN
    452 
    453 
    454 ; Set_Expression function
    455 ; Receives List in X0, CHAR* in X1 and CHAR* in X2
    456 ; Updates the list in place; does not modify registers
    457 ; Uses X1 for C, X2 for EXP and X3 for I
    458 :Set_Expression
    459 	e00f1ff8                     # PUSH_X0                         ; Protect X0
    460 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    461 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    462 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    463 	fe0f1ff8                     # PUSH_LR
    464 	e30300aa                     # SET_X3_FROM_X0                  ; Set I
    465 :Set_Expression_Loop
    466 	600440f9                     # LDR_X0_[X3,8]                   ; I->TYPE
    467 	1f0400f1                     # CMP_X0_TO_1                     ; IF MACRO == I->TYPE
    468 	41000054                     # SKIP_INST_NE                    ; Ignore and move on
    469 	^~Set_Expression_Next 14     # ^~Set_Expression_Next FBRANCH
    470 
    471 	600840f9                     # LDR_X0_[X3,16]                  ; I->TEXT
    472 	^~match 97                   # ^~match RCALL                   ; Check for match
    473 	1f0000f1                     # CMP_X0_TO_0                     ; If match
    474 	40000054                     # SKIP_INST_EQ                    ; Otherwise next
    475 	^~Set_Expression_Next 14     # ^~Set_Expression_Next FBRANCH
    476 
    477 	; We have a non-macro match
    478 	620c00f9                     # STR_X2_[X3,24]                  ; I->EXPRESSION = EXP
    479 
    480 :Set_Expression_Next
    481 	630040f9                     # LDR_X3_[X3]                     ; I = I->NEXT
    482 	7f0000f1                     # CMP_X3_TO_0                     ; IF NULL == I
    483 	40000054                     # SKIP_INST_EQ                    ; Otherwise keep looping
    484 	^~Set_Expression_Loop 17     # ^~Set_Expression_Loop RBRANCH
    485 
    486 	fe0741f8                     # POP_LR
    487 	e30741f8                     # POP_X3                          ; Restore X3
    488 	e20741f8                     # POP_X2                          ; Restore X2
    489 	e10741f8                     # POP_X1                          ; Restore X1
    490 	e00741f8                     # POP_X0                          ; Restore X0
    491 	c0035fd6                     # RETURN
    492 
    493 
    494 ; Process_String function
    495 ; Receives List in X0
    496 ; Update the list in place; does not modify registers
    497 ; Uses X1 for I->TEXT, X2 for I and X3 for S
    498 :Process_String
    499 	e00f1ff8                     # PUSH_X0                         ; Protect X0
    500 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    501 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    502 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    503 	fe0f1ff8                     # PUSH_LR
    504 
    505 	e20300aa                     # SET_X2_FROM_X0                  ; I = HEAD
    506 :Process_String_loop
    507 	400440f9                     # LDR_X0_[X2,8]                   ; I->TYPE
    508 	1f0800f1                     # CMP_X0_TO_2                     ; IF STRING == I->TYPE
    509 	40000054                     # SKIP_INST_EQ                    ; Skip to next
    510 	^~Process_String_Next 14     # ^~Process_String_Next FBRANCH
    511 
    512 	410840f9                     # LDR_X1_[X2,16]                  ; I->TEXT
    513 	20004039                     # LDR_BYTE_W0_[X1]                ; I->TEXT[0]
    514 	1f9c00f1                     # CMP_X0_TO_39                    ; IF '\'' == I->TEXT[0]
    515 	40000054                     # SKIP_INST_EQ                    ; Deal with '\"'
    516 	^~Process_String_Raw 14      # ^~Process_String_Raw FBRANCH
    517 
    518 	; Deal with '\''
    519 	21040091                     # ADD_X1_X1_1                     ; I->TEXT + 1
    520 	410c00f9                     # STR_X1_[X2,24]                  ; I->EXPRESSION = I->TEXT + 1
    521 	^~Process_String_Next 14     # ^~Process_String_Next FBRANCH   ; Move on to next
    522 
    523 :Process_String_Raw
    524 	e00301aa                     # SET_X0_FROM_X1                  ; Get length of I->TEXT
    525 	^~string_length 94           # ^~string_length FCALL           ; Do it
    526 	00fc42d3                     # LSR_X0_X0_2                     ; LENGTH = LENGTH >> 2
    527 	00040091                     # ADD_X0_X0_1                     ; LENGTH = LENGTH + 1
    528 	00f07dd3                     # LSL_X0_X0_3                     ; LENGTH = LENGTH << 3
    529 	^~malloc 97                  # ^~malloc RCALL                  ; Get string
    530 	e30301aa                     # SET_X3_FROM_X1                  ; S = I->TEXT
    531 	63040091                     # ADD_X3_X3_1                     ; S = S + 1
    532 	400c00f9                     # STR_X0_[X2,24]                  ; I->EXPRESSION = hexify
    533 	e10300aa                     # SET_X1_FROM_X0                  ; Put hexify buffer in x1
    534 
    535 :Process_String_Raw_Loop
    536 	60144038                     # LDR_BYTE_W0_[X3]_1              ; Read 1 chars
    537 	e00f1ff8                     # PUSH_X0
    538 	^~hex8 94                    # ^~hex8 FCALL                    ; write them all
    539 	e00741f8                     # POP_X0
    540 	1f0000f1                     # CMP_X0_TO_0                     ; Check for NULL
    541 	40000054                     # SKIP_INST_EQ                    ; Keep looping
    542 	^~Process_String_Raw_Loop 17 # ^~Process_String_Raw_Loop RBRANCH
    543 
    544 :Process_String_Next
    545 	420040f9                     # LDR_X2_[X2]                     ; I = I->NEXT
    546 	5f0000f1                     # CMP_X2_TO_0                     ; IF NULL == I
    547 	40000054                     # SKIP_INST_EQ                    ; Otherwise keep looping
    548 	^~Process_String_loop 17     # ^~Process_String_loop RBRANCH
    549 
    550 	fe0741f8                     # POP_LR
    551 	e30741f8                     # POP_X3                          ; Restore X3
    552 	e20741f8                     # POP_X2                          ; Restore X2
    553 	e10741f8                     # POP_X1                          ; Restore X1
    554 	e00741f8                     # POP_X0                          ; Restore X0
    555 	c0035fd6                     # RETURN
    556 
    557 
    558 ; string_length function
    559 ; Receives CHAR* in X0
    560 ; Returns INT in X0
    561 ; Uses X0 for CH, X1 for S and X2 for INDEX
    562 :string_length
    563 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    564 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    565 	e10300aa                     # SET_X1_FROM_X0                  ; Set S
    566 	020080d2                     # SET_X2_TO_0                     ; INDEX = 0
    567 :string_length_loop
    568 	20686238                     # LDR_BYTE_W0_[X1,X2]             ; S[INDEX]
    569 	1f0000f1                     # CMP_X0_TO_0                     ; IF NULL == S[INDEX]
    570 	41000054                     # SKIP_INST_NE                    ; Stop
    571 	^~string_length_done 14      # ^~string_length_done FBRANCH
    572 
    573 	42040091                     # ADD_X2_X2_1                     ; INDEX = INDEX + 1
    574 	^~string_length_loop 17      # ^~string_length_loop RBRANCH    ; Keep going
    575 
    576 :string_length_done
    577 	e00302aa                     # SET_X0_FROM_X2                  ; RETURN INDEX
    578 	e20741f8                     # POP_X2                          ; Restore X2
    579 	e10741f8                     # POP_X1                          ; Restore X1
    580 	c0035fd6                     # RETURN
    581 
    582 
    583 ; Eval_Immediates function
    584 ; Receives List in X0
    585 ; Updates the list in place; does not modify registers
    586 ; Uses X1 for I->TEXT[0], X2 for I->TEXT[1] and X3 for I
    587 :Eval_Immediates
    588 	e00f1ff8                     # PUSH_X0                         ; Protect X0
    589 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    590 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    591 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    592 	fe0f1ff8                     # PUSH_LR
    593 	e30300aa                     # SET_X3_FROM_X0                  ; I = HEAD
    594 :Eval_Immediates_Loop
    595 	; Check for MACRO
    596 	600440f9                     # LDR_X0_[X3,8]                   ; I->TYPE
    597 	1f0400f1                     # CMP_X0_TO_1                     ; IF MACRO == I-TYPE
    598 	41000054                     # SKIP_INST_NE                    ; Skip to next
    599 	^~Eval_Immediates_Next 14    # ^~Eval_Immediates_Next FBRANCH
    600 
    601 	; Check for NULL EXPRESSION
    602 	600c40f9                     # LDR_X0_[X3,24]                  ; I->EXPRESSION
    603 	1f0000f1                     # CMP_X0_TO_0                     ; IF NULL == I->EXPRESSION
    604 	40000054                     # SKIP_INST_EQ                    ; Skip to next
    605 	^~Eval_Immediates_Next 14    # ^~Eval_Immediates_Next FBRANCH
    606 
    607 	; Check if number
    608 	600840f9                     # LDR_X0_[X3,16]                  ; I->TEXT
    609 	01144038                     # LDR_BYTE_W1_[X0]_1              ; I->TEXT[0]
    610 	02004039                     # LDR_BYTE_W2_[X0]                ; I->TEXT[1]
    611 	^~numerate_string 94         # ^~numerate_string FCALL         ; Convert string to INT
    612 	1f0000f1                     # CMP_X0_TO_0                     ; IF 0 == numerate_number(I->TEXT + 1)
    613 	40000054                     # SKIP_INST_EQ                    ; Has a value
    614 	^~Eval_Immediates_value 14   # ^~Eval_Immediates_value FBRANCH
    615 
    616 	; Last chance for Immediate
    617 	5fc000f1                     # CMP_X2_TO_48                    ; If '0' == I->TEXT[1]
    618 	40000054                     # SKIP_INST_EQ                    ; Skip to next
    619 	^~Eval_Immediates_Next 14    # ^~Eval_Immediates_Next FBRANCH
    620 
    621 :Eval_Immediates_value
    622 	^~express_number 94          # ^~express_number FCALL          ; Convert value to hex string
    623 	600c00f9                     # STR_X0_[X3,24]                  ; I->EXPRESSION = express_number(value, I-TEXT[0])
    624 
    625 :Eval_Immediates_Next
    626 	630040f9                     # LDR_X3_[X3]                     ; I = I->NEXT
    627 	7f0000f1                     # CMP_X3_TO_0                     ; IF NULL == I
    628 	40000054                     # SKIP_INST_EQ                    ; Otherwise keep looping
    629 	^~Eval_Immediates_Loop 17    # ^~Eval_Immediates_Loop RBRANCH
    630 
    631 	fe0741f8                     # POP_LR
    632 	e30741f8                     # POP_X3                          ; Restore X3
    633 	e20741f8                     # POP_X2                          ; Restore X2
    634 	e10741f8                     # POP_X1                          ; Restore X1
    635 	e00741f8                     # POP_X0                          ; Restore X0
    636 	c0035fd6                     # RETURN
    637 
    638 
    639 ; numerate_string function
    640 ; Receives CHAR* in X0
    641 ; Returns value of CHAR* in X0
    642 ; Uses X0 for VALUE, X1 for S, X2 for CH and X4 for NEGATIVE?
    643 :numerate_string
    644 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    645 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    646 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    647 	e40f1ff8                     # PUSH_X4
    648 	e10300aa                     # SET_X1_FROM_X0                  ; put S in correct place
    649 	000080d2                     # SET_X0_TO_0                     ; Initialize to Zero
    650 :numerate_string_loop
    651 	22044039                     # LDR_BYTE_W2_[X1,1]              ; S[1]
    652 	5fe001f1                     # CMP_X2_TO_120                   ; IF 'x' == S[1]
    653 	41000054                     # SKIP_INST_NE                    ; Deal with hex input
    654 	^~numerate_hex 14            # ^~numerate_hex FBRANCH
    655 
    656 	; Assume decimal input
    657 	040080d2                     # SET_X4_TO_0                     ; Assume no negation
    658 	22004039                     # LDR_BYTE_W2_[X1]                ; S[0]
    659 	5fb400f1                     # CMP_X2_TO_45                    ; IF '-' == S[0]
    660 	40000054                     # SKIP_INST_EQ                    ; Skip negation
    661 	^~numerate_decimal 14        # ^~numerate_decimal FBRANCH
    662 
    663 	240080d2                     # SET_X4_TO_1                     ; Set FLAG
    664 	21040091                     # ADD_X1_X1_1                     ; S = S + 1
    665 
    666 :numerate_decimal
    667 	22004039                     # LDR_BYTE_W2_[X1]                ; S[0]
    668 	5f0000f1                     # CMP_X2_TO_0                     ; IF NULL == S[0]
    669 	41000054                     # SKIP_INST_NE                    ; We are done
    670 	^~numerate_decimal_done 14   # ^~numerate_decimal_done FBRANCH
    671 
    672 	e10f1ff8                     # PUSH_X1
    673 	410180d2                     # SET_X1_TO_10
    674 	007c019b                     # MUL_X0_X0_X1                    ; VALUE = VALUE * 10
    675 	e10741f8                     # POP_X1
    676 
    677 	42c000d1                     # SUB_X2_X2_48                    ; CH = CH - '0'
    678 	5f2400f1                     # CMP_X2_TO_9                     ; Check for illegal
    679 	4d000054                     # SKIP_INST_LE                    ; If CH > '9'
    680 	^~numerate_string_fail 14    # ^~numerate_string_fail FBRANCH
    681 	5f0000f1                     # CMP_X2_TO_0                     ; Check for illegal
    682 	4a000054                     # SKIP_INST_GE                    ; IF CH < 0
    683 	^~numerate_string_fail 14    # ^~numerate_string_fail FBRANCH
    684 	0000028b                     # ADD_X0_X0_X2                    ; VALUE = VALUE + CH
    685 	21040091                     # ADD_X1_X1_1                     ; S = S + 1
    686 	^~numerate_decimal 17        # ^~numerate_decimal RBRANCH      ; Keep looping
    687 
    688 :numerate_decimal_done
    689 	9f0400f1                     # CMP_X4_TO_1                     ; Check if need to negate
    690 	40000054                     # SKIP_INST_EQ                    ; Nope
    691 	^~numerate_string_done 14    # ^~numerate_string_done FBRANCH
    692 
    693 	e00300cb                     # NEG_X0_X0                       ; VALUE = VALUE * -1
    694 	^~numerate_string_done 14    # ^~numerate_string_done FBRANCH  ; Done
    695 
    696 :numerate_hex
    697 	21080091                     # ADD_X1_X1_2                     ; S = S + 2
    698 :numerate_hex_loop
    699 	22004039                     # LDR_BYTE_W2_[X1]                ; S[0]
    700 	5f0000f1                     # CMP_X2_TO_0                     ; IF NULL == S[0]
    701 	41000054                     # SKIP_INST_NE                    ; We are done
    702 	^~numerate_string_done 14    # ^~numerate_string_done FBRANCH
    703 
    704 	00ec7cd3                     # LSL_X0_X0_4                     ; VALUE = VALUE << 4
    705 	42c000d1                     # SUB_X2_X2_48                    ; CH = CH - '0'
    706 	5f2800f1                     # CMP_X2_TO_10                    ; IF 10 >= CH
    707 	4a000054                     # SKIP_INST_GE                    ; NO
    708 	^~numerate_hex_digit 14      # ^~numerate_hex_digit FBRANCH
    709 	421c00d1                     # SUB_X2_X2_7                     ; Push A-F into range
    710 :numerate_hex_digit
    711 	5f3c00f1                     # CMP_X2_TO_15                    ; Check for illegal
    712 	4d000054                     # SKIP_INST_LE                    ; If CH > 'F'
    713 	^~numerate_string_fail 14    # ^~numerate_string_fail FBRANCH
    714 	5f0000f1                     # CMP_X2_TO_0                     ; Check for illegal
    715 	4a000054                     # SKIP_INST_GE                    ; IF CH < 0
    716 	^~numerate_string_fail 14    # ^~numerate_string_fail FBRANCH
    717 	0000028b                     # ADD_X0_X0_X2                    ; VALUE = VALUE + CH
    718 	21040091                     # ADD_X1_X1_1                     ; S = S + 1
    719 	^~numerate_hex_loop 17       # ^~numerate_hex_loop RBRANCH     ; Keep looping
    720 
    721 :numerate_string_fail
    722 	000080d2                     # SET_X0_TO_0                     ; Return ZERO
    723 
    724 :numerate_string_done
    725 	e40741f8                     # POP_X4                          ; Restore X4
    726 	e30741f8                     # POP_X3                          ; Restore X3
    727 	e20741f8                     # POP_X2                          ; Restore X2
    728 	e10741f8                     # POP_X1                          ; Restore X1
    729 	c0035fd6                     # RETURN
    730 
    731 
    732 ; express_number function
    733 ; Receives INT in X0 and CHAR in X1
    734 ; Allocates a string and expresses the value in hex
    735 ; Returns string in X0
    736 ; Uses X0 for VALUE, X1 for S and X2 for CH
    737 :express_number
    738 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    739 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    740 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    741 	fe0f1ff8                     # PUSH_LR
    742 
    743 	e20301aa                     # SET_X2_FROM_X1                  ; Put CH in right place
    744 	e10300aa                     # SET_X1_FROM_X0                  ; Protect VALUE
    745 	5f9400f1                     # CMP_X2_TO_37                    ; IF '%' == CH
    746 	40000054                     # SKIP_INST_EQ                    ; Otherwise try @
    747 	^~express_number2 14         # ^~express_number2 FBRANCH
    748 
    749 	200180d2                     # SET_X0_TO_9                     ; We need 9bytes
    750 	^~malloc 97                  # ^~malloc RCALL                  ; Get S pointer
    751 	e30300aa                     # SET_X3_FROM_X0                  ; Put S and VALUE in place
    752 	e00301aa                     # SET_X0_FROM_X1
    753 	e10303aa                     # SET_X1_FROM_X3
    754 	e10f1ff8                     # PUSH_X1                         ; Protect S
    755 	^~hex32l 94                  # ^~hex32l FCALL                  ; Store 32bits
    756 	^~express_number_done 14     # ^~express_number_done FBRANCH   ; done
    757 
    758 :express_number2
    759 	5f0001f1                     # CMP_X2_TO_64                    ; IF '@' == CH
    760 	40000054                     # SKIP_INST_EQ                    ; Othrewise try !
    761 	^~express_number1 14         # ^~express_number1 FBRANCH
    762 
    763 	a00080d2                     # SET_X0_TO_5                     ; We need 5bytes
    764 	^~malloc 97                  # ^~malloc RCALL                  ; Get S pointer
    765 	e30300aa                     # SET_X3_FROM_X0                  ; Put S and VALUE in place
    766 	e00301aa                     # SET_X0_FROM_X1
    767 	e10303aa                     # SET_X1_FROM_X3
    768 	e10f1ff8                     # PUSH_X1                         ; Protect S
    769 	^~hex16l 94                  # ^~hex16l FCALL                  ; Store 16bits
    770 	^~express_number_done 14     # ^~express_number_done FBRANCH   ; done
    771 
    772 :express_number1
    773 	600080d2                     # SET_X0_TO_3                     ; We need 3bytes
    774 	^~malloc 97                  # ^~malloc RCALL                  ; Get S pointer
    775 	e30300aa                     # SET_X3_FROM_X0                  ; Put S and VALUE in place
    776 	e00301aa                     # SET_X0_FROM_X1
    777 	e10303aa                     # SET_X1_FROM_X3
    778 	e10f1ff8                     # PUSH_X1                         ; Protect S
    779 	^~hex8 94                    # ^~hex8 FCALL                    ; Store 8bit
    780 
    781 :express_number_done
    782 	e00741f8                     # POP_X0                          ; Restore S
    783 	fe0741f8                     # POP_LR
    784 	e30741f8                     # POP_X3                          ; Restore X3
    785 	e20741f8                     # POP_X2                          ; Restore X2
    786 	e10741f8                     # POP_X1                          ; Restore X1
    787 	c0035fd6                     # RETURN
    788 
    789 
    790 ; HEX to ascii routine
    791 ; Receives INT in X0 and CHAR* in X1
    792 ; Stores ascii of INT in CHAR*
    793 ; Returns only modifying X0
    794 :hex64l
    795 	fe0f1ff8                     # PUSH_LR
    796 	e00f1ff8                     # PUSH_X0                         ; Protect top 32
    797 	^~hex32l 94                  # ^~hex32l FCALL                  ; Store it
    798 	e00741f8                     # POP_X0                          ; do top 32
    799 	00fc60d3                     # LSR_X0_X0_32                    ; do bottom 32 first
    800 	^~hex32l 94                  # ^~hex32l FCALL
    801 	fe0741f8                     # POP_LR
    802 	c0035fd6                     # RETURN
    803 
    804 :hex32l
    805 	fe0f1ff8                     # PUSH_LR
    806 	e00f1ff8                     # PUSH_X0                         ; Protect top 16
    807 	^~hex16l 94                  # ^~hex16l FCALL                  ; Store it
    808 	e00741f8                     # POP_X0                          ; do top 16
    809 	00fc50d3                     # LSR_X0_X0_16                    ; do bottom 16 first
    810 	^~hex16l 94                  # ^~hex16l FCALL
    811 	fe0741f8                     # POP_LR
    812 	c0035fd6                     # RETURN
    813 
    814 :hex16l
    815 	fe0f1ff8                     # PUSH_LR
    816 	e00f1ff8                     # PUSH_X0                         ; Protect top byte
    817 	^~hex8 94                    # ^~hex8 FCALL                    ; Store it
    818 	e00741f8                     # POP_X0                          ; do high byte
    819 	00fc48d3                     # LSR_X0_X0_8                     ; do bottom byte first
    820 	^~hex8 94                    # ^~hex8 FCALL
    821 	fe0741f8                     # POP_LR
    822 	c0035fd6                     # RETURN
    823 
    824 :hex8
    825 	fe0f1ff8                     # PUSH_LR
    826 	e00f1ff8                     # PUSH_X0                         ; Protect bottom nibble
    827 	00fc44d3                     # LSR_X0_X0_4                     ; do high nibble first
    828 	^~hex4 94                    # ^~hex4 FCALL                    ; Store it
    829 	e00741f8                     # POP_X0                          ; do low nibble
    830 	^~hex4 94                    # ^~hex4 FCALL
    831 	fe0741f8                     # POP_LR
    832 	c0035fd6                     # RETURN
    833 
    834 :hex4
    835 	000c4092                     # AND_X0_X0_0xF                   ; isolate nibble
    836 	00c00091                     # ADD_X0_X0_48                    ; convert to ascii
    837 	1fe400f1                     # CMP_X0_TO_57                    ; valid digit?
    838 	4c000054                     # SKIP_INST_GT                    ; yes
    839 	^~hex1 14                    # ^~hex1 FBRANCH
    840 	001c0091                     # ADD_X0_X0_7                     ; use alpha range
    841 :hex1
    842 	20140038                     # STR_BYTE_W0_[X1]_1              ; store result
    843 	c0035fd6                     # RETURN
    844 
    845 
    846 ; Preserve_Other function
    847 ; Receives List in X0
    848 ; Updates the list in place; does not modify registers
    849 ; Uses X0 for I, X1 for I->TEXT
    850 :Preserve_Other
    851 	e00f1ff8                     # PUSH_X0                         ; Protect X0
    852 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    853 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    854 	e30f1ff8                     # PUSH_X3                         ; Protect X3
    855 :Preserve_Other_Loop
    856 	010c40f9                     # LDR_X1_[X0,24]                  ; I->EXPRESSION
    857 	3f0000f1                     # CMP_X1_TO_0                     ; IF NULL == I->EXPRESSION
    858 	40000054                     # SKIP_INST_EQ                    ; Otherwise next
    859 	^~Preserve_Other_Next 14     # ^~Preserve_Other_Next FBRANCH
    860 
    861 	; Needs preserving
    862 	010840f9                     # LDR_X1_[X0,16]                  ; I->TEXT
    863 	010c00f9                     # STR_X1_[X0,24]                  ; I->EXPRESSION = I->TEXT
    864 
    865 :Preserve_Other_Next
    866 	000040f9                     # LDR_X0_[X0]                     ; I = I->NEXT
    867 	1f0000f1                     # CMP_X0_TO_0                     ; IF NULL == I
    868 	40000054                     # SKIP_INST_EQ                    ; Otherwise keep looping
    869 	^~Preserve_Other_Loop 17     # ^~Preserve_Other_Loop RBRANCH
    870 
    871 	e30741f8                     # POP_X3                          ; Restore X3
    872 	e20741f8                     # POP_X2                          ; Restore X2
    873 	e10741f8                     # POP_X1                          ; Restore X1
    874 	e00741f8                     # POP_X0                          ; Restore X0
    875 	c0035fd6                     # RETURN
    876 
    877 
    878 ; Print_Hex function
    879 ; Receives list in X0
    880 ; walks the list and prints the I->EXPRESSION for all nodes followed by newline
    881 ; Uses X1 for I
    882 :Print_Hex
    883 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    884 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    885 	fe0f1ff8                     # PUSH_LR
    886 	e1030daa                     # SET_X1_FROM_X13                 ; I = Head
    887 :Print_Hex_Loop
    888 	200440f9                     # LDR_X0_[X1,8]                   ; I->TYPE
    889 	1f0400f1                     # CMP_X0_TO_1                     ; IF MACRO == I->TYPE
    890 	41000054                     # SKIP_INST_NE                    ; Skip
    891 	^~Print_Hex_Next 14          # ^~Print_Hex_Next FBRANCH
    892 
    893 	200c40f9                     # LDR_X0_[X1,24]                  ; Using EXPRESSION
    894 	^~File_Print 94              # ^~File_Print FCALL              ; Print it
    895 	400180d2                     # SET_X0_TO_10                    ; NEWLINE
    896 	^~fputc 94                   # ^~fputc FCALL                   ; Append it
    897 
    898 :Print_Hex_Next
    899 	210040f9                     # LDR_X1_[X1]                     ; Iterate to next Token
    900 	3f0000f1                     # CMP_X1_TO_0                     ; Check for NULL
    901 	40000054                     # SKIP_INST_EQ                    ; Otherwise keep looping
    902 	^~Print_Hex_Loop 17          # ^~Print_Hex_Loop RBRANCH
    903 
    904 	fe0741f8                     # POP_LR
    905 	e20741f8                     # POP_X2                          ; Restore X2
    906 	e10741f8                     # POP_X1                          ; Restore X1
    907 	c0035fd6                     # RETURN
    908 
    909 
    910 ; File_Print function
    911 ; Receives CHAR* in X0
    912 ; calls fputc for every non-null char
    913 :File_Print
    914 	e10f1ff8                     # PUSH_X1                         ; Protect X1
    915 	e20f1ff8                     # PUSH_X2                         ; Protect X2
    916 	fe0f1ff8                     # PUSH_LR
    917 	e10300aa                     # SET_X1_FROM_X0                  ; Protect S
    918 	1f0000f1                     # CMP_X0_TO_0                     ; Protect against nulls
    919 	41000054                     # SKIP_INST_NE                    ; Simply don't try to print them
    920 	^~File_Print_Done 14         # ^~File_Print_Done FBRANCH
    921 :File_Print_Loop
    922 	20004039                     # LDR_BYTE_W0_[X1]                ; Read byte
    923 	1f0000f1                     # CMP_X0_TO_0                     ; Check for NULL
    924 	41000054                     # SKIP_INST_NE                    ; Stop at NULL
    925 	^~File_Print_Done 14         # ^~File_Print_Done FBRANCH
    926 
    927 	^~fputc 94                   # ^~fputc FCALL                   ; write it
    928 	21040091                     # ADD_X1_X1_1                     ; S = S + 1
    929 	^~File_Print_Loop 17         # ^~File_Print_Loop RBRANCH       ; Keep going
    930 
    931 :File_Print_Done
    932 	fe0741f8                     # POP_LR
    933 	e20741f8                     # POP_X2                          ; Restore X2
    934 	e10741f8                     # POP_X1                          ; Restore X1
    935 	c0035fd6                     # RETURN
    936 
    937 
    938 ; fputc function
    939 ; receives CHAR in X0 and FILE* in X14
    940 ; writes char and Returns
    941 :fputc
    942 	e10f1ff8                     # PUSH_X1
    943 	e20f1ff8                     # PUSH_X2
    944 
    945 	e00f1ff8                     # PUSH_X0                         ; We are writing x0
    946 	e1030091                     # SET_X1_FROM_SP                  ; Get stack address
    947 	e0030eaa                     # SET_X0_FROM_X14                 ; Write to target file
    948 	080880d2                     # SET_X8_TO_SYS_WRITE             ; the syscall number for write
    949 	220080d2                     # SET_X2_TO_1                     ; set the size of chars we want
    950 
    951 	010000d4                     # SYSCALL                         ; call the Kernel
    952 
    953 	e00741f8                     # POP_X0                          ; Restore stack
    954 	e20741f8                     # POP_X2                          ; Restore X3
    955 	e10741f8                     # POP_X1
    956 	c0035fd6                     # RETURN
    957 
    958 :ELF_end