Stream device

This is the user manual for the stream device of the Bedrock computer system.

This document is aimed at people who are learning about or writing programs for the Bedrock system. For people who are implementing the system from scratch, the stream device specification will provide more relevant information.

Overview

The stream device provides access to the standard terminal streams, and allows a program to connect to the internet as a client or a server.

Concepts

This device connects to other systems using two bi-directional bytestreams, called the local bytestream and the remote bytestream. The first half of the device controls the local bytestream, which connects to the standard terminal streams, and the second half controls the remote bytestream, which connects to networked systems over the internet.

Each bytestream is divided into an input channel and an output channel. Data is sent as a series of transmissions from the transmitting end of a channel to the receiving end. The receiving system is in control of the channel, with the transmitting system only allowed to transmit when requested. This system is connected to the receiving end of the input channels and the transmitting end of the output channels.

Each channel is buffered with a queue. The head port for each bytestream will read from the queue of the input channel and write to the queue of the output channel.

The remote bytestream can connect to a networked sytem by writing a network path to the remote input connection port or the remote output connection port. Writing to the input connection port will start the connection in server mode, binding to the address and listening for incoming transmissions, with each transmission representing a request from a different client system. Writing to the output connection port will start the connection in client mode, connecting to a single server system and allowing transmissions to be exchanged until either system disconnects.

Ports

Port Name Description Read Write
80 Input connection Connection state of local input channel.
81 Output connection Connection state of local output channel.
82 Input transmission Transmission state of local input channel.
83 Output transmission Transmission state of local output channel.
84 Input queue Bytes waiting in the local input queue.
85 Output queue Bytes free in the local output queue.
86 Head Read and write to the local bytestream.
87 aliased aliased
88 Input connection Connection state of remote input channel.
89 Output connection Connection state of remote output channel.
8A Input transmission Transmission state of remote input channel.
8B Output transmission Transmission state of remote output channel.
8C Input queue Bytes waiting in the remote input queue.
8D Output queue Bytes free in the remote output queue.
8E Head Read and write to the remote bytestream.
8F aliased aliased

Input connection

There are two input connection ports, one for each bytestream. Reading a byte from one of the ports will return FF if the input channel of that stream is connected to a system, or 00 otherwise. For the local bytestream, the input channel would be connected to the standard input stream. For the remote bytestream, the input channel would be connected to a networked system.

The remote input connection port can also be used to connect to a remote system. Writing each byte of a network path to this port followed by a zero byte will attempt to connect the remote bytestream in server mode, binding to a network path and listening for inbound connections. A network path is a UTF-8 encoded URL string that is at most 255 bytes long.

See the receiving a transmission and server mode examples.

Output connection

There are two output connection ports, one for each bytestream. Reading a byte from one of the ports will return FF if the output channel of that stream is connected to a system, or 00 otherwise. For the local bytestream, the output channel would be connected to the standard output stream. For the remote bytestream, the output channel would be connected to a networked system.

The remote output connection port can also be used to connect to a remote system. Writing each byte of a network path to this port followed by a zero byte will attempt to connect the remote bytestream in client mode, connecting to the system addressed by the network path. A network path is a UTF-8 encoded URL string that is at most 255 bytes long.

See the sending a transmission and client mode examples.

Input transmission

There are two input transmission ports, one for each bytestream. Reading a byte from one of the ports will return FF if that bytestream is ready to receive an incoming transmission, or 00 if the transmission has ended. Writing any value to one of the ports will clear the input queue of that bytestream, and then the bytestream will be marked as ready to receive a transmission.

See the receiving a transmission example.

Output transmission

There are two output transmission ports, one for each bytestream. Reading a byte from one of the ports will return FF if that bytestream is ready to receive an outgoing transmission, or 00 if the connected system is not ready for an outgoing transmission. Writing any value to one of the ports will end the outgoing transmission on that bytestream.

See the sending a transmission example.

Input queue

There are two input queue ports, one for each bytestream.

Reading a byte from one of the ports will return the number of bytes waiting in the input queue for that bytestream. Writing any value to one of the ports will drop the incoming transmission on that bytestream, clearing the input queue and dropping the rest of the transmission.

See the receiving a transmission example.

Output queue

There are two output queue ports, one for each bytestream. Reading a byte from one of the ports will return the number of bytes of space free in the output queue for that bytestream.

See the sending a transmission example.

There are two head port groups, one for each bytestream. Reading a byte from one of the ports will remove and return the oldest byte from the input queue for that bytestream, or 00 if the queue is empty. Writing a byte to one of the ports will push the byte into the output queue for that bytestream, or will drop the byte if the output transmission value for the bytestream is 00 or the queue is full.

Each head port is followed by a port alias, allowing double values to be read or written with a single instruction.

Wake behaviour

