v80 is a (very) small, (very) fast Z80 assembler that runs on Z80 for bootstrapping larger projects.
It currently runs on CP/M with plans to make it self-hosting (can assemble itself) with ports to Zeal 8-bit OS and Agon Light.
- No Macros! Copy + Paste is your friend
- No linker. You don't need one
- No decimal numbers. Let's face it, you think in hexadecimal anyway
- No floating point numbers. Have you tried coding floats on an 8-bit CPU??
- No negative numbers. It's all 1s in 2s compliment
- No shift operators. Multiply/divide by powers of 2
(honestly the reason for this is just because the parser is limited to single-character operators) - true = 0, like how a CPU actually works
v80 uses a non-standard syntax designed for parsing simplicity / speed, not compatibility with existing source code.
The basic principle is that v80 can only recognise a word by the first character, so everything must be prefixed with a sigil type or otherwise be easily categorizable, e.g. a-z = instruction.
v80: ................ zilog:
adc adc
adc.a adc A
adc.hl+bc adc HL, BC
adc*hl adc [HL] ; "*" like a pointer
adc*ix $FF adc [IX+$FF]
bit7.a bit 7, A
bit7*hl bit 7, [HL]
call $FFFF call $FFFF
call?nz $FFFF call nz $FFFF
Labels begin with a colon.
:label
Labels can begin with a number; in fact, almost any character is game as only whitespace is considered the end of a label name, although you should avoid going too far. a-z, 0-9 and _ are recommended.
:1
A line that 'begins' with a label name, that is, before any keyword, defines the label as having the current Program Counter.
Labels cannot be redefined. All labels must be unique.
A constant is a reusable value given a name. A line that begins with constant name defines a constant:
#true 0
Constant names can begin with a number:
(the parser is very basic and considers everything between # and whitespace to be the constant name, although you should try stick to a-z, 0-9 & _)
#1 $31 ; ASCII "1"
Constants can be redefined, but their value must be constant!
That is, a constant cannot use a forward-reference or undefined constant.
:1
#back :1 ; OK!
:2
#back :2 ; OK!
#fwd :3 ; invalid! `:3` is unknown
:3
Use .b & .w to write bytes and words to the assembled binary.
.b $AD $DE $EF $BE ; write bytes
.w $DEAD $BEEF ; write words
Once either keyword is encountered, expressions will be read until the end of the line or another keyword is encountered; keywords cannot be expressions.
.b $1 $2 $3 .w $4 $5 $6 ; = $01 $02 $03 $0400 $0500 $0600
An expression can be described as:
an optional unary operator followed by a value,
optionally followed by an operator and another expression
With a value being any word that evaluates to a number;
i.e. number literals, labels, or constants.
The unary operators come before a value:
! not
< lo-byte
> hi-byte
Standard operators come between values:
+ add
- subtract
* multiply
/ divide (integer)
& and
| or
^ xor
% modulo
There are no shift operators, simply because the parser can only handle single-character operators and couldn't distinguish > and >>. To do shifts, multiply or divide by powers of 2, e.g. * 2 = << 1, / 8 = >> 3. Also, there's no power / exponentiation operator :P