Macros with Pic Microcontroller in Assembly Language
I am studying Assembly Language Programming with Pic Microcontrollers. I am programming with MPLAB X IDE and having trouble understanding the concept of programming a macro program to work in conjunction with my main program. I am able set up and save a macro with macro name and .inc extension to be linked to my main program. I don't understand what programming information needs to be in my macro for accessing and programming my complete program to be successful. I have searched the internet for information relating to my frustration and I cannot find anything suitable. Does anyone know of any macro development documentation available, even a basic understanding would by helpfull. The tutarials I have, state macros are easy, they probably are for people that understand the concept. I wish I did. Can someone please help.
CommentsAdd a comment
One thing to keep in mind is that, as macros are defining shorthand to the assembler, you have to tell the assembler to read those shorthand definitions before you can use (invoke) the macro. You do that by either putting the macro definitions directly in your assembler source file (near the top, before you attempt to use the macros you are defining), or by using a #include directive to tell the assembler the name of an assembler source file containing the macro definitions to be read in at the point where it encountered the #include directive. The #include needs to be above the point in your file where you attempt to use the macros defined in the included file.
I don't know what resources you have read already to learn about macros for Microchip Assembler. Chapter 7 in Microchip's manual for the Assembler is devoted to Macros. The manual should be on your system if you installed the assembler and MPLAB X.The exact location of the manual will depend on which operating system and version you are using, but you can search for it by name: MPASM_MPLINK_User_Guide.pdf.
The first thing to understand about macros is the difference between a macro and a subroutine.
Both are used when you have something that needs to be done multiple places within a program.
For example, you might have multiple places where you need to double a number and then add six to it.
A subroutine is a list of instructions for the processor to carry out. There is only one copy of the list. When you call the subroutine, a special instruction in your main program tells the processor to save a bookmark as to where it is in its current list of instructions, then go start processing the subroutine's list of instructions. The final instruction of the subroutine is a special instruction to return control back to that bookmark the processor saved when you called the subroutine.
Suppose our double-and-add-six calculation was needed seven places in the program, and that doubling a number and adding six to it took two machine instructions to do. With a subroutine, we would have three instructions set aside in the program memory for the subroutine - two instructions to do the math, and a third instruction to tell the processor to return to where it was working when it called the subroutine.
Without using a subroutine, we would need the two instructions to double and add six at each of the seven places in our program where needed that math done, for a total of 2 x 7 = 14 instruction words in the program memory.
Using a subroutine instead, you would have one instruction to make the subroutine call at each of the seven places you needed the math done. So the memory used would be (one instruction x seven places)routine is called) + (three instructions for the one copy of the subroutine), or a total of (7+3) = ten instruction words in memory, a net savings of 4 instruction words in memory. For more complex or more frequently used subroutines, the program memory savings can be even bigger.
Another advantage to a subroutine is clarity in reading the program, if you choose a good (descriptive) name for the subroutine, and document the subroutine's code well. And if you make a mistake in the code for the subroutine, you can fix it in one place (the subroutine). If you didn't use a subroutine, but just inserted the instructions for doubling and adding six at each of the seven locations in the program where you needed to do that calculation, you would have to make the changes seven places if you goofed when coding how to double and add six.
The disadvantage of the subroutine is slower speed. Every time you use the subroutine in your main program, the processor has to execute the call instruction (in the main program) and a return instruction (in the subroutine) in addition to the instructions necessary to do the double and add six. In simple subroutines like this, the extra time for the call and return instructions can make the subroutine call take twice as long (when the program is running) as just putting the double and add instructions each of the seven places that functionality
Now lets look at macros. A macro is like an abbreviation or shorthand you are teaching the assembler. When you define the macro, you are telling the assembler that whenever it sees the macro name in a place where it is expecting the name of a processor instruction, the assembler should look over to the macro definition and put the things it finds there into the program at that point, then continue processing the assembler source.
We could define a DoubleThenAddSix macro to accomplish the same math that the subroutine mentioned above did, using the same two instructions that the subroutine did. Wherever we invoked that macro (used its name in place of an instruction), the assembler would insert those two instructions into the program. The resulting program would be the same as if we typed those two instructions in the program at each of the seven places we used the macro. The program would be the same size typing the instructions directly (2 instructions times 7 instances of using the macro).
So what are the advantages of the macro?
It saves typing - a single line instead of two lines at each place where it is used.
If you choose a good name for the macro, it can make the program more clear, describing what is being done in higher-level terms instead of processor-specific instructions. For example, the macro might use a shift instruction to do the doubling. Someone less familiar with binary arithmetic might have to think about the purpose of the shift instruction was in this case. But reading the name of the macro (DoubleThenAddSix ) instead of the two instructions, the purpose is clear.
If you needed to double and add six three times in a row, it is easier to count the three lines using the macro than to count the six lines of raw instructions.
Like the subroutine, if you realize later that you got the instructions for doubling then adding six wrong, you can fix it one place (in the macro), and all seven places the macro is used will be fixed.
The other thing is that macros can be more than just a direct abbreviation for an instruction sequence.
Macros can also contain logic for the assembler to follow in generating instructions. As an example, the macro can tell the assembler to see if the chip it is building the program for has a multiply instruction. If it has a multiply instruction, the macro can generate code that uses it. If it doesn't, the macro can generate code using a longer sequence of instructions to do the equivalent multiply. So the macro can hide the details of how the multiply is done, and let someone reading the program concentrate on the higher-level concept that you are multiplying. Likewise, some processors have instructions that can shift or rotate my multiple bit positions, others only have instructions to shift or rotate one position at a time. A macro can generate different code depending on which processor the program is being generated for, using the multi-position shift or rotate instruction if available, or generating as many single-position shift or rotate instructions as needed to do the same thing.
Macros can also do math on constants that are arguments to the macro. There is an example in the Microchip manual where they use this to split a 32 bit value into four equivalent bytes.
One thing to keep in mind is that macros are instruction to the assembler as to what code to generate, and are processed by the assembler when creating the program. When running the program, the processor executes the instructions that were placed in the program by the macro.
By contrast, subroutines contain direct instructions for the processor, not the assembler.
I hope this clarifies the macros for you a little bit. Macros can seem complicated at first, because they are instructions to the assembler about generating instructions for the processor. But they can be very powerful, and hide details or differences between processors and let you concentrate on the higher-level functionality of a program.