/***********************************************************************************************************************
PicoMite MMBasic

Picomite.c

<COPYRIGHT HOLDERS>  Geoff Graham, Peter Mather
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved. 
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 
1.	Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2.	Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
    in the documentation and/or other materials provided with the distribution.
3.	The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed 
    on the console at startup (additional copyright messages may be added).
4.	All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed 
    by the <copyright holder>.
5.	Neither the name of the <copyright holder> nor the names of its contributors may be used to endorse or promote products derived from this software 
    without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDERS> AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDERS> BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

************************************************************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdbool.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "pico/binary_info.h"
#include "configuration.h"
#include "hardware/watchdog.h"
#include "hardware/clocks.h"
#include "hardware/flash.h"
#include "hardware/adc.h"
#include "hardware/exception.h"
#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
#include "hardware/structs/systick.h"
#include "hardware/structs/timer.h"
#include "hardware/vreg.h"
#include "hardware/structs/pads_qspi.h"
#include "pico/unique_id.h"
#include "hardware/pwm.h"
extern void start_i2s(int pio, int sm);
#ifdef rp2350
#include "hardware/structs/qmi.h"
#endif
#ifdef PICOMITEVGA
extern void start_vga_i2s(void);
#ifndef HDMI
#endif
#endif
#define COPYRIGHT   "Copyright " YEAR " Geoff Graham\r\n"\
                    "Copyright " YEAR2 " Peter Mather\r\n\r\n"

#ifdef USBKEYBOARD
    #include "tusb.h"
    #include "host/hcd.h"
    #include "usb_host_files/tusb_config.h"
#else
    #include "pico/unique_id.h"
    #include "class/cdc/cdc_device.h" 
#endif
#ifndef rp2350
    #include "hardware/structs/ssi.h"
    #include "hardware/vreg.h"
#else
    #ifdef HDMI
        #include "hardware/structs/hstx_ctrl.h"
        #include "hardware/structs/hstx_fifo.h"
    #endif
    #include "hardware/dma.h"
    #include "hardware/gpio.h"
    #include "hardware/irq.h"
    #include "hardware/structs/bus_ctrl.h"
    #include "hardware/structs/xip_ctrl.h"
    #include "hardware/structs/sio.h"
    #include "hardware/vreg.h"
    #include "pico/multicore.h"
    #include "pico/sem.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include "pico/stdlib.h"
    #include "hardware/clocks.h"
    #include <string.h>
    #include "hardware/regs/sysinfo.h"
    #include "hardware/regs/powman.h"
    bool rp2350a=true;
    uint32_t PSRAMsize=0;
#endif
#include "hardware/structs/bus_ctrl.h"
#include <pico/bootrom.h>
#include "hardware/irq.h"
#include "hardware/pio.h"
#include "hardware/pio_instructions.h"
#ifdef PICOMITEWEB
    #include "lwipopts.h"
    #include "pico/cyw43_arch.h"
    #include "lwip/pbuf.h"
    #include "lwip/tcp.h"
    #include "lwip/dns.h"
    #include "lwip/pbuf.h"
    #include "lwip/udp.h"
#endif
#ifdef PICOMITEVGA
    uint16_t map16[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    uint32_t map16pairs[16];
    volatile uint8_t transparent=0;
    volatile uint8_t transparents=0;
    volatile int RGBtransparent=0;
    int MODE1SIZE, MODE2SIZE, MODE3SIZE, MODE4SIZE, MODE5SIZE;
#ifdef HDMI
    uint16_t map16d[16];
    uint32_t map16q[16];
    uint8_t  map16s[16];
#endif
    // 126 MHz timings
int QVGA_TOTAL;// total clock ticks (= QVGA_HSYNC + QVGA_BP + WIDTH*QVGA_CPP[1600] + QVGA_FP)
int QVGA_HSYNC;	// horizontal sync clock ticks
int QVGA_BP	;	// back porch clock ticks
int QVGA_FP;	// front porch clock ticks

// QVGA vertical timings
int QVGA_VACT;	// V active scanlines (= 2*HEIGHT)
int QVGA_VFRONT;	// V front porch
int QVGA_VSYNC;	// length of V sync (number of scanlines)
int QVGA_VBACK;	// V back porch
int QVGA_VTOT;	// total scanlines (= QVGA_VSYNC + QVGA_VBACK + QVGA_VACT + QVGA_VFRONT)
int QVGA_HACT;	// V active scanlines (= 2*HEIGHT)

    #ifndef HDMI
    #include "Include.h"
    #endif
    #ifdef USBKEYBOARD
        #ifdef HDMI
            #define MES_SIGNON  "\rPicoMiteHDMI MMBasic USB " CHIP " Edition V"VERSION "\r\n"
        #else
            #define MES_SIGNON  "\rPicoMiteVGA MMBasic USB " CHIP " Edition V"VERSION "\r\n"
        #endif
        extern void hid_app_task(void);
        volatile int keytimer=0;
        extern void USB_bus_reset(void);
        bool USBenabled=false;
    #else
        #ifdef HDMI
            #define MES_SIGNON  "\rPicoMiteHDMI MMBasic " CHIP " Edition V"VERSION "\r\n"
        #else
            #define MES_SIGNON  "\rPicoMiteVGA MMBasic " CHIP " Edition V"VERSION "\r\n"
        #endif
#endif

#endif
#ifdef PICOMITEWEB
    #define MES_SIGNON  "\rWebMite MMBasic " CHIP " Edition V"VERSION "\r\n"
    volatile int WIFIconnected=0;
    int startupcomplete=0;
    void ProcessWeb(int mode);
    char LCDAttrib=0;
    #endif
#ifdef PICOMITE
    #ifdef USBKEYBOARD
        #include "tusb.h"
        #include "host/hcd.h"
        #define MES_SIGNON  "\rPicoMite MMBasic USB " CHIP " Edition V"VERSION "\r\n"
        extern void hid_app_task(void);
        volatile int keytimer=0;
        extern void USB_bus_reset(void);
        bool USBenabled=false;
        #include "pico/multicore.h"
        mutex_t	frameBufferMutex;					// mutex to lock frame buffer
    #else
        #define MES_SIGNON  "\rPicoMite MMBasic " CHIP " Edition V"VERSION "\r\n"
        #include "pico/multicore.h"
        mutex_t	frameBufferMutex;					// mutex to lock frame buffer
        #endif
        char LCDAttrib=0;
#endif
#define KEYCHECKTIME 16
int ListCnt;
int MMCharPos;
int MMPromptPos;
int busfault=0; 
int ExitMMBasicFlag = false;
volatile int MMAbort = false;
unsigned int _excep_peek;
void CheckAbort(void);
void TryLoadProgram(void);
unsigned char lastchar=0;
int adc_clk_div;
unsigned char BreakKey = BREAK_KEY;                                          // defaults to CTRL-C.  Set to zero to disable the break function
volatile char ConsoleRxBuf[CONSOLE_RX_BUF_SIZE]={0};
volatile int ConsoleRxBufHead = 0;
volatile int ConsoleRxBufTail = 0;
volatile char ConsoleTxBuf[CONSOLE_TX_BUF_SIZE]={0};
volatile int ConsoleTxBufHead = 0;
volatile int ConsoleTxBufTail = 0;
uint I2SOff;

#ifndef USBKEYBOARD
extern void initMouse0(int sensitivity);
volatile unsigned int MouseTimer = 0;
#endif
volatile unsigned int AHRSTimer = 0;
volatile unsigned int InkeyTimer = 0;
volatile long long int mSecTimer = 0;                               // this is used to count mSec
volatile unsigned int WDTimer = 0;
volatile unsigned int diskchecktimer = DISKCHECKRATE;
volatile unsigned int clocktimer=60*60*1000;
volatile unsigned int PauseTimer = 0;
volatile unsigned int ClassicTimer = 0;
volatile unsigned int NunchuckTimer = 0;
volatile unsigned int IntPauseTimer = 0;
volatile unsigned int Timer1=0, Timer2=0, Timer3=0, Timer4=0, Timer5=0;		                       //1000Hz decrement timer
volatile unsigned int KeyCheck=2000;
volatile int ds18b20Timer = -1;
volatile unsigned int ScrewUpTimer = 0;
//volatile int second = 0;                                            // date/time counters
//volatile int minute = 0;
//volatile int hour = 0;
//volatile int day = 1;
//volatile int month = 1;
//volatile int year = 2000;
volatile unsigned int GPSTimer = 0;
volatile unsigned int SecondsTimer = 0;
volatile unsigned int I2CTimer = 0;
volatile int day_of_week=1;
unsigned char PulsePin[NBR_PULSE_SLOTS];
unsigned char PulseDirection[NBR_PULSE_SLOTS];
int PulseCnt[NBR_PULSE_SLOTS];
int PulseActive;
const uint8_t *flash_option_contents = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET);
const uint8_t *SavedVarsFlash = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET +  FLASH_ERASE_SIZE);
const uint8_t *flash_target_contents = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET + FLASH_ERASE_SIZE + SAVEDVARS_FLASH_SIZE);
const uint8_t *flash_progmemory = (const uint8_t *) (XIP_BASE + PROGSTART);
const uint8_t *flash_libmemory = (const uint8_t *) (XIP_BASE + PROGSTART - MAX_PROG_SIZE);
int ticks_per_second; 
int InterruptUsed;
int calibrate=0;
char id_out[12];
MMFLOAT VCC=3.3;
int PromptFont, PromptFC=0xFFFFFF, PromptBC=0;                             // the font and colours selected at the prompt
volatile int DISPLAY_TYPE;
volatile bool processtick = true;
unsigned char WatchdogSet = false;
unsigned char IgnorePIN = false;
unsigned char SPIatRisk = false;
uint32_t restart_reason=0;
uint32_t __uninitialized_ram(_excep_code);
uint64_t __uninitialized_ram(_persistent);
unsigned char lastcmd[STRINGSIZE*2];                                           // used to store the last command in case it is needed by the EDIT command
FATFS fs;                 // Work area (file system object) for logical drive
bool timer_callback(repeating_timer_t *rt);
static uint64_t __not_in_flash_func(uSecFunc)(uint64_t a){
    uint64_t b=time_us_64()+a;
    while(time_us_64()<b){}
    return b;
}
extern void MX470Display(int fn);
//Vector to CFunction routine called every command (ie, from the BASIC interrupt checker)
extern unsigned int CFuncInt1;
//Vector to CFunction routine called by the interrupt 2 handler
extern unsigned int CFuncInt2;
extern unsigned int CFuncmSec;
extern void CallCFuncInt1(void);
extern void CallCFuncInt2(void);
extern volatile bool CSubComplete;
static uint64_t __not_in_flash_func(uSecTimer)(void){ return time_us_64();}
static int64_t PinReadFunc(int a){return gpio_get(PinDef[a].GPno);}
extern void CallCFuncmSec(void);
extern volatile uint32_t irqs;
#define CFUNCRAM_SIZE   256
int CFuncRam[CFUNCRAM_SIZE/sizeof(int)];
repeating_timer_t timer;
MMFLOAT IntToFloat(long long int a){ return a; }
MMFLOAT FMul(MMFLOAT a, MMFLOAT b){ return a * b; }
MMFLOAT FAdd(MMFLOAT a, MMFLOAT b){ return a + b; }
MMFLOAT FSub(MMFLOAT a, MMFLOAT b){ return a - b; }
MMFLOAT FDiv(MMFLOAT a, MMFLOAT b){ return a / b; }
uint32_t CFunc_delay_us;
#ifndef HDMI
int QVGA_CLKDIV;	// SM divide clock ticks
#endif
void PIOExecute(int pion, int sm, uint32_t ins){
    PIO pio = (pion ? pio1: pio0);
    pio_sm_exec(pio, sm, ins);
}

int IDiv(int a, int b){return a/b;}
int   FCmp(MMFLOAT a,MMFLOAT b){if(a>b) return 1;else if(a<b)return -1; else return 0;}
MMFLOAT LoadFloat(unsigned long long c){union ftype{ unsigned long long a; MMFLOAT b;}f;f.a=c;return f.b; }
const void * const CallTable[] __attribute__((section(".text")))  = {	(void *)uSecFunc,	//0x00
																		(void *)putConsole,	//0x04
																		(void *)getConsole,	//0x08
																		(void *)ExtCfg,	//0x0c
																		(void *)ExtSet,	//0x10
																		(void *)ExtInp,	//0x14
																		(void *)PinSetBit,	//0x18
																		(void *)PinReadFunc,	//0x1c
																		(void *)MMPrintString,	//0x20
																		(void *)IntToStr,	//0x24
																		(void *)CheckAbort,	//0x28
																		(void *)GetMemory,	//0x2c
																		(void *)GetTempMemory,	//0x30
																		(void *)FreeMemory, //0x34
																		(void *)&DrawRectangle,	//0x38
																		(void *)&DrawBitmap,	//0x3c
																		(void *)DrawLine,	//0x40
																		(void *)FontTable,	//0x44
																		(void *)&ExtCurrentConfig,	//0x48
																		(void *)&HRes,	//0x4C
																		(void *)&VRes,	//0x50
																		(void *)SoftReset, //0x54
																		(void *)error,	//0x58
																		(void *)&ProgMemory,	//0x5c
																		(void *)&g_vartbl, //0x60
																		(void *)&g_varcnt,  //0x64
																		(void *)&DrawBuffer,	//0x68
																		(void *)&ReadBuffer,	//0x6c
																		(void *)&FloatToStr,	//0x70
                                                                        (void *)CallExecuteProgram, //0x74
                                                                        (void *)&CFuncmSec, //0x78
                                                                        (void *)CFuncRam,	//0x7c
                                                                        (void *)&ScrollLCD,	//0x80
																		(void *)IntToFloat, //0x84
																		(void *)FloatToInt64,	//0x88
																		(void *)&Option,	//0x8c
																		(void *)sin,	//0x90
																		(void *)DrawCircle,	//0x94
																		(void *)DrawTriangle,	//0x98
																		(void *)uSecTimer,	//0x9c
                                                                        (void *)FMul,//0xa0
                                                                        (void *)FAdd,//0xa4
                                                                        (void *)FSub,//0xa8
                                                                        (void *)FDiv,//0xac
                                                                        (void *)FCmp,//0xb0
                                                                        (void *)&LoadFloat,//0xb4
                                                                        (void *)&CFuncInt1,	//0xb8
                                                                        (void *)&CFuncInt2,	//0xbc
																		(void *)&CSubComplete,	//0xc0
																		(void *)&AudioOutput,	//0xc4
                                                                        (void *)IDiv,//0x0xc8
                                                                        (void *)&AUDIO_WRAP,//0x0xcc
                                                                        (void *)&CFuncInt3,	//0xb8
                                                                        (void *)&CFuncInt4,	//0xbc
                                                                        (void *)PIOExecute,
									   	   	   	   	   	   	   	   	   	   };

const struct s_PinDef PinDef[]={
	    { 0, 99, "NULL",  UNUSED  ,99, 99},
	    { 1,  0, "GP0",  DIGITAL_IN | DIGITAL_OUT | SPI0RX | UART0TX  | I2C0SDA | PWM0A,99,0},  	// pin 1
		{ 2,  1, "GP1",  DIGITAL_IN | DIGITAL_OUT | UART0RX | I2C0SCL | PWM0B ,99,128},    		    // pin 2
		{ 3, 99, "GND",  UNUSED  ,99,99},                                                           // pin 3
		{ 4,  2, "GP2",  DIGITAL_IN | DIGITAL_OUT | SPI0SCK | I2C1SDA | PWM1A ,99,1},   		    // pin 4
		{ 5,  3, "GP3",  DIGITAL_IN | DIGITAL_OUT | SPI0TX | I2C1SCL | PWM1B ,99,129},    			// pin 5
		{ 6,  4, "GP4",  DIGITAL_IN | DIGITAL_OUT | SPI0RX| UART1TX  | I2C0SDA | PWM2A ,99,2},  	// pin 6
		{ 7,  5, "GP5",   DIGITAL_IN | DIGITAL_OUT | UART1RX | I2C0SCL | PWM2B	,99,130},    		// pin 7
		{ 8, 99, "GND",  UNUSED  ,99, 99},                                                          // pin 8
		{ 9,  6, "GP6",  DIGITAL_IN | DIGITAL_OUT | SPI0SCK | I2C1SDA | PWM3A	,99, 3},  			// pin 9
		{ 10,  7, "GP7",  DIGITAL_IN | DIGITAL_OUT | SPI0TX | I2C1SCL | PWM3B	,99, 131}, 		    // pin 10
	    { 11,  8, "GP8",  DIGITAL_IN | DIGITAL_OUT | SPI1RX | UART1TX  | I2C0SDA | PWM4A ,99, 4}, 	// pin 11
		{ 12,  9, "GP9",  DIGITAL_IN | DIGITAL_OUT | UART1RX | I2C0SCL | PWM4B	,99, 132},    		// pin 12
		{ 13, 99, "GND",  UNUSED  ,99, 99},                                                         // pin 13
		{ 14, 10, "GP10",  DIGITAL_IN | DIGITAL_OUT | SPI1SCK | I2C1SDA | PWM5A	,99, 5},  			// pin 14
		{ 15, 11, "GP11",  DIGITAL_IN | DIGITAL_OUT | SPI1TX | I2C1SCL | PWM5B	,99, 133},       	// pin 15
#ifdef HDMI
		{ 16, 12, "HDMI", UNUSED  ,99, 99},                                                          // pin 16
		{ 17, 13, "HDMI", UNUSED  ,99, 99},                                                          // pin 17
		{ 18, 99, "GND", UNUSED  ,99, 99},                                                          // pin 18
		{ 19, 14, "HDMI", UNUSED  ,99, 99},                                                          // pin 19
		{ 20, 15, "HDMI", UNUSED  ,99, 99},                                                          // pin 20
		{ 21, 16, "HDMI", UNUSED  ,99, 99},                                                          // pin 21
		{ 22, 17, "HDMI", UNUSED  ,99, 99},                                                          // pin 22
		{ 23, 99, "GND", UNUSED  ,99, 99},                                                          // pin 23
		{ 24, 18, "HDMI", UNUSED  ,99, 99},                                                          // pin 24
		{ 25, 19, "HDMI", UNUSED  ,99, 99},                                                          // pin 25
#else	
		{ 16, 12, "GP12",  DIGITAL_IN | DIGITAL_OUT | SPI1RX | UART0TX | I2C0SDA | PWM6A ,99, 6},   // pin 16
		{ 17, 13, "GP13",  DIGITAL_IN | DIGITAL_OUT | UART0RX | I2C0SCL | PWM6B	,99, 134},    		// pin 17
		{ 18, 99, "GND", UNUSED  ,99, 99},                                                          // pin 18
		{ 19, 14, "GP14",  DIGITAL_IN | DIGITAL_OUT | SPI1SCK | I2C1SDA | PWM7A	,99, 7},    		// pin 19
		{ 20, 15, "GP15",  DIGITAL_IN | DIGITAL_OUT | SPI1TX | I2C1SCL | PWM7B	,99, 135},  		// pin 20

		{ 21, 16, "GP16",  DIGITAL_IN | DIGITAL_OUT | SPI0RX | UART0TX | I2C0SDA | PWM0A ,99, 0},   // pin 21
		{ 22, 17, "GP17",  DIGITAL_IN | DIGITAL_OUT | UART0RX | I2C0SCL | PWM0B	,99, 128},    		// pin 22
		{ 23, 99, "GND",  UNUSED  ,99, 99},                                                         // pin 23
	    { 24, 18, "GP18",  DIGITAL_IN | DIGITAL_OUT | SPI0SCK | I2C1SDA | PWM1A	,99, 1}, 			// pin 24
	    { 25, 19, "GP19",  DIGITAL_IN | DIGITAL_OUT | SPI0TX | I2C1SCL | PWM1B	,99, 129},  		// pin 25
#endif
	    { 26, 20, "GP20",  DIGITAL_IN | DIGITAL_OUT | SPI0RX | UART1TX| I2C0SDA | PWM2A	,99, 2},    // pin 26
	    { 27, 21, "GP21",  DIGITAL_IN | DIGITAL_OUT | UART1RX| I2C0SCL | PWM2B	,99, 130},    		// pin 27
		{ 28, 99, "GND",  UNUSED  ,99, 99},                                                         // pin 28
		{ 29, 22, "GP22",  DIGITAL_IN | DIGITAL_OUT | SPI0SCK | I2C1SDA| PWM3A	,99, 3},    				// pin 29
		{ 30, 99, "RUN",  UNUSED  ,99, 99},                                                         // pin 30
	    { 31, 26, "GP26",  DIGITAL_IN | DIGITAL_OUT	| ANALOG_IN | SPI1SCK| I2C1SDA | PWM5A , 0 , 5},// pin 31
	    { 32, 27, "GP27",  DIGITAL_IN | DIGITAL_OUT	| ANALOG_IN | SPI1TX| I2C1SCL | PWM5B , 1, 133},// pin 32
		{ 33, 99, "AGND", UNUSED  ,99, 99},                                                         // pin 33
		{ 34, 28, "GP28", DIGITAL_IN |DIGITAL_OUT| ANALOG_IN| SPI1RX| UART0TX|I2C0SDA| PWM6A, 2, 6},// pin 34
	    { 35, 99, "VREF", UNUSED  ,99, 99},                                                         // pin 35
		{ 36, 99, "3V3", UNUSED  ,99, 99},                                                          // pin 36
		{ 37, 99, "3V3E", UNUSED  ,99, 99},                                                         // pin 37
		{ 38, 99, "GND", UNUSED  ,99, 99},                                                          // pin 38
		{ 39, 99, "VSYS", UNUSED  ,99, 99},                                                         // pin 39
		{ 40, 99, "VBUS", UNUSED  ,99, 99},                                                         // pin 40
    #ifndef PICOMITEWEB
		{ 41, 23, "GP23", DIGITAL_IN | DIGITAL_OUT | SPI0TX | I2C1SCL| PWM3B  ,99 , 131},           // pseudo pin 41
		{ 42, 24, "GP24", DIGITAL_IN | DIGITAL_OUT | SPI1RX | UART1TX | I2C0SDA| PWM4A  ,99 , 4},   // pseudo pin 42
		{ 43, 25, "GP25", DIGITAL_IN | DIGITAL_OUT | UART1RX | I2C0SCL| PWM4B  ,99 , 132},          // pseudo pin 43
		{ 44, 29, "GP29", DIGITAL_IN | DIGITAL_OUT | ANALOG_IN | UART0RX | I2C0SCL | PWM6B, 3, 134},// pseudo pin 44
    #endif
    #ifdef rp2350
    #ifndef PICOMITEWEB
		{ 45, 30, "GP30", DIGITAL_IN | DIGITAL_OUT | SPI1SCK | I2C1SDA | PWM7A  ,99 , 7},           // pseudo pin 45
		{ 46, 31, "GP31", DIGITAL_IN | DIGITAL_OUT | SPI1TX | I2C1SCL| PWM7B  ,99 , 135},           // pseudo pin 46
		{ 47, 32, "GP32", DIGITAL_IN | DIGITAL_OUT | UART0TX | SPI0RX | I2C0SDA| PWM8A  ,99 , 8},           // pseudo pin 47
		{ 48, 33, "GP33", DIGITAL_IN | DIGITAL_OUT | UART0RX | I2C0SCL| PWM8B  ,99 , 136},           // pseudo pin 48
		{ 49, 34, "GP34", DIGITAL_IN | DIGITAL_OUT | SPI0SCK | I2C1SDA| PWM9A  ,99 , 9},           // pseudo pin 49
		{ 50, 35, "GP35", DIGITAL_IN | DIGITAL_OUT | SPI0TX | I2C1SCL| PWM9B  ,99 , 137},           // pseudo pin 50
		{ 51, 36, "GP36", DIGITAL_IN | DIGITAL_OUT | UART1TX | SPI0RX | I2C0SDA| PWM10A  ,99 , 10},           // pseudo pin 51
		{ 52, 37, "GP37", DIGITAL_IN | DIGITAL_OUT | UART1RX | I2C0SCL| PWM10B  ,99 , 138},           // pseudo pin 52
		{ 53, 38, "GP38", DIGITAL_IN | DIGITAL_OUT | SPI0SCK | I2C1SDA| PWM11A  ,99 , 11},           // pseudo pin 53
		{ 54, 39, "GP39", DIGITAL_IN | DIGITAL_OUT | SPI0TX | I2C1SCL| PWM11B  ,99 , 139},           // pseudo pin 54
		{ 55, 40, "GP40", DIGITAL_IN | DIGITAL_OUT | ANALOG_IN| UART1TX | SPI1RX | I2C0SDA| PWM8A  ,0 , 8},           // pseudo pin 55
		{ 56, 41, "GP41", DIGITAL_IN | DIGITAL_OUT | ANALOG_IN| UART1RX | I2C0SCL| PWM8B  ,1 , 136},           // pseudo pin 56
		{ 57, 42, "GP42", DIGITAL_IN | DIGITAL_OUT | ANALOG_IN| SPI1SCK | I2C1SDA| PWM9A  ,2 , 9},           // pseudo pin 57
		{ 58, 43, "GP43", DIGITAL_IN | DIGITAL_OUT | ANALOG_IN| SPI1TX | I2C1SCL| PWM9B  ,3 , 137},           // pseudo pin 58
		{ 59, 44, "GP44", DIGITAL_IN | DIGITAL_OUT | UART0TX | ANALOG_IN | SPI1RX | I2C0SDA| PWM10A  ,4 , 10},           // pseudo pin 59
		{ 60, 45, "GP45", DIGITAL_IN | DIGITAL_OUT | UART0RX | ANALOG_IN | I2C0SCL| PWM10B  ,5 , 138},           // pseudo pin 60
		{ 61, 46, "GP46", DIGITAL_IN | DIGITAL_OUT | ANALOG_IN | SPI1SCK | I2C1SDA| PWM11A  ,6 , 11},           // pseudo pin 61
		{ 62, 47, "GP47", DIGITAL_IN | DIGITAL_OUT | ANALOG_IN | SPI1TX | I2C1SCL| PWM11B  ,7 , 139},           // pseudo pin 62

    #endif
    #endif
};
char alive[]="\033[?25h";
const char DaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

static inline CommandToken commandtbl_decode(const unsigned char *p){
    return ((CommandToken)(p[0] & 0x7f)) | ((CommandToken)(p[1] & 0x7f)<<7);
}
char banner[64];
void __not_in_flash_func(routinechecks)(void){
    static int when=0;
    if(abs((time_us_64()-mSecTimer*1000))> 5000){
        cancel_repeating_timer(&timer);
        add_repeating_timer_us(-1000, timer_callback, NULL, &timer);
        mSecTimer=time_us_64()/1000;
    }
    if (CurrentlyPlaying == P_WAV || CurrentlyPlaying == P_FLAC || CurrentlyPlaying==P_MP3 || CurrentlyPlaying==P_MIDI ){
#ifdef PICOMITE
        if(SPIatRisk)mutex_enter_blocking(&frameBufferMutex);			// lock the frame buffer
#endif
        checkWAVinput();
#ifdef PICOMITE
        if(SPIatRisk)mutex_exit(&frameBufferMutex);
#endif
    }
    if(CurrentlyPlaying == P_MOD) checkWAVinput();
    if(++when & 7 && CurrentLinePtr) return;
#ifdef USBKEYBOARD
    if(USBenabled){
        if(mSecTimer>2000){
            tuh_task();
            hid_app_task();
        }
    }
#else
	 static int c, read=0;
     if(tud_cdc_connected() && (Option.SerialConsole==0 || Option.SerialConsole>4) && Option.Telnet!=-1){
        while(( c=tud_cdc_read_char())!=-1){
            ConsoleRxBuf[ConsoleRxBufHead] = c;
            if(BreakKey && ConsoleRxBuf[ConsoleRxBufHead] == BreakKey) {// if the user wants to stop the progran
                MMAbort = true;                                        // set the flag for the interpreter to see
                ConsoleRxBufHead = ConsoleRxBufTail;                    // empty the buffer
            } else if(ConsoleRxBuf[ConsoleRxBufHead] == keyselect && KeyInterrupt!=NULL){
                Keycomplete=true;
            } else {
                ConsoleRxBufHead = (ConsoleRxBufHead + 1) % CONSOLE_RX_BUF_SIZE;     // advance the head of the queue
                if(ConsoleRxBufHead == ConsoleRxBufTail) {                           // if the buffer has overflowed
                    ConsoleRxBufTail = (ConsoleRxBufTail + 1) % CONSOLE_RX_BUF_SIZE; // throw away the oldest char
                }
            }
        }
    }
#endif
	if(GPSchannel)processgps();
    if(diskchecktimer == 0)CheckSDCard();
#ifdef GUICONTROLS
    if(Ctrl && TOUCH_GETIRQTRIS && !calibrate)ProcessTouch();
#endif

//        if(tud_cdc_connected() && KeyCheck==0){
//            SSPrintString(alive);
//        }
    if(clocktimer==0 && Option.RTC){
        if(classicread==0 && nunchuckread==0){
            RtcGetTime(0);
        }
    }
#ifndef USBKEYBOARD
    if(Option.KeyboardConfig==CONFIG_I2C && KeyCheck==0){
        if(read==0){
            CheckI2CKeyboard(0,0);
            read=1;
        } else {
            CheckI2CKeyboard(0,1);
            read=0;
        }
        KeyCheck=KEYCHECKTIME;
    }
#endif
    if(classic1 && ClassicTimer>=10){
        if(classicread==0){
			WiiSend(sizeof(readcontroller),(char *)readcontroller);
            if(!mmI2Cvalue)classicread=1;
        } else if(classicread==1){
			WiiReceive(6, (char *)nunbuff);
            if(!mmI2Cvalue)classicread=2;
            else classicread=0;
        } else {
            classicproc();
            classicread=0;
            classic1=2;
        }
        ClassicTimer=0; 
    }
    if(nunchuck1 && NunchuckTimer>=10){ 
        if(nunchuckread==false){
			WiiSend(sizeof(readcontroller),(char *)readcontroller);
            if(!mmI2Cvalue)nunchuckread=1;
        } else if(nunchuckread==1){
			WiiReceive(6, (char *)nunbuff);
            if(!mmI2Cvalue)nunchuckread=2;
            else nunchuckread=0;
        } else {
            nunproc();
            nunchuckread=0;
            nunchuck1=2;
        }
        NunchuckTimer=0;
    }
/*frame
    if(frame && CurrentLinePtr)ShowCursor(framecursor);
*/
}

