sio

package
v0.0.0-...-68e8ff9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 1, 2026 License: MIT Imports: 13 Imported by: 0

README

Serial I/O Peripherals

The μCAPP can access a number of I/O peripherals, which are mapped as follows:

Channel Type Purpose
0 Temp Temporary store
1 Depot Persistent storage
2 Tape Input/Output linear tape
3 VT Virtual Terminal
4 n/a unused
5 n/a unused
6 debug Debug channel
7 rom OS ROM image

Temp

IO Operations
store temp MASK ; store bits from tagged cells in MASK to temp
fetch temp MASK ; fetch bit data from temp into tagged cells according to MASK

Depot

A depot is a storage device, which consists of several storage drums (each with a unique ID), and each storage drum has up to 256 rings, which can each store up to 64K bytes of data.

IO Operations
fetch depot MASK ; fetch currently selected drum/ring into bits of tags in MASK
store depot MASK ; append tags' MASK bits to currently selected drum/ring
alert depot ((0x0 << 23) | DRUM) ; Select a specific drum (20 bits)
alert depot ((0x1 << 23) | (0 << 8) | RING) ; Select a specific ring (8 bits).
alert depot ((0x1 << 23) | (1 << 8) | 0) ; Rewind current ring's read pointer.
alert depot ((0x1 << 23) | (1 << 8) | 1) ; Rewind current ring's write pointer.

NOTE: A read of a ring cannot go past its current write pointer.

Drum

A drum is a storage device for multiple rings of data, which can be read or written to, used as a program library, or any number of other purposes.

A drum is divided into rings, each of which can store up to 64K of byte oriented data.

Rings

A ring is simply an array of 8-bit data, of up to 64K bytes.

Compiling a ucapp program to a ring.

When ucapp is run with the -s option, a dump of the CAPP before the program would have been executed will be saved to the depot in the drum/ring specified in the command line, defaulting to drum 0 ring 255.

$ go run ./cmd/ucapp -D depot/ -s -c example/hello_world.ucapp
$ go run ./cmd/ucapp -D depot/
Hello World!
Executing a Drum

To execute a drum, use the -d option, and -r to execute an arbitrary ring.

$ go run ./cmd/ucapp --verbose -D depot/ -d 0x1234 -r 3
This is ring 3! Calling ring 2...
This is ring 2! Calling ring 1...
This is ring 1! Hello World!
.. back in ring 2.
.. back in ring 3.
ucapp: Drum exited.

Tape

A tape is a byte stream, and can be specified to the μCAPP emulator with the --tape-input and --tape-output parameters (default are stdin and stdout, respectively). The input tape can only be sequentially read, and the output tape any only be sequentially written to.

IO Operations
fetch tape MASK ; Load input tape into MASK bits of tagged cells.
store tape MASK ; Append MASK bits of tagged cells to output tape.
Reading

If insufficient tagged CAPP entries are available for the read, the remainder of the tape segment is left unread.

Virtual Terminal (VT)

The Virtual Terminal (VT) provides a full screen UTF-8 terminal.

IO Operations
fetch vt 0xFF ; Read 8-bit keycode from the VT input buffer to tagged cells
store vt 0x3FFF_FFFF      ; Write tagged cells to VT display
Reading

Using the fetch vt 0xff opcode, the tagged CAPP cells' lower 8 bits are replaced with the keystroke scan code of the next keys in the VT key queue.

The VT can address a matrix of up to 128x64 cells.

See io/KEYMAP.md for the complete key mapping.

Writing

Using the store vt 0x3FFF_FFFF, modify the VT's frame buffer with the tagged CAPP cells' lower 24 bits.

Bits Purpose Comment
0..7 Glyph Glyph value
8 Unused Unused
9 Unused Unused
10 0 Indicates cell content write
11..16 Row Row number 0..63
17..23 Column Column number 0..127
Bits Purpose Comment
0..3 Foreground Cell foreground color
4..7 Background Cell background color
8 Bold Set to make the cell bold
9 Italic Set to make the cell italic
10 1 Indicates cell attribute write
11..16 Row Row number 0..63
17..23 Column Column number 0..127

