~rabbits/uxn

uxn/projects/software/asma.tal -rw-r--r-- 23.2 KiB
b6c40129neauoire Cleanup on controller.tal 36 minutes ago
                                                                                
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
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
( devices )

|00 @System     [ &vector $2 &wst      $1 &rst    $1 &pad   $4 &r      $2 &g     $2 &b      $2 ]
|10 @Console    [ &pad    $8 &write    $1 ]
|a0 @File       [ &vector $2 &success  $2 &offset $2 &pad   $2 &name  $2 &length $2 &load $2 &save $2 ]

( vectors )

(
	Asma - an in-Uxn assembler

	This assembler aims to be binary compatible with the output from
	src/uxnasm.c, but unlike that assembler this one can be run inside Uxn
	itself!

	Asma is designed to be able to be copy-pasted inside another project, so
	all its routines are prefixed with "asma-" to prevent clashes with labels
	used in the incorporating project. The reset vector contains a couple of
	examples of asma's usage and can be discarded.
)

|0100 @reset
	(
		Set the log level for helping to debug stuff.
		Its value is the bitwise OR of all the following output types:
			#01 prints the number of lines in the source code,
			#04 dumps all defined labels at end, and
			#08 prints the heap usage.
	)
	#09 ;asma/log-level STA

	(
		Assemble the source code into an output ROM file.

		If all you want is to use asma.tal to assemble files, insert a BRK
		after this statement.
	)
	;&source-file ;&dest-file ;asma-assemble-file JSR2

	(
		If an error has occurred, BRK here, otherwise continue. (The error
		message will already have been printed to the Console in
		asma-assemble-file.)
	)
	;asma/error LDA2 #0000 EQU2 JMP BRK

	(
		Load the output ROM over the currently running program, almost as if
		we loaded the ROM with uxnemu directly!

		It's not a totally pristine environment, as File/load doesn't zero out
		memory beyond the end of the file. So if the assembled program assumes
		that all memory above it is zero, it may misbehave.

		Asma itself doesn't use the zero page, but this example code writes a
		DEO2 instruction to 0x00ff. In order to execute File/load and have the
		CPU continue at memory location 0x0100, we write the final DEO2
		instruction there and jump there as our final act.

		Just in case the assembled code is zero-length (which can occur when
		assembling an empty source file), we write a BRK to the reset vector so
		that will prevent an infinite loop.
	)
	;&dest-file .File/name DEO2
	#0000 .File/offset DEO2
	#ff00 .File/length DEO2
	#0100 .File/load
	LIT DEO2 #00ff STA
	LIT BRK #0100 STA
	#00ff JMP2

	&source-file
		"projects/examples/demos/piano.tal 00
	&dest-file
		"bin/asma-boot.rom 00

(
	Common macros for use later on.
)

%asma-IF-ERROR { ;asma/error LDA2 ORA }

(
	Asma's public interface.
	These routines are what are expected to be called from programs that bundle
	Asma into bigger projects.
)

@asma-assemble-file ( src-filename* dest-filename* -- )
	;asma/dest-filename STA2 ;asma/src-filename STA2

	;asma-init-first-pass JSR2
	;asma-flush-ignore ;asma/flush-fn STA2
	;asma/src-filename LDA2 ;asma-assemble-file-pass JSR2
	asma-IF-ERROR ,&error JCN

	;asma-init-next-pass JSR2
	;asma-flush-to-file ;asma/flush-fn STA2
	;asma/src-filename LDA2 ;asma-assemble-file-pass JSR2
	asma-IF-ERROR ,&error JCN

	;asma-trees/labels ;asma-print-labels JSR2 ( DEBUG )
	;asma-print-line-count JSR2 ( DEBUG )
	;asma-print-heap-usage JSR2 ( DEBUG )
	JMP2r

	&error
	;asma-print-error JSR2 ( DEBUG )
	JMP2r

(
	Debugging routines. These all output extra information to the Console.
	These can be stripped out to save space, once the references to them are
	removed. Look for the word DEBUG above to find these references: the lines
	that contain that word can be deleted to strip out the functionality
	cleanly.
)

