Made NEDsim double-buffered.
[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 {
12 /* Various X resources */
13 Display * dpy;
14 Window win;
15 GC gc;
16
17 // TODO: Explain these
18 int dpy_width, dpy_height;
19
20 // TODO: Explain that this is created during init, then lights are populated/overwritten on each frame.
21 Pixmap panel;
22
23 /* Delay (in microseconds) between clock cycles in the NED CPU. */
24 size_t delay;
25
26 int cell_size;
27 int border_size;
28 int origin_x_offset, origin_y_offset;
29 int num_data_rows;
30
31 size_t color_index;
32
33 Bool suitable_display;
34
35 char * current_font;
36
37 // TODO: Explain that this contains all the actual state of the NED machine being simulated.
38 struct NEDstate * nedstate;
39};
40
41struct color_rgb {
42 // TODO: Explain why this is an unsigned short. Copy from WolframAutomata.
43 unsigned short red, green, blue;
44};
45
46struct color_scheme {
47 // TODO: Explain all this.
48 struct color_rgb
49 panel_bg,
50 panel_fg,
51 light_on,
52 light_off,
53 error_on,
54 error_off,
55 primary,
56 secondary,
57 tertiary,
58 border,
59 text;
60};
61
84b74595
AT
62static struct color_scheme color_list[] = {
63 // TODO: Explain all this.
64 // TODO: Add other color schemes, like purple PDP-11/70.
65 // TODO: http://www.chdickman.com/pdp8/DECcolors/
66 {
67 {63479,63479,63479}, // 092-XXXX-123
68 { 5140, 2056, 5654}, // 092-XXXX-152
69 {65535,11051,15677}, // 092-XXXX-139 - edit
70 {20000,13107,12850}, // 092-XXXX-154 - edit
71 {65535,13107,12850}, // homemade
72 {20000,13107,12850}, // 092-XXXX-154 - edit
73 { 4112,12850,20046}, // 092-XXXX-145
74 {12336,29555,37008}, // 092-XXXX-151
75 {30326,24158, 6425}, // 092-XXXX-157
76 {63479,63479,63479}, // 092-XXXX-123
77 {63479,63479,63479} // 092-XXXX-123
78 }
79};
80
81/* -------------------------------------------------------------------------- */
82/* Helper Functions */
83/* -------------------------------------------------------------------------- */
84
85// TODO: Explain
86static void
87set_color(struct NEDsim * nedsim, struct color_rgb * color)
88{
89 XColor temp;
90 XWindowAttributes xgwa;
91
92 XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa);
93
94 temp.red = color->red;
95 temp.green = color->green;
96 temp.blue = color->blue;
97
98 XAllocColor(nedsim->dpy, xgwa.colormap, &temp);
99 XSetForeground(nedsim->dpy, nedsim->gc, temp.pixel);
100}
101
102// TODO: Explain
103// TODO: Make this a lot faster.
104// Input: size in 'cells', and sets font to fill that size, minus border and padding room.
105static void
106set_font_size(struct NEDsim * nedsim, int size)
107{
108 // vvv--- for border ---vvv vvv--- for padding ---vvv
109 int desired_height_in_pixels = (size * nedsim->cell_size) - (2 * nedsim->border_size) - (8 * nedsim->border_size);
110
111 const char * font_size_prefix = "-*-helvetica-*-r-*-*-";
112 const char * font_size_suffix = "-*-*-*-*-*-*-*";
113
114 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.
115 char * font_full_name = malloc(buffer_size);
116 int font_size_in_points = 2; // Start with a 2 pt font and work our way up.
117
118 while (1) {
119 // Load the font.
120 snprintf(font_full_name, buffer_size, "%s%d%s", font_size_prefix, font_size_in_points, font_size_suffix);
121 XFontStruct * font = XLoadQueryFont(nedsim->dpy, font_full_name);
122 if (!font) {
123 printf("WARNING: Unable to load font %s. Probably gonna look wonky.\n", font_full_name);
124 font = XLoadQueryFont(nedsim->dpy, "fixed");
125 break;
126 }
127 XSetFont(nedsim->dpy, nedsim->gc, font->fid);
128
129 // Get the height.
130 int direction, ascent, descent;
131 XCharStruct overall;
132 XTextExtents(font, "X", 1, &direction, &ascent, &descent, &overall);
133
134 // Compare the height.
135 int height = overall.ascent - overall.descent;
136 if (height == desired_height_in_pixels) {
137 break;
138 } else if (height > desired_height_in_pixels) {
139 font_size_in_points--;
140 snprintf(font_full_name, buffer_size, "%s%d%s", font_size_prefix, font_size_in_points, font_size_suffix);
141 XFontStruct * font = XLoadQueryFont(nedsim->dpy, font_full_name);
142 if (!font) {
143 printf("WARNING: Unable to load font %s. Probably gonna look wonky.\n", font_full_name);
144 font = XLoadQueryFont(nedsim->dpy, "fixed");
145 break;
146 }
147 XSetFont(nedsim->dpy, nedsim->gc, font->fid);
148 break;
149 } else {
150 font_size_in_points++;
151 }
152 }
153
154 free(nedsim->current_font);
155 nedsim->current_font = font_full_name;
156}
157
158// TODO: Explain
159// TODO: Explain that this returns result in pixels so we can track fractional cell usage.
160static void
161get_text_size(struct NEDsim * nedsim, const char * text, int * x_size, int * y_size)
162{
163 int direction, ascent, descent;
164 XCharStruct overall;
165 XFontStruct * font = XLoadQueryFont(nedsim->dpy, nedsim->current_font);
166 XTextExtents(font, text, strlen(text), &direction, &ascent, &descent, &overall);
167 *x_size = overall.width;
168 *y_size = overall.ascent - overall.descent;
169}
170
171// TODO: Explain
172// TODO: Note that this might leave the foreground color changed.
173// Argument coordinates are in 'cells', not pixels.
174static void
175draw_rect_area(struct NEDsim * nedsim, size_t x_origin, size_t y_origin, size_t x_size, size_t y_size,
176 Bool bord_top, Bool bord_bottom, Bool bord_left, Bool bord_right)
177{
178 x_origin *= nedsim->cell_size;
179 x_origin += nedsim->origin_x_offset;
180 y_origin *= nedsim->cell_size;
181 y_origin += nedsim->origin_y_offset;
182 x_size *= nedsim->cell_size;
183 y_size *= nedsim->cell_size;
839b757b 184 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, y_origin, x_size, y_size);
84b74595
AT
185
186 set_color(nedsim, &color_list[nedsim->color_index].border);
187 if (bord_top) {
839b757b 188 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, y_origin, x_size, nedsim->border_size);
84b74595
AT
189 }
190 if (bord_bottom) {
839b757b 191 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, (y_origin + y_size - nedsim->border_size), x_size, nedsim->border_size);
84b74595
AT
192 }
193 if (bord_left) {
839b757b 194 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, x_origin, y_origin, nedsim->border_size, y_size);
84b74595
AT
195 }
196 if (bord_right) {
839b757b 197 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, (x_origin + x_size - nedsim->border_size), y_origin, nedsim->border_size, y_size);
84b74595
AT
198 }
199}
200
201// TODO: Explain
202// Arguments are in units of 'cells', not pixels.
203// Will leave foreground color changed.
204// Draws filled circle with upper left corner at x,y.
205static void
206draw_circular_area(struct NEDsim * nedsim, size_t x, size_t y, double diameter)
207{
208 // First, convert the function argument units from 'cells' to 'pixels'
209 x *= nedsim->cell_size;
210 y *= nedsim->cell_size;
211 diameter *= nedsim->cell_size;
212
213 // Add the panel's absolute x,y offset to the requested coordinates.
214 x += nedsim->origin_x_offset;
215 y += nedsim->origin_y_offset;
216
217 // Shrink the circle to be a bit smaller than the bounding box allows. Note
218 // that the three adjustment values must sum to 1.0.
219 // For example, 0.7 + 0.15 + 0.15 = 1.0.
220 x += (0.15 * diameter);
221 y += (0.15 * diameter);
222 diameter *= 0.7;
223
224 // Because we only draw the bottom border on repeated rows (e.g. draw_wordline()),
225 // we need to offset vertically by half a border height.
226 y -= (0.5 * nedsim->border_size);
227
228 // Start angle 0 and ending angle 360*64 is one full circle in Xlib units.
839b757b 229 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc, x, y, diameter, diameter, 0, 360*64);
84b74595
AT
230}
231
232// TODO: Explain
233static void
234draw_panel(struct NEDsim * nedsim)
235{
236// TODO: Collect all relevant #defines somewhere.
237#define OVERALL_WIDTH_IN_CELLS 70
238#define HEADER_HEIGHT_IN_CELLS 14
239#define FOOTER_HEIGHT_IN_CELLS 2
240
241 // Draw background color over entire window.
242 set_color(nedsim, &color_list[nedsim->color_index].panel_bg);
839b757b 243 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, 0, 0, nedsim->dpy_width, nedsim->dpy_height);
84b74595
AT
244
245 // Draw NED panel in foreground color.
246 set_color(nedsim, &color_list[nedsim->color_index].panel_fg);
839b757b 247 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
84b74595
AT
248 nedsim->origin_x_offset,
249 nedsim->origin_y_offset,
250 nedsim->cell_size * OVERALL_WIDTH_IN_CELLS,
251 nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS)
252 );
a98a69c1
AT
253
254 // Give the panel rounded corners by first deleting the four right-angle corners...
255 set_color(nedsim, &color_list[nedsim->color_index].panel_bg);
839b757b 256 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
257 nedsim->origin_x_offset,
258 nedsim->origin_y_offset,
259 nedsim->cell_size,
260 nedsim->cell_size
261 );
839b757b 262 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
263 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-1)),
264 nedsim->origin_y_offset,
265 nedsim->cell_size,
266 nedsim->cell_size
267 );
839b757b 268 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
269 nedsim->origin_x_offset,
270 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 1)),
271 nedsim->cell_size,
272 nedsim->cell_size
273 );
839b757b 274 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
275 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-1)),
276 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 1)),
277 nedsim->cell_size,
278 nedsim->cell_size
279 );
280 // ...and then replacing them with filled arcs, forming rounded corners.
281 set_color(nedsim, &color_list[nedsim->color_index].panel_fg);
839b757b 282 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
283 nedsim->origin_x_offset,
284 nedsim->origin_y_offset,
285 nedsim->cell_size * 2, nedsim->cell_size * 2,
286 180*64, -90*64
287 );
839b757b 288 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
289 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-2)),
290 nedsim->origin_y_offset,
291 nedsim->cell_size * 2, nedsim->cell_size * 2,
292 0, 90*64
293 );
839b757b 294 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
295 nedsim->origin_x_offset,
296 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 2)),
297 nedsim->cell_size * 2, nedsim->cell_size * 2,
298 180*64, 90*64
299 );
839b757b 300 XFillArc(nedsim->dpy, nedsim->panel, nedsim->gc,
a98a69c1
AT
301 nedsim->origin_x_offset + (nedsim->cell_size * (OVERALL_WIDTH_IN_CELLS-2)),
302 nedsim->origin_y_offset + (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS - 2)),
303 nedsim->cell_size * 2, nedsim->cell_size * 2,
304 0, -90*64
305 );
84b74595
AT
306}
307
308// TODO: Explain
309static void
310draw_logo(struct NEDsim * nedsim)
311{
312#define LOGO_X_OFFSET 2
313#define LOGO_Y_OFFSET 2
314#define LOGO_WIDTH 20
315#define LOGO_NAME_HEIGHT 6
316#define LOGO_WEBSITE_HEIGHT 2
317
318 // First draw the two colored boxes that comprise the logo area.
319 set_color(nedsim, &color_list[nedsim->color_index].primary);
320 draw_rect_area(nedsim, LOGO_X_OFFSET, LOGO_Y_OFFSET, LOGO_WIDTH, LOGO_NAME_HEIGHT, True, True, False, False);
321 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
322 draw_rect_area(nedsim, LOGO_X_OFFSET, LOGO_Y_OFFSET+LOGO_NAME_HEIGHT, LOGO_WIDTH, LOGO_WEBSITE_HEIGHT, False, True, False, False);
323
324 // Now draw the 'NED' text in the top box.
325 set_color(nedsim, &color_list[nedsim->color_index].text);
326 set_font_size(nedsim, LOGO_NAME_HEIGHT);
327 int text_x_size, text_y_size;
328 get_text_size(nedsim, "NED", &text_x_size, &text_y_size);
329 int local_x_offset = ((LOGO_WIDTH * nedsim->cell_size) - text_x_size) / 2;
330 int local_y_offset = ((LOGO_NAME_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 331 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (LOGO_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
332 ((LOGO_Y_OFFSET+LOGO_NAME_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "NED", 3);
333
334 // And draw the 'subgeniuskitty.com' text in the bottom box.
335 set_font_size(nedsim, LOGO_WEBSITE_HEIGHT);
336 get_text_size(nedsim, "subgeniuskitty.com", &text_x_size, &text_y_size);
337 local_x_offset = ((LOGO_WIDTH * nedsim->cell_size) - text_x_size) / 2;
338 local_y_offset = ((LOGO_WEBSITE_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 339 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (LOGO_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
340 ((LOGO_Y_OFFSET+LOGO_NAME_HEIGHT+LOGO_WEBSITE_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "subgeniuskitty.com", 18);
341}
342
343// TODO: Explain
344static void
345draw_halt(struct NEDsim * nedsim)
346{
347#define HALT_X_OFFSET 26
348#define HALT_Y_OFFSET 2
349#define HALT_WIDTH 6
350#define HALT_LIGHT_HEIGHT 6
351#define HALT_LABEL_HEIGHT 2
352
353 // First draw the two colored boxes that comprise the halt area.
354 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
355 draw_rect_area(nedsim, HALT_X_OFFSET, HALT_Y_OFFSET, HALT_WIDTH, HALT_LIGHT_HEIGHT, True, True, False, False);
356 set_color(nedsim, &color_list[nedsim->color_index].secondary);
357 draw_rect_area(nedsim, HALT_X_OFFSET, HALT_Y_OFFSET+HALT_LIGHT_HEIGHT, HALT_WIDTH, HALT_LABEL_HEIGHT, False, True, False, False);
358
359 // And finally, draw the label.
360 set_color(nedsim, &color_list[nedsim->color_index].text);
361 int text_x_size, text_y_size;
362 get_text_size(nedsim, "HALT", &text_x_size, &text_y_size);
363 int local_x_offset = ((HALT_WIDTH * nedsim->cell_size) - text_x_size) / 2;
364 int local_y_offset = ((HALT_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 365 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (HALT_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
366 ((HALT_Y_OFFSET+HALT_LIGHT_HEIGHT+HALT_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "HALT", 4);
367}
368
369// TODO: Explain
370static void
371draw_wordline_lights(struct NEDsim * nedsim, uint32_t word, int x, int y)
372{
373#define WORDLINE_BITS_PER_STRIPE 4
374#define WORDLINE_WIDTH 32
375#define WORDLINE_HEIGHT 1
376
377 for (int i = 0; i < WORDLINE_WIDTH; i++) {
378 if (word & (1<<(WORDLINE_WIDTH-1-i))) {
379 set_color(nedsim, &color_list[nedsim->color_index].light_on);
380 } else {
381 set_color(nedsim, &color_list[nedsim->color_index].light_off);
382 }
383 draw_circular_area(nedsim, x+i, y, WORDLINE_HEIGHT);
384 }
385}
386
387// TODO: Explain
388static void
389draw_wordline(struct NEDsim * nedsim, int x, int y)
390{
391 // First, draw a solid box in the primary color over the entire wordline area.
392 set_color(nedsim, &color_list[nedsim->color_index].primary);
393 draw_rect_area(nedsim, x, y, WORDLINE_WIDTH, WORDLINE_HEIGHT, False, True, False, False);
394
395 // Now, draw stripes in the secondary color.
396 int i;
397 for (i = 0; i < (WORDLINE_WIDTH/(2*WORDLINE_BITS_PER_STRIPE)); i++) {
398 set_color(nedsim, &color_list[nedsim->color_index].secondary);
399 draw_rect_area(nedsim, (x+(i*(WORDLINE_WIDTH/WORDLINE_BITS_PER_STRIPE))), y,
400 WORDLINE_BITS_PER_STRIPE, WORDLINE_HEIGHT, False, True, False, False);
401 }
402
403 // Finally, draw the lights.
404 draw_wordline_lights(nedsim, 0, x, y);
405}
406
407// TODO: Explain
408static void
409draw_pc(struct NEDsim * nedsim)
410{
411// TODO: Note that all #defines use units of 'cells', not 'pixels', etc.
412#define PC_X_OFFSET 36
413#define PC_Y_OFFSET 7
414#define PC_WIDTH 32
415#define PC_LABEL_HEIGHT 2
416#define PC_LIGHT_HEIGHT 1
417
418 // First draw the two colored boxes that comprise the PC area.
419 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
420 draw_rect_area(nedsim, PC_X_OFFSET, PC_Y_OFFSET, PC_WIDTH, PC_LABEL_HEIGHT, True, True, False, False);
421 draw_wordline(nedsim, PC_X_OFFSET, PC_Y_OFFSET+PC_LABEL_HEIGHT);
422
423 // Now draw the label text "PC".
424 set_color(nedsim, &color_list[nedsim->color_index].text);
425 int text_x_size, text_y_size;
426 get_text_size(nedsim, "PC", &text_x_size, &text_y_size);
427 int local_x_offset = ((PC_WIDTH * nedsim->cell_size) - text_x_size) / 2;
428 int local_y_offset = ((PC_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 429 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (PC_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
430 ((PC_Y_OFFSET+PC_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "PC", 2);
431}
432
433// TODO: Explain
434static void
435draw_sc(struct NEDsim * nedsim)
436{
437#define SC_X_OFFSET 42
438#define SC_Y_OFFSET 2
439#define SC_WIDTH 5
440#define SC_LABEL_HEIGHT 2
441#define SC_LIGHT_HEIGHT 1
442
443 // First draw the two colored boxes that comprise the SC area.
444 set_color(nedsim, &color_list[nedsim->color_index].secondary);
445 draw_rect_area(nedsim, SC_X_OFFSET, SC_Y_OFFSET, SC_WIDTH, SC_LABEL_HEIGHT, True, True, False, False);
446 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
447 draw_rect_area(nedsim, SC_X_OFFSET, SC_Y_OFFSET+SC_LABEL_HEIGHT, SC_WIDTH, SC_LIGHT_HEIGHT, False, True, False, False);
448
449 // Now draw the label text "SC".
450 set_color(nedsim, &color_list[nedsim->color_index].text);
451 int text_x_size, text_y_size;
452 get_text_size(nedsim, "SC", &text_x_size, &text_y_size);
453 int local_x_offset = ((SC_WIDTH * nedsim->cell_size) - text_x_size) / 2;
454 int local_y_offset = ((SC_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 455 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (SC_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
456 ((SC_Y_OFFSET+SC_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "SC", 2);
457}
458
459// TODO: Explain
460static void
461draw_psw(struct NEDsim * nedsim)
462{
463#define PSW_N_X_OFFSET 51
464#define PSW_Z_X_OFFSET 58
465#define PSW_Y_OFFSET 2
466#define PSW_LABEL_WIDTH 3
467#define PSW_LABEL_HEIGHT 2
468#define PSW_LIGHT_WIDTH 1
469#define PSW_LIGHT_HEIGHT 1
470
471 // First draw the four colored boxes that comprise the two PSW areas.
472 set_color(nedsim, &color_list[nedsim->color_index].secondary);
473 draw_rect_area(nedsim, PSW_N_X_OFFSET, PSW_Y_OFFSET, PSW_LABEL_WIDTH, PSW_LABEL_HEIGHT, True, True, False, False);
474 set_color(nedsim, &color_list[nedsim->color_index].secondary);
475 draw_rect_area(nedsim, PSW_Z_X_OFFSET, PSW_Y_OFFSET, PSW_LABEL_WIDTH, PSW_LABEL_HEIGHT, True, True, False, False);
476 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
477 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);
478 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
479 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);
480
481 // Now draw the label text "N".
482 set_color(nedsim, &color_list[nedsim->color_index].text);
483 int text_x_size, text_y_size;
484 get_text_size(nedsim, "N", &text_x_size, &text_y_size);
485 int local_x_offset = ((PSW_LABEL_WIDTH * nedsim->cell_size) - text_x_size) / 2;
486 int local_y_offset = ((PSW_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 487 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (PSW_N_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
488 ((PSW_Y_OFFSET+PSW_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "N", 1);
489
490 // Now draw the label text "Z".
491 set_color(nedsim, &color_list[nedsim->color_index].text);
492 get_text_size(nedsim, "Z", &text_x_size, &text_y_size);
493 local_x_offset = ((PSW_LABEL_WIDTH * nedsim->cell_size) - text_x_size) / 2;
494 local_y_offset = ((PSW_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 495 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, (PSW_Z_X_OFFSET * nedsim->cell_size + nedsim->origin_x_offset + local_x_offset),
84b74595
AT
496 ((PSW_Y_OFFSET+PSW_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "Z", 1);
497}
498
499// TODO: Explain
500static void
501draw_stack(struct NEDsim * nedsim)
502{
503#define STACK_X_OFFSET 2
504#define STACK_Y_OFFSET 12
505#define STACK_WIDTH 32
506#define STACK_LABEL_HEIGHT 2
507#define STACK_LIGHT_HEIGHT 1
508
509 // First draw the two colored boxes that comprise the stack area.
510 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
511 draw_rect_area(nedsim, STACK_X_OFFSET, STACK_Y_OFFSET, STACK_WIDTH, STACK_LABEL_HEIGHT, True, True, False, False);
512 for (int i = 0; i < nedsim->num_data_rows; i++) {
513 draw_wordline(nedsim, STACK_X_OFFSET, STACK_Y_OFFSET+STACK_LABEL_HEIGHT+i);
514 }
515
516 // Now draw the label text "Stack Size:".
517 set_color(nedsim, &color_list[nedsim->color_index].text);
518 int text_x_size, text_y_size;
519 get_text_size(nedsim, "Stack Size:", &text_x_size, &text_y_size);
520 int local_y_offset = ((STACK_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 521 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((STACK_X_OFFSET + 1) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
522 ((STACK_Y_OFFSET+STACK_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "Stack Size:", 11);
523}
524
525// TODO: Explain
526static void
527draw_heap(struct NEDsim * nedsim)
528{
529#define HEAP_X_OFFSET 36
530#define HEAP_Y_OFFSET 12
531#define HEAP_WIDTH 32
532#define HEAP_LABEL_HEIGHT 2
533#define HEAP_LIGHT_HEIGHT 1
534// 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.
535#define HEAP_START_ADDRESS 0x20000000
536
537 // First draw the two colored boxes that comprise the heap area.
538 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
539 draw_rect_area(nedsim, HEAP_X_OFFSET, HEAP_Y_OFFSET, HEAP_WIDTH, HEAP_LABEL_HEIGHT, True, True, False, False);
540 for (int i = 0; i < nedsim->num_data_rows; i++) {
541 draw_wordline(nedsim, HEAP_X_OFFSET, HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT+i);
542 }
543
544 // Now draw the label text "RAM Base:".
545 set_color(nedsim, &color_list[nedsim->color_index].text);
546 int text_x_size, text_y_size;
547 get_text_size(nedsim, "RAM Base:", &text_x_size, &text_y_size);
548 int local_y_offset = ((HEAP_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 549 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((HEAP_X_OFFSET + 1) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
550 ((HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), "RAM Base:", 9);
551
552 // Now draw the address text.
553 set_color(nedsim, &color_list[nedsim->color_index].text);
554 char address[11];
555 snprintf(address, sizeof(address), "0x%08X", HEAP_START_ADDRESS);
556 get_text_size(nedsim, address, &text_x_size, &text_y_size);
557 local_y_offset = ((HEAP_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 558 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((HEAP_X_OFFSET + 1 + (HEAP_WIDTH / 2)) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
559 ((HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), address, strlen(address));
560}
561
562// TODO: Explain
563static void
564update_display(struct NEDsim * nedsim)
565{
566 // Draw the halt indicator.
567 if (nedsim->nedstate->halted) {
568 set_color(nedsim, &color_list[nedsim->color_index].error_on);
569 } else {
570 set_color(nedsim, &color_list[nedsim->color_index].error_off);
571 }
572 draw_circular_area(nedsim, HALT_X_OFFSET, HALT_Y_OFFSET, HALT_WIDTH);
573
574 // Draw the PSW "N" light.
575 if (nedsim->nedstate->active_thread->psw->negative) {
576 set_color(nedsim, &color_list[nedsim->color_index].light_on);
577 } else {
578 set_color(nedsim, &color_list[nedsim->color_index].light_off);
579 }
580 draw_circular_area(nedsim, PSW_N_X_OFFSET+1, PSW_Y_OFFSET+PSW_LABEL_HEIGHT, PSW_LIGHT_HEIGHT);
581
582 // Draw the PSW "Z" light.
583 if (nedsim->nedstate->active_thread->psw->zero) {
584 set_color(nedsim, &color_list[nedsim->color_index].light_on);
585 } else {
586 set_color(nedsim, &color_list[nedsim->color_index].light_off);
587 }
588 draw_circular_area(nedsim, PSW_Z_X_OFFSET+1, PSW_Y_OFFSET+PSW_LABEL_HEIGHT, PSW_LIGHT_HEIGHT);
589
590
591 // Draw the SC.
592 int i;
593 for (i = 0; i < SC_WIDTH; i++) {
594 if ((SC_WIDTH-1-i) == nedsim->nedstate->active_thread->sc) {
595 set_color(nedsim, &color_list[nedsim->color_index].light_on);
596 } else {
597 set_color(nedsim, &color_list[nedsim->color_index].light_off);
598 }
599 draw_circular_area(nedsim, SC_X_OFFSET+i, SC_Y_OFFSET+SC_LABEL_HEIGHT, SC_LIGHT_HEIGHT);
600 }
601
602 // Draw the PC.
603 draw_wordline_lights(nedsim, nedsim->nedstate->active_thread->pc, PC_X_OFFSET, PC_Y_OFFSET+PC_LABEL_HEIGHT);
604
605 // Draw the stack lights.
606 int64_t top_of_stack = ((int64_t)nedsim->nedstate->active_thread->sp) - 1;
607 for (i = 0; i < nedsim->num_data_rows; i++) {
608 if ((top_of_stack-i) >= 0) {
609 draw_wordline_lights(nedsim, nedsim->nedstate->active_thread->stack[top_of_stack-i], STACK_X_OFFSET, STACK_Y_OFFSET+STACK_LABEL_HEIGHT+i);
610 } else {
611 draw_wordline_lights(nedsim, 0, STACK_X_OFFSET, STACK_Y_OFFSET+STACK_LABEL_HEIGHT+i);
612 }
613 }
614
615 // Draw the stack size in text.
616 set_color(nedsim, &color_list[nedsim->color_index].tertiary);
617 draw_rect_area(nedsim, STACK_X_OFFSET+(STACK_WIDTH/2), STACK_Y_OFFSET, STACK_WIDTH/2, STACK_LABEL_HEIGHT, True, True, False, False);
618 set_color(nedsim, &color_list[nedsim->color_index].text);
619 char stack_size[11];
620 snprintf(stack_size, sizeof(stack_size), "0x%08X", nedsim->nedstate->active_thread->sp);
621 int text_x_size, text_y_size;
622 get_text_size(nedsim, stack_size, &text_x_size, &text_y_size);
623 int local_y_offset = ((STACK_LABEL_HEIGHT * nedsim->cell_size) - text_y_size) / 2;
839b757b 624 XDrawString(nedsim->dpy, nedsim->panel, nedsim->gc, ((STACK_X_OFFSET + 1 + (STACK_WIDTH / 2)) * nedsim->cell_size + nedsim->origin_x_offset),
84b74595
AT
625 ((STACK_Y_OFFSET+STACK_LABEL_HEIGHT) * nedsim->cell_size + nedsim->origin_y_offset - local_y_offset), stack_size, strlen(stack_size));
626
627 // Draw the heap lights.
628 for (i = 0; i < nedsim->num_data_rows; i++) {
629 draw_wordline_lights(nedsim, ram_r_word(nedsim->nedstate, HEAP_START_ADDRESS+(i*BPW)), HEAP_X_OFFSET, HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT+i);
630 }
631}
632
633/* -------------------------------------------------------------------------- */
634/* Screenhack API Functions */
635/* -------------------------------------------------------------------------- */
636
637static Bool
638NEDsim_event(Display * dpy, Window win, void * closure, XEvent * event)
639{
640 return False;
641}
642
643static void
644NEDsim_free(Display * dpy, Window win, void * closure)
645{
84b74595 646 struct NEDsim * nedsim = closure;
d87b1e06
AT
647
648 if (nedsim->nedstate != NULL) {
649 free(nedsim->nedstate->active_thread->psw);
650 free(nedsim->nedstate->active_thread);
651 free(nedsim->nedstate->hack);
652 free(nedsim->nedstate);
653 }
654
655 // TODO: Replace all this with proper code to free everything related to the screensaver itself.
84b74595 656 XFreeGC(nedsim->dpy, nedsim->gc);
839b757b 657 XFreePixmap(nedsim->dpy, nedsim->panel);
84b74595
AT
658 free(nedsim);
659}
660
661static void *
662NEDsim_init(Display * dpy, Window win)
663{
664 struct NEDsim * nedsim;
665 XGCValues gcv;
666 XWindowAttributes xgwa;
667
668 nedsim = calloc(1, sizeof(*nedsim));
669 if (!nedsim) {
670 fprintf(stderr, "ERROR: Failed to calloc() for NEDsim struct in NEDsim_init().\n");
671 exit(EXIT_FAILURE);
672 }
673
674 nedsim->dpy = dpy;
675 nedsim->win = win;
676
677 XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa);
678 nedsim->dpy_width = xgwa.width;
679 nedsim->dpy_height = xgwa.height;
680
681 // TODO: Explain that this is the delay between each clock cycle of the simulated NED CPU.
682 nedsim->delay = get_integer_resource(nedsim->dpy, "delay", "Integer");
683 nedsim->delay *= 1000; /* Turn milliseconds into microseconds. */
684
5923644e
AT
685 // Load the program file specified by the user or, if none is specified,
686 // randomly select one of the included programs.
687 char * input_file = get_string_resource(nedsim->dpy, "binary", "String");
688 if (input_file == NULL) {
689 // TODO: Need to include some default programs and randomly select one to load into RAM.
690 nedsim->nedstate = init_simulator("./test.out");
691 } else {
692 nedsim->nedstate = init_simulator(input_file);
693 }
84b74595
AT
694
695 nedsim->gc = XCreateGC(nedsim->dpy, nedsim->win, GCForeground, &gcv);
839b757b 696 nedsim->panel = XCreatePixmap(nedsim->dpy, nedsim->win, nedsim->dpy_width, nedsim->dpy_height, xgwa.depth);
84b74595
AT
697
698 // TODO: Do this properly.
699 nedsim->color_index = 0;
700
701// TODO: Save the GIMP reference diagram somewhere, along with notes about the size/spacing, and that each cell is 10 pixels across in the reference image.
702
703 nedsim->cell_size = nedsim->dpy_width / OVERALL_WIDTH_IN_CELLS ; // make panel as wide as it can be while keeping every cell an integer pixel size.
704 // TODO: What is my minimum cell_size? Below that, I should simply paint the window red and print an error in the console. Perform that check here, right after setting cell_size.
705 // For now, we'll just make it 10 pixels?
706 if (nedsim->cell_size < 10) {
707 nedsim->suitable_display = False;
708 return nedsim;
709 }
710 nedsim->origin_x_offset = (nedsim->dpy_width - (nedsim->cell_size * OVERALL_WIDTH_IN_CELLS)) / 2; // center panel horizontally
711
712 // Determine how many rows for the stack and heap displays. Make it the largest power of two that fits on the display.
713 int available_space_for_data_rows = nedsim->dpy_height - (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + FOOTER_HEIGHT_IN_CELLS));
714 for (int i = 0; ; i++) {
715 if ((nedsim->cell_size * (1 << i)) > available_space_for_data_rows) {
716 nedsim->num_data_rows = (1 << --i);
717 break;
718 }
719 }
720 if (nedsim->num_data_rows < 4) {
721 nedsim->suitable_display = False;
722 return nedsim;
723 }
724 nedsim->origin_y_offset = (nedsim->dpy_height - (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + nedsim->num_data_rows + FOOTER_HEIGHT_IN_CELLS))) / 2; // center panel vertically
725
726 // Scale border relative to cell_size in a 1:10 relationship.
727 nedsim->border_size = nedsim->cell_size / 10;
728
729 draw_panel(nedsim);
730 draw_logo(nedsim);
731 draw_halt(nedsim);
732 draw_pc(nedsim);
733 draw_sc(nedsim);
734 draw_psw(nedsim);
735 draw_stack(nedsim);
736 draw_heap(nedsim);
737
738 nedsim->suitable_display = True;
739
740 return nedsim;
741}
742
743static unsigned long
744NEDsim_draw(Display * dpy, Window win, void * closure)
745{
746 struct NEDsim * nedsim = closure;
747
748 if (nedsim->suitable_display) {
749 nedsim->nedstate = run_simulator(nedsim->nedstate);
750 update_display(nedsim);
751 } else {
752 set_color(nedsim, &color_list[nedsim->color_index].error_on);
839b757b 753 XFillRectangle(nedsim->dpy, nedsim->panel, nedsim->gc, 0, 0, nedsim->dpy_width, nedsim->dpy_height);
84b74595
AT
754 }
755
839b757b
AT
756 XCopyArea(nedsim->dpy, nedsim->panel, nedsim->win, nedsim->gc, 0, 0, nedsim->dpy_width, nedsim->dpy_height, 0, 0);
757
84b74595
AT
758 return nedsim->delay;
759}
760
761static void
762NEDsim_reshape(Display * dpy, Window win, void * closure, unsigned int w, unsigned int h)
763{
764 struct NEDsim * nedsim = closure;
765 XWindowAttributes xgwa;
766 XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa);
767 /* Only restart the simulation if the window changed size. */
768 if (nedsim->dpy_width != xgwa.width || nedsim->dpy_height != xgwa.height) {
503470d7
AT
769 struct NEDstate * original_nedstate = nedsim->nedstate;
770 nedsim->nedstate = NULL;
771 NEDsim_free(dpy, win, nedsim);
772 struct NEDsim * new_nedsim = NEDsim_init(dpy, win);
773 new_nedsim->nedstate = original_nedstate;
774 closure = new_nedsim;
84b74595
AT
775 }
776}
777
778static const char * NEDsim_defaults[] = {
779 "*delay: 250",
780 0
781};
782
783static XrmOptionDescRec NEDsim_options[] = {
784 { "-delay", ".delay", XrmoptionSepArg, 0 },
5923644e 785 { "-binary", ".binary", XrmoptionSepArg, 0 },
84b74595
AT
786 { 0, 0, 0, 0 }
787};
788
789XSCREENSAVER_MODULE ("Blinken-lights simulator for NED1 CPU architecture.", NEDsim)