312 lines
11 KiB
Plaintext
312 lines
11 KiB
Plaintext
|
# TRS-80 Model 4p
|
||
|
|
||
|
The TRS-80 (models 1, 3 and 4) are among the most popular z80
|
||
|
machines. They're very nicely designed and I got my hands on a
|
||
|
4p with two floppy disk drives and a RS-232 port. In this
|
||
|
recipe, we're going to get Collapse OS running on it.
|
||
|
|
||
|
# Not entirely standalone
|
||
|
|
||
|
Collapse OS uses the TRS-80 drivers rather than its own. On most
|
||
|
TRS-80 models, those drivers are on ROM, but in the case of the
|
||
|
4P model, those drivers are on the TRSDOS disk (well, from what
|
||
|
I understand, not all of it, but still, a big part of it).
|
||
|
|
||
|
It would be preferable to develop drivers from scratch, but it
|
||
|
represents a significant effort for a modest payout (because
|
||
|
it's only actually useful when you want to use a 4P model that
|
||
|
has no TRSDOS disk).
|
||
|
|
||
|
Maybe those drivers will be developed later, but it's not a
|
||
|
priority for now.
|
||
|
|
||
|
# Floppy or RS-232?
|
||
|
|
||
|
There are many ways to get Collapse OS to run on it. One would
|
||
|
involve writing it to a floppy. I bought myself old floppy
|
||
|
drives for that purpose, but I happen to not have any functional
|
||
|
computer with a floppy port on it. I still have the motherboard
|
||
|
of my old pentium, but I don't seem to have a video card for it
|
||
|
any more.
|
||
|
|
||
|
Because my 4p has a RS-232 port and because I have equipment to
|
||
|
do serial communication from modern machines (I didn't have a
|
||
|
DB-9 to DB-25 adapter though, I had to buy one), I chose that
|
||
|
route.
|
||
|
|
||
|
# Gathering parts
|
||
|
|
||
|
* A TRS-80 model 4p with a RS-232 port
|
||
|
* A TRSDOS 6.x disk
|
||
|
* A means to do serial communication. In my case, that meant:
|
||
|
* A USB-to-serial device
|
||
|
* A null modem cable
|
||
|
* A DB-9 gender changer
|
||
|
* A DB-9 to DB-25 adapter
|
||
|
|
||
|
# Overview
|
||
|
|
||
|
We need to send sizeable binary programs through the RS-232 port
|
||
|
and then run it. The big challenge here is ensuring data
|
||
|
integrity. Sure, serial communication has parity check, but it
|
||
|
has no helpful way of dealing with parity errors. When parity
|
||
|
check is enabled and that a parity error occurs, the byte is
|
||
|
simply dropped on the receiving side. Also, a double bit error
|
||
|
could be missed by those checks.
|
||
|
|
||
|
What we'll do here is to ping back every received byte back and
|
||
|
have the sender do the comparison and report mismatched data.
|
||
|
|
||
|
Another problem is ASCII control characters. When those are sent
|
||
|
across serial communication channels, all hell breaks lose. When
|
||
|
sending binary data, those characters have to be avoided. We use
|
||
|
tools/ttysafe for that.
|
||
|
|
||
|
Does TRSDOS have a way to receive this binary inside these
|
||
|
constraints? Not to my knowledge. As far as I know, the COMM
|
||
|
program doesn't allow this.
|
||
|
|
||
|
What are we going to do? We're going to punch in a binary
|
||
|
program to handle that kind of reception! You're gonna feel real
|
||
|
badass about it too...
|
||
|
|
||
|
# Keyboard tips
|
||
|
|
||
|
* "_" is CLEAR+ENTER.
|
||
|
|
||
|
# Building the binary
|
||
|
|
||
|
You can build the binary to send to the TRS-80 with "make" in
|
||
|
/arch/z80/trs80, which will yield
|
||
|
"os.bin". You'll need it later.
|
||
|
|
||
|
# Testing serial communication
|
||
|
|
||
|
The first step here is ensuring that you have bi-directional
|
||
|
serial communication. To do this, first prepare your TRS-80:
|
||
|
|
||
|
set *cl to com
|
||
|
setcomm (word=8,parity=no)
|
||
|
|
||
|
The first line loads the communication driver from the COM/DRV
|
||
|
file on the TRSDOS disk and binds it to *cl, the name generally
|
||
|
used for serial communication devices. The second line sets
|
||
|
communication parameters in line with what is generally the
|
||
|
default on modern machine. Note that I left the default of 300
|
||
|
bauds as-is.
|
||
|
|
||
|
Then, you can run "COMM *cl" to start a serial communication
|
||
|
console.
|
||
|
|
||
|
Then, on the modern side, use your favorite serial communication
|
||
|
program and set the tty to 300 baud with option "raw". Make sure
|
||
|
you have -parenb.
|
||
|
|
||
|
If your line is good, then what you type on either side should
|
||
|
echo on the other side. If it does not, something's wrong.
|
||
|
Debug.
|
||
|
|
||
|
# Punching in the goodie
|
||
|
|
||
|
As stated in the overview, we need a program on the TRS-80 that:
|
||
|
|
||
|
1. Listens to *cl
|
||
|
2. Echoes each character back to *cl
|
||
|
3. Adjusts ttysafe escapes
|
||
|
4. Stores received bytes in memory
|
||
|
|
||
|
You're in luck: that program has already been written. It's in
|
||
|
B612 and B613 in /arch/z80/trs80/blk. By running "make" earlier,
|
||
|
you have, in your current folder, a blkfs that contains those
|
||
|
blocks. To have a forth running on it, you could overwrite
|
||
|
/cvm/blkfs and run /cvm/forth. You can then compile it with:
|
||
|
|
||
|
5 LOAD ( z80 assembler )
|
||
|
0x0238 CONSTANT COM_DRV_ADDR
|
||
|
0x3000 CONSTANT DEST_ADDR
|
||
|
612 LOAD
|
||
|
613 LOAD
|
||
|
|
||
|
Then, you can use "DUMP" to visualize the data you'll need to
|
||
|
punch in:
|
||
|
|
||
|
H@ ORG @ - ORG @ DUMP
|
||
|
|
||
|
It can run from any offset (all jumps in it are relative), but
|
||
|
writes to DEST_ADDR. Make sure you don't place it in a way to be
|
||
|
overwritten by its received data.
|
||
|
|
||
|
Wondering what is that COM_DRV_ADDR constant? That's the DCB
|
||
|
handle of your *cl device. You will need to get that address
|
||
|
before you continue. Go read the following section and come back
|
||
|
here.
|
||
|
|
||
|
How will you punch that in? The "debug" program! This very
|
||
|
useful piece of software is supplied in TRSDOS. To invoke it,
|
||
|
first run "debug (on)" and then press the BREAK key. You'll get
|
||
|
the debug interface which allows you to punch in any data in any
|
||
|
memory address. Let's use 0x5000 which is the offset it's
|
||
|
designed for.
|
||
|
|
||
|
For reference: to go back to the TRSDOS prompt, it's
|
||
|
"o<return>".
|
||
|
|
||
|
First, display the 0x5000-0x503f range with the d5000<space>
|
||
|
command (I always press Enter by mistake, but it's space you
|
||
|
need to press). Then, you can begin punching in with
|
||
|
h5000<space>. This will bring up a visual indicator of the
|
||
|
address being edited. Punch in the stuff with a space in between
|
||
|
each byte and end the edit session with "x".
|
||
|
|
||
|
# Getting your DCB address
|
||
|
|
||
|
In the previous step, you need to set COM_DRV_ADDR to your "DCB"
|
||
|
address for *cl. That address is your driver "handle". To get
|
||
|
it, first get the address where the driver is loaded in memory.
|
||
|
You can get this by running "device (b=y)". That address you see
|
||
|
next to *cl? that's it. But that's not our DCB.
|
||
|
|
||
|
To get your DBC, go explore that memory area. Right after the
|
||
|
part where there's the *cl string, there's the DCB address
|
||
|
(little endian). On my setup, the driver was loaded in 0x0ff4
|
||
|
and the DCB address was 8 bytes after that, with a value of
|
||
|
0x0238. Don't forget that z80 is little endian. 38 will come
|
||
|
before 02.
|
||
|
|
||
|
# Saving that program for later
|
||
|
|
||
|
If you want to save yourself typing for later sessions, why not
|
||
|
save the program you've painfully typed to disk? TRSDOS enables
|
||
|
that easily. Let's say that you typed your program at 0x5000 and
|
||
|
that you want to save it to RECV/CMD on your second floppy
|
||
|
drive, you'd do:
|
||
|
|
||
|
dump recv/cmd:1 (start=x'5000',end=x'5030',tra='5000')
|
||
|
|
||
|
A memory range dumped this way will be re-loaded at the same
|
||
|
offset through "load recv/cmd:1". Even better, TRA indicates
|
||
|
when to jump after load when using the RUN command. Therefore,
|
||
|
you can avoid all this work above in later sessions by simply
|
||
|
typing "recv" in the DOS prompt.
|
||
|
|
||
|
Note that you might want to turn "debug" off for these commands
|
||
|
to run. I'm not sure why, but when the debugger is on, launching
|
||
|
the command triggers the debugger.
|
||
|
|
||
|
# Sending binary through the RS-232 port
|
||
|
|
||
|
Once you're finished punching your program in memory, you can
|
||
|
run it with g5000<enter> (not space). If you've saved it to
|
||
|
disk, run "recv" instead. Because it's an infinite loop, your
|
||
|
screen will freeze. You can start sending your data.
|
||
|
|
||
|
To that end, there's the tools/pingpong program. It takes a
|
||
|
device and a filename to send. Before you send the binary, make
|
||
|
it go through tools/ttysafe first (which just takes input from
|
||
|
stdin and spits tty-safe content to stdout):
|
||
|
|
||
|
./ttysafe < stage1.bin > stage1.ttysafe
|
||
|
|
||
|
On OpenBSD, the invocation can look like:
|
||
|
|
||
|
doas ./pingpong /dev/ttyU0 os.ttysafe
|
||
|
|
||
|
You will be prompted for a key before the contents is sent. This
|
||
|
is because on OpenBSD, TTY configuration is lost as soon as the
|
||
|
TTY is closed, which means that you can't just run stty before
|
||
|
running pingpong. So, what you'll do is, before you press your
|
||
|
key, run "doas stty -f /dev/ttyU0 300 raw" and then press any
|
||
|
key on the pingpong invocation.
|
||
|
|
||
|
If everything goes well, the program will send your contents,
|
||
|
verifying every byte echoed back, and then send a null char to
|
||
|
indicate to the receiving end that it's finished sending. This
|
||
|
will end the infinite loop on the TRS-80 side and return. That
|
||
|
should bring you back to a refreshed debug display and you
|
||
|
should see your sent content in memory, at the specified address
|
||
|
(0x3000 if you didn't change it).
|
||
|
|
||
|
If there was no error during pingpong, the content should be
|
||
|
exact. Nevertheless, I recommend that you manually validate a
|
||
|
few bytes using TRSDOS debugger before carrying on.
|
||
|
|
||
|
*debugging tip*: Sometimes, the communication channel can be a
|
||
|
bit stubborn and always fail, as if some leftover data was
|
||
|
consistently blocking the channel. It would cause a data
|
||
|
mismatch at the very beginning of the process, all the time.
|
||
|
What I do in these cases is start a "COMM *cl" session on one
|
||
|
side and a screen session on the other, type a few characters,
|
||
|
and try pingpong again.
|
||
|
|
||
|
# Saving to disk
|
||
|
|
||
|
If everything went well, you could run Collapse OS with
|
||
|
g3000<return>. But instead of doing that, why not save it to
|
||
|
disk?
|
||
|
|
||
|
dump cos/cmd:1 (start=x'5000',end=x'7000',tra='5000')
|
||
|
|
||
|
And there we go! A Collapse OS launchable from floppy!
|
||
|
|
||
|
# Sending blkfs to floppy
|
||
|
|
||
|
As it is, your system fully supports reading and writing to
|
||
|
floppy drive 1. It also had *CL< to read a char from *cl and
|
||
|
*CL> to emit a char to *cl.
|
||
|
|
||
|
That's all you need to have a full Collapse OS with access to
|
||
|
disk blocks.
|
||
|
|
||
|
First, make sure your floppies are formatted. Collapse OS is
|
||
|
currently hardcoded to single side and single density, which
|
||
|
means there's a limit of 100 blocks per disk.
|
||
|
|
||
|
You'll need to send those blocks through RS-232. Begin by taking
|
||
|
over the prompt:
|
||
|
|
||
|
' *CL> ' EMIT **!
|
||
|
' *CL< ' KEY **!
|
||
|
|
||
|
See B80 for details about those RAM offsets. Your serial link
|
||
|
now has the prompt. You will also have to make your newlines
|
||
|
CRLF. The TRS-80 wants CR only, but serial communications (and
|
||
|
blkup) expect CRLF:
|
||
|
|
||
|
' CRLF ' NL **!
|
||
|
|
||
|
Now, you can use /tools/blkup to send a disk's contents. First,
|
||
|
extract the first 100 blocks from blkfs:
|
||
|
|
||
|
dd if=emul/blkfs bs=1024 count=100 > d1
|
||
|
|
||
|
Now, insert your formatted disk in drive 1 and push your blocks:
|
||
|
|
||
|
tools/blkup /dev/ttyUSB0 0 d1
|
||
|
|
||
|
It takes a while, but you will end up having your first 100
|
||
|
blocks on floppy! Go ahead, LIST around. Then, repeat for other
|
||
|
disks.
|
||
|
|
||
|
# Floppy organisation
|
||
|
|
||
|
Making blkfs span multiple disk is a bit problematic with
|
||
|
regards to absolute block references in the code. You'll need to
|
||
|
work a bit to design your very own Collapse OS floppy set. See
|
||
|
/doc/usage.txt for details.
|
||
|
|
||
|
# Coming back to keyboard
|
||
|
|
||
|
Once you're done, you will want to go back to local control:
|
||
|
|
||
|
' CR ' NL **!
|
||
|
' (emit) ' EMIT **!
|
||
|
' (key) ' KEY **!
|
||
|
|
||
|
# Self-hosting
|
||
|
|
||
|
As it is, your installment of Collapse OS is self-hosting using
|
||
|
instructions from /doc/selfhost.txt. The difference is that
|
||
|
instead of writing the binary you have in memory to EEPROM,
|
||
|
you'll quit to TRSDOS with BYE and use TRSDOS' DUMP utility to
|
||
|
save to disk like you already did before.
|