Pochi Doc

Devices

The device model of Pochi bears resemblance with uxn’s Varvara. The advantages of such a system is to separate all the external interactions between the core of the vm and the system. Usually the devices’ implementation will be different based on the platform (e.g. the screen will be different on desktop than on web) while the core of the vm will remain almost unchanged.

The difference with uxn is that access to devices is the same as a memory access with the difference that the address that is accessed is in the HIGHMEM (0x10000 - 0x1FFFF). This aspect is more similar to how the GreenArrays’ F18a chip does I/O.

Each device has 16 ports (0x0 - 0xF) and the device id is specified by the rest of the address. Concretely in the following address D represent the device id and P the port: 0x1DDDP. This means that there are potentially 4095 different devices but we will strive to use way less than that.

Port name with a * usually represent addresses in memory, otherwise port holds/store integer values.

Disclaimer: As we are very early in the design phase, things may change a lot. The documentation can also lag behind. I strongly encourage you to have a look at the code.

Vectors

Each device usually has a vector on port 0x0. When set, a vector contains the address of a word that will be executed when the required conditions are met. The conditions for the vector to be triggered will be specified in the relevant device sections.

Devices layout

id addr device
0 0x10000 System
1 0x10010 Console
2 0x10020 Files 1
3 0x10030 Files 2
4 0x10040 Datetime
5 0x10050 Screen
6 0x10060 Grid
7 0x10070 Controller
8 0x10080 Mouse

System

The system device is a general device that manages the Pochi vm.

port addr name type
0 0x10000 *vector rw
1 0x10001 halt rw
2 0x10002 debug -w

System Vector

The system vector is triggered once an error occurs, right before exiting the program. It is not triggered when the program exits normally.

The errors are the following:

When an error occurs and if the system’s vector is defined, the word pointed vector execute with the working stack filled like so: ( addr inst err )

Halt

When a non-zero value is written to the halt port, the vm will stop and the program will exit.

Debug

The value written to the debug port is a bitmask where the bits have the following meaning:

Note that the range for the RAM addresses to be dumped is currently hard coded in the devices/system.c file.

Console

The console device, as its name suggests deals with I/O within the console/terminal.

port addr name type
0 0x10010 *vector rw
1 0x10011 stdin/stdout rw
2 0x10012 cmdin/cmdout rw
3 0x10013 stderr -w

Console Vector

The console vector is triggered whenever something is available to be read on the stdin or cmdin port.

Stdin / Stdout

Reading gets a char from stdin. Writing writes a char to stdout.

Cmdin / Cmdout

The cmdin/cmdout port behave the same as stdin/stdout except that it reads/write a Pochi instruction.

An instruction is 32 bit unsigned integer that has 6 slots (5x6 bits + 1x2 bits).

Stderr

Writes a char to stderr.

Files

The files device provide an interface to interact with the host’s file system.

port addr name type
0 0x100?0 unused
1 0x100?1 flag r-
2 0x100?2 nread r-
3 0x100?3 *path -w
4 0x100?4 *addr rw
5 0x100?5 maxread rw
6 0x100?6 append rw
7 0x100?7 action -w
8 0x100?8 read/write rw
9 0x100?9 delete -w
A 0x100?A tell/seek rw

Where ? in the addr is either 2 or 3.

Note that there are 2 files devices to facilitate operations between two files.

Flag

Reading from the flag port indicates whether a previous read/write operation failed, succeeded or if there is more data to read. The following values are possible:

Nread

The value read from the nread port is the number of “units” that were read/written. It is usually chars, but in the case of a directory listing it’ll be the number of entries.

Path

The value written to the *path port is an address to a Pochi string. When written, it will close any previously opened file. The string can be a path to a file as well as a directory.

Addr

An address in memory to/from which any read/write operation will operate. In other words it’s the address of a buffer. See the description for action to see how the data that is written to the *addr will look like.

Maxread

The maximum number of bytes that can be read into the buffer at *addr. The flag will be useful to detect if the buffer is full and there is more to be read.

Append

Any non-zero value written to the append port will enable append mode, which means that writing in a file won’t override it’s content.

Note that even if the value in append is 0, the file won’t be truncated, you should first delete the file and then write to it if you want to emulate “truncating”.

Action

Before writing a value to the action port you want to make sure that *path and *addr are set as well as optionally make sure that append is set as desired and the position in the file is good.

There are 3 different values that can be written to the action port. The result will always be written to *addr up to maxread bytes. After writing to the action port, the flag and nread will be set accordingly.

Since the value in *path can be both a file or a directory, we need to specify the different behaviors depending on the type.

File

The possible values are as follow:

For read and write actions, nread will be set to the number of bytes read/written. What is written to *addr is the same as a Pochi string, 1 cell whose value is the number of bytes in the string that follows.

For stat nread will be 1 (entry) in case of success or 0 in case of failure. The entry will look like this in addr:

0001    size in bytes
0002    num of chars in the name
0003    char string of the name
...

Each cell (line) is 32 bits (4 bytes).

Directory

We can’t write to a directory, so there are only two actions possible:

Both read and stat will set nread to the number of entries that where read. (stat will be 1 in case of success)

When reading a directory, what is written to *addr is in the form of multiple stat entries following each other:

0001    size in bytes of the 1st entry
0002    num of chars in the name of the 1st entry
0003    char string of the name of the 1st entry
...     ...
000N    size in bytes of the 2nd entry
000N+1  num of chars in the name of the 2nd entry
000N+2  char string of the name of the 2nd entry
...     etc...

Read / Write

