posted by Wagner Lipnharski @ 1:58pm, Thursday 4 April 2013.
O instruçtion set da familia AVR não possui uma instrução de adição em um registrador com um valor imediato.
Por exemplo, você queira somar o valor fixo “6″ no registrador R18, pela lógica seria a instrução ADDI R14, 0×06, ou, ADI R14, 0×06, mas essa instrução não existe, mas existe a instrução ADD R18, Rnn. Para faze-la, se tem que carregar o valor 0×06 em outro registrador, por exemplo, LDI R20, 0×06, e então efetuar a soma ADD R18, R20.
Mas lembrar que somar +6 é o mesmo que subtrair -6, pois “menos com menos resulta em mais”.
Então, ADDI R18, 0×06 é quase o mesmo que SUBI R18, -0×06, e essa instrução existe.
Outro exemplo, 7+1 é o mesmo que 7-255, o resultado sempre será 8.
Lembrar que -6 também pode ser escrito como 0-6, ou seja, 0xFA.
Então, pode-se escrever SUBI R18, -6 ou SUBI R18, 0xFA.
A diferença entre a suposta ADDI R18, 6 e a SUBI R18, 0xFA, é o carry bit.
Se R18 tivesse um conteúdo de 3, ao somar 6 resultaria em 9, sem carry bit, pois o resultado da soma não ultrapassou 255 e não virou o carry bit. Mas ao subrair 254 (-6 ou 0xFA) de 3, o resultado será 2, 1, 0 -1 -2 -3 ….. até chegar ao 9 esperado, na verdade -9, e o sinal negativo ai é o carry bit que ficou ligado. É só ignorar o carry bit, ou prestar a atenção e tomar a atitude correta com relação ao carry bit.
A forma de fazer o carry bit assumir o valor correto após essa “soma” feita subtração, é comparar o resultado com o valor “6″ que deveria ter sido somado.
Então após a SUBI R18, -6, a instrução Compare Imediate “CPI R18, 6″ fará o serviço correto no carry bit. Veja, como o R18 antes da subtração era “3″, somando 6 ela irá para “9″, e o carry bit deveria estar desligado, mas devido à subtração de -6, ele ficou ligado “indevidamente” para o que se destina. Ao comparar o resultado de R18 com 6, e R18 é 9, o carry bit ficará baixo, pois 9 é maior que 6. Nessa comparação de R18 com 6, o carry bit só ficará ligado quando R18 for entre zero e cinco, ou seja, menor que o valor “6″, e isso só irá ocorrer após a suposta soma com “6″, se o R18 original fosse entre 0xF9 e 0xFF, e a suposta soma com 6 fosse efetivamente gerar o carry bit.
Então, para não usar um segundo registrador para fazer a soma imediata, pode-se sim usar o SUBI R18, -6 ou SUBI R18, 0xF9, mas para acertar o carry bit, em seguida teremos que usar CPI R18, 6.
Mas, necessitando fazer uma soma de valor constante (imediato) à um numero que use mais de 8 bits, ou seja, mais um byte, pode-se fazer toda a operação de soma usando o SUBI, e o SBCI (subtract com valor imediato e com carry) e com isso resolve tudo ao contrário e obtendo o valor certo ao final. Por exemplo 3 bytes, R18, R19 e R20, somando o valor 6 e que propagasse o valor nos 24 bits, considerando que R18 seja o byte de menor ordem, ficaria assim:
SUBI R18, -6
SBCI R19, -1
SBCI R20, -1
Ao subtrair -1 de um número qualquer, ele soma um, mas a instrução SBCI irá também considerar o carry bit na subtração. Se o carry bit estiver desligado, ele irá efetivamente subtrair -1, ou seja, somar 1 no registrador R19 ou R20, no exemplo acima. O carry bit só estará desligado se a instrução anterior efetivamente criaria um carry numa soma, tipo, somar 0x00FF + 0×0004 = 0×103, mas na subi ou sbci tal carry não existiria. Então, o SBCI Rxx, -1 irá só subtrair efetivamente -1 do registrador quando o carry bit estiver desligado, e ele só está desligado quando precisa propagar o carry que não ocorreu.
Ou seja, numa subtração de número negativo, o carry bit funciona ao contrário. Se o carry estiver desligado é porque precisa somar 1 no byte à esquerda, se o carry estiver ligado é porque não precisa somar 1.
Então, é o mesmo que seria (mas não existe):
ADI R18, 6
ADCI R19, 0
ADCI R20, 0
Onde o carry bit se propaga para os bytes de mais alta ordem, e transforma, por exemplo, 00|05|FE + 6 em 00|06|04.
A restrição é que devido à definição de bits nas instruções, a instrução SUBI e SBCI só funcionam nos registradores R16 a R31