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

custom.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. 

************************************************************************************************************************/
/**
* @file Custom.c
* @author Geoff Graham, Peter Mather
* @brief Source for PIO, JSON and WEB MMBasic commands and function
*/
/**
 * @cond
 * The following section will be excluded from the documentation.
 */
#include <stdio.h>

#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
#include "hardware/dma.h"
#include "hardware/structs/bus_ctrl.h"
#include "hardware/structs/dma.h"
#include "hardware/irq.h"
#include "hardware/pwm.h"
#ifdef PICOMITEWEB
#define CJSON_NESTING_LIMIT 100
#include "cJSON.h"
#endif
#define STATIC static

/*************************************************************************************************************************
**************************************************************************************************************************
IMPORTANT:
This module is empty and should be used for your special functions and commands.  In the standard distribution this file  
will never be changed, so your code should be safe here.  You should avoid placing commands and functions in other files as
they may be changed and you would then need to re insert your changes in a new release of the source.

**************************************************************************************************************************
**************************************************************************************************************************/


/********************************************************************************************************************************************
 custom commands and functions
 each function is responsible for decoding a command
 all function names are in the form cmd_xxxx() (for a basic command) or fun_xxxx() (for a basic function) so, if you want to search for the
 function responsible for the NAME command look for cmd_name

 There are 4 items of information that are setup before the command is run.
 All these are globals.

 int cmdtoken	This is the token number of the command (some commands can handle multiple
				statement types and this helps them differentiate)

 unsigned char *cmdline	This is the command line terminated with a zero unsigned char and trimmed of leading
				spaces.  It may exist anywhere in memory (or even ROM).

 unsigned char *nextstmt	This is a pointer to the next statement to be executed.  The only thing a
				command can do with it is save it or change it to some other location.

 unsigned char *CurrentLinePtr  This is read only and is set to NULL if the command is in immediate mode.

 The only actions a command can do to change the program flow is to change nextstmt or
 execute longjmp(mark, 1) if it wants to abort the program.

 ********************************************************************************************************************************************/
