Updates for the tranZPUter development

This commit is contained in:
Philip Smart
2020-05-29 14:04:09 +01:00
parent d88a36c65c
commit 39939843f8
38 changed files with 6398 additions and 1593 deletions

7
.gitignore vendored
View File

@@ -60,3 +60,10 @@ apps/*/*.k64
common/old2
include.bak
libraries/umlibc/misc2
apps/tzpu/tzpu.c.1605
apps/tzpu/tzpu.h.1605
c
teensy3/TeensyThreads/
x
zOS/main.hex

View File

@@ -36,41 +36,43 @@
#########################################################################################################
ifeq ($(__K64F__),1)
TOOLSPATH = $(CURDIR)/../../tools
COMPILERPATH = $(TOOLSPATH)/arm/bin
BASE = $(abspath $(COMPILERPATH))/arm-none-eabi
TOOLSPATH = $(CURDIR)/../../tools
COMPILERPATH = $(TOOLSPATH)/arm/bin
BASE = $(abspath $(COMPILERPATH))/arm-none-eabi
else
TOOLSPATH = /opt/zpu
COMPILERPATH = $(TOOLSPATH)/bin
BASE = zpu-elf
TOOLSPATH = /opt/zpu
COMPILERPATH = $(TOOLSPATH)/bin
BASE = zpu-elf
endif
CC = $(BASE)-gcc
CXX = $(BASE)-g++
LD = $(BASE)-gcc
AS = $(BASE)-as
CP = $(BASE)-objcopy
DUMP = $(BASE)-objdump
OBJCOPY = $(BASE)-objcopy
SIZE = $(BASE)-size
CC = $(BASE)-gcc
CXX = $(BASE)-g++
LD = $(BASE)-gcc
AS = $(BASE)-as
CP = $(BASE)-objcopy
DUMP = $(BASE)-objdump
OBJCOPY = $(BASE)-objcopy
SIZE = $(BASE)-size
FS_SUBDIRS := falloc fattr fcat fcd fclose fconcat fcp fdel fdir fdrive fdump finspect flabel fmkdir
FS_SUBDIRS += fmkfs fopen fread frename fsave fseek fshowdir fstat ftime ftrunc fwrite fxtract
DISK_SUBDIRS := ddump dstat
BUFFER_SUBDIRS:= bdump bedit bread bwrite bfill blen
MEM_SUBDIRS := mclear mcopy mdiff mdump meb meh mew mperf msrch mtest
HW_SUBDIRS := hr ht tcpu
TST_SUBDIRS := dhry coremark
MISC_SUBDIRS := help time
APP_SUBDIRS := tbasic mbasic kilo ed
FS_SUBDIRS := falloc fattr fcat fcd fclose fconcat fcp fdel fdir fdrive fdump finspect flabel fmkdir
FS_SUBDIRS += fmkfs fopen fread frename fsave fseek fshowdir fstat ftime ftrunc fwrite fxtract
DISK_SUBDIRS := ddump dstat
BUFFER_SUBDIRS := bdump bedit bread bwrite bfill blen
MEM_SUBDIRS := mclear mcopy mdiff mdump meb meh mew mperf msrch mtest
HW_SUBDIRS := hr ht tcpu
TST_SUBDIRS := dhry coremark
MISC_SUBDIRS := help time
APP_SUBDIRS := tbasic mbasic kilo ed
ifeq ($(__K64F__),1)
TZPU_SUBDIRS:= tzpu tzload
ifeq ($(__TRANZPUTER__),1)
TZPU_SUBDIRS := tzpu tzload tzdump tzclear tzreset
endif
else
TZPU_SUBDIRS:=
TZPU_SUBDIRS :=
endif
SUBDIRS := $(FS_SUBDIRS) $(DISK_SUBDIRS) $(BUFFER_SUBDIRS) $(MEM_SUBDIRS) $(HW_SUBDIRS) $(TST_SUBDIRS) $(MISC_SUBDIRS) $(APP_SUBDIRS) $(TZPU_SUBDIRS)
BASEDIR = ../..
TARGETS := all clean install
SUBDIRS := $(FS_SUBDIRS) $(DISK_SUBDIRS) $(BUFFER_SUBDIRS) $(MEM_SUBDIRS) $(HW_SUBDIRS) $(TST_SUBDIRS) $(MISC_SUBDIRS) $(APP_SUBDIRS) $(TZPU_SUBDIRS)
BASEDIR = ../..
TARGETS := all clean install
# Our target.
$(TARGETS): $(SUBDIRS)

View File

@@ -604,9 +604,9 @@ void _init_Teensyduino_internal_(void)
// https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
// https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
delay(TEENSY_INIT_USB_DELAY_BEFORE);
//delay(TEENSY_INIT_USB_DELAY_BEFORE);
//usb_init();
delay(TEENSY_INIT_USB_DELAY_AFTER);
//delay(TEENSY_INIT_USB_DELAY_AFTER);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,293 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tranzputer.h
// Created: May 2020
// Author(s): Philip Smart
// Description: The TranZPUter library.
// This file contains methods which allow applications to access and control the traZPUter board and the underlying Sharp MZ80A host.
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef TRANZPUTER_H
#define TRANZPUTER_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 FILL_RFSH_BYTE_CNT 256 // Number of bytes we can write before needing a full refresh for the DRAM.
// IO addresses on the tranZPUter or mainboard.
//
#define IO_TZ_CTRLLATCH 0x60
// SHarp MZ80A constants.
//
#define MZ_VID_RAM_ADDR 0xD000 // Start of Video RAM
#define MZ_VID_RAM_SIZE 2048 // Size of Video RAM.
#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.
// Pin Constants - Pins assigned at the hardware level to specific tasks/signals.
//
#define MAX_TRANZPUTER_PINS 47
#define Z80_MEM0_PIN 46
#define Z80_MEM1_PIN 47
#define Z80_MEM2_PIN 48
#define Z80_MEM3_PIN 49
#define Z80_MEM4_PIN 50
#define Z80_WR_PIN 10
#define Z80_RD_PIN 12
#define Z80_IORQ_PIN 8
#define Z80_MREQ_PIN 9
#define Z80_A0_PIN 39
#define Z80_A1_PIN 38
#define Z80_A2_PIN 37
#define Z80_A3_PIN 36
#define Z80_A4_PIN 35
#define Z80_A5_PIN 34
#define Z80_A6_PIN 33
#define Z80_A7_PIN 32
#define Z80_A8_PIN 31
#define Z80_A9_PIN 30
#define Z80_A10_PIN 29
#define Z80_A11_PIN 28
#define Z80_A12_PIN 27
#define Z80_A13_PIN 26
#define Z80_A14_PIN 25
#define Z80_A15_PIN 24
#define Z80_A16_PIN 23
#define Z80_A17_PIN 22
#define Z80_A18_PIN 21
#define Z80_D0_PIN 0
#define Z80_D1_PIN 1
#define Z80_D2_PIN 2
#define Z80_D3_PIN 3
#define Z80_D4_PIN 4
#define Z80_D5_PIN 5
#define Z80_D6_PIN 6
#define Z80_D7_PIN 7
#define Z80_WAIT_PIN 13
#define Z80_BUSACK_PIN 17
#define Z80_NMI_PIN 43
#define Z80_INT_PIN 44
#define CTL_RFSH_PIN 45
#define CTL_HALT_PIN 14
#define CTL_M1_PIN 20
#define CTL_BUSRQ_PIN 15
#define CTL_BUSACK_PIN 16
#define CTL_CLK_PIN 18
#define CTL_CLKSLCT_PIN 19
// Customised pin manipulation methods implemented as stripped down macros. The original had too much additional overhead with procedure call and validation tests,
// speed is of the essence for this project as pins change mode and value constantly.
//
#define pinLow(a) *portClearRegister(pinMap[a]) = 1
#define pinHigh(a) *portSetRegister(pinMap[a]) = 1
#define pinSet(a, b) if(b) { *portSetRegister(pinMap[a]) = 1; } else { *portClearRegister(pinMap[a]) = 1; }
#define pinGet(a) *portInputRegister(pinMap[a])
#define pinInput(a) { *portModeRegister(pinMap[a]) = 0; *ioPin[pinMap[a]] = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; }
#define pinOutput(a) { *portModeRegister(pinMap[a]) = 1;\
*ioPin[pinMap[a]] = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);\
*ioPin[pinMap[a]] &= ~PORT_PCR_ODE; }
#define pinOutputSet(a,b) { *portModeRegister(pinMap[a]) = 1;\
*ioPin[pinMap[a]] = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);\
*ioPin[pinMap[a]] &= ~PORT_PCR_ODE;\
if(b) { *portSetRegister(pinMap[a]) = 1; } else { *portClearRegister(pinMap[a]) = 1; } }
#define setZ80Data(a) { pinSet(Z80_D7, ((a >> 7) & 0x1)); pinSet(Z80_D6, ((a >> 6) & 0x1));\
pinSet(Z80_D5, ((a >> 5) & 0x1)); pinSet(Z80_D4, ((a >> 4) & 0x1));\
pinSet(Z80_D3, ((a >> 3) & 0x1)); pinSet(Z80_D2, ((a >> 2) & 0x1));\
pinSet(Z80_D1, ((a >> 1) & 0x1)); pinSet(Z80_D0, ((a ) & 0x1)); }
#define setZ80Addr(a) { pinSet(Z80_A15, ((a >> 15) & 0x1)); pinSet(Z80_A14, ((a >> 14) & 0x1));\
pinSet(Z80_A13, ((a >> 13) & 0x1)); pinSet(Z80_A12, ((a >> 12) & 0x1));\
pinSet(Z80_A11, ((a >> 11) & 0x1)); pinSet(Z80_A10, ((a >> 10) & 0x1));\
pinSet(Z80_A9, ((a >> 9) & 0x1)); pinSet(Z80_A8, ((a >> 8) & 0x1));\
pinSet(Z80_A7, ((a >> 7) & 0x1)); pinSet(Z80_A6, ((a >> 6) & 0x1));\
pinSet(Z80_A5, ((a >> 5) & 0x1)); pinSet(Z80_A4, ((a >> 4) & 0x1));\
pinSet(Z80_A3, ((a >> 3) & 0x1)); pinSet(Z80_A2, ((a >> 2) & 0x1));\
pinSet(Z80_A1, ((a >> 1) & 0x1)); pinSet(Z80_A0, ((a ) & 0x1)); }
#define setZ80RefreshAddr(a) { pinSet(Z80_A6, ((a >> 6) & 0x1)); pinSet(Z80_A5, ((a >> 5) & 0x1));\
pinSet(Z80_A4, ((a >> 4) & 0x1)); pinSet(Z80_A3, ((a >> 3) & 0x1));\
pinSet(Z80_A2, ((a >> 2) & 0x1)); pinSet(Z80_A1, ((a >> 1) & 0x1));\
pinSet(Z80_A0, ((a ) & 0x1)); }
#define readDataBus() ( pinGet(Z80_D7) << 7 | pinGet(Z80_D6) << 6 | pinGet(Z80_D5) << 5 | pinGet(Z80_D4) << 4 |\
pinGet(Z80_D3) << 3 | pinGet(Z80_D2) << 2 | pinGet(Z80_D1) << 1 | pinGet(Z80_D0) )
//#define readCtrlLatch() ( pinGet(Z80_A18) << 7 | pinGet(Z80_A17) << 6 | pinGet(Z80_A16) << 5 | pinGet(Z80_MEM4) << 4 |\
// pinGet(Z80_MEM3) << 3 | pinGet(Z80_MEM2) << 2 | pinGet(Z80_MEM1) << 1 | pinGet(Z80_MEM0) )
// Special case during development where the pins for the MEM4:1 are not connected.
#define readCtrlLatch() ((pinGet(Z80_A18) << 7 | pinGet(Z80_A17) << 6 | pinGet(Z80_A16) << 5) & 0b11100000)
#define writeCtrlLatch(a) { writeZ80IO(IO_TZ_CTRLLATCH, a); }
#define readUpperAddr() ((pinGet(Z80_A18) << 2 | pinGet(Z80_A17) << 1 | pinGet(Z80_A16)) & 0b00000111)
#define setZ80Direction(a) { for(uint8_t idx=Z80_D0; idx <= Z80_D7; idx++) { if(a == WRITE) { pinOutput(idx); } else { pinInput(idx); } }; z80Control.busDir = a; }
#define reqZ80BusChange(a) { if(a == MAINBOARD_ACCESS && z80Control.ctrlMode == TRANZPUTER_ACCESS) \
{\
pinHigh(CTL_BUSACK);\
z80Control.ctrlMode = MAINBOARD_ACCESS;\
z80Control.curCtrlLatch = 0b00000000;\
writeCtrlLatch(z80Control.curCtrlLatch);\
} else if(a == TRANZPUTER_ACCESS && z80Control.ctrlMode == MAINBOARD_ACCESS)\
{\
pinLow(CTL_BUSACK);\
z80Control.ctrlMode = TRANZPUTER_ACCESS;\
z80Control.curCtrlLatch = 0b00011111;\
writeCtrlLatch(z80Control.curCtrlLatch);\
} }
// Enumeration of the various pins on the project. These enums make it easy to refer to a signal and they are mapped
// to the actual hardware pin via the pinMap array.
// One of the big advantages is that a swath of pins, such as the address lines, can be switched in a tight loop rather than
// individual pin assignments or clunky lists.
//
enum pinIdxToPinNumMap {
Z80_A0 = 0,
Z80_A1 = 1,
Z80_A2 = 2,
Z80_A3 = 3,
Z80_A4 = 4,
Z80_A5 = 5,
Z80_A6 = 6,
Z80_A7 = 7,
Z80_A8 = 8,
Z80_A9 = 9,
Z80_A10 = 10,
Z80_A11 = 11,
Z80_A12 = 12,
Z80_A13 = 13,
Z80_A14 = 14,
Z80_A15 = 15,
Z80_A16 = 16,
Z80_A17 = 17,
Z80_A18 = 18,
Z80_D0 = 19,
Z80_D1 = 20,
Z80_D2 = 21,
Z80_D3 = 22,
Z80_D4 = 23,
Z80_D5 = 24,
Z80_D6 = 25,
Z80_D7 = 26,
Z80_MEM0 = 27,
Z80_MEM1 = 28,
Z80_MEM2 = 29,
Z80_MEM3 = 30,
Z80_MEM4 = 31,
Z80_IORQ = 32,
Z80_MREQ = 33,
Z80_RD = 34,
Z80_WR = 35,
Z80_WAIT = 36,
Z80_BUSACK = 37,
Z80_NMI = 38,
Z80_INT = 39,
CTL_BUSACK = 40,
CTL_BUSRQ = 41,
CTL_RFSH = 42,
CTL_HALT = 43,
CTL_M1 = 44,
CTL_CLK = 45,
CTL_CLKSLCT = 46
};
// Possible control modes that the K64F can be in, do nothing where the Z80 runs normally, control the Z80 and mainboard, or control the Z80 and tranZPUter.
enum CTRL_MODE {
Z80_RUN = 0,
TRANZPUTER_ACCESS = 1,
MAINBOARD_ACCESS = 2
};
// Possible bus directions that the K64F can setup for controlling the Z80.
enum BUS_DIRECTION {
READ = 0,
WRITE = 1,
TRISTATE = 2
};
// Possible video frames stored internally.
//
enum VIDEO_FRAMES {
SAVED = 0,
WORKING = 1
};
// Structure to maintain all the control and management variables so that the state of run is well known by any called method.
//
typedef struct {
uint8_t refreshAddr; // Refresh address for times when the K64F must issue refresh cycles on the Z80 bus.
uint8_t runCtrlLatch; // Latch value the Z80 is running with.
uint8_t curCtrlLatch; // Latch value set during tranZPUter access of the Z80 bus.
uint8_t videoRAM[2][2048]; // Two video memory buffer frames, allows for storage of original frame in [0] and working frame in [1].
uint8_t attributeRAM[2][2048]; // Two attribute memory buffer frames, allows for storage of original frame in [0] and working frame in [1].
enum CTRL_MODE ctrlMode; // Mode of control, ie normal Z80 Running, controlling mainboard, controlling tranZPUter.
enum BUS_DIRECTION busDir; // Direction the bus has been configured for.
} t_z80Control;
// Application execution constants.
//
// References to variables within the main library code.
extern volatile uint32_t *ioPin[MAX_TRANZPUTER_PINS];
extern uint8_t pinMap[MAX_TRANZPUTER_PINS];
// Prototypes.
//
void yield(void);
void setupPins(volatile uint32_t *);
uint8_t reqZ80Bus(uint32_t);
void relinquishZ80Bus(void);
uint8_t reqMainboardBus(uint32_t);
uint8_t reqTranZPUterBus(uint32_t);
void setupSignalsForZ80Access(enum BUS_DIRECTION);
void releaseZ80(void);
void refreshZ80(void);
uint8_t writeZ80Memory(uint16_t, uint8_t);
uint8_t readZ80Memory(uint16_t);
uint8_t writeZ80IO(uint16_t, uint8_t);
uint8_t readZ80IO(uint16_t);
void fillZ80Memory(uint32_t, uint32_t, uint8_t, uint8_t);
void captureVideoFrame(enum VIDEO_FRAMES, uint8_t);
void refreshVideoFrame(enum VIDEO_FRAMES, uint8_t, uint8_t);
FRESULT loadVideoFrameBuffer(char *, enum VIDEO_FRAMES);
FRESULT saveVideoFrameBuffer(char *, enum VIDEO_FRAMES);
FRESULT loadZ80Memory(char *, uint32_t, uint8_t, uint8_t);
FRESULT saveZ80Memory(char *, uint32_t, uint32_t, uint8_t);
// Debug methods.
void displaySignals(void);
#ifdef __cplusplus
}
#endif
#endif // TRANZPUTER_H

84
apps/tzclear/Makefile Executable file
View File

@@ -0,0 +1,84 @@
#########################################################################################################
##
## Name: Makefile
## Created: July 2019
## Author(s): Philip Smart
## Description: App Makefile - Build an App for the ZPU Test Application (zputa) or the zOS
## operating system.
## This makefile builds an app which is stored on an SD card and called by ZPUTA/zOS
## The app is for testing some component where the code is not built into ZPUTA or
## a user application for zOS.
##
## Credits:
## Copyright: (c) 2019-20 Philip Smart <philip.smart@net2net.org>
##
## History: July 2019 - Initial Makefile created for template use.
## April 2020 - Added K64F as an additional target and resplit ZPUTA into zOS.
##
## Notes: Optional component enables:
## USELOADB - The Byte write command is implemented in hw#sw so use it.
## USE_BOOT_ROM - The target is ROM so dont use initialised data.
## MINIMUM_FUNTIONALITY - Minimise functionality to limit code size.
##
#########################################################################################################
## This source file is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This source file is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
#########################################################################################################
APP_NAME = tzclear
APP_DIR = $(CURDIR)/..
APP_COMMON_DIR = $(CURDIR)/../common
COMMON_DIR = $(CURDIR)/../../common
BASEDIR = ../../..
TEENSYDIR = ../../teensy3/
# Override values given by parent make for this application as its memory usage differs from the standard app.
ifeq ($(__K64F__),1)
#override HEAPADDR = 0x2002f000
#override HEAPSIZE = 0x00000000
#override STACKADDR = 0x2002f000
#override STACKSIZE = 0x00000000
# Modules making up tzpu.
APP_C_SRC = $(APP_COMMON_DIR)/pins_teensy.c $(APP_COMMON_DIR)/analog.c $(COMMON_DIR)/tranzputer.c
CFLAGS =
CPPFLAGS =
LDFLAGS =
LIBS =
else
# Modules making up tcpu.
APP_C_SRC = #$(APP_COMMON_DIR)/sysutils.c $(APP_COMMON_DIR)/ctypelocal.c
CFLAGS =
CPPFLAGS =
LDFLAGS = -nostdlib
LIBS = -lumansi-zpu -limath-zpu
endif
# Filter out the standard HEAP address and size, replacing with the ones required for this application.
# Useful for sub-makes
FILTER1 = $(filter-out $(filter HEAPADDR=%,$(MAKEFLAGS)), $(MAKEFLAGS))
FILTER2 = $(filter-out $(filter HEAPSIZE=%,$(FILTER1)), $(FILTER1))
NEWMAKEFLAGS = $(FILTER2) HEAPADDR=$(HEADADDR) HEAPSIZE=$(HEAPSIZE)
ifeq ($(__K64F__),1)
include $(APP_DIR)/Makefile.k64f
else
# There currently is no code for the ZPU, all development being done on the K64F for this app.
all:
clean:
install:
endif

258
apps/tzclear/tzclear.c Normal file
View File

@@ -0,0 +1,258 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tzclear.c
// Created: May 2020
// Author(s): Philip Smart
// Description: A TranZPUter helper utility, allowing the realtime clearing of the tranZPUter
// or host mainboard memory.
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__K64F__)
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <usb_serial.h>
#include <core_pins.h>
#include <Arduino.h>
#include <getopt.h>
#include "k64f_soc.h"
#include <../../libraries/include/stdmisc.h>
#elif defined(__ZPU__)
#include <stdint.h>
#include <stdio.h>
#include "zpu_soc.h"
#include <stdlib.h>
#include <ctype.h>
#include <stdmisc.h>
#else
#error "Target CPU not defined, use __ZPU__ or __K64F__"
#endif
#include "interrupts.h"
#include "ff.h" /* Declarations of FatFs API */
#include "utils.h"
//
#if defined __ZPUTA__
#include "zputa_app.h"
#elif defined __ZOS__
#include "zOS_app.h"
#else
#error OS not defined, use __ZPUTA__ or __ZOS__
#endif
//
#include <app.h>
#include <tranzputer.h>
#include "tzclear.h"
// Utility functions.
#include <tools.c>
// Version info.
#define VERSION "v1.0"
#define VERSION_DATE "15/05/2020"
#define APP_NAME "TZCLEAR"
// Simple help screen to remmber how this utility works!!
//
void usage(void)
{
printf("%s %s\n", APP_NAME, VERSION);
printf("\nCommands:-\n");
printf(" -h | --help This help text.\n");
printf(" -a | --start Start address.\n");
printf("\nOptions:-\n");
printf(" -e | --end End address (alternatively use --size).\n");
printf(" -s | --size Size of memory block to clear (alternatively use --end).\n");
printf(" -b | --byte Byte value to place into each cleared memory location, defaults to 0x00.\n");
printf(" -m | --mainboard Operations will take place on the MZ80A mainboard. Default without this flag is to target the tranZPUter memory.\n");
printf(" -v | --verbose Output more messages.\n");
printf("\nExamples:\n");
printf(" tzclear -a 0x000000 -s 0x200 -b 0xAA # Clears memory locations in the tranZPUter memory from 0x000000 to 0x000200 using value 0xAA.\n");
}
// Main entry and start point of a zOS/ZPUTA Application. Only 2 parameters are catered for and a 32bit return code, additional parameters can be added by changing the appcrt0.s
// startup code to add them to the stack prior to app() call.
//
// Return code for the ZPU is saved in _memreg by the C compiler, this is transferred to _memreg in zOS/ZPUTA in appcrt0.s prior to return.
// The K64F ARM processor uses the standard register passing conventions, return code is stored in R0.
//
uint32_t app(uint32_t param1, uint32_t param2)
{
// Initialisation.
//
uint32_t startAddr = 0xFFFFFFFF;
uint32_t endAddr = 0xFFFFFFFF;
uint32_t memSize = 0xFFFFFFFF;
uint8_t byte = 0x00;
int argc = 0;
int help_flag = 0;
int mainboard_flag = 0;
int verbose_flag = 0;
int opt;
int option_index = 0;
long val = 0;
char *argv[20];
char *ptr = strtok((char *)param1, " ");
// Initialisation.
// If the invoking command is given, add it to argv at the start.
//
if(param2 != 0)
{
argv[argc++] = (char *)param2;
}
// Now convert the parameter line into argc/argv suitable for getopt to use.
while (ptr && argc < 20-1)
{
argv[argc++] = ptr;
ptr = strtok(0, " ");
}
argv[argc] = 0;
// Define parameters to be processed.
static struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{"start", required_argument, 0, 'a'},
{"end", required_argument, 0, 'e'},
{"size", required_argument, 0, 's'},
{"byte", required_argument, 0, 'b'},
{"mainboard", no_argument, 0, 'm'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
// Parse the command line options.
//
while((opt = getopt_long(argc, argv, ":hs:e:s:mv", long_options, &option_index)) != -1)
{
switch(opt)
{
case 'h':
help_flag = 1;
break;
case 'm':
mainboard_flag = 1;
break;
case 'a':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(5);
}
startAddr = (uint32_t)val;
break;
case 'e':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(6);
}
endAddr = (uint32_t)val;
break;
case 's':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(7);
}
memSize = (uint32_t)val;
break;
case 'b':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(6);
}
byte = (uint8_t)val;
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();
return(0);
}
if(startAddr == 0xFFFFFFFF)
{
printf("Please define the start address, size will default to 0x100.\n");
return(10);
}
if(endAddr == 0xFFFFFFFF && memSize == 0xFFFFFFFF)
{
memSize = 0x100;
} else if(memSize == 0xFFFFFFFF)
{
memSize = endAddr - startAddr;
}
if(mainboard_flag == 1 && (startAddr > 0x10000 || startAddr + memSize > 0x10000))
{
printf("Mainboard only has 64K, please change the address or size.\n");
return(11);
}
if(mainboard_flag == 0 && (startAddr >= 0x80000 || startAddr + memSize > 0x80000))
{
printf("tranZPUter board only has 512K, please change the address or size.\n");
return(12);
}
// Initialise the IO.
setupZ80Pins(1, G->millis);
// Call the fill utility to clear memory.
//
fillZ80Memory(startAddr, memSize, byte, mainboard_flag);
return(0);
}
#ifdef __cplusplus
}
#endif

