From: Aaron Taylor Date: Thu, 10 Jan 2019 04:50:08 +0000 (-0800) Subject: Initial commit of `nedfp` as a front panel for `nedsim`. X-Git-Url: http://git.subgeniuskitty.com/ned1/.git/commitdiff_plain/de1f134509e09860d0577e6e9f4b7a15c6dfa6e5 Initial commit of `nedfp` as a front panel for `nedsim`. --- diff --git a/Makefile b/Makefile index d45d9da..fa3d29e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # © 2018 Aaron Taylor # See LICENSE.txt file for copyright and license details. -MODULES = nedsim nedasm neddis +MODULES = nedsim nedasm neddis nedfp all: @for dir in $(MODULES); do \ diff --git a/README.md b/README.md index d311c7d..7f729a2 100644 --- 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. +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. diff --git a/docs/compat_matrix.md b/docs/compat_matrix.md index b2a15c8..ca6cc01 100644 --- a/docs/compat_matrix.md +++ b/docs/compat_matrix.md @@ -7,10 +7,11 @@ projects. 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 | . | . | . | +| 5 | . | . | . | . | 1 | diff --git a/misc/licenses/OFL11 b/misc/licenses/OFL11 new file mode 100644 index 0000000..735d859 --- /dev/null +++ b/misc/licenses/OFL11 @@ -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 index 0000000..8c4e053 --- /dev/null +++ b/nedfp/Makefile @@ -0,0 +1,17 @@ +# © 2018 Aaron Taylor +# 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 index 0000000..bb871c0 --- /dev/null +++ b/nedfp/README.md @@ -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 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 index 0000000..30f7e6b --- /dev/null +++ b/nedfp/nedfp.c @@ -0,0 +1,298 @@ +/* + * © 2018 Aaron Taylor + * See LICENSE.txt file for copyright and license details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 to a TTF file.\n" + " Default is './liberation_mono.tff'.\n" + " -p 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> 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); +} diff --git a/nedsim/README.md b/nedsim/README.md index 846cb34..b8507af 100644 --- a/nedsim/README.md +++ b/nedsim/README.md @@ -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 `nedfp` program provides a passive front panel compatible with `nedsim`. diff --git a/nedsim/nedsim.c b/nedsim/nedsim.c index b88328b..be63706 100644 --- a/nedsim/nedsim.c +++ b/nedsim/nedsim.c @@ -15,10 +15,15 @@ #include #include #include +#include +#include +#include +#include +#include #include "../common/a.out.h" -#define VERSION 4 +#define VERSION 5 /* Bytes per word. */ #define BPW 4 @@ -94,12 +99,14 @@ void print_usage(char ** argv) { printf( "NED Simulator v%d (www.subgeniuskitty.com)\n" - "Usage: %s -i \n" + "Usage: %s -f \n" " -h Help (prints this message)\n" - " -i Specify a binary image file to load in RAM.\n" - " -p Period in nanoseconds of simulated system clock.\n" + " -f Specify a binary image file to load in RAM.\n" + " -s Frequency of simulated system clock.\n" " Allowable values are 1 <= clock <= 1,000,000,000.\n" - " -t Saves a trace of JMP and BRZ syllables to file.\n" + " -t Saves a trace of JMP and BRZ syllables to file.\n" + " -i IP address of nedfp instance. Enables front panel output.\n" + " -p Port of nedfp instance. Required when '-i' specified.\n" , 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) @@ -645,27 +699,38 @@ main(int argc, char ** argv) 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) { - 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; - 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; + case 'i': + // TODO: What do I want to consider valid input? + fp_ip = optarg; + break; 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 { - fprintf(stderr, "WARN: Clock period out of range.\n"); + fprintf(stderr, "WARNING: Clock period out of range.\n"); } break; } @@ -682,6 +747,7 @@ main(int argc, char ** argv) print_usage(argv); exit(EXIT_FAILURE); } + if (fp_ip != NULL && fp_port == NULL) asprintf(&fp_port, "4999"); /* * 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); + /* 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 */ @@ -761,6 +858,7 @@ main(int argc, char ** argv) /* 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; @@ -769,6 +867,7 @@ main(int argc, char ** argv) 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);