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 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);
- }
- 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.
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 ú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;
- }
- }
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);
- }
- 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);
- }
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;
- 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;
- }
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.
Um comentário:
O link do simulador esta fora do ar.
Postar um comentário