I/O devices on Nga are exposed via three instructions:

ie  enumerate i/o devices iq  query i/o device for class and version ii  invoke i/o interaction

All devices are registered with the VM. How this occurs is implementation dependent.



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.



Use iq to query an attached device. This will return two values, a device class and a revision number.

The device class will be the top value on the stack.



You can trigger an I/O operation by passing the device handle 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 handle, not the device class.



Ultimately devices are implementation-specific, but the standard system provides or reserves the following:

 ID  | Device Class     | Notes                      | -----+------------------+----------------------------+ 0000 | Generic Output   | Always present as handle 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 class identifiers, and one must take care to use an Nga implementation that provides the devices they need.



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.





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).



Read and return a keypress.

Consumes no data, returns a single value representing the character that was read.

No subcommands are defined.



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)



Reserved for future use.



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             |



This is currently at revision 0.

On invocation, this returns a random number.



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 Class */ }

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.