I have the impression that an assembler non-interrupt subroutine called from 'C' need save no registers. Is that correct? Erik
Correct. As stated in the manual, "Assembler functions can change all register contents in the current selected register bank as well as the contents of the registers ACC, B, DPTR, and PSW."
This is why having lots of "leaf" functions can greatly affect the optimiser - it has to assume that it needs to save & restore registers around a function call. However, the "Global Register Optimisation" can really help here.
When C makes a call to a function written in assembler, the C optimiser generally has to assume the worst case - that any of the registers may be overwritten by the assembly language function. However, using the REGUSE control in the assembly language function will inform the optimiser about exactly which registers are changed by the function - this allows global register optimisation to be applied. In the REGUSE control, you only have to specify those registers that are actually modified by the function. In order to optimise performance, you may choose to save and restore some (or all) of the registers that you use. If you are keen to optimise performance, you will need to do some investigation. In general, it is the registers R0 to R7 that are critical. The optimiser may alocate these registers to automatic variables in the calling C function - that are the most important. This optimisation can be very significant to performance so it may be important to ensure that the optimiser can apply it effectivly.
Thanks Dan, Andy & Graham. Michael, I was not asking about assembler code, but about the 'C' compiler. y'all have fun now, ya hear Erik
The point is the REGUSE control in the assembly code makes a big difference in the C code that calls the assembly code!! In the following example the calling C code was reduced by almost 1/2 in size by using REGUSE on the assembly side.
Main.c void WithoutRegUse( char C ); void WithRegUse( char C ); void CallWithoutRegUse( void ) { char i; char xdata* p = 0; for( i = 0; i != 0x12; ++i, ++p ) WithoutRegUse(*p); } void CallWithRegUse( void ) { char i; char xdata* p = 0; for( i = 0; i != 0x12; ++i, ++p ) WithRegUse(*p); } void main( void ) { CallWithoutRegUse(); CallWithRegUse(); }
Sub.c #include <reg52.h> volatile V; void WithoutRegUse( char C ) { ACC = C; #pragma asm swap a mov V,a #pragma endasm return; } #pragma asm $reguse _WithRegUse( A, R7 ) #pragma endasm void WithRegUse( char C ) { ACC = C; #pragma asm swap a mov V,a #pragma endasm return; }
Partial Main.lst ; FUNCTION CallWithoutRegUse (BEGIN) ; SOURCE LINE # 7 ; SOURCE LINE # 8 ; SOURCE LINE # 10 0000 E4 CLR A 0001 F500 R MOV p,A 0003 F500 R MOV p+01H,A ; SOURCE LINE # 11 0005 F500 R MOV i,A 0007 ?C0001: 0007 E500 R MOV A,i 0009 6412 XRL A,#012H 000B 6017 JZ ?C0004 ; SOURCE LINE # 12 000D 850082 R MOV DPL,p+01H 0010 850083 R MOV DPH,p 0013 E0 MOVX A,@DPTR 0014 FF MOV R7,A 0015 120000 E LCALL _WithoutRegUse 0018 0500 R INC i 001A 0500 R INC p+01H 001C E500 R MOV A,p+01H 001E 70E7 JNZ ?C0001 0020 0500 R INC p 0022 ?C0010: 0022 80E3 SJMP ?C0001 ; SOURCE LINE # 13 0024 ?C0004: 0024 22 RET ; FUNCTION CallWithoutRegUse (END) ; FUNCTION CallWithRegUse (BEGIN) ; SOURCE LINE # 15 ; SOURCE LINE # 16 ; SOURCE LINE # 18 ;---- Variable 'p' assigned to Register 'DPTR' ---- 0000 900000 MOV DPTR,#00H ; SOURCE LINE # 19 ;---- Variable 'i' assigned to Register 'R6' ---- 0003 E4 CLR A 0004 FE MOV R6,A 0005 ?C0005: 0005 EE MOV A,R6 0006 6412 XRL A,#012H 0008 6009 JZ ?C0008 ; SOURCE LINE # 20 000A E0 MOVX A,@DPTR 000B FF MOV R7,A 000C 120000 E LCALL _WithRegUse 000F 0E INC R6 0010 A3 INC DPTR 0011 80F2 SJMP ?C0005 ; SOURCE LINE # 21 0013 ?C0008: 0013 22 RET ; FUNCTION CallWithRegUse (END)
Too right. Thanks for the nice examples.