After sitting down and playing around with syntax I think I've figured out what a programming language for an accumulator machine would look like. If you are familiar with FORTH or with RPN calculators, this makes a very good langauge to natively program a stack machine in:
C or BASIC: Z = X + Y FORTH: x @ y @ + z ! stack machine: push Xaddress // x load // @ push Yaddress //y load // @ add // + push Zaddress // z store //!
With the above snippet, forth and stack machine assembly langauge have a 1 to 1 correspondence, and the same is true for most forth expressions.
How do you generate code on a standard register machine? One way to do it, is to keep track of which register is the top of the stack as code is generated. Push a value? Put it in A. Push a second value? Put it in B. Add the top two values? A and B are the top, so add them, keep track of which one is on top. If you need more values than registers, you can spill to the stack, and still keep track:
FORTH | assembly | stack register allocation x | mov a, @x | a @ | ld a | a b y | mov b, @y | a b @ | ld b | a b z | mov c, @z | a b c @ | ld c | a b c w | mov d, @z | a b c d @ | ld d | a b c d | push a | (real stack) b c d k | mov a, 2k | (real stack) b c d a @ | ld a | (real stack) b c d a + | add d,a | (real stack) b c d + | add c,d | (real stack) b c + | add b,c | (real stack) b | pop a | a b + | add a,b | a
If you switch to an accumulator architecture, instead of operating on the top of the stack, the cpu operates on the accumulator, and whatever the top of the stack is. There are a few options to deal with this. First, the simplest is pretend you have a register machine, and as a post process, use swap instructions to put operands into the accumulator.
I wanted to look and see what a forth-like language that explicitly supported an accumulator would look like. I came up with a stack-accumulator abstraction. The above stack-register allocation scheme will be used with registers B,C,D, will spilling onto the hardware stack. All math operations will be between the accumulator, and the current top of the stack.
I added the symbol '%' to represent the accumulator. If '%' is prepended to a number or constant, it means that push operation puts the number in the accumulator instead of the stack. When doing this, the current value in the accumulator maybe stored in the stack.
%4 3 + // put 4 in accumulator 3 on stack, add accumulator and stack
The above %4, will overwrite whatever was in the accumulator. Sometimes you want to preserve what is in the accumulator to the stack:
%1 %%4 3 + + breaks down to: %1: put 1 in accumulator, overwriting whatever is there %%4: put 4 in accumulator. the '1' is displayed to the stack 3: put 3 on the stack +: adds accumulator (4) to top of stack (3) +: adds accumulator (7) to top of stack (1) giving 8, left in accumulator.
There are a few other symbols needed:
@ : load address on top of stack to top of stack (same as forth)
%pop : move top of stack to accumulator
%@: load address in accumulator to accumulator
%%@: load address in accumulator to accumulator. the original address that was i the accumulator is preserved on the stack
dup: copy top of stack to top of stack
%dup: copy top of stack to accumulator
WIP, to be continued