rewriting first program for learn-uxn, thanks 256k!

This commit is contained in:
sejo 2024-03-09 15:10:45 +01:00
parent dd87cd2837
commit 616b71de72
2 changed files with 55 additions and 144 deletions

View File

@ -28,8 +28,12 @@ implement the following concepts as {coloring computers}:
## tutorial
### day 1
* check links / update links
* check new instructions? and check changes in opcodes - for assembled rom in day 1
* updating tutorial for uxntal playground
### day 2 and onwards
* recap how to use learn-uxn for the programs there
* check current color theme: Background/alpha, Selection, Foreground, Application
* update images if needed
* check blend mode 0
@ -41,6 +45,8 @@ implement the following concepts as {coloring computers}:
=> https://lists.sr.ht/~rabbits/uxn/%3CCAE2DaSQQMb8XVfsn2NSsXQO+-0m2t4U2GD7nYD3GBUO4GPeTxQ%40mail.gmail.com%3E Whole auto sprite flipping
* make a folder of examples
* include uxn5 in the web tutorial so that code can be run from mthere
=> https://git.sr.ht/~rabbits/uxn5 uxn5
## traducción:

View File

@ -161,67 +161,27 @@ the instruction will normally imply a change in the stack(s), and sometimes it m
to run varvara, you have several options: running it from your (web)browser, downloading it as a pre-built application, or building it from source yourself.
## online: learn-uxn site
## online
you can experiment with all the materials in the tutorial using the learn-uxn site by metasyn:
=> https://metasyn.srht.site/learn-uxn/ learn-uxn by metasyn
## desktop bundles
for the exercises in day 1, you can alternatively use the uxntal playground that is optimized for working with text-only:
=> https://wiki.xxiivv.com/etc/uxnrepl/index.html Uxntal Playground
in order to run varvara locally and off the grid we need to get an appropriate emulator.
## desktop bundles, building from source and more
the 100R website allows you to download the emulators for major desktop systems; these come bundled with a selection of programs in the form of "roms":
=> https://100r.co/site/uxn.html 100R — uxn
depending on your system, you might be able to launch the emulator (Uxn32.exe or uxnemu) with a double-click, or you might need to use a console to navigate to its location and run it:
```
# in Linux/Unix:
$ ./uxnemu launcher.rom
```
that's it! you will be greeted by a screen corresponding to the launcher rom.
from there you will be able to run the other roms by clicking them, pressing Enter, or the Ctrl key. you can go back to the launcher by pressing the F4 key.
### uxnemu controls
these are the controls you can use within uxnemu, regardless of the rom that you are running:
* F1 circles between different zoom levels.
* F2 toggles the debugger.
* F3 takes a screenshot of the window.
* F4 loads the launcher.rom that lets you browse and open roms in the current directory.
## building from source
alternatively, you can get the sources from the uxn git repository, where you will also find the building and installation instructions:
=> https://git.sr.ht/~rabbits/uxn ~rabbits/uxn - sourcehut git
## the toolchain
depending on your system, you might see that besides uxnemu or uxn32 there are a couple of other programs, uxnasm and uxncli.
* uxnemu or uxn32 are the full-featured emulators.
* uxncli is a console-based emulator.
* uxnasm is an uxntal assembler.
during this journey we will be writing our programs in uxntal, the assembly language for uxn machines.
these programs will have to be assembled in order to become roms that we will be able to run with an emulator.
in order to run varvara locally and off the grid we need to get an appropriate emulator. for further instructions in how to run varvara in this way, see {uxn running}.
# uxntal and a very basic hello world
uxntal is the assembly language for uxn machines.
we were talking before about the uxn cpu and the 32 instructions it knows how to perform, each of them encoded as a single 8-bit word (byte).
above, we were talking about the uxn cpu and the 36 instructions it knows how to perform, each of them encoded as a single 8-bit word (byte).
that uxntal is an assembly language implies that there's a one-to-one mapping of a written instruction in the language to a corresponding 8-bit word that the cpu can interpret.
uxntal being an assembly language implies that there's a one-to-one mapping of a written instruction in the language to a corresponding 8-bit word that the cpu can interpret.
for example, the instruction ADD in uxntal is encoded as a single byte with the value 18 in hexadecimal, and corresponds to the following set of actions: take the top two elements from the stack, add them, and push down the result.
for example, the instruction ADD in uxntal is encoded as a single byte with the value 18 in hexadecimal (that's what's called its opcode), and corresponds to the following set of actions: take the top two elements from the stack, add them, and push down the result.
in forth-like systems we can see the following kind of notation to express the operands that an instruction takes from the stack, and the result(s) that it pushes down onto the stack:
@ -239,48 +199,31 @@ SUB ( a b -- a-b )
note that the order of the operands in the subtraction is similar to the order for the division as we discussed above when talking about postfix notation: it is as if we moved the operator from between operands, to the end after the second operand.
also, note that in unxtal, text in between parenthesis is a comment, i.e. it is used for documentation purposes.
## a first program
### using left and the launcher
let's use learn-uxn to write our first program:
uxn and the tools that have been built around it allow us to learn and develop completely within the system.
=> https://metasyn.srht.site/learn-uxn/ learn-uxn by metasyn
for instance, when you run the emulator you will see that you can open a program called left: a text editor!
=> https://100r.co/site/left.html 100R — left
open it, as we will write our first program there!
first of all, rename the file you are working on. you can use the visual menu or press Ctrl + r, then delete the current filename, replace it with hello.tal, and press Enter to confirm.
now write the following program:
press the "new" button or delete the code that you find there, and then, write the following code:
```
( hello.tal )
|0100 LIT 68 LIT 18 DEO
|0100 LIT 68 LIT 18 DEO LIT 0a LIT 18 DEO
```
save it using the visual menu or Ctrl + s, and then go back to the launcher by pressing F4.
press the "assemble" button, and look at the box in the bottom-right corner!
you will see now that the listing in the launcher includes your newly created file, hello.tal!
note that this is a text file only, and it's not a rom yet. however, the launcher makes it very easy to convert it to a rom, or more precisely, to assemble it!
just click the hello.tal, or use the arrow keys to reach it and then press Enter or Ctrl: if everything went alright you will see that a hello.tal.rom file appears!
additionally, note that the accompanying console will print something like:
scroll up within that box to see several messages, some of them tagged with [web], others with [asm] and lastly some tagged with [emu].
the [asm] message, corresponding to the output of the assembler that read the code and converted it into a rom, will look something like this:
```
on-reset 0x0000
0x0004 lines of source code.
0x002e bytes of heap used, 0xa34e bytes free.
Assembled output.rom in 10 bytes(0.02% used), 0 labels, 0 macros.
```
if there are errors during assembly, you will see them there.
now that you have hello.tal.rom you can run it from the launcher as any other rom!
when you run it you will see that the screen will be cleared and that the console will show the output of our program:
then, the first [emu] message should read something like this:
```
h
@ -288,40 +231,9 @@ h
interesting, what is happening?
i invite you to try replacing the 68 in the code with, for example, 65.
i invite you to try replacing the 68 in the code with, for example, 65. then, assemble and run the program again.
to do that you'll have to open left again, rename the file to hello.tal so that it opens it, modify the file, save it, return to the launcher, assemble, and then run again!
### using another text editor and uxn tools
alternatively, you can use your favorite text editor and the uxn programs to replicate what we just did from within uxnemu.
you can write hello.tal using your text editor, and then save it along the other files bundled with the emulator:
```
( hello.tal )
|0100 LIT 68 LIT 18 DEO
```
once you have the file, we can assemble and run it from the console:
```
$ ./uxnasm hello.tal hello.rom && ./uxnemu hello.rom
```
a black window will open, and in the console we will see an output that looks like the following:
```
Assembled hello.rom in 5 bytes(0.01% used), 0 labels, 0 macros.
Loaded hello.rom
h
```
as we saw in the previous case, the last 'h' we see is the output of our program.
feel free to use any of these approaches, either working completely from within uxnemu, or using your own tools and the uxn programs.
everything that follows in this journey regarding uxntal is completely agnostic to the tooling you use!
what was the difference in the output now?
## one instruction at a time
@ -329,38 +241,40 @@ we just ran the following program written in uxntal:
```
( hello.tal )
|0100 LIT 68 LIT 18 DEO
|0100 LIT 68 LIT 18 DEO LIT 0a LIT 18 DEO
```
now let's analyze it!
the first line is a comment: comments are enclosed between parenthesis and there have to be spaces in between them. similar to other programming languages, comments are ignored by the assembler.
the first line is a comment: comments are enclosed between parenthesis. there have to be spaces in between them. similar to other programming languages, comments are ignored by the assembler.
the second line has several things going on:
* |0100 : you may remember this number from before - this is the initial value of the program counter; the address of the first byte that the cpu reads. we use this notation to indicate that whatever is written afterwards, will be written in memory starting at this address.
* LIT : this appears twice; it is an uxn instruction that performs the following actions: it pushes the next byte in memory down onto the stack, and it makes the program counter skip that byte.
* 68 : an hexadecimal number, that corresponds to the ascii code of the character 'h'
* 18 : an hexadecimal number, that corresponds to an i/o address: device 1, port 8.
* DEO : another uxn instruction, that we could define as the following: output the given value (1 byte) into the given device address, both taken from the stack ( value address -- )
* |0100 : you may remember this number from before—this is the initial value of the program counter; the address of the first byte in the main memory that the cpu reads. we use this notation to indicate that whatever is written afterwards, will be written in memory starting at this address.
* LIT : this appears four times; it is an uxntal instruction that performs the following actions: it pushes the next byte in memory down onto the stack, and it makes the program counter skip that byte.
* 68 : an hexadecimal number that corresponds to the ascii code of the character 'h'.
* 18 : an hexadecimal number that corresponds to an i/o address: device 1, port 8.
* 0a : an hexadecimal number that corresponds to the ascii code of a 'newline'.
* DEO : another uxntal instruction, that we could define as the following: output the given value (1 byte) into the given device address, both taken from the stack ( value address -- )
reading the program from left to right, we can see the following behavior:
* the LIT instruction pushes number 68 down onto the stack
* the LIT instruction pushes number 18 down onto the stack
* the DEO instruction takes the top element from the stack (18) and uses it as a device address
* the DEO instruction takes the top element from the stack (68) and uses it as a value to output
* the DEO instruction outputs the value to the device address, leaving the stack empty
* the LIT instruction pushes number 68 down onto the stack.
* the LIT instruction pushes number 18 down onto the stack.
* the DEO instruction takes the top element from the stack (18) and uses it as a device address.
* the DEO instruction takes the top element from the stack (68) and uses it as a value to output.
* the DEO instruction outputs the value to the device address, leaving the stack empty.
* then, the same behavior is repeated, but with the number 0a instead of 68.
and what is the i/o device with address 18?
we're talking about a device address, 18, but what does it mean?
looking at the devices table from the varvara reference, we can see that the device with address 1 in the high nibble is the console (standard input and output), and that the column with address 8 in the low nibble corresponds to the "write" port.
=> https://wiki.xxiivv.com/site/varvara.html varvara
=> https://wiki.xxiivv.com/site/varvara.html#console varvara: console device
so, device address 18 corresponds to "console write", or standard output.
our program is sending the hexadecimal value 68 (character 'h') to standard output!
our program is sending the hexadecimal values 68 (character 'h') and 0a (newline) to standard output!
you can see the hexadecimal values of the ascii characters in the following table:
@ -368,43 +282,34 @@ you can see the hexadecimal values of the ascii characters in the following tabl
### raw numbers
note that the raw numbers that we wrote, 0100, 18 and 68, are written in hexadecimal using either 4 digits corresponding to two bytes, or 2 digits corresponding to one byte.
note that the raw numbers that we wrote, 0100, 18, 68 and 0a, are written in hexadecimal using either 4 digits corresponding to two bytes, or 2 digits corresponding to one byte.
in uxntal we can only write numbers that are 2 or 4 hexadecimal digits long. if, for example, we were only interested in writing a single hexadecimal digit, we would have to include a 0 at its left.
in uxntal we can only write numbers that are 2 or 4 hexadecimal digits (nibbles) long. if, for example, we were only interested in writing a single hexadecimal digit, we would have to include a 0 at its left.
## assembled rom
when we assembled our program, we saw that it was 5 bytes in size.
when we assembled our program, we saw that it was 10 bytes in size.
we can confirm it using the wc (word count) program:
if we looked at the numerical contents of the rom, we would see something like this:
```
$ wc -c hello.rom
5 hello.rom
80 68 80 18 17 80 0a 80 18 17
```
for the curious (like you!), we could use a tool like hexdump to see its contents:
80 is the "opcode" corresponding to LIT, and 17 is the opcode corresponding to DEO. and there you can see our 68, 18 and 0a!
```
$ hexdump -C hello.rom
00000000 80 68 80 18 17 |.h...|
00000005
```
80 is the "opcode" corresponding to LIT, and 17 is the opcode corresponding to DEO. and there they are our 68 and 18!
so, effectively, our assembled program matches one-to-one the instructions we just wrote!
so, our assembled program matches one-to-one the instructions we just wrote!
actually, we could have written our program using these hexadecimal numbers, i.e. the machine code, and it would have worked the same way:
```
( hello.tal )
|0100 80 68 80 18 17 ( LIT 68 LIT 18 DEO )
|0100 80 68 80 18 17 80 0a 80 18 17 ( LIT 68 LIT 18 DEO LIT 0a LIT 18 DEO )
```
maybe not the most practical way of programming, but indeed a fun one :)
maybe it's not the most practical way of programming, but indeed it's a fun and beautiful one :)
you can find the opcodes of all 32 instructions in the uxntal reference
you can find the opcodes of all 36 instructions in the uxntal reference
=> https://wiki.xxiivv.com/site/uxntal.html XXIIVV - uxntal