/* (c) 2021 Aaron Taylor <ataylor at subgeniuskitty dot com> */
/* See LICENSE.txt file for copyright and license details. */
// - Write a brief description of the machine being simulated. Only one thread, reduced RAM, no meaningful console when running as screensaver, etc.
// Ideas for sample programs to include:
// - Build list of integers on the stack (DUP, IM_1, ADD).
// - Calculate prime numbers, placing them on the stack.
// - Lights sliding back and forth, like PDP-11 front panel with some OSes.
/* Keep this source code C89 compliant per XScreensaver's instructions. */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* Various X resources */
int dpy_width
, dpy_height
;
// TODO: Explain that this is created during init, then lights are populated/overwritten on each frame.
/* Delay (in microseconds) between clock cycles in the NED CPU. */
int origin_x_offset
, origin_y_offset
;
// TODO: Explain that this contains all the actual state of the NED machine being simulated.
struct NEDstate
* nedstate
;
// TODO: Explain why this is an unsigned short. Copy from WolframAutomata.
unsigned short red
, green
, blue
;
// TODO: Explain all this.
//static struct color_scheme color_list[] = {
// // TODO: Explain all this.
// // TODO: Add other color schemes, like purple PDP-11/70.
// // TODO: http://www.chdickman.com/pdp8/DECcolors/
// {63479,63479,63479}, // 092-XXXX-123
// { 5140, 2056, 5654}, // 092-XXXX-152
// {40092,11051,15677}, // 092-XXXX-139
// {30326,13107,12850}, // 092-XXXX-154
// {65535,13107,12850}, // homemade
// {30326,13107,12850}, // 092-XXXX-154
// { 4112,12850,20046}, // 092-XXXX-145
// {12336,29555,37008}, // 092-XXXX-151
// {30326,24158, 6425}, // 092-XXXX-157
// {63479,63479,63479}, // 092-XXXX-123
// {63479,63479,63479} // 092-XXXX-123
static struct color_scheme color_list
[] = {
// TODO: Explain all this.
// TODO: Add other color schemes, like purple PDP-11/70.
// TODO: http://www.chdickman.com/pdp8/DECcolors/
{63479,63479,63479}, // 092-XXXX-123
{ 5140, 2056, 5654}, // 092-XXXX-152
{65535,11051,15677}, // 092-XXXX-139 - edit
{20000,13107,12850}, // 092-XXXX-154 - edit
{65535,13107,12850}, // homemade
{20000,13107,12850}, // 092-XXXX-154 - edit
{ 4112,12850,20046}, // 092-XXXX-145
{12336,29555,37008}, // 092-XXXX-151
{30326,24158, 6425}, // 092-XXXX-157
{63479,63479,63479}, // 092-XXXX-123
{63479,63479,63479} // 092-XXXX-123
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
set_color(struct NEDsim
* nedsim
, struct color_rgb
* color
)
XGetWindowAttributes(nedsim
->dpy
, nedsim
->win
, &xgwa
);
temp
.green
= color
->green
;
XAllocColor(nedsim
->dpy
, xgwa
.colormap
, &temp
);
XSetForeground(nedsim
->dpy
, nedsim
->gc
, temp
.pixel
);
// TODO: Make this a lot faster.
// Input: size in 'cells', and sets font to fill that size, minus border and padding room.
set_font_size(struct NEDsim
* nedsim
, int size
)
// vvv--- for border ---vvv vvv--- for padding ---vvv
int desired_height_in_pixels
= (size
* nedsim
->cell_size
) - (2 * nedsim
->border_size
) - (8 * nedsim
->border_size
);
const char * font_size_prefix
= "-*-helvetica-*-r-*-*-";
const char * font_size_suffix
= "-*-*-*-*-*-*-*";
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.
char * font_full_name
= malloc(buffer_size
);
int font_size_in_points
= 2; // Start with a 2 pt font and work our way up.
snprintf(font_full_name
, buffer_size
, "%s%d%s", font_size_prefix
, font_size_in_points
, font_size_suffix
);
XFontStruct
* font
= XLoadQueryFont(nedsim
->dpy
, font_full_name
);
printf("WARNING: Unable to load font %s. Probably gonna look wonky.\n", font_full_name
);
font
= XLoadQueryFont(nedsim
->dpy
, "fixed");
XSetFont(nedsim
->dpy
, nedsim
->gc
, font
->fid
);
int direction
, ascent
, descent
;
XTextExtents(font
, "X", 1, &direction
, &ascent
, &descent
, &overall
);
int height
= overall
.ascent
- overall
.descent
;
if (height
== desired_height_in_pixels
) {
} else if (height
> desired_height_in_pixels
) {
snprintf(font_full_name
, buffer_size
, "%s%d%s", font_size_prefix
, font_size_in_points
, font_size_suffix
);
XFontStruct
* font
= XLoadQueryFont(nedsim
->dpy
, font_full_name
);
printf("WARNING: Unable to load font %s. Probably gonna look wonky.\n", font_full_name
);
font
= XLoadQueryFont(nedsim
->dpy
, "fixed");
XSetFont(nedsim
->dpy
, nedsim
->gc
, font
->fid
);
free(nedsim
->current_font
);
nedsim
->current_font
= font_full_name
;
// TODO: Explain that this returns result in pixels so we can track fractional cell usage.
get_text_size(struct NEDsim
* nedsim
, const char * text
, int * x_size
, int * y_size
)
int direction
, ascent
, descent
;
XFontStruct
* font
= XLoadQueryFont(nedsim
->dpy
, nedsim
->current_font
);
XTextExtents(font
, text
, strlen(text
), &direction
, &ascent
, &descent
, &overall
);
*y_size
= overall
.ascent
- overall
.descent
;
// TODO: Note that this might leave the foreground color changed.
// Argument coordinates are in 'cells', not pixels.
draw_rect_area(struct NEDsim
* nedsim
, size_t x_origin
, size_t y_origin
, size_t x_size
, size_t y_size
,
Bool bord_top
, Bool bord_bottom
, Bool bord_left
, Bool bord_right
)
x_origin
*= nedsim
->cell_size
;
x_origin
+= nedsim
->origin_x_offset
;
y_origin
*= nedsim
->cell_size
;
y_origin
+= nedsim
->origin_y_offset
;
x_size
*= nedsim
->cell_size
;
y_size
*= nedsim
->cell_size
;
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, x_origin
, y_origin
, x_size
, y_size
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].border
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, x_origin
, y_origin
, x_size
, nedsim
->border_size
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, x_origin
, (y_origin
+ y_size
- nedsim
->border_size
), x_size
, nedsim
->border_size
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, x_origin
, y_origin
, nedsim
->border_size
, y_size
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (x_origin
+ x_size
- nedsim
->border_size
), y_origin
, nedsim
->border_size
, y_size
);
// Arguments are in units of 'cells', not pixels.
// Will leave foreground color changed.
// Draws filled circle with upper left corner at x,y.
draw_circular_area(struct NEDsim
* nedsim
, size_t x
, size_t y
, double diameter
)
// First, convert the function argument units from 'cells' to 'pixels'
diameter
*= nedsim
->cell_size
;
// Add the panel's absolute x,y offset to the requested coordinates.
x
+= nedsim
->origin_x_offset
;
y
+= nedsim
->origin_y_offset
;
// Shrink the circle to be a bit smaller than the bounding box allows. Note
// that the three adjustment values must sum to 1.0.
// For example, 0.7 + 0.15 + 0.15 = 1.0.
// Because we only draw the bottom border on repeated rows (e.g. draw_wordline()),
// we need to offset vertically by half a border height.
y
-= (0.5 * nedsim
->border_size
);
// Start angle 0 and ending angle 360*64 is one full circle in Xlib units.
XFillArc(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, x
, y
, diameter
, diameter
, 0, 360*64);
draw_panel(struct NEDsim
* nedsim
)
// TODO: Collect all relevant #defines somewhere.
#define OVERALL_WIDTH_IN_CELLS 70
#define HEADER_HEIGHT_IN_CELLS 14
#define FOOTER_HEIGHT_IN_CELLS 2
// Draw background color over entire window.
set_color(nedsim
, &color_list
[nedsim
->color_index
].panel_bg
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, 0, 0, nedsim
->dpy_width
, nedsim
->dpy_height
);
// Draw NED panel in foreground color.
set_color(nedsim
, &color_list
[nedsim
->color_index
].panel_fg
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->cell_size
* OVERALL_WIDTH_IN_CELLS
,
nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ nedsim
->num_data_rows
+ FOOTER_HEIGHT_IN_CELLS
)
// Give the panel rounded corners by first deleting the four right-angle corners...
set_color(nedsim
, &color_list
[nedsim
->color_index
].panel_bg
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->origin_x_offset
+ (nedsim
->cell_size
* (OVERALL_WIDTH_IN_CELLS
-1)),
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->origin_y_offset
+ (nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ nedsim
->num_data_rows
+ FOOTER_HEIGHT_IN_CELLS
- 1)),
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->origin_x_offset
+ (nedsim
->cell_size
* (OVERALL_WIDTH_IN_CELLS
-1)),
nedsim
->origin_y_offset
+ (nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ nedsim
->num_data_rows
+ FOOTER_HEIGHT_IN_CELLS
- 1)),
// ...and then replacing them with filled arcs, forming rounded corners.
set_color(nedsim
, &color_list
[nedsim
->color_index
].panel_fg
);
XFillArc(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->cell_size
* 2, nedsim
->cell_size
* 2,
XFillArc(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->origin_x_offset
+ (nedsim
->cell_size
* (OVERALL_WIDTH_IN_CELLS
-2)),
nedsim
->cell_size
* 2, nedsim
->cell_size
* 2,
XFillArc(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->origin_y_offset
+ (nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ nedsim
->num_data_rows
+ FOOTER_HEIGHT_IN_CELLS
- 2)),
nedsim
->cell_size
* 2, nedsim
->cell_size
* 2,
XFillArc(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
,
nedsim
->origin_x_offset
+ (nedsim
->cell_size
* (OVERALL_WIDTH_IN_CELLS
-2)),
nedsim
->origin_y_offset
+ (nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ nedsim
->num_data_rows
+ FOOTER_HEIGHT_IN_CELLS
- 2)),
nedsim
->cell_size
* 2, nedsim
->cell_size
* 2,
draw_logo(struct NEDsim
* nedsim
)
#define LOGO_NAME_HEIGHT 6
#define LOGO_WEBSITE_HEIGHT 2
// First draw the two colored boxes that comprise the logo area.
set_color(nedsim
, &color_list
[nedsim
->color_index
].primary
);
draw_rect_area(nedsim
, LOGO_X_OFFSET
, LOGO_Y_OFFSET
, LOGO_WIDTH
, LOGO_NAME_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
draw_rect_area(nedsim
, LOGO_X_OFFSET
, LOGO_Y_OFFSET
+LOGO_NAME_HEIGHT
, LOGO_WIDTH
, LOGO_WEBSITE_HEIGHT
, False
, True
, False
, False
);
// Now draw the 'NED' text in the top box.
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
set_font_size(nedsim
, LOGO_NAME_HEIGHT
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, "NED", &text_x_size
, &text_y_size
);
int local_x_offset
= ((LOGO_WIDTH
* nedsim
->cell_size
) - text_x_size
) / 2;
int local_y_offset
= ((LOGO_NAME_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (LOGO_X_OFFSET
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((LOGO_Y_OFFSET
+LOGO_NAME_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "NED", 3);
// And draw the 'subgeniuskitty.com' text in the bottom box.
set_font_size(nedsim
, LOGO_WEBSITE_HEIGHT
);
get_text_size(nedsim
, "subgeniuskitty.com", &text_x_size
, &text_y_size
);
local_x_offset
= ((LOGO_WIDTH
* nedsim
->cell_size
) - text_x_size
) / 2;
local_y_offset
= ((LOGO_WEBSITE_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (LOGO_X_OFFSET
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((LOGO_Y_OFFSET
+LOGO_NAME_HEIGHT
+LOGO_WEBSITE_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "subgeniuskitty.com", 18);
draw_halt(struct NEDsim
* nedsim
)
#define HALT_LIGHT_HEIGHT 6
#define HALT_LABEL_HEIGHT 2
// First draw the two colored boxes that comprise the halt area.
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
draw_rect_area(nedsim
, HALT_X_OFFSET
, HALT_Y_OFFSET
, HALT_WIDTH
, HALT_LIGHT_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].secondary
);
draw_rect_area(nedsim
, HALT_X_OFFSET
, HALT_Y_OFFSET
+HALT_LIGHT_HEIGHT
, HALT_WIDTH
, HALT_LABEL_HEIGHT
, False
, True
, False
, False
);
// And finally, draw the label.
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, "HALT", &text_x_size
, &text_y_size
);
int local_x_offset
= ((HALT_WIDTH
* nedsim
->cell_size
) - text_x_size
) / 2;
int local_y_offset
= ((HALT_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (HALT_X_OFFSET
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((HALT_Y_OFFSET
+HALT_LIGHT_HEIGHT
+HALT_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "HALT", 4);
draw_wordline_lights(struct NEDsim
* nedsim
, uint32_t word
, int x
, int y
)
#define WORDLINE_BITS_PER_STRIPE 4
#define WORDLINE_WIDTH 32
#define WORDLINE_HEIGHT 1
for (int i
= 0; i
< WORDLINE_WIDTH
; i
++) {
if (word
& (1<<(WORDLINE_WIDTH
-1-i
))) {
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_on
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_off
);
draw_circular_area(nedsim
, x
+i
, y
, WORDLINE_HEIGHT
);
draw_wordline(struct NEDsim
* nedsim
, int x
, int y
)
// First, draw a solid box in the primary color over the entire wordline area.
set_color(nedsim
, &color_list
[nedsim
->color_index
].primary
);
draw_rect_area(nedsim
, x
, y
, WORDLINE_WIDTH
, WORDLINE_HEIGHT
, False
, True
, False
, False
);
// Now, draw stripes in the secondary color.
for (i
= 0; i
< (WORDLINE_WIDTH
/(2*WORDLINE_BITS_PER_STRIPE
)); i
++) {
set_color(nedsim
, &color_list
[nedsim
->color_index
].secondary
);
draw_rect_area(nedsim
, (x
+(i
*(WORDLINE_WIDTH
/WORDLINE_BITS_PER_STRIPE
))), y
,
WORDLINE_BITS_PER_STRIPE
, WORDLINE_HEIGHT
, False
, True
, False
, False
);
// Finally, draw the lights.
draw_wordline_lights(nedsim
, 0, x
, y
);
draw_pc(struct NEDsim
* nedsim
)
// TODO: Note that all #defines use units of 'cells', not 'pixels', etc.
#define PC_LABEL_HEIGHT 2
#define PC_LIGHT_HEIGHT 1
// First draw the two colored boxes that comprise the PC area.
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
draw_rect_area(nedsim
, PC_X_OFFSET
, PC_Y_OFFSET
, PC_WIDTH
, PC_LABEL_HEIGHT
, True
, True
, False
, False
);
draw_wordline(nedsim
, PC_X_OFFSET
, PC_Y_OFFSET
+PC_LABEL_HEIGHT
);
// Now draw the label text "PC".
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, "PC", &text_x_size
, &text_y_size
);
int local_x_offset
= ((PC_WIDTH
* nedsim
->cell_size
) - text_x_size
) / 2;
int local_y_offset
= ((PC_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (PC_X_OFFSET
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((PC_Y_OFFSET
+PC_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "PC", 2);
draw_sc(struct NEDsim
* nedsim
)
#define SC_LABEL_HEIGHT 2
#define SC_LIGHT_HEIGHT 1
// First draw the two colored boxes that comprise the SC area.
set_color(nedsim
, &color_list
[nedsim
->color_index
].secondary
);
draw_rect_area(nedsim
, SC_X_OFFSET
, SC_Y_OFFSET
, SC_WIDTH
, SC_LABEL_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
draw_rect_area(nedsim
, SC_X_OFFSET
, SC_Y_OFFSET
+SC_LABEL_HEIGHT
, SC_WIDTH
, SC_LIGHT_HEIGHT
, False
, True
, False
, False
);
// Now draw the label text "SC".
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, "SC", &text_x_size
, &text_y_size
);
int local_x_offset
= ((SC_WIDTH
* nedsim
->cell_size
) - text_x_size
) / 2;
int local_y_offset
= ((SC_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (SC_X_OFFSET
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((SC_Y_OFFSET
+SC_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "SC", 2);
draw_psw(struct NEDsim
* nedsim
)
#define PSW_N_X_OFFSET 51
#define PSW_Z_X_OFFSET 58
#define PSW_LABEL_WIDTH 3
#define PSW_LABEL_HEIGHT 2
#define PSW_LIGHT_WIDTH 1
#define PSW_LIGHT_HEIGHT 1
// First draw the four colored boxes that comprise the two PSW areas.
set_color(nedsim
, &color_list
[nedsim
->color_index
].secondary
);
draw_rect_area(nedsim
, PSW_N_X_OFFSET
, PSW_Y_OFFSET
, PSW_LABEL_WIDTH
, PSW_LABEL_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].secondary
);
draw_rect_area(nedsim
, PSW_Z_X_OFFSET
, PSW_Y_OFFSET
, PSW_LABEL_WIDTH
, PSW_LABEL_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
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
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
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
);
// Now draw the label text "N".
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, "N", &text_x_size
, &text_y_size
);
int local_x_offset
= ((PSW_LABEL_WIDTH
* nedsim
->cell_size
) - text_x_size
) / 2;
int local_y_offset
= ((PSW_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (PSW_N_X_OFFSET
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((PSW_Y_OFFSET
+PSW_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "N", 1);
// Now draw the label text "Z".
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
get_text_size(nedsim
, "Z", &text_x_size
, &text_y_size
);
local_x_offset
= ((PSW_LABEL_WIDTH
* nedsim
->cell_size
) - text_x_size
) / 2;
local_y_offset
= ((PSW_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, (PSW_Z_X_OFFSET
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((PSW_Y_OFFSET
+PSW_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "Z", 1);
draw_stack(struct NEDsim
* nedsim
)
#define STACK_Y_OFFSET 12
#define STACK_LABEL_HEIGHT 2
#define STACK_LIGHT_HEIGHT 1
// First draw the two colored boxes that comprise the stack area.
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
draw_rect_area(nedsim
, STACK_X_OFFSET
, STACK_Y_OFFSET
, STACK_WIDTH
, STACK_LABEL_HEIGHT
, True
, True
, False
, False
);
for (int i
= 0; i
< nedsim
->num_data_rows
; i
++) {
draw_wordline(nedsim
, STACK_X_OFFSET
, STACK_Y_OFFSET
+STACK_LABEL_HEIGHT
+i
);
// Now draw the label text "Stack Size:".
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, "Stack Size:", &text_x_size
, &text_y_size
);
int local_y_offset
= ((STACK_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, ((STACK_X_OFFSET
+ 1) * nedsim
->cell_size
+ nedsim
->origin_x_offset
),
((STACK_Y_OFFSET
+STACK_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "Stack Size:", 11);
draw_heap(struct NEDsim
* nedsim
)
#define HEAP_LABEL_HEIGHT 2
#define HEAP_LIGHT_HEIGHT 1
// 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.
#define HEAP_START_ADDRESS 0x20000000
// First draw the two colored boxes that comprise the heap area.
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
draw_rect_area(nedsim
, HEAP_X_OFFSET
, HEAP_Y_OFFSET
, HEAP_WIDTH
, HEAP_LABEL_HEIGHT
, True
, True
, False
, False
);
for (int i
= 0; i
< nedsim
->num_data_rows
; i
++) {
draw_wordline(nedsim
, HEAP_X_OFFSET
, HEAP_Y_OFFSET
+HEAP_LABEL_HEIGHT
+i
);
// Now draw the label text "RAM Base:".
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, "RAM Base:", &text_x_size
, &text_y_size
);
int local_y_offset
= ((HEAP_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, ((HEAP_X_OFFSET
+ 1) * nedsim
->cell_size
+ nedsim
->origin_x_offset
),
((HEAP_Y_OFFSET
+HEAP_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), "RAM Base:", 9);
// Now draw the address text.
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
snprintf(address
, sizeof(address
), "0x%08X", HEAP_START_ADDRESS
);
get_text_size(nedsim
, address
, &text_x_size
, &text_y_size
);
local_y_offset
= ((HEAP_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, ((HEAP_X_OFFSET
+ 1 + (HEAP_WIDTH
/ 2)) * nedsim
->cell_size
+ nedsim
->origin_x_offset
),
((HEAP_Y_OFFSET
+HEAP_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), address
, strlen(address
));
update_display(struct NEDsim
* nedsim
)
// Draw the halt indicator.
if (nedsim
->nedstate
->halted
) {
set_color(nedsim
, &color_list
[nedsim
->color_index
].error_on
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].error_off
);
draw_circular_area(nedsim
, HALT_X_OFFSET
, HALT_Y_OFFSET
, HALT_WIDTH
);
// Draw the PSW "N" light.
if (nedsim
->nedstate
->active_thread
->psw
->negative
) {
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_on
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_off
);
draw_circular_area(nedsim
, PSW_N_X_OFFSET
+1, PSW_Y_OFFSET
+PSW_LABEL_HEIGHT
, PSW_LIGHT_HEIGHT
);
// Draw the PSW "Z" light.
if (nedsim
->nedstate
->active_thread
->psw
->zero
) {
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_on
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_off
);
draw_circular_area(nedsim
, PSW_Z_X_OFFSET
+1, PSW_Y_OFFSET
+PSW_LABEL_HEIGHT
, PSW_LIGHT_HEIGHT
);
for (i
= 0; i
< SC_WIDTH
; i
++) {
if ((SC_WIDTH
-1-i
) == nedsim
->nedstate
->active_thread
->sc
) {
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_on
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].light_off
);
draw_circular_area(nedsim
, SC_X_OFFSET
+i
, SC_Y_OFFSET
+SC_LABEL_HEIGHT
, SC_LIGHT_HEIGHT
);
draw_wordline_lights(nedsim
, nedsim
->nedstate
->active_thread
->pc
, PC_X_OFFSET
, PC_Y_OFFSET
+PC_LABEL_HEIGHT
);
// Draw the stack lights.
int64_t top_of_stack
= ((int64_t)nedsim
->nedstate
->active_thread
->sp
) - 1;
for (i
= 0; i
< nedsim
->num_data_rows
; i
++) {
if ((top_of_stack
-i
) >= 0) {
draw_wordline_lights(nedsim
, nedsim
->nedstate
->active_thread
->stack
[top_of_stack
-i
], STACK_X_OFFSET
, STACK_Y_OFFSET
+STACK_LABEL_HEIGHT
+i
);
draw_wordline_lights(nedsim
, 0, STACK_X_OFFSET
, STACK_Y_OFFSET
+STACK_LABEL_HEIGHT
+i
);
// Draw the stack size in text.
set_color(nedsim
, &color_list
[nedsim
->color_index
].tertiary
);
draw_rect_area(nedsim
, STACK_X_OFFSET
+(STACK_WIDTH
/2), STACK_Y_OFFSET
, STACK_WIDTH
/2, STACK_LABEL_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].text
);
snprintf(stack_size
, sizeof(stack_size
), "0x%08X", nedsim
->nedstate
->active_thread
->sp
);
int text_x_size
, text_y_size
;
get_text_size(nedsim
, stack_size
, &text_x_size
, &text_y_size
);
int local_y_offset
= ((STACK_LABEL_HEIGHT
* nedsim
->cell_size
) - text_y_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, ((STACK_X_OFFSET
+ 1 + (STACK_WIDTH
/ 2)) * nedsim
->cell_size
+ nedsim
->origin_x_offset
),
((STACK_Y_OFFSET
+STACK_LABEL_HEIGHT
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), stack_size
, strlen(stack_size
));
for (i
= 0; i
< nedsim
->num_data_rows
; i
++) {
draw_wordline_lights(nedsim
, ram_r_word(nedsim
->nedstate
, HEAP_START_ADDRESS
+(i
*BPW
)), HEAP_X_OFFSET
, HEAP_Y_OFFSET
+HEAP_LABEL_HEIGHT
+i
);
/* -------------------------------------------------------------------------- */
/* Screenhack API Functions */
/* -------------------------------------------------------------------------- */
NEDsim_event(Display
* dpy
, Window win
, void * closure
, XEvent
* event
)
NEDsim_free(Display
* dpy
, Window win
, void * closure
)
// TODO: Replace all this with proper code to free everything.
struct NEDsim
* nedsim
= closure
;
XFreeGC(nedsim
->dpy
, nedsim
->gc
);
NEDsim_init(Display
* dpy
, Window win
)
nedsim
= calloc(1, sizeof(*nedsim
));
fprintf(stderr
, "ERROR: Failed to calloc() for NEDsim struct in NEDsim_init().\n");
XGetWindowAttributes(nedsim
->dpy
, nedsim
->win
, &xgwa
);
nedsim
->dpy_width
= xgwa
.width
;
nedsim
->dpy_height
= xgwa
.height
;
// TODO: Explain that this is the delay between each clock cycle of the simulated NED CPU.
nedsim
->delay
= get_integer_resource(nedsim
->dpy
, "delay", "Integer");
nedsim
->delay
*= 1000; /* Turn milliseconds into microseconds. */
// TODO: Read in the a.out file. This should be done in the simulator's init function call?
nedsim
->nedstate
= init_simulator();
nedsim
->gc
= XCreateGC(nedsim
->dpy
, nedsim
->win
, GCForeground
, &gcv
);
// TODO: Do this properly.
// 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.
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.
// 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.
// For now, we'll just make it 10 pixels?
if (nedsim
->cell_size
< 10) {
nedsim
->suitable_display
= False
;
nedsim
->origin_x_offset
= (nedsim
->dpy_width
- (nedsim
->cell_size
* OVERALL_WIDTH_IN_CELLS
)) / 2; // center panel horizontally
// Determine how many rows for the stack and heap displays. Make it the largest power of two that fits on the display.
int available_space_for_data_rows
= nedsim
->dpy_height
- (nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ FOOTER_HEIGHT_IN_CELLS
));
if ((nedsim
->cell_size
* (1 << i
)) > available_space_for_data_rows
) {
nedsim
->num_data_rows
= (1 << --i
);
if (nedsim
->num_data_rows
< 4) {
nedsim
->suitable_display
= False
;
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
// Scale border relative to cell_size in a 1:10 relationship.
nedsim
->border_size
= nedsim
->cell_size
/ 10;
nedsim
->suitable_display
= True
;
NEDsim_draw(Display
* dpy
, Window win
, void * closure
)
struct NEDsim
* nedsim
= closure
;
if (nedsim
->suitable_display
) {
nedsim
->nedstate
= run_simulator(nedsim
->nedstate
);
set_color(nedsim
, &color_list
[nedsim
->color_index
].error_on
);
XFillRectangle(nedsim
->dpy
, nedsim
->win
, nedsim
->gc
, 0, 0, nedsim
->dpy_width
, nedsim
->dpy_height
);
NEDsim_reshape(Display
* dpy
, Window win
, void * closure
, unsigned int w
, unsigned int h
)
struct NEDsim
* nedsim
= closure
;
XGetWindowAttributes(nedsim
->dpy
, nedsim
->win
, &xgwa
);
/* Only restart the simulation if the window changed size. */
if (nedsim
->dpy_width
!= xgwa
.width
|| nedsim
->dpy_height
!= xgwa
.height
) {
struct NEDstate
* original_nedstate
= nedsim
->nedstate
;
NEDsim_free(dpy
, win
, nedsim
);
struct NEDsim
* new_nedsim
= NEDsim_init(dpy
, win
);
new_nedsim
->nedstate
= original_nedstate
;
static const char * NEDsim_defaults
[] = {
static XrmOptionDescRec NEDsim_options
[] = {
{ "-delay", ".delay", XrmoptionSepArg
, 0 },
XSCREENSAVER_MODULE ("Blinken-lights simulator for NED1 CPU architecture.", NEDsim
)