4. Operações sobre valores booleanos

Em linguagem C uma variável do tipo bool pode assumir os valores true ou false. Em memória, uma variável do tipo bool ocupa um byte (uma posição de memória). O valor numérico zero é avaliado como false e um valor numérico diferente de zero é avaliado como true.

Qualquer valor numérico do tipo char, short, int ou long pode também ser avaliado como booleano, segundo o mesmo critério.

Tabela 4.1 Avaliação booleana de uma variável
bool a;
int b;

if (a)
    b = 3
int a, b;


if (a)
    b = 3
int a, b;


if (a != 0)
    b = 3
; a = r0   b = r1
   add   r0, r0, #0
   bzc   if_end
   mov   r1, #3
if_end:
bool a;
int b;

if (!a)
    b = 3
int a, b;


if (!a)
    b = 3
int a, b;


if (a == 0)
    b = 3
; a = r0   b = r1
   add   r0, r0, #0
   bzs   if_end
   mov   r1, #3
if_end:

(a)

(b)

(c)

(d)

Na Tabela 4.1, no que concerne à condição do if, o código das colunas (a) e (b) trata de avaliar se a variável a tem o valor true ou false. Nas colunas (c) e (d), trata de avaliar ser a variável a é igual ou diferente de zero. O que é equivalente, segundo o critério de representação dos valores booleanos.

A instrução add   r0, r0, #0 ao adicionar zero a R0 não altera o seu valor original mas afeta a flag Z em conformidade com o valor de a – se a for zero a flag Z recebe 1; se a for diferente de zero a flag Z recebe 0. A flag Z é afetada com o valor contrário ao valor booleano da expressão.

As operações de comparação ( ==, !=, <, >, <=, >= ) produzem valores booleanos – true ou false.

Em linguagem C, um valor booleano pode ser afetado a uma variável de qualquer tipo numérico ou ser operado com operadores numéricos. Para este efeito, o valor booleano false é convertido no valor numérico zero e o valor booleano true é convertido no valor numérico um.

Tabela 4.2 Afetação com expressão booleana.
int x, y;

y = x == 20;

(a)

1; x = r0   y = r1
2mov   r2, #20
3cmp   r0, r2
4mrs   r1, cpsr
5mov   r2, #1
6and   r1, r1, r2

(b)

No programa (a) da Tabela 4.2, a variável y é afetada com o valor zero ou um, resultante da conversão para valor numérico, do valor booleano resultado da expressão x == 20.

No programa (b) da Tabela 4.2, a instrução cmp  r0, r2 afeta a flag Z com o resultado da comparação de x com 20 – valor 1 se forem iguais; valor 0 se forem diferentes. A instrução mrs  r1, cpsr copia o registo CPSR para R1. A aplicação da máscara 0000 0000 0000 0001 (linhas 5 e 6), garante em R1 a representação numérica a 16 bits do valor da flag Z que está posicionada no bit de menor peso de CPSR, absorvendo o valor das outras flags que fazem parte deste registo.

4.1. Operações de comparação

As operações de comparação <, >, <= e >= operam sobre valores numéricos. As operações == e != operam sobre valores numéricos ou booleanos. Todas elas produzem valores booleanos – true ou false.

Concretizar uma destas operações consiste em realizar operações aritméticas ou lógicas que afetem as flags do processador. Estas operações devem ser escolhidas de modo que a análise do valor das flags seja conclusiva em relação àquilo que se quer avaliar.

4.1.1. Comparação de valores numéricos

Tabela 4.3 Operadores de comparação numérica

== igualdade

> maior que

>= maior ou igual a

!= diferença

< menor que

<= menor ou igual a

A comparação de valores numéricos baseia-se no valor das flags N, V, C e/ou Z, resultante de uma operação de subtração. A instrução de subtração que normalmente se usa é a instrução cmp rn, rm, que é idêntica à instrução sub rd, rn, rm, com a diferença de não se aproveitar o resultado (a diferença dos operandos), apenas as flags são afetadas em conformidade com o resultado.

Tabela 4.4 Comparação "igual a".
int a, b, c;

