sexta-feira, 5 de julho de 2013

SDCC: Relocação de Código - PIC (Parte 3)

Ainda na saga de construir um bootloader para o Picolino (http://mmc-zaap.blogspot.com.br/2013/06/picolino-development-board-para-pic_26.html). Hoje vou demonstrar os "internals" de uma técnica um tanto avançada para invocação de rotinas baseados em outros espaços de endereços.

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