Added ability to directly embed NED programs in NEDsim binary for runtime execution.
authorAaron Taylor <ataylor@subgeniuskitty.com>
Mon, 12 Jul 2021 21:49:30 +0000 (14:49 -0700)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Mon, 12 Jul 2021 21:49:30 +0000 (14:49 -0700)
hacks/NEDsim/Makefile
hacks/NEDsim/NEDsim.c
hacks/NEDsim/ned_programs/Makefile [new file with mode: 0644]
hacks/NEDsim/ned_programs/README.md [new file with mode: 0644]
hacks/NEDsim/ned_programs/container.x86_asm_template [new file with mode: 0644]
hacks/NEDsim/ned_programs/integer_stack.ned_asm [new file with mode: 0644]
hacks/NEDsim/simulator.c
hacks/NEDsim/simulator.h

index 693eaed..dd1f25b 100644 (file)
@@ -39,15 +39,26 @@ DEFINES  = -DGETTIMEOFDAY_TWO_ARGS -DHAVE_UNISTD_H
 # comprise the screenhack library and link them directly into the binary.
 SCREENHACK_SRC != ls -1 ../../screenhack/*.c
 
 # comprise the screenhack library and link them directly into the binary.
 SCREENHACK_SRC != ls -1 ../../screenhack/*.c
 
+# Create NED_OBJ containing a list of object files with embedded NED1 machine
+# code which should be linked to the NEDsim binary.
+.SUFFIXES: .ned_asm
+NED_SRC != ls ned_programs/*.ned_asm
+NED_OBJ := $(NED_SRC:.ned_asm=.o)
+
 ################################################################################
 # Targets
 ################################################################################
 
 ################################################################################
 # Targets
 ################################################################################
 
-all:
-       @$(CC) $(CC_FLAGS) $(DEFINES) $(INC_PATH) $(LIB_PATH) $(LIBS) -o NEDsim NEDsim.c simulator.c $(SCREENHACK_SRC)
+all: embed
+       @$(CC) $(CC_FLAGS) $(DEFINES) $(INC_PATH) $(LIB_PATH) $(LIBS) \
+                       -o NEDsim NEDsim.c simulator.c $(SCREENHACK_SRC) $(NED_OBJ)
+
+embed:
+       @cd ned_programs && make all && cd ..
 
 clean:
 
 clean:
-       @rm -f NEDsim NEDsim.core
+       @cd ned_programs && make clean && cd ..
+       @rm -f NEDsim NEDsim.core *.o
 
 run: clean all
        @echo ""
 
 run: clean all
        @echo ""
index 15a25a8..9b5ceef 100644 (file)
@@ -3,9 +3,7 @@
 
 #include "screenhack.h"
 #include "simulator.h"
 
 #include "screenhack.h"
 #include "simulator.h"
-
-// 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
+#include "ned_programs/embedded_ned_program_declarations.h"
 
 /* -------------------------------------------------------------------------- */
 /* Front Panel Layout                                                         */
 
 /* -------------------------------------------------------------------------- */
 /* Front Panel Layout                                                         */
@@ -122,6 +120,11 @@ struct NEDsim {
     // if the display were using a 72 point helvetica font without emphasis.
     char * current_font;
 
     // if the display were using a 72 point helvetica font without emphasis.
     char * current_font;
 
+    // Contains the address of the top of the heap window. In total, the heap
+    // window will display from `heap_window_offset` to
+    // `heap_window_offset+num_data_rows`.
+    uint32_t heap_window_offset;
+
     // This struct contains all machine state for the simulated NED computer.
     struct NEDstate * nedstate;
 };
     // This struct contains all machine state for the simulated NED computer.
     struct NEDstate * nedstate;
 };
@@ -185,6 +188,24 @@ static struct color_scheme color_list[] = {
     }
 };
 
     }
 };
 
+// Each instance of this struct will describe a single NED1 program embedded in
+// NEDsim as a binary blob. See `ned_programs/README.md` for details.
+struct ned_program {
+    const uint8_t * binary_blob; // Pointer to raw NED1 machine code
+    const size_t * blob_size;    // Size of raw NED1 machine code
+    uint32_t heap_window_offset; // Location of window into NED system RAM
+};
+
+// All NED1 programs embedded in NEDsim should be manually added to this array.
+// See `ned_programs/README.md` for details.
+static struct ned_program ned_programs[] = {
+    {
+        integer_stack,
+        &integer_stack_size,
+        0x20000000
+    }
+};
+
 /* -------------------------------------------------------------------------- */
 /* Helper Functions                                                           */
 /* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 /* Helper Functions                                                           */
 /* -------------------------------------------------------------------------- */
