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.
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.
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¶
== 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.
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).
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.
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.
Condição |
Operação |
Números naturais |
Números relativos |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
#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¶
|| 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.
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.
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.
char a = 35; bool b = (a & 8) == 0;