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.

  1. 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.

    1. 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).
    2. 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).

  2. 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!

  3. 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".

  4. 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.

  5. 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.

  6. (**)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.