~~~ c uxn on ilo c This is console-only, assembled with pali. The RAM packs four c bytes per 32-bit cell, with the lowest address in bits 0-7; c shorts are big-endian. Guest addresses use memory helpers. The c guest stacks and devices use one byte per cell, with circular c stacks and 8-bit pointers, separate from ilo's stacks. Helpers c preserve deeper operands and balance temporary entries. Jumps c to helpers act as tail calls; related routines sometimes fall c through to avoid call/return overhead. Stack effects describe c ilo operands unless marked guest; a is below b, and value is c a byte or short for the current mode. Guest stack effects c assume keep mode is off; keep mode retains the input operands. : uxn:start i lica.... r uxn:load-rom i lica.... r uxn:eval i lica.... r uxn:console-loop : uxn:halt c Keep re in a separate bundle: halt I/O sets IP past memory, c but re in the same bundle would restore the caller's IP and c undo the halt. i lica.... r uxn:stderr-flush i liio.... d 6 i re...... : uxn:load-rom c The ROM is loaded from the ilo block storage because ilo does c not have host file system access. i lilist.. d 0 r uxn:block : uxn:load-next i lifeduli r uxn:block d 1024 i muliad.. r uxn:ram i liadliio d 64 d 2 i lifeliad r uxn:block d 1 i dulist.. r uxn:block i liltlicj d 16 r uxn:load-next i re...... : uxn:eval i lilist.. d 1 r uxn:running : uxn:eval-next i lica.... r uxn:fetch i listlica r uxn:opcode r uxn:decode i lica.... r uxn:dispatch i lifelicj r uxn:running r uxn:eval-next i re...... : uxn:fetch c (-- byte) i lifedu.. r uxn:pc i liadlian d 1 d 65535 i listliju r uxn:pc r uxn:read-byte : uxn:decode i lifelian r uxn:opcode d 32 i listlife r uxn:short r uxn:opcode i lianlist d 128 r uxn:keep : uxn:decode-stack i lililife r uxn:wsp r uxn:working-stack r uxn:opcode i lianlicj d 64 r uxn:decode-return i liju.... r uxn:decode-selected : uxn:decode-return i drdrlili r uxn:rsp r uxn:return-stack : uxn:decode-selected c read-ptr is a separate pop cursor. Keep-mode pops decrement c only this cursor; pushes start at the retained real pointer c and increment it normally. i listduli r uxn:stack-base r uxn:ptr-address i stfelist r uxn:read-ptr i re...... : uxn:dispatch i lifelian r uxn:opcode d 31 i liadfeju r uxn:op-table : uxn:read-byte c (address -- byte) i lianduli d 65535 d 3 i anlimupu d 8 i lisrliad d 2 r uxn:ram i feposrli d 255 i anre.... : uxn:write-byte c (byte address --) i lianduli d 65535 d 2 i srliadpu r uxn:ram i lianlimu d 3 d 8 i dupuswli d 255 i answslli d 255 i posllixo d -1 i podufesw i puanorpo i stre.... : uxn:read-short c (address -- short) i dulica.. r uxn:read-byte i lislswli d 8 d 1 i adlica.. r uxn:read-byte i orre.... : uxn:write-short c (short address --) i lianpudu d 65535 i lisrpodu d 8 i pulica.. r uxn:write-byte i poliad.. d 1 i liju.... r uxn:write-byte : uxn:pop-byte c (-- byte) i lifelisu r uxn:read-ptr d 1 i lianduli d 255 r uxn:read-ptr i stlife.. r uxn:keep i licj.... r uxn:pop-kept i dulifest r uxn:ptr-address : uxn:pop-kept i lifeadfe r uxn:stack-base i re...... : uxn:push-byte c (byte --) i lianlife d 255 r uxn:ptr-address i felifead r uxn:stack-base i stlifefe r uxn:ptr-address i liadlian d 1 d 255 i lifestre r uxn:ptr-address : uxn:pop-value c (-- value) i lica.... r uxn:pop-byte i lifelicj r uxn:short r uxn:pop-short i re...... : uxn:pop-short c (low-byte -- short) i lica.... r uxn:pop-byte i lislorre d 8 : uxn:push-value c (value --) i lifelicj r uxn:short r uxn:push-short i liju.... r uxn:push-byte : uxn:push-short c (short --) i dulisr.. d 8 i lica.... r uxn:push-byte i liju.... r uxn:push-byte : uxn:other-stack i lifelieq r uxn:ptr-address r uxn:wsp i licj.... r uxn:select-return i lilist.. r uxn:wsp r uxn:ptr-address i lilistre r uxn:working-stack r uxn:stack-base : uxn:select-return i lilist.. r uxn:rsp r uxn:ptr-address i lilistre r uxn:return-stack r uxn:stack-base : uxn:device-read c (port -- byte) i lianliad d 255 r uxn:devices i fere.... : uxn:device-store c (byte port --) i lianliad d 255 r uxn:devices i pulianpo d 255 i stre.... : uxn:device-write c DEO2 invokes only the low-byte port callback, matching c the sibling uxn-min.c. c (byte port --) i listlist r uxn:address r uxn:value i lifelife r uxn:value r uxn:address i lica.... r uxn:device-store i lifelian r uxn:address d 255 i lieqlicj d 17 r uxn:device-vector i lifelian r uxn:address d 255 i lieqlicj d 24 r uxn:device-output i lifelian r uxn:address d 255 i lieqlicj d 25 r uxn:device-error i re...... : uxn:device-vector i lilica.. d 16 r uxn:device-read i lislli.. d 8 d 17 i lica.... r uxn:device-read i orlistre r uxn:console-vector : uxn:device-output i lifelian r uxn:value d 255 i liiore.. d 0 : uxn:device-error c Build-time stderr toggle: reference stderr-empty to drop c characters (the default), or stderr-write to c buffer them in the block store. c Buffered stderr stores 1024 byte values per block, one per c 32-bit cell, starting at block 64. Each fresh image that writes c stderr overwrites from there; older trailing blocks remain. c uxn:halt flushes a partial block with zero padding for unused c cells. c Standard ilo character output has no stdout/stderr selector. i liju.... r uxn:stderr-empty : uxn:stderr-write i lifelian r uxn:value d 255 i lifeliad r uxn:stderr-count r uxn:block-buffer i st...... i lifeliad r uxn:stderr-count d 1 i dulistli r uxn:stderr-count d 1024 i eqlicj.. r uxn:stderr-flush i re...... : uxn:stderr-flush i lifelieq r uxn:stderr-count d 0 i licj.... r uxn:stderr-empty i lifelili r uxn:stderr-block r uxn:block-buffer d 3 i iolife.. r uxn:stderr-block i liadlist d 1 r uxn:stderr-block i lilistli d 0 r uxn:stderr-count r uxn:block-buffer : uxn:stderr-clear c Clear the buffer so a later partial block has zero padding. i dulisw.. d 0 i stliaddu d 1 i liliadlt r uxn:block-buffer d 1024 i licj.... r uxn:stderr-clear i dr...... : uxn:stderr-empty i re...... : uxn:console-loop i lilica.. d 15 r uxn:device-read i licj.... r uxn:console-exit i lifelieq r uxn:console-vector d 0 i licj.... r uxn:console-exit i liiodu.. d 1 i lieqlicj d -1 r uxn:console-eof i lilica.. d 18 r uxn:device-store i lililica d 1 d 23 r uxn:device-store i lica.... r uxn:console-event i liju.... r uxn:console-loop : uxn:console-eof i drlili.. d 10 d 18 i lica.... r uxn:device-store i lililica d 4 d 23 r uxn:device-store i lica.... r uxn:console-event : uxn:console-exit c Save guest exit status; ilo has no standard way to return it c to the host. i lilica.. d 15 r uxn:device-read i lianlist d 127 r uxn:exit-status i re...... : uxn:console-event i lifelist r uxn:console-vector r uxn:pc i liju.... r uxn:eval : uxn:op-table r uxn:op-special r uxn:op-inc r uxn:op-pop r uxn:op-nip r uxn:op-swp r uxn:op-rot r uxn:op-dup r uxn:op-ovr r uxn:op-equ r uxn:op-neq r uxn:op-gth r uxn:op-lth r uxn:op-jmp r uxn:op-jcn r uxn:op-jsr r uxn:op-sth r uxn:op-ldz r uxn:op-stz r uxn:op-ldr r uxn:op-str r uxn:op-lda r uxn:op-sta r uxn:op-dei r uxn:op-deo r uxn:op-add r uxn:op-sub r uxn:op-mul r uxn:op-div r uxn:op-and r uxn:op-ora r uxn:op-eor r uxn:op-sft : uxn:op-special i lilist.. d 0 r uxn:keep i lifelisr r uxn:opcode d 5 i liadfeju r uxn:imm-table : uxn:op-inc i lica.... r uxn:pop-value i liadliju d 1 r uxn:push-value : uxn:op-pop i lica.... r uxn:pop-value i drre.... : uxn:op-nip c guest (a b -- b) i lica.... r uxn:pop-value i lica.... r uxn:pop-value i drliju.. r uxn:push-value : uxn:op-swp c guest (a b -- b a) i lica.... r uxn:pop-value i lica.... r uxn:pop-value i swlica.. r uxn:push-value i liju.... r uxn:push-value : uxn:op-rot c guest (a b c -- b c a) i lica.... r uxn:pop-value i lica.... r uxn:pop-value i lica.... r uxn:pop-value i pulica.. r uxn:push-value i lica.... r uxn:push-value i poliju.. r uxn:push-value : uxn:op-dup c guest (a -- a a) i lica.... r uxn:pop-value i dulica.. r uxn:push-value i liju.... r uxn:push-value : uxn:op-ovr c guest (a b -- a b a) i lica.... r uxn:pop-value i lica.... r uxn:pop-value i dupulica r uxn:push-value i lica.... r uxn:push-value i poliju.. r uxn:push-value : uxn:op-equ c guest (a b -- boolean-byte) i lica.... r uxn:pop-value i lica.... r uxn:pop-value i eqlian.. d 1 i liju.... r uxn:push-byte : uxn:op-neq c guest (a b -- boolean-byte) i lica.... r uxn:pop-value i lica.... r uxn:pop-value i nelian.. d 1 i liju.... r uxn:push-byte : uxn:op-gth i lica.... r uxn:pop-value i lica.... r uxn:pop-value i swgtlian d 1 i liju.... r uxn:push-byte : uxn:op-lth i lica.... r uxn:pop-value i lica.... r uxn:pop-value i swltlian d 1 i liju.... r uxn:push-byte : uxn:op-jmp i lica.... r uxn:pop-value i liju.... r uxn:move : uxn:op-jcn i lica.... r uxn:pop-value i lica.... r uxn:pop-byte i licj.... r uxn:move i drre.... : uxn:op-jsr i lica.... r uxn:pop-value i lica.... r uxn:other-stack i lifelica r uxn:pc r uxn:push-short i liju.... r uxn:move : uxn:move c (target --) i lifelicj r uxn:short r uxn:move-absolute i lica.... r uxn:signed-byte i lifead.. r uxn:pc : uxn:move-absolute c (target --) i lianlist d 65535 r uxn:pc i re...... : uxn:signed-byte c (byte -- signed-offset) i dulilt.. d 128 i licj.... r uxn:signed-positive i lisure.. d 256 : uxn:signed-positive i re...... : uxn:op-sth i lica.... r uxn:pop-value i lica.... r uxn:other-stack i liju.... r uxn:push-value : uxn:op-ldz c LDZ/STZ use address-mask so short accesses wrap within the c zero page. i lilist.. d 255 r uxn:address-mask i lica.... r uxn:pop-byte i lifean.. r uxn:address-mask i listliju r uxn:address r uxn:memory-load : uxn:op-stz i lilist.. d 255 r uxn:address-mask i lica.... r uxn:pop-byte i lifean.. r uxn:address-mask i listliju r uxn:address r uxn:memory-store : uxn:op-ldr i lilist.. d 65535 r uxn:address-mask i lica.... r uxn:pop-byte i lica.... r uxn:signed-byte i lifead.. r uxn:pc i lifean.. r uxn:address-mask i listliju r uxn:address r uxn:memory-load : uxn:op-str i lilist.. d 65535 r uxn:address-mask i lica.... r uxn:pop-byte i lica.... r uxn:signed-byte i lifead.. r uxn:pc i lifean.. r uxn:address-mask i listliju r uxn:address r uxn:memory-store : uxn:op-lda i lilist.. d 65535 r uxn:address-mask i lica.... r uxn:pop-byte i lica.... r uxn:pop-byte i lislor.. d 8 i lifean.. r uxn:address-mask i listliju r uxn:address r uxn:memory-load : uxn:op-sta i lilist.. d 65535 r uxn:address-mask i lica.... r uxn:pop-byte i lica.... r uxn:pop-byte i lislor.. d 8 i lifean.. r uxn:address-mask i listliju r uxn:address r uxn:memory-store : uxn:memory-load i lifelica r uxn:address r uxn:read-byte i lifelicj r uxn:short r uxn:memory-load-short i liju.... r uxn:push-byte : uxn:memory-load-short c (high-byte --) i lisllife d 8 r uxn:address i liadlife d 1 r uxn:address-mask i anlica.. r uxn:read-byte i orliju.. r uxn:push-short : uxn:memory-store i lica.... r uxn:pop-value i lifelicj r uxn:short r uxn:memory-store-short i lifeliju r uxn:address r uxn:write-byte : uxn:memory-store-short c (short --) i dulisr.. d 8 i lifelica r uxn:address r uxn:write-byte i lifeliad r uxn:address d 1 i lifean.. r uxn:address-mask i liju.... r uxn:write-byte : uxn:op-dei i lica.... r uxn:pop-byte i listlife r uxn:address r uxn:address i lica.... r uxn:device-read i lifelicj r uxn:short r uxn:dei-short i liju.... r uxn:push-byte : uxn:dei-short c (high-byte --) i lisllife d 8 r uxn:address i liadlica d 1 r uxn:device-read i orliju.. r uxn:push-short : uxn:op-deo i lica.... r uxn:pop-byte i listlica r uxn:address r uxn:pop-value i lifelicj r uxn:short r uxn:deo-short i lifeliju r uxn:address r uxn:device-write : uxn:deo-short c (short --) i dulisrli d 8 r uxn:address i felica.. r uxn:device-store i lifeliad r uxn:address d 1 i liju.... r uxn:device-write : uxn:op-add i lica.... r uxn:pop-value i lica.... r uxn:pop-value i adliju.. r uxn:push-value : uxn:op-sub i lica.... r uxn:pop-value i lica.... r uxn:pop-value i swsuliju r uxn:push-value : uxn:op-and i lica.... r uxn:pop-value i lica.... r uxn:pop-value i anliju.. r uxn:push-value : uxn:op-ora i lica.... r uxn:pop-value i lica.... r uxn:pop-value i orliju.. r uxn:push-value : uxn:op-eor i lica.... r uxn:pop-value i lica.... r uxn:pop-value i xoliju.. r uxn:push-value : uxn:op-mul c Split one operand into bytes to keep both partial products c below 2^31. Only the low 16 product bits are needed. Restore c address-stack temporaries before calling push-value. i lica.... r uxn:pop-value i lica.... r uxn:pop-value i dulianpu d 255 i lisrswdu d 8 i pumulian d 255 i lislpopo d 8 i muadlian d 65535 i liju.... r uxn:push-value : uxn:op-div i lica.... r uxn:pop-value i lica.... r uxn:pop-value i swdulieq d 0 i licj.... r uxn:divide-zero i diswdr.. i liju.... r uxn:push-value : uxn:divide-zero c (dividend divisor --) i drdrli.. d 0 i liju.... r uxn:push-value : uxn:op-sft i lica.... r uxn:pop-byte i lica.... r uxn:pop-value i swdulisr d 4 i puliansr d 15 i poslliju r uxn:push-value : uxn:imm-table c Base-zero opcodes bypass normal keep semantics. r uxn:imm-brk r uxn:imm-jci r uxn:imm-jmi r uxn:imm-jsi r uxn:imm-lit r uxn:imm-lit2 r uxn:imm-litr r uxn:imm-lit2r : uxn:imm-brk i lilistre d 0 r uxn:running : uxn:imm-jci i lica.... r uxn:pop-byte i lica.... r uxn:immediate-offset i swlicj.. r uxn:immediate-move i drre.... : uxn:imm-jmi i lica.... r uxn:immediate-offset i liju.... r uxn:immediate-move : uxn:imm-jsi i lica.... r uxn:immediate-offset i lilist.. r uxn:rsp r uxn:ptr-address i lilist.. r uxn:return-stack r uxn:stack-base i lifelica r uxn:pc r uxn:push-short i liju.... r uxn:immediate-move : uxn:immediate-offset c (-- offset) i lica.... r uxn:fetch i lisllica d 8 r uxn:fetch i orre.... : uxn:immediate-move c The post-operand PC is the base for immediate branches. c (offset --) i lifeadli r uxn:pc d 65535 i anlistre r uxn:pc : uxn:imm-lit i lica.... r uxn:fetch i liju.... r uxn:push-byte : uxn:imm-lit2 i lica.... r uxn:fetch i lica.... r uxn:push-byte i lica.... r uxn:fetch i liju.... r uxn:push-byte : uxn:imm-litr i lica.... r uxn:fetch i liju.... r uxn:push-byte : uxn:imm-lit2r i lica.... r uxn:fetch i lica.... r uxn:push-byte i lica.... r uxn:fetch i liju.... r uxn:push-byte : uxn:code-end c Debug marker for the end of code and dispatch tables. : uxn:working-stack * 256 : uxn:return-stack * 256 : uxn:devices * 256 : uxn:state c State and scratch cells start here : uxn:pc d 256 : uxn:wsp d 0 : uxn:rsp d 0 : uxn:opcode d 0 : uxn:short d 0 : uxn:keep d 0 : uxn:read-ptr d 0 : uxn:stack-base d 0 : uxn:address d 0 : uxn:value d 0 : uxn:block d 0 : uxn:console-vector d 0 : uxn:exit-status d 0 : uxn:ptr-address d 0 : uxn:running d 0 : uxn:address-mask d 0 : uxn:stderr-count d 0 : uxn:stderr-block d 64 : uxn:block-buffer * 1024 : uxn:ram ~~~