Good news, everyone! I’ve just made a computer nobody’s ever heard of load a program from a floppy disk!
Update, 2017-12-08: I actually got this wrong — I mean, it worked, but it would only load programs smaller than 512 bytes (one sector). Turns out that the bootloader in ROM has an exciting bug which needs a workaround, making life much more complex. I’ve updated the document.
⇡Context
…so the backstory is: I’m the proud owner of possibly one of the best Z80 portable computers ever made, the Amstrad NC200. It’s actually my second, but I forgot to take the five C batteries out of my old one, and they burst and spewed corrosive gunk all over the motherboard, wrecking it. My new one arrived a few weeks ago.
I’m not going to go into the history — it’s complicated and I tend to get enthusiastic — but this machine is a Z80 with 128kB RAM expandable up to 1128kB (I have the upgrade), a 720kB floppy disk drive, a pretty good 80x16 character monochrome screen, an excellent keyboard, and would run for up to ten hours on a set of five C batteries (I use a power supply). In onboard ROM was a set of excellent productivity software including a word processor, spreadsheet, and a copy of R.T. Russell’s superb BBC Basic to write your own programs with.
The only problem is that as the software’s all in ROM, trying to run, say, a Unix on it is hard. But it turns out that Amstrad put enough escape hatches in that it’s possible.
Needless to say, don’t do any of this if you have data you want on your machine. It’ll be a dead loss.
⇡Running a program from floppy
(As far as I can tell, I am the first person ever to figure this out. It only took about two weeks of poring through ROM disassembly. Go me.)
The undocumented Function+R
key sequence causes the NC200 to load and run a
program from a specially-formatted floppy disk. Making such a floppy disk is
dead easy once you know the secret, and practically impossible if you don’t.
The secret is: it’s a FAT12 filesystem with one extra reserved sector, inside
which there is a second copy of the boot sector. If these both look good,
then the file called AUTO.PRG
in the root directory will be loaded at
0x4000 and run.
No, I don’t know why Ranger (who Amstrad subcontracted to do the floppy disk routines) did it like that.
But there’s more to it than that.
That extra reserved sector pushes the rest of the filesystem up by one sector. This means that the DOS clusters aren’t aligned with track boundaries any more. This shouldn’t be a problem, but… Ranger’s software is unable to access these clusters, and will error out if you try to make it access one. So in order to load files which span multiple tracks, you need to mark these clusters as bad so that your file avoids them. This actually adds up to 1/9th of the disk’s capacity.
So, what I have is a carefully hand-tooled filesystem image, with all the bad clusters marked in the right place. It’s as small as I could make it and still have enough space for a boot program and two 16kB data files (which is what I need to load Fuzix). The boot block contains a partition table for easy access to the rest of the disk, on which you could put a proper filesystem.
The easiest way to create such an image is to use Linux and this precanned boot block.
Do this:
$ zcat bootblock.img.gz | dd of=/dev/fd0 bs=512 count=2
$ mount /dev/fd0 /mnt -t vfat
$ cp auto.prg /mnt/auto.prg
$ umount /mnt
…and that’s all you need.
The resulting file system cannot be mounted by the NC200’s filer itself —
it doesn’t understand the extra reserved sector, and will complain it’s not
formatted. But Linux will mount it fine, so use your favourite tool to
assemble the program below and copy it onto the floppy, calling it
AUTO.PRG
. Insert the floppy into the NC200 and press Function+R
. It’ll
load and run. Note that this program is different to the SRAM example above.
txtoutput: equ 0xb81e ; prints a string
diskservice: equ 0xba5e ; FDD routines
org 0x4000
main:
ld c, 0x30 ; r_finish
call diskservice
loop:
ld hl, .str.hello
call txtoutput
jr loop
.str.hello: db "Hello, world! ", 0
We call r_finish
in order to turn the floppy disk motor off before starting
our program (otherwise it’ll run forever). Just like the SRAM version, you
get to use all the standard system calls.
⇡Running programs from SRAM card
This one’s been known about for years; but a lot of the documentation is subtly wrong, however, and is mostly aimed at the NC100, which didn’t have a floppy drive. So I’m focusing on how to achieve this from the NC200 here.
The undocumented Function+X
key sequence causes the NC200 (and NC100 and
NC150) to run a program written in a special format to the SRAM card. To make
this work you can either copy the program directly to the card via a laptop
with a PCMCIA slot, or use the NC200 itself. I’m going to cover the latter
only, because my SRAM card has no battery in it so I have to leave it plugged
in or it loses its contents.
What you do is you assemble a Z80 program with a special header, put it on a floppy, then use the NC200 to copy the program to the card. Here’s an example program, suitable for assembling with z80asm:
txtoutput: equ 0xb81e ; prints a string
org 0xc210
jp start
ds 0xc220 - $
start:
ld hl, .str.hello
call txtoutput
jr start
.str.hello: db "Hello, world! ", 0
The binary must be an image starting at 0xc210. The first 3 bytes must be
a jp 0xc220
instruction. (The program won’t run if it’s not.) Note that
this is not the same header that you need when writing a program directly
to the card!
To install it, do Function+F
to open the floppy drive browser, select the
binary with SPACE
, copy it to internal memory with C
; then, switch to the
RAM drive browser with Function+L
, format the SRAM card with Menu, F, M
,
select your binary with SPACE
and install it with Menu, T, P
.
Once done, you can run the program with Function+X
. The first 16kB of the
card will be mapped in at 0xc000, in the upper ROM bank, and your entrypoint
is called. You can make use of all the native OS services — the OS will
bank switch automatically.
⇡Cleaning up after yourself
If you’ve tried any of these, you’ll probably be wondering how to exit the
programs. Good news — there’s an easily accessible hard reset function.
Turn the machine off with the button above the keyboard. Then hold
Function + Stop + <-Del
, and press the power button again. The NC200 will
beep and wipe its memory, ready for another go.
⇡Extra bonus content!
While disassembling my way through the ROM, I found this:
$ strings ranger.bin
Copyright RANGER COMPUTERS, NORTHAMPTON, ENGLAND, NN5 6JG
Test bed software for disk i/f for NC200 V0.41
New Interrupts
English Language
0123456789ABCDEF
NC200 Disk Test Suite V0.41
A - Reset disk H - Spin speed
B - Seek randomly
C - Seek sequentially
D - Write test (wipes existing disk data)
E - Format test (wipes existing disk data)
F - Head Alignment (wipes existing disk data)
G - Disk change
STOP - Quit
It turns out there’s a complete floppy disk drive test and diagnostics suite in there! And it’s really easy to get at from native code (but won’t run from BBC Basic). You need this program:
diskservice: equ 0xba5e ; FDD routines
org 0x4000
main:
ld c, 0x30 ; r_finish
call diskservice
ld c, 0x0 ; r_test
jp diskservice
Copy onto a bootable floppy, call it AUTO.PRG
, and it’ll boot and enter the
tool. The SRAM version is left as an exercise for the reader.
⇡Other tools
Most programming documentation comes from NC100 IO Specification on cpcwiki, very kindly contributed by Cliff Lawson (the Amstrad project manager). It goes into reasonable detail of all the system call entry points as well as the IO ports, and should be enough to write useful programs. There’s also a NC 100/150/200 IO Specification which just lists the differences between the different machines which is somewhat sparser.
Back in 2003ish, I wrote a tool to extract files from SRAM cards (I had a battery in mine back then). This may be of interest to people wanting to recover data.
Believe it or not, there is still an NC user’s group.