46
apps/tzclear/tzclear.h Executable file
View File

@@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tzclear.h
// Created: May 2020
// Author(s): Philip Smart
// Description: A TranZPUter helper utility, allowing the realtime clearing of the tranZPUter
// or host mainboard memory.
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef TZCLEAR_H
#define TZCLEAR_H
#ifdef __cplusplus
extern "C" {
#endif
// Components to be embedded in the program.
//
// Filesystem components to be embedded in the program.
// Application execution constants.
//
#ifdef __cplusplus
}
#endif
#endif // TZCLEAR_H

84
apps/tzdump/Makefile Executable file
View File

@@ -0,0 +1,84 @@
#########################################################################################################
##
## Name: Makefile
## Created: July 2019
## Author(s): Philip Smart
## Description: App Makefile - Build an App for the ZPU Test Application (zputa) or the zOS
## operating system.
## This makefile builds an app which is stored on an SD card and called by ZPUTA/zOS
## The app is for testing some component where the code is not built into ZPUTA or
## a user application for zOS.
##
## Credits:
## Copyright: (c) 2019-20 Philip Smart <philip.smart@net2net.org>
##
## History: July 2019 - Initial Makefile created for template use.
## April 2020 - Added K64F as an additional target and resplit ZPUTA into zOS.
##
## Notes: Optional component enables:
## USELOADB - The Byte write command is implemented in hw#sw so use it.
## USE_BOOT_ROM - The target is ROM so dont use initialised data.
## MINIMUM_FUNTIONALITY - Minimise functionality to limit code size.
##
#########################################################################################################
## This source file is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This source file is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
#########################################################################################################
APP_NAME = tzdump
APP_DIR = $(CURDIR)/..
APP_COMMON_DIR = $(CURDIR)/../common
COMMON_DIR = $(CURDIR)/../../common
BASEDIR = ../../..
TEENSYDIR = ../../teensy3/
# Override values given by parent make for this application as its memory usage differs from the standard app.
ifeq ($(__K64F__),1)
#override HEAPADDR = 0x2002f000
#override HEAPSIZE = 0x00000000
#override STACKADDR = 0x2002f000
#override STACKSIZE = 0x00000000
# Modules making up tzpu.
APP_C_SRC = $(APP_COMMON_DIR)/pins_teensy.c $(APP_COMMON_DIR)/analog.c $(COMMON_DIR)/tranzputer.c
CFLAGS =
CPPFLAGS =
LDFLAGS =
LIBS =
else
# Modules making up tcpu.
APP_C_SRC = #$(APP_COMMON_DIR)/sysutils.c $(APP_COMMON_DIR)/ctypelocal.c
CFLAGS =
CPPFLAGS =
LDFLAGS = -nostdlib
LIBS = -lumansi-zpu -limath-zpu
endif
# Filter out the standard HEAP address and size, replacing with the ones required for this application.
# Useful for sub-makes
FILTER1 = $(filter-out $(filter HEAPADDR=%,$(MAKEFLAGS)), $(MAKEFLAGS))
FILTER2 = $(filter-out $(filter HEAPSIZE=%,$(FILTER1)), $(FILTER1))
NEWMAKEFLAGS = $(FILTER2) HEAPADDR=$(HEADADDR) HEAPSIZE=$(HEAPSIZE)
ifeq ($(__K64F__),1)
include $(APP_DIR)/Makefile.k64f
else
# There currently is no code for the ZPU, all development being done on the K64F for this app.
all:
clean:
install:
endif

247
apps/tzdump/tzdump.c Normal file
View File

@@ -0,0 +1,247 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tzdump.c
// Created: May 2020
// Author(s): Philip Smart
// Description: A TranZPUter helper utility, allowing the realtime display of the tranZPUter
// or host mainboard memory.
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__K64F__)
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <usb_serial.h>
#include <core_pins.h>
#include <Arduino.h>
#include <getopt.h>
#include "k64f_soc.h"
#include <../../libraries/include/stdmisc.h>
#elif defined(__ZPU__)
#include <stdint.h>
#include <stdio.h>
#include "zpu_soc.h"
#include <stdlib.h>
#include <ctype.h>
#include <stdmisc.h>
#else
#error "Target CPU not defined, use __ZPU__ or __K64F__"
#endif
#include "interrupts.h"
#include "ff.h" /* Declarations of FatFs API */
#include "utils.h"
//
#if defined __ZPUTA__
#include "zputa_app.h"
#elif defined __ZOS__
#include "zOS_app.h"
#else
#error OS not defined, use __ZPUTA__ or __ZOS__
#endif
//
#include <app.h>
#include <tranzputer.h>
#include "tzdump.h"
// Utility functions.
#include <tools.c>
// Version info.
#define VERSION "v1.0"
#define VERSION_DATE "15/05/2020"
#define APP_NAME "TZDUMP"
// Simple help screen to remmber how this utility works!!
//
void usage(void)
{
printf("%s %s\n", APP_NAME, VERSION);
printf("\nCommands:-\n");
printf(" -h | --help This help text.\n");
printf(" -a | --start Start address.\n");
printf("\nOptions:-\n");
printf(" -e | --end End address (alternatively use --size).\n");
printf(" -s | --size Size of memory block to dump (alternatively use --end).\n");
printf(" -m | --mainboard Operations will take place on the MZ80A mainboard. Default without this flag is to target the tranZPUter memory.\n");
printf(" -v | --verbose Output more messages.\n");
printf("\nExamples:\n");
printf(" tzdump -a 0x000000 -s 0x200 # Dump tranZPUter memory from 0x000000 to 0x000200.\n");
}
// Main entry and start point of a zOS/ZPUTA Application. Only 2 parameters are catered for and a 32bit return code, additional parameters can be added by changing the appcrt0.s
// startup code to add them to the stack prior to app() call.
//
// Return code for the ZPU is saved in _memreg by the C compiler, this is transferred to _memreg in zOS/ZPUTA in appcrt0.s prior to return.
// The K64F ARM processor uses the standard register passing conventions, return code is stored in R0.
//
uint32_t app(uint32_t param1, uint32_t param2)
{
// Initialisation.
//
uint32_t startAddr = 0xFFFFFFFF;
uint32_t endAddr = 0xFFFFFFFF;
uint32_t memSize = 0xFFFFFFFF;
int argc = 0;
int help_flag = 0;
int mainboard_flag = 0;
int verbose_flag = 0;
int opt;
int option_index = 0;
long val = 0;
char *argv[20];
char *ptr = strtok((char *)param1, " ");
// Initialisation.
// If the invoking command is given, add it to argv at the start.
//
if(param2 != 0)
{
argv[argc++] = (char *)param2;
}
// Now convert the parameter line into argc/argv suitable for getopt to use.
while (ptr && argc < 20-1)
{
argv[argc++] = ptr;
ptr = strtok(0, " ");
}
argv[argc] = 0;
// Define parameters to be processed.
static struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{"start", required_argument, 0, 'a'},
{"end", required_argument, 0, 'e'},
{"size", required_argument, 0, 's'},
{"mainboard", no_argument, 0, 'm'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
// Parse the command line options.
//
while((opt = getopt_long(argc, argv, ":hs:e:s:mv", long_options, &option_index)) != -1)
{
switch(opt)
{
case 'h':
help_flag = 1;
break;
case 'm':
mainboard_flag = 1;
break;
case 'a':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(5);
}
startAddr = (uint32_t)val;
break;
case 'e':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(6);
}
endAddr = (uint32_t)val;
break;
case 's':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(7);
}
memSize = (uint32_t)val;
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();
return(0);
}
if(startAddr == 0xFFFFFFFF)
{
printf("Please define the start address, size will default to 0x100.\n");
return(10);
}
if(endAddr == 0xFFFFFFFF && memSize == 0xFFFFFFFF)
{
memSize = 0x100;
} else if(memSize == 0xFFFFFFFF)
{
memSize = endAddr - startAddr;
}
if(mainboard_flag == 1 && (startAddr > 0x10000 || startAddr + memSize > 0x10000))
{
printf("Mainboard only has 64K, please change the address or size.\n");
return(11);
}
if(mainboard_flag == 0 && (startAddr >= 0x80000 || startAddr + memSize > 0x80000))
{
printf("tranZPUter board only has 512K, please change the address or size.\n");
return(12);
}
// Initialise the IO.
setupZ80Pins(1, G->millis);
// Call the dump utility to list out memory.
//
memoryDumpZ80(startAddr, memSize, startAddr, 32, mainboard_flag);
return(0);
}
#ifdef __cplusplus
}
#endif

46
apps/tzdump/tzdump.h Executable file
View File

@@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tzdump.h
// Created: May 2020
// Author(s): Philip Smart
// Description: A TranZPUter helper utility, allowing the realtime display of the tranZPUter
// or host mainboard memory.
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef TZDUMP_H
#define TZDUMP_H
#ifdef __cplusplus
extern "C" {
#endif
// Components to be embedded in the program.
//
// Filesystem components to be embedded in the program.
// Application execution constants.
//
#ifdef __cplusplus
}
#endif
#endif // TZDUMP_H

View File

@@ -38,6 +38,7 @@
APP_NAME = tzload
APP_DIR = $(CURDIR)/..
APP_COMMON_DIR = $(CURDIR)/../common
COMMON_DIR = $(CURDIR)/../../common
BASEDIR = ../../..
TEENSYDIR = ../../teensy3/
@@ -49,7 +50,7 @@ ifeq ($(__K64F__),1)
#override STACKSIZE = 0x00000000
# Modules making up tzpu.
APP_C_SRC = $(APP_COMMON_DIR)/pins_teensy.c $(APP_COMMON_DIR)/analog.c $(APP_COMMON_DIR)/tranzputer.c #$(TEENSYDIR)/yield.cpp
APP_C_SRC = $(APP_COMMON_DIR)/pins_teensy.c $(APP_COMMON_DIR)/analog.c $(COMMON_DIR)/tranzputer.c
CFLAGS =
CPPFLAGS =
LDFLAGS =

