file device

This commit is contained in:
sejo 2021-10-12 16:54:14 -05:00
parent 68df772613
commit 9b79fae0be
1 changed files with 188 additions and 2 deletions

View File

@ -1,9 +1,195 @@
# uxn tutorial: day 7, more devices
(work in progress)
this is the seventh and last section of the <(uxn tutorial)>! here we talk about the devices in the varvara computer that we haven't covered yet: audio, file, and datetime.
this should be a light and calm end of our journey, as it has to do less with programming logic and more with the input and output conventions in these devices.
let's begin!
# the file device
the file device in the varvara computer allows us to read and write to external files.
its ports are normally defined as follows:
```
|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ]
```
* the vector short is currently unused
* the success short stores the length of the data that was successfully read or written, or zero if there was an error
* offset-hs and offset-ls are a couple of shorts that compose a 32-bits number corresponding to an offset for the reading or writing of a file: how many bytes should the operation skip from the beginning of the file.
* the name short is for the memory address where the filename (null-terminated, i.e. with a 00) is stored
* the length short is the amount of bytes to read or write: don't forget that the program memory is ffff plus 1 bytes long, and that the program itself is stored there!
* the load short is for the starting memory address where the read data should be stored
* the save short is for the starting memory address where the data to be written is stored
a read operation is started when the load short is written to, and a write operation is started when the save short is written to.
these might seem like a lot of fields, but we'll see that they are not too much of a problem!
## reading a file
in order to read a file, we need to know the following:
* the path of the file, written as a string of text in program memory: terminated by a 00, and with an appropriate label - this path would be relative to the location where uxnemu is ran.
* the amount of bytes that we want to read from the file: it's okay if this number is not equal to the size of the file; it can be smaller or even greater.
* the label for a reserved section of program memory where read data will be stored
and that's it!
we can use a structure like the following, where the filename and reserved memory are under a label, and the load-file subroutine under another one:
```
@load-file ( -- )
;file/name .File/name DEO2 ( set address of file path )
#00ff .File/length DEO2 ( will attempt to read 255 bytes )
;file/data .File/load DEO2 ( set address for the data to read, and read )
( check the success byte and jump accordingly )
.File/success DEI2 #0000 EQU2 ,&failed JCN
&success
LIT 'Y .Console/write DEO
RTN
&failed
LIT 'N .Console/write DEO
RTN
@file
&name "test.txt 00
&data $ff ( reserving 255 bytes for the data )
```
note that in this case we are writing a character to the console according to the success short being zero or not, but we could decide to take any action that we consider appropriate.
also, in this example we are not really concerned with how many bytes were actually read: keep in mind that this information is stored in File/success until another read or write happens!
it's important to remember that, as always in this context, we are dealing with raw bytes.
not only we can choose to treat these bytes as text characters, but also we can choose to use them as sprites, coordinates, dimensions, colors, etc!
## writing a file
in order to write a file, we need:
* the path of the file
* the amount of bytes that we want to write into the file
* the label for the section of program memory that we will write into the file
keep in mind that the file will be completely overwritten unless you set some offset!
the following program will write "hello" and a newline (0a) into a file called "test.txt":
```
@save-file ( -- )
;file/name .File/name DEO2 ( set file name )
#0006 .File/length DEO2 ( will attempt to write 6 files )
;file/data .File/save DEO2 ( set data starting address, and write )
.File/success DEI2 #0006 NEQ2 ,&failed JCN
&success
LIT 'Y .Console/write DEO
RTN
&failed
LIT 'N .Console/write DEO
RTN
@file
&name "test.txt 00
&data "hello 0a
```
note how similar it is to the load-file subroutine!
the only differences, beside the use of File/save instead of File/load, are the file length and the comparison for the success short: in this case we know for sure how many bytes should have been written.
## offsets
if we wanted to read or write our file starting from a specific position (in bytes) in it, different from its beginning, we'd need to set the File/offset-ls short, and very rarely (i guess) the File/offset-hs short.
for example, if we wanted to continue writing to our file after the "hello" and newline, we could set:
```
#0006 .File/offset-ls DEO2
```
and then the next write will not overwrite the file, but append to it.
## a brief case study: the theme file
varvara programs written by 100r tend to have the ability to read a "theme" file that contains six bytes corresponding to the three shorts for the system colors.
this file has the name ".theme" and can be written to a local directory from nasu, using the ctrl+p shortcut.
=> https://wiki.xxiivv.com/site/theme.html uxn themes
### reading the theme file
we could adapt our previous subroutine in order to load the theme file and apply its data as system colors:
```
@load-theme ( -- )
;theme/name .File/name DEO2 ( set address of file path )
#0006 .File/length DEO2 ( will attempt to read 6 bytes )
;theme/data .File/load DEO2 ( set address for the data to read, and read )
( check the success byte and jump accordingly )
.File/success DEI2 #0006 NEQ2 ,&failed JCN
&success
( set the system colors from the read data )
;theme/r LDA2 .System/r DEO2
;theme/g LDA2 .System/g DEO2
;theme/b LDA2 .System/b DEO2
RTN
&failed
RTN
@theme
&name ".theme 00
&data ( reserving 6 bytes for the data: )
&r $2 &g $2 &b $2
```
### writing the theme file
and for doing the opposite operation, we can read the system colors into our reserved space in memory, and then write them into the file:
```
@save-theme ( -- )
( read system colors into program memory )
.System/r DEO2 ;theme/r STA2
.System/g DEO2 ;theme/g STA2
.System/b DEO2 ;theme/b STA2
;theme/name .File/name DEO2 ( set address of file path )
#0006 .File/length DEO2 ( will attempt to write 6 bytes )
;theme/data .File/save DEO2 ( set address for the data and write )
( check the success byte and jump accordingly )
.File/success DEI2 #0006 NEQ2 ,&failed JCN
&success
( report success? )
RTN
&failed
RTN
```
i invite you to compare these subroutines with the ones present in the 100r programs like nasu!
=> https://git.sr.ht/~rabbits/nasu/tree/main/item/src/main.tal nasu source code
* the audio device
* practice: small music instrument
* file device: saving and loading a simple state
* datetime device: reading the date and time
* practice: visualization of time
# support
if you found this tutorial to be helpful, consider sharing it and giving it your <(support)> :)