@@ -484,7 +505,7 @@ update_display(struct NEDsim * nedsim)
     // Draw the heap lights.
     for (int i = 0; i < nedsim->num_data_rows; i++) {
         draw_wordline_lights(nedsim,
     // Draw the heap lights.
     for (int i = 0; i < nedsim->num_data_rows; i++) {
         draw_wordline_lights(nedsim,
-                ram_r_word(nedsim->nedstate, HEAP_START_ADDRESS+(i*BPW)),
+                ram_r_word(nedsim->nedstate, nedsim->heap_window_offset+(i*BPW)),
                 HEAP_X_OFFSET, HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT+i
         );
     }
                 HEAP_X_OFFSET, HEAP_Y_OFFSET+HEAP_LABEL_HEIGHT+i
         );
     }
@@ -717,7 +738,7 @@ draw_heap(struct NEDsim * nedsim)
             HEAP_WIDTH, HEAP_LABEL_HEIGHT, False
     );
     char address[11];
             HEAP_WIDTH, HEAP_LABEL_HEIGHT, False
     );
     char address[11];
-    snprintf(address, sizeof(address), "0x%08X", HEAP_START_ADDRESS);
+    snprintf(address, sizeof(address), "0x%08X", nedsim->heap_window_offset);
     draw_text(nedsim, address, (HEAP_X_OFFSET+(HEAP_WIDTH/2)+1), HEAP_Y_OFFSET,
             HEAP_WIDTH, HEAP_LABEL_HEIGHT, False
     );
     draw_text(nedsim, address, (HEAP_X_OFFSET+(HEAP_WIDTH/2)+1), HEAP_Y_OFFSET,
             HEAP_WIDTH, HEAP_LABEL_HEIGHT, False
     );
@@ -785,10 +806,18 @@ NEDsim_init(Display * dpy, Window win)
     // of the included programs.
     char * input_file = get_string_resource(nedsim->dpy, "binary", "String");
     if (input_file == NULL) {
     // 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");
+        size_t id = random() % sizeof(ned_programs)/sizeof(ned_programs[0]);
+        nedsim->nedstate = init_simulator(NULL, ned_programs[id].binary_blob, ned_programs[id].blob_size);
+        nedsim->heap_window_offset = ned_programs[id].heap_window_offset;
     } else {
     } else {
-        nedsim->nedstate = init_simulator(input_file);
+        nedsim->nedstate = init_simulator(input_file, NULL, 0);
+        // Note: Since we're accepting this as a runtime CLI option, we really
+        // should verify that `heap_window_offset+num_data_rows` doesn't exceed
+        // the limits of NED's simulated RAM. However, since the RAM size is a
+        // #define located in the simulator itself, and since I mostly plan to
+        // use this screensaver with built-in programs, for now we'll just let
+        // it segfault if the user requests a window outside simulated RAM.
+        nedsim->heap_window_offset = get_integer_resource(nedsim->dpy, "heapwindow", "Integer");
     }
 
     // If the user did not select a color scheme, select one randomly.
     }
 
     // If the user did not select a color scheme, select one randomly.
@@ -896,7 +925,8 @@ NEDsim_reshape(Display * dpy, Window win, void * closure, unsigned int w, unsign
 
 static const char * NEDsim_defaults[] = {
     "*delay:              250",
 
 static const char * NEDsim_defaults[] = {
     "*delay:              250",
-    "*color:               -1",
+    "*color:              -1",
+    "*heapwindow:         0x20000000",
     0
 };
 
     0
 };
 
@@ -904,6 +934,7 @@ static XrmOptionDescRec NEDsim_options[] = {
     { "-delay",              ".delay",                  XrmoptionSepArg, 0      },
     { "-binary",             ".binary",                 XrmoptionSepArg, 0      },
     { "-color",              ".color",                  XrmoptionSepArg, 0      },
     { "-delay",              ".delay",                  XrmoptionSepArg, 0      },
     { "-binary",             ".binary",                 XrmoptionSepArg, 0      },
     { "-color",              ".color",                  XrmoptionSepArg, 0      },
+    { "-heapwindow",         ".heapwindow",             XrmoptionSepArg, 0      },
     { 0, 0, 0, 0 }
 };
 
     { 0, 0, 0, 0 }
 };
 
