From: Aaron Taylor Date: Mon, 12 Jul 2021 21:49:30 +0000 (-0700) Subject: Added ability to directly embed NED programs in NEDsim binary for runtime execution. X-Git-Url: http://git.subgeniuskitty.com/screensavers/.git/commitdiff_plain/b73247cfaa571f5f162eca6e55ee7ea803d31dd7 Added ability to directly embed NED programs in NEDsim binary for runtime execution. --- diff --git a/hacks/NEDsim/Makefile b/hacks/NEDsim/Makefile index 693eaed..dd1f25b 100644 --- a/hacks/NEDsim/Makefile +++ b/hacks/NEDsim/Makefile @@ -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 +# 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 ################################################################################ -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: - @rm -f NEDsim NEDsim.core + @cd ned_programs && make clean && cd .. + @rm -f NEDsim NEDsim.core *.o run: clean all @echo "" diff --git a/hacks/NEDsim/NEDsim.c b/hacks/NEDsim/NEDsim.c index 15a25a8..9b5ceef 100644 --- a/hacks/NEDsim/NEDsim.c +++ b/hacks/NEDsim/NEDsim.c @@ -3,9 +3,7 @@ #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 */ @@ -122,6 +120,11 @@ struct NEDsim { // 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; }; @@ -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 */ /* -------------------------------------------------------------------------- */ @@ -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, - 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 ); } @@ -717,7 +738,7 @@ draw_heap(struct NEDsim * nedsim) 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 ); @@ -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) { - // 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 { - 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. @@ -896,7 +925,8 @@ NEDsim_reshape(Display * dpy, Window win, void * closure, unsigned int w, unsign static const char * NEDsim_defaults[] = { "*delay: 250", - "*color: -1", + "*color: -1", + "*heapwindow: 0x20000000", 0 }; @@ -904,6 +934,7 @@ static XrmOptionDescRec NEDsim_options[] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-binary", ".binary", XrmoptionSepArg, 0 }, { "-color", ".color", XrmoptionSepArg, 0 }, + { "-heapwindow", ".heapwindow", XrmoptionSepArg, 0 }, { 0, 0, 0, 0 } }; diff --git a/hacks/NEDsim/ned_programs/Makefile b/hacks/NEDsim/ned_programs/Makefile new file mode 100644 index 0000000..716c5c6 --- /dev/null +++ b/hacks/NEDsim/ned_programs/Makefile @@ -0,0 +1,61 @@ +############################################################# +# (c) 2021 Aaron Taylor # +# 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 index 0000000..859201c --- /dev/null +++ b/hacks/NEDsim/ned_programs/README.md @@ -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 index 0000000..115ebd0 --- /dev/null +++ b/hacks/NEDsim/ned_programs/container.x86_asm_template @@ -0,0 +1,20 @@ +# (c) 2021 Aaron Taylor +# 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 index 0000000..d3285e8 --- /dev/null +++ b/hacks/NEDsim/ned_programs/integer_stack.ned_asm @@ -0,0 +1,12 @@ +# (c) 2021 Aaron Taylor +# 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 diff --git a/hacks/NEDsim/simulator.c b/hacks/NEDsim/simulator.c index 7f5f135..ec1db20 100644 --- a/hacks/NEDsim/simulator.c +++ b/hacks/NEDsim/simulator.c @@ -364,7 +364,7 @@ parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment, } 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)); @@ -381,20 +381,39 @@ init_simulator(char * input_file) 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; } - 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; } diff --git a/hacks/NEDsim/simulator.h b/hacks/NEDsim/simulator.h index 83f8622..51c461e 100644 --- a/hacks/NEDsim/simulator.h +++ b/hacks/NEDsim/simulator.h @@ -63,7 +63,7 @@ enum syllables { 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);