1785 lines
65 KiB
C
1785 lines
65 KiB
C
/* 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 <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "ibm1130_defs.h"
|
|
#include "ibm1130res.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;
|
|
|
|
#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) {}
|
|
static void init_console_window (void) {}
|
|
static void destroy_console_window (void) {}
|
|
|
|
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 <windows.h>
|
|
|
|
#define BUTTON_WIDTH 90
|
|
#define BUTTON_HEIGHT 50
|
|
|
|
#define IDC_KEYBOARD_SELECT 0
|
|
#define IDC_DISK_UNLOCK 1
|
|
#define IDC_RUN 2
|
|
#define IDC_PARITY_CHECK 3
|
|
#define IDC_UNUSED 4
|
|
#define IDC_FILE_READY 5
|
|
#define IDC_FORMS_CHECK 6
|
|
#define IDC_POWER_ON 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, "KEYBOARD\nSELECT", 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, "PARITY\nCHECK", FALSE, FALSE, RGB(255,0,0), NULL, NULL, NULL, TRUE,
|
|
|
|
1, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "", FALSE, FALSE, 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, "FORMS\nCHECK", FALSE, FALSE, RGB(255,255,0), NULL, NULL, NULL, TRUE,
|
|
1, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "POWER\nON", FALSE, TRUE, RGB(255,255,180), 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, "CHECK\nRESET", 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;
|
|
|
|
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 = GetWindowLong(hWnd, GWL_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, int 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) GetWindowLong(hBtn, GWL_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);
|
|
}
|
|
|
|
SetWindowLong(hBtn, GWL_WNDPROC, (LONG) 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, 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;
|
|
reset_all(0);
|
|
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);
|
|
*/
|
|
}
|
|
|
|
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);
|
|
|
|
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 */
|
|
reset_all(0);
|
|
forms_check(0); /* clear forms-check status */
|
|
print_check(0);
|
|
}
|
|
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
|
|
stuff_cmd("boot cr");
|
|
#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 = GetWindowLong((HWND) lParam, GWL_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)
|
|
{
|
|
btn[IDC_KEYBOARD_SELECT].state = select;
|
|
|
|
if (select)
|
|
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, "Process \"%s\" as SCP commands?", fname);
|
|
if (IDCANCEL == MessageBox(hConsoleWnd, msg, "", MB_OKCANCEL))
|
|
return;
|
|
cardreader = FALSE; /* Process as SCP commands */
|
|
}
|
|
} else {
|
|
if (!scp_file) {
|
|
snprintf(msg, sizeof(msg)-1, "Ignoring Invalid SCP command file \"%s\"", fname);
|
|
MessageBox(hConsoleWnd, msg, "", MB_OK);
|
|
return;
|
|
}
|
|
}
|
|
/* 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);
|
|
}
|
|
|
|
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<cmdsize; i++, j++) {
|
|
ip[j].EventType = KEY_EVENT;
|
|
ip[j].Event.KeyEvent.bKeyDown = TRUE;
|
|
ip[j].Event.KeyEvent.wRepeatCount = 1;
|
|
ip[j].Event.KeyEvent.uChar.AsciiChar = cmd[i];
|
|
j++;
|
|
ip[j] = ip[j-1];
|
|
ip[j].Event.KeyEvent.bKeyDown = FALSE;
|
|
}
|
|
ip[j].EventType = KEY_EVENT;
|
|
ip[j].Event.KeyEvent.bKeyDown = TRUE;
|
|
ip[j].Event.KeyEvent.wRepeatCount = 1;
|
|
ip[j].Event.KeyEvent.uChar.AsciiChar = '\r';
|
|
j++;
|
|
ip[j] = ip[j-1];
|
|
ip[j].Event.KeyEvent.bKeyDown = FALSE;
|
|
WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ip, 2+j, &dwEventsWritten);
|
|
free(ip);
|
|
return scp_cmd;
|
|
}
|
|
|
|
/* my_yield - process GUI messages. It's not apparent why stuff_and_wait would block,
|
|
* since it sleeps in the GUI thread while scp runs in the main thread. However,
|
|
* at the end of every command scp calls update_gui, which can result in messages
|
|
* being sent to the GUI thread. So, the GUI thread has to process messages while
|
|
* stuff_and_wait is waiting.
|
|
*/
|
|
static void my_yield (void)
|
|
{
|
|
MSG msg;
|
|
/* multitask */
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
/* stuff_and_wait -- stuff a command and wait for the emulator to process the command
|
|
* and come back to prompt for another
|
|
*/
|
|
|
|
t_bool stuff_and_wait (char *cmd, int timeout, int delay)
|
|
{
|
|
long scp_cmd = stuff_cmd(cmd);
|
|
|
|
while (scp_cmd == SCP_COMMAND) {
|
|
if (timeout < 0)
|
|
return FALSE;
|
|
|
|
my_yield();
|
|
if (scp_cmd != SCP_COMMAND)
|
|
break;
|
|
|
|
Sleep(50);
|
|
if (timeout)
|
|
if ((timeout -= 50) <= 0)
|
|
timeout = -1;
|
|
|
|
my_yield();
|
|
}
|
|
|
|
if (delay)
|
|
Sleep(delay);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* remark_cmd - print a remark from inside a command processor. This routine takes
|
|
* into account the possiblity that the command might have been stuffed, in which
|
|
* case the sim> 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 */
|