This device will wake the system from sleep when a byte is sent from an output queue, received onto an input queue, a transmission is started or ended, or a system is connected or disconnected from a bytestream.

Examples

Sending a transmission

An outgoing transmission is a block of bytes sent from this Bedrock system to a connected system along one of the bytestreams. This example will send a transmission through the local bytestream, which normally prints the contents of the transmission to the terminal.

We first check if a system is connected to the output channel of the bytestream by reading the output connection port. If this returns FF, we then check if the connected system is ready to receive a new transmission by reading the output transmission port. When this returns FF, we can write each byte of our transmission to the head port. Sending bytes too fast along a slow connection could result in bytes getting dropped, so we check the output queue port to see if there is space free in the output queue before sending each byte. Finally, we write a byte to the output transmission port to end the transmission:

*:string JMS:transmit-string HLT

@string "GET / HTTP/1.1"

%WAIT: *:0080 STD*:00 JMP: ;

@transmit-string  ( string* -- )
  &check-connection
    LDD:81 JCN:{ WAIT:~check-connection }
  &check-transmission
    LDD:83 JCN:{ WAIT:~check-transmission }
  &check-queue
    LDD:85 JCN:{ WAIT:~check-queue }
    DUP* LDA DUP JCN:{ POP POP* JMPr }
    STA:86 INC* JMP:~check-queue

Receiving a transmission

An incoming transmission is a block of bytes sent from a connected system to this Bedrock system along one of the bytestreams. This example will receive a transmission through the local bytestream, which normally receives input from a terminal.

We first check if a system is connected to the input channel of the bytestream by reading the input connection port. If this returns FF, we signal that we are ready to receive a transmission by writing a byte to the input transmission port. We then repeatedly check the input queue port to see if bytes have been received, and if so then we read them from the head port. Once the input transmission port returns 00, the transmission will have ended and no more bytes will be added to the input queue:

JMS:receive-string HLT

%WAIT: *:0080 STD*:00 JMP: ;

@receive-string  ( -- )
  &check-connection
    LDD:80 JCN:{ WAIT:~check-connection }
  &start-transmission
    :FF STD:82
  &receive-bytes
    LDD:84 DUP LDD:82 IOR JCN:{ POP JMPr }
    DUP JCN:{ POP WAIT:~receive-bytes }
    &loop  ( queue )
      LDD:86 STD:86 DEC DUP JCN:~loop
    POP JMP:~receive-bytes

Client mode

Writing a network address to the output connection port will attempt to connect the remote bytestream to a networked system in client mode. This will allow this Bedrock system to exchange transmissions with the connected system until either system closes the connection:

%λ:       JMSr:      ;
%NEWLINE  :0A STD:86 ;

λ:{"http://benbridle.com"} JMS:connect-client-mode HLT

@connect-client-mode  ( url* -- )
  λ:{"Connecting to: "} JMS:print-string
  DUP* JMS:print-string NEWLINE
  JMS:open-client-connection
  LDD:88 JCN:success
  &failure
    λ:{"Connection failed"}
    JMS:print-string NEWLINE JMPr
  &success
    λ:{"Connected successfully"}
    JMS:print-string NEWLINE JMPr

@open-client-connection  ( string* -- )
  :89 JMS:write-string :00 STD:89 JMPr
@print-string  ( string* -- )
  :86 ( ...fall through... )

@write-string  ( string* port -- )
  STA:~port JMP:~start &loop
    STD:[&port 00] INC* &start
    DUP* LDA DUP JCN:~loop
  POP POP* JMPr

Server mode

Writing a network address to the input connection port will attempt to bind the remote bytestream to that address in server mode. This will allow other systems to connect to this Bedrock system, send a request, and receive a response. Each incoming transmission represents a request from a connected client, and an outgoing transmission represents a response to that client. A client will be disconnected when a response is sent, a request is dropped, or a new transmission is requested:

%λ:       JMSr:      ;
%NEWLINE  :0A STD:86 ;

λ:{"http://0.0.0.0:8080"} JMS:connect-server-mode HLT

@connect-server-mode  ( url* -- )
  λ:{"Binding to: "} JMS:print-string
  DUP* JMS:print-string NEWLINE
  JMS:open-server-connection
  LDD:88 JCN:success
  &failure
    λ:{"Could not bind to address"}
    JMS:print-string NEWLINE JMPr
  &success
    λ:{"Bound successfully"}
    JMS:print-string NEWLINE JMPr

@open-server-connection  ( string* -- )
  :88 JMS:write-string :00 STD:88 JMPr
@print-string  ( string* -- )
  :86 ( ...fall through... )

@write-string  ( string* port -- )
  STA:~port JMP:~start &loop
    STD:[&port 00] INC* &start
    DUP* LDA DUP JCN:~loop
  POP POP* JMPr