int __not_in_flash_func(getConsole)(void) {
    int c=-1;
#ifdef PICOMITEWEB
    ProcessWeb(1);
#endif
    CheckAbort();
    if(ConsoleRxBufHead != ConsoleRxBufTail) {                            // if the queue has something in it
        c = ConsoleRxBuf[ConsoleRxBufTail];
        ConsoleRxBufTail = (ConsoleRxBufTail + 1) % CONSOLE_RX_BUF_SIZE;   // advance the head of the queue
	}
    return c;
}

void __not_in_flash_func(putConsole)(int c, int flush) {
    if(OptionConsole & 2)DisplayPutC(c);
    if(OptionConsole & 1)SerialConsolePutC(c, flush);
}
// put a character out to the serial console
char  __not_in_flash_func(SerialConsolePutC)(char c, int flush) {
	if(c == '\b') {
	   	if (MMCharPos!=1){
		   MMCharPos -= 1;
	   	}
	}    
#ifdef PICOMITEWEB
    if(Option.Telnet!=-1){
#endif
#ifndef USBKEYBOARD
    if(Option.SerialConsole==0 || Option.SerialConsole>4){
        if(tud_cdc_connected()){
            putc(c,stdout);
            if(flush){
                fflush(stdout);
            }
        }
    } 
#endif
    if(Option.SerialConsole){
        int empty=uart_is_writable((Option.SerialConsole & 3)==1 ? uart0 : uart1);
		while(ConsoleTxBufTail == ((ConsoleTxBufHead + 1) % CONSOLE_TX_BUF_SIZE)); //wait if buffer full
		ConsoleTxBuf[ConsoleTxBufHead] = c;							// add the char
		ConsoleTxBufHead = (ConsoleTxBufHead + 1) % CONSOLE_TX_BUF_SIZE;		   // advance the head of the queue
		if(empty){
            while(irqs){}
	        uart_set_irq_enables((Option.SerialConsole & 3)==1 ? uart0 : uart1, true, true);
			irq_set_pending((Option.SerialConsole & 3)==1 ? UART0_IRQ : UART1_IRQ);
		}
    }
#ifdef PICOMITEWEB
    }
    TelnetPutC(c,flush);
    ProcessWeb(1);
#endif
    return c;
}
char MMputchar(char c, int flush) {
    putConsole(c, flush);
    if(isprint(c)) MMCharPos++;
    if(c == '\r') {
        MMCharPos = 1;
    }
    return c;
}
// returns the number of character waiting in the console input queue
int kbhitConsole(void) {
    int i;
    i = ConsoleRxBufHead - ConsoleRxBufTail;
    if(i < 0) i += CONSOLE_RX_BUF_SIZE;
    return i;
}
// check if there is a keystroke waiting in the buffer and, if so, return with the char
// returns -1 if no char waiting
// the main work is to check for vt100 escape code sequences and map to Maximite codes
#if (defined(PICOMITEVGA) || defined(PICOMITEWEB)) && !defined(rp2350)
int MMInkey(void) {
#else
int __not_in_flash_func(MMInkey)(void) {
#endif
    unsigned int c = -1;                                            // default no character
    unsigned int tc = -1;                                           // default no character
    unsigned int ttc = -1;                                          // default no character
    static unsigned int c1 = -1;
    static unsigned int c2 = -1;
    static unsigned int c3 = -1;
    static unsigned int c4 = -1;
//	static int crseen = 0;

    if(c1 != -1) {                                                  // check if there are discarded chars from a previous sequence
        c = c1; c1 = c2; c2 = c3; c3 = c4; c4 = -1;                 // shuffle the queue down
        return c;                                                   // and return the head of the queue
    }

    c = getConsole();                                               // do discarded chars so get the char
#ifndef USBKEYBOARD
    if(c==-1)CheckKeyboard();
#endif
    if(!(c==0x1b))return c;
    InkeyTimer = 0;                                             // start the timer
    while((c = getConsole()) == -1 && InkeyTimer < 30);         // get the second char with a delay of 30mS to allow the next char to arrive
    if(c == 'O'){   //support for many linux terminal emulators
        while((c = getConsole()) == -1 && InkeyTimer < 50);        // delay some more to allow the final chars to arrive, even at 1200 baud
        if(c == 'P') return F1;
        if(c == 'Q') return F2;
        if(c == 'R') return F3;
        if(c == 'S') return F4;
        if(c == 'T') return F5;
        if(c == '2'){
            while((tc = getConsole()) == -1 && InkeyTimer < 70);        // delay some more to allow the final chars to arrive, even at 1200 baud
            if(tc == 'R') return F3 + 0x20;
            c1 = 'O'; c2 = c; c3 = tc; return 0x1b;                 // not a valid 4 char code
        }
        c1 = 'O'; c2 = c; return 0x1b;                 // not a valid 4 char code
    }
    if(c != '[') { c1 = c; return 0x1b; }                       // must be a square bracket
    while((c = getConsole()) == -1 && InkeyTimer < 50);         // get the third char with delay
    if(c == 'A') return UP;                                     // the arrow keys are three chars
    if(c == 'B') return DOWN;
    if(c == 'C') return RIGHT;
    if(c == 'D') return LEFT;
    if(c < '1' && c > '6') { c1 = '['; c2 = c; return 0x1b; }   // the 3rd char must be in this range
    while((tc = getConsole()) == -1 && InkeyTimer < 70);        // delay some more to allow the final chars to arrive, even at 1200 baud
    if(tc == '~') {                                             // all 4 char codes must be terminated with ~
        if(c == '1') return HOME;
        if(c == '2') return INSERT;
        if(c == '3') return DEL;
        if(c == '4') return END;
        if(c == '5') return PUP;
        if(c == '6') return PDOWN;
        c1 = '['; c2 = c; c3 = tc; return 0x1b;                 // not a valid 4 char code
    }
    while((ttc = getConsole()) == -1 && InkeyTimer < 90);       // get the 5th char with delay
    if(ttc == '~') {                                            // must be a ~
        if(c == '1') {
            if(tc >='1' && tc <= '5') return F1 + (tc - '1');   // F1 to F5
            if(tc >='7' && tc <= '9') return F6 + (tc - '7');   // F6 to F8
        }
        if(c == '2') {
            if(tc =='0' || tc == '1') return F9 + (tc - '0');   // F9 and F10
            if(tc =='3' || tc == '4') return F11 + (tc - '3');  // F11 and F12
            if(tc =='5' || tc=='6') return F3 + 0x20 + tc-'5';                      // SHIFT-F3 and F4
            if(tc =='8' || tc=='9') return F5 + 0x20 + tc-'8';                      // SHIFT-F5 and F6
        }
        if(c == '3') {
            if(tc >='1' && tc <= '4') return F7 + 0x20 + (tc - '1');   // SHIFT-F7 to F10
        }
        //NB: SHIFT F1, F2, F11, and F12 don't appear to generate anything
    }
    // nothing worked so bomb out
    c1 = '['; c2 = c; c3 = tc; c4 = ttc;
    return 0x1b;
}
// get a line from the keyboard or a serial file handle
// filenbr == 0 means the console input
void MMgetline(int filenbr, char *p) {
	int c, nbrchars = 0;
	char *tp;

    while(1) {
        CheckAbort();	
        if(FileTable[filenbr].com > MAXCOMPORTS && FileEOF(filenbr)) break;
        c = MMfgetc(filenbr);
        if(c <= 0) continue;                                       // keep looping if there are no chars

        // if this is the console, check for a programmed function key and insert the text
        if(filenbr == 0) {
            tp = NULL;
            if(c == F2)  tp = "RUN";
            if(c == F3)  tp = "LIST";
            if(c == F4)  tp = "EDIT";
            if(c == F10) tp = "AUTOSAVE";
            if(c == F11) tp = "XMODEM RECEIVE";
            if(c == F12) tp = "XMODEM SEND";
            if(c == F1) tp = (char *)Option.F1key;
            if(c == F5) tp = (char *)Option.F5key;
            if(c == F6) tp = (char *)Option.F6key;
            if(c == F7) tp = (char *)Option.F7key;
            if(c == F8) tp = (char *)Option.F8key;
            if(c == F9) tp = (char *)Option.F9key;
            if(tp) {
                strcpy(p, tp);
                if(EchoOption) { MMPrintString(tp); MMPrintString("\r\n"); }
                return;
            }
        }

		if(c == '\t') {												// expand tabs to spaces
			 do {
				if(++nbrchars > MAXSTRLEN) error("Line is too long");
				*p++ = ' ';
				if(filenbr == 0 && EchoOption) MMputchar(' ',1);
			} while(nbrchars % 4);
			continue;
		}

		if(c == '\b') {												// handle the backspace
			if(nbrchars) {
				if(filenbr == 0 && EchoOption) MMPrintString("\b \b");
				nbrchars--;
				p--;
			}
			continue;
		}

        if(c == '\n') {                                             // what to do with a newline
                break;                                              // a newline terminates a line (for a file)
        }

        if(c == '\r') {
            if(filenbr == 0 && EchoOption) {
                MMPrintString("\r\n");
                break;                                              // on the console this means the end of the line - stop collecting
            } else
                continue ;                                          // for files loop around looking for the following newline
        }
        
		if(isprint(c)) {
			if(filenbr == 0 && EchoOption) MMputchar(c,1);           // The console requires that chars be echoed
		}
		if(++nbrchars > MAXSTRLEN) error("Line is too long");		// stop collecting if maximum length
		*p++ = c;													// save our char
	}
	*p = 0;
}
// insert a string into the start of the lastcmd buffer.
// the buffer is a sequence of strings separated by a zero byte.
// using the up arrow usere can call up the last few commands executed.
void MIPS16 InsertLastcmd(unsigned char *s) {
int i, slen;
    if(strcmp((const char *)lastcmd, (const char *)s) == 0) return;                             // don't duplicate
    slen = strlen((const char *)s);
    if(slen < 1 || slen > sizeof(lastcmd) - 1) return;
    slen++;
    for(i = sizeof(lastcmd) - 1; i >=  slen ; i--)
        lastcmd[i] = lastcmd[i - slen];                             // shift the contents of the buffer up
    strcpy((char *)lastcmd, (char *)s);                                             // and insert the new string in the beginning
    for(i = sizeof(lastcmd) - 1; lastcmd[i]; i--) lastcmd[i] = 0;             // zero the end of the buffer
}

void MIPS16 EditInputLine(void) {
    char *p = NULL;
    char buf[MAXKEYLEN + 3];
    char goend[10];
    int lastcmd_idx, lastcmd_edit;
    int insert, /*startline,*/ maxchars;
    int CharIndex, BufEdited;
    int c, i, j;
    int l4,l3,l2;
    maxchars=255; 
    if(Option.DISPLAY_CONSOLE && Option.Width<=SCREENWIDTH){     //We will always assume the Vt100 is 80 colums if LCD is the console <=80.
      l2=SCREENWIDTH+1-MMPromptPos;
      l3=2*SCREENWIDTH+2-MMPromptPos;
      l4=3*SCREENWIDTH+3-MMPromptPos;
    }else{                         // otherwise assume the VT100 matches Option.Width
      l2=Option.Width +1-MMPromptPos;
      l3=2*Option.Width+2-MMPromptPos;
      l4=3*Option.Width+3-MMPromptPos;
    }
    // Build "\e[80C" equivalent string for the line length
    //strcpy(goend,"\e[");IntToStr(linelen,l2+MMPromptPos, 10);strcat(goend,linelen); strcat(goend, "C");
     strcpy(goend,"\e[");IntToStr(&goend[strlen(goend)],l2+MMPromptPos, 10);strcat(goend, "C");
    
    MMPrintString((char *)inpbuf);                                                     // display the contents of the input buffer (if any)
    CharIndex = strlen((const char *)inpbuf);                                          // get the current cursor position in the line
    insert = false;
//    Cursor = C_STANDARD;
    lastcmd_edit = lastcmd_idx = 0;
    BufEdited = false; //(CharIndex != 0);
    while(1) {
        c = MMgetchar();
        if(c == TAB) {
            strcpy(buf, "        ");
            switch (Option.Tab) {
              case 2:
                buf[2 - (CharIndex % 2)] = 0; break;
              case 3:
                buf[3 - (CharIndex % 3)] = 0; break;
              case 4:
                buf[4 - (CharIndex % 4)] = 0; break;
              case 8:
                buf[8 - (CharIndex % 8)] = 0; break;
            }
        } else {
            buf[0] = c;
            buf[1] = 0;
        }
        do {
            switch(buf[0]) {
                case '\r':
                case '\n':  //if(autoOn && atoi(inpbuf) > 0) autoNext = atoi(inpbuf) + autoIncr;
                            //if(autoOn && !BufEdited) *inpbuf = 0;
                            goto saveline;
                            break;

                case '\b':
                            if(CharIndex > 0) {
                                BufEdited = true;
                                i = CharIndex - 1;
                                j= CharIndex;
                                for(p = (char *)inpbuf + i; *p; p++) *p = *(p + 1);                 // remove the char from inpbuf
 
                                // Lets put the cursor at the beginning of where the command is displayed.
                                // backspace to the beginning of line
#define USEBACKSPACE
#ifdef USEBACKSPACE                                
                                while(j)  {
                                  if (j==l4 || j==l3 ||j==l2 ){DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{ MMputchar('\b',0);}
                                  j--;
                                }
                                fflush(stdout);                                
                                 MX470Display(CLEAR_TO_EOS);SSPrintString("\033[0J");        //Clear to End Of Screen
#else
                                 CurrentX=0;CurrentY=CurrentY-((CharIndex+1)/Option.Width * gui_font_height);
                                 if (CharIndex>l4-1)SSPrintString("\e[3A");
                                 else if (CharIndex>l3-1)SSPrintString("\e[2A");
                                 else if(CharIndex>l2-1)SSPrintString("\e[1A");
                                 SSPrintString("\r");
                                 //CurrentX=0;SerUSBPutS("\r");
                                 MX470Display(CLEAR_TO_EOS);SSPrintString("\033[0J");
        				         MMPrintString("> ");
        				         fflush(stdout);


#endif

                                 j=0;
                                 while(j < strlen((const char *)inpbuf)) {
                                      MMputchar(inpbuf[j],0);
                                      if((j==l4-1 || j==l3-1 || j==l2-1 ) && j == strlen((const char *)inpbuf)-1 ){SSPrintString(" ");SSPrintString("\b");}
                                      if((j==l4-1 || j==l3-1 || j==l2-1 ) && j < strlen((const char *)inpbuf)-1 ){SerialConsolePutC(inpbuf[j+1],0);SSPrintString("\b");}
                                      j++;
                                 }
                                 fflush(stdout);

                                 // return the cursor to the right position
                                 for(j = strlen((const char *)inpbuf); j > i; j--){
                                   if (j==l4 || j==l3 || j==l2) {DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{MMputchar('\b',0);}
                                 }
                                 CharIndex--;
                                 fflush(stdout);
                                 if(strlen((const char *)inpbuf)==0)BufEdited = false;
                            }
                            break;

                case CTRLKEY('S'):
                case LEFT:

                	    BufEdited = true;
                	    insert=false; //left at first char will turn OVR on
                	    if(CharIndex > 0) {
                               // if(CharIndex == strlen((const char *)inpbuf)) {
                                    //insert = true;
                               // }
                                if (CharIndex==l4 || CharIndex==l3 || CharIndex==l2 ){DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{MMputchar('\b',1);}
                                insert=true; //Any left turns on INS
                                CharIndex--;
                         }
                     break;

                case CTRLKEY('D'):
                case RIGHT:

                	  if(CharIndex < strlen((const char *)inpbuf)) {
                	   	BufEdited = true;
                	    MMputchar(inpbuf[CharIndex],1);
                	    if((CharIndex==l4-1 || CharIndex==l3-1|| CharIndex==l2-1 ) && CharIndex == strlen((const char *)inpbuf)-1 ){SSPrintString(" ");SSPrintString("\b");}
                	    if((CharIndex==l4-1 || CharIndex==l3-1|| CharIndex==l2-1 ) && CharIndex < strlen((const char *)inpbuf)-1 ){SerialConsolePutC(inpbuf[CharIndex+1],0);SSPrintString("\b");}
                        CharIndex++;
                      }
//                      insert=false; //right always switches to OVER
                     break;
                case CTRLKEY(']'):
                case DEL:

                	      if(CharIndex < strlen((const char *)inpbuf)) {
                	           BufEdited = true;
                	           i = CharIndex;

                	           for(p = (char *)inpbuf + i; *p; p++) *p = *(p + 1);                 // remove the char from inpbuf
                	           j = strlen((const char *)inpbuf);
                	           // Lets put the cursor at the beginning of where the command is displayed.
                               // backspace to the beginning of line
                	           j=CharIndex;
                               while(j)  {
                                  if (j==l4 || j==l3 ||j==l2 ){DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{ MMputchar('\b',0);}
                                  j--;
                               }
                               fflush(stdout);
                               MX470Display(CLEAR_TO_EOS);SSPrintString("\033[0J");        //Clear to End Of Screen
                               j=0;
                               while(j < strlen((const char *)inpbuf)) {
                                    MMputchar(inpbuf[j],0);
                                    if((j==l4-1 || j==l3-1 || j==l2-1 ) && j == strlen((const char *)inpbuf)-1 ){SSPrintString(" ");SSPrintString("\b");}
                                    if((j==l4-1 || j==l3-1 || j==l2-1 ) && j < strlen((const char *)inpbuf)-1 ){SerialConsolePutC(inpbuf[j+1],0);SSPrintString("\b");}
                                    j++;
                               }
                               fflush(stdout);
                               // return the cursor to the right position
                               for(j = strlen((const char *)inpbuf); j > i; j--){
                                 if (j==l4 || j==l3 || j==l2) {DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{ MMputchar('\b',0);}
                               }
                               fflush(stdout);
                           }
                	       break;


                case CTRLKEY('N'):
                case INSERT:insert = !insert;
//                            Cursor = C_STANDARD + insert;
                            break;

                case CTRLKEY('U'):
                case HOME:  
                           BufEdited = true;
                           if(CharIndex > 0) {
                                if(CharIndex == strlen((const char *)inpbuf)) {
                                    insert = true;
//                                    Cursor = C_INSERT;
                                }
                                // backspace to the beginning of line
                                while(CharIndex)  {
                                 	 if (CharIndex==l4 || CharIndex==l3 || CharIndex==l2 ){DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{MMputchar('\b',0);}
                                   	 CharIndex--;
                                }
                                fflush(stdout);
                            }else{ //HOME @ home turns off edit mode
                            	BufEdited = false;
                                insert=false; //home at first char will turn OVR on
                            }
                            break;

                case CTRLKEY('K'):
                case END:   
                            BufEdited = true;
                            while(CharIndex < strlen((const char *)inpbuf)){
                                MMputchar(inpbuf[CharIndex++],0);
                            }   
                            fflush(stdout);
                            break;

/*            if(c == F2)  tp = "RUN";
            if(c == F3)  tp = "LIST";
            if(c == F4)  tp = "EDIT";
            if(c == F10) tp = "AUTOSAVE";
            if(c == F11) tp = "XMODEM RECEIVE";
            if(c == F12) tp = "XMODEM SEND";
            if(c == F5) tp = Option.F5key;
            if(c == F6) tp = Option.F6key;
            if(c == F7) tp = Option.F7key;
            if(c == F8) tp = Option.F8key;
            if(c == F9) tp = Option.F9key;
*/
                case 0x91:
                    if(*Option.F1key)strcpy(&buf[1],(char *)Option.F1key);
                    break;
                case 0x92:
                    strcpy(&buf[1],"RUN\r\n");
                    break;
                case 0x93:
                    strcpy(&buf[1],"LIST\r\n");
                    break;
                case 0x94:
                    strcpy(&buf[1],"EDIT\r\n");
                    break;
                case 0x95:
                    if(*Option.F5key){
                        strcpy(&buf[1],(char *)Option.F5key);
                    }else{
                         /*** F5 will clear the VT100  ***/
            	         SSPrintString("\e[2J\e[H");
            	         fflush(stdout);
                         if(Option.DISPLAY_CONSOLE){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
                         if(FindSubFun((unsigned char *)"MM.PROMPT", 0) >= 0) {
                            ExecuteProgram((unsigned char *)"MM.PROMPT\0");
                         } else{
                             MMPrintString("> ");                                    // print the prompt
                         }                           
            	         //MMPrintString("> ");
            	         fflush(stdout);
                    }    
                    break;
                case 0x96:
                    if(*Option.F6key)strcpy(&buf[1],(char *)Option.F6key);
                    break;
                case 0x97:
                    if(*Option.F7key)strcpy(&buf[1],(char *)Option.F7key);
                    break;
                case 0x98:
                    if(*Option.F8key)strcpy(&buf[1],(char *)Option.F8key);
                    break;
                case 0x99:
                    if(*Option.F9key)strcpy(&buf[1],(char *)Option.F9key);
                    break;
                case 0x9a:
                    strcpy(&buf[1],"AUTOSAVE\r\n");
                    break;
                case 0x9b:
                    strcpy(&buf[1],"XMODEM RECEIVE\r\n");
                    break;
                 case 0x9c:
                    strcpy(&buf[1],"XMODEM SEND\r\n");
                    break;
                case CTRLKEY('E'):
                case UP:    if(!(BufEdited /*|| autoOn || CurrentLineNbr */)) {
                              
                                if(lastcmd_edit) {
                                    i = lastcmd_idx + strlen((const char *)&lastcmd[lastcmd_idx]) + 1;    // find the next command
                                    if(lastcmd[i] != 0 && i < sizeof(lastcmd) - 1) lastcmd_idx = i;  // and point to it for the next time around
                                } else
                                    lastcmd_edit = true;
                                strcpy((char *)inpbuf, (const char *)&lastcmd[lastcmd_idx]);                      // get the command into the buffer for editing
                                goto insert_lastcmd;
                            }
                            break;

                case CTRLKEY('X'):
                case DOWN:  
                           if(!(BufEdited /*|| autoOn || CurrentLineNbr */)) {
                               if(lastcmd_idx == 0)
                                    *inpbuf = lastcmd_edit = 0;
                                else {
                                    for(i = lastcmd_idx - 2; i > 0 && lastcmd[i - 1] != 0; i--);// find the start of the previous command
                                    lastcmd_idx = i;                                        // and point to it for the next time around
                                    strcpy((char *)inpbuf, (const char *)&lastcmd[i]);                            // get the command into the buffer for editing
                                }
                                goto insert_lastcmd;                                        // gotos are bad, I know, I know
                            }
                            break;

                insert_lastcmd: 

                            // If NoScroll and its near the bottom then clear screen and write command at top
                            //if(Option.NoScroll && Option.DISPLAY_CONSOLE && (CurrentY + 2*gui_font_height >= VRes)){
                            if(Option.NoScroll && Option.DISPLAY_CONSOLE && (CurrentY + (2 + strlen((const char *)inpbuf)/Option.Width)*gui_font_height >= VRes)){    
                                      ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;
                                      if(FindSubFun((unsigned char *)"MM.PROMPT", 0) >= 0) {
                                         SSPrintString("\r");
                                         ExecuteProgram((unsigned char *)"MM.PROMPT\0");
                                      } else{
                                         SSPrintString("\r");
                                         MMPrintString("> ");                           // print the prompt
                                      }    
                          
                            }else{
			                   // Lets put the cursor at the beginning of where the command is displayed.
                               // backspace to the beginning of line
                                j=CharIndex;  //????????????????????????????????
                                while(j)  {
                                  if (j==l4 || j==l3 ||j==l2 ){DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{ MMputchar('\b',0);}
                                  j--;
                                }
                                fflush(stdout);
                                MX470Display(CLEAR_TO_EOS);SSPrintString("\033[0J");        //Clear to End Of Screen
                            }

				            CharIndex = strlen((const char *)inpbuf);
                            MMPrintString((char *)inpbuf);                                          // display the line
                            if(CharIndex==l4 || CharIndex==l3 || CharIndex==l2){SSPrintString(" ");SSPrintString("\b");}
                            fflush(stdout);
                            CharIndex = strlen((const char *)inpbuf);                                     // get the current cursor position in the line
                            break;
                            

 
 
                default:    if(buf[0] >= ' ' && buf[0] < 0x7f) {
                               // BufEdited = true;  
                               
                                i = CharIndex;
                                j = strlen((const char *)inpbuf);
                                if(insert) {
                                    if(strlen((const char *)inpbuf) >= maxchars - 1) break;               // sorry, line full
                                    for(p = (char *)inpbuf + strlen((const char *)inpbuf); j >= CharIndex; p--, j--) *(p + 1) = *p;
                                    inpbuf[CharIndex] = buf[0];                             // insert the char
                                    MMPrintString((char *)&inpbuf[CharIndex]);                      // display new part of the line
                                    CharIndex++;
                                   // return the cursor to the right position
                                    for(j = strlen((const char *)inpbuf); j > CharIndex; j--){
                                      if (j==l4 || j==l3 || j==l2){DisplayPutC('\b');SSPrintString("\e[1A");SSPrintString(goend);}else{ MMputchar('\b',0);}
                                    }
                                    fflush(stdout);  
                                } else {
                                    if(strlen((const char *)inpbuf) >= maxchars-1 ) break;               // sorry, line full  just ignore
                                    inpbuf[strlen((const char *)inpbuf) + 1] = 0;                         // incase we are adding to the end of the string
                                    inpbuf[CharIndex++] = buf[0];                           // overwrite the char
                                    MMputchar(buf[0],0);  
                                    if(j==l4-1 || j==l3-1 || j==l2-1){SSPrintString(" ");SSPrintString("\b");}
                                    fflush(stdout);
                                  
                                }
#ifndef PICOMITEVGA                                                                     
                                i = CharIndex;
                                j = strlen((const char *)inpbuf);
                                // If its going to scroll then clear screen
                                if(Option.NoScroll && Option.DISPLAY_CONSOLE){
                                   if(CurrentY + 2*gui_font_height >= VRes) {
                                      ClearScreen(gui_bcolour);/*CurrentX=0*/;CurrentY=0;
                                      CurrentX = (MMPromptPos-2)*gui_font_width  ;          
                                      //if(FindSubFun((unsigned char *)"MM.PROMPT", 0) >= 0) {
                                      //   ExecuteProgram((unsigned char *)"MM.PROMPT\0");
                                      //} else{
                                         //SSPrintString("\r");
                                         //MMPrintString("> ");                           // print the prompt
                                         DisplayPutC('>');
                                         DisplayPutC(' ');
                                      //}    
                                      DisplayPutS((char *)inpbuf);                      // display the line
                                      
                                    }
                                }
#endif                                
 
                            }
                            break;
            }
            for(i = 0; i < MAXKEYLEN + 1; i++) buf[i] = buf[i + 1];                             // shuffle down the buffer to get the next char
        } while(*buf);
        if(CharIndex == strlen((const char *)inpbuf)) {
          insert = false;
//        Cursor = C_STANDARD;
        }
    }
    
    saveline:
//    Cursor = C_STANDARD;
   
   if(strlen((const char *)inpbuf) < maxchars)InsertLastcmd(inpbuf);
   MMPrintString("\r\n");
}

#ifdef OLDSTUFF
void MIPS16 EditInputLine(void) {
    char *p = NULL;
    char buf[MAXKEYLEN + 3];
    int lastcmd_idx, lastcmd_edit;
    int insert, startline, maxchars;
    int CharIndex, BufEdited;
    int c, i, j;
    maxchars = Option.Width;
    if(strlen((const char *)inpbuf) >= maxchars) {
        MMPrintString((char *)inpbuf);
        error("Line is too long to edit");
    }
    startline = MMCharPos - 1;                                                          // save the current cursor position
    MMPrintString((char *)inpbuf);                                                              // display the contents of the input buffer (if any)
    CharIndex = strlen((const char *)inpbuf);                                                         // get the current cursor position in the line
    insert = false;
//    Cursor = C_STANDARD;
    lastcmd_edit = lastcmd_idx = 0;
    BufEdited = false; //(CharIndex != 0);
    while(1) {
        c = MMgetchar();
        if(c == TAB) {
            strcpy(buf, "        ");
            switch (Option.Tab) {
              case 2:
                buf[2 - (CharIndex % 2)] = 0; break;
              case 3:
                buf[3 - (CharIndex % 3)] = 0; break;
              case 4:
                buf[4 - (CharIndex % 4)] = 0; break;
              case 8:
                buf[8 - (CharIndex % 8)] = 0; break;
            }
        } else {
            buf[0] = c;
            buf[1] = 0;
        }
        do {
            switch(buf[0]) {
                case '\r':
                case '\n':  //if(autoOn && atoi(inpbuf) > 0) autoNext = atoi(inpbuf) + autoIncr;
                            //if(autoOn && !BufEdited) *inpbuf = 0;
                            goto saveline;
                            break;

                case '\b':  if(CharIndex > 0) {
                                BufEdited = true;
                                i = CharIndex - 1;
                                for(p = (char *)inpbuf + i; *p; p++) *p = *(p + 1);                 // remove the char from inpbuf
                                while(CharIndex)  { MMputchar('\b',0); CharIndex--; }         // go to the beginning of the line
                                MMPrintString((char *)inpbuf); MMputchar(' ',0); MMputchar('\b',0);     // display the line and erase the last char
                                for(CharIndex = strlen((const char *)inpbuf); CharIndex > i; CharIndex--)
                                    MMputchar('\b',0);  
                                fflush(stdout);                                      // return the cursor to the righ position
                            }
                            break;

                case CTRLKEY('S'):
                case LEFT:  if(CharIndex > 0) {
                                if(CharIndex == strlen((const char *)inpbuf)) {
                                    insert = true;
      //                              Cursor = C_INSERT;
                                }
                                MMputchar('\b',1);
                                CharIndex--;
                            }
                            break; 

                case CTRLKEY('D'):
                case RIGHT: if(CharIndex < strlen((const char *)inpbuf)) {
                                MMputchar(inpbuf[CharIndex],1);
                                CharIndex++;
                            }
                            break;

                case CTRLKEY(']'):
                case DEL:   if(CharIndex < strlen((const char *)inpbuf)) {
                                BufEdited = true;
                                i = CharIndex;
                                for(p = (char *)inpbuf + i; *p; p++) *p = *(p + 1);                 // remove the char from inpbuf
                                while(CharIndex)  { MMputchar('\b',0); CharIndex--; }         // go to the beginning of the line
                                MMPrintString((char *)inpbuf); MMputchar(' ',0); MMputchar('\b',0);     // display the line and erase the last char
                                for(CharIndex = strlen((const char *)inpbuf); CharIndex > i; CharIndex--)
                                    MMputchar('\b',0);   
                                fflush(stdout);                                     // return the cursor to the right position
                            }
                            break;

                case CTRLKEY('N'):
                case INSERT:insert = !insert;
//                            Cursor = C_STANDARD + insert;
                            break;

                case CTRLKEY('U'):
                case HOME:  if(CharIndex > 0) {
                                if(CharIndex == strlen((const char *)inpbuf)) {
                                    insert = true;
//                                    Cursor = C_INSERT;
                                }
                                while(CharIndex)  { MMputchar('\b',0); CharIndex--; }
                                fflush(stdout);
                            }
                            break;

                case CTRLKEY('K'):
                case END:   while(CharIndex < strlen((const char *)inpbuf)){
                                MMputchar(inpbuf[CharIndex++],0);
                            }   
                            fflush(stdout);
                            break;

/*            if(c == F2)  tp = "RUN";
            if(c == F3)  tp = "LIST";
            if(c == F4)  tp = "EDIT";
            if(c == F10) tp = "AUTOSAVE";
            if(c == F11) tp = "XMODEM RECEIVE";
            if(c == F12) tp = "XMODEM SEND";
            if(c == F5) tp = Option.F5key;
            if(c == F6) tp = Option.F6key;
            if(c == F7) tp = Option.F7key;
            if(c == F8) tp = Option.F8key;
            if(c == F9) tp = Option.F9key;*/
                case 0x91:
                    if(*Option.F1key)strcpy(&buf[1],(char *)Option.F1key);
                    break;
                case 0x92:
                    strcpy(&buf[1],"RUN\r\n");
                    break;
                case 0x93:
                    strcpy(&buf[1],"LIST\r\n");
                    break;
                case 0x94:
                    strcpy(&buf[1],"EDIT\r\n");
                    break;
                case 0x95:
                    if(*Option.F5key){
                        strcpy(&buf[1],(char *)Option.F5key);
                    }else{
                         /*** F5 will clear the VT100  ***/
            	         SSPrintString("\e[2J\e[H");
            	         fflush(stdout);
                         if(Option.DISPLAY_CONSOLE){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
            	         MMPrintString("> ");
            	         fflush(stdout);
                    }    
                    break;
                case 0x96:
                    if(*Option.F6key)strcpy(&buf[1],(char *)Option.F6key);
                    break;
                case 0x97:
                    if(*Option.F7key)strcpy(&buf[1],(char *)Option.F7key);
                    break;
                case 0x98:
                    if(*Option.F8key)strcpy(&buf[1],(char *)Option.F8key);
                    break;
                case 0x99:
                    if(*Option.F9key)strcpy(&buf[1],(char *)Option.F9key);
                    break;
                case 0x9a:
                    strcpy(&buf[1],"AUTOSAVE\r\n");
                    break;
                case 0x9b:
                    strcpy(&buf[1],"XMODEM RECEIVE\r\n");
                    break;
                 case 0x9c:
                    strcpy(&buf[1],"XMODEM SEND\r\n");
                    break;
                case CTRLKEY('E'):
                case UP:    if(!(BufEdited /*|| autoOn || CurrentLineNbr */)) {
                                while(CharIndex)  { MMputchar('\b',0); CharIndex--; } 
                                fflush(stdout);       // go to the beginning of line
                                if(lastcmd_edit) {
                                    i = lastcmd_idx + strlen((const char *)&lastcmd[lastcmd_idx]) + 1;    // find the next command
                                    if(lastcmd[i] != 0 && i < sizeof(lastcmd) - 1) lastcmd_idx = i;  // and point to it for the next time around
                                } else
                                    lastcmd_edit = true;
                                strcpy((char *)inpbuf, (const char *)&lastcmd[lastcmd_idx]);                      // get the command into the buffer for editing
                                goto insert_lastcmd;
                            }
                            break;


                case CTRLKEY('X'):
                case DOWN:  if(!(BufEdited /*|| autoOn || CurrentLineNbr */)) {
                                while(CharIndex)  { MMputchar('\b',0); CharIndex--; }   
                                fflush(stdout);      // go to the beginning of line
                                if(lastcmd_idx == 0)
                                    *inpbuf = lastcmd_edit = 0;
                                else {
                                    for(i = lastcmd_idx - 2; i > 0 && lastcmd[i - 1] != 0; i--);// find the start of the previous command
                                    lastcmd_idx = i;                                        // and point to it for the next time around
                                    strcpy((char *)inpbuf, (const char *)&lastcmd[i]);                            // get the command into the buffer for editing
                                }
                                goto insert_lastcmd;                                        // gotos are bad, I know, I know
                            }
                            break;

                insert_lastcmd:                                                             // goto here if we are just editing a command
                            if(strlen((const char *)inpbuf) + startline >= maxchars) {                    // if the line is too long
                                while(CharIndex)  { MMputchar('\b',0); CharIndex--; }         // go to the start of the line
                                MMPrintString((char *)inpbuf);                                      // display the offending line
                                error("Line is too long to edit");
                            }
                            MMPrintString((char *)inpbuf);                                          // display the line
                            CharIndex = strlen((const char *)inpbuf);                                     // get the current cursor position in the line
                            for(i = 1; i <= maxchars - strlen((const char *)inpbuf) - startline; i++) {
                                MMputchar(' ',0);                                             // erase the rest of the line
                                CharIndex++;
                            }
                            while(CharIndex > strlen((const char *)inpbuf)) { MMputchar('\b',0); CharIndex--; } // return the cursor to the right position

                            SSPrintString("\033[0K");
                            break;

                default:    if(buf[0] >= ' ' && buf[0] < 0x7f) {
                                BufEdited = true;                                           // this means that something was typed
                                i = CharIndex;
                                j = strlen((const char *)inpbuf);
                                if(insert) {
                                    if(strlen((const char *)inpbuf) >= maxchars - 1) break;               // sorry, line full
                                    for(p = (char *)inpbuf + strlen((const char *)inpbuf); j >= CharIndex; p--, j--) *(p + 1) = *p;
                                    inpbuf[CharIndex] = buf[0];                             // insert the char
                                    MMPrintString((char *)&inpbuf[CharIndex]);                      // display new part of the line
                                    CharIndex++;
                                    for(j = strlen((const char *)inpbuf); j > CharIndex; j--) MMputchar('\b',0); 
                                        fflush(stdout);                                   // return the cursor to the right position
                                } else {
                                    inpbuf[strlen((const char *)inpbuf) + 1] = 0;                         // incase we are adding to the end of the string
                                    inpbuf[CharIndex++] = buf[0];                           // overwrite the char
                                    MMputchar(buf[0],1);                                      // display it
                                    if(CharIndex + startline >= maxchars) {                 // has the input gone beyond the end of the line?
                                        MMgetline(0, (char *)inpbuf);                               // use the old fashioned way of getting the line
                                        //if(autoOn && atoi(inpbuf) > 0) autoNext = atoi(inpbuf) + autoIncr;
                                        goto saveline;
                                    }
                                }
                            }
                            break;
            }
            for(i = 0; i < MAXKEYLEN + 1; i++) buf[i] = buf[i + 1];                             // shuffle down the buffer to get the next char
        } while(*buf);
    if(CharIndex == strlen((const char *)inpbuf)) {
        insert = false;
//        Cursor = C_STANDARD;
        }
    }

    saveline:
//    Cursor = C_STANDARD;
    MMPrintString("\r\n");
}
#endif

// get a keystroke.  Will wait forever for input
// if the unsigned char is a cr then replace it with a newline (lf)
int MMgetchar(void) {
	int c;
	do {
		ShowCursor(1);
		c=MMInkey();
	} while(c == -1);
	ShowCursor(0);
	return c;
}
// print a string to the console interfaces
void MMPrintString(char* s) {
    while(*s) {
        if(s[1])MMputchar(*s,0);
        else MMputchar(*s,1);
        s++;
    }
    fflush(stdout);
}
void SSPrintString(char* s) {
    while(*s) {
        SerialConsolePutC(*s,0);
        s++;
    }
    fflush(stdout);
}

/*void myprintf(char *s){
   fputs(s,stdout);
     fflush(stdout);
}*/

void __not_in_flash_func(mT4IntEnable)(int status){
	if(status){
		processtick=true;
	} else{
		processtick=false;
	}
}

volatile int onoff=0;
bool MIPS16 __not_in_flash_func(timer_callback)(repeating_timer_t *rt)
{
    mSecTimer++;                                                      // used by the TIMER function
    if(processtick){
        static int IrTimeout, IrTick, NextIrTick;
        int ElapsedMicroSec, IrDevTmp, IrCmdTmp;
#ifdef rp2350
    if(ExtCurrentConfig[FAST_TIMER_PIN] == EXT_FAST_TIMER && --INT5Timer <= 0) { 
        static uint64_t now,last=0;
        uint32_t hi = INT5Count;
        uint32_t lo;
        do {
            // Read the lower 32 bits
            lo = pwm_get_counter(0);
            // Now read the upper 32 bits again and
            // check that it hasn't incremented. If it has loop around
            // and read the lower 32 bits again to get an accurate value
            uint32_t next_hi = INT5Count;
            if (hi == next_hi) break;
            hi = next_hi;
        } while (true);
        now=((uint64_t) hi *50000) + lo;
        INT5Value=now-last;
        last=now;
        INT5Timer = INT5InitTimer; 
    }
#endif
        AHRSTimer++;
        InkeyTimer++;                                                     // used to delay on an escape character
        PauseTimer++;													// used by the PAUSE command
        IntPauseTimer++;												// used by the PAUSE command inside an interrupt
        ds18b20Timer++;
		GPSTimer++;
        I2CTimer++;
#ifdef USBKEYBOARD
		keytimer++;
        for(int i=0;i<4;i++){
            if(HID[i].Device_type){ 
                HID[i].report_timer++;
            }
        }
#else
		nunstruct[2].type++;
        MouseTimer++;
#endif
        if(clocktimer)clocktimer--;
        if(Timer5)Timer5--;
        if(Timer4)Timer4--;
        if(Timer3)Timer3--;
        if(Timer2)Timer2--;
        if(Timer1)Timer1--;
        if(KeyCheck)KeyCheck--;
        ClassicTimer++;
        NunchuckTimer++;
        if(diskchecktimer && (Option.SD_CS || Option.CombinedCS))diskchecktimer--;
	    if(++CursorTimer > CURSOR_OFF + CURSOR_ON) CursorTimer = 0;		// used to control cursor blink rate
        if(CFuncmSec) CallCFuncmSec();                                  // the 1mS tick for CFunctions (see CFunction.c)
        if(InterruptUsed) {
            int i;
            for(i = 0; i < NBRSETTICKS; i++) if(TickActive[i])TickTimer[i]++;			// used in the interrupt tick
         }
    if(WDTimer) {
        if(--WDTimer == 0) {
            _excep_code = WATCHDOG_TIMEOUT;
            watchdog_enable(1, 1);
            while(1);
        }
    }
        if (ScrewUpTimer) {
            if (--ScrewUpTimer == 0) {
                _excep_code = SCREWUP_TIMEOUT;
                watchdog_enable(1, 1);
                while(1);
            }
        }
        if(PulseActive) {
            int i;
            for(PulseActive = i = 0; i < NBR_PULSE_SLOTS; i++) {
                if(PulseCnt[i] > 0) {                                   // if the pulse timer is running
                    PulseCnt[i]--;                                      // and decrement our count
                    if(PulseCnt[i] == 0)                                // if this is the last count reset the pulse
                        PinSetBit(PulsePin[i], LATINV);
                    else
                        PulseActive = true;                             // there is at least one pulse still active
                }
            }
        }
        ElapsedMicroSec = readIRclock();
        if(IrState > IR_WAIT_START && ElapsedMicroSec > 15000) IrReset();
        IrCmdTmp = -1;
        
        // check for any Sony IR receive activity
        if(IrState == SONY_WAIT_BIT_START && ElapsedMicroSec > 2800 && (IrCount == 12 || IrCount == 15 || IrCount == 20)) {
            IrDevTmp = ((IrBits >> 7) & 0b11111);
            IrCmdTmp = (IrBits & 0b1111111) | ((IrBits >> 5) & ~0b1111111);
        }
        
        // check for any NEC IR receive activity
        if(IrState == NEC_WAIT_BIT_END && IrCount == 32) {
            // check if it is a NON extended address and adjust if it is
            if((IrBits >> 24) == ~((IrBits >> 16) & 0xff)) IrBits = (IrBits & 0x0000ffff) | ((IrBits >> 8) & 0x00ff0000);
            IrDevTmp = ((IrBits >> 16) & 0xffff);
            IrCmdTmp = ((IrBits >> 8) & 0xff);
        }
#ifdef GUICONTROLS
    // check on the touch panel, is the pen down?

    TouchTimer++;
    if(CheckGuiFlag) CheckGuiTimeouts();                            // are blinking LEDs in use?  If so count down their timers

    if(TOUCH_GETIRQTRIS){                       // is touch enabled and the PEN IRQ pin an input?
        if(TOUCH_DOWN) {                                            // is the pen down
            if(!TouchState) {                                       // yes, it is.  If we have not reported this before
                TouchState = TouchDown = true;                      // set the flags
//                TouchUp = false;
            }
        } else {
            if(TouchState) {                                        // the pen is not down.  If we have not reported this before
                TouchState/* = TouchDown*/ = false;                     // set the flags
                TouchUp = true;
            }
        }
    }

    if(ClickTimer) {
        ClickTimer--;
        if(Option.TOUCH_Click) PinSetBit(Option.TOUCH_Click, ClickTimer ? LATSET : LATCLR);
    }
#endif
    // now process the IR message, this includes handling auto repeat while the key is held down
    // IrTick counts how many mS since the key was first pressed
    // NextIrTick is used to time the auto repeat
    // IrTimeout is used to detect when the key is released
    // IrGotMsg is a signal to the interrupt handler that an interrupt is required
    if(IrCmdTmp != -1) {
        if(IrTick > IrTimeout) {
            // this is a new keypress
            IrTick = 0;
            NextIrTick = 650;
        }
        if(IrTick == 0 || IrTick > NextIrTick) {
            if(IrVarType & 0b01)
                *(MMFLOAT *)IrDev = IrDevTmp;
            else
                *(long long int *)IrDev = IrDevTmp;
            if(IrVarType & 0b10)
                *(MMFLOAT *)IrCmd = IrCmdTmp;
            else
                *(long long int *)IrCmd = IrCmdTmp;
            IrGotMsg = true;
            NextIrTick += 250;
        }
        IrTimeout = IrTick + 150;
        IrReset();
    }
    IrTick++;
	if(ExtCurrentConfig[Option.INT1pin] == EXT_PER_IN) INT1Count++;
	if(ExtCurrentConfig[Option.INT2pin] == EXT_PER_IN) INT2Count++;
	if(ExtCurrentConfig[Option.INT3pin] == EXT_PER_IN) INT3Count++;
	if(ExtCurrentConfig[Option.INT4pin] == EXT_PER_IN) INT4Count++;
    if(ExtCurrentConfig[Option.INT1pin] == EXT_FREQ_IN && --INT1Timer <= 0) { INT1Value = INT1Count; INT1Count = 0; INT1Timer = INT1InitTimer; }
    if(ExtCurrentConfig[Option.INT2pin] == EXT_FREQ_IN && --INT2Timer <= 0) { INT2Value = INT2Count; INT2Count = 0; INT2Timer = INT2InitTimer; }
    if(ExtCurrentConfig[Option.INT3pin] == EXT_FREQ_IN && --INT3Timer <= 0) { INT3Value = INT3Count; INT3Count = 0; INT3Timer = INT3InitTimer; }
    if(ExtCurrentConfig[Option.INT4pin] == EXT_FREQ_IN && --INT4Timer <= 0) { INT4Value = INT4Count; INT4Count = 0; INT4Timer = INT4InitTimer; }

    ////////////////////////////////// this code runs once a second /////////////////////////////////
    if(++SecondsTimer >= 1000) {
        SecondsTimer -= 1000; 
    #ifndef PICOMITEWEB
        if(ExtCurrentConfig[PinDef[HEARTBEATpin].pin]==EXT_HEARTBEAT)gpio_xor_mask64(1<<PinDef[HEARTBEATpin].GPno);
    #endif
            // keep track of the time and date
/*        if(++second >= 60) {
            second = 0 ;
            if(++minute >= 60) {
                minute = 0;
                if(++hour >= 24) {
                    hour = 0;
                    if(++day > DaysInMonth[month] + ((month == 2 && (year % 4) == 0)?1:0)) {
                        day = 1;
                        if(++month > 12) {
                            month = 1;
                            year++;
                        }
                    }
                }
            }
        }*/
        }
    }
  return 1;
}
void __not_in_flash_func(uSec)(int us) {
#ifdef PICOMITEWEB
	if(us<500){
		busy_wait_us(us);
	} else {
    	uint64_t end=time_us_64()+us;
    	while(time_us_64()<end){
        if(time_us_64() % 500 ==0)ProcessWeb(1);
    }
}
#else
	busy_wait_us(us);
#endif
}
#ifdef PICOMITEWEB
void __not_in_flash_func(ProcessWeb)(int mode){
    static uint64_t flushtimer=0;
    static uint64_t lastusec=0;
    static int testcount=0;  
    static int lastonoff=0;
    static uint64_t lastheartmsec=0;
    uint64_t timenow=time_us_64();   
    if(!WIFIconnected && startupcomplete)goto flashonly;
    TCP_SERVER_T *state = (TCP_SERVER_T*)TCPstate;
    if(!state)return;
    int t=0;
    for(int i=0;i<MaxPcb;i++){
        if(state->client_pcb[i]==NULL){
                t++;
        } else if(state->client_pcb[i]==(struct tcp_pcb *)44){
            if(timenow-state->pcbopentime[i] > 1000*(uint32_t)Option.ServerResponceTime + 20000000 && !state->keepalive[i]){
                state->client_pcb[i]=NULL;
//                    printf("PCB %d should be closed by now\r\n", i);
            }
        } else {
            if(timenow-state->pcbopentime[i] > 1000*(uint32_t)Option.ServerResponceTime && !state->keepalive[i]){
//                    printf("Warning PCB %d still open\r\n", i);
                    if(state->buffer_recv[i]){
                            tcp_server_close(state,i);
                            error("No response to request from connection no. %",i+1);
//                            printf("Warning: No response to request from connection no. %d\r\n",i+1);
                    }
                    tcp_server_close(state,i);
                    state->client_pcb[i]=(struct tcp_pcb *)44;
            }
        }
    }
    if(testcount == 0 || timenow>lastusec){
        lastusec=timenow+1000;
        testcount = 0 ;
        if(startupcomplete)cyw43_arch_poll();
    }
    testcount++;
    if(testcount==100)testcount=0;
    if(!mode)return;
    if(state->telnet_pcb_no!=99){
        if(timenow > flushtimer){
            TelnetPutC(0,-1);
            flushtimer=timenow+5000;
        }
    }
    flashonly:;
    if(Option.NoHeartbeat){
        if(lastonoff!=2){
            if(startupcomplete){
                if(cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN)) cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
                lastonoff=2;
            }
        }
    } else {
        if(lastonoff==2)lastonoff=0;
        if(timenow-lastheartmsec>(WIFIconnected ? 500000:1000000) && startupcomplete){
            lastheartmsec=timenow;
            if(lastonoff)cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
            else cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
            lastonoff^=1;
        }
    }
}
#endif
void __not_in_flash_func(CheckAbort)(void) {
#ifdef PICOMITEWEB
    ProcessWeb(1);
#endif
    routinechecks();
    if(MMAbort) {
        WDTimer = 0;                                                // turn off the watchdog timer
        calibrate=0;
        ShowCursor(false);
#ifdef PICOMITE
        if(mergerunning){
            multicore_fifo_push_blocking(0xFF);
            busy_wait_ms(mergetimer+200);
            if(mergerunning){
                _excep_code = RESET_COMMAND;
                SoftReset();
            }
        }
#endif
        do_end(false);
        longjmp(mark, 1);												// jump back to the input prompt
}
}
void PRet(void){
    MMPrintString("\r\n");
}
void SRet(void){
    SSPrintString("\r\n");
}

void PInt(int64_t n) {
    char s[20];
    IntToStr(s, (int64_t)n, 10);
    MMPrintString(s);
}
void SInt(int64_t n) {
    char s[20];
    IntToStr(s, (int64_t)n, 10);
    SSPrintString(s);
}

void SIntComma(int64_t n) {
    SSPrintString(", "); SInt(n);
}

void PIntComma(int64_t n) {
    MMPrintString(", "); PInt(n);
}

void PIntH(unsigned long long int n) {
    char s[20];
    IntToStr(s, (int64_t)n, 16);
    MMPrintString(s);
}
void PIntB(unsigned long long int n) {
    char s[65];
    IntToStr(s, (int64_t)n, 2);
    MMPrintString(s);
}
void PIntHC(unsigned long long int n) {
    MMPrintString(", "); PIntH(n);
}
void PIntBC(unsigned long long int n) {
    MMPrintString(", "); PIntB(n);
}

void PFlt(MMFLOAT flt){
	   char s[20];
	   FloatToStr(s, flt, 4,4, ' ');
	    MMPrintString(s);
}
void PFltComma(MMFLOAT n) {
    MMPrintString(", "); PFlt(n);
}
void sigbus(void){
    MMPrintString("Error: Invalid address - resetting\r\n");
	uSec(250000);
	disable_interrupts_pico();
//	flash_range_erase(PROGSTART, MAX_PROG_SIZE);
    LoadOptions();
    if(Option.NoReset==0){
        Option.Autorun=0;
        SaveOptions();
    }
	enable_interrupts_pico();
    memset(inpbuf,0,STRINGSIZE);
    SoftReset();
}

#ifdef PICOMITEVGA
int vgaloop1,vgaloop2, vgaloop4, vgaloop8, vgaloop16, vgaloop32;

#ifndef HDMI
// ****************************************************************************
//
//                                  QVGA
//
// ****************************************************************************
// VGA resolution:
// - 640x480 pixels
// - vertical frequency 60 Hz
// - horizontal frequency 31.4685 kHz
// - pixel clock 25.175 MHz
//
// QVGA resolution:
// - 320x240 pixels
// - vertical double image scanlines
// - vertical frequency 60 Hz
// - horizontal frequency 31.4685 kHz
// - pixel clock 12.5875 MHz
//
// VGA vertical timings:
// - 525 scanlines total
// - line 1,2: (2) vertical sync
// - line 3..35: (33) dark
// - line 36..515: (480) image lines 0..479
// - line 516..525: (10) dark
//
// VGA horizontal timings:
// - 31.77781 total scanline in [us] (800 pixels, QVGA 400 pixels)
// - 0.63556 H front porch (after image, before HSYNC) in [us] (16 pixels, QVGA 8 pixels)
// - 3.81334 H sync pulse in [us] (96 pixels, QVGA 48 pixels)
// - 1.90667 H back porch (after HSYNC, before image) in [us] (48 pixels, QVGA 24 pixels)
// - 25.42224 H full visible in [us] (640 pixels, QVGA 320 pixels)
// - 0.0397222625 us per pixel at VGA, 0.079444525 us per pixel at QVGA
//
// We want reach 25.175 pixel clock (at 640x480). Default system clock is 125 MHz, which is
// approx. 5x pixel clock. We need 25.175*5 = 125.875 MHz. We use nearest frequency 126 MHz.
//	126000, 1512000, 126, 6, 2,     // 126.00MHz, VC0=1512MHz, FBDIV=126, PD1=6, PD2=2
//	126000, 504000, 42, 4, 1,       // 126.00MHz, VC0=504MHz, FBDIV=42, PD1=4, PD2=1
//	sysclk=126.000000 MHz, vco=504 MHz, fbdiv=42, pd1=4, pd2=1
//	sysclk=126.000000 MHz, vco=504 MHz, fbdiv=42, pd1=2, pd2=2
//	sysclk=126.000000 MHz, vco=756 MHz, fbdiv=63, pd1=6, pd2=1
//	sysclk=126.000000 MHz, vco=756 MHz, fbdiv=63, pd1=3, pd2=2
//	sysclk=126.000000 MHz, vco=1008 MHz, fbdiv=84, pd1=4, pd2=2 !!!!!
//	sysclk=126.000000 MHz, vco=1260 MHz, fbdiv=105, pd1=5, pd2=2
//	sysclk=126.000000 MHz, vco=1512 MHz, fbdiv=126, pd1=6, pd2=2
//	sysclk=126.000000 MHz, vco=1512 MHz, fbdiv=126, pd1=4, pd2=3
// Pixel clock is now:
//      5 system clock ticks per pixel at VGA ... pixel clock = 25.2 MHz, 0.039683 us per pixel
//     10 system clock ticks per pixel at QVGA ... pixel clock = 12.6 MHz, 0.079365 us per pixel
//
// - active image is 640*5=3200 clock ticks = 25.3968 us (QVGA: 1600 clock ticks)
// - total scanline is 126*31.77781=4004 clock ticks (QVGA: 2002 clock ticks)
// - H front porch = 82 clock ticks (QVGA: 41 clock ticks)
// - H sync pulse = 480 clock ticks (QVGA: 240 clock ticks)
// - H back porch = 242 clock ticks (QVGA: 121 clock ticks)

// in case of system clock = 125 MHz
// - PIO clock = system clock / 2
// - 5 PIO clocks per pixel = 10 system clocks per pixel
// - PIO clock = 62.5 MHz
// - pixel clock = 12.5 MHz
// - active image (320 pixels): 320*5 = 1600 PIO clocks = 3200 system ticks = 25.6 us (2.2 pixels stays invisible)
// - total scanline: 125*31.77781 = 3972 system clocks = 1986 PIO clocks
// - H front porch = 34 PIO clk
// - H sync = 238 PIO clk
// - H back = 114 PIO clk

extern volatile int VGAscrolly;

// PIO command (jmp=program address, num=loop counter)
#define QVGACMD(jmp, num) ( ((uint32_t)((jmp)+QVGAOff)<<27) | (uint32_t)(num))

// display frame buffer

// pointer to current frame buffer
uint QVGAOff;	// offset of QVGA PIO program
// Scanline data buffers (commands sent to PIO)
uint32_t ScanLineImg[3];	// image: HSYNC ... back porch ... image command
uint32_t ScanLineFp;		// front porch
uint32_t ScanLineDark[2];	// dark: HSYNC ... back porch + dark + front porch
uint32_t ScanLineSync[2];	// vertical sync: VHSYNC ... VSYNC(back porch + dark + front porch)

// Scanline control buffers
#define CB_MAX 8	// size of one scanline control buffer (1 link to data buffer requires 2x uint32_t)
uint32_t ScanLineCB[2*CB_MAX]; // 2 control buffers
int QVgaBufInx;		// current running control buffer
uint32_t* ScanLineCBNext;	// next control buffer

// handler variables
volatile int QVgaScanLine; // current processed scan line 0... (next displayed scan line)
volatile uint32_t QVgaFrame;	// frame counter
#ifdef rp2350
uint16_t fbuff[2][212]={0};
#else
uint16_t fbuff[2][180]={0};
#endif
volatile int X_TILE=80, Y_TILE=40;
// saved integer divider state
// VGA DMA handler - called on end of every scanline
static int VGAnextbuf=0,VGAnowbuf=1, tile=0, tc=0;

void MIPS32 __not_in_flash_func(QVgaLine1)()
{
    int i,line;
    uint8_t l,d;
    uint8_t transparent16=(uint8_t)transparent;
#ifdef rp2350
    uint8_t s;
    uint8_t transparent16s=(uint8_t)transparents;
#endif
	// Clear the interrupt request for DMA control channel
	dma_hw->ints0 = (1u << QVGA_DMA_PIO);

	// update DMA control channel and run it
	dma_channel_set_read_addr(QVGA_DMA_CB, ScanLineCBNext, true);
	// save integer divider state
//	hw_divider_save_state(&SaveDividerState);

	// prepare control buffer to be processed
	uint32_t* cb = &ScanLineCB[QVgaBufInx*CB_MAX];
	// switch current buffer index (bufinx = current preparing buffer, MiniVgaBufInx = current running buffer)
    QVgaBufInx ^= 1;
	ScanLineCBNext = cb;

	// increment scanline (1..)
	line = QVgaScanLine+1; // current scanline
//	line++; 		// new current scanline
	if (line >= QVGA_VTOT) // last scanline?
	{
		QVgaFrame++;	// increment frame counter
		line = 0; 	// restart scanline
        tile=0;
        tc=0;
	}
	QVgaScanLine = line;	// store new scanline

	// check scanline
	line -= QVGA_VSYNC;
	if (line < 0)
	{
		// VSYNC
		*cb++ = 2;
		*cb++ = (uint32_t)&ScanLineSync[0];
	}
	else
	{
		// front porch and back porch
		line -= QVGA_VBACK;
		if ((line < 0) || (line >= QVGA_VACT))
		{
			// dark line
			*cb++ = 2;
			*cb++ = (uint32_t)&ScanLineDark[0];
		}

		// image scanlines
		else
		{
        // prepare image line
            if(DISPLAY_TYPE==SCREENMODE1){
                uint16_t *q=&fbuff[VGAnextbuf][0];
                volatile unsigned char *p=&DisplayBuf[line * vgaloop8];
                volatile unsigned char *pp=&LayerBuf[line * vgaloop8];
                if(tc==ytileheight){
                    tile++;
                    tc=0;
                }
                tc++;
                register int pos=tile*X_TILE;
                for(i=0;i<vgaloop16;i++){
                    register int d=*p++ | *pp++;
                    register int low= d & 0xF;
                    register int high=d >>4;
                    *q++=(M_Foreground[low] & tilefcols[pos]) | (M_Background[low] & tilebcols[pos]) ;
                    *q++=(M_Foreground[high]& tilefcols[pos]) | (M_Background[high] & tilebcols[pos]) ;
                    pos++;
                    d=*p++ | *pp++;
                    low= d & 0xF;
                    high=d++ >>4;
                    *q++=(M_Foreground[low] & tilefcols[pos]) | (M_Background[low] & tilebcols[pos]) ;
                    *q++=(M_Foreground[high]& tilefcols[pos]) | (M_Background[high] & tilebcols[pos]) ;
                    pos++;
                }
#ifdef rp2350
            } else if(DISPLAY_TYPE==SCREENMODE3){
                register unsigned char *p=&DisplayBuf[line * vgaloop2];
                register unsigned char *q=&LayerBuf[line * vgaloop2];
                register int low, high, low2, high2;
                register uint8_t *r=(uint8_t *)fbuff[VGAnextbuf];
                for(int i=0;i<vgaloop2;i++){
                    low= map16[p[i] & 0xF];
                    high=map16[(p[i] & 0xF0)>>4];
                    low2= map16[q[i] & 0xF];
                    high2=map16[(q[i] & 0xF0)>>4];
                    if(low2!=transparent16)low=low2;
                    if(high2!=transparent16)high=high2;
                    *r++=low | (high<<4);
                }
#endif
            } else { //mode 2
                line>>=1;
                register unsigned char *dd=&DisplayBuf[line * vgaloop4];
                register unsigned char *ll=&LayerBuf[line * vgaloop4];
#ifdef rp2350
                register unsigned char *ss=&SecondLayer[line * vgaloop4];
                if(ss==dd){
                    ss=ll;
                    transparent16s=transparent16;
                }
                register int low3, high3;
#endif
                register int low, high, low2, high2;
                register uint16_t *r=(uint16_t *)fbuff[VGAnextbuf];
                for(int i=0;i<vgaloop4;i+=2){
                    d=*dd++;
                    l=*ll++;
                    low= map16[d & 0xF];
                    d>>=4;
                    high=map16[d];
                    low2= map16[l & 0xF];
                    l>>=4;
                    high2=map16[l];
#ifdef rp2350
                    s=*ss++;
                    low3= map16[s & 0xF];
                    s>>=4;
                    high3=map16[s];
#endif
                    if(low2!=transparent16)low=low2;
                    if(high2!=transparent16)high=high2;
#ifdef rp2350
                    if(low3!=transparent16s)low=low3;
                    if(high3!=transparent16s)high=high3;
#endif
                    *r++=(low | (low<<4) | (high<<8) | (high<<12));
                    d=*dd++;
                    l=*ll++;
                    low= map16[d & 0xF];
                    d>>=4;
                    high=map16[d];
                    low2= map16[l & 0xF];
                    l>>=4;
                    high2=map16[l];
#ifdef rp2350
                    s=*ss++;
                    low3= map16[s & 0xF];
                    s>>=4;
                    high3=map16[s];
#endif
                    if(low2!=transparent16)low=low2;
                    if(high2!=transparent16)high=high2;
#ifdef rp2350
                    if(low3!=transparent16s)low=low3;
                    if(high3!=transparent16s)high=high3;
#endif
                    *r++=(low | (low<<4) | (high<<8) | (high<<12));
                }
            }
            VGAnextbuf ^=1;
            VGAnowbuf ^=1;

			// HSYNC ... back porch ... image command
			*cb++ = 3;
			*cb++ = (uint32_t)&ScanLineImg[0];

			// image data
			*cb++ = vgaloop8;
			*cb++ = (uint32_t)fbuff[VGAnowbuf];

			// front porch
			*cb++ = 1;
			*cb++ = (uint32_t)&ScanLineFp;
		}
	}

	// end mark
	*cb++ = 0;
	*cb = 0;

	// restore integer divider state
//	hw_divider_restore_state(&SaveDividerState);
}

void QVgaPioInit()
{
	int i;
    if((Option.CPU_Speed % 126000) ==0){
        QVGA_TOTAL= 4000;// total clock ticks (= QVGA_HSYNC + QVGA_BP + WIDTH*QVGA_CPP[1600] + QVGA_FP)
        QVGA_HSYNC = 480;	// horizontal sync clock ticks
        QVGA_HACT = 640;
        QVGA_BP	= 240;	// back porch clock ticks
        QVGA_FP	= 80;	// front porch clock ticks
        // QVGA vertical timings
        QVGA_VACT	= 480;	// V active scanlines (= 2*HEIGHT)
        QVGA_VFRONT	= 10;	// V front porch
        QVGA_VSYNC	= 2;	// length of V sync (number of scanlines)
        QVGA_VBACK	= 33;	// V back porch
        QVGA_VTOT	= 525;	// total scanlines (= QVGA_VSYNC + QVGA_VBACK + QVGA_VACT + QVGA_VFRONT)
    } else if(Option.CPU_Speed == Freq848){
        QVGA_TOTAL= 1088*5;// total clock ticks (= QVGA_HSYNC + QVGA_BP + WIDTH*QVGA_CPP[1600] + QVGA_FP)
        QVGA_HSYNC = 112*5;	// horizontal sync clock ticks
        QVGA_HACT = 848;
        QVGA_BP	= 112*5;	// back porch clock ticks
        QVGA_FP	= 16*5;	// front porch clock ticks
        // QVGA vertical timings
        QVGA_VACT	= 480;	// V active scanlines (= 2*HEIGHT)
        QVGA_VFRONT	= 8;	// V front porch
        QVGA_VSYNC	= 6;	// length of V sync (number of scanlines)
        QVGA_VBACK	= 23;	// V back porch
        QVGA_VTOT	= 517;	// total scanlines (= QVGA_VSYNC + QVGA_VBACK + QVGA_VACT + QVGA_VFRONT)
    } else if(Option.CPU_Speed == FreqSVGA){
        QVGA_TOTAL= 1024*5;// total clock ticks (= QVGA_HSYNC + QVGA_BP + WIDTH*QVGA_CPP[1600] + QVGA_FP)
        QVGA_HSYNC = 72*5;	// horizontal sync clock ticks
        QVGA_HACT = 800;
        QVGA_BP	= 128*5;	// back porch clock ticks
        QVGA_FP	= 24*5;	// front porch clock ticks
        // QVGA vertical timings
        QVGA_VACT	= 600;	// V active scanlines (= 2*HEIGHT)
        QVGA_VFRONT	= 1;	// V front porch
        QVGA_VSYNC	= 2;	// length of V sync (number of scanlines)
        QVGA_VBACK	= 22;	// V back porch
        QVGA_VTOT	= 625;	// total scanlines (= QVGA_VSYNC + QVGA_VBACK + QVGA_VACT + QVGA_VFRONT)
    } else if(Option.CPU_Speed == Freq400){
        QVGA_TOTAL= 900*5;// total clock ticks (= QVGA_HSYNC + QVGA_BP + WIDTH*QVGA_CPP[1600] + QVGA_FP)
        QVGA_HSYNC = 108*5;	// horizontal sync clock ticks
        QVGA_HACT = 720;
        QVGA_BP	= 54*5;	// back porch clock ticks
        QVGA_FP	= 18*5;	// front porch clock ticks
        // QVGA vertical timings
        QVGA_VACT	= 400;	// V active scanlines (= 2*HEIGHT)
        QVGA_VFRONT	= 12;	// V front porch
        QVGA_VSYNC	= 2;	// length of V sync (number of scanlines)
        QVGA_VBACK	= 35;	// V back porch
        QVGA_VTOT	= 449;	// total scanlines (= QVGA_VSYNC + QVGA_VBACK + QVGA_VACT + QVGA_VFRONT)
    } else {
        QVGA_TOTAL= 4200;// total clock ticks (= QVGA_HSYNC + QVGA_BP + WIDTH*QVGA_CPP[1600] + QVGA_FP)
        QVGA_HSYNC =	320;	// horizontal sync clock ticks
        QVGA_HACT = 640;
        QVGA_BP	= 600;	// back porch clock ticks
        QVGA_FP	= 80;	// front porch clock ticks
        // QVGA vertical timings
        QVGA_VACT	= 480;	// V active scanlines (= 2*HEIGHT)
        QVGA_VFRONT	= 1;	// V front porch
        QVGA_VSYNC	= 3;	// length of V sync (number of scanlines)
        QVGA_VBACK	= 16;	// V back porch
        QVGA_VTOT	= 500;	// total scanlines (= QVGA_VSYNC + QVGA_VBACK + QVGA_VACT + QVGA_VFRONT)
    }
	// load PIO program
#ifdef rp2350
    if(piomap[QVGA_PIO_NUM] & (uint64_t)0xFFFF00000000)pio_set_gpio_base(QVGA_PIO,16);
#endif
    I2SOff = pio_add_program(QVGA_PIO, &i2s_program);
	QVGAOff = pio_add_program(QVGA_PIO, &qvga_program);
	// configure GPIOs for use by PIO
	for (i = QVGA_GPIO_FIRST; i <= QVGA_GPIO_LAST; i++) pio_gpio_init(QVGA_PIO, i);
	pio_gpio_init(QVGA_PIO, QVGA_GPIO_HSYNC);
	pio_gpio_init(QVGA_PIO, QVGA_GPIO_VSYNC);

	// set pin direction to output
	pio_sm_set_consecutive_pindirs(QVGA_PIO, QVGA_SM, QVGA_GPIO_FIRST, QVGA_GPIO_NUM, true);
	pio_sm_set_consecutive_pindirs(QVGA_PIO, QVGA_SM, QVGA_GPIO_HSYNC, 2, true);

	// negate HSYNC and VSYNC output
    if(!(Option.CPU_Speed==Freq848 || Option.CPU_Speed==FreqSVGA)){
        gpio_set_outover(QVGA_GPIO_HSYNC, GPIO_OVERRIDE_INVERT);
        gpio_set_outover(QVGA_GPIO_VSYNC, GPIO_OVERRIDE_INVERT);
    }

	// prepare default PIO program config
	pio_sm_config cfg = qvga_program_get_default_config(QVGAOff);

	// map state machine's OUT and MOV pins	
	sm_config_set_out_pins(&cfg, QVGA_GPIO_FIRST, QVGA_GPIO_NUM);

	// set sideset pins (HSYNC and VSYNC)
	sm_config_set_sideset_pins(&cfg, QVGA_GPIO_HSYNC);

	// join FIFO to send only
	sm_config_set_fifo_join(&cfg, PIO_FIFO_JOIN_TX);

	// PIO clock divider
	sm_config_set_clkdiv(&cfg, QVGA_CLKDIV);

	// shift right, autopull, pull threshold
	sm_config_set_out_shift(&cfg, true, true, 32);

	// initialize state machine
	pio_sm_init(QVGA_PIO, QVGA_SM, QVGAOff+qvga_offset_entry, &cfg);
}

// initialize scanline buffers
void QVgaBufInit()
{
	// image scanline data buffer: HSYNC ... back porch ... image command
	ScanLineImg[0] = QVGACMD(qvga_offset_hsync, QVGA_HSYNC-3); // HSYNC
	ScanLineImg[1] = QVGACMD(qvga_offset_dark, QVGA_BP-4); // back porch
	ScanLineImg[2] = QVGACMD(qvga_offset_output, QVGA_HACT-2); // image

	// front porch
	ScanLineFp = QVGACMD(qvga_offset_dark, QVGA_FP-4); // front porch

	// dark scanline: HSYNC ... back porch + dark + front porch
	ScanLineDark[0] = QVGACMD(qvga_offset_hsync, QVGA_HSYNC-3); // HSYNC
	ScanLineDark[1] = QVGACMD(qvga_offset_dark, QVGA_TOTAL-QVGA_HSYNC-4); // back porch + dark + front porch

	// vertical sync: VHSYNC ... VSYNC(back porch + dark + front porch)
	ScanLineSync[0] = QVGACMD(qvga_offset_vhsync, QVGA_HSYNC-3); // VHSYNC
	ScanLineSync[1] = QVGACMD(qvga_offset_vsync, QVGA_TOTAL-QVGA_HSYNC-3); // VSYNC(back porch + dark + front porch)

	// control buffer 1 - initialize to VSYNC
	ScanLineCB[0] = 2; // send 2x uint32_t (send ScanLineSync)
	ScanLineCB[1] = (uint32_t)&ScanLineSync[0]; // VSYNC data buffer
	ScanLineCB[2] = 0; // stop mark
	ScanLineCB[3] = 0; // stop mark

	// control buffer 1 - initialize to VSYNC
	ScanLineCB[CB_MAX+0] = 2; // send 2x uint32_t (send ScanLineSync)
	ScanLineCB[CB_MAX+1] = (uint32_t)&ScanLineSync[0]; // VSYNC data buffer
	ScanLineCB[CB_MAX+2] = 0; // stop mark
	ScanLineCB[CB_MAX+3] = 0; // stop mark
}

// initialize QVGA DMA
//   control blocks aliases:
//                  +0x0        +0x4          +0x8          +0xC (Trigger)
// 0x00 (alias 0):  READ_ADDR   WRITE_ADDR    TRANS_COUNT   CTRL_TRIG
// 0x10 (alias 1):  CTRL        READ_ADDR     WRITE_ADDR    TRANS_COUNT_TRIG
// 0x20 (alias 2):  CTRL        TRANS_COUNT   READ_ADDR     WRITE_ADDR_TRIG
// 0x30 (alias 3):  CTRL        WRITE_ADDR    TRANS_COUNT   READ_ADDR_TRIG ... we use this!
void QVgaDmaInit()
{

// ==== prepare DMA control channel
	// prepare DMA default config
	dma_channel_config cfg = dma_channel_get_default_config(QVGA_DMA_CB);

	// increment address on read from memory
	channel_config_set_read_increment(&cfg, true);

	// increment address on write to DMA port
	channel_config_set_write_increment(&cfg, true);

	// each DMA transfered entry is 32-bits
	channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32);

	// write ring - wrap to 8-byte boundary (TRANS_COUNT and READ_ADDR_TRIG of data DMA)
	channel_config_set_ring(&cfg, true, 3);

	// DMA configure
	dma_channel_configure(
		QVGA_DMA_CB,		// channel
		&cfg,			// configuration
		&dma_hw->ch[QVGA_DMA_PIO].al3_transfer_count, // write address
		&ScanLineCB[0],		// read address - as first, control buffer 1 will be sent out
		2,			// number of transfers in uint32_t (number of transfers per one request from data DMA)
		false			// do not start yet
	);

// ==== prepare DMA data channel

	// prepare DMA default config

	cfg = dma_channel_get_default_config(QVGA_DMA_PIO);

	// increment address on read from memory
	channel_config_set_read_increment(&cfg, true);

	// do not increment address on write to PIO
	channel_config_set_write_increment(&cfg, false);

	// each DMA transfered entry is 32-bits
	channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32);

	// DMA data request for sending data to PIO
	channel_config_set_dreq(&cfg, pio_get_dreq(QVGA_PIO, QVGA_SM, true));

	// chain channel to DMA control block
	channel_config_set_chain_to(&cfg, QVGA_DMA_CB);

	// raise the IRQ flag when 0 is written to a trigger register (end of chain)
	channel_config_set_irq_quiet(&cfg, true);

	// set high priority
	cfg.ctrl |= DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS;

	// DMA configure
	dma_channel_configure(
		QVGA_DMA_PIO,		// channel
		&cfg,			// configuration
		&QVGA_PIO->txf[QVGA_SM], // write address
		NULL,			// read address
		0,			// number of transfers in uint32_t
		false			// do not start immediately
	);

// ==== initialize IRQ0, raised from DMA data channel

	// enable DMA channel IRQ0
	dma_channel_set_irq0_enabled(QVGA_DMA_PIO, true);

	// set DMA IRQ handler
    irq_set_exclusive_handler(DMA_IRQ_0, QVgaLine1);
    vgaloop4=QVGA_HACT/4;
    vgaloop8=QVGA_HACT/8;
    vgaloop16=QVGA_HACT/16;
    vgaloop2=QVGA_HACT/2;
    MODE1SIZE=QVGA_HACT*QVGA_VACT/8;
    MODE2SIZE=(QVGA_HACT/2)*(QVGA_VACT/2)/2;
    MODE3SIZE=QVGA_HACT*QVGA_VACT/2;
    HRes=QVGA_HACT;
    VRes=QVGA_VACT;
// set highest IRQ priority
	irq_set_priority(DMA_IRQ_0, 0);
}

// initialize QVGA (can change system clock)
void QVgaInit()
{
    X_TILE=Option.X_TILE;
    Y_TILE=Option.Y_TILE;
    ytileheight=(X_TILE==80 || X_TILE==90 || X_TILE==106 || X_TILE==100)? 12 : 16;
	// initialize PIO
	QVgaPioInit();

	// initialize scanline buffers
	QVgaBufInit();

	// initialize DMA
	QVgaDmaInit();

	// initialize parameters
	QVgaScanLine = 0; // currently processed scanline
	QVgaBufInx = 0; // at first, control buffer 1 will be sent out
	QVgaFrame = 0; // current frame
	ScanLineCBNext = &ScanLineCB[CB_MAX]; // send control buffer 2 next

	// enable DMA IRQ
	irq_set_enabled(DMA_IRQ_0, true);

	// start DMA
	dma_channel_start(QVGA_DMA_CB);

	// run state machine
	pio_sm_set_enabled(QVGA_PIO, QVGA_SM, true);
}

void (* volatile Core1Fnc)() = NULL; // core 1 remote function

// QVGA core
void __not_in_flash_func(QVgaCore)()
{
	// initialize QVGA
	QVgaInit();

	// infinite loop
	while (true)
	{
		// data memory barrier
		__dmb();
        if (multicore_fifo_rvalid()) {
            int command=multicore_fifo_pop_blocking();
            if(command==0x5555){
                irq_set_enabled(DMA_IRQ_0, false);
            };
            if(command==0xAAAA){
                irq_set_enabled(DMA_IRQ_0, true);
            }
        } 
    }
}
uint32_t core1stack[128];
#else
extern uint16_t HDMIlines[2][800];
// DVI constants

#define TMDS_CTRL_00 0x354u
#define TMDS_CTRL_01 0x0abu
#define TMDS_CTRL_10 0x154u
#define TMDS_CTRL_11 0x2abu

#define SYNC_V0_H0 (TMDS_CTRL_00 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V0_H1 (TMDS_CTRL_01 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H0 (TMDS_CTRL_10 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))

#define MODE_H_S_SYNC_POLARITY 0
#define MODE_H_S_FRONT_PORCH   (Option.CPU_Speed % 126000==0 ? 16 : 16)
#define MODE_H_S_SYNC_WIDTH    (Option.CPU_Speed % 126000==0 ? 96 : 64)
#define MODE_H_S_BACK_PORCH    (Option.CPU_Speed % 126000==0 ? 48 : 120)

#define MODE_V_S_SYNC_POLARITY 0
#define MODE_V_S_FRONT_PORCH   (Option.CPU_Speed % 126000==0 ? 10 : 1)
#define MODE_V_S_SYNC_WIDTH    (Option.CPU_Speed % 126000==0 ? 2 : 3)
#define MODE_V_S_BACK_PORCH    (Option.CPU_Speed % 126000==0 ? 33 : 16)

#define MODE_H_F_SYNC_POLARITY 0
#define MODE_H_F_ACTIVE_PIXELS 640
#define MODE_H_F_FRONT_PORCH   16
#define MODE_H_F_SYNC_WIDTH    96
#define MODE_H_F_BACK_PORCH    48

#define MODE_V_F_SYNC_POLARITY 0
#define MODE_V_F_ACTIVE_LINES 480
#define MODE_V_F_FRONT_PORCH   10
#define MODE_V_F_SYNC_WIDTH    2
#define MODE_V_F_BACK_PORCH    33

#define MODE_H_8_SYNC_POLARITY 1
#define MODE_H_8_FRONT_PORCH   16
#define MODE_H_8_SYNC_WIDTH    112
#define MODE_H_8_BACK_PORCH    112

#define MODE_V_8_SYNC_POLARITY 1
#define MODE_V_8_FRONT_PORCH   8
#define MODE_V_8_SYNC_WIDTH    6
#define MODE_V_8_BACK_PORCH    23

#define MODE_H_4_SYNC_POLARITY 0
#define MODE_H_4_FRONT_PORCH   18
#define MODE_H_4_SYNC_WIDTH    108
#define MODE_H_4_BACK_PORCH    54

#define MODE_V_4_SYNC_POLARITY 0
#define MODE_V_4_FRONT_PORCH   12
#define MODE_V_4_SYNC_WIDTH    2
#define MODE_V_4_BACK_PORCH    35

#define MODE_H_W_SYNC_POLARITY 1
#define MODE_H_W_FRONT_PORCH   110
#define MODE_H_W_SYNC_WIDTH    40
#define MODE_H_W_BACK_PORCH    220

#define MODE_V_W_SYNC_POLARITY 1
#define MODE_V_W_FRONT_PORCH   5
#define MODE_V_W_SYNC_WIDTH    5
#define MODE_V_W_BACK_PORCH    20


#define MODE_H_L_SYNC_POLARITY 0
#define MODE_H_L_FRONT_PORCH   24
#define MODE_H_L_SYNC_WIDTH    136
#define MODE_H_L_BACK_PORCH    144

#define MODE_V_L_SYNC_POLARITY 0
#define MODE_V_L_FRONT_PORCH   3
#define MODE_V_L_SYNC_WIDTH    6
#define MODE_V_L_BACK_PORCH    29

#define MODE_H_V_SYNC_POLARITY 1
#define MODE_H_V_FRONT_PORCH   24
#define MODE_H_V_SYNC_WIDTH    72
#define MODE_H_V_BACK_PORCH    128

#define MODE_V_V_SYNC_POLARITY 1
#define MODE_V_V_FRONT_PORCH   1
#define MODE_V_V_SYNC_WIDTH    2
#define MODE_V_V_BACK_PORCH    22

#define MODE_H_S_TOTAL_PIXELS ( \
    MODE_H_S_FRONT_PORCH + MODE_H_S_SYNC_WIDTH + \
    MODE_H_S_BACK_PORCH  + MODE_H_S_ACTIVE_PIXELS \
)
#define MODE_V_S_TOTAL_LINES  ( \
    MODE_V_S_FRONT_PORCH + MODE_V_S_SYNC_WIDTH + \
    MODE_V_S_BACK_PORCH  + MODE_V_S_ACTIVE_LINES \
)
#define MODE_H_F_TOTAL_PIXELS ( \
    MODE_H_F_FRONT_PORCH + MODE_H_F_SYNC_WIDTH + \
    MODE_H_F_BACK_PORCH  + MODE_H_F_ACTIVE_PIXELS \
)
#define MODE_V_F_TOTAL_LINES  ( \
    MODE_V_F_FRONT_PORCH + MODE_V_F_SYNC_WIDTH + \
    MODE_V_F_BACK_PORCH  + MODE_V_F_ACTIVE_LINES \
)
#define MODE_H_W_TOTAL_PIXELS ( \
    MODE_H_W_FRONT_PORCH + MODE_H_W_SYNC_WIDTH + \
    MODE_H_W_BACK_PORCH  + MODE_H_W_ACTIVE_PIXELS \
)
#define MODE_V_W_TOTAL_LINES  ( \
    MODE_V_W_FRONT_PORCH + MODE_V_W_SYNC_WIDTH + \
    MODE_V_W_BACK_PORCH  + MODE_V_W_ACTIVE_LINES \
)
#define MODE_H_L_TOTAL_PIXELS ( \
    MODE_H_L_FRONT_PORCH + MODE_H_L_SYNC_WIDTH + \
    MODE_H_L_BACK_PORCH  + MODE_H_L_ACTIVE_PIXELS \
)
#define MODE_V_L_TOTAL_LINES  ( \
    MODE_V_L_FRONT_PORCH + MODE_V_L_SYNC_WIDTH + \
    MODE_V_L_BACK_PORCH  + MODE_V_L_ACTIVE_LINES \
)
#define MODE_H_V_TOTAL_PIXELS ( \
    MODE_H_V_FRONT_PORCH + MODE_H_V_SYNC_WIDTH + \
    MODE_H_V_BACK_PORCH  + MODE_H_V_ACTIVE_PIXELS \
)
#define MODE_V_V_TOTAL_LINES  ( \
    MODE_V_V_FRONT_PORCH + MODE_V_V_SYNC_WIDTH + \
    MODE_V_V_BACK_PORCH  + MODE_V_V_ACTIVE_LINES \
)
#define MODE_H_8_TOTAL_PIXELS ( \
    MODE_H_8_FRONT_PORCH + MODE_H_8_SYNC_WIDTH + \
    MODE_H_8_BACK_PORCH  + MODE_H_8_ACTIVE_PIXELS \
)
#define MODE_V_8_TOTAL_LINES  ( \
    MODE_V_8_FRONT_PORCH + MODE_V_8_SYNC_WIDTH + \
    MODE_V_8_BACK_PORCH  + MODE_V_8_ACTIVE_LINES \
)
#define MODE_H_4_TOTAL_PIXELS ( \
    MODE_H_4_FRONT_PORCH + MODE_H_4_SYNC_WIDTH + \
    MODE_H_4_BACK_PORCH  + MODE_H_4_ACTIVE_PIXELS \
)
#define MODE_V_4_TOTAL_LINES  ( \
    MODE_V_4_FRONT_PORCH + MODE_V_4_SYNC_WIDTH + \
    MODE_V_4_BACK_PORCH  + MODE_V_4_ACTIVE_LINES \
)

