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 process in a VHDL file runs in parallel with every other process; signals change value all at once on each clock edge.

ENTITY and ARCHITECTURE
Every VHDL module has two parts:
  • ENTITY declares the module's interface — its input and output pins. Think of it as the function signature in a software language. The coreMZ entity, for example, declares CLOCK_50, VZ80_ADDR, VZ80_DATA, the VGA outputs, and dozens of other pins as in, out, or inout ports.
  • ARCHITECTURE contains the implementation — the internal signals, component instantiations, and processes that describe how outputs are computed from inputs. The architecture for coreMZ is named rtl (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
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:
  • 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.
Signals declared in the architecture are not visible outside it. They are analogous to local variables in software, except that every assignment to a signal takes effect at the end of the current delta cycle — changes do not propagate instantaneously within a single process execution.

PROCESS
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:
  • 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).
The CPLD is built with Quartus Prime 13.0.1 using the CPLDfit fitter tool. Programming uses a standard USB-Blaster cable in JTAG mode.

Key Functions
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 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.

TZMM Memory Modes
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:
  • 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.
All three share identical peripheral IP in the devices/sysbus/ tree and the same VideoController module.

CPU Switching State Machine
The most architecturally significant logic in coreMZ_SoftCPU.vhd is the CPU switching FSM. The tranZPUter SW-700 can run with:
  1. The hard Z80 on the MZ mainboard — the original Sharp CPU, clocked by the CPLD at mainboard speed or at the K64F programmable frequency.
  2. 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.
  3. The ZPU Evolution — a 32-bit stack CPU inside the FPGA, used to run zOS when the K64F is not present.
The switching FSM coordinates the handover:
  1. Assert Z80_BUSRQn to request the hard Z80 release the bus.
  2. Wait for Z80_BUSACKn to go low (Z80 has tri-stated its bus).
  3. Enable the selected soft CPU's bus drivers.
  4. Assert SW_CPUEN to the soft CPU module.
  5. On return to hard CPU: disable soft CPU, de-assert Z80_BUSRQn, wait for hard Z80 to resume.
The 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.

T80 Soft Z80 Integration
The T80 is a well-established open-source VHDL Z80 implementation. The tranZPUter wraps it in 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 uses SYS_CLK for its internal pipeline while presenting bus transactions at Z80_CLK speed, 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 shared VZ80_DATA bus.
The T80 core source files live in 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.

ZPU Evolution Core
The ZPU Evolution is a 32-bit stack-based soft CPU originally designed for embedded use in FPGAs. The tranZPUter wraps it in 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.

Video Controller
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:
  1. 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).
  2. 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.
  3. 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.
  4. OSD plane: An On-Screen Display plane is overlaid for status messages and mode indicators.
  5. 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.
Output modes: The video controller supports native composite output (COLR_OUT, CSYNC_OUTn) for the original Sharp monitor, and multiple VGA output modes (640×480, 800×600 upscaled from the native resolution) for modern displays. The active mode is selected by writing to the video mode I/O register.
Machine mode constants in 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 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_A16VZ80_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.

K64F Communication Interface
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 (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.

Building the CPLD
# 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.
  1. 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.
  2. FPGA: add host hardware model constant in FPGA/SW700/v1.3/MZ700/coreMZ_pkg.vhd. Add a HOST_HW_MZXXXX constant following the existing pattern (e.g. constant HOST_HW_MZXXXX : integer := 8;). Add the corresponding video mode constant in VideoController/VideoController_pkg.vhd (MODE_MZXXXX : integer := N).
  3. 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 in PLL/.
  4. K64F: add service commands in software.moved.to.TZFS/zOS/MZ2000/. Add a LOAD_MZXXXXBIOS service command handler that loads the new machine's monitor/IPL ROM from the SD card into the designated SRAM block. Follow the existing LOAD_MZ700BIOS and LOAD_MZ2000BIOS command handlers as templates.
  5. TZFS: add machine model code in software/TZFS/asm/include/tzfs_definitions.asm. Add a BUILD_MZXXXX EQU 0 flag. Add new conditional assembly blocks (following the existing IF BUILD_MZ2000 pattern) wherever machine-specific I/O port addresses, screen dimensions, or memory layouts differ.
  6. TZFS: add BIOS load and machine switch command in tzfs.asm / the relevant bank file. Implement an MZ XXXX monitor 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 to MODE_MZXXXX.
  7. 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. Use RIO 0x6D to read the CPU Info register and RIO 0x60 to 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.
TZMM mode verification: After any machine switch or TZFS command that changes the memory map, use 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