Read/write a 32 bits unsigned integer from/to the file, at the position of the cursor given/set by tell/seek and move that cursor forward.

This can be useful to read a file containing instructions, for instance, one could jump to this port and “execute” the file directly by reading it instruction by instruction.

Delete

Writing a non-zero value to the delete port will delete the file/directory pointed to by *path.

Tell / Seek

Get/set the cursor position in a file or in a directory by reading/writing to this port.

Datetime

The datetime device provides a way to obtain date and time information as well as a convenient timer.

port addr name type
0 0x10040 *vector rw
1 0x10041 interval rw
2 0x10042 timestamp r-
3 0x10043 year r-
4 0x10044 month r-
5 0x10045 day r-
6 0x10046 hour r-
7 0x10047 min r-
8 0x10048 sec r-
9 0x10049 wday r-
A 0x1004A yday r-
B 0x1004B isdst r-

It is worth noting that most of the read only values come from the localtime(3) function from the time.h header in C. The interface should feel familiar to C programmers, just be careful with year and month which are slightly different.

Datetime Vector

The datetime vector is triggered each time the timer “ticks”.

The timer is controlled by the interval port.

Interval

The interval in milliseconds between each tick of the timer. Initially the timer is disabled.

Writing a non-zero value to it will activate the timer, writing 0 to it will disable the timer.

Reading will return the amount of milliseconds left until the next tick.

Timestamp

The usual UNIX timestamp, aka number of seconds from the 1st January 1970. Don’t rely on it too much since 32 bits integers will overflow in 2038. I suggest to use it just to get “unique” timestamps if needed.

Year

The current year.

Note that unlike localtime(3) the year is not 1900 - current_year.

Month

The month of this year, it’s a value between 1 (for January) and 12 (for December). This is slightly different than localtime(3).

Day

The day of the month, the value is between 1 and 31.

Hour, Min, Sec

The usual hours, minutes and seconds, nothing surprising here.

Wday

The day of the week. Note that Sunday is 0.

Yday

The day of the year, the value is from 1 to 365.

Isdst

A value of 1 indicates daylight savings (summer time).

Screen

The screen is a bitmap graphics screen that works very similarly to the uxn screen.

port addr name type
0 0x10050 *vector rw
1 0x10051 palette -w
2 0x10052 width rw
3 0x10053 height rw
4 0x10054 auto rw
5 0x10055 x rw
6 0x10056 y rw
7 0x10057 *addr rw
8 0x10058 pixel -w
9 0x10059 sprite -w
A 0x1005A poly -w

Sprite

The sprite port works similarily to uxn’s screen sprite port with a little extension to benefit from Pochi’s 16 colors while remaining compatible with uxn’s 1bpp and 2bpp sprite format.

The lowest significant byte works exactly like it does in uxn.

The 2 following bytes select the colors from Pochi’s palette. This means that when drawing a 2bpp sprite in the foreground by using the first 4 color (same behavior as uxn), you can either set the extension bytes to 0 or set them to 0x0123 which will select Pochi’s color 0 for the sprite’s color 0 etc..

The two folowing value for the sprite port are equivalent:

The first 4 digits selects the “sprite palette” from the Pochi palette of 16 colors. From left to right they select colors from 0 to 4.

Poly

The poly port draws a polygon when written to. The x and y ports don’t matter, the coordinates of the corners of the polygon are absolute and (0,0) is the top left corner of the screen. The two ports that matter are *addr and auto.

The *addr port is an address in memory that points to a cell containing the number of corner the polygon has followed by that number of cells containing the coordinates of the corners. A cell containing the coordinate has the 16 least significant bits represent the y coordinate and the 16 most significant bits represent the x coordinate.

The (40,125) coordinate would then look like this in memory: 0x0028007D. With 0x28 = 40 and 0x7D = 125.

The auto byte is only used when the autoaddr bit is set, which will then move *addr right after the cell containing the last corner coordinate of the polygon.

The value written to the poly port is used to specify how the polygon will be drawn:

* O L F              C C C C
| | | +-- Fill       | | | +-- Color
| | +---- Layer      | | +---- Color
| +------ Openshape  | +------ Color
+-------- Unused     +-------- Color

The Openshape bit will be 1 when we want the shape to be open (aka only a path and not a polygon). This will work only when the Fill bit is 0, a polygon cannot be open and filled.

We assume that it is more common to draw closed polygons, thus the “default” is to close the shapes, conveniently the Layer, Fill and Color bits fit in 6 bits and this makes it possible to encode the value in a gray number instead of a regular green number.

Grid

port addr name type
0 0x10060 unused
1 0x10061 cols r-
2 0x10062 rows r-
3 0x10063 *font -w
4 0x10064 fontheight rw
5 0x10065 auto rw
6 0x10066 pos rw
7 0x10067 x rw
8 0x10068 y rw
9 0x10069 *straddr rw
A 0x1006A char rw
B 0x1006B str -w

Auto

* * * *              * * A P
| | | +-- Unused     | | | +-- Auto pos
| | +---- Unused     | | +---- Auto addr
| +------ Unused     | +------ Unused
+-------- Unused     +-------- Unused

Controller

The controller device is pretty straigthforward and again very similar to the uxn controller.

port addr name type
0 0x10070 *vector rw
1 0x10071 key r-
2 0x10072 buttons rw

Mouse

The mouse device is pretty straigthforward and again very similar to the uxn mouse.

port addr name type
0 0x10080 *vector rw
1 0x10081 x rw
2 0x10082 y rw
3 0x10083 scrollx r-
4 0x10084 scrolly r-
5 0x10085 buttons rw

Audio

Not yet implemented