Writing a ROM
Roland Waddilove offers a way out of the
perpetual space problem - put your utilities on ROM
This article originally appeared in the July 1987 issue of Computing With The Amstrad CPC. I've edited it to produce a shorter article more suitable for reading on the web, and updated it where relevant - DC.
Each ROM, apart from the OS starts at &C000 in memory. Now, so that the Amstrad doesn't get confused with all these ROMs it is able to switch each one in and out of the memory map. Effectively, each ROM can be turned on or off by your micro as and when it feels like it. Each ROM is given a unique number, for instance BASIC is 0 and the disc ROM is 7. If the micro wants to use the disc ROM, say to load a program off disc, it switches out (or off) all the ROMs except number 7. The machine code routines in this are then located from &C000 onwards and can quite easily be called. Then, after calling a routine the micro switches the disc ROM out and switches in whatever ROM it was using before the interruption.
When the operating system comes across an RSX command it searches through each of the ROMs for the command. So, when we use the |DISC command it switches in ROM 0 (and all the others out) and looks for the command. If it can't find it, it will look in ROM 1, then ROM 2, ROM 3 and so on until it finds it in ROM 7, the disc ROM.
The listing accompanying this article is the Computing with the Amstrad CPC utility ROM. You can assemble it with Maxam and either blow vour own ROM, save it as a file on your PC and use it as a ROM in an emulator, or put it into a RAMROM as described in Simon Matthews WACCI articles. Maxam is available for download as a ROM image or as a pair of disk image files.
Two RSX commands are set up - |DOUBLE, a double height text routine (use |DOUBLE,@a$ where a$ contains the text), and |HELP which prints a list of the commands accepted by this ROM. Not tremendously useful you might think, but that isn't my intention. They are two simple commands that show how to implement RSX commands in ROM. We'll see how these are set up.
The first few bytes form the ROM prefix or header. You can see this at the start of the listing. This essential information is required by the operating system and if it's not there the ROM will be ignored or the micro may even crash.
The first byte is the ROM type - 0 for a foreground ROM such as a language, 1 for a background ROM (containing utilities), and 2 for an extension ROM containing bits that won't fit into a foreground ROM. As ROMs are 16k long it's unlikely that you'll see an extension ROM. [Barely suppressed laughter - DC]
The next three bytes aren't that important. They contain the mark, version and. modification level of the ROM and are for your own use. As you modify and improve your ROM you can increase these numbers so that you can tell which is the most recent version.
Byte four marks the start of a normal RSX command table. The first two bytes - 4 and 5 in the ROM header - point to the list of RSX names and the third byte is the start of the jump table. Unlike ordinary RSXs you don't need to log them in by calling &BCD1 and there's no need to provide a four byte workspace.
The first entry in the jump table is rather special and is only called when the Amstrad is reset or powered up. It should also be given a unique name so that it can't be called as an RSX. This can even be an 'illegal' name such as '|MY ROM' - it's impossible to enter, from the keyboard, an RSX name containing a space.
When this first entry is called by the operating system the lowest and highest memory locations available are passed in the DE and HL registers. If your utilities require some workspace to temporarily store variables you can grab a chunk of RAM by increasing DE or decreasing HL. All the currently available ROMs take RAM from the top of memory by lowering HL so it's probably best to do likewise. During this call the title string is printed and you'll see the message "Computing with the Amstrad ROM" printed when you reset or switch the micro on.
This sample ROM subtracts 32 from HL, so the OS reserves 32 bytes for our own private use. However, we don't know the address of this 32 byte block.
Having set up the ROM header, command and jump tables and power-up entry, the only remaining task is to write the utilities themselves. The |HELP command is straightforward and merely prints a string of text. The |DOUBLE command is slightly more complex, although hardly rocket science.
When writing a ROM-based RSX the difficulty lies in not knowing where your workspace is. Every RSX command in ROM is entered with the IY register pointing to the start of this workspace. You must write your code so that no matter where the workspace is located the routine will work providing the IY register points to it.
As you can see from the listing you don't need to write a lot of code to create a ROM. Providing the header is correct you can have just a few simple commands and pad out the rest of the ROM with spaces or zeros.