revision day 4

This commit is contained in:
sejo 2022-01-03 18:58:35 -06:00
parent 66bad031a4
commit 9fe7de2737
2 changed files with 101 additions and 57 deletions

View File

@ -151,11 +151,11 @@ you can find a more detailed reference of all the opcodes in the uxntal opcode m
## day 4
* LDA: load and push down into the stack the value at the given absolute address ( address -- value )
* STA: store into the given absolute address the value at the top of the stack ( value address -- )
* STA: store into the given absolute address the given value ( value address -- )
* LDZ: load and push down into the stack the value at the given zero page address ( address -- value )
* STZ: store into the given zero page address the value at the top of the stack ( value address -- )
* STZ: store into the given zero page address the given value ( value address -- )
* LDR: load and push down into the stack the value at the given relative address ( address -- value )
* STR: store into the given relative address the value at the top of the stack ( value address -- )
* STR: store into the given relative address the given value ( value address -- )
## day 5

View File

@ -4,17 +4,19 @@ this is the fourth section of the {uxn tutorial}!
here we discuss the animation loop of the varvara computer, via its screen device vector.
we also talk about using the program memory as a space for data via "variables", in order to have some persistency of data during the runtime of our programs, and/or in order to save us from complex stack wrangling :)
we also talk about using the program memory as a space for data using "variables". this allows us to save and retrieve data during the runtime of our programs, and can save us from complex stack wrangling :)
# the screen vector
we discussed the varvara screen device on day 2, but we skipped its vector port in order to focus in how to draw with it.
we discussed the varvara screen device on day 2, but we skipped its vector port in order to focus in how to draw with it:
```
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
```
now that we have covered devices vectors on day 3, let's jump right in into how to use the one in the screen!
now that we have the concept of vectors of devices on {uxn tutorial day 3}, let's jump right in into how to use the one in the screen!
## assignment
the following line of uxntal would assign the absolute address of the label on-frame to the screen vector:
@ -22,18 +24,18 @@ the following line of uxntal would assign the absolute address of the label on-f
;on-frame .Screen/vector DEO2
```
uxn will jump to that label at a rate of 60 times per second: we can use the subroutine under on-frame to change the contents of the screen, generating animation, and/or we can also use it for other timing-related purposes.
uxn will jump to the location of at label at a rate of 60 times per second: we can use the subroutine under on-frame to change the contents of the screen, generating animation, and/or we can also use it for other timing-related purposes.
## a line that grows
the following program demonstrates a basic but powerful use of the screen vector: on each frame, it draws a pixel at the given screen x,y coordinates, and it adds 1 to the value of Screen/x:
the following program demonstrates a basic but powerful use of the screen vector: on each frame, it draws a pixel at the given screen x,y coordinates, and it adds 1 to the value of the x coordinate:
```
( hello-line.tal )
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
( init )
|0100
@ -59,11 +61,15 @@ BRK
BRK
```
note that the code is very similar to the one we wrote on day 2 for drawing a line. in that one, we manually incremented the value of Screen/x in order to draw 6 pixels.
note that the code is very similar to the one we wrote on day 2 for drawing a line.
here, the same code for incrementing Screen/x is called instead inside the on-frame subroutine, having it occurring 60 times per second.
in that one, we manually incremented the value of Screen/x in order to draw 6 pixels.
some change for you to try and practice:
here, the code for incrementing Screen/x is called inside the on-frame subroutine instead, having it occurring 60 times per second.
## possibilities for growth
these are some changes for you to try and practice:
* how would you make the line grow vertically? and diagonally?
* how would you make the line grow in the opposite direction?
@ -73,7 +79,9 @@ some change for you to try and practice:
# variables
the varvara screen vector opens up a whole world of possibilities! it's worth noting that many of these possibilities require ways of storing and retrieving data between frames.
the varvara screen vector opens up a whole world of possibilities!
it's worth noting that many of these possibilities require ways of storing and retrieving data between frames.
in the previous example, we are using the screen ports for the x and y coordinates as a way of storing the coordinates for the pixel.
@ -109,7 +117,7 @@ or if we didn't want to initialize them here, we could define them as follows:
@pixel-y $2
```
remember that $2 creates a relative pad of two byes, such that pixel-y is a label for an address in memory two bytes after pixel-x.
remember that $2 creates a relative pad of two byes: this makes pixel-y a label for an address in memory two bytes after pixel-x. and whatever code afterwards will happen two bytes after pixel-y.
we could also use labels and sublabels, in a manner that would be very similar to how we define devices and their ports:
@ -121,10 +129,10 @@ we could also use labels and sublabels, in a manner that would be very similar t
how could we read (load) and write (store) the contents of the memory at those labels?
here are the two instructions that would help us with that:
here are the two instructions that would help us:
* LDA: load and push down into the stack the value at the given absolute address ( address -- value )
* STA: store into the given absolute address the value at the top of the stack ( value address -- )
* STA: store into the given absolute address the given value ( value address -- )
as we have discussed already, an absolute address will always be two bytes long.
@ -160,29 +168,40 @@ BRK
@pixel [ &x $2 &y $2 ]
```
we could have achieved the same result by storing the result first, then re-loading it and sending it as an output.
note that we could have achieved the same result by storing the result, and then re-loading it and sending it as an output.
here we can see how a DUP2 can make that operation easier, as long as we keep a mental (or tangible!) model of what is happening on the stack.
### initial values
a possible advantage of using absolute addresses is that we can initialize the contents of our variables at assembly time, e.g.:
```
@pixel [ &x 0008 &y 0008 ]
```
these initial contents will change whenever we use a STA instruction there :)
## variables in the zero-page
variables with absolute address work well for cases where we want to be able to access their contents from any part of our program (i.e. "global variables").
however, uxn has a better mechanism for those cases: the zero page!
as you may recall, the zero page consists of the first 256 addresses of program memory. a program starts at address 0100, that is the next address after the zero page.
as you may recall, the zero page consists of the first 256 addresses of program memory. normally, a program starts at address 0100, that is the next address after the zero page.
we can refer to any of the 256 addresses of the zero page using one byte only, instead of the two bytes that are needed for absolute addresses.
the only caveat to keep in mind is that in order to initialize these variables we need to do it during runtime.
something importat to keep in mind is that the contents of the zero page are not present in uxn roms.
### labels
this means that a caveat of using variables there, is that in order to initialize them we need to do it during runtime, by storing values from the stack into them.
### labels in the zero page
labels for the zero page would work the same as before; we only need to specify that they are in the zero page with an absolute pad:
```
|0000 ( zero page )
@pixel [ &x $2 &y $2 ]
```
@ -194,7 +213,7 @@ in order to refer to them, we would use the dot (.) rune for literal zero page a
the instructions for loading (reading) and storing (writing) from and to the zero page are:
* LDZ: load and push down into the stack the value at the given zero page address ( address -- value )
* STZ: store into the given zero page address the value at the top of the stack ( value address -- )
* STZ: store into the given zero page address the given value ( value address -- )
in these instructions, the address will always be one byte long.
@ -227,6 +246,7 @@ in this case the program is longer, but it could be seen as a nice template for
( set initial x,y coordinates )
#0008 .pixel/x STZ2
#0008 .pixel/y STZ2
( set screen vector )
;on-frame .Screen/vector DEO2
BRK
@ -244,17 +264,23 @@ BRK
BRK
```
as an example to practice with the stack, note that the following instructions would also increment .pixel/x, but using its address only once:
note the use of the literal zero page address rune (.) to refer to the .pixel label.
also, note that in the case of .pixel the address is referring to the zero page, accessed with LDZ/STZ, and in the case of .Screen the address is referring to the i/o address space, accessed with DEO/DEI.
### a little stack wrangling practice
note that the following instructions would also increment .pixel/x, but setting its address only once:
```
.pixel/x DUP LDZ2 INC2 ROT STZ2
```
i recommend you to follow how the stack contents change in each of the steps :)
i recommend you to follow how the stack contents change in each of the steps: keep in mind that some of the instructions are in short mode :)
this line of code contains the same amount of bytes than the previous one.
a possible disadvantage is that it might be less readable. and a possible advantage is that it could get converted to a macro:
a possible disadvantage is that it might be less readable. but a possible advantage is that it could get converted into a macro:
```
( increment a short from the zero-page )
@ -274,7 +300,7 @@ however, as these addresses are given as relative and signed offsets, they can o
the instructions for working in this way are:
* LDR: load and push down into the stack the value at the given relative address ( address -- value )
* STR: store into the given relative address the value at the top of the stack ( value address -- )
* STR: store into the given relative address the given value ( value address -- )
similar to LDZ and STZ, in these instructions the address will always be one byte long.
@ -334,23 +360,29 @@ the use of "variables" will help us now in discussing three different ways of an
* interactive change of position (with keyboard)
* autonomous change of drawn tile
we will review them separately in order to keep the examples simple and readable. it will be up to you to combine and/or improve upon them :)
we will review them separately in order to keep the examples relatively simple and readable.
note that these examples also serve as a way of discussing more uxntal programming possibilities, and they might be a litle bit overwhelming.
i recommend you to review and experiment with one at a time, patiently :)
## autonomous change of position
we discussed already how to have uxn change the position of a pixel in the screen, leaving a trail.
changing that program in order to draw an 8x8 sprite instead would be relatively straightforward, and you may have tried it already: we would need to use Screen/sprite instead of Screen/pixel, with an appropriate mode byte, and we would need to set the address of our sprite data in Screen/addr.
changing that program in order to draw an 8x8 sprite instead would be relatively straightforward, and you may have tried it already: we would need to use Screen/sprite instead of Screen/pixel, with an appropriate byte to define its color and orientation, and we would need to set the address of our sprite data in Screen/addr.
that would result in a sprite that moves and that also leaves a trail.
that would result in a sprite that moves and that also leaves a trail: i invite you to try it first!
how can we avoid leaving that trail?
### without a trail
ok, that can be useful in some instances, but how can we avoid leaving the trail?
a possible way of achieving it would be by following this order of operations inside the on-frame subroutine:
* clear sprite
* clear previously drawn sprite
* change position
* draw sprite
* draw new sprite
this allows us to clear the sprite from its position in the previous frame, update its coordinates to a new position, and then draw it there.
@ -365,7 +397,7 @@ it combines several things that we have covered in the past few days!
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
( macros/constants )
%HALF2 { #01 SFT2 } ( shift one bit to the right ) ( short -- short/2 )
@ -414,7 +446,11 @@ BRK
@square ff81 8181 8181 81ff
```
note that on one hand there are some things that could be optimized in order to make our program smaller, and that on the other there are some things that could be useful but that were left out, like the initial value of the x coordinate, or the use of the y coordinate.
nice, isn't it? :)
as this is just an example to illustrate a point, there are some things that could be optimized in order to make our program smaller, and there are some things that could be useful but were left out. for example, there's not an initial value for the x coordinate, or the y coordinate is not used.
### extra possibilities
regarding optimization, and as an example, section 2 and the first part of section 3 of on-frame could have been written as follows:
@ -430,7 +466,7 @@ regarding optimization, and as an example, section 2 and the first part of secti
.Screen/x DEO2
```
as always, it is up to us how we want to navigate between efficient code and readability :)
as always, it is up to us how we want to navigate between shorter code and readability :)
here are some questions for you to ponder and try:
@ -439,26 +475,28 @@ here are some questions for you to ponder and try:
## interactive change of position
when we use the controller vector, we are acting upon a change in the button(s) or key(s) that were pressed or released. this can be very useful for some applications.
when we use the controller vector, we are acting based on a change in the button(s) or key(s) that were pressed or released. this can be very useful for some applications.
but, how can we deal with doing a continuous action when a key is kept pressed?
in some operating systems, if we keep a key pressed, it fires the controller vector several times.
in some operating systems, if we keep a key pressed, it fires the controller vector several times, but not necessarily at the same rate as the screen vector!
however, this repetition might not allow for a smooth movement like what we can get if we check for the state of the controller inside the on-frame subroutine!
this repetition might not allow for a smooth movement like what we can get if we check for the state of the controller inside the on-frame subroutine!
### horizontally interactive square
the following program allows us to control the horizontal position of our square using the arrow keys.
=> ./img/screencap_uxn-moving-square.gif animated gif showing a square moving horizontally in the screen, apparently controlled by a human.
note the similarities between the previous program, and what we covered on day 3!
note the similarities between the previous program, and what we covered on {uxn tutorial day 3}!
```
( hello-moving-sprite.tal )
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|80 @Controller [ &vector $2 &button $1 &key $1 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|80 @Controller [ &vector $2 &button $1 &key $1 ]
( macros/constants )
%HALF2 { #01 SFT2 } ( shift one bit to the right ) ( short -- short/2 )
@ -604,7 +642,7 @@ for example, the following is a sequence of eight 1bpp sprites corresponding to
&frame7 c0 80 00 00 00 00 00 00
```
note that each frame consists in 8 bytes; that implies that there's an offset of 8 bytes between the addresses corresponding to each sublabel.
note that each frame consists in 8 bytes. that implies that there's an offset of 8 bytes between the addresses corresponding to each sublabel.
for example, the address of &frame1 would be 8 bytes more than the address of &frame0.
@ -618,7 +656,9 @@ how can we know the sprite address that we should be using at each frame?
one way of achieving it is having a "global variable" in the zero page that counts the frames of the program. also, we would need to have that count constrained in a range corresponding to the amount of frames in our animation.
we know already how to do the first part!
we know already how to do the first part, and kind of know how to do the second!
### load, increment and store frame count
in the zero page we declare the label for our framecount:
@ -631,8 +671,8 @@ in the zero page we declare the label for our framecount:
and in the on-frame subroutine we increment it:
```
( increment framecount )
.framecount LDZ INC .framecount STZ
( increment framecount )
.framecount LDZ INC .framecount STZ
```
note that we are using a single byte to count, so it will go from 0 to 255 in a little bit more than 4 seconds, and then restart when overflowing its count.
@ -670,6 +710,8 @@ we could define this fast modulo operation as a macro to make the code more read
%8MOD { #07 AND } ( byte -- byte%8 )
```
if this wasn't too clear for you, i recommend you look back to {uxn tutorial day 3}, into the discussion of logic operations.
## pointer arithmetic
how can we use that count to select the sprite for the animation frame that we want to show?
@ -708,9 +750,9 @@ after applying the modulo 8 to our framecount, we can multiply it times 8 to get
note that so far we have been working with bytes, and everything has been fine.
however, absolute addresses are actually shorts!
however, absolute addresses are shorts!
we need to convert our result to a short so that we can add it to the address of the animation data.
this means we need to convert our offset to a short so that we can add it to the address of the animation data.
one way of doing it is with this macro that adds a 00 before the top element in the stack:
@ -738,7 +780,7 @@ another way, less clear but kind of fun (and slightly shorter in program memory)
### adding the offset
adding this offset to the address of our animation is simple:
adding this offset to the address of our animation is comparatively simple:
```
.framecount LDZ ( load framecount )
@ -776,7 +818,7 @@ the clear sprite section is not actually needed in this case because of the colo
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
( macros/constants )
%HALF2 { #01 SFT2 } ( shift one bit to the right ) ( short -- short/2 )
@ -842,9 +884,11 @@ BRK
&frame7 c0 80 00 00 00 00 00 00
```
i invite you to draw the tile multiple times with different flipping modes, in order to create more interesting animations!
it wasn't as complicated, was it? :) this example includes many concepts that are worth studying, so i invite you to read it carefully!
or, better yet, design and use your own sprites!
for some fun possibilities, i invite you to draw the tile multiple times in different places and possibly with different flipping modes! that may generate more interesting animations!
or, even better, design and use your own sprites!
## slow down!
@ -890,7 +934,7 @@ this might also happen if you have an animation consisting of a number of frames
the easiest workaround for these issues would be to use a short-sized framecount that would only cause those overflow glitches at approximately every 18 minutes.
you'd have to adapt the program to work with that size of framecount - nice exercise, i feel-think!
you'd have to adapt the program to work with that size of framecount - nice exercise, i feel and think!
# instructions of day 4
@ -898,11 +942,11 @@ you'd have to adapt the program to work with that size of framecount - nice exer
these are all the uxntal instructions that we discussed today!
* LDA: load and push down into the stack the value at the given absolute address ( address -- value )
* STA: store into the given absolute address the value at the top of the stack ( value address -- )
* STA: store into the given absolute address the given value ( value address -- )
* LDZ: load and push down into the stack the value at the given zero page address ( address -- value )
* STZ: store into the given zero page address the value at the top of the stack ( value address -- )
* STZ: store into the given zero page address the given value ( value address -- )
* LDR: load and push down into the stack the value at the given relative address ( address -- value )
* STR: store into the given relative address the value at the top of the stack ( value address -- )
* STR: store into the given relative address the given value ( value address -- )
the addresses for LDA and STA are always shorts, while the addresses for the other instructions are always one byte.
@ -910,7 +954,7 @@ in short mode, these instructions load or store shorts from or into memory.
# day 5
in {uxn tutorial day 5} here we introduce the varvara mouse device to explore more possible interactions, and we cover the remaining elements of uxntal and uxn: the return stack, the return mode and the keep mode.
in {uxn tutorial day 5} we introduce the varvara mouse device to explore more possible interactions, and we cover the remaining elements of uxntal and uxn: the return stack, the return mode and the keep mode.
we also discuss possible structures to create loops and more complex programs using these resources!