quinta-feira, julho 12, 2007
Republicando artigos meus da Sharepedia
Animal
Um exemplo simples (bobo ?) de uso de árvore binária: um jogo de adivinhação de animais. A árvore consiste em vária perguntas do tipo Sim / Não, onde as folhas são animais. O programa vai percorrendo a árvore (fazendo as perguntas e obtendo as respostas). Ao chegar em uma folha, pergunta se este é o animal que o jogador pensou. Se não for, pergunta qual o animal e pede uma pergunta para distinguí-lo da resposta errada. A árvoré é então atualizada com a nova pergunta e o novo animal.
Arquivo
Avisa se esqueceu disco na unidade
CDAlert é um pequeno utilitário que testa se existe disco removível quando é feito shutdown. Ilustra como descobrir as unidades existentes e seus tipos, através da API Win32.
Arquivo
Aviso (Recado tipo Post-It)
Este é um programinha simples para colocar uma mensagem na tela. A mensagem pode ser colocada na linha de comando, chamando o programa sem parâmetro é apresentado um dialogo para digitacao da mensagem. A janela com a mensagem fica por cima das demais janelas e pode ser arrastada. Para fechá-la, basta dar um double click. Ilustra algumas técnicas menos comuns de programação Windows direto com a API.
Arquivo
Desenhando em Diálogo
O objetivo deste artigo é apresentar uma forma de desenhar gráficos em uma caixa de diálogo, utilizando diretamente a API do Windows. O artigo inclui um programa exemplo que mostra um relógio de ponteiros em um diálogo.
Arquivo
Como fazer um Editor Simples
Este exemplo ilustra três formas de criar um editor simples: usando um Edit Box, usando um Rich Edit controle e "na raça". O código está em C e usa apenas o Win32 SDK .
Arquivo
Launch - demo do uso de CreateProcess
Este programa mostra como usar a função CreateProcess da API do Windows para disparar uma aplicação.
Arquivo
Lista Ligada - O Guia dos Mestres
Este artigo explica a teoria e a prática das Listas Ligadas, com atenção particular à implementação em C e C++.
Arquivo
Exemplo de Uso de ListView
Exemplo de uso de ListView com imagens, usando C + API. Nova versão implementa ordenação pelo cabeçalho das colunas. Destaque para os infames dados usados no exemplo!
Arquivo
Screen Savers - "O Gua dos Mestres"
Uma descrição bem detalhada de como funciona e como fazer screen savers. Inclui dois exemplos, um usando a biblioteca Scrnsave.lib e o outro chamando direto as API do Windows.
Arquivo
Simulação de um Computador
Um exemplo (simples) de como simular um computador. O simulador apresenta na tela os registradores e a memória de um computador hipotético. Programas podem ser entrados em linguagem de máquina ou em assembler. Interessante para quem quer aprender um pouco sobre linguagem de máquina, sobre a simulação de computadores ou mesmo sobre programação Windows.
Arquivo
Threads & Sockets: exemplo de uso
Este código mostra como usar threads e sockets para fazer uma comunicação cliente/servidor.
Arquivo
quinta-feira, junho 28, 2007
Controlando um LED com um PIC - Parte III
Montagem do Circuito
Praticamente qualquer tipo de montagem pode ser feito, tomando-se o cuidado de usar um soquete para o PIC, já que ele terá que ser retirado do circuito para ser programado. Para uma montagem de teste, use uma breadboard como a abaixo:

Para uma montagem definitiva, você pode usar uma placa padrão. Existem algumas cujas trilhas seguem o mesmo padrão das breadboards.
Programação do PIC
O modelo de PIC selecionado armazena o programa em memória Flash, o que permite a gravação de forma relativamente simples (inclusive sem retirar o PIC do circuito, o que o nosso projeto não inclui).
De uma forma simplificada, a programação do PIC requer:
- alimentá-lo através dos pinos VDD e VSS, tipicamente 5 e 0 volts.
- colocar uma tensão de programação no pino GP3, tipicamente 13 volts.
- enviar comandos e receber respostas serialmente pelo pino GP0, usando o pino GP1 para sinalizar os bits (clock).
Para isto normalmente é usado um hardware adicional (o programador) que de um lado se conecta ao PIC e de outro a um PC (através de uma porta serial, paralela ou USB). Uma aplicação no PC controla a programação.
Existem vários modelos de programador para o PIC no mercado, com características diversas. Os mais aventureiros podem até projetar o seu próprio gravador a partir das especificações da Microchip.
No meu caso, usei o programador McFlash da Mosaico que permite a programação a partir do próprio MPLAB.
Referências
- PIC12F629/675 Datasheet - http://www.microchip.com/
- Desbravando o PIC - David José de Souza - Editora Érica
terça-feira, junho 26, 2007
Controlando um LED com um PIC - Parte II
Uma vez que o software é bastante simples, vamos desenvolvê-lo em linguagem assembler e usar o ambiente de desenvolvimento MPLab, disponibilizado gratuitamente pela Microchip.
Definido o algorítmo é só pegar o manual de referência do PIC e enfrentar as esquisitices do conjunto de instruções. Alguns pontos a destacar:
- a execução começa sempre no endereço 0, a interrupção desvia sempre para o endereço 4. No endereço 0 foi colocado um GOTO para desviar para o corpo do programa, que fica após o tratamenteo da interrupção.
- é preciso tomar cuidado com o fato dos registradores de controle (SFR) estarem armazenados em dois bancos (BANK0 e BANK1). Antes de acessar um registrador de controle é preciso garantir que o banco correto está selecionado.
- no início da rotina de interrupção é preciso salvar os registradores W e STATUS. Ao final da interrupção estes registradores precisam ser restaurados.
- o PIC não possui instruções de desvio condicional convencionais. As instruções condicionais são sempre na forma de Skip If, na qual a instrução seguinte é pulada se uma determinada condição for verdadeira.
; Configuração do PIC;
- habilita BrownOut Detect;
- desabilita pino MCLR;
- desabilita Watchdog;
- usa o oscilador interno
__CONFIG _BODEN_ON & _MCLRE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT
Uma variável (MODO_LED) é usada armazenar o modo atual de atuação do LED (APAGADO, ACESO ou PISCANDO). Quando for detectado o pressionamento do botão o modo será avançado para o modo seguinte (circularmente).
; Estados do LED
#define APAGADO 0
#define ACESO 1
#define PISCANDO 2
; Loop principal
PRINC
BTFSS MODO_BOTAO, BOTAO_APERT
GOTO PRINC ; aguarda apertar o botão
BCF MODO_BOTAO, BOTAO_APERT ; limpa indicação
MOVF MODO_LED,W
XORLW APAGADO
BTFSC STATUS,Z
GOTO ACENDER
MOVF MODO_LED,W
XORLW ACESO
BTFSC STATUS,Z
GOTO PISCAR
; Estava piscando, vamos apagar
MOVLW APAGADO
MOVWF MODO_LED
BCF LED
GOTO PRINC
; Estava apagado, vamos acender
ACENDER
MOVLW ACESO
MOVWF MODO_LED
BSF LED
GOTO PRINC
; Estava aceso, vamos piscar
PISCAR
MOVLW PISCANDO
MOVWF MODO_LED
GOTO PRINC
O tratamento do botão possui uma pequena dificuldade: quando o botão é apertado ou solto, por alguns instantes o contato abre e fecha rapidamente. Isto é chamado de 'bounce' (algo como 'quicar'). Para evitar que cada uma destas aberturas/fechamentos seja tratada como um aperta/solta do botão, é preciso aguardar que o sinal do botão fique estável por um certo tempo.
Para fazer o LED piscar e executar o 'debounce' do botão, vamos configurar o Timer 0 do PIC para gerar interrupções periodicamente. O valor que adotei foi aproximadamente 50 milisegundos, o que permite um piscar nítido e fazer o debounce simplesmente exigindo duas leituras iguais em interrupções consecutivas.
; Inicia o timer
BANK0
BCF INTCON,T0IF ; limpa a interrupção do timer
MOVLW .256-CNT_TIMER
MOVWF TMR0 ; programa o timer
BSF INTCON,T0IE
BSF INTCON,GIE ; permite interrupções
O piscar do LED fica bastante simples: na interrupção testamos se o modo atual é piscar; se sim invertemos o sinal no pino do LED.
MOVF MODO_LED,W
XORLW PISCANDO
BTFSS STATUS,Z
GOTO TRATA_BOTAO
MOVLW 0x01
XORWF GPIO,F ; pisca o LED
TRATA_BOTAO
A lógica do botão é um pouco mais complicada. São usados três valores lógicos, armazenados em três bits de uma variável (MODO_BOTAO):
- o estado do botão na interrupção anterior, que é atualizado ao final de toda interrupção
- o estado do botão após o debounce, que é atualizado somente quando o estado atual do botão é igual ao estado anterior
- uma indicação de que o botão foi apertado, que é ligado na interrupção quando o estado após o debounce passa de solto para apertado. Esta indicação é limpa no laço principal do programa, após tratar o acionamento da tecla.
; Controles do estado do botão
#define BOTAO_ANT 0x01 ; este bit indica o estado anterior
#define BOTAO_DEB 0x02 ; este bit tem o valor c/ "debounce"
#define BOTAO_APERT 0x04 ; este botão indica que foi detectado
; um pressionmento do botão
TRATA_BOTAO
BTFSC BOTAO ; testa o botão
GOTO SOLTO
; botao apertado
BTFSC MODO_BOTAO,BOTAO_ANT ; testa leitura anterior
GOTO APERT_10
BSF MODO_BOTAO,BOTAO_ANT ; mudou
GOTO FIM_INT
APERT_10 ; igual a vez anterior
BTFSC MODO_BOTAO,BOTAO_DEB
GOTO FIM_INT ; ja estava apertado
BSF MODO_BOTAO,BOTAO_APERT ; apertou agora
BSF MODO_BOTAO,BOTAO_DEB
GOTO FIM_INT
SOLTO ; botao solto
BTFSS MODO_BOTAO,BOTAO_ANT
GOTO SOLTO_10
BCF MODO_BOTAO,BOTAO_ANT ; mudou
GOTO FIM_INT
SOLTO_10 ; igual a vez anterior
BCF MODO_BOTAO,BOTAO_DEB
O fonte completo está aqui
Na próxima parte vamos encerrar esta série, vendo como montar o circuito e as referências para maiores detalhes.
segunda-feira, junho 18, 2007
Controlando um LED com um PIC - Parte I
O Objetivo
O objetivo desta série é mostrar o projeto do hardware e software de um pequeno dispositivo que ilustra como controlar um LED usando um PIC. O dispositivo possui um LED e um botão que será usado para controlar o estado do LED (apagado, piscando ou aceso).
O Projeto de hardware
Para este projeto selecionei um modelo de PIC bastante simples, o 12F675. O modelo 12F629 pode ser usado sem nenhuma alteração e é simples alterar tanto o hardware como o software para outros modelos.
O PIC 12F675 tem as seguintes vantagens para este projeto:
- pode operar com alimentação de 2 a 5.5V, o que simplifica a operação com baterias e pilhas
- disponível em encapsulamento DIP de 8 pinos, o que simplifica a montagem
- possui um oscilador interno de 4MHz, dispensando a conexão de um cristal ou ressonador
- memória Flash para o programa, o que simplifica a gravação e regravação
No datasheet verificamos que podemos operar de 4 a 10 MHz com uma alimentação de 3 a 5.5 V (a operação com tensões entre 2 e 3 Volts requer clock inferior a 4MHz). Minha opção foi operar com o oscilador interno de 4MHz usando uma bateria de 3V (Duracell DL2032 ou equivalente). O positivo da bateria deve ser conectado ao pino 1 (VDD) do PIC e o negativo ao pino 8 (VSS).
O LED e o botão são conectados a pinos de entrada/saída de uso geral, que no 12F675 são qualquer um dos outros 6 pinos (para ser mais preciso, o botão não pode ser ligado ao pino 4 pois vou usar o pull-up interno que não está disponível no GP3). Escolhi o pino 7 para o LED e o pino 2 para o botão.
O LED, como diz a sigla, é um diodo emissor de luz. Quando submetido a uma tensão direta acima de sua tensão de queda ele emite uma luz com intensidade proporcional à corrente. Existem vários modelos de LEDs, que emitem as mais diversas cores. A tensão de queda é tipicamente de 2V e uma intensidade boa para um LED montado em painel pode ser obtida com uma corrente de 10 mA.
Voltando ao datasheet do PIC, verificamos que um pino de entrada/saída é capaz de gerar ou absorver uma corrente de até 125mA e tem uma tensão de 0,6V (nível zero) ou VDD-0.7V (nível um). A capacidade de corrente do PIC permite ligar um LED diretamente das duas maneiras abaixo:

