Buscar

Programar para Microcontroladores

Prévia do material em texto

1 
Linguagem para Programar Microcontroladores: 
Assembly, C ou Basic? 
 
 
Introdução: 
 
Num processador (microprocessador ou microcontrolador), o significado de cada 
instrução do conjunto de instruções está embutido ao nível do hardware, nos circuitos, 
mais precisamente no bloco do processador denominado decodificador de instrução. 
Tais instruções são denominadas “opcodes” (códigos operacionais). Apesar das 
instruções poderem ser escritas em código binário, em função da arquitetura dos 
processadores disponíveis, o tamanho mínimo das instruções e dos dados utilizado 
normalmente é de oito bits, o que caracteriza um Byte. A este nível, para facilitar o 
trabalho dos programadores, o código pode ser escrito na notação de dígitos 
hexadecimal (dois dígitos em hexadecimal para cada Byte). Dizemos que códigos 
escritos desta forma são escritos em linguagem de máquina. 
 
Porém, este método de escrever o código é complexo e exige o conhecimento de 
um grande número de códigos operacionais e a criação de programas para 
microcontroladores e microprocessadores pode se tornar uma tarefa desgastante á 
medida que aumenta a complexidade da aplicação desenvolvida. Os primeiros 
dispositivos programáveis tinham seus programas escritos em linguagem de código de 
máquina, os quais eram geralmente inseridos através de um teclado de uma unidade de 
programação, em formato de dígitos hexadecimais. 
 
O trabalho de programação era assim bastante complexo tomando um longo 
tempo dos programadores, o que o tornava também muito oneroso. Aliado a isso, a 
sempre crescente demanda pela programação de sistemas, para facilitar o trabalho de 
escrever código de baixo nível, foi desenvolvido um sistema onde grupos de códigos 
operacionais semelhantes receberam nomes que lembram suas funções e que os 
tornaram muito mais práticos de serem usados. Estes nomes são denominados 
mnemônicos. Isto provocou o surgimento de uma nova forma de programação, dando 
origem à linguagem Assembly, que consiste numa forma de representação dos códigos 
de máquina usando mnemônicos, ou seja, abreviações de termos usuais que descrevem a 
operação efetuada pelo comando do código de máquina. 
 
Um mnemônico é um nome reservado de uma família de códigos operacionais 
que realizam tarefas semelhantes no processador. Os códigos operacionais atuais 
diferem quanto ao tamanho e tipo de operandos que sejam utilizados. 
 
Exemplo: 
 
Opcode Operando Instrução 
(mnemônico) (hexadecimal) 
DEC A 14H --- 
SUBB A, R4 9CH --- 
SUBB A, 0XXH 94H XXH 
 
 2 
Os mnemônicos possibilitam escrever código de um modo muito mais intuitivo e 
sem perda de precisão. Existe uma correlação exata entre o que você escreve com 
mnemônicos e o que você obtém como códigos operacionais acabados. 
 
A linguagem de mnemônicos se chama: ASSEMBLY (repare no Y no final). O 
programa montador, ou seja, o programa que transforma o código escrito na linguagem 
Assembly em linguagem de máquina, substituindo as instruções, variáveis pelos 
códigos binários e endereços de memória correspondente, é que se chama 
ASSEMBLER. 
 
Voltando ao exemplo anterior, escrevemos o mnemônico SUBB A, R4 e o 
assembler transforma SUBB A, R4 na sequência de bits correspondente ao hexadecimal 
9C, ou seja, em 1001 1100. Esta sequência de bits corresponde à instrução embutida nos 
circuitos do microcontrolador Atmel TSC80251 (código operacional) que faz com que o 
dado contido no registrador acumulador seja subtraido do valor contido no registrador 
R4 e do valor do flag de carry, deixando o resultado no acumulador. 
 
Assim, para trabalhar com Assembly é necessário o uso de um programa 
montador (Assembler) que faz a conversão dos mnemônicos em código de máquina. 
Sem dúvida nenhuma, a representação das instruções em Assembly tornou interpretação 
da leitura do programa pelos seres humanos muito mais simples do que se empregarmos 
o código de máquina original, pois os mnemônicos são contrações de palavras básicas 
da língua inglesa. No entanto, ainda ficam pendentes muitos problemas. 
 
