Exercícios "desafio"
Atualizado em 03-09-2012. Prazo de submissão: 04-10-2012
Os exercícios a seguir ilustram conceitos importantes de arquitetura e de programação
com o AVR.
Devem ser feitos individualmente: os alunos que resolverem a maioria
dos exercícios terão um bônus razoável na média final. Obviamente as soluções deverão ser
demonstradas de forma convincente no laboratório.
- Escreva um trecho de programa com apenas 6 instruções para zerar todos os registradores de propósito geral.
Obs: a fim de testar seu programa você deve inicializar os registradores com um valor diferente
de zero, digamos com ff. Você pode fazer isto via programa ou, no simulador, abra
View -> Memory -> Register e altere manualmente o conteúdo de todos os registradores.
-
- Escreva um trecho de programa com menos de 10 instruções para empilhar o conteúdo inicial dos
registradores r31, r30, ..., r0, nesta ordem (você pode iniciailizar via programa esse conteúdo a fim de facilitar
a depuração).
- Escreva um trecho de programa com menos de 10 instruções para desempilhar (via instruções pop) os
registradores r31, r30, ..., r0, previamente empilhados nesta ordem(r0 está no topo da pilha).
- Usando exclusivamente os registradores r0 e r1, escreva um trecho de programa para ligar
o bit 0 de r1 se o valor em r0 for negativo
e ligar o bit 1 de r1 se o valor em r0 for impar (obs: o valor inicial de r1 é indefinido
e apenas esses bits de r1 devem, se for o caso, permanecer ligados.
Deve ser feito com apenas 5 instruções!
- Escreva uma subrotina f16 para calcular de forma não recursiva e com precisão
de 16 bits o fatorial de um inteiro passado em r16 e devolvendo o fatorial no par (r2,r3 - low,high).
Além disso, se houver overflow deve retornar com o CY ligado e desligado, caso contrário.
Você pode ver uma tabela de fatoriais em hexadecimal
neste link.
(**)Estenda a solução acima para calcular o fatorial com precisão limitada apenas
pela capacidade da memória RAM. O fatorial pode ser armazenado na memória RAM no
formato little endian, o que simplifica a solução (pois a multiplicação é feita
a partir dos dígitos menos significativos): inicialize um vetor na memória RAM
com 0x01, 0x00,0x00...etc para representar
o valor 1 no formato little endian (veja otimização a seguir) e se N é o número
cujo fatorial desejamos calcular
multiplique sucessivamente esse valor por N, N-1,...,1. Se escrito com cuidado e não
contando as inicializações o algoritmo pode ser codificado em cerca de 20 instruções
do AVR! Otimização simples de implementar: como log n! = log 1 + log 2 + ... + log n < nlog n
um limite superior para o tamanho do buffer para armazenar o fatorial de n é dado por
M= n*log2(n) bits = (n*log2(n))/8 bytes e pode ser calculado em tempo de montagem
usando a diretiva .set do montador, pois o montador implementa a função log2(n).
Sugestão: multiplique (na base 10) 9 x 19 e analize cuidadosamente a
propagação dos "vai um".
- Escreva uma subrotina f16rec para calcular de forma recursiva e com precisão
de 16 bits o fatorial de um inteiro passado em r16, devolvendo o fatorial no par (r2,r3 - low,high).
Além disso, se houver overflow deve retornar com o CY ligado e desligado, caso contrário.
-
Escreva uma macro recursiva para calcular com precisão de 16 bits a sequencia de Fibonacci,
armazenando-a na memoria RAM no formato little endian. Devido à limitação de 16 recursões
do macro-montador do AVR apenas uma parte da sequencia pode ser calculada, mas você
pode iniciá-la com valores maiores da sequencia.
-
(**)Suponha que o mecanismo de incremento/decremento do SP num AVR está danificado, de forma que
as seguintes instruções não funcionam: CALL, RCALL, ICALL, RET, RETI, PUSH e POP (mas IN e OUT
continuam funcionando).
A fim de permitir escrever programas com subrotinas você deve escrever 4 macros denominadas
myrcall, myret, mypush e mypop que serão usadas em vez de rcall, ret, push e pop,
respectivamente, e que deverão causar o mesmo efeito que essas instruções, exceto que podem
usar e destruir apenas um par de registradores, digamos Y. Observe que agora a única forma
de "chamar" uma subrotina é através da instrução ijmp que usa o par z e que, obviamente
precisa também ser salvo antes de ser usado. O retorno de uma "chamada" também deve ser feito através
da instrução ijmp. Se você for suficientemente cuidadoso poderá inclusive garantir que o
registrador de estado SREG seja preservado pelas suas macros de forma que o único efeito colateral
seria a destruição do par Y.
(**) Embora não necessariamente maiores em termos de código, a dificuldade dos exercícios
marcados com "(**)" é maior que a dos demais.