View File

@@ -89,13 +89,13 @@ void usage(void)
printf(" -u | --upload <file> File whose contents are uploaded into the traZPUter memory.\n");
printf(" -U | --uploadset <file>:<addr>,...,<file>:<addr>\n");
printf(" Upload a set of files at the specified locations. --mainboard specifies mainboard is target, default is tranZPUter.\n");
printf(" -f | --fill <byte> Fill the memory specified by --addr, --size and [--mainboard] with the value <byte>.\n");
printf(" -V | --video The specified input file is uploaded into the video frame buffer or the specified output file is filled with the video frame buffer.\n");
printf("\nOptions:-\n");
printf(" -a | --addr Memory address to read/write.\n");
printf(" -l | --size Size of memory block to read. This option is only used when reading tranZPUter memory, for writing, the file size is used.\n");
printf(" -s | --swap Read tranZPUter memory and store in <infile> then write out <outfile> to the same memory location.\n");
printf(" -m | --mainboard Operations will take place on the MZ80A mainboard. Default without this flag is to target the tranZPUter memory.\n");
printf(" -z | --mzf File operations are to process the file as an MZF format file, --addr and --size will override the MZF header values if needed.\n");
printf(" -v | --verbose Output more messages.\n");
printf("\nExamples:\n");
@@ -115,17 +115,17 @@ uint32_t app(uint32_t param1, uint32_t param2)
//
uint32_t memAddr = 0xFFFFFFFF;
uint32_t memSize = 0xFFFFFFFF;
uint16_t fillByte = 0xFFFF;
int argc = 0;
int help_flag = 0;
int mainboard_flag = 0;
int mzf_flag = 0;
int swap_flag = 0;
int verbose_flag = 0;
int video_flag = 0;
int opt;
int option_index = 0;
int uploadFNLen = 0;
int downloadFNLen = 0;
int uploadFNLen = 0;
int downloadFNLen = 0;
int uploadCnt = 0;
long val;
char *argv[20];
@@ -162,8 +162,8 @@ uint32_t app(uint32_t param1, uint32_t param2)
{"uploadset", required_argument, 0, 'U'},
{"addr", required_argument, 0, 'a'},
{"size", required_argument, 0, 'l'},
{"fill", required_argument, 0, 'f'},
{"mainboard", no_argument, 0, 'm'},
{"mzf", no_argument, 0, 'z'},
{"swap", no_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{"video", no_argument, 0, 'V'},
@@ -172,7 +172,7 @@ uint32_t app(uint32_t param1, uint32_t param2)
// Parse the command line options.
//
while((opt = getopt_long(argc, argv, ":ha:l:mvU:Vsd:u:", long_options, &option_index)) != -1)
while((opt = getopt_long(argc, argv, ":ha:l:mvU:Vzsd:u:", long_options, &option_index)) != -1)
{
switch(opt)
{
@@ -188,15 +188,6 @@ uint32_t app(uint32_t param1, uint32_t param2)
swap_flag = 1;
break;
case 'f':
if(xatoi(&argv[optind-1], &val) == 0)
{
printf("Illegal numeric:%s\n", argv[optind-1]);
return(5);
}
fillByte = (uint16_t)val;
break;
case 'a':
if(xatoi(&argv[optind-1], &val) == 0)
{
@@ -230,15 +221,12 @@ uint32_t app(uint32_t param1, uint32_t param2)
case 'U':
// Extract an array of <file>:<addr>,... sets for upload.
//
printf("This line:%s\n", argv[optind-1]);
ptr = strtok((char *)argv[optind-1], ",");
while (ptr && uploadCnt < 20-1)
{
printf("Split:%s\n", ptr);
uploadArr[uploadCnt++] = ptr;
ptr = strtok(0, ",");
}
printf("Final count=%d\n", uploadCnt);
if(uploadCnt == 0)
{
printf("Upload set command should use format <file>:<addr>,...\n");
@@ -254,6 +242,10 @@ uint32_t app(uint32_t param1, uint32_t param2)
video_flag = 1;
break;
case 'z':
mzf_flag = 1;
break;
case ':':
printf("Option %s needs a value\n", argv[optind-1]);
break;
@@ -264,25 +256,20 @@ uint32_t app(uint32_t param1, uint32_t param2)
}
// Validate the input.
if(uploadCnt && (help_flag == 1 || uploadFNLen > 0 || downloadFNLen > 0 || swap_flag == 1 || video_flag == 1 || fillByte != 0xFFFF || memAddr != 0xFFFFFFFF || memSize != 0xFFFFFFFF))
if(uploadCnt && (help_flag == 1 || uploadFNLen > 0 || downloadFNLen > 0 || swap_flag == 1 || video_flag == 1 || memAddr != 0xFFFFFFFF || memSize != 0xFFFFFFFF))
{
printf("Illegal combination of flags, --upload can only be used with --mainboard.\n");
printf("Illegal combination of flags, --uploadset can only be used with --mainboard.\n");
return(10);
}
if(video_flag == 1 && (help_flag == 1 || swap_flag == 1 || fillByte != 0xFFFF || mainboard_flag == 1 || memAddr != 0xFFFFFFFF || memSize != 0xFFFFFFFF))
if(video_flag == 1 && (help_flag == 1 || swap_flag == 1 || mainboard_flag == 1 || memAddr != 0xFFFFFFFF || memSize != 0xFFFFFFFF))
{
printf("Illegal combination of flags, --video can only be used with --infile, --outfile and --mainboard.\n");
printf("Illegal combination of flags, --video can only be used with --upload, --download and --mainboard.\n");
return(11);
}
if(fillByte != 0xFFFF && (help_flag == 1 || uploadFNLen > 0 || downloadFNLen > 0 || swap_flag == 1 || video_flag == 1 || memAddr == 0xFFFFFFFF || memSize == 0xFFFFFFFF))
{
printf("Illegal combination of flags, --fill can only be used with --addr, --size and --mainboard.\n");
return(12);
}
// If the Video and Upload modes arent required, check other argument combinations.
//
if(uploadCnt == 0 && video_flag == 0 && fillByte == 0xFFFF)
if(uploadCnt == 0 && video_flag == 0)
{
if(help_flag == 1)
{
@@ -307,28 +294,33 @@ uint32_t app(uint32_t param1, uint32_t param2)
printf("Please define the size of memory you wish to read.\n");
return(16);
}
if(memAddr == 0xFFFFFFFF)
if(mzf_flag == 1 && downloadFNLen > 0)
{
printf("MZF Format can currently only be used for file uploading.\n");
return(17);
}
if(memAddr == 0xFFFFFFFF && mzf_flag == 0)
{
printf("Please define the target address.\n");
return(17);
return(18);
}
}
if(uploadCnt == 0 && video_flag == 0)
{
if(mainboard_flag == 1 && (memAddr > 0x10000 || memAddr + memSize > 0x10000))
if(mainboard_flag == 1 && mzf_flag == 0 && (memAddr > 0x10000 || memAddr + memSize > 0x10000))
{
printf("Mainboard only has 64K, please change the address and size.\n");
return(17);
return(19);
}
if(mainboard_flag == 0 && (memAddr >= 0x80000 || memAddr + memSize > 0x80000))
if(mainboard_flag == 0 && mzf_flag == 0 && (memAddr >= 0x80000 || memAddr + memSize > 0x80000))
{
printf("tranZPUter board only has 512K, please change the address and size.\n");
return(18);
return(20);
}
}
// Initialise the IO.
setupPins(G->millis);
setupZ80Pins(1, G->millis);
// Bulk file upload command (used to preload a file set).
//
@@ -343,13 +335,19 @@ uint32_t app(uint32_t param1, uint32_t param2)
if(xatoi(&ptr, &val) == 0)
{
printf("Illegal numeric in upload list:%s\n", ptr);
return(20);
return(30);
}
memAddr = (uint32_t)val;
// Now we have the input file and the address where it should be loaded, call the load function.
//
loadZ80Memory(uploadFile, memAddr, mainboard_flag, (idx == uploadCnt-1) ? 1 : 0);
if(mzf_flag == 0)
{
loadZ80Memory(uploadFile, 0, memAddr, 0, mainboard_flag, (idx == uploadCnt-1) ? 1 : 0);
} else
{
loadMZFZ80Memory(uploadFile, memAddr, mainboard_flag, (idx == uploadCnt-1) ? 1 : 0);
}
}
}
@@ -368,13 +366,6 @@ uint32_t app(uint32_t param1, uint32_t param2)
}
}
// Fill tranZPUter memory or mainboard memory with a fixed value to initialise memory before an upload?
//
else if(fillByte != 0xFFFF)
{
fillZ80Memory(memAddr, memSize, (uint8_t)fillByte, mainboard_flag);
}
else
{
if(downloadFNLen > 0)
@@ -383,15 +374,22 @@ uint32_t app(uint32_t param1, uint32_t param2)
{
printf("Saving %s memory at address:%06lx into file:%s\n", (mainboard_flag == 0 ? "tranZPUter" : "mainboard"), memAddr, downloadFile);
}
saveZ80Memory(downloadFile, memAddr, memSize, mainboard_flag);
saveZ80Memory(downloadFile, memAddr, memSize, 0, mainboard_flag);
}
if(uploadFNLen > 0)
{
if(verbose_flag)
{
printf("Loading file:%s into the %s at address:%06lx\n", uploadFile, (mainboard_flag == 0 ? "tranZPUter" : "mainboard"), memAddr);
printf("Loading file:%s into the %s memory\n", uploadFile, (mainboard_flag == 0 ? "tranZPUter" : "mainboard"));
}
if(mzf_flag == 0)
{
loadZ80Memory(uploadFile, 0, memAddr, 0, mainboard_flag, 1);
} else
{
loadMZFZ80Memory(uploadFile, memAddr, mainboard_flag, 1);
}
loadZ80Memory(uploadFile, memAddr, mainboard_flag, 1);
}
}

View File

@@ -38,6 +38,7 @@
APP_NAME = tzpu
APP_DIR = $(CURDIR)/..
APP_COMMON_DIR = $(CURDIR)/../common
COMMON_DIR = $(CURDIR)/../../common
BASEDIR = ../../..
TEENSYDIR = ../../teensy3/
@@ -49,8 +50,8 @@ ifeq ($(__K64F__),1)
#override STACKSIZE = 0x00000000
# Modules making up tzpu.
APP_C_SRC = $(APP_COMMON_DIR)/pins_teensy.c $(APP_COMMON_DIR)/analog.c $(APP_COMMON_DIR)/tranzputer.c #$(TEENSYDIR)/yield.cpp
CFLAGS =
APP_C_SRC = $(APP_COMMON_DIR)/pins_teensy.c $(APP_COMMON_DIR)/analog.c $(COMMON_DIR)/tranzputer.c
CFLAGS = #-D__TZPU_DEBUG__
CPPFLAGS =
LDFLAGS =
LIBS =

View File

@@ -41,7 +41,7 @@
#include <unistd.h>
#include <stdarg.h>
#include <usb_serial.h>
#include <core_pins.h>
#include <core_pins.h>
#include <Arduino.h>
#include "k64f_soc.h"
#include <../../libraries/include/stdmisc.h>
@@ -123,26 +123,18 @@ uint32_t app(uint32_t param1, uint32_t param2)
//{
// printf("Usage: tzpu <file>\n");
//}
_init_Teensyduino_internal_();
setupPins(G->millis);
// _init_Teensyduino_internal_();
// setupZ80Pins(1, G->millis);
printf("Loading Monitor ROM\n");
loadZ80Memory("SA1510.rom", 0x00000000, 0, 1);
loadZ80Memory("SA1510.rom", 0, 0x00000000, 0, 0, 1);
printf("Loading Floppy ROM\n");
loadZ80Memory("1Z-013A.rom", 0x0000F000, 0, 1);
loadZ80Memory("1Z-013A.rom", 0, 0x0000F000, 0, 0, 1);
printf("Testing Display\n");
testBus();
displaySignals();
pinMode(13, OUTPUT);
while (1) {
digitalWriteFast(13, HIGH);
delay(500);
digitalWriteFast(13, LOW);
delay(500);
}
//displaySignals();
return(retCode);
}

84
apps/tzreset/Makefile Executable file
View File

@@ -0,0 +1,84 @@
#########################################################################################################
##
## Name: Makefile
## Created: July 2019
## Author(s): Philip Smart
## Description: App Makefile - Build an App for the ZPU Test Application (zputa) or the zOS
## operating system.
## This makefile builds an app which is stored on an SD card and called by ZPUTA/zOS
## The app is for testing some component where the code is not built into ZPUTA or
## a user application for zOS.
##
## Credits:
## Copyright: (c) 2019-20 Philip Smart <philip.smart@net2net.org>
##
## History: July 2019 - Initial Makefile created for template use.
## April 2020 - Added K64F as an additional target and resplit ZPUTA into zOS.
##
## Notes: Optional component enables:
## USELOADB - The Byte write command is implemented in hw#sw so use it.
## USE_BOOT_ROM - The target is ROM so dont use initialised data.
## MINIMUM_FUNTIONALITY - Minimise functionality to limit code size.
##
#########################################################################################################
## This source file is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This source file is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
#########################################################################################################
APP_NAME = tzreset
APP_DIR = $(CURDIR)/..
APP_COMMON_DIR = $(CURDIR)/../common
COMMON_DIR = $(CURDIR)/../../common
BASEDIR = ../../..
TEENSYDIR = ../../teensy3/
# Override values given by parent make for this application as its memory usage differs from the standard app.
ifeq ($(__K64F__),1)
#override HEAPADDR = 0x2002f000
#override HEAPSIZE = 0x00000000
#override STACKADDR = 0x2002f000
#override STACKSIZE = 0x00000000
# Modules making up tzpu.
APP_C_SRC = $(APP_COMMON_DIR)/pins_teensy.c $(APP_COMMON_DIR)/analog.c $(COMMON_DIR)/tranzputer.c
CFLAGS =
CPPFLAGS =
LDFLAGS =
LIBS =
else
# Modules making up tcpu.
APP_C_SRC = #$(APP_COMMON_DIR)/sysutils.c $(APP_COMMON_DIR)/ctypelocal.c
CFLAGS =
CPPFLAGS =
LDFLAGS = -nostdlib
LIBS = -lumansi-zpu -limath-zpu
endif
# Filter out the standard HEAP address and size, replacing with the ones required for this application.
# Useful for sub-makes
FILTER1 = $(filter-out $(filter HEAPADDR=%,$(MAKEFLAGS)), $(MAKEFLAGS))
FILTER2 = $(filter-out $(filter HEAPSIZE=%,$(FILTER1)), $(FILTER1))
NEWMAKEFLAGS = $(FILTER2) HEAPADDR=$(HEADADDR) HEAPSIZE=$(HEAPSIZE)
ifeq ($(__K64F__),1)
include $(APP_DIR)/Makefile.k64f
else
# There currently is no code for the ZPU, all development being done on the K64F for this app.
all:
clean:
install:
endif

201
apps/tzreset/tzreset.c Normal file
View File

