updated example sprite for 1bpp (thanks snufkinvc and evincarofautumn), color table for low nibble and other minor changes

This commit is contained in:
sejo 2024-03-16 21:02:45 +01:00
parent f729efed18
commit b733fa0e65
2 changed files with 90 additions and 84 deletions

View File

@ -47,6 +47,8 @@ implement the following concepts as {coloring computers}:
### further changes
* {uxn tutorial day 2}: divide into morning and evening
* {uxn tutorial day 2}: check sprite nibbles tables. include colors when colors are mentioned.
* new day: lambdas and more complex stuff (jumps, callings, etc)
* include uxn5 in the web tutorial so that code can be run from there
=> https://git.sr.ht/~rabbits/uxn5 uxn5

View File

@ -131,7 +131,7 @@ the DEO instruction needs a value (1 byte) to output, and an i/o address (1 byte
DEO ( value address -- )
```
this instuction has a counterpart: DEI (device in).
this instruction has a counterpart: DEI (device in).
the DEI instruction takes an i/o address (1 byte) from the stack, and it will push down onto the stack the value (1 byte) that corresponds to reading that input.
@ -506,23 +506,23 @@ we will be storing and accessing these tiles from the main memory.
# drawing 1bpp sprites
a 1bpp tile consists in a set of 8 bytes that encode the state of its 8x8 pixels.
a 1bpp tile consists of a set of 8 bytes that encodes the state of its 8x8 pixels.
each byte corresponds to a row of the tile, and each bit in a row corresponds to the state of a pixel from left to right: it can be either "on" (1) or "off" (0).
each byte corresponds to a row of the tile. each bit in a row, going from left to right, corresponds to the state of a pixel: it can be either "on" (1) or "off" (0).
## encoding a 1bpp sprite
for example, we could design a tile that corresponds to the outline of an 8x8 square, turning on or off its pixels accordingly.
for example, we could design a tile that corresponds to an arrow pointing downwards and to the left, within an 8x8 square, turning on or off its pixels accordingly. (if it doesn't look like an arrow yet, trust me for a little bit and you'll see)
``` the outline of a square marked with 1s, and its insides marked with 0s
11111111
10000001
10000001
10000001
10000001
10000001
10000001
11111111
``` an arrow pointing downwards and left, made with 1s, the negative space marked with 0s
00000001
00000010
00000100
10001000
11010000
11100000
11110000
11111000
```
as each of the rows is a byte, we can encode them as hexadecimal numbers instead of binary.
@ -531,28 +531,31 @@ it's worth noting (or remembering) that groups of four bits correspond to a nibb
based on that, we could encode our square as follows:
``` the outline of a square marked with 1s, and its insides marked with 0s, and its equivalent in hexadecimal
11111111: ff
10000001: 81
10000001: 81
10000001: 81
10000001: 81
10000001: 81
10000001: 81
11111111: ff
``` the same arrow as above, and its equivalent in hexadecimal
00000001: 01
00000010: 02
00000100: 04
10001000: 88
11010000: d0
11100000: e0
11110000: f0
11111000: f8
```
this process is illustrated by Rostiger's notes here, but using a square:
=> https://nchrs.xyz/uxn_notes.html nchrs: uxn notes
## storing the sprite
in uxntal, we need to label and write into main memory the data corresponding to the sprite. we write the bytes going from top to bottom of the sprite:
in uxntal, we need to label and write into main memory the data corresponding to the sprite. we write the bytes in order, from left to right, starting from the top and towards the bottom of the sprite:
```
@square ff81 8181 8181 81ff
@arrow [ 0102 0488 d0e0 f0f8 ]
```
note that we are not using the literal hex (#) rune here: we want to use the raw bytes in memory, and we don't need to push them down onto the stack.
note that we are not using the literal hex (#) rune here: we want to use the raw bytes in memory, and we don't need to push them down onto the stack. the square brackets are only used for visually grouping the code and are ignored by the assemblers.
to make sure that these bytes are not read as instructions by the uxn cpu, it's a good practice to precede them with the BRK instruction: this will interrupt the execution of the program before arriving here, leaving uxn in a state where it's waiting for inputs.
to make sure that these bytes are not read as instructions by the uxn cpu, it's a good practice to precede them with the BRK instruction. this is a new instruction for us! this instruction will interrupt the execution of the program before arriving to the data, leaving uxn in a state where it's waiting for inputs.
we'll see in a moment where to write this code.
@ -563,24 +566,26 @@ in order to draw the sprite, we need to send its address in memory to the screen
to achieve the former, we write the following:
```
;square .Screen/addr DEO2
;arrow .Screen/addr DEO2
```
a new rune is here! the literal absolute address rune (;) lets us push down onto the stack the absolute address of the given label in main memory.
an absolute address would be 2-bytes long, and is pushed down onto the stack with LIT2, included by the assembler when using this rune.
an absolute address is an address pointing to a location within the whole main memory space of 65536 bytes (from 0000 to ffff in hexadecimal). therefore, an absolute address is one short, i.e. it is 2-bytes long.
because the address is 2-bytes long, we output it using DEO2.
when using the literal absolute address rune (;), the assembler precedes the address with a LIT2 instruction. in this way, the address is pushed down onto the stack.
because the address is 2-bytes long, we send it to the screen device it using DEO2.
## setting the color
similar to what we saw already with the pixel, sending a byte to .Screen/sprite will perform the drawing in the screen.
similar to what we saw above when drawing a pixel, sending a byte to .Screen/sprite will perform the drawing of the sprite in the screen.
### sprite high nibble for 1bpp
the high nibble of the 'sprite' byte will determine the layer in which we'll draw, just like when we were drawing using the 'pixel' byte.
however, in this case we'll have other possibilities: we can flip the sprite in the horizontal (x) and/or the vertical (y) axis.
however, in this case we'll have other possibilities: we can flip the sprite in the vertical (y) and/or the horizontal (x) axis.
the eight possible values of the sprite high nibble, used for drawing a 1bpp sprite, are:
@ -629,63 +634,61 @@ a high nible of 1, i.e. 0001 in binary, has the last flag on, so that's why it's
### sprite low nibble for 1bpp
the low nibble of the 'sprite' byte will determine the colors that are used to draw the "on" (1) and "off"(0) pixels of the tiles.
the low nibble of the 'sprite' byte will determine the colors that are used to draw the "on" (1) and "off" (0) pixels of the tiles.
+ <table>
+ <tr><th></th><th colspan="4">colors for:</th></tr>
+ <tr><th>sprite low</th><th>1</th><th>0</th></tr>
+ <tr><td class="num">0</td><td>clear</td><td>clear</td></tr>
+ <tr><td class="num">1</td><td>1</td><td>0</td></tr>
+ <tr><td class="num">2</td><td>2</td><td>0</td></tr>
+ <tr><td class="num">3</td><td>3</td><td>0</td></tr>
+ <tr><td class="num">4</td><td>0</td><td>1</td></tr>
+ <tr><td class="num">5</td><td>1</td><td>none</td></tr>
+ <tr><td class="num">6</td><td>2</td><td>1</td></tr>
+ <tr><td class="num">7</td><td>3</td><td>1</td></tr>
+ <tr><td class="num">8</td><td>0</td><td>2</td></tr>
+ <tr><td class="num">9</td><td>1</td><td>2</td></tr>
+ <tr><td class="num">a</td><td>2</td><td>none</td></tr>
+ <tr><td class="num">b</td><td>3</td><td>2</td></tr>
+ <tr><td class="num">c</td><td>0</td><td>3</td></tr>
+ <tr><td class="num">d</td><td>1</td><td>3</td></tr>
+ <tr><td class="num">e</td><td>2</td><td>3</td></tr>
+ <tr><td class="num">f</td><td>3</td><td>none</td></tr>
+ <tr><td class="num">0</td><td>clear</td><td>none</td></tr>
+ <tr><td class="num">1</td><td>color1</td><td>color0</td></tr>
+ <tr><td class="num">2</td><td>color2</td><td>color0</td></tr>
+ <tr><td class="num">3</td><td>color3</td><td>color0</td></tr>
+ <tr><td class="num">4</td><td>color0</td><td>color1</td></tr>
+ <tr><td class="num">5</td><td>color1</td><td>none</td></tr>
+ <tr><td class="num">6</td><td>color2</td><td>color1</td></tr>
+ <tr><td class="num">7</td><td>color3</td><td>color1</td></tr>
+ <tr><td class="num">8</td><td>color0</td><td>color2</td></tr>
+ <tr><td class="num">9</td><td>color1</td><td>color2</td></tr>
+ <tr><td class="num">a</td><td>color2</td><td>none</td></tr>
+ <tr><td class="num">b</td><td>color3</td><td>color2</td></tr>
+ <tr><td class="num">c</td><td>color0</td><td>color3</td></tr>
+ <tr><td class="num">d</td><td>color1</td><td>color3</td></tr>
+ <tr><td class="num">e</td><td>color2</td><td>color3</td></tr>
+ <tr><td class="num">f</td><td>color3</td><td>none</td></tr>
+ </table>
& * 0: clear tile
& * 1: "on" with color 1, "off" with color 0
& * 2: "on" with color 2, "off" with color 0
& * 3: "on" with color 3, "off" with color 0
& * 4: "on" with color 0, "off" with color 1
& * 5: "on" with color 1, "off" with no color
& * 6: "on" with color 2, "off" with color 1
& * 7: "on" with color 3, "off" with color 1
& * 8: "on" with color 0, "off" with color 2
& * 9: "on" with color 1, "off" with color 2
& * a: "on" with color 2, "off" with no color
& * b: "on" with color 3, "off" with color 2
& * c: "on" with color 0, "off" with color 3
& * d: "on" with color 1, "off" with color 3
& * e: "on" with color 2, "off" with color 3
& * f: "on" with color 3, "off" with no color
& * 0: "on" will be cleared, "off" will be left as is
& * 1: "on" with color1, "off" with color0
& * 2: "on" with color2, "off" with color0
& * 3: "on" with color3, "off" with color0
& * 4: "on" with color0, "off" with color1
& * 5: "on" with color1, "off" will be left as is
& * 6: "on" with color2, "off" with color1
& * 7: "on" with color3, "off" with color1
& * 8: "on" with color0, "off" with color2
& * 9: "on" with color1, "off" with color2
& * a: "on" with color2, "off" will be left as is
& * b: "on" with color3, "off" with color2
& * c: "on" with color0, "off" with color3
& * d: "on" with color1, "off" with color3
& * e: "on" with color2, "off" with color3
& * f: "on" with color3, "off" will be left as is
as an example, this means that setting the sprite low nibble to 6 will draw the sprite with color 2 in the "on" pixels, and color 1 in the "off" pixels.
as an example, this means that setting the sprite low nibble to 6 will draw the sprite with color2 in the "on" pixels, and color1 in the "off" pixels.
note that 0 in the low nibble will clear the tile.
additionally, 5, 'a' and 'f' in the low nibble will draw the pixels that are "on" but will leave the ones that are "off" as is: this will allow you to draw over something that has been drawn before, without erasing it completely.
note that 0 in the low nibble will clear the "on" pixels of the tile, leaving the remaining ones as is. additionally, 5, 'a' and 'f' in the low nibble will draw the pixels that are "on" and will also leave the ones that are "off" as is: this will allow you to draw over something that has been drawn before, without erasing it completely.
don't worry if this is not making a lot of sense: let's see an example!
## hello sprite
the following program will draw our sprite once:
the following program will draw our sprite once using color1 over a background of color0:
```
( hello-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 ]
|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
( main program )
|0100
@ -699,17 +702,19 @@ the following program will draw our sprite once:
#0008 .Screen/y DEO2
( set sprite address )
;square .Screen/addr DEO2
;arrow .Screen/addr DEO2
( draw sprite in the background )
( using color 1 for the outline )
( draw 1bpp sprite in the background )
( using color1 for the outline )
#01 .Screen/sprite DEO
BRK
@square ff81 8181 8181 81ff
@arrow [ 0102 0488 d0e0 f0f8 ]
```
nice, there's our arrow!
## hello sprites
=> ./img/screenshot_uxn-tiles.png screenshot of the output of the program, showing 16 squares colored with different combinations of outline and fill.
@ -720,8 +725,8 @@ the following code will draw our square sprite with all 16 combinations of color
( hello-sprites.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 ]
|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
( macros )
%INIT-X { #0008 .Screen/x DEO2 } ( -- )
@ -740,7 +745,7 @@ the following code will draw our square sprite with all 16 combinations of color
INIT-X INIT-Y
( set sprite address )
;square .Screen/addr DEO2
;arrow .Screen/addr DEO2
#00 .Screen/sprite DEO 8ADD-X
#01 .Screen/sprite DEO 8ADD-X
@ -767,23 +772,22 @@ the following code will draw our square sprite with all 16 combinations of color
BRK
@square ff81 8181 8181 81ff
@arrow [ 0102 0488 d0e0 f0f8 ]
```
note that in this case, we have a couple of 8ADD-X and 8ADD-Y macros to increment each coordinate by 0008: that's the size of the tile.
## flipping experiments
because the square sprite is symmetric, we can't really see the effect of flipping it.
i invite you to try changing the sprite byte high nibble to explore the effects of flipping the sprites.
here are the sprites of the boulder/rock and the character of {darena}:
additionally, here are more sprites that you can use to test. they are the boulder/rock and the character of {darena}:
```
@rock 3c4e 9ffd f962 3c00
@character 3c7e 5a7f 1b3c 5a18
@rock [ 3c4e 9ffd f962 3c00 ]
@character [ 3c7e 5a7f 1b3c 5a18 ]
```
i invite you to try using these sprites instead to explore how to draw them flipped in the different directions.
# drawing 2bpp sprites