volatile int mode = 1;
#define HSTX_CMD_RAW         (0x0u << 12)
#define HSTX_CMD_RAW_REPEAT  (0x1u << 12)
#define HSTX_CMD_TMDS        (0x2u << 12)
#define HSTX_CMD_TMDS_REPEAT (0x3u << 12)
#define HSTX_CMD_NOP         (0xfu << 12)
#define DMACH_PING 0
#define DMACH_PONG 1
const uint32_t MAP256DEF[256] =
{
0x0,0x55,0xAA,0xFF,0x2400,0x2455,0x24AA,0x24FF,
0x4900,0x4955,0x49AA,0x49FF,0x6D00,0x6D55,0x6DAA,0x6DFF,
0x9200,0x9255,0x92AA,0x92FF,0xB600,0xB655,0xB6AA,0xB6FF,
0xDB00,0xDB55,0xDBAA,0xDBFF,0xFF00,0xFF55,0xFFAA,0xFFFF,
0x240000,0x240055,0x2400AA,0x2400FF,0x242400,0x242455,0x2424AA,0x2424FF,
0x244900,0x244955,0x2449AA,0x2449FF,0x246D00,0x246D55,0x246DAA,0x246DFF,
0x249200,0x249255,0x2492AA,0x2492FF,0x24B600,0x24B655,0x24B6AA,0x24B6FF,
0x24DB00,0x24DB55,0x24DBAA,0x24DBFF,0x24FF00,0x24FF55,0x24FFAA,0x24FFFF,
0x490000,0x490055,0x4900AA,0x4900FF,0x492400,0x492455,0x4924AA,0x4924FF,
0x494900,0x494955,0x4949AA,0x4949FF,0x496D00,0x496D55,0x496DAA,0x496DFF,
0x499200,0x499255,0x4992AA,0x4992FF,0x49B600,0x49B655,0x49B6AA,0x49B6FF,
0x49DB00,0x49DB55,0x49DBAA,0x49DBFF,0x49FF00,0x49FF55,0x49FFAA,0x49FFFF,
0x6D0000,0x6D0055,0x6D00AA,0x6D00FF,0x6D2400,0x6D2455,0x6D24AA,0x6D24FF,
0x6D4900,0x6D4955,0x6D49AA,0x6D49FF,0x6D6D00,0x6D6D55,0x6D6DAA,0x6D6DFF,
0x6D9200,0x6D9255,0x6D92AA,0x6D92FF,0x6DB600,0x6DB655,0x6DB6AA,0x6DB6FF,
0x6DDB00,0x6DDB55,0x6DDBAA,0x6DDBFF,0x6DFF00,0x6DFF55,0x6DFFAA,0x6DFFFF,
0x920000,0x920055,0x9200AA,0x9200FF,0x922400,0x922455,0x9224AA,0x9224FF,
0x924900,0x924955,0x9249AA,0x9249FF,0x926D00,0x926D55,0x926DAA,0x926DFF,
0x929200,0x929255,0x9292AA,0x9292FF,0x92B600,0x92B655,0x92B6AA,0x92B6FF,
0x92DB00,0x92DB55,0x92DBAA,0x92DBFF,0x92FF00,0x92FF55,0x92FFAA,0x92FFFF,
0xB60000,0xB60055,0xB600AA,0xB600FF,0xB62400,0xB62455,0xB624AA,0xB624FF,
0xB64900,0xB64955,0xB649AA,0xB649FF,0xB66D00,0xB66D55,0xB66DAA,0xB66DFF,
0xB69200,0xB69255,0xB692AA,0xB692FF,0xB6B600,0xB6B655,0xB6B6AA,0xB6B6FF,
0xB6DB00,0xB6DB55,0xB6DBAA,0xB6DBFF,0xB6FF00,0xB6FF55,0xB6FFAA,0xB6FFFF,
0xDB0000,0xDB0055,0xDB00AA,0xDB00FF,0xDB2400,0xDB2455,0xDB24AA,0xDB24FF,
0xDB4900,0xDB4955,0xDB49AA,0xDB49FF,0xDB6D00,0xDB6D55,0xDB6DAA,0xDB6DFF,
0xDB9200,0xDB9255,0xDB92AA,0xDB92FF,0xDBB600,0xDBB655,0xDBB6AA,0xDBB6FF,
0xDBDB00,0xDBDB55,0xDBDBAA,0xDBDBFF,0xDBFF00,0xDBFF55,0xDBFFAA,0xDBFFFF,
0xFF0000,0xFF0055,0xFF00AA,0xFF00FF,0xFF2400,0xFF2455,0xFF24AA,0xFF24FF,
0xFF4900,0xFF4955,0xFF49AA,0xFF49FF,0xFF6D00,0xFF6D55,0xFF6DAA,0xFF6DFF,
0xFF9200,0xFF9255,0xFF92AA,0xFF92FF,0xFFB600,0xFFB655,0xFFB6AA,0xFFB6FF,
0xFFDB00,0xFFDB55,0xFFDBAA,0xFFDBFF,0xFFFF00,0xFFFF55,0xFFFFAA,0xFFFFFF
};
const uint32_t MAP16DEF[16] = {0x00,0xFF,0x5500,0x55ff,0xAA00,0xAAff,0xff00,0xffff,0xff0000,0xff00FF,0xff5500,0xff55ff,0xffAA00,0xffAAff,0xffff00,0xffffff};
const uint32_t MAP4DEF[4] = {0,0xFF,0xFF00,0xFF0000};
uint16_t map256[256];
static uint32_t vblank_line_vsync_off[7] ;
static uint32_t vblank_line_vsync_on[7];
static uint32_t vactive_line[9];
static bool dma_pong = false;
int MODE_H_SYNC_POLARITY, MODE_V_TOTAL_LINES, MODE_ACTIVE_LINES, MODE_ACTIVE_PIXELS;
int MODE_H_ACTIVE_PIXELS, MODE_H_FRONT_PORCH, MODE_H_SYNC_WIDTH, MODE_H_BACK_PORCH;
int MODE_V_SYNC_POLARITY ,MODE_V_ACTIVE_LINES ,MODE_V_FRONT_PORCH, MODE_V_SYNC_WIDTH, MODE_V_BACK_PORCH;
int PIXELS_PER_WORD, TRANSFER_COUNT, BLANKING_COUNT;
// A ping and a pong are cued up initially, so the first time we enter this
// handler it is to cue up the second ping after the first ping has completed.
// This is the third scanline overall (-> =2 because zero-based).
volatile int32_t v_scanline = 2;