@@ -0,0 +1,201 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tzreset.c
// Created: May 2020
// Author(s): Philip Smart
// Description: A TranZPUter helper utility, allowing a remote hardware reset of the tranZPUter
// board and host (not K64F).
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__K64F__)
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <usb_serial.h>
#include <core_pins.h>
#include <Arduino.h>
#include <getopt.h>
#include "k64f_soc.h"
#include <../../libraries/include/stdmisc.h>
#elif defined(__ZPU__)
#include <stdint.h>
#include <stdio.h>
#include "zpu_soc.h"
#include <stdlib.h>
#include <ctype.h>
#include <stdmisc.h>
#else
#error "Target CPU not defined, use __ZPU__ or __K64F__"
#endif
#include "interrupts.h"
#include "ff.h" /* Declarations of FatFs API */
#include "utils.h"
//
#if defined __ZPUTA__
#include "zputa_app.h"
#elif defined __ZOS__
#include "zOS_app.h"
#else
#error OS not defined, use __ZPUTA__ or __ZOS__
#endif
//
#include <app.h>
#include <tranzputer.h>
#include "tzreset.h"
// Utility functions.
#include <tools.c>
// Version info.
#define VERSION "v1.0"
#define VERSION_DATE "15/05/2020"
#define APP_NAME "TZRESET"
// Simple help screen to remmber how this utility works!!
//
void usage(void)
{
printf("%s %s\n", APP_NAME, VERSION);
printf("\nCommands:-\n");
printf(" -h | --help This help text.\n");
printf(" -r | --reset Perform a hardware reset.\n");
printf(" -l | --load Reload the default ROMS.\n");
printf("\nOptions:-\n");
printf(" -v | --verbose Output more messages.\n");
printf("\nExamples:\n");
printf(" tzreset -r # Resets the Z80 and associated tranZPUter logic..\n");
}
// Main entry and start point of a zOS/ZPUTA Application. Only 2 parameters are catered for and a 32bit return code, additional parameters can be added by changing the appcrt0.s
// startup code to add them to the stack prior to app() call.
//
// Return code for the ZPU is saved in _memreg by the C compiler, this is transferred to _memreg in zOS/ZPUTA in appcrt0.s prior to return.
// The K64F ARM processor uses the standard register passing conventions, return code is stored in R0.
//
uint32_t app(uint32_t param1, uint32_t param2)
{
// Initialisation.
//
int argc = 0;
int help_flag = 0;
int load_flag = 0;
int reset_flag = 0;
int verbose_flag = 0;
int opt;
int option_index = 0;
long val = 0;
char *argv[20];
char *ptr = strtok((char *)param1, " ");
// Initialisation.
// If the invoking command is given, add it to argv at the start.
//
if(param2 != 0)
{
argv[argc++] = (char *)param2;
}
// Now convert the parameter line into argc/argv suitable for getopt to use.
while (ptr && argc < 20-1)
{
argv[argc++] = ptr;
ptr = strtok(0, " ");
}
argv[argc] = 0;
// Define parameters to be processed.
static struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{"load", no_argument, 0, 'l'},
{"reset", no_argument, 0, 'r'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
// Parse the command line options.
//
while((opt = getopt_long(argc, argv, ":hlrv", long_options, &option_index)) != -1)
{
switch(opt)
{
case 'h':
help_flag = 1;
break;
case 'l':
load_flag = 1;
break;
case 'r':
reset_flag = 1;
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 || reset_flag == 0)
{
usage();
return(0);
}
// Initialise the IO.
setupZ80Pins(1, G->millis);
// Call the reset method to do the hard work.
//
resetZ80();
// Reload the memory on the tranZPUter to boot default.
if(load_flag)
{
loadTranZPUterDefaultROMS();
}
return(0);
}
#ifdef __cplusplus
}
#endif

46
apps/tzreset/tzreset.h Executable file
View File

@@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tzreset.h
// Created: May 2020
// Author(s): Philip Smart
// Description: A TranZPUter helper utility, allowing a remote hardware reset of the tranZPUter
// and host (not K64F).
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef TZRESET_H
#define TZRESET_H
#ifdef __cplusplus
extern "C" {
#endif
// Components to be embedded in the program.
//
// Filesystem components to be embedded in the program.
// Application execution constants.
//
#ifdef __cplusplus
}
#endif
#endif // TZRESET_H

View File

@@ -23,6 +23,7 @@
# -s <size> = Required size of application stack
# -a <size> = Maximum size of an app, defaults to (BRAM SIZE - App Start Address - Stack Size)
# if the App Start is located within BRAM otherwise defaults to 0x10000.
# -T = TranZPUter specific build, adds initialisation and setup code.
# -d = Debug mode.
# -x = Shell trace mode.
# -h = This help screen.
@@ -247,12 +248,13 @@ APP_HEAP_SIZE=0x1000;
APP_STACK_SIZE=0x400;
OS_HEAP_SIZE=0x4000;
OS_STACK_SIZE=0x1000;
TRANZPUTER=0
OSVER=2;
# Process parameters, loading up variables as necessary.
#
if [ $# -gt 0 ]; then
while getopts ":hC:I:O:o:M:B:A:N:n:S:s:da:x" opt; do
while getopts ":hC:I:O:o:M:B:A:N:n:S:s:da:xT" opt; do
case $opt in
d) DEBUGMODE=1;;
C) CPU=`echo ${OPTARG} | tr 'a-z' 'A-Z'`;;
@@ -267,6 +269,7 @@ if [ $# -gt 0 ]; then
S) getHex ${OPTARG} OS_STACK_SIZE;;
s) getHex ${OPTARG} APP_STACK_SIZE;;
a) getHex ${OPTARG} APP_LEN;;
T) TRANZPUTER=1;;
x) set -x; TRACEMODE=1;;
h) Usage;;
\?) FatalUsage "Unknown option: -${OPTARG}";;
@@ -313,6 +316,13 @@ if [ "${CPU}" = "K64F" -a "${OSVER}" != "2" ]; then
Fatal "-o <os ver> has no meaning for the K64F, base is in Flash RAM and applications, if SD card enabled, are in RAM."
fi
# Setup any specific build options.
if [ ${TRANZPUTER} -eq 1 ]; then
if [ "${CPU}" = "K64F" -a "${OS}" = "ZOS" ]; then
BUILDFLAGS="__TRANZPUTER__=1"
fi
fi
# Clear out the build target directory.
rm -fr ${BUILDPATH}/build
mkdir -p ${BUILDPATH}/build
@@ -483,7 +493,7 @@ else
# Calculate the heap, stack and RAM start address vars.
OS_RAM_ENDADDR=0x20030000
OS_RAM_MASK=0x3FFFF000
OS_RAM_OSMEM=0x004000
OS_RAM_OSMEM=0x005000
subHex ${OS_RAM_ENDADDR} 8 OS_STACK_ENDADDR
subHex ${OS_RAM_ENDADDR} ${OS_STACK_SIZE} OS_STACK_STARTADDR
roundHex ${OS_STACK_STARTADDR} ${OS_RAM_MASK} OS_STACK_STARTADDR
@@ -546,17 +556,17 @@ if [ "${OS}" = "ZPUTA" ]; then
-e "s/STACK_ADDR/${OS_STACK_STARTADDR}/g" > ${BUILDPATH}/startup/${OSBUILDSTR}.ld
cd ${BUILDPATH}/zputa
make ${CPUTYPE}=1 ${OSTYPE}=1 clean
make ${CPUTYPE}=1 ${OSTYPE}=1 ${BUILDFLAGS} clean
if [ $? != 0 ]; then
Fatal "Aborting, failed to clean ZPUTA build environment!"
fi
if [ "${CPUTYPE}" = "__ZPU__" ]; then
Log "make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU}"
make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU}
Log "make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} ${BUILDFLAGS}"
make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} ${BUILDFLAGS}
else
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU}"
make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU}
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} ${BUILDFLAGS}"
make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} ${BUILDFLAGS}
fi
if [ $? != 0 ]; then
Fatal "Aborting, failed to build ZPUTA!"
@@ -592,11 +602,11 @@ elif [ "${OS}" = "ZOS" ]; then
Fatal "Aborting, failed to clean zOS build environment!"
fi
if [ "${CPUTYPE}" = "__ZPU__" ]; then
Log "make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE}"
make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE}
Log "make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE} ${BUILDFLAGS}"
make ${OSBUILDSTR} ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE} ${BUILDFLAGS}
else
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE}"
make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE}
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE} ${BUILDFLAGS}"
make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} HEAPADDR=${OS_HEAP_STARTADDR} HEAPSIZE=${OS_HEAP_SIZE} STACKADDR=${OS_STACK_STARTADDR} STACKENDADDR=${OS_STACK_ENDADDR} STACKSIZE=${OS_STACK_SIZE} ${BUILDFLAGS}
fi
if [ $? != 0 ]; then
Fatal "Aborting, failed to build zOS!"
@@ -617,20 +627,20 @@ else
fi
cd ${BUILDPATH}/apps
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 clean"
make ${CPUTYPE}=1 ${OSTYPE}=1 clean
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 ${BUILDFLAGS} clean"
make ${CPUTYPE}=1 ${OSTYPE}=1 ${BUILDFLAGS} clean
if [ $? != 0 ]; then
Fatal "Aborting, failed to clean Apps build environment!"
fi
Log "make ${CPUTYPE} ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} TMPLFILE=${TMPLFILE} BASEADDR=${APP_BASEADDR} BASELEN=${APP_BOOTLEN} HEAPADDR=${APP_HEAP_STARTADDR} HEAPSIZE=${APP_HEAP_SIZE} STACKADDR=${APP_STACK_STARTADDR} STACKENDADDR=${APP_STACK_ENDADDR} STACKSIZE=${APP_STACK_SIZE} APPSTART=${APP_STARTADDR} APPSIZE=${APP_LEN}"
make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} TMPLFILE=${TMPLFILE} BASEADDR=${APP_BASEADDR} BASELEN=${APP_BOOTLEN} HEAPADDR=${APP_HEAP_STARTADDR} HEAPSIZE=${APP_HEAP_SIZE} STACKADDR=${APP_STACK_STARTADDR} STACKENDADDR=${APP_STACK_ENDADDR} STACKSIZE=${APP_STACK_SIZE} APPSTART=${APP_STARTADDR} APPSIZE=${APP_LEN}
Log "make ${CPUTYPE} ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} TMPLFILE=${TMPLFILE} BASEADDR=${APP_BASEADDR} BASELEN=${APP_BOOTLEN} HEAPADDR=${APP_HEAP_STARTADDR} HEAPSIZE=${APP_HEAP_SIZE} STACKADDR=${APP_STACK_STARTADDR} STACKENDADDR=${APP_STACK_ENDADDR} STACKSIZE=${APP_STACK_SIZE} APPSTART=${APP_STARTADDR} APPSIZE=${APP_LEN} ${BUILDFLAGS}"
make ${CPUTYPE}=1 ${OSTYPE}=1 OS_BASEADDR=${OS_BOOTADDR} OS_APPADDR=${APP_BASEADDR} CPU=${CPU} TMPLFILE=${TMPLFILE} BASEADDR=${APP_BASEADDR} BASELEN=${APP_BOOTLEN} HEAPADDR=${APP_HEAP_STARTADDR} HEAPSIZE=${APP_HEAP_SIZE} STACKADDR=${APP_STACK_STARTADDR} STACKENDADDR=${APP_STACK_ENDADDR} STACKSIZE=${APP_STACK_SIZE} APPSTART=${APP_STARTADDR} APPSIZE=${APP_LEN} ${BUILDFLAGS}
if [ $? != 0 ]; then
Fatal "Aborting, failed to build Apps!"
fi
mkdir -p bin
rm -f bin/*
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 install"
make ${CPUTYPE}=1 ${OSTYPE}=1 install
Log "make ${CPUTYPE}=1 ${OSTYPE}=1 ${BUILDFLAGS} install"
make ${CPUTYPE}=1 ${OSTYPE}=1 ${BUILDFLAGS} install
if [ $? != 0 ]; then
Fatal "Aborting, failed to install generated binaries!"
fi

View File

@@ -96,8 +96,14 @@
/ 0 - Include all code pages above and configured by f_setcp()
*/
// zOS running on the tranZPUter uses LFN.
//
//#if defined __TRANZPUTER__
#define FF_USE_LFN 1
//#else
//#define FF_USE_LFN 0
//#endif
#define FF_USE_LFN 0
#define FF_MAX_LFN 255
/* The FF_USE_LFN switches the support for LFN (long file name).
/

3036
common/tranzputer.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -103,6 +103,11 @@ extern "C" {
#define CMD_APP_MBASIC 141 // Mini Basic
#define CMD_APP_KILO 142 // Kilo Editor
#define CMD_APP_ED 143 // Ed Editor
#define CMD_TZ_ZPU 150 // tranZPUter interface/test.
#define CMD_TZ_LOAD 151 // tranZPUter memory load/save tool.
#define CMD_TZ_DUMP 152 // tranZPUter memory dump tool.
#define CMD_TZ_CLEAR 153 // tranZPUter memory clear tool.
#define CMD_TZ_RESET 154 // tranZPUter memory reset tool.
#define CMD_BADKEY -1
#define CMD_NOKEY 0
#define CMD_GROUP_DISK 1
@@ -114,6 +119,7 @@ extern "C" {
#define CMD_GROUP_EXEC 7
#define CMD_GROUP_MISC 8
#define CMD_GROUP_APP 9
#define CMD_GROUP_TZ 10
#define CMD_GROUP_DISK_NAME "DISK IO CONTROLS"
#define CMD_GROUP_BUFFER_NAME "DISK BUFFER CONTROLS"
#define CMD_GROUP_FS_NAME "FILESYSTEM CONTROLS"
@@ -123,6 +129,7 @@ extern "C" {
#define CMD_GROUP_EXEC_NAME "EXECUTION"
#define CMD_GROUP_MISC_NAME "MISC COMMANDS"
#define CMD_GROUP_APP_NAME "APPLICATIONS"
#define CMD_GROUP_TZ_NAME "TRANZPUTER"
// File Execution modes.
//
@@ -377,6 +384,13 @@ static t_cmdstruct cmdTable[] = {
{ "mbasic", BUILTIN_DEFAULT, CMD_APP_MBASIC, CMD_GROUP_APP },
{ "kilo", BUILTIN_DEFAULT, CMD_APP_KILO, CMD_GROUP_APP },
{ "ed", BUILTIN_DEFAULT, CMD_APP_ED, CMD_GROUP_APP },
#if defined __TRANZPUTER__
{ "tzpu", BUILTIN_DEFAULT, CMD_TZ_TZPU, CMD_GROUP_TZ },
{ "tzload", BUILTIN_DEFAULT, CMD_TZ_LOAD, CMD_GROUP_TZ },
{ "tzdump", BUILTIN_DEFAULT, CMD_TZ_DUMP, CMD_GROUP_TZ },
{ "tzclear", BUILTIN_DEFAULT, CMD_TZ_CLEAR, CMD_GROUP_TZ },
{ "tzreset", BUILTIN_DEFAULT, CMD_TZ_RESET, CMD_GROUP_TZ },
#endif
};
#endif
@@ -477,6 +491,14 @@ static t_helpstruct helpTable[] = {
{ CMD_APP_MBASIC, "[<file]>", "Mini Basic" },
{ CMD_APP_KILO, "<file>", "VT100 editor" },
{ CMD_APP_ED, "<file>", "Minimal VT100 editor" },
#if defined __TRANZPUTER__
// TranZPUter commands.
{ CMD_TZ_TZPU, "--help", "Testing tool" },
{ CMD_TZ_LOAD, "--help", "Memory load/save tool" },
{ CMD_TZ_DUMP, "--help", "Memory dump tool" },
{ CMD_TZ_CLEAR, "--help", "Memory clearing tool" },
{ CMD_TZ_RESET, "--help", "Remote reset tool" },
#endif
};
#endif
#define NGRPKEYS (sizeof(groupTable)/sizeof(t_groupstruct))

571
include/tranzputer.h Executable file
View File

@@ -0,0 +1,571 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: tranzputer.h
// Created: May 2020
// Author(s): Philip Smart
// Description: The TranZPUter library.
// This file contains methods which allow applications to access and control the traZPUter board and the underlying Sharp MZ80A host.
// Credits:
// Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
//
// History: May 2020 - Initial write of the TranZPUter software.
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef TRANZPUTER_H
#define TRANZPUTER_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.
// 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 0x14 // CPM main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used as the static CBIOS and F000-FFFF is the paged CBIOS, TPA is from 0000-D000(BDOS+CCP = 1800), all is in 64K Block 4, F000-FFFF is in 64K Block 4.
#define TZMM_CPM2 0x15 // CPM main memory configuration. E800-EFFF and TPA 0000-D000 are in tranZPUter RAM 64K block 4, CBIOS2 F000-FFFF is in 64K block 5 and video and memory control D000-E7FF are on the mainboard.
#define TZMM_CPM3 0x16 // CPM main memory configuration. E800-EFFF and TPA 0000-D000 are in tranZPUter RAM 64K block 4, CBIOS2 F000-FFFF is in 64K block 6 and video and memory control D000-E7FF are on the mainboard.
#define TZMM_CPM4 0x17 // CPM main memory configuration. E800-EFFF and TPA 0000-D000 are in tranZPUter RAM 64K block 4, CBIOS2 F000-FFFF is in 64K block 7 and video and memory control D000-E7FF are on the mainboard.
#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.
// Sharp MZ80A constants.
//
#define MZ_MROM_ADDR 0x0000 // Monitor ROM start address.
#define MZ_MROM_STACK_ADDR 0x1000 // Monitor ROM start stack address.
#define MZ_MROM_STACK_SIZE 0x0200 // Monitor ROM stack size.
#define MZ_UROM_ADDR 0xE800 // User ROM start address.
#define MZ_BANKRAM_ADDR 0xF000 // Floppy API address which is used in TZFS as the paged RAM for additional functionality.
#define MZ_CMT_ADDR 0x10F0 // Address of the CMT (tape) header record.
#define MZ_CMT_DEFAULT_LOAD_ADDR 0x1200 // The default load address for a CMT, anything below this is normally illegal.
#define MZ_VID_RAM_ADDR 0xD000 // Start of Video RAM
#define MZ_VID_RAM_SIZE 2048 // Size of Video RAM.
#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_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_TZFS "TZFS.ROM" // tranZPUter Filing System ROM.
// Service request constants.
//
#define TZSVC_CMD_STRUCT_ADDR 0xEC80 // Address of the command structure.
#define TZSVC_CMD_STRUCT_SIZE 0x280 // Size of the inter z80/K64 service command memory.
#define TZSVC_CMD_SIZE 0x04+TZSVC_DIRNAME_SIZE+\
+TZSVC_FILENAME_SIZE+\
TZSVC_WILDCARD_SIZE // Size of the command/result portion of the control structure.
#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_MEXTREADFILE 0x04 // Service command to return the next block of an open file.
#define TZSVC_CMD_WRITEFILE 0x05 // Service command to create a file and write the first block into it.
#define TZSVC_CMD_NEXTWRITEFILE 0x06 // Service command to write the next block into 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_DEFAULT_DIR "MZF" // Default directory where MZF files are stored.
#define TZSVC_DEFAULT_EXT "MZF" // Default file extension for MZF files.
#define TZSVC_DEFAULT_WILDCARD "*" // Default wildcard file matching.
#define TZSVC_RESULT_OFFSET 0x01 // Offset into structure of the result byte.
#define TZSVC_DIRNAME_SIZE 8 // Limit is size of FAT32 directory name.
#define TZSVC_WILDCARD_SIZE 8 // Very basic pattern matching so small size.
#define TZSVC_FILENAME_SIZE MZF_FILENAME_LEN // Length of a Sharp MZF filename.
#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_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.
// 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.
// Pin Constants - Pins assigned at the hardware level to specific tasks/signals.
//
#define MAX_TRANZPUTER_PINS 50
#define Z80_MEM0_PIN 46
#define Z80_MEM1_PIN 47
#define Z80_MEM2_PIN 48
#define Z80_MEM3_PIN 49
#define Z80_MEM4_PIN 50
#define Z80_WR_PIN 10
#define Z80_RD_PIN 12
#define Z80_IORQ_PIN 8
#define Z80_MREQ_PIN 9
#define Z80_A0_PIN 39
#define Z80_A1_PIN 38
#define Z80_A2_PIN 37
#define Z80_A3_PIN 36
#define Z80_A4_PIN 35
#define Z80_A5_PIN 34
#define Z80_A6_PIN 33
#define Z80_A7_PIN 32
#define Z80_A8_PIN 31
#define Z80_A9_PIN 30
#define Z80_A10_PIN 29
#define Z80_A11_PIN 28
#define Z80_A12_PIN 27
#define Z80_A13_PIN 26
#define Z80_A14_PIN 25
#define Z80_A15_PIN 24
#define Z80_A16_PIN 23
#define Z80_A17_PIN 22
#define Z80_A18_PIN 21
#define Z80_D0_PIN 0
#define Z80_D1_PIN 1
#define Z80_D2_PIN 2
#define Z80_D3_PIN 3
#define Z80_D4_PIN 4
#define Z80_D5_PIN 5
#define Z80_D6_PIN 6
#define Z80_D7_PIN 7
#define Z80_WAIT_PIN 13
#define Z80_BUSACK_PIN 17
#define Z80_NMI_PIN 43
#define Z80_INT_PIN 44
#define Z80_RESET_PIN 54
#define SYSCLK_PIN 11
#define CTL_RFSH_PIN 45
#define CTL_HALT_PIN 18
#define CTL_M1_PIN 20
#define CTL_BUSRQ_PIN 15
#define CTL_BUSACK_PIN 16
#define CTL_CLK_PIN 14
#define CTL_CLKSLCT_PIN 19
#define TZ_BUSACK_PIN 55
// IRQ mask values for the different types of IRQ trigger.
//
#define IRQ_MASK_CHANGE 0x0B0040
#define IRQ_MASK_RISING 0x040040
#define IRQ_MASK_FALLING 0x0A0040
#define IRQ_MASK_LOW 0x080040
#define IRQ_MASK_HIGH 0x0C0040
// Customised pin manipulation methods implemented as stripped down macros. The original had too much additional overhead with procedure call and validation tests,
// speed is of the essence for this project as pins change mode and value constantly.
//
// Studying the Teensyduino code these macros could be stripped down further and go direct to the BITBAND registers if more speed is needed.
//
#define STR(x) #x
#define XSTR(s) STR(s)
#define pinLow(a) *portClearRegister(pinMap[a]) = 1
#define pinHigh(a) *portSetRegister(pinMap[a]) = 1
#define pinSet(a, b) if(b) { *portSetRegister(pinMap[a]) = 1; } else { *portClearRegister(pinMap[a]) = 1; }
#define pinGet(a) *portInputRegister(pinMap[a])
#define pinInput(a) { *portModeRegister(pinMap[a]) = 0; *ioPin[a] = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; }
#define pinOutput(a) { *portModeRegister(pinMap[a]) = 1;\
*ioPin[a] = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);\
*ioPin[a] &= ~PORT_PCR_ODE; }
#define pinOutputSet(a,b) { *portModeRegister(pinMap[a]) = 1;\
*ioPin[a] = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);\
*ioPin[a] &= ~PORT_PCR_ODE;\
if(b) { *portSetRegister(pinMap[a]) = 1; } else { *portClearRegister(pinMap[a]) = 1; } }
#define installIRQ(a, mask) { uint32_t cfg;\
__disable_irq();\
cfg = *ioPin[a];\
cfg &= ~0x000F0000;\
*ioPin[a] = cfg;\
cfg |= mask;\
*ioPin[a] = cfg;\
__enable_irq();\
}
#define pinIndex(a) getPinIndex(pinMap[a])
#define setZ80Data(a) { pinSet(Z80_D7, ((a >> 7) & 0x1)); pinSet(Z80_D6, ((a >> 6) & 0x1));\
pinSet(Z80_D5, ((a >> 5) & 0x1)); pinSet(Z80_D4, ((a >> 4) & 0x1));\
pinSet(Z80_D3, ((a >> 3) & 0x1)); pinSet(Z80_D2, ((a >> 2) & 0x1));\
pinSet(Z80_D1, ((a >> 1) & 0x1)); pinSet(Z80_D0, ((a ) & 0x1)); }
#define setZ80Addr(a) { pinSet(Z80_A15, ((a >> 15) & 0x1)); pinSet(Z80_A14, ((a >> 14) & 0x1));\
pinSet(Z80_A13, ((a >> 13) & 0x1)); pinSet(Z80_A12, ((a >> 12) & 0x1));\
pinSet(Z80_A11, ((a >> 11) & 0x1)); pinSet(Z80_A10, ((a >> 10) & 0x1));\
pinSet(Z80_A9, ((a >> 9) & 0x1)); pinSet(Z80_A8, ((a >> 8) & 0x1));\
pinSet(Z80_A7, ((a >> 7) & 0x1)); pinSet(Z80_A6, ((a >> 6) & 0x1));\
pinSet(Z80_A5, ((a >> 5) & 0x1)); pinSet(Z80_A4, ((a >> 4) & 0x1));\
pinSet(Z80_A3, ((a >> 3) & 0x1)); pinSet(Z80_A2, ((a >> 2) & 0x1));\
pinSet(Z80_A1, ((a >> 1) & 0x1)); pinSet(Z80_A0, ((a ) & 0x1)); }
#define setZ80RefreshAddr(a) { pinSet(Z80_A6, ((a >> 6) & 0x1)); pinSet(Z80_A5, ((a >> 5) & 0x1));\
pinSet(Z80_A4, ((a >> 4) & 0x1)); pinSet(Z80_A3, ((a >> 3) & 0x1));\
pinSet(Z80_A2, ((a >> 2) & 0x1)); pinSet(Z80_A1, ((a >> 1) & 0x1));\
pinSet(Z80_A0, ((a ) & 0x1)); }
#define readZ80AddrLower() ( pinGet(Z80_A7) << 7 | pinGet(Z80_A6) << 6 | pinGet(Z80_A5) << 5 | pinGet(Z80_A4) << 4 |\
pinGet(Z80_A3) << 3 | pinGet(Z80_A2) << 2 | pinGet(Z80_A1) << 1 | pinGet(Z80_A0) )
#define readZ80Addr(a) ( pinGet(Z80_A15) << 15 | pinGet(Z80_A14) << 14 | pinGet(Z80_A13) << 13 | pinGet(Z80_A12) << 12 |\
pinGet(Z80_A11) << 11 | pinGet(Z80_A10) << 10 | pinGet(Z80_A9) << 9 | pinGet(Z80_A8) << 8 |\
pinGet(Z80_A7) << 7 | pinGet(Z80_A6) << 6 | pinGet(Z80_A5) << 5 | pinGet(Z80_A4) << 4 |\
pinGet(Z80_A3) << 3 | pinGet(Z80_A2) << 2 | pinGet(Z80_A1) << 1 | pinGet(Z80_A0) )
#define readDataBus() ( pinGet(Z80_D7) << 7 | pinGet(Z80_D6) << 6 | pinGet(Z80_D5) << 5 | pinGet(Z80_D4) << 4 |\
pinGet(Z80_D3) << 3 | pinGet(Z80_D2) << 2 | pinGet(Z80_D1) << 1 | pinGet(Z80_D0) )
#define readCtrlLatch() ( (pinGet(Z80_MEM4) << 4 | pinGet(Z80_MEM3) << 3 | pinGet(Z80_MEM2) << 2 | pinGet(Z80_MEM1) << 1 | pinGet(Z80_MEM0)) & 0x1F )
#define writeCtrlLatch(a) { writeZ80IO(IO_TZ_CTRLLATCH, a); }
#define setZ80Direction(a) { for(uint8_t idx=Z80_D0; idx <= Z80_D7; idx++) { if(a == WRITE) { pinOutput(idx); } else { pinInput(idx); } }; z80Control.busDir = a; }
#define reqZ80BusChange(a) { if(a == MAINBOARD_ACCESS && z80Control.ctrlMode == TRANZPUTER_ACCESS) \
{\
pinHigh(CTL_BUSACK);\
z80Control.ctrlMode = MAINBOARD_ACCESS;\
z80Control.curCtrlLatch = 0b00000000;\
writeCtrlLatch(z80Control.curCtrlLatch);\
} else if(a == TRANZPUTER_ACCESS && z80Control.ctrlMode == MAINBOARD_ACCESS)\
{\
pinLow(CTL_BUSACK);\
z80Control.ctrlMode = TRANZPUTER_ACCESS;\
z80Control.curCtrlLatch = 0b00011111;\
writeCtrlLatch(z80Control.curCtrlLatch);\
} }
// Lower level macro without pin mapping as this is called in the ResetHandler to halt the Z80 whilst the K64F starts up and is able to load up tranZPUter software.
#define holdZ80() { \
*portModeRegister(CTL_BUSRQ_PIN) = 1; \
*portConfigRegister(CTL_BUSRQ_PIN) = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); \
*portConfigRegister(CTL_BUSRQ_PIN) &= ~PORT_PCR_ODE; \
*portClearRegister(CTL_BUSRQ_PIN) = 1; \
}
// Enumeration of the various pins on the project. These enums make it easy to refer to a signal and they are mapped
// to the actual hardware pin via the pinMap array.
// One of the big advantages is that a swath of pins, such as the address lines, can be switched in a tight loop rather than
// individual pin assignments or clunky lists.
//
enum pinIdxToPinNumMap {
Z80_A0 = 0,
Z80_A1 = 1,
Z80_A2 = 2,
Z80_A3 = 3,
Z80_A4 = 4,
Z80_A5 = 5,
Z80_A6 = 6,
Z80_A7 = 7,
Z80_A8 = 8,
Z80_A9 = 9,
Z80_A10 = 10,
Z80_A11 = 11,
Z80_A12 = 12,
Z80_A13 = 13,
Z80_A14 = 14,
Z80_A15 = 15,
Z80_A16 = 16,
Z80_A17 = 17,
Z80_A18 = 18,
Z80_D0 = 19,
Z80_D1 = 20,
Z80_D2 = 21,
Z80_D3 = 22,
Z80_D4 = 23,
Z80_D5 = 24,
Z80_D6 = 25,
Z80_D7 = 26,
Z80_MEM0 = 27,
Z80_MEM1 = 28,
Z80_MEM2 = 29,
Z80_MEM3 = 30,
Z80_MEM4 = 31,
Z80_IORQ = 32,
Z80_MREQ = 33,
Z80_RD = 34,
Z80_WR = 35,
Z80_WAIT = 36,
Z80_BUSACK = 37,
Z80_NMI = 38,
Z80_INT = 39,
Z80_RESET = 40,
MB_SYSCLK = 41,
TZ_BUSACK = 42,
CTL_BUSACK = 43,
CTL_BUSRQ = 44,
CTL_RFSH = 45,
CTL_HALT = 46,
CTL_M1 = 47,
CTL_CLK = 48,
CTL_CLKSLCT = 49
};
// Possible control modes that the K64F can be in, do nothing where the Z80 runs normally, control the Z80 and mainboard, or control the Z80 and tranZPUter.
enum CTRL_MODE {
Z80_RUN = 0,
TRANZPUTER_ACCESS = 1,
MAINBOARD_ACCESS = 2
};
// Possible bus directions that the K64F can setup for controlling the Z80.
enum BUS_DIRECTION {
READ = 0,
WRITE = 1,
TRISTATE = 2
};
// Possible video frames stored internally.
//
enum VIDEO_FRAMES {
SAVED = 0,
WORKING = 1
};
// 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 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.
char directory[TZSVC_DIRNAME_SIZE]; // Directory this mapping is associated with.
t_sharpToSDMap *file[TZSVC_MAX_DIR_ENTRIES]; // File mapping of SD file to its Sharp MZ80A name.
} t_dirMap;
// 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 {
#ifndef __APP__
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.
uint8_t runCtrlLatch; // Latch value the Z80 is running with.
uint8_t curCtrlLatch; // Latch value set during tranZPUter access of the Z80 bus.
uint8_t videoRAM[2][2048]; // Two video memory buffer frames, allows for storage of original frame in [0] and working frame in [1].
uint8_t attributeRAM[2][2048]; // Two attribute memory buffer frames, allows for storage of original frame in [0] and working frame in [1].
enum CTRL_MODE ctrlMode; // Mode of control, ie normal Z80 Running, controlling mainboard, controlling tranZPUter.
enum BUS_DIRECTION busDir; // Direction the bus has been configured for.
uint8_t resetEvent; // A Z80_RESET event occurred, probably user pressing RESET button.
uint8_t ioAddr; // Address of a Z80 IO instruction.
uint8_t ioData; // Data of a Z80 IO instruction.
uint8_t ioEvent; // Event flag to indicate that an IO instruction was captured.
uint8_t memorySwap; // A memory Swap event has occurred, 0000-0FFF -> C000-CFFF (1), or C000-CFFF -> 0000-0FFF (0)
uint8_t crtMode; // A CRT event has occurred, Normal mode (0) or Reverse Mode (1)
uint8_t scroll; // Hardware scroll offset.
volatile uint32_t portA;
volatile uint32_t portB;
volatile uint32_t portC;
volatile uint32_t portD;
volatile uint32_t portE;
#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_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.
};
// uint16_t sectorNo; // Sector number of file to retrieve.
uint8_t fileNo; // File number of a file within the last directory listing to open/update.
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;
// Application execution constants.
//
// For the ARM Cortex-M compiler, the standard filestreams in an app are set by the CRT0 startup code,
// the original reentrant definition is undefined as it is not needed in the app.
#if defined __APP__ && defined __K64F__
#undef stdout
#undef stdin
#undef stderr
FILE *stdout;
FILE *stdin;
FILE *stderr;
#endif
// References to variables within the main library code.
extern volatile uint32_t *ioPin[MAX_TRANZPUTER_PINS];
extern uint8_t pinMap[MAX_TRANZPUTER_PINS];
// Prototypes.
//
#if defined __APP__
void yield(void);
#endif
void setupZ80Pins(uint8_t, volatile uint32_t *);
void resetZ80(void);
uint8_t reqZ80Bus(uint32_t);
uint8_t reqMainboardBus(uint32_t);
uint8_t reqTranZPUterBus(uint32_t);
void setupSignalsForZ80Access(enum BUS_DIRECTION);
void releaseZ80(void);
void refreshZ80(void);
void setCtrlLatch(uint8_t);
uint8_t copyFromZ80(uint8_t *, uint32_t, uint32_t, uint8_t);
uint8_t copyToZ80(uint32_t, uint8_t *, uint32_t, uint8_t);
uint8_t writeZ80Memory(uint16_t, uint8_t);
uint8_t readZ80Memory(uint16_t);
uint8_t writeZ80IO(uint16_t, uint8_t);
uint8_t readZ80IO(uint16_t);
void fillZ80Memory(uint32_t, uint32_t, uint8_t, uint8_t);
void captureVideoFrame(enum VIDEO_FRAMES, uint8_t);
void refreshVideoFrame(enum VIDEO_FRAMES, uint8_t, uint8_t);
FRESULT loadVideoFrameBuffer(char *, enum VIDEO_FRAMES);
FRESULT saveVideoFrameBuffer(char *, enum VIDEO_FRAMES);
char *getVideoFrame(enum VIDEO_FRAMES);
char *getAttributeFrame(enum VIDEO_FRAMES);
FRESULT loadZ80Memory(const char *, uint32_t, uint32_t, uint32_t, uint8_t, uint8_t);
FRESULT saveZ80Memory(const char *, uint32_t, uint32_t, t_svcDirEnt *, uint8_t);
FRESULT loadMZFZ80Memory(const char *, uint32_t, uint8_t, uint8_t);
// Getter/Setter methods!
uint8_t isZ80Reset(void);
uint8_t isZ80MemorySwapped(void);
uint8_t getZ80IO(uint8_t *, uint8_t *);
void clearZ80Reset(void);
void convertSharpFilenameToAscii(char *, char *, uint8_t);
// tranZPUter OS i/f methods.
uint8_t setZ80SvcStatus(uint8_t);
void svcSetDefaults(void);
uint8_t svcReadDir(uint8_t);
uint8_t svcFindFile(char *, char *, uint32_t);
uint8_t svcReadDirCache(uint8_t);
uint8_t svcFindFileCache(char *, char *, uint32_t);
uint8_t svcCacheDir(const char *, uint8_t);
uint8_t svcReadFile(uint8_t);
uint8_t svcLoadFile(void);
uint8_t svcEraseFile(void);
void processServiceRequest(void);
void loadTranZPUterDefaultROMS(void);
void tranZPUterControl(void);
uint8_t testTZFSAutoBoot(void);
void setupTranZPUter(void);
#if defined __APP__
int memoryDumpZ80(uint32_t, uint32_t, uint32_t, uint8_t, uint8_t);
#endif
// Debug methods.
#if defined __APP__ && defined __TZPU_DEBUG__
void displaySignals(void);
#endif
#ifdef __cplusplus
}
#endif
#endif // TRANZPUTER_H

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -273,18 +273,18 @@ BSS_END: .word __bss_section_end__
#defapifunc f_printf funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc f_gets funcAddr
#
# Low level disk calls.
#
#
# Low level disk calls.
#
.equ funcAddr, funcAddr+funcNext;
defapifunc disk_read funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc disk_write funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc disk_ioctl funcAddr
#
# Miscellaneous calls.
#
#
# Miscellaneous calls.
#
.equ funcAddr, funcAddr+funcNext;
defapifunc getStrParam funcAddr
.equ funcAddr, funcAddr+funcNext;
@@ -306,4 +306,69 @@ BSS_END: .word __bss_section_end__
.equ funcAddr, funcAddr+funcNext;
defapifunc sys_free funcAddr
# tranZPUter kernel methods.
.equ funcAddr, funcAddr+funcNext;
defapifunc setupZ80Pins funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc resetZ80 funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc reqZ80Bus funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc reqMainboardBus funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc reqTranZPUterBus funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc setupSignalsForZ80Access funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc releaseZ80 funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc writeZ80Memory funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc readZ80Memory funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc writeZ80IO funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc readZ80IO funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc refreshZ80 funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc refreshZ80AllRows funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc setCtrlLatch funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc copyFromZ80 funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc copyToZ80 funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc fillZ80Memory funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc captureVideoFrame funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc refreshVideoFrame funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc loadVideoFrameBuffer funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc saveVideoFrameBuffer funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc getVideoFrame funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc getAttributeFrame funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc loadZ80Memory funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc loadMZFZ80Memory funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc saveZ80Memory funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc memoryDumpZ80 funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc isZ80Reset funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc isZ80MemorySwapped funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc getZ80IO funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc clearZ80Reset funcAddr
.equ funcAddr, funcAddr+funcNext;
defapifunc loadTranZPUterDefaultROMS funcAddr
.end

View File

@@ -36,6 +36,11 @@
#include "usb_ser_print.h" // testing only
#include <errno.h>
#if defined __TRANZPUTER__
#define FRESULT uint8_t
#include <tranzputer.h>
#endif
// Flash Security Setting. On Teensy 3.2, you can lock the MK20 chip to prevent
// anyone from reading your code. You CAN still reprogram your Teensy while
@@ -979,6 +984,44 @@ void _ZPUTA_Vectors(void)
__asm__ volatile ("b realloc");
__asm__ volatile ("b calloc");
__asm__ volatile ("b free");
#if defined __TRANZPUTER__
//
// tranZPUter methods which need to be called via the kernel not accessed by the application directly.
//
__asm__ volatile ("b setupZ80Pins");
__asm__ volatile ("b resetZ80");
__asm__ volatile ("b reqZ80Bus");
__asm__ volatile ("b reqMainboardBus");
__asm__ volatile ("b reqTranZPUterBus");
__asm__ volatile ("b setupSignalsForZ80Access");
__asm__ volatile ("b releaseZ80");
__asm__ volatile ("b writeZ80Memory");
__asm__ volatile ("b readZ80Memory");
__asm__ volatile ("b writeZ80IO");
__asm__ volatile ("b readZ80IO");
__asm__ volatile ("b refreshZ80");
__asm__ volatile ("b refreshZ80AllRows");
__asm__ volatile ("b setCtrlLatch");
__asm__ volatile ("b copyFromZ80");
__asm__ volatile ("b copyToZ80");
__asm__ volatile ("b fillZ80Memory");
__asm__ volatile ("b captureVideoFrame");
__asm__ volatile ("b refreshVideoFrame");
__asm__ volatile ("b loadVideoFrameBuffer");
__asm__ volatile ("b saveVideoFrameBuffer");
__asm__ volatile ("b getVideoFrame");
__asm__ volatile ("b getAttributeFrame");
__asm__ volatile ("b loadZ80Memory");
__asm__ volatile ("b loadMZFZ80Memory");
__asm__ volatile ("b saveZ80Memory");
__asm__ volatile ("b memoryDumpZ80");
__asm__ volatile ("b isZ80Reset");
__asm__ volatile ("b isZ80MemorySwapped");
__asm__ volatile ("b getZ80IO");
__asm__ volatile ("b clearZ80Reset");
__asm__ volatile ("b loadTranZPUterDefaultROMS");
#endif
}
// Automatically initialize the RTC. When the build defines the compile
@@ -999,6 +1042,7 @@ static void startup_default_early_hook(void) {
#endif
}
static void startup_default_late_hook(void) {}
void startup_early_hook(void) __attribute__ ((weak, alias("startup_default_early_hook")));
void startup_late_hook(void) __attribute__ ((weak, alias("startup_default_late_hook")));
@@ -1068,6 +1112,7 @@ void ResetHandler(void)
UART0_C2 = UART_C2_TE;
PORTB_PCR17 = PORT_PCR_MUX(3);
#endif
#if defined(KINETISK) && !defined(__MK66FX1M0__)
// If the RTC oscillator isn't enabled, get it started early.
// But don't do this early on Teensy 3.6 - RTC_CR depends on 3.3V+VBAT
@@ -1087,12 +1132,12 @@ void ResetHandler(void)
#else
SMC_PMPROT = SMC_PMPROT_AVLP | SMC_PMPROT_ALLS | SMC_PMPROT_AVLLS;
#endif
// TODO: do this while the PLL is waiting to lock....
while (dest < &_edata) *dest++ = *src++;
dest = &_sbss;
while (dest < &_ebss) *dest++ = 0;
// default all interrupts to medium priority level
for (i=0; i < NVIC_NUM_INTERRUPTS + 16; i++) _VectorsRam[i] = _VectorsFlash[i];
for (i=0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_PRIORITY(i, 128);
@@ -1439,6 +1484,16 @@ void ResetHandler(void)
SYST_CVR = 0;
SYST_CSR = SYST_CSR_CLKSOURCE | SYST_CSR_TICKINT | SYST_CSR_ENABLE;
SCB_SHPR3 = 0x20200000; // Systick = priority 32
// Earliest point when the K64F can change the GPIO pins.
// This macro sets the CTL_BUSRQ signal as an output and low to halt the Z80 from going through its startup
// procedure. This then gives the needed time for the K64F to startup and bring the SD card online so
// that the tranZPUter board can be configured before releasing the Z80.
//
//#if defined __TRANZPUTER__
// holdZ80();
//#endif
//init_pins();
__enable_irq();
@@ -1477,7 +1532,6 @@ void ResetHandler(void)
#endif
__libc_init_array();
startup_late_hook();
main();

View File

@@ -39,7 +39,7 @@ ENTRY(_VectorsFlash)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00080000
RAM (rwx) : ORIGIN = 0x2001A000, LENGTH = 0x00016000
RAM (rwx) : ORIGIN = 0x20019000, LENGTH = 0x00017000
}
/*
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K

187
teensy3/TeensyThreads-asm.s Normal file
View File

@@ -0,0 +1,187 @@
/*
* Threads-asm.S - Library for threading on the Teensy.
*
*******************
*
* Copyright 2017 by Fernando Trias.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*******************
*
* context_switch() changes the context to a new thread. It follows this strategy:
*
* 1. Abort if called from within an interrupt (unless using PIT)
* 2. Save registers r4-r11 to the current thread state (s0-s31 for FPU)
* 3. If not running on MSP, save PSP to the current thread state
* 4. Get the next running thread state
* 5. Restore r4-r11 from thread state (s0-s31 for FPU)
* 6. Set MSP or PSP depending on state
* 7. Switch MSP/PSP on return
*
* Notes:
* - Cortex-M has two stack pointers, MSP and PSP, which we alternate. See the
* reference manual under the Exception Model section.
* - I tried coding this in asm embedded in Threads.cpp but the compiler
* optimizations kept changing my code and removing lines so I have to use
* a separate assembly file. But if you try it, make sure to declare the
* function "naked" so the stack pointer SP is not modified when called.
* This means you can't use local variables, which are stored in stack.
* Try to turn optimizations off using optimize("O0") (which doesn't really
* turn off all optimizations).
* - Function can be called from systick_isr() or from the PIT timer (implemented
* by IntervalTimer)
* - If using systick, we override the default systick_isr() in order
* to preserve the stack and LR. If using PIT, we override the pitX_isr() for
* the same reason.
* - Since Systick can be called from within another interrupt, for simplicity, we
* check for this and abort.
* - Teensy uses MSP for it's main thread; we preserve that. Alternatively, we
* could have used PSP for all threads, including main, and reserve MSP for
* interrupts only. This would simplify the code slightly, but could introduce
* incompatabilities.
* - If this interrupt is nested within another interrupt, all kinds of bad
* things can happen. This is especially true if usb_isr() is active. In theory
* we should be able to do a switch even within an interrupt, but in my
* tests, it would not work reliably.
* - If using the PIT interrupt, it's priority is set to 255 (the lowest) so it
* cannot interrupt an interrupt.
*/
.syntax unified
.align 2
.thumb
.global context_switch_direct
.thumb_func
context_switch_direct:
CPSID I
// Call here to force a context switch, so we skip checking the tick counter.
B call_direct
.global context_switch_direct_active
.thumb_func
context_switch_direct_active:
CPSID I
// Call here to force a context switch, so we skip checking the tick counter.
B call_direct_active
.global context_switch_pit_isr
.thumb_func
context_switch_pit_isr:
CPSID I
LDR r0, =context_timer_flag // acknowledge the interrupt by
LDR r0, [r0] // getting the pointer to the pointer
MOVS r1, #1 //
STR r1, [r0] // and setting to 1
B context_switch_check // now go do the context switch
.global context_switch
.thumb_func
context_switch:
// Disable all interrupts; if we get interrupted during a context switch this
// could corrupt the system.
CPSID I
// Did we interrupt another interrupt? If so, don't switch. Switching would
// wreck the system. In theory, we could reschedule the switch until the
// other interrupt is done. Or we could do a more sophisticated switch, but the
// easiest thing is to just ignore this condition.
CMP lr, #0xFFFFFFF1 // this means we interrupted an interrupt
BEQ to_exit // so don't do anything until next time
CMP lr, #0xFFFFFFE1 // this means we interrupted an interrupt with FPU
BEQ to_exit // so don't do anything until next time
context_switch_check:
// Count down number of ticks we should stay in thread
LDR r0, =currentCount // get the tick count (address to variable)
LDR r1, [r0] // get the value from the address
CMP r1, #0 // is it 0?
BEQ call_direct // if so, thread is done, so switch
SUB r1, #1 // otherwise, subtract 1 tick
STR r1, [r0] // and put it back
B to_exit // and quit until next context_switch
call_direct:
// Just do the context-switch (even if it's not time)
LDR r0, =currentActive // If the thread isn't active, skip it
LDR r0, [r0]
CMP r0, #1
BNE to_exit
call_direct_active:
// Save the r4-r11 registers; (r0-r3,r12 are saved by the interrupt handler).
// Most thread libraries save this to the thread stack. I don't for simplicity
// and to make debugging easier. Since the Teensy doesn't have a debugging port,
// it's hard to examine the stack so this is easier.
LDR r0, =currentSave // get the address of the pointer
LDR r0, [r0] // get the pointer itself
STMIA r0!, {r4-r11,lr} // save r4-r11 to buffer
#ifdef __ARM_PCS_VFP // compile if using FPU
VSTMIA r0!, {s0-s31} // save all FPU registers
VMRS r1, FPSCR // and FPU app status register
STMIA r0!, {r1}
#endif
// Are we running on thread 0, which is MSP?
// It so, there is no need to save the stack pointer because MSP is never changed.
// If not, save the stack pointer.
LDR r0, =currentMSP // get the address of the variable
LDR r0, [r0] // get value from address
CMP r0, #0 // it is 0? This means it's PSP
BNE current_is_msp // not 0, so MSP, we can skip saving SP
MRS r0, psp // get the PSP value
LDR r1, =currentSP // get the address of our save variable
STR r0, [r1] // and store the PSP value there
current_is_msp:
BL loadNextThread; // set the state to next running thread
// Restore the r4-r11 registers from the saved thread
LDR r0, =currentSave // get address of pointer save buffer
LDR r0, [r0] // get the actual pointer
LDMIA r0!, {r4-r11,lr} // and restore r4-r11 & lr from save buffer
#ifdef __ARM_PCS_VFP // compile if using FPU
VLDMIA r0!, {s0-s31} // restore all FPU registers
LDMIA r0!, {r1} // and the FP app status register
VMSR FPSCR, r1
#endif
// Setting LR causes the handler to switch MSP/PSP when returning.
// Switching to MSP? no need to restore MSP.
AND lr, lr, #0x10 // return stack with FP bit?
ORR lr, lr, #0xFFFFFFE9 // add basic LR bits
LDR r0, =currentMSP // get address of the variable
LDR r0, [r0] // get the actual value
CMP r0, #0 // is it 0? Then it's PSP
BNE to_exit // it's not 0, so it's MSP, all done
// if it's PSP, we need to switch PSP
LDR r0, =currentSP // get address of stack pointer
LDR r0, [r0] // get the actual value
MSR psp, r0 // save it to PSP
ORR lr, lr, #0b100 // set the PSP context switch
to_exit:
// Re-enable interrupts
CPSIE I
// Return. The CPU will change MSP/PSP as needed based on LR
BX lr

