/* http://neil.franklin.ch/Projects/VirtexTools/vv.c */
/*   - VirtexView, graphical view of LUTs and BRAMdatas */
/*     and entire GCLKs/CLBs/IOBs/BRAMs/DLLs from Virtex bitstreams */
/*     presently only CLBs (LUTs and entire CLBs) implemented */
/* author Neil Franklin, last modification 2002.10.07 */


/* ------ 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 */

#include "virtex.h"


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

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

void getoptions(char *argv[]);
void helpoptions(int error);

void viewoutfile(char *OutfileName, int Verbose);

void viewclbs(void);
void viewheader(void);
void viewline(void);
void viewlineas4x4(uint *LutLine, int Count, int Decor);
void viewhorizseparator(void);

void outpicheader(int PicWidth, int PicHight);
void outpicfooter(void);
void outpicline(char *PicLine);

void viewentireclbs(void);
void viewentireheader(void);
void viewentireline(void);
void viewentirelinelayouted(char PicLine[]);
void viewentirehorizseparator(void);


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

char *MainProgName;

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

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

#define MainNoVerbose 0
#define MainDoVerbose 1
int MainVerbose = MainNoVerbose;

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

  /* straight forward, really :-) */

  char *serror;

  getoptions(argv);

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

  viewoutfile(MainOutfileName, MainVerbose == MainDoVerbose);

  exit(0); }


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

#define OptNoEntire 0
#define OptDoEntire 1
int OptEntire = OptNoEntire;

#define OptNormalLayout 0
#define OptTrueLayout 1
#define OptFoldedLayout 2
int OptLayout = OptNormalLayout;

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

#define OptBitmapFormat 0
#define OptAsciiFormat 1
int OptFormat = OptBitmapFormat;

/* NULL for no range given, use default complete chip */
char *OptRange = NULL;

/* NULL for no colour given, use default colours in outpicheader() */
char *OptColour = NULL;

/* NULL for no zoom given, use default 2x2 for LUTs or 1x1 for entire */
char *OptZoom = NULL;

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 == 'e') {
        /* -e (entire bits) */
        OptEntire = OptDoEntire; }

      else if (*option == 't') {
        /* -t (true layout), implies -e (entire bits) */
        OptEntire = OptDoEntire;
        OptLayout = OptTrueLayout; }

      else if (*option == 'f') {
        /* -f (folded layout), implies -e (entire bits) */
        OptEntire = OptDoEntire;
        OptLayout = OptFoldedLayout; }

      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 == 'a') {
        /* -a (ASCII format) */
        OptFormat = OptAsciiFormat; }

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

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

      else if (*option == 'p') {
        /* -p (paint): next argument must be colouring description, take it */
        argv++;
        OptColour = *argv; }

      else if (*option == 'z') {
        /* -z (zoom): next argument must be zoom factor, take it */
        argv++;
        OptZoom = *argv; }

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

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

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

    argv++; }

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

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

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

  return; }


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

void helpoptions(int error) {

  FILE *Output;

  if (error) {
    Output = stderr; }
  else {
    Output = stdout; }

  fprintf(Output,
    "Usage: %s [-etflsqavh] [-r range] [-z factor] [-p colour] [-o file] [bitfile]\n",
    MainProgName);
  fprintf(Output, "\n");
  fprintf(Output, "Options:\n");
  fprintf(Output,
    "  -e            entire element: CLB as bit pattern, not just LUTs\n");
  fprintf(Output,
    "  -et or -t     entire true layout: spaced with 3 dummy lines/line\n");
  fprintf(Output,
    "  -ef or -f     entire folded layout: folded pairs of hor pixels vert\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,
    "  -a            ASCII format: produce ASCII graphic (default bitmap)\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,
    "  -p colour     paint: with colour targetRGB[targetRGB...] for bitmap\n");
  fprintf(Output,
    "                target: o ones, z zeros, t text, b t back, s separtor\n");
  fprintf(Output,
    "                RGB: 3 digit octal value 000 - 777, red/green/blue\n");
  fprintf(Output,
    "  -z factor     zoom: 1 logical pixel to factor physical (def 2x2)\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");

  /* and abort, do not run actual program, neither after -h nor after error */
  exit(error); }


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

FILE *Outfile;

