Help for Lab 1 Subroutines calling Subroutines Topics
Help for Lab. 1 Subroutines calling Subroutines
Topics tackled today l Handling some “C++” keywords when programming assembly code ¡ Programming “subroutines called by subroutines” ¡ Programming “extern” variables ¡ Programming “volatile” variables l Understanding what I did when ¡ Programming “subroutines called by subroutines” ¡ Programming “extern” variables ¡ Programming “volatile” variables
Subroutine calling subroutine All void functions File “blood. Englishman. ASM. asm”. extern _Fee__Fv; . extern _Fi__Fv; . extern _Fo__Fv; . section program; . global _Fum__Fv; _Fum__Fv: LINK 16; CALL _Fee __Fv; CALL _Fi __Fv; CALL _Fo__Fv; P 0 = [FP +4]; UNLINK; _Fum__Fv. END: JUMP (P 0); Stores RETS Overwrites RETS P 0 = stored RTS Recover RTS Use P 0 as RTS WHY – Pipeline speed issues I think File “blood. Englishman. CPP. cpp” void Fee(void); void Fi(void); void Fo(void); void Fum(void) { Fee( ); Fi( ); Fo( ); }
What nursery rhyme? l Fe, l Fi, l Fo, l Fum, l I smell the blood of an Englishman?
Subroutine calling subroutine All integer functions File “blood. Englishman. ASM. asm”. extern _Fee__Fi; . extern _Fi__Fi; . extern _Fo__Fi; . section program; . global _Fum__Fi; _Fum__Fi: // value passed in R 0 LINK 16; Stores RETS R 0 += 6; // pass parameter in R 0 // R 0 is dead – value is no longer needed CALL _Fee __Fi; Overwrites RETS R 0 = 7; // pass parameter in R 0 CALL _Fi __Fi; R 0 = 8; // pass parameter in R 0 CALL _Fo__Fi; P 0 = [FP +4]; UNLINK; _Fum__Fi. END: JUMP (P 0); Recover RETS File “blood. Englishman. CPP. cpp” void Fee(int); void Fi(int); void Fo(int); void Fum(int value) { Fee(6 + value); Fi(7); Fo(8); }
Subroutine calling subroutine All integer functions -- WRONG APPROACH File “blood. Englishman. CPP. cpp” . extern _Fee__Fi; . extern _Fi__Fi; . extern _Fo__Fi; . section program; . global _Fum__Fi; _Fum__Fi: // value passed in R 0 LINK 16; Stores RETS R 0 = 6; // pass parameter in R 0 CALL _Fee __Fi; R 0 = 7; // pass parameter in R 0 CALL _Fi __Fi; // Problem – can’t do this R 0 destroyed R 0 += 8; // pass parameter in R 0 CALL _Fo__Fi; P 0 = [FP +4]; UNLINK; _Fum__Fi. END: JUMP (P 0); Recovers RETS void Fee(int); void Fi(int); void Fo(int); void Fum(int value) { Fee(6); Fi(7); Fo(8 + value); }
Subroutine calling integer subroutine Save R 0 to stack frame – Review of ENCM 368 MIPS ideas . section program; . global _Fum__Fi; _Fum__Fi: // value passed in R 0 LINK (16 + 4); [FP + 20] = R 0; SAVE INCOMING R 0 = 6; // pass parameter in R 0 CALL _Fee __Fi; DESTROYS R 0, R 1, R 2 R 0 = 7; // pass parameter in R 0 CALL _Fi __Fi; DESTROYS R 0, R 1, R 2 R 0 = [FP + 20]; // Recover R 0 += 8; // pass parameter in R 0 CALL _Fo__Fi; P 0 = [FP +4]; UNLINK; // This unlink discards 20 _Fum__Fi. END: JUMP (P 0); File “blood. Englishman. CPP. cpp” void Fee(int); void Fi(int); void Fo(int); void Fum(int value) { Fee(6); Fi(7); Fo(8 + value); }
Subroutine calling integer subroutine (passing parameters) The way I remember the easiest and have the least trouble with . section program; . global _Fum__Fi; _Fum__Fi: // value passed in R 0 [--SP] = R 7; // R 7 always saved by other routines – SAVE before LINK R 7 = R 0; // Save R 0 to R 7 LINK 16 R 0 = 6; // pass parameter in R 0 CALL _Fee __Fi; R 0 = 7; // pass parameter in R 0 CALL _Fi __Fi; R 0 = R 7 // Recover R 0 += 8; // pass parameter in R 0 CALL _Fo__Fi; P 0 = [FP +4]; UNLINK; R 7 = [SP++]; // Recover R 7 AFTER unlink _Fum__Fi. END: JUMP (P 0); File “blood. Englishman. CPP. cpp” void Fee(int); void Fi(int); void Fo(int); void Fum(int value) { Fee(6); Fi(7); Fo(8 + value); }
long int and extern long int arrays long int * and extern long int *. extern _thumb; in C++. extern _thumb. Pt; in C++ Means the following in assembly code. section L 1_data; . global _plumb; . var _plumb[5] = {1, 2, 3, 4, 5}; . global _pointers; . var _pointers[5]; . section program; File “Put. In. His. Thumb. cpp” extern long int thumb[10]; extern long int *thumb. Pt[10]; long int plumb[5] = {1, 2, 3, 4, 5}; long int *pointers[6]; long int Jack(long int *goodboy) long int Jack(long int *goodboy{ long int sum; goodboy[3] = thumb[2] + plumb[1] }
extern long int File “Put. In. His. Thumb. cpp” . extern _thumb; _ . section L 1_data; . global _plumb; . var _plumb[5] = {1, 2, 3, 4, 5}; . section program; . global _Jack__FPl; // Pointer long _Jack__FPl: // pointer passed in R 0 LINK 16; P 0 = R 0; // Use passed value as a pointer P 1. L = _thumb; P 1. H = _thumb; R 0 = [P 1 + (2 * 4)]; long int Jack(long int *goodboy{ long int sum; // LOAD P 1. L = _plumb; P 1. H = _plumb; R 1= [P 1 + (1 * 4)]; // LOAD R 0 = R 0 + R 1; [P 0 + (3 * 4)] = R 0; // STORE P 0 = [FP +4]; UNLINK; _ Jack__FPl. END: : JUMP (P 0); extern long int thumb[10]; long int plumb[5] = {1, 2, 3, 4, 5} long int Jack(long int *goodboy) goodboy[3] = thumb[2] + plumb[1] }
extern volatile long int File “Put. In. His. Thumb. cpp” . extern _thumb; . section L 1_data; . global _plumb[5] = {1, 2, 3, 4, 5}; . section program; . global _Jack__FPl; // Pointer long _Jack__FPl: // pointer passed in R 0 LINK 16; P 0 = R 0; // Use passed value as a pointer // R 0 is now dead – can reuse P 1. L = _thumb; P 1. H = _thumb; R 0 = [P 1 + (2 * 4)]; long int Jack(long int *goodboy{ long int sum; // LOAD P 1. L = _plumb; P 1. H = _plumb; R 1= [P 1 + (1 * 4)]; // LOAD R 0 = R 0 + R 1; [P 0 + (3 * 4)] = R 0; // STORE P 0 = [FP +4]; UNLINK; _Jack__FPl. END: JUMP (P 0); extern volatile long int thumb[10]; long int plumb[5] = {1, 2, 3, 4, 5} long int Jack(long int *goodboy) goodboy[3] = thumb[2] + plumb[1] }
extern long int – optimized code generated by C++ -- NOT CORRECT. extern _star; . extern _Blink. Light__Fv; . section program; . global _Wonderwhat__Fv; _Wonderwhat__Fv: LINK 16; P 1. L = _star; P 1. H = _star; R 0 = [P 1 + (2 * 4)]; // LOAD CC = R 0 == 2; IF !CC JUMP DONE; LOOP: CALL _Blink. Light__Fv; JUMP LOOP; DONE: P 0 = [FP +4]; UNLINK; _Wonderwhat__Fv. END: JUMP (P 0); File “Twinkle. cpp” extern long int star[10]; void Wonder. What(void); void Blink. Light(void); void Wonder. What(void) { while (star[2] == 2) { Blink. Light( ); } } Either star[2] == 2, or it does not If star[2] == 2 then get an infinite loop otherwise Blink. Light( ) never called
extern volatile long int optimized code – STILL WRONG. extern _star; // No volatile in ASM. extern _Blink. Light__Fv; . section program; . global _Wonderwhat__Fv; _Wonderwhat__Fv: LINK 16; P 1. L = _star; P 1. H = _star; // Re-use as pointer LOOP: R 0 = [P 1 + (2 * 4)]; // KEEP LOADING // THIS CODES THE VOLATILITY CC = R 0 == 2; // LOOP NEEDED IF !CC JUMP DONE; CALL _Blink. Light__Fv; DESTROYS P 1 JUMP LOOP; DONE: P 0 = [FP +4]; UNLINK; _Wonderwhat__Fv. END: JUMP (P 0); File “Twinkle. cpp” extern volatile long int star[10]; void Wonder. What(void); void Blink. Light(void); void Wonder. What(void) { while (star[2] == 2) { Blink. Light( ); } } star[2] == 2 MAY START OFF BEING 2 But since star is “volatile” then some external action may change it. Loop needed in optimized code if “volatile” memory value GOOD IDEA – WILL NOT WORK AS P 1 is destroyed during CALL Blink. Light
extern volatile long int optimized code -- CORRECT. extern _star; . extern _Blink. Light__Fv; . section program; . global _Wonderwhat__Fv; _Wonderwhat__Fv: [--SP] = P 5; // Save the non-volatile LINK 16; P 5. L = _star; P 5. H = _star; LOOP: R 0 = [P 5 + (2 * 4)]; // KEEP LOADING // THIS CODES THE VOLATILITY CC = R 0 == 2; // LOOP NEEDED IF !CC JUMP DONE; CALL _Blink. Light__Fv; JUMP LOOP; DONE: P 0 = [FP +4]; UNLINK; P 5 = [SP++]; _Wonderwhat__Fv. END: KEEPS P 5 UNCHANGED File “Twinkle. cpp” extern volatile long int star[10]; void Wonder. What(void); void Blink. Light(void); void Wonder. What(void) { while (star[2] == 2) { Blink. Light( ); } } star[2] == 2 MAY START OFF BEING 2 But since star is “volatile” then some external action may change it. Loop needed in optimized code if “volatile” memory value
Understanding what I did l Will look at things in more detail later in class l But here are some ideas of why we did what we did
LINK – what does it do? A correction – Processor Programming Reference 4: 17 LINK 16 LINK (200 + 16) Does all the following in one instruction [--SP] = RETS; [--SP] = FP; FP = SP; SP – 16; [--SP] = RETS; [--SP] = FP; FP = SP; SP – 200; (Space for an array) SP – 16;
UNLINK – what does it do? LINK 16 LINK (200 + 16) UNLINK Unlink does all the following in one instruction SP = FP; FP = [SP++]; RETS = [SP++];
Subroutine calling subroutine All void functions File “blood. Englishman. ASM. asm”. extern _Fee__Fv; defined elsewhere. extern _Fi__Fv; . extern _Fo__Fv; . section program; . global _Fum__Fv; _Fum__Fv: Since the other functions are coded “outside” or “external” to this file then we indicate that with the keyword. extern File “blood. Englishman. CPP. cpp” void Fee(void); void Fi(void); void Fo(void); void Fum(void) { }
Subroutine calling subroutine All void functions File “blood. Englishman. ASM. asm”. extern _Fee__Fv; . extern _Fi__Fv; . extern _Fo__Fv; . section program; . global _Fum__Fv; // Not “private” _Fum__Fv: Since we want other functions (coded “outside” or “external” to this file) to be able to use Fum( ) coded in this file we must “globalize” (tell everybody) this functions name with the keyword. global File “blood. Englishman. CPP. cpp” void Fee(void); void Fi(void); void Fo(void); void Fum(void) { }
Subroutine calling subroutine All void functions File “blood. Englishman. ASM. asm”. extern _Fee__Fv; . extern _Fi__Fv; . extern _Fo__Fv; . section program; . global _Fum__Fv; _Fum__Fv: LINK 16; // Save RETS to stack CALL _Fee __Fv; // Changes RETS CALL _Fi __Fv; // Changes RETS CALL _Fo__Fv; // Changes RETS P 0 = [FP +4]; // Saved RETS into P 0 UNLINK; // Saved RETS into RETS // and destroy (remove) stack frame _Fum__Fv. END: JUMP (P 0); // Now equivalent to RTS Why this approach? Possibly more efficient because of the pipeline The LINK 16; operation saves this subroutine’s return address (stored in the “RETurn from Subroutine register” RETS) onto the stack. This allows the processor to use register RETS when it calls other subroutines – same was as the MIPS – different register name UNLINK restores (recovers, reads) RETS from the stack so we can exit this subroutine.
Subroutine calling subroutine All integer functions l Can’t overload on the Name mangle changes not need to remember basis of return value – (Dodetails in quizzes and exams – critical to get C++ rule correct in labs) l This is the “assembly code reason why void Foo(void) _Foo__Fv int Foo(int) _Foo__Fi int Foo(long int) _Foo__Fl int Foo(int *) _Foo__FPi Int Foo(long int *) _Foo_FPl
Subroutine calling subroutine All integer functions. extern _Fee__Fi; . extern _Fi__Fi; . extern _Fo__Fi; . section program; . global _Fum__Fi; _Fum__Fi: // value passed in R 0 LINK 16; // R 0 incoming parameter is unsaved R 0 = 6; // pass parameter in R 0 CALL _Fee __Fi; Destroys R 0, R 1, P 0, P 1 R 0 = 7; // pass parameter in R 0 CALL _Fi __Fi; Destroys R 0, R 1, P 0, P 1 R 0 += 8; // pass parameter in R 0 – WRONG VALUE AS TRUE VALUE HAS BEEN DESTROYED CALL _Fo__Fi; P 0 = [FP +4]; UNLINK; _Fum__Fi. END: JUMP (P 0); void Fum(int value) { Fee(6); Fi(7); Fo(8 + value); } ALWAYS pass the parameter to the subroutine in R 0 – very similar to MIPS Must save the passed value (in R 0), otherwise it will be destroyed when we call the other subroutines while we use R 0 to pass the parameter
Subroutine calling subroutine All integer functions. section program; . global _Fum__Fi; _Fum__Fi: // value passed in R 0 [--SP] = R 7; // Save volatile register R 7 = R 0; // Save passed value LINK 16; R 0 = 6; // pass parameter in R 0 CALL _Fee __Fi; R 0 = 7; // pass parameter in R 0 CALL _Fi __Fi; R 0 = R 7; // Recover passed value R 0 += 8; // pass parameter in R 0 CALL _Fo__Fi; P 0 = [FP +4]; UNLINK; R 7 = [SP++]; // Recover saved volatile register _Fum__Fi. END: JUMP (P 0); void Fum(int value) { Fee(6); Fi(7); Fo(8 + value); } I always save a non-volatile register (R 7) onto the stack -- and then save R 0 into R 7. Easier to remember and optimize ALWAYS pass the parameter to the subroutine in R 0 –
This file is in a project by itself. It compiles but does not link or run! Why not? A project containing only the file “Put. In. His. Thumb. cpp” will not link and run because Very common error l All projects must have a main( ) in them l If you receive a message – can’t link to _main – when you are doing the laboratory – then this is the problem. File “Put. In. His. Thumb. cpp” extern long int thumb[10]; long int plumb[5] = {1, 2, 3, 4, 5} long int Jack(long int *goodboy) long int Jack(long int *goodboy{ long int sum; goodboy[3] = thumb[2] + plumb[1] }
These two files compile but does not link and run-- Why not? File “main. cpp” File “Put. In. His. Thumb. cpp” extern long int thumb[10]; extern long int plumb[5] ; long int goodboy[10]; extern long int thumb[10]; long int plumb[5] = {1, 2, 3, 4, 5} long int Jack(long int *goodboy) long int Jack(long int *goodboy{ long int sum; int main(void) { for (int count = 0; count < 10; count++) goodboy[count] = 0; Jack(goodboy); printf(“%dn”, goodboy[3]); } goodboy[3] = thumb[2] + plumb[1] }
This does not link and run Why not? File “Put. In. His. Thumb. cpp” DECLARED IN ANOTHER FILE DECLARED IN THIS FILE extern long int thumb[10]; long int plumb[5] = {1, 2, 3, 4, 5} IN NO FILE HAS MEMORY SPACE BEEN SET ASIDE FOR THE “THUMB” ARRAY File “main. cpp” extern long int thumb[10]; extern long int plumb[5] ; long int goodboy[10]; DECLARED IN ANOTHER FILE DECLARED IN THIS FILE
This does link and run -- because all arrays have been given “space” File “main. cpp” File “Put. In. His. Thumb. cpp” long int thumb[10] = {2, 4, 6, 8, 10}; extern long int plumb[5] ; long int goodboy[10]; extern long int thumb[10]; long int plumb[5] = {1, 2, 3, 4, 5} long int Jack(long int *goodboy) long int Jack(long int *goodboy{ long int sum; int main(void) { for (int count = 0; count < 10; count++) goodboy[count] = 0; Jack(goodboy); printf(“%dn”, goodboy[3]); } goodboy[3] = thumb[2] + plumb[1] }
How can something in memory change like this. extern _star; . extern _Blink. Light__Fv; . section program; . global _Wonderwhat__Fv; _Wonderwhat__Fv: LINK 16; P 0 = R 0; // Use passed value as a pointer P 1. L = _thumb; P 1. H = _thumb; LOOP: R 0 = [P 1 + (2 * 4)]; // KEEP LOADING CC = R 0 == 2; // LOOP NEEDED IF !CC JUMP DONE; CALL _Blink. Light__Fv; JUMP LOOP; DONE: P 0 = [FP +4]; UNLINK; _Wonderwhat__Fv. END: File “Twinkle. cpp” extern volatile long int star[10]; void Wonder. What(void); void Blink. Light(void); void Wonder. What(void) { while (star[2] == 2) { Blink. Light( ); } } star[2] == 2 MAY START OFF BEING 2 But since star is “volatile” then some external action may change it. Loop with repeated read needed in optimized code if “volatile” memory value
Example from Lab. 1 Audio talkthrough l Difference between subroutines and interrupts l There is a main routine l There is an “interrupt audio” routine. Every 1 / 40000 s the interrupt routine “interrupts” (temporarily halts) main( ), then runs itself, and then returns control to main l Interrupts run under “external control” when “something happens” ¡ switch changes or voltage changes -- unexpected l Subroutines run under “internal control” ¡ ONLY WHEN CALLED – NEVER UNEXPECTED
Example from Lab. 1 Task 4 File “main. cpp” File “interruptservice. cpp” volatile boolean mute_on = FALSE; extern volatile boolean mute_on; void Process_Data. ASM(void); long int Read. Switches(void); int main( ) Initialize. Switches( ); Initialize. Audio( ); Start. Interrupts( ); while (1) { int value = Read. Switches( ); // If switch pressed // – turn off sound; if (value == 0 x 100) mute_on = TRUE; else mute_on = FALSE; } } EX_INTERRUPT_HANDLER(Sport 0_RX_ISR) { ……. . /// Lots of good stuff Process_Data. ASM( ); // Make the sound occur ……. . // Lots of more good stuff; } void Process_Data. ASM(void) { if (mute_on = = FALSE) Make. The. Sound( ); } WORRY ABOUT WHAT EX_INTERRUPT_HANDLER( ) MEANS IN LAB. 2
- Slides: 30