So it seems … I have a working computer! It has ROM, RAM, address decoder logic, serial communication with a host computer, LCD screen for general-purpose info display, and a VIA for general hardware control.
The ROM provides me with a simple monitor, where I can poke values into RAM (or control peripherals), read values back, and begin execution from any address … so it’s programmable, too! Well, technically …
Though it is programmable as it is, it’s hardly friendly. There’s no nice assembler, no error checking, no debugging, no hand-holding. And whilst this might have been acceptable for the primitive home-computer kits of the early 1980s, it’s not really enough now.
Initially, I had intended to write extra utilities on the PC side that would assemble code and then transfer it over serial connection in the form the monitor expects. Then the code would be run from RAM – all allowing for quick iteration of development. Whenever I got to a good point, I’d update the OS ROM. And so on.
However … I’ve now decided to take a different approach: I’d like to get BBC BASIC to run on it instead. It’s friendlier, and would make experimentation with peripherals easier.
This isn’t quite as ridiculous a task as you might first think. If you’ve followed this series you’ll know that when it comes to memory mapping of my computer, I’ve looked at how the BBC Micro does it for inspiration – so the two are fairly similar! I’ve deliberately left the first half of my ROM alone thus far, which is where BBC BASIC expects to sit.
Some of the devices I’ve chosen to use do not match their equivalents in the Beeb (for example: the serial interface) but as I’ll explain later: that doesn’t matter as much as you might expect.
So other than downloading a BASIC ROM image and configuring the assembler to insert it into the first half of my ROM image, what else do I need to do?
Lets Talk About Operating Systems
In the BBC Micro, the Machine Operating System (“MOS”) ROM is in the highest 16K of the 64K address space (from C000 to FFFF). When the 6502 starts, it gets its initial execution address from the almost-final bytes (FFFC and FFFD) of the address space. So: to put those initial addresses and then the core operating system code all into one 16K ROM device makes a lot of sense.
The MOS does all its hardware initialisation (graphics, sound, etc) and then eventually passes on control to a sideways ROM.
Sideways ROMs sit in the 16K of address space immediately before (from 8000 to BFFF, which I’ve marked as “third”). Sideways ROMs are how the BBC micro does expansion – so named because the act of deactivating one ROM and selecting another would be drawn in a sideways fashion (if you look at that memory map diagram). There’s a latching register on-board which selects which ROM slot (maximum of 16) is currently active. As part of initialisation, the operating system checks each slot in turn, populates a small table in RAM, and keeps a record of whichever is the first “language” ROM.
When the OS has done all its initialisation, control then passes to that language ROM. For almost all users of the BBC Micro, this is BBC BASIC.
My current hardware setup uses a single 32K EEPROM instead of two separate 16K devices. For now, I’m not implementing sideways ROMs (but I may do at a later) so I don’t need to worry about enumerating multiple ROMs and selecting the right one. I’ll just put BASIC into the right place.
But the hardware setup, ROM enumeration and selection of the BASIC ROM is not the only thing the MOS does. The MOS provides a layer of insulation (and standardisation) between the hardware of the computer, and the programs that run on it.
The MOS handles what the programs don’t need to care about
Imagine you’re writing a program in machine code for the BBC micro, and you need to display a message to the user, on screen. If the OS didn’t exist, your program would need its own copy of the font, know all about how to draw things on screen, keep track of where the cursor is, and so on. What if the user wasn’t using your program the conventional way (keyboard and screen) but via a serial connection? You’d have to implement serial control too! What if they’ve added another screen type that your code doesn’t know about? It would be unusable!
Or … you could ask the operating system to do it. When your program needs to print a message, it shouldn’t need to know how to print it, or even what hardware it will appear on – it should just ask the OS to print it, and then not worry about it!
The Beeb MOS provides a standardised way of doing common things (outputting characters, accepting input, file access, and so on). These are known as the Operating System Calls, or the OS API. Their use is well-documented in the BBC Micro User Guide, the Advanced Guide, a multitude of programming books for the Beeb, and the BeebWiki.
These routines all have standard entry points – that is, a dependable method by which you should invoke them. It is the agreement between Acorn (the operating system creators) and you (the programmer) that this is the proper way of using these routines. They set these entry points in stone, so that any programs you write now should remain compatible with any new versions of hardware they make in future. And it provides a way to utilise the code already in the operating system so that you don’t have to reinvent it.
For example: OSWRCH is the routine for outputting a character (“OS write character”). Its entry point is at &FFEE. To use this routine, store the ASCII value you want to output into the accumulator, and then call OSWRCH as a subroutine. Like this:
LDA #&41 ; 41 hex (65 decimal) is the ASCII value for 'A'
JSR &FFEE ; call the OSWRCH routine and let it do its thing
(Obviously, you either put the address directly in your code, or create a constant named OSWRCH which your assembler will replace when it needs to – just makes your code a bit easier to read.)
Your program doesn’t need to know (or care) whether that character will go to the screen, or the serial port, or the printer, or an after-market display. Everything that happens after that JSR is in operating-system land, and as far as you the programmer is concerned: it’s a black box. Your program will continue when the job is done.
If you’ve ever done any programming on the BBC Micro and not needed to use these vectors, then that’s because you’ve been writing your programs in BASIC. The BASIC interpreter calls these routines when your BASIC program demands them. For example: it’s fairly easy to see that the PRINT keyword will invoke OSWRCH.
What does this mean for me?
From my perspective as a half-wit developer of a 6502-based computer: if I can implement this same interface in my OS ROM, then I can begin to use software written for the BBC Micro. My computer stops being just a solitary homebrew-6502 project, and starts to become a BBC Micro Compatible homebrew-6502 project. The more of the OS I implement, the more compatible it becomes. I don’t necessarily have to make each function as comprehensive as the Beeb – I just have to provide enough.
To make BBC BASIC run on my computer, I need to implement those aspects of the BBC MOS that BASIC expects. BASIC needs to be fooled into thinking it is running inside a BBC Micro.
So … lets look at exactly what I’ll need to implement …