add description and use of screen/auto

This commit is contained in:
sejo 2022-04-22 12:50:12 -05:00
parent 772a96eda0
commit 66c71b0ecd
3 changed files with 148 additions and 160 deletions

View File

@ -29,7 +29,9 @@ implement the following concepts as {coloring computers}:
# uxn
* update instructions for running uxnemu on {uxn tutorial day 1}
* include screen/auto byte description on {uxn tutorial day 6}
## traducción:
* agrega las indicaciones sobre el uso de screen/auto en {uxn tutorial day 6}
# wiki

View File

@ -209,28 +209,30 @@ as a recap: we mentioned that the screen device can only show four different col
now let's discuss the screen device and start using it!
## inputs and outputs
## device ports
in uxntal programs for the varvara computer you will be able to find the labels corresponding to this device as follows:
in uxntal programs for the varvara computer you will be able to find the labels corresponding to this device ports as follows:
```
|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 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]
```
the inputs that we can read from this device are:
these are the ports in list format:
* vector (2 bytes)
* width of the screen (2 bytes)
* height of the screen (2 bytes)
and the output ports of this device are:
* auto (1 byte)
* x coordinate (2 bytes)
* y coordinate (2 bytes)
* memory address (2 bytes)
* pixel (1 byte)
* sprite (1 byte)
we will talk about the vector short on {uxn tutorial day 4}, and about the auto byte on {uxn tutorial day 6}.
we will cover the rest of the ports today!
## foreground and background
the screen device has two overlayed layers of the same size, the foreground and the background.

View File

