I/O Devices
I/O devices on Nga are exposed via three instructions:
ie enumerate i/o devices
iq query i/o device
ii invoke i/o interaction
All devices are registered with the VM. How this occurs is
implementation dependent.
Counting Devices
Use the ie instruction to return the number of attached devices.
i ie......
Upon running, the stack will contain the number of devices. You
can then query these by passing the device number to iq.
Query Devices
Use iq to query an attached device. This will return two values,
a device identifer and a revision number.
The device identifier will be the top value on the stack.
Invoking a Device
You can trigger an I/O operation by passing the device number to
the ii instruction.
E.g., to display a character (ASCII code 98 in this case):
i liliii..
d 98
d 0
Be sure to pass the device number, not the device identifier.
Device Identifiers
Ultimately device identifiers are implementation-specific, but the
most common system (Nga on Unix) provides or reserves the following:
ID | Device Type | Notes |
-----+------------------+----------------------------+
0000 | Generic Output | Always present as device 0 |
0001 | Keyboard | |
0002 | Floating Point | |
0003 | Block Storage | Raw, 1024 cell blocks |
0004 | Filesystem | Unix-style Files |
0005 | Clock | |
0006 | | |
0007 | Network: Sockets | |
0008 | Syscalls: Unix | |
0009 | Scripting Hooks | |
0010 | Random Number | |
1000 | Image Saving | |
It must be noted here that nothing forces devices to use these
identifiers, and one must take care to use an Nga implementation
that provides the devices they need.
Device Revisions
Over time, the functionality a device provides may change. To allow
detection of this, the query functionality provides a revision number.
Your code can use this to ensure that the device provided supports
the level of functionality you need.
Nga/Retro-Unix Device Details
0000: Generic Output
Supported by all Nga implementations. This is required to be the
first device, and is the only one guaranteed to be provided. It
consumes a value from the stack, writing to to the host-specific
output. (This does not need to be a screen).
0001: Keyboard
Read and return a keypress.
Consumes no data, returns a single value representing the
character that was read.
No subcommands are defined.
0002: Floating Point
The current revision is 1.
It currently provides:
n:to-float (n-_f:-n)
s:to-float (s-_f:-n)
f:to-number (f:a-__-n)
f:to-string (f:n-__-s)
f:+ (f:ab-c)
f:- (f:ab-c)
f:* (f:ab-c)
f:/ (f:ab-c)
f:floor (f:ab-c)
f:ceiling (f:f-f)
f:sqrt (f:f-f)
f:eq? (f:ab-c)
f:-eq? (f:ab-c)
f:lt? (f:ab-c)
f:gt? (f:ab-c)
f:depth (-n)
f:dup (f:a-aa)
f:drop (f:a-)
f:swap (f:ab-ba)
f:log (f:ab-c)
f:power (f:ab-c)
f:sin (f:f-f)
f:cos (f:f-f)
f:tan (f:f-f)
f:asin (f:f-f)
f:acos (f:f-f)
f:atan (f:f-f)
f:push (f:f-)
f:pop (f:-f)
f:adepth (-n)
0003: Block Storage
Reserved for future use.
0004: Filesystem
Currently at revision 0.
This implements a device providing traditional Unix-like files.
Takes a value indicating an operation, and each operation takes
additional values.
| Operation | Stack | Action |
| --------- | ----- | -------------------------------- |
| 0 | sm-h | Open a file |
| 1 | h- | Close a file |
| 2 | h-c | Read a byte from a file |
| 3 | ch- | Write a byte to a file |
| 4 | h-n | Return current pointer into file |
| 5 | nh- | Move pointer in a file |
| 6 | h-n | Return the size of a file |
| 7 | s- | Delete a file |
| 8 | h- | Flush pending writes |
0010: Random Number Generator
This is currently at revision 0.
On invocation, this returns a random number.
Implementation Details (C)
On the C implementation, each I/O device has the needed support
functions defined, then a query function and invocation function
defined.
As an example, to add a device that has two functions, I might do:
void one() {
stack_push(100);
}
void two() {
stack_push(200);
}
Handler device_actions[] = {
one, two
}
void io_device() {
device_actions[stack_pop()]();
}
void query_device() {
stack_push(0); /* Revision */
stack_push(1234); /* Device ID */
}
Then add pointers to io_device to IO_deviceHandlers and
query_device to IO_queryHandlers and increase the NUM_DEVICES
by one.
You will then need to write a set of Retro words to use the new
device.
:device:one #1 #1234 io:scan-for io:invoke ;
:device:two #2 #1234 io:scan-for io:invoke ;
Rebuild the VM, adding these to image.