679
teensy3/TeensyThreads.cpp Normal file
View File

@@ -0,0 +1,679 @@
/*
* Threads.cpp - Library for threading on the Teensy.
*
*******************
*
* Copyright 2017 by Fernando Trias.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*******************
*/
#include "TeensyThreads.h"
#include <Arduino.h>
#ifndef __IMXRT1062__
#include <IntervalTimer.h>
IntervalTimer context_timer;
#endif
Threads threads;
unsigned int time_start;
unsigned int time_end;
#define __flush_cpu() __asm__ volatile("DMB");
// These variables are used by the assembly context_switch() function.
// They are copies or pointers to data in Threads and ThreadInfo
// and put here seperately in order to simplify the code.
extern "C" {
int currentUseSystick; // using Systick vs PIT/GPT
int currentActive; // state of the system (first, start, stop)
int currentCount;
ThreadInfo *currentThread; // the thread currently running
void *currentSave;
int currentMSP; // Stack pointers to save
void *currentSP;
void loadNextThread() {
threads.getNextThread();
}
}
extern "C" void stack_overflow_default_isr() {
currentThread->flags = Threads::ENDED;
}
extern "C" void stack_overflow_isr(void) __attribute__ ((weak, alias("stack_overflow_default_isr")));
extern unsigned long _estack; // the main thread 0 stack
// static void threads_svcall_isr(void);
// static void threads_systick_isr(void);
IsrFunction Threads::save_systick_isr;
IsrFunction Threads::save_svcall_isr;
/*
* Teensy 3:
* Replace the SysTick interrupt for our context switching. Note that
* this function is "naked" meaning it does not save it's registers
* on the stack. This is so we can preserve the stack of the caller.
*
* Interrupts will save r0-r4 in the stack and since this function
* is short and simple, it should only use those registers. In the
* future, this should be coded in assembly to make sure.
*/
extern volatile uint32_t systick_millis_count;
extern "C" void systick_isr();
void __attribute((naked, noinline)) threads_systick_isr(void)
{
if (Threads::save_systick_isr) {
asm volatile("push {r0-r4,lr}");
(*Threads::save_systick_isr)();
asm volatile("pop {r0-r4,lr}");
}
// TODO: Teensyduino 1.38 calls MillisTimer::runFromTimer() from SysTick
if (currentUseSystick) {
// we branch in order to preserve LR and the stack
__asm volatile("b context_switch");
}
__asm volatile("bx lr");
}
void __attribute((naked, noinline)) threads_svcall_isr(void)
{
if (Threads::save_svcall_isr) {
asm volatile("push {r0-r4,lr}");
(*Threads::save_svcall_isr)();
asm volatile("pop {r0-r4,lr}");
}
// Get the right stack so we can extract the PC (next instruction)
// and then see the SVC calling instruction number
__asm volatile("TST lr, #4 \n"
"ITE EQ \n"
"MRSEQ r0, msp \n"
"MRSNE r0, psp \n");
register unsigned int *rsp __asm("r0");
unsigned int svc = ((uint8_t*)rsp[6])[-2];
if (svc == Threads::SVC_NUMBER) {
__asm volatile("b context_switch_direct");
}
else if (svc == Threads::SVC_NUMBER_ACTIVE) {
currentActive = Threads::STARTED;
__asm volatile("b context_switch_direct_active");
}
__asm volatile("bx lr");
}
#ifdef __IMXRT1062__
/*
*
* Teensy 4:
* Use unused GPT timers for context switching
*/
extern "C" void unused_interrupt_vector(void);
static void __attribute((naked, noinline)) gpt1_isr() {
GPT1_SR |= GPT_SR_OF1; // clear set bit
__asm volatile ("dsb"); // see github bug #20 by manitou48
__asm volatile("b context_switch");
}
static void __attribute((naked, noinline)) gpt2_isr() {
GPT2_SR |= GPT_SR_OF1; // clear set bit
__asm volatile ("dsb"); // see github bug #20 by manitou48
__asm volatile("b context_switch");
}
bool gtp1_init(unsigned int microseconds)
{
// Initialization code derived from @manitou48.
// See https://github.com/manitou48/teensy4/blob/master/gpt_isr.ino
// See https://forum.pjrc.com/threads/54265-Teensy-4-testing-mbed-NXP-MXRT1050-EVKB-(600-Mhz-M7)?p=193217&viewfull=1#post193217
// keep track of which GPT timer we are using
static int gpt_number = 0;
// not configured yet, so find an inactive GPT timer
if (gpt_number == 0) {
if (! NVIC_IS_ENABLED(IRQ_GPT1)) {
attachInterruptVector(IRQ_GPT1, &gpt1_isr);
NVIC_SET_PRIORITY(IRQ_GPT1, 255);
NVIC_ENABLE_IRQ(IRQ_GPT1);
gpt_number = 1;
}
else if (! NVIC_IS_ENABLED(IRQ_GPT2)) {
attachInterruptVector(IRQ_GPT2, &gpt2_isr);
NVIC_SET_PRIORITY(IRQ_GPT2, 255);
NVIC_ENABLE_IRQ(IRQ_GPT2);
gpt_number = 2;
}
else {
// if neither timer is free, we fail
return false;
}
}
switch (gpt_number) {
case 1:
CCM_CCGR1 |= CCM_CCGR1_GPT(CCM_CCGR_ON) ; // enable GPT1 module
GPT1_CR = 0; // disable timer
GPT1_PR = 23; // prescale: divide by 24 so 1 tick = 1 microsecond at 24MHz
GPT1_OCR1 = microseconds - 1; // compare value
GPT1_SR = 0x3F; // clear all prior status
GPT1_IR = GPT_IR_OF1IE; // use first timer
GPT1_CR = GPT_CR_EN | GPT_CR_CLKSRC(1) ; // set to peripheral clock (24MHz)
break;
case 2:
CCM_CCGR1 |= CCM_CCGR1_GPT(CCM_CCGR_ON) ; // enable GPT1 module
GPT2_CR = 0; // disable timer
GPT2_PR = 23; // prescale: divide by 24 so 1 tick = 1 microsecond at 24MHz
GPT2_OCR1 = microseconds - 1; // compare value
GPT2_SR = 0x3F; // clear all prior status
GPT2_IR = GPT_IR_OF1IE; // use first timer
GPT2_CR = GPT_CR_EN | GPT_CR_CLKSRC(1) ; // set to peripheral clock (24MHz)
break;
default:
return false;
}
return true;
}
#endif
Threads::Threads() : current_thread(0), thread_count(0), thread_error(0) {
// initilize thread slots to empty
for(int i=0; i<MAX_THREADS; i++) {
threadp[i] = NULL;
}
// fill thread 0, which is always running
threadp[0] = new ThreadInfo();
// initialize context_switch() globals from thread 0, which is MSP and always running
currentThread = threadp[0]; // thread 0 is active
currentSave = &threadp[0]->save;
currentMSP = 1;
currentSP = 0;
currentCount = Threads::DEFAULT_TICKS;
currentActive = FIRST_RUN;
threadp[0]->flags = RUNNING;
threadp[0]->ticks = DEFAULT_TICKS;
threadp[0]->stack = (uint8_t*)&_estack - DEFAULT_STACK0_SIZE;
threadp[0]->stack_size = DEFAULT_STACK0_SIZE;
#ifdef __IMXRT1062__
// commandeer SVCall & use GTP1 Interrupt
save_svcall_isr = _VectorsRam[11];
if (save_svcall_isr == unused_interrupt_vector) save_svcall_isr = 0;
_VectorsRam[11] = threads_svcall_isr;
currentUseSystick = 0; // disable Systick calls
gtp1_init(1000); // tick every millisecond
#else
currentUseSystick = 1;
// commandeer the SVCall & SysTick Exceptions
save_svcall_isr = _VectorsRam[11];
if (save_svcall_isr == unused_isr) save_svcall_isr = 0;
_VectorsRam[11] = threads_svcall_isr;
save_systick_isr = _VectorsRam[15];
if (save_systick_isr == unused_isr) save_systick_isr = 0;
_VectorsRam[15] = threads_systick_isr;
#ifdef DEBUG
#if defined(__MK20DX256__) || defined(__MK20DX128__)
ARM_DEMCR |= ARM_DEMCR_TRCENA; // Make ssure Cycle Counter active
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
#endif
#endif
#endif
}
/*
* start() - Begin threading
*/
int Threads::start(int prev_state) {
__disable_irq();
int old_state = currentActive;
if (prev_state == -1) prev_state = STARTED;
currentActive = prev_state;
__enable_irq();
return old_state;
}
/*
* stop() - Stop threading, even if active.
*
* If threads have already started, this should be called sparingly
* because it could destabalize the system if thread 0 is stopped.
*/
int Threads::stop() {
__disable_irq();
int old_state = currentActive;
currentActive = STOPPED;
__enable_irq();
return old_state;
}
/*
* getNextThread() - Find next running thread
*
* This will also set the context_switcher() state variables
*/
void Threads::getNextThread() {
#ifdef DEBUG
// Keep track of the number of cycles expended by each thread.
// See @dfragster: https://forum.pjrc.com/threads/41504-Teensy-3-x-multithreading-library-first-release?p=213086#post213086
currentThread->cyclesAccum += ARM_DWT_CYCCNT - currentThread->cyclesStart;
#endif
// First, save the currentSP set by context_switch
currentThread->sp = currentSP;
// did we overflow the stack (don't check thread 0)?
// allow an extra 8 bytes for a call to the ISR and one additional call or variable
if (current_thread && ((uint8_t*)currentThread->sp - currentThread->stack <= 8)) {
stack_overflow_isr();
}
// Find the next running thread
while(1) {
current_thread++;
if (current_thread >= MAX_THREADS) {
current_thread = 0; // thread 0 is MSP; always active so return
break;
}
if (threadp[current_thread] && threadp[current_thread]->flags == RUNNING) break;
}
currentCount = threadp[current_thread]->ticks;
currentThread = threadp[current_thread];
currentSave = &threadp[current_thread]->save;
currentMSP = (current_thread==0?1:0);
currentSP = threadp[current_thread]->sp;
#ifdef DEBUG
currentThread->cyclesStart = ARM_DWT_CYCCNT;
#endif
}
/*
* Empty placeholder for IntervalTimer class
*/
static void context_pit_empty() {}
/*
* Store the PIT timer flag register for use in assembly
*/
volatile uint32_t *context_timer_flag;
/*
* Defined in assembly code
*/
extern "C" void context_switch_pit_isr();
/*
* Stop using the SysTick interrupt and start using
* the IntervalTimer timer. The parameter is the number of microseconds
* for each tick.
*/
int Threads::setMicroTimer(int tick_microseconds)
{
#ifdef __IMXRT1062__
gtp1_init(tick_microseconds);
#else
/*
* Implementation strategy suggested by @tni in Teensy Forums; see
* https://forum.pjrc.com/threads/41504-Teensy-3-x-multithreading-library-first-release
*/
// lowest priority so we don't interrupt other interrupts
context_timer.priority(255);
// start timer with dummy fuction
if (context_timer.begin(context_pit_empty, tick_microseconds) == 0) {
// failed to set the timer!
return 0;
}
currentUseSystick = 0; // disable Systick calls
// get the PIT number [0-3] (IntervalTimer overrides IRQ_NUMBER_t op)
int number = (IRQ_NUMBER_t)context_timer - IRQ_PIT_CH0;
// calculate number of uint32_t per PIT; should be 4.
// Not hard-coded in case this changes in future CPUs.
const int width = (PIT_TFLG1 - PIT_TFLG0) / sizeof(uint32_t);
// get the right flag to ackowledge PIT interrupt
context_timer_flag = &PIT_TFLG0 + (width * number);
attachInterruptVector(context_timer, context_switch_pit_isr);
#endif
return 1;
}
/*
* Set each time slice to be 'microseconds' long
*/
int Threads::setSliceMicros(int microseconds)
{
setMicroTimer(microseconds);
setDefaultTimeSlice(1);
return 1;
}
/*
* Set each time slice to be 'milliseconds' long
*/
int Threads::setSliceMillis(int milliseconds)
{
if (currentUseSystick) {
setDefaultTimeSlice(milliseconds);
}
else {
// if we're using the PIT, we should probably really disable it and
// re-establish the systick timer; but this is easier for now
setSliceMicros(milliseconds * 1000);
}
return 1;
}
/*
* del_process() - This is called when the task returns
*
* Turns thread off. Thread continues running until next call to
* context_switch() at which point it all stops. The while(1) statement
* just stalls until such time.
*/
void Threads::del_process(void)
{
int old_state = threads.stop();
ThreadInfo *me = threads.threadp[threads.current_thread];
// Would love to delete stack here but the thread doesn't
// end now. It continues until the next tick.
// if (me->my_stack) {
// delete[] me->stack;
// me->stack = 0;
// }
threads.thread_count--;
me->flags = ENDED; //clear the flags so thread can stop and be reused
threads.start(old_state);
while(1); // just in case, keep working until context change when execution will not return to this thread
}
/*
* Initializes a thread's stack. Called when thread is created
*/
void *Threads::loadstack(ThreadFunction p, void * arg, void *stackaddr, int stack_size)
{
interrupt_stack_t * process_frame = (interrupt_stack_t *)((uint8_t*)stackaddr + stack_size - sizeof(interrupt_stack_t) - 8);
process_frame->r0 = (uint32_t)arg;
process_frame->r1 = 0;
process_frame->r2 = 0;
process_frame->r3 = 0;
process_frame->r12 = 0;
process_frame->lr = (uint32_t)Threads::del_process;
process_frame->pc = ((uint32_t)p);
process_frame->xpsr = 0x1000000;
uint8_t *ret = (uint8_t*)process_frame;
// ret -= sizeof(software_stack_t); // uncomment this if we are saving R4-R11 to the stack
return (void*)ret;
}
/*
* Add a new thread to the queue.
* add_thread(fund, arg)
*
* fund : is a function pointer. The function prototype is:
* void *func(void *param)
* arg : is a void pointer that is passed as the first parameter
* of the function. In the example above, arg is passed
* as param.
* stack_size : the size of the buffer pointed to by stack. If
* it is 0, then "stack" must also be 0. If so, the function
* will allocate the default stack size of the heap using new().
* stack : pointer to new data stack of size stack_size. If this is 0,
* then it will allocate a stack on the heap using new() of size
* stack_size. If stack_size is 0, a default size will be used.
* return: an integer ID to be used for other calls
*/
int Threads::addThread(ThreadFunction p, void * arg, int stack_size, void *stack)
{
int old_state = stop();
if (stack_size == -1) stack_size = DEFAULT_STACK_SIZE;
for (int i=1; i < MAX_THREADS; i++) {
if (threadp[i] == NULL) { // empty thread, so fill it
threadp[i] = new ThreadInfo();
}
if (threadp[i]->flags == ENDED || threadp[i]->flags == EMPTY) { // free thread
ThreadInfo *tp = threadp[i]; // working on this thread
if (tp->stack && tp->my_stack) {
delete[] tp->stack;
}
if (stack==0) {
stack = new uint8_t[stack_size];
tp->my_stack = 1;
}
else {
tp->my_stack = 0;
}
tp->stack = (uint8_t*)stack;
tp->stack_size = stack_size;
void *psp = loadstack(p, arg, tp->stack, tp->stack_size);
tp->sp = psp;
tp->ticks = DEFAULT_TICKS;
tp->flags = RUNNING;
tp->save.lr = 0xFFFFFFF9;
#ifdef DEBUG
tp->cyclesStart = ARM_DWT_CYCCNT;
tp->cyclesAccum = 0;
#endif
currentActive = old_state;
thread_count++;
if (old_state == STARTED || old_state == FIRST_RUN) start();
return i;
}
}
if (old_state == STARTED) start();
return -1;
}
int Threads::getState(int id)
{
return threadp[id]->flags;
}
int Threads::setState(int id, int state)
{
threadp[id]->flags = state;
return state;
}
int Threads::wait(int id, unsigned int timeout_ms)
{
unsigned int start = millis();
// need to store state in temp volatile memory for optimizer.
// "while (thread[id].flags != RUNNING)" will be optimized away
volatile int state;
while (1) {
if (timeout_ms != 0 && millis() - start > timeout_ms) return -1;
state = threadp[id]->flags;
if (state != RUNNING) break;
yield();
}
return id;
}
int Threads::kill(int id)
{
threadp[id]->flags = ENDED;
return id;
}
int Threads::suspend(int id)
{
threadp[id]->flags = SUSPENDED;
return id;
}
int Threads::restart(int id)
{
threadp[id]->flags = RUNNING;
return id;
}
void Threads::setTimeSlice(int id, unsigned int ticks)
{
threadp[id]->ticks = ticks - 1;
}
void Threads::setDefaultTimeSlice(unsigned int ticks)
{
DEFAULT_TICKS = ticks - 1;
}
void Threads::setDefaultStackSize(unsigned int bytes_size)
{
DEFAULT_STACK_SIZE = bytes_size;
}
void Threads::yield() {
__asm volatile("svc %0" : : "i"(Threads::SVC_NUMBER));
}
void Threads::yield_and_start() {
__asm volatile("svc %0" : : "i"(Threads::SVC_NUMBER_ACTIVE));
}
void Threads::delay(int millisecond) {
int mx = millis();
while((int)millis() - mx < millisecond) yield();
}
int Threads::id() {
volatile int ret;
__disable_irq();
ret = current_thread;
__enable_irq();
return ret;
}
int Threads::getStackUsed(int id) {
return threadp[id]->stack + threadp[id]->stack_size - (uint8_t*)threadp[id]->sp;
}
int Threads::getStackRemaining(int id) {
return (uint8_t*)threadp[id]->sp - threadp[id]->stack;
}
#ifdef DEBUG
unsigned long Threads::getCyclesUsed(int id) {
stop();
unsigned long ret = threadp[id]->cyclesAccum;
start();
return ret;
}
#endif
/*
* On creation, stop threading and save state
*/
Threads::Suspend::Suspend() {
__disable_irq();
save_state = currentActive;
currentActive = 0;
__enable_irq();
}
/*
* On destruction, restore threading state
*/
Threads::Suspend::~Suspend() {
__disable_irq();
currentActive = save_state;
__enable_irq();
}
int Threads::Mutex::getState() {
int p = threads.stop();
int ret = state;
threads.start(p);
return ret;
}
int __attribute__ ((noinline)) Threads::Mutex::lock(unsigned int timeout_ms) {
if (try_lock()) return 1; // we're good, so avoid more checks
uint32_t start = systick_millis_count;
while (1) {
if (try_lock()) return 1;
if (timeout_ms && (systick_millis_count - start > timeout_ms)) return 0;
if (waitthread==-1) { // can hold 1 thread suspend until unlock
int p = threads.stop();
waitthread = threads.current_thread;
waitcount = currentCount;
threads.suspend(waitthread);
threads.start(p);
}
threads.yield();
}
__flush_cpu();
return 0;
}
int Threads::Mutex::try_lock() {
int p = threads.stop();
if (state == 0) {
state = 1;
threads.start(p);
return 1;
}
threads.start(p);
return 0;
}
int __attribute__ ((noinline)) Threads::Mutex::unlock() {
int p = threads.stop();
if (state==1) {
state = 0;
if (waitthread >= 0) { // reanimate a suspended thread waiting for unlock
threads.restart(waitthread);
waitthread = -1;
__flush_cpu();
threads.yield_and_start();
return 1;
}
}
__flush_cpu();
threads.start(p);
return 1;
}

