Aí estava fazendo uma rotina que recebe um nibble e escreve os 4 bits menos significativos no 4 bits do display, normal. Normalmente, esta rotina é algo do tipo
- Código: Selecionar todos
void escreve_nibble(unsigned char valor)
{
if(valor & 0x01)
LATBbits.LATB0 = 1;
else
LATBbits.LATB0 = 0;
if(valor & 0x02)
LATBbits.LATB3 = 1;
else
LATBbits.LATB3 = 0;
if(valor & 0x04)
LATCbits.LATC1 = 1;
else
LATCbits.LATC1 = 0;
if(valor & 0x08)
LATBbits.LATB5 = 1;
else
LATBbits.LATB5 = 0;
Aí eu pensei "ah, mas isso é tosco. O PIC18 tem uma instrução chamada Bittest (BTFSS e BTFSC) que serve exatamente prá isso. Fazer esse mascaramento e esse if é muito pouco otimizado". Eu lembrei que o CCS possui o comando bittest() exatamente prá isso. Dei uma procurada no manual do XC8, mas não tem nada parecido com bittest.
Aí eu me toquei que o XC8 usa umas structs com bitfields prá acessar bits de registradores.
Quebrei a cabeça (porque eu me bato com essas coisas exotéricas do C) e consegui criar uma struct com uma union que tinha um bitfield, eu jogava o valor recebido da função prá dentro e acessava os valores bit por bit. Não vou colar aqui porque eu apaguei e esqueci como eu fiz (já vai entender o motivo).
Aí compilei, fui ver o assembly, e tadá: ficou lindo, no assembly ele usava BTFSS prá testar o bit, a manipulava os bits do registrador prá escrever na GPIO.
Eu estava explodindo de orgulho (é que eu realmente sou muito noob prá essas coisas diferentonas do C), mas eu queria a cereja do bolo. Queria comparar quanto minha implementação era mais otimizada que usar um monte de if.
Refiz a rotina, mas prá economizar dedos, usei operadores ternários (depois de uns 20 anos olhando feio prá esse treco, eu meio que consigo entender agora como usar essa bagaça). A rotina ficou:
- Código: Selecionar todos
void write_nibble(unsigned char val)
{
val & 0x01 ? LATBbits.LATB0 = 1 : LATBbits.LATB0 = 0;
val & 0x02 ? LATBbits.LATB3 = 1 : LATBbits.LATB3 = 0;
val & 0x04 ? LATCbits.LATC1 = 1 : LATCbits.LATC1 = 0;
val & 0x08 ? LATBbits.LATB5 = 1 : LATBbits.LATB5 = 0;
}
É basicamente exatamente igual ao que fiz acima, só troquei os if else por um ternário. Em C ficou curtinho, mas isso não tem a menor relevância, o que importa é o assembly. Minha decepção foi esta:
- Código: Selecionar todos
6: val & 0x01 ? DB4 = 1 : DB4 = 0;
11CB 1C70 BTFSS __pcstackCOMMON, 0x0
11CC 29D0 GOTO 0x1D0
11CD 0022 MOVLB 0x2
11CE 140E BSF LATC, 0x0
11CF 29D2 GOTO 0x1D2
11D0 0022 MOVLB 0x2
11D1 100E BCF LATC, 0x0
Onde está a operação de &? Onde está o if? Ond está o else? Poisé, esse malditinho otimizou o operador ternário em um único BTFSS. E o pior: ficou exatamente perfeitamente igual ao que ele compilou usando bit fields. Com a diferença que, prá mim, usar operadores ternário ainda é muitas vezes mais simples de entender do que aquela estratura com unions e bit fields.
Só queria compartilhar a experiência. Muitas vezes é bem legal olhar o assembly gerado, prá se tocar que às vezes fazer aqueles malabarismos todos em C que deixam o código impossível de entender, acaba gerando o mesmo assembly de um algoritmo simples e elegante.