wiz-lang / wiz

A high-level assembly language for writing homebrew software and games on retro console platforms.
http://wiz-lang.org/
Other
409 stars 40 forks source link

Allow syntax sugar for "assignment through" a temporary (the 'via' keyword) #96

Open Bananattack opened 4 years ago

Bananattack commented 4 years ago

Right now it is somewhat cumbersome to write 16-bit load instructions and arithmetic on 8-bit systems, because they need to be decomposed into several assignments.

Wiz could keep this decomposition into smaller parts explicit, but make the process for writing this somewhat compiler-assisted, by using a keyword after an assignment statement.

via seems like a good fit, because it's unlikely to be used by code, and it's short, and gets the point of it being for an intermediary across. I dislike thru because it should be spelt through, and through is too long, meanwhile by doesn't really convey the purpose well, and is already used as a keyword for range expressions.

For instance, assigning a 16-bit value using a temporary register.

var hp : u16;

// ...

hp = 12345 via a;
// 16-bit load. Loads each byte, in order, using a as a temporary. Equivalent to:
// <:hp = a = <:12345;
// >:hp = a = >:12345;

It would also be possible to use this when dealing with compound operators, which often must be performed on a register. This will take care of the work for pulling things in and out of registers, and synthesize things as appropriate.

var foo :  u8;

// ...

foo += 100 via a;
// 8-bit compound += is equivalent to:
// foo = a = foo + 100;
// adds 100 to foo, using a as a temporary.

var bar : u16;

// ...

bar += 0x1234 via a;
// 16-bit compound += is equivalent to:
// <:bar = a = <:bar + 0x34;
// >:bar = a = >:bar +# 0x12;
// Like the 8-bit version, this also takes care of loading to and from registers.
// This also decomposes addition into an add followed by add-with-carries.
// (also allow expanding subtraction, add-with-carry, sub-with-carry, bitwise operations)

For systems where compound operators are only available on certain operands, would be possible to designate a secondary piece of storage, too. (eg. Z80 only allowing addition between accumulator and immediates + registers, not main memory) By naming two terms after the via keyword, it would be possible to name the primary ("left-hand temporary") and secondary ("right-hand temporary") of a compound expression.

var amount : u8;
var counter : u8;
// ...

// eg. on a system where addition is only supported between
// the accumulator and a registers or an immediate.
counter += amount via (a, b);
// shorthand for:
// b = a = amount;
// counter = a = counter + b;

Since this keyword already opens up a fair amount of work, I would probably limit it to these cases for now.

But the idea with this keyword is to cut down on boilerplate, but still keep things generally explicit about which registers will be affected by an assignment statement.

Since assignments can be chained, and that could quickly get messy, this will only be possible to apply to the innermost assignment. Assignments would be trailed by a single via keyword to indicate which temporary to use to load, or perform a compound operation on some term. The via keyword could also be allowed in the argument position of a function call to indicate how to load an argument.

The intent is to decorate roughly single assignments / single operations and say explicitly how to calculate them, without writing out every statement by hand. Complex arithmetic must be broken into multiple statements, maybe some day this could be enhanced if someone (other than just me) was eager to help improve the compiler with some of the magic needed to do more elaborate expressions.

This feature has been something I've wanted for a long time, since it can use the compiler's type information and instruction set information to synthesize instructions for the user, and furthers the goal of making it possible to write "higher level assembly" code, without losing explicit control over what registers are affected.