diff --git a/apps/Makefile b/apps/Makefile index 1d4278c..6de5041 100755 --- a/apps/Makefile +++ b/apps/Makefile @@ -36,13 +36,13 @@ ######################################################################################################### 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++ @@ -60,8 +60,15 @@ 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 test tbasic mbasic kilo ed -SUBDIRS := $(FS_SUBDIRS) $(DISK_SUBDIRS) $(BUFFER_SUBDIRS) $(MEM_SUBDIRS) $(HW_SUBDIRS) $(TST_SUBDIRS) $(MISC_SUBDIRS) +MISC_SUBDIRS := help time +APP_SUBDIRS := tbasic mbasic kilo ed +ifeq ($(__K64F__),1) + TZPU_SUBDIRS:= tzpu tzload +else + 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 diff --git a/apps/common/analog.c b/apps/common/analog.c new file mode 100644 index 0000000..254f8be --- /dev/null +++ b/apps/common/analog.c @@ -0,0 +1,579 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2017 PJRC.COM, LLC. + * + * 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: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * 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 "core_pins.h" +//#include "HardwareSerial.h" + +static uint8_t calibrating; +static uint8_t analog_right_shift = 0; +static uint8_t analog_config_bits = 10; +static uint8_t analog_num_average = 4; +static uint8_t analog_reference_internal = 0; + +// the alternate clock is connected to OSCERCLK (16 MHz). +// datasheet says ADC clock should be 2 to 12 MHz for 16 bit mode +// datasheet says ADC clock should be 1 to 18 MHz for 8-12 bit mode +#if F_BUS == 128000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(3) + ADC_CFG1_ADICLK(1) // 8 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 16 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 16 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 16 MHz +#elif F_BUS == 120000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(3) + ADC_CFG1_ADICLK(1) // 7.5 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 15 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 15 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 15 MHz +#elif F_BUS == 108000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(3) + ADC_CFG1_ADICLK(1) // 7 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 14 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 14 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 14 MHz +#elif F_BUS == 96000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 12 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 12 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 12 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 24 MHz +#elif F_BUS == 90000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 11.25 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 11.25 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 11.25 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 22.5 MHz +#elif F_BUS == 80000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 10 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 10 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 10 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 20 MHz +#elif F_BUS == 72000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 9 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 18 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 18 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 18 MHz +#elif F_BUS == 64000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 8 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 16 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 16 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 16 MHz +#elif F_BUS == 60000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 7.5 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 15 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 15 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 15 MHz +#elif F_BUS == 56000000 || F_BUS == 54000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(2) + ADC_CFG1_ADICLK(1) // 7 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 14 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 14 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 14 MHz +#elif F_BUS == 48000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 12 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 12 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 12 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(1) // 24 MHz +#elif F_BUS == 40000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 10 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 10 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 10 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(1) // 20 MHz +#elif F_BUS == 36000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(1) // 9 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(1) // 18 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(1) // 18 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(1) // 18 MHz +#elif F_BUS == 24000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(0) // 12 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(0) // 12 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(1) + ADC_CFG1_ADICLK(0) // 12 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 24 MHz +#elif F_BUS == 16000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 8 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 8 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 8 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 16 MHz +#elif F_BUS == 8000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 8 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 8 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 8 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 8 MHz +#elif F_BUS == 4000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 4 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 4 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 4 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 4 MHz +#elif F_BUS == 2000000 + #define ADC_CFG1_16BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 2 MHz + #define ADC_CFG1_12BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 2 MHz + #define ADC_CFG1_10BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 2 MHz + #define ADC_CFG1_8BIT ADC_CFG1_ADIV(0) + ADC_CFG1_ADICLK(0) // 2 MHz +#else +#error "F_BUS must be 128, 120, 108, 96, 90, 80, 72, 64, 60, 56, 54, 48, 40, 36, 24, 4 or 2 MHz" +#endif + +void analog_init(void) +{ + uint32_t num; + + #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + VREF_TRM = 0x60; + VREF_SC = 0xE1; // enable 1.2 volt ref + #endif + + if (analog_config_bits == 8) { + ADC0_CFG1 = ADC_CFG1_8BIT + ADC_CFG1_MODE(0); + ADC0_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(3); + #ifdef HAS_KINETIS_ADC1 + ADC1_CFG1 = ADC_CFG1_8BIT + ADC_CFG1_MODE(0); + ADC1_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(3); + #endif + } else if (analog_config_bits == 10) { + ADC0_CFG1 = ADC_CFG1_10BIT + ADC_CFG1_MODE(2) + ADC_CFG1_ADLSMP; + ADC0_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(3); + #ifdef HAS_KINETIS_ADC1 + ADC1_CFG1 = ADC_CFG1_10BIT + ADC_CFG1_MODE(2) + ADC_CFG1_ADLSMP; + ADC1_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(3); + #endif + } else if (analog_config_bits == 12) { + ADC0_CFG1 = ADC_CFG1_12BIT + ADC_CFG1_MODE(1) + ADC_CFG1_ADLSMP; + ADC0_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(2); + #ifdef HAS_KINETIS_ADC1 + ADC1_CFG1 = ADC_CFG1_12BIT + ADC_CFG1_MODE(1) + ADC_CFG1_ADLSMP; + ADC1_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(2); + #endif + } else { + ADC0_CFG1 = ADC_CFG1_16BIT + ADC_CFG1_MODE(3) + ADC_CFG1_ADLSMP; + ADC0_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(2); + #ifdef HAS_KINETIS_ADC1 + ADC1_CFG1 = ADC_CFG1_16BIT + ADC_CFG1_MODE(3) + ADC_CFG1_ADLSMP; + ADC1_CFG2 = ADC_CFG2_MUXSEL + ADC_CFG2_ADLSTS(2); + #endif + } + + #if defined(__MK20DX128__) + if (analog_reference_internal) { + ADC0_SC2 = ADC_SC2_REFSEL(1); // 1.2V ref + } else { + ADC0_SC2 = ADC_SC2_REFSEL(0); // vcc/ext ref + } + #elif defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + if (analog_reference_internal) { + ADC0_SC2 = ADC_SC2_REFSEL(1); // 1.2V ref + ADC1_SC2 = ADC_SC2_REFSEL(1); // 1.2V ref + } else { + ADC0_SC2 = ADC_SC2_REFSEL(0); // vcc/ext ref + ADC1_SC2 = ADC_SC2_REFSEL(0); // vcc/ext ref + } + #elif defined(__MKL26Z64__) + if (analog_reference_internal) { + ADC0_SC2 = ADC_SC2_REFSEL(0); // external AREF + } else { + ADC0_SC2 = ADC_SC2_REFSEL(1); // vcc + } + #endif + + num = analog_num_average; + if (num <= 1) { + ADC0_SC3 = ADC_SC3_CAL; // begin cal + #ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_CAL; // begin cal + #endif + } else if (num <= 4) { + ADC0_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(0); + #ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(0); + #endif + } else if (num <= 8) { + ADC0_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(1); + #ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(1); + #endif + } else if (num <= 16) { + ADC0_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(2); + #ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(2); + #endif + } else { + ADC0_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(3); + #ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_CAL + ADC_SC3_AVGE + ADC_SC3_AVGS(3); + #endif + } + calibrating = 1; +} + +static void wait_for_cal(void) +{ + uint16_t sum; + + //serial_print("wait_for_cal\n"); +#if defined(HAS_KINETIS_ADC0) && defined(HAS_KINETIS_ADC1) + while ((ADC0_SC3 & ADC_SC3_CAL) || (ADC1_SC3 & ADC_SC3_CAL)) { + // wait + } +#elif defined(HAS_KINETIS_ADC0) + while (ADC0_SC3 & ADC_SC3_CAL) { + // wait + } +#endif + __disable_irq(); + if (calibrating) { + //serial_print("\n"); + sum = ADC0_CLPS + ADC0_CLP4 + ADC0_CLP3 + ADC0_CLP2 + ADC0_CLP1 + ADC0_CLP0; + sum = (sum / 2) | 0x8000; + ADC0_PG = sum; + //serial_print("ADC0_PG = "); + //serial_phex16(sum); + //serial_print("\n"); + sum = ADC0_CLMS + ADC0_CLM4 + ADC0_CLM3 + ADC0_CLM2 + ADC0_CLM1 + ADC0_CLM0; + sum = (sum / 2) | 0x8000; + ADC0_MG = sum; + //serial_print("ADC0_MG = "); + //serial_phex16(sum); + //serial_print("\n"); +#ifdef HAS_KINETIS_ADC1 + sum = ADC1_CLPS + ADC1_CLP4 + ADC1_CLP3 + ADC1_CLP2 + ADC1_CLP1 + ADC1_CLP0; + sum = (sum / 2) | 0x8000; + ADC1_PG = sum; + sum = ADC1_CLMS + ADC1_CLM4 + ADC1_CLM3 + ADC1_CLM2 + ADC1_CLM1 + ADC1_CLM0; + sum = (sum / 2) | 0x8000; + ADC1_MG = sum; +#endif + calibrating = 0; + } + __enable_irq(); +} + +// ADCx_SC2[REFSEL] bit selects the voltage reference sources for ADC. +// VREFH/VREFL - connected as the primary reference option +// 1.2 V VREF_OUT - connected as the VALT reference option + +#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define DEFAULT 0 +#define INTERNAL 2 +#define INTERNAL1V2 2 +#define INTERNAL1V1 2 +#define EXTERNAL 0 + +#elif defined(__MKL26Z64__) +#define DEFAULT 0 +#define INTERNAL 0 +#define EXTERNAL 1 +#endif + +void analogReference(uint8_t type) +{ + if (type) { + // internal reference requested + if (!analog_reference_internal) { + analog_reference_internal = 1; + if (calibrating) { + ADC0_SC3 = 0; // cancel cal +#ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = 0; // cancel cal +#endif + } + analog_init(); + } + } else { + // vcc or external reference requested + if (analog_reference_internal) { + analog_reference_internal = 0; + if (calibrating) { + ADC0_SC3 = 0; // cancel cal +#ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = 0; // cancel cal +#endif + } + analog_init(); + } + } +} + + +void analogReadRes(unsigned int bits) +{ + unsigned int config; + + if (bits >= 13) { + if (bits > 16) bits = 16; + config = 16; + } else if (bits >= 11) { + config = 12; + } else if (bits >= 9) { + config = 10; + } else { + config = 8; + } + analog_right_shift = config - bits; + if (config != analog_config_bits) { + analog_config_bits = config; + if (calibrating) { + ADC0_SC3 = 0; // cancel cal + #ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = 0; + #endif + } + analog_init(); + } +} + +void analogReadAveraging(unsigned int num) +{ + + if (calibrating) wait_for_cal(); + if (num <= 1) { + num = 0; + ADC0_SC3 = 0; + } else if (num <= 4) { + num = 4; + ADC0_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(0); +#ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(0); +#endif + } else if (num <= 8) { + num = 8; + ADC0_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(1); +#ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(1); +#endif + } else if (num <= 16) { + num = 16; + ADC0_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(2); +#ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(2); +#endif + } else { + num = 32; + ADC0_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(3); +#ifdef HAS_KINETIS_ADC1 + ADC1_SC3 = ADC_SC3_AVGE + ADC_SC3_AVGS(3); +#endif + } + analog_num_average = num; +} + +// The SC1A register is used for both software and hardware trigger modes of operation. + +#if defined(__MK20DX128__) +static const uint8_t pin2sc1a[] = { + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, 0, 19, 3, 21, // 0-13 -> A0-A13 + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, // 14-23 are A0-A9 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 24-33 are digital only + 0, 19, 3, 21, // 34-37 are A10-A13 + 26, // 38 is temp sensor + 22, // 39 is vref + 23 // 40 is unused analog pin +}; +#elif defined(__MK20DX256__) +static const uint8_t pin2sc1a[] = { + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, 0, 19, 3, 19+128, // 0-13 -> A0-A13 + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, // 14-23 are A0-A9 + 255, 255, // 24-25 are digital only + 5+192, 5+128, 4+128, 6+128, 7+128, 4+192, // 26-31 are A15-A20 + 255, 255, // 32-33 are digital only + 0, 19, 3, 19+128, // 34-37 are A10-A13 + 26, // 38 is temp sensor, + 18+128, // 39 is vref + 23 // 40 is A14 +}; +#elif defined(__MKL26Z64__) +static const uint8_t pin2sc1a[] = { + 5, 14, 8, 9, 13, 12, 6, 7, 15, 11, 0, 4+64, 23, // 0-12 -> A0-A12 + 255, // 13 is digital only (no A13 alias) + 5, 14, 8, 9, 13, 12, 6, 7, 15, 11, 0, 4+64, 23, // 14-26 are A0-A12 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 27-37 unused + 26, // 38=temperature + 27 // 39=bandgap ref (PMC_REGSC |= PMC_REGSC_BGBE) +}; +#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) +static const uint8_t pin2sc1a[] = { + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, 3, 19+128, 14+128, 15+128, // 0-13 -> A0-A13 + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, // 14-23 are A0-A9 + 255, 255, 255, 255, 255, 255, 255, // 24-30 are digital only + 14+128, 15+128, 17, 18, 4+128, 5+128, 6+128, 7+128, 17+128, // 31-39 are A12-A20 + 255, 255, 255, 255, 255, 255, 255, 255, 255, // 40-48 are digital only + 10+128, 11+128, // 49-50 are A23-A24 + 255, 255, 255, 255, 255, 255, 255, // 51-57 are digital only + 255, 255, 255, 255, 255, 255, // 58-63 (sd card pins) are digital only + 3, 19+128, // 64-65 are A10-A11 + 23, 23+128,// 66-67 are A21-A22 (DAC pins) + 1, 1+128, // 68-69 are A25-A26 (unused USB host port on Teensy 3.5) + 26, // 70 is Temperature Sensor + 18+128 // 71 is Vref +}; +#endif + + + +// TODO: perhaps this should store the NVIC priority, so it works recursively? +static volatile uint8_t analogReadBusyADC0 = 0; +#ifdef HAS_KINETIS_ADC1 +static volatile uint8_t analogReadBusyADC1 = 0; +#endif + +int analogRead(uint8_t pin) +{ + int result; + uint8_t channel; + + //serial_phex(pin); + //serial_print(" "); + + if (pin >= sizeof(pin2sc1a)) return 0; + channel = pin2sc1a[pin]; + if (channel == 255) return 0; + + if (calibrating) wait_for_cal(); + +#ifdef HAS_KINETIS_ADC1 + if (channel & 0x80) goto beginADC1; +#endif + + __disable_irq(); +startADC0: + //serial_print("startADC0\n"); +#if defined(__MKL26Z64__) + if (channel & 0x40) { + ADC0_CFG2 &= ~ADC_CFG2_MUXSEL; + channel &= 0x3F; + } else { + ADC0_CFG2 |= ADC_CFG2_MUXSEL; + } +#endif + ADC0_SC1A = channel; + analogReadBusyADC0 = 1; + __enable_irq(); + while (1) { + __disable_irq(); + if ((ADC0_SC1A & ADC_SC1_COCO)) { + result = ADC0_RA; + analogReadBusyADC0 = 0; + __enable_irq(); + result >>= analog_right_shift; + return result; + } + // detect if analogRead was used from an interrupt + // if so, our analogRead got canceled, so it must + // be restarted. + if (!analogReadBusyADC0) goto startADC0; + __enable_irq(); + yield(); + } + +#ifdef HAS_KINETIS_ADC1 +beginADC1: + __disable_irq(); +startADC1: + //serial_print("startADC1\n"); + // ADC1_CFG2[MUXSEL] bit selects between ADCx_SEn channels a and b. + if (channel & 0x40) { + ADC1_CFG2 &= ~ADC_CFG2_MUXSEL; + } else { + ADC1_CFG2 |= ADC_CFG2_MUXSEL; + } + ADC1_SC1A = channel & 0x3F; + analogReadBusyADC1 = 1; + __enable_irq(); + while (1) { + __disable_irq(); + if ((ADC1_SC1A & ADC_SC1_COCO)) { + result = ADC1_RA; + analogReadBusyADC1 = 0; + __enable_irq(); + result >>= analog_right_shift; + return result; + } + // detect if analogRead was used from an interrupt + // if so, our analogRead got canceled, so it must + // be restarted. + if (!analogReadBusyADC1) goto startADC1; + __enable_irq(); + yield(); + } +#endif +} + +typedef int16_t __attribute__((__may_alias__)) aliased_int16_t; + +void analogWriteDAC0(int val) +{ +#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + SIM_SCGC2 |= SIM_SCGC2_DAC0; + if (analog_reference_internal) { + DAC0_C0 = DAC_C0_DACEN; // 1.2V ref is DACREF_1 + } else { + DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 3.3V VDDA is DACREF_2 + } + __asm__ ("usat %[value], #12, %[value]\n\t" : [value] "+r" (val)); // 0 <= val <= 4095 + + *(volatile aliased_int16_t *)&(DAC0_DAT0L) = val; +#elif defined(__MKL26Z64__) + SIM_SCGC6 |= SIM_SCGC6_DAC0; + if (analog_reference_internal == 0) { + // use 3.3V VDDA power as the reference (this is the default) + DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS | DAC_C0_DACSWTRG; // 3.3V VDDA + } else { + // use whatever voltage is on the AREF pin + DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACSWTRG; // 3.3V VDDA + } + if (val < 0) val = 0; + else if (val > 4095) val = 4095; + + *(volatile aliased_int16_t *)&(DAC0_DAT0L) = val; +#endif +} + + +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) +void analogWriteDAC1(int val) +{ + SIM_SCGC2 |= SIM_SCGC2_DAC1; + if (analog_reference_internal) { + DAC1_C0 = DAC_C0_DACEN; // 1.2V ref is DACREF_1 + } else { + DAC1_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 3.3V VDDA is DACREF_2 + } + __asm__ ("usat %[value], #12, %[value]\n\t" : [value] "+r" (val)); // 0 <= val <= 4095 + + *(volatile aliased_int16_t *)&(DAC1_DAT0L) = val; +} +#endif + + + + + + + + + + + + + + + diff --git a/apps/common/pins_teensy.c b/apps/common/pins_teensy.c new file mode 100644 index 0000000..412459e --- /dev/null +++ b/apps/common/pins_teensy.c @@ -0,0 +1,1363 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2017 PJRC.COM, LLC. + * + * 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: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * 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 "core_pins.h" +#include "pins_arduino.h" +#include "HardwareSerial.h" + + +#if defined(KINETISK) +#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) +#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit))) +//#define GPIO_SET_BIT(reg, bit) (*GPIO_BITBAND_PTR((reg), (bit)) = 1) +//#define GPIO_CLR_BIT(reg, bit) (*GPIO_BITBAND_PTR((reg), (bit)) = 0) +const struct digital_pin_bitband_and_config_table_struct digital_pin_to_info_PGM[] = { + {GPIO_BITBAND_PTR(CORE_PIN0_PORTREG, CORE_PIN0_BIT), &CORE_PIN0_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN1_PORTREG, CORE_PIN1_BIT), &CORE_PIN1_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN2_PORTREG, CORE_PIN2_BIT), &CORE_PIN2_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN3_PORTREG, CORE_PIN3_BIT), &CORE_PIN3_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN4_PORTREG, CORE_PIN4_BIT), &CORE_PIN4_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN5_PORTREG, CORE_PIN5_BIT), &CORE_PIN5_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN6_PORTREG, CORE_PIN6_BIT), &CORE_PIN6_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN7_PORTREG, CORE_PIN7_BIT), &CORE_PIN7_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN8_PORTREG, CORE_PIN8_BIT), &CORE_PIN8_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN9_PORTREG, CORE_PIN9_BIT), &CORE_PIN9_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN10_PORTREG, CORE_PIN10_BIT), &CORE_PIN10_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN11_PORTREG, CORE_PIN11_BIT), &CORE_PIN11_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN12_PORTREG, CORE_PIN12_BIT), &CORE_PIN12_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN13_PORTREG, CORE_PIN13_BIT), &CORE_PIN13_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN14_PORTREG, CORE_PIN14_BIT), &CORE_PIN14_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN15_PORTREG, CORE_PIN15_BIT), &CORE_PIN15_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN16_PORTREG, CORE_PIN16_BIT), &CORE_PIN16_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN17_PORTREG, CORE_PIN17_BIT), &CORE_PIN17_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN18_PORTREG, CORE_PIN18_BIT), &CORE_PIN18_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN19_PORTREG, CORE_PIN19_BIT), &CORE_PIN19_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN20_PORTREG, CORE_PIN20_BIT), &CORE_PIN20_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN21_PORTREG, CORE_PIN21_BIT), &CORE_PIN21_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN22_PORTREG, CORE_PIN22_BIT), &CORE_PIN22_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN23_PORTREG, CORE_PIN23_BIT), &CORE_PIN23_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN24_PORTREG, CORE_PIN24_BIT), &CORE_PIN24_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN25_PORTREG, CORE_PIN25_BIT), &CORE_PIN25_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN26_PORTREG, CORE_PIN26_BIT), &CORE_PIN26_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN27_PORTREG, CORE_PIN27_BIT), &CORE_PIN27_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN28_PORTREG, CORE_PIN28_BIT), &CORE_PIN28_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN29_PORTREG, CORE_PIN29_BIT), &CORE_PIN29_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN30_PORTREG, CORE_PIN30_BIT), &CORE_PIN30_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN31_PORTREG, CORE_PIN31_BIT), &CORE_PIN31_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN32_PORTREG, CORE_PIN32_BIT), &CORE_PIN32_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN33_PORTREG, CORE_PIN33_BIT), &CORE_PIN33_CONFIG}, +#ifdef CORE_PIN34_PORTREG + {GPIO_BITBAND_PTR(CORE_PIN34_PORTREG, CORE_PIN34_BIT), &CORE_PIN34_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN35_PORTREG, CORE_PIN35_BIT), &CORE_PIN35_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN36_PORTREG, CORE_PIN36_BIT), &CORE_PIN36_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN37_PORTREG, CORE_PIN37_BIT), &CORE_PIN37_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN38_PORTREG, CORE_PIN38_BIT), &CORE_PIN38_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN39_PORTREG, CORE_PIN39_BIT), &CORE_PIN39_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN40_PORTREG, CORE_PIN40_BIT), &CORE_PIN40_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN41_PORTREG, CORE_PIN41_BIT), &CORE_PIN41_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN42_PORTREG, CORE_PIN42_BIT), &CORE_PIN42_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN43_PORTREG, CORE_PIN43_BIT), &CORE_PIN43_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN44_PORTREG, CORE_PIN44_BIT), &CORE_PIN44_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN45_PORTREG, CORE_PIN45_BIT), &CORE_PIN45_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN46_PORTREG, CORE_PIN46_BIT), &CORE_PIN46_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN47_PORTREG, CORE_PIN47_BIT), &CORE_PIN47_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN48_PORTREG, CORE_PIN48_BIT), &CORE_PIN48_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN49_PORTREG, CORE_PIN49_BIT), &CORE_PIN49_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN50_PORTREG, CORE_PIN50_BIT), &CORE_PIN50_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN51_PORTREG, CORE_PIN51_BIT), &CORE_PIN51_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN52_PORTREG, CORE_PIN52_BIT), &CORE_PIN52_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN53_PORTREG, CORE_PIN53_BIT), &CORE_PIN53_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN54_PORTREG, CORE_PIN54_BIT), &CORE_PIN54_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN55_PORTREG, CORE_PIN55_BIT), &CORE_PIN55_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN56_PORTREG, CORE_PIN56_BIT), &CORE_PIN56_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN57_PORTREG, CORE_PIN57_BIT), &CORE_PIN57_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN58_PORTREG, CORE_PIN58_BIT), &CORE_PIN58_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN59_PORTREG, CORE_PIN59_BIT), &CORE_PIN59_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN60_PORTREG, CORE_PIN60_BIT), &CORE_PIN60_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN61_PORTREG, CORE_PIN61_BIT), &CORE_PIN61_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN62_PORTREG, CORE_PIN62_BIT), &CORE_PIN62_CONFIG}, + {GPIO_BITBAND_PTR(CORE_PIN63_PORTREG, CORE_PIN63_BIT), &CORE_PIN63_CONFIG}, +#endif +}; + +#elif defined(KINETISL) +const struct digital_pin_bitband_and_config_table_struct digital_pin_to_info_PGM[] = { + {((volatile uint8_t *)&CORE_PIN0_PORTREG + (CORE_PIN0_BIT >> 3)), &CORE_PIN0_CONFIG, (1<<(CORE_PIN0_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN1_PORTREG + (CORE_PIN1_BIT >> 3)), &CORE_PIN1_CONFIG, (1<<(CORE_PIN1_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN2_PORTREG + (CORE_PIN2_BIT >> 3)), &CORE_PIN2_CONFIG, (1<<(CORE_PIN2_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN3_PORTREG + (CORE_PIN3_BIT >> 3)), &CORE_PIN3_CONFIG, (1<<(CORE_PIN3_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN4_PORTREG + (CORE_PIN4_BIT >> 3)), &CORE_PIN4_CONFIG, (1<<(CORE_PIN4_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN5_PORTREG + (CORE_PIN5_BIT >> 3)), &CORE_PIN5_CONFIG, (1<<(CORE_PIN5_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN6_PORTREG + (CORE_PIN6_BIT >> 3)), &CORE_PIN6_CONFIG, (1<<(CORE_PIN6_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN7_PORTREG + (CORE_PIN7_BIT >> 3)), &CORE_PIN7_CONFIG, (1<<(CORE_PIN7_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN8_PORTREG + (CORE_PIN8_BIT >> 3)), &CORE_PIN8_CONFIG, (1<<(CORE_PIN8_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN9_PORTREG + (CORE_PIN9_BIT >> 3)), &CORE_PIN9_CONFIG, (1<<(CORE_PIN9_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN10_PORTREG + (CORE_PIN10_BIT >> 3)), &CORE_PIN10_CONFIG, (1<<(CORE_PIN10_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN11_PORTREG + (CORE_PIN11_BIT >> 3)), &CORE_PIN11_CONFIG, (1<<(CORE_PIN11_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN12_PORTREG + (CORE_PIN12_BIT >> 3)), &CORE_PIN12_CONFIG, (1<<(CORE_PIN12_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN13_PORTREG + (CORE_PIN13_BIT >> 3)), &CORE_PIN13_CONFIG, (1<<(CORE_PIN13_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN14_PORTREG + (CORE_PIN14_BIT >> 3)), &CORE_PIN14_CONFIG, (1<<(CORE_PIN14_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN15_PORTREG + (CORE_PIN15_BIT >> 3)), &CORE_PIN15_CONFIG, (1<<(CORE_PIN15_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN16_PORTREG + (CORE_PIN16_BIT >> 3)), &CORE_PIN16_CONFIG, (1<<(CORE_PIN16_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN17_PORTREG + (CORE_PIN17_BIT >> 3)), &CORE_PIN17_CONFIG, (1<<(CORE_PIN17_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN18_PORTREG + (CORE_PIN18_BIT >> 3)), &CORE_PIN18_CONFIG, (1<<(CORE_PIN18_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN19_PORTREG + (CORE_PIN19_BIT >> 3)), &CORE_PIN19_CONFIG, (1<<(CORE_PIN19_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN20_PORTREG + (CORE_PIN20_BIT >> 3)), &CORE_PIN20_CONFIG, (1<<(CORE_PIN20_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN21_PORTREG + (CORE_PIN21_BIT >> 3)), &CORE_PIN21_CONFIG, (1<<(CORE_PIN21_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN22_PORTREG + (CORE_PIN22_BIT >> 3)), &CORE_PIN22_CONFIG, (1<<(CORE_PIN22_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN23_PORTREG + (CORE_PIN23_BIT >> 3)), &CORE_PIN23_CONFIG, (1<<(CORE_PIN23_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN24_PORTREG + (CORE_PIN24_BIT >> 3)), &CORE_PIN24_CONFIG, (1<<(CORE_PIN24_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN25_PORTREG + (CORE_PIN25_BIT >> 3)), &CORE_PIN25_CONFIG, (1<<(CORE_PIN25_BIT & 7))}, + {((volatile uint8_t *)&CORE_PIN26_PORTREG + (CORE_PIN26_BIT >> 3)), &CORE_PIN26_CONFIG, (1<<(CORE_PIN26_BIT & 7))} +}; + +#endif + +static void dummy_isr() {}; + +typedef void (*voidFuncPtr)(void); +#if defined(KINETISK) +#ifdef NO_PORT_ISR_FASTRUN +static void port_A_isr(void); +static void port_B_isr(void); +static void port_C_isr(void); +static void port_D_isr(void); +static void port_E_isr(void); +#else +static void port_A_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone )); +static void port_B_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone )); +static void port_C_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone )); +static void port_D_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone )); +static void port_E_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone )); +#endif + +voidFuncPtr isr_table_portA[CORE_MAX_PIN_PORTA+1] = { [0 ... CORE_MAX_PIN_PORTA] = dummy_isr }; +voidFuncPtr isr_table_portB[CORE_MAX_PIN_PORTB+1] = { [0 ... CORE_MAX_PIN_PORTB] = dummy_isr }; +voidFuncPtr isr_table_portC[CORE_MAX_PIN_PORTC+1] = { [0 ... CORE_MAX_PIN_PORTC] = dummy_isr }; +voidFuncPtr isr_table_portD[CORE_MAX_PIN_PORTD+1] = { [0 ... CORE_MAX_PIN_PORTD] = dummy_isr }; +voidFuncPtr isr_table_portE[CORE_MAX_PIN_PORTE+1] = { [0 ... CORE_MAX_PIN_PORTE] = dummy_isr }; + +// The Pin Config Register is used to look up the correct interrupt table +// for the corresponding port. +static inline voidFuncPtr* getIsrTable(volatile uint32_t *config) { + voidFuncPtr* isr_table = NULL; + if(&PORTA_PCR0 <= config && config <= &PORTA_PCR31) isr_table = isr_table_portA; + else if(&PORTB_PCR0 <= config && config <= &PORTB_PCR31) isr_table = isr_table_portB; + else if(&PORTC_PCR0 <= config && config <= &PORTC_PCR31) isr_table = isr_table_portC; + else if(&PORTD_PCR0 <= config && config <= &PORTD_PCR31) isr_table = isr_table_portD; + else if(&PORTE_PCR0 <= config && config <= &PORTE_PCR31) isr_table = isr_table_portE; + return isr_table; +} + +inline uint32_t getPinIndex(volatile uint32_t *config) { + uintptr_t v = (uintptr_t) config; + // There are 32 pin config registers for each port, each port starting at a round address. + // They are spaced 4 bytes apart. + return (v % 128) / 4; +} +#elif defined(KINETISL) +volatile static voidFuncPtr intFunc[CORE_NUM_DIGITAL] = { [0 ... CORE_NUM_DIGITAL-1] = dummy_isr }; +static void porta_interrupt(void); +static void portcd_interrupt(void); +#endif + +void attachInterruptVector(enum IRQ_NUMBER_t irq, void (*function)(void)) +{ +// _VectorsRam[irq + 16] = function; +} + +void attachInterrupt(uint8_t pin, void (*function)(void), int mode) +{ + volatile uint32_t *config; + uint32_t cfg, mask; + + if (pin >= CORE_NUM_DIGITAL) return; + switch (mode) { + case CHANGE: mask = 0x0B; break; + case RISING: mask = 0x09; break; + case FALLING: mask = 0x0A; break; + case LOW: mask = 0x08; break; + case HIGH: mask = 0x0C; break; + default: return; + } + mask = (mask << 16) | 0x01000000; + config = portConfigRegister(pin); + if ((*config & 0x00000700) == 0) { + // for compatibility with programs which depend + // on AVR hardware default to input mode. + pinMode(pin, INPUT); + } +#if defined(KINETISK) + attachInterruptVector(IRQ_PORTA, port_A_isr); + attachInterruptVector(IRQ_PORTB, port_B_isr); + attachInterruptVector(IRQ_PORTC, port_C_isr); + attachInterruptVector(IRQ_PORTD, port_D_isr); + attachInterruptVector(IRQ_PORTE, port_E_isr); + voidFuncPtr* isr_table = getIsrTable(config); + if(!isr_table) return; + uint32_t pin_index = getPinIndex(config); + __disable_irq(); + cfg = *config; + cfg &= ~0x000F0000; // disable any previous interrupt + *config = cfg; + isr_table[pin_index] = function; // set the function pointer + cfg |= mask; + *config = cfg; // enable the new interrupt + __enable_irq(); +#elif defined(KINETISL) + attachInterruptVector(IRQ_PORTA, porta_interrupt); + attachInterruptVector(IRQ_PORTCD, portcd_interrupt); + __disable_irq(); + cfg = *config; + cfg &= ~0x000F0000; // disable any previous interrupt + *config = cfg; + intFunc[pin] = function; // set the function pointer + cfg |= mask; + *config = cfg; // enable the new interrupt + __enable_irq(); +#endif +} + +void detachInterrupt(uint8_t pin) +{ + volatile uint32_t *config; + + config = portConfigRegister(pin); +#if defined(KINETISK) + voidFuncPtr* isr_table = getIsrTable(config); + if(!isr_table) return; + uint32_t pin_index = getPinIndex(config); + __disable_irq(); + *config = ((*config & ~0x000F0000) | 0x01000000); + isr_table[pin_index] = dummy_isr; + __enable_irq(); +#elif defined(KINETISL) + __disable_irq(); + *config = ((*config & ~0x000F0000) | 0x01000000); + intFunc[pin] = dummy_isr; + __enable_irq(); +#endif +} + + +typedef void (*voidFuncPtr)(void); + +// Using CTZ instead of CLZ is faster, since it allows more efficient bit +// clearing and fast indexing into the pin ISR table. +#define PORT_ISR_FUNCTION_CLZ(port_name) \ + static void port_ ## port_name ## _isr(void) { \ + uint32_t isfr = PORT ## port_name ##_ISFR; \ + PORT ## port_name ##_ISFR = isfr; \ + voidFuncPtr* isr_table = isr_table_port ## port_name; \ + uint32_t bit_nr; \ + while(isfr) { \ + bit_nr = __builtin_ctz(isfr); \ + isr_table[bit_nr](); \ + isfr = isfr & (isfr-1); \ + if(!isfr) return; \ + } \ + } +// END PORT_ISR_FUNCTION_CLZ + +#if defined(KINETISK) +PORT_ISR_FUNCTION_CLZ(A) +PORT_ISR_FUNCTION_CLZ(B) +PORT_ISR_FUNCTION_CLZ(C) +PORT_ISR_FUNCTION_CLZ(D) +PORT_ISR_FUNCTION_CLZ(E) +#elif defined(KINETISL) +// Kinetis L (Teensy LC) is based on Cortex M0 and doesn't have hardware +// support for CLZ. + +#define DISPATCH_PIN_ISR(pin_nr) { voidFuncPtr pin_isr = intFunc[pin_nr]; \ + if(isfr & CORE_PIN ## pin_nr ## _BITMASK) pin_isr(); } + +static void porta_interrupt(void) +{ + uint32_t isfr = PORTA_ISFR; + PORTA_ISFR = isfr; + DISPATCH_PIN_ISR(3); + DISPATCH_PIN_ISR(4); +} + +static void portcd_interrupt(void) +{ + uint32_t isfr = PORTC_ISFR; + PORTC_ISFR = isfr; + DISPATCH_PIN_ISR(9); + DISPATCH_PIN_ISR(10); + DISPATCH_PIN_ISR(11); + DISPATCH_PIN_ISR(12); + DISPATCH_PIN_ISR(13); + DISPATCH_PIN_ISR(15); + DISPATCH_PIN_ISR(22); + DISPATCH_PIN_ISR(23); + isfr = PORTD_ISFR; + PORTD_ISFR = isfr; + DISPATCH_PIN_ISR(2); + DISPATCH_PIN_ISR(5); + DISPATCH_PIN_ISR(6); + DISPATCH_PIN_ISR(7); + DISPATCH_PIN_ISR(8); + DISPATCH_PIN_ISR(14); + DISPATCH_PIN_ISR(20); + DISPATCH_PIN_ISR(21); +} +#undef DISPATCH_PIN_ISR + +#endif + + +#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + +unsigned long rtc_get(void) +{ + return RTC_TSR; +} + +void rtc_set(unsigned long t) +{ + RTC_SR = 0; + RTC_TPR = 0; + RTC_TSR = t; + RTC_SR = RTC_SR_TCE; +} + +// adjust is the amount of crystal error to compensate, 1 = 0.1192 ppm +// For example, adjust = -100 is slows the clock by 11.92 ppm +// +void rtc_compensate(int adjust) +{ + uint32_t comp, interval, tcr; + + // This simple approach tries to maximize the interval. + // Perhaps minimizing TCR would be better, so the + // compensation is distributed more evenly across + // many seconds, rather than saving it all up and then + // altering one second up to +/- 0.38% + if (adjust >= 0) { + comp = adjust; + interval = 256; + while (1) { + tcr = comp * interval; + if (tcr < 128*256) break; + if (--interval == 1) break; + } + tcr = tcr >> 8; + } else { + comp = -adjust; + interval = 256; + while (1) { + tcr = comp * interval; + if (tcr < 129*256) break; + if (--interval == 1) break; + } + tcr = tcr >> 8; + tcr = 256 - tcr; + } + RTC_TCR = ((interval - 1) << 8) | tcr; +} + +#else + +unsigned long rtc_get(void) { return 0; } +void rtc_set(unsigned long t) { } +void rtc_compensate(int adjust) { } + +#endif + + + +#if 0 +// TODO: build system should define this +// so RTC is automatically initialized to approx correct time +// at least when the program begins running right after upload +#ifndef TIME_T +#define TIME_T 1350160272 +#endif + +void init_rtc(void) +{ + serial_print("init_rtc\n"); + //SIM_SCGC6 |= SIM_SCGC6_RTC; + + // enable the RTC crystal oscillator, for approx 12pf crystal + if (!(RTC_CR & RTC_CR_OSCE)) { + serial_print("start RTC oscillator\n"); + RTC_SR = 0; + RTC_CR = RTC_CR_SC16P | RTC_CR_SC4P | RTC_CR_OSCE; + } + // should wait for crystal to stabilize..... + + serial_print("SR="); + serial_phex32(RTC_SR); + serial_print("\n"); + serial_print("CR="); + serial_phex32(RTC_CR); + serial_print("\n"); + serial_print("TSR="); + serial_phex32(RTC_TSR); + serial_print("\n"); + serial_print("TCR="); + serial_phex32(RTC_TCR); + serial_print("\n"); + + if (RTC_SR & RTC_SR_TIF) { + // enable the RTC + RTC_SR = 0; + RTC_TPR = 0; + RTC_TSR = TIME_T; + RTC_SR = RTC_SR_TCE; + } +} +#endif + +extern void usb_init(void); + + +// create a default PWM at the same 488.28 Hz as Arduino Uno + +#if defined(KINETISK) +#define F_TIMER F_BUS +#elif defined(KINETISL) + +#if F_CPU > 16000000 +#define F_TIMER (F_PLL/2) +#else +#define F_TIMER (F_PLL) +#endif//Low Power + +#endif + +#if F_TIMER == 128000000 +#define DEFAULT_FTM_MOD (65536 - 1) +#define DEFAULT_FTM_PRESCALE 2 +#elif F_TIMER == 120000000 +#define DEFAULT_FTM_MOD (61440 - 1) +#define DEFAULT_FTM_PRESCALE 2 +#elif F_TIMER == 108000000 +#define DEFAULT_FTM_MOD (55296 - 1) +#define DEFAULT_FTM_PRESCALE 2 +#elif F_TIMER == 96000000 +#define DEFAULT_FTM_MOD (49152 - 1) +#define DEFAULT_FTM_PRESCALE 2 +#elif F_TIMER == 90000000 +#define DEFAULT_FTM_MOD (46080 - 1) +#define DEFAULT_FTM_PRESCALE 2 +#elif F_TIMER == 80000000 +#define DEFAULT_FTM_MOD (40960 - 1) +#define DEFAULT_FTM_PRESCALE 2 +#elif F_TIMER == 72000000 +#define DEFAULT_FTM_MOD (36864 - 1) +#define DEFAULT_FTM_PRESCALE 2 +#elif F_TIMER == 64000000 +#define DEFAULT_FTM_MOD (65536 - 1) +#define DEFAULT_FTM_PRESCALE 1 +#elif F_TIMER == 60000000 +#define DEFAULT_FTM_MOD (61440 - 1) +#define DEFAULT_FTM_PRESCALE 1 +#elif F_TIMER == 56000000 +#define DEFAULT_FTM_MOD (57344 - 1) +#define DEFAULT_FTM_PRESCALE 1 +#elif F_TIMER == 54000000 +#define DEFAULT_FTM_MOD (55296 - 1) +#define DEFAULT_FTM_PRESCALE 1 +#elif F_TIMER == 48000000 +#define DEFAULT_FTM_MOD (49152 - 1) +#define DEFAULT_FTM_PRESCALE 1 +#elif F_TIMER == 40000000 +#define DEFAULT_FTM_MOD (40960 - 1) +#define DEFAULT_FTM_PRESCALE 1 +#elif F_TIMER == 36000000 +#define DEFAULT_FTM_MOD (36864 - 1) +#define DEFAULT_FTM_PRESCALE 1 +#elif F_TIMER == 24000000 +#define DEFAULT_FTM_MOD (49152 - 1) +#define DEFAULT_FTM_PRESCALE 0 +#elif F_TIMER == 16000000 +#define DEFAULT_FTM_MOD (32768 - 1) +#define DEFAULT_FTM_PRESCALE 0 +#elif F_TIMER == 8000000 +#define DEFAULT_FTM_MOD (16384 - 1) +#define DEFAULT_FTM_PRESCALE 0 +#elif F_TIMER == 4000000 +#define DEFAULT_FTM_MOD (8192 - 1) +#define DEFAULT_FTM_PRESCALE 0 +#elif F_TIMER == 2000000 +#define DEFAULT_FTM_MOD (4096 - 1) +#define DEFAULT_FTM_PRESCALE 0 +#endif + +//void init_pins(void) +__attribute__((noinline)) +void _init_Teensyduino_internal_(void) +{ +#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + NVIC_ENABLE_IRQ(IRQ_PORTA); + NVIC_ENABLE_IRQ(IRQ_PORTB); + NVIC_ENABLE_IRQ(IRQ_PORTC); + NVIC_ENABLE_IRQ(IRQ_PORTD); + NVIC_ENABLE_IRQ(IRQ_PORTE); +#elif defined(__MKL26Z64__) + NVIC_ENABLE_IRQ(IRQ_PORTA); + NVIC_ENABLE_IRQ(IRQ_PORTCD); +#endif + //SIM_SCGC6 |= SIM_SCGC6_FTM0; // TODO: use bitband for atomic read-mod-write + //SIM_SCGC6 |= SIM_SCGC6_FTM1; + FTM0_CNT = 0; + FTM0_MOD = DEFAULT_FTM_MOD; + FTM0_C0SC = 0x28; // MSnB:MSnA = 10, ELSnB:ELSnA = 10 + FTM0_C1SC = 0x28; + FTM0_C2SC = 0x28; + FTM0_C3SC = 0x28; + FTM0_C4SC = 0x28; + FTM0_C5SC = 0x28; +#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + FTM0_C6SC = 0x28; + FTM0_C7SC = 0x28; +#endif +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) + FTM3_C0SC = 0x28; + FTM3_C1SC = 0x28; + FTM3_C2SC = 0x28; + FTM3_C3SC = 0x28; + FTM3_C4SC = 0x28; + FTM3_C5SC = 0x28; + FTM3_C6SC = 0x28; + FTM3_C7SC = 0x28; +#endif + FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(DEFAULT_FTM_PRESCALE); + FTM1_CNT = 0; + FTM1_MOD = DEFAULT_FTM_MOD; + FTM1_C0SC = 0x28; + FTM1_C1SC = 0x28; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(DEFAULT_FTM_PRESCALE); +#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__MKL26Z64__) + FTM2_CNT = 0; + FTM2_MOD = DEFAULT_FTM_MOD; + FTM2_C0SC = 0x28; + FTM2_C1SC = 0x28; + FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(DEFAULT_FTM_PRESCALE); +#endif +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) + FTM3_CNT = 0; + FTM3_MOD = DEFAULT_FTM_MOD; + FTM3_C0SC = 0x28; + FTM3_C1SC = 0x28; + FTM3_SC = FTM_SC_CLKS(1) | FTM_SC_PS(DEFAULT_FTM_PRESCALE); +#endif +#if defined(__MK66FX1M0__) + SIM_SCGC2 |= SIM_SCGC2_TPM1; + SIM_SOPT2 |= SIM_SOPT2_TPMSRC(2); + TPM1_CNT = 0; + TPM1_MOD = 32767; + TPM1_C0SC = 0x28; + TPM1_C1SC = 0x28; + TPM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); +#endif + analog_init(); + +#if !defined(TEENSY_INIT_USB_DELAY_BEFORE) + #if TEENSYDUINO >= 142 + #define TEENSY_INIT_USB_DELAY_BEFORE 25 + #else + #define TEENSY_INIT_USB_DELAY_BEFORE 50 + #endif +#endif + +#if !defined(TEENSY_INIT_USB_DELAY_AFTER) + #if TEENSYDUINO >= 142 + #define TEENSY_INIT_USB_DELAY_AFTER 275 + #else + #define TEENSY_INIT_USB_DELAY_AFTER 350 + #endif +#endif + + // for background about this startup delay, please see these conversations + // 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); + //usb_init(); + delay(TEENSY_INIT_USB_DELAY_AFTER); +} + + +#if defined(__MK20DX128__) +#define FTM0_CH0_PIN 22 +#define FTM0_CH1_PIN 23 +#define FTM0_CH2_PIN 9 +#define FTM0_CH3_PIN 10 +#define FTM0_CH4_PIN 6 +#define FTM0_CH5_PIN 20 +#define FTM0_CH6_PIN 21 +#define FTM0_CH7_PIN 5 +#define FTM1_CH0_PIN 3 +#define FTM1_CH1_PIN 4 +#elif defined(__MK20DX256__) +#define FTM0_CH0_PIN 22 +#define FTM0_CH1_PIN 23 +#define FTM0_CH2_PIN 9 +#define FTM0_CH3_PIN 10 +#define FTM0_CH4_PIN 6 +#define FTM0_CH5_PIN 20 +#define FTM0_CH6_PIN 21 +#define FTM0_CH7_PIN 5 +#define FTM1_CH0_PIN 3 +#define FTM1_CH1_PIN 4 +#define FTM2_CH0_PIN 32 +#define FTM2_CH1_PIN 25 +#elif defined(__MKL26Z64__) +#define FTM0_CH0_PIN 22 +#define FTM0_CH1_PIN 23 +#define FTM0_CH2_PIN 9 +#define FTM0_CH3_PIN 10 +#define FTM0_CH4_PIN 6 +#define FTM0_CH5_PIN 20 +#define FTM1_CH0_PIN 16 +#define FTM1_CH1_PIN 17 +#define FTM2_CH0_PIN 3 +#define FTM2_CH1_PIN 4 +#elif defined(__MK64FX512__) +#define FTM0_CH0_PIN 22 +#define FTM0_CH1_PIN 23 +#define FTM0_CH2_PIN 9 +#define FTM0_CH3_PIN 10 +#define FTM0_CH4_PIN 6 +#define FTM0_CH5_PIN 20 +#define FTM0_CH6_PIN 21 +#define FTM0_CH7_PIN 5 +#define FTM1_CH0_PIN 3 +#define FTM1_CH1_PIN 4 +#define FTM2_CH0_PIN 29 +#define FTM2_CH1_PIN 30 +#define FTM3_CH0_PIN 2 +#define FTM3_CH1_PIN 14 +#define FTM3_CH2_PIN 7 +#define FTM3_CH3_PIN 8 +#define FTM3_CH4_PIN 35 +#define FTM3_CH5_PIN 36 +#define FTM3_CH6_PIN 37 +#define FTM3_CH7_PIN 38 +#elif defined(__MK66FX1M0__) +#define FTM0_CH0_PIN 22 +#define FTM0_CH1_PIN 23 +#define FTM0_CH2_PIN 9 +#define FTM0_CH3_PIN 10 +#define FTM0_CH4_PIN 6 +#define FTM0_CH5_PIN 20 +#define FTM0_CH6_PIN 21 +#define FTM0_CH7_PIN 5 +#define FTM1_CH0_PIN 3 +#define FTM1_CH1_PIN 4 +#define FTM2_CH0_PIN 29 +#define FTM2_CH1_PIN 30 +#define FTM3_CH0_PIN 2 +#define FTM3_CH1_PIN 14 +#define FTM3_CH2_PIN 7 +#define FTM3_CH3_PIN 8 +#define FTM3_CH4_PIN 35 +#define FTM3_CH5_PIN 36 +#define FTM3_CH6_PIN 37 +#define FTM3_CH7_PIN 38 +#define TPM1_CH0_PIN 16 +#define TPM1_CH1_PIN 17 +#endif +#define FTM_PINCFG(pin) FTM_PINCFG2(pin) +#define FTM_PINCFG2(pin) CORE_PIN ## pin ## _CONFIG + +static uint8_t analog_write_res = 8; + +// SOPT4 is SIM select clocks? +// FTM is clocked by the bus clock, either 24 or 48 MHz +// input capture can be FTM1_CH0, CMP0 or CMP1 or USB start of frame +// 24 MHz with reload 49152 to match Arduino's speed = 488.28125 Hz + +void analogWrite(uint8_t pin, int val) +{ + uint32_t cval, max; + +#if defined(__MK20DX256__) + if (pin == A14) { + uint8_t res = analog_write_res; + if (res < 12) { + val <<= 12 - res; + } else if (res > 12) { + val >>= res - 12; + } + analogWriteDAC0(val); + return; + } +#elif defined(__MKL26Z64__) + if (pin == A12) { + uint8_t res = analog_write_res; + if (res < 12) { + val <<= 12 - res; + } else if (res > 12) { + val >>= res - 12; + } + analogWriteDAC0(val); + return; + } +#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) + if (pin == A21 || pin == A22) { + uint8_t res = analog_write_res; + if (res < 12) { + val <<= 12 - res; + } else if (res > 12) { + val >>= res - 12; + } + if (pin == A21) analogWriteDAC0(val); + else analogWriteDAC1(val); + return; + } +#endif + + max = 1 << analog_write_res; + if (val <= 0) { + digitalWrite(pin, LOW); + pinMode(pin, OUTPUT); // TODO: implement OUTPUT_LOW + return; + } else if (val >= max) { + digitalWrite(pin, HIGH); + pinMode(pin, OUTPUT); // TODO: implement OUTPUT_HIGH + return; + } + + //serial_print("analogWrite\n"); + //serial_print("val = "); + //serial_phex32(val); + //serial_print("\n"); + //serial_print("analog_write_res = "); + //serial_phex(analog_write_res); + //serial_print("\n"); + if (pin == FTM1_CH0_PIN || pin == FTM1_CH1_PIN) { + cval = ((uint32_t)val * (uint32_t)(FTM1_MOD + 1)) >> analog_write_res; +#if defined(FTM2_CH0_PIN) + } else if (pin == FTM2_CH0_PIN || pin == FTM2_CH1_PIN) { + cval = ((uint32_t)val * (uint32_t)(FTM2_MOD + 1)) >> analog_write_res; +#endif +#if defined(FTM3_CH0_PIN) + } else if (pin == FTM3_CH0_PIN || pin == FTM3_CH1_PIN || pin == FTM3_CH2_PIN + || pin == FTM3_CH3_PIN || pin == FTM3_CH4_PIN || pin == FTM3_CH5_PIN + || pin == FTM3_CH6_PIN || pin == FTM3_CH7_PIN) { + cval = ((uint32_t)val * (uint32_t)(FTM3_MOD + 1)) >> analog_write_res; +#endif +#if defined(TPM1_CH0_PIN) + } else if (pin == TPM1_CH0_PIN || pin == TPM1_CH1_PIN) { + cval = ((uint32_t)val * (uint32_t)(TPM1_MOD + 1)) >> analog_write_res; +#endif + } else { + cval = ((uint32_t)val * (uint32_t)(FTM0_MOD + 1)) >> analog_write_res; + } + //serial_print("cval = "); + //serial_phex32(cval); + //serial_print("\n"); + switch (pin) { +#ifdef FTM0_CH0_PIN + case FTM0_CH0_PIN: // PTC1, FTM0_CH0 + FTM0_C0V = cval; + FTM_PINCFG(FTM0_CH0_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM0_CH1_PIN + case FTM0_CH1_PIN: // PTC2, FTM0_CH1 + FTM0_C1V = cval; + FTM_PINCFG(FTM0_CH1_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM0_CH2_PIN + case FTM0_CH2_PIN: // PTC3, FTM0_CH2 + FTM0_C2V = cval; + FTM_PINCFG(FTM0_CH2_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM0_CH3_PIN + case FTM0_CH3_PIN: // PTC4, FTM0_CH3 + FTM0_C3V = cval; + FTM_PINCFG(FTM0_CH3_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM0_CH4_PIN + case FTM0_CH4_PIN: // PTD4, FTM0_CH4 + FTM0_C4V = cval; + FTM_PINCFG(FTM0_CH4_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM0_CH5_PIN + case FTM0_CH5_PIN: // PTD5, FTM0_CH5 + FTM0_C5V = cval; + FTM_PINCFG(FTM0_CH5_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM0_CH6_PIN + case FTM0_CH6_PIN: // PTD6, FTM0_CH6 + FTM0_C6V = cval; + FTM_PINCFG(FTM0_CH6_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM0_CH7_PIN + case FTM0_CH7_PIN: // PTD7, FTM0_CH7 + FTM0_C7V = cval; + FTM_PINCFG(FTM0_CH7_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM1_CH0_PIN + case FTM1_CH0_PIN: // PTA12, FTM1_CH0 + FTM1_C0V = cval; + FTM_PINCFG(FTM1_CH0_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM1_CH1_PIN + case FTM1_CH1_PIN: // PTA13, FTM1_CH1 + FTM1_C1V = cval; + FTM_PINCFG(FTM1_CH1_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM2_CH0_PIN + case FTM2_CH0_PIN: // PTB18, FTM2_CH0 + FTM2_C0V = cval; + FTM_PINCFG(FTM2_CH0_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM2_CH1_PIN + case FTM2_CH1_PIN: // PTB19, FTM1_CH1 + FTM2_C1V = cval; + FTM_PINCFG(FTM2_CH1_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH0_PIN + case FTM3_CH0_PIN: + FTM3_C0V = cval; + FTM_PINCFG(FTM3_CH0_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH1_PIN + case FTM3_CH1_PIN: + FTM3_C1V = cval; + FTM_PINCFG(FTM3_CH1_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH2_PIN + case FTM3_CH2_PIN: + FTM3_C2V = cval; + FTM_PINCFG(FTM3_CH2_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH3_PIN + case FTM3_CH3_PIN: + FTM3_C3V = cval; + FTM_PINCFG(FTM3_CH3_PIN) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH4_PIN + case FTM3_CH4_PIN: + FTM3_C4V = cval; + FTM_PINCFG(FTM3_CH4_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH5_PIN + case FTM3_CH5_PIN: + FTM3_C5V = cval; + FTM_PINCFG(FTM3_CH5_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH6_PIN + case FTM3_CH6_PIN: + FTM3_C6V = cval; + FTM_PINCFG(FTM3_CH6_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef FTM3_CH7_PIN + case FTM3_CH7_PIN: + FTM3_C7V = cval; + FTM_PINCFG(FTM3_CH7_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef TPM1_CH0_PIN + case TPM1_CH0_PIN: + TPM1_C0V = cval; + FTM_PINCFG(TPM1_CH0_PIN) = PORT_PCR_MUX(6) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif +#ifdef TPM1_CH1_PIN + case TPM1_CH1_PIN: + TPM1_C1V = cval; + FTM_PINCFG(TPM1_CH1_PIN) = PORT_PCR_MUX(6) | PORT_PCR_DSE | PORT_PCR_SRE; + break; +#endif + default: + digitalWrite(pin, (val > 127) ? HIGH : LOW); + pinMode(pin, OUTPUT); + } +} + + +uint32_t analogWriteRes(uint32_t bits) +{ + uint32_t prior_res; + if (bits < 1) { + bits = 1; + } else if (bits > 16) { + bits = 16; + } + prior_res = analog_write_res; + analog_write_res = bits; + return prior_res; +} + + +void analogWriteFrequency(uint8_t pin, float frequency) +{ + uint32_t prescale, mod, ftmClock, ftmClockSource; + float minfreq; + + //serial_print("analogWriteFrequency: pin = "); + //serial_phex(pin); + //serial_print(", freq = "); + //serial_phex32((uint32_t)frequency); + //serial_print("\n"); + +#ifdef TPM1_CH0_PIN + if (pin == TPM1_CH0_PIN || pin == TPM1_CH1_PIN) { + ftmClockSource = 1; + ftmClock = 16000000; + } else +#endif +#if defined(__MKL26Z64__) + // Teensy LC does not support slow clock source (ftmClockSource = 2) + ftmClockSource = 1; // Use default F_TIMER clock source + ftmClock = F_TIMER; // Set variable for the actual timer clock frequency +#else + if (frequency < (float)(F_TIMER >> 7) / 65536.0f) { + // frequency is too low for working with F_TIMER: + ftmClockSource = 2; // Use alternative 31250Hz clock source + ftmClock = 31250; // Set variable for the actual timer clock frequency + } else { + ftmClockSource = 1; // Use default F_TIMER clock source + ftmClock = F_TIMER; // Set variable for the actual timer clock frequency + } +#endif + + + for (prescale = 0; prescale < 7; prescale++) { + minfreq = (float)(ftmClock >> prescale) / 65536.0f; //Use ftmClock instead of F_TIMER + if (frequency >= minfreq) break; + } + //serial_print("F_TIMER/ftm_Clock = "); + //serial_phex32(ftmClock >> prescale); + //serial_print("\n"); + //serial_print("prescale = "); + //serial_phex(prescale); + //serial_print("\n"); + mod = (float)(ftmClock >> prescale) / frequency - 0.5f; //Use ftmClock instead of F_TIMER + if (mod > 65535) mod = 65535; + //serial_print("mod = "); + //serial_phex32(mod); + //serial_print("\n"); + if (pin == FTM1_CH0_PIN || pin == FTM1_CH1_PIN) { + FTM1_SC = 0; + FTM1_CNT = 0; + FTM1_MOD = mod; + FTM1_SC = FTM_SC_CLKS(ftmClockSource) | FTM_SC_PS(prescale); //Use ftmClockSource instead of 1 + } else if (pin == FTM0_CH0_PIN || pin == FTM0_CH1_PIN + || pin == FTM0_CH2_PIN || pin == FTM0_CH3_PIN + || pin == FTM0_CH4_PIN || pin == FTM0_CH5_PIN +#ifdef FTM0_CH6_PIN + || pin == FTM0_CH6_PIN || pin == FTM0_CH7_PIN +#endif + ) { + FTM0_SC = 0; + FTM0_CNT = 0; + FTM0_MOD = mod; + FTM0_SC = FTM_SC_CLKS(ftmClockSource) | FTM_SC_PS(prescale); //Use ftmClockSource instead of 1 + } +#ifdef FTM2_CH0_PIN + else if (pin == FTM2_CH0_PIN || pin == FTM2_CH1_PIN) { + FTM2_SC = 0; + FTM2_CNT = 0; + FTM2_MOD = mod; + FTM2_SC = FTM_SC_CLKS(ftmClockSource) | FTM_SC_PS(prescale); //Use ftmClockSource instead of 1 + } +#endif +#ifdef FTM3_CH0_PIN + else if (pin == FTM3_CH0_PIN || pin == FTM3_CH1_PIN + || pin == FTM3_CH2_PIN || pin == FTM3_CH3_PIN + || pin == FTM3_CH4_PIN || pin == FTM3_CH5_PIN + || pin == FTM3_CH6_PIN || pin == FTM3_CH7_PIN) { + FTM3_SC = 0; + FTM3_CNT = 0; + FTM3_MOD = mod; + FTM3_SC = FTM_SC_CLKS(ftmClockSource) | FTM_SC_PS(prescale); //Use the new ftmClockSource instead of 1 + } +#endif +#ifdef TPM1_CH0_PIN + else if (pin == TPM1_CH0_PIN || pin == TPM1_CH1_PIN) { + TPM1_SC = 0; + TPM1_CNT = 0; + TPM1_MOD = mod; + TPM1_SC = FTM_SC_CLKS(ftmClockSource) | FTM_SC_PS(prescale); + } +#endif +} + + + +// TODO: startup code needs to initialize all pins to GPIO mode, input by default + +void digitalWrite(uint8_t pin, uint8_t val) +{ + if (pin >= CORE_NUM_DIGITAL) return; +#ifdef KINETISK + if (*portModeRegister(pin)) { + if (val) { + *portSetRegister(pin) = 1; + } else { + *portClearRegister(pin) = 1; + } +#else + if (*portModeRegister(pin) & digitalPinToBitMask(pin)) { + if (val) { + *portSetRegister(pin) = digitalPinToBitMask(pin); + } else { + *portClearRegister(pin) = digitalPinToBitMask(pin); + } +#endif + } else { + volatile uint32_t *config = portConfigRegister(pin); + if (val) { + // TODO use bitband for atomic read-mod-write + *config |= (PORT_PCR_PE | PORT_PCR_PS); + //*config = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; + } else { + // TODO use bitband for atomic read-mod-write + *config &= ~(PORT_PCR_PE); + //*config = PORT_PCR_MUX(1); + } + } + +} + +uint8_t digitalRead(uint8_t pin) +{ + if (pin >= CORE_NUM_DIGITAL) return 0; +#ifdef KINETISK + return *portInputRegister(pin); +#else + return (*portInputRegister(pin) & digitalPinToBitMask(pin)) ? 1 : 0; +#endif +} + + + +void pinMode(uint8_t pin, uint8_t mode) +{ + volatile uint32_t *config; + + if (pin >= CORE_NUM_DIGITAL) return; + config = portConfigRegister(pin); + + if (mode == OUTPUT || mode == OUTPUT_OPENDRAIN) { +#ifdef KINETISK + *portModeRegister(pin) = 1; +#else + *portModeRegister(pin) |= digitalPinToBitMask(pin); // TODO: atomic +#endif + *config = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + if (mode == OUTPUT_OPENDRAIN) { + *config |= PORT_PCR_ODE; + } else { + *config &= ~PORT_PCR_ODE; + } + } else { +#ifdef KINETISK + *portModeRegister(pin) = 0; +#else + *portModeRegister(pin) &= ~digitalPinToBitMask(pin); +#endif + if (mode == INPUT) { + *config = PORT_PCR_MUX(1); + } else if (mode == INPUT_PULLUP) { + *config = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; + } else if (mode == INPUT_PULLDOWN) { + *config = PORT_PCR_MUX(1) | PORT_PCR_PE; + } else { // INPUT_DISABLE + *config = 0; + } + } +} + + +void _shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t value) +{ + if (bitOrder == LSBFIRST) { + shiftOut_lsbFirst(dataPin, clockPin, value); + } else { + shiftOut_msbFirst(dataPin, clockPin, value); + } +} + +void shiftOut_lsbFirst(uint8_t dataPin, uint8_t clockPin, uint8_t value) +{ + uint8_t mask; + for (mask=0x01; mask; mask <<= 1) { + digitalWrite(dataPin, value & mask); + digitalWrite(clockPin, HIGH); + digitalWrite(clockPin, LOW); + } +} + +void shiftOut_msbFirst(uint8_t dataPin, uint8_t clockPin, uint8_t value) +{ + uint8_t mask; + for (mask=0x80; mask; mask >>= 1) { + digitalWrite(dataPin, value & mask); + digitalWrite(clockPin, HIGH); + digitalWrite(clockPin, LOW); + } +} + +uint8_t _shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) +{ + if (bitOrder == LSBFIRST) { + return shiftIn_lsbFirst(dataPin, clockPin); + } else { + return shiftIn_msbFirst(dataPin, clockPin); + } +} + +uint8_t shiftIn_lsbFirst(uint8_t dataPin, uint8_t clockPin) +{ + uint8_t mask, value=0; + for (mask=0x01; mask; mask <<= 1) { + digitalWrite(clockPin, HIGH); + if (digitalRead(dataPin)) value |= mask; + digitalWrite(clockPin, LOW); + } + return value; +} + +uint8_t shiftIn_msbFirst(uint8_t dataPin, uint8_t clockPin) +{ + uint8_t mask, value=0; + for (mask=0x80; mask; mask >>= 1) { + digitalWrite(clockPin, HIGH); + if (digitalRead(dataPin)) value |= mask; + digitalWrite(clockPin, LOW); + } + return value; +} + + + +// the systick interrupt is supposed to increment this at 1 kHz rate +volatile uint32_t systick_millis_count = 0; + +//uint32_t systick_current, systick_count, systick_istatus; // testing only + +uint32_t micros(void) +{ + uint32_t count, current, istatus; + + __disable_irq(); + current = SYST_CVR; + count = systick_millis_count; + istatus = SCB_ICSR; // bit 26 indicates if systick exception pending + __enable_irq(); + //systick_current = current; + //systick_count = count; + //systick_istatus = istatus & SCB_ICSR_PENDSTSET ? 1 : 0; + if ((istatus & SCB_ICSR_PENDSTSET) && current > 50) count++; + current = ((F_CPU / 1000) - 1) - current; +#if defined(KINETISL) && F_CPU == 48000000 + return count * 1000 + ((current * (uint32_t)87381) >> 22); +#elif defined(KINETISL) && F_CPU == 24000000 + return count * 1000 + ((current * (uint32_t)174763) >> 22); +#endif + return count * 1000 + current / (F_CPU / 1000000); +} + +void delay(uint32_t ms) +{ + uint32_t start = micros(); + + if (ms > 0) { + while (1) { + while ((micros() - start) >= 1000) { + ms--; + if (ms == 0) return; + start += 1000; + } + yield(); + } + } +} + +// TODO: verify these result in correct timeouts... +#if F_CPU == 256000000 +#define PULSEIN_LOOPS_PER_USEC 34 +#elif F_CPU == 240000000 +#define PULSEIN_LOOPS_PER_USEC 33 +#elif F_CPU == 216000000 +#define PULSEIN_LOOPS_PER_USEC 31 +#elif F_CPU == 192000000 +#define PULSEIN_LOOPS_PER_USEC 29 +#elif F_CPU == 180000000 +#define PULSEIN_LOOPS_PER_USEC 27 +#elif F_CPU == 168000000 +#define PULSEIN_LOOPS_PER_USEC 25 +#elif F_CPU == 144000000 +#define PULSEIN_LOOPS_PER_USEC 21 +#elif F_CPU == 120000000 +#define PULSEIN_LOOPS_PER_USEC 18 +#elif F_CPU == 96000000 +#define PULSEIN_LOOPS_PER_USEC 14 +#elif F_CPU == 72000000 +#define PULSEIN_LOOPS_PER_USEC 10 +#elif F_CPU == 48000000 +#define PULSEIN_LOOPS_PER_USEC 7 +#elif F_CPU == 24000000 +#define PULSEIN_LOOPS_PER_USEC 4 +#elif F_CPU == 16000000 +#define PULSEIN_LOOPS_PER_USEC 1 +#elif F_CPU == 8000000 +#define PULSEIN_LOOPS_PER_USEC 1 +#elif F_CPU == 4000000 +#define PULSEIN_LOOPS_PER_USEC 1 +#elif F_CPU == 2000000 +#define PULSEIN_LOOPS_PER_USEC 1 +#endif + +#if defined(KINETISK) +uint32_t pulseIn_high(volatile uint8_t *reg, uint32_t timeout) +{ + uint32_t timeout_count = timeout * PULSEIN_LOOPS_PER_USEC; + uint32_t usec_start, usec_stop; + + // wait for any previous pulse to end + while (*reg) { + if (--timeout_count == 0) return 0; + } + // wait for the pulse to start + while (!*reg) { + if (--timeout_count == 0) return 0; + } + usec_start = micros(); + // wait for the pulse to stop + while (*reg) { + if (--timeout_count == 0) return 0; + } + usec_stop = micros(); + return usec_stop - usec_start; +} + +uint32_t pulseIn_low(volatile uint8_t *reg, uint32_t timeout) +{ + uint32_t timeout_count = timeout * PULSEIN_LOOPS_PER_USEC; + uint32_t usec_start, usec_stop; + + // wait for any previous pulse to end + while (!*reg) { + if (--timeout_count == 0) return 0; + } + // wait for the pulse to start + while (*reg) { + if (--timeout_count == 0) return 0; + } + usec_start = micros(); + // wait for the pulse to stop + while (!*reg) { + if (--timeout_count == 0) return 0; + } + usec_stop = micros(); + return usec_stop - usec_start; +} + +// TODO: an inline version should handle the common case where state is const +uint32_t pulseIn(uint8_t pin, uint8_t state, uint32_t timeout) +{ + if (pin >= CORE_NUM_DIGITAL) return 0; + if (state) return pulseIn_high(portInputRegister(pin), timeout); + return pulseIn_low(portInputRegister(pin), timeout);; +} + +#elif defined(KINETISL) +// For TeencyLC need to use mask on the input register as the register is shared by several IO pins +uint32_t pulseIn_high(volatile uint8_t *reg, uint8_t mask, uint32_t timeout) +{ + uint32_t timeout_count = timeout * PULSEIN_LOOPS_PER_USEC; + uint32_t usec_start, usec_stop; + // wait for any previous pulse to end + while (*reg & mask) { + if (--timeout_count == 0) return -1; + } + // wait for the pulse to start + while (!(*reg & mask)) { + if (--timeout_count == 0) return 0; + } + usec_start = micros(); + // wait for the pulse to stop + while (*reg & mask) { + if (--timeout_count == 0) return 0; + } + usec_stop = micros(); + return usec_stop - usec_start; +} + +uint32_t pulseIn_low(volatile uint8_t *reg, uint8_t mask, uint32_t timeout) +{ + uint32_t timeout_count = timeout * PULSEIN_LOOPS_PER_USEC; + uint32_t usec_start, usec_stop; + + // wait for any previous pulse to end + while (!(*reg & mask)) { + if (--timeout_count == 0) return 0; + } + // wait for the pulse to start + while (*reg & mask) { + if (--timeout_count == 0) return 0; + } + usec_start = micros(); + // wait for the pulse to stop + while (!(*reg & mask)) { + if (--timeout_count == 0) return 0; + } + usec_stop = micros(); + return usec_stop - usec_start; +} + +// TODO: an inline version should handle the common case where state is const +uint32_t pulseIn(uint8_t pin, uint8_t state, uint32_t timeout) +{ + if (pin >= CORE_NUM_DIGITAL) return 0; + if (state) return pulseIn_high(portInputRegister(pin), digitalPinToBitMask(pin), timeout); + return pulseIn_low(portInputRegister(pin), digitalPinToBitMask(pin), timeout);; +} +#endif diff --git a/apps/common/tranzputer.c b/apps/common/tranzputer.c new file mode 100644 index 0000000..edf1c11 --- /dev/null +++ b/apps/common/tranzputer.c @@ -0,0 +1,1165 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: tranzputer.c +// 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. +// I had considered writing this module in C++ but decided upon C as speed is more +// important and C++ always adds a little additional overhead. Some of the methods need +// to be written as embedded assembler but this is for a later time when using the +// tranZPUter on faster motherboards. +// Credits: +// Copyright: (c) 2019-2020 Philip Smart +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined(__K64F__) + #include + #include + #include + #include + #include + #include + #include + #include "k64f_soc.h" + #include <../../libraries/include/stdmisc.h> +#elif defined(__ZPU__) + #include + #include + #include "zpu_soc.h" + #include + #include + #include +#else + #error "Target CPU not defined, use __ZPU__ or __K64F__" +#endif + +#include "ff.h" /* Declarations of FatFs API */ +#include "utils.h" +#include + + +// Global scope variables, used by pin assignment and configuration macros. +volatile uint32_t *ioPin[MAX_TRANZPUTER_PINS]; +uint8_t pinMap[MAX_TRANZPUTER_PINS]; +uint32_t volatile *ms; +t_z80Control z80Control; + + +// Dummy function to override a weak definition in the Teensy library. Currently the yield functionality is not +// needed within apps running on the K64F it is only applicable in the main OS. +// +void yield(void) +{ + return; +} + +// Method to setup the pins and the pin map to power up default. +// The OS millisecond counter address is passed into this library to gain access to time without the penalty of procedure calls. +// Time is used for timeouts and seriously affects pulse width of signals when procedure calls are made. +// +void setupPins(volatile uint32_t *millisecondTick) +{ + // Locals. + // + static uint8_t firstCall = 0; + + // This method can be called more than once as a convenient way to return all pins to default. On first call though + // the teensy library needs initialising, hence the static check. + // + if(firstCall == 0) + { + firstCall = 1; + _init_Teensyduino_internal_(); + + ms = millisecondTick; + } + + // Setup the map of a loop useable array with its non-linear Pin Number. + // + pinMap[Z80_A0] = Z80_A0_PIN; + pinMap[Z80_A1] = Z80_A1_PIN; + pinMap[Z80_A2] = Z80_A2_PIN; + pinMap[Z80_A3] = Z80_A3_PIN; + pinMap[Z80_A4] = Z80_A4_PIN; + pinMap[Z80_A5] = Z80_A5_PIN; + pinMap[Z80_A6] = Z80_A6_PIN; + pinMap[Z80_A7] = Z80_A7_PIN; + pinMap[Z80_A8] = Z80_A8_PIN; + pinMap[Z80_A9] = Z80_A9_PIN; + pinMap[Z80_A10] = Z80_A10_PIN; + pinMap[Z80_A11] = Z80_A11_PIN; + pinMap[Z80_A12] = Z80_A12_PIN; + pinMap[Z80_A13] = Z80_A13_PIN; + pinMap[Z80_A14] = Z80_A14_PIN; + pinMap[Z80_A15] = Z80_A15_PIN; + pinMap[Z80_A16] = Z80_A16_PIN; + pinMap[Z80_A17] = Z80_A17_PIN; + pinMap[Z80_A18] = Z80_A18_PIN; + + pinMap[Z80_D0] = Z80_D0_PIN; + pinMap[Z80_D1] = Z80_D1_PIN; + pinMap[Z80_D2] = Z80_D2_PIN; + pinMap[Z80_D3] = Z80_D3_PIN; + pinMap[Z80_D4] = Z80_D4_PIN; + pinMap[Z80_D5] = Z80_D5_PIN; + pinMap[Z80_D6] = Z80_D6_PIN; + pinMap[Z80_D7] = Z80_D7_PIN; + + pinMap[Z80_MEM0] = Z80_MEM0_PIN; + pinMap[Z80_MEM1] = Z80_MEM1_PIN; + pinMap[Z80_MEM2] = Z80_MEM2_PIN; + pinMap[Z80_MEM3] = Z80_MEM3_PIN; + pinMap[Z80_MEM4] = Z80_MEM4_PIN; + + pinMap[Z80_IORQ] = Z80_IORQ_PIN; + pinMap[Z80_MREQ] = Z80_MREQ_PIN; + pinMap[Z80_RD] = Z80_RD_PIN; + pinMap[Z80_WR] = Z80_WR_PIN; + pinMap[Z80_WAIT] = Z80_WAIT_PIN; + pinMap[Z80_BUSACK] = Z80_BUSACK_PIN; + + pinMap[Z80_NMI] = Z80_NMI_PIN; + pinMap[Z80_INT] = Z80_INT_PIN; + + pinMap[CTL_BUSACK] = CTL_BUSACK_PIN; + pinMap[CTL_BUSRQ] = CTL_BUSRQ_PIN; + pinMap[CTL_RFSH] = CTL_RFSH_PIN; + pinMap[CTL_HALT] = CTL_HALT_PIN; + pinMap[CTL_M1] = CTL_M1_PIN; + pinMap[CTL_CLK] = CTL_CLK_PIN; + pinMap[CTL_CLKSLCT] = CTL_CLKSLCT_PIN; + + // Now build the config array for all the ports. This aids in more rapid function switching as opposed to using + // the pinMode and digitalRead/Write functions provided in the Teensy lib. + // + for(uint8_t idx=0; idx < MAX_TRANZPUTER_PINS; idx++) + { + ioPin[pinMap[idx]] = portConfigRegister(pinMap[idx]); + + // Set to input, will change as functionality dictates. + pinInput(idx); + } + + // Initialise control structure. + z80Control.refreshAddr = 0x00; + z80Control.runCtrlLatch = readCtrlLatch(); + z80Control.ctrlMode = Z80_RUN; + z80Control.busDir = TRISTATE; + + return; +} + + +// Method to request the Z80 Bus. This halts the Z80 and sets all main signals to tri-state. +// Return: 0 - Z80 Bus obtained. +// 1 - Failed to obtain the Z80 bus. +uint8_t reqZ80Bus(uint32_t timeout) +{ + // Locals. + // + uint8_t result = 0; + uint32_t startTime = *ms; + + // Set the control pins in order to request the bus. + // + pinOutputSet(CTL_BUSRQ, HIGH); + pinInput(Z80_BUSACK); + + // Set BUSRQ low which sets the Z80 BUSRQ low. + pinLow(CTL_BUSRQ); + + // Wait for the Z80 to acknowledge the request. + while((*ms - startTime) < timeout && pinGet(Z80_BUSACK)); + + // If we timed out, reset the pins and return error. + // + if((*ms - startTime) >= timeout) + { + pinInput(CTL_BUSRQ); + result = 1; + } + + // Store the control latch state before we start modifying anything enabling a return to the pre bus request state. + z80Control.runCtrlLatch = readCtrlLatch(); + + return(result); +} + +// Simple method to release the Z80 Bus. +// +void relinquishZ80Bus(void) +{ + pinInput(CTL_BUSACK); + pinInput(CTL_BUSRQ); + return; +} + +// Method to request access to the main motherboard circuitry. This involves requesting the Z80 bus and then +// setting the Z80_RD and Z80_WR signals to low, this maps to a ENABLE_BUS signal which ensures that one half of +// the Mainboard BUSACK request signal is disabled (BUSACK to the motherboard has the opposite meaning, when active +// the mainboard is tri-stated and signals do not pass from th e Z80 and local memory to the mainboard). The other +// half of the mainboard request signal is disabled by setting CTL_BUSACK high. +// +uint8_t reqMainboardBus(uint32_t timeout) +{ + // Locals. + // + uint8_t result = 0; + + // Set the CTL BUSACK signal high so we dont assert the mainboard BUSACK signal. + pinOutputSet(CTL_BUSACK, HIGH); + + // Requst the Z80 Bus to tri-state the Z80. + if((result=reqZ80Bus(timeout)) == 0) + { + // Now request the mainboard by setting BUSACK high and Z80_RD/Z80_WR low. + pinOutput(Z80_RD); + pinOutput(Z80_WR); + + // A special mode in the FlashRAM decoder detects both RD and WR being low at the same time, this is not feasible under normal Z80 operating conditions, but what it signals + // here is to raise ENABLE_BUS which ensures that BUSACK on the mainboard is deasserted. This is for the case where the Z80 may be running in tranZPUter memory with the + // mainboard disabled. + pinLow(Z80_RD); + pinLow(Z80_WR); + + // On a Teensy3.5 K64F running at 120MHz this delay gives a pulsewidth of 760nS. + for(volatile uint32_t pulseWidth=0; pulseWidth < 1; pulseWidth++); + + // Immediately return the RD/WR to HIGH to complete the ENABLE_BUS latch action. + pinHigh(Z80_RD); + pinHigh(Z80_WR); + + // Store the mode. + z80Control.ctrlMode = MAINBOARD_ACCESS; + + // For mainboard access, MEM4:0 should be 0 so no activity is made to the tranZPUter circuitry except the control latch. + z80Control.curCtrlLatch = 0b00000000; + } else + { + printf("Failed to request Mainboard Bus\n"); + } + + return(result); +} + +// Method to request the local tranZPUter bus. This involves making a Z80 bus request and when relinquished, setting +// the CTL_BUSACK signal to low which disables (tri-states) the mainboard circuitry. +// +uint8_t reqTranZPUterBus(uint32_t timeout) +{ + // Locals. + // + uint8_t result = 0; + + // Set the CTL BUSACK signal high so we dont assert the mainboard BUSACK signal. + pinOutputSet(CTL_BUSACK, HIGH); + + // Requst the Z80 Bus to tri-state the Z80. + if((result=reqZ80Bus(timeout)) == 0) + { + // Now disable the mainboard by setting BUSACK low. + pinLow(CTL_BUSACK); + + // Store the mode. + z80Control.ctrlMode = TRANZPUTER_ACCESS; + + // For tranZPUter access, MEM4:0 should be 1 so no activity is made to the mainboard circuitry. + z80Control.curCtrlLatch = 0b00011111; + } + + return(result); +} + +// Method to set all the pins to be able to perform a transaction on the Z80 bus. +// +void setupSignalsForZ80Access(enum BUS_DIRECTION dir) +{ + // Address lines (apart from the upper bank address lines A18:16) need to be outputs. + for(uint8_t idx=Z80_A0; idx <= Z80_A15; idx++) + { + pinOutput(idx); + } + pinInput(Z80_A16); // The upper address bits can only be changed via the 273 latch IC which requires a Z80 IO Write transaction. + pinInput(Z80_A17); + pinInput(Z80_A18); + + // Control signals need to be output and deasserted. + // + pinOutputSet(Z80_IORQ, HIGH); + pinOutputSet(Z80_MREQ, HIGH); + pinOutputSet(Z80_RD, HIGH); + pinOutputSet(Z80_WR, HIGH); + + // Additional control lines need to be outputs and deasserted. These lines are for the main motherboard and not used on the tranZPUter. + // + pinOutputSet(CTL_HALT, HIGH); + pinOutputSet(CTL_RFSH, HIGH); + pinOutputSet(CTL_M1, HIGH); + + // Setup bus direction. + // + setZ80Direction(dir); + return; +} + +// Method to release the Z80, set all signals to input and disable BUSRQ. +// +void releaseZ80(void) +{ + // All address lines to inputs, upper A18:16 are always defined as inputs as they can only be read by the K64F, they are driven by a + // latch on the tranZPUter board. + // + for(uint8_t idx=Z80_A0; idx <= Z80_A15; idx++) + { + pinInput(idx); + } + // Same for data lines, revert to being inputs. + for(uint8_t idx=Z80_D0; idx <= Z80_D7; idx++) + { + pinInput(idx); + } + + // All control signals to inputs. + pinInput(CTL_HALT); + pinInput(CTL_RFSH); + pinInput(CTL_M1); + pinInput(Z80_IORQ); + pinInput(Z80_MREQ); + pinInput(Z80_RD); + pinInput(Z80_WR); + + // Finally release the Z80 by deasserting the CTL_BUSRQ signal. + relinquishZ80Bus(); + + // Store the mode. + z80Control.ctrlMode = Z80_RUN; + + // Indicate bus direction. + z80Control.busDir = TRISTATE; + return; +} + + +// Method to write a memory mapped byte onto the Z80 bus. +// As the underlying motherboard is 2MHz we keep to its timing best we can in C, for faster motherboards this method may need to +// be coded in assembler. +// +uint8_t writeZ80Memory(uint16_t addr, uint8_t data) +{ + // Locals. + uint32_t startTime = *ms; + + // Set the data and address on the bus. + // + setZ80Addr(addr); + setZ80Data(data); + pinLow(Z80_MREQ); + + // Different logic according to what is being accessed. The mainboard needs to uphold timing and WAIT signals whereas the Tranzputer logic has no wait + // signals and faster memory. + // + if(z80Control.ctrlMode == MAINBOARD_ACCESS) + { + // If WAIT has been asserted, loop. Set a timeout to prevent a total lockup. Wait shouldnt exceed 100mS, it it does there is a + // hardware fault and components such as DRAM will lose data due to no refresh. + while((*ms - startTime) < 100 && pinGet(Z80_WAIT) == 0); + + // Start the write cycle, MREQ and WR go low. + pinLow(Z80_WR); + + // On a Teensy3.5 K64F running at 120MHz this delay gives a pulsewidth of 760nS. + //for(volatile uint32_t pulseWidth=0; pulseWidth < 2; pulseWidth++); + + // Another wait loop check as the Z80 can assert wait at the time of Write or anytime before it is deasserted. + while((*ms - startTime) < 200 && pinGet(Z80_WAIT) == 0); + } else + { + // Start the write cycle, MREQ and WR go low. + pinLow(Z80_WR); + } + + // Complete the write cycle. + // + pinHigh(Z80_WR); + pinHigh(Z80_MREQ); + + return(0); +} + +// Method to read a memory mapped byte from the Z80 bus. +// Keep to the Z80 timing diagram, but for better accuracy of matching the timing diagram this needs to be coded in assembler. +// +uint8_t readZ80Memory(uint16_t addr) +{ + // Locals. + uint32_t startTime = *ms; + uint8_t data; + + // Set the address on the bus and assert MREQ and RD. + // + setZ80Addr(addr); + pinLow(Z80_MREQ); + pinLow(Z80_RD); + + // Different logic according to what is being accessed. The mainboard needs to uphold timing and WAIT signals whereas the Tranzputer logic has no wait + // signals and faster memory. + // + if(z80Control.ctrlMode == MAINBOARD_ACCESS) + { + // A wait loop check as the Z80 can assert wait during the Read operation to request more time. Set a timeout in case of hardware lockup. + while((*ms - startTime) < 100 && pinGet(Z80_WAIT) == 0); + + // On a Teensy3.5 K64F running at 120MHz this delay gives a pulsewidth of 760nS. This gives time for the addressed device to present the data + // on the data bus. + for(volatile uint32_t pulseWidth=0; pulseWidth < 1; pulseWidth++); + } + + // Fetch the data before deasserting the signals. + // + data = readDataBus(); + + // Complete the read cycle. + // + pinHigh(Z80_RD); + pinHigh(Z80_MREQ); + + // Finally pass back the byte read to the caller. + return(data); +} + + +// Method to write a byte onto the Z80 I/O bus. This method is almost identical to the memory mapped method but are kept seperate for different +// timings as needed, the more code will create greater delays in the pulse width and timing. +// +// As the underlying motherboard is 2MHz we keep to its timing best we can in C, for faster motherboards this method may need to +// be coded in assembler. +// +uint8_t writeZ80IO(uint16_t addr, uint8_t data) +{ + // Locals. + uint32_t startTime = *ms; + + // Set the data and address on the bus. + // + setZ80Addr(addr); + setZ80Data(data); + pinLow(Z80_IORQ); + + // Different logic according to what is being accessed. The mainboard needs to uphold timing and WAIT signals whereas the Tranzputer logic has no wait + // signals and faster memory. + // + if(z80Control.ctrlMode == MAINBOARD_ACCESS) + { + // If WAIT has been asserted, loop. Set a timeout to prevent a total lockup. Wait shouldnt exceed 100mS, it it does there is a + // hardware fault and components such as DRAM will lose data due to no refresh. + while((*ms - startTime) < 100 && pinGet(Z80_WAIT) == 0); + + // Start the write cycle, MREQ and WR go low. + pinLow(Z80_WR); + + // On a Teensy3.5 K64F running at 120MHz this delay gives a pulsewidth of 760nS. + //for(volatile uint32_t pulseWidth=0; pulseWidth < 2; pulseWidth++); + + // Another wait loop check as the Z80 can assert wait at the time of Write or anytime before it is deasserted. + while((*ms - startTime) < 200 && pinGet(Z80_WAIT) == 0); + } else + { + // Start the write cycle, MREQ and WR go low. + pinLow(Z80_WR); + } + + // Complete the write cycle. + // + pinHigh(Z80_WR); + pinHigh(Z80_IORQ); + + return(0); +} + +// Method to read a byte from the Z80 I/O bus. This method is almost identical to the memory mapped method but are kept seperate for different +// timings as needed, the more code will create greater delays in the pulse width and timing. +// +// As the underlying motherboard is 2MHz we keep to its timing best we can in C, for faster motherboards this method may need to +// be coded in assembler. +uint8_t readZ80IO(uint16_t addr) +{ + // Locals. + uint32_t startTime = *ms; + uint8_t data; + + // Set the address on the bus and assert MREQ and RD. + // + setZ80Addr(addr); + pinLow(Z80_IORQ); + pinLow(Z80_RD); + + // Different logic according to what is being accessed. The mainboard needs to uphold timing and WAIT signals whereas the Tranzputer logic has no wait + // signals and faster memory. + // + if(z80Control.ctrlMode == MAINBOARD_ACCESS) + { + // On a Teensy3.5 K64F running at 120MHz this delay gives a pulsewidth of 760nS. This gives time for the addressed device to present the data + // on the data bus. + //for(volatile uint32_t pulseWidth=0; pulseWidth < 2; pulseWidth++); + + // A wait loop check as the Z80 can assert wait during the Read operation to request more time. Set a timeout in case of hardware lockup. + while((*ms - startTime) < 100 && pinGet(Z80_WAIT) == 0); + } + + // Fetch the data before deasserting the signals. + // + data = readDataBus(); + + // Complete the read cycle. + // + pinHigh(Z80_RD); + pinHigh(Z80_IORQ); + + // Finally pass back the byte read to the caller. + return(data); +} + +// Method to perform a refresh cycle on the z80 mainboard bus to ensure dynamic RAM contents are maintained during extended bus control periods. +// +// Under normal z80 processing a refresh cycle is issued every instruction during the T3/T4 cycles. As we arent reading instructions but just reading/writing +// then the refresh cycles are made after a cnofigurable set number of bus transactions (ie. 128 read/writes). This has to occur when either of the busses +// are under K64F control so a call to this method should be made frequently. +// +void refreshZ80(void) +{ + // Locals. + volatile uint8_t idx = 0; + + // Set 7 bits on the address bus. + setZ80RefreshAddr(z80Control.refreshAddr); + + // If we are controlling the tranZPUter bus then a switch to the mainboard needs to be made first. + // + if(z80Control.ctrlMode == TRANZPUTER_ACCESS) + { + // Quick way to gain mainboard access, force an enable of the Z80_BUSACK on the mainboard by setting RD/WR low, this fires an enable pulse at the 279 RS Flip Flop + // and setting CTL_BUSACK high enables the second component forming the Z80_BUSACK signal. + pinLow(Z80_RD); + pinLow(Z80_WR); + pinHigh(Z80_RD); + pinHigh(Z80_WR); + pinHigh(CTL_BUSACK); + } + + // Assert Refresh. + pinLow(CTL_RFSH); + pinLow(Z80_MREQ); + idx++; // Increase the pulse width of the MREQ signal. + pinHigh(Z80_MREQ); + pinHigh(CTL_RFSH); + + // Restore access to tranZPUter bus if this was the original mode. + if(z80Control.ctrlMode == TRANZPUTER_ACCESS) + { + // Restore tranZPUter bus control. + pinLow(CTL_BUSACK); + } + + // Increment refresh address to complete. + z80Control.refreshAddr++; + z80Control.refreshAddr &= 0x7f; + return; +} + +// Method to perform a full row refresh on the dynamic DRAM. This method writes out a full 7bit address so that all rows are refreshed. +// +void refreshZ80AllRows(void) +{ + // Locals. + volatile uint8_t idx; + + // If we are controlling the tranZPUter bus then a switch to the mainboard needs to be made first. + // + if(z80Control.ctrlMode == TRANZPUTER_ACCESS) + { + // Quick way to gain mainboard access, force an enable of the Z80_BUSACK on the mainboard by setting RD/WR low, this fires an enable pulse at the 279 RS Flip Flop + // and setting CTL_BUSACK high enables the second component forming the Z80_BUSACK signal. + pinLow(Z80_RD); + pinLow(Z80_WR); + pinHigh(Z80_RD); + pinHigh(Z80_WR); + pinHigh(CTL_BUSACK); + } + + // Loop through all 7 bits of refresh rows. + idx = 0; + while(idx < 0x80) + { + // Set 7 bits on the address bus. + setZ80RefreshAddr(idx); + + // Assert Refresh. + pinLow(CTL_RFSH); + pinLow(Z80_MREQ); + idx++; + pinHigh(Z80_MREQ); + pinHigh(CTL_RFSH); + } + + // Restore access to tranZPUter bus if this was the original mode. + if(z80Control.ctrlMode == TRANZPUTER_ACCESS) + { + // Restore tranZPUter bus control. + pinLow(CTL_BUSACK); + } + return; +} + +// Method to fill memory under the Z80 control, either the mainboard or tranZPUter memory. +// +void fillZ80Memory(uint32_t addr, uint32_t size, uint8_t data, uint8_t mainBoard) +{ + // Locals. + + if( (mainBoard == 0 && reqTranZPUterBus(100) == 0) || (mainBoard != 0 && reqMainboardBus(100) == 0) ) + { + // Setup the pins to perform a read operation (after setting the latch to starting value). + // + setupSignalsForZ80Access(WRITE); + writeCtrlLatch(z80Control.curCtrlLatch); + + // Fill the memory but every FILL_RFSH_BYTE_CNT perform a DRAM refresh. + // + for(uint32_t idx=addr; idx < (addr+size); idx++) + { + // If the address changes the upper address bits, update the latch to reflect the change. + if((uint8_t)(idx >> 16) != readUpperAddr()) + { + // Write the upper address bits to the 273 control latch. The lower 5 bits remain as set by the bus select methods. + writeCtrlLatch( (uint8_t)((idx >> 11) | (z80Control.curCtrlLatch & 0b00011111)) ); + } + + if(idx % FILL_RFSH_BYTE_CNT == 0) + { + // Perform a full row refresh to maintain the DRAM. + refreshZ80AllRows(); + } + writeZ80Memory((uint16_t)idx, data); + } + + // Restore the control latch to its original configuration. + // + setZ80Direction(WRITE); + writeCtrlLatch(z80Control.runCtrlLatch); + releaseZ80(); + } + return; +} + +// A method to read the full video frame buffer from the Sharp MZ80A and store it in local memory (control structure). +// No refresh cycles are needed as we grab the frame but between frames a full refresh is performed. +// +void captureVideoFrame(enum VIDEO_FRAMES frame, uint8_t noAttributeFrame) +{ + // Locals. + + if(reqMainboardBus(100) == 0) + { + // Setup the pins to perform a read operation (after setting the latch to starting value). + // + setupSignalsForZ80Access(WRITE); + writeCtrlLatch(z80Control.curCtrlLatch); + setZ80Direction(READ); + + // No need for refresh as we take less than 2ms time, just grab the video frame. + for(uint16_t idx=0; idx < MZ_VID_RAM_SIZE; idx++) + { + z80Control.videoRAM[frame][idx] = readZ80Memory((uint16_t)idx+MZ_VID_RAM_ADDR); + } + + // Perform a full row refresh to maintain the DRAM. + refreshZ80AllRows(); + + // If flag not set capture the attribute RAM. This is normally not present on a standard Sharp MZ80A only present on models + // with the MZ80A Colour Board upgrade. + // + if(noAttributeFrame == 0) + { + // Same for the attribute frame, no need for refresh as we take 2ms or less, just grab it. + for(uint16_t idx=0; idx < MZ_ATTR_RAM_SIZE; idx++) + { + z80Control.attributeRAM[frame][idx] = readZ80Memory((uint16_t)idx+MZ_ATTR_RAM_ADDR); + } + + // Perform a full row refresh to maintain the DRAM. + refreshZ80AllRows(); + } + + // Restore the control latch to its original configuration. + // + setZ80Direction(WRITE); + writeCtrlLatch(z80Control.runCtrlLatch); + releaseZ80(); + } + return; +} + +// Method to refresh the video frame buffer on the Sharp MZ80A with the data held in local memory. +// +void refreshVideoFrame(enum VIDEO_FRAMES frame, uint8_t scrolHome, uint8_t noAttributeFrame) +{ + // Locals. + + if(reqMainboardBus(100) == 0) + { + // Setup the pins to perform a write operation. + // + setupSignalsForZ80Access(WRITE); + writeCtrlLatch(z80Control.curCtrlLatch); + + // No need for refresh as we take less than 2ms time, just write the video frame. + for(uint16_t idx=0; idx < MZ_VID_RAM_SIZE; idx++) + { + writeZ80Memory((uint16_t)idx+MZ_VID_RAM_ADDR, z80Control.videoRAM[frame][idx]); + } + + // Perform a full row refresh to maintain the DRAM. + refreshZ80AllRows(); + + // If flag not set write out to the attribute RAM. This is normally not present on a standard Sharp MZ80A only present on models + // with the MZ80A Colour Board upgrade. + // + if(noAttributeFrame == 0) + { + // No need for refresh as we take less than 2ms time, just write the video frame. + for(uint16_t idx=0; idx < MZ_ATTR_RAM_SIZE; idx++) + { + writeZ80Memory((uint16_t)idx+MZ_ATTR_RAM_ADDR, z80Control.attributeRAM[frame][idx]); + } + + // Perform a full row refresh to maintain the DRAM. + refreshZ80AllRows(); + } + + // If the Scroll Home flag is set, this means execute a read against the hardware scoll register to restore the position 0,0 to location MZ_VID_RAM_ADDR. + if(scrolHome) + { + setZ80Direction(READ); + readZ80Memory((uint16_t)MZ_SCROL_BASE); + } + + // Restore the control latch to its original configuration. + // + setZ80Direction(WRITE); + writeCtrlLatch(z80Control.runCtrlLatch); + releaseZ80(); + } + return; +} + +// Method to load up the local video frame buffer from a file. +// +FRESULT loadVideoFrameBuffer(char *src, enum VIDEO_FRAMES frame) +{ + // Locals. + // + FIL File; + unsigned int readSize; + FRESULT fr0; + + // Sanity check on filenames. + if(src == NULL) + return(FR_INVALID_PARAMETER); + + // Try and open the source file. + fr0 = f_open(&File, src, FA_OPEN_EXISTING | FA_READ); + + // If no errors in opening the file, proceed with reading and loading into memory. + if(!fr0) + { + memset(z80Control.videoRAM[frame], MZ_VID_DFLT_BYTE, MZ_VID_RAM_SIZE); + fr0 = f_read(&File, z80Control.videoRAM[frame], MZ_VID_RAM_SIZE, &readSize); + if (!fr0) + { + memset(z80Control.attributeRAM[frame], MZ_ATTR_DFLT_BYTE, MZ_ATTR_RAM_SIZE); + fr0 = f_read(&File, z80Control.attributeRAM[frame], MZ_ATTR_RAM_SIZE, &readSize); + } + + // Close to sync files. + f_close(&File); + } else + { + printf("File not found:%s\n", src); + } + + return(fr0 ? fr0 : FR_OK); +} + +// Method to save the local video frame buffer into a file. +// +FRESULT saveVideoFrameBuffer(char *dst, enum VIDEO_FRAMES frame) +{ + // Locals. + // + FIL File; + unsigned int writeSize; + FRESULT fr0; + + // Sanity check on filenames. + if(dst == NULL) + return(FR_INVALID_PARAMETER); + + // Try and create the destination file. + fr0 = f_open(&File, dst, FA_CREATE_ALWAYS | FA_WRITE); + + // If no errors in opening the file, proceed with reading and loading into memory. + if(!fr0) + { + // Write the entire framebuffer to the SD file, video then attribute. + // + fr0 = f_write(&File, z80Control.videoRAM[frame], MZ_VID_RAM_SIZE, &writeSize); + if (!fr0 && writeSize == MZ_VID_RAM_SIZE) + { + fr0 = f_write(&File, z80Control.attributeRAM[frame], MZ_ATTR_RAM_SIZE, &writeSize); + } + + // Close to sync files. + f_close(&File); + } else + { + printf("Cannot create file:%s\n", dst); + } + + return(fr0 ? fr0 : FR_OK); +} + + +// Method to load a file from the SD card directly into the tranZPUter static RAM or mainboard RAM. +// +FRESULT loadZ80Memory(char *src, uint32_t addr, uint8_t mainBoard, uint8_t releaseBus) +{ + // Locals. + // + FIL File; + uint32_t loadSize = 0L; + uint32_t memPtr = addr; + unsigned int readSize; + unsigned char buf[SECTOR_SIZE]; + FRESULT fr0; + + // Sanity check on filenames. + if(src == NULL) + return(FR_INVALID_PARAMETER); + + // Try and open the source file. + fr0 = f_open(&File, src, FA_OPEN_EXISTING | FA_READ); + + // If no errors in opening the file, proceed with reading and loading into memory. + if(!fr0) + { + // If the Z80 is in RUN mode, request the bus. + // This mechanism allows for the load command to leave the BUS under the tranZPUter control for upload of multiple files. + // Care needs to be taken though that no more than 2-3ms passes without a call to refreshZ80AllRows() otherwise memory loss + // may occur. + // + if(z80Control.ctrlMode == Z80_RUN) + { + // Request the board according to the mainboard flag, mainboard = 1 then the mainboard is controlled otherwise the tranZPUter board. + if(mainBoard == 0) + { + reqTranZPUterBus(100); + } else + { + reqMainboardBus(100); + } + + // If successful, setup the control pins for upload mode. + // + if(z80Control.ctrlMode != Z80_RUN) + { + // Setup the pins to perform a write operation. + // + setupSignalsForZ80Access(WRITE); + + // Setup the control latch to the required starting configuration. + writeCtrlLatch(z80Control.curCtrlLatch); + } + } else + { + // See if the bus needs changing. + // + enum CTRL_MODE newMode = (mainBoard == 0) ? TRANZPUTER_ACCESS : MAINBOARD_ACCESS; + reqZ80BusChange(newMode); + } + + // If we have taken control of the bus, commence upload. + // + if(z80Control.ctrlMode != Z80_RUN) + { + // Loop, reading a sector at a time from SD file and writing it directly into the Z80 tranZPUter RAM or mainboard RAM. + // + loadSize = 0; + memPtr = addr; + for (;;) { + // Wrap a disk read with two full refresh periods to counter for the amount of time taken to read disk. + refreshZ80AllRows(); + fr0 = f_read(&File, buf, SECTOR_SIZE, &readSize); + refreshZ80AllRows(); + if (fr0 || readSize == 0) break; /* error or eof */ + + // Go through each byte in sector and send to Z80 bus. + for(unsigned int idx=0; idx < readSize; idx++) + { + // If the address changes the upper address bits, update the latch to reflect the change. + if((uint8_t)(memPtr >> 16) != readUpperAddr()) + { + // Write the upper address bits to the 273 control latch. The lower 5 bits remain as set by the bus select methods. + writeCtrlLatch( (uint8_t)((memPtr >> 11) | (z80Control.curCtrlLatch & 0b00011111)) ); + } + + // At the halfway mark perform a full refresh. + if(idx == (SECTOR_SIZE/2)) + { + refreshZ80AllRows(); + } + + // And now write the byte and to the next address! + writeZ80Memory((uint16_t)memPtr, buf[idx]); + memPtr++; + } + loadSize += readSize; + } + } else + { + printf("Failed to request Z80 access.\n"); + fr0 = FR_INT_ERR; + } + + // Close to sync files. + f_close(&File); + } else + { + printf("File not found:%s\n", src); + } + + // If requested or an error occurs, then release the Z80 bus as no more uploads will be taking place in this batch. + // + if(releaseBus == 1 || fr0) + { + // Restore the control latch to its original configuration. + // + writeCtrlLatch(z80Control.runCtrlLatch); + releaseZ80(); + } + + return(fr0 ? fr0 : FR_OK); +} + +// Method to read a section of the tranZPUter/mainboard memory and store it in an SD file. +// +FRESULT saveZ80Memory(char *dst, uint32_t addr, uint32_t size, uint8_t mainBoard) +{ + // Locals. + // + FIL File; + uint32_t saveSize = 0L; + uint32_t sizeToWrite; + uint32_t memPtr = addr; + uint32_t endAddr = addr + size; + unsigned int writeSize; + unsigned char buf[SECTOR_SIZE]; + FRESULT fr0; + + // Sanity check on filenames. + if(dst == NULL || size == 0) + return(FR_INVALID_PARAMETER); + + // Try and create the destination file. + fr0 = f_open(&File, dst, FA_CREATE_ALWAYS | FA_WRITE); + + // If no errors in opening the file, proceed with reading and loading into memory. + if(!fr0) + { + if( (mainBoard == 0 && reqTranZPUterBus(100) == 0) || (mainBoard != 0 && reqMainboardBus(100) == 0) ) + { + // Setup the pins to perform a read operation (after setting the latch to starting value). + // + setupSignalsForZ80Access(WRITE); + writeCtrlLatch(z80Control.curCtrlLatch); + setZ80Direction(READ); + + // Loop, reading a sector worth of data (or upto limit remaining) from the Z80 tranZPUter RAM or mainboard RAM and writing it into the open SD card file. + // + saveSize = 0; + for (;;) { + + // Work out how many bytes to write in the sector then fetch from the Z80. + sizeToWrite = (endAddr-saveSize) > SECTOR_SIZE ? SECTOR_SIZE : endAddr - saveSize; + for(unsigned int idx=0; idx < sizeToWrite; idx++) + { + // If the address changes the upper address bits, update the latch to reflect the change. + if((uint8_t)(memPtr >> 16) != readUpperAddr()) + { + // Write the upper address bits to the 273 control latch. The lower 5 bits remain as set by the bus select methods. + setZ80Direction(WRITE); + writeCtrlLatch( (uint8_t)((memPtr >> 11) | (z80Control.curCtrlLatch & 0b00011111)) ); + setZ80Direction(READ); + } + + // At the halfway mark perform a full refresh. + if(idx == (SECTOR_SIZE/2)) + { + refreshZ80AllRows(); + } + + // And now read the byte and to the next address! + buf[idx] = readZ80Memory((uint16_t)memPtr); + memPtr++; + } + + // Wrap disk write with two full refresh periods to counter for the amount of time taken to write to disk. + refreshZ80AllRows(); + fr0 = f_write(&File, buf, sizeToWrite, &writeSize); + refreshZ80AllRows(); + saveSize += writeSize; + if (fr0 || writeSize < sizeToWrite || saveSize >= size) break; // error, disk full or range written. + } + + // Restore the control latch to its original configuration. + // + setZ80Direction(WRITE); + writeCtrlLatch(z80Control.runCtrlLatch); + releaseZ80(); + printf("Saved %ld bytes, final address:%lx\n", saveSize, memPtr); + + } else + { + printf("Failed to request Z80 access.\n"); + } + + // Close to sync files. + f_close(&File); + + } else + { + printf("Cannot create file:%s\n", dst); + } + + return(fr0 ? fr0 : FR_OK); +} + +#if defined DEBUG +// Simple method to output the Z80 signals to the console - feel good factor. To be of real use the signals need to be captured against the system +// clock and the replayed, perhaps a todo! +// +void displaySignals(void) +{ + uint32_t ADDR = 0; + uint8_t DATA = 0; + uint8_t RD = 0; + uint8_t WR = 0; + uint8_t IORQ = 0; + uint8_t MREQ = 0; + uint8_t NMI = 0; + uint8_t INT = 0; + uint8_t M1 = 0; + uint8_t RFSH = 0; + uint8_t WAIT = 0; + uint8_t BUSRQ = 0; + uint8_t BUSACK = 0; + uint8_t ZBUSACK = 0; + uint8_t HALT = 0; + uint8_t CLKSLCT = 0; + + setupPins(NULL); + + printf("Z80 Bus Signals:\r\n"); + while(1) + { + ADDR = (pinGet(Z80_A18) & 0x1) << 18; + ADDR |= (pinGet(Z80_A17) & 0x1) << 17; + ADDR |= (pinGet(Z80_A16) & 0x1) << 16; + ADDR |= (pinGet(Z80_A15) & 0x1) << 15; + ADDR |= (pinGet(Z80_A14) & 0x1) << 14; + ADDR |= (pinGet(Z80_A13) & 0x1) << 13; + ADDR |= (pinGet(Z80_A12) & 0x1) << 12; + ADDR |= (pinGet(Z80_A11) & 0x1) << 11; + ADDR |= (pinGet(Z80_A10) & 0x1) << 10; + ADDR |= (pinGet(Z80_A9) & 0x1) << 9; + ADDR |= (pinGet(Z80_A8) & 0x1) << 8; + ADDR |= (pinGet(Z80_A7) & 0x1) << 7; + ADDR |= (pinGet(Z80_A6) & 0x1) << 6; + ADDR |= (pinGet(Z80_A5) & 0x1) << 5; + ADDR |= (pinGet(Z80_A4) & 0x1) << 4; + ADDR |= (pinGet(Z80_A3) & 0x1) << 3; + ADDR |= (pinGet(Z80_A2) & 0x1) << 2; + ADDR |= (pinGet(Z80_A1) & 0x1) << 1; + ADDR |= (pinGet(Z80_A0) & 0x1); + DATA = (pinGet(Z80_D7) & 0x1) << 7; + DATA |= (pinGet(Z80_D6) & 0x1) << 6; + DATA |= (pinGet(Z80_D5) & 0x1) << 5; + DATA |= (pinGet(Z80_D4) & 0x1) << 4; + DATA |= (pinGet(Z80_D3) & 0x1) << 3; + DATA |= (pinGet(Z80_D2) & 0x1) << 2; + DATA |= (pinGet(Z80_A1) & 0x1) << 1; + DATA |= (pinGet(Z80_D0) & 0x1); + RD=pinGet(Z80_RD); + WR=pinGet(Z80_WR); + MREQ=pinGet(Z80_MREQ); + IORQ=pinGet(Z80_IORQ); + NMI=pinGet(Z80_NMI); + INT=pinGet(Z80_INT); + M1=pinGet(CTL_M1); + RFSH=pinGet(CTL_RFSH); + WAIT=pinGet(Z80_WAIT); + BUSRQ=pinGet(CTL_BUSRQ); + BUSACK=pinGet(CTL_BUSACK); + ZBUSACK=pinGet(Z80_BUSACK); + HALT=pinGet(CTL_HALT); + CLKSLCT=pinGet(CTL_CLKSLCT); + + printf("\rADDR=%06lx %08x %3s %3s %3s %3s %3s %3s %2s %4s %4s %2s %2s %3s %4s %4s", ADDR, DATA, + (RD == 0 && MREQ == 0 && WR == 1 && IORQ == 1) ? "MRD" : " ", + (RD == 0 && IORQ == 0 && WR == 1 && MREQ == 1) ? "IRD" : " ", + (WR == 0 && MREQ == 0 && RD == 1 && IORQ == 1) ? "MWR" : " ", + (WR == 0 && IORQ == 0 && RD == 1 && MREQ == 1) ? "IWR" : " ", + (NMI == 0) ? "NMI" : " ", + (INT == 0) ? "INT" : " ", + (M1 == 0) ? "M1" : " ", + (RFSH == 0) ? "RFSH" : " ", + (WAIT == 0) ? "WAIT" : " ", + (BUSRQ == 0) ? "BR" : " ", + (BUSACK == 0) ? "BA" : " ", + (ZBUSACK == 0) ? "ZBA" : " ", + (HALT == 0) ? "HALT" : " ", + (CLKSLCT == 0) ? "CLKS" : " " + ); + } + + return; +} +#endif + + +#ifdef __cplusplus +} +#endif diff --git a/apps/fcat/fcat.c b/apps/fcat/fcat.c index 5b57871..7748b5f 100755 --- a/apps/fcat/fcat.c +++ b/apps/fcat/fcat.c @@ -89,7 +89,11 @@ uint32_t app(uint32_t param1, uint32_t param2) char *fileName; uint32_t retCode = 0xffffffff; FRESULT fr = 1; -printf("0=%08lx, 1=%08lx, 2=%08lx, _IOB=%08lx\n", __iob[0], __iob[1], __iob[2], (uint32_t)__iob); + +#if defined __ZPU__ + printf("0=%08lx, 1=%08lx, 2=%08lx, _IOB=%08lx\n", __iob[0], __iob[1], __iob[2], (uint32_t)__iob); +#endif + fileName = getStrParam(&ptr); if(*fileName == 0x00) { diff --git a/apps/include/tranzputer.h b/apps/include/tranzputer.h new file mode 100755 index 0000000..c5af993 --- /dev/null +++ b/apps/include/tranzputer.h @@ -0,0 +1,293 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#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 diff --git a/apps/tzload/Makefile b/apps/tzload/Makefile new file mode 100755 index 0000000..08861a8 --- /dev/null +++ b/apps/tzload/Makefile @@ -0,0 +1,83 @@ +######################################################################################################### +## +## 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 +## +## 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 . +######################################################################################################### + +APP_NAME = tzload +APP_DIR = $(CURDIR)/.. +APP_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 $(APP_COMMON_DIR)/tranzputer.c #$(TEENSYDIR)/yield.cpp + 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 diff --git a/apps/tzload/tzload.c b/apps/tzload/tzload.c new file mode 100644 index 0000000..8198965 --- /dev/null +++ b/apps/tzload/tzload.c @@ -0,0 +1,403 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: tzload.c +// Created: May 2020 +// Author(s): Philip Smart +// Description: A TranZPUter helper program, responsible for loading images off the SD card into +// memory on the tranZPUter board or host mainboard./ +// Credits: +// Copyright: (c) 2019-2020 Philip Smart +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined(__K64F__) + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include "k64f_soc.h" + #include <../../libraries/include/stdmisc.h> +#elif defined(__ZPU__) + #include + #include + #include "zpu_soc.h" + #include + #include + #include +#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 +#include +#include "tzload.h" + +// Utility functions. +#include + +// Version info. +#define VERSION "v1.0" +#define VERSION_DATE "15/05/2020" +#define APP_NAME "TZLOAD" + +// 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(" -d | --download File into which memory contents from the tranZPUter are stored.\n"); + printf(" -u | --upload File whose contents are uploaded into the traZPUter memory.\n"); + printf(" -U | --uploadset :,...,:\n"); + printf(" Upload a set of files at the specified locations. --mainboard specifies mainboard is target, default is tranZPUter.\n"); + printf(" -f | --fill Fill the memory specified by --addr, --size and [--mainboard] with the value .\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 then write out 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(" -v | --verbose Output more messages.\n"); + + printf("\nExamples:\n"); + printf(" tzload --outfile monitor.rom -a 0x000000 # Load the file monitor.rom into the tranZPUter memory at address 0x000000.\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 memAddr = 0xFFFFFFFF; + uint32_t memSize = 0xFFFFFFFF; + uint16_t fillByte = 0xFFFF; + int argc = 0; + int help_flag = 0; + int mainboard_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 uploadCnt = 0; + long val; + char *argv[20]; + char *uploadArr[20]; + char *ptr = strtok((char *)param1, " "); + char uploadFile[32]; + char downloadFile[32]; + + // Initialisation. + uploadFile[0] = '\0'; + downloadFile[0] = '\0'; + + // 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'}, + {"download", required_argument, 0, 'd'}, + {"upload", required_argument, 0, 'u'}, + {"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'}, + {"swap", no_argument, 0, 's'}, + {"verbose", no_argument, 0, 'v'}, + {"video", no_argument, 0, 'V'}, + {0, 0, 0, 0} + }; + + // Parse the command line options. + // + while((opt = getopt_long(argc, argv, ":ha:l:mvU:Vsd:u:", long_options, &option_index)) != -1) + { + switch(opt) + { + case 'h': + help_flag = 1; + break; + + case 'm': + mainboard_flag = 1; + break; + + case 's': + 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) + { + printf("Illegal numeric:%s\n", argv[optind-1]); + return(6); + } + memAddr = (uint32_t)val; + break; + + case 'l': + if(xatoi(&argv[optind-1], &val) == 0) + { + printf("Illegal numeric:%s\n", argv[optind-1]); + return(7); + } + memSize = (uint32_t)val; + break; + + case 'd': + // Data is read from the tranZPUter and stored in the given file. + strcpy(downloadFile, argv[optind-1]); + downloadFNLen = strlen(downloadFile); + break; + + case 'u': + // Data is uploaded from the given file into the tranZPUter. + strcpy(uploadFile, argv[optind-1]); + uploadFNLen = strlen(uploadFile); + break; + + case 'U': + // Extract an array of :,... 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 :,...\n"); + return(6); + } + break; + + case 'v': + verbose_flag = 1; + break; + + case 'V': + video_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(uploadCnt && (help_flag == 1 || uploadFNLen > 0 || downloadFNLen > 0 || swap_flag == 1 || video_flag == 1 || fillByte != 0xFFFF || memAddr != 0xFFFFFFFF || memSize != 0xFFFFFFFF)) + { + printf("Illegal combination of flags, --upload 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)) + { + printf("Illegal combination of flags, --video can only be used with --infile, --outfile 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(help_flag == 1) + { + usage(); + return(0); + } + if( (uploadFNLen == 0 && downloadFNLen == 0) || + (swap_flag == 0 && uploadFNLen > 0 && downloadFNLen > 0) ) + { + if(swap_flag == 0) + { + printf("Input file or Output file (only one) needs to be specified.\n"); + } + else + { + printf("Both an Input file and an Output file need to be specified for swap mode.\n"); + } + return(15); + } + if(swap_flag == 0 && downloadFNLen > 0 && memSize == 0xFFFFFFFF) + { + printf("Please define the size of memory you wish to read.\n"); + return(16); + } + if(memAddr == 0xFFFFFFFF) + { + printf("Please define the target address.\n"); + return(17); + } + } + if(uploadCnt == 0 && video_flag == 0) + { + if(mainboard_flag == 1 && (memAddr > 0x10000 || memAddr + memSize > 0x10000)) + { + printf("Mainboard only has 64K, please change the address and size.\n"); + return(17); + } + if(mainboard_flag == 0 && (memAddr >= 0x80000 || memAddr + memSize > 0x80000)) + { + printf("tranZPUter board only has 512K, please change the address and size.\n"); + return(18); + } + } + + // Initialise the IO. + setupPins(G->millis); + + // Bulk file upload command (used to preload a file set). + // + if(uploadCnt > 0) + { + // Process all the files in the upload list. + // + for(uint8_t idx=0; idx < uploadCnt; idx++) + { + strcpy(uploadFile, strtok((char *)uploadArr[idx], ":")); + ptr = strtok(0, ","); + if(xatoi(&ptr, &val) == 0) + { + printf("Illegal numeric in upload list:%s\n", ptr); + return(20); + } + 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); + } + } + + // Standard read/write operation or swapping out memory? + // + else if(video_flag) + { + if(downloadFNLen > 0) + { + captureVideoFrame(SAVED, 0); + saveVideoFrameBuffer(downloadFile, SAVED); + } else + { + loadVideoFrameBuffer(uploadFile, SAVED); + refreshVideoFrame(SAVED, 1, 0); + } + } + + // 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) + { + if(verbose_flag) + { + printf("Saving %s memory at address:%06lx into file:%s\n", (mainboard_flag == 0 ? "tranZPUter" : "mainboard"), memAddr, downloadFile); + } + saveZ80Memory(downloadFile, memAddr, memSize, 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); + } + loadZ80Memory(uploadFile, memAddr, mainboard_flag, 1); + } + } + + return(0); +} + +#ifdef __cplusplus +} +#endif diff --git a/apps/tzload/tzload.h b/apps/tzload/tzload.h new file mode 100755 index 0000000..7d39ed8 --- /dev/null +++ b/apps/tzload/tzload.h @@ -0,0 +1,46 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: tzload.h +// Created: May 2020 +// Author(s): Philip Smart +// Description: A TranZPUter helper program, responsible for loading images off the SD card into +// memory on the tranZPUter board or host mainboard. +// Credits: +// Copyright: (c) 2019-2020 Philip Smart +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef TZLOAD_H +#define TZLOAD_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 // TZLOAD_H diff --git a/apps/tzpu/Makefile b/apps/tzpu/Makefile new file mode 100755 index 0000000..b3dfde3 --- /dev/null +++ b/apps/tzpu/Makefile @@ -0,0 +1,83 @@ +######################################################################################################### +## +## 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 +## +## 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 . +######################################################################################################### + +APP_NAME = tzpu +APP_DIR = $(CURDIR)/.. +APP_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 $(APP_COMMON_DIR)/tranzputer.c #$(TEENSYDIR)/yield.cpp + 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 diff --git a/apps/tzpu/tzpu.c b/apps/tzpu/tzpu.c new file mode 100644 index 0000000..f0090d7 --- /dev/null +++ b/apps/tzpu/tzpu.c @@ -0,0 +1,152 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: tzpu.c +// Created: May 2020 +// Author(s): Philip Smart +// Description: The TranZPUter control program, responsible for booting up and configuring the +// underlying host, providing SD card services and interactive menus. +// Credits: +// Copyright: (c) 2019-2020 Philip Smart +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define TZPU_VERSION "1.0" + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined(__K64F__) + #include + #include + #include + #include + #include + #include + #include +#include + #include + #include "k64f_soc.h" + #include <../../libraries/include/stdmisc.h> +#elif defined(__ZPU__) + #include + #include + #include "zpu_soc.h" + #include + #include + #include +#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 +#include +#include "tzpu.h" + +// Utility functions. +#include + +// Version info. +#define VERSION "v1.0" +#define VERSION_DATE "15/05/2020" +#define APP_NAME "TZPU" + +void testBus(void) +{ + printf("Requesting Z80 BUS and Mainboard access\n"); + if(reqMainboardBus(100) == 0) + { + setupSignalsForZ80Access(WRITE); + uint8_t data = 0x00; + // while(digitalRead(Z80_WAIT) == 0); + for(uint16_t idx=0xD000; idx < 0xD800; idx++) + { + writeZ80Memory(idx, data); + // delay(1000000); + data++; + } + releaseZ80(); + } + else + { + printf("Failed to obtain the Z80 bus.\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. + // + // char *ptr = (char *)param1; + // char *pathName; + uint32_t retCode = 1; + + // Get name of file to load. + // + //pathName = getStrParam(&ptr); + //if(strlen(pathName) == 0) + //{ + // printf("Usage: tzpu \n"); + //} + _init_Teensyduino_internal_(); + setupPins(G->millis); + + printf("Loading Monitor ROM\n"); + loadZ80Memory("SA1510.rom", 0x00000000, 0, 1); + + printf("Loading Floppy ROM\n"); + loadZ80Memory("1Z-013A.rom", 0x0000F000, 0, 1); + + printf("Testing Display\n"); + testBus(); + displaySignals(); + + pinMode(13, OUTPUT); + while (1) { + digitalWriteFast(13, HIGH); + delay(500); + digitalWriteFast(13, LOW); + delay(500); + } + + return(retCode); +} + +#ifdef __cplusplus +} +#endif diff --git a/apps/tzpu/tzpu.h b/apps/tzpu/tzpu.h new file mode 100755 index 0000000..eba5752 --- /dev/null +++ b/apps/tzpu/tzpu.h @@ -0,0 +1,46 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: tzpu.h +// Created: May 2020 +// Author(s): Philip Smart +// Description: The TranZPUter control program, responsible for booting up and configuring the +// underlying host, providing SD card services and interactive menus. +// Credits: +// Copyright: (c) 2019-2020 Philip Smart +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef TZPU_H +#define TZPU_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 // TZPU_H diff --git a/common/FatFS/ff.h b/common/FatFS/ff.h index 4369340..0628236 100644 --- a/common/FatFS/ff.h +++ b/common/FatFS/ff.h @@ -399,6 +399,7 @@ int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ #define AM_DIR 0x10 /* Directory */ #define AM_ARC 0x20 /* Archive */ +#define SECTOR_SIZE 512 #ifdef __cplusplus } diff --git a/common/tools.c b/common/tools.c index 86e5ace..ad98094 100644 --- a/common/tools.c +++ b/common/tools.c @@ -57,8 +57,6 @@ int16_t decodeCommand(char **ptr) { uint8_t idx; char *paramptr = (*ptr); - // char *spaceptr = NULL; - // char *splitptr = NULL; // If no command entered, exit. if(*ptr == 0x0) @@ -66,9 +64,6 @@ int16_t decodeCommand(char **ptr) // Find the end of the command and terminate it for comparison. while(*paramptr == ' ') paramptr++; -// spaceptr = paramptr; -// while(*spaceptr != ' ' && *spaceptr != 0x00) spaceptr++; -// if(*spaceptr == ' ') { (*spaceptr) = 0x00; splitptr = spaceptr; spaceptr++; } // Loop through all the commands and try to find a match. for (idx=0; idx < NCMDKEYS; idx++) @@ -82,10 +77,6 @@ int16_t decodeCommand(char **ptr) } } - // // Restore buffer as we didnt process it. - // if(splitptr != NULL) - // *splitptr = ' '; - // No command found, so raise error. return CMD_BADKEY; } diff --git a/common/utils.c b/common/utils.c index 2db7480..0157fc1 100644 --- a/common/utils.c +++ b/common/utils.c @@ -150,7 +150,7 @@ char *getStrParam(char **ptr) { char *paramptr = (*ptr); char *spaceptr; - uint8_t inQuotes = 0; + uint8_t inQuotes = 0; // If no parameter available, exit. if(*ptr == 0x0) @@ -158,15 +158,15 @@ char *getStrParam(char **ptr) // Find the end of the command and terminate it. while(*paramptr == ' ') paramptr++; - if(*paramptr == '"') { paramptr++; inQuotes=1; } + if(*paramptr == '"') { paramptr++; inQuotes=1; } spaceptr = paramptr; - if(inQuotes == 1) - { - while(*spaceptr != '"' && *spaceptr != 0x00) spaceptr++; - } else - { - while(*spaceptr != ' ' && *spaceptr != 0x00) spaceptr++; - } + if(inQuotes == 1) + { + while(*spaceptr != '"' && *spaceptr != 0x00) spaceptr++; + } else + { + while(*spaceptr != ' ' && *spaceptr != 0x00) spaceptr++; + } if(*spaceptr == ' ' || *spaceptr == '"') { (*spaceptr) = 0x00; spaceptr++; } // Callers pointer is advanced to the next argument or end of string. diff --git a/libraries/lib/libimath2-k64f.a b/libraries/lib/libimath2-k64f.a index 879dfde..a052a26 100644 Binary files a/libraries/lib/libimath2-k64f.a and b/libraries/lib/libimath2-k64f.a differ diff --git a/libraries/lib/libumansi-k64f.a b/libraries/lib/libumansi-k64f.a index ae365af..52effe9 100644 Binary files a/libraries/lib/libumansi-k64f.a and b/libraries/lib/libumansi-k64f.a differ diff --git a/libraries/lib/libummath-k64f.a b/libraries/lib/libummath-k64f.a index e4134e7..a62b851 100644 Binary files a/libraries/lib/libummath-k64f.a and b/libraries/lib/libummath-k64f.a differ diff --git a/libraries/lib/libummathf-k64f.a b/libraries/lib/libummathf-k64f.a index e5d5017..33c873d 100644 Binary files a/libraries/lib/libummathf-k64f.a and b/libraries/lib/libummathf-k64f.a differ diff --git a/libraries/lib/libummisc-k64f.a b/libraries/lib/libummisc-k64f.a index 4db5d97..d63cdfa 100644 Binary files a/libraries/lib/libummisc-k64f.a and b/libraries/lib/libummisc-k64f.a differ diff --git a/libraries/lib/libumstdio-k64f.a b/libraries/lib/libumstdio-k64f.a index cc5ac8e..84c8ee1 100644 Binary files a/libraries/lib/libumstdio-k64f.a and b/libraries/lib/libumstdio-k64f.a differ diff --git a/zOS/src/zOS.cpp b/zOS/src/zOS.cpp index 42f322f..5e8b6ea 100644 --- a/zOS/src/zOS.cpp +++ b/zOS/src/zOS.cpp @@ -233,6 +233,7 @@ int cmdProcessor(void) // Local variables. char *ptr; char line[120]; + char *cmdline; long p1; long p2; long p3; @@ -242,9 +243,9 @@ int cmdProcessor(void) #if defined(__SD_CARD__) char *src1FileName; uint8_t diskInitialised = 0; - uint8_t fsInitialised = 0; - uint8_t trying = 0; - uint32_t retCode; + uint8_t fsInitialised = 0; + uint8_t trying = 0; + uint32_t retCode = 0xffffffff; FRESULT fr; #if defined(BUILTIN_HW_TEST_TIMERS) && BUILTIN_HW_TEST_TIMERS == 1 RTC rtc; @@ -614,66 +615,81 @@ int cmdProcessor(void) default: // Reset to start of line - if we match a command but it isnt built in, need to search for it on disk. // - ptr = line; - if(*ptr != 0x00) + if(line[0] != '\0') { - #if defined(__SD_CARD__) - if(diskInitialised && fsInitialised) + // Duplicate the command line to pass it unmodified to the application. + cmdline = strndup(line, sizeof(line)); + if(cmdline != NULL) { - // Append the app extension to the command and try to execute. + #if defined(__SD_CARD__) + // Get the name of the command to try the various formats of using it. + ptr = cmdline; src1FileName=getStrParam(&ptr); - // 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; - while(trying) + if(diskInitialised && fsInitialised && strlen(src1FileName) < 16) { - switch(trying) + // 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; + while(trying) { - // Try formatting with all the required drive and path fields. - case 1: - sprintf(&line[40], "%d:\\%s\\%s.%s", APP_CMD_BIN_DRIVE, APP_CMD_BIN_DIR, src1FileName, APP_CMD_EXTENSION); - break; - - // Try command as is. - case 2: - sprintf(&line[40], "%s", src1FileName); - break; - - // Try command as is but with drive and bin dir. - case 3: - sprintf(&line[40], "%d:\\%s\\%s", APP_CMD_BIN_DRIVE, APP_CMD_BIN_DIR, src1FileName); - break; - - // Try command as is but with just drive. - case 4: - default: - sprintf(&line[40], "%d:\\%s", APP_CMD_BIN_DRIVE, src1FileName); - break; - } - // command Load addr Exec addr Mode of exec Param1 Param2 Global struct SoC Config struct - #if defined __ZPU__ - retCode = fileExec(&line[40], APP_CMD_LOAD_ADDR, APP_CMD_EXEC_ADDR, EXEC_MODE_CALL, (uint32_t)ptr, 0, (uint32_t)&G, (uint32_t)&cfgSoC); - #else - retCode = fileExec(&line[40], APP_CMD_LOAD_ADDR, APP_CMD_EXEC_ADDR, EXEC_MODE_CALL, (uint32_t)ptr, 0, (uint32_t)&G, (uint32_t)&cfgSoC); - #endif + switch(trying) + { + // Try formatting with all the required drive and path fields. + case 1: + sprintf(&line[40], "%d:\\%s\\%s.%s", APP_CMD_BIN_DRIVE, APP_CMD_BIN_DIR, src1FileName, APP_CMD_EXTENSION); + break; + + // Try command as is. + case 2: + sprintf(&line[40], "%s", src1FileName); + break; + + // Try command as is but with drive and bin dir. + case 3: + sprintf(&line[40], "%d:\\%s\\%s", APP_CMD_BIN_DRIVE, APP_CMD_BIN_DIR, src1FileName); + break; + + // Try command as is but with just drive. + case 4: + default: + sprintf(&line[40], "%d:\\%s", APP_CMD_BIN_DRIVE, src1FileName); + break; + } + // command Load addr Exec addr Mode of exec Param1 Param2 Global struct SoC Config struct + #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 + 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 - if(retCode == 0xffffffff && trying <= 3) - { - trying++; - } else - { - trying = 0; + if(retCode == 0xffffffff && trying <= 3) + { + trying++; + } else + { + trying = 0; + } } } - } - if(!diskInitialised || !fsInitialised || retCode == 0xffffffff) - { - printf("Bad command.\n"); - } + if(!diskInitialised || !fsInitialised || retCode == 0xffffffff) + { + printf("Bad command.\n"); + } + + // Free up the duplicated command line. + // + free(cmdline); #else - printf("Unknown command!\n"); + // Free up the duplicated command line. + // + free(cmdline); + printf("Unknown command!\n"); #endif + } else + { + printf("Memory exhausted, cannot process command.\n"); + } } break;