Uxn - Virtual Computer

I automatically look for possible optimisation and errors with uxnlin, the linter ignores what is inside brackets, sometimes it’s valid hacky ways to do something and I want to keep it but mute the warnings, so I put them withing square brackets :slight_smile:

Exactly, it frees and allocate memory to windows, it’s still early in its development tho.

I prepare the arguments on the stack before jumping, I recommend that you look for the book Starting Forth, if you’re interested to know more about this way of thinking.

The return stack gives you a bit more options when juggling multiple items on the stack, you can put some stuff in it momentarily before returning from a subroutine, when you have like positions and colors and other things to sort out in the stack, being able to send one to the return stack is simplifying things a lot.

JMP2r is just JMP2, but on the return stack, think of it like this: if you put an address #1234 on the return stack, like LIT2 1234, and then JMP2r, it will jump to that address. When you JSR2, it puts your current location in the program to the return stack automatically.

Of course, that’s how most things work, that’s how loops are made.

Forth is a famous one that will get you there, I recommend again to look through Starting Forth, it’s an excellent book. If you come from the Lambda/Lisp world, I recommend that you come through Joy.

Uxntal is a mixture of Forth and 6502, it’s a kind of strange 8-bit hybrid of the languages. But knowing 6502 will only get you as far as understanding how memory works on small systems.

If you’re not familiar with how low-level computing works, you could have a look at CHIP-8, Gyo, or paper computers.

If you’re interested in learning forth, I recommend using UF, it’s an amazing graphical forth system for Varvara, it’s easy to setup, and it will let you mess around forth to your heart’s content. If you’d like to familiarize yourself with RPN notation, you could noodle around with CCCC.

Let me know if you need a hand with anything, I’ll be happy to help you figure these things out!

3 Likes

Ah, that’s a great starting point, thank you. I do better understand how Forth works, I think. I get how it thinks, anywho.
In an effort to create a foundation for myself where I could actually read my own Tal code (I find it difficult to follow basic instructions), I sought to translate my recently-acquired looping skills to legible instructions. However, I encountered a problem: it only loops once, and I don’t know why!
This is the code:


( Pardon the photograph, I’m not a seasoned linux user and I got frustrated trying to yank lines from Vim into my clipboard.)

