/* http://neil.franklin.ch/Projects/VirtexTools/vd.c */
/*   - VirtexDump, textual dump of LUTs and BRAMdatas from Virtex bitstreams */
/*     and entire centers/GCLKs/CLBs/IOBs(N/S+W/E)/corners/BRAMs/DLLs */
/* author Neil Franklin, last modification 2003.09.20 */


/* ------ prerequisites section */

/* to really understand this code you should */
/*   have an understanding of the basics of */
/*     designing logic circuits, such as with [74|74LS|74F]xx(x) chips */
/*     basics of how programmable logic works, particularly FPGAs */
/*   have read the specifics from the */
/*     Xilinx Virtex 2.5V FPGAs (XCV00) data sheet */
/*       http://www.xilinx.com/partinfo/ds003.pdf */
/*     Xilinx Virtex-E 1.8V FPGAs (XCV00E) data sheet */
/*       http://www.xilinx.com/partinfo/ds022.pdf */
/*     Xilinx Virtex-E 1.8V Extended Memory FPGAs (XCV00E) data sheet */
/*       http://www.xilinx.com/partinfo/ds025.pdf */
/*     Xilinx Spartan[tm]-II data sheet (XC2S00) */
/*       http://www.xilinx.com/partinfo/ds001.htm */
/*     Xilinx Spartan[tm]-IIE data sheet (XC2S00E) */
/*       http://www.xilinx.com/partinfo/ds077.htm */


/* ------ include section */

/* ulong */
#include <sys/types.h>
/* malloc(), realloc(), free(), exit() */
#include <stdlib.h>
/* fopen(), fclose(), fread(), fprintf(), printf(), stdin, stdour, stderr */
#include <stdio.h>
/* strerror() */
#include <string.h>
/* errno */
#include <errno.h>

/* our own functions and definitions */
#include "virtex.h"


/* ------ prototypes section */

int main(int argc, char *argv[], char *env[]);

void getoptions(char *argv[]);
void helpoptions(FILE *Output);

void dumpoutfile(FILE *OutfilePtr, int Verbose);

void dumpnormals(void);
void dumpnormal(void);

void dumpentires(void);
void dumpentire(void);

void dumpmemorys(void);
void dumpmemory(void);

void dumpmemoryzzs(void);
void dumpmemoryzz(void);

void setformat(char *Base, int WordLength);
char *formatheaderline(char *HeaderLine);
char *formatmemoryword(char *MemoryWord);


/* ------ main program control section */

char *MainProgName;

/* NULL for no file given, = use stdin */
char *MainParamBitfileName = NULL;

/* NULL for no file given, = use stdout */
char *MainOptOutfileName = NULL;

/* 0 for no verbosity, ++ on every -v option */
int MainOptVerbose = 0;

int main(int argc, char *argv[], char *env[]) {

  FILE *Bitfile, *Outfile;
  char *serror;
  int ierror;

  getoptions(argv);


  /* read in bitstream */

  if (MainParamBitfileName) {
    if (MainOptVerbose) {
      fprintf(stderr, "reading from file: %s\n", MainParamBitfileName); }
    /* we open with "b" for binary, because else some systems do CRLF->LF */
    Bitfile = fopen(MainParamBitfileName, "rb");
    if (!Bitfile) {
      fprintf(stderr, "%s: %s: %s\n", MainProgName, strerror(errno),
        MainParamBitfileName);
      exit(1); } }
  else {
    if (MainOptVerbose) {
      fprintf(stderr, "reading from standard input\n"); }
    Bitfile = stdin; }

  serror = readbitfile(Bitfile, MainOptVerbose);
  if (serror != NULL) {
    fprintf(stderr, "%s: %s\n", MainProgName, serror);
    exit(1); }

  ierror = fclose(Bitfile);
  if (ierror) {
    fprintf(stderr, "%s: %s: %s\n", MainProgName, strerror(errno),
      MainParamBitfileName);
    exit(1); }


  /* dump data */

  if (MainOptOutfileName) {
    if (MainOptVerbose) {
      fprintf(stderr, "dumping to file: %s\n", MainOptOutfileName); }
    Outfile = fopen(MainOptOutfileName, "w");
    if (!Outfile) {
      fprintf(stderr, "%s: %s: %s\n",
        MainProgName, strerror(errno), MainOptOutfileName);
      exit(1); } }
  else {
    if (MainOptVerbose) {
      fprintf(stderr, "dumping to standard output\n"); }
    Outfile = stdout; }

  dumpoutfile(Outfile, MainOptVerbose);

  ierror = fclose(Outfile);
  if (ierror) {
    fprintf(stderr, "%s: %s: %s\n", MainProgName, strerror(errno),
      MainOptOutfileName);
    exit(1); }


  exit(0); }


/* ------ get command line options section */

#define OptNormalMode 0
#define OptEntireMode 1
#define OptMemoryMode 2
#define OptZigzagMode 3
int OptMode = OptNormalMode;

