sábado, fevereiro 07, 2009

DVD Concert for George - Crítica

Em 29 de novembro de 2002, exatamente um ano após a morte de George Harrison, vários dos seus amigos se reuniram para fazer um show em sua homenagem. O show foi registrado em um DVD que eu finalmente adquiri.

Estamos falando de uma das mais incríveis reuniões de ídolos consagrados (se você não conhece algum dos nomes mencionados, é só procurar na Wikipedia). Em um monento, Dahni (filho de George, parecidíssimo com o pai e que participa do show) assustado com os oito guitarristas ao se redor resolve virar para o fundo do palco - e se dá conta que tem quatro bateristas! O que poderia ser um desastre se torna um momento mágico graças à humildade de todos os presentes, que estão completamente maravilhados de simplesmente participarem do show mesmo que discretamente.

O pacote é composto de dois DVDs, um com o show completo e o outro com uma versão editada (misturando o show com entrevistas) e alguns extras.



DVD Um - Concerto na Íntegra

O evento começa com uma invocação indiana, enquanto Olivia (a viúva de George) acende uma pira. Em seguida temos uma breve introdução de Eric Clapton (responsável pela direção musical do show) e de Ravi Shankar.

A primeira parte do show é música indiana: sob o comando de Anoushka Shankar (filha de Ravi e exímia tocadora de cítara), são executadas "Your Eyes" (de Ravi Shnkar), "The Inner Light" (belíssima música de George, vocal de Jeff Lynne cujo cabelo me lembra uma peruca de palhaço) e "Arpan" (composta por Ravi Shankar especialmente para o show e que encerra com um trecho incrível de Eric Clapton no violão).

Em seguida tem um intervalo que pode parecer deslocado do resto. Além da música, George também atuou como produtor de filmes. Entre outros, ele patrocinou o filme "A Vida de Brian" do grupo Monty Python. Daí a apresentação de dois números musicais do Monty Python: "Sit On My Face" e "The Lumberjack Song" (com a participação discreta de Tom Hanks).

E é hora do rock! Começam a subir as feras no palco (sob o nome de "Banda do George"): Eric Clapton, Jeff Lyne (guitarrista fundador da Eletric Light Orchestra, produtor do álbum Cloud 9 de Harrison com quem tocou no Traveling Wilburrys), Gary Brooker (tecladista compositor de "A Whiter Shade of Pale"), Andy Fairweather-Low (guitarrista que costuma acompanhar Eric Clapton e Roger Waters), Ray Cooper (percurcionista com uma incrível presença no palco, excursionou com Eric Clapton, Pink Floyd, Elton John, etc), Albert Lee (guitarrista extraordinário, mas com atuação discreta no show), Marc Mann (guitarrista pouco conhecido, mas que tem uma atuação marcante no show, executando a maioria das partes de slide guitar típicas de George Harrison), Chris Stainton (teclado), Jim Keltner (baterista, famoso pelo seus trabalhos em conjunto com George, Ringo e Jonh Lennon), e Henry Spinetti (um baterista de estúdio que tocou com um monte de gente famosa) e mais dois baixistas, dois saxofonistas e uma dupla de backing vocals (nada menos que Katie Kissoon e Tessa Niles, com currículos extraordinários).

A segunda parte começa com "I Want to Tell You" (de George, vocal de Jeff Lyne). Em seguida temos "If I Needed Someone" (de George, vocal de Eric Clapton), "Old Brown Shoe" (de George, vocal de Garry Broker e destaque para as guitarras de Andy Fairweather-Low e Marc Mann), "Give Me Love (Give Me Peace on Earth)" (uma música delicada de George, vocais de Jeff Lyne e belos trabalhos de slide de Andy Fairweather-Low e Marc Mann) e "Beware Of Darkness" (George, vocal de Eric Clapton).

Em seguida entra no palco Joe Brown (cantor, guitarrista e padrinho de casamento de George), um homenzarrão tocador de violão e ukulele (uma pequena guitarra havaiana) e sua banda, para interpretar as lentas "Here Comes de Sun" e "That's The Way It Goes" (ambas de George).

Joe Brown sai do palco e volta a banda, acrescida de Jim Capaldi (outro baterista famoso, que trabalhou com Jimi Hendrix e Eric Clapton e na banda Traffic e viveu por muitos anos no Brasil), Jools Holland (pianista) e Sam Brown (cantora enérgica, filha de Joe Brown) para executar "Horse To The Water" (de George e Dahni).

A banda sai então do palco, para a entrada de Tom Petty and The Heartbreakers. Embora um dos guitarristas dos Heartbreakers se esforce, a voz de Tom Petty não combina muito com "Taxman" e a bela "I Need You" parece aguada, sem o pedal de volume que George usou na gravação original e sem os backing vocals de Jonh e Paul. Uma parte da banda se junta aos Heartbreakers para tocar "Handle With Care" sucesso dos Traveling Wilburrys.

E volta mais uma vez a banda toda para tocar a melancólica "Isn't It a Pitty", com vocal de Eric Clapton no início e depois do grande Billy Preston (tecladista que foi o único músico a ter seu nome incluído na capa de um disco dos Beatles) e novo slide de Marc Mann, seguido de um solo de Eric.