/*
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "pico/stdlib.h"
#include "hardware/irq.h"
#include "hardware/claim.h"
#define PIO_NUM(pio) ((pio) == pio0 ? 0 : (pio1 ? 1 :2))
#define CLKMIN ((Option.CPU_Speed*125)>>13)
#define CLKMAX (Option.CPU_Speed *1000)

#include "hardware/pio.h"
#include "hardware/pio_instructions.h"
char *pioRXinterrupts[4][3]={0};
char *pioTXinterrupts[4][3]={0};
uint8_t pioTXlast[4][3]={0};
char *DMAinterruptRX=NULL;
char *DMAinterruptTX=NULL;
uint32_t dma_rx_chan = PIO_RX_DMA;
uint32_t dma_tx_chan = PIO_TX_DMA;
uint32_t dma_rx_chan2 = PIO_RX_DMA2;
uint32_t dma_tx_chan2 = PIO_TX_DMA2;
int dma_tx_pio;
int dma_tx_sm;
int dma_rx_pio;
int dma_rx_sm;
#ifdef PICOMITE
#ifdef rp2350
bool PIO0=true;
bool PIO1=true;
bool PIO2=true;
#else
bool PIO0=true;
bool PIO1=true;
bool PIO2=false;
#endif
#endif
#ifdef PICOMITEVGA
#ifdef rp2350
#ifdef HDMI
bool PIO0=true;
#else
bool PIO0=false;
#endif
bool PIO1=true;
bool PIO2=true;
#else
bool PIO0=false;
bool PIO1=true;
bool PIO2=false;
#endif
#endif
#ifdef PICOMITEWEB
#ifdef rp2350
bool PIO0=true;
bool PIO1=false;
bool PIO2=true;
#else
bool PIO0=true;
bool PIO1=false;
bool PIO2=false;
#endif
extern void setwifi(unsigned char *tp);
volatile bool TCPreceived=false;
char *TCPreceiveInterrupt=NULL;
#endif
#ifdef rp2350
uint64_t piomap[2]={0};
#endif
#define MAXLABEL 16
static int sidepins=0,sideopt=0,sidepinsdir=0, PIOlinenumber=0, PIOstart=0, p_wrap=31, p_wrap_target=0;
static int delaypossible=5;
static int checksideanddelay=0;
int dirOK=2;
static int *instructions=NULL;
static char *labelsfound=NULL, *labelsneeded=NULL;
int piointerrupt=0;
uint8_t nextline[4]={0};
static int pioinuse=99;
extern bool PIO2, PIO1, PIO0;
extern void on_pwm_wrap(void);
PIO pioi2s;
uint8_t i2ssm;
//#if defined( PICOMITEVGA) && !defined(HDMI)
#include "PicoMiteI2S.pio.h"
//#endif
static inline uint32_t pio_sm_calc_wrap(uint wrap_target, uint wrap) {
    uint32_t calc=0;
//    valid_params_if(PIO, wrap < PIO_INSTRUCTION_COUNT);
//    valid_params_if(PIO, wrap_target < PIO_INSTRUCTION_COUNT);
        return  (calc & ~(PIO_SM0_EXECCTRL_WRAP_TOP_BITS | PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) |
                (wrap_target << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) |
                (wrap << PIO_SM0_EXECCTRL_WRAP_TOP_LSB);
}
int calcsideanddelay(char *p, int sidepins, int maxdelaybits){
        int data=0;
        char *pp;
        if((pp=fstrstr(p,"side "))){
                pp+=5;
                skipspace(pp);
                char *ss=pp;
                char save=0;
                if((*ss>='0' && *ss<='9') || *ss=='&' ){
                        char *ppp=ss;
                        if(*ss=='&'){
                                if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax");
                                ppp+=2;
                        } 
                        while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;}
                        if(*ppp){
                                save=*ppp;
                                *ppp=',';
                        }
                        data=(int)(getint((unsigned char *)ss,0,((1<<sidepins)-1))<<(8+maxdelaybits));
                        if(sideopt)data|=0x1000;
                        *ppp=save;
                } else error("Syntax");
        }
        if((pp=fstrstr(p,"["))){
                pp++;
                char *s=strstr(pp,"]");
                *s=' ';
                data|=(getint((unsigned char *)pp,0,(1<<maxdelaybits)-1))<<8;
        }
        return data;
}
int getirqnum(char *p){
        int data=0;
        char *pp=p;
        int rel=0;
#ifdef rp2350
        int prev=0,next=0;
#endif
        skipspace(pp);
        char *ppp=pp;
        char save=0;
        while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;}
        if(*ppp){
                save=*ppp;
                *ppp=',';
        }
        data=(int)getint((unsigned char *)pp,0,7);
        if(*ppp==',')*ppp=save;
        if((pp=fstrstr(p," rel")) && (pp[4]==0 || pp[4]==' ' || pp[4]==';'))rel=1;
#ifdef rp2350
        if((pp=fstrstr(p," prev")) && (pp[5]==0 || pp[5]==' ' || pp[5]==';'))prev=1;
        if((pp=fstrstr(p," next")) && (pp[5]==0 || pp[5]==' ' || pp[5]==';'))next=1;
        if(next)data|=0b11;
        if(prev)data|=0b01;
#endif
        if(rel){
                data|=0x10;
        }
        return data;
}
void pio_init(int pior, int sm, uint32_t pinctrl, uint32_t execctrl, uint32_t shiftctrl, int start, float clock, bool sideout, bool setout, bool outout){
        pio_sm_config mypio=pio_get_default_sm_config();
#ifdef rp2350
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ? pio0: pio1);
#endif
        clock=(float)Option.CPU_Speed*1000.0/clock;
        int sidebase=(pinctrl & 0b111110000000000)>>10;
        int setbase=(pinctrl &       0b1111100000)>>5;
        int outbase=(pinctrl &            0b11111);
        int sidecount=(pinctrl & 0xE0000000)>>29;
        int setcount=(pinctrl & 0x1C000000)>>26;
        int outcount=(pinctrl & 0x3F00000)>>20;
//        int inbase=(pinctrl & 0xF8000)>>15;
        int opt=(execctrl>>30) & 1;
        mypio.clkdiv = (uint32_t) (clock * (1 << 16));
        mypio.execctrl=execctrl;
        mypio.shiftctrl=shiftctrl;
        mypio.pinctrl=pinctrl;
        #ifdef rp2350
        #ifdef PICOMITEWEB
                for(int i = 1; i < (NBRPINS) ; i++) {
        #else
                for(int i = 1; i < (rp2350a ? 44:NBRPINS) ; i++) {
        #endif
        #else
        for(int i = 1; i < (NBRPINS) ; i++) {
        #endif
                if(CheckPin(i, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED)) {    // don't reset invalid or boot reserved pins
                        gpio_set_input_enabled(PinDef[i].GPno, true);
                }
        }
        if(sidecount && sideout)pio_sm_set_consecutive_pindirs(pio, sm, sidebase, sidecount-opt, true);
        if(outcount && outout)pio_sm_set_consecutive_pindirs(pio, sm, outbase, outcount, true);
        if(setcount && setout)pio_sm_set_consecutive_pindirs(pio, sm, setbase, setcount, true);
        pio_sm_set_config(pio, sm, &mypio);
        pio_sm_init(pio, sm, start, &mypio);
        pio_sm_clear_fifos(pio,sm);
}
int checkblock(char *p){
        int data=0;
        char *pp;
        if((pp=fstrstr(p,"IFFULL"))){
                if(!(pp[6]==' ' || pp[6]==0))error("Syntax");
                data=0b1000000;
        }
        if((pp=fstrstr(p,"NOBLOCK"))){
                if(!(pp[7]==' ' || pp[7]==0))error("Syntax");
                return data;
        }
        if((pp=fstrstr(p,"BLOCK"))){
                if(!(pp[5]==' ' || pp[5]==0))error("Syntax");
                data=0b100000;
        }
        return data;
}
extern uint I2SOff;
extern void start_vga_i2s(void);
extern void start_i2s(int pio, int sm);

void start_i2s(int pior, int sm){
    if(!Option.audio_i2s_bclk)return;
    i2ssm=sm;
#ifdef rp2350
        pioi2s = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        pioi2s = (pior==0 ? pio0: pio1);
#endif
#if !defined(PICOMITEVGA) || defined(HDMI)
#ifdef rp2350
        if(PinDef[Option.audio_i2s_bclk].GPno+1>31 || PinDef[Option.audio_i2s_data].GPno>31)pio_set_gpio_base(pioi2s,16);
#endif
        I2SOff = pio_add_program(pioi2s, &i2s_program);

#endif
        gpio_set_input_enabled(PinDef[Option.audio_i2s_data].GPno, true);
	gpio_set_input_enabled(PinDef[Option.audio_i2s_bclk].GPno, true);
	gpio_set_input_enabled(PinDef[Option.audio_i2s_bclk].GPno+1, true);
#ifdef rp2350
        gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno  , pior==0 ? GPIO_FUNC_PIO0 : (pior==1? GPIO_FUNC_PIO1 : GPIO_FUNC_PIO2));
        gpio_set_function(PinDef[Option.audio_i2s_data].GPno  , pior==0 ? GPIO_FUNC_PIO0 : (pior==1? GPIO_FUNC_PIO1 : GPIO_FUNC_PIO2));
        gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno+1, pior==0 ? GPIO_FUNC_PIO0 : (pior==1? GPIO_FUNC_PIO1 : GPIO_FUNC_PIO2));
#else
        gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno  , pior==0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1);
        gpio_set_function(PinDef[Option.audio_i2s_data].GPno  , pior==0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1);
        gpio_set_function(PinDef[Option.audio_i2s_bclk].GPno+1, pior==0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1);
#endif
        ExtCfg(Option.audio_i2s_bclk, EXT_BOOT_RESERVED, 0);
        ExtCfg(Option.audio_i2s_data, EXT_BOOT_RESERVED, 0);
        ExtCfg(PINMAP[PinDef[Option.audio_i2s_bclk].GPno+1], EXT_BOOT_RESERVED, 0);

	// prepare default PIO program config
	pio_sm_config cfg = i2s_program_get_default_config(I2SOff);

	// map state machine's OUT and MOV pins	
	sm_config_set_out_pins(&cfg, PinDef[Option.audio_i2s_data].GPno, 1);

	// set sideset pins (BCLK and LCLK)
	sm_config_set_sideset_pins(&cfg, PinDef[Option.audio_i2s_bclk].GPno);
        sm_config_set_sideset(&cfg,2, false, false);

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

	// PIO clock divider
        float clockdiv=(Option.CPU_Speed*1000.0f)/(float)(44100*128);
//        pio_sm_set_clkdiv(pioi2s,i2ssm,clockdiv);
        sm_config_set_clkdiv(&cfg, clockdiv);
        sm_config_set_out_shift(&cfg,false,false,false);
        sm_config_set_in_shift(&cfg,false,false,false);

// initialize state machine
	pio_sm_init(pioi2s, sm, I2SOff, &cfg);
	pio_sm_set_consecutive_pindirs(pioi2s, sm, PinDef[Option.audio_i2s_data].GPno, 1, true);
	pio_sm_set_consecutive_pindirs(pioi2s, sm, PinDef[Option.audio_i2s_bclk].GPno, 2, true);
        pio_sm_set_enabled(pioi2s, sm, true);

        AUDIO_SLICE=Option.AUDIO_SLICE;
        AUDIO_WRAP=(Option.CPU_Speed*10)/441  - 1 ;
        pwm_set_wrap(AUDIO_SLICE, AUDIO_WRAP);
        pwm_clear_irq(AUDIO_SLICE);
        irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
        irq_set_enabled(PWM_IRQ_WRAP, true);
        irq_set_priority(PWM_IRQ_WRAP,255);
	pwm_set_enabled(AUDIO_SLICE, true); 
	if(pior==2)PIO2=false;
        else if(pior==1)PIO1=false;
        else PIO0=false;
}
int getGPpin(unsigned char *pinarg, int pio, int base){
	char code;
        int pin;
	if(!(code=codecheck(pinarg)))pinarg+=2;
	pin = getinteger(pinarg);
	if(!code)pin=codemap(pin);
        if(IsInvalidPin(pin)) error("Invalid pin");
        if((ExtCurrentConfig[pin] == EXT_PIO0_OUT && pio!=0) ||
                (ExtCurrentConfig[pin] == EXT_PIO1_OUT && pio!=1)
#ifdef rp2350
                || (ExtCurrentConfig[pin] == EXT_PIO2_OUT && pio!=2)
#endif
                || ExtCurrentConfig[pin] == EXT_BOOT_RESERVED
        ) error("Pin in use");
        if(PinDef[pin].GPno<base || PinDef[pin].GPno>base+31)error("Pin out of range for base %");
        return PinDef[pin].GPno;

}

/*  @endcond */
void MIPS16 cmd_pio(void){
    unsigned char *tp;
    #ifdef rp2350
    int dims[MAXDIM]={0};
    #else
    short dims[MAXDIM]={0};
    #endif
    tp = checkstring(cmdline, (unsigned char *)"EXECUTE");
    if(tp){
        int i;
        getargs(&tp, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",");
        if((argc & 0x01) == 0) error("Syntax");
        if(argc<5)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int sm=getint(argv[2],0,3);
        for(i = 4; i < argc; i += 2) {
            pio_sm_exec(pio, sm, getint(argv[i],0,65535));
        }
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"WRITE");
    if(tp){
        int i=6;
        getargs(&tp, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",");
        if((argc & 0x01) == 0) error("Syntax");
        if(argc<5)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int sm=getint(argv[2],0,3);
        int count=getint(argv[4],0,MAX_ARG_COUNT-3);
        while(count--) {
            pio_sm_put_blocking(pio, sm, getint(argv[i],0,(int64_t)0xFFFFFFFF));
            i+=2;
        }
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"DMA RX");
    if(tp){
        getargs(&tp, 13, (unsigned char *)",");
        if(checkstring(argv[0],(unsigned char *)"OFF")){
                dma_hw->abort = ((1u << dma_rx_chan2) | (1u << dma_rx_chan));
                if(dma_channel_is_busy(dma_rx_chan))dma_channel_abort(dma_rx_chan);
                if(dma_channel_is_busy(dma_rx_chan2))dma_channel_abort(dma_rx_chan2);
                return;
        }
        if(DMAinterruptRX || dma_channel_is_busy(dma_rx_chan)|| dma_channel_is_busy(dma_rx_chan2)) {
                dma_hw->abort = ((1u << dma_rx_chan2) | (1u << dma_rx_chan));
                if(dma_channel_is_busy(dma_rx_chan))dma_channel_abort(dma_rx_chan);
                if(dma_channel_is_busy(dma_rx_chan2))dma_channel_abort(dma_rx_chan2);
        }
        if(argc<7)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int sm=getint(argv[2],0,3);
        dma_rx_pio=pior;
        dma_rx_sm=sm;
        int nbr=getint(argv[4],0,0xFFFFFFFF);
        uint32_t s_nbr=nbr;
        static uint32_t *a1int=NULL;
        int64_t *aint=NULL;
        int toarraysize=parseintegerarray(argv[6],&aint,4,1,dims, true);
        a1int=(uint32_t *)aint;
        if(argc>=9 && *argv[8]){
                if(nbr==0)error("Interrupt incopmpatible with continuous running");
                DMAinterruptRX=(char *)GetIntAddress(argv[8]);
                InterruptUsed=true;
        }
        int dmasize=DMA_SIZE_32;
        if(argc>=11 && *argv[10]){
                dmasize=getinteger(argv[10]);
                if(!(dmasize==8 || dmasize==16 || dmasize==32))error("Invalid transfer size");
                if(dmasize==8)dmasize=DMA_SIZE_8;
                else if(dmasize==16)dmasize=DMA_SIZE_16;
                else if(dmasize==32)dmasize=DMA_SIZE_32;
        }
        dma_channel_config c = dma_channel_get_default_config(dma_rx_chan);
        channel_config_set_read_increment(&c, false);
        channel_config_set_transfer_data_size(&c, dmasize);
        if(dma_rx_pio==2)channel_config_set_dreq(&c, 20+sm);
        else channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true));
        channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false));
        if(argc==13){
                int size=getinteger(argv[12]);
                if(!(size==1 || size==2 || size==4 || size==8 || size==16 || size==32 || size==64 || size==128 || size==256 || size==512 || size==1024 || size== 2048 || size==4096 || size==8192 || size==16384 || size==32768))error("Not power of 2");
                if(size!=1){
                        int i=0,j=size;
                        if(((uint32_t)a1int & (j-1)) && nbr==0)error("Data alignment error");
                        while(j>>=1)i++;
                        i+=dmasize;
                        if((1<<i)>(toarraysize*8))error("Array size");
                         if(nbr==0){
                                nbr=size;
                                dma_channel_config c2 = dma_channel_get_default_config(dma_rx_chan2); //Get configurations for control channel
                                channel_config_set_transfer_data_size(&c2, DMA_SIZE_32); //Set control channel data transfer size to 32 bits
                                channel_config_set_read_increment(&c2, false); //Set control channel read increment to false
                                channel_config_set_write_increment(&c2, false); //Set control channel write increment to false
                                channel_config_set_dreq(&c2, 0x3F);
                                dma_channel_configure(dma_rx_chan2,
                                        &c2,
                                        &dma_hw->ch[dma_rx_chan].al2_write_addr_trig,
                                        &a1int,
                                        1,
                                        false); //Configure control channel  
                        }
                       if(s_nbr!=0)channel_config_set_ring(&c,true,i);
                        channel_config_set_write_increment(&c, true);
                } else channel_config_set_write_increment(&c, false);
        } else {
                if((nbr<<dmasize)>(toarraysize*8))error("Array size");
                channel_config_set_write_increment(&c, true);
        } 
        if(s_nbr==0) channel_config_set_chain_to(&c, dma_rx_chan2); //When this channel completes, it will trigger the channel indicated by chain_to
        dma_channel_configure(dma_rx_chan,
                &c,
                a1int,        // Destination pointer
                &pio->rxf[sm],      // Source pointer
                nbr, // Number of transfers
                (s_nbr==0 ? false :true)                // Start immediately
        );
        if(s_nbr==0) dma_start_channel_mask(1u << dma_rx_chan2);
        pio_sm_restart(pio, sm);
        pio_sm_set_enabled(pio, sm, true);
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"DMA TX");
    if(tp){
        getargs(&tp, 13, (unsigned char *)",");
        if(checkstring(argv[0],(unsigned char *)"OFF")){
                dma_hw->abort = ((1u << dma_tx_chan2) | (1u << dma_tx_chan));
                if(dma_channel_is_busy(dma_tx_chan))dma_channel_abort(dma_tx_chan);
                if(dma_channel_is_busy(dma_tx_chan2))dma_channel_abort(dma_tx_chan2);
                return;
        }
        if(DMAinterruptTX || dma_channel_is_busy(dma_tx_chan) || dma_channel_is_busy(dma_tx_chan2)){
                dma_hw->abort = ((1u << dma_tx_chan2) | (1u << dma_tx_chan));
                if(dma_channel_is_busy(dma_tx_chan))dma_channel_abort(dma_tx_chan);
                if(dma_channel_is_busy(dma_tx_chan2))dma_channel_abort(dma_tx_chan2);
        }
        if(argc<7)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int sm=getint(argv[2],0,3);
        dma_tx_pio=pior;
        dma_tx_sm=sm;
        uint32_t nbr=getint(argv[4],0,0xFFFFFFFF);
        uint32_t s_nbr=nbr;
        static uint32_t *a1int=NULL;
        int64_t *aint=NULL;
        int toarraysize=parseintegerarray(argv[6],&aint,4,1,dims, true);
        a1int=(uint32_t *)aint;
        if(argc>=9 && *argv[8]){
                if(nbr==0)error("Interrupt incopmpatible with continuous running");
                DMAinterruptTX=(char *)GetIntAddress(argv[8]);
                InterruptUsed=true;
        }
        int dmasize=DMA_SIZE_32;
        if(argc>=11 && *argv[10]){
                dmasize=getinteger(argv[10]);
                if(!(dmasize==8 || dmasize==16 || dmasize==32))error("Invalid transfer size");
                if(dmasize==8)dmasize=DMA_SIZE_8;
                else if(dmasize==16)dmasize=DMA_SIZE_16;
                else if(dmasize==32)dmasize=DMA_SIZE_32;
        }
        dma_channel_config c = dma_channel_get_default_config(dma_tx_chan);
        channel_config_set_write_increment(&c, false);
        if(dma_tx_pio==2)channel_config_set_dreq(&c, 16+sm);
        else channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true));

        channel_config_set_transfer_data_size(&c, dmasize);
        if(argc==13){
                int size=getinteger(argv[12]);
                if(!(size==1 || size==2 || size==4 || size==8 || size==16 || size==32 || size==64 || size==128 || size==256 || size==512 || size==1024 || size== 2048 || size==4096 || size==8192 || size==16384 || size==32768))error("Not power of 2");
                if(size!=1){
                        int i=0,j=size;
                        if(((uint32_t)a1int & (j-1)) && nbr==0)error("Data alignment error");
                        while(j>>=1)i++;
                        i+=dmasize;
                        if((1<<i)>(toarraysize*8))error("Array size");
                        if(nbr==0){
                                nbr=size;
                                dma_channel_config c2 = dma_channel_get_default_config(dma_tx_chan2); //Get configurations for control channel
                                channel_config_set_transfer_data_size(&c2, DMA_SIZE_32); //Set control channel data transfer size to 32 bits
                                channel_config_set_read_increment(&c2, false); //Set control channel read increment to false
                                channel_config_set_write_increment(&c2, false); //Set control channel write increment to false
                                channel_config_set_dreq(&c2, 0x3F);
//                                channel_config_set_chain_to(&c2, dma_tx_chan); 
                                dma_channel_configure(dma_tx_chan2,
                                        &c2,
                                        &dma_hw->ch[dma_tx_chan].al3_read_addr_trig,
                                        &a1int,
                                        1,
                                        false); //Configure control channel  
                        }
                        channel_config_set_read_increment(&c, true);
                        if(s_nbr!=0)channel_config_set_ring(&c,false,i);
                } else channel_config_set_read_increment(&c, false);
        } else {
                if((nbr<<dmasize)>(toarraysize*8))error("Array size");
                channel_config_set_read_increment(&c, true);
        } 
        if(s_nbr==0) channel_config_set_chain_to(&c, dma_tx_chan2); //When this channel completes, it will trigger the channel indicated by chain_to

                
        dma_channel_configure(dma_tx_chan,
                &c,
                &pio->txf[sm],      // Destination pointer
                a1int,        // Source pointer
                nbr, // Number of transfers
                (s_nbr==0 ? false :true)                // Start immediately
        );
        if(s_nbr==0) dma_start_channel_mask(1u << dma_tx_chan2);
        pio_sm_restart(pio, sm);
        pio_sm_set_enabled(pio, sm, true);
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"INTERRUPT");
    if(tp){
        getargs(&tp, 7, (unsigned char *)",");
        if((argc & 0x01) == 0) error("Syntax");
        if(argc<5)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
#endif
        int sm=getint(argv[2],0,3);
        if(*argv[4]){
                if(checkstring(argv[4],(unsigned char *)"0"))pioRXinterrupts[sm][pior]=NULL;
                else pioRXinterrupts[sm][pior]=(char *)GetIntAddress(argv[4]);
        }
        if(argc==7){
                if(checkstring(argv[6],(unsigned char *)"0"))pioTXinterrupts[sm][pior]=NULL;
                else pioTXinterrupts[sm][pior]=(char *)GetIntAddress(argv[6]);
        }
        piointerrupt=0;
        for(int i=0;i<4;i++){
                for(int j=0 ;j<PIOMAX;j++){
                        if(pioRXinterrupts[i][j] || pioTXinterrupts[i][j]){
                                piointerrupt=1;
                                InterruptUsed=1;
                        }
                }
        }
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"READ");
    if(tp){
//        unsigned char *p;
        unsigned int nbr;
        long long int *dd;
        getargs(&tp, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",");
        if((argc & 0x01) == 0) error("Syntax");
        if(argc<5)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int sm=getint(argv[2],0,3);
        nbr = getinteger(argv[4]);
        dd = findvar(argv[6], V_FIND | V_EMPTY_OK | V_NOFIND_ERR);
        if(((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] > 0 && g_vartbl[g_VarIndex].dims[1] == 0))
        {		// integer array
            if( (((long long int *)dd - g_vartbl[g_VarIndex].val.ia) + nbr) > (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase) )
                error("Insufficient array size");
        }  else  if ((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] == 0 && nbr==1){
            // single variable
        }  else error("Invalid variable");
        
        while(nbr--) {
       	    *dd = pio_sm_get(pio, sm);
            if(pio->fdebug & (1<<(sm + 16)))*dd=-1;
       	    if(nbr)dd++;
        }
        return;
    }
