Command Scripts and Extensions

The MIRTOS System Command Processor was designed to be used interactively, which is actually the meaning of the "I" in the MIRTOS acronym. But to actually use a microcontroller for control, there should be some way of executing a series of commands. Unique device initialization is needed with a startup configuration file. Furthermore, it would be convenient to be able to change those commands easily, store them in a readable format, and be able to invoke them on command.

All of the interactive commands can also be stored in either EEPROM or FLASH memory and sequentially executed as "batch" of commands. These human-readable "scripts" can either be invoked from the serial command console using the Script system command or programmatically from a subroutine, setting the script pointer WORD variable, ScriptNextOp and then either setting or clearing the bit BIT_SYS_SCRIPT_FLASH in the bFlags_System BYTE variable, as the script location determines. The three variables mentioned in the last sentence are defined in the SystemSRAM.Def file.

Not every form of every System Command, however, can be placed in a script. The text character limit for a script line is 50 bytes. Since the serial Parsing Buffer is larger than that, a script line will always be able to fit in the Parsing Buffer. It is not difficult to limit script text lines to 50 bytes, however. Note also that an nRF command limit is even less than 50 bytes.

The Script Processor is enabled after a system reboot only after the first full second has passed. It always verifies that the serial Parsing Buffer is empty before it inserts a new script command into that buffer. Entering even one character into the Parsing Buffer will prevent any script from executing. One way to stop a script from initializing a device is to hit the spacebar during the first second and enter " E 1 = 0", for example. Any leading space is (or spaces are) removed from the Parsing Buffer before the command is handed to the System Command Processor.

There are some commands which are ONLY a part of the scripting language command set. For example, there is no need for an interactive command to delay processing, for example. When executing a command sequence, however, it is sometimes necessary to delay execution until some task has completed. The scripting language therefore contains timed delay commands. The script timers will mark time but will not timeout while there is any command being processed by the System Command Processor. This is because script commands will not interrupt a system command or entry of a system command.

.STRING  "; OFF script:  "       ; Note the start and purpose
.STRING  "N 0x60"                ; Change the nRF24L01 from PRX to PTX mode
.STRING  "N> 'SNPW:Node_Name'"   ; Log into the remote device
.STRING  "N> 'ES 0x282 = 0'"     ; Set the BYTE at uVars.B[2] to 0
.STRING  "N> 'Logoff'"           ; Log out of the remote device
.STRING  "ES 0x284 = 50 M"       ; Setup a BYTE mSec timer at uVars.B[4]
.STRING  "TM1 V4"                ; Start the 50 millisecond BYTE timer
.STRING  "N*"                    ; Reload the default nRF configuration
.STRING  "S = -1 E"              ; Park the script pointer
.BYTE    0xFF                    ; Terminate the script

In the example above, the "N>" commands transmit the string which follows to be sent to the nRF24L01, as "immediately" as possible. If the default configuration were then to be reloaded immediately, the transmission may be interrupted and message not be received. For that reason, the "ES ..." and "TM1 ..." script lines configure and implement a 50 mSec delay. This is to be certain that the radio has enough time to send the message before the default PRX configuration is reloaded with "N*".