// During the vertical active period, we take two IRQs per scanline: one to
// post the command list, and another to post the pixels.
static bool vactive_cmdlist_posted = false;
void MIPS64 __not_in_flash_func(dma_irq_handler0)() {
    // dma_pong indicates the channel that just finished, which is the one
    // we're about to reload.
    uint ch_num = dma_pong ? DMACH_PONG : DMACH_PING;
    dma_channel_hw_t *ch = &dma_hw->ch[ch_num];
    dma_hw->intr = 1u << ch_num;
    dma_pong = !dma_pong;
 
    if (v_scanline >= MODE_V_FRONT_PORCH && v_scanline < (MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH)) {
        ch->read_addr = (uintptr_t)vblank_line_vsync_on;
        ch->transfer_count = count_of(vblank_line_vsync_on);
    } else if (v_scanline < BLANKING_COUNT) {
        ch->read_addr = (uintptr_t)vblank_line_vsync_off;
        ch->transfer_count = count_of(vblank_line_vsync_off);
    } else if (!vactive_cmdlist_posted) {
        ch->read_addr = (uintptr_t)vactive_line;
        ch->transfer_count = count_of(vactive_line);
        vactive_cmdlist_posted = true;
    } else {
        ch->read_addr = (uintptr_t)HDMIlines[v_scanline & 1];
        ch->transfer_count = TRANSFER_COUNT;
        vactive_cmdlist_posted = false;
    }

    if (!vactive_cmdlist_posted) {
        v_scanline = (v_scanline + 1) % MODE_V_TOTAL_LINES;
    } 
}

