Browse code

Progress on in-Uxn assembler

Andrew Alderwick authored on 11/04/2021 08:40:26
Showing 3 changed files
... ...
@@ -74,6 +74,14 @@ dump = function(f, root, dag, level)
74 74
     return dump(f, dag[root][2], dag, level + 1)
75 75
   end
76 76
 end
77
+local convert = setmetatable({
78
+  ['.'] = 'dot',
79
+  ['\0'] = 'nul'
80
+}, {
81
+  __index = function(self, k)
82
+    return k
83
+  end
84
+})
77 85
 local write_opcode_tree
78 86
 do
79 87
   local byte_to_opcode = { }
... ...
@@ -105,12 +113,12 @@ do
105 113
   table.sort(order_to_opcode)
106 114
   local root, opcode_to_links = build_dag(order_to_opcode)
107 115
   write_opcode_tree = function(f)
116
+    f:write(('\t$tree   .$op-%s ( opcode tree )\n'):format(root:lower()))
117
+    f:write('\t$start\n')
108 118
     for i = 0, #byte_to_opcode do
109 119
       local opcode = byte_to_opcode[i]
110 120
       f:write('\t')
111
-      if opcode == root then
112
-        f:write('$root   ')
113
-      elseif opcode ~= '---' then
121
+      if opcode ~= '---' then
114 122
         f:write(('$op-%s '):format(opcode:lower()))
115 123
       else
116 124
         f:write('        ')
... ...
@@ -184,19 +192,49 @@ end
184 192
 do
185 193
   local root, dag = build_dag_from_chars('{}[]%@$;|=~,.^#"\0', '(', ')')
186 194
   check_terminals(dag, ')')
187
-  local convert = {
188
-    ['.'] = 'dot',
189
-    ['\0'] = 'nul'
190
-  }
191 195
   local label_name
192 196
   label_name = function(s)
193
-    return ('first-char-%-3s'):format(convert[s] or s)
197
+    return ('normal-%-3s'):format(convert[s])
198
+  end
199
+  local label_value
200
+  label_value = function(k)
201
+    return ('[ %02x ]'):format(k:byte())
202
+  end
203
+  add_globals(root, dag, label_name, label_value, '', '   ')
204
+end
205
+do
206
+  local root, dag = build_dag_from_chars('{}', '\0', '(')
207
+  dump(io.stdout, root, dag)
208
+  local label_name
209
+  label_name = function(s)
210
+    if s == '(' then
211
+      return 'normal-(  '
212
+    end
213
+    return ('variable-%s'):format(convert[s])
214
+  end
215
+  local label_value
216
+  label_value = function(k)
217
+    return ('[ %02x ]'):format(k:byte())
218
+  end
219
+  dag['('] = nil
220
+  add_globals(root, dag, label_name, label_value, '', '   ')
221
+end
222
+do
223
+  local root, dag = build_dag_from_chars('{}\0', '(')
224
+  dump(io.stdout, root, dag)
225
+  local label_name
226
+  label_name = function(s)
227
+    if s == '(' then
228
+      return 'normal-(  '
229
+    end
230
+    return ('macro-%-3s'):format(convert[s])
194 231
   end
195 232
   local label_value
196 233
   label_value = function(k)
197 234
     return ('[ %02x ]'):format(k:byte())
198 235
   end
199
-  add_globals(root, dag, label_name, label_value, '  ', '     ')
236
+  dag['('] = nil
237
+  add_globals(root, dag, label_name, label_value, '', '   ')
200 238
 end
201 239
 local devices = { }
202 240
 local add_device
... ...
@@ -252,7 +290,7 @@ local f = assert(io.open(('%s.tmp'):format(filename), 'w'))
252 290
 local state = 'normal'
