Code: Select all
The latest version of this code is always at
This is a Unix TubeHost filesystem written in perl based around JGH's
In this model, the HostFS ROM makes "Tube" like communication but using
the RS423 serial port instead. Since RS423 is a single channel in each
directions, commands are embedded in the datastream. Essentially the
HostFS passes standard BBC calls (eg OSBGET, OSARGS, OSFSC) and data
structures across the channel, and all the complicated processing is
performed on the remote server (aka TubeHost or Host, for short).
Now this design (whilst slightly idiosyncratic - eg in how file data is
transferred) is very clever and flexible. Ultimately it's up to the Host
as to how to present a filesystem. *COMMANDS are also interpreted by
the Host. In JGH's reference code he has a BASIC TubeHost server (and a
precompiled Windows .exe from it). This has an ADFS look and feel; it's
a hierarchical filesystem, with *DIR enter subdirectories, and "." as the
directory separator and so on. His code translates Host<->Beeb filenames
and presents them in a form native to the Beeb.
I took a more DFS-like approach, taking ideas from MMB structures. In
my system we have a single level tree (eg $HOME/Beeb_Disks). Each Unix
subdirectory becomes an available disk which can be "inserted" into a
drive (using the *DIN) command. There are 10 drives available (0->9).
If the Unix directory is numbered then we assign this disk to a slot of
that number; any unnumbered directories are assigned sequential numbers
So, for example, on Unix:
% ls Beeb_Disks
0.Mr_Ee 1.test1 10.test10 TAPES
We would see this on the Beeb:
"TAPES" was assigned "11" because it was the next free slot.
The rationale behind this structure was to make SSDs easier to handle.
Using MMB_utils ( http://sweh.spuddy.org/Beeb/mmb_utils.html ) we could
extract the contents of an SSD into a directory and now it shows up on
the Beeb exactly as it should.
% cd Beeb_Disks
% beeb getfile ~/Cylon_Attack.ssd Cylon_Attack
Saving $.Ca as Ca
Saving $.Cylon as Cylon
Saving $.!BOOT as !BOOT
% ls Cylon_Attack
!BOOT !BOOT.inf Ca Ca.inf Cylon Cylon.inf
>*DIN 5 Cylon_Attack
>REM could also have done *DIN 5 11 'cos the assigned number was 11
$.Cylon L FF1900 FF8023 00237E
$.Ca L 001100 00257D 004900
$.!BOOT 000000 000000 000025
We can see the load/exec/locked attributes have been mainted, due to the
INF files (which should be bbcim compatible).
When TubeHost starts up, any disks with numbers 0->9 are pre-loaded into
slots 0->9. You can see the current disks loaded:
Disk 0: 0.Mr_Ee
Disk 1: 1.test1
Disk 5: Cylon_Attack
Current drive: 5
Current directory: $ (:5:$)
Current library: :0.$
0x0C => 'OSARGS',
Y=0, A=0 return current filesystem in A (Also handled in HOSTFS ROM)
Y=0, A=1 return rest of command line (Handled in HOSTFS ROM)
Y=0, A=255 sync all open files
Y!=0, A=0 Get PTR#
Y!=0, A=1 Set PTR#
Y!=0, A=2 Read length (EXT#)
Y!=0, A=3 Set length (EXT#) [ see note ]
Y!=0, A=255 Sync this file
0x0E => 'OSBGET',
Y=filehandle, read byte from file into A, C set if EOF before read occured
0x10 => 'OSBPUT',
Y=filehandle, write byte from A into file
0x12 => 'OSFIND',
A=0, close file Y
0x14 => 'OSFILE',
01 rewrite load/exec/attr
02 rewrite load
03 rewrite exec
04 rewrite attr
05 read catalog entry
07 Create empty file of defined size
0x16 => 'OSGBPB',
A=1 write bytes to new ptr
A=2 write bytes to current ptr
A=3 get bytes from new ptr
A=4 get bytes from current ptr
A=5 Read disks title, boot opts
A=6 read directory name
A=7 read library name
A=8 read files from directory
0x18 => 'OSFSC',
00 *OPT [ see note ]
06 New filesystem (handled by HOSTFS ROM)
07 Handles (handled by HOSTFS ROM? Done here as 0x80-0x9F just in case)
08 *command alert (dropped by HOSTFS ROM)
0B *RUN for library [ see note ]
OSARGS Y!=0, A=3 Set length (EXT#) - Neither B nor Master support this;
the code is there but I dunno if it'll work
OSFILE 07 Create empty file of defined size - appears to work.
00 *OPT - "*OPT 5" sets debug level on host.
"*OPT 6" sets speed delays (see below)
No other *OPT value does anything
0B *RUN for library - not sure how to verify this one works!
A=5 Read disks title, boot opts - disk name truncated to 12 chars if
A=8 read files from directory - This appears to work. Note that Unix
filenames may be long and BBC programs may expect 7 or 10 character
limits; not a bug in TubeHost but in Beeb programs
A full Tube Host should also handle other types of calls (eg OSRDCHIO).
These are not handled by this program; we are a pure filesystem server
and not a generic Host application. Calls *NOT* handled:
0x00 => 'OSRDCHIO',
0x02 => 'OSCLI',
0x04 => 'OSBYTELO',
0x06 => 'OSBYTEHI',
0x08 => 'OSWORD',
0x0A => 'OSWORD0',
can be prefixed with a ! if a ROM or OS commands conflicts
Displays status of Host, including what disks are in what slots,
current directory/library status, and what files are currently open.
"D" limits to disks/directories; "F" limits to open files
Shows what disks (Unix subdirectories) are available
Creates a new Unix subdirectory
*DIN drive disk
Inserts "disk" into drive
Ejects "disk" from drive
As per DFS
Causes TubeHost to re-exec itself, which effectively resets it
to startup mode (default disks, default directories, no open files etc)
*RENAME file1 file2
*ACCESS file [L]
As per DFS
Test emulation of *DUMP performed on Host side
Debug command; sends "count" number of X's to the screen
This is pure perl code. To talk to a serial port you need the
Device::SerialPort module. I'm not sure if I'm making any hard assumptions
around Unix/Linux so this may work on other platforms.
./TubeHost [-s|-u] device [speed]
"-s" ==> serial device (default)
"-u" ==> Unix domain socket
device ==> filename to connect to
speed ==> Baud rate (only for serial; 9600 default).
"Unix domain socket" is for VirtualBox users and BeebEm. It's possible
to configure VirtualBox so the client OS (Windows, for example) has its
serial port mapped to a Unix domain socket. Then BeebEm can connect its
serial port to the client OS serial port. In this way TubeHost can talk
to HostFS inside BeebEm inside Virtualbox. It's how I did my debugging!
Now HostFS and TubeHost is totally reliant on a clean serial channel.
I've found, in testing, that sending data too quickly can cause
characters to be lost, and thus corruption. I've seen this with both
real serial ports and the VirtualBox emulated serial port. It's unclear
if the issue is in HostFS or in my serial cable and the Linux/Virtualbox
serial emulation. To work around this the serial send routines
have delays built into them; after "A" characters have been sent, delay
for "B" microseconds. The default is "A=1,B=1", which causes a 1ms
delay after each character sent. Throughput on a 9600 baud connection
works out around 720cps. Not as fast as floppy, but a lot faster than
These values can be tuned with "*OPT6". Convert AB into hex; &AB (or
A*16+B) and pass to *OPT6. So the default is, effectively, *OPT6 17.
If you are finding communication issues you can use *OPT6 and "*FOO"
to help tune these timings. Once you have good values then you can, of
course, hard code them into the script.