"Please Welcome Ringo Starr" anuncia Eric, para delírio da plateia. Ringo canta duas músicas: Photograph (uma música simples de George e Ringo, mas - como o próprio Ringo diz - cujas palavras tomam um novo sentido - All I got is a photograph and I realize you're not coming back anymore) e Honey Don't (de Carl Perkins que George admirava muito). Ao final Ringo anuncia "um outro amigo de George" - Paul McCartney. Ringo vai para a quarta bateria e Paul assume os vocais de "For You Blue" (de George), com mais um slide de Marc Mann e solo de piano de Garry Broker.

Paul pega então um ukulele e inicia uma belíssima versão de "Something" (que todo mundo, menos Frank Sinatra, sabe que é do George), com Paul segurando o começo "no gogó" e aos poucos os outros instrumentos vão entrando. Após o solo (de Marc Mann) Eric se junta a Paul nos vocais - uma combinação incrível. Em seguida a maioria troca a guitarra por violão (exceção para Dhani que estava tocando violão nas demais e nesta toca uma Telecaster e para Marc Mann que mais uma vez assume o slide) para "All Things Must Pass" (de George), com vocal de Paul.

Paul passa então para o piano para "While My Guitar Gently Weeps", dividindo o vocal novamente com Eric. Embora no início Marc Man se destaque na guitarra, quem a faz chorar, como na gravação dos Beatles, é Eric.

E vem depois "My Sweet Lord", com um show de Billy Preston nos vocais e Marc Mann e Andy Fairweather-Low dividindo o slide.

Já em clima de final, Tom Petty and The Heartbreakers se acrescentam à banda para a estranha "Wah-Wah" (composta por George nos tempos mais tumultuados dos Beatles) . E, por último, Joe Brown retorna com o ukulele para a bela "I'll See You In My Dreams" (que não é de George).

DVD Dois - Versão Cinematográfica e Material Adicional

A versão cinematográfica substitui algumas músicas por trechos de entrevistas; a ordem das músicas é também alterada. No final fica supérflua, pois todo o material está mais completo no resto dos DVDs.

O material adicional é composto dos bastidores da apresentação do Monty Python (mostrando a presença do Tom Hanks), uma curta parte dos ensaios da "Banda do George" (com vocais alternativos para partes cantadas por Paul e Tom Petty no show) e da orquestra de Ravi Shankar e entrevistas (como curiosidades como quando o "uncle Ringo" cortou o interesse de Dhani pela bateria).

Concluindo

Imperdível.

quinta-feira, fevereiro 05, 2009

O Não Tão Básico do Básico - Paradigmas de Programação - Parte 1

De uma forma informal, podemos dizer que um Paradigma de Programação é um estilo de programação, sugerindo como um programa deve ser estruturado quanto às suas estruturas de dados e controle de fluxo.

O Básico do Controle de Fluxo

Embora os computadores se notabilizem pela rápida e precisa execução de sequências de instruções, o seu poder e flexibilidade vem da sua capacidade de decidir quais instruções serão executadas.

Os computadores atuais seguem a filosofia de programa armazenado no qual as instruções estão armazenadas em memória e um registrador (o contador de programa) aponta para a próxima instrução a executar. Na operação normal este registrador é incrementado a cada instrução executada para apontar para a instrução no endereço seguinte de memória.

Instruções de desvio permitem alterar este comportamento. O exemplo mais simples é o desvio incondicional (conhecido como goto ou jump) que coloca um novo valor no contador de programa, desviando o fluxo de execução para uma outra região da memória. As instruções de desvio condicional prosseguem sequencialmente ou desviam o fluxo em função de alguma condição. A forma como os desvios condicionais são implementados variam um pouco conforme o processador. O mais comum é a instrução definir (de forma absoluta ou relativa) o endereço para o qual o controle será desviado se a condição for verdadeira, mas existem processadores em que todas as instruções podem ser condicionais ou que possuam instruções do tipo skip que pulam a instrução seguinte se a condição for verdadeira (neste caso a instrução seguinte costuma ser um desvio incondicional).

Uma situação recorrente na programação é a necessidade de executar a mesma sequência de instruções em vários pontos do programa. A solução mais simples é repetir esta sequência em cada ponto, mas isto consome memória de forma redundante e dificulta a manutenção do programa. Surge assim a necessidade de uma instrução que desvie o fluxo para uma sequência de instruções (sub-rotina) porém permita retornar ao ponto seguinte à chamada. Existem várias formas de implementar isto, mas a mais usual é utilizar uma pilha para armazenar o endereço de retorno. A maioria dos processadores possui instruções dedicadas para chamar uma sub-rotina salvando o endereço de retorno no topo da pilha (CALL) e para retornar de uma sub-rotina para o endereço que está no topo da pilha (RET). O uso da pilha não somente permite termos vários níveis de sub-rotina como situações em que uma sub-rotina chama a si mesma (recursividade) ou é chamada por uma rotina que ela chamou (o que também causa uma re-entrância).

É em cima destas instruções básicas do processador que as linguagens de alto nível constroem as suas instruções de controle de fluxo, que os programadores usam para implementar os paradigmas de programação.

A seguir: A programação Não Estruturada

terça-feira, fevereiro 03, 2009

Básico do Básico: Álgebra Booleana

Colaborando com a série do meu amigo Caloni e reaproveitando um artigo meu para o wiki do C&C++ Brasil, vou falar um pouco sobre um fundamento básico da programação que muitos não conhecem de uma forma mais formal: a Álgebra Booleana.

Introdução

A álgebra booleana trata de expressões lógicas. George Boole inventou esta algebra para analisar expressões lógicas envolvendo conjuntos (que ele chamava de classes), porém atualmente a usamos principalmente com variáveis que podem assumir um dentre dois valores: verdadeiro ou falso, 0 ou 1, Sim ou Não, etc.

A álgebra booleana pode ser facilmente expressa através de circuitos elétricos e eletrônicos e é a base do projeto dos computadores digitais. Na programação, um conceito essencial é o desvio (ou tomada de decisão) que consiste em mudar a ordem de execução conforme uma expressão lógica.

Operações na Álgebra Booleana

Conforme mencionado, a álgebra booleana envolve variáveis que podem assumir somente dois valores. Nesta descrição vamos chamar estes valores de 0 e 1, porém lembre-se que eles podem significar qualquer par de condições que sejam exclusivas (isto é, uma variável não pode ser 0 e 1 simultaneamente) e complementares (isto é, se não for 0 é 1 e vice-versa).

Uma forma de representar as operações da álgebra booleana é através das tabelas verdade, que mostram os resultados para todas as combinações de entrada.

Uma primeira operação lógica é a negação (ou complemento), que vamos representar por !
A  !A
0 1
1 0
Uma segunda operação é o E (ou AND) que vamos representar por &&
A B A&&B
0 0 0
0 1 0
1 0 0
1 1 1
Ou seja, o E resulta em 1 somente se os dois operandos forem 1.

Outra operação é o OU (ou OR),representado abaixo por ||
A B A||B
0 0 0
0 1 1
1 0 1
1 1 1
O OU resulta em 1 se pelo menos um dos operandos for 1.

Por último vamos considerar o OU EXCLUSIVO (ou XOR), representado abaixo por ^
A B A^B
0 0 0
0 1 1
1 0 1
1 1 0
O XOR resulta 1 se um e somente um dos operados for 1. O XOR é equivalente a uma combinação de E e OU:

A^B = (A||B) && !(A&&B)

Ou seja, o XOR resulta em 1 se um ou o outro for 1 mas não ambos.

Nos circuitos eletrônicos, devido às características dos circuitos construídos com transistores, é comum encontrarmos outros tipos de operações:

A NAND B = !(A && B)
A NOR B = !(A || B)

Propriedades das Operações Booleanas

Da mesma forma que na aritmética e álgebra comuns, as operações booleanas apresentam uma série de propriedades úteis:
A && 1 = A  (elemento neutro)
A && 0 = 0
A && A = A
A && B = B && A (comutativa)
(A && B) && C = A && (B && C) (associativa)

A || 0 = A
A || 1 = 1
A || A = A
A || B = B || A
(A || B) || C = A || (B || C)

A ^ 0 = A
A ^ 1 = !A
A ^ A = 0
A ^ B = B ^ A
(A ^ B) ^ C = A ^ (B ^ C)

A && (B || C) = (A && B) || (A && C) (distributiva)
A || (B && C) = (A || B) && (A || C)

!(A && B) = (!A) || (!B)
!(A || B) = (!A) && (!B)
Notar que existe alguma semelhança entre, respectivamente, OU e E e SOMA e MULTIPLICAÇÃO, mas ela não é completa.

Álgebra Booleana na Programação

A maioria das linguagens possui o conceito de expressões lógicas, que retornam um valor verdadeiro ou falso. Estas expressões são expressões booleanas e utilizam os operadores que vimos acima. A forma mais comum de se obter um valor lógico é a partir de operadores de comparação, como igual, diferente, maior, maior ou igual, etc.

A principal utilidade das expressões lógicas na programação é no controle de fluxo, onde a próxima instrução a ser executada é determinada pelo resultado da expressão. As construções típicas para isto são os IFs e WHILEs.

As linguagens costumam também permitir armazenar o resultado das expressões lógicas em vaiáveis. A maioria das linguagens possui um tipo específico para isto, outras (como C) se utilizam de um tipo mais genérico.

O conhecimento das operações booleanas e suas propriedades facilitam a conversão de condições descritas textualmente em expressões lógicas, assim como as suas simplificações.

Encerrando com dois exemplos:

Testar se o conteúdo da variável num é maior que 0 e menor que 10

if ((num > 0) && (num < 10))

Testar se o conteúdo da variável num não é maior que 0 e menor que 10

if (!((num > 0) && (num < 10))) ou
if ((!(num > 0)) || (!(num < 10))) ou ainda
if ((num <= 0) || (num >= 10))

Reparar que neste segundo caso a negação "converteu" o E em um OU. Um erro comum é manter o E, escrevendo:

if ((num <= 0) && (num >= 10))

neste caso a expressão será sempre falsa (não existe um número que seja menor ou igual a zero E maior e igual que dez)!

12/05/09: acertada a parte final, onde alguns '<' e '>' bagunçaram tudo.

sábado, janeiro 31, 2009

Livro do Mês: Battle of Wits

Já escrevi anteriormente sobre um outro livro sobre criptografia na 2a Guerra que não me agradou por completo.

Insistindo em saber mais sobre o assunto, comprei e li o livro "Battle of Wits" de Stephen Budiansky, que gostei e aqui comento.

Abrangência Ambiciosa

Enquanto "From Fish To Colossus" se concentra na quebra de uma das criptografias usadas pelos alemães, "Battle of Wits" tem uma proposta mais ambiciosa, expressa na própria capa: ser a história completa da quebra de códigos na Segunda Guerra Mundial. O livro não chega a tudo isto, pois:
  • Como a maioria dos livros sobre o assunto, trata quase que exclusivamente do lado dos aliados. As referências aos esforços alemães para quebrar os códigos dos aliados é mínima e não existe nenhuma menção aos esforços japoneses.
  • Poucas páginas são dedicadas a cada um dos códigos. O Lorentz, tratado em "From Fish To Colossus" ganha pouco mais de um par de páginas.
  • O livro propositalmente não entra a fundo nos aspectos mais técnicos.
A Criptografia entre a Primeira e a Segunda Guerra

Interessantemente, o livro começa antes da Segunda Guerra. Embora hoje em dia os serviços de inteligência militar de todas as naçoes reconheçam a importância das informações obtidas pela monitoração das comunicações dos adversários (e dos aliados também), não era assim antes e no começo da guerra.

As equipes de criptografia eram pequenas e desprestigiadas. Em 1929, o Secretário de Estado americano Henry Stimson fechou a seção de criptografia (Black Chamber) sob o argumento que "cavalheiros não lêem a correspondência de outros".

Para complicar ainda mais, o chefe da Black Chamber, ao se ver sem emprego, escreveu um livro contando tudo, particularmente a quebra do código diplomático japonês. Isto não somente levou os japoneses a aumentar a segurança dos seus códigos, como fez com que os ingleses relutassem em passar segredos ciptográficos para os americanos durante a 2a Guerra.

Nos anos que antecederam a eclosão da guerra, militares ingleses ignoraram informações obtidas pela quebra de código, preferindo se basear em informações provenientes de fontes bem menos confiáveis. Mesmo durante a guerra, após vitorias em que as informações obtidas por criptoanálise foram fundamentais, muitos militares desdenhavam informações de inteligência, particularmente as provenientes de civis.

Códigos e Mais Códigos

O Enigma é o código mais famoso da 2a Guerra, mas na verdade existiram uma quantidade muito grande de códigos. Marinha, Exército, Aeronáutica e Chancelaria, da Alemanha, Japão e Itália, adotavam cada um os seus próprios códigos. Existiam também variações dos códigos para níveis diferentes de informações.

Por último, deve-se lembrar que os códigos e procedimentos foram alterados durante a guerra. Por exemplo, na chamada "Batalha do Atlântico" (a luta entre os combois dos EUA para a Inglaterra e suas escoltas contra os submarinos alemães) ocorreu em certa época uma alternância na capacidade de ler os códigos do inimigo, o que criou em ambos os lados uma falsa sensação de segurança.

Muita Gente Decifrando

Assim como existiam vários códigos, eram várias as equipes de criptoanálise. Enquanto a Inglaterra conseguiu concentrar seus esforços em Bletchley Park, os americanos possuiam equipes separadas do exército (em Arlington Hall) e da marinha (em Mount Vernon). A cooperação entre estas equipes era dificultada por brigas políticas e desconfianças mútuas.

Um ponto interessante do livro é descrever e comparar o ambiente nestes três locais.

As Máquinas

Embora muitos associem a criptoanálise na 2a Guerra ao surgimento dos computadores, na verdade as máquinas utilizadas eram muito diferentes do computador eletrônico de uso geral.

Na quebra da Enigma, a máquina principal foia bomba, criada originalmente pelos poloneses. Era um equipamento eletro-mecânico voltado para testar combinações na base da força bruta, listando soluções potenciais.

Como descrito em detalhes no "From Fish To Colossus", o Colossus usado para quebrar a cifra Lorenz era um equipamento eletro-eletrônico também voltado para testar combinações na base da força bruta. O interessante nele é que ele gerava a chave eletrônicamente.

Os americanos usavam principalmente cartões perfurados. Máquinas da IBM eram usadas para classificar e tabular os dados contidos nos cartões, gerando estatísticas usadas para determinar e confirmar chaves.

Os americanos usaram também técnicas ópticas para construir computadores analógicos, uma tecnologia que no pós-guerra foi abandonada.

Os "Causos"

O livro é em vários momentos divertido e/ou empolgante, graças a várias histórias curiosas ou drmáticas. A atitude informal dos civis em Bletchley Park é responsável por vários deles.

Na parte mais dramática, o livro conta como três marinheiros ingleses recuperaram documentos de um submarino alemão afundando, possibilitando a quebra da Enigma naval utilizada nos submarinos. Dois deles pagaram com a vida pela proesa e foram agraciados postumamente com medalhas de bravura civil (pois o ato de bravura foi realizado quando a batalha já tinha acabado). O terceiro, que sobreviveu, ganhou uma medalha menos significativa e foi prontamente expulso da marinha, pois descobriu-se que era menor e mentira sobre a idade para se alistar.

Um caso incrível foi o do espanhol Juan Pujol Garcia. Após ter seu apoio rejeitado pela Inglaterra, se ofereceu por conta própria para ser espião para a Alemanha, passando informações totalmente falsas. Quando a Inglaterra finalmente o aceitou, as informações obtidas pela criptoanálise permitiram elevar ainda mais o seu moral junto aos alemães (que o agraciaram com uma Cruz de Ferro). Isto ajudou um dos maiores engodos dos aliados, fazendo os alemães acreditarem que o desembarque do dia D seria em outro local. Mesmo semanas após o desembarque na Normandia, os alemães continuavam aguardando o desembarque de um exército americano ficticio.

Concluindo

"Battle of Wits" não é um livro perfeito, mas é uma leitura muito interessante e prazeirosa. Os interessados nas minúcias técnicas poderão ficar insatisfeitos e em alguns momentos o livro fica monótono com um amontoados de nomes de pessoas e lugares, mas na maior parte do tempo o livro faz juz ao tamanho épico da Segunda Grande Guerra.

Ao contrário de "From Fish To Colossus", "Battle of Wits" pode ser encomendado aqui no Brasil na Livraria Saraiva ou na Livraria Cultura. O meu exemplar veio da Amazon (nos bons tempos do dolar baixo).

sexta-feira, janeiro 30, 2009

Jogos Antigos em Condições Acessíveis

O site Good Old Games (GOG.com) oferece jogos antigos em condições muito interessantes:
  • Compatibilidade garantida com XP e Vista
  • Entrega via download
  • Software sem proteção (DRM-free)
  • Preços baixos (atualmente a maioria custa US$5,99 e alguns custam US$9,99)
Para quem quiser experimentar o serviço, existem dois jogos grátis no setor de aventuras: Beneath a Steel Sky e Lure of the Temptress.

No caso destes dois jogos, e várias outras aventuras, é usado o ScummVM (com o apoio dos seus desenvolvedores). Uma dica para quem for rodar estes jogos: dê uma lida na documentação do ScummVM. Por exemplo, o Lure of the Temptress foi originalmente feito para uma resolução de 320x240. Por default o ScummVM irá rodar este jogo em 640x480 simplesmente convertendo cada ponto em quatro, porém ele tem opções mais avançadas para aumentar a resolução. Usando as teclas Alt 1 a Alt 8 você seleciona o algorítimo; em alguns deles você pode usar Alt + e Alt - para mudar o fator multiplicador. Quando você encontrar a configuração que mais agrade, você pode acrescentar o parâmetro gfx_mode no arquivo lure.ini (no meu caso estou usando gfx_mode=hq3x que gera uma imagem 960x720 bem razoável).

quinta-feira, janeiro 29, 2009

Screen Savers - Exemplos

O código fonte e executável dos exemplos podem ser baixados daqui.

ScrBasico é um exemplo de screen saver feito utilizando diretamente a API do Windows. Não é um screen saver muito sofisticado: simplesmente mostra um texto no meio da tela, com a cor variando continuamente entre branco e preto.

O programa principal é simples:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrecInst,
LPSTR lpCmdLine, int nShowCmd)
{
hInst = hInstance;
AnalisaCmd (lpCmdLine);
switch (modo)
{
case smConfig:
DialogBox (hInst, MAKEINTRESOURCE (IDD_CONFIG), hMainWnd,
ConfigDlgProc);
break;

case smPassword:
ChangePassword ();
break;

case smPreview:
case smSaver:
DoScreenSaver ();
break;
}

return 0;
}
A rotina AnalisaCmd trata os parâmetros na linha de comando, conforme o que foi visto na segunda parte desta série:
static void AnalisaCmd (char *szCmd)
{
// valores default
hMainWnd = NULL;
modo = smConfig;

// por seguranca
if (szCmd == NULL)
return;

// pula delimitadores inicias
while ((*szCmd == ' ') || (*szCmd == 0x09))
szCmd++;

// testa comando vazio
if (*szCmd == 0)
return;

// testa e trata flags
if ((*szCmd == '-') || (*szCmd == '/'))
{
szCmd++;
switch (*szCmd)
{
// config
case 'c':
case 'C':
modo = smConfig;
hMainWnd = PegaHandle (szCmd+1);
return;

// screen saver
case 's':
case 'S':
modo = smSaver;
return;

// preview
case 'p':
case 'P':
case 'l':
case 'L':
modo = smPreview;
hMainWnd = PegaHandle (szCmd+1);
return;

// alteração de senha
case 'a':
case 'A':
modo = smPassword;
hMainWnd = PegaHandle (szCmd+1);
return;
}
}

// algo inesperado
modo = smNone;
}
O diálogo de configuração é simples, com um editbox para entrar a mensagem a ser apresentada. Esta mensagem é salva no registry.

