M0-macro.s 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. ; Copyright (C) 2016 Jeremiah Orians
  2. ; This file is part of stage0.
  3. ;
  4. ; stage0 is free software: you can redistribute it and/or modify
  5. ; it under the terms of the GNU General Public License as published by
  6. ; the Free Software Foundation, either version 3 of the License, or
  7. ; (at your option) any later version.
  8. ;
  9. ; stage0 is distributed in the hope that it will be useful,
  10. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ; GNU General Public License for more details.
  13. ;
  14. ; You should have received a copy of the GNU General Public License
  15. ; along with stage0. If not, see <http://www.gnu.org/licenses/>.
  16. :start
  17. ;; We will be using R13 for storage of Head
  18. LOADUI R14 0x700 ; Our malloc pointer (Initialized)
  19. LOADUI R15 $stack ; Put stack at end of program
  20. ;; Main program
  21. ;; Reads contents of Tape_01 and applies all Definitions
  22. ;; Writes results to Tape_02
  23. ;; Accepts no arguments and HALTS when done
  24. :main
  25. CALLI R15 @Tokenize_Line ; Call Tokenize_Line
  26. CALLI R15 @reverse_list ; Reverse the list of tokens
  27. CALLI R15 @Identify_Macros ; Tag all nodes that are macros
  28. CALLI R15 @Line_Macro ; Apply macros down nodes
  29. CALLI R15 @Process_String ; Convert string values to Hex16
  30. CALLI R15 @Eval_Immediates ; Convert numbers to hex
  31. CALLI R15 @Preserve_Other ; Ensure labels/Pointers aren't lost
  32. CALLI R15 @Print_Hex ; Write Nodes to Tape_02
  33. HALT ; We are Done
  34. ;; Tokenize_Line function
  35. ;; Opens tape_01 and reads into a backwards linked list in R13
  36. ;; Returns to whatever called it
  37. :Tokenize_Line
  38. ;; Prep TAPE_01
  39. LOADUI R0 0x1100
  40. FOPEN_READ
  41. FALSE R13 ; Head is NULL
  42. MOVE R1 R0 ; Read Tape_01
  43. :Tokenize_Line_0
  44. FGETC ; Get a Char
  45. ;; Deal with lines comments starting with #
  46. CMPSKIPI.NE R0 35
  47. JUMP @Purge_Line_Comment
  48. ;; Deal with Line comments starting with ;
  49. CMPSKIPI.NE R0 59
  50. JUMP @Purge_Line_Comment
  51. ;; Deal with Tab
  52. CMPSKIPI.NE R0 9
  53. JUMP @Tokenize_Line_0 ; Throw away byte and try again
  54. ;; Deal with New line
  55. CMPSKIPI.NE R0 10
  56. JUMP @Tokenize_Line_0 ; Throw away byte and try again
  57. ;; Deal with space characters
  58. CMPSKIPI.NE R0 32
  59. JUMP @Tokenize_Line_0 ; Throw away byte and try again
  60. ;; Stop if EOF
  61. CMPSKIPI.GE R0 0
  62. JUMP @Tokenize_Line_Done
  63. ;; Allocate a new Node
  64. COPY R2 R14 ; Get address of new Node
  65. ADDUI R14 R14 16 ; Allocate 16 Bytes
  66. STORE32 R14 R2 8 ; Set Text pointer
  67. ;; Deal with Strings wrapped in "
  68. CMPSKIPI.NE R0 34
  69. JUMP @Store_String
  70. ;; Deal with Strings wrapped in '
  71. CMPSKIPI.NE R0 39
  72. JUMP @Store_String
  73. ;; Everything else is an atom store it
  74. CALLI R15 @Store_Atom
  75. :Tokenize_Line_1
  76. STORE32 R13 R2 0 ; Set p->next to head
  77. MOVE R13 R2 ; Set head to p
  78. JUMP @Tokenize_Line_0 ; Keep getting tokens
  79. :Tokenize_Line_Done
  80. ;; Done reading File
  81. LOADUI R0 0x1100 ; Close TAPE_01
  82. FCLOSE
  83. RET R15
  84. ;; reverse_list Function
  85. ;; Reverses the list given in R13
  86. :reverse_list
  87. ;; Initialize
  88. COPY R0 R13 ; Using R0 as head
  89. FALSE R1 ; Using R1 as root
  90. ; Using R2 as next
  91. :reverse_list_0
  92. JUMP.Z R0 @reverse_list_done ; Stop if NULL == head
  93. LOAD R2 R0 0 ; next = head->next
  94. STORE R1 R0 0 ; head->next = root
  95. MOVE R1 R0 ; root = head
  96. MOVE R0 R2 ; head = next
  97. JUMP @reverse_list_0 ; Keep looping
  98. :reverse_list_done
  99. ;; Clean up
  100. MOVE R13 R1 ; Set token_list to root
  101. RET R15
  102. ;; Purge_Line_Comment Function
  103. ;; Receives char in R0 and desired input in R1
  104. ;; Modifies R0
  105. ;; Returns to Tokenize_Line as if the entire line
  106. ;; Comment never existed
  107. :Purge_Line_Comment
  108. FGETC ; Get another Char
  109. CMPSKIPI.E R0 10 ; Stop When LF is reached
  110. JUMP @Purge_Line_Comment ; Otherwise keep looping
  111. JUMP @Tokenize_Line_0 ; Return as if this never happened
  112. ;; Store_String function
  113. ;; Receives Char in R0, desired input in R1
  114. ;; And node pointer in R2
  115. ;; Modifies node Text to point to string and sets
  116. ;; Type to string.
  117. :Store_String
  118. ;; Initialize
  119. COPY R3 R0 ; Copy Char for comparison
  120. :Store_String_0
  121. STORE8 R0 R14 0 ; Store the Byte
  122. FGETC ; Get next Byte
  123. ADDUI R14 R14 1 ; Prep for next loop
  124. CMPJUMPI.NE R0 R3 @Store_String_0 ; Loop if matching not found
  125. ;; Clean up
  126. ADDUI R14 R14 4 ; Correct Malloc
  127. LOADUI R0 2 ; Using type string
  128. STORE32 R0 R2 4 ; Set node type
  129. JUMP @Tokenize_Line_1
  130. ;; Store_Atom function
  131. ;; Receives Char in R0, desired input in R1
  132. ;; And node pointer in R2
  133. ;; Modifies node Text to point to string
  134. :Store_Atom
  135. STORE8 R0 R14 0 ; Store the Byte
  136. FGETC ; Get next Byte
  137. ADDUI R14 R14 1 ; Prep for next loop
  138. CMPSKIPI.NE R0 9 ; If char is Tab
  139. JUMP @Store_Atom_Done ; Be done
  140. CMPSKIPI.NE R0 10 ; If char is LF
  141. JUMP @Store_Atom_Done ; Be done
  142. CMPSKIPI.NE R0 32 ; If char is Space
  143. JUMP @Store_Atom_Done ; Be done
  144. ;; Otherwise loop
  145. JUMP @Store_Atom
  146. :Store_Atom_Done
  147. ;; Cleanup
  148. ADDUI R14 R14 1 ; Correct Malloc
  149. RET R15
  150. ;; strcmp function
  151. ;; Receives pointers to null terminated strings
  152. ;; In R0 and R1
  153. ;; Returns if they are equal in R0
  154. ;; Returns to whatever called it
  155. :strcmp
  156. ;; Preserve registers
  157. PUSHR R1 R15
  158. PUSHR R2 R15
  159. PUSHR R3 R15
  160. PUSHR R4 R15
  161. ;; Setup registers
  162. MOVE R2 R0 ; Put R0 in a safe place
  163. MOVE R3 R1 ; Put R1 in a safe place
  164. FALSE R4 ; Starting at index 0
  165. :cmpbyte
  166. LOADXU8 R0 R2 R4 ; Get a byte of our first string
  167. LOADXU8 R1 R3 R4 ; Get a byte of our second string
  168. ADDUI R4 R4 1 ; Prep for next loop
  169. CMP R0 R0 R1 ; Compare the bytes
  170. CMPSKIPI.E R1 0 ; Stop if byte is NULL
  171. JUMP.E R0 @cmpbyte ; Loop if bytes are equal
  172. ;; Done
  173. ;; Restore registers
  174. POPR R4 R15
  175. POPR R3 R15
  176. POPR R2 R15
  177. POPR R1 R15
  178. RET R15
  179. ;; Identify_Macros Function
  180. ;; If the text stored in its Text segment matches
  181. ;; DEFINE, flag it and Collapse it down to a single Node
  182. ;; Loop until all nodes are checked
  183. ;; Return to whatever called it
  184. :Identify_Macros
  185. ;; Initializ
  186. LOADUI R1 $Identify_Macros_string
  187. COPY R2 R13 ; i = head
  188. ;; Main Loop
  189. :Identify_Macros_0
  190. LOAD32 R0 R2 8 ; Get Pointer to Text
  191. CALLI R15 @strcmp
  192. JUMP.NE R0 @Identify_Macros_1
  193. ;; It is a definition
  194. ;; Set i->Type = macro
  195. LOADUI R0 1 ; The Enum value for macro
  196. STORE32 R0 R2 4 ; Set node type
  197. ;; Set i->Text = i->Next->Text
  198. LOAD32 R4 R2 0 ; Get Next
  199. LOAD32 R0 R4 8 ; Get Next->Text
  200. STORE32 R0 R2 8 ; Set i->Text = Next->Text
  201. ;; Set i->Expression = i->next->next->Text
  202. LOAD32 R4 R4 0 ; Get Next->Next
  203. LOAD32 R0 R4 8 ; Get Next->Next->Text
  204. LOAD32 R3 R4 4 ; Get Next->Next->type
  205. CMPSKIPI.NE R3 2 ; If node is a string
  206. ADDUI R0 R0 1 ; Skip first char
  207. STORE32 R0 R2 12 ; Set Expression = Next->Next->Text
  208. ;; Set i->Next = i->Next->Next->Next
  209. LOAD32 R4 R4 0 ; Get Next->Next->Next
  210. STORE32 R4 R2 0 ; Set i->Next = Next->Next->Next
  211. :Identify_Macros_1
  212. LOAD32 R2 R2 0 ; Get node->next
  213. JUMP.NZ R2 @Identify_Macros_0 ; Loop if i not NULL
  214. RET R15
  215. :Identify_Macros_string
  216. "DEFINE"
  217. ;; Line_Macro Function
  218. ;; Receives a node pointer in R0
  219. ;; Causes macros to be applied
  220. ;; Returns to whatever called it
  221. :Line_Macro
  222. ;; Initialize
  223. COPY R0 R13 ; Start with Head
  224. ;; Main loop
  225. :Line_Macro_0
  226. LOAD32 R3 R0 4 ; Load Node type
  227. LOAD32 R2 R0 12 ; Load Expression pointer
  228. LOAD32 R1 R0 8 ; Load Text pointer
  229. LOAD32 R0 R0 0 ; Load Next pointer
  230. CMPSKIPI.NE R3 1 ; If a macro
  231. CALLI R15 @setExpression ; Apply to other nodes
  232. JUMP.NZ R0 @Line_Macro_0 ; If Next is Null Don't loop
  233. RET R15
  234. ;; setExpression Function
  235. ;; Receives a node pointer in R0
  236. ;; A string pointer to compare against in R1
  237. ;; A string pointer for replacement in R2
  238. ;; Doesn't modify any registers
  239. ;; Returns to whatever called it
  240. :setExpression
  241. ;; Preserve registers
  242. PUSHR R0 R15
  243. ;; Initialize
  244. COPY R4 R0 ; Use R4 for Node pointer
  245. :setExpression_0
  246. LOAD32 R3 R4 4 ; Load type into R3
  247. CMPSKIPI.NE R3 1 ; Check if Macro
  248. JUMP @setExpression_1 ; Move to next if Macro
  249. LOAD32 R0 R4 8 ; Load Text pointer into R0 for Comparision
  250. CALLI R15 @strcmp ; compare Text and Macro Text
  251. JUMP.NE R0 @setExpression_1 ; Move to next if not Match
  252. STORE32 R2 R4 12 ; Set node->Expression = Exp
  253. :setExpression_1
  254. LOAD32 R4 R4 0 ; Load Next
  255. JUMP.NZ R4 @setExpression_0 ; Loop if next isn't NULL
  256. :setExpression_Done
  257. ;; Restore registers
  258. POPR R0 R15
  259. RET R15
  260. ;; Process_String Function
  261. ;; Receives a Node in R0
  262. ;; Doesn't modify registers
  263. ;; Returns back to whatever called it
  264. :Process_String
  265. ;; Initialize
  266. COPY R0 R13 ; Start with Head
  267. :Process_String_0
  268. ;; Get node type
  269. LOAD32 R1 R0 4 ; Load Type
  270. CMPSKIPI.E R1 2 ; If not a string
  271. JUMP @Process_String_Done ; Just go to next
  272. ;; Its a string
  273. LOAD32 R1 R0 8 ; Get Text pointer
  274. LOAD8 R2 R1 0 ; Get first char of Text
  275. ;; Deal with '
  276. CMPSKIPI.E R2 39 ; If char is not '
  277. JUMP @Process_String_1 ; Move to next label
  278. ;; Simply use Hex strings as is
  279. ADDUI R1 R1 1 ; Move Text pointer by 1
  280. STORE32 R1 R0 12 ; Set expression to Text + 1
  281. JUMP @Process_String_Done ; And move on
  282. :Process_String_1
  283. ;; Deal with (")
  284. CALLI R15 @Hexify_String
  285. :Process_String_Done
  286. LOAD32 R0 R0 0 ; Load Next
  287. JUMP.NZ R0 @Process_String_0 ; If Next isn't NULL Recurse down list
  288. RET R15
  289. ;; Hexify_String Function
  290. ;; Receives a node pointer in R0
  291. ;; Converts Quoted text to Hex values
  292. ;; Pads values up to multiple of 4 bytes
  293. ;; Doesn't modify registers
  294. ;; Returns to whatever called it
  295. :Hexify_String
  296. ;; Preserve Registers
  297. PUSHR R0 R15
  298. ;; Initialize
  299. MOVE R1 R0 ; Move R0 out of the way
  300. STORE32 R14 R1 12 ; Set node expression pointer
  301. LOAD32 R1 R1 8 ; Load Text pointer into R2
  302. ADDUI R1 R1 1 ; SKip leading "
  303. ;; Main Loop
  304. :Hexify_String_0
  305. LOAD32 R0 R1 0 ; Load 4 bytes into R0 from Text
  306. ANDI R2 R0 0xFF ; Preserve byte to check for NULL
  307. CALLI R15 @hex32 ; Convert to hex and store in Expression
  308. ADDUI R1 R1 4 ; Pointer Text pointer to next 4 bytes
  309. JUMP.NZ R2 @Hexify_String_0
  310. ;; Done
  311. ADDUI R14 R14 1 ; Correct malloc value
  312. POPR R0 R15
  313. RET R15
  314. ;; hex32 functionality
  315. ;; Accepts 32bit value in R0
  316. ;; Require R14 to be the heap pointer
  317. ;; WILL ALTER R14 !
  318. ;; Returns to whatever called it
  319. :hex32
  320. PUSHR R0 R15
  321. SR0I R0 16 ; Do high word first
  322. CALLI R15 @hex16
  323. POPR R0 R15
  324. :hex16
  325. PUSHR R0 R15
  326. SR0I R0 8 ; Do high byte first
  327. CALLI R15 @hex8
  328. POPR R0 R15
  329. :hex8
  330. PUSHR R0 R15
  331. SR0I R0 4 ; Do high nybble first
  332. CALLI R15 @hex4
  333. POPR R0 R15
  334. :hex4
  335. ANDI R0 R0 0xF ; isolate nybble
  336. ADDUI R0 R0 48 ; convert to ascii
  337. CMPSKIPI.LE R0 57 ; If nybble was greater than '9'
  338. ADDUI R0 R0 7 ; Shift it into 'A' range of ascii
  339. STORE8 R0 R14 0 ; Store Hex Char
  340. ADDUI R14 R14 1 ; Increment address pointer
  341. RET R15 ; Get next nybble or return if done
  342. ;; Eval_Immediates function
  343. ;; Receives a node in R0
  344. ;; Converts number into Hex
  345. ;; And write into Memory and fix pointer
  346. :Eval_Immediates
  347. ;; Initialize
  348. COPY R3 R13 ; Start with Head
  349. ;; Process Text
  350. :Eval_Immediates_0
  351. LOAD32 R2 R3 0 ; Load Node->Next
  352. LOAD32 R0 R3 12 ; Load Expression pointer
  353. JUMP.NZ R0 @Eval_Immediates_1 ; Don't do anything if Expression is set
  354. LOAD32 R0 R3 4 ; Load Node type
  355. JUMP.NZ R0 @Eval_Immediates_1 ; Don't do anything if Typed
  356. LOAD32 R0 R3 8 ; Load Text pointer
  357. LOAD8 R1 R0 0 ; Get first char of Text
  358. CALLI R15 @numerate_string ; Convert to number in R0
  359. CMPSKIPI.E R1 48 ; Skip next comparision if '0'
  360. JUMP.Z R0 @Eval_Immediates_1 ; Don't do anything if string isn't a number
  361. STORE R14 R3 12 ; Preserve pointer to expression
  362. CALLI R15 @hex16 ; Shove our number into expression
  363. ADDUI R14 R14 1 ; Allocate enough space for a null
  364. ;; Handle looping
  365. :Eval_Immediates_1
  366. MOVE R3 R2 ; Prepare for next loop
  367. JUMP.NZ R3 @Eval_Immediates_0 ; And loop
  368. RET R15
  369. ;; numerate_string function
  370. ;; Receives pointer To string in R0
  371. ;; Returns number in R0 equal to value of string
  372. ;; Or Zero in the event of invalid string
  373. :numerate_string
  374. ;; Preserve Registers
  375. PUSHR R1 R15
  376. PUSHR R2 R15
  377. PUSHR R3 R15
  378. PUSHR R4 R15
  379. ;; Initialize
  380. MOVE R1 R0 ; Get Text pointer out of the way
  381. FALSE R2 ; Set Negative flag to false
  382. FALSE R3 ; Set current count to Zero
  383. LOAD8 R0 R1 1 ; Get second byte
  384. CMPSKIPI.NE R0 120 ; If the second byte is x
  385. JUMP @numerate_string_hex ; treat string like hex
  386. ;; Deal with Decimal input
  387. LOADUI R4 10 ; Multiply by 10
  388. LOAD8 R0 R1 0 ; Get a byte
  389. CMPSKIPI.NE R0 45 ; If - toggle flag
  390. TRUE R2 ; So that we know to negate
  391. CMPSKIPI.E R2 0 ; If toggled
  392. ADDUI R1 R1 1 ; Move to next
  393. :numerate_string_dec
  394. LOAD8 R0 R1 0 ; Get a byte
  395. CMPSKIPI.NE R0 0 ; If NULL
  396. JUMP @numerate_string_done ; Be done
  397. MUL R3 R3 R4 ; Shift counter by 10
  398. SUBI R0 R0 48 ; Convert ascii to number
  399. CMPSKIPI.GE R0 0 ; If less than a number
  400. JUMP @numerate_string_done ; Terminate NOW
  401. CMPSKIPI.L R0 10 ; If more than a number
  402. JUMP @numerate_string_done ; Terminate NOW
  403. ADDU R3 R3 R0 ; Don't add to the count
  404. ADDUI R1 R1 1 ; Move onto next byte
  405. JUMP @numerate_string_dec
  406. ;; Deal with Hex input
  407. :numerate_string_hex
  408. LOAD8 R0 R1 0 ; Get a byte
  409. CMPSKIPI.E R0 48 ; All hex strings start with 0x
  410. JUMP @numerate_string_done ; Be done if not a match
  411. ADDUI R1 R1 2 ; Move to after leading 0x
  412. :numerate_string_hex_0
  413. LOAD8 R0 R1 0 ; Get a byte
  414. JUMP.Z R0 @numerate_string_done ; If NULL Be done
  415. SL0I R3 4 ; Shift counter by 16
  416. SUBI R0 R0 48 ; Convert ascii number to number
  417. CMPSKIPI.L R0 10 ; If A-F
  418. SUBI R0 R0 7 ; Shove into Range
  419. CMPSKIPI.L R0 16 ; If a-f
  420. SUBI R0 R0 32 ; Shove into Range
  421. ADDU R3 R3 R0 ; Add to the count
  422. ADDUI R1 R1 1 ; Get next Hex
  423. JUMP @numerate_string_hex_0
  424. ;; Clean up
  425. :numerate_string_done
  426. CMPSKIPI.E R2 0 ; If Negate flag has been set
  427. NEG R3 R3 ; Make the number negative
  428. MOVE R0 R3 ; Put number in R0
  429. ;; Restore Registers
  430. POPR R4 R15
  431. POPR R3 R15
  432. POPR R2 R15
  433. POPR R1 R15
  434. RET R15
  435. ;; Preserve_Other function
  436. ;; Sets Expression pointer to Text pointer value
  437. ;; For all unset nodes
  438. :Preserve_Other
  439. ;; Initialize
  440. COPY R0 R13 ; Start with HEAD
  441. ;; Process Node
  442. :Preserve_Other_0
  443. LOAD32 R2 R0 0 ; Load Node->Next
  444. LOAD32 R1 R0 4 ; Load Node type
  445. JUMP.NZ R1 @Preserve_Other_1 ; Don't do anything if Typed
  446. LOAD32 R1 R0 12 ; Load Expression pointer
  447. JUMP.NZ R1 @Preserve_Other_1 ; Don't do anything if Expression is set
  448. LOAD32 R1 R0 8 ; Load Text pointer
  449. STORE32 R1 R0 12 ; Set Expression pointer to Text pointer
  450. ;; Loop through nodes
  451. :Preserve_Other_1
  452. MOVE R0 R2 ; Prepare for next loop
  453. JUMP.NZ R0 @Preserve_Other_0
  454. RET R15
  455. ;; Print_Hex Function
  456. ;; Print all of the expressions
  457. ;; Starting with HEAD
  458. :Print_Hex
  459. ;; Prep TAPE_02
  460. LOADUI R0 0x1101
  461. FOPEN_WRITE
  462. ;; Initialize
  463. COPY R0 R13 ; Start with HEAD
  464. LOADUI R1 0x1101 ; Write to Tape_02
  465. :Print_Hex_0
  466. LOAD32 R2 R0 0 ; Load Node->Next
  467. LOAD32 R3 R0 4 ; Load Node type
  468. LOAD32 R0 R0 12 ; Load Expression pointer
  469. SUBI R3 R3 1 ; Check for Macros
  470. JUMP.Z R3 @Print_Hex_1 ; Don't print Macros
  471. CALLI R15 @Print_Line ; Print the Expression
  472. ;; Loop down the nodes
  473. :Print_Hex_1
  474. MOVE R0 R2 ; Prepare for next loop
  475. JUMP.NZ R0 @Print_Hex_0 ; Keep looping if not NULL
  476. ;; Done writing File
  477. LOADUI R0 0x1101 ; Close TAPE_01
  478. FCLOSE
  479. RET R15
  480. ;; Print_Line Function
  481. ;; Receives a pointer to a string in R0
  482. ;; And an interface in R1
  483. ;; Writes all Chars in string
  484. ;; Then writes a New line character to interface
  485. :Print_Line
  486. ;; Initialize
  487. MOVE R3 R0 ; Get Pointer safely out of the way
  488. :Print_Line_0
  489. LOADU8 R0 R3 0 ; Get our first byte
  490. CMPSKIPI.NE R0 0 ; If the loaded byte is NULL
  491. JUMP @Print_Line_Done ; Be done
  492. FPUTC ; Otherwise print
  493. ADDUI R3 R3 1 ; Increment for next loop
  494. JUMP @Print_Line_0 ; And Loop
  495. ;; Clean up
  496. :Print_Line_Done
  497. LOADUI R0 10 ; Put in Newline char
  498. FPUTC ; Write it out
  499. RET R15
  500. ;; Where we are putting the start of our stack
  501. :stack