Tiny computers - hardware thoughts on high level languages

Playing around with my many failed 20 bit computer designs, I noticed a few things. My 24 bit computer design is getting to complex for a home brew design.
Computers with no operating systems tend to be controling things so only
about 4096 instructions and 1024 words for data. Most any thing goes,
but indirection or a index register is needed.
B and BCPL and LISP are word sized languages, but eveything else
needs a character data size. Simple integer languages require subroutines
and array access. Internal use oftern requires a second scratch acumulator.
FORTRAN II for example, ignoring floating point is needed
for many real world problems. BCD is needed for correct floating point and
punch card use. The decline of punched media and the rise of CRT terminals
implies a REAL OS that is about 32K instructions/data and 32K instructions/data
for each user. Timesharing cheats by using virtual memory, more hardware
vs more memory.
Structures, recursion,and relocateable code and data requires 4 index registers
at least. SP,BP,IX,IY as well as a # data type, This is where I am hacking my clean architecture for a second index reg needed for structures, and adding
simple structures to my high level language.
Hardware is two 256x8 proms + 2901ā€™s (bitslice) plus TTL glue with
.75 us core memory half cycle time. 1.5 us for full memory access.
22v10 PALs may replace the proms and some control glue logic.
A computer with 4 registers like the DG Nova,require a lot of work done
by a C compiler, compared to the easy code of a Dec PDP 11.
I wonder if UNIX would have developed the same way had Bell labs had a NOVA
computer rather than a PDP 11? 4 registers vs 8 registers assuming both had
the same MMU.
Ben.

1 Like

Itā€™s a good connection, I think: a CPU simple enough to build, and to get the bugs out, but capable enough that a reasonably productive language can be written - and self-hosted - which in turn is very useful to build a usefully capable OS and applications.

(Itā€™s possible to write everything in assembly language, but a great deal harder. And itā€™s possible to make a very CISCy superscalar homebrew CPU, but a great deal harder.)

I think limiting ambitions - limiting scope creep - is a major factor in a successful project.

1 Like

Naā€¦ my 6803 system has one index register, one accumulator, one stack pointer and a program counter. It runs C code fine. The conventional small machine approach is to turn each expression into a tree and then walk the tree from the bottom left, stacking one side on a two argument operation.

So you effectively turn (in RPN) * 3 + 5 4 into

                              load 4
                              push
                              load 5
                              addtos (add top of stack popping it)
                              push
                              load 3
                              multos

so long as youā€™ve got your stack based maths, store accumulator to addr top of stack, load accumulator addr top of stack, and some mechanism for local variables you are good. A bit of optimizing for constants and names helps if your CPU can do it.

I chopped up cc65 to do the 680x but I think you could abuse it fairly well for any similar machine, whilst anything with a few registers and vaguely sane and regular instruction set should be doable with ANSI pcc pretty easily - tms9995 took me about a week. I have Z8 on the todo list next 8)

Iā€™d also disagree about the 11. The original compilers struggled a lot to deal with register allocation. Not because things like register colouring were not solved problems by then but because they were not solved problems in small memories. There are reasons C has ā€œregisterā€.

The nova is thing of beauty, and has a C compiler of sorts (Raggeā€™s ANSI pcc). The nova3/4 and eclipse are a lot nicer because they added bytepointers, stack (and a very clever call/return instruction sequence), and the eclipse added immediates instead of having to use literals.

4 Likes

Stack designs are nice, but have extra code if you need to do
complex operations like block move, or multiply. A good job for microde
but not a simple TTL style cpu. The BCPL virtual machine is a nice
example.
To produce good code you need N operands look ahead
and recusive decent compliers are 0 operand look head.They parse
nice, but you still have build that parse tree and walk it at some point.

C compilers have a hidden feature, the macro pre-processing. This
makes the compiler more complex than need be, for C compiler development,
or porting to a different language.
Any software I have written is in a subset of C, to make self hosting and porting easy.
I had a version of small C kicking around under DOS-BOX, but I never could
get a DOS complier to compile it as cross compiler.

1 Like

Random example - the bourne shell on a 6803 is smaller and runs faster than on a Z80 with all its zillions of registers. And that is despite the 6803 compiler being cc65 based plus some peephole rules and the Z80 compiler being sdcc which does all sorts of fancy register allocation and optimizing.

The cpp macro preprocessor is generally a completely separate application so the compiler can ignore it. There are a bunch of them available like the DECUS cpp so you donā€™t have to worry about it in a compiler.

If you want a small C thatā€™s probably compilable with a real C compiler take a look at the 8085 small C. Itā€™s a somewhat weird fork of SmallC 3.0 with structs and unions but like the original small C still totally confused about type handling and more of a B compiler with strings. I have been able to build it easily with a real C compiler.

Be aware that like pretty much every version of small C you donā€™t want to feed it untrusted input, you can exploit the compiler from a source file!

1 Like

What is a ā€˜REALā€™ compiler? I tend to play under windows here.
Hardware for C generaly impliles a hardware stack, push this, pop
that. With my current hardware I only have software stack, and two
addressing modes, Immediate and indexed. Porting small C makes
for very bloated code and requires register to register operations.
I use a frame based model for high level languges, where I alot a frame
of N words for local varables and temps for each subroutine. This has
the disadvatage of only one subroutine call per statement, but has the
advantage of simple arguments for subroutines like printf.

Iā€™ve built that version (or a tweaked version of it) with ack, sdcc, gcc and a couple of other compilers. The tweaked version wonā€™t build itself but it will build the unmodified one which will build itself so you can bootstrap a self hosting small C.

1 Like