#define OptLongDecoration 0
#define OptShortDecoration 1
#define OptQuietDecoration 2
int OptDecoration = OptLongDecoration;

/* use default complete chip */
char *OptRange = "-/-";

/* use default binary display and byte length = word length */
char *OptMemoryBase = "bI";

void getoptions(char *argv[]) {

  MainProgName = *argv++;

  while (*argv != NULL && (*argv)[0] == '-') {
    /* it is a bunch options, modify fitting Opt* variables */

    char *option;
    /* can be -xyz multiple options */
    for (option = (*argv)+1; *option != 0; option++) {

      if (*option == 'n') {
        /* -n (normal mode) */
        OptMode = OptNormalMode; }

      if (*option == 'e') {
        /* -e (entire mode) */
        OptMode = OptEntireMode; }

      else if (*option == 'm') {
        /* -m (memory mode) */
        OptMode = OptMemoryMode; }

      else if (*option == 'z') {
        /* -z (zigzag memory mode) */
        OptMode = OptZigzagMode; }

      else if (*option == 'l') {
        /* -l (long decoration) */
        OptDecoration = OptLongDecoration; }

      else if (*option == 's') {
        /* -s (short decoration) */
        OptDecoration = OptShortDecoration; }

      else if (*option == 'q') {
        /* -q (quiet decoration) */
        OptDecoration = OptQuietDecoration; }

      else if (*option == 'v') {
        /* -v (verbose operation) */
        MainOptVerbose++; }

      else if (*option == 'r') {
        /* -r (range): next argument must be range description, take it  */
        argv++;
        OptRange = *argv; }

      else if (*option == 't') {
        /* -t (type): next argument must be base description, take it  */
        argv++;
        OptMemoryBase = *argv; }

      else if (*option == 'o') {
        /* -o (output set): next argument must be outfile name, take it */
        argv++;
        MainOptOutfileName = *argv; }

      else if (*option == 'h') {
        /* -h (help): output help option text, and exit then */
        helpoptions(stdout);
        exit(0); }

      else {
        /* unknown option given, give error message, help and error exit */
        fprintf(stderr, "%s: Error: unknown option: -%c\n\n",
          MainProgName, *option);
        helpoptions(stderr);
        exit(1); } }

    argv++; }

  if (*argv != NULL) {
    /* starting with non-digit, must be an bit file name */
    MainParamBitfileName = *argv++; }

  if (*argv != NULL) {
    /* there should be no params any more, give msg and help and error exit */
    fprintf(stderr, "%s: Error: too many parameters at: %s\n\n",
      MainProgName, *argv);
    helpoptions(stderr);
    exit(1); }

  if (MainOptVerbose) {
    fprintf(stderr, "*** %s: running in verbose mode ***\n", MainProgName); }

  return; }


/* ------ output help options text section */

void helpoptions(FILE *Output) {

  fprintf(Output,
    "Usage: %s [-emzlsqvh] [-r range] [-t base[byte]] [-o file] [bitfile]\n",
    MainProgName);
  fprintf(Output,
    "\n");
  fprintf(Output,
    "Options:\n");
  fprintf(Output,
    "  -n            normal dump: just LUT patterns (default)\n");
  fprintf(Output,
    "  -e            entire dump: CLB as full bit pattern\n");
  fprintf(Output,
    "  -m            memory dump: just LUT-RAMs, rows merged and rotated\n");
  fprintf(Output,
    "  -z            zigzag dump: LUT-RAM32, bit0/2/.. 1st sli, 1/3/.. 2nd\n");
  fprintf(Output,
    "  -l            long decoration: labels/addresses/data (default)\n");
  fprintf(Output,
    "  -s            short decoration: addresses/data, no labels\n");
  fprintf(Output,
    "  -q            quiet decoration: just data, no labels/addresses\n");
  fprintf(Output,
    "  -v            verbose operation: running activity report to stderr\n");
  fprintf(Output,
    "  -h            help: display this help text, then abort\n");
  fprintf(Output,
    "  -r spec       restrict range to only: [horizontal][/vertical]\n");
  fprintf(Output,
    "                horizontal: [left][-[right]], with left<right\n");
  fprintf(Output,
    "                vertical: [bottom][-[top]], with bottom<top\n");
  fprintf(Output,
    "                left, right: column[slice];  bottom, top: row[LUT]\n");
  fprintf(Output,
    "                column, row: number 0 to size-1 (from left or bottom)\n");
  fprintf(Output,
    "                slice: L (left=1) or R (right=0);  LUT: F or G\n");
  fprintf(Output,
    "  -t base[byte] type: of base of memory output (default bI)\n");
  fprintf(Output,
    "                base: a ASCII/name, b binary, c ASCII/backslash/octal\n");
  fprintf(Output,
    "                      d decimal, o octal, h/x hex, u unsigned decimal\n");
  fprintf(Output,
    "                byte: width number 1 to n or I=word, C=8, S=16, L=32\n");
  fprintf(Output,
    "  -o file       output: send to file (default stdout)\n");
  fprintf(Output,
    "\n");
  fprintf(Output,
    "bitfile:        read bitstream from this file (default stdin)\n"); }


