I am going to show you some code generated by the ARM compiler. Why not code for the 8x300 you ask? I don't have an assembler for the 8x300, never mind a compiler! Why not code for the Intel x86 we all have? The x86 is butt ugly and a total horror show, so we will go with the ARM.
Let's say we want to know how the ARM adds two numbers. We write the following C code as "sample.c"
int a = 2; int b = 3; int c = 99; void sample ( void ) { c = a + b; }The trick is the "-S" switch to the compiler, telling it to generate assembly code for us to look at. We use the following command:
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -o sample.s -S sample.cThere is a fair bit of "extra baggage" in the generated code, so I have extracted the interesting part, which is as follows:
sample: ldr r3, .L2 ldr r2, [r3] ldr r3, .L2+4 ldr r3, [r3] add r3, r3, r2 ldr r2, .L2+8 str r3, [r2]First, I'll summarize what goes on, then explain in detail. The compiler generates code to load "a" into register r2, then "b" in register r3. Then it adds the two, putting the result into r3, then it stores r3 into "c".
The compiler has placed a,b,c in that order in memory with the symbol ".L2" being the address of "a". These are all 4 byte objects, so "a" is at .L2, "b" is at .L2+4 and "c" is at .L2+8.
The code first loads the address of the variable into a register, then performs a load or store from/to the address in that register. So two instructions are required to do a load or a store. The first loads the address, the second loads or stores the value.
Now look more closely at the first two instructions. The first loads a value ".L2" into register r3, that is simple enough. The second is more interesting. It says to load into register r2, but from [r3]. This notation with square brackets says not to load the value in register r3 itself (that would simply be ldr r2,r3), but to use r3 as an address to reference memory and to load from that address. So r3 is what the C programmer would call a pointer.
Note also that we can move small constants into registers directly via something like "ldr r3, 13". Write the C code c = a + 13 and you will see something like this get used.
An important topic to mention is the "direction of flow" for a given assembler syntax. The flow here is from right to left. When we write ldr r3, 13 we are loading the value 13 in register r3. Be warned that some assemblers flow from left to right and the same statement in that case would be "ldr 13, r3". You have been warned! Be sure to verify how the game is played with whatever assembler (or disassembler) you are dealing with.
All of this is just to give a "taste" of assembly language. Each processor is unique and different. The main point or "take-away" here is the use of the -S switch to the compiler to get it to show us the code it generates. Also note that adding the -O option to turn on optimization can produce quite different (and sometimes surprising) code. Non-optimized code is often more straightforward and easy to understand.
Tom's Computer Info / tom@mmto.org