The VT will always use the most recently written row/col value.

Monitor

The Monitor channel contains the boot ROM for the CPU, and is the target for inter-drum communication. It is bitstream of 32 bit wide words (2 bits of arena ID, 10 bits of IP data, and 20 bits of opcode), which is loaded in at machine reset.

See cpu/README.md for bootstrap details.

After bootstrap is complete, the Monitor is available for inter-process communication use between drums.

Inter-Process Communication

Inter-process communication can be done between any two drums in the depot.

Call Procedure
; select a drum to communicate with. Ring should be set to 0xff (active CPU)
io alert depot $((DRUM_ID << 8) | 0xff)
; await the drum's readyness.
io await depot r0
; (optional) check return code
if ne? r0 0
? exit
; send the IPC key for the function call to the Monitor
io alert monitor SOME_IPC_FUNCTION_KEY
; (optional) send any parameter data
io store monitor
list not
list write CAPP_FREE
; (optional) fetch any result data
list of CAPP_FREE
list all
io fetch monitor
list not
; await response code
io await monitor r0
; (optional) check return code
if ne? r0 0
? exit
Inter-Process Communication Server
LOOP:
; await a remote IPC on the monitor
io await monitor r0
; (optional) read data from the monitor channel
list of CAPP_FREE
list all
io fetch monitor
list not
; switch based off the key to the action to perform
if eq? r0 SOME_IPC_FUNCTION_KEY
? call SOME_IPC_FUNCTION_HANDLER ; result is in r0
; (optional) store result data to the monitor channel
io store monitor
list not
; alert with result code
io alert monitor r0

Documentation

Overview

Package io provides I/O channel implementations for the μCAPP emulator. It includes various channel types for bit-level I/O operations including temporary storage (Temp), persistent drum/ring storage (Depot, Drum, Ring), sequential I/O (Tape), and ROM.

Index

Constants

View Source
const (
	// DEPOT_OP_MASK masks the depot operation type from an alert request.
	DEPOT_OP_MASK = (1 << 23)
	// DEPOT_OP_SELECT indicates a drum selection operation.
	DEPOT_OP_SELECT = (0 << 23)
	// DEPOT_OP_DRUM indicates a drum-level operation.
	DEPOT_OP_DRUM = (1 << 23)
	// DEPOT_OP_SELECT_MASK masks the drum ID from a select operation.
	DEPOT_OP_SELECT_MASK = ((1 << 23) - 1)
)
View Source
const (
	// DRUM_OP_MASK masks the drum operation type from an alert request.
	DRUM_OP_MASK = (1 << 8)
	// DRUM_OP_SELECT indicates a ring selection operation.
	DRUM_OP_SELECT = (0 << 8)
	// DRUM_OP_SELECT_MASK masks the ring ID from a select operation.
	DRUM_OP_SELECT_MASK = 0xff
	// DRUM_OP_RING indicates a ring-level operation.
	DRUM_OP_RING = (1 << 8)
)
View Source
const (
	// RING_OP_MASK masks the ring operation type from an alert request.
	RING_OP_MASK = (1 << 7) - 1
	// RING_OP_REWIND_READ resets the read position to the start.
	RING_OP_REWIND_READ = 0
	// RING_OP_REWIND_WRITE resets the write position to the start.
	RING_OP_REWIND_WRITE = 1

	// RING_DEFAULT_CAPACITY is the default capacity in bits for a new ring.
	RING_DEFAULT_CAPACITY = 65536 * 8
)
View Source
const (
	// ARENA_ID_PROGRAM is the arena ID for program memory space.
	ARENA_ID_PROGRAM = uint32(2 << 30)
	// ROM_OP_TRAP sets up the trap notification channel for the ROM.
	ROM_OP_TRAP = uint32(0)
)

Variables

View Source
var ErrChannelFull = errors.New(f("channel full"))

ErrChannelFull is returned when attempting to write to a full channel.

View Source
var ErrDrumMissing = errors.New(f("drum missing"))

