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