Já os compiladores das várias outras linguagens de nível mais alto, como os 
compiladores de linguagem C, por exemplo, fazem a compilação dos programas em 
duas etapas: na primeira etapa, o código fonte escrito em C é transformando para código 
em Assembly e na segunda etapa é feita a montagem, ou seja, é gerado o código binário 
com a ajuda de um Assembler. 
 
A linguagem C foi originalmente desenvolvida para programação de baixo nível 
para sistemas com poucos recursos, “runtime” problemático e que processam operações 
básicas e de baixo nível. 
 
Até o desenvolvimento da linguagem C, não existiam linguagens de 
programação de alto nível adequadas à tarefa de desenvolver os sistemas operacionais, 
ou seja, os programas especiais utilizados para o controle genérico de um computador, e 
também outros softwares de baixo nível. Aos desenvolvedores restava somente 
conformar-se em utilizar o Assembly para a execução destas tarefas. 
 
Foi principalmente a partir das necessidades de reescrita do sistema operacional 
UNIX, que até então era escrito em Assembly, que surgiu a linguagem C. De fato, a 
implementação da linguagem é tão poderosa que a linguagem C foi escolhida para o 
desenvolvimento de outros sistemas operacionais como o Windows e o Linux. 
 
A linguagem C foi criada em 1972 por Dennis Ritchie, da Bell Laboratories. 
Esta linguagem consiste na realidade em uma linguagem de nível intermediário entre 
o Assembly e as demais linguagens de alto nível, como Pascal, Java, etc. 
 
Mas afinal de contas, qual é a linguagem mais adequada para desenvolvimento 
de projetos de eletrônica envolvendo microcontroladores. Apesar de a resposta parecer 
 3 
bastante óbvia, várias pessoas se perguntam sobre qual linguagem é melhor: C ou 
Assembly. 
 
Essa resposta é dependente da situação: para programas muito pequenos, que 
utilizam apenas funções básicas do microcontrolador, talvez o assembler seja a solução 
mais adequada. Já para programas maiores, que incluem um grande número de projetos 
comerciais, a linguagem C é sem dúvidas a mais adequada. 
 
O primeiro fato a se considerar é que tanto a linguagem de máquina quanto a 
linguagem Assembly, bem como o programa montador, são específicos de cada 
processador. Isso significa que um programa desenvolvido em Assembly para um 
microcontrolador 8051 não tem portabilidade para outro microcontrolador, por 
exemplo, o PIC. Para utilizar-se um programa desenvolvido em linguagem Assembly de 
um processador, em outro diferente, o programador deve executar a tradução cabal do 
programa para a linguagem Assembly do processador de destino. Isso equivale a dizer 
que Assembly não é propriamente dita uma linguagem, mas sim um conjunto de muitos 
dialetos. Quem aprende uma linguagem Assembly, aprende a linguagem Assembly de 
um determinado produto (processador). 
 
Outro fato é que linguagem Assembly é de baixo nível, ou seja, ela tem ainda as 
seguintes desvantagens: 
 
• Ela não utiliza uma filosofia de programação estruturada a qual propicia uma 
construção mais simples e clara das aplicações pela organização dos programas 
em módulos ou estruturas independentes entre si (que em linguagem C são 
chamadas de funções), as quais tem o objetivo de realizar uma tarefa específica. 
 
• Ela não possui nenhum comando, instrução ou função que vá além daqueles 
definidos no conjunto de instruções do processador utilizado. Isso implica em 
um trabalho extra para os programadores ao desenvolver rotinas ou operações 
que não fazem parte do conjunto de instruções do processador, produzindo, por 
conseguinte, programas muito extensos e complexos, com um fluxo muitas 
vezes difícil de ser seguido, pois qualquer operação mais complexa que se enseje 
programar na aplicação, esta deve ser traduzida em um conjuntode instruções 
Assembly. 
 
A linguagem de programação C é uma linguagem de uso geral, que permite 
obter um código eficiente, implementar uma programação estruturada e ainda 
disponibiliza um conjunto bastante sofisticado de operadores. A linguagem C, não foi 
desenvolvida para nenhuma área em particular. A sua generalidade, combinada com 
ausência de restrições, torna a linguagem C numa solução de programação eficiente e 
conveniente para uma grande variedade de aplicações de software. Muitas aplicações 
podem ser resolvidas de uma forma mais fácil e eficiente em C, do que nas suas 
linguagens nativas. 
 