ErrDrumMissing is returned when attempting to access a drum that doesn't exist.

View Source
var ErrNameRuneInvalid = errors.New(f("name has an unknown rune"))

ErrNameRuneInvalid is returned when the dirent name has invalid characters.

View Source
var ErrNameTooLong = errors.New(f("name length too long"))

ErrNameTooLong is returned when the name is too long.

View Source
var ErrNameTooShort = errors.New(f("name length too short"))

ErrNameTooShort is returned when the dirent name is empty.

Functions

func ReceiveAsUint8

func ReceiveAsUint8(ch Channel) iter.Seq[uint8]

ReceiveAsUint8 returns an iterator that reads bits from the channel and yields complete 8-bit unsigned integers, LSB first.

func ReceiveAsUint16

func ReceiveAsUint16(ch Channel) iter.Seq[uint16]

ReceiveAsUint16 returns an iterator that reads bits from the channel and yields complete 16-bit unsigned integers, LSB first.

func ReceiveAsUint32

func ReceiveAsUint32(ch Channel) iter.Seq[uint32]

ReceiveAsUint32 returns an iterator that reads bits from the channel and yields complete 32-bit unsigned integers, LSB first.

func SendAsUint8

func SendAsUint8(ch Channel, value uint8) (err error)

SendAsUint8 sends an 8-bit unsigned integer as 8 bits to the channel, LSB first.

func SendAsUint16

func SendAsUint16(ch Channel, value uint16) (err error)

SendAsUint16 sends a 16-bit unsigned integer as 16 bits to the channel, LSB first.

func SendAsUint32

func SendAsUint32(ch Channel, value uint32) (err error)

SendAsUint32 sends a 32-bit unsigned integer as 32 bits to the channel, LSB first.

Types

type Channel

type Channel interface {
	// Per-channel defines
	Defines() iter.Seq2[string, string]
	// Rewind resets the channel to its initial state.
	Rewind()
	// Receive returns an iterator that yields bits from the channel.
	Receive() iter.Seq[bool]
	// Send writes a single bit to the channel.
	Send(value bool) error
	// Alert sends a control message to the channel with a response callback.
	Alert(value uint32, response chan uint32)
}

Channel defines the interface for all I/O channels in the μCAPP system. Channels operate at the bit level and support sequential reading, writing, and control operations via alerts.

type CreateFS

type CreateFS interface {
	// Sub returns a filesystem for a subdirectory.
	Sub(name string) (sub CreateFS, err error)
	// Create creates a new file for writing.
	Create(name string) (file io.WriteCloser, err error)
	// Mkdir creates a new directory with the specified permissions.
	Mkdir(name string, filemode fs.FileMode) (err error)
}

CreateFS defines a file system interface that supports creating files and directories. It extends basic file system operations with write capabilities for marshaling depot and drum data structures.

type Depot

type Depot struct {
	*Drum
	Drums map[uint32](*Drum)
}

Depot represents a collection of drums providing persistent storage. It implements the Channel interface and manages multiple Drum instances, allowing selection between them via Alert operations.

func (*Depot) Alert

func (depot *Depot) Alert(request uint32, response chan uint32)

Alert handles depot control operations including drum selection and forwarding drum-specific operations to the currently selected drum.

func (*Depot) Defines

func (depot *Depot) Defines() iter.Seq2[string, string]

Defines returns an iter of defines for the channel.

func (*Depot) Dirty

func (depot *Depot) Dirty() bool

Dirty returns true if any drum in the depot has unflushed changes.

func (*Depot) Marshal

func (depot *Depot) Marshal(filesys CreateFS) (err error)

Marshal writes the depot's drums to a file system, creating directories named XXXXXX.ud for each drum.

func (*Depot) Rewind

func (depot *Depot) Rewind()

Rewind resets all drums in the depot to their initial positions.

func (*Depot) Save

func (depot *Depot) Save(name string, content io.Reader) (err error)

Save a data stream into the currently selected drum. Save allocates a dirent in the drum's ring and saves the content.

func (*Depot) Unmarshal