/* ------ dump bits to output file section */

/* this needs to be global, as all dump*() functions use it for fprintf() */
FILE *Outfile;

void dumpoutfile(FILE *OutfilePtr, int Verbose) {

  char *serror;

  Outfile = OutfilePtr;

  /* select what rage we want to see dumped */
  if (Verbose) {
    fprintf(stderr, "setting range for dumping: %s\n", OptRange); }
  serror = setlutrange(OptRange);
  if (serror) {
    fprintf(stderr, "%s: %s\n", MainProgName, serror);
    exit(1); }

  /* *fixme*: at present we only support dumping CLBs */

  /* do the actual dumping */
  /*  select type of dumping, from more special to least special */
  if (OptMode == OptZigzagMode) {
    if (Verbose) {
      fprintf(stderr, "dumping data as zigzagged memory\n"); }
    dumpmemoryzzs(); }
  else if (OptMode == OptMemoryMode) {
    if (Verbose) {
      fprintf(stderr, "dumping data as wordwise memory\n"); }
    dumpmemorys(); }
  else if (OptMode == OptEntireMode) {
    if (Verbose) {
      fprintf(stderr, "dumping entire as bitmap\n"); }
    dumpentires(); }
  else {
    if (Verbose) {
      fprintf(stderr, "dumping data as normal\n"); }
    dumpnormals(); }

  if (Verbose) {
    fprintf(stderr, "finished dumping\n"); } }


/* ------ dump normal to outfile section */

/* grid of LUTs, call dumpnormal() for each LUT */

void dumpnormals(void) {

  for (firstsli(); isinslis(); nextsli()) {
    for (firstlut(); isinluts(); nextlut()) {

      dumpnormal(); } } }


/* single LUT, position from global vars in virtex.c */

void dumpnormal(void) {

  if (OptDecoration == OptLongDecoration) {
    /* long decoration of line for each LUT pair: */
    /* Column: ccc, Slice: s, Row: rrr, LUT: l, Value: 0xvvvv */
    /* example for one (bottom left) LUT: */
    /* Column:   0, Slice: L, Row:   0, LUT: F, Value: 0x0000 */
    lutpos *Pos = getlutpos();
    fprintf(Outfile,
            "Column: %3i, Slice: %1c, Row: %3i, LUT: %1c, Value: 0x%04hx\n",
            Pos->Col, Pos->Sli, Pos->Row, Pos->Lut, getlut()); }

  else if (OptDecoration == OptShortDecoration) {
    /* short decoration of line for each LUT pair: */
    /* cccs/rrrl: 0xvvvv */
    /* example for one (bottom left) LUT: */
    /* 000L/000F: 0x0000 */
    lutpos *Pos = getlutpos();
    fprintf(Outfile, "%03i%1c/%03i%1c: 0x%04hx\n",
            Pos->Col, Pos->Sli, Pos->Row, Pos->Lut, getlut()); }

  else {
    /* quiet decoration of line for each LUT pair: */
    /* 0xvvvv */
    /* example for one (bottom left) LUT: */
    /* 0x0000 */
    fprintf(Outfile, "0x%04hx\n", getlut()); } }


/* ------ dump entire to outfile section */

void dumpentires(void) {

  /* before first block print nothing ... */
  char *Separator = "";

  /* twice nextsli() or nextlut(), as only one dump per 2x2 LUTs */
  /*   what happens with the slice or LUT index is irrelevant, as not used */
  for (firstsli(); isinslis(); nextsli(), nextsli()) {
    for (firstlut(); isinluts(); nextlut(), nextlut()) {

      fprintf(Outfile, Separator);    
      dumpentire();
      /* ... then between blocks print an empty line (as separator) */
      Separator = "\n"; } } }