@asma-print-error ( -- )
	;asma/error LDA2 ;asma-print-string JSR2
	#3a .Console/write DEO
	#20 .Console/write DEO
	;asma/orig-token LDA2 ;asma-print-string JSR2
	;&line ;asma-print-string JSR2
	;asma/line LDA2 ;asma-print-short JSR2
	#2e .Console/write DEO
	#0a .Console/write DEO
	JMP2r

	&line 20 "on 20 "line 20 00

@asma-print-line-count ( -- )
	;asma/log-level LDA #01 AND #00 EQU ,&skip JCN
	;asma/line LDA2 ;asma-print-short JSR2
	;&lines ;asma-print-string JSR2
	&skip
	JMP2r

	&lines [ 20 "lines 20 "of 20 "source 20 "code. 0a 00 ]

@asma-print-heap-usage ( -- )
	;asma/log-level LDA #08 AND #00 EQU ,&skip JCN
	;asma/heap LDA2 ;asma-heap SUB2 ;asma-print-short JSR2
	;&str1 ;asma-print-string JSR2
	;asma-heap/end ;asma/heap LDA2 SUB2 ;asma-print-short JSR2
	;&str2 ;asma-print-string JSR2
	&skip
	JMP2r

	&str1 [ 20 "bytes 20 "of 20 "heap 20 "used, 20 00 ]
	&str2 [ 20 "bytes 20 "free. 0a 00 ]

@asma-print-sublabels ( incoming-ptr* -- )
	LDA2
	ORAk ,&valid-incoming-ptr JCN
	POP2 JMP2r

	&valid-incoming-ptr
	( left node )
	DUP2 ,asma-print-sublabels JSR
	( here )
	#09 .Console/write DEO
	DUP2 #0004 ADD2
	&loop
	DUP2 #0001 ADD2 SWP2 LDA
	DUP #00 EQU ,&end JCN
	.Console/write DEO
	,&loop JMP
	&end
	POP
	#09 .Console/write DEO
	LDA2 ;asma-print-short JSR2
	#0a .Console/write DEO

	( right node )
	#0002 ADD2 ,asma-print-sublabels JSR
	JMP2r

@asma-print-labels ( incoming-ptr* -- )
	;asma/log-level LDA #04 AND #00 EQU ,&skip JCN
	LDA2
	ORAk ,&valid-incoming-ptr JCN
	&skip
	POP2 JMP2r

	&valid-incoming-ptr
	( left node )
	DUP2 ,asma-print-labels JSR
	( here )
	DUP2 #0004 ADD2
	&loop
	DUP2 #0001 ADD2 SWP2 LDA
	DUP #00 EQU ,&end JCN
	.Console/write DEO
	,&loop JMP
	&end
	POP
	#09 .Console/write DEO
	LDA2k ;asma-print-short JSR2
	#0a .Console/write DEO
	( subtree )
	#0002 ADD2 ;asma-print-sublabels JSR2

	( right node )
	#0002 ADD2 ,asma-print-labels JSR
	JMP2r

@asma-print-string ( ptr* -- )
	LDAk DUP ,&keep-going JCN
	POP POP2 JMP2r

	&keep-going
	.Console/write DEO
	#0001 ADD2
	,asma-print-string JMP

@asma-print-short ( short* -- )
	LIT '0 .Console/write DEO
	LIT 'x .Console/write DEO
	OVR #04 SFT ,&hex JSR
	SWP #0f AND ,&hex JSR
	DUP #04 SFT ,&hex JSR
	    #0f AND ,&hex JMP

	&hex
	#30 ADD DUP #39 GTH #27 MUL ADD .Console/write DEO
	JMP2r

(
	Initialise the assembler state before loading a file or chunk.
)

@asma-init-first-pass ( -- )
	#ff ;asma/pass STA
	#0000 DUP2k
		;asma/error STA2
		;asma-trees/labels STA2
		;asma-trees/macros STA2
	;asma-heap ;asma/heap STA2
	;asma-opcodes/_entry ;asma-trees/opcodes STA2
	( fall through )