#ifdef rp2350
    tp = checkstring(cmdline, (unsigned char *)"WRITEFIFO");
    if(tp){
        getargs(&tp,7,(unsigned char *)",");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
        int sm=getint(argv[2],0,3);
        int fifo=getint(argv[4],0,3);
        pio->rxf_putget[sm][fifo]=getint(argv[6],0,0xFFFFFFFF); // jmp pin
        targ=T_INT;
        return;
    }
#endif
    tp = checkstring(cmdline, (unsigned char *)"PROGRAM LINE");
    if(tp){
        getargs(&tp,5,(unsigned char *)",");
        if(argc!=5)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int slot=getint(argv[2],0,31);
        int instruction=getint(argv[4],0,0xFFFF);
        pio->instr_mem[slot]=instruction;
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"ASSEMBLE");
    if(tp){
        static int wrap_target_set=0, wrap_set=0;
        getargs(&tp,3,(unsigned char *)",");
        if(!argc)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
        if(PIO2==false && pior==2)error("PIO 2 not available");
        pioinuse=pior;
#ifdef rp2350
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        unsigned int ins=0;
        char *ss;
        if(argc==3){
                ss=(char *)getCstring(argv[2]);
                char *comment=strchr(ss,';');
                if(comment)*comment=0;
                skipspace(ss);
                if(*ss==0)return;
        } else return;
        if(!strncasecmp(ss,".PROGRAM ",9)){
                if(dirOK!=2)error("Program already started");
                sidepins=0,sideopt=0,sidepinsdir=0;
                delaypossible=5;
                checksideanddelay=0;
                dirOK=1;
                PIOlinenumber=0;
                instructions = (int *)GetMemory(sizeof(int)*32);
                for(int i=0;i<32;i++)instructions[i]=-1;
                labelsfound = GetMemory(32 * MAXLABEL);
                labelsneeded = GetMemory(32 * MAXLABEL);
                PIOstart=0;
                p_wrap=31;
                p_wrap_target=0;
                wrap_target_set=0;
                wrap_set=0;
                return;
        }
        if(dirOK!=2){
                char *p;
                if(strchr(ss,':') && !fstrstr(ss,"::")){
                        p=strchr(ss,':');
                        skipspace(ss);
                        *p=0;
                        if(((uint32_t)p-(uint32_t)ss) > (MAXLABEL-1))error("Label too long");
                        for(int j=PIOstart;j<PIOlinenumber;j++){
                                if(strcasecmp(ss,&labelsfound[j*MAXLABEL])==0) {
                                        error("Duplicate label");
                                }
                        }
                        strcpy(&labelsfound[MAXLABEL*PIOlinenumber],ss);
                        return;
                } else if(*ss!='.'){
                        if(PIOlinenumber>31)error("Program too large");
                        if(!strncasecmp(ss,"JMP ",4)){
                                int dup=0;checksideanddelay=1;
                                ss+=3;
                                dirOK=0;
                                skipspace(ss);
                                if(strncasecmp(ss,"!X",2)==0 && (ss[2]==' ' || ss[2]==',')) {
                                        ins|=0x20;
                                        dup=1;
                                        ss+=2;
                                } 
                                if(strncasecmp(ss,"X--",3)==0 && (ss[3]==' ' || ss[3]==',')) {
                                        if(dup)error("Syntax");
                                        ins|=0x40;
                                        dup=1;
                                        ss+=3;
                                }
                                if(strncasecmp(ss,"!Y",2)==0 && (ss[2]==' ' || ss[2]==',')) {
                                        if(dup)error("Syntax");
                                        ins|=0x60;
                                        dup=1;
                                        ss+=2;
                                }
                                if(strncasecmp(ss,"Y--",3)==0 && (ss[3]==' ' || ss[3]==',')) {
                                        if(dup)error("Syntax");
                                        ins|=0x80;
                                        dup=1;
                                        ss+=3;
                                }
                                if(strncasecmp(ss,"X!=Y",4)==0 && (ss[4]==' ' || ss[4]==',')) {
                                        if(dup)error("Syntax");
                                        ins|=0xA0;
                                        dup=1;
                                        ss+=4;
                                }
                                if(strncasecmp(ss,"PIN",3)==0 && (ss[3]==' ' || ss[3]==',')) {
                                        if(dup)error("Syntax");
                                        ins|=0xC0;
                                        dup=1;
                                        ss+=3;
                                }
                                if(strncasecmp(ss,"!OSRE",5)==0 && (ss[5]==' ' || ss[5]==',')) {
                                        if(dup)error("Syntax");
                                        ins|=0xC0;
                                        dup=1;
                                        ss+=5;
                                }
                                char save=0;
                                skipspace(ss);
                                if(*ss==','){
                                        ss++;
                                        skipspace(ss);
                                }
                                if((*ss>='0' && *ss<='9') || *ss=='&' ){
                                        char *ppp=ss;
                                        if(*ss=='&'){
                                                if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax");
                                                ppp+=2;
                                        } 
                                        while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;}
                                        if(*ppp){
                                                save=*ppp;
                                                *ppp=',';
                                        }

                                        ins|=getint((unsigned char *)ss,0,31);
                                        ins|=0x10000;
                                        if(*ppp==',')*ppp=save;
                                } else {
                                        char *ppp=ss;
                                        if(!isnamestart(*ppp))error("Syntax");
                                        while(isnamechar(*ppp)){ppp++;}
                                        if(*ppp){
                                                save=*ppp;
                                                *ppp=0;
                                        }
                                        strcpy(&labelsneeded[PIOlinenumber*MAXLABEL],ss);
                                        *ppp=save;
                                }
                        } else if(!strncasecmp(ss,"WAIT ",5)){
                                dirOK=0;
                                ss+=4;
                                bool PINCTRL_JMP_PIN=false;
                                ins=0x2000;checksideanddelay=1;
                                skipspace(ss);
                                if(!(*ss=='1' || *ss=='0'))error("Syntax");
                                if(*ss=='1')ins |=0x80;
                                ss++;
                                skipspace(ss);
                                if(*ss==','){
                                        ss++;
                                        skipspace(ss);
                                }
                                int rel=2;
                                if(strncasecmp(ss,"GPIO",4)==0 && (ss[4]==' ' || ss[4]==',')){
                                        ss+=4;
                                } else if(strncasecmp(ss,"PIN",3)==0 && (ss[3]==' ' || ss[3]==',')){
                                        ss+=3;
                                        ins |=0b0100000;
                                } else if(strncasecmp(ss,"IRQ",3)==0 && (ss[3]==' ' || ss[3]==',')){
                                        char *pp;
                                        ss+=3;
                                        rel=0;
                                        ins |=0b1000000;
                                        if((pp=fstrstr(ss," rel")) && (pp[4]==0 || pp[4]==' ' || pp[4]==';'))rel=1;
#ifdef rp2350
                                } else if(strncasecmp(ss,"JMPPIN",6)==0 && (ss[6]==' ' || ss[6]==',')){
                                        ss+=6;
                                        ins |=0b1100000;
                                        PINCTRL_JMP_PIN=true;
#endif
                                } else error("syntax");
                                skipspace(ss);
                                if(*ss==','){
                                        ss++;
                                        skipspace(ss);
                                }
                                char save=0;
                                char *ppp=ss;
                                if((*ss>='0' && *ss<='9') || *ss=='&' ){
                                        char *ppp=ss;
                                        if(*ss=='&'){
                                                if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax");
                                                ppp+=2;
                                        } 
                                        while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;}
                                        if(*ppp){
                                                save=*ppp;
                                                *ppp=',';
                                        }
                                } else error("Syntax");
                                int bits=getint((unsigned char *)ss,0,rel==2? 31 : PINCTRL_JMP_PIN ? 3: 7);
                                if(*ppp==',')*ppp=save;
                                if(rel==1) bits |=0x10;
                                ins |=bits;
                        } else if(!strncasecmp(ss,"IN ",3)){
                                dirOK=0;
                                ss+=3;
                                ins=0x4000;checksideanddelay=1;
                                skipspace(ss);
                                if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b00100000;
                                } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b01000000;
                                } else if(strncasecmp(ss,"NULL",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                        ins|=0b01100000;
                                } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==' ' || ss[3]==',') ){
                                        ss+=3;
                                        ins|=0b11000000;
                                } else if(strncasecmp(ss,"OSR",3)==0 && (ss[3]==' ' || ss[3]==',') ){
                                        ss+=3;
                                        ins|=0b11100000;
                                } else error("Syntax");
                                skipspace(ss);
                                if(*ss!=',')error("Syntax");
                                ss++;
                                char save=0;
                                skipspace(ss);
                                char *ppp=ss;
                                if((*ss>='0' && *ss<='9') || *ss=='&' ){
                                        char *ppp=ss;
                                        if(*ss=='&'){
                                                if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax");
                                                ppp+=2;
                                        } 
                                        while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;}
                                        if(*ppp){
                                                save=*ppp;
                                                *ppp=',';
                                        }
                                } else error("Syntax");
                                int bits=getint((unsigned char *)ss,1,32);
                                if(bits==32)bits=0;
                                ins|=bits;
                                if(*ppp==',')*ppp=save;
                        } else if(!strncasecmp(ss,"OUT ",4)){
                                dirOK=0;
                                ss+=3;
                                ins=0x6000;checksideanddelay=1;
                                skipspace(ss);
                                if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b00100000;
                                } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b01000000;
                                } else if(strncasecmp(ss,"NULL",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                        ins|=0b01100000;
                                } else if(strncasecmp(ss,"PINDIRS",7)==0 && (ss[7]==' ' || ss[7]==',') ){
                                        ss+=7;
                                        ins|=0b10000000;
                                } else if(strncasecmp(ss,"PC",2)==0 && (ss[2]==' ' || ss[2]==',') ){
                                        ss+=2;
                                        ins|=0b10100000;
                                } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==' ' || ss[3]==',') ){
                                        ss+=3;
                                        ins|=0b11000000;
                                } else if(strncasecmp(ss,"EXEC",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                        ins|=0b11100000;
                                } else error("Syntax");
                                skipspace(ss);
                                if(*ss!=',')error("Syntax");
                                ss++;
                                char save=0;
                                skipspace(ss);
                                char *ppp=ss;
                                if((*ss>='0' && *ss<='9') || *ss=='&' ){
                                        char *ppp=ss;
                                        if(*ss=='&'){
                                                if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax");
                                                ppp+=2;
                                        } 
                                        while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;}
                                        if(*ppp){
                                                save=*ppp;
                                                *ppp=',';
                                        }
                                } else error("Syntax");
                                int bits=getint((unsigned char *)ss,1,32);
                                if(bits==32)bits=0;
                                ins|=bits;
                                if(*ppp==',')*ppp=save;
                        } else if(!strncasecmp(ss,"PUSH",4) && (ss[4]==0 || ss[4]==' ')){
                                dirOK=0;
                                ss+=4;
                                ins=0x8000;checksideanddelay=1;
                                ins |= checkblock(ss);
                        } else if(!strncasecmp(ss,"PULL ",4) && (ss[4]==0 || ss[4]==' ')){
                                dirOK=0;
                                ss+=4;
                                ins=0x8080;checksideanddelay=1;
                                ins |= checkblock(ss);
                        } else if(!strncasecmp(ss,"MOV ",4)){
                                dirOK=0;
                                ss+=3;
                                ins=0xA000;checksideanddelay=1;
                                skipspace(ss);
                                if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b00100000;
                                } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b01000000;