Na primeira maneira, com o LED ligado entre o pino do PIC e VSS, o valor do resistor em série (conforme a lei de Ohm) deve ser
(VDD - 0.7 - 2,0)/0,01 = 30 ohms
Analogamente, com o LED ligando entre o pino do PIC e VDD, o valor do resistor deve ser
(VDD - 0.6 - 2,0)/0,01 = 40 ohms
No primeiro caso, o LED acende quando o pino do PIS está no nível um, no segundo quando está no nível zero. No meu circuito adotei a primeira maneira com um resistor de 33 ohms.
Para a ligação do botão poderia ser usado uma forma semelhante às vistas para o LED. Olhando mais uma vez o datasheet, o PIC considera nível zero um valor abaixo de 0.15*VDD (0.45V) e nível um um valor acima de 0.25*VDD+0,8 (1,55V). Poderíamos calcular a partir destes dados valores apropriados para o resistor em série com o botão que garantam os níveis apropriados com um valor reduzido de corrente.
Entretanto, a Microchip já fez estes cálculos e disponibiliza internamente ao PIC um resistor de weak pull-up, que faz com que um pino aberto seja lido como em nível um. Desta forma, o botão pode ser ligado diretamente ao VSS e erá lido como nível zero quando fechado e como nível um quando aberto.
A lista de componentes para o circuito fica sendo:
- 1 PIC 12F675 (ou 12F629)
- 1 LED
- 1 Botão de contato momentâneo
- 1 Resistor de 33 Ohms 1/8 W
- 1 Bateria de 3V
- 1 Suporte para a bateria

No próximo post da série vamos ver o software.
segunda-feira, junho 11, 2007
Construindo um Compilador - Parte 4
A estrutura principal para realizar a análise sintática representa o grafo sintático e é um vetor de estruturas que armazenam os nós. Exemplificando em C:
typedef struct
{
int simb; // código do símbolo
int fTerm; // TRUE se simbolo terminal, FALSe se não-terminal
int alt; // índice do nó alternativo (-1 se não tiver)
int seg; // índice do nó seguinte (-1 se não tiver)
} NO;
Um segundo vetor armazena o índice do primeiro nó de cada não-terminal:
typedef struct
{
char *nome; // nome do não terminal (para debug)
int prim; // índice do primeiro nó
} NT;
Desta forma, quando queremos reconhecer um não-terminal, partimos do primeiro nó e vemos se ele corresponde ao item léxico atual. Se sim, obtemos o próximo item léxico e passamos ao nó seguinte. Se for diferente, vamos examinar o nó alternativo.
Estas tabelas podem ser constantes dentro do programa ou carregadas dinamicamente a partir de um arquivo. O trecho abaixo corresponde ao nosso exemplo da parte anterior:
// códigos dos terminais
#define T_VAZIO 0
#define T_NUM 1
#define T_MUL 2
#define T_DIV 3
#define T_SOMA 4
#define T_SUB 5
#define T_ABRE 6
#define T_FECHA 7
// códigos dos não-terminais
#define NT_FATOR 0
#define NT_TERMO 1
#define NT_EXPR 2
NT TabNaoTerm[] =
{
{ "Fator", 0 },
{ "Termo", 4 },
{ "Expr", 8 }
}
NO GrafoSint[] =
{
/* simb fTerm alt seg */
/* 0 */ { T_NUM, FALSE, 1, -1 },
/* 1 */ { T_ABRE, TRUE, -1, 2 },
/* 2 */ { NT_EXPR, FALSE, -1, 3 },
/* 3 */ { T_ABRE, TRUE, -1, -1 },
/* 4 */ { NT_FATOR, FALSE, -1, 5 },
/* 5 */ { T_MUL, TRUE, 5, 4 },
/* 6 */ { T_DIV, TRUE, 6, 4 },
/* 7 */ { T_VAZIO, TRUE, -1, -1 },
/* 8 */ { NT_TERMO, FALSE, -1, 9 },
/* 9 */ { T_SOMA, TRUE, 10, 8 },
/* 10 */ { T_SUB, TRUE, 11, 8 },
/* 11 */ { T_VAZIO, TRUE, -1, -1 },
}
O terminal T_VAZIO indica a situação em que o item léxico atual não precisa ser consumido para o reconhecimento.
O algorítmo do analisador sintático fica assim:
#define TAM_PILHA
int pilha[TAM_PILHA];
int topo = 0;
// retorna TRUE se sucesso, FALSE se encontrou erro
int AnalisadorSintatico ()
{
int item; // item léxico atual
int no; // no atual
// Nosso objetivo é uma expressão
no = TabNaoTerm [NT_EXPR].prim;
// le o primeiro item léxico
item = AnalisadorLexico ();
while (TRUE)
{
if (no != -1)
{
if (GrafoSint[no].fTerm)
{
if (GrafoSint[no].simb == T_VAZIO)
no = GrafoSint[no].seg; // reconheceu vazio
else if (GrafoSint[no].simb == item)
{
no = GrafoSint[no].seg; // reconheceu item léxico
item = AnalisadorLexico ();
}
else if (GrafoSint[no].alt != -1)
no = GrafoSint[no].alt; // não reconheceu mas tem alternativa
else
return FALSE; // sem alternativas: erro sintático
}
else
{
// temos um novo objetivo
if (topo >= TAM_PILHA)
return FALSE; // pilha cheia
pilha[topo++] = no;
no = TabNaoTerm [GrafoSint[no].simb].prim;
}
}
else
{
// reconheceu um não terminal
if (topo > 0)
{
// continua no seguinte
no = pilha[--topo];
no = GrafoSint[no].seg;
}
else
return TRUE; // chegamos ao final
}
}
}
Obviamente estamos fazendo grandes simplicações neste código.
Em primeiro lugar, estamos ignorando o tratamento de erros sintáticos. No mínimo o analisador deveria indicar em que ponto ocorreu o erro. Uma outra informação é a indicação do que era esperado. Por último existe a questão de recuperação do erro, permitindo prosseguir a análise. Embora tudo isto possa ser feito automaticamente a partir do grafo sintático, na prática é preferível programar um heurística específica para a linguagem, considerando quais os erros mais comuns e quais as recuperações que introduzem menos erros espúrios.
Neste algorítmo estamos apenas percorrendo o gráfico e verificando se a entrada obedece à gramática. Um compilador ou interpretador precisa executar ações à medida em que os itens são reconhecidos. Isto pode ser feito acrescentando ao nó uma indicação de qual rotina semântica deve ser executada (esta indicação pode ser um índice ou um ponteiro).
Na próxima parte vamos examinar um pouco mais o tratamento semântico.
sexta-feira, junho 08, 2007
De volta com o post #100 e um balanço
Pois é, este é o centésimo post. Num levantamento rápido, os 99 posts passados envolveram principalmente Programação (27), Livros (22) e Microsoft (21). Como o gráfico abaixo (total de visitas por semana) mostra, tem algumas pessoas passando por aqui, mesmo quando não tem um post novo. As séries sobre gerenciamento de memória e construção de compiladores são as mais populares . Os posts sobre livros também tem os seus hits, principalmente por gente procurando eBooks para baixar.
O número de posts me parece satisfatório, considerando o tipo de posts que eu procuro fazer. De um modo geral, eu evito os posts curtos que tem apenas um link (principalmente para notícias efêmeras). A maioria dos meus posts exige um tempo razoável de pesquisa e escrita e certamente não dá para fazer um por dia.Segue uma lista parcial de idéias para posts (ou série de posts) futuros; aceito sugestões nos comentários:
- Concluir a série "Construindo um Compilador" (urgente!)
- Projeto de Hw com PIC (controlando um LED, construindo um velocímetro para bicicleta)
- Gravação de CD usando a interface IMAPI do Windows
- A Reserva de Mercado de Informática
- Memórias (programando em Assembler 8bits e participando do projeto de um clone do IBM-PC na Scopus, desenvolvendo um software de comunicação de dados, etc)
- Meus primeiros computadores (TK-82C e o Apple II)
segunda-feira, maio 07, 2007
This may take a few minutes...
Venho usando-o com bastante sucesso, até que fui instalar em um Grande Cliente. Tudo ia bem até que instalação deu uma parada no meio. Dando uma olhada no log (o que é fácil, basta clicar em um link na tela do instalador) deu para ver que parecia estar todando muito devagar. Após cerca de uma hora, cancelei a instalação. Tentei novamente mudando alguns parâmetros e aconteceu a mesma coisa. Após várias tentativas (inclusive baixando novamente o instalador), jogamos a toalha e pusemos a culpa no sistema operacional. Depois de um mês (Grande Cliente é assim mesmo) tentamos de novo e novamente "parou".
Felizmente, nesta altura a Microsoft já tinha publicado um fix. Veja os detalhes aqui:
http://support.microsoft.com/kb/910070/en-us
SYMPTOMS
The Microsoft SQL Server 2005 Setup program may take a very long time to be completed. Sometimes this time will be more than 24 hours.
Obviamente é algo raro (a explicação está no artigo). A Microsoft disponibiliza um hotfix (um remendo que você precisa pedir para eles para receber), um workaround editando o registry e uma quebra galho muito simples: desligue o cabo de rede antes de instalar (e ligue de novo ao final da instalação).
Pois é, foi só soltar o cabo de rede do micro (só complicado porque era um servidor de teste que estava em outro local).
segunda-feira, abril 16, 2007
Comprando OnLine com Cartão de Crédito
O problema que tive foi um problema de usabilidade, não de segurança. A coisa começou meio estranha quando a loja virtual criou um "pré-pedido" sem perguntar sobre a forma de pagamento. No e-mail de confirmação veio um "opção de pagamento disponível em nossa página 24h após o recebimento desta confirmação". Lendo nas entrelinhas, o pedido colocado na loja é conferido (particularmente preços e estoque) antes de permitir o pagamento. Talvez seja até mais seguro, mas é diferente do que se vê na maioria das lojas virtuais.
Algumas horas depois, veio o aviso que o "status do seu pedido mudou para aguardando pagamento". É um erro clássico (no qual também caio com frequência): falar com o usuário como se estivesse falando para o compilador. De qualquer forma, quer dizer que deu tudo certo com o pré-pedido e você pode voltar ao site para escolher a forma de pagamento.
Escolhi cartão de crédito Mastercard. O pagamento em si não é feito pela loja virtual, mas sim por um site padrão de pagamento; quando clico em prosseguir aparece o aviso de popup bloqueado. Volto atrás, habilito popups para o site. Aparece o formulário para entrar com os dados, devidamente desformatado no Firefox. Entre com os dados e ... tenta abrir um novo popup (que é bloqueado, pois agora é um outro domínio). Beco sem saída, será que o cartão foi debitado? Dica para os programadores Web: a maioria dos usuários usa bloqueador de popup, os browsers mais recentes saem com o bloqueador ligado. Se possível, não use popup. Se for usar, avise ao usuário para desbloquear e coloque um link para abrir a janela se o popup for bloqueado. Tem muita gente que faz isto direito.
Consulto o cartão de crédito e parece que não foi debitado. Resolvo arriscar novamente, usando o IE com o bloqueio de popup desligado. As janelas abrem, a formatação está correta. E no final um aviso de "erro no processamento". E novo beco sem saída.
Aguardo mais um pouco e decido tentar com um cartão Visa. O site para entrar com os dados é outro. As coisas vão melhor até que aparece dois botões em branco. Clico em um deles e aparece um message box de confirmação, também com o texto em branco. Sim ou Não. Sim... e acabo de desistir! Repito tudo e clico no outro botão. Aparece uma mensagem dizendo sucesso e um número de confirmação. Mais nada. Numa das primeiras telas aparecia um comprovante bonitinho com o impresso nos PDVs; será que custava muito apresentar de novo no final, para imprimir junto com o número de confirmação?
De qualquer forma, o pagamento parece ter sido feito correto e sem duplicidade. Mas a experiência foi terrível!
Obs.: Continuo na correria, ainda não consegui escrever a próxima parte da construção de compiladores, nem retomar uns outros textos maiores que eu comecei há muito tempo atrás. Só escrevi isto porque precisava desabafar!
segunda-feira, abril 09, 2007
A Dura Vida do Desenvolvedor para Windows CE
Em 1994 a Microsoft iniciou o desenvolvimento de um novo sistema operacional de 32 bits, destinado a sistemas dedicados e/ou embarcados. Dadas as restrições de memória e capacidade de processamento destes sistemas, foi adotado um subconjunto da API Win32, reduzindo a funcionalidade e removendo funções redundantes. Embora tenham sido aproveitadas as APIs e idéias dos outros dois Windows de 32 bits da Microsoft (Windows 95 e NT), o código fonte do Windows CE é em grande parte novo. Como o NT, O Windows CE foi projetado para suportar diversos modelos de processadores.
Uma característica importante do Windows CE é a modularidade. Um fabricante de hardware pode selecionar os módulos que necessita (usando uma ferramenta gráfica, o Plataform Builder) e gerar uma versão do Windows CE contendo apenas os recursos que necessita. Neste processo o fabricante acrescenta os drivers e aplicações específicas ao seu equipamento. Uma das aplicações importantes a serem acrescentadas é o Shell que é o módulo responsável pela interface com o operador (por exemplo, apresentação do desktop para os dispositivos que o tiverem).
Ao contrário das demais versões de Windows, a Microsoft não comercializa o Windows CE diretamente para usuários. A Microsoft licencia o Windows CE para os fabricantes que o disponibilizam para os usuários.
Palm-Sized PC, Pocket PC e Windows Mobile
A própria Microsoft gera versões específicas do Windows CE. Basicamente, a Microsoft define os requisitos da plataforma de hardware (em alguns casos bastante específicos), desenvolve alguns drivers e aplicações (notamente o Shell) e licencia o resultado para os fabricantes de hardware. Os times que geram estas versões específicas são times separados dos que desenvolvem o sistema operacional.
Uma destas versões foi o Palm-sized PC, baseado no Windows CE 2.1. Esta foi a primeira experiência da Microsoft em projetar um PDA. O resultado não foi muito animador: a interface com o usuário era desconfortável e o desempenho irritante no hardware disponível na época.
A plataforma PDA seguinte veio em 2000 com um novo nome: PocketPC (talvez devido às pressões da Palm contra o nome anterior). Baseado no Windows CE 3.0 e com uma especificação de hardware mais recheada de MHz e Megabytes, permitiu à Microsoft fincar o pé definitivamente no mercado de PDAs.
A versão seguinte foi o Pocket PC 2002 (a versão original do Pocket PC foi retroativamente denominada de Pocket PC 2000), ainda baseada no Windows CE 3.0.
O nome foi alterado novamente na versão seguinte, que foi o Windows Mobile 2003, baseada no Windows CE .Net 4.0 (sim, o nome do sistema também mudou). A ele se seguiram o Windows Mobile 2003 Second Edition e o Windows Mobile 5.0.
Vários fabricantes fabricaram e fabricam para o "mercado de consumo" PDAs baseados nestes padrões. Estes fabricantes costumam lançar com frequência novos modelos. Uma pergunta comum dos compradores destes equipamentos é sobre a atualização do sistema operacional. Como vimos, os fabricantes é que tem a responsabilidade de gerar e disponibilizar estas novas versões, o que normalmente não preferem não fazer.
Um outro mercado que usa estes padrões é o de coletores de dados. Neste caso estamos falando de equipamentos mais robustos e caros (às vezes dez vezes mais caros que um PDA) e que são produzidos com volumes muito menores. Por estes motivo, não é incomum os fabricantes comercializarem a atualização do sistema operacional. Um outro ponto é que as exigências de hardware destes padrões são às vezes rigorosas demais para o mercado de coletores de dados (por exemplo, o Pocket PC exige um microfone e um botão para a função de gravação de voz). Em alguns modelos os fabricantes de coletores preferem fugir destes padrões e gerar a sua própria versão do Windows CE. Mesmo nos modelos compatíveis com os padrões às vezes é oferecida uma opção de menor custo baseada em uma versão customizada do Windows CE.
Desenvolvendo Para o Windows CE
A primeira dificuldade já deve estar óbvia: conviver com uma nomenclatura mutante e confusa.
O segundo ponto é que cada uma destas plataformas tem um SDK próprio. Particularmente no caso dos coletores de dados, existem diferenças de fabricante para fabricante e os modelos antigos (dada a sua robustez) se recusam a morrer. No caso dos coletores baseados em um padrão da Microsoft, normalmente é necessário instalar primeiro o SDK da Microsoft e depois o SDK do fabricante.
Algumas pessoas pensam que sendo o Windows CE um Windows, deve dar para executar os mesmos programas que no desktop. Como vimos, a API do Windows CE é diferente da Win32 do desktop. Na maioria dos casos, o processador é diferente. A memória disponível é muito menor e não existe disco. E, talvez o mais importante, a forma de operação é completamente diferente.
Inicialmente a Microsoft vendia Toolkits para o Visual C++ e Visual Basic (versões 5 e 6) para o desenvolvimento para o CE. No caso do VB, a linguagem era semelhante ao VBA e não ao VB propriamente dito.
Junto com o Pocket PC, a Microsoft lançou o eMbedded Visual Tools, composto pelo eMbedded Visual C++ (eVC) e do eMbedded Visual Basic (eVB). Estes produtos são gratuitos e sairam direto na versão 3. O eVC é bastante completo, incluindo os frameworks ATL e MFC. O eVB continuou sendo baseado no VBA.
Com o Pocket PC 2002 foi lançado o eVC 4 (o eVB morreu na versão 3 mesmo). Os SDKs anteriores continuavam exigindo o eVC3. É possível instalar na mesma máquina as duas versões do eVC, porém o ícone do programa é o mesmo e o Open With... se recusa a distinguir as duas.
Com o Windows CE .Net passou a estar disponível o Compact Framework para desenvolvimento usando C# e VB.Net. Entretanto, isto não ficou pronto a tempo para inclusão no Visual Studio .Net 2002 e acabou sendo disponilizado como um download gratuito. O Visual Studio .Net 2003 incluiu isto (da mesma forma que incluiu outras funcionalidades e correções de bugs que não ficaram prontas em 2002) e o download para o VS 2002 sumiu.
O Visual Studio 2005 absorveu a funcionalidade do eVC. E chegamos assim à data atual, quando resolvo fazer meu primeiro projeto em C para CE no VS 2005. Seleciono Smart Sevice Win32 Project, informo o nome do projeto e ... nada. Discretamente na linha de status aparece a mensagem "project creation failed". Vários minutos no Google e localizo a solução mágica, que reproduzo abaixo, em:
http://blogs.msdn.com/jeffabraham/archive/2007/02/13/are-you-having-issues-creating-native-projects.aspx
1) [opcional] Acenda uma vela vermelha de cada lado do micro. Sacrifique uma galinha preta sobre o teclado.
2) Feche o Visual Studio.
3) Chame o RegEdit e navege para
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Ext\PreApproved
4) Crie uma nova chave com o nome
{D245F352-3F45-4516-B1E6-04608DA126CC}
5) Feche o RegEdit e rode novamente o Visual Studio
É um problema conhecido que ocorre quando se faz upgrade para o Vista ou se instala o Internet Explorer 7. A solução está incorporada no Service Pack 1 do VS 2005.
É mole?
terça-feira, março 27, 2007
Bill Gates
Da leitura destes livros me veio uma imagem de Bill Gates diferente daquilo que a media normalmente apresenta. Sem dúvida ele tem grande conhecimento técnico e é um grande programador. Também é inegável o seu faro em reconhecer oportunidades. Entretanto, tudo isso me parece secundário frente ao que considero ter sido o principal motivador de Bill: fechar o negócio. Para mim é esta agressividade comercial a principal marca da Microsoft, pelo menos até o ano 2000. Agressividade esta que às vezes encrencou a Microsoft e que levou a práticas algumas vezes consideradas ilegais pela justiça americana.
Relato abaixo duas situações famosas, descritas em detalhes nos livros que citei, e que acredito reforçarem a minha opinião sobre Bill Gates.
O Surgimento da Microsoft
A maioria das pessoas já ouviu falar de como Paul Allen foi correndo mostrar para Bill Gates a edição de janeiro de 1975 da revista Popular Electronics que trazia na capa o Altair 8800 da MITS e neste momento os dois decidiram fazer um interpretador BASIC para ele. Muitos talvez saibam que Bill Gates ofereceu o produto para a Altair, apesar de não ter começado o desenvolvimento, e como a primeira versão foi construída rapidamente. O que poucos sabem é que este foi um mau negócio na ponta do lápis.
O Altair 8800 que aparecia na capa era um mockup de papelão (o protótipo, que não incluia ainda o famoso barramento S-100, foi perdido no transporte). Para aparecer na capa da revista, Ed Roberts (o dono da MITS) chutou um preço bem baixo e corria o risco de ter prejuizo. A solução era lucrar nos acessórios e o principal deles era a placa de memória. O kit original vinha com apenas 256 bytes de Ram, quem queria fazer algo sério precisava da placa de 4K e do BASIC da Microsoft. A placa de memória tinha uma margem imensa e um imenso problema: não funcionava. Não demorou muito para alguém se cansar da placa de memória da MITS, projetar uma e descobrir que dava para vender uma placa que funcionava por um preço bem inferior. Ed Roberts resolveu apelar para o seu trunfo: o BASIC. A MITS tinha um contrato de exclusividade do BASIC da Microsoft para processadores 8080 e compatíveis e Ed Roberts passou a vender o BASIC por $150 para quem comprasse a placa da MITS e $500 para os outros. A reação dos clientes foi a pirataria do BAISC, que resultou em uma famosa "Carta Aberta aos Hobistas" do Bill Gates.
A exclusividade da MITS estava prejudicando a Microsoft com mais que as baixas vendas do BASIC. Embora a Microsoft tivesse fechado acordo com outros fabricantes que usavam outros processadores, o primeiro grande negócio em vista era com a Tandy/Radio Shack cujo TRS-80 usava o Z80 que estava coberto pela exclusividade da MITS. Em meados de 1977 a Microsoft estava em dificuldades financeiras. A sorte da Microsoft foi que Ed Roberts tinha negligenciado itens básicos do acordo, inclusive não pagando os royalties combinados. Com base nisto a Microsoft conseguiu cancelar o acordo na justiça e fechar o negócio com a Tandy e equilibrar as contas.
O Sistema Operacional do PC-IBM
Quando a IBM resolveu desenvolver o seu computador pessoal, ela decidiu usar componentes de mercado, inclusive o BASIC da Microsoft e o sistema operacional CP/M da Digital Research. Como todos sabem o PC-IBM acabou sendo lançado com um sistema operacional da Microsoft, o MS-DOS.
Até o final de sua vida, Gary Kildall (dono da Digital Research) teve que agüentar a versão de que ele tinha ido passear em seu avião ao invés de atender a IBM. Na verdade, Kildall cuidava mais da parte técnica da Digital Research, quem cuidava dos contratos com os fabricantes de hardware era a sua esposa, Dorothy.
No dia da visita da IBM, Gary foi cumprir uma visita agendada anteriormente com outro cliente (pilotando realmente o seu avião) e Dorothy atendeu os representantes da IBM. Antes de dizer qualquer coisa, um advogado da IBM solicitou que ela assinasse um NDA (Non-Disclosure Agreement). Dorothy fez aquilo que todos nós sabemos ser o certo antes de assinar um documento: leu-o cuidadosamente. O documento era terrivelmente favorável à IBM. Dorothy fez aquilo que os advogados sempre recomendam: recusou-se a assinar e passou a negociar o texto do contrato. Embora este impasse tenha sido vencido, a relação entre a Digital Research e a IBM nunca foi boa. Também não ajudou nada o fato do CP/M-86 ter sido repetidamente adiado.
Como foi que Bill Gates se comportou quando a IBM entrou em contato com ele? A reação inicial dele foi: "a IBM é uma grande companhia". Ao contrário de Kildall, Gates se envolvia diretamente nas negociações. E não teve dúvidas em cancelar uma reunião com o chairman da Atari para atender a IBM, vestir um terno e gravata e convocar para a reunião um assistente - Steve Balmer. Ao ser apresentado o NDA, Bill Gates deu uma passada de olhos e assinou sem hesitar.
Enquanto que a diferença de cultura entre as empresas era um problema na relação entre a Digital Research e a IBM, Bill Gates se esforçava para agradar a IBM, apesar de tanto a Microsoft como a Digital Research terem um bando de hippies nos seus quadros. Quando, preocupada com os atrasos e os conflitos com a Digital Research, a IBM perguntou a Bill Gates se ele poderia fornecer também um sistema operacional, ele já tinha a resposta na ponta da língua (embora ainda não tivesse o produto).
segunda-feira, março 26, 2007
Construindo um Compilador - Parte 3
Na análise sintática esta forma de operação não é suficiente. O reconhecimento de uma construção sintática requer o reconhecimento de vários trechos, não necessáriamente da esquerda para a direita. Além disso, é comum um item ser definido de forma recursiva. Consideremos este exemplo de linguagem, onde os itens léxicos são num, '*', '/', '+', '-', '(' e ')':
e vamos analisar o seguinte "programa fonte":10 + ( 70 - 4 * 5 )
o reconhecimento dos vários itens léxicos e sintáticos gera um árvore:
A figura acima sugere duas estatégias básicas para a análise sintática:- análise ascendente, onde itens sintáticos mais complexos vão sendo reconhecidos a partir de itens sintáticos mais simples.
- análise descentente, onde se procura identificar itens sintáticos cada ver mais simples.
Considerando a expressão de exemplo, teríamos os seguintes passos iniciais:
- O nosso objetivo inicial é reconhecer uma expressão.
- O primeiro item de uma expressão tem que ser um termo, salvamos na pilha o ponto em que estamos no reconhecimento da expressão e passamos a tentar reconhecer um termo.
- O primeiro item de um termo tem que ser um fator, salvamos na pilha o ponto em que estamos no reconhecimento do termo e passamos a tentar reconhecer um fator.
- Um fator pode começar com um '(' ou com um número. No nosso caso encontramos o número '10' e com isto concluímos o reconhecimento de um fator.
- Retiramos da pilha o nosso objetivo. Estamos reconhecendo um termo, já reconhecemos um fator. As alteranativas seguintes são '*', '/' ou final do reconhecimento. Como o item seguinte é um '+', damos por encerrado o reconhecimento do termo.
- Voltamos assim ao reconhecimento da expressão. Já reconhecemos um termo, as alternativas seguintes são '+', '-' ou fim do reconhecimento.
- Como temos um '+', passamos a reconhecer um novo termo
No próximo post vamos ver em detalhes como se implementa este algorítimo.
quarta-feira, março 14, 2007
Três Livros Sobre C#
Pro C# 2005 and the .NET 2.0 Platform, Third Edition
Andrew Troelsen
Um livro bastante ambicioso, que tenta descrever a linguagem C#, a plataforma .Net e o framework .Net (e ainda encontra espaço para falar de conceitos gerais sobre programação orientada a objetos). É um livro imenso (ainda estou no primeiro terço), mas que gostaria de ter lido quando comecei a aprender C#. É claro que alguns assuntos como Windows Forms, ASP.Net, ADO.Net e Web Services ficam um pouco corridos (afinal existem livros inteiros sobre estes assuntos), mas é um dos poucos livros que explica bem o básico (no sentido de fundamento não de simples).
ASP.NET 2.0 Website Programming
Marco Bellinaso
A idéia do livro é ótima: descrever a construção de um site real do começo ao fim. Cada capítulo possui três partes: o problema, o projeto e a solução. Na primeira são descritos os requisitos, na segunda são discutidas as alternativas para atendê-los e na terceira é apresentado com detalhes a implementação adotada. O código todo está disponível para download (inclusive foi adotado como um dos exemplos pela Microsoft, veja aqui) e o site está on-line em http://www.dotnet2themax.com/thebeerhouse/.
O autor não foge de assuntos complexos nem economiza linhas de código e utiliza os recursos mais recentes do C# e do ASP.Net. O único porém é a diagramação do livro que deixa a leitura cansativa.
Altamente recomendado para quem desenvolver uma aplicação Web mais real que os Next/Next/Finish dos wizards do Visual Studio.
Visual C# 2005 - A Developer´s Notebook
Jesse Liberty
Este é um livro curto, que se concentra em algumas poucas (e importantes) novidades do C# 2.0 e Visual Studio 2005 e se destaca pela parte visual (imita um caderno com direito a um quadriculado de fundo e manchas de copo). O texto é muito bom, mas nem sempre muito profundo ou completo. Resumindo, é um livro extremamente agradável de ler, voltado para quem já conhece as versões anteriores do C# e quer se atualizar.
PS: Para quem está aguardando a terceira parte da série sobre Compiladores, peço um pouco de paciência, pois as coisas estão um pouco corridas.
quarta-feira, março 07, 2007
Livro do Mês - Montenegro
Deste forma, resolvi comprar na Siciliano este livro junto com outros para dar de presente aos sobrinhos. Descobri também nas buscas a existência de um outro livro sobre o mesmo assunto: "Montenegro - As aventuras do marechal que fez uma revolução nos céus do Brasil" de Fernando Morais (autor de Olga e Chatô, que eu já tinha ouvido falar mas ainda não li). Resolvi surpreender meu pai e comprar os dois livros, o que me dava como vantagem adicional uma segurança extra no caso do livro não chegar a tempo para o Natal.
E realmente o livro não veio. Como desconfiei desde o começo, o prazo de 6 dias significava que o livro não estava em estoque e tinha que ser encomendado à editora. Por motivos que desconheço, a editora não entregou o livro à Siciliano (que me manteve informado sobre o atraso e desde o começo deu a opção de cancelar o pedido). A Livraria Cultura teve mais sucesso e o livro chegou até a ser objeto da "entrega foguete" (entrega no mesmo dia para São Paulo). Em meados de fevereiro resolvi entregar os pontos e cancelar o pedido na Siciliano e comprar da Cultura. Neste ponto a Cultura estava indicando prazo de 3 dias e poucas unidades em estoque (no momento está com prazo de 15 dias úteis). No lugar da entrega foguete as opções de frete eram expresso e normal. A entrega normal tinha um preço baixo (R$ 3) e um prazo razoável (3 dias) e foi minha opção. O livro chegou em dois dias via Sedex, de Porto Alegre (onde a Cultura tem uma loja).
Folheando o livro antes de entregar para o meu pai fiquei com a impressão de ser inferior ao do Fernando Morais, o que veio a ser confirmado pelo meu pai. Curioso sobre o assunto do livro, peguei emprestado o Montenegro do Fernando Marais para ler e é ele o "livro do mês" de fevereiro.
Montenegro - As aventuras do marechal que fez uma revolução nos céus do Brasil
O livro é a biografia de Casimiro Montenegro Filho, um cearense nascido em 1904 e foi o principal responsável pela criação do ITA (origem do interesse do meu pai).
O primeiro sentimento que me veio lendo o livro foi o meu grande desconhecimento da história do Brasil, talvez fruto de ter feito primário e ginásio numa época em que se ensinava que Tiradentes tinha a mesma aparência que Jesus. A história de Montenegro se entrelaça com a história do Brasil, dos acontecimentos que testemunhou na infância até a ditadura militar de 64 que o afastou do ITA, passando pelas revoluções de 30 e 32.
Na minha cabeça, o ITA era uma escola militar e uma fábrica de loucos. Nada sabia das idéias e motivos por trás da sua criação. Embora Montenegro fosse um militar, na sua concepção o ITA era um instituto primordialmente civil. Inspirado no MIT americano, o ITA foi uma tentativa de criação de um instituto de ensino de alto nível, com independência administrativa, para formar profissionais altamente qualificados e viabilizar o surgimento de uma industria aeronáutica nacional. A existência da Embraer é uma prova de que grande parte dos objetivos foram cumpridos (embora a independência nunca tenha vindo por completo e foi sufocada após o golpe de 64).
As dificuldades para a construção do ITA foram imensas. Burocracias, intrigas, invejas e paranoias se somaram à audácia de um idéia extremamente ambiciosa.
É bastante claro o esforço do autor em obter informações precisas sobre os fatos, não hesitando em apresentar múltiplas versões quando as pesquisas foram inconclusivas. O livro é bastante cativante, fazendo o leitor se apaixonar pela causa de Montenegro, passando a vibrar com as conquistas e sofrer com os reveses. Os inúmeros 'causos' relatados oferecem ao mesmo tempo um tom humorístico e uma idéia dos inúmeros contratempos enfrentados.
Na história de Montenegro estão elementos que continuam presentes: as intrigas políticas, a questão da educação, os sonhos de fazer no Brasil coisas no mesmo nível que o Primeiro Mundo.
Leitura altamente recomendada.
terça-feira, março 06, 2007
Fontes do DOS para Download
Uma busca curiosa foi pelos fontes do DOS. Embora até onde eu saiba a Microsoft não publicou os fontes do MS-DOS, quem estiver curioso sobre como fazer um sistema compatível tem pelo menos duas opções:
- FreeDOS: um projeto de software livre, é usado pelo excelente DosBox para simular um PC antigo e permitir rodar programas antigos (particularmente jogos).
- DR-DOS: tem uma história longa e curiosa. A Digital Research era a lider dos sistemas operacionais para os computadores profissionais de 8 bits com o CP/M-80. No lançamento do PC, a IBM deu preferência ao MS-DOS. Após anos tentando sem sucesso vender o CP/M-86, a Digital Research aproveitou um momento em que a Microsoft estava ocupada com os projetos do Windows e do OS/2 para lançar o DR-DOS. Altamente compatível com o MS-DOS, o DR-DOS 5 vinha com utilitários adicionais e utilizava os recursos de gerenciamento de memória do 386. A resposta da Microsoft foi o MS-DOS 5 e uma série de ações que posteriormente foram julgadas anti-competitivas (alguns documentos a respeitos podem ser vistos aqui). A Novell adquiriu a Digital Research e lançou um bundle do DR-DOS 6 com um pacote de rede local peer-to-peer (durante toda a vida do MS-DOS, a comunicação via rede local sempre foi um item a parte e que era explorado por empresas como a Novell e a Lantastic). Apesar de todo este esforço, o DR-DOS nunca conseguiu decolar e entre várias mudanças de dono acabou tendo os fontes liberados para o uso não comercial.
segunda-feira, março 05, 2007
Construindo um Compilador - Parte 2
Itens léxicos típicos são identificadores de variáveis e rotinas, palavras reservadas, constantes e símbolos. Cada um destes itens se caracteriza por uma seqüência de caracteres que segue uma regra simples (por exemplo, "um identificador deve começar por uma letra e possuir somente letras e números"). Isto recomenda o uso de máquinas de estado para o reconhecer os itens léxicos. Toda vez que o analisador léxico é chamado para reconhecer o próximo item léxico, ele parte de um estado inicial e vai mudando de estado a cada caractere examinado, até reconhecer o item ou detectar uma seqüência inválida de caracteres (um "erro léxico").
Por exemplo, vamos considerar um caso simples, em que os itens léxicos são números inteiros decimais e as operações + e -. De uma forma livre, teríamos o seguinte analisador léxico na linguagem C:
estado = INICIAL;
while (estado != FINAL)
{
c = ProximoCaracter();
switch (estado)
{
case INICIAL:
if (c == EOF)
{
item = EOF;
estado = FINAL;
}
else if (isdigit(c))
estado = NUMERO;
else if (c == '+')
{
item = MAIS;
estado = FINAL;
}
else if (c == '-')
{
item = MENOS;
estado = FINAL;
}
else if (!isspace(c))
{
item = ERRO;
estado = FINAL;
}
break;
case NUMERO:
if (c == EOF)
{
item = NUMERO;
estado = FINAL;
}
if (!isdigit(c))
{
DevolveCaracter(c);
item = NUMERO;
estado = FINAL;
}
}
}
Para não estender mais o código, não estamos guardando o valor do número (o que fica como exercício para o leitor). Reparar que eventualmente o código acima examina um caractere além do final do item e precisa "devolvê-lo". Tipicamente um analisador léxico não precisa guardar estados entre uma chamada e outra nem voltar atrás nos seus passos.
Algumas linguagens possuem idiossincrasias que dificultam o analisador léxico. Por exemplo, algumas versões do FORTRAN permitem colocar espaços no meio de identificadores, criando seqüências de difícil análise como:
DO 10 I = 1, 10
Somente ao encontrar a vírgula o analisador léxico descobre que os itens léxicos são <DO> <10> <I> <=> (comando DO) ao invés de <DO10I> <=> (o que seria um comando de atribuição).
O analisador léxico é também responsável por desprezar os comentários nos fontes.
No caso de identificadores e palavras reservadas, a máquina de estado apenas isola a seqüência de caracteres. O analisador léxico precisa também identificar o item.
No caso das palavras reservadas, normalmente existe uma tabela ou lista onde a seqüência isolada deve ser procurada. Uma forma simples, mas ineficiente, é fazer uma busca seqüencial. Uma forma mais inteligente é ordenar previamente a lista e fazer uma busca binária.
Uma forma ainda melhor é usar hashing. No hashing, calcula-se um número (hash) a partir da seqüência de caracteres e usa-se este número como índice para a tabela.
O principal problema com o hashing é que várias seqüências podem gerar o mesmo hash (gerando uma colisão). Ao inserir um item numa tabela de hash, se a posição já estiver ocupada é preciso fazer uma lista ligada na posição para colocar o novo item. Ao fazer uma busca, é preciso fazer uma busca seqüencial nesta lista ligada.
Uma opção para diminuir as colisões é aumentar a faixa de valores para o hash, porém isto leva ao desperdício de memória. No caso das palavras reservadas, a lista é constante, o que permite escolher previamente uma função de hash que evite colisões. Por exemplo, pode-se criar uma função parametrizada e deixar um computador procurando exaustivamente os parâmetros que otimizem a função de hash.
Uma vez determinado que uma seqüência não é uma palavra reservada, o analisador léxico deve tentar descobrir o tipo de identificador. Isto é feito procurando a seqüência em tabelas de nomes de variáveis, funções, etc construída pelo analisador semântico. Estas tabelas podem usar o hashing para otimizar a busca. Um truque simples mas eficaz consiste ao encontrar um item na lista ligada da tabela de hash mover este item para o início da lista. Isto faz com que a lista automaticamente se ordene em função do uso dos itens.
Em algumas linguagens é permitido usar um identificador que ainda não foi declarado e portanto tem tipo ignorado. Isto não afeta o analisador léxico porém complica o analisador sintático e pode obrigar o analisador semântico a segurar a geração do código objeto até que o identificador seja declarado.
Na próxima parte vamos ver o coração do compilador, o analisador sintático.
terça-feira, fevereiro 27, 2007
Construindo um Compilador - Parte 1
Minha primeira experiência em construir um compilador foi na faculdade. Lembro vagamente que era um compilador que gerava um programa para uma calculadora Texas a partir de uma linguagem parecida com Basic.
Recem formado, tive a oportunidade de participar de uma série de cursos na Scopus, não relacionados diretamente a projetos. Um deles foi A Construção de Um Compilador, lecionado pelo professor Valdemar Setzer. O material para este curso foi uma versão preliminar de um livro do próprio Setzer (em conjunto com Inês S Homem de Melo), que posteriormente viria a ser publicado pela Editora Campus.
Vários anos depois, tive a oportunidade de utilizar na prática estes conhecimentos, ao participar da definição e desenvolvimento de uma linguagem de script para o programa de comunicação Zapt da Humana Informática.A forma de implementar um compilador que vou apresentar nestes posts se baseia diretamente no livro do Setzer. Obviamente vou ser bastante superficial nestes posts (o livro tem mais de 300 páginas).
O Que é Um Compilador?
Podemos dizer que um compilador é um programa que traduz um programa escrito em uma linguagem (linguagem fonte) para uma outra linguagem (linguagem objeto). Tipicamente a linguagem fonte é uma linguagem de alto nível e a linguagem objeto é o código de máquina.
Um parente próximo do compilador é o interpretador, que ao invés de gerar o programa em linguagem objeto ele o executa.
Um compilador pode não converter diretamente a linguagem fonte na linguagem objeto, ele pode ser composto de vários passos utilizando linguagens intermediárias. Um exemplo típico são compiladores que usam linguagem Assembly como uma linguagem intermediária.
Definindo Uma Linguagem
Tipicamente a definição de uma linguagem começa com os itens básicos para a escrita de programas: palavras reservadas, símbolos especiais, constantes e identificadores. Estes itens são denominados itens léxicos.
A forma como estes itens léxicos são combinados para gerar declarações, expressões e comandos em geral de uma linguagem é definida pela gramática da linguagem. Existem várias formas de se definir uma linguagem, algumas mais formais (como a notação BNF) e outras menso. Nestes posts vou usar os grafos sintáticos para descrever a gramática.
A definição da linguagem pode facilitar ou complicar a sua compilação. O método que será abordado é mais adequado para lingugens "bem comportadas".Estrutura de Um Compilador
Tradicionalmente se divide o compilador em três grandes partes:
- Analisador Léxico: reconhece no fonte os itens léxicos
- Analisador Sintático: verifica se a sequência de itens léxicos contida no fonte está de acordo com a gramática da linguagem e reconhece as várias construções.
- Analisador Semântico: Gera o código objeto para as construções reconhecidas pala analisador sintático.
quarta-feira, fevereiro 21, 2007
Programação Assembly no ARM - Parte IV
Estas instruções, denominadas de Single Data Transfer, são codificadas conforme a figura abaixo:
Note que embora sejam apenas duas instruções assembly, LDR e STR, existem uma grande quantidade de opções. Como sempre, o opcode contém uma condição. O bit L diferencia a instrução de Load (carga do registrador a partir da memória) da instrução de Store (armazenamento na memória do conteudo do registrador). Rd indica o registrador que receberá o valor lido (LDR) ou que contém o valor a ser armazenado (STR). O bit B indica se estamos manipulando um word (32 bits) ou byte (8 bits); existem instruções separadas para manipular valores de 16 bits (half-words). O resto dos campos do opcode determinam o endereço da posição de memória a ser manipulada.Este endereço é determinado por duas partes: a base (que fica no registrador Rn) e o offset (que, conforme o bit I, pode ser um valor imediato de 12 bits ou o conteúdo de um registrador deslocado). O deslocamento do registrador usado para o offset é determinado de forma semelhante ao usado para o valor imediato nas instruções lógicas e aritméticas. O bit U indica se o offset deve ser somado ou subtraído da base.
O bit P determina se a combinação da base com o offset deve ser feita antes (pré-indexação) ou depois (pós-indexação) do acesso à memória. O bit W permite atualizar o registrador base com o valor final do endereço. Quando se especifica a pós-indexação, o ARM atualiza sempre o registrador base, para não alterá-lo deve-se colocar um offset de zero. Na prática, isto significa:
- P = 0, W = 0: normalmente tratado pelo ARM como P = 0, W = 1. (para ser preciso, no modo privelegiado em um processador com hardware de gerenciamento de memória esta combinação faz com que o endereço seja tratado como um endereço de usuário ao invés de endereço de sistema).
- P = 1, W = 0: combina base e offset para determinar o endereço, o registrador base fica inalterado. Útil, por exemplo, para acessar um item de um vetor (base é o endereço inicial e o offset é o deslocamento em relação ao início).
- P = 0, W = 1: usa somente a base para determinar o endereço, após o acesso à memória a base é atualizada com a combinação da base com o offset. Permite implementar em uma única instrução a construção *p++ do C (acessa a posição apontada pelo registrador base e o avança para o próximo item).
- P = 1, W = 1: combina base e offset para determinar o endereço, o registrador base é atualizado para o endereço usado. Permite implementar em uma única instrução a construção *++p do C (avança o registrador base para o próximo item e o acessa).
LDR{cond}{B} Rd,<endereço>
STR{cond}{B} Rd,<endereço>
Onde B indica que é um acesso a byte.
- uma expressão, que resulta em um endereço. O Assembler tenta montar uma instrução que combine um offset ao PC para obter este endereço, se não conseguir gerará um erro. Esta forma é útil para carregar em registradores constantes que estão armazenadas junto ao código.
- uma especificação de endereçamento pré-indexado (combina base e offset antes de acessar a memória):
- [Rn]
- [Rn,#expressão] {!}
- [Rn,{+/-}Rm{,<shift>}] {!}
- uma especificação de endereçamento pós-indexado (combina base e offset depois de acessar a memória):
- [Rn],#expressão
- [Rn],{+/-}Rm{,
}
nestes casos o registrador base é sempre atualizado.
A presença de ! indica que o registrador base deve ser atualizado (W = 1).
; coloca valores conhecidos em R1 e R2
MOV R1,#0
MOV R2,#4
; coloca em R0 o conteúdo da posição 0 da memória
LDR R0,[R1]
; coloca em R0 o conteúdo da posição 4 da memória
LDR R0,[R1,R2]
; coloca em R0 o conteúdo da posição 32 da memória
LDR R0,[R1,R2,LSL #3]
; idem
LDR R0,[R1+#32]
; coloca em R0 o conteúdo da posição 4 da memória
; R1 passa a ser 4
LDR R0,[R1,R2]!
; coloca em R0 o conteúdo da posição 4 da memória
; R1 passa a ser 8
LDR R0,[R1],R2
Reparar que não é possível especificar diretamente um endereço qualquer da memória, pois o offset imediato tem 12 bits e um endereço tem 32 bits. Além disso, já vimos que as instruções lógicas e aritméticas são limitadas quanto aos valores imediatos que podem ser usados. Para carregar em um registrador um endereço ou constante qualquer, é preciso armazená-lo em memória em uma posição que possa ser acessada por um LDR. Isto torna frequente a presença de constantes no meio do código:
...
LDR Rd,X ; usa endereçamento relativo ao PC
...
X .long valor ; em algum lugar próximo
Para facilitar isto, o Assembler permite a seguinte construção:
LDR Rd,=valor
Se possível, o assembler gera uma instrução MOV ou MVN para carregar o valor no registrador. Se isto não for possível, o assembler coloca a constante em um trecho próximo e gera uma instrução LDR usando endereçamento relativo ao PC.
Outras Instruções de Acesso à Memória
As instruções LDRH, LDRSH e STRH permitem carregar e armazenar valores de 16 bits (half word). A instrução LDRSH faz a "extensão do sinal", isto é, preenche os 16 bits mais significativos do registrador com o bit mais significativo do valor de 16 bits, de forma a manter o sinal do valor. O mesmo opcode permite também carregar em um registrador um valor de 8 bits com extensão de sinal (LDRSB). Estas instruções oferecem os mesmos recursos de endereçamento que LDR e STR.
As instruções LDM e STM (Block Data Transfer) permitem carregar ou armazenar um conjunto de registradores em posições contiguas de memória. A codificação destas instruções é apresentada abaixo:

Register list possui 16 bits, um para cada registrador. Os bits com 1 indicam os registradores a serem transferidos. O endereço inicial é obtido a partir de um registrador, que será automaticamente incrementado ou decrementado, antes ou depois de transferir cada registrador. O registrador utilizado pode ou não ser atualizado ao final da transferência.
A codificação em assembly destas instruções é:
LDM{cond}<fd|ed|fa|ea|ia|ib|da|db> Rn{!},<rlist>
STM{cond}<fd|ed|fa|ea|ia|ib|da|db> Rn{!},<rlist>
Onde
- {cond} é a condição em que a instrução será executada.
- FD, ED,..DB determinam os bits P e U. FD, ED, FA e EA são (respectivamente) idênticos a IA, IB, DA e DB; os primeiros são utilizados para documentar quando os registradores estão sendo transferidos para uma pilha.
- ! indica que o registrador Rn deve ser atualizado ao final
; salva todos os registradores em posições
; consecutivas a partir da apontada por R0
STMIA R0,{R0-R15}
; empilha os registradores (exceto R15/PC)
STMFD SP!,{R0-R15}
; Salva na pilha os registradores R1, R3 e R5
STMFD SP!,{R1, R3, R5}
; desempilha os registradores
LDMED SP!,{R0-R14}
terça-feira, fevereiro 06, 2007
Programação Assembly no ARM - Parte III
A figura abaixo mostra os vários campos do opcode destas instruções:
Na parte II nós já vimos os campos Cond e S. O campo Opcode determina a operação que será executada:
Opcode mne ação
0000 AND Rd := Op1 AND Op2
0001 EOR Rd := Op1 XOR Op2
0010 SUB Rd := Op1 - Op2
0011 RSB Rd := Op2 - Op1
0100 ADD Rd := Op1 + Op2
0101 ADC Rd := Op1 + Op2 + C
0110 SBC Rd := Op1 - Op2 + C - 1
0111 RSC Rd := Op2 - Op1 + C - 1
1000 TST posiciona flags conforme Op1 AND Op2
1001 TEQ posiciona flags conforme Op1 XOR Op2
1010 CMP posiciona flags conforme Op1 - Op2
1011 CMN posiciona flags conforme Op1 + Op2
1100 ORR Rd := Op1 OR Op2
1101 MOV Rd := Op2
1110 BIC Rd := Op1 AND NOT Op2
1111 MVN Rd := NOT Op2
Portanto o campo Rd determina o registrador (R0 a R15) que será o destino do resultado da operação. As instruções TST, TEQ, CMP e CMN ignoram este campo. Como o único resultado destas operações é o posicionamento dos flags, o bit S é sempre forçado pelos assemblers.
Op1 é sempre um registrador e é definido por Rn; as instruções MOV e MVN ignoram o campo Rn.
Existem duas opções para a codificação do campo Operand 2.
1. Se I for 0, o segundo operando é um registrador, cujo conteúdo pode ser deslocado:
Este deslocamento pode de quatro formas:- Lógico para a esquerda (LSL): desloca para a esquerda introduzindo zeros à direita
- Lógico para a direita (LSR): desloca para a direita introduzindo zeros à esquerda
- Aritmético para a direita (ASR): desloca para a direita repetindo o bit mais significativo na esquerda
- Rotação para a direita (ROR): desloca para a direita, introduzindo na esquerda os bits que 'saem' à direita
2. Se I for 1, o segundo operando é um valor imediato (codificado na própria instrução):
O valor de 8 bits (Imm) contido na instrução é expandido para 32 bits colocando-se zeros à esquerda e em seguida rodado para a direita o dobro de vezes do especificado em Rotate. Como Rotate tem 4 bits, isto significa que o valor pode ser rodado 0, 2, 4, 8 .. 30 bits para a direita. Embora esta codificação permita colocar um grande variedade de constantes na instrução, uma quantidade ainda maior não tem como ser codificada. Neste caso o valor precisa ser armazenado em uma posição da memória e carregado para um registrador antes de executar a operação. Por exemplo, as constantes 0, 1, 0x100 e 0x110 podem ser codificadas mas 0x101 não.Vejamos agora como escrever estas instruções na linguagem Assembly.
MOV e MVN
CMP,CMN,TEQ,TST
AND,EOR,SUB,RSB,ADD,ADC,SBC,RSC,ORR,BIC
onde
{cond} é a condição (opcional)
{S} indica que pode ser acrescentado S para a instrução afetar os flags
Rd, Rn, Rm e Rs são os registradores R0 a R15
Quando Op2 for especificado com #expressão, o assembler tentará codificá-lo em um valor imediato. Se não conseguir, acusará erro.
Vejamos se alguns exemplos esclarecem um pouco:
MOV R1,#100 ; coloca 100 em R1
MOVS R1,R0 ; coloca o conteúdo de R0 em R1, atualiza os flags
MOV R1,R0 ASL 5 ; coloca R0 * 32 em R1
MOV R1, R0 ROR R2 ; coloca em R1 o valor de R0 rodado para a direita de R2 bits
ADD R2,R1,R0 ; soma R1 a R0 e coloca o resultado em R2
ADD R1,R1,#1 ; incrementa R1
CMP R1,R2 ; compara R1 a R2, flags posicionados cf R1-R2
TST R1,#0x80000000 ; testa o bit mais significativo de R1
BIC R1,R1,#1 ; zera o bit menos significativo de R1
No próximo post da série vamos ver como acessar dados na memória. Até lá!
sexta-feira, fevereiro 02, 2007
Programação Assembly no ARM - Parte II
O primeiro ponto a reparar é como a codificação é uniforme em todas as instruções. Isto visa simplificar o hardware de decodificação das instruções e faz parte da filosofia RISC.Um segundo ponto interessante é o campo Cond que aparece em todas as instruções. Este campo determina como devem estar os flags para a instrução ser executada. Na maioria dos processadores apenas as instruções de desvio dependem dos flags, no ARM todas as instruções são condicionais! A tabela abaixo mostra as condições disponíveis:
O sufixo na tabela acima deve ser acrescentado ao mnemônico da instrução a ser afetada; o sufixo AL pode ser omnitido (e normalmente é). Este sufixo fica obvio no caso de uma instrução de desvio: B (ou BAL) é um desvio incondicional, BEQ é um desvio se igual. As coisas ficam um pouco mais confusas com outras instruções, por exemplo LDREQ é uma instrução de Load Register que só é executado quando o flag Z estiver ligado.Ainda na codificação das instruções, repare no bit S nas três primeiras linhas. Estas linhas codificam as instruções aritméticas, lógicas e de movimentação. O bit S indica se a instrução vai ou não afetar os flags. No Assembly, isto é feito acrescentando o sufixo S no mnemônico quando se deseja afetar os flags. Por exemplo, uma instrução AND não afeta os flags, mas ANDS afeta (é claro de ANDEQS afeta os flags somente quando for executada, o que só ocorre quando o flag Z está ligado).
E quais são as instruções disponíveis? A figura abaixo tem a lista completa:
Para podermos entender estas instruções é necessário conhecer primeiro como são codificados os operandos, o que veremos no próximo post.
quinta-feira, fevereiro 01, 2007
Programação Assembly no ARM - Parte I
O que é a arquitetura ARM?
Eu diria que a arquitetura ARM (que já foi chamadado de "Advanced RISC Machine" e "Acorn RISC Machine") é uma especificação dos registradores e instruções de máquina de um processador de 32 bits baseado na filosofia RISC (a Wikipedia tem uma definição mais longa).
Na verdade existem várias versões desta especificação, a que vou tratar aqui é chamada de ARM7TDMI. Esta especificação inclui um modo com instruções de 16 bits (Thumb) que vou ignorar nestes posts. A empresa responsável pelo ARM7TDMI licencia a sua implemantação para inclusão em vários microcontroladores, como os da Atmel e ST Microeletronics.
A descrição do ARM7DMI em formato pdf pode ser encontrada aqui.
São características RISC do ARM:
- As instruções são todas codificadas da mesma forma, ocupando sempre 32 bits
- Existem vários registradores e as instruções aceitam qualquer um deles como operando
- A memória é usada como operando somente para movimentação; operações aritméticas e lógicas usam registradores e constantes como operando
- A maioria das instruções são executadas em um único ciclo
Existem inúmeros Assemblers disponíveis para o ARM, utilizando diversas sintaxes. A sintaxe que eu uso aqui é compatível com o GNU Arm Toolchain.
Os registradores do ARM
O ARM possui 16 registradores de 32 bits, denominados simplesmente de R0 a R15. O registrador R15 é sempre usado como ponteiro para a próxima instrução a executar e pode ser referenciado no Assembly como PC.
A função de chamada de subrotina salva o endereço de retorno no registrador R14, também chamado de link register (LR no Assembly).
O registrador R13 é normalmente usado como ponteiro de pilha (SP no Assembly). Entretanto, ao contrário de outros processadores, este uso é apenas convenção.
Por último, existe um registrador de status e flags, o CPSR, que é acessível apenas por instruções especiais.
No próximo post começaremos a ver as instruções.
Comemoração
Agora só falta acertar com a "patroa"...