A rotina de alteração de senha (usada somente pelos arqueólogos brincando com Windows 9x) apela para uma rotina não documentada do Windows, linkada dinamicamente da MPR.DLL:
static void ChangePassword()
{
HINSTANCE hmpr;


hmpr = LoadLibrary("MPR.DLL");
if (hmpr != NULL)
{
PWDCHANGEPASSWORD PwdChangePassword = (PWDCHANGEPASSWORD)
GetProcAddress (hmpr,"PwdChangePasswordA");
if (PwdChangePassword != NULL)
PwdChangePassword("SCRSAVE", hMainWnd, 0, 0);
FreeLibrary(hmpr);
}
}
A rotina DoScreenSaver funciona como um programa principal típico de aplicações Windows API: registra uma classe de janela, cria a janela e fica em um laço de tratamento de mensagens:
static void DoScreenSaver ()
{
WNDCLASS wc;
int cx, cy;
UINT oldval;
MSG msg;

// Registra a nossa classe de janela
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = ScreenSaverProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = szWndClass;
RegisterClass (&wc);

// Cria a janela
if (modo == smPreview)
{
RECT rc;

GetWindowRect (hMainWnd, &rc);
cx = rc.right - rc.left;
cy = rc.bottom - rc.top;
hSSWnd = CreateWindowEx (0, szWndClass, "Preview", WS_CHILD | WS_VISIBLE,
0, 0, cx, cy, hMainWnd, NULL, hInst, NULL);
}
else
{
cx = GetSystemMetrics (SM_CXSCREEN);
cy = GetSystemMetrics (SM_CYSCREEN);
hSSWnd = CreateWindowEx (WS_EX_TOPMOST, szWndClass, "ScreenSaver",
WS_POPUP | WS_VISIBLE,
0, 0, cx, cy, NULL, NULL, hInst, NULL);
}
if (hSSWnd == NULL)
return;

// avisa screen saver rodando (desativa Ctrl Alt Del)
if (modo == smSaver)
SystemParametersInfo(SPI_SCREENSAVERRUNNING,1,&oldval,0);

// Loop de tratamento de mensagens
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

// avisa fim do screen saver (reativa Ctrl Alt Del)
if (modo == smSaver)
SystemParametersInfo(SPI_SCREENSAVERRUNNING,0,&oldval,0);
}
A rotina de janela trata as mensagens relevantes:
LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static POINT InitCursorPos; // posicao inicial do mouse
static UINT uTimer; // identificador do timer
static int iCor; // cor da mensagem
static int iDelta; // variacao da cor
static BOOL bFecha = FALSE; // TRUE se geramos WM_CLOSE
static DWORD dwInicio; // inicio da execucao do screen saver


switch (uMsg)
{
case WM_CREATE: // janela foi criada
LeCfg (szMsg, sizeof (szMsg));
GetCursorPos (&InitCursorPos); // salva posicao do mouse
uTimer = SetTimer(hWnd, 1, 150, NULL); // liga o timer
dwInicio = GetTickCount (); // guarda horario inicial
iCor = 0;
iDelta = 4;
break;

case WM_TIMER: // mensagem periodica
Desenha (hWnd, szMsg, iCor);
iCor += iDelta;
if (iCor < icor =" 0;"> 255)
iCor = 255;
if ((iCor == 0) || (iCor == 255))
iDelta = -iDelta;
break;

case WM_ACTIVATE: case WM_ACTIVATEAPP: case WM_NCACTIVATE:
if ((modo == smSaver) && !bDialogo && (LOWORD(wParam) == WA_INACTIVE))
{
// Desativar screen saver
bFecha = TRUE;
PostMessage (hWnd, WM_CLOSE, 0, 0);
}
break;

case WM_SETCURSOR:
if ((modo == smSaver) && !bDialogo)
SetCursor (NULL); // apaga o cursor do mouse
else
SetCursor (LoadCursor (NULL,IDC_ARROW));
break;

case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN:
if ((modo == smSaver) && !bDialogo)
{
// apertou botao ou tecla, fechar screen saver
bFecha = TRUE;
PostMessage (hWnd, WM_CLOSE, 0, 0);
}
break;

case WM_MOUSEMOVE:
if ((modo == smSaver) && !bDialogo)
{
POINT pt;
int dx, dy;

// Verifica se moveu o suficente para desativar
GetCursorPos(&pt);
dx = pt.x - InitCursorPos.x;
if (dx < dx =" -dx;" dy =" pt.y" dy =" -dy;"> MouseThreshold) || (dy > MouseThreshold))
{
bFecha = TRUE;
PostMessage (hWnd, WM_CLOSE, 0, 0);
}
}
break;

