Rpi Assembler Tutorial 7
Datum: 2019/01/15
De ARM architectuur is vooral gericht op embedded systeemen. Deze zijn vaak gebruikt in massaproductie-producten. In deze context zijn de marges klein, dus zal de ontwerper zo min mogenlijk componenten willen gebruiken. Een relatief duur component is geheugen, hoewel het elke dag een stukje goedkoper word. In zulke situaties wordt geheugen als bespaard door de ARM instructieset. Deze is ontworpen met dat doel. Ik zal een paar delen nodig hebben om alles uit te leggen, maar we zullen beginnen met ‘Shifted operand’.
Indexeermodes
We hebben gezien dat voor alles behalve load store and branches, ARM instructies als operators ofwel registers ofwel de waarde zelf gebruiken. We hebben ook gezien dat de eerste operator vaak het doelregister is. De instructie mov heeft nog een andere operator: een register of een waarde. Instructies zoals ADD en AND hebben vaak nog meer bronregisters. De eerste is altijd een register en de 2e kan een register zijn, maar ook een andere waarde.
Dit maakt het mogenlijk voor operators in instructies om collectief te zijn. Dit worden indexing modes genoemd. Dit idee kan een beetje ‘off-topic’ lijken omdat we niets zullen indexeren. De naam Indexing heeft wel nut voor geheugen operators. ARM instructies buiten load en store hebben geen geheugen operators.
De syntax van de meeste ARM instructies is als volgt:
instruction Rdest, Rsource1, source2
Er zijn een paar uitzonderingen: mov, b en bxx, ldr en str.
In een volgend deel zal ik de indexeermodes van ldr en str overlopen. Voor Branches is het echter zeer simpel. Hier is het enkel een label in ons programma.
Shift operaties
Wat raar is aan source2 in de instructies hierboven is dat source2 zowel een register als een waarde kan zijn. Dit hebben we al gebruikt in vorige delen bv:
mov r0, #1 mov r1, r0 add r2, r1, r0 add r2, r3, #4
Source2 kan veel meer zijn dan een waarde of een register. We kunnen het zelf combineren met een shift operatie. In deel 6 zagen we er al een, maar nu is het tijd om ze allemaal te laten zien.
LSL #n
Logical Shift Left. Shifts bitsn
keer naar links. Den
de linkse bits zijn weg en den
de rechtse bits zijn 0.LSL Rsource3
Zoals de voorige maar met de waarde van een register.LSR #n
Logical Shift Right. Zelfde van de eerste, maar dan langs rechts,LSR Rsource3
Zelfde van de vorige, maar dan met een register.ASR #n
Arithmetic Shift Right. Zoals LSR Maar de meest linkse bit voor het shiften is gebruikt in plaats van 0 in den
meest linkse.ASR Rsource3
Zoals de vorige, maar met een register.ROR #n
Rotate Right. Zoals LSR maar den
meest rechtste bits zijn niet weg maar worden den
meest linkse bits.ROR Rsource3
Zoals ROR, maar dan met een register.
In bovenstaande lijst is n een nummer van 1 tot 31. Arm heeft geen shift right en shift left instructie, dus moet je de mov instructie gebruiken:
mov r1, r2, LSL #1
Als je je afvraagt waarom je een register naar links of naar rechts wilt sturen, maar als je in deel 6 hebt gezien dat LSL een waarde geeft die hetzelfde doet als x2, dan geeft ASR een waarde die gedeeld door 2 is. Aangezien een shift van n hetzelfde is als n shifts van 1. Shifts zijn eigenlijk maal of gedeeld door een waarde van 2^n.
mov r1, r2, LSL #1 /* r1 ← (r2*2) */ mov r1, r2, LSL #2 /* r1 ← (r2*4) */ mov r1, r3, ASR #3 /* r1 ← (r3/8) */ mov r3, #4 mov r1, r2, LSL r3 /* r1 ← (r2*16) */
We kunnen dit combineren voor zeer handige gevallen:
add r1, r2, r2, LSL #1 /* r1 ← r2 + (r2*2) equivalent to r1 ← r2*3 */ add r1, r2, r2, LSL #2 /* r1 ← r2 + (r2*4) equivalent to r1 ← r2*5 */
Je kan ook zoiets doen met sub
sub r1, r2, r2, LSL #3 /* r1 ← r2 - (r2*8) equivalent to r1 ← r2*(-7)
Arm komt met een handige rsb functie (reverse subtract) instructie, deze doet het omgekeerde van de sub instructie
rsb r1, r2, r2, LSL #3 /* r1 ← (r2*8) - r2 equivalent to r1 ← r2*7 */
Een voorbeeld:
* Complicated way to multiply the initial value of r1 by 42 = 7*3*2 */ rsb r1, r1, r1, LSL #3 /* r1 ← (r1*8) - r1 equivalent to r1 ← 7*r1 */ add r1, r1, r1, LSL #1 /* r1 ← r1 + (2*r1) equivalent to r1 ← 3*r1 */ add r1, r1, r1 /* r1 ← r1 + r1 equivalent to r1 ← 2*r1 */
Je zult je afvragen waarom we deze functies willen gebruiken, maar omdat de standaard vermenigvuldigingsinstructie veel meer werk is voor de cpu, dus langer duurt, is het voor bepaalde gevallen handig (zeker als het kleinere bewerkingen zijn) om het zo te doen.
Rotaties zijn minder handig dan shifts in het werkelijke gebruik. Deze zijn dan ook vooral gebruikt voor het versleutelen van bits. ARM heeft geen functie om naar links te roteren, maar we kunnen wel iets anders doen:
/* Assume r1 is 0x12345678 */ mov r1, r1, ROR #1 /* r1 ← r1 ror 1. This is r1 ← 0x91a2b3c */ mov r1, r1, ROR #31 /* r1 ← r1 ror 31. This is r1 ← 0x12345678 */