X-Git-Url: http://git.subgeniuskitty.com/ned1/.git/blobdiff_plain/b534dfa24f97d29ca672db9c60e7558ba2bf328b..de1f134509e09860d0577e6e9f4b7a15c6dfa6e5:/nedfp/nedfp.c 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); +}