Initial commit of `nedfp` as a front panel for `nedsim`.
authorAaron Taylor <ataylor@subgeniuskitty.com>
Thu, 10 Jan 2019 04:50:08 +0000 (20:50 -0800)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Thu, 10 Jan 2019 04:50:08 +0000 (20:50 -0800)
Makefile
README.md
docs/compat_matrix.md
misc/licenses/OFL11 [new file with mode: 0644]
nedfp/Makefile [new file with mode: 0644]
nedfp/README.md [new file with mode: 0644]
nedfp/liberation_mono.ttf [new file with mode: 0644]
nedfp/nedfp.c [new file with mode: 0644]
nedsim/README.md
nedsim/nedsim.c

index d45d9da..fa3d29e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 # © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
 # See LICENSE.txt file for copyright and license details.
 
 # © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
 # See LICENSE.txt file for copyright and license details.
 
-MODULES = nedsim nedasm neddis
+MODULES = nedsim nedasm neddis nedfp
 
 all:
        @for dir in $(MODULES); do \
 
 all:
        @for dir in $(MODULES); do \
index d311c7d..7f729a2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -10,6 +10,9 @@ The folders `nedsim/`, `nedasm/` and `neddis/` form a simulator, assembler and
 disassembler for the NED architecture. More details can be found in the
 `README.md` located in the top level directory of each sub-project.
 
 disassembler for the NED architecture. More details can be found in the
 `README.md` located in the top level directory of each sub-project.
 
+A passive front panel, found in the `nedfp/` folder, works in conjunction with
+`nedsim`.
+
 Among other things, the `docs/` folder contains an architecture manual,
 instruction reference, and compatibility matrix for the various parts of NED.
 
 Among other things, the `docs/` folder contains an architecture manual,
 instruction reference, and compatibility matrix for the various parts of NED.
 
index b2a15c8..ca6cc01 100644 (file)
@@ -7,10 +7,11 @@ projects.
 Compatibility Matrix
 ====================
 
 Compatibility Matrix
 ====================
 
-| nedsim | nedasm | neddis | Arch. Man. | Inst. Ref. |
-| ------ | ------ | ------ | ---------- | ---------- |
+| nedsim | nedasm | neddis | Arch. Man. | Inst. Ref. | nedfp |
+| ------ | ------ | ------ | ---------- | ---------- | ----- |
 |      1 |      1 |      1 |          1 |          1 |
 |      2 |      2 |      2 |          . |          . |
 |      3 |      . |      . |          . |          . |
 |      4 |      . |      . |          2 |          2 |
 |      . |      3 |      . |          . |          . |
 |      1 |      1 |      1 |          1 |          1 |
 |      2 |      2 |      2 |          . |          . |
 |      3 |      . |      . |          . |          . |
 |      4 |      . |      . |          2 |          2 |
 |      . |      3 |      . |          . |          . |
