RPI assembler tutorial #7

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 bits n keer naar links. De n de linkse bits zijn weg en de n 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 de  n meest linkse.
  • ASR Rsource3
    Zoals de vorige, maar met een register.
  • ROR #n
    Rotate Right. Zoals LSR maar de n meest rechtste bits zijn niet weg maar worden de  n  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 */

Dat was alles (andere delen)

3 thoughts on “RPI assembler tutorial #7

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *