commit 4d6fc067782b105687225d92f84d597f6c2048b3
parent 532c1959dc55f3fd23c841b5fb33123980b20a7d
Author: Andrew Alderwick <andrew@alderwick.co.uk>
Date: Thu, 7 Oct 2021 21:08:18 +0100
Splitted asma into library and piano demo.
Diffstat:
3 files changed, 959 insertions(+), 957 deletions(-)
diff --git a/projects/examples/demos/asma-piano.tal b/projects/examples/demos/asma-piano.tal
@@ -0,0 +1,101 @@
+( devices )
+
+|00 @System [ &vector $2 &wst $1 &rst $1 &pad $4 &r $2 &g $2 &b $2 &debug $1 &halt $1 ]
+|10 @Console [ &pad $8 &write $1 ]
+|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ]
+
+( vectors )
+
+|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.
+ )
+ #0d ;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-ls 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
+
+include projects/library/asma.tal
+
+(
+ 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
+
diff --git a/projects/library/asma.tal b/projects/library/asma.tal
@@ -0,0 +1,858 @@
+(
+ 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.
+)
+
+(
+ 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
+
+ ( flush output buffer )
+ ;asma-output/ptr LDA2 ;asma-write-buffer SUB2 ;asma/flush-fn LDA2 JSR2
+
+ ;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
+ ;heap LDA2 ;asma-heap SUB2 ;asma-print-short JSR2
+ ;&str1 ;asma-print-string JSR2
+ ;asma-heap/end ;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 INC2 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 INC2 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
+ INC2
+ ,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 #3a LTH ,¬-alpha JCN
+ #27 ADD
+ ¬-alpha
+ .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 ;heap STA2
+ ;asma-opcodes/_entry ;asma-trees/opcodes STA2
+ ( fall through )
+
+@asma-init-next-pass ( -- )
+ ;asma/pass LDA INC ;asma/pass STA
+ ;asma-write-buffer ;asma-output/ptr STA2
+ #0000 DUP2k
+ ;asma-output/offset STA2
+ ;asma/addr STA2
+ ;asma/state STA
+ #01 SWP ( 0100 ) ;asma/written-addr STA2
+ ;&preamble-end ;&preamble SUB2k ,asma-assemble-chunk JSR POP2 POP2
+ JMP2r
+
+ &preamble
+ "%BRK 20 '{ 20 "00 20 '} 20
+ &preamble-end
+
+(
+ Divide a file up into chunks, and pass each chunk to asma-assemble-chunk.
+)
+
+@asma-assemble-file-pass ( filename-ptr* -- )
+ ;asma-assemble-chunk #0000 ROT2 ( func* line^ filename* )
+ ;asma-read-buffer DUP2 ;asma-read-buffer/end ROT2 SUB2 ( func* line^ filename* buf* size^ )
+ ROT2 ( func* line^ buf* size^ filename* )
+ ,file-read-chunks JSR
+
+ asma-IF-ERROR ,&error JCN
+
+ &error
+ POP2 POP2 POP2 POP2 POP2
+ JMP2r
+
+include projects/library/file-read-chunks.tal
+
+(
+ 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 ( line^ chunk* len^ -- line^ assembled-up-to-chunk* )
+ ROT2 STH2 ( chunk* len^ / line^ )
+ OVR2 ADD2 ( chunk* end-chunk* / line^ )
+ OVR2 ;asma-read-buffer EQU2 STH
+ DUP2 ;asma-read-buffer/end NEQ2
+ STHr AND ;asma/eof STA
+ SWP2 STH2k ( end-chunk* chunk* / line^ start-of-token* )
+
+ &loop ( end-chunk* char* / line^ start-of-token* )
+ LDAk #21 LTH ,&whitespace JCN
+ INC2 ,&loop JMP
+
+ &whitespace ( end-chunk* ws-char* / line^ start-of-token* )
+ GTH2k ,&within-chunk JCN
+ ;asma/eof LDA ,&eof JCN
+
+ ( reached the end of the chunk, start-of-token* is where we assembled up to )
+ POP2 POP2 STH2r STH2r SWP2 JMP2r
+
+ &within-chunk ( end-chunk* ws-char* / line^ start-of-token* )
+ LDAk #0a NEQ ( end-chunk* ws-char* not-newline / line^ start-of-token* )
+ #00 OVR2 STA
+ STH2r ,asma-assemble-token JSR ( end-chunk* ws-char* not-newline / line^ )
+ asma-IF-ERROR ,&error JCN
+ JMP INC2r ( end-chunk* ws-char* / line^ )
+ ;asma/break LDA ,&break JCN
+ INC2 STH2k ( end-chunk* start-of-token* / line^ start-of-token* )
+ ,&loop JMP
+
+ &break ( end-chunk* ws-char* / line^ )
+ ( the read buffer has been invalidated, ws-char* plus one is where we assembled up to )
+ ;asma/break LDA #01 SUB ;asma/break STA
+ INC2 NIP2 ( assembled-up-to-ptr* / line^ )
+ STH2r SWP2 JMP2r
+
+ &error ( end-chunk* ws-char* not-newline / line^ )
+ ( return no progress with assembly to make file-read-chunks exit )
+ POP POP2 POP2
+ STH2kr ;asma/line STA2
+ STH2r ;asma-read-buffer
+ JMP2r
+
+ &eof ( end-chunk* ws-char* / line^ start-of-token* )
+ ( reached the end of file, end-chunk* is safe to write since the buffer is bigger )
+ ( return no progress with assembly to make file-read-chunks exit )
+ POP2 ( end-chunk* / line^ start-of-token* )
+ #00 ROT ROT STA ( / line^ start-of-token* )
+ STH2r ,asma-assemble-token JSR ( / line^ )
+ STH2r ;asma-read-buffer JMP2r
+
+@asma [
+ &pass $1 &state $1 &line $2 &break $1 &eof $1
+ &token $2 &orig-token $2
+ &addr $2 &written-addr $2 &flush-fn $2
+ &src-filename $2 &dest-filename $2
+ &error $2 &log-level $1
+]
+@asma-trees [ &labels $2 ¯os $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,
+ 0x08 we are expecting an include filename, and
+ 0x10 we are in a macro body that we are ignoring
+ (because the macro was already defined in a previous pass).
+ Since 0x10 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 ,¬-empty JCN
+ POP2
+ JMP2r
+
+ ¬-empty ( token* / )
+ ( truncate to one char long )
+ INC2 ( 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
+
+ ,¬-found JCN
+
+ ( tree-offset* token-routine-ptr* / end* )
+ STH2r ;asma/token STA2
+ NIP2 LDA2
+ JMP2 ( tail call )
+
+ ¬-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
+ $2 ( invalid position )
+ $2 ( empty tree for include )
+
+ &body-routines
+ :asma-normal-body
+ :asma-ignore
+ :asma-macro-body
+ $2 ( invalid position )
+ :asma-include-filename
+
+@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 ,strlen JSR #0002 GTH2 ROT ROT
+ LIT2r 0000
+
+ &loop
+ LDAk
+ DUP ,¬-end JCN
+ POP POP2
+ STH2r ROT INC DUPk ADD ADD
+ JMP2r
+
+ ¬-end
+ ,asma-parse-hex-digit JSR
+ DUP #f0 AND ,&fail JCN
+ LITr 40 SFT2r
+ #00 STH STH ADD2r
+ INC2
+ ,&loop JMP
+
+ &fail
+ POP POP2 POP2r
+ DUP EOR
+ JMP2r
+
+%asma-SHORT-FLAG { #20 }
+%asma-RETURN-FLAG { #40 }
+%asma-KEEP-FLAG { #80 }
+
+@asma-traverse-tree ( incoming-ptr* -- binary-ptr* 00 if key found
+ OR node-incoming-ptr* 01 if key not found )
+ ;asma/token LDA2
+ ( fall through to traverse-tree )
+
+include projects/library/binary-tree.tal
+include projects/library/string.tal
+
+@asma-parse-opcode ( -- byte 00 if valid opcode
+ OR 01 otherwise )
+ ;asma/token LDA2
+ DUP2 ,strlen JSR #0003 LTH2 ,&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
+ ,¬-found JCN
+
+ ;asma-opcodes/_disasm SUB2 #03 SFT2 ( 00 byte / end* )
+ &loop
+ LDAkr STHr LIT2r 0001 ADD2r ( 00 byte char / end* )
+ DUP ,¬-end JCN
+ POP POP2r
+ DUP ,¬-zero JCN #80 NIP ( LIT by itself needs keep flag, to distinguish from BRK )
+ ¬-zero
+ SWP
+ JMP2r
+
+ ¬-end
+ DUP LIT '2 NEQ ,¬-two JCN
+ POP asma-SHORT-FLAG ORA ,&loop JMP
+
+ ¬-two
+ DUP LIT 'r NEQ ,¬-return JCN
+ POP asma-RETURN-FLAG ORA ,&loop JMP
+
+ ¬-return
+ LIT 'k NEQ ,¬-keep JCN
+ asma-KEEP-FLAG ORA ,&loop JMP
+
+ ¬-keep ( 00 byte / end* )
+ ¬-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
+ INC2
+ ,&loop JMP
+
+ &rewound
+ ;asma-msg-rewound ;asma/error STA2
+ POP2 POP2 POP JMP2r
+
+ &ready
+ POP2 INC2
+ 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 INC2 ,asma-output/ptr STR2
+ JMP2r
+
+ &flush ( ptr* -- start-of-buffer* )
+ ;asma-write-buffer SUB2k ( ptr* start* len* )
+ ;asma/flush-fn LDA2 JSR2
+ NIP2 ( 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-ls DEO2 ADD2 ,asma-output/offset STR2
+ ;asma/dest-filename LDA2 .File/name DEO2
+ ;asma-write-buffer .File/save DEO2
+ JMP2r
+
+include projects/library/heap.tal
+
+(
+ 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 ,¬-exist JCN
+ POP2
+ ;asma-msg-macro ;asma/error STA2
+ JMP2r
+
+ ¬-exist ( incoming-ptr* )
+ ( define macro by creating new node )
+ ;heap LDA2 SWP2 STA2
+ #0000 ;append-heap-short JSR2 ( less-than pointer )
+ #0000 ;append-heap-short JSR2 ( greater-than pointer )
+ ;asma/token LDA2 ;append-heap-string JSR2 ( key )
+ #04 asma-STATE-SET
+ JMP2r
+
+ &ignore-macro
+ #14 asma-STATE-SET
+ JMP2r
+
+@asma-macro-body
+ ;asma/state LDA #10 AND ,&skip JCN
+ ;asma/token LDA2 ;append-heap-string JSR2
+ &skip
+ JMP2r
+
+@asma-macro-end
+ #00 ;append-heap-byte JSR2
+ #14 asma-STATE-CLEAR
+ JMP2r
+
+@asma-label-define
+ ;asma-trees/labels ,asma-label-helper JSR
+ ,&already-existed JCN
+
+ #0000 ;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 )
+ ;heap LDA2 SWP2 STA2
+ #0000 ;append-heap-short JSR2 ( less-than pointer )
+ #0000 ;append-heap-short JSR2 ( greater-than pointer )
+ ;asma/token LDA2 ;append-heap-string JSR2 ( key )
+
+ ;heap LDA2
+
+ ;asma/addr LDA2 ;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
+ POP2
+ 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 ,¬-end JCN
+
+ POP POP2
+ JMP2r
+
+ ¬-end
+ ;asma-write-byte JSR2
+ INC2
+ ,&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
+
+ ,¬-zero-page JCN
+ JMP2r
+
+ ¬-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 ,¬-local JCN
+ INC2 ;asma/token STA2
+ ;asma-trees/scope LDA2
+ ,&final-lookup JMP
+
+ ¬-local ( token* )
+ LDAk
+ DUP ,¬-end JCN
+ POP POP2
+ ;asma-trees/labels
+ ,&final-lookup JMP
+
+ ¬-end ( token* char )
+ #2f EQU ,&found-slash JCN
+ INC2
+ ,¬-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 ,¬-found2 JCN
+ ( token* binary-ptr* )
+ INC2 ;asma/token STA2
+ #0002 ADD2
+
+ &final-lookup ( addr-offset* incoming-ptr* )
+ ;asma-traverse-tree JSR2
+ ,¬-found JCN
+ LDA2
+ JMP2r
+
+ ¬-found2 ( dummy* dummy* )
+ POP2
+ ¬-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 ,¬-opcode JCN
+ ;asma-write-byte JMP2 ( tail call )
+
+ ¬-opcode
+ ;asma-parse-hex-string JSR2 JMP
+ ( hex invalid ) ,¬-hex JMP
+ ( hex byte ) ,asma-byte-helper/raw JMP
+ ( hex short ) ,asma-short-helper/raw JMP
+
+ ¬-hex
+ ;asma-trees/macros ;asma-traverse-tree JSR2 ,¬-macro JCN
+
+ .System/rst DEI #e0 GTH ,&too-deep JCN
+
+ ¯o-loop
+ LDAk ,&keep-going JCN
+ POP2
+ JMP2r
+
+ &keep-going
+ DUP2k ;strlen JSR2 INC2 ADD2
+ SWP2 ;asma-assemble-token JSR2 asma-IF-ERROR ,&error JCN
+ ,¯o-loop JMP
+
+ ¬-macro
+ POP2
+ ;&include-string ;asma/token LDA2
+ ;strcmp JSR2 NIP2 NIP2 NIP ,¬-include JCN
+ #08 asma-STATE-SET
+ JMP2r
+
+ ¬-include
+ ;asma-msg-label ;asma/error STA2
+ &error
+ JMP2r
+
+ &too-deep
+ POP2
+ ;asma-msg-too-deep ;asma/error STA2
+ JMP2r
+
+ &include-string "include 00
+
+@asma-include-filename
+ #08 asma-STATE-CLEAR
+ ;heap LDA2
+ ;asma/token LDA2 ;append-heap-string JSR2
+ ;asma-assemble-file-pass JSR2
+ ;asma/break LDAk INC ROT ROT STA
+ 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
+ &_entry :>H :&ROT &_disasm "LIT 00
+ &INC $2 $2 "INC 00
+ &POP $2 $2 "POP 00
+ &DUP $2 $2 "DUP 00
+ &NIP :&MUL :&OVR "NIP 00
+ &SWP $2 $2 "SWP 00
+ &OVR :&ORA :&POP "OVR 00
+ &ROT :&NIP :&STR "ROT 00
+ &EQU $2 $2 "EQU 00
+ &NEQ $2 $2 "NEQ 00
+ >H :&DIV :&JSR "GTH 00
+ <H $2 $2 "LTH 00
+ &JMP $2 $2 "JMP 00
+ &JCN :&INC :&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 :&AND :&DEO "DEI 00
+ &DEO $2 $2 "DEO 00
+ &ADD $2 $2 "ADD 00
+ &SUB :&STZ :&SWP "SUB 00
+ &MUL :<H :&NEQ "MUL 00
+ &DIV :&DEI :&EOR "DIV 00
+ &AND :&ADD $2 "AND 00
+ &ORA $2 $2 "ORA 00
+ &EOR :&DUP :&EQU "EOR 00
+ &SFT $2 $2 "SFT 00
+
diff --git a/projects/software/asma.tal b/projects/software/asma.tal
@@ -1,957 +0,0 @@
-( devices )
-
-|00 @System [ &vector $2 &wst $1 &rst $1 &pad $4 &r $2 &g $2 &b $2 &debug $1 &halt $1 ]
-|10 @Console [ &pad $8 &write $1 ]
-|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $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-ls 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
-
- ( flush output buffer )
- ;asma-output/ptr LDA2 ;asma-write-buffer SUB2 ;asma/flush-fn LDA2 JSR2
-
- ;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
- ;heap LDA2 ;asma-heap SUB2 ;asma-print-short JSR2
- ;&str1 ;asma-print-string JSR2
- ;asma-heap/end ;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 INC2 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 INC2 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
- INC2
- ,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 #3a LTH ,¬-alpha JCN
- #27 ADD
- ¬-alpha
- .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 ;heap STA2
- ;asma-opcodes/_entry ;asma-trees/opcodes STA2
- ( fall through )
-
-@asma-init-next-pass ( -- )
- ;asma/pass LDA INC ;asma/pass STA
- ;asma-write-buffer ;asma-output/ptr STA2
- #0000 DUP2k
- ;asma-output/offset STA2
- ;asma/addr STA2
- ;asma/state STA
- #01 SWP ( 0100 ) ;asma/written-addr STA2
- ;&preamble-end ;&preamble SUB2k ,asma-assemble-chunk JSR POP2 POP2
- JMP2r
-
- &preamble
- "%BRK 20 '{ 20 "00 20 '} 20
- &preamble-end
-
-(
- Divide a file up into chunks, and pass each chunk to asma-assemble-chunk.
-)
-
-@asma-assemble-file-pass ( filename-ptr* -- )
- ;asma-assemble-chunk #0000 ROT2 ( func* line^ filename* )
- ;asma-read-buffer DUP2 ;asma-read-buffer/end ROT2 SUB2 ( func* line^ filename* buf* size^ )
- ROT2 ( func* line^ buf* size^ filename* )
- ,file-read-chunks JSR
-
- asma-IF-ERROR ,&error JCN
-
- &error
- POP2 POP2 POP2 POP2 POP2
- JMP2r
-
-include projects/library/file-read-chunks.tal
-
-(
- 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 ( line^ chunk* len^ -- line^ assembled-up-to-chunk* )
- ROT2 STH2 ( chunk* len^ / line^ )
- OVR2 ADD2 ( chunk* end-chunk* / line^ )
- OVR2 ;asma-read-buffer EQU2 STH
- DUP2 ;asma-read-buffer/end NEQ2
- STHr AND ;asma/eof STA
- SWP2 STH2k ( end-chunk* chunk* / line^ start-of-token* )
-
- &loop ( end-chunk* char* / line^ start-of-token* )
- LDAk #21 LTH ,&whitespace JCN
- INC2 ,&loop JMP
-
- &whitespace ( end-chunk* ws-char* / line^ start-of-token* )
- GTH2k ,&within-chunk JCN
- ;asma/eof LDA ,&eof JCN
-
- ( reached the end of the chunk, start-of-token* is where we assembled up to )
- POP2 POP2 STH2r STH2r SWP2 JMP2r
-
- &within-chunk ( end-chunk* ws-char* / line^ start-of-token* )
- LDAk #0a NEQ ( end-chunk* ws-char* not-newline / line^ start-of-token* )
- #00 OVR2 STA
- STH2r ,asma-assemble-token JSR ( end-chunk* ws-char* not-newline / line^ )
- asma-IF-ERROR ,&error JCN
- JMP INC2r ( end-chunk* ws-char* / line^ )
- ;asma/break LDA ,&break JCN
- INC2 STH2k ( end-chunk* start-of-token* / line^ start-of-token* )
- ,&loop JMP
-
- &break ( end-chunk* ws-char* / line^ )
- ( the read buffer has been invalidated, ws-char* plus one is where we assembled up to )
- ;asma/break LDA #01 SUB ;asma/break STA
- INC2 NIP2 ( assembled-up-to-ptr* / line^ )
- STH2r SWP2 JMP2r
-
- &error ( end-chunk* ws-char* not-newline / line^ )
- ( return no progress with assembly to make file-read-chunks exit )
- POP POP2 POP2
- STH2kr ;asma/line STA2
- STH2r ;asma-read-buffer
- JMP2r
-
- &eof ( end-chunk* ws-char* / line^ start-of-token* )
- ( reached the end of file, end-chunk* is safe to write since the buffer is bigger )
- ( return no progress with assembly to make file-read-chunks exit )
- POP2 ( end-chunk* / line^ start-of-token* )
- #00 ROT ROT STA ( / line^ start-of-token* )
- STH2r ,asma-assemble-token JSR ( / line^ )
- STH2r ;asma-read-buffer JMP2r
-
-@asma [
- &pass $1 &state $1 &line $2 &break $1 &eof $1
- &token $2 &orig-token $2
- &addr $2 &written-addr $2 &flush-fn $2
- &src-filename $2 &dest-filename $2
- &error $2 &log-level $1
-]
-@asma-trees [ &labels $2 ¯os $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,
- 0x08 we are expecting an include filename, and
- 0x10 we are in a macro body that we are ignoring
- (because the macro was already defined in a previous pass).
- Since 0x10 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 ,¬-empty JCN
- POP2
- JMP2r
-
- ¬-empty ( token* / )
- ( truncate to one char long )
- INC2 ( 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
-
- ,¬-found JCN
-
- ( tree-offset* token-routine-ptr* / end* )
- STH2r ;asma/token STA2
- NIP2 LDA2
- JMP2 ( tail call )
-
- ¬-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
- $2 ( invalid position )
- $2 ( empty tree for include )
-
- &body-routines
- :asma-normal-body
- :asma-ignore
- :asma-macro-body
- $2 ( invalid position )
- :asma-include-filename
-
-@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 ,strlen JSR #0002 GTH2 ROT ROT
- LIT2r 0000
-
- &loop
- LDAk
- DUP ,¬-end JCN
- POP POP2
- STH2r ROT INC DUPk ADD ADD
- JMP2r
-
- ¬-end
- ,asma-parse-hex-digit JSR
- DUP #f0 AND ,&fail JCN
- LITr 40 SFT2r
- #00 STH STH ADD2r
- INC2
- ,&loop JMP
-
- &fail
- POP POP2 POP2r
- DUP EOR
- JMP2r
-
-%asma-SHORT-FLAG { #20 }
-%asma-RETURN-FLAG { #40 }
-%asma-KEEP-FLAG { #80 }
-
-@asma-traverse-tree ( incoming-ptr* -- binary-ptr* 00 if key found
- OR node-incoming-ptr* 01 if key not found )
- ;asma/token LDA2
- ( fall through to traverse-tree )
-
-include projects/library/binary-tree.tal
-include projects/library/string.tal
-
-@asma-parse-opcode ( -- byte 00 if valid opcode
- OR 01 otherwise )
- ;asma/token LDA2
- DUP2 ,strlen JSR #0003 LTH2 ,&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
- ,¬-found JCN
-
- ;asma-opcodes/_disasm SUB2 #03 SFT2 ( 00 byte / end* )
- &loop
- LDAkr STHr LIT2r 0001 ADD2r ( 00 byte char / end* )
- DUP ,¬-end JCN
- POP POP2r
- DUP ,¬-zero JCN #80 NIP ( LIT by itself needs keep flag, to distinguish from BRK )
- ¬-zero
- SWP
- JMP2r
-
- ¬-end
- DUP LIT '2 NEQ ,¬-two JCN
- POP asma-SHORT-FLAG ORA ,&loop JMP
-
- ¬-two
- DUP LIT 'r NEQ ,¬-return JCN
- POP asma-RETURN-FLAG ORA ,&loop JMP
-
- ¬-return
- LIT 'k NEQ ,¬-keep JCN
- asma-KEEP-FLAG ORA ,&loop JMP
-
- ¬-keep ( 00 byte / end* )
- ¬-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
- INC2
- ,&loop JMP
-
- &rewound
- ;asma-msg-rewound ;asma/error STA2
- POP2 POP2 POP JMP2r
-
- &ready
- POP2 INC2
- 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 INC2 ,asma-output/ptr STR2
- JMP2r
-
- &flush ( ptr* -- start-of-buffer* )
- ;asma-write-buffer SUB2k ( ptr* start* len* )
- ;asma/flush-fn LDA2 JSR2
- NIP2 ( 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-ls DEO2 ADD2 ,asma-output/offset STR2
- ;asma/dest-filename LDA2 .File/name DEO2
- ;asma-write-buffer .File/save DEO2
- JMP2r
-
-include projects/library/heap.tal
-
-(
- 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 ,¬-exist JCN
- POP2
- ;asma-msg-macro ;asma/error STA2
- JMP2r
-
- ¬-exist ( incoming-ptr* )
- ( define macro by creating new node )
- ;heap LDA2 SWP2 STA2
- #0000 ;append-heap-short JSR2 ( less-than pointer )
- #0000 ;append-heap-short JSR2 ( greater-than pointer )
- ;asma/token LDA2 ;append-heap-string JSR2 ( key )
- #04 asma-STATE-SET
- JMP2r
-
- &ignore-macro
- #14 asma-STATE-SET
- JMP2r
-
-@asma-macro-body
- ;asma/state LDA #10 AND ,&skip JCN
- ;asma/token LDA2 ;append-heap-string JSR2
- &skip
- JMP2r
-
-@asma-macro-end
- #00 ;append-heap-byte JSR2
- #14 asma-STATE-CLEAR
- JMP2r
-
-@asma-label-define
- ;asma-trees/labels ,asma-label-helper JSR
- ,&already-existed JCN
-
- #0000 ;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 )
- ;heap LDA2 SWP2 STA2
- #0000 ;append-heap-short JSR2 ( less-than pointer )
- #0000 ;append-heap-short JSR2 ( greater-than pointer )
- ;asma/token LDA2 ;append-heap-string JSR2 ( key )
-
- ;heap LDA2
-
- ;asma/addr LDA2 ;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
- POP2
- 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 ,¬-end JCN
-
- POP POP2
- JMP2r
-
- ¬-end
- ;asma-write-byte JSR2
- INC2
- ,&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
-
- ,¬-zero-page JCN
- JMP2r
-
- ¬-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 ,¬-local JCN
- INC2 ;asma/token STA2
- ;asma-trees/scope LDA2
- ,&final-lookup JMP
-
- ¬-local ( token* )
- LDAk
- DUP ,¬-end JCN
- POP POP2
- ;asma-trees/labels
- ,&final-lookup JMP
-
- ¬-end ( token* char )
- #2f EQU ,&found-slash JCN
- INC2
- ,¬-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 ,¬-found2 JCN
- ( token* binary-ptr* )
- INC2 ;asma/token STA2
- #0002 ADD2
-
- &final-lookup ( addr-offset* incoming-ptr* )
- ;asma-traverse-tree JSR2
- ,¬-found JCN
- LDA2
- JMP2r
-
- ¬-found2 ( dummy* dummy* )
- POP2
- ¬-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 ,¬-opcode JCN
- ;asma-write-byte JMP2 ( tail call )
-
- ¬-opcode
- ;asma-parse-hex-string JSR2 JMP
- ( hex invalid ) ,¬-hex JMP
- ( hex byte ) ,asma-byte-helper/raw JMP
- ( hex short ) ,asma-short-helper/raw JMP
-
- ¬-hex
- ;asma-trees/macros ;asma-traverse-tree JSR2 ,¬-macro JCN
-
- .System/rst DEI #e0 GTH ,&too-deep JCN
-
- ¯o-loop
- LDAk ,&keep-going JCN
- POP2
- JMP2r
-
- &keep-going
- DUP2k ;strlen JSR2 INC2 ADD2
- SWP2 ;asma-assemble-token JSR2 asma-IF-ERROR ,&error JCN
- ,¯o-loop JMP
-
- ¬-macro
- POP2
- ;&include-string ;asma/token LDA2
- ;strcmp JSR2 NIP2 NIP2 NIP ,¬-include JCN
- #08 asma-STATE-SET
- JMP2r
-
- ¬-include
- ;asma-msg-label ;asma/error STA2
- &error
- JMP2r
-
- &too-deep
- POP2
- ;asma-msg-too-deep ;asma/error STA2
- JMP2r
-
- &include-string "include 00
-
-@asma-include-filename
- #08 asma-STATE-CLEAR
- ;heap LDA2
- ;asma/token LDA2 ;append-heap-string JSR2
- ;asma-assemble-file-pass JSR2
- ;asma/break LDAk INC ROT ROT STA
- 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
- &_entry :>H :&ROT &_disasm "LIT 00
- &INC $2 $2 "INC 00
- &POP $2 $2 "POP 00
- &DUP $2 $2 "DUP 00
- &NIP :&MUL :&OVR "NIP 00
- &SWP $2 $2 "SWP 00
- &OVR :&ORA :&POP "OVR 00
- &ROT :&NIP :&STR "ROT 00
- &EQU $2 $2 "EQU 00
- &NEQ $2 $2 "NEQ 00
- >H :&DIV :&JSR "GTH 00
- <H $2 $2 "LTH 00
- &JMP $2 $2 "JMP 00
- &JCN :&INC :&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 :&AND :&DEO "DEI 00
- &DEO $2 $2 "DEO 00
- &ADD $2 $2 "ADD 00
- &SUB :&STZ :&SWP "SUB 00
- &MUL :<H :&NEQ "MUL 00
- &DIV :&DEI :&EOR "DIV 00
- &AND :&ADD $2 "AND 00
- &ORA $2 $2 "ORA 00
- &EOR :&DUP :&EQU "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
-