func (depot *Depot) Unmarshal(filesys fs.FS) (err error)

Unmarshal loads depot data from a file system by scanning for drum directories matching the pattern XXXXXX.ud (6 hex digits).

type Drum

type Drum struct {
	*Ring
	Rings map[uint8](*Ring)
}

Drum represents a collection of up to 256 rings, providing persistent storage similar to a drum memory device. It implements the Channel interface by forwarding operations to the currently selected ring.

func (*Drum) Alert

func (drum *Drum) Alert(request uint32, response chan uint32)

Alert handles drum control operations including ring selection and forwarding ring-specific operations to the currently selected ring.

func (*Drum) Defines

func (drum *Drum) Defines() iter.Seq2[string, string]

Defines returns an iter of defines for the channel.

func (*Drum) Delete

func (drum *Drum) Delete(name string) (err error)

Delete deletes a file from a drum.

func (*Drum) Dirents

func (drum *Drum) Dirents() iter.Seq[DrumDirent]

Dirents returns the sequence of directory entries from the drum.

func (*Drum) Dirty

func (drum *Drum) Dirty() bool

Dirty returns true if any ring in the drum has unflushed changes.

func (*Drum) Marshal

func (drum *Drum) Marshal(filesys CreateFS) (err error)

Marshal writes the drum's rings to a file system, creating files named XX.ur for each ring.

func (*Drum) Receive

func (drum *Drum) Receive() iter.Seq[bool]

Receive returns an iterator that yields bits from the currently selected ring. If no ring is selected, selects ring 0 by default.

func (*Drum) Rewind

func (drum *Drum) Rewind()

Rewind resets all rings in the drum to their initial positions.

func (*Drum) Save

func (drum *Drum) Save(name string, content io.Reader) (err error)

Save saves a file into a drum, allocating a name in the dirent for it.

func (*Drum) Send

func (drum *Drum) Send(value bool) (err error)

Send writes a bit to the currently selected ring. If no ring is selected, selects ring 0 by default.

func (*Drum) Unmarshal

func (drum *Drum) Unmarshal(filesys fs.FS) (err error)

Unmarshal loads drum data from a file system by scanning for ring files matching the pattern XX.ur (2 hex digits).

type DrumDirent

type DrumDirent struct {
	Name string
	Ring uint8
}

DrumDirent is a drum directory entry.

func (*DrumDirent) Delete

func (dirent *DrumDirent) Delete()

Delete marks the entry as deleted.

func (*DrumDirent) Deleted

func (dirent *DrumDirent) Deleted() bool

Deleted returns true if the entry is deleted.

func (*DrumDirent) Marshal

func (dirent *DrumDirent) Marshal() (content []uint8, err error)

Marshal converts the dirent to the on-ring representation

func (*DrumDirent) NameIs

func (dirent *DrumDirent) NameIs(name string) bool

NameIs returns true if the name supplied would match the name in the dirent.

func (*DrumDirent) Size

func (dirent *DrumDirent) Size() int

Size returns the on-ring size of the dirent itself in bytes.

func (*DrumDirent) Unmarshal

func (dirent *DrumDirent) Unmarshal(content []uint8) (err error)

Unmarshal converts an on-ring representation of the dirent into the structure.

type Ring

type Ring struct {
	Capacity int

	Readable   bool
	Writable   bool
	Executable bool

	WriteIndex int
	ReadIndex  int
	Data       []uint8
	// contains filtered or unexported fields
}

Ring represents a circular buffer storage device with separate read and write positions. It stores up to 64KB of data and supports sequential bit-level I/O.

func (*Ring) Alert

func (ring *Ring) Alert(request uint32, response chan uint32)

Alert handles ring control operations including resetting read and write positions.

func (*Ring) Defines

func (ring *Ring) Defines() iter.Seq2[string, string]

Defines returns an iter of defines for the channel.

func (*Ring) Dirty

func (ring *Ring) Dirty() bool

Dirty returns true if the ring has unflushed changes

func (*Ring) Marshal

func (ring *Ring) Marshal(file io.Writer) (err error)

