Rpi Assembler Tutorial 6
In het vorige deel heb ik de branch instructies vergeleken met control-flow tools, dit was niet helemaal juist. Er zijn in ARM assembler ingebouwde if-else structuren te vinden.
IF, THEN, ELSE
Dit is een gemakkelijke. We hebben deze structuur al gebruikt in het vorige deel. Beeld je de volgende structuur in, waar E een expressie is, en S1 en S2 statements zijn.
if (E) then S1 else S2
Een manier om dit in assembler voor te stellen is:
if_eval: /* Assembler that evaluates E and updates the cpsr accordingly */ bXX else /* Here XX is the appropiate condition */ then_part: /* assembler for S1, the "then" part */ b end_of_if else: /* assembler for S2, the "else" part */ end_of_if:
Als er geen else deel is, kunnen we de bXX else met Bxx end_of_if vervangen.
Loops
Dit is ook een belangrijke. Een loop kan je voorstellen als volgt:
while (E) S
Zo doet S iets waardoor E uiteindelijk een waarde = false krijgt, waardoor de loop stopt. Anders zou hij altijd in de loop blijven. Dit is soms handig maar niet voor onze voorbeelden. Zo kan je deze implementeren:
while_condition : /* assembler to evaluate E and update cpsr */ bXX end_of_loop /* If E is false, then leave the loop right now */ /* assembler of S */ b while_condition /* Unconditional branch to the beginning */ end_of_loop:
Een andere veelvoorkomende loop is de FOR loop, deze laat een integer altijd stijgen (of dalen) zoals:
for (i = L; i < N; i += K) S
Deze is niets meer dan:
i = L; while (i < N) { S; i += K; }
Hierdoor kunnen we door een while-loop te gebruiken dezelfde functie krijgen als een for-loop
Een voorbeeld
Laat ons voor dit voorbeeld alle nummers van 1-22 optellen (1+2+3+… …+22)
/* -- loop01.s */ .text .global main main: mov r1, #0 /* r1 ← 0 */ mov r2, #1 /* r2 ← 1 */ loop: cmp r2, #22 /* compare r2 and 22 */ bgt end /* branch if r2 > 22 to end */ add r1, r1, r2 /* r1 ← r1 + r2 */ add r2, r2, #1 /* r2 ← r2 + 1 */ b loop end: mov r0, r1 /* r0 ← r1 */ bx lr
Hier tellen we van 1 naar 22, we zullen r2 als counter gebruiken. Op lijn 6 zetten we r2 naar 1. De som zal opgeslagen worden in r1. Op het eind verplaatsten we de inhoud van r1 naar r0, waardoor we het resultaat van de som als een error code kunnen zien. Deze laatste stap kan je echter overslaan door direct r0 te gebruiken.
Op lijn 8 vergelijken we r2 (de counter) met 22 Dit update de cpsr, dus op lijn 9 kunnen we het resultaat gaan controleren. Als dat het geval is kunnen we branchen naar het eind (de end: tag). Anders voegen we de waarde van r2 toe aan r1.
Lijn 11 is een belangerijke. Hier voegen we 1 toe aan de waarde van r2. Dit is omdat we van 1 naar 22 aan het tellen zijn we hebben de waarde van r2 al bij de som van r1 geteld. Op regel 12 branchen we weer naar het begin van de loop. Als regel 11 er niet was zou de loop nooit eindigen.
Als we dit uitvoeren zien we het resultaat.
$ ./loop01; echo $? 253
Nu, waarom heb ik 22 gekozen, en geen getal zoals bijvoorbeeld 100. Als we het zouden aanpassen naar 100 zouden we 5050 moeten krijgen, maar:
$ ./loop01; echo $? 186
Hier loopt iets mis. Dit gebeurt omdat de error-code van een programma in linux een getal is van 0 tot 255 (8 bits, 1 byte). Als het resultaat 5050 is, worden enkel de laagste 8 bits van het getal gebruikt. zo word 5050 in binair: 1001110111010, De laagste 8 bits zijn 10111010, wat 186 is. Laat ons GDB gebruiken om het te debuggen.
$ gdb loop ... (gdb) start Temporary breakpoint 1 at 0x8390 Starting program: /home/roger/asm/chapter06/loop01 Temporary breakpoint 1, 0x00008390 in main () (gdb) disas main,+(9*4) Dump of assembler code from 0x8390 to 0x83b4: 0x00008390 <main+0>: mov r1, #0 0x00008394 <main+4>: mov r2, #1 0x00008398 <loop+0>: cmp r2, #100 ; 0x64 0x0000839c <loop+4>: bgt 0x83ac <end> 0x000083a0 <loop+8>: add r1, r1, r2 0x000083a4 <loop+12>: add r2, r2, #1 0x000083a8 <loop+16>: b 0x8398 <loop> 0x000083ac <end+0>: mov r0, r1 0x000083b0 <end+4>: bx lr End of assembler dump.
Als we een breakpoint instellen op 0x00083ac, net voordat we r1 naar r0 verplaatsen:
(gdb) break *0x000083ac (gdb) cont Continuing. Breakpoint 2, 0x000083ac in end () (gdb) disas Dump of assembler code for function end: => 0x000083ac <+0>: mov r0, r1 0x000083b0 <+4>: bx lr End of assembler dump. (gdb) info register r1 r1 0x13ba 5050
Hier zien we wat we verwacht hadden, maar we konden het niet zien door de limieten van error codes.
Er is ook iets aan de gang met onze labels die functies worden. Dit zal ik verder uitleggen in een verder deel.
Dit was alles voor deel 6.