void dumpentire(void) {

  int Frames, Frame, Bits, Bit;
  char *TileTypeName;
  int (*getbit)(int Frame, int Bit);

  /* CLBs presently only supported */
  if      (0) {
    Frames = VirtexCenterFrames; Bits = VirtexMiddleBits;
    TileTypeName = "center"; getbit = getcenterbit; }
  else if (0) {
    Frames = VirtexGclkFrames; Bits = VirtexTopBotBits;
    TileTypeName = "GCLK"; getbit = getgclkbit; }
  else if (1) {
    Frames = VirtexClbFrames; Bits = VirtexMiddleBits;
    TileTypeName = "CLB"; getbit = getclbbit; }
  else if (0) {
    Frames = VirtexIobNSFrames; Bits = VirtexTopBotBits;
    TileTypeName = "IOBN/S"; getbit = getiobnsbit; }
  else if (0) {
    Frames = VirtexIobWEFrames; Bits = VirtexMiddleBits;
    TileTypeName = "IOBW/E"; getbit = getiobwebit; }
  else if (0) {
    Frames = VirtexCornerFrames; Bits = VirtexTopBotBits;
    TileTypeName = "corner"; getbit = getcornerbit; }
  else if (0) {
    Frames = VirtexBramCFrames; Bits = VirtexMiddleBits;
    TileTypeName = "BRAMcontrol"; getbit = getbramcbit; }
  else if (0) {
    Frames = VirtexDllFrames; Bits = VirtexTopBotBits;
    TileTypeName = "DLL"; getbit = getdllbit; }
  else if (0) {
    Frames = VirtexBramDFrames; Bits = VirtexMiddleBits;
    TileTypeName = "BRAMdata"; getbit = getbramdbit; }
  else if (0) {
    Frames = VirtexBramDPadFrames; Bits = VirtexTopBotBits;
    TileTypeName = "BRAMdatapadding"; getbit = getbramdpadbit; }

  if (OptDecoration == OptLongDecoration) {
    /* long stats (what we are dumping) */

    lutpos *Pos = getlutpos();
    int Line;

    fprintf(Outfile, "Tile Type: %s, Column: %3i, Row: %3i, Entire Bitmap:\n",
      TileTypeName, Pos->Col, Pos->Row);

    /* number frames(columns) */
    for (Line = 0; Line < 3; Line++) {
      char Address[4], HeaderLine[Frames+1], *HeaderChar = HeaderLine;

      for (Frame = 0; Frame < Frames; Frame++) {
        sprintf(Address, "%02i-", Frame);
        *HeaderChar++ = Address[Line]; }
      *HeaderChar = '\0';

      if      (Line == 0) {
        fprintf(Outfile, "   %s frame\n",   HeaderLine); }
      else if (Line == 1) {
        fprintf(Outfile, "   %s address\n", HeaderLine); }
      else if (Line == 2) {
        fprintf(Outfile, "  .%s\n",         HeaderLine); } } }

  if (OptDecoration == OptShortDecoration) {
    /* short stats, just data without headers */

    lutpos *Pos = getlutpos();
    fprintf(Outfile, "%s:%03i/%03i:\n", TileTypeName, Pos->Col, Pos->Row); }


  for (Bit = 0; Bit < Bits; Bit++) {

    char BitLine[Frames+1], *BitChar = BitLine;

    for (Frame = 0; Frame < Frames; Frame++) {
      int BitVal = (*getbit)(Frame, Bit);
      *BitChar++ = BitVal ? '1' : '0'; }
    *BitChar = '\0';

    if (OptDecoration == OptLongDecoration) {
      /* number bits(rows) */
      fprintf(Outfile, "%02i|", Bit); }
    fprintf(Outfile, "%s\n", BitLine); }


  if (OptDecoration == OptLongDecoration) {
    /* footer which completes the header */
    fprintf(Outfile, "bit address\n"); } }


/* ------ dump memory to outfile section */

void dumpmemorys(void) {

  char *Separator = "";

  for (firstsli(); isinslis(); nextsli()) {

    fprintf(Outfile, Separator);
    dumpmemory();
    Separator = "\n"; } }


/* stripe is an vertical group of LUTs */

#define MEMORYHEADERLINES 5

void dumpmemory(void) {

  int MemWidth = 0, BitNo;
  int HeaderLine, MemoryAddr;
  char *Header[MEMORYHEADERLINES], *Memory[16];


  /* calculate bits we need memory space for, counting LUTs is simplest way */
  for (firstlut(); isinluts(); nextlut()) {
    MemWidth++; }

  /* we can only set this here, after knowing memory width / word length */
  setformat(OptMemoryBase, MemWidth);


  /* get space to render memory into */

  for (HeaderLine = 0; HeaderLine < MEMORYHEADERLINES; HeaderLine++) {
    Header[HeaderLine] = malloc(MemWidth+1);
    /* zero terminate the strings, will be filled by indexed writes */
    Header[HeaderLine][MemWidth] = '\0'; }

  for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
    Memory[MemoryAddr] = malloc(MemWidth+1);
    Memory[MemoryAddr][MemWidth] = '\0'; }


  /* we want MSB left, = first in output string */
  /*   row+LUT count from bottom, = LSB, so start at end of string */
  BitNo = MemWidth;

  for (firstlut(); isinluts(); nextlut()) {

    char LutAddr[MEMORYHEADERLINES+1];
    lutpos *Pos = getlutpos(); lutval LutVal = getlut();

    BitNo--;

    snprintf(LutAddr, sizeof(LutAddr), "%03i%1c-", Pos->Row, Pos->Lut);
    for (HeaderLine = 0; HeaderLine < MEMORYHEADERLINES; HeaderLine++) {
      Header[HeaderLine][BitNo] = LutAddr[HeaderLine]; }

    /* transpose bits, from  Lut ver / Addr hor  to  Bit hor / Addr ver */
    for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
      char BitChar = (LutVal & (1<<MemoryAddr)) ? '1' : '0';
      Memory[MemoryAddr][BitNo] = BitChar; } }


  if (OptDecoration == OptLongDecoration) {
    /* output stats (what we are dumping), column and slice */

    lutpos *Pos;
    firstlut(); Pos = getlutpos();
    fprintf(Outfile, "Column: %3i, Slice: %1c, Memory Data:\n",
            Pos->Col, Pos->Sli);

    /* output which row we are in */
    fprintf(Outfile, "   %s row+LUT\n", formatheaderline(Header[0]));
    fprintf(Outfile, "   %s address\n", formatheaderline(Header[1]));
    fprintf(Outfile, "   %s\n",         formatheaderline(Header[2]));
    /* output which LUT we are in */
    fprintf(Outfile, "   %s\n",         formatheaderline(Header[3]));
    /* output the - separator, with . (corner) before it */
    fprintf(Outfile, "  .%s\n",         formatheaderline(Header[4])); }

  if (OptDecoration == OptShortDecoration) {
    /* short stats, just column+slice number and then data without headers */

    lutpos *Pos;

    /* from MSB column+slice/row+LUT */
    firstlut(); Pos = getlutpos();
    fprintf(Outfile, "%03i%1c/%03i%1c",
            Pos->Col, Pos->Sli, Pos->Row, Pos->Lut);
    /* to LSB row+LUT */
    lastlut();  Pos = getlutpos();
    fprintf(Outfile, "-%03i%1c:\n", Pos->Row, Pos->Lut); }

  for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
    if (OptDecoration == OptLongDecoration) {
      /* number addresses */
      fprintf(Outfile, "%02i|", MemoryAddr); }
    fprintf(Outfile, "%s\n", formatmemoryword(Memory[MemoryAddr])); }

  if (OptDecoration == OptLongDecoration) {
    /* footer which completes the header */
    fprintf(Outfile, "memory address\n"); }


  for (HeaderLine = 0; HeaderLine < MEMORYHEADERLINES; HeaderLine++) {
    free(Header[HeaderLine]); }

  for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
    free(Memory[MemoryAddr]); } }