if (a == b)
    c = a;
; a = r0   b = r1   c = r2
    cmp   r0, r1
    bzc   if_end
    mov   r2, r0
if_end:

No programa da Tabela 4.4 a instrução mov  r2, r0, correspondente a c = a, não deve ser executada se a for diferente de b. A instrução cmp  r0, r1 ao realizar a subtração de R1 a R0, afeta a flag Z com 1 se a e b forem iguais e afeta a flag Z com 0 se forem diferentes. Portanto, a instrução mov  r2, r0 não deve ser executada se Z for 0. É o que resulta da utilização da instrução bzc  if_end (Branch if flag Zero is Clear). Esta instrução transfere a execução para a posição do programa indicada pela label if_end se a flag Z for 0.

A instrução BZC tem o nome alternativo BNE (Branch if Not Equal), que permite escrever o programa em assembly de forma mais direta. A mnemónica NE corresponde à flag Z ser 0, porque a flag Z é afetada com 0 se os operandos não forem iguais. Em coerência, a instrução BZS (Branch if flag Zero is Set) tem o nome alternativo BEQ (Branch if Equal).

Tabela 4.5 Comparação "menor que".
1int a, b, c;
2
3if (a < b)
4    c = a;
; a = r0   b = r1   c = r2
    cmp   r0, r1
    bcs   if_end
    mov   r2, r0
if_end:

No programa da Tabela 4.5 a instrução mov  r2, r0, correspondente a c = a, não deve ser executada se a for maior ou igual a b. A instrução cmp  r0, r1 ao realizar a subtração de R1 a R0 (R0 - R1) afeta a flag C com 0 se a for menor que b e afeta a flag C com 1 se a for maior ou igual a b. A flag C assume o valor contrário ao do arrasto da subtração da posição de peso 16 para a posição de peso 15. Assim, a instrução mov  r2, r0 não deve ser executada se a flag C for 1, que é o que resulta da utilização da instrução bcs  if_end (Branch if flag Carry is Set). Esta instrução transfere a execução para a posição do programa indicada pela label if_end se a flag C for 1.

A instrução BCS tem o nome alternativo BHS (Branch if Higher or Same). Onde está bcs  if_end poderia estar bhs  if_end. A mnemónica HS corresponde à flag C ser 1, o que acontece se numa instrução CMP ou SUB o subtraendo for maior ou igual ao subtrator.

A sequência

cmp  rm, rn
bhs  label

pode ter a seguinte leitura: a instrução BHS realiza “salto” se rm for maior ou igual a rn.

Tabela 4.6 Comparação "maior que".
1int a, b, c;
2
3if (a > b)
4    c = a;
; a = r0   b = r1   c = r2
    cmp   r1, r0
    bhs   if_end
    mov   r2, r0
if_end:

Para avaliar a condição a > b no programa da Tabela 4.6 com base na mesma instrução cmp r0, r1 a condição de salto seria a contrária à do programa da Tabela 4.5 – seria “saltar” se menor ou igual (Lower or Same).

Como no P16 não existe a suposta instrução de salto BLS, a solução apresentada realiza a subtração com os operandos em posições invertidas (cmp  r1, r0) e continua a aplicar BHS.

A instrução cmp  r1, r0 afeta a flag C com 1 se a for maior que b e afeta a flag C com 0 se a for menor ou igual a b.

Tabela 4.7 Condições de comparação de números.

Condição

Operação

Números naturais

Números relativos

if (a < b)

cmp  r0, r1

bhs

bge

if (a <= b)

cmp  r1, r0

blo

blt

if (a > b)

cmp  r1, r0

bhs

bge

if (a >= b)

cmp  r0, r1

blo

blt

Na Tabela 4.7 apresentam-se soluções de programação para as quatro relações possíveis de comparação.

Na comparação de números relativos, codificados em código de complementos, devem ser utilizadas as instruções BGE (Branch if Greater or Equal) ou BLT (Branch if Less Than).

Regra prática: a mnemónica da instrução branch aplica-se ao primeiro operando da instrução compare anterior.