case WM_SYSCOMMAND:
if (modo == smSaver)
{
// Ignora ativacao de screen saver e close
if ((wParam == SC_SCREENSAVE) || (wParam == SC_CLOSE))
return FALSE;
}
break;

case WM_CLOSE:
if ((modo == smSaver) && !bDialogo && bFecha)
{
BOOL bOk = TRUE;

// Estamos tentando fechar
if (GetTickCount() > 5000) // Pode interromper sem senha se for rapido
{
bOk = VerifyPassword (hWnd);
GetCursorPos (&InitCursorPos); // salva posicao do mouse
}
if (bOk)
DestroyWindow (hWnd); // encerra screen saver
}
bFecha = FALSE;
if (modo == smSaver)
return FALSE; // ignora WM_CLOSE
break;

case WM_DESTROY:
if (uTimer)
KillTimer(hWnd, uTimer);
PostQuitMessage(0);
break;
}

return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
Um timer é usado para re-escrever a mensagem em uma cor diferente a cada 150 milisegundos.

Um último ponto de interesse é a rotina VerifyPassword:
static BOOL VerifyPassword (HWND hwnd)
{
OSVERSIONINFO osv;
HINSTANCE hpwdcpl;
VERIFYSCREENSAVEPWD VerifyScreenSavePwd;
BOOL bResult;

// No NT e derivados, o screen saver deve terminar, o sistema solicita a senha
// e re-executa o screen saver se senha incorreta
osv.dwOSVersionInfoSize = sizeof(osv);
GetVersionEx (&osv);
if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT)
return TRUE;

// No 95/98/Me, usa funcao nao documentada
hpwdcpl = LoadLibrary ("PASSWORD.CPL");
if (hpwdcpl == NULL)
return TRUE;
VerifyScreenSavePwd = (VERIFYSCREENSAVEPWD)
GetProcAddress (hpwdcpl,"VerifyScreenSavePwd");
if (VerifyScreenSavePwd == NULL)
{
FreeLibrary(hpwdcpl);
return TRUE;
}
bDialogo = TRUE;
SendMessage (hwnd, WM_SETCURSOR, 0, 0); // mostra cursor
bResult = VerifyScreenSavePwd (hwnd);
bDialogo = FALSE;
SendMessage (hwnd, WM_SETCURSOR, 0, 0); // esconde cursor
FreeLibrary (hpwdcpl);
return bResult;
}
Reparar que no Windows 9x usamos uma segunda função não documentada; nos Windows mais recentes não precisamos fazer nada.

O mesmo screen saver, feito com a Screen Saver Library está em LibScrSaver. Neste caso a biblioteca cuida de quase tudo, o que interessa é somente a rotina ScreenSaverProc:
LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static UINT uTimer;
static int iCor;
static int iDelta;

// Trata mensagens
switch (uMsg)
{
// Iniciacao: le config e cria timer
case WM_CREATE:
LeCfg (szMsg, sizeof (szMsg));
uTimer = SetTimer(hWnd, 1, 150, NULL);
iCor = 0;
iDelta = 4;
break;

// Chamada periodica, faz a animacao
case WM_TIMER:
Desenha (hWnd, szMsg, iCor);
iCor += iDelta;
if (iCor < icor =" 0;"> 255)
iCor = 255;
if ((iCor == 0) || (iCor == 255))
iDelta = -iDelta;
break;

// Termino: destroi o timer
case WM_DESTROY:
if (uTimer)
KillTimer(hWnd, uTimer);
break;
}

// Executa tratamento default
return DefScreenSaverProc(hWnd, uMsg, wParam, lParam);
}
Percebe-se que com a biblioteca podemos nos concentrar na parte que interessa: atualizar a tela.

terça-feira, janeiro 27, 2009

Screen Savers: Construindo Screen Savers

Neste post veremos como construir um screen saver em C.

Construindo um Screen Saver Usando Somente a API do Windows

Os passos básicos para construir um screen saver “na raça” são:
  • Analisar a linha de comando (parâmetro lpCmdLine de WinMain), para detectar qual a operação desejada (configuração, preview, alteração de senha ou operação normal).
  • Se a operação for configuração ou alteração da senha, mostrar o diálogo correspondente e encerrar.
  • Criar a janela onde será feito o screen saver. No caso de preview, esta janela está limitada uma pequena parte da tela, na operação normal esta janela ocupa a tela toda. Uma vez criada a janela, toda a ação passa a ser na respectiva rotina de janela:
    • Na mensagem de criação da janela (WM_CREATE) são feitas as iniciações. Normalmente é criado um timer, para gerar periodicamente uma mensagem WM_TIMER.
    • Na mensagem WM_TIMER a tela é atualizada.
    • As mensagens WM_KEYDOWN, WM_ACTIVATE. WM_ACTIVATEAPP, WM_NCACTIVATE, WM_LBUTTONDOWN, WM_MBUTTONDOWN e WM_RBUTTONDOWN solicitam o fechamento da janela.
    • No tratamento da mensagem WM_SETCURSOR o ponteiro do mouse deve ser apagado na operação normal (desde que não esteja sendo apresentado o diálogo de senha).
    • No tratamento da mensagem WM_MOUSEMOVE é verificado se o mouse se moveu mais que uma certa tolerância. Em caso afirmativo, é solicitado o fechamento da janela.
    • No tratamento da mensagem WM_CLOSE é solicitada a senha. Uma cortesia adicional ao usuário é só pedir a senha alguns segundos após o início da execução do screen saver. Desta forma, se o usuário está na frente do micro ele pode abortar o screen saver sem digitar a senha.
    • Por último, a mensagem WM_DESTROY é usada para fazer a limpeza final (como destruir o timer) e encerrar o programa.
Construindo um Screen Saver Usando a Screen Saver Library

Para facilitar a construção de screen savers, a Microsoft desenvolveu uma biblioteca que já contem parte do código necessário. Para gerar um screen saver usando a biblioteca é necessário:
  • linkar Scrnsave.lib ao programa;
  • escrever três rotinas: ScreenSaverProc, ScreenSaverConfigureDialog e RegisterDialogClasses; e
  • definir nos resources um ícone, a descrição do screensaver e o diálogo de configuração.
A biblioteca contém o programa principal do screen saver (função WinMain), que trata os parâmetros na linha de comando. Em caso de ativação do screen saver em modo tela cheia, a biblioteca cria a janela de apresentação.

As rotinas que devem ser implementadas são:

LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

Esta rotina é a rotina de janela da janela usada para apresentar a imagem do screen saver. As mensagens não tratadas devem ser passadas para a rotina DefScreenSaverProc da biblioteca, que cuida de esconder o cursor e de terminar o screen saver quando necessário.

Tipicamente esta rotina trata as mensagens WM_CREATE (para realizar iniciações, como criar um timer), WM_TIMER (para atualizar a tela) e WM_DESTROY (para liberar recursos, como o timer)

