1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
|
## Copyright (C) 2016 Jeremiah Orians
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with stage0. If not, see <http://www.gnu.org/licenses/>.
;; Node format:
;; PREV->pointer (register size)
;; Address (register size)
;; NULL terminated string (strln + 1)
# :start 0
:0
# ;; R1 is reserved for reading/writing bytes (don't touch)
# ;; We will be using R8 for our malloc pointer
# ;; We will be using R9 for our header size in bytes
# ;; We will be using R10 for our toggle
# ;; We will be using R11 for our PC counter
# ;; We will be using R12 for holding our nybble
# ;; We will be using R13 for our register size in bytes
# ;; We will be using R14 for our head-node
E0002D2F 0400 # LOADUI R15 $stack ; We will be using R15 for our stack
# ;; Main program functionality
# ;; Reads in Tape_01 and writes out results onto Tape_02
# ;; Accepts no arguments and HALTS when done
# :main 6
:2
# ;; Initialize header info
0D000010 # READSCID R0 ; Get process capabilities
E100B010000f # ANDI R1 R0 0xF ; We only care about size nybble
E0002D200001 # LOADUI R0 1 ; Assume we are 8bit
05032D01 # SL0 R13 R0 R1 ; Let size nybble correct answer
0900049D # COPY R9 R13 ; Prepare Header size
E0002D590001 # SL0I R9 1 ; Double to make proper size
# ;; Prep TAPE_01
E0002D201100 # LOADUI R0 0x1100
42100000 # FOPEN_READ
# ;; Intialize environment
E0002D211100 # LOADUI R1 0x1100 ; Read from tape_01
0D00002C # FALSE R12 ; Set holder to zero
0D00002B # FALSE R11 ; Set PC counter to zero
0D00002A # FALSE R10 ; Our toggle
E0002D280700 # LOADUI R8 0x700 ; Where we want our heap to start
# ;; Perform first pass
E0002D0F @3 # CALLI R15 @first_pass
# ;; We need to rewind tape_01 to perform our second pass
E0002D201100 # LOADUI R0 0x1100
42100003 # REWIND
# ;; Reintialize environment
0D00002C # FALSE R12 ; Set holder to zero
0D00002B # FALSE R11 ; Set PC counter to zero
0D00002A # FALSE R10 ; Our toggle
# ;; Prep TAPE_02
E0002D201101 # LOADUI R0 0x1101
42100001 # FOPEN_WRITE
E0002D0F @4 # CALLI R15 @second_pass
# ;; Close up as we are done
E0002D201100 # LOADUI R0 0x1100 ; Close TAPE_01
42100002 # FCLOSE
E0002D201101 # LOADUI R0 0x1101 ; Close TAPE_02
42100002 # FCLOSE
FFFFFFFF # HALT
# ;; First pass function
# ;; Reads Tape_01 and creates our label table
# ;; Will Overwrite R0 R10 R11
# ;; Returns to Main function when done
# :first_pass 8a
:3
42100100 # FGETC ; Read a Char
# ;; Check for EOF
E000A0100000 # CMPSKIPI.GE R0 0
0D01001F # RET R15
# ;; Check for and deal with label (:)
E000A030003a # CMPSKIPI.NE R0 58
3C00 @5 # JUMP @storeLabel
# ;; Check for and deal with pointers to labels
# ;; Starting with (@)
E000A0300040 # CMPSKIPI.NE R0 64
3C00 @6 # JUMP @ThrowAwayPointer
# ;; Then dealing with ($)
E000A0300024 # CMPSKIPI.NE R0 36
3C00 @6 # JUMP @ThrowAwayPointer
# ;; Now check for absolute addresses (&)
E000A0300026 # CMPSKIPI.NE R0 38
3C00 @7 # JUMP @ThrowAwayAddress
# ;; Otherwise attempt to process
E0002D0F @8 # CALLI R15 @hex ; Convert it
E0002CC0 @3 # JUMP.NP R0 @first_pass ; Don't record, nonhex values
# ;; Flip the toggle
090006AA # NOT R10 R10
E0002C9A @3 # JUMP.Z R10 @first_pass ; Jump if toggled
# ;; Deal with case of second half of byte
E1000FBB0001 # ADDUI R11 R11 1 ; increment PC now that that we have a full byte
3C00 @3 # JUMP @first_pass
# ;; Second pass function
# ;; Reads from Tape_01 and uses the values in the table
# ;; To write desired contents onto Tape_02
# ;; Will Overwrite R0 R10 R11
# ;; Returns to Main function when done
# :second_pass e0
:4
42100100 # FGETC ; Read a Char
# ;; Check for EOF
E000A0100000 # CMPSKIPI.GE R0 0
0D01001F # RET R15
# ;; Check for and deal with label
E000A030003a # CMPSKIPI.NE R0 58
3C00 @9 # JUMP @ThrowAwayLabel
# ;; Check for and deal with Pointers to labels
E000A0300040 # CMPSKIPI.NE R0 64 ; @ for relative
3C00 @a # JUMP @StoreRelativePointer
E000A0300024 # CMPSKIPI.NE R0 36 ; $ for absolute
3C00 @b # JUMP @StoreAbsolutePointer
E000A0300026 # CMPSKIPI.NE R0 38 ; & for address
3C00 @c # JUMP @StoreAbsoluteAddress
# ;; Process everything else
E0002D0F @8 # CALLI R15 @hex ; Attempt to Convert it
E000A0100000 # CMPSKIPI.GE R0 0 ; Don't record, nonhex values
3C00 @4 # JUMP @second_pass ; Move onto Next char
# ;; Determine if we got a full byte
090006AA # NOT R10 R10
E0002C9A @d # JUMP.Z R10 @second_pass_0 ; Jump if toggled
# ;; Deal with case of first half of byte
E100B0C0000f # ANDI R12 R0 0x0F ; Store our first nibble
3C00 @4 # JUMP @second_pass
# :second_pass_0 13a
:d
# ;; Deal with case of second half of byte
E0002D5C0004 # SL0I R12 4 ; Shift our first nybble
E100B000000f # ANDI R0 R0 0x0F ; Mask out top
0500000C # ADD R0 R0 R12 ; Combine nybbles
E0002D211101 # LOADUI R1 0x1101 ; Write the combined byte
42100200 # FPUTC ; To TAPE_02
E0002D211100 # LOADUI R1 0x1100 ; Read from tape_01
E1000FBB0001 # ADDUI R11 R11 1 ; increment PC now that that we have a full byte
3C00 @4 # JUMP @second_pass
# ;; Store Label function
# ;; Writes out the token and the current PC value
# ;; Its static variable for storing the next index to be used
# ;; Will overwrite R0
# ;; Returns to first pass when done
# :storeLabel 164
:5
09000408 # COPY R0 R8 ; get current malloc
05000889 # ADD R8 R8 R9 ; update malloc
# ;; Add node info
05048B0D # STOREX R11 R0 R13 ; Store the PC of the label
E10020E00000 # STORE R14 R0 0 ; Store the Previous Head
090005E0 # MOVE R14 R0 ; Update Head
# ;; Store the name of the Label
E0002D0F @e # CALLI R15 @writeout_token
# ;; And be done
3C00 @3 # JUMP @first_pass
# ;; StoreRelativepointer function
# ;; Deals with the special case of relative pointers
# ;; Stores string
# ;; Finds match in Table
# ;; Writes out the offset
# ;; Modifies R0 R11
# ;; Jumps back into Pass2
# :StoreRelativePointer 184
:a
# ;; Correct the PC to reflect the size of the pointer
E1000FBB0002 # ADDUI R11 R11 2 ; Exactly 2 bytes
E0002D0F @o # CALLI R15 @Match_string ; Find the Match
0500200B # SUB R0 R0 R11 ; Determine the difference
E0002D0F @p # CALLI R15 @ProcessImmediate ; Write out the value
3C00 @4 # JUMP @second_pass
# ;; StoreAbsolutepointer function
# ;; Deals with the special case of absolute pointers
# ;; Stores string
# ;; Finds match in Table
# ;; Writes out the absolute address of match
# ;; Modifies R0 R11
# ;; Jumps back into Pass2
# :StoreAbsolutePointer 1a4
:b
# ;; Correct the PC to reflect the size of the pointer
E1000FBB0002 # ADDUI R11 R11 2 ; Exactly 2 bytes
E0002D0F @o # CALLI R15 @Match_string ; Find the Match
E0002D0F @p # CALLI R15 @ProcessImmediate ; Write out the value
3C00 @4 # JUMP @second_pass
# ;; StoreAbsoluteAddress function
# ;; Deal with the special case of absolute Addresses
# ;; Stores string
# ;; Finds match in Table
# ;; Writes out the full absolute address [32 bit machine]
# ;; Modifies R0 R11
# ;; Jumpbacs back into Pass2
# :StoreAbsoluteAddress 1ba
:c
# ;; Correct the PC to reflect the size of the address
E1000FBB0004 # ADDUI R11 R11 4 ; 4 Bytes on 32bit machines
E0002D0F @o # CALLI R15 @Match_string ; Find the Match
E100B020ffff # ANDI R2 R0 0xFFFF ; Save bottom half for next function
E0002D400010 # SARI R0 16 ; Drop bottom 16 bits
E0002D0F @p # CALLI R15 @ProcessImmediate ; Write out top 2 bytes
09000502 # MOVE R0 R2 ; Use the saved 16bits
E0002D0F @p # CALLI R15 @ProcessImmediate ; Write out bottom 2 bytes
3C00 @4 # JUMP @second_pass
# ;; Writeout Token Function
# ;; Writes the Token [minus first char] to the address
# ;; given by malloc and updates malloc pointer
# ;; Returns starting address of string
# :writeout_token 1e6
:e
# ;; Preserve registers
0902001F # PUSHR R1 R15
0902002F # PUSHR R2 R15
# ;; Initialize
09000428 # COPY R2 R8 ; Get current malloc pointer
# ;; Our core loop
# :writeout_token_0 1f2
:r
42100100 # FGETC ; Get another byte
# ;; Deal with termination cases
E000A0300020 # CMPSKIPI.NE R0 32 ; Finished if space
3C00 @q # JUMP @writeout_token_done
E000A0300009 # CMPSKIPI.NE R0 9 ; Finished if tab
3C00 @q # JUMP @writeout_token_done
E000A030000a # CMPSKIPI.NE R0 10 ; Finished if newline
3C00 @q # JUMP @writeout_token_done
E000A030ffff # CMPSKIPI.NE R0 -1 ; Finished if EOF
3C00 @q # JUMP @writeout_token_done
# ;; Deal with valid input
E10021080000 # STORE8 R0 R8 0 ; Write out the byte
E1000F880001 # ADDUI R8 R8 1 ; Increment
3C00 @r # JUMP @writeout_token_0 ; Keep looping
# ;; Clean up now that we are done
# :writeout_token_done 22e
:q
# ;; Fix malloc
E1000F880001 # ADDUI R8 R8 1
# ;; Prepare for return
09000502 # MOVE R0 R2
# ;; Restore registers
0902802F # POPR R2 R15
0902801F # POPR R1 R15
# ;; And be done
0D01001F # RET R15
# ;; Match string function
# ;; Walks down list until match is found or returns -1
# ;; Reads a token
# ;; Then returns address of match in R0
# ;; Returns to whatever called it
# :Match_string 244
:o
# ;; Preserve registers
0902001F # PUSHR R1 R15
0902002F # PUSHR R2 R15
# ;; Initialize for Loop
E0002D0F @e # CALLI R15 @writeout_token ; Get our desired string
09000510 # MOVE R1 R0 ; Position our desired string
0900042E # COPY R2 R14 ; Begin at our head node
# ;; Loop until we find a match
# :Match_string_0 25a
:h
05000029 # ADD R0 R2 R9 ; Where the string is located
E0002D0F @f # CALLI R15 @strcmp
E0002C50 @g # JUMP.E R0 @Match_string_1 ; It is a match!
# ;; Prepare for next loop
E10013220000 # LOAD R2 R2 0 ; Move to next node
E0002CA2 @h # JUMP.NZ R2 @Match_string_0 ; Keep looping
0D000032 # TRUE R2 ; Set result to -1 if not found
# :Match_string_1 27a
:g
# ;; Store the correct answer
E000A022ffff # CMPSKIPI.E R2 -1 ; Otherwise get the value
0503802D # LOADX R0 R2 R13 ; Get the value we care about
# ;; Restore registers
0902802F # POPR R2 R15
0902801F # POPR R1 R15
0D01001F # RET R15
# ;; Our simple string compare function
# ;; Receives two pointers in R0 and R1
# ;; Returns the difference between the strings in R0
# ;; Returns to whatever called it
# :strcmp 290
:f
# ;; Preserve registers
0902001F # PUSHR R1 R15
0902002F # PUSHR R2 R15
0902003F # PUSHR R3 R15
0902004F # PUSHR R4 R15
# ;; Setup registers
09000520 # MOVE R2 R0
09000531 # MOVE R3 R1
E0002D240000 # LOADUI R4 0
# :cmpbyte 2ae
:i
0503A024 # LOADXU8 R0 R2 R4 ; Get a byte of our first string
0503A134 # LOADXU8 R1 R3 R4 ; Get a byte of our second string
E1000F440001 # ADDUI R4 R4 1 ; Prep for next loop
05004101 # CMP R1 R0 R1 ; Compare the bytes
E000A0200000 # CMPSKIPI.E R0 0 ; Stop if byte is NULL
E0002C51 @i # JUMP.E R1 @cmpbyte ; Loop if bytes are equal
# ;; Done
09000501 # MOVE R0 R1 ; Prepare for return
# ;; Restore registers
0902804F # POPR R4 R15
0902803F # POPR R3 R15
0902802F # POPR R2 R15
0902801F # POPR R1 R15
0D01001F # RET R15
# ;; Processimmediate Function
# ;; Receives an integer value in R0
# ;; Writes out the values to Tape_02
# ;; Doesn't modify registers
# ;; Returns to whatever called it
# :ProcessImmediate 2e4
:p
# ;; Preserve registers
0902000F # PUSHR R0 R15
0902001F # PUSHR R1 R15
0902002F # PUSHR R2 R15
# ;; Break up Immediate
E100B02000ff # ANDI R2 R0 0xFF ; Put lower byte in R2
E0002D400008 # SARI R0 8 ; Drop Bottom byte from R0
E100B00000ff # ANDI R0 R0 0xFF ; Maskout everything outside of top byte
# ;; Write out Top Byte
E0002D211101 # LOADUI R1 0x1101 ; Write the byte
42100200 # FPUTC ; To TAPE_02
# ;; Write out bottom Byte
09000502 # MOVE R0 R2 ; Put Lower byte in R0
42100200 # FPUTC ; To TAPE_02
# ;; Restore registers
0902802F # POPR R2 R15
0902801F # POPR R1 R15
0902800F # POPR R0 R15
# ;; Be Done
0D01001F # RET R15
# ;; ThrowAwaypointer function
# ;; Handle the special case of a generic problem
# ;; for Pass1, Will update R11 and modify R0
# ;; Will return to the start of first_pass
# ;; Never call this function, only jump to it
# :ThrowAwayPointer 324
:6
E1000FBB0002 # ADDUI R11 R11 2 ; Pointers always take up 2 bytes
E0002D0F @j # CALLI R15 @throwAwayToken ; Get rid of rest of token
3C00 @3 # JUMP @first_pass ; Then return to the proper place
# ;; ThrowAwayAddress function
# ;; Handle the case of a 32bit absolute address storage
# ;; for Pass1, Will update R11 and modify R0
# ;; Will return to the start of first_pass
# ;; Never call this function, conly jump to it
# :ThrowAwayAddress 334
:7
E1000FBB0004 # ADDUI R11 R11 4 ; Addresses on 32bit systems take up 4 bytes
E0002D0F @j # CALLI R15 @throwAwayToken ; Get rid of rest of token
3C00 @3 # JUMP @first_pass ; Then return to the proper place
# ;; ThrowAwaylabel function
# ;; Handle the special case of a generic problem
# ;; for Pass2, Will update R11 and modify R0
# ;; Will return to the start of second_pass
# ;; Never call this function, only jump to it
# :ThrowAwayLabel 344
:9
E0002D0F @j # CALLI R15 @throwAwayToken ; Get rid of rest of token
3C00 @4 # JUMP @second_pass
# ;; Throw away token function
# ;; Deals with the general case of not wanting
# ;; The rest of the characters in a token
# ;; This Will alter the values of R0 R1
# ;; Returns back to whatever called it
# :throwAwayToken 34e
:j
42100100 # FGETC ; Read a Char
# ;; Stop looping if space
E000A0300020 # CMPSKIPI.NE R0 32
0D01001F # RET R15
# ;; Stop looping if tab
E000A0300009 # CMPSKIPI.NE R0 9
0D01001F # RET R15
# ;; Stop looping if newline
E000A030000a # CMPSKIPI.NE R0 10
0D01001F # RET R15
# ;; Stop looping if EOF
E000A030ffff # CMPSKIPI.NE R0 -1
0D01001F # RET R15
# ;; Otherwise keep looping
3C00 @j # JUMP @throwAwayToken
# ;; Hex function
# ;; This function is serving three purposes:
# ;; Identifying hex characters
# ;; Purging line comments
# ;; Returning the converted value of a hex character
# ;; This function will alter the values of R0
# ;; Returns back to whatever called it
# :hex 37e
:8
# ;; Deal with line comments starting with #
E000A0300023 # CMPSKIPI.NE R0 35
3C00 @k # JUMP @ascii_comment
# ;; Deal with line comments starting with ;
E000A030003b # CMPSKIPI.NE R0 59
3C00 @k # JUMP @ascii_comment
# ;; Deal with all ascii less than '0'
E000A0100030 # CMPSKIPI.GE R0 48
3C00 @l # JUMP @ascii_other
# ;; Deal with '0'-'9'
E000A0000039 # CMPSKIPI.G R0 57
3C00 @m # JUMP @ascii_num
# ;; Deal with all ascii less than 'A'
E000A0100041 # CMPSKIPI.GE R0 65
3C00 @l # JUMP @ascii_other
# ;; Unset high bit to set everything into uppercase
E100B00000df # ANDI R0 R0 0xDF
# ;; Deal with 'A'-'F'
E000A0000046 # CMPSKIPI.G R0 70
3C00 @n # JUMP @ascii_high
# ;; Ignore the rest
3C00 @l # JUMP @ascii_other
# :ascii_num 3c4
:m
E10011000030 # SUBUI R0 R0 48
0D01001F # RET R15
# :ascii_high 3ce
:n
E10011000037 # SUBUI R0 R0 55
0D01001F # RET R15
# :ascii_comment 3d8
:k
42100100 # FGETC ; Read another char
E0002CC0 @l # JUMP.NP R0 @ascii_other ; Stop with EOF
E000A020000a # CMPSKIPI.E R0 10 ; Stop at the end of line
3C00 @k # JUMP @ascii_comment ; Otherwise keep looping
# :ascii_other 3ec
:l
0D000030 # TRUE R0
0D01001F # RET R15
# ;; Where we will putting our stack
# :stack 3f4
:1
|