The first way is to access them indirectly using the ICALL and/or IJMP microcontroller (or "MCU") instruction, as appropriate, by loading the index into R30 and then calling or jumping to (again, as appropriate) to absolute address 0, which is named SoftVector in the example code.
The second way is to use each one's Addresses###.Def (see the links below) in the 128-entry System Function Jump Table. It is not anticipated that the first 125 will change, but the last three (3) very well might.
For routines that have a MUST use note, using the ICALL when the IJMP is specified (or vice versa) will very likely cause a system crash and certainly cause data corruption. For routines that do not have that note, either an ICALL or IJMP may be used, depending on stack usage at that point, of course. Note, however, that using any form of a JMP instruction when trying to use a routine which returns a value is really not that useful.
The general Register usage rules for subroutines, (unless otherwise noted):
* R0 is a temporary "scratch" Register; assume any called routine may have
changed it,
* R1 should always contain the value zero (0), but may be used and restored,
if needed,
* R2 through R17, R28 and R29 contain system variables; although they may also
be used, they MUST restored to their prior system value on exit,
* R18 through R27, R30 and R31, like R0, may all be used without saving and
restoring them.
Arguments are passed in Registers R25 to R18, with BYTE values occuping WORD Register pairs. Refer to the examples and documentation of each to understand the idea of how each of these work. Each function's documentation lists which Register(s) can possibly be altered by that function. Most routines preserve some or all of Registers R0, R18 to R27, R30, and R31 in order to ease the burden of repeated Register save/restore requirements for their callers.
These rules may seem somewhat familiar. They are the same as those used by the AVR compiler and the Arduino IDE and its GCC compiler. Since there is nothing objectionable with those rules, and they may already be familiar, they were simply reimplemented for MIRTOS. It also allows Assembly code generated by those compilers to be more easily incorporated into a MIRTOS-based device.
Index | System Function Name | Description | Routine aliases and notes |
---|---|---|---|
0 | PushAll | Push Registers R31 through R0, in that order | PushAllRegisters; MUST use CALL or ICALL |
1 | PopAll | Pop Registers R0 through R31, in that order | PopAllRegisters; MUST use JMP or IJMP |
2 | StackAlloc50 | Allocate 50 BYTEs on the stack | MUST use CALL or ICALL |
3 | StackFree50 | Free the 50 BYTEs previously allocated by StackAlloc50 | MUST use CALL or ICALL |
4 | StackAlloc250 | Allocate 250 BYTEs on the stack | MUST use CALL or ICALL |
5 | StackFree250 | Free the 250 BYTEs previously allocated by StackAlloc250 | MUST use CALL or ICALL |
6 | Calculate_UBRR | Given the data rate, calculate the USART Baud Rate Register (UBRR) WORD value | |
7 | Check_BG_Task | See if there is any Background task (printing, specifically) to process | |
8 | ICALL_IfValid_EEPROM | Call a subroutine if the EEPROM WORD passed points to an initialized WORD | Initialized here means any value except 0xFFFF |
9 | ICALL_IfValid_PGM | Call a subroutine if the FLASH pointer passed points to an initialized WORD | Initialized here means any value except 0xFFFF |
10 | ReadAtoDValue | Read one of the eleven Analog Input values | GetAtoDValue |
11 | ReadFuseBYTE | Read one of the MCU Fuse BYTEs or the MCU Lock BYTE | |
12 | ReadSignatureBYTE | Read one of the MCU Signature BYTEs or the MCU Oscillator Calibration BYTE | |
13 | millisLowBYTE | Read the low order BYTE (0 to 255) of the milliseconds rollover counter | |
14 | millisLowWORD | Read the low order WORD (0 to 65,530) of the milliseconds rollover counter | |
15 | millis | Read the unsigned LONG (0 to 4,294,967,295) milliseconds rollover counter | |
16 | SystemSecondsLowBYTE | Read the low order BYTE (0 to 255) of the seconds rollover counter | |
17 | SystemSecondsLowWORD | Read the low order WORD (0 to 65,535) of the seconds rollover counter | |
18 | SystemSeconds | Read the unsigned LONG (0 to 4,294,967,295) seconds rollover counter | |
19 | msTimerBYTE | Determine if a 0 to 250 millisecond timer has completed its time | |
20 | msTimerWORD | Determine if a 0 to 65,535 millisecond timer has completed its time | |
21 | msTimerULONG | Determine if a 0 to 4,294,967,290 millisecond timer has completed its time | |
22 | SecTimerBYTE | Determine if a 0 to 250 Seconds timer has completed its time | |
23 | SecTimerWORD | Determine if a 0 to 65,530 Seconds timer has completed its time | |
24 | CheckIndexAndTimer | Check both for a valid uVars.B[##] index and a related timer has completed its time | |
25 | CRC | Generate a 16-bit CRC over a range of SRAM | |
26 | RunningCRC | Add another BYTE into a CRC calculation already in progress | |
27 | meminit | Initialize a range of SRAM btyes to some constant value | memset |
28 | memcpy | Copy a range of SRAM to another SRAM location | Handles any range overlapping correctly |
28 | strchr | Search for a specific character in an SRAM ASCIIz string | |
30 | strcmp | Case-sensitive comparison of one SRAM ASCIIz string with another | |
31 | strcpy | Copy an ASCIIz character string from one SRAM location to another | Handles any range overlapping correctly |
32 | strlen | Determine the number of BYTEs in an ASCIIz string in SRAM | |
33 | ReadEEPROM_BYTE | Read a single BYTE from EEPROM | ReadEEPROM_1BYTE or ReadEEPROM |
34 | ReadEEPROM_WORD | Read 2 successive BYTEs from EEPROM, low order BYTE first | ReadEEPROM_2BYTEs |
35 | ReadEEPROM_ULONG | Read 4 successive BYTES from EEPROM, low order first to high order last | ReadEEPROM_4BYTEs |
36 | WriteEEPROM_BYTE | Write a single BYTE to EEPROM | WriteEEPROM_1BYTE or WriteEEPROM |
37 | WriteEEPROM_WORD | Write 2 successive BYTEs to EEPROM, low order BYTE first | WriteEEPROM_2BYTEs |
38 | WriteEEPROM_ULONG | Write 4 successive BYTEs from EEPROM, low order first to high order last | WriteEEPROM_4BYTEs |
39 | meminitEEPROM | Initialize a range of EEPROM BYTEs to some constant value | memsetEEPROM |
40 | memcpyEEPROM | Copy a range of EEPROM into an SRAM buffer | Because SRAM is the default copy destination |
41 | memcpySRAMtoEEPROM | Copy a range from SRAM to EEPROM | memcpyRAMtoEEPROM |
42 | memcpyEEPROMtoEEPROM | Copy a range of EEPROM to another EEPROM location | Handles any range overlapping correctly |
43 | strlenEEPROM | Determine the number of BYTEs in an ASCIIz string in EEPROM | |
44 | ReadPGM_BYTE | Read a single BYTE from the FLASH, or PROGRAM memory which MIRTOS exposes | ReadPGM_1BYTE or ReadPGM |
45 | ReadPGM_WORD | Read 2 successive BYTEs from FLASH memory, low order BYTE first | ReadPGM_2BYTEs |
46 | ReadPGM_ULONG | Read 4 successive BYTEs from FLASH memory, low order to high order BYTE | ReadPGM_4BYTEs |
47 | memcpyPGM | Copy a range of FLASH memory to an SRAM buffer | Because SRAM is the default copy destination |
48 | strcmpPGM | Case-sensitive comparison of a ASCIIz string in FLASH with one in SRAM | |
49 | strcpyPGM | Copy an ASCIIz string from FLASH memory to an SRAM buffer | |
50 | strlenPGM | Determine the number of BYTEs in an ASCIIz string in FLASH memory | |
51 | EraseFLASHPage | Reset all (128 or 256) BYTEs of a PROGRAM memory page to 0xFF | Only when logged on in either Manager or Admin mode |
52 | FindFirstNumericDigit | Search a range of SRAM for a numeric (0 through 9) ASCII character | |
53 | FormatULONGHex | Format an UNSIGNED LONG INTEGER (32-bit) value as an ASCIIz hexadecimal string | |
54 | FormatULONG | Format an UNSIGNED LONG (32-bit) value as an ASCIIz decimal string | |
55 | FormatWORD | Format a WORD (16-bit or UNSIGNED INTEGER) value as an ASCIIz decimal string | |
56 | FormatCopyAndPrint | Designed for the three (3) subroutines above to copy and/or print the resulting string | |
57 | High4BitsToPORT | Convert the 4 high bits of R24 to the SRAM address of the MCU Port | 1 = PORTB, 2 = PORTC, 3 = PORTD |
58 | Low4BitsToPORT | Same as above, but uses the 4 low order bits of R24 as the index | |
59 | Low3BitsToBitmask | Convert the binary value in the 3 low order bits of R24 to 1, 2, 4, 8, 16, 32, 64, or 128 | |
60 | COM1_GetRxCount | Determine the number of BYTEs that are in the serial receive buffer | |
61 | COM1_GetRxFree | Determine the number of BYTEs left in the serial receive buffer before it overflows | |
62 | COM1_RxPeekHead | Get, but do not remove, the next BYTE from the serial port receive buffer | |
63 | COM1_RxPeekTail | Get, but do not alter, the last BYTE that is in the serial port receive buffer | |
64 | COM1_Read1CHAR | Read and remove a BYTE from the serial port receive buffer | |
65 | COM1_GetTxCount | Determine the number of buffered BYTEs awaiting transmission out the serial port | |
66 | COM1_GetTxFree | Determine how many BYTEs can be written to the serial port buffer without blocking | |
67 | COM1_TxPeekHead | Get the next enbuffered BYTE that will be sent out the serial port | |
68 | COM1_TxPeekTail | Get the last character that was sent to the serial port for transmission | |
69 | COM1_WaitForTxEmpty | Hold (block), waiting for the serial output buffer to be completely empty | |
70 | COM1_Write1CHAR | Output a single character out the serial port; block if necessary | |
71 | COM1_Write | Output a specific number of BYTEs out the serial port; block if needed | |
"Print" in the lines below means "buffer and emit output to the COM1 serial port" | |||
72 | PrintEEPROMASCIIz | Print an ASCIIz (NULL terminated) string from EEPROM | |
73 | PrintFLASHASCIIz | Print an ASCIIz string from FLASH (or PROGRAM) memory | PrintPGMASCIIz |
74 | PrintSRAMASCIIz | Print an ASCIIz string from SRAM | PrintRAMASCIIz |
75 | PrintEEPROMString | Print a specific number of BYTEs from an EEPROM location | |
76 | PrintFLASHString | Print a specific number of BYTEs from FLASH memory | PrintPGMString |
77 | PrintSRAMString | Print a specific number of BYTEs from an SRAM location | PrintRAMString |
78 | Print0xPrefix | Print the string "0x" | |
79 | PrintBINARY | Print a 9-BYTE string representing a BYTE in BINARY format | |
80 | PrintCHAR | Print a single ASCII character | |
81 | PrintBYTE | Print a BYTE value in decimal format | |
82 | PrintHexBYTE | Print a 2-BYTE string then a BYTE value in hexadecimal format | |
83 | Print0xHexBYTE | Call Print0xPrefix() then PrintHexBYTE (BYTE) | |
84 | PrintWORD | Print a WORD value in decimal format | |
85 | PrintHexWORD | Print a 4-BYTE string then a WORD value in hexadecimal format | |
86 | Print0xHexWORD | Call Print0xPrefix() then PrintHexWORD (WORD) | |
87 | PrintScaledULONG | Print an UNSIGNED LONG value as a REAL, scaled by a post-decimal digit count | |
88 | PrintNewLine | Print the ASCII Carriage Return and Line Feed characters | |
89 | PrintSpace | Print a single ASCII space character | |
90 | PrintSpaces | Print a series of ASCII space characters | |
91 | Print1SpaceAnd1CHAR | Print one ASCII space character then one other ASCII character | |
92 | Print1CHARAnd1Space | Print one ASCII character then an ASCII space | |
93 | PrintDescAndHexBYTE | Print an ASCIIz string then the numeric value in a BYTE | |
94 | PrintDescAndHexWORD | Print an ASCIIz string then the numeric value in a WORD | |
95 | PrintEqualDecWORD | Print an ASCII equal sign, a space then call FormatWORD (WORD) | |
96 | PrintAllAINs | Print the hexadecimal value of all eleven Analog Inputs | |
97 | PrintMainProgramHz | Print the number of times the main loop executed during the prior second | |
98 | PrintScriptProgramHz | Print the number of script instructions executed during the prior second | PrintUserProgramHz |
99 | ASCIIToHex | Parse two ASCII BYTEs into the hexadecimal value which they represent | |
100 | HexToASCII | Convert the low order nybble of R24 to the ASCII character representing it | |
101 | UpperCase | Convert any BYTE from ASCII 'a' to 'z' to its equivalent 'A' to 'Z' value | |
102 | NegateLONG | Form the negative value of a LONG (4-BYTE) variable | |
103 | Divide2LONGs | Divide one SIGNED LONG INTEGER (32-bit) value with another one | |
104 | Divide2ULONGs | Divide one UNSIGNED LONG (32-bit positive) value with another one | |
105 | Multiply2ULONGs | Multiply one UNSIGNED LONG value with another one | |
106 | MultiplyULONGbyBYTE | Multiply an UNSIGNED LONG with a BYTE (unsigned 8-bit) value | |
107 | ParseValue | Convert a string to its signed or unsigned LONG INTEGER numeric equivalent | |
108 | ParseValueAfter | Search for a specific character and if found, call ParseValue (SZPtr+1) | |
109 | PseudoRandom | Generate a pseudo-random UNSIGNED LONG value | |
110 | RandomBYTE | Generate a scaled pseudo-random BYTE value | |
111 | RandomULONG | Generate a scaled pseudo-random UNSIGNED LONG value | |
112 | DiscreteInput | Read the value of a specific Discrete Input or Output | |
113 | DiscreteOutput | Turn one of the Discrete Outputs either ON or OFF | |
114 | ProcessCommand | Request that the system command processor execute a text command | |
115 | nRF_CommandHandler | Handle all nRF24L01+ commands (those that begin with 'N') from MCU logic | |
116 | nRF_Initialize | Power the nRF24L01+ down, load all default values, and it power back up | |
117 | nRF_Read1Register | Read one of the nRF24L01+ (1-BYTE or 5-BYTE) Registers | |
118 | nRF_SPI_Command | Send a command BYTE and up to 32 data BYTEs to the nRF24L01+ | |
119 | nRF_Write1Register | Write one of the nRF24L01+ (1-BYTE or 5-BYTE) Registers | |
120 | WS2812_Command | Output a series of BYTEs to a port or ports using the WS2812 protocol | |
121 | WS2812_Cmd_PortSet | Same as WS2812_Command above, but skips over the Port checking and setting logic | |
122 | DumpMemory | Format and print a range of SRAM, EEPROM or FLASH | |
123 | DumpRegisters | Conditionally format and print all MCU Register values | |
124 | DumpRegs_Begin | Unconditionally format and print the MCU Registers | |
125 | DumpStack | Format and print the contents of the MCU stack | |
126 | Spare (presently just a "RET" in the 328P) | ||
" | PrintFLASH_Seg0 | Print a string located in the first 64K of FLASH (1284P and 2560) | |
127 | Spare (presently just a "RET" in the 328P) | ||
" | PrintFLASH_Seg1 | Print a string located in the second 64K of FLASH (1284P only) | |
" | PrintFLASH_Seg3 | Print a string located in the third 64K of FLASH (2560 only) |
There is a second jump table in MIRTOS for both the 1284P and 2560 MCUs that is not available in the 328P.
There are a number of constant definition files to include at the beginning of your assembly language source code files. If not specified by numeric MCU in the name, each file below is for all three (3) supported MCUs. Consider including this block (some depend on the .INCLUDE order):
; All INCLUDE files should be in the same directory as this .ASM source code (or) ; use the "-I" parameter to set the AVR-AS "Include" directory where they are located: .INCLUDE "CommonDefs.Def" ; TRUE, FALSE, YES, NO, BIT_PV_*, et.al. .INCLUDE "BinDefs.Def" ; The B0 to B11111111 definitions .INCLUDE "Indices.Def" ; All 128 indirectly-accessed system jump indices .IF PROCESSOR == 328 .INCLUDE "IOM328P.Def" ; Register definitions for the ATmega328P .INCLUDE "Addresses328.Def" ; All 128 328P system routines' Jump Table addresses .INCLUDE "SystemSRAM328.Def" ; Names and addresses of 328P system SRAM variables .ENDIF .IF PROCESSOR == 1284 .INCLUDE "IOM1284P.Def" ; Register definitions for the ATmega1284P .INCLUDE "Addresses1284.Def" ; All 128 1284P system routines' Jump Table addresses .INCLUDE "SystemSRAM1284.Def" ; Names and addresses of 1284P system SRAM variables .ENDIF .IF PROCESSOR == 2560 .INCLUDE "IOM2560.Def" ; Register definitions for the ATmega2560 .INCLUDE "Addresses2560.Def" ; All 128 2560 system routines' Jump Table addresses .INCLUDE "SystemSRAM2560.Def" ; Names and addresses of 2560 system SRAM variables .ENDIF .IFNDEF APushAllRegisters .ERROR "Exiting: definition files not pulled in." .ENDIF .INCLUDE "SystemEEPROM.Def" ; Names and addresses of system EEPROM variables .INCLUDE "SystemFLASH.Def" ; Names and addresses of system FLASH constants .INCLUDE "nRF24L01.Def" ; Register and variable names for nRF radio useSince Addresses###.Def and Indices.Def contain similar information, selecting which to include depends on how you want to access the Operating System routines. For example, these three blocks of code do the very same thing:
The code above would require 14 BYTEs of FLASH memory. It can be recoded to require fewer BYTEs, expecially if more system calls are required by the code within the section contracted to "..." below. This first block of code below would require 12 BYTEs of FLASH memory to implement:SetSameColor: .INCLUDE "Indices.Def" LDI R30, I_PushAll ; Load the index of the PushAll routine JMP ASoftVector ; Call into the System Call table RCALL ASharedSetup ; Call the local shared setup routine ... LDI R30, I_PopAll ; Load the PopAll index JMP ASoftVector ; Exit, restoring all registers used
When using Addresses###.Def, there is no need to load R30, but each system function is called using an ABSOLUTE address (i.e., a LONG or FAR CALL), and this code would require 10 BYTEs of FLASH:SetSameColor: .INCLUDE "Indices.Def" LDI R30, I_PushAll ; Load the index of the PushAll routine RCALL ALocal_SoftVector ; Call into the System Call table RCALL ASharedSetup ; Call the local shared setup routine ... LDI R30, I_PopAll ; Load the PopAll index to restore and exit Local_SoftVector: JMP ASoftVector
Of course, it wouldn't hurt anything to include both an Addresses###.Def and the Indices.Def files, since they don't conflict with one another. Either system routine access method may then be used as desired.SetSameColor: .INCLUDE "Addresses328.Def" CALL APushAll ; Call the PushAll routine RCALL ASharedSetup ; Call the local shared setup routine ... JMP APopAll