diff --git a/hacks/NEDsim/ned_programs/Makefile b/hacks/NEDsim/ned_programs/Makefile
new file mode 100644 (file)
index 0000000..716c5c6
--- /dev/null
@@ -0,0 +1,61 @@
+#############################################################
+# (c) 2021 Aaron Taylor <ataylor at subgeniuskitty dot com> #
+# See LICENSE.txt file for copyright and license details.   #
+#############################################################
+
+################################################################################
+# Configuration
+################################################################################
+
+# To keep the rules simple, since we are dealing with six different file
+# extensions, NED_EMBED is defined with no suffix, leaving the suffix to be
+# hardcoded in each step as appropriate and simplying the make target list.
+.SUFFIXES: .ned_asm
+NED_SRC != ls *.ned_asm
+NED_EMBED := $(NED_SRC:.ned_asm=)
+
+# This header file will contain C declarations for the binary blobs and a
+# variable which tracks their size. It should be included in any C source code
+# that will link against the object files containing the binary blobs.
+DECLARATION_HEADER_FILE = embedded_ned_program_declarations.h
+
+# Misc program names/paths.
+GNU_ASM     = as
+NED_ASM     = ned1asm
+NED_OBJDUMP = ned1objdump
+
+################################################################################
+# Targets
+################################################################################
+
+# Since nedasm and nedobjcopy do not overwrite existing files, include target
+# `clean` as a preq.
+all: clean $(NED_EMBED)
+    # Build an #include guard around the C declarations of the binary blobs.
+       @mv $(DECLARATION_HEADER_FILE) $(DECLARATION_HEADER_FILE).tempfile
+       @echo "/* Autogenerated code. Do not edit. */" >> $(DECLARATION_HEADER_FILE)
+       @echo "#ifndef NEDSIM_AUTOGEN_BLOB_DECLARATIONS" >> $(DECLARATION_HEADER_FILE)
+       @echo "#define NEDSIM_AUTOGEN_BLOB_DECLARATIONS" >> $(DECLARATION_HEADER_FILE)
+       @cat $(DECLARATION_HEADER_FILE).tempfile >> $(DECLARATION_HEADER_FILE)
+       @rm $(DECLARATION_HEADER_FILE).tempfile
+       @echo "#endif" >> $(DECLARATION_HEADER_FILE)
+
+.ned_asm:
+       # We assemble the NED source and then dump the executable code section of
+       # the resulting a.out file as a binary blob.
+       @$(NED_ASM) -i $*.ned_asm -o $*.out
+       @$(NED_OBJDUMP) -i $*.out -o $*.bin
+       # Using `sed`, prepare an x86 assembly file as a container for each NED
+       # binary blob. This includes building a C header file with relevant
+       # declarations for each binary blob.
+       @sed 's/INSERTNAME/$*/g' container.x86_asm_template > $*.x86_asm
+       @echo "extern const uint8_t $*[];" >> $(DECLARATION_HEADER_FILE)
+       @echo "extern const size_t $*_size;" >> $(DECLARATION_HEADER_FILE)
+       # Assemble the container, creating a linkable object file containing the
+       # NED binary blob.
+       @$(GNU_ASM) -o $*.o $*.x86_asm
+       @rm -f $*.bin $*.x86_asm $*.out
+
+clean:
+       @rm -f *.bin *.x86_asm *.out *.o $(DECLARATION_HEADER_FILE)*
+
diff --git a/hacks/NEDsim/ned_programs/README.md b/hacks/NEDsim/ned_programs/README.md
new file mode 100644 (file)
index 0000000..859201c
--- /dev/null
@@ -0,0 +1,50 @@
+Overview
+========
+
+This directory contains programs written in NED1 assembly for use with the
+NEDsim screensaver.
+
+
+Status
+======
+
+All programs in this directory are complete and verified to run on NEDsim.
+
+
+Instructions
+============
+
+First, note that none of this is necessary if you simply wish to run a custom
+program on the NEDsim screensaver. Any NED1 a.out format executable may be
+passed to the screensaver on the CLI using the flag `-binary filename.out`.
+
+The instructions that follow explain how to directly embed NED machine code in
+the NEDsim binary as built-in, default programs.
+
+The first step is simply copying a NED1 assembly file into the present
+directory with a `.ned_asm` suffix. The filename (minus the suffix) determines
+the identifier name in C. For example, if you add a file named
+`example.ned_asm`, then in C it would be accessed as `example[]` and its size
+would be tracked in `example_size`.
+
+The second step requires editing `NEDsim.c` in the parent directory, adding a
+new entry to the `ned_programs[]` array based on the name chosen in the
+previous step. Continuing the previous example, the new entry would resemble
+this:
+
+    {
+        example,
+        &example_size,
+        0x20000000
+    }
+
+Note that `0x20000000` is the address of the RAM Window displayed by the
+screensaver. You may move this window around if you wish to display a different
+region of system memory; it has no bearing on code execution, only on the
+screensaver's display. Since code is loaded starting at `0x20000000`, using
+this value will display the machine code and is a suitable fallback for
+programs without interesting memory patterns.
+
+With your NED1 assembly file in this directory and an appropriate entry added
+to the `NEDsim.c` file, simply recompile NEDsim and your code will be embedded
+in the NEDsim binary as a new default program.
diff --git a/hacks/NEDsim/ned_programs/container.x86_asm_template b/hacks/NEDsim/ned_programs/container.x86_asm_template
new file mode 100644 (file)
index 0000000..115ebd0
--- /dev/null
@@ -0,0 +1,20 @@
+# (c) 2021 Aaron Taylor <ataylor at subgeniuskitty dot com>
+# See LICENSE.txt file for copyright and license details.
+
+# This container is used to wrap NED binary blobs in x86 assembly files,
+# allowing them to be assembled into linkable object files.
+
+    .section .rodata
+
+    .global INSERTNAME
+    .type   INSERTNAME, @object
+    .balign 8
+INSERTNAME:
+    .incbin "INSERTNAME.bin"
+INSERTNAME_end:
+
+    .global INSERTNAME_size
+    .type   INSERTNAME_size, @object
+    .balign 8
+INSERTNAME_size:
+    .int    INSERTNAME_end - INSERTNAME
diff --git a/hacks/NEDsim/ned_programs/integer_stack.ned_asm b/hacks/NEDsim/ned_programs/integer_stack.ned_asm
new file mode 100644 (file)
index 0000000..d3285e8
--- /dev/null
@@ -0,0 +1,12 @@
+# (c) 2021 Aaron Taylor <ataylor at subgeniuskitty dot com>
+# See LICENSE.txt file for copyright and license details.
+
+# Starting at 1, build the positive integers on the stack.
+
+IM_1
+loop
+       LDSP+0
+       IM_1
+       ADD
+       JMP>loop
+HALT
index 7f5f135..ec1db20 100644 (file)
@@ -364,7 +364,7 @@ parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment,
 }
 
 struct NEDstate *
 }
 
 struct NEDstate *