#ifdef rp2350
                                } else if(strncasecmp(ss,"PINDIRS",7)==0 && (ss[7]==' ' || ss[7]==',') ){
                                        ss+=7;
                                        ins|=0b01100000;
#endif
                                } else if(strncasecmp(ss,"EXEC",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                        ins|=0b10000000;
                                } else if(strncasecmp(ss,"PC",2)==0 && (ss[2]==' ' || ss[2]==',') ){
                                        ss+=2;
                                        ins|=0b10100000;
                                } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==' ' || ss[3]==',') ){
                                        ss+=3;
                                        ins|=0b11000000;
                                } else if(strncasecmp(ss,"OSR",3)==0 && (ss[3]==' ' || ss[3]==',') ){
                                        ss+=3;
                                        ins|=0b11100000;
#ifdef rp2350
                                } else if(strncasecmp(ss,"RXFIFO[",7)==0){ //this is really a push instruction
                                        ss+=7;
                                        ins=0x8010;
                                        if(strncasecmp(ss,"0]",2)==0 && (ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1000;
                                        } else if(strncasecmp(ss,"1]",2)==0 && (ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1001;
                                        } else if(strncasecmp(ss,"2]",2)==0 && (ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1010;
                                        } else if(strncasecmp(ss,"3]",2)==0 && (ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1011;
                                        } else if(!(strncasecmp(ss,"Y]",2)==0 && (ss[2]==' ' || ss[2]==',')))error("Syntax");
                                        ss+=2;
#endif
                                } else error("Syntax");
                                skipspace(ss);
                                if(*ss!=',')error("Syntax");
                                ss++;
                                skipspace(ss);
                                if(*ss=='~' || *ss=='!'){
                                        ins |=8;
                                        ss++;
                                }
                                if(*ss==':' && ss[1]==':'){
                                        ins |=0x10;
                                        ss+=2;
                                }
                                skipspace(ss);
                                if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==0 || ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                        if(!(ins&0x2000))error("Syntax");
                                } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==0 || ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b001;
                                        if(!(ins&0x2000))error("Syntax");
                                } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==0 || ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b010;
                                        if(!(ins&0x2000))error("Syntax");
                                } else if(strncasecmp(ss,"NULL",4)==0 && (ss[4]==0 || ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                        ins|=0b011;
                                        if(!(ins&0x2000))error("Syntax");
                                } else if(strncasecmp(ss,"STATUS",6)==0 && (ss[6]==0 || ss[6]==' ' || ss[6]==',') ){
                                        ss+=6;
                                        ins|=0b101;
                                        if(!(ins&0x2000))error("Syntax");
                                } else if(strncasecmp(ss,"ISR",3)==0 && (ss[3]==0 || ss[3]==' ' || ss[3]==',') ){
                                        ss+=3;
                                        if(ins&0x2000)ins|=0b110;
                                } else if(strncasecmp(ss,"OSR",3)==0 && (ss[3]==0 || ss[3]==' ' || ss[3]==',') ){
                                        ss+=3;
                                        ins|=0b111;
                                        if(!(ins&0x2000))error("Syntax");
#ifdef rp2350
                                } else if(strncasecmp(ss,"RXFIFO",6)==0 && (ss[6]=='[') && ins==(0xA000 | 0b11100000)){ //this is really a pull instruction
                                        ss+=7;
                                        ins=0x8090;
                                        if(strncasecmp(ss,"0]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1000;
                                        } else if(strncasecmp(ss,"1]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1001;
                                        } else if(strncasecmp(ss,"2]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1010;
                                        } else if(strncasecmp(ss,"3]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')){
                                                ins|=0b1011;
                                        } else if(!(strncasecmp(ss,"Y]",2)==0 && (ss[2]==0 || ss[2]==' ' || ss[2]==',')))error("Syntax");
                                        ss+=2;
#endif
                                } else error("Syntax");

                        } else if(!strncasecmp(ss,"NOP",3) && (ss[3]==0 || ss[3]==' ')){
                                dirOK=0;
                                ss+=3;
                                ins=0xA000;
                                ins |=0b01000010;checksideanddelay=1;
                        } else if(!strncasecmp(ss,"IRQ SET ",8)){
                                dirOK=0;
                                ss+=7;
                                ins=0xC000;checksideanddelay=1;
                                ins |= getirqnum(ss);
                        } else if(!strncasecmp(ss,"IRQ WAIT ",9)){
                                dirOK=0;
                                ss+=8;
                                ins=0xC020;checksideanddelay=1;
                                ins |= getirqnum(ss);
                        } else if(!strncasecmp(ss,"IRQ CLEAR ",10)){
                                dirOK=0;
                                ss+=9;
                                ins=0xC040;checksideanddelay=1;
                                ins |= getirqnum(ss);
                        } else if(!strncasecmp(ss,"IRQ NOWAIT ",11)){
                                dirOK=0;
                                ss+=10;
                                ins=0xC000;checksideanddelay=1;
                                ins |= getirqnum(ss);
                        } else if(!strncasecmp(ss,"IRQ ",4)){
                                dirOK=0;
                                ss+=3;
                                ins=0xC000;checksideanddelay=1;
                                ins |= getirqnum(ss);
                        } else if(!strncasecmp(ss,"SET ",4)){
                                dirOK=0;
                                ss+=3;
                                ins=0xE000;checksideanddelay=1;
                                skipspace(ss);
                                if(strncasecmp(ss,"PINS",4)==0 && (ss[4]==' ' || ss[4]==',') ){
                                        ss+=4;
                                } else if(strncasecmp(ss,"X",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b100000;
                                } else if(strncasecmp(ss,"Y",1)==0 && (ss[1]==' ' || ss[1]==',') ){
                                        ss++;
                                        ins|=0b1000000;
                                } else if(strncasecmp(ss,"PINDIRS",7)==0 && (ss[7]==' ' || ss[7]==',') ){
                                        ss+=7;
                                        ins|=0b10000000;
                                } else error("Syntax");
                                skipspace(ss);
                                if(*ss!=',')error("Syntax");
                                ss++;
                                char save=0;
                                skipspace(ss);
                                char *ppp=ss;
                                if((*ss>='0' && *ss<='9') || *ss=='&' ){
                                        char *ppp=ss;
                                        if(*ss=='&'){
                                                if(!(toupper(ss[1])=='B' || toupper(ss[1])=='H' || toupper(ss[1])=='O')) error("Syntax");
                                                ppp+=2;
                                        } 
                                        while(*ppp>='0' && *ppp<='9' && *ppp){ppp++;}
                                        if(*ppp){
                                                save=*ppp;
                                                *ppp=',';
                                        }
                                } else error("Syntax");
                                ins|=getint((unsigned char *)ss,0,31);
                                if(*ppp==',')*ppp=save;
                        } else error("PIO instruction not found");
                        if(checksideanddelay==1)ins|=calcsideanddelay(ss, sidepins, delaypossible);
                        instructions[PIOlinenumber]=ins;
                        PIOlinenumber++;
                        return;
                } else {
                        if(!strncasecmp(ss,".LINE ",5)){
                                if(!dirOK)error("Directive must appear before instructions");
                                ss+=5;
                                skipspace(ss);
                                if(!strncasecmp(ss,"NEXT",4) && (ss[4]==0 || ss[4]==' '))PIOlinenumber=nextline[pioinuse];
                                else PIOlinenumber=getint((unsigned char *)ss,0,31);
                                PIOstart=PIOlinenumber;
                                return;
                        } else if(!strncasecmp(ss,".WRAP TARGET",12)){
                                if(wrap_target_set)error("Repeat directive");
                                p_wrap_target=PIOlinenumber;
                                wrap_target_set=1;
                                return;
                                 //sort out the jmps
                        } else if(!strncasecmp(ss,".WRAP",4)){
//                                if(!wrap_target_set)error("Wrap target not set");
                                if(wrap_set)error("Repeat directive");
                                p_wrap=PIOlinenumber-1;
                                if(PIOlinenumber==-1)PIOlinenumber=31;
                                return;
                                 //sort out the jmps
                        } else if(!strncasecmp(ss,".END PROGRAM",12)){ //sort out the jmps
                                if(dirOK==2)error("Program not started");
                                int totallines=0;
                                dirOK=2;
                                for(int i=PIOstart; i<32;i++){
                                        if(instructions[i]!=-1){
                                                totallines++;
                                        }
                                }
                                for(int i=PIOstart; i<PIOstart+totallines;i++){
                                        if(!(instructions[i] & 0x1E000)){ // jmp instructions needs resolving
                                                int notfound=1;
                                                for(int j=PIOstart;j<PIOstart+totallines;j++){
                                                        if(strcasecmp(&labelsneeded[i*MAXLABEL],&labelsfound[j*MAXLABEL])==0) {
                                                                instructions[i] |=j; 
                                                                notfound=0;
                                                        }
                                                }
                                                if(notfound)error("label not found at line %",i);
                                        }
                                        if(instructions[i]&0x10000)instructions[i]&=0xFFFF;
                                }
                                ss+=12;
                                skipspace(ss);
                                for(int i=PIOstart;i<PIOstart+totallines;i++){
                                        pio->instr_mem[i]=instructions[i];
                                                if(!strncasecmp(ss,"LIST",4) && (ss[4]==0 || ss[4]==' ')){
                                                char c[10]={0};
                                                PInt(i);
                                                MMPrintString(": ");
                                                IntToStr(c,instructions[i]+0x10000,16);
                                                MMPrintString(&c[1]);
                                                PRet();
                                        }
                                }
                                nextline[pioinuse]=PIOstart+totallines;
                                nextline[3]=pioinuse;
                                FreeMemorySafe((void **)&instructions);
                                FreeMemorySafe((void **)&labelsneeded);
                                FreeMemorySafe((void **)&labelsfound);
                                pioinuse=99;
                                return;
                        } else if(!strncasecmp(ss,".SIDE_SET ",10)){
                                if(!dirOK)error("Directive must appear before instructions");
                                ss+=10;
                                sidepins=*ss-'0';
                                if(sidepins<1 || sidepins>5)error("Invalid side_set pin count");
                                ss++;
                                delaypossible=5-sidepins;
                                ins=0xE000;
                                skipspace(ss);
                                if(!strncasecmp(ss,"opt",3) && (ss[3]==0 || ss[3]==' ')){
                                        sideopt=1;
                                        delaypossible=4-sidepins;
                                        if(sideopt && sidepins==5)error("Only 4 side set pins allowed with opt set");
                                        ss+=3;
                                        skipspace(ss);
                                }
                                if(!strncasecmp(ss,"pindirs",7) && (ss[7]==0 || ss[7]==' '))sidepinsdir=1;
                                return;
                        } else error("PIO directive not found");
                }
        } else error(".program must be first statement");
    }
    tp = checkstring(cmdline, (unsigned char *)"CLEAR");
    if(tp){
        getargs(&tp,1,(unsigned char *)",");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        for(int sm=0;sm<4;sm++){
            hw_clear_bits(&pio->ctrl, 1 << (PIO_CTRL_SM_ENABLE_LSB + sm));
            pio->sm[sm].pinctrl=(5<<26);
            pio->sm[sm].execctrl=(0x1f<<12);
            pio->sm[sm].shiftctrl=(3<<18);
            pio_sm_clear_fifos(pio,sm);
        }
        pio_clear_instruction_memory(pio);
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"MAKE RING BUFFER");
    if(tp){
        getargs(&tp,3,(unsigned char *)",");
        if(argc<3)error("Syntax");
        int size=getinteger(argv[2]);
        if(!(size==256 || size==512 || size==1024 || size== 2048 || size==4096 || size==8192 || size==16384 || size==32768))error("Not power of 2");
        findvar(argv[0], V_FIND | V_NOFIND_ERR);
        if ((g_vartbl[g_VarIndex].type & T_INT) && g_vartbl[g_VarIndex].dims[0] == 0 && g_vartbl[g_VarIndex].level==0){
                g_vartbl[g_VarIndex].val.s =(unsigned char *)GetAlignedMemory(size);
                g_vartbl[g_VarIndex].size=255;
                g_vartbl[g_VarIndex].dims[0] = size/8-1+g_OptionBase;
        }  else error("Invalid variable");
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"PROGRAM");
    if(tp){
        struct pio_program program;
        getargs(&tp,3,(unsigned char *)",");
        if(argc!=3)error("Syntax");
//        void *prt1;
        program.length=32;
        program.origin=0;
        int64_t *a1int=NULL;
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int toarraysize=parseintegerarray(argv[2],&a1int,2,1,dims, true);
        if(toarraysize!=8)error("Array size");
        program.instructions = (const uint16_t *)a1int;
        for(int sm=0;sm<4;sm++)hw_clear_bits(&pio->ctrl, 1 << (PIO_CTRL_SM_ENABLE_LSB + sm));
        pio_clear_instruction_memory(pio);
        pio_add_program(pio, &program);
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"START");
    if(tp){
        getargs(&tp,3,(unsigned char *)",");
        if(argc!=3)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int sm=getint(argv[2],0,3);
        pio_sm_clear_fifos(pio,sm);
        pio_sm_restart(pio, sm);
        pio_sm_set_enabled(pio, sm, true);
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"STOP");
    if(tp){
        getargs(&tp,3,(unsigned char *)",");
        if(argc!=3)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int sm=getint(argv[2],0,3);
        pio_sm_set_enabled(pio, sm, false);
        return;
    }
    tp = checkstring(cmdline, (unsigned char *)"INIT MACHINE");
    if(tp){
        int start=0;
        bool setout=false,sideout=false,outout=false;
        getargs(&tp,19,(unsigned char *)",");
        if(argc<5)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
#endif
        int sm=getint(argv[2],0,3);
        float clock=getnumber(argv[4]);
        if(clock<CLKMIN || clock> CLKMAX)error("Clock must be in range % to %",CLKMIN,CLKMAX);
        int pinctrl=0, execctrl=0,shiftctrl=0;
        if(argc>5 && *argv[6])pinctrl = getint(argv[6],0,0xFFFFFFFF);
        if(argc>7 && *argv[8])execctrl= getint(argv[8],0,0xFFFFFFFF);
        if(argc>9 && *argv[10])shiftctrl= getint(argv[10],0,0xFFFFFFFF);
        if(argc>11 && *argv[12]) start=getint(argv[12],0,31);
        if(argc>13 && *argv[14]) sideout=getint(argv[14],0,1);
        if(argc>15 && *argv[16]) setout=getint(argv[16],0,1);
        if(argc>171 && *argv[18]) outout=getint(argv[18],0,1);
        pio_init(pior, sm, pinctrl, execctrl, shiftctrl, start, clock, sideout, setout, outout);
        return;
    }
    #ifdef rp2350
    tp = checkstring(cmdline, (unsigned char *)"SET BASE");
    if(tp){
        getargs(&tp,3,(unsigned char *)",");
        if(argc<3)error("Syntax");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        int i=getinteger(argv[2]);
        if(!(i==0 || i==16))error("Valid base values are 0 and 16");
        if(rp2350a && i==16)error("Invalid for 60-pin chip");
        pio_set_gpio_base(pio,i);
        return;
    }
#endif
    tp = checkstring(cmdline, (unsigned char *)"CONFIGURE");
/*PIO Configure pio, sm, clock, startaddress,
sidesetbase, sidesetno, sidesetout, setbase, setno, setout, outbase, outno, outout, inbase,
jmppin, wraptarget, wrap, sideenable,sidepindir,
pushthreshold, pullthreshold, autopush, autopull, inshiftdir, outshiftdir, joinrcfifo, jointxfifo, joinrxfifoget, joinrxfifoput
*/
    if(tp){
        int startaddress=0, base=0;
        int sidesetbase=0, sidesetno=0, sidesetout=0, setbase=0, setno=0, setout=0, outbase=0, outno=0, outout=0, inbase=0;
        int jmppin=-1, wraptarget=0, wrap=31, sideenable=0, sidepindir=0;
        int pushthreshold=0, pullthreshold=0, autopush=0, autopull=0, inshiftdir=1, outshiftdir=1, joinrxfifo=0, jointxfifo=0;
#ifdef rp2350
        int joinrxfifoget=0, joinrxfifoput=0;
        getargs(&tp,57,(unsigned char *)",");
#else
        getargs(&tp,53,(unsigned char *)",");
#endif
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
        base=pio_get_gpio_base(pio);
#else
        PIO pio = (pior==0 ? pio0: pio1);
#endif
        if(base==16){
                sidesetbase=16;
                setbase=16;
                outbase=16;  
                inbase=16;
        }
        int sm=getint(argv[2],0,3);
        float clock=getnumber(argv[4]);
        if(clock<CLKMIN || clock> CLKMAX)error("Clock must be in range % to %",CLKMIN,CLKMAX);
        if(argc>5 && *argv[6]) startaddress=getint(argv[6],0,31);
        if(argc>7 && *argv[8]) sidesetbase=getGPpin(argv[8],pior, base);
        if(argc>9 && *argv[10]) sidesetno=getint(argv[10],0,5);
        if(argc>11 && *argv[12]) sidesetout=getint(argv[12],0,1);
        if(argc>13 && *argv[14]) setbase=getGPpin(argv[14],pior, base);
        if(argc>15 && *argv[16]) setno=getint(argv[16],0,5);
        if(argc>17 && *argv[18]) setout=getint(argv[18],0,1);
        if(argc>19 && *argv[20]) outbase=getGPpin(argv[20],pior, base);
        if(argc>21 && *argv[22]) outno=getint(argv[22],0,5);
        if(argc>23 && *argv[24]) outout=getint(argv[24],0,1);
        if(argc>25 && *argv[26]) inbase=getGPpin(argv[26],pior, base);
        if(argc>27 && *argv[28]) jmppin=getGPpin(argv[28],pior, base);
        if(argc>29 && *argv[30]) wraptarget=getint(argv[30],0,31);
        if(argc>31 && *argv[32]) wrap=getint(argv[32],0,31);
        if(argc>33 && *argv[34]) sideenable=getint(argv[34],0,1);
        if(sideenable+sidesetno>5)error("Max 4 sideset pins with option set");
        if(argc>35 && *argv[36]) sidepindir=getint(argv[36],0,1); // push threshold
        if(argc>37 && *argv[38]) pushthreshold=getint(argv[38],0,31); // push threshold
        if(argc>39 && *argv[40]) pullthreshold=getint(argv[40],0,31); // pull threshold
        if(argc>41 && *argv[42]) autopush=getint(argv[42],0,1); // autopush
        if(argc>43 && *argv[44]) autopull=getint(argv[44],0,1); // autopull
        if(argc>45 && *argv[46]) inshiftdir=getint(argv[46],0,1); // IN_SHIFTDIR
        if(argc>47 && *argv[48]) outshiftdir=getint(argv[48],0,1); // OUT_SHIFTDIR
        if(argc>49 && *argv[50]) joinrxfifo=getint(argv[50],0,1); // FJOIN_RX
        if(argc>51 && *argv[52]) jointxfifo=getint(argv[52],0,1); // FJOIN_TX
        if(joinrxfifo && jointxfifo)error("Attempt to join both RX and TX FIFOs");
#ifdef rp2350
        if(argc>53 && *argv[54]) joinrxfifoget=getint(argv[54],0,1); // FJOIN_RX_GET
        if(argc>55 && *argv[56]) joinrxfifoput=getint(argv[56],0,1); // FJOIN_RX_PUT
#endif
        #ifdef rp2350
        #ifdef PICOMITEWEB
                for(int i = 1; i < (NBRPINS) ; i++) {
        #else
                for(int i = 1; i < (rp2350a ? 44:NBRPINS) ; i++) {
        #endif
        #else
        for(int i = 1; i < (NBRPINS) ; i++) {
        #endif
                if(CheckPin(i, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED)) {    // don't reset invalid or boot reserved pins
                        gpio_set_input_enabled(PinDef[i].GPno, true);
                }
        }
        pio_set_gpio_base(pio,base);
        pio_sm_config cfg = pio_get_default_sm_config();
        sm_config_set_in_pin_base(&cfg, inbase);
	if(jointxfifo)sm_config_set_fifo_join(&cfg, PIO_FIFO_JOIN_TX);
	if(joinrxfifo)sm_config_set_fifo_join(&cfg, PIO_FIFO_JOIN_RX);
        if(outno)sm_config_set_out_pins(&cfg, outbase, outno);
        if(sidesetno){
                sm_config_set_sideset_pins(&cfg,sidesetbase);
                sm_config_set_sideset(&cfg,sidesetno+sideenable,sideenable,sidepindir);
        }
        if(setno){
                sm_config_set_set_pin_base(&cfg, setbase);
                sm_config_set_set_pin_count(&cfg, setno);
        }
        if(jmppin>=0)sm_config_set_jmp_pin(&cfg, jmppin);
        sm_config_set_wrap(&cfg, wraptarget, wrap);
        sm_config_set_in_shift(&cfg,inshiftdir,autopush,pushthreshold);
        sm_config_set_out_shift(&cfg,outshiftdir, autopull,pullthreshold);
#ifdef rp2350
        if(joinrxfifoget)cfg.shiftctrl|=(1<<14);
        if(joinrxfifoput)cfg.shiftctrl|=(1<<15);
#endif
        sm_config_set_clkdiv(&cfg, (Option.CPU_Speed*1000.0f)/clock);
        pio_sm_set_config(pio, sm, &cfg);
        pio_sm_init(pio, sm, startaddress, &cfg);
        if(sidesetout && sidesetno)pio_sm_set_consecutive_pindirs(pio, sm, sidesetbase, sidesetno, true);
        if(setout && setno)pio_sm_set_consecutive_pindirs(pio, sm, setbase, setno, true);
        if(outout && outno)pio_sm_set_consecutive_pindirs(pio, sm, outbase, outno, true);
        return;
}
    error("Syntax");
}
void fun_pio(void){
    unsigned char *tp;
    tp = checkstring(ep, (unsigned char *)"PINCTRL");
    if(tp){
        int64_t myret=0;
        getargs(&tp,13,(unsigned char *)",");
        if(argc<3)error("Syntax");
        myret=(getint(argv[0],0,5)<<29); // no of side set pins
        if(argc>1 && *argv[2])myret|=(getint(argv[2],0,5)<<26); // no of set pins
        if(argc>3 && *argv[4])myret|=(getint(argv[4],0,31)<<20); // no of OUT pins
        if(argc>5 && *argv[6]){
            if(!(toupper((char)*argv[6])=='G'))error("Syntax");
            argv[6]++;
            if(!(toupper((char)*argv[6])=='P'))error("Syntax");
            argv[6]++;
            myret|=(getint(argv[6],0,31)<<15); // IN base
        }
        if(argc>7 && *argv[8]){
            if(!(toupper((char)*argv[8])=='G'))error("Syntax");
            argv[8]++;
            if(!(toupper((char)*argv[8])=='P'))error("Syntax");
            argv[8]++;
            myret|=(getint(argv[8],0,31)<<10); // SIDE SET base
        }
        if(argc>9 && *argv[10]){
            if(!(toupper((char)*argv[10])=='G'))error("Syntax");
            argv[10]++;
            if(!(toupper((char)*argv[10])=='P'))error("Syntax");
            argv[10]++;
            myret|=(getint(argv[10],0,31)<<5); // SET base
        }
        if(argc==13){
            if(!(toupper((char)*argv[12])=='G'))error("Syntax");
            argv[12]++;
            if(!(toupper((char)*argv[12])=='P'))error("Syntax");
            argv[12]++;
            myret|=getint(argv[12],0,31); //OUT base
        }
        iret=myret;
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)"EXECCTRL");
    if(tp){
        int64_t myret=0;
        getargs(&tp,9,(unsigned char *)",");
        if(!(argc==5 || argc==7 || argc==9))error("Syntax");
        if(!(toupper((char)*argv[0])=='G'))error("Syntax");
        argv[0]++;
        if(!(toupper((char)*argv[0])=='P'))error("Syntax");
        argv[0]++;
        myret=(getint(argv[0],0,31)<<24); // jmp pin
        myret |= pio_sm_calc_wrap(getint(argv[2],0,31), getint(argv[4],0,31));
        if(argc>=7 && *argv[6])myret|=(getint(argv[6],0,1)<<29); //SIDE_PINDIR
        if(argc==9)myret|=(getint(argv[8],0,1)<<30); // SIDE_EN
        iret=myret;
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)".WRAP TARGET");
    if(tp){
        iret=p_wrap_target;
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)".WRAP");
    if(tp){
        iret=p_wrap;
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)"SHIFTCTRL");
    if(tp){
#ifdef rp2350
        getargs(&tp,19,(unsigned char *)",");
#else
        getargs(&tp,15,(unsigned char *)",");
#endif
        if(argc<1)error("Syntax");
        int64_t myret=0;
        myret=(getint(argv[0],0,31)<<20); // push threshold
        myret|=(getint(argv[2],0,31)<<25); // pull threshold
        if(argc>3 && *argv[4])myret|=(getint(argv[4],0,1)<<16); // autopush
        if(argc>5 && *argv[6])myret|=(getint(argv[6],0,1)<<17); // autopull
        if(argc>7 && *argv[8])myret|=(getint(argv[8],0,1)<<18); // IN_SHIFTDIR
        if(argc>9 && *argv[10])myret|=(getint(argv[10],0,1)<<19); // OUT_SHIFTDIR
        if(argc>11 && *argv[12])myret|=(getint(argv[12],0,1)<<30); // FJOIN_RX
        if(argc>13 && *argv[14])myret|=(getint(argv[14],0,1)<<31); // FJOIN_TX
#ifdef rp2350
        if(argc>15 && *argv[16])myret|=(getint(argv[16],0,1)<<14); // FJOIN_RX_GET
        if(argc>17 && *argv[18])myret|=(getint(argv[18],0,1)<<15); // FJOIN_RX_PUT
#endif
        iret=myret;
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)"FSTAT");
    if(tp){
        getargs(&tp,1,(unsigned char *)",");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        iret=pio->fstat; // jmp pin
        targ=T_INT;
        return;
    }
#ifdef rp2350
    tp = checkstring(ep, (unsigned char *)"READFIFO");
    if(tp){
        getargs(&tp,5,(unsigned char *)",");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
        int sm=getint(argv[2],0,3);
        int fifo=getint(argv[4],0,3);
        iret=pio->rxf_putget[sm][fifo]; // jmp pin
        targ=T_INT;
        return;
    }
#endif
    tp = checkstring(ep, (unsigned char *)"FDEBUG");
    if(tp){
        getargs(&tp,1,(unsigned char *)",");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        iret=pio->fdebug; // jmp pin
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)"NEXT LINE");
    if(tp){
        getargs(&tp,1,(unsigned char *)",");
        int pior=nextline[3];
        if(argc)pior=getint(argv[0],0,PIOMAX-1);
        if(pior==99)error("No current PIO in use");
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
#endif
        iret=nextline[pior];
        targ=T_INT;
        return;
}
    tp = checkstring(ep, (unsigned char *)"FLEVEL");
    if(tp){
        getargs(&tp,5,(unsigned char *)",");
        int pior=getint(argv[0],0,PIOMAX-1);
        if(PIO0==false && pior==0)error("PIO 0 not available");
        if(PIO1==false && pior==1)error("PIO 1 not available");
#ifdef rp2350
        if(PIO2==false && pior==2)error("PIO 2 not available");
        PIO pio = (pior==0 ? pio0: (pior==1 ? pio1: pio2));
#else
        PIO pio = (pior==0 ?  pio0: pio1);
#endif
        if(argc==1)iret=pio->flevel; // jmp pin
        else {
                if(argc!=5)error("Syntax");
                int sm=getint(argv[2],0,3)*8;
                if(checkstring(argv[4],(unsigned char *)"TX"))iret=((pio->flevel)>>sm) & 0xf;
                else if(checkstring(argv[4],(unsigned char *)"RX"))iret=((pio->flevel)>>(sm+4)) & 0xf;
                else error("Syntax");
        }
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)"DMA RX POINTER");
    if(tp){
        iret=dma_hw->ch[dma_rx_chan].write_addr;
        targ=T_INT;
        return;
    }
    tp = checkstring(ep, (unsigned char *)"DMA TX POINTER");
    if(tp){
        iret=dma_hw->ch[dma_tx_chan].read_addr;
        targ=T_INT;
        return;
    }
    error("Syntax");


}
// mode determines functionality
// bit 0 sets wheter to add a space after the text
// bit 1 sets whether to omit the missing program check
// bit 2 sets whether to add a colon after the text
// bit 3 enables the diagnostic print
void call_pio(char * instruction, uint8_t mode){
       if((instructions==NULL || pioinuse==99) && !(mode & 2))error("Missing Program statement");
       skipspace(cmdline);
       char *buff=GetTempMemory(STRINGSIZE);
       sprintf(buff,"ASSEMBLE %d, \"",pioinuse);
       strcat(buff,instruction);
       if(mode & 1)strcat(buff," ");
       strcat(buff,(char *)cmdline);
       char *p=buff;
      while(*p){ //strip comments and loose spurrious tokens
               if(*p==GetTokenValue((unsigned char *)"-"))*p=(unsigned char)'-';
               if(*p==GetTokenValue((unsigned char *)"="))*p=(unsigned char)'=';
               if(*p=='\'' || *p==';'){
                       *p=0;
                       break;
               }
               p++;
       }
       // strip trailing spaces
       p=buff+strlen(buff)-1;
       while(*p==' ')*p--=0;
       if(mode & 4)strcat(buff,":");
       strcat(buff,"\"");
       cmdline=((unsigned char *)buff);
       if(mode & 8){MMPrintString((char *)cmdline);PRet();}
       cmd_pio();
}
void cmd_program(void){
       call_pio(".program",3);
}
void cmd_wrap(void){
       call_pio(".wrap",0);
}
void cmd_wraptarget(void){
       call_pio(".wrap target",0);
}
void cmd_PIOline(void){
       call_pio(".line",1);
}
void cmd_sideset(void){
       call_pio(".side_set",1);
}
void cmd_endprogram(void){
       call_pio(".end program",strlen((char *)cmdline) ? 1 : 0);
}
void cmd_jmp(void){
       call_pio("jmp",1);
}
void cmd_wait(void){
       call_pio("wait",1);
}
void cmd_in(void){
        call_pio("in",1);
}
void cmd_out(void){
        call_pio("out",strlen((char *)cmdline) ? 1 : 0);
}
void cmd_push(void){
        call_pio("push",strlen((char *)cmdline) ? 1 : 0);
}
void cmd_pull(void){
        call_pio("pull",1);
}
void cmd_mov(void){
        call_pio("mov",1);
}
void cmd_nop(void){
        call_pio("nop",strlen((char *)cmdline) ? 1 : 0);
}
void cmd_irqset(void){
        call_pio("irq set",1);
}
void cmd_irqwait(void){
        call_pio("irq wait",1);
}
void cmd_irqclear(void){
        call_pio("irq clear",1);
}
void cmd_irqnowait(void){
        call_pio("irq mov",1);
}
void cmd_irq(void){
        call_pio("irq",1);
}
void cmd_set(void){
       call_pio("set",1);
}
void cmd_label(void){
       call_pio("",4);
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */

#ifdef PICOMITEWEB
char *scan_dest=NULL;
volatile char *scan_dups=NULL; 
volatile int scan_size;
static int scan_result(void *env, const cyw43_ev_scan_result_t *result) {
    if (result) {
        Timer4=5000;
        char buff[STRINGSIZE]={0};
//        int found=0;
        if(scan_dups==NULL)return 0;
        for(int i=0;i<scan_dups[32*100];i++){
                if(strcmp((char *)result->ssid,(char *)&scan_dups[32*i])==0)return 0;
        }
        for(int i=0;i<100;i++){
                if(scan_dups[32*i]==0){
                        strcpy((char *)&scan_dups[32*i],(char *)result->ssid);
                        scan_dups[32*100]++;
                        break;
                }
        }
        sprintf(buff,"ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %u\r\n",
            result->ssid, result->rssi, result->channel,
            result->bssid[0], result->bssid[1], result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5],
            result->auth_mode);
        if(scan_dest!=NULL){
                if(strlen(((char *)&scan_dest[8]) + strlen(buff)) > scan_size){
                        FreeMemorySafe((void **)&scan_dups);
                        scan_dest=NULL;
                        error("Array too small");
                }
                if(scan_dest[8]==0)strcpy(&scan_dest[8],buff);
                else strcat(&scan_dest[8],buff);
        } else MMPrintString(buff);
    } 
    return 0;
}
/*  @endcond */
void cmd_web(void){
        unsigned char *tp;
        tp=checkstring(cmdline, (unsigned char *)"CONNECT");
        if(tp){
            if(*tp){
            	setwifi(tp);
                WebConnect();
            } else {
	            if(cyw43_wifi_link_status(&cyw43_state,CYW43_ITF_STA)<0){
	                WebConnect();
	            }
	        }
           return;   
        }
        tp=checkstring(cmdline, (unsigned char *)"SCAN");
        if(tp){
                void *ptr1 = NULL;
                if(*tp){
                        ptr1 = findvar(tp, V_FIND | V_EMPTY_OK);
                        if(g_vartbl[g_VarIndex].type & T_INT) {
                                if(g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable");
                                if(g_vartbl[g_VarIndex].dims[0] <= 0) {		// Not an array
                                        error("Argument 1 must be integer array");
                                }
                                scan_size=(g_vartbl[g_VarIndex].dims[0]-g_OptionBase)*8;
                                scan_dest = (char *)ptr1;
                                scan_dest[8]=0;
                        } else error("Argument 1 must be integer array");

                }
                scan_dups=GetMemory(32*100+1);
                cyw43_wifi_scan_options_t scan_options = {0};
                int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result);
                if (err == 0) {
                    MMPrintString("\nPerforming wifi scan\r\n");
                } else {
                    char buff[STRINGSIZE]={0};
                    sprintf(buff,"Failed to start scan: %d\r\n", err);
                    MMPrintString(buff);
                }
                Timer4=500;
                while (Timer4)if(startupcomplete)ProcessWeb(0);
                if(scan_dest){
                        uint64_t *p=(uint64_t *)scan_dest;
                        *p=strlen(&scan_dest[8]);
                }
                scan_dest=NULL;
                FreeMemorySafe((void **)&scan_dups);
                return;   
        }
        if(!(WIFIconnected &&  cyw43_tcpip_link_status(&cyw43_state,CYW43_ITF_STA)==CYW43_LINK_UP))error("WIFI not connected");
        tp=checkstring(cmdline, (unsigned char *)"NTP");
        if(tp){
            cmd_ntp(tp);
            return;   
        }
        tp=checkstring(cmdline, (unsigned char *)"UDP");
        if(tp){
            cmd_udp(tp);
            return;   
        }
        if(cmd_mqtt())return;
        if(cmd_tcpclient())return;
        if(cmd_tcpserver())return;
//        if(cmd_tls())return;
        error("Syntax");
}

void fun_json(void){
    char *json_string=NULL;
    const cJSON *root = NULL;
    void *ptr1 = NULL;
    char *p;
    sret=GetTempMemory(STRINGSIZE);
    int64_t *dest=NULL;
    MMFLOAT tempd;
    int i,j,k,mode,index;
    char field[32],num[6];
    getargs(&ep, 3, (unsigned char *)",");
    char *a=GetTempMemory(STRINGSIZE);
    ptr1 = findvar(argv[0], V_FIND | V_EMPTY_OK);
    if(g_vartbl[g_VarIndex].type & T_INT) {
    if(g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable");
    if(g_vartbl[g_VarIndex].dims[0] <= 0) {		// Not an array
        error("Argument 1 must be integer array");
    }
    dest = (long long int *)ptr1;
    json_string=(char *)&dest[1];
    } else error("Argument 1 must be integer array");
    cJSON_InitHooks(NULL);
    cJSON * parse = cJSON_Parse(json_string);
    if(parse==NULL)error("Invalid JSON data");
    root=parse;
    p=(char *)getCstring((unsigned char *)argv[2]);
    int len = strlen(p);
    memset(field,0,32);
    memset(num,0,6);
    i=0;j=0;k=0;mode=0;
    while(i<len){
        if(p[i]=='['){ //start of index
            mode=1;
            field[j]=0;
            root = cJSON_GetObjectItemCaseSensitive(root, field);
            memset(field,0,32);
            j=0;
        }
        if(p[i]==']'){
            num[k]=0;
            index=atoi(num);
            root = cJSON_GetArrayItem(root, index);
            memset(num,0,6);
            k=0;
        }
        if(p[i]=='.'){ //new field separator
            if(mode==0){
                field[j]=0;
                root = cJSON_GetObjectItemCaseSensitive(root, field);
             memset(field,0,32);
                j=0;
            } else { //step past the dot after a close bracket
                mode=0;
            }
        } else  {
            if(mode==0)field[j++]=p[i];
            else if(p[i]!='[')num[k++]=p[i];
        }
        i++;
    }
    root = cJSON_GetObjectItem(root, field);

    if (cJSON_IsObject(root)){
        cJSON_Delete(parse);
        error("Not an item");
        return;
    }
    if (cJSON_IsInvalid(root)){
        cJSON_Delete(parse);
        error("Not an item");
        return;
    }
    if (cJSON_IsNumber(root))
    {
        tempd = root->valuedouble;

        if((MMFLOAT)((int64_t)tempd)==tempd) IntToStr(a,(int64_t)tempd,10);
        else FloatToStr(a, tempd, 0, STR_AUTO_PRECISION, ' ');   // set the string value to be saved
        cJSON_Delete(parse);
        sret=(unsigned char *)a;
        sret=CtoM(sret);
        targ=T_STR;
        return;
    }
    if (cJSON_IsBool(root)){
        int64_t tempint;
        tempint=root->valueint;
        cJSON_Delete(parse);
        if(tempint)strcpy((char *)sret,"true");
        else strcpy((char *)sret,"false");
        sret=CtoM(sret);
        targ=T_STR;
        return;
    }
    if (cJSON_IsString(root)){
        strcpy(a,root->valuestring);
        cJSON_Delete(parse);
        sret=(unsigned char *)a;
        sret=CtoM(sret);
        targ=T_STR;
        return;
    }
    cJSON_Delete(parse);
    targ=T_STR;
    sret=(unsigned char *)a;
}
#endif