/* ------ dump zigzagged memory to outfile section */

void dumpmemoryzzs(void) {

  char *Separator = "";

  /* we always have pairs of slices, one for 0/2/.., one for 1/3/.. */
  /*   so we call with the first column/slice of an pair, so step 2 silces  */
  for (firstsli(); isinslis(); nextsli(), nextsli()) {

    fprintf(Outfile, Separator);
    dumpmemoryzz();
    Separator = "\n"; } }


/* stripe is here 2 neighbor vertical groups of LUTs, for zigzagging */

#define MEMORYZZHEADERLINES 4

void dumpmemoryzz(void) {

  int MemWidth = 0, BitNo;
  int HeaderLine, MemoryAddr;
  char *Header[MEMORYZZHEADERLINES], *Memory[32];


  /* calculate bits we need memory space for, counting LUTs is simplest way */
  for (firstlut(); isinluts(); nextlut(), nextlut()) {
    /* 1 bit pro row, as F+G one bit, but 2 slices being used */
    MemWidth += 2; }

  /* we can only set this here, after knowing memory width / word length */
  setformat(OptMemoryBase, MemWidth);


  /* get space to render memory into */

  for (HeaderLine = 0; HeaderLine < MEMORYZZHEADERLINES; HeaderLine++) {
    Header[HeaderLine] = malloc(MemWidth+1);
    /* zero terminate the strings, will be filled by indexed writes */
    Header[HeaderLine][MemWidth] = '\0'; }

  for (MemoryAddr = 0; MemoryAddr < 32; MemoryAddr++) {
    Memory[MemoryAddr] = malloc(MemWidth+1);
    Memory[MemoryAddr][MemWidth] = '\0'; }


  /* we want MSB left, = first in output string */
  /*   row+LUT count from bottom, = LSB, so start at end of string */
  BitNo = MemWidth;

  /* we requre 2 LUTs per bit, must be an F/G pair, so no for (Lut...) */
  for (firstlut(); isinluts(); nextlut(), nextlut()) {

    char RowAddr[MEMORYZZHEADERLINES+1];
    lutpos *Pos; lutval LutValF, LutValG;

    /* 2 slices gives 2 pairs of LUTs as 2 32bit memories */


    /* bits 0/2/../n-2 come from first column+slice */

    BitNo--;

    Pos = getlutpos();
    snprintf(RowAddr, sizeof(RowAddr), "%03i-", Pos->Row);
    for (HeaderLine = 0; HeaderLine < MEMORYZZHEADERLINES; HeaderLine++) {
      Header[HeaderLine][BitNo] = RowAddr[HeaderLine]; }

    LutValF = getlut(); nextlut();
    LutValG = getlut(); prevlut();

    /* transpose LUT bits, from  Addr hor / Lut ver  to  Bit hor / Addr ver */
    /* first 16 addresses come from F LUTs */
    for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
      char BitChar = (LutValF & (1<<MemoryAddr)) ? '1' : '0';
      Memory[MemoryAddr][BitNo] = BitChar; }
    /* second 16 addresses come from G LUTs */
    for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
      char BitChar = (LutValG & (1<<MemoryAddr)) ? '1' : '0';
      Memory[MemoryAddr+16][BitNo] = BitChar; }

    /* zigzag to second slice */
    nextsli();


    /* bits 1/3/../n-1 come from second column+slice */

    BitNo--;

    Pos = getlutpos();
    snprintf(RowAddr, sizeof(RowAddr), "%03i-", Pos->Row);
    for (HeaderLine = 0; HeaderLine < MEMORYZZHEADERLINES; HeaderLine++) {
      Header[HeaderLine][BitNo] = RowAddr[HeaderLine]; }

    LutValF = getlut(); nextlut();
    LutValG = getlut(); prevlut();

    /* transpose LUT bits, from  Addr hor / Lut ver  to  Bit hor / Addr ver */
    /* first 16 addresses come from F LUTs */
    for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
      char BitChar = (LutValF & (1<<MemoryAddr)) ? '1' : '0';
      Memory[MemoryAddr][BitNo] = BitChar; }
    /* second 16 addresses come from G LUTs */
    for (MemoryAddr = 0; MemoryAddr < 16; MemoryAddr++) {
      char BitChar = (LutValG & (1<<MemoryAddr)) ? '1' : '0';
      Memory[MemoryAddr+16][BitNo] = BitChar; }

    /* zigzag back to first slice */
    prevsli(); }


  if (OptDecoration == OptLongDecoration) {
    /* output stats (what we are dumping), column and slice */

    lutpos *Pos;

    /* LSB column+slice, because it is in first slice */
    firstlut(); Pos = getlutpos(); nextsli();
    fprintf(Outfile, "Column+Slice: %3i%1c", Pos->Col, Pos->Sli);
    /* MSB column+slice, because it is in last slice */
    lastlut(); Pos = getlutpos(); prevsli();
    fprintf(Outfile, " and %3i%1c, Memory Data:\n", Pos->Col, Pos->Sli);

    /* output which row we are in */
    fprintf(Outfile, "    %s Row\n",     formatheaderline(Header[0]));
    fprintf(Outfile, "    %s Address\n", formatheaderline(Header[1]));
    fprintf(Outfile, "    %s\n",         formatheaderline(Header[2]));
    /* output the - separator, with . (corner) before it */
    fprintf(Outfile, "   .%s\n",         formatheaderline(Header[3])); }

  if (OptDecoration == OptShortDecoration) {
    /* short stats, just column+slice number and then data without headers */

    lutpos *Pos;

    /* LSB column+slice, because it is in first slice */
    firstlut(); Pos = getlutpos(); nextsli();
    fprintf(Outfile, "%03i%1c", Pos->Col, Pos->Sli);
    /* MSB column+slice, because it is in last slice */
    /* MSB row (no LUT, as LUTRAM32) */
    lastlut(); Pos = getlutpos(); prevsli();
    fprintf(Outfile, "+%03i%1c/%03i", Pos->Col, Pos->Sli, Pos->Row);
    /* LSB row (no LUT, as LUTRAM32) */
    firstlut();  Pos = getlutpos();
    fprintf(Outfile, "-%03i:\n", Pos->Row); }

  for (MemoryAddr = 0; MemoryAddr < 32; MemoryAddr++) {
    if (OptDecoration == OptLongDecoration) {
      /* number addresses */
      fprintf(Outfile, "%c%02i|", (MemoryAddr<16) ? 'F' : 'G', MemoryAddr); }
    fprintf(Outfile, "%s\n", formatmemoryword(Memory[MemoryAddr])); }

  if (OptDecoration == OptLongDecoration) {
    /* footer which completes the header */
    fprintf(Outfile, "memory address\n"); }


  for (HeaderLine = 0; HeaderLine < MEMORYZZHEADERLINES; HeaderLine++) {
    free(Header[HeaderLine]); }

  for (MemoryAddr = 0; MemoryAddr < 32; MemoryAddr++) {
    free(Memory[MemoryAddr]); } }


