Cleaned up XScreensaver portion of NEDsim.c.
[screensavers] / hacks / NEDsim / NEDsim.c
CommitLineData
84b74595
AT
1/* (c) 2021 Aaron Taylor <ataylor at subgeniuskitty dot com> */
2/* See LICENSE.txt file for copyright and license details. */
3
84b74595
AT
4#include "screenhack.h"
5#include "simulator.h"
6
84b74595
AT
7/* -------------------------------------------------------------------------- */
8/* Data Structures */
9/* -------------------------------------------------------------------------- */
10
11struct NEDsim {
f93fc861 12 // Various X resources
84b74595
AT
13 Display * dpy;
14 Window win;
15 GC gc;
16
f93fc861 17 // Tracks the width and height (in pixels) of 'win' on 'dpy'.
84b74595
AT
18 int dpy_width, dpy_height;
19
f93fc861
AT
20 // To ensure the NED display is always synchronized/valid, we
21 // double-buffer. This is that buffer.
84b74595
AT
22 Pixmap panel;
23
f93fc861
AT
24 // The front panel is defined in a fixed grid of 'cells' which are then
25 // scaled and projected onto the actual screen. This variable tracks the
26 // size of one 'cell' in screen pixels.
84b74595 27 int cell_size;
f93fc861
AT
28 // Like above, this variable tracks the scaled size (in pixels) of cell
29 // borders when projecting them on the screen.
84b74595 30 int border_size;
f93fc861
AT
31
32 // Since the panel will be smaller than the display window, these two
33 // variable track the location (in pixels) of the upper left corner of the
34 // NED panel (pretending it didn't have rounded corners) relative to the
35 // display window's origin.
84b74595 36 int origin_x_offset, origin_y_offset;
f93fc861
AT
37
38 // Delay (in microseconds) between clock cycles in the NED CPU.
39 size_t delay;
40
41 // Since the heap and stack areas are resized to fit the display, this
42 // variable tracks their height.
84b74595
AT
43 int num_data_rows;
44
f93fc861 45 // This is an index into the color_list[] array, selecting a color scheme.
84b74595
AT
46 size_t color_index;
47
f93fc861
AT
48 // If the display is unsuitable (e.g. too small), this boolean is 'true'.
49 // With this, we blank the display but keep the simulation itself running,
50 // allowing the ongoing simulation to display when the situation is
51 // corrected, and allowing us to avoid boring the user with unnecessary
52 // simulation restarts.
84b74595
AT
53 Bool suitable_display;
54
f93fc861
AT
55 // The font size is dynamically adjusted based on display size. This
56 // variable stores the full font description, including the correct font
57 // size, all in the X11 font specifier format. For example,
58 // -*-helvetica-*-r-*-*-72-*-*-*-*-*-*-*
59 // if the display were using a 72 point helvetica font without emphasis.
84b74595
AT
60 char * current_font;
61
f93fc861 62 // This struct contains all machine state for the simulated NED computer.
84b74595
AT
63 struct NEDstate * nedstate;
64};
65
66struct color_rgb {
f93fc861
AT
67 // The type 'unsigned short' comes from the XColor struct definition.
68 // The red, green, and blue values are always in the range 0 to 65535
69 // inclusive, independent of the number of bits actually used in the
70 // display hardware. The server scales these values to the range used
71 // by the hardware. Black is represented by (0,0,0), and white is
72 // represented by (65535,65535,65535).
84b74595
AT
73 unsigned short red, green, blue;
74};
75
76struct color_scheme {
f93fc861 77 // The following entries define a full color scheme for a NED panel.
84b74595 78 struct color_rgb
f93fc861
AT
79 panel_bg, // Empty screen space not taken up by the panel
80 panel_fg, // Panel body/faceplate
81 light_on, // Illuminated data indicator
82 light_off, // Non-illuminated data indicator
83 error_on, // Illuminated error indicator
84 error_off, // Non-illuminated error indicator
85 primary, // Primary panel color (see: logo background)
86 secondary, // Secondary panel color (see: "HALT" text background)
87 tertiary, // Tertiary panel color (see: "subgeniuskitty.com" text background)
88 border, // Cell borders
89 text; // Panel text
84b74595
AT
90};
91
f93fc861
AT
92// All included color schemes for NEDsim are defined in this array. To select a
93// particular color scheme at runtime, use the `-color N` CLI option.
84b74595 94static struct color_scheme color_list[] = {
f93fc861
AT
95 { // This color scheme is inspired by the KI10 version of the PDP-10.
96 // Many RGB values came from: http://www.chdickman.com/pdp8/DECcolors/
84b74595
AT
97 {63479,63479,63479}, // 092-XXXX-123
98 { 5140, 2056, 5654}, // 092-XXXX-152
f93fc861
AT
99 {65535,11051,15677}, // homemade, derived from 092-XXXX-139
100 {20000,13107,12850}, // homemade, derived from 092-XXXX-154
101 {65535,13107,12850}, // homemade, derived from 092-XXXX-154
102 {20000,13107,12850}, // homemade, derived from 092-XXXX-154
84b74595
AT
103 { 4112,12850,20046}, // 092-XXXX-145
104 {12336,29555,37008}, // 092-XXXX-151
105 {30326,24158, 6425}, // 092-XXXX-157
106 {63479,63479,63479}, // 092-XXXX-123
107 {63479,63479,63479} // 092-XXXX-123
f93fc861
AT
108 },
109 { // This color scheme is (very loosely) inspired by the first version of the PDP-11/70.
110 // Many RGB values came from: http://www.chdickman.com/pdp8/DECcolors/
111 {63479,63479,63479}, // 092-XXXX-123
112 { 5140, 2056, 5654}, // 092-XXXX-152
113 {51400,34952,26682}, // homemade, derived from 092-XXXX-136
114 {20000,13107,12850}, // homemade, derived from 092-XXXX-154
115 {65535,13107,12850}, // homemade, derived from 092-XXXX-154
116 {20000,13107,12850}, // homemade, derived from 092-XXXX-154
117 {28270, 8995,19532}, // 092-XXXX-140
118 {40092,11051,15677}, // 092-XXXX-139
119 {15000,21000, 4000}, // homemade, derived from 092-XXXX-129
120 {63479,63479,63479}, // 092-XXXX-123
121 {63479,63479,63479} // 092-XXXX-123
84b74595
AT
122 }
123};
124
125/* -------------------------------------------------------------------------- */
126/* Helper Functions */
127/* -------------------------------------------------------------------------- */
128
129// TODO: Explain
130static void
131set_color(struct NEDsim * nedsim, struct color_rgb * color)
132{
133 XColor temp;
134 XWindowAttributes xgwa;
135
136 XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa);
137
138 temp.red = color->red;
139 temp.green = color->green;
140 temp.blue = color->blue;
141
142 XAllocColor(nedsim->dpy, xgwa.colormap, &temp);
143 XSetForeground(nedsim->dpy, nedsim->gc, temp.pixel);
144}
145
146// TODO: Explain
147// TODO: Make this a lot faster.
148// Input: size in 'cells', and sets font to fill that size, minus border and padding room.
149static void
150set_font_size(struct NEDsim * nedsim, int size)
151{
152 // vvv--- for border ---vvv vvv--- for padding ---vvv
153 int desired_height_in_pixels = (size * nedsim->cell_size) - (2 * nedsim->border_size) - (8 * nedsim->border_size);
154
155 const char * font_size_prefix = "-*-helvetica-*-r-*-*-";
156 const char * font_size_suffix = "-*-*-*-*-*-*-*";
157
158 size_t buffer_size = strlen(font_size_prefix) + strlen(font_size_suffix) + 100; // '100' since nobody needs font with size in 'points' greater than 100 decimal digits long.
159 char * font_full_name = malloc(buffer_size);
160 int font_size_in_points = 2; // Start with a 2 pt font and work our way up.
161
162 while (1) {
163 // Load the font.
164 snprintf(font_full_name, buffer_size, "%s%d%s", font_size_prefix, font_size_in_points, font_size_suffix);
165 XFontStruct * font = XLoadQueryFont(nedsim->dpy, font_full_name);
166 if (!font) {
167 printf("WARNING: Unable to load font %s. Probably gonna look wonky.\n", font_full_name);
168 font = XLoadQueryFont(nedsim->dpy, "fixed");
169 break;
170 }
171 XSetFont(nedsim->dpy, nedsim->gc, font->fid);
172
173 // Get the height.
174 int direction, ascent, descent;
175 XCharStruct overall;
176 XTextExtents(font, "X", 1, &direction, &ascent, &descent, &overall);
177
178 // Compare the height.
179 int height = overall.ascent - overall.descent;
180 if (height == desired_height_in_pixels) {
181 break;
182 } else if (height > desired_height_in_pixels) {
183 font_size_in_points--;
184 snprintf(font_full_name, buffer_size, "%s%d%s", font_size_prefix, font_size_in_points, font_size_suffix);
185 XFontStruct * font = XLoadQueryFont(nedsim->dpy, font_full_name);
186 if (!font) {
187 printf("WARNING: Unable to load font %s. Probably gonna look wonky.\n", font_full_name);
188 font = XLoadQueryFont(nedsim->dpy, "fixed");
189 break;
190 }
191 XSetFont(nedsim->dpy, nedsim->gc, font->fid);
192 break;
193 } else {
194 font_size_in_points++;
195 }
196 }
197
198 free(nedsim->current_font);
199 nedsim->current_font = font_full_name;
200}
201
202// TODO: Explain
203// TODO: Explain that this returns result in pixels so we can track fractional cell usage.
204static void
205get_text_size(struct NEDsim * nedsim, const char * text, int * x_size, int * y_size)
206{
207 int direction, ascent, descent;
208 XCharStruct overall;
209 XFontStruct * font = XLoadQueryFont(nedsim->dpy, nedsim->current_font);
210 XTextExtents(font, text, strlen(text), &direction, &ascent, &descent, &overall);
211 *x_size = overall.width;
212 *y_size = overall.ascent - overall.descent;
213}
214
215// TODO: Explain
216// TODO: Note that this might leave the foreground color changed.
217// Argument coordinates are in 'cells', not pixels.
218static void
219draw_rect_area(struct NEDsim * nedsim, size_t x_origin, size_t y_origin, size_t x_size, size_t y_size,
220 Bool bord_top, Bool bord_bottom, Bool bord_left, Bool bord_right)
221{
222 x_origin *= nedsim->cell_size;
223 x_origin += nedsim->origin_x_offset;
224 y_origin *= nedsim->cell_size;
225 y_origin += nedsim->origin_y_offset;
226 x_size *= nedsim->cell_size;
227 y_size *= nedsim->cell_size;
839b757b 228 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, y_origin, x_size, y_size);
84b74595
AT
229
230 set_color(nedsim, &color_list[nedsim->color_index].border);
231 if (bord_top) {
839b757b 232 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, y_origin, x_size, nedsim->border_size);
84b74595
AT
233 }
234 if (bord_bottom) {
839b757b 235 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, (y_origin + y_size - nedsim->border_size), x_size, nedsim->border_size);
84b74595
AT
236 }
237 if (bord_left) {
839b757b 238 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, y_origin, nedsim->border_size, y_size);
84b74595
AT
239 }
240 if (bord_right) {
839b757b 241 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, (x_origin + x_size - nedsim->border_size), y_origin, nedsim->border_size, y_size);
84b74595
AT
242 }
243}
244
245// TODO: Explain
246// Arguments are in units of 'cells', not pixels.
247// Will leave foreground color changed.
248// Draws filled circle with upper left corner at x,y.
249static void
250draw_circular_area(struct NEDsim * nedsim, size_t x, size_t y, double diameter)
251{
252 // First, convert the function argument units from 'cells' to 'pixels'
253 x *= nedsim->cell_size;
254 y *= nedsim->cell_size;
255 diameter *= nedsim->cell_size;
256
257 // Add the panel's absolute x,y offset to the requested coordinates.
258 x += nedsim->origin_x_offset;
259 y += nedsim->origin_y_offset;
260
261 // Shrink the circle to be a bit smaller than the bounding box allows. Note
262 // that the three adjustment values must sum to 1.0.
263 // For example, 0.7 + 0.15 + 0.15 = 1.0.
264 x += (0.15 * diameter);
265 y += (0.15 * diameter);
266 diameter *= 0.7;
267
268 // Because we only draw the bottom border on repeated rows (e.g. draw_wordline()),
269 // we need to offset vertically by half a border height.
270 y -= (0.5 * nedsim->border_size);
271
272 // Start angle 0 and ending angle 360*64 is one full circle in Xlib units.
839b757b 273 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc, x, y, diameter, diameter, 0, 360*64);
84b74595
AT
274}
275
276// TODO: Explain
277static void
278draw_panel(struct NEDsim * nedsim)
279{
280// TODO: Collect all relevant #defines somewhere.
281#define OVERALL_WIDTH_IN_CELLS 70
282#define HEADER_HEIGHT_IN_CELLS 14
283#define FOOTER_HEIGHT_IN_CELLS 2
284
285 // Draw background color over entire window.
286 set_color(nedsim, &color_list[nedsim->color_index].panel_bg);
839b757b 287 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, 0, 0, nedsim->dpy_width, nedsim->dpy_height);
84b74595
AT
288
289 // Draw NED panel in foreground color.
290 set_color(nedsim, &color_list[nedsim->color_index].panel_fg);
839b757b 291 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
84b74595
AT
292 nedsim->origin_x_offset,
293 nedsim->origin_y_offset,
294 nedsim->cell_size * OVERALL_WIDTH_IN_CELLS,
295 nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS)
296 );
a98a69c1
AT
297
298 // Give the panel rounded corners by first deleting the four right-angle corners...
299 set_color(nedsim, &color_list[nedsim->color_index].panel_bg);
839b757b 300 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
301 nedsim->origin_x_offset,
302 nedsim->origin_y_offset,
303 nedsim->cell_size,
304 nedsim->cell_size
305 );
839b757b 306 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
307 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-1)),
308 nedsim->origin_y_offset,
309 nedsim->cell_size,
310 nedsim->cell_size
311 );
839b757b 312 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
313 nedsim->origin_x_offset,
314 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 1)),
315 nedsim->cell_size,
316 nedsim->cell_size
317 );
839b757b 318 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
319 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-1)),
320 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 1)),
321 nedsim->cell_size,
322 nedsim->cell_size
323 );
324 // ...and then replacing them with filled arcs, forming rounded corners.
325 set_color(nedsim, &color_list[nedsim->color_index].panel_fg);
839b757b 326 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
327 nedsim->origin_x_offset,
328 nedsim->origin_y_offset,
329 nedsim->cell_size * 2, nedsim->cell_size * 2,
330 180*64, -90*64
331 );
839b757b 332 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
333 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-2)),
334 nedsim->origin_y_offset,
335 nedsim->cell_size * 2, nedsim->cell_size * 2,
336 0, 90*64
337 );
839b757b 338 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
339 nedsim->origin_x_offset,
340 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 2)),
341 nedsim->cell_size * 2, nedsim->cell_size * 2,
342 180*64, 90*64
343 );
839b757b 344 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
345 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-2)),
346 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 2)),
347 nedsim->cell_size * 2, nedsim->cell_size * 2,
348 0, -90*64
349 );
84b74595
AT
350}
351
352// TODO: Explain
353static void
354draw_logo(struct NEDsim * nedsim)
355{
356#define LOGO_X_OFFSET 2
357#define LOGO_Y_OFFSET 2
358#define LOGO_WIDTH 20
359#define LOGO_NAME_HEIGHT 6
360#define LOGO_WEBSITE_HEIGHT 2
361
362 // First draw the two colored boxes that comprise the logo area.
363 set_color(nedsim, &color_list[nedsim->color_index].primary);
364 draw_rect_area(nedsim, LOGO_X_OFFSET, LOGO_Y_OFFSET, LOGO_WIDTH, LOGO_NAME_HEIGHT, True, True, False, False);
365 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
366 draw_rect_area(nedsim, LOGO_X_OFFSET, LOGO_Y_OFFSET+LOGO_NAME_HEIGHT, LOGO_WIDTH, LOGO_WEBSITE_HEIGHT, False, True, False, False);
367
368 // Now draw the 'NED' text in the top box.
369 set_color(nedsim, &color_list[nedsim->color_index].text);
370 set_font_size(nedsim, LOGO_NAME_HEIGHT);
371 int text_x_size, text_y_size;
372 get_text_size(nedsim, "NED", &text_x_size, &text_y_size);
373 int local_x_offset = ((LOGO_WIDTH * nedsim->cell_size) - text_x_size) / 2;
374 int local_y_offset = ((LOGO_NAME_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 375 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (LOGO_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
376 ((LOGO_Y_OFFSET+LOGO_NAME_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "NED", 3);
377
378 // And draw the 'subgeniuskitty.com' text in the bottom box.
379 set_font_size(nedsim, LOGO_WEBSITE_HEIGHT);
380 get_text_size(nedsim, "subgeniuskitty.com", &text_x_size, &text_y_size);
381 local_x_offset = ((LOGO_WIDTH * nedsim->cell_size) - text_x_size) / 2;
382 local_y_offset = ((LOGO_WEBSITE_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 383 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (LOGO_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
384 ((LOGO_Y_OFFSET+LOGO_NAME_HEIGHT+LOGO_WEBSITE_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "subgeniuskitty.com", 18);
385}
386
387// TODO: Explain
388static void
389draw_halt(struct NEDsim * nedsim)
390{
391#define HALT_X_OFFSET 26
392#define HALT_Y_OFFSET 2
393#define HALT_WIDTH 6
394#define HALT_LIGHT_HEIGHT 6
395#define HALT_LABEL_HEIGHT 2
396
397 // First draw the two colored boxes that comprise the halt area.
398 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
399 draw_rect_area(nedsim, HALT_X_OFFSET, HALT_Y_OFFSET, HALT_WIDTH, HALT_LIGHT_HEIGHT, True, True, False, False);
400 set_color(nedsim, &color_list[nedsim->color_index].secondary);
401 draw_rect_area(nedsim, HALT_X_OFFSET, HALT_Y_OFFSET+HALT_LIGHT_HEIGHT, HALT_WIDTH, HALT_LABEL_HEIGHT, False, True, False, False);
402
403 // And finally, draw the label.
404 set_color(nedsim, &color_list[nedsim->color_index].text);
405 int text_x_size, text_y_size;
406 get_text_size(nedsim, "HALT", &text_x_size, &text_y_size);
407 int local_x_offset = ((HALT_WIDTH * nedsim->cell_size) - text_x_size) / 2;
408 int local_y_offset = ((HALT_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 409 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (HALT_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
410 ((HALT_Y_OFFSET+HALT_LIGHT_HEIGHT+HALT_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "HALT", 4);
411}
412
413// TODO: Explain
414static void
415draw_wordline_lights(struct NEDsim * nedsim, uint32_t word, int x, int y)
416{
417#define WORDLINE_BITS_PER_STRIPE 4
418#define WORDLINE_WIDTH 32
419#define WORDLINE_HEIGHT 1
420
421 for (int i = 0; i < WORDLINE_WIDTH; i++) {
422 if (word & (1<<(WORDLINE_WIDTH-1-i))) {
423 set_color(nedsim, &color_list[nedsim->color_index].light_on);
424 } else {
425 set_color(nedsim, &color_list[nedsim->color_index].light_off);
426 }
427 draw_circular_area(nedsim, x+i, y, WORDLINE_HEIGHT);
428 }
429}
430
431// TODO: Explain
432static void
433draw_wordline(struct NEDsim * nedsim, int x, int y)
434{
435 // First, draw a solid box in the primary color over the entire wordline area.
436 set_color(nedsim, &color_list[nedsim->color_index].primary);
437 draw_rect_area(nedsim, x, y, WORDLINE_WIDTH, WORDLINE_HEIGHT, False, True, False, False);
438
439 // Now, draw stripes in the secondary color.
440 int i;
441 for (i = 0; i < (WORDLINE_WIDTH/(2*WORDLINE_BITS_PER_STRIPE)); i++) {
442 set_color(nedsim, &color_list[nedsim->color_index].secondary);
443 draw_rect_area(nedsim, (x+(i*(WORDLINE_WIDTH/WORDLINE_BITS_PER_STRIPE))), y,
444 WORDLINE_BITS_PER_STRIPE, WORDLINE_HEIGHT, False, True, False, False);
445 }
446
447 // Finally, draw the lights.
448 draw_wordline_lights(nedsim, 0, x, y);
449}
450
451// TODO: Explain
452static void
453draw_pc(struct NEDsim * nedsim)
454{
455// TODO: Note that all #defines use units of 'cells', not 'pixels', etc.
456#define PC_X_OFFSET 36
457#define PC_Y_OFFSET 7
458#define PC_WIDTH 32
459#define PC_LABEL_HEIGHT 2
460#define PC_LIGHT_HEIGHT 1
461
462 // First draw the two colored boxes that comprise the PC area.
463 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
464 draw_rect_area(nedsim, PC_X_OFFSET, PC_Y_OFFSET, PC_WIDTH, PC_LABEL_HEIGHT, True, True, False, False);
465 draw_wordline(nedsim, PC_X_OFFSET, PC_Y_OFFSET+PC_LABEL_HEIGHT);
466
467 // Now draw the label text "PC".
468 set_color(nedsim, &color_list[nedsim->color_index].text);
469 int text_x_size, text_y_size;
470 get_text_size(nedsim, "PC", &text_x_size, &text_y_size);
471 int local_x_offset = ((PC_WIDTH * nedsim->cell_size) - text_x_size) / 2;
472 int local_y_offset = ((PC_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 473 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (PC_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
474 ((PC_Y_OFFSET+PC_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "PC", 2);
475}
476
477// TODO: Explain
478static void
479draw_sc(struct NEDsim * nedsim)
480{
481#define SC_X_OFFSET 42
482#define SC_Y_OFFSET 2
483#define SC_WIDTH 5
484#define SC_LABEL_HEIGHT 2
485#define SC_LIGHT_HEIGHT 1
486
487 // First draw the two colored boxes that comprise the SC area.
488 set_color(nedsim, &color_list[nedsim->color_index].secondary);
489 draw_rect_area(nedsim, SC_X_OFFSET, SC_Y_OFFSET, SC_WIDTH, SC_LABEL_HEIGHT, True, True, False, False);
490 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
491 draw_rect_area(nedsim, SC_X_OFFSET, SC_Y_OFFSET+SC_LABEL_HEIGHT, SC_WIDTH, SC_LIGHT_HEIGHT, False, True, False, False);
492
493 // Now draw the label text "SC".
494 set_color(nedsim, &color_list[nedsim->color_index].text);
495 int text_x_size, text_y_size;
496 get_text_size(nedsim, "SC", &text_x_size, &text_y_size);
497 int local_x_offset = ((SC_WIDTH * nedsim->cell_size) - text_x_size) / 2;
498 int local_y_offset = ((SC_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 499 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (SC_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
500 ((SC_Y_OFFSET+SC_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "SC", 2);
501}
502
503// TODO: Explain
504static void
505draw_psw(struct NEDsim * nedsim)
506{
507#define PSW_N_X_OFFSET 51
508#define PSW_Z_X_OFFSET 58
509#define PSW_Y_OFFSET 2
510#define PSW_LABEL_WIDTH 3
511#define PSW_LABEL_HEIGHT 2
512#define PSW_LIGHT_WIDTH 1
513#define PSW_LIGHT_HEIGHT 1
514
515 // First draw the four colored boxes that comprise the two PSW areas.
516 set_color(nedsim, &color_list[nedsim->color_index].secondary);
517 draw_rect_area(nedsim, PSW_N_X_OFFSET, PSW_Y_OFFSET, PSW_LABEL_WIDTH, PSW_LABEL_HEIGHT, True, True, False, False);
518 set_color(nedsim, &color_list[nedsim->color_index].secondary);
519 draw_rect_area(nedsim, PSW_Z_X_OFFSET, PSW_Y_OFFSET, PSW_LABEL_WIDTH, PSW_LABEL_HEIGHT, True, True, False, False);
520 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
521 draw_rect_area(nedsim, (PSW_N_X_OFFSET + 1), PSW_Y_OFFSET+PSW_LABEL_HEIGHT, PSW_LIGHT_WIDTH, PSW_LIGHT_HEIGHT, False, True, False, False);
522 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
523 draw_rect_area(nedsim, (PSW_Z_X_OFFSET + 1), PSW_Y_OFFSET+PSW_LABEL_HEIGHT, PSW_LIGHT_WIDTH, PSW_LIGHT_HEIGHT, False, True, False, False);
524
525 // Now draw the label text "N".
526 set_color(nedsim, &color_list[nedsim->color_index].text);
527 int text_x_size, text_y_size;
528 get_text_size(nedsim, "N", &text_x_size, &text_y_size);
529 int local_x_offset = ((PSW_LABEL_WIDTH * nedsim->cell_size) - text_x_size) / 2;
530 int local_y_offset = ((PSW_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 531 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (PSW_N_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
532 ((PSW_Y_OFFSET+PSW_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "N", 1);
533
534 // Now draw the label text "Z".
535 set_color(nedsim, &color_list[nedsim->color_index].text);
536 get_text_size(nedsim, "Z", &text_x_size, &text_y_size);
537 local_x_offset = ((PSW_LABEL_WIDTH * nedsim->cell_size) - text_x_size) / 2;
538 local_y_offset = ((PSW_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 539 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (PSW_Z_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
540 ((PSW_Y_OFFSET+PSW_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "Z", 1);
541}
542
543// TODO: Explain
544static void
545draw_stack(struct NEDsim * nedsim)
546{
547#define STACK_X_OFFSET 2
548#define STACK_Y_OFFSET 12
549#define STACK_WIDTH 32
550#define STACK_LABEL_HEIGHT 2
551#define STACK_LIGHT_HEIGHT 1
552
553 // First draw the two colored boxes that comprise the stack area.
554 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
555 draw_rect_area(nedsim, STACK_X_OFFSET, STACK_Y_OFFSET, STACK_WIDTH, STACK_LABEL_HEIGHT, True, True, False, False);
556 for (int i = 0; i < nedsim->num_data_rows; i++) {
557 draw_wordline(nedsim, STACK_X_OFFSET, STACK_Y_OFFSET+STACK_LABEL_HEIGHT+i);
558 }
559
560 // Now draw the label text "Stack Size:".
561 set_color(nedsim, &color_list[nedsim->color_index].text);
562 int text_x_size, text_y_size;
563 get_text_size(nedsim, "Stack Size:", &text_x_size, &text_y_size);
564 int local_y_offset = ((STACK_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 565 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((STACK_X_OFFSET + 1) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
566 ((STACK_Y_OFFSET+STACK_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "Stack Size:", 11);
567}
568
569// TODO: Explain
570static void
571draw_heap(struct NEDsim * nedsim)
572{
573#define HEAP_X_OFFSET 36
574#define HEAP_Y_OFFSET 12
575#define HEAP_WIDTH 32
576#define HEAP_LABEL_HEIGHT 2
577#define HEAP_LIGHT_HEIGHT 1
578// TODO: What should I do about this define? I would like to be able to specify the address so I can do things like display the code itself if nothign interesting happens in RAM.
579#define HEAP_START_ADDRESS 0x20000000
580
581 // First draw the two colored boxes that comprise the heap area.
582 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
583 draw_rect_area(nedsim, HEAP_X_OFFSET, HEAP_Y_OFFSET, HEAP_WIDTH, HEAP_LABEL_HEIGHT, True, True, False, False);
584 for (int i = 0; i < nedsim->num_data_rows; i++) {
585 draw_wordline(nedsim, HEAP_X_OFFSET, HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT+i);
586 }
587
588 // Now draw the label text "RAM Base:".
589 set_color(nedsim, &color_list[nedsim->color_index].text);
590 int text_x_size, text_y_size;
591 get_text_size(nedsim, "RAM Base:", &text_x_size, &text_y_size);
592 int local_y_offset = ((HEAP_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 593 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((HEAP_X_OFFSET + 1) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
594 ((HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "RAM Base:", 9);
595
596 // Now draw the address text.
597 set_color(nedsim, &color_list[nedsim->color_index].text);
598 char address[11];
599 snprintf(address, sizeof(address), "0x%08X", HEAP_START_ADDRESS);
600 get_text_size(nedsim, address, &text_x_size, &text_y_size);
601 local_y_offset = ((HEAP_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 602 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((HEAP_X_OFFSET + 1 + (HEAP_WIDTH / 2)) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
603 ((HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), address, strlen(address));
604}
605
606// TODO: Explain
607static void
608update_display(struct NEDsim * nedsim)
609{
610 // Draw the halt indicator.
611 if (nedsim->nedstate->halted) {
612 set_color(nedsim, &color_list[nedsim->color_index].error_on);
613 } else {
614 set_color(nedsim, &color_list[nedsim->color_index].error_off);
615 }
616 draw_circular_area(nedsim, HALT_X_OFFSET, HALT_Y_OFFSET, HALT_WIDTH);
617
618 // Draw the PSW "N" light.
619 if (nedsim->nedstate->active_thread->psw->negative) {
620 set_color(nedsim, &color_list[nedsim->color_index].light_on);
621 } else {
622 set_color(nedsim, &color_list[nedsim->color_index].light_off);
623 }
624 draw_circular_area(nedsim, PSW_N_X_OFFSET+1, PSW_Y_OFFSET+PSW_LABEL_HEIGHT, PSW_LIGHT_HEIGHT);
625
626 // Draw the PSW "Z" light.
627 if (nedsim->nedstate->active_thread->psw->zero) {
628 set_color(nedsim, &color_list[nedsim->color_index].light_on);
629 } else {
630 set_color(nedsim, &color_list[nedsim->color_index].light_off);
631 }
632 draw_circular_area(nedsim, PSW_Z_X_OFFSET+1, PSW_Y_OFFSET+PSW_LABEL_HEIGHT, PSW_LIGHT_HEIGHT);
633
634
635 // Draw the SC.
636 int i;
637 for (i = 0; i < SC_WIDTH; i++) {
638 if ((SC_WIDTH-1-i) == nedsim->nedstate->active_thread->sc) {
639 set_color(nedsim, &color_list[nedsim->color_index].light_on);
640 } else {
641 set_color(nedsim, &color_list[nedsim->color_index].light_off);
642 }
643 draw_circular_area(nedsim, SC_X_OFFSET+i, SC_Y_OFFSET+SC_LABEL_HEIGHT, SC_LIGHT_HEIGHT);
644 }
645
646 // Draw the PC.
647 draw_wordline_lights(nedsim, nedsim->nedstate->active_thread->pc, PC_X_OFFSET, PC_Y_OFFSET+PC_LABEL_HEIGHT);
648
649 // Draw the stack lights.
650 int64_t top_of_stack = ((int64_t)nedsim->nedstate->active_thread->sp) - 1;
651 for (i = 0; i < nedsim->num_data_rows; i++) {
652 if ((top_of_stack-i) >= 0) {
653 draw_wordline_lights(nedsim, nedsim->nedstate->active_thread->stack[top_of_stack-i], STACK_X_OFFSET, STACK_Y_OFFSET+STACK_LABEL_HEIGHT+i);
654 } else {
655 draw_wordline_lights(nedsim, 0, STACK_X_OFFSET, STACK_Y_OFFSET+STACK_LABEL_HEIGHT+i);
656 }
657 }
658
659 // Draw the stack size in text.
660 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
661 draw_rect_area(nedsim, STACK_X_OFFSET+(STACK_WIDTH/2), STACK_Y_OFFSET, STACK_WIDTH/2, STACK_LABEL_HEIGHT, True, True, False, False);
662 set_color(nedsim, &color_list[nedsim->color_index].text);
663 char stack_size[11];
664 snprintf(stack_size, sizeof(stack_size), "0x%08X", nedsim->nedstate->active_thread->sp);
665 int text_x_size, text_y_size;
666 get_text_size(nedsim, stack_size, &text_x_size, &text_y_size);
667 int local_y_offset = ((STACK_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 668 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((STACK_X_OFFSET + 1 + (STACK_WIDTH / 2)) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
669 ((STACK_Y_OFFSET+STACK_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), stack_size, strlen(stack_size));
670
671 // Draw the heap lights.
672 for (i = 0; i < nedsim->num_data_rows; i++) {
673 draw_wordline_lights(nedsim, ram_r_word(nedsim->nedstate, HEAP_START_ADDRESS+(i*BPW)), HEAP_X_OFFSET, HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT+i);
674 }
675}
676
677/* -------------------------------------------------------------------------- */
678/* Screenhack API Functions */
679/* -------------------------------------------------------------------------- */
680
681static Bool
682NEDsim_event(Display * dpy, Window win, void * closure, XEvent * event)
683{
684 return False;
685}
686
687static void
688NEDsim_free(Display * dpy, Window win, void * closure)
689{
84b74595 690 struct NEDsim * nedsim = closure;
d87b1e06
AT
691
692 if (nedsim->nedstate != NULL) {
693 free(nedsim->nedstate->active_thread->psw);
694 free(nedsim->nedstate->active_thread);
695 free(nedsim->nedstate->hack);
696 free(nedsim->nedstate);
697 }
698
699 // TODO: Replace all this with proper code to free everything related to the screensaver itself.
84b74595 700 XFreeGC(nedsim->dpy, nedsim->gc);
839b757b 701 XFreePixmap(nedsim->dpy, nedsim->panel);
84b74595
AT
702 free(nedsim);
703}
704
705static void *
706NEDsim_init(Display * dpy, Window win)
707{
2c7168ae
AT
708 // =========================================================================
709 // Basic Setup
710 // =========================================================================
711
84b74595 712 struct NEDsim * nedsim;
84b74595
AT
713
714 nedsim = calloc(1, sizeof(*nedsim));
715 if (!nedsim) {
716 fprintf(stderr, "ERROR: Failed to calloc() for NEDsim struct in NEDsim_init().\n");
717 exit(EXIT_FAILURE);
718 }
719
720 nedsim->dpy = dpy;
721 nedsim->win = win;
722
2c7168ae
AT
723 XGCValues gcv;
724 nedsim->gc = XCreateGC(nedsim->dpy, nedsim->win, GCForeground, &gcv);
725
726 XWindowAttributes xgwa;
84b74595
AT
727 XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa);
728 nedsim->dpy_width = xgwa.width;
729 nedsim->dpy_height = xgwa.height;
730
2c7168ae
AT
731 nedsim->panel = XCreatePixmap(nedsim->dpy, nedsim->win, nedsim->dpy_width, nedsim->dpy_height, xgwa.depth);
732
733 // =========================================================================
734 // Process User Requests
735 // =========================================================================
736
84b74595
AT
737 nedsim->delay = get_integer_resource(nedsim->dpy, "delay", "Integer");
738 nedsim->delay *= 1000; /* Turn milliseconds into microseconds. */
739
2c7168ae
AT
740 // If the user did not supply a program for execution, randomly select one
741 // of the included programs.
5923644e
AT
742 char * input_file = get_string_resource(nedsim->dpy, "binary", "String");
743 if (input_file == NULL) {
744 // TODO: Need to include some default programs and randomly select one to load into RAM.
745 nedsim->nedstate = init_simulator("./test.out");
746 } else {
747 nedsim->nedstate = init_simulator(input_file);
748 }
84b74595 749
2c7168ae 750 // If the user did not select a color scheme, select one randomly.
f93fc861
AT
751 nedsim->color_index = get_integer_resource(nedsim->dpy, "color", "Integer");
752 if (nedsim->color_index == -1) {
753 nedsim->color_index = random() % sizeof(color_list)/sizeof(color_list[0]);
754 } else if (nedsim->color_index >= sizeof(color_list)/sizeof(color_list[0])) {
755 fprintf(stderr, "WARNING: Color index out of range.\n");
756 nedsim->color_index = 0;
757 }
84b74595 758
2c7168ae
AT
759 // =========================================================================
760 // Scale Panel To Screen
761 // =========================================================================
84b74595 762
2c7168ae
AT
763 // We desire to make the panel as wide as possible while also making the
764 // cell size an integer pixel size.
765 nedsim->cell_size = nedsim->dpy_width / OVERALL_WIDTH_IN_CELLS;
766 // Cell sizes below about 10 pixels result in unreadable fonts. For now,
767 // we'll use that as our cutoff. It also works well with a 1:10 border:cell
768 // ratio.
84b74595
AT
769 if (nedsim->cell_size < 10) {
770 nedsim->suitable_display = False;
771 return nedsim;
772 }
2c7168ae
AT
773 // Center the panel horizontally.
774 nedsim->origin_x_offset = (nedsim->dpy_width -
775 (nedsim->cell_size * OVERALL_WIDTH_IN_CELLS)) / 2;
776
777 // The stack and heap displays are variable height. Size them to fit the
778 // display window.
779 int available_space_for_data_rows = nedsim->dpy_height -
780 (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + FOOTER_HEIGHT_IN_CELLS));
84b74595
AT
781 for (int i = 0; ; i++) {
782 if ((nedsim->cell_size * (1 << i)) > available_space_for_data_rows) {
783 nedsim->num_data_rows = (1 << --i);
784 break;
785 }
786 }
2c7168ae 787 // Enforce a minimum vertical size, though '4' is arbitrary.
84b74595
AT
788 if (nedsim->num_data_rows < 4) {
789 nedsim->suitable_display = False;
790 return nedsim;
791 }
2c7168ae
AT
792 // Center the panel vertically.
793 nedsim->origin_y_offset = (nedsim->dpy_height -
794 (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS))) / 2;
84b74595
AT
795
796 // Scale border relative to cell_size in a 1:10 relationship.
797 nedsim->border_size = nedsim->cell_size / 10;
798
2c7168ae
AT
799 // =========================================================================
800 // Draw Initial Panel
801 // =========================================================================
802
84b74595
AT
803 draw_panel(nedsim);
804 draw_logo(nedsim);
805 draw_halt(nedsim);
806 draw_pc(nedsim);
807 draw_sc(nedsim);
808 draw_psw(nedsim);
809 draw_stack(nedsim);
810 draw_heap(nedsim);
811
812 nedsim->suitable_display = True;
813
814 return nedsim;
815}
816
817static unsigned long
818NEDsim_draw(Display * dpy, Window win, void * closure)
819{
820 struct NEDsim * nedsim = closure;
821
2c7168ae 822 // Update the panel display buffer.
84b74595
AT
823 if (nedsim->suitable_display) {
824 nedsim->nedstate = run_simulator(nedsim->nedstate);
825 update_display(nedsim);
826 } else {
827 set_color(nedsim, &color_list[nedsim->color_index].error_on);
839b757b 828 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, 0, 0, nedsim->dpy_width, nedsim->dpy_height);
84b74595
AT
829 }
830
2c7168ae 831 // Copy panel buffer to display window.
839b757b
AT
832 XCopyArea(nedsim->dpy, nedsim->panel, nedsim->win, nedsim->gc, 0, 0, nedsim->dpy_width, nedsim->dpy_height, 0, 0);
833
84b74595
AT
834 return nedsim->delay;
835}
836
837static void
838NEDsim_reshape(Display * dpy, Window win, void * closure, unsigned int w, unsigned int h)
839{
840 struct NEDsim * nedsim = closure;
2c7168ae
AT
841
842 // Only re-initialize the display if it changed size.
84b74595
AT
843 XWindowAttributes xgwa;
844 XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa);
84b74595 845 if (nedsim->dpy_width != xgwa.width || nedsim->dpy_height != xgwa.height) {
2c7168ae
AT
846 // Although we are re-initializing the display, we wish to retain the
847 // in-progress NED simulation. Thus, we retain the NEDstate pointer.
503470d7
AT
848 struct NEDstate * original_nedstate = nedsim->nedstate;
849 nedsim->nedstate = NULL;
850 NEDsim_free(dpy, win, nedsim);
851 struct NEDsim * new_nedsim = NEDsim_init(dpy, win);
852 new_nedsim->nedstate = original_nedstate;
853 closure = new_nedsim;
84b74595
AT
854 }
855}
856
857static const char * NEDsim_defaults[] = {
858 "*delay: 250",
f93fc861 859 "*color: -1",
84b74595
AT
860 0
861};
862
863static XrmOptionDescRec NEDsim_options[] = {
864 { "-delay", ".delay", XrmoptionSepArg, 0 },
5923644e 865 { "-binary", ".binary", XrmoptionSepArg, 0 },
f93fc861 866 { "-color", ".color", XrmoptionSepArg, 0 },
84b74595
AT
867 { 0, 0, 0, 0 }
868};
869
870XSCREENSAVER_MODULE ("Blinken-lights simulator for NED1 CPU architecture.", NEDsim)