/* http://neil.franklin.ch/Projects/VirtexTools/vv.c */
/*   - VirtexView, graphic view 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.00.01 */


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

/* malloc(), 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 viewoutfile(FILE *OutfilePtr, int Verbose);

void setcolour(char *Colour);
void setzoom(char *Zoom);

void viewnormals(void);
void viewnormalsheader(void);
void viewnormalline(void);
void viewnormalline4x4(lutval *Luts, int LutCount, int DecorLuts, char Space);
void viewnormalhsepar(void);

void viewentires(void);
void viewentiresheader(void);
void viewentireline(void);
void viewentirelinelayouted(char *Pixels);
void viewentirehsepar(void);

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


/* ------ 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); }


  /* view data */

  if (MainOptOutfileName) {
    if (MainOptVerbose) {
      fprintf(stderr, "viewing 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, "viewing to standard output\n"); }
    Outfile = stdout; }

  viewoutfile(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
int OptMode = OptNormalMode;

#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;

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

/* default colour, overwrite with -p */
#define PixZero 'z'
#define PixOne  'o'
#define PixBack 'b'
#define PixText 't'
#define PixSep  's'
char *OptColour = "z007o077b333t777s000";
#define PpmCharsPerPixel 6
#define PpmLineWidth 79
char OptPpmColour[128][PpmCharsPerPixel+1];

/* default zoom for normal or entire, overwrite with -z */
char *OptNormalZoom = "2x2"; /* few bits, so enlarge */
char *OptEntireZoom = "1x1"; /* many bits, so small */
char *OptZoom;
int ZoomWidth, ZoomHight;


void getoptions(char *argv[]) {

  MainProgName = *argv++;

  OptZoom = OptNormalZoom;

  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;
        OptZoom = OptNormalZoom; }

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

      else if (*option == 't') {
        /* -t (true layout), implies -e (entire bits) */
        OptMode = OptEntireMode;
        OptZoom = OptEntireZoom;
        OptLayout = OptTrueLayout; }

      else if (*option == 'f') {
        /* -f (folded layout), implies -e (entire bits) */
        OptMode = OptEntireMode;
        OptZoom = OptEntireZoom;
        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) */
        MainOptVerbose++; }

      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++;
        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 [-etflsqavh] [-r range] [-p colours] [-z w[xh]] [-o file] [bitfile]\n",
    MainProgName);
  fprintf(Output,
    "\n");
  fprintf(Output,
    "Options:\n");
  fprintf(Output,
    "  -n            normal view: just LUT patterns (default)\n");
  fprintf(Output,
    "  -e            entire view: CLB as full bit pattern\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 colours    paint: bitmap with colours tRGB[tRGB...]\n");
  fprintf(Output,
    "                t: z zeros, o ones, b background, t text, s separtors\n");
  fprintf(Output,
    "                RGB: 3 digit octal value 000 - 777, red/green/blue\n");
  fprintf(Output,
    "                (default %s)\n", OptColour);
  fprintf(Output,
    "  -z w[xh]      zoom: 1 logical pixel to factor physical\n");
  fprintf(Output,
    "                w zooms both directions, wxh w width and h height\n");
  fprintf(Output,
    "                (default %s for normal view, %s for entire view)\n",
    OptNormalZoom, OptEntireZoom);
  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"); }


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

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

void viewoutfile(FILE *OutfilePtr, int Verbose) {

  char *serror;

  Outfile = OutfilePtr;

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

  /* select what colours we want */
  setcolour(OptColour);
  /* and what zoom factor we want */
  setzoom(OptZoom);

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

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

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


/* ------ parse colour option section */

/* allow colouring of bitmap graphics, parse default or -p user input */
void setcolour(char *Colour) {

  while (*Colour != '\0') {
    if ((*Colour == PixZero) || (*Colour == PixOne)  ||
        (*Colour == PixBack) || (*Colour == PixText) || (*Colour == PixSep)) {
      /* which colour entry we are changing */
      char ColourToSet = *Colour++;
      /* input 3-digit octal number, extract digits, store as chars */
      int ColourNumber = (int)strtoul(Colour, &Colour, 8);
      /* generate 6 char + '\0' format strings for printing to .ppm */
      sprintf(OptPpmColour[ColourToSet], "%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); } } }


/* ------ parse zoom option section */

/* allow zooming of bitmap graphics, parse user input if any */
void setzoom(char *Zoom) {

  ZoomWidth = (int)strtoul(Zoom, &Zoom, 10);
    ZoomHight = ZoomWidth;
  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); } }


/* ------ view LUTs to outfile section */

/* bit arrangement in 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|         */


/* size of the picture in LUTs, actual LUTs + decoration pseudo-LUTs */
int PicSlis, PicLuts;

void viewnormals(void) {

  /* before first row no separator ... */
  int Separator = 0;

  /* size of picture we are making */
  PicSlis = 0, PicLuts = 0;
  for (firstsli(); isinslis(); nextsli()) {
    PicSlis++; }
  for (firstlut(); isinluts(); nextlut()) {
    PicLuts++; }

  /* add space for decoration, using pseudo-LUTs with LutFont[] in them */
  if (OptDecoration == OptLongDecoration) {
    /* 3 chars column or row, 1 char slice or LUT, 1 char separat '-' or '|' */
    PicSlis += 5;
    PicLuts += 5; }
  else if (OptDecoration == OptShortDecoration) {
    /* just add one line of text at top */
    PicLuts += 1; }

  /* size of picture we will produce, 4 pixels per LUT, 1 separator between */
  outpicheader(PicSlis*(16/4+1)-1, PicLuts*(4+1)-1);

  viewnormalsheader();

  /* draw using row+LUT for each output line */
  /*   row+LUT counts from bottom to top, we want top to bottom, so reverse */
  for (lastlut(); isinluts(); prevlut()) {

    /* if already an row has been viewed, output an separating bar */
    if (Separator) {
      viewnormalhsepar(); }

    viewnormalline();

    /* ... then between rows view an empty line (as separator) */
    Separator = 1; }

  outpicfooter(); }


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

void viewnormalsheader(void) {

  if (OptDecoration == OptLongDecoration) {
    /* output column+slice address, then an '-' separ, so 5 pseudo-LUTs high */

    int Line;

    for (Line = 0; Line < 5; Line++) {

      lutval *Luts = malloc(PicSlis*sizeof(lutval)), *Lut = Luts;

      /* 4 chars space, to output row+LUT address in the actual data lines */
      *Lut++ = LutFont[' ']; *Lut++ = LutFont[' '];
      *Lut++ = LutFont[' ']; *Lut++ = LutFont[' '];
      /* 5th char, put corner between '-' and '|' characters, else space */
      /*   use an real corner (0x4C00) instead of ASCII art LutFont['.'] */
      *Lut++ = (Line == 4) ? 0x4C00 : LutFont[' '];

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

        lutpos *Pos = getlutpos(); char Address[6];
        sprintf(Address, "%03i%c-", Pos->Col, Pos->Sli);

        *Lut++ = LutFont[Address[Line]]; }

      viewnormalline4x4(Luts, PicSlis, PicSlis, PixSep);

      /* always data lines following, always output an separator */
      viewnormalhsepar();

      free(Luts); } }

  else if (OptDecoration == OptShortDecoration) {
    /* only one line, with range on it */

    lutpos *Pos = getlutpos();
    int TopRightCol, TopRightRow, BotLeftCol, BotLeftRow;
    char TopRightSli, TopRightLut, BotLeftSli, BotLeftLut;
    char Range[(4+1+4)+1+(4+1+4)+1];
    char *HeaderLine = malloc(PicSlis+1), *HeaderChar;
    lutval *Luts = malloc(PicSlis*sizeof(lutval)), *Lut = Luts;

    firstsli(); firstlut(); Pos = getlutpos();
    TopRightCol = Pos->Col; TopRightSli = Pos->Sli;
    TopRightRow = Pos->Row; TopRightLut = Pos->Lut;

    lastsli(); lastlut(); Pos = getlutpos();
    BotLeftCol = Pos->Col; BotLeftSli = Pos->Sli;
    BotLeftRow = Pos->Row; BotLeftLut = Pos->Lut;

    sprintf(Range, "%03i%1c-%03i%1c/%03i%1c-%03i%1c",
            TopRightCol, TopRightSli, BotLeftCol,  BotLeftSli,
            TopRightRow, TopRightLut, BotLeftRow,  BotLeftLut);

    /* how much space do we have, chop range string if not fitting */
    if (strlen(Range) > PicSlis) {
      /* chop off range string to fit in picture */
      Range[PicSlis] = '\0';
      /* use '\' as last char to show range string has been chopped */
      Range[PicSlis-1] = '\\'; }

    /* all blanks where no range */
    memset(HeaderLine, ' ', PicSlis);
    HeaderLine[PicSlis] = '\0';
    strncpy(HeaderLine, Range, strlen(Range));

    /* convert charakters to bitmaps, then output them as usual */
    for (HeaderChar = HeaderLine; *HeaderChar != '\0'; HeaderChar++) {
      *Lut++ = LutFont[*HeaderChar]; }

    /* for normal picture force separators to text background colour */
    /*   this gives a text bar, as chars not related to specific column */
    /*   but for ascii picture need standard separator, so it gets expanded */
    viewnormalline4x4(Luts, PicSlis, PicSlis,
      OptFormat == OptAsciiFormat ? PixSep : PixBack);

    viewnormalhsepar();

    free(Luts);
    free(HeaderLine); } }


/* view 1 horizontal line of LUTs, in 4 picture lines */

void viewnormalline(void) {

  lutval *Luts = malloc(PicSlis*sizeof(lutval)), *Lut = Luts;
  int DecorLuts = 0;

  if (OptDecoration == OptLongDecoration) {
    /* output row+LUT address, 5 pseudo-LUTs wide */
    lutpos *Pos = getlutpos(); char Address[6], *Char;
    sprintf(Address, "%03i%1c|", Pos->Row, Pos->Lut);

    for (Char = Address; Char < Address+5; Char++) {
      *Lut++ = LutFont[*Char]; }
    DecorLuts = 5; }

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

    *Lut++ = getlut(); }

  viewnormalline4x4(Luts, PicSlis, DecorLuts, PixSep);

  free(Luts); }


/* convert 1 line of LUTs to 4 picture lines of 4(+1) pixels per LUT */
/*   not possible to null-term Luts (0 is valid data), so use length count */

void viewnormalline4x4(lutval *Luts, int LutCount, int DecorLuts, char Space) {

  int FontQuartet, FontPixel;

  for (FontQuartet = 0; FontQuartet < 16; FontQuartet+=4) {

    char *Pixels = malloc(LutCount*(4+1)-1+1), *Pixel = Pixels;
    lutval *Lut;
    int Separator = 0;

    for (Lut = Luts; Lut < Luts+LutCount; Lut++) {

      /* if already an LUT has been viewed, output vertical separating space */
      if (Separator) {
        /* Space will be s = space in data and b = back in decoration */
        *Pixel++ = Space; }

      for (FontPixel = FontQuartet; FontPixel < FontQuartet+4; FontPixel++) {
        if (Lut < Luts+DecorLuts) {
          /* if decoration LUT, use decoration colour (t = text, b = back) */
          *Pixel++ = ((*Lut)&(1<<FontPixel)) ? PixText : PixBack; }
        else {
          /* else, use standard colours (o = 1 = set bit, z = 0 = clear bit) */
          *Pixel++ = ((*Lut)&(1<<FontPixel)) ? PixOne  : PixZero; } }

      Separator = 1; }

    *Pixel = 0;
    outpicline(Pixels);

    free(Pixels); } }


/* view 1 horizontal picture line of separator space */

void viewnormalhsepar(void) {

  char *Pixels = malloc(PicSlis*(4+1)-1+1);

  memset(Pixels, PixSep, PicSlis*(4+1)-1);
  Pixels[PicSlis*(4+1)-1] = 0;
  outpicline(Pixels);

  free(Pixels); }


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

int Cols, Rows, HeaderSpace, LeaderSpace;

void viewentires(void) {

  int Separator = 0;

  /* size of picture we are making */
  /* twice nextsli() or nextlut(), as only one view per CLB */
  /*   what happens with the slice or LUT index is irrelevant, as not used */
  Cols = 0; Rows = 0;
  for (firstsli(); isinslis(); nextsli(), nextsli()) {
    Cols++; }
  for (firstlut(); isinluts(); nextlut(), nextlut()) {
    Rows++; }

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

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

  viewentiresheader();

  /* draw using row for each output line */
  /*   row counts from bottom to top, we want top to bottom, so reverse */
  for (lastlut(); isinluts(); prevlut(), prevlut()) {

    /* if already an row has been viewed, output an separating bar */
    if (Separator) {
      viewentirehsepar(); }

    viewentireline();

    Separator = 1; }

  outpicfooter(); }


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

void viewentiresheader(void) {

  if (OptDecoration == OptLongDecoration) {
    /* output column address, 3 chars * 5 bits, above 48bits, so 1 line fits */

    int PicWidth = LeaderSpace+Cols*(VirtexClbFrames+2)-2;
    int ColStep = VirtexClbFrames;
    int FontQuartet, FontPixel;

    if (OptLayout == OptFoldedLayout) {
      /* folding halves the width available */
      PicWidth /= 2;
      ColStep /= 2; }

    for (FontQuartet = 0; FontQuartet < 16; FontQuartet+=4) {

      char *HeaderLine = malloc(PicWidth+1), *Pixel = HeaderLine;
      int Separator = 0;

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

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

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

        lutpos *Pos = getlutpos(); char Address[4], *AddressChar;

        /* if already an column has been viewed, jump rest space, then separ */
        if (Separator) {
          Pixel += ColStep-(3*5);
          *Pixel++ = PixSep;
          if (OptLayout != OptFoldedLayout) {
            *Pixel++ = PixSep; } }

        sprintf(Address, "%03i", Pos->Col);

        for (AddressChar = Address; AddressChar < Address+3; AddressChar++) {

          for (FontPixel = FontQuartet; FontPixel < FontQuartet+4;
               FontPixel++) {
            *Pixel++ = (LutFont[*AddressChar]&(1<<FontPixel))
              ? PixText : PixBack; }

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

        Separator = 1; }

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

      free(HeaderLine); }

    viewentirehsepar(); }

  else if (OptDecoration == OptShortDecoration) {
    /* only one line, with range on it */

    lutpos *Pos = getlutpos();
    int TopRightCol, TopRightRow, BotLeftCol, BotLeftRow;
    char Range[(3+1+3)+1+(3+1+3)+1], *RangeChar;
    int PicWidth, FontQuartet, FontPixel;

    firstsli(); firstlut(); Pos = getlutpos();
    TopRightCol = Pos->Col; TopRightRow = Pos->Row;

    lastsli(); lastlut(); Pos = getlutpos();
    BotLeftCol = Pos->Col; BotLeftRow = Pos->Row;

    sprintf(Range, "%03i-%03i/%03i-%03i",
            TopRightCol, BotLeftCol, TopRightRow, BotLeftRow);

    /* how much space do we have, chop range string if not fitting */
    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 */
      Range[(PicWidth+1)/(4+1)] = '\0';
      /* use '\' to show it has been chopped */
      Range[strlen(Range)-1] = '\\'; }

    /* can not use viewnormalline4x4() */
    /*   that assumes PicSlis*5-1 width, here Cols*50-1 */
    for (FontQuartet = 0; FontQuartet < 16; FontQuartet+=4) {

      char *HeaderLine = malloc(PicWidth+1), *Pixel = HeaderLine;
      int Separator = 0;

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

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

        /* if already an column has been viewed, then separator */
        if (Separator) {
          Pixel++; }

        for (FontPixel = FontQuartet; FontPixel < FontQuartet+4; FontPixel++) {
          *Pixel++ = (LutFont[*RangeChar]&(1<<FontPixel))
            ? PixText : PixBack; }

        Separator = 1; }

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

      free(HeaderLine); }

    viewentirehsepar(); } }


/* one row indexed horizontal line of CLBs */

void viewentireline(void) {

  lutpos *Pos = getlutpos(); char Address[4];
  int BitNo, FrameNo;

  sprintf(Address, "%03i", Pos->Row);

  for (BitNo = 0; BitNo < VirtexMiddleBits; BitNo++) {

    int PicWidth = LeaderSpace+Cols*(VirtexClbFrames+2)-2;
    char *Pixels = malloc(PicWidth+1), *Pixel = Pixels;
    int Separator = 0;

    if (LeaderSpace == 4+2) {
      if ((BitNo/5)*5+4 == BitNo || BitNo >= 15) {
        /* spaces between row address characters or after them */
        *Pixel++ = PixBack; *Pixel++ = PixBack;
        *Pixel++ = PixBack; *Pixel++ = PixBack; }
      else {
        int Char = BitNo/5, FontQuartet = 4*(BitNo-(BitNo/5)*5), FontPixel;
        for (FontPixel = FontQuartet; FontPixel < FontQuartet+4; FontPixel++) {
          *Pixel++ = (LutFont[Address[Char]]&(1<<FontPixel))
            ? PixText : PixBack; } }

      *Pixel++ = PixSep; *Pixel++ = PixSep; }

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

      /* if already an column has been viewed, then separator */
      if (Separator) {
        *Pixel++ = PixSep; *Pixel++ = PixSep; }

      for (FrameNo = 0; FrameNo < VirtexClbFrames; FrameNo++) {
        *Pixel++ = getclbbit(FrameNo, BitNo) ? PixOne : PixZero; }

      Separator = 1; }

    *Pixel = '\0';
    viewentirelinelayouted(Pixels);

    free(Pixels); } }


void viewentirelinelayouted(char *Pixels) {

  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 *FirstPixels = malloc(PicWidth+1), *FirstPixel = FirstPixels;
    char *SecondPixels = malloc(PicWidth+1), *SecondPixel = SecondPixels;

    for (Pixel = Pixels; *Pixel != '\0'; Pixel++) {
      if (((Pixel-Pixels) & 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; } }

    *FirstPixel = '\0';
    *SecondPixel = '\0';
    outpicline(FirstPixels);
    outpicline(SecondPixels);

    free(SecondPixels);
    free(FirstPixels); }

  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(Pixels);
 
    memset(Pixels, PixSep, PicWidth);
    Pixels[PicWidth] = '\0';
    outpicline(Pixels);
    outpicline(Pixels);
    outpicline(Pixels); }

  else {
    outpicline(Pixels); } }


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

void viewentirehsepar(void) {

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

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

  free(Pixels); }


/* ------ output pixels to file section */

void outpicheader(int PicWidth, int PicHight) {

  if (OptFormat == OptBitmapFormat) {

    /* use .ppm format, is simple and convertable by ImageMagick or netpbm */
    /* output file header, is a P3 (= .ppm) file, and size, and max value */
    fprintf(Outfile, "P3\n");
    fprintf(Outfile, "%i %i\n", ZoomWidth*PicWidth,
                                ZoomHight*PicHight);
    /* 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 Pixels, 1 char/pixel */

void outpicline(char *Pixels) {

  /* Pixels is an zero-terminated string with this 1 char/pixel format: */
  /* PixZero draw an 0 bit, PixOne draw an 1 bit */
  /* PixBack draw an character non-pixel, PixText draw an character pixel */
  /* PixSep  draw an vertical separator */

  if (OptFormat == OptAsciiFormat) {

    char *Pixel, PrintChars[128], *PrintLine, *Print;
    int PixelCount = 0, SeparatorCount = 0;

    /* check if we are actually drawing something */
    for (Pixel = Pixels; *Pixel != '\0'; Pixel++) {
      PixelCount++;
      if (*Pixel == PixSep) {
        SeparatorCount++; } }
    if (SeparatorCount == PixelCount) {
      /* all separators, nothing drawn, = horiz separator, output empty line */
      fprintf(Outfile, "\n");
      return; }

    /* set output translation of pixel values to print characters */
    PrintChars[PixZero] = '.';
    PrintChars[PixOne]  = '1';
    PrintChars[PixBack] = ' ';
    PrintChars[PixText] = '#';
    PrintChars[PixSep]  = ' ';

    /* expand to 2 spaces for each vertical separator (now one space) */
    /*   so that they are visually the same width as horizontal are high */
    Print = PrintLine = malloc(PixelCount+SeparatorCount+1);

    for (Pixel = Pixels; *Pixel != '\0'; Pixel++) {

      /* while at copying, convert pixel values to output chars to print */
      *Print++ = PrintChars[*Pixel];

      if (*Pixel == PixSep) {
        /* vertical separator, duplicate it, 2 chars for width = height */
        *Print++ = PrintChars[*Pixel]; } }

    *Print = '\0';
    fprintf(Outfile, "%s\n", PrintLine);

    free(PrintLine); }

  else {

    char *Pixel, *PrintLine, *Print;
    int PixelCount = 0, PixelsPerSect, Sects, StepHight, StepWidth;

    for (Pixel = Pixels; *Pixel != '\0'; Pixel++) {
      PixelCount++; }

    /* max PpmLineWidth chars with ZoomWidth*PpmCharsPerPixel per Pixel */
    /*   after these we need to insert an linefeed */
    PixelsPerSect = PpmLineWidth/(ZoomWidth*PpmCharsPerPixel);
    Sects = PixelCount/PixelsPerSect;

    /* add space for an linefeed for every section */
    PrintLine = malloc(PixelCount*ZoomWidth*PpmCharsPerPixel+Sects+1);
    Print = PrintLine;

    for (Pixel = Pixels; *Pixel != 0; ) {

      char *EndSectPixel = Pixel+PixelsPerSect;

      /* one section of pixels, to fit on .ppm line of max 79 chars */
      for (; Pixel <= EndSectPixel && *Pixel != 0; Pixel++) {

        /* repeat pixel in line for width zoom factor times */
        for (StepWidth = 0; StepWidth < ZoomWidth; StepWidth++) {
          /* convert pixel values to colour strings and add to output */
          strncpy(Print, OptPpmColour[*Pixel], PpmCharsPerPixel);
          Print += PpmCharsPerPixel; } }

      /* not yet at end of Pixels, must be end of section, put in linefeed */
      if (*Pixel != '\0') {
        *Print++ = '\n'; } }

    *Print = '\0';

    /* repeat line in output for height zoom factor times */
    for (StepHight = 0; StepHight < ZoomHight; StepHight++) {
      fprintf(Outfile, "%s\n", PrintLine); }

    free(PrintLine); } }