void viewoutfile(char *OutfileName, int Verbose) {

  char *serror;
  int ierror;

  if (OutfileName) {
    if (Verbose) {
      fprintf(stderr, "viewing to outfile %s\n", OutfileName); }
    Outfile = fopen(OutfileName, "w");
    if (!Outfile) {
      fprintf(stderr, "%s: %s: %s\n",
        MainProgName, strerror(errno), OutfileName);
      exit(1); } }
  else {
    if (Verbose) {
      fprintf(stderr, "viewing to standard output\n"); }
    Outfile = stdout; }

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

  /* select what rage we want to see viewed */
  if (Verbose) {
    fprintf(stderr, "setting range for viewing CLBs\n"); }
  serror = setclbrange(OptRange);
  if (serror != NULL) {
    fprintf(stderr, "%s: %s\n", MainProgName, serror);
    exit(1); }

  /* do the actual viewing */
  /*  select type of viewing, from more special to least special */
  if (OptEntire == OptDoEntire) {
    if (Verbose) {
      fprintf(stderr, "viewing entire CLB data\n"); }
    viewentireclbs(); }
  else {
    if (Verbose) {
      fprintf(stderr, "viewing LUT data\n"); }
    viewclbs(); }

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

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


/* ------ view CLBs to outfile section */

/* arrangement of graphic, for each CLBs 2x2 LUTs of 4x4 bits */
/* Slice Slice         */
/* L(=1) R(=0)         */
/* ----------.         */
/* 0123  0123|  G LUTs */
/* 4567  4567|         */
/* 89AB  89AB|         */
/* CDEF  CDEF|         */
/*           |         */
/* 0123  0123|  F LUTs */
/* 4567  4567|         */
/* 89AB  89AB|         */
/* CDEF  CDEF|         */

int Slis, Luts;

void viewclbs(void) {

  /* *Row and *Lut are extern global vars, from libvirtex */
  /*   First*, Last*, Begin* and End* have been set by setclbrange() */

  /* size of picture we are making */
  Slis = (LastCol-FirstCol+1)*2;
  if (FirstSli == SLIR || LastSli == SLIL) {
    /* slices have hab been cut down to 1 slice per column */
    Slis /= 2; }
  else {
    if (BeginSli == SLIR) {
      /* first column only 1 slice */
      Slis -= 1; }
    if (EndSli == SLIL) {
      /* last column only 1 slice */
      Slis -= 1; } }
  Luts = (LastRow-FirstRow+1)*2;
  if (FirstLut == LUTG || LastLut == LUTF) {
    /* LUTs have hab been cut down to 1 LUT per row */
    Luts /= 2; }
  else {
    if (BeginLut == LUTG) {
      /* first row only 1 LUT */
      Luts -= 1; }
    if (EndLut == LUTF) {
      /* last row only 1 LUT */
      Luts -= 1; } }

  /* add space for decoration, using pseudo-LUTs with LutFont[] in them */
  if (OptDecoration == OptLongDecoration) {
    /* 3 chars Col or Row, 1 char Sli or Lut, 1 char separator '-' or '|' */
    Slis += 5;
    Luts += 5; }
  else if (OptDecoration == OptShortDecoration) {
    /* just add one line of text */
    Luts += 1; }

  /* picture pixel size we will produce */
  outpicheader(Slis*(16/4+1)-1, Luts*(4+1)-1);

  viewheader();

  /* draw using Row/Lut for each output line, rows/LUTs from top to bottom */
  for (Row = LastRow; Row >= FirstRow; Row--) {
    for (Lut = LastLut; Lut >= FirstLut; Lut--) {
      if (!(Row == FirstRow && Lut == LUTF && BeginLut == LUTG ||
	    Row == LastRow  && Lut == LUTG && EndLut   == LUTF)) {
        viewline(); } } }

  outpicfooter(); }


/* all 5 or 1 or 0 horizontal lines of header */

void viewheader(void) {

  if (OptDecoration == OptLongDecoration) {
    /* output Col/Sli address, then an '-', so 5 pseudo-LUTs high */
    int Line;
    for (Line = 0; Line < 5; Line++) {

      uint LutLine[Slis], *LutVal = LutLine;

      /* space to output Row/Lut address in the actual LUT data rows */
      *LutVal++ = 0x0000; *LutVal++ = 0x0000;
      *LutVal++ = 0x0000; *LutVal++ = 0x0000;
      /* put a corner between the '-' and '|' characters, else spaces */
      *LutVal++ = (Line == 4) ? 0x4C00 : 0x0000;

      /* Col/Sli also from globals from libvirtex */
      for (Col = FirstCol; Col <= LastCol; Col++) {
        for (Sli = FirstSli; Sli <= LastSli; Sli++) {
          if (!(Col == FirstCol && Sli == SLIL && BeginSli == SLIR ||
                Col == LastCol  && Sli == SLIR && EndSli   == SLIL)) {

            char Address[6];
            sprintf(Address, "%03i%c-", Col, (Sli == SLIR) ? 'R' : 'L');
            *LutVal++ = LutFont[Address[Line]]; } } }
      viewlineas4x4(LutLine, Slis, Slis);

      /* allways data lines following, allways output an separator */
      viewhorizseparator(); } }

  else if (OptDecoration == OptShortDecoration) {
    /* only one line, no looping */
    char Range[20], HeaderLine[Slis], *HeaderChar;
    uint LutLine[Slis], *LutVal = LutLine;

    sprintf(Range, "%03i%1c-%03i%1c/%03i%1c-%03i%1c",
            FirstCol, (FirstSli == SLIR) ? 'R' : 'L',
            LastCol,  (LastSli == SLIR)  ? 'R' : 'L',
            FirstRow, (FirstLut == LUTG) ? 'G' : 'F',
            LastRow,  (LastLut == LUTG)  ? 'G' : 'F');

    if (Slis < strlen(Range)) {
      /* chop off range string to fit, use '\' to show it has been chopped */
      Range[Slis] = '\0';
      Range[strlen(Range)-1] = '\\'; }

    memset(HeaderLine, ' ', Slis);
    strncpy(HeaderLine, Range, strlen(Range));

    for (HeaderChar = HeaderLine; HeaderChar < HeaderLine+Slis; HeaderChar++) {
      *LutVal++ = LutFont[*HeaderChar]; }
    viewlineas4x4(LutLine, Slis, Slis);

    viewhorizseparator(); } }


/* one Row/Lut indexed horizontal line of LUTs */

void viewline(void) {

  uint LutLine[Slis], *LutVal = LutLine, DecorLuts = 0;

  if (OptDecoration == OptLongDecoration) {
    /* output Row/Lut address, 5 pseudo-LUTs wide */
    char Address[6], *Char;
    sprintf(Address, "%03i%c|", Row, (Lut == LUTG) ? 'G' : 'F');
    for (Char = Address; Char < Address+5; Char++) {
      *LutVal++ = LutFont[*Char]; }
    DecorLuts = 5; }

  /* Col/Sli also from globals from libvirtex */
  for (Col = FirstCol; Col <= LastCol; Col++) {
    for (Sli = FirstSli; Sli <= LastSli; Sli++) {
      if (!(Col == FirstCol && Sli == SLIL && BeginSli == SLIR ||
            Col == LastCol  && Sli == SLIR && EndSli   == SLIL)) {

        *LutVal++ = getlut(); } } }

  viewlineas4x4(LutLine, Slis, DecorLuts);

  /* if another LUT is to be viewed, output an separating line */
  if (!(Row == FirstRow && Lut == BeginLut)) {
  viewhorizseparator(); } }


/* convert 1 line of Count LUTs into 4 picture lines of 4+1 pixels per LUT */

void viewlineas4x4(uint *LutLine, int Count, int Decor) {

  int BitQuartet;
  for (BitQuartet = 0; BitQuartet < 16; BitQuartet+=4) {

    char PicLine[Count*(4+1)-1+1], *Pixel = PicLine;

    uint *Lut;
    for (Lut = LutLine; Lut < LutLine+Count; Lut++) {

      int BitNo;
      for (BitNo = BitQuartet; BitNo < BitQuartet+4 ; BitNo++) {
        /* if still a decoration LUT colour it decoration colour */
        if (Lut < LutLine+Decor) {
          *Pixel++ = ((*Lut)&(1<<BitNo)) ? '3' : '2'; }
        else {
          *Pixel++ = ((*Lut)&(1<<BitNo)) ? '1' : '0'; } }

      /* if another LUT is to be viewed, output an vertical separating space */
      if (!(Lut == LutLine+Count-1)) {
        *Pixel++ = ' '; } }

    *Pixel = 0;
    outpicline(PicLine); } }


/* output line of LUT times 4+1 pixels wide horizontal separator */

void viewhorizseparator(void) {

  char PicLine[Slis*(4+1)-1+1];
  memset(PicLine, ' ', Slis*(4+1)-1);
  PicLine[Slis*(4+1)-1] = 0;
  outpicline(PicLine); }


/* ------ output an line of pixels section */

/* this is used by the ASCII graphic output */
char OutAscii[128];

/* this is used by the bitmap graphic output */
char OutPpmColour[128][7];
#define PpmCharsPerPixel 6
int OutPicWidth, OutPicHight;
int ZoomWidth, ZoomHight;
#define PpmLineWidth 79

void outpicheader(int PicWidth, int PicHight) {

  OutPicWidth = PicWidth;
  OutPicHight = PicHight;

  if (OptFormat == OptAsciiFormat) {
    /* set output translation of logical pixels to physical chars */
    /* do not output ' ' for anything other than the ' ' separator */
    /*   else the 1->2 space expander screws up the output format */
    OutAscii['0'] = '.';
    OutAscii['1'] = '1';
    OutAscii['2'] = '.';
    OutAscii['3'] = '#';
    OutAscii[' '] = ' '; }

  else {
    /* allow colouring of bitmap graphics, set default */
    /*   the various colours, as RGB components as 0..7, giving 8*8*8 = 512 */
    strcpy(OutPpmColour['0'], "0 0 7 "); /* blue */
    strcpy(OutPpmColour['1'], "0 7 7 "); /* cyan */
    strcpy(OutPpmColour['2'], "3 3 3 "); /* gray */
    strcpy(OutPpmColour['3'], "7 7 7 "); /* white */
    strcpy(OutPpmColour[' '], "0 0 0 "); /* black */
    /* allow colouring of bitmap graphics, parse user input if any */
    if (OptColour != NULL) {
      char *Colour = OptColour;
      while (*Colour != '\0') {
        int ColourNumber;
        if      (*Colour == 'z') { /* zeros */
          Colour++;
          /* input 3-digit octal number, extract digits, store as chars */
          ColourNumber = (int)strtoul(Colour, &Colour, 8);
          sprintf(OutPpmColour['0'], "%o %o %o ",
            (ColourNumber>>6)&07, (ColourNumber>>3)&07, ColourNumber&07); }
        else if (*Colour == 'o') { /* ones */
          Colour++;
          ColourNumber = (int)strtoul(Colour, &Colour, 8);
          sprintf(OutPpmColour['1'], "%o %o %o ",
            (ColourNumber>>6)&07, (ColourNumber>>3)&07, ColourNumber&07); }
        else if (*Colour == 'b') { /* text background */
          Colour++;
          ColourNumber = (int)strtoul(Colour, &Colour, 8);
          sprintf(OutPpmColour['2'], "%o %o %o ",
            (ColourNumber>>6)&07, (ColourNumber>>3)&07, ColourNumber&07); }
        else if (*Colour == 't') { /* text */
          Colour++;
          ColourNumber = (int)strtoul(Colour, &Colour, 8);
          sprintf(OutPpmColour['3'], "%o %o %o ",
            (ColourNumber>>6)&07, (ColourNumber>>3)&07, ColourNumber&07); }
        else if (*Colour == 's') { /* separators */
          Colour++;
          ColourNumber = (int)strtoul(Colour, &Colour, 8);
          sprintf(OutPpmColour[' '], "%o %o %o ",
            (ColourNumber>>6)&07, (ColourNumber>>3)&07, ColourNumber&07); }
        else {
          fprintf(stderr, "%s: Error: unknown char in colour spec at: %s\n",
            MainProgName, Colour);
          exit(1); } } }

    /* allow zooming of bitmap graphics, set default */
    if (OptEntire == OptDoEntire) {
      ZoomWidth = 1; ZoomHight = 1; }
    else {
      ZoomWidth = 2; ZoomHight = 2; }

    /* allow zooming of bitmap graphics, parse user input if any */
    if (OptZoom != NULL) {
      char *Zoom = OptZoom;
      ZoomHight = ZoomWidth = (int)strtoul(Zoom, &Zoom, 10);
      if (*Zoom == 'x') {
        Zoom++;
        ZoomHight = (int)strtoul(Zoom, &Zoom, 10); }
      if (*Zoom != 0) {
        fprintf(stderr, "%s: Error: unknown char in zoom factor at: %s\n",
          MainProgName, Zoom);
        exit(1); }
      if (ZoomWidth > PpmLineWidth/PpmCharsPerPixel) {
        fprintf(stderr, "%s: Error: horizontal zoom factor too large: %i>%i\n",
          MainProgName, ZoomWidth, PpmLineWidth/PpmCharsPerPixel);
        exit(1); } }

    /* use .ppm format, is simple and convertable by ImageMagick or netpbm */
    /* is a P3 (= .ppm) file */
    fprintf(Outfile, "P3\n");
    fprintf(Outfile, "%i %i\n", ZoomWidth*OutPicWidth,
                                ZoomHight*OutPicHight);
    /* 3bit (0..7) per pixel channel, 8*8*8 = 512 colours possible */
    fprintf(Outfile, "7\n"); } }


void outpicfooter(void) {
  /* presently do nothing, just for call list completeness */; }


/* output one line of pixels, line delivered as string PicLine, 1 char/pixel */
/* '0' draw an 0 bit, '1' draw an 1 bit, ' ' draw an vertical separator */

void outpicline(char *PicLine) {

  if (OptFormat == OptAsciiFormat) {
    char *Pixel, OnlySpace = ' ';
    /* format chars for better visibility of bit pattern */
    for (Pixel = PicLine; *Pixel != 0; Pixel++) {
      OnlySpace |= *Pixel;
      /* convert pixel values to output chars */
      *Pixel = OutAscii[*Pixel]; }
    if (OnlySpace == ' ') {
      /* only spaces, is an horizontal separator, just output an empty line */
      fprintf(Outfile, "\n");
      return; }
    /* expand to 2 spaces for each vertical separator (one space) */
    while (1) {
      char *FirstVertSep = strchr(PicLine, ' ');
      if (FirstVertSep != NULL) {
        int BlockLength = FirstVertSep-PicLine;
        char *Block = malloc(BlockLength+1);
        strncpy(Block, PicLine, BlockLength);
        Block[BlockLength] = 0;
        fprintf(Outfile, "%s  ", Block);
        free(Block);
        PicLine = FirstVertSep+1; }
      else {
        fprintf(Outfile, "%s\n", PicLine);
        return; } } }

  else {
    int StepHight, StepWidth;
    for (StepHight = 0; StepHight < ZoomHight; StepHight++) {
      /* OutputSect can hold up to PpmLineWidth chars and '\0' wide */
      /* max PpmLineWidth chars with ZoomWidth*PpmCharsPerPixel per Pixel */
      char *Sect;
      int PixelPerSect = PpmLineWidth/(ZoomWidth*PpmCharsPerPixel);
      for (Sect = PicLine; Sect < PicLine+OutPicWidth; Sect+=PixelPerSect) {
        char *Pixel, OutputSect[PpmLineWidth+1], *Output = OutputSect;
        for (Pixel = Sect; Pixel < Sect+PixelPerSect && *Pixel != 0; Pixel++) {
          for (StepWidth = 0; StepWidth < ZoomWidth; StepWidth++) {
            /* convert pixel values to colour strings and add to output */
            strcpy(Output, OutPpmColour[*Pixel]);
            Output += PpmCharsPerPixel; } }
        *Output = 0;
        fprintf(Outfile, "%s\n", OutputSect); } } } }


/* ------ view entire CLBs to outfile section */

int LeaderSpace, Cols, Rows;

void viewentireclbs(void) {

  int Header = 0;
  LeaderSpace = 0;

  /* the "entire" mode simply ignores *Sli and *Lut variables */
  /*   as all 4 quaters of an CLB are in same CLB */

  /* size of picture we are making */
  Cols = LastCol-FirstCol+1;
  Rows = LastRow-FirstRow+1;

  /* add space for decoration, using pseudo-LUTs with LutFont[] in them */
  if (OptDecoration == OptLongDecoration) {
    /* add header (top) and leader (left) */
    Header = 1; LeaderSpace = 4+2; }
  else if (OptDecoration == OptShortDecoration) {
    /* add header */
    Header = 1; }

  /* picture pixel size we will produce */
  if (OptLayout == OptFoldedLayout) {
    outpicheader(Cols*(VirtexClbFrames/2+2/2)-2/2 + LeaderSpace/2,
                 Rows*(VirtexMiddleBits*2+2)-2 + Header*(4+2)); }
  else if (OptLayout == OptTrueLayout) {
    outpicheader(Cols*(VirtexClbFrames+2)-2 + LeaderSpace,
                 Rows*(VirtexMiddleBits*4+3)-3 + Header*(4+3)); }
  else {
    outpicheader(Cols*(VirtexClbFrames+2)-2 + LeaderSpace,
                 Rows*(VirtexMiddleBits+2)-2 + Header*(4+2)); }

  viewentireheader();

  /* draw using Row for each output line, rows from top to bottom */
  for (Row = LastRow; Row >= FirstRow; Row--) {
    viewentireline(); }

  outpicfooter(); }


/* one single horizontal line of header, 4 pixels high */

void viewentireheader(void) {

  if (OptDecoration == OptLongDecoration) {
    int PicWidth, ColStep, BitQuartet;

    PicWidth = LeaderSpace+Cols*(VirtexClbFrames+2)-2;
    ColStep = VirtexClbFrames;
    if (OptLayout == OptFoldedLayout) {
      /* folding halves the width available */
      PicWidth /= 2;
      ColStep /= 2; }

    for (BitQuartet = 0; BitQuartet < 16; BitQuartet+=4) {
      char HeaderLine[PicWidth+1], *Pixel = HeaderLine;

      /* fill entire line with text background, no separators */
      memset(HeaderLine, '2', PicWidth);

      /* space to output Row/Lut address in the actual CLB data rows */
      Pixel += 2;
      if (OptLayout != OptFoldedLayout) {
        Pixel += 2; }
      *Pixel++ = ' ';
      if (OptLayout != OptFoldedLayout) {
        *Pixel++ = ' '; }

      for (Col = FirstCol; Col <= LastCol; Col++) {
        char Address[4], *AddressChar;
 
        sprintf(Address, "%03i", Col);
 
        for (AddressChar = Address; AddressChar < Address+3; AddressChar++) {
          int BitNo;

          for (BitNo = BitQuartet; BitNo < BitQuartet+4 ; BitNo++) {
            *Pixel++ = (LutFont[*AddressChar]&(1<<BitNo)) ? '3' : '2'; }

          /* vertical separating space for next char, no separators */
          /*   here space (text background), not separator, separ missmatch */
          Pixel++; }

        /* if another Col to be shown, jump rest of space, then vert separ */
        if (!(Col == LastCol)) {
          Pixel += ColStep-(3*5);
          *Pixel++ = ' ';
          if (OptLayout != OptFoldedLayout) {
            *Pixel++ = ' '; } } }

      HeaderLine[PicWidth] = '\0';
      outpicline(HeaderLine); }

    viewentirehorizseparator(); }

  else if (OptDecoration == OptShortDecoration) {
    char Range[16], *RangeChar;
    int PicWidth, BitQuartet;
 
    sprintf(Range, "%03i-%03i/%03i-%03i",
                   FirstCol, LastCol, FirstRow, LastRow);

    PicWidth = Cols*(VirtexClbFrames+2)-2;
    if (OptLayout == OptFoldedLayout) {
      /* folding halves the width available */
      PicWidth /= 2; }

    if (PicWidth < strlen(Range)*(4+1)-1) {
      /* chop off range string to fit, use '\' to show it has been chopped */
      Range[(PicWidth+1)/(4+1)] = '\0';
      Range[strlen(Range)-1] = '\\'; }

    for (BitQuartet = 0; BitQuartet < 16; BitQuartet+=4) {
      char HeaderLine[PicWidth+1], *Pixel = HeaderLine;

      /* fill entire line with text background, no separators */
      memset(HeaderLine, '2', PicWidth);

      for (RangeChar = Range; RangeChar < Range+strlen(Range); RangeChar++) {

        int BitNo;
        for (BitNo = BitQuartet; BitNo < BitQuartet+4 ; BitNo++) {
          *Pixel++ = (LutFont[*RangeChar]&(1<<BitNo)) ? '3' : '2'; }

        /* if another char to be shown, leave an vertical separating space */
        if (!(RangeChar == Range+strlen(Range)-1)) {
          Pixel++; } }

      HeaderLine[PicWidth] = 0;
      outpicline(HeaderLine); }

    viewentirehorizseparator(); } }


/* one Row indexed horizontal line of CLBs */

void viewentireline(void) {

  int PicWidth = LeaderSpace+Cols*(VirtexClbFrames+2)-2;

  char Address[4];
  sprintf(Address, "%03i", Row);

  /* Bit and Frame are extern global vars, from libvirtex */
  for (Bit = 0; Bit < VirtexMiddleBits; Bit++) {

    char PicLine[PicWidth+1], *Pixel = PicLine;

    if (LeaderSpace == 4+2) {
      if ((Bit/5)*5+4 == Bit || Bit >= 15) {
        /* line with no row address character on */
        *Pixel++ = '2'; *Pixel++ = '2';
        *Pixel++ = '2'; *Pixel++ = '2'; }
      else {
        int Char = Bit/5, BitQuartet = 4*(Bit-(Bit/5)*5), BitNo;
        for (BitNo = BitQuartet; BitNo < BitQuartet+4 ; BitNo++) {
          *Pixel++ = (LutFont[Address[Char]]&(1<<BitNo)) ? '3' : '2'; } }

      *Pixel++ = ' '; *Pixel++ = ' '; }

    for (Col = FirstCol; Col <= LastCol; Col++) {
      for (Frame = 0; Frame < VirtexClbFrames; Frame++) {
        *Pixel++ = getclb() ? '1' : '0'; }

      /* if another column is to be viewed, print 2 separating spaces */
      if (!(Col == LastCol)) {
        *Pixel++ = ' '; *Pixel++ = ' '; } }

    PicLine[PicWidth] = '\0';
    viewentirelinelayouted(PicLine); }

  /* if annother Row is to be viewed, print 3 separating lines */
  if (!(Row == FirstRow)) {
    viewentirehorizseparator(); } }


void viewentirelinelayouted(char PicLine[]) {

  if (OptLayout == OptFoldedLayout) {
    /* fold each line to 2 1/2 length lines, to convert 48x18 to 24x36 */
    /*   consequently also only 1 pixel vertical separator */
    /*   and the 1 line horizontal separator will become 2 lines */
    int PicWidth = LeaderSpace/2+Cols*(VirtexClbFrames/2+2/2)-2/2;
    char *Pixel;
    char FirstPicLine[PicWidth+1], *FirstPixel = FirstPicLine;
    char SecondPicLine[PicWidth+1], *SecondPixel = SecondPicLine;

    for (Pixel = PicLine; *Pixel != '\0'; Pixel++) {
      if (((Pixel-PicLine) & 1) == 0) { 
        /* first line recieves all the 0/2/4../46 frames */
        *FirstPixel++ = *Pixel; }
      else { 
        /* second line recieves all the 1/3/5../47 frames */
        *SecondPixel++ = *Pixel; } }

    FirstPicLine[PicWidth] = '\0';
    SecondPicLine[PicWidth] = '\0';
    outpicline(FirstPicLine);
    outpicline(SecondPicLine); }

  else if (OptLayout == OptTrueLayout) {
    /* add to each line 3 filling lines, to expand 48x18 to 48x72 */
    /*   and the 1 line horizontal separator will also become 4 lines */
    int PicWidth = LeaderSpace+Cols*(VirtexClbFrames+2)-2;
    outpicline(PicLine);
    memset(PicLine, ' ', PicWidth);
    PicLine[PicWidth] = '\0';

    outpicline(PicLine);
    outpicline(PicLine);
    outpicline(PicLine); }

  else {
    outpicline(PicLine); } }


/* output line of CLB times 48+2 pixels wide horizontal separator */

void viewentirehorizseparator(void) {

  int PicWidth = LeaderSpace+Cols*(VirtexClbFrames+2)-2;
  char PicLine[PicWidth+1];
  memset(PicLine, ' ', PicWidth);
  PicLine[PicWidth] = '\0';

  if (OptLayout == OptFoldedLayout) {
    /* 1 call, gets duplicated into 2 horiz lines when folding */
    viewentirelinelayouted(PicLine); }
  else if (OptLayout == OptTrueLayout) {
    /* 3 horiz lines so that with 2 vert the desired 2x3 format results */
    outpicline(PicLine); outpicline(PicLine); outpicline(PicLine); }
  else {
    /* 2 horiz lines only, as no 2x3 layout aimed for, go for equal width 2 */
    outpicline(PicLine); outpicline(PicLine); } }

