RC2014: Z80 Assembly for Catgirls đ»
âRC2014 is a simple 8 bit Z80 based modular computer originally built to run Microsoft BASIC. It is inspired by the home built computers of the late 70s and computer revolution of the early 80s. It is not a clone of anything specific, but there are suggestions of the ZX81, UK101, S100, Superboard II and Apple I in here. It nominally has 8K ROM, 32K RAM, runs at 7.3728MHz and communicates over serial at 115,200 baud.â - RC2014 Homepage
I soldered this kit together recently, which was an undertaking that I underestimated. The Pro kit (pictured above) requires over 1,000 joints to be soldered just on the backplane itself.
My kit came with the compact flash and digital I/O modules. With the ROM jumpers set to the correct page, the kit boots to the Z80 SBC Boot ROM, which can then boot the install of CP/M 2.2 that comes preinstalled on the CF card.
Editor Woes
I might one day learn how ED.COM
works. Today is not that day. Instead, Iâve installed NVEDIT
using DOWNLOAD.COM
. Thereâs a copy of DOWNLOAD.COM
preinstalled on the virtual A:
drive.
Workflow will differ depending on your editor of choice, but writing assembly will be the same as
long as youâre using the standard CP/M tools for development. I may write a future post looking into
Small Computer Monitor, at which point
your assembly mnemonics and syntax will vary slightly.
A Quick Aside
CP/M ships with three executables, ASM.COM
, DDT.COM
, and LOAD.COM
. Once youâve written your
source code, you can assemble your program to hex with ASM FILE.ASM
. The file extension is also
implicit if youâve named your file .ASM
. This outputs a .HEX
file.
You can invoke the DDT
debugger on it, and execute it. If you want to build a self-contained
executable, invoke LOAD
on your .HEX
file and it will generate a .COM
file.
There Are Eight Lights!
The Digital I/O module makes for a great first device to fiddle with. It is addressed as device 0, accepts an 8-bit value that it displays in twoâs complement with 8 LEDs, and has eight momentary pushbuttons that are read back as an 8-bit value. The LEDs it comes with are very bright, though! Letâs fix that and turn them off.
The official page for this module mentions this little blurb about controlling it:
In BASIC the port can be read with the INP(0) command which will return a number from 0 to 255, and written to with OUT 0,x where x is the number to output from 0 to 255
In assembly language, the mnemonics
in a,(0)
and out(0),a
do the same function
Some research and trial/error led me to this simple program that will turn those lights off. On the
Apple II, a popular memory location for the ORG
statement (where this program is loaded into memory)
is $8000
, but in CP/M $0100
is a standard choice. Note that the h
in 0100h
stands for
hexadecimal, and is just the convention that CP/Mâs assembler uses.
So, what does the rest of this do? Well, if we want none of the lights lit up, weâd want to send 0
to the device. CP/Mâs assembler just uses the A
register for OUT commands. So we need to stick 0
into the A
register.
Weâll do this with MVI
, which is the move immediate data
instruction. 00h
is a hexadecimal constant for 0, and once itâs in the proper register, we just call out (0)
. This
sends the data in the A
register to the device on the bus of ID 0
.
Thatâs all of the âdoing stuffâ assembly. RET
returns function to CP/M; Without RET
, the lights
will extinguish, but the system will also hang.
So, altogether:
- Stick our assembled data in memory at
0100h
- Load
00h
into theA
register - Send the contents of the
A
register to device of ID0
- Return execution to parent (returning control to CP/M).
Actually pretty simple!
Letâs do something more fun, though.
Reinventing The Signal Lamp, Poorly
CP/M has a BIOS
and BDOS
(Basic Disk Operating System), collectively referred to as the FDOS
.
The documentation for this can be found in
Chapter 5 of the CP/M 2 Manual. This
includes an ABI that can be called
from your program. For now, weâll just mess with 1: Console Input
.
In general, the function number is passed in register C with the information address in the double byte pair DE. Single byte values are returned in register A âŠ
So we want function number 1
. Letâs use that same MVI
instruction to stick 01h
in
register C
, this time.
Okay, so now we need to call the FDOS
functions. The documentation states:
As mentioned above, access to the FDOS functions is accomplished by passing a function number and information address through the primary point at location BOOT+0005H
All standard CP/M versions assume BOOT=0000H, which is the base of random access memory.
0000h
+ 0005h
= 0005h
, So letâs CALL it!
Remember, single byte values are returned in register A. That makes the last step the same as in the first example. Weâll call:
Combining everything together, our entire program is:
Altogether:
- Stick our assembled data in memory at
0100h
- Call the console input function, getting a single byte from console input
- Send that to device (0)
- Return execution to parent
Now you can see the binary representation of whichever key you press!
But wait, yours doesnât quite do that..?
When you run it, youâll notice that your program executes after one keystroke. Not as fun as it could be. If you take a closer look at Chapter 5, youâll find a solution to this in just a few more lines of ASM. I am doing the annoying thing, and leaving it as an exercise to the reader. đ€
âŠ
Alright, alright. The solution is below. đ
mvi c,01h
call 0005h
out (0)
cpi '*'
jnz 0100h
ret