My line of logic is as follows:

  1. At the beginning of the macro “routine”, my stack should look like this, assuming I have popped my working stack frame:
    • address (short)
    • iterator (byte)
    • iterator max (byte)
  2. since the iterator and max always travel together, I can treat them as a short while I’m shuffling things around. I swap the address and the M-I pair to bring the pair to the front to compare.
    • M-I pair (short)
    • address
  3. Comparing M and I results in a single byte. I need to shuffle things around, so I add a dummy byte to crate a “short”.
    • dummy (#00)
    • boolean
    • M-I pair
    • address
  4. JCN expects a boolean as the top argument, and an address as the second. I shuffle things around so the address is now second, and pop the dummy byte as I don’t need it anymore.
    • boolean (byte)
    • address (short)
    • M-I pair
  5. I JCN2 to the address if need be.

However, when I run the code, it only ever loops through once, and I don’t know why. I wrote a sanity check conventional loop, and it works fine.
My output is 01234567890, with the last 0 being the second loop.
How could this be?
Also, I am well aware that doing it this way adds way more instructions than need be, but I just wanted to see if I could actually do it.

Exactly, it frees and allocate memory to windows

  1. So when you “virtualize” the processes of the child programs, if you’re going to do that, this will act as a virtual memory for them? How are you going to track what memory is stored where, free, fetch, and allocate? Do you even know yet or are you winging it?
  2. The Uxn machine has 256 bytes of working stack, and 64 Kb of memory (I think). is the 64 Kb of memory for the actual bytes of the program, or just anything you use in general (and the program happens to start at 0x0100?) With a heap, I imagine that it will be dynamically growing and shrinking, somehow. Will it use this 64Kb? How will it know when it has run out (if heap.size > end of program - heap start?)

It doesn’t. It expected an address on top.

JCN cond8 addr – If the byte preceeding the address is not 00, moves the program counter by a signed value equal to the byte on the top of the stack, or an absolute address in short mode.

Yes, right now, if you open the About panel it displays how the memory is being used in its current order. When you close a window, it frees up that memory, so another application can use it.

Since the memory is so small, I don’t have to fracture the allocation like that, I can just shift everything after the closed window by that window’s allocation lenght, and move the pointers by that offset, it’s what my memory helpers do. It’s fast enough.

It’s everything. All the OS can do is give away bits of that 64kb space, it won’t extend Uxn’s working memory. Right now, if an allocation ends at a lower address than where it starts, it means it overflows, and a pop-up will say “Please close some windows” or something :slight_smile:

An aside,

Uxntal doesn’t use variables or registers like some other forth machines have, it doesn’t need it because it has a super-power that none of the others do, you can write bytes directly into the program, instead of juggling complicated stacks, you can instead write the value that you will need later, directly at the end of your routine inside a LIT.

Whenever you see something that looks like [ LIT &var $1 ], it means that I’m storing my variable directly in memory, it’s faster than doing a load, because it’s already ready to be read as a literal and put into the stack :slight_smile: It’s a good thing to remember when trying to juggle complicated stacks.

Also, here’s a vim syntax highlight for uxntal.

2 Likes

On the uxntal reference page I read the following description for JSR:

Pushes the value of the program counter to the return-stack […]

Is it possible to do this manually, without creating a new label? If so, I think it should be possible to create a combination macro of JCN and JSR, which might be pretty convenient.

It doesn’t. It expected an address on top.

okay, got it! The macro works, but I eschewed it in favor of just writing it normally because each loop call took like a dozen ops and .03% of memory or something like that.

If I have a zero-page variable that’s like @game state $8, is there a way I can LDZ and STZ all 8 bytes at once?
My goal for my training program is a simple conway’s game of life, and I was planning to store the cell states as 8 bytes of single bits to make an 8x8 grid, and use bit shifts to look at the neighbors (<<1, <<*, etc), but I can’t see how to load all the data in at once, or if I should go about it a different way. having labels for each row seems tedious.

Whenever you see something that looks like [ LIT &var $1 ], it means that I’m storing my variable directly in memory

What do you mean exactly? Is this a storage and definition simultaneously?

Yes, if you do #00 JSR, it will push the PC into the return stack :slight_smile:

Load all of them on the stack? well, you could do this to bring them all up into the return stack two at a time:

#0400 &l DUP .state ADD LDZ2 STH2 INC INC GTHk ,&l JCN POP2

But I wouldn’t load all of the game state on the stack at once, but operate 8 cells at a time. But try it out for yourself, see what works best.

Game of Life is a cellular Automata, the means you need 2 game states, you read one and write onto the next frame. The way to do this fast usually, is by simply changing the state pointers, from one frame to the other, without ever clearing the world.

It might be a bit too advanced for now, let’s keep this for later.

1 Like

I ended up making a somewhat verbose pseudo-linked list for each row. Now, how do I set a specific bit of a byte to be 0? I imagine it has to do with xor…

almost! to set a specific bit to 0 (in some contexts that’s called resetting the bit) you would use an AND “mask” with all 1s except for the target position.

the bits where the mask is 1 will be left as they are (0 AND 1 is 0, 1 AND 1 is 1) and the bit(s) where the mask is 0 will be forced to be 0 (0 AND 0 is 0, 1 AND 0 is 0)

e.g. to set the least significant bit to 0, you can do:

#fe AND ( reset the lsb )

EOR (XOR) would help you to toggle the bits where the “mask” is 1 (0 XOR 1 is 1, 1 XOR 1 is 0), and leave the other bits as they are (0 XOR 0 is 0, 1 XOR 0 is 1)

e.g. to toggle the least significant bit:

#01 EOR ( toggle the lsb )

and for the sake of completeness, ORA (OR) would allow you to set a specific bit to 1 (setting the bit, in some contexts), “forcing” it to be 1 where the mask is 1 (0 OR 1 is 1, 1 OR 1 is 1), leaving the other bits as they are (0 OR 0 is 0, 1 OR 0 is 1)

e.g. setting the least significant bit to 1:

#01 ORA ( set the lsb )
3 Likes

Ah nice. I remember seeing the “a clever hack” comment and not understanding what PC meant. I guess it didn’t occur to me to check if it was mentioned elsewhere in the article :sweat_smile: Maybe the comment can be changed to “a clever hack to get the current program counter” anyway?


I ended up with the following to do a conditional JSR2. Let me know if there is a better way!

@_?jsr2 ( <wst> bool addr-to-optionally-jump-to^  <rst> addr-to-eventually-definitely-return-to^ )
  ROT #00 EQU ,&skip JCN
  JMP2
  &skip POP2
RTN

%?JSR2 { #04 JSR ;_?jsr2 JMP2 }

Edit: I guess this…

  ROT #00 EQU ,&skip JCN
  JMP2
  &skip POP2

…can be replaced with this:

  JCN2

To experiment with Uxn’s bitwise operators, I recommend also trying bitwise.tal

Looks good to me :slight_smile: Looking forward to see how you make use of this.

1 Like

perhaps this is a backwards way of figuring this out, but let me see if I understand the following structures in higher level programming translation into uxn properly:

-- FOR
byte x = 0
for byte i, i < 8, i++ do
    x += i
end

&x $1
#00 ,&x STR
#08 #00
&loop_begin
    ,&x LDR
    ADDk
    NIP ( get rid of &x )
    ,&x STR ( store incremented value in x )
    INC
    GTHk ,&loop_begin JMP
POP2
-- WHILE
byte x
while x < 8 do
    x++
end

&x $1
#00 ,&x STR
#08
&while_begin
    ,&x LDR
    INC
    DUP
    ,&x STR
    GTHk POP ,&while_begin JMP ( Pop gets rid of x from DUP )

-- IF
byte x = 0
if x == 1 do
   x = 3
else
  x = 1
end

&x $1
#00 ,&x STR
,&x LDR
#01 EQU ,&if_true JMP
( if false )
#01 ,&x STR
&if_true
   #03 ,&x STR

-- FUNCTION
def my_func()
   return 3 + 1
end
...
my_func()

@my_func
    #03 #01 ADD ( i mean optimizer would evaluate this but you get the idea )
JMP2r
...
;my_func JSR2

-- FUNCTION W/ ARGUMENTS

def my_func(byte x, byte y)
    return x + y
end
...
byte x = 4
my_func(x, 6)

@my_func
    ADD
JMP2r
...
&x $1
#04 ,&x STR

,&x LDR
#06
;my_func JSR2

of course, these are simple examples, more complex stuff would be much harder. gotta keep the stack under control…

1 Like

That’s pretty much the best way to figure out uxntal! You’ve recreated all standard program structures in it, with this, you’ll be invincible!

Normally, for while loops, I use LDAk, like:

;word
@while
	LDAk #18 DEO
	INC2 LDAk ,while JCN
POP2
BRK
@word "vermillion $1

So if I understand this, this is like, a foreach loop? it loads the value and then increments the address, and then loads the next byte? and then the $1 is a null byte at the end, which counts as a false for the JCN boolean?

1 Like

Exactly :slight_smile:

Here’s a little something I’ve been toying around

9 Likes

I have a question.
The memory space of 10000h bytes, from 0000 to FFFF.
Is it an absolute requirement?
If implementing uxn on a device which itself has 10000h or less RAM available then this is not possible.
However most programs probably don’t need all memory.
Is there some convention used of how data is allocated in compiled programs? So that it would be safe to implement an uxn with less memory, skip some memory region, and expect at least some programs to work?

(I’m not actually considering implementing such a thing now. Just wondering.)

The convention is to use the most memory that you can, but like you said, most roms won’t be impacted by this at all. The convention is to use a page for the devices, but everything else is somewhat flexible.

For the arduino port, what I did is to use shallower stacks, so instead of a whole page, I use 80 bytes per stack, and only 1800 bytes of memory. It allows me to run most little roms.

I read about the fantastic live rom assembly/loading here:

So I created a .drifblim with src and dest path and run „uxnemu drifblim.rom“ but no success so far :frowning: How does it exactly work? Thanks in advance!

Heyo, just dipping my toes into uxn with the lovely tutorial from compudanzas.net and having some trouble with the raw character rune. uxnasm returns “Unknown token: 'h” on the following bit of code.

( hello.tal )
|0100 LIT 'h #18 DEO
      LIT 'e #18 DEO
      LIT 'l #18 DEO
      LIT 'l #18 DEO
      LIT 'o #18 DEO
      #0a #18 DEO ( newline )

If I change the single quote to double quotes it assembles just fine in uxnasm.

( hello.tal )
|0100 LIT "h #18 DEO
etc, etc.

Neither versions assemble in drifblim for me. the single quote version returns the same “Unknown token” error and the double quote version just returns an error with the name of the input file.

I’m trying to do this on Windows, so that is probably the root of the problem but any suggestions would be appreciated.

That’s a bit tricky, your drifblim file needs to look like this:

ss

I haven’t written an interface to the project mode, it only writes this file directly in the zero-page. If this is all too cryptic, you should use the normal interface:

uxncli drifblim.rom input.tal output.rom

Ah! Yes, the ' and " were exactly the same behaviour, so I’ve removed the ' since it was creating confusion. Whenever you see single quotes in docs, you should use double-quotes, sorry about that.

2 Likes

Ah, I’ve got it. I also needed to rename drifblim.rom to launcher.rom and run it with uxnemu launcher.rom. Now whenever I want to refresh the screen I have to press F4 and the source gets assembled again. Great! I really love this development flow :heart: Thanks!

1 Like