C é uma linguagem de programação genérica desenvolvida para ser tão 
eficiente e rápida quanto a linguagem Assembly e tão calcada em filosofia de 
programação estruturada quanto as demais linguagens de alto nível. De fato, 
devido a sua proximidade com o hardware e com o Assembly, a linguagem C é 
considerada como a linguagem mais eficiente atualmente disponível, entendendo-se por 
eficiência como a velocidade com que o compilador traduz um programa em C para o 
 4 
código de máquina bem como o tamanho do pacote de código de máquina gerado na 
compilação, o que é muito relevante devido aos limitados recursos de memória 
normalmente encontrado nos microcontroladores. 
 
A Maioria dos compiladores C respeita o padrão definido para a linguagem C 
pelo ANSI, (American National Standards Institute). Como a linguagem C por si só não 
é capaz de efetuar operações de leitura ou escrita em periféricos, o que requer 
normalmente intervenção de um sistema operativo, estas facilidades são 
disponibilizadas como parte da biblioteca standard, pelo que algumas funções da 
biblioteca e da própria linguagem estão adaptadas para endereçar as particularidades do 
microcontrolador, e que é dedicado a gerar um código para o microcontrolador 
específico de uma forma extremamente rápida e compacta. 
 
 
Algumas comparações entre as duas linguagens: 
 
• Ao programar em assembly o código gerado não irá depender do compilador 
utilizado, pois o mesmo precisará apenas traduzir as instruções digitadas pelo 
programador para o código de máquina a ser gravado no microcontrolador. 
 
• Quando um programa é feito em C o compilador tem um papel fundamental e 
decisivo na eficiência do mesmo. Bons compiladores C para 8051, por 
exemplo, em geral são caros. 
 
• Codificar em assembly é muito mais susceptível a erros que em C. 
 
• Modificar um programa grande em assembly é extremamente mais 
complicado que modificar um programa em C. 
 
• Testar um programa grande em um simulador assembly é em geral mais difícil 
e às vezes quase impossível dependendo da complexidade do hardware 
externo envolvido. 
 
• A maioria dos fabricantes disponibiliza ferramentas e ambientes de 
desenvolvimento considerando que os programas serão feitos em C. 
 
• A maior parte das bibliotecas, funções e material de apoio encontrados na 
internet estão em C. 
 
• Apesar dos compiladores de linguagem C se tornarem cada vez melhores, 
muitas vezes é possível otimizar trechos de códigos escrevendo-os em 
Assembly. 
 
A linguagem assembly é uma linguagem de baixo nível que exige um bom 
conhecimento do hardware no qual pretendemos implementá-la, no caso um 
microcontrolador específico, o que dificulta o reaproveitamento de código, pois 
geralmente um programa feito para um hardware não pode ser utilizado na 
implementação para outro hardware por causa das diferenças de suas arquiteturas. No 
entanto, em aplicações críticas que exigem muito em termos de memória e velocidade 
de execução, nenhuma outra linguagem é tão eficiente quanto um código bem feito em 
Assembly, contudo a complexidade de implementação nessa linguagem de baixo nível é 
elevada se comparada à feita em uma linguagem de nível mais alto, como o C, e 
 5 
dependendo da complexidade do sistema, a utilização do assembly pode se tornar até 
mesmo inviável em termos de tempo de projeto e desenvolvimento e organização do 
código. 
 
Como se pode observar, a linguagem assembler pode ser extremamente 
improdutiva na maioria das situações e geralmente é encarada, fortemente, como um 
método didático e aproveitada neste caso. Ter um razoável conhecimento do que está 
por trás das instruções C durante a elaboração de um programa é sem dúvida um fator 
diferencial para um programador de microcontroladores. 
 
A linguagem C, porém, encapsula a complexidade de implementação de algumas 
funcionalidades em simples funções e diretivas, tais como a conversão A/D e a 
comunicação serial, usadas no presente estudo de caso. A desvantagem desta linguagem 
de alto nível está no consumo de memória RAN e ROM, devido ao encapsulamento do 
código. 
 
De forma semelhante a um montador, um compilador é um programa capaz de 
gerar código de máquina para ser executado por um microcontrolador (ou algum outro 
processador qualquer) a partir do código-fonte escrito por um programador. Há, no 
entanto, claras diferenças entre um compilador e um montador, como mostra a tabela a 
seguir: 
 
