-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdisassembly.txt
441 lines (415 loc) · 9.2 KB
/
disassembly.txt
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
; This file is an annotated disassembly of interpreter.trg.
; (Actually, that file was created from this one).
; It interprets Qdeql code.
;;;;; SYNTAX NOTES ;;;;;
; Any text after an unquoted semicolon is a comment.
; Instructions are indented and in all-caps.
; Decimal literals are prefixed with "#"; hex literals are prefixed with "0x";
; and ASCII character literals are wrapped in single quotes.
; Any snake_case text identifies a label. Labels identifying a section of code are unindented and followed by a colon,
; while labels used as arguments to instructions do not have the colon.
; Multiple labels may identify the same instruction.
; When comments mention stack layout, it will be in a box diagram like this:
; +===+---+---+
; | a | b | c |
; +===+---+---+
; The left side of the diagram is the bottom of the stack, and the right side is the top of the stack.
; The === indicate that 'a' is an "array" of potentially multiple values filling multiple stack slots,
; while the --- indicate that 'b' and 'c' are each only a single number.
;;;;; SETUP ;;;;;
; Split into a "code thread" and a "memory thread"
; The code thread will handle I/O and most logic, while the memory thread holds the queue.
TSP memory_thread_setup
JMP read_program
; Push a 0 to the memory thread to indicate initial queue size
memory_thread_setup:
PSI #0
JMP memory_thread
read_program:
; set program counter to -1
PSI #0
DEC ; or NOT
read_loop:
GTC
DEC ; allow NUL to separate program from input
BNG end_program
; & 2 -1
; * 0 3
; - 0 1 -1
; / 0 5
; = 0 -1
; \ 0 4 -1 -1 -1
; anything else is ignored
PSC '%' ; '&' - 1
SUB
BNG non_program_character
DEC
BNG read_ampersand
PSI #3 ; '*' - '&' - 1
SUB
BNG non_program_character
DEC
BNG read_star
DEC ; '-' - '*' - 1 == 2
DEC ; DEC twice is shorter than PSI #2; SUB
BNG non_program_character
DEC
BNG read_minus
DEC ; '/' - '-' - 1 == 1
BNG non_program_character
DEC
BNG read_fwd_slash
; '=' - '/' - 1 == 13, which is \r. This poses some problems for encoding it directly.
; Most control characters are fine, but Trilangle ignores newlines, and \r's behavior is OS-dependent.
; This is one of several ways to achieve it.
PSC 'M' ; 0b0100'1101
PSC '?' ; & 0b0011'1111
AND ; = 0b0000'1101 = 13
SUB ; Now, TOS == input - '=' + 1
BNG non_program_character
DEC
BNG read_equals
PSC 0x1E ; '\' - '=' - 1
SUB
BNG non_program_character
DEC
BNG read_backslash
JMP non_program_character
end_program:
POP ; remove EOF character
PSI #0 ; set the accumulator to 0 (see code thread layout below)
JMP code_thread
non_program_character:
POP
JMP read_loop
read_ampersand:
POP
PSI #2
SWP
INC
PSI #0
DEC ; or NOT
SWP
INC
JMP read_loop
read_star:
; TOS should be -1
INC ; or NOT
SWP
INC
PSI #3
SWP
INC
JMP read_loop
read_minus:
; TOS should be -1
INC ; or NOT
SWP
; for 3+ uops, it's faster to add to length once at the end than to INC repeatedly
PSI #1
SWP
PSI #0
DEC ; or NOT
SWP
PSI #3
ADD
JMP read_loop
read_fwd_slash:
; TOS should be -1
INC ; or NOT
SWP
INC
PSI #5
SWP
INC
JMP read_loop
read_equals:
; TOS should be -1
INC ; or NOT
SWP
INC
PSI #0
DEC ; or NOT
SWP
INC
JMP read_loop
read_backslash:
; TOS should be -1
INC ; or NOT
SWP
PSI #4
SWP
PSI #0
DEC ; or NOT
SWP
; DP2-POP-SWP duplicates the item immediately below TOS while preserving the value above it.
; Since negative numbers cannot be pushed directly, this is an efficient way to append multiple -1's.
DP2
POP
SWP
DP2
POP
SWP
; Finally, increase the starting PC by the number of uops.
PSI #5
ADD
JMP read_loop
;;;;; INTER-THREAD SIGNALS ;;;;;
; Send an 'enqueue' signal
enqueue_signal_send:
PSI #2
JMP memory_thread_receiver
; Send a 'dequeue' signal
dequeue_signal_send:
PSI #1
JMP memory_thread_receiver
; Create a response to a 'dequeue' signal
dequeue_response_send:
PSI #1
JMP code_thread_deq_receive
; Create a response to an 'enqueue' signal
enqueue_ack_send:
PSI #0
JMP code_thread_enq_receive
;;;;; CODE THREAD ;;;;;
code_thread:
; The layout of the code thread looks like this:
; +======+----+-----+
; | uops | PC | acc |
; +======+----+-----+
; 'PC' is an index into the uops array. As such, it counts in the opposite direction of a traditional IP.
; When the PC is -1, there are no instructions left, and execution terminates.
; 'acc' is the most recently dequeued value, or 0 if no value is available.
; Always having something there even if no value is available means the PC is always at the same stack depth.
; Fetch the next uop
ct_fetch_loop:
DP2
POP
BNG end ; if PC is negative, the end of the program has been reached
INC
INC
IDX
JMP ct_decode_uop
end:
EXT
; There are seven uops:
; - enq (-1): Send an 'enqueue' instruction to the memory thread, and clear the accumulator.
; - deq (0): Send a 'dequeue' instruction to the memory thread, and update the accumulator.
; - sub (1): Decrement the accumulator mod 256.
; - get (2): Get a character from stdin, setting EOF to 0 instead of -1, and store it in the accumulator.
; - put (3): Print a character to stdout, and clear the accumulator.
; - bgn (4): If the accumulator is 0, jump forwards (decrement PC) to the matching end.
; - end (5): Jump backwards (increment PC) to the matching bgn.
ct_decode_uop:
; +======+----+-----+-----+
; | uops | PC | acc | uop |
; +======+----+-----+-----+
DEC
BNG ct_create_signal ; enq or deq
DEC
BNG ct_decrement
DEC
BNG ct_getchar
DEC
BNG ct_putchar
DEC
BNG ct_begin
JMP ct_end
ct_decrement:
POP
DEC
PSC 0xFF ; or, failing that: PSI #8, EXP, DEC
AND
JMP ct_advance_pc
ct_getchar:
POP
POP
GTC
BNG ct_gtc_eof
JMP ct_advance_pc
ct_gtc_eof:
INC ; or NOT
JMP ct_advance_pc
ct_putchar:
POP
PTC
DUP
XOR
JMP ct_advance_pc
ct_begin:
POP
DEC
BNG ct_bgn_find_end
INC
JMP ct_advance_pc
ct_bgn_find_end:
INC ; or NOT
SWP
; Stack layout:
; +======+---------+----+
; | uops | nBegins | PC |
; +======+---------+----+
; The accumulator is always zero, so it can be discarded.
; nBegins is incremented at every bgn and decremented at every end. When it hits -1, exit the loop.
ct_bgn_find_end_loop:
DUP
IDX
; end is the largest uop, so it's easy to detect
PSI #5
SUB
BNG ct_bgn_not_end
POP
SWP
DEC
BNG ct_bgn_found_end
SWP
DEC ; Advance the PC
JMP ct_bgn_find_end_loop
ct_bgn_not_end:
INC
BNG ct_bgn_not_end_cleanup
POP
SWP
INC ; increment nBegins
SWP
DEC ; Advance the PC
JMP ct_bgn_find_end_loop
ct_bgn_not_end_cleanup:
POP ; Remove the uop from the stack
DEC ; Advance the PC
JMP ct_bgn_find_end_loop
ct_bgn_found_end:
; If PC is pointing at the end uop, the stack layout is this:
; +======+------+----+
; | uops | PC+1 | -1 |
; +======+------+----+
; We want the PC to be pointing past the end uop, and we need a 0 on the stack above it.
ADD ; PC+1 + -1 = PC
DEC
PSI #0
JMP ct_advance_pc
ct_end:
; Stack layout on entering this branch:
; +======+----+-----+---+
; | uops | PC | acc | 0 |
; +======+----+-----+---+
; We have some shuffling to do:
; - The accumulator is not needed for this process, but must be preserved
; - The PC must be easily accessible (top two values)
; - The other slot in the top two will be used to track how many levels of nesting there are
POP
SWP
PSI #0
SWP
INC
ct_end_find_begin_loop:
; New stack layout:
; +======+-----+-------+----+
; | uops | acc | nEnds | PC |
; +======+-----+-------+----+
; Because the PC is further from the uops array than it is during standard instruction fetch,
; we must add 3 before indexing.
DUP
PSI #3
ADD
IDX
PSI #5
SUB
BNG ct_end_not_end
; current uop is a nested end
POP
SWP
INC ; increment nEnds
SWP
INC ; move PC backwards
JMP ct_end_find_begin_loop
ct_end_not_end:
; TOS is uop-5 and uop is not end
INC
BNG ct_end_not_end_cleanup
POP
; uop is bgn, but may be nested
SWP
DEC
BNG ct_end_found_begin
SWP
INC ; move PC
JMP ct_end_find_begin_loop
ct_end_not_end_cleanup:
POP
INC
JMP ct_end_find_begin_loop
ct_end_found_begin:
POP
; PC is pointing at bgn, so counteract the advancement
INC
SWP
JMP ct_advance_pc
ct_create_signal:
INC
BNG ct_create_enq
; create deq
TSP dequeue_signal_send
; discard old acc
SWP
POP
; convert deq 0 into -1 to indicate to keep everything
DEC
code_thread_deq_receive:
TJN
JMP ct_advance_pc
ct_create_enq:
TSP enqueue_signal_send
; use the enq -1 to also indicate to keep everything
code_thread_enq_receive:
TJN
POP ; discard old acc
PSI #0
JMP ct_advance_pc
ct_advance_pc:
SWP
DEC
SWP
JMP ct_fetch_loop
;;;;; MEMORY THREAD ;;;;;
memory_thread:
; The layout of the memory thread looks like this:
; +=======+--------+
; | queue | length |
; +=======+--------+
; This thread will receive a series of "enqueue" and "dequeue" commands from the code thread.
; An enqueue command will be of the form:
; +-------+----+
; | value | -1 |
; +-------+----+
; A dequeue command will be a single 0.
; Use the length to determine how much to keep
DUP
INC
memory_thread_receiver:
TJN
BNG mt_enqueue
; If it was positive, this is a dequeue
POP
DUP
DEC
BNG mt_create_zero
INC
IDX
JMP mt_deq_response
mt_create_zero:
INC ; or NOT
SWP
INC
SWP
mt_deq_response:
TSP dequeue_response_send
POP
DEC
JMP memory_thread
mt_enqueue:
POP ; remove the '-1' that indicates enqueue
SWP ; put the value into the queue
INC ; increment the length
TSP enqueue_ack_send
JMP memory_thread