The syntax of the Script Timer Command is:
T[M1|M2|M4|S1|S2] [V#|Address]
   where "M" or "S" select either a mSec or Seconds timer
   "1", "2", or "4" select the number of bytes (BYTE, WORD, ULONG)
   "V#" or "Address" specifies the timer's starting SRAM address
Notes:
  The space between the timer specifier and the address is required.
  There is no ULONG Seconds timer, i.e., "TS4" is invalid.
  The memory address must be that of uVars.B[0] or higher.

If an invalid timer syntax is encountered, script execution will stop, pointing at the start of the timer string which it could not accept. The memory address specified may be any valid SRAM address at or above that of uVars.B[0]. These addresses in MIRTOS Version 3.01 are:
  0x280 (= 640 decimal) in the 328P,
  0x360 (= 864 decimal) in the 1284P, and
  0x5C0 (= 1,472 decimal) in the 2560

Examples using each of the five (5) types of script timers:
Time Desired Start at Command to initialize   uVars.B[##] Uses
Range Type of timer Delay  uVars.B[##]    the uVars.B[##]   the Timer     Delay Start
1 to
250 mSec
1-byte
(BYTE)
mSec
4 mSec 0 ES 640 = 4 M TM1 V0 0 1
10 mSec 1 ES 0x281 = 10 M TM1 V0 1 2
100 mSec 5 ES 645 = 100 M TM1 0x281 5 6
250 mSec 18 ES 658 = 250 M TM1 658 18 19

1 to
65,530 mSec
2-byte
(WORD)
mSec
250 mSec 0 ESW 0x280 = 250 M TM2 V0 0 & 1 2 & 3
750 mSec 8 ESW 648 = 750 M TM2 V8 8 & 9 10 & 11
1 Second 8 EWS 0x288 = 1000 M TM2 0x288 8 & 9 10 & 11
2.496 Seconds 24 EWS 664 = 2496 M TM2 V24 24 & 25 26 & 27
60 Seconds 28 EWS 668 = 60000 M TM2 668 28 & 29 30 & 31

1 to
4,294,967,290
mSec
2-byte
(WORD)
mSec
4 mSec 0 ESL 640 = 4 M TM4 V0 0, 1, 2 & 3 4, 5, 6 & 7
60 Seconds 24 ESL 664 = 60000 M TM4 664 24, 25, 26 & 27 28, 29, 30 & 31
6 Minutes 24 ESL 0x298 = 360000 M TM4 V24 24, 25, 26 & 27 28, 29, 30 & 31
1 Hour 9 ELS 649 = 3600000 M TM4 0x289 9, 10, 11 & 12 13, 14, 15 & 16

1 to
256
Seconds
1-byte
(BYTE)
Seconds
4 Seconds 2 ES 642 = 4 S TS1 V2 2 3
60 Seconds 24 ES 664 = 60 S TS1 0x298 24 25
240 Seconds 30 ES 670 = 240 S TS1 V30 30 31

1 to
65,536
Seconds
2-byte
(WORD)
Seconds
4 Seconds 2 ESW 642 = 4 S TS2 V2 2 & 3 4 & 5
60 Seconds 24 ESW 664 = 60 S TS2 0x298 24 & 25 26 & 27
600 Seconds 6 EWS 646 = 600 S TS2 V6 6 & 7 8 & 9
900 Seconds - ESW 0x500 = 900 S TS2 0x500 none; above uVars.B[##] range

All Foreground and Background processes (command processing, serial output, nRF radio, WS2812, etc.) continue to operate while a script timer is timing. The seconds heartbeat LED, for example, can be used to demonstrate this aspect. Recall from the "S" command description, that when an "S" or "S =" command is executed, it will display the script operation pointer.

Example of entering a script, starting at EEPROM address 0x130:
>S = -1 E   ; Park the script pointer at an invalid EEPROM address
Next operation pointer: 0xFFFF
>DE+ 0x130 48   ; Verify that portion of EEPROM is unused
EEPROM contents:
0130:  FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  ................
0140:  FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  ................
0150:  FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  ................
>E 0x130 = "ESW 0x500 = 4 S"
>E 0x140 = "TS2 0x500"
>E 0x14A = "ESW 0x510 = 1 S"   ; Saved after the timer expires
>DE+ 0x130 48
EEPROM contents:
0130:  45 53 57 20 30 78 35 30-30 20 3D 20 34 20 53 00  ESW 0x500 = 4 S.
0140:  54 53 32 20 30 78 35 30-30 00 45 53 57 20 30 78  TS2 0x500.ESW 0x
0150:  35 31 30 20 3D 20 31 20-53 00 FF FF FF FF FF FF  510 = 1 S.......
>DS+ 0x500 20
SRAM contents:
0500:  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0510:  00 00 00 00                                      ....
>S = 0x130   ; Start the script at EEEPROM address 0x130
Next operation pointer: 0x0130 ESW 0x500 = 4 S
>>>                                                                             

Notice that when the script started, it immediately output two (2) prompt (">") characters, as shown above. It then began timing and after 4 seconds, the third prompt character was printed, after the timer timed out. If the seconds heartbeat LED was enabled, the LED changed states while the timer was timing. Many timers can be timing simultaneously, but only a single script can be running at any time.

Shown below is the output beginning at the time that the script was started, so some is repeated. After the timer fired, the last script line which saved the time value was executed. Let's check that first.

Next operation pointer: 0x0130 ESW 0x500 = 4 S
>>>DS+ 0x500 20   ; Display the section of SRAM used
SRAM contents:
0500:  04 00 73 1C 00 00 00 00-00 00 00 00 00 00 00 00  ..s.............
0510:  01 00 77 1C                                      ..w.
>                                                                               

There was no output while the timer was timing. Script debugging can be enabled so you can actually see that it is doing something, and even what portion of the script is executing. Type the ']' character at the start of a new line. If it is displayed, it was not the first character on the line. That toggle key can generate significantly more output. Before executing the next command, it will probably be very good to note that it can even be toggled (off or on) while a script is running (especially if the Stack Dump option with the Registers display, using '}', is enabled.)

>S = 304  ; = 0x130

 10   32   54   76   98  1110 1312 1514 1716 1918 2120 2322 2524 2726 2928 3130  SP  RetAdd   SREG
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ------ -------- 
0000 054D 090E FF28-0004 0000 0000 6000-0021 007F 5312 3D20-940F 0230 0280 1B00 08F1  3604  ItHsvnZc 
Next operation pointer: 0x0130 ESW 0x500 = 4 S
> <==> Next operation pointer: 0x0130 ESW 0x500 = 4 S
 <==> Next operation pointer: 0x0140 TS2 0x500
0000 054E 090E FF28-0004 0000 0000 6000-0021 207F 450F 5753-940F 0230 0280 1B00 08F1  3604  ItHsvnZc
> <==> Next operation pointer: 0x0140 TS2 0x500
 <==> Next operation pointer: 0x0140 TS2 0x500
 <==> Next operation pointer: 0x0140 TS2 0x500
     (omitted the same line above, repeatedly output over 430 times during the 4 second delay)
 <==> Next operation pointer: 0x0140 TS2 0x500
 <==> Next operation pointer: 0x014A ESW 0x510 = 1 S
 <==> Next operation pointer: 0x015A

 10   32   54   76   98  1110 1312 1514 1716 1918 2120 2322 2524 2726 2928 3130  SP  RetAdd   SREG
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ------ --------
0000 0002 0912 FF28-0004 0000 0000 6000-0021 007F 450F 5753-940F 0230 0280 1B00 08F1  3604  ItHsvnZc
>
...  (toggled debugging back off using ']' and '}')
>
If your terminal program cannot display lines with 100 characters, the output may look different that that shown above. Be sure that script debugging is toggled off and copy "E 4 ^= 32" into your cut-and-paste buffer (Ctrl-C in Windows) and enter these 3 commands, which are shown entered into the captured screen below:
  S = -1
  E 4 ^= 32 ; Display the script logic Hz line each second
  S = 0x130
>S = -1
Next operation pointer: 0xFFFF
>E 4 ^= 32  ; Display the script logic Hz line each second
>Script logic Hz: 1
Script logic Hz: 1
>S = 0x130
Next operation pointer: 0x0130 ESW 0x500 = 4 S
>>Script logic Hz: 29
Script logic Hz: 2,943
Script logic Hz: 2,940
Script logic Hz: 2,942
>Script logic Hz: 2
Script logic Hz: 1
E 4 ^= 32  ; Disable the 1 second script logic Hz output
>                                                                               

When an "S" or "S =" command is executed, the script operation pointer string is output to the COM1 serial port. It helps to have the command you'll need in the clipboard and just paste "E 4 ^= 32" into the terminal window.

Scripts obey all rules which apply to a System Command, with the additional restriction of length: 50 BYTEs for scripts, 80 BYTEs for Commands. For example, the iHex memory change command requires that the device be in Manager or Administrative mode to actually make any change. If the device is not in either of those modes at the time the script command is issued, the desired change will not take place.

Script extensions are also like command extensions in that they are enabled by placing a script extension handler routine in FLASH and entering its address in EEfptrScriptExts (which is defined in SystemEEPROM.Def) in EEPROM.

The Script Processor only enbuffers a script command into the Parsing Buffer; it does not directly call the System Command Processing routine, since it doesn't know what else may be occurring in the system. If there is something in the parsing buffer (for example, an incomplete command being typed, or even just a single space character), the Script Processor will wait to enbuffer the script command. Only the main Operating System routine actually calls the System Command Processor and does that in the Foreground. Note that a Background process can be interrupted by the Foreground process.

Here is an example of a bootup script:
>DE+ 0xB0 0x70
00B0:  3B 20 42 6F 6F 74 75 70-20 49 6E 69 74 3A 20 00  ; Bootup Init: .
00C0:  45 20 31 20 7E 3D 20 30-78 43 30 00 45 53 20 30  E 1 ~= 0xC0.ES 0
00D0:  78 31 44 46 20 3D 20 30-78 33 41 00 4E 2A 2A 00  x1DF = 0x3A.N**.
00E0:  4E 44 2B 00 50 20 3D 20-38 00 50 47 3D 31 20 00  ND+.P = 8.PG=1 .
00F0:  56 57 20 34 20 3D 20 39-30 30 20 4D 00 54 4D 32  VW 4 = 900 M.TM2
0100:  20 56 20 34 00 50 42 3D-31 00 45 20 30 78 31 39   V 4.PB=1.E 0x19
0110:  20 3D 20 30 78 31 35 00-45 20 33 32 20 3D 20 30   = 0x15.E 32 = 0
0120:  78 32 32 00 FF FF FF FF-FF FF FF FF FF FF FF FF  x22.............
>                                                                               
The content and meaning of each of the ten (10) script lines:
  ; Bootup Init:This is simply a comment
  E 1 ~= 0xC0Disable both the sequencing and multitasking logic
  ES 0x1DF = 0x3A    Set the format of the Register Dump ("R") command
  N**Load the default nRF24L01+ configuration
  P = 8Configure the WS2812 routine for 8 pixels (24 BYTEs)
  PG=1Set all 8 pixels to Green, intensity 1, and output them
  VW 4 = 900 MInitialize a WORD timer for 900 mSec
  TM2 V 4Run the timer to perform the delay
  PB=1Set all 8 pixels to Blue, intensity 1, and update them
  E 0x19 = 0x15Configure the heartbeat LED to use PORT B, BIT 5
  E 32 = 0x22Configure the Manager/Admin mode indicator LED to use PORT C, BIT 2

Also see:  The PrintScriptProgramHz system function, "S" command documentation and the Annunciate example program which invokes scripts.