Developing Trinity-specific code has typically meant assembling directly on real SAM hardware, or assembling on the PC and transferring the program over to SAM. In my case the latter involved writing the disk image out from pyz80 to an SD card using SAMdisk, moving the card over to Trinity, rebooting SAM to have the new card recognised, re-selecting the development record, then loading booting or loading the program. Despite the benefits of a familiar PC code editor and faster assembler, the transfer process was still a chore.

I had a similar experience using homebrew code on the Sega Dreamcast. The earliest method was to burn content to a CD, but that was terribly slow and wasteful (re-writable CDs didn’t work). Next best was to push code to it over a serial cable, which was better but still became a chore as programs grew in size. The best option was to use the BroadBand Adapter and push code to it over the network. This required booting a helper utility (“dcload-ip”) from CD, which listened for and executed any code sent to it.

Given one of Trinity’s features is an ethernet adapter, it made sense to do something similar for SAM — and so TrinLoad was created!

My initial requirements were:

  • be discoverable from a desktop PC.
  • accept code or data over the network, written to a given page and offset.
  • execute from a given page and offset.
  • simple implementation on SAM to minimise RAM footprint (and work!).

Using a UDP broadcast for discovery seemed like a no-brainer. SAM would always be on the same sub-net, and UDP is a simple connectionless protocol to implement. I chose to use UDP port EDB0, with a single byte payload of “?” as my discovery request. Any listening SAM machines would respond with “!” to indicate their availability. The UDP response would automatically include their IP address for any further communications. As an added bonus I included handlers for ARP who-has and ICMP echo, allowing SAM to respond to pings.

TCP would be the natural choice for reliable data communications, but without a network stack we’d have to implement it ourselves. For that reason I decided to stick with UDP for the data transmission, using the same port as before. Each packet would be ACKed on receipt, to confirm successful delivery, and to act as a transmission throttle so Trinity’s receive buffer didn’t overflow. The data format begins with a 4-byte header: “@” for a type indicator, followed by the target page number, then a 16-bit page offset in little-endian format. This is followed immediately by the data to write to that location. No length is needed as it can be calculated from the UDP data length, minus the 4-byte header. Data transfers average 29K/s, which includes the network receive, copying into place, and ACK.

The longest data block we can transfer in a single packet is 1468 bytes. This is calculated from the ethernet packet data size (1500 bytes), minus headers for IPv4 (20 bytes), UDP (8 bytes), and our data header (4 bytes). Longer blocks must be split into multiple packets, with the client program advancing the offset and page in each one.

After transferring the code we can execute it using a package beginning with &”X”. This is followed by a page number to write to HMPR, and a 16-bit address to start execution. LMPR points to the normal BASIC location, with ROM0 enabled, so you’re free to load small routines at address 0x4000 if you want to. The only area to avoid is 0x6000-0x7fff, which is used for TrinLoad code and ethernet buffer. If the calling environment is preserved, returning from your test code will drop back into TrinLoad, ready to receive the next build.

To further streamline the process you can also start TrinLoad automatically on boot-up. This requires special versions of the Trinity flash code and Trinity BDOS, to skip the SD card reporting delay and auto-boot record 1. Then simply add a small auto-boot BASIC program to record 1, to switch to the TrinLoad record and load it. The final piece of the puzzle is an enhanced SAMdisk, with a special sam: target to find a SAM on the local network and send a binary to it from a disk image. Adding this to an existing pyz80 build process makes testing code on real hardware easier than ever.

Potential future uses of the network link:

  • read and write records on the SD card from the PC.
  • dump floppy disks (even custom formats) to a disk image on the PC.
  • link to modified TurboMON for single stepping and software breakpoints.
  • custom stream for read/write link to the PC from BASIC.

A pre-built disk image can be downloaded here, for use with SAMdisk v3.8.5 or later. Boot this on your real SAM with Trinity attached, then send your pyz80 disk image output to it using:

SAMdisk image.dsk sam:

The source code for TrinLoad is available on GitHub.