tranZPUter SW-700 — Developer's Guide
tranZPUter SW-700 Developer's Guide
This guide is a detailed walkthrough of the tranZPUter SW-700 source code and development environment. It introduces VHDL for developers who may not be familiar with the language, walks through every major source module, documents the CPLD and FPGA designs, explains the memory mapping and CPU switching architecture, and shows how to build the firmware, add support for new Sharp MZ machines, and debug hardware problems.
For hardware architecture and schematics see the tranZPUter SW-700 page. For user-facing operation and TZFS commands see the TZFS User Manual. For TZFS source walkthrough see the TZFS Developer's Guide.
Introduction to VHDL for Non-HDL Programmers
The entire tranZPUter SW-700 digital logic — the CPLD glue logic and the FPGA video and soft-CPU core — is written in VHDL (VHSIC Hardware Description Language). Unlike software, VHDL does not describe a sequence of steps that a processor executes one at a time. It describes hardware: collections of logic gates, flip-flops, and state machines that all operate simultaneously. Every
ENTITY and ARCHITECTURE
process in a VHDL file runs in parallel with every other process; signals change value all at once on each clock edge.
Every VHDL module has two parts:
SIGNALs
- ENTITY declares the module's interface — its input and output pins. Think of it as the function signature in a software language. The
coreMZentity, for example, declaresCLOCK_50,VZ80_ADDR,VZ80_DATA, the VGA outputs, and dozens of other pins asin,out, orinoutports. - ARCHITECTURE contains the implementation — the internal signals, component instantiations, and processes that describe how outputs are computed from inputs. The architecture for
coreMZis namedrtl(Register Transfer Level), the conventional name for synthesisable behavioural descriptions.
entity coreMZ is
port (
CLOCK_50 : in std_logic; -- 50 MHz base clock
VZ80_ADDR : inout std_logic_vector(15 downto 0); -- Z80 address bus
VGA_R : out std_logic_vector(3 downto 0) -- VGA red channel
-- ... further ports ...
);
end entity;
architecture rtl of coreMZ is
signal PLL_LOCKED : std_logic := '0'; -- internal signal
begin
-- concurrent statements and processes go here
end architecture;
Signals are the internal wires of a VHDL module — they connect processes to each other and to the ports. A signal declaration names the wire and gives its type. The most common types in this design are:
PROCESS
std_logic— a single bit that can be'0','1','Z'(high-impedance / tri-state), or several other simulation values.std_logic_vector(N downto 0)— a bus of N+1 bits, indexed from N (MSB) to 0 (LSB).std_logic_vector(7 downto 0)is an 8-bit bus.unsigned/integer— numeric types used for counters and arithmetic.
A
process is a block of sequential VHDL statements that re-executes whenever any signal in its sensitivity list changes. Processes are the fundamental building block of synchronous logic.
A clocked process — the most important pattern in this design — responds only to the rising edge of a clock. It models flip-flops (registers):
process(SYS_CLK, RESETn)
begin
if RESETn = '0' then
-- Asynchronous reset: drive all registers to their initial state.
MY_REGISTER <= (others => '0');
elsif rising_edge(SYS_CLK) then
-- Synchronous logic: executes once per clock tick.
MY_REGISTER <= NEXT_VALUE;
end if;
end process;
A combinatorial process has all inputs in the sensitivity list and no clock edge test. It models pure logic — AND/OR/mux trees:
process(Z80_ADDR, Z80_IORQn, Z80_M1n)
begin
TZIO_CSn <= '1'; -- default inactive
if Z80_IORQn = '0' and Z80_M1n = '1'
and Z80_ADDR(7 downto 4) = "0110" then
TZIO_CSn <= '0'; -- I/O address 0x60–0x6F selected
end if;
end process;
Finite State Machines (FSMs)
The tranZPUter SW-700 uses FSMs extensively — for bus mastering control, CPU switching, memory mode transitions, and the K64F service request handshake. An FSM in VHDL is typically a clocked process that updates a
state signal (an enumerated type) on each clock edge, combined with a combinatorial process that decodes the current state to produce outputs:
type BUS_STATE_TYPE is (IDLE, REQ_BUS, WAIT_ACK, BUS_HELD, RELEASE);
signal BUS_STATE : BUS_STATE_TYPE := IDLE;
-- Clocked next-state logic:
process(SYS_CLK, RESETn)
begin
if RESETn = '0' then
BUS_STATE <= IDLE;
elsif rising_edge(SYS_CLK) then
case BUS_STATE is
when IDLE => if BUSRQ_TRIGGER = '1' then BUS_STATE <= REQ_BUS; end if;
when REQ_BUS => BUS_STATE <= WAIT_ACK;
when WAIT_ACK => if Z80_BUSACKn = '0' then BUS_STATE <= BUS_HELD; end if;
when BUS_HELD => if RELEASE_REQ = '1' then BUS_STATE <= RELEASE; end if;
when RELEASE => BUS_STATE <= IDLE;
end case;
end if;
end process;
Component Instantiation
Large designs are assembled from smaller modules using component instantiation — the VHDL equivalent of calling a function, except that each instantiation produces an independent hardware block that runs permanently in parallel. The
coreMZ_SoftCPU top-level file instantiates the VideoController, softT80, and softZPU modules, wiring their ports together via internal signals:
VideoCtrl: entity work.VideoController
port map (
SYS_CLK => SYS_CLK,
VZ80_ADDR => VZ80_ADDR,
VZ80_DATA => VZ80_DATA,
VGA_R => VGA_R,
-- ...
);
SoftCPU_Z80: entity work.softT80
port map (
SYS_RESETn => RESETn,
SYS_CLK => SYS_CLK,
Z80_CLK => VZ80_CLK,
T80_ADDR => T80_ADDR,
-- ...
);
The top-level entity's job is entirely connection — it has almost no logic of its own. All logic lives in the leaf modules.
Source Tree
| Path | Contents |
|---|---|
CPLD/EPM7160/ |
CPLD VHDL: tranZPUterSW_EPM7160.vhd (main), tranZPUterSW_Toplevel_EPM7160.vhd, tranZPUterSW_pkg_EPM7160.vhd |
CPLD/Fusion/ |
Fusion variant CPLD (alternative board variant) |
FPGA/SW700/v1.2/ |
Cyclone III FPGA VHDL (v1.2, video-only board) |
FPGA/SW700/v1.3/ |
Cyclone IV FPGA VHDL (v1.3, current mainstream — see below) |
FPGA/a-z80/ |
AZ80 open-source Z80 RTL (alternative soft Z80 IP) |
FPGA/nextz80/ |
NextZ80 alternative soft Z80 |
schematics/SW700/v1.0/–v1.4/ |
KiCad schematics for each board revision |
pcb/SW700/v1.2a/, v1.3/, v1.3a/ |
Gerber PCB files |
software/TZFS/ |
TZFS filing system software (active development) |
software.moved.to.TZFS/zOS/MZ700/ |
Legacy K64F zOS source for MZ-700 |
software.moved.to.TZFS/zOS/MZ2000/ |
K64F zOS for MZ-2000 (current reference) |
software.moved.to.TZFS/CPM/ |
CP/M BIOS |
tools/ |
Build utilities: build_meminitfiles.sh, Python BRAM init scripts |
build/ |
Quartus build output (Makefile-driven) |
FPGA/SW700/v1.3/ layout (the active source tree, organised per machine target under MZ700/, MZ2000/, MZ80A/):
| Path | Contents |
|---|---|
MZ700/coreMZ.vhd |
Top-level FPGA entity — video-only build |
MZ700/coreMZ_SoftCPU.vhd |
Top-level — video + soft CPUs (T80 + ZPU) |
MZ700/coreMZ_emuMZ.vhd |
Top-level — video + Sharp MZ series emulator core |
MZ700/coreMZ_pkg.vhd |
Package: host model constants, IMPL_SOFTCPU_Z80, IMPL_SOFTCPU_ZPUEVO flags |
MZ700/functions.vhd |
Utility functions shared across all modules |
MZ700/VideoController/VideoController.vhd |
Complete video pipeline (character, graphics, OSD, palette) |
MZ700/VideoController/VideoController_pkg.vhd |
Video mode constants (MODE_MZ700, MODE_MZ2000, etc.) |
MZ700/VideoController/ChrGenRAM_DP_3208.vhd |
Character generator dual-port BRAM template |
MZ700/VideoController/VideoRAM_DP_3216.vhd |
Video RAM dual-port BRAM template |
MZ700/softT80/softT80.vhd |
T80 soft Z80 wrapper module |
MZ700/softT80/softT80_pkg.vhd |
T80 configuration package |
MZ700/softT80/T80/ |
T80 Z80 IP core source (T80.vhd, T80_ALU.vhd, T80_MCode.vhd, etc.) |
MZ700/softZPU/softZPU.vhd |
ZPU Evolution soft CPU wrapper |
MZ700/softZPU/softZPU_pkg.vhd |
ZPU configuration package |
MZ700/softZPU/ZPU/ |
ZPU Evolution core VHDL (zpu_core_evo_issue.vhd, etc.) |
MZ700/devices/sysbus/BRAM/ |
BRAM templates and boot ROMs |
MZ700/devices/sysbus/SDRAM/ |
SDRAM controller (W9864G6, 48LC16M16 variants) |
MZ700/devices/sysbus/uart/ |
UART peripheral |
MZ700/devices/sysbus/timer/ |
Timer controller |
MZ700/devices/sysbus/intr/ |
Interrupt controller |
MZ700/devices/sysbus/SDMMC/ |
SD/MMC card controller |
MZ700/devices/sysbus/RAM/ |
Dual-port synchronous RAM |
MZ700/PLL/ |
Altera PLL megafunction instances (video clock generation) |
MZ700/SFL/ |
Serial Flash Loader megafunction (EPCS NV boot ROM programming) |
MZ700/emuMZ/ |
Sharp MZ series FPGA emulator integration |
MZ700/build/ |
Quartus project files (.qpf, .qsf per device/build variant) |
CPLD Design
The CPLD sits between the Sharp MZ mainboard's 5V bus and the 3.3V FPGA and SRAM. It is the first level of logic encountered on every bus transaction and handles all timing-critical glue that cannot tolerate FPGA configuration latency.
Device: Altera MAX 7000A EPM7160
The EPM7160 is a 5V-tolerant CPLD with 160 macro cells arranged as 10 logic array blocks. At 5V tolerance it can accept the mainboard's TTL-level signals directly on its inputs while driving 3.3V outputs to the FPGA. This makes it ideal as the interface bridge: no separate level-translation chips are needed for the control bus.
The CPLD source consists of three files:
Key Functions
tranZPUterSW_EPM7160.vhd— the main architecture, containing all logic processes.tranZPUterSW_Toplevel_EPM7160.vhd— the I/O pin assignments (top-level entity).tranZPUterSW_pkg_EPM7160.vhd— the package (shared constants and type definitions).
Level translation and bus interface: The CPLD drives the VZ80_* signals (voltage-translated Z80 bus) from the mainboard's Z80_* signals (5V). All mainboard-facing pins operate at 5V tolerance; all FPGA-facing pins drive 3.3V.
/BUSRQ logic and bus mastering: The CPLD implements the S-R latches and combinatorial logic that control the Z80 /BUSRQ and /BUSACK handshake. When the K64F asserts
CTL_BUSRQn, the CPLD forwards this as Z80_BUSRQn to the mainboard Z80. When the Z80 acknowledges (Z80_BUSACKn goes low), the CPLD asserts TZ_BUSACKn to indicate the tranZPUter board owns the bus. Two S-R latches (notSRLatch1 for bus grant, notSRLatch3 for clock select) ensure glitch-free transitions.
Clock frequency switching: Two D flip-flops implement a glitch-free clock multiplexer. The Z80 can run on either the mainboard SYSCLK (original machine speed) or on CTLCLK (a programmable frequency generated by the K64F's timer). The active clock is selected by writing to the tranZPUter I/O port at 0x62–0x63. The CPLD ensures the changeover occurs cleanly by waiting for both clocks to be idle before switching.
TZMM mode register (MEM_MODE_LATCH): The 8-bit memory mode latch stores the current tranZPUter Memory Mode (TZMM). Writing to I/O port 0x60 loads this latch. Bits 4:0 are forwarded on the Z80_MEM[4:0] outputs to the FPGA where they select which 64KB block of 512KB SRAM is mapped into the Z80 address space. Bit 5 enables the I/O wait state generator.
I/O address decode: The combinatorial process decodes writes to the tranZPUter I/O range 0x60–0x6F into individual chip-select signals:
-- I/O Port decode (addr[7:4] = 0110, i.e. 0x60–0x6F): TZIO_CSn -- 0x60–0x6F tranZPUter I/O block active MEM_CFGn -- 0x60–0x61 write TZMM mode register SCK_CTLCLKn -- 0x62–0x63 switch to CTLCLK (K64F programmable frequency) SCK_SYSCLKn -- 0x64–0x65 switch back to SYSCLK (mainboard clock) SCK_RDn -- 0x66–0x67 read back current clock/mode state SVCREQn -- 0x68–0x69 service request to K64F SYSREQn -- 0x6A–0x6B system request
SRAM enable signals: The CPLD generates
TZMM Memory Modes
RAM_CSn, RAM_OEn, and RAM_WEn from the Z80 control signals and the TZMM mode latch, routing memory accesses either to the mainboard or to the tranZPUter SRAM depending on the current mode.
I/O wait state insertion: When bit 5 of MEM_MODE_LATCH is set (ENIOWAIT), the CPLD inserts a wait state on any I/O access to addresses 0xE0–0xFF. This gives the K64F sufficient time to service the I/O request before the Z80 reads the response. The wait is implemented with a further S-R latch (notSRLatch4) that holds Z80_WAITn low until the K64F de-asserts CTL_BUSRQn.
The TZMM mode register (written to I/O port 0x60) selects how the Z80 address space is mapped to mainboard memory versus tranZPUter SRAM blocks. The modes defined in the CPLD source are:
| Mode | Description |
|---|---|
| 0 | Default — all memory and I/O on mainboard (normal Sharp MZ operation) |
| 1 | As 0 but User ROM mapped to tranZPUter SRAM |
| 2–5 | TZFS modes — Monitor ROM, main RAM, User ROM and Floppy ROM in SRAM blocks 0–3 |
| 6–7 | CP/M modes — full 64KB in SRAM block 4/5 with mainboard video and floppy exceptions |
| 10–14 | MZ-700 specific modes — split maps for IPL ROM, main RAM, and D000–FFFF area |
| 24–31 | Direct block select — entire 64KB space from SRAM blocks 0–7 respectively |
FPGA Design
The Cyclone IV FPGA (EP4CE75F23I7, 75K logic elements, 2.8Mbit embedded RAM) provides the tranZPUter SW-700 with its video controller, soft CPU capability, and K64F communication interface. The same VHDL source tree supports three distinct build configurations controlled by which top-level file Quartus is pointed at:
CPU Switching State Machine
- coreMZ — video controller only (lightest build, fits smaller devices).
- coreMZ_SoftCPU — video controller plus T80 soft Z80 and ZPU Evolution (mainstream build for SW-700 v1.3).
- coreMZ_emuMZ — video controller plus the full Sharp MZ Series FPGA emulator core.
devices/sysbus/ tree and the same VideoController module.
The most architecturally significant logic in
T80 Soft Z80 Integration
coreMZ_SoftCPU.vhd is the CPU switching FSM. The tranZPUter SW-700 can run with:
- The hard Z80 on the MZ mainboard — the original Sharp CPU, clocked by the CPLD at mainboard speed or at the K64F programmable frequency.
- A soft T80 inside the FPGA — a cycle-accurate VHDL Z80 implementation clocked at up to 50–100 MHz, connected to the same bus via tri-state drivers.
- The ZPU Evolution — a 32-bit stack CPU inside the FPGA, used to run zOS when the K64F is not present.
- Assert
Z80_BUSRQnto request the hard Z80 release the bus. - Wait for
Z80_BUSACKnto go low (Z80 has tri-stated its bus). - Enable the selected soft CPU's bus drivers.
- Assert
SW_CPUENto the soft CPU module. - On return to hard CPU: disable soft CPU, de-assert
Z80_BUSRQn, wait for hard Z80 to resume.
MODE_CPU_SOFT signal (read from the CPU configuration register at I/O port 0x6D) controls which CPU is active. The TZFS SC command writes this register.
The T80 is a well-established open-source VHDL Z80 implementation. The tranZPUter wraps it in
ZPU Evolution Core
softT80.vhd, which provides:
- Software-controlled reset (
SW_RESET) and clock enable (SW_CLKEN) — allows the FPGA logic to hold the T80 in reset while bus ownership is transferred. - Separate system clock (
SYS_CLK, ~120 MHz internal) and Z80 clock (Z80_CLK) inputs — the T80 usesSYS_CLKfor its internal pipeline while presenting bus transactions atZ80_CLKspeed, matching the Sharp MZ bus timing requirements. - Full Z80 bus signal set:
T80_MREQn,T80_IORQn,T80_RDn,T80_WRn,T80_M1n,T80_RFSHn,T80_BUSACKn,T80_HALTn. - Separate data-in and data-out buses (
T80_DATA_IN,T80_DATA_OUT) rather than a bidirectional bus — the top-level module handles the tri-state merge onto the sharedVZ80_DATAbus.
softT80/T80/: T80.vhd (top level), T80_ALU.vhd (arithmetic/logic unit), T80_MCode.vhd (microcode decoder), T80_Reg.vhd (register file), T80_Pack.vhd (package). A second variant, T80sed.vhd, is a size-optimised edition.
In addition to the T80, the softT80/ directory contains stubs for the AZ80 (from the FPGA/a-z80/ tree) and NextZ80 alternative cores that were evaluated during development. The AZ80 essentially works but has a slight timing discrepancy when accessing Sharp I/O devices in the E000–E7FF region. The NextZ80 requires further integration work. The T80 remains the default production soft Z80.
The ZPU Evolution is a 32-bit stack-based soft CPU originally designed for embedded use in FPGAs. The tranZPUter wraps it in
Video Controller
softZPU.vhd and uses it to run zOS — the embedded operating system that provides SD card access and service request handling. The ZPU accesses the tranZPUter 512KB SRAM at a base address offset of 0x80000 (to avoid the Z80's 64KB address space), and the entire zOS image fits in BRAM at boot time.
The ZPU core source lives in softZPU/ZPU/: zpu_core_evo_issue.vhd (with in-order issue) and zpu_core_evo_noissue.vhd (sequential variant). The softZPU_pkg.vhd selects the variant and sets the system clock frequency constant SYSTEM_FREQUENCY.
VideoController.vhd is the largest single source file. It implements the complete video pipeline for the Sharp MZ series, emulating the original character display hardware and extending it with colour graphics and multiple output modes.
Display pipeline:
- Timing generation: A PLL (in
PLL/Video_Clock_IV.vhd) derives the pixel clock from the 50 MHz system clock. Horizontal and vertical sync counters track the current pixel position. Multiple timing parameter sets are stored — one per supported machine/mode combination (MZ-700 40/80 column, MZ-2000, MZ-80B, VGA upscaled modes). - Character plane: The video RAM (a dual-port BRAM,
VideoRAM_DP_3216.vhd) stores one byte per character cell (40 or 80 columns × 25 rows). Each byte indexes into the character generator RAM (ChrGenRAM_DP_3208.vhd), which holds the 8×8 pixel bitmaps. The character ROM content is loaded from BRAM initialisation data at synthesis time. - Graphics plane: Up to two graphics planes (GRAM I and GRAM II) each hold a pixel bitmap. The MZ-700 uses a 320×200 monochrome plane; the MZ-2000 uses 640×200 graphics regardless of character mode. Each plane is implemented as a dual-port BRAM allowing simultaneous Z80 write and display read access.
- OSD plane: An On-Screen Display plane is overlaid for status messages and mode indicators.
- Palette and blend: The three planes (character, graphics, OSD) are merged immediately before the palette lookup registers. The palette maps the combined pixel value to 4-bit R/G/B output for the VGA connector and for the original MZ composite output.
VideoController_pkg.vhd:
constant MODE_MZ80K : integer := 0; -- MZ-80K constant MODE_MZ80C : integer := 1; -- MZ-80C constant MODE_MZ1200 : integer := 2; -- MZ-1200 constant MODE_MZ80A : integer := 3; -- MZ-80A constant MODE_MZ700 : integer := 4; -- MZ-700 (primary target) constant MODE_MZ800 : integer := 5; -- MZ-800 constant MODE_MZ80B : integer := 7; -- MZ-80B constant MODE_MZ2000 : integer := 8; -- MZ-2000 (current extended target)Memory Controller
The FPGA's memory controller translates the TZMM mode value (received from the CPLD via
K64F Communication Interface
Z80_MEM[4:0]) into SRAM bank select signals. The tranZPUter board carries 512KB of fast SRAM organised as eight 64KB banks (blocks 0–7). For Z80 access, the FPGA presents a 64KB window into the selected block. For ZPU access, the full 512KB is directly addressable.
The memory controller also handles direct addressing of FPGA-internal resources (video RAM, graphics RAM, character generator RAM) via the upper address bits VZ80_A16–VZ80_A21. When these upper bits select an FPGA resource, bus cycles are steered to the appropriate internal BRAM rather than to external SRAM. This allows soft CPUs to access video memory without using the Sharp MZ's register-selected 8KB window mechanism.
The K64F Kinetis ARM Cortex-M4 microcontroller runs zOS and acts as the I/O processor for the tranZPUter. It communicates with the Z80 software through two mechanisms:
Service request protocol: When Z80 software writes to I/O port 0x68 (the SVCREQ port), the CPLD asserts
SVCREQn. The FPGA monitors this signal and raises an interrupt or flag visible to the K64F. The Z80 then inserts wait states (via the CPLD's ENIOWAIT mechanism) until the K64F completes the requested service and de-asserts CTL_BUSRQn.
Shared SRAM block: The service request parameters and response data are exchanged through a dedicated 128-byte area at address 0xED80 in the Z80 address space (within SRAM block 0). The Z80 writes a command byte and parameters at this address before asserting the service request; the K64F reads them, performs the operation (typically an SD card access or BIOS load), writes the result, and signals completion.
The FPGA's role in this protocol is passive — it provides the SRAM dual-port access so both the Z80 bus and the K64F's memory-mapped interface can reach the shared block simultaneously. The FPGA also monitors SVCREQn and can generate the interrupt signal to the K64F directly without requiring polling.
Building the FPGA
The FPGA build uses Quartus Prime (version 13.0.1 for compatibility with older Cyclone IV device support, or 17.1 Lite which is confirmed in the
.qsf project files). The Quartus project file is FPGA/SW700/v1.3/MZ700/build/coreMZ.qpf; individual build configurations are selected by pointing Quartus at the corresponding .qsf file:
| QSF File | Build | Device |
|---|---|---|
coreMZ_E75.qsf |
Video only, EP4CE75 | EP4CE75F23I7 |
coreMZ_E75_SoftCPU.qsf |
Video + T80 + ZPU, EP4CE75 | EP4CE75F23I7 |
coreMZ_E75_emuMZ.qsf |
Video + emulator, EP4CE75 | EP4CE75F23I7 |
coreMZ_E115.qsf |
Video only, EP4CE115 | EP4CE115F23I7 |
coreMZ_E115_SoftCPU.qsf |
Video + soft CPUs, EP4CE115 | EP4CE115F23I7 |
coreMZ_E115_emuMZ.qsf |
Video + emulator, EP4CE115 | EP4CE115F23I7 |
# Full compilation (command line): cd /dvlp/Projects/tranZPUter/FPGA/SW700/v1.3/MZ700/build quartus_sh --flow compile coreMZ --rev coreMZ_E75_SoftCPU # Programme FPGA via JTAG (volatile — lost on power cycle): quartus_pgm -m JTAG -o "P;output_files/coreMZ_E75_SoftCPU.sof@1" # Programme persistent NV boot ROM (via SFL megafunction in design): quartus_pgm -m JTAG -o "P;output_files/coreMZ_E75_SoftCPU.jic@1"
Note on NV programming: The FPGA boots from an EPCS64 serial configuration device. The design includes the Serial Flash Loader (SFL) megafunction (
Building the CPLD
SFL/SFL_IV.vhd) which, when IMPL_SFL = true in coreMZ_pkg.vhd, exposes a JTAG interface to the EPCS64 through the FPGA. This allows the FPGA to be programmed persistently through the same USB-Blaster connection without a separate SPI programmer.
# Compile and fit the CPLD: cd /dvlp/Projects/tranZPUter/CPLD/EPM7160 quartus_sh --flow compile "tranZPUterSW EPM7160" # Programme via JTAG using USB-Blaster: quartus_pgm -m JTAG -o "P;output_files/tranZPUterSW_EPM7160.pof"
The CPLD project file is
tranZPUterSW_EPM7160.qpf. The fitter tool (CPLDfit) is invoked automatically by the Quartus flow. Programming produces a .pof (Programmer Object File) which is written to the CPLD's internal EEPROM — persistent across power cycles.
Docker Build Environment
A Docker image containing Quartus (with the correct Cyclone IV device support) provides a reproducible build environment without requiring Quartus to be installed on the host. The image is used in the same pattern as the Video Module and other tranZPUter subprojects:
# Start the Quartus container (forwards display for GUI, passes USB for programmer): docker run --rm -it \ -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \ -v /dvlp/Projects/tranZPUter:/project \ --device /dev/bus/usb quartus:13.1 bash # Inside the container — compile the SoftCPU build for EP4CE75: cd /project/FPGA/SW700/v1.3/MZ700/build quartus_sh --flow compile coreMZ --rev coreMZ_E75_SoftCPU # Programme via JTAG (USB-Blaster forwarded via --device): quartus_pgm -m JTAG -o "P;output_files/coreMZ_E75_SoftCPU.sof@1"
The
--device /dev/bus/usb flag gives the container access to the USB subsystem so the USB-Blaster cable appears inside the container as it would on the host. On some Linux hosts you may also need to add --privileged or configure udev rules for the USB-Blaster device (Altera/Intel USB-Blaster VID:PID 09fb:6001).
K64F Firmware (zOS)
The K64F Kinetis microcontroller on the tranZPUter SW-700 runs zOS — a small embedded operating system providing SD card initialisation and file I/O, service request processing, and system startup sequencing. The K64F source is in
software.moved.to.TZFS/zOS/; the MZ-700 firmware is in zOS/MZ700/ and the MZ-2000 firmware (current reference build) is in zOS/MZ2000/.
The firmware is written in C (with some ARM assembly startup code in startup/) and built with the ARM GCC toolchain. The build system is Make-based; a top-level build script assembles all components:
# Build all K64F firmware from the TZFS tree: cd /dvlp/Projects/tranZPUter/software/TZFS ./buildhost.sh # Or build just the MZ-700 zOS firmware: cd /dvlp/Projects/tranZPUter/software.moved.to.TZFS/zOS/MZ700 ./build.sh
Flashing to the K64F: The Kinetis K64F includes an OpenSDA bootloader on a secondary microcontroller. When the K64F board is connected via USB with the reset button held, it enumerates as a USB mass storage device (a virtual drive). Copy the compiled
.bin file to the root of this drive; the OpenSDA bootloader programmes the K64F's flash and resets automatically. The LED on the board indicates completion.
OpenSDA debugging: The same USB connection supports GDB-over-OpenSDA when the board runs normally (not in bootloader mode). Connect with OpenOCD and the Kinetis target configuration, then attach GDB to the OpenOCD server at localhost:3333 for full source-level debugging of zOS.
TZFS Software
TZFS (tranZPUter Filing System) is the Z80 software stack that runs on the Sharp MZ host CPU and provides the command monitor, SD card filing system, CP/M boot loader, and machine emulation commands. The TZFS source is in
software/TZFS/ (also symlinked to /dvlp/Projects/TZFS/).
TZFS shares its roots with RFS (the RomDisk Filing System) but is adapted for the tranZPUter's large 512KB SRAM, the K64F I/O processor, and the multi-machine target support. Key source files:
| Source file | Contents |
|---|---|
asm/tzfs.asm |
Main TZFS bank 0 — command dispatcher, bank-switch stub |
asm/tzfs_bank2.asm |
SD card controller |
asm/tzfs_bank3.asm |
Memory utilities (hex dump, memory edit, tape←→SD) |
asm/tzfs_bank4.asm |
CMT (tape) controller |
asm/include/tzfs_definitions.asm |
Central configuration: BUILD_MZ700, BUILD_MZ2000, ENADEBUG, address constants |
asm/include/tzfs_svcstruct.asm |
Service request structure definitions (shared memory at 0xED80) |
asm/cbios.asm |
CP/M CBIOS entry point and disk parameter tables |
asm/cbiosII.asm |
CBIOS II — extended CBIOS for tranZPUter hardware |
asm/monitor_sa1510.asm |
Patched SA-1510 monitor ROM |
asm/mz2000_ipl.asm |
MZ-2000 IPL ROM |
# Assemble TZFS and all ROM images: cd /dvlp/Projects/tranZPUter/software/TZFS ./buildhost.sh # Assemble only TZFS (faster during development): tools/assemble_tzfs.sh # Copy assembled images to a mounted SD card: tools/copytosd.sh -D/dvlp/Projects/tranZPUter -M/media/sdcard -tMZ-700
The
buildhost.sh script calls four sub-scripts in sequence: assemble_tzfs.sh, assemble_roms.sh, assemble_cpm.sh, and make_cpmdisks.sh. Each must succeed before the next runs. The optional -m flag to buildhost.sh additionally runs processMZFfiles.sh to re-sector any changed MZF program files — this is slow and only needed when the software library changes.
For full TZFS source walkthrough see the TZFS Developer's Guide.
Adding Support for a New Sharp MZ Machine
The tranZPUter SW-700 is designed for multi-machine support. Adding a new Sharp MZ target requires coordinated changes across the CPLD VHDL, FPGA VHDL, K64F firmware, and TZFS software. The steps below use a hypothetical new machine "MZ-XXXX" as the example.
-
CPLD: add TZMM mode constants in
CPLD/EPM7160/tranZPUterSW_EPM7160.vhd. Define new mode values (extend the 5-bit MEM_MODE_LATCH decode) for the new machine's memory map — which address ranges live on the mainboard versus in which SRAM block. Document the new mode numbers in the comment block alongside the existing mode table. -
FPGA: add host hardware model constant in
FPGA/SW700/v1.3/MZ700/coreMZ_pkg.vhd. Add aHOST_HW_MZXXXXconstant following the existing pattern (e.g.constant HOST_HW_MZXXXX : integer := 8;). Add the corresponding video mode constant inVideoController/VideoController_pkg.vhd(MODE_MZXXXX : integer := N). -
FPGA: add video timing parameters in
VideoController/VideoController.vhd. The video timing tables are arrays indexed by machine mode. Add a new row with the new machine's horizontal/vertical pixel counts, sync polarities, front/back porch widths, and character cell dimensions. If the machine uses a non-standard pixel clock frequency, a new PLL configuration may be needed inPLL/. -
K64F: add service commands in
software.moved.to.TZFS/zOS/MZ2000/. Add aLOAD_MZXXXXBIOSservice command handler that loads the new machine's monitor/IPL ROM from the SD card into the designated SRAM block. Follow the existingLOAD_MZ700BIOSandLOAD_MZ2000BIOScommand handlers as templates. -
TZFS: add machine model code in
software/TZFS/asm/include/tzfs_definitions.asm. Add aBUILD_MZXXXX EQU 0flag. Add new conditional assembly blocks (following the existingIF BUILD_MZ2000pattern) wherever machine-specific I/O port addresses, screen dimensions, or memory layouts differ. -
TZFS: add BIOS load and machine switch command in
tzfs.asm/ the relevant bank file. Implement anMZ XXXXmonitor command that: (a) sets the appropriate TZMM mode, (b) issues the LOAD_MZXXXXBIOS service request to the K64F, and (c) sets the FPGA video mode register toMODE_MZXXXX. -
Test: From the TZFS monitor, type
MZ XXXX(or the appropriate command string). Verify the FPGA switches video mode, the K64F loads the ROM, and the machine boots correctly. UseRIO 0x6Dto read the CPU Info register andRIO 0x60to verify the active TZMM mode.
Debugging Tips
SignalTap II Logic Analyser: Quartus Prime includes SignalTap II, an in-system logic analyser that captures signal transitions on the live FPGA without modifying the circuit's timing. To add a SignalTap probe: open the SignalTap II file in the Quartus project, add the signals of interest (e.g.
BUS_STATE, VZ80_ADDR, MEM_MODE_LATCH), set a trigger condition, and recompile. The analyser streams captured data over the JTAG cable. This is the primary tool for diagnosing bus timing and FSM sequencing issues.
K64F OpenSDA debugging: The K64F has built-in OpenSDA support. Connect via USB, start OpenOCD with the Kinetis K64 target configuration, and attach GDB to localhost:3333 for source-level debugging of zOS service request handlers and SD card initialisation code.
TZFS register I/O commands: From the TZFS monitor, the RIO (Read I/O) command reads directly from FPGA/CPLD registers:
RIO 0x6D— CPU Info register: returns the enabled soft CPU capabilities (T80, ZPU, etc.).RIO 0x6F— System Info register: returns FPGA build variant and feature flags.RIO 0x60— reads back the current TZMM mode on builds where the mode latch is readable.
RIO 0x60 and cross-reference against the mode table in the CPLD source to confirm the expected block mapping is active.
Enable ENADEBUG: Set ENADEBUG EQU 1 in software/TZFS/asm/include/tzfs_definitions.asm before building. This enables verbose output during SD card initialisation (CMD0/CMD8/ACMD41 responses, sector read status) and during service request dispatch. Useful for diagnosing SD card compatibility issues and K64F communication problems.
Soft CPU clock rate: If the T80 produces incorrect results on physical hardware accesses (e.g. missed keystrokes or wrong data from the Sharp I/O region E000–E7FF), reduce the effective T80 clock rate by adjusting SW_CLKEN timing in softT80.vhd. The AZ80 core is known to have a marginal timing issue in this region; use the T80 for production.
Video timing issues: If switching VGA modes causes flicker that persists until switching again, the PLL for that mode has not locked. This is a known issue with modes that use a separate PLL instance (VGA modes 1 and 2 in some builds). Switching away and back to the affected mode forces the PLL to re-acquire lock.
BRAM initialisation: The character generator BRAM content is embedded at synthesis time as VHDL array constants in ChrGenRAM_DP_3208.vhd. If the character display shows garbage after a build, verify that the BRAM init data matches the target machine's character set. Use tools/build_meminitfiles.sh to regenerate the BRAM init files from source character ROM binaries, then rebuild.
Reference Sites
| Resource | Link |
|---|---|
| tranZPUter SW-700 project page | /sharpmz-700-upgrades-tranzputer-sw/ |
| tranZPUter SW-700 current README | /sharpmz-700-upgrades-tranzputer-sw-current/ |
| TZFS User Manual | /tzfs-usermanual/ |
| TZFS Developer’s Guide | /tzfs-developersguide/ |
| Sharp MZ Series FPGA Emulator | /sharpmz-emulator/ |
| tranZPUter Gitea repository | git.eaw.app/eaw/tranZPUter |
| Altera MAX 7000A datasheet | EPM7160 device family — macro cell architecture, timing specs |
| Altera Cyclone IV datasheet | EP4CE75 / EP4CE115 — logic element count, BRAM, PLL specifications |
| Quartus Prime 13.0.1 | Intel (Altera) — CPLD/FPGA synthesis, CPLDfit, SignalTap II |
| T80 Z80 core | OpenCores — cycle-accurate VHDL Z80 implementation |
| ZPU Evolution | OpenCores — 32-bit stack CPU for FPGA embedding |
| Zilog Z80 CPU User Manual | Standard datasheet — bus timing, instruction set reference |