5. Controlo da execução

5.1. if else

Tabela 5.1 if sem else.
int i, j, k

if (i >= j) {
    k = 19;
    i = j;
}

(a)

1; i = r0   j = r1    k = r2
2   cmp    r0, r1
3   blo    if_end
4   mov    r2, #19
5   mov    r0, r1
6if_end:

(b)

O código do bloco if (linhas 4 e 5) é colocado imediatamente após o código de avaliação da condição (linhas 2 e 3). Como determinar a condição da instrução de salto? Esta instrução realiza o salto se a expressão do if for falsa. A mnemónica da condição está relacionada com o primeiro operando da instrução compare anterior. Há que refletir sobre o que este registo representa na expressão que está a ser avaliada e determinar a condição que torna a expressão falsa. A condição LO (Lower) da instrução blo  if_end, implica R0 menor que R1, que a verificar-se, significa que i é menor que j.

Tabela 5.2 if com else.
int i, j, f;

if (i == j) {
    f <<= 1;
    i++;
} else {
    f >>= 2;
    i -= 2;
}
j++;

(a)

 1; i = r5   j = r3   f = r2
 2   cmp    r5, r3
 3   bne    if_else
 4   lsl    r2, r2, #1
 5   add    r5, r5, #1
 6   b      if_end
 7if_else:
 8   lsr    r2, r2, #2
 9   sub    r5, r5, #2
10if_end:
11   add    r3, r3, #1

(b)

A instrução cmp r5, r3 realiza a subtração R5 – R3 e afeta a flag Z com 1, se a diferença for zero, significa que i (R5) e j (R3) são iguais. No caso de i ser diferente de j, a flag Z é afetada com 0 e a instrução bne if_else “salta por cima” do bloco if (linhas 4 a 6) diretamente para o código do bloco else (linhas 8 e 9). No caso de i ser igual a j a instrução bne if_else não realiza salto e executa o bloco if (linhas 4 a 6). Este bloco termina com um salto incondicional (linha 6), para não executar o bloco else posicionado nas posições seguintes.

5.2. switch case

Tabela 5.3 switch case.
 1int v, a;
 2
 3switch (v) {
 4    case 1:
 5        a = 11;
 6        break;
 7    case 10:
 8        a = 111;
 9        break;
10    default:
11        a = 0;
12}

(a)

 1; v = r0   a = r1
 2switch_case_1:
 3    mov   r2, #1
 4    cmp   r0, r2
 5    bne   switch_case_10
 6    mov   r1, #11
 7    b     switch_break;
 8switch_case_10:
 9    mov   r2, #10
10    cmp   r0, #r2
11    bne   switch_default
12    mov   r1, #111
13    b     switch_break;
14switch_default:
15    mov   r1, #0
16switch_break:

(b)

A implementação do switch/case consiste em encadear um conjunto de ifs, um para cada caso.

5.3. do while

Tabela 5.4 do while.
1int v, z;
2
3do {
4    v >> 1;
5    z += 1;
6} while (v != 0);

(a)

1; v = r0   z = r1
2do_while:
3    lsr    r0, r0, #1
4    add    r1, r1, #1
5    sub    r0, r0, #0
6    bne    do_while

(b)

A programação assembly segue a ordem de escrita e de execução da programação em C – primeiro executa o corpo de instruções (linhas 3 e 4) e no final avalia a condição (linhas 5 e 6).

5.4. while

Tabela 5.5 while
int v, z;

while (v != 0) {
    v >> 1;
    z += 1;
}

(a)

1; v = r0   z = r1
2while:
3    sub    r0, r0, #0
4    beq    while_end
5    lsr    r0, r0, #1
6    add    r1, r1, #1
7    b      while
8while_end:

(b)

1; v = r0   z = r1
2while:
3    b      while_cond
4while_do:
5    lsr    r0, r0, #1
6    add    r1, r1, #1
7while_cond:
8    sub    r0, r0, #0
9    bne    while_do

(c)

O programa (b) da Tabela 5.5 é escrito e executado pela ordem da linguagem C – primeiro a avaliação da condição (linhas 3 e 4) e depois o bloco de instruções do while (linhas 5 e 6). Com esta programação, o processador executa 5 instruções durante o ciclo (linhas 3 a 7), entre elas duas instruções branch (linhas 4 e 7). No programa (c) da Tabela 5.5 o programa é escrito como num do while, com a avaliação da condição no final (linhas 8 e 9). O while começa com um salto incondicional (linha 3) para a avaliação da condição (linhas 8 e 9), porque esta deve ser executada em primeiro lugar. Esta programação resulta na supressão de uma instrução branch durante o ciclo, relativamente à programação apresentada na versão (b), o que a torna preferível. A supressão de uma instrução num ciclo, pode equivaler a uma redução significativa de processamento, porque essa instrução é executada múltiplas vezes.

5.5. for

Tabela 5.6 for.
1int i, a;
2
3for (i = 0, a = 1; i < n; ++i) {
4    a <<= 1;
5}

(a)

 1; i = r0   a = r1   n = r2
 2    mov    r0, #0
 3    mov    r1, #1
 4    b      for_cond
 5for:
 6    lsl    r1, r1, #1
 7    add    r0, r0, #1
 8for_cond:
 9    cmp    r0, r2
10    blo    for

(b)

A instrução

for (expression1; expression2; expression3)
    statement;

é equivalente a

expression1;
while (expression2) {
    statement;
    expression3;
}

A programação assembly apresentada na Tabela 5.6 (b) reflete esta equivalência, com o while implementado na variante mais eficiente – Tabela 5.5 (c).

5.6. Exercícios

Este conjunto de exercícios destina-se a exercitar a programação, em linguagem assembly do P16, das estruturas de controlo da execução if, switch, while e for.

O objetivo de cada exercício é traduzir para assembly, o trecho de código apresentado em linguagem C.

Concretize as variáveis como registos do processador.

  1. int a, b, c;
    
    if (a >= b)
        c = 99;
    
  2. int a = 34, b = 34, c = 100;
    
    if (a == b)
        c++;
    else
        c--;
    b += 1;
    
  3. char c, a;
    
    switch (c) {
        case 'a':
            a = '0';
            break;
        case 'k':
            a = '1';
            break;
        default:
            a = '9';
    }
    
  4. int v, z;
    
    do {
        v >> 1;
        z += 1;
    } while (v != 0);
    
  5. int v, z;
    
    while (v != 0) {
        v >> 1;
        z += 1;
    }
    
  6. int i, a;
    
    for (i = 0, a = 1; i < n; ++i) {
        a <<= 1;
    }
    
  7. uint16_t p;       /* produto */
    uint16_t m = 20;  /* multiplicando */
    uint16_t n = 14;  /* multiplicador */
    
    p = 0;
    while (n > 0) {
        p = p + m;
        n = n - 1;
    }
    
  8. uint16_t d = 255; /* dividendo */
    uint16_t e = 10;  /* divisor */
    uint16_t q;       /* quociente */
    uint16_t r;       /* resto */
    
    r = a;
    q = 0;
    while (d > e) {
        d = d - e;
        q = q + 1;
    }