// ----------------------------------------------------------------------------
// Main program

/*void MIPS32 __not_in_flash_func(HDMIloop1)(void){
    int last_line=2,load_line, line_to_load, Line_dup, Line_quad;
    while(1){
        if(v_scanline!=last_line){
            last_line=v_scanline;
            load_line=v_scanline - (MODE_V_TOTAL_LINES - MODE_V_ACTIVE_LINES);
            Line_dup=load_line>>1;
            Line_quad=load_line>>2;
            line_to_load = last_line & 1;
            if(load_line>=0 && load_line<MODE_V_ACTIVE_LINES){
                __dmb();
                switch(DISPLAY_TYPE){
                case SCREENMODE1: //1280x720x2 colour with tiles
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t *fcol_w=tilefcols_w+load_line/ytileheight*X_TILE, *bcol_w=tilebcols_w+load_line/ytileheight*X_TILE; //get the relevant tile
                        uint32_t *pp=(uint32_t *)&DisplayBuf[load_line*vgaloop8];
                        uint32_t *qq=(uint32_t *)&LayerBuf[load_line*vgaloop8];
                        uint32_t d=*pp | *qq;
                        for(int i=0; i<vgaloop32 ; i++){
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            fcol_w++;
                            bcol_w++;
                            d=*(++pp) | *(++qq);
                        }
                    }
                    break;
                case SCREENMODE2: //320 x 180 x 4bit-colour mapped to 256
                    {
                        uint32_t *p=(uint32_t *)HDMIlines[line_to_load];
                        uint8_t l,d,s;
                        int pp= (Line_quad)*vgaloop8;
                        for(int i=0; i<vgaloop8 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];s=SecondLayer[pp+i];
                            if((s&0xf)!=transparents){
                                *p++=map16q[s&0xf];
                            } else {
                                if((l&0xf)!=transparent){
                                    *p++=map16q[l&0xf];
                                } else {
                                    *p++=map16q[d&0xf];
                                }
                            }
                            d>>=4;l>>=4;s>>=4;
                            if((s&0xf)!=transparents){
                                *p++=map16q[s&0xf];
                            } else {
                                if((l&0xf)!=transparent){
                                    *p++=map16q[l&0xf];
                                } else {
                                    *p++=map16q[d&0xf];
                                }
                            }
                        }
                    }
                    break;
            case SCREENMODE3: //640 x 360 x 4bit-colour mapped to 256
                    {
                        int pp= (Line_dup)*vgaloop4;
                        uint16_t *p=(uint16_t *)HDMIlines[line_to_load];
                        uint8_t l,d;
                        register uint8_t *dd=&DisplayBuf[pp];
                        register uint8_t *ll=&LayerBuf[pp];
                        for(int i=0; i<vgaloop16 ; i++){
                            l=*ll++;d=*dd++;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            l=*ll++;d=*dd++;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            l=*ll++;d=*dd++;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            l=*ll++;d=*dd++;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                        }
                    }
                    break;
                case SCREENMODE5: //320 x 180 x 8bit-colour 
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t l,d,s;
                        int pp= (Line_quad)*vgaloop4;
                        for(int i=0; i<vgaloop4 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];s=SecondLayer[pp+i];
                            if(s!=transparents){
                                *p++=s;
                                *p++=s;
                                *p++=s;
                                *p++=s;
                            } else {
                                if(l!=transparent){
                                    *p++=l;
                                    *p++=l;
                                    *p++=l;
                                    *p++=l;
                                } else {
                                    *p++=d;
                                    *p++=d;
                                    *p++=d;
                                    *p++=d;
                                }
                            }
                        }
                    }
                    break;
                default:
                }
            }
        }
    }
}*/
void MIPS32 __not_in_flash_func(HDMIloop1)(void){
    int last_line=2,load_line, line_to_load, Line_dup, Line_quad;
    while(1){
        if(v_scanline!=last_line){
            last_line=v_scanline;
            load_line=v_scanline - (MODE_V_W_TOTAL_LINES - MODE_V_W_ACTIVE_LINES);
            Line_dup=load_line>>1;
            Line_quad=load_line>>2;
            line_to_load = last_line & 1;
            if(load_line>=0 && load_line<MODE_V_W_ACTIVE_LINES){
                __dmb();
                switch(DISPLAY_TYPE){
                case SCREENMODE1: //1280x720x2 colour with tiles
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t *fcol_w=tilefcols_w+load_line/ytileheight*X_TILE, *bcol_w=tilebcols_w+load_line/ytileheight*X_TILE; //get the relevant tile
                        uint32_t *pp=(uint32_t *)&DisplayBuf[load_line*MODE_H_W_ACTIVE_PIXELS/8];
                        uint32_t *qq=(uint32_t *)&LayerBuf[load_line*MODE_H_W_ACTIVE_PIXELS/8];
                        uint32_t d=*pp | *qq;
                        for(int i=0; i<MODE_H_W_ACTIVE_PIXELS/32 ; i++){
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            fcol_w++;
                            bcol_w++;
                            d=*(++pp) | *(++qq) ;
                        }
                    }
                    break;
                case SCREENMODE2: //320 x 180 x 4bit-colour mapped to 256
                    {
                        uint32_t *p=(uint32_t *)HDMIlines[line_to_load];
                        uint8_t l,d,s;
                        int pp= (Line_quad)*MODE_H_W_ACTIVE_PIXELS/8;
                        for(int i=0; i<MODE_H_W_ACTIVE_PIXELS/8 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];s=SecondLayer[pp+i];
                            if((s&0xf)!=transparents){
                                *p++=map16q[s&0xf];
                            } else {
                                if((l&0xf)!=transparent){
                                    *p++=map16q[l&0xf];
                                } else {
                                    *p++=map16q[d&0xf];
                                }
                            }
                            d>>=4;l>>=4;s>>=4;
                            if((s&0xf)!=transparents){
                                *p++=map16q[s&0xf];
                            } else {
                                if((l&0xf)!=transparent){
                                    *p++=map16q[l&0xf];
                                } else {
                                    *p++=map16q[d&0xf];
                                }
                            }
                        }
                    }
                    break;
            case SCREENMODE3: //640 x 360 x 4bit-colour mapped to 256
                    {
                        int pp= (Line_dup)*MODE_H_W_ACTIVE_PIXELS/4;
                        uint16_t *p=(uint16_t *)HDMIlines[line_to_load];
                        uint8_t l,d;
                        for(int i=0; i<MODE_H_W_ACTIVE_PIXELS/4 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                        }
                    }
                    break;
                case SCREENMODE5: //320 x 180 x 8bit-colour 
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t l,d,s;
                        int pp= (Line_quad)*MODE_H_W_ACTIVE_PIXELS/4;
                        for(int i=0; i<MODE_H_W_ACTIVE_PIXELS/4 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];s=SecondLayer[pp+i];
                            if(s!=transparents){
                                *p++=s;
                                *p++=s;
                                *p++=s;
                                *p++=s;
                            } else {
                                if(l!=transparent){
                                    *p++=l;
                                    *p++=l;
                                    *p++=l;
                                    *p++=l;
                                } else {
                                    *p++=d;
                                    *p++=d;
                                    *p++=d;
                                    *p++=d;
                                }
                            }
                        }
                    }
                    break;
                default:
                }
            }
        }
    }
}
void MIPS32 __not_in_flash_func(HDMIloop2)(void){
    int last_line=2,load_line, line_to_load, Line_dup, Line_quad;
    while(1){
        if(v_scanline!=last_line){
            last_line=v_scanline;
            load_line=v_scanline - (MODE_V_L_TOTAL_LINES - MODE_V_L_ACTIVE_LINES);
            Line_dup=load_line>>1;
            Line_quad=load_line>>2;
            line_to_load = last_line & 1;
            if(load_line>=0 && load_line<MODE_V_L_ACTIVE_LINES){
                __dmb();
                switch(DISPLAY_TYPE){
                case SCREENMODE1: //1024x768x2 colour with tiles
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t *fcol_w=tilefcols_w+load_line/ytileheight*X_TILE, *bcol_w=tilebcols_w+load_line/ytileheight*X_TILE; //get the relevant tile
                        uint32_t *pp=(uint32_t *)&DisplayBuf[load_line*MODE_H_L_ACTIVE_PIXELS/8];
                        uint32_t *qq=(uint32_t *)&LayerBuf[load_line*MODE_H_L_ACTIVE_PIXELS/8];
                        uint32_t d=*pp | *qq;
                        for(int i=0; i<MODE_H_L_ACTIVE_PIXELS/32 ; i++){
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            fcol_w++;
                            bcol_w++;
                            d=*(++pp) | *(++qq) ;
                        }
                    }
                    break;
                case SCREENMODE2: //256 x 192 x 4bit-colour mapped to 256
                    {
                        uint32_t *p=(uint32_t *)HDMIlines[line_to_load];
                        uint8_t l,d,s;
                        int pp= (Line_quad)*MODE_H_L_ACTIVE_PIXELS/8;
                        for(int i=0; i<MODE_H_L_ACTIVE_PIXELS/8 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];s=SecondLayer[pp+i];
                            if((s&0xf)!=transparents){
                                *p++=map16q[s&0xf];
                            } else {
                                if((l&0xf)!=transparent){
                                    *p++=map16q[l&0xf];
                                } else {
                                    *p++=map16q[d&0xf];
                                }
                            }
                            d>>=4;l>>=4;s>>=4;
                            if((s&0xf)!=transparents){
                                *p++=map16q[s&0xf];
                            } else {
                                if((l&0xf)!=transparent){
                                    *p++=map16q[l&0xf];
                                } else {
                                    *p++=map16q[d&0xf];
                                }
                            }
                        }
                    }
                    break;
            case SCREENMODE3: //512 x 384 x 4bit-colour mapped to 256
                    {
                        int pp= (Line_dup)*MODE_H_L_ACTIVE_PIXELS/4;
                        uint16_t *p=(uint16_t *)HDMIlines[line_to_load];
                        uint8_t l,d;
                        for(int i=0; i<MODE_H_L_ACTIVE_PIXELS/4 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                        }
                    }
                    break;
                case SCREENMODE5: //256 x 192 x 8bit-colour 
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t l,d,s;
                        int pp= (Line_quad)*MODE_H_L_ACTIVE_PIXELS/4;
                        for(int i=0; i<MODE_H_L_ACTIVE_PIXELS/4 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];s=SecondLayer[pp+i];
                            if(s!=transparents){
                                *p++=s;
                                *p++=s;
                                *p++=s;
                                *p++=s;
                            } else {
                                if(l!=transparent){
                                    *p++=l;
                                    *p++=l;
                                    *p++=l;
                                    *p++=l;
                                } else {
                                    *p++=d;
                                    *p++=d;
                                    *p++=d;
                                    *p++=d;
                                }
                            }
                        }
                    }
                    break;
                default:
                }
            }
        }
    }
}
void MIPS32 __not_in_flash_func(HDMIloop3)(void){
    int last_line=2,load_line, line_to_load, Line_dup;
    while(1){
        if(v_scanline!=last_line){
            last_line=v_scanline;
            load_line=v_scanline - (MODE_V_TOTAL_LINES - MODE_V_ACTIVE_LINES);
            Line_dup=load_line>>1;
            line_to_load = last_line & 1;
            if(load_line>=0 && load_line<MODE_V_ACTIVE_LINES){
                __dmb();
                switch(DISPLAY_TYPE){
                case SCREENMODE1: //800x600x2 or 848x480x2 colour with tiles
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t *fcol_w=tilefcols_w+load_line/ytileheight*X_TILE, *bcol_w=tilebcols_w+load_line/ytileheight*X_TILE; //get the relevant tile
                        uint16_t *pp=(uint16_t *)&DisplayBuf[load_line*vgaloop8];
                        uint16_t *qq=(uint16_t *)&LayerBuf[load_line*vgaloop8];
                        uint16_t d=*pp | *qq;
                        for(int i=0; i<vgaloop16 ; i++){
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            *p++ = (d&0x1) ? *fcol_w : *bcol_w;
                            d>>=1;
                            fcol_w++;
                            bcol_w++;
                            d=*(++pp) | *(++qq) ;
                        }
                    }
                    break;
                case SCREENMODE2: //400 X 300 x 4bit-colour mapped to 256 or 424 X 240 x 4bit-colour mapped to 256
                    {
                        uint16_t *p=(uint16_t *)HDMIlines[line_to_load];
                        uint8_t l,d;
                        int pp= (Line_dup)*vgaloop4;
                        for(int i=0; i<vgaloop4 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16d[l&0xf];
                            } else {
                                *p++=map16d[d&0xf];
                            }
                        }
                    }
                    break;
                case SCREENMODE3: //800 x 600 x 4bit-colour mapped to 256 or 848 x 480 x 4bit-colour mapped to 256
                    {
                        int pp= load_line*vgaloop2;
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t l,d;
                        for(int i=0; i<vgaloop2 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];
                            if((l&0xf)!=transparent){
                                *p++=map16s[l&0xf];
                            } else {
                                *p++=map16s[d&0xf];
                            }
                            d>>=4;l>>=4;
                            if((l&0xf)!=transparent){
                                *p++=map16s[l&0xf];
                            } else {
                                *p++=map16s[d&0xf];
                            }
                        }
                    }
                    break;
                case SCREENMODE5: //400 x 300 x 8bit-colour or 424 x 240 x 8bit-colour 
                    {
                        uint8_t *p=(uint8_t *)HDMIlines[line_to_load];
                        uint8_t l,d;
                        int pp= (Line_dup)*vgaloop2;
                        for(int i=0; i<vgaloop2 ; i++){
                            l=LayerBuf[pp+i];d=DisplayBuf[pp+i];
                            if(l!=transparent){
                                *p++=l;
                                *p++=l;
                            } else {
                                *p++=d;
                                *p++=d;
                            }
                        }
                    }
                    break;
                default:
                }
            }
        }
    }
}

void MIPS32 __not_in_flash_func(HDMIloop0)(void){
    int last_line=2,load_line, line_to_load, line;
    while (1){
        if(v_scanline!=last_line){
            uint8_t transparent16=(uint8_t)transparent;
            uint8_t transparent16s=(uint8_t)transparents;
            last_line=v_scanline;
            load_line=line=v_scanline - (MODE_V_TOTAL_LINES - MODE_V_ACTIVE_LINES);
            if(HRes==vgaloop2)line>>=1;
            line_to_load = last_line & 1;
            uint8_t l,d,s;
            register unsigned char *dd;
            register unsigned char *ll;
            int pp;
            uint16_t *p=HDMIlines[line_to_load];
            if(load_line>=0 && load_line<MODE_V_ACTIVE_LINES){
                __dsb();
                switch(DISPLAY_TYPE){
                case SCREENMODE1: //720x400x2 colour
                    uint16_t *fcol=tilefcols+line/ytileheight*X_TILE, *bcol=tilebcols+line/ytileheight*X_TILE; //get the relevant tile
                    pp= line*vgaloop8;
                    dd=&DisplayBuf[pp];
                    ll=&LayerBuf[pp];
                    for(int i=0; i<vgaloop8 ; i++){
                        d=*dd | *ll;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        d>>=1;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        d>>=1;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        d>>=1;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        d>>=1;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        d>>=1;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        d>>=1;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        d>>=1;
                        *p++ = (d&0x1) ? fcol[i] : bcol[i];
                        ll++;dd++;
                    }
                    break;
                case SCREENMODE2: //360x200x16 colour with support for a top layer
                    {
                        pp= (line)*vgaloop4;
                        uint32_t *up=(uint32_t *)p;
                        dd=&DisplayBuf[pp];
                        ll=&LayerBuf[pp];
                        volatile register unsigned char *ss=&SecondLayer[pp];
                        if(ss==dd){
                            ss=ll;
                            transparent16s=transparent16;
                        }
                        for(int i=0; i<vgaloop4 ; i++){
                            l=ll[i];d=dd[i];s=ss[i];
                            if((s&0xf)!=transparent16s){
                                *up++=map16pairs[s&0xf];
                            } else {
                                if((l&0xf)!=transparent16){
                                    *up++=map16pairs[l&0xf];
                                } else {
                                    *up++=map16pairs[d&0xf];
                                }
                            }
                            d>>=4;l>>=4;s>>=4;
                            if((s&0xf)!=transparent16s){
                                *up++=map16pairs[s&0xf];
                            } else {
                                if((l&0xf)!=transparent16){
                                    *up++=map16pairs[l&0xf];
                                } else {
                                    *up++=map16pairs[d&0xf];
                                }
                            }
                        }
                    }
                    break;
                case SCREENMODE3: //720x400x16 colour
                    pp= line*vgaloop2;
                    for(int i=0;i<vgaloop2;i++){
                        d=DisplayBuf[pp+i];
                        l=LayerBuf[pp+i];
                        if((l&0xf)!=transparent16){
                            *p++=map16[l&0xf];
                        } else {
                            *p++=map16[d&0xf];
                        }
                        d>>=4;l>>=4;
                        if((l&0xf)!=transparent16){
                            *p++=map16[l&0xf];
                        } else {
                            *p++=map16[d&0xf];
                        }
                    }
                    break;
                case SCREENMODE4: //360x200xRGB555 colour
                    pp=line*vgaloop1;
                    uint16_t* d=(uint16_t *)&DisplayBuf[pp];
                    uint16_t* l=(uint16_t *)&LayerBuf[pp];
                    for(int i=0; i<vgaloop2 ; i++){
                        if(*l!=RGBtransparent){
                            *p++=*l;
                            *p++=*l;
                        } else {
                            *p++=*d;
                            *p++=*d;
                        }
                        l++;d++;
                    }
                    break;
                case SCREENMODE5: //360x200x256 colour
                    pp=line*vgaloop2;
                    dd=&DisplayBuf[pp];
                    ll=&LayerBuf[pp];
                    register unsigned char *ss=&SecondLayer[pp];
                    if(ss==dd){
                        ss=ll;
                        transparent16s=transparent16;
                    }
                    for(int i=0; i<vgaloop2 ; i++){
                        int d=dd[i];
                        int l=ll[i];
                        int s=ss[i];
                        if(s!=transparent16s){
                            *p++=map256[s];
                            *p++=map256[s];
                        } else {
                            if(l!=transparent16){
                                *p++=map256[l];
                                *p++=map256[l];
                            } else {
                                *p++=map256[d];
                                *p++=map256[d];
                            }
                        }
                    }
                    break;
                default:
                }
            }
        }
    }
}
void HDMICore(void){
    for(int i=0;i<256;i++)map256[i]=RGB555(MAP256DEF[i]);
    for(int i=0;i<16;i++){
        map16[i]=RGB555(MAP16DEF[i]);
        map16pairs[i]=map16[i] | (map16[i]<<16);
        map16d[i]=(RGB332(MAP16DEF[i])<<8) | RGB332(MAP16DEF[i]);
        map16q[i]=(RGB332(MAP16DEF[i])<<24) | (RGB332(MAP16DEF[i])<<16) | (RGB332(MAP16DEF[i])<<8) | RGB332(MAP16DEF[i]);
        map16s[i]=RGB332(MAP16DEF[i]);
    }
    if(Option.CPU_Speed==FreqXGA){
        MODE_H_SYNC_POLARITY=MODE_H_L_SYNC_POLARITY;
        MODE_ACTIVE_LINES=MODE_V_L_ACTIVE_LINES;
        MODE_ACTIVE_PIXELS=MODE_H_L_ACTIVE_PIXELS;
        MODE_V_TOTAL_LINES=MODE_V_L_TOTAL_LINES;
        MODE_H_ACTIVE_PIXELS=MODE_H_L_ACTIVE_PIXELS;
        MODE_H_FRONT_PORCH=MODE_H_L_FRONT_PORCH;
        MODE_H_SYNC_WIDTH=MODE_H_L_SYNC_WIDTH;
        MODE_H_BACK_PORCH=MODE_H_L_BACK_PORCH;
        MODE_V_SYNC_POLARITY=MODE_V_L_SYNC_POLARITY;
        MODE_V_ACTIVE_LINES=MODE_V_L_ACTIVE_LINES;
        MODE_V_FRONT_PORCH=MODE_V_L_FRONT_PORCH;
        MODE_V_SYNC_WIDTH=MODE_V_L_SYNC_WIDTH;
        MODE_V_BACK_PORCH=MODE_V_L_BACK_PORCH;
        MODE1SIZE=MODE1SIZE_L;
        MODE2SIZE=MODE2SIZE_L;
        MODE3SIZE=MODE3SIZE_L;
        MODE4SIZE=0L;
        MODE5SIZE=MODE5SIZE_L;
        PIXELS_PER_WORD=4;
    } else if(Option.CPU_Speed==Freq720P){
        MODE_H_SYNC_POLARITY=MODE_H_W_SYNC_POLARITY;
        MODE_ACTIVE_LINES=MODE_V_W_ACTIVE_LINES;
        MODE_ACTIVE_PIXELS=MODE_H_W_ACTIVE_PIXELS;
        MODE_V_TOTAL_LINES=MODE_V_W_TOTAL_LINES;
        MODE_H_ACTIVE_PIXELS=MODE_H_W_ACTIVE_PIXELS;
        MODE_H_FRONT_PORCH=MODE_H_W_FRONT_PORCH;
        MODE_H_SYNC_WIDTH=MODE_H_W_SYNC_WIDTH;
        MODE_H_BACK_PORCH=MODE_H_W_BACK_PORCH;
        MODE_V_SYNC_POLARITY=MODE_V_W_SYNC_POLARITY;
        MODE_V_ACTIVE_LINES=MODE_V_W_ACTIVE_LINES;
        MODE_V_FRONT_PORCH=MODE_V_W_FRONT_PORCH;
        MODE_V_SYNC_WIDTH=MODE_V_W_SYNC_WIDTH;
        MODE_V_BACK_PORCH=MODE_V_W_BACK_PORCH;
        MODE1SIZE=MODE1SIZE_W;
        MODE2SIZE=MODE2SIZE_W;
        MODE3SIZE=MODE3SIZE_W;
        MODE4SIZE=0L;
        MODE5SIZE=MODE5SIZE_W;
        PIXELS_PER_WORD=4;
    } else if(Option.CPU_Speed==FreqSVGA){
        MODE_H_SYNC_POLARITY=MODE_H_V_SYNC_POLARITY;
        MODE_ACTIVE_LINES=MODE_V_V_ACTIVE_LINES;
        MODE_ACTIVE_PIXELS=MODE_H_V_ACTIVE_PIXELS;
        MODE_V_TOTAL_LINES=MODE_V_V_TOTAL_LINES;
        MODE_H_ACTIVE_PIXELS=MODE_H_V_ACTIVE_PIXELS;
        MODE_H_FRONT_PORCH=MODE_H_V_FRONT_PORCH;
        MODE_H_SYNC_WIDTH=MODE_H_V_SYNC_WIDTH;
        MODE_H_BACK_PORCH=MODE_H_V_BACK_PORCH;
        MODE_V_SYNC_POLARITY=MODE_V_V_SYNC_POLARITY;
        MODE_V_ACTIVE_LINES=MODE_V_V_ACTIVE_LINES;
        MODE_V_FRONT_PORCH=MODE_V_V_FRONT_PORCH;
        MODE_V_SYNC_WIDTH=MODE_V_V_SYNC_WIDTH;
        MODE_V_BACK_PORCH=MODE_V_V_BACK_PORCH;
        MODE1SIZE=MODE1SIZE_V;
        MODE2SIZE=MODE2SIZE_V;
        MODE3SIZE=MODE3SIZE_V;
        MODE5SIZE=MODE5SIZE_V;
        PIXELS_PER_WORD=4;
    } else if(Option.CPU_Speed==Freq848){
        MODE_H_SYNC_POLARITY=MODE_H_8_SYNC_POLARITY;
        MODE_ACTIVE_LINES=MODE_V_8_ACTIVE_LINES;
        MODE_ACTIVE_PIXELS=MODE_H_8_ACTIVE_PIXELS;
        MODE_V_TOTAL_LINES=MODE_V_8_TOTAL_LINES;
        MODE_H_ACTIVE_PIXELS=MODE_H_8_ACTIVE_PIXELS;
        MODE_H_FRONT_PORCH=MODE_H_8_FRONT_PORCH;
        MODE_H_SYNC_WIDTH=MODE_H_8_SYNC_WIDTH;
        MODE_H_BACK_PORCH=MODE_H_8_BACK_PORCH;
        MODE_V_SYNC_POLARITY=MODE_V_8_SYNC_POLARITY;
        MODE_V_ACTIVE_LINES=MODE_V_8_ACTIVE_LINES;
        MODE_V_FRONT_PORCH=MODE_V_8_FRONT_PORCH;
        MODE_V_SYNC_WIDTH=MODE_V_8_SYNC_WIDTH;
        MODE_V_BACK_PORCH=MODE_V_8_BACK_PORCH;
        MODE1SIZE=MODE1SIZE_8;
        MODE2SIZE=MODE2SIZE_8;
        MODE3SIZE=MODE3SIZE_8;
        MODE5SIZE=MODE5SIZE_8;
        PIXELS_PER_WORD=4;
    } else if(Option.CPU_Speed==Freq400){
        MODE_H_SYNC_POLARITY=MODE_H_4_SYNC_POLARITY;
        MODE_ACTIVE_LINES=MODE_V_4_ACTIVE_LINES;
        MODE_ACTIVE_PIXELS=MODE_H_4_ACTIVE_PIXELS;
        MODE_V_TOTAL_LINES=MODE_V_4_TOTAL_LINES;
        MODE_H_ACTIVE_PIXELS=MODE_H_4_ACTIVE_PIXELS;
        MODE_H_FRONT_PORCH=MODE_H_4_FRONT_PORCH;
        MODE_H_SYNC_WIDTH=MODE_H_4_SYNC_WIDTH;
        MODE_H_BACK_PORCH=MODE_H_4_BACK_PORCH;
        MODE_V_SYNC_POLARITY=MODE_V_4_SYNC_POLARITY;
        MODE_V_ACTIVE_LINES=MODE_V_4_ACTIVE_LINES;
        MODE_V_FRONT_PORCH=MODE_V_4_FRONT_PORCH;
        MODE_V_SYNC_WIDTH=MODE_V_4_SYNC_WIDTH;
        MODE_V_BACK_PORCH=MODE_V_4_BACK_PORCH;
        MODE1SIZE=MODE1SIZE_4;
        MODE2SIZE=MODE2SIZE_4;
        MODE3SIZE=MODE3SIZE_4;
        MODE4SIZE=MODE3SIZE_4;
        MODE5SIZE=MODE5SIZE_4;
        PIXELS_PER_WORD=2;
    }  else {
        MODE_H_SYNC_POLARITY=MODE_H_S_SYNC_POLARITY;
        MODE_ACTIVE_LINES=MODE_V_S_ACTIVE_LINES;
        MODE_ACTIVE_PIXELS=MODE_H_S_ACTIVE_PIXELS;
        MODE_V_TOTAL_LINES=MODE_V_S_TOTAL_LINES;
        MODE_H_ACTIVE_PIXELS=MODE_H_S_ACTIVE_PIXELS;
        MODE_H_FRONT_PORCH=MODE_H_S_FRONT_PORCH;
        MODE_H_SYNC_WIDTH=MODE_H_S_SYNC_WIDTH;
        MODE_H_BACK_PORCH=MODE_H_S_BACK_PORCH;
        MODE_V_SYNC_POLARITY=MODE_V_S_SYNC_POLARITY;
        MODE_V_ACTIVE_LINES=MODE_V_S_ACTIVE_LINES;
        MODE_V_FRONT_PORCH=MODE_V_S_FRONT_PORCH;
        MODE_V_SYNC_WIDTH=MODE_V_S_SYNC_WIDTH;
        MODE_V_BACK_PORCH=MODE_V_S_BACK_PORCH;
        MODE1SIZE=MODE1SIZE_S;
        MODE2SIZE=MODE2SIZE_S;
        MODE3SIZE=MODE3SIZE_S;
        MODE4SIZE=MODE4SIZE_S;
        MODE5SIZE=MODE5SIZE_S;
        PIXELS_PER_WORD=2;
    }
    vgaloop1=MODE_H_ACTIVE_PIXELS;
    vgaloop2=MODE_H_ACTIVE_PIXELS/2;
    vgaloop4=MODE_H_ACTIVE_PIXELS/4;
    vgaloop8=MODE_H_ACTIVE_PIXELS/8;
    vgaloop16=MODE_H_ACTIVE_PIXELS/16;
    vgaloop32=MODE_H_ACTIVE_PIXELS/32;
    TRANSFER_COUNT=MODE_H_ACTIVE_PIXELS/PIXELS_PER_WORD;
    BLANKING_COUNT=MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH + MODE_V_BACK_PORCH;

    vblank_line_vsync_off[0] =       HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH;
    vblank_line_vsync_off[1] =       SYNC_V1_H1;
    vblank_line_vsync_off[2] =       HSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH;
    vblank_line_vsync_off[3] =       SYNC_V1_H0;
    vblank_line_vsync_off[4] =       HSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS);
    vblank_line_vsync_off[5] =       SYNC_V1_H1;
    vblank_line_vsync_off[6] =       HSTX_CMD_NOP;

    vblank_line_vsync_on[0] =    HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH;
    vblank_line_vsync_on[1] =    SYNC_V0_H1;
    vblank_line_vsync_on[2] =    HSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH;
    vblank_line_vsync_on[3] =    SYNC_V0_H0,
    vblank_line_vsync_on[4] =    HSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS);
    vblank_line_vsync_on[5] =    SYNC_V0_H1;
    vblank_line_vsync_on[6] =    HSTX_CMD_NOP;

    vactive_line[0] =    HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH;
    vactive_line[1] =    SYNC_V1_H1;
    vactive_line[2] =    HSTX_CMD_NOP;
    vactive_line[3] =    HSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH;
    vactive_line[4] =    SYNC_V1_H0;
    vactive_line[5] =    HSTX_CMD_NOP;
    vactive_line[6] =    HSTX_CMD_RAW_REPEAT | MODE_H_BACK_PORCH;
    vactive_line[7] =    SYNC_V1_H1;
    vactive_line[8] =    HSTX_CMD_TMDS       | MODE_H_ACTIVE_PIXELS;
        // Configure HSTX's TMDS encoder for RGB332
    hstx_ctrl_hw->expand_tmds =
        ((FullColour) ? 
            (29 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB   |
            4  << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
            2 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB   |
            4  << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
            7 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB   |
            4  << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB) 
            :
            (2  << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
            0  << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB   |
            2  << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
            29 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB   |
            1  << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
            26 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB));

    // Pixels (TMDS) come in 4 8-bit chunks. Control symbols (RAW) are an
    // entire 32-bit word.
    hstx_ctrl_hw->expand_shift =
        ((FullColour) ? 
            (2 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
            16 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
            1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
            0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB)
            :
            (4 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
            8 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
            1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
            0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB));


        // Serial output config: clock period of 5 cycles, pop from command
        // expander every 5 cycles, shift the output shiftreg by 2 every cycle.
    hstx_ctrl_hw->csr = 0;
    hstx_ctrl_hw->csr =
        HSTX_CTRL_CSR_EXPAND_EN_BITS |
        5u << HSTX_CTRL_CSR_CLKDIV_LSB |
        5u << HSTX_CTRL_CSR_N_SHIFTS_LSB |
        2u << HSTX_CTRL_CSR_SHIFT_LSB |
        HSTX_CTRL_CSR_EN_BITS; 

    // Note we are leaving the HSTX clock at the SDK default of 125 MHz; since
    // we shift out two bits per HSTX clock cycle, this gives us an output of
    // 250 Mbps, which is very close to the bit clock for 480p 60Hz (252 MHz).
    // If we want the exact rate then we'll have to reconfigure PLLs.

    // HSTX outputs 0 through 7 appear on GPIO 12 through 19.
    // Pinout on Pico DVI sock:
    //
    //   GP12 D0+  GP13 D0-
    //   GP14 CK+  GP15 CK-
    //   GP16 D2+  GP17 D2-
    //   GP18 D1+  GP19 D1-

    // Assign clock pair to two neighbouring pins:
    if(Option.HDMIclock & 1){
        hstx_ctrl_hw->bit[Option.HDMIclock] = HSTX_CTRL_BIT0_CLK_BITS;
        hstx_ctrl_hw->bit[Option.HDMIclock-1] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS;

    } else {
        hstx_ctrl_hw->bit[Option.HDMIclock] = HSTX_CTRL_BIT0_CLK_BITS;
        hstx_ctrl_hw->bit[Option.HDMIclock+1] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS;
    }
    int lane_to_output_bit[3];
    lane_to_output_bit[0]=Option.HDMId0;
    lane_to_output_bit[1]=Option.HDMId1;
    lane_to_output_bit[2]=Option.HDMId2;
        
    for (uint lane = 0; lane < 3; ++lane) {
        // For each TMDS lane, assign it to the correct GPIO pair based on the
        // desired pinout:
        int bit = lane_to_output_bit[lane];
        // Output even bits during first half of each HSTX cycle, and odd bits
        // during second half. The shifter advances by two bits each cycle.
        uint32_t lane_data_sel_bits =
            (lane * 10    ) << HSTX_CTRL_BIT0_SEL_P_LSB |
            (lane * 10 + 1) << HSTX_CTRL_BIT0_SEL_N_LSB;
        // The two halves of each pair get identical data, but one pin is inverted.
        if(bit & 1){
            hstx_ctrl_hw->bit[bit    ] = lane_data_sel_bits;
            hstx_ctrl_hw->bit[bit - 1] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS;
        } else {
            hstx_ctrl_hw->bit[bit    ] = lane_data_sel_bits;
            hstx_ctrl_hw->bit[bit + 1] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS;
        }
    }

    for (int i = 12; i <= 19; ++i) {
        gpio_set_function(i, 0); // HSTX
        gpio_set_drive_strength (i, GPIO_DRIVE_STRENGTH_8MA);
        gpio_set_slew_rate(i,GPIO_SLEW_RATE_FAST);
        gpio_set_input_enabled(i, false);
        gpio_set_pulls(i,false,false);
        gpio_set_input_hysteresis_enabled(i,false);
    }

    // Both channels are set up identically, to transfer a whole scanline and
    // then chain to the opposite channel. Each time a channel finishes, we
    // reconfigure the one that just finished, meanwhile the opposite channel
    // is already making progress.
    dma_channel_config c;
    c = dma_channel_get_default_config(DMACH_PING);
    channel_config_set_chain_to(&c, DMACH_PONG);
    channel_config_set_dreq(&c, DREQ_HSTX);
    dma_channel_configure(
        DMACH_PING,
        &c,
        &hstx_fifo_hw->fifo,
        vblank_line_vsync_off,
        count_of(vblank_line_vsync_off),
        false
    );
    c = dma_channel_get_default_config(DMACH_PONG);
    channel_config_set_chain_to(&c, DMACH_PING);
    channel_config_set_dreq(&c, DREQ_HSTX);
    dma_channel_configure(
        DMACH_PONG,
        &c,
        &hstx_fifo_hw->fifo,
        vblank_line_vsync_off,
        count_of(vblank_line_vsync_off),
        false
    );

    dma_hw->ints0 = (1u << DMACH_PING) | (1u << DMACH_PONG);
    dma_hw->inte0 = (1u << DMACH_PING) | (1u << DMACH_PONG);
    irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler0);
    irq_set_enabled(DMA_IRQ_0, true);

