- Added build_image() function and --image flag to build.sh that invokes Build_FusionX.sh to create the Linux SD card image for the SSD202 SOM - Fixed all hardcoded paths in Build_FusionX.sh to use relative paths derived from the script's directory location - Image outputs (sdrootfs.tar.gz, SigmastarUpgradeSD.bin) can be included in Gitea releases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
493 lines
17 KiB
Bash
Executable File
493 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
|
#########################################################################################################
|
|
##
|
|
## Name: build.sh
|
|
## Created: March 2026
|
|
## Author(s): Philip Smart
|
|
## Description: FusionX global build script
|
|
## Builds all software components for the FusionX tranZPUter board:
|
|
## - Z80 assembly ROMs (monitor, MS BASIC, CP/M, TZFS)
|
|
## - Linux kernel modules (z80drv, ttymzdrv) for each target machine
|
|
## - User-space applications (sharpbiter, k64fcpu, z80ctrl)
|
|
## - SPI tools (mspi_main)
|
|
## - CPLD bitstreams for each target machine (requires Quartus Docker)
|
|
##
|
|
## Credits:
|
|
## Copyright: (c) 2019-2026 Philip Smart <philip.smart@net2net.org>
|
|
##
|
|
## History: March 2026 - Initial global build script.
|
|
##
|
|
#########################################################################################################
|
|
## This source file is free software: you can redistribute it and#or modify
|
|
## it under the terms of the GNU General Public License as published
|
|
## by the Free Software Foundation, either version 3 of the License, or
|
|
## (at your option) any later version.
|
|
##
|
|
## This source file is distributed in the hope that it will be useful,
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
## GNU General Public License for more details.
|
|
##
|
|
## You should have received a copy of the GNU General Public License
|
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#########################################################################################################
|
|
|
|
# Root directory of the FusionX project.
|
|
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
# Directories.
|
|
SOFTDIR="${ROOT_DIR}/software"
|
|
ASMDIR="${SOFTDIR}/asm"
|
|
INCDIR="${ASMDIR}/include"
|
|
TMPDIR="${SOFTDIR}/tmp"
|
|
ROMDIR="${SOFTDIR}/roms"
|
|
TOOLDIR="${SOFTDIR}/tools"
|
|
CPLDDIR="${ROOT_DIR}/CPLD/v1.0"
|
|
DRVDIR="${SOFTDIR}/FusionX/src/z80drv"
|
|
TTYDIR="${SOFTDIR}/FusionX/src/ttymz"
|
|
SPIDIR="${SOFTDIR}/FusionX/src/spitools"
|
|
BINDIR="${SOFTDIR}/FusionX/bin"
|
|
MODDIR="${SOFTDIR}/FusionX/modules"
|
|
|
|
# Tools.
|
|
ASM_JAR="${TOOLDIR}/glass-0.5.1.jar"
|
|
CROSS_PREFIX="arm-linux-gnueabihf-"
|
|
|
|
# Build targets.
|
|
SOFTWARE_TARGETS="MZ80A MZ700 MZ2000"
|
|
CPLD_TARGETS="MZ80A MZ700 MZ2000 PCW8256"
|
|
|
|
# Assembly build lists.
|
|
BUILDROMLIST="mz2000_ipl_original mz2000_ipl_tzpu mz2000_ipl_fusionx mz800_1z_013b mz800_9z_504m mz800_iocs mz80afi monitor_sa1510 monitor_80c_sa1510 monitor_1z-013a monitor_80c_1z-013a monitor_1z-013a-km monitor_80c_1z-013a-km mz80b_ipl"
|
|
BUILDMZFLIST="5z009-1b sa-5510_tzfs msbasic_mz80a msbasic_mz700 msbasic_tz40 msbasic_tz80 sharpmz-test"
|
|
BUILDTZFSROMLIST="tzfs"
|
|
BUILDTZFSMZFLIST="testtz"
|
|
CPMVERSIONS="mz700_80c:0 mz80a_80c:1 mz80a_std:2"
|
|
|
|
# Counters for summary.
|
|
PASS=0
|
|
FAIL=0
|
|
SKIP=0
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Helper functions.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
log_info() { echo "==> $*"; }
|
|
log_ok() { echo " [OK] $*"; PASS=$((PASS + 1)); }
|
|
log_fail() { echo " [FAIL] $*"; FAIL=$((FAIL + 1)); }
|
|
log_skip() { echo " [SKIP] $*"; SKIP=$((SKIP + 1)); }
|
|
|
|
usage() {
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "Build all or selected FusionX components."
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --all Build everything (default)"
|
|
echo " --asm Build Z80 assembly ROMs only"
|
|
echo " --tzfs Build TZFS ROM only"
|
|
echo " --cpm Build CP/M only"
|
|
echo " --drivers Build kernel modules and user-space apps only"
|
|
echo " --spi Build SPI tools only"
|
|
echo " --cpld Build CPLD bitstreams only (requires Quartus Docker)"
|
|
echo " --image Build Linux SD card image (requires ARM cross-compiler)"
|
|
echo " --clean Clean all build artifacts"
|
|
echo " --help Show this help"
|
|
echo ""
|
|
echo "Software targets: ${SOFTWARE_TARGETS}"
|
|
echo "CPLD targets: ${CPLD_TARGETS}"
|
|
exit 0
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Build Z80 Assembly ROMs and MZF files.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
build_asm_roms() {
|
|
log_info "Building Z80 Assembly ROMs and MZF files..."
|
|
|
|
if ! command -v java &>/dev/null; then
|
|
log_skip "Java not found - cannot assemble Z80 ROMs"
|
|
return
|
|
fi
|
|
if [ ! -f "${ASM_JAR}" ]; then
|
|
log_skip "GLASS assembler not found at ${ASM_JAR}"
|
|
return
|
|
fi
|
|
|
|
mkdir -p "${TMPDIR}" "${ROMDIR}"
|
|
|
|
for f in ${BUILDROMLIST} ${BUILDMZFLIST}; do
|
|
SRCNAME="${f}"
|
|
ASMNAME="${f}.asm"
|
|
OBJNAME="${f}.obj"
|
|
SYMNAME="${f}.sym"
|
|
ROMNAME="${f}.rom"
|
|
MZFNAME="${f}.mzf"
|
|
|
|
# MS BASIC variant handling.
|
|
case "${SRCNAME}" in
|
|
msbasic_mz80a)
|
|
ASMNAME="msbasic.asm"
|
|
echo "BUILD_VERSION EQU 0" > "${INCDIR}/MSBASIC_BuildVersion.asm"
|
|
;;
|
|
msbasic_mz700)
|
|
ASMNAME="msbasic.asm"
|
|
echo "BUILD_VERSION EQU 1" > "${INCDIR}/MSBASIC_BuildVersion.asm"
|
|
;;
|
|
msbasic_tz40)
|
|
ASMNAME="msbasic.asm"
|
|
echo "BUILD_VERSION EQU 2" > "${INCDIR}/MSBASIC_BuildVersion.asm"
|
|
;;
|
|
msbasic_tz80)
|
|
ASMNAME="msbasic.asm"
|
|
echo "BUILD_VERSION EQU 3" > "${INCDIR}/MSBASIC_BuildVersion.asm"
|
|
;;
|
|
esac
|
|
|
|
if java -jar "${ASM_JAR}" "${ASMDIR}/${ASMNAME}" "${TMPDIR}/${OBJNAME}" "${TMPDIR}/${SYMNAME}" -I "${INCDIR}" -I "${ASMDIR}" 2>&1; then
|
|
if echo "${BUILDROMLIST}" | grep -qw "${SRCNAME}"; then
|
|
cp "${TMPDIR}/${OBJNAME}" "${ROMDIR}/${ROMNAME}"
|
|
log_ok "ROM: ${ROMNAME}"
|
|
else
|
|
cp "${TMPDIR}/${OBJNAME}" "${ROMDIR}/${MZFNAME}"
|
|
log_ok "MZF: ${MZFNAME}"
|
|
fi
|
|
else
|
|
log_fail "${SRCNAME}"
|
|
fi
|
|
done
|
|
|
|
# Composite MZ-800 ROM.
|
|
if [ -f "${ROMDIR}/mz800_1z_013b.rom" ] && [ -f "${ROMDIR}/mz800_cgrom.ori" ] && \
|
|
[ -f "${ROMDIR}/mz800_9z_504m.rom" ] && [ -f "${ROMDIR}/mz800_iocs.rom" ]; then
|
|
cat "${ROMDIR}/mz800_1z_013b.rom" "${ROMDIR}/mz800_cgrom.ori" \
|
|
"${ROMDIR}/mz800_9z_504m.rom" "${ROMDIR}/mz800_iocs.rom" > "${ROMDIR}/mz800_ipl.rom"
|
|
log_ok "ROM: mz800_ipl.rom (composite)"
|
|
fi
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Build TZFS ROM for each machine+board combination.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
build_tzfs() {
|
|
log_info "Building TZFS ROMs..."
|
|
|
|
if ! command -v java &>/dev/null; then
|
|
log_skip "Java not found - cannot assemble TZFS"
|
|
return
|
|
fi
|
|
|
|
mkdir -p "${TMPDIR}" "${ROMDIR}"
|
|
|
|
local DEFSFILE="${INCDIR}/tzfs_definitions.asm"
|
|
local MACHINES="MZ80A MZ700 MZ2000"
|
|
|
|
for machine in ${MACHINES}; do
|
|
# Set machine target in definitions.
|
|
for m in MZ80A MZ700 MZ1500 MZ2000; do
|
|
local val=0
|
|
[ "${m}" = "${machine}" ] && val=1
|
|
sed -i "s/^BUILD_${m}\s\+EQU\s\+[01]/BUILD_${m} EQU ${val}/" "${DEFSFILE}"
|
|
done
|
|
|
|
# Set board to FusionX.
|
|
sed -i "s/^BUILD_FUSIONX\s\+EQU\s\+[01]/BUILD_FUSIONX EQU 1/" "${DEFSFILE}"
|
|
sed -i "s/^BUILD_PICOZ80\s\+EQU\s\+[01]/BUILD_PICOZ80 EQU 0/" "${DEFSFILE}"
|
|
|
|
for f in ${BUILDTZFSROMLIST} ${BUILDTZFSMZFLIST}; do
|
|
if java -jar "${ASM_JAR}" "${ASMDIR}/${f}.asm" "${TMPDIR}/${f}.obj" "${TMPDIR}/${f}.sym" -I "${INCDIR}" -I "${ASMDIR}" 2>&1; then
|
|
local lower_machine=$(echo "${machine}" | tr '[:upper:]' '[:lower:]')
|
|
if echo "${BUILDTZFSROMLIST}" | grep -qw "${f}"; then
|
|
cp "${TMPDIR}/${f}.obj" "${ROMDIR}/${f}_${lower_machine}_fusionx.rom"
|
|
log_ok "TZFS ROM: ${f}_${lower_machine}_fusionx.rom"
|
|
else
|
|
cp "${TMPDIR}/${f}.obj" "${ROMDIR}/${f}_${lower_machine}_fusionx.mzf"
|
|
log_ok "TZFS MZF: ${f}_${lower_machine}_fusionx.mzf"
|
|
fi
|
|
else
|
|
log_fail "TZFS: ${f} (${machine})"
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Build CP/M for each version.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
build_cpm() {
|
|
log_info "Building CP/M variants..."
|
|
|
|
if ! command -v java &>/dev/null; then
|
|
log_skip "Java not found - cannot assemble CP/M"
|
|
return
|
|
fi
|
|
|
|
mkdir -p "${TMPDIR}" "${ROMDIR}"
|
|
|
|
for ver in ${CPMVERSIONS}; do
|
|
local FILEEXT=$(echo "${ver}" | cut -d: -f1)
|
|
local BUILDVER=$(echo "${ver}" | cut -d: -f2)
|
|
echo "BUILD_VERSION EQU ${BUILDVER}" > "${INCDIR}/cpm_buildversion.asm"
|
|
|
|
local all_ok=true
|
|
for f in cbios cpm22; do
|
|
if java -jar "${ASM_JAR}" "${ASMDIR}/${f}.asm" "${TMPDIR}/${f}.obj" "${TMPDIR}/${f}.sym" -I "${INCDIR}" -I "${ASMDIR}" 2>&1; then
|
|
cp "${TMPDIR}/${f}.obj" "${ROMDIR}/${f}.bin"
|
|
else
|
|
log_fail "CP/M: ${f} (${FILEEXT})"
|
|
all_ok=false
|
|
fi
|
|
done
|
|
|
|
if ${all_ok}; then
|
|
cat "${ROMDIR}/cpm22.bin" "${ROMDIR}/cbios.bin" > "${ROMDIR}/cpm223_${FILEEXT}.bin"
|
|
log_ok "CP/M: cpm223_${FILEEXT}.bin"
|
|
fi
|
|
done
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Build kernel modules and user-space applications for each target machine.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
build_drivers() {
|
|
log_info "Building kernel modules and user-space applications..."
|
|
|
|
if ! command -v ${CROSS_PREFIX}gcc &>/dev/null; then
|
|
log_skip "ARM cross-compiler (${CROSS_PREFIX}gcc) not found"
|
|
return
|
|
fi
|
|
|
|
local KERNEL_DIR="${SOFTDIR}/linux/kernel"
|
|
if [ ! -f "${KERNEL_DIR}/Makefile" ]; then
|
|
log_skip "Kernel source tree not found at ${KERNEL_DIR}"
|
|
return
|
|
fi
|
|
|
|
for target in ${SOFTWARE_TARGETS}; do
|
|
log_info " Target: ${target}"
|
|
|
|
# Build z80drv kernel module + user-space tools.
|
|
# Must cd into directory because Makefile uses $(PWD) for kernel path.
|
|
if [ -f "${DRVDIR}/Makefile" ]; then
|
|
if (cd "${DRVDIR}" && make "${target}" 2>&1); then
|
|
(cd "${DRVDIR}" && make install 2>&1) || true
|
|
log_ok "z80drv + apps (${target})"
|
|
else
|
|
log_fail "z80drv (${target})"
|
|
fi
|
|
(cd "${DRVDIR}" && make clean 2>&1) || true
|
|
fi
|
|
|
|
# Build ttymzdrv kernel module.
|
|
if [ -f "${TTYDIR}/Makefile" ]; then
|
|
if (cd "${TTYDIR}" && make "${target}" 2>&1); then
|
|
(cd "${TTYDIR}" && make install 2>&1) || true
|
|
log_ok "ttymzdrv (${target})"
|
|
else
|
|
log_fail "ttymzdrv (${target})"
|
|
fi
|
|
(cd "${TTYDIR}" && make clean 2>&1) || true
|
|
fi
|
|
done
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Build SPI tools.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
build_spi() {
|
|
log_info "Building SPI tools..."
|
|
|
|
if ! command -v ${CROSS_PREFIX}gcc &>/dev/null; then
|
|
log_skip "ARM cross-compiler (${CROSS_PREFIX}gcc) not found"
|
|
return
|
|
fi
|
|
|
|
if [ -f "${SPIDIR}/Makefile" ]; then
|
|
if (cd "${SPIDIR}" && make all 2>&1); then
|
|
log_ok "SPI tools (mspi_main)"
|
|
else
|
|
log_fail "SPI tools"
|
|
fi
|
|
else
|
|
log_skip "SPI tools Makefile not found"
|
|
fi
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Build CPLD bitstreams for each target machine.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
build_cpld() {
|
|
log_info "Building CPLD bitstreams..."
|
|
|
|
if ! command -v quartus_sh &>/dev/null; then
|
|
log_skip "Quartus not found in PATH (use Quartus Docker: qp130)"
|
|
return
|
|
fi
|
|
|
|
for target in ${CPLD_TARGETS}; do
|
|
local BUILDDIR="${CPLDDIR}/${target}/build"
|
|
local PROJECT="tzpuFusionX_${target}"
|
|
|
|
if [ ! -f "${BUILDDIR}/${PROJECT}.qpf" ]; then
|
|
log_skip "CPLD: No Quartus project for ${target}"
|
|
continue
|
|
fi
|
|
|
|
log_info " Compiling CPLD: ${target}"
|
|
if (cd "${BUILDDIR}" && quartus_sh --flow compile "${PROJECT}" 2>&1); then
|
|
if [ -f "${BUILDDIR}/output_files/${PROJECT}.pof" ]; then
|
|
log_ok "CPLD: ${PROJECT}.pof"
|
|
else
|
|
log_fail "CPLD: ${target} (no .pof output)"
|
|
fi
|
|
else
|
|
log_fail "CPLD: ${target}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Build Linux SD card image using Build_FusionX.sh.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
build_image() {
|
|
log_info "Building Linux SD card image..."
|
|
|
|
local BUILD_SCRIPT="${SOFTDIR}/linux/Build_FusionX.sh"
|
|
local IMAGE_OUTPUT="${SOFTDIR}/linux/project/image/output/images"
|
|
|
|
if [ ! -f "${BUILD_SCRIPT}" ]; then
|
|
log_skip "Build_FusionX.sh not found at ${BUILD_SCRIPT}"
|
|
return
|
|
fi
|
|
|
|
if ! command -v ${CROSS_PREFIX}gcc &>/dev/null; then
|
|
log_skip "ARM cross-compiler (${CROSS_PREFIX}gcc) not found - cannot build Linux image"
|
|
return
|
|
fi
|
|
|
|
if [ ! -d "${SOFTDIR}/linux/kernel" ] || [ ! -d "${SOFTDIR}/linux/boot" ]; then
|
|
log_skip "Linux kernel/boot source tree not found"
|
|
return
|
|
fi
|
|
|
|
chmod +x "${BUILD_SCRIPT}"
|
|
log_info " Running Build_FusionX.sh -f nand -p ssd202 -o 2D06 ..."
|
|
if "${BUILD_SCRIPT}" -f nand -p ssd202 -o 2D06 2>&1; then
|
|
# Check for output images.
|
|
local has_output=false
|
|
if [ -f "${IMAGE_OUTPUT}/sdrootfs.tar.gz" ]; then
|
|
log_ok "Image: sdrootfs.tar.gz"
|
|
has_output=true
|
|
fi
|
|
if [ -f "${IMAGE_OUTPUT}/SigmastarUpgradeSD.bin" ]; then
|
|
log_ok "Image: SigmastarUpgradeSD.bin"
|
|
has_output=true
|
|
fi
|
|
if [ -f "${IMAGE_OUTPUT}/SigmastarUpgrade.bin" ]; then
|
|
log_ok "Image: SigmastarUpgrade.bin"
|
|
has_output=true
|
|
fi
|
|
if ! ${has_output}; then
|
|
log_fail "Linux image build produced no output files"
|
|
fi
|
|
else
|
|
log_fail "Linux image build (Build_FusionX.sh)"
|
|
fi
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Clean all build artifacts.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
do_clean() {
|
|
log_info "Cleaning build artifacts..."
|
|
|
|
# Assembly temporaries.
|
|
rm -f "${TMPDIR}"/*.obj "${TMPDIR}"/*.sym 2>/dev/null
|
|
|
|
# Driver build artifacts.
|
|
if [ -f "${DRVDIR}/Makefile" ]; then
|
|
(cd "${DRVDIR}" && make clean 2>/dev/null) || true
|
|
fi
|
|
if [ -f "${TTYDIR}/Makefile" ]; then
|
|
(cd "${TTYDIR}" && make clean 2>/dev/null) || true
|
|
fi
|
|
if [ -f "${SPIDIR}/Makefile" ]; then
|
|
(cd "${SPIDIR}" && make clean 2>/dev/null) || true
|
|
fi
|
|
|
|
log_ok "Clean complete"
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------
|
|
# Main.
|
|
#-------------------------------------------------------------------------------------------------------
|
|
|
|
BUILD_ASM=false
|
|
BUILD_TZFS=false
|
|
BUILD_CPM=false
|
|
BUILD_DRIVERS=false
|
|
BUILD_SPI=false
|
|
BUILD_CPLD=false
|
|
BUILD_IMAGE=false
|
|
BUILD_ALL=false
|
|
DO_CLEAN=false
|
|
|
|
if [ $# -eq 0 ]; then
|
|
BUILD_ALL=true
|
|
fi
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--all) BUILD_ALL=true ;;
|
|
--asm) BUILD_ASM=true ;;
|
|
--tzfs) BUILD_TZFS=true ;;
|
|
--cpm) BUILD_CPM=true ;;
|
|
--drivers) BUILD_DRIVERS=true ;;
|
|
--spi) BUILD_SPI=true ;;
|
|
--cpld) BUILD_CPLD=true ;;
|
|
--image) BUILD_IMAGE=true ;;
|
|
--clean) DO_CLEAN=true ;;
|
|
--help|-h) usage ;;
|
|
*) echo "Unknown option: $1"; usage ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
echo "========================================"
|
|
echo " FusionX Build System"
|
|
echo "========================================"
|
|
echo ""
|
|
|
|
if ${DO_CLEAN}; then
|
|
do_clean
|
|
exit 0
|
|
fi
|
|
|
|
if ${BUILD_ALL} || ${BUILD_ASM}; then build_asm_roms; fi
|
|
if ${BUILD_ALL} || ${BUILD_TZFS}; then build_tzfs; fi
|
|
if ${BUILD_ALL} || ${BUILD_CPM}; then build_cpm; fi
|
|
if ${BUILD_ALL} || ${BUILD_DRIVERS}; then build_drivers; fi
|
|
if ${BUILD_ALL} || ${BUILD_SPI}; then build_spi; fi
|
|
if ${BUILD_ALL} || ${BUILD_CPLD}; then build_cpld; fi
|
|
if ${BUILD_ALL} || ${BUILD_IMAGE}; then build_image; fi
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo " Build Summary"
|
|
echo " Passed: ${PASS} Failed: ${FAIL} Skipped: ${SKIP}"
|
|
echo "========================================"
|
|
|
|
if [ ${FAIL} -gt 0 ]; then
|
|
exit 1
|
|
fi
|
|
exit 0
|