( cat input.tal | uxncli drifloon.rom > output.rom ) |00 @System/vector $2 &expansion $2 &wst $1 &rst $1 &metadata $2 &r $2 &g $2 &b $2 &debug $1 &state $1 |10 @Console/vector $2 &read $1 &pad $4 &type $1 &write $1 &error $1 |000 @scope/buf $3f &cap $1 @token/buf $3f &cap $1 |100 @on-reset ( -> ) ;meta #06 DEO2 ;dict/reset scope/ ;assembly/on-console .Console/vector DEO2 BRK @assembly/on-console ( -> ) [ LIT2 04 -Console/type ] DEI EQU ?{ .Console/read DEI token/ BRK } / BRK @rom/ ( byte addr* -- ) ;&mem ADD2 STA JMP2r @rom/ ( -- ) ;dict/assembled err/ ;dict/in err/ ;head/length LDA2 DUP2 #0100 SUB2 err/ ;dict/bytes err/ ( | write ) ;rom/mem ADD2 ;rom/output &>l LDAk #18 DEO INC2 GTH2k ?&>l POP2 POP2 JMP2r @runes/concat ( t* -- ) POP2 JMP2r %err/ ( c -- ) { #19 DEO } @meta $1 ( name ) "Drifloon 0a ( desc ) "Uxntal 20 "Assembler 0a ( auth ) "By 20 "Devine 20 "Lu 20 "Linvega 0a ( date ) "22 20 "Oct 20 "2025 $2 ( Core ) @assembly/ ( -- ) ( cap ) #0a token/ ,&mode LDR2 ;comment/assemble NEQ2 ?{ ( ! ) ;dict/open ;dict/trail ;dict/Comment err/ } ,&mode LDR2 ;macros/assemble NEQ2 ?{ ( ! ) ;dict/open ;dict/trail ;dict/Macro err/ } .System/state DEI ?{ refs/ .System/state DEI ?{ [ LIT2 80 -System/state ] DEO !syms/ } } JMP2r @assembly/apply ( t* -- ) LDZk ?{ POP2 JMP2r } [ LIT2 &mode =standard/assemble ] JMP2 ( @|Standard ) @standard/ ( -- ) ;&assemble ;assembly/mode STA2 JMP2r @standard/assemble ( t* -- ) ( hex ) str/is-hex ?rom/ ( opc ) opcodes/is-opcode ?rom/ LDZk runes/find INC2k ORA ?{ POP2 ( mac ) DUP2 macros/find-name [ INC2k ORA ?macros/ POP2 ] ( imm ) !runes/litjsi } INC2 LDA2 JMP2 ( @|Comment ) @comment/ ( t* -- ) POP2 ;&assemble ;assembly/mode STA2 [ LIT2 01 _&depth ] STR JMP2r @comment/assemble ( t* -- ) LDA2 DUP2 [ LITr &depth $1 ] ( a ) LIT2 "( $1 EQU2 [ STH ADDr ] ( b ) LIT2 ") $1 EQU2 [ STH SUBr ] ( . ) STHkr LITr _&depth STRr ?{ !standard/ } JMP2r ( @|Macros ) @macros/ ( t* -- ) name/ / #00 / ;&walk ;assembly/mode STA2 JMP2r &walk ( t* -- ) LDA2 [ LIT2 "{ $1 ] NEQ2 ?{ ;&assemble ;assembly/mode STA2 [ LIT2 01 _&depth ] STR } JMP2r @macros/assemble ( t* -- ) LDA2k DUP2 [ LITr &depth $1 ] ( a ) LIT "{ EQU SWP LIT "{ EQU ORA [ STH ADDr ] ( b ) LIT2 "} $1 EQU2 [ STH SUBr ] ( . ) STHkr LITr _&depth STRr ?{ POP2 #00 / !standard/ } DUP2 / #20 !/ @macros/find-name ( name* -- * ) STH2 ,&ptr LDR2 ;&mem &>lf EQU2k ?&fail DUP2 STH2kr str/cmp ?{ str/cap str/cap !&>lf &fail POP2 #ffff } NIP2 POP2r JMP2r @macros/ ( t* -- ) &>wp LDAk DUP ?{ POP POP2 JMP2r } / INC2 !&>wp @macros/ ( byte -- ) [ LIT2 &ptr =&mem ] INC2k ( | check overflow ) DUP2 ;&memend LTH2 ?{ ( ! ) ;dict/exceeded ;dict/Macros err/ } ,&ptr STR2 STA JMP2r @macros/ ( t* macro* -- ) token/ str/cap &>wm LDAk DUP ?{ POP POP2 POP2 JMP2r } token/ INC2 !&>wm ( @|Token ) @token/ ( -- ) [ LIT2 -&buf _&ptr ] STR [ LIT2 00 -&buf ] STZ JMP2r @token/ ( c -- ) DUP #20 GTH ?{ ;&buf assembly/apply #0a NEQ ?{ [ LIT2 &line 0001 ] INC2 ,&line STR2 } !/ } [ LIT2 00 &ptr -&buf ] INCk ( | check overflow ) DUP .&cap LTH ?{ ( ! ) ;dict/exceeded ;dict/Name err/ } ,&ptr STR STZ2 JMP2r ( @|Scope ) @scope/ ( c -- ) [ LIT2 00 &ptr -&buf ] INCk ( | check overflow ) DUP .&cap LTH ?{ ( ! ) ;dict/exceeded ;dict/Symbol err/ } ,&ptr STR STZ2 JMP2r @scope/ ( name* -- ) [ LIT2 -&buf _&ptr ] STR &>w LDAk [ LIT "/ ] EQU ?{ LDAk / INC2 LDAk ?&>w } POP2 ,&ptr LDR ,&anchor STR JMP2r @scope/make-name ( name* -- scope/label* ) INC2 [ LIT2 &anchor $1 _&ptr ] STR [ LIT "/ ] / &>wl LDAk DUP ?{ POP POP2 ;&buf JMP2r } / INC2 !&>wl ( @|Runes ) @runes/find ( char -- * ) STH ;&lut &>w LDAk STHkr EQU ?{ #0003 ADD2 LDAk ?&>w POP2 #ffff } POPr JMP2r @runes/ignore ( t* -- ) POP2 JMP2r &lambda ( t* -- ) POP2 !lambda/pop &coment ( t* -- ) !comment/ ¯os ( t* -- ) /req-name !macros/ &padabs ( t* -- ) /req-name refs/get-any !head/ &padrel ( t* -- ) /req-name refs/get-any !head/ &toplab ( t* -- ) /req-name DUP2 scope/ !syms/ &sublab ( t* -- ) scope/make-name !syms/ &litrel ( t* -- ) #80 rom/ &rawrel /req-name refs/get-rel !rom/ &litzep ( t* -- ) #80 rom/ &rawzep /req-name refs/get-abs !rom/ &litabs ( t* -- ) #a0 rom/ &rawabs /req-name refs/get-abs2 !rom/ &litjci ( t* -- ) /req-name #20 !rom/ &litjmi ( t* -- ) /req-name #40 !rom/ &litjsi ( t* -- ) #60 !rom/ &lithex ( t* -- ) /req-name !rom/ &rawstr ( t* -- ) /req-name !rom/ @runes/req-name ( str* -- str1* ) INC2 LDAk #20 GTH ?{ ;dict/invalid ;dict/Name !err/ } JMP2r @runes/lut [ "| =&padabs "$ =&padrel "@ =&toplab "& =&sublab ", =&litrel "_ =&rawrel ". =&litzep "- =&rawzep "; =&litabs "= =&rawabs "! =&litjmi "? =&litjci "# =&lithex "" =&rawstr "} =&lambda "~ =&concat "( =&coment ") =&ignore "[ =&ignore "] =&ignore "% =¯os ] $1 ( @|Opcodes ) @opcodes/is-opcode ( str* -- str* bool ) DUP2 /parse #00 NEQ STH DUP2 ;&brk str/cmp STHr ORA JMP2r @opcodes/parse ( str* -- byte ) [ LIT2r 1f00 ] ;&lut &>w1 SWP2k #0003 SWP2 mem/cmp ?{ INCr #0003 ADD2 LDAk ?&>w1 POP2 POP2 POP2r #00 JMP2r } POP2 ( mask ) ANDr ( litk ) LDA2k [ LIT2 "LI ] EQU2 #70 SFT [ STH ORAr ] ( move ) #0003 ADD2 &>w2 LDAk #21 LTH ?{ ( | parse modes ) LDAk [ LIT "2 ] NEQ ?{ LITr 20 !&r } LDAk [ LIT "r ] NEQ ?{ LITr 40 !&r } LDAk [ LIT "k ] NEQ ?{ LITr 80 !&r } POP2 POPr #00 JMP2r &r ORAr INC2 !&>w2 } POP2 STHr JMP2r @opcodes/lut [ "LIT "INC "POP "NIP "SWP "ROT "DUP "OVR "EQU "NEQ "GTH "LTH "JMP "JCN "JSR "STH "LDZ "STZ "LDR "STR "LDA "STA "DEI "DEO "ADD "SUB "MUL "DIV "AND "ORA "EOR "SFT ] &brk "BRK $1 ( @|Lambda ) @lambda/make-name ( token* -- name* ) POP2 [ LIT &count $1 ] INCk ,&count STR DUP [ LIT2 &ptr =&mem ] INC2k ,&ptr STR2 STA ( >> ) @lambda/name ( id -- str* ) DUP #04 SFT hexc SWP hexc ,&id STR2 ;&sym JMP2r @lambda/pop ( -- ) ,&ptr LDR2 #0001 SUB2 LDAk /name syms/ ,&ptr STR2 JMP2r &sym cebb &id ".. $1 ( @|Name ) @name/ ( name* -- name* ) ( not hex ) str/is-hex ?&fail ( not lambda ) LDAk LIT "{ EQU ?&fail ( not runic ) LDAk runes/find INC2 ORA ?&fail ( dup macros ) DUP2 macros/find-name INC2 ORA ?&dup ( dup symbol ) DUP2 syms/find-name INC2 ORA ?&dup ( not opcode ) opcodes/is-opcode [ JMP JMP2r ] &fail ( -- ) ;dict/invalid ;dict/Name !err/ &dup ( -- ) ;dict/duplicate ;dict/Name !err/ @name/unpack ( name* -- name* ) LDAk [ LIT "{ ] EQU ?lambda/make-name LDAk [ LIT "/ ] EQU ?scope/make-name LDAk [ LIT "& ] EQU ?scope/make-name JMP2r ( @|Syms ) @syms/ ( name* -- ) DUP2 /find-name INC2k ORA ?{ ( alloc ) POP2 .SymType/declared head/get ;&ptr LDA2 refs/ !/ } ( | name* sym* -- ) DUP2 refs/ INC2k INC2 LDA .SymType/declared AND ?{ ( addr* ) head/get OVR2 STA2 ( type ) INC2 INC2 LDAk .SymType/declared ORA ROT ROT STA ( name* ) POP2 JMP2r } POP2 POP2 ( ! ) ;dict/duplicate ;dict/Symbol !err/ @syms/find-name ( name* -- * ) STH2 ;&ptr LDA2 ;&mem &>lfn DUP2 #0003 ADD2 STH2kr str/cmp ?{ /next GTH2k ?&>lfn POP2 #ffff } NIP2 POP2r JMP2r @syms/find-alloc ( name* -- * ) DUP2 /find-name INC2k ORA ?{ ( null* -> ptr* ) POP2 ,&ptr LDR2 ( alloc ) OVR2 .SymType/used #ffff / } NIP2 JMP2r @syms/ ( name* type addr* -- ) ( hb ) SWP / ( lb ) / ( type ) / name/ ( >> ) @syms/ ( word* -- ) LDAk / INC2 LDAk ?& LDA ( >> ) @syms/ ( byte -- ) [ LIT2 &ptr =&mem ] INC2k ( | check overflow ) DUP2 ;&memend LTH2 ?{ ( ! ) ;dict/exceeded ;dict/Symbols err/ } ,&ptr STR2 STA JMP2r @syms/ ( -- ) ;&ptr LDA2 ;&mem &>ls EQU2k ?{ INC2k INC2 LDA .SymType/used AND ?{ LDA2k #0100 EQU2 ?{ DUP2 #0003 ADD2 LDAk [ LIT "A ] SUB #1a LTH ?{ ;dict/unused err/ DUP2 err/ #0a err/ } POP2 } } /next !&>ls } POP2 POP2 !rom/ @syms/next ( sym* -- next* ) #0003 ADD2 !str/cap @syms/byte-distance ( addr* -- addr* ) DUP2 #0080 ADD2 POP ?{ JMP2r } ( ! ) ;dict/too-far ;dict/Symbol !err/ ( @|References ) @refs/get-any ( str* -- value* ) str/is-hex ?str/hex name/unpack syms/find-name INC2k ORA ?{ ( ! ) ;dict/invalid ;dict/Reference err/ } LDA2 JMP2r @refs/get-type ( token* type* -- addr* ) ,&type STR2 name/unpack ( | find symbol or create it ) syms/find-alloc ( | check if declared ) INC2k INC2 STH2k LDA .SymType/declared AND ?{ DUP2 head/get ( addr* ) / ( symbol* ) / ( type-fn* ) [ LIT2 &type $2 ] / ( scope* ) [ LIT2 &scope $2 ] / ( line* ) ;token/line LDA2 / } ( | mark as used ) LDAkr STHr .SymType/used ORA STH2r STA LDA2 JMP2r @refs/ ( value* -- ) SWP / ( >> ) @refs/ ( byte -- ) [ LIT2 &ptr =&mem ] INC2k ( | check overflow ) DUP2 ;&memend LTH2 ?{ ( ! ) ;dict/exceeded ;dict/References err/ } ,&ptr STR2 STA JMP2r @refs/get-abs ( label* -- addr ) ;&handle-abs /get-type NIP JMP2r @refs/get-abs2 ( label* -- addr* ) ;&handle-abs2 !/get-type @refs/get-rel ( label* -- distance ) ;&handle-rel /get-type INC2k ORA ?{ ( undefined ) POP2 #00 JMP2r } head/get /get-distance syms/byte-distance NIP JMP2r @refs/get-rel2 ( label* -- distance* ) ;&handle-rel2 /get-type head/get ( >> ) @refs/get-distance ( a* b* -- distance* ) INC2 INC2 SUB2 JMP2r @refs/ ( -- ) ,&ptr LDR2 ;&mem &>l EQU2k ?{ DUP2 ;err/ref STA2 DUP2k #0004 ADD2 LDA2 JSR2 ( ) #000a ADD2 !&>l } POP2 POP2 JMP2r @refs/resolve-sym ( ref* -- ref* sym/addr* ) LDA2k head/ ( ref* sym* ) INC2k INC2 LDA2 ( ref* sym/addr* ) LDA2 ( ref* sym/addr* ) INC2k ORA ?{ ( ! ) ;dict/invalid !err/ } ( ref* sym/addr* ) JMP2r @refs/handle-abs ( ref* -- ) /resolve-sym NIP2 NIP !rom/ @refs/handle-abs2 ( ref* -- ) /resolve-sym NIP2 !rom/ @refs/handle-rel ( ref* -- ) /resolve-sym SWP2 LDA2 /get-distance /byte-distance NIP !rom/ @refs/handle-rel2 ( ref* -- ) /resolve-sym SWP2 LDA2 /get-distance !rom/ @refs/byte-distance ( addr* -- addr* ) DUP2 #0080 ADD2 POP ?{ JMP2r } ( ! ) ;dict/too-far !err/ @refs/ ( sym* -- ) DUP2 #0003 ADD2 LDA2 #cebb NEQ2 ?{ POP2 JMP2r } ;refs/scope STA2 JMP2r ( @|Rom ) @rom/ ( str* -- ) LDAk / INC2 LDAk ?& POP2 JMP2r @rom/ ( str* -- ) opcodes/parse !/ @rom/ ( str* -- ) str/len #02 NEQ #50 SFT #80 ORA / ( >> ) @rom/ ( str* -- ) str/is-hex #00 EQU ?{ str/len DUP #02 NEQ ?{ POP str/hex NIP !/ } #04 NEQ ?{ str/hex !/ } } POP2 ;dict/invalid ;dict/Number !err/ @rom/ ( str* opc -- ) / refs/get-rel2 ( >> ) @rom/ ( short* -- ) SWP / ( >> ) @rom/ ( byte -- ) head/get-inc ( | test zero-page ) OVR ?{ POP2 POP ( ! ) ;dict/zero-page ;dict/Writing !err/ } !rom/ @head/get-inc ( -- addr* ) [ LIT2 &addr 0100 ] INC2k ,&addr STR2 INC2k [ LIT2 &length 0100 ] LTH2 ?{ INC2k ,&length STR2 } JMP2r @head/get ( -- addr* ) ,&addr LDR2 JMP2r @head/ ( addr* -- ) /get ADD2 ( >> ) @head/ ( addr* -- ) ,&addr STR2 JMP2r ( @|Stdlib ) @hexc ( hex -- char ) #0f AND #0a LTHk ?{ SUB [ LIT "a ] ADD JMP2r } POP [ LIT "0 ] ADD JMP2r @chex ( addr* -- addr* ) LDAk ( dec ) [ LIT "0 ] SUB DUP #09 GTH [ JMP JMP2r ] ( hex ) #27 SUB DUP #0a SUB #05 GTH [ JMP JMP2r ] ( nil ) POP #ff JMP2r @str/hex ( str* -- value* ) [ LIT2r 0000 ] &>wh [ LITr 40 ] SFT2r chex [ LITr 00 ] STH ADD2r INC2 LDAk ?&>wh POP2 STH2r JMP2r @str/len ( str* -- str* length ) DUP2k /cap SWP2 INC2 SUB2 NIP JMP2r @str/is-hex ( str* -- str* f ) DUP2 &>wih chex INC ?{ LDA #00 EQU JMP2r } INC2 !&>wih @str/cap ( str* -- end* ) LDAk ?{ INC2 JMP2r } INC2 !/cap @str/cmp ( a* b* -- bool ) DUP2k /cap SWP2 SUB2 SWP2 ( >> ) @mem/cmp ( a* length* b* -- t ) STH2 OVR2 ADD2 SWP2 &>l EQU2k ?{ LDAk LDAkr STHr NEQ ?{ INC2 INC2r !&>l } } POP2r EQU2 JMP2r ( @|Error ) @err/ ( adj* topic* -- ) .System/state DEI ?{ [ LIT2 01 -System/state ] DEO / #20 err/ / ;dict/spacer / ;token/buf / ;token/line LDA2 ;scope/buf !/ } POP2 POP2 JMP2r @err/ ( adj* -- ) .System/state DEI ?{ [ LIT2 01 -System/state ] DEO ;dict/Reference err/ #20 err/ err/ ;dict/spacer err/ [ LIT2 &ref $2 ] INC2k INC2 LDA2 #0003 ADD2 err/ DUP2 #0008 ADD2 LDA2 SWP2 #0006 ADD2 LDA2 #0003 ADD2 !/ } POP2 JMP2r @err/ ( line* scope* -- ) ;dict/in / err/ LIT ": err/ err/ #0a err/ JMP2r @err/ ( adj* keyword* topic* -- ) .System/state DEI ?{ [ LIT2 01 -System/state ] DEO / #20 err/ SWP2 / ;dict/spacer / / #0a err/ JMP2r } POP2 POP2 POP2 JMP2r @err/ ( str* -- ) &>w LDAk DUP ?{ POP POP2 JMP2r } err/ INC2 !&>w @err/ ( short* -- ) [ LIT2r ff00 ] &>read #000a DIV2k STH2k MUL2 SUB2 STH2r INCr ORAk ?&>read POP2 &>write NIP #30 ADD err/ OVRr ADDr STHkr ?&>write POP2r JMP2r @dict/assembled "Assembled $1 &in 20 "in 20 $1 &bytes 20 "bytes. 0a $1 &unused "-- 20 "Unused &spacer ": 20 $1 &References "References $1 &Reference "Reference $1 &Symbols "Symbols $1 &Symbol "Symbol $1 &Macros "Macros $1 &Macro "Macro $1 &Name "Name $1 &Number "Number $1 &Comment "Comment $1 &Writing "Writing $1 &exceeded "exceeded $1 &invalid "invalid $1 &duplicate "duplicate $1 &too-far "too 20 "far $1 &zero-page "zero-page $1 &open "open $1 &trail ".. $1 &reset "RESET $1 ( @|Buffers ) @lambda/mem $100 @macros/mem ( name\0, value\0 ) $1000 &memend @refs/mem ( addr*, symbol*, type-fn*, scope*, line* ) $3000 &memend @syms/mem ( addr*, SymType, name\0 ) $4800 &memend @rom/mem ( zeropage ) $100 &output ( @|Enums ) |00 @SymType/empty $1 &used $1 &declared