@asma-init-next-pass ( -- )
	;asma/pass LDA #01 ADD ;asma/pass STA
	;asma-write-buffer ;asma-output/ptr STA2
	#0000 DUP2k
		;asma-output/offset STA2
		;asma/addr STA2
		;asma/state STA
	#01 ( 0001 )
	SWPk ( 0001 0100 ) ;asma/written-addr STA2
	;asma/line STA2
	JMP2r

(
	Divide a file up into chunks, and pass each chunk to asma-assemble-chunk.
)

@asma-assemble-file-pass ( filename-ptr* -- )
	#0000

	&loop
	OVR2 .File/name DEO2
	DUP2 .File/offset DEO2
	;asma-read-buffer/end ;asma-read-buffer SUB2 STH2k .File/length DEO2
	;asma-read-buffer DUP2k .File/load DEO2
	.File/success DEI2
	DUP2 STH2r SUB2 ORA ,&last-one JCN
	,asma-assemble-chunk JSR asma-IF-ERROR ,&error JCN
	SUB2 SUB2
	,&loop JMP

	&last-one
	ADD2k #00 ROT ROT STA
	#0001 ADD2
	,asma-assemble-chunk JSR asma-IF-ERROR ,&error JCN

	( flush output buffer )
	;asma-output/ptr LDA2 ;asma-write-buffer SUB2 ;asma/flush-fn LDA2 JSR2

	POP2
	&error
	POP2 POP2 POP2
	JMP2r

(
	Assemble a chunk of source code, which begins with whitespace or the start
	of a token and is divided up into tokens separated by whitespace. If the
	chunk ends with whitespace, assembled-up-to-ptr* will equal ptr* + len* and
	every token in the chunk will have been assembled. If the chunk ends with a
	non-whitespace character, assembled-up-to-ptr* will point to the beginning
	of the last token in the chunk.
)

@asma-assemble-chunk ( ptr* len* -- assembled-up-to-ptr* )
	OVR2 ADD2 #0001 SUB2 SWP2 STH2k
	,&loop JMP

	&next-char-pop
	POP
	&next-char
	#0001 ADD2
	&loop ( last-ptr* ptr* / start-of-token* )
	OVR2 OVR2 LTH2 ,&end JCN
	LDAk ( last-ptr* ptr* char / start-of-token* )
	DUP #20 GTH ,&next-char-pop JCN

	#00 OVR2 ( last-ptr* ptr* char 00 ptr* / start-of-token* )
	STA
	STH2r ,asma-assemble-token JSR asma-IF-ERROR ,&error JCN

	#0a NEQ ,&not-newline JCN
	;asma/line LDA2 #0001 ADD2 ;asma/line STA2
	&not-newline

	DUP2 #0001 ADD2 STH2 ,&next-char JMP

	&end
	POP2 POP2 STH2r
	JMP2r

	&error
	POP POP2 POP2
	JMP2r

@asma [
	&pass $1 &state $1 &line $2
	&token $2 &orig-token $2
	&heap $2 &addr $2 &written-addr $2 &flush-fn $2
	&src-filename $2 &dest-filename $2
	&error $2 &log-level $1
]
@asma-trees [ &labels $2 &macros $2 &opcodes $2 &scope $2 ]

(
	The main routine to assemble a single token.
	asma/state contains several meaningful bits:
	0x02 we are in a comment,
	0x04 we are in a macro body, and
	0x08 we are in a macro body that we are ignoring
	   (because the macro was already defined in a previous pass).
	Since 0x08 never appears without 0x04, the lowest bit set in asma/state is
	always 0x00, 0x02, or 0x04, which is very handy for use with jump tables.
	The lowest bit set can be found easily by #00 (n) SUBk AND.
)

