Commit | Line | Data |
---|---|---|
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 | ||
26 | struct 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 | ||
56 | struct color_rgb { | |
57 | // TODO: Explain why this is an unsigned short. Copy from WolframAutomata. | |
58 | unsigned short red, green, blue; | |
59 | }; | |
60 | ||
61 | struct 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 | ||
96 | static 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 | |
120 | static void | |
121 | set_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. | |
139 | static void | |
140 | set_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. | |
194 | static void | |
195 | get_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. | |
208 | static void | |
209 | draw_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. | |
239 | static void | |
240 | draw_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 | |
267 | static void | |
268 | draw_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 | |
343 | static void | |
344 | draw_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 | |
378 | static void | |
379 | draw_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 | |
404 | static void | |
405 | draw_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 | |
422 | static void | |
423 | draw_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 | |
442 | static void | |
443 | draw_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 | |
468 | static void | |
469 | draw_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 | |
494 | static void | |
495 | draw_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 | |
534 | static void | |
535 | draw_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 | |
560 | static void | |
561 | draw_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 | |
597 | static void | |
598 | update_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 | ||
671 | static Bool | |
672 | NEDsim_event(Display * dpy, Window win, void * closure, XEvent * event) | |
673 | { | |
674 | return False; | |
675 | } | |
676 | ||
677 | static void | |
678 | NEDsim_free(Display * dpy, Window win, void * closure) | |
679 | { | |
680 | // TODO: Replace all this with proper code to free everything. | |
681 | struct NEDsim * nedsim = closure; | |
682 | XFreeGC(nedsim->dpy, nedsim->gc); | |
683 | free(nedsim); | |
684 | } | |
685 | ||
686 | static void * | |
687 | NEDsim_init(Display * dpy, Window win) | |
688 | { | |
689 | struct NEDsim * nedsim; | |
690 | XGCValues gcv; | |
691 | XWindowAttributes xgwa; | |
692 | ||
693 | nedsim = calloc(1, sizeof(*nedsim)); | |
694 | if (!nedsim) { | |
695 | fprintf(stderr, "ERROR: Failed to calloc() for NEDsim struct in NEDsim_init().\n"); | |
696 | exit(EXIT_FAILURE); | |
697 | } | |
698 | ||
699 | nedsim->dpy = dpy; | |
700 | nedsim->win = win; | |
701 | ||
702 | XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa); | |
703 | nedsim->dpy_width = xgwa.width; | |
704 | nedsim->dpy_height = xgwa.height; | |
705 | ||
706 | // TODO: Explain that this is the delay between each clock cycle of the simulated NED CPU. | |
707 | nedsim->delay = get_integer_resource(nedsim->dpy, "delay", "Integer"); | |
708 | nedsim->delay *= 1000; /* Turn milliseconds into microseconds. */ | |
709 | ||
710 | // TODO: Read in the a.out file. This should be done in the simulator's init function call? | |
711 | nedsim->nedstate = init_simulator(); | |
712 | ||
713 | nedsim->gc = XCreateGC(nedsim->dpy, nedsim->win, GCForeground, &gcv); | |
714 | ||
715 | // TODO: Do this properly. | |
716 | nedsim->color_index = 0; | |
717 | ||
718 | // 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. | |
719 | ||
720 | 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. | |
721 | // 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. | |
722 | // For now, we'll just make it 10 pixels? | |
723 | if (nedsim->cell_size < 10) { | |
724 | nedsim->suitable_display = False; | |
725 | return nedsim; | |
726 | } | |
727 | nedsim->origin_x_offset = (nedsim->dpy_width - (nedsim->cell_size * OVERALL_WIDTH_IN_CELLS)) / 2; // center panel horizontally | |
728 | ||
729 | // Determine how many rows for the stack and heap displays. Make it the largest power of two that fits on the display. | |
730 | int available_space_for_data_rows = nedsim->dpy_height - (nedsim->cell_size * (HEADER_HEIGHT_IN_CELLS + FOOTER_HEIGHT_IN_CELLS)); | |
731 | for (int i = 0; ; i++) { | |
732 | if ((nedsim->cell_size * (1 << i)) > available_space_for_data_rows) { | |
733 | nedsim->num_data_rows = (1 << --i); | |
734 | break; | |
735 | } | |
736 | } | |
737 | if (nedsim->num_data_rows < 4) { | |
738 | nedsim->suitable_display = False; | |
739 | return nedsim; | |
740 | } | |
741 | 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 | |
742 | ||
743 | // Scale border relative to cell_size in a 1:10 relationship. | |
744 | nedsim->border_size = nedsim->cell_size / 10; | |
745 | ||
746 | draw_panel(nedsim); | |
747 | draw_logo(nedsim); | |
748 | draw_halt(nedsim); | |
749 | draw_pc(nedsim); | |
750 | draw_sc(nedsim); | |
751 | draw_psw(nedsim); | |
752 | draw_stack(nedsim); | |
753 | draw_heap(nedsim); | |
754 | ||
755 | nedsim->suitable_display = True; | |
756 | ||
757 | return nedsim; | |
758 | } | |
759 | ||
760 | static unsigned long | |
761 | NEDsim_draw(Display * dpy, Window win, void * closure) | |
762 | { | |
763 | struct NEDsim * nedsim = closure; | |
764 | ||
765 | if (nedsim->suitable_display) { | |
766 | nedsim->nedstate = run_simulator(nedsim->nedstate); | |
767 | update_display(nedsim); | |
768 | } else { | |
769 | set_color(nedsim, &color_list[nedsim->color_index].error_on); | |
770 | XFillRectangle(nedsim->dpy, nedsim->win, nedsim->gc, 0, 0, nedsim->dpy_width, nedsim->dpy_height); | |
771 | } | |
772 | ||
773 | return nedsim->delay; | |
774 | } | |
775 | ||
776 | static void | |
777 | NEDsim_reshape(Display * dpy, Window win, void * closure, unsigned int w, unsigned int h) | |
778 | { | |
779 | struct NEDsim * nedsim = closure; | |
780 | XWindowAttributes xgwa; | |
781 | XGetWindowAttributes(nedsim->dpy, nedsim->win, &xgwa); | |
782 | /* Only restart the simulation if the window changed size. */ | |
783 | if (nedsim->dpy_width != xgwa.width || nedsim->dpy_height != xgwa.height) { | |
784 | NEDsim_free(dpy, win, closure); | |
785 | closure = NEDsim_init(dpy, win); | |
786 | } | |
787 | } | |
788 | ||
789 | static const char * NEDsim_defaults[] = { | |
790 | "*delay: 250", | |
791 | 0 | |
792 | }; | |
793 | ||
794 | static XrmOptionDescRec NEDsim_options[] = { | |
795 | { "-delay", ".delay", XrmoptionSepArg, 0 }, | |
796 | { 0, 0, 0, 0 } | |
797 | }; | |
798 | ||
799 | XSCREENSAVER_MODULE ("Blinken-lights simulator for NED1 CPU architecture.", NEDsim) |