Kroc / v80

A minimal Z80 assembler, running on Z80. Useful for bootstrapping bigger projects.
MIT License
23 stars 2 forks source link
asm cpm z80

v80

If a computer can’t compile and deploy its own software then it’s not a general purpose computer, it's an appliance.

v80 is an 8-bit native, multi-platform, multi-CPU cross-assembler that runs on real 8-bit hardware, but also on PC (via NTVCM or RunCPM). v80's goal is to ensure reproducability of 8-bit software on real 8-bit hardware, both modern and retro, whilst never excluding PC-based development.

"v80" refers to the Z80 port, but versions that target different CPUs and/or run on different host CPUs are available, or are planned:

v80         <- runs on Z80  - assembles Z80 code ".v80"
v80x65      <-      "       - assembles 6502 code ".v65"
v80x69*     <-      "       - assembles 6809 code ".v69"
v80x83*     <-      "       - assembles GameBoy (SM83) code ".v83"

v65*        <- runs on 6502 - assembles 6502 code ".v65"

v69*        <- runs on 6809 - assembles 6809 code (.v69)
v69x65*     <-      "       - assembles 6502 code (.v65)

*(coming in a future release)

If you would like to see v80 on your favourite CPU / system consider raising an issue at https://github.com/kroc/v80

It currently runs on CP/M and can assemble itself with plans to port it to Agon Light, Zeal 8-bit OS and other Z80-based systems. v80 can assemble 6502 code via its pluggable ISA support and a native 6502 port is planned.

If you feel like you could write a C version of v80, your help would be appreciated as it would remove the need to simulate CP/M to assemble on PC, with the no-directories limit that imposes.

Features

Features added since v0:

Syntax Sample:

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 or otherwise be easily categorizable, e.g. a-z = instruction.

; v80: ................ ; zilog:
;                       ;
add   $ff               ; add A,    $ff
adc.c                   ; adc A,    C
sub*hl                  ; sub A,    [HL]
sbc.hl.bc               ; sbc HL,   BC
ld.a*ix $00             ; ld  A,    [IX+$00]
bit7.a                  ; bit 7,    A
jr?nz   :label          ; jr  nz,   :label
; ...
; (see "release/z80.txt" for full list)

#true   $1              ; constant define

; constants can be redefined, but the value
; must be constant (no forward-references)
;
#true   $0

; a hex number at the start of a line
; sets the program-counter
;
$c000

; or use `$` to set program-counter
; using an expression
;
$   #base + $0100

; other number types supported:
;
#bin    %10000001       ; binary (1-16 digits)
#dec    65535           ; decimal
#chr    'A              ; character literals

:label                  ; label define

; local labels are appended to the
; last defined non-local label
;
_local                  ; i.e. :label_local

; keywords begin with `.`
;
.b <bytes>
.w <words>

; in expressions, `$` returns
; current program-counter
;
.b :label - $

; no string escapes, just use numbers
;
.b "line 1" $0a "line 2"

; unary operators:
; (before value)
;
; <         = lo (bottom byte of word)
; >         = hi (top byte of word)
; !         = not (flip all bits)
; -         = neg (flip all bits and +1)

; infix operators:
;
; + - * /   = add sub mul div
; ^ & | \   = xor and or  mod

; file includes
;
.i "file.v80"

; condition markers: the expression is evaluated and
; the condition passes / skips based on equality with 0
;
?= <expr>   ; only 0 passes
?! <expr>   ; only not-0 passes
?+ <expr>   ; only positive (sign+) passes
?- <expr>   ; only negative (sign-) passes
    ; if the condition fails,
    ; indented lines are skipped
    ?!      <expr>
            ; this allows for nested conditions

; align: skip bytes until the program-counter
; modulo the expression is zero
;
.a  $100                ; align to next page

; or fill a specific number of bytes,
; with a given value
;
.f  '!  10              ; emit "!", 10 times

A full guide on the syntax is included in "release/readme.txt". If you're using VSCode, there's an extension for v80 syntax highlighting in the marketplace under v80-vscode.

Building v80

If you just want to use v80 to write and assemble Z80 (or 6502) software, just download a release. If you want to build v80 from source, everything needed to build v80 on Windows is included in the repository. Building on Mac, Linux and UNIX-likes can be done, but will require you to source / compile your own binaries of WLA-DX & NTVCM.

Just run "build.bat" to assemble v80 and related cross-assemblers -- the binary is placed in the "releases" folder.

Running on Real 8-bit, CP/M Hardware:

v80 is assembled as a generic CP/M binary that should run on any Z80-based, CP/M v2.2 (or above) system -- no hardware or system-specific calls are used. For now, v80 does not provide any system-specific floppy-disk images for loading v80 on to real hardware, but you can use tools like Disk Image Manager (Amstrad / Sinclair) or cpmtools to make disk-images. If you would like a specific system to have pre-loaded v80 disk images, please consider submitting an issue.

Change History

2024/7/?: v1.0.0

2024/7/1: v0.1.3

In a spectacular case of not seeing the woods for the trees, the expression parser wasn't evaluating anything more than a single operator and ignoring the rest of the expression. Unary operators were also not working due to expected registers changing at some point.

v80 now assembles itself byte-for-byte compared to the WLA-DX version meaning there should be no more surprise bug-fix releases, and a v80 version of v80 will be coming with v1.0!

2024/7/1: v0.1.2

Many fixes realised whilst working on v80 assembling itself:

2024/6/30: v0.1.1

2024/6/30: v0.1

2024/6/27: v0.0

First release, v80 includes: