FEP 003 - FujiNetWIFI/fujinet-firmware GitHub Wiki
The NetSIO protcol for FujiNet PC
Name | Value |
---|---|
PEP ID | 003 |
Title | NetSIO Protocol |
Author | Andrew Diller / Jan Krupa |
Status | Draft |
Type | Informational |
Created | 2025-04-23 |
Version | 1.0 |
Input | Jan, Mozzwald, TCH |
Abstract
NetSIO is a lightweight protocol for transmitting Atari SIO (Serial Input/Output) signals and data over UDP, enabling network-based communication with peripherals like FujiNet. While no existing Atari emulator natively supports NetSIO, developers can implement direct support by writing code to handle the protocol and communicate with FujiNet devices. netsio-hub.py, written in Python3, serves as a reference implementation that translates Altirra emulator messages into NetSIO datagrams and vice versa.
Current Implementations
- NetSIO Hub (python) - https://github.com/FujiNetWIFI/fujinet-emulator-bridge
- NetSIO Able Archer (c89) - https://github.com/dillera/atari800
- NetSIO Mozzable (c89) - https://github.com/mozzwald/atari800
Notes about NSIO - need to be organized
Connection Phase
- fujinet-pc is repeatedly sending ping requests (0xC2) until the ping response (0xC3) is received from netsiohub and now atrari800
- then fujinet-pc sends device connected (0xC1) to hub/a800
- hub/a800 needs to "remember" source IP:port of device connected (0xC1) message - this is address of our connected device (fujinet-pc) to communicate with
- There is no specific connection acknowledgment from hub/a800 back to fujinet-pc
Request Response Flows
- ping: to test if the hub is running
- connected/disconnected: to "register device" on hub for messages
- alive: notify we are still there, intereted for messages
- credit: flow control
Credit
- If FN-PC needs to send msg to hub/a800 but is out of credit, then it first asks for new credit via credit status (0xC6). Hub/a800 should respond with updated credit (0xC7), if/when emulated Atari is (almost) ready to process new message (serial data or signal). In other words, credit system allows FN-PC to emulator flow control.
Frames
- command frame should be three UDP packets (NetSIO messages):
- #1 UDP payload: 0x11
- #2 UDP payload: 0x02, followed by 4 bytes of CF + CF checksum
- #3 UDP payload: 0x81, syncNumber
Atari800 Integration
SIO Patch
-
SIO_SwitchCommandFrame() - traditional SIO path
-
SIO_Handler() - for SIO patch and increased cassette speeds
-
SIO_SwitchCommandFrame is called from pia.c
There are multiple paths for SIO command frames in atari800 emulator. By default is uses "patch emulation" mode which passes everything through SIO_Handler(). If you turn off patch emulation with command line switch -nopatchall then it does take the different path for command frames. I'm not sure which is better path
It seems, SIO_Handler() is shortcut/patch for calling $E459, SIO call is handled by a800 emulator, not by Atari (emulated) For now I suggest to go non-patch way. Start with hook in SIO_PutByte() and SIO_SwitchCommandFrame(). Most simple would be to send data byte msg (0x01) in SIO_PutByte() and to send command ON/OFF (0x11/0x10) in SIO_SwitchCommandFrame() If this works, multiple single data byte msgs can be replaced with data block msg (0x02). Expected result is that the command frame is received on FN-PC. Then we can take a look on opposite direction, command frame should be ACKed by FN-PC ... we will need something in SIO_GetByte()
Original Documentation From Jan
NetSIO
NetSIO is simple protocol to transmit signals and data bytes of Atari SIO (serial) port over network. UDP datagrams are used to exchange NetSIO messages between emulated Atari and NetSIO enabled peripherals, like FujiNet.
At the time of writing, no Atari emulator can speak directly NetSIO protocol. The middle component which can communicate with an emulator on one end and NetSIO protocol on the other end is needed - sort of bridge or hub. Such a middle component is netsio-hub.py
(written in Python3). It listens to Altirra messages (via interface for Altirra's custom devices) and translates them into NetSIO messages (UDP datagrams) and vice versa.
NetSIO protocol
Message | ID | Parameters |
---|---|---|
Data byte | 0x01 | data_byte: uint8 |
Data block | 0x02 | byte_array: uint8[] |
Data byte and Sync request | 0x09 | data_byte: uint8, sync_number: uint8 |
Command ON | 0x11 | |
Command OFF | 0x10 | |
Command OFF and Sync request | 0x18 | sync_number: uint8 |
Motor ON | 0x21 | |
Motor OFF | 0x20 | |
Proceed ON | 0x31 | |
Proceed OFF | 0x30 | |
Interrupt ON | 0x41 | |
Interrupt OFF | 0x40 | |
Speed change | 0x80 | baud: uint32 |
Sync response | 0x81 | sync_number: uint8, ack_type: uint8, ack_byte: uint8, write_size: uint16 |
Connection management | ||
Device connected | 0xC1 | |
Device disconnected | 0xC0 | |
Ping request | 0xC2 | |
Ping response | 0xC3 | |
Alive request | 0xC4 | |
Alive response | 0xC5 | |
Credit status | 0xC6 | |
Credit update | 0xC7 | |
Notifications | ||
Warm reset | 0xFE | |
Cold reset | 0xFF |
With the exception to the ping the device must be first connected (Device connected) to be able to participate in NetSIO communication.
Data byte
Data Byte | |
---|---|
ID | 0x01 |
Direction | Atari -> Device, Device -> Atari |
Parameters | data_byte: uint8 - byte to transfer |
Transfers the SIO data byte from Atari to Device or from Device to Atari.
Used to, but not limited to, transfer completion byte 'C' or checksum byte.
Data block
Data block | |
---|---|
ID | 0x02 |
Direction | Atari -> Device, Device -> Atari |
Parameters | byte_array: uint8[] - one or more bytes to transfer (up to 512) |
Transfers multiple data bytes from Atari to Device or from Device to Atari.
Data byte and Sync request
Data Byte and Sync request | |
---|---|
ID | 0x09 |
Direction | Atari -> Device |
Parameters | data_byte: uint8 - byte to transfer |
sync_number: uint8 - sync request number |
Transfers the SIO data byte from Atari to Device together with the request to synchronize on next byte from Device to Atari. Atari emulation is paused waiting for Sync response.
Used on last byte (checksum) of SIO write command when Atari is sending data frame to the peripheral and expects the acknowledgment byte (ACK or NAK) to be delivered withing 850 us to 16 ms. The acknowledgment byte will be sent from device as Sync response. Atari emulation is resumed after Sync response is delivered to the emulator. This pause-resume mechanism allows to extend the 16 ms requirement for the acknowledgment delivery.
sync request number
is incremented with every Sync request sent. It is used to match corresponding Sync response.
Command ON
Command ON | |
---|---|
ID | 0x11 |
Direction | Atari -> Device |
Parameters | none |
Command was asserted. Atari indicates to all connected devices the start of command frame.
Note: The command pin uses negative logic. Active command means low voltage on corresponding SIO pin and inactive command is high on SIO pin.
Command OFF
Command OFF | |
---|---|
ID | 0x10 |
Direction | Atari -> Device |
Parameters | none |
Command was de-asserted. Atari indicates to all connected devices the end of command frame.
Note: The command pin uses negative logic.
Note: Currently not used, see Command OFF and Sync request
Command OFF and Sync request
Command OFF and Sync request | |
---|---|
ID | 0x18 |
Direction | Atari -> Device |
Parameters | sync_number: uint8 - sync request number |
Command was de-asserted. Atari indicates to all connected devices the end of command frame together with the request to synchronize on next byte from Device to Atari. Atari emulation is paused waiting for Sync response.
When Atari is sending command frame to the peripheral it expects the acknowledgment byte (ACK or NAK) to be delivered withing 16 ms. The acknowledgment byte will be sent from device as Sync response. Atari emulation is resumed after Sync response is delivered to the emulator. This pause-resume mechanism allows to extend the 16 ms requirement for the acknowledgment delivery.
sync request number
is incremented with every Sync request sent. It is used to match corresponding Sync response.
Motor ON
Motor OFF | |
---|---|
ID | 0x21 |
Direction | Atari -> Device |
Parameters | none |
Cassette player motor on. Atari starts the cassette motor.
Motor OFF
Motor OFF | |
---|---|
ID | 0x20 |
Direction | Atari -> Device |
Parameters | none |
Cassette player motor off. Atari stops the cassette motor.
Proceed ON
Proceed OFF
Proceed ON | |
---|---|
ID | 0x41 |
Direction | Device -> Atari |
Parameters | none |
Proceed OFF | |
---|---|
ID | 0x40 |
Direction | Device -> Atari |
Parameters | none |
The device indicates to the Atari that it needs some attention. Used by FujiNet to indicate there is a data available for read.
Note: The proceed pin uses negative logic.
Interrupt ON
Interrupt OFF
Interrupt ON | |
---|---|
ID | 0x31 |
Direction | Device -> Atari |
Parameters | none |
Interrupt OFF | |
---|---|
ID | 0x30 |
Direction | Device -> Atari |
Parameters | none |
Similar to proceed
, the device indicates to the Atari that the device needs some attention.
Note: The interrupt pin uses negative logic.
Speed change
Speed change | |
---|---|
ID | 0x80 |
Direction | Atari -> Device, Device -> Atari |
Parameters | baud: uint32 - 4 bytes little-endian baud |
Indicates the outgoing data rate has changed. Next Data byte or Data block will be transmitted at specified rate.
The speed has an effect when transferring data to emulated Atari. It will appear as Data bits will "arrive" to SIO Data In pin at specified rate.
In opposite direction, when NetSIO device is receiving data, the bitrate of data is just complementary information. However this information can be used to simulate errors in case the device is currently expecting data to arrive at different bitrate. E.g. this is used by FujiNet to toggle speed between standard 19200 and high speed when specific error threshold is reached.
Sync response
Sync response | |
---|---|
ID | 0x81 |
Direction | Device -> Atari |
Parameters | sync_number: uint8 - sync request number |
ack_type: uint8 - acknowledgment type | |
ack_byte: uint8 - acknowledgment byte | |
write_size: uint16 - LSB+MSB write size next sync |
Response to Command OFF and Sync request or Data byte and Sync request. Atari emulation is paused after sending sync request and it's waiting for Sync response. After Sync response is delivered the emulation is resumed.
The purpose of Sync request-response mechanism is to allow SIO acknowledgment (ACK/NAK) in time delivery. There are two scenarios when ACK/NAK is expected. First scenario, anytime when Atari sends command frame to all connected devices, it expects acknowledgment byte from device which will handle the command. Second scenario is for SIO write command, when Atari sends data frame (data part of SIO write command), it expects the acknowledgment of successful delivery. In both cases the acknowledgment delivery is expected no later then 16 ms after command frame or data frame was sent by Atari. With emulation pause and resume it's possible to meet this timing requirement even with NetSIO devices connected over latent networks.
-
sync request number
matchessync request number
from Sync request. -
acknowledgment type
0 = Empty acknowledgment (device is not interested into this command),
acknowledgment byte
andwrite size next sync
are ignored. This allows to resume the emulation in case there is no acknowledgment from any device.1 = Valid acknowledgment,
acknowledgment byte
will be sent to Atari. -
acknowledgment byte
is a byte the Atari is waiting for. For standard SIO it is ACK (65, 'A') or NAK (78, 'N'). -
write size next sync
this is used to "plan" next Sync request for SIO write command.non zero value = current command is SIO write and next acknowledgment (via Sync request-response) is expected after this amount of bytes will be sent from Atari to the device.
0 = do not "plan" next sync
Device connected
Device connected | |
---|---|
ID | 0xC1 |
Direction | Device -> hub |
Parameters | none |
The device was connected to NetSIO bus. NetSIO messages from Atari will be sent to the device and messages from the device will be delivered to Atari.
Device disconnected
Device disconnected | |
---|---|
ID | 0xC0 |
Direction | Device -> hub |
Parameters | none |
The device was disconnected from NetSIO bus. It will not receive NetSIO messages anymore and messages from it will not be delivered to Atari anymore.
Ping request
Ping response
Ping request | |
---|---|
ID | 0xC2 |
Direction | Device -> hub |
Parameters | none |
Ping response | |
---|---|
ID | 0xC3 |
Direction | hub -> Device |
Parameters | none |
Allows the device to test the availability of NetSIO hub. Similar to ICMP ping, it can be used to measure network round trip time between device and the hub.
Alive request
Alive response
Alive request | |
---|---|
ID | 0xC4 |
Direction | Device -> hub |
Parameters | none |
Alive response | |
---|---|
ID | 0xC5 |
Direction | hub -> Device |
Parameters | none |
The device informs the hub, that the device is still connected and interested into communication. The device must send the Alive request
in regular intervals (every TBD). In turn, the hub must send Alive response
to the device to let the device know the connection is still established.
Credit status
Credit update
Credit status | |
---|---|
ID | 0xC6 |
Direction | Device -> hub |
Parameters | credit: uint8 - remaining credit on device |
Credit update | |
---|---|
ID | 0xC7 |
Direction | hub -> Device |
Parameters | credit: uint8 - credit given to device |
Device uses a credit system for sending NetSIO messages which should be processed by emulator (data bytes, proceed, interrupt). Processing of these messages on emulator can take some time (e.g. if emulator emulates POKEY receiving a byte). When such a message is sent one credit is consumed. If device is out of credit it informs the hub and then waits for additional credit from hub before sending the message. This mechanism prevents the queue on emulator side to be overfilled with incoming messages, whereas it allows few messages to be waiting in that queue for processing.
Warm reset
Warm reset | |
---|---|
ID | 0xFE |
Direction | Atari -> device |
Parameters | none |
Informs the connected device the emulated Atari did warm reset.
Cold reset
Cold reset | |
---|---|
ID | 0xFF |
Direction | Atari -> device |
Parameters | none |
Informs the connected device the emulated Atari did cold reset, i.e. power cycle. The device might react to this message by resetting itself to simulate the situation when the device is powered from Atari.