+|      5 |      . |      . |          . |          . |     1 |
diff --git a/misc/licenses/OFL11 b/misc/licenses/OFL11
new file mode 100644 (file)
index 0000000..735d859
--- /dev/null
@@ -0,0 +1,98 @@
+This license applies to sections of the following files:
+
+ned/nedfp/liberation_mono.ttf
+    Originally /usr/local/share/fonts/Liberation/LiberationMono-Regular.ttf on FreeBSD 11.1
+
+================================================================================
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/nedfp/Makefile b/nedfp/Makefile
new file mode 100644 (file)
index 0000000..8c4e053
--- /dev/null
@@ -0,0 +1,17 @@
+# © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
+# See LICENSE.txt file for copyright and license details.
+
+include ../common/main.mk
+
+SRC != ls *.c
+
+SDL_FLAGS = -L/usr/local/lib -lSDL2 -lSDL2_ttf -I/usr/local/include
+CC_FLAGS += $(SDL_FLAGS)
+
+all: nedfp
+
+nedfp:
+       $(CC) $(CC_FLAGS) -o $@ $(SRC)
+
+clean:
+       @rm -f nedfp
diff --git a/nedfp/README.md b/nedfp/README.md
new file mode 100644 (file)
index 0000000..bb871c0
--- /dev/null
@@ -0,0 +1,21 @@
+Overview
+========
+
+A passive, graphical front panel for use with `nedsim`.
+
+Status
+======
+
+Functional without restrictions.
+
+Installation
+============
+
+Use `make` and `make clean` to build/remove nedfp files. System installation
+requires manually copying the binary in to place. If a suitable font is not
+already installed, `liberation_mono.ttf`, located in this folder, may be used.
+
+Operation
+=========
+
+Use `nedfp -h` to see current command-line options.
diff --git a/nedfp/liberation_mono.ttf b/nedfp/liberation_mono.ttf
new file mode 100644 (file)
index 0000000..1a39bc7
Binary files /dev/null and b/nedfp/liberation_mono.ttf differ
diff --git a/nedfp/nedfp.c b/nedfp/nedfp.c
new file mode 100644 (file)
index 0000000..30f7e6b
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
+ * See LICENSE.txt file for copyright and license details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_ttf.h>
+
+#define VERSION 1
+
+#define DEFAULT_PORT    "4999"
+#define MAX_STRING      255
+#define BITSPERWORD     32
+
+#define WINDOW_HEIGHT   1000
+#define WINDOW_WIDTH    1000
+#define DOT_HEIGHT      (WINDOW_HEIGHT / 140)
+#define DOT_WIDTH       (WINDOW_WIDTH / 140)
+#define CHUNK_HEIGHT    (DOT_HEIGHT * 4)
+#define CHUNK_WIDTH     (DOT_WIDTH * 8)
+#define FONT_WIDTH      (DOT_WIDTH * 4)
+#define BORDER          (DOT_WIDTH * 3)
+
+void
+print_usage(char ** argv)
+{
+    printf( "NED Front Panel v%d (www.subgeniuskitty.com)\n"
+            "Usage: %s\n"
+            "  -h                      Help (prints this message)\n"
+            "  -f <path, optional>     Path to a TTF file.\n"
+            "                          Default is './liberation_mono.tff'.\n"
+            "  -p <port, optional>     Port to listen on for NED snapshots.\n"
+            "                          Default is 4999.\n"
+            , VERSION, argv[0]
+    );
+}
+
+void
+word_to_leds(uint32_t voffset, uint32_t hoffset, uint32_t word, SDL_Renderer * renderer)
+{
+    SDL_Rect r;
+
+    for (int i=0; i<8; i++) {
+        if (i%2) {
+            SDL_SetRenderDrawColor(renderer, 100, 150, 100, SDL_ALPHA_OPAQUE);
+        } else {
+            SDL_SetRenderDrawColor(renderer, 100, 100, 150, SDL_ALPHA_OPAQUE);
+        }
+        r.x = ((CHUNK_WIDTH * i) + hoffset);
+        r.y = voffset;
+        r.h = CHUNK_HEIGHT;
+        r.w = CHUNK_WIDTH;
+        SDL_RenderFillRect(renderer, &r);
+    }
+
+    for (int i=0; i<BITSPERWORD; i++) {
+        r.x = ((((2 * (BITSPERWORD - i)) * DOT_WIDTH) - (1.5 * DOT_WIDTH)) + hoffset);
+        r.y = (voffset + (2 * DOT_HEIGHT));
+        r.w = DOT_WIDTH;
+        r.h = DOT_HEIGHT;
+        if (((word >> i)) & 1) {
+            SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
+        } else {
+            SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+        }
+        SDL_RenderFillRect(renderer, &r);
+    }
+}
+
+void
+print_text(uint32_t voffset, uint32_t hoffset, char * str, TTF_Font * font, SDL_Renderer * renderer)
+{
+    SDL_Rect r;
+
+    r.x = hoffset;
+    r.y = voffset;
+    r.h = CHUNK_HEIGHT;
+    r.w = (FONT_WIDTH * strnlen(str, MAX_STRING));
+
+    SDL_Color white = {255,255,255};
+
+    SDL_Surface * surface = TTF_RenderText_Solid(font, str, white);
+    SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
+    SDL_RenderCopy(renderer, texture, NULL, &r);
+
+    SDL_DestroyTexture(texture);
+    SDL_FreeSurface(surface);
+}
+
+void
+sdl_init(SDL_Window ** window, SDL_Renderer ** renderer, TTF_Font ** font, char * font_path)
+{
+    /* Initialize SDL subsystems. */
+    /* We require the VIDEO subsystem for display. */
+    /* The EVENTS and FILE I/O subsystems are initialized by default. */
+    SDL_Init(SDL_INIT_VIDEO);
+    if(TTF_Init() == -1) printf("TTF_Init: %s\n", TTF_GetError());
+
+    /* Create an SDL window and renderer, and populate the pointers. */
+    SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, window, renderer);
+
+    /* Clear window so it appears normal to user. */
+    SDL_SetRenderDrawColor(*renderer, 128, 128, 128, SDL_ALPHA_OPAQUE);
+    SDL_RenderClear(*renderer);
+    SDL_RenderPresent(*renderer);
+
+    *font = TTF_OpenFont(font_path, 24);
+    if (font == NULL) printf("TTF_OpenFont: %s\n", TTF_GetError());
+}
+
+void
+sigint_handler(int sig)
+{
+    exit(EXIT_SUCCESS);
+}
+
+bool
+valid_sequence_number(uint32_t new_sn)
+{
+    static uint32_t old_sn;
+
+    /* Comparison is "new_sn > old_sn" per RFC1982: Serial Number Arithmetic. */
+    if (
+            ((new_sn < old_sn) && ((old_sn - new_sn) > (0x80000000)))
+            ||
+            ((new_sn > old_sn) && ((new_sn - old_sn) < (0x80000000)))
+        ) {
+        old_sn = new_sn;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+int
+main(int argc, char ** argv)
+{
+    /*
+     * Process command line arguments
+     */
+    int c;
+    char * font_path = NULL;
+    char * fp_port = NULL;
+    while ((c = getopt(argc,argv,"hp:f:")) != -1) {
+        switch (c) {
+            case 'f':
+                // TODO: What do I want to consider valid input?
+                font_path = optarg;
+                break;
+            case 'p':
+                // TODO: What do I want to consider valid input?
+                fp_port = optarg;
+                break;
+            case 'h':
+                print_usage(argv);
+                exit(EXIT_SUCCESS);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /*
+     * Initialization
+     */
+    if (font_path == NULL) asprintf(&font_path, "./liberation_mono.ttf");
+    if (fp_port == NULL) asprintf(&fp_port, "4999");
+
+    signal(SIGINT, sigint_handler);
+
+    SDL_Window * window;
+    SDL_Renderer * renderer;
+    TTF_Font * font;
+
+    sdl_init(&window, &renderer, &font, font_path);
+
+    int sockfd;
+    struct addrinfo hints;
+    struct addrinfo * servinfo;
+    struct addrinfo * p;
+    int rv;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_flags = AI_PASSIVE;
+    if ((rv = getaddrinfo(NULL, fp_port, &hints, &servinfo)) != 0) {
+        fprintf(stderr, "ERROR: getaddrinfo: %s\n", gai_strerror(rv));
+        exit(EXIT_FAILURE);
+    }
+    for (p = servinfo; p != NULL; p = p->ai_next) {
+        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+            continue;
+        }
+        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+            close(sockfd);
+            continue;
+        }
+        break;
+    }
+    if (p == NULL) {
+        fprintf(stderr, "ERROR: Failed to bind to socket.\n");
+        exit(EXIT_FAILURE);
+    }
+    freeaddrinfo(servinfo);
+
+    while(1) {
+        /*
+         * Datagram format (all entries are 4 bytes):
+         *   Sequence number (See RFC1982)
+         *   Currently executing word
+         *   PC
+         *   SC
+         *   PSW
+         *   SP
+         *   Top 16 stack entries
+         *   RAM start address
+         *   Top 16 RAM words from start address
+         */
+
+        uint32_t word[39];
+        socklen_t addr_len;
+        int numbytes;
+        struct sockaddr_storage their_addr;
+        char * msg;
+
+        addr_len = sizeof their_addr;
+        if ((numbytes = recvfrom(sockfd, word, 156, 0,
+                (struct sockaddr *) &their_addr, &addr_len)) == -1
+            ) {
+            fprintf(stderr, "WARNING: Unable to receive matching snapshot packet.\n");
+        }
+
+        if ((numbytes == 156) && valid_sequence_number(word[0])) {
+            /* Clear the screen */
+            SDL_SetRenderDrawColor(renderer, 128, 128, 128, SDL_ALPHA_OPAQUE);
+            SDL_RenderClear(renderer);
+    
+            /* Write the PC */
+            asprintf(&msg, "PC: 0x%08x", word[2]);
+            print_text((1 * CHUNK_HEIGHT), BORDER, msg, font, renderer);
+            free(msg);
+            word_to_leds((2 * CHUNK_HEIGHT), BORDER, word[2], renderer);
+    
+            /* Write the SC */
+            asprintf(&msg, "SC: 0x%08x", word[3]);
+            print_text((4 * CHUNK_HEIGHT), BORDER, msg, font, renderer);
+            free(msg);
+            word_to_leds((5 * CHUNK_HEIGHT), BORDER, word[3], renderer);
+    
+            /* Write the stack */
+            asprintf(&msg, "TOS@%u", word[5]);
+            print_text((7 * CHUNK_HEIGHT), BORDER, msg, font, renderer);
+            free(msg);
+            for (int i=0; i<16; i++) {
+                word_to_leds(((i+8) * CHUNK_HEIGHT), BORDER, word[(i+6)], renderer);
+            }
+    
+            /* Write the CW */
+            asprintf(&msg, "CW: 0x%08x", word[1]);
+            print_text((1 * CHUNK_HEIGHT), ((WINDOW_WIDTH / 2) + BORDER), msg, font, renderer);
+            free(msg);
+            word_to_leds((2 * CHUNK_HEIGHT), ((WINDOW_WIDTH / 2) + BORDER), word[1], renderer);
+    
+            /* Write the PSW */
+            asprintf(&msg, "PSW: 0x%08x", word[4]);
+            print_text((4 * CHUNK_HEIGHT), ((WINDOW_WIDTH / 2) + BORDER), msg, font, renderer);
+            free(msg);
+            word_to_leds((5 * CHUNK_HEIGHT), ((WINDOW_WIDTH / 2) + BORDER), word[4], renderer);
+    
+            /* Write the RAM dump */
+            asprintf(&msg, "RAM: 0x%08x", word[22]);
+            print_text((7 * CHUNK_HEIGHT), ((WINDOW_WIDTH / 2) + BORDER), msg, font, renderer);
+            free(msg);
+            for (int i=0; i<16; i++) {
+                word_to_leds(((i+8) * CHUNK_HEIGHT), ((WINDOW_WIDTH / 2) + BORDER), word[(i+23)], renderer);
+            }
+    
+            /* Render */
+            SDL_RenderPresent(renderer);
+        }
+    }
+
+    close(sockfd);
+    exit(EXIT_SUCCESS);
+}
index 846cb34..b8507af 100644 (file)
@@ -33,3 +33,5 @@ Use `nedsim -h` to see current command-line options.
 
 The simulator will terminate if a HALT instruction is reached and can also be
 terminated during operation with Ctrl-C.
 
 The simulator will terminate if a HALT instruction is reached and can also be
 terminated during operation with Ctrl-C.
+
+The `nedfp` program provides a passive front panel compatible with `nedsim`.
index b88328b..be63706 100644 (file)
 #include <time.h>
 #include <termios.h>
 #include <signal.h>
 #include <time.h>
 #include <termios.h>
 #include <signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
 
 #include "../common/a.out.h"
 
 
 #include "../common/a.out.h"
 
-#define VERSION 4
+#define VERSION 5
 
 /* Bytes per word. */
 #define BPW 4
 
 /* Bytes per word. */
 #define BPW 4
@@ -94,12 +99,14 @@ void
 print_usage(char ** argv)
 {
     printf( "NED Simulator v%d (www.subgeniuskitty.com)\n"
 print_usage(char ** argv)
 {
     printf( "NED Simulator v%d (www.subgeniuskitty.com)\n"
-            "Usage: %s -i <file>\n"
+            "Usage: %s -f <file>\n"
             "  -h                      Help (prints this message)\n"
             "  -h                      Help (prints this message)\n"
-            "  -i <file>               Specify a binary image file to load in RAM.\n"
-            "  -p <int ns, optional>   Period in nanoseconds of simulated system clock.\n"
+            "  -f <file>               Specify a binary image file to load in RAM.\n"
+            "  -s <int hz, optional>   Frequency of simulated system clock.\n"
             "                          Allowable values are 1 <= clock <= 1,000,000,000.\n"
             "                          Allowable values are 1 <= clock <= 1,000,000,000.\n"
-            "  -t <file>               Saves a trace of JMP and BRZ syllables to file.\n"
+            "  -t <file, optional>     Saves a trace of JMP and BRZ syllables to file.\n"
+            "  -i <ip-addr, optional>  IP address of nedfp instance. Enables front panel output.\n"
+            "  -p <port, optional>     Port of nedfp instance. Required when '-i' specified.\n"
             , VERSION, argv[0]
     );
 }
             , VERSION, argv[0]
     );
 }
@@ -634,6 +641,53 @@ parse_aout_file(FILE * input, struct exec * aout_exec, uint8_t * text_segment,
 
 }
 
 
 }
 
+void
+send_front_panel_update(struct NEDstate * state, int sockfd, uint32_t ram_address,
+                        const struct sockaddr *to, socklen_t tolen)
+{
+    /*
+     * Datagram format (all entries are 4 bytes):
+     *   Sequence number (See RFC1982)
+     *   Currently executing word
+     *   PC
+     *   SC
+     *   PSW
+     *   SP
+     *   Top 16 stack entries
+     *   RAM start address
+     *   Top 16 RAM words from start address
+     */
+    uint32_t snapshot[39];
+
+    static uint32_t seq_num;
+    seq_num == 0xffffffff ? seq_num = 0 : seq_num++;
+    snapshot[0] = seq_num;
+
+    snapshot[1] = ram_r_word(state, (state->active_thread->pc - 4));
+    snapshot[2] = state->active_thread->pc;
+    snapshot[3] = (uint32_t) state->active_thread->sc;
+    snapshot[4] = generate_binary_psw(state);
+    snapshot[5] = state->active_thread->sp;
+
+    int64_t sp = state->active_thread->sp;
+    for (int i=0; i<16; i++) {
+        if ((sp-i) < 0) {
+            snapshot[i+6] = 0;
+        } else {
+            snapshot[i+6] = state->active_thread->stack[sp-i];
+        }
+    }
+
+    snapshot[22] = ram_address;
+    for (int i=0; i<16; i++) {
+        snapshot[i+23] = ram_r_word(state,(ram_address + (i * 4)));
+    }
+
+    int numbytes;
+    if ((numbytes = sendto(sockfd, snapshot, 156, 0, to, tolen)) == -1) {
+        fprintf(stderr, "WARNING: Unable to send machine snapshot to front panel socket.\n");
+    }
+}
 
 int
 main(int argc, char ** argv)
 
 int
 main(int argc, char ** argv)
@@ -645,27 +699,38 @@ main(int argc, char ** argv)
     FILE * input = NULL;
     FILE * trace = NULL;
     uint32_t clock_period = 1;
     FILE * input = NULL;
     FILE * trace = NULL;
     uint32_t clock_period = 1;
-    while ((c = getopt(argc,argv,"i:p:t:h")) != -1) {
+    char * fp_ip =  NULL;
+    char * fp_port = NULL;
+    uint32_t fp_ram = 0x40000000;
+    while ((c = getopt(argc,argv,"hi:p:t:s:f:")) != -1) {
         switch (c) {
         switch (c) {
-            case 't':
-                if ((trace = fopen(optarg, "wx")) == NULL) {
+            case 'f':
+                if ((input = fopen(optarg, "r")) == NULL) {
                     fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
                     exit(EXIT_FAILURE);
                 }
                 break;
                     fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
                     exit(EXIT_FAILURE);
                 }
                 break;
-            case 'i':
-                if ((input = fopen(optarg, "r")) == NULL) {
+            case 't':
+                if ((trace = fopen(optarg, "wx")) == NULL) {
                     fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
                     exit(EXIT_FAILURE);
                 }
                 break;
                     fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
                     exit(EXIT_FAILURE);
                 }
                 break;
+            case 'i':
+                // TODO: What do I want to consider valid input?
+                fp_ip = optarg;
+                break;
             case 'p':
             case 'p':
+                // TODO: What do I want to consider valid input?
+                fp_port = optarg;
+                break;
+            case 's':
                 {
                 {
-                    intmax_t temp_p = strtoimax(optarg, NULL, 0);
-                    if (1 <= temp_p && temp_p <= 1000000000) {
-                        clock_period = temp_p;
+                    intmax_t freq = strtoimax(optarg, NULL, 0);
+                    if (1 <= freq && freq <= 1000000000) {
+                        clock_period = ((1.0 / freq) * 1000000000);
                     } else {
                     } else {
-                        fprintf(stderr, "WARN: Clock period out of range.\n");
+                        fprintf(stderr, "WARNING: Clock period out of range.\n");
                     }
                     break;
                 }
                     }
                     break;
                 }
@@ -682,6 +747,7 @@ main(int argc, char ** argv)
         print_usage(argv);
         exit(EXIT_FAILURE);
     }
         print_usage(argv);
         exit(EXIT_FAILURE);
     }
+    if (fp_ip != NULL && fp_port == NULL) asprintf(&fp_port, "4999");
 
     /*
      * Initialization
 
     /*
      * Initialization
@@ -712,6 +778,37 @@ main(int argc, char ** argv)
     parse_aout_file(input, &aout_exec, &(state->ram[address]), &symbol_table, &symbol_count);
     fclose(input);
 
     parse_aout_file(input, &aout_exec, &(state->ram[address]), &symbol_table, &symbol_count);
     fclose(input);
 
+    /* Start up the front panel connections, if specified. */
+    int sockfd;
+    struct addrinfo * p;
+    if (fp_ip) {
+        struct addrinfo hints;
+        struct addrinfo * servinfo;
+        int rv;
+    
+        memset(&hints, 0, sizeof hints);
+        hints.ai_family = AF_UNSPEC;
+        hints.ai_socktype = SOCK_DGRAM;
+    
+        if ((rv = getaddrinfo(fp_ip, fp_port, &hints, &servinfo)) != 0) {
+            fprintf(stderr, "ERROR: getaddrinfo: %s\n", gai_strerror(rv));
+            exit(EXIT_FAILURE);
+        }
+    
+        for(p = servinfo; p != NULL; p = p->ai_next) {
+            if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+                continue;
+            }
+            break;
+        }
+    
+        if (p == NULL) {
+            fprintf(stderr, "ERROR: Failed to create socket\n");
+            exit(EXIT_FAILURE);
+        }
+        freeaddrinfo(servinfo);
+    }
+
     /*
      * Main Loop
      */
     /*
      * Main Loop
      */
@@ -761,6 +858,7 @@ main(int argc, char ** argv)
 
         /* Decode instruction word format and execute. */
         if (iw & (0b1 << 31)) {                /* Instruction word is type A. */
 
         /* Decode instruction word format and execute. */
         if (iw & (0b1 << 31)) {                /* Instruction word is type A. */
+            if (fp_ip) send_front_panel_update(state, sockfd, fp_ram, p->ai_addr, p->ai_addrlen);
             wait_for_next_clock_cycle(state);
             stack_push(state->active_thread, (iw << 1));
             state->active_thread->debug->cycle_count += 1;
             wait_for_next_clock_cycle(state);
             stack_push(state->active_thread, (iw << 1));
             state->active_thread->debug->cycle_count += 1;
@@ -769,6 +867,7 @@ main(int argc, char ** argv)
                 state->active_thread->sc < SPW;
                 state->active_thread->sc++
             ) {
                 state->active_thread->sc < SPW;
                 state->active_thread->sc++
             ) {
+            if (fp_ip) send_front_panel_update(state, sockfd, fp_ram, p->ai_addr, p->ai_addrlen);
                 wait_for_next_clock_cycle(state);
                 uint8_t syllable = extract_syllable_from_word(iw, state->active_thread->sc);
                 execute_syllable(state, syllable);
                 wait_for_next_clock_cycle(state);
                 uint8_t syllable = extract_syllable_from_word(iw, state->active_thread->sc);
                 execute_syllable(state, syllable);