253 291
 local machine = {
254 292
   normal = function(l)
255
-    if l:match('%$disasm .*%$asm') then
293
+    if l:match('%( opcode tree %)') then
256 294
       write_opcode_tree(f)
257 295
       state = 'opcode'
258 296
     elseif l:match('^%@') then
... ...
@@ -277,7 +315,7 @@ local machine = {
277 315
     end
278 316
   end,
279 317
   opcode = function(l)
280
-    if not l:match('%[') then
318
+    if not l:match('.') then
281 319
       f:write(l)
282 320
       f:write('\n')
283 321
       state = 'normal'
... ...
@@ -35,6 +35,8 @@ dump = (f, root, dag, level = 0) ->
35 35
 	if dag[root][2]
36 36
 		dump f, dag[root][2], dag, level + 1
37 37
 
38
+convert = setmetatable { ['.']: 'dot', ['\0']: 'nul' },
39
+	__index: (k) => k
38 40
 -- deal with opcodes
39 41
 
40 42
 write_opcode_tree = do
... ...
@@ -53,12 +55,12 @@ write_opcode_tree = do
53 55
 	table.sort order_to_opcode
54 56
 	root, opcode_to_links = build_dag order_to_opcode
55 57
 	(f) ->
58
+		f\write '\t$tree   .$op-%s ( opcode tree )\n'\format root\lower!
59
+		f\write '\t$start\n'
56 60
 		for i = 0, #byte_to_opcode
57 61
 			opcode = byte_to_opcode[i]
58 62
 			f\write '\t'
59
-			if opcode == root
60
-				f\write '$root   '
61
-			elseif opcode != '---'
63
+			if opcode != '---'
62 64
 				f\write '$op-%s '\format opcode\lower!
63 65
 			else
64 66
 				f\write '        '
... ...
@@ -111,14 +113,31 @@ add_globals = (root, dag, key_to_label, key_to_contents, pad_before = '', pad_af
111 113
 do
112 114
 	root, dag = build_dag_from_chars '{}[]%@$;|=~,.^#"\0', '(', ')'
113 115
 	check_terminals dag, ')'
114
-	convert = {
115
-		['.']: 'dot'
116
-		['\0']: 'nul'
117
-	}
118
-	label_name = (s) -> 'first-char-%-3s'\format convert[s] or s
116
+	label_name = (s) -> 'normal-%-3s'\format convert[s]
117
+	label_value = (k) -> '[ %02x ]'\format k\byte!
118
+	add_globals root, dag, label_name, label_value, '', '   '
119
+
120
+do
121
+	root, dag = build_dag_from_chars '{}', '\0', '('
122
+	dump io.stdout, root, dag
123
+	label_name = (s) ->
124
+		if s == '('
125
+			return 'normal-(  '
126
+		'variable-%s'\format convert[s]
127
+	label_value = (k) -> '[ %02x ]'\format k\byte!
128
+	dag['('] = nil
129
+	add_globals root, dag, label_name, label_value, '', '   '
130
+
131
+do
132
+	root, dag = build_dag_from_chars '{}\0', '('
133
+	dump io.stdout, root, dag
134
+	label_name = (s) ->
135
+		if s == '('
136
+			return 'normal-(  '
137
+		'macro-%-3s'\format convert[s]
119 138
 	label_value = (k) -> '[ %02x ]'\format k\byte!
120
-	add_globals root, dag, label_name, label_value, '  ', '     '
139
+	dag['('] = nil
140
+	add_globals root, dag, label_name, label_value, '', '   '
121 141
 
122 142
 devices = {}
123 143
 
... ...
@@ -147,7 +166,7 @@ f = assert io.open '%s.tmp'\format(filename), 'w'
147 166
 state = 'normal'
148 167
 machine =
149 168
 	normal: (l) ->
150
-		if l\match '%$disasm .*%$asm'
169
+		if l\match '%( opcode tree %)'
151 170
 			write_opcode_tree f
152 171
 			state = 'opcode'
153 172
 		elseif l\match '^%@'
... ...
@@ -166,7 +185,7 @@ machine =
166 185
 			f\write l
167 186
 			f\write '\n'
168 187
 	opcode: (l) ->
169
-		if not l\match '%['
188
+		if not l\match '.'
170 189
 			f\write l
171 190
 			f\write '\n'
172 191
 			state = 'normal'
... ...
@@ -1,54 +1,41 @@
1 1
 ;tree { search-key 2 max-key-len 1 }
2
-;assembler { pass 1 state 1 token 2 scope-len 1 scope 80 }
2
+;assembler { pass 1 state 1 token 2 scope-len 1 scope 80 heap 2 addr 2 subtree 2 }
3 3
 
4 4
 %HCF { #0000 DIV }
5
+%SHORT_FLAG { #20 }
5 6
 
6 7
 ( devices )
7 8
 
8
-|0100 ;Console { pad 8 char 1 byte 1 short 2 string 2 }
9
-|0110 ;Screen { width 2 height 2 pad 4 x 2 y 2 color 1 }
10
-|0120 ;Sprite { pad 8 x 2 y 2 addr 2 color 1 }
11
-|0130 ;Controller { p1 1 }
12
-|0140 ;Keys { key 1 }
13
-|0150 ;Mouse { x 2 y 2 state 1 chord 1 }
14
-|0160 ;File { pad 8 name 2 length 2 load 2 save 2 }
15
-|01F0 ;System { pad 8 r 2 g 2 b 2 }
9
+|0100 ;System { vector 2 pad 6 r 2 g 2 b 2 }
10
+|0110 ;Console { vector 2 pad 6 char 1 byte 1 short 2 string 2 }
11
+|0120 ;Screen { vector 2 width 2 height 2 pad 2 x 2 y 2 addr 2 color 1 }
12
+|0130 ;Audio { wave 2 envelope 2 pad 4 volume 1 pitch 1 play 1 value 2 delay 2 finish 1 }
13
+|0140 ;Controller { vector 2 button 1 key 1 }
14
+|0160 ;Mouse { vector 2 x 2 y 2 state 1 chord 1 }
15
+|0170 ;File { vector 2 pad 6 name 2 length 2 load 3 save 2 }
16
+|01a0 ;DateTime { year 2 month 1 day 1 hour 1 minute 1 second 1 dotw 1 doty 2 isdst 1 refresh 1 }
16 17
 
17 18
 ( vectors )
18 19
 
19 20
 |0200 ^RESET JMP
20
-|0204 BRK
21
-|0208 BRK
22 21
 
23 22
 @RESET
24
-	#b000 #c000 #0010 ,memcpy JSR2
25
-	HCF
26
-
27
-	,$token ,strlen JSR2
28
-	HCF
29
-
30
-	#00
31
-	$loop
32
-	DUP ,highest-bit JSR2
33
-	( )
34
-	POP
35
-	#01 ADD
36
-	DUP ^$loop JNZ
37
-	POP
38
-
23
+	,assembler-heap-start =assembler.heap
24
+	#0070 =assembler.addr
39 25
 
40 26
 	,$token ^assemble-token JSR
41 27
 	,$token2 ^assemble-token JSR
42 28
 	,$token3 ^assemble-token JSR
29
+	,$token4 ^assemble-token JSR
30
+	,$token5 ^assemble-token JSR
43 31
 	~assembler.state
44 32
 	HCF
45 33
 
46
-	$token [ hello 00 ]
47
-	$token2 [ 00 ]
48
-	$token3 [ 00 ]
49
-
50
-@assemble-tokens ( string-ptr* -- )
51
-	DUP2 ^assemble-token JSR
34
+	$token [ 25 xyllo 00 ]
35
+	$token2 [ 7b 00 ]
36
+	$token3 [ there 00 ]
37
+	$token4 [ 00 ]
38
+	$token5 [ 7d 00 ]
52 39
 
53 40
 @assemble-token ( string-ptr* -- )
54 41
 	( get location of tree )
... ...
@@ -179,12 +166,29 @@
179 166
 	STH POP2 POP2 STHr POP2r
180 167
 	JMP2r
181 168
 
182
-@memcpy ( src-ptr* dest-ptr* length* -- )
169
+@highest-bit ( n -- 00 if n is 00
170
+                 OR 01 if n is 01
171
+                 OR 02 if n is 02..03
172
+                 OR 03 if n is 04..07
173
+                 OR 04 if n is 08..0f
174
+                 ..
175
+                 OR 08 if n is 80..ff )
176
+	DUP #00 NEQ JMP JMP2r
177
+	DUP #01 SFT ORA
178
+	DUP #02 SFT ORA
179
+	DUP #04 SFT ORA
180
+	#1d MUL #05 SFT #00 SWP ,$lookup ADD2 PEK2
181
+	JMP2r
182
+
183
+	$lookup
184
+	[ 01 06 02 07 05 04 03 08 ]
185
+
186
+@memcpy ( src-ptr* dest-ptr* length* -- after-dest-ptr* )
183 187
 	SWP2 STH2
184 188
 
185 189
 	$loop
186 190
 	DUP2 ORA ^$keep-going JNZ
187
-	POP2 POP2 POP2r
191
+	POP2 POP2 STH2r
188 192
 	JMP2r
189 193
 
190 194
 	$keep-going
... ...
@@ -194,6 +198,9 @@
194 198
 	LIT2r [ 0001 ] ADD2r
195 199
 	^$loop JMP
196 200
 
201
+@strcpy ( src-ptr* dest-ptr* -- after-dest-ptr* )
202
+	OVR2 ^strlen JSR #0001 ADD2 ^memcpy JMP
203
+
197 204
 @strlen ( string-ptr* -- length* )
198 205
 	DUP2 #0001 SUB2
199 206
 	$loop
... ...
@@ -202,29 +209,77 @@
202 209
 	SWP2 SUB2
203 210
 	JMP2r
204 211
 
212
+@append-heap ( string-ptr* -- after-string-ptr* )
213
+	~assembler.heap ,strcpy JSR2
214
+	DUP2 =assembler.heap
215
+	JMP2r
205 216
 
217
+@append-tree ( string-ptr* incoming-ptr* -- binary-data* )
218
+	~assembler.heap SWP2 STR2
219
+	,$zero-pointers ~assembler.heap #0004 ^memcpy JSR =assembler.heap
220
+	^append-heap JSR
221
+	JMP2r
206 222
 
223
+	$zero-pointers [ 0000 0000 ]
207 224
 
208 225
 @add-label ( string-ptr* label-flags -- )
209
-	( NYI )
210
-	POP POP2 JMP2r
226
+	ROT ROT
227
+	DUP2 ,label-tree SWP2 #ff ,traverse-tree JSR2
228
+	^$new-label JNZ
229
+
230
+	( label already exists, check the flags and addr value )
231
+	SWP2 POP2
232
+	DUP2 #0001 ADD2 LDR2 ~assembler.addr EQU2 ^$addr-okay JNZ
233
+	( FIXME address is different to previous run, or label defined twice )
234
+	$addr-okay
235
+	PEK2 EQU ^$type-okay JNZ
236
+	( FIXME node type is different to before )
237
+	$type-okay
238
+	JMP2r
211 239
 
212
-@highest-bit ( n -- 00 if n is 00
213
-                 OR 01 if n is 01
214
-                 OR 02 if n is 02..03
215
-                 OR 03 if n is 04..07
216
-                 OR 04 if n is 08..0f
217
-                 ..
218
-                 OR 08 if n is 80..ff )
219
-	DUP #00 NEQ JMP JMP2r
220
-	DUP #01 SFT ORA
221
-	DUP #02 SFT ORA
222
-	DUP #04 SFT ORA
223
-	#1d MUL #05 SFT #00 SWP ,$lookup ADD2 PEK2
240
+	$new-label
241
+	^append-tree JSR
242
+	(
243
+	~assembler.heap SWP2 STR2
244
+	,$zero-pointers ~assembler.heap #0004 ^memcpy JSR =assembler.heap
245
+	~assembler.heap ,strcpy JSR2
246
+	)
247
+	DUP2 STH2 POK2 STH2r
248
+	DUP2 #0001 ADD2 ~assembler.addr SWP2 STR2
249
+	#0003 ADD2 =assembler.heap
224 250
 	JMP2r
225 251
 
226
-	$lookup
227
-	[ 01 06 02 07 05 04 03 08 ]
252
+@lookup-label ( string-ptr* -- address* node-type if found
253
+                            OR false-address* 00 if not found )
254
+	( FIXME deal with dotted labels )
255
+	DUP2 ,label-tree SWP2 #ff ,traverse-tree JSR2
256
+	^$not-found JNZ
257
+
258
+	SWP2 POP2
259
+	DUP2 #0001 ADD2 LDR2 SWP2 PEK2
260
+	JMP2r
261
+
262
+	$not-found
263
+	POP2
264
+	( FIXME complain about missing label )
265
+	POP2
266
+	( false-address is out of reach for JMP )
267
+	~assembler.addr #8765 ADD2
268
+	#00
269
+	JMP2r
270
+
271
+@write-byte ( byte -- )
272
+	( FIXME ) =Console.byte
273
+	~assembler.addr #0001 ADD2 =assembler.addr
274
+	JMP2r
275
+
276
+@write-short ( short -- )
277
+	( FIXME ) =Console.short
278
+	~assembler.addr #0002 ADD2 =assembler.addr
279
+	JMP2r
280
+
281
+@label-tree .l-root
282
+@macro-tree [ 0000 ]
228 283
 
229 284
 @opcodes
230 285
 	(
... ...
@@ -266,7 +321,8 @@
266 321
 		by seven (the size of each node). By multiplying the byte value by seven
267 322
 		and adding to $disasm, we get the opcode name when disassembling too.
268 323
 	)
269
-	$tree   .$root
324
+	$tree   .$op-lth ( opcode tree )
325
+	$start
270 326
 	$op-brk .$op-add .$op-dup $disasm [ BRK ] $asm
271 327
 	$op-nop .$op-mul .$op-ovr         [ NOP ]
272 328
 	$op-lit [ 0000 ] [ 0000 ]         [ LIT ]
... ...
@@ -278,7 +334,7 @@
278 334
 	$op-equ .$op-brk .$op-jnz         [ EQU ]
279 335
 	$op-neq [ 0000 ] [ 0000 ]         [ NEQ ]
280 336
 	$op-gth [ 0000 ] [ 0000 ]         [ GTH ]
281
-	$root   .$op-equ .$op-pok         [ LTH ]
337
+	$op-lth .$op-equ .$op-pok         [ LTH ]
282 338
 	$op-gts .$op-gth .$op-jmp         [ GTS ]
283 339
 	$op-lts [ 0000 ] [ 0000 ]         [ LTS ]
284 340
 	        [ 0000 ] [ 0000 ]         [ ??? ]
... ...
@@ -302,23 +358,23 @@
302 358
 
303 359
 @state-machine-pointers
304 360
 ( normal mode 00 )
305
-.first-char-root .nyi
306
-( FIXME 01 )
307
-.nyi .nyi
308
-( FIXME 02 )
309
-.nyi .nyi
310
-( FIXME 04 )
311
-.nyi .nyi
312
-( FIXME 08 )
313
-.nyi .nyi
314
-( FIXME 10 )
315
-.nyi .nyi
361
+.normal-root   .nyi
362
+( macro definition 01 )
363
+.macro-root    .macro-main
364
+( macro definition, contents ignored 02 )
365
+.macro-root    .ignore
366
+( variable definition, expect field size 08 )
367
+.variable-nul  .variable-size
368
+( variable definition, expect field name 04 )
369
+.variable-root .variable-name
370
+( reserved for future use 10 )
371
+.normal-(      .ignore
316 372
 ( literal data 20 )
317
-[ 0000 ] .nyi
318
-( FIXME 40 )
319
-.nyi .nyi
373
+[ 0000 ]       .nyi
374
+( reserved for future use 40 )
375
+.normal-(      .ignore
320 376
 ( comment 80 )
321
-.first-char-) .ignore
377
+.normal-)      .ignore
322 378
 
323 379
 (
324 380
 	Next up, we have the tree of code corresponding to each token's
... ...
@@ -337,11 +393,11 @@
337 393
 	doesn't matter what other bits are set, a comment's a comment.
338 394
 )
339 395
 
340
-@first-char-(     [ 0000 ]      .first-char-)   [ 28 ]
396
+@normal-(   [ 0000 ]    .normal-)   [ 28 ]
341 397
 	~assembler.state #80 ORA =assembler.state
342 398
 JMP2r
343 399
 
344
-@first-char-)     [ 0000 ]        [ 0000 ]      [ 29 ]
400
+@normal-)   [ 0000 ]    [ 0000 ]    [ 29 ]
345 401
 	~assembler.state #7f AND =assembler.state
346 402
 JMP2r
347 403
 
... ...
@@ -349,11 +405,11 @@ JMP2r
349 405
 	Left and right square brackets start and end literal data sections.
350 406
 )
351 407
 
352
-@first-char-[   .first-char-@   .first-char-]   [ 5b ]
408
+@normal-[   .normal-@   .normal-]   [ 5b ]
353 409
 	~assembler.state #20 ORA =assembler.state
354 410
 JMP2r
355 411
 
356
-@first-char-]     [ 0000 ]        [ 0000 ]      [ 5d ]
412
+@normal-]   [ 0000 ]    [ 0000 ]    [ 5d ]
357 413
 	~assembler.state #df AND =assembler.state
