diff --git a/software/READ.ME b/software/READ.ME new file mode 100644 index 0000000..b1c2d9d --- /dev/null +++ b/software/READ.ME @@ -0,0 +1,2 @@ +Please see the zSoft repository for the main software components of the tranZPUter/tranZPUterSW. This tree contains software tools to aid +in the development and configuration of the tranZPUter only. diff --git a/software/src/tools/Makefile b/software/src/tools/Makefile new file mode 100644 index 0000000..61804b6 --- /dev/null +++ b/software/src/tools/Makefile @@ -0,0 +1,121 @@ +######################################################################################################### +## +## Name: Makefile +## Created: May 2020 +## Author(s): Philip Smart +## Description: Helper tools for the MZ80A tranZPUter / tranZPUterSW upgrades. +## This makefile builds tools written in C which help with building/setting up the +## tranZPUter/tranZPUterSW and configuration images. +## +## Credits: +## Copyright: (c) 2020 Philip Smart +## +## History: May 2020 - Initial Makefile creation +## +## Notes: +## +######################################################################################################### +## 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 . +######################################################################################################### +BASE = +CC = $(BASE)gcc +LD = $(BASE)gcc +AS = $(BASE)as +CP = $(BASE)objcopy +DUMP = $(BASE)objdump + +BASEDIR = ../../.. +SWDIR = $(BASEDIR)/software/src +INSTALLDIR = $(BASEDIR)/software/tools + +# we use printf from here +COMMON_DIR = $(SWDIR)/common +INCLUDE_DIR = $(SWDIR)/include + +# Working directory to build object files. +BUILD_DIR = tools_obj + +COMMON_SRC = +COMMON_OBJ = $(patsubst $(COMMON_DIR)/%.c,$(BUILD_DIR)/%.o,$(COMMON_SRC)) + +FLASHMMCFG_PRJ = flashmmcfg +FLASHMMCFG_SRC = flashmmcfg.c +FLASHMMCFG_OBJ = $(COMMON_OBJ) $(patsubst %.c,$(BUILD_DIR)/%.o,$(FLASHMMCFG_SRC)) + +# Commandline options for each tool. +OPTS = + +CFLAGS = -I. -I$(COMMON_DIR) -I$(INCLUDE_DIR) -O3 +# Enable debug output. +OFLAGS += -DDEBUG +LFLAGS = -Wl,--gc-sections -Wl,--relax -Os +# +# Assembler flags. +ASFLAGS = -I. -I$(COMMON_DIR) -I$(INCLUDE_DIR) -I$(STARTUP_DIR) +# + +# Our target. +all: clean $(BUILD_DIR) $(FLASHMMCFG_PRJ) + +install: all + cp $(FLASHMMCFG_PRJ) $(INSTALLDIR) + +clean: + rm -f $(BUILD_DIR)/*.o *.hex *.lss *.elf *.map *.lst *.srec *~ */*.o *.bin *.srec *.dmp *.vhd *.rpt $(FLASHMMCFG_PRJ) + +$(FLASHMMCFG_PRJ): $(FLASHMMCFG_PRJ).elf $(FLASHMMCFG_PRJ).dmp $(FLASHMMCFG_PRJ).lss + +# Convert ELF binary to bin file. +%.bin: %.elf + @$(CP) -O binary $< $@ + +# Convert ELF to srec format for serial upload. +%.srec: %.elf + @$(CP) -O srec $< $@ + +%.dmp: %.elf + @$(DUMP) -x $< >>$@ + +# Create extended listing file from ELF output file. +# testing: option -C +%.lss: %.elf + @echo + @$(DUMP) -h -S -C $< > $@ + +$(FLASHMMCFG_PRJ): $(FLASHMMCFG_OBJ) + $(CC) $(LFLAGS) $(FLASHMMCFG_OBJ) -o $@ $(LIBS) + chmod +x $@ + +# Link - this produces an ELF binary. +$(FLASHMMCFG_PRJ).elf: $(FLASHMMCFG_OBJ) + $(LD) $(LFLAGS) -o $@ $+ $(LIBS) + +$(BUILD_DIR)/%.o: %.c Makefile + $(CC) $(CFLAGS) $(OFLAGS) -o $@ -c $< + +$(BUILD_DIR)/%.o: %.cpp Makefile + $(CC) $(CFLAGS) $(OFLAGS) -o $@ -c $< + +$(BUILD_DIR)/%.o: $(COMMON_DIR)/%.c Makefile + $(CC) $(CFLAGS) $(OFLAGS) -o $@ -c $< + +$(BUILD_DIR)/%.o: %.s + $(AS) $(ASFLAGS) -o $@ $< + +$(BUILD_DIR)/%.o: $(STARTUP_DIR)/%.s + $(AS) $(ASFLAGS) -o $@ $< + +$(BUILD_DIR): + mkdir $(BUILD_DIR) + diff --git a/software/src/tools/flashmmcfg.c b/software/src/tools/flashmmcfg.c new file mode 100644 index 0000000..5571b67 --- /dev/null +++ b/software/src/tools/flashmmcfg.c @@ -0,0 +1,501 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: flashmmcfg.c +// Created: May 2020 +// Author(s): Philip Smart +// Description: tranZPUter SW Memory Map Configuration Tool +// This program creates the 512KB array which forms the tranZPUterSW memory decoder. +// The 512KB Flash has 19 inputs and 8 outputs, the outputs selecting or enabling +// a function on the tranZPUter SW design. +// +// Inputs: +// A0 - Z80_MEM0 = MEM[4:0] for the latched configuration selection. +// A1 - Z80_MEM1 Based on these bits the decoder operates in a +// A2 - Z80_MEM2 differing manner. Basically it will allow areas +// A3 - Z80_MEM3 of the Z80 Memory to use the onboard 512K Static RAM +// A4 - Z80_MEM4 at a 64Byte granularity or the Sharp MZ80A mainboard. +// A5 - Z80_WR = Z80 Write Signal +// A6 - Z80_RD = Z80 Read Signal +// A7 - Z80_IORQ = Z80 IORQ Signal, an IO operation is taking place. +// A8 - Z80_MREQ = Z80 MREQ Signal, a memory operation is taking place. +// A9 - Z80_A6 = A[15:6] Z80 address lines of IO or Memory target. +// A10 - Z80_A7 +// A11 - Z80_A8 +// A12 - Z80_A9 +// A13 - Z80_A10 +// A14 - Z80_A11 +// A15 - Z80_A12 +// A16 - Z80_A13 +// A17 - Z80_A14 +// A18 - Z80_A15 +// +// 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 Floppy ROM and User ROM are mapped to tranZPUter RAM. +// 31 - tranZPUter, all memory and IO are exclusively to the devices on the tranZPUter board, no mainboard access is made. +// +// +// Credits: +// Copyright: (c) 2020 Philip Smart +// +// History: May 2020 - Initial program written. +// +// Notes: +// +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "1.0" +#define FLASHRAMBITS 19 +#define FLASHRAMSIZE 1 << FLASHRAMBITS +#define CFGBITS 5 +#define CFGSETS (1 << CFGBITS) +#define TRANCHESIZE (FLASHRAMSIZE) / (CFGSETS) + +#define CFG_IO_CONTROL_ADDR 0x60 + +typedef struct __attribute__((__packed__)) { + uint8_t DISABLE_BUS : 1; + uint8_t ENABLE_BUS : 1; + uint8_t reserved2 : 1; + uint8_t reserved3 : 1; + uint8_t reserved4 : 1; + uint8_t IODECODE : 1; + uint8_t RAM_WE : 1; + uint8_t RAM_OE : 1; +} t_map_output; + +// Structure to represent one of the 32 possible memory map configurations as set by the MEM[4:0] latch. +// +typedef struct __attribute__((__packed__)) { + t_map_output tranche[TRANCHESIZE]; +} t_memmap_tranche; + +static t_memmap_tranche flashRAM[CFGSETS]; +static int verbose_flag = 0; +static uint32_t ioAddr = CFG_IO_CONTROL_ADDR; + + +// Simple help screen to remmber how this utility works!! +// +void usage(void) +{ + printf("FLASHMMCFG v%s\n", VERSION); + printf("\nOptions:-\n"); + printf(" -h | --help This help test.\n"); + printf(" -i | --io-addr Base address for the IO Control Registers.\n"); + printf(" -o | --output Output the final binary image to the given file. This file is programmed into the Flash RAM.\n"); + printf(" -v | --verbose Output more messages.\n"); + + printf("\nExamples:\n"); + printf(" flashmmcfg --output Decode1.bin --io-addr 0x20 Create the mapping binary using 0x20 as the base address for the IO Control Registers.\n"); + +} + +// Method to convert a little endian <-> big endian 32bit unsigned. +// +uint32_t swap_endian(uint32_t value) +{ + uint32_t b[4]; + b[0] = ((value & 0x000000ff) << 24u); + b[1] = ((value & 0x0000ff00) << 8u); + b[2] = ((value & 0x00ff0000) >> 8u); + b[3] = ((value & 0xff000000) >> 24u); + + return(b[0] | b[1] | b[2] | b[3]); +} + +// Method to initialise the structures which represent the output binary image for uploading into the decoder Flash RAM. +// There are a fixed number of 'sets' of configurations which are selected by the MEM[4:0] latch bits, each set gives a +// different decoding action so that different memory maps and IO maps can be realised by the Z80 dependent upon its task +// or machine it is emulating. +// +void initMap(void) +{ + for(uint8_t idx=0; idx < CFGSETS; idx++) + { + for(uint32_t idx2=0; idx2 < TRANCHESIZE; idx2++) + { + flashRAM[idx].tranche[idx2].DISABLE_BUS = 1; + flashRAM[idx].tranche[idx2].ENABLE_BUS = 1; + flashRAM[idx].tranche[idx2].reserved2 = 1; + flashRAM[idx].tranche[idx2].reserved3 = 1; + flashRAM[idx].tranche[idx2].reserved4 = 1; + flashRAM[idx].tranche[idx2].IODECODE = 1; + flashRAM[idx].tranche[idx2].RAM_WE = 1; + flashRAM[idx].tranche[idx2].RAM_OE = 1; + } + } + return; +} + +// This method takes the internal array, organised as sets and tranches and manipulates them to fit the actual hardware +// definition. As the configuration bits MEM[4:0] operate the lowest address select bits of the Flash RAM the output needs +// to be sliced into CFGSETS where byte 0 = byte 0 of config set 0 ..... byte n = byte n of config set n, n = 31 for the +// current hardware design. This slice is repeated for all TRANCHESIZE bytes in each tranche. +// +void outputMap(FILE *fp) +{ + // Locals. + uint32_t idx; + uint8_t idx2; + uint8_t outbuf[CFGSETS]; + + // As the configuration bits MEM[4:0] operate the lowest address select bits of the Flash RAM the output needs to be + // + // + for(idx=0; idx < TRANCHESIZE; idx++) + { + for(idx2=0; idx2 < CFGSETS; idx2++) + { + outbuf[idx2] = flashRAM[idx2].tranche[idx].RAM_OE << 7 | + flashRAM[idx2].tranche[idx].RAM_WE << 6 | + flashRAM[idx2].tranche[idx].IODECODE << 5 | + flashRAM[idx2].tranche[idx].reserved4 << 4 | + flashRAM[idx2].tranche[idx].reserved3 << 3 | + flashRAM[idx2].tranche[idx].reserved2 << 2 | + flashRAM[idx2].tranche[idx].ENABLE_BUS << 1 | + flashRAM[idx2].tranche[idx].DISABLE_BUS; + } + if(fwrite(outbuf, 1, CFGSETS, fp) != CFGSETS) + { + printf("Write Error: Failed to write %d bytes of set:tranche %u:%u\n", CFGSETS, idx2, idx); + } + } + return; +} + +// This method looks at the input signals for a given set and updates the output bits accordingly. +// +void setMap(uint8_t set, uint32_t inSignals) +{ + // Decode the input signals into there components. + uint8_t Z80_WR = (inSignals & 0b00000000000001); + uint8_t Z80_RD = (inSignals & 0b00000000000010) >> 1; + uint8_t Z80_IORQ = (inSignals & 0b00000000000100) >> 2; + uint8_t Z80_MREQ = (inSignals & 0b00000000001000) >> 3; + uint8_t Z80_A6 = (inSignals & 0b00000000010000) >> 4; + uint8_t Z80_A7 = (inSignals & 0b00000000100000) >> 5; + uint8_t Z80_A8 = (inSignals & 0b00000001000000) >> 6; + uint8_t Z80_A9 = (inSignals & 0b00000010000000) >> 7; + uint8_t Z80_A10 = (inSignals & 0b00000100000000) >> 8; + uint8_t Z80_A11 = (inSignals & 0b00001000000000) >> 9; + uint8_t Z80_A12 = (inSignals & 0b00010000000000) >> 10; + uint8_t Z80_A13 = (inSignals & 0b00100000000000) >> 11; + uint8_t Z80_A14 = (inSignals & 0b01000000000000) >> 12; + uint8_t Z80_A15 = (inSignals & 0b10000000000000) >> 13; + uint32_t Z80_ADDR = (inSignals & 0b11111111110000) << 2; // 16 bit memory address. + uint32_t Z80_IO_ADDR = ((inSignals & 0b00000000110000) << 2) | 0b00100000; // 8 bit IO address, bit 5 is hardwired to 1, bit 4 is hardwired to 0. + uint8_t Z80_MEM_WRITE = (Z80_WR == 0 && Z80_MREQ == 0 && Z80_RD == 1 && Z80_IORQ == 1) ? 1 : 0; + uint8_t Z80_MEM_READ = (Z80_RD == 0 && Z80_MREQ == 0 && Z80_WR == 1 && Z80_IORQ == 1) ? 1 : 0; + uint8_t Z80_IO_WRITE = (Z80_WR == 0 && Z80_IORQ == 0 && Z80_RD == 1 && Z80_MREQ == 1) ? 1 : 0; + uint8_t Z80_IO_READ = (Z80_RD == 0 && Z80_IORQ == 0 && Z80_WR == 1 && Z80_MREQ == 1) ? 1 : 0; + + //printf("Signals(%u): Z80_WR=%u, Z80_RD=%u, Z80_IORQ=%u, Z80_MREQ=%u, Z80_ADDR=%u, Z80_IO_ADDR=%u\n", inSignals, Z80_WR, Z80_RD, Z80_IORQ, Z80_MREQ, Z80_ADDR, Z80_IO_ADDR); + //printf("MEMWR=%u, MEMRD=%u, IOWR=%u, IORD=%u\n", Z80_MEM_WRITE, Z80_MEM_READ, Z80_IO_WRITE, Z80_IO_WRITE); + + // Defaults for IO operations, can be overriden for a specific set but should be present in all other sets. + // + if(Z80_IO_WRITE || Z80_IO_READ) + { + // If the address is within configured IO control register range, activate the IODECODE signal. + if(Z80_IO_ADDR == ioAddr) + { + flashRAM[set].tranche[inSignals].DISABLE_BUS = 0; + flashRAM[set].tranche[inSignals].IODECODE = 0; + } + } + + // If the non-standard case of Z80 RD and Z80 WR being set low occurs, enable the ENABLE_BUS signal as the K64F is requesting access to the MZ80A motherboard. + // This signal is not set dependent, it can occur at any time and with any set. + // + if(Z80_RD == 0 && Z80_WR == 0 && Z80_MREQ == 1 && Z80_IORQ == 1) + { + flashRAM[set].tranche[inSignals].DISABLE_BUS = 1; + flashRAM[set].tranche[inSignals].ENABLE_BUS = 0; + flashRAM[set].tranche[inSignals].RAM_OE = 1; + flashRAM[set].tranche[inSignals].RAM_WE = 1; + flashRAM[set].tranche[inSignals].IODECODE = 1; + } + + // Specific mapping for Memory Writes. + if(Z80_MEM_WRITE) + { + switch(set) + { + // Set 0 - default, the Z80 uses the motherboard so no special signal activation is needed. + case 0: + flashRAM[set].tranche[inSignals].ENABLE_BUS = 0; + flashRAM[set].tranche[inSignals].DISABLE_BUS = 1; + flashRAM[set].tranche[inSignals].RAM_OE = 1; + flashRAM[set].tranche[inSignals].RAM_WE = 1; + break; + + // Set 1 - A standard MZ80A.the tranZPUter maps in RAM to the User and Floppy drive slots but otherwise all standard. + case 1: + // Place a bank of RAM into the floppy disk bios area. + if( (Z80_ADDR >= 0xF000 && Z80_ADDR < 0x10000) //|| + // Place RAM into the User ROM socket. + // (Z80_ADDR >= 0xE800 && Z80_ADDR < 0xF000) + ) + { + flashRAM[set].tranche[inSignals].DISABLE_BUS = 0; + flashRAM[set].tranche[inSignals].RAM_WE = 0; + } else + { + flashRAM[set].tranche[inSignals].ENABLE_BUS = 0; + flashRAM[set].tranche[inSignals].RAM_OE = 1; + } + break; + + // Set 31 - All memory and IO are on the tranZPUter board. + case 31: + flashRAM[set].tranche[inSignals].DISABLE_BUS = 0; + flashRAM[set].tranche[inSignals].RAM_WE = 0; + break; + + // For default, do nothing. + default: + break; + } + } + + // Specific mapping for Memory Reads. + else if(Z80_MEM_READ) + { + switch(set) + { + // Set 0 - default, the Z80 uses the motherboard so no signal activation is needed. + case 0: + flashRAM[set].tranche[inSignals].ENABLE_BUS = 0; + flashRAM[set].tranche[inSignals].DISABLE_BUS = 1; + flashRAM[set].tranche[inSignals].RAM_OE = 1; + flashRAM[set].tranche[inSignals].RAM_WE = 1; + break; + + // Set 1 - A standard MZ80A.the tranZPUter maps in RAM to the User and Floppy drive slots but otherwise all standard. + case 1: + // Place a bank of RAM into the floppy disk bios area. + if( (Z80_ADDR >= 0xF000 && Z80_ADDR < 0x10000) //|| + // Place RAM into the User ROM socket. + // (Z80_ADDR >= 0xE800 && Z80_ADDR < 0xF000) + ) + { + flashRAM[set].tranche[inSignals].DISABLE_BUS = 0; + flashRAM[set].tranche[inSignals].RAM_OE = 0; + } else + { + flashRAM[set].tranche[inSignals].ENABLE_BUS = 0; + flashRAM[set].tranche[inSignals].RAM_OE = 1; + } + break; + + // Set 31 - All memory and IO are on the tranZPUter board. + case 31: + flashRAM[set].tranche[inSignals].DISABLE_BUS = 0; + flashRAM[set].tranche[inSignals].RAM_OE = 0; + break; + + // For default, do nothing. + default: + break; + } + } + + // Specific mapping for IO Writes. + else if(Z80_IO_WRITE) + { + switch(set) + { + case 0: + if(Z80_IO_ADDR != ioAddr) + { + flashRAM[set].tranche[inSignals].ENABLE_BUS = 0; + flashRAM[set].tranche[inSignals].DISABLE_BUS = 1; + flashRAM[set].tranche[inSignals].RAM_OE = 1; + flashRAM[set].tranche[inSignals].RAM_WE = 1; + } + break; + + // Set 31 - All memory and IO are on the tranZPUter board. + case 31: + flashRAM[set].tranche[inSignals].DISABLE_BUS = 0; + flashRAM[set].tranche[inSignals].IODECODE = 0; + break; + + // For default, do nothing. + default: + break; + } + } + + // Specific mapping for IO Reads. + else if(Z80_IO_READ) + { + switch(set) + { + case 0: + if(Z80_IO_ADDR != ioAddr) + { + flashRAM[set].tranche[inSignals].ENABLE_BUS = 0; + flashRAM[set].tranche[inSignals].DISABLE_BUS = 1; + flashRAM[set].tranche[inSignals].RAM_OE = 1; + flashRAM[set].tranche[inSignals].RAM_WE = 1; + } + break; + + // Set 31 - All memory and IO are on the tranZPUter board. + case 31: + flashRAM[set].tranche[inSignals].DISABLE_BUS = 0; + flashRAM[set].tranche[inSignals].IODECODE = 0; + break; + + // For default, do nothing. + default: + break; + } + } + + // Actions which generally arent a valid Z80 transaction. + else + { + } +} + + +// A method to create each set definition. At the moment this is manually coded, if I think of a suitable algorithm then this set of +// procedures will change. +// +void createMap(void) +{ + for(uint8_t idx=0; idx < CFGSETS; idx++) + { + for(uint32_t idx2=0; idx2 < TRANCHESIZE; idx2++) + { + setMap(idx, idx2); + } + } +} + + + + +// Main program, to be split up into methods at a later date!! Just quick write as Im concentrating on the tranZPUterSW!! +// +int main(int argc, char *argv[]) +{ + // Locals. + char outputFile[1024]; + FILE *fpOutput; + int help_flag = 0; + int opt; + int option_index = 0; + + // Initialise any other variables as needed. + // + outputFile[0] = '\0'; + + // Modes of operation. + // flashmmcfg --output file + // flashmmcfg --help + static struct option long_options[] = + { + {"help", no_argument, 0, 'h'}, + {"ioaddr", required_argument, 0, 'i'}, + {"output", required_argument, 0, 'o'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + + // Parse the command line options. + // + while((opt = getopt_long(argc, argv, ":hvo;", long_options, &option_index)) != -1) + { + switch(opt) + { + case 'h': + help_flag = 1; + break; + + case 'i': + ioAddr = atoi(argv[optind]); + break; + + case 'o': + strcpy(outputFile, argv[optind]); + break; + + case 'v': + verbose_flag = 1; + break; + + case ':': + printf("Option %s needs a value\n", argv[optind-1]); + break; + case '?': + printf("Unknown option: %s, ignoring!\n", argv[optind-1]); + break; + } + } + + // Validate the input. + if(help_flag == 1) + { + usage(); + exit(0); + } + if(strlen(outputFile) == 0 ) + { + printf("Output file not specified, please use --output .\n"); + exit(10); + } + + // Open the output file for read/write operations to store the final Flash RAM binary byte image. + fpOutput = fopen(outputFile, "w+"); + if(fpOutput == NULL) + { + printf("Couldnt open the output file:%s.\n", outputFile); + exit(20); + } + if(ioAddr != 0x20 && ioAddr != 0x60 && ioAddr != 0xA0 && ioAddr != 0xF0) + { + printf("IO Control Register Base address is illegal:%04x, it should be one of 0x20, 0x60, 0xA0, 0xF0.\n", ioAddr); + exit(30); + } + + // Initialise the flash map to default unused state. + // + initMap(); + + // Create the required map. + // + createMap(); + + // Output the binary image for flashing into the FlashRAM to perform required decoding. + // + outputMap(fpOutput); + + // Tidy up, close and finish. + fclose(fpOutput); + if(verbose_flag) + printf("Output file created.\n"); +} diff --git a/software/tools/flashmmcfg b/software/tools/flashmmcfg new file mode 100755 index 0000000..7ac9031 Binary files /dev/null and b/software/tools/flashmmcfg differ