Hope it works.
Do go through the OPC (One Page CPU) project pages:
where there is discussion of using BCPL on OPCv6. They go from BCPl → SIAL → Native machine code.
So what are your aims? Mine was for a stand-alone operating system capable of program development - editing, compiling code directly as I did “back then”. (as opposed to cross compiling, downloading, running)
There is a working compiler that you can run under Linux to produce OCODE (which is a file full of decimal numbers in plaintext/ascii format), SIAL (which I’ve never looked at) or CINTCODE which can either be a file of plaintext decimal numbers or binary. The “cintsys” “OS” uses CINTCODE and lets you load and run compiled BCPL programs - the obvious first compiled program you’ll want to run is the compiler to compile your own little test program… fortunately the compiler is provided as both source and binary.
I use the binary format because my storage (SD) interface is relatively slow (max. 32KB/sec) and converting from decimal to binary at program load time is wasteful and slow.
Once you have it all working under Linux, then … Well it gets hard. BCPL needs something called the “global vector” this is where you store global variables and pointers to global functions - e.g. your run-time library. You’ll need to have some sort of means to load those libraries or some sort of means to “link” your program with the libraries on your Linux host then copy the entire thing to the target. Look at the cintsys program which includes an interpreter for cintcode and a minimal CLI interface to allow you to run compiled BCPL programs. You can look at cinterp to see how to interpret cintcode.
So you then need to write/compile the libraries to suite your system. It’s not easy - although I’ve been tinkering with it for some 8 years now, it still sometimes trips me up. My BCPL system runs on top of a “bios” in some native code (assembler or C) which does the hard work of all IO, serial, screen, keyboard, storage, etc., then that loads the cintcode interpreter and a small bootstrap (written in BCPL) to initialise the heap which then loads all the run-time libraries from storage into RAM, then finally runs a CLI. The CLI lets you load a program from storage, gives it a stack, automatically links it with the run-time libraries as required and runs it.
And if interpreting the CINTCODE (bytecode)… Your underlying architecture needs to be able to load bytes efficiently and then use them as an index into a jump table to cater for each of the 256 instructions. Instructions are one, 2, 3 or 5 bytes long. The 3 bytes ones are “load halfword” and the 5 byte ones are “load word” so being able to load unaligned data is a boon in a 32-bit native system. Not all can so you may end up assembling a 32-bit word from 4 separate memory reads
…
The dispatcher is the crux. On my 65816 system it takes 29 (or occasionally 36) cycles just to fetch the opcode, increment the VM PC and jump to the handler. That gives it a virtual “clock speed” of under 0.5Mhz on my 16Mhz system. The RISC-V implementation is 5 or 6 cycles (RV extension dependant) and the ARM32 just 2. The ARM32 can fetch, shift, increment and jump in 2 instructions. Almost as if it were designed to be a bytecode interpreter from day 1.
Having ‘real’ registers for the main CINTCODE registers is a boon - in my ARM32 and RISC-V implementations I can keep the entire state/registers in the CPU registers. You need 6 plus a temporary or 2. (A, B, C, PC, GP and stacK)
CINTCODE is very CISC in nature. There are 2 ‘switch’ opcodes for example.
Oh, be aware that pointers in BCPL are word pointers. This makes accessing hardware a step trickier, so e.g. in my 65816 system, the hardware IO chip is at address $00FE00, so #xFE00%0 := #x55 might look right, but it’s not. You need to divide it by bytesPerWord, so it’s #x3F80… Just one of many ‘gotchas’ …
I’ve a funny feeling it might be easier to re-target a C compiler to your hardware…
Enjoy,
-Gordon

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.