/* ibm1130_gui.c: IBM 1130 CPU simulator Console Display * * Based on the SIMH package written by Robert M Supnik * * (C) Copyright 2002, Brian Knittel. * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN * RISK basis, there is no warranty of fitness for any purpose, and the rest of the * usual yada-yada. Please keep this notice and the copyright in any distributions * or modifications. * * This is not a supported product, but I welcome bug reports and fixes. * Mail to simh@ibm1130.org * * 30-Dec-05 BLK Fixed mask for IAR and SAR register display and added display * of Arithmetic Factor, per Carl Claunch. * * 09-Apr-04 BLK Changed code to use stock windows cursor IDC_HAND if available * * 02-Dec-02 BLK Changed display, added printer and card reader icons * Added drag and drop support for scripts and card decks * Added support for physical card reader and printer (hides icons) * * 17-May-02 BLK Pulled out of ibm1130_cpu.c */ /* ------------------------------------------------------------------------ * Definitions * ------------------------------------------------------------------------ */ #include #include #include #include "ibm1130_defs.h" #include "ibm1130res.h" #include "sim_tmxr.h" #define UPDATE_BY_TIMER #ifdef UPDATE_BY_TIMER # define UPDATE_INTERVAL 20 /* set to desired number of updates/second */ #else # define UPDATE_INTERVAL 5000 /* GUI: set to 100000/f where f = desired updates/second of 1130 time */ #endif #define UNIT_V_CR_EMPTY (UNIT_V_UF + 5) /* NOTE: THESE MUST MATCH THE DEFINITION IN ibm1130_cr.c */ #define UNIT_CR_EMPTY (1u << UNIT_V_CR_EMPTY) #define UNIT_V_PHYSICAL (UNIT_V_UF + 9) #define UNIT_PHYSICAL (1u << UNIT_V_PHYSICAL) #define UNIT_V_PHYSICAL_PTR (UNIT_V_UF + 10) /* NOTE: THESE MUST MATCH THE DEFINITION IN ibm1130_prt.c */ #define UNIT_PHYSICAL_PTR (1u << UNIT_V_PHYSICAL_PTR) /* I think I had it wrong; Program Load actually does start the processor after * reading in the card? */ #define PROGRAM_LOAD_STARTS_CPU /* ------------------------------------------------------------------------ * Function declarations * ------------------------------------------------------------------------ */ t_stat console_reset (DEVICE *dptr); /* ------------------------------------------------------------------------ * Console display - on Windows builds (only) this code displays the 1130 console * and toggle switches. It really enhances the experience. * * Currently, when the IPS throttle is nonzero, I update the display after every * UPDATE_INTERVAL instructions, plus or minus a random amount to avoid aliased * sampling in loops. When UPDATE_INTERVAL is defined as zero, we update every * instruction no matter what the throttle. This makes the simulator too slow * but it's cool and helpful during development. * ------------------------------------------------------------------------ */ #define UNIT_V_DISPLAY (UNIT_V_UF + 0) #define UNIT_DISPLAY (1u << UNIT_V_DISPLAY) MTAB console_mod[] = { { UNIT_DISPLAY, 0, "off", "OFF", NULL }, { UNIT_DISPLAY, UNIT_DISPLAY, "on", "ON", NULL }, { 0 } }; UNIT console_unit = {UDATA (NULL, UNIT_DISABLE|UNIT_DISPLAY, 0) }; DEVICE console_dev = { "GUI", &console_unit, NULL, console_mod, 1, 16, 16, 1, 16, 16, NULL, NULL, console_reset, NULL, NULL, NULL }; /* reset for the "console" display device */ extern UNIT cr_unit; /* pointers to 1442 and 1132 (1403) printers */ extern UNIT prt_unit; extern UNIT dsk_unit[]; extern int boot_drive; extern t_bool program_is_loaded; #ifndef GUI_SUPPORT void update_gui (int force) {} /* stubs for non-GUI builds */ void forms_check (int set) {} void print_check (int set) {} void keyboard_select (int select) {} void keyboard_selected (int select) {} void disk_ready (int ready) {} void disk_unlocked (int unlocked) {} void gui_run (int running) {} t_stat console_reset (DEVICE *dptr) {return SCPE_OK;} long stuff_cmd (char *cmd) {return 0;} t_bool stuff_and_wait (char *cmd, int timeout, int delay) {return FALSE;} char *read_cmdline (char *ptr, int size, FILE *stream) {return read_line(ptr, size, stream);} void remark_cmd (char *remark) {sim_printf("%s\n", remark);} #else static HWND hConsoleWindow = NULL; t_stat console_reset (DEVICE *dptr) { if (! sim_gui) { SETBIT(console_unit.flags, UNIT_DIS); /* disable the GUI */ CLRBIT(console_unit.flags, UNIT_DISPLAY); /* turn the GUI off */ } else { if (!hConsoleWindow) hConsoleWindow = GetConsoleWindow(); } update_gui(FALSE); return SCPE_OK; } /* scp_panic - report fatal internal programming error */ void scp_panic (const char *msg) { fprintf(stderr, "%s\n", msg); exit(1); } #ifdef _WIN32 /* only _WIN32 is defined right now */ #include #define BUTTON_WIDTH 90 #define BUTTON_HEIGHT 50 #define IDC_UNUSED 0 #define IDC_DISK_UNLOCK 1 #define IDC_RUN 2 #define IDC_KEYBOARD_SELECT 3 #define IDC_POWER_ON 4 #define IDC_FILE_READY 5 #define IDC_PARITY_CHECK 6 #define IDC_FORMS_CHECK 7 #define IDC_POWER 8 #define IDC_PROGRAM_START 9 #define IDC_PROGRAM_STOP 10 #define IDC_LOAD_IAR 11 #define IDC_KEYBOARD 12 #define IDC_IMM_STOP 13 #define IDC_RESET 14 #define IDC_PROGRAM_LOAD 15 #define IDC_TEAR 16 /* standard button */ #define IDC_1442 17 /* device images */ #define IDC_1132 18 #define LAMPTIME 500 /* 500 msec delay on updating */ #define FLASH_TIMER_ID 1 #define UPDATE_TIMER_ID 2 #define RUNSWITCH_X 689 /* center of the run mode switch dial */ #define RUNSWITCH_Y 107 #define TOGGLES_X 122 /* left edge of series of toggle switches */ #define TXTBOX_X 200 /* text labels showing attached devices */ #define TXTBOX_Y 300 #define TXTBOX_WIDTH 195 #define TXTBOX_HEIGHT 12 static BOOL class_defined = FALSE; static HWND hConsoleWnd = NULL; static HBITMAP hBitmap = NULL; static HFONT hFont = NULL; static HFONT hBtnFont = NULL; static HFONT hTinyFont = NULL; static HBRUSH hbLampOut = NULL; static HBRUSH hbWhite = NULL; static HBRUSH hbBlack = NULL; static HBRUSH hbGray = NULL; static HPEN hSwitchPen = NULL; static HPEN hWhitePen = NULL; static HPEN hBlackPen = NULL; static HPEN hLtGreyPen = NULL; static HPEN hGreyPen = NULL; static HPEN hDkGreyPen = NULL; static int hUpdateTimer = 0; static int hFlashTimer = 0; static HCURSOR hcArrow = NULL; static HCURSOR hcHand = NULL; static HINSTANCE hInstance; static HDC hCDC = NULL; static char szConsoleClassName[] = "1130CONSOLE"; static DWORD PumpID = 0; static HANDLE hPump = INVALID_HANDLE_VALUE; static int bmwid, bmht; static HANDLE hbm1442_full, hbm1442_empty, hbm1442_eof, hbm1442_middle; static HANDLE hbm1132_full, hbm1132_empty; static struct tag_btn { int x, y, wx, wy; char *txt; BOOL pushable, state; COLORREF clr; HBRUSH hbrLit, hbrDark; HWND hBtn; BOOL subclassed; } btn[] = { 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "", FALSE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, 0, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "DISK\nUNLOCK", FALSE, TRUE, RGB(255,255,180), NULL, NULL, NULL, TRUE, 0, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "RUN", FALSE, FALSE, RGB(0,255,0), NULL, NULL, NULL, TRUE, 0, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "K B\nSELECT", FALSE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, 1, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "POWER\nON", FALSE, TRUE, RGB(255,255,180), NULL, NULL, NULL, TRUE, 1, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "FILE\nREADY", FALSE, FALSE, RGB(0,255,0), NULL, NULL, NULL, TRUE, 1, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "PARITY\nCHECK", FALSE, FALSE, RGB(255,0,0), NULL, NULL, NULL, TRUE, 1, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "FORMS\nCHECK", FALSE, FALSE, RGB(255,255,0), NULL, NULL, NULL, TRUE, 2, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "POWER", TRUE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, 2, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "PROGRAM\nSTART", TRUE, FALSE, RGB(0,255,0), NULL, NULL, NULL, TRUE, 2, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "PROGRAM\nSTOP", TRUE, FALSE, RGB(255,0,0), NULL, NULL, NULL, TRUE, 2, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "LOAD\nIAR", TRUE, FALSE, RGB(0,0,255), NULL, NULL, NULL, TRUE, 3, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "KEYBOARD", TRUE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, 3, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "IMM\nSTOP", TRUE, FALSE, RGB(255,0,0), NULL, NULL, NULL, TRUE, 3, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "RESET", TRUE, FALSE, RGB(0,0,255), NULL, NULL, NULL, TRUE, 3, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "PROGRAM\nLOAD", TRUE, FALSE, RGB(0,0,255), NULL, NULL, NULL, TRUE, TXTBOX_X+40, TXTBOX_Y+25, 35, 12, "Tear", TRUE, FALSE, 0, NULL, NULL, NULL, FALSE, 635, 238, 110, 110, "EMPTY_1442", TRUE, FALSE, 0, NULL, NULL, NULL, FALSE, 635, 366, 110, 110, "EMPTY_1132", TRUE, FALSE, 0, NULL, NULL, NULL, FALSE, }; #define NBUTTONS (sizeof(btn) / sizeof(btn[0])) #define STATE_1442_EMPTY 0 /* no cards (no file attached) */ #define STATE_1442_FULL 1 /* cards in hopper (file attached at BOF) */ #define STATE_1442_MIDDLE 2 /* cards in hopper and stacker (file attached, neither EOF nor BOF) */ #define STATE_1442_EOF 3 /* cards in stacker (file attached, at EOF) */ #define STATE_1442_HIDDEN 4 /* simulator is attached to physical card reader */ #define STATE_1132_EMPTY 0 /* no paper hanging out of printer */ #define STATE_1132_FULL 1 /* paper hanging out of printer */ #define STATE_1132_HIDDEN 2 /* printer is attached to physical printer */ static struct tag_txtbox { int x, y; char *txt; char *unitname; int idctrl; } txtbox[] = { TXTBOX_X, TXTBOX_Y, "Card Reader", "CR", -1, TXTBOX_X, TXTBOX_Y+ 25, "Printer", "PRT", IDC_1132, TXTBOX_X, TXTBOX_Y+ 50, "Disk 0", "DSK0", -1, TXTBOX_X, TXTBOX_Y+ 75, "Disk 1", "DSK1", -1, TXTBOX_X, TXTBOX_Y+100, "Disk 2", "DSK2", -1, TXTBOX_X, TXTBOX_Y+125, "Disk 3", "DSK3", -1, TXTBOX_X, TXTBOX_Y+150, "Disk 4", "DSK4", -1, }; #define NTXTBOXES (sizeof(txtbox) / sizeof(txtbox[0])) #define TXTBOX_BOTTOM (TXTBOX_Y+150) static void init_console_window (void); static void destroy_console_window (void); LRESULT CALLBACK ConsoleWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static DWORD WINAPI Pump (LPVOID arg); static void accept_dropped_file (HANDLE hDrop); static void tear_printer (void); #define NIXOBJECT(hObj) if (hObj != NULL) {DeleteObject(hObj); hObj = NULL;} /* ------------------------------------------------------------------------ * init_console_window - display the 1130 console. Actually just creates a thread * to run the Pump routine which does the actual work. * ------------------------------------------------------------------------ */ static void init_console_window (void) { static BOOL did_atexit = FALSE; if (hConsoleWnd != NULL) return; if (PumpID == 0) hPump = CreateThread(NULL, 0, Pump, 0, 0, &PumpID); if (! did_atexit) { atexit(destroy_console_window); did_atexit = TRUE; } } /* ------------------------------------------------------------------------ * destroy_console_window - delete GDI objects. * ------------------------------------------------------------------------ */ static void destroy_console_window (void) { int i; if (hConsoleWnd != NULL) SendMessage(hConsoleWnd, WM_CLOSE, 0, 0); /* cross thread call is OK */ if (hPump != INVALID_HANDLE_VALUE) { /* this is not the most graceful way to do it */ TerminateThread(hPump, 0); hPump = INVALID_HANDLE_VALUE; PumpID = 0; hConsoleWnd = NULL; } if (hCDC != NULL) { DeleteDC(hCDC); hCDC = NULL; } NIXOBJECT(hBitmap) NIXOBJECT(hbLampOut) NIXOBJECT(hFont) NIXOBJECT(hBtnFont); NIXOBJECT(hTinyFont); NIXOBJECT(hcHand) NIXOBJECT(hSwitchPen) NIXOBJECT(hLtGreyPen) NIXOBJECT(hGreyPen) NIXOBJECT(hDkGreyPen) for (i = 0; i < NBUTTONS; i++) { NIXOBJECT(btn[i].hbrLit); NIXOBJECT(btn[i].hbrDark); } /* if (class_defined) { UnregisterClass(hInstance, szConsoleClassName); class_defined = FALSE; } */ } /* ------------------------------------------------------------------------ * these variables hold the displayed versions of the system registers * ------------------------------------------------------------------------ */ static int shown_iar = 0, shown_sar = 0, shown_sbr = 0, shown_afr = 0, shown_acc = 0, shown_ext = 0; static int shown_op = 0, shown_tag = 0, shown_irq = 0, shown_ccc = 0, shown_cnd = 0, shown_wait = 0; static int shown_ces = 0, shown_arf = 0, shown_runmode = MODE_RUN; static int CND; /* ------------------------------------------------------------------------ * RedrawRegion - mark a region for redrawing without background erase * ------------------------------------------------------------------------ */ static void RedrawRegion (HWND hWnd, int left, int top, int right, int bottom) { RECT r; r.left = left; r.top = top; r.right = right; r.bottom = bottom; InvalidateRect(hWnd, &r, FALSE); } /* ------------------------------------------------------------------------ * RepaintRegion - mark a region for redrawing with background erase * ------------------------------------------------------------------------ */ static void RepaintRegion (HWND hWnd, int left, int top, int right, int bottom) { RECT r; r.left = left; r.top = top; r.right = right; r.bottom = bottom; InvalidateRect(hWnd, &r, TRUE); } /* ------------------------------------------------------------------------ * update_gui - sees if anything on the console display has changed, and invalidates * the changed regions. Then it calls UpdateWindow to force an immediate repaint. This * function (update_gui) should probably not be called every time through the main * instruction loop but it should be called at least whenever wait_state or int_req change, and then * every so many instructions. It's also called after every simh command so manual changes are * reflected instantly. * ------------------------------------------------------------------------ */ void update_gui (BOOL force) { int i; BOOL state; static int in_here = FALSE; static int32 displayed = 0; RECT xin; if ((int32)(console_unit.flags & UNIT_DISPLAY) != displayed) { /* setting has changed */ displayed = console_unit.flags & UNIT_DISPLAY; if (displayed) init_console_window(); else destroy_console_window(); } if (hConsoleWnd == NULL) return; GUI_BEGIN_CRITICAL_SECTION /* only one thread at a time, please */ if (in_here) { GUI_END_CRITICAL_SECTION return; } in_here = TRUE; GUI_END_CRITICAL_SECTION CND = 0; /* combine carry and V as two bits */ if (C) CND |= 2; if (V) CND |= 1; if ((boot_drive<0) || (!program_is_loaded)) { boot_drive = CES & 7; if (boot_drive > 4) boot_drive = -1; } if ((boot_drive>=0) && (dsk_unit[boot_drive].flags&UNIT_ATT)) { disk_ready(TRUE); disk_unlocked(FALSE); } else { disk_ready(FALSE); disk_unlocked(TRUE); } int_lamps |= int_req; if (ipl >= 0) int_lamps |= (0x20 >> ipl); if (RUNMODE == MODE_LOAD) SBR = CES; /* in load mode, SBR follows the console switches */ if (IAR != shown_iar) {shown_iar = IAR; RedrawRegion(hConsoleWnd, 75, 8, 364, 32);} /* lamps: don't bother erasing bkgnd */ if (SAR != shown_sar) {shown_sar = SAR; RedrawRegion(hConsoleWnd, 75, 42, 364, 65);} if (ARF != shown_arf) {shown_arf = ARF; RedrawRegion(hConsoleWnd, 75, 114, 364, 136);} if (ACC != shown_acc) {shown_acc = ACC; RedrawRegion(hConsoleWnd, 75, 141, 364, 164);} if (EXT != shown_ext) {shown_ext = EXT; RedrawRegion(hConsoleWnd, 75, 174, 364, 197);} if (SBR != shown_sbr) {shown_sbr = SBR; RedrawRegion(hConsoleWnd, 75, 77, 364, 97);} if (OP != shown_op) {shown_op = OP; RedrawRegion(hConsoleWnd, 501, 8, 595, 32);} if (TAG != shown_tag) {shown_tag = TAG; RedrawRegion(hConsoleWnd, 501, 77, 595, 97);} if (int_lamps != shown_irq) {shown_irq = int_lamps; RedrawRegion(hConsoleWnd, 501, 108, 595, 130);} if (CCC != shown_ccc) {shown_ccc = CCC; RedrawRegion(hConsoleWnd, 501, 141, 595, 164);} if (CND != shown_cnd) {shown_cnd = CND; RedrawRegion(hConsoleWnd, 501, 174, 595, 197);} if ((wait_state|wait_lamp) != shown_wait) {shown_wait= (wait_state|wait_lamp); RedrawRegion(hConsoleWnd, 380, 77, 414, 97);} if (CES != shown_ces) {shown_ces = CES; RepaintRegion(hConsoleWnd, TOGGLES_X-7, 230, TOGGLES_X+360, 275);} /* console entry sw: do erase bkgnd */ if (RUNMODE != shown_runmode) {shown_runmode = RUNMODE;RepaintRegion(hConsoleWnd, RUNSWITCH_X-50, RUNSWITCH_Y-50, RUNSWITCH_X+50, RUNSWITCH_Y+50);} int_lamps = 0; /* this loop works with lamp buttons that are calculated on-the-fly only */ for (i = 0; i < NBUTTONS; i++) { if (btn[i].pushable) continue; switch (i) { case IDC_RUN: state = hFlashTimer || (running && ! wait_state); break; /* this button is always off case IDC_PARITY_CHECK */ /* these buttons are enabled/disabled directly case IDC_POWER_ON: case IDC_FILE_READY: case IDC_FORMS_CHECK: case IDC_KEYBOARD_SELECT: case IDC_DISK_UNLOCK: */ default: continue; } if (state != btn[i].state) { /* state has changed */ EnableWindow(btn[i].hBtn, state); btn[i].state = state; } } if (force) { /* if force flag is set, update text region */ SetRect(&xin, TXTBOX_X, TXTBOX_Y, TXTBOX_X+TXTBOX_WIDTH, TXTBOX_BOTTOM+2*TXTBOX_HEIGHT); InvalidateRect(hConsoleWnd, &xin, TRUE); } state = ((cr_unit.flags & UNIT_ATT) == 0) ? STATE_1442_EMPTY : (cr_unit.flags & UNIT_PHYSICAL) ? STATE_1442_HIDDEN : (cr_unit.flags & UNIT_CR_EMPTY) ? STATE_1442_EOF : cr_unit.pos ? STATE_1442_MIDDLE : STATE_1442_FULL; if (state != btn[IDC_1442].state) { if (state == STATE_1442_HIDDEN) ShowWindow(btn[IDC_1442].hBtn, SW_HIDE); else { if (btn[IDC_1442].state == STATE_1442_HIDDEN) ShowWindow(btn[IDC_1442].hBtn, SW_SHOWNA); SendMessage(btn[IDC_1442].hBtn, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) ( (state == STATE_1442_FULL) ? hbm1442_full : (state == STATE_1442_MIDDLE) ? hbm1442_middle : (state == STATE_1442_EOF) ? hbm1442_eof : hbm1442_empty)); } btn[IDC_1442].state = state; } state = ((prt_unit.flags & UNIT_ATT) == 0) ? STATE_1132_EMPTY : (prt_unit.flags & UNIT_PHYSICAL_PTR) ? STATE_1132_HIDDEN : prt_unit.pos ? STATE_1132_FULL : STATE_1132_EMPTY; if (state != btn[IDC_1132].state) { if (state == STATE_1132_HIDDEN) ShowWindow(btn[IDC_1132].hBtn, SW_HIDE); else { if (btn[IDC_1132].state == STATE_1132_HIDDEN) ShowWindow(btn[IDC_1132].hBtn, SW_SHOWNA); SendMessage(btn[IDC_1132].hBtn, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) ( (state == STATE_1132_FULL) ? hbm1132_full : hbm1132_empty)); } btn[IDC_1132].state = state; } in_here = FALSE; } WNDPROC oldButtonProc = NULL; /* ------------------------------------------------------------------------ * ------------------------------------------------------------------------ */ LRESULT CALLBACK ButtonProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int i; i = GetWindowLongPtr(hWnd, GWLP_ID); if (! btn[i].pushable) { if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP || uMsg == WM_LBUTTONDBLCLK) return 0; if (uMsg == WM_CHAR) if ((TCHAR) wParam == ' ') return 0; } return CallWindowProc(oldButtonProc, hWnd, uMsg, wParam, lParam); } /* ------------------------------------------------------------------------ * ------------------------------------------------------------------------ */ static int occurs (char *txt, char ch) { int count = 0; while (*txt) if (*txt++ == ch) count++; return count; } /* ------------------------------------------------------------------------ * turns out to get properly colored buttons you have to paint them yourself. Sheesh. * On the plus side, this lets do a better job of aligning the button text than * the button would by itself. * ------------------------------------------------------------------------ */ void PaintButton (LPDRAWITEMSTRUCT dis) { int i = dis->CtlID, nc, nlines, x, y, dy; BOOL down = dis->itemState & ODS_SELECTED; HPEN hOldPen; HFONT hOldFont; UINT oldAlign; COLORREF oldBk; char *txt, *tstart; if (! BETWEEN(i, 0, NBUTTONS-1)) return; if (! btn[i].subclassed) return; FillRect(dis->hDC, &dis->rcItem, ((btn[i].pushable || power) && IsWindowEnabled(btn[i].hBtn)) ? btn[i].hbrLit : btn[i].hbrDark); if (! btn[i].pushable) { hOldPen = SelectObject(dis->hDC, hBlackPen); MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.top, NULL); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.bottom-1); LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-1); LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.top); } else if (down) { /* do the three-D thing */ hOldPen = SelectObject(dis->hDC, hDkGreyPen); MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-2, NULL); LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.top); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); SelectObject(dis->hDC, hWhitePen); MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-1, NULL); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.bottom-1); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); SelectObject(dis->hDC, hGreyPen); MoveToEx(dis->hDC, dis->rcItem.left+1, dis->rcItem.bottom-3, NULL); LineTo(dis->hDC, dis->rcItem.left+1, dis->rcItem.top+1); LineTo(dis->hDC, dis->rcItem.right-3, dis->rcItem.top+1); } else { hOldPen = SelectObject(dis->hDC, hWhitePen); MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-2, NULL); LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.top); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); SelectObject(dis->hDC, hDkGreyPen); MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-1, NULL); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.bottom-1); LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); SelectObject(dis->hDC, hGreyPen); MoveToEx(dis->hDC, dis->rcItem.left+1, dis->rcItem.bottom-2, NULL); LineTo(dis->hDC, dis->rcItem.right-2, dis->rcItem.bottom-2); LineTo(dis->hDC, dis->rcItem.right-2, dis->rcItem.top+1); } SelectObject(dis->hDC, hOldPen); hOldFont = SelectObject(dis->hDC, hBtnFont); oldAlign = SetTextAlign(dis->hDC, TA_CENTER|TA_TOP); oldBk = SetBkMode(dis->hDC, TRANSPARENT); txt = btn[i].txt; nlines = occurs(txt, '\n')+1; x = (dis->rcItem.left + dis->rcItem.right) / 2; y = (dis->rcItem.top + dis->rcItem.bottom) / 2; dy = 14; y = y - (nlines*dy)/2; if (down) { x += 1; y += 1; } for (;;) { for (nc = 0, tstart = txt; *txt && *txt != '\n'; txt++, nc++) ; TextOut(dis->hDC, x, y, tstart, nc); if (*txt == '\0') break; txt++; y += dy; } SetTextAlign(dis->hDC, oldAlign); SetBkMode(dis->hDC, oldBk); SelectObject(dis->hDC, hOldFont); } /* ------------------------------------------------------------------------ * ------------------------------------------------------------------------ */ HWND CreateSubclassedButton (HWND hwParent, UINT_PTR i) { HWND hBtn; int x, y; int r, g, b; y = bmht - (4*BUTTON_HEIGHT) + BUTTON_HEIGHT * btn[i].y; x = (btn[i].x < 2) ? (btn[i].x*BUTTON_WIDTH) : (598 - (4-btn[i].x)*BUTTON_WIDTH); if ((hBtn = CreateWindow("BUTTON", btn[i].txt, WS_CHILD|WS_VISIBLE|BS_CENTER|BS_MULTILINE|BS_OWNERDRAW, x, y, BUTTON_WIDTH, BUTTON_HEIGHT, hwParent, (HMENU) i, hInstance, NULL)) == NULL) return NULL; btn[i].hBtn = hBtn; if (oldButtonProc == NULL) oldButtonProc = (WNDPROC) GetWindowLongPtr(hBtn, GWLP_WNDPROC); btn[i].hbrLit = CreateSolidBrush(btn[i].clr); if (! btn[i].pushable) { r = GetRValue(btn[i].clr) / 4; g = GetGValue(btn[i].clr) / 4; b = GetBValue(btn[i].clr) / 4; btn[i].hbrDark = CreateSolidBrush(RGB(r,g,b)); EnableWindow(hBtn, FALSE); } SetWindowLongPtr(hBtn, GWLP_WNDPROC, (UINT_PTR) ButtonProc); return hBtn; } /* ------------------------------------------------------------------------ * Pump - thread that takes care of the console window. It has to be a separate thread so that it gets * execution time even when the simulator is compute-bound or IO-blocked. This routine creates the window * and runs a standard Windows message pump. The window function does the actual display work. * ------------------------------------------------------------------------ */ static DWORD WINAPI Pump (LPVOID arg) { MSG msg; int wx, wy; UINT_PTR i; RECT r, ra; BITMAP bm; WNDCLASS cd; HDC hDC; HWND hActWnd; hActWnd = GetForegroundWindow(); if (! class_defined) { /* register Window class */ hInstance = GetModuleHandle(NULL); memset(&cd, 0, sizeof(cd)); cd.style = CS_NOCLOSE; cd.lpfnWndProc = ConsoleWndProc; cd.cbClsExtra = 0; cd.cbWndExtra = 0; cd.hInstance = hInstance; cd.hIcon = NULL; cd.hCursor = hcArrow; cd.hbrBackground = NULL; cd.lpszMenuName = NULL; cd.lpszClassName = szConsoleClassName; if (! RegisterClass(&cd)) { PumpID = 0; return 0; } class_defined = TRUE; } hbWhite = GetStockObject(WHITE_BRUSH); /* create or fetch useful GDI objects */ hbBlack = GetStockObject(BLACK_BRUSH); /* create or fetch useful GDI objects */ hbGray = GetStockObject(GRAY_BRUSH); hSwitchPen = CreatePen(PS_SOLID, 5, RGB(255,255,255)); hWhitePen = GetStockObject(WHITE_PEN); hBlackPen = GetStockObject(BLACK_PEN); hLtGreyPen = CreatePen(PS_SOLID, 1, RGB(190,190,190)); hGreyPen = CreatePen(PS_SOLID, 1, RGB(128,128,128)); hDkGreyPen = CreatePen(PS_SOLID, 1, RGB(64,64,64)); hcArrow = LoadCursor(NULL, IDC_ARROW); #ifdef IDC_HAND hcHand = LoadCursor(NULL, IDC_HAND); /* use stock object provided by Windows */ if (hcHand == NULL) hcHand = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYHAND)); #else hcHand = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYHAND)); #endif if (hBitmap == NULL) hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_CONSOLE)); if (hbLampOut == NULL) hbLampOut = CreateSolidBrush(RGB(50,50,50)); if (hFont == NULL) hFont = CreateFont(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FIXED_PITCH, FF_SWISS, "Arial"); if (hBtnFont == NULL) hBtnFont = CreateFont(-12, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FIXED_PITCH, FF_SWISS, "Arial"); if (hTinyFont == NULL) hTinyFont = CreateFont(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FIXED_PITCH, FF_SWISS, "Arial"); if (hConsoleWnd == NULL) { /* create window */ if ((hConsoleWnd = CreateWindow(szConsoleClassName, "IBM 1130", WS_OVERLAPPED|WS_CLIPCHILDREN, 0, 0, 200, 200, NULL, NULL, hInstance, NULL)) == NULL) { PumpID = 0; return 0; } DragAcceptFiles(hConsoleWnd, TRUE); /* let it accept dragged files (scripts) */ } GetObject(hBitmap, sizeof(bm), &bm); /* get bitmap size */ bmwid = bm.bmWidth; bmht = bm.bmHeight; for (i = 0; i < NBUTTONS; i++) { if (! btn[i].subclassed) continue; CreateSubclassedButton(hConsoleWnd, i); if (! btn[i].pushable) EnableWindow(btn[i].hBtn, btn[i].state); } /* This isn't needed anymore, now that we have the big printer icon -- it acts like a button now * i = IDC_TEAR; * btn[i].hBtn = CreateWindow("BUTTON", btn[i].txt, WS_CHILD|WS_VISIBLE|BS_CENTER, * btn[i].x, btn[i].y, btn[i].wx, btn[i].wy, hConsoleWnd, (HMENU) i, hInstance, NULL); * * SendMessage(btn[i].hBtn, WM_SETFONT, (WPARAM) hTinyFont, TRUE); */ hbm1442_full = LoadBitmap(hInstance, "FULL_1442"); hbm1442_empty = LoadBitmap(hInstance, "EMPTY_1442"); hbm1442_eof = LoadBitmap(hInstance, "EOF_1442"); hbm1442_middle = LoadBitmap(hInstance, "MIDDLE_1442"); hbm1132_full = LoadBitmap(hInstance, "FULL_1132"); hbm1132_empty = LoadBitmap(hInstance, "EMPTY_1132"); i = IDC_1442; btn[i].hBtn = CreateWindow("STATIC", btn[i].txt, WS_CHILD|WS_VISIBLE|SS_BITMAP|SS_SUNKEN|WS_BORDER|SS_REALSIZEIMAGE|SS_NOTIFY, btn[i].x, btn[i].y, btn[i].wx, btn[i].wy, hConsoleWnd, (HMENU) i, hInstance, NULL); btn[i].state = STATE_1442_EMPTY; wx = SendMessage(btn[i].hBtn, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbm1442_empty); i = IDC_1132; btn[i].hBtn = CreateWindow("STATIC", btn[i].txt, WS_CHILD|WS_VISIBLE|SS_BITMAP|SS_SUNKEN|WS_BORDER|SS_REALSIZEIMAGE|SS_NOTIFY, btn[i].x, btn[i].y, btn[i].wx, btn[i].wy, hConsoleWnd, (HMENU) i, hInstance, NULL); btn[i].state = FALSE; wx = SendMessage(btn[i].hBtn, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbm1132_empty); GetWindowRect(hConsoleWnd, &r); /* get window size as created */ wx = r.right - r.left + 1; wy = r.bottom - r.top + 1; if (hCDC == NULL) { /* get a memory DC and select the bitmap into ti */ hDC = GetDC(hConsoleWnd); hCDC = CreateCompatibleDC(hDC); SelectObject(hCDC, hBitmap); ReleaseDC(hConsoleWnd, hDC); } GetClientRect(hConsoleWnd, &r); wx = (wx - r.right - 1) + bmwid; /* compute new desired size based on how client area came out */ wy = (wy - r.bottom - 1) + bmht; MoveWindow(hConsoleWnd, 0, 0, wx, wy, FALSE); /* resize window */ ShowWindow(hConsoleWnd, SW_SHOWNOACTIVATE); /* display it */ UpdateWindow(hConsoleWnd); if (hActWnd != NULL) { /* bring console (sim) window back to top */ GetWindowRect(hConsoleWnd, &r); ShowWindow(hActWnd, SW_NORMAL); /* and move it just below the display window */ SetWindowPos(hActWnd, HWND_TOP, 0, r.bottom, 0, 0, SWP_NOSIZE); GetWindowRect(hActWnd, &ra); if (ra.bottom >= GetSystemMetrics(SM_CYSCREEN)) { /* resize if it goes of bottom of screen */ ra.bottom = GetSystemMetrics(SM_CYSCREEN) - 1; SetWindowPos(hActWnd, 0, 0, 0, ra.right-ra.left+1, ra.bottom-ra.top+1, SWP_NOZORDER|SWP_NOMOVE); } } if (running) /* if simulator is already running, start update timer */ gui_run(TRUE); while (GetMessage(&msg, hConsoleWnd, 0, 0)) { /* message pump - this basically loops forevermore */ TranslateMessage(&msg); DispatchMessage(&msg); } if (hConsoleWnd != NULL) { DragAcceptFiles(hConsoleWnd, FALSE); /* unregister as drag/drop target */ DestroyWindow(hConsoleWnd); /* but if a quit message got posted, clean up */ hConsoleWnd = NULL; } PumpID = 0; return 0; } /* ------------------------------------------------------------------------ * DrawBits - starting at position (x,y), draw lamps for nbits bits of word 'bits', looking only at masked bits * ------------------------------------------------------------------------ */ static void DrawBits (HDC hDC, int x, int y, int bits, int nbits, int mask, char *syms) { int i, b = 0x0001 << (nbits-1); for (i = 0; i < nbits; i++, b >>= 1) { if (mask & b) { /* select white or black lettering then write 2 chars */ SetTextColor(hDC, (b & bits && power) ? RGB(255,255,255) : RGB(0,0,0)); TextOut(hDC, x, y, syms, 2); } syms += 2; /* go to next symbol pair */ if (i < 10) x += 15; /* step between lamps */ else x += 19; if (x < 500) { if (b & 0x1110) x += 10; /* step over nibble divisions on left side */ else if (b & 0x0001) x += 9; } } } /* ------------------------------------------------------------------------ * DrawToggles - display the console sense switches * ------------------------------------------------------------------------ */ static void DrawToggles (HDC hDC, int bits) { int b, x; for (b = 0x8000, x = TOGGLES_X; b != 0; b >>= 1) { if (shown_ces & b) { /* up */ SelectObject(hDC, hbWhite); Rectangle(hDC, x, 232, x+9, 240); SelectObject(hDC, hbGray); Rectangle(hDC, x, 239, x+9, 255); } else { /* down */ SelectObject(hDC, hbWhite); Rectangle(hDC, x, 263, x+9, 271); SelectObject(hDC, hbGray); Rectangle(hDC, x, 248, x+9, 264); } x += (b & 0x1111) ? 31 : 21; } } /* ------------------------------------------------------------------------ * DrawRunmode - draw the run mode rotary switch's little tip * ------------------------------------------------------------------------ */ void DrawRunmode (HDC hDC, int mode) { double angle = (mode*45. + 90.) * 3.1415926 / 180.; /* convert mode position to angle in radians */ double ca, sa; /* sine and cosine */ int x0, y0, x1, y1; HPEN hOldPen; ca = cos(angle); sa = sin(angle); x0 = RUNSWITCH_X + (int) (20.*ca + 0.5); /* inner radius */ y0 = RUNSWITCH_Y - (int) (20.*sa + 0.5); x1 = RUNSWITCH_X + (int) (25.*ca + 0.5); /* outer radius */ y1 = RUNSWITCH_Y - (int) (25.*sa + 0.5); hOldPen = SelectObject(hDC, hSwitchPen); MoveToEx(hDC, x0, y0, NULL); LineTo(hDC, x1, y1); SelectObject(hDC, hOldPen); } /* ------------------------------------------------------------------------ * HandleClick - handle mouse clicks on the console window. Now we just * look at the console sense switches. Actual says this is a real click, rather * than a mouse-region test. Return value TRUE means the cursor is over a hotspot. * ------------------------------------------------------------------------ */ static BOOL HandleClick (HWND hWnd, int xh, int yh, BOOL actual, BOOL rightclick) { int b, x, r, ang, i; for (b = 0x8000, x = TOGGLES_X; b != 0; b >>= 1) { if (BETWEEN(xh, x-3, x+8+3) && BETWEEN(yh, 230, 275)) { if (actual) { CES ^= b; /* a hit. Invert the bit and redisplay */ update_gui(TRUE); } return TRUE; } x += (b & 0x1111) ? 31 : 21; } if (BETWEEN(xh, RUNSWITCH_X-50, RUNSWITCH_X+50) && BETWEEN(yh, RUNSWITCH_Y-50, RUNSWITCH_Y+50)) { /* hit near rotary switch */ ang = (int) (atan2(RUNSWITCH_X-xh, RUNSWITCH_Y-yh)*180./3.1415926); /* this does implicit 90 deg rotation by the way */ r = (int) sqrt((xh-RUNSWITCH_X)*(xh-RUNSWITCH_X)+(yh-RUNSWITCH_Y)*(yh-RUNSWITCH_Y)); if (r > 12) { for (i = MODE_LOAD; i <= MODE_INT_RUN; i++) { if (BETWEEN(ang, i*45-12, i*45+12)) { if (actual) { RUNMODE = i; update_gui(TRUE); } return TRUE; } } } } return FALSE; } /* ------------------------------------------------------------------------ * DrawConsole - refresh the console display. (This routine could be sped up by intersecting * the various components' bounding rectangles with the repaint rectangle. The bounding rects * could be put into an array and used both here and in the refresh routine). * * RedrawRegion -> force repaint w/o background redraw. used for lamps which are drawn in the same place in either state * RepaintRegion-> repaint with background redraw. Used for toggles which change position. * ------------------------------------------------------------------------ */ static void DrawConsole (HDC hDC, PAINTSTRUCT *ps) { static char digits[] = " 0 1 2 3 4 5 6 7 8 9101112131415"; static char cccs[] = "3216 8 4 2 1"; static char cnds[] = " C V"; static char waits[] = " W"; HFONT hOldFont, hOldBrush; RECT xout, xin; int i, n; DEVICE *dptr; UNIT *uptr; t_bool enab; char nametemp[50], *dispname; hOldFont = SelectObject(hDC, hFont); /* use that tiny font */ hOldBrush = SelectObject(hDC, hbWhite); SetBkMode(hDC, TRANSPARENT); /* overlay letters w/o changing background */ DrawBits(hDC, 76, 15, shown_iar, 16, mem_mask, digits); /* register holds only 15 bits */ DrawBits(hDC, 76, 48, shown_sar, 16, mem_mask, digits); /* but let's display only used bits */ DrawBits(hDC, 76, 81, shown_sbr, 16, 0xFFFF, digits); DrawBits(hDC, 76, 114, shown_arf, 16, 0xFFFF, digits); DrawBits(hDC, 76, 147, shown_acc, 16, 0xFFFF, digits); DrawBits(hDC, 76, 180, shown_ext, 16, 0xFFFF, digits); DrawBits(hDC, 506, 15, shown_op, 5, 0x001F, digits); DrawBits(hDC, 506, 81, shown_tag, 4, 0x0007, digits); DrawBits(hDC, 506, 114, shown_irq, 6, 0x003F, digits); DrawBits(hDC, 506, 147, shown_ccc, 6, 0x003F, cccs); DrawBits(hDC, 506, 180, shown_cnd, 2, 0x0003, cnds); DrawBits(hDC, 390, 81, shown_wait?1:0,1, 0x0001, waits); DrawToggles(hDC, shown_ces); DrawRunmode(hDC, shown_runmode); SelectObject(hDC, hOldFont); SelectObject(hDC, hOldBrush); SetBkColor(hDC, RGB(0,0,0)); SetRect(&xin, TXTBOX_X, TXTBOX_Y, TXTBOX_X+TXTBOX_WIDTH, TXTBOX_BOTTOM+TXTBOX_HEIGHT); if (IntersectRect(&xout, &xin, &ps->rcPaint)) { hOldFont = SelectObject(hDC, hTinyFont); for (i = 0; i < NTXTBOXES; i++) { enab = FALSE; dptr = find_unit(txtbox[i].unitname, &uptr); if (dptr != NULL && uptr != NULL) { if (uptr->flags & UNIT_DIS) { SetTextColor(hDC, RGB(128,0,0)); } else if (uptr->flags & UNIT_ATT) { SetTextColor(hDC, RGB(0,0,255)); if ((n = strlen(uptr->filename)) > 30) { strcpy(nametemp, "..."); strcpy(nametemp+3, uptr->filename+n-30); dispname = nametemp; } else dispname = uptr->filename; TextOut(hDC, txtbox[i].x+25, txtbox[i].y+TXTBOX_HEIGHT, dispname, strlen(dispname)); SetTextColor(hDC, RGB(255,255,255)); enab = TRUE; } else { SetTextColor(hDC, RGB(128,128,128)); } TextOut(hDC, txtbox[i].x, txtbox[i].y, txtbox[i].txt, strlen(txtbox[i].txt)); } if (txtbox[i].idctrl >= 0) EnableWindow(btn[txtbox[i].idctrl].hBtn, enab); } SelectObject(hDC, hOldFont); } } /* ------------------------------------------------------------------------ * Handles button presses. Remember that this occurs in the context of * the Pump thread, not the simulator thread. * ------------------------------------------------------------------------ */ void flash_run (void) { EnableWindow(btn[IDC_RUN].hBtn, TRUE); /* enable the run lamp */ if (hFlashTimer != 0) KillTimer(hConsoleWnd, FLASH_TIMER_ID); /* (re)schedule lamp update */ hFlashTimer = SetTimer(hConsoleWnd, FLASH_TIMER_ID, LAMPTIME, NULL); } void gui_run (int running) { if (running && hUpdateTimer == 0 && hConsoleWnd != NULL) { hUpdateTimer = SetTimer(hConsoleWnd, UPDATE_TIMER_ID, 1000/UPDATE_INTERVAL, NULL); } else if (hUpdateTimer != 0 && ! running) { KillTimer(hConsoleWnd, UPDATE_TIMER_ID); hUpdateTimer = 0; } flash_run(); /* keep run lamp active for a while after we stop running */ } void HandleCommand (HWND hWnd, WORD wNotify, WORD idCtl, HWND hwCtl) { int i; switch (idCtl) { case IDC_POWER: /* toggle system power */ power = ! power; if (running && ! power) { /* turning off */ reason = STOP_POWER_OFF; /* wait for execution thread to exit */ /* this prevents message pump from running, which unfortunately locks up * the emulator thread when it calls gui_run(FALSE) which calls EnableWindow on the Run lamp * while (running) * Sleep(10); */ } stuff_and_wait("reset", 0, 500); btn[IDC_POWER_ON].state = power; EnableWindow(btn[IDC_POWER_ON].hBtn, power); for (i = 0; i < NBUTTONS; i++) /* repaint all of the lamps */ if (! btn[i].pushable) InvalidateRect(btn[i].hBtn, NULL, TRUE); if ((cr_unit.flags & UNIT_ATT) && (btn[IDC_1442].state!=STATE_1442_FULL)) { stuff_and_wait("detach cr", 0, 500); update_gui(TRUE); } program_is_loaded = FALSE; break; case IDC_PROGRAM_START: /* begin execution */ if (! running) { switch (RUNMODE) { case MODE_INT_RUN: case MODE_RUN: case MODE_SI: stuff_cmd("cont"); break; case MODE_DISP: /* display core and advance IAR */ ReadW(IAR); IAR = IAR+1; flash_run(); /* illuminate run lamp for .5 sec */ break; case MODE_LOAD: /* store to core and advance IAR */ WriteW(IAR, CES); IAR = IAR+1; flash_run(); break; } } break; case IDC_PROGRAM_STOP: if (running) { /* potential race condition here */ GUI_BEGIN_CRITICAL_SECTION SETBIT(con_dsw, CPU_DSW_PROGRAM_STOP); SETBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP); int_req |= INT_REQ_5; /* note: calc_ints() is not needed in this case */ int_lamps |= INT_REQ_5; GUI_END_CRITICAL_SECTION } break; case IDC_LOAD_IAR: if (! running) { IAR = CES & mem_mask; /* set IAR from console entry switches */ } break; case IDC_KEYBOARD: /* toggle between console/keyboard mode */ break; case IDC_IMM_STOP: if (running) { reason = STOP_IMMEDIATE; /* terminate execution without setting wait_mode */ /* wait for execution thread to exit */ /* this prevents message pump from running, which unfortunately locks up * the emulator thread when it calls gui_run(FALSE) which calls EnableWindow on the Run lamp * while (running) * Sleep(10); */ } break; case IDC_RESET: if (! running) { /* check-reset is disabled while running */ stuff_and_wait("reset", 0, 500); forms_check(0); /* clear forms-check status */ print_check(0); } if ((cr_unit.flags & UNIT_ATT) && (btn[IDC_1442].state!=STATE_1442_FULL)) { stuff_and_wait("detach cr", 0, 500); update_gui(TRUE); } program_is_loaded = FALSE; break; case IDC_PROGRAM_LOAD: if (! running) { /* if card reader is attached to a file, do cold start read of one card */ IAR = 0; /* reset IAR */ #ifdef PROGRAM_LOAD_STARTS_CPU if (cr_unit.flags & UNIT_ATT) stuff_cmd("boot cr"); else { if (((CES & 7) <= 4) && (dsk_unit[(CES & 7)].flags&UNIT_ATT)) boot_drive = CES & 7; else boot_drive = -1; if (boot_drive >= 0) { char cmd[50]; sprintf(cmd, "boot dsk%d", boot_drive); stuff_cmd(cmd); } } #else if (cr_boot(0, NULL) != SCPE_OK) /* load boot card */ remark_cmd("IPL failed"); #endif } break; case IDC_TEAR: /* "tear off printer output" */ case IDC_1132: /* do same if they click on the printer icon */ if (btn[IDC_1132].state && (wNotify == STN_CLICKED || wNotify == STN_DBLCLK)) tear_printer(); break; case IDC_1442: if (btn[IDC_1442].state == STATE_1442_FULL || wNotify == STN_DBLCLK) { if (running) MessageBeep(0); else stuff_cmd("detach cr"); } else if (btn[IDC_1442].state != STATE_1442_EMPTY && wNotify == STN_CLICKED) { cr_rewind(); update_gui(TRUE); } break; } SetForegroundWindow(hConsoleWindow); update_gui(FALSE); } /* ------------------------------------------------------------------------ * ConsoleWndProc - window process for the console display * ------------------------------------------------------------------------ */ LRESULT CALLBACK ConsoleWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hDC; PAINTSTRUCT ps; POINT p; RECT clip, xsect, rbmp; int i; switch (uMsg) { case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: gui_run(FALSE); hConsoleWnd = NULL; break; case WM_ERASEBKGND: hDC = (HDC) wParam; GetClipBox(hDC, &clip); SetRect(&rbmp, 0, 0, bmwid, bmht); if (IntersectRect(&xsect, &clip, &rbmp)) BitBlt(hDC, xsect.left, xsect.top, xsect.right-xsect.left+1, xsect.bottom-xsect.top+1, hCDC, xsect.left, xsect.top, SRCCOPY); return TRUE; /* let Paint do this so we know what the update region is (ps.rcPaint) */ case WM_PAINT: hDC = BeginPaint(hWnd, &ps); DrawConsole(hDC, &ps); EndPaint(hWnd, &ps); break; case WM_COMMAND: /* button click */ HandleCommand(hWnd, HIWORD(wParam), LOWORD(wParam), (HWND) lParam); break; case WM_CTLCOLOREDIT: /* text color for edit controls */ SetBkColor((HDC) wParam, RGB(0,0,0)); SetTextColor((HDC) wParam, RGB(255,255,255)); break; case WM_DRAWITEM: PaintButton((LPDRAWITEMSTRUCT) lParam); break; case WM_SETCURSOR: GetCursorPos(&p); ScreenToClient(hWnd, &p); SetCursor(HandleClick(hWnd, p.x, p.y, FALSE, FALSE) ? hcHand : hcArrow); return TRUE; case WM_LBUTTONDOWN: HandleClick(hWnd, LOWORD(lParam), HIWORD(lParam), TRUE, FALSE); break; case WM_RBUTTONDOWN: HandleClick(hWnd, LOWORD(lParam), HIWORD(lParam), TRUE, TRUE); break; case WM_CTLCOLORBTN: i = GetWindowLongPtr((HWND) lParam, GWLP_ID); if (BETWEEN(i, 0, NBUTTONS-1)) return (LRESULT) (power && IsWindowEnabled((HWND) lParam) ? btn[i].hbrLit : btn[i].hbrDark); case WM_TIMER: if (wParam == FLASH_TIMER_ID && hFlashTimer != 0) { KillTimer(hWnd, FLASH_TIMER_ID); hFlashTimer = 0; } update_gui(FALSE); break; case WM_DROPFILES: accept_dropped_file((HANDLE) wParam); /* console window - dragged file is a script or card deck */ break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } enum {PRINTER_OK = 0, FORMS_CHECK = 1, PRINT_CHECK = 2, BOTH_CHECK = 3} printerstatus = PRINTER_OK; void forms_check (int set) { COLORREF oldcolor = btn[IDC_FORMS_CHECK].clr; if (set) SETBIT(printerstatus, FORMS_CHECK); else CLRBIT(printerstatus, FORMS_CHECK); btn[IDC_FORMS_CHECK].clr = (printerstatus & PRINT_CHECK) ? RGB(255,0,0) : RGB(255,255,0); btn[IDC_FORMS_CHECK].state = printerstatus; if (btn[IDC_FORMS_CHECK].hBtn != NULL) { EnableWindow(btn[IDC_FORMS_CHECK].hBtn, printerstatus); if (btn[IDC_FORMS_CHECK].clr != oldcolor) InvalidateRect(btn[IDC_FORMS_CHECK].hBtn, NULL, TRUE); /* change color in any case */ } } void print_check (int set) { COLORREF oldcolor = btn[IDC_FORMS_CHECK].clr; if (set) SETBIT(printerstatus, PRINT_CHECK); else CLRBIT(printerstatus, PRINT_CHECK); btn[IDC_FORMS_CHECK].clr = (printerstatus & PRINT_CHECK) ? RGB(255,0,0) : RGB(255,255,0); btn[IDC_FORMS_CHECK].state = printerstatus; if (btn[IDC_FORMS_CHECK].hBtn != NULL) { EnableWindow(btn[IDC_FORMS_CHECK].hBtn, printerstatus); if (btn[IDC_FORMS_CHECK].clr != oldcolor) InvalidateRect(btn[IDC_FORMS_CHECK].hBtn, NULL, TRUE); /* change color in any case */ } } void keyboard_selected (int select) { extern TMLN sim_con_ldsc; extern TMXR sim_con_tmxr; btn[IDC_KEYBOARD_SELECT].state = select; if (select && /* selected */ (sim_con_tmxr.master != 0) && /* not Telnet? */ (sim_con_ldsc.serport != 0)) /* and not serial? */ SetForegroundWindow(hConsoleWindow); if (btn[IDC_KEYBOARD_SELECT].hBtn != NULL) EnableWindow(btn[IDC_KEYBOARD_SELECT].hBtn, select); } void disk_ready (int ready) { btn[IDC_FILE_READY].state = ready; if (btn[IDC_FILE_READY].hBtn != NULL) EnableWindow(btn[IDC_FILE_READY].hBtn, ready); } void disk_unlocked (int unlocked) { btn[IDC_DISK_UNLOCK].state = unlocked; if (btn[IDC_DISK_UNLOCK].hBtn != NULL) EnableWindow(btn[IDC_DISK_UNLOCK].hBtn, unlocked); } static BOOL is_scp_file(const char *filename) { char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE]; char *argv[1] = {NULL}; FILE *f = fopen(filename, "r"); int lines = 0, comment_lines = 0; BOOL result = TRUE; size_t i; if (!f) return FALSE; while (result) { CONST char *cptr; cptr = fgets(cbuf, sizeof(cbuf), f); if (cptr == NULL) break; if (strlen(cptr) == sizeof(cbuf)-1) /* VERY long lines are not SCP commands */ result = FALSE; if (!strchr(cptr, '\n')) /* lines without newlines are not SCP commands */ result = FALSE; if (!memcmp(cptr,"!// ", 4)) /* indirect deck file literals are not SCP commands */ result = FALSE; cptr = sim_trim_endspc(cbuf); while (sim_isspace (*cptr)) /* trim leading space */ cptr++; ++lines; for (i = 0; i < strlen(cptr); i++) if ((cptr[i] & 0x80) || ((!isprint(cptr[i])) && (!isspace(cptr[i])))) result = FALSE; /* SCP files only have printable ASCII */ if ((*cptr == ';') || (*cptr == '#')) { /* ignore comments */ ++comment_lines; continue; } if (*cptr == 0) /* ignore blank */ continue; sim_sub_args (cbuf, sizeof(cbuf), argv); cptr = get_glyph_cmd (cptr, gbuf); /* get command glyph */ if (!find_cmd (gbuf)) { /* lookup command */ result = FALSE; break; } } fclose(f); if (lines == 0) /* Empty file isn't SCP */ result = FALSE; return result; } static void accept_dropped_file (HANDLE hDrop) { int nfiles; char fname[MAX_PATH], cmd[MAX_PATH+50], *deckfile; BOOL cardreader; BOOL scp_file; POINT pt; HWND hWndDrop; char msg[512]; msg[sizeof(msg)-1] = '\0'; nfiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); /* get file count, */ DragQueryFile(hDrop, 0, fname, sizeof(fname)); /* get first filename */ DragQueryPoint(hDrop, &pt); /* get location of drop */ DragFinish(hDrop); if (nfiles <= 0) /* hmm, this seems unlikely to occur, but better check */ return; if (running) { /* can only accept a drop while processor is stopped */ MessageBeep(0); return; } if ((hWndDrop = ChildWindowFromPoint(hConsoleWnd, pt)) == btn[IDC_1442].hBtn) cardreader = TRUE; /* file was dropped onto 1442 card reader */ else if (hWndDrop == NULL || hWndDrop == hConsoleWnd) cardreader = FALSE; /* file was dropped onto console window, not a button */ else { MessageBeep(0); /* file was dropped onto another button */ return; } if (nfiles > 1) { /* oops, we wouldn't know what order to read them in */ MessageBox(hConsoleWnd, "You may only drop one file at a time", "", MB_OK); return; } scp_file = is_scp_file(fname); if (cardreader) { if (scp_file) { snprintf(msg, sizeof(msg)-1, "\"%s\"\r\n\r\nContains SCP commands (not card reader input).\r\n\r\nProcess as SCP commands?", fname); if (IDYES != MessageBox(hConsoleWnd, msg, "", MB_YESNO)) return; cardreader = FALSE; /* Process as SCP commands */ } } else { if (!scp_file) { snprintf(msg, sizeof(msg)-1, "Invalid SCP command file:\r\n\r\n\"%s\"\r\n\r\nProcess as Card Reader Input?", fname); if (IDYES != MessageBox(hConsoleWnd, msg, "", MB_YESNO)) return; cardreader = TRUE; /* Process as Card Input */ } } /* if shift key is down, prepend @ to name (make it a deck file) */ deckfile = ((GetKeyState(VK_SHIFT) & 0x8000) && cardreader) ? "@" : ""; sprintf(cmd, "%s \"%s%s\"", cardreader ? "attach cr" : "do", deckfile, fname); stuff_cmd(cmd); } static void tear_printer (void) { char cmd[MAX_PATH+100], filename[MAX_PATH]; if ((prt_unit.flags & UNIT_ATT) == 0) return; if (running) { /* can only accept a drop while processor is stopped */ MessageBeep(0); return; } strcpy(filename, prt_unit.filename); /* save current attached filename */ if (! stuff_and_wait("detach prt", 1000, 0)) /* detach it */ return; sprintf(cmd, "view \"%s\"", filename); /* spawn notepad to view it */ if (! stuff_and_wait(cmd, 3000, 2000)) return; remove(filename); /* delete the file */ sprintf(cmd, "attach prt %s", filename); /* reattach */ stuff_cmd(cmd); } #ifdef XXX if ((hBtn = CreateWindow("BUTTON", btn[i].txt, WS_CHILD|WS_VISIBLE|BS_CENTER|BS_MULTILINE|BS_OWNERDRAW, x, y, BUTTON_WIDTH, BUTTON_HEIGHT, hwParent, (HMENU) i, hInstance, NULL)) == NULL) return NULL; #endif CRITICAL_SECTION critsect; void begin_critical_section (void) { static BOOL mustinit = TRUE; if (mustinit) { InitializeCriticalSection(&critsect); mustinit = FALSE; } EnterCriticalSection(&critsect); } void end_critical_section (void) { LeaveCriticalSection(&critsect); } #ifndef MIN # define MIN(a,b) (((a) <= (b)) ? (a) : (b)) #endif /* win32 - use a separate thread to read command lines so the GUI * can insert commands as well */ static HANDLE hCmdThread = NULL; static DWORD iCmdThreadID = 0; static HANDLE hCmdReadEvent = NULL; static HANDLE hCmdReadyEvent = NULL; static BOOL scp_reading = FALSE; static long scp_command = 0; static char cmdbuffer[256]; static BOOL read_exiting = FALSE; #define SCP_COMMAND InterlockedExchangeAdd(&scp_command, 0L) #define NEXT_SCP_COMMAND InterlockedIncrement(&scp_command) /* CmdThread - separate thread to read commands from stdin upon request */ static DWORD WINAPI CmdThread (LPVOID arg) { HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); DWORD dwBytesRead; for (;;) { if (WAIT_TIMEOUT == WaitForSingleObject(hCmdReadEvent, 2000)) /* wait for request */ continue; /* put breakpoint here to debug */ if (read_exiting) break; scp_reading = FALSE; if (ReadFile(hStdIn, cmdbuffer, sizeof(cmdbuffer)-1, &dwBytesRead, NULL)) { cmdbuffer[dwBytesRead] = '\0'; scp_reading = FALSE; NEXT_SCP_COMMAND; SetEvent(hCmdReadyEvent); /* notify main thread a line is ready */ } else { DWORD dwError = GetLastError(); scp_reading = FALSE; NEXT_SCP_COMMAND; } } return 0; } static void read_atexit (void) { typedef BOOL (WINAPI *_func)(HANDLE, LPOVERLAPPED); _func pCancelIoEx; read_exiting = TRUE; pCancelIoEx = (_func)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CancelIoEx"); if (pCancelIoEx) { pCancelIoEx(GetStdHandle(STD_INPUT_HANDLE), NULL); SetEvent(hCmdReadEvent); /* wake read thread */ WaitForSingleObject(hCmdThread, INFINITE); } CloseHandle(hCmdReadyEvent); hCmdReadyEvent = NULL; CloseHandle(hCmdReadEvent); hCmdReadEvent = NULL; CloseHandle(hCmdThread); hCmdThread = NULL; } char *read_cmdline (char *ptr, int size, FILE *stream) { char *cptr; if (hCmdThread == NULL) { /* set up command-reading thread */ if ((hCmdReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) scp_panic("Can't create command line read event"); if ((hCmdReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) scp_panic("Can't create command line ready event"); /* start up the command thread */ if ((hCmdThread = CreateThread(NULL, 0, CmdThread, NULL, 0, &iCmdThreadID)) == NULL) scp_panic("Unable to create command line reading thread"); atexit(read_atexit); Sleep(500); /* Let GUI threads startup and start to process messages */ } update_gui(TRUE); SetEvent(hCmdReadEvent); /* let read thread get one line */ WaitForSingleObject(hCmdReadyEvent, INFINITE); /* wait for read thread or GUI to respond */ strncpy(ptr, cmdbuffer, MIN(size, sizeof(cmdbuffer))); /* copy line to caller's buffer */ for (cptr = ptr; isspace(*cptr); cptr++) /* absorb spaces */ ; return cptr; } /* stuff_cmd - force a command into the read_cmdline output buffer. Called asynchronously by GUI */ long stuff_cmd (char *cmd) { INPUT_RECORD *ip; size_t i, j, cmdsize = strlen(cmd); DWORD dwEventsWritten; long scp_cmd = SCP_COMMAND; ip = (INPUT_RECORD *)calloc(2+2*cmdsize, sizeof(*ip)); for (i=j=0; i prompt needs to be reprinted. */ void remark_cmd (char *remark) { if (scp_reading) sim_printf("\n"); sim_printf("%s\n", remark); if (scp_reading) sim_printf("%s", sim_prompt); } #endif /* _WIN32 defined */ #endif /* GUI_SUPPORT defined */