/* ------ format memory modes output section */

/* shared data, set by setup, used by the 2 formatters */
char FormatChar;
int ByteLength, DigitsPerByte;

/* shared output buffers used by the 2 formatters */
/*   widest memory is XCV3200 (104 CLBs * 2bit) or (26 BRAMs * 16bit) */
/*   even with one separ space per bit and \0 that is max 26*16*2+1 = 833 */
char ByteSplit[833], BaseConvert[833];


void setformat(char *Base, int WordLength) {

  /* set users demanded formatting type */
  /* 'a' = printable ASCII or named ASCII, as in "od" dump program */
  if      (*Base == 'a') { FormatChar = *Base++; }

  /* 'b' = binary, this is not available in "od" */
  else if (*Base == 'b') { FormatChar = *Base++; }

  /* 'c'= printable ASCII or backslashed or octal, as in "od" */
  else if (*Base == 'c') { FormatChar = *Base++; }

  /* 'd' = decimal (signed), as in "od" */
  else if (*Base == 'd') { FormatChar = *Base++; }

  /* 'h' = hex, more obvious way to write than the 'x' in "od" */
  else if (*Base == 'h') { FormatChar = 'x'; Base++; }

  /* 'o' = octal, as in "od" */
  else if (*Base == 'o') { FormatChar = *Base++; }

  /* 'u' = unsigned decimal, as in "od" */
  else if (*Base == 'u') { FormatChar = *Base++; }

  /* 'x' = hex, as in "od" */
  else if (*Base == 'x') { FormatChar = *Base++; }

  /* none, default do 'b' (binary), as this is like no formatting at all */
  else                   { FormatChar = 'b'; }


  /* set users demanded byte width */
  /* 'C' = character = 8bit */
  if      (*Base == 'C')   { ByteLength = 8;        Base++; }

  /* 'I' = interger = word width */
  else if (*Base == 'I')   { ByteLength = WordLength; Base++; }

  /* 'L' = long = 32bit */
  else if (*Base == 'L')   { ByteLength = 32;       Base++; }

  /* 'S' = short = 16bit */
  else if (*Base == 'S')   { ByteLength = 16;       Base++; }

  /* numeric */
  else if (isdigit(*Base)) { ByteLength = (int)strtoul(Base, &Base, 10); }

  /* none, use defaults */
  else {
    /* char format is usually individual characters, default 8bit/char */
    if (FormatChar == 'a' || FormatChar == 'c') { ByteLength = 8; }
    /* default byte length is memory width, as like no formatting at all */
    else                                        { ByteLength = WordLength; } }


  if (*Base != '\0') {
    fprintf(stderr, "%s: Error: unknown char in base type at: %s\n\n",
      MainProgName, Base);
    exit(1); }

  if (ByteLength > WordLength) {
    fprintf(stderr, "%s: Error: byte longer than word is nonsense: %i>%i\n",
      MainProgName, ByteLength, WordLength);
    exit(1); }

  if (FormatChar == 'a' && ByteLength < 7) {
    fprintf(stderr, "%s: Error: byte too short to contain ASCII: %i<7\n",
      MainProgName, ByteLength);
    exit(1); }

  if (FormatChar == 'c' && ByteLength < 8) {
    fprintf(stderr, "%s: Error: byte too short to contain char: %i<8\n",
      MainProgName, ByteLength);
    exit(1); }

  /* use of sprintf and "ulong" sets limit, alternative lots of code */
  if (FormatChar != 'b' && ByteLength > 32) {
    fprintf(stderr, "%s: Error: byte to long to convert: %i>32 (unsig long)\n",
      MainProgName, ByteLength);
    exit(1); }


  /* calculate how many chars space we need for displaying an byte */
  if (FormatChar == 'a' || FormatChar == 'c') {
    /* ASCII name or octal value need 3, backslash 2, ASCII char 1 */
    DigitsPerByte = 3; }

  else if (FormatChar == 'b') {
    DigitsPerByte = ByteLength; }

  else if (FormatChar == 'o') {
    int BitsPerDigit = 3;
    DigitsPerByte = ByteLength/3;
    /* if no exact fit, / rounds downwards, we want upward for partial digit */
    if (DigitsPerByte*3 < ByteLength) {
      DigitsPerByte++; } }

  else if (FormatChar == 'd' || FormatChar == 'u') {
    /* for decimal we can only use an table lookup */
    /*   it would be add 1 every ln10/ln2 = 3.3219 steps */
    /*   no I am not calculating ln10/ln2 in integer, *shudder* */
    /*   table for 1-32 bit wide bytes, larger are not converted anyway */
    int DecimalDigitsPerByte[] = { 1,1,1,2,2,2,3,3, 3,4,4,4,4,5,5,5,
                                   6,6,6,7,7,7,7,8, 8,8,9,9,9,10,10,10 };
    DigitsPerByte = DecimalDigitsPerByte[ByteLength-1];
    /* if 'd' signed decimal, add one for sign space */
    if (FormatChar == 'd') {
      DigitsPerByte++; } }

  else if (FormatChar == 'x') {
    DigitsPerByte = ByteLength/4;
    if (DigitsPerByte*4 < ByteLength) {
      DigitsPerByte++; } } }


/* Header must point to string, one char per bit */

char *formatheaderline(char *HeaderLine) {

  char *From, *To;
  int HeaderWidth = strlen(HeaderLine);
  From = HeaderLine; To = ByteSplit;

  /* header insert identical spaces between bytes, copy rest */
  while (From+ByteLength < HeaderLine+HeaderWidth) {
    strncpy(To, From, ByteLength);
    From += ByteLength; To += ByteLength;
    *To++ = ' '; }

  /* remaining bits, less than ByteLength */
  if (From < HeaderLine+HeaderWidth) {
    strncpy(To, From, HeaderLine+HeaderWidth-From);
    To += HeaderLine+HeaderWidth-From; }
  /* else undo blank after last byte */
  else {
    To--; }

  *To = '\0';


  /* header, copy header of 1st address in bit group, that makes one char */
  if (FormatChar != 'b') {

    int BitsPerDigit, BitsInFirstDigit;

    /* integer division rounds down if incomplete digit at front of byte */
    BitsPerDigit = ByteLength/DigitsPerByte;
    if (BitsPerDigit*DigitsPerByte < ByteLength) {
      /* correct this into rounding up, so we have true value */
      BitsPerDigit++; }

    if (BitsPerDigit*DigitsPerByte > ByteLength) {
      BitsInFirstDigit = ByteLength-(DigitsPerByte-1)*BitsPerDigit; }
    else {
      BitsInFirstDigit = BitsPerDigit; }

    HeaderWidth = strlen(ByteSplit);
    From = ByteSplit; To = BaseConvert;

    while (From < ByteSplit+HeaderWidth) {

      /* for the first digit, the possibly reduced size */
      int BitsInThisDigit = BitsInFirstDigit;

      while (From < ByteSplit+HeaderWidth && *From != ' ') {

        *To++ = *From; From += BitsInThisDigit;

        /* for later digits in this byte, allways full size */
        BitsInThisDigit = BitsPerDigit; }

      /* copy the space after non-last byte */
      if (*From == ' ') {
        *To++ = *From++; } }

    *To = '\0';

    return (BaseConvert); }

  return (ByteSplit); }


/* Memory must point to string, one char 0/1 per bit */

char *formatmemoryword(char *MemoryWord) {

  char *From, *To;
  int MemoryWidth = strlen(MemoryWord);
  From = MemoryWord; To = ByteSplit;

  /* separate bytes with spaces */
  while (From+ByteLength < MemoryWord+MemoryWidth) {
    strncpy(To, From, ByteLength);
    From += ByteLength; To += ByteLength;
    *To++ = ' '; }

  /* remaining bits, less than ByteLength */
  if (From < MemoryWord+MemoryWidth) {
    strncpy(To, From, MemoryWord+MemoryWidth-From);
    To += MemoryWord+MemoryWidth-From; }
  /* else undo blank after last byte */
  else {
    To--; }

  *To = '\0';


  /* non-binary, so convert binary number to other forms */
  if (FormatChar != 'b') {

    char FormatString[10];

    MemoryWidth = strlen(ByteSplit);
    From = ByteSplit; To = BaseConvert;

    while (From < ByteSplit+MemoryWidth) {

      ulong Byte = strtoul(From, &From, 2);

      if (FormatChar == 'a') {
        /* only one 7bit character/byte, multi lot of work, unlikely used */
        Byte &= 0x7F;
        /* control chars converted to names, upper case here, "od" lower */
        if      (Byte < 0x20) {
          char *Ctrl[] = {
            "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
            " BS", " HT", " LF", " VT", " FF", " CR", " SO", " SI",
            "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYM", "ETB",
            "CAN", " EM", "SUB", "ESC", " FS", " GS", " RS", " US" };
          To += sprintf(To, "%3s", Ctrl[Byte]); }
        else if (Byte == 0x7F) {
          To += sprintf(To, "DEL");  }
        else {
          To += sprintf(To, "  %c", Byte); } }

      else if (FormatChar == 'c') {
        /* only one 8bit character/byte, multi lot of work, unlikely used */
         Byte &= 0xFF;
        /* control chars converted to names, else they kill terminal */
        if      (Byte < 0x20) {
          char *Ctrl[] = {
            " \\0","001", "002", "003", "004", "005", "006", " \\a",
            " \\b"," \\t"," \\n"," \\v"," \\f"," \\r","016", "017",
            "020", "021", "022", "023", "024", "025", "026", "027",
            "030", "031", "032", "033", "034", "035", "036", "037" };
          To += sprintf(To, "%3s", Ctrl[Byte]); }
        else if (Byte < 0x7F){
          To += sprintf(To, "  %c", Byte); }
        else if (Byte == 0x7F) {
          To += sprintf(To, "DEL");  }
        else {
          To += sprintf(To, "%03o", Byte); } }

      else if (FormatChar == 'b') {
        sprintf(FormatString, "%%0%ib", DigitsPerByte);
        To += sprintf(To, FormatString, Byte); }

      else if (FormatChar == 'd') {
        sprintf(FormatString, "%% %id", DigitsPerByte);
        /* sign extend to 32bit, so 'd' in FormatString works */
        Byte |= Byte >> (ByteLength-1) ? (0xFFFFFFFF << ByteLength) : 0;
        To += sprintf(To, FormatString, Byte); }

      else if (FormatChar == 'o') {
        sprintf(FormatString, "%%0%io", DigitsPerByte);
        To += sprintf(To, FormatString, Byte); }

      else if (FormatChar == 'u') {
        sprintf(FormatString, "%%%iu", DigitsPerByte);
        To += sprintf(To, FormatString, Byte); }

      else if (FormatChar == 'x') {
        sprintf(FormatString, "%%0%ix", DigitsPerByte);
        To += sprintf(To, FormatString, Byte); }

      if (*From == ' ') {
        *To++ = *From++; } }

    *To = '\0';

    return (BaseConvert); }

  return (ByteSplit); }