424
teensy3/TeensyThreads.h Normal file
View File

@@ -0,0 +1,424 @@
/*
* Threads.h - Library for threading on the Teensy.
*
*******************
*
* Copyright 2017 by Fernando Trias.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*******************
*
* Multithreading library for Teensy board.
* See Threads.cpp for explanation of internal functions.
*
* A global variable "threads" of type Threads will be created
* to provide all threading functions. See example below:
*
* #include <Threads.h>
*
* volatile int count = 0;
*
* void thread_func(int data){
* while(1) count++;
* }
*
* void setup() {
* threads.addThread(thread_func, 0);
* }
*
* void loop() {
* Serial.print(count);
* }
*
* Alternatively, you can use the std::threads class defined
* by C++11
*
* #include <Threads.h>
*
* volatile int count = 0;
*
* void thread_func(){
* while(1) count++;
* }
*
* void setup() {
* std::thead th1(thread_func);
* th1.detach();
* }
*
* void loop() {
* Serial.print(count);
* }
*
*/
#ifndef _THREADS_H
#define _THREADS_H
#include <stdint.h>
/* Enabling debugging information allows access to:
* getCyclesUsed()
*/
// #define DEBUG
extern "C" {
void context_switch(void);
void context_switch_direct(void);
void context_switch_pit_isr(void);
void loadNextThread();
void stack_overflow_isr(void);
void threads_svcall_isr(void);
void threads_systick_isr(void);
}
// The stack frame saved by the interrupt
typedef struct {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t xpsr;
} interrupt_stack_t;
// The stack frame saved by the context switch
typedef struct {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t lr;
#ifdef __ARM_PCS_VFP
uint32_t s0;
uint32_t s1;
uint32_t s2;
uint32_t s3;
uint32_t s4;
uint32_t s5;
uint32_t s6;
uint32_t s7;
uint32_t s8;
uint32_t s9;
uint32_t s10;
uint32_t s11;
uint32_t s12;
uint32_t s13;
uint32_t s14;
uint32_t s15;
uint32_t s16;
uint32_t s17;
uint32_t s18;
uint32_t s19;
uint32_t s20;
uint32_t s21;
uint32_t s22;
uint32_t s23;
uint32_t s24;
uint32_t s25;
uint32_t s26;
uint32_t s27;
uint32_t s28;
uint32_t s29;
uint32_t s30;
uint32_t s31;
uint32_t fpscr;
#endif
} software_stack_t;
// The state of each thread (including thread 0)
class ThreadInfo {
public:
int stack_size;
uint8_t *stack=0;
int my_stack = 0;
software_stack_t save;
volatile int flags = 0;
void *sp;
int ticks;
#ifdef DEBUG
unsigned long cyclesStart; // On T_4 the CycCnt is always active - on T_3.x it currently is not - unless Audio starts it AFAIK
unsigned long cyclesAccum;
#endif
};
extern "C" void unused_isr(void);
typedef void (*ThreadFunction)(void*);
typedef void (*ThreadFunctionInt)(int);
typedef void (*ThreadFunctionNone)();
typedef void (*IsrFunction)();
/*
* Threads handles all the threading interaction with users. It gets
* instantiated in a global variable "threads".
*/
class Threads {
public:
// The maximum number of threads is hard-coded to simplify
// the implementation. See notes of ThreadInfo.
static const int MAX_THREADS = 8;
int DEFAULT_STACK_SIZE = 1024;
const int DEFAULT_STACK0_SIZE = 10240; // estimate for thread 0?
int DEFAULT_TICKS = 10;
static const int DEFAULT_TICK_MICROSECONDS = 100;
// State of threading system
static const int STARTED = 1;
static const int STOPPED = 2;
static const int FIRST_RUN = 3;
// State of individual threads
static const int EMPTY = 0;
static const int RUNNING = 1;
static const int ENDED = 2;
static const int ENDING = 3;
static const int SUSPENDED = 4;
static const int SVC_NUMBER = 0x21;
static const int SVC_NUMBER_ACTIVE = 0x22;
protected:
int current_thread;
int thread_count;
int thread_error;
/*
* The maximum number of threads is hard-coded. Alternatively, we could implement
* a linked list which would mean using up less memory for a small number of
* threads while allowing an unlimited number of possible threads. This would
* probably not slow down thread switching too much, but it would introduce
* complexity and possibly bugs. So to simplifiy for now, we use an array.
* But in the future, a linked list might be more appropriate.
*/
ThreadInfo *threadp[MAX_THREADS];
// This used to be allocated statically, as below. Kept for reference in case of bugs.
// ThreadInfo thread[MAX_THREADS];
public: // public for debugging
static IsrFunction save_systick_isr;
static IsrFunction save_svcall_isr;
public:
Threads();
// Create a new thread for function "p", passing argument "arg". If stack is 0,
// stack allocated on heap. Function "p" has form "void p(void *)".
int addThread(ThreadFunction p, void * arg=0, int stack_size=-1, void *stack=0);
// For: void f(int)
int addThread(ThreadFunctionInt p, int arg=0, int stack_size=-1, void *stack=0) {
return addThread((ThreadFunction)p, (void*)arg, stack_size, stack);
}
// For: void f()
int addThread(ThreadFunctionNone p, int arg=0, int stack_size=-1, void *stack=0) {
return addThread((ThreadFunction)p, (void*)arg, stack_size, stack);
}
// Get the state; see class constants. Can be EMPTY, RUNNING, etc.
int getState(int id);
// Explicityly set a state. See getState(). Call with care.
int setState(int id, int state);
// Wait until thread returns up to timeout_ms milliseconds. If ms is 0, wait
// indefinitely.
int wait(int id, unsigned int timeout_ms = 0);
// Permanently stop a running thread. Thread will end on the next thread slice tick.
int kill(int id);
// Suspend a thread (on the next slice tick). Can be restarted with restart().
int suspend(int id);
// Restart a suspended thread.
int restart(int id);
// Set the slice length time in ticks for a thread (1 tick = 1 millisecond, unless using MicroTimer)
void setTimeSlice(int id, unsigned int ticks);
// Set the slice length time in ticks for all new threads (1 tick = 1 millisecond, unless using MicroTimer)
void setDefaultTimeSlice(unsigned int ticks);
// Set the stack size for new threads in bytes
void setDefaultStackSize(unsigned int bytes_size);
// Use the microsecond timer provided by IntervalTimer & PIT; instead of 1 tick = 1 millisecond,
// 1 tick will be the number of microseconds provided (default is 100 microseconds)
int setMicroTimer(int tick_microseconds = DEFAULT_TICK_MICROSECONDS);
// Simple function to set each time slice to be 'milliseconds' long
int setSliceMillis(int milliseconds);
// Set each time slice to be 'microseconds' long
int setSliceMicros(int microseconds);
// Get the id of the currently running thread
int id();
int getStackUsed(int id);
int getStackRemaining(int id);
#ifdef DEBUG
unsigned long getCyclesUsed(int id);
#endif
// Yield current thread's remaining time slice to the next thread, causing immediate
// context switch
void yield();
// Wait for milliseconds using yield(), giving other slices your wait time
void delay(int millisecond);
// Start/restart threading system; returns previous state: STARTED, STOPPED, FIRST_RUN
// can pass the previous state to restore
int start(int old_state = -1);
// Stop threading system; returns previous state: STARTED, STOPPED, FIRST_RUN
int stop();
// Allow these static functions and classes to access our members
friend void context_switch(void);
friend void context_switch_direct(void);
friend void context_pit_isr(void);
friend void threads_systick_isr(void);
friend void threads_svcall_isr(void);
friend void loadNextThread();
friend class ThreadLock;
protected:
void getNextThread();
void *loadstack(ThreadFunction p, void * arg, void *stackaddr, int stack_size);
static void force_switch_isr();
private:
static void del_process(void);
void yield_and_start();
public:
class Mutex {
private:
volatile int state = 0;
volatile int waitthread = -1;
volatile int waitcount = 0;
public:
int getState(); // get the lock state; 1=locked; 0=unlocked
int lock(unsigned int timeout_ms = 0); // lock, optionally waiting up to timeout_ms milliseconds
int try_lock(); // if lock available, get it and return 1; otherwise return 0
int unlock(); // unlock if locked
};
class Scope {
private:
Mutex *r;
public:
Scope(Mutex& m) { r = &m; r->lock(); }
~Scope() { r->unlock(); }
};
class Suspend {
private:
int save_state;
public:
Suspend(); // Stop threads and save thread state
~Suspend(); // Restore saved state
};
template <class C> class GrabTemp {
private:
Mutex *lkp;
public:
C *me;
GrabTemp(C *obj, Mutex *lk) { me = obj; lkp=lk; lkp->lock(); }
~GrabTemp() { lkp->unlock(); }
C &get() { return *me; }
};
template <class T> class Grab {
private:
Mutex lk;
T *me;
public:
Grab(T &t) { me = &t; }
GrabTemp<T> grab() { return GrabTemp<T>(me, &lk); }
operator T&() { return grab().get(); }
T *operator->() { return grab().me; }
Mutex &getLock() { return lk; }
};
#define ThreadWrap(OLDOBJ, NEWOBJ) Threads::Grab<decltype(OLDOBJ)> NEWOBJ(OLDOBJ);
#define ThreadClone(NEWOBJ) (NEWOBJ.grab().get())
};
extern Threads threads;
/*
* Rudimentary compliance to C++11 class
*
* See http://www.cplusplus.com/reference/thread/thread/
*
* Example:
* int x;
* void thread_func() { x++; }
* int main() {
* std::thread(thread_func);
* }
*
*/
namespace std {
class thread {
private:
int id; // internal thread id
int destroy; // flag to kill thread on instance destruction
public:
// By casting all (args...) to (void*), if there are more than one args, the compiler
// will fail to find a matching function. This fancy template just allows any kind of
// function to match.
template <class F, class ...Args> explicit thread(F&& f, Args&&... args) {
id = threads.addThread((ThreadFunction)f, (void*)args...);
destroy = 1;
}
// If thread has not been detached when destructor called, then thread must end
~thread() {
if (destroy) threads.kill(id);
}
// Threads are joinable until detached per definition, but in this implementation
// that's not so. We emulate expected behavior anyway.
bool joinable() { return destroy==1; }
// Once detach() is called, thread runs until it terminates; otherwise it terminates
// when destructor called.
void detach() { destroy = 0; }
// In theory, the thread merges with the running thread; if we just wait until
// termination, it's basically the same thing except it's slower because
// there are two threads running instead of one. Close enough.
void join() { threads.wait(id); }
// Get the unique thread id.
int get_id() { return id; }
};
class mutex {
private:
Threads::Mutex mx;
public:
void lock() { mx.lock(); }
bool try_lock() { return mx.try_lock(); }
void unlock() { mx.unlock(); }
};
template <class cMutex> class lock_guard {
private:
cMutex *r;
public:
explicit lock_guard(cMutex& m) { r = &m; r->lock(); }
~lock_guard() { r->unlock(); }
};
}
#endif

