/* (c) 2021 Aaron Taylor <ataylor at subgeniuskitty dot com> */
/* See LICENSE.txt file for copyright and license details. */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
// All of these values are in units of 'cells', not 'pixels'. Where applicable,
// coordinates refer to the upper-left corner of a cell. See the files
// `layout_reference.*` for details.
#define OVERALL_WIDTH_IN_CELLS 70
#define HEADER_HEIGHT_IN_CELLS 14
#define FOOTER_HEIGHT_IN_CELLS 2
#define LOGO_NAME_HEIGHT 6
#define LOGO_WEBSITE_HEIGHT 2
#define HALT_LIGHT_HEIGHT 6
#define HALT_LABEL_HEIGHT 2
#define WORDLINE_BITS_PER_STRIPE 4
#define WORDLINE_WIDTH 32
#define WORDLINE_HEIGHT 1
#define PC_LABEL_HEIGHT 2
#define PC_LIGHT_HEIGHT 1
#define SC_LABEL_HEIGHT 2
#define SC_LIGHT_HEIGHT 1
#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
#define STACK_Y_OFFSET 12
#define STACK_LABEL_HEIGHT 2
#define STACK_LIGHT_HEIGHT 1
#define HEAP_LABEL_HEIGHT 2
#define HEAP_LIGHT_HEIGHT 1
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
// Tracks the width and height (in pixels) of 'win' on 'dpy'.
int dpy_width
, dpy_height
;
// To ensure the NED display is always synchronized/valid, we
// double-buffer. This is that buffer.
// The front panel is defined in a fixed grid of 'cells' which are then
// scaled and projected onto the actual screen. This variable tracks the
// size of one 'cell' in screen pixels.
// Like above, this variable tracks the scaled size (in pixels) of cell
// borders when projecting them on the screen.
// Since the panel will be smaller than the display window, these two
// variable track the location (in pixels) of the upper left corner of the
// NED panel (pretending it didn't have rounded corners) relative to the
// display window's origin.
int origin_x_offset
, origin_y_offset
;
// Delay (in microseconds) between clock cycles in the NED CPU.
// Since the heap and stack areas are resized to fit the display, this
// variable tracks their height.
// This is an index into the color_list[] array, selecting a color scheme.
// If the display is unsuitable (e.g. too small), this boolean is 'true'.
// With this, we blank the display but keep the simulation itself running,
// allowing the ongoing simulation to display when the situation is
// corrected, and allowing us to avoid boring the user with unnecessary
// The font size is dynamically adjusted based on display size. This
// variable stores the full font description, including the correct font
// size, all in the X11 font specifier format. For example,
// -*-helvetica-*-r-*-*-72-*-*-*-*-*-*-*
// if the display were using a 72 point helvetica font without emphasis.
// This struct contains all machine state for the simulated NED computer.
struct NEDstate
* nedstate
;
// The type 'unsigned short' comes from the XColor struct definition.
// The red, green, and blue values are always in the range 0 to 65535
// inclusive, independent of the number of bits actually used in the
// display hardware. The server scales these values to the range used
// by the hardware. Black is represented by (0,0,0), and white is
// represented by (65535,65535,65535).
unsigned short red
, green
, blue
;
// The following entries define a full color scheme for a NED panel.
panel_bg
, // Empty screen space not taken up by the panel
panel_fg
, // Panel body/faceplate
light_on
, // Illuminated data indicator
light_off
, // Non-illuminated data indicator
error_on
, // Illuminated error indicator
error_off
, // Non-illuminated error indicator
primary
, // Primary panel color (see: logo background)
secondary
, // Secondary panel color (see: "HALT" text background)
tertiary
, // Tertiary panel color (see: "subgeniuskitty.com" text background)
// All included color schemes for NEDsim are defined in this array. To select a
// particular color scheme at runtime, use the `-color N` CLI option.
static struct color_scheme color_list
[] = {
{ // This color scheme is inspired by the KI10 version of the PDP-10.
// Many RGB values came from: http://www.chdickman.com/pdp8/DECcolors/
{63479,63479,63479}, // 092-XXXX-123
{ 5140, 2056, 5654}, // 092-XXXX-152
{65535,11051,15677}, // homemade, derived from 092-XXXX-139
{20000,13107,12850}, // homemade, derived from 092-XXXX-154
{65535,13107,12850}, // homemade, derived from 092-XXXX-154
{20000,13107,12850}, // homemade, derived from 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
{ // This color scheme is (very loosely) inspired by the first version of the PDP-11/70.
// Many RGB values came from: http://www.chdickman.com/pdp8/DECcolors/
{63479,63479,63479}, // 092-XXXX-123
{ 5140, 2056, 5654}, // 092-XXXX-152
{51400,34952,26682}, // homemade, derived from 092-XXXX-136
{20000,13107,12850}, // homemade, derived from 092-XXXX-154
{65535,13107,12850}, // homemade, derived from 092-XXXX-154
{20000,13107,12850}, // homemade, derived from 092-XXXX-154
{28270, 8995,19532}, // 092-XXXX-140
{40092,11051,15677}, // 092-XXXX-139
{15000,21000, 4000}, // homemade, derived from 092-XXXX-129
{63479,63479,63479}, // 092-XXXX-123
{63479,63479,63479} // 092-XXXX-123
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
// Fill in boilerplate when selecting colors.
#define COLOR(X) &color_list[nedsim->color_index].X
// Set foreground color for the current graphics context.
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.
// Set font size to fill 'size' cells vertically, minus space for border and padding.
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
;
// Unlike most functions in this program that input/output in units of 'cells',
// this function uses units of 'pixels' so the caller can track fractional cell
// usage (e.g. for centering the text).
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
;
// When specifying the rectangular area to draw, all coordinates and sizes are
// in units of 'cells'. Also, be aware that this function alters the
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
)
// First fill in the rectangular area...
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
->panel
, nedsim
->gc
, x_origin
, y_origin
, x_size
, y_size
);
// ...then give it a border, if requested.
set_color(nedsim
, COLOR(border
));
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
,
x_origin
, y_origin
, x_size
, nedsim
->border_size
);
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
,
x_origin
, (y_origin
+ y_size
- nedsim
->border_size
), x_size
, nedsim
->border_size
);
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
,
x_origin
, y_origin
, nedsim
->border_size
, y_size
);
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
,
(x_origin
+ x_size
- nedsim
->border_size
), y_origin
, nedsim
->border_size
, y_size
);
// Draws filled circle in a square area with upper left corner at x,y. When
// specifying the location and size to draw, all values are in units of
// 'cells'. Also, be aware that this function alters the foreground color.
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
->panel
, nedsim
->gc
, x
, y
, diameter
, diameter
, 0, 360*64);
// Draws text in a square area with upper left corner at (x_origin,y_origin).
// Requires that set_font_size() has been run at least once. All values are in
draw_text(struct NEDsim
* nedsim
, const char * text
, int x_origin
, int y_origin
, int x_size
, int y_size
, Bool horizontally_center
)
set_color(nedsim
, COLOR(text
));
int text_x_size
, text_y_size
;
get_text_size(nedsim
, text
, &text_x_size
, &text_y_size
);
int local_y_offset
= ((y_size
* nedsim
->cell_size
) - text_y_size
) / 2;
if (horizontally_center
) local_x_offset
= ((x_size
* nedsim
->cell_size
) - text_x_size
) / 2;
XDrawString(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
, (x_origin
* nedsim
->cell_size
+ nedsim
->origin_x_offset
+ local_x_offset
),
((y_origin
+ y_size
) * nedsim
->cell_size
+ nedsim
->origin_y_offset
- local_y_offset
), text
, strlen(text
));
// Draws the panel itself. Not the lights/labels/etc, but the flat sheet of
// metal that is the front panel.
draw_panel(struct NEDsim
* nedsim
)
// Draw background color over entire window.
set_color(nedsim
, COLOR(panel_bg
));
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
, 0, 0, nedsim
->dpy_width
, nedsim
->dpy_height
);
// Draw NED panel in foreground color.
set_color(nedsim
, COLOR(panel_fg
));
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, 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(panel_bg
));
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
,
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
,
nedsim
->origin_x_offset
+ (nedsim
->cell_size
* (OVERALL_WIDTH_IN_CELLS
-1)),
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, 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
->panel
, 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(panel_fg
));
XFillArc(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
,
nedsim
->cell_size
* 2, nedsim
->cell_size
* 2,
XFillArc(nedsim
->dpy
, nedsim
->panel
, 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
->panel
, 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
->panel
, 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 the "NED" and "subgeniuskitty.com" logos on the front panel.
draw_logo(struct NEDsim
* nedsim
)
// First draw the two colored boxes that comprise the logo area.
set_color(nedsim
, COLOR(primary
));
draw_rect_area(nedsim
, LOGO_X_OFFSET
, LOGO_Y_OFFSET
, LOGO_WIDTH
, LOGO_NAME_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, COLOR(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_font_size(nedsim
, LOGO_NAME_HEIGHT
);
draw_text(nedsim
, "NED", LOGO_X_OFFSET
, LOGO_Y_OFFSET
, LOGO_WIDTH
, LOGO_NAME_HEIGHT
, True
);
// And draw the 'subgeniuskitty.com' text in the bottom box.
set_font_size(nedsim
, LOGO_WEBSITE_HEIGHT
);
draw_text(nedsim
, "subgeniuskitty.com", LOGO_X_OFFSET
, LOGO_Y_OFFSET
+LOGO_NAME_HEIGHT
, LOGO_WIDTH
, LOGO_WEBSITE_HEIGHT
, True
);
// Draw the HALT indicator area on the front panel.
draw_halt(struct NEDsim
* nedsim
)
// First draw the two colored boxes that comprise the halt area.
set_color(nedsim
, COLOR(tertiary
));
draw_rect_area(nedsim
, HALT_X_OFFSET
, HALT_Y_OFFSET
, HALT_WIDTH
, HALT_LIGHT_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, COLOR(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.
draw_text(nedsim
, "HALT", HALT_X_OFFSET
, HALT_Y_OFFSET
+HALT_LIGHT_HEIGHT
, HALT_WIDTH
, HALT_LABEL_HEIGHT
, True
);
// Draw the 32 lights corresponding to 'word' at coordinates ('x','y').
// Note that this function ONLY draws the lights and is used each frame for
// updating the panel. To draw the wordline area itself, use draw_wordline().
draw_wordline_lights(struct NEDsim
* nedsim
, uint32_t word
, int x
, int y
)
for (int i
= 0; i
< WORDLINE_WIDTH
; i
++) {
if (word
& (1<<(WORDLINE_WIDTH
-1-i
))) {
set_color(nedsim
, COLOR(light_on
));
set_color(nedsim
, COLOR(light_off
));
draw_circular_area(nedsim
, x
+i
, y
, WORDLINE_HEIGHT
);
// Draw a single 32-bit NED word line at coordinates ('x','y').
// Note that this draws a wordline with value 0. To update with a specific
// value, call draw_wordline_lights() after drawing the wordline area at least
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(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(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 the Program Counter area (but don't populate it yet).
draw_pc(struct NEDsim
* nedsim
)
// First draw the two colored boxes that comprise the PC area.
set_color(nedsim
, COLOR(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".
draw_text(nedsim
, "PC", PC_X_OFFSET
, PC_Y_OFFSET
, PC_WIDTH
, PC_LABEL_HEIGHT
, True
);
// Draw the Stack Counter area (but don't populate it yet).
draw_sc(struct NEDsim
* nedsim
)
// First draw the two colored boxes that comprise the SC area.
set_color(nedsim
, COLOR(secondary
));
draw_rect_area(nedsim
, SC_X_OFFSET
, SC_Y_OFFSET
, SC_WIDTH
, SC_LABEL_HEIGHT
, True
, True
, False
, False
);
set_color(nedsim
, COLOR(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".
draw_text(nedsim
, "SC", SC_X_OFFSET
, SC_Y_OFFSET
, SC_WIDTH
, SC_LABEL_HEIGHT
, True
);
// Draw areas for the two PSW flags, 'Z'ero and 'N'egative.
draw_psw(struct NEDsim
* nedsim
)
// First draw the four colored boxes that comprise the two PSW areas.
set_color(nedsim
, COLOR(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(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(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(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.
draw_text(nedsim
, "N", PSW_N_X_OFFSET
, PSW_Y_OFFSET
, PSW_LABEL_WIDTH
, PSW_LABEL_HEIGHT
, True
);
draw_text(nedsim
, "Z", PSW_Z_X_OFFSET
, PSW_Y_OFFSET
, PSW_LABEL_WIDTH
, PSW_LABEL_HEIGHT
, True
);
// Draw the stack area (but don't populate it yet).
draw_stack(struct NEDsim
* nedsim
)
// First draw the two colored boxes that comprise the stack area.
set_color(nedsim
, COLOR(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:".
draw_text(nedsim
, "Stack Size:", STACK_X_OFFSET
+1, STACK_Y_OFFSET
, STACK_WIDTH
, STACK_LABEL_HEIGHT
, False
);
// Draw the heap area (but don't populate it yet).
draw_heap(struct NEDsim
* nedsim
)
// 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(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 text label "RAM Base:".
draw_text(nedsim
, "RAM Base:", HEAP_X_OFFSET
+1, HEAP_Y_OFFSET
, HEAP_WIDTH
, HEAP_LABEL_HEIGHT
, False
);
// Now draw the address text.
snprintf(address
, sizeof(address
), "0x%08X", HEAP_START_ADDRESS
);
draw_text(nedsim
, address
, (HEAP_X_OFFSET
+(HEAP_WIDTH
/2)+1), HEAP_Y_OFFSET
, HEAP_WIDTH
, HEAP_LABEL_HEIGHT
, False
);
// After the static front panel has been drawn at least once, this function
// updates all the dynamic parts of the panel (lights + text values) and is
update_display(struct NEDsim
* nedsim
)
// Draw the halt indicator.
if (nedsim
->nedstate
->halted
) {
set_color(nedsim
, COLOR(error_on
));
set_color(nedsim
, COLOR(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(light_on
));
set_color(nedsim
, COLOR(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(light_on
));
set_color(nedsim
, COLOR(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(light_on
));
set_color(nedsim
, COLOR(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(tertiary
));
draw_rect_area(nedsim
, STACK_X_OFFSET
+(STACK_WIDTH
/2), STACK_Y_OFFSET
, STACK_WIDTH
/2, STACK_LABEL_HEIGHT
, True
, True
, False
, False
);
snprintf(stack_size
, sizeof(stack_size
), "0x%08X", nedsim
->nedstate
->active_thread
->sp
);
draw_text(nedsim
, stack_size
, (STACK_X_OFFSET
+(STACK_WIDTH
/2)+1), STACK_Y_OFFSET
, STACK_WIDTH
, STACK_LABEL_HEIGHT
, False
);
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
)
struct NEDsim
* nedsim
= closure
;
if (nedsim
->nedstate
!= NULL
) {
free(nedsim
->nedstate
->active_thread
->psw
);
free(nedsim
->nedstate
->active_thread
);
free(nedsim
->nedstate
->hack
);
// TODO: Replace all this with proper code to free everything related to the screensaver itself.
XFreeGC(nedsim
->dpy
, nedsim
->gc
);
XFreePixmap(nedsim
->dpy
, nedsim
->panel
);
NEDsim_init(Display
* dpy
, Window win
)
// =========================================================================
// =========================================================================
nedsim
= calloc(1, sizeof(*nedsim
));
fprintf(stderr
, "ERROR: Failed to calloc() for NEDsim struct in NEDsim_init().\n");
nedsim
->gc
= XCreateGC(nedsim
->dpy
, nedsim
->win
, GCForeground
, &gcv
);
XGetWindowAttributes(nedsim
->dpy
, nedsim
->win
, &xgwa
);
nedsim
->dpy_width
= xgwa
.width
;
nedsim
->dpy_height
= xgwa
.height
;
nedsim
->panel
= XCreatePixmap(nedsim
->dpy
, nedsim
->win
, nedsim
->dpy_width
, nedsim
->dpy_height
, xgwa
.depth
);
// =========================================================================
// =========================================================================
nedsim
->delay
= get_integer_resource(nedsim
->dpy
, "delay", "Integer");
nedsim
->delay
*= 1000; /* Turn milliseconds into microseconds. */
// If the user did not supply a program for execution, randomly select one
// of the included programs.
char * input_file
= get_string_resource(nedsim
->dpy
, "binary", "String");
if (input_file
== NULL
) {
// TODO: Need to include some default programs and randomly select one to load into RAM.
nedsim
->nedstate
= init_simulator("./test.out");
nedsim
->nedstate
= init_simulator(input_file
);
// If the user did not select a color scheme, select one randomly.
nedsim
->color_index
= get_integer_resource(nedsim
->dpy
, "color", "Integer");
if (nedsim
->color_index
== -1) {
nedsim
->color_index
= random() % sizeof(color_list
)/sizeof(color_list
[0]);
} else if (nedsim
->color_index
>= sizeof(color_list
)/sizeof(color_list
[0])) {
fprintf(stderr
, "WARNING: Color index out of range.\n");
// =========================================================================
// =========================================================================
// We desire to make the panel as wide as possible while also making the
// cell size an integer pixel size.
nedsim
->cell_size
= nedsim
->dpy_width
/ OVERALL_WIDTH_IN_CELLS
;
// Cell sizes below about 10 pixels result in unreadable fonts. For now,
// we'll use that as our cutoff. It also works well with a 1:10 border:cell
if (nedsim
->cell_size
< 10) {
nedsim
->suitable_display
= False
;
// Center the panel horizontally.
nedsim
->origin_x_offset
= (nedsim
->dpy_width
-
(nedsim
->cell_size
* OVERALL_WIDTH_IN_CELLS
)) / 2;
// The stack and heap displays are variable height. Size them to fit the
int available_space_for_data_rows
= nedsim
->dpy_height
-
(nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ FOOTER_HEIGHT_IN_CELLS
));
if ((nedsim
->cell_size
* i
) > available_space_for_data_rows
) {
nedsim
->num_data_rows
= --i
;
// Enforce a minimum vertical size, though '4' is arbitrary.
if (nedsim
->num_data_rows
< 4) {
nedsim
->suitable_display
= False
;
// Center the panel vertically.
nedsim
->origin_y_offset
= (nedsim
->dpy_height
-
(nedsim
->cell_size
* (HEADER_HEIGHT_IN_CELLS
+ nedsim
->num_data_rows
+ FOOTER_HEIGHT_IN_CELLS
))) / 2;
// 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
;
// Update the panel display buffer.
if (nedsim
->suitable_display
) {
nedsim
->nedstate
= run_simulator(nedsim
->nedstate
);
set_color(nedsim
, COLOR(error_on
));
XFillRectangle(nedsim
->dpy
, nedsim
->panel
, nedsim
->gc
, 0, 0, nedsim
->dpy_width
, nedsim
->dpy_height
);
// Copy panel buffer to display window.
XCopyArea(nedsim
->dpy
, nedsim
->panel
, nedsim
->win
, nedsim
->gc
, 0, 0, nedsim
->dpy_width
, nedsim
->dpy_height
, 0, 0);
NEDsim_reshape(Display
* dpy
, Window win
, void * closure
, unsigned int w
, unsigned int h
)
struct NEDsim
* nedsim
= closure
;
// Only re-initialize the display if it changed size.
XGetWindowAttributes(nedsim
->dpy
, nedsim
->win
, &xgwa
);
if (nedsim
->dpy_width
!= xgwa
.width
|| nedsim
->dpy_height
!= xgwa
.height
) {
// Although we are re-initializing the display, we wish to retain the
// in-progress NED simulation. Thus, we retain the NEDstate pointer.
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 },
{ "-binary", ".binary", XrmoptionSepArg
, 0 },
{ "-color", ".color", XrmoptionSepArg
, 0 },
XSCREENSAVER_MODULE ("Blinken-lights simulator for NED1 CPU architecture.", NEDsim
)