( usage: drifblim.rom input.tal output.rom ) |10 @Console &vector $2 &read $1 &pad $4 &type $1 &write $1 &error $1 |a0 @File &vector $2 &success $1 &success-lb $1 &stat $2 &delete $1 &append $1 &name $2 &length $2 &read $2 &write $2 |00 @RefType &abs-byte $1 &abs-short $1 &rel-byte $1 &rel-short $1 |00 @SymType $40 &used $40 &declared |000 @src $30 @dst $30 @token $2f &cap $1 @scope $30 @sublabel $30 @halt $1 @rom/head $2 @rom/length $2 |100 @on-reset ( -> ) ;meta #06 DEO2 ;await-src ( >> ) @ ( addr* -> ) .Console/vector DEO2 .Console/type DEI ?{ ;dict/usage #010f DEO } BRK @await-src ( -> ) .Console/read DEI ( ) DUP #20 GTH ?{ POP ;await-dst ! } [ LIT2 00 &ptr -src ] INCk ,&ptr STR STZ2 BRK @await-dst ( -> ) .Console/read DEI ( ) DUP #20 GTH ?{ POP refs/ ( | handle error ) .halt LDZ ?{ .rom/length LDZ2 ORA ?on-success ;dst ;err/empty } #010f DEO BRK } [ LIT2 00 &ptr -dst ] INCk ,&ptr STR STZ2 BRK @on-success ( -> ) rom/ syms/ #800f DEO BRK @meta $1 ( name ) "Drifblim 0a ( desc ) "Uxntal 20 "Assembler 0a ( auth ) "By 20 "Devine 20 "Lu 20 "Linvega 0a ( date ) "17 20 "Aug 20 "2024 $2 ( @|Core ) @ ( -- ) #0100 rom/ ;dict/reset ;src ( >> ) @ ( f* -- ) DUP2 .File/name DEO2 #0001 .File/length DEO2 token/ #0000 &>s ( len* -- ) ;&c feof ?{ INC2 [ LIT &c $1 ] token/ [ LIT2 00 -halt ] LDZ EQU ?&>s } ORA ?{ ( ! ) DUP2 ;err/file } POP2 JMP2r @ ( token* name* -- ) [ LIT2 01 -halt ] STZ [ LIT2 ": 18 ] DEO #2018 DEO #0a18 DEO JMP2r @ ( keyword* name* -- ) [ LIT2 01 -halt ] STZ [ LIT2 ": 18 ] DEO #2018 DEO ;dict/in / ;scope / #0a18 DEO JMP2r @ ( -- ) ( > count ) [ LIT2r 0000 ] syms/get-bounds &>l ( -- ) INC2r ( is used ) INC2k INC2 LDA .SymType/used AND ?{ ( is capitalized ) DUP2 #0003 ADD2 LDA ciuc ?{ ;dict/unused / DUP2 #0003 ADD2 / #0a18 DEO } } #0003 ADD2 scap/ INC2 GTH2k ?&>l POP2 POP2 ( | result ) ;dict/assembled / ;dst / ;dict/in / .rom/length LDZ2 #00ff SUB2 ;dict/bytes / ( labels ) STH2r #0001 SUB2 ;dict/labels / ( symbols ) ;syms/mem-end ;syms/ptr LDA2 SUB2 ;dict/syms-free / ( references ) ;refs/mem-end ;refs/ptr LDA2 SUB2 ;dict/refs-free / ( macros ) ;macros/mem-end ;macros/ptr LDA2 SUB2 ;dict/macros-free !/ ( @|Tokenizer ) @token/ ( -- ) ;token DUP ,&ptr STR ! @token/ ( c -- ) DUP #20 GTH ?{ POP ;token LDZk ?{ POP2 JMP2r } token/ !& } ( | append ) [ LIT2 00 &ptr -token ] ( overflow ) DUP .&cap NEQ ?{ ( ! ) ;token ;err/token-mem } ( move ptr ) INCk ,&ptr STR STZ2 JMP2r @token/ ( t* -- ) LDZk find-rune INC2k ORA ?{ POP2 ( | non-runic ) is-hex ?rom/ is-opcode ?rom/ ( | macros ) DUP2 macros/find INC2k ORA ?& POP2 ( immediate word ) !lib/litjsi } ( call ) INC2 LDA2 JMP2 @token/ ( t* macro* -- ) token/ scap/ INC2 &>w ( -- ) LDAk token/ INC2 LDAk ?&>w POP2 POP2 #20 !token/ ( @|Library ) @lib ( runics ) &padabs INC2 get-any !rom/ &padrel INC2 get-any rom/get-head ADD2 !rom/ &toplab INC2 ! &sublab INC2 get-sublabel !syms/ &litrel #80 rom/ &rawrel INC2 refs/get-rel-byte !rom/ &litzep #80 rom/ &rawzep INC2 refs/get-abs-byte !rom/ &litabs #a0 rom/ &rawabs INC2 refs/get-abs-short !rom/ &litjci INC2 #20 !rom/ &litjmi INC2 #40 !rom/ &litjsi #60 !rom/ &lithex INC2 !rom/ &rawstr INC2 !rom/ &lambda POP2 !lambda/pop &inctal INC2 ;include/mem ;include/mem ! &ignore POP2 JMP2r @lib/comment ( t* -- ) POP2 ( depth ) [ LITr 00 ] &>sc ( -- ) ;&c feof ?{ [ LIT2 &c $1 "( ] NEQk ?{ INCr } POP [ LIT ") ] NEQ ?{ STHkr ?{ POPr JMP2r } [ LITr 01 ] SUBr } !&>sc } POPr JMP2r @lib/macros ( t* -- ) INC2 ( | validate ) is-hex ?¯o-invalid is-opcode ?¯o-invalid is-runic ?¯o-invalid DUP2 syms/find INC2 ORA ?¯o-duplicate DUP2 macros/find INC2 ORA ?¯o-duplicate ( | record ) macros/ #20 ;macros/last STA ( walk to macro start ) &>sm ( -- ) ;&cm feof ?{ [ LIT2 &cm $1 "{ ] EQU ?{ !&>sm } } ( record macro body until bracket ) &>sb ( -- ) ;&cb feof ?{ [ LIT2 "} &cb $1 ] EQUk ?{ DUP macros/ } EQU ?{ !&>sb } } #00 !macros/ ¯o-invalid ( t* -- ) POP2 ( ! ) ;token ;err/macro ! ¯o-duplicate ( t* -- ) POP2 ( ! ) ;token ;err/duplicate ! ( @|Lambda ) @lambda/push ( -- name* ) [ 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 0000 $1 ( @|Macros ) @macros/find ( name* -- * ) ,&t STR2 ,&ptr LDR2 ;&mem &>l ( -- ) DUP2 [ LIT2 &t $2 ] wcmp ?{ scap/ INC2 scap/ INC2 GTH2k ?&>l POP2 POP2 #ffff JMP2r } NIP2 JMP2r @macros/ ( t* -- ) LDAk / INC2 LDAk ?& POP2 #00 !& @macros/ ( c -- ) ( | normalize ) #20 GTHk ?{ SWP } POP ( | join spaces ) DUP #20 NEQ ?{ DUP [ LIT &last 20 ] NEQ ?{ POP JMP2r } } DUP ,&last STR ( >> ) @macros/ ( byte -- ) [ LIT2 &ptr =&mem ] INC2k ,&ptr STR2 STA ( | check overflow ) ,&ptr LDR2 ;&mem-end LTH2 ?{ ( ! ) ;token ;err/macros-mem ! } JMP2r ( @|Syms ) @syms/find ( name* -- * ) ,&t STR2 /get-bounds &>l ( -- ) #0003 ADD2 DUP2 [ LIT2 &t $2 ] wcmp ?{ scap/ INC2 GTH2k ?&>l POP2 POP2 #ffff JMP2r } #0003 SUB2 NIP2 JMP2r @syms/find-alloc ( name* -- addr* ) DUP2 /find INC2k ORA ?{ ( null* -> ptr* ) POP2 ,&ptr LDR2 ( alloc ) OVR2 .SymType/used #ffff / } NIP2 JMP2r @syms/ ( name* -- ) DUP2 macros/find INC2 ORA ?&duplicate DUP2 /find INC2k ORA ?{ ( alloc ) POP2 .SymType/declared rom/get-head !/ } ( | name* sym* -- ) INC2k INC2 LDA .SymType/declared AND ?{ ( addr* ) rom/get-head OVR2 STA2 ( type ) INC2 INC2 LDAk .SymType/declared ORA ROT ROT STA ( name* ) POP2 JMP2r } POP2 &duplicate ( name* -- ) POP2 ( ! ) ;token ;err/duplicate ! @syms/get-bounds ( -- to* from* ) ,&ptr LDR2 ;&mem JMP2r @syms/ ( name* type addr* -- ) ( hb ) SWP / ( lb ) / ( type ) / is-hex ?&invalid is-opcode ?&invalid is-runic ?&invalid ( >> ) @syms/ ( word* -- ) LDAk / INC2 LDAk ?& POP2 #00 ( >> ) @syms/ ( byte -- ) [ LIT2 &ptr =&mem ] INC2k ,&ptr STR2 STA ( | check overflow ) ,&ptr LDR2 ;&mem-end LTH2 ?{ ( ! ) ;token ;err/syms-mem ! } JMP2r @syms/invalid ( name* -- ) / ( ! ) ;token ;err/label ! @syms/ ( -- ) ( add ext ) ;dst DUP2 scap/ ;dict/sym-ext OVR2 SWP2 .File/name DEO2 ;syms/mem-end ;syms/mem &>w ( -- ) ( addr ) #0002 .File/length DEO2 DUP2 .File/write DEO2 ( name ) #0003 ADD2 DUP2 slen INC2 STH2k .File/length DEO2 DUP2 .File/write DEO2 STH2r ADD2 GTH2k ?&>w POP2 POP2 ( rem ext ) #00 ROT ROT STA JMP2r ( @|References ) @get-sublabel ( name* -- sublabel* ) DUP2 slen NIP ;sublabel slen NIP ADD ( check length ) #30 LTH ?{ ( ! ) ;token ;err/sublabel ! } [ LIT2 &ptr $2 ] ;sublabel JMP2r @ ( t* -- ) ( | copy scope until sublabel ) DUP2 [ LITr -scope ] &>w ( -- ) LDAk [ LIT "/ ] EQU ?{ LDAk STHkr STZ INCr INC2 LDAk ?&>w } POP2 #00 STHr STZ ( | prepare sublabel pointer ) ;scope ;sublabel [ LIT2 "/ 00 ] ;sublabel scap/ ( ptr ) INC2k ,get-sublabel/ptr STR2 ( cap ) STA2 !syms/ @get-any ( str* -- value* ) is-hex ?shex ( >> ) @get-ref ( token* -- addr* ) LDA2k [ LIT2 "{ 00 ] NEQ2 ?{ POP2 lambda/push } LDAk [ LIT "/ ] NEQ ?{ INC2 get-sublabel } LDAk [ LIT "& ] NEQ ?{ INC2 get-sublabel } ( | find symbol or create it ) syms/find-alloc ( | check if declared ) INC2k INC2 STH2k LDA .SymType/declared AND ?{ STH2k [ LIT &type $1 ] STH2r rom/get-head refs/ } ( | mark as used ) LDAkr STHr .SymType/used ORA STH2r STA LDA2 JMP2r @refs/ ( type value* addr* -- ) [ LIT2 &ptr =&mem ] STH2k ( addr* ) STA2 ( value* ) INC2r INC2r STH2kr STA2 ( type ) INC2r INC2r STH2kr STA ( save ) INC2r [ LITr _&ptr ] STR2r ( | check overflow ) ,&ptr LDR2 ;&mem-end LTH2 ?{ ( ! ) ;token ;err/refs-mem ! } JMP2r @refs/ ( -- ) ,&ptr LDR2 ;&mem &>l ( -- ) STH2k ( | route ) DUP2 #0004 ADD2 LDA ( ) DUP .RefType/abs-byte NEQ ?{ STH2kr /resolve-abs STA POP } ( ) DUP .RefType/abs-short NEQ ?{ STH2kr /resolve-abs STA2 } ( ) DUP .RefType/rel-byte NEQ ?{ STH2kr /resolve-rel ( | validate distance ) OVR2 #0080 ADD2 POP #00 EQU ?{ ( sym* ) STH2kr INC2 INC2 LDA2 ( sym/name* ) #0003 ADD2 ;err/distance } STA POP } ( ) DUP .RefType/rel-short NEQ ?{ STH2kr /resolve-rel STA2 } POP POP2r ( ) #0005 ADD2 GTH2k ?&>l POP2 POP2 JMP2r @refs/resolve-sym ( ref* -- ref* addr* ) ( sym* ) INC2k INC2 LDA2 ( sym/addr* ) LDA2k INC2k ORA ?{ ( ! ) OVR2 #0003 ADD2 ;err/reference } ( | sym* addr* ) NIP2 JMP2r @refs/resolve-abs ( ref* -- value* addr* ) ( value* ) /resolve-sym ( addr* ) SWP2 LDA2 ;rom/mem ADD2 JMP2r @refs/resolve-rel ( ref* -- value* addr* ) ( value* ) /resolve-sym OVR2 LDA2 INC2 INC2 SUB2 ( addr* ) SWP2 LDA2 ;rom/mem ADD2 JMP2r @refs/get-abs-byte ( label* -- addr ) .RefType/abs-byte ;get-ref/type STA get-ref NIP JMP2r @refs/get-abs-short ( label* -- addr* ) .RefType/abs-short ;get-ref/type STA !get-ref @refs/get-rel-byte ( label* -- distance ) .RefType/rel-byte ;get-ref/type STA get-ref INC2k ORA ?{ ( undefined ) POP2 #00 JMP2r } rom/get-head INC2 INC2 SUB2 ( | check distance ) DUP2 #0080 ADD2 POP ?{ NIP JMP2r } NIP ( ! ) ;token ;err/distance ! @refs/get-rel-short ( label* -- distance* ) .RefType/rel-short ;get-ref/type STA get-ref rom/get-head INC2 INC2 SUB2 JMP2r ( @|Rom ) @rom/ ( str* -: ) LDAk / INC2 LDAk ?& POP2 JMP2r @rom/ ( str* -: ) find-opcode !& @rom/ ( str* -: ) is-hex ?{ ( ! ) ;token ;err/hex } DUP2 slen NIP ( LIT ) DUP #04 EQU #50 SFT #80 ORA / !& @rom/ ( str* -: ) DUP2 slen NIP ( >> ) @rom/ ( str* len -- ) DUP #02 NEQ ?{ POP shex NIP !& } #04 NEQ ?{ shex !& } POP2 ( ! ) ;token ;err/hex ! @rom/ ( str* opc -: ) / refs/get-rel-short ( >> ) @rom/ ( short* -: ) SWP / ( >> ) @rom/ ( byte -: ) DUP .&head LDZ2 INC2k .&head STZ2 ;&mem ADD2 STA ( | change length ) ?{ JMP2r } .&head LDZ2 ( ) OVR ?{ ( ! ) ;token ;err/zeropage } ( ) DUP2 #8000 LTH2 ?{ ( ! ) ;token ;err/rom-mem } .&length STZ2 JMP2r @rom/ ( addr* -: ) .&head STZ2 JMP2r @rom/get-head ( -- addr* ) .&head LDZ2 JMP2r @rom/ ( -: ) ;dst .File/name DEO2 .&length LDZ2 #00ff SUB2 .File/length DEO2 ;&output .File/write DEO2 JMP2r ( @|Helpers ) @is-runic ( str* -- str* f ) LDAk find-rune INC2 ORA JMP2r @find-rune ( char -- * ) ,&rune STR ;runes/end ;runes &>l ( -- ) LDAk [ LIT &rune $1 ] NEQ ?{ NIP2 JMP2r } #0003 ADD2 GTH2k ?&>l POP2 POP2 #ffff JMP2r @is-opcode ( str* -- str* f ) DUP2 find-opcode ?{ DUP2 ;opcodes/brk !scmp3 } #01 JMP2r @find-opcode ( name* -- byte ) ,&t STR2 #2000 &>l ( -- ) #00 OVR #03 MUL ;opcodes ADD2 [ LIT2 &t $2 ] scmp3 ?{ INC GTHk ?&>l POP2 #00 JMP2r } ( add keep mode to LIT ) NIP DUP #00 EQU #70 SFT ORA ( move to modes ) ,&t LDR2 #0003 ADD2 ( keep flag ) [ LITr 00 ] &>w ( -- ) LDAk #20 OVR [ LIT "2 ] EQU ?&end DUP ADD OVR [ LIT "r ] EQU ?&end DUP ADD OVR [ LIT "k ] EQU ?&end DUP ADD OVR #00 EQU ?&end ( ! ) ;token ;err/opcode &end STH POP ORAr INC2 LDAk ?&>w POP2 STHr ADD JMP2r @is-hex ( str* -- str* f ) DUP2 &>w ( -- ) LDAk chex INC ?{ POP2 #00 JMP2r } INC2 LDAk ?&>w POP2 #01 JMP2r ( @|Stdlib ) @scap ( str* -: end* ) INC2 & LDAk ?scap JMP2r @slen ( str* -: len* ) DUP2 scap/ SWP2 SUB2 JMP2r @scmp3 ( a* b* -- f ) STH2 LDAkr LDAk STHr NEQ ?{ INC2r INC2 } LDA2r LDA2 STH2r EQU2 JMP2r @shex ( str* -: value* ) [ LIT2r 0000 ] &>w ( `i* -- ) ( acc ) [ LITr 40 ] SFT2r ( res ) LDAk chex [ LITr 00 ] STH ADD2r INC2 LDAk ?&>w POP2 STH2r JMP2r @ ( src* dst* -: ) STH2 &>w ( src* `dst* -- ) LDAk #00 STH2kr STA2 INC2r INC2 LDAk ?&>w POP2 POP2r JMP2r @ ( str* -- ) #00 ROT ROT &>w ( -- ) STAk INC2 LDAk ?&>w STA JMP2r @wcmp ( a* b* -: f ) STH2 &>l ( a* `b* -- f ) LDAk #21 LTH ?{ LDAk LDAkr STHr NEQ ?{ INC2 INC2r !&>l } } LDA #21 LTH LDAr STHr #21 LTH AND JMP2r @hexc ( hex -- char ) #0f AND #0a LTHk ?{ SUB [ LIT "a ] ADD JMP2r } POP [ LIT "0 ] ADD JMP2r @ciuc ( char -- uppercase ) [ LIT "A ] SUB #1a LTH JMP2r @chex ( c -: ) [ LIT "0 ] SUB DUP #0a LTH ?&end #27 SUB DUP #10 LTH ?&end POP #ff &end JMP2r @feof ( addr* -: f ) .File/read DEO2 [ LIT2 00 -File/success-lb ] DEI EQU JMP2r @ ( str* -: ) LDAk #18 DEO INC2 & LDAk ? POP2 JMP2r @ ( short* -- ) ORAk ?{ [ LIT2 "0 18 ] DEO JMP2r } #2710 [ LIT2r 00fb ] &>w ( -- ) DIV2k #000a DIV2k MUL2 SUB2 SWPr EQUk OVR STHkr EQU AND ?{ DUP [ LIT "0 ] ADD #18 DEO INCr } POP2 #000a DIV2 SWPr INCr STHkr ?&>w POP2r POP2 POP2 JMP2r ( @|Assets ) @dict &usage "usage: 20 "drifblim.rom 20 "in.tal 20 "out.rom 0a $1 &unused "-- 20 "Unused: 20 $1 &reset "RESET $1 &sym-ext ".sym $1 &assembled "Assembled 20 $1 &in 20 "in 20 $1 &bytes 20 "bytes( $1 &labels 20 "labels, 20 $1 &syms-free 20 "syms 20 "free, 20 $1 &refs-free 20 "refs 20 "free, 20 $1 ¯os-free 20 "macros 20 "free). 0a $1 @err &file "File 20 "missing $1 &duplicate "Label 20 "duplicate $1 &label "Label 20 "invalid $1 &hex "Hexadecimal 20 "invalid $1 &opcode "Opcode 20 "invalid $1 &reference "Reference 20 "invalid $1 ¯o "Macro 20 "invalid $1 &zeropage "Writing 20 "zero-page $1 &distance "Reference 20 "too 20 "far $1 &empty "Output 20 "empty $1 &sublabel "Sublabel $1 &token-mem "Token 20 "exceeded $1 &rom-mem "Rom 20 "exceeded $1 &refs-mem "References 20 "exceeded $1 &syms-mem "Symbols 20 "exceeded $1 ¯os-mem "Macros 20 "exceeded $1 @opcodes [ "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 ] @runes [ "| =lib/padabs "$ =lib/padrel "} =lib/lambda "( =lib/comment "@ =lib/toplab "& =lib/sublab ", =lib/litrel "_ =lib/rawrel ". =lib/litzep "- =lib/rawzep "; =lib/litabs "= =lib/rawabs "? =lib/litjci "! =lib/litjmi 5b =lib/ignore 5d =lib/ignore "# =lib/lithex "" =lib/rawstr "~ =lib/inctal "% =lib/macros ] &end ( .. ) ( @|Buffers ) @lambda/mem $100 @include/mem $30 @refs/mem ( addr*, symbol*, RefType ) $1800 &mem-end @syms/mem ( addr*, SymType, body..0 ) $4800 &mem-end @macros/mem ( name..0, value..0 ) $1000 &mem-end @rom/mem ( zeropage ) $100 @rom/output $7f00 &mem-end