Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | #include "ibm1130_defs.h"\r |
2 | \r | |
3 | /* ibm1130_gdu.c: IBM 1130 2250 Graphical Display Unit\r | |
4 | \r | |
5 | (Under construction)\r | |
6 | stuff to fix:\r | |
7 | "store revert" might be backwards?\r | |
8 | alpha keyboard is not implemented\r | |
9 | pushbuttons are not implemented\r | |
10 | there is something about interrupts being deferred during a subroutine transition?\r | |
11 | \r | |
12 | Based on the SIMH package written by Robert M Supnik\r | |
13 | \r | |
14 | * (C) Copyright 2002, Brian Knittel.\r | |
15 | * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN\r | |
16 | * RISK basis, there is no warranty of fitness for any purpose, and the rest of the\r | |
17 | * usual yada-yada. Please keep this notice and the copyright in any distributions\r | |
18 | * or modifications.\r | |
19 | *\r | |
20 | * This is not a supported product, but I welcome bug reports and fixes.\r | |
21 | * Mail to simh@ibm1130.org\r | |
22 | */\r | |
23 | \r | |
24 | #define BLIT_MODE /* define for better performance, undefine when debugging generate_image() */\r | |
25 | /* #define DEBUG_LIGHTPEN */ /* define to debug light-pen sensing */\r | |
26 | \r | |
27 | #define DEFAULT_GDU_RATE 20 /* default frame rate */\r | |
28 | #define DEFAULT_PEN_THRESHOLD 3 /* default looseness of light-pen hit */\r | |
29 | #define INDWIDTH 32 /* width of an indicator (there are two columns of these) */\r | |
30 | #define INITSIZE 512 /* initial window size */\r | |
31 | \r | |
32 | #define GDU_DSW_ORDER_CONTROLLED_INTERRUPT 0x8000\r | |
33 | #define GDU_DSW_KEYBOARD_INTERUPT 0x4000\r | |
34 | #define GDU_DSW_DETECT_INTERRUPT 0x2000\r | |
35 | #define GDU_DSW_CYCLE_STEAL_CHECK 0x1000\r | |
36 | #define GDU_DSW_DETECT_STATUS 0x0800\r | |
37 | #define GDU_DSW_LIGHT_PEN_SWITCH 0x0100\r | |
38 | #define GDU_DSW_BUSY 0x0080\r | |
39 | #define GDU_DSW_CHARACTER_MODE 0x0040\r | |
40 | #define GDU_DSW_POINT_MODE 0x0020\r | |
41 | #define GDU_DSW_ADDR_DISP 0x0003\r | |
42 | \r | |
43 | #define GDU_FKEY_DATA_AVAILABLE 0x8000\r | |
44 | #define GDU_FKEY_KEY_CODE 0x1F00\r | |
45 | #define GDU_FKEY_OVERLAY_CODE 0x00FF\r | |
46 | \r | |
47 | #define GDU_AKEY_DATA_AVAILABLE 0x8000\r | |
48 | #define GDU_AKEY_END 0x1000\r | |
49 | #define GDU_AKEY_CANCEL 0x0800\r | |
50 | #define GDU_AKEY_ADVANCE 0x0400\r | |
51 | #define GDU_AKEY_BACKSPACE 0x0200\r | |
52 | #define GDU_AKEY_JUMP 0x0100\r | |
53 | #define GDU_AKEY_KEY_CODE 0x00FF\r | |
54 | \r | |
55 | /* -------------------------------------------------------------------------------------- */\r | |
56 | \r | |
57 | #define UNIT_V_DISPLAYED (UNIT_V_UF + 0)\r | |
58 | #define UNIT_V_DETECTS_ENABLED (UNIT_V_UF + 1)\r | |
59 | #define UNIT_V_INTERRUPTS_DEFERRED (UNIT_V_UF + 2)\r | |
60 | #define UNIT_V_LARGE_CHARS (UNIT_V_UF + 3)\r | |
61 | \r | |
62 | #define UNIT_DISPLAYED (1u << UNIT_V_DISPLAYED) /* display windows is up */\r | |
63 | #define UNIT_DETECTS_ENABLED (1u << UNIT_V_DETECTS_ENABLED) /* light pen detects are enabled */\r | |
64 | #define UNIT_INTERRUPTS_DEFERRED (1u << UNIT_V_INTERRUPTS_DEFERRED) /* light pen interrupts are deferred */\r | |
65 | #define UNIT_LARGE_CHARS (1u << UNIT_V_LARGE_CHARS) /* large character mode */\r | |
66 | \r | |
67 | static t_stat gdu_reset (DEVICE *dptr);\r | |
68 | \r | |
69 | static int16 gdu_dsw = 1; /* device status word */\r | |
70 | static int16 gdu_ar = 0; /* address register */\r | |
71 | static int16 gdu_x = 0; /* X deflection */\r | |
72 | static int16 gdu_y = 0; /* Y deflection */\r | |
73 | static int16 gdu_fkey = 0; /* function keyboard register */\r | |
74 | static int16 gdu_akey = 0; /* alphanumeric keyboard register */\r | |
75 | static int16 gdu_revert = 0; /* revert address register */\r | |
76 | static int32 gdu_indicators = 0; /* programmed indicator lamps */\r | |
77 | static int32 gdu_threshold = DEFAULT_PEN_THRESHOLD; /* mouse must be within 3/1024 of line to be a hit */\r | |
78 | static int32 gdu_rate = DEFAULT_GDU_RATE; /* refresh rate. 0 = default */\r | |
79 | \r | |
80 | UNIT gdu_unit = { UDATA (NULL, 0, 0) };\r | |
81 | \r | |
82 | REG gdu_reg[] = {\r | |
83 | { HRDATA (GDUDSW, gdu_dsw, 16) }, /* device status word */\r | |
84 | { HRDATA (GDUAR, gdu_ar, 16) }, /* address register */\r | |
85 | { HRDATA (GDUXREG, gdu_x, 16) }, /* X deflection register */\r | |
86 | { HRDATA (GDUYREG, gdu_y, 16) }, /* Y deflection register */\r | |
87 | { HRDATA (GDUFKEY, gdu_fkey, 16) }, /* function keyboard register */\r | |
88 | { HRDATA (GDUAKEY, gdu_akey, 16) }, /* alphanumeric keyboard register */\r | |
89 | { HRDATA (GDUREVERT,gdu_revert, 16) }, /* revert address register */\r | |
90 | { HRDATA (GDUAKEY, gdu_indicators, 32) }, /* programmed indicators */\r | |
91 | { DRDATA (GDUTHRESH,gdu_threshold, 32) }, /* mouse closeness threshhold */\r | |
92 | { DRDATA (GDURATE, gdu_rate, 32) }, /* refresh rate in frames/sec */\r | |
93 | { NULL } };\r | |
94 | \r | |
95 | DEVICE gdu_dev = {\r | |
96 | "GDU", &gdu_unit, gdu_reg, NULL,\r | |
97 | 1, 16, 16, 1, 16, 16,\r | |
98 | NULL, NULL, gdu_reset,\r | |
99 | NULL, NULL, NULL};\r | |
100 | \r | |
101 | /* -------------------------------------------------------------------------------------- */\r | |
102 | \r | |
103 | #ifndef GUI_SUPPORT\r | |
104 | \r | |
105 | static t_stat gdu_reset (DEVICE *dptr)\r | |
106 | {\r | |
107 | return SCPE_OK;\r | |
108 | }\r | |
109 | \r | |
110 | void xio_2250_display (int32 addr, int32 func, int32 modify)\r | |
111 | {\r | |
112 | /* ignore commands if device is nonexistent */\r | |
113 | }\r | |
114 | \r | |
115 | t_bool gdu_active (void)\r | |
116 | {\r | |
117 | return 0;\r | |
118 | }\r | |
119 | \r | |
120 | /* -------------------------------------------------------------------------------------- */\r | |
121 | #else /* GUI_SUPPORT defined */\r | |
122 | \r | |
123 | /******* PLATFORM INDEPENDENT CODE ********************************************************/\r | |
124 | \r | |
125 | static int32 gdu_instaddr; // address of first word of instruction\r | |
126 | static int xmouse, ymouse, lpen_dist, lpen_dist2; // current mouse pointer, scaled closeness threshhold, same squared\r | |
127 | static double sfactor; // current scaling factor\r | |
128 | static t_bool last_abs = TRUE; // last positioning instruction was absolute\r | |
129 | static t_bool mouse_present = FALSE; // mouse is/is not in the window\r | |
130 | static void clear_interrupts (void);\r | |
131 | static void set_indicators (int32 new_inds);\r | |
132 | static void start_regeneration (void);\r | |
133 | static void halt_regeneration (void);\r | |
134 | static void draw_characters (void);\r | |
135 | static void notify_window_closed (void);\r | |
136 | \r | |
137 | // routines that must be implemented per-platform\r | |
138 | \r | |
139 | static void DrawLine(int x0, int y0, int x1, int y1);\r | |
140 | static void DrawPoint(int x, int y);\r | |
141 | static void CheckGDUKeyboard(void);\r | |
142 | static t_bool CreateGDUWindow(void);\r | |
143 | static void StartGDUUpdates(void);\r | |
144 | static void StopGDUUpdates(void);\r | |
145 | static void GetMouseCoordinates(void);\r | |
146 | static void UpdateGDUIndicators(void);\r | |
147 | static void ShowPenHit (int x, int y);\r | |
148 | static void EraseGDUScreen (void);\r | |
149 | \r | |
150 | /* -------------------------------------------------------------------------------------- */\r | |
151 | \r | |
152 | void xio_2250_display (int32 addr, int32 func, int32 modify)\r | |
153 | {\r | |
154 | switch (func) {\r | |
155 | case XIO_SENSE_DEV:\r | |
156 | ACC = (gdu_dsw & GDU_DSW_BUSY) ? GDU_DSW_BUSY : gdu_dsw;\r | |
157 | if (modify & 1)\r | |
158 | clear_interrupts();\r | |
159 | break;\r | |
160 | \r | |
161 | case XIO_READ: /* store status data into word pointed to by IOCC packet */\r | |
162 | if (gdu_dsw & GDU_DSW_BUSY) /* not permitted while device is busy */\r | |
163 | break;\r | |
164 | \r | |
165 | WriteW(addr, gdu_ar); /* save status information */\r | |
166 | WriteW(addr+1, gdu_dsw);\r | |
167 | WriteW(addr+2, gdu_x & 0x7FF);\r | |
168 | WriteW(addr+3, gdu_y & 0x7FF);\r | |
169 | WriteW(addr+4, gdu_fkey);\r | |
170 | WriteW(addr+5, gdu_akey);\r | |
171 | gdu_ar = (int16) (addr+6); /* this alters the channel address register? */\r | |
172 | \r | |
173 | clear_interrupts(); /* read status clears the interrupts */\r | |
174 | break;\r | |
175 | \r | |
176 | case XIO_WRITE:\r | |
177 | if (gdu_dsw & GDU_DSW_BUSY) /* treated as no-op if display is busy */\r | |
178 | break;\r | |
179 | \r | |
180 | if (modify & 0x80) { /* bit 8 on means set indicators, 0 means start regeneration */\r | |
181 | set_indicators((ReadW(addr) << 16) | ReadW(addr+1));\r | |
182 | }\r | |
183 | else {\r | |
184 | gdu_ar = (int16) addr;\r | |
185 | gdu_fkey = 0;\r | |
186 | gdu_akey = 0;\r | |
187 | clear_interrupts();\r | |
188 | start_regeneration();\r | |
189 | }\r | |
190 | break;\r | |
191 | \r | |
192 | case XIO_CONTROL:\r | |
193 | if (modify & 0x80) { /* bit 8 on means reset, off is no-op */\r | |
194 | gdu_reset(&gdu_dev);\r | |
195 | set_indicators((addr << 16) | addr);\r | |
196 | }\r | |
197 | break;\r | |
198 | \r | |
199 | default: /* all other commands are no-ops */\r | |
200 | break;\r | |
201 | }\r | |
202 | }\r | |
203 | \r | |
204 | static t_stat gdu_reset (DEVICE *dptr)\r | |
205 | {\r | |
206 | halt_regeneration();\r | |
207 | clear_interrupts();\r | |
208 | set_indicators(0);\r | |
209 | gdu_x = gdu_y = 512;\r | |
210 | CLRBIT(gdu_unit.flags, UNIT_INTERRUPTS_DEFERRED | UNIT_DETECTS_ENABLED | UNIT_LARGE_CHARS);\r | |
211 | gdu_dsw = 1;\r | |
212 | return SCPE_OK;\r | |
213 | }\r | |
214 | \r | |
215 | static void clear_interrupts (void)\r | |
216 | {\r | |
217 | CLRBIT(gdu_dsw, GDU_DSW_ORDER_CONTROLLED_INTERRUPT | GDU_DSW_KEYBOARD_INTERUPT | GDU_DSW_DETECT_INTERRUPT);\r | |
218 | CLRBIT(ILSW[3], ILSW_3_2250_DISPLAY);\r | |
219 | calc_ints();\r | |
220 | }\r | |
221 | \r | |
222 | static void gdu_interrupt (int32 dswbit)\r | |
223 | {\r | |
224 | SETBIT(gdu_dsw, dswbit);\r | |
225 | SETBIT(ILSW[3], ILSW_3_2250_DISPLAY);\r | |
226 | calc_ints();\r | |
227 | halt_regeneration();\r | |
228 | }\r | |
229 | \r | |
230 | static void set_indicators (int32 new_inds)\r | |
231 | {\r | |
232 | gdu_indicators = new_inds;\r | |
233 | if (gdu_unit.flags & UNIT_DISPLAYED)\r | |
234 | UpdateGDUIndicators();\r | |
235 | }\r | |
236 | \r | |
237 | static void start_regeneration (void)\r | |
238 | {\r | |
239 | SETBIT(gdu_dsw, GDU_DSW_BUSY);\r | |
240 | \r | |
241 | if ((gdu_unit.flags & UNIT_DISPLAYED) == 0) {\r | |
242 | if (! CreateGDUWindow())\r | |
243 | return;\r | |
244 | \r | |
245 | SETBIT(gdu_unit.flags, UNIT_DISPLAYED);\r | |
246 | }\r | |
247 | \r | |
248 | StartGDUUpdates();\r | |
249 | }\r | |
250 | \r | |
251 | static void halt_regeneration (void)\r | |
252 | {\r | |
253 | // halt_regeneration gets called at end of every refresh interation, so it should NOT black out the\r | |
254 | // screen -- this is why it was flickering so badly. The lower level code (called on a timer)\r | |
255 | // should check to see if GDU_DSW_BUSY is clear, and if it it still zero after several msec,\r | |
256 | // only then should it black out the screen and call StopGDUUpdates.\r | |
257 | if (gdu_dsw & GDU_DSW_BUSY) {\r | |
258 | // StopGDUUpdates(); // let lower level code discover this during next refresh\r | |
259 | CLRBIT(gdu_dsw, GDU_DSW_BUSY);\r | |
260 | }\r | |
261 | // EraseGDUScreen(); // let cessation of regeneration erase it (eventually)\r | |
262 | }\r | |
263 | \r | |
264 | static void notify_window_closed (void)\r | |
265 | {\r | |
266 | if (gdu_dsw & GDU_DSW_BUSY) {\r | |
267 | StopGDUUpdates();\r | |
268 | CLRBIT(gdu_dsw, GDU_DSW_BUSY);\r | |
269 | }\r | |
270 | \r | |
271 | CLRBIT(gdu_unit.flags, UNIT_DISPLAYED);\r | |
272 | \r | |
273 | gdu_reset(&gdu_dev);\r | |
274 | }\r | |
275 | \r | |
276 | static int32 read_gduword (void)\r | |
277 | {\r | |
278 | int32 w;\r | |
279 | \r | |
280 | w = M[gdu_ar++ & mem_mask];\r | |
281 | gdu_dsw = (int16) ((gdu_dsw & ~GDU_DSW_ADDR_DISP) | ((gdu_ar - gdu_instaddr) & GDU_DSW_ADDR_DISP));\r | |
282 | \r | |
283 | return w;\r | |
284 | }\r | |
285 | \r | |
286 | #define DIST2(x0,y0,x1,y1) (((x1)-(x0))*((x1)-(x0))+((y1)-(y0))*((y1)-(y0)))\r | |
287 | \r | |
288 | static void draw (int32 newx, int32 newy, t_bool beam)\r | |
289 | {\r | |
290 | int xmin, xmax, ymin, ymax, xd, yd;\r | |
291 | double s;\r | |
292 | int hit = FALSE;\r | |
293 | \r | |
294 | if (beam) {\r | |
295 | if (gdu_dsw & GDU_DSW_POINT_MODE) {\r | |
296 | DrawPoint(newx, newy);\r | |
297 | \r | |
298 | #ifdef DEBUG_LIGHTPEN\r | |
299 | if (DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2)\r | |
300 | hit = TRUE;\r | |
301 | #else\r | |
302 | if (gdu_unit.flags & UNIT_DETECTS_ENABLED && mouse_present)\r | |
303 | if (DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2)\r | |
304 | hit = TRUE;\r | |
305 | #endif\r | |
306 | }\r | |
307 | else {\r | |
308 | DrawLine(gdu_x, gdu_y, newx, newy);\r | |
309 | \r | |
310 | // calculate proximity of light pen to the line\r | |
311 | #ifndef DEBUG_LIGHTPEN\r | |
312 | if (gdu_unit.flags & UNIT_DETECTS_ENABLED && mouse_present) {\r | |
313 | #endif\r | |
314 | if (gdu_x <= newx)\r | |
315 | xmin = gdu_x, xmax = newx;\r | |
316 | else\r | |
317 | xmin = newx, xmax = gdu_x;\r | |
318 | \r | |
319 | if (gdu_y <= newy)\r | |
320 | ymin = gdu_y, ymax = newy;\r | |
321 | else\r | |
322 | ymin = newy, ymax = gdu_y;\r | |
323 | \r | |
324 | if (newx == gdu_x) {\r | |
325 | // line is vertical. Nearest point is an endpoint if the mouse is above or\r | |
326 | // below the line segment, otherwise the segment point at the same y as the mouse\r | |
327 | xd = gdu_x;\r | |
328 | yd = (ymouse <= ymin) ? ymin : (ymouse >= ymax) ? ymax : ymouse;\r | |
329 | \r | |
330 | if (DIST2(xd, yd, xmouse, ymouse) <= lpen_dist2)\r | |
331 | hit = TRUE;\r | |
332 | }\r | |
333 | else if (newy == gdu_y) {\r | |
334 | // line is horizontal. Nearest point is an endpoint if the mouse is to the left or\r | |
335 | // the right of the line segment, otherwise the segment point at the same x as the mouse\r | |
336 | xd = (xmouse <= xmin) ? xmin : (xmouse >= xmax) ? xmax : xmouse;\r | |
337 | yd = gdu_y;\r | |
338 | \r | |
339 | if (DIST2(xd, yd, xmouse, ymouse) <= lpen_dist2)\r | |
340 | hit = TRUE;\r | |
341 | }\r | |
342 | else {\r | |
343 | // line is diagonal. See if the mouse is inside the box lpen_dist wider than the line segment's bounding rectangle\r | |
344 | if (xmouse >= (xmin-lpen_dist) && xmouse <= (xmax+lpen_dist) && ymouse >= (ymin-lpen_dist) || ymouse <= (ymax+lpen_dist)) {\r | |
345 | // compute the point at the intersection of the line through the line segment and the normal\r | |
346 | // to that line through the mouse. This is the point on the line through the line segment\r | |
347 | // nearest the mouse\r | |
348 | \r | |
349 | s = (double)(newy - gdu_y) / (double)(newx - gdu_x); // slope of line segment\r | |
350 | xd = (int) ((ymouse + xmouse/s - gdu_y + s*gdu_x) / (s + 1./s) + 0.5);\r | |
351 | \r | |
352 | // if intersection is beyond either end of the line segment, the nearest point to the\r | |
353 | // mouse is nearest segment end, otherwise it's the computed intersection point\r | |
354 | if (xd < xmin || xd > xmax) {\r | |
355 | #ifdef DEBUG_LIGHTPEN\r | |
356 | // if it's a hit, set xd and yd so we can display the hit\r | |
357 | if (DIST2(gdu_x, gdu_y, xmouse, ymouse) <= lpen_dist2) {\r | |
358 | hit = TRUE;\r | |
359 | xd = gdu_x;\r | |
360 | yd = gdu_y;\r | |
361 | }\r | |
362 | else if (DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2) {\r | |
363 | hit = TRUE;\r | |
364 | xd = newx;\r | |
365 | yd = newy;\r | |
366 | }\r | |
367 | #else\r | |
368 | if (DIST2(gdu_x, gdu_y, xmouse, ymouse) <= lpen_dist2 || DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2)\r | |
369 | hit = TRUE;\r | |
370 | #endif\r | |
371 | }\r | |
372 | else {\r | |
373 | yd = (int) (gdu_y + s*(xd - gdu_x) + 0.5);\r | |
374 | if (DIST2(xd, yd, xmouse, ymouse) <= lpen_dist2)\r | |
375 | hit = TRUE;\r | |
376 | }\r | |
377 | }\r | |
378 | }\r | |
379 | #ifndef DEBUG_LIGHTPEN\r | |
380 | }\r | |
381 | #endif\r | |
382 | }\r | |
383 | }\r | |
384 | \r | |
385 | if (hit) {\r | |
386 | #ifdef DEBUG_LIGHTPEN\r | |
387 | ShowPenHit(xd, yd);\r | |
388 | if (gdu_unit.flags & UNIT_DETECTS_ENABLED && mouse_present)\r | |
389 | SETBIT(gdu_dsw, GDU_DSW_DETECT_STATUS);\r | |
390 | #else\r | |
391 | SETBIT(gdu_dsw, GDU_DSW_DETECT_STATUS);\r | |
392 | #endif\r | |
393 | }\r | |
394 | \r | |
395 | gdu_x = (int16) newx;\r | |
396 | gdu_y = (int16) newy;\r | |
397 | }\r | |
398 | \r | |
399 | static void generate_image (void)\r | |
400 | {\r | |
401 | int32 instr, new_addr, newx, newy;\r | |
402 | t_bool run = TRUE, accept;\r | |
403 | \r | |
404 | if (! (gdu_dsw & GDU_DSW_BUSY))\r | |
405 | return;\r | |
406 | \r | |
407 | GetMouseCoordinates();\r | |
408 | \r | |
409 | lpen_dist = (int) (gdu_threshold/sfactor + 0.5); // mouse-to-line threshhold at current scaling factor\r | |
410 | lpen_dist2 = lpen_dist * lpen_dist;\r | |
411 | \r | |
412 | while (run) {\r | |
413 | if ((gdu_dsw & GDU_DSW_DETECT_STATUS) && ! (gdu_unit.flags & UNIT_INTERRUPTS_DEFERRED)) {\r | |
414 | CLRBIT(gdu_dsw, GDU_DSW_DETECT_STATUS); // clear when interrupt is activated\r | |
415 | gdu_interrupt(GDU_DSW_DETECT_INTERRUPT);\r | |
416 | run = FALSE;\r | |
417 | break;\r | |
418 | }\r | |
419 | \r | |
420 | gdu_instaddr = gdu_ar; // remember address of GDU instruction\r | |
421 | instr = read_gduword(); // fetch instruction (and we really are cycle stealing here!)\r | |
422 | \r | |
423 | switch ((instr >> 12) & 0xF) { // decode instruction\r | |
424 | case 0: // short branch\r | |
425 | case 1:\r | |
426 | gdu_revert = gdu_ar; // save revert address & get new address\r | |
427 | gdu_ar = (int16) (read_gduword() & 0x1FFF);\r | |
428 | if (gdu_dsw & GDU_DSW_CHARACTER_MODE) {\r | |
429 | draw_characters(); // in character mode this means we are at character data\r | |
430 | gdu_ar = gdu_revert;\r | |
431 | }\r | |
432 | break;\r | |
433 | \r | |
434 | case 2: // long branch/interrupt\r | |
435 | new_addr = read_gduword(); // get next word\r | |
436 | accept = ((instr & 1) ? (gdu_dsw & GDU_DSW_LIGHT_PEN_SWITCH) : TRUE) && ((instr & 2) ? (gdu_dsw & GDU_DSW_DETECT_STATUS) : TRUE);\r | |
437 | \r | |
438 | if (instr & 2) // clear after testing\r | |
439 | CLRBIT(gdu_dsw, GDU_DSW_DETECT_STATUS);\r | |
440 | \r | |
441 | if (instr & 0x0400) // NOP\r | |
442 | accept = FALSE;\r | |
443 | \r | |
444 | if (accept) {\r | |
445 | if (instr & 0x0800) { // branch\r | |
446 | gdu_revert = gdu_ar;\r | |
447 | \r | |
448 | if (instr & 0x0080) // indirect\r | |
449 | new_addr = M[new_addr & mem_mask];\r | |
450 | \r | |
451 | gdu_ar = (int16) new_addr;\r | |
452 | \r | |
453 | if (gdu_dsw & GDU_DSW_CHARACTER_MODE) {\r | |
454 | draw_characters();\r | |
455 | gdu_ar = gdu_revert;\r | |
456 | }\r | |
457 | }\r | |
458 | else { // interrupt\r | |
459 | gdu_interrupt(GDU_DSW_ORDER_CONTROLLED_INTERRUPT);\r | |
460 | run = FALSE;\r | |
461 | }\r | |
462 | }\r | |
463 | break;\r | |
464 | \r | |
465 | case 3: // control instructions\r | |
466 | CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE);\r | |
467 | \r | |
468 | switch ((instr >> 8) & 0xF) {\r | |
469 | case 1: // set pen mode\r | |
470 | if ((instr & 0xC) == 8)\r | |
471 | SETBIT(gdu_unit.flags, UNIT_DETECTS_ENABLED);\r | |
472 | else if ((instr & 0xC) == 4)\r | |
473 | CLRBIT(gdu_unit.flags, UNIT_DETECTS_ENABLED);\r | |
474 | \r | |
475 | if ((instr & 0x3) == 2)\r | |
476 | SETBIT(gdu_unit.flags, UNIT_INTERRUPTS_DEFERRED);\r | |
477 | else if ((instr & 0x3) == 1)\r | |
478 | CLRBIT(gdu_unit.flags, UNIT_INTERRUPTS_DEFERRED);\r | |
479 | break;\r | |
480 | \r | |
481 | case 2: // set graphic mode\r | |
482 | if (instr & 1)\r | |
483 | SETBIT(gdu_dsw, GDU_DSW_POINT_MODE);\r | |
484 | else\r | |
485 | CLRBIT(gdu_dsw, GDU_DSW_POINT_MODE);\r | |
486 | break;\r | |
487 | \r | |
488 | case 3: // set character mode\r | |
489 | SETBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE);\r | |
490 | if (instr & 1)\r | |
491 | SETBIT(gdu_unit.flags, UNIT_LARGE_CHARS);\r | |
492 | else\r | |
493 | CLRBIT(gdu_unit.flags, UNIT_LARGE_CHARS);\r | |
494 | break;\r | |
495 | \r | |
496 | case 4: // start timer\r | |
497 | run = FALSE; // (which, for us, means stop processing until next timer message)\r | |
498 | CheckGDUKeyboard();\r | |
499 | break;\r | |
500 | \r | |
501 | case 5: // store revert\r | |
502 | M[gdu_ar & mem_mask] = gdu_revert;\r | |
503 | read_gduword(); // skip to next address\r | |
504 | break;\r | |
505 | \r | |
506 | case 6: // revert\r | |
507 | gdu_ar = gdu_revert;\r | |
508 | break;\r | |
509 | \r | |
510 | default: // all others treated as no-ops\r | |
511 | break;\r | |
512 | }\r | |
513 | break;\r | |
514 | \r | |
515 | case 4: // long absolute\r | |
516 | case 5:\r | |
517 | CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE);\r | |
518 | newx = instr & 0x3FF;\r | |
519 | newy = read_gduword() & 0x3FF;\r | |
520 | draw(newx, newy, instr & 0x1000);\r | |
521 | last_abs = TRUE;\r | |
522 | break;\r | |
523 | \r | |
524 | case 6: // short absolute\r | |
525 | case 7:\r | |
526 | CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE);\r | |
527 | newx = gdu_x;\r | |
528 | newy = gdu_y;\r | |
529 | if (instr & 0x0800)\r | |
530 | newy = instr & 0x3FF;\r | |
531 | else\r | |
532 | newx = instr & 0x3FF;\r | |
533 | draw(newx, newy, instr & 0x1000);\r | |
534 | last_abs = TRUE;\r | |
535 | break;\r | |
536 | \r | |
537 | default: // high bit set - it's a relative instruction\r | |
538 | CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE);\r | |
539 | newx = (instr >> 8) & 0x3F;\r | |
540 | newy = instr & 0x3F;\r | |
541 | \r | |
542 | if (instr & 0x4000) // sign extend x - values are in 2's complement\r | |
543 | newx |= -1 & ~0x3F; // although documentation doesn't make that clear\r | |
544 | \r | |
545 | if (instr & 0x0040) // sign extend y\r | |
546 | newy |= -1 & ~0x3F;\r | |
547 | \r | |
548 | newx = gdu_x + newx;\r | |
549 | newy = gdu_y + newy;\r | |
550 | draw(newx, newy, instr & 0x0080);\r | |
551 | last_abs = FALSE;\r | |
552 | break;\r | |
553 | }\r | |
554 | }\r | |
555 | }\r | |
556 | \r | |
557 | static struct charinfo { // character mode scaling info:\r | |
558 | int dx, dy; // character and line spacing\r | |
559 | double sx, sy; // scaling factors: character units to screen units\r | |
560 | int xoff, yoff; // x and y offset to lower left corner of character\r | |
561 | int suby; // subscript/superscript offset\r | |
562 | } cx[2] = {\r | |
563 | {14, 20, 1.7, 2.0, -6, -7, 6}, // regular\r | |
564 | {21, 30, 2.5, 3.0, -9, -11, 9} // large\r | |
565 | };\r | |
566 | \r | |
567 | static void draw_characters (void)\r | |
568 | {\r | |
569 | int32 w, x0, y0, x1, y1, yoff = 0, ninstr = 0;\r | |
570 | t_bool dospace, didstroke = FALSE;\r | |
571 | struct charinfo *ci;\r | |
572 | \r | |
573 | ci = &cx[(gdu_unit.flags & UNIT_LARGE_CHARS) ? 1 : 0];\r | |
574 | x0 = gdu_x + ci->xoff; // starting position\r | |
575 | y0 = gdu_y + ci->yoff;\r | |
576 | \r | |
577 | do {\r | |
578 | if (++ninstr > 29) { // too many control words\r | |
579 | gdu_interrupt(GDU_DSW_CYCLE_STEAL_CHECK);\r | |
580 | return;\r | |
581 | }\r | |
582 | \r | |
583 | dospace = TRUE;\r | |
584 | w = M[gdu_ar++ & mem_mask]; // get next stroke or control word\r | |
585 | \r | |
586 | x1 = (w >> 12) & 7;\r | |
587 | y1 = (w >> 8) & 7;\r | |
588 | \r | |
589 | if (x1 == 7) { // this is a character control word\r | |
590 | dospace = FALSE; // inhibit character spacing\r | |
591 | \r | |
592 | switch (y1) {\r | |
593 | case 1: // subscript\r | |
594 | if (yoff == 0) // (ignored if superscript is in effect)\r | |
595 | yoff = -ci->suby;\r | |
596 | break;\r | |
597 | \r | |
598 | // case 2: // no-op or null (nothing to do)\r | |
599 | // default: // all unknowns are no-ops\r | |
600 | // break;\r | |
601 | \r | |
602 | case 4: // superscript\r | |
603 | yoff = ci->suby;\r | |
604 | break;\r | |
605 | \r | |
606 | case 7: // new line\r | |
607 | gdu_x = 0;\r | |
608 | gdu_y -= (int16) ci->dy;\r | |
609 | if (gdu_y < 0 && last_abs)\r | |
610 | gdu_y = (int16) (1024 - ci->dy); // this is a guess\r | |
611 | break;\r | |
612 | }\r | |
613 | }\r | |
614 | else { // this is stroke data -- extract two strokes\r | |
615 | x1 = gdu_x + (int) (x1*ci->sx + 0.5);\r | |
616 | y1 = gdu_y + (int) ((y1+yoff)*ci->sy + 0.5);\r | |
617 | \r | |
618 | if (w & 0x0800) {\r | |
619 | didstroke = TRUE;\r | |
620 | DrawLine(x0, y0, x1, y1);\r | |
621 | }\r | |
622 | \r | |
623 | x0 = (w >> 4) & 7;\r | |
624 | y0 = w & 7;\r | |
625 | \r | |
626 | x0 = gdu_x + (int) (x0*ci->sx + 0.5);\r | |
627 | y0 = gdu_y + (int) ((y0+yoff)*ci->sy + 0.5);\r | |
628 | \r | |
629 | if (w & 0x0008) {\r | |
630 | didstroke = TRUE;\r | |
631 | DrawLine(x1, y1, x0, y0);\r | |
632 | } \r | |
633 | }\r | |
634 | \r | |
635 | if (dospace) {\r | |
636 | gdu_x += ci->dx;\r | |
637 | if (gdu_x > 1023 && last_abs) { // line wrap\r | |
638 | gdu_x = 0;\r | |
639 | gdu_y -= (int16) ci->dy;\r | |
640 | }\r | |
641 | }\r | |
642 | } while ((w & 0x0080) == 0); // repeat until we hit revert bit\r | |
643 | \r | |
644 | if (didstroke && mouse_present && (gdu_unit.flags & UNIT_DETECTS_ENABLED)) {\r | |
645 | if (xmouse >= (gdu_x - ci->xoff/2) && xmouse <= (gdu_x + ci->xoff/2) &&\r | |
646 | ymouse >= (gdu_y - ci->yoff/2) && ymouse <= (gdu_y + ci->yoff/2))\r | |
647 | SETBIT(gdu_dsw, GDU_DSW_DETECT_STATUS);\r | |
648 | }\r | |
649 | }\r | |
650 | \r | |
651 | /******* PLATFORM SPECIFIC CODE ***********************************************************/\r | |
652 | \r | |
653 | #ifdef _WIN32\r | |
654 | \r | |
655 | #include <windows.h>\r | |
656 | #include <windowsx.h>\r | |
657 | \r | |
658 | #define APPCLASS "IBM2250GDU" // window class name\r | |
659 | \r | |
660 | #define RGB_GREEN RGB(0,255,0) // handy colors\r | |
661 | #define RGB_RED RGB(255,0,0)\r | |
662 | \r | |
663 | static HINSTANCE hInstance;\r | |
664 | static HWND hwGDU = NULL;\r | |
665 | static HDC hdcGDU = NULL;\r | |
666 | static HBITMAP hBmp = NULL;\r | |
667 | static int curwid = 0;\r | |
668 | static int curht = 0;\r | |
669 | static BOOL wcInited = FALSE;\r | |
670 | static DWORD GDUPumpID = 0;\r | |
671 | static HANDLE hGDUPump = INVALID_HANDLE_VALUE;\r | |
672 | static HPEN hGreenPen = NULL;\r | |
673 | static HBRUSH hRedBrush = NULL;\r | |
674 | #ifdef DEBUG_LIGHTPEN\r | |
675 | static HPEN hRedPen = NULL;\r | |
676 | #endif\r | |
677 | static HBRUSH hGrayBrush, hDarkBrush;\r | |
678 | static HPEN hBlackPen;\r | |
679 | static int halted = 0; // number of time intervals that GDU has been halted w/o a regeneration\r | |
680 | static LRESULT APIENTRY GDUWndProc (HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\r | |
681 | static DWORD WINAPI GDUPump (LPVOID arg);\r | |
682 | \r | |
683 | static void destroy_GDU_window (void)\r | |
684 | {\r | |
685 | if (hwGDU != NULL)\r | |
686 | SendMessage(hwGDU, WM_CLOSE, 0, 0); // cross thread call is OK\r | |
687 | \r | |
688 | if (hGDUPump != INVALID_HANDLE_VALUE) { // this is not the most graceful way to do it\r | |
689 | TerminateThread(hGDUPump, 0);\r | |
690 | hGDUPump = INVALID_HANDLE_VALUE;\r | |
691 | GDUPumpID = 0;\r | |
692 | hwGDU = NULL;\r | |
693 | }\r | |
694 | \r | |
695 | if (hdcGDU != NULL) {\r | |
696 | DeleteDC(hdcGDU);\r | |
697 | hdcGDU = NULL;\r | |
698 | }\r | |
699 | \r | |
700 | if (hBmp != NULL) {\r | |
701 | DeleteObject(hBmp);\r | |
702 | hBmp = NULL;\r | |
703 | }\r | |
704 | \r | |
705 | if (hGreenPen != NULL) {\r | |
706 | DeleteObject(hGreenPen);\r | |
707 | hGreenPen = NULL;\r | |
708 | }\r | |
709 | \r | |
710 | if (hRedBrush != NULL) {\r | |
711 | DeleteObject(hRedBrush);\r | |
712 | hRedBrush = NULL;\r | |
713 | }\r | |
714 | \r | |
715 | #ifdef DEBUG_LIGHTPEN\r | |
716 | if (hRedPen != NULL) {\r | |
717 | DeleteObject(hRedPen);\r | |
718 | hRedPen = NULL;\r | |
719 | }\r | |
720 | #endif\r | |
721 | }\r | |
722 | \r | |
723 | static t_bool CreateGDUWindow (void)\r | |
724 | {\r | |
725 | static BOOL did_atexit = FALSE;\r | |
726 | \r | |
727 | hInstance = GetModuleHandle(NULL);\r | |
728 | \r | |
729 | if (hGDUPump == INVALID_HANDLE_VALUE)\r | |
730 | hGDUPump = CreateThread(NULL, 0, GDUPump, 0, 0, &GDUPumpID);\r | |
731 | \r | |
732 | if (! did_atexit) {\r | |
733 | atexit(destroy_GDU_window);\r | |
734 | did_atexit = TRUE;\r | |
735 | }\r | |
736 | \r | |
737 | return TRUE;\r | |
738 | }\r | |
739 | \r | |
740 | // windows message handlers ----------------------------------------------------\r | |
741 | \r | |
742 | // close the window\r | |
743 | \r | |
744 | static void gdu_WM_CLOSE (HWND hWnd)\r | |
745 | {\r | |
746 | DestroyWindow(hWnd);\r | |
747 | }\r | |
748 | \r | |
749 | // the window is being destroyed\r | |
750 | \r | |
751 | static void gdu_WM_DESTROY (HWND hWnd)\r | |
752 | {\r | |
753 | notify_window_closed();\r | |
754 | hwGDU = NULL;\r | |
755 | }\r | |
756 | \r | |
757 | // adjust the min and max resizing boundaries\r | |
758 | \r | |
759 | static void gdu_WM_GETMINMAXINFO (HWND hWnd, LPMINMAXINFO mm)\r | |
760 | {\r | |
761 | mm->ptMinTrackSize.x = 100 + 2*INDWIDTH;\r | |
762 | mm->ptMinTrackSize.y = 100;\r | |
763 | }\r | |
764 | \r | |
765 | static void PaintImage (HDC hDC, BOOL draw_indicators)\r | |
766 | {\r | |
767 | HPEN hOldPen;\r | |
768 | RECT r;\r | |
769 | int wid, ht, x, y, dy, i, j, ycirc;\r | |
770 | unsigned long bit;\r | |
771 | \r | |
772 | GetClientRect(hwGDU, &r);\r | |
773 | wid = r.right+1 - 2*INDWIDTH;\r | |
774 | ht = r.bottom+1;\r | |
775 | sfactor = (double) MIN(wid,ht) / 1024.;\r | |
776 | \r | |
777 | if (gdu_dsw & GDU_DSW_BUSY) {\r | |
778 | #ifdef BLIT_MODE\r | |
779 | // if compiled for BLIT_MODE, draw the image into a memory display context, then\r | |
780 | // blit the new image over window. This eliminates the flicker that a normal erase-and-\r | |
781 | // repaint would cause.\r | |
782 | \r | |
783 | if (wid != curwid || ht != curht) { // dimensions have changed, discard old memory display context\r | |
784 | if (hdcGDU != NULL) {\r | |
785 | DeleteDC(hdcGDU);\r | |
786 | hdcGDU = NULL;\r | |
787 | }\r | |
788 | curwid = wid;\r | |
789 | curht = ht;\r | |
790 | }\r | |
791 | \r | |
792 | if (hdcGDU == NULL) { // allocate memory display context & select a bitmap into it\r | |
793 | hdcGDU = CreateCompatibleDC(hDC);\r | |
794 | if (hBmp != NULL)\r | |
795 | DeleteObject(hBmp);\r | |
796 | hBmp = CreateCompatibleBitmap(hDC, wid, ht);\r | |
797 | SelectObject(hdcGDU, hBmp);\r | |
798 | }\r | |
799 | \r | |
800 | PatBlt(hdcGDU, 0, 0, wid, ht, BLACKNESS); // start with a black screen\r | |
801 | \r | |
802 | hOldPen = SelectObject(hdcGDU, hGreenPen);\r | |
803 | \r | |
804 | SetMapMode(hdcGDU, MM_ANISOTROPIC);\r | |
805 | SetWindowExtEx(hdcGDU, 1024, -1024, NULL);\r | |
806 | SetViewportExtEx(hdcGDU, wid, ht, NULL);\r | |
807 | SetWindowOrgEx(hdcGDU, 0, 1023, NULL);\r | |
808 | \r | |
809 | generate_image(); // run the display program to paint the image into the memory context\r | |
810 | \r | |
811 | SetWindowExtEx(hdcGDU, wid, ht, NULL); // undo the scaling so the blit isn't distorted\r | |
812 | SetViewportExtEx(hdcGDU, wid, ht, NULL);\r | |
813 | SetWindowOrgEx(hdcGDU, 0, 0, NULL);\r | |
814 | BitBlt(hDC, 0, 0, wid, ht, hdcGDU, 0, 0, SRCCOPY); // blit the new image over the old\r | |
815 | \r | |
816 | SelectObject(hdcGDU, hOldPen);\r | |
817 | #else\r | |
818 | // for testing purposes -- draw the image directly onto the screen.\r | |
819 | // Compile this way when you want to single-step through the image drawing routine,\r | |
820 | // so you can see the draws occur.\r | |
821 | hdcGDU = hDC;\r | |
822 | hOldPen = SelectObject(hdcGDU, hGreenPen);\r | |
823 | \r | |
824 | SetMapMode(hdcGDU, MM_ANISOTROPIC);\r | |
825 | SetWindowExtEx(hdcGDU, 1024, -1024, NULL);\r | |
826 | SetViewportExtEx(hdcGDU, wid, ht, NULL);\r | |
827 | SetWindowOrgEx(hdcGDU, 0, 1023, NULL);\r | |
828 | \r | |
829 | generate_image();\r | |
830 | \r | |
831 | SelectObject(hdcGDU, hOldPen);\r | |
832 | hdcGDU = NULL;\r | |
833 | #endif\r | |
834 | }\r | |
835 | \r | |
836 | if (draw_indicators) {\r | |
837 | x = r.right-2*INDWIDTH+1;\r | |
838 | dy = ht / 16;\r | |
839 | ycirc = MIN(dy-2, 8);\r | |
840 | \r | |
841 | r.left = x;\r | |
842 | FillRect(hDC, &r, hGrayBrush);\r | |
843 | SelectObject(hDC, hBlackPen);\r | |
844 | \r | |
845 | bit = 0x80000000L;\r | |
846 | for (i = 0; i < 2; i++) {\r | |
847 | MoveToEx(hDC, x, 0, NULL);\r | |
848 | LineTo(hDC, x, r.bottom);\r | |
849 | y = 0;\r | |
850 | for (j = 0; j < 16; j++) {\r | |
851 | MoveToEx(hDC, x, y, NULL);\r | |
852 | LineTo(hDC, x+INDWIDTH, y);\r | |
853 | \r | |
854 | SelectObject(hDC, (gdu_indicators & bit) ? hRedBrush : hDarkBrush);\r | |
855 | Pie(hDC, x+1, y+1, x+1+ycirc, y+1+ycirc, x+1, y+1, x+1, y+1);\r | |
856 | \r | |
857 | y += dy;\r | |
858 | bit >>= 1;\r | |
859 | }\r | |
860 | x += INDWIDTH;\r | |
861 | }\r | |
862 | }\r | |
863 | }\r | |
864 | \r | |
865 | // repaint the window\r | |
866 | \r | |
867 | static void gdu_WM_PAINT (HWND hWnd)\r | |
868 | {\r | |
869 | PAINTSTRUCT ps;\r | |
870 | HDC hDC;\r | |
871 | // code for display\r | |
872 | hDC = BeginPaint(hWnd, &ps);\r | |
873 | PaintImage(hDC, TRUE);\r | |
874 | EndPaint(hWnd, &ps);\r | |
875 | }\r | |
876 | \r | |
877 | // the window has been resized\r | |
878 | \r | |
879 | static void gdu_WM_SIZE (HWND hWnd, UINT state, int cx, int cy)\r | |
880 | {\r | |
881 | InvalidateRect(hWnd, NULL, TRUE);\r | |
882 | }\r | |
883 | \r | |
884 | // tweak the sizing rectangle during a resize to guarantee a square window\r | |
885 | \r | |
886 | static void gdu_WM_SIZING (HWND hWnd, WPARAM fwSide, LPRECT r)\r | |
887 | {\r | |
888 | switch (fwSide) {\r | |
889 | case WMSZ_LEFT:\r | |
890 | case WMSZ_RIGHT:\r | |
891 | case WMSZ_BOTTOMLEFT:\r | |
892 | case WMSZ_BOTTOMRIGHT:\r | |
893 | r->bottom = r->right - r->left - 2*INDWIDTH + r->top;\r | |
894 | break;\r | |
895 | \r | |
896 | case WMSZ_TOP:\r | |
897 | case WMSZ_BOTTOM:\r | |
898 | case WMSZ_TOPRIGHT:\r | |
899 | r->right = r->bottom - r->top + r->left + 2*INDWIDTH;\r | |
900 | break;\r | |
901 | \r | |
902 | case WMSZ_TOPLEFT:\r | |
903 | r->left = r->top - r->bottom + r->right - 2*INDWIDTH;\r | |
904 | break;\r | |
905 | }\r | |
906 | }\r | |
907 | \r | |
908 | // the refresh timer has gone off\r | |
909 | \r | |
910 | static void gdu_WM_TIMER (HWND hWnd, UINT id)\r | |
911 | {\r | |
912 | HDC hDC;\r | |
913 | \r | |
914 | if (running) { // if CPU is running, update picture\r | |
915 | if ((gdu_dsw & GDU_DSW_BUSY) == 0) { // regeneration is not to occur\r | |
916 | if (++halted >= 4) { // stop the timer if four timer intervals go by with the display halted\r | |
917 | EraseGDUScreen(); // screen goes black due to cessation of refreshing\r | |
918 | StopGDUUpdates(); // might as well kill the timer\r | |
919 | return;\r | |
920 | }\r | |
921 | }\r | |
922 | else\r | |
923 | halted = 0;\r | |
924 | \r | |
925 | #ifdef BLIT_MODE\r | |
926 | hDC = GetDC(hWnd); // blit the new image right over the old\r | |
927 | PaintImage(hDC, FALSE);\r | |
928 | ReleaseDC(hWnd, hDC);\r | |
929 | #else\r | |
930 | InvalidateRect(hWnd, NULL, TRUE); // repaint\r | |
931 | #endif\r | |
932 | }\r | |
933 | }\r | |
934 | \r | |
935 | // window procedure ------------------------------------------------------------\r | |
936 | \r | |
937 | #define HANDLE(msg) case msg: return HANDLE_##msg(hWnd, wParam, lParam, gdu_##msg);\r | |
938 | \r | |
939 | #ifndef HANDLE_WM_SIZING\r | |
940 | // void Cls_OnSizing(HWND hwnd, UINT fwSide, LPRECT r)\r | |
941 | # define HANDLE_WM_SIZING(hwnd, wParam, lParam, fn) \\r | |
942 | ((fn)((hwnd), (UINT)(wParam), (LPRECT)(lParam)), 0L)\r | |
943 | #endif\r | |
944 | \r | |
945 | static LRESULT APIENTRY GDUWndProc (HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)\r | |
946 | {\r | |
947 | switch (iMessage) {\r | |
948 | HANDLE(WM_CLOSE);\r | |
949 | HANDLE(WM_GETMINMAXINFO);\r | |
950 | HANDLE(WM_DESTROY);\r | |
951 | HANDLE(WM_PAINT);\r | |
952 | HANDLE(WM_SIZE);\r | |
953 | HANDLE(WM_SIZING);\r | |
954 | HANDLE(WM_TIMER);\r | |
955 | default: // any message we don't process\r | |
956 | return DefWindowProc(hWnd, iMessage, wParam, lParam);\r | |
957 | }\r | |
958 | return 0L;\r | |
959 | }\r | |
960 | \r | |
961 | // graphic calls ----------------------------------------------------------------\r | |
962 | \r | |
963 | static void DrawLine (int x0, int y0, int x1, int y1)\r | |
964 | {\r | |
965 | MoveToEx(hdcGDU, x0, y0, NULL);\r | |
966 | LineTo(hdcGDU, x1, y1);\r | |
967 | }\r | |
968 | \r | |
969 | static void DrawPoint (int x, int y)\r | |
970 | {\r | |
971 | SetPixel(hdcGDU, x, y, RGB_GREEN);\r | |
972 | }\r | |
973 | \r | |
974 | static void UpdateGDUIndicators(void)\r | |
975 | {\r | |
976 | if (hwGDU != NULL)\r | |
977 | InvalidateRect(hwGDU, NULL, FALSE); // no need to erase the background -- the draw routine fully paints the indicator\r | |
978 | }\r | |
979 | \r | |
980 | static void CheckGDUKeyboard (void)\r | |
981 | {\r | |
982 | }\r | |
983 | \r | |
984 | static UINT idTimer = 0;\r | |
985 | \r | |
986 | static void StartGDUUpdates (void)\r | |
987 | {\r | |
988 | int msec;\r | |
989 | \r | |
990 | if (idTimer == 0) {\r | |
991 | msec = (gdu_rate == 0) ? (1000 / DEFAULT_GDU_RATE) : 1000/gdu_rate;\r | |
992 | idTimer = SetTimer(hwGDU, 1, msec, NULL);\r | |
993 | }\r | |
994 | halted = 0;\r | |
995 | }\r | |
996 | \r | |
997 | static void StopGDUUpdates (void)\r | |
998 | {\r | |
999 | if (idTimer != 0) {\r | |
1000 | KillTimer(hwGDU, 1);\r | |
1001 | idTimer = 0;\r | |
1002 | halted = 10000;\r | |
1003 | }\r | |
1004 | }\r | |
1005 | \r | |
1006 | static void GetMouseCoordinates()\r | |
1007 | {\r | |
1008 | POINT p;\r | |
1009 | RECT r;\r | |
1010 | \r | |
1011 | GetCursorPos(&p);\r | |
1012 | GetClientRect(hwGDU, &r);\r | |
1013 | if (! ScreenToClient(hwGDU, &p)) {\r | |
1014 | xmouse = ymouse = -2000;\r | |
1015 | mouse_present = FALSE;\r | |
1016 | return;\r | |
1017 | }\r | |
1018 | \r | |
1019 | if (p.x < r.left || p.x >= r.right || p.y < r.top || p.y > r.bottom) {\r | |
1020 | mouse_present = FALSE;\r | |
1021 | return;\r | |
1022 | }\r | |
1023 | \r | |
1024 | // convert mouse coordinates to scaled coordinates\r | |
1025 | \r | |
1026 | xmouse = (int) (1024./(r.right+1.-2*INDWIDTH)*p.x + 0.5);\r | |
1027 | ymouse = 1023 - (int) (1024./(r.bottom+1.)*p.y + 0.5);\r | |
1028 | mouse_present = TRUE;\r | |
1029 | }\r | |
1030 | \r | |
1031 | t_bool gdu_active (void)\r | |
1032 | {\r | |
1033 | return gdu_dsw & GDU_DSW_BUSY;\r | |
1034 | }\r | |
1035 | \r | |
1036 | static void EraseGDUScreen (void)\r | |
1037 | {\r | |
1038 | if (hwGDU != NULL) /* redraw screen. it will be blank if GDU is not running */\r | |
1039 | InvalidateRect(hwGDU, NULL, TRUE);\r | |
1040 | }\r | |
1041 | \r | |
1042 | /* GDUPump - thread responsible for creating and displaying the graphics window */\r | |
1043 | \r | |
1044 | static DWORD WINAPI GDUPump (LPVOID arg)\r | |
1045 | {\r | |
1046 | MSG msg;\r | |
1047 | WNDCLASS wc;\r | |
1048 | \r | |
1049 | if (! wcInited) { /* register Window class */\r | |
1050 | memset(&wc, 0, sizeof(wc));\r | |
1051 | wc.style = CS_NOCLOSE;\r | |
1052 | wc.lpfnWndProc = GDUWndProc;\r | |
1053 | wc.hInstance = hInstance;\r | |
1054 | wc.hCursor = LoadCursor(NULL, IDC_ARROW);\r | |
1055 | wc.hbrBackground = GetStockObject(BLACK_BRUSH);\r | |
1056 | wc.lpszClassName = APPCLASS;\r | |
1057 | \r | |
1058 | if (! RegisterClass(&wc)) {\r | |
1059 | GDUPumpID = 0;\r | |
1060 | return 0;\r | |
1061 | }\r | |
1062 | \r | |
1063 | wcInited = TRUE;\r | |
1064 | }\r | |
1065 | \r | |
1066 | if (hGreenPen == NULL)\r | |
1067 | hGreenPen = CreatePen(PS_SOLID, 1, RGB_GREEN);\r | |
1068 | \r | |
1069 | #ifdef DEBUG_LIGHTPEN\r | |
1070 | if (hRedPen == NULL)\r | |
1071 | hRedPen = CreatePen(PS_SOLID, 1, RGB_RED);\r | |
1072 | #endif\r | |
1073 | \r | |
1074 | if (hRedBrush == NULL)\r | |
1075 | hRedBrush = CreateSolidBrush(RGB_RED);\r | |
1076 | \r | |
1077 | hGrayBrush = GetStockObject(GRAY_BRUSH);\r | |
1078 | hDarkBrush = GetStockObject(DKGRAY_BRUSH);\r | |
1079 | hBlackPen = GetStockObject(BLACK_PEN);\r | |
1080 | \r | |
1081 | if (hwGDU == NULL) { /* create window */\r | |
1082 | hwGDU = CreateWindow(APPCLASS,\r | |
1083 | "2250 Display",\r | |
1084 | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,\r | |
1085 | CW_USEDEFAULT, CW_USEDEFAULT, // initial x, y position\r | |
1086 | INITSIZE+2*INDWIDTH, INITSIZE, // initial width and height\r | |
1087 | NULL, // parent window handle\r | |
1088 | NULL, // use class menu\r | |
1089 | hInstance, // program instance handle\r | |
1090 | NULL); // create parameters\r | |
1091 | \r | |
1092 | if (hwGDU == NULL) {\r | |
1093 | GDUPumpID = 0;\r | |
1094 | return 0;\r | |
1095 | }\r | |
1096 | }\r | |
1097 | \r | |
1098 | ShowWindow(hwGDU, SW_SHOWNOACTIVATE); /* display it */\r | |
1099 | UpdateWindow(hwGDU);\r | |
1100 | \r | |
1101 | while (GetMessage(&msg, hwGDU, 0, 0)) { /* message pump - this basically loops forevermore */\r | |
1102 | TranslateMessage(&msg);\r | |
1103 | DispatchMessage(&msg);\r | |
1104 | }\r | |
1105 | \r | |
1106 | if (hwGDU != NULL) {\r | |
1107 | DestroyWindow(hwGDU); /* but if a quit message got posted, clean up */\r | |
1108 | hwGDU = NULL;\r | |
1109 | }\r | |
1110 | \r | |
1111 | GDUPumpID = 0;\r | |
1112 | return 0;\r | |
1113 | }\r | |
1114 | \r | |
1115 | #ifdef DEBUG_LIGHTPEN\r | |
1116 | static void ShowPenHit (int x, int y)\r | |
1117 | {\r | |
1118 | HPEN hOldPen;\r | |
1119 | \r | |
1120 | hOldPen = SelectObject(hdcGDU, hRedPen);\r | |
1121 | DrawLine(x-10, y-10, x+10, y+10);\r | |
1122 | DrawLine(x-10, y+10, x+10, y-10);\r | |
1123 | SelectObject(hdcGDU, hOldPen);\r | |
1124 | }\r | |
1125 | #endif\r | |
1126 | \r | |
1127 | #endif // _WIN32 defined\r | |
1128 | #endif // GUI_SUPPORT defined\r |