Característica Montador Compilador 
Tipo da linguagem do código-fonte Linguagem de montagem (“assembly”) 
Linguagem de alto nível 
(C, Pascal, etc). 
Dependência entre o código-fonte 
e o processador sim não 
Dependência entre o código de 
máquina gerado e o processador sim sim 
Possibilidade de inclusão de 
arquivos no código-fonte sim sim 
Número de instruções de código de 
máquina (opcodes) geradas para 
cada instrução do código-fonte 
Uma ou mais (duas no 
máximo) 
uma ou mais (pode 
passar de 100) 
Existência de “diretivas” (não 
geram código de máquina, mas 
fornecem informações para essa 
geração) 
sim sim 
Custo baixo; geralmente, 
gratuito 
alto; pode chegar a 
centenas de dólares 
Possibilidade de otimização do 
código de máquina gerado 
Não (deve ser gerado já 
otimizado) sim 
Tamanho do código-fonte maior menor 
Tamanho do código de máquina 
gerado menor maior 
Tempo de execução do código de 
máquina gerado menor maior 
Tempo para o desenvolvimento do 
código-fonte maior menor 
Facilidade de alteração do código-
fonte menor maior 
Principais características de um compilador, comparadas com as de um montador. 
 6 
 
As quatro últimas linhas desta tabela praticamente definem a escolha da 
linguagem para o desenvolvimento de um projeto. Quando há restrições para o tamanho 
do código de máquina gerado e/ou para o tempo de execução (o que acontece, muitas 
vezes, em aplicações “embarcadas”, onde os microcontroladores são mais comuns), em 
geral utiliza-se programação em linguagem de montagem. Já quando a preocupação é 
com a redução do tempo de desenvolvimento do código-fonte e com a facilidade de sua 
manutenção, em geral opta-se pela programação em linguagem de alto nível. Não são 
raros os casos em que se utilizam os dois tipos de linguagem. 
 
 
Linguagens Compiladas X Linguagens Interpretadas: 
 
Durante a fase de codificação do programa da aplicação os programadores 
normalmente utilizam ferramentas, as quais são programas de computador especiais, 
denominados geradores de programa, que realizam automaticamente a conversão do 
programa da aplicação (algoritmo) em códigos ou mesmo comandos de determinada 
linguagem e em alguns casos, os mesmos fazem também a tradução do código. Esses 
programas especiais são classificados em duas categorias distintas: 
 
• Compiladores e 
• Interpretadores. 
 
As linguagens interpretadas, como o BASIC e BASIC STAMP, são aquelas em 
que a tradução da linguagem é feita em tempo real, durante a execução do 
programa. Isso significa que as linguagens interpretadas são mais lentas, pois o 
processo de tradução tem de ser feita para cada instrução do programa. 
 
Por sua vez as linguagens compiladas são aquelas em que o processode tradução 
(compilação) é feito previamente e o código gerado pelo compilador pode ser carregado 
na memória e a execução pode ser feita diretamente por esse código. 
 
Note que no caso das linguagens interpretadas, o trabalho de tradução do código-
fonte, além de ter de ser feito em tempo de execução, o que torna a execução lenta, tal 
trabalho deve ser feito pelo próprio processador que irá executar o código gerado, o que 
significa dizer que o programa interpretador BASIC fica carregado consumindo 
recursos de memória e devido a limitação de tais recursos nos microcontroladores isso 
resulta uma enorme diferença, se comparado com as linguagens compiladas. 
 
Existe ainda a questão da otimização do código: nas linguagens interpretadas 
cada comando de alto nível é traduzido em uma respectiva seqüência de comandos de 
baixo nível pelo programa interpretador. Se um mesmo comando for utilizado diversas 
vezes no programa, serão geradas tantas seqüências de comandos de baixo nível quantas 
forem as aparições do comando de alto nível. 
 
Já por sua vez os compiladores, por serem programas altamente complexos são 
também programas volumosos e de modo algum poderiam ser carregados na parca 
memória dos microcontroladores. Os compiladores utilizam-se de uma variedade de 
artifícios para a otimização do código gerado, tanto tendo objetivando a minimização 
do espaço ocupado pelo código, quanto na velocidade de execução do mesmo. 
 
 7 
Outra questão interessante é que em aplicações críticas, nas quais cada 
microssegundo é muito relevante, como rotinas de tratamento de interrupção com 
elevado número de interrupções num curto espaço de tempo, rotinas de manipulação de 
dados como conversões entre bases numéricas, etc, podemos utilizar código Assembly 
dentro do programa em linguagem C, de forma a acelerar ou otimizar ainda mais a 
sua execução. A linguagem Assembly pode ser inserida no programa C em qualquer 
ponto, bastando para isso utilizar as diretivas #asm e #endasm. As variáveis C 
conservam seus nomes na utilização da linguagem Assembly. Que outra linguagem de 
alto nível permite isso? 
 
Além daquilo que já foi exposto, por ser calcado em filosofia de programação 
estruturada, a linguagem C permite construções mais simples e claras dos programas de 
aplicação, o que facilita a criação de programas de maior complexidade, quando 
comparado com outras linguagens as quais não são estruturadas como Assembly ou 
BASIC. 
 
 
Utilizando a Linguagem C para Programar Microcontroladores: 
 
Os compiladores são ferramentas poderosas para a produção de software, por 
traduzirem programas escritos em linguagem de alto nível para programas escritos em 
linguagem de máquina. 
 
Entretanto, a interação da linguagem de alto nível com as operações sobre o 
hardware (operações de entrada e saída), em alguns casos, não são previstas na 
linguagem. Nos programas executados em computadores, as operações de entrada e 
saída são executadas pelo sistema operacional, através de "systems calls". 
 
Nos equipamentos desprovidos de sistema operacional é necessário que o 
programador crie os programas básicos de entrada e saída, escritos usualmente em 
linguagem assembly para que apresentem um bom grau de otimização, minimizando o 
tempo de sua execução. Eles serão utilizados por outros programas, escritos em 
linguagem de alto nível ou mesmo em linguagem assembly, e são comumente 
agrupados em bibliotecas. 
 
Esses programas são escritos na forma de sub-rotinas, e convém altamente que 
sejam respeitados os padrões de passagem de parâmetros e de devolução de resultados 
definidos pelo compilador que se pretende utilizar, visando as suas chamadas dentro de 
programas escritos em linguagem de alto nível. 
 
Esse conjunto de sub-rotinas básicas deve ser anexado ao programa que as 
utilize na fase de ligação/alocação. Cada sub-rotina deve, portanto, ser declarada como 
pública para que sejam satisfeitas as chamadas geradas pelo programa em linguagem de 
alto nível, que a declarará como externa. De forma análoga, pode-se desenvolver rotinas 
em C e chamá-las em programas elaborados em linguagem assembly. 
 
Em C é de extrema importância os tipos de dados. Identifique os diferentes tipos 
de dados utilizados e a correspondência ao número de bits e intervalo de valores que 
podem tomar. 
 
 8 
No processo de compilação também é possível definir o modelo de memória a 
utilizar. Os tipos de modelos de memória são: SMALL, COMPACT e LARGE. A 
utilização do compilador de C, permite a declaração de rotinas de serviço à interrupção, 
o que faz que o programa salte para essa parte do código quando uma interrupção 
ocorre. 
 
 
Características Inerentes dos Compiladores “C”: 
 
O uso de uma linguagem de alto nível no desenvolvimento de programas para 
microprocessadores e microcontroladores requer alguns cuidados adicionais. Mesmo se 
utilizando linguagens padronizadas, como a Linguagem C, não existe um padrão de 
como o código deve ser gerado para cada processador. Compiladores de fabricantes 
diferentes que geram código para um mesmo processador não necessariamente adotam 
as mesmas convenções. Assim, cada compilador apresenta características próprias para 
a geração das instruções em linguagem de máquina, tais como passagem de parâmetros 
para subrotinas, devolução de resultados de funções, tratamento de interrupções, etc. 
Assim, compiladores de fabricantes diferentes, que geram código para um mesmo 
processador, não necessariamente adotam as mesmas convenções. 
 
Além disso, muitos recursos existentes em uma implementação convencional de 
C, utilizada em computadores, nem sempre são encontrados em implementações 
especificas para a geração de código para determinados microprocessadores e 
microcontroladores (por exemplo, 8051, 8080/8085, Z80, 8086/8088, 68000, etc). 
 
Com o advento do microcomputador pessoal (por exemplo, o PC, da IBM e 
sucessores), surgiram compiladores C específicos para essas máquinas, com bibliotecas 
de rotinas de manipulação dos seus periféricos típicos (teclado, vídeo, disco, etc), e 
gerando o programa executável compatível com elas, e que nem sempre permitem que o 
mesmo possa ser utilizado para a geração de programas para sistemas diferentes, 
baseados no mesmo processador (por exemplo, baseados no 8086/8088). Tais sistemas 
costumam apresentar áreas de memória especificas, diferente dos computadores 
pessoais, exigindo compiladores e outras ferramentas de geração de programa, com 
capacidade de alocação de programas para qualquer região de memória escolhida pelo 
programador. 
 
Os manuais específicos de cada compilador devem ser sempre consultados, 
observando-se as restrições existentes, os recursos de biblioteca disponíveis, os 
mecanismos de geração de código e demais convenções adotadas. 
 
 
Compiladores “C” e os Microcontroladores: 
 
Atualmente, a grande maioria dos compiladores “C” para microcontroladores 
não é gratuita. Em muitos casos os custos envolvidos cabem no orçamento das empresas 
que desejam desenvolver seus produtos utilizando um compilador comercial. 
 
Porém para muitos leitores, este “custo” é às vezes inaceitável, pois não cabe em 
seus orçamentos. Apesar da maioria dos fabricantes oferecer seus compiladores em 
versões para testes, eles sempre o fazem com algumas restrições tais como: 
 
 9 
• Limite no tamanho máximo do código gerado; 
• Impossibilidade de gerar código para alguns tipos de microcontroladores; 
• Tempo de uso limitado há alguns meses, entre outras. 
 
Talvez devido a isto, possa-se explicar o uso por muitos da linguagem 
“Assembly”. Esta, geralmente é fornecida de maneira gratuita pelo próprio fabricante do 
microcontrolador. Assim, muitos optam por esta linguagem não por escolha, mas por 
pura falta de alternativa. 
 
Não se deve descarta o aprendizadoda linguagem “assembly” do 
microcontrolador que se deseja utilizar. Ela ainda é, e continuará sendo, a “base” de 
toda programação para microcontroladores. Muitas vezes, em uma determinada parte do 
programa, pode-se necessitar de uma “gerência” maior das ações sobre o 
microcontrolador, por exemplo. E isso só poderá ser obtido se o leitor construir esta 
parte de código em “Assembly” (a maioria dos compiladores “C” aceita a inserção de 
mnemônicos Assembly diretamente no código “C” a ser copilado). 
 
Mas, felizmente, nem tudo está perdido para aquele que não pode adquirir um 
compilador do tipo comercial. Existem versões de compiladores, desenvolvidas por 
programadores “autônomos”, que são distribuídas gratuitamente na Internet. 
 
Claro que o leitor deve ter em mente que se trata, na sua maioria, de 
compiladores em “desenvolvimento”. Muitos ainda estão disponibilizados em versão 
betha, novos implementos são inseridos e antigos “bugs” corrigidos. 
 
Sendo assim, estes compiladores não são indicados para empresas que desejam 
desenvolver seus produtos, até porque estes compiladores não possuem qualquer 
garantia e / ou suporte. Eles são indicados apenas para aficionados, estudantes e 
amantes do “mundo microcontrolado” que desejam desenvolver suas próprias soluções 
sem arcar com os custos de um compilador durante a fase de aprendizado. 
 
 
 Um compilador gratuito: 
 
Na Internet é possível encontrar bons compiladores “C” para os mais variados 
tipos de microcontroladores. No caso eu sugiro que o leitor comece seu aprendizado 
com o compilador SDCC para o 8051. 
 
O SDCC (Small Device C Compiler) é um compilador distribuído gratuitamente 
na Web sob a licença GNU. Para obter este compilador o leitor deverá acessar o link 
http://souceforge.net/project/showfiles.php?group_id=599 e fazer o “download” do 
arquivo “sdcc-2.4.0-setup.exe” presente na pagina indicada. 
 
 
André Luis Lenz 
Técnico de Ensino do NAI – Núcleo de Automação Industrial 
Escola SENAI “Mariano Ferraz” – Vila Leopoldina – São Paulo – SP 
andrellenz@hotmail.com

Outros materiais

Materiais relacionados

Perguntas relacionadas

Materiais recentes

Perguntas Recentes