mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-17 09:38:05 +11:00
91f79d1131
The idea is to consider assemblers as "runtime" apps instead of placing them in the "bootstrap" section of the blocks. These apps will be used for much more than bootstrapping. Moved its documentation to doc/asm.txt and made its code blocks more compact.
279 lines
11 KiB
Markdown
279 lines
11 KiB
Markdown
# 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.
|
|
|
|
![Collapse OS on a TRS-80 Model 4P](collapseos-on-trs80.jpg)
|
|
|
|
## 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`, 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 B502 and B503.
|
|
You can compile it with:
|
|
|
|
5 LOAD ( z80 assembler )
|
|
0x0238 CONSTANT COM_DRV_ADDR
|
|
0x3000 CONSTANT DEST_ADDR
|
|
502 LOAD
|
|
503 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> 0x53 RAM+ !
|
|
' *CL< 0x55 RAM+ !
|
|
|
|
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 0x0a RAM+ !
|
|
|
|
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 Usage guide (B3) for details.
|
|
|
|
## Coming back to keyboard
|
|
|
|
Once you're done, you will want to go back to local control:
|
|
|
|
' CR 0x0a RAM+ !
|
|
0 0x55 RAM+ !
|
|
0 0x53 RAM+ !
|
|
|
|
## Self-hosting
|
|
|
|
As it is, your installment of Collapse OS is self-hosting using instructions
|
|
very similar to `recipes/rc2014/selhost`. 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.
|
|
|