View File

@@ -63,10 +63,10 @@ BUILD_DIR = $(abspath $(CURDIR)/build)
# Addresses where the zOS/ZPUTA base program loads and where apps load and execute.
# With IOCP. These are defaults as they should be overriden by caller.
ifeq ($(OS_BASEADDR),)
OS_BASEADDR = 0x00000000
OS_BASEADDR = 0x00000000
endif
ifeq ($(OS_APPADDR),)
OS_APPADDR = 0x1FFF8000
OS_APPADDR = 0x1FFF8000
endif
#************************************************************************
@@ -89,7 +89,7 @@ endif
# path location for the arm-none-eabi compiler
COMPILERPATH = $(TOOLSPATH)/arm/bin
COMPILERPATH = $(TOOLSPATH)/arm/bin
# path location for Teensy 3 core
TEENSY3_DIR = $(CURDIR)/../teensy3
@@ -127,6 +127,9 @@ CPPFLAGS = -Wall -g -Os -mthumb -fno-builtin -ffunction-sections -fdata-se
CPPFLAGS += -Isrc -Iteensy3 -I$(COMMON_DIR) -I$(FATFS_DIR) -I$(TEENSY3_DIR) -I$(INCLUDE_DIR)
CPPFLAGS += -D__K64F__ -D__ZOS__ -DDEBUG -D__SD_CARD__ -DOS_BASEADDR=$(OS_BASEADDR) -DOS_APPADDR=$(OS_APPADDR)
#CPPFLAGS += -fno-use-cxa-atexit
ifeq ($(__TRANZPUTER__),1)
CPPFLAGS += -D__TRANZPUTER__
endif
# compiler options for C++ only
#CXXFLAGS = -std=gnu++0x -felide-constructors -fno-exceptions -fno-rtti
@@ -165,6 +168,7 @@ else ifeq ($(TEENSY), LC)
LIBS += -larm_cortexM0l_math
else ifeq ($(TEENSY), 35)
CPPFLAGS += -D__MK64FX512__ -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
ASFLAGS += -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
LDSCRIPT = $(STARTUP_DIR)/zos_k64f.ld
#LDFLAGS += -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -T$(STARTUP_DIR)/zos_k64f.ld -ffreestanding -mthumb #-fno-use-cxa-atexit
LDFLAGS += -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -T$(LDSCRIPT)
@@ -188,18 +192,25 @@ endif
# automatically create lists of the sources and objects
TEENSY_C_FILES := $(wildcard $(TEENSY3_DIR)/*.c)
TEENSY_CPP_FILES:= $(wildcard $(TEENSY3_DIR)/*.cpp)
TEENSY_ASM_FILES:= $(wildcard $(TEENSY3_DIR)/*.s)
SRC_C_FILES := $(wildcard src/*.c)
SRC_CPP_FILES := $(wildcard src/*.cpp)
INO_FILES := $(wildcard src/*.ino)
CRT0_ASM_FILES := #$(STARTUP_DIR)/zos_k64f_crt0.s
CRT0_C_FILES := $(STARTUP_DIR)/mk20dx128.c
COMMON_FILES := $(COMMON_DIR)/utils.c $(COMMON_DIR)/k64f_soc.c $(COMMON_DIR)/interrupts.c $(COMMON_DIR)/ps2.c $(COMMON_DIR)/readline.c
ifeq ($(__TRANZPUTER__),1)
COMMON_FILES += $(COMMON_DIR)/tranzputer.c
endif
FATFS_C_FILES := $(FATFS_DIR)/ff.c
ifeq ($(__TRANZPUTER__),1)
FATFS_C_FILES += $(FATFS_DIR)/ffunicode.c
endif
FATFS_CPP_FILES:= $(FATFS_DIR)/sdmmc_k64f.cpp
PFS_FILES := $(PFS_DIR)/sdmmc_teensy.c $(PFS_DIR)/pff.c
# Define the sources and what they compile from->to.
SOURCES := $(CRT0_ASM_FILES:.s=.o) $(CRT0_C_FILES:.c=.o) $(COMMON_FILES:.c=.o) $(FATFS_C_FILES:.c=.o) $(FATFS_CPP_FILES:.cpp=.o) $(SRC_C_FILES:.c=.o) $(SRC_CPP_FILES:.cpp=.o) $(INO_FILES:.ino=.o) $(TEENSY_C_FILES:.c=.o) $(TEENSY_CPP_FILES:.cpp=.o)
SOURCES := $(CRT0_ASM_FILES:.s=.o) $(CRT0_C_FILES:.c=.o) $(COMMON_FILES:.c=.o) $(FATFS_C_FILES:.c=.o) $(FATFS_CPP_FILES:.cpp=.o) $(SRC_C_FILES:.c=.o) $(SRC_CPP_FILES:.cpp=.o) $(INO_FILES:.ino=.o) $(TEENSY_C_FILES:.c=.o) $(TEENSY_CPP_FILES:.cpp=.o) $(TEENSY_ASM_FILES:.s=.o)
OBJS := $(foreach src,$(SOURCES), $(BUILD_DIR)/$(src))
all: version hex bin srec rpt lss dmp
@@ -282,7 +293,7 @@ $(BUILD_DIR)/%.o: %.cpp
$(BUILD_DIR)/%.o: %.s
@echo "[AS]\t$<"
@mkdir -p "$(dir $@)"
@$(AS) $(ASFLAGS) -o "$@" -c "$<"
$(AS) $(ASFLAGS) -o "$@" -c "$<"
$(BUILD_DIR)/%.o: %.ino
@echo "[CXX]\t$<"

View File

@@ -56,7 +56,9 @@
#include "WProgram.h"
#include "k64f_soc.h"
#include <../libraries/include/stdmisc.h>
#else
#include <TeensyThreads.h>
#else // __ZPU__
#include <stdint.h>
#include <string.h>
#include <stdio.h>
@@ -75,6 +77,10 @@
#include "zOS_app.h" /* Header for definitions specific to apps run from zOS */
#include "zOS.h"
#if defined __TRANZPUTER__
#include <tranzputer.h>
#endif
#if defined(BUILTIN_TST_DHRYSTONE) && BUILTIN_TST_DHRYSTONE == 1
#include <dhry.h>
#endif
@@ -226,6 +232,64 @@ uint8_t getCommandLine(char *buf, uint8_t bufSize)
return(result);
}
#if defined __TRANZPUTER__
// Thread to monitor and control the tranZPUter board offering needed services and monitoring.
//
void tranZPUterControl(void)
{
// Locals.
uint8_t ioAddr;
uint8_t ioData;
// Loop waiting on events and processing.
//
while(1)
{
// Indicate the thread is busy so we are not interrupted whilst servicing the tranZPUter.
//
G.ctrlThreadBusy = 1;
// If a user reset event occurred, reload the default ROM set.
//
if(isZ80Reset())
{
printf("Doing a reset load\n");
// Reload the memory on the tranZPUter to boot default.
loadTranZPUterDefaultROMS();
// Clear reset event which caused this reload.
clearZ80Reset();
}
// Has there been an IO instruction for a service request?
//
if(getZ80IO(&ioAddr, &ioData) == 1)
{
printf("Got an IO request, addr:%02x, Data:%02x\n", ioAddr, ioData);
switch(ioAddr)
{
// Service request. Actual data about the request is stored in the Z80 memory, so read the request and process.
//
case IO_TZ_SVCREQ:
// Handle the service request.
//
processServiceRequest();
break;
default:
break;
}
}
// Indicate the thread is free.
//
G.ctrlThreadBusy = 0;
threads.delay(100);
threads.yield();
}
}
#endif
// Interactive command processor. Allow user to input a command and execute accordingly.
//
int cmdProcessor(void)
@@ -273,7 +337,22 @@ int cmdProcessor(void)
} else
{
diskInitialised = 1;
fsInitialised = 1;
fsInitialised = 1;
#if defined __TRANZPUTER__
// Setup the tranZPUter ready for action!
setupTranZPUter();
// Setup memory on Z80 to default.
loadTranZPUterDefaultROMS();
// Cache initial directory.
svcCacheDir(TZSVC_DEFAULT_DIR, 1);
// For the tranZPUter, once we know that an SD card is available, launch seperate thread to handle hardware and service functionality.
// No SD card = no tranZPUter functionality.
G.ctrlThreadId = threads.addThread(tranZPUterControl, 0, 16384);
#endif
}
#endif
@@ -628,6 +707,18 @@ int cmdProcessor(void)
if(diskInitialised && fsInitialised && strlen(src1FileName) < 16)
{
#if defined __TRANZPUTER__
// If running on the tranZPUter, suspend the control thread if we are to run a tranZPUter applet to prevent clashes with hardware.
//
if(src1FileName[0] == 't' && src1FileName[1] == 'z')
{
// Wait until the control thread has completed a loop then suspend thread for the duration of the app run to avoid clash for resources.
//
while(G.ctrlThreadBusy == 1);
threads.suspend(G.ctrlThreadId);
}
#endif
// The user normally just types the command, but it is possible to type the drive and or path and or extension, so cater
// for these possibilities by trial. An alternate way is to disect the entered command but I think this would take more code space.
trying = 1;
@@ -660,6 +751,7 @@ int cmdProcessor(void)
#if defined __ZPU__
retCode = fileExec(&line[40], APP_CMD_LOAD_ADDR, APP_CMD_EXEC_ADDR, EXEC_MODE_CALL, (uint32_t)ptr, (uint32_t)cmdline, (uint32_t)&G, (uint32_t)&cfgSoC);
#else
//printf("%s,%08lx,%08lx,%d,%s,%s\n", &line[40], APP_CMD_LOAD_ADDR, APP_CMD_EXEC_ADDR, EXEC_MODE_CALL, ptr, cmdline);
retCode = fileExec(&line[40], APP_CMD_LOAD_ADDR, APP_CMD_EXEC_ADDR, EXEC_MODE_CALL, (uint32_t)ptr, (uint32_t)cmdline, (uint32_t)&G, (uint32_t)&cfgSoC);
#endif
@@ -671,6 +763,11 @@ int cmdProcessor(void)
trying = 0;
}
}
#if defined __TRANZPUTER__
// Restart the suspended control thread, the application should have left the
threads.restart(G.ctrlThreadId);
#endif
}
if(!diskInitialised || !fsInitialised || retCode == 0xffffffff)
{

View File

@@ -39,13 +39,17 @@
// Global parameters accessible in applications.
typedef struct {
uint8_t fileInUse; /* Flag to indicate if file[0] is in use. */
FIL File[MAX_FILE_HANDLE]; /* Maximum open file objects */
FATFS FatFs[FF_VOLUMES]; /* Filesystem object for each logical drive */
BYTE Buff[512]; /* Working buffer */
DWORD Sector; /* Sector to read */
uint8_t fileInUse; // Flag to indicate if file[0] is in use.
FIL File[MAX_FILE_HANDLE]; // Maximum open file objects
FATFS FatFs[FF_VOLUMES]; // Filesystem object for each logical drive
BYTE Buff[512]; // Working buffer
DWORD Sector; // Sector to read
#if defined __K64F__
uint32_t volatile *millis; /* Pointer to the K64F millisecond tick */
uint32_t volatile *millis; // Pointer to the K64F millisecond tick
#endif
#if defined __TRANZPUTER__
int ctrlThreadId; // Id of tranZPUter control thread.
uint8_t ctrlThreadBusy; // Flag to indicate when the control thread cannot be disturbed.
#endif
} GLOBALS;