//    bus_ctrl_hw->priority = 1;
    dma_channel_start(DMACH_PING);
    if(Option.CPU_Speed==Freq480P || Option.CPU_Speed==Freq378P  || Option.CPU_Speed==Freq252P || Option.CPU_Speed==Freq400)HDMIloop0();
    else if(Option.CPU_Speed==Freq720P)HDMIloop1();
    else if(Option.CPU_Speed==FreqXGA)HDMIloop2();
    else HDMIloop3();
}
void settiles(void){
    if(DISPLAY_TYPE!=SCREENMODE1)return;
    if(FullColour){
        tilefcols=(uint16_t *)((uint32_t)FRAMEBUFFER+(MODE1SIZE*3));
        tilebcols=(uint16_t *)((uint32_t)FRAMEBUFFER+(MODE1SIZE*3)+(MODE1SIZE>>1));
        ytileheight=gui_font_height;
        Y_TILE=VRes/ytileheight;
        X_TILE=HRes/8;
        if(VRes % ytileheight)Y_TILE++;
            for(int x=0;x<X_TILE;x++){
            for(int y=0;y<Y_TILE;y++){
                tilefcols[y*X_TILE+x]=RGB555(gui_fcolour);
                tilebcols[y*X_TILE+x]=RGB555(gui_bcolour);
            }
        }
    } else {
        tilefcols_w=(uint8_t *)DisplayBuf+MODE1SIZE;
        tilebcols_w=tilefcols_w+(MODE_H_ACTIVE_PIXELS/8)*(MODE_V_ACTIVE_LINES/8); //minimum tilesize is 8x8
        memset(tilefcols_w,RGB332(gui_fcolour),(MODE_H_ACTIVE_PIXELS/8)*(MODE_V_ACTIVE_LINES/8)*sizeof(uint8_t));
        memset(tilebcols_w,RGB332(gui_bcolour),(MODE_H_ACTIVE_PIXELS/8)*(MODE_V_ACTIVE_LINES/8)*sizeof(uint8_t));
        ytileheight=(MediumRes? 12:24);
        X_TILE=MODE_H_ACTIVE_PIXELS/8;Y_TILE=MODE_V_ACTIVE_LINES/ytileheight;
    }
}

#endif
#else
#ifdef PICOMITE
#include "pico/multicore.h"
void __not_in_flash_func(UpdateCore)()
{
    systick_hw->csr = 0x5;
    systick_hw->rvr = 0x00FFFFFF;
    while(multicore_fifo_rvalid()) {
        multicore_fifo_pop_blocking();
    }

	while (true)
	{
		// data memory barrier
		__dmb();
        if (multicore_fifo_rvalid()) {
            int command=multicore_fifo_pop_blocking();
			if(command==3){
                uint8_t colour=(uint8_t)multicore_fifo_pop_blocking();
                uint32_t timer=(uint32_t)multicore_fifo_pop_blocking();
                uint64_t delaytime=0;
                if(timer)delaytime=time_us_64()+timer;
                mergerunning=true;
                while(1){
                    if (multicore_fifo_rvalid()){
                        int a;
                        if(((a=multicore_fifo_pop_blocking())==0xff)){
                            mergerunning=false;
                            break;
                        }
                    }
                    if(timer){
                        busy_wait_until(delaytime);
                        delaytime=time_us_64()+timer;
                    }
                    merge(colour);
                }
            } else if(command==2){
                uint8_t colour=(uint8_t)multicore_fifo_pop_blocking();
                merge(colour);
            } else if(command==4){
                int x1=multicore_fifo_pop_blocking();
                int y1=multicore_fifo_pop_blocking();
                int w=multicore_fifo_pop_blocking();
                int h=multicore_fifo_pop_blocking();
                uint8_t colour=(uint8_t)multicore_fifo_pop_blocking();
                blitmerge(x1,y1,w,h,colour);
			} else if(command==5){
                int x1=multicore_fifo_pop_blocking();
                int y1=multicore_fifo_pop_blocking();
                int w=multicore_fifo_pop_blocking();
                int h=multicore_fifo_pop_blocking();
                uint8_t colour=(uint8_t)multicore_fifo_pop_blocking();
                uint32_t timer=(uint32_t)multicore_fifo_pop_blocking();
                uint64_t delaytime=0;
                if(timer)delaytime=time_us_64()+timer;
                mergerunning=true;
                while(1){
                    if (multicore_fifo_rvalid()){
                        int a;
                        if(((a=multicore_fifo_pop_blocking())==0xff)){
                            mergerunning=false;
                            break;
                        }
                    }
                    if(timer){
                        busy_wait_until(delaytime);
                        delaytime=time_us_64()+timer;
                    }
                    blitmerge(x1,y1,w,h,colour);
                }
            } else if(command==1){ 
                uint8_t *s=(uint8_t *)multicore_fifo_pop_blocking();
                mutex_enter_blocking(&frameBufferMutex);			// lock the frame buffer
                copyframetoscreen(s,0,HRes-1,0,VRes-1,0);
                mutex_exit(&frameBufferMutex);
            }
        } 
    }

}
uint32_t core1stack[512];
#endif
#endif
#ifndef rp2350
void __no_inline_not_in_flash_func(modclock)(uint16_t speed){
       ssi_hw->ssienr=0;
       ssi_hw->baudr=0;
       ssi_hw->baudr=speed;
       ssi_hw->ssienr=1;
}
#else
#ifndef PICOMITEWEB
uint32_t testPSRAM(void){
    uint32_t *p=(uint32_t *)PSRAMbase;
    uint32_t *q=(uint32_t *)PSRAMbase;
    for(int i=0;i<65536;i++)*p++=i;
    __dmb();
    for(int i=0;i<65536;i++)if(*q++!=i) return 0;
    p=(uint32_t *)PSRAMbase;
    q=(uint32_t *)PSRAMbase;
    p[8*1024*1024/4-1]=0x12345678;
    __dmb();
    if(q[8*1024*1024/4-1]==0x12345678)return 6*1024*1024;
    else return 0;
}
#endif
#endif
lfs_t lfs;
lfs_dir_t lfs_dir;
struct lfs_info lfs_info;
void MIPS16 updatebootcount(void){
    lfs_file_t lfs_file;
    pico_lfs_cfg.block_count = (Option.FlashSize-RoundUpK4(TOP_OF_SYSTEM_FLASH)-(Option.modbuff ? 1024*Option.modbuffsize : 0))/4096;
    int err,boot_count=0;
 	    err= lfs_mount(&lfs, &pico_lfs_cfg);
    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
    if (err) {
        err=lfs_format(&lfs, &pico_lfs_cfg);
        err=lfs_mount(&lfs, &pico_lfs_cfg);
    }

    err=lfs_file_open(&lfs, &lfs_file, "bootcount", LFS_O_RDWR | LFS_O_CREAT);;
    int dt=get_fattime();
    err=lfs_setattr(&lfs, "bootcount", 'A', &dt,   4);
    err=lfs_file_read(&lfs, &lfs_file, &boot_count, sizeof(boot_count));;
    boot_count+=1;
    err=lfs_file_rewind(&lfs, &lfs_file);
    err=lfs_file_write(&lfs, &lfs_file, &boot_count, sizeof(boot_count));
    err=lfs_file_close(&lfs, &lfs_file);
}
/**
 * @brief Transforms input beginning with * into a corresponding RUN command.
 *
 * e.g.
 *   *foo              =>  RUN "foo"
 *   *"foo bar"        =>  RUN "foo bar"
 *   *foo --wombat     =>  RUN "foo", "--wombat"
 *   *foo "wom"        =>  RUN "foo", Chr$(34) + "wom" + Chr$(34)
 *   *foo "wom" "bat"  =>  RUN "foo", Chr$(34) + "wom" + Chr$(34) + " " + Chr$(34) + "bat" + Chr$(34)
 *   *foo --wom="bat"  =>  RUN "foo", "--wom=" + Chr$(34) + "bat" + Chr$(34)
 */
static void MIPS16 transform_star_command(char *input) {
    char *src = input;
    while (isspace((uint8_t)*src)) src++; // Skip leading whitespace.
    if (*src != '*') error("Internal fault");
    src++;

    // Trim any trailing whitespace from the input.
    char *end = input + strlen(input) - 1;
    while (isspace((uint8_t)*end)) *end-- = '\0';

    // Allocate extra space to avoid string overrun.
    char *tmp = (char *) GetTempMemory(STRINGSIZE + 32);
    strcpy(tmp, "RUN");
    char *dst = tmp + 3;

    if (*src == '"') {
        // Everything before the second quote is the name of the file to RUN.
        *dst++ = ' ';
        *dst++ = *src++; // Leading quote.
        while (*src && *src != '"') *dst++ = *src++;
        if (*src == '"') *dst++ = *src++; // Trailing quote.
    } else {
        // Everything before the first space is the name of the file to RUN.
        int count = 0;
        while (*src && !isspace((uint8_t)*src)) {
            if (++count == 1) {
                *dst++ = ' ';
                *dst++ = '\"';
            }
            *dst++ = *src++;
        }
        if (count) *dst++ = '\"';
    }

    while (isspace((uint8_t)*src)) src++; // Skip whitespace.

    // Anything else is arguments.
    if (*src) {
        *dst++ = ',';
        *dst++ = ' ';

        // If 'src' starts with double-quote then replace with: Chr$(34) +
        if (*src == '"') {
            memcpy(dst, "Chr$(34) + ", 11);
            dst += 11;
            src++;
        }

        *dst++ = '\"';

        // Copy from 'src' to 'dst'.
        while (*src) {
            if (*src == '"') {
                // Close current set of quotes to insert a Chr$(34)
                memcpy(dst, "\" + Chr$(34)", 12);
                dst += 12;

                // Open another set of quotes unless this was the last character.
                if (*(src + 1)) {
                    memcpy(dst, " + \"", 4);
                    dst += 4;
                }
                src++;
            } else {
                *dst++ = *src++;
            }
            if (dst - tmp >= STRINGSIZE) error("String too long");
        }

        // End with a double quote unless 'src' ended with one.
        if (*(src - 1) != '"') *dst++ = '\"';

        *dst = '\0';
    }

    if (dst - tmp >= STRINGSIZE) error("String too long");

    // Copy transformed string back into the input buffer.
    memcpy(input, tmp, STRINGSIZE);
    input[STRINGSIZE - 1] = '\0';

    ClearSpecificTempMemory(tmp);
}
#ifdef PICOMITEWEB
void WebConnect(void){
    if(*Option.SSID){
        if(*Option.ipaddress){
            cyw43_arch_enable_sta_mode();
            dhcp_stop(cyw43_state.netif);
            ip4_addr_t ipaddr, gateway, mask;
            ip4addr_aton(Option.ipaddress, &ipaddr);
            ip4addr_aton(Option.gateway, &gateway);
            ip4addr_aton(Option.mask, &mask);
            netif_set_addr( cyw43_state.netif,&ipaddr,&mask,&gateway);
        } else cyw43_arch_enable_sta_mode();
        if(*Option.hostname){
            MMPrintString(Option.hostname);
            netif_set_hostname(cyw43_state.netif, Option.hostname);
        }
        cyw43_wifi_pm(&cyw43_state, CYW43_NO_POWERSAVE_MODE);        
        MMPrintString(" connecting to WiFi...\r\n");
        if (cyw43_arch_wifi_connect_timeout_ms((char *)Option.SSID, (char *)(*Option.PASSWORD ? Option.PASSWORD : NULL), (*Option.PASSWORD ? CYW43_AUTH_WPA2_AES_PSK : CYW43_AUTH_OPEN), 30000)) {
            MMPrintString("failed to connect.\r\n");
            WIFIconnected=0;
        } else {
            char buff[STRINGSIZE]={0};
            sprintf(buff,"Connected %s\r\n",ip4addr_ntoa(netif_ip4_addr(netif_list)));
            MMPrintString(buff);
            WIFIconnected=1;
            open_tcp_server();
            if(!Option.disabletftp)cmd_tftp_server_init();
            if(Option.UDP_PORT)open_udp_server();
        }
    } else {
        cyw43_arch_enable_sta_mode();
        cyw43_wifi_pm(&cyw43_state, CYW43_NO_POWERSAVE_MODE);        
    }
    cyw43_wifi_pm(&cyw43_state, CYW43_DEFAULT_PM & ~0xf);
}
#endif

int MIPS16 main(){
    static int ErrorInPrompt;
    int i=0;
    char savewatchdog=false;
        i=watchdog_caused_reboot();
#ifdef rp2350
    restart_reason=powman_hw->chip_reset | i;
    rp2350a=(*((io_ro_32*)(SYSINFO_BASE + SYSINFO_PACKAGE_SEL_OFFSET)) & 1);
#else
    restart_reason=vreg_and_chip_reset_hw->chip_reset | i;
#endif
    if(_excep_code == SOFT_RESET || _excep_code == SCREWUP_TIMEOUT )restart_reason=0xFFFFFFFF;
    if((_excep_code == WATCHDOG_TIMEOUT) & i) restart_reason=0xFFFFFFFE;
    if((_excep_code == POSSIBLE_WATCHDOG) & i)restart_reason=0xFFFFFFFD;
    LoadOptions();
#ifdef rp2350
    if(rom_get_last_boot_type()==BOOT_TYPE_FLASH_UPDATE)restart_reason=0xFFFFFFFC;
#else
    if(restart_reason==0x10001 || restart_reason==0x101)restart_reason=0xFFFFFFFC;
#endif
    uint32_t excep=_excep_code;
    if(  Option.Baudrate == 0 ||
        !(Option.Tab==2 || Option.Tab==3 || Option.Tab==4 ||Option.Tab==8) ||
        !(Option.Autorun>=0 && Option.Autorun<=MAXFLASHSLOTS+1) ||
        Option.CPU_Speed<MIN_CPU || Option.CPU_Speed>MAX_CPU ||
        Option.PROG_FLASH_SIZE!=MAX_PROG_SIZE ||
        (Option.heartbeatpin==0 && Option.NoHeartbeat==0) ||
        !(Option.Magic==MagicKey)
        ){
        ResetAllFlash();              // init the options if this is the very first startup
        _excep_code=0;
        watchdog_enable(1, 1);
        while(1);
    }
#ifndef HDMI
    if(Option.VGA_HSYNC==0){
        Option.VGA_HSYNC=21;
        Option.VGA_BLUE=24;
        SaveOptions();
    }
#else
    if(!(Option.CPU_Speed==Freq720P || Option.CPU_Speed==Freq378P || Option.CPU_Speed==Freq252P || Option.CPU_Speed==Freq848  || Option.CPU_Speed==Freq400 || Option.CPU_Speed==FreqSVGA || Option.CPU_Speed==Freq480P|| Option.CPU_Speed==FreqXGA )){
        Option.CPU_Speed=Freq480P;
        SaveOptions();
    }
#endif
    m_alloc(M_PROG);                                           // init the variables for program memory
    LibMemory = (uint8_t *)flash_libmemory;
    uSec(100);
    if(_excep_code == RESET_CLOCKSPEED) {
#ifdef PICOMITEVGA
#ifdef HDMI
        Option.CPU_Speed=Freq480P;              // init the options if this is the very first startup
#else
        Option.CPU_Speed=Freq252P;              // init the options if this is the very first startup
#endif
#else
        Option.CPU_Speed=200000;              // init the options if this is the very first startup
#endif
        SaveOptions();
        _excep_code=INVALID_CLOCKSPEED;
        watchdog_enable(1, 1);
        while(1);
    } else {
        _excep_code=RESET_CLOCKSPEED;
        watchdog_enable(1000, 1);
    }
#ifdef rp2350
    if(!rp2350a){
        if(!Option.AllPins){
            Option.AllPins=true;
            SaveOptions();
        }
    }
#endif
    vreg_disable_voltage_limit ();
#ifdef rp2350
//    volatile uint32_t *qmi_m0_timing=(uint32_t *)0x400d000c;
//    volatile uint32_t *qmi_m1_timing=(uint32_t *)0x400d0020;
#endif
    if(Option.CPU_Speed<=200000)vreg_set_voltage(VREG_VOLTAGE_1_15);
//    else if(Option.CPU_Speed>200000 && Option.CPU_Speed<=300000 )vreg_set_voltage(VREG_VOLTAGE_1_25);  // Std default @ boot is 1_10
    else if(Option.CPU_Speed>200000  && Option.CPU_Speed<=320000 )vreg_set_voltage(VREG_VOLTAGE_1_30);  // Std default @ boot is 1_10
#ifdef rp2350
    else if(Option.CPU_Speed>320000  && Option.CPU_Speed<=360000 )vreg_set_voltage(VREG_VOLTAGE_1_40);  // Std default @ boot is 1_10
    else vreg_set_voltage(VREG_VOLTAGE_1_60);  // Std default @ boot is 1_10
#else
    else vreg_set_voltage(VREG_VOLTAGE_1_30); 
#endif
    sleep_ms(10);
#ifdef rp2350
#define QMI_COOLDOWN 30    // 0xc0000000 [31:30] COOLDOWN     (0x1) Chip select cooldown period
#define QMI_PAGEBREAK 28        // 0x30000000 [29:28] PAGEBREAK    (0x0) When page break is enabled, chip select will...
#define QMI_SELECT_SETUP 25        // 0x02000000 [25]    SELECT_SETUP (0) Add up to one additional system clock cycle of setup...
#define QMI_SELECT_HOLD 23        // 0x01800000 [24:23] SELECT_HOLD  (0x0) Add up to three additional system clock cycles of active...
#define QMI_MAX_SELECT 17        // 0x007e0000 [22:17] MAX_SELECT   (0x00) Enforce a maximum assertion duration for this window's...
#define QMI_MIN_DESELECT 12        // 0x0001f000 [16:12] MIN_DESELECT (0x00) After this window's chip select is deasserted, it...
#define QMI_RXDELAY 8          // 0x00000700 [10:8]  RXDELAY      (0x0) Delay the read data sample timing, in units of one half...
#define QMI_CLKDIV 0           // 0x000000ff [7:0]   CLKDIV       (0x04) Clock divisor
    pads_qspi_hw->io[0]=0x67;
    pads_qspi_hw->io[1]=0x67;
    pads_qspi_hw->io[2]=0x67;
    pads_qspi_hw->io[3]=0x6B;
    pads_qspi_hw->io[4]=0x6B;
    pads_qspi_hw->io[5]=0x6B;
    qmi_hw->m[1].timing = (1<<QMI_COOLDOWN) | (2<<QMI_PAGEBREAK) | (3<<QMI_SELECT_HOLD) | (18<<QMI_MAX_SELECT) | (4<<QMI_MIN_DESELECT) | (6<<QMI_RXDELAY) | (5<<QMI_CLKDIV);
    if(Option.CPU_Speed<=324000)qmi_hw->m[1].timing = (1<<QMI_COOLDOWN) | (2<<QMI_PAGEBREAK) | (3<<QMI_SELECT_HOLD) | (18<<QMI_MAX_SELECT) | (4<<QMI_MIN_DESELECT) | (4<<QMI_RXDELAY) | (4<<QMI_CLKDIV);
    if(Option.CPU_Speed<=288000)qmi_hw->m[0].timing = 0x40006202;
    if(Option.CPU_Speed<=150000)qmi_hw->m[1].timing = 0x60006102;
    sleep_ms(2);
#endif
    set_sys_clock_khz(Option.CPU_Speed, false);
#ifdef rp2350
    qmi_hw->m[1].timing = (1<<QMI_COOLDOWN) | (2<<QMI_PAGEBREAK) | (3<<QMI_SELECT_HOLD) | (18<<QMI_MAX_SELECT) | (4<<QMI_MIN_DESELECT) | (6<<QMI_RXDELAY) | (5<<QMI_CLKDIV);
    if(Option.CPU_Speed<=324000)qmi_hw->m[1].timing = (1<<QMI_COOLDOWN) | (2<<QMI_PAGEBREAK) | (3<<QMI_SELECT_HOLD) | (18<<QMI_MAX_SELECT) | (4<<QMI_MIN_DESELECT) | (4<<QMI_RXDELAY) | (4<<QMI_CLKDIV);
    if(Option.CPU_Speed<=288000)qmi_hw->m[0].timing = 0x40006202;
    if(Option.CPU_Speed<=150000)qmi_hw->m[1].timing = 0x60006102;

    sleep_ms(2);
#endif
    PWM_FREQ=44100;
    pico_get_unique_board_id_string (id_out,12);
#ifdef rp2350
#ifndef PICOMITEWEB
    if(Option.PSRAM_CS_PIN){
        gpio_set_function(PinDef[Option.PSRAM_CS_PIN].GPno, GPIO_FUNC_XIP_CS1); // CS for PSRAM
        xip_ctrl_hw->ctrl|=XIP_CTRL_WRITABLE_M1_BITS;
        if(!(PSRAMsize=testPSRAM())){
            Option.PSRAM_CS_PIN=0;
            SaveOptions();
        }
    }
#endif
#endif
    if(clock_get_hz(clk_usb)!=48000000){
        ResetAllFlash();              // init the options if this is the very first startup
        _excep_code=INVALID_CLOCKSPEED;
        watchdog_enable(1, 1);
        while(1);
    }
    clock_configure(
        clk_adc,
        0,                                                // No glitchless mux
        CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, // System PLL on AUX mux
        Option.CPU_Speed * 1000,                               // Input frequency
        ADC_CLK_SPEED                                 // Output (must be same as no divider)
    );
    SetADCFreq(500000);
    adc_clk_div=adc_hw->div;
    systick_hw->csr = 0x5;
    systick_hw->rvr = 0x00FFFFFF;
#ifdef PICOMITE
	mutex_init( &frameBufferMutex );						// create a mutex to lock frame buffer
#endif
#ifndef rp2350
    if(Option.CPU_Speed<=200000)modclock(2);
#else
#ifdef HDMI
    if(FullColour || MediumRes){
        clock_configure(
            clk_hstx,
            0,                                                // No glitchless mux
            CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, // System PLL on AUX mux
            Option.CPU_Speed * 1000,                               // Input frequency
            Option.CPU_Speed * (Option.CPU_Speed==Freq378P ? 332:500) // Output (must be same as no divider)
        );
    }
    if(Option.CPU_Speed==FreqSVGA){ //adjust the size of the heap
        framebuffersize=400*300*2;
        heap_memory_size=HEAP_MEMORY_SIZE-framebuffersize+320*240*2;
        FRAMEBUFFER=AllMemory+heap_memory_size+256;
    }
    if(Option.CPU_Speed==Freq848){ //adjust the size of the heap
        framebuffersize=424*240*2;
        heap_memory_size=HEAP_MEMORY_SIZE-framebuffersize+320*240*2;
        FRAMEBUFFER=AllMemory+heap_memory_size+256;
    }
#endif 
#endif
    uSec(100);
    hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
    _excep_code=excep;
#ifdef PICOMITEVGA
#ifndef HDMI
    if(Option.CPU_Speed == Freq252P || Option.CPU_Speed == Freq480P  || Option.CPU_Speed == Freq848  || Option.CPU_Speed == Freq400   || Option.CPU_Speed == FreqSVGA )QVGA_CLKDIV= 2;
    else if(Option.CPU_Speed == 378000)QVGA_CLKDIV= 3;
    else QVGA_CLKDIV= 1;
#ifdef rp2350
if(Option.CPU_Speed==Freq848){ //adjust the size of the heap
    framebuffersize=424*240*2;
    heap_memory_size=HEAP_MEMORY_SIZE-framebuffersize+320*240*2;
    FRAMEBUFFER=AllMemory+heap_memory_size+256;
    MODE1SIZE=MODE1SIZE_8;
    MODE2SIZE=MODE2SIZE_8;
    MODE2SIZE=MODE2SIZE_8;
    MODE2SIZE=MODE2SIZE_8;
    MODE2SIZE=MODE2SIZE_8;
    HRes=848;
}
if(Option.CPU_Speed==FreqSVGA){ //adjust the size of the heap
    framebuffersize=400*300*2;
    heap_memory_size=HEAP_MEMORY_SIZE-framebuffersize+320*240*2;
    FRAMEBUFFER=AllMemory+heap_memory_size+256;
    MODE1SIZE=MODE1SIZE_V;
    MODE2SIZE=MODE2SIZE_V;
    MODE3SIZE=MODE3SIZE_V;
    MODE5SIZE=MODE5SIZE_V;
    HRes=800;
    VRes=600;
}

#endif
#endif
#endif
    systick_hw->csr = 0x5;
    systick_hw->rvr = 0x00FFFFFF;
    ticks_per_second = Option.CPU_Speed*1000;
    // The serial clock won't vary from this point onward, so we can configure
    // the UART etc.
#ifndef USBKEYBOARD
    stdio_set_translate_crlf(&stdio_usb, false);
#endif
    LoadOptions();
	stdio_init_all();
    adc_init();
    adc_set_temp_sensor_enabled(true);
    mSecTimer=time_us_64()/1000;
    add_repeating_timer_us(-1000, timer_callback, NULL, &timer);
    InitReservedIO();
    ClearExternalIO();
    ConsoleRxBufHead = 0;
    ConsoleRxBufTail = 0;
    ConsoleTxBufHead = 0;
    ConsoleTxBufTail = 0;
    PromptFC=gui_fcolour=Option.DefaultFC;
    PromptBC=gui_bcolour=Option.DefaultBC;
    InitHeap(true);              										// initilise memory allocation
    uSecFunc(1000);
    disable_interrupts_pico();
    enable_interrupts_pico();
    mSecTimer=time_us_64()/1000;
    DISPLAY_TYPE = Option.DISPLAY_TYPE;
    // negative timeout means exact delay (rather than delay between callbacks)
	OptionErrorSkip = false;
#ifndef USBKEYBOARD
    if(!(Option.SerialConsole==1 || Option.SerialConsole==2) || Option.Telnet==-1) {
        uint64_t t=time_us_64();
        while(1){
            if(tud_cdc_connected())break;
            if(time_us_64()-t>5000000)break;
        }
    }
    initKeyboard();
#endif
	InitBasic();
#ifndef PICOMITEVGA
#ifndef PICOCALC
    InitDisplaySSD();
#endif
    InitDisplaySPI(0);
#ifndef PICOCALC
    InitDisplayI2C(0);
    InitDisplayVirtual();
#endif
    InitTouch();
#ifndef PICOCALC
    if(Option.BackLightLevel)setBacklight(Option.BackLightLevel, 0);
#else
    uSec(300000);
    if(Option.BackLightLevel)setBacklight(Option.BackLightLevel);
#endif
#endif
    ErrorInPrompt = false;
    exception_set_exclusive_handler(HARDFAULT_EXCEPTION,sigbus);
    exception_set_exclusive_handler(SVCALL_EXCEPTION,sigbus);
    exception_set_exclusive_handler(PENDSV_EXCEPTION,sigbus);
    exception_set_exclusive_handler(NMI_EXCEPTION ,sigbus);
    exception_set_exclusive_handler(SYSTICK_EXCEPTION,sigbus);
    while((i=getConsole())!=-1){}
    
#ifdef PICOMITEVGA
//        bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;
    #ifdef HDMI
//    bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS | BUSCTRL_BUS_PRIORITY_PROC1_BITS;
        multicore_launch_core1_with_stack(HDMICore,core1stack,512);
        core1stack[0]=0x12345678;
        uSec(1000);
        #else
    #ifdef rp2350
    piomap[QVGA_PIO_NUM]=(uint64_t)((uint64_t)1<<(uint64_t)PinDef[Option.VGA_BLUE].GPno);
    piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)(PinDef[Option.VGA_BLUE].GPno+1));
    piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)(PinDef[Option.VGA_BLUE].GPno+2));
    piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)(PinDef[Option.VGA_BLUE].GPno+3));
    piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)PinDef[Option.VGA_HSYNC].GPno);
    piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)(PinDef[Option.VGA_HSYNC].GPno+1));
    if(Option.audio_i2s_bclk){
        piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)PinDef[Option.audio_i2s_data].GPno);
        piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)PinDef[Option.audio_i2s_bclk].GPno);
        piomap[QVGA_PIO_NUM]|=(uint64_t)((uint64_t)1<<(uint64_t)(PinDef[Option.audio_i2s_bclk].GPno+1));
    }
    #endif        
        X_TILE=Option.X_TILE;
        Y_TILE=Option.Y_TILE;
        ytileheight=(X_TILE==80 || X_TILE==106)? 12 : 16;
        bus_ctrl_hw->priority=0x100;
        multicore_launch_core1_with_stack(QVgaCore,core1stack,512);
        core1stack[0]=0x12345678;
        memset((void *)WriteBuf, 0, 38400);
    #endif
    ResetDisplay();
    ClearScreen(Option.DefaultBC);
    #else
    #ifdef PICOMITE
        bus_ctrl_hw->priority=0x100;
        multicore_launch_core1_with_stack(UpdateCore,core1stack,2048);
        core1stack[0]=0x12345678;
    #endif
