int main(){
asm volatile(
// IMPORTANTE:
// registradores r6, r7, r8 e r9 reservados para valores decimais dos dígitos
// registrador r0 reservado para controle dos segundos
"cli \n\t" // desabilita interrupts
"clr r0 \n\t" // limpa (zera) r0
"clr r1 \n\t" // limpa (zera) r1
// configurando Stack Pointer para o final da SRAM de 2 KB
"ldi r16, 0b00000111\n\t"
"out 62, r16 \n\t" // move pro high byte do SP
"ldi r16, 0b11111111\n\t"
"out 61, r16 \n\t" // move pro low byte do SP
// configurando o timer1
// modo CTC (Clear Timer on Compare-match), prescaler 1024 (um ciclo a cada 64 microssegundos)
"ldi r25, 0b00000000 \n\t" // coloca zero no r25
"sts 128, r25 \n\t" // envia o zero para o endereço do registrador TCCR1A (128)
"ldi r25, 0b00001101 \n\t" //coloca no r25 o valor do registrador TCCR1B (endereço 129)
"sts 129, r25 \n\t" // envia o valor para o registrador TCCR1B (endereço 129)
// zerando o contador do timer 1
"ldi r25, 0 \n\t" // coloca zero no r25
"sts 132, r25 \n\t" // envia o zero para o endereço do registrador TCNT1L (132)
"sts 133, r25 \n\t" // envia o zero para o endereço do registrador TCNT1H (133)
// estabelecendo 15.625 como valor do Compare-match A do Timer1
// cerca de 1 segundo para o Compare-match
"ldi r25, 0b00111101 \n\t" // valor do high byte
"sts 137, r25 \n\t" // envia o valor para o endereço do registrador OCR1AH (137)
"ldi r25, 0b00001001 \n\t" // valor do low byte
"sts 136, r25 \n\t" // envia o valor para o endereço do registrador OCR1AL (136)
// configurando o timer2
// modo CTC (Clear Timer on Compare-match), prescaler 1024 (um ciclo a cada 64 microssegundos)
"ldi r25, 0b00000010 \n\t" // coloca no r25 o valor do registrador TCCR2A (endereço 176)
"sts 176, r25 \n\t" // envia o zero para o endereço do registrador TCCR2A (endereço 176)
"ldi r25, 0b00000111 \n\t" //coloca no r25 o valor do registrador TCCR2B (endereço 177)
"sts 177, r25 \n\t" // envia o valor para o registrador TCCR2B (endereço 177)
// zerando o contador do timer 2
"ldi r25, 0 \n\t" // coloca zero no r25
"sts 178, r25 \n\t" // envia o zero para o endereço do registrador TCNT2 (178)
// estabelecendo 16 como valor do Compare-match A do Timer2
// cerca de 1024 microssegundos para o Compare-match
"ldi r25, 0b00010000 \n\t" // valor do OCR2A
"sts 179, r25 \n\t" // envia o valor para o endereço do registrador OCR2A (179)
// habilitando a interrupt do Compare-match A do Timer1
"lds r25, 111 \n\t" //copia para o r25 o valor do registrador TIMSK1 (endereço 111)
"ori r25, 0b00000010 \n\t" // seta o bit na posição OCIE1A
"sts 111, r25 \n\t" // envia o novo valor para o registrador TIMSK1 (endereço 111)
// habilitando a interrupt do Compare-match A do Timer2
"lds r25, 112 \n\t" //copia para o r25 o valor do registrador TIMSK2 (endereço 112)
"ori r25, 0b00000010 \n\t" // seta o bit na posição OCIE2A
"sts 112, r25 \n\t" // envia o novo valor para o registrador TIMSK2 (endereço 112)
// Porta D toda em OUTPUT
"sbi 10, 0 \n\t"
"sbi 10, 1 \n\t"
"sbi 10, 2 \n\t"
"sbi 10, 3 \n\t"
"sbi 10, 4 \n\t"
"sbi 10, 5 \n\t"
"sbi 10, 6 \n\t"
"sbi 10, 7 \n\t"
// Porta D toda em LOW
"cbi 11, 0 \n\t"
"cbi 11, 1 \n\t"
"cbi 11, 2 \n\t"
"cbi 11, 3 \n\t"
"cbi 11, 4 \n\t"
"cbi 11, 5 \n\t"
"cbi 11, 6 \n\t"
"cbi 11, 7 \n\t"
// pinMode(9, INPUT_PULLUP) e digitalWrite(9, HIGH) em assembly:
"cbi 4, 1 \n\t"
"sbi 5, 1 \n\t"
// pinMode(10, INPUT_PULLUP) e digitalWrite(10, HIGH) em assembly:
"cbi 4, 2 \n\t"
"sbi 5, 2 \n\t"
// pinMode(11, INPUT_PULLUP) e digitalWrite(11, HIGH) em assembly:
"cbi 4, 3 \n\t"
"sbi 5, 3 \n\t"
// pinMode(12, INPUT_PULLUP) e digitalWrite(12, HIGH) em assembly:
"cbi 4, 4 \n\t"
"sbi 5, 4 \n\t"
// zerando endereços de memória das variáveis
"clr r25 \n\t"
"sts 821, r25 \n\t" // endereço 821 guarda a variável de controle do modo de operação
"sts 1005, r25 \n\t" // endereço 1005 guarda a variável do contador primário
"sts 911, r25 \n\t" // endereço 911 guarda a variável do contador secundário
"nop \n\t"
"sei \n\t" // habilita interrupts
"nop \n\t"
"loop: \n\t" //inicio do loop infinito
"nop \n\t"
"nop \n\t"
"lds r25, 911 \n\t" // carrega valor do contador secundário da RAM para o registrador r25
"cp r24, r25 \n\t" // verifica se r25 está com um valor diferente da última iteração
"breq Contadores \n\t" // se contador secundário ainda não tiver mudado pula pro final
"mov r16, r0 \n\t" // copia o valor atual dos segundos de r0 para r16
"call ConverteBinDecimal \n\t" // chama a função que converte o valor binário para decimal
"cpi r25, 1 \n\t" //compara o contador secundário com 1
"breq CS1 \n\t"
"cpi r25, 2 \n\t" //compara o contador secundário com 2
"breq CS2 \n\t"
"cpi r25, 3 \n\t" //compara o contador secundário com 3
"breq CS3 \n\t"
"cpi r25, 4 \n\t" //compara o contador secundário com 4
"breq CS4 \n\t"
"rjmp Contadores \n\t"
"CS1: \n\t"
"ldi r22, 1 \n\t" // exibir dígito da milhar
"call ExibeDigito\n\t"
"rjmp Contadores \n\t"
"CS2: \n\t"
"ldi r22, 2 \n\t" // exibir dígito da centena
"call ExibeDigito \n\t"
"rjmp Contadores \n\t"
"CS3: \n\t"
"ldi r22, 3 \n\t" // exibir dígito da dezena
"call ExibeDigito \n\t"
"rjmp Contadores \n\t"
"CS4: \n\t"
"ldi r22, 4 \n\t" // exibir dígito da unidade
"call ExibeDigito \n\t"
"clr r18 \n\t"
"sts 911, r18 \n\t" // zera o valor do contador secundário
"rjmp Contadores \n\t"
"nop \n\t"
"Contadores: \n\t"
"mov r24, r25 \n\t"
"call RotinaContadores \n\t"
"nop \n\t"
"nop \n\t"
"rjmp loop \n\t" // volta para o inicio do loop infinito
// função que converte o valor binário dos segundos para dezenas e unidades
"ConverteBinDecimal: \n\t"
"push r23 \n\t"
"cpi r16, 10 \n\t"
"brlo MenorQue10 \n\t"
"cpi r16, 20 \n\t"
"brlo MenorQue20 \n\t"
"cpi r16, 30 \n\t"
"brlo MenorQue30 \n\t"
"cpi r16, 40 \n\t"
"brlo MenorQue40 \n\t"
"cpi r16, 50 \n\t"
"brlo MenorQue50 \n\t"
"cpi r16, 60 \n\t"
"brlo MenorQue60 \n\t"
"ldi r23, 0 \n\t"
"mov r8, r23 \n\t" // guarda valor 0 no registrador das dezenas (r8)
"mov r9, r23 \n\t" // guarda valor 0 no registrador das unidades (r9)
"rjmp FimConverte \n\t"
"MenorQue10: \n\t"
"ldi r23, 0 \n\t"
"mov r8, r23 \n\t" // guarda valor 0 no registrador das dezenas (r8)
"mov r9, r16 \n\t" // guarda valor que resta no registrador das unidades (r9)
"rjmp FimConverte \n\t"
"MenorQue20: \n\t"
"ldi r23, 1 \n\t"
"mov r8, r23 \n\t" // guarda valor 1 no registrador das dezenas (r8)
"subi r16, 10 \n\t" // subtrai dez do valor, para saber a unidade
"mov r9, r16 \n\t" // guarda valor que resta no registrador das unidades (r9)
"rjmp FimConverte \n\t"
"MenorQue30: \n\t"
"ldi r23, 2 \n\t"
"mov r8, r23 \n\t" // guarda valor 2 no registrador das dezenas (r8)
"subi r16, 20 \n\t" // subtrai 20 do valor, para saber a unidade
"mov r9, r16 \n\t" // guarda valor que resta no registrador das unidades (r9)
"rjmp FimConverte \n\t"
"MenorQue40: \n\t"
"ldi r23, 3 \n\t"
"mov r8, r23 \n\t" // guarda valor 3 no registrador das dezenas (r8)
"subi r16, 30 \n\t" // subtrai 30 do valor, para saber a unidade
"mov r9, r16 \n\t" // guarda valor que resta no registrador das unidades (r9)
"rjmp FimConverte \n\t"
"MenorQue50: \n\t"
"ldi r23, 4 \n\t"
"mov r8, r23 \n\t" // guarda valor 4 no registrador das dezenas (r8)
"subi r16, 40 \n\t" // subtrai 40 do valor, para saber a unidade
"mov r9, r16 \n\t" // guarda valor que resta no registrador das unidades (r9)
"rjmp FimConverte \n\t"
"MenorQue60: \n\t"
"ldi r23, 5 \n\t"
"mov r8, r23 \n\t" // guarda valor 5 no registrador das dezenas (r8)
"subi r16, 50 \n\t" // subtrai 50 do valor, para saber a unidade
"mov r9, r16 \n\t" // guarda valor que resta no registrador das unidades (r9)
"rjmp FimConverte \n\t"
"nop \n\t"
"FimConverte: \n\t"
"pop r23 \n\t"
"ret \n\t"
// função que exibe o digito de acordo com a ordem informada em r22
"ExibeDigito: \n\t"
"push r17 \n\t" // manda r17 para a stack
"cpi r22, 1 \n\t"
"breq Dig1 \n\t"
"cpi r22, 2 \n\t"
"breq Dig2 \n\t"
"cpi r22, 3 \n\t"
"breq Dig3 \n\t"
"cpi r22, 4 \n\t"
"breq Dig4 \n\t"
"rjmp FimExibe \n\t"
"nop \n\t"
"Dig1: \n\t"
"sbi 5, 4 \n\t" // Apaga digito das unidades
"nop \n\t"
"ldi r17, 0 \n\t" // Coloca o valor 0 em r17
"call FormataPortaD \n\t" // função que Formata a Porta D de acordo com r17
"nop \n\t"
"cbi 5, 1 \n\t" // Acende digito da milhar
"rjmp FimExibe \n\t"
"Dig2: \n\t"
"sbi 5, 1 \n\t" // Apaga digito da milhar
"nop \n\t"
"ldi r17, 0 \n\t" // Coloca o valor 0 em r17
"call FormataPortaD \n\t" // função que Formata a Porta D de acordo com r17
"nop \n\t"
"cbi 5, 2 \n\t" // Acende digito das centenas
"rjmp FimExibe \n\t"
"nop \n\t"
"Dig3: \n\t"
"sbi 5, 2 \n\t" // Apaga digito das centenas
"nop \n\t"
"mov r17, r8 \n\t" // Coloca o valor da dezena em r17
"call FormataPortaD \n\t" // função que Formata a Porta D de acordo com r17
"nop \n\t"
"cbi 5, 3 \n\t" // Acende digito das dezenas
"rjmp FimExibe \n\t"
"nop \n\t"
"Dig4: \n\t"
"sbi 5, 3 \n\t" // Apaga digito das dezenas
"nop \n\t"
"mov r17, r9 \n\t" // Coloca o valor da unidade em r17
"call FormataPortaD \n\t" // função que Formata a Porta D de acordo com r17
"nop \n\t"
"cbi 5, 4 \n\t" // Acende digito das unidades
"rjmp FimExibe \n\t"
"nop \n\t"
"FimExibe: \n\t"
"pop r17 \n\t" // restaura r17 da stack
"ret \n\t"
// função que Formata a Porta D de acordo com r17:
"FormataPortaD: \n\t"
"push r16 \n\t" // manda r16 para a stack
"cpi r17, 0 \n\t"
"breq Numeral0 \n\t"
"cpi r17, 1 \n\t"
"breq Numeral1 \n\t"
"cpi r17, 2 \n\t"
"breq Numeral2 \n\t"
"cpi r17, 3 \n\t"
"breq Numeral3 \n\t"
"cpi r17, 4 \n\t"
"breq Numeral4 \n\t"
"cpi r17, 5 \n\t"
"breq Numeral5 \n\t"
"cpi r17, 6 \n\t"
"breq Numeral6 \n\t"
"cpi r17, 7 \n\t"
"breq Numeral7 \n\t"
"cpi r17, 8 \n\t"
"breq Numeral8 \n\t"
"cpi r17, 9 \n\t"
"breq Numeral9 \n\t"
"Numeral0: \n\t"
"ldi r16, 0b01111111 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral1: \n\t"
"ldi r16, 0b00001101 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral2: \n\t"
"ldi r16, 0b10110111 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral3: \n\t"
"ldi r16, 0b10011111 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral4: \n\t"
"ldi r16, 0b11001101 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral5: \n\t"
"ldi r16, 0b11011011 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral6: \n\t"
"ldi r16, 0b11111011 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral7: \n\t"
"ldi r16, 0b00001111 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral8: \n\t"
"ldi r16, 0b11111111 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"Numeral9: \n\t"
"ldi r16, 0b11011111 \n\t" // configura os 8 bits
"sts 43, r16 \n\t" //envia 8 bits para a Porta D
"rjmp FimFormata \n\t"
"nop \n\t"
"FimFormata: \n\t"
"pop r16 \n\t" // restaura r16 da stack
"ret \n\t"
// rotina que lida com o contador primário (incrementado pela interrupt do timer) e
// com o contador secundário (incrementado por ela própria)
"RotinaContadores: \n\t"
"push r21 \n\t" // manda r21 para a stack
"push r3 \n\t" // manda r3 para a stack
"lds r21, 1005 \n\t" // carrega o valor no contador primário para r21
"cpi r21, 30 \n\t" //compara com o número 30
"brne Fim \n\t" // branch para o fim da rotina se não for igual a 30
"nop \n\t"
"clr 21 \n\t" // para limpar o contador primário
"sts 1005, r21 \n\t" // zera o valor do contador na memória SRAM
"nop \n\t"
"lds r3, 911 \n\t"
"inc r3 \n\t" // incrementa o contador secundário
"sts 911, r3 \n\t" // atualiza o valor do contador secundário na memória SRAM
"nop \n\t"
"Fim: \n\t"
"nop \n\t"
"pop r3 \n\t" // restaura r3 da stack
"pop r21 \n\t" // restaura r21 da stack
"ret \n\t"
);
}
// ISR do Timer1 COMP A (sem código de prólogo ou epílogo gerado pelo compilador - atributo ISR_NAKED)
ISR(TIMER1_COMPA_vect, ISR_NAKED){
asm volatile(
//"sbi 8, 1 \n\t"
"push r23 \n\t"
"inc r0 \n\t" // incrementa r0 a cada segundo
"mov r23, r0 \n\t" // copia r0 para r23 (Rd > 15)
"cpi r23, 60 \n\t" // se r0 for 60 tem que voltar a ser 0
"brne final \n\t" // pula pro final se r0 não for 60
"clr r0 \n\t" // limpa (zera) r0 se for 60
"final: \n\t"
"pop r23 \n\t"
"reti \n\t" // retorno de interrupção (necessário porque não tem epílogo)
);
}
// ISR do Timer2 COMP A (sem código de prólogo ou epílogo gerado pelo compilador - atributo ISR_NAKED)
ISR(TIMER2_COMPA_vect, ISR_NAKED){
asm volatile(
"push r23 \n\t"
"lds r23, 1005 \n\t"
"inc r23 \n\t" // incrementa o contador primário
"sts 1005, r23 \n\t" // atualiza o valor do contador primário na memória SRAM
"pop r23 \n\t"
"reti \n\t" // retorno de interrupção (necessário porque não tem epílogo)
);
}