sábado, janeiro 31, 2009

Livro do Mês: Battle of Wits

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.