-init_simulator(char * input_file)
+init_simulator(char * input_file, const uint8_t * blob, const size_t * blob_size)
 {
     struct NEDstate * state = malloc(sizeof(struct NEDstate));
     state->hack = malloc(sizeof(struct NEDhack));
 {
     struct NEDstate * state = malloc(sizeof(struct NEDstate));
     state->hack = malloc(sizeof(struct NEDhack));
@@ -381,20 +381,39 @@ init_simulator(char * input_file)
     state->hack->resume_word = false;
 
     /* Load an initial image into memory. */
     state->hack->resume_word = false;
 
     /* Load an initial image into memory. */
-    struct exec aout_exec;
-    struct nlist * symbol_table;
-    uint32_t symbol_count;
-    FILE * input = NULL;
-    if ((input = fopen(input_file, "r")) == NULL) {
-        fprintf(stderr, "ERROR: %s: %s\n", input_file, strerror(errno));
+    if (input_file) {
+        struct exec aout_exec;
+        struct nlist * symbol_table;
+        uint32_t symbol_count;
+        FILE * input = NULL;
+        if ((input = fopen(input_file, "r")) == NULL) {
+            fprintf(stderr, "ERROR: %s: %s\n", input_file, strerror(errno));
+            state->halted = true;
+        }
+        parse_aout_file(input, &aout_exec, state->ram, &symbol_table, &symbol_count);
+        fclose(input);
+        for (size_t i=0; i < symbol_count; i++) {
+            free(symbol_table[i].n_un.n_name);
+        }
+        free(symbol_table);
+    } else if (blob && blob_size) {
+        if (*blob_size <= RAM_LENGTH) {
+            size_t index = *blob_size;
+            while (index) {
+                index--;
+                state->ram[index] = blob[index];
+            }
+        } else {
+            fprintf(stderr,
+                    "ERROR: Built-in NED1 program is larger than simulated RAM (%zu vs %d bytes)",
+                    *blob_size, RAM_LENGTH
+            );
+            state->halted = true;
+        }
+    } else {
+        fprintf(stderr, "ERROR: No suitable binary image passed when initializing simulator.\n");
         state->halted = true;
     }
         state->halted = true;
     }
-    parse_aout_file(input, &aout_exec, state->ram, &symbol_table, &symbol_count);
-    fclose(input);
-    for (size_t i=0; i < symbol_count; i++) {
-        free(symbol_table[i].n_un.n_name);
-    }
-    free(symbol_table);
 
     return state;
 }
 
     return state;
 }
index 83f8622..51c461e 100644 (file)
@@ -63,7 +63,7 @@ enum syllables {
     HALT    = 0b00000000
 };
 
     HALT    = 0b00000000
 };
 
-struct NEDstate * init_simulator(char * input_file);
+struct NEDstate * init_simulator(char * input_file, const uint8_t * blob, const size_t * blob_size);
 struct NEDstate * run_simulator(struct NEDstate * state);
 uint32_t ram_r_word(struct NEDstate * state, uint32_t address);
 
 struct NEDstate * run_simulator(struct NEDstate * state);
 uint32_t ram_r_word(struct NEDstate * state, uint32_t address);