Uxn - Virtual Computer

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

just updated the online tutorial to reflect this — i wasn’t aware the change had happened for sure!
thank you and sorry @A_Cormorant! i hope you enjoy what comes next :grin:

2 Likes

Is there any convention around clearing the screen every frame?

I noticed that none of the uxntal I’ve seen does a full redraw every frame (except in special cases). Instead, for moving sprites people do the pattern “clear sprite, update, draw new sprite.” Is it un-uxn to redraw everything every frame?

Is it un-uxn to redraw everything every frame?

It’s totally fine to do it, there’s a few apps that do fullscreen clearing, like Turye, but not through the screen vector. Just don’t do it pixel-per-pixel, depending on your program, you can do 16 tiles vertically at once, and wipe horizontal sections of the screen. That should be fast enough for the DS and GBA to handle.

Left does a 8 tiles high fast wipe that you could inspire yourself from. Start by saving the screen size to wipe, at the beginning of your program, so you don’t recalculate each frame:

.Screen/height DEI2 #06 SFT2 NIP INC ;draw-textarea/height STA
.Screen/width DEI2 #03 SFT2 NIP ;draw-textarea/width STA

Then you’re good to clear each frame

( fast clear )
	#71 .Screen/auto DEO
	#0000 .Screen/y DEO2
	[ LIT &height $1 ] #00
	&h
		#0000 .Screen/x DEO2
		[ LIT &width $1 ] #00
		&w
			#00 .Screen/sprite DEO
			INC GTHk ,&w JCN
		POP2
		.Screen/y DEI2k #0040 ADD2 ROT DEO2
		INC GTHk ,&h JCN
	POP2
1 Like

Thank you very much!