BOOL WINAPI ScreenSaverConfigureDialog (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

Esta rotina é a rotina de diálogo do diálogo de configuração. Este diálogo é criado automaticamente pela biblioteca.

BOOL WINAPI RegisterDialogClasses (HINSTANCE hInst)

Esta rotina é chamada pela biblioteca para permitir registrar classes não padrões utilizadas pelo screen saver. A rotina deve retornar TRUE em caso de sucesso.

Os resources que devem ser criados são:
  • Um ícone, com identificador ID_APP (definido em Scrnsave.h como 100);
  • Um string resource com identificador IDS_DESCRIPTION (definido em Scrnsave.h como 1), contendo o nome do screen saver; e
  • O template do diálogo de configuração, com identificador ID_SCRNSAVECONFIGURE (definido em Scrnsave.h como 2003).

No próximo post vamos ver um exemplo de como colocar tudo isto em prática.

domingo, janeiro 25, 2009

Screen Savers: Anatomia

Neste post vamos ver como é um Screen Saver por dentro; nesta descrição assumimos que o leitor possui um bom conhecimento das APIs e do funcionamento do Windows.

O que é um Screen Saver

Um screen saver é um programa .EXE. O Windows reconhece um screen saver por duas características:
  • Extensão .SCR
  • O primeiro resource (ID 1) é um string resource (este string é usado como o nome apresentado para seleção)
Para funcionar como um screen saver, o programa deve ter alguns comportamentos adicionais:
  • Tratar alguns parâmetros na linha de comando
  • Cria a sua janela principal com um determina estilo
  • Tratar algumas mensagens
  • Encerar quando o operador pressionar uma tecla ou movimentar o mouse
Recursos de um Screen Saver

Um screen saver completo fornece os seguintes recursos:
  • O screen saver em si, que executa a apresentação na tela quando o sistema está inativo;
  • Uma tela de configuração, onde o usuário pode alterar o funcionamento do screen saver;
  • Uma modo preview, no qual o screen saver opera dentro da imagem de um monitor na tela de seleção de screen saver do Display Properties.
  • Configuração de senha (para Windows 95/98/Me)
Implementação da Senha do Screen Saver

O objetivo da senha do screen saver é impedir que uma pessoa não autorizada encerre o screen saver e consiga visualizar o conteúdo da tela e operar o computador.

No Windows NT, 2000 e XP a senha do screen saver é tratada diretamente pelo sistema operacional, não exigindo nenhum tratamento especial no screen saver.

No Windows 9x, cabe ao screen saver:
  • Apresentar o diálogo de mudança da senha
  • Salvar a senha
  • Apresentar o diálogo de solicitação da senha
  • Conferir a senha
  • Desabilitar Alt Esc e Ctrl Alt Del
Quando um Screen Saver é Executado

O Windows pode disparar um Screen Saver de várias maneiras:
  • Ativação por inatividade – este é o funcionamento normal do screen saver. Quando o Windows detecta que o sistema está inativo pelo tempo configurado para o screen saver, ele envia para a janela em foreground uma mensagem WM_SYSCOMMAND com o parâmetro SC_SCREENSAVE. Se esta mensagem é passada para DefWindowProc, o screen saver configurado é disparado com o parâmetro /s na linha de comando.
  • Configuração – quando o botão Settings é clicado na tela de seleção de screen saver do Display Properties, o screen saver é disparado com o parâmetro /c na linha de comando.
  • Preview full-screen - quando o botão Preview é clicado na tela de seleção de screen saver do Display Properties, o screen saver é disparado com o parâmetro /s na linha de comando.
  • Preview em Display Properties – Quando a tela de seleção de screen saver do Display Properties recebe o foco, ou quando um novo screen saver é selecionado nesta tela, o screen saver é disparado com o parâmetro /p #### na linha de comando. #### corresponde ao valor (em decimal) do handle da janela onde o screen saver deverá ser apresentado (ou seja, a área correspondente à tela do desenho de um monitor).
  • Mudança de senha – No Windows 9x o screen saver é o responsável pela senha (no NT, 2000 e XP, o próprio sistema gerencia a senha). Quando o botão Change Password é clicado na tela de seleção de screen saver do Display Properties, o screen saver é disparado com o parâmetro /a #### na linha de comando. #### corresponde ao valor (em decimal) do handle da janela de seleção de screen saver.
  • Test no Windows Explorer – clicando duas vezes no ícone de um screen saver, ou clicando com o botão direito e escolhendo Test, o screen saver é executado com o parâmetro /s na linha de comando.
  • Config no Windows Explorer – com o botão direito no ícone de um screen saver, e escolhendo Config, o screen saver é executado sem parâmetros.
Obviamente, outras aplicações podem simular estas situações, executando o screen saver com o correspondente parâmetro na linha de comando.

Parâmetros na Linha de Comando de um Screen Saver

A lista abaixo resume os parâmetros passados para um screen saver na linha de comando e a ação a ser executada pelo screen saver:
  • (nenhum) Apresentar a tela de configuração
  • /c Apresentar o diálogo de configuração, como filho da janela em foreground
  • /c #### Apresentar o diálogo de configuração, tendo como pai a janela cujo handle corresponde a ####
  • /a #### Apresentar o diálogo de alteração da senha, tendo como pai a janela cujo handle corresponde a ####
  • /s Executar o screen saver propriamente dito
  • /p #### ou /l #### Executar o preview do screen saver dentro da janela cujo handle corresponde a ####
A definição correta do pai dos diálogos apresentados é importante para garantir que o usuário não possa interagir com a tela de seleção de screen saver enquanto não tiver fechado o diálogo do screen saver.

Na análise dos parâmetros deve-se aceitar ‘-‘ no lugar de ‘/’ e letras tanto maiúsculas como minúsculas.

Tratamento de Mensagens no Screen Saver

A lista a seguir mostra as principais mensagens que devem ser tratadas por um screen saver:
  • WM_ACTIVATE, WM_ACTIVATEAPP, WM_NCACTIVATE Se LOWORD(wParam) for WA_INACTIVE, indicando que outra aplicação está sendo ativada, o screen saver deve terminar
  • WM_SETCURSOR Desligar o cursor, através de SetCursor(NULL)
  • WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN, WM_KEYDOWN Encerrar o screen saver
  • WM_MOUSEMOVE Se as coordenadas do mouse mudaram mais que um certo mínimo, encerrar o screen saver
Na próxima parte vamos ver como construir um screen saver em C.

sábado, janeiro 24, 2009

Screen Savers - Introdução

Um Breve Histórico

Os screen savers (protetores de tela) surgiram quando as telas dos computadores e terminais eram predominantemente monocromáticas e não-gráficas. Uma vez que a frequência de refresh dos monitores era baixa (60Hz ou menos) era comum o uso de fósforo de maior persistência, para reduzir o flicker (piscamento) da tela. Entretanto, a apresentação de um ponto aceso por um tempo prolongado danificava o fósforo, deixando uma marca permanente. Até o final dos anos 80 era comum se encontrar monitores que apresentavam, mesmo quando desligados, marcas das regiões fixas da tela (como linhas de status) ou telas de apresentação frequente.

A idéia original dos screen savers era, portanto, mostrar uma tela dinâmica após um determinado período de inatividade do operador.

Como os primeiros sistemas operacionais eram monotarefa, a responsabilidade de proteger a tela ficava com a aplicação e a presença de screen savers era rara. Com o surgimento dos sistemas gráficos e multi-tarefas, como o Windows e o MacOS, os screen savers se difundiram.

Os screen savers foram descobertos rapidamente pela crescente legião de usuários do Windows (logo após a Paciência). Aos screen savers padrões do Windows logo se juntaram outros. A febre dos screen savers se intensificou na primeira metade dos anos 90, passando a constituir uma importante categoria de utilitário, que teve sucessos comerciais como o After Dark (com suas torradeiras voadoras) e o Screen Antics da Sierra (que mostrava as aventuras de um náufrago em uma ilha deserta).

Uma vantagem adicional do screen saver é proteger o conteúdo da tela contra curiosos, principalmente quando protegido por senha.

Os screen savers são hoje usados como material de marketing, para divulgar filmes, jogos e séries de TV.

Embora os monitores atuais não precisem mais de proteção, os screen savers continuam atraindo a atenção de usuários e programadores.

Categorias de Screen Savers


Categorizar algo é sempre uma tarefa incompleta, pois sempre existem os que se encaixam em mais de uma categoria e, ocasionalmente, surge algo novo que não se encaixa em nenhuma delas.

O objetivo da lista abaixo é, antes de mais nada, dar uma idéia do que pode ser feito com um screen saver.
  • Slideshow: É provavelmente o tipo mais comum. Uma série de imagens estáticas são apresentadas sequencialmente. Para um maior impacto pode-se acrescentar um efeito de transição entre uma imagem e outra.
  • Figuras gráficas: O screen saver apresenta uma figura composta por textos e/ou figuras geométicas (linhas, círculos, esferas, etc). Ao contrário do slideshow, a imagem não é estática: ela é construída ou transformada ao longo do tempo. A maioria dos screen savers que vem com o Windows se enquadram nesta categoria.
  • Animações: O screen saver apresenta uma animação, como um desenho animado onde objetos ou personagens se movimentam, possivelmente encenando uma pequena história.
  • Manipulação da imagem da tela: Basicamente uma variação da anterior, onde a imagem da tela no momento em que o screen saver é ativado é utilizada como ponto de partida. Alguns exemplos são bagunçar a tela e colocar personagens intergindo com o conteúdo da tela.
Evolução da Programação dos Screen Savers no Windows

Os screen savers surgiram inicialmente no Window 16 bits. Com o surgimento dos Windows de 32 bits (NT e 95), a especificação dos screen savers foi revista; o que iremos descrever adiante é esta especificação.

No Windows NT e descendentes (200, XP), o próprio Windows gerencia a senha dos screen savers, no 95 e sucessores (98 e Me) o tratamento da senha deve ser feito pelo próprio screen saver. O pacote Plus! do Windows 95 acrescentava algumas funcionalidades adicionais, que não serão tratadas aqui.

sexta-feira, janeiro 23, 2009

Simulando Um Computador - 4/4

Nesta última parte vamos dar uma examinada no código fonte do simulador, que pode ser baixado daqui.

Estrutura Básica

O SimComp é um programa C que segue a estrutura típica de uma aplicação "Windows API", nos moldes popularizados pelo Petzold.

O programa inicia pela rotina WinMain, que comanda a criação e apresentação da janela e depois entra no loop de tratamento de mensagens:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst,
LPSTR lpCmdLine, int nCmdShow)
{
HACCEL hAccel;
MSG msg;

// Iniciacoes
hInst = hInstance;
InitComp ();
nModoAtual = MODO_CMD;
hevBreak = CreateEvent (NULL, FALSE, FALSE, NULL);
hevInput = CreateEvent (NULL, FALSE, FALSE, NULL);

// Cria a janela principal
if (! CriaJanela())
return 1;

// Apresenta a janela
ShowWindow (hMainWnd, nCmdShow == SW_SHOWMAXIMIZED ? SW_SHOW :
nCmdShow);
UpdateWindow (hMainWnd);

// Trata as mensagens
hAccel = LoadAccelerators (hInst, MAKEINTRESOURCE (IDR_ACCEL));
while (GetMessage (&msg, NULL, 0, 0))
{
// Usa accelerator para converter Enter em Exec
if (!TranslateAccelerator (hMainWnd, hAccel, &msg))
TranslateMessage (&msg);
DispatchMessage (&msg);
}

// Limpeza final
CloseHandle (hevBreak);
CloseHandle (hevInput);
return 0;
}
A criação da janela não tem nada de especial, uma classe de janela é registrada e a janela é criada (vide rotina CriaJanela).

