text: #include <signal.h>
  #include <stdio.h>
  #include <stdarg.h>
  #include <limits.h>
  #include <errno.h>
  #include <string.h>
  #include <stdarg.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <getopt.h>
  #include <math.h>
  #include <byteswap.h>
  #include <assert.h>
  #include <termios.h>
  #include <unistd.h>
  #include <stdio.h>
  #include <fcntl.h>
  #include <time.h>
  #include <sys/mman.h>
  
  # define CRTSCTS 020000000000
  
  #include <readline/readline.h>
  #include <readline/history.h>
  
  #include "zforth.h"
  
  #define SPF_SYSCALL_QUIT 0 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_SIN 1 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_INCLUDE 2 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_SAVE 3 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_DECIMAL 4 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_HEX 5 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_NANOSLEEP 6 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_PEEK 7 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_POKE 8 + ZF_SYSCALL_USER
  #define SPF_SYSCALL_PROMPT_MODE 9 + ZF_SYSCALL_USER
  
  #define DEVICE_MEM_START 0x41200000
  #define DEVICE_MEM_END   0x43c1ffff
  static int DEVMEM_FD;
  static size_t PAGE_SIZE;
  static uint8_t *MEM_BASE;
  static uint8_t *MAPPED_MEM;
  
  enum rlp_mode
      {
       rlp_base_only,
       rlp_vert_stack,
       rlp_horiz_stack
      };
  
  static enum rlp_mode RL_PROMPT_MODE = rlp_base_only;
  static char RL_PROMPT[128];
  
  enum num_format
      {
       nf_decimal,
       nf_hex
      };
  
  enum num_format g_number_display_format = nf_decimal;
  
  volatile uint8_t channels = 0;
  FILE *sample_file = NULL;
  
  void intIgnorer(int dummy) {}
  
  static void update_prompt()
  {
  #define MAX_NUM_STR_LEN 12
  #define STACK_DEPTH 3
  	zf_cell topstack[STACK_DEPTH] = {0};
  	zf_addr size = 0;
  	char val[MAX_NUM_STR_LEN];
  	char *format;
  	int i;
  
  	/* first, reset the prompt */
  	memset(RL_PROMPT, 0, sizeof(RL_PROMPT));
  
  	/* if we want the top of the stack visible */
  	if (RL_PROMPT_MODE != rlp_base_only)
  	{
  		/* choose display format */
  		if (g_number_display_format == nf_decimal)
  			format = "%d";
  		else
  			format = "0x%x";
  
  		/* try to get the top 3 of the stack,
  		 * size is how many we actually get if < 3 */
  		size = get_dstack_contents(topstack, STACK_DEPTH);
  
  		if (RL_PROMPT_MODE == rlp_vert_stack)
  			strcat(RL_PROMPT, "\n");
  
  		/* print the three stack values */
  		i = STACK_DEPTH;
  		while(i-- > 0)
  		{
  			/* print number if we can */
  			if(size > 0)
  			{
  				/* create str from the zf_cell */
  				snprintf(val, MAX_NUM_STR_LEN, format, topstack[i]);
  				strcat(RL_PROMPT, val);
  				size--;
  			}
  			/* print . if no values left */
  			else
  				strcat(RL_PROMPT, ".");
  
  			/* add space or \n depending on vert or horiz mode */
  			if (RL_PROMPT_MODE == rlp_vert_stack)
  				strcat(RL_PROMPT, "\n");
  			else
  				strcat(RL_PROMPT, " ");
  		}
  	}
  
  	/* displaying the current base */
  	if (g_number_display_format == nf_hex)
  		strcat(RL_PROMPT, "hex");
  	else
  		strcat(RL_PROMPT, "dec");
  
  	strcat(RL_PROMPT, "> ");
  }
  
  
  /*  taken from https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c */
  
  int set_interface_attribs(int fd, int speed)
  {
      struct termios tty;
  
      int rc = tcgetattr(fd, &tty);
      assert (rc >= 0);
  
      cfsetospeed(&tty, (speed_t)speed);
      cfsetispeed(&tty, (speed_t)speed);
  
      tty.c_cflag |= (CLOCAL | CREAD);    /*  ignore modem controls */
      tty.c_cflag &= ~CSIZE;
      tty.c_cflag |= CS8;         /*  8-bit characters */
      tty.c_cflag &= ~PARENB;     /*  no parity bit */
      tty.c_cflag &= ~CSTOPB;     /*  only need 1 stop bit */
      tty.c_cflag &= ~CRTSCTS;    /*  no hardware flowcontrol */
      tty.c_cflag &= ~ECHO;
  
      /*  setup for non-canonical mode */
      tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
      tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
      tty.c_oflag &= ~OPOST;
  
      /*  fetch bytes as they become available */
      tty.c_cc[VMIN] = 1;
      tty.c_cc[VTIME] = 1;
  
      rc = tcsetattr(fd, TCSANOW, &tty);
      assert (rc == 0);
  
      return 0;
  
  }
  
  /*
   * Evaluate buffer with code, check return value and report errors
   */
  
  zf_result do_eval(const char *src, int line, const char *buf)
  {
      const char *msg = NULL;
  
      zf_result rv = zf_eval(buf);
  
      switch(rv)
      {
      case ZF_OK: break;
      case ZF_ABORT_INTERNAL_ERROR: msg = "internal error"; break;
      case ZF_ABORT_OUTSIDE_MEM: msg = "outside memory"; break;
      case ZF_ABORT_DSTACK_OVERRUN: msg = "dstack overrun"; break;
      case ZF_ABORT_DSTACK_UNDERRUN: msg = "dstack underrun"; break;
      case ZF_ABORT_RSTACK_OVERRUN: msg = "rstack overrun"; break;
      case ZF_ABORT_RSTACK_UNDERRUN: msg = "rstack underrun"; break;
      case ZF_ABORT_NOT_A_WORD: msg = "not a word"; break;
      case ZF_ABORT_COMPILE_ONLY_WORD: msg = "compile-only word"; break;
      case ZF_ABORT_INVALID_SIZE: msg = "invalid size"; break;
      case ZF_ABORT_DIVISION_BY_ZERO: msg = "division by zero"; break;
      default: msg = "unknown error";
      }
  
      if(msg) {
          fprintf(stderr, "\033[31m");
          if(src) fprintf(stderr, "%s:%d: ", src, line);
          fprintf(stderr, "%s\033[0m\n", msg);
      }
  
      return rv;
  }
  
  
  /*
   * Load given forth file
   */
  
  void include(const char *fname)
  {
      char buf[256];
  
      FILE *f = fopen(fname, "rb");
      int line = 1;
      if(f) {
          while(fgets(buf, sizeof(buf), f)) {
              do_eval(fname, line++, buf);
          }
          fclose(f);
      } else {
          fprintf(stderr, "error opening file '%s': %s\n", fname, strerror(errno));
      }
  }
  
  
  /*
   * Save dictionary
   */
  
  static void save(const char *fname)
  {
      size_t len;
      void *p = zf_dump(&len);
      FILE *f = fopen(fname, "wb");
      if(f) {
          fwrite(p, 1, len, f);
          fclose(f);
      }
  }
  
  
  /*
   * Load dictionary
   */
  
  static void load(const char *fname)
  {
      size_t len;
      void *p = zf_dump(&len);
      FILE *f = fopen(fname, "rb");
      if(f) {
          (void)!fread(p, 1, len, f);
          fclose(f);
      } else {
          perror("read");
      }
  }
  
  /*
   * Syscall functions
   */
  
  static void spf_emit()
  {
          putchar((char)zf_pop());
          fflush(stdout);
  }
  
  static void spf_print()
  {
          if (g_number_display_format == nf_decimal)
              printf("%d" " ", zf_pop());
  	else if (g_number_display_format == nf_hex)
              printf("0x%x" " ", zf_pop());
  	else
              printf(ZF_CELL_FMT " ", zf_pop());
  }
  
  static void spf_tell()
  {
          zf_cell len = zf_pop();
          void *buf = (uint8_t *)zf_dump(NULL) + (int)zf_pop();
          (void)fwrite(buf, 1, len, stdout);
          fflush(stdout);
  }
  
  static void spf_quit()
  {
  	munmap(MEM_BASE, PAGE_SIZE);
  	close(DEVMEM_FD);
          printf("\n");
          exit(0);
  }
  
  
  static void spf_nanosleep()
  {
  #ifdef __USE_POSIX199309
      int rc = -1;
      struct timespec ts;
      ts.tv_sec = 0;
      ts.tv_nsec = (long) zf_pop();
      rc = nanosleep(&ts, NULL);
      if (rc)
      {
          fprintf(stderr, "\033[31m%s\033[0m\n", "could not sleep ");
  	fflush(stderr);
  	zf_abort(ZF_ABORT_INTERNAL_ERROR);
      }
  #else
          fprintf(stderr, "\033[31m%s\033[0m\n", "no nanosleep on this system ");
  	fflush(stderr);
  	zf_abort(ZF_ABORT_INTERNAL_ERROR);
  #endif
  }
  
  static void spf_peek()
  {
  	uint32_t addr = zf_pop();
  	uint32_t offset = addr - DEVICE_MEM_START;
  	uint32_t *localaddr;
  
  	if (addr < DEVICE_MEM_START || addr > DEVICE_MEM_END){
          	fprintf(stderr, "\033[31m%s\033[0m\n", "invalid address ");
  		fflush(stderr);
  		zf_abort(ZF_ABORT_INTERNAL_ERROR);
  	}
  	localaddr = (uint32_t*) &(MAPPED_MEM[offset]);
  	zf_push((zf_cell) *localaddr);
  }
  
  static void spf_poke()
  {
  	uint32_t value = zf_pop();
  	uint32_t addr = zf_pop();
  	uint32_t offset = addr - DEVICE_MEM_START;
  	volatile uint32_t *localaddr;
  
  	if (addr < DEVICE_MEM_START || addr > DEVICE_MEM_END){
          	fprintf(stderr, "\033[31m%s\033[0m\n", "invalid address ");
  		fflush(stderr);
  		zf_abort(ZF_ABORT_INTERNAL_ERROR);
  	}
  	localaddr = (uint32_t*) &(MAPPED_MEM[offset]);
  	*localaddr = value;
  }
  
  /*
   * Sys callback function
   */
  
  zf_input_state zf_host_sys(zf_syscall_id id, const char *input)
  {
  
      switch((int)id) {
  
      /* The core system callbacks */
  
      case ZF_SYSCALL_EMIT:
  	spf_emit();
          break;
  
      case ZF_SYSCALL_PRINT:
  	spf_print();
          break;
  
      case ZF_SYSCALL_TELL:
  	spf_tell();
          break;
  
  
          /* Application specific callbacks */
  
      case SPF_SYSCALL_QUIT:
  	spf_quit();
          break;
  
      case SPF_SYSCALL_SIN:
          zf_push(sin(zf_pop()));
          break;
  
      case SPF_SYSCALL_INCLUDE:
          if(input == NULL) {
              return ZF_INPUT_PASS_WORD;
          }
          include(input);
          break;
  
      case SPF_SYSCALL_SAVE:
          save("zforth.save");
          break;
  
      case SPF_SYSCALL_DECIMAL:
          g_number_display_format = nf_decimal;
          break;
  
      case SPF_SYSCALL_HEX:
          g_number_display_format = nf_hex;
          break;
  
      case SPF_SYSCALL_NANOSLEEP:
          spf_nanosleep();
          break;
  
      case SPF_SYSCALL_PEEK:
  	spf_peek();
  	break;
  
      case SPF_SYSCALL_POKE:
  	spf_poke();
  	break;
  
      case SPF_SYSCALL_PROMPT_MODE:
  	RL_PROMPT_MODE = (RL_PROMPT_MODE + 1) % 3;
  	break;
  
      default:
          printf("unhandled syscall %d\n", id);
          break;
      }
  
      return ZF_INPUT_INTERPRET;
  }
  
  
  /*
   * Tracing output
   */
  
  void zf_host_trace(const char *fmt, va_list va)
  {
      fprintf(stderr, "\033[1;30m");
      vfprintf(stderr, fmt, va);
      fprintf(stderr, "\033[0m");
  }
  
  
  /*
   * Parse number
   */
  
  zf_cell zf_host_parse_num(const char *buf)
  {
      zf_cell v;
      int r;
      char *check;
  
      if (nf_decimal == g_number_display_format)
          v = strtol(buf, &check, 10);
      else
          v = strtol(buf, &check, 16);
  
      if(strlen(check) > 0) {
          zf_abort(ZF_ABORT_NOT_A_WORD);
      }
      return v;
  }
  
  
  void usage(void)
  {
      fprintf(stderr,
              "usage: zfort [options] [src ...]\n"
              "\n"
              "Options:\n"
              "   -h         show help\n"
              "   -t         enable tracing\n"
              "   -l FILE    load dictionary from FILE\n"
  	);
  }
  
  
  /*
   * Main
   */
  
  int main(int argc, char **argv)
  {
      int i;
      int c;
      int trace = 0;
      int line = 0;
      const char *fname_load = NULL;
      int fd;
      uint32_t offset = DEVICE_MEM_START;
      size_t len = DEVICE_MEM_END - DEVICE_MEM_START;
      uint32_t page_base, page_offset;
      size_t pagesize;
      unsigned char *mem_base;
  
      /* Parse command line options */
  
      while((c = getopt(argc, argv, "hl:t")) != -1) {
          switch(c) {
          case 't':
              trace = 1;
              break;
          case 'l':
              fname_load = optarg;
              break;
          case 'h':
              usage();
              exit(0);
          }
      }
  
      argc -= optind;
      argv += optind;
  
  
      /* Initialize zforth */
  
      zf_init(trace);
  
      /* Initialize memory map */
  
      /* Truncate offset to a multiple of the page size, or mmap will fail. */
      pagesize = sysconf(_SC_PAGE_SIZE);
      page_base = (offset / pagesize) * pagesize;
      page_offset = offset - page_base;
  
      fd = open("/dev/mem", (O_RDWR | O_SYNC));
      mem_base = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page_base);
      if (mem_base == MAP_FAILED) {
          fprintf(stderr, "\033[31m%s\033[0m\n", "could not map memory\n ");
  	fflush(stderr);
          return -1;
      }
  
      DEVMEM_FD = fd;
      PAGE_SIZE = pagesize;
      MEM_BASE = mem_base;
      MAPPED_MEM = (uint8_t*) mem_base + page_offset;
  
      /* Load dict from disk if requested, otherwise bootstrap fort
       * dictionary */
  
      if(fname_load) {
          load(fname_load);
      } else {
          zf_bootstrap();
  	do_eval("core.zf", -1, "130 sys /usr/share/forth/core.zf");
      }
  
  
      /* Include files from command line */
  
      for(i=0; i<argc; i++) {
          include(argv[i]);
      }
  
  
      /* Interactive interpreter: read a line using readline library,
       * and pass to zf_eval() for evaluation*/
  
      read_history(".zforth.hist");
  
      /* prevent CTRL-C */
      signal(SIGINT, intIgnorer);
  
      for(;;) {
  	char *buf;
  	update_prompt();
          buf = readline(RL_PROMPT);
          if(buf == NULL) break;
  
          if(strlen(buf) > 0) {
  
              do_eval("stdin", ++line, buf);
              printf("\n");
  
              add_history(buf);
              write_history(".zforth.hist");
  
          }
  
          free (buf);
  
      }
  
      return 0;
  }
  
  
  /*
   * End
   */
  