We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
I have the impression that an assembler non-interrupt subroutine called from 'C' need save no registers. Is that correct? Erik
It depends on your skill. If you don't alter registers in your subroutine, why to save them? I'll say even more, I never save registers even for interrupts, neither for C functions, but it is only possible if you have reached a certain level of qualification. Have fun. M.
If you do not use the registers you do not need to save them, that is evident even to the rankest amateur. I would have no reason to ask this question if I did not use the registers in routines. I have, however, noted that C functions do not seem to save the regs and thus ask the question. REPHRASE: The 'c' functions do not - as far as I have seen - save registers. Are there any cases - outside interrupts - where not saving (these specific) the registers will topple the code generated by the 'C' compiler. Erik
Excuse me, your rephrase seems even less clear than the original question. You say: "Are there any cases ... where not saving will topple the code." Should that be understood as "Can an example be constucted..."? If so, the answer is evidently YES. Everything is possible in an assembler routine. If you speak of some practical cases - all depends on your style and skill. M.
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
Yes. That's Correct. Saving registers in an assembler function (the way that I think you are thinking) is unnecessary and gains nothing. Note that you may want to use REGUSE in the assembler module to help the Global Register Coloring optimization (if you use it). But, this is not required. Jon
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.