A rotina de janela cuida da interface com o operador:
LRESULT CALLBACK MainWinProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
char szCmd [40]; // comando
PAINTSTRUCT ps;
HDC hdc;


switch (message)
{
case WM_CREATE:
// salva o handle, cria o fonte de letra e acerta o tamanho
hMainWnd = hwnd;
CriaFonte ();
MoveWindow (hwnd, 0, 0, 90*nCxCar, 32*nCyCar, FALSE);

// cria controles auxiliares e os inicia
hEdit = CreateWindow ("edit", "",
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_UPPERCASE,
2*nCxCar, 28*nCyCar, 72*nCxCar, nCyCar+2,
hwnd, (HMENU) IDC_INPUT, hInst, 0);
hExec = CreateWindow ("button", "Exec",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
76*nCxCar, 28*nCyCar-3, 7*nCxCar, nCyCar+6,
hwnd, (HMENU) IDC_EXEC, hInst, 0);
hBreak = CreateWindow ("button", "Break",
WS_CHILD | BS_PUSHBUTTON,
76*nCxCar, nCyCar-3, 7*nCxCar, nCyCar+6,
hwnd, (HMENU) IDC_BREAK, hInst, 0);
SendMessage (hEdit, EM_SETLIMITTEXT, sizeof(szCmd)-1, 0);
SendMessage (hEdit, WM_SETFONT, (WPARAM) hFont, 0);
SendMessage (hExec, WM_SETFONT, (WPARAM) hFont, 0);
SendMessage (hBreak, WM_SETFONT, (WPARAM) hFont, 0);
return 0;

case WM_SETFOCUS:
// Deixar foco sempre no editbox
if (hEdit != NULL)
SetFocus (hEdit);
return 0;

case WM_PAINT:
if (! IsIconic (hwnd))
{
// Refaz a tela
hdc = BeginPaint (hwnd, &ps);
AtlReg (hdc);
AtlMem (hdc);
AtlDisplay (hdc);
Copyright (hdc);
EndPaint (hwnd, &ps);
}
return 0;

case WM_COMMAND: // Botoes
switch (LOWORD(wParam))
{
case IDC_EXEC: // Trata texto digitado
SendMessage (hEdit, WM_GETTEXT, sizeof (szCmd), (LPARAM) szCmd);
TrataDigitacao (szCmd);
SendMessage (hEdit, WM_SETTEXT, 0, (LPARAM) "");
SetFocus (hEdit);
return 0;

case IDC_BREAK: // Interrompe o programa
SetEvent (hevBreak);
SetFocus (hEdit);
return 0;
}
break;

case WM_CLOSE:
// Destroi controles e a janela
if (hEdit != NULL)
DestroyWindow (hEdit);
if (hExec != NULL)
DestroyWindow (hExec);
if (hBreak != NULL)
DestroyWindow (hBreak);
DestroyWindow (hwnd);
DeleteObject (hFont);
return 0;

case WM_DESTROY:
// Encerra o programa
PostQuitMessage (0);
return 0;
}

// Demais mensagens vao para a rotina padrao
return DefWindowProc (hwnd, message, wParam, lParam);
}
Os detalhes mais importantes desta rotina, como indicado nos comentários, são o tratamento das seguintes mensagens:
  • WM_CREATE: acerta o tamanho da janela, cria e configura os controles auxiliares (editbox de digitação os botões Exec e Break) na janela principal.
  • WM_SETFOCURS: obriga o foco a estar sempre no editbox.
  • WM_PAINT: redesenha a tela, mais detalhes adiante.
  • WM_COMMAND: trata os botões.
  • WM_CLOSE: faz a limpeza dos controles e do fonte criado.
  • WM_DESTROY: encerra o programa.
A Representação do Computador

Como o COMP é muito simples, a sua representação também é:
static int ac;            // acumulador
static int cy; // "sobrecarga" (carry)
static int cp; // contador de programa
static int mem [100]; // memoria

#define NLIN_DISP 11
#define NCOL_DISP 80

static char szDisplay [NLIN_DISP] [NCOL_DISP+1]; // display do computador
static int nLinDisp; // linha do cursor do display
static int nColDisp; // coluna do cursor do display

// Modo atual do computador
static enum
{
MODO_CMD, // aguardando um comando
MODO_ASM, // executando comando Asm
MODO_EDIT, // executando comando Edit
MODO_RUN, // executando comando Run
MODO_INPUT // aguradando INPUT no comando Run
} nModoAtual;
Por simplificação usei int para o acumulador e memória, na verdade eles estão limitados a valores entre 0 e 9999. Da mesma forma, carry pode ser somente 0 ou 1 e cp pode variar de 0 a 99 mas também são int. O display do COMP foi organizado em 11 linhas de 80 colunas.

Por último, e mais importante, temos o "modo de operação" do COMP, que indica o que ele está fazendo. Isto é usado para definir o tratamento a ser dado ao texto digitado no textbox quando o botão Exec é pressionado.

O Monitor

Inicialmente o COMP está em MODO_CMD, no qual o texto digitado é tratado como um comando do monitor. A rotina TrataCmd examina a primeira letra do comando e executa a ação correspondente.
static void TrataCmd (char *szCmd)
{
unsigned tid;
HANDLE hThread;
char szAux [10];
int i;

// pula espacos iniciais
for (i = 0; *szCmd == ' '; i++)
;
if (szCmd[i] == 0) // ignora linha vazia
return;

// coloca a linha digitada no display
Display (szCmd, TRUE, TRUE);

// primeira letra deve ser comando
switch (szCmd[i++])
{
case 'A': // Asm
nPosAtl = PegaValor (szCmd, &i, 100);
wsprintf (szAux, "%02d: ", nPosAtl);
Display (szAux, FALSE, TRUE);
nModoAtual = MODO_ASM;
break;

case 'C': // Clear
InitComp ();
RefazTela ();
break;

case 'E': // Edit
nPosAtl = PegaValor (szCmd, &i, 100);
wsprintf (szAux, "%02d: ", nPosAtl);
Display (szAux, FALSE, TRUE);
nModoAtual = MODO_EDIT;
break;

case 'L': // Load
Load ();
Display (">", TRUE, TRUE);
break;

case 'R': // Run
cp = PegaValor (szCmd, &i, 100);
nVeloc = PegaValor (szCmd, &i, 100) * 100;
nModoAtual = MODO_RUN;
ShowWindow (hBreak, SW_SHOW);
SetFocus (hBreak);
ShowWindow (hEdit, SW_HIDE);
ShowWindow (hExec, SW_HIDE);
ResetEvent (hevBreak);
hThread = (HANDLE) _beginthreadex (NULL, 0, Exec, NULL, 0, &tid);
CloseHandle (hThread);
break;

case 'S': // Save
Save ();
Display (">", TRUE, TRUE);
break;

default:
Display ("Comando Invalido!", TRUE, TRUE);
break;
}
}
No caso do comando Asm a rotina Assembla é quem faz o parse da instrução assembly e coloca o código de máquina correspondente na memória.

Atualizando a Janela

Conforme pode ser visto no tratamento de WM_PAINT, as seguintes rotinas são responsáveis por atualizar a janela do programa: AtlReg, AtlMem, AtlDisplay e Copyright. Estas rotinas usam as funções do GDI do Windows.

A rotina Print encapsula a escrita de textos na janela:
static void Print (HDC hdc, int lin, int col, COLORREF cor, char *szTexto)
{
HFONT hOldFont;

hOldFont = (HFONT) SelectObject (hdc, hFont);
SetTextColor (hdc, cor);
SetBkColor (hdc, RGB(255,255,255));
SetBkMode (hdc, OPAQUE);
TextOut (hdc, col*nCxCar, lin*nCyCar, szTexto, strlen (szTexto));
SelectObject (hdc, hOldFont);
}
No caso da rotina Copyright, o texto é escrito duas vezes, uma em preto e outra em branco, com a defasagem de 1 pixel para produzir um efeito de baixo relevo:
static void Copyright (HDC hdc)
{
static char szTexto[] = "(C) 2004, Daniel Quadros";
HFONT hOldFont;

hOldFont = (HFONT) SelectObject (hdc, hFont);
SetBkMode (hdc, TRANSPARENT);
SetTextColor (hdc, RGB (255, 255, 255));
TextOut (hdc, 59*nCxCar+1, 26*nCyCar+6, szTexto, strlen (szTexto));
SetTextColor (hdc, RGB (0, 0, 0));
TextOut (hdc, 59*nCxCar, 26*nCyCar+5, szTexto, strlen (szTexto));
SelectObject (hdc, hOldFont);
}
Executando um programa

A execução de um programa no COMP é feita em uma thread separada, para não interferir no funcionamento da UI. A thread é criada quando o comando Run é interpretado:
        case 'R':        // Run