Marshal writes the ring's data to a writer up to the current write position.

func (*Ring) Receive

func (ring *Ring) Receive() iter.Seq[bool]

Receive returns an iterator that yields bits from the ring starting at the current read position up to the write position.

func (*Ring) Rewind

func (ring *Ring) Rewind()

Rewind resets the ring's read position to the start and write position to the end of existing data. Initializes the data buffer if not already allocated.

func (*Ring) Send

func (ring *Ring) Send(value bool) (err error)

Send writes a bit to the ring at the current write position. Returns ErrChannelFull if the ring has reached capacity.

func (*Ring) Unmarshal

func (ring *Ring) Unmarshal(file io.Reader) (err error)

Unmarshal loads ring data from a reader, replacing any existing data.

type Rom

type Rom struct {
	Data []uint32
	// contains filtered or unexported fields
}

Rom represents read-only memory that can issue trap notifications. It contains program data as 32-bit words and supports bit-level reading but not writing (writes return ErrChannelFull).

func (*Rom) Alert

func (rc *Rom) Alert(request uint32, response chan uint32)

Alert handles ROM control operations, currently only supporting trap channel registration.

func (*Rom) Defines

func (rc *Rom) Defines() iter.Seq2[string, string]

Defines returns an iter of defines for the channel.

func (*Rom) Receive

func (rc *Rom) Receive() iter.Seq[bool]

Receive returns an iterator that yields all bits from the ROM data, reading each 32-bit word LSB first.

func (*Rom) Rewind

func (rc *Rom) Rewind()

Rewind is a no-op for ROM as it is read-only and stateless.

func (*Rom) Send

func (rc *Rom) Send(value bool) error

Send always returns ErrChannelFull as ROM is read-only.

func (*Rom) Trap

func (rc *Rom) Trap()

Trap sends a trap notification to the registered trap channel if one is set.

type Tape

type Tape struct {
	Input  io.Reader
	Output io.Writer
	// contains filtered or unexported fields
}

Tape provides sequential I/O operations for reading and writing byte streams. It wraps an io.Reader for input and io.Writer for output, converting between bit-level Channel operations and byte-level I/O.

func (*Tape) Alert

func (tc *Tape) Alert(request uint32, response chan uint32)

Alert returns an error response for all requests as Tape does not support control operations.

func (*Tape) Defines

func (tc *Tape) Defines() iter.Seq2[string, string]

Defines returns an iter of defines for the channel.

func (*Tape) Receive

func (tc *Tape) Receive() iter.Seq[bool]

Receive returns an iterator that yields bits from the input stream, reading bytes as needed and yielding them LSB first.

func (*Tape) Rewind

func (tc *Tape) Rewind()

Rewind is not possible on a tape.

func (*Tape) Send

func (tc *Tape) Send(value bool) (err error)

Send writes a bit to the output stream, buffering bits until a complete byte is assembled, then writing it.

type Temporary

type Temporary struct {
	Capacity int // Capacity in bits.

	ReadIndex  int
	WriteIndex int
	Size       int
	Data       []bool
}

Temporary implements a circular buffer for temporary bit storage. It operates as a FIFO queue with a fixed capacity and separate read/write positions.

func (*Temporary) Alert

func (temp *Temporary) Alert(request uint32, response chan uint32)

Alert returns an error response for all requests as Temporary does not support control operations.

func (*Temporary) Defines

func (temp *Temporary) Defines() iter.Seq2[string, string]

Defines returns an iter of defines for the channel.

func (*Temporary) Receive

func (temp *Temporary) Receive() iter.Seq[bool]

Receive returns an iterator that yields bits from the buffer until empty. The buffer wraps around at the capacity boundary.

func (*Temporary) Rewind

func (temp *Temporary) Rewind()

Rewind resets the temporary storage to empty, resetting indices and reinitializing the data buffer.

func (*Temporary) Send

func (temp *Temporary) Send(value bool) (err error)

Send writes a bit to the buffer at the current write position. Returns ErrChannelFull if the buffer has reached capacity.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL