~MM.STARTUP,SYSTEM,*
------------------------------------
MM.STARTUP
There may be a need to execute some code on initial power up, perhaps to initialise some hardware, set some options or print a custom start-up banner. 
This can be accomplished by creating a subroutine with the name MM.STARTUP. 
When the PicoMite firmware is first powered up or reset it will search for this subroutine and, if found, it will be run once.
For example, if the Raspberry Pi Pico has a real time clock attached, the program could contain the following code:
SUB MM.STARTUP
RTC GETTIME
END SUB
This would cause the internal clock within MMBasic to be set to the current time on every power up or reset.
After the code in MM.STARTUP has been run MMBasic will continue with running the rest of the program in program memory. 
If there is no other code MMBasic will return to the command prompt. 
Note that you should not use MM.STARTUP for general setup of MMBasic (like dimensioning arrays, opening communication channels, etc) before running a program. 

The reason is that when you use the RUN command MMBasic will first clear the interpreter's state ready for a fresh start.
~MM.PROMPT,SYSTEM,*
------------------------------------
MM.PROMPT
If a subroutine with this name exists it will be automatically executed by MMBasic instead of displaying the command prompt. 
This can be used to display a custom prompt, set colours, define variables, etc all of which will be active at the command prompt.
Note that MMBasic will clear all variables and I/O pin settings when a program is run so anything set in this subroutine will only be valid 

for commands typed at the command prompt (i.e. in immediate mode).
As an example the following will display a custom prompt:
SUB MM.PROMPT
PRINT TIME$ "> ";
END SUB
However, DIM will create variables that are global that that should be used instead.

~MM.END,SYSTEM,*
------------------------------------
MM.END
If a subroutine named MM.END exists in the program it will be executed whenever the program ends with an actual or implied END command. 
It is not executed if the program ends with a Ctrl-C. 
The optional parameter 'noend' to the END command can be used to block execution of the MM.END subroutine if needed (see the END command for more information).
~MM.DEVICE$ ,PERIPHERALS,*
------------------------------------
MM.DEVICE$ 
A string representing the device or platform that MMBasic is running on. 
~MM.ADDRESS$ ,SYSVARS,*
------------------------------------
MM.ADDRESS$ 
This variable returns the IP address of the sender of the last UDP datagram received
~MM.CMDLINE$ ,PROGVARS,*
------------------------------------
MM.CMDLINE$ 
This constant variable containing any command line arguments passed to the current program is automatically created when an MMBasic program runs; 
see RUN and * commands for details.
-> Programs run from the Editor or using OPTION AUTORUN will set MM.CMDLINE$ to the empty string.
-> If not required this constant variable may be removed from memory using ERASE MM.CMDLINE$
~MM.ERRNO  ,PROGVARS,*
------------------------------------
MM.ERRNO  
If a statement caused an error which was ignored these variables will be set accordingly. MM.ERRNO is a number where non zero means that 
there was an error and MM.ERRMSG$ is a string representing the error message 
that would have normally been displayed on the console. They are reset to zero and an empty string by RUN, ON ERROR IGNORE or ON ERROR SKIP.
~MM.ERRMSG$  ,PROGVARS,*
------------------------------------
MM.ERRMSG$  
If a statement caused an error which was ignored these variables will be set accordingly. MM.ERRNO is a number where non zero means that 
there was an error and MM.ERRMSG$ is a string representing the error message 
that would have normally been displayed on the console. They are reset to zero and an empty string by RUN, ON ERROR IGNORE or ON ERROR SKIP. 
~MM.INFO() MM.INFO$(),MMBASIC,*
------------------------------------
MM.INFO() MM.INFO$()
These two versions can be used interchangeably but good programming practice would require that you use the one corresponding to the returned datatype. 
~MM.INFO$(AUTORUN),SYSVARS,*
------------------------------------
MM.INFO$(AUTORUN)
Returns the setting of the <t1>OPTION AUTORUN</t2> command 
~MM.INFO(ADC) ,HVARS,*
------------------------------------
MM.INFO(ADC) 
Returns the number of the buffer currently ready to read when using ADC RUN (1 or 2). Returns 0 if nothing ready.
~MM.INFO(ADC DMA) ,HVARS,*
------------------------------------
MM.INFO(ADC DMA) 
Returns true (1) if the ADC DMA is active.
~MM.INFO(BOOT COUNT),SYSVARS,*
------------------------------------
MM.INFO(BOOT COUNT)
Returns the number of times the Pico has been restarted since the flash drive was last formatted.
~MM.INFO$(CPUSPEED),HVARS,*
------------------------------------
MM.INFO$(CPUSPEED)
Returns the CPU speed as a string 
~MM.INFO$(LCDPANEL),HVARS,LCD,*
------------------------------------
MM.INFO$(LCDPANEL)
Returns the name of the configured LCD panel or a blank string.
~MM.INFO(LCD320),HVARS,*
------------------------------------
MM.INFO(LCD320)
Returns true if the display is capable of 320x240 operation using the OPTION LCD320 command
~MM.INFO$(SDCARD) ,HVARS,*
------------------------------------
MM.INFO$(SDCARD) 
Returns the status of the SD card. Valid returns are: DISABLED, NOT PRESENT, READY, and UNUSED 
~MM.INFO$(CURRENT),PROGVARS,*
------------------------------------
MM.INFO$(CURRENT)
Returns the name of the current program when loaded from a file or NONE if called after a NEW, AUTOSAVE, XMODEM or EDIT Command.
~MM.INFO$(PATH) ,PROGVARS,*
------------------------------------
MM.INFO$(PATH) 
Returns the path of the current program or NONE if called after a NEW or EDIT Command.
~MM.INFO(DISK SIZE) ,SYSVARS,*
------------------------------------
MM.INFO(DISK SIZE) 
Returns the capacity of the SD card in bytes 
~MM.INFO$(DRIVE) ,PROGVARS,*
------------------------------------
MM.INFO$(DRIVE) 
Returns the active drive "A:" or "B:" 
~MM.INFO(EXISTS FILE fname$) ,PROGVARS,*
------------------------------------
MM.INFO(EXISTS FILE fname$) 
Returns 1 if the file specified exists, returns -1 if fname$ is a directory, otherwise returns 0. 
~MM.INFO(EXISTS DIR dirname$) ,PROGVARS,*
------------------------------------
MM.INFO(EXISTS DIR dirname$) 
Returns a Boolean indicating whether the directory specified exits.
~MM.INFO$(FREE SPACE) ,SYSVARS,*
------------------------------------
MM.INFO$(FREE SPACE) 
Returns the free space on the SD card. 
~MM.INFO$(FILESIZE file$) ,PROGVARS,*
------------------------------------
MM.INFO$(FILESIZE file$) 
Returns the size of file$ in bytes or -1 if not found, -2 if a directory. 
~MM.INFO$(MODIFIED file$) ,PROGVARS,*
------------------------------------
MM.INFO$(MODIFIED file$) 
Returns the date/time that file$ was modified, Empty string if not found 
~MM.INFO(FCOLOUR) ,PROGVARS,COLORS,*
------------------------------------
MM.INFO(FCOLOUR) 
Returns the current foreground colour 
~MM.INFO(BCOLOUR) ,PROGVARS,COLORS,*
------------------------------------
MM.INFO(BCOLOUR) 
Returns the current background colour
~MM.INFO(FONT) ,PROGVARS,FONTS,*
------------------------------------
MM.INFO(FONT) 
Returns the number of the currently active font
~MM.INFO(FONT ADDRESS n) ,PROGVARS,*
------------------------------------
MM.INFO(FONT ADDRESS n) 
Returns the address of the memory location with the address of FONT n
~MM.INFO(FONT POINTER n) ,PROGVARS,*
------------------------------------
MM.INFO(FONT POINTER n) 
Returns a POINTER to the start of FONT n in memory
~MM.INFO(FONTHEIGHT) ,PROGVARS,FONTS,*
------------------------------------
MM.INFO(FONTHEIGHT) 
Integers representing the height of the current font (in pixels).
~MM.INFO(FONTWIDTH),PROGVARS,FONTS,*
------------------------------------
MM.INFO(FONTWIDTH)
Integers representing the width of the current font (in pixels).
~MM.INFO$(FLASH) ,PROGVARS,FLASH,*
------------------------------------
MM.INFO$(FLASH) 
Reports which flash slot the program was loaded from if applicable
~MM.INFO(FLASH ADDRESS n) ,PROGVARS,FLASH,*
------------------------------------
MM.INFO(FLASH ADDRESS n) 
Returns the address of the flash slot n.
~MM.INFO(HEAP) ,SYSVARS,*
------------------------------------
MM.INFO(HEAP) 
Returns the amount of MMbasic Heap memory free. MMBasic heap is used for strings, arrays and various other temporary and permanent buffers (eg, audio)
~MM.INFO(HPOS) ,PROGVARS,*
------------------------------------
MM.INFO(HPOS) 
The current horizontal position (in pixels) following the last graphics or print command.
~MM.INFO(VPOS) ,PROGVARS,*
------------------------------------
MM.INFO(VPOS) 
The current vertical position (in pixels) following the last graphics or print command.
~MM.INFO(ID) ,SYSVARS,*
------------------------------------
MM.INFO(ID) 
Returns the unique ID of the Pico.
~MM.INFO$(IP ADDRESS) ,SYSVARS,*
------------------------------------
MM.INFO$(IP ADDRESS) 
Returns the IP address of the WebMite
~MM.INFO(OPTION option)  ,PROGVARS,*
------------------------------------
MM.INFO(OPTION option)  
Returns the current value of a range of options that affect how a program will run. 
"option" can be one of AUTORUN, BASE, BREAK, DEFAULT, EXPLICIT, KEYBOARD, ANGLE, HEIGHT, WIDTH, FLASH SIZE
~MM.INFO$(PIN pinno) ,HVARS,*
------------------------------------
MM.INFO$(PIN pinno) 
Returns the status of I/O pin 'pinno'. Valid returns are: OFF, DIN, DOUT, AIN, etc
~MM.INFO$(PINNO GPnn) ,HVARS,*
------------------------------------
MM.INFO$(PINNO GPnn) 
Returns the physical pin number for a given GP number. 
GPnn can be an unquoted string (GP01), a string literal("GP01") or a string variable. 
Ie, A$="GP01": MM.INFO(PINNO A$) .
~MM.INFO(PERSISTENT),HVARS,WATCHDOG,BETA,*
------------------------------------
MM.INFO(PERSISTENT)
Recovers any value saved after SAVE PERSISTENT n% whitch saves the value n% in memory that will survive a watchdog reset (RP2040 and RP2350) or a physical reset (RP2040 only)
BETA 6.02 ONLY!
~MM.INFO(PIO RX DMA) ,HVARS,*
------------------------------------
MM.INFO(PIO RX DMA) 
Indicates whether the PIO RX DMA channel is busy
~MM.INFO(PIO TX DMA) ,HVARS,*
------------------------------------
MM.INFO(PIO TX DMA) 
Indicates whether the PIO TX DMA channel is busy
~MM.INFO$(PLATFORM) ,SYSVARS,*
------------------------------------
MM.INFO$(PLATFORM) 
Returns the string previously set with OPTION PLATFORM.
~MM.INFO(PS2) ,HVARS,PS2,*
------------------------------------
MM.INFO(PS2) 
Reports the last raw message received on the PS2 interface if enabled.
~MM.INFO$(SOUND),PROGVARS,*
------------------------------------
MM.INFO$(SOUND)
Returns the current activity on the audio output (OFF, PAUSED, TONE, WAV, FLAC, SOUND)
~MM.INFO(STACK),PROGVARS,*
------------------------------------
MM.INFO(STACK)
Returns the C stack pointer. Complex or recursive Basic code may result in the error "Stack overflow, expression too complex at depth %" 
This will occur when the stack is below &H 2003f800. 
Monitoring the stack will allow the programmer to identify simplifications to the Basic code to avoid the error.
~MM.INFO$(SYSTEM I2C) ,HVARS,*
------------------------------------
MM.INFO$(SYSTEM I2C) 
Returns I2C, I2C2, or NOT SET as applicable.
~MM.INFO(SYSTEM HEAP) ,SYSVARS,*
------------------------------------
MM.INFO(SYSTEM HEAP) 
Returns the free space on the System Heap.
~MM.INFO(TILE HEIGHT) ,PROGVARS,COLORS,*
------------------------------------
MM.INFO(TILE HEIGHT) 
Returns the current setting of the tile height.
~MM.INFO(TRACK) ,PROGVARS,*
------------------------------------
MM.INFO(TRACK) 
Returns the name of the FLAC, MP3, WAV or MIDI file currently playing on the audio output.
~MM.INFO$(TOUCH) ,HVARS,*
------------------------------------
MM.INFO$(TOUCH) 
Returns the status of the Touch controller. Valid returns are: "Disabled", "Not calibrated", and "Ready".
~MM.INFO(USB n) ,HVARS,*
------------------------------------
MM.INFO(USB n) 
Return the device code for any device connected to channel 'n' which is a number from 1 to 4. 
The returned device code can be: 
0=not in use, 
1=keyboard, 
2=mouse, 
128=ps4, 
129=ps3, 
130=SNES/Generic

By default a connected keyboard will be allocated to channel 1, a mouse the channel 2, and gamepads to channel 3 and then channel 4. 
If 2 or more keyboards or mice are connected or 3 or more gamepads then the additional devices will be allocated to the highest available channel.
~MM.INFO(VARCNT) ,PROGVARS,*
------------------------------------
MM.INFO(VARCNT) 
Returns the number of variables in use in the MMBasic program.
~MM.INFO$(LINE) ,PROGVARS,*
------------------------------------
MM.INFO$(LINE) 
Returns the current line number as a string. LIBRARY returned if in the Library and UNKNOWN if not in a program. 
Assists in diagnostics while unit testing.
~MM.INFO(UPTIME) ,SYSVARS,*
------------------------------------
MM.INFO(UPTIME) 
Returns the time in seconds since booted as a floating point number.
~MM.INFO(VERSION) ,SYSVARS,*
------------------------------------
MM.INFO(VERSION) 
Returns the version number as a floating point number
~MM.INFO(WRITEBUFF) ,PROGVARS,*
------------------------------------
MM.INFO(WRITEBUFF) 
Returns the address in memory of the current buffer used for drawing commands.
~MM.INFO(TCP PORT) ,PROGVARS,*
------------------------------------
MM.INFO(TCP PORT) 
WEBMITE ONLY 
Returns the TCP port set as a server or 0 if not set
~MM.INFO(UDP PORT) ,PROGVARS,*
------------------------------------
MM.INFO(UDP PORT) 
WEBMITE ONLY Returns the UDP port set as a server or 0 if not set
~MM.INFO(TCPIP STATUS) ,PROGVARS,*
------------------------------------
MM.INFO(TCPIP STATUS) 
Returns the TCPIP status of the connection 
~MM.INFO(WIFI STATUS) ,PROGVARS,*
------------------------------------
MM.INFO(WIFI STATUS) 
Returns the WIFI status of the connection. 
Valid returns are: 
0 WiFi is down  
1 Connected to WiFi  
2 Connected to WiFi, but no IP address (TCPIP STATUS only) 
3 Connected to WiFi with an IP address (TCPIP STATUS only) 
-1 Connection failed  
-2 No matching SSID found (could be out of range, or down) 
-3 Authentication failure
~MM.HRES ,PROGVARS,VIDEO SYSTEM,LCD,*
------------------------------------
MM.HRES 
Integers representing the current horizontal  resolution of the VGA/HDMI video output or the LCD display panel (if configured) in pixels.
~MM.VRES ,PROGVARS,VIDEO SYSTEM,LCD,*
------------------------------------
MM.VRES 
Integers representing the current vertical resolution of the VGA/HDMI video output or the LCD display panel (if configured) in pixels.
~MM.MESSAGE$ ,PROGVARS,*
------------------------------------
MM.MESSAGE$ 
Returns the contents of the last UDP datagram received
~MM.ONEWIRE ,HVARS,*
------------------------------------
MM.ONEWIRE 
Following a 1-Wire reset function this integer variable will be set to indicate the result of the operation: 
0 = Device not found, 
1 = Device found.
~MM.I2C ,HVARS,*
------------------------------------
MM.I2C 
Following an I2C write or read command this integer variable will be set to indicate the result of the operation as follows: 
0 = The command completed without error. 
1 = Received a NACK response 
2 = Command timed out
~MM.WATCHDOG ,SYSVARS,*
------------------------------------
MM.WATCHDOG 
An integer which is true if MMBasic was restarted as the result of a  Watchdog timeout (see the WATCHDOG command) otherwise false. 
~Options,ENVIRONMENT,*
------------------------------------
Options
More on options here
~OPTION ANGLE RADIANS | DEGREES ,MMBASIC,ENVIRONMENT,*
------------------------------------
OPTION ANGLE RADIANS | DEGREES 
This command switches trig functions between degrees and radians. 
Acts on SIN, COS, TAN, ATN, ATAN2, MATH ATAN3, ACOS, ASIN
~OPTION AUDIO PWMnApin, PWMnBpin  or OPTION AUDIO DISABLE ,HARDWARE,*
------------------------------------
OPTION AUDIO PWMnApin, PWMnBpin  or OPTION AUDIO DISABLE 
Configures one of the PWM channels as an audio output. 
'PWMnApin' is the left audio channel, 'PWMnBpin' is the right. Both pins must belong to the same audio channel. 
Example, OPTION AUDIO GP18, GP19 would use PWM1A and PWM1B on pins 24 and 25 respectively. 
This option prevents use of these pins in the BASIC program. 
The audio output is generated using PWM so a low pass filter is necessary on the output. 
The audio output from the Raspberry Pi Pico is very noisy. 
Using OPTION POWER and/or supplying power via a separate 3.3V linear regulator can reduce this. 
This command must be run at the command prompt (not in a program).
~OPTION AUDIO DISABLE,HARDWARE,*
------------------------------------
OPTION AUDIO DISABLE
DISABLE disables audio configurations 
~OPTION AUDIO SPI Cspin,  CLKpin, MOSIpin  or OPTION AUDIO DISABLE ,HARDWARE,*
------------------------------------
OPTION AUDIO SPI Cspin,  CLKpin, MOSIpin  or OPTION AUDIO DISABLE 
Configures the audio output to be directed to a MCP48n2 DAC connected to the specified pins. 
The LDAC pin on the DAC should be connected to GND.
~OPTION AUDIO VS1053 CLKpin, MOSIpin, MISOpin, XCSpin, XDCSpin, DREQpin, XRSTpin or OPTION AUDIO DISABLE,HARDWARE,*
------------------------------------
OPTION AUDIO VS1053 CLKpin, MOSIpin, MISOpin, XCSpin, XDCSpin, DREQpin, XRSTpin or OPTION AUDIO DISABLE
Configures the audio output to be directed to a VS1053 CODEC. 
This allows MP3 and MIDI playback in addition to the other formats supported and also supports real-time MIDI output. 
See the PLAY command for more details
~OPTION AUDIO I2S BCLKpin, DATApin,HARDWARE,AUDIO,I2S,DAC,BETA,*
------------------------------------
OPTION AUDIO I2S BCLKpin, DATApin
This allows a I2S DAC to be used to provide high quality Audio. The firmware transmits the data in 32-bit mode and FLAC, MP3, and WAV data is output with no loss of information.
This command reserves BCLKpin and BCLKpin+1 for the bit clock and word clock and Datapin for the I2S data. It requires an I2S DAC that can PLL the bitclock to create its own master clock. 
Tested on PCM5102A and TDA1387T , UDA1334A .

For example:
OPTION AUDIO I2S GP1,GP5
GP1 is the bit clock
GP2 is the word clock
GP5 is the data pin
All normal audio should work TONE, MP3, FLAC, WAV, MOD, SOUND
Note that you will see jitter on the BCLK. For the CPU at 150MHz the PIO needs to run at 44100*128 = 5644800 = 1/26.57 of the CPU clock speed. 
The way the H/W does this is to add or subtract clock ticks to the length of the PIO clock to give an average of 26.57 CPU ticks per PIO clock - hence jitter. 
I2S DAC could be enabled for RP2040. 
NB: for RP2040 VGA and WEB versions there are no PIO available for user use if I2S is enabled.RP2040 doesn't have enough memory or CPU to decode MP3  
NOTE: For VGA version the I2S PIO now shares with the VGA PIO (PIO0) so PIO 1 is available for use in MMBasic 
BETA 6.02 ONLY!
~OPTION AUTORUN OFF,MMBASIC,ENVIRONMENT,*
------------------------------------
OPTION AUTORUN OFF
Disables autorun.
~OPTION AUTORUN ON [,NORESET] or OPTION AUTORUN n [,NORESET] or OPTION AUTORUN OFF,ENVIRONMENT,*
------------------------------------
OPTION AUTORUN ON [,NORESET] or OPTION AUTORUN n [,NORESET] or OPTION AUTORUN OFF
Instructs MMBasic to automatically run a program on power up or restart. 
ON will cause the current program in program memory to be run. Specifying 'n' will cause that location in flash memory to be run. 'n' must be in the range 1 to 3. 
Specifying the optional parameter "NORESET" will maintain AUTORUN even if the program causes a system error (by default this will cause the firmware to cancel any OPTION AUTORUN setting). 
OFF will disable the autorun option and is the default for a new program. 
Entering the break key (default CTRL-C) at the console will interrupt the running program and return to the command prompt.
~OPTION BASE 0 | 1 ,MMBASIC,ENVIRONMENT,*
------------------------------------
OPTION BASE 0 | 1 
Set the lowest value for array subscripts to either 0 or 1. This must be used before any arrays are declared and is reset to the default of 0 on power up. 
~OPTION BAUDRATE nn ,ENVIRONMENT,*
------------------------------------
OPTION BAUDRATE nn 
Set the baudrate of the serial console (if it is configured). 
~OPTION BREAK nn ,ENVIRONMENT,*
------------------------------------
OPTION BREAK nn 
Set the value of the break key to the ASCII value 'nn'. 
This key is used  to interrupt a running program. 
The value of the break key is set to CTRL-C key at power up but it can be changed to any keyboard key using this command (for example, OPTION BREAK 4 will set the break key to the CTRL-D key). 
Setting this option to zero will disable the break function entirely. 
~OPTION CASE LOWER | UPPER | TITLE ,ENVIRONMENT,*
------------------------------------
OPTION CASE LOWER | UPPER | TITLE 
-> Change the case used for listing command and function names when using the LIST command. 

The default is TITLE but the old standard of MMBasic can be restored using OPTION CASE UPPER. 
This command must be run at the command prompt (not in a program). 
~OPTION COLOURCODE OFF,ENVIRONMENT,COLORS,*
------------------------------------
OPTION COLOURCODE OFF
Disables colours in EDITOR
~OPTION COLOURCODE ON or OPTION COLOURCODE OFF ,ENVIRONMENT,COLORS,*
------------------------------------
OPTION COLOURCODE ON or OPTION COLOURCODE OFF 
-> Turn on or off colour coding for the editor's output. Keywords will be in cyan, comments in yellow, etc. or 

The keyword COLORCODE (USA spelling) can also be used.  
This requires a terminal emulator that can interpret the appropriate  escape codes (eg, Tera Term). 
This command must be run at the command prompt (not in a program). 
~OPTION CONSOLE output,ENVIRONMENT,*
------------------------------------
OPTION CONSOLE output
Specifies where print statements will output. Valid settings are BOTH (i.e. SCREEN and SERIAL), SERIAL, SCREEN, NONE. 
This is a temporary option that is reset when a program exists.
~OPTION CPUSPEED ,HARDWARE,*
------------------------------------
OPTION CPUSPEED 
Change the CPU clock speed. 'speed' is the CPU clock in KHz in the range of 48000 to 378000. 
Speeds above 133MHz (150MHz for the RP2350) are regarded as overclocking as that is the specified maximum speed of the standard Raspberry Pi Pico. 
For VGA versions valid speeds are 126, 157.5, 252, 315, and 378MHz. The default is 126MHz. 
For other versions the CPU speed will default to 133000 (150000 for RP2350). 
This command must be run at the command prompt (not in a program).
~OPTION COUNT pin1, pin2, pin3, pin4,HARDWARE,*
------------------------------------
OPTION COUNT pin1, pin2, pin3, pin4
Specifies which pins are to be used as Count inputs. 
By default these are GP6, GP7, GP8 and GP9. The SETPIN command defines the Counter mode. 
This command must be run at the command prompt (not in a program). 
~OPTION DEFAULT FLOAT | INTEGER | STRING | NONE ,MMBASIC,ENVIRONMENT,*
------------------------------------
OPTION DEFAULT FLOAT | INTEGER | STRING | NONE 
If OPTION DEFAULT NONE Used to set the default type for a variable which is not explicitly defined. 
If OPTION DEFAULT NONE is used then all variables must have their type explicitly defined or the error "Variable type not specified" will occur. 
When a program is run the default is set to FLOAT for compatibility with Microsoft BASIC and previous versions of MMBasic.
~OPTION DEFAULT COLOURS foreground [,background],ENVIRONMENT,COLOR,COLOUR,*
------------------------------------
OPTION DEFAULT COLOURS foreground [,background]
Set the default foreground and background colours for both the monochrome and colour modes. 
The colour must be one of the following: 
white, yellow, lilac, brown, fuchsia, rust, magenta, red, cyan, green, cerulean, midgreen, cobalt, myrtle, blue and black. 
A numeric value cannot be used. The default is white, black. 
If background is omitted it defaults to black.
~OPTION DEFAULT MODE n ,ENVIRONMENT,VIDEO SYSTEM,COLORS,*
------------------------------------
OPTION DEFAULT MODE n 
-> This sets the default display mode on boot. This command must be run at the command prompt (not in a program).
~OPTION DISK SAVE fname$ | OPTION DISK LOAD fname$ ,ENVIRONMENT,*
------------------------------------
OPTION DISK SAVE fname$ | OPTION DISK LOAD fname$ 
These commands let the user save and restore the complete set of options defined to and from a disk file. 
The file could then be transferred to a host computer using XMODEM allowing additional devices to be easily configured or options recovered after a firmware upgrade 
~OPTION DISPLAY lines [,chars],ENVIRONMENT,*
------------------------------------
OPTION DISPLAY lines [,chars]
Set the characteristics of the display terminal used for the console. 
Both the LIST and EDIT commands need to know this information to correctly format the text for display.  
'lines' is the number of lines on the display and 'chars' is the width of the display in characters. 
The default is 24 lines x 80 chars and when changed this option will be  remembered even when the power is removed. 
Maximum values are 100 lines and 240chars.  This will send an ESC sequence to set the VT100 terminal to the matching size. 
TerraTerm, Putty and MMCC respond to this sequence and set the terminal width (if the option is enabled in the terminal setup). 
This option is not available if an LCD display is being used as the console.
~OPTION ESCAPE,ENVIRONMENT,*
------------------------------------
OPTION ESCAPE
Enables the ability to insert escape sequences into string constants. See the section Special Characters in Strings.
~OPTION EXPLICIT ,ENVIRONMENT,*
------------------------------------
OPTION EXPLICIT 
Placing this command at the start of a program will require that every variable be explicitly declared using the DIM, LOCAL or STATIC commands 
before it can be used in the program. This option is disabled by default when a program is run. 
If it is used it must be specified before any variables are used. 
~OPTION FAST AUDIO ON|OFFF,HARDWARE,*
------------------------------------
OPTION FAST AUDIO ON|OFFF
When using the PLAY SOUND command, changes to sounds, volumes, or frequencies can cause audible clicks in the output. 
The firmware attempts to mitigate this by ramping the volume down on the channel's previous output before changing the output and ramping it back up again. 
This significantly improves the audio output but at the expense of a short delay in the PLAY SOUND command (worst case 3mSec). 
This delay can be avoided using OPTION FAST AUDIO ON in a program.  
The audible clicks may then re-appear but this is at the programmer's discretion. 
This is a temporary option that is reset to OFF whenever a program is run.
~OPTION FNKey string$,ENVIRONMENT,FN,*
------------------------------------
OPTION FNKey string$
Define the string that will be generated when a function key is pressed at the command prompt. 
'FNKey' can be F1, and F5 thru to F9. 
Example: 
OPTION F8 "RUN "+chr$(34)+"myprog" +chr$(34)+chr$(13)+chr$(10). 
This command must be run at the command prompt (not in a program). 
~OPTION HDMI PINS  clockpositivepin, d0positivepin, d1positivepin, d2positivepin ,HARDWARE,*
------------------------------------
OPTION HDMI PINS  clockpositivepin, d0positivepin, d1positivepin, d2positivepin 
Set the I/O pins used for the HDMI video output. 
This is only required to suit nonstandard PCB layouts. 
The positive HDMI signal pins are set according to 'nbr' below. 
Valid values are 0-7 and the pins must not overlap for each channel. 
If 'nbr' is an even number the negative output is on physical pin+1, if 'nbr' is odd it will be on physical pin-1. 
Nbr |HSTX   |Nbr    |Physical 
                     Pin
|0  |HSTX0  |GP12   |   1 
|1  |HSTX1  |GP13   |   2
|2  |HSTX2  |GP14   |   3
|3  |HSTX3  |GP15   |   4
|4  |HSTX4  |GP16   |   5 
|5  |HSTX5  |GP17   |   6 
|6  |HSTX6  |GP18   |   7
|7  |HSTX7  |GP19   |   8

The default is: OPTION HDMI PINS 2, 0, 6, 4 
Which means that:
CK+ and CK- are allocated to GP14 and GP15
D0+ and D0- are allocated to GP12 and GP13
D1+ and D1- are allocated to GP18 and GP19
D2+ and D2- are allocated to GP16 and GP17
~OPTION HEARTBEAT ON/OFF ,ENVIRONMENT,*
------------------------------------
OPTION HEARTBEAT ON/OFF 
Enables or disables the output of the heartbeat LED. If it is disabled the program can control the LED via GP25.
~OPTION KEYBOARD nn [,capslock] [,numlock] [,repeatstart] [,repeatrate] or OPTION KEYBOARD DISABLE,ENVIRONMENT,PS2,*
------------------------------------
OPTION KEYBOARD nn [,capslock] [,numlock] [,repeatstart] [,repeatrate] or OPTION KEYBOARD DISABLE
Configure a keyboard. This can be used for console input and any characters typed will be available via any commands that read from the console (serial over USB). 
'nn is a two character code defining the keyboard layout. 
The choices are US for the standard keyboard layout in the USA, Australia and New Zealand and UK for the United Kingdom, 
GR for Germany, FR for France, BR for Brazil and ES for Spain. 
This command must be entered at the command line and will cause a reboot. 
This setting can be reset with: 
OPTION KEYBOARD DISABLE 
The optional parameters 'capslock' and 'numlock' are true/false integers that set the initial state of the keyboard (default is 0 and 1). 
The optional parameters 'repeatstart' and 'repeatrate' set the time for the first repeat of a key that is held down and subsequent repeats. 
For a USB keyboard they are 100 to 2000mSec and 25 to 2000mSec. 
For a PS2 keyboard they are 0 to 3 indicating 250mSec, 500mSec, 750mSec and 1000mSec (default is 1) and 0 to 31 indicating 33mSec to 500mSec 
as per the PS2 keyboard specification (default is 12 or 100mSec).
~OPTION KEYBOARD I2C,HARDWARE,*
------------------------------------
OPTION KEYBOARD I2C
Configures support for the Solderparty bbq20 mini I2C keyboard. 
Note: OPTION SYSTEM I2C must be set before executing this command
~OPTION KEYBOARD PINS clockpin, datapin,HARDWARE,*
------------------------------------
OPTION KEYBOARD PINS clockpin, datapin
Allows the user to select the pins to be used for connecting a PS2 keyboard. 
The default is pin 11 (GP8) and pin 12 (GP9). 
The PS2 keyboard must be disabled (OPTION KEYBOARD DISABLE)
~OPTION KEYBOARD REPEAT repeatstart , repeatrate ,HARDWARE,*
------------------------------------
OPTION KEYBOARD REPEAT repeatstart , repeatrate 
Sets the time for the first repeat of a key that is held down (100-2000) and subsequent repeats (25-2000) in  milliseconds.
~OPTION LCDPANEL /* ,ENVIRONMENT,*
------------------------------------
OPTION LCDPANEL /* 
Configures an LCD panel on versions that accept a connected LCD.
~OPTION LCDPANEL VIRTUAL_C  or OPTION LCDPANEL VIRTUAL_M,ENVIRONMENT,LCD,*
------------------------------------
OPTION LCDPANEL VIRTUAL_C  or OPTION LCDPANEL VIRTUAL_M
Configures a virtual LCD panel without a physically connected panel. 
VIRTUAL _C = Colour, 4bit, 320 x 240 
VIRTUAL _M = Monochrome, 640 x 480 
Using this feature a program can draw graphical images on this virtual panel and then save them as a BMP file. 
Useful for creating a graphic image for export without an attached display
~OPTION LCDPANEL options or OPTION LCDPANEL DISABLE,ENVIRONMENT,LCD,*
------------------------------------
OPTION LCDPANEL options or OPTION LCDPANEL DISABLE
Configures the PicoMite firmware to work with an attached LCD panel. 
See the chapter LCD Displays for the details. This command must be run at the command prompt (not in a program).
~OPTION LCDPANEL CONSOLE [font [, fc [, bc [,blight]]] [,NOSCROLL] or OPTION LCDPANEL NOCONSOLE,ENVIRONMENT,LCD,*
------------------------------------
OPTION LCDPANEL CONSOLE [font [, fc [, bc [,blight]]] [,NOSCROLL] or OPTION LCDPANEL NOCONSOLE
Configures the LCD display panel for use as the console output. 
The LCD must support transparent text (i.e. the SSD1963_x, ILI9341 or ST7789_320 controllers). 
'font' is the default font, 'fc' is the default foreground colour, 'bc' is the default background colour. 
These parameters are optional and default to font 1, white, black and 100%. 
These settings are applied at power up. 
The optional NOSCROLL command changes the firmware such that when outputting to the last line of the display rather 
than the display scrolling it is cleared and output continues at the top of the display. 
This allows displays that don't support reading to be used as a console device and: 
Note that for displays other than the SSD1963 scrolling for any console output is very slow so it is recommended to use the NOSCROLL option for these displays. 

This setting is saved in flash and will be automatically applied on startup. 
To disable it use the OPTION LCDPANEL NOCONSOLE command. 
This command must be run at the command prompt.
~OPTION LCDPANEL USER hres, vres,HARDWARE,*
------------------------------------
OPTION LCDPANEL USER hres, vres
Configures a user written display driver in MMBasic. 
See the file "User Display Driver.txt" in the PicoMite firmware distribution for a description of how to write the driver.
~OPTION LCDPANEL CONSOLE [font [, fc [,bc]] or OPTION LCDPANEL NOCONSOLE /* ,ENVIRONMENT,FONTS,*
------------------------------------
OPTION LCDPANEL CONSOLE [font [, fc [,bc]] or OPTION LCDPANEL NOCONSOLE /* 
Changes the default font used on the VGA or HDMI display. 
'fc' is the foreground colour and 'bc' is the background colour. Disables the console output to the VGA/HDMI display. 
This option is permanent, both print output and console output will be disabled and only graphics commands will output to the VGA screen 
If output is required to be temporarily disabled in a program use the OPTION CONSOLE command
~OPTION LCD320 ON/OFF ,ENVIRONMENT,LCD,*
------------------------------------
OPTION LCD320 ON/OFF 
This enables or disables 16-bit LCD displays in 320x240 mode allowing things like games on these larger LCD displays. 
In the case of 800x480 displays the 320x240 image is scaled by 2 and occupies the screen area 80,0 to 719,479 
In the case of 480x272 displays the 320x240 image is windowed and occupies the screen area 80,16 to 399,255
~OPTION LEGACY ON or OPTION LEGACY OFF  ,MMBASIC,ENVIRONMENT,*
------------------------------------
OPTION LEGACY ON or OPTION LEGACY OFF  
This will turn on or off compatibility mode with the graphic commands used in the original Colour Maximite. 
The commands COLOUR, LINE, CIRCLE and PIXEL use the legacy syntax and all drawing commands will accept colours in the range of 0 to 7. 

Notes: 
-Keywords such as RED, BLUE, etc are not implemented so they should be defined as constants if needed. 

Refer to the Colour Maximite MMBasic Language Manual for the syntax of the legacy commands.  This can be downloaded from ttps://geoffg.net/OriginalColourMaximite.html . 
~OPTION LIST ,ENVIRONMENT,*
------------------------------------
OPTION LIST 
This will list the settings of any options that have been changed from their default setting and are the permanent type. 
~OPTION MILLISECONDS ON|OFF,MMBASIC,ENVIRONMENT,*
------------------------------------
OPTION MILLISECONDS ON|OFF
This enables or disables a millisecond output in the TIME$ function. 
Ie, HH:MM:SS.mmm The milliseconds counter is set to zero whenever the time is updated using the TIME command, WEB NTP command or RTC GETTIME command. Default is OFF.
~OPTION MODBUFF ENABLE/DISABLE [sizeinK],ENVIRONMENT,*
------------------------------------
OPTION MODBUFF ENABLE/DISABLE [sizeinK]
Creates or removes an area of flash memory used for loading and playing .MOD files. 
If enabled then a mod buffer is created with a size of 128Kbytes. 
This can be overridden with "sizeinK". 
Note that this option reserves part of the Flash Filesystem (ie, it shrinks the Flash Filesystem). 

The default is disabled.
~OPTION MOUSE CLKpin, DATApin ,HARDWARE,MOUSE,PS2,*
------------------------------------
OPTION MOUSE CLKpin, DATApin 
Set the pins to be used to connect a PS2 mouse. 
Using this command the mouse is automatically configured on boot and you can set up interrupts and read values with no additional commands. 
This is different from the MOUSE OPEN which only connects to a mouse while the program is running. 
The PS2 mouse MUST be is disabled.
NOTE: The mouse only works in the editor in mode 1. This is because it uses the tile mechanism do do the highlighting. If you are in mode 2 the editor automatically switches to mode 1 during the edit hence that also works.
~OPTION MOUSE DISABLE ,HARDWARE,MOUSE,*
------------------------------------
OPTION MOUSE DISABLE 
Disables the automatic connection to a PS2 mouse and frees up the pins for normal usage.
~OPTION PICO ON/OFF,HARDWARE,*
------------------------------------
OPTION PICO ON/OFF
When set to OFF pins GP23, GP24 and GP29 are not set up for normal. 
Pico use and are immediately available to use. Default ON for RP2350A and RP2040, OFF for RP2350B
~OPTION PIN nbr ,ENVIRONMENT,*
------------------------------------
OPTION PIN nbr 
Set 'nbr' as the PIN (Personal Identification Number) for access to the console prompt. 
'nbr' can be any non zero number of up to eight digits. 
Whenever a running program tries to exit to the command prompt for whatever reason MMBasic will request this number before the prompt is presented. 
This is a security feature as without access to the command prompt an intruder cannot list or change the program in memory or modify the operation of MMBasic in any way.
To disable this feature enter zero for the PIN number (i.e. OPTION PIN 0). A permanent lock can be applied by using 99999999 for the PIN number. 
If a permanent lock is applied or the PIN number is lost the only way to recover is to erase the Raspberry Pi Pico flash to factory defaule and then reload the PicoMiteVGA firmware.
This command must be run at the command prompt (not in a program). 
~OPTION PLATFORM name$,ENVIRONMENT,*
------------------------------------
OPTION PLATFORM name$
Allows a user to identify a particular hardware configuration that can then be used in programs to control the program's operation. 
'name$' can be up to 31 characters long. This is a permanent option. MM.INFO$(PLATFORM) returns this string. 
For example, this can be used on a particular hardware configuration: OPTION PLATFORM "GameMite" 
Then programs that might run on this or other platforms can use: 
IF MM.INFO$(PLATFORM) = "GameMite" THEN ...
~OPTION POWER PFM | PWM,HARDWARE,*
------------------------------------
OPTION POWER PFM | PWM
By default this runs in PFM mode. PWM gives better noise performance but is less power-efficient. 
Note that under heavy load the system will run in PWM mode regardless of this setting. 
~OPTION PSRAM PIN n  or OPTION PSRAM DISABLE,HARDWARE,*
------------------------------------
OPTION PSRAM PIN n  or OPTION PSRAM DISABLE
Enable/disable PSRAM support. 'n' is the PSRAM chip select (CS) pin and can be 0, 8, 19, or 47. 
Typically GP47 is used for Pimoroni boards. Default is disabled. 
~OPTION RESET  ,ENVIRONMENT,*
------------------------------------
OPTION RESET  
Reset all saved options to their default values. 
This command must be run at the command prompt (not in a program).
~OPTION RESET cfg or OPTION RESET LIST,ENVIRONMENT,*
------------------------------------
OPTION RESET cfg or OPTION RESET LIST
Reset all options to default values for the configuration 'cfg'. 
OPTION RESET LIST will list all available configurations. 
This command must be run at the command prompt (not in a program).
~OPTION RESOLUTION nn [,cpuspeedinKhz] ,ENVIRONMENT,*
------------------------------------
OPTION RESOLUTION nn [,cpuspeedinKhz] 
For firmware with HDMI video set the video resolution to 'nn'. 
Where 'nn' is: 640x480 or 640 // 1280x720 or 1280 // 1024x768 or 1024.  
For 640x480 the display frequency can be set to 60Hz (252Mhz) or 75Hz (315MHz) by appending 'cpuspeedinKHz' to the command (ie, 252000 or 315000). 
Each HDMI resolution can operate in a number of modes which are set using the MODE command.
beta 02b17 
Now supports 800x600 operations which should be perfect for the 10.1" monitors and any standard 4:3 monitors and improves compatibility with some CMM2 programs.
This trades heap for framebuffer space so heap is reduced from 180KB to 100KB but it allows 800x600x16 colour operation (with mapping to any RGB332 colour) which is the highest colour resolution available.

OPTION RESOLUTION 800

MODE 1 
800x600x1-bit with tiles
MODE 2 
400x300x4-bit with layers
MODE 3 
800x600x4-bit
MODE 5 
400x300x8-bit (RGB332) with layer
~OPTION RTC AUTO ENABLE | DISABLE ,ENVIRONMENT,*
------------------------------------
OPTION RTC AUTO ENABLE | DISABLE 
-> Enable auto-load time$ & date$ from RTC on boot & every hour. 

If enabled and the RTC does not respond then any running program will abort with an error. 
At the command prompt an information message will be output. 
This command must be run at the command prompt (not in a program). 
~OPTION SDCARD Cspin [,CLKpin, MOSIpin, MISOpin] or OPTION SDCARD DISABLE,HARDWARE,*
------------------------------------
OPTION SDCARD Cspin [,CLKpin, MOSIpin, MISOpin] or OPTION SDCARD DISABLE
Specify or disable the I/O pins to use for the SD Card interface. 
If the optional pins are not specified the SD Card will use the pins specified by OPTION SYSTEM SPI. 
Note: The pins specified by OPTION SYSTEM SPI must be a valid set of hardware SPI pins (SPI or SPI2), however, the pins specified by OPTION SDCARD can be any pins. 

The pins specified by OPTION SYSTEM SPI and OPTION SDCARD cannot be the same. 
This command must be run at the command prompt (not in a program).
~OPTION SDCARD COMBINED CS,HARDWARE,*
------------------------------------
OPTION SDCARD COMBINED CS
If this is specified the touch chip select pin is also used for the Sdcard. In this case external circuitry is needed to implement the SD chip select as follows.
PICT (NEEDS PIC)
The firmware uses the touch pin as follows: 
TOUCH_CS low: TOUCH_CS low, SD_CS high | TOUCH CS high: SD_CS low: TOUCH_CS high | TOUCH CS set as input (high impedance) TOUCH_CS and SD_CS high.
~OPTION SERIAL CONSOLE uartapin, uartbpin,HARDWARE,*
------------------------------------
OPTION SERIAL CONSOLE uartapin, uartbpin
Specify that the console be accessed via a hardware serial port (instead of virtual serial over USB). 
'uartapin' and 'uartbpin' can be any valid pair of rx and tx pins for either COM1 or COM2. 
The order that they are specified is not important. 
The speed defaults to 115200 baud but can be changed with OPTION BAUDRATE. 
~OPTION SERIAL CONSOLE DISABLE ,ENVIRONMENT,*
------------------------------------
OPTION SERIAL CONSOLE DISABLE 
Revert to the normal the USB console. 
These commands must be run at the command prompt (not in a program). 
~OPTION SYSTEM I2C sdapin, sclpin [,SLOW/FAST],HARDWARE,*
------------------------------------
OPTION SYSTEM I2C sdapin, sclpin [,SLOW/FAST]
Specify the I2C port and pins for use by system devices (LCD panel, and RTC). 
The PicoMite firmware uses a specific I2C port for system devices, leaving the other for the programmer. 
This command specifies which pins are to be used, and hence which of the I2C ports is to be used.  
The pins allocated to the SYSTEM I2C will not be available for other MMBasic SETPIN settings but can be used for additional I2C devices using the standard I2C command. 
Note: I2C(2) OPEN and I2C(2) CLOSE are not available in this case. By default the I2C port is opened at a speed of 400KHz and with a 100mSec timeout. 

The I2C frequency can be set using the optional third parameter which can take the values FAST = 400KHz or SLOW = 100KHz. 
This command must be run at the command prompt (not in a program).
~OPTION SYSTEM SPI CLKpin, MOSIpin, MISOpin or OPTION SYSTEM SPI DISABLE,HARDWARE,*
------------------------------------
OPTION SYSTEM SPI CLKpin, MOSIpin, MISOpin or OPTION SYSTEM SPI DISABLE
Specify or disable the SPI port and pins for use by system devices (SD Card, LCD panel, etc). 
The PicoMite firmware uses a specific hardware SPI port for system devices, leaving the other for the user. 
This command specifies which pins are to be used, and hence which of the SPI ports is to be used. 
The pins allocated to the SYSTEM SPI will not be available to other MMBasic commands. 
This command must be run at the command prompt (not in a program).
~OPTION TAB 2 | 3 | 4 | 8 ,ENVIRONMENT,*
------------------------------------
OPTION TAB 2 | 3 | 4 | 8 
-> Set the spacing for the tab key. Default is 2. 
~OPTION TCP SERVER PORT n  ,ENVIRONMENT,*
------------------------------------
OPTION TCP SERVER PORT n  
Launches a TCP server on port 'n' during every restart of the WebMite. 
Typically HTTP servers use port 80.
USE "OPTION TCP SERVER PORT 0" to disable 
When the server is running it can respond to up to MM.INFO(MAX CONNECTIONS) 
~OPTION TELNET CONSOLE OFF|ONLY|ON ,ENVIRONMENT,*
------------------------------------
OPTION TELNET CONSOLE OFF|ONLY|ON 
Configures the handling the console over Telnet. 
ON = Console output sent to USB and Telnet 
ONLY= Console output sent to Telnet only 
OFF = Console output sent to USB only 
~OPTION TFTP OFF|ON ,ENVIRONMENT,*
------------------------------------
OPTION TFTP OFF|ON 
Enables or disables the TFTP server. 
Default is on. 
~OPTION TOUCH FT6336 IRQpin, RESETpin [,BEEPpin] [,sensitivity],HARDWARE,LCD,BETA,*
------------------------------------
OPTION TOUCH FT6336 IRQpin, RESETpin [,BEEPpin] [,sensitivity]
Configures MMBasic for the touch sensitive feature of an attached LCD panel. 
'T_CS pin' and 'T_IRQ pin' are the I/O pins to be used for chip select and touch interrupt respectively (any free pins can be used). 
The remaining pins are connected to those specified using the OPTION SYSTEM SPI command. 
'Beep' is an optional pin which can be connected to a small buzzer/beeper to generate a "click" or beep sound when an Advanced Graphics control is touched 
(ie, radio button, switch, etc). This is described in Advanced Graphics Functions.pdf. This command must be run at the command prompt (not in a program).
'sensitivity' is a number between 0 and 255 - defaults to 50, lower is more sensitive. 
SDA and SCK should be connected to valid I2C pins and set up with OPTION SYSTEM I2C
TOUCH function works as before + TOUCH(X2) and TOUCH(Y2) returns the position of a second touch location or -1 if no second location is touched.
BETA 6.02 ONLY!
~OPTION TOUCH T_CS pin, T_IRQ pin [, Beep] or OPTION TOUCH DISABLE ,HARDWARE,LCD,*
------------------------------------
OPTION TOUCH T_CS pin, T_IRQ pin [, Beep] or OPTION TOUCH DISABLE 
Configures MMBasic for the touch sensitive feature of an attached LCD panel. 
'T_CS pin' and 'T_IRQ p
~OPTION VCC voltage ,HARDWARE,*
------------------------------------
OPTION VCC voltage 
Specifies the voltage (Vcc) supplied to the Raspberry Pi Pico. 
When using the ADC pins to measure voltage the PicoMite firmware uses the voltage on the pin marked VREF as its reference. 
This voltage can be accurately measured using a DMM and set using this command for more accurate measurement.
The parameter is not saved and should be initialised either on the command line or in a program. 
The default if not set is 3.3.
~OPTION UDP SERVER PORT n ,ENVIRONMENT,*
------------------------------------
OPTION UDP SERVER PORT n 
Sets up a listening socket on the port specified. 
Any UDP datagrams received on that port will be processed and the contents saved in MM.MESSAGE$. 
The IP address of the sender will be stored in MM.ADDRESS$. 
Note: If the UDP datagram is longer than 255 characters then any extra is discarded. 
USE "OPTION UDP SERVER PORT 0" to disable
~OPTION VGA PINS HSYNCpin, BLUEpin ,HARDWARE,*
------------------------------------
OPTION VGA PINS HSYNCpin, BLUEpin 
Changes the pins used for VGA display output allowing more flexibility in PCB design or wiring. 
"HSYNCpin" defines the start of a pair of contiguous GP numbered pins that are connected to HSYNC and VSYNC "BLUEpin" defines the start of four contiguous GP numbered pins that are connected to BLUE, GREEN_LSB, GREEN_MSB, and RED.
~OPTION WEB MESSAGES ON/OFF ,ENVIRONMENT,*
------------------------------------
OPTION WEB MESSAGES ON/OFF 
Disable informational web messages when set to OFF. Default is ON
~OPTION WIFI ssid$, passwd$, [name$] [,ipaddress$, mask$, gateway$] ,ENVIRONMENT,*
------------------------------------
OPTION WIFI ssid$, passwd$, [name$] [,ipaddress$, mask$, gateway$] 
Configures the firmware to automatically connect to a WiFi network on restart. 
'ssid$' is the name of the network and 'password$' is the access password. 
Both are strings and if string constants are used they should be quoted. 
Optionally a name for the device can be specified 'name$', otherwise a name is created from the unique device ID.
Optionally, a static IP address, IP mask, and gateway address can be specified as 'ipaddress$', 'mask$', 'gateway$' 
eg, OPTION WIFI "mysid","mypassword", "myPico", "192.168.1.111", "255.255.255.0", "192.168.1.1"
~About MBASIC commands,MMBASIC,*
------------------------------------
About MBASIC commands
Square brackets indicate that the parameter or characters are optional. 
' (single quotation mark) 
Starts a comment and any text following it will be ignored. Comments can be placed anywhere on a line. 
? (question mark) 
Shortcut for the PRINT command. 
/* ...........  */ 
Start and end of multiline comments. /* and */ must be the first non-space characters at the start of a line and have a space or end-of-line after them (i.e. they are MMBasic commands). 
Multi-line comments cannot be used inside subroutines and functions. Any characters after */ on a line are also treated as  a comment.
~A:  B:      Shortcut,FILE SYSTEM,DRIVE,STORAGE,*
------------------------------------
A:  B:      Shortcut
Shortcut for DRIVE "A:" and DRIVE "B:" at the command prompt
~ADC,HARDWARE,*
------------------------------------
ADC
The ADC commands provide an alternate method of recording analog inputs and are intended for high speed recording of many readings into an array.
~ADC OPEN freq, n_channels [,interrupt] ,HARDWARE,*
------------------------------------
ADC OPEN freq, n_channels [,interrupt] 
This allocates up to 4 ADC channels for use and sets them to be converted at the specified frequency. The range of pins are GP26, GP27, GP28, and GP29 for the RP2940 and RP2350A. 
Plus GP55, GP56, GP57, GP58 on the RP2350B. 
If the number of channels is one then it will always be GP26 used, if two then GP26 and GP27 are used, etc. 
Sampling of multiple channels is sequential (there is only one ADC). The pins are locked to the function when ADC OPEN is active 
The maximum total frequency is CPUspeed/96 (eg, 346KHz if all four channels are to be sampled with the CPU set at 133MHz). 
Note that a aggregate sampling frequency over 500Khz is overclocking the ADC. 

The optional interrupt parameter specifies an interrupt to call when the conversion completes. If not specified then conversion will be blocking
~ADC FREQUENCY freq ,HARDWARE,*
------------------------------------
ADC FREQUENCY freq 
This changes the sampling frequency of the ADC conversion without having to close and re-open 
~ADC CLOSE ,HARDWARE,*
------------------------------------
ADC CLOSE 
Releases the pins to normal usage 
~ADC START array1!() [,array2!()] [,array3!()] [,array4!()] [,Chan4arr!()] [,C1min] [,C1max] [,C2min] [,C2max] [,C3min] [,C3max] [,C4min] [,C4max] ,HARDWARE,*
------------------------------------
ADC START array1!() [,array2!()] [,array3!()] [,array4!()] [,Chan4arr!()] [,C1min] [,C1max] [,C2min] [,C2max] [,C3min] [,C3max] [,C4min] [,C4max] 
This starts conversion into the specified arrays. The arrays must be floating point and the same size. The size of the arrays defines the number of conversions. 
Start can be called repeatedly once the ADC is OPEN 'Cxmin' and 'Cxmax' will scale the readings. 
For example, C1min=200 and C1max=100 will create values ranging from 200 to 100 for equivalent voltages of 0 - 3.3. 
If the scaling is not used the results are returned as a voltage between 0 and OPTION VCC (defaults to 3.3V).
~ADC RUN array1%(),array2%),HARDWARE,*
------------------------------------
ADC RUN array1%(),array2%)
Runs the ADC continuously in double buffered mode. The ADC first fills array1% and then array2% and then back to array1% etc. 
If more than one ADC channel is specified in the ADC OPEN command the data are interleaved. 
The data is returned as packed 8-bit values (Use MEMORY UNPACK to convert to a normal array). 
MM.INFO(ADC) will return the number of the buffer currently available for reading (1 or 2).
~ARC x, y, r1, [r2], a1, a2 [, c] ,DRAWING,*
------------------------------------
ARC x, y, r1, [r2], a1, a2 [, c] 
Draws an arc of a circle with a given colour and width between two radials (defined in degrees). 
Parameters for the ARC command are: x: X coordinate of centre of arc y: Y coordinate of centre of arc r1: inner radius of arc r2: 
outer radius of arc -can be omitted if 1 pixel wide a1: start angle of arc in degrees a2: end angle of arc in degrees c: Colour of arc (if omitted it will default to the foreground colour) 
Zero degrees is at the 12 o'clock position. 
~AUTOSAVE or AUTOSAVE CRUNCH or AUTOSAVE APPEND ,WORKFLOW,*
------------------------------------
AUTOSAVE or AUTOSAVE CRUNCH or AUTOSAVE APPEND 
Enter automatic program entry mode. 
This command will take lines of text from the console serial input and save them to program memory. 
This mode is terminated by entering Control-Z or F1 which will then cause the received data to be transferred into program memory overwriting the previous program. 
Use F2 to exit and immediately run the program. The CRUNCH option instructs MMBasic to remove all comments, blank lines and unnecessary spaces from the program before saving. 
This can be used on large programs to allow them to fit into limited memory. 
CRUNCH can be abbreviated to the single letter C. The APPEND option will leave the existing program intact and append the new data from the serial input to the end of it. 
At any time this command can be aborted by Control-C which will leave program memory untouched. This is one way of transferring a BASIC program into the Raspberry Pi Pico. 
The program to be transferred can be pasted into a terminal emulator and this command will capture the text stream and store it into program memory. 
It can also be used for entering a small program directly at the console input.
~BACKLIGHT n [,DEFAULT] ,HARDWARE,*
------------------------------------
BACKLIGHT n [,DEFAULT] 
Sets the display backlight, valid values are 0 to 100. 
If DEFAULT is specified then the firmware will automatically set the backlight to that level on power-up. 
This is particularly useful for battery operation where reducing  the backlight level can significantly increase battery life 
~BITBANG ,HARDWARE,*
------------------------------------
BITBANG 
Replaced by the command DEVICE. For compatibility BITBANG can still be used in programs and will be automatically converted to DEVICE
~BLIT,MEMORY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT
BLIT is a simple memory operation copying to and from a display or memory to a display or memory. 

Notes:
- 32 buffers are available ranging from #1 to #32.  When specifying the buffer number the # symbol is optional. All other arguments are in pixels.
~BLIT READ [#]b, x, y, w, h ,GRAPHIC,VIDEO MEMORY,MEMORY,*
------------------------------------
BLIT READ [#]b, x, y, w, h 
BLIT READ will copy a portion of the display to the memory buffer '#b'. 
The source coordinate is 'x' and 'y' and the width of the display area to copy is 'w' and the height is 'h'. 
When this command is used the memory buffer is automatically created and sufficient memory allocated. 
This buffer can be freed and the memory recovered with the BLIT CLOSE command.
~BLIT WRITE [#]b, x, y [,mode] %, x, y [,col] ,GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT WRITE [#]b, x, y [,mode] %, x, y [,col] 
BLIT WRITE will copy the memory buffer '#b' to the display. The destination coordinate is 'x' and 'y'. 
The optional 'mode' parameter defaults to 0 and specifies how the stored image data is changed as it is written out. 
It is the bitwise AND of the following values: 
&B001 = mirrored left to right ; 
&B010 = mirrored top to bottom ; 
&B100 = don't copy transparent pixels
~BLIT LOAD[BMP] [#]b, fname$ [,x] [,y] [,w] [,h],GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT LOAD[BMP] [#]b, fname$ [,x] [,y] [,w] [,h]
BLIT LOAD will load a blit buffer from a 24-bit bmp image file. x,y define the start position in the image to start loading and w,h specify the width 
and height of the area to be loaded. This command will work on most display 
panels (not just panels using the ILI9341 controller). Eg, 
BLIT LOAD #1,"image1", 50,50,100,100 
~BLIT CLOSE [#]b ,GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT CLOSE [#]b 
BLIT CLOSE will close the memory buffer '#b' to allow it to be used for another BLIT READ operation and recover the memory used. 
~BLIT MERGE colour, x, y, w, h ,GRAPHIC,VIDEO MEMORY,COLORS,LCD,*
------------------------------------
BLIT MERGE colour, x, y, w, h 
Copies an area of the framebuffer defined by the 'x' and 'y' pixel coordinates of the top left and with a width of 'w' and height 'h' to the LCD 
display. As part of the copy it will overlay the LCD display with pixels from the layer
buffer that aren't set to the 'colour' specified. The colour is specified as a number between 0 and 15 representing:
Black, Blue, Myrtle, Cobalt, Midgreen, Cerulean, green, cyan, red, magenta, rust, fuschia, brown, lilac, yellow and white Requires both a framebuffer 
and a layer buffer to have been created to operate. Will automatically wait for frame blanking before starting the copy on ILI9341, ST7789_320 and ILI9488 displays
~BLIT FRAMEBUFFER from, to, xin, yin, xout, yout, width, height [,colour],GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT FRAMEBUFFER from, to, xin, yin, xout, yout, width, height [,colour]
Copies an area of a specific 'from' framebuffer N, F, or L to another different 'to' framebuffer N, F, or L. 'xin' and 'yin' define the top left of 
the area of 'width' and 'height' on the source framebuffer to be copied. 'xout' 
and 'yout' define the top left of the area on the target framebuffer to receive the copy. The optional parameter colour defines a pixel colour 
on the source which will not be copied. If omitted all pixels are copied. The colour is specified as a number between 0 and 15 representing: 
Black, Blue, Myrtle, Cobalt, Midgreen, Cerulean, green, cyan, red, magenta,rust, fuschia, brown, lilac, yellow and white 

Requires both a framebuffer and a layer buffer to have been created to operate. Will automatically wait for frame blanking before starting the copy on ILI9341, ST7789_320 and ILI9488 displays
~BLIT MEMORY address, x, y [,col],GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT MEMORY address, x, y [,col]
Copies an area of memory treated as a packed array of colour nibbles to the current graphical output as specified by FRAMEBUFFER WRITE. The colour is 
specified as a number between 0 and 15 representing: 
Black, Blue, Myrtle, Cobalt, Midgreen, Cerulean, green, cyan, red, magenta, rust, fuschia, brown, lilac, yellow and white 

The first word of the area of memory starting at 'address%' must contain the width and height of the area to be copied as 
16-bit integers with the width as the bottom 16 bits. The address must be aligned to a word boundary (divisible
by 4). If the optional parameter 'col' is specified then that specific colour is not copied. If the top bit of either the width or height is set to 1 
then the colour data is treated as compressed (the remaining 15 bits are used 
as the width and/or height). The compression algorithm is simple, each byte contains a count in the bottom nibble (1-15) and a colour in the top 
nibble (0-15). In the event that more than 15 pixels are the same colour additional bytes are used for that colour.
~BLIT COMPRESSED address%, x, y [,col],GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT COMPRESSED address%, x, y [,col]
Acts the same as BLIT MEMORY but assumes the data is compressed and ignores the top bit in the width and height
~BLIT x1, y1, x2, y2, w, h,GRAPHIC,VIDEO MEMORY,*
------------------------------------
BLIT x1, y1, x2, y2, w, h
Copy one section of the display screen to another part of the display. The source coordinate is 'x1' and 'y1'. The destination coordinate is 'x2' and 
'y2'. The width of the screen area to copy is 'w' and the height is 'h'. 
All arguments are in pixels. If the output is to an LCD panel it must be either the SSD19863, ILI9341_8, ILI9341, ILI9488 (if  MISO connected), or ST7789_320 controllers.
~BOX x, y, w, h [, lw] [,c] [,fill] ,DRAWING,*
------------------------------------
BOX x, y, w, h [, lw] [,c] [,fill] 
Draws a box on the VGA output with the top left hand corner at 'x' and 'y'  with a width of 'w' pixels and a height of 'h' pixels. 
'lw' is the width of the sides of the box and can be zero. It defaults to 1. 
'c' specifies the colour and defaults to the default foreground colour if not specified. 
'fill' is the fill colour. It can be omitted or set to -1 in which case the box will not be filled. 
All parameters can be expressed as arrays and the software will plot the number of boxes as determined by the dimensions of the smallest array. 
'x', 'y', 'w', and 'h' must all be arrays or all be single variables /constants otherwise an error will be generated. 
'lw', 'c', and fill can be either arrays or single variables/constants. 
See "Graphics Commands and Functions" for a definition of the colours and graphics coordinates. 
~CALL usersubname$ [,usersubparameters,....] ,MMBASIC,ENVIRONMENT,*
------------------------------------
CALL usersubname$ [,usersubparameters,....] 
This is an efficient way of programmatically calling user defined subroutines  (see also the CALL() 
function). In many case it can allow you to get rid of complex SELECT and IF THEN ELSEIF ENDIF clauses and is processed in a much more efficient way. 
The "usersubname$" can be any string or variable or function that resolves to the name of a normal user subroutine (not an in-built command). 
The "usersubparameters" are the same parameters that would be used to call the subroutine directly. 
A typical use could be writing any sort of emulator where one of a large number of subroutines should be called depending on some variable. 
It also allows a way of passing a subroutine name to another subroutine or function as a variable. 
~CAMERA ,PERIPHERALS,*
------------------------------------
CAMERA 
Command supporting the OV7670 camera module.
The PicoMite firmware has support for a OV7670 camera module. See the CAMERA command for details
~CAMERA OPEN XLKpin, PLKpin, HSpin, VSCpin, RETpin, D0pin,PERIPHERALS,*
------------------------------------
CAMERA OPEN XLKpin, PLKpin, HSpin, VSCpin, RETpin, D0pin
This initialises the camera, It outputs a 12MHz clock on XLK (PWM) and checks that it is correctly receiving  signals on PLK, VS, and HS. The camera 
is set to a resolution of 160x120 (QQVGA) which is the maximum achievable 
within the limits of the available memory. Enable OPTION SYSTEM I2C in the PicoMite firmware and wire SCL and SDA to the relevant pins (may be labelled SIOC and SIOD on the camera module). 
These connections must have a pullup to 3.3V - 2K7 recommended) Other pins are wired as per the OPEN command. 
(NB: VS may be labelled VSYNC, HS may be labelled HREF, PLK may be labelled PCLK, RET may be labelled RESET and XLK may be XCLK on your module) D0pin defines the start of a range of 8 contiguous pins (eg,GP0 - GP7).
~CAMERA CAPTURE [scale, [x , y]],PERIPHERALS,LCD,*
------------------------------------
CAMERA CAPTURE [scale, [x , y]]
This captures a picture from the camera (RGB565) and displays it on an LCD screen. An SPI LCD must be connected and enabled in order for the command to work. 
(ILI9341 and ST7789_320 recommended). Scale defaults to 1 and x,y each to 0 By default a 160x120 image is output on the LCD with the top left at 0,0 on the LCD. 
Setting scale to 2 will fill a 320x240 display with the image. Setting the x and y parameters will offset the top left of the image on the LCD. 
Update rate in a continuous loop is 7FPS onto the display at 1:1 scale and 5FPS scaled to 320x240. 
Assuming the display has MISO wired it is then possible to save the image to disk using the SAVE IMAGE command as used to create the example image above.
~CAMERA CLOSE,PERIPHERALS,*
------------------------------------
CAMERA CLOSE
Closes the camera subsystem and frees up all the pins allocated in the OPEN command.
~CAMERA CHANGE image%(),change! [,scale [,x ,y]],PERIPHERALS,*
------------------------------------
CAMERA CHANGE image%(),change! [,scale [,x ,y]]
The camera firmware is also able to detect motion in the camera's field of view using the command. It does this by operating the camera in YUV mode rather than RGB. 
This has the advantage that the intensity information and colour information are separated and just one byte is needed for a 256-level greyscale image which is ideal fer detecting movement. 
image% is an array of size 160x120 bytes (DIM image%(160,120/8-1) 
On calling the command it holds a packed 8-bit greyscale image. The change! variable returns the percentage the image has changed from the previous time the command was called. 
Optionally if "scale" is set then the image delta is output to the screen i.e. the difference between the previous image and this one. 
As in the CAPTURE command the delta image can be scaled and positioned as required. If the scale parameter is omitted then the LCD is not updated by this command.
~CAMERA TEST tnum,PERIPHERALS,*
------------------------------------
CAMERA TEST tnum
Enables or disables a test signal from the camera. 
tnum=2 generates colourbars and 
tnum=0 sets back to the visual input. 
tnum = 1 and tnum = 3 do something but what?
~CAMERA REGISTER reg%, data%,PERIPHERALS,*
------------------------------------
CAMERA REGISTER reg%, data%
Sets the register "reg%" in the camera to the value "data%". 
When used the command will report to the console the previous value and automatically confirms that the new value has been set as requested. 
The colour rendition of the camera as initialised is reasonable but could probably be improved further by tuning the various camera registers.
~CAT S$, N$ ,STRINGS,*
------------------------------------
CAT S$, N$ 
Concatenates the strings by appending N$ to S$. This functionally the same as S$ = S$ + N$ but operates somewhat faster.
~CHAIN fname$ [,cmdline$] ,FILES,PROGRAMCONTROL,PROGRAM,CHAIN,BETA,*
------------------------------------
CHAIN fname$ [,cmdline$] 
BETA 6.02 ONLY!
Allows the program to run another program with the variable space preserved - command is recommended to be used in top level program and not from within a subroutine (may work OK but not tested for side-effects)
~CHDIR dir$ ,FILES,*
------------------------------------
CHDIR dir$ 
Change the current working directory on the SD card to 'dir$' 
The special entry ".." represents the parent of the current directory and "." represents the current directory. "/" is the root directory. 
~CIRCLE x, y, r [,lw] [, a] [, c] [, fill] ,DRAWING,*
------------------------------------
CIRCLE x, y, r [,lw] [, a] [, c] [, fill] 
Draw a circle on the video output centred at 'x' and 'y' with a radius of 'r' on the VGA monitor. 
'lw' is optional and is the line width (defaults to 1). 
'c' is the optional colour and defaults to the current foreground colour if not specified. 
The optional 'a' is a floating point number which will define the aspect ratio. If the aspect is not specified the default is 1.0 which gives a standard circle. 
'fill' is the fill colour. It can be omitted or set to -1 in which case the box will not be filled.  
All parameters can be expressed as arrays and the software will plot the number of circles as determined by the dimensions of the smallest array. 
'x', 'y' and 'r' must all be arrays or all be single variables/constants otherwise an error will be generated. 
'lw', 'a', 'c', and fill can be either arrays or single variables/constants. 
See "Graphics Commands and Functions" for a definition of the colours and graphics coordinates. 
~CLEAR ,VIARIABLES,*
------------------------------------
CLEAR 
Delete all variables and recover the memory used by them. See ERASE for deleting specific array variables. 
~CLOSE [#]fnbr [,[#]fnbr] ... ,FILES,*
------------------------------------
CLOSE [#]fnbr [,[#]fnbr] ... 
Close the file(s) previously opened on the SD card with the file number '#fnbr'. The # is optional. Also see the OPEN command. 
~CLS [colour] ,GRAPHIC,SCREEN,COLORS,*
------------------------------------
CLS [colour] 
Clears the VGA monitor's screen. Optionally 'colour' can be specified which will be used for the background when clearing the screen. 
~CMM2 LOAD or CMM2 RUN,WORKFLOW,*
------------------------------------
CMM2 LOAD or CMM2 RUN
Loads and or runs a program from disk using the CMM2 program loading mechanism. 
This includes an aggressive crunching of the program and supports #INCLUDE files and #DEFINE text replacement. 
This can be used for compatibility  with CMM2 programs or to allow structuring programs into separate modules. 
It is important to note that if used all editing of programs must be offline or direct to and from disk as the source files cannot be reconstructed from the version loaded by these commands.
~COLOUR fore [, back] or COLOR fore [, back] ,GRAPHIC,SCREEN,COLORS,*
------------------------------------
COLOUR fore [, back] or COLOR fore [, back] 
Sets the default colour for commands (PRINT, etc) that display on the on the VGA monitor. 
'fore' is the foreground colour, 
'back' is the background colour. 
The background is optional and if not specified will default to black. 
~COLOUR MAP inarray%(),outarray%() [,colourmap%()],GRAPHIC,COLOURS*
------------------------------------
COLOUR MAP inarray%(),outarray%() [,colourmap%()]
This command generates RGB888 colours in outarray% from colour codes (0-15) in inarray%. 
If the optional colourmap% parameter is used this must be 16 elements long). 
In this case the values in inarray% are mapped to the colours for that index value in colourmap%
~CONFIGURE cfg  ,ENVIRONMENT,*
------------------------------------
CONFIGURE cfg  
Configures a board as per the "cfg" specified equivalent of OPTION RESET).
~CONFIGURE LIST ,ENVIRONMENT,*
------------------------------------
CONFIGURE LIST 
Lists all the various configurations available for the firmware version.
~CONST id = expression [, id = expression] ... etc  ,VARIABLES,*
------------------------------------
CONST id = expression [, id = expression] ... etc  
Create a constant identifier which cannot be changed once created. 'id' is the identifier which follows the same rules as for variables. 
The identifier can have a type suffix (!, %, or $) but it is not required. If it is specified it must match the type of 'expression'. 
'expression' is the value of the identifier and it can be a normal expression (including user defined functions) which will be evaluated when the constant is created. 
A constant defined outside a sub or function is global and can be seen throughout the program. 
A constant defined inside a sub or function is local to that routine and will hide a global constant with the same name. 
~CONTINUE ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
CONTINUE 
Resume running a program that has been stopped by an END statement, an error, or CTRL-C. 
The program will restart with the next statement following the previous stopping point. 
Note that it is not always possible to resume the program correctly - this particularly applies to complex programs with graphics, nested loops and/or nested subroutines and functions. 
~CONTINUE DO or CONTINUE FOR ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
CONTINUE DO or CONTINUE FOR 
Skip to the end of a DO/LOOP or a FOR/NEXT loop. 
The loop condition will then be tested and if still valid the loop will continue with the next iteration. 
~COPY fname1$ TO fname2$ Copy a file from 'fname1$' to 'fname2$' ,FILES,*
------------------------------------
COPY fname1$ TO fname2$ Copy a file from 'fname1$' to 'fname2$' 
Both are strings. A directory path can be used in both 'fname$' and 'fname$'. 
If the paths differ the file specified in 'fname$' will be copied to the path specified in 'fname2$'  with the file name as specified. 
The filenames can include the drive specification in the case that you are copying to and or from the non-active drive (see the DRIVE command)
~COPY fname$ TO dirname$,FILES,*
------------------------------------
COPY fname$ TO dirname$
Wildcard copy. 
The bulk copy is triggered if fname$ contains a '*' or a '?' character. 
dirname$ must be a valid directory name and should NOT end in a slash character
~CPU RESTART ,HARDWARE,*
------------------------------------
CPU RESTART 
Will force a restart of the processors. 
This will clear all variables and reset everything (eg, timers, COM ports, I2C,etc) similar to a power up situation but without the power up banner. 
If OPTION AUTORUN has been set the program in the specified flash location or program memory will restart.
~CPU SLEEP n ,HARDWARE,*
------------------------------------
CPU SLEEP n 
Will cause the processor to sleep for 'n' seconds. 
Note that the CPU does not have a true low power sleep so the power saving is limited. 
~CSUB name [type [, type] ...] /hex [[ hex[...] / END CSUB ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
CSUB name [type [, type] ...] /hex [[ hex[...] / END CSUB 

CSUB name [type [, type] ...] 
hex [[ hex[...] 
hex [[ hex[...] 
END CSUB 


Defines the binary code for an embedded machine code program module written in C or ARM assembler. The module will appear in MMBasic as the command 
'name' and can be used in the same manner as a built-in command. Multiple embedded routines can be used in a program with each defining 
a different module with a different 'name'. The first 'hex' word is a 32 bit word which is the offset in bytes from the start of the CSUB to the entry point of 
the embedded routine (usually the function main()). The following hex words are the compiled binary code for the module. These are automatically 
programmed into MMBasic when the program is saved. Each 'hex' must be exactly eight hex digits representing the bits in a 32-bit word 
and be separated by one or more spaces or new lines. The command must be terminated by a matching END CSUB. Any errors in the 
data format will be reported when the program is run. During execution MMBasic will skip over any CSUB commands so they can be placed anywhere in the 
program. The type of each parameter can be specified in the definition. 
For example: 
CSUB MySub integer, integer, string 
This specifies that there will be three parameters, the first two being integers and the third a string. 
Note: 
-Up to ten arguments can be specified ('arg1', 'arg2', etc). 
-If a variable or array is specified as an argument the C routine will receive a pointer to the memory allocated to the variable or array and the C routine can change this memory to return a value to the caller. 
In the case of arrays, they should be passed with empty brackets e.g. arg(). In the CSUB the argument will be supplied as a pointer to the first element of the array. 
-Constants and expressions will be passed to the embedded C routine as pointers to a temporary memory space holding the value. 
~DATA constant[,constant]... ,TIME,DATA,*
------------------------------------
DATA constant[,constant]... 
Stores numerical and string constants to be accessed by READ. 
In general string constants should be surrounded by double quotes ("). 
An exception is when the string consists of just alphanumeric  characters that do not represent MMBasic keywords (such as THEN, WHILE, etc). In that case quotes are not needed. 
Numerical constants can also be expressions such as 5 * 60. 
~DATE$ = "DD-MM-YY[YY]" or DATE$ = "DD/MM/YY[YY]" or DATE$ ="YYYY-MM-DD" or DATE$="YYYY/MM/DD" ,TIME,DATE,*
------------------------------------
DATE$ = "DD-MM-YY[YY]" or DATE$ = "DD/MM/YY[YY]" or DATE$ ="YYYY-MM-DD" or DATE$="YYYY/MM/DD" 
Set the date of the internal clock/calendar. DD, MM and YY are numbers, for example: 
DATE$ = "28-7-2014"
With OPTION RTC AUTO ENABLE the PicoMite firmware starts with the DATE$ programmed in RTC.
Without OPTION RTC AUTO ENABLE the PicoMite firmware starts with the date set to "01-01-2024" on power up.
~DEFINEFONT #Nbr /hex [[ hex[...] /END DEFINEFONT ,GRAPHIC,FONTS,*
------------------------------------
DEFINEFONT #Nbr /hex [[ hex[...] /END DEFINEFONT 

DEFINEFONT #Nbr 
hex [[ hex[...] 
hex [[ hex[...] 
END DEFINEFONT 


This will define an embedded font which can be used alongside or to replace  the built in font(s) used on the VGA output. 
These work exactly same as the built in fonts (i.e. selected using the FONT command or specified in the TEXT command).
See the Embedded Fonts folder in the PicoMiteVGA distribution zip file for a selection of embedded fonts and a full description of how to create them. 
'#Nbr' is the font's reference number (from 1 to 16). It can be the same number as a built in font and in that case it will replace the built in font. 
Each 'hex' must be exactly eight hex digits and beseparated by spaces or new lines from the next.  
- Multiple lines of 'hex' words can be used with the command terminated by a matching END DEFINEFONT.  
-Multiple embedded fonts can be used in a program with each defining a different font with a different font number. 
-During execution MMBasic will skip over any DEFINEFONT commands so they can be placed anywhere in the program. 
-Any errors in the data format will be reported when the program is saved. 
~DEVICE BITSTREAM pinno, n_transitions, array%(),PERIPHERALS,*
------------------------------------
DEVICE BITSTREAM pinno, n_transitions, array%()
This command is used to generate an extremely accurate bit sequence on the pin specified. The pin must have previously been set up as an output and 
set to the required starting level. 

Notes: 
- The array contains the length of each level in the bitstream in microseconds. The maximum period allowed is 65.5 mSec 
-The first transition will occur immediately on executing the command.
- The last period in the array is ignored other than defining the time before control returns to the program or command line.
- The pin is left in the starting state if the number of transitions is even and the opposite state if the number of transitions is odd.
~DEVICE CAMERA ,PERIPHERALS,*
------------------------------------
DEVICE CAMERA 
See CAMERA command
~DEVICE GAMEPAD ,PERIPHERALS,GAMEPAD,*
------------------------------------
DEVICE GAMEPAD 
See GAMEPAD command
~DEVICE HUMID ,PERIPHERALS,*
------------------------------------
DEVICE HUMID 
See HUMID command
~DEVICE KEYPAD ,PERIPHERALS,*
------------------------------------
DEVICE KEYPAD 
See KEYPAD command
~DEVICE MOUSE ,PERIPHERALS,MOUSE,*
------------------------------------
DEVICE MOUSE 
See MOUSE command 
~DEVICE LCD ,HARDWARE,PERIPHERALS,LCD,*
------------------------------------
DEVICE LCD 
See LCD command
~DEVICE SERIALTX pinno,baudrate,ostring$,PERIPHERALS,*
------------------------------------
DEVICE SERIALTX pinno,baudrate,ostring$
Outputs 'ostring$' as a serial data stream on 'pinno'. 'baudrate' can be between 110 and 230400 (230400 may need CPU to be overclocked). 
Note that the program will halt and interrupts ignored during transmission.
~DEVICE SERIALRX pinno, baudrate, istring$, timeout_in_ms, status% [,nbr] [,terminators$],PERIPHERALS,*
------------------------------------
DEVICE SERIALRX pinno, baudrate, istring$, timeout_in_ms, status% [,nbr] [,terminators$]
Inputs serial data on 'pinno'. 
'baudrate' can be between 110 and 230400 (230400 may need CPU to be overclocked).
'status%' returns: -1 = timeout  | 2 = number of characters requested satisfied | 3 = terminating character satisfied | 
(Note: use LEN(istring$) to see number of chars received)

'nbr' specifies the number of characters to be received before the command returns. 
'terminators$' specifies one or more signle characters that can be used to terminate reception. 
The program will halt and interrupts ignored while this command is executing.
~DEVICE WII ,PERIPHERALS,*
------------------------------------
DEVICE WII 
See WII command
~DEVICE WS2812 ,PERIPHERALS,*
------------------------------------
DEVICE WS2812 
See WS2812 command
~DIM [type] decl [,decl]...  ,VARIABLES,*
------------------------------------
DIM [type] decl [,decl]...  
where 
'decl' is: var [length] [type] [init] 
'var' is a variable name with optional dimensions 
'length' is used to set the maximum size of the string to 'n' as in LENGTH n 
'type' is one of FLOAT or INTEGER or STRING (the type can be prefixed by the keyword AS - as in AS FLOAT) 
'init' is the value to initialise the variable and consists of: = <expression>  
For a simple variable one expression is used, for an array a list of comma separated expressions surrounded by brackets is used.
Examples:
DIM nbr(50)
DIM INTEGER nbr(50)
DIM name AS STRING
DIM a, b$, nbr(100), strn$(20)
DIM a(5,5,5), b(1000)
DIM strn$(200) LENGTH 20
DIM STRING strn(200)LENGTH 20
DIM a = 1234, b = 345
DIM STRING strn = "text"
DIM x%(3) = (11, 22, 33, 44)
Declares one or more variables (i.e. makes the variable name and its characteristics known to the interpreter).
When OPTION EXPLICIT is used (as recommended) the DIM, LOCAL or  STATIC commands are the only way that a variable can be created. 
If this option is not used then using the DIM command is optional and if not used the variable will be created automatically when first referenced. 
The type of the variable (i.e. string, float or integer) can be specified in one of three ways: 
By using a type suffix (i.e. !, % or $ for float, integer or string). For example:
DIM nbr%, amount!, name$
By using one of the keywords FLOAT, INTEGER or STRING immediately after the command DIM and before the variable(s) are listed. 
The specified type then applies to all variables listed (i.e. it does not have to be repeated). 
For example:
DIM STRING first_name, last_name, city
By using the Microsoft convention of using the keyword "AS" and the type keyword (i.e. FLOAT, INTEGER or STRING) after each variable. If you use this method the type must be specified for each variable and can be changed from variable to variable.
For example:
DIM amount AS FLOAT, name AS STRING
Floating point or integer variables will be set to zero when created and strings will be set to an empty string (i.e. ""). 
You can initialise the value of the variable with something different by using an equals symbol (=) and an expression following the variable definition. 
For example: 
DIM STRING city = "Perth", house = "Brick"
The initialising value can be an expression (including other variables) and will be evaluated when the DIM command is executed. See "Defining and Using Variables" for more examples of the syntax.
As well as declaring simple variables the DIM command will also declare arrayed variables (i.e. an indexed variable with a number of dimensions). 
Following the variable's name the dimensions are specified by a list of numbers separated by commas and enclosed in brackets. 
For example:
DIM array(10, 20)
Each number specifies the number of elements in each dimension. Normally the numbering of each dimension starts at 0 but the OPTION BASE command can be used to change this to 1. 
The above example specifies a two dimensional array with 11 elements (0 to 10) in the first dimension and 21 (0 to 20) in the second dimension. 
The total number of elements is 231 and because each floating point number on the PicoMiteVGA requires 8 bytes a total of 1848 bytes of memory will be allocated. 
Strings will default to allocating 255 bytes (i.e. characters) of memory for each element and this can quickly use up memory when defining arrays of strings. 
In that case the LENGTH keyword can be used to specify the amount of memory to be allocated to each element and therefore the maximum length of the string that can be stored. This allocation ('n') can be from 1 to 255 characters. 
For example: 
DIM STRING s(5, 10) 
will declare a string array with 66 elements consuming 16,896 bytes of memory while:
DIM STRING s(5, 10) LENGTH 20
Will only consume 1,386 bytes of memory. 
Note that the amount of memory allocated for each element is n + 1 as the extra byte is used to track the actual length of the string stored in each element. 

If a string longer than 'n' is assigned to an element of the array an error will be produced. 
Other than this, string arrays created with the LENGTH keyword act exactly the same as other string arrays. 
This keyword can also be used with non-array string variables but it will not save any memory.  
In the above example you can also use the Microsoft syntax of specifying the type after the length qualifier. 
For example:
DIM s(5, 10) LENGTH 20 AS STRING
Arrays can also be initialised when they are declared by adding an equals symbol (=) followed by a bracketed list of values at the end of the declaration. 
For example: 
DIM INTEGER nbr(4) = (22, 44, 55, 66, 88) or DIM s$(3) = ("foo", "boo", "doo", "zoo")
Note that the number of initialising values must match the number of elements in the array including the base value set by OPTION BASE. 
If a multi dimensioned array is initialised then the first dimension will be initialised first followed by the second, etc. 
Also note that the initialising values must be after the LENGTH qualifier (if used) and after the type declaration (if used).
~DO <statements> LOOP ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
DO <statements> LOOP 
DO 
<statements> 
LOOP 
This structure will loop forever; the EXIT DO command can be used to terminate the loop or control must be explicitly transferred outside of the loop by commands like GOTO or EXIT SUB (if in a subroutine). 
~DO WHILE expression <statements> LOOP ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
DO WHILE expression <statements> LOOP 
DO WHILE expression 
<statements> 
LOOP 
~DO <statements> LOOP UNTIL expression ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
DO <statements> LOOP UNTIL expression 
DO 
<statements> 
LOOP UNTIL expression 
Loops until the expression following UNTIL is true. Because the test is made at the end of the loop the statements inside the loop will be executed at least once, even if the expression is true. 
~DRAW3D ,GRAPHIC,3D,*
------------------------------------
DRAW3D 
The 3D engine includes commands for manipulating 3D images including setting the camera, creating, hiding, rotating, etc. 
Command DRAW3D has a number of sub commands.

DRAW3D CAMERA, DRAW3D CLOSE, DRAW3D CLOSE ALL, DRAW3D CREATE , DRAW3D HIDE , DRAW3D HIDE ALL, DRAW3D RESET, DRAW3D ROTATE, DRAW3D SHOW, DRAW3D WRITE

Before looking at the commands in detail I will try and explain the concept and the limitations of the engine.
The 3D world is an area of space 65532 x 65532 x 32766 units (x, y, z) centred at 0,0,0 
In other words an object can be placed from -32766 to 32766 in the x-axis, -32766 to 32766 in the y-axis and 0 to 32766 in the z-axis You can define up to 128 3D objects numbered 1 to 128.

Each object is described in terms of how many vertices it has, how many faces it has, which vertices make up each face and the colours of the edges and infill of each face. 
Objects can have any number of vertices and faces limited only by system memory.
The vertices are specified as x,y,z coordinates referenced to the object centre at 0,0,0 In addition for each object you will define the "camera" that is used to view the object. 
The 3D engine supports up to 6 camera positions numbered 1 to 6 

All cameras look along their Z axis and before you display a 3D object the associated camera must be initialised by defining the x,y position of the camera and its viewplane. 
In camera terms the viewplane can be thought of as the focal length of the camera lens. So the bigger the value of the viewplane the more the camera "magnifies" the image.
For example, if we position a 3D object 1000 units away from the camera and the viewplane is at 200 then the projected image of the object onto the viewplane will be 20% of its original size. 
If we "zoom" the viewplane to a "focal length" of 800 the projected image will now be 80% of its original size.
When a 3D object is created the data used to create it is stored in CMM2 memory and any MMBasic arrays used to create the object can be "erased" if required.

All objects are stored in their initial orientation as defined by their initialising data but they can be rotated in three dimensions using the DRAW3D ROTATE command. 
This command acts upon the initial orientation and stores a rotated copy transparently in the object data internally in the firmware. 
Rotation takes place around the objects own centre. If you wish to rotate around any position in the 3D world this should be done as first a rotation of the object and then a move of the object. 
It is important to understand that every rotation requested for an object starts from the initial orientation and is not cumulative. 
(However, this can be overridden - see the DRAW3D RESET command) 

Rotation is specified using a quaternion but don't worry I've included a very simple MATH command to convert yaw, pitch and roll angles into the required quaternion (MATH Q_EULER)
Rotation has no effect on a displayed object but merely updates the internal memory definition of the object.
There are two commands used to place an object in the 3D world - DRAW3D WRITE and DRAW3D SHOW. 
The only difference is that, assuming the object was already displayed, the SHOW command will clear a rectangle on the current write page sufficient to remove the existing object image 
before displaying it whereas DRAW3D WRITE just overwrites whatever in on the write page with the 2D representation of the object.
It is entirely up to the MMBasic programmer to deal with things like overlap of objects in the 3D world and on the screen but to aid this objects that have been SHOWn can be removed 
and the rectangular area of the screen in which they were drawn cleared using the DRAW3D HIDE command.

All objects and camera positions are deleted on any mode change and every time a program is run. Hopefully the above gives you a basic understanding of how the 3D engine works and its limitations.
The way the camera works may seem to create a specific limitation in terms of multiple views of an object but we will see in a subsequent post how this can be overcome.
~DRAW3D CREATE,3D,DRAW3D,CREATE,*
------------------------------------
DRAW3D CREATE
DRAW3D CREATE n, nv, nf, camera, vertices(), fc(), faces(), colours() , edge() [,fill()]
DRAW3D CREATE is the command that creates a 3D object and all the information needed for the object is included in the parameter list. We will use a cube as an example.
(ASCII drawing)

n is the object number (1-128)
nv: number of vertices (e.g. 8 for a cube)
nf: number of faces (e.g. 6 for a cube)

camera: number of the camera to use when displaying the object (1 to 6) vertices(): This is a 3 by nv array that holds the x,y,z coordinates of the 3D object. 
For example the vertex definition for our cube with side length 2 with Option Base 0 centred on 0,0,0 could be:
DIM FLOAT vertices(2,7) = (-1,1,-1, 1,1,-1, 1,-1,-1, -1,-1,-1, -1,1,1, 1,1,1, 1,-1,1, -1,-1,1)

Note that the negative values represent the vertices closest to the camera.

facecount(): is a count of how many vertices are needed to define each face, so in our example for the cube which has 6 faces it would be:
DIM INTEGER facecount(5)=(4,4,4,4,4,4)
faces(): This is a very important array and defines the vertices that make up each face of the 3D object. 
There is one critical thing in setting up this array. The vertices must be listed in a clockwise order for each face as though you were looking at that face from in front of it. 
It doesn't matter which order the faces are listed as long as they match the correct vertex count in fc() and it doesn't matter which vertex you start on for each face. 
In our example this array could be: 
DIM INTEGER faces(23)=(0,1,2,3, 1,5,6,2, 0,4,5,1, 5,4,7,6, 2,6,7,3, 0,3,7,4)
colours(): This is an array that holds a simple list of all the colours you want to use to colour the 3D object. 
So if we want a different colour for each face and another one for all the edges we could set this array as follow:
DIM INTEGER colours(6)=(rgb(blue), rgb(green), rgb(yellow), rgb(cyan), rgb(red), rgb(magenta), rgb(yellow))
edge(): This arrays specifies which of our colours to use for each edge of the 3D object. We will set them all to the array index in colours() holding the value yellow
DIM INTEGER edge(5)=(6,6,6,6,6,6)
fill(): This array specifies which colour to use for each face of the 3D object. We will set them each to a different colour by specifying the array index into colours()
DIM INTEGER fill(5)=(0,1,2,3,4,5)
If the optional parameter fill() is omitted then the 3D object will be drawn as a wireframe 
Those familiar with 3D graphics will notice that the parameters to DRAW3D CREATE match the way 3D objects are defined in many 3D description files like wrl or ply files.
~DRAW3D ROTATE q(), n [,n1 [,n2...]},3D,DRAW3D,ROTATE,*
------------------------------------
DRAW3D ROTATE q(), n [,n1 [,n2...]}
This rotates one or more 3D objects about their centres.
q() is a matrix (quaternion) that defines the required rotation. We use quaternions because they don't suffer from gimbal lock and are computationally fairly efficient 
but the math is completely hidden by the firmware.
The n values are the 3D object IDs assigned when the object(s) was(were) created. From the perspective of the MMBasic user a quaternion is simply a 5 element floating point array  and it is loaded using one of two methods
MATH Q_EULER yaw, pitch, roll, q()
MATH Q_CREATE theta, x, y, z, q()
MATH Q_CREATE defines a rotation around the vector x,y,z by theta degrees (defaults to radians but supports OPTION ANGLE). 
If x is zero and y is zero then the rotation is around the z-axis which is equivalent to rolling the object. 
If only x is non-zero then the rotation will pitch the object and y non-zero will yaw the object. 

MATH Q_EULER sets q() to perform a rotation as defined by the yaw, pitch and roll angles 
With the camera facing the object yaw is looking from the top of the object and rotates clockwise, pitch rotates the top away from the camera and roll rotates around the z-axis clockwise.
The yaw, pitch and roll angles default to radians but respect the setting of OPTION ANGLE All objects specified in the ROTATE command are rotated by the same amount. 
Nothing happens on the screen but internally the firmware stores the rotated coordinates as well as the original ones.
It is very important to note that the rotate command acts on the original object as defined in the CREATE command. 

Rotate commands are not cumulative. This ensures that rounding errors cannot affect the accuracy.
However, there is a command that can override this: DRAW RESET
~DRAW3D RESET n [,n1 [,n2...]},3D,DRAW3D RESET,*
------------------------------------
DRAW3D RESET n [,n1 [,n2...]}
This command takes the current rotated version of the object(s) and copies it into the initialisation data. 
Whilst this isn't recommended for iterative rotations it is very useful in establishing multiple views of the same object. 
Later in this document I will show how to use DRAW3D RESET to create and simultaneously manipulate front and plan views of identical objects
~DRAW3D CAMERA n, z_viewplane[,x_camera [,y_camera] [,PAN_X] [,PAN_Y],3D,DRAW3D CAMERA,CAMERA,*
------------------------------------
DRAW3D CAMERA n, z_viewplane[,x_camera [,y_camera] [,PAN_X] [,PAN_Y]
The camera number 'n' and the 'viewplane' z distance are mandatory, all other parameters are optional and all default to 0 
The camera can be placed anywhere in the plane x, y, 0 but always looks out along the z axis. 
The viewplane is perpendicular to the Z axis and is a plane sized 65532 x 65532 in the x and y axis stretching, like the world from -32766 to 32766 in the x-axis and -32766 to 32766 in the y-axis 
However, our VGA display can only show a very small part of the viewplane as limited by the screen dimensions (MM.HRES x MM.VRES). 
We could call this the "viewport". By default the viewport will be set to +/- MM.HRES\2 either side of the camera x position and +/- MM.VRES either side of the camera y position.
This means If I place a 3D object at 0,0,Z in the 3D world and set my camera at 0,0,0 in the 3D world then the object will project into the centre of the screen.
Likewise, if I place a 3D object at 400,400,Z in the 3D world and set my camera at 400,400,0 in the 3D world then the object will also project into the centre of the screen.
However, there are occasions when this may not be what we want so there are two extra parameters to the CAMERA command - PAN-X and PAN-Y.
These move the position of the viewport on the viewplane relative to the camera position.
A practical example makes this clearer:

Suppose we position a number of objects in the 3D world with their lower extermities at x, 0, z. In other words they are all sitting on the ground.
To look at them we may want the camera somewhere above the ground so we are looking down on them. 
If the viewport is centred on the camera (the default) then all the objects will appear in the bottom half of the screen.
Now this may be exactly what we want but the firmware allows you to pan the viewport up and down and/or left and right relative to the camera.
So in our example we could pan the viewport down to better frame the image on the screen.
This does not change the perspective of the image, that is locked in by the relative positions of the object and the camera.
It merely allows us to frame the image better given our limited screen resolution 
~DRAW3D SHOW n, x, y, z [,nocull] or DRAW3D WRITE n, x, y, z [,nocull],3D,DRAW3D SHOW,DRAW3D WRITE,*
------------------------------------
DRAW3D SHOW n, x, y, z [,nocull] or DRAW3D WRITE n, x, y, z [,nocull]
DRAW3D SHOW , DRAW3D WRITE
This says that we want to position the centre of the object at coordinates x, y, z in our virtual 3D world. 
This command also projects the object 'n' onto the imaginary screen at "viewpoint" from the camera. 
The mechanism of projection interprets the relative position of the object in 3 dimensions and does full perspective compensation 
taking into account the relative positions of each vertex in three dimensional space relative to the viewplane and the x,y coordinates of the camera. 
As it displays the object it calculates the screen coordinates of the minimum rectangle into which the rendered object fits. 
This allows a subsequent SHOW command (but not WRITE command) to erase the previous render and draw the object onto a clean screen.
The firmware uses the technique of a combination of hidden face culling (faces pointing away from the viewer are not drawn) 
and Painter's algorithm (furthest face drawn first) to render the 3D object onto the viewplane.
Set nocull to 1 to disable hidden face culling and rely on Painter's algorithm for the display. Omit or set to 0 for normal culling. 
Setting nocull to 1 allows you to see inside hollow objects but decreases rendering efficiency and may result in artefacts on objects that have concave faces.
~DRAW3D HIDE n [,n1 [,n2...]],3D,DRAW3D HIDE,*
------------------------------------
DRAW3D HIDE n [,n1 [,n2...]]
This command hides one or more 3D objects that have been rendered using SHOW by clearing the screen in the area occupied by the object.
~DRAW3D HIDE ALL,3D,DRAW3D HIDE ALL,*
------------------------------------
DRAW3D HIDE ALL
This command does the same as HIDE but for all 3D objects
~DRAW3D CLOSE n [,n1 [,n2...]],3D,DRAW3D CLOSE,*
------------------------------------
DRAW3D CLOSE n [,n1 [,n2...]]
This command both hides any 3D objects that have been rendered using SHOW and deletes the object in memory freeing up both the memory used and the object "slot"
~DRAW3D CLOSE ALL,3D,DRAW3D CLOSE ALL,*
------------------------------------
DRAW3D CLOSE ALL
This command does the same as CLOSE but for all 3D objects
~DRAW3D DIAGNOSE objectno, x, y, z,3D,DRAW3D DIAGNOSE,*
------------------------------------
DRAW3D DIAGNOSE objectno, x, y, z
This command calculates the position of the 3D object and then lists the faces in depth order with an analysis of whether they would be hidden or not based on their surface normal. 
Using the command will help to test 3D object definitions to make sure all faces are correctly specified with their vertices correctly clockwise ordered when looking at the face from the outside of the object.
~DRIVE drive$,FILES,*
------------------------------------
DRIVE drive$
Sets the active disk drive as 'drive$'. 
'drive$' can be "A:" or "B:" where A is the flash drive and B is the SD Card if configured 
~EDIT or  EDIT fname$ or EDIT FILE fname$ ,WORKFLOW,*
------------------------------------
EDIT or  EDIT fname$ or EDIT FILE fname$ 
Invoke the full screen editor.
If a filename is specified the editor will load the file from the current disk (A: or B:) to allow editing and on exit with F1 or F2 save it to the disk. 
If the file does not exist it is created on exit. The current program stored in flash memory is not affected. 
If editing an existing file a backup with .bak appended to the filename is also created on exit. If  fname$ includes an extension other than .bas then colour coding will be temporarily turned off during the edit. 
If no extension is specified the firmware will assume .bas 
Editing a file from disk allows non-Basic files such as html or sprite files to be edited without corruption during the tokenising process that happens when stored to flash. 
EDIT and EDIT fname$ can only be invoked at the command prompt. However, it you require to edit a file in a program you can use the EDIT FILE fname$ command. 
This differs from EDIT fname$ in that it does not clear any variables and can only use any free memory for the edit buffer. 
This will place greater limits on the size of the file that can be edited if the memory usage of the calling program is itself large. 
See Full Screen Editor for details of how to use the editor. 
~EDIT FILE ,WORKFLOW,EDIT,BETA,*
------------------------------------
EDIT FILE 
Command used in a program will now only be memory limited the same as EDIT
BETA 6.02 ONLY!
~ELSE ,MMBASIC,ENVIRONMENT,CONTROL,CONDITIONAL,*
------------------------------------
ELSE 
Introduces an optional default condition in a multiline IF statement. 
See the multiline IF statement for more details. 
~ELSEIF expression THEN or ELSE IF expression THEN ,MMBASIC,ENVIRONMENT,CONTROL,CONDITIONAL,*
------------------------------------
ELSEIF expression THEN or ELSE IF expression THEN 
Introduces an optional secondary condition in a multiline IF statement. 
See the multiline IF statement for more details. 
~END ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
END 
End the running program and return to the command prompt. 
~END CSUB ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
END CSUB 
-f Marks the end of a C subroutine. See the CSUB command.  Each CSUB must have one and only one matching END CSUB statement. 
~END FUNCTION ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
END FUNCTION 
Marks the end of a user defined function. See the FUNCTION command. Each function must have one and only one matching END FUNCTION statement. 
Use EXIT FUNCTION if you need to return from a function from within its body. 
~ENDIF or END IF ,MMBASIC,ENVIRONMENT,CONTROL,CONDITIONAL,*
------------------------------------
ENDIF or END IF 
Terminates a multiline IF statement. See the multiline IF statement for more details. 
~END SUB ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
END SUB 
Marks the end of a user defined subroutine. See the SUB command. 
Each sub must have one and only one matching END SUB statement. 
Use EXIT SUB if you need to return from a subroutine from within its body. 
~ERASE variable [,variable]... ,VARIABLES,*
------------------------------------
ERASE variable [,variable]... 
Deletes variables and frees up the memory allocated to them. 
This will work with arrayed variables and normal (non array) variables. 
Arrays can be specified using empty brackets (e.g. dat()) or just by specifying the variable's name (e.g. dat). 
Use CLEAR to delete all variables at the same time (including arrays). 
~ERROR [error_msg$] ,MMBASIC,ENVIRONMENT,CONTROL,ENVIRONMENT,*
------------------------------------
ERROR [error_msg$] 
Forces an error and terminates the program. This is normally used in debugging or to trap events that should not occur. 
'error_msg$' is optional and is the message to display on the console.
~EXECUTE command$ ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
EXECUTE command$ 
This executes the Basic command "command$". 
Use should be limited to basic commands that execute sequentially for example the GOTO statement will not work properly. Things that are tested and work OK include GOSUB, Subroutine calls, other simple statements (like PRINT and simple assignments) 
Multiple statements separated by : are not allowed and will error 
The command sets an internal watchdog before executing the requested command and if control does not return to the command, like in a GOTO  statement, the timer will expire. 
In this case you will get the message "Command timeout". 
RUN is a special case and will cancel the timer allowing you to use the command to chain programs if required. 
~EXIT DO ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
EXIT DO 
Provides an early exit from a DO...LOOP 
~EXIT FOR ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
EXIT FOR 
Provides an early exit from a FOR...NEXT loop. 
~EXIT FUNCTION  ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
EXIT FUNCTION  
Provides an early exit from a defined function. 
~EXIT SUB ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
EXIT SUB 
Provides an early exit from a defined subroutine.  The old standard of EXIT on its own (exit a do loop) is also supported. 
~FILES [fspec$] [,sort] ,FILES,*
------------------------------------
FILES [fspec$] [,sort] 
Lists files in any directory on the SD card. 'fspec$' (if specified) can contain a path and search wildcards in the filename. 
Question marks (?) will match any character and an asterisk (*) will match any number of characters. 
If omitted, all files will be listed. 
For example: 
* Find all entries 
*.TXT Find all entries with an extension of TXT 
E*.* Find all entries starting with E 
X?X.* Find all three letter file names starting and ending with X 
mydir/* Find all entries in directory mydir 

'sort' specifies the sort order as follows:  
size by ascending size ; 
time by descending time/date ; 
name by file name (default if not specified) ; 
type by file extension 

BETA 6.02 ONLY! (?) 
FILE command can now be used in a program
~FILES [fspec$] [,sort] ,FILE,*
------------------------------------
FILES [fspec$] [,sort] 

~FLASH ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH 
Manages the storage of programs in the flash memory. Up to three programs can be stored in the flash memory and retrieved as required. 
Note that these saved programs will be erased with a firmware upgrade. 

One of these flash memory locations can be automatically loaded and run when power is applied using the OPTION AUTORUN n command. 
In the following 'n' is a number 1 to 3.
~FLASH LIST ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH LIST 
Displays a list of all flash locations including the first line of the program. 
~FLASH LIST n [,all] ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH LIST n [,all] 
List the program saved to slot n. Use ALL to list without page breaks.
~FLASH ERASE n ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH ERASE n 
Erase a flash program location.
~FLASH ERASE ALL ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH ERASE ALL 
Erase all flash program locations.
~FLASH SAVE n ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH SAVE n 
Save the current program to the flash location specified.
~FLASH LOAD n ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH LOAD n 
Load a program from the specified flash location into program memory.
~FLASH RUN n ,WORKFLOW,STORAGE,FILES,FLASH,*
------------------------------------
FLASH RUN n 
Runs the program in flash location n, clear all variables. Does not change the program memory.
~FLASH CHAIN n ,WORKFLOW,STORAGE,FILES,FLASH,*
------------------------------------
FLASH CHAIN n 
Runs the program in flash location n, leaving all variables intact (allows for a program that is much bigger than the program memory). 
Does not change the program memory.
~FLASH OVERWRITE n ,STORAGE,FILES,FLASH,*
------------------------------------
FLASH OVERWRITE n 
Erase a flash program location and then save the current program to the flash location specified.
~FLASH DISK LOAD n, fname$ [,O[VERWRITE]],WORKFLOW,STORAGE,FILES,FLASH,*
------------------------------------
FLASH DISK LOAD n, fname$ [,O[VERWRITE]]
Loads the contents of file fname$ into flash slot n as a binary image. 
The file can be created using LIBRARY DISK SAVE. Also, any file created externally with data required by a program can be loaded and accessed using commands like PEEK and MEMORY COPY using the address of the flash slot. 
If the optional parameter OVERWRITE (or O) is specified the content of the flash slot will be overwritten without an error being raised.
~FLUSH [#]fnbr ,WORKFLOW,STORAGE,FILES,*
------------------------------------
FLUSH [#]fnbr 
Causes any buffered writes to a file previously opened with the file number '#fnbr' to be written to disk. The # is optional. 
Using this command ensures that no data is lost if there is a power cut after a write command. 
~FONT [#]font-number, scaling ,FONTS,GRAPHIC,*
------------------------------------
FONT [#]font-number, scaling 
This will set the default font for displaying text on the VGA output. Fonts are specified as a number. 
For example, #2 (the # is optional). See  Graphics Commands and Functions for details of the available fonts. 
'scaling' can range from 1 to 15 and will multiply the size of the pixels making the displayed character correspondingly wider and higher. E.g. a scale of 2 will double the height and width. 
~FOR counter = start TO finish [STEP increment] ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
FOR counter = start TO finish [STEP increment] 
Initiates a FOR-NEXT loop with the variable 'counter' initially set to 'start' [STEP increment] and incrementing in 'increment' steps (default is 1) until 'counter' is greater than 'finish'. 
The 'increment' can be an integer or a floating point number. 
Note that using a floating point fractional number for 'increment' can accumulate rounding errors in 'counter' which could cause the loop to terminate early or late. 

'increment' can be negative in which case 'finish' should be less than 'start' and the loop will count downwards. 
See also the NEXT command. 
~FRAMEBUFFER NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER NOT HDMI AND VGA VERSIONS
The Framebuffer command allow you to allocate some of the variable memory to either a framebuffer, a second display layer, or both and then use these in interesting ways 
to both avoid tearing artefacts and/or play graphics objects over the background display.
~FRAMEBUFFER CREATE NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER CREATE NOT HDMI AND VGA VERSIONS
Creates a framebuffer "F" with a RGB121 colour space and resolution to match the configured SPI colour display
~FRAMEBUFFER LAYER NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER LAYER NOT HDMI AND VGA VERSIONS
Creates a framebuffer "L" with a RGB121 colour space and resolution to match the configured SPI colour display
~FRAMEBUFFER WRITE where/where$ NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER WRITE where/where$ NOT HDMI AND VGA VERSIONS
Specifies the target for subsequent graphics commands. "where" can be N, F, or L where N is the actual display. 
AA string variable can be used or a literal
~FRAMEBUFFER CLOSE [which] NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER CLOSE [which] NOT HDMI AND VGA VERSIONS
Closes a framebuffer and releases the memory. The optional parameter "which" can be F or L. If omitted closes both.
~FRAMEBUFFER COPY from, to [,b] NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,LCD,*
------------------------------------
FRAMEBUFFER COPY from, to [,b] NOT HDMI AND VGA VERSIONS
Does a highly optimised full screen copy of one framebuffer to another. "from" and "to" can be N, F, or L where N is the physical display. You can 
only copy from N on displays that support BLIT and transparent text. 
The firmware will automatically compress or expand the RGB resolution when copying to and from unmatched framebuffers.
Of course copying from RGB565 to RGB121 loses information but for many applications (eg, games) 16 colour levels is more than enough. 
When copying to an LCD display the optional parameter "b" can be used (FRAMEBUFFER COPY F/L, N, B). 
This instructs the firmware to action the copy using the second CPU in the Raspberry Pi Pico and control returns immediately to the Basic program
~FRAMEBUFFER WAIT NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER WAIT NOT HDMI AND VGA VERSIONS
Pauses processing until the LCD display enters frame blanking. Implemented for ILI9341, ST7789_320 and ILI9488 displays. 
Used to reduce artefacts when writing to the screen
~FRAMEBUFFER MERGE [colour] [,mode] [,updaterate] NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,LCD,*
------------------------------------
FRAMEBUFFER MERGE [colour] [,mode] [,updaterate] NOT HDMI AND VGA VERSIONS
Copies the contents of the Layer buffer and Framebuffer onto the LCD display omitting all pixels of a particular colour. Preconditions for the command 
are that FRAMEBUFFER and LAYERBUFFER are both created FRAMEBUFFER MERGE - writes the contents of the framebuffer to the physical display 
overwriting any pixels in the framebuffer that are set in the layerbuffer (not zero) 
FRAMEBUFER MERGE col - writes the contents of the framebuffer to the physical display overwriting any pixels in the framebuffer that are 
in the layerbuffer not set to the transparent colour "col". 
The colour is specified as a number between 0 and 15 representing: 
0:BLACK, 1:BLUE, 2:MYRTLE, 3:COBALT, 4:MIDGREEN, 5:CERULEAN, 6: GREEN, 7:CYAN, 8:RED, 9:MAGENTA, 10:RUST, 11:FUCHSIA, 12:BROWN, 13:LILAC, 14:YELLOW, 15:WHITE

FRAMEBUFFER MERGE col,B - as above except that the transfer to the physical display takes place on the second CPU and control returns to Basic 
immediately FRAMEBUFFER MERGE col,R [,updaterate] - sets the second CPU to continuously update the physical display with the merger of the two buffers.  
Automatically sets FRAMEBUFFER WRITE F if not F or L already set. By default the screen will update as fast as possible (At 133MHz an ILI9341 in
SPI mode updates about 13 times a second, in 8-bit parallel mode the ILI9341 achieves 27 FPS) If "updaterate" is set then the screen will update to 
the rate specified in milliseconds (unless that is less than the fastest achievable on the display) 
NB: FRAMEBUFFER WRITE cannot be set to N while continuous merged update is active. FRAMEBUFFER MERGE col,A - aborts the continuous updates 
In addition deleting either the layerbuf or framebuffer, ctrl-C, or END will abort the automatic update as well.
~FRAMEBUFFER SYN NOT HDMI AND VGA VERSIONS,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER SYN NOT HDMI AND VGA VERSIONS
Waits for the latest update on the second CPU to complete to allow drawing without tearing
~FRAMEBUFFER  HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER  HDMI AND VGA VERSIONS ONLY
The Framebuffer command allow you to allocate some of the variable memory to framebuffers, layer buffers, or both and then use these in interesting ways to both avoid tearing artefacts and/or play graphics objects over the  background  display.
~FRAMEBUFFER CREATE HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER CREATE HDMI AND VGA VERSIONS ONLY
Creates a framebuffer "F" with a colour space and resolution to match the current display mode
~FRAMEBUFFER CREATE 2  HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER CREATE 2  HDMI AND VGA VERSIONS ONLY
RP2350 only: Creates a second framebuffer "2" with a colour space and resolution to match the current display mode
~FRAMEBUFFER LAYER [colour] HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER LAYER [colour] HDMI AND VGA VERSIONS ONLY
Creates a layer buffer "L" with a colour space and resolution to match the current display mode. 
The optional parameter colour is specified as a number 0-15 (modes 2 and 3), RGB888 colour (mode 4) or 0-255 (mode 5) and specifies a colour which is ignored when the layer is applied to the display. 
In display modes where automatic layer application is not supported a layer buffer acts as another framebuffer.
~FRAMEBUFFER LAYER TOP [colour] HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER LAYER TOP [colour] HDMI AND VGA VERSIONS ONLY
RP2350 only: 

Creates a second layer buffer "T" with a colour space and resolution to match the current display mode. 
The optional parameter colour is specified as a number 0-15 (modes 2 and 3), 0-255 (mode 5) and specifies a colour which is ignored when the layer Is applied to the display. 
In display modes where automatic 2nd layer application is not supported acts as another framebuffer.
~FRAMEBUFFER WRITE where/where$ HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER WRITE where/where$ HDMI AND VGA VERSIONS ONLY
Specifies the target for subsequent graphics commands. 
"where" can be N, F, 2, T, or L where N is the actual display. A string variable can be used
~FRAMEBUFFER CLOSE [which] HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER CLOSE [which] HDMI AND VGA VERSIONS ONLY
Closes a framebuffer and releases the memory. The optional parameter "which" can be F, 2, T or L. If omitted closes all.
~FRAMEBUFFER COPY from, to [,b] HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER COPY from, to [,b] HDMI AND VGA VERSIONS ONLY
Does a highly optimised full screen copy of one framebuffer to another. "from" and "to" can be N, F, 2, T, or L where N is the physical display. 
If the optional parameter 'b' is specified pauses processing until the Monitor enters frame blanking.
~FRAMEBUFFER WAIT HDMI AND VGA VERSIONS ONLY,GRAPHIC,VIDEO MEMORY,*
------------------------------------
FRAMEBUFFER WAIT HDMI AND VGA VERSIONS ONLY
Pauses processing until the next frame blanking starts
~FUNCTION xxx (arg1 [,arg2, ...]) [AS <type>} <statements> xxx = <return value> END FUNCTION ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
FUNCTION xxx (arg1 [,arg2, ...]) [AS <type>} <statements> xxx = <return value> END FUNCTION 
FUNCTION xxx (arg1 
[,arg2, ...]) [AS <type>} 
<statements> 
<statements> 
xxx = <return value> 
END FUNCTION 
Defines a callable function. This is the same as adding a new function to MMBasic while it is running your program. 'xxx' is the function name and it must meet the specifications for naming a variable.
The type of the function can be specified by using a type suffix (i.e. xxx$) or by specifying the type using AS <type> at the end of the functions definition. 
For example: 
FUNCTION xxx (arg1, arg2) AS STRING 'arg1', 'arg2', etc 
are the arguments or parameters to the function (the brackets are always required, even if there are no arguments). 
An array is specified by using empty brackets. i.e. arg3(). The type of the argument can be specified by using a type suffix (i.e. arg1$) or by specifying the type using AS <type> (i.e. arg1 AS STRING).  
The argument can also be another defined function or the same function if  recursion is to be used (the recursion stack is limited to 50 nested calls). 
To set the return value of the function you assign the value to the function's name. 
For example: 
FUNCTION SQUARE(a) 
SQUARE = a * a 
END FUNCTION 
Every definition must have one END FUNCTION statement. When this is reached the function will return its value to the expression from which it was called. 
The command EXIT FUNCTION can be used for an early exit.  
You use the function by using its name and arguments in a program just as you would a normal MMBasic function. 
For example:  
PRINT SQUARE(56.8)  
When the function is called each argument in the caller is matched to the argument in the function definition. 
These arguments are available only inside the function.  Functions can be called with a variable number of arguments. 
Any omitted arguments in the function's list will be set to zero or a null string.
Arguments in the caller's list that are a variable and have the correct type will be passed by reference to the function. This means that any changes 
to the corresponding argument in the function will also be copied to the caller's variable and therefore may be accessed after the function has ended. 
Arrays are passed by specifying the array name with empty brackets (e.g. arg()) and are always passed by reference and must be the correct type. 
You must not jump into or out of a function using commands like GOTO, GOSUB, etc. 
Doing so will have undefined side effects including the possibility of ruining your day. 
~GAMEPAD COLOUR channel, colour,GAMEPAD,PERIPHERALS,COLORS,*
------------------------------------
GAMEPAD COLOUR channel, colour
Changes the colour of the display panel on a PS4 controller on USB channel 'channel'. 'colour' is set as a standard RGB888 value e.g. RGB(RED) 
~GAMEPAD HAPTIC channel left, right,GAMEPAD,PERIPHERALS,*
------------------------------------
GAMEPAD HAPTIC channel left, right
Causes the left and right vibration motors to operate on a PS4 controller on USB channel 'channel'. 'left' and 'right' must be a number between 0 (off) and 255 (maximum).
~GAMEPAD INTERRUPT ENABLE channel, int [,mask],GAMEPAD,PERIPHERALS,*
------------------------------------
GAMEPAD INTERRUPT ENABLE channel, int [,mask]
Enables interrupts on the button presses on a USB game controller. 
The optional parameter 'mask' defines which of the switches will trigger the interrupt (defaults to all). 
'mask' is a bitmap corresponding to the output of the DEVICE(GAMEPAD channel,B) function.
~GAMEPAD CONFIGURE vid, pid, i0, c0, i1, c1, i2, c2, i3, c3, i4, c4, i5, c5, i6, c6, i7, c7, i8, c8, i9, c9, i10, c10, i11, c11, i12, c12, i13, c13, i14, c14, i15, c15,GAMEPAD,PERIPHERALS,BETA,*
------------------------------------
GAMEPAD CONFIGURE vid, pid, i0, c0, i1, c1, i2, c2, i3, c3, i4, c4, i5, c5, i6, c6, i7, c7, i8, c8, i9, c9, i10, c10, i11, c11, i12, c12, i13, c13, i14, c14, i15, c15
Use GAMEPAD CONFIGURE to set up a gamepad that isn't supported before plugging it in. 
All 34 parameters are mandatory. 
BETA 6.02 ONLY!

Note:
struct s_Buttons {
uint8_t index; // which report element relates to this bit set to 0xFF if bit not used
// code can be a bit number 0-7 for positive if pressed
// 128-135 for negative if pressed
// 64 for value less than 64 if pressed
// 192 for value greater than 192 if pressed
uint8_t code ;
};
struct s_Gamepad {
uint16_t vid;
uint16_t pid;
struct s_Buttons b_R;
struct s_Buttons b_START;
struct s_Buttons b_HOME;
struct s_Buttons b_SELECT;
struct s_Buttons b_L;
struct s_Buttons b_DOWN;
struct s_Buttons b_RIGHT;
struct s_Buttons b_UP;
struct s_Buttons b_LEFT;
struct s_Buttons b_R2;
struct s_Buttons b_X;
struct s_Buttons b_A;
struct s_Buttons b_Y;
struct s_Buttons b_B;
struct s_Buttons b_L2;
struct s_Buttons b_TOUCH;
};
~GAMEPAD INTERRUPT DISABLE channel,GAMEPAD,PERIPHERALS,*
------------------------------------
GAMEPAD INTERRUPT DISABLE channel
~GAMEPAD MONITOR,GAMEPAD,PERIPHERALS,BETA,*
------------------------------------
GAMEPAD MONITOR
Use GAMEPAD MONITOR before plugging in a gamepad and when plugged in it will show the before and after report with each change of buttons
BETA 6.02 ONLY!
~GOTO target ,MMBASIC,ENVIRONMENT,CONTROL,GO TO,*
------------------------------------
GOTO target 
Branches program execution to the target, which can be a line number or a label. 
~GUI BITMAP x, y, bits [, width] [, height] [, scale] [, c] [, bc] ,GRAPHIC,DRAWING,*
------------------------------------
GUI BITMAP x, y, bits [, width] [, height] [, scale] [, c] [, bc] 
Displays the bits in a bitmap on the VGA monitor starting at 'x' and 'y' pixel. 
'height' and 'width' are the dimensions of the bitmap in pixels and default to 8x8. 
'scale' is optional and defaults to that set by the FONT command. 
'c' is the drawing colour and 'bc' is the background colour. 
They are optional and default to the current foreground and background colours. 
The bitmap can be an integer or a string variable or constant and is drawn using the first byte as the first bits of the top line (bit 7 first, then bit 6, etc) followed by the next byte, etc. 
When the top line has been filled the next line of the displayed bitmap will start with the next bit in the integer or string. 
See Graphics Commands and Functions for a definition of the colours and graphics coordinates. 
~GUI CALIBRATE or  GUI CALIBRATE a,b,c,d,d ,WORKFLOW,LCD,*
------------------------------------
GUI CALIBRATE or  GUI CALIBRATE a,b,c,d,d 
This command is used to calibrate the touch feature on an LCD panel. It will display a series of targets on the screen and wait for each one to be precisely touched.  
The command can also be used with five arguments which specify the calibration values and in this case the calibration will be done without displaying any targets or requiring an input from the user. 
To discover the values use the OPTION LIST after calibrating the display normally. 
Note that these values are specific to that display and can vary considerably.
~GUI RESET LCDPANEL ,WOKFLOW,LCD,*
------------------------------------
GUI RESET LCDPANEL 
Will reinitialise the configured LCD panel. Initialisation is automatically done when the PicoMite firmware starts up but in some circumstances it may be necessary 
to interrupt power to the LCD panel (eg, to save battery power) and this command can then be used to reinitialise the display.
~GUI TEST LCDPANEL Or GUI TEST TOUCH ,WORKFLOW,GUI,LCD,*
------------------------------------
GUI TEST LCDPANEL Or GUI TEST TOUCH 
Will test the display or touch feature on an LCD panel. With GUI TEST LCDPANEL an animated display of colour circles will be rapidly drawn on top of each other. 
With GUI TEST TOUCH the screen will blank and wait for a touch which will cause a white dot to be placed on the display marking the touch position on the screen. 
Any character entered at the console will terminate the test.
~HUMID pin, tvar, hvar [,DHT11],HARDWARE,PERIPHERALS,HUMIDITY,*
------------------------------------
HUMID pin, tvar, hvar [,DHT11]
Returns the temperature and humidity using the DHT22 sensor. Alternative versions of the DHT22 are the AM2303 or the RHT03 (all are compatible). 
'pin' is the I/O pin connected to the sensor. Any I/O pin may be used. 
'tvar' is the variable that will hold the measured temperature and 
'hvar' is the same for humidity. 
Both must be present and both must be floating point variables. For example: HUMID 3, TEMP!, HUMIDITY! 
Temperature is measured in C degree and the humidity is percent relative humidity. 
Both will be measured with a resolution of 0.1. If an error occurs (sensor not connected or corrupt signal) both values will be 1000.0. 
Normally the DHT22 should powered by 3.3V to keep its output below 3.6V for the Raspberry Pi Pico (the Pico 2 does not have this issue) 
and the signal pin of should be pulled up by a 1K to 10K resistor (4.7K recommended) to 3.3V. The optional DHT11 parameter modifies the timings to work with the DHT11. 
Set to 1 for DHT11 and 0 or omit for DHT22.

The HUMID command will read the humidity and temperature from a DHT22 humidity/temperature sensor. 
This device is also sold as the RHT03 or AM2302 but all are compatible and can be purchased on eBay for under $5. The DHT11 sensor is also supported.
The DHT22 must be powered from 3.3V (or up to 5V with the Raspberry Pi Pico 2) and it should have a pullup resistor on the data line as shown. 
This is suitable for long cable runs (up to 20 meters) but for short runs the resistor can be omitted as the PicoMite firmware also provides an internal weak pullup.
To get the temperature or humidity you use the HUMID command with three arguments as follows:
HUMID pin, tVar, hVar [,DHT11]
Where 'pin' is the I/O pin to which the sensor is connected. The I/O pin will be automatically configured by MMBasic.
'tVar' is a floating point variable in which the temperature is returned and 'hVar' is a second variable for the humidity. 
The temperature is returned as degrees C with a resolution of one decimal place (eg, 23.4) and the humidity is returned as a percentage relative humidity (eg, 54.3).
If the optional DHT11 parameter is set to 1 then the command will use device timings suitable for that device.
In this case the results will be returned with a resolution of 1 degree and 1% humidity 
For example:
DIM FLOAT temp, humidity
HUMID GP15, temp, humidity
PRINT "The temperature is" temp " and the humidity is" humidity
~I2C Communications Appendix B,I2C,HARDWARE,PERIPHERALS,*
------------------------------------
I2C Communications Appendix B
There are two I2C channels. They can operate in master or slave mode.
I/O Pins
Before the I2C interface can be used the I/O pins must be defined using the following command for the first channel (referred as I2C):
SETPIN sda, scl, I2C
Valid pins are
SDA:
GP0, GP4, GP8, GP12, GP16, GP20 or GP28
SCL:
GP1, GP5, GP9, GP13, GP17 or GP21
Note that on the WebMite version I2C SDA is not available on GP28 And the following command for the second channel (referred to as I2C2):
SETPIN sda, scl, I2C2
Valid pins are
SDA:
GP2, GP6, GP10, GP14, GP18, GP22 or GP26
SCL:
GP3, GP7, GP11, GP15, GP19 or GP27

When running the I2C bus at above 100 kHz the cabling between the devices becomes important. 
Ideally the cables should be as short as possible (to reduce capacitance) and the data and clock lines should not run next to each other but have a ground wire between them (to reduce crosstalk).
If the data line is not stable when the clock is high, or the clock line is jittery, the I2C peripherals can get "confused" and end up locking the bus (normally by holding the clock line low). 
If you do not need the higher speeds then operating at 100 kHz is the safest choice. The command I2C CHECK addr can be used to check if a device is present at the address 'addr’. 
This will set the read only variable MM.I2C to 0 if a device responds or 1 if there is no response.
I2C Master Commands There are four commands that can be used for the first channel (I2C) in master mode as follows. 
The commands for the second channel (I2C2) are identical except that the command is I2C2
I2C OPEN speed, timeout 
Enables the I2C module in master mode. The I2C command refers to channel 1 while the command I2C2 refers to channel 2 using the same syntax.
'speed' is the clock speed (in KHz) to use and must be either 100, 400 or 1000.
'timeout' is a value in milliseconds after which the master send and receive commands will be interrupted if they have not completed. 
The minimum value is 100. A value of zero will disable the timeout (though this is not recommended).

I2C WRITE addr,option, sendlen,senddata [,sendata ..]
Send data to the I2C slave device. The I2C command refers to channel 1 while the command I2C2 refers to channel 2 using the same syntax.
'addr' is the slave's I2C address. 
'option' can be 0 for normal operation or 1 to keep control of the bus after the command (a stop condition will not be sent at the completion of the command)
'sendlen' is the number of bytes to send.
'senddata' is the data to be sent - this can be specified in various ways (all data sent will be sent as bytes with a value between 0 and 255):
-> The data can be supplied as individual bytes on the command line.

Example: 
I2C WRITE &H6F, 0, 3, &H23, &H43, &H25
-> The data can be in a one dimensional array specified with empty brackets (i.e. no dimensions). 'sendlen' bytes of the array will be sent starting with the first element. 

Example: 
I2C WRITE &H6F, 0, 3, ARRAY()
-> The data can be a string variable (not a constant).
Example: 
I2C WRITE &H6F, 0, 3, STRING$
I2C READ addr,option, rcvlen, rcvbuf
Get data from the I2C slave device. The I2C command refers to channel 1 while the command I2C2 refers to channel 2 using the same syntax.
'addr' is the slave's I2C address.
'option' can be 0 for normal operation or 1 to keep control of the bus after the command (a stop condition will not be sent at the completion of the command)
'rcvlen' is the number of bytes to receive.
'rcvbuf' is the variable or array used to save the received data - this can be:
-> A string variable. Bytes will be stored as sequential characters in the string.
-> A one dimensional array of numbers specified with empty brackets. Received bytes will be stored in sequential elements of the array starting with the first.

Example: 
I2C READ &H6F, 0, 3, ARRAY()
-> A normal numeric variable (in this case 'rcvlen' must be 1).
I2C CLOSE

I2C Slave Commands

I2C SLAVE OPEN addr, send_int, rcv_int
Enables the I2C module in slave mode. The I2C command refers to channel 1 while the command I2C2 refers to channel 2 using the same syntax.
'addr' is the slave I2C address.
'send_int' is the subroutine to be invoked when the module has detected that the master is expecting data.
'rcv_int is the subroutine to be called when the module has received data from the master. 
Note that this is triggered on the first byte received so your program might need to wait until all the data is received.
I2C SLAVE WRITE sendlen, senddata [,sendata ..]
Send the data to the I2C master. The I2C command refers to channel 1 while the command I2C2 refers to channel 2 using the same syntax.
This command should be used in the send interrupt (ie in the 'send_int' subroutine when the master has requested data). 
Alternatively, a flag can be set in the interrupt subroutine and the command invoked from the main program loop when the flag is set.
'sendlen is the number of bytes to send.
'senddata' is the data to be sent. This can be specified in various ways, see the I2C WRITE commands for details.
I2C SLAVE READ rcvlen, rcvbuf, rcvd
Receive data from the I2C master device. The I2C command refers to channel 1 while the command I2C2 refers to channel 2 using the same syntax.
This command should be used in the receive interrupt (ie in the 'rcv_int' subroutine when the master has sent some data). 
Alternatively a flag can be set in the receive interrupt subroutine and the command invoked from the main program loop when the flag is set.
'rcvlen' is the maximum number of bytes to receive.
'rcvbuf' is the variable to receive the data. This can be specified in various ways, see the I2C READ commands for details.
'rcvd' is a variable that, at the completion of the command, will contain the actual number of bytes received (which might differ from 'rcvlen’).
I2C SLAVE CLOSE
Disables the slave I2C module and returns the external I/O pins to a "not configured" state. They can then be configured using SETPIN.
ERRORS

Following an I2C write or read the automatic variable MM.I2C will be set to indicate the result as follows:
0 = The command completed without error.
1 = Received a NACK response
2 = Command timed out
7-Bit Addressing

The standard addresses used in these commands are 7-bit addresses (without the read/write bit). 
MMBasic will add the read/write bit and manipulate it accordingly during transfers.
Some vendors provide 8-bit addresses which include the read/write bit. 
You can determine if this is the case because they will provide one address for writing to the slave device and another for reading from the slave. 
In these situations you should only use the top seven bits of the address. For example: If the read address is 9B (hex) and the write address is 9A (hex) 
then using only the top seven bits will give you an address of 4D (hex).
Another indicator that a vendor is using 8-bit addresses instead of 7-bit addresses is to check the address range.
All 7-bit addresses should be in the range of 08 to 77 (hex). If your slave address is greater than this range then probably your vendor has provided an 8-bit address.

Examples
As an example of a simple communications where the Raspberry Pi Pico is the master, the following program will read and display the current time (hours and minutes) maintained by a PCF8563 real time clock chip connected to the second I2C channel:
DIM AS INTEGER RData(2)
' this will hold received data
SETPIN GP6, GP7, I2C2
' assign the I/O pins for I2C2
I2C2 OPEN 100, 1000
' open the I2C channel
I2C2 WRITE &H51, 0, 1, 3
' set the first register to 3
I2C2 READ &H51, 0, 2, RData()
' read two registers
I2C2 CLOSE
' close the I2C channel
PRINT "Time is " RData(1) ":" RData(0)
This is an example of communications between two Raspberry Pi Picos where one is the master and the other is the slave.

First the master:
SETPIN GP2, GP3, I2C2
I2C2 OPEN 100, 1000
i = 10
DO
i = i + 1
a$ = STR$(i)
I2C2 WRITE &H50, 0, LEN(a$), a$
PAUSE 200
I2C2 READ &H50, 0, 8, a$
PRINT a$
PAUSE 200
LOOP
Then the slave:
SETPIN GP2, GP3, I2C2
I2C2 SLAVE OPEN &H50, tint, rint
DO : LOOP
SUB rint
LOCAL count, a$
I2C2 SLAVE READ 10, a$, count
PRINT LEFT$(a$, count)
END SUB
SUB tint
LOCAL a$ = Time$
I2C2 SLAVE WRITE LEN(a$), a$
END SUB
~I2C OPEN speed, timeout ,I2C,HARDWARE,PERIPHERALS,*
------------------------------------
I2C OPEN speed, timeout 
'speed' is the clock speed (in KHz) to use and must be one of 100, 400 or 1000. 
'timeout' is a value in milliseconds after which the master send and receive commands will be interrupted if they have not completed. 
The minimum value is 100. A value of zero will disable the timeout (though this is not recommended).
~I2C WRITE addr, option, sendlen, senddata [,sendata ....] ,I2C,HARDWARE,PERIPHERALS,*
------------------------------------
I2C WRITE addr, option, sendlen, senddata [,sendata ....] 
Send data to the I2C slave device. 
'addr' is the slave's I2C address. 
'option' can be 0 for normal operation or 1 to keep control of the bus after the command (a stop condition will not be sent at the completion of the command) 
'sendlen' is the number of bytes to send. 
'senddata' is the data to be sent - this can be specified in various ways (all values sent will be between 0 and 255).

Notes:
-The data can be supplied as individual bytes on the command line.
Example: I2C WRITE &H6F, 0, 3, &H23, &H43, &H25
-The data can be in a one dimensional array specified with empty brackets (i.e. no dimensions). 'sendlen' bytes of the array will be sent starting 
with the first element.

Example: 
I2C WRITE &H6F, 0, 3, ARRAY()
The data can be a string variable (not a constant).
Example: 
I2C WRITE &H6F, 0, 3, STRING$
~I2C READ addr, option, rcvlen, rcvbuf ,I2C,HARDWARE,PERIPHERALS,READ,*
------------------------------------
I2C READ addr, option, rcvlen, rcvbuf 
'addr' is the slave's I2C address. 
'option' can be 0 for normal operation or 1 to keep control of the bus after the command (a stop condition will not be sent at the completion of the command) 
'rcvlen' is the number of bytes to receive. 
'rcvbuf' is the variable or array used to save the received data - this can be:
- A string variable. Bytes will be stored as sequential characters.
- A one dimensional array of numbers specified with empty brackets.

Received bytes will be stored in sequential elements of the array starting with the first.
Example: 
I2C READ &H6F, 0, 3, ARRAY() 
~I2C CHECK addr,I2C,HARDWARE,PERIPHERALS,*
------------------------------------
I2C CHECK addr
Will set the read only variable MM.I2C to 0 if a device responds at the address 'addr'. MM.I2C will be set to 1 if there is no response. 
~I2C CLOSE ,I2C,HARDWARE,PERIPHERALS,CLOSE,*
------------------------------------
I2C CLOSE 
Disables the master I2C module. This command will also send a stop if the bus is still held.
~I2C SLAVE ,I2C,HARDWARE,PERIPHERALS,SLAVE,*
------------------------------------
I2C SLAVE 
See Appendix B
~I2C2 ,I2C2,HARDWARE,PERIPHERALS,*
------------------------------------
I2C2 
The same set of commands as for I2C (above) but applying to the second I2C channel. 
~IF expr THEN stmt [: stmt] or IF expr THEN stmt ELSE stmt ,MMBASIC,ENVIRONMENT,CONTROL,IF,THEN,ELSE,*
------------------------------------
IF expr THEN stmt [: stmt] or IF expr THEN stmt ELSE stmt 
Evaluates the expression 'expr' and performs the statement following the THEN keyword if it is true or skips to the next line if false. 
If there are more statements on the line (separated by colons (:) they will also be executed if true or skipped if false. 
The ELSE keyword is optional and if present the  statement(s) following it will be executed if 'expr' resolved to be false. 
The 'THEN statement' construct can be also replaced with:  
GOTO linenumber | label'. This type of IF statement is all on one line. 
IF expression THEN
<statements>
[ELSEIF expression THEN
<statements>]
[ELSE
<statements>]
ENDIF
Multiline IF statement with optional ELSE and ELSEIF cases and ending with ENDIF. 
Each component is on a separate line. Evaluates 'expression' and performs the statement(s) following THEN if the expression is true or optionally 
the statement(s) following the ELSE statement if false. The ELSEIF statement (if present) is executed if the previous condition is false 
and it starts a new IF chain with further ELSE and/or ELSEIF statements as required. 
~INPUT ["prompt$"] var1 [,var2[, var3 [, etc]]],INPUT,USER INPUT,FILES,*
------------------------------------
INPUT ["prompt$"] var1 [,var2[, var3 [, etc]]]
Will take a list of values separated by commas (,) entered at the console and will assign them to a sequential list of variables. 
For example, if the command is: INPUT a, b, c And the following is typed on the keyboard: 23, 87, 66 Then a = 23 and b = 87 and c = 66 
The list of variables can be a mix of float, integer or string variables. The values entered at the console must correspond to the type of variable. 
If a single value is entered a comma is not required (however that value cannot contain a comma). 
'prompt$' is a string constant (not a variable or expression) and if specified it will be printed first. 
Normally the prompt is terminated with a semicolon (;) and in that case a question mark will be printed following the prompt. 
If the prompt is terminated with a comma (,) rather than the semicolon (;) the question mark will be suppressed.
~INPUT #nbr, list of variables ,INPUT,USER INPUT,FILES,*
------------------------------------
INPUT #nbr, list of variables 
Same as above except that the input is read from a serial port or file previously opened for INPUT as 'nbr'. See the OPEN command.
~INTERRUPT [myint] ,HARDWARE,PERIPHERALS,INTERRUPT,PIN,*
------------------------------------
INTERRUPT [myint] 
This command triggers a software interrupt. The interrupt is set up using  INTERRUPT 'myint' where 'myint' is the name of a subroutine that will be executed when the interrupt is triggered.  
Use INTERRUPT 0 to disable the interrupt  Use INTERRUPT without parameters to trigger the interrupt. 
NB: the interrupt can also be triggered from within a CSUB 
~IR dev, key , int or IR CLOSE ,HARDWARE,PERIPHERALS,IR,PIN,*
------------------------------------
IR dev, key , int or IR CLOSE 
Decodes NEC or Sony infrared remote control signals.  An IR Receiver Module is used to sense the IR light and demodulate the  signal. 
It can be connected to any pin however this pin must be configured in advanced using the command: SETPIN n, IR  
The IR signal decode is done in the background and the program will continue after this command without interruption. 
'dev' and 'key' should be numeric variables and their values will be updated whenever a new signal is received 
('dev' is the device code transmitted by the remote and 'key' is the key pressed). 
'int' is a user defined subroutine that will be called when a new key press is received or when the existing key is held down for auto repeat. 
In the interrupt subroutine the program can examine the variables 'dev' and 'key' and take appropriate action. 
The IR CLOSE command will terminate the IR decoder. 
Note that for the NEC protocol the bits in 'dev' and 'key' are reversed. 

For example, in 'key' bit 0 should be bit 7, bit 1 should be bit 6, etc. 
This does not affect normal use but if you are looking for a specific numerical code provided by a manufacturer you should reverse the bits. 
This describes how to do it: http://www.thebackshed.com/forum/forum_posts.asp?TID=8367 
INFRARED REMOTE CONTROL DECODER

You can easily add a remote control to your project using the IR command. When enabled this function will run in the background and interrupt the running program whenever a key is pressed on the IR remote control.
It will work with any NEC or Sony compatible remote controls including ones that generate extended messages. 
Most cheap programmable remote controls will generate either protocol and using one of these you can add a sophisticated flair to your Pico based project.
The NEC protocol is also used by many other manufacturers including Apple, Pioneer, Sanyo, Akai and Toshiba so their branded remotes can be used.
To detect the IR signal you need an IR receiver. NEC remotes use a 38kHz modulation of the IR signal and suitable receivers tuned to this frequency include the Vishay TSOP4838, Jaycar ZD1952 and Altronics Z1611A. 
Note that the I/O pins on the Raspberry Pi Pico are only 3.3V tolerant and so the receiver must be powered by a maximum of 3.3V. The Raspberry Pi Pico 2 is different and can withstand 5V.

Sony remotes use a 40 kHz modulation but receivers for this frequency can be hard to find. Generally 38 kHz receivers will work but maximum sensitivity will be achieved with a 40 kHz receiver.
The IR receiver can be connected to any pin on the Raspberry Pi Pico. This pin must be configured by the program using the command:
SETPIN n, IR
where n is the I/O pin to use for this function.
To setup the decoder you use the command:
IR dev, key, interrupt
Where dev is a variable that will be updated with the device code and key is the variable to be updated with the key code. Interrupt is the interrupt subroutine to call when a new key press has been detected. 
The IR decoding is done in the background and the program will continue after this command without interruption.This is an example of using the IR decoder connected to the GP6 pin:
SETPIN GP6, IR ' define the pin to be used
DIM INTEGER DevCode, KeyCode ' variables used by the decoder
IR DevCode, KeyCode, IRInt ' start the IR decoder
DO
' < body of the program >
LOOP
SUB IRInt ' a key press has been detected
PRINT "Received device = " DevCode " key = " KeyCode
END SUB
IR remote controls can address many different devices (VCR, TV, etc) so the program would normally examine the device code first to determine if the signal was intended for the program and, if it was, then take action based on the key pressed.
There are many different devices and key codes so the best method of determining what codes your remote generates is to use the above program to discover the codes.

INFRARED REMOTE CONTROL TRANSMITTER

Using the IR SEND command you can transmit a 12 bit Sony infrared remote control signal. 
This is intended for Raspberry Pi Pico to Raspberry Pi Pico or Micromite communications but it will also work with Sony equipment that uses 12 bit codes.
Note that all Sony products require that the message be sent three times with a 26 ms delay between each message.

The circuit on the right illustrates what is required. The transistor is used to drive the infrared LED because the output capability of the Raspberry Pi Pico is limited. 
This circuit provides about 50 mA to the LED.
To send a signal you use the command:
IR SEND pin, dev, key
Where pin is the I/O pin used, dev is the device code to send and key is the key code. Any I/O pin on the Raspberry Pi Pico can be used and you do not have to set it up beforehand (IR SEND will automatically do that).
The modulation frequency used is 38 kHz and this matches the common IR receivers (described in the previous page) for maximum sensitivity when communicating between two Raspberry Pi Picos or with a Micromite.
~IR SEND pin, dev, key ,HARDWARE,PERIPHERALS,IR,PIN,*
------------------------------------
IR SEND pin, dev, key 
Generate a 12-bit Sony Remote Control protocol infrared signal. 'pin' is the I/O pin to use. 
This can be any I/O pin which will be automatically configured as an output and should be connected to an infrared LED. 
Idle is low with high levels indicating when the LED should be turned on. 
'dev' is the device being controlled and is a number from 0 to 31, 
'key' is the simulated key press and is a number from 0 to 127. 
The IR signal is modulated at about 38KHz and sending the signal takes about 25mS. 
~KEYPAD var, int, r1, r2, r3, r4, c1, c2, c3 [, c4] or KEYPAD CLOSE ,HARDWARE,PERIPHERALS,PIN,KEYPAD,*
------------------------------------
KEYPAD var, int, r1, r2, r3, r4, c1, c2, c3 [, c4] or KEYPAD CLOSE 
Monitor and decode key presses on a 4x3 or 4x4 keypad. Monitoring of the keypad is done in the background and the program will continue after this command without interruption. 
'var' should be a numeric variable and its value will be updated whenever a key press is detected. 
'int' is a user defined subroutine that will be called when a new key press is received. 
In the interrupt subroutine the program can examine the variable 'var' and take appropriate action. r1, r2, r3 and r4 are pin numbers used 
for the four row connections to the keypad and c1, c2, c3 and c4 are the column connections. c4 is optional and is only used with 4x4 keypads. 
This command will automatically configure these pins as required. On a key press the value assigned to 'var' is the number of a numeric key 
(e.g. '6' will return 6) or 10 for the * key and 11 for the # key. On 4x4 keypads the number 20 will be returned for A, 21 for B, 22 for C and 23 for D. 
The KEYPAD CLOSE command will terminate the keypad function and return the I/O pin to a not configured state. 

A keypad is a low tech but effective method of entering numeric data. The PicoMite firmware supports either a 4x3 keypad or a 4x4 keypad and the monitoring and decoding of key presses is done in the background. 
When a key press is detected an interrupt will be issued where the program can deal with it.
Examples of a 4x3 keypad and a 4x4 keypad are the Altronics S5381 and S5383 (go to www.altronics.com ).
To enable the keypad feature you use the command:
KEYPAD var, int, r1, r2, r3, r4, c1, c2, c3, c4
Where 'var' is a variable that will be updated with the key code and 'int' is the name of the interrupt subroutine to call when a new key press has been detected. 
'r1’, 'r2’, 'r3' and 'r4' are the pin numbers used for the four row connections to the keypad (see the diagram below) and 'c1’, 'c2’, 'c3' and 'c4' are the column connections. 
'c4' is only used with 4x4 keypads and should be omitted if you are using a 4x3 keypad.
Any I/O pins on the Raspberry Pi Pico can be used and you do not have to set them up beforehand, the KEYPAD command will automatically do that for you.

The detection and decoding of key presses is done in the background and the program will continue after this command without interruption. 
When a key press is detected the value of the variable var will be set to the number representing the key (this is the number inside the circles in the diagram above). 
Then the interrupt will be called.
For example:
Keypad KeyCode, KP_Int, GP2, GP3, GP4, GP5, GP6, GP7, GP8 ' 4x3 keybd
DO
< body of the program >
LOOP
SUB KP_Int ' a key press has been detected
PRINT "Key press = " KeyCode
END SUB
~KILL file$ [,all] ,FILES,DELETE,*
------------------------------------
KILL file$ [,all] 
If the optional 'all' parameter is used then you will be prompted for a single confirmation. If 'all' is not specified you will be prompted on each file.
~LCD INIT d4, d5, d6, d7, rs, en   or  LCD line, pos, text$  or  LCD CLEAR   or  LCD CLOSE,HARDWARE,PERIPHERALS,LCD,PIN,*
------------------------------------
LCD INIT d4, d5, d6, d7, rs, en   or  LCD line, pos, text$  or  LCD CLEAR   or  LCD CLOSE
Display text on an LCD character display module. This command will work with most 1-line, 2-line or 4-line LCD modules that use the KS0066, HD44780 or 
SPLC780 controller (however this is not guaranteed). The LCD INIT command is used to initialise the LCD module for use. 
'd4' to 'd7' are the I/O pins that connect to inputs D4 to D7 on the LCD module (inputs D0 to D3 should be connected to ground). 
'rs' is the pin connected to the register select input on the module (sometimes called CMD). 
'en' is the pin connected to the enable or chip select input on the module. The R/W input on the module should always be grounded. 
The above I/O pins are automatically set to outputs by this command. 
When the module has been initialised data can be written to it using the LCD command. 
'line' is the line on the display (1 to 4) and 'pos' is the character location on the line (the first location is 1). 
'text$' is a string containing the text to write to the LCD display. 
'pos' can also be C8, C16, C20 or C40 in which case the line will be cleared and the text centred on a 8 or 16, 20 or 40 line display. 
For example: 
LCD 1, C16, "Hello" 
LCD CLEAR 
will erase all data displayed on the LCD and LCD CLOSE will terminate the LCD function and return all I/O pins to the not configured state. 
See the chapter Special Hardware Devices for more details. 
~LCD CMD d1 [, d2 [, etc]] or LCD DATA d1 [, d2 [, etc]],HARDWARE,PERIPHERALS,LCD,PIN,16x2,*
------------------------------------
LCD CMD d1 [, d2 [, etc]] or LCD DATA d1 [, d2 [, etc]]
These commands will send one or more bytes to an LCD display as either a command (LCD CMD) or as data (LCD DATA). 
Each byte is a number between 0 and 255 and must be separated by commas. 
The LCD must have been previously initialised using the LCD INIT command (see above). 
These commands can be used to drive a non standard LCD in "raw mode" or they can be used to enable specialised features such as scrolling, cursors and custom character sets. 
You will need to refer to the data sheet for your LCD to find the necessary command and data values.

The LCD command will display text on a standard LCD module with the minimum of programming effort.
This command will work with LCD modules that use the KS0066, HD44780 or SPLC780 controller chip and have 1, 2 or 4 lines. 
Typical displays include the LCD16X2 (futurlec.com), the Z7001 (altronics.com.au) and the QP5512 (jaycar.com.au). eBay is another good source where prices can range from $10 to $50.
To setup the display you use the DEVICE LCD INIT command:
LCD INIT d4, d5, d6, d7, rs, en 
d4, d5, d6 and d7 are the numbers of the I/O pins that connect to inputs D4, D5, D6 and D7 on the LCD module (inputs D0 to D3 and R/W on the module should be connected to ground). 
'rs' is the pin connected to the register select input on the module (sometimes called CMD or DAT). 'en' is the pin connected to the enable or chip select input on the module.
Any I/O pins on the Raspberry Pi Pico can be used and you do not have to set them up beforehand (the LCD command automatically does that for you). The following shows a typical set up.

To display characters on the module you use the LCD command:
LCD line, pos, data$
Where 'line' is the line on the display (1 to 4) and 'pos' is the position on the line where the data is to be written (the first position on the line is 1). 
'data$’ is a string containing the data to write to the LCD display. The characters in 'data$’ will overwrite whatever was on that part of the LCD.
The following shows a typical usage where d4 to d7 are connected to pins GP2 to GP5, rs is connected to pin GP6 and en to pin GP7.

LCD INIT GP2, GP3, GP4, GP5, GP6, GP7
LCD 1, 2, "Temperature"
LCD 2, 6, STR$(TEMPR(GP15)) ' DS18B20 connected to pin GP15
~LET variable = expression ,VARIABLES,*
------------------------------------
LET variable = expression 
Assigns the value of 'expression' to the variable. LET is automatically assumed if a statement does not start with a command. 
For example: 
Var = 56 
~LIBRARY SAVE  or LIBRARY DELETE or LIBRARY LIST or LIBRARY LIST ALL or LIBRARY DISK SAVE fname$ or LIBRARY DISK LOAD fname$,WORKFLOW,LIBRARY,DELETE,SAVE,DISK,LIST,LOAD,*
------------------------------------
LIBRARY SAVE  or LIBRARY DELETE or LIBRARY LIST or LIBRARY LIST ALL or LIBRARY DISK SAVE fname$ or LIBRARY DISK LOAD fname$
These routines are not visible to the programmer but are available to the running program and act the same as the built in commands and functions in MMBasic. 
Any code in the library that is not contained within a subroutine or function will be executed immediately before a program is run. 
This can be used to initialise constants, set options, etc. See the heading The Library in this manual for a full explanation. 
The library is stored in program memory Flash Slot 3 which will then not be available for saving a program (slots 1 to 2 will still be available). 
LIBRARY SAVE 
will take whatever is in normal program memory, compress it (remove redundant data such as comments) and append it to the library area 
(main program memory is then empty). The code in the library will not show in LIST or EDIT and will not be deleted when a new program is loaded or NEW is used. 
LIBRARY DELETE 
will remove the library and return Flash Slot 3 for normal use (OPTION RESET will do the same). 
LIBRARY LIST 
will list the contents of the library. Use ALL to list without page confirmations. 
LIBRARY DISK SAVE fname$ 
will save the current library as a binary file allowing a subsequent call to LIBRARY DISK LOAD fname$ to restore the library. 
Together, these allow libraries specific for individual programs to be stored and restored easily and distributed. 
Other than using version specific functionality in the library (WEB, VGA, GUI) libraries can be shared between versions.
~LINE x1, y1, x2, y2 [, LW [, C]] ,DRAWING,GRAPHIC,*
------------------------------------
LINE x1, y1, x2, y2 [, LW [, C]] 
On the VGA monitor this command will draw a line starting at the coordinates 'x1' and 'y1' and ending at 'x2' and 'y2'. 
'LW' is the line's width and is only valid for horizontal or vertical lines. It defaults to 1 if not specified or if the line is a diagonal. 
'C' is an integer representing the colour and defaults to the current foreground colour. 
All parameters can be expressed as arrays and the software will plot the number of lines as determined by the dimensions of the smallest array. 
'x1', 'y1', 'x2', and 'y2' must all be arrays or all be single variables /constants otherwise an error will be generated. 
'lw' and 'c' can be either arrays or single variables/constants. 
~LINE AA x1, y1, x2, y2 [, LW [, C]] ,DRAWING,GRAPHIC,*
------------------------------------
LINE AA x1, y1, x2, y2 [, LW [, C]] 
Draws a line with anti-aliasing . The parameters are as per the LINE command above. 
However this version will use variable intensity values of the specified colour to reduce the "staggered"  quality of diagonal lines. 
In addition this version can draw diagonal lines of any width. 
Note that it does not accept arrays as parameters.
~LINE GRAPH x(), y(), colour,DRAWING,GRAPHIC,*
------------------------------------
LINE GRAPH x(), y(), colour
This command generates a line graph of the coordinate pairs specified in "x()" and "y()". 
The graph will have n-1 segments where there are n elements in the x and y arrays.
~LINE INPUT [prompt$,] string-variable$ ,INPUT,USER INPUT,FILES,*
------------------------------------
LINE INPUT [prompt$,] string-variable$ 
Reads an entire line from the console input into 'string-variable$'.  
'prompt$' is a string constant (not a variable or expression) and if specified it will be printed first. 
Unlike INPUT, this command will read a whole line, not stopping for comma delimited data items. 
A question mark is not printed unless it is part of 'prompt$'. 
~LINE INPUT #nbr, string-variable$  ,INPUT,USER INPUT,FILES,*
------------------------------------
LINE INPUT #nbr, string-variable$  
Same as above except that the input is read from a serial communications port or a file previously opened for INPUT as 'nbr'. See the OPEN command. 
~LINE PLOT ydata() [,nbr][,xstart] [,xinc] [,ystart] [,yinc][,colour],DRAWING,GRAPHIC,*
------------------------------------
LINE PLOT ydata() [,nbr][,xstart] [,xinc] [,ystart] [,yinc][,colour]
Plots a line graph from an array of y-axis data points. 
'ydata' is an array of floats or integers to be plotted 
'nbr 'is the number of line segments to be plotted - defaults to the lesser of the array size and MM.HRES-2 if omitted 
'xstart' is the x-coordinate to start plotting - defaults to 0 
'xinc' is the increment along the x-axis to plot each coordinate - defaults to 1 
'ystart' is the location in ydata to start the plot - defaults to the array start 
'yinc' is the increment in ydata to add for each point to be plotted 
'colour' is the colour to draw the line
~LIST [fname$] or LIST ALL [fname$] ,WORKFLOW,*
------------------------------------
LIST [fname$] or LIST ALL [fname$] 
List a program on the console. LIST on its own will list the program with a pause at every screen full. 
LIST ALL will list the program without pauses. 
This is useful if you wish to transfer the program to a terminal emulator on a PC that has the ability to capture its input stream to a file. 
If the optional 'fname$' is specified then that file on the SD card will be listed. 
~LIST COMMANDS or LIST FUNCTIONS ,WORKFLOW,HELP,*
------------------------------------
LIST COMMANDS or LIST FUNCTIONS 
Lists all valid commands or functions 
~LIST PINS,WORKFLOW,HELP,BETA,*
------------------------------------
LIST PINS
Lists pins

This is actually quite a sneaky implementation. 
There is a CSUB command CallExecuteProgram which allows a CSUB to call a Basic subroutine in the user's program. 
It`s embedded  Basic implementation of the sub FREEPINS into the firmware completely transparently. 
When you call LIST PINS you are actually running a little Basic program but you don't see it and it doesn't use any user resource. 
AND the code is 100x simpler than doing the same thing in C
BETA 6.02 ONLY!
~LIST SYSTEM I2C,WORKFLOW,HELP,BETA,*
------------------------------------
LIST SYSTEM I2C
Produces a display showing the addresses of all I2C device connected to the STSTEM I2C pins
BETA 6.02 ONLY!
~LOAD file$ [,R] ,FILES,WORKFLOW,*
------------------------------------
LOAD file$ [,R] 
Loads a program called 'file$' from an SD card into program memory. 
If the optional suffix ,R is added the program will be immediately run without prompting (in this case 'file$' must be a string constant). 
If an extension is not specified ".BAS" will be added to the file name. 
~LOAD IMAGE file$ [, x] [, y] ,GRAPHIC,FILES,*
------------------------------------
LOAD IMAGE file$ [, x] [, y] 
Load a bitmapped image from the SD card and display it on the VGA  screen. 
''file$' is the name of the file and 'x' and 'y' are the screen coordinates for the top left hand corner of the image. 
If the coordinates are not specified the image will be drawn at the top left hand position on the screen. 
If an extension is not specified ".BMP" will be added to the file name. 
All types of the BMP format are supported including black and white and true colour 24-bit images. 
~LOAD JPG file$ [, x] [, y] ,GRAPHIC,FILES,*
------------------------------------
LOAD JPG file$ [, x] [, y] 
Load a jpg image from the SD card and display it on the VGA screen. 
'file$' is the name of the file and 'x' and 'y' are the screen coordinates for the top left hand corner of the image. 
If the coordinates are not specified the image will be drawn at the top left hand position on the screen. 
If an extension is not specified ".JPG" will be added to the file name. 
Progressive jpg images are not supported. 
~LOAD PNG fname$ [, x] [, y][,transparent] [,alphacut],GRAPHIC,FILES,*
------------------------------------
LOAD PNG fname$ [, x] [, y][,transparent] [,alphacut]
Loads and displays a png file 'fname' If no extension is specified .png will be automatically added to the filename.
The file must be in RGBA8888 format which is the normal default. 
If specified 'x' and 'y' indicate where on the display or framebuffer the image will appear. 
The optional parameter 'transparent' (defaults to 0) specifies one of the colour codes (0-15) which will be allocated to pixels 
in the png file with an alpha value less than 'alphacut' (defaults to 20). 
If 'transparent' is set to -1 the png image is written with pixels with an alpha value less than 'alphacut' missed completely.
~LOCAL variable [, variables] ,VARIABLES,*
------------------------------------
LOCAL variable [, variables] 
See DIM for the full syntax. 
Defines a list of variable names as local to the subroutine or function. 
This command uses exactly the same syntax as DIM and will create variables that will only be visible within the subroutine or function. 
They will be automatically discarded when the subroutine or function exits. 
~LONGSTRING ,STRINGS,*
------------------------------------
LONGSTRING 
The LONGSTRING commands allow for the manipulation of strings longer than the normal MMBasic limit of 255 characters.
Variables for holding long strings must be defined as single dimensioned integer arrays with the number of elements set to the number of characters 
required for the maximum string length divided by eight. The reason for dividing by eight is that each integer in an MMBasic array occupies eight bytes. 
Note that the long string routines do not check for overflow in the length of the strings. 

If an attempt is made to create a string longer than a long string variable's size the outcome will be undefined.
~LONGSTRING AES128 ENCRYPT/DECRYPT CBC/ECB/CTR key$/key[!/%](), in%(), out%() [,iv$/iv[!/%]()],STRINGS,*
------------------------------------
LONGSTRING AES128 ENCRYPT/DECRYPT CBC/ECB/CTR key$/key[!/%](), in%(), out%() [,iv$/iv[!/%]()]
Encrypts or decrypts the longstring in in%() putting the answer in out%() 
For CBC and CTR modes the encryption will generate a random initialisation vector and prepend out%() with the IV. 
If an explicit IV is specified this will be used instead of the random vector and this will be prepended to out%() 
For CBC and CTR decryption the firmware assumes that the first 16 bytes of in%() are the initialisation vector. 
In the case where you want to transmit a message without IV you can use LONGSTRING RIGHT to remove the IV before sending the message. 
In this case the recipient must know the IV as well as the key and create a complete longstring before using DECRYPT. 
This can be done by using LONGSTRING CONCAT to add the incoming message to a longstring containing the IV.
~LONGSTRING APPEND array%(), string$ ,STRINGS,*
------------------------------------
LONGSTRING APPEND array%(), string$ 
Append a normal MMBasic string to a long string variable. array%() is a long string variable while string$ is a normal MMBasic string expression.
~LONGSTRING BASE64 ENCODE/DECODE in%(), out%(),STRINGS,*
------------------------------------
LONGSTRING BASE64 ENCODE/DECODE in%(), out%()
This BASE64 encodes or decodes the longstring in in%() placing the answer in out%(). 
The array used as the output must be big enough relative to the input and the direction. 
Encoding increases length by 4/3 and decoding decreases it by 3/4
~LONGSTRING CLEAR array%() ,STRINGS,*
------------------------------------
LONGSTRING CLEAR array%() 
Will clear the long string variable array%(). i.e. it will be set to an empty string.
~LONGSTRING COPY dest%(), src%() ,STRINGS,*
------------------------------------
LONGSTRING COPY dest%(), src%() 
Copy one long string to another. dest%() is the destination variable and src%() is the source variable. Whatever was in dest%() will be overwritten.
~LONGSTRING CONCAT dest%(), src%() ,STRINGS,*
------------------------------------
LONGSTRING CONCAT dest%(), src%() 
Concatenate one long string to another. dest%() is the destination variable and src%() is the source variable. 
src%() will the added to the end of dest%() (the destination will not be overwritten).
~LONGSTRING LCASE array%() ,STRINGS,*
------------------------------------
LONGSTRING LCASE array%() 
Will convert any uppercase characters in array%() to lowercase. array%() must be long string variable. 
~LONGSTRING LEFT dest%(), src%(), nbr ,STRINGS,*
------------------------------------
LONGSTRING LEFT dest%(), src%(), nbr 
Will copy the left hand 'nbr' characters from src%() to dest%() overwriting whatever was in dest%(). i.e. copy from the beginning of src%(). 
src%() and  dest%() must be long string variables. 'nbr' must be an integer constant or expression. 
~LONGSTRING LOAD array%(), nbr, string$ ,STRINGS,*
------------------------------------
LONGSTRING LOAD array%(), nbr, string$ 
Will copy 'nbr' characters from string$ to the long string variable array%() overwriting whatever was in array%(). 
~LONGSTRING MID dest%(), src%(), start, nbr ,STRINGS,*
------------------------------------
LONGSTRING MID dest%(), src%(), start, nbr 
Will copy 'nbr' characters from src%() to dest%() starting at character position 'start' overwriting whatever was in dest%(). i.e. 
copy from the middle of src%(). 
'nbr' is optional and if omitted the characters from 'start' to the end of the string will be copied src%() and dest%() must be long string variables. 
'start' and 'nbr' must be an integer constants or expressions. 
~LONGSTRING PRINT [#n,] src%() ,STRINGS,BETA,*
------------------------------------
LONGSTRING PRINT [#n,] src%() 
Prints the longstring stored in 'src%()' to the file or COM port opened as '#n'. 
If '#n' is not specified the output will be sent to the console. 

BETA 6.02 ONLY!
Supports ; at the end of LONGSTRING PRINT to suppress the CR/LF 

~LONGSTRING REPLACE array%() , string$, start ,STRINGS,*
------------------------------------
LONGSTRING REPLACE array%() , string$, start 
Will substitute characters in the normal MMBasic string string$ into an existing long string array%() starting at position 'start' in the long string. 
~LONGSTRING RESIZE addr%(), nbr ,STRINGS,*
------------------------------------
LONGSTRING RESIZE addr%(), nbr 
Sets the size of the longstring to nbr. This overrides the size set by other longstring commands so should be used with caution. 
Typical use would be in using a longstring as a byte array. 
~LONGSTRING RIGHT dest%(), src%(), nbr ,STRINGS,*
------------------------------------
LONGSTRING RIGHT dest%(), src%(), nbr 
Will copy the right hand 'nbr' characters from src%() to dest%() overwriting whatever was in dest%(). i.e. copy from the end of src%(). 
src%() and dest%() must be long string variables. 
'nbr' must be an integer constant or expression. 
~LONGSTRING SETBYTE addr%(), nbr, data ,STRINGS,*
------------------------------------
LONGSTRING SETBYTE addr%(), nbr, data 
Sets byte nbr to the value "data", nbr respects OPTION BASE 
~LONGSTRING TRIM array%(), nbr ,STRINGS,*
------------------------------------
LONGSTRING TRIM array%(), nbr 
Will trim 'nbr' characters from the left of a long string. array%() must be a long string variables. 'nbr' must be an integer constant or expression. 
~LONGSTRING UCASE array%() ,STRINGS,*
------------------------------------
LONGSTRING UCASE array%() 
Will convert any lowercase characters in array%() to uppercase. array%() must be long string variable. 
~LOOP [UNTIL expression] ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
LOOP [UNTIL expression] 
Terminates a program loop: see DO. 
~MAP commands HDMI VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP commands HDMI VERSION ONLY
The MAP commands allow the programmer to set the colours used in 4 or 8-bit colour modes. 
Each value in the 4 or 8-bit colour pallet can be set to an independent 24-bit colour (ie, RGB555 format). 
See the MAP function for more information
~MAP( n ) = rgb%   <- HDMI VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP( n ) = rgb%   <- HDMI VERSION ONLY
This will assign the 24-bit colour 'rgb% to all pixels with the 4 or 8-bit colour value of 'n'. 
The change is activated after the MAP SET command.
~MAP MAXIMITE   <- HDMI VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP MAXIMITE   <- HDMI VERSION ONLY
This will set the colour map to the colours implemented in the original Colour Maximite.
~MAP GREYSCALE   <- HDMI VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP GREYSCALE   <- HDMI VERSION ONLY
This will set the colour map to 16 or 32 levels of grey (depending on the MODE). MAP GRAYSCALE is also valid.
~MAP SET    <- HDMI VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP SET    <- HDMI VERSION ONLY
This will cause MMBasic to update the colour map (set using MAP(n)=rgb%) during the next frame blanking interval.
~MAP RESET    <- HDMI VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP RESET    <- HDMI VERSION ONLY
This will reset the colour map to the default colours
~MAP commands    <- VGA VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP commands    <- VGA VERSION ONLY
The MAP commands allow the programmer to select the colours used in 4-bit colour modes. 
Each value in the 4-bit colour pallet can be set to one of the 16 available colours . 
See the MAP function for more information.
~MAP( n ) = rgb%    <- VGA VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP( n ) = rgb%    <- VGA VERSION ONLY
This will assign the 24-bit colour 'rgb% to all pixels with the 4-bit colour value of 'n'. 
The RGB value is converted to one of the available 16 VGA RGB121 colours as set by the resistor network. 
The change is activated after the MAP SET command.
~MAP MAXIMITE    <- VGA VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP MAXIMITE    <- VGA VERSION ONLY
This will set the colour map to the colours implemented in the original Colour Maximite.
~MAP SET    <- VGA VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP SET    <- VGA VERSION ONLY
This will cause MMBasic to update the colour map (set using MAP(n)=rgb%) during the next frame blanking interval.
~MAP RESET    <- VGA VERSION ONLY,GRAPHIC,COLORS,*
------------------------------------
MAP RESET    <- VGA VERSION ONLY
This will reset the colour map to the default colours which in 4-bit mode are: 
'n' | Colour | Value 
15 WHITE RGB(255, 255, 255) 
14 YELLOW RGB(255, 255, 0) 
13 LILAC RGB(255, 128, 255)
12 BROWN RGB(255, 128, 0) 
11 FUCHSIA RGB(255, 64, 255) 
10 RUST RGB(255, 64, 0) 
9  MAGENTA RGB(255, 0, 255) 
8  RED RGB(255, 0, 0) 
7  CYAN RGB(0, 255, 255) 
6  GREEN RGB(0, 255, 0) 
5  CERULEAN RGB(0, 128, 255) 
4  MIDGREEN RGB(0, 128, 0)
3  COBALT RGB(0, 64, 255) 
2  MYRTLE RGB(0, 64, 0) 
1  BLUE RGB(0, 0, 255) 
0  BLACK  RGB(0, 0, 0)
~MATH commands,MATH,CALCUALTIONS,*
------------------------------------
MATH commands
structures in the firmware and there is the advantage that once debugged they are there for everyone without re-inventing the wheel. 
Note: 2 dimensional maths matrices are always specified DIM matrix(n_columns, n_rows) and of course the dimensions respect OPTION BASE. 

Quaternions are stored as a 5 element array w, x, y, z, magnitude.
~MATH RANDOMIZE [n],MATH,CALCUALTIONS,*
------------------------------------
MATH RANDOMIZE [n]
Seeds the Mersenne Twister algorithm. If n is not specified the seed is the time in microseconds since boot 
The Mersenne Twister algorithm gives a much better random number than the C-library inbuilt function 
~Simple array arithmetic commands,MATH,CALCUALTIONS,*
------------------------------------
Simple array arithmetic commands
More on simple array arithmetic commands
~MATH SET nbr, array() ,MATH,CALCUALTIONS,*
------------------------------------
MATH SET nbr, array() 
Sets all elements in array() to the value nbr. 
Note this is the fastest way of clearing an array by setting it to zero.

~MATH SCALE in(), scale ,out() ,MATH,CALCUALTIONS,*
------------------------------------
MATH SCALE in(), scale ,out() 
This scales the matrix in() by the scalar scale and puts the answer in out(). 
Works for arrays of any dimensionality of both integer and float and can convert between. 
Setting b to 1 is optimised and is the fastest way of copying an entire array.
~MATH ADD in(), num ,out() ,MATH,CALCUALTIONS,*
------------------------------------
MATH ADD in(), num ,out() 
This adds the value 'num' to every element of the matrix in() and puts the answer in out(). 
Works for arrays of any dimensionality of both integer and float and can convert between. 
Setting num to 0 is optimised and is a fast way of copying an entire array. 
in() and out() can be the same array. 
~MATH INTERPOLATE in1(), in2(), ratio, out() ,MATH,CALCUALTIONS,*
------------------------------------
MATH INTERPOLATE in1(), in2(), ratio, out() 
This command implements the following equation on every array element: out = (in2 - in1) * ratio + in1
Arrays can have any number of dimensions and must be distinct and have the same number of total elements. 
The command works with bot integer and floating point arrays in any mixture
~MATH WINDOW in(), minout, maxout, out() [,minin, maxin],MATH,CALCUALTIONS,*
------------------------------------
MATH WINDOW in(), minout, maxout, out() [,minin, maxin]
This command takes the "in" array and scales it between "minout" and "maxout" returning the answer in "out".  
Optionally, it can also return the minimum and maximum values found in the original data ("minin" and "minout").
Note: "minout" can be greater than "maxout" and in this case the data will be both scaled and inverted.e.g 

DIM IN(2)=(1,2,3)
DIM OUT(2)
MATH WINDOW IN(),7,3,OUT(),LOW,HIGH
Will return OUT(0)=7, OUT(1)=5,OUT(2)=3,LOW=1,HIGH=3
This command can massively simplify scaling data for plotting etc.
~MATH SLICE sourcearray(), [d1] [,d2] [,d3] [,d4] [,d5] , destinationarray() ,MATH,CALCUALTIONS,*
------------------------------------
MATH SLICE sourcearray(), [d1] [,d2] [,d3] [,d4] [,d5] , destinationarray() 
This command copies a specified set of values from a multi-dimensional array into a single dimensional array. 
It is much faster than using a FOR loop. 
The slice is specified by giving a value for all but one of the source array indices and there should be as many indices in the command, 
including the blank one, as there are dimensions in the source array e.g.
OPTION BASE 1
DIM a(3,4,5)
DIM b(4)
MATH SLICE a(), 2, , 3, b()
Will copy the elements 2,1,3 and 2,2,3 and 2,3,3 and 2,4,3 into array b()
~MATH INSERT targetarray(), [d1] [,d2] [,d3] [,d4] [,d5] , sourcearray() ,MATH,CALCUALTIONS,*
------------------------------------
MATH INSERT targetarray(), [d1] [,d2] [,d3] [,d4] [,d5] , sourcearray() 
This is the opposite of MATH SLICE, has a very similar syntax, and allows you, for example, to substitute 
a single vector into an array of vectors with a single instruction e.g.
OPTION BASE 1
DIM targetarray(3,4,5)
DIM sourcearray(4)=(1,2,3,4)
MATH INSERT targetarray(), 2, , 3, sourcearray()
Will set elements 2,1,3 = 1 and 2,2,3 = 2 and 2,3,3 = 3 and 2,4,3 = 4
~MATH POWER inarray(), power, outarray(),MATH,CALCUALTIONS,*
------------------------------------
MATH POWER inarray(), power, outarray()
Raises each element in 'inarray()' to the 'power' defined and puts the output in 'outarray()'
~Matrix arithmetic commands,MATH,CALCUALTIONS,*
------------------------------------
Matrix arithmetic commands
More on Matrix arithmetic commands
~MATH M_INVERSE array!(), inversearray!() ,MATH,CALCUALTIONS,*
------------------------------------
MATH M_INVERSE array!(), inversearray!() 
This returns the inverse of array!() in inversearray!(). 
The array must be square and you will get an error if the array cannot be inverted (determinant=0). array!() and inversearray!() cannot be the same.
~MATH M_PRINT array() ,MATH,CALCUALTIONS,*
------------------------------------
MATH M_PRINT array() 
Quick mechanism to print a 2D matrix one row per line.
~MATH M_TRANSPOSE in(), out() ,MATH,CALCUALTIONS,*
------------------------------------
MATH M_TRANSPOSE in(), out() 
Transpose matrix in() and put the answer in matrix out(), both arrays must be2D but need not be square. 
If not square then the arrays must be dimensioned in(m,n) out(n,m)
~MATH M_MULT in1(), in2(), out() ,MATH,CALCUALTIONS,*
------------------------------------
MATH M_MULT in1(), in2(), out() 
Multiply the arrays in1() and in2() and put the answer in out()c. All arrays must be 2D but need not be square. 
If not square then the arrays must be dimensioned in1(m,n) in2(p,m) ,out(p,n) 
~Vector arithmetic ,MATH,CALCUALTIONS,*
------------------------------------
Vector arithmetic 
More on Vector arithmetic commands
~MATH V_PRINT array() ,MATH,CALCUALTIONS,PRINT,*
------------------------------------
MATH V_PRINT array() 
Quick mechanism to print a small array on a single line
~MATH V_NORMALISE inV(), outV() ,MATH,CALCUALTIONS,*
------------------------------------
MATH V_NORMALISE inV(), outV() 
Converts a vector inV() to unit scale and puts the answer in outV()(sqr(x*x + y*y +.......)=1 
There is no limit on number of elements in the vector 
~MATH V_MULT matrix(), inV(), outV() ,MATH,CALCUALTIONS,*
------------------------------------
MATH V_MULT matrix(), inV(), outV() 
Multiplies matrix() and vector inV() returning vector outV(). 
The vectors and the 2D matrix can be any size but must have the same cardinality.
~MATH V_CROSS inV1(), inV2(), outV() ,MATH,CALCUALTIONS,*
------------------------------------
MATH V_CROSS inV1(), inV2(), outV() 
Calculates the cross product of two three element vectors inV1() and inV2() and puts the answer in outV()
~MATH V_ROTATE x, y, a, xin(), yin(), xout(), yout(),MATH,CALCUALTIONS,*
------------------------------------
MATH V_ROTATE x, y, a, xin(), yin(), xout(), yout()
This command rotates the coordinate pairs in "xin()" and "yin()" around the centre point defined by "x" and "y" by the angle "a" and puts the results in "xout()" and "yout()". 
NB: the input and output arrays can be the same and the rotation angle is, by default, in radians but this can be changed using the OPTION ANGLE command. 
~Quaternion arithmetic,MATH,CALCUALTIONS,*
------------------------------------
Quaternion arithmetic
More on Quaternion arithmetic
~MATH Q_INVERT inQ(), outQ() ,MATH,CALCUALTIONS,*
------------------------------------
MATH Q_INVERT inQ(), outQ() 
Invert the quaternion in inQ() and put the answer in outQ()
~MATH Q_VECTOR x, y, z, outVQ() ,MATH,CALCUALTIONS,*
------------------------------------
MATH Q_VECTOR x, y, z, outVQ() 
Converts a vector specified by x , y, and z to a normalised quaternion vector outVQ() with the original magnitude stored
~MATH Q_CREATE theta, x, y, z, outRQ() ,MATH,CALCUALTIONS,*
------------------------------------
MATH Q_CREATE theta, x, y, z, outRQ() 
Generates a normalised rotation quaternion outRQ() to rotate quaternion vectors around axis x,y,z by an angle of theta. 
Theta is specified in radians.
~MATH Q_EULER yaw, pitch, roll, outRQ() ,MATH,CALCUALTIONS,*
------------------------------------
MATH Q_EULER yaw, pitch, roll, outRQ() 
Generates a normalised rotation quaternion outRQ() to rotate quaternion vectors as defined by the yaw, pitch and roll angles 
With the vector in front of the "viewer" yaw is looking from the top of the ector and rotates clockwise, pitch rotates the top away from the camera and roll  rotates around the z-axis clockwise. 
The yaw, pitch and roll angles default to radians but respect the setting of  OPTION ANGLE
~MATH Q_MULT inQ1,inQ2,outQ ,MATH,CALCUALTIONS,*
------------------------------------
MATH Q_MULT inQ1,inQ2,outQ 
Multiplies two quaternions inQ1() and inQ2() and puts the answer in outQ()
~MATH Q_ROTATE , RQ(), inVQ(), outVQ() ,MATH,CALCUALTIONS,*
------------------------------------
MATH Q_ROTATE , RQ(), inVQ(), outVQ() 
Rotates the source quaternion vector inVQ() by the rotate quaternion RQ()  and puts the answer in outVQ()
~MATH C,MATH,CALCUALTIONS,*
------------------------------------
MATH C
MATH C_ADD array1%(), array2%(), array3%()
MATH C_SUB array1%(), array2%(), array3%()
MATH C_MULT array1%(), array2%(), array3%()
MATH C_DIV array1%(), array2%(), array3%()
MATH C_ADD array1!(), array2!(), array3!()
MATH C_SUB array1!(), array2!(), array3!()
MATH C_MULT array1!(), array2!(), array3!()
MATH C_DIV array1!(), array2!(), array3!()
These commands do cell by cell operations (hence C_) on identically sized arrays. 
There are no restrictions on the number of dimensions and no restrictions on using the same array twice or even three times in the parameters.
The datatype must be the same for all the arrays. 
Eg, 
MATH C_MULT a%(),a%(),a%() 
will square all the values in the array a%()
~MATH FFT signalarray!(), FFTarray!() ,MATH,CALCUALTIONS,*
------------------------------------
MATH FFT signalarray!(), FFTarray!() 
Performs a fast fourier transform of the data in "signalarray!". 
"signalarray" must be floating point and the size must be a power of 2 (e.g. s(1023) assuming OPTION  BASE is zero) 
"FFTarray" must be floating point and have dimension 2*N where N is the same as the signal array (e.g. f(1,1023) assuming OPTION BASE is zero) 
The command will return the FFT as complex numbers with the real part in f(0,n) and the imaginary part in f(1,n)
~MATH FFT INVERSE FFTarray!(), signalarray!() ,MATH,CALCUALTIONS,*
------------------------------------
MATH FFT INVERSE FFTarray!(), signalarray!() 
Performs an inverse fast fourier transform of the data in "FFTarray!". "FFTarray" must be floating point and have dimension 2*N 
where N must be a power of 2 (e.g. f(1,1023) assuming OPTION BASE is zero) with the real part in f(0,n) and the imaginary part in f(1,n).
"signalarray" must be floating point and the single dimension must be the same as the FFT array. 
The command will return the real part of the inverse transform in "signalarray".
~MATH FFT MAGNITUDE signalarray!(),magnitudearray!() ,MATH,CALCUALTIONS,*
------------------------------------
MATH FFT MAGNITUDE signalarray!(),magnitudearray!() 
Generates magnitudes for frequencies for the data in "signalarray!" 
"signalarray" must be floating point and the size must be a power of 2 (e.g. s(1023) assuming OPTION BASE is zero) 
"magnitudearray" must be floating point and the size must be the same as the signal array 
The command will return the magnitude of the signal at various frequencies according to the formula: 
frequency at array position N = N * sample_frequency / number_of_samples
~MATH FFT PHASE signalarray!(), phasearray!() ,MATH,CALCUALTIONS,*
------------------------------------
MATH FFT PHASE signalarray!(), phasearray!() 
Generates phases for frequencies for the data in  "signalarray!". 
"signalarray" must be floating point and the size must be a power of 2 (e.g. s(1023) assuming OPTION BASE is zero) 
"phasearray" must be floating point and the size must be the same as the signal array
The command will return the phase angle of the signal at various frequencies according to the formula  above.
~MATH SENSORFUSION type ax, ay, az, gx, gy, gz, mx, my, mz, pitch, roll, yaw [,p1] [,p2],MATH,CALCUALTIONS,*
------------------------------------
MATH SENSORFUSION type ax, ay, az, gx, gy, gz, mx, my, mz, pitch, roll, yaw [,p1] [,p2]
Type can be MAHONY or MADGWICK 
Ax, ay, and az are the accelerations in the three directions and should be specified in units of standard gravitational acceleration. 
Gx, gy, and gz are the instantaneous values of rotational speed which should be specified in radians per second. 
Mx, my, and mz are the magnetic fields in the three directions and should be specified in nano-Tesla (nT) 
Care must be taken to ensure that the x, y and z components are consistent between the three inputs. 
So , for example, using the MPU-9250 the correct input will be ax, ay,az, gx, gy, gz, my, mx, -mz based on the reading from the sensor. 
Pitch, roll and yaw should be floating point variables and will contain the outputs from the sensor fusion. 
The SENSORFUSION routine will automatically measure the time between consecutive calls and will use this in its internal calculations. 
The Madwick algorithm takes an optional parameter p1. This is used as beta in the calculation. 
It defaults to 0.5 if not specified The Mahony algorithm takes two optional parameters p1, and p2. 
These are used as Kp and Ki in the calculation. If not specified these default to 10.0 and 0.0 respectively. 
A fully worked example of using the code is given on the BackShed forum at: 
~MATH PID INIT channel, pid_params!(), callback,MATH,CALCUALTIONS,PID,*
------------------------------------
MATH PID INIT channel, pid_params!(), callback
This command sets up a PID controller that can work automatically in the background. 
Up to 8 PID controllers can run simultaneously (channels 1 to 8) 
'callback' is a MMbasic subroutine which is called at the rate defined by the sample time. 
See the MATH(PID ...) function for details of what should be included in the subroutine. 
The pid_params!() array must be dimensioned for all the listed elements, including the controller memory parameters 
(DIM pid_params!(13)) and be initialised with the required settings.
PID configuration
Element 0 = Kp
Element 1 = Ki
Element 2 = Kd
Element 3 = tau ' Derivative low-pass filter time constant
Element 4 = limMin 'Output limits
Element 5 = limMax
Element 6 = limMinInt 'Integrator limits
Element 7 = limMaxInt
Element 8 = T 'Sample time (in seconds)
Controller "memory"
Element 9 = integrator
Element 10 = prevError
Element 11 = differentiator
Element 12 = prevMeasurement
Element 13 = out
~MATH PID START channel ,MATH,CALCUALTIONS,PID,*
------------------------------------
MATH PID START channel 
Starts a previously initialised PID controller on the channel specified
~MATH PID STOP channel ,MATH,CALCUALTIONS,PID,*
------------------------------------
MATH PID STOP channel 
Stops a previously initialised PID controller on the channel specified and deletes the internal data structures 
See https://www.thebackshed.com/forum/ViewTopic.php?FID=16&TID=17263
For an example of setting up and running a PID controller 

The concept could be as follows:
DEVICE PID INIT channel%, pid_params!(), callback_function
pid_params!()
float Kp
float Ki
float Kd

' Derivative low-pass filter time constant float tau

'Output limits
float limMin
float limMax

'Integrator limits
float limMinInt
float limMaxInt

'Sample time (in milliseconds) float T

'Controller "memory"
float integrator
float prevError 'Required for integrator'
float differentiator
float prevMeasurement 'Required for differentiator

'Controller output float out
DEVICE PID START channel
DEVICE PID STOP channel
output! = DEVICE(PID channel, setpoint!, measurement!)
The PID init would set up a MMBasic function to be called every T milliseconds.

Following DEVICE PID START the callback function (MMBasic interrupt) would be called as requested and the Basic program should update the algorithm using the DEVICE(PID function.
It is left to the MMBasic user to derive the measurement (e.g. ADC input) and set the output (e.g. adjust PWM duty) 
in the callback function 
(NB: slow measurements like ADC input could be run in the main program so the value is always available for the PID callback). 
~MATH AES128 ENCRYPT/DECRYPT CBC/ECB/CTR key$/key(), in$/in(), out$/out() [,iv$/iv()],MATH,CALCUALTIONS,SECURITY,AES128,ENCRYPT,DECRYPT,*
------------------------------------
MATH AES128 ENCRYPT/DECRYPT CBC/ECB/CTR key$/key(), in$/in(), out$/out() [,iv$/iv()]
This command encrypts or decrypts the data in 'in' and puts the answer in 'out' using the AES128 encryption method specified 
The parameters can each be either a string, integer array, or float array with any mix possible 
The key must be 16 elements long (16*8=128bits), in and out must be a multiple of 16 elements long. 
In the case of out being specified as a string (e.g. out$), the string variable must exist and should be set to empty (DIM out$="") 
The maximum number of elements in 'in' and 'out' is limited by memory when defined as arrays. 
Strings for encrypting are limited to 240bytes (EBR) and 224bytes (CTR and CBC).  
For CBC and CTR encryption you can optionally specify an initialisation vector 'iv'. 
'iv' must be 16 elements long (16*8=128bits). 
If an initialisation vector is not specified encryption will generate a random initialisation vector. 
In both cases the output is prepended with the IV. 
For CBC and CTR, decryption requires that the first 16 elements of the input are the initialisation vector. 
In the case where you want to transmit a message without IV you should remove the IV before sending the message using standard MMBasic manipulations. 
In this case the recipient must know the IV as well as the key and create a complete message before using DECRYPT by prepending the IV to the incoming message.
~MEMORY ,WORKFLOW,MEMORY,*
------------------------------------
MEMORY 
List the amount of memory currently in use. 
For example:  
Program:
0K ( 0%) Program (0 lines) 108K (100%) 
Free  RAM:  0K ( 0%) 0 Variables  0K ( 0%) 

General 
140K (100%) Free 

Notes: 
- Memory usage is rounded to the nearest 1K byte.
- General memory is used by serial I/O buffers, etc. 
~MEMORY SET ,MEMORY,*
------------------------------------
MEMORY SET 
MEMORY SET address, byte, numberofbytes
MEMORY SET BYTE address, byte, numberofbytes
MEMORY SET SHORT address, short, numberofshorts
MEMORY SET WORD address, word, numberofwords
MEMORY SET INTEGER address, integervalue ,numberofintegers [,increment]
MEMORY SET FLOAT address, floatingvalue ,numberofloats [,increment]
This command will set a region of memory to a value. 
BYTE = One byte per memory address. 
SHORT = Two bytes per memory address. 
WORD = Four bytes per memory address. 
FLOAT = Eight bytes per memory address.
'increment' is optional and controls the increment of the 'address' pointer as the operation is executed. 
For example, if increment=3 then only every third element of the target is set. 
The default is 1.
Sub test
 Const slen=16
 Local dirct$(255) length slen
 dirct$(255)="1234567890abcdef"
 dirct$(0)="ABCDEFGHIJKLMNOP"
 Print dirct$(0),dirct$(255)
 Local integer a%=Peek(varaddr dirct$())
 Memory set byte a%,0,(Bound(dirct$(),1)+1)*(slen+1)
 Print dirct$(0),dirct$(255)
End Sub
~MEMORY COPY sourceaddress, destinationaddres, numberofbytes,MEMORY,*
------------------------------------
MEMORY COPY sourceaddress, destinationaddres, numberofbytes
See also COPY INTEGER and FLOAT 
COPY INTEGER and FLOAT will copy eight bytes per operation.
'sourceincrement' is optional and controls the increment of the 'sourceaddress'
pointer as the operation is executed. For example, if sourceincrement=3 then
only every third element of the source will be copied. The default is 1.
'destinationincrement' is similar and operates on the 'destinationaddress'
pointer.
~MEMORY COPY INTEGER (or FLOAT) sourceaddress, destinationaddress, numberofintegers [,sourceincrement] [,destinationincrement] ,MEMORY,*
------------------------------------
MEMORY COPY INTEGER (or FLOAT) sourceaddress, destinationaddress, numberofintegers [,sourceincrement] [,destinationincrement] 
MEMORY COPY INTEGER sourceaddress, destinationaddress, numberofintegers [,sourceincrement] [,destinationincrement]
MEMORY COPY FLOAT sourceaddress, destinationaddress, numberoffloats [,sourceincrement] [,destinationincrement]
This command will copy one region of memory to another. 
COPY INTEGER and FLOAT will copy eight bytes per operation. 
'sourceincrement' is optional and controls the increment of the 'sourceaddress' pointer as the operation is executed. 
For example, if sourceincrement=3 then only every third element of the source will be copied. 
The default is 1. 'destinationincrement' is similar and operates on the 'destinationaddress'
pointer.
~MEMORY PRINT #]fnbr , nbr, address%/array() MEMORY INPUT [#]fnbr , nbr, address%/array(),MEMORY PRINT,MEMORY INPUT,MEMORY,*
------------------------------------
MEMORY PRINT #]fnbr , nbr, address%/array() MEMORY INPUT [#]fnbr , nbr, address%/array()
These commands save or read 'nbr' of data bytes from or to memory from or to an open disk file. 
The memory to be saved can be specified as an integer array in which case the nbr of bytes to be saved or read is checked against the array size. 
Alternatively, a memory address can be used in which case no checking can take place and user errors could result in a crash of the firmware..
~MEMORY PACK (or UNPACK ) source%()/sourceaddress%, dest%()/destaddress%, number, size ,MEMORY,MEMORY PACK,*
------------------------------------
MEMORY PACK (or UNPACK ) source%()/sourceaddress%, dest%()/destaddress%, number, size 
MEMORY PACK source%()/sourceaddress%, dest%()/destaddress%, number, size 
MEMORY UNPACK source%()/sourceaddress%, dest%()/destaddress%, number, size
Memory pack and unpack allow integer values from one array to be compressed into another or uncompressed from one to the other. 
The two arrays are always normal integer arrays but the packed array can have 2, 4, 8, 16 or 64 values "packed into them. 
Thus a single integer array element could store 2 off 32-bit words, 4 off 16 bit values, 8 bytes, 16 nibbles, or 64 booleans (bits). 
"number specifies the number of values to be packed or unpacked and "size" specifies the number of bits (1,4,8,16,or 32) 
Alternatively, memory address(es) can be used in which case no checking can take place and user errors could result in a crash of the firmware.
~MKDIR dir$ ,FILES,*
------------------------------------
MKDIR dir$ 
Make, or create, the directory 'dir$' on the SD card.
~MID$( str$, start [, num]) = str2$ ,STRINGS,*
------------------------------------
MID$( str$, start [, num]) = str2$ 
The characters in 'str$', beginning at position 'start', are replaced by the characters in 'str2$'. 
The optional 'num' refers to the number of characters from 'str2' that are used in the replacement. 
If 'num' is omitted, all of 'str2' is used. 
Whether 'num' is omitted or included, the replacement of characters never goes beyond the original length of 'str$'.
~MODE 1 or MODE 2 Or MODE 3 (RP2350 only) ,GRAPHIC,VIDEO SYSTEM,WORKFLOW,COLORS,BETA,*
------------------------------------
MODE 1 or MODE 2 Or MODE 3 (RP2350 only) 
Switches between 
640 x 480 monochrome (MODE 1) and 
320 x 240 4-bit colour (MODE 2) and 
640 x 480 4-bit colour (MODE 3 - RP2350 only). 
On reboot the mode is reset to that defined with the OPTION DEFAULT MODE command. 

MODE 1 
640 x 480 x 2 colours (monochrome). Default at startup. 
Tiles width is fixed at 8 pixels. Tile height defaults to 12 pixels but can be from 8 to MM.HRES. 
Tiles colours are specified using the standard RGB888 notation. 
This is converted to RGB121. A framebuffer (F) and a layer buffer (L) can be created. 
These have no impact on the display and do not use user memory but both can be used for creating images and copying to the display screen (N) 

BETA 6.02 ONLY! 
Layer buffer now available in VGA mode 1 (252MHz and above) 
For more, read Graphics Functions; FRAMEBUFFERS AND LAYERS

MODE 2 
320 x 240 x 16 colours. RGB121 format (i.e. 1 bit for red, 2 bits for green, and 1 bit for blue). 
A framebuffer (F) can be created. 
This have no impact on the display and does not use user memory but can be used for creating images and copying to the display screen (N). 
In addition a layer buffer can be created. This also does not use user memory. 
Any pixels written to the layer buffer will automatically appear on the display sitting on top of whatever may be in the main display buffer. 
A colour can be specified (0-15: defaults to 0) which does not show allowing the main display buffer to show through. 
Map functionality is available to override the default colours of the 16 available 
In the case of VGA, the hardware is limited to the 16 colours defined by the resistor network 

MODE 3 
640 x 480 x 16 colours. (RP2350 only)
RGB121 format (i.e. 1 bit for red, 2 bits for green, and 1 bit for blue). A framebuffer (F) can be created. 
This have no impact on the display and does not use user memory but can be used for creating images and copying to the display screen (N). 
In addition a layer buffer can be created. 
This also does not use user memory. any pixels written to the layer buffer will automatically appear on the display sitting on top of whatever may be in the main display buffer. 
A colour can be specified (0-15: defaults to 0) which does not show allowing the main display buffer to show through. 
Map functionality is available to override the default colours of the 16 available. 
In the case of VGA, the hardware is limited to the 16 colours defined by the resistor network

~MODE n ,GRAPHIC,VIDEO SYSTEM,WORKFLOW,COLORS,BETA,*
------------------------------------
MODE n 
HDMI video supports a number of resolutions (see OPTION RESOLUTION). 
This command will select the mode 'n' depending on the resolution: OPTION RESOLUTION 640 x 480

MODE 1
640 x 480 x 2-colours (monochrome). Default at startup. Use the command as normal. 
Tiles width is fixed at 8 pixels. Tile height defaults to 12 pixels but can be from 8 to MM.HRES. 
Tiles colours are specified using the standard RGB888 notation. This is converted to RGB555. 
A framebuffer (F) and a layer buffer (L) can be created. These can be used for creating images and copying to the display screen (N)

BETA 6.02 ONLY! 
Layer buffer now available in HDMI mode 1 
For more, read Graphics Functions; FRAMEBUFFERS AND LAYERS
beta 02b17 
Now supports 800x600 operations which should be perfect for the 10.1" monitors and any standard 4:3 monitors and improves compatibility with some CMM2 programs.
This trades heap for framebuffer space so heap is reduced from 180KB to 100KB but it allows 800x600x16 colour operation (with mapping to any RGB332 colour) which is the highest colour resolution available.

OPTION RESOLUTION 800

MODE 1 
800x600x1-bit with tiles
MODE 2 
400x300x4-bit with layers
MODE 3 
800x600x4-bit
MODE 5 
400x300x8-bit (RGB332) with layer

MODE 2
320 x 240 x 16 colours. A framebuffer (F) can be created. This can be used for creating images and copying to the display screen (N). 
In addition a layer buffer can be created. 
Any pixels written to the layer buffer will automatically appear on the display sitting on top of whatever may be in the main display buffer. 
A colour can be specified (0-15: defaults to 0) which does not show allowing the main display buffer to show through. 
Map functionality is available to override the default colours. 

MODE 3
640 x 480 x 16 colours. Colour mapping to RGB555 palette. A framebuffer (F) can be created. 
It can be used for creating images and copying to the display screen (N). In addition a layer buffer can be created. 
Any pixels written to the layer buffer will automatically appear on the display sitting on top of whatever may be in the main display buffer. 
A colour can be specified (0-15: defaults to 0) which does not show allowing the main display buffer to show through. 

MODE 4 
320 x 240 x 32768 colours. This is full RGB555 allowing good quality colour images to be displayed. 
A framebuffer (F) and a layer buffer (L) can be created. 
These have no impact on the display and can be used for creating images and copying to the display screen (N). Only one can be created 

MODE 5 
320 x 240 x 256 colours. A framebuffer (F) can be created. This has no impact on the display. 
It can be used for creating images and copying to the display screen (N). 
In addition a layer buffer can be created. This does not use user memory. 
Any pixels written to the layer buffer will automatically appear on the display sitting on top of whatever may be in the main display buffer. 
A colour can be specified (0-255: defaults to 0) which does not show allowing the main display buffer to show through. 
Map functionality is available to override the default colours of the 256 available. 
Each of the 256 colours can be mapped to any RGB555 colour. 

~OPTION RESOLUTION,GRAPHIC,VIDEO SYSTEM,WORKFLOW,COLORS,*
------------------------------------
OPTION RESOLUTION

OPTION RESOLUTION 1280 x 720
MODE 1
1280 x 720 x 2-colours with RGB332 tiles (use the TILE command as normal)

MODE 2
320 x 180 x 16colours with 2 optional layers and colour mapping to RGB332 palette

MODE 3
640 x 360 x 16 colours with optional layer and colour mapping to RGB332 palette

MODE 5
320 x 180 x 256 colours with optional layer (no memory usage) 

OPTION RESOLUTION 1024 x 768

MODE 1
1024 x 768 x 2 colours with RGB332 tiles (use the TILE command as normal)

MODE 2
256 x 192 x 16 colours with 2 optional layers and colour mapping to RGB332 palette

MODE 3
512 x 384 x 16 colours with optional layer and colour mapping to RGB332 palette

MODE 5
256 x 192 x 256 colours with optional layer.
beta 02b17 
Now supports 800x600 operations which should be perfect for the 10.1" monitors and any standard 4:3 monitors and improves compatibility with some CMM2 programs.
This trades heap for framebuffer space so heap is reduced from 180KB to 100KB but it allows 800x600x16 colour operation (with mapping to any RGB332 colour) which is the highest colour resolution available.

OPTION RESOLUTION 800

MODE 1 
800x600x1-bit with tiles
MODE 2 
400x300x4-bit with layers
MODE 3 
800x600x4-bit
MODE 5 
400x300x8-bit (RGB332) with layer
~MOUSE  ,MOUSE,PERIPHERALS,HARDWARE,PIN,*
------------------------------------
MOUSE  
For all variants of the command. In the case of USB firmware 'channel' is the USB port that the mouse is connected to (1-4). 
See MM.INFO(USB n) for more information. For PS2 firmware 'channel' is fixed at the value 2
~MOUSE INTERRUPT ENABLE channel, int,MOUSE,PERIPHERALS,HARDWARE,PIN,*
------------------------------------
MOUSE INTERRUPT ENABLE channel, int
'int' is a user defined subroutine that will be called when the left mouse button is pressed.
~MOUSE INTERRUPT DISABLE channel,MOUSE,PERIPHERALS,HARDWARE,PIN,*
------------------------------------
MOUSE INTERRUPT DISABLE channel
Disables an interrupt on the left mouse button
~MOUSE SET channel, y- coord, y-coord [, wheel-count],MOUSE,PERIPHERALS,HARDWARE,PIN,*
------------------------------------
MOUSE SET channel, y- coord, y-coord [, wheel-count]
Sets the current position that will be returned by the mouse x, y and optionally wheel positions
~MOUSE OPEN channel, CLKpin, DATApin ,MOUSE,PERIPHERALS,HARDWARE,PIN,*
------------------------------------
MOUSE OPEN channel, CLKpin, DATApin 
Opens a connection to a PS2 mouse connected to the two specified pins. 
This command can be used in a program to configure the mouse while the program is running as against OPTION MOUSE which permanently configures the mouse. 
Channel is included for compatibility with USB mouse functionality and must be set to 2. 
If a mouse is not connected you will get an error and the command can be called again once the mouse is connected
~MOUSE CLOSE channel,MOUSE,PERIPHERALS,HARDWARE,PIN,*
------------------------------------
MOUSE CLOSE channel
Closes access to the mouse and restores the pins to normal use. 
The command will error if OPTION MOUSE has been set.
~NEW ,MMBASIC,ENVIRONMENT,CONTROL,WORKFLOW,*
------------------------------------
NEW 
Erases the current program and clears all non permanent options and variables including saved variables. 
This command resets the interpreter to a known state. 
~NEXT [counter-variable] [,counter-variable], etc ,WORKFLOW,PROGRAMCONTROL,*
------------------------------------
NEXT [counter-variable] [,counter-variable], etc 
NEXT comes at the end of a FOR-NEXT loop; see FOR. 
The 'counter-variable' specifies exactly which loop is being operated on. 
If  no 'counter-variable' is specified the NEXT will default to the innermost  loop. 
It is also possible to specify multiple counter-variables as in:   NEXT x, y, z  
~ON ERROR ABORT or ON ERROR IGNORE or ON ERROR SKIP [nn] or ON ERROR CLEAR,ERROR,PROGRAMCONTROL,*
------------------------------------
ON ERROR ABORT or ON ERROR IGNORE or ON ERROR SKIP [nn] or ON ERROR CLEAR
This controls the action taken if an error occurs while running a program and or applies to all errors discovered by MMBasic including syntax errors, wrong data, missing hardware, etc. 
~ON ERROR ABORT ,ERROR,PROGRAMCONTROL,*
------------------------------------
ON ERROR ABORT 
Will cause MMBasic to display an error message, abort the program and return to the command prompt. 
This is the normal behaviour and is the default when a program starts running. 
~ON ERROR IGNORE ,ERROR,PROGRAMCONTROL,*
------------------------------------
ON ERROR IGNORE 
Will cause any error to be ignored. 
~ON ERROR SKIP ,ERROR,PROGRAMCONTROL,*
------------------------------------
ON ERROR SKIP 
Will ignore an error in a number of commands (specified by the number 'nn') executed following this command. 
'nn' is optional, the default if not specified is one. 
After the number of commands has completed (with an error or not) the behaviour of MMBasic will revert to ON ERROR ABORT. 
If an error occurs and is ignored/skipped the read only variable MM.ERRNO 
will be set to non zero and MM.ERRMSG$ will be set to the error message that would normally be generated. 
These are reset to zero and an empty string by ON ERROR CLEAR. 
They are also cleared when the program is run and when ON ERROR IGNORE and ON ERROR SKIP are used.
ON ERROR IGNORE can make it very difficult to debug a program so it is strongly recommended that only ON ERROR SKIP be used.
~ON ERROR CLEAR. ,ERROR,PROGRAMCONTROL,*
------------------------------------
ON ERROR CLEAR. 
They are also cleared when the program is run and when ON ERROR IGNORE and ON ERROR SKIP are used. 
ON ERROR IGNORE can make it very difficult to debug a program so it is strongly recommended that only ON ERROR SKIP be used. 
~ON KEY target or ON KEY ASCIIcode, target ,KEYBOARD,*
------------------------------------
ON KEY target or ON KEY ASCIIcode, target 
The first version of the command sets an interrupt which will call 'target' user defined subroutine 
whenever there is one or more characters waiting in the console input buffer. 
Note that all characters waiting in the input buffer should be read in the interrupt subroutine 

otherwise another interrupt will be automatically generated as soon as the program returns from the interrupt. 
The second version allows you to associate an interrupt routine with a specific key press. 
This operates at a low level for the console and if activated the key does not get put into the input buffer but merely triggers the interrupt. 
It uses a separate interrupt from the simple ON KEY command so can be used at the same time if required. 
In both variants, to disable the interrupt use numeric zero for the target, i.e.: ON KEY 0. or ON KEY ASCIIcode, 0 
~ON PS2 target,MOUSE,PS2,*
------------------------------------
ON PS2 target
This triggers an interrupt whenever the PicoMite firmware sees a message from the PS2 interface.
Use MM.info(PS2) to report the raw message received. This allows the programmer to trap both keypress and release. 
See https://wiki.osdev.org/PS/2_Keyboard for the scan codes (Set 2).
~ONEWIRE RESET pin or ONEWIRE WRITE pin, flag, length, data [, data...] or  ONEWIRE READ pin, flag, length, data [, data...] ,HARDWARE,PERIPHERALS,*
------------------------------------
ONEWIRE RESET pin or ONEWIRE WRITE pin, flag, length, data [, data...] or  ONEWIRE READ pin, flag, length, data [, data...] 
Commands for communicating with 1-Wire devices. 
ONEWIRE RESET will reset the 1-Wire bus ONEWIRE WRITE will send a number of bytes ONEWIRE READ will read a number of bytes 
'pin' is the I/O pin (located in the rear connector) to use. It can be any pin capable of digital I/O. 
'flag' is a combination of the following options: 
1 -Send reset before command 
2 -Send reset after command 
4 -Only send/recv a bit instead of a byte of data 
8 -Invoke a strong pullup after the command (the pin will be set high and open drain disabled) 

'length' is the length of data to send or receive 'data' is the data to send or variable to receive. 
The number of data items must agree with the length parameter. 

Appendix C - 1-Wire Communications
1-WIRE COMMUNICATIONS

The 1-Wire protocol was developed by Dallas Semiconductor to communicate with chips using a single signalling line. 
This implementation was written for MMBasic by Gerard Sexton. There are three commands that you can use:
ONEWIRE RESET pin
Reset the 1-Wire bus
ONEWIRE WRITE pin, flag, length, data [, data…]
Send a number of bytes
ONEWIRE READ pin, flag, length, data [, data…]
Get a number of bytes
Where:
pin - The I/O pin to use. It can be any pin capable of digital I/O.

flag - A combination of the following options:
1 - Send reset before command
2 - Send reset after command
4 - Only send/recv a bit instead of a byte of data
8 - Invoke a strong pullup after the command (the pin will be set high and open drain disabled)

length - Length of data to send or receive data - Data to send or variable to receive.
The number of data items must agree with the length parameter.
The automatic variable MM.ONEWIRE returns true if a device was found 
After the command is executed, the I/O pin will be set to the not configured state unless flag option 8 is used.
When a reset is requested the automatic variable MM.ONEWIRE will return true if a device was found. 
This will occur with the ONEWIRE RESET command and the ONEWIRE READ and ONEWIRE WRITE commands if a reset was requested (flag = 1 or 2).
The 1-Wire protocol is often used in communicating with the DS18B20 temperature measuring sensor and to help in that regard MMBasic includes the TEMPR() function 
which provides a convenient method of directly reading the temperature of a DS18B20 without using these functions. 
~OPEN fname$ FOR mode AS [#]fnbr ,FILES,INPUT,OUTPUT,*
------------------------------------
OPEN fname$ FOR mode AS [#]fnbr 
Opens a file for reading or writing. 
'fname' is the filename with an optional extension separated by a dot (.). 
Long file names with upper and lower case characters are supported. A directory path can be specified with the backslash as directory separators. 
The parent of the current directory can be specified by using a directory name of .. (two dots) and the current directory with . (a single dot). 
For example 
OPEN "..\dir1\dir2\filename.txt" FOR INPUT AS #1 
'mode' is INPUT, OUTPUT, APPEND or RANDOM. 
INPUT will open the file for reading and throw an error if the file does not exist. 
OUTPUT will open the file for writing and will automatically overwrite any existing file with the same name. 
APPEND will also open the file for writing but it will not overwrite an existing file; instead any writes will be appended to the end of the file. 
If there is no existing file the APPEND mode will act the same as the OUTPUT mode (i.e. the file is created then opened for writing). 

RANDOM will open the file for both read and write and will allow random access using the SEEK command. 

When opened the read/write pointer is positioned at the end of the file. 'fnbr' is the file number (1 to 10).

RANDOM works the same as output and creates a file if one does not exist The # is optional. Up to 10 files can be open simultaneously. 

The INPUT, LINE INPUT, PRINT, WRITE and CLOSE commands as well as the EOF() and INPUT$() functions all use 'fnbr' to identify the file being operated on. 
See also ON ERROR and MM.ERRNO for error handling. 
~OPEN comspec$ AS [#]fnbr ,COM1,COM2,SERIAL,INPUT,OUTPUT,*
------------------------------------
OPEN comspec$ AS [#]fnbr 
Will open a serial communications port for reading and writing. Two ports are available (COM1: and COM2:) and both can be open simultaneously. 
Using 'fnbr' the port can be written to and read from using any command or function that uses a file number. 

Appendix A - Serial Communications
Serial Communications
Two serial interfaces are available for asynchronous serial communications. They are labelled COM1: and COM2:.
I/O Pins
Before a serial interface can be used the I/O pins must be defined using the following command for the first channel (referred as COM1):
SETPIN rx, tx, COM1
Valid pins are
RX: 
GP1, GP13 or GP17
TX:
GP0, GP12, GP16 or GP28

And the following command for the second channel (referred to as COM2):
SETPIN rx, tx, COM2
Valid pins are
RX:
GP5, GP9 or GP21
TX:
GP4, GP8 or GP20

TX is data from the Raspberry Pi Pico and RX is data to it. 
Note that on the WebMite version COM1 and COM2 are not available on GP20 to GP28

The signal polarity is standard for devices running at TTL voltages. 
Idle is voltage high, the start bit is voltage low, data uses a high voltage for logic 1 and the stop bit is voltage high. 
These signal levels allow you to directly connect to devices like GPS modules (which generally use TTL voltage levels).
COMMANDS

After being opened the serial port will have an associated file number and you can use any commands that operate with a file number to read and write to/from it. 
A serial port can be closed using the CLOSE command.

The following is an example:
SETPIN GP13, GP16, COM1
' assign the I/O pins for the first serial port
OPEN "COM1:4800" AS #5
' open the first serial port with a speed of 4800 baud
PRINT #5, "Hello"
' send the string "Hello" out of the serial port
dat$ = INPUT$(20, #5)
' get up to 20 characters from the serial port
CLOSE #5
' close the serial port

A serial port is opened using the command:
OPEN comspec$ AS #fnbr
'fnbr' is the file number to be used. It must be in the range of 1 to 10. The # is optional.
'comspec$’ is the communication specification and is a string (it can be a string variable) specifying the serial port to be opened and optional parameters. 
The default is 9600 baud, 8 data bits, no parity and one stop bit.
It has the form "COMn: baud, buf, int, int-trigger, EVEN, ODD, S2, 7BIT" where:
->‘n' is the serial port number for either COM1: or COM2:.
->‘baud' is the baud rate. This can be any number from 1200 to 921600. Default is 9600.
->‘buf' is the receive buffer size in bytes (default size is 256). The transmit buffer is fixed at 256 bytes.
->‘int' is interrupt subroutine to be called when the serial port has received some data.
->‘int-trigger' is the number of characters received which will trigger an interrupt.

All parameters except the serial port name (COMn:) are optional. If any one parameter is left out then all the following parameters must also be left out and the defaults will be used.

Five options can be added to the end of 'comspec$'. These are:
->'S2' specifies that two stop bits will be sent following each character transmitted.
->EVEN specifies that an even parity bit will be applied, this will result in a 9-bit transfer unless 7BIT is set.
->ODD specifies that an odd parity bit will be applied, this will result in a 9-bit transfer unless 7BIT is set
->7BIT specifies that there a 7bits of data. This is normally used with EVEN or ODD
->INV specifies that the output signals will be inverted and input assumed to be inverted 

Examples
Opening a serial port using all the defaults:
OPEN "COM1:" AS #2
Opening a serial port specifying only the baud rate (4800 bits per second):
OPEN "COM1:4800" AS #1
Opening a serial port specifying the baud rate (9600 bits per second) and receive buffer size (1KB):
OPEN "COM2:9600, 1024" AS #8
The same as above but with two stop bits enabled:
OPEN "COM2:9600, 1024, S2" AS #8
An example specifying everything including an interrupt, an interrupt level, and two stop bits:
OPEN "COM2:19200, 1024, ComIntLabel, 256, S2" AS #5
READING AND WRITING

Once a serial port has been opened you can use any command or function that uses a file number to read from and write to the port. 
Data received by the serial port will be automatically buffered in memory by MMBasic until it is read by the program and the INPUT$() function is the most convenient way of doing that. 
When using the INPUT$() function the number of characters specified will be the maximum number of characters returned but it could be less if there are less characters in the receive buffer.
 In fact the INPUT$() function will immediately return an empty string if there are no characters available in the receive buffer.
The LOC() function is also handy; it will return the number of characters waiting in the receive buffer (i.e. the maximum number characters that can be retrieved by the INPUT$() function). 
Note that if the receive buffer overflows with incoming data the serial port will automatically discard the oldest data to make room for the new data.

The PRINT command is used for outputting to a serial port and any data to be sent will be held in a memory buffer while the serial port is sending it.
This means that MMBasic will continue with executing the commands after the PRINT command while the data is being transmitted. 
The one exception is if the output buffer is full and in that case MMBasic will pause and wait until there is sufficient space before continuing. 
The LOF() function will return the amount of space left in the transmit buffer and you can use this to avoid stalling the program while waiting for space in the buffer to become available.
If you want to be sure that all the data has been sent (perhaps because you want to read the response from the remote device) 
you should wait until the LOF() function returns 256 (the transmit buffer size) indicating that there is nothing left to be sent.
Serial ports can be closed with the CLOSE command. This will wait for the transmit buffer to be emptied then free up the memory used by the buffers and cancel the interrupt (if set). 
A serial port is also automatically closed when commands such as RUN and NEW are issued.

INTERRUPTS

The interrupt subroutine (if specified) will operate the same as a general interrupt on an external I/O pin (see the chapter Using the I/O pins for a description).
When using interrupts you need to be aware that it will take some time for MMBasic to respond to the interrupt and more characters could have arrived in the meantime, especially at high baud rates.

For example, if you have specified the interrupt level as 200 characters and a buffer of 256 characters then quite easily the buffer will have overflowed 
by the time the interrupt subroutine can read the data. In this case the buffer should be increased to 512 characters or more.
~OPEN comspec$ AS GPS [,timezone_offset] [,monitor] ,COM1,COM2,SERIAL,INPUT,OUTPUT,GPS,*
------------------------------------
OPEN comspec$ AS GPS [,timezone_offset] [,monitor] 
Will open a serial communications port for reading from a GPS receiver. See  the GPS function for details. 
The sentences interpreted are GPRMC, GNRMC, GPCGA and GNCGA. 
The timezone_offset parameter is used to convert UTC as received from the GPS to the local timezone. 
If omitted the timezone will default to UTC. 
The timezone_offset can be a any number between -12 and 14 allowing the time to be set correctly even for the Chatham Islands in New Zealand (UTC +12:45). 
If the monitor parameter is set to 1 then all GPS input is directed to the console. 
This can be stopped by closing the GPS channel. 
~PAUSE delay ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
PAUSE delay 
Halt execution of the running program for 'delay' ms. This can be a fraction. 
For example, 0.2 is equal to 200 us. The maximum delay is 2147483647 ms (about 24 days). 
Note that interrupts will be recognised and processed during a pause. 
~PIN( pin ) = value ,PIN,HARDWARE,*
------------------------------------
PIN( pin ) = value 
For a 'pin' configured as digital output this will set the output to low ('value' is zero) or high ('value' non-zero). 
You can set an output high or low before it is configured as an output and that setting will be the default output when the SETPIN command takes effect. 
See the function PIN() for reading from a pin and the command SETPIN for configuring it. 
Refer to the chapter "Using the I/O pins" for a general description of the PicoMiteVGA 's input/output capabilities. 
~PIO machine,PIO,HARDWARE,BETA,*
------------------------------------
PIO machine
The processors chip in the Raspberry Pi Pico with the RP2040 processors 
contains a programmable I/O system with two identical PIO devices (pio%=0 or pio%=1) acting like specialised CPU cores. 
The Raspberry Pi Pico 2 with the RP2350 processors has three PIO devices. 
BETA 6.02 ONLY!

Allows ";" as a comment separator for PIO commands

New PIO functionality for RP2350A and RP2350B
Adds MOV PINDIRS,src as valid instruction
Adds support for 'prev' and 'next' for the IRQ command

New PIO functionality for RP2350B
PIO SET BASE pionum, n 'sets the base of logical PIO pins to 0 or 16 to allow pins GP30-47 to be used for PIO

Alternative label syntax added:

PicoMite MMBasic RP2350A Edition V6.00.02b5
> list
PIO ASSEMBLE 1
program uart_rx        'uart tx program from RP2040 datasheet
.line 5                '5=start of PIO program memory RX
label start:           'label
.wrap target           'outer loop return
wait 0 pin 0           'wait for a start bit
set x,7 [10]           'set counter to 7, wait 10 cycles (=mid lsb)
label bitloop2:        'inner loop
in pins,1              'shift in 1 bit
jmp x-- bitloop2 [6]   'if x>0 next bit, delay 6, then x--
jmp pin good_stop      'if valid stop bit -> good_stop
IRQ 4 rel              '?? flag for ARM ??
wait 1 pin 0           'wait for a valid stop
jmp start              'then goto wait for new start (loop)
label good_stop:       'label
in null,24             'shift the data to LSB in the ISR
push                   'shift data to fifo
.wrap                  'outer loop for new character
.end program list
5: 2020
6: EA27
7: 4001
8: 0647
9: 00CD
10: C014
11: 4001
12: 00C5
13: 4005
14: 8000

EXAMPLE2 (Also improved colour coding of PIO directives)
Print "compiling first"
PIO assemble 1
.program first
.side set 1
.line 0
Pull block
.wrap target
Mov x,osr side 1
.label loop
Nop side 0 [4]
Jmp x--,loop side 1 [4]
IRQ 0 side 0
.wrap
.end program list
Print "compiling second"
PIO assemble 1
.program second
.side set 1
.line next
.wrap target
Wait 1 irq 0 side 0
Wait 1 irq 0 side 1
.wrap
.end program list
See the Appendix F for a more detailed description of programming PIOs.

Appendix F 
The PIO Programming Package

INTRODUCTION TO THE PIO

The RP2040 and RP2350 have many built in peripherals such as PWM, UART, ADC, SPI. 
Using PIOs it is possible to add specialised functions/peripherals such as high accuracy serial data interfaces and bit streams.
PIOs can be thought of as cut-down, highly specialised CPU cores. The RP2040 contains two PIO blocks while the RP2350 has three blocks. 
MMBasic refers to them as PIO0, PIO1 and PIO2 in line with the Raspberry Pi documentation. 
The PIOs run completely independently of the main system and of each other and run extremely fast, with a throughput of up to 32 bits during every clock cycle.
PIOs implement state machines. Before a state machine can execute it's program, the program needs to be written to PIO memory, and the state machine needs to be configured.
This appendix describes the support MMBasic can give in using PIO. 
It does not contain an explanation how to write PIO state machine programs. 
For better understanding how the PIO state machines work refer to the following thread "PIO explained PICOMITE" on the thebackshed.com forum:
https://www.thebackshed.com/forum/ViewTopic.php?FID=16&TID=15385

OVERVIEW OF PIO
A single PIO block has four independent state machines. All four state machines share a single 32 instruction program area of flash memory. 
This memory has write-only access from the main system, but has four read ports, one for each state machine, 
so that each can access it independently at its own speed. Each state machine has its own program counter.
Each state machine also has two 32-bit "scratchpad" registers, X and Y, which can be used as temporary data stores.
I/O pins are accessed via an input/output mapping module that can access 32 pins (but limited to 30 for the RP2040). 
All state machines can access all the pins independently and simultaneously. 
The system can write data into the input end of a 4-word 32-bit wide TX FIFO buffer. 
The state machine can then use pull to move the output word of the FIFO into the OSR (Output Shift Register). 
It can also use out to shift 1-32 bits at a time from the OSR into the output mapping module or other destinations. 
AUTOPULL can be used to automatically pull data until the TX FIFO is empty or reaches a preset level.
The system can read data from the output end of a 4-word 32-bit wide RX FIFO buffer. 
The state machine can then use in to shift 1-32 bits of data at a time from the input mapping module into the ISR (Input Shift Register). 
It can also use push to move the contents of the ISR into the FIFO. 
AUTOPUSH can be used to automatically push data until the RX FIFO is full or reaches a preset level.
The FIFO buffers can be reconfigured to form a single direction 8-word 32-bit FIFO in a single direction. 
The buffers allow data to be passed to and from the state machines without either the system or the state machine having to wait for the other.
Each of the four state machines in the PIO has four registers associated with it:
-> CLKDIV is the clock divider, which has a 16-bit integer divider and an 8-bit fractional divider. This sets how fast the state machine runs. It divides down from the main system clock.
-> EXECCTRL holds information controlling the translation and execution of the program memory
-> SHIFTCTRL controls the arrangement and usage of the shift registers
-> PINCTRL controls which and how the GPIO pins are used.

The four state machines of a PIO have shared access to its block of 8 interrupt flags. Any state machine can use any flag. 
They can set, reset or wait for them to change. In this way they can be made to run synchronously if required. 
The lower four flags are also accessible to and from the main system, so the PIO can be controlled or pass interrupts back.
DMA can be used to pass information to and from the PIO block via its FIFO from the RP2040's memory 
A PIO has nine possible programming instructions, but there can be many variations on each one. 
For example, Mov can have up to 8 sources, 8 destinations, 3 process operations during the copy, with optional delay and/or side set operations!
-> Jmp      Jump to an absolute address in program memory if a condition is true (or instantly).
-> Wait     Stall operation of the state machine until a condition is true.
-> In       Shift a number of bits from a source into the ISR.
-> Out      Shift a number of bits out of the OSR to a destination.
-> Push     Push the contents of the ISR into the RX FIFO as a single 32-bit word.
-> Pull     Load a 32-bit word from the TX FIFO into the OSR.
-> Mov      Copy date from a source to a destination.
-> Irq      Set or clear an interrupt flag.
-> Set      Immediately write data to a destination.

Instructions are all 16-bit and contain both the instruction and all data associated with it. 
All instructions operate in 1 clock cycle, but it is possible to introduce a delay of several idle clock cycles between an instruction and the next.
Additionally, there is a facility called "side-set" which allows a value to be written to some pre-defined output pins while an instruction is being read from memory. 
This is transparent to the program.
PROGRAMMING PIO

PicoMite firmware programs the PIO state machine memory using one of 3 methods. 
Each method will be explained with an example of the exact same program that toggles one of the GPIO pins of the Raspberry Pi Pico. 
Which GPIO pin is toggled, is determined by the configuration, not in the program itself. 

PIO ASSEMBLE

This command is used to use the build in assembler to generate the program from mnemonics, then write it directly into PIO memory.
PIO ASSEMBLE 1,".program test"  'a program has to have a name
PIO ASSEMBLE 1,".line 0"        'start the program at line 0
PIO ASSEMBLE 1,"SET PINDIRS,1"  'SET the GPIO line to output
PIO ASSEMBLE 1,"label:"         'define a label called "label"
PIO ASSEMBLE 1,"SET PINS,1"     'SET the GPIO pin high
PIO ASSEMBLE 1,"SET PINS,0"     'SET the GPIO pin low
PIO ASSEMBLE 1,"JMP label"      'JuMP to "label"
PIO ASSEMBLE 1,".end program list"  'end program, list=show result

This command can be used to program 16bit values to indidual lines (locations) in the PIO memory.
PIO PROGRAM LINE 1,0,&hE081     'SET pin output
PIO PROGRAM LINE 1,1,&hE001     'SET pin high
PIO PROGRAM LINE 1,2,&hE000     'SET pin low
PIO PROGRAM LINE 1,3,&h0001     'JMP to line 1

This command writes all 32 lines in one PIO from an array. This is useful once a PIO program is debugged. It is extremely compact.
DIM a%(7)=(&h0001E0000E001E081,0,0,0,0,0,0,0)
PIO PROGRAM 1,a%()

The PicoMite firmware can configure each state machine individually. Configuration allows 2 state machines to run the exact same program lines 
(eg, an SPI interface) but operate with different GPIO pins and at different speeds. There are several configuration fields.

FREQUENCY

PicoMite firmware contains a default configuration for each configuration field, except for the frequency. 
The frequency is set by a 16 bit CLKDIV divider from the ARM clock. 
Example: when OPTION CPUSPEED 126000 is set the PIO can run at speeds between 126MHz and 1.922kHz (126000000 / 65536). 
Be aware that higher CPU speeds (overclocking) directly impact the state machine frequency.

PIN CONTROL

PicoMite firmware defaults the GPIO pins for use by MMBasic. For the PIO to take ownership of a GPIO pin MMBasic needs to assign it to PIO as below.
SETPIN GPxx,PIOx 
(eg, SETPIN gp0,pio1)

A state machine can SET the state of a pin (SET is a state machine instruction), but can also output serial data to one or more GPIO pins using the OUT instruction. 
Or read serial data using the IN instruction. And GPIO pins can be set as a side effect of any state machine instruction (SIDE SET). 
For each method of interfacing, different pins can be mapped to the state machine.It is important to understand is that these instructions work on consecutive pins. 
This means that there is a range of pins that can be controlled, starting at the lowest GPx pin number (eg, GP0), and pins next to it can be included (up to 5 pins in total). So GP0,GP1,GP2 is a valid set of IO pins. GP0,GP1,GP6 is not. 
Consider this when designing a PIO application.
Assigning GPIO pins to a state machine uses the PIO helper function PINCTRL:
PIO(PINCTRL a,b,c,d,e,f,g)
a/ the number of SIDE SET pins (0...5), SIDE SET can write 5 pins at once
b/ the number of SET pins (0...5), SET can write 5 pins at once
c/ the number of OUT pins (0...31), OUT can write 32 pins at once
d/ the lowest pin for IN pins (GP0.....GP31) IN can read up to 32 pins at once
e/ the lowest pin for SIDE SET (GP0.....GP31)
f/ the lowest pin for SET (GP0.....GP31)
g/ the lowest pin for OUT (GP0.....GP31)
Ranges for the different functions can overlap, be identical, or adjacent.

EXECUTE CONTROL

The execute control register EXECCTRL configures the program flow. 
There is a field that connects a GPIO pin to a conditional jump (JMP instruction), and fields that hold the line address of the main program loop begin (.WRAP TARGET) and end (.WRAP).
If we want the program flow to change in response of a GPIO pin state, a JMP PIN is used. 
The JMP pin is assigned in the execute control configuration (there can only be 1 pin per state machine) and the JMP happens only when the pin is high).
The state machine program starts at the beginning and runs until it reaches the end. 
In above demo program, the program loops from the end to beginning using a (unconditional) JMP instruction. 
An alternative way to using the JMP instruction is defining the beginning of the loop (WRAP TARGET = line 1) and end of the loop (WRAP = line 2) 
and configure the state machine to only execute these instructions in between. The JMP instruction in line 3 is obsolete when WRAP/WRAP TARGET is used.
PIO(EXECCTRL a,b,c)
b/ the WRAP TARGET line number (eg, 1)
c/ the WRAP line number (eg, 2)
SHIFT CONTROL

The IN and OUT instructions shift data from the FIFO register to the GPIO pins. 
In between MMBasic and the PIO, 32bit words can be communicated. Since both the ARM cores and the PIO microcontrollers operate independently, the data is exchanged through FIFO's. 
The ARM (MMBasic) puts data in the FIFO, PIO reads it. 
This uses the TX FIFO. The other way around uses the RX FIFO. The FIFO's are normally 4 words deep but can be configured to a single 8 word deep RX or TX FIFO.
The PIO can "shift" data IN the RX FIFO from the MSB side, or from the LSB side. That is set with the IN SHIFTDIR bit. 
Similar the OUT SHIFTDIR bit for OUT data. The autopull and autopush flags in combination with the pull and push thresholds determine when FIFO is replenished.
In RP2350 the FIFO's can also be used as individual registers, allowing more flexible communication between MMBasic and the state machines. 
This is achieved by FJOIN_RX_GET and FJOIN_RX_PUT in the SHIFTCTRL register.

PIO(SHIFTCTRL a,b,c,d,e,f,g,h)
a/ push threshold       (leave 0 for now)
b/ pull threshold       (leave 0 for now)
c/ autopush             (leave 0 for now)
d/ autopull             (leave 0 for now)
e/ IN-shiftdir          (1 = shift MSB, 0 = shift LSB)
f/ OUT-shiftdir         (1 = shift MSB, 0 = shift LSB)
g/ fjoin_rx             (join TX and RX fifo to 1 RX fifo)
h/ fjoin_tx             (join TX and RX fifo to 1 TX fifo)
i/ fjoin_rx_get         (1=ARM write individual FIFO registers, 2350 only)
j/ fjoin_rx_put         (1=ARM read individual FIFO registers, 2350 only)

WRITING THE STATE MACHINE CONFIGURATION

A state machine configuration is written using the command:
PIO INIT MACHINE a,b,c,d,e,f,g
a/ the PIO                      (0 or 1)
b/ the state machine number     (0...3)
c/ frequency                    (CPUSPEED/65536...CPUSPEED in Hz)
d/ pincontrol value             (PIO(PINCTRL ......))
e/ execture control value       (PIO(EXECCTRL......))
f/ shiftcontrol value           (PIO(SHIFCTRL......))
g/ start address                (0....31, the line at which the state machine starts executing,can be a label)

STARTING AND STOPPING A STATE MACHINES

Once the PIO is configured, you can start and stop the state machine using:
PIO START a,b
PIO STOP a,b
a/ the PIO number           (0 or 1)
b/ the state machine        (0...3)
Note that when stopping a state machine, it stops right where it is. To restart the state machine it is advisable to PIO INIT MACHINE first.

EXAMPLE PROGRAM 1

A complete PIO implementation that toggles a GPIO pins can be implemented in MMBasic as shown below.
Connect a buzzer to GP0, and hear the audio tone generated by the PIO.
'disconnect ARM from GP0
setpin gp0,pio1         'use GP0 as output pin for PIO 1

'pio program used
'0 E081     'SET pin output
'1 E001     'SET pin high
'2 E000     'SET pin low
'3 0001     'jmp 1

'program pio 1 using an array to write the program in PIO memory, and start

Dim a%(7)=(&h0001E000E001E081,0,0,0,0,0,0,0)
PIO program 1,a%()      'configure pio 1 statemachine 0
p=Pio(pinctrl 0,1,,,,gp0,) 'define SET uses 1 pin, and that is GP0
f=2029      '2029 Hz is lowest frequency CPUSPEED 133000
PIO init machine 1,0,f,p        'use default for execctrl, shiftctrl, start address(=0)
'start the PIO 1 state machine 0
PIO start 1,0
Entering the MMBasic editor will stop the PIO. 

FIFO's

MMBasic and the PIO exchange information using FIFO's. The PIO's PUSH data into the RX FIFO (MMBasic is the receiver), or PULL data from the TX FIFO (MMBasic is the transmitter).
When PIO is fetching data from the FIFO the data is transferred to the OSR (Output Shift Register), from there is can be processed. 
The PIO can push the data from the ISR (Input shift register) into the FIFO. Additionally, the PIO has 2 registers X and Y that can be used for storage, or counting. 
PIO cannot add or subtract or compare.

Data flow:
MMBasic -> FIFO -> OSR -> PIO (or pins)
PIO (or pins) -> ISR -> FIFO -> MMBasic

MMBasic can write data into the TX FIFO and read data from the RX FIFO using:
PIO READ a,b,c,d
PIO WRITE a,b,c,d
a/ PIO number               (0 or 1)
b/ state machine number     (0...3)
c/ number of 32 bit words   (1...4)
d/ integer variable name (i.e. variable% or array%())

PIO CLEAR 

clears all the PIO FIFO's, as does PIO START and PIO INIT MACHINE.
The MMBAsic program doesn't need to wait for data in the FIFO to appear since the RX FIFO can be assigned an interrupt. 
The MMBasic interrupt routine can fetch the data from the FIFO.
Similar for TX interrupt in which case MMBasic gets an interrupt when data is needed for the TX FIFO.

PIO INTERRUPT a,b,c,d
a/ PIO                  (0 or 1)
b/ state machine        (0...3)
c/ Name of RX interrupt handler (i.e. "myRX_Interrupt" or 0 to disable)
d/ Name of TX interrupt handler (i.e. "myTX_Interrupt" or 0 to disable)

EXAMPLE PROGRAM 2

Below program explains many of the above presented MMbasic functions and commands. The program reads a NES controller (SPI) connected to the Raspberry Pi Pico. 
The NES controller consists of a HEF4021 shift register connected to 8 push button switches.
Program uses: wrap and wrap target, IN, side set and delay, PUSH, PIO READ. GP0 and GP1 are in SET for pin direction, and in side set for compact code.

The wiring is as defined in the code:
'disconnect ARM from GP0/1/2
setpin gp0,pio1     'clock out
setpin gp1,pio1     'load out
setpin gp2,pio1     'data in
'PIO program
PIO assemble 1,".program NES"   'a program needs a name
PIO assemble 1,".side_set 2"    'use 2 bits for side set, 3 for 'delay
PIO assemble 1,".line 0"        'start code at line 0
PIO assemble 1,"SET pindirs,&b11" 'set GP0,GP1 output, side GP0,GP1 'low
PIO assemble 1,".wrap target"   'wrap target = top of the loop
PIO assemble 1,"IN null,32 side 2" 'set ISR to 0, GP1 high (load), 'GP0 low
PIO assemble 1,"SET X,7 side 0" 'set X counter to 7, GP0,GP1 low
PIO assemble 1,"loop:"
'inner loop label
PIO assemble 1,"IN pins,1 side 0"       'shift 1 databit in, keep GP0,GP1 'low
PIO assemble 1,"JMP X-- loop side 1"    'jmp to loop, dec. X, GP0 'high(clock)
PIO assemble 1,"PUSH side 0 [7]"        'now X=0, PUSH result into FIFO, 'delay 7
PIO assemble 1,".wrap"          'end outer loop, repeat
PIO assemble 1,".end program list" 'end of program, list result 

'configure pio1
p=Pio(pinctrl 2,2,,gp2,gp0,gp0,)    'GP0,GP1 out (SET and SIDE
                                    ' SET), GP2 IN
f=1e5                               '100kHz
s=PIO(shiftctrl 0,0,0,0,0,0)        'shift in from LSB for IN (and
                                    'OUT)
e=PIO(execctrl gp0,PIO(.wrap target),PIO(.wrap) 'wrap and wrap target
                                    'write the configuration
PIO init machine 1,0,f,p,e,s,0      

'start the pio1 code
PIO start 1,0
'Check the the read data in MMBasic and print
dim d%
do
pio read 1,0,1,d%
print bin$(d%)
pause 200
loop
END

The way that DMA works is as follows:
When reading from the FIFO the DMA controller waits on data being in the FIFO and when it appears transfers that data into processor memory. 
Each time it reads it increments the pointer into the processor memory so that it can, for example, incrementally fill an array as each and every data item is made available.
When writing to the FIFO the DMA controller writes data from processor memory to the FIFO automatically waiting whenever the FIFO is full. 
Thus, data can be prepared in an array and the DMA controller will stream that data to the PIO FIFO as fast as the PIO program requires it.
DMA can transfer a 32-bit word, a 16-bit short, or an 8-bit byte and when setting up DMA you need to tell it the size of the tranfer and how many transfers to make. 
Because each transfer will increment the memory pointer by 1,2, or 4 bytes MMBasic must deal with the data packed into memory rather than the 64-bits used for MMbasic integers and floats. 
Luckily MMBasic implements two commands MEMORY PACK and MEMORY UNPACK to do this very efficiently but it could equally be done using standard BASIC arithmetic.
The DMA can be configured to repeatedly loop data into or out of a section of memory (a ring buffer) 

The commands are:
PIO DMA_IN a,b,c,d,e,f,g
PIO DMA_OUT a,b,c,d,e,f,g
a/ pio              (0 or 1)
b/ state machine    (0...3)
c/ nbr              (number of words to be transferred)
d/ data%()          (interger array name)
e/ completioninterrupt  (where to go when done, optional)
f/ transfersize     (8/16/32, optional)
g/ loopbackcount    (used data%() as a ring buffer, optional, loopbackcount = 2^n)
The DMA will start the state machine automatically and there is no need for a PIO START command. 
But,before starting the transfer make sure a fresh PIO INIT MACHINE is done, so the state machine starts at the required start address.

When a ring buffer is used, it requires special preparation:
PIO MAKE RING BUFFER a,b
a/ name of integer buffer
b/ size of the array in bytes

Example:
DIM packed%
PIO MAKE RING BUFFER packed%,4096
packed% will then be an integer array holding 4096/8=512 integers
This can then be used by the DMA for a loopbackcounter with DMA of 1024 32-bit words, 2048 16-bit shorts
or 4096 8-bit bytes

EXAMPLE PROGRAM 3

This program brings everything together and uses DMA to read 128 samples from the PIO RX FIFO. 
For the demonstration, GP0 to GP5 are outputs of 3 PWMS, and are ,at the same time, sampled by the PIO as a 6 channel logic analyser or oscilloscope. 
The 128 samples are sent to the serial port as waveforms. 
This Logic Analyzer program also demonstrates PIO DMA RX, MEMORY UNPACK, the use of buffers. 
It uses PWM's to generate a test signal on gp0..gp5 for demo purpose. 
These same pins are read by the logic analyzer and output to the console.
To use this Logic Analyzer normally comment out first 14 lines.
'generate a 50Hz 3 phase test signal to demonstrate the DMA on 6 GPIO pins.
SetPin gp0,pwm          'CH 0a
SetPin gp1,pwm          'CH 0b
SetPin gp2,pwm          'CH 1a
SetPin gp3,pwm          'CH 1b
SetPin gp4,pwm          'CH 2a
SetPin gp5,pwm          'CH 2b
Fpwm = 50: PW = 100 / 3
PWM 0, Fpwm, PW, PW - 100, 1, 1
PWM 1, Fpwm, PW, PW - 100, 1, 1
PWM 2, Fpwm, PW, PW - 100, 1, 1
PWM sync 0, 100/3, 200/3

'------------------------------- LA code PIO --------------------------
'PIO code to sample GP0..GP6 as elementary logic analyser
PIO clear 1
'in this program the PIO reads GP0..GP5 brute force
'and pushes data into FIFO. The clock speed determines the
'sampling rate. There are 2 instructions per cycle
'taking 10000/2 / 50 = 100 samples per 50Hz cycle.
PIO assemble 1,".program push"
PIO assemble 1,".line 0"
PIO assemble 1,".wrap target"
PIO assemble 1,"IN pins,6"
'get 6 bits from GP0..GP5
PIO assemble 1,"PUSH block"
'push data when FIFO has room
PIO assemble 1,".wrap"
PIO assemble 1,".end program list"
'configuration
f=1e4
'PIO run at 10kHz
p=Pio(pinctrl 0,0,0,gp0,,,)
'IN base = GP0
e=Pio(execctrl gp0,PIO(.wrap target),PIO(.wrap)) 'wrap 1 to 0, gp0 is
'default
s=Pio(shiftctrl 0,0,0,0,0,0) 'shift in through LSB, out is not used
'write the configuration, speed 10kHz (data in FIFO 10us after edge GP0)
PIO init machine 1,0,f,p,e,s,0
'start address = 0
'-------------------------- LA code MMBasic ----------------------------
'define memory buffers
Dim a$(1)=("_","-")
'characters for the printout
length%=64
'size of the packed array
Dim data%(2*length%-1)
'array to put the 32 bit samples
'FIFO format
Dim packed%(length%-1)
'DMA array to pack 32 bit samples 'in 64
bit integers
'let the DMA machine run, and repeat at will
Do
PIO DMA RX 1,0,2*length%,packed%(),ReadyInt
print "press any key to restart sampling"
do:loop while inkey$=""
Loop
End
'--------------------------------SUBS MMBasic --------------------------
Sub ReadyInt
'stop the PIO and re-init for next run
PIO stop 1,0
PIO init machine 1,0,f,p,e,s,0
'start address = 0
'get the data from the packed DMA buffer and unpack to original 32 bit 'format
Memory unpack packed%(),data%(),2*length%,32
'Serial output as if logic analyzer traces
For j=0 To 5
mask%=2^j
For i=0 To 2*length%-1
If i<106 Then Print a$(((data%(i) And mask%)=mask%));
Next i
Print : Print
Next j
End Sub

This program runs on RP2350 only and demonstrates the use of the FIFO's as individual registers. 
The PIO of the RP2350 has specific instructions to support this MOV RXFIFO[y],ISR and MOV OSR,RXFIFO[y].
MMBasic can read/write the 4 individual FIFO registers by use of:
PIO WRITEFIFO a, b, c, d
PIO READFIFO( a, b, c)
a/ pio              (0 or 1)
b/ state machine    (0...3)
c/ nbr              (FIFO register 0...3)
d/ data%            (32 bit integer value)

The program configures the FIFO for individual reads, then writes some values in these register. 
It starts the PIO that updates 2 of the 4 individual FIFO registers. Then MMBasic reads the values to show only 2 register have changes, and no data shifted (as would happen in RP2040 FIFO).
'only for RP2350 assembler. this does not work on RP2040
pio clear 1
pio assemble 1,".program test"
pio assemble 1,".line 0"
pio assemble 1,"set y,4" 'just a value 4 in Y
pio assemble 1,"mov isr,y" 'copy Y to isr
pio assemble 1,"jmp y--,next" 'y=y-1 always to next
pio assemble 1,"next:"
'label
pio assemble 1,"mov rxfifo[y],isr" 'should program 4 in fifo [3]
pio assemble 1,"mov isr,y" 'copy Y to isr
pio assemble 1,"mov rxfifo[2],isr" 'should program 3 in fifo [2]
pio assemble 1,"jmp 0" 'repeat
pio assemble 1,".end program"
f=1e6 '1MHz
'PIO(EXECCTRL a,b,c)
e=pio(execctrl gp0,0,31)
'default value, not actually changed
'PIO(SHIFTCTRL a,b,c,d,e,f,g,h,i,j)
sr=pio(shiftctrl 0,0,0,0,0,0,0,0,0,1) 'read individual RX, RX=4 deep
sw=pio(shiftctrl 0,0,0,0,0,0,0,0,1,0) 'write individual RX, RX=4 deep
'PIO(PINCTRL a,b,c,d,e,f,g)
p=pio(pinctrl 0,0,0,gp0,gp0,gp0,gp0) 'defaultvalue , not actually changed
'test the use of FIFO as individual registers
'fill the fifo with pre-determined values
pio init machine 1,0,f,p,e,sw,0 'init machine for writing to RX fifo
for i=0 to &h3 'write the 4 RXFIFO registers
pio writefifo 1,0,i,&h100*(i+1) 'write values &h100, &h200, &h300, &h400 
next
'check the values are written correctly
print "3 RXFIFO registers before running the program"
pio init machine 1,0,f,p,e,sr,0 'init machine for reading the RX fifo
for i=0 to 3
'read the 4 RXFIFO registers
print i,hex$(pio(readfifo 1,0,i)) 'verify they are correctly written
next
print
'run the PIO program. That should (continuously) write to reg 2 and 3 in the FIFO,
but not alter register 0 and 1
pio start 1,0
'show the updated FIFO registers
print "3 RXFIFO registers after running the program"
for i=0 to 3
'read the 4 FIFO registers to see if the program works
print i,hex$(pio(readfifo 1,0,i))
next
The expected output is:
3 RXFIFO registers before running the program
0 100
1 200
2 300
3 400
3 RXFIFO registers after running the program
0 100
1 200
2 3
3 4
~PIO assemble pio,linedata$,PIO,HARDWARE,*
------------------------------------
PIO assemble pio,linedata$
This command will assemble and load text based PIO assembler code including labels for jumps 
Use: PIO assemble pio,".program anything" to initialise the assembler 
Use: PIO assemble pio,".side_set n [opt] [pindirs]" if using side set. 
This is mandatory in order to correctly construct the op-codes if one or more side set pins are used. 
It does not load the pinctrl register as this is specific to the state-machine. 
Also note the "opt" parameter changes the op-code on instructions that have a side parameter 

Use: PIO assemble pio,".line n" to assemble starting from a line other than 1 - this is optional 
Use: PIO assemble pio,".end program [list]" to terminate the assembly and program the pio. 
The optional parameter LIST causes a hex dump of the op-codes to the terminal. 
Use: PIO assemble pio,"label:" to define a label. This must appear as a separate command.
Use: PIO assemble "'.wrap target" to specify where the program will wrap to. See PIO(.wrap target) for how to use this.
Use: PIO assemble ".wrap" to specify where the program should wrap back to from ".wrap target" . See PIO(.wrap) for how to use this. 
Use: PIO assemble pio "instruction [parameters]" to define the actual PIO instructions that will be converted to machine code
~PIO DMA RX pio, sm, nbr, data%() [,completioninterrupt] [,transfersize] [,loopbackcount] or PIO DMA TX pio, sm, nbr, data%() [,completioninterrupt] [,transfersize] [,loopbackcount],PIO,HARDWARE,*
------------------------------------
PIO DMA RX pio, sm, nbr, data%() [,completioninterrupt] [,transfersize] [,loopbackcount] or PIO DMA TX pio, sm, nbr, data%() [,completioninterrupt] [,transfersize] [,loopbackcount]
Sets up DMA transfers from PIO to MMBasic memory pio specifies which of the two pio instances to use (0 or 1 sm specifies which of the state machine to use (0-3) nbr specifies how many 32-bit words to transfer. 
See below for the special case of setting nbr to zero. 
data%() is the array that will either supply or receive the PIO data The optional parameter completioninterrupt is the name of a MMBasic subroutine rthat will be called when the DMA completes and in the case of DMA_OUT the FIFO has been emptied. 
If the optional interrupt is not used then the status of the DMA can be checked using the functions 
MM.INFO(PIO RX DMA) 
MM.INFO(PIO TX DMA) 
The optional parameter transfersize allows the user to override the normal 32-bit transfers and select 8, 16, or 32. 
The optional parameter loopbackcount specifies how many data items are to be read or written before the DMA starts again at the beginning of the buffer 
The parameter must be a power of 2 between 2 and 32768 Due to a limitation in the RP2040/RP2350 if loopbackcount is used the MMBasic array must be aligned in memory to the number of bytes in the loop Thus if the array is 64 integers long 
which is 512 bytes then the array must be aligned to a 512byte boundary in memory All MMBasic arrays are aligned to a 256 byte boundary but to create an array which is guaranteed to be aligned 
to a 512 byte boundary or greater the PIO MAKE RING BUFFER command must be used 
If loopbackcount is set then "nbr" can be set to 0. In this case the transfer will run continuously repeatedly filling the buffer until explicitly stopped
~PIO DMA RX OFF,PIO,HARDWARE,*
------------------------------------
PIO DMA RX OFF
PIO DMA TX OFF
Aborts a running DMA
~PIO INTERRUPT pio, sm [,RXinterrupt] [,TXinterrupt],PIO,HARDWARE,*
------------------------------------
PIO INTERRUPT pio, sm [,RXinterrupt] [,TXinterrupt]
Sets Basic interrupts for PIO activity. 
Use the value 0 for RXinterrupt or TXinterrupt to disable an interrupt Omit values not needed The RX interrupt triggers whenever a word has been "pushed" by the PIO code into the specified FIFO. 
The data MUST be read in the interrupt to clear it. 
The TX interrupt triggers whenever the specified FIFO has been FULL and the PIO code has now "pulled" it
~PIO INIT MACHINE pio%, statemachine%, clockspeed [,pinctrl] [,execctrl] [,shiftctrl] [,startinstruction],PIO,HARDWARE,*
------------------------------------
PIO INIT MACHINE pio%, statemachine%, clockspeed [,pinctrl] [,execctrl] [,shiftctrl] [,startinstruction]
PIO interrupts have priority have keyboard interrupts but before anything else. 
As with all interrupts interrupt conditions are processed one at a time. 
Initialises PIO 'pio%' with state machine 'statemachine%'. 'clockspeed' is the clock speed of the state machine in kHz. 
The four optional arguments are variables holding initialising values of the state machine registers and the address of the first instruction to execute (defaults to zero). 
These decide how the PIO will operate. It is anticipated that eventually the PIO assembler will be able to generate the register values for the user along with the program array based on the defined assembler directives.
~PIO INIT MACHINE pio%,statemachine%, clockspeed[,pinctrl] [,execctrl] [,shiftctrl][,startinstruction] [,sideout [,setout] [,outout],PIO,HARDWARE,BETA,*
------------------------------------
PIO INIT MACHINE pio%,statemachine%, clockspeed[,pinctrl] [,execctrl] [,shiftctrl][,startinstruction] [,sideout [,setout] [,outout]
See PIO INIT MACHINE pio%, statemachine%, clockspeed [,pinctrl] [,execctrl] [,shiftctrl] [,startinstruction] for more detailed info.

Sideout, setout, and outout can be set to 0 (default) or 1 to specify if pins defined in pinctrl should be initialised as inputs (0) or outputs (1)
BETA 6.02 ONLY!
~PIO EXECUTE pio, state_machine, instruction%,PIO,HARDWARE,*
------------------------------------
PIO EXECUTE pio, state_machine, instruction%
Immediately executes the instruction on the pio and state machine specified.
~PIO WRITE pio, state_machine, count, data0 [,data1..] ,PIO,HARDWARE,*
------------------------------------
PIO WRITE pio, state_machine, count, data0 [,data1..] 
Writes the data elements to the pio and state machine specified. 
The write is blocking so the state machine needs to be able to take the data supplied 
NB: this command will probably need additional capability in future releases
~PIO WRITEFIFO a,b,v,d,PIO,HARDWARE,*
------------------------------------
PIO WRITEFIFO a,b,v,d
Writes to one of the 4 individual FIFO registers. 
'a' is the pio (0 or 1), 'b' id the state machine (0...3), 'c' is the FIFO register *0...3), 
'd' is the data% (32 bit integer value)
~PIO READ pio, state_machine, count, data%[()],PIO,HARDWARE,*
------------------------------------
PIO READ pio, state_machine, count, data%[()]
Reads the data elements from the pio and state machine specified. 
The read Is non-blocking so the state machine needs to be able to supply the data requested. 
When count is one then an integer can be used to receive the data, otherwise and integer array should be specified. 
NB: this command will probably need additional capability in future releases
~PIO START pio, statemachine ,PIO,HARDWARE,*
------------------------------------
PIO START pio, statemachine 
Start a given state machine on pio
~PIO STOP pio, statemachine  ,PIO,HARDWARE,*
------------------------------------
PIO STOP pio, statemachine  
Stop a given state machine on pio
~PIO CLEAR pio ,PIO,HARDWARE,*
------------------------------------
PIO CLEAR pio 
This stops the pio specified on all statemachines and clears the control registers 
for the statemachines PINCTRL, EXECTRL, and SHIFTCTRL to defaults 
~PIO PROGRAM LINE pio, line, instruction ,PIO,HARDWARE,*
------------------------------------
PIO PROGRAM LINE pio, line, instruction 
Programs just the specified line in a PIO program
~PIXEL x, y [,c] ,DRAWING,GRAPHIC,*
------------------------------------
PIXEL x, y [,c] 
Set a pixel on the VGA screen to a colour. 
'x' is the horizontal coordinate and 'y' is the vertical coordinate of the pixel.
'c' is a 24 bit number specifying the colour. 
'c' is optional and if omitted the current foreground colour will be used.
All parameters can be expressed as arrays and the software will plot the number of pixels as determined by the dimensions of  the smallest array.
'x' and 'y' must both be arrays or both be single variables /constants otherwise an error will be generated.
'c' can be either an array or a  single variable or constant.
See the section Graphics Commands and Functions for a definition of the colours and graphics coordinates. 
~Sound Output ,AUDIO,SOUND,*
------------------------------------
Sound Output 
The PicoMite firmware can play stereo WAV, FLAC, MP3 or MOD files located on the Flash Filesystem or SD Card and generate precise sine waves using the PLAY command.
Note that the switching power regulator on the Raspberry Pi Pico can cause some noise on the audio output.

This can be reduced by disabling the regulator and powering the module via an external linear regulator.
Pulse Width Modulated (PWM) Signal The audio is created using PWM outputs so before the PLAY commands can be used the PWM output pins must be allocated as audio outputs:
This is done using the OPTION AUDIO command as follows:
OPTION AUDIO PWM-A-PIN, PWM-B-PIN
This command should be entered at the command prompt and will be saved, so it only needs to be run once.
Both pins must be on the same PWM channel with PWM-A-PIN the left audio channel and PWM-B-PIN the right.
For example:
OPTION AUDIO GP0, GP1
The audio signal is superimposed on a 44KHz square wave (called the carrier wave) as a pulse width modulated (PWM) signal. 
This means that a low pass filter is required to remove the carrier and recover the audio signal. 
FILTER CIRCUITS

Most low cost amplified speakers (for a personal computer) will not respond to the carrier frequency so they will act as a low pass filter in themselves. 
Therefore, if you want to keep it simple, you can directly connect the PWM output to an amplified speaker's input and a reasonable sound output should be achieved.
However, the high level of the 44KHz carrier may cause problems for the amplifier (eg, overheating or distortion) so the following filter is recommended. 
This removes most of the carrier and delivers about 2V peak to peak (0.6V RMS) with reasonable fidelity up to 8KHz (more than enough for most amplified speakers):

VS1053 support

The audio output can be generated using a VS1053 CODEC module which is configured using the command OPTION AUDIO VS1053 CLKpin, MOSIpin, MISOpin, XCSpin, XDCSpin, DREQpin,XRSTpin
This requires no output filtering and can drive 32Ω headphones direct. It also supports additional playback capabilities.
If a VS1053 codec is used as the audio output device, additional commands are available:
PLAY MP3 file$, interrupt
PLAY MIDIFILE file$, interrupt
PLAY MIDI
PLAY MIDI CMD cmd%, data1% [,data2%]
PLAY NOTE ON channel, note, velocity
PLAY NOTE OFF channel, note [,velocity]
PLAY HALT
PLAY CONTINUE track$
PLAY STREAM buffer%(), readpointer%, writepointer%
These are explained in more detail in the commands listing section.

MCP48n2 DAC

The audio output can also be generated through a connected MCP48n2 DAC (eg, MCP4822) in which case it is configured using the command:
OPTION AUDIO SPI CS-PIN, CLK-PIN, MOSI-PIN
The DAC does not need a complex low pass filter and a 120Ω resistor connected to the DAC output with the other end of the resistor connected to GND via a 100nF capacitor will be adequate. 
When a MCP4822 is used the LDAC pin on the DAC should be connected to GND.

Playing WAV, FLAC, MP3 and MOD Files

The PLAY command can play a WAV, FLAC, MP3 (RP2350 only) or MOD file residing on the Flash Filesystem or SD Card to the sound output. 
It can be used to provide background music, add sound effects to programs and provide informative announcements.

The commands are:
PLAY WAV file$, interrupt
PLAY FLAC file$, interrupt
PLAY MODFILE file$, interrupt
PLAY MP3 file$, interrupt 'RP2350 only
'file$’ is the name of the audio file to play. 

It must be on the Flash Filesystem or SD Card and the appropriate extension (eg .WAV) will be appended if missing. 
The audio will play in the background (ie, the program will continue without pause). 
'interrupt' is optional and is the name of a subroutine which will be called when the file has finished playing.

Generating Sine Waves

The PLAY TONE command uses the audio output to generate sine waves with selectable frequencies for the left and right channels. 
This feature is intended for generating attention catching sounds but, because the frequency is very accurate, it can be used for many other applications. 
For example, signalling DTMF tones down a telephone line or testing the frequency response of loudspeakers.

The syntax of the command is:
PLAY TONE left, right, duration, interrupt
'left' and 'right' are the frequencies in Hz to use for the left and right channels.
The tone plays in the background (the program will continue running after this command) and 'duration' specifies the number of milliseconds that the tone will sound for. 
'duration' is optional and if not specified the tone will continue until explicitly stopped or the program terminates. 
'interrupt' (if specified) will be triggered when the duration has finished.
The specified frequency can be from 1 Hz to 20 KHz and is very accurate (it is based on a crystal oscillator).
The frequency can be changed at any time by issuing a new PLAY TONE command.
Using PLAY

It is important to realise that the PLAY command will generate the audio in the background. This allows a program (for example) to play the sound of a bell while continuing with its control function. 
Without the background facility the whole BASIC program would freeze while the sound was played. However, generating the audio in the background has some subtle inferences which can trip up newcomers.
For example, take the following program:
PLAY TONE 500, 500, 2000
END
You may expect the 500Hz tone to sound for 2 seconds but in practice it will not make any sound at all. 
This is because MMBasic will execute the PLAY TONE command (which will start generating the sound in the background) and then it will immediately execute the END command which will terminate the program and the background sound. 
This will happen so fast that nothing is heard. Similarly the following program will not work either:
PLAY TONE 500, 500, 2000
PLAY TONE 300, 300, 5000
This is because the first command will set a 500Hz the tone playing but then the second PLAY command will immediately replace that with a 300Hz tone 
and following that the program will run off the end terminating the program (and the background audio), resulting in nothing being heard.
If you want MMBasic to wait while the PLAY command is doing its thing you should use suitable PAUSE commands. For example:
PLAY TONE 500, 500
PAUSE 2000
PLAY TONE 300, 300
PAUSE 5000
This applies to all versions of the PLAY command including PLAY WAV. 

UTILITY COMMANDS 

There are a number of commands that can be used to manage the sound output:

PLAY PAUSE      Temporarily halt (pause) the currently playing file or tone.
PLAY RESUME     Resume playing a file or tone that was previously paused.
PLAY NEXT       Play the next WAV or FLAC file in a directory
PLAY PREVIOUS   Play the previous WAV or FLAC file in a directory
PLAY STOP       Terminate the playing of the file or tone. The sound output 
                will also be automatically stopped when the program ends.
PLAY VOLUME L,R Set the volume to between 0 and 100 with 100 being the maximum volume. 
                The volume will reset to the maximum level when a program is run. 
                A logarithmic scale is used so that 
                PLAY VOLUME 50,50 should sound half as loud as 100,100.

The PLAY SOUND command will generate an output based on a mixture of sine, square, etc waveforms. See the details in the command listing.
~PLAY ,AUDIO,*
------------------------------------
PLAY 
This command will generate a variety of audio outputs. See the OPTION AUDIO command for setting the I/O pins to be used for the output. 
The audio is a pulse width modulated signal so a low pass filter is required to remove the carrier frequency. 
~PLAY TONE left [, right [, dur] [,interrupt]]] ,AUDIO,*
------------------------------------
PLAY TONE left [, right [, dur] [,interrupt]]] 
Generates two separate frequencies on the sound output left and right channels. 
'left' and 'right' are the frequencies in Hz to use for the left and right channels. 
The tone plays in the background (the program will continue running after this command) 
and 'dur' specifies the number of milliseconds that the tone will sound for. 
If the duration is not specified the tone will continue until explicitly stopped or the program terminates. 
'interrupt' is an optional subroutine which will be called when the play terminates. 
The frequency can be from 1Hz to 20KHz and is very accurate (it is based on a crystal oscillator). 
The frequency can be changed at any time by issuing a new PLAY TONE command.
~PLAY FLAC file$ [, interrupt],AUDIO,*
------------------------------------
PLAY FLAC file$ [, interrupt]
Will play a FLAC file on the sound output. 
'file$' is the FLAC file to play (the extension of .flac will be appended if missing). 
The sample rate can be up to 48kHz in stereo (96kHz if the Pico is overclocked) The FLAC file is played in the background. 
'interrupt' is optional and is the name of a subroutine which will be called when the file has finished playing. 
If file$ is a directory the Pico will play all of the files in that directory in turn.
~PLAY WAV file$ [, interrupt] ,AUDIO,*
------------------------------------
PLAY WAV file$ [, interrupt] 
Will play a WAV file on the sound output. 
'file$' is the WAV file to play (the extension of .wav will be appended if missing). 
The WAV file must be PCM encoded in mono or stereo with 8 or 16-bit sampling. 
The sample rate can be up to 48kHz in stereo (96kHz if the Pico is overclocked). The WAV file is played in the background. 
'interrupt' is optional and is the name of a subroutine which will be called when the file has finished playing.
~PLAY MP3 file$ [, interrupt],AUDIO,*
------------------------------------
PLAY MP3 file$ [, interrupt]
Will play a MP3 file on the sound output (RP2350 ONLY). 
'file$' is the MP3file to play (the extension of .mp3 will be appended if missing). 
The sample rate can be up to 48kHz. The MP3 file is played in the background. 
'interrupt' is optional and is the name of a subroutine which will be called when the file has finished playing. 
If file$ is a directory the Pico will play all of the files in that directory in turn.
~PLAY MODFILE file$ [,interrupt],AUDIO,BETA,*
------------------------------------
PLAY MODFILE file$ [,interrupt]
Will play a MOD file on the sound output. 
'file$' is the MOD file to play (the extension of .mod will be appended if missing). 
The MOD file is played in the background and will play continuously in a loop. 
If the optional 'interrupt' is specified 
This will be called when the file has played once through the sequence and playback will then be  terminated.

BETA 6.02 ONLY!
PLAY MODFILE will preferentially use space in PSRAM if enabled for the file buffer (RP2350 only). In this case a modbuffer does not need to be enabled with the OPTION command
~PLAY MODSAMPLE Samplenum, channel [,volume],AUDIO,*
------------------------------------
PLAY MODSAMPLE Samplenum, channel [,volume]
Plays a specific sample in the mod file on the channel specified. The volume is optional and can be between 0 and 64. 
This command can only be used when there is a mod file already playing and allows sound effects to be output whilst the background music is still playing.
~PLAY LOAD SOUND array%(),AUDIO,*
------------------------------------
PLAY LOAD SOUND array%()
Loads a 1024 element array comprising 4096 16-bit values between 0 and 4095. 
This provides the data for any arbitrary waveform that can be played by the PLAY SOUND command. 
You can use the MEMORY PACK command to create the arrays from a normal 40956 element integer array.
~PLAY SOUND soundno, channelno, type [,frequency] [,volume] ,AUDIO,*
------------------------------------
PLAY SOUND soundno, channelno, type [,frequency] [,volume] 
Play a series of sounds simultaneously on the audio output. 'soundno' is the sound number 
and can be from 1 to 4 allowing for four simultaneous sounds on each channel. 
'channelno' specifies the output channel. It can be L (left speaker), R (right speaker) or B (both speakers) 
'type' is the type of waveform. It can be S (sine wave), Q (square wave), T (triangle wave), W (rising sawtooth) or O (turn off sound). 
'frequency' is the frequency from 1 to 20000 (Hz) and it must be specified except when type is O. 
'volume' is optional and must be between 1 and 25. It defaults to 25 
The first time PLAY SOUND is called all other audio usage will be blocked and will remain blocked until PLAY STOP is called. 
Output can be stopped  temporarily using PLAY PAUSE and PLAY RESUME. 
Calling SOUND on an already running 'soundno' will immediately replace  the previous output. 
Individual sounds are turned off using type "O" 
Running 4 sounds simultaneously on both channels of the audio output consumes about 23% of the CPU. 
~PLAY PAUSE ,AUDIO,*
------------------------------------
PLAY PAUSE 
Will temporarily halt the currently playing file or tone. 
~PLAY RESUME ,AUDIO,*
------------------------------------
PLAY RESUME 
Will resume playing a sound that was paused. 
~PLAY STOP ,AUDIO,*
------------------------------------
PLAY STOP 
Will terminate the playing of the file or tone. 
When the program terminates for whatever reason the sound output will also be automatically stopped. 
~PLAY VOLUME left, right ,AUDIO,*
------------------------------------
PLAY VOLUME left, right 
Will adjust the volume of the audio output. 
'left' and 'right' are the levels to use for the left and right channels and can be between 0 and 100 with 100 being the maximum volume. 
There is a linear relationship between the specified level and the output. 
The volume defaults to maximum when a program is run. 
~PLAY NEXT ,AUDIO,*
------------------------------------
PLAY NEXT 
Stops playback of the current audio file and starts the next one in the directory
~PLAY PREVIOUS ,AUDIO,*
------------------------------------
PLAY PREVIOUS 
Stops playback of the current audio file and starts the previous one in the directory
~PLAY MP3 file$ [, interrupt] (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY MP3 file$ [, interrupt] (VS1053 specific)
Will play a MP3 file on the sound output. 
'file$' is the MP3 file to play (the extension of .mp3 will be appended if missing). 
The sample rate should be 44100Hz stereo. The MP3 file is played in the background. 
'interrupt' is optional and is the name of a subroutine which will be called when the file has finished playing. 
If file$ is a directory the Pico will play all of the files in that directory in turn. 
~PLAY HALT (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY HALT (VS1053 specific)
This command works when a MP3 file is playing. 
It stops playback and records the current file position to allow playback to be resumed from the same point. 
This command is specifically designed to support for mp3 audio books
~PLAY CONTINUE track$ (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY CONTINUE track$ (VS1053 specific)
Resumes playback of the MP3 track specified. 
"track$" will be the name of the file that was playing when halted with all file attributes removed eg, 
PLAY MP3 "B:/mp3/mymp3.mp3" 
sometime later 
PLAY HALT 
later again 
PLAY CONTINUE "mymp3"
~PLAY MIDIFILE file$ [,interrupt] (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY MIDIFILE file$ [,interrupt] (VS1053 specific)
The MIDI file is played in the background. 
'interrupt' is optional and is the name of a subroutine which will be called when the file has finished playing. 
If file$ is a directory the Pico will play all of the files in that directory in turn.
~PLAY MIDI (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY MIDI (VS1053 specific)
Initiates the real-time midi mode. 
In this mode midi instructions can be sent to the VS1053 to select 
which instruments to play on which channels, turn notes on, and turn them off in real timer
~PLAY MIDI CMD cmd%,data1%, data2% (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY MIDI CMD cmd%,data1%, data2% (VS1053 specific)
Sends a midi command when in real time midi mode. 
An example would be to allocate an instrument to a channel. Eg, PLAY MIDI CMD &B11000001,4 'set channel 1 to instrument 4
~PLAY MIDI TEST n (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY MIDI TEST n (VS1053 specific)
Plays a MIDI test sequence, n=0 to 3, 0 = normal realtime, the others play note and instrument samples
~PLAY NOTE ON channel%, note%, velocity% (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY NOTE ON channel%, note%, velocity% (VS1053 specific)
Turns on the note on the channel specified when in real time MIDI mode
~PLAY NOTE OFF channel%, note% [,velocity%] (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY NOTE OFF channel%, note% [,velocity%] (VS1053 specific)
Turns off the note on the channel specified when in real time MIDI mode
~PLAY STREAM buffer%(),readpointer%, writepointer%  (VS1053 specific),AUDIO,VS1053,*
------------------------------------
PLAY STREAM buffer%(),readpointer%, writepointer%  (VS1053 specific)
Sends data to the VS1053 CODEC from the circular buffer "buffer%". 
This command initiates a background output stream where the VS1053 is sent anything in the buffer 
between the readpointer and the write pointer, updating the readpointer as it goes. 
Can be used for arbitrary waveform output.
~POKE BYTE addr%, byte or POKE SHORT addr%, short% or POKE WORD addr%, word% or POKE INTEGER addr%, int% or POKE FLOAT addr%, float!  or POKE VAR var, offset, byte or POKE VARTBL, offset, byte,MEMORY,*
------------------------------------
POKE BYTE addr%, byte or POKE SHORT addr%, short% or POKE WORD addr%, word% or POKE INTEGER addr%, int% or POKE FLOAT addr%, float!  or POKE VAR var, offset, byte or POKE VARTBL, offset, byte

Will set a byte or a word within the virtual memory space.
POKE BYTE will set the byte (i.e. 8 bits) at the memory location 'addr%' to 'byte'. 'addr%' should be an integer.
POKE SHORT will set the short integer (i.e. 16 bits) at the memory location 'addr%' to 'word%'. 'addr%' and short%' should be integers.
POKE WORD will set the word (i.e. 32 bits) at the memory location 'addr%' to 'word%'. 'addr%' and 'word%' should be integers.
POKE INTEGER will set the MMBasic integer (i.e. 64 bits) at the memory location 'addr%' to int%'. 'addr%' and int%' should be integers.
POKE FLOAT will set the word (i.e. 32 bits) at the memory location 'addr%' to 'float!'. 'addr%' should be an integer and 'float!' a floating point number.
POKE VAR will set a byte in the memory address of 'var'. 'offset' is the ±offset from the address of the variable. An array is specified as var().
POKE VARTBL will set a byte in MMBasic's variable table. 'offset' is the ±offset from the start of the variable table. 
Note that a comma is required after the keyword VARTBL.
~POKE DISPLAY command [,data1] [,data2] [,datan],MEMORY,GRAPHIC,LCD,*
------------------------------------
POKE DISPLAY command [,data1] [,data2] [,datan]
This command sends commands and associated data to the display controller for a connected display. 
This allows the programmer to change parameters of how the display is configured. 
eg, POKE DISPLAY &H28 will turn off an SSD1963 display and POKE DISPLAY &H29 will turn it back on again. 
Works for all displays except the ST7790.
~POKE DISPLAY HRES n. (or VRES n),MEMORY,GRAPHIC,LCD,*
------------------------------------
POKE DISPLAY HRES n. (or VRES n)
POKE DISPLAY HRES n
POKE DISPLAY VRES n
These commands change the stored value of MM.HRES and MM.VRES allowing the programmer to configure non-standard displays.
~POLYGON (polygon drawing commands),DRAWING,GRAPHIC,*
------------------------------------
POLYGON (polygon drawing commands)
POLYGON n, xarray%(), yarray%() [, bordercolour] [, fillcolour] 
POLYGON n(), xarray%(), yarray%() [, bordercolour()] [, fillcolour()] 
POLYGON n(), xarray%(), yarray%() [, bordercolour] [, fillcolour] 
Draws a filled or outline polygon with n xy-coordinate pairs in xarray%() and yarray%(). 
If 'fillcolour' is omitted then just the polygon outline is drawn. 
If 'bordercolour' is omitted then it will default to the current default foreground colour. 
If the last xy-coordinate pair is not the same as the first the firmware will automatically create an additional xy-coordinate pair to complete the polygon. 
The size of the arrays should be at least as big as the number of x,y coordinate pairs. 
'n' can be an array and the colours can also optionally be arrays as follows: 
<CPDE>
POLYGON n(), xarray%(), yarray%() [, bordercolour()] [, fillcolour()] 
POLYGON n(), xarray%(), yarray%() [, bordercolour] [, fillcolour] 
The elements of array n() define the number of xy-coordinate pairs in each of the polygons. e.g. 
DIM n(1)=(3,3) would define that 2 polygons are to be drawn with three vertices each. 
The size of the n array determines the number of polygons that will be drawn unless an element is found with the value zero in which case the firmware only processes polygons up to that point. 
The x,y-coordinate pairs for all the polygons are stored in xarray%() and yarray%(). The xarray%() and yarray%() parameters must have at least as many elements as the total of the values in the n array. 
Each polygon can be closed with the first and last elements the same. If the last element is not the same as the first the firmware will automatically create an additional x,y-coordinate pair to complete the polygon. 
If fill colour is omitted then just the polygon outlines are drawn. The colour parameters can be a single value in which case all polygons are drawn in the same colour or they can be arrays with the same cardinality as n. 
In this case each polygon drawn can have a different colour of both border and/or fill. For example, this will draw 3 triangles in yellow, green and red: 
DIM c%(2)=(3,3,3) 
DIM x%(8)=(100,50,150,100,50,150,100,50,150) 
DIM y%(8)=(50,100,100,150,200,200,250,300,300) 
DIM fc%(2)=(rgb(yellow),rgb(green),rgb(red))
POLYGON c%(),x%(),y%(),fc%(),fc%() 
~PORT(start, nbr [,start, nbr]...) = value ,PIN,HARDWARE,PORT,*
------------------------------------
PORT(start, nbr [,start, nbr]...) = value 
'start' is an I/O pin number and the lowest bit in 'value' (bit 0) will be used to set that pin. 
Bit 1 will be used to set the pin 'start' plus 1, bit 2 will set pin 'start'+2 and so on for 'nbr' number of bits. 
I/O pins used must be numbered consecutively and any I/O pin that is invalid or not configured as an output will cause an error. 
The start/nbr pair can be repeated if an additional group of output pins needed to be added. 
For example; PORT(15, 4, 23, 4) = &B10000011 Will set eight I/O pins. 
Pins 15 and 16 will be set high while 17, 18, 23, 24 and 25 will be set to a low and finally 26 will be set high. 
This command can be used to conveniently communicate with parallel devices like LCD displays. 
Any number of I/O pins (and therefore bits) can be used from 1 to the number of I/O pins on the chip. 
See the PORT function to simultaneously read from a number of pins. 
~PRINT expression [[,; ]expression] ... etc ,PRINT,INPUT,OUTPUT,*
------------------------------------
PRINT expression [[,; ]expression] ... etc 
Outputs text to the serial console followed by a carriage return/newline pair. 
Multiple expressions can be used and must be separated by either a: 
- Comma (,) which will output the tab character 
- Semicolon (;) which will not output anything (it is just used to separate expressions).
- Nothing or a space which will act the same as a semicolon.

A semicolon (;) or a comma (,) at the end of the expression list will suppress the output of the carriage return/newline pair at the end of a print statement. 
When printed, a number is preceded with a space if positive or a minus (-) if negative but is not followed by a space. 
Integers (whole numbers) are printed without a decimal point while fractions are printed with the decimal point and the significant decimal digits. 
Large or small floating point numbers are automatically printed in scientific number format. 
The function TAB() can be used to space to a certain column and the STR$() function can be used to justify or otherwise format strings.
~PRINT #nbr, expression [[,; ]expression] ... etc,PRINT,INPUT,OUTPUT,SERIAL,COM1,COM2,*
------------------------------------
PRINT #nbr, expression [[,; ]expression] ... etc
Same as above except that the output is directed to a serial communications port or a file opened for OUTPUT or APPEND with a file number of 'nbr'. 
See the OPEN command.
~PRINT #GPS, expression [[,; ]expression] ... etc [[,; ]expression] ... etc ,PRINT,GPS,*
------------------------------------
PRINT #GPS, expression [[,; ]expression] ... etc [[,; ]expression] ... etc 
Outputs a NMEA string to an opened GPS device. The string must start with a $ character and end with a * character. 
The checksum is automatically calculated and appended to the string together with the CR/LF characters.
~PRINT @(x [, y]) expression Or PRINT @(x, [y], m) expression ,PRINT,PRINT AT,INPUT,OUTPUT,*
------------------------------------
PRINT @(x [, y]) expression Or PRINT @(x, [y], m) expression 
Same as the standard PRINT command except that the cursor is positioned at the coordinates x, y expressed in pixels. 
If y is omitted the cursor will be positioned at "x" on the current line. 
Example: 
PRINT @(150, 45) "Hello World" 
The @ function can be used anywhere in a print command. 
Example: 
PRINT @(150, 45) "Hello" @(150, 55) "World" 
The @(x,y) function can be used to position the cursor anywhere on or off the screen. 
For example, 
PRINT @(-10, 0) "Hello" 
will only show "llo" as the first two characters could not be shown because they were off the screen. 
The @(x,y) function will automatically suppress the automatic line wrap normally performed when the cursor goes beyond the right screen margin. 
If 'm' is specified the mode of the video operation will be as follows: m = 0 Normal text (white letters, black background) 
m = 1 The background will not be drawn (ie, transparent) 
m = 2 The video will be inverted (black letters, white background) 
m = 5 Current pixels will be inverted (transparent background) 
~PULSE pin, width ,PIN, HARDWARE,*
------------------------------------
PULSE pin, width 
Will generate a pulse on 'pin' with duration of 'width' ms. 'width' can be a fraction. 
For example, 0.01 is equal to 10us and this enables the generation of very narrow pulses. 
The minimum is 5 us at 40 MHz to 40 us at 5 MHz. 
The generated pulse is of the opposite polarity to the state of the I/O pin when the command is executed. 
For example, if the output is set high the PULSE command will generate a negative going pulse. 
Notes: 
-'pin' must be configured as an output. 
- For a pulse of less than 3 ms the accuracy is ± 1 us. 
- For a pulse of 3 ms or more the accuracy is ± 0.5 ms.  
A pulse of 3 ms or more will run in the background. 
Up to five different and concurrent pulses can be running in the background and each can have its time changed by issuing a new PULSE command or it can be terminated by issuing a PULSE command with zero for 'width'. 
~PWM channel, frequency,[dutyA][,dutyB][,phase][,defer],PWM,PIN,HARDWARE,*
------------------------------------
PWM channel, frequency,[dutyA][,dutyB][,phase][,defer]
There are 8 separate PWM frequencies available (channels 0 to 7) and up to 16 outputs with individually controlled duty cycle. 
You can output on either PWMnA or PWMnB or both for each channel - no restriction. 
Duty cycles arespecified as a percentage and you can use a negative value to invert the output (-100.0 <= duty <=100.0).
Minimum frequency = (cpuspeed + 1) / (2^24) Hz. 
Maximum speed is OPTION CPUSPEED/4. 

At very fast speeds the duty cycles will be increasingly limited. 
Phase is a parameter that causes the waveforms to be centred such that a wave form with a shorter duty cycle starts and ends equal times from a longer one.  
Use 1 to enable this mode and 0 (or omit) to run as normal 
The parameter 'deferredstart' when set to 1 configures the PWM channels as but does not start the output running. 
They can the be started using the PWM SYNC command. This can be used to avoid any undesirable startup artefacts 
The PWM command is also capable of driving servos as follows:
PWM 1,50,(position_as_a_percentage * 0.05 + 5)
~PWM SYNC s0 [,s1][,s2][,s3][,s4][,s5][,s6][,s7],PWM,PIN,HARDWARE,*
------------------------------------
PWM SYNC s0 [,s1][,s2][,s3][,s4][,s5][,s6][,s7]
However, the power comes in the ability to offset the channels one to another (defined as a percentage of the time period as per the duty cycle - can be a float) 
You can use an offset of -1 to omit a channel from the synch
~PWM channel, OFF  ,PWM,PIN,HARDWARE,*
------------------------------------
PWM channel, OFF  
Stop output on 'channel'.
~RAM,MMBASIC,RAM,PROGRAMCONTROL,PROGRAM,DISK,SLOT,BETA,*
------------------------------------
RAM
RAM command functions like the FLASH command with 5 program slots available in PSRAM. 
All commands except RAM LOAD (copies a RAM slot to the main flash program space) can be used in a program.
RAM FILE LOAD n,fname$ [,O[VERWRITE]] can be used to load a RAM slot from a standard disk .BAS file
RP2350 with PSRAM only!
BETA 6.02 ONLY!
~RANDOMIZE nbr ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
RANDOMIZE nbr 
Seed the random number generator with 'nbr'. 
On the RP2040 the random number generator is seeded with zero at power up and will generate the same sequence of random numbers each time. 
To generate a different random  sequence each time you must use a different value for 'nbr' (the TIMER function is handy for that). 
This command does nothing on the RP2350 which has a hardware random generator that does not require seeding.
~RBOX x, y, w, h [, r] [,c] [,fill] ,DRAWING,GRAPHIC,*
------------------------------------
RBOX x, y, w, h [, r] [,c] [,fill] 
Draws a box with rounded corners on the VGA screen starting at 'x' and 'y' which is 'w' pixels wide and 'h' pixels high. 
'r' is the radius of the corners of the box. It defaults to 10. 'c' specifies the colour and defaults to the default foreground colour if not specified. 
'fill' is the fill colour. It can be omitted or set to -1 in which case the box will not be filled. 
All parameters can be expressed as arrays and the software will plot the number of boxes as determined by the dimensions of the smallest array. 
'x', 'y', 'w', and 'h' must all be arrays or all be single variables /constants otherwise an error will be generated. 
'r', 'c', and 'fill' can be either arrays or single variables/constants. 
See the section Graphics Commands and Functions for a definition of the colours and graphics coordinates. 
~READ variable[, variable]... ,DATA,VARIABLES,*
------------------------------------
READ variable[, variable]... 
Reads values from DATA statements and assigns these values to the named variables. 
Variable types in a READ statement must match the data types in DATA statements as they are read. 
Arrays can be used as variables (specified with empty brackets, e.g. a()) and in that case the size of the array is used to determine how many elements are to be read. 
If the array is multidimensional then the leftmost dimension will be the fastest moving. See also DATA  and RESTORE. 
~READ SAVE or READ RESTORE ,DATA,VARIABLES,*
------------------------------------
READ SAVE or READ RESTORE 
READ SAVE will save the virtual pointer used by the READ command to point to the next DATA to be read. 
READ RESTORE will restore the pointer that was previously saved. 
This enables subroutines to READ data and then restore the read pointer so as not to disturb other parts of the program that may be reading the same data statements. These commands can be nested.
~REFRESH,PERIPHERALS,LCD,*
------------------------------------
REFRESH
Initiates an update of the screen for e-ink black and white displays. 
These can only be updated a full screen at a time and if OPTION AUTOREFRESH is OFF this command can be used to trigger the write. 
This command works with the following displays: N5110, SSD1306I2C, SSD1306I2C32, SSD1306SPI, ST7920.
~REM string ,MMBASIC,ENVIRONMENT,CONTROL,WORKFLOW,*
------------------------------------
REM string 
REM allows remarks to be included in a program. 
Note the Microsoft style use of the single quotation mark (') to denote remarks is also supported and is preferred. 
~RENAME old$ AS new$ ,FILES,*
------------------------------------
RENAME old$ AS new$ 
Rename a file or a directory from 'old$' to 'new$'. Both are strings. 
A directory path can be used in both 'old$' and 'new$'. 
If the paths differ the file specified in 'old$' will be moved to the path specified in 'new$' with the file name as specified. 
~RESTORE [line] ,DATA,VARIABLES,*
------------------------------------
RESTORE [line] 
Resets the line and position counters for the READ statement. 
If 'line' is specified the counters will be reset to the beginning of the specified line. 
'line' can be a line number or label or a variable with these values. 
If 'line' is not specified the counters will be reset to the start of the program. 
~RMDIR dir$ ,FILES,*
------------------------------------
RMDIR dir$ 
Remove, or delete, the directory 'dir$' on the SD card. 
~RTC GETTIME  ,TIME,DATE,*
------------------------------------
RTC GETTIME  
RTC GETTIME will get the current date/time from a PCF8563, DS1307 or DS3231 real time clock and set the internal MMBasic clock accordingly. 
The date/time can then be retrieved with the DATE$ and TIME$ functions. 

Using the RTC GETTIME command it is easy to get the current time from a PCF8563, DS1307, DS3231 or DS3232 real time clock as well as compatible devices such as the M41T11. 
These integrated circuits are popular and cheap, will keep accurate time even with the power removed and can be purchased for US$2 to $8 on eBay. 
Complete modules including the battery can also be purchased on eBay for a little more. 
The PCF8563 and DS1307 will keep time to within a minute or two over a month while the DS3231 and DS3232 are particularly precise and will remain accurate to within a minute over a year.
These chips are I2C devices and should be connected to the I2C I/O pins of the Raspberry Pi Pico.
Internal pullup resistors (100KΩ) are applied to the I2C I/O pins so, in many cases external resistors are not needed.
In order to enable the RTC you first need to allocate the I2C pins to be used using the command:
OPTION SYSTEM I2C SDApin, SCLpin
The time used by the RTC must also be set. That is done with the RTC SETTIME command which uses the format :
RTC SETTIME year, month, day, hour, minute, second

For example, the following will set the real time clock to 4PM on the 10th November 2025:
RTC SETTIME 2025, 11, 10, 16, 0, 0
To get the time you use the RTC GETTIME command which will read the time from the real time clock chip and set the clock inside the Raspberry Pi Pico. 
Normally this command will be placed at the beginning of the program or in the subroutine MM.STARTUP so that the time is set on power up. 
The command OPTION RTC AUTO ENABLE can also be used to set an automatic update of the TIME$ and DATE$ read only variables from the real time clock chip on boot and every hour.

~RTC SETTIME year, month,day, hour, minute, second ,TIME,DATE,*
------------------------------------
RTC SETTIME year, month,day, hour, minute, second 
RTC SETTIME will set the time in the clock chip. 
'hour' must use 24 hour notation. 
'year' can be two or four digits. 
The RTC SETTIME command will also accept a single string argument in the format of dd/mm/yy hh:mm. 
This means the date/time could be entered by the user using a GUI FORMATBOX with the DATETIME2 format.

Using the RTC GETTIME command it is easy to get the current time from a PCF8563, DS1307, DS3231 or DS3232 real time clock as well as compatible devices such as the M41T11. 
These integrated circuits are popular and cheap, will keep accurate time even with the power removed and can be purchased for US$2 to $8 on eBay. 
Complete modules including the battery can also be purchased on eBay for a little more. 
The PCF8563 and DS1307 will keep time to within a minute or two over a month while the DS3231 and DS3232 are particularly precise and will remain accurate to within a minute over a year.
These chips are I2C devices and should be connected to the I2C I/O pins of the Raspberry Pi Pico.
Internal pullup resistors (100KΩ) are applied to the I2C I/O pins so, in many cases external resistors are not needed.
In order to enable the RTC you first need to allocate the I2C pins to be used using the command:
OPTION SYSTEM I2C SDApin, SCLpin
The time used by the RTC must also be set. That is done with the RTC SETTIME command which uses the format :
RTC SETTIME year, month, day, hour, minute, second

Note that the hour must be in 24 hour format.

For example, the following will set the real time clock to 4PM on the 10th November 2025:
RTC SETTIME 2025, 11, 10, 16, 0, 0
To get the time you use the RTC GETTIME command which will read the time from the real time clock chip and set the clock inside the Raspberry Pi Pico. 
Normally this command will be placed at the beginning of the program or in the subroutine MM.STARTUP so that the time is set on power up. 
The command OPTION RTC AUTO ENABLE can also be used to set an automatic update of the TIME$ and DATE$ read only variables from the real time clock chip on boot and every hour.

~RTC SETREG reg, value or RTC GETREG reg, var ,TIME,DATE,*
------------------------------------
RTC SETREG reg, value or RTC GETREG reg, var 
RTC SETREG reg, value  
RTC GETREG reg, var 
The RTC SETREG and GETREG commands can be used to set or read the contents of registers within the chip. 
'reg' is the register's number, 
'value' is the number to store in the register and 
'var' is a variable that will receive the number read from the register. 
These commands are not necessary for normal operation but they can be used to manipulate special features of the chip (alarms, output signals, etc). 
They are also useful for storing temporary information in the chip's battery backed RAM. 
These chips are I2C devices and must be connected to the two I2C pins as specified by OPTION SYSTEM I2C with appropriate pullup resistors. 
Also see the command OPTION RTC AUTO ENABLE.
~RUN or RUN [file$] [, cmdline$],WORKFLOW,PROGRAMCONTROL,*
------------------------------------
RUN or RUN [file$] [, cmdline$]
Run a program. 
If 'file$' is not supplied then run the program currently held in program  memory.
If 'file$' is supplied then run the named file from the Flash or SD Card filesystem. 
If 'file$' does not contain a '.BAS' extension then one will be automatically added. 
If 'cmdline$' is supplied then pass its value to the MM.CMDLINE$ constant of the program when it runs. 
If 'cmdline'$ is not supplied then an empty string value is passed to  MM.CMDLINE$.. 

Notes:
-> Both 'file$' and 'cmdline$' may be supplied as string expressions.
-> Use FLASH RUN n to run a program stored in a Flash Slot.
~SAVE file$ ,WORKFLOW,FILES,*
------------------------------------
SAVE file$ 
Saves the program in the current working directory of the Flash Filesystem or SD Card as 'file$'. 
Example: 
SAVE "TEST.BAS" 
If an extension is not specified ".BAS" will be added to the file name. See also FLASH SAVE n for saving to a Flash Slot. 
~SAVE CONTEXT [CLEAR] or LOAD CONTEXT [KEEP],WORKFLOW,FILES,VARIABLES,BETA,*
------------------------------------
SAVE CONTEXT [CLEAR] or LOAD CONTEXT [KEEP]
SAVE CONTEXT [CLEAR]
Saves the variable space and optionally clears it. Command should be used in top level program and not from within a subroutine (may work OK but not tested for side-effects). 
Note:
If used in a subroutine a subsequent LOAD CONTEXT must DEFINITELY be in the same subroutine or a crash/corruption will DEFINITELY happen.
LOAD CONTEXT [KEEP]
Restores the variable space to the previously saved state and optionally preserves the stored variables to allow a second LOAD if required. 
BETA 6.02 ONLY!
~SAVE PERSISTENT n%,WORKFLOW,WATCHDOG,RESET,BETA,*
------------------------------------
SAVE PERSISTENT n%
Saves the value n% in memory that will survive a watchdog reset (RP2040 and RP2350) or a physical reset (RP2040 only)
BETA 6.02 ONLY! 
~SAVE IMAGE file$ [,x, y, w, h]  or  SAVE COMPRESSED IMAGE file$ [,x, y, w, h] ,GRAPHIC,FILES,LCD,*
------------------------------------
SAVE IMAGE file$ [,x, y, w, h]  or  SAVE COMPRESSED IMAGE file$ [,x, y, w, h] 
Save the current image on the video output or LCD panel as a BMP file. 
Any LCD panel must be capable of being read, for example, a ILI9341 based panel or a VIRTUAL_M or VIRTUAL_C panel. 
'file$' is the name of the file. If an extension is not specified ".BMP" will be added to the file name. 
The image is saved as a true colour 24-bit image. 
'x', 'y', 'w' and 'h' are optional and are the coordinates ('x' and 'y' are the top left coordinate) and  dimensions (width and height) of the area to be saved. 
If not specified the whole screen will be saved. 
SAVE COMPRESSED IMAGE will work the same except that RLE compression will be used to reduce the file size.
~SEEK [#]fnbr, pos ,FILES,INPUT,OUTPUT,*
------------------------------------
SEEK [#]fnbr, pos 
Will position the read/write pointer in a file that has been opened on the Flash Filesystem or SD Card for RANDOM / INPUT access to the 'pos' byte. 
The first byte in a file is numbered one so SEEK #5,1 will position the read/write pointer to the start of the file opened as #5. 
~SELECT CASE value CASE testexp <statements> CASE ELSE <statements> END SELECT ,MMBASIC,ENVIRONMENT,CONTROL,CONDITIONAL,*
------------------------------------
SELECT CASE value CASE testexp <statements> CASE ELSE <statements> END SELECT 
SELECT CASE value 
CASE testexp [[, testexp] ...] 
<statements> 
<statements> 
CASE ELSE 
<statements> 
<statements> 
END SELECT 
Executes one of several groups of statements, depending on the value of an expression. 
'value' is the expression to be tested. It can be a number or string variable or a complex expression. 
'testexp' is the value that is to be compared against. 
It can be: 
- A single expression (i.e. 34, "string" or PIN(4)*5) to which it may equal
- A range of values in the form of two single expressions separated by the keyword "TO" (i.e. 5 TO 9 or "aa" TO "cc") 
- A comparison starting with the keyword "IS" (which is optional). 

For example: 
IS > 5, IS <= 10. 
When a number of test expressions (separated by commas) are used the CASE statement will be true if any one of these tests evaluates to true. 
If 'value' cannot be matched with a 'testexp' it will be automatically matched to the CASE ELSE. 
If CASE ELSE is not present the program will not execute any <statements> and continue with the code following the END SELECT.
When a match is made the <statements> following the CASE statement will be executed until END SELECT or another CASE 
is encountered when the program will then continue with the code following the END SELECT. 
An unlimited number of CASE statements can be used but there must be only one CASE ELSE and that should be the last before the END SELECT. 

Example: 
SELECT CASE nbr% 
CASE 4, 9, 22, 33 TO 88 
statements 
CASE IS < 4, IS > 88, 5 TO 8 
statements 
CASE ELSE 
statements 
END SELECT 
Each SELECT CASE must have one and one only matching END SELECT statement. 
Any number of SELECT...CASE statements can be nested inside the CASE statements of other SELECT...CASE statements. 
~SERVO channel [positionA] [,positionB],PERIPHERALS,HARDWARE,*
------------------------------------
SERVO channel [positionA] [,positionB]
Control a standard servo. 'positionA' and 'positionB' can be between -20 and 120 and will generate a 50Hz signal between 800uSec and 2.2mSec 
As with the PWM command the pins must be set up with SETPIN n,PWM 
To use just channel B use the syntax: SERVO channel,,positionB 
Refer to the pinout to give the channel and sub-channel (A or B) for each pin
~SETPIN pin, cfg, target [,option] ,PIN,HARDWARE,*
------------------------------------
SETPIN pin, cfg, target [,option] 
Will configure an external I/O pin. Refer to the chapter Using the I/O pins for a general description of the Pico's input/output capabilities. 
'pin' is the I/O pin to configure, 
'cfg' is the mode that the pin is to be set to and 
'option' is an optional parameter. 
'cfg' is a keyword and can be any one of the following:
OFF Not configured or inactive 
AIN Analog input (i.e. measure the voltage on the input).
ARAW Fast analog input returning a value between 0 and 1023.
DIN Digital input 
If 'option' is omitted the input will be high impedance If 'option' is the keyword "PULLUP" or "PULLDOWN" a constant current of about 50uA 
will be used to pull the input pin up or down to 3.3V. Due to a bug in the RP2350 chips it is recommended that a pulldown be implemented using a 8.2K or less resistor.
FIN Frequency input 'option' can be used to specify the gate time (the length of time used to count the input cycles). 
It can be any number between 10 ms and 100000 ms. 
The PIN() function will always return the frequency correctly scaled in Hz regardless of the gate time used. 
If 'option' is omitted the gate time will be 1 second. The pins can be GP6, GP7, GP8 or GP9 (can be changed with OPTION COUNT).
PIN Period input 'option' can be used to specify the number of input cycles to average the period measurement over. 
It can be any number between 1 and 10000. 
The PIN() function will always return the average period of one cycle correctly scaled in ms regardless of the number of cycles used for the average. 
If 'option' is omitted the period of just one cycle will be used. The pins can be GP6, GP7, GP8 or GP9 (can be changed with OPTION COUNT).
CIN Counting input 'option' can be used to specify which edge triggers the count and if any pullup or pulldown is enabled 2 specifies a falling edge with pullup, 
3 specifies that both a falling and rising edge will trigger a count with no pullup applied, 5 specifies both edges but with a pullup applied. 
If 'option' is omitted a rising edge will trigger the count. Due to a bug in the RP2350 chips pulldown is not recommended. 
The pins can be GP6, GP7, GP8 or GP9 (can be changed with OPTION COUNT). 
DOUT Digital output 'option' is not used in this mode. 
The functions PIN() and PORT() can also be used to return the value on one or more output pins. 
See the function PIN() for reading inputs and the statement PIN()= for setting an output. See the command below if an interrupt is configured.
~SETPIN pin, cfg, target [, option],PIN,HARDWARE,*
------------------------------------
SETPIN pin, cfg, target [, option]
Will configure 'pin' to generate an interrupt according to 'cfg'. 
Any I/O pin capable of digital input can be  configured to generate an interrupt with a maximum of ten interrupts configured at any one time. 
'cfg' is a keyword and can be any one of the following:
OFF Not configured or inactive
INTH Interrupt on low to high input
INTL Interrupt on high to low input
INTB Interrupt on both (i.e. any change to the input) 
'target' is a user defined subroutine which will be called when  the event happens. Return from the interrupt is via the END SUB or EXIT SUB commands. 
'option' is the same as used in SETPIN pin DIN (above). 
This mode also configures the pin as a digital input so the value of the pin can always be retrieved using the function PIN(). 
Refer to the chapter Using the I/O pins for a general description.
~SETPIN GP25, DOUT | HEARTBEAT,PIN,HARDWARE,*
------------------------------------
SETPIN GP25, DOUT | HEARTBEAT
This version of SETPIN controls the on-board LED. If it is configured as DOUT then it can be switched on and off under program control. 
If configured as HEARTBEAT then it will flash 1s on, 1s off continually while powered. 
This is the default state and will be restored to this when the user program stops running.
~SETPIN p1[, p2 [, p3]], device ,PIN,HARDWARE,*
------------------------------------
SETPIN p1[, p2 [, p3]], device 
These commands are used for the pin allocation for special devices. 
Pins must be chosen from the pin designation diagram and must be allocated before the devices can be used. 
Note that the pins (eg, rx, tx, etc) can be declared in any order and that the pins can be referred to by using their pin number (eg, 1, 2) or GP number (eg, GP0, GP1). 
Note that on the WebMite version:
- SPI1 and SPI2 are not available on GP20 to GP28
- COM1 and COM2 are not available on P20 to GP28
- I2C is not available on pin 34 (GP28)
- The following are not available; GP29, GP25, GP24 and GP23
~SETPIN rx, tx, COM1 ,COM1,PIN,HARDWARE,*
------------------------------------
SETPIN rx, tx, COM1 
Allocate the pins to be used for serial port COM1. Valid pins are RX: GP1 or GP13 TX: GP0, GP12 or GP28 
~SETPIN rx, tx, COM2 ,COM2,PIN,HARDWARE,*
------------------------------------
SETPIN rx, tx, COM2 
Allocate the pins to be used for serial port COM2. Valid pins are RX: GP5 TX: GP4 
~SETPIN rx, tx, clk, SPI ,SPI,PIN,HARDWARE,*
------------------------------------
SETPIN rx, tx, clk, SPI 
Allocate the pins to be used for SPI port SPI. Valid pins are RX: GP0 or GP4 TX: GP3 or GP7 CLK: GP2 or GP6 
~SETPIN rx, tx, clk, SPI2 ,SPI2,PIN,HARDWARE,*
------------------------------------
SETPIN rx, tx, clk, SPI2 
Allocate the pins to be used for SPI port SPI2. Valid pins are RX: GP12 or GP28 TX: GP11, GP15 or GP27 CLK: GP10, GP14 or GP26 
~SETPIN sda, scl, I2C ,I2C,PIN,HARDWARE,*
------------------------------------
SETPIN sda, scl, I2C 
Allocate the pins to be used for the I2C port I2C. Valid pins are SDA: GP0, GP4, GP12 or GP28 
SCL: GP1, GP5 or GP13 
~SETPIN sda, scl, I2C2 ,I2C2,PIN,HARDWARE,*
------------------------------------
SETPIN sda, scl, I2C2 
Allocate the pins to be used for the I2C port I2C2. Valid pins are SDA: GP2, GP6, GP10, GP14, GP22 or GP26 SCL: GP3, GP7, GP11, GP15 or GP27 
~SETPIN pin, PWMnx ,PIN,PWM,HARDWARE,*
------------------------------------
SETPIN pin, PWMnx 
'n' is the PWM number (0 to 7) and 'x' and is the channel (A or B). n and x are optional. 
The setpin can be changed until the PWM command is issued. At that point the pin becomes locked to PWM until PWMn,OFF is issued.
~SETPIN pin, IR  ,PIN,IR,PERIPHERALS,*
------------------------------------
SETPIN pin, IR  
Allocate pins for InfraRed (IR) communications (can be any pin). 
~SETPIN pin, PIOn  ,PIN,HARDWARE,PERIPHERALS,*
------------------------------------
SETPIN pin, PIOn  
Reserve pin for use by a PIO0 or PIO1 (see official user manual Appendix F for PIO details).
~SETPIN GP1, FFIN [,gate],PIN,HARDWARE,*
------------------------------------
SETPIN GP1, FFIN [,gate]
Sets GP1 as a fast frequency input. 
Inputs up to the CPU speed /2 can be recorded. 'gate' can be used to specify the gate time (the length of time used to count the input cycles). 
It can be any number between 10 ms and 100000 ms. 
The PIN() function will always return the frequency correctly scaled in Hz regardless of the gate time used. 
If 'option' is omitted the gate time will be 1 second. 
The function uses PWM channel 0 to do the counting so it is incompatible with any other use of that PWM channel.
~SETTICK period, target [, nbr] ,TIME,*
------------------------------------
SETTICK period, target [, nbr] 
This will setup a periodic interrupt (or "tick"). Four tick timers are available ('nbr' = 1, 2, 3 or 4). 
'nbr' is optional and if not specified timer number 1 will be used. 
The time between interrupts is 'period' milliseconds and 'target' is the interrupt subroutine which will be called when the timed event occurs. 
The period can range from 1 to 2147483647 ms (about 24 days). 
These interrupts can be disabled by setting 'period' to zero  (i.e. SETTICK 0, 0, 3 will disable tick timer number 3). 
~SETTICK PAUSE, target [, nbr]  or  SETTICK RESUME, target [, nbr] ,TIME,*
------------------------------------
SETTICK PAUSE, target [, nbr]  or  SETTICK RESUME, target [, nbr] 
Pause or resume the specified tick timer. When paused the interrupt is delayed but the current count is maintained. 
~SORT array() [,indexarray()] [,flags] [,startposition] [,elementstosort]  ,ARRAY,STRINGS,VARIABLES,*
------------------------------------
SORT array() [,indexarray()] [,flags] [,startposition] [,elementstosort]  
This command takes an array of any type (integer, float or string) and sorts it into ascending order in place. 
It has an optional parameter 'indexarray%()'. If used this must be an integer array of the same size as the array to be sorted.
After the sort this array will contain the original index position of each element in the array being sorted before it was sorted. 
Any data in the array will be overwritten. This allows connected arrays to be sorted.
The 'flag' parameter is optional and valid flag values are:
bit0: 0 (default if omitted) normal sort - 1 reverse sort
bit1: 0 (default) case dependent - 1 sort is case independent (strings only).
bit2: 0 (default) normal sort - 1 empty strings go to the end of the array

The optional 'startposition' defines which element in the array to start the sort. Default is 0 (OPTION BASE 0) or 1 (OPTION BASE 1)
The optional 'elementstosort' defines how many elements in the array should be sorted. The default is all elements after the 'startposition'.
Any of the optional parameters may be omitted so, for example, to sort just the first 50 elements of an array you could use:
SORT array(), , , ,50
Example:
The array city$() might contain the names of world cities and can be easily sorted into increasing alphabetical order with the command: 
SORT city$() 
The SORT command will work with strings, floats and integers however the array to be sorted must be single dimensioned. 
Often data is held in multiple arrays, for example, the name of each city might be held in the array city$(), the population held in the array pop%() 
and the size of the city held in area!(). The same index would refer to the name, population and the area of the city. 
Sorting and accessing this data is a little more complex but it can be done relatively easily using an optional parameter to the sort command as follows:
SORT array(), indexarray%() 
indexarray%() must be a single dimension integer array of the same size as the array being sorted. 
Following the sort indexarray%() will contain the corresponding index to the original data before it was sorted. 
(anything previously in indexarray%() will be overwritten).
To access the sorted data you would first copy the array holding the main key to a temporary array and sort that while specifying indexarray%(). 
After the sort indexarray%() can be used to index the original arrays.

For example:
DIM city$(100),pop%(100),area!(100),sindex%(100),t$(100)
FOR i = 0 to 100
t$(i) = city$(i) ' temporary copy of the keys
NEXT i
SORT t$(), sindex%() ' sort the temporary array,
FOR i = 0 to 100
k = sindex%(i) ' index to the original array
PRINT city$(k),pop%(k),area!(k)' print in sorted order
NEXT i
~SPI OPEN speed, mode, bits or SPI READ nbr, array() or SPI WRITE nbr, data1, data2,data3, ... etc or SPI WRITE nbr, string$ or SPI WRITE nbr, array() or SPI CLOSE,SPI,PERIPHERALS,*
------------------------------------
SPI OPEN speed, mode, bits or SPI READ nbr, array() or SPI WRITE nbr, data1, data2,data3, ... etc or SPI WRITE nbr, string$ or SPI WRITE nbr, array() or SPI CLOSE
See Appendix D for the details. 
'nbr' is the number of data items to send or receive 'data1', 'data2', etc can be float or integer and in the case of WRITE can be a constant or expression. 
If 'string$' is used 'nbr' characters will be sent. 'array' must be a single dimension float or integer array and 'nbr' elements will be sent or received.
Appendix D - SPI Communications

SPI COMMUNICATIONS

The Serial Peripheral Interface (SPI) communications protocol is used to send and receive data between integrated circuits. The Raspberry Pi Pico acts as the master (i.e. it generates the clock).
I/O PINS

Before an SPI interface can be used the I/O pins for the channel must be allocated using the following commands. 
For the first channel (referred as SPI) it is:
SETPIN rx, tx, clk, SPI
Valid pins are
RX:
GP0, GP4, GP16 or GP20
TX:
GP3, GP7 or GP19
CLK:
GP2, GP6 or GP18

And the following command for the second channel (referred to as SPI2) is:
SETPIN rx, tx, clk, SPI2
Valid pins are
RX:
GP8, GP12 or GP28
TX:
GP11, GP15 or GP27
CLK:
GP10, GP14 or GP26

TX is data from the Raspberry Pi Pico and RX is data to it. 
Note that on the WebMite version SPI1 and SPI2 are not available on GP20 to GP28 

SPI OPEN

To use the SPI function the SPI channel must be first opened.
The syntax for opening the first SPI channel is (use SPI2 for the second channel):
SPI OPEN speed, mode, bits
Where:
-> 'speed' is the speed of the clock. It is a number representing the clock speed in Hz.
-> 'mode' is a single numeric digit representing the transmission mode - see Transmission Format below.
-> 'bits' is the number of bits to send/receive. This can be any number in the range of 4 to 16 bits.
-> It is the responsibility of the program to separately manipulate the CS (chip select) pin if required.

TRANSMISSION FORMAT 

The most significant bit is sent and received first. The format of the transmission can be specified by the 'mode' as shown below. Mode 0 is the most common format.
Mode                Description                         CPOL    CPHA 

0       Clock is active high, data is captured on       0       0
        the rising edge and output on the falling 
        edge 
1       Clock is active high, data is captured on       0       1
        the falling edge and output on the rising 
        edge 
2       Clock is active low, data is captured on        1       0
        the falling edge and output on the rising 
        edge 
3       Clock is active low, data is captured on        1       1
        the rising edge and output on the falling 
        edge

For a more complete explanation see: http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus

STANDARD SEND/RECEIVE

When the first SPI channel is open data can be sent and received using the SPI function (use SPI2 for the second channel). 
The syntax is: received_data = SPI(data_to_send) 
Note that a single SPI transaction will send data while simultaneously receiving data from the slave.

'data_to_send' is the data to send and the function will return the data received during the transaction.
'data_to_send' can be an integer or a floating point variable or a constant.
If you do not want to send any data (i.e. you wish to receive only) any number (eg, zero) can be used for the data to send. 
Similarly if you do not want to use the data received it can be assigned to a variable and ignored.

BULK SEND/RECEIVE

Data can also be sent in bulk (use SPI2 for the second channel):
SPI WRITE nbr, data1, data2, data3, ... etc 
or
SPI WRITE nbr, string$
or
SPI WRITE nbr, array()
In the first method 'nbr' is the number of data items to send and the data is the expressions in the argument list (i.e. 'data1', data2' etc). The data can be an integer or a floating point variable or a constant.

In the second or third method listed above the data to be sent is contained in the 'string$' or the contents of 'array()' (which must be a single dimension array of integer or floating point numbers). 
The string length, or the size of the array must be the same or greater than nbr. Any data returned from the slave is discarded.

Data can also be received in bulk (use SPI2 for the second channel):
SPI READ nbr, array()
Where 'nbr' is the number of data items to be received and array() is a single dimension integer array where the received data items will be saved. This command sends zeros while reading the data from the slave.

SPI CLOSE

If required the first SPI channel can be closed as follows (the I/O pins will be set to inactive):
SPI CLOSE
Use SPI2 for the second channel.

Examples
The following example shows how to use the SPI port for general I/O. 
It will send a command 80 (hex) and receive two bytes from the slave SPI device using the standard send/receive function:
PIN(10) = 1 : SETPIN 10, DOUT       ' pin 10 will be used as the enable signal
SETPIN GP20, GP3, GP2, SPI      ' assign the I/O pins
SPI OPEN 5000000, 3, 8      ' speed is 5 MHz and the data size is 8 bits
PIN(10) = 0     ' assert the enable line (active low)
junk = SPI(&H80)        ' send the command and ignore the return
byte1 = SPI(0)      ' get the first byte from the slave
byte2 = SPI(0)      ' get the second byte from the slave
PIN(10) = 1     ' deselect the slave
SPI CLOSE       ' and close the channel
The following is similar to the example given above but this time the transfer is made using the bulk send/receive commands:
OPTION BASE 1       ' our array will start with the index 1
DIM data%(2)        ' define the array for receiving the data
SETPIN GP20, GP3, GP2, SPI      ' assign the I/O pins
PIN(10) = 1 : SETPIN 10, DOUT       ' pin 10 will be used as the enable signal
SPI OPEN 5000000, 3, 8      ' speed is 5 MHz, 8 bits data
PIN(10) = 0     ' assert the enable line (active low)
SPI WRITE 1, &H80       ' send the command
SPI READ 2, data%()     ' get two bytes from the slave
PIN(10) = 1     ' deselect the slave
SPI CLOSE       ' and close the channel
~SPI2 ,SPI2,PERIPHERALS,*
------------------------------------
SPI2 
~SPRITE (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE (VGA AND HDMI VERSIONS ONLY)
The SPRITE commands are used to manipulate small graphic images on the VGA or HDMI screen and are useful when writing games. 
Sprites operate in framebuffers in MODEs 2 and 3 only. Sprites are always stored as RGB121 'nibbles' for efficiency 
The maximum size of a sprite is MM.HRES-1 and MM.VRES-1. See also the BLIT command and SPRITE() functions.

Appendix G
Sprites
VGA AND HDMI VERSIONS ONLY
You can create a sprite in various ways but essentially you are just storing an image in a buffer. 
The difference comes when you SHOW the sprite. In this case, first time in, the firmware stores the area of memory (or display real-estate) that will be replaced by the sprite and then draws the sprite in its place.
Subsequent SHOW commands replace the sprite with the stored background, store the background for the new location and finally draw the sprite. 
In this way you can move the sprite over the background without any extra code.
Collision detection then sits on top of this and looks for the rectangular boundaries of sprites touching to create an interrupt or a sprite touching the edge of the frame.
Sprites are ordered so the drawing order is held in a lifo. suppose you have sprite 1 overlapped by sprite 2 and then by sprite 3. 
If you simply moved sprite 1 then its background would overwrite bits of 2 and 3 - not what we want. 
SPRITE SHOW SAFE unwinds the LIFO by removing each sprite in reverse order, moves sprite 1 and then restores first 2 and then 3 on top of it. 
Finally there is the concept of layers (this is the 4th parameter in SPRITE SHOW). The concept of the sprite implementation is as follows:
-> Sprites are full colour and of any size. The collision boundary is the enclosing rectangle.
-> Sprites are loaded to a specific number (1 to 64).
-> Sprites are displayed using the SPRITE SHOW command.
-> For each SHOW command the user must select a "layer". This can be between 0 and 10.
-> Sprites collide with sprites on the same layer, layer 0, or the screen edge.
-> Layer 0 is a special case and sprites on all other layers will collide with it.
-> The SCROLL commands leave sprites on all layers except layer 0 unmoved.
-> Layer 0 sprites scroll with the background and this can cause collisions.
-> There is no practical limit on the number of collisions caused by SHOW or SCROLL commands.
-> The SPRITE() function allows the user to fully interrogate the details of a collision.
-> A SHOW command will overwrite the details of any previous collisions for that sprite.
-> A SCROLL command will overwrite details of previous collisions for ALL sprites.
-> To restore a screen to a previous state sprites should be removed in the opposite order to which they were written (ie, last in first out).

Because moving a sprite or, particularly, scrolling the background can cause multiple sprite collisions it is important to understand how they can be interrogated.
The best way to deal with a sprite collision is using the interrupt facility. A collision interrupt routine is set up using the SPRITE INTERRUPT command. 
Eg:
SPRITE INTERRUPT collision
The following is an example program for identifying all collisions that have resulted from either a SPRITE SHOW command or a SCROLL command
'
' This routine demonstrates a complete interrogation of collisions
'
SUB collision
LOCAL INTEGER i
' First use the SPRITE(S) function to see what caused the interrupt 
    IF SPRITE(S) <> 0 THEN 'collision of specific individual sprite
'SPRITE(S) returns the sprite that moved to cause the collision
    PRINT "Collision on sprite ", SPRITE(S)
process_collision(SPRITE(S))
PRINT
ELSE '0 means collision of one or more sprites caused by background move
' SPRITE(C, 0) will tell us how many sprites had a collision
PRINT "Scroll caused a total of ", SPRITE(C,0)," sprites to have collisions"
FOR I = 1 TO SPRITE(C, 0)
' SPRITE(C, 0, i) will tell us the sprite number of the 'I'th sprite
PRINT "Sprite ", SPRITE(C, 0, i)
process_collision(SPRITE(C, 0, i))
NEXT i
PRINT
ENDIF
END SUB
' get details of the specific collisions for a given sprite
SUB process_collision(S AS INTEGER)
LOCAL INTEGER i, j
' SPRITE(C, #n) returns the number of current collisions for sprite n
PRINT "Total of " SPRITE(C, S) " collisions"
FOR I = 1 TO SPRITE(C, S)
' SPRITE(C, S, i) will tell us the sprite number of the 'I'th sprite
j = SPRITE(C, S, i)
IF j = &HF1 THEN
PRINT "collision with left of screen"
ELSE IF j = &HF2 THEN
PRINT "collision with top of screen"
ELSE IF j = &HF4 THEN
PRINT "collision with right of screen"
ELSE IF j = &HF8 THEN
PRINT "collision with bottom of screen"
ELSE
' SPRITE(C, #n, #m) returns details of the mth collision
PRINT "Collision with sprite ", SPRITE(C, S, i)
ENDIF
NEXT i
END SUB
~SPRITE CLOSE [#]n  (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE CLOSE [#]n  (VGA AND HDMI VERSIONS ONLY)
The command will give an error if other sprites are copied from this one unless they are closed first.
~SPRITE CLOSE ALL (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE CLOSE ALL (VGA AND HDMI VERSIONS ONLY)
Closes all sprites and releases all sprite memory. The screen is not changed
~SPRITE COPY [#]n, [#]m, nbr (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE COPY [#]n, [#]m, nbr (VGA AND HDMI VERSIONS ONLY)
Makes a copy of sprite "n" to "nbr" of new sprites starting a number "m". Copied sprites share the same loaded image as the original to save memory
~SPRITE HIDE [#]n  (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE HIDE [#]n  (VGA AND HDMI VERSIONS ONLY)
Removes sprite n from the display and replaces the stored background. 
To restore a screen to a previous state sprites should be hidden in the opposite order to which they were written "LIFO"
~SPRITE HIDE ALL (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE HIDE ALL (VGA AND HDMI VERSIONS ONLY)
Hides all the sprites allowing the background to be manipulated. The following commands cannot be used when all sprites are hidden:
SPRITE SHOW (SAFE) ,  SPRITE HIDE (SAFE, ALL) ,  SPRITE SWAP , SPRITE MOVE , SPRITE SCROLLR , SPRITE SCROLL
~SPRITE RESTORE  (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE RESTORE  (VGA AND HDMI VERSIONS ONLY)
Restores the sprites that were previously hidden with SPRITE HIDE ALL.
~SPRITE HIDE SAFE [#]n (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE HIDE SAFE [#]n (VGA AND HDMI VERSIONS ONLY)
Removes sprite n from the display and replaces the stored background. Automatically hides all more recent sprites as well as the requested one and then replaces them afterwards. 
This ensures that sprites that are covered by other sprites can be removed without the user tracking the write order. 
Of course this version is less performant than the simple version and should only be used it there is a risk of the sprite being partially covered.
~SPRITE INTERRUPT sub  (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE INTERRUPT sub  (VGA AND HDMI VERSIONS ONLY)
Specifies the name of the subroutine that will be called when a sprite collision occurs. 
See Appendix H for how to use the function SPRITE to interrogate details of what has collided.
~SPRITE READ [#]b, x, y, w, h (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE READ [#]b, x, y, w, h (VGA AND HDMI VERSIONS ONLY)
This will copy a portion of the display to the memory buffer '#b'. 
The source coordinate is 'x' and 'y' and the width of the display area to copy is 'w' and the height is 'h'. 
When this command is used the memory buffer is automatically created and sufficient memory allocated. 
This buffer can be freed and the memory recovered with the SPRITE CLOSE command.
~SPRITE WRITE [#]b, x, y (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE WRITE [#]b, x, y (VGA AND HDMI VERSIONS ONLY)
Will copy sprite '#b' to the display. The destination coordinate is 'x' and 'y' and the width/height of the buffer to copy is 'w' and 'h'. 
The optional 'mode' parameter defaults to 4 and specifies how the stored image data is changed as it is written out. 
It is the bitwise AND of the following values: 
&B001 = mirrored left to right
&B010 = mirrored top to bottom
&B100 = don't copy transparent pixels
~SPRITE LOAD fname$ [,start_sprite_number] [,mode] (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE LOAD fname$ [,start_sprite_number] [,mode] (VGA AND HDMI VERSIONS ONLY)
Loads the file 'fname$' which must be formatted as an original Colour Maximite sprite file. 
See the original Colour Maximite MMBasic Language Manual for the file format. 
Multiple sprite files can be loaded by specifying a different 'start_sprite_number' for each file. 
The programmer is responsible for making sure that the sprites do not overlap. 
Mode defaults to zero in which case the CMM1/CMM2 colour codes are used:
(Black, Blue, Green, Cyan, Red, Magenta, Yellow, White, Myrtle, Cobalt, Midgreen, Cerulean, Rust, Fuchsia, Brown, Lilac); 

If mode is specified as 1 then the RGB121 colour codes are used: 
(Black, Blue, Myrtle, Cobalt, Midgreen, Cerulean, Green, Cyan, Red, Magenta, Rust, Fuchsia, Brown, Lilac, Yellow, White).
~SPRITE LOADARRAY [#]n, w, h, array%() (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE LOADARRAY [#]n, w, h, array%() (VGA AND HDMI VERSIONS ONLY)
Creates the sprite 'n' with width 'w' and height 'h' by reading w*h RGB888 values from 'array%()'. 
The RGB888 values must be stored in order of columns across and then rows down starting at the top left. 
This allows the programmer to create simple sprites in a program without needing to load them from disk or read them from the display. 
The firmware will generate an error if 'array%()' is not big enough to hold the number of values required.
~SPRITE LOADBMP [#]b, fname$ [,x] [,y] [,w] [,h] (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE LOADBMP [#]b, fname$ [,x] [,y] [,w] [,h] (VGA AND HDMI VERSIONS ONLY)
Will load a blit buffer from a 24-bit bmp image file. 
'x' and 'y' define the start position in the image to start loading and 'w' and 'h' specify the width and height of the area to be loaded. 
eg, BLIT LOAD #1,"image1", 50,50,100,100 will load an area of 100 pixels square with the top left had corner at 50, 50 from the image image1.bmp
~SPRITE LOADPNG [#]b, fname$ [,transparent] [,alphacut] (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE LOADPNG [#]b, fname$ [,transparent] [,alphacut] (VGA AND HDMI VERSIONS ONLY)
Loads SPRITE number 'b' from the png file 'fname$'. 
If no extension is specified .png will be automatically added to the filename. The file must be in RGBA8888 format which is the normal default. 
The optional parameter 'transparent' (defaults to 0) specifies one of the colour codes (0-15) which will be allocated to pixels in the png file with an alpha value less than 'alphacut' (defaults to 20). 
The variable transparency can then used with the command SPRITE SET TRANSPARENT n or FRAMEBUFFER LAYER n to display the sprite with the transparent region hidden.
~SPRITE MOVE (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE MOVE (VGA AND HDMI VERSIONS ONLY)
Actions a single atomic transaction that re-locates all sprites which have previously had a location change set up using the SPRITE NEXT command. 
Collisions are detected once all sprites are moved and reported in the same way as from a scroll 
~SPRITE NEXT [#]n, x, y (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE NEXT [#]n, x, y (VGA AND HDMI VERSIONS ONLY)
Sets the X and Y coordinate of the sprite to be used when the screen is next scrolled or the SPRITE MOVE command is executed. 
Using SPRITE NEXT rather than SPRITE SHOW allows multiple sprites to be moved as part of the same atomic transaction.
~SPRITE SCROLL x, y [,col] (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE SCROLL x, y [,col] (VGA AND HDMI VERSIONS ONLY)
Scrolls the background and any sprites on the active framebuffer (L or N) 'x' pixels to the right and 'y' pixels up. 
'x' can be any number between - MM.HRES-1 and MM.HRES-1, 'y' can be any number between -MM.VRES-1 and MM.VRES-1. 
Sprites on any layer other than zero will remain fixed in position on the screen. 
By default the scroll wraps the image round. If 'col' is specified the colour will replace the area behind the scrolled image. 
If 'col' is set to -1 the scrolled area will be left untouched.
~SPRITE SET TRANSPARENT n (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE SET TRANSPARENT n (VGA AND HDMI VERSIONS ONLY)
Sets the colour code (0-15) which will be used as transparent when sprites are displayed over a background (defaults to 0).
~SPRITE SHOW [#]n, x,y, layer [,options] (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE SHOW [#]n, x,y, layer [,options] (VGA AND HDMI VERSIONS ONLY)
Displays sprite 'n' on the screen with the top left at coordinates 'x', 'y'. 
Sprites will only collide with other sprites on the same layer, layer zero, or with the screen edge. 
If a sprite is already displayed on the screen, then the SPRITE SHOW command acts to move the sprite to the new location. 
The display background is stored as part of the command and will be replaced when the sprite is hidden or moved further. 
The parameter 'options' is optional and can be set as follows:
bit 0 set - mirrored left to right
bit 1 set - mirrored top to bottom
bit 2 set - black pixels not treated as transparent default is 0
~SPRITE SHOW SAFE [#]n, x,y, layer [,orientation] [,ontop] (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE SHOW SAFE [#]n, x,y, layer [,orientation] [,ontop] (VGA AND HDMI VERSIONS ONLY)
Shows a sprite and automatically compensates for any other sprites that overlap it. 
If the sprite is not already being displayed the command acts exactly the same as SPRITE SHOW. 
If the sprite is already shown it is moved and remains in its position relative to other sprites based on the original order of writing. 
i.e. if sprite 1 was written before sprite 2 and it is moved to overlap sprite 2 it will display under sprite 2. 
If the optional "ontop" parameter is set to 1 then the sprite moved will become the newest sprite and will sit on top of any other sprite it overlaps. 
Refer to SPRITE SHOW for details of the orientation parameter.
~SPRITE SWAP [#]n1, [#]n2 [,orientation] (VGA AND HDMI VERSIONS ONLY),GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE SWAP [#]n1, [#]n2 [,orientation] (VGA AND HDMI VERSIONS ONLY)
Replaces the sprite 'n1' with the sprite 'n2'. The sprites must have the same width and height and 'n1' must be displayed or an error will be generated. 
Refer to SPRITE SHOW for details of the orientation parameter. 
The replacement sprite inherits the background from the original as well as its position in the list of order drawn.
~STATIC variable [, variables] ,VARIABLES,*
------------------------------------
STATIC variable [, variables] 
See DIM for the full syntax. Defines a list of variable names which are local to the subroutine or function. 
These variables will retain their value between calls to the subroutine or function (unlike variables created using the LOCAL command). 
This command uses exactly the same syntax as DIM. 
The only difference is that the length of the variable name created by STATIC and the length of the subroutine or function name added together cannot exceed 32 characters. 
Static variables can be initialised to a value. This initialisation will take effect only on the first call to the subroutine (not on subsequent calls). 
~SUB xxx (arg1 [,arg2, ...]) <statements> END SUB ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
SUB xxx (arg1 [,arg2, ...]) <statements> END SUB 

SUB xxx (arg1 [,arg2, ...]) 
<statements> 
<statements> 
END SUB 


Defines a callable subroutine. This is the same as adding a new command to MMBasic while it is running your program. 
'xxx' is the subroutine name and it must meet the specifications for naming a variable. 
'arg1', 'arg2', etc are the arguments or parameters to the subroutine. An array is specified by using empty brackets. i.e. arg3(). 
The type of the argument can be specified by using a type suffix (i.e. arg1$) or by specifying the type using AS <type> (i.e. arg1 AS STRING). 
Every definition must have one END SUB statement. When this is reached the program will return to the next statement after the call to the subroutine. 
The command EXIT SUB can be used for an early exit. You use the subroutine by using its name and arguments in a program just as you would a normal command. 
For example: 
MySub a1, a2 
When the subroutine is called each argument in the caller is matched to the argument in the subroutine definition. 
These arguments are available only inside the subroutine. Subroutines can be called with a variable number of arguments. 
Any omitted arguments in the subroutine's list will be set to zero or a null string. 
Arguments in the caller's list that are a variable and have the correct type will be passed by reference to the subroutine. 
This means that any changes to the corresponding argument in the subroutine will also be copied to the caller's variable 
and therefore may be accessed after the subroutine has ended. Arrays are passed by specifying the array name with empty brackets 
(e.g. arg()) and are always passed by reference. Brackets around the argument list in both the caller and the definition are optional. 
~SYNC time% [,period] or SYNC,TIME,*
------------------------------------
SYNC time% [,period] or SYNC
The SYNC command allows the user to implement very precisely timed repeated actions (1-2 microseconds accuracy). 
To enable this the command is first called with the parameter time%. This sets up a repeating clock for time% microseconds. 
The optional parameter 'period' modifies the time and can be "U" for microseconds, "M" for milliseconds or "S" for seconds. 
Once the clock is set up the program is synchronised to it using the SYNC command without parameters. 
This waits for the clock period to expire. For periods below 2 ms this is non-interruptible. Above 2 ms the program will respond to Ctrl-C but not any MMBasic interrupts. 
Typical use is to set the clock outside of a loop and then at the top of the loop call the SYNC command without parameters. 
This means the contents of the loop will be executed exactly once for each clock period set. 
For example, the following would drive a servo with the required precise 50Hz timing:
SYNC 20, M
DO
SYNC
PULSE GP0,n
LOOP
~TEMPR START pin [, precision] ,PERIPHERAL,PIN,*
------------------------------------
TEMPR START pin [, precision] 
Normally the TEMPR() function alone is sufficient to make a temperature measurement so usage of this command is optional. 
This command will start the measurement on the temperature sensor. 
The program can then attend to other duties while the measurement is running and later use the TEMPR() function to get the reading. 
If the TEMPR() function is used before the conversion time has completed the function will wait for the remaining conversion time before returning the value. 
Any number of these conversions (on different pins) can be started and be running simultaneously. 
'precision' is the resolution of the measurement and is optional. It is a number between 0 and 3 meaning: 
0 = 0.5 C resolution, 100 ms conversion time. 
1 = 0.25 C resolution, 200 ms conversion time (this is the default). 
2 = 0.125 C resolution, 400 ms conversion time. 
3 = 0.0625 C resolution, 800 ms conversion time. 
~TEXT x, y, string$ [,alignment$] [, font] [, scale] [, c] [, bc] ,GRAPHIC,LCD,PRINT,FONTS,LCD,*
------------------------------------
TEXT x, y, string$ [,alignment$] [, font] [, scale] [, c] [, bc] 
Displays a string on the VGA screen starting at 'x' and 'y'. 
'string$' is the string to be displayed. Numeric data should be converted to a string and formatted using the Str$() function. 
' alignment$' is a string expression or string variable consisting of 0, 1 or 2 letters where the first letter is the horizontal alignment around 'x' 
and can be L, C or R for LEFT, CENTER, RIGHT and the second letter is the vertical alignment around 'y' and can be T, M or B for TOP, MIDDLE, BOTTOM. 
The default alignment is left/top. 
For example. 
"CM" will centre the text vertically and horizontally. The 'alignment$' string can be a constant (e.g. "CM") or it can be a string variable. 
For backwards compatibility with earlier versions of MMBasic the string can also be unquoted (e.g. CM). 
A third letter can be used in the alignment string to specify the rotation of the text. 
This can be 'N' for normal orientation, 'V' for vertical text with each character under the previous running from top to bottom, 'I' the text will be inverted 
(i.e. upside down), 'U' the text will be rotated counter clockwise by 90 degree and 'D' the text will be rotated clockwise by 90 degree 
'font' and 'scale' are optional and default to that set by the FONT command. 
'c' is the drawing colour and 'bc' is the background colour. 
They are optional and default to the current foreground and background colours. See the section Graphics Commands and Functions for a definition of the colours and graphics coordinates. 
~TILE x, y [,foreground] [,background] [,nbr_tiles_wide]  [,nbr_tiles_high] ,GRAPHIC,COLORS,*
------------------------------------
TILE x, y [,foreground] [,background] [,nbr_tiles_wide]  [,nbr_tiles_high] 
Sets the colour for one or more tiles on the screen. 
When in monochrome mode by default the video screen is split up into 80x40 tiles each 8x12 pixels. 
This matches font 1 and allows full colour coding in the editor in monochrome mode. 
Each tile can have a different foreground and background named colour assigned to it from the following: 
white, yellow, lilac, brown, fuchsia, rust, magenta, red, cyan, green, cerulean, midgreen, cobalt, myrtle, blue and black. 

'x' and 'y' are the coordinates of the start block (0-79, 0-39) 'foreground ' and 'background' are the new colours selected. 
'nbr_tiles_wide' and 'nbr_tiles_high' are the number of tiles to change. 
The change is instant and does not affect the text or graphics currently displayed in the tiles (just the colours).
~TILE HEIGHT n ,GRAPHIC,COLORS,*
------------------------------------
TILE HEIGHT n 
Sets the height of the tiles. 'n' can be between 12 and 480 (RP2040) or between 8 and 480 (RP2350)
~TIME$ = "HH:MM:SS" or TIME$ = "HH:MM" or TIME$ = "HH" ,TIME,*
------------------------------------
TIME$ = "HH:MM:SS" or TIME$ = "HH:MM" or TIME$ = "HH" 
Sets the time of the internal clock. MM and SS are optional and will default to zero if not specified. 
For example 
TIME$ = "14:30" 
will set the clock to 14:30 with zero seconds. 
With 
OPTION RTC AUTO ENABLE 
the PicoMite firmware starts with the TIME$ programmed in RTC. 
Without OPTION RTC AUTO ENABLE the firmware starts with TIME$="00:00:00"
~TIMER = msec ,TIME,*
------------------------------------
TIMER = msec 
Resets the timer to a number of milliseconds. Normally this is just used to reset the timer to zero but you can set it to any positive integer. 
See the TIMER function for more details. 
~TRACE ON TRACE ON/OFF or  TRACE OFF or TRACE LIST nn ,WORKFLOW,*
------------------------------------
TRACE ON TRACE ON/OFF or  TRACE OFF or TRACE LIST nn 
Will turn on/off the trace facility. 
This facility will print the number of each line (counting from the beginning of the program) in square brackets as the program is executed. 
This is useful in debugging programs. Will list the last 'nn' lines executed in the format described above. 
MMBasic is always logging the lines executed so this facility is always available (i.e. it does not have to be turned on). 
~TRIANGLE X1, Y1, X2, Y2, X3, Y3 [, C [, FILL]] ,DRAWING,GRAPHIC,*
------------------------------------
TRIANGLE X1, Y1, X2, Y2, X3, Y3 [, C [, FILL]] 
Draws a triangle on the VGA screen with the corners at X1, Y1 and X2, Y2 and X3, Y3. 
'C' is the colour of the triangle and defaults to the current foreground colour. 
'FILL' is the fill colour and defaults to no fill (it can also be set to -1 for no fill). 
All parameters can be expressed as arrays and the software will plot the number of triangles as determined by the dimensions of the smallest array 
unless X1 = Y1 = X2 = Y2 = X3 = Y3 = -1 in which case processing will stop at that point 'x1', 'y1', 'x2', 'y2', 'x3',and 'y3' must all be arrays 
or all be single variables /constants otherwise an error will be generated 
'c' and 'fill' can be either arrays or single variables/constants. 
~UPDATE FIRMWARE ,WORKFLOW,*
------------------------------------
UPDATE FIRMWARE 
Causes the PicoMite firmware to enter the firmware update mode (the same as applying power while holding down the BOOTSEL button). 
This command is only available at the command prompt.
~VAR SAVE var [, var]... or VAR RESTORE or VAR CLEAR,VARIABLES,*
------------------------------------
VAR SAVE var [, var]... or VAR RESTORE or VAR CLEAR
VAR SAVE 
Will save one or more variables to non-volatile flash memory where they can be restored later (normally after a power interruption). 
'var' can be any number of numeric or string variables and/or arrays. Arrays are specified by using empty brackets. 
For example: 
var()
The VAR SAVE command can be used repeatedly. 
Variables that had been previously saved will be updated with their new value and any new variables (not previously saved) will be added to the saved list for later restoration. 
VAR RESTORE will retrieve the previously saved variables and insert them (and their values) into the variable table. VAR CLEAR will erase all saved variables. 
This command is normally used to save calibration data, options, and other data which does not change often but needs to be retained across a power interruption. 
Normally the VAR RESTORE command is placed at the start of the program so that previously saved variables are restored and immediately available to the program when it starts. 
Notes: 
The storage space available to this command is 16KB.
Using VAR RESTORE without a previous save will have no effect and will not generate an error.
If, when using RESTORE, a variable with the same name already exists its value will be overwritten.
Saved arrays must be declared (using DIM) before they can be restored.
Be aware that string arrays can rapidly use up all the memory allocated to this command. 
The LENGTH qualifier can be used when a string array is declared to reduce the size of the array (see the DIM command). 
This is not needed for ordinary string variables.
The saved variables will be automatically cleared by a firmware upgrade, by the NEW command or when a new program is loaded via AUTOSAVE, XMODEM, etc.
~WATCHDOG timeout or WATCHDOG OFF or WATCHDOG HW timeout or WATCHDOG HW OFF  ,TIME,HARDWARE,PROGRAMCONTROL,*
------------------------------------
WATCHDOG timeout or WATCHDOG OFF or WATCHDOG HW timeout or WATCHDOG HW OFF  
Starts the watchdog timer which will automatically restart the processors when it has timed out. 
This can be used to recover from some event that disabled the running program (such as an endless loop or a programming or other error that halts a running program). 
This can be important in an unattended control situation. 
The timeout can either be processed in the system timer interrupt (WATCHDOG command) or as a true CPU/hardware watchdog (WATCHDOG HW command). 
If the hardware watchdog is used the timer has a maximum of 8.3 seconds. No such limitation exists for the software watchdog. 
'timeout' is the time in milliseconds (ms) before a restart is forced. 
This command should be placed in strategic locations in the running BASIC program to constantly reset the watchdog timer (to 'timeout') and 
therefore prevent it from counting down to zero. 
If the timer count does reach zero (perhaps because the BASIC program has stopped running) the PicoMite firmware will be automatically restarted 
and the automatic variable MM.WATCHDOG will be set to true (i.e. 1) indicating that an error occurred. 
On a normal startup MM.WATCHDOG will be set to false (i.e. 0). 
Note that OPTION AUTORUN must be specified for the program to restart.  

WATCHDOG OFF can be used to disable the watchdog timer (this is the default on a reset or power up). 
The timer is also turned off when the break character (CTRL-C) is used on the console to interrupt a running program.
~WII [CLASSIC] OPEN [,interrupt]PERIPHERAL,WII*
------------------------------------
WII [CLASSIC] OPEN [,interrupt]
Opens a WII Classic controller and implements background polling of the device. 
The Wii Classic must be wired to the pins specified by OPTION SYSTEM I2C which is a prerequisite. 
Open attempts to talk to the Wii Classic and will return an error if not found. 
If found the firmware will sample the Wii data in the background at a rate of 50Hz. 
If an optional user interrupt is specified this will be triggered if any of the buttons changes (both on and off) 
See the DEVICE function for how to read data from the Wii Classic.
~WII [CLASSIC] CLOSEPERIPHERAL,WII*
------------------------------------
WII [CLASSIC] CLOSE
CLOSE will stop the background polling and disable any interrupt specified
~WII NUNCHUCK OPEN [,interrupt]PERIPHERAL,WII*
------------------------------------
WII NUNCHUCK OPEN [,interrupt]
Opens a WII Nunchuck controller and implements background polling of the device. 
The Wii Nunchuck must be wired to the pins specified by OPTION SYSTEM I2C which is a prerequisite. 
Open attempts to talk to the Wii Nunchuck and will return an error if not found. 
If found the firmware will sample the Wii data in the background at a rate of 50Hz. 
If an optional user interrupt is specified this will be triggered if either of the buttons changes (both on and off) 
See the DEVICE function for how to read data from the Wii Nunchuck.
~WII NUNCHUCK CLOSEPERIPHERAL,WII*
------------------------------------
WII NUNCHUCK CLOSE
CLOSE will stop the background polling and disable any interrupt specified
~WEB  WEBMITE ONLY,WEB,CONNECTIONS,*
------------------------------------
WEB  WEBMITE ONLY
Webmite WEB commands
The WEB commands are used to manage the Internet capability of the WebMite.
~WEB CONNECT [ssid$, passwd$, [name$] [,ipaddress$, mask$, gateway$]],WEB,CONNECTIONS,*
------------------------------------
WEB CONNECT [ssid$, passwd$, [name$] [,ipaddress$, mask$, gateway$]]
This command, with no optional parameters, will connect to the default network if possible (as previously set with OPTION WIFI) 
or with the optional parameters will connect to the network specified and also set up the OPTION WIFI for future use.
~WEB MQTT CONNECT addr$, port, user$, passwd$ [, interrupt],WEB,CONNECTIONS,*
------------------------------------
WEB MQTT CONNECT addr$, port, user$, passwd$ [, interrupt]
Connect to an MQTT Broker. 
'addr$' is the IP address, 
'port' is the port number to use, 
'user$' is the user name, 
'passwd$' is the account's password and 
'interrupt' is optional and if specified is the subroutine to call when a message is received. 
WEB CONNECT does not disconnect from a previously connected network so should only be used where nothing has been previously set up 
or where a previously configured network is not active or a previously configured network has failed to connect on boot (no parameters) 
~WEB MQTT PUBLISH topic$, msg$, [,qos] [,retain] ,WEB,CONNECTIONS,*
------------------------------------
WEB MQTT PUBLISH topic$, msg$, [,qos] [,retain] 
Publish content to an MQTT broker topic. 
'topic$' is the topic name and 'msg$' is the message/ 'qos' is the optional quality of service with values of 0, 1 or 2 (default is 1). 
~WEB MQTT SUBSCRIBE topic$ [,qos],WEB,CONNECTIONS,*
------------------------------------
WEB MQTT SUBSCRIBE topic$ [,qos]
Subscribe to an MQTT broker topic. 'topic$' is the topic name and 'qos' is the optional quality of service with values of 0, 1 or 2 (default is 1). 
~WEB MQTT UNSUBSCRIBE topic$,WEB,CONNECTIONS,*
------------------------------------
WEB MQTT UNSUBSCRIBE topic$
Unsubscribe from an MQTT broker topic. 'topic$' is the topic name.
~WEB MQTT CLOSE,WEB,CONNECTIONS,*
------------------------------------
WEB MQTT CLOSE
Close a persistent MQTT Connection. 
~WEB NTP [timeoffset [,NTPserver$]] [,timeout]]],WEB,CONNECTIONS,*
------------------------------------
WEB NTP [timeoffset [,NTPserver$]] [,timeout]]]
Get the date/time from an NTP server and set the internal WebMite date/time clock. 
' timeoffset' is the local time zone, and if omitted the date/time will be set to GMT. 
' NTPserver$' is the timeserver to use and if omitted will default to an international timeserver pool. 
'timeout' is the optional time out in milliseconds and defaults to 5000.
~WEB OPEN TCP CLIENT address$, port,WEB,CONNECTIONS,*
------------------------------------
WEB OPEN TCP CLIENT address$, port
Opens a TCP client connection to a WEB server. 
'address$' is a string and is the address of the server to connect to. 
It can be either a URL (eg, "api.openweathermap.org") or an IP address (eg, "192.168.1.111"). 
'port' is the number of the port to use. Used with WEB TCP CLIENT REQUEST to interrogate the server.  
Note that one CLIENT  connection is allowed.
~WEB OPEN TCP STREAM address$, port,WEB,CONNECTIONS,*
------------------------------------
WEB OPEN TCP STREAM address$, port
Opens a TCP client connection to a WEB server like WEB OPEN TCP CLIENT but connects the WEB TCP CLIENT STREAM receiver logic 
rather than the logic for WEB TCP CLIENT REQUEST. 
'address$' is a string and is the address of the server to connect to. 
It can be either a URL (eg, "api.openweathermap.org") or an IP address (eg,"192.168.1.111"). 
'port' is the number of the port to use. 
Note that one CLIENT connection is allowed.
~WEB SCAN [array%()],WEB,CONNECTIONS,*
------------------------------------
WEB SCAN [array%()]
Scans for all available wifi connections. 
If 'array%()' is specified the output will be stored in a Longstring, otherwise output will be to the console. 
The command can be used whether ot not an network connection is already active.
~WEB TCP CLIENT REQUEST request$, buff%() [,timeout],WEB,CONNECTIONS,*
------------------------------------
WEB TCP CLIENT REQUEST request$, buff%() [,timeout]
Send a request to the remote server opened with WEB OPEN TCP CLIENT and wait for an answer. 
'request$' is a string and is the request to be sent to the server. 
'buff%()' is an integer array which will receive the response as a LONGSTRING. The size of this buffer will limit the amount of data received from the server. 
'timeout' is the optional time out in milliseconds and defaults to 5000. 
If the request times out an error will occur, otherwise the received data will be saved in the LONGSTRING 'buff%()'. 
If the received data is a JSON string then the JSON$() function can be used to parse it. 
~WEB TCP CLIENT STREAM command$, buffer%(), readpointer%, writepointer%,WEB,CONNECTIONS,*
------------------------------------
WEB TCP CLIENT STREAM command$, buffer%(), readpointer%, writepointer%
Connects to a server previously opened with WEB OPEN TCP STREAM. 
'command$' is a string and is the request to be sent to the server. 
'buffer%()' is an integer array which will receive the ongoing responses and acts as a  circular buffer of bytes received. 
The firmware maintains the parameter 'writepointer%' as the data from the server arrives.
'readpointer%' should be maintained by the Basic program as it removes data from the circular buffer. 
If 'writepointer%' catches up with 'readpointer%' then 'readpointer%' will be incremented to stay one byte ahead and incoming data will be lost. 
This command is designed to be compatible with the PLAY STREAM command to allow the implementation of streaming internet audio.
~WEB CLOSE TCP CLIENT,WEB,CONNECTIONS,*
------------------------------------
WEB CLOSE TCP CLIENT
Closes the connection to the remote server opened with WEB OPEN TCP CLIENT. This must be done before another open is attempted. 
~WEB TCP INTERRUPT InterruptSub,WEB,CONNECTIONS,*
------------------------------------
WEB TCP INTERRUPT InterruptSub
Start the TCP server running. 
'InterruptSub' is the subroutine to call when a request is made of the TCP server (ie, an interrupt). 
Note that the OPTION WIFI command must have been used first followed by the OPTION TCP SERVER PORT command to enable the TCP server. 
~WEB TCP READ cb%, buff%(),WEB,CONNECTIONS,*
------------------------------------
WEB TCP READ cb%, buff%()
Read the data from a potential TCP connection' cb%'. 
' buff%()' is an array to receive any data from that connection as a longstring. 
The size of this buffer will limit the amount of data received from the remote client. 
If there is nothing received on that connection this will return an empty string (ie, LLEN(buff%())=0). 
If there is data that has been received then the BASIC program must respond with one of the WEB TRANSMIT commands in order to respond and close the connection.
~WEB TCP SEND cb%, data%() or WEB TCP CLOSE cb%,WEB,CONNECTIONS,*
------------------------------------
WEB TCP SEND cb%, data%() or WEB TCP CLOSE cb%
These two commands allow more flexibility in using the TCP server. 
Unlike WEB TRANSMIT PAGE or WEB TRANSMIT FILE, WEB TCP SEND does not create any sort of header, nor does it close the TCP connection after transmission. 
It just sends exactly what is in the LONGSTRING data%() and it is up to the Basic programmer to close the connection when appropriate. 
~WEB TRANSMIT CODE cb%, nnn%,WEB,CONNECTIONS,*
------------------------------------
WEB TRANSMIT CODE cb%, nnn%
Send a numerical response to the open TCP connection 'cb%' and then closes the connection. 
Typical use would be TRANSMIT CODE cb%, 404 to indicate page not found.
~WEB TRANSMIT FILE cb%, filename$, content-type$,WEB,CONNECTIONS,*
------------------------------------
WEB TRANSMIT FILE cb%, filename$, content-type$
Constructs an HTTP 1.1 header with the 'content-type$' as specified, 
sends it and then sends the contents of the file to the open TCP connection cb% and on completion, closes the connection. 
'content-type$' is a MIME type expressed as a string. Eg, "image/jpeg" 
~WEB TRANSMIT PAGE cb%, filename$ [,buffersize],WEB,CONNECTIONS,*
------------------------------------
WEB TRANSMIT PAGE cb%, filename$ [,buffersize]
Constructs an HTTP 1.1 header, sends it and then sends the contents of the file to the open TCP connection cb% and on completion closes the connection. 
MMBasic will substitute current values for any MMBasic variables or expressions defined in the file inside curly brackets eg, {myvar%}. 
Variables can be simple, array elements or expressions. An opening curly bracket can be included in the output by using {{. 
By default the command allocates a buffer the size of the file + 4096 bytes to build the page to transmit. 
However, if the page is complex and includes many MMBasic variables that yield text bigger than the variable name 
it is possible that the buffer will not be big enough. In this case the user can specify the extra space required (defaults to 4096 if not specified)
~WEB UDP INTERRUPT intname,WEB,CONNECTIONS,*
------------------------------------
WEB UDP INTERRUPT intname
Sets up a BASIC interrupt routine that will be triggered whenever a UDP datagram is received. 
The contents will be saved in MM.MESSAGE$. The IP address of the sender will be stored in MM.ADDRESS$. 
~WEB UDP SEND addr$, port, data$,WEB,CONNECTIONS,*
------------------------------------
WEB UDP SEND addr$, port, data$
Used to send a datagram to a remote receiver. 
In this case the IP address must be specified and can be either a numeric address (eg, "192.168.1.147") or a normal text address (eg, "google.com"). 
The port number of the receiver must also be specified and the message itself. The SEND command can be used as a response to an incoming message or stand-alone.
~WS2812 type, pin, nbr, value%[()],WS2812,PERIPHERAL,HARDWARE,PIN,*
------------------------------------
WS2812 type, pin, nbr, value%[()]
This command will drive one or more WS2812 LED chips connected to 'pin'. 
Note that the pin must be set to a  digital output before this command is used. 

'type' is a single character specifying the type of chip being driven:
O = original WS2812
B = WS2812B
S = SK6812
W =SK6812W (RGBW)

'nbr' is the number of LEDs in the chain (1 to 256). 
The 'value%()' array should be an integer array sized to have exactly the same number of elements as the number of LEDs to be driven. 
For the first three variants each element in the array should contain the colour in the normal RGB888 format (i.e. 0 to &HFFFFFF). 
For type W use a RGBW value (0-&HFFFFFFFF). If only one LED is connected then a single integer should be used for 'value%' (ie, not an array).

WS2812 SUPPORT

The PicoMite firmware has built in support for the WS2812 multicolour LED chip. This chip needs a very specific timing to work properly and with the DEVICE WS2812 command it is easy to control these devices with minimal effort.
This command will output the required signals needed to drive a chain of WS2812 LED chips connected to the pin specified and set the colours of each LED in the chain. 
The syntax of the command is: 
WS2812 type, pin, nbr%, colours%[()]

Each element in the array should contain the colour in the normal RGB888 format (0 - HFFFFFF). Where a single LED is to be driven then colours% should be a simple variable.
Up to 256 WS2812 chips in a string are supported.
'type' is a single character specifying the type of chip being driven as follows:
O = original WS2812
B = WS2812B
S = SK6812
W = SK6812W (RGBW)

As an example:
DIM b%(4)=(RGB(red), Rgb(green), RGB(blue), RGB(Yellow), rgb(cyan))
SETPIN GP5, DOUT
WS2812 O, GP5, 5, b%()
will output the specified colours to an array of five WS2812 LEDs daisy chained off pin GP5.
It is possible that a WS2812 will not work reliably with the 3.3V output from the Raspberry Pi Pico. In this case there are a number of solutions:
-> Use the WS2812B which will work with a 3.3V supply and inputs.
-> Use the Raspberry Pi Pico 2 which can tolerate 5V (while powered) so, in this case, level shifting is not required..
-> Use a single WS2812 powered from 3.3V as a first stage to buffer the input of the first "real" LED in the string. The minimum supply for the WS2812 is 4V but in many cases it will work at 3.3V.
~XMODEM SEND or XMODEM SEND file$ or XMODEM RECEIVE or XMODEM RECEIVE file$ or XMODEM CRUNCH,WORKFLOW,*
------------------------------------
XMODEM SEND or XMODEM SEND file$ or XMODEM RECEIVE or XMODEM RECEIVE file$ or XMODEM CRUNCH
Transfers a BASIC program to or from a remote computer using the Xmodem protocol. 
The transfer is done over the USB console connection. XMODEM SEND will send the current program held in the PicoMite's program memory to the remote device.
XMODEM RECEIVE will accept a program sent by the remote device and save it into the PicoMite's the program memory overwriting the program currently held there. 
In both cases you can also specify 'file$' which will transfer the data to/from a file on the Flash Filesystem or SD Card. 
If the file already exists it will be overwritten when receiving a file. 
Note that the data is buffered in RAM which limits the maximum transfer size. 

This command also creates a backup of the program in flash memory which will be automatically retrieved if the CPU is reset of the power is lost. 
The CRUNCH option works like RECEIVE but will remove all comments, blank lines and unnecessary spaces from the program before saving. 
This can be used on large programs to allow them to fit into limited memory. 
SEND, RECEIVE and CRUNCH can be abbreviated to S, R and C.  
The XModem protocol requires a cooperating software program running on the remote computer and connected to its serial port. 
It has been tested on Tera Term running on Windows and it is recommended that this be used.  
After running the XMODEM command in MMBasic select: File -> Transfer -> XMODEM -> Receive/Send from the Tera Term menu to start the transfer. 
The transfer can take up to 15 seconds to start and if the XMODEM command fails to establish communications it will return to the MMBasic prompt 
after 60 seconds and leave the program memory untouched. Download Tera Term from http://ttssh2.sourceforge.jp/
~MMBASIC Functions ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
MMBASIC Functions 
Functions related to communications functions (I2C, 1-Wire, and SPI) are described in the appendices  
Square brackets indicate that the parameter or characters are optional. 
~ABS( number ) ,MATH,MATH FUNCTIONS,*
------------------------------------
ABS( number ) 
Returns the absolute value of the argument 'number' (i.e. any negative sign is removed and a positive number is returned). 
~ACOS( number ) ,MATH,MATH FUNCTIONS,*
------------------------------------
ACOS( number ) 
Returns the inverse cosine of the argument 'number' in radians. 
~ASC( string$ ) ,MATH,MATH FUNCTIONS,*
------------------------------------
ASC( string$ ) 
Returns the ASCII code (i.e. byte value) for the first letter in 'string$'. 
~ASIN( number ) ,MATH,MATH FUNCTIONS,*
------------------------------------
ASIN( number ) 
Returns the inverse sine value of the argument 'number' in radians. 
~ATN( number ) ,MATH,MATH FUNCTIONS,*
------------------------------------
ATN( number ) 
Returns the arctangent of the argument 'number' in radians. 
~ATAN2( y, x ) ,MATH,MATH FUNCTIONS,*
------------------------------------
ATAN2( y, x ) 
Returns the arc tangent of the two numbers x and y as an angle expressed in radians. 
It is similar to calculating the arc tangent of y / x, except that the signs of both arguments are used to determine the quadrant of the result. 
~BIN$( number [, chars]) ,STRINGS,CONVERSION,CONVERSION,*
------------------------------------
BIN$( number [, chars]) 
Returns a string giving the binary (base 2) value for the 'number'. 
'chars' is optional and specifies the number of characters in the string with zero as the leading padding character(s). 
~BIN2STR$(type, value [,BIG]) ,STRINGS,CONVERSION,CONVERSION,*
------------------------------------
BIN2STR$(type, value [,BIG]) 
Returns a string containing the binary representation of 'value'. 'type' can be: 
INT64 signed 64-bit integer converted to an 8 byte string 
UINT64 unsigned 64-bit integer converted to an 8 byte string 
INT32 signed 32-bit integer converted to a 4 byte string 
UINT32 unsigned 32-bit integer converted to a 4 byte string 
INT16 signed 16-bit integer converted to a 2 byte string 
UINT16 unsigned 16-bit integer converted to a 2 byte string 
INT8 signed 8-bit integer converted to a 1 byte string 
UINT8 unsigned 8-bit integer converted to a 1 byte string 
SINGLE single precision floating point number converted to a 4 byte string 
DOUBLE double precision floating point number converted to a 8 byte string 

By default the string contains the number in little-endian format (i.e. the least significant byte is the first one in the string). 
Setting the third parameter to 'BIG' will return the string in big-endian format (i.e. the most significant byte is the first one in the string) 
In the case of the integer conversions, an error will be generated if the 'value' cannot fit into the 'type' (e.g. an attempt to store the value 400 in a INT8). 
This function makes it easy to prepare data for efficient binary file I/O or for preparing numbers for output to sensors and saving to flash memory. 
See also the function STR2BIN 
~BOUND(array() [,dimension] ,ARRAY,*
------------------------------------
BOUND(array() [,dimension] 
This returns the upper limit of the array for the dimension requested. The dimension defaults to one if not specified. 
Specifying a dimension value of 0 will return the current value of OPTION BASE. 
Unused dimensions will return a value of zero. For example: 
DIM myarray(44,45) 
BOUND(myarray(),2) 
will return 45 
~CALL(userfunname$, [,userfunparameters,....]) ,MMBASIC,ENVIRONMENT,CONTROL,*
------------------------------------
CALL(userfunname$, [,userfunparameters,....]) 
This is an efficient way of programmatically calling user defined functions. (See also the CALL command). 
In many cases it can be used to eliminate complex SELECT and IF THEN ELSEIF ENDIF clauses and is processed in a much more efficient manner. 
'userfunname$' can be any string or variable or function that resolves to the name of a normal user function (not an in-built command). 
'userfunparameters' are the same parameters that would be used to call the function directly. 
A typical use for this command could be writing any sort of emulator where one of a large number of functions should be called depending on a some variable. 
It also provides a method of passing a function name to another subroutine or function as a variable. 
~CHOICE(condition, ExpressionIfTrue, ExpressionIfFalse) ,MMBASIC,ENVIRONMENT,CONTROL,CONDITIONAL,*
------------------------------------
CHOICE(condition, ExpressionIfTrue, ExpressionIfFalse) 
This function allows you to do simple either/or selections more efficiently and faster than using IF THEN ELSE ENDIF clauses. 
The condition is anything that will resolve to nonzero (true) or zero (false). 
The expressions are anything that you could normally assign to a variable or use in a command and can be integers, floats or strings. 
Examples: 
PRINT CHOICE(1, "hello","bye") will print "Hello" 
PRINT CHOICE (0, "hello","bye") will print "Bye" 
a=1 : b=1 : PRINT CHOICE (a=b, 4, 5) will print 4 
~CHR$( number ) ,STRINGS,CONVERSION,CONVERT,*
------------------------------------
CHR$( number ) 
~CINT( number ) ,NUMBERS,*
------------------------------------
CINT( number ) 
Round numbers with fractional portions up or down to the next whole number or integer. 
For example,
45.47 will round to 45 
45.57 will round to 46 
-34.45 will round to -34 
-34.55 will round to -35 
See also INT() and FIX(). 
~COS( number ) ,MATH,TRIGONOMETRY,FUNCTIONS,*
------------------------------------
COS( number ) 
Returns the cosine of the argument 'number' in radians. 
~CWD$ ,FILES,*
------------------------------------
CWD$ 
The current working directory on the SD card. Invalid for exFAT format. The format is: A:/dir1/dir2. 
~DATE$ ,TIME,DATE,*
------------------------------------
DATE$ 
Returns the current date based on MMBasic's internal clock as a string in the form "DD-MM-YYYY". 
For example, "28-07-2012". The internal clock/calendar will keep track of the time and date including leap years. 
To set the date use the command DATE$ =. 
~DATETIME$(n) ,TIME,DATE,*
------------------------------------
DATETIME$(n) 
Returns the date and time corresponding to the epoch number n (number of seconds that have elapsed since midnight GMT on January 1, 1970). 
The format of the returned string is "dd-mm-yyyy hh:mm:ss". Use the text NOW to get the current datetime string, i.e. ? DATETIME$(NOW) 
~DAY$(date$) ,TIME,DATE,*
------------------------------------
DAY$(date$) 
Returns the day of the week for a given date as a string "Monday", "Tuesday" etc. 
The format for date$ is "DD-MM-YY", "DD-MM-YYYY", or "YYYY-MM-DD". Use NOW to get the day for the current date, e.g. PRINT DAY$(NOW) 
~DEG( radians ) ,MATH,TRIGONOMETRY,FUNCTIONS,CONVERSION,CONVERT,*
------------------------------------
DEG( radians ) 
Converts 'radians' to degrees. 
~DEVICE(GAMEPAD channel, funct),PERIPHERAL,GAMEPAD,*
------------------------------------
DEVICE(GAMEPAD channel, funct)
Returns data from a USB PS3 or PS4 controller. 'funct' is a 1 or 2 letter code indicating the information to return as follows:
LX the position of the analog left joystick x axis
LY the position of the analog left joystick y axis
RX the position of the analog right joystick x axis
RY the position of the analog right joystick y axis
GX the reading from the X axis gyro (where supported)
GY the reading from the Y axis gyro (where supported)
GZ the reading from the Z axis gyro (where supported)
AX the reading from the X axis accelerometer (where supported)
AY the reading from the Y axis accelerometer (where supported)
AZ the reading from the Z axis accelerometer (where supported)
L the position of the analog left button
R the position of the analog right button
B a bitmap of the state of all the buttons. A bit will be set to 1 if the button is pressed.
T the ID code of the controller 

The button bitmap is as follows:
BIT 0 Button R/R1       
BIT 1 Button start/options
BIT 2 Button home
BIT 3 Button select/share
BIT 4 Button L/L1
BIT 5 Button down cursor
BIT 6 Button right cursor
BIT 7 Button up cursor
BIT 8 Button left cursor
BIT 9 Right shoulder button 2/R2
BIT 10 Button x/triangle
BIT 11 Button a/circle
BIT 12 Button y/square
BIT 13 Button b/cross
BIT 14 Left should button 2/L2
BIT 15 Touchpad
~DEVICE(MOUSE channel, funct),PERIPHERAL,MOUSE,*
------------------------------------
DEVICE(MOUSE channel, funct)
Returns data from a mouse connected via 'channel'. A PS2 mouse is always allocated channel 2. Normally a USB mouse is also allocated to channel 2 but 
this can vary. See MM.INFO(USB n) for more information. 'funct' is a 1 letter 
code indicating the information to return as follows:
X the X coordinate (0 to MM.HRES-1)
Y the Y coordinate (0 to MM.VRES-1)
L the state of the left mouse button
R the state of the right mouse button
M the state of the middle mouse button (wheel click)
D 1 if there has been a double click of the left mouse button
~DEVICE(WII [CLASSIC] funct),PERIPHERAL,WII,*
------------------------------------
DEVICE(WII [CLASSIC] funct)
Returns data from a Wii Classic controller. 'funct' is a 1 or 2 letter code indicating the information to return as follows:
LX the position of the analog left joystick x axis
LY the position of the analog left joystick y axis
RX the position of the analog right joystick x axis
RY the position of the analog right joystick y axis
L the position of the analog left button
R the position of the analog right button
B a bitmap of the state of all the buttons. A bit will be set to 1 if the button is pressed.
T the ID code of the controller - should be hex &HA4200101

The button bitmap is as follows:
BIT 0 Button R
BIT 1 Button start
BIT 2 Button home
BIT 3 Button select
BIT 4 Button L
BIT 5 Button down cursor
BIT 6 Button right cursor
BIT 7 Button up cursor
BIT 8 Button left cursor
BIT 9 Button ZR
BIT 10 Button x
BIT 11 Button a
BIT 12 Button y
BIT 13 Button b
BIT 14 Button ZL
~DEVICE(WII NUNCHUCK funct),PERIPHERAL,WII,*
------------------------------------
DEVICE(WII NUNCHUCK funct)
Returns data from a Wii Classic controller. 'funct' is a 1 or 2 letter code indicating the information to return as follows: 
AX the x axis acceleration
AY the y axis acceleration
AZ the z axis acceleration
JX the position of the joystick x axis
JY the position of joystick y axis
C the state of the C button
Z the state of the Z button
T the ID code of the controller - should be hex &HA4200000
~DIR$( fspec, type ) or DIR$( fspec )  or DIR$( ),FILES,*
------------------------------------
DIR$( fspec, type ) or DIR$( fspec )  or DIR$( )
Will search an SD card for files and return the names of entries found. or 'fspec' is a file specification using wildcards 
the same as used by the FILES command. E.g. "*.*" will return all entries, "*.TXT" will return text files.  
Note that the wildcard *.* does not find files or folders without an extension. 

'type' is the type of entry to return and can be one of: 
VOL Search for the volume label only 
DIR Search for directories only 
FILE Search for files only (the default if 'type' is not specified) The function will return the first entry found. 

To retrieve subsequent entries use the function with no arguments. i.e. DIR$( ). 
The return of an empty string indicates that there are no more entries to retrieve. This example will print all the files in a directory:
f$ = DIR$("*.*", FILE) 
DO WHILE f$ <> "" 
PRINT f$ 
f$ = DIR$() 
LOOP 
You must change to the required directory before invoking this command. 
~DISTANCE( trigger, echo ) or DISTANCE( trig-echo ) ,PERIPHERALS,DISTANCE,*
------------------------------------
DISTANCE( trigger, echo ) or DISTANCE( trig-echo ) 
Measure the distance to a target using the HC-SR04 ultrasonic distance sensor. 
Four pin sensors have separate trigger and echo connections. 
'trigger' is the I/O pin connected to the "trig" input of the sensor and 
'echo' is the pin connected to the "echo" output of the sensor. 
Three pin sensors have a combined trigger and echo connection and in that case you only need to specify one I/O pin to interface to the sensor. 
Note that any I/O pins used with the HC-SR04 should be 5V capable as the HC-SR04 is a 5V device. 

The I/O pins are automatically configured by this function and multiple sensors can be used on different I/O pins. 
The value returned is the distance in centimetres to the target or -1 if no target was detected or -2 if there was an error (i.e. sensor not connected). 

Using a HC-SR04 ultrasonic sensor and the DISTANCE() function you can measure the distance to a target.
This device can be found on eBay for about US$4 and it will measure the distance to a target from 3cm to 3m. It works by sending an ultrasonic sound pulse and measuring the time it takes for the echo to be returned.
Compatible sensors are the SRF05, SRF06, Parallax PING and the DYP-ME007 (which is waterproof and therefore good for monitoring the level of a water tank). 
Others that have been reported as working well use the CS100 chip - such as the HC-SR04 and US-025.
In the PicoMite firmware you use the DISTANCE function as follows:
d = DISTANCE(trig, echo)
The value returned is the distance in centimetres to the target.
Where trig is the I/O pin connected to the "trig" input of the sensor and echo is the pin connected the "echo" output of the sensor. You can also use 3-pin devices and in that case only one pin number is specified.
Note that the maximum voltage on all the Raspberry Pi Pico's I/O pins is 3.3V. Level shifting will be required for this sensor because it uses 5V levels for its echo output. 

The Raspberry Pi Pico 2 can tolerate 5V (while powered) so, in this case, level shifting is not required.
~EOF( [#]fnbr ) ,FILES,*
------------------------------------
EOF( [#]fnbr ) 
Will return true if the file previously opened on the SD card for INPUT with the file number '#fnbr' is positioned at the end of the file. 
For a serial communications port this function will return true if there are no characters waiting in the receive buffer. 
#0 can be used which refers to the console's input buffer. 
The # is optional. Also see the OPEN, INPUT and LINE INPUT commands and the INPUT$ function. 
~EPOCH(DATETIME$) ,TIME,*
------------------------------------
EPOCH(DATETIME$) 
Returns the epoch number (number of seconds that have elapsed since midnight GMT on January 1, 1970) for the supplied DATETIME$ string. 
The format for DATETIME$ is "dd-mm-yyyy hh:mm:ss", "dd-mm-yy hh:mm:ss", or "yyyy-mm-dd hh:mm:ss",. 
Use NOW to get the epoch number for the current date and time, i.e. 
PRINT EPOCH(NOW) 
~EVAL( string$ ) ,MMBASIC,ENVIRONMENT,CONTROL,STRING,*
------------------------------------
EVAL( string$ ) 
'string$' can be a constant, a variable or a string expression. 
The expression can use any operators, functions, variables, subroutines, etc that are known at the time of execution. 
The returned value will be an integer, float or  string depending on the result of the evaluation. 
For example: 
S$ = "COS(RAD(30)) * 100" : PRINT EVAL(S$) 
Will display: 86.6025 
~EXP( number ) ,MATH,TRIGONOMETRY,FUNCTIONS,*
------------------------------------
EXP( number ) 
Returns the exponential value of 'number', i.e. ex where x is 'number'. 
~FIELD$( string1, nbr, string2 [, string3] ) ,STRINGS,*
------------------------------------
FIELD$( string1, nbr, string2 [, string3] ) 
Returns a particular field in a string with the fields separated by delimiters. 
'nbr' is the field to return (the first is nbr 1). 'string1' is the string to search and 'string2' is a string holding the delimiters (more than one can be used). 
'string3' is optional and if specified will include characters that are used to quote text in 'string1' (ie, quoted text will not be searched for a delimiter). 
For example: 
S$ = "foo, boo, zoo, doo" 
r$ = FIELD$(s$, 2, ",") 
will result in r$ = "boo". While: 
s$ = "foo, 'boo, zoo', doo" 
r$ = FIELD$(s$, 2, ",", "'") 
will result in r$ = "boo, zoo". 
~FIX( number ) ,NUMBERS,*
------------------------------------
FIX( number ) 
Truncate a number to a whole number by eliminating the decimal point and all characters to the right of the decimal point. 
For example 9.89 will return 9 and -2.11 will return -2. 
The major difference between FIX() and INT() is that FIX() provides a true integer function (i.e. does not return the next lower number for negative numbers as INT() does). 
This behaviour is for Microsoft compatibility. 
See also CINT() . 
~FORMAT$( nbr [, fmt$] ) ,STRINGS,NUMBERS,*
------------------------------------
FORMAT$( nbr [, fmt$] ) 
Will return a string representing 'nbr' formatted according to the specifications in the string 'fmt$'. 
The format specification starts with a % character and ends with a letter. Anything outside of this construct is copied to the output as is. 
The structure of a format specification is: 
% [flags] [width] [.precision] type Where 'flags' can be: 
-Left justify the value within a given field width 
0 Use 0 for the pad character instead of space 
+ Forces the + sign to be shown for positive numbers space Causes a positive value to display a space for the sign. 

Negative values still show the - sign 'width' is the minimum number of characters to output, less than this the number will be padded, more than this the width will be expanded. 
'precision' specifies the number of fraction digits to generate with an e, or f type or the maximum number of significant digits to generate with a g type. 
If specified, the precision must be preceded by a dot (.). 
'type' can be one of: 
g Automatically format the number for the best presentation. 
f Format the number with the decimal point and following digits 
e Format the number in exponential format 

If uppercase G or F is used the exponential output will use an uppercase E. 
If the format specification is not specified "%g" is assumed. 
Examples: 
format$(45) will return 45 ; format$(45, "%g") 
will return 45 
~GETSCANLINE ,GRAPHIC,VIDEO SYSTEM,*
------------------------------------
GETSCANLINE 
This will report on the line that is currently being drawn on the VGA monitor in the range of 0 to 525. 
This is irrespective of the current MODE. Using this to time updates to the screen can avoid timing effects caused by updates while the screen is being updated. 
The first visible line will return a value of 0. Any line number above 479 is in the frame blanking period. 
~GPS(),GPS,*
------------------------------------
GPS()
The GPS functions are used to return data from a serial communications channel opened as GPS. 
The function GPS(VALID) should be checked before any of these functions are used to ensure that the returned value is valid.
~GPS(ALTITUDE) ,GPS,*
------------------------------------
GPS(ALTITUDE) 
Returns current altitude (if sentence GGA is enabled).
~GPS(DATE) ,GPS,*
------------------------------------
GPS(DATE) 
Returns the normal date string corrected for local time eg, "12-01-2020".
~GPS(DOP) ,GPS,*
------------------------------------
GPS(DOP) 
Returns DOP (dilution of precision) value (if sentence GGA is enabled).
~GPS(FIX) ,GPS,*
------------------------------------
GPS(FIX) 
Returns non zero (true) if the GPS has a fix on sufficient satellites and is producing valid data.
~GPS(GEOID) ,GPS,*
------------------------------------
GPS(GEOID) 
Returns the geoid-ellipsoid separation (if sentence GGA is enabled).
~GPS(LATITUDE) ,GPS,*
------------------------------------
GPS(LATITUDE) 
Returns the latitude in degrees as a floating point number, values are negative for South of equator
~GPS(LONGITUDE) ,GPS,*
------------------------------------
GPS(LONGITUDE) 
Returns the longitude in degrees as a floating point number, values are negative for West of the meridian.
~GPS(SATELLITES) ,GPS,*
------------------------------------
GPS(SATELLITES) 
Returns number of satellites in view (if sentence GGA is enabled). 
~GPS(SPEED) ,GPS,*
------------------------------------
GPS(SPEED) 
Returns the ground speed in knots as a floating point number.
~GPS(TIME) ,GPS,TIME,*
------------------------------------
GPS(TIME) 
Returns the normal time string corrected for local time eg, "12:09:33".
~GPS(TRACK) ,GPS,*
------------------------------------
GPS(TRACK) 
Returns the track over the ground (degrees true) as a floating point number. 
~GPS(VALID)  ,GPS,*
------------------------------------
GPS(VALID)  
Returns: 0=invalid data, 1=valid data
~HEX$( number [, chars]) ,STRINGS,CONVERSION,CONVERT,HEX,*
------------------------------------
HEX$( number [, chars]) 
Returns a string giving the hexadecimal (base 16) value for the 'number'. 
'chars' is optional and specifies the number of characters in the string with zero as the leading padding character(s). 
~INKEY$ ,KEYBOARD,USER INPUT,*
------------------------------------
INKEY$ 
Checks the console input buffer and, if there is one or more characters waiting in the queue, will remove the first character and return it as a single character in a string. 
If the input buffer is empty this function will immediately return with an empty string (i.e. ""). 
~INPUT$(nbr, [#]fnbr) ,INPUT,USER INPUT,FILES,INPUT,OUTPUT,*
------------------------------------
INPUT$(nbr, [#]fnbr) 
Will return a string composed of 'nbr' characters read from a file on the SD card previously opened for INPUT with the file number '#fnbr'. 
This function will read all characters including carriage return and new line without translation. 
Will return a string composed of 'nbr' characters read from a serial communications port opened as 'fnbr'. 
This function will return as many characters as are waiting in the receive buffer up to 'nbr'. 
If there are no characters waiting it will immediately return with an empty string. 
#0 can be used which refers to the console's input buffer. The # is optional. 
Also see the OPEN command. 
~INSTR( [start-position,] string-searched$, string- pattern$ [,size] )  ,STRINGS,SEARCH,*
------------------------------------
INSTR( [start-position,] string-searched$, string- pattern$ [,size] )  
Returns the position at which 'string-pattern$' occurs in 'string-searched$', beginning at 'start-position'. 
If 'start-position' is not provided it will default to 1. Both the position returned and 'start-position' use 1 for the first character, 2 for the second, etc. 
The function returns zero if 'string-pattern$' is not found. If the optional parameter "size" is specified the "string-pattern" is treated as a regular expression. 

Appendix E - Regex Syntax
The alternate forms of the INSTR() and LINSTR() functions can take a regular expression as the search pattern.
The alternate form of the commands are:
INSTR([start],text$, search$ [,size])
LINSTR(text%(),search$ [,start] [,size]
In both cases specifying the size parameter causes the firmware to interpret the search string as a regular expression. 
The size parameter is a floating point variable that is used by the firmware to return the size of a matching string. 
If the variable doesn't exist it is created. As implemented in MMBasic you need to apply the returned start and size values to the MID$ function to extract the matched string. 
eg,
IF start THEN match$=MID$(text$,start,size) ELSE match$=”” ENDIF
The library used for the regular expressions implements POSIX draft P1003.2/D11.2, except for some of the internationalization features. 
See http://mirror.math.princeton.edu/pub/oldlinux/Linux.old/Ref-docs/POSIX/all.pdf section 2.8 for details of constructing Regular Expressions or other online tutorials.
The syntax of regular expressions can vary slightly with the various implementations. 
This document is asummary of the syntax and supported operations used in the MMBasic implementation.
Anchors

    ^       Start of string
    $       End of string
    \b      Word Boundary
    \B      Not a word boundary
    \<      Start of word
    \>      End of word
Qualifiers

    *       0 or more (not escaped)
    \+      1 or more
    \?      0 or 1
    \{3\}   Exactly 3
    \{3,\}  3 or more
    \{3,5\} 3,4 or 5
Groups and Ranges

    (a\|b)  a or b
    \(…\)     group
    [abc]   Range (a or b or c)
    [^abc]  Not (a or b or c]
    [a-q]   lower case letters a to q
    [A-Q]   upper case letters A to Q
    [0-7]   Digits from 0 to 7
Escapes Required to Match Normal Characters

    \^ to match ^ (caret)
    \. to match . (dot)
    \* to match * (asterix)
    \$ to match $ (dollar)
    \[ to match [ (left bracket)
    \\ to match \ (backslash)
Escapes with Special Functions

    \+ See Quantifiers
    \? See Quantifiers
    \{ See Quantifiers
    \} See Quantifiers
    \| See Groups and Ranges
    \( See Groups and Ranges
    \) See Groups and Ranges
    \w See Character Classes
Character Classes

    \w digits,letters and _
    [:word:] digits,letters and _
    [:upper:] Upper case letters_
    [:lower:] Lower case letters_
    [:alpha:] All letters
    [:alnum:] Digits and letters
    [:digit:] Digits
    [:xdigit:] Hexidecimal digits
    [:punct:] Puntuation
    [:blank:] Space and tab
    [:space:] Blank charaters
    [:cntrl:] Control charaters
    [:graph:] Printed characters
    [:print:] Printed chars and spaces
    
Example expression to match an IP Address which is contained within a word boundary.
"\<[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\>"
~INT( number ) ,NUMBERS,*
------------------------------------
INT( number ) 
For example 9.89 will return 9 and -2.11 will return -3. 
This behaviour is for Microsoft compatibility, the FIX() function provides a true integer function. See also CINT() . 
~JSON$(array%(), string$),ARRAY,STRING,*
------------------------------------
JSON$(array%(), string$)
Returns a string representing a specific item out of the JSON input stored in the longstring array%(). 
Note that many JSON data sets are quite large and may be too big to parse with the memory available. 

Examples (taken from api.openweathermap.org):
JSON$(a%(), "name")
JSON$(a%(), "coord.lat")
JSON$(a%(), "weather[0].description")
JSON$(a%(),"list[4].weather[0].description
~KEYDOWN(n),KEYBOARD,KEYS,KEY,*
------------------------------------
KEYDOWN(n)
The decimal values for the function and arrow keys are listed in Appendix F. 
This function will report multiple simultaneous key presses and the parameter 'n' is the number of the keypress to report. 

KEYDOWN(0) will return the number of keys being pressed 

For example, if "c", "g" and "p" are pressed simultaneously KEYDOWN(0) will return 3, KEYDOWN(1) will return 99, KEYDOWN(2) will return 103, etc. 
The keys do not need to be pressed simultaneously and will report in the order pressed. 
Taking a finger off a key will promote the next key pressed to #1. 
The first key ('n' = 1) is entered in the keyboard buffer (accessible using INKEY$) while keys 2 to 6 can only be accessed via this function. 
Using this function will clear the console input buffer. 

KEYDOWN(7) will give any modifier keys that are pressed. These keys do not add to the count in keydown(0)

The return value is a bitmask as follows: lalt ? 1, lctrl ? 2, lgui ? 4, lshift ? 8, ralt ? 16, rctrl ? 32, rgui ? 64, rshift ? 128

KEYDOWN(8) will give the current status of the lock keys. These keys do not add to the count in keydown(0)

The return value is a bitmask as follows: caps_lock ? 1, num_lock ? 2, scroll_lock ? 4 
Note that some keyboards will limit the number of active keys that they can report on.

Appendix H - Special Keyboard Keys
Special Keyboard Keys
MMBasic generates a single unique character for the function keys and other special keys on the keyboard.
These are shown in this table as hexadecimal and decimal numbers:
Keyboard    Key Code    Key Code    
Key         (Hex)       (Decimal)

DEL         7F          127
Up Arrow    80          128
Down Arrow  81          129
Left Arrow  82          130
Right Arrow 83          131
Insert      84          132
Home        86          134
End         87          135
Page Up     88          136
Page Down   89          137
Alt         8B          139
F1          91          145
F2          92          146
F3          93          147
F4          94          148
F5          95          149
F6          96          150
F7          97          151
F8          98          152
F9          99          153
F10         9A          154
F11         9B          155
F12         9C          156
PrtScr/SysRq        
            9D          157
PAUSE/BREAK
            9E          158
SHIFT_TAB   9F          159
SHIFT_DEL   A0          160
SHIFT_DOWN_ARROW
            A1          161
SHIFT_RIGHT_ARROW
            A3          163
            
For PS2 and USB keyboards, if the shift key is simultaneously pressed with the function keys F1 to F12 then 40 (hex) is added to the code (this is the equivalent of setting bit 6). 
For example Shift-F10 will generate DA(hex). 
The shift modifier works with the function keys F1 to F12; it is ignored for the other keys except TAB,DEL, DOWN_ARROW, and RIGHT_ARROW as identified above.
MMBasic will translate most VT100 escape codes generated by terminal emulators such as Tera Term and Putty to these codes (excluding the shift and control modifiers). 
This means that a terminal emulator operating over a USB or a serial port opened as console will generate the same key codes as a directly attached keyboard
~LCASE$( string$ ) ,STRINGS,CONVERSION,CONVERT,*
------------------------------------
LCASE$( string$ ) 
Returns 'string$' converted to lowercase characters. 
~LCOMPARE(array1%(), array2%()) ,STRINGS,COMPARE,*
------------------------------------
LCOMPARE(array1%(), array2%()) 
Compare the contents of two long string variables array1%() and array2%().
The returned is an integer and will be -1 if array1%() is less than array2%(). 
It will be zero if they are equal in length and content and +1 if array1%() is greater than array2%().
The comparison uses the ASCII character set and is case sensitive. 
~LEFT$( string$, nbr ) ,STRINGS,*
------------------------------------
LEFT$( string$, nbr ) 
Returns a substring of 'string$' with 'nbr' of characters from the left (beginning) of the string. 
~LEN( string$ ) ,STRINGS,*
------------------------------------
LEN( string$ ) 
Returns the number of characters in 'string$'. 
~LGETBYTE(array%(), n) ,STRINGS,BYTE,*
------------------------------------
LGETBYTE(array%(), n) 
Returns the numerical value of the 'n'th byte in the LONGSTRING held in 'array%()'. 
This function respects the setting of OPTION BASE in determining which byte to return. 
~LGETSTR$(array%(), start, length) ,STRINGS,*
------------------------------------
LGETSTR$(array%(), start, length) 
Returns part of a long string stored in array%() as a normal MMBasic string. 
The parameters start and length define the part of the string to be returned. 
~LINSTR(array%(), search$ [,start] [,size])) ,STRINGS,*
------------------------------------
LINSTR(array%(), search$ [,start] [,size])) 
Returns the position of a search string in a long string. The returned value is an integer and will be zero if the substring cannot be found. 
'array%()' is the string to be searched and must be a long string variable. 
'search$' is the substring to look for and it must be a normal MMBasic string or expression (not a long string). The search is case sensitive. 
Normally the search will start at the first character in ' array%()' but the optional third parameter allows the start position of the search to be specified. 
If the optional parameter 'size' is specified the 'search$' is treated as a regular expression. 
See Appendix E for the details.
~LLEN(array%()) ,STRINGS,ARRAY,*
------------------------------------
LLEN(array%()) 
Returns the length of a long string stored in array%() 
~LOC( [#]fnbr ) ,FILES,*
------------------------------------
LOC( [#]fnbr ) 
For a file on the SD card opened as RANDOM this will return the current position of the read/write pointer in the file. 
Note that the first byte in a file is numbered 1. 

For a serial communications port opened as 'fnbr' this function will return the number of bytes received and waiting in the receive buffer to be read. 
#0 can be used which refers to the console's input buffer. The # is optional. 
~LOF( [#]fnbr ) ,FILES,*
------------------------------------
LOF( [#]fnbr ) 
For a file on the SD card this will return the current length of the file in bytes. 
For a serial communications port opened as 'fnbr' this function will return the space (in characters) remaining in the transmit buffer. 
Note that when the buffer is full MMBasic will pause when adding a new character and wait for some space to become available. The # is optional. 
~LOG( number ) ,MATH,NUMBERS,*
------------------------------------
LOG( number ) 
Returns the natural logarithm of the argument 'number'. 
~MAP( n ),GRAPHIC,*
------------------------------------
MAP( n )
Returns the 24-bit RGB value for the index 'n' in the colour map table. See the MAP command. 
This allows the Basic programmer to use a colour specified by the MAP command e.g 
MAP(8),RGB(100,100,100)
MAP SET
Pixel x,y,map(8)
For HDMI displays colours will be converted to the nearest RGB555 colour (640x480 resolution) or RGB332 colour (1024x768 or 1280x720 resolution) 
~MATH functions,MATH,*
------------------------------------
MATH functions
The math function performs many simple mathematical calculations that can be programmed in Basic but there are speed advantages to coding looping 
structures in C and there is the advantage that once debugged they are there for everyone without re-inventing the wheel. 
~MATH(ATAN3 x,y) ,MATH,*
------------------------------------
MATH(ATAN3 x,y) 
Returns ATAN3 of x and y 
~MATH(COSH a) ,MATH,*
------------------------------------
MATH(COSH a) 
Returns the hyperbolic cosine of a 
~MATH(LOG10 a) ,MATH,*
------------------------------------
MATH(LOG10 a) 
Returns the base 10 logarithm of a 
~MATH(SINH a) ,MATH,*
------------------------------------
MATH(SINH a) 
Returns the hyperbolic sine of a 
~MATH(TANH a) ,MATH,*
------------------------------------
MATH(TANH a) 
Returns the hyperbolic tan of a 
~MATH(CRCn data [,length] [,polynome] [,startmask] [,endmask] [,reverseIn] [,reverseOut],MATH,*
------------------------------------
MATH(CRCn data [,length] [,polynome] [,startmask] [,endmask] [,reverseIn] [,reverseOut]
Calculates the CRC to n bits (8, 12, 16, 32) of "data". 
"data" can be an integer or floating point array or a string variable. "Length" is optional and if not specified the size of the array or string length is used. 
The defaults for startmask, endmask reverseIn, and reversOut are all zero. reverseIn, and reversOut are both Booleans and take the value 1 or 0. 
The defaults for polynomes are CRC8=&H07, CRC12=&H80D, CRC16=&H1021, crc32=&H04C11DB7 eg, for crc16_CCITT use MATH(CRC16 array(), n,, &HFFFF)
~MATH(RAND),MATH,*
------------------------------------
MATH(RAND)
Returns a random number 0.0 <= n < 1.0 using the "Mersenne Twister algorithm. 
If not seeded with MATH RANDOMIZE the first usage seeds with the time in microseconds since boot
~Simple Statistics,MATH,*
------------------------------------
Simple Statistics
More on simple statistics
~MATH(CHI a()) ,MATH,*
------------------------------------
MATH(CHI a()) 
Returns the Pearson's chi-squared value of the two dimensional array a()) 
~MATH(CHI_p a()) ,MATH,*
------------------------------------
MATH(CHI_p a()) 
Returns the associated probability in % of the Pearson's chi-squared value of the two dimensional array a()) 
~MATH(CROSSING array() [,level] [,direction],MATH,*
------------------------------------
MATH(CROSSING array() [,level] [,direction]
This returns the array index at which the values in the array pass the "level" in the direction specified. level defaults to 0. 
Direction defaults to 1 ( valid values are -1 or 1)
~MATH(CORREL a(), a()) ,MATH,*
------------------------------------
MATH(CORREL a(), a()) 
Returns the Pearson's correlation coefficient between arrays a() and b() 
~MATH(MAX a() [,index%]) ,MATH,*
------------------------------------
MATH(MAX a() [,index%]) 
Returns the maximum of all values in the a() array, a() can have any number of dimensions. 
If the integer variable is specified then it will be updated with the index of the maximum value in the array. 
This is only available on one-dimensional arrays 
~MATH(MEAN a()) ,MATH,*
------------------------------------
MATH(MEAN a()) 
Returns the average of all values in the a() array, a() can have any number of dimensions 
~MATH(MEDIAN a()) ,MATH,*
------------------------------------
MATH(MEDIAN a()) 
Returns the median of all values in the a() array, a() can have any number of dimensions 
~MATH(MIN a(), [index%]) ,MATH,*
------------------------------------
MATH(MIN a(), [index%]) 
Returns the minimum of all values in the a() array, a() can have any number of dimensions. 
If the integer variable is specified then it will be updated with the index of the maximum value in the array. 
This is only available on one-dimensional arrays. 
~MATH(SD a()) ,MATH,*
------------------------------------
MATH(SD a()) 
Returns the standard deviation of all values in the a() array, a() can have any number of dimensions 
~MATH(SUM a()) ,MATH,*
------------------------------------
MATH(SUM a()) 
Returns the sum of all values in the a() array, a() can have any number of dimensions 
~Vector Arithmetic ,MATH,*
------------------------------------
Vector Arithmetic 
More on vector arithmetic
~MATH(MAGNITUDE v()) ,MATH,*
------------------------------------
MATH(MAGNITUDE v()) 
Returns the magnitude of the vector v(). The vector can have any number of elements 
~MATH(DOTPRODUCT v1(), v2()) ,MATH,*
------------------------------------
MATH(DOTPRODUCT v1(), v2()) 
Returns the dot product of two vectors v1() and v2(). The vectors can have any number of elements but must have the same cardinality 
~Matrix Arithmetic ,MATH,*
------------------------------------
Matrix Arithmetic 
More on Matrix Arithmetic 
~MATH(M_DETERMINANT array!()) ,MATH,*
------------------------------------
MATH(M_DETERMINANT array!()) 
Returns the determinant of the array. The array must be square. 

MMBasic supports a full range of functions to allow the manipulation of complex numbers. In this implementation complex numbers have a 32- bit real 
and 32-bit imaginary part and to make this work in MMBasic, it uses integers 
(64-bit) to hold these.
CREATION
complex% = MATH(C_CPLX r!, i!)
complex% = MATH(C_POLAR radius!, angle!)
Floating returns
real! = MATH(C_REAL complex%)
imag! = MATH(C_IMAG complex%)
arg! = MATH(C_ARG complex%)
mod! = MATH(C_MOD complex%)
phase! = MATH(C_PHASE complex%)
complex1% = MATH(C_CONJ complex2%)
complex1% = MATH(C_SIN complex2%)
complex1% = MATH(C_COS complex2%)
complex1% = MATH(C_TAN complex2%)
complex1% = MATH(C_ASIN complex2%)
complex1% = MATH(C_ACOS complex2%)
complex1% = MATH(C_ATAN complex2%)
complex1% = MATH(C_SINH complex2%)
complex1% = MATH(C_COSH complex2%)
complex1% = MATH(C_TANH complex2%)
complex1% = MATH(C_ASINH complex2%)
complex1% = MATH(C_ACOSH complex2%)
complex1% = MATH(C_ATANH complex2%)
complex1% = MATH(C_PROJ complex2%)
BASIC ARITHMETIC
complex1% = MATH(C_ADD complex2%,complex3%)
complex1% = MATH(C_SUB complex2%,complex3%)
complex1% = MATH(C_MUL complex2%,complex3%)
complex1% = MATH(C_DIV complex2%,complex3%)
complex1% = MATH(C_POW complex2%,complex3%)
complex1% = MATH(C_AND complex2%,complex3%)
complex1% = MATH(C_OR complex2%,complex3%)
complex1% = MATH(C_XOR complex2%,complex3%)
~MATH(PID channel, setpoint!, measurement)),MATH,PID,*
------------------------------------
MATH(PID channel, setpoint!, measurement))
The 'setpoint' value is the desired state that the controller is trying to achieve. 
The 'measurement' is the current value of the real world. https://www.thebackshed.com/forum/ViewTopic.php?FID=16&TID=17263 
For an example of setting up and running a PID controller
~MATH(BASE64 ENCODE/DECODE in$/in(), out$/out()),MATH,*
------------------------------------
MATH(BASE64 ENCODE/DECODE in$/in(), out$/out())
Returns the length of out$/out(). This base64 encodes or decodes the data in 'in' and puts the result in 'out'. 
Where arrays are used as the output they must be big enough relative to the input and the direction. 
Encryption increases length by 4/3 and decryption decreases it by 3/4.
~MAX( arg1 [, arg2 [, ...]] )  or MIN( arg1 [, arg2 [, ...]] ) ,MATH,*
------------------------------------
MAX( arg1 [, arg2 [, ...]] )  or MIN( arg1 [, arg2 [, ...]] ) 
Returns the maximum or minimum number in the argument list. 
Note that the comparison is a floating point c
~MID$( string$, start ) or MID$( string$, start, nbr ) ,STRINGS,*
------------------------------------
MID$( string$, start ) or MID$( string$, start, nbr ) 
Returns a substring of 'string$' beginning at 'start' and continuing for 'nbr' characters. 
The first character in the string is number 1. If 'nbr' is omitted the returned string will extend to the end of 'string$' 
~OCT$( number [, chars]) ,STRINGS,CONVERSION,CONVERT,OCT,*
------------------------------------
OCT$( number [, chars]) 
Returns a string giving the octal (base 8) representation of 'number'. 
'chars' is optional and specifies the number of characters in the string with zero as the leading padding character(s). 
~PEEK(BYTE addr%) or PEEK(SHORT addr%) or PEEK(WORD addr%) or PEEK(INTEGER addr%) or PEEK(FLOAT addr%) or PEEK(VARADDR var) or PEEK(CFUNADDR cfun) or PEEK(VAR var, +/-offset) or PEEK( VARTBL, +/-offset) or PEEK( PROGMEM, +/-offset),MEMORY,MMBASIC,*
------------------------------------
PEEK(BYTE addr%) or PEEK(SHORT addr%) or PEEK(WORD addr%) or PEEK(INTEGER addr%) or PEEK(FLOAT addr%) or PEEK(VARADDR var) or PEEK(CFUNADDR cfun) or PEEK(VAR var, +/-offset) or PEEK( VARTBL, +/-offset) or PEEK( PROGMEM, +/-offset)

PEEK(BYTE addr%) 
PEEK(SHORT addr%) 
PEEK(WORD addr%) 
PEEK(INTEGER addr%) 
PEEK(FLOAT addr%) 
PEEK(VARADDR var) 
PEEK(CFUNADDR cfun) 
PEEK(VAR var, ±offset)  
PEEK( VARTBL, ±offset) 
PEEK( PROGMEM, ±offset) 


Will return a byte or a word within the PIC32 virtual memory space. 
BYTE will return the byte (8-bits) located at 'addr%' 
SHORT will return the short integer (16-bits) located at 'addr%' 
WORD will return the word (32-bits) located at 'addr%' 
INTEGER will return the integer (64-bits) located at 'addr%' 
FLOAT will return the floating point number (32-bits) located at 'addr%' 
VARADDR will return the address (32-bits) of the variable 'var' in memory. An array is specified as var(). 
CFUNADDR will return the address (32-bits) of the CFunction 'cfun' in memory. This address can be passed to another CFunction which can then call it to perform some common process. 
VAR, will return a byte in the memory allocated to 'var'. An array is specified as var(). 
VARTBL, will return a byte in the memory allocated to the variable table maintained by MMBasic. 
Note that there is a comma after the keyword VARTBL. 

PROGMEM, will return a byte in the memory allocated to the program. 
Note that there is a comma after the keyword PROGMEM. 
Note that 'addr%' should be an integer. 
~PEEK(BP, n%) ,MEMORY,*
------------------------------------
PEEK(BP, n%) 
PEEK(bp n%) ' returns the byte at address n% and increments n% to point to the next byte.
~PEEK(SP,n%) PEEK with increment to the next short,MEMORY,*
------------------------------------
PEEK(SP,n%) PEEK with increment to the next short
PEEK(sp n%) ' returns the short at address n% and increments n% to point to the next short.
~PEEK(WP,n%)PEEK with increment to the next word,MEMORY,*
------------------------------------
PEEK(WP,n%)PEEK with increment to the next word
PEEK(wp n%) ' returns the word at address n% and increments n% to point to the next word.
~PI ,PI,NUMBERS,CONSTANTS,*
------------------------------------
PI 
Returns the value of pi. 
~PIN( pin ) ,PIN,HARDWARE,*
------------------------------------
PIN( pin ) 
Returns the value on the external I/O 'pin'. Zero means digital low, 1 means digital high and for analogue inputs it will return the measured voltage as a floating point number. Frequency inputs will return the frequency in Hz. 
A period input will return the period in milliseconds while a count input will return the count since reset (counting is done on the positive rising edge). 
The count input can be reset to zero by resetting the pin to counting input (even if it is already so configured). This function will also return the state of a pin configured as an output.
Also see the SETPIN and PIN() = commands. 
Refer to the chapter "Using the I/O pins" for a general description of the PicoMiteVGA 's input/output  capabilities. 
~PIN( BOOTSEL ) ,PIN,HARDWARE,*
------------------------------------
PIN( BOOTSEL ) 
Returns the state of the boot select switch allowing it to be used as a user input in a program.
~PIN( TEMP ) ,PIN,HARDWARE,*
------------------------------------
PIN( TEMP ) 
Returns the temperature of the RP2040 chip (see the RP2040 data sheet for the details) 
~PIO(DMA RX POINTER) or PIO(DMA TX POINTER) ,PIO,*
------------------------------------
PIO(DMA RX POINTER) or PIO(DMA TX POINTER) 
Returns the current data item being written or read by the PIO.
~PIO (SHIFTCTRL push_threshold [,pull_threshold] [,autopush] [,autopull] [,in_shiftdir] [,out_shiftdir] [,fjoin_rx] [,fjoin_tx]),PIO,*
------------------------------------
PIO (SHIFTCTRL push_threshold [,pull_threshold] [,autopush] [,autopull] [,in_shiftdir] [,out_shiftdir] [,fjoin_rx] [,fjoin_tx])
Helper function to calculate the value of shiftctrl for the INIT MACHINE command .
~PIO (PINCTRL no_side_set_pins [,no_set_pins] [,no_out_pins] [,IN base] [,side_set_base] [,set_base][, out_base]),PIO,*
------------------------------------
PIO (PINCTRL no_side_set_pins [,no_set_pins] [,no_out_pins] [,IN base] [,side_set_base] [,set_base][, out_base])
Helper function to calculate the value of pinctrl for the INIT MACHINE command. 
Note: The pin parameters must be formatted as Gpn.
~PIO (EXECCTRL jmp_pin ,wrap_target, wrap [,side_pindir] [,side_en]),PIO,*
------------------------------------
PIO (EXECCTRL jmp_pin ,wrap_target, wrap [,side_pindir] [,side_en])
Helper function to calculate the value of execctrl for the INIT MACHINE command
~PIO(READFIFO a, b, c),PIO,*
------------------------------------
PIO(READFIFO a, b, c)
Read from a PIO FIFO 'a' is the pio (0 or 1), 'b' id the state machine (0...3), 'c' is the FIFO register *0...3)
~PIO(NEXT LINE [channel]),PIO,BETA,*
------------------------------------
PIO(NEXT LINE [channel])
If channel is not specified then it uses the last pio specified by a PIO ASSEMBLE command or gives an error if there hasn't been one.
If channel is specified, it returns the next line for that channel which will be zero if that channel has not been used.
BETA 6.02 ONLY!
~PIO (FDEBUG pio) ,PIO,*
------------------------------------
PIO (FDEBUG pio) 
Returns the value of the FSDEBUG register for the pio specified
~PIO (FSTAT pio) ,PIO,*
------------------------------------
PIO (FSTAT pio) 
Returns the value of the FSTAT register for the pio specified
~PIO (FLEVEL pio) ,PIO,*
------------------------------------
PIO (FLEVEL pio) 
Returns the value of the FLEVEL register for the pio specified PIO(FLEVEL pio)
~PIO(FLEVEL pio ,sm, DIR) dir can be RX or TX. ,PIO,*
------------------------------------
PIO(FLEVEL pio ,sm, DIR) dir can be RX or TX. 
Returns the level of the specific fifo
~PIO(.WRAP) ,PIO,*
------------------------------------
PIO(.WRAP) 
Returns the location of the .wrap directive in PIO ASSEMBLE
~PIO(.WRAP TARGET) ,PIO,*
------------------------------------
PIO(.WRAP TARGET) 
Returns the location of the .wrap target directive in PIO ASSEMBLE. 
These can be used in the PIO(EXECCTRL function as follows: 
PIO (EXECCTRL jmp_pin PIO(.WRAP TARGET), PIO(.WRAP)  [,side_pindir] [,side_en])
~PIXEL( x, y),GRAPHIC,LCD,COLOR,DRAWING,*
------------------------------------
PIXEL( x, y)
If an LCD display is used it must use one of the SSD1963, ILI9341, ILI9488, or ST7789_320 controllers.

~PORT(start, nbr [,start, nbr]...) ,HARDWARE,PIN,*
------------------------------------
PORT(start, nbr [,start, nbr]...) 
Returns the value of a number of I/O pins in one operation. 
'start' is an I/O pin number and its value will be returned as bit 0. 
'start'+1 will be returned as bit 1, 'start'+2 will be returned as bit 2, and so on for 'nbr' number of bits. 
I/O pins used must be numbered consecutively and any I/O pin that is invalid or not configured as an input will cause an error. 
The start/nbr pair can be repeated up to 25 times if additional groups of input pins need to be added.  
This function will also return the state of a pin configured as an output. It can be used to conveniently communicate with parallel devices like memory chips. 
Any number of I/O pins (and therefore bits) can be used from 1 to the number of I/O pins on the chip. 
See the PORT command to simultaneously output to a number of pins. 

~PULSIN( pin, polarity ) or PULSIN( pin, polarity, t1 ) or PULSIN( pin, polarity, t1, t2 ) ,PIN,*
------------------------------------
PULSIN( pin, polarity ) or PULSIN( pin, polarity, t1 ) or PULSIN( pin, polarity, t1, t2 ) 
Measures the width of an input pulse from 1us to 1 second with 0.1us resolution.  
'pin' is the I/O pin to use for the measurement, it must be previously configured as a digital input. 
'polarity' is the type of pulse to measure, if zero the function will return the width of the next negative pulse, if non zero it will measure the next positive pulse. 
't1' is the timeout applied while waiting for the pulse to arrive, 
't2' is the timeout used while measuring the pulse. Both are in microseconds (us) and are optional. If 't2' is omitted the value of 't1' will be used for both timeouts. 
If both 't1' and 't2' are omitted then the timeouts will be set at 100000 (i.e. 100ms). 
This function returns the width of the pulse in microseconds (us) or -1 if a timeout has occurred. 
The measurement is accurate to ±0.5% and ±0.5us
Note that this function will cause the running program to pause while the measurement is made and interrupts will be ignored during this period. 
~RAD( degrees ) ,NUMBERS,CONVERT,CONVERSION,*
------------------------------------
RAD( degrees ) 
Converts 'degrees' to radians. 
~RGB(red, green, blue) or  RGB(shortcut) ,GRAPHIC,COLORS,*
------------------------------------
RGB(red, green, blue) or  RGB(shortcut) 
Generates an RGB true colour value. 'red', 'blue' and 'green' represent the intensity of each colour. 
A value of zero represents black and 255 represents full intensity. 
'shortcut' allows common colours to be specified by naming them. 
The colours that can be named are 
white, yellow, lilac, brown, fuchsia, rust, magenta, red, cyan, green, cerulean, midgreen, cobalt, myrtle, blue and black. 
For example, 
RGB(red) or RGB(cyan). 
Unoficial note (means it is not from official user manual)
Predefinded colour values are:
WHITE       RGB(255,  255,  255)
YELLOW      RGB(255,  255,    0)
LILAC       RGB(255,  128,  255)
BROWN       RGB(255,  128,    0)
FUCHSIA     RGB(255,  64,   255)
RUST        RGB(255,  64,     0)
MAGENTA     RGB(255,  0,    255)
RED         RGB(255,  0,      0)
CYAN        RGB(0,    255,  255)
GREEN       RGB(0,    255,    0)
CERULEAN    RGB(0,    128,  255)
MIDGREEN    RGB(0,    128,    0)
COBALT      RGB(0,    64,   255)
MYRTLE      RGB(0,    64,     0)
BLUE        RGB(0,    0,    255)
BLACK       RGB(0,    0,      0)
~RIGHT$( string$, number-ofchars ) ,STRINGS,*
------------------------------------
RIGHT$( string$, number-ofchars ) 
Returns a substring of 'string$' with 'number-of-chars' from the right (end) of the string. 
~RND( number ) or RND ,NUMBERS,*
------------------------------------
RND( number ) or RND 
Returns a pseudo-random number in the range of 0 to 0.999999. 
The 'number' value is ignored if supplied. 
The RANDOMIZE command reseeds the random number generator. 
SGN( number ) Returns the sign of the argument 'number', +1 for positive numbers, 0 for 0, and -1 for negative numbers. 
~SGN( number )  ,NUMBERS,*
------------------------------------
SGN( number )  
Returns the sign of the argument 'number', +1 for positive numbers, 0 for 0, and -1 for negative numbers.
~SIN( number ) ,MATH,*
------------------------------------
SIN( number ) 
Returns the sine of the argument 'number' in radians. 
~SPACE$( number ) ,STRINGS,*
------------------------------------
SPACE$( number ) 
Returns a string of blank spaces 'number' characters long. 
~SPI ( data ) or SPI2 ( data )  ,SPI,PERIPHERALS,*
------------------------------------
SPI ( data ) or SPI2 ( data )  
Send and receive data using an SPI channel. A single SPI transaction will send data while simultaneously receiving data from the slave. 
'data' is the data to send and the function will return the data received during the transaction. 
'data' can be an integer or a floating point variable or a constant. 
~SPRITE()   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE()   <- VGA AND HDMI VERSIONS ONLY
The SPRITE functions return information regarding sprites which are small graphic images on the VGA/HDMI screen. 
These are useful when writing  games. 
See also the SPRITE commands.
~SPRITE(C, [#]n )   <-  VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(C, [#]n )   <-  VGA AND HDMI VERSIONS ONLY
Returns the number of currently active collisions for sprite n. 
If n=0 then returns the number of sprites that have a currently active collision following a  SPRITE SCROLL command
~SPRITE(C, [#]n, m)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(C, [#]n, m)   <- VGA AND HDMI VERSIONS ONLY
Returns the number of the sprite which caused the "m"th collision of sprite n. 
If n=0 then returns the sprite number of "m"th sprite that has a currently active  collision following a SPRITE SCROLL command. 
If the collision was with the edge of the screen then the return value will be:
&HF1 collision with left of screen
&HF2 collision with top of screen
&HF4 collision with right of screen
&HF8 collision with bottom of screen
~SPRITE(D ,[#]s1, [#]s2)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(D ,[#]s1, [#]s2)   <- VGA AND HDMI VERSIONS ONLY
Returns the distance between the centres of sprites 's1' and 's2' (returns -1 if either sprite is not active)
~SPRITE(E, [#]n   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(E, [#]n   <- VGA AND HDMI VERSIONS ONLY
Returns a bitmap indicating any edges of the screen the sprite is in contact with: 1 =left of screen, 2=top of screen, 4=right of screen, 8=bottom of screen
~SPRITE(H,[#]n)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(H,[#]n)   <- VGA AND HDMI VERSIONS ONLY
Returns the height of sprite n. This function is active whether or not the sprite is currently displayed (active).
~SPRITE(L, [#]n)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(L, [#]n)   <- VGA AND HDMI VERSIONS ONLY
Returns the layer number of active sprites number n
~SPRITE(N)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(N)   <- VGA AND HDMI VERSIONS ONLY
Returns the number of displayed (active) sprites
~SPRITE(N,n)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(N,n)   <- VGA AND HDMI VERSIONS ONLY
Returns the number of displayed (active) sprites on layer n
~SPRITE(S)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(S)   <- VGA AND HDMI VERSIONS ONLY
Returns the number of the sprite which last caused a collision. 
NB if the number returned is Zero then the collision is the result of a SPRITE SCROLL command and the SPRITE(C...) function should be used to find how many and which sprites collided. 
~SPRITE(V,spriteno1,spriteno 2)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(V,spriteno1,spriteno 2)   <- VGA AND HDMI VERSIONS ONLY
Returns the vector from 'spriteno1' to 'spriteno2' in radians.  
The angle is based on the clock so if 'spriteno2' is above 'spriteno1' on the screen then the answer will be zero. 
This can be used on any pair of sprites that are visible. If either sprite is not visible the function will return -1. 
This is particularly useful after a collision if the programmer wants to make some differential decision based on where the collision occurred. 
The angle is calculated between the centre of each of the sprites which may of course be different sizes.
~SPRITE(T, [#]n)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(T, [#]n)   <- VGA AND HDMI VERSIONS ONLY
Returns a bitmap showing all the sprites currently touching the requested sprite Bits 0-63 in the returned integer represent a current collision with sprites 1 to 64 respectively
~SPRITE(V,[#]so1, [#]s2)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(V,[#]so1, [#]s2)   <- VGA AND HDMI VERSIONS ONLY
Returns the vector from sprite 's1' to 's2' in radians. 
The angle is based on the clock so if 's2' is above 's1' on the screen then the answer will be zero. 
This can be used on any pair of sprites that are visible. If either sprite is not visible the function will return -1. 
This is particularly useful after a collision if the programmer wants to make some differential decision based on where the collision occurred. 
The angle is calculated between the centre of each of the sprites which may of course be different sizes.
~SPRITE(W, [#]n)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(W, [#]n)   <- VGA AND HDMI VERSIONS ONLY
Returns the width of sprite n. This function is active whether or not the sprite is currently displayed (active).
~SPRITE(X, [#]n)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(X, [#]n)   <- VGA AND HDMI VERSIONS ONLY
Returns the X-coordinate of sprite n. This function is only active when the sprite is currently displayed (active). Returns 10000 otherwise.
~SPRITE(Y, [#]n)   <- VGA AND HDMI VERSIONS ONLY,GRAPHIC,SPRITES,VIDEO MEMORY,*
------------------------------------
SPRITE(Y, [#]n)   <- VGA AND HDMI VERSIONS ONLY
Returns the Y-coordinate of sprite n. This function is only active when the sprite is currently displayed (active). Returns 10000 otherwise.
~SQR( number )   <- VGA AND HDMI VERSIONS ONLY,MATH,NUMBERS,*
------------------------------------
SQR( number )   <- VGA AND HDMI VERSIONS ONLY
Returns the square root of the argument 'number'. 
~STR$( number ) or STR$( number, m ) or STR$( number, m, n ) or STR$( number, m, n, c$ ) ,STRINGS,FORMAT,*
------------------------------------
STR$( number ) or STR$( number, m ) or STR$( number, m, n ) or STR$( number, m, n, c$ ) 
Returns a string in the decimal (base 10) representation of 'number'. If 'm' is specified sufficient spaces will be added to the start of the number 
to ensure that the number of characters before the decimal point (including the negative or positive sign) will be at least 'm' characters. 
If 'm' is zero or the number has more than 'm' significant digits no padding spaces will be added. 
If 'm' is negative, positive numbers will be prefixed with the plus symbol and negative numbers with the negative symbol. 
If 'm' is positive then only the negative symbol will be used. 'n' is the number of digits required to follow the decimal place. 
If it is zero the string will be returned without the decimal point. 
If it is negative the output will always use the exponential format with 'n' digits resolution. 
If 'n' is not specified the number of decimal places and output format will vary automatically according to the number.
'c$' is a string and if specified the first character of this string will be used as the padding character instead of a space (see the 'm' argument). 
Examples:
STR$(123.456)  will return "123.456"
STR$(-123.456) will return "-123.456"
STR$(123.456, 1) will return "123.456"
STR$(123.456, -1) will return "+123.456"
STR$(123.456, 6) will return " 123.456"
STR$(123.456, -6) will return " +123.456"
STR$(-123.456, 6) will return " -123.456"
STR$(-123.456, 6, 5) will return " -123.45600"
STR$(-123.456, 6, -5) will return " -1.23456e+02"
STR$(53, 6) will return " 53"
STR$(53, 6, 2) will return " 53.00"
STR$(53, 6, 2, "*") will return "****53.00"
~STR2BIN(type, string$ [,BIG]) ,STRINGS,CONVERSION,CONVERT,BIN,*
------------------------------------
STR2BIN(type, string$ [,BIG]) 
Returns a number equal to the binary representation in 'string$'. 'type' can be: 
INT64 converts 8 byte string representing a signed 64-bit integer to an integer 
UINT64 converts 8 byte string representing an unsigned 64-bit integer to an integer 
INT32 converts 4 byte string representing a signed 32-bit integer to an integer 
UINT32 converts 4 byte string representing an unsigned 32-bit integer to an integer 
INT16 converts 2 byte string representing a signed 16-bit integer to an integer 
UINT16 converts 2 byte string representing an unsigned 16-bit integer to an integer 
INT8 converts 1 byte string representing a signed 8-bit integer to an integer 
UINT8 converts 1 byte string representing an unsigned 8-bit integer to an integer 
SINGLE converts 4 byte string representing single precision float to a float 
DOUBLE converts 8 byte string representing single precision float to a float 

By default the string must contain the number in little-endian format (i.e. the least significant byte is the first one in the string). 
Setting the third parameter to 'BIG' will interpret the string in big-endian format (i.e. the most significant byte is the first one in the string). 
This function makes it easy to read data from binary data files, interpret numbers from sensors or efficiently read binary data from flash memory chips. 
An error will be generated if the string is the incorrect length for the conversion requested 
See also the function BIN2STR$ 
~STRING$( nbr, ascii ) or STRING$( nbr, string$ ) ,STRINGS,*
------------------------------------
STRING$( nbr, ascii ) or STRING$( nbr, string$ ) 
Returns a string 'nbr' bytes long consisting of either the first character of string$ or the character representing the ASCII value 'ascii' which is an integer or float number in the range of 0 to 255.
TAB( number ) Outputs spaces until the column indicated by 'number' has been reached on the console output. 
~TAN( number ) ,MATH,TRIGONOMETRY,*
------------------------------------
TAN( number ) 
Returns the tangent of the argument 'number' in radians. 
~TEMPR( pin ) ,PIN,HARDWARE,PERIPHERAL,*
------------------------------------
TEMPR( pin ) 
Return the temperature measured by a DS18B20 temperature sensor connected to 'pin' (which does not have to be configured). 
The returned value is degrees C with a default resolution of 0.25 C. If there is an error during the measurement the returned value will be 1000. 
The time required for the overall measurement is 200ms and interrupts will be ignored during this period. 
Alternatively the TEMPR START command can be used to start the measurement and your program can do other things while the conversion is progressing. 
When this function is called the value will then be returned instantly assuming the conversion period has expired. 
If it has not, this function will wait out the remainder of the conversion time before returning the value. 
The DS18B20 can be powered separately by a 3V to 5V supply or it can operate on parasitic power from the PicoMiteVGA .

The TEMPR() function will get the temperature from a DS18B20 temperature sensor. This device can be purchased on eBay for about US$5 in a variety of packages including a waterproof probe version.
The DS18B20 can be powered separately by a 3.3V supply or it can operate on parasitic power from the Raspberry Pi Pico as shown on the right. 
Multiple sensors can be used but a separate I/O pin and a 4.7K pullup resistor is required for each one.
To get the current temperature you just use the TEMPR() function in an expression. For example:
PRINT "Temperature: " TEMPR(pin)
Where 'pin' is the I/O pin to which the sensor is connected. You do not have to configure the I/O pin, that is handled by MMBasic.
The returned value is in degrees C with a resolution of 0.25 ºC and is accurate to ±0.5 ºC. If there is an error during the measurement the returned value will be 1000.
The time required for the overall measurement is 200ms and the running program will halt for this period while the measurement is being made.
This also means that interrupts will be disabled for this period. If you do not want this you can separately trigger the conversion using the TEMPR START command then later use the TEMPR() function to retrieve the temperature reading. 
The TEMPR() function will always wait if the sensor is still making the measurement. For example:
TEMPR START GP15
...do other tasks...
PRINT "Temperature: " TEMPR(GP15)
The TEMPR START command can also be used to change the resolution of the measurement (from the default 0.25 ºC) and the associated conversion time.
~TIME$ ,TIME,*
------------------------------------
TIME$ 
Returns the current time based on MMBasic's internal clock as a string in the form "HH:MM:SS" in 24 hour notation. 
For example, "14:30:00". To set the current time use the command TIME$ = . 
~TIMER ,TIME,*
------------------------------------
TIMER 
Returns the elapsed time in milliseconds (e.g. 1/1000 of a second) since reset. 
The timer is reset to zero on power up or a CPU restart and you can also reset it by using TIMER as a command. 
If not specifically reset it will continue to count up forever (it is a 64 bit number and therefore will only roll over to zero after 200 million years). 
~TOUCH(X) or TOUCH(Y),LCD,HARDWARE,*
------------------------------------
TOUCH(X) or TOUCH(Y)
Will return the X or Y coordinate of the location currently touched on an LCD panel.  If the screen is not being touched the function will return -1.
~UCASE$( string$ ) ,STRINGS,*
------------------------------------
UCASE$( string$ ) 
Returns 'string$' converted to uppercase characters. 
~VAL( string$ ) ,STRINGS,NUMBER,*
------------------------------------
VAL( string$ ) 
Returns the numerical value of the 'string$'. If 'string$' is an invalid number the function will return zero. 
This function will recognise the &H prefix for a hexadecimal number, &O for octal and &B for binary.