cp = PegaValor (szCmd, &i, 100);
nVeloc = PegaValor (szCmd, &i, 100) * 100;
nModoAtual = MODO_RUN;
ShowWindow (hBreak, SW_SHOW);
SetFocus (hBreak);
ShowWindow (hEdit, SW_HIDE);
ShowWindow (hExec, SW_HIDE);
ResetEvent (hevBreak);
hThread = (HANDLE) _beginthreadex (NULL, 0, Exec, NULL, 0, &tid);
CloseHandle (hThread);
break;
A execução em si é feita pela rotina Exec. De forma resumida, ela pega na memória a instrução, separa as várias partes e usa switches para decodificar as várias combinações. O trecho abaixo está simplificado, veja no fonte a rotina completa.
static unsigned __stdcall Exec (void *pParam)
{
int instr, op, ind, addr, val;
BOOL fCont, fErro;
HANDLE tbEvt [2];

fCont = TRUE;
fErro = FALSE;
while (fCont && !fErro)
{
// Pega a instrucao e avanca contador ("fetch")
instr = mem [cp++];
if (cp > 99)
cp = 0;

// Decodifica a instrução ("decode")
ind = instr / 1000;
op = (instr / 100) % 10;
addr = instr % 100;

// Decodifica o endereçamento
switch (ind)
{
case 0: // direto
val = mem [addr];
break;
case 1: // indireto
addr = mem [addr] % 100;
val = mem [addr];
break;
case 2: // imediato
default:
val = addr;
break;
}
if ((ind == 2) && (cTipoEnder [op] != '2'))
{
fErro = TRUE;
break;
}

// Executa a instrucao
switch (op)
{
case 0: // LDA
ac = val;
break;
case 1: // STA
mem [addr] = ac;
nPosMem = addr;
break;

// etc
}

if (fCont)
{
// testa break e controla a velociadade
fCont = WaitForSingleObject (hevBreak, nVeloc) == WAIT_TIMEOUT;
}
}

nModoAtual = MODO_CMD;
return fErro;
}
Dois eventos, cujos handles são mantidos em hevBreak e hevInput são usados para sincronizar a execução com a UI.

Quem quiser ver um exemplo bem sofisticado de simulação de computador pode examinar os fontes do POSE (Palm OS Emulator), que nomento podem ser baixados daqui.

quarta-feira, janeiro 21, 2009

Simulando Um Computador - 3/4

Neste post vamos ver a operação do Simulador de Computador. O programa simulador contém tanto o interpretador das instruções do COMP como o monitor que fornece comandos para entrada e execução de programas no COMP.

A figura abaixo mostra a tela do programa simulador (clique para ampliar):


Na parte superior são apresentados os registradores AC, C e CP e a instrução atual (IA) em código de máquina e em assembler. Em seguida é mostrado o conteúdo da memória.

A parte seguinte é o display do COMP, usado tanto pelo interpretador como pelo monitor.

Finalmente, um editbox permite a entrada de comandos para o monitor e a entrada de dados para o programa simulado. A entrada de comandos ou dados é finalizada pelo botão Exec ou pela tecla Enter.

Os Comandos do Monitor

O monitor indica com > quando está pronto para receber um novo comando. Os comandos disponíveis são:
  • A end - Asm, permite entrar com instruções em assembler. As instruções são colocadas a partir da posição de endereço end. A entrada de uma linha vazia encerra o comando.
  • C - Clear, limpa os registradores e a memória do COMP.
  • E end Edit, permite entrar diretamente com valores na memória do COMP, a partir da posição de endereço end. A entrada de uma linha vazia encerra o comando.
  • L - Load, permite carregar os registradores e a memória do COMP a partir de um arquivo gerado pelo Save.
  • R end vel - Run, executa um programa. A execução começa pelo endereço end. vel controla a velocidade da execução, definindo o tempo para executar cada instrução (em décimos de segundo).
  • S - Save, salva os registradores e a memória do COMP em um arquivo.
Um Exemplo

Quem quiser ver o simulador na prática, pode baixar o programa daqui. Execute SimComp.exe e siga os passos abaixo:
  • Digite A 0
  • Em seguida, digite as instruções em assembler abaixo (digitando ao final de cada uma). Encerre a entrada do programa digintando um com o editbox vazio.
  • Execute o programa com o comando R 0 10 .
O programa abaixo mostra na tela os números 0009, 0008, ... até 0000:
LDA $9
STA 10
OUT 10
SUB $1
BCY 6
BRN 1
STP
Na quarta e última parte vamos dar uma examinada no código do simulador e ver como ele funciona por dentro.

segunda-feira, janeiro 19, 2009

Simulando um Computador - 2/4

Nesta segunda parte vamos descrever o computador hipotético COMP.

A Arquitetura do Comp

Como já dito, o COMP é extremamente simples. Ele trabalha sobre números decimais de 4 dígitos (sem sinal) e é composto por:
  • um acumulador (AC), utilizado para cálculos;
  • um indicador de sobrecarga (C) que indica quando o resultado de uma operação não cabe no acumulador;
  • um contador de programa (CP) que indica qual posição de memória contem que contém a próxima instrução a ser executada;
  • uma memória com 100 posições, referenciadas por endereços de dois dígitos decimais (00 a 99);
  • um dispositivo de entrada (teclado); e
  • um dispositivo de saída (vídeo).
As Instruções do COMP

As instruções do COMP são codificadas em números de quatro dígitos com o formato IOMM.

O dígito O é o código da operação. A tabela abaixo descreve as operações disponíveis. E indica o endereço do operando e (E) o valor do operando. x <- y indica armazenar o valor y na posição apontada por x.
I e MM indicam o operando a ser utilizado pela instrução:
  • I = 0 endereçamento direto. MM é o endereço do operando a ser usado pela instrução;
  • I = 1 endereçamento indireto. A posição de endereço MM contém (nos dois dígitos menos significativos) o endereço do operando; e
  • I = 2 endereçamento imediato. MM é o próprio operando
A instrução STP não tem operando. As instruções STA, INP, BRN, BZR e BCY não permitem operando imediato.

Alguns exemplos de instrução, supondo que a posição 10 contém 0020 e a posição 20 contem 9901.
  • 0010 (AC <- 0020) Carrega no acumulador o valor na posição de endereço 10 (endereçamento direto).
  • 1010 (AC <- 9901) A posição de endereço 10 contém o endereço de onde será obtido o operando (endereçamento indireto).
  • 2010 (AC <- 0010) Carrega no acumulador o valor contido na própria instrução (endereçamento imediato).
  • 0020 (AC <- 9901), 2299 (AC <- 0000, C <- 1) Carrega 9901 no acumulador (endereçamento direto) e depois soma 99 (endereçamento imediato). Como o resultado (10000) não cabe no acumulador, o indicador de sobrecarga (C) é ligado para indicar o vai-um.
A Linguagem Assembly do COMP

Uma instrução do COMP é codificada em Assembler da seguinte forma:
  • mnemônico (três letras)
  • separador (zero ou mais espaços)
  • modo de endereçamento (* se indireto, $ se imediato, ausente se direto)
  • operando (um ou dois dígitos)
Re-escrevendo em assembler as três primeiras instruções do exemplo anterior:
LDA 10
LDA *10
LDA $10

domingo, janeiro 18, 2009

Simulando Um Computador - 1/4

O aprendizado de linguagem de máquina, diretamente em um computador, é uma tarefa ingrata, visto a dificuldade em se visualizar a execução e a possibilidade (frequente) de um programa incorreto “travar” a máquina.

Por estes motivos, é bastante interessante o uso de um simulador que facilite a entrada, execução e depuração de programas em linguagem de máquina. Um exemplo famoso de simulador é o MIX utilizado na famosa série The Art of Computer Programming de Donald Knuth. (Outro exemplo, não tão famoso, é o computador HIPO utilizado nas aulas de computação da USP no final dos anos 70).

O programa aqui descrito simula um computador hipotético (COMP), extremamente simples. O simulador permite a entrada de programas tanto em linguagem de máquina como assembler. A velocidade da execução é controlável e o conteúdo dos registradores e da memória são apresentados na tela.

A versão original deste programa foi feita em 1985, para ser executado em um microcomputador TK-82C e publicada na revista Microbits.

sábado, janeiro 17, 2009

Procurando O Que Falar

Espero que a situação no meu blog não esteja como neste cartum do Reality Check (clique para ampliar)

Por outro lado, o ritmo dos posts deu uma diminuída de dezembro para cá. Não que falte o que falar, mas falta tempo para organizar as idéias e reduzir o texto. O que me leva a apelar para uma idéia de outra tirinha:

Há muito tempo atrás eu publiquei alguns artigos em uma seção atualmente descontinuada do site MSDN brasil, a Sharepedia. Mais recentemente, eu publiquei aqui no blog uma lista dos artigos e links para download.

Utilizando a consagrada tecnologia cut-and-paste, vou transformar nas próximas semanas alguns destes artigos em séries de posts.

segunda-feira, janeiro 12, 2009

Fast Dial: A Polêmica Continua

No post anterior eu mencionei os problemas introduzidos com a versão 2.15 do add-on Fast Dial para o Firefox. Nestes últimos dias o desenvolvedor soltou uma enxurrada de versões; neste momento a versão atual no site da Mozilla é a 2.22b que, segundo alguns, resolve todos os problemas.

Isto não acalmou todos os usuários e avaliações negativas continuam sendo postadas. A explicação e o pedido de desculpas do desenvolvedor, embora soem sinceros, possuem um trecho que incomoda muita gente (eu inclusive):
Why did I insert spam links into your bookmarks? This seemed a good idea to me. I spent a year polishing Fast Dial and decided that I should have something in return.
Ou seja, o desenvolvedor colocou propositalmente links não solicitados com o objetivo de ganhar algo. Ele já percebeu que os usuários não gostam disto, mas será que compreendeu que é errado?

Um outro ponto que me incomoda é a grande quantidade de versões liberadas em um espaço curto de tempo. A própria descrição das diferenças entre as versões mostram que bugs estão sendo introduzidos, sinalizando que os testes não estão sendo adequados. Me parece que o mais correto seria uma volta para a última versão estável enquanto uma nova versão é cuidadosamente testada e depurada.

Mas, nestes tempos em que muita gente se orgulha de versões diárias, de betas em produção por mais de um ano e aplicações sem identificação visível de versão, este problema de qualidade me parece ser mais generalizado e profundo.

terça-feira, janeiro 06, 2009

Add-ons do Firefox: Problemas

Já mencionei no passado o meu gosto pelo Firefox e seus add-ons. Nos últimos meses uma coisa vem me incomodando: a alta frequência de atualizações dos add-ons. Pelo menos uma vez por semana aparece um aviso de uma nova versão disponível. Cada atualização envolve um download (felizmente curto na maioria dos casos), uma reiniciação do Firefox e, às vezes, a apresentação de uma página que deveria informar as novidades da nova versão.

Eu acabei pegando o mau hábito de atualizar cegamente e hoje isto me pegou de jeito. O add-on que eu mais uso (pelo serviço que ele oferece) é o Fast Dial. De forma resumida, ele apresenta uma tela com miniaturas dos sites que você marcar, facilitando a navegação. Imagine a minha surpresa, após atualizar para a versão 2.15 de verificar que:
  • um novo mecanismo de busca foi silenciosamente acrescentado.
  • que este mecanismo de busca foi marcado como padrão.
  • que foram acrescentados três sites no topo da tela, empurrando para fora sites que eu tinha acrescentado.
  • foi acrescentado no topo da tela uma caixa de busca (é claro que com o mecanismo de busca que ele tinha acrescentado), reduzindo o tamanho das miniaturas.
Estess problemas podem ser resolvidos através de uma paciente reconfiguração do Firefox e do Fast Dial, mas o ponto é que estas alterações são feitas sem aviso e consentimento do usuário. Muitos estão considerando o Fast Dial como malware devido a este comportamento.

Existem também relatos de outros problemas, como interferência com outros add-ons, que eu não observei.

A minha conclusão é clara: apesar dos add-ons serem muito úteis, eles acrescentam uma carga não desprezível ao gerenciamento do micro. A partir de agora vou aguardar alguns dias e ler com atenção os comentários antes de instalar um upgrade de add-on.

sábado, janeiro 03, 2009

Controlando um LED com um MSP430 - Parte IV

É hora de finalizar esta série de posts, com algumas considerações de ordem prática.

Onde Encontrar os Compnentes
Nas boas casas do ramo (alguém ainda fala isto?). Um lugar que eu sei que tem é na Farnell. Agradeço se algum dos meus leitores puderem indicar outras empresas que vendam componentes pela internet.

Montagem do Circuito

Praticamente qualquer tipo de montagem pode ser usado, no caso fiz em uma breadboard:


Reparar na parte inferior esquerda o conector de programação. O pino 1 é o mais à esquerda.

Preparando o Programador

Como mencionei na primeira parte, fiz um adaptação técnica no EZ430-F2013 para poder usar um conector normal no circuito. A figura abaixo (clique para ampliar) de ser suficiente para quem quiser fazer o mesmo (atenção: esta gambiarra pode danificar o programador, invalida a garantia, etc. Faça por sua própria conta e risco).


Lembrando novamente, não conectei os pinos 1 que correspondem à alimentação do circuito. Se preferir você pode conectar porém neste caso a bateria do circuito deve ser desconectada quando o programador for conectado.

Compilando e Testando o Programa C

Quando você executa o IAR Embedded Workbench, ele vai apresentar o diálogo Embedded Workbench Startup; selecione Create new project in curent workspace. Selecione C main, clique em ok e selecione o diretório e nome do projeto. Troque o código apresentado pelo que mostrei na segunda parte da série.

A parte mais importante é acertar as opções do projeto, o que você faz através de Project Options:
  • Em General Options, Target, selecione o device MSP430F2011.
  • Em Debugger, Setup, escolha o driver FET Debugger.
  • Confira em FET Debugger, Setup, que está selecionado o Texas instrument USB-IF e que target VCC é 3.0V.
Selecione Project Rebuild All. Na primeira vez vai pedir o nome do Workspace. Se você (e eu) fizemos tudo certo, vai indicar zero errors e zero warnings.

Conecte o programador a uma porta USB, ligue o conector ao circuito e selecione Project Debug. O código deve ser gravado no MSP430 e o depurador vai parar na primeira linha do código. Pressione F5 para executar o código, pressione o botão e observe o LED.

Compilando e Testando o Programa Assembly

Quando você executa o IAR Embedded Workbench, ele vai apresentar o diálogo Embedded Workbench Startup; selecione Create new project in curent workspace. Selecione asm asm, clique em ok e selecione o diretório e nome do projeto. Troque o código apresentado pelo que mostrei na terceira parte da série.

Novamente é preciso acertar as opções do projeto, o que você faz através de Project Options:
  • Em General Options, Target, selecione o device MSP430F2011.
  • Em Debugger, Setup, escolha o driver FET Debugger.
  • Confira em FET Debugger, Setup, que está selecionado o Texas instrument USB-IF e que target VCC é 3.0V.
Selecione Project Rebuild All. Na primeira vez vai pedir o nome do Workspace. Se você (e eu) fizemos tudo certo, vai indicar zero errors e zero warnings.

Conecte o programador a uma porta USB, ligue o conector ao circuito e selecione Project Debug. O código deve ser gravado no MSP430 e o depurador vai parar na primeira linha do código. Pressione F5 para executar o código, pressione o botão e observe o LED.

Para Quem Prefere Pronto

Baixe daqui os workspaces, projetos e fontes.

quinta-feira, janeiro 01, 2009

Controlando um LED com um MSP430 - Parte III

Como curiosidade vamos ver como fica o software do nosso projeto em Assembly.

A programação em Assembly no MSP430 é simplificada por uma série de fatores:
  • mapeamento contínuo da memória através de endereços de 16 bits;
  • conjunto de instruções bastante conciso ;
  • um conjunto completo de modos de endereçamento, incluindo operando imediato (de 8 ou 16 bits) e referências diretas ou inderetas à memória para os dois operandos;
  • uma boa quantidade de registradores de uso geral.
No caso do meu programa não foi preciso nem usar os registradores de uso geral, foi possível acessar sempre a memória diretamente.

Como no caso do programa em C, usei como ambiente de desenvolvimento o IAR Embedded Workbench Kickstart.

O programa ficou assim:
#include "msp430.h"

NAME main ; nome do modulo

; Vetor de Interrupções
ORG 0FFFEh ; reset
DC16 init

ORG 0FFF0h ; timer2A
DC16 t2int

; Bits correspondentes aos pinos de E/S
#define BOTAO 01h
#define LED 40h

; Valor para contar 50ms c/ clock de 12KHz
#define TEMPO_50MS 600


RSEG CSTACK ; declara segmento da pilha

RSEG DATA16_N ; segmento de dados

; Controle do LED
ModoLed: DS8 1
#define LED_APAGADO 0
#define LED_ACESO 1
#define LED_PISCANDO 2

; Controle do Botao
ModoBotao: DS8 1
#define BOTAO_ANTERIOR 01h
#define BOTAO_APERTADO 02h


; Código

RSEG CODE ; programa vai no segmento 'CODE'

init:
MOV #SFE(CSTACK), SP ; inicia a pilha
MOV.W #WDTPW+WDTHOLD,&WDTCTL ; para o WDT

BIS.B #LFXT1S1,&BCSCTL3 ; ativa VLOCLK

MOV.B #0, &P1SEL ; todos os pinos como I/O
MOV.B #0FFh & ~BOTAO, &P1DIR ; somente o botão é entrada
MOV.B #BOTAO, &P1REN ; usar resistor no botão
MOV.B #BOTAO, &P1OUT ; usar resistor no botão

MOV.B #0, &P2SEL ; todos os pinos como I/O
MOV.B #0FFh, &P2DIR ; todos os pinos como saída
MOV.B #0, &P2OUT ; todas as saidas em zero

MOV.B #0, ModoBotao ; botao solto
MOV.B #LED_APAGADO, ModoLed ; led apagado

MOV.B &CALBC1_12MHZ, &BCSCTL1 ; configura o DCO
MOV.B &CALDCO_12MHZ, &DCOCTL

MOV #TEMPO_50MS, &TACCR0 ; interrupção a cada 50ms
MOV #TASSEL_1 + MC_1 + TAIE, &TACTL


main:
BIS #GIE+CPUOFF+SCG1+SCG0,SR ; LPM3

; não deve chegar até aqui...
JMP main ; loop sem fim


; interrupção do timer2A
t2int:
CMP #10,&TAIV
JNZ t2int_fim

CMP #LED_PISCANDO, ModoLed
JNZ trata_botao
XOR.B #LED, &P2OUT ; pisca o LED

trata_botao:
BIT.B #BOTAO, &P1IN
JNZ botao_solto

BIT.B #BOTAO_ANTERIOR, ModoBotao
JNZ continua_apertado

BIS.B #BOTAO_ANTERIOR, ModoBotao ; aguarda confirmação
JMP t2int_fim

continua_apertado:
BIT.B #BOTAO_APERTADO, ModoBotao
JNZ t2int_fim

BIS.B #BOTAO_APERTADO, ModoBotao ; confirmamos aperto

CMP.B #LED_APAGADO, ModoLed
JNZ led_nao_apagado

MOV.B #LED_ACESO, ModoLed
BIS.B #LED, &P2OUT ; acende o LED
JMP t2int_fim
led_nao_apagado:
CMP.B #LED_ACESO, ModoLed
JNZ led_piscando

MOV.B #LED_PISCANDO, ModoLed ; piscar o LED
JMP t2int_fim
led_piscando:
MOV.B #LED_APAGADO, ModoLed
BIC.B #LED, &P2OUT ; apaga o LED
JMP t2int_fim
botao_solto:
BIT.B #BOTAO_ANTERIOR, ModoBotao
JZ continua_solto
BIC.B #BOTAO_ANTERIOR, ModoBotao ; aguarda confirmação
JMP t2int_fim
continua_solto:
BIC.B #BOTAO_APERTADO, ModoBotao ; confirmado

t2int_fim:
RETI

END


Na quarta e última parte veremos como montar o circuito, compilar o código e gravá-lo no microcontrolador.