@ -4,7 +4,7 @@ this is the sixth section of the {uxn tutorial}! here we talk about how we can i
we base our discussion in a recreation of the classic pong game.
besides using previous strategies and snippets of code, we cover strategies for drawing and controlling multi-tile sprites, and for checking collisions.
besides using previous strategies and snippets of code, we cover strategies for drawing and controlling multi-tile sprites with the screen auto byte, and for checking collisions.
# general logic
@ -16,6 +16,8 @@ we will tackle the following elements in order:
* drawing and movement of the paddles
* drawing and bouncing of the ball
before discussing the paddles and the ball we will cover the screen auto byte that we mentioned on {uxn tutorial day 2}, as it will help us in the drawing of these elements.
# drawing the background: repeating a tile
in {uxn tutorial day 5} we discussed a way of creating a loop in order to repeat a 1bpp tile multiple times in a row.
@ -324,6 +326,95 @@ in {uxn tutorial appendix a} you can find a detailed discussion of how to genera
it goes into several possibilities for using uxntal in that abstract way: i'd say it's very interesting, but it is definitely out of scope for making the game :)
# the screen auto byte
the screen auto byte is an advanced feature of the screen device that allows us to easily draw multiple sprites with a single call.
we didn't cover it on {uxn tutorial day 2} to avoid adding more complexity, and also because it really shines when using the keep mode that we just discussed on {uxn tutorial day 5}.
## fields
the left nibble of the byte corresponds to the auto byte length.
the remaining four bits, numbered from right to left and from 0 to 3, are flags that indicate the following:
+ <table>
+ <tr><th>bit</th><th>flag</th></tr>
+ <tr><td class="num">3</td><td>unused</td></tr>
+ <tr><td class="num">2</td><td>auto address</td></tr>
+ <tr><td class="num">1</td><td>auto y</td></tr>
+ <tr><td class="num">0</td><td>auto x</td></tr>
+ </table>
& * 3: unused
& * 2: auto address
& * 1: auto y
& * 0: auto x
## incrementing position
the first application of the screen auto byte is to automatically increment the x or y coordinates of the screen device after drawing a sprite.
### without auto byte
for example, without the auto byte, if we wanted to draw a sprite just at the right of another, we would do something like the following, reading the screen x coordinate, incrementing it, and then saving it again:
```
;square .Screen/addr DEO2 ( set sprite address )
#41 .Screen/sprite DEO ( draw sprite )
( increment screen/x by 8 pixels: )
.Screen/x DEI2 #0008 ADD2 .Screen/x
#41 .Screen/sprite DEO ( draw sprite again )
```
the operation to increment the screen x is straightforward, but it's so common that the auto byte internalizes it.
### with auto byte
to replicate the same behavior, we can turn on the auto x flag, and instead do:
```
;square .Screen/addr DEO2 ( set sprite address )
( set auto byte: auto x )
#01 .Screen/auto
#41 .Screen/sprite DEO ( draw sprite )
#41 .Screen/sprite DEO ( draw sprite again )
```
if we hadn't set up the auto byte, the last two lines would be redundant. however, now that we included it, each of them draws a tile in a differet position.
### auto byte and keep mode
notice how in the previous code we are pushing twice into the stack the color and the screen sprite port address.
with the keep mode we can use those values but without removing them from the stack!
therefore, the last two lines of the previous snipped could be written instead using DEOk:
```
#41 .Screen/sprite DEOk ( draw sprite and keep values )
DEO ( draw sprite again )
```
## incrementing address
as we will see with the paddles and the ball of the game, in many times it makes sense to draw sprites consisting of several tiles.
additionally, it makes sense to store these tiles contiguosly in memory, as we have been doing before.
the auto address flag will allow us to increment the screen address short so that it points to the next address in memory of the tile that was just drawn.
if what we drew was a 1bpp tile, the address will be incremented by 8 bytes, and if we drew a 2bpp tile the address will be incremented by 16 bytes.
## multiple sprites
finally, the length field of the auto byte will allow us to set how many extra sprites, besides the original one, we want to draw in a row.
to reiterate, with the paddles and the ball below we will see how all these fields can be applied to draw multi-tile sprites!
# the paddles
we can think of the two paddles of the game as two rectangles, each one with its own x and y coordinates, and both with the same width and height.
@ -390,18 +481,7 @@ on one hand this second version would allow us to change colors when e.g. hittin
in principle the subroutine should be straightforward: we have to set the x and y coordinates of each of the tiles, relative to the given x and y coordinates, and draw them with the given color.
there are many ways to do it, depending on taste.
we could for example draw the tiles in the following order, with the following operations:
* draw tile 0, then add 8 to x
* draw tile 1, then add 8 to y
* draw tile 3, then subtract 8 to x
* draw tile 2, then add 8 to y
* draw tile 4, then add 8 to x
* draw tile 5
or we could do it more traditionally:
if we didn't want to use the screen auto byte, we could do something like the following:
* draw tile 0, then add 8 to x
* draw tile 1, then subtract 8 to x, and add 8 to y
@ -410,13 +490,9 @@ or we could do it more traditionally:
* draw tile 4, then add 8 to x
* draw tile 5
instead of subtracting we could recover x from the return stack, or from a relative variable.
however, as that approach or similar ones would imply a lot of operations, it makes sense to use the auto byte.
a possible advantage of going in order is that we can increment the address of the sprite by 10 (16 in decimal) to get to the address of the next tile. for this, and/or for the changes in coordinates, we can take advantage of the screen auto byte.
however, in this case i'll go for the first approach, and i'll manually set the address for each tile.
additionally, i'll save the color in the return stack:
for this, we can configure it so that we draw two tiles in a row, automatically incrementing the screen address, and also automatically incrementing the screen y coordinate after being done with a row:
```
@draw-paddle ( x^ y^ color -- )
@ -427,52 +503,25 @@ additionally, i'll save the color in the return stack:
.Screen/y DEO2
.Screen/x DEO2
( draw tile 0 )
;paddle/tile0 .Screen/addr DEO2
( copy color from return stack: )
STHkr .Screen/sprite DEO
( set first sprite address )
;paddle-sprite .Screen/addr DEO2
( add 8 to x: )
.Screen/x DEI2 #0008 ADD2 .Screen/x DEO2
( auto byte: length: +1 sprite )
( set auto addr and auto y )
#16 .Screen/auto DEO
( draw tile 1 )
;paddle/tile1 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( add 8 to y: )
.Screen/y DEI2 #0008 ADD2 .Screen/y DEO2
( draw tile 3 )
;paddle/tile3 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( sub 8 to x: )
.Screen/x DEI2 #0008 SUB2 .Screen/x DEO2
( draw tile 2 )
;paddle/tile2 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( add 8 to y: )
.Screen/y DEI2 #0008 ADD2 .Screen/y DEO2
( draw tile 4 )
;paddle/tile4 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( add 8 to x: )
.Screen/x DEI2 #0008 ADD2 .Screen/x DEO2
( draw tile 5 )
;paddle/tile5 .Screen/addr DEO2
( get and don't keep color from return stack: )
STHr .Screen/sprite DEO
( get color from return stack: )
STHr
( draw three rows: )
.Screen/sprite DEOk DEOk DEO
RTN
```
that's it!
now we can call it in e.g. the following way and get our paddle drawn:
note the use of the keep mode at the end: DEOk will output the color to the screen sprite port, but it will keep both of these parameters in the stack so that we can re-use them!
now that the subroutine is ready, we can call it in e.g. the following way and get our paddle drawn:
```
#0008 #0008 #c5 ;draw-paddle JSR2
@ -480,16 +529,6 @@ now we can call it in e.g. the following way and get our paddle drawn:
=> ./img/screenshot_uxn-pong-paddle.png screenshot of the paddle drawn over the background
it would be up to discussion if there are more efficient ways of drawing it. for example, we could have a generalized draw-sprite that receives the initial address of a set of tiles, and the width and height in terms of number of tiles:
```
@draw-sprite ( x^ y^ width height addr* color )
```
creating that could be a good exercise for you to try! in this case i'll just stay with the manual approach.
the good thing of this process being in a subroutine is that we can "forget" about its inner working and just use it :)
## variables and constants for the paddles
let's reserve some space in the zero page for the x and y coordinates of each paddle.
@ -804,34 +843,22 @@ let's have the subroutine receive the color as an argument, so that we can clear
.ball/x LDZ2 .Screen/x DEO2
.ball/y LDZ2 .Screen/y DEO2
( draw tile 0 )
;ball-sprite/tile0 .Screen/addr DEO2
( color byte was in the stack already )
DUP .Screen/sprite DEO
( set sprite address )
;ball-sprite .Screen/addr DEO2
( move right )
.Screen/x DEI2 #0008 ADD2 .Screen/x DEO2
( auto byte: draw +1 sprite )
( set auto addr and auto y )
#16 .Screen/auto DEO
( draw tile 1 )
;ball-sprite/tile1 .Screen/addr DEO2
DUP .Screen/sprite DEO
( move down )
.Screen/y DEI2 #0008 ADD2 .Screen/y DEO2
( draw tile 3 )
;ball-sprite/tile3 .Screen/addr DEO2
DUP .Screen/sprite DEO
( move left )
.Screen/x DEI2 #0008 SUB2 .Screen/x DEO2
( draw tile 2 )
;ball-sprite/tile2 .Screen/addr DEO2
.Screen/sprite DEO
( set color from working stack: )
.Screen/sprite
( draw two rows: )
DEOk DEO
RTN
```
notice how we are using a very similar approach to the draw-paddle subroutine!
in order to draw it, we'd just need to do:
```
@ -1508,31 +1535,18 @@ RTN
.ball/x LDZ2 .Screen/x DEO2
.ball/y LDZ2 .Screen/y DEO2
( draw tile 0 )
;ball-sprite/tile0 .Screen/addr DEO2
( color byte was in the stack already )
DUP .Screen/sprite DEO
( move right )
.Screen/x DEI2 #0008 ADD2 .Screen/x DEO2
( set sprite address )
;ball-sprite .Screen/addr DEO2
( draw tile 1 )
;ball-sprite/tile1 .Screen/addr DEO2
DUP .Screen/sprite DEO
( auto byte: draw +1 sprite )
( set auto addr and auto y )
#16 .Screen/auto DEO
( move down )
.Screen/y DEI2 #0008 ADD2 .Screen/y DEO2
( draw tile 3 )
;ball-sprite/tile3 .Screen/addr DEO2
DUP .Screen/sprite DEO
( move left )
.Screen/x DEI2 #0008 SUB2 .Screen/x DEO2
( draw tile 2 )
;ball-sprite/tile2 .Screen/addr DEO2
.Screen/sprite DEO
( set color from working stack: )
.Screen/sprite
( draw two rows: )
DEOk DEO
RTN
```
@ -1589,47 +1603,17 @@ RTN
( set initial y and x )
.Screen/y DEO2
.Screen/x DEO2
( set sprite address )
;paddle-sprite .Screen/addr DEO2
( draw tile 0 )
;paddle-sprite/tile0 .Screen/addr DEO2
( copy color from return stack: )
STHkr .Screen/sprite DEO
( auto byte: length: +1 sprite )
( set auto addr and auto y )
#16 .Screen/auto DEO
( add 8 to x: )
.Screen/x DEI2 #0008 ADD2 .Screen/x DEO2
( draw tile 1 )
;paddle-sprite/tile1 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( add 8 to y: )
.Screen/y DEI2 #0008 ADD2 .Screen/y DEO2
( draw tile 3 )
;paddle-sprite/tile3 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( sub 8 to x: )
.Screen/x DEI2 #0008 SUB2 .Screen/x DEO2
( draw tile 2 )
;paddle-sprite/tile2 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( add 8 to y: )
.Screen/y DEI2 #0008 ADD2 .Screen/y DEO2
( draw tile 4 )
;paddle-sprite/tile4 .Screen/addr DEO2
STHkr .Screen/sprite DEO
( add 8 to x: )
.Screen/x DEI2 #0008 ADD2 .Screen/x DEO2
( draw tile 5 )
;paddle-sprite/tile5 .Screen/addr DEO2
( get and don't keep color from return stack: )
STHr .Screen/sprite DEO
( get color from return stack: )
STHr .Screen/sprite
( draw three rows: )
DEOk DEOk DEO
RTN
```