Imagine o leitor o seguinte: Temos um programa, chamado bootloader. E além dele, um outro que é o programa do usuário. Lembro-lhe de que os ciclos de desenvolvimentos são extremamente diferentes e independentes. Portanto, são programas independentes. E como o bootloader sabe como chamar o código do usuário?
Ponteiros para funções
Até o presente momento não "chutei o balde". Estou tentando o máximo possivel ficar somente circulando entre o C e o que ele puder me prover! (com uma linha de código em assembler mato esse capitulo - aí fica sem graça!)
Quando eu fiz escola técnica (lá em 1990) sempre ouvi dizer que em C o poder da linguagem está em ponteiros. E mantenho esse princípio. Feliz daquele que souber trabalhar com essa estrutura de dados!
Um ponteiro de funções cria uma referência "callable" (você não vai encontrar descrito assim nos livros). Essa referência é um endereço de memória, o qual eu invoco como se fosse uma função... a sintaxe é assim
void (*user_init)()=0x200;
No caso acima, eu declarei um ponteiro para uma função que não recebe parâmetro algum (void) e também não retorna nada (void). Tendo o seu endereço de inicio a posição 0x200.
Para chamar é a coisa mais simples:
(*user_init)();
Pronto! O programa irá delegar controle para o que estiver em 0x200.
O que o compilador gera? Bom, para isso teremos que analisar o .lst novamente.
Primeiramente, ele irá declarar um segmento para manter o endereço declarado na inicialização:
idata
_boot db 0x00, 0x02, 0x00
Mas no corpo da função MAIN, veja o que ele faz:
_main:
; .line 6; blptf.c (*boot)();
000000 cff2 movff 0xff2, 0xfe5 MOVFF INTCON, POSTDEC1
000002 ffe5
000004 9ef2 bcf 0xf2, 0x7, 0 BCF INTCON, 7
000006 0005 push PUSH
000008 0e26 movlw 0x26 MOVLW LOW(_00107_DS_)
00000a 6efd movwf 0xfd, 0 MOVWF TOSL
00000c 0e00 movlw 0 MOVLW HIGH(_00107_DS_)
00000e 6efe movwf 0xfe, 0 MOVWF TOSH
000010 0e00 movlw 0 MOVLW UPPER(_00107_DS_)
000012 6eff movwf 0xff, 0 MOVWF TOSU
000014 bee4 btfsc 0xe4, 0x7, 0 BTFSC PREINC1, 7
000016 8ef2 bsf 0xf2, 0x7, 0 BSF INTCON, 7
000018 c082 movff 0x82, 0xffb MOVFF (_boot + 2), PCLATU
00001a fffb
00001c c081 movff 0x81, 0xffa MOVFF (_boot + 1), PCLATH
00001e fffa
000020 0100 movlb 0 BANKSEL _boot
000022 5180 movf 0x80, 0, 0x1 MOVF _boot, W, B
000024 6ef9 movwf 0xf9, 0 MOVWF PCL
_00107_DS_:
_00105_DS_:
000026 0012 return 0 RETURN
Marquei a parte do código que nos interessa. Ele lança mão dos registros PCLATU, PCLATH e PCL. Que fazem a transferência de controle (branch) de forma calculada! :)
Matando a técnica
Para um bootloader, isso é muito grande. A solução em C puro é fantástica. Mas não é limpa em termos de código assembler. Como eu disse em uma linha de assembly consigo matar isso:
Em vez de chamar (*user_init)(), basta no lugar colocar a seguinte instrução:
__asm BRA 0x200 __endasm;
E está feito! O código chega a emagrecer 30 bytes!
--//--
Estamos chegando lá. Nesse interim, trabalhei em 3 frentes:
1) criação do bootloader pro Picolino - EM C+ASM (um mínimo de ASM), ocupando uns 400bytes +/-
2) criação do ambiente de desenvolvimento para gerar códigos suporados pelo bootloader do Picolino (será abordado em breve)
3) criação do programa que lê o .HEX do usuário e grava no Picolino usando o bootloader.
--//--
Esse malabarismo todo tem objetivos:
1) mostrar pro leitor o quão é complexo desenvolver uma solução que pode vir a facilitar.
2) ensinar ao leitor, técnicas avançadas de programação.
3) documentar de forma didática a minha saga no desenvolvimento de sistemas micrcontrolados.
Próximos posts vou colocar + circuitos! É para relaxar!
Nenhum comentário:
Postar um comentário