4.1.2. Testar o valor de um bit

Testar o valor de um bit de uma variável consiste em fazer refletir o valor desse bit numa das flags do processador. Para isso realizam-se operações sobre a variável que transfiram o valor desse bit para uma flag.

Tabela 4.8 Testar o valor de um bit
#define N 2

int16_t a, b;

if ((a & (1 << N)) != 0)
    b = 3
1; a = r0   b = r1
2   .equ  N, 2
3
4   mov   r2, #(1 << N)
5   and   r2, r0, r2
6   bzs   if_end
7   mov   r1, #3
8if_end:
1; a = r0   b = r1
2   .equ  N, 2
3
4   ror   r0, r0, #(N + 1)
5   bcc   if_end
6   mov   r1, #3
7if_end:

(a)

(b)

(c)

O programa da Tabela 4.8 testa o valor do bit da terceira posição (peso 2) da variável a.

Na versão (b), é realizada uma operação and entre a variável e uma constante formada por zeros e um 1 na posição que se pretende testar. Esta constante designam-se por máscara. Neste caso a máscara tem o valor 0000 0000 0000 0100. O valor 1 na posição N, sendo o elemento neutro da operação and, faz com que o resultado da instrução and r3, r0, r2 seja zero, no caso do bit da posição N da variável ser 0 ou diferente de zero no caso do bit da posição N da variável ser 1. A flag Z é afetada com o valor contrário ao do bit que se pretende testar.

Na versão (c), o bit da variável que se pretende testar é deslocado para a flag C pela instrução ror   r0, #(N + 1). O número de posições a deslocar é N + 1.

Em ambos os casos a instrução branch “salta por cima” da instrução mov  r1, #3 (b = 3) quando o bit em avaliação é 0.

4.2. Operadores booleanos

Tabela 4.9 Operadores booleanos

|| disjunção

&& conjunção

! negação

Em geral nas linguagens de programação, a avaliação dos operandos da disjunção ou conjunção realiza-se da esquerda para a direita (ordem de leitura do texto). Nesta avaliação, assim que for encontrado um resultado igual ao elemento absorvente, as restantes sub-expressões já não serão avaliadas (lazy evaluation). A utilização deste critério visa a não realização de processamento desnecessário. Pelo conhecimento que o programador tiver dos dados, deve começar por escrever, em primeiro lugar, as sub-expressões cujo resultado mais provável evite o processamento das seguintes.

Tabela 4.10 Expressão com operação conjunção.
1int a, b, c;
2
3if (a >= 3 && b >= 3)
4    c += 3;

(a)

1; a = r0   b = r1   c = r2
2   mov    r3, #3
3   cmp    r0, r3
4   blo    if_end
5   cmp    r1, r3
6   blo    if_end
7   add    r2, r2, #3
8if_end:

(b)

No programa (b) da :Tabela 4.10, se o resultado da avaliação de a >= 3 (linhas 2 e 3) for falso, a sub-expressão b >= 3 (linhas 5 e 6) já não será avaliada, nem o bloco do if (linha 7) será executado.

Tabela 4.11 Expressão com operação disjunção.
1int a, b, c;
2
3if (a >= 3 || b >= 3)
4    c += 3;

(a)

1; a = r0   b = r1   c = r2
2   mov    r3, #3
3   cmp    r0, r3
4   bhs    if_then
5   cmp    r1, r3
6   blo    if_end
7if_then:
8   add    r2, r2, #3
9if_end:

(b)

No programa (b) da :Tabela 4.11, se o resultado da avaliação de a >= 3 (linhas 2 e 3) for verdadeiro, a sub-expressão b >= 3 (linhas 5 e 6) já não será avaliada, e o bloco do if (linha 8) é imediatamente executado.

4.3. Exercícios

Este conjunto de exercícios destina-se a exercitar a programação, em linguagem assembly do P16, de operações envolvendo valores booleanos.

O objetivo de cada exercício é programar em assembly, o cálculo da expressão definida em linguagem C.

Concretize as variáveis como registos do processador.

  1. char a = 35;
    
    bool b = (a & 8) == 0;