# 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 ""
#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 */
// 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;
};
}
};
+// 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 */
/* -------------------------------------------------------------------------- */
// 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_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
);
// 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.
static const char * NEDsim_defaults[] = {
"*delay: 250",
- "*color: -1",
+ "*color: -1",
+ "*heapwindow: 0x20000000",
0
};
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-binary", ".binary", XrmoptionSepArg, 0 },
{ "-color", ".color", XrmoptionSepArg, 0 },
+ { "-heapwindow", ".heapwindow", XrmoptionSepArg, 0 },
{ 0, 0, 0, 0 }
};
--- /dev/null
+#############################################################
+# (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)*
+
--- /dev/null
+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.
--- /dev/null
+# (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
--- /dev/null
+# (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
}
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));
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;
}
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);