#endif
        strcpy((char *)banner,MES_SIGNON); 
#ifdef rp2350
    #ifdef PICOMITEVGA
        #ifdef HDMI
            #ifdef USBKEYBOARD
                banner[32]=(rp2350a?'A':'B');
            #else
                banner[28]=(rp2350a?'A':'B');
            #endif
        #else
            #ifdef USBKEYBOARD
                banner[31]=(rp2350a?'A':'B');
            #else
                banner[27]=(rp2350a?'A':'B');
            #endif
        #endif
    #else
        #ifdef USBKEYBOARD
            banner[28]=(rp2350a?'A':'B');
        #else
            #ifdef PICOMITEWEB
                banner[23]=(rp2350a?'A':'B');
            #else
                banner[24]=(rp2350a?'A':'B');
            #endif
        #endif
    #endif
#endif
    if(!(_excep_code == RESTART_NOAUTORUN || _excep_code == INVALID_CLOCKSPEED || _excep_code == SCREWUP_TIMEOUT || _excep_code == WATCHDOG_TIMEOUT || (_excep_code==POSSIBLE_WATCHDOG && watchdog_caused_reboot()))){
        if(Option.Autorun==0 ){
            if(!(_excep_code == RESET_COMMAND || _excep_code == SOFT_RESET)){
                MMPrintString((char *)banner); // print sign on message
                MMPrintString((char *)COPYRIGHT); // print sign on message
            }
        } else {
            if(Option.Autorun!=MAXFLASHSLOTS+1){
                ProgMemory=(unsigned char *)(flash_target_contents+(Option.Autorun-1)*MAX_PROG_SIZE);
            }
            if(*ProgMemory != 0x01 ) {
                MMPrintString((char *)banner);
                MMPrintString((char *)COPYRIGHT); // print sign on message
            }
        }
    }
    memset(inpbuf,0,STRINGSIZE);
    WatchdogSet = false;
    if(_excep_code == INVALID_CLOCKSPEED) {
        MMPrintString("\r\nInvalid clock speed - reset to default\r\n");
        restart_reason=0xFFFFFFFF;
    }
    if(_excep_code == SCREWUP_TIMEOUT) {
        MMPrintString("\r\nCommand timeout\r\n");
        restart_reason=0xFFFFFFFF;
    }
    if(restart_reason==0xFFFFFFFE) {
        WatchdogSet = true;                                 // remember if it was a watchdog timeout
        MMPrintString("\r\nMMBasic Watchdog timeout\r\n");
    }
    if(restart_reason==0xFFFFFFFD){
        MMPrintString("\r\nHW Watchdog timeout\r\n");
        WatchdogSet = true;                                 // remember if it was a watchdog timeout
        _excep_code=0;
    }
    if(restart_reason==0xFFFFFFFC) {
        WatchdogSet = true;                                 // remember if it was a watchdog timeout
        MMPrintString("\rFirmware updated\r\n");
    }
    savewatchdog=WatchdogSet;
    if(noRTC){
        noRTC=0;
        Option.RTC=0;
        SaveOptions();
        MMPrintString("RTC not found, OPTION RTC AUTO disabled\r\n");
    }
    if(noI2C){
        noI2C=0;
        Option.KeyboardConfig=NO_KEYBOARD;
        SaveOptions();
        MMPrintString("I2C Keyboard not found, OPTION KEYBOARD disabled\r\n");
    }
    updatebootcount();
    *tknbuf = 0;
     ContinuePoint = nextstmt;                               // in case the user wants to use the continue command
#ifdef USBKEYBOARD
	clearrepeat();
     for(int i=0;i<4;i++){
        memset((void *)&HID[i],0,sizeof(struct s_HID));
        HID[i].report_requested=true;
    }
//    USB_bus_reset();
    hcd_port_reset(BOARD_TUH_RHPORT);
    uSec(10000); //wait for any hub to power up
    hcd_port_reset_end(BOARD_TUH_RHPORT);
    tuh_init(BOARD_TUH_RHPORT);
    USBenabled=true;
#else
    initMouse0(0);
#endif
#ifdef rp2350
    if(PSRAMsize){MMPrintString("Total of ");PInt(PSRAMsize/(1024*1024));MMPrintString(" Mbytes PSRAM available\r\n");}
    #if defined(PICOMITEVGA) && !defined(HMDI)
        start_i2s(QVGA_PIO_NUM,1);
    #else
        start_i2s(2,1);
    #endif
#else
    start_i2s(QVGA_PIO_NUM,1);
#endif

   
	if(setjmp(mark) != 0) {
     // we got here via a long jump which means an error or CTRL-C or the program wants to exit to the command prompt
        FlashLoad = 0;
//        LoadOptions();
#ifdef USBKEYBOARD
	    clearrepeat();
#endif	    
        ScrewUpTimer = 0;
        ProgMemory=(uint8_t *)flash_progmemory;
        ContinuePoint = nextstmt;                               // in case the user wants to use the continue command
		*tknbuf = 0;											// we do not want to run whatever is in the token buffer
		optionangle=1.0;
        useoptionangle=false;
        savewatchdog = WatchdogSet = false;
		char *ptr = findvar((unsigned char *)"MM.ENDLINE$", V_NOFIND_NULL);
        if(ptr && *ptr){
            CurrentLinePtr=0;
            memcpy(inpbuf,ptr,*ptr+1);
            *ptr=0;
            MtoC(inpbuf);
            *ptr=0;
            tokenise(true);
            goto autorun;
        }
    } else {
        if(*ProgMemory == 0x01 ) ClearVars(0,true);
        else {
            ClearProgram(true);
        }
    #ifdef PICOMITEWEB
    if (cyw43_arch_init()==0) {
        startupcomplete=1;
        WebConnect();
    }
    #endif
#ifdef PICOMITE
    SPIatRisk=((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel) && Option.SD_CLK_PIN==0);
#endif
        PrepareProgram(true);
        if(FindSubFun((unsigned char *)"MM.STARTUP", 0) >= 0) {
            ExecuteProgram((unsigned char *)"MM.STARTUP\0");
            memset(inpbuf,0,STRINGSIZE);
        }
        if(Option.Autorun && _excep_code != RESTART_DOAUTORUN) {
            ClearRuntime(true);
            PrepareProgram(true);
            if(*ProgMemory == 0x01 ){
                memset(tknbuf,0,STRINGSIZE);
                unsigned short tkn=GetCommandValue((unsigned char *)"RUN");
                tknbuf[0] = (tkn & 0x7f ) + C_BASETOKEN;
                tknbuf[1] = (tkn >> 7) + C_BASETOKEN; //tokens can be 14-bit
                goto autorun;
            }  else {
                Option.Autorun=0;
                SaveOptions();
            }       
        }
    }
    while(1) {
    if(Option.DISPLAY_CONSOLE) {
        SetFont(PromptFont);
        gui_fcolour = PromptFC;
        gui_bcolour = PromptBC;
        if(CurrentX != 0) MMPrintString("\r\n");                    // prompt should be on a new line
    }
        MMAbort = false;
        BreakKey = BREAK_KEY;
        EchoOption = true;
        g_LocalIndex = 0;                                             // this should not be needed but it ensures that all space will be cleared
        ClearTempMemory();                                          // clear temp string space (might have been used by the prompt)
        CurrentLinePtr = NULL;                                      // do not use the line number in error reporting
        if(MMCharPos > 1) MMPrintString("\r\n");                    // prompt should be on a new line
        while(Option.PIN && !IgnorePIN) {
            _excep_code = PIN_RESTART;
            if(Option.PIN == 99999999)                              // 99999999 is permanent lockdown
                MMPrintString("Console locked, press enter to restart: ");
            else
                MMPrintString("Enter PIN or 0 to restart: ");
            MMgetline(0, (char *)inpbuf);
            if(Option.PIN == 99999999) SoftReset();
            if(*inpbuf != 0) {
                uSec(3000000);
                i = atoi((char *)inpbuf);
                if(i == 0) SoftReset();
                if(i == Option.PIN) {
                    IgnorePIN = true;
                    break;
                }
            }
        }
        if(_excep_code!=POSSIBLE_WATCHDOG)_excep_code = 0;
        PrepareProgram(false);
        if(!ErrorInPrompt && FindSubFun((unsigned char *)"MM.PROMPT", 0) >= 0) {
            ErrorInPrompt = true;
            ExecuteProgram((unsigned char *)"MM.PROMPT\0");
            MMPromptPos=MMCharPos-1;    //Save length of prompt
        } else{
            MMPrintString("> ");                                    // print the prompt
            MMPromptPos=2;    //Save length of prompt
        }    
        ErrorInPrompt = false;
        EditInputLine();
        //InsertLastcmd(inpbuf);                                  // save in case we want to edit it later
        if(!*inpbuf) continue;                                      // ignore an empty line
        char *p=(char *)inpbuf;
        skipspace(p);
//        executelocal(p);
        if(strlen(p)==2 && p[1]==':'){
            if(toupper(*p)=='A')strcpy(p,"drive \"a:\"");
            if(toupper(*p)=='B')strcpy(p,"drive \"b:\"");
        }
        if(*p=='*' && p[1]!='('){ //shortform RUN command so convert to a normal version
                transform_star_command((char *)inpbuf);
                p = (char *)inpbuf;
        }
        multi=false;
        tokenise(true);                                             // turn into executable code
autorun:
        i=0;
        WatchdogSet=savewatchdog;
        CommandToken tkn=commandtbl_decode(tknbuf);
        if(tkn==GetCommandValue((unsigned char *)"RUN") || tkn==GetCommandValue((unsigned char *)"EDIT") || tkn==GetCommandValue((unsigned char *)"AUTOSAVE"))i=1;
        if (setjmp(jmprun) != 0) {
            PrepareProgram(false);
            CurrentLinePtr = 0;
        }
        ExecuteProgram(tknbuf);                                     // execute the line straight away
        if(i){
            cmdline=NULL;
            do_end(false);
            longjmp(mark, 1);												// jump back to the input prompt
        }
        else {
            memset(inpbuf,0,STRINGSIZE);
	        longjmp(mark, 1);												// jump back to the input prompt
        }
	}
}
void stripcomment(char *p){
    char *q=p;
    int toggle=0;
    while(*q){
        if(*q=='\'' && toggle==0){
            *q=0;
            break;
        }
        if(*q=='"')toggle^=1;
        q++;
    }
}

// takes a pointer to RAM containing a program (in clear text) and writes it to memory in tokenised format
void MIPS16 SaveProgramToFlash(unsigned char *pm, int msg) {
    unsigned char *p, fontnbr, prevchar = 0, buf[STRINGSIZE];
    unsigned short endtoken, tkn;
    int nbr, i, j, n, SaveSizeAddr;
    bool continuation=false;
    multi=false;
    uint32_t storedupdates[MAXCFUNCTION], updatecount=0, realflashsave;
    initFonts();
#ifdef rp2350
    __dsb();
#endif
#ifdef USBKEYBOARD
	clearrepeat();
#endif	
    memcpy(buf, tknbuf, STRINGSIZE);                                // save the token buffer because we are going to use it
    FlashWriteInit(PROGRAM_FLASH);
    flash_range_erase(realflashpointer, MAX_PROG_SIZE);
    j=MAX_PROG_SIZE/4;
    int *pp=(int *)(flash_progmemory);
        while(j--)if(*pp++ != 0xFFFFFFFF){
            enable_interrupts_pico();
            error("Flash erase problem");
        }
    nbr = 0;
    // this is used to count the number of bytes written to flash
    while(*pm) {
contloop:
        if(continuation){
            p=&inpbuf[strlen((char *)inpbuf)];
            continuation=false;
        }
        else p = inpbuf;
        while(!(*pm == 0 || *pm == '\r' || (*pm == '\n' && prevchar != '\r'))) {
            if(*pm == TAB) {
                do {*p++ = ' ';
                    if((p - inpbuf) >= MAXSTRLEN) goto exiterror;
                } while((p - inpbuf) % 2);
            } else {
                if(isprint((uint8_t)*pm)) {
                    *p++ = *pm;
                    if((p - inpbuf) >= MAXSTRLEN) goto exiterror;
                }
            }
            prevchar = *pm++;
        }
        if(*pm) prevchar = *pm++;                                   // step over the end of line char but not the terminating zero
        *p = 0;                                                     // terminate the string in inpbuf

        if(*inpbuf == 0 && (*pm == 0 || (!isprint((uint8_t)*pm) && pm[1] == 0))) break; // don't save a trailing newline
        if(inpbuf[strlen((char *)inpbuf)-1]==Option.continuation && inpbuf[strlen((char *)inpbuf)-2]==' ' && Option.continuation){
            continuation=true;
            inpbuf[strlen((char *)inpbuf)-2]=0; //strip the continuation character
            goto contloop;
        }
        tokenise(false);                                            // turn into executable code
        p = tknbuf;
        while(!(p[0] == 0 && p[1] == 0)) {
            FlashWriteByte(*p++); nbr++;

            if((int)((char *)realflashpointer - (uint32_t)PROGSTART) >= MAX_PROG_SIZE - 5)  goto exiterror;
        }
        FlashWriteByte(0); nbr++;                              // terminate that line in flash
    }
    FlashWriteByte(0);
    FlashWriteAlign();                                            // this will flush the buffer and step the flash write pointer to the next word boundary
    // now we must scan the program looking for CFUNCTION/CSUB/DEFINEFONT statements, extract their data and program it into the flash used by  CFUNCTIONs
     // programs are terminated with two zero bytes and one or more bytes of 0xff.  The CFunction area starts immediately after that.
     // the format of a CFunction/CSub/Font in flash is:
     //   Unsigned Int - Address of the CFunction/CSub in program memory (points to the token representing the "CFunction" keyword) or NULL if it is a font
     //   Unsigned Int - The length of the CFunction/CSub/Font in bytes including the Offset (see below)
     //   Unsigned Int - The Offset (in words) to the main() function (ie, the entry point to the CFunction/CSub).  Omitted in a font.
     //   word1..wordN - The CFunction/CSub/Font code
     // The next CFunction/CSub/Font starts immediately following the last word of the previous CFunction/CSub/Font
    int firsthex=1;
    realflashsave= realflashpointer;
    p = (unsigned char *)flash_progmemory;                                              // start scanning program memory
    while(*p != 0xff) {
    	nbr++;
        if(*p == 0) p++;                                            // if it is at the end of an element skip the zero marker
        if(*p == 0) break;                                          // end of the program
        if(*p == T_NEWLINE) {
            CurrentLinePtr = p;
            p++;                                                    // skip the newline token
        }
        if(*p == T_LINENBR) p += 3;                                 // step over the line number

        skipspace(p);
        if(*p == T_LABEL) {
            p += p[1] + 2;                                          // skip over the label
            skipspace(p);                                           // and any following spaces
        }
        tkn=p[0] & 0x7f;
        tkn |= ((unsigned short)(p[1] & 0x7f)<<7);
        if(tkn == cmdCSUB || tkn == GetCommandValue((unsigned char *)"DefineFont")) {      // found a CFUNCTION, CSUB or DEFINEFONT token
            if(tkn == GetCommandValue((unsigned char *)"DefineFont")) {
             endtoken = GetCommandValue((unsigned char *)"End DefineFont");
             p+=2;                                                // step over the token
             skipspace(p);
             if(*p == '#') p++;
             fontnbr = getint(p, 1, FONT_TABLE_SIZE);
                                                 // font 6 has some special characters, some of which depend on font 1
             if(fontnbr == 1 || fontnbr == 6 || fontnbr == 7) {
                enable_interrupts_pico();
                error("Cannot redefine fonts 1, 6 or 7");
             }
             realflashpointer+=4;
             skipelement(p);                                     // go to the end of the command
             p--;
            } else {
                endtoken = GetCommandValue((unsigned char *)"End CSub");
                realflashpointer+=4;
                fontnbr = 0;
                firsthex=0;
                p++;
            }
             SaveSizeAddr = realflashpointer;                                // save where we are so that we can write the CFun size in here
             realflashpointer+=4;
             p++;
             skipspace(p);
             if(!fontnbr) { //process CSub 
                 if(!isnamestart((uint8_t)*p)){
                    enable_interrupts_pico();
                    error("Function name");
                 }  
                 do { p++; } while(isnamechar((uint8_t)*p));
                 skipspace(p);
                 if(!(isxdigit((uint8_t)p[0]) && isxdigit((uint8_t)p[1]) && isxdigit((uint8_t)p[2]))) {
                     skipelement(p);
                     p++;
                    if(*p == T_NEWLINE) {
                        CurrentLinePtr = p;
                        p++;                                        // skip the newline token
                    }
                    if(*p == T_LINENBR) p += 3;                     // skip over a line number
                 }
             }
             do {
                 while(*p && *p != '\'') {
                     skipspace(p);
                     n = 0;
                     for(i = 0; i < 8; i++) {
                         if(!isxdigit((uint8_t)*p)) {
                            enable_interrupts_pico();
                            error("Invalid hex word");
                         }
                         if((int)((char *)realflashpointer - (uint32_t)PROGSTART) >= MAX_PROG_SIZE - 5) goto exiterror;
                         n = n << 4;
                         if(*p <= '9')
                             n |= (*p - '0');
                         else
                             n |= (toupper(*p) - 'A' + 10);
                         p++;
                     }
                     realflashpointer+=4;
                     skipspace(p);
                     if(firsthex){
                    	 firsthex=0;
                    	 if(((n>>16) & 0xff) < 0x20){
                            enable_interrupts_pico();
                            error("Can't define non-printing characters");
                         }
                     }
                 }
                 // we are at the end of a embedded code line
                 while(*p) p++;                                      // make sure that we move to the end of the line
                 p++;                                                // step to the start of the next line
                 if(*p == 0) {
                     enable_interrupts_pico();
                     error("Missing END declaration");
                 }
                 if(*p == T_NEWLINE) {
                     CurrentLinePtr = p;
                     p++;                                            // skip the newline token
                 }
                 if(*p == T_LINENBR) p += 3;                         // skip over the line number
                 skipspace(p);
                tkn=p[0] & 0x7f;
                tkn |= ((unsigned short)(p[1] & 0x7f)<<7);
             } while(tkn != endtoken);
             storedupdates[updatecount++]=realflashpointer - SaveSizeAddr - 4;
         }
         while(*p) p++;                                              // look for the zero marking the start of the next element
     }
    realflashpointer = realflashsave ;
    updatecount=0;
    p = (unsigned char *)flash_progmemory;                                              // start scanning program memory
     while(*p != 0xff) {
     	nbr++;
         if(*p == 0) p++;                                            // if it is at the end of an element skip the zero marker
         if(*p == 0) break;                                          // end of the program
         if(*p == T_NEWLINE) {
             CurrentLinePtr = p;
             p++;                                                    // skip the newline token
         }
         if(*p == T_LINENBR) p += 3;                                 // step over the line number

         skipspace(p);
         if(*p == T_LABEL) {
             p += p[1] + 2;                                          // skip over the label
             skipspace(p);                                           // and any following spaces
         }
         tkn=p[0] & 0x7f;
         tkn |= ((unsigned short)(p[1] & 0x7f)<<7);
         if(tkn == cmdCSUB || tkn == GetCommandValue((unsigned char *)"DefineFont")) {      // found a CFUNCTION, CSUB or DEFINEFONT token
         if(tkn == GetCommandValue((unsigned char *)"DefineFont")) {      // found a CFUNCTION, CSUB or DEFINEFONT token
             endtoken = GetCommandValue((unsigned char *)"End DefineFont");
             p+=2;                                                // step over the token
             skipspace(p);
             if(*p == '#') p++;
             fontnbr = getint(p, 1, FONT_TABLE_SIZE);
                                                 // font 6 has some special characters, some of which depend on font 1
             if(fontnbr == 1 || fontnbr == 6 || fontnbr == 7) {
                 enable_interrupts_pico();
                 error("Cannot redefine fonts 1, 6, or 7");
             }

             //FlashWriteWord(fontnbr - 1);                        // a low number (< FONT_TABLE_SIZE) marks the entry as a font
             // B31 = 1 now marks entry as font.
             FlashWriteByte(fontnbr - 1);
             FlashWriteByte(0x00);  
             FlashWriteByte(0x00);
             FlashWriteByte(0x80);    
           

             skipelement(p);                                     // go to the end of the command
             p--;
         } else {
             endtoken = GetCommandValue((unsigned char *)"End CSub");
             FlashWriteWord((unsigned int)(p-flash_progmemory));               // if a CFunction/CSub save a relative pointer to the declaration
             fontnbr = 0;
             p++;
         }
            SaveSizeAddr = realflashpointer;                                // save where we are so that we can write the CFun size in here
             FlashWriteWord(storedupdates[updatecount++]);                        // leave this blank so that we can later do the write
             p++;
             skipspace(p);
             if(!fontnbr) {
                 if(!isnamestart((uint8_t)*p))  {
                     enable_interrupts_pico();
                     error("Function name");
                 }
                 do { p++; } while(isnamechar(*p));
                 skipspace(p);
                 if(!(isxdigit(p[0]) && isxdigit(p[1]) && isxdigit(p[2]))) {
                     skipelement(p);
                     p++;
                    if(*p == T_NEWLINE) {
                        CurrentLinePtr = p;
                        p++;                                        // skip the newline token
                    }
                    if(*p == T_LINENBR) p += 3;                     // skip over a line number
                 }
             }
             do {
                 while(*p && *p != '\'') {
                     skipspace(p);
                     n = 0;
                     for(i = 0; i < 8; i++) {
                         if(!isxdigit(*p)) {
                            enable_interrupts_pico();
                            error("Invalid hex word");
                         }
                         if((int)((char *)realflashpointer - (uint32_t)PROGSTART) >= MAX_PROG_SIZE - 5) goto exiterror;
                         n = n << 4;
                         if(*p <= '9')
                             n |= (*p - '0');
                         else
                             n |= (toupper(*p) - 'A' + 10);
                         p++;
                     }

                     FlashWriteWord(n);
                     skipspace(p);
                 }
                 // we are at the end of a embedded code line
                 while(*p) p++;                                      // make sure that we move to the end of the line
                 p++;                                                // step to the start of the next line
                 if(*p == 0) {
                    enable_interrupts_pico();
                    error("Missing END declaration");
                 }
                 if(*p == T_NEWLINE) {
                    CurrentLinePtr = p;
                    p++;                                        // skip the newline token
                 }
                 if(*p == T_LINENBR) p += 3;                     // skip over a line number
                 skipspace(p);
                tkn=p[0] & 0x7f;
                tkn |= ((unsigned short)(p[1] & 0x7f)<<7);
              } while(tkn != endtoken);
         }
         while(*p) p++;                                              // look for the zero marking the start of the next element
     }
     FlashWriteWord(0xffffffff);                                // make sure that the end of the CFunctions is terminated with an erased word
     FlashWriteClose();                                              // this will flush the buffer and step the flash write pointer to the next word boundary
    if(msg) {                                                       // if requested by the caller, print an informative message
        if(MMCharPos > 1) MMPrintString("\r\n");                    // message should be on a new line
        MMPrintString("Saved ");
        IntToStr((char *)tknbuf, nbr + 3, 10);
        MMPrintString((char *)tknbuf);
        MMPrintString(" bytes\r\n");
    }
    memcpy(tknbuf, buf, STRINGSIZE);                                // restore the token buffer in case there are other commands in it
//    initConsole();
#ifdef USBKEYBOARD
	clearrepeat();
#endif
    enable_interrupts_pico();
    return;

    // we only get here in an error situation while writing the program to flash
    exiterror:
        FlashWriteByte(0); FlashWriteByte(0); FlashWriteByte(0);    // terminate the program in flash
        FlashWriteClose();
        error("Not enough memory");
}
 

#ifdef __cplusplus
}
#endif

/// \end:uart_advanced[]
