Homebrew CPUs: Color Languages
Color Languages
Here on bizarro we program using -- get this – text! Our other senses - hearing, touch, smell, are not used at all. Even our visual perception is greatly underutilized - we just use two-dimensional text on a flat display a foot in front of our eyes.
Color is just beginning to be used, although in a lame syntax coloring way only. Granted, it makes it easier to detect stupid syntax errors such as misspelled keywords. Sadly, color carries zero semantic or syntactic information in modern IDEs.
In this article I will address one possible use of color - as a way to select the source and destination of operations, as applied to a CPU described in the last article, Homebrew CPUs: Messing around with a J1
A Nod to Chuck Moore and ColorForth
A notable (and probably the only) use of semantic color in modern languages is ColorForth. Written by Chuck Moore, the inventor of the Forth language, ColorForth uses different colors to indicate how words are to be compiled. This is a substantial deviation from normal computing practices (where code is just compiled), and even traditional Forth. See http://www.colorforth.com/cf.htm
With ColorForth the programmer winds up pre-parsing or even pre-compiling the code without thinking about it too much. Literals are colored with a specific color (or they might be treated as symbols otherwise) making parsing and compiling easier.
Any operation may be colored with an 'immediate' color, forcing the computation to happen _now_ and the results to be embedded as a literal value into the final program. In fact, right in the middle of a function definition, you can pop up a level and do any computing task, just by changing to a different color. All with the same language, not some lame pre-processor. This kind of power is known to few programmers unless they use Lisp or Forth. And you never have to guess if something is a 'macro' - just look at the color.
ColorForth control structures, such as conditionals and loops, are built just that way - by popping up a level with _if_ and _then_ setting up jump targets and compiling code to branch around. Compared to old-school forth This is of course how we do it in regular forth with immediate words, but with ColorhForth the mechanism is explicit and consistent.
ColorForth is finally available as part of Chuck's Green Arrays development system. It certainly shows off the conciseness of Forth-like languages, considering that each Green Arrays processor only has 64 words of RAM and 64 words of ROM. http://www.greenarraychips.com/home/documents/inde...
Motivation
In my last article, I had dissected and recombined the pieces of the J1 processor, winding up with a mongrel with two datastacks. How would one program such a machine? How do you expand a forth-like language to work with more than one datastack without duplicating all the instructions? Surely there is a better way than bloating the language with DUP-A and DUP-B and such nonsense.
About 10 years ago I experimented extensively with color for selecting registers in assembly-like languages. About the only thing I remember about it is that it was extremely stimulating. Sadly, Linux updates made most of my old code useless. Such is life.
A Rainbow Forth
You may have guessed by now that I intend to use color to select the datastack. So let's try it out, to see how it plays out. We'll start with a simple premise: there is a single Top-Of-Stack (TOS) register and two datastacks, from which Next-On-Stack (NOS) comes from. Let's call the datastacks Green and Blue.
Literals are simple. A literal is always loaded into TOS, after the previous value of TOS is pushed onto a datastack. Which datastack? The color of the literal decides: Green numbers indicate that TOS is backstored onto the Green datastack, while Blue numbers indicate the use of the Blue datastack.
Same with simple stack operations. DUP SWAP OVER + - work the same way. The operator color decides which stack to use.
Interestingly, using color, we can even integrate the Return stack into the same methodology, streamlining the language quite a bit.
The return stack still serves a special purpose - it is explicitly used by the call/return machinery. To remember that, let's use the color Red to indicate return stack usage.
No longer do we need a separate PUSH and POP - we can simply
for PUSH (and for POP). Yes, that's two instructions but they should be combinable into one with a processor like J1.
Non-stack Operations
Literals and Operators are easy - and colored Green, Blue and Red as needed. But there are some operations that do not affect the stack. For instance, there is the increment operator called 1+. Since it operates on TOS exclusively, coloring it would be misleading.
My preferred solution would be to color it White. White is a neutral color, and 1+ is a stack-neutral word. Of course leaving it some other color does not cause any harm either, other than being somewhat misleading. We can decide on that later on.
A Slight Complication - user-defined functions
While implementing a mock-up of Rainbow Forth on x86 I quickly ran into a slight complication, that turned out to be an indication of a bigger problem. Let's take DUP. How do we implement it so that it affects the right register (erhm, the right datastack)? I don't mind writing three versions of DUP (and all other primitives) and having the compiler select the right one based on color, not too much anyway.
But what about user-defined functions? Do we expect the user to write three different versions, one for each color stack? Clearly that cannot work.
The half-assed solution is to say that one datastack, the Green one, is 'preferred'. It is used to pass parameters between high-level routines. This feels just awful, as it immediately destroys the power of the language and relegates the other stacks to second-class-citizen status, good only for intermediate calculations. There must be a better way.
Come to think of it, what does it mean to have a procedure/function have a color? Nothing stops the user from using any-color stack inside, or in fact, all of them. It is more of a convention, I suppose - a request to have side effects on a particularly-colored stack.
Some functions are meant to be color-static, or operate on a particular-color stack only. Return comes to mind – it is expected to use the red stack. Others like the operators + and - are meant to take on different colors, and perform their duties on different stacks accordingly.
A Possible Solution
Let's assume that the user can define a word FOO once and apply different colors to it. Let's assume that we have a processor that runs Rainbow Forth. Let's also assume that every time a function call is made, the color of the function winds up in a register called COLOR. We are building the CPU, so why not?
The implementation of FOO may use different-colored words as needed, but in addition, choose the runtime color requested by the caller. We shall use another color, say Gray, to indicate "the color that this word was invoked with".
To make this happen, the stack selection bits in the opcode have to have another possibility, one that means use the COLOR register to select the datastack. Words coded in GRAY become color-agnostic until they are executed. This is a minor modification that turns a 3:1 mux into a 4:1 mux and should not cause us any issues.
The COLOR register needs to be preserved across every function call. The simplest way to implement it is to attach it to the return address as it gets stacked during a call. Since J1 return stack is 16 bits wide, we can steal a couple of bits to store the current color.
The COLOR register also needs to be set for every function call. For that we need a couple of extra bits in the call instruction. If we use 8K-words of program memory or less (which is pretty likely) we can just steal them from the 15 bits of target address. Otherwise, we could extend the instruction/data size to 18 bits, which is a nice number.
Wow, this is complicated. And if we don't know the color in advance, how do we store temporaries in a way that does not mess up the final result? We need a color remapping scheme.
Perhaps the half-assed solution is the way to go...
Compile-time colors
I like Chuck Moore's use of color to indicate immediate execution. In our case, we cannot use a single color since we need three to pick the stack. We can indicate immediacy by changing the background color to dark gray and keeping the Green, Blue or Red colors intact.
Upon encountering a word with an immediate background color, the compiler will call it right then and there.
Taking Stock
We've developed a roadmap for a Forth-like language that uses colors to indicate which of the 3 possible stacks to use as the NOS register. The hard colors, colors that actually result in the selection of NOS, are Green, Blue and Red.
In addition, there are 'soft' colors. They assist the compiler in making the right choices:
- White means don't care (compile as Green most likely, since we have to choose one anyway)
- Green, Blue and Red on a dark-gray background indicate immediate mode
The language should make it possible to integrate multiple stacks into a forth-like environment.
In upcoming posts I will attempt to cover the practical issues, such as editing color source code and implementing Rainbow Forth on various hardware.
- Comments
- Write a Comment Select to add a comment
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: