BCPL virtual machine

Did anyone implement the BCPL virtual machine in hardware or microcode?

I’ll throw out my guess: no.

Based on reading about virtual machines and microcode around that era. For example, the Alto would have been a prime target, since BCPL was a popular language for that machine. But the compiler and microcode targeted Nova (like) machine code.

PS. By some odd quirk, the Alto BCPL compiler was based on the one for the Lincoln Labs TX-2.

Not that I’m aware of.

But do note; There is no single ā€œBCPL virtual machineā€. There are at least 3 virtual machines that the current compiler can output - the older OCODE, newer CINTCODE and SIAL - the latter being intended to be easy to translate to an underlying native machine code. (As is done in the OPC projects, AUIU)

Also note that BCPL compilers in the past have generated native machine code - e.g. DG Nova, PDP-11, 68K to name a few. See the distribution of MRs Cambridge website for examples and (once) working code generators. A good example of the 68K version was the early OS on the Amiga which was a port of the Tripos system developed at Cambridge.

The compiler has a separate code generator, so in theory you just re-write this for your own target and off you go…

One thing to look at though - the Inmos Transputer. There are some similarities there - 3 registers in a stack, relative stack addressing, variable length instructions to load up constants - maybe not a coincidence that many Inmos people came from Cambridge?

And once upon a time, I did spend an afternoon pondering the possibilities of implementing a ā€˜real’ CINTCODE machine in an FPGA before realising I didn’t have enough brain cells to burn in my lifetime, so stuck to interpreting the bytecode in a native assembler (3 times now; 65c816, ARM32 and RISC-V) I stuck to CINTCODE partly for the ā€œCā€ meaning compact. Working with limited RAM 8/16 bit systems when I started my own journey there. (Well, continued as I’d used it on the BBC Micro in the early '80s which also used an early version of a 16-bit CINTCODE).

-Gordon

3 Likes

I need 32 bit ASCII Intcode, if I ever get the hardware working here.
BCPL is on my ā€˜round to it’ list that may get to later in the FALL.

I have been designing with 128 cell CPLD’s, for hardware logic,
and using wincupl for design software. That I can understand what gets
compiled unlike VHDL or Verlog. Alas the CPLD’s will not permit as much
much more logic than a basic cpu, so microcode is not a option for me
or I would have several hardware instruction sets and more than 64K word
addressing.

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 :frowning: …

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

2 Likes

The fly in the ointment here is Windows and a non byte addressable CPU design.
I like vintage hardware design, compared to running RISC of the day.The modern machines have too
steep a learning curve for IO. Right now I am in the prototype stage so just having clean power
was a major problem. Modern CMOS parts are low power except for that 30 ns voltage spike
on the clock edge.
That aside running on a modern machine is the same as microcode in the 70’s looking at your timings.

There is a compiled windows executable too. Just Get the entire archive from MRs website and it’s there.

As for non byte addressable - that may make writing a bytecode interpreter on your own hardware somewhat challenging… However older versions of BCPL did run on work based systems with packstring() and unpackstring() functions to pack/unpack strings into arrays (vectors) of words… But you’d need to re-write every function that uses strings to work with them if you went down that route.

As far as timings - on the '816 it takes 29 cycles (16Mhz clock) at best before it starts to interpret an opcode - the simplest might be ā€œload a small constantā€ - a single byte that can load a value from 0 to 10. It’s defined as: B := A ; A := c so copy register A into register B (12 cycles) then load a constant into register A (6 cycles) and zero the top half word - another 3 cycles.

So we’re way slower than a PDP11 now.

It’s usable with patience:

% type hw.b 

GET "libhdr.h"

LET start () = VALOF
{
  writes ("Hello world!*n")
  writes ("Hello some more!*n")

  RESULTIS 0
}
% time bc hw.b to hw 

Ruby BCPL (10 Oct 2014) with simple floating point
Code size =    84 bytes of 32-bit little ender Cintcode
Time: 2884mS
% time hw 
Hello world!
Hello some more!
Time: 81mS

Most of the last 81ms is loading it from storage - but you get that authentic retro experience…

-Gordon

Do you also include The 5 minute warm up for the drive to reach 1500 rpm?
This also brings up the question how large were BCPL programs on the average,
in the day and age media was floppy sized and programs came on punched cards?

Huh? I don’t have drives, I have SD cards. The slowest drives I think I’ve ever had hands-on with would have been on a PDP-11. I think they spun up in under 30 seconds…

Want to see my system boot-up on a 150Mhz RISC-V system? It takes about one second from the power light going on to asking you to ā€˜login’.

On my 16Mhz '816 system I have to type in a command once the underlying OS has loaded into RAM (which takes half a second), but from there it then takes 3-4 seconds before it’s ready to login.

Which day and age? 1986 saw a multi-user dungeon written in BCPL running on a PDP-10 running TOPS10… It’s pretty big and supported dozens of users at a time.

1972 saw an OS written in BCPL for the CTS Modular-1 which had a hard drive although I’m not sure its capacity. (Lookup papers on OS6 Titled PRG08 and PRG09)

Then Tripos which was ported to the first Amiga as it’s initial operating system at launch in 1985.

There is a lot of old source code out there, some just a page long, some many, many pages. Want a Lisp interpreter written in BCPL? (it’s just over 1000 lines)

The biggest single thing I think I’ve written is my editor. It’s 1800 lines. Next is the ā€œexecā€ that’s the CLI which is essentially the OS that handles running programs, creating background jobs, IO redirection and ā€œshell-likeā€ stuff. That’s 1750 lines but I’m in the process of splitting it out for other reasons. My run-time library which is partly written by me and partly by MR is just over 7000 lines in 21 files. That’s the things like string handling, device, file and stream handling. Serial IO, formatted printing, managing background tasks and so on.

The editor compiles to 5560 bytes.

But don’t let that stop you using paper tapes, punched cards or whatever. My first experience of BCPL was in 1982/83 on a BBC Micro - 8-bit 6502 @ 2Mhz and it used floppy disks and network drives.

-Gordon

3 Likes