diff --git a/.gitignore b/.gitignore index 7bd807e5f..424936d92 100644 --- a/.gitignore +++ b/.gitignore @@ -341,6 +341,7 @@ software/FusionX/src/driver/Z80 software/FusionX/src/driver/Z80/ software/FusionX/src/driver/Z80.c.old software/FusionX/src/driver/z80ctrl +software/FusionX/src/driver/k64fcpu software/FusionX/src/driver/Z80.rc.in software/FusionX/src/hello software/FusionX/src/spitools @@ -1681,3 +1682,7 @@ software/FusionX/src/driver/MZ80A/z80vhw_rfs.c.bad software/FusionX/disk software/FusionX/roms software/linux/project/FusionX/ +software/FusionX/src/driver/MZ80A/k64fcpu.c.hld +software/linux/buildroot/.config.old +software/linux/kernel/scripts/kconfig/.mconf.cmd + diff --git a/software/FusionX/etc/startZ80_RFS.sh b/software/FusionX/etc/startZ80_RFS.sh index 883ebf58f..de2910c49 100755 --- a/software/FusionX/etc/startZ80_RFS.sh +++ b/software/FusionX/etc/startZ80_RFS.sh @@ -33,10 +33,10 @@ insmod z80drv.ko sleep 1 # Load the original RFS ROM images. -${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/MROM_256_${SCREENWIDTH}c.bin --addr 0x000000 -${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/USER_ROM_256_${SCREENWIDTH}c.bin --addr 0x80000 -${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/USER_ROM_II_256_${SCREENWIDTH}c.bin --addr 0x100000 -${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/USER_ROM_III_256_${SCREENWIDTH}c.bin --addr 0x180000 +${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/MROM_256_${SCREENWIDTH}c.bin --addr 0x000000 --type 1 +${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/USER_ROM_256_${SCREENWIDTH}c.bin --addr 0x80000 --type 1 +${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/USER_ROM_II_256_${SCREENWIDTH}c.bin --addr 0x100000 --type 1 +${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/rom/USER_ROM_III_256_${SCREENWIDTH}c.bin --addr 0x180000 --type 1 # Add the RFS Virtual Hardware to the driver. ${FUSIONXDIR}/bin/z80ctrl --adddev --device rfs diff --git a/software/FusionX/etc/startZ80_TZFS.sh b/software/FusionX/etc/startZ80_TZFS.sh new file mode 100755 index 000000000..28c3a5297 --- /dev/null +++ b/software/FusionX/etc/startZ80_TZFS.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +FUSIONXDIR=/apps/FusionX + +# Setup screen width, used to load correct Monitor ROM image. +SCREENWIDTH=40 +if [[ "$#" -ne 0 ]]; then + if [[ "$1" -eq 80 ]]; then + SCREENWIDTH=80 + fi +fi +echo "Screen width set to: ${SCREENWIDTH}" + +# Detach CPU 1 from scheduler and IRQ's as it will be dedicated to the z80drv. +for f in `ps -eaf |grep -v kthread_z80 | awk '{print $1}'` +do + taskset -pc 0 $f >/dev/null 2>/dev/null +done + +# Detach IRQ's +for I in $(ls /proc/irq) +do + if [[ -d "/proc/irq/$I" ]] + then + echo 0 > /proc/irq/$I/smp_affinity_list 2>/dev/null + fi +done + +# Load the Z80 driver. Small pause to ensure the driver is fully loaded and initialsed prior to loading ROM images. +cd ${FUSIONXDIR}/modules +rmmod z80drv 2>/dev/null +insmod z80drv.ko +sleep 1 + +# Load the original Monitor and TZFS ROM images. This is done in the K64F daemon but can be manually enabled. +#${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/roms/monitor_${SCREENWIDTH}c_sa1510.rom --addr 0x000000 --type 1 +#${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/roms/tzfs.rom --offset 0x000000 --len 0x001800 --addr 0x00E800 --type 1 +#${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/roms/tzfs.rom --offset 0x001800 --len 0x001000 --addr 0x01F000 --type 1 +#${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/roms/tzfs.rom --offset 0x002800 --len 0x001000 --addr 0x02F000 --type 1 +#${FUSIONXDIR}/bin/z80ctrl --loadrom --file ${FUSIONXDIR}/roms/tzfs.rom --offset 0x003800 --len 0x001000 --addr 0x03F000 --type 1 + +# Add the TZPU Virtual Hardware to the driver. +${FUSIONXDIR}/bin/z80ctrl --adddev --device tzpu + +# Start the K64F Virtual CPU Emulation. +${FUSIONXDIR}/bin/k64fcpu & + +# Ensure the system is set for performance mode with max frequency. +# NB: Enabling this prior to starting the Z80 results in a kernel error. +echo performance > /sys/devices//system/cpu/cpufreq/policy0/scaling_governor +echo 1200000 > /sys/devices//system/cpu/cpufreq/policy0/scaling_min_freq + +# Done. +echo "FusionX loaded and configured in RFS mode." diff --git a/software/FusionX/src/driver/MZ80A/k64fcpu.c b/software/FusionX/src/driver/MZ80A/k64fcpu.c new file mode 100644 index 000000000..903aec32a --- /dev/null +++ b/software/FusionX/src/driver/MZ80A/k64fcpu.c @@ -0,0 +1,3079 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: k64fcpu.c +// Created: Oct 2022 +// Author(s): Philip Smart +// Description: Z80 Control Interface +// This file contains a user space daemon which emulates the services provided by +// the K64FX512 Cortex-M4 CPU from NXP used as the I/O processor on all tranZPUter SW +// boards. +// The daemon connects to the Z80 driver memory and awaits requests via a signal +// and the host service request API, performs the required task and returns the result. +// The primary code base is take from tranzputer.c/.h which is part of the zSoft OS +// package. It is customised for this application. +// +// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi +// The Z80 CPU Emulator is the heart of the Z80 device driver. +// Copyright: (c) 2019-2023 Philip Smart +// (c) 1999-2023 Manuel Sainz de Baranda y Goñi +// +// History: Feb 2023 v1.0 - Source copied from zSoft and modified to run as a daemon, stripping +// out all low level control methods. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z80driver.h" +#include "tzpu.h" + +#define VERSION "1.0" +#define AUTHOR "P.D.Smart" +#define COPYRIGHT "(c) 2018-23" + +// Getopt_long is buggy so we use optparse. +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#include "optparse.h" + +// Device driver name. +#define DEVICE_FILENAME "/dev/z80drv" + +// Constants for the Sharp MZ80A MZF file format. +#define MZF_HEADER_SIZE 128 // Size of the MZF header. +#define MZF_ATTRIBUTE 0x00 // Code Type, 01 = Machine Code. +#define MZF_FILENAME 0x01 // Title/Name (17 bytes). +#define MZF_FILENAME_LEN 17 // Length of the filename, it is not NULL terminated, generally a CR can be taken as terminator but not guaranteed. +#define MZF_FILESIZE 0x12 // Size of program. +#define MZF_LOADADDR 0x14 // Load address of program. +#define MZF_EXECADDR 0x16 // Exec address of program. +#define MZF_COMMENT 0x18 // Comment, used for details of the file or startup code. +#define MZF_COMMENT_LEN 104 // Length of the comment field. +#define CMT_TYPE_OBJCD 0x001 // MZF contains a binary object. +#define CMT_TYPE_BTX1CD 0x002 // MZF contains a BASIC program. +#define CMT_TYPE_BTX2CD 0x005 // MZF contains a BASIC program. +#define CMT_TYPE_TZOBJCD0 0x0F8 // MZF contains a TZFS binary object for page 0. +#define CMT_TYPE_TZOBJCD1 0x0F9 +#define CMT_TYPE_TZOBJCD2 0x0FA +#define CMT_TYPE_TZOBJCD3 0x0FB +#define CMT_TYPE_TZOBJCD4 0x0FC +#define CMT_TYPE_TZOBJCD5 0x0FD +#define CMT_TYPE_TZOBJCD6 0x0FE +#define CMT_TYPE_TZOBJCD7 0x0FF // MZF contains a TZFS binary object for page 7. + +// Global scope variables used within the zOS kernel. +// +static t_z80Control z80Control; +static t_osControl osControl; +static t_svcControl svcControl; +// Shared memory between this process and the Z80 driver. +static t_Z80Ctrl *Z80Ctrl = NULL; +static uint8_t *Z80RAM = NULL; +static uint8_t *Z80ROM = NULL; +static uint8_t runControl = 0; + +// Mapping table to map Sharp MZ80A Ascii to Standard ASCII. +// +static t_asciiMap asciiMap[] = { + { 0x00 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x00 }, { 0x20 }, { 0x20 }, // 0x0F + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x1F + { 0x20 }, { 0x21 }, { 0x22 }, { 0x23 }, { 0x24 }, { 0x25 }, { 0x26 }, { 0x27 }, { 0x28 }, { 0x29 }, { 0x2A }, { 0x2B }, { 0x2C }, { 0x2D }, { 0x2E }, { 0x2F }, // 0x2F + { 0x30 }, { 0x31 }, { 0x32 }, { 0x33 }, { 0x34 }, { 0x35 }, { 0x36 }, { 0x37 }, { 0x38 }, { 0x39 }, { 0x3A }, { 0x3B }, { 0x3C }, { 0x3D }, { 0x3E }, { 0x3F }, // 0x3F + { 0x40 }, { 0x41 }, { 0x42 }, { 0x43 }, { 0x44 }, { 0x45 }, { 0x46 }, { 0x47 }, { 0x48 }, { 0x49 }, { 0x4A }, { 0x4B }, { 0x4C }, { 0x4D }, { 0x4E }, { 0x4F }, // 0x4F + { 0x50 }, { 0x51 }, { 0x52 }, { 0x53 }, { 0x54 }, { 0x55 }, { 0x56 }, { 0x57 }, { 0x58 }, { 0x59 }, { 0x5A }, { 0x5B }, { 0x5C }, { 0x5D }, { 0x5E }, { 0x5F }, // 0x5F + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x6F + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x7F + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0x8F + { 0x20 }, { 0x20 }, { 0x65 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x74 }, { 0x67 }, { 0x68 }, { 0x20 }, { 0x62 }, { 0x78 }, { 0x64 }, { 0x72 }, { 0x70 }, { 0x63 }, // 0x9F + { 0x71 }, { 0x61 }, { 0x7A }, { 0x77 }, { 0x73 }, { 0x75 }, { 0x69 }, { 0x20 }, { 0x4F }, { 0x6B }, { 0x66 }, { 0x76 }, { 0x20 }, { 0x75 }, { 0x42 }, { 0x6A }, // 0XAF + { 0x6E }, { 0x20 }, { 0x55 }, { 0x6D }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x6F }, { 0x6C }, { 0x41 }, { 0x6F }, { 0x61 }, { 0x20 }, { 0x79 }, { 0x20 }, { 0x20 }, // 0xBF + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0XCF + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0XDF + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0XEF + { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 } // 0XFF +}; +static t_dispCodeMap dispCodeMap[] = { + { 0xCC }, // NUL '\0' (null character) + { 0xE0 }, // SOH (start of heading) + { 0xF2 }, // STX (start of text) + { 0xF3 }, // ETX (end of text) + { 0xCE }, // EOT (end of transmission) + { 0xCF }, // ENQ (enquiry) + { 0xF6 }, // ACK (acknowledge) + { 0xF7 }, // BEL '\a' (bell) + { 0xF8 }, // BS '\b' (backspace) + { 0xF9 }, // HT '\t' (horizontal tab) + { 0xFA }, // LF '\n' (new line) + { 0xFB }, // VT '\v' (vertical tab) + { 0xFC }, // FF '\f' (form feed) + { 0xFD }, // CR '\r' (carriage ret) + { 0xFE }, // SO (shift out) + { 0xFF }, // SI (shift in) + { 0xE1 }, // DLE (data link escape) + { 0xC1 }, // DC1 (device control 1) + { 0xC2 }, // DC2 (device control 2) + { 0xC3 }, // DC3 (device control 3) + { 0xC4 }, // DC4 (device control 4) + { 0xC5 }, // NAK (negative ack.) + { 0xC6 }, // SYN (synchronous idle) + { 0xE2 }, // ETB (end of trans. blk) + { 0xE3 }, // CAN (cancel) + { 0xE4 }, // EM (end of medium) + { 0xE5 }, // SUB (substitute) + { 0xE6 }, // ESC (escape) + { 0xEB }, // FS (file separator) + { 0xEE }, // GS (group separator) + { 0xEF }, // RS (record separator) + { 0xF4 }, // US (unit separator) + { 0x00 }, // SPACE + { 0x61 }, // ! + { 0x62 }, // " + { 0x63 }, // # + { 0x64 }, // $ + { 0x65 }, // % + { 0x66 }, // & + { 0x67 }, // ' + { 0x68 }, // ( + { 0x69 }, // ) + { 0x6B }, // * + { 0x6A }, // + + { 0x2F }, // , + { 0x2A }, // - + { 0x2E }, // . + { 0x2D }, // / + { 0x20 }, // 0 + { 0x21 }, // 1 + { 0x22 }, // 2 + { 0x23 }, // 3 + { 0x24 }, // 4 + { 0x25 }, // 5 + { 0x26 }, // 6 + { 0x27 }, // 7 + { 0x28 }, // 8 + { 0x29 }, // 9 + { 0x4F }, // : + { 0x2C }, // ; + { 0x51 }, // < + { 0x2B }, // = + { 0x57 }, // > + { 0x49 }, // ? + { 0x55 }, // @ + { 0x01 }, // A + { 0x02 }, // B + { 0x03 }, // C + { 0x04 }, // D + { 0x05 }, // E + { 0x06 }, // F + { 0x07 }, // G + { 0x08 }, // H + { 0x09 }, // I + { 0x0A }, // J + { 0x0B }, // K + { 0x0C }, // L + { 0x0D }, // M + { 0x0E }, // N + { 0x0F }, // O + { 0x10 }, // P + { 0x11 }, // Q + { 0x12 }, // R + { 0x13 }, // S + { 0x14 }, // T + { 0x15 }, // U + { 0x16 }, // V + { 0x17 }, // W + { 0x18 }, // X + { 0x19 }, // Y + { 0x1A }, // Z + { 0x52 }, // [ + { 0x59 }, // \ '\\' + { 0x54 }, // ] + { 0xBE }, // ^ + { 0x3C }, // _ + { 0xC7 }, // ` + { 0x81 }, // a + { 0x82 }, // b + { 0x83 }, // c + { 0x84 }, // d + { 0x85 }, // e + { 0x86 }, // f + { 0x87 }, // g + { 0x88 }, // h + { 0x89 }, // i + { 0x8A }, // j + { 0x8B }, // k + { 0x8C }, // l + { 0x8D }, // m + { 0x8E }, // n + { 0x8F }, // o + { 0x90 }, // p + { 0x91 }, // q + { 0x92 }, // r + { 0x93 }, // s + { 0x94 }, // t + { 0x95 }, // u + { 0x96 }, // v + { 0x97 }, // w + { 0x98 }, // x + { 0x99 }, // y + { 0x9A }, // z + { 0xBC }, // { + { 0x80 }, // | + { 0x40 }, // } + { 0xA5 }, // ~ + { 0xC0 } // DEL +}; + + +// Method to obtain and return the output screen width. +// +uint8_t getScreenWidth(void) +{ + return(MAX_SCREEN_WIDTH); +} + +struct termios orig_termios; + +void reset_terminal_mode() +{ + tcsetattr(0, TCSANOW, &orig_termios); +} + +void set_conio_terminal_mode() +{ + struct termios new_termios; + + /* take two copies - one for now, one for later */ + tcgetattr(0, &orig_termios); + memcpy(&new_termios, &orig_termios, sizeof(new_termios)); + + /* register cleanup handler, and set the new terminal mode */ + atexit(reset_terminal_mode); + cfmakeraw(&new_termios); + tcsetattr(0, TCSANOW, &new_termios); +} + +int kbhit() +{ + struct timeval tv = { 0L, 0L }; + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + return select(1, &fds, NULL, NULL, &tv) > 0; +} + +int getch(uint8_t wait) +{ + int r; + unsigned char c; + + if(wait != 0 || (wait == 0 && kbhit())) + { + if ((r = read(0, &c, sizeof(c))) < 0) { + return r; + } else { + return c; + } + } + return 0; +} + +void delay(int number_of_seconds) +{ + // Converting time into milli_seconds + int milli_seconds = 1000 * number_of_seconds; + + // Storing start time + clock_t start_time = clock(); + + // looping till required time is not achieved + while (clock() < start_time + milli_seconds); +} + +// Function to dump out a given section of memory via the UART. +// +int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryType, uint32_t memwidth, uint32_t dispaddr, uint8_t dispwidth) +{ + uint8_t displayWidth = dispwidth;; + uint32_t pnt = memaddr; + uint32_t endAddr = memaddr + memsize; + uint32_t addr = dispaddr; + uint32_t i = 0; + //uint32_t data; + int8_t keyIn; + int result = -1; + char c = 0; + + // Sanity check. memoryType == 0 required kernel driver to dump so we exit as it cannot be performed here. + if(memoryType == 0) + return(-1); + + // Reconfigure terminal to allow non-blocking key input. + // + set_conio_terminal_mode(); + + // If not set, calculate output line width according to connected display width. + // + if(displayWidth == 0) + { + switch(getScreenWidth()) + { + case 40: + displayWidth = 8; + break; + case 80: + displayWidth = 16; + break; + default: + displayWidth = 32; + break; + } + } + + // Loop, displaying memory contents until we get to the last byte then break out. + // + while (1) + { + printf("%08lX", addr); // print address + printf(": "); + + // print hexadecimal data + for (i=0; i < displayWidth; ) + { + switch(memwidth) + { + case 16: + if(pnt+i < endAddr) + printf("%04X", memoryType == 1 ? (uint16_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint16_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint16_t)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : memoryType == 4 ? (uint16_t)Z80Ctrl->iopage[pnt+i] : *(uint16_t *)(pnt+i)); + else + printf(" "); + i++; + break; + + case 32: + if(pnt+i < endAddr) + printf("%08lX", memoryType == 1 ? (uint32_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint32_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint32_t)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : memoryType == 4 ? (uint32_t)Z80Ctrl->iopage[pnt+i] : *(uint32_t *)(pnt+i)); + else + printf(" "); + i++; + break; + + case 8: + default: + if(pnt+i < endAddr) + printf("%02X", memoryType == 1 ? (uint8_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint8_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint8_t)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : memoryType == 4 ? (uint8_t)Z80Ctrl->iopage[pnt+i] : *(uint8_t *)(pnt+i)); + else + printf(" "); + i++; + break; + } + fputc((char)' ', stdout); + } + + // print ascii data + printf(" |"); + + // print single ascii char + for (i=0; i < displayWidth; i++) + { + c = memoryType == 1 ? (char)Z80RAM[pnt+i] : memoryType == 2 ? (char)Z80ROM[pnt+i] : memoryType == 3 ? (char)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : memoryType == 4 ? (char)Z80Ctrl->iopage[pnt+i] : (char)*(uint8_t *)(pnt+i); + if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~')) + fputc((char)c, stdout); + else + fputc((char)' ', stdout); + } + + printf("|\r\n"); + fflush(stdout); + + // Move on one row. + pnt += displayWidth; + addr += displayWidth; + + // User abort (ESC), pause (Space) or all done? + // + keyIn = getch(0); + if(keyIn == ' ') + { + do { + keyIn = getch(0); + } while(keyIn != ' ' && keyIn != 0x1b); + } + // Escape key pressed, exit with 0 to indicate this to caller. + if (keyIn == 0x1b) + { + sleep(1); + result = 0; + goto memoryDumpExit; + } + + // End of buffer, exit the loop. + if(pnt >= (memaddr + memsize)) + { + break; + } + } + + // Normal exit, return -1 to show no key pressed. +memoryDumpExit: + reset_terminal_mode(); + return(result); +} + +// Method to reset the Z80 CPU. +// +void reqResetZ80(uint8_t memoryMode) +{ + // Locals. + // + struct ioctlCmd ioctlCmd; + + // Send command to driver to reset Z80. + ioctlCmd.cmd = IOCTL_CMD_Z80_RESET; + ioctl(z80Control.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); +} + +// Method to start the Z80 CPU. +// +void startZ80(uint8_t memoryMode) +{ + // Locals. + // + struct ioctlCmd ioctlCmd; + + // Send command to driver to reset Z80. + ioctlCmd.cmd = IOCTL_CMD_Z80_START; + ioctl(z80Control.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); +} + +// Method to stop the Z80 CPU. +// +void stopZ80(uint8_t memoryMode) +{ + // Locals. + // + struct ioctlCmd ioctlCmd; + + // Send command to driver to reset Z80. + ioctlCmd.cmd = IOCTL_CMD_Z80_STOP; + ioctl(z80Control.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); +} + +// Method to read a memory mapped byte from the Z80 bus. +// +uint8_t readZ80Memory(uint32_t addr) +{ + // Locals. + uint8_t data; + + // Fetch the data. + // + data = Z80RAM[addr]; + + // Finally pass back the byte read to the caller. + return(data); +} + +// Method to write a memory mapped byte onto the Z80 bus. +// +uint8_t writeZ80Memory(uint32_t addr, uint8_t data, enum TARGETS target) +{ + // Locals. + + Z80RAM[addr] = data; + return(0); +} + +// Method to set the service status flag on the Z80 (and duplicated in the internal +// copy). +// +uint8_t setZ80SvcStatus(uint8_t status) +{ + // Locals + uint8_t result = 1; + + // Update the memory location. + // + result=writeZ80Memory(z80Control.svcControlAddr+TZSVC_RESULT_OFFSET, status, TRANZPUTER); + + return(result); +} + +// Method to fill memory under the Z80 control, either the mainboard or tranZPUter memory. +// +void fillZ80Memory(uint32_t addr, uint32_t size, uint8_t data, enum TARGETS target) +{ + // Locals. + + // Sanity checks. + // + if((target == MAINBOARD && (addr+size) > 0x10000) || (target == TRANZPUTER && (addr+size) > TZ_MAX_Z80_MEM)) + return; + + // Fill the memory by simply writing byte at a time. + // + for(uint32_t idx=addr; idx < (addr+size); idx++) + { + writeZ80Memory((uint32_t)idx, data, target); + } + + return; +} + +// Method to load a file from the SD card directly into the tranZPUter static RAM or mainboard RAM. +// +FRESULT loadZ80Memory(const char *src, uint32_t fileOffset, uint32_t addr, uint32_t size, uint32_t *bytesRead, enum TARGETS target) +{ + // Locals. + // + FILE *File; + uint32_t loadSize = 0L; + uint32_t sizeToRead = 0L; + uint32_t memPtr = addr; + unsigned int readSize; + unsigned char buf[TZSVC_SECTOR_SIZE]; + FRESULT fr0; + + // Sanity check on filenames. + if(src == NULL) + return(FR_INVALID_PARAMETER); + + // Try and open the source file. + fr0 = (File = fopen(src, "r")) != NULL ? FR_OK : FR_NO_FILE; + + // If no size given get the file size. + // + if(size == 0) + { + if(!fr0) + fr0 = (fseek(File, 0, SEEK_END) != -1) ? FR_OK : FR_DISK_ERR; + if(!fr0) + { + size = ftell(File); + fr0 = (fseek(File, 0, SEEK_SET) != -1) ? FR_OK : FR_DISK_ERR; + } + } + + // Seek to the correct location. + // + if(!fr0) + fr0 = (fseek(File, fileOffset, SEEK_SET) != -1) ? FR_OK : FR_DISK_ERR; + + #if(DEBUG_ENABLED & 0x02) + if(Z80Ctrl->debug & 0x02) printf("Loading file(%s,%08lx,%08lx)\n", src, addr, size); + #endif + + // If no errors in opening the file, proceed with reading and loading into memory. + if(!fr0) + { + // Loop, reading a sector at a time from SD file and writing it directly into the Z80 tranZPUter RAM or mainboard RAM. + // + loadSize = 0; + memPtr = addr; + do { + sizeToRead = (size-loadSize) > TZSVC_SECTOR_SIZE ? TZSVC_SECTOR_SIZE : size - loadSize; + readSize = fread(buf, 1, sizeToRead, File); + if (fr0 || readSize == 0) break; /* error or eof */ + + // Go through each byte in sector and write to the Z80 memory. + for(unsigned int idx=0; idx < readSize; idx++) + { + // And now write the byte and to the next address! + writeZ80Memory((uint32_t)memPtr, buf[idx], target); + memPtr++; + } + loadSize += readSize; + } while(loadSize < size); + + // Close to sync files. + fclose(File); + } else + { + printf("File not found:%s\n", src); + } + + // Return number of bytes read if caller provided a variable. + // + if(bytesRead != NULL) + { + *bytesRead = loadSize; + } + + return(fr0 ? fr0 : FR_OK); +} + + +// Method to load an MZF format file from the SD card directly into the tranZPUter static RAM or mainboard RAM. +// If the load address is specified then it overrides the MZF header value, otherwise load addr is taken from the header. +// +FRESULT loadMZFZ80Memory(const char *src, uint32_t addr, uint32_t *bytesRead, uint8_t hdrOnly, enum TARGETS target) +{ + // Locals. + FILE *File; + unsigned int readSize; + uint32_t addrOffset = SRAM_BANK0_ADDR; + uint32_t cmtHdrAddr = MZ_CMT_ADDR; + t_svcDirEnt mzfHeader; + FRESULT fr0; + + // Sanity check on filenames. + if(src == NULL) + return(FR_INVALID_PARAMETER); + + // Try and open the source file. + fr0 = (File = fopen(src, "r")) != NULL ? FR_OK : FR_NO_FILE; + + // If no error occurred, read in the header. + // + if(!fr0) + readSize = fread((char *)&mzfHeader, 1, MZF_HEADER_SIZE, File); + + // No errors, process. + if(!fr0 && readSize == MZF_HEADER_SIZE) + { + // Firstly, close the file, no longer needed. + fclose(File); + + // Setup bank in which to load the header/data. Default is bank 0, different host hardware uses different banks. + // + if(target == TRANZPUTER && z80Control.hostType == HW_MZ800) + { + addrOffset = SRAM_BANK6_ADDR; + } + else if(target == TRANZPUTER && z80Control.hostType == HW_MZ2000) + { + addrOffset = SRAM_BANK6_ADDR; + } + + // Save the header into the CMT area for reference, some applications expect it. If the load address is below 1200H this could be wiped out, the code below stores a copy + // in the service record sector on exit for this purpose. The caller needs to check the service record and if the Load Address is below >= 1200H use the CMT header else + // use the service sector. + // + // NB: This assumes TZFS is running and made this call. Ignore for MZ2000 when in IPL mode as the header isnt needed. + // + if(z80Control.hostType != HW_MZ2000 || (z80Control.hostType == HW_MZ2000 && z80Control.iplMode == 0)) + { + copyToZ80(addrOffset+cmtHdrAddr, (uint8_t *)&mzfHeader, MZF_HEADER_SIZE, target); + } + + if(hdrOnly == 0) + { + // Now obtain the parameters. + // + if(addr == 0xFFFFFFFF) + { + // If the address needs to be determined from the header yet the header is below the RAM, set the buffer to 0x1200 and let the caller sort it out. + if(mzfHeader.loadAddr > 0x1000) + addr = mzfHeader.loadAddr; + else + addr = 0x1200; + } + + // Look at the attribute byte, if it is >= 0xF8 then it is a special tranZPUter binary object requiring loading into a seperate memory bank. + // The attribute & 0x07 << 16 specifies the memory bank in which to load the image. + if(mzfHeader.attr >= 0xF8) + { + addr += ((mzfHeader.attr & 0x07) << 16); + } else + { + addr += addrOffset; + } + + // Ok, load up the file into Z80 memory. + fr0 = loadZ80Memory(src, MZF_HEADER_SIZE, addr, (mzfHeader.attr >= 0xF8 ? 0 : mzfHeader.fileSize), bytesRead, target); + + // If the load address was below 0x11D0, the lowest free point in the original memory map then the load is said to be in lower DRAM. In this case the CMT header wont be available + // so load the header into the service sector as well so the caller can determine the load state. + memcpy((uint8_t *)&svcControl.sector, (uint8_t *)&mzfHeader, MZF_HEADER_SIZE); + } + } + return(fr0 ? fr0 : FR_OK); +} + + +// Method to read a section of the tranZPUter/mainboard memory and store it in an SD file. +// +FRESULT saveZ80Memory(const char *dst, uint32_t addr, uint32_t size, t_svcDirEnt *mzfHeader, enum TARGETS target) +{ + // Locals. + // + FILE *File; + uint32_t saveSize = 0L; + uint32_t sizeToWrite; + uint32_t memPtr = addr; + unsigned int writeSize = 0; + unsigned char buf[TZSVC_SECTOR_SIZE]; + FRESULT fr0; + + // Sanity check on filenames. + if(dst == NULL || size == 0) + return(FR_INVALID_PARAMETER); + + // Try and create the destination file. + fr0 = (File = fopen(dst, "w+")) != NULL ? FR_OK : FR_NO_FILE; + + // If no errors in opening the file, proceed with reading and loading into memory. + if(!fr0) + { + // If an MZF header has been passed, we are saving an MZF file, write out the MZF header first prior to the data. + // + if(mzfHeader) + { + fr0 = (writeSize = fwrite((char *)mzfHeader, 1, MZF_HEADER_SIZE, File)) == MZF_HEADER_SIZE ? FR_OK : FR_DISK_ERR; + } + + if(!fr0) + { + // Loop, reading a sector worth of data (or upto limit remaining) from the Z80 tranZPUter RAM or mainboard RAM and writing it into the open SD card file. + // + saveSize = 0; + for (;;) { + + // Work out how many bytes to write in the sector then fetch from the Z80. + sizeToWrite = (size-saveSize) > TZSVC_SECTOR_SIZE ? TZSVC_SECTOR_SIZE : size - saveSize; + for(unsigned int idx=0; idx < sizeToWrite; idx++) + { + // And now read the byte and to the next address! + buf[idx] = readZ80Memory((uint32_t)memPtr); + memPtr++; + } + + fr0 = (writeSize = fwrite(buf, 1, sizeToWrite, File)) == sizeToWrite ? FR_OK : FR_DISK_ERR; + saveSize += writeSize; + + if (fr0 || writeSize < sizeToWrite || saveSize >= size) break; // error, disk full or range written. + } + printf("Saved %ld bytes, final address:%lx\n", saveSize, memPtr); + } else + { + printf("Failed to write the MZF header.\n"); + } + + // Close to sync files. + fclose(File); + + } else + { + printf("Cannot create file:%s\n", dst); + } + + return(fr0 ? fr0 : FR_OK); +} + + +/////////////////////////////////////////////////////// +// Getter/Setter methods to keep z80Control private. // +/////////////////////////////////////////////////////// + +// Method to convert a Sharp filename into an Ascii filename. +// +void convertSharpFilenameToAscii(char *dst, char *src, uint8_t size) +{ + // Loop through and convert each character via the mapping table. + for(uint8_t idx=0; idx < size; idx++) + { + *dst = (char)asciiMap[(int)*src].asciiCode; + src++; + dst++; + } + // As the Sharp Filename does not always contain a terminator, set a NULL at the end of the string (size+1) so that it can be processed + // by standard routines. This means dst must always be size+1 in length. + // + *dst = 0x00; + return; +} + +// Helper method to convert a Sharp ASCII filename to one acceptable for use in the FAT32 filesystem. +// +void convertToFAT32FileNameFormat(char *dst) +{ + // Go through the given filename and change any characters which FAT32 doesnt support. This is necessary as the Sharp filenaming convention allows + // for almost any character! + // + for(int idx=0; idx < strlen(dst); idx++) + { + if(dst[idx] == '/') + { + dst[idx] = '-'; + } + } + return; +} + +// Method to change the secondary CPU frequency and optionally enable/disable it. +// On the FusionX there is no secondary frequency but as the emulation is being +// governed (delayed), we can change the delay to increase processing speed. +// +// Input: frequency = desired frequency in Hertz. +// action = 0 - take no action, just change frequency, 1 - set and enable the secondary CPU frequency, 2 - set and disable the secondary CPU frequency, +// 3 - enable the secondary CPU frequency, 4 - disable the secondary CPU frequency +// Output: actual set frequency in Hertz. +// +uint32_t setZ80CPUFrequency(float frequency, uint8_t action) +{ + // Locals. + // + uint32_t freqMultiplier = 0; + struct ioctlCmd ioctlCmd; + + // Setup the alternative clock frequency based on a multiplier of the host base frequency. + // + if(action == 0 || action == 1 || action == 2) + { + z80Control.freqMultiplier = (uint32_t)(frequency / CPU_FREQUENCY_NORMAL); + if(z80Control.freqMultiplier <= 1) z80Control.freqMultiplier = 1; + } + + // Switch to new frequency. + if(action == 1 || action == 3) + { + ioctlCmd.speed.speedMultiplier = z80Control.freqMultiplier; + } + // Switch to original frequency. + if(action == 2 || action == 4) + { + ioctlCmd.speed.speedMultiplier = 1; + } + + // Send the change frequency to the driver, it will adjust the governor. + ioctlCmd.cmd = IOCTL_CMD_Z80_CPU_FREQ; + + // Send IOCTL command to change to the new multiplier. + ioctl(z80Control.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); + + // Return the actual frequency set, this will be the nearest frequency the timers are capable of resolving. + return(z80Control.freqMultiplier * CPU_FREQUENCY_NORMAL); +} + +// Simple method to set defaults in the service structure if not already set by the Z80. +// +void svcSetDefaults(enum FILE_TYPE type) +{ + // Set according to the type of file were working with. + // + switch(type) + { + case CAS: + // If there is no directory path, use the inbuilt default. + if(svcControl.directory[0] == '\0') + { + strcpy((char *)svcControl.directory, TZSVC_DEFAULT_CAS_DIR); + } + // If there is no wildcard matching, use default. + if(svcControl.wildcard[0] == '\0') + { + strcpy((char *)svcControl.wildcard, TZSVC_DEFAULT_WILDCARD); + } + break; + + case BAS: + // If there is no directory path, use the inbuilt default. + if(svcControl.directory[0] == '\0') + { + strcpy((char *)svcControl.directory, TZSVC_DEFAULT_BAS_DIR); + } + // If there is no wildcard matching, use default. + if(svcControl.wildcard[0] == '\0') + { + strcpy((char *)svcControl.wildcard, TZSVC_DEFAULT_WILDCARD); + } + break; + + case MZF: + default: + // If there is no directory path, use the inbuilt default. + if(svcControl.directory[0] == '\0') + { + strcpy((char *)svcControl.directory, TZSVC_DEFAULT_MZF_DIR); + } + + // If there is no wildcard matching, use default. + if(svcControl.wildcard[0] == '\0') + { + strcpy((char *)svcControl.wildcard, TZSVC_DEFAULT_WILDCARD); + } + break; + } + return; +} + +// Helper method for matchFileWithWildcard. +// Get the next character from the filename or pattern and modify it if necessary to match the Sharp character set. +static uint32_t getNextChar(const char** ptr) +{ + uint8_t chr; + + // Get a byte + chr = (uint8_t)*(*ptr)++; + + // To upper ASCII char + if(islower(chr)) chr -= 0x20; + + return chr; +} + +// Match an MZF name with a given wildcard. +// This method originated from the private method in FatFS but adapted to work with MZF filename matching. +// Input: wildcard - Pattern to match +// fileName - MZF fileName, either CR or NUL terminated or TZSVC_FILENAME_SIZE chars long. +// skip - Number of characters to skip due to ?'s +// infinite - Infinite search as * specified. +// Output: 0 - No match +// 1 - Match +// +static int matchFileWithWildcard(const char *pattern, const char *fileName, int skip, int infinite) +{ + const char *pp, *np; + uint32_t pc, nc; + int nm, nx; + + // Pre-skip name chars + while (skip--) + { + // Branch mismatched if less name chars + if (!getNextChar(&fileName)) return 0; + } + // (short circuit) + if (*pattern == 0 && infinite) return 1; + + do { + // Top of pattern and name to match + pp = pattern; np = fileName; + for (;;) + { + // Wildcard? + if (*pp == '?' || *pp == '*') + { + nm = nx = 0; + + // Analyze the wildcard block + do { + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + + // Test new branch (recurs upto number of wildcard blocks in the pattern) + if (matchFileWithWildcard(pp, np, nm, nx)) return 1; + + // Branch mismatched + nc = *np; break; + } + + // End of filename, Sharp filenames can be terminated with 0x00, CR or size. If we get to the end of the name then it is + // a match. + // + if((np - fileName) == TZSVC_FILENAME_SIZE) return 1; + + // Get a pattern char + pc = getNextChar(&pp); + + // Get a name char + nc = getNextChar(&np); + + // Sharp uses null or CR to terminate a pattern and a filename, which is not determinate! + if((pc == 0x00 || pc == 0x0d) && (nc == 0x00 || nc == 0x0d)) return 1; + + // Branch mismatched? + if (pc != nc) break; + + // Branch matched? (matched at end of both strings) + if (pc == 0) return 1; + } + + // fileName++ + getNextChar(&fileName); + + /* Retry until end of name if infinite search is specified */ + } while (infinite && nc != 0x00 && nc != 0x0d && (np - fileName) < TZSVC_FILENAME_SIZE); + + return 0; +} + +// Method to open/read a directory listing. +// This method opens a given directory on the SD card (the z80 provides the directory name or it defaults to MZF). The directory +// is read and a unique incrementing number is given to each entry, this number can be used in a later request to open the file to save on +// name entry and matching. +// A basic pattern filter is applied to the results returned, ie A* will return only files starting with A. +// +// The parameters are passed in the svcControl block. +// +uint8_t svcReadDir(uint8_t mode, enum FILE_TYPE type) +{ + // Locals - dont use global as this is a seperate thread. + // + static DIR *dirFp; + static uint8_t dirOpen = 0; // Seperate flag as their is no public way to validate that dirFp is open and valid, the method in FatFS is private for this functionality. + static struct dirent *fno; + static uint8_t dirSector = 0; // Virtual directory sector. + FRESULT result = FR_OK; + unsigned int readSize; + char fqfn[256 + 13]; + FILE *File; + t_svcCmpDirBlock *dirBlock = (t_svcCmpDirBlock *)&svcControl.sector; + + // Request to open? Validate that we dont already have an open directory then open the requested one. + if(mode == TZSVC_OPEN) + { + // Close if previously open. + if(dirOpen == 1) + svcReadDir(TZSVC_CLOSE, type); + + // Setup the defaults + // + svcSetDefaults(type); + + // Open the directory. + sprintf(fqfn, "%s%s", OS_BASE_DIR, svcControl.directory); + result = (dirFp = opendir((char *)&fqfn)) != NULL ? FR_OK : FR_NO_PATH; + if(!result) + { + // Reentrant call to actually read the data. + // + dirOpen = 1; + dirSector = 0; + result = (FRESULT)svcReadDir(TZSVC_NEXT, type); + } + } + + // Read a block of directory entries into the z80 service buffer sector. + else if(mode == TZSVC_NEXT && dirOpen == 1) + { + // If the Z80 is requesting a non sequential directory sector then we have to start from the beginning as each sector is built up not read. + // + if(dirSector != svcControl.dirSector) + { + // If the current sector is after the requested sector, rewind by re-opening. + // + if(dirSector < svcControl.dirSector) + { + result=svcReadDir(TZSVC_OPEN, type); + } + if(!result) + { + // Now get the sector by advancement. + for(uint8_t idx=dirSector; idx < svcControl.dirSector && result == FR_OK; idx++) + { + result=svcReadDir(TZSVC_NEXT, type); + } + } + } + + // Proceed if no errors have occurred. + // + if(!result) + { + // Zero the directory entry block - unused entries will then appear as NULLS. + memset(dirBlock, 0x00, TZSVC_SECTOR_SIZE); + + // Loop the required number of times to fill a sector full of entries. + // + uint8_t idx=0; + while(idx < TZVC_MAX_CMPCT_DIRENT_BLOCK && result == FR_OK) + { + // Read an SD directory entry then open the returned SD file so that we can to read out the important MZF data which is stored in it as it will be passed to the Z80. + // + fno = readdir(dirFp); + + // If an error occurs or we are at the end of the directory listing close the sector and pass back. + if(fno == NULL || fno->d_name[0] == 0) break; + + // Check to see if this is a valid file for the given type. + const char *ext = strrchr(fno->d_name, '.'); + if(type == MZF && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_MZF_EXT) != 0)) + continue; + if(type == BAS && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_BAS_EXT) != 0)) + continue; + if(type == CAS && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_CAS_EXT) != 0)) + continue; + if(type == ALL && !ext) + continue; + + // Sharp files need special handling, the file needs te opened and the Sharp filename read out, this is then returned as the filename. + // + if(type == MZF) + { + // Build filename. + // + sprintf(fqfn, "%s%s/%s", OS_BASE_DIR, svcControl.directory, fno->d_name); + + // Open the file so we can read out the MZF header which is the information TZFS/CPM needs. + // + result = (File = fopen(fqfn, "r")) != NULL ? FR_OK : FR_NO_FILE; + + // If no error occurred, read in the header. + // + if(File != NULL) readSize = fread((char *)&dirBlock->dirEnt[idx], 1, TZSVC_CMPHDR_SIZE, File); + + // No errors, read the header. + if(File != NULL && readSize == TZSVC_CMPHDR_SIZE) + { + // Close the file, no longer needed. + fclose(File); + + // Check to see if the file matches any given wildcard. + // + if(matchFileWithWildcard((char *)&svcControl.wildcard, (char *)&dirBlock->dirEnt[idx].fileName, 0, 0)) + { + // Valid so find next entry. + idx++; + } else + { + // Scrub the entry, not valid. + memset((char *)&dirBlock->dirEnt[idx], 0x00, TZSVC_CMPHDR_SIZE); + } + } + } else + { + // Check to see if the file matches any given wildcard. + // + if(matchFileWithWildcard((char *)&svcControl.wildcard, fno->d_name, 0, 0)) + { + // If the type is ALL FORMATTED then output a truncated directory entry formatted for display. + // + if(type == ALLFMT) + { + // Get the address of the file extension. + char *ext = strrchr(fno->d_name, '.'); + + // Although not as efficient, maintain the use of the Sharp MZF header for normal directory filenames just use the extra space for increased filename size. + if(ext) + { + // Although not as efficient, maintain the use of the Sharp MZF header for normal directory filenames just use the extra space for increased filename size. + if((ext - fno->d_name) > TZSVC_LONG_FMT_FNAME_SIZE-5) + { + fno->d_name[TZSVC_LONG_FMT_FNAME_SIZE-6] = '*'; // Place a '*' to show the filename was truncated. + fno->d_name[TZSVC_LONG_FMT_FNAME_SIZE-5] = 0x00; + } + *ext = 0x00; + ext++; + + sprintf((char *)&dirBlock->dirEnt[idx].fileName, "%-*s.%3s", TZSVC_LONG_FMT_FNAME_SIZE-5, fno->d_name, ext); + } else + { + fno->d_name[TZSVC_LONG_FMT_FNAME_SIZE] = 0x00; + strncpy((char *)&dirBlock->dirEnt[idx].fileName, fno->d_name, TZSVC_LONG_FMT_FNAME_SIZE); + } + } else + // All other types just output the filename upto the limit truncating as necessary. + { + fno->d_name[TZSVC_LONG_FNAME_SIZE] = 0x00; + strncpy((char *)&dirBlock->dirEnt[idx].fileName, fno->d_name, TZSVC_LONG_FNAME_SIZE); + } + + // Set the attribute in the directory record to indicate this is a valid record. + dirBlock->dirEnt[idx].attr = 0xff; + + // Valid so find next entry. + idx++; + } else + { + // Scrub the entry, not valid. + memset((char *)&dirBlock->dirEnt[idx], 0x00, TZSVC_CMPHDR_SIZE); + } + } + } + } + + // Increment the virtual directory sector number as the Z80 expects a sector of directory entries per call. + if(!result) + dirSector++; + } + // Close the currently open directory. + else if(mode == TZSVC_CLOSE) + { + if(dirOpen) + closedir(dirFp); + dirOpen = 0; + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + + +// A method to find a file either using a Sharp MZ80A name, a standard filename or a number assigned to a directory listing. +// For the Sharp MZ80A it is a bit long winded as each file that matches the filename specification has to be opened and the MZF header filename +// has to be checked. For standard files it is just a matter of matching the name. For both types a short cut is a number which is a files position +// in a directory obtained from a previous directory listing. +// +uint8_t svcFindFile(char *file, char *searchFile, uint8_t searchNo, enum FILE_TYPE type) +{ + // Locals + uint8_t fileNo = 0; + uint8_t found = 0; + unsigned int readSize; + char fqfn[256 + 13]; + FILE *File; + struct dirent *fno; + DIR *dirFp; + FRESULT result = FR_OK; + t_svcCmpDirEnt dirEnt; + + // Setup the defaults + // + svcSetDefaults(type); + + // Open the directory. + sprintf(fqfn, "%s%s", OS_BASE_DIR, svcControl.directory); + result = (dirFp = opendir((char *)&fqfn)) != NULL ? FR_OK : FR_NO_PATH; + if(result == FR_OK) + { + fileNo = 0; + + do { + // Read an SD directory entry then open the returned SD file so that we can to read out the important MZF data for name matching. + // + result = (fno = readdir(dirFp)) != NULL ? FR_OK : FR_NO_FILE; + + // If an error occurs or we are at the end of the directory listing close the sector and pass back. + if(result != FR_OK || fno->d_name[0] == 0) break; + + // Check to see if this is a valid file for the given type. + const char *ext = strrchr(fno->d_name, '.'); + if(type == MZF && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_MZF_EXT) != 0)) + continue; + if(type == BAS && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_BAS_EXT) != 0)) + continue; + if(type == CAS && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_CAS_EXT) != 0)) + continue; + if(type == ALL && !ext) + continue; + + // Sharp files need special handling, the file needs te opened and the Sharp filename read out, this is then used as the filename for matching. + // + if(type == MZF) + { + // Build filename. + // + sprintf(fqfn, "%s%s/%s", OS_BASE_DIR, svcControl.directory, fno->d_name); + + // Open the file so we can read out the MZF header which is the information TZFS/CPM needs. + // + result = (File = fopen(fqfn, "r")) != NULL ? FR_OK : FR_NO_FILE; + + // If no error occurred, read in the header. + // + if(!result) readSize = fread((char *)&dirEnt, 1, TZSVC_CMPHDR_SIZE, File); + + // No errors, read the header. + if(!result && readSize == TZSVC_CMPHDR_SIZE) + { + // Close the file, no longer needed. + fclose(File); + + // Check to see if the file matches any given wildcard. If we dont have a match loop to next directory entry. + // + if(matchFileWithWildcard((char *)&svcControl.wildcard, (char *)&dirEnt.fileName, 0, 0)) + { + // If a filename has been given, see if this file matches it. + if(searchFile != NULL) + { + // Check to see if the file matches the name given with wildcard expansion if needed. + // + if(matchFileWithWildcard(searchFile, (char *)&dirEnt.fileName, 0, 0)) + { + found = 2; + } + } + + // If we are searching on file number and the latest directory entry retrieval matches, exit and return the filename. + if(searchNo != 0xFF && fileNo == (uint8_t)searchNo) + { + found = 1; + } else + { + fileNo++; + } + } + } + } else + { + // Check to see if the file matches any given wildcard. If we dont have a match loop to next directory entry. + // + if(matchFileWithWildcard((char *)&svcControl.wildcard, (char *)&fno->d_name, 0, 0)) + { + // If a filename has been given, see if this file matches it. + if(searchFile != NULL) + { + // Check to see if the file matches the name given with wildcard expansion if needed. + // + if(matchFileWithWildcard(searchFile, (char *)&fno->d_name, 0, 0)) + { + found = 2; + } + } + + // If we are searching on file number and the latest directory entry retrieval matches, exit and return the filename. + if(searchNo != 0xFF && fileNo == (uint8_t)searchNo) + { + found = 1; + } else + { + fileNo++; + } + } + } + } while(!result && !found); + + // If the file was found, copy the FQFN into its buffer. + // + if(found) + { + strcpy(file, fqfn); + } + } + + // Return 1 if file was found, 0 for all other cases. + // + return(result == FR_OK ? (found == 0 ? 0 : 1) : 0); +} + +// Method to read the current directory from the cache. If the cache is invalid resort to using the standard direct method. +// +uint8_t svcReadDirCache(uint8_t mode, enum FILE_TYPE type) +{ + // Locals - dont use global as this is a seperate thread. + // + static uint8_t dirOpen = 0; // Seperate flag as their is no public way to validate that dirFp is open and valid, the method in FatFS is private for this functionality. + static uint8_t dirSector = 0; // Virtual directory sector. + static uint8_t dirEntry = 0; // Last cache entry number processed. + FRESULT result = FR_OK; + t_svcCmpDirBlock *dirBlock = (t_svcCmpDirBlock *)&svcControl.sector; + + // Setup the defaults + // + svcSetDefaults(type); + + // Need to refresh cache directory? + if(!osControl.dirMap.valid || strcasecmp((const char *)svcControl.directory, osControl.dirMap.directory) != 0 || osControl.dirMap.type != type) + result=svcCacheDir((const char *)svcControl.directory, svcControl.fileType, 0); + + // If there is no cache revert to direct directory read. + // + if(!osControl.dirMap.valid || result) + { + result = svcReadDir(mode, type); + } else + { + // Request to open? No need with cache, just return next block. + if(mode == TZSVC_OPEN) + { + // Reentrant call to actually read the data. + // + dirOpen = 1; + dirSector = 0; + dirEntry = 0; + result = (FRESULT)svcReadDirCache(TZSVC_NEXT, type); + } + + // Read a block of directory entries into the z80 service buffer sector. + else if(mode == TZSVC_NEXT && dirOpen == 1) + { + // If the Z80 is requesting a non sequential directory sector then calculate the new cache entry position. + // + if(dirSector != svcControl.dirSector) + { + dirEntry = svcControl.dirSector * TZVC_MAX_CMPCT_DIRENT_BLOCK; + dirSector = svcControl.dirSector; + if(dirEntry > osControl.dirMap.entries) + { + dirEntry = osControl.dirMap.entries; + dirSector = osControl.dirMap.entries / TZVC_MAX_CMPCT_DIRENT_BLOCK; + } + } + + // Zero the directory entry block - unused entries will then appear as NULLS. + memset(dirBlock, 0x00, TZSVC_SECTOR_SIZE); + + // Loop the required number of times to fill a sector full of entries. + // + uint8_t idx=0; + while(idx < TZVC_MAX_CMPCT_DIRENT_BLOCK && dirEntry < osControl.dirMap.entries && result == FR_OK) + { + // Check to see if the file matches any given wildcard. + // + if(matchFileWithWildcard((char *)&svcControl.wildcard, type == MZF ? (char *)&osControl.dirMap.mzfFile[dirEntry]->mzfHeader.fileName : (char *)osControl.dirMap.sdFileName[dirEntry], 0, 0)) + { + // Sharp files we copy the whole header into the entry. + // + if(type == MZF) + { + // Valid so store and find next entry. + // + memcpy((char *)&dirBlock->dirEnt[idx], (char *)&osControl.dirMap.mzfFile[dirEntry]->mzfHeader, TZSVC_CMPHDR_SIZE); + } else + { + // If the type is ALL FORMATTED then output a truncated directory entry formatted for display. + // + if(type == ALLFMT) + { + // Duplicate the cache entry, formatting is destructuve and less time neeed to duplicate than repair. + char *fname = strdup((char *)osControl.dirMap.sdFileName[dirEntry]); + if(fname != NULL) + { + // Get the address of the file extension. + char *ext = strrchr(fname, '.'); + + // Although not as efficient, maintain the use of the Sharp MZF header for normal directory filenames just use the extra space for increased filename size. + if(ext) + { + // Although not as efficient, maintain the use of the Sharp MZF header for normal directory filenames just use the extra space for increased filename size. + if((ext - fname) > TZSVC_LONG_FMT_FNAME_SIZE-5) + { + fname[TZSVC_LONG_FMT_FNAME_SIZE-6] = '*'; // Place a '*' to show the filename was truncated. + fname[TZSVC_LONG_FMT_FNAME_SIZE-5] = 0x00; + } + *ext = 0x00; + ext++; + sprintf((char *)&dirBlock->dirEnt[idx].fileName, "%-*s.%3s", TZSVC_LONG_FMT_FNAME_SIZE-5, fname, ext); + } else + { + fname[TZSVC_LONG_FMT_FNAME_SIZE] = 0x00; + strncpy((char *)&dirBlock->dirEnt[idx].fileName, fname, TZSVC_LONG_FMT_FNAME_SIZE); + } + + // Release the duplicate memory. + free(fname); + } else + { + printf("Out of memory duplicating directory filename.\n"); + } + } else + // All other types just output the filename upto the limit truncating as necessary. + { + osControl.dirMap.sdFileName[dirEntry][TZSVC_LONG_FNAME_SIZE] = 0x00; + strncpy((char *)&dirBlock->dirEnt[idx].fileName, (char *)osControl.dirMap.sdFileName[dirEntry], TZSVC_LONG_FNAME_SIZE); + } + + // Set the attribute in the directory record to indicate this is a valid record. + dirBlock->dirEnt[idx].attr = 0xff; + } + idx++; + } + dirEntry++; + } + + // Increment the virtual directory sector number as the Z80 expects a sector of directory entries per call. + if(!result) + dirSector++; + } + // Close the currently open directory. + else if(mode == TZSVC_CLOSE) + { + dirOpen = 0; + } + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// A method to find a file using the cached directory. If the cache is not available (ie. no memory) use the standard method. +// +uint8_t svcFindFileCache(char *file, char *searchFile, uint8_t searchNo, enum FILE_TYPE type) +{ + // Locals + uint8_t fileNo = 0; + uint8_t found = 0; + uint8_t idx = 0; + FRESULT result = FR_OK; + + // If there is no cache revert to direct search. + // + if(!osControl.dirMap.valid) + { + result = (found = svcFindFile(file, searchFile, searchNo, type)) != 0 ? FR_OK : FR_NO_FILE; + } else + { + // If we are searching on file number and there is no filter in place, see if it is valid and exit with data. + if(searchNo != 0xFF && strcmp((char *)svcControl.wildcard, TZSVC_DEFAULT_WILDCARD) == 0) + { + if(searchNo < osControl.dirMap.entries && osControl.dirMap.mzfFile[searchNo]) // <- this is the same for standard files as mzfFile is a union for sdFileName + { + found = 1; + idx = searchNo; + } else + { + result = FR_NO_FILE; + } + } else + { + do { + // Check to see if the file matches any given wildcard. If we dont have a match loop to next directory entry. + // + if(matchFileWithWildcard((char *)&svcControl.wildcard, type == MZF ? (char *)&osControl.dirMap.mzfFile[idx]->mzfHeader.fileName : (char *)&osControl.dirMap.sdFileName[idx], 0, 0)) + { + // If a filename has been given, see if this file matches it. + if(searchFile != NULL) + { + // Check to see if the file matches the name given with wildcard expansion if needed. + // + if(matchFileWithWildcard(searchFile, type == MZF ? (char *)&osControl.dirMap.mzfFile[idx]->mzfHeader.fileName : (char *)&osControl.dirMap.sdFileName[idx], 0, 0)) + { + found = 2; + } + } + + // If we are searching on file number then see if it matches (after filter has been applied above). + if(searchNo != 0xFF && fileNo == (uint8_t)searchNo) + { + found = 1; + } else + { + fileNo++; + } + } + if(!found) + { + idx++; + } + } while(!result && !found && idx < osControl.dirMap.entries); + } + + // If the file was found, copy the FQFN into its buffer. + // + if(found) + { + // Build filename. + // + sprintf(file, "%s%s/%s", OS_BASE_DIR, osControl.dirMap.directory, type == MZF ? osControl.dirMap.mzfFile[idx]->sdFileName : osControl.dirMap.sdFileName[idx]); + } + } + + // Return 1 if file was found, 0 for all other cases. + // + return(result == FR_OK ? (found == 0 ? 0 : 1) : 0); +} + +// Method to build up a cache of all the files on the SD card in a given directory along with any mapping to Sharp MZ80A headers if required. +// For Sharp MZ80A files this involves scanning each file to extract the MZF header and creating a map. +// +uint8_t svcCacheDir(const char *directory, enum FILE_TYPE type, uint8_t force) +{ + // Locals + uint8_t fileNo = 0; + unsigned int readSize; + char fqfn[256 + 13]; + FILE *File; + struct dirent *fno; + DIR *dirFp; + FRESULT result = FR_OK; + t_svcCmpDirEnt dirEnt; + + // No need to cache directory if we have already cached it. + if(force == 0 && osControl.dirMap.valid && strcasecmp(directory, osControl.dirMap.directory) == 0 && osControl.dirMap.type == type) + return(1); + + // Invalidate the map and free existing memory incase of errors. + // + osControl.dirMap.valid = 0; + for(uint8_t idx=0; idx < osControl.dirMap.entries; idx++) + { + if(osControl.dirMap.type == MZF && osControl.dirMap.mzfFile[idx]) + { + free(osControl.dirMap.mzfFile[idx]->sdFileName); + free(osControl.dirMap.mzfFile[idx]); + osControl.dirMap.mzfFile[idx] = 0; + } else + { + free(osControl.dirMap.sdFileName[idx]); + osControl.dirMap.sdFileName[idx] = 0; + } + } + osControl.dirMap.entries = 0; + osControl.dirMap.type = MZF; + + // Open the directory and extract all files. + sprintf(fqfn, "%s%s", OS_BASE_DIR, directory); + result = (dirFp = opendir(fqfn)) != NULL ? FR_OK : FR_NO_PATH; + if(result == FR_OK) + { + fileNo = 0; + + do { + // Read an SD directory entry. If reading Sharp MZ80A files then open the returned SD file so that we can to read out the important MZF data for name matching. + // + fno = readdir(dirFp); + + // If an error occurs or we are at the end of the directory listing close the sector and pass back. + if(fno == NULL || fno->d_name[0] == 0) break; + + // Check to see if this is a valid file for the given type. + const char *ext = strrchr(fno->d_name, '.'); + if(type == MZF && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_MZF_EXT) != 0)) + continue; + if(type == BAS && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_BAS_EXT) != 0)) + continue; + if(type == CAS && (!ext || strcasecmp(++ext, TZSVC_DEFAULT_CAS_EXT) != 0)) + continue; + if(type == ALL && !ext) + continue; + // Sharp files need special handling, the file needs te opened and the Sharp filename read out, this is then used in the cache. + // + if(type == MZF) + { + // Build filename. + // + sprintf(fqfn, "%s%s/%s", OS_BASE_DIR, directory, fno->d_name); + + // Open the file so we can read out the MZF header which is the information TZFS/CPM needs. + // + result = (File = fopen(fqfn, "r")) != NULL ? FR_OK : FR_NO_FILE; + + // If no error occurred, read in the header. + // + if(!result) readSize = fread((char *)&dirEnt, 1, TZSVC_CMPHDR_SIZE, File); + + // No errors, read the header. + if(!result && readSize == TZSVC_CMPHDR_SIZE) + { + // Close the file, no longer needed. + fclose(File); + + // Cache this entry. The SD filename is dynamically allocated as it's size can be upto 255 characters for LFN names. The Sharp name is + // fixed at 17 characters as you cant reliably rely on terminators and the additional data makes it a constant 32 chars long. + osControl.dirMap.mzfFile[fileNo] = (t_sharpToSDMap *)malloc(sizeof(t_sharpToSDMap)); + if(osControl.dirMap.mzfFile[fileNo] != NULL) + osControl.dirMap.mzfFile[fileNo]->sdFileName = (uint8_t *)malloc(strlen(fno->d_name)+1); + + if(osControl.dirMap.mzfFile[fileNo] == NULL || osControl.dirMap.mzfFile[fileNo]->sdFileName == NULL) + { + printf("Out of memory cacheing directory:%s\n", directory); + for(uint8_t idx=0; idx <= fileNo; idx++) + { + if(osControl.dirMap.mzfFile[idx]) + { + free(osControl.dirMap.mzfFile[idx]->sdFileName); + free(osControl.dirMap.mzfFile[idx]); + osControl.dirMap.mzfFile[idx] = 0; + } + } + result = FR_NOT_ENOUGH_CORE; + } else + { + // Copy in details into this maps node. + strcpy((char *)osControl.dirMap.mzfFile[fileNo]->sdFileName, fno->d_name); + memcpy((char *)&osControl.dirMap.mzfFile[fileNo]->mzfHeader, (char *)&dirEnt, TZSVC_CMPHDR_SIZE); + fileNo++; + } + } + } else + { + // Cache this entry. The SD filename is dynamically allocated as it's size can be upto 255 characters for LFN names. The SD service header is based + // on Sharp names which are fixed at 17 characters which shouldnt be a problem, just truncate the name as there is no support for longer names yet! + osControl.dirMap.sdFileName[fileNo] = (uint8_t *)malloc(strlen(fno->d_name)+1); + + if(osControl.dirMap.sdFileName[fileNo] == NULL) + { + printf("Out of memory cacheing directory:%s\n", directory); + for(uint8_t idx=0; idx <= fileNo; idx++) + { + if(osControl.dirMap.sdFileName[idx]) + { + free(osControl.dirMap.sdFileName[idx]); + osControl.dirMap.sdFileName[idx] = 0; + } + } + result = FR_NOT_ENOUGH_CORE; + } else + { + // Copy in details into this maps node. + strcpy((char *)osControl.dirMap.sdFileName[fileNo], fno->d_name); + fileNo++; + } + } + } while(!result && fileNo < TZSVC_MAX_DIR_ENTRIES); + } + + // Success? + if(result == FR_OK && (fno == NULL || fno->d_name[0] == 0 || fileNo == TZSVC_MAX_DIR_ENTRIES)) + { + // Validate the cache. + osControl.dirMap.valid = 1; + osControl.dirMap.entries = fileNo; + strcpy(osControl.dirMap.directory, directory); + // Save the filetype. + osControl.dirMap.type = type; + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to open a file for reading and return requested sectors. +// +uint8_t svcReadFile(uint8_t mode, enum FILE_TYPE type) +{ + // Locals - dont use global as this is a seperate thread. + // + static FILE *File; + static uint8_t fileOpen = 0; // Seperate flag as their is no public way to validate that File is open and valid, the method in FatFS is private for this functionality. + static uint8_t fileSector = 0; // Sector number being read. + FRESULT result = FR_OK; + unsigned int readSize; + char fqfn[256 + 13]; + + // Find the required file. + // Request to open? Validate that we dont already have an open file then find and open the file. + if(mode == TZSVC_OPEN) + { + // Close if previously open. + if(fileOpen == 1) + svcReadFile(TZSVC_CLOSE, type); + + // Setup the defaults + // + svcSetDefaults(type); + + if(type == CAS || type == BAS) + { + // Build the full filename from what has been provided. + // Cassette and basic images, create filename as they are not cached. + sprintf(fqfn, "%s%s/%s.%s", OS_BASE_DIR, svcControl.directory, svcControl.filename, (type == MZF ? TZSVC_DEFAULT_MZF_EXT : type == CAS ? TZSVC_DEFAULT_CAS_EXT : TZSVC_DEFAULT_BAS_EXT)); + } + + // Find the file using the given file number or file name. + // + if( (type == MZF && (svcFindFileCache(fqfn, (char *)&svcControl.filename, svcControl.fileNo, type))) || type == CAS || type == BAS ) + { + // Open the file, fqfn has the FQFN of the correct file on the SD drive. + result = (File = fopen(fqfn, "r")) != NULL ? FR_OK : FR_NO_FILE; + + if(result == FR_OK) + { + // Reentrant call to actually read the data. + // + fileOpen = 1; + fileSector = 0; + result = (FRESULT)svcReadFile(TZSVC_NEXT, type); + } + } + } + + // Read the next sector from the file. + else if(mode == TZSVC_NEXT && fileOpen == 1) + { + // If the Z80 is requesting a non sequential sector then seek to the correct location prior to the read. + // + if(fileSector != svcControl.fileSector) + { + result = (fseek(File, (svcControl.fileSector * TZSVC_SECTOR_SIZE), SEEK_SET) != -1) ? FR_OK : FR_DISK_ERR; + fileSector = svcControl.fileSector; + } + + // Proceed if no errors have occurred. + // + if(!result) + { + // Read the required sector. + readSize = fread((char *)&svcControl.sector, 1, TZSVC_SECTOR_SIZE, File); + + // Place number of bytes read into the record for the Z80 to know where EOF is. + // + svcControl.loadSize = readSize; + } + + // Move onto next sector. + fileSector++; + } + + // Close the currently open file. + else if(mode == TZSVC_CLOSE) + { + if(fileOpen) + fclose(File); + fileOpen = 0; + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to create a file for writing and on subsequent calls write the data into that file. +// +uint8_t svcWriteFile(uint8_t mode, enum FILE_TYPE type) +{ + // Locals - dont use global as this is a seperate thread. + // + static FILE *File; + static uint8_t fileOpen = 0; // Seperate flag as their is no public way to validate that File is open and valid, the method in FatFS is private for this functionality. + static uint8_t fileSector = 0; // Sector number being read. + FRESULT result = FR_OK; + unsigned int readSize; + char fqfn[256 + 13]; + + // Request to createopen? Validate that we dont already have an open file and the create the file. + if(mode == TZSVC_OPEN) + { + // Close if previously open. + if(fileOpen == 1) + svcWriteFile(TZSVC_CLOSE, type); + + // Setup the defaults + // + svcSetDefaults(type); + + // Build the full filename from what has been provided. + sprintf(fqfn, "%s%s/%s.%s", OS_BASE_DIR, svcControl.directory, svcControl.filename, (type == MZF ? TZSVC_DEFAULT_MZF_EXT : type == CAS ? TZSVC_DEFAULT_CAS_EXT : TZSVC_DEFAULT_BAS_EXT)); + + // Create the file, fqfn has the FQFN of the correct file on the SD drive. + result = (File = fopen(fqfn, "w+")) != NULL ? FR_OK : FR_NO_FILE; + + // If the file was opened, set the flag to enable writing. + if(!result) + fileOpen = 1; + } + + // Write the next sector into the file. + else if(mode == TZSVC_NEXT && fileOpen == 1) + { + // If the Z80 is requesting a non sequential sector then seek to the correct location prior to the write. + // + if(fileSector != svcControl.fileSector) + { + result = (fseek(File, (svcControl.fileSector * TZSVC_SECTOR_SIZE), SEEK_SET) != -1) ? FR_OK : FR_DISK_ERR; + fileSector = svcControl.fileSector; + } + + // Proceed if no errors have occurred. + // + if(!result) + { + // Write the required sector. + result = (readSize = fwrite((char *)&svcControl.sector, 1, svcControl.saveSize, File)) == svcControl.saveSize ? FR_OK : FR_DISK_ERR; + } + + // Move onto next sector. + fileSector++; + } + + // Close the currently open file. + else if(mode == TZSVC_CLOSE) + { + if(fileOpen) + fclose(File); + fileOpen = 0; + } else + { + printf("WARNING: svcWriteFile called with unknown mode:%d\n", mode); + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to load a file from SD directly into the tranZPUter memory. +// +uint8_t svcLoadFile(enum FILE_TYPE type) +{ + // Locals - dont use global as this is a seperate thread. + // + FRESULT result = FR_OK; + uint32_t bytesRead; + char fqfn[256 + 13]; + + // Setup the defaults + // + svcSetDefaults(type); + + // MZF and MZF Headers are handled with their own methods as it involves looking into the file to determine the name and details. + // + if(type == MZF || type == MZFHDR) + { + // Find the file using the given file number or file name. + // + if(svcFindFileCache(fqfn, (char *)&svcControl.filename, svcControl.fileNo, MZF)) + { + // Call method to load an MZF file. + result = loadMZFZ80Memory(fqfn, (svcControl.loadAddr == 0xFFFF ? 0xFFFFFFFF : svcControl.loadAddr), 0, (type == MZFHDR ? 1 : 0), (svcControl.memTarget == 0 ? TRANZPUTER : MAINBOARD)); + + // Store the filename, used in reload or immediate saves. + // + osControl.lastFile = (uint8_t *)realloc(osControl.lastFile, strlen(fqfn)+1); + if(osControl.lastFile == NULL) + { + printf("Out of memory saving last file name, dependent applications (ie. CP/M) wont work!\n"); + result = FR_NOT_ENOUGH_CORE; + } else + { + strcpy((char *)osControl.lastFile, fqfn); + } + + } else + { + result = FR_NO_FILE; + } + } else + // Cassette images are for NASCOM/Microsoft Basic. The files are in NASCOM format so the header needs to be skipped. + // BAS files are for human readable BASIC files. + if(type == CAS || type == BAS) + { + // Build the full filename from what has been provided. + sprintf(fqfn, "%s%s/%s.%s", OS_BASE_DIR, svcControl.directory, svcControl.filename, type == CAS ? TZSVC_DEFAULT_CAS_EXT : TZSVC_DEFAULT_BAS_EXT); + + // For the tokenised cassette, skip the header and load directly into the memory location provided. The load size given is the maximum size of file + // and the loadZ80Memory method will only load upto the given size. + // + if((result=loadZ80Memory((const char *)fqfn, 0, svcControl.loadAddr, svcControl.loadSize, &bytesRead, TRANZPUTER)) != FR_OK) + { + printf("Error: Failed to load CAS:%s into tranZPUter memory.\n", fqfn); + } else + { + // Return the size of load in the service control record. + svcControl.loadSize = (uint16_t) bytesRead; + } + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to save a file from tranZPUter memory directly into a file on the SD card. +// +uint8_t svcSaveFile(enum FILE_TYPE type) +{ + // Locals - dont use global as this is a seperate thread. + // + FRESULT result = FR_OK; + char fqfn[256 + 13]; + char asciiFileName[TZSVC_FILENAME_SIZE+1]; + uint32_t addrOffset = SRAM_BANK0_ADDR; + t_svcDirEnt mzfHeader; + + // Setup the defaults + // + svcSetDefaults(type); + + // MZF are handled with their own methods as it involves looking into the file to determine the name and details. + // + if(type == MZF) + { + // Setup bank in which to load the header/data. Default is bank 0, different host hardware uses different banks. + // + if(svcControl.memTarget == 0 && z80Control.hostType == HW_MZ800) + { + addrOffset = SRAM_BANK6_ADDR; + } + else if(svcControl.memTarget == 0 && z80Control.hostType == HW_MZ2000) + { + addrOffset = SRAM_BANK6_ADDR; + } + + // Get the MZF header which contains the details of the file to save. + copyFromZ80((uint8_t *)&mzfHeader, addrOffset + MZ_CMT_ADDR, MZF_HEADER_SIZE, TRANZPUTER); + + // Need to extract and convert the filename to create a file. + // + convertSharpFilenameToAscii(asciiFileName, (char *)mzfHeader.fileName, TZSVC_FILENAME_SIZE); + + // Build filename. + // + sprintf(fqfn, "%s%s/%s.%s", OS_BASE_DIR, svcControl.directory, asciiFileName, TZSVC_DEFAULT_MZF_EXT); + + // Convert any non-FAT32 characters prior to save. + convertToFAT32FileNameFormat(fqfn); + + // Call the main method to save memory passing in the correct MZF details and header. + result = saveZ80Memory(fqfn, (mzfHeader.loadAddr < MZ_CMT_DEFAULT_LOAD_ADDR-3 ? addrOffset+MZ_CMT_DEFAULT_LOAD_ADDR : addrOffset+mzfHeader.loadAddr), mzfHeader.fileSize, &mzfHeader, (svcControl.memTarget == 0 ? TRANZPUTER : MAINBOARD)); + } else + // Cassette images are for NASCOM/Microsoft Basic. The files are in NASCOM format so the header needs to be skipped. + // BAS files are for human readable BASIC files. + if(type == CAS || type == BAS) + { + // Build the full filename from what has been provided. + sprintf(fqfn, "%s%s/%s.%s", OS_BASE_DIR, svcControl.directory, svcControl.filename, type == CAS ? TZSVC_DEFAULT_CAS_EXT : TZSVC_DEFAULT_BAS_EXT); + + // Convert any non-FAT32 characters prior to save. + convertToFAT32FileNameFormat(fqfn); + + // Call the main method to save memory passing in the correct details from the service record. + result = saveZ80Memory(fqfn, svcControl.saveAddr, svcControl.saveSize, NULL, TRANZPUTER); + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to erase a file on the SD card. +// +uint8_t svcEraseFile(enum FILE_TYPE type) +{ + // Locals - dont use global as this is a seperate thread. + // + FRESULT result = FR_OK; + char fqfn[256 + 13]; + + // Setup the defaults + // + svcSetDefaults(MZF); + + // MZF are handled with their own methods as it involves looking into the file to determine the name and details. + // + if(type == MZF) + { + // Find the file using the given file number or file name. + // + if(svcFindFileCache(fqfn, (char *)&svcControl.filename, svcControl.fileNo, type)) + { + // Call method to load an MZF file. + result = remove(fqfn); + } else + { + result = FR_NO_FILE; + } + } else + // Cassette images are for NASCOM/Microsoft Basic. The files are in NASCOM format so the header needs to be skipped. + if(type == CAS) + { + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to add a SD disk file as a CP/M disk drive for read/write by CP/M. +// +uint8_t svcAddCPMDrive(void) +{ + // Locals + char fqfn[256 + 13]; + FRESULT result = FR_OK; + // Sanity checks. + // + if(svcControl.fileNo >= CPM_MAX_DRIVES) + return(TZSVC_STATUS_FILE_ERROR); + + // Disk already allocated? May be a reboot or drive reassignment so free up the memory to reallocate. + // + if(osControl.cpmDriveMap.drive[svcControl.fileNo] != NULL) + { + if(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName != NULL) + { + free(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName); + osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName = 0; + } + free(osControl.cpmDriveMap.drive[svcControl.fileNo]); + osControl.cpmDriveMap.drive[svcControl.fileNo] = 0; + } + + // Build filename for the drive. + // + sprintf(fqfn, "%s/%s/" CPM_DRIVE_TMPL, OS_BASE_DIR, CPM_SD_DRIVES_DIR, svcControl.fileNo); + osControl.cpmDriveMap.drive[svcControl.fileNo] = (t_cpmDrive *)malloc(sizeof(t_cpmDrive)); + + if(osControl.cpmDriveMap.drive[svcControl.fileNo] == NULL) + { + printf("Out of memory adding CP/M drive:%s\n", fqfn); + result = FR_NOT_ENOUGH_CORE; + } else + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName = (uint8_t *)malloc(strlen(fqfn)+1); + if(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName == NULL) + { + printf("Out of memory adding filename to CP/M drive:%s\n", fqfn); + result = FR_NOT_ENOUGH_CORE; + } else + { + strcpy((char *)osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName, fqfn); + + // Open the file to verify it exists and is valid, also to assign the file handle. + // + result = (osControl.cpmDriveMap.drive[svcControl.fileNo]->File = fopen(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName, "r+")) != NULL ? FR_OK : FR_NO_FILE; + + // If no error occurred, read in the header. + // + if(!result) + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastTrack = 0; + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastSector = 0; + } else + { + // Error opening file so free up and release slot, return error. + free(osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName); + osControl.cpmDriveMap.drive[svcControl.fileNo]->fileName = 0; + free(osControl.cpmDriveMap.drive[svcControl.fileNo]); + osControl.cpmDriveMap.drive[svcControl.fileNo] = 0; + result = FR_NOT_ENOUGH_CORE; + } + } + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to read one of the opened, attached CPM drive images according to the Track and Sector provided. +// Inputs: +// svcControl.trackNo = Track to read from. +// svcControl.sectorNo = Sector to read from. +// svcControl.fileNo = CPM virtual disk number, which should have been attached with the svcAddCPMDrive method. +// Outputs: +// svcControl.sector = 512 bytes read from file. +// +uint8_t svcReadCPMDrive(void) +{ + // Locals. + FRESULT result = FR_OK; + uint32_t fileOffset; + unsigned int readSize; + + // Sanity checks. + // + if(svcControl.fileNo >= CPM_MAX_DRIVES || osControl.cpmDriveMap.drive[svcControl.fileNo] == NULL) + { + printf("svcReadCPMDrive: Illegal input values: fileNo=%d, driveMap=%08lx\n", svcControl.fileNo, (uint32_t)osControl.cpmDriveMap.drive[svcControl.fileNo]); + return(TZSVC_STATUS_FILE_ERROR); + } + + // Calculate the offset into the file. + fileOffset = ((svcControl.trackNo * CPM_SECTORS_PER_TRACK) + svcControl.sectorNo) * TZSVC_SECTOR_SIZE; + + // Seek to the correct location as directed by the track/sector. + result = (fseek(osControl.cpmDriveMap.drive[svcControl.fileNo]->File, fileOffset, SEEK_SET) != -1) ? FR_OK : FR_DISK_ERR; + if(!result) + readSize = fread((char *)svcControl.sector, 1, TZSVC_SECTOR_SIZE, osControl.cpmDriveMap.drive[svcControl.fileNo]->File); + + // No errors but insufficient bytes read, either the image is bad or there was an error! + if(!result && readSize != TZSVC_SECTOR_SIZE) + { + result = FR_DISK_ERR; + } else + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastTrack = svcControl.trackNo; + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastSector = svcControl.sectorNo; + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Method to write to one of the opened, attached CPM drive images according to the Track and Sector provided. +// Inputs: +// svcControl.trackNo = Track to write into. +// svcControl.sectorNo = Sector to write into. +// svcControl.fileNo = CPM virtual disk number, which should have been attached with the svcAddCPMDrive method. +// svcControl.sector = 512 bytes to write into the file. +// Outputs: +// +uint8_t svcWriteCPMDrive(void) +{ + // Locals. + FRESULT result = FR_OK; + uint32_t fileOffset; + unsigned int writeSize; + + // Sanity checks. + // + if(svcControl.fileNo >= CPM_MAX_DRIVES || osControl.cpmDriveMap.drive[svcControl.fileNo] == NULL) + { + printf("svcWriteCPMDrive: Illegal input values: fileNo=%d, driveMap=%08lx\n", svcControl.fileNo, (uint32_t)osControl.cpmDriveMap.drive[svcControl.fileNo]); + return(TZSVC_STATUS_FILE_ERROR); + } + + // Calculate the offset into the file. + fileOffset = ((svcControl.trackNo * CPM_SECTORS_PER_TRACK) + svcControl.sectorNo) * TZSVC_SECTOR_SIZE; + + // Seek to the correct location as directed by the track/sector. + result = (fseek(osControl.cpmDriveMap.drive[svcControl.fileNo]->File, fileOffset, SEEK_SET) != -1) ? FR_OK : FR_DISK_ERR; + if(!result) + { + #if(DEBUG_ENABLED & 0x02) + if(Z80Ctrl->debug & 0x02) + { + printf("Writing offset=%08lx\n", fileOffset); + for(uint16_t idx=0; idx < TZSVC_SECTOR_SIZE; idx++) + { + printf("%02x ", svcControl.sector[idx]); + if(idx % 32 == 0) + printf("\n"); + } + printf("\n"); + } + #endif + + writeSize = fwrite((char *)svcControl.sector, 1, TZSVC_SECTOR_SIZE, osControl.cpmDriveMap.drive[svcControl.fileNo]->File); + } + + // No errors but insufficient bytes written, either the image is bad or there was an error! + if(!result && writeSize != TZSVC_SECTOR_SIZE) + { + result = FR_DISK_ERR; + } else + { + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastTrack = svcControl.trackNo; + osControl.cpmDriveMap.drive[svcControl.fileNo]->lastSector = svcControl.sectorNo; + } + + // Return values: 0 - Success : maps to TZSVC_STATUS_OK + // 1 - Fail : maps to TZSVC_STATUS_FILE_ERROR + return(result == FR_OK ? TZSVC_STATUS_OK : TZSVC_STATUS_FILE_ERROR); +} + +// Simple method to get the service record address which is dependent upon memory mode which in turn is dependent upon software being run. +// +uint32_t getServiceAddr(void) +{ + // Locals. + uint32_t addr = TZSVC_CMD_STRUCT_ADDR_TZFS; + uint8_t memoryMode = Z80Ctrl->memoryMode; + + // If in CPM mode then set the service address accordingly. + if(memoryMode == TZMM_CPM || memoryMode == TZMM_CPM2) + addr = TZSVC_CMD_STRUCT_ADDR_CPM; + + // If in MZ700 mode then set the service address accordingly. + if(memoryMode == TZMM_MZ700_0 || memoryMode == TZMM_MZ700_2 || memoryMode == TZMM_MZ700_3 || memoryMode == TZMM_MZ700_4) + addr = TZSVC_CMD_STRUCT_ADDR_MZ700; + + // If in MZ2000 mode then the service address differs according to boot state. + if(memoryMode == TZMM_MZ2000) + { + // Get the machine state and set the service address accordingly. + if(z80Control.iplMode) + { + addr = TZSVC_CMD_STRUCT_ADDR_MZ2000_IPL; + } + else + { + addr = TZSVC_CMD_STRUCT_ADDR_MZ2000_NST; + } + } + return(addr); +} + + +// Method to copy memory from the K64F to the Z80. +// +uint8_t copyFromZ80(uint8_t *dst, uint32_t src, uint32_t size, enum TARGETS target) +{ + // Locals. + uint8_t result = 0; + + // Sanity checks. + // + if((target == MAINBOARD && (src+size) > 0x10000) || (target == TRANZPUTER && (src+size) > TZ_MAX_Z80_MEM)) + return(1); + + // Simple copy in FusionX. + for(uint32_t idx=0; idx < size; idx++) + { + // And now read the byte and store. + *dst = readZ80Memory((uint32_t)src); + src++; + dst++; + } + + return(result); +} + +// Method to copy memory to the K64F from the Z80. +// +uint8_t copyToZ80(uint32_t dst, uint8_t *src, uint32_t size, enum TARGETS target) +{ + // Locals. + uint8_t result = 0; + + // Sanity checks. + // + if((target == MAINBOARD && (dst+size) > 0x10000) || (target == TRANZPUTER && (dst+size) > TZ_MAX_Z80_MEM)) + return(1); + + // Simple copy in FusionX. + for(uint32_t idx=0; idx < size; idx++) + { + // And now write the byte and to the next address! + writeZ80Memory((uint32_t)dst, *src, target); + src++; + dst++; + } + + return(result); +} + +// Method to test if the autoboot TZFS flag file exists on the SD card. If the file exists, set the autoboot flag. +// +uint8_t testTZFSAutoBoot(void) +{ + // Locals. + uint8_t result = 0; + FILE *File; + + // Detect if the autoboot tranZPUter TZFS flag is set. This is a file called TZFSBOOT in the SD root directory. + if((File = fopen(TZFS_AUTOBOOT_FLAG, "r")) != NULL) + { + result = 1; + fclose(File); + } + + return(result); +} + +// Method to load a BIOS into the tranZPUter and configure for a particular host type. +// +uint8_t loadBIOS(const char *biosFileName, uint32_t loadAddr) +{ + // Locals. + uint8_t result = FR_OK; + + // Load up the given BIOS into tranZPUter memory. + if((result=loadZ80Memory(biosFileName, 0, loadAddr, 0, 0, TRANZPUTER)) != FR_OK) + { + printf("Error: Failed to load %s into tranZPUter memory.\n", biosFileName); + } else + { + // Change frequency to default. + setZ80CPUFrequency(0, 4); + } + return(result); +} + +// Method to load TZFS with an optional BIOS. +// +FRESULT loadTZFS(char *biosFile, uint32_t loadAddr) +{ + // Locals. + FRESULT result = 0; + char fqBiosFile[256 + 13]; + + if(biosFile) + { + sprintf(fqBiosFile, "%s%s/%s", OS_BASE_DIR, TZSVC_DEFAULT_TZFS_DIR, biosFile); + result = loadBIOS(fqBiosFile, loadAddr); + } + if(!result && (result=loadZ80Memory((const char *)OS_BASE_DIR TZSVC_DEFAULT_TZFS_DIR "/" MZ_ROM_TZFS, 0, MZ_UROM_ADDR, 0x1800, 0, TRANZPUTER) != FR_OK)) + { + printf("Error: Failed to load bank 1 of %s into tranZPUter memory.\n", MZ_ROM_TZFS); + } + if(!result && (result=loadZ80Memory((const char *)OS_BASE_DIR TZSVC_DEFAULT_TZFS_DIR "/" MZ_ROM_TZFS, 0x1800, MZ_BANKRAM_ADDR+0x10000, 0x1000, 0, TRANZPUTER) != FR_OK)) + { + printf("Error: Failed to load page 2 of %s into tranZPUter memory.\n", MZ_ROM_TZFS); + } + if(!result && (result=loadZ80Memory((const char *)OS_BASE_DIR TZSVC_DEFAULT_TZFS_DIR "/" MZ_ROM_TZFS, 0x2800, MZ_BANKRAM_ADDR+0x20000, 0x1000, 0, TRANZPUTER) != FR_OK)) + { + printf("Error: Failed to load page 3 of %s into tranZPUter memory.\n", MZ_ROM_TZFS); + } + if(!result && (result=loadZ80Memory((const char *)OS_BASE_DIR TZSVC_DEFAULT_TZFS_DIR "/" MZ_ROM_TZFS, 0x3800, MZ_BANKRAM_ADDR+0x30000, 0x1000, 0, TRANZPUTER) != FR_OK)) + { + printf("Error: Failed to load page 4 of %s into tranZPUter memory.\n", MZ_ROM_TZFS); + } + return(result); +} + +// Method to load the default ROMS into the tranZPUter RAM ready for start. +// If the autoboot flag is set, perform autoboot by wiping the STACK area +// of the SA1510 - this has the effect of JP 00000H. +// +void loadTranZPUterDefaultROMS(uint8_t cpuConfig) +{ + // Locals. + FRESULT result = 0; + + // Now load the default BIOS into memory for the host type. + switch(z80Control.hostType) + { + case HW_MZ700: + result = loadTZFS(MZ_ROM_1Z_013A_40C, MZ_MROM_ADDR); + break; + + case HW_MZ800: + // The MZ-800 uses a composite ROM containing the modified BIOS of the MZ-700 (1Z_013B), the IPL of the MZ-800 (9Z_504M), the CGROM for video text output + // and the BASIC IOCS, a common code area for BASIC, + // + // First we load the MZ-700 compatible BIOS in page 0 to support TZFS when activated. + result = loadTZFS(MZ_ROM_1Z_013A_40C, MZ_MROM_ADDR); + + // Next we load the MZ-800 BIOS in page 7. + if(!result) + { + #if(DEBUG_ENABLED & 0x2) + if(Z80Ctrl->debug & 0x02) printf("Loading 1Z_013B\n"); + #endif + + //result = loadBIOS(MZ_ROM_1Z_013B, MZ800, MZ_800_MROM_ADDR); + result = loadBIOS(MZ_ROM_1Z_013B, MZ_800_MROM_ADDR); + } + + // Load up the CGROM into RAM. + //printf("Loading CGROM\n"); + //result = loadBIOS(MZ_ROM_800_CGROM, MZ800, MZ_800_CGROM_ADDR); + + // Next we load the modified 9Z-504M - modified to add an option to start TZFS. + if(!result) + { + #if(DEBUG_ENABLED & 0x2) + if(Z80Ctrl->debug & 0x02) printf("Loading 9Z_504M\n"); + #endif + result = loadBIOS(MZ_ROM_9Z_504M, MZ_800_IPL_ADDR); + } + + // Finally we load the common IOCS. + if(!result) + { + #if(DEBUG_ENABLED & 0x2) + if(Z80Ctrl->debug & 0x02) printf("Loading BASIC IOCS\n"); + #endif + result = loadBIOS(MZ_ROM_800_IOCS, MZ_800_IOCS_ADDR); + } + break; + + case HW_MZ80B: + //result = loadBIOS(MZ_ROM_MZ80B_IPL, MZ80B, MZ_MROM_ADDR); + result = loadBIOS(MZ_ROM_MZ80B_IPL, MZ_MROM_ADDR); + break; + + case HW_MZ2000: + // Load up the IPL BIOS at least try even if the CGROM failed. + #if(DEBUG_ENABLED & 0x2) + if(Z80Ctrl->debug & 0x02) printf("Loading IPL\n"); + #endif + result = loadBIOS(MZ_ROM_MZ2000_IPL_TZPU, MZ_MROM_ADDR); + if(result != FR_OK) + { + printf("Error: Failed to load IPL ROM %s into tranZPUter memory.\n", MZ_ROM_MZ2000_IPL_TZPU); + } + break; + + case HW_MZ80A: + case HW_UNKNOWN: + default: + result = loadTZFS(MZ_ROM_SA1510_40C, MZ_MROM_ADDR); + break; + } + + // If all was ok loading the roms, complete the startup. + // + if(!result) + { + // Check to see if autoboot is needed. + osControl.tzAutoBoot = testTZFSAutoBoot(); + + // If autoboot flag set, force a restart to the ROM which will call User ROM startup code. + if(osControl.tzAutoBoot) + { + if(z80Control.hostType == HW_MZ800) + { + // On the MZ-800, once all firmware loaded, set the memory mode to MZ-800 and reset to enable processing on the tranZPUter memory rather than host memory. + reqResetZ80(TZMM_MZ800); + } + // MZ-2000 stays in original mode, user will start TZFS via key press in modified IPL bios. + else if(z80Control.hostType == HW_MZ2000) + { + reqResetZ80(TZMM_MZ2000); + } + else + { + // Set the memory model to BOOT so we can bootstrap TZFS. + reqResetZ80(TZMM_BOOT); + } + } + } else + { + printf("Firmware load failure\n"); + } + return; +} + +// Method to process a service request from the z80 running TZFS or CPM. +// +void processServiceRequest(void) +{ + // Locals. + // + uint8_t refreshCacheDir = 0; + uint8_t status = 0; + uint8_t doExit = 0; + uint8_t doReset = 0; + uint32_t actualFreq; + uint32_t copySize = TZSVC_CMD_STRUCT_SIZE; + uint32_t idx = 0; + + // Update the service control record address according to memory mode. + // + z80Control.svcControlAddr = getServiceAddr(); + + // Get the command and associated parameters. + copyFromZ80((uint8_t *)&svcControl, z80Control.svcControlAddr, TZSVC_CMD_SIZE, TRANZPUTER); + + // Need to get the remainder of the data for the write operations. + if(svcControl.cmd == TZSVC_CMD_WRITEFILE || svcControl.cmd == TZSVC_CMD_NEXTWRITEFILE || svcControl.cmd == TZSVC_CMD_WRITESDDRIVE || svcControl.cmd == TZSVC_CMD_SD_WRITESECTOR) + { + copyFromZ80((uint8_t *)&svcControl.sector, z80Control.svcControlAddr+TZSVC_CMD_SIZE, TZSVC_SECTOR_SIZE, TRANZPUTER); + } + + //memoryDump((uint32_t)&svcControl, sizeof(svcControl), 5, 8, 0, 0); + // Check this is a valid request. + if(svcControl.result == TZSVC_STATUS_REQUEST) + { + // Set status to processing. Z80 can use this to decide if the K64F received its request after a given period of time. + setZ80SvcStatus(TZSVC_STATUS_PROCESSING); + + //memoryDump((uint32_t)&svcControl, sizeof(svcControl), 5, 8, 0, 0); + // Action according to command given. + // + switch(svcControl.cmd) + { + // Open a directory stream and return the first block. + case TZSVC_CMD_READDIR: + status=svcReadDirCache(TZSVC_OPEN, svcControl.fileType); + break; + + // Read the next block in the directory stream. + case TZSVC_CMD_NEXTDIR: + status=svcReadDirCache(TZSVC_NEXT, svcControl.fileType); + break; + + // Open a file stream and return the first block. + case TZSVC_CMD_READFILE: + status=svcReadFile(TZSVC_OPEN, svcControl.fileType); + break; + + // Read the next block in the file stream. + case TZSVC_CMD_NEXTREADFILE: + status=svcReadFile(TZSVC_NEXT, svcControl.fileType); + break; + + // Create a file for data write. + case TZSVC_CMD_WRITEFILE: + status=svcWriteFile(TZSVC_OPEN, svcControl.fileType); + break; + + // Write a block of data to an open file. + case TZSVC_CMD_NEXTWRITEFILE: + status=svcWriteFile(TZSVC_NEXT, svcControl.fileType); + break; + + // Close an open dir/file. + case TZSVC_CMD_CLOSE: + svcReadDir(TZSVC_CLOSE, svcControl.fileType); + svcReadFile(TZSVC_CLOSE, svcControl.fileType); + svcWriteFile(TZSVC_CLOSE, svcControl.fileType); + + // Only need to copy the command section back to the Z80 for a close operation. + // + copySize = TZSVC_CMD_SIZE; + break; + + // Load a file directly into target memory. + case TZSVC_CMD_LOADFILE: + status=svcLoadFile(svcControl.fileType); + break; + + // Save a file directly from target memory. + case TZSVC_CMD_SAVEFILE: + status=svcSaveFile(svcControl.fileType); + refreshCacheDir = 1; + break; + + // Erase a file from the SD Card. + case TZSVC_CMD_ERASEFILE: + status=svcEraseFile(svcControl.fileType); + refreshCacheDir = 1; + break; + + // Change active directory. Do this immediately to validate the directory name given. + case TZSVC_CMD_CHANGEDIR: + status=svcCacheDir((const char *)svcControl.directory, svcControl.fileType, 0); + break; + + // Load the 40 column version of the default host bios into memory. + case TZSVC_CMD_LOAD40ABIOS: + loadBIOS(MZ_ROM_SA1510_40C, MZ_MROM_ADDR); + + // Set the frequency of the CPU if we are emulating the hardware. + if(z80Control.hostType != HW_MZ80A) + { + // Change frequency to match Sharp MZ-80A + setZ80CPUFrequency(MZ_80A_CPU_FREQ, 1); + } + break; + + // Load the 80 column version of the default host bios into memory. + case TZSVC_CMD_LOAD80ABIOS: + loadBIOS(MZ_ROM_SA1510_80C, MZ_MROM_ADDR); + + // Set the frequency of the CPU if we are emulating the hardware. + if(z80Control.hostType != HW_MZ80A) + { + // Change frequency to match Sharp MZ-80A + setZ80CPUFrequency(MZ_80A_CPU_FREQ, 1); + } + break; + + // Load the 40 column MZ700 1Z-013A bios into memory for compatibility switch. + case TZSVC_CMD_LOAD700BIOS40: + loadBIOS(MZ_ROM_1Z_013A_40C, MZ_MROM_ADDR); + + // Set the frequency of the CPU if we are emulating the hardware. + if(z80Control.hostType != HW_MZ700) + { + // Change frequency to match Sharp MZ-700 + setZ80CPUFrequency(MZ_700_CPU_FREQ, 1); + } + break; + + // Load the 80 column MZ700 1Z-013A bios into memory for compatibility switch. + case TZSVC_CMD_LOAD700BIOS80: + loadBIOS(MZ_ROM_1Z_013A_80C, MZ_MROM_ADDR); + + // Set the frequency of the CPU if we are emulating the hardware. + if(z80Control.hostType != HW_MZ700) + { + // Change frequency to match Sharp MZ-700 + setZ80CPUFrequency(MZ_700_CPU_FREQ, 1); + } + break; + + // Load the MZ800 9Z-504M bios into memory for compatibility switch. + case TZSVC_CMD_LOAD800BIOS: + loadBIOS(MZ_ROM_9Z_504M, MZ_MROM_ADDR); + + // Set the frequency of the CPU if we are emulating the hardware. + if(z80Control.hostType != HW_MZ800) + { + // Change frequency to match Sharp MZ-800 + setZ80CPUFrequency(MZ_800_CPU_FREQ, 1); + } + break; + + // Load the MZ-80B IPL ROM into memory for compatibility switch. + case TZSVC_CMD_LOAD80BIPL: + loadBIOS(MZ_ROM_MZ80B_IPL, MZ_MROM_ADDR); + + // Set the frequency of the CPU if we are emulating the hardware. + if(z80Control.hostType != HW_MZ80B) + { + // Change frequency to match Sharp MZ-80B + setZ80CPUFrequency(MZ_80B_CPU_FREQ, 1); + } + break; + + // Load the MZ-2000 IPL ROM into memory for compatibility switch. + case TZSVC_CMD_LOAD2000IPL: + loadBIOS(MZ_ROM_MZ2000_IPL, MZ_MROM_ADDR); + + // Set the frequency of the CPU if we are emulating the hardware. + if(z80Control.hostType != HW_MZ2000) + { + // Change frequency to match Sharp MZ-80B + setZ80CPUFrequency(MZ_2000_CPU_FREQ, 1); + } + break; + + // Load TZFS upon request. This service is for the MZ-80B/MZ-2000 which dont have a monitor BIOS installed and TZFS isnt loaded upon reset but rather through user request. + case TZSVC_CMD_LOADTZFS: + switch(z80Control.hostType) + { + case HW_MZ80B: + break; + + case HW_MZ2000: + // Load TZFS and a modified 1Z-013A MZ700 Monitor to provide an interactive TZFS session during IPL mode. + if(loadTZFS(MZ_ROM_1Z_013A_2000, MZ_MROM_ADDR) == FR_OK) + { + // Request a cold IPL start with no ROM load, this will invoke the loaded TZFS as the BOOT IPL. + z80Control.blockResetActions = 1; + } + break; + + // Default, ie. not a valid host, is to do nothing. + default: + break; + } + break; + + // Load the CPM CCP+BDOS from file into the address given. + case TZSVC_CMD_LOADBDOS: + // Flush out drives prior to BDOS reload. + for(idx=0; idx < CPM_MAX_DRIVES; idx++) + { + if(osControl.cpmDriveMap.drive[idx] == NULL || osControl.cpmDriveMap.drive[svcControl.fileNo]->File == NULL) + continue; + + // Flush out drive contents. + fflush(osControl.cpmDriveMap.drive[svcControl.fileNo]->File); + } + if((status=loadZ80Memory((const char *)osControl.lastFile, MZF_HEADER_SIZE, svcControl.loadAddr+0x40000, svcControl.loadSize, 0, TRANZPUTER)) != FR_OK) + { + printf("Error: Failed to load BDOS:%s into tranZPUter memory.\n", (char *)osControl.lastFile); + } + break; + + // Add a CP/M disk to the system for read/write access by CP/M on the Sharp MZ80A. + // + case TZSVC_CMD_ADDSDDRIVE: + status=svcAddCPMDrive(); + break; + + // Read an assigned CP/M drive sector giving an LBA address and the drive number. + // + case TZSVC_CMD_READSDDRIVE: + status=svcReadCPMDrive(); + break; + + // Write a sector to an assigned CP/M drive giving an LBA address and the drive number. + // + case TZSVC_CMD_WRITESDDRIVE: + status=svcWriteCPMDrive(); + + // Only need to copy the command section back to the Z80 for a write operation. + // + copySize = TZSVC_CMD_SIZE; + break; + + // Switch to the mainboard frequency (default). + case TZSVC_CMD_CPU_BASEFREQ: + setZ80CPUFrequency(0, 4); + break; + + // Switch to the alternate frequency managed by the K64F counters. + case TZSVC_CMD_CPU_ALTFREQ: + setZ80CPUFrequency(0, 3); + break; + + // Set the alternate frequency. The TZFS command provides the frequency in KHz so multiply up to Hertz before changing. + case TZSVC_CMD_CPU_CHGFREQ: + actualFreq = setZ80CPUFrequency(svcControl.cpuFreq * 1000, 1); + svcControl.cpuFreq = (uint16_t)(actualFreq / 1000); + break; + + // Switch the hardware to select the hard Z80 CPU. This involves the switch then a reset procedure. + // + case TZSVC_CMD_CPU_SETZ80: + printf("Switch to Z80 unsupported\n"); + + // Set a reset event so that the ROMS are reloaded and the Z80 set. + z80Control.resetEvent = 1; + break; + + // Switch the hardware to select the soft T80 CPU. This involves the switch. + // + case TZSVC_CMD_CPU_SETT80: + printf("Switch to T80 unsupported\n"); + + // Set a reset event so that the ROMS are reloaded and a reset occurs. + z80Control.resetEvent = 1; + break; + + // Switch the hardware to select the soft ZPU Evolution CPU. This involves the switch. + // + case TZSVC_CMD_CPU_SETZPUEVO: + printf("Switch to EVO unsupported\n"); + + // Set a reset event so that the ROMS are reloaded and a reset occurs. + z80Control.resetEvent = 1; + break; + + // Not yet supported on the FusionX! + // + case TZSVC_CMD_EMU_SETMZ80K: + case TZSVC_CMD_EMU_SETMZ80C: + case TZSVC_CMD_EMU_SETMZ1200: + case TZSVC_CMD_EMU_SETMZ80A: + case TZSVC_CMD_EMU_SETMZ700: + case TZSVC_CMD_EMU_SETMZ1500: + case TZSVC_CMD_EMU_SETMZ800: + case TZSVC_CMD_EMU_SETMZ80B: + case TZSVC_CMD_EMU_SETMZ2000: + case TZSVC_CMD_EMU_SETMZ2200: + case TZSVC_CMD_EMU_SETMZ2500: + printf("Error: Unsupported Emulation feature.\n"); + break; + + // Raw direct access to the SD card. This request initialises the requested disk - if shared with this I/O processor then dont perform + // physical initialisation. + case TZSVC_CMD_SD_DISKINIT: + // No logic needed as the K64F initialises the underlying drive and the host accesses, via a mapping table, the 2-> partitions. + break; + + // Raw read access to the SD card. + // + case TZSVC_CMD_SD_READSECTOR: + printf("Error: Unsupported Raw SD Read feature.\n"); + break; + + // Raw write access to the SD card. + // + case TZSVC_CMD_SD_WRITESECTOR: + printf("Error: Unsupported Raw SD Write feature.\n"); + break; + + // Command to exit from TZFS and return machine to original mode. + case TZSVC_CMD_EXIT: + // Disable secondary frequency. + setZ80CPUFrequency(0, 4); + + // Clear the stack and monitor variable area as DRAM wont have been refreshed so will contain garbage. + fillZ80Memory(MZ_MROM_STACK_ADDR, MZ_MROM_STACK_SIZE, 0x00, MAINBOARD); + + // Set to refresh mode just in case the I/O processor performs any future action in silent mode! + z80Control.disableRefresh = 0; + + // Set a flag to perform the physical reset. This is necessary as the service routine holds the Z80 bus and there is an issue with the Z80 if you reset whilst + // the bus is being held (it sometimes misses the first instruction). + doExit = 1; + break; + + default: + printf("WARNING: Unrecognised command:%02x\n", svcControl.cmd); + status=TZSVC_STATUS_BAD_CMD; + break; + } + } else + { + // Indicate error condition. + status=TZSVC_STATUS_BAD_REQ; + } + + // Update the status in the service control record then copy it across to the Z80. + // + svcControl.result = status; + copyToZ80(z80Control.svcControlAddr, (uint8_t *)&svcControl, copySize, TRANZPUTER); + + //memoryDump((uint32_t)&svcControl, sizeof(svcControl), 5, 8, 0, 0); + + // Need to refresh the directory? Do this at the end of the routine so the Sharp MZ80A isnt held up. + if(refreshCacheDir) + svcCacheDir((const char *)svcControl.directory, svcControl.fileType, 1); + + // If the doExit flag is set it means the CPU should be set to original memory mode and reset. + if(doExit == 1) + { + // Now reset the machine so everything starts as power on. + reqResetZ80(TZMM_ORIG); + } + // If the doReset force a reset of the CPU. + if(doReset == 1) + { + // Reset the machine requested by earlier action. + reqResetZ80(TZMM_BOOT); + } + return; +} + +// Handler for the SIGIO signal used by the Z80 Driver when a service request is required. +// +void z80ServiceRequest(int signalNo) +{ + // Locals. + + // Simply call the handler to process the request. + processServiceRequest(); + return; +} + +// Handler for the SIGUSR1 signal which indicates the Z80 has an external reset applied. +// +void z80ResetRequest(int signalNo) +{ + // Locals. + uint32_t status; + + // Stop the Z80 in case it is running. + stopZ80(TZMM_BOOT); + + // Setup memory on Z80 to default. + loadTranZPUterDefaultROMS(CPUMODE_SET_Z80); + + // Setup the defaults + svcSetDefaults(MZF); + + // Cache initial directory. + svcCacheDir(TZSVC_DEFAULT_MZF_DIR, MZF, 1); + + // Start the Z80 after initialisation of the memory. + startZ80(TZMM_BOOT); + return; +} + +// Handler for the HUP, INT, QUIT or TERM signals. On receipt, start a shutdown request. +void shutdownRequest(int signalNo) +{ + // Locals. + + // Terminate signals flag the request by clearing runControl. + if(signalNo == SIGHUP || signalNo == SIGINT || signalNo == SIGQUIT || signalNo == SIGTERM) + { + printf("Terminate request.\n"); + runControl = 0; + } + return; +} + + +// Output usage screen. So mamy commands you do need to be prompted!! +void showArgs(char *progName, struct optparse *options) +{ + printf("%s %s %s %s\n\n", progName, VERSION, COPYRIGHT, AUTHOR); + printf("Synopsis:\n"); + printf("%s --help # This help screen.\n", progName); + return; +} + + +// This is a daemon process, process arguments, initialise logic and enter a loop waiting for signals to arrive. +// The signals indicate an interrupt (service request), shutwon or indicate that the Z80 has had a reset request. +// Loop until a terminate signal comes in. +int main(int argc, char *argv[]) +{ + char buff[64]; + char cmd[64] = { 0 }; + char fileName[256] = { 0 }; + char devName[32] = { 0 }; + int opt; + uint32_t hexData = 0; + long fileOffset = -1; + long fileLen = -1; + int helpFlag = 0; + int verboseFlag = 0; + uint8_t memoryType = 0; + struct ioctlCmd ioctlCmd; + + // Define parameters to be processed. + struct optparse options; + static struct optparse_long long_options[] = + { + {"help", 'h', OPTPARSE_NONE}, + {"verbose", 'v', OPTPARSE_NONE}, + {0} + }; + + // Parse the command line options. + // + optparse_init(&options, argv); + while((opt = optparse_long(&options, long_options, NULL)) != -1) + { + switch(opt) + { + // Hex data. + case 'd': + sscanf(options.optarg, "0x%08x", &hexData); + printf("Hex data:%08x\n", hexData); + break; + + // Verbose mode. + case 'v': + verboseFlag = 1; + break; + + // Command help needed. + case 'h': + helpFlag = 1; + break; + + // Unrecognised, show synopsis. + case '?': + showArgs(argv[0], &options); + printf("%s: %s\n", argv[0], options.errmsg); + return(1); + } + } + + // Open the z80drv driver and attach to its shared memory, basically the Z80 control structure which includes the virtual Z80 memory. + z80Control.fdZ80 = open(DEVICE_FILENAME, O_RDWR|O_NDELAY); + if(z80Control.fdZ80 >= 0) + { + Z80Ctrl = (t_Z80Ctrl *)mmap(0, sizeof(t_Z80Ctrl), PROT_READ | PROT_WRITE, MAP_SHARED, z80Control.fdZ80, 0); + + if(Z80Ctrl == (void *)-1) + { + printf("Failed to attach to the Z80 Control structure, cannot continue, exiting....\n"); + close(z80Control.fdZ80); + exit(1); + } + Z80RAM = (uint8_t *)mmap(0, Z80_VIRTUAL_RAM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, z80Control.fdZ80, 0); + if(Z80RAM == (void *)-1) + { + printf("Failed to attach to the Z80 RAM, cannot continue, exiting....\n"); + close(z80Control.fdZ80); + exit(1); + } + Z80ROM = (uint8_t *)mmap(0, Z80_VIRTUAL_ROM_SIZE+0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, z80Control.fdZ80, 0); + if(Z80ROM == (void *)-1) + { + printf("Failed to attach to the Z80 ROM, cannot continue, exitting....\n"); + close(z80Control.fdZ80); + exit(1); + } + } else + { + printf("Failed to open the Z80 Driver, exiting...\n"); + exit(1); + } + + // Register the service request handler. + signal(SIGIO, z80ServiceRequest); + + // Register the reset request handler. + signal(SIGUSR1, z80ResetRequest); + + // Register close down handlers. + signal(SIGHUP, shutdownRequest); + signal(SIGINT, shutdownRequest); + signal(SIGQUIT, shutdownRequest); + signal(SIGTERM, shutdownRequest); + + // Initiate a reset which loads the default roms and caches SD directory. + z80ResetRequest(0); + + // Enter a loop, process requests as the come in and terminate if requested by signals. + runControl = 1; + while(runControl) + { + usleep(1000); + } + + // Unmap shared memory and close the device. + munmap(Z80Ctrl, sizeof(t_Z80Ctrl)); + close(z80Control.fdZ80); + + return(0); +} + +#ifdef __cplusplus +} +#endif diff --git a/software/FusionX/src/driver/MZ80A/tzpu.h b/software/FusionX/src/driver/MZ80A/tzpu.h new file mode 100755 index 000000000..e84191f44 --- /dev/null +++ b/software/FusionX/src/driver/MZ80A/tzpu.h @@ -0,0 +1,738 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: tzpu.h +// Created: May 2020 +// Author(s): Philip Smart +// Description: The TranZPUter library. +// This file is copied from the original zSoft tranZPUter library header file and +// modified for the FusionX tranZPUter SW driver. It is used by the Z80 driver and the +// user space daemon. +// Hardware references have been removed as the K64F is a virtual process rather +// than a physical MPU. +// Credits: +// Copyright: (c) 2019-2023 Philip Smart +// +// History: May 2020 - Initial write of the TranZPUter software. +// Jul 2020 - Updates to accommodate v2.1 of the tranZPUter board. +// Sep 2020 - Updates to accommodate v2.2 of the tranZPUter board. +// May 2021 - Changes to use 512K-1Mbyte Z80 Static RAM, build time configurable. +// Feb 2023 - Adaptation of zSoft tranzputer.h for the FusionX tranZPUter SW driver. +// +// Notes: See Makefile to enable/disable conditional components +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef TZPU_H +#define TZPU_H + +#ifdef __cplusplus + extern "C" { +#endif + +// Configurable constants. +// +#define REFRESH_BYTE_COUNT 8 // This constant controls the number of bytes read/written to the z80 bus before a refresh cycle is needed. +#define RFSH_BYTE_CNT 256 // Number of bytes we can write before needing a full refresh for the DRAM. +#define HOST_MON_TEST_VECTOR 0x4 // Address in the host monitor to test to identify host type. +#define OS_BASE_DIR "/apps/FusionX/disk/MZ-80A/" // Linux base directory where all the files are stored. On a real tranZPUter this would be the SD card root dir. +#define TZFS_AUTOBOOT_FLAG OS_BASE_DIR "/TZFSBOOT.FLG" // Filename used as a flag, if this file exists in the base directory then TZFS is booted automatically. +#define TZ_MAX_Z80_MEM 0x100000 // Maximum Z80 memory available on the tranZPUter board. + +// tranZPUter Memory Modes - select one of the 32 possible memory models using these constants. +// +#define TZMM_ORIG 0x00 // Original Sharp MZ80A mode, no tranZPUter features are selected except the I/O control registers (default: 0x60-063). +#define TZMM_BOOT 0x01 // Original mode but E800-EFFF is mapped to tranZPUter RAM so TZFS can be booted. +#define TZMM_TZFS 0x02 // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-FFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected. +#define TZMM_TZFS2 0x03 // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 1. +#define TZMM_TZFS3 0x04 // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 2. +#define TZMM_TZFS4 0x05 // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 3. +#define TZMM_CPM 0x06 // CPM main memory configuration, all memory on the tranZPUter board, 64K block 4 selected. Special case for F3C0:F3FF & F7C0:F7FF (floppy disk paging vectors) which resides on the mainboard. +#define TZMM_CPM2 0x07 // CPM main memory configuration, F000-FFFF are on the tranZPUter board in block 4, 0040-CFFF and E800-EFFF are in block 5, mainboard for D000-DFFF (video), E000-E800 (Memory control) selected. + // Special case for 0000:003F (interrupt vectors) which resides in block 4, F3C0:F3FF & F7C0:F7FF (floppy disk paging vectors) which resides on the mainboard. +#define TZMM_COMPAT 0x08 // Original mode but with main DRAM in Bank 0 to allow bootstrapping of programs from other machines such as the MZ700. +#define TZMM_HOSTACCESS 0x09 // Mode to allow code running in Bank 0, address E800:FFFF to access host memory. Monitor ROM 0000-0FFF and Main DRAM 0x1000-0xD000, video and memory mapped I/O are on the host machine, User/Floppy ROM E800-FFFF are in tranZPUter memory. +#define TZMM_MZ700_0 0x0a // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the mainboard. +#define TZMM_MZ700_1 0x0b // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6. +#define TZMM_MZ700_2 0x0c // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6. +#define TZMM_MZ700_3 0x0d // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible. +#define TZMM_MZ700_4 0x0e // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible. +#define TZMM_MZ800 0x0f // MZ800 Mode - Host is an MZ-800 and mode provides for MZ-700/MZ-800 decoding per original machine. +#define TZMM_MZ2000 0x10 // MZ2000 Mode - Running on MZ2000 hardware, configuration set according to runtime configuration registers. +#define TZMM_FPGA 0x15 // Open up access for the K64F to the FPGA resources such as memory. All other access to RAM or mainboard is blocked. +#define TZMM_TZPUM 0x16 // Everything is on mainboard, no access to tranZPUter memory. +#define TZMM_TZPU 0x17 // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory. K64F drives A18-A16 allowing full access to RAM. +//#define TZMM_TZPU0 0x18 // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 0 is selected. +//#define TZMM_TZPU1 0x19 // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 1 is selected. +//#define TZMM_TZPU2 0x1A // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 2 is selected. +//#define TZMM_TZPU3 0x1B // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 3 is selected. +//#define TZMM_TZPU4 0x1C // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 4 is selected. +//#define TZMM_TZPU5 0x1D // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 5 is selected. +//#define TZMM_TZPU6 0x1E // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 6 is selected. +//#define TZMM_TZPU7 0x1F // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 7 is selected. + +// IO addresses on the tranZPUter or mainboard. +// +#define IO_TZ_CTRLLATCH 0x60 // Control latch which specifies the Memory Model/mode. +#define IO_TZ_SETXMHZ 0x62 // Switch to alternate CPU frequency provided by K64F. +#define IO_TZ_SET2MHZ 0x64 // Switch to system CPU frequency. +#define IO_TZ_CLKSELRD 0x66 // Read the status of the clock select, ie. which clock is connected to the CPU. +#define IO_TZ_SVCREQ 0x68 // Service request from the Z80 to be provided by the K64F. +#define IO_TZ_SYSREQ 0x6A // System request from the Z80 to be provided by the K64F. +#define IO_TZ_CPLDCMD 0x6B // Version 2.1 CPLD command register. +#define IO_TZ_CPLDSTATUS 0x6B // Version 2.1 CPLD status register. +#define IO_TZ_CPUCFG 0x6C // Version 2.2 CPU configuration register. +#define IO_TZ_CPUSTATUS 0x6C // Version 2.2 CPU runtime status register. +#define IO_TZ_CPUINFO 0x6D // Version 2.2 CPU information register. +#define IO_TZ_CPLDCFG 0x6E // Version 2.1 CPLD configuration register. +#define IO_TZ_CPLDINFO 0x6F // Version 2.1 CPLD version information register. +#define IO_TZ_PALSLCTOFF 0xA3 // set the palette slot Off position to be adjusted. +#define IO_TZ_PALSLCTON 0xA4 // set the palette slot On position to be adjusted. +#define IO_TZ_PALSETRED 0xA5 // set the red palette value according to the PALETTE_PARAM_SEL address. +#define IO_TZ_PALSETGREEN 0xA6 // set the green palette value according to the PALETTE_PARAM_SEL address. +#define IO_TZ_PALSETBLUE 0xA7 // set the blue palette value according to the PALETTE_PARAM_SEL address. +#define IO_TZ_OSDMNU_SZX 0xA8 // Get OSD Menu Horizontal Size (X). +#define IO_TZ_OSDMNU_SZY 0xA9 // Get OSD Menu Vertical Size (Y). +#define IO_TZ_OSDHDR_SZX 0xAA // Get OSD Status Header Horizontal Size (X). +#define IO_TZ_OSDHDR_SZY 0xAB // Get OSD Status Header Vertical Size (Y). +#define IO_TZ_OSDFTR_SZX 0xAC // Get OSD Status Footer Horizontal Size (X). +#define IO_TZ_OSDFTR_SZY 0xAD // Get OSD Status Footer Vertical Size (Y). +#define IO_TZ_PALETTE 0xB0 // Sets the palette. The Video Module supports 4 bit per colour output but there is only enough RAM for 1 bit per colour so the pallette is used to change the colours output. + // Bits [7:0] defines the pallete number. This indexes a lookup table which contains the required 4bit output per 1bit input. +#define IO_TZ_GPUPARAM 0xB2 // Set parameters. Store parameters in a long word to be used by the graphics command processor. + // The parameter word is 128 bit and each write to the parameter word shifts left by 8 bits and adds the new byte at bits 7:0. +#define IO_TZ_GPUCMD 0xB3 // Set the graphics processor unit commands. + // Bits [5:0] - 0 = Reset parameters. + // 1 = Clear to val. Start Location (16 bit), End Location (16 bit), Red Filter, Green Filter, Blue Filter +#define IO_TZ_VMCTRL 0xB8 // Video Module control register. [2:0] - 000 (default) = MZ80A, 001 = MZ-700, 010 = MZ800, 011 = MZ80B, 100 = MZ80K, 101 = MZ80C, 110 = MZ1200, 111 = MZ2000. [3] = 0 - 40 col, 1 - 80 col. +#define IO_TZ_VMGRMODE 0xB9 // Video Module graphics mode. 7/6 = Operator (00=OR,01=AND,10=NAND,11=XOR), 5=GRAM Output Enable, 4 = VRAM Output Enable, 3/2 = Write mode (00=Page 1:Red, 01=Page 2:Green, 10=Page 3:Blue, 11=Indirect), 1/0=Read mode (00=Page 1:Red, 01=Page2:Green, 10=Page 3:Blue, 11=Not used). +#define IO_TZ_VMREDMASK 0xBA // Video Module Red bit mask (1 bit = 1 pixel, 8 pixels per byte). +#define IO_TZ_VMGREENMASK 0xBB // Video Module Green bit mask (1 bit = 1 pixel, 8 pixels per byte). +#define IO_TZ_VMBLUEMASK 0xBC // Video Module Blue bit mask (1 bit = 1 pixel, 8 pixels per byte). +#define IO_TZ_VMPAGE 0xBD // Video Module memory page register. [1:0] switches in 1 16Kb page (3 pages) of graphics ram to C000 - FFFF. Bits [1:0] = page, 00 = off, 01 = Red, 10 = Green, 11 = Blue. This overrides all MZ700/MZ80B page switching functions. [7] 0 - normal, 1 - switches in CGROM for upload at D000:DFFF. +#define IO_TZ_VMVGATTR 0xBE // Select VGA Border colour and attributes. Bit 2 = Red, 1 = Green, 0 = Blue, 4:3 = VGA Mode, 00 = Off, 01 = 640x480, 10 = 800x600, 11 = 50Hz Internal +#define IO_TZ_VMVGAMODE 0xBF // Select VGA Output mode, ie. Internal, 640x480 etc. Bits [3:0] specify required mode. Undefined default to internal standard frequency. +#define IO_TZ_GDGWF 0xCC // MZ-800 write format register +#define IO_TZ_GDGRF 0xCD // MZ-800 read format register +#define IO_TZ_GDCMD 0xCE // MZ-800 CRTC Mode register +#define IO_TZ_GDCCTRL 0xCF // MZ-800 CRTC control register +#define IO_TZ_MMIO0 0xE0 // MZ-700/MZ-800 Memory management selection ports. +#define IO_TZ_MMIO1 0xE1 // "" +#define IO_TZ_MMIO2 0xE2 // "" +#define IO_TZ_MMIO3 0xE3 // "" +#define IO_TZ_MMIO4 0xE4 // "" +#define IO_TZ_MMIO5 0xE5 // "" +#define IO_TZ_MMIO6 0xE6 // "" +#define IO_TZ_MMIO7 0xE7 // MZ-700/MZ-800 Memory management selection ports. +#define IO_TZ_PPIA 0xE0 // MZ80B/MZ2000 8255 PPI Port A +#define IO_TZ_PPIB 0xE1 // MZ80B/MZ2000 8255 PPI Port B +#define IO_TZ_PPIC 0xE2 // MZ80B/MZ2000 8255 PPI Port C +#define IO_TZ_PPICTL 0xE3 // MZ80B/MZ2000 8255 PPI Control Register +#define IO_TZ_PIT0 0xE4 // MZ80B/MZ2000 8253 PIT Timer 0 +#define IO_TZ_PIT1 0xE5 // MZ80B/MZ2000 8253 PIT Timer 1 +#define IO_TZ_PIT2 0xE6 // MZ80B/MZ2000 8253 PIT Timer 2 +#define IO_TZ_PITCTL 0xE7 // MZ80B/MZ2000 8253 PIT Control Register +#define IO_TZ_PIOA 0xE8 // MZ80B/MZ2000 Z80 PIO Port A +#define IO_TZ_PIOCTLA 0xE9 // MZ80B/MZ2000 Z80 PIO Port A Control Register +#define IO_TZ_PIOB 0xEA // MZ80B/MZ2000 Z80 PIO Port B +#define IO_TZ_PIOCTLB 0xEB // MZ80B/MZ2000 Z80 PIO Port B Control Register +#define IO_TZ_SYSCTRL 0xF0 // System board control register. [2:0] - 000 MZ80A Mode, 2MHz CPU/Bus, 001 MZ80B Mode, 4MHz CPU/Bus, 010 MZ700 Mode, 3.54MHz CPU/Bus. +#define IO_TZ_GRAMMODE 0xF4 // MZ80B Graphics mode. Bit 0 = 0, Write to Graphics RAM I, Bit 0 = 1, Write to Graphics RAM II. Bit 1 = 1, blend Graphics RAM I output on display, Bit 2 = 1, blend Graphics RAM II output on display. +//#define IO_TZ_GRAMOPT 0xF4 // MZ80B/MZ2000 GRAM configuration option. +#define IO_TZ_CRTGRPHPRIO 0xF5 // MZ2000 Graphics priority register, character or a graphics colour has front display priority. +#define IO_TZ_CRTGRPHSEL 0xF6 // MZ2000 Graphics output select on CRT or external CRT +#define IO_TZ_GRAMCOLRSEL 0xF7 // MZ2000 Graphics RAM colour bank select. + +// Addresses on the tranZPUter board. +// +#define SRAM_BANK0_ADDR 0x00000 // Address of the 1st 64K RAM bank in the SRAM chip. +#define SRAM_BANK1_ADDR 0x10000 // "" +#define SRAM_BANK2_ADDR 0x20000 // "" +#define SRAM_BANK3_ADDR 0x30000 // "" +#define SRAM_BANK4_ADDR 0x40000 // "" +#define SRAM_BANK5_ADDR 0x50000 // "" +#define SRAM_BANK6_ADDR 0x60000 // "" +#define SRAM_BANK7_ADDR 0x70000 // "" +#define SRAM_BANK8_ADDR 0x80000 // "" +#define SRAM_BANK9_ADDR 0x90000 // "" +#define SRAM_BANKA_ADDR 0xA0000 // "" +#define SRAM_BANKB_ADDR 0xB0000 // "" +#define SRAM_BANKC_ADDR 0xC0000 // "" +#define SRAM_BANKD_ADDR 0xD0000 // "" +#define SRAM_BANKE_ADDR 0xE0000 // "" +#define SRAM_BANKF_ADDR 0xF0000 // Address of the 16th 64K RAM bank in the SRAM chip. + +// IO register constants. +// +#define CPUMODE_SET_Z80 0x00 // Set the CPU to the hard Z80. +#define CPUMODE_SET_T80 0x01 // Set the CPU to the soft T80. +#define CPUMODE_SET_ZPU_EVO 0x02 // Set the CPU to the soft ZPU Evolution. +#define CPUMODE_SET_EMU_MZ 0x04 // +#define CPUMODE_SET_BBB 0x08 // Place holder for a future soft CPU. +#define CPUMODE_SET_CCC 0x10 // Place holder for a future soft CPU. +#define CPUMODE_SET_DDD 0x20 // Place holder for a future soft CPU. +#define CPUMODE_IS_Z80 0x00 // Status value to indicate if the hard Z80 available. +#define CPUMODE_IS_T80 0x01 // Status value to indicate if the soft T80 available. +#define CPUMODE_IS_ZPU_EVO 0x02 // Status value to indicate if the soft ZPU Evolution available. +#define CPUMODE_IS_EMU_MZ 0x04 // Status value to indicate if the Sharp MZ Series Emulation is available. +#define CPUMODE_IS_BBB 0x08 // Place holder to indicate if a future soft CPU is available. +#define CPUMODE_IS_CCC 0x10 // Place holder to indicate if a future soft CPU is available. +#define CPUMODE_IS_DDD 0x20 // Place holder to indicate if a future soft CPU is available. +#define CPUMODE_CLK_EN 0x40 // Toggle the soft CPU clock, 1 = enable, 0 = disable. +#define CPUMODE_RESET_CPU 0x80 // Reset the soft CPU. Active high, when high the CPU is held in RESET, when low the CPU runs. +#define CPUMODE_IS_SOFT_AVAIL 0x040 // Marker to indicate if the underlying FPGA can support soft CPU's. +#define CPUMODE_IS_SOFT_MASK 0x03F // Mask to filter out the Soft CPU availability flags. + +// CPLD Configuration constants. +#define HWMODE_MZ80K 0x00 // Hardware mode = MZ80K +#define HWMODE_MZ80C 0x01 // Hardware mode = MZ80C +#define HWMODE_MZ1200 0x02 // Hardware mode = MZ1200 +#define HWMODE_MZ80A 0x03 // Hardware mode = MZ80A +#define HWMODE_MZ700 0x04 // Hardware mode = MZ700 +#define HWMODE_MZ800 0x05 // Hardware mode = MZ800 +#define HWMODE_MZ80B 0x06 // Hardware mode = MZ80B +#define HWMODE_MZ2000 0x07 // Hardware mode = MZ2000 +#define MODE_VIDEO_MODULE_ENABLED 0x08 // Hardware enable (bit 3 = 1) or disable of the Video Module on the newer version, the one below will be removed. +#define MODE_VIDEO_MODULE_DISABLED 0x00 // Hardware enable (bit 3 = 0) or disable of the Video Module. +#define MODE_PRESERVE_CONFIG 0x80 // Preserve hardware configuration on RESET. +#define CPLD_HAS_FPGA_VIDEO 0x00 // Flag to indicate if this device supports enhanced video. +#define CPLD_VERSION 0x01 // Version of the CPLD which is being emulated. Version 1 was the original version. + +// CPLD Command Instruction constants. +#define CPLD_RESET_HOST 1 // CPLD level command to reset the host system. +#define CPLD_HOLD_HOST_BUS 2 // CPLD command to hold the host bus. +#define CPLD_RELEASE_HOST_BUS 3 // CPLD command to release the host bus. + +// Video Module control bits. +#define SYSMODE_MZ80A 0x00 // System board mode MZ80A, 2MHz CPU/Bus. +#define SYSMODE_MZ80B 0x01 // System board mode MZ80B, 4MHz CPU/Bus. +#define SYSMODE_MZ700 0x02 // System board mode MZ700, 3.54MHz CPU/Bus. +#define VMMODE_MASK 0xF0 // Mask to mask out video mode. +#define VMMODE_MZ80K 0x00 // Video mode = MZ80K +#define VMMODE_MZ80C 0x01 // Video mode = MZ80C +#define VMMODE_MZ1200 0x02 // Video mode = MZ1200 +#define VMMODE_MZ80A 0x03 // Video mode = MZ80A +#define VMMODE_MZ700 0x04 // Video mode = MZ700 +#define VMMODE_MZ800 0x05 // Video mode = MZ800 +#define VMMODE_MZ1500 0x06 // Video mode = MZ1500 +#define VMMODE_MZ80B 0x07 // Video mode = MZ80B +#define VMMODE_MZ2000 0x08 // Video mode = MZ2000 +#define VMMODE_MZ2200 0x09 // Video mode = MZ2200 +#define VMMODE_MZ2500 0x0A // Video mode = MZ2500 +#define VMMODE_80CHAR 0x10 // Enable 80 character display. +#define VMMODE_80CHAR_MASK 0xEF // Mask to filter out display width control bit. +#define VMMODE_COLOUR 0x20 // Enable colour display. +#define VMMODE_COLOUR_MASK 0xDF // Mask to filter out colour control bit. +#define VMMODE_PCGRAM 0x40 // Enable PCG RAM. +#define VMMODE_VGA_MASK 0xF0 // Mask to filter out the VGA output mode bits. +#define VMMODE_VGA_OFF 0x00 // Set VGA mode off, external monitor is driven by standard internal 60Hz signals. +#define VMMODE_VGA_INT 0x00 // Set VGA mode off, external monitor is driven by standard internal 60Hz signals. +#define VMMODE_VGA_INT50 0x01 // Set VGA mode off, external monitor is driven by standard internal 50Hz signals. +#define VMMODE_VGA_640x480 0x02 // Set external monitor to VGA 640x480 @ 60Hz mode. +#define VMMODE_VGA_800x600 0x03 // Set external monitor to VGA 800x600 @ 60Hz mode. + +// VGA mode border control constants. +// +#define VMBORDER_BLACK 0x00 // VGA has a black border. +#define VMBORDER_BLUE 0x01 // VGA has a blue border. +#define VMBORDER_RED 0x02 // VGA has a red border. +#define VMBORDER_PURPLE 0x03 // VGA has a purple border. +#define VMBORDER_GREEN 0x04 // VGA has a green border. +#define VMBORDER_CYAN 0x05 // VGA has a cyan border. +#define VMBORDER_YELLOW 0x06 // VGA has a yellow border. +#define VMBORDER_WHITE 0x07 // VGA has a white border. +#define VMBORDER_MASK 0xF8 // Mask to filter out current border setting. + +// Sharp MZ colour attributes. +#define VMATTR_FG_BLACK 0x00 // Foreground black character attribute. +#define VMATTR_FG_BLUE 0x10 // Foreground blue character attribute. +#define VMATTR_FG_RED 0x20 // Foreground red character attribute. +#define VMATTR_FG_PURPLE 0x30 // Foreground purple character attribute. +#define VMATTR_FG_GREEN 0x40 // Foreground green character attribute. +#define VMATTR_FG_CYAN 0x50 // Foreground cyan character attribute. +#define VMATTR_FG_YELLOW 0x60 // Foreground yellow character attribute. +#define VMATTR_FG_WHITE 0x70 // Foreground white character attribute. +#define VMATTR_FG_MASKOUT 0x8F // Mask to filter out foreground attribute. +#define VMATTR_FG_MASKIN 0x70 // Mask to filter out foreground attribute. +#define VMATTR_BG_BLACK 0x00 // Background black character attribute. +#define VMATTR_BG_BLUE 0x01 // Background blue character attribute. +#define VMATTR_BG_RED 0x02 // Background red character attribute. +#define VMATTR_BG_PURPLE 0x03 // Background purple character attribute. +#define VMATTR_BG_GREEN 0x04 // Background green character attribute. +#define VMATTR_BG_CYAN 0x05 // Background cyan character attribute. +#define VMATTR_BG_YELLOW 0x06 // Background yellow character attribute. +#define VMATTR_BG_WHITE 0x07 // Background white character attribute. +#define VMATTR_BG_MASKOUT 0xF8 // Mask to filter out background attribute. +#define VMATTR_BG_MASKIN 0x07 // Mask to filter out background attribute. + +// Sharp MZ constants. +// +#define MZ_MROM_ADDR 0x00000 // Monitor ROM start address. +#define MZ_800_MROM_ADDR 0x70000 // MZ-800 Monitor ROM address. +#define MZ_800_CGROM_ADDR 0x71000 // MZ-800 CGROM address during reset when it is loaded into the PCG. +#define MZ_800_IPL_ADDR 0x7E000 // Address of the 9Z_504M IPL BIOS. +#define MZ_800_IOCS_ADDR 0x7F400 // Address of the MZ-800 common IOCS bios. +#define MZ_MROM_STACK_ADDR 0x01000 // Monitor ROM start stack address. +#define MZ_MROM_STACK_SIZE 0x000EF // Monitor ROM stack size. +#define MZ_UROM_ADDR 0x0E800 // User ROM start address. +#define MZ_BANKRAM_ADDR 0x0F000 // Floppy API address which is used in TZFS as the paged RAM for additional functionality. +#define MZ_CMT_ADDR 0x010F0 // Address of the CMT (tape) header record. +#define MZ_CMT_DEFAULT_LOAD_ADDR 0x01200 // The default load address for a CMT, anything below this is normally illegal. +#define MZ_VID_RAM_ADDR 0x0D000 // Start of Video RAM +#define MZ_VID_RAM_SIZE 2048 // Size of Video RAM. +#define MZ_VID_MAX_COL 40 // Maximum column for the host display +#define MZ_VID_MAX_ROW 25 // Maximum row for the host display +#define MZ_VID_DFLT_BYTE 0x00 // Default character (SPACE) for video RAM. +#define MZ_ATTR_RAM_ADDR 0xD800 // On machines with the upgrade, the start of the Attribute RAM. +#define MZ_ATTR_RAM_SIZE 2048 // Size of the attribute RAM. +#define MZ_ATTR_DFLT_BYTE 0x07 // Default colour (White on Black) for the attribute. +#define MZ_SCROL_BASE 0xE200 // Base address of the hardware scroll registers. +#define MZ_SCROL_END 0xE2FF // End address of the hardware scroll registers. +#define MZ_MEMORY_SWAP 0xE00C // Address when read swaps the memory from 0000-0FFF -> C000-CFFF +#define MZ_MEMORY_RESET 0xE010 // Address when read resets the memory to the default location 0000-0FFF. +#define MZ_CRT_NORMAL 0xE014 // Address when read sets the CRT to normal display mode. +#define MZ_CRT_INVERSE 0xE018 // Address when read sets the CRT to inverted display mode. +#define MZ_80A_CPU_FREQ 2000000 // CPU Speed of the Sharp MZ-80A +#define MZ_700_CPU_FREQ 3580000 // CPU Speed of the Sharp MZ-700 +#define MZ_80B_CPU_FREQ 4000000 // CPU Speed of the Sharp MZ-80B +#define MZ_2000_CPU_FREQ 4000000 // CPU Speed of the Sharp MZ-2000 +#define MZ_800_CPU_FREQ 3580000 // CPU Speed of the Sharp MZ-800 + +// Service request constants. +// +#define TZSVC_CMD_STRUCT_ADDR_TZFS 0x0ED80 // Address of the command structure within TZFS - exists in 64K Block 0. +#define TZSVC_CMD_STRUCT_ADDR_CPM 0x4F560 // Address of the command structure within CP/M - exists in 64K Block 4. +#define TZSVC_CMD_STRUCT_ADDR_MZ700 0x6FD80 // Address of the command structure within MZ700 compatible programs - exists in 64K Block 6. +#define TZSVC_CMD_STRUCT_ADDR_ZOS 0x11FD80 // 0x7FD80 // Address of the command structure for zOS use, exists in shared memory rather than FPGA. Spans top of block 6 and all of block 7. +#define TZSVC_CMD_STRUCT_ADDR_MZ2000_NST 0x6FD80 // Address of the command structure within MZ2000 compatible programs during normal state - exists in 64K Block 1. +#define TZSVC_CMD_STRUCT_ADDR_MZ2000_IPL 0x07D80 // Address of the command structure within MZ2000 compatible programs during IPL state - exists in 64K Block 0. +#define TZSVC_CMD_STRUCT_SIZE 0x280 // Size of the inter z80/K64 service command memory. +#define TZSVC_CMD_SIZE (sizeof(t_svcControl)-TZSVC_SECTOR_SIZE) +#define TZVC_MAX_CMPCT_DIRENT_BLOCK TZSVC_SECTOR_SIZE/TZSVC_CMPHDR_SIZE // Maximum number of directory entries per sector. +#define TZSVC_MAX_DIR_ENTRIES 255 // Maximum number of files in one directory, any more than this will be ignored. +#define TZSVC_CMPHDR_SIZE 32 // Compacted header size, contains everything except the comment field, padded out to 32bytes. +#define MZF_FILLER_LEN 8 // Filler to pad a compacted header entry to a power of 2 length. +#define TZVC_MAX_DIRENT_BLOCK TZSVC_SECTOR_SIZE/MZF_HEADER_SIZE // Maximum number of directory entries per sector. +#define TZSVC_CMD_READDIR 0x01 // Service command to open a directory and return the first block of entries. +#define TZSVC_CMD_NEXTDIR 0x02 // Service command to return the next block of an open directory. +#define TZSVC_CMD_READFILE 0x03 // Service command to open a file and return the first block. +#define TZSVC_CMD_NEXTREADFILE 0x04 // Service command to return the next block of an open file. +#define TZSVC_CMD_WRITEFILE 0x05 // Service command to create a file and save the first block. +#define TZSVC_CMD_NEXTWRITEFILE 0x06 // Service command to write the next block to the open file. +#define TZSVC_CMD_CLOSE 0x07 // Service command to close any open file or directory. +#define TZSVC_CMD_LOADFILE 0x08 // Service command to load a file directly into tranZPUter memory. +#define TZSVC_CMD_SAVEFILE 0x09 // Service command to save a file directly from tranZPUter memory. +#define TZSVC_CMD_ERASEFILE 0x0a // Service command to erase a file on the SD card. +#define TZSVC_CMD_CHANGEDIR 0x0b // Service command to change active directory on the SD card. +#define TZSVC_CMD_LOAD40ABIOS 0x20 // Service command requesting that the 40 column version of the SA1510 BIOS is loaded. +#define TZSVC_CMD_LOAD80ABIOS 0x21 // Service command requesting that the 80 column version of the SA1510 BIOS is loaded. +#define TZSVC_CMD_LOAD700BIOS40 0x22 // Service command requesting that the MZ700 1Z-013A 40 column BIOS is loaded. +#define TZSVC_CMD_LOAD700BIOS80 0x23 // Service command requesting that the MZ700 1Z-013A 80 column patched BIOS is loaded. +#define TZSVC_CMD_LOAD80BIPL 0x24 // Service command requesting the MZ-80B IPL is loaded. +#define TZSVC_CMD_LOAD800BIOS 0x25 // Service command requesting that the MZ800 9Z-504M BIOS is loaded. +#define TZSVC_CMD_LOAD2000IPL 0x26 // Service command requesting the MZ-2000 IPL is loaded. +#define TZSVC_CMD_LOADTZFS 0x2F // Service command requesting the loading of TZFS. This service is for machines which normally dont have a monitor BIOS. ie. MZ-80B/MZ-2000 and manually request TZFS. +#define TZSVC_CMD_LOADBDOS 0x30 // Service command to reload CPM BDOS+CCP. +#define TZSVC_CMD_ADDSDDRIVE 0x31 // Service command to attach a CPM disk to a drive number. +#define TZSVC_CMD_READSDDRIVE 0x32 // Service command to read an attached SD file as a CPM disk drive. +#define TZSVC_CMD_WRITESDDRIVE 0x33 // Service command to write to a CPM disk drive which is an attached SD file. +#define TZSVC_CMD_CPU_BASEFREQ 0x40 // Service command to switch to the mainboard frequency. +#define TZSVC_CMD_CPU_ALTFREQ 0x41 // Service command to switch to the alternate frequency provided by the K64F. +#define TZSVC_CMD_CPU_CHGFREQ 0x42 // Service command to set the alternate frequency in hertz. +#define TZSVC_CMD_CPU_SETZ80 0x50 // Service command to switch to the external Z80 hard cpu. +#define TZSVC_CMD_CPU_SETT80 0x51 // Service command to switch to the internal T80 soft cpu. +#define TZSVC_CMD_CPU_SETZPUEVO 0x52 // Service command to switch to the internal ZPU Evolution cpu. +#define TZSVC_CMD_EMU_SETMZ80K 0x53 // Service command to switch to the internal Sharp MZ Series Emulation of the MZ80K. +#define TZSVC_CMD_EMU_SETMZ80C 0x54 // "" "" "" MZ80C. +#define TZSVC_CMD_EMU_SETMZ1200 0x55 // "" "" "" MZ1200. +#define TZSVC_CMD_EMU_SETMZ80A 0x56 // "" "" "" MZ80A. +#define TZSVC_CMD_EMU_SETMZ700 0x57 // "" "" "" MZ700. +#define TZSVC_CMD_EMU_SETMZ800 0x58 // "" "" "" MZ800. +#define TZSVC_CMD_EMU_SETMZ1500 0x59 // "" "" "" MZ1500. +#define TZSVC_CMD_EMU_SETMZ80B 0x5A // "" "" "" MZ80B. +#define TZSVC_CMD_EMU_SETMZ2000 0x5B // "" "" "" MZ2000. +#define TZSVC_CMD_EMU_SETMZ2200 0x5C // "" "" "" MZ2200. +#define TZSVC_CMD_EMU_SETMZ2500 0x5D // "" "" "" MZ2500. +#define TZSVC_CMD_SD_DISKINIT 0x60 // Service command to initialise and provide raw access to the underlying SD card. +#define TZSVC_CMD_SD_READSECTOR 0x61 // Service command to provide raw read access to the underlying SD card. +#define TZSVC_CMD_SD_WRITESECTOR 0x62 // Service command to provide raw write access to the underlying SD card. +#define TZSVC_CMD_EXIT 0x7F // Service command to terminate TZFS and restart the machine in original mode. +#define TZSVC_DEFAULT_TZFS_DIR "TZFS" // Default directory where TZFS files are stored. +#define TZSVC_DEFAULT_CPM_DIR "CPM" // Default directory where CPM files are stored. +#define TZSVC_DEFAULT_MZF_DIR "MZF" // Default directory where MZF files are stored. +#define TZSVC_DEFAULT_CAS_DIR "CAS" // Default directory where BASIC CASsette files are stored. +#define TZSVC_DEFAULT_BAS_DIR "BAS" // Default directory where BASIC text files are stored. +#define TZSVC_DEFAULT_MZF_EXT "MZF" // Default file extension for MZF files. +#define TZSVC_DEFAULT_CAS_EXT "CAS" // Default file extension for CASsette files. +#define TZSVC_DEFAULT_BAS_EXT "BAS" // Default file extension for BASic script files stored in readable text. +#define TZSVC_DEFAULT_WILDCARD "*" // Default wildcard file matching. +#define TZSVC_RESULT_OFFSET 0x01 // Offset into structure of the result byte. +#define TZSVC_DIRNAME_SIZE 20 // Limit is size of FAT32 directory name. +#define TZSVC_WILDCARD_SIZE 20 // Very basic pattern matching so small size. +#define TZSVC_FILENAME_SIZE MZF_FILENAME_LEN // Length of a Sharp MZF filename. +#define TZSVC_LONG_FNAME_SIZE (sizeof(t_svcCmpDirEnt) - 1) // Length of a standard filename to fit inside a directory entry. +#define TZSVC_LONG_FMT_FNAME_SIZE 20 // Length of a standard filename formatted in a directory listing. +#define TZSVC_SECTOR_SIZE 512 // SD Card sector buffer size. +#define TZSVC_STATUS_OK 0x00 // Flag to indicate the K64F processing completed successfully. +#define TZSVC_STATUS_FILE_ERROR 0x01 // Flag to indicate a file or directory error. +#define TZSVC_STATUS_BAD_CMD 0x02 // Flag to indicate a bad service command was requested. +#define TZSVC_STATUS_BAD_REQ 0x03 // Flag to indicate a bad request was made, the service status request flag was not set. +#define TZSVC_STATUS_REQUEST 0xFE // Flag to indicate Z80 has posted a request. +#define TZSVC_STATUS_PROCESSING 0xFF // Flag to indicate the K64F is processing a command. +#define TZSVC_OPEN 0x00 // Service request to open a directory or file. +#define TZSVC_NEXT 0x01 // Service request to return the next directory block or file block or write the next file block. +#define TZSVC_CLOSE 0x02 // Service request to close open dir/file. + +// ROM file paths. +#define MZ_ROM_SP1002 "SP1002.rom" // Original MZ-80K ROM +#define MZ_ROM_SA1510_40C "SA1510.rom" // Original 40 character Monitor ROM. +#define MZ_ROM_SA1510_80C "SA1510-8.rom" // Original Monitor ROM patched for 80 character screen mode. +#define MZ_ROM_1Z_013A_40C "1Z-013A.rom" // Original 40 character Monitor ROM for the Sharp MZ700. +#define MZ_ROM_1Z_013A_80C "1Z-013A-8.rom" // Original Monitor ROM patched for the Sharp MZ700 patched for 80 column mode. +#define MZ_ROM_1Z_013A_KM_40C "1Z-013A-KM.rom" // Original 40 character Monitor ROM for the Sharp MZ700 with keyboard remapped for the MZ80A. +#define MZ_ROM_1Z_013A_KM_80C "1Z-013A-KM-8.rom" // Original Monitor ROM patched for the Sharp MZ700 with keyboard remapped for the MZ80A and patched for 80 column mode. +#define MZ_ROM_1Z_013A_2000 "1Z-013A-2000.rom" // Original 40 character Monitor ROM for the Sharp MZ700 modified to run on an MZ-2000. +#define MZ_ROM_9Z_504M_COMBINED "MZ800_IPL.rom" // Original MZ-800 BIOS which comprises the 1Z_013B BIOS, 9Z_504M IPL, CGROM and IOCS. +#define MZ_ROM_9Z_504M "MZ800_9Z_504M.rom" // Modified MZ-800 9Z_504M IPL to contain a select TZFS option. +#define MZ_ROM_1Z_013B "MZ800_1Z_013B.rom" // Original MZ-800 1Z_013B MZ-700 compatible BIOS. +#define MZ_ROM_800_CGROM "MZ800_CGROM.ori" // Original MZ-800 Character Generator ROM. +#define MZ_ROM_800_IOCS "MZ800_IOCS.rom" // Original MZ-800 common IOCS bios. +#define MZ_ROM_MZ80B_IPL "MZ80B_IPL.rom" // Original IPL ROM for the Sharp MZ-80B. +#define MZ_ROM_MZ2000_IPL "MZ2000_IPL.rom" // Original IPL ROM for the Sharp MZ-2000. +#define MZ_ROM_MZ2000_IPL_TZPU "MZ2000_IPL_TZPU.rom" // Modified IPL ROM for the tranZPUter running on the Sharp MZ-2000. +#define MZ_ROM_MZ2000_CGROM "MZ2000_CGROM.rom" // MZ-2000 CGROM. +#define MZ_ROM_TZFS "tzfs.rom" // tranZPUter Filing System ROM. + +// CP/M constants. +// +#define CPM_MAX_DRIVES 16 // Maximum number of drives in CP/M. +#define CPM_SD_DRIVES_DIR TZSVC_DEFAULT_CPM_DIR "/SDC16M/RAW" // Default directory where CPM SD disk drive images are stored. +#define CPM_DRIVE_TMPL "CPMDSK%02u.RAW" // Template for CPM disk drives stored on the SD card. +#define CPM_SECTORS_PER_TRACK 32 // Number of sectors in a track on the virtual CPM disk. +#define CPM_TRACKS_PER_DISK 1024 // Number of tracks on a disk. + + +// Constants for the Sharp MZ80A MZF file format. +#define MZF_HEADER_SIZE 128 // Size of the MZF header. +#define MZF_ATTRIBUTE 0x00 // Code Type, 01 = Machine Code. +#define MZF_FILENAME 0x01 // Title/Name (17 bytes). +#define MZF_FILENAME_LEN 17 // Length of the filename, it is not NULL terminated, generally a CR can be taken as terminator but not guaranteed. +#define MZF_FILESIZE 0x12 // Size of program. +#define MZF_LOADADDR 0x14 // Load address of program. +#define MZF_EXECADDR 0x16 // Exec address of program. +#define MZF_COMMENT 0x18 // Comment, used for details of the file or startup code. +#define MZF_COMMENT_LEN 104 // Length of the comment field. +#define CMT_TYPE_OBJCD 0x001 // MZF contains a binary object. +#define CMT_TYPE_BTX1CD 0x002 // MZF contains a BASIC program. +#define CMT_TYPE_BTX2CD 0x005 // MZF contains a BASIC program. +#define CMT_TYPE_TZOBJCD0 0x0F8 // MZF contains a TZFS binary object for page 0. +#define CMT_TYPE_TZOBJCD1 0x0F9 +#define CMT_TYPE_TZOBJCD2 0x0FA +#define CMT_TYPE_TZOBJCD3 0x0FB +#define CMT_TYPE_TZOBJCD4 0x0FC +#define CMT_TYPE_TZOBJCD5 0x0FD +#define CMT_TYPE_TZOBJCD6 0x0FE +#define CMT_TYPE_TZOBJCD7 0x0FF // MZF contains a TZFS binary object for page 7. + +// Constants for other handled file formats. +// +#define CAS_HEADER_SIZE 256 // Size of the CASsette header. + +// Possible targets the K64F can read from/write to. +enum TARGETS { + MAINBOARD = 0, + TRANZPUTER = 1 +}; + +// Possible machine hardware types the tranZPUter is functioning within. +// +enum MACHINE_HW_TYPES { + HW_MZ80K = HWMODE_MZ80K, // Host hardware = MZ-80K. + HW_MZ80C = HWMODE_MZ80C, // Host hardware = MZ-80C. + HW_MZ1200 = HWMODE_MZ1200, // Host hardware = MZ-1200. + HW_MZ80A = HWMODE_MZ80A, // Host hardware = MZ-80A. + HW_MZ700 = HWMODE_MZ700, // Host hardware = MZ-700. + HW_MZ800 = HWMODE_MZ800, // Host hardware = MZ-800. + HW_MZ80B = HWMODE_MZ80B, // Host hardware = MZ-80B. + HW_MZ2000 = HWMODE_MZ2000, // Host hardware = MZ-2000. + HW_UNKNOWN = 0xFF // Host hardware unknown, fault or CPLD misconfiguration. +}; + +// Groups to which the machines belong. This is a lineage route of the Sharp machines. +// +enum MACHINE_GROUP { + GROUP_MZ80K = 0, // Machines in the MZ80K group, ie. MZ80K/C/1200/80A + GROUP_MZ700 = 1, // Machines in the MZ700 group, ie. MZ700/800/1500 + GROUP_MZ80B = 2 // Machines in the MZ80B group, ie. MZ80B/2000/2200/2500 +}; + +// Types of file which have handlers and can be processed. +// +enum FILE_TYPE { + MZF = 0, // Sharp MZF tape image files. + MZFHDR = 1, // Sharp MZF Header from file only. + CAS = 2, // BASIC CASsette image files. + BAS = 3, // BASic ASCII text script files. + + ALL = 10, // All files to be considered. + ALLFMT = 11 // Special case for directory listings, all files but truncated and formatted. +}; + +// File function return code (FRESULT) - From FatFS. +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + +// Following are only required for the user space daemon which emulates the Cortex-M4 on the tranZPUter SW +#ifndef __KERNEL_DRIVER__ + +// Structure to define a Sharp MZ80A MZF directory structure. This header appears at the beginning of every Sharp MZ80A tape (and more recently archived/emulator) images. +// +typedef struct __attribute__((__packed__)) { + uint8_t attr; // MZF attribute describing the file. + uint8_t fileName[MZF_FILENAME_LEN]; // Each directory entry is the size of an MZF filename. + uint16_t fileSize; // Size of file. + uint16_t loadAddr; // Load address for the file. + uint16_t execAddr; // Execution address where the Z80 starts processing. + uint8_t comment[MZF_COMMENT_LEN]; // Text comment field but often contains a startup machine code program. +} t_svcDirEnt; + +// Structure to define a compacted Sharp MZ80A MZF directory structure (no comment) for use in directory listings. +// This header appears at the beginning of every Sharp MZ80A tape (and more recently archived/emulator) images. +// +typedef struct __attribute__((__packed__)) { + uint8_t attr; // MZF attribute describing the file. + uint8_t fileName[MZF_FILENAME_LEN]; // Each directory entry is the size of an MZF filename. + uint16_t fileSize; // Size of file. + uint16_t loadAddr; // Load address for the file. + uint16_t execAddr; // Execution address where the Z80 starts processing. + uint8_t filler[MZF_FILLER_LEN]; // Filler to pad to a power of 2 length. +} t_svcCmpDirEnt; + +// Structure to hold the map betwen an SD filename and the Sharp file it contains. The file is an MZF format file with a 128 byte header +// and this header contains the name understood on the Sharp MZ80A. +// +typedef struct __attribute__((__packed__)) { + uint8_t *sdFileName; // Name of file on the SD card. + t_svcCmpDirEnt mzfHeader; // Compact Sharp header data of this file. +} t_sharpToSDMap; + +// Structure to define the control information for a CP/M disk drive. +// +typedef struct { + uint8_t *fileName; // FQFN of the CPM disk image file. + uint32_t lastTrack; // Track of last successful operation. + uint32_t lastSector; // Sector of last successful operation. + FILE *File; // Opened file handle of the CPM disk image. +} t_cpmDrive; + +// Structure to define which CP/M drives are added to the system, mapping a number from CP/M into a record containing the details of the file on the SD card. +// +typedef struct { + t_cpmDrive *drive[CPM_MAX_DRIVES]; // 1:1 map of CP/M drive number to an actual file on the SD card. +} t_cpmDriveMap; + +// Structure to hold a map of an entire directory of files on the SD card and their associated Sharp MZ0A filename. +typedef struct __attribute__((__packed__)) { + uint8_t valid; // Is this mapping valid? + uint8_t entries; // Number of entries in cache. + uint8_t type; // Type of file being cached. + char directory[TZSVC_DIRNAME_SIZE]; // Directory this mapping is associated with. + union { + t_sharpToSDMap *mzfFile[TZSVC_MAX_DIR_ENTRIES]; // File mapping of SD file to its Sharp MZ80A name. + uint8_t *sdFileName[TZSVC_MAX_DIR_ENTRIES]; // No mapping for SD filenames, just the file name. + }; +} t_dirMap; + +// Structure to maintain all MZ700 hardware control information in order to emulate the machine. +// +typedef struct { + uint32_t config; // Compacted control register, 31:19 = reserved, 18 = Inhibit mode, 17 = Upper D000:FFFF is RAM (=1), 16 = Lower 0000:0FFF is RAM (=1), 15:8 = old memory mode, 7:0 = current memory mode. + //uint8_t memoryMode; // The memory mode the MZ700 is currently running under, this is determined by the memory control commands from the MZ700. + //uint8_t lockMemoryMode; // The preserved memory mode when entering the locked state. + //uint8_t inhibit; // The inhibit flag, blocks the upper 0xD000:0xFFFF region from being accessed, affects the memoryMode temporarily. + //uint8_t update; // Update flag, indicates to the ISR that a memory mode update is needed. + //uint8_t b0000; // Block 0000:0FFF mode. + //uint8_t bD000; // Block D000:FFFF mode. +} t_mz700; + +// Structure to maintain all MZ-80B hardware control information in order to emulate the machine as near as possible. +typedef struct { + uint32_t config; // Compacted control register, 31:19 = reserved, 18 = Inhibit mode, 17 = Upper D000:FFFF is RAM (=1), 16 = Lower 0000:0FFF is RAM (=1), 15:8 = old memory mode, 7:0 = current memory mode. +} t_mz80b; + +// Structure to maintain all the control and management variables of the Z80 and underlying hardware so that the state of run is well known by any called method. +// +typedef struct { + #if !defined(__APP__) || defined(__TZFLUPD__) + uint32_t svcControlAddr; // Address of the service control record within the Z80 static RAM bank. + uint8_t refreshAddr; // Refresh address for times when the K64F must issue refresh cycles on the Z80 bus. + uint8_t disableRefresh; // Disable refresh if the mainboard DRAM isnt being used. + + enum MACHINE_HW_TYPES hostType; // The underlying host machine, 0 = Sharp MZ-80A, 1 = MZ-700, 2 = MZ-80B + uint8_t iplMode; // Flag to indicate if the host is in IPL (boot) or run mode. Applicable on the MZ-2000/MZ-80B only. + uint8_t blockResetActions; // Flag to request reset actions are blocked on the next detected reset. This is useful on startup or when loading a monitor ROM set different to the default. + t_mz700 mz700; // MZ700 emulation control to detect IO commands and adjust the memory map accordingly. + t_mz80b mz80b; // MZ-80B emulation control to detect IO commands and adjust the memory map and I/O forwarding accordingly. + + uint8_t resetEvent; // A Z80_RESET event occurred, probably user pressing RESET button. + uint32_t freqMultiplier; // Multipler to be applied to CPU frequency. + int fdZ80; // Handle to the Z80 kernel. + #endif +} t_z80Control; + +// Structure to maintain higher level OS control and management variables typically used for TZFS and CPM. +// +typedef struct { + uint8_t tzAutoBoot; // Autoboot the tranZPUter into TZFS mode. + t_dirMap dirMap; // Directory map of SD filenames to Sharp MZ80A filenames. + t_cpmDriveMap cpmDriveMap; // Map of file number to an open SD disk file to be used as a CPM drive. + uint8_t *lastFile; // Last file loaded - typically used for CPM to reload itself. +} t_osControl; + +// Structure to contain inter CPU communications memory for command service processing and results. +// Typically the z80 places a command into the structure in it's memory space and asserts an I/O request, +// the K64F detects the request and reads the lower portion of the struct from z80 memory space, +// determines the command and then either reads the remainder or writes to the remainder. This struct +// exists in both the z80 and K64F domains and data is sync'd between them as needed. +// +typedef struct __attribute__((__packed__)) { + uint8_t cmd; // Command request. + uint8_t result; // Result code. 0xFE - set by Z80, command available, 0xFE - set by K64F, command ack and processing. 0x00-0xF0 = cmd complete and result of processing. + union { + uint8_t dirSector; // Virtual directory sector number. + uint8_t fileSector; // Sector within open file to read/write. + uint8_t vDriveNo; // Virtual or physical SD card drive number. + }; + union { + struct { + uint16_t trackNo; // For virtual drives with track and sector this is the track number + uint16_t sectorNo; // For virtual drives with track and sector this is the sector number. NB For LBA access, this is 32bit and overwrites fileNo/fileType which arent used during raw SD access. + }; + uint32_t sectorLBA; // For LBA access, this is 32bit and used during raw SD access. + struct { + uint8_t memTarget; // Target memory for operation, 0 = tranZPUter, 1 = mainboard. + uint8_t spare1; // Unused variable. + uint16_t spare2; // Unused variable. + }; + }; + uint8_t fileNo; // File number of a file within the last directory listing to open/update. + uint8_t fileType; // Type of file being processed. + union { + uint16_t loadAddr; // Load address for ROM/File images which need to be dynamic. + uint16_t saveAddr; // Save address for ROM/File images which need to be dynamic. + uint16_t cpuFreq; // CPU Frequency in KHz - used for setting of the alternate CPU clock frequency. + }; + union { + uint16_t loadSize; // Size for ROM/File to be loaded. + uint16_t saveSize; // Size for ROM/File to be saved. + }; + uint8_t directory[TZSVC_DIRNAME_SIZE]; // Directory in which to look for a file. If no directory is given default to MZF. + uint8_t filename[TZSVC_FILENAME_SIZE]; // File to open or create. + uint8_t wildcard[TZSVC_WILDCARD_SIZE]; // A basic wildcard pattern match filter to be applied to a directory search. + uint8_t sector[TZSVC_SECTOR_SIZE]; // Sector buffer generally for disk read/write. +} t_svcControl; + +// Structure to define all the directory entries which are packed into a single SD sector which is used between the Z80<->K64F. +// +typedef struct __attribute__((__packed__)) { + t_svcDirEnt dirEnt[TZVC_MAX_DIRENT_BLOCK]; // Fixed number of directory entries per sector/block. +} t_svcDirBlock; + +// Structure to hold compacted directory entries which are packed into a single SD sector which is used between the Z80<->K64F. +// +typedef struct __attribute__((__packed__)) { + t_svcCmpDirEnt dirEnt[TZVC_MAX_CMPCT_DIRENT_BLOCK];// Fixed number of compacted directory entries per sector/block. +} t_svcCmpDirBlock; + +// Mapping table from Sharp MZ80A Ascii to real Ascii. +// +typedef struct { + uint8_t asciiCode; +} t_asciiMap; + +// Mapping table from Ascii to Sharp MZ display code. +// +typedef struct { + uint8_t dispCode; +} t_dispCodeMap; + +// Prototypes. +// +void reqResetZ80(uint8_t); +void startZ80(uint8_t memoryMode); +void stopZ80(uint8_t memoryMode); +uint32_t setZ80CPUFrequency(float, uint8_t); +uint8_t copyFromZ80(uint8_t *, uint32_t, uint32_t, enum TARGETS); +uint8_t copyToZ80(uint32_t, uint8_t *, uint32_t, enum TARGETS); +uint8_t writeZ80Memory(uint32_t, uint8_t, enum TARGETS); +uint8_t readZ80Memory(uint32_t); +void fillZ80Memory(uint32_t, uint32_t, uint8_t, enum TARGETS); +FRESULT loadZ80Memory(const char *, uint32_t, uint32_t, uint32_t, uint32_t *, enum TARGETS); +FRESULT saveZ80Memory(const char *, uint32_t, uint32_t, t_svcDirEnt *, enum TARGETS); +FRESULT loadMZFZ80Memory(const char *, uint32_t, uint32_t *, uint8_t, enum TARGETS); +int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryType, uint32_t memwidth, uint32_t dispaddr, uint8_t dispwidth); + +// Getter/Setter methods! +void convertSharpFilenameToAscii(char *, char *, uint8_t); +void convertToFAT32FileNameFormat(char *); + +// tranZPUter OS i/f methods. +uint8_t setZ80SvcStatus(uint8_t); +void svcSetDefaults(enum FILE_TYPE); +uint8_t svcReadDir(uint8_t, enum FILE_TYPE); +uint8_t svcFindFile(char *, char *, uint8_t, enum FILE_TYPE); +uint8_t svcReadDirCache(uint8_t, enum FILE_TYPE); +uint8_t svcFindFileCache(char *, char *, uint8_t, enum FILE_TYPE); +uint8_t svcCacheDir(const char *, enum FILE_TYPE, uint8_t); +uint8_t svcReadFile(uint8_t, enum FILE_TYPE); +uint8_t svcWriteFile(uint8_t, enum FILE_TYPE); +uint8_t svcLoadFile(enum FILE_TYPE); +uint8_t svcSaveFile(enum FILE_TYPE); +uint8_t svcEraseFile(enum FILE_TYPE); +uint8_t svcAddCPMDrive(void); +uint8_t svcReadCPMDrive(void); +uint8_t svcWriteCPMDrive(void); +uint32_t getServiceAddr(void); +void processServiceRequest(void); +uint8_t loadBIOS(const char *, uint32_t); +FRESULT loadTZFS(char *, uint32_t); +void loadTranZPUterDefaultROMS(uint8_t); +uint8_t testTZFSAutoBoot(void); + +#endif // __KERNEL_DRIVER__ + +#ifdef __cplusplus +} +#endif +#endif // TZPU_H diff --git a/software/FusionX/src/driver/MZ80A/z80ctrl.c b/software/FusionX/src/driver/MZ80A/z80ctrl.c index d360ab859..c50fafb64 100644 --- a/software/FusionX/src/driver/MZ80A/z80ctrl.c +++ b/software/FusionX/src/driver/MZ80A/z80ctrl.c @@ -116,7 +116,10 @@ enum CTRL_COMMANDS { Z80_CMD_DEL_DEVICE = 11, CPLD_CMD_SEND_CMD = 12, CPLD_CMD_SPI_TEST = 13, - CPLD_CMD_PRL_TEST = 14 + CPLD_CMD_PRL_TEST = 14, + #if(DEBUG_ENABLED != 0) + Z80_CMD_DEBUG = 15, + #endif }; @@ -242,7 +245,7 @@ int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryType, uint32_t { case 16: if(pnt+i < endAddr) - printf("%04X", memoryType == 1 ? (uint16_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint16_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint16_t)Z80Ctrl->page[pnt+i] : (uint16_t)Z80Ctrl->iopage[pnt+i]); + printf("%04X", memoryType == 1 ? (uint16_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint16_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint16_t)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : (uint16_t)Z80Ctrl->iopage[pnt+i]); else printf(" "); i++; @@ -250,7 +253,7 @@ int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryType, uint32_t case 32: if(pnt+i < endAddr) - printf("%08lX", memoryType == 1 ? (uint32_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint32_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint32_t)Z80Ctrl->page[pnt+i] : (uint32_t)Z80Ctrl->iopage[pnt+i]); + printf("%08lX", memoryType == 1 ? (uint32_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint32_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint32_t)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : (uint32_t)Z80Ctrl->iopage[pnt+i]); else printf(" "); i++; @@ -259,7 +262,7 @@ int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryType, uint32_t case 8: default: if(pnt+i < endAddr) - printf("%02X", memoryType == 1 ? (uint8_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint8_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint8_t)Z80Ctrl->page[pnt+i] : (uint8_t)Z80Ctrl->iopage[pnt+i]); + printf("%02X", memoryType == 1 ? (uint8_t)Z80RAM[pnt+i] : memoryType == 2 ? (uint8_t)Z80ROM[pnt+i] : memoryType == 3 ? (uint8_t)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : (uint8_t)Z80Ctrl->iopage[pnt+i]); else printf(" "); i++; @@ -274,7 +277,7 @@ int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryType, uint32_t // print single ascii char for (i=0; i < displayWidth; i++) { - c = memoryType == 1 ? (char)Z80RAM[pnt+i] : memoryType == 2 ? (char)Z80ROM[pnt+i] : memoryType == 3 ? (char)Z80Ctrl->page[pnt+i] : (char)Z80Ctrl->iopage[pnt+i]; + c = memoryType == 1 ? (char)Z80RAM[pnt+i] : memoryType == 2 ? (char)Z80ROM[pnt+i] : memoryType == 3 ? (char)*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (pnt+i)) : (char)Z80Ctrl->iopage[pnt+i]; if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~')) fputc((char)c, stdout); else @@ -320,47 +323,57 @@ memoryDumpExit: // Method to load a program or data file into the Z80 memory. First load into Virtual memory and then trigger a sync to bring Host RAM in line. // -int z80load(int fdZ80, char *fileName, uint32_t romLoadAddr, uint8_t useROM) +int z80load(int fdZ80, char *fileName, uint32_t memLoadAddr, long fileOffset, long fileLen, uint8_t memoryType, uint8_t isMZF) { // Locals. struct ioctlCmd ioctlCmd; int result = 0; long fileSize; t_svcDirEnt mzfHeader; + FILE *ptr; - // Open the file and read directly into the Virtual memory via the share. - FILE *ptr; - ptr = fopen(fileName, "rb"); - if(ptr) + // Sanity check. + if(isMZF == 0 && (fileOffset == -1 && fileLen != -1 || fileOffset != -1 && fileLen == -1)) { - // Get size of file for sanity checks. - fseek(ptr, 0, SEEK_END); - fileSize = ftell(ptr); - fseek(ptr, 0, SEEK_SET); + printf("Error: Both --offset and --len are required, dont specify to use entire file.\n"); + result = 1; + } + if(result == 0) + { + // Open the file and read directly into the Virtual memory via the share. + ptr = fopen(fileName, "rb"); + if(ptr) + { + // Get size of file for sanity checks. + fseek(ptr, 0, SEEK_END); + fileSize = ftell(ptr); + fseek(ptr, 0, SEEK_SET); - // Sanity checks. - if(useROM) - { - if((romLoadAddr+fileSize) > Z80_VIRTUAL_ROM_SIZE) + // Sanity checks. + if(isMZF == 0) { - printf("Error: Binary ROM file out of ROM bounds (Size=%ld, Load=%08x)\n", fileSize, romLoadAddr); - result = 1; - } - } else - { - // First the header. - fread((uint8_t *)&mzfHeader, MZF_HEADER_SIZE, 1, ptr); - if((mzfHeader.loadAddr + mzfHeader.fileSize) > Z80_VIRTUAL_RAM_SIZE) + fileSize = (fileLen == -1 ? fileSize : fileLen); + if((memoryType == 1 && (memLoadAddr+fileSize) > Z80_VIRTUAL_RAM_SIZE) || (memoryType == 2 && (memLoadAddr+fileSize) > Z80_VIRTUAL_ROM_SIZE)) + { + printf("Error: Binary file out of memory bounds (Size=%ld, Load=%08x)\n", fileSize, memLoadAddr); + result = 1; + } + } else { - printf("Error: MZF file out of RAM bounds (Size=%ld, Load=%08x)\n", fileSize, romLoadAddr); - result = 1; + // First the header. + fread((uint8_t *)&mzfHeader, MZF_HEADER_SIZE, 1, ptr); + if((mzfHeader.loadAddr + mzfHeader.fileSize) > Z80_VIRTUAL_RAM_SIZE) + { + printf("Error: MZF file out of RAM bounds (Size=%ld, Load=%08x)\n", fileSize, memLoadAddr); + result = 1; + } } } - } - else - { - printf("Couldnt open file:%s\n", fileName); - result = 1; + else + { + printf("Couldnt open file:%s\n", fileName); + result = 1; + } } // No file errors, read contents into Z80 memory. @@ -371,11 +384,35 @@ int z80load(int fdZ80, char *fileName, uint32_t romLoadAddr, uint8_t useROM) ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE; ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd); - if(useROM) + if(isMZF == 0) { + // Position file if offset provided. + if(fileOffset != -1) fseek(ptr, fileOffset, SEEK_SET); + // Now read in the data. - fread(&Z80ROM[romLoadAddr], fileSize, 1, ptr); - printf("Loaded %s, Size:%08x, Addr:%08x\n", fileName, fileSize, romLoadAddr); + switch(memoryType) + { + case 0: + break; + + case 2: + fread(&Z80ROM[memLoadAddr], fileSize, 1, ptr); + break; + + case 3: + fread(&Z80Ctrl->page[memLoadAddr], fileSize, 1, ptr); + break; + + case 4: + fread(&Z80Ctrl->iopage[memLoadAddr], fileSize, 1, ptr); + break; + + case 1: + default: + fread(&Z80RAM[memLoadAddr], fileSize, 1, ptr); + break; + } + printf("Loaded %s, Size:%08x, Addr:%08x, memoryType:%d\n", fileName, fileSize, memLoadAddr, memoryType); } else { @@ -423,7 +460,7 @@ int z80load(int fdZ80, char *fileName, uint32_t romLoadAddr, uint8_t useROM) // Method to save FusionX memory to a local file. // -int z80save(int fdZ80, char *fileName, long addr, long size, long memoryType) +int z80save(int fdZ80, char *fileName, long addr, long size, uint8_t memoryType) { // Locals. struct ioctlCmd ioctlCmd; @@ -447,6 +484,7 @@ int z80save(int fdZ80, char *fileName, long addr, long size, long memoryType) case 2: fwrite(&Z80ROM[addr], size, 1, ptr); + break; case 3: fwrite(&Z80Ctrl->page[addr], size, 1, ptr); @@ -584,6 +622,14 @@ int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long pa ioctlCmd.cmd = IOCTL_CMD_USE_VIRTUAL_RAM; ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd); break; + #if(DEBUG_ENABLED != 0) + case Z80_CMD_DEBUG: + // Send command to enable/disable debugging output. + ioctlCmd.cmd = IOCTL_CMD_DEBUG; + ioctlCmd.debug.level = (uint8_t)param1; + ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd); + break; + #endif case Z80_CMD_MEMORY_TEST: // Send command to test the SPI. ioctlCmd.cmd = IOCTL_CMD_Z80_MEMTEST; @@ -672,10 +718,14 @@ void showArgs(char *progName, struct optparse *options) printf(" = DELDEV --device # Remove a virtual device from the Z80 configuration.\n"); printf(" = SPEED --mult <1, 2, 4, 8, 16, 32, 64, 128> # In Virtual RAM mode, set CPU speed to base clock x factor.\n"); printf(" = LOADMZF --file # Load MZF file into RAM.\n"); - printf(" = LOADROM --file --addr <24 bit addr> # Load contents of binary file into ROM at address. default = 0x000000.\n"); + printf(" # Load contents of binary file into memory at address. default = 0x000000.\n"); + printf(" = LOADMEM --file --addr <24 bit addr> --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM> [--offset --len ]\n"); printf(" = SAVE --file --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = PageTable, 4 = IOPageTable>\n"); printf(" = DUMP --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = PageTable, 4 = IOPageTable>\n"); printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n"); + #if(DEBUG_ENABLED != 0) + printf(" = DEBUG --level # 0 = off, 1 = driver, 2 = k64f, 3 = both.\n"); + #endif printf(" = Z80TEST # Perform various debugging tests\n"); printf(" = SPITEST # Perform SPI testing\n"); printf(" = PRLTEST # Perform Parallel Bus testing\n"); @@ -696,9 +746,14 @@ int main(int argc, char *argv[]) long speedMultiplier = 1; long startAddr = 0x0000; long endAddr = 0x1000; - int memoryType = 0; + long fileOffset = -1; + long fileLen = -1; + #if(DEBUG_ENABLED != 0) + int debugLevel = 0; + #endif int helpFlag = 0; int verboseFlag = 0; + uint8_t memoryType = 0; // Define parameters to be processed. struct optparse options; @@ -714,6 +769,11 @@ int main(int argc, char *argv[]) {"end", 'e', OPTPARSE_REQUIRED}, {"size", 's', OPTPARSE_REQUIRED}, {"device", 'D', OPTPARSE_REQUIRED}, + {"offset", 'O', OPTPARSE_REQUIRED}, + {"len", 'L', OPTPARSE_REQUIRED}, + #if(DEBUG_ENABLED != 0) + {"level", 'l', OPTPARSE_REQUIRED}, + #endif {"verbose", 'v', OPTPARSE_NONE}, {"save", '0', OPTPARSE_NONE}, {"dump", '1', OPTPARSE_NONE}, @@ -726,6 +786,7 @@ int main(int argc, char *argv[]) {"continue", '8', OPTPARSE_NONE}, {"speed", '9', OPTPARSE_NONE}, {"cpldcmd", '+', OPTPARSE_NONE}, + {"debug", 'x', OPTPARSE_NONE}, {"adddev", '-', OPTPARSE_NONE}, {"deldev", ':', OPTPARSE_NONE}, {0} @@ -768,11 +829,30 @@ int main(int argc, char *argv[]) endAddr = startAddr + strtol(options.optarg, NULL, 0); //printf("End Addr:%04x\n", endAddr); break; + + // File offset. + case 'O': + fileOffset = strtol(options.optarg, NULL, 0); + //printf("File Offset:%04x\n", fileOffset); + break; + + // Length. + case 'L': + fileLen = strtol(options.optarg, NULL, 0); + //printf("File Len:%04x\n", fileLen); + break; // Memory type flag, 0 = host, 1 = virtual RAM, 2 = virtual ROM, 3 = page table, 4 = iopage table. case 'T': memoryType = atoi(options.optarg); break; + + #if(DEBUG_ENABLED != 0) + // Debug level, 0 = off, 1 = driver, 2 = k64f, 3 = both. + case 'E': + debugLevel = atoi(options.optarg); + break; + #endif // Filename. case 'f': @@ -800,7 +880,7 @@ int main(int argc, char *argv[]) strcpy(cmd, "LOADMZF"); break; case '3': - strcpy(cmd, "LOADROM"); + strcpy(cmd, "LOADMEM"); break; case '4': strcpy(cmd, "RESET"); @@ -828,6 +908,12 @@ int main(int argc, char *argv[]) break; case ':': strcpy(cmd, "DELDEV"); + break; + #if(DEBUG_ENABLED != 0) + case 'x': + strcpy(cmd, "DEBUG"); + break; + #endif // Verbose mode. case 'v': @@ -882,11 +968,11 @@ int main(int argc, char *argv[]) // Basic string to method mapping. Started off with just 1 or two but has grown, may need a table! if(strcasecmp(cmd, "LOADMZF") == 0) { - z80load(fdZ80, fileName, 0, 0); + z80load(fdZ80, fileName, 0, -1, -1, 1, 1); } else - if(strcasecmp(cmd, "LOADROM") == 0) + if(strcasecmp(cmd, "LOADMEM") == 0) { - z80load(fdZ80, fileName, startAddr, 1); + z80load(fdZ80, fileName, startAddr, fileOffset, fileLen, memoryType, 0); } else if(strcasecmp(cmd, "RESET") == 0) { @@ -940,6 +1026,12 @@ int main(int argc, char *argv[]) { ctrlCmd(fdZ80, CPLD_CMD_SEND_CMD, hexData, 0, 0); } else + #if(DEBUG_ENABLED != 0) + if(strcasecmp(cmd, "DEBUG") == 0) + { + ctrlCmd(fdZ80, Z80_CMD_DEBUG, debugLevel, 0, 0); + } else + #endif // Test methods, if the code is built-in to the driver. if(strcasecmp(cmd, "Z80TEST") == 0) diff --git a/software/FusionX/src/driver/MZ80A/z80driver.c b/software/FusionX/src/driver/MZ80A/z80driver.c index 0b20daff1..d57f29c8d 100644 --- a/software/FusionX/src/driver/MZ80A/z80driver.c +++ b/software/FusionX/src/driver/MZ80A/z80driver.c @@ -29,6 +29,12 @@ // in host memory at full speed. // Feb 2023 - v1.2 Added MZ-80A Rom Filing System device driver. This allows the FusionX // hosted in an MZ-80A to run the original RFS Monitor and software. +// Feb 2023 - v11.3 Added tranZPUter SW device driver. This allows the FusionX hosted +// in any supported host to run TZFS and the updated applications +// such as CP/M, SA-5510 Basic, MS-Basic etc. Adding this device driver +// prepares the ground to add the SOM GPU as the Video emulation of +// the Sharp machines. +// // // Notes: See Makefile to enable/disable conditional components // @@ -132,6 +138,10 @@ static inline void decodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t io rfsDecodeMemoryMapSetup(address, data, ioFlag, readFlag); } else #endif + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU) + { + rfsDecodeMemoryMapSetup(address, data, ioFlag, readFlag); + } else { // Decoding memory address or I/O address? if(ioFlag == 0) @@ -332,7 +342,7 @@ static inline void decodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t io setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx); } } - z80_instant_reset(&Z80CPU); + resetZ80(); } break; @@ -386,9 +396,9 @@ static inline void decodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t io return; } -// Method to decode address and invoke virtual hardware to handle accordingly. +// Method to decode address and invoke virtual RAM, ROM or hardware to handle accordingly. // -static inline zuint8 readVirtualHW(zuint16 address, uint8_t ioFlag) +static inline zuint8 readVirtual(zuint16 address, uint8_t ioFlag) { // Locals. // @@ -400,20 +410,27 @@ static inline zuint8 readVirtualHW(zuint16 address, uint8_t ioFlag) // RFS only has memory mapped registers. if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_RFS) && ioFlag == 0) { - data = rfsRead(address); - } + data = rfsRead(address, ioFlag); + } else #endif - if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU) && (ioFlag == 0 || isVirtualDevice(address, VIRTUAL_DEVICE_TZPU))) + + if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)) { - data = tzpuRead(address); + data = tzpuRead(address, ioFlag); + } + + else if(isVirtualMemory(address)) + { + // Retrieve data from virtual memory. + data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); } return(data); } -// Method to decode address and invoke virtual hardware to handle accordingly. +// Method to decode address and invoke virtual ROM, RAM or hardware to handle accordingly. // -static inline void writeVirtualHW(zuint16 address, zuint8 data, uint8_t ioFlag) +static inline void writeVirtual(zuint16 address, zuint8 data, uint8_t ioFlag) { // Locals. @@ -423,12 +440,19 @@ static inline void writeVirtualHW(zuint16 address, zuint8 data, uint8_t ioFlag) // RFS only has memory mapped registers. if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_RFS) && ioFlag == 0) { - rfsWrite(address, data); - } + rfsWrite(address, data, ioFlag); + } else #endif - if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU) && (ioFlag == 0 || isVirtualDevice(address, VIRTUAL_DEVICE_TZPU))) + + if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)) { - tzpuWrite(address, data); + tzpuWrite(address, data, ioFlag); + } + + else if(isVirtualRAM(address)) + { + // Update virtual memory. + writeVirtualRAM(address, data); } return; @@ -513,15 +537,10 @@ static zuint8 z80_read(void *context, zuint16 address) // Pause until the Last T-State is detected. //while(CPLD_LAST_TSTATE() == 0); } - else if(isVirtualHW(address)) + else if(isVirtual(address)) { - // Decode the address and if virtual logic exists, invoke it. - data = readVirtualHW(address, 0); - } - else if(isVirtualMemory(address)) - { - // Retrieve data from virtual memory. - data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); + // Decode the address and if virtual RAM, ROM or logic exists, invoke it. + data = readVirtual(address, 0); } // Keyport data? Store. @@ -533,6 +552,12 @@ static zuint8 z80_read(void *context, zuint16 address) { Z80Ctrl->keyportHotKey = 0x01; } + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) + { + pr_info("Read:%04x,%02x,%d\n", address, data, CPLD_Z80_INT()); + } + #endif return(data); } @@ -566,17 +591,22 @@ static void z80_write(void *context, zuint16 address, zuint8 data) // Pause until the Last T-State is detected. //while(CPLD_LAST_TSTATE() == 0); } - else if(isVirtualHW(address)) + // Virtual ROM, technically isnt writable, but some devices such as the TZPU use RAM as ROM and mask it + // according to operating mode. + // Virtual Hardware is driver dependent, common method called to write to ROM/HW. + // Virtual RAM is generally a direct write but any driver may change the action. + else if(isVirtual(address)) { - // Decode the address and if virtual logic exists, invoke and write to it. - writeVirtualHW(address, data, 0); + // Decode the address and process. + writeVirtual(address, data, 0); } - else if(isVirtualRAM(address)) + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) { - // Update virtual memory. - writeVirtualRAM(address, data); + pr_info("Write:%04x,%02x,%d\n", address, data, CPLD_Z80_INT()); } - // Cannot write to virtual ROM so no logic. + #endif + return; } // Primary Opcode fetch method. This method is called each time a single or multi-byte opcode is @@ -628,12 +658,18 @@ static zuint8 z80_fetch_opcode(void *context, zuint16 address) for(idx=0; idx < Z80Ctrl->cpuGovernorDelayRAM; idx++); } } -//if(address < 0x9e0 || address > 0xA00) -//if(address >= 0xE800 && address < 0xF000) -//pr_info("Fetch:%04x(%08x):%02x\n", address, getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK), opcode); + // Check if this operation is I/O or known memory I/O so we can look ahead to optimise sending request to CPLD. lookAhead(address, opcode, isVirtualROM((address+1)) ? readVirtualROM((address+1)) : readVirtualRAM((address+1))); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) + { + if(address < 0xF036 || address > 0xF197) + pr_info("Fetch:%04x,%02x,%d\n", address, opcode, CPLD_Z80_INT()); + } + //if(address >= 0xE800) pr_info("Fetch:%04x,%02x\n", address, opcode); + #endif return(opcode); } @@ -669,9 +705,19 @@ static zuint8 z80_fetch(void *context, zuint16 address) } // Check for interrupts. - if(CPLD_Z80_NMI() != 0) z80_nmi(&Z80CPU); + if(CPLD_Z80_NMI() != 0) + { + z80_nmi(&Z80CPU); + } z80_int(&Z80CPU, CPLD_Z80_INT() != 0); -//if(CPLD_Z80_INT() != 0) pr_info("Interrupt High\n"); + + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) + { + if(address < 0xF036 || address > 0xF197) + pr_info("FetchB:%04x,%02x,%d\n", address, data, CPLD_Z80_INT()); + } + #endif return(data); } @@ -705,9 +751,12 @@ static zuint8 z80_in(void *context, zuint16 port) if(isVirtualIO(port)) { // Virtual I/O - call the handler. - value = readVirtualHW(port, 1); + value = readVirtual(port, 1); } -//pr_info("z80_in:0x%x, 0x%x\n", port, value); + + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("z80_in:0x%x, 0x%x\n", port, value); + #endif return(value); } @@ -741,9 +790,12 @@ static void z80_out(void *context, zuint16 port, zuint8 value) if(isVirtualIO(port)) { // Decode the address and if virtual logic exists, invoke and write to it. - writeVirtualHW(port, value, 1); + writeVirtual(port, value, 1); } -//pr_info("z80_out:0x%x, 0x%x\n", port, value); + + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("z80_out:0x%x, 0x%x\n", port, value); + #endif } // NOP - No Operation method. This instruction is used for timing, padding out an application or during @@ -760,7 +812,6 @@ static zuint8 z80_nop(void *context, zuint16 address) // If autorefresh is not enabled, send a single refresh request. if(Z80Ctrl->refreshDRAM == 0) SPI_SEND8(CPLD_CMD_REFRESH); -pr_info("NOP"); } return 0x00; } @@ -816,7 +867,19 @@ static void z80_ldra(void *context) static void z80_reti(void *context) { Z_UNUSED(context) - pr_info("z80_reti\n"); + if(CPLD_Z80_INT() != 0) + { + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) + { + pr_info("LOCKUP:%d\n", CPLD_Z80_INT()); + } + #endif + z80_int(&Z80CPU, false); + } + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("z80_reti\n"); + #endif } static void z80_retn(void *context) { @@ -859,8 +922,9 @@ int thread_z80(void * thread_nr) // Reset pressed? if(CPLD_RESET()) { - z80_instant_reset(&Z80CPU); - setupMemory(Z80Ctrl->defaultPageMode); + resetZ80(); + //z80_instant_reset(&Z80CPU); + //setupMemory(Z80Ctrl->defaultPageMode); // Wait for release before restarting CPU. while(CPLD_RESET()); @@ -905,8 +969,18 @@ int thread_z80(void * thread_nr) static int z80drv_release(struct inode *inodep, struct file *filep) { // Locals. + struct task_struct *task = get_current(); - mutex_unlock(&Z80DRV_MUTEX); + // Is this the K64F de-registering? + if(Z80Ctrl->ioTask == task) + { + Z80Ctrl->ioTask = NULL; + pr_info("I/O processor stopped.\n"); + } else + { + // Free up the mutex preventing more than one control process taking control at the same time. + mutex_unlock(&Z80DRV_MUTEX); + } //pr_info("z80drv: Device successfully closed\n"); return(0); @@ -920,16 +994,28 @@ static int z80drv_open(struct inode *inodep, struct file *filep) { // Locals. int ret = 0; + struct task_struct *task = get_current(); + // I/O Processor? + if(Z80Ctrl->ioTask == NULL && strcmp(task->comm, IO_PROCESSOR_NAME) == 0) + { + Z80Ctrl->ioTask = task; + pr_info("Registering I/O Processor:%s\n", Z80Ctrl->ioTask->comm); + } else + if(Z80Ctrl->ioTask != NULL && strcmp(task->comm, IO_PROCESSOR_NAME) == 0) + { + pr_info("I/O Processor already registered, PID:%d\n", Z80Ctrl->ioTask->pid); + ret = -EBUSY; + goto out; + } else if(!mutex_trylock(&Z80DRV_MUTEX)) { - pr_alert("z80drv: device busy!\n"); + pr_alert("z80drv: Device busy!\n"); ret = -EBUSY; goto out; } + //pr_info("z80drv: Device successfully opened\n"); - //pr_info("z80drv: Device opened\n"); - out: return(ret); } @@ -1129,13 +1215,27 @@ void setupMemory(enum Z80_MEMORY_PROFILE mode) // Locals. uint32_t idx; + // Check to see if the memory mode page has been allocated for current mode. + if(Z80Ctrl->page[Z80Ctrl->memoryMode] == NULL) + { + pr_info("Allocating memory page:%d\n", Z80Ctrl->memoryMode); + (Z80Ctrl->page[Z80Ctrl->memoryMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL); + if ((Z80Ctrl->page[Z80Ctrl->memoryMode]) == NULL) + { + pr_info("z80drv: failed to allocate memory mapping page:%d memory!", Z80Ctrl->memoryMode); + Z80Ctrl->page[Z80Ctrl->memoryMode] = Z80Ctrl->page[0]; + } + } + + // Setup default mode according to run mode, ie. Physical run or Virtual run. + // if(mode == USE_PHYSICAL_RAM) { #if(TARGET_HOST_MZ700 == 1) #endif #if(TARGET_HOST_MZ2000 == 1) // Initialise the page pointers and memory to use physical RAM. - for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) + for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY) { if(idx >= 0 && idx < 0x8000) { @@ -1158,7 +1258,7 @@ void setupMemory(enum Z80_MEMORY_PROFILE mode) #endif #if(TARGET_HOST_MZ80A == 1) // Initialise the page pointers and memory to use physical RAM. - for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) + for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY) { if(idx >= 0 && idx < 0x1000) { @@ -1185,7 +1285,7 @@ void setupMemory(enum Z80_MEMORY_PROFILE mode) } } #endif - for(idx=0x0000; idx < 0x10000; idx++) + for(idx=0x0000; idx < IO_PAGE_SIZE; idx++) { Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW; } @@ -1197,7 +1297,7 @@ void setupMemory(enum Z80_MEMORY_PROFILE mode) #if(TARGET_HOST_MZ2000 == 1) // Initialise the page pointers and memory to use virtual RAM. // MZ-2000 comes up in IPL mode where lower 32K is ROM and upper 32K is RAM remapped from 0x0000. - for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) + for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY) { if(idx >= 0 && idx < 0x8000) { @@ -1208,7 +1308,7 @@ void setupMemory(enum Z80_MEMORY_PROFILE mode) setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (Z80Ctrl->lowMemorySwap ? idx - 0x8000 : idx)); } } - for(idx=0x0000; idx < 0x10000; idx++) + for(idx=0x0000; idx < IO_PAGE_SIZE; idx++) { Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW; } @@ -1217,34 +1317,30 @@ void setupMemory(enum Z80_MEMORY_PROFILE mode) #endif #if(TARGET_HOST_MZ80A == 1) // Initialise the page pointers and memory to use virtual RAM. - for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) + for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY) { if(idx >= 0 && idx < 0x1000) { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx); + setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx); } else if(idx >= 0xD000 && idx < 0xE000) { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx); + setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_VRAM, idx); } else if(idx >= 0xE000 && idx < 0xE800) { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx); + setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_HW, idx); } else if(idx >= 0xE800 && idx < 0xF000) { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_HW, idx); + setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_HW, idx); } else if(idx >= 0xF000 && idx < 0x10000) { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx); - } - else - { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx); + setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx); } } - for(idx=0x0000; idx < 0x10000; idx++) + for(idx=0x0000; idx < IO_PAGE_SIZE; idx++) { Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW; } @@ -1292,7 +1388,9 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_info("IOCTL - Couldnt retrieve command!\n"); else { - //pr_info("IOCTL - Command (%08x)\n", ioctlCmd.cmd); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("IOCTL - Command (%08x)\n", ioctlCmd.cmd); + #endif switch(ioctlCmd.cmd) { // Command to stop the Z80 CPU and power off. @@ -1303,7 +1401,9 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) z80_power(&Z80CPU, FALSE); Z80_PC(Z80CPU) = 0; - pr_info("Z80 stopped.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 stopped.\n"); + #endif break; // Command to power on and start the Z80 CPU. @@ -1311,19 +1411,25 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_RUNNING; mutex_unlock(&Z80RunModeMutex); z80_power(&Z80CPU, TRUE); - pr_info("Z80 started.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 started.\n"); + #endif break; // Command to pause the Z80. case IOCTL_CMD_Z80_PAUSE: mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_PAUSE; mutex_unlock(&Z80RunModeMutex); - pr_info("Z80 paused.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 paused.\n"); + #endif break; // Command to release a paused Z80. case IOCTL_CMD_Z80_CONTINUE: mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_CONTINUE; mutex_unlock(&Z80RunModeMutex); - pr_info("Z80 running.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 running.\n"); + #endif break; // Command to perform a CPU reset. @@ -1333,10 +1439,12 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); } while(nextRunMode == Z80_STOP); - z80_instant_reset(&Z80CPU); - setupMemory(Z80Ctrl->defaultPageMode); + resetZ80(); + mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - pr_info("Z80 Reset.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 Reset.\n"); + #endif break; // Command to setup the page table to use host memory and physical hardware. @@ -1347,11 +1455,12 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) } while(nextRunMode == Z80_STOP); Z80Ctrl->defaultPageMode = USE_PHYSICAL_RAM; - setupMemory(Z80Ctrl->defaultPageMode); - z80_instant_reset(&Z80CPU); + resetZ80(); mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - pr_info("Z80 Set to use Host Memory.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 Set to use Host Memory.\n"); + #endif break; // Command to setup the page table to use virtual memory, only physical hardware is accessed on the host. @@ -1362,10 +1471,15 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) } while(nextRunMode == Z80_STOP); Z80Ctrl->defaultPageMode = USE_VIRTUAL_RAM; - setupMemory(Z80Ctrl->defaultPageMode); - z80_instant_reset(&Z80CPU); + resetZ80(); + + //setupMemory(Z80Ctrl->defaultPageMode); + // z80_instant_reset(&Z80CPU); + mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - pr_info("Z80 Set to use Virtual Memory.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 Set to use Virtual Memory.\n"); + #endif break; // Command to synchronise virtual memory to host DRAM. @@ -1382,7 +1496,9 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) } mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - pr_info("Z80 Host DRAM syncd with Virtual Memory.\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug & 0x01) pr_info("Z80 Host DRAM syncd with Virtual Memory.\n"); + #endif break; // Command to dump out host memory. @@ -1586,6 +1702,13 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); break; + #if(DEBUG_ENABLED & 1) + // Method to turn on/off debug output. + case IOCTL_CMD_DEBUG: + Z80Ctrl->debug = ioctlCmd.debug.level; + break; + #endif + // Command to run a series of SOM to CPLD SPI tests. case IOCTL_CMD_SPI_TEST: // Stop the CPU prior to SPI testing. @@ -1692,7 +1815,7 @@ static int __init ModuleInit(void) // Get device Major number. major = register_chrdev(0, DEVICE_NAME, &z80drv_fops); if (major < 0) { - pr_info("z80drv: fail to register major number!"); + pr_info("z80drv: fail to register major number!\n"); ret = major; goto initExit; } @@ -1700,7 +1823,7 @@ static int __init ModuleInit(void) class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(class)){ unregister_chrdev(major, DEVICE_NAME); - pr_info("z80drv: failed to register device class"); + pr_info("z80drv: failed to register device class\n"); ret = PTR_ERR(class); goto initExit; } @@ -1717,21 +1840,36 @@ static int __init ModuleInit(void) Z80Ctrl = (t_Z80Ctrl *)kmalloc(sizeof(t_Z80Ctrl), GFP_KERNEL); if (Z80Ctrl == NULL) { - pr_info("z80drv: failed to allocate ctrl memory!"); + pr_info("z80drv: failed to allocate ctrl memory!\n"); ret = -ENOMEM; goto initExit; } Z80Ctrl->ram= (uint8_t *)kmalloc(Z80_VIRTUAL_RAM_SIZE, GFP_KERNEL); if (Z80Ctrl->ram == NULL) { - pr_info("z80drv: failed to allocate RAM memory!"); + pr_info("z80drv: failed to allocate RAM memory!\n"); ret = -ENOMEM; goto initExit; } Z80Ctrl->rom= (uint8_t *)kmalloc(Z80_VIRTUAL_ROM_SIZE, GFP_KERNEL); if (Z80Ctrl->rom == NULL) { - pr_info("z80drv: failed to allocate ROM memory!"); + pr_info("z80drv: failed to allocate ROM memory!\n"); + ret = -ENOMEM; + goto initExit; + } + // Default memory mode is 0, ie. Original. Additional modes may be used by drivers such as the tzpu driver. + Z80Ctrl->memoryMode = 0; + for(idx=0; idx < MEMORY_MODES; idx++) + { + (Z80Ctrl->page[idx]) = NULL; + } + + // Allocate standard memory mode mapping page. + (Z80Ctrl->page[Z80Ctrl->memoryMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL); + if ((Z80Ctrl->page[Z80Ctrl->memoryMode]) == NULL) + { + pr_info("z80drv: failed to allocate default memory mapping page memory!\n"); ret = -ENOMEM; goto initExit; } @@ -1856,6 +1994,9 @@ static int __init ModuleInit(void) // Setup memory profile to use internal virtual RAM (SOM kernel RAM rather than HOST DRAM). setupMemory(Z80Ctrl->defaultPageMode); + // Initialise control handles. + Z80Ctrl->ioTask = NULL; + // Initialse run control. mutex_init(&Z80RunModeMutex); mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); @@ -1877,7 +2018,7 @@ static int __init ModuleInit(void) pr_info("Initialisation complete.\n"); // Create thread to run the Z80 cpu. - kthread_z80 = kthread_create(thread_z80, &threadId_z80, "kthread_z80"); + kthread_z80 = kthread_create(thread_z80, &threadId_z80, "z80"); if(kthread_z80 != NULL) { pr_info("kthread - Thread Z80 was created, waking...!\n"); @@ -1900,10 +2041,28 @@ initExit: // the device from the /dev directory. static void __exit ModuleExit(void) { + // Locals. + uint32_t idx; + int result; + // Stop the internal threads. - kthread_stop(kthread_z80); + result = kthread_stop(kthread_z80); + if(result != 0) + { + pr_info("Failed to stop Z80 thread, reason:%d\n", result); + } // Return the memory used for the Z80 'virtual memory' and control variables. + for(idx=0; idx < MEMORY_MODES; idx++) + { + if(Z80Ctrl->page[idx] != NULL) + { + kfree(Z80Ctrl->page[idx]); + Z80Ctrl->page[idx] = NULL; + } + } + kfree(Z80Ctrl->ram); Z80Ctrl->ram = NULL; + kfree(Z80Ctrl->rom); Z80Ctrl->rom = NULL; kfree(Z80Ctrl); // Nothing to be done for the hardware. diff --git a/software/FusionX/src/driver/MZ80A/z80driver.h b/software/FusionX/src/driver/MZ80A/z80driver.h index d92d41bd2..54abfb44e 100644 --- a/software/FusionX/src/driver/MZ80A/z80driver.h +++ b/software/FusionX/src/driver/MZ80A/z80driver.h @@ -39,7 +39,7 @@ #define DRIVER_LICENSE "GPL" #define DRIVER_AUTHOR "Philip D Smart" #define DRIVER_DESCRIPTION "Z80 CPU Emulator and Hardware Interface Driver" -#define DRIVER_VERSION "v1.2" +#define DRIVER_VERSION "v1.3" #define DRIVER_VERSION_DATE "Feb 2023" #define DRIVER_COPYRIGHT "(C) 2018-2023" #define TARGET_HOST_MZ700 0 // Target compilation for an MZ700 @@ -52,6 +52,8 @@ #define MAX_VIRTUAL_DEVICES 5 // Maximum number of allowed virtual devices. #define DEVICE_NAME "z80drv" #define CLASS_NAME "mogu" +#define IO_PROCESSOR_NAME "k64fcpu" // Name of the I/O processor user space application. +#define DEBUG_ENABLED 1 // Memory and IO page types. Used to create a memory page which maps type of address space to real address space on host or virtual memory. #define MEMORY_TYPE_VIRTUAL_MASK 0x00FFFFFF @@ -64,7 +66,8 @@ #define MEMORY_TYPE_PHYSICAL_HW 0x10000000 #define MEMORY_TYPE_VIRTUAL_RAM 0x08000000 #define MEMORY_TYPE_VIRTUAL_ROM 0x04000000 -#define MEMORY_TYPE_VIRTUAL_HW 0x02000000 +#define MEMORY_TYPE_VIRTUAL_RAM_RO 0x02000000 +#define MEMORY_TYPE_VIRTUAL_HW 0x01000000 #define IO_TYPE_PHYSICAL_HW 0x80000000 #define IO_TYPE_VIRTUAL_HW 0x40000000 @@ -88,6 +91,14 @@ #define INSTRUCTION_DELAY_RAM_112MHZ 8 #define INSTRUCTION_DELAY_RAM_224MHZ 4 #define INSTRUCTION_DELAY_RAM_448MHZ 1 +#define INSTRUCTION_EQUIV_FREQ_3_54MHZ 3540000 +#define INSTRUCTION_EQUIV_FREQ_7MHZ 7000000 +#define INSTRUCTION_EQUIV_FREQ_14MHZ 14000000 +#define INSTRUCTION_EQUIV_FREQ_28MHZ 28000000 +#define INSTRUCTION_EQUIV_FREQ_56MHZ 56000000 +#define INSTRUCTION_EQUIV_FREQ_112MHZ 112000000 +#define INSTRUCTION_EQUIV_FREQ_224MHZ 224000000 +#define INSTRUCTION_EQUIV_FREQ_448MHZ 448000000 enum Z80_INSTRUCTION_DELAY { ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_3_54MHZ, @@ -106,6 +117,14 @@ enum Z80_INSTRUCTION_DELAY { RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_112MHZ, RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_224MHZ, RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_448MHZ + CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_3_54MHZ, + CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_7MHZ, + CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_14MHZ, + CPU_FREQUENCY_X8 = INSTRUCTION_EQUIV_FREQ_28MHZ, + CPU_FREQUENCY_X16 = INSTRUCTION_EQUIV_FREQ_56MHZ, + CPU_FREQUENCY_X32 = INSTRUCTION_EQUIV_FREQ_112MHZ, + CPU_FREQUENCY_X64 = INSTRUCTION_EQUIV_FREQ_224MHZ, + CPU_FREQUENCY_X128 = INSTRUCTION_EQUIV_FREQ_448MHZ, }; #endif @@ -127,6 +146,14 @@ enum Z80_INSTRUCTION_DELAY { #define INSTRUCTION_DELAY_RAM_128MHZ 7 #define INSTRUCTION_DELAY_RAM_256MHZ 3 #define INSTRUCTION_DELAY_RAM_512MHZ 1 +#define INSTRUCTION_EQUIV_FREQ_4MHZ 4000000 +#define INSTRUCTION_EQUIV_FREQ_8MHZ 8000000 +#define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000 +#define INSTRUCTION_EQUIV_FREQ_32MHZ 32000000 +#define INSTRUCTION_EQUIV_FREQ_64MHZ 64000000 +#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000 +#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000 +#define INSTRUCTION_EQUIV_FREQ_512MHZ 512000000 enum Z80_INSTRUCTION_DELAY { ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_4MHZ, @@ -145,6 +172,14 @@ enum Z80_INSTRUCTION_DELAY { RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_128MHZ, RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_256MHZ, RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_512MHZ, + CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_4MHZ, + CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_8MHZ, + CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_16MHZ, + CPU_FREQUENCY_X8 = INSTRUCTION_EQUIV_FREQ_32MHZ, + CPU_FREQUENCY_X16 = INSTRUCTION_EQUIV_FREQ_64MHZ, + CPU_FREQUENCY_X32 = INSTRUCTION_EQUIV_FREQ_128MHZ, + CPU_FREQUENCY_X64 = INSTRUCTION_EQUIV_FREQ_256MHZ, + CPU_FREQUENCY_X128 = INSTRUCTION_EQUIV_FREQ_512MHZ, }; #endif @@ -165,7 +200,15 @@ enum Z80_INSTRUCTION_DELAY { #define INSTRUCTION_DELAY_RAM_32MHZ 26 #define INSTRUCTION_DELAY_RAM_64MHZ 13 #define INSTRUCTION_DELAY_RAM_128MHZ 7 -#define INSTRUCTION_DELAY_RAM_256MHZ 3 +#define INSTRUCTION_DELAY_RAM_256MHZ 0 +#define INSTRUCTION_EQUIV_FREQ_2MHZ 2000000 +#define INSTRUCTION_EQUIV_FREQ_4MHZ 4000000 +#define INSTRUCTION_EQUIV_FREQ_8MHZ 8000000 +#define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000 +#define INSTRUCTION_EQUIV_FREQ_32MHZ 32000000 +#define INSTRUCTION_EQUIV_FREQ_64MHZ 64000000 +#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000 +#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000 enum Z80_INSTRUCTION_DELAY { ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_2MHZ, @@ -184,6 +227,14 @@ enum Z80_INSTRUCTION_DELAY { RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_64MHZ, RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_128MHZ, RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_256MHZ, + CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_2MHZ, + CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_4MHZ, + CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_8MHZ, + CPU_FREQUENCY_X8 = INSTRUCTION_EQUIV_FREQ_16MHZ, + CPU_FREQUENCY_X16 = INSTRUCTION_EQUIV_FREQ_32MHZ, + CPU_FREQUENCY_X32 = INSTRUCTION_EQUIV_FREQ_64MHZ, + CPU_FREQUENCY_X64 = INSTRUCTION_EQUIV_FREQ_128MHZ, + CPU_FREQUENCY_X128 = INSTRUCTION_EQUIV_FREQ_256MHZ, }; #endif @@ -203,11 +254,10 @@ enum Z80_INSTRUCTION_DELAY { #define IOCTL_CMD_SEND _IOW('c', 'c', int32_t *) #define IOCTL_CMD_SETPC _IOW('p', 'p', int32_t *) #define IOCTL_CMD_SYNC_TO_HOST_RAM 'V' +#define IOCTL_CMD_DEBUG 'd' #define IOCTL_CMD_SPI_TEST '1' #define IOCTL_CMD_PRL_TEST '2' #define IOCTL_CMD_Z80_MEMTEST '3' - - // Chip Select map MZ80K-MZ700. // @@ -261,38 +311,75 @@ enum Z80_INSTRUCTION_DELAY { // * = MZ-800 host only. // Macros to lookup and test to see if a given memory block or IO byte is of a given type. Also macros to read/write to the memory block and IO byte. -#define MEMORY_BLOCK_GRANULARITY 0x800 -#define MEMORY_BLOCK_SLOTS (0x10000 / MEMORY_BLOCK_GRANULARITY) -#define MEMORY_BLOCK_MASK (0x10000 - MEMORY_BLOCK_GRANULARITY) -#define MEMORY_BLOCK_SHIFT 11 -#define getPageData(a) (Z80Ctrl->page[(a & 0xF800) >> MEMORY_BLOCK_SHIFT]) -#define getIOPageData(a) (Z80Ctrl->iopage[(a & 0xFFFF]) +// The memory page arrays dont check for allocation due to speed, it is assumed a memory mode page has been allocated and defined prior to the memoryMode +// variable being set to that page. +#define MEMORY_MODES 32 // Maximum number of different memory modes. +#define MEMORY_PAGE_SIZE 0x10000 // Total size of directly addressable memory. +#define MEMORY_BLOCK_GRANULARITY 0x1 // Any change update MEMORY_BLOCK_SHIFT and mask in MEMORY_BLOCK_MASK +#define MEMORY_BLOCK_SHIFT 0 +#define MEMORY_BLOCK_SLOTS (MEMORY_PAGE_SIZE / MEMORY_BLOCK_GRANULARITY) +#define MEMORY_BLOCK_MASK (MEMORY_PAGE_SIZE - MEMORY_BLOCK_GRANULARITY) +#define IO_PAGE_SIZE 0x10000 // Total size of directly addressable I/O. +#define IO_BLOCK_GRANULARITY 0x1 // Any change update MEMORY_BLOCK_SHIFT and mask in MEMORY_BLOCK_MASK +#define IO_BLOCK_SHIFT 0 +#define IO_BLOCK_SLOTS (IO_PAGE_SIZE / IO_BLOCK_GRANULARITY) +#define IO_BLOCK_MASK (IO_PAGE_SIZE - IO_BLOCK_GRANULARITY) +//#define getPageData(a) (Z80Ctrl->page[(a & 0xFFFF) >> MEMORY_BLOCK_SHIFT]) +//#define getIOPageData(a) (Z80Ctrl->iopage[(a & 0xFFFF]) +#define getPageData(a) (*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + ((a & MEMORY_BLOCK_MASK) >> MEMORY_BLOCK_SHIFT))) +#define getIOPageData(a) (Z80Ctrl->iopage[(a & IO_BLOCK_MASK]) + #define getPageType(a, mask) (getPageData(a) & mask) #define getPageAddr(a, mask) ((getPageData(a) & mask) + (a & (MEMORY_BLOCK_GRANULARITY-1))) #define getIOPageType(a, mask) (getIOPageData(a) & mask) #define getIOPageAddr(a, mask) (getIOPageData(a) & mask) -#define realAddress(a) (Z80Ctrl->page[getPageAddr(a, MEMORY_TYPE_REAL_MASK)]) -#define realPort(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_MASK) +//#define realAddress(a) (Z80Ctrl->page[getPageAddr(a, MEMORY_TYPE_REAL_MASK)]) +#define realAddress(a) (*(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (getPageAddr(a, MEMORY_TYPE_REAL_MASK)))) +#define realPort(a) (Z80Ctrl->iopage[a & IO_BLOCK_MASK] & IO_TYPE_MASK) #define isPhysicalRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_RAM)) #define isPhysicalVRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_VRAM)) #define isPhysicalROM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_ROM)) #define isPhysicalMemory(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM))]) #define isPhysicalHW(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_HW)) #define isPhysical(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM))) -#define isPhysicalIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_PHYSICAL_HW) -#define isVirtualRAM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_RAM)) +#define isPhysicalIO(a) (Z80Ctrl->iopage[a & IO_BLOCK_MASK] & IO_TYPE_PHYSICAL_HW) +#define isVirtualRAM(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_RAM | MEMORY_TYPE_VIRTUAL_RAM_RO))) +#define isVirtualRO(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_RAM_RO)) +#define isVirtualRW(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_RAM)) #define isVirtualROM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_ROM)) -#define isVirtualMemory(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_RAM))) +#define isVirtualMemory(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_RAM | MEMORY_TYPE_VIRTUAL_RAM_RO))) #define isVirtualHW(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_HW)) -#define isVirtualIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_VIRTUAL_HW) -#define isVirtualDevice(a, d) (Z80Ctrl->iopage[a & 0xFFFF] & d) +#define isVirtualIO(a) (Z80Ctrl->iopage[a & IO_BLOCK_MASK] & IO_TYPE_VIRTUAL_HW) +#define isVirtual(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_RAM | MEMORY_TYPE_VIRTUAL_RAM_RO | MEMORY_TYPE_VIRTUAL_HW))) +#define isVirtualDevice(a, d) (Z80Ctrl->iopage[a & IO_BLOCK_MASK] & d) #define isHW(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_VIRTUAL_HW))) #define readVirtualRAM(a) (Z80Ctrl->ram[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ]) #define readVirtualROM(a) (Z80Ctrl->rom[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ]) #define writeVirtualRAM(a, d) { Z80Ctrl->ram[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ] = d; } -#define setMemoryType(_block_,_type_,_addr_) { Z80Ctrl->page[_block_] = _type_ | _addr_; } -#define backupMemoryType(_block_) { Z80Ctrl->shadowPage[_block_] = Z80Ctrl->page[_block_]; } -#define restoreMemoryType(_block_) { Z80Ctrl->page[_block_] = Z80Ctrl->shadowPage[_block_]; } +#define writeVirtualROM(a, d) { Z80Ctrl->rom[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ] = d; } +//#define setMemoryType(_block_,_type_,_addr_) { Z80Ctrl->page[_block_] = _type_ | _addr_; } +#define setMemoryType(_block_,_type_,_addr_) { *(*(Z80Ctrl->page + Z80Ctrl->memoryMode) +_block_) = _type_ | _addr_; } +#define backupMemoryType(_block_) { Z80Ctrl->shadowPage[_block_] = *(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (_block_)); } +//#define restoreMemoryType(_block_) { Z80Ctrl->page[_block_] = Z80Ctrl->shadowPage[_block_]; } +#define restoreMemoryType(_block_) { *(*(Z80Ctrl->page + Z80Ctrl->memoryMode) + (_block_)) = Z80Ctrl->shadowPage[_block_]; } +#define sendSignal(_signal_) { struct siginfo sigInfo;\ + if(Z80Ctrl->ioTask != NULL)\ + {\ + memset(&sigInfo, 0, sizeof(struct siginfo));\ + sigInfo.si_signo = _signal_;\ + sigInfo.si_code = SI_QUEUE;\ + sigInfo.si_int = 1;\ + if(send_sig_info(_signal_, &sigInfo, Z80Ctrl->ioTask) < 0)\ + {\ + pr_info("Error: Failed to send Request to I/O Processor:%d, %s\n", _signal_, Z80Ctrl->ioTask->comm);\ + }\ + }\ + } +#define resetZ80() {\ + sendSignal(SIGUSR1); \ + setupMemory(Z80Ctrl->defaultPageMode);\ + z80_instant_reset(&Z80CPU);\ + } #define IO_ADDR_E0 0xE0 #define IO_ADDR_E1 0xE1 @@ -343,7 +430,7 @@ typedef struct { // Page pointer map. // // Each pointer points to a byte or block of bytes in the Z80 Memory frame, 64K Real + Banked. - // This is currently set at a block of size 0x800 per memory pointer for the MZ-700. + // This is currently set at a block of size 0x1 per memory pointer for the MZ-700. // The LSB of the pointer is a direct memory index to a byte or block of bytes, the upper byte of the pointer indicates type of memory space. // 0x80 - physical host RAM // 0x40 - physical host ROM @@ -354,8 +441,12 @@ typedef struct { // 0x02 - virtual host hardware // 16bit Input Address -> map -> Pointer to 24bit memory address + type flag. // -> Pointer+ to 24bit memory address + type flag. - uint32_t page[MEMORY_BLOCK_SLOTS]; - uint32_t shadowPage[MEMORY_BLOCK_SLOTS]; + //uint32_t page[MEMORY_BLOCK_SLOTS]; + uint32_t *page[MEMORY_MODES]; + uint32_t shadowPage[MEMORY_BLOCK_SLOTS]; // Shadow page is for manipulation and backup of an existing page. + + // Current memory mode as used by active driver. + uint8_t memoryMode; // I/O Page map. // @@ -380,13 +471,13 @@ typedef struct { uint8_t ioReadAhead; uint8_t ioWriteAhead; -#if(TARGET_HOST_MZ2000 == 1) + #if(TARGET_HOST_MZ2000 == 1) uint8_t lowMemorySwap; -#endif -#if(TARGET_HOST_MZ80A == 1) + #endif + #if(TARGET_HOST_MZ80A == 1) // MZ-80A can relocate the lower 4K ROM by swapping RAM at 0xC000. uint8_t memSwitch; -#endif + #endif // Keyboard strobe and data. Required to detect hotkey press. uint8_t keyportStrobe; @@ -400,36 +491,52 @@ typedef struct { // is quicker than RAM (both are in the same kernel memory) as a pointer calculation needs to be made. uint32_t cpuGovernorDelayROM; uint32_t cpuGovernorDelayRAM; + + // An I/O processor, running as a User Space daemon, can register to receive signals and events. + struct task_struct *ioTask; + + #if(DEBUG_ENABLED == 1) + // Debugging flag. + uint8_t debug; + #endif } t_Z80Ctrl; // IOCTL structure for passing data from user space to driver to perform commands. // struct z80_addr { - uint32_t start; - uint32_t end; - uint32_t size; + uint32_t start; + uint32_t end; + uint32_t size; }; struct z80_ctrl { - uint16_t pc; + uint16_t pc; }; struct speed { - uint32_t speedMultiplier; + uint32_t speedMultiplier; }; struct virtual_device { enum VIRTUAL_DEVICE device; }; struct cpld_ctrl { - uint32_t cmd; + uint32_t cmd; }; +#if(DEBUG_ENABLED == 1) +struct debug { + uint8_t level; +}; +#endif struct ioctlCmd { - int32_t cmd; - union { - struct z80_addr addr; - struct z80_ctrl z80; - struct speed speed; - struct virtual_device vdev; - struct cpld_ctrl cpld; - }; + int32_t cmd; + union { + struct z80_addr addr; + struct z80_ctrl z80; + struct speed speed; + struct virtual_device vdev; + struct cpld_ctrl cpld; + #if(DEBUG_ENABLED == 1) + struct debug debug; + #endif + }; }; // Prototypes. diff --git a/software/FusionX/src/driver/MZ80A/z80vhw_rfs.c b/software/FusionX/src/driver/MZ80A/z80vhw_rfs.c index cf5478b45..906cfd579 100644 --- a/software/FusionX/src/driver/MZ80A/z80vhw_rfs.c +++ b/software/FusionX/src/driver/MZ80A/z80vhw_rfs.c @@ -166,8 +166,6 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode) // is controlled by the REG_BANK1 (MROM) and REG_BANK2 (UROM) registers with the upper UROM registers provided in REG_CTRL. // So on initial setup, map the MROM page to point to the Z80 ROM area 0x00000 which is the base of the first 512K Flash ROM (virtual). - pr_info("RFS Memory Setup complete.\n"); - // Setup defaults. RFSCtrl.regBank1 = 0x00; RFSCtrl.regBank2 = 0x00; @@ -202,6 +200,7 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode) } // No I/O Ports on the RFS board. + pr_info("RFS Memory Setup complete.\n"); } // Perform any setup operations, such as variable initialisation, to enable use of this module. @@ -561,7 +560,7 @@ void rfsSDCard(void) } // Method to read from either the memory mapped registers if enabled else the ROM. -static inline uint8_t rfsRead(zuint16 address) +static inline uint8_t rfsRead(zuint16 address, uint8_t ioFlag) { // Locals. uint8_t data = 0xFF; @@ -570,14 +569,14 @@ static inline uint8_t rfsRead(zuint16 address) // Any access to the control region increments the enable counter till it reaches terminal count and enables writes to the registers. When the counter // gets to 15 the registers are enabled and the EPROM, in the control region, is disabled. - if(address >= BNKCTRLRST && RFSCtrl.upCntr < 15) + if((address >= BNKCTRLRST && address <= BNKCTRL) && RFSCtrl.upCntr < 15) { RFSCtrl.upCntr++; } // Address in control region and register bank enabled? // - if(RFSCtrl.upCntr >= 15 && address >= BNKCTRLRST) + if(RFSCtrl.upCntr >= 15 && (address >= BNKCTRLRST && address <= BNKCTRL)) { switch(address) { @@ -616,7 +615,7 @@ static inline uint8_t rfsRead(zuint16 address) } else { // Return the contents of the ROM at given address. - data = readVirtualROM(address); + data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); } //pr_info("RFS-Read:%04x, Data:%02x, BK1:%02x, BK2:%02x, CTRL:%02x, MROM:%08x, UROM:%08x\n", address, data, RFSCtrl.regBank1, RFSCtrl.regBank2, RFSCtrl.regCtrl, RFSCtrl.mromAddr, RFSCtrl.uromAddr); @@ -624,7 +623,7 @@ static inline uint8_t rfsRead(zuint16 address) } // Method to handle writes to the RFS board. Generally the RFS board. -static inline void rfsWrite(zuint16 address, zuint8 data) +static inline void rfsWrite(zuint16 address, zuint8 data, uint8_t ioFlag) { // Locals. uint32_t idx; @@ -633,14 +632,14 @@ static inline void rfsWrite(zuint16 address, zuint8 data) // Any access to the control region increments the enable counter till it reaches terminal count and enables writes to the registers. When the counter // gets to 15 the registers are enabled and the EPROM, in the control region, is disabled. - if(address >= BNKCTRLRST && RFSCtrl.upCntr < 15) + if((address >= BNKCTRLRST && address <= BNKCTRL) && RFSCtrl.upCntr < 15) { RFSCtrl.upCntr++; } // Address in control region and register bank enabled? // - if(RFSCtrl.upCntr >= 15 && address >= BNKCTRLRST) + if(RFSCtrl.upCntr >= 15 && (address >= BNKCTRLRST && address <= BNKCTRL)) { switch(address) { @@ -708,6 +707,10 @@ static inline void rfsWrite(zuint16 address, zuint8 data) } break; } + } else + { + // Any unprocessed write is commited to RAM. + writeVirtualRAM(address, data); } //pr_info("RFS-Write:%04x, Data:%02x, BK1:%02x, BK2:%02x, CTRL:%02x, MROM:%08x, UROM:%08x\n", address, data, RFSCtrl.regBank1, RFSCtrl.regBank2, RFSCtrl.regCtrl, RFSCtrl.mromAddr, RFSCtrl.uromAddr); return; diff --git a/software/FusionX/src/driver/MZ80A/z80vhw_tzpu.c b/software/FusionX/src/driver/MZ80A/z80vhw_tzpu.c index 3a3e2d348..cce2af527 100644 --- a/software/FusionX/src/driver/MZ80A/z80vhw_tzpu.c +++ b/software/FusionX/src/driver/MZ80A/z80vhw_tzpu.c @@ -6,14 +6,17 @@ // Description: Z80 Virtual Hardware Driver - tranZPUter SW // This file contains the methods used to emulate the first tranZPUter Software series // the tranZPUter SW which used a Cortex-M4 to enhance the host machine and provide -// an upgraded monitor, the tzfs (tranZPUter Filing System). +// an upgraded monitor, the tzfs (tranZPUter Filing System). As the original hardware +// use an independent processor to provide services to the Z80, this driver is made up +// of two parts, 1) the Z80, ie. this driver, 2) a user space daemon which fulfils the +// role of the independent processor. // // These drivers are intended to be instantiated inline to reduce overhead of a call // and as such, they are included like header files rather than C linked object files. // Credits: // Copyright: (c) 2019-2023 Philip Smart // -// History: Feb 2023 - Initial write based on the RFS hardware. +// History: Feb 2023 v1.0 - Initial write based on the tranZPUter SW hardware. // // Notes: See Makefile to enable/disable conditional components // @@ -53,19 +56,69 @@ #include #include +#include "tzpu.h" + +// TZPU Board registers. +typedef struct { + uint8_t clkSrc; // Source of clock, host - 0, secondary - 1 + uint8_t regCmd; // Internal command register of the CPLD. + uint8_t regCmdStatus; // Internal command status register of the CPLD. + uint8_t regCpuCfg; // Internal FPGA CPU config register. + uint8_t regCpuInfo; // Internal FPGA CPU information register. + uint8_t regCpldCfg; // Internal CPLD config register. + uint8_t regCpldInfo; // Internal CPLD information register. +} t_TZPUCtrl; + +// TZPU Board control. +static t_TZPUCtrl TZPUCtrl; + //------------------------------------------------------------------------------------------------------------------------------- // // //------------------------------------------------------------------------------------------------------------------------------- -// Method to setup the memory page config to reflect installation of a tranZPUter SW Board. -void tzpuSetupMemory(enum Z80_MEMORY_PROFILE mode) -{ -} +// Memory Modes: 0 - Default, normal Sharp MZ80A operating mode, all memory and IO (except tranZPUter control IO block) are on the mainboard +// 1 - As 0 except User ROM is mapped to tranZPUter RAM. +// 2 - TZFS, Monitor ROM 0000-0FFF, Main DRAM 0x1000-0xD000, User/Floppy ROM E800-FFFF are in tranZPUter memory. Two small holes of 2 bytes at F3FE and F7FE exist +// for the Floppy disk controller, the fdc uses the rom as a wait detection by toggling the ROM lines according to WAIT, the Z80 at 2MHz hasnt enough ooomph to read WAIT and action it. +// NB: Main DRAM will not be refreshed so cannot be used to store data in this mode. +// 3 - TZFS, Monitor ROM 0000-0FFF, Main RAM area 0x1000-0xD000, User ROM 0xE800-EFFF are in tranZPUter memory block 0, Floppy ROM F000-FFFF are in tranZPUter memory block 1. +// NB: Main DRAM will not be refreshed so cannot be used to store data in this mode. +// 4 - TZFS, Monitor ROM 0000-0FFF, Main RAM area 0x1000-0xD000, User ROM 0xE800-EFFF are in tranZPUter memory block 0, Floppy ROM F000-FFFF are in tranZPUter memory block 2. +// NB: Main DRAM will not be refreshed so cannot be used to store data in this mode. +// 5 - TZFS, Monitor ROM 0000-0FFF, Main RAM area 0x1000-0xD000, User ROM 0xE800-EFFF are in tranZPUter memory block 0, Floppy ROM F000-FFFF are in tranZPUter memory block 3. +// NB: Main DRAM will not be refreshed so cannot be used to store data in this mode. +// 6 - CPM, all memory on the tranZPUter board, 64K block 4 selected. +// Special case for F3FE:F3FF & F7FE:F7FF (floppy disk paging vectors) which resides on the mainboard. +// 7 - CPM, F000-FFFF are on the tranZPUter board in block 4, 0040-CFFF and E800-EFFF are in block 5 selected, mainboard for D000-DFFF (video), E000-E800 (Memory control) selected. +// Special case for 0000:00FF (interrupt vectors) which resides in block 4 and CPM vectors and two small holes of 2 bytes at F3FE and F7FE exist for the Floppy disk controller, the fdc +// uses the rom as a wait detection by toggling the ROM lines according to WAIT, the Z80 at 2MHz hasnt enough ooomph to read WAIT and action it. +// 8 - Monitor ROM (0000:0FFF) on mainboard, Main RAM (1000:CFFF) in tranZPUter bank 0 and video, memory mapped I/O, User/Floppy ROM on mainboard. +// NB: Main DRAM will not be refreshed so cannot be used to store data in this mode. +// 9 - Monitor ROM 0000-0FFF and Main DRAM 0x1000-0xD000, video and memory mapped I/O are on the host machine, User/Floppy ROM E800-FFFF are in tranZPUter memory. +// 10 - MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the mainboard. +// 11 - MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6. +// 12 - MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6. +// 13 - MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible. +// 14 - MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible. +// 15 - MZ800 Mode - Running on MZ800 hardware, configuration set according to MZ700/MZ800 mode. +// 16 - MZ2000 Mode - Running on MZ2000 hardware, configuration set according to the MZ2000 runtime configuration registers. +// 21 - Access the FPGA memory by passing through the full 24bit Z80 address, typically from the K64F. +// 22 - Access to the host mainboard 64K address space only. +// 23 - Access all memory and IO on the tranZPUter board with the K64F addressing the full 512K-1MB RAM. +// 24 - All memory and IO are on the tranZPUter board, 64K block 0 selected. +// 25 - All memory and IO are on the tranZPUter board, 64K block 1 selected. +// 26 - All memory and IO are on the tranZPUter board, 64K block 2 selected. +// 27 - All memory and IO are on the tranZPUter board, 64K block 3 selected. +// 28 - All memory and IO are on the tranZPUter board, 64K block 4 selected. +// 29 - All memory and IO are on the tranZPUter board, 64K block 5 selected. +// 30 - All memory and IO are on the tranZPUter board, 64K block 6 selected. +// 31 - All memory and IO are on the tranZPUter board, 64K block 7 selected. // Perform any setup operations, such as variable initialisation, to enable use of this module. void tzpuInit(void) { + pr_info("Enabling TZPU driver.\n"); } // Method to decode an address and make any system memory map changes as required. @@ -73,19 +126,678 @@ void tzpuInit(void) static inline void tzpuDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag) { // Locals. + + // I/O or Memory? + if(ioFlag == 0) + { + // Memory map switch. + if(readFlag == 0) + { + + } else + { + + } + } else + // I/O Decoding. + { + // Only lower 8 bits recognised in the tzpu. + switch(address & 0xFF) + { + default: + break; + + } + } } // Method to read from the tranZPUter SW memory or I/O ports. -static inline uint8_t tzpuRead(zuint16 address) +static inline uint8_t tzpuRead(zuint16 address, uint8_t ioFlag) { // Locals. uint8_t data = 0x00; + // The tranZPUter board, in order to autoboot and use valuable space for variables, allows writing into the User ROM + // space above 0xEBFF. 0xE800 is used to detect if a ROM is installed and cannot be written. + if(ioFlag == 0 && isVirtualRAM(address)) + { + // Retrieve data from virtual memory. + data = readVirtualRAM(address); + } else + if(ioFlag) + { + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("Read IO:%02x\n", address); + #endif + + // Only the lower 8 bits of the I/O address are processed as the upper byte is not used in the Sharp models. + // + switch(address & 0x00FF) + { + case IO_TZ_CTRLLATCH: + data = Z80Ctrl->memoryMode; + break; + + case IO_TZ_SETXMHZ: + break; + case IO_TZ_SET2MHZ: + break; + + case IO_TZ_CLKSELRD: + data = TZPUCtrl.clkSrc; + break; + + case IO_TZ_SVCREQ: + break; + + case IO_TZ_SYSREQ: + break; + + case IO_TZ_CPLDSTATUS: + data = TZPUCtrl.regCmdStatus; + break; + + case IO_TZ_CPUSTATUS: + data = TZPUCtrl.regCpuCfg; + break; + + case IO_TZ_CPUINFO: + data = TZPUCtrl.regCpuInfo; + break; + + case IO_TZ_CPLDCFG: + data = TZPUCtrl.regCpldCfg; + break; + + case IO_TZ_CPLDINFO: + data = TZPUCtrl.regCpldInfo; + break; + + default: + break; + } + } + return(data); } // Method to write to the tranZPUter SW memory or I/O ports. -static inline void tzpuWrite(zuint16 address, zuint8 data) +static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag) { + // Locals + uint32_t idx; + + // The tranZPUter board, in order to autoboot and use valuable space for variables, allows writing into the User ROM + // space above 0xE800. 0xE800 is used to detect if a ROM is installed and cannot be written. + if(ioFlag == 0) + { + // Virtual ROM in range 0xEC00:0xEFFF is Read/Write. + if(isVirtualRW(address)) + { + //if(address >= 0xE800) pr_info("Write:%04x,%02x\n", address, data); + writeVirtualRAM(address, data); + } + } else + if(ioFlag) + { + // Only the lower 8 bits of the I/O address are processed as the upper byte is not used in the Sharp models. + // + switch(address & 0x00FF) + { + case IO_TZ_CTRLLATCH: + //pr_info("CTRLLATCH:%02x\n", data); + + // Check to see if the memory mode page has been allocated for requested mode, if it hasnt, we need to allocate and then define. + Z80Ctrl->memoryMode = (data & (MEMORY_MODES - 1)); + if(Z80Ctrl->page[Z80Ctrl->memoryMode] == NULL) + { + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("Allocating memory page:%d\n", Z80Ctrl->memoryMode); + #endif + (Z80Ctrl->page[Z80Ctrl->memoryMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL); + if ((Z80Ctrl->page[Z80Ctrl->memoryMode]) == NULL) + { + pr_info("z80drv: failed to allocate memory mapping page:%d memory!", Z80Ctrl->memoryMode); + Z80Ctrl->page[Z80Ctrl->memoryMode] = Z80Ctrl->page[0]; + } + + // A lot of the memory maps below are identical, minor changes such as RAM bank. This is a direct conversion of the VHDL code from the CPLD. + // + for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) + { + switch(Z80Ctrl->memoryMode) + { + // Original Sharp MZ80A mode, no tranZPUter features are selected except the I/O control registers (default: 0x60-063). + case TZMM_ORIG: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE800 && idx < 0xEC00) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xEC00 && idx < 0xF000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xF000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + break; + + // Original mode but E800-EFFF is mapped to tranZPUter RAM so TZFS can be booted. + case TZMM_BOOT: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE800 && idx < 0xEC00) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xEC00 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + break; + + // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-FFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected. + case TZMM_TZFS: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx == 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE801 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + break; + + // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 1. + case TZMM_TZFS2: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE800 && idx < 0xEC00) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xEC00 && idx < 0xF000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xF000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK1_ADDR+idx)); + } + break; + + // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 2. + case TZMM_TZFS3: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx == 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE801 && idx < 0xF000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xF000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK2_ADDR+idx)); + } + break; + + // TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 3. + case TZMM_TZFS4: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx == 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE801 && idx < 0xF000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xF000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK3_ADDR+idx)); + } + break; + + // CPM main memory configuration, all memory on the tranZPUter board, 64K block 4 selected. Special case for F3C0:F3FF & F7C0:F7FF (floppy disk paging vectors) which resides on the mainboard. + case TZMM_CPM: + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK4_ADDR+idx)); + break; + + // CPM main memory configuration, F000-FFFF are on the tranZPUter board in block 4, 0040-CFFF and E800-EFFF are in block 5, mainboard for D000-DFFF (video), E000-E800 (Memory control) selected. + // Special case for 0000:003F (interrupt vectors) which resides in block 4, F3C0:F3FF & F7C0:F7FF (floppy disk paging vectors) which resides on the mainboard. + case TZMM_CPM2: + if(idx >= 0 && idx < 0x40) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK4_ADDR+idx)); + } + else if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK5_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK5_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE800 && idx < 0xEC00) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK5_ADDR+idx)); + } + else if(idx >= 0xEC00 && idx < 0xF000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK5_ADDR+idx)); + } + else if(idx >= 0xF000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK4_ADDR+idx)); + } + break; + + // Original mode but with main DRAM in Bank 0 to allow bootstrapping of programs from other machines such as the MZ700. + case TZMM_COMPAT: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE800 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + break; + + // Mode to allow code running in Bank 0, address E800:FFFF to access host memory. Monitor ROM 0000-0FFF and Main DRAM 0x1000-0xD000, video and memory mapped I/O are on the host machine, User/Floppy ROM E800-FFFF are in tranZPUter memory. + case TZMM_HOSTACCESS: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE800 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + break; + + // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the mainboard. + case TZMM_MZ700_0: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK6_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0xE000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE000 && idx < 0xE800) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xE800 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM_RO, (SRAM_BANK0_ADDR+idx)); + } + break; + + // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6. + case TZMM_MZ700_1: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK6_ADDR+idx)); + } + break; + + // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6. + case TZMM_MZ700_2: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK6_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK6_ADDR+idx)); + } + break; + + // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible. + case TZMM_MZ700_3: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, (SRAM_BANK0_ADDR+idx)); + } + break; + + // MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible. + case TZMM_MZ700_4: + if(idx >= 0 && idx < 0x1000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK6_ADDR+idx)); + } + else if(idx >= 0x1000 && idx < 0xD000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (SRAM_BANK0_ADDR+idx)); + } + else if(idx >= 0xD000 && idx < 0x10000) + { + setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, (SRAM_BANK0_ADDR+idx)); + } + break; + + // MZ800 Mode - Host is an MZ-800 and mode provides for MZ-700/MZ-800 decoding per original machine. + case TZMM_MZ800: + + // MZ2000 Mode - Running on MZ2000 hardware, configuration set according to runtime configuration registers. + case TZMM_MZ2000: + + // Open up access for the K64F to the FPGA resources such as memory. All other access to RAM or mainboard is blocked. + case TZMM_FPGA: + + // Everything is on mainboard, no access to tranZPUter memory. + case TZMM_TZPUM: + + // Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory. K64F drives A18-A16 allowing full access to RAM. + case TZMM_TZPU: + + default: + pr_info("Memory Mode(%d) not available.\n", data); + break; + } + } + } + // Memory map now created/switched. + break; + + case IO_TZ_SETXMHZ: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("SETXMHZ:%02x\n", data); + #endif + TZPUCtrl.clkSrc = 1; + break; + + case IO_TZ_SET2MHZ: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("SET2MHZ:%02x\n", data); + #endif + TZPUCtrl.clkSrc = 0; + break; + + case IO_TZ_CLKSELRD: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("CKSELRD:%02x\n", data); + #endif + break; + + case IO_TZ_SVCREQ: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("SVCREQ:%02x\n", data); + #endif + + // If a k64f process has registered, send it a service request signal. + sendSignal(SIGIO); + + // A strange race state exists with CP/M and interrupts during disk requests. If no delay is + // given for read/write requests, the interrupt line will eventually lockup active and the Z80 + // enters an interrupt loop. Strange because the Z80 interrupt line is a buffered signal from the + // host motherboard to the SOM via CPLD without latches. The 8253 timer generates the interrupt + // but the ISR resets it. My guess is memory corruption due to a race state, more time is needed + // debugging but in the meantime, a 2ms delay ensures the read/write completes prior to next + // request. + if(Z80Ctrl->ram[0x4f560] == 0x32 || Z80Ctrl->ram[0x4f560] == 0x33) + { + udelay(2000); + } + break; + + case IO_TZ_SYSREQ: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("SYSREQ:%02x\n", data); + #endif + break; + + case IO_TZ_CPLDCMD: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("CPLDCMD:%02x\n", data); + #endif + TZPUCtrl.regCmd = data; + break; + + case IO_TZ_CPUINFO: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("CPUINFO:%02x\n", data); + #endif + break; + + case IO_TZ_CPLDCFG: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("CPLDCFG:%02x\n", data); + #endif + break; + + case IO_TZ_CPLDINFO: + #if(DEBUG_ENABLED & 0x01) + if(Z80Ctrl->debug & 0x01) pr_info("CPLDINFO:%02x\n", data); + #endif + break; + + default: + pr_info("PORT:%02x\n", data); + break; + } + } return; } + +// Method to setup the memory page config to reflect installation of a tranZPUter SW Board. This sets up the default +// as the memory map changes according to selection and handled in-situ. +void tzpuSetupMemory(enum Z80_MEMORY_PROFILE mode) +{ + // Locals. + uint32_t idx; + + // The tranZPUter SW uses a CPLD to set a 4K Z80 memory range window into a 512K-1MB linear RAM block. The actual map required + // at any one time is governed by the Memory Config register at I/O port 0x60. + // This method sets the initial state, which is a normal Sharp operating mode, all memory and IO (except tranZPUter + // control IO block) are on the mainboard. + + // Setup defaults. + TZPUCtrl.clkSrc = 0x00; // Clock defaults to host. + TZPUCtrl.regCmd = 0x00; // Default for the CPLD Command. + TZPUCtrl.regCmdStatus = 0x00; // Default for the CPLD Command Status. + TZPUCtrl.regCpuCfg = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed. + TZPUCtrl.regCpuInfo = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed. + // Setup the CPLD status value, this is used by the host for configuration of tzfs. + #if(TARGET_HOST_MZ80A == 1) + TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ80A; + #endif + #if(TARGET_HOST_MZ700 == 1) + TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ700; + #endif + #if(TARGET_HOST_MZ2000 == 1) + TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ2000; + #endif + TZPUCtrl.regCpldCfg = 0x00; // Not used, as no CPLD available, but need to store/return value if addressed. + + // Go through and clear all memory maps, valid for startup and reset. + for(idx=0; idx < MEMORY_MODES; idx++) + { + if(Z80Ctrl->page[idx] != NULL) + { + kfree(Z80Ctrl->page[idx]); + Z80Ctrl->page[idx] = NULL; + } + } + + // Setup all initial TZFS memory modes + tzpuWrite(IO_TZ_CTRLLATCH, TZMM_ORIG, 1); + tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS, 1); + tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS2, 1); + tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS3, 1); + tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS4, 1); + Z80Ctrl->memoryMode = 0x02; // Default memory mode, MZ-80A. + + // I/O Ports on the tranZPUter SW board. All hosts have the same ports for the tzpu board. + for(idx=0x0000; idx < 0x10000; idx+=0x0100) + { + Z80Ctrl->iopage[idx+IO_TZ_CTRLLATCH] = IO_TZ_CTRLLATCH | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_SETXMHZ] = IO_TZ_SETXMHZ | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_SET2MHZ] = IO_TZ_SET2MHZ | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CLKSELRD] = IO_TZ_CLKSELRD | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_SVCREQ] = IO_TZ_SVCREQ | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_SYSREQ] = IO_TZ_SYSREQ | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CPLDCMD] = IO_TZ_CPLDCMD | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CPLDSTATUS] = IO_TZ_CPLDSTATUS | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CPUCFG] = IO_TZ_CPUCFG | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CPUSTATUS] = IO_TZ_CPUSTATUS | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CPUINFO] = IO_TZ_CPUINFO | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CPLDCFG] = IO_TZ_CPLDCFG | IO_TYPE_VIRTUAL_HW; + Z80Ctrl->iopage[idx+IO_TZ_CPLDINFO] = IO_TZ_CPLDINFO | IO_TYPE_VIRTUAL_HW; + } + + pr_info("TZPU Memory Setup complete.\n"); +} diff --git a/software/FusionX/src/driver/Makefile b/software/FusionX/src/driver/Makefile index 2a2cfc20c..2d3029d35 100644 --- a/software/FusionX/src/driver/Makefile +++ b/software/FusionX/src/driver/Makefile @@ -4,7 +4,7 @@ MODEL := MZ80A KERNEL := $(PWD)/../../../linux/kernel FUSIONX := $(PWD)/../.. CROSS := arm-linux-gnueabihf- -ccflags-y += -O2 -I${src}/Zeta/API -I${src}/Z80/API -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m +ccflags-y += -O2 -I${src}/Zeta/API -I${src}/Z80/API -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__ CTRLINC += -IZeta/API -IZ80/API obj-m += z80drv.o @@ -16,9 +16,14 @@ z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables all: - @echo "Build driver for host: $(MODEL)" @echo "" + @echo "Build K64F Daemon for host: $(MODEL)" + $(CROSS)gcc $(CTRLINC) $(MODEL)/k64fcpu.c -o k64fcpu + @echo "" + @echo "Build driver for host: $(MODEL)" make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules + @echo "" + @echo "Build z80ctrl tool for host: $(MODEL)" $(CROSS)gcc $(CTRLINC) $(MODEL)/z80ctrl.c -o z80ctrl install: diff --git a/software/linux/buildroot/.config b/software/linux/buildroot/.config index 8f2dd3a39..0d8a1f1bc 100644 --- a/software/linux/buildroot/.config +++ b/software/linux/buildroot/.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Buildroot 2020.05-g1e61fe54a-dirty Configuration +# Buildroot 2020.05-00004-g652bdd81e-dirty Configuration # BR2_HAVE_DOT_CONFIG=y BR2_HOST_GCC_AT_LEAST_4_9=y @@ -2033,7 +2033,7 @@ BR2_PACKAGE_NCURSES_ADDITIONAL_TERMINFO="" # BR2_PACKAGE_ONIGURUMA is not set # BR2_PACKAGE_PCRE is not set # BR2_PACKAGE_PCRE2 is not set -# BR2_PACKAGE_POPT is not set +BR2_PACKAGE_POPT=y # BR2_PACKAGE_READLINE is not set # BR2_PACKAGE_SLANG is not set # BR2_PACKAGE_TCLAP is not set @@ -2295,7 +2295,7 @@ BR2_PACKAGE_PPPD_OVERWRITE_RESOLV_CONF=y # BR2_PACKAGE_RP_PPPOE is not set # BR2_PACKAGE_RPCBIND is not set # BR2_PACKAGE_RSH_REDONE is not set -# BR2_PACKAGE_RSYNC is not set +BR2_PACKAGE_RSYNC=y # BR2_PACKAGE_RTORRENT is not set # BR2_PACKAGE_RTPTOOLS is not set # BR2_PACKAGE_RYGEL is not set