In the code below I'm setting the first, then the second, then the third and finally the fourth bit of TRIS register. why is the generated assembly code that... strange?
Good to know: The data memory of the microcontroller that code is compiled for is devided into several banks. Bit 6 and 7 go into position 5 and 6 of status
register, only bits 0-5 are stored in the opcode. When accessing TRIS
register at location 0x86, the value getting stored in the opcode is therefore 6
.
(134)^080
: This is translating to 6
, which is the adress of TRIS
on that specific bank. Why doesn't the compiler just write (6)
or (134)
? Is it just to make it clear that TRIS is not on bank 0, or are there any other reasons?+(0/8)
: 0
stands for the bit position, 8
for the total number of bits in that register. In my oppinion, this expression doesn't serve any purpose as the result is always a fraction, never an integer. Hence it doesn't contribute to the adress calculation.(0)&7
: (0)
stands for the bit position again, but which purpose a binary-AND with 7
serves is untirely unclear to me.line
command do?;main.c: 9: TRISB |= 1;
bsf status, 5 ;RP0=1, select bank1
bcf status, 6 ;RP1=0, select bank1
bsf (134)^080h+(0/8),(0)&7 ;volatile
line 10
;main.c: 10: TRISB |= 2;
bsf (134)^080h+(1/8),(1)&7 ;volatile
line 11
;main.c: 11: TRISB |= 4;
bsf (134)^080h+(2/8),(2)&7 ;volatile
line 12
;main.c: 12: TRISB |= 8;
bsf (134)^080h+(3/8),(3)&7 ;volatile
line 13
Not being familiar with XC8 compiler or the PIC architecture I can't really say for sure why the compiler is emitting (134)^080
but it should make reading the assembly easier as it shows the intended address. If it just put 6
there you'd have to go back through the assembly and look for where status
had last been modified in order to figure out what the actual address is.
As for your other questions, those are easier to answer. The reason for the constant divide and masking of the bit position is so that the same instruction template can be used to set bits in variables that are larger than a single byte. Consider if you were writing a C function that prints an assembly instruction that sets a bit in a memory location. One way you could write it is something like this:
void emit_set_bit_insn(char const *addr, int bit) {
printf("bsf %s+(%d/8),(%d)&7\n", addr, bit, bit);
}
Now consider what emit_set_bit_insn("var16", 10)
will output:
bsf var16+(10/8),(10)&7
which the assembler will evaluate as:
bsf var16+1,2
Which sets bit 2 in the second byte of var16
, which is the same thing as setting bit 10 of the 16-bit variable var16
. (Well... assuming little-endian byte order, which is apparently the convention that the XC8 compiler follows.)
Sure the compiler could do all the arithmetic itself, but not doing it makes the compiler's code that much simpler. The assembler will generate the same machine code either way.
As kirill suggested the line
directive isn't an assembly instruction. It's used to inform the assembler which line in the C source code to associate with the following instructions. The assembler will be use this information to generate debugging information that the debugger can use to display relevant source code for a given address in the program.