# I/O Devices 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. ## 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 class and a revision number. The device class will be the top value on the stack. ## Invoking a Device 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. ## 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. ## 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. ## Device Class 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 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.