# RTS10 - Assembly Assignment Finn van Reenen ## opdrachten Ik moest de volgende assignment maken: - Assignment 1: Testing a simple LEGv7 Pinky program - Assignment 2: Writeting a simple multiplication program - Assignment 5: A smarter tail recursive multiply algorithm - Assignment 7: A smarter power algorithm ## Testing a simple LEGv7 Pinky program Er is een project gegeven die toegevoerd en getest moet worden. De eerste keer dat ik door de stappen heen ging, kreeg een error dat de openOCD.exe fout ging. Dit is logisch voor mij, omdat ik Linux gebruikt als OS en .exe alleen voor Windows zijn. ![[assignment1/first_error.png]] Het opnieuw selecteren van de OpenOCD debug probe in het 'Debug Configurations' venster werd de Linux variant correct ingesteld en werkte het. Voor de zekerheid heb ik het nog een keer geprobeerd terwel ik alle wijzigingen in het project bij hou ik git^[voor de geïnteresseerde heb ik de git repo op mijn server gezet waar de commit van de clock wijzigingen te vinden is met de volgende link (https://gitea.finnvanreenen.nl/FReenen/RTS10_assignment_project_files/commit/4dcb59bc058b3a710a1ca471e4a5d007f64f344c)[https://gitea.finnvanreenen.nl/FReenen/RTS10_assignment_project_files/commit/4dcb59bc058b3a710a1ca471e4a5d007f64f344c]]. Hier zag ik dat er wat clock instellingen werden gewijzigd. Dus ik heb het opnieuw geprobeerd, maar dan alleen het veld 'OpenOCD Command' aangepast in de debugconfiguratie. Met deze methode werden de clock instellingen niet aangepast, alleen een aantal dingen dat ik herken als de verschillen tussen Windows en Linux. Hierna werken het nog steeds zoals verwacht. ![[assignment1/output.png]] ## Writeting a simple multiplication program Om te beginnen heb ik het project van de vorige assignment. Hier heb ik aan *main.c* een paar simpele regels toegevoegd om de basis werking te testen. ```c /* main.c simple program to test assembler program */ #include extern int test(int a, int b); extern unsigned int multiply(unsigned int a, unsigned int b); int main(void) { extern void initialise_monitor_handles(void); initialise_monitor_handles(); int a = test(3, 5); printf("Result of test(3, 5) = %d\n", a); unsigned int b = multiply(3, 5); printf("Result of multipy(3, 5) = %d\n", b); b = multiply(3, 0); printf("Result of multipy(3, 0) = %d\n", b); return 0; } ``` Pas toen ik het werkend had, kwam ik achter dat ik `a` en `b` heb omgedraaid. Dit maakt gelukkig niet uit in een vermenigvuldiging. Daarnaast heb ik ook `i` weg gewerkt door i.p.v. `i` te verhogen en daarna te vergelijken met `b` heb ik `b` verlaagd tot het 0 is. Dit is mijn implementatie: ```assambly .cpu cortex-m4 .thumb .syntax unified .globl multiply .text .thumb_func multiply: MOVS.N R2, #0 // set result to 0 CMP.N R1, #0 BEQ.N exit // goto exit if b == 0 loop: ADDS.N R2, R0, R2 // add a to result SUBS.N R1, #1 // substract 1 from b BEQ.N exit // goto exit if b == 0 B.N loop // loop otherwise exit: MOVS.N R0, R2 // move result to R0 BX.N LR ``` En het testresultaat: ![[assignment2/rest_result.png]] ### How many instructions does it take your procedure to run the code: `multiply(65535, 65535)`? 65535 (decimaal) = 0xFFFF (hex) ofwel 16 enen in binair. Voor de loop zijn 3 instructies. Een iteratie van de loop zijn 4 instructies, deze loop wordt net zo vaak als de waarde van `b` uitgevoerd dus 4 * 65535 - 1 = 262139 instructies (de - 1 is voor de laatste iteratie waar die uit de loop springt waardoor de laatste instructie in de loop wordt overgeslagen). Na de loop zijn nog 2 instructie, dat een totaal maakt van 262144 instructies ## A smarter tail recursive multiply algorithm Voor deze opdracht heb ik niet exact de gegeven C code na gemaakt. Ik heb de twee functies als een geschreven. Hierbij is het argument `m` van `function multiply2` verplaatst als laatste argument i.p.v. de eerste en de `if (b == 0)` heb ik verplaats naar `multiply`. Het verplaatsen van argument `m` is alleen een probleem wanneer er andere code is die gebruik maakt dat deze functie behalve `multiply`. Het verplassen van de `if (b == 0)`statement is een optimalisatie. Dit statement kan nooit waar zijn na de eerste iteratie. `b` wordt steeds 1 plek naar recht geschoven tot dat de laatste bit die `1` is op de 1 positie staat. In dat geval eindigt de functie direct. De assembly code: ```assambly .cpu cortex-m4 .thumb .syntax unified .globl multiply .text .thumb_func // R0 = a, R1 = b, R2 = result (m), R3 = odd/even mask/result multiply: MOVS.N R2, #0 // set result to 0 CMP.N R1, #0 BEQ.N exit // goto exit if b == 0 multiply2: CMP.N R1, #1 BNE.N check_even // goto check_even if b == 1 ADDS.N R2, R0 // move a to result B.N exit // goto exit check_even: MOVS.N R3, #1 // set R3 to 1 as bit mask ANDS.N R3, R1 // and R3 (bit mask) with b BNE.N odd // goto exit if b == 0 even: LSLS.N R0, R0, #1 // a = a << 1 LSRS.N R1, R1, #1 // b = b >> 1 B.N multiply2 // recurse odd: ADDS.N R2, R0 // add a to result LSLS.N R0, R0, #1 // a = a << 1 LSRS.N R1, R1, #1 // b = b >> 1 B.N multiply2 // recurse exit: MOVS.N R0, R2 // move result to R0 BX.N LR ``` Het testresultaat: ![[assignment5/result.png]] ### How many instructions does it take your procedure to run the code: `multiply(65535, 65535)`? 65535 (decimaal) = 0xFFFF (hex) ofwel 16 enen in binair. Vanaf label `multiply` duurt het 3 instructie tot de eerste branch instructie. Deze wordt niet genomen omdat `b` niet 0 is. Hierna komt `multiply2` waar de 2de instructie een branche is die genomen wordt. `b` nog geen 0. `check_even` heeft 3 instructies tot een brance. Deze brance wordt altijd genomen, omdat `b` in binair alleen maar uit enen bestaat, dus het schuiven naar rechts (de enige bewerking die gedaan wordt op `b`) verandert bit 1 niet naar 0 voor dat `b` 1 is geweest wanneer de loop eindigt. `odd` heeft 4 instructies, waarna `multiply2` weer wordt geroepen. De loop van `multiply2` -> `check_even` -> `odd` word 15 keer gedaan. Voor elk bitje dat 1 is in `b` tot dat er nog maar 1 over is. Deze loop bestaat uit 8 instructies, dus totaal 120 instructies. Wanneer `b` == 1 is net nog 6 instructies tot de `BX.N LR` instructie. Totaal is dat dus 129 instructies (3 van `multiply` + 120 van de loop + 6 van het einde). Dit is significant minder dat bij assignment 1, dit waren er 262144 dus deze implementatie is ongeveer 2032 keer minder instructies in dit specifieke geval. ## A smarter power algorithm Ik kwam wat kort in tijd en heb de machten tot 0 niet werkend kunnen maken. Hier wordt p * n terg gestuurd i.p.v. 1. Verder werkt de code. De assembly voor de power functie: ```assambly .cpu cortex-m4 .thumb .syntax unified .globl power .text .thumb_func // R5 = n, R6 = m, R4 = result (p) power: PUSH.N {R4, R5, R6} SUB.N SP, SP, #4 STR LR, [SP, #0] // push LR to the stack MOVS.N R4, #1 // set result to 1 CMP.N R1, #0 BEQ.N exit // goto exit if m == 0 MOVS.N R5, R0 // move n to R5 MOVS.N R6, R1 // move m to R6 loop: CMP.N R6, #1 BEQ.N exit // goto exit if m == 1 MOVS.N R3, #1 ANDS.N R3, R6 // R3 = m & 0x1 BEQ.N even MOVS.N R0, R4 MOVS.N R1, R5 LDR.N R3, =multiply BLX.N R3 // p * n MOVS.N R4, R0 // move multiply result to p even: MOVS.N R0, R5 MOVS.N R1, R5 LDR.N R3, =multiply BLX.N R3 // p * n MOVS.N R5, R0 // move multiply result to n LSRS.N R6, R6, #1 // m = m >> 1 B.N loop exit: MOVS.N R0, R4 MOVS.N R1, R5 LDR.N R3, =multiply BLX.N R3 // p * n LDR LR, [SP, #0] // pop LR from the stack ADD.N SP, SP, #4 POP.N {R4, R5, R6} BX.N LR ``` de test code die ik heb gebruikt om de functie te testen: ```c #include unsigned int multiply(unsigned int a, unsigned int b); unsigned int power(unsigned int n, unsigned int m); int main() { extern void initialise_monitor_handles(void); initialise_monitor_handles(); unsigned int n[] = {0, 1, 0, 1, 2000, 2 , 7, 4294967295u, 3 }; unsigned int m[] = {0, 0, 1, 1, 2, 31 , 11, 1 , 20 }; unsigned int r[] = {1, 1, 0, 1, 4000000, 0x80000000u, 1977326743, 4294967295u, 3486784401u}; for (size_t i = 0; i != sizeof(n)/sizeof(n[0]); i++) { printf("%u ^ %u: ", n[i], m[i]); unsigned int result = power(n[i], m[i]); unsigned int correct = r[i]; if (result != correct) { printf("Failed, function returned %u but the correct answer is %u\n", result, correct); } else { printf("Passed, %u\n", result); } } return 0; } ``` het testresultaat: ![[assignment7/result.png]]