The uVars.B[] array is the block of SRAM in a constant location which is allocated and reserved for User Variables (or V-Registers.) As can be done with all of SRAM, they can be displayed using the MIRTOS "DS" (Display SRAM) command and edited using the "ES" Edit SRAM command. This Command Extension allows them to be edited using a "U" or "V" command as well. For example, you could use "V 4 = 200" to set uVars.B[4] to 200 instead of using the more explicit "ES 0x284 = 200".
This Command Extension comes with MIRTOS. Although the source code below only assembles to us about 170 BYTEs of FLASH, if you don't use V-Registers, it can be removed. This source code is contained in two subroutines, both of which are documented here. It is probably best to place both subroutines in the same file. CmdExtUorV.Asm is the filename used by this example and is the file pulled in by the main Command Extensions source code module. Again, you may want to add a blank line or two between blocks of code as you copy it in order to improve readability.
/****************************************************************************** * * Routine "CmdExt_UorV" * * Purpose: Verify that the type of 'V' command issues is in the syntax that * is requesting V-Register(s) be changed (e.g., "V 11 += 2 3 4".) * Examine and modify the command buffer, implementing the command * to change one or more value(s) in the User Variable (uVars.B[##]) * SRAM array. Change the 'V' command to the Operating System's * built-in "ES" (Edit SRAM) system command to perform the change(s). * * Assumes: The buffer being parsed is expected to be the command buffer, but * it need not necessarily be that. The command buffer has the same * address MSByte for all elements (e.g., 0x230 to 0x27F) and the * buffer being parsed should also have that attribute. Some BYTEs * should be available in the buffer passed, if needed - up to * either three (3) for the 328P, or four (4) for the 1284P or 2560. * * Passed: R29:R28 *uVars.B[0] * R27:R26 *bBuffer being parsed (format notes below): * R23 third buffered character, uppercase (but may be 0) * R22 second buffered character, uppercase (but may be 0) * R21 first buffered character, uppercase; the command byte * R20 string length, or buffered byte count * * Returns: ZERO FLAG TRUE if NOT handled, FALSE if it was handled * (the first command upon return is BRNE to the EXIT) * * Notes: This is just a reminder that the only safe changes to make when * MIRTOS is to process the command are those to the FLAGs and to * Registers R0, R18, R19, R24, R25, R30, and R31. Of course, the * point of this routine is to possibly modify the command string! * ******************************************************************************/
This is the entry point to these subroutines. The main Command Extension routine passes execution here after only verifying that the first command letter is either a "U" or a "V", either uppercase or lowercase. There must be an equal sign within the command for this to be the syntax that these extensions support.
CmdExt_UorV: MOV R18, R22 ; Save 2nd buffered character for exit MOVW R24, R26 ; R27:R26 passed is the buffer pointer LDI R22, '=' ; Search for the first '='; if not found, RCALL ACmdExt_strchr ; the command is not in the format that BREQ ACmdExt_UorV_Exit ; this routine is designed to handle ; Setup for the strchr() call (above and below): ; Pass: R25:R24 byte * szBuffer address to examine ; R22 byte bChar to find first occurrence ; Returns: R25:R24 byte * pointer to the character in the buffer or NULL ; (Also): ZERO FLAG TRUE if NOT found (R25:R24 = NULL), FALSE if found ; Alters: Only R24, R25, and the FLAGS are changed ; CmdExt_UorV_CheckComment: MOVW R30, R24 ; Save the pointer to '=' returned MOVW R24, R26 ; Reload the buffer pointer then set the LDI R22, ';' ; search character (comment delimiter) RCALL ACmdExt_strchr ; Only check the relative position of the BREQ ACmdExt_UorV_Process ; '=' BYTE when there was a comment SUB R24, R30 ; If the first '=' is after the ';', it is ;- SBC R25, R31 ; (not needed) ; within the comment, so process as the BRLO ACmdExt_UorV_Exit ; original command by just returning
As the comment for the first line of source code below states, the helper routine being called handles most of the command transformation. Since more Registers are needed to parse and modify the command buffer, it's much easier to use a subroutine at this point. If the helper routine rejects the syntax, it will return with both the Registers and the buffer preserved so the original, default Operating System command handler can process the command.
CmdExt_UorV_Process: RCALL ACmdExt_UorV_Helper ; The subroutine does most of the work; it BRNE ACmdExt_UorV_Exit ; returns the ZERO flag CLEAR if rejected ; Setup for the strcpy() call: ; Passed: R25:R24 byte * bPtrTo pointer to the destination SRAM buffer ; R23:R22 byte * bPtrFrom pointer to the source SRAM buffer ; Returns: R25:R24 byte * the same bPtrTo pointer as the caller passed ; Alters: R21, the FLAGs, and the destination buffer specified ; MOVW R24, R26 ; The source pointer is the buffer address ADIW R24, 1 ; plus 1 (skip the string length), then MOVW R22, R24 ; the destination pointer is that value ADIW R24, 1 ; plus another 1, making room (1 BYTE) to RCALL ACmdExt_strcpy ; change the "V" to an "ES" -> Edit SRAM
The last five (5) assembly instructions above and those before CmdExt_UorV_Exit: below are only executed if the helper routine has already made some changes to the command buffer.
Register pair R23:R22 was pointed at the second BYTE in the SRAM buffer by the code above; the call to strcpy() didn't change it. The original SRAM buffer pointer in "X" (R27:R26) has not been modified either, so point "Z" (R31:R30) at it after determining the string length. The task here is to replace the "U" or "V" with "ES" in the command buffer, to update R21, R22, R23 with the first three (3) buffered characters, and to set the new command string length.
; Setup for the strlen() call: ; Passed: R25:R24 byte * szBuffer address to examine ; Returns: R25:R24 word the count of bytes in the ASCIIZ string ; ZERO FLAG set (TRUE) when length is zero (and R25:R24 = 0) ; clear (FALSE) when length is > 0 (and R25:R24 > 0) ; Alters: Only R25 and R24 (the value being returned) ; CmdExt_UorV_PrepareToExit: MOVW R24, R22 ; The source buffer pointer as used above CALL Astrlen ; R24 has the BYTE string length returned MOVW R30, R26 ; Pointer to the buffer; 1st BYTE is count ; When changing the command to (also) have MIRTOS handle it, some Registers ; and SRAM (buffer) contents will change. Specifically, set these here: ; R20 string length, or buffered BYTE count ; R21 first buffered character, uppercase; the command BYTE 'E' ; R22 second buffered character, uppercase; the BYTE 'S' ; R23 third buffered character, uppercase; a size modifier or ' ' ; ST Z, R24 ; Replace the prior buffered BYTE count MOV R20, R24 ; with that obtained from strlen() call LDI R21, 'E' STD Z+1, R21 ; Set the 1st buffered character (now 'E') MOV R23, R18 ; 2nd uppercase buffered char is now 3rd LDI R18, 'S' ; Notice that R22 is set from R18 below STD Z+2, R18 ; Set the 2nd buffered character (now 'S')
This is the "parent" routine's exit point. Whether the command buffer was changed or not, it returns the same result, which is to process either the original command or the modified commmand.
CmdExt_UorV_Exit: MOV R22, R18 ; Restore/set the 2nd buffered character RJMP ACmdExt_NotHandled ; Let the Operating System do the work ;- SEZ ; (alternate ; The ZERO flag TRUE tells the caller "NOT ;- RET ; method) ; HANDLED", or "go ahead and process it"
A rather long subroutine explanation header follows. It lists quite a few examples of possible command transformations, for each of the three (3) supported microcontrollers. The "Exp." column shows the number of BYTES that the command "expanded." The "+1" is for the "V" to "ES" common to all and the second value is the change in the number of numeric characters when transforming a V index to an absolute SRAM address.
/****************************************************************************** * * Routine "CmdExt_UorV_Helper" * * Purpose: Assist the routine above. Since more than just a few Registers * need to be preserved and restored, a routine that begins with a * PushAll() and exits with a PopAll() is a great way to do that. * * Assumes: The buffer being parsed may be the command buffer, but it need * not necessarily be. The buffer has the same address MSByte for * all elements (e.g., 0x230 to 0x27F for the command buffer.) Some * BYTEs should still be available in the buffer, if needed. Up to * either three (3) for 328, or four (4) for the 1284 and 2560. * * Passed: R29:R28 *uVars.B[0] * R27:R26 *bBuffer being parsed (format notes below): * R22 the character ';', from the comment check * R21 first buffered character, uppercase; the command byte 'V' * R20 string length, or buffered byte count * R18 second buffered character, uppercase * * Returns: The ZERO flag CLEAR if rejected for any reason (or) * SET if processed and the buffer was changed * * Alters: The command buffer and the FLAGS. Since it uses the PushAll() * subroutine on entry and the PopAll() subroutine on exit, none of * the caller's Register values are (or even can be) modified. * * Example: Some example commands, with their resulting buffer changes: * Command 328P Exp. 1284P Exp. 2560 Exp. * ----------- ------------------ ------------------ ------------------- * V 0 += 1 ES 640 += 1 +1+2 ES 864 += 1 +1+2 ES 1472 += 1 +1+3 * V 0x2 += 1 ES 642 += 1 +1+0 ES 866 += 1 +1+0 ES 1474 += 1 +1+1 * V 10 = 1 2 ES 650 = 1 2 +1+1 ES 874 = 1 2 +1+1 ES 1482 = 1 2 +1+2 * VW 14 += 1 ESW 654 += 1 +1+1 ESW 878 += 1 +1+1 ESW 1486 += 1 +1+2 * VL 16 -= 1 ESL 656 -= 1 +1+1 ESL 880 -= 1 +1+1 ESL 1488 -= 1 +1+2 * VL 0x10 = 2 ESL 656 = 2 +1-1 ESL 880 = 2 +1-1 ESL 1488 = 2 +1+0 * V 100 ~= 2 ES 740 ~= 2 +1+0 ES 964 ~= 2 +1+0 ES 1572 ~= 2 +1+1 * V 136 ^= 4 ES 776 ^= 4 +1+0 ES 1000 ^= 4 +1+1 ES 1508 ^= 4 +1+1 * V 255 |= 8 ES 895 |= 8 +1+0 ES 1119 |= 8 +1+1 ES 1727 |= 8 +1+1 * V 0xFF = 9 ES 895 = 9 +1-1 ES 1119 = 9 +1+0 ES 1727 = 9 +1+0 * * Notes: Avoid modifying R8, the various "Quick Access" bits, especially * if using any system memory dump routines to debug the code. * ******************************************************************************/
The code begins by searching for the field delimiter, a space. If one is found, it makes a ParseValue system call. That routine both skips past any space (or spaces) that precede the expected numeric value and returns flags indicating, among other things, if a numeric result was parsed from the buffer. The exit is taken unless a valid number was parsed into R25:R24:R23:R22 and R25, R24 and R23 are all zeros.
CmdExt_UorV_Helper: RCALL ACmdExt_PushAll ; Since we'll need most of the Registers MOVW R6, R26 ; Save in a low, preserved, register pair CmdExt_UorV_SearchLoop: LD R25, X+ ; Search the buffer until either the first CPI R25, 0 ; NULL (string terminator) or a space BREQ ACmdExt_UorV_FailExit ; character is found; this ends up either CPI R25, ' ' ; bailing out on the NULL or with R27:R26 BRNE ACmdExt_UorV_SearchLoop ; pointing to the first non-space MOVW R10, R26 ; Save the pointer to the array index text ; Setup for the ParseValue() call: ; Passed: R25:R24 char * cPtrBuffer pointer to buffer to examine ; Returns: R25:R24:R23:R22 long lData 4-byte data value read ; (Also): R27:R26 char * pointer to next byte to parse (or NULL) ; R21 byte bFlags indicates results (BIT_PV_*) ; BIT_PV_VALID = 0 -> B00000001 BIT_PV_OVERFLOW = 4 -> B00010000 ; BIT_PV_ACTIVE = 1 -> B00000010 BIT_PV_POSITIVE = 5 -> B00100000 ; BIT_PV_HEX = 2 -> B00000100 _PV__spare6__ = 6 (unused; spare) ; BIT_PV_NONZERO = 3 -> B00001000 BIT_PV_NEGATIVE = 7 -> B10000000 ; MOVW R24, R26 ; Point to the index text in the buffer CALL AParseValue ; and parse the value at that address CmdExt_UorV_AfterParse: SBRS R21, BIT_PV_VALID ; If the PV_VALID bit is set, skip over RJMP ACmdExt_UorV_FailExit ; the bailout RJMP instruction OR R24, R23 ; Result in R25:R24:R23:R22 cannot be more ADIW R24, 0 ; than 255, so R25, R24, and R23 must all BREQ ACmdExt_UorV_Validate ; be zero; accept the command if so
This code block below begins with the fail exit. In addition to the code immediately above, which can either branch around or fall into it, there are three (3) other places in this subroutine that jump to this point, one of which is in the 328P conditional code just below it. Note that no change has been made to the original command buffer yet.
The comments in the middle portion should be sufficient. They last portion of this code block are the set up for the loop which replaces all of the index text BYTEs (i.e., the "20" portion of "V 20 = 4") with spaces. The comment on the line with "not needed" is because the MSByte of the SRAM address of the system command buffer's first and last BYTE is the same. Explicitly, the range is 0x0230 to 0x027F in the 328P and 1284P and 0x0330 to 0x037F in the 2560. The comment was left as a reminder of a potential bug source if some SRAM buffer other than the system command buffer is used.
CmdExt_UorV_FailExit: CLZ ; Set the "rejected" return status and RJMP ACmd_Ext_UorV_SubRet ; jump to exit using the PopAll() jump CmdExt_UorV_Validate: .IF PROCESSOR == 328 ; Only in the 328, the V-Register count LDI R25, 32 ; can be between 32 and 256; R15 has the ADD R25, R15 ; adder to the 32; if the index is the CP R22, R25 ; same or more than that configured value BRSH ACmdExt_UorV_FailExit ; (R15+32), exit since it's invalid .ENDIF ; Note: Commands with multiple values assigned (e.g., "V 255 += 1 2 3 4") can ; overrun past the end of the uVars.B[##] array. That aspect is NOT checked. SUB R26, R10 ; Calculate the index text BYTE count ;- SBC R27, R11 ; (not needed) ; Parsing buffer range MSByte assumption LDI R19, ' ' ; Overwrite the index text with spaces MOV R24, R26 ; Set the number of BYTEs to replace MOVW R30, R10 ; Point to where the index text begins
It takes more code to test whether or not to overwrite the index text than to just replace it with spaces. Also, "V 0x000F += 1", for example, is a valid command. Leaving any part of the "0x000F" would cause a bug. As noted above, the command buffer range is usually 0x230 to 0x27F or 0x330 to 0x37F, so its size is 0x40 (= 80) BYTEs, but the command may not be presented from there.
CmdExt_UorV_ReplaceLoop: ST Z+, R19 ; Loop here, replacing all of the index DEC R24 ; string characters with spaces (it only BRNE ACmdExt_UorV_ReplaceLoop ; takes 6 BYTEs, or 3 OPs for each loop) MOV R12, R22 ; Save the array index value just parsed ; Register contents at this point: ; R7:R6 Pointer to the buffer to examine (BYTE #1 is buffered byte count) ; R10:R11 Pointer to the array index text (e.g., "15") within the buffer ; R12 The array index (the value parsed above, transferred from R22) ; * R15 The uVars.B[##] array size adder (* only used for the 328P MCU) ; R19 A space (' ') character, which should persist until PopAll ; R21 The bFlags results (BIT_PV_*) from the ParseValue() call ; R22 The parsed value of the array index text (about to be trashed) ; R26 BYTEs in the array index text (e.g., 2 for "15" in "V 15 += 1")
Some number of BYTEs which are after the index text need to be moved to make room for the SRAM address. Determine what that number of BYTEs to advance is. The decimal absolute SRAM address text (i.e., after "ES") is always 3 BYTEs in the 328P, always 4 BYTEs in the 2560, but can be either 3 or 4 BYTEs in the 1284P. Since any extra space will be ignored during "Edit SRAM" command parsing, 4 BYTEs are always used the 1284P as well.
.IF PROCESSOR == 328 POSITIONS_NEEDED = 3 ; This is the number of BYTEs needed to .ELSE ; represent any uVars.B[##] index in POSITIONS_NEEDED = 4 ; decimal (not hexadecimal) format .ENDIF CmdExt_UorV_Expand: LDI R20, POSITIONS_NEEDED ; Number of BYTEs needed for any index SUB R20, R26 ; How many characters to insert into the BREQ ACmdExt_UorV_NewIndex ; index string location; skip the copy if BRLO ACmdExt_UorV_NewIndex ; no characters need to be inserted ; Setup for the strcpy() call: ; Passed: R25:R24 byte * bPtrTo pointer to the destination SRAM buffer ; R23:R22 byte * bPtrFrom pointer to the source SRAM buffer ; Returns: R25:R24 byte * the same bPtrTo pointer as the caller passed ; Alters: R21, the FLAGs, and the destination buffer specified ; MOVW R22, R10 ; The source and destination pointers are MOVW R24, R10 ; the same, except add how many BYTEs to ADD R24, R20 ; insert to the destination pointer RCALL ACmdExt_strcpy ; Move the substring, inserting spaces
The validated index (0 to 255) returned is in R12, to which the uVars.B[##] starting address is added. That value is 640 (= 0x280) for 328P, 864 (= 0x360) for the 1284P, or 1472 (= 0x5C0) for the 2560.
; Setup for the FormatWORD() call: ; Passed: R25:R24 WORD wValue to be formatted ; R23:R22 byte * pointer to the szDest buffer ; R20 byte bFlags with 3 bit variables used: ; BIT_FMT_COMMAS (1 includes commas, 0 omits) ; BIT_FMT_POST_Sp (1 pad a ' ' after, 0 don't) ; BIT_FMT_PRINT (1 print the string, 0 don't) ; Alters: R18, R20, R21, R22, R23, R24, and R25 ; CmdExt_UorV_NewIndex: LDI R25, uVars >> 8 ; Calculate the uVars.B[##] array index LDI R24, uVars & 0xFF ; of the value parsed from the buffer, ADD R24, R12 ; noting that it is a 2-BYTE value, so a ADC R25, R1 ; CARRY may be needed MOVW R22, R10 ; Set the string's starting address LDI R20, 0 ; No BIT_FMT_* options selected CALL AFormatWORD ; Let the system routine insert the text MOVW R30, R10 ; Reload the index string pointer CmdExt_UorV_Find0Loop: LD R25, Z+ ; FormatWORD terminates each string with a CPI R25, 0 ; NULL byte; since the index text parsed BRNE ACmdExt_UorV_Find0Loop ; can vary in length, find the first NUL SBIW R30, 1 ; after that string (the "SBIW R30, 1" is ;- LDI R19, ' ' ; (redundant) ; needed because Z was post-incremented) ST Z, R19 ; Replace that NUL terminator with a space ; RCALL ACmdExt_DumpRegs_Begin ; Remove either or both comments here to ; RCALL ADumpParsingBuffer ; see the Registers or buffer changes SEZ ; Set the "valid, processed" return status Cmd_Ext_UorV_SubRet: RJMP ACmdExt_PopAll ; MUST use JUMP for all PopAll() accesses
The buffer has been changed if the bulk of the code block above was executed. It is not yet ready to be give to the System Command Processor, however, since the command length BYTE as well as Registers R21, R22, and R21 have not been updated. That little buffer cleanup task is done by the CmdExt_UorV routine, to which the helper above is returning.
C:\TestDir> Copy CON CmdExtUorV.Asm (copy each of the eleven (11) blocks above) ^Z C:\TestDir>
After copying all of source code blocks into the CmdExtUorV.Asm file, use Control-Z (or F6) to end input and close that file. Note that this code will not assemble as a stand-alone file. Return to the main Command Extensions source code, which pulls this file into the Assembly.