beginning of uxn tutorial day 2

This commit is contained in:
sejo 2021-07-23 20:19:08 -05:00
parent 9f21c38e6c
commit 93a5d61a14
2 changed files with 292 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

292
src/uxn_tutorial_day_2.gmo Normal file
View File

@ -0,0 +1,292 @@
# uxn tutorial: day 2, the screen
this is the second section of the {uxn tutorial}!
here we start exploring the visual aspects of the uxn computer: we talk about the fundamentals of the screen device so that we can draw on it!
we also discuss working with shorts (2-bytes) besides single bytes, and go over basic operations for manipulating the contents in the stack.
if you haven't done it already, i recommend you read the previous section at {uxn tutorial day 1}
# where are your shorts?
before jumping right into drawing to the screen, we need to talk about bytes and shorts :)
even though uxn is a computer that works natively with 8-bits-sized words (bytes), there are several occasions in which the amount of data that it is possible to store in one byte is not enough.
when we use 8 bits, we can represent 256 different values (2 to the power of 8). at any given time, one byte will store only one of those possible values.
in the previous section we talked already about a case in uxn where this amount of possible values is not enough: the number of bytes that the main memory holds, 65536.
that amount of bytes is not arbitrary: that number corresponds to the values that can be represented using two bytes, or 16 bits, or a "short"; 2 to the power of 16. (that quantity is also known as 64KB, where 1KB corresponds to 1024 or 2 to the power of 10)
besides expressing addresses in main memory, today we will see another case where 256 values is not always enough: the x and y coordinates for the pixels in our screen.
for these and other cases, using shorts instead of bytes will be the way to go.
how do we deal with them?
## the short mode
counting from right to left, the 6th bit of a byte that encodes an instruction for the uxn computer is a "flag" that corresponds to what is called the short mode.
whenever this flag is set, i.e. when that bit is 1 instead of 0, the uxn cpu will perform the instruction given by the first 5 bits (the opcode) but using pairs of bytes instead of single bytes.
the byte that is deeper inside the stack will be the "high" byte of the short, and the byte that is closer to the top of the stack will be the "low" byte of the short.
in uxntal, we indicate that we want to set this flag adding the digit '2' to the end of an instruction mnemonic.
let's see some examples!
## short mode examples
### LIT2
first of all, let's recap. the following code will push number 02 down onto the stack, then it will push number 30 (in hexadecimal) down onto the stack, and finally add them together, leaving number 32 in the stack:
```
#02 #30 ADD
```
final state of the stack:
```
32 <- top
```
in the previous section we said that this was equivalent to using the LIT instruction instead of the literal hex rune (#)
```
LIT 02 LIT 30 ADD ( assembled code: 01 02 01 30 18 )
```
now, if we add the '2' suffix to the LIT instruction, we could write instead:
```
LIT2 02 30 ADD ( assembled code: 21 02 30 18 )
```
instead of pushing one byte, LIT2 is pushing the next short (two bytes) in memory, down onto the stack.
we can use the literal hex rune (#) with a short (four nibbles) instead of a byte (two nibbles), and it will work as a shorthand for LIT2:
```
#0230 ADD
```
### ADD2
now let's see what happens with the ADD instruction and the short mode.
what would be the state of the stack after executing this code?
```
#0004 #0008 ADD
```
answer: the stack will have the following values, because we are pushing 4 bytes down onto the stack, ADDing the two of them closest to the top, and pushing the result down onto the stack
```
00 04 08 <- top
```
now, let's compare with what happens with ADD2:
```
#0004 #0008 ADD2
```
in this case we are pushing the same 4 bytes down onto the stack, but ADD2 is doing the following actions:
* take the top element of the stack (08), and store it as the low byte of the first short
* take the new top element of the stack (00), and store it as the high byte of the first short, that is now 0008
* take the new top element of the stack (04), and store it as the low byte of the second short
* take the new top element of the stack (00), and store it as the high byte of the second short, that is now 0004
* add the two shorts (0004 + 0008), getting a result of 000c
* push the high byte of the result (00) down onto the stack
* push the low byte of the result (0c) down onto the stack
the stack ends up looking as follows:
```
00 0c <- top
```
we might not need to think too much about the per-byte manipulations of arithmetic operations, as we can think that they are doing "the same as before", but using pairs of bytes instead of single bytes; not really changing their order.
in any case, it's useful to keep them in mind for some behaviors we might need later :)
### DEO2, DEI, DEI2
let's talk now about the DEO (device out) instruction we discussed, as its short mode implies something special.
the DEO instruction needs a value (1 byte) to output, and an i/o address (1 byte) in the stack, in order to output that value to that address.
```
DEO ( value address -- )
```
now that we are at it, let's mention its counterpart DEI (device in); this instruction needs an i/o address (1 byte) in the stack, and it will push down onto the stack the value (1 byte) that corresponds to that input.
```
DEI ( address -- value )
```
what would DEO2 and DEI2 do?
in the case of the short mode of DEO and DEI the short aspect applies to the value to output or input, and not to the address.
remember that i/o addresses can be covered using one byte only already, so using one short for them would be redundant: the high byte would be always 00.
therefore, this is the behavior that we can expect: the DEO2 (device out in short mode) instruction needs a value (1 short) to output, and an i/o address (1 byte) in the stack, in order to output that value to that address.
on the other hand, the DEI2 instruction needs an i/o address (1 byte) in the stack, and it will push down onto the stack the value (1 short) that corresponds to that input.
we will see next some examples where we'll be able to use these instructions.
the 'write' output of the console device has a size of 1 byte, so we can't really use with it these instructions in a meaningful way .
# system device and colors
the system device is the uxn device with an address of 00. its output addresses (starting at address 08) correspond to three different shorts: one called red, the other one green, and the last one blue.
in uxntal examples we can see its labels defined as follows:
```
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
```
we will ignore the first elements for the moment, and focus on the color components.
## system colors
the uxn screen device can only show a maximum of four colors at a time.
these four colors are called color 0, color 1, color 2 and color 3.
each color has a depth of 12 bits: 4 bits for the red component, 4 bits for the green component, and 4 bits for the blue component.
we can define the values of these colors setting the r, g, b values of the system device.
the way we could write that would be as follows:
```
( hello-screen.tal )
( devices )
|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
( main program )
|0100
( set system colors )
#2ce9 .System/r DEO2
#01c0 .System/g DEO2
#2ce5 .System/b DEO2
```
what do the shorts mean?
we can read them vertically, from left to right:
* color 0 would be red: 2, green: 0, blue: 2 ( #220022 in hex color notation, dark purple )
* color 1 would be red: c, green: 1, blue: c ( #cc11cc in hex color notation, magenta )
* color 2 would be red: e, green: c, blue: e ( #eeccee in hex color notation, light pink )
* color 3 would be red: 9, green: 0, blue: 5 ( #990055 in hex color notation, dark red )
if we run the program now, we'll see a dark purple screen, instead of black as before.
try changing the values of color 0, and see what happens :)
# on-screen debugger
if you tried using the F2 key while running your program before today, you would have found that apparently nothing happened.
that was because the on-screen debugger that the F2 key shows uses the screen device, and therefore needs the system colors to be set.
now that you have some system colors, run your program and press the F2 key: you'll see several elements now!
=> ./img/screenshot_uxn-debugger.png screenshot of the on-screen debugger using the assigned system colors
* there are some lines and a crosshair drawn with color 2
* there are four rows of eight hexadecimal representations of one byte each, drawn with color 1; these 32 bytes show the deeper contents of the stack, with the stack "top" highlighted using color 2.
* there is a single byte drawn with color 2: it corresponds to the address of the top of the return stack (we'll talk about it on day 5)
* there is another set of 32 bytes, drawn with color 3; these show the contents of the first section of the zero page in the main memory.
take a look at the representation of the stack: if you didn't change the values i suggested above, you'll the the following numbers at the top left:
``` 25 e5 0c
[25] e5 0c
```
what are these numbers?
25e5 is the short we assigned to the blue components of the system colors, and 0c is the i/o address of the short corresponding to .System/b !
the highlight in the leftmost 25 implies that there's the "top" of the stack now (i.e., that the stack is empty)
tip: the stack memory is not erased when taking elements out of it, what changes is the value of the address that points to its top.
## stack debugging test
let's try adding to our program, after setting the system colors, the example code we discussed above:
```
#0004 #0008 ADD2
```
run it, open the debugger, and see the contents of the stack.
what does it mean?
if everything went alright, you'll see:
```
00 0c [00] 08
```
that corresponds to some extent with the result that we wrote before!
```
00 0c <- top
```
000c is the result of the addition, stored in the stack!
the 00 with the highlight, and the 08 to its right, correspond to the 0008 of our second operand.they were used by the ADD2 instruction already, but they are left unused in the stack memory until overwritten.
in general, if our program is functioning alright, we will see the top of the stack always at the top left.
otherwise, it means that our operations with the stack were left unbalanced: there were more elements added to it than element removed from it.
* draw pixels
* sprites: chr format, nasu
* draw sprites
* stack operations
* practice: manual repetition of sprite
new mode: short mode
=> https://wiki.xxiivv.com/site/nasu.html nasu
# new instructions
these are the instructions we covered today:
new instructions: DEI, MUL, DIV, SWP, OVR, ROT, DUP, POP
* ADD: take the top two elements from the stack, add them, and push down the result ( a b -- a+b )
* SUB: take the top two elements from the stack, subtract them, and push down the result ( a b -- a-b )
* LIT: push the next byte in memory down onto the stack
* DEO: output the given byte into the given device address, both taken from the stack ( byte address -- )
# day 3
stay tuned for the next sections of the {uxn tutorial}!
# support
if you found this tutorial to be helpful, consider sharing it and giving it your {support} :)