358 414
 JMP2r
359 415
 
... ...
@@ -362,33 +418,229 @@ JMP2r
362 418
 	local labels that follow.
363 419
 )
364 420
 
365
-@first-char-@     [ 0000 ]        [ 0000 ]      [ 40 ]
366
-	~assembler.pass ^$scope JNZ
421
+@normal-@   [ 0000 ]    [ 0000 ]    [ 40 ]
422
+	~assembler.token
367 423
 	DUP2 #00 ,add-label JSR2
368 424
 
369 425
 	$scope
370
-	DUP2 ,strlen JSR2
371
-	DUP2 =assembler.scope-len POP
372
-	,assembler.scope SWP2 JMP2
373
-
374
-@first-char-root
375
-@first-char-=   .first-char-$   .first-char-^   [ 3d ]
376
-@first-char-"   .first-char-nul .first-char-#   [ 22 ]
377
-@first-char-#     [ 0000 ]        [ 0000 ]      [ 23 ]
378
-@first-char-$   .first-char-"   .first-char-,   [ 24 ]
379
-@first-char-%     [ 0000 ]      .first-char-(   [ 25 ]
380
-@first-char-,   .first-char-%   .first-char-dot [ 2c ]
381
-@first-char-dot   [ 0000 ]      .first-char-;   [ 2e ]
382
-@first-char-;     [ 0000 ]        [ 0000 ]      [ 3b ]
383
-@first-char-^   .first-char-[   .first-char-|   [ 5e ]
384
-@first-char-{     [ 0000 ]        [ 0000 ]      [ 7b ]
385
-@first-char-|   .first-char-{   .first-char-}   [ 7c ]
386
-@first-char-}     [ 0000 ]      .first-char-~   [ 7d ]
387
-@first-char-~     [ 0000 ]        [ 0000 ]      [ 7e ]
388
-
389
-@first-char-nul   [ 0000 ]        [ 0000 ]      [ 00 ]
426
+	,assembler.scope ,strcpy JSR2
427
+	DUP2 ,assembler.scope SUB2 =assembler.scope-len POP
428
+	#0001 SUB2 #2d SWP POK POP
429
+	JMP2r
430
+
431
+(
432
+	Dollar signs introduce local labels, which use the scope defined above.
433
+)
434
+
435
+@normal-$   .normal-"   .normal-,   [ 24 ]
436
+	~assembler.token
437
+	,assembler.scope ~assembler.scope-len ADD
438
+	,strcpy JSR2 POP2
439
+
440
+	,assembler.scope #00 ,add-label JMP2 ( tail call )
441
+
442
+(
443
+	Hash signs followed by two or four hex digits write a literal.
444
+)
445
+
446
+@normal-#   [ 0000 ]    [ 0000 ]    [ 23 ]
447
+	~assembler.token ,parse-hex-string JSR2
448
+	DUP ^$valid JNZ
449
+	( FIXME complain about invalid hex literal )
450
+	POP
451
+	JMP2r
452
+	
453
+	$valid
454
+	DUP #01 SUB SHORT_FLAG MUL ( short flag for opcode )
455
+	,opcodes-op-lit ,opcodes-start SUB2 #07 DIV
456
+	ADD ADD ,write-byte JSR2
457
+
458
+	#02 EQU ^$short JNZ
459
+	,write-byte JMP2 ( tail call )
460
+
461
+	$short
462
+	,write-short JMP2 ( tail call )
463
+
464
+(
465
+	A pipe moves the current address to the hex value given.
466
+)
467
+
468
+@normal-|   .normal-{   .normal-}   [ 7c ]
469
+	~assembler.token ,parse-hex-string JSR2
470
+	DUP #02 EQU ^$valid JNZ
471
+	#00 EQU JMP POP
472
+	( FIXME complain about invalid hex literal )
473
+	JMP2r
474
+
475
+	$valid
476
+	POP
477
+	DUP2 ~assembler.addr LTH2 ^$backwards JNZ
478
+	( FIXME add zeroes when writing )
479
+	=assembler.addr
480
+	JMP2r
481
+
482
+	$backwards
483
+	( FIXME complain about going backwards )
484
+	POP2
485
+	JMP2r
486
+
487
+(
488
+	Commas and dots write the label address - the comma precedes this
489
+	with a LIT2 opcode.
490
+)
491
+
492
+@normal-,   .normal-%   .normal-dot [ 2c ]
493
+	,opcodes-op-lit ,opcodes-start SUB2 #07 DIV SHORT_FLAG ADD ,write-byte JSR2 POP
494
+	^normal-dot-main JMP
495
+
496
+@normal-dot [ 0000 ]    .normal-;   [ 2e ]
497
+	$main
498
+	~assembler.token ,lookup-label JSR2
499
+	POP ( don't care about node type )
500
+	,write-short JMP2 ( tail call )
501
+
502
+(
503
+	Caret writes LIT, followed by the label address as an offset.
504
+)
505
+
506
+@normal-^   .normal-[   .normal-|   [ 5e ]
507
+	,opcodes-op-lit ,opcodes-start SUB2 #07 DIV ,write-byte JSR2 POP
508
+	~assembler.token ,lookup-label JSR2
509
+	POP ( don't care about node type )
510
+	~assembler.addr SUB2
511
+	DUP2 #ff79 GTH2 ^$okay JNZ
512
+	DUP2 #0080 LTH2 ^$okay JNZ
513
+
514
+	( FIXME complain about jump being too far )
515
+
516
+	$okay
517
+	,write-byte JSR2 POP
518
+	JMP2r
519
+
520
+(
521
+	Tilde and equals are the load and store helpers respectively.
522
+	If the target is in the zero page, use LDR/PEK or STR/POK opcodes,
523
+	otherwise use LDR2/PEK2 or STR2/POK2 opcodes.
524
+)
525
+@normal-~   [ 0000 ]    [ 0000 ]    [ 7e ]
526
+	LIT2r .opcodes-op-ldr LIT2r .opcodes-op-pek
527
+	^normal-=-main JMP
528
+
529
+@normal-root
530
+@normal-=   .normal-$   .normal-^   [ 3d ]
531
+	LIT2r .opcodes-op-str LIT2r .opcodes-op-pok
532
+	$main
533
+	~assembler.token ,lookup-label JSR2
534
+	DUP #01 AND ^$valid JNZ
535
+
536
+	( FIXME complain about helper not being usable )
537
+	POP2 JMP2r
538
+
539
+	$valid
540
+	#02 AND ^$two-byte JNZ
541
+	SWP2r
542
+	$two-byte
543
+	POP2r
544
+	LIT2r .opcodes-start SUB2r LITr [ 07 ] DIVr
545
+	OVR #00 EQU ^$byte-mode JNZ
546
+
547
+	,write-short SHORT_FLAG ^$end JMP
548
+
549
+	$byte-mode
550
+	SWP POP
551
+	,write-byte #00
552
+
553
+	$end
554
+	,opcodes-op-lit ,opcodes-start SUB2 #07 DIV ADD ADD ,write-byte JSR2
555
+	JSR2
556
+	STHr ,write-byte JSR2
557
+	POPr
558
+	JMP2r
559
+
560
+(
561
+	Semicolons introduce variables. The variable name is added to the label
562
+	tree as usual, but all of the subfields are collected into their own tree
563
+	pointed to in the variable name's binary data.
564
+)
565
+@normal-;   [ 0000 ]    [ 0000 ]    [ 3b ]
566
+	~assembler.token #80 ,add-label JSR2
567
+	~assembler.heap #0000 OVR2 STR2
568
+	DUP2 =assembler.subtree
569
+	#0002 ADD2 =assembler.heap
570
+
571
+	~assembler.state #0c ORA =assembler.state
572
+	JMP2r
573
+
574
+@variable-root
575
+@variable-{ .variable-nul .variable-} [ 7b ]
576
+	JMP2r
577
+
578
+@variable-nul [ 0000 ]    .normal-(   [ 00 ]
579
+	JMP2r
580
+
581
+@variable-} [ 0000 ]    [ 0000 ]    [ 7d ]
582
+	~assembler.state #f3 AND =assembler.state
583
+	JMP2r
584
+
585
+@variable-name
586
+@variable-size
587
+	,nyi JMP2r
588
+
589
+(
590
+	Percent signs introduce macros. The macro name is added to the macro tree,
591
+	and all the arguments are collected into a list that follows the label's
592
+	binary data.
593
+)
594
+@normal-%   [ 0000 ]    .normal-(   [ 25 ]
595
+	,macro-tree ~assembler.token #ff ,traverse-tree JSR2
596
+	^$new-macro JNZ
597
+
598
+	( macro already exists, we assume defined in a previous pass
599
+	  we totally ignore the contents )
600
+	POP2
601
+	~assembler.state #02 ORA =assembler.state
602
+	JMP2r
603
+
604
+	$new-macro
605
+	~assembler.token SWP2 ,append-tree JSR2
606
+	POP2
607
+	~assembler.state #01 ORA =assembler.state
608
+	JMP2r
609
+
610
+@macro-root
611
+@macro-{   .macro-nul .macro-}   [ 7b ]
612
+	JMP2r
613
+
614
+@macro-}   [ 0000 ]    [ 0000 ]    [ 7d ]
615
+	~assembler.heap DUP2 #f0 ROT ROT POK2
616
+	#0001 ADD2 =assembler.heap
617
+	~assembler.state #fc AND =assembler.state
618
+	JMP2r
619
+
620
+@macro-nul [ 0000 ]    .normal-(   [ 00 ]
621
+	JMP2r
622
+
623
+@macro-main
624
+	~assembler.token ,append-heap JSR2
625
+	POP2
626
+	JMP2r
627
+
628
+
629
+@normal-"   .normal-nul .normal-#   [ 22 ]
630
+	( FIXME NYI )
631
+	JMP2r
632
+
633
+@normal-{   [ 0000 ]    [ 0000 ]    [ 7b ]
634
+	( these are spurious, but ignore them anyway )
635
+	JMP2r
636
+
637
+@normal-}   [ 0000 ]    .normal-~   [ 7d ]
638
+	( these are spurious, but ignore them anyway )
639
+	JMP2r
640
+
641
+@normal-nul [ 0000 ]    [ 0000 ]    [ 00 ]
390 642
 @ignore
391
-JMP2r
643
+	JMP2r
392 644
 
393 645
 @nyi
394 646
 	,$string =Console.string
... ...
@@ -419,47 +671,70 @@ JMP2r
419 671
 	If there is a subtree, it is searched when the reference contains a dot.
420 672
 )
421 673
 
422
-@l-Console         [ 0000 ]          [ 0000 ]         [ Console 00 ]    [ 80 ] .Console .l-Console-root
423
-@l-Console-byte    [ 0000 ]          [ 0000 ]         [ byte 00 ]       [ 01 ] .Console.byte
674
+@l-Audio           [ 0000 ]          [ 0000 ]         [ Audio 00 ]      [ 80 ] .Audio .l-Audio-root
675
+@l-Audio-delay     [ 0000 ]          [ 0000 ]         [ delay 00 ]      [ 03 ] .Audio.delay
676
+@l-Audio-envelope .l-Audio-delay    .l-Audio-finish   [ envelope 00 ]   [ 03 ] .Audio.envelope
677
+@l-Audio-finish    [ 0000 ]          [ 0000 ]         [ finish 00 ]     [ 01 ] .Audio.finish
678
+@l-Audio-pitch    .l-Audio-envelope .l-Audio-value    [ pitch 00 ]      [ 01 ] .Audio.pitch
679
+@l-Audio-play      [ 0000 ]          [ 0000 ]         [ play 00 ]       [ 01 ] .Audio.play
680
+@l-Audio-root
681
+@l-Audio-value    .l-Audio-play     .l-Audio-volume   [ value 00 ]      [ 03 ] .Audio.value
682
+@l-Audio-volume    [ 0000 ]         .l-Audio-wave     [ volume 00 ]     [ 01 ] .Audio.volume
683
+@l-Audio-wave      [ 0000 ]          [ 0000 ]         [ wave 00 ]       [ 03 ] .Audio.wave
684
+@l-Console        .l-Audio          .l-Controller     [ Console 00 ]    [ 80 ] .Console .l-Console-root
685
+@l-Console-byte    [ 0000 ]         .l-Console-char   [ byte 00 ]       [ 01 ] .Console.byte
686
+@l-Console-char    [ 0000 ]          [ 0000 ]         [ char 00 ]       [ 01 ] .Console.char
424 687
 @l-Console-root
425
-@l-Console-char   .l-Console-byte   .l-Console-short  [ char 00 ]       [ 01 ] .Console.char
426
-@l-Console-short   [ 0000 ]         .l-Console-string [ short 00 ]      [ 03 ] .Console.short
427
-@l-Console-string  [ 0000 ]          [ 0000 ]         [ string 00 ]     [ 03 ] .Console.string
428
-@l-Controller     .l-Console        .l-File           [ Controller 00 ] [ 80 ] .Controller .l-Controller-root
688
+@l-Console-short  .l-Console-byte   .l-Console-string [ short 00 ]      [ 03 ] .Console.short
689
+@l-Console-string  [ 0000 ]         .l-Console-vector [ string 00 ]     [ 03 ] .Console.string
690
+@l-Console-vector  [ 0000 ]          [ 0000 ]         [ vector 00 ]     [ 03 ] .Console.vector
691
+@l-Controller      [ 0000 ]          [ 0000 ]         [ Controller 00 ] [ 80 ] .Controller .l-Controller-root
692
+@l-Controller-button  [ 0000 ]          [ 0000 ]         [ button 00 ]     [ 01 ] .Controller.button
693
+@l-Controller-key .l-Controller-button .l-Controller-vector [ key 00 ]        [ 01 ] .Controller.key
429 694
 @l-Controller-root
430
-@l-Controller-p1   [ 0000 ]          [ 0000 ]         [ p1 00 ]         [ 01 ] .Controller.p1
695
+@l-Controller-vector  [ 0000 ]          [ 0000 ]         [ vector 00 ]     [ 03 ] .Controller.vector
696
+@l-DateTime       .l-Console        .l-Mouse          [ DateTime 00 ]   [ 80 ] .DateTime .l-DateTime-root
697
+@l-DateTime-day    [ 0000 ]          [ 0000 ]         [ day 00 ]        [ 01 ] .DateTime.day
698
+@l-DateTime-dotw  .l-DateTime-day   .l-DateTime-doty  [ dotw 00 ]       [ 01 ] .DateTime.dotw
699
+@l-DateTime-doty   [ 0000 ]         .l-DateTime-hour  [ doty 00 ]       [ 03 ] .DateTime.doty
700
+@l-DateTime-hour   [ 0000 ]          [ 0000 ]         [ hour 00 ]       [ 01 ] .DateTime.hour
701
+@l-DateTime-isdst .l-DateTime-dotw  .l-DateTime-refresh [ isdst 00 ]      [ 01 ] .DateTime.isdst
702
+@l-DateTime-minute  [ 0000 ]         .l-DateTime-month [ minute 00 ]     [ 01 ] .DateTime.minute
703
+@l-DateTime-month  [ 0000 ]          [ 0000 ]         [ month 00 ]      [ 01 ] .DateTime.month
704
+@l-DateTime-refresh .l-DateTime-minute .l-DateTime-second [ refresh 00 ]    [ 01 ] .DateTime.refresh
705
+@l-DateTime-root
706
+@l-DateTime-second  [ 0000 ]         .l-DateTime-year  [ second 00 ]     [ 01 ] .DateTime.second
707
+@l-DateTime-year   [ 0000 ]          [ 0000 ]         [ year 00 ]       [ 03 ] .DateTime.year
431 708
 @l-File            [ 0000 ]          [ 0000 ]         [ File 00 ]       [ 80 ] .File .l-File-root
432
-@l-File-length     [ 0000 ]          [ 0000 ]         [ length 00 ]     [ 03 ] .File.length
709
+@l-File-length     [ 0000 ]         .l-File-load      [ length 00 ]     [ 03 ] .File.length
710
+@l-File-load       [ 0000 ]          [ 0000 ]         [ load 00 ]       [ 00 ] .File.load
711
+@l-File-name      .l-File-length    .l-File-save      [ name 00 ]       [ 03 ] .File.name
433 712
 @l-File-root
434
-@l-File-load      .l-File-length    .l-File-name      [ load 00 ]       [ 03 ] .File.load
435
-@l-File-name       [ 0000 ]         .l-File-save      [ name 00 ]       [ 03 ] .File.name
436
-@l-File-save       [ 0000 ]          [ 0000 ]         [ save 00 ]       [ 03 ] .File.save
437
-@l-root
438
-@l-Keys           .l-Controller     .l-Screen         [ Keys 00 ]       [ 80 ] .Keys .l-Keys-root
439
-@l-Keys-root
440
-@l-Keys-key        [ 0000 ]          [ 0000 ]         [ key 00 ]        [ 01 ] .Keys.key
441
-@l-Mouse           [ 0000 ]          [ 0000 ]         [ Mouse 00 ]      [ 80 ] .Mouse .l-Mouse-root
442
-@l-Mouse-chord     [ 0000 ]          [ 0000 ]         [ chord 00 ]      [ 01 ] .Mouse.chord
713
+@l-File-save       [ 0000 ]         .l-File-vector    [ save 00 ]       [ 03 ] .File.save
714
+@l-File-vector     [ 0000 ]          [ 0000 ]         [ vector 00 ]     [ 03 ] .File.vector
715
+@l-Mouse          .l-File           .l-Screen         [ Mouse 00 ]      [ 80 ] .Mouse .l-Mouse-root
716
+@l-Mouse-chord     [ 0000 ]         .l-Mouse-state    [ chord 00 ]      [ 01 ] .Mouse.chord
443 717
 @l-Mouse-root
444
-@l-Mouse-state    .l-Mouse-chord    .l-Mouse-x        [ state 00 ]      [ 01 ] .Mouse.state
718
+@l-Mouse-state     [ 0000 ]          [ 0000 ]         [ state 00 ]      [ 01 ] .Mouse.state
719
+@l-Mouse-vector   .l-Mouse-chord    .l-Mouse-x        [ vector 00 ]     [ 03 ] .Mouse.vector
445 720
 @l-Mouse-x         [ 0000 ]         .l-Mouse-y        [ x 00 ]          [ 03 ] .Mouse.x
446 721
 @l-Mouse-y         [ 0000 ]          [ 0000 ]         [ y 00 ]          [ 03 ] .Mouse.y
447
-@l-Screen         .l-Mouse          .l-Sprite         [ Screen 00 ]     [ 80 ] .Screen .l-Screen-root
448
-@l-Screen-color    [ 0000 ]         .l-Screen-height  [ color 00 ]      [ 01 ] .Screen.color
722
+@l-Screen          [ 0000 ]         .l-System         [ Screen 00 ]     [ 80 ] .Screen .l-Screen-root
723
+@l-Screen-addr     [ 0000 ]          [ 0000 ]         [ addr 00 ]       [ 03 ] .Screen.addr
724
+@l-Screen-color   .l-Screen-addr    .l-Screen-height  [ color 00 ]      [ 01 ] .Screen.color
449 725
 @l-Screen-height   [ 0000 ]          [ 0000 ]         [ height 00 ]     [ 03 ] .Screen.height
450 726
 @l-Screen-root
451
-@l-Screen-width   .l-Screen-color   .l-Screen-x       [ width 00 ]      [ 03 ] .Screen.width
452
-@l-Screen-x        [ 0000 ]         .l-Screen-y       [ x 00 ]          [ 03 ] .Screen.x
727
+@l-Screen-vector  .l-Screen-color   .l-Screen-x       [ vector 00 ]     [ 03 ] .Screen.vector
728
+@l-Screen-width    [ 0000 ]          [ 0000 ]         [ width 00 ]      [ 03 ] .Screen.width
729
+@l-Screen-x       .l-Screen-width   .l-Screen-y       [ x 00 ]          [ 03 ] .Screen.x
453 730
 @l-Screen-y        [ 0000 ]          [ 0000 ]         [ y 00 ]          [ 03 ] .Screen.y
454
-@l-Sprite          [ 0000 ]         .l-System         [ Sprite 00 ]     [ 80 ] .Sprite .l-Sprite-root
455
-@l-Sprite-addr     [ 0000 ]          [ 0000 ]         [ addr 00 ]       [ 03 ] .Sprite.addr
456
-@l-Sprite-root
457
-@l-Sprite-color   .l-Sprite-addr    .l-Sprite-x       [ color 00 ]      [ 01 ] .Sprite.color
458
-@l-Sprite-x        [ 0000 ]         .l-Sprite-y       [ x 00 ]          [ 03 ] .Sprite.x
459
-@l-Sprite-y        [ 0000 ]          [ 0000 ]         [ y 00 ]          [ 03 ] .Sprite.y
460 731
 @l-System          [ 0000 ]          [ 0000 ]         [ System 00 ]     [ 80 ] .System .l-System-root
461 732
 @l-System-b        [ 0000 ]          [ 0000 ]         [ b 00 ]          [ 03 ] .System.b
462
-@l-System-root
463 733
 @l-System-g       .l-System-b       .l-System-r       [ g 00 ]          [ 03 ] .System.g
464
-@l-System-r        [ 0000 ]          [ 0000 ]         [ r 00 ]          [ 03 ] .System.r
734
+@l-System-r        [ 0000 ]         .l-System-vector  [ r 00 ]          [ 03 ] .System.r
735
+@l-System-root
736
+@l-System-vector   [ 0000 ]          [ 0000 ]         [ vector 00 ]     [ 03 ] .System.vector
737
+@l-root
738
+
739
+@assembler-heap-start
465 740