@asma-assemble-token ( string-ptr* -- )
	DUP2 ;asma/token STA2
	DUP2 ;asma/orig-token STA2
	LDAk ,&not-empty JCN
	POP2
	JMP2r

	&not-empty ( token* / )
	( truncate to one char long )
	#0001 ADD2 ( end* / )
	STH2k LDAkr ( end* / end* char )
	STH2k ( end* / end* char end* )
	LITr 00 STH2 ( / end* char end* 00 end* )
	STAr ( / end* char end* )

	#00 ;asma/state LDA SUBk AND ( tree-offset* / end* )
	DUP2 ;&first-char-trees ADD2 ( tree-offset* incoming-ptr* / end* )
	;asma-traverse-tree JSR2

	( restore truncated char )
	STAr

	,&not-found JCN

	( tree-offset* token-routine-ptr* / end* )
	STH2r ;asma/token STA2
	SWP2 POP2 LDA2
	JMP2 ( tail call )

	&not-found ( tree-offset* dummy* / end* )
	POP2 POP2r
	;&body-routines ADD2 LDA2
	JMP2 ( tail call )

	&first-char-trees
		:asma-first-char-normal/_entry
		:asma-first-char-comment/_entry
		:asma-first-char-macro/_entry

	&body-routines
		:asma-normal-body
		:asma-ignore
		:asma-macro-body

@asma-parse-hex-digit ( charcode -- 00-0f if valid hex
                                 OR 10-ff otherwise )
	DUP #3a LTH ,&digit JCN
	DUP #60 GTH ,&letter JCN
	JMP2r

	&digit
	#30 SUB
	JMP2r

	&letter
	#57 SUB
	JMP2r

@asma-parse-hex-string ( -- value* 06 if valid hex and length > 2
                         OR value* 03 if valid hex and length <= 2
                         OR 00 otherwise )
	;asma/token LDA2 DUP2 ,asma-strlen JSR #02 GTH ROT ROT
	LIT2r 0000

	&loop
	LDAk
	DUP ,&not-end JCN
	POP POP2
	STH2r ROT #01 ADD #03 MUL
	JMP2r

	&not-end
	,asma-parse-hex-digit JSR
	DUP #f0 AND ,&fail JCN
	LITr 40 SFT2r
	#00 STH STH ADD2r
	#0001 ADD2
	,&loop JMP

	&fail
	POP POP2 POP2r
	DUP EOR
	JMP2r

@asma-strlen ( string-ptr* -- length )
	LITr 00

	&loop
	LDAk
	,&not-end JCN
	POP2 STHr
	JMP2r

	&not-end
	LITr 01 ADDr
	#0001 ADD2
	,&loop JMP

%asma-SHORT-FLAG { #20 }
%asma-RETURN-FLAG { #40 }
%asma-KEEP-FLAG { #80 }

@asma-parse-opcode ( -- byte 00 if valid opcode
                     OR 01 otherwise )
	;asma/token LDA2
	DUP2 ,asma-strlen JSR #03 LTH ,&too-short JCN

	( truncate to three chars long )
	#0003 ADD2 ( end* / )
	STH2k LDAkr ( end* / end* char )
	STH2k ( end* / end* char end* )
	LITr 00 STH2 ( / end* char end* 00 end* )
	STAr ( / end* char end* )

	;asma-trees/opcodes ;asma-traverse-tree JSR2
	STAr
	,&not-found JCN

	;asma-opcodes/_disasm SUB2 #03 SFT2 ( 00 byte / end* )
	&loop
	LDAkr STHr LIT2r 0001 ADD2r ( 00 byte char / end* )
	DUP ,&not-end JCN
	POP POP2r
	SWP
	JMP2r

	&not-end
	DUP LIT '2 NEQ ,&not-two JCN
	POP asma-SHORT-FLAG ORA ,&loop JMP

	&not-two
	DUP LIT 'r NEQ ,&not-return JCN
	POP asma-RETURN-FLAG ORA ,&loop JMP

	&not-return
	LIT 'k NEQ ,&not-keep JCN
	asma-KEEP-FLAG ORA ,&loop JMP

	&not-keep ( 00 byte / end* )
	&not-found ( incoming-ptr* / end* )
	POP2r
	&too-short ( token* / )
	POP2 #01
	JMP2r

@asma-write-short ( short -- )
	SWP
	,asma-write-byte JSR
	,asma-write-byte JMP ( tail call )

@asma-write-byte ( byte -- )
	;asma/addr LDA2 ;asma/written-addr LDA2
	LTH2k ,&rewound JCN
	&loop
	EQU2k ,&ready JCN
	#00 ,&write JSR
	#0001 ADD2
	,&loop JMP

	&rewound
	;asma-msg-rewound ;asma/error STA2
	POP2 POP2 POP JMP2r

	&ready
	POP2 #0001 ADD2
	DUP2 ;asma/addr STA2
	;asma/written-addr STA2

	&write
	,asma-output/ptr LDR2
	DUP2 ;asma-write-buffer/end EQU2 ,&flush JCN
	&after-flush
	STH2k STA
	STH2r #0001 ADD2 ,asma-output/ptr STR2
	JMP2r

	&flush ( ptr* -- start-of-buffer* )
	;asma-write-buffer SUB2k ( ptr* start* len* )
	;asma/flush-fn LDA2 JSR2
	SWP2 POP2 ( start* )
	,&after-flush JMP

@asma-output [ &ptr $2 &offset $2 &filename $2 ]

@asma-flush-ignore ( len* -- )
	POP2
	JMP2r

@asma-flush-to-file ( len* -- )
	DUP2 .File/length DEO2
	,asma-output/offset LDR2 DUP2 .File/offset DEO2 ADD2 ,asma-output/offset STR2
	;asma/dest-filename LDA2 .File/name DEO2
	;asma-write-buffer .File/save DEO2
	JMP2r

@asma-append-heap-byte ( dummy byte -- dummy )
	;asma/heap LDA2
	OVR2 OVR2 STA POP
	#0001 ADD2 ;asma/heap STA2
	POP
	JMP2r

@asma-append-heap-short ( dummy short* -- dummy )
	SWP
	,asma-append-heap-byte JSR
	,asma-append-heap-byte JMP ( tail call )

@asma-append-heap-string ( string* -- )
	LDAk
	DUP ,asma-append-heap-byte JSR
	,&keep-going JCN
	POP2 JMP2r

	&keep-going
	#0001 ADD2
	,asma-append-heap-string JMP

@asma-traverse-tree ( incoming-ptr* -- binary-ptr* 00 if key found
                                    OR node-incoming-ptr* 01 if key not found )
	&loop ( incoming-ptr* )
	LDA2k ORA ,&valid-node JCN
	#01 JMP2r

	&valid-node
	LDA2 STH2k
	#0004 ADD2 ,asma-strcmp-tree JSR
	DUP ,&nomatch JCN
	POP2r JMP2r

	&nomatch
	#06 SFT #02 AND #00 SWP
	STH2r ADD2
	,&loop JMP

	( &help-str "Looking 20 "up 20 00 )

@asma-strcmp-tree ( node-key* -- order if strings differ
                              OR after-node-key* 00 if strings match )
	;asma/token LDA2 STH2

	&loop ( node-key* / token* )
	DUP2 #0001 ADD2 SWP2 LDA LDAkr STHr
	ORAk ,&not-end JCN

	( end of C strings, match found )
	POP2r POP
	JMP2r

	&not-end
	SUB
	DUP ,&nomatch JCN
	POP
	LIT2r 0001 ADD2r
	,&loop JMP

	&nomatch
	POP2r ROT ROT POP2
	JMP2r

(
	First character routines.
	The following routines (that don't have a FORTH-like signature) are called
	to deal with tokens that begin with particular first letters, or (for
	-body routines) tokens that fail to match any first letter in their tree.
)

%asma-STATE-SET { ;asma/state LDA ORA ;asma/state STA }
%asma-STATE-CLEAR { #ff EOR ;asma/state LDA AND ;asma/state STA }

@asma-comment-start
	#02 asma-STATE-SET
@asma-ignore
	JMP2r

@asma-comment-end
	#02 asma-STATE-CLEAR
	JMP2r

@asma-macro-define
	;asma/pass LDA ,&ignore-macro JCN

	;asma-trees/macros ;asma-traverse-tree JSR2 ,&not-exist JCN
	POP2
	;asma-msg-macro ;asma/error STA2
	JMP2r

	&not-exist
	( define macro by creating new node )
	;asma/heap LDA2 SWP2 STA2
	#0000 ;asma-append-heap-short JSR2 ( less-than pointer )
	#0000 ;asma-append-heap-short JSR2 ( greater-than pointer )
	;asma/token LDA2 ;asma-append-heap-string JSR2 ( key )
	#04 asma-STATE-SET
	JMP2r

	&ignore-macro
	#0c asma-STATE-SET
	JMP2r

@asma-macro-body
	;asma/state LDA #08 AND ,&skip JCN
	;asma/token LDA2 ;asma-append-heap-string JSR2
	&skip
	JMP2r

@asma-macro-end
	#00 ;asma-append-heap-byte JSR2
	#0c asma-STATE-CLEAR
	JMP2r

@asma-label-define
	;asma-trees/labels ,asma-label-helper JSR
	,&already-existed JCN

	#0000 ;asma-append-heap-short JSR2 ( data2: subtree incoming ptr )

	&already-existed
	#0002 ADD2 ;asma-trees/scope STA2
	JMP2r

@asma-sublabel-define
	;asma-trees/scope LDA2 ,asma-label-helper JSR
	POP POP2
	JMP2r

@asma-label-helper ( incoming-ptr* -- binary-ptr* 00 if label existed already
                                   OR binary-ptr* 01 if label was created )
	;asma-traverse-tree JSR2
	,&new-label JCN

	( label already exists )
	( FIXME check label address hasn't changed (label defined twice) )
	#01 JMP2r

	&new-label ( incoming-ptr* )
	( define label by creating new node )
	;asma/heap LDA2 SWP2 STA2
	#0000 ;asma-append-heap-short JSR2 ( less-than pointer )
	#0000 ;asma-append-heap-short JSR2 ( greater-than pointer )
	;asma/token LDA2 ;asma-append-heap-string JSR2 ( key )

	;asma/heap LDA2

	;asma/addr LDA2 ;asma-append-heap-short JSR2 ( data1: address )
	#00 JMP2r

@asma-pad-absolute
	#0000 ,asma-pad-helper JMP

@asma-pad-relative
	;asma/addr LDA2
	( fall through )

@asma-pad-helper ( offset* -- )
	;asma-parse-hex-string JSR2
	,&valid JCN

	;asma-msg-hex ;asma/error STA2
	JMP2r

	&valid
	ADD2 ;asma/addr STA2
	JMP2r

@asma-raw-char
	;asma/token LDA2 LDA
	;asma-write-byte JMP2 ( tail call )

@asma-raw-word
	;asma/token LDA2

	&loop
	LDAk
	DUP ,&not-end JCN

	POP POP2
	JMP2r

	&not-end
	;asma-write-byte JSR2
	#0001 ADD2
	,&loop JMP

@asma-literal-abs-addr
	LIT LIT2 ;asma-write-byte JSR2
	( fall through )

@asma-abs-addr
	,asma-addr-helper JSR
	;asma-write-short JMP2 ( tail call )

@asma-literal-zero-addr
	LIT LIT ;asma-write-byte JSR2
	,asma-addr-helper JSR
	;asma-write-byte JSR2

	,&not-zero-page JCN
	JMP2r

	&not-zero-page
	;asma-msg-zero-page ;asma/error STA2
	JMP2r

@asma-literal-rel-addr
	LIT LIT ;asma-write-byte JSR2
	,asma-addr-helper JSR ;asma/addr LDA2 SUB2 #0002 SUB2

	DUP2 #0080 LTH2 STH
	DUP2 #ff7f GTH2 STHr ORA ,&in-bounds JCN

	POP2
	;asma-msg-relative ;asma/error STA2
	JMP2r

	&in-bounds
	;asma-write-byte JSR2
	POP
	JMP2r

@asma-addr-helper ( -- addr* )
	;asma/token LDA2 LDAk #26 NEQ ,&not-local JCN
	#0001 ADD2 ;asma/token STA2
	;asma-trees/scope LDA2
	,&final-lookup JMP

	&not-local ( token* )
	LDAk
	DUP ,&not-end JCN
	POP POP2
	;asma-trees/labels
	,&final-lookup JMP

	&not-end ( token* char )
	#2f EQU ,&found-slash JCN
	#0001 ADD2
	,&not-local JMP

	&found-slash ( token* )
	DUP2 #00 ROT ROT STA
	;asma-trees/labels ;asma-traverse-tree JSR2 STH
	SWP2 DUP2 #2f ROT ROT STA
	STHr ,&not-found2 JCN
	( token* binary-ptr* )
	#0001 ADD2 ;asma/token STA2
	#0002 ADD2

	&final-lookup ( addr-offset* incoming-ptr* )
	;asma-traverse-tree JSR2
	,&not-found JCN
	LDA2
	JMP2r

	&not-found2 ( dummy* dummy* )
	POP2
	&not-found ( dummy* )
	POP2

	;asma/pass LDA #00 EQU ,&ignore-error JCN
	;asma-msg-label ;asma/error STA2
	&ignore-error

	;asma/addr LDA2
	JMP2r

@asma-literal-hex
	;asma-parse-hex-string JSR2 JMP
	( hex invalid ) ,&invalid JMP
	( hex byte    ) ,asma-byte-helper JMP
	( hex short   ) ,asma-short-helper JMP

	&invalid
	;asma-msg-hex ;asma/error STA2
	JMP2r

@asma-byte-helper ( dummy value -- )
	LIT LIT ;asma-write-byte JSR2
	&raw
	;asma-write-byte JSR2
	POP
	JMP2r

@asma-short-helper ( value* -- )
	LIT LIT2 ;asma-write-byte JSR2
	&raw
	;asma-write-short JMP2 ( tail call )

@asma-normal-body
	;asma-parse-opcode JSR2 ,&not-opcode JCN
	;asma-write-byte JMP2 ( tail call )

	&not-opcode
	;asma-parse-hex-string JSR2 JMP
	( hex invalid ) ,&not-hex JMP
	( hex byte    ) ,asma-byte-helper/raw JMP
	( hex short   ) ,asma-short-helper/raw JMP

	&not-hex
	;asma-trees/macros ;asma-traverse-tree JSR2 ,&not-macro JCN

	.System/rst DEI #e0 GTH ,&too-deep JCN

	&macro-loop
	LDAk ,&keep-going JCN
	POP2
	JMP2r

	&keep-going
	DUP2k ;asma-strlen JSR2 #00 SWP #0001 ADD2 ADD2
	SWP2 ;asma-assemble-token JSR2 asma-IF-ERROR ,&error JCN
	,&macro-loop JMP

	&not-macro
	;asma-msg-label ;asma/error STA2
	&error
	POP2
	JMP2r

	&too-deep
	;asma-msg-too-deep ;asma/error STA2
	POP2
	JMP2r

( Error messages )

@asma-msg-hex       "Invalid 20 "hexadecimal 00
@asma-msg-zero-page "Address 20 "not 20 "in 20 "zero 20 "page 00
@asma-msg-relative  "Address 20 "outside 20 "range 00
@asma-msg-label     "Label 20 "not 20 "found 00
@asma-msg-macro     "Macro 20 "already 20 "exists 00
@asma-msg-rewound   "Memory 20 "overwrite 00
@asma-msg-too-deep  "Macro 20 "expansion 20 "level 20 "too 20 "deep 00

( trees )

( --- 8< ------- 8< --- cut here --- 8< ------- 8< --- )
(          automatically generated code below          )
(          see etc/asma.moon for instructions          )

(	label       less       greater      key            binary
	            than       than         string         data )

@asma-first-char-comment
	&_entry      $2         $2          ') 00          :asma-comment-end

@asma-first-char-macro
	&28          $2         $2          '( 00          :asma-comment-start
	&29         :&28        $2          ') 00          :asma-comment-end
	&_entry     :&29       :&7d         '{ 00          :asma-ignore
	&7d          $2         $2          '} 00          :asma-macro-end

@asma-first-char-normal
	&22          $2         $2          '" 00          :asma-raw-word
	&23         :&22        $2          '# 00          :asma-literal-hex
	&24         :&23       :&25         '$ 00          :asma-pad-relative
	&25          $2         $2          '% 00          :asma-macro-define
	&26         :&24       :&29         26 00 ( & )    :asma-sublabel-define
	&27          $2         $2          '' 00          :asma-raw-char
	&28         :&27        $2          '( 00          :asma-comment-start
	&29         :&28       :&2c         ') 00          :asma-comment-end
	&2c          $2         $2          ', 00          :asma-literal-rel-addr
	&_entry     :&26       :&5d         '. 00          :asma-literal-zero-addr
	&3a          $2         $2          ': 00          :asma-abs-addr
	&3b         :&3a        $2          '; 00          :asma-literal-abs-addr
	&40         :&3b       :&5b         '@ 00          :asma-label-define
	&5b          $2         $2          '[ 00          :asma-ignore
	&5d         :&40       :&7c         '] 00          :asma-ignore
	&7b          $2         $2          '{ 00          :asma-ignore
	&7c         :&7b       :&7d         '| 00          :asma-pad-absolute
	&7d          $2         $2          '} 00          :asma-ignore

@asma-opcodes
	&BRK        :&AND      :&DEI &_disasm "BRK 00
	&_entry     :&EQU      :&ROT        "LIT 00
	&NOP        :&MUL      :&OVR        "NOP 00
	&POP         $2         $2          "POP 00
	&DUP        :&DIV      :&EOR        "DUP 00
	&SWP         $2         $2          "SWP 00
	&OVR        :&ORA      :&POP        "OVR 00
	&ROT        :&NOP      :&STR        "ROT 00
	&EQU        :&DEO      :&JSR        "EQU 00
	&NEQ         $2         $2          "NEQ 00
	&GTH         $2         $2          "GTH 00
	&LTH         $2         $2          "LTH 00
	&JMP         $2         $2          "JMP 00
	&JCN        :&GTH      :&JMP        "JCN 00
	&JSR        :&JCN      :&LDR        "JSR 00
	&STH         $2         $2          "STH 00
	&LDZ         $2         $2          "LDZ 00
	&STZ         $2         $2          "STZ 00
	&LDR        :&LDA      :&LDZ        "LDR 00
	&STR        :&STA      :&SUB        "STR 00
	&LDA         $2         $2          "LDA 00
	&STA        :&SFT      :&STH        "STA 00
	&DEI         $2         $2          "DEI 00
	&DEO        :&BRK      :&DUP        "DEO 00
	&ADD         $2         $2          "ADD 00
	&SUB        :&STZ      :&SWP        "SUB 00
	&MUL        :&LTH      :&NEQ        "MUL 00
	&DIV         $2         $2          "DIV 00
	&AND        :&ADD       $2          "AND 00
	&ORA         $2         $2          "ORA 00
	&EOR         $2         $2          "EOR 00
	&SFT         $2         $2          "SFT 00

(
	Heap, a large temporary area for keeping track of labels. More complex
	programs need more of this space. If there's insufficient space then the
	assembly process will fail, but having extra space above what the most
	complex program needs provides no benefit.

	This heap, and the buffers below, are free to be used to hold temporary
	data between assembly runs, and do not need to be initialized with any
	particular contents to use the assembler.
)

@asma-heap

|e000 &end

(
	Buffer for use with loading source code.
	The minimum size is the length of the longest token plus one, which is
	0x21 to keep the same capability of the C assembler.
	Larger sizes are more efficient, provided there is enough
	heap space to keep track of all the labels.
)

@asma-read-buffer

|f800 &end

(
	Buffer for use with writing output.
	The minimum size is 1, and larger sizes are more efficient.
)

@asma-write-buffer

|ffff &end