keybd2video.hcc


//  keybd2video.hcc

/*  Displays the hexadecimal representation of the ASCII code of
 *  characters typed on a keyboard on two seven segment displays
 *  connect to the FPGA and two seven segment displays drawn on
 *  a video output device.  Sized and positioned for a 640x480
 *  pixel display.  Features lozenge shapes for the segments.
 *
 *  When a keyboard key is typed, it's ASCII code is displayed on
 *  the video output device, and then fades away.  Pushbutton 0
 *  swaps the foreground and background colors; button 1 cycles
 *  through a set of foreground and background pairs.
 *
 *  C. Vickery
 *  October, 2006
 */

#define PAL_TARGET_CLOCK_RATE 25175000
#include <stdlib.hch>
#include <pal_master.hch>
#include <pal_keyboard.hch>

#include "../delayProcs/delayProcs.hch"

//  Clock and I/O handles
macro expr  ClockRate = PAL_ACTUAL_CLOCK_RATE;
macro expr  video = PalVideoOutOptimalCT(ClockRate);
PalKeyboard *keybd;

macro expr currX = PalVideoOutGetX(video);
macro expr currY = PalVideoOutGetY(video);

//  Hexadecimal to seven-segment lookup table
static unsigned rom 7 hex2seven[16] =
{
  0b1111110, 0b0110000, 0b1101101, 0b1111001,
  0b0110011, 0b1011011, 0b1011111, 0b1110000,
  0b1111111, 0b1111011, 0b1110111, 0b0011111,
  0b1001110, 0b0111101, 0b1001111, 0b1000111,
};


//  Which seven-segment display segments to display.
//  Updated from keyboard data.
static unsigned 7 leftShape =0x7F, rightShape= 0x7F;

//  Drawing parameters: positions
macro expr leftLeft     = 150;
macro expr leftTop      = 125;
macro expr rightLeft    = 350;
macro expr rightTop     = leftTop;
//  Drawing parameters: sizes
macro expr thick        = 20;
macro expr wide         = 150;
macro expr high         = (wide-thick);

//  hSeg()
//  ------------------------------------------------------------------
/*  Evaluates to true if (currX, currY) is inside a horizontal segment
 *  with origin at (x,y), width == wide, and thickness == thick.
 */
  macro expr hSeg(x, y) =
  ((y < currY) && (currY < (y+thick)))
  &&
  (   // center bar
    (((x+thick) < currX) && (currX < (x+wide-thick)))
    ||
    ( //  left end
      ((x+(thick/2)) < currX) && (currX < (x+thick+1))
      &&
      (
        ( //  top triangle
          (currY < (y+(thick/2)))
          &&
          (
            (currX - (x+(thick/2))) > (y+(thick/2) - currY)
          )
        )
        ||
        ( //  bottom triangle
          (currY > (y-1+(thick/2)))
          &&
          (
            (currX - (x+(thick/2))) > (currY - (y-1+(thick/2)))
          )
        )
      )
    )
    ||
    ( //  right end
      (currX > (x+wide-thick-1)) && (currX < (x+wide-(thick/2)))
      &&
      (
        ( //  top triangle
          (currY < (y+(thick/2)))
          &&
          (
            (currX - (x+wide-thick-1)) < (currY - y)
          )
        )
        ||
        ( //  bottom triangle
          (currY > (y-1+(thick/2)))
          &&
          (
            (currX - (x+wide-thick)) < ((y-1+thick) - currY)
          )
        )
      )
    )
  );


//  vSeg()
//  ------------------------------------------------------------------
/*  Evaluates to true if (currX, currY) is inside a vertical segment
 *  with origin at (x,y), height == high, and thickness == thick.
 */
  macro expr vSeg(x, y) =
  ((x < currX) && (currX < (x+thick)))
  &&
  (
    (y < currY) && (currY < (y+high))
    &&
    (
      ( //  center bar
        ((y+thick) < currY) && (currY < (y+high-thick))
      )
      ||
      ( //  top end
        (y+(thick/2) < currY) && (currY < (y+1+thick))
        &&
        (
          ( //  left triangle
            (currY - (y+1+(thick/2))) > (x+(thick/2) - currX)
          )
          ||
          ( // right triangle
            (currY - (y+1+(thick/2))) > (currX - (x+(thick/2)))
          )
        )
      )
      ||
      ( //  bottom end
        (y-1+high-thick < currY) && (currY < (y+high-(thick/2)))
        &&
        (
          ( // left triangle
            (currX < (x+1+(thick/2)))
            &&
            ((currX - x) > (currY - (y-1+high-thick)))
          )
          ||
          ( //  right triangle
            (currX > (x+(thick/2)))
            &&
            (((x+thick) - currX) > (currY - (y-1+high-thick)))
          )
        )
      )
    )
  );


//  isForeground
//  ------------------------------------------------------------------
/*  Evaluates to true if current position is inside any segment that
 *  is currently on.
 */
  macro expr isForeground =
  (
    leftShape[6] &&
    hSeg(leftLeft, leftTop)                         //  Left A
  ||
    leftShape[5] &&
    vSeg(leftLeft+wide-thick, leftTop)              //  Left B
  ||
    leftShape[4] &&
    vSeg(leftLeft+wide-thick, leftTop+high-thick)   //  Left C
  ||
    leftShape[3] &&
    hSeg(leftLeft, leftTop+high-thick+high-thick)   //  Left D
  ||
    leftShape[2] &&
    vSeg(leftLeft, leftTop+high-thick)              //  Left E
  ||
    leftShape[1] &&
    vSeg(leftLeft, leftTop)                         //  Left F
  ||
    leftShape[0] &&
    hSeg(leftLeft, leftTop+high-thick)              //  Left G

  ||

    rightShape[6] &&
    hSeg(rightLeft, rightTop)                       //  Right A
  ||
    rightShape[5] &&
    vSeg(rightLeft+wide-thick, rightTop)            //  Right B
  ||
    rightShape[4] &&
    vSeg(rightLeft+wide-thick, rightTop+high-thick) //  Right C
  ||
    rightShape[3] &&
    hSeg(rightLeft, rightTop+high-thick+high-thick) //  Right D
  ||
    rightShape[2] &&
    vSeg(rightLeft, rightTop+high-thick)            //  Right E
  ||
    rightShape[1] &&
    vSeg(rightLeft, rightTop)                       //  Right F
  ||
    rightShape[0] &&
    hSeg(rightLeft, rightTop+high-thick)            //  Right G
);


//  isBorder
//  -------------------------------------------------------------------
/*  Evaluates to true if current position is within 2 pixels of any
 *  edge of the screen.
 */
  macro expr isBorder =
  (currX < 3) || (currX > 637) || (currY < 3) || (currY > 477);


//  Color Information
//  ------------------------------------------------------------------
//  Parameters affecting the fade effect
macro expr msecPerFadeStep  = 32;
macro expr fadeSteps        = 65;      //  Demonstrate a bad choice.
/*  When the number of fade steps is slightly over a power of two,
 *  almost all of the second half of the fade cycle gets lost and the
 *  image snaps to the background color after the specified number of
 *  steps. This may or may not be the desired visual effect. */

//  Derived parameters for color representation.
macro expr numFractionBits = (log2ceil(fadeSteps));
static unsigned (numFractionBits) frameCount;

//  Color structure: 8 bits per color channel.
struct color {
  unsigned 8 red;
  unsigned 8 green;
  unsigned 8 blue;
  };

//  Palette of foreground and background pairs.
/*  Values chosen to test combinations where Fg intensity is
 *  >, <, and == Bg, and with leftmost bit of pairs having
 *  all four combinations of {0,1}.  Includes end of range values
 *  0 and 255.
 */
struct palette_s {
  struct color fg;
  struct color bg;
  } palette[4] = {
    //  Foreground            Background
    //  Red   Green Blue      Red   Green Blue
    { { 0xFF, 0xFF, 0x10 }, { 0x10, 0x10, 0xFF } }, // yellow|blue
    { { 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF } }, // black|white
    { { 0xFF, 0x7F, 0x00 }, { 0x00, 0x7F, 0xFF } }, // yellow|cyan
    { { 0xFF, 0xFF, 0x01 }, { 0x80, 0x80, 0x7F } }, // yellow|gray
    };
unsigned 2 paletteIndex = 0;

//  Color channels with fraction bits for calculating intensities
//  incrementally.
macro expr colorWithFraction = (numFractionBits + 8);
typedef unsigned (colorWithFraction) fullColor;
struct colorCalc {
  fullColor red;
  fullColor green;
  fullColor blue;
  } fgCurrent = {0, 0, 0}, deltas;


//  Color management utilities
//  --------------------------
//  Assign current foreground a known value.
macro proc setForeground(red, green, blue)
{
  unsigned (numFractionBits) zeros;
  zeros = 0;
  par
  {
    fgCurrent.red    = red@zeros;
    fgCurrent.green  = green@zeros;
    fgCurrent.blue   = blue@zeros;
  }
}
//  Calculates new foreground at each fade step
macro proc updateForeground()
{
  par
  {
    fgCurrent.red   += deltas.red;
    fgCurrent.green += deltas.green;
    fgCurrent.blue  += deltas.blue;
  }
}
//  Converts current fg fixed point channel values into a
//  video out pixel value.
macro expr currentFgRGB() = (fgCurrent.red   \\ numFractionBits)@
                            (fgCurrent.green \\ numFractionBits)@
                            (fgCurrent.blue  \\ numFractionBits);

//  Colors: foreground, background, border
//  --------------------------------------
//  When key is pressed fgCurrent will be set to fg value from current
//  palette and then fade to bg value in fadeSteps increments.
macro expr          fgColor = currentFgRGB();
macro expr          bgColor = palette[paletteIndex].bg.red @
                              palette[paletteIndex].bg.green @
                              palette[paletteIndex].bg.blue;
static unsigned 24  boColor = 0xFF0000;

//  calcDeltas()
//  -------------------------------------------------------------------
/*
 *    Calculates the RGB deltas for fading foreground to background.
 *    Each 8-bit color channel is converted to a 9-bit unsigned value,
 *    and the 9-bit difference is sign extended to the width of a
 *    full-width color increment.
 */
  macro proc calcDeltas()
  {
    unsigned 9 fgR, fgG, fgB, bgR, bgG, bgB;
    unsigned 9 deltaR, deltaG, deltaB;

    par
    { //  Generate 9-bit channel values.
      fgR = 0@palette[paletteIndex].fg.red;
      bgR = 0@palette[paletteIndex].bg.red;
      fgG = 0@palette[paletteIndex].fg.green;
      bgG = 0@palette[paletteIndex].bg.green;
      fgB = 0@palette[paletteIndex].fg.blue;
      bgB = 0@palette[paletteIndex].bg.blue;
    }
    par
    { //  Unsigned subtraction produces signed differences.
      deltaR  = (bgR - fgR);
      deltaG  = (bgG - fgG);
      deltaB  = (bgB - fgB);
    }
    par
    { //  Sign extend to full color channel width.
      deltas.red   = copy(deltaR[8],(numFractionBits-1))@deltaR;
      deltas.green = copy(deltaG[8],(numFractionBits-1))@deltaG;
      deltas.blue  = copy(deltaB[8],(numFractionBits-1))@deltaB;
    }
  }


//  main()
//  -------------------------------------------------------------------
/*
 *    Reads from keyboard and draws seven segment displays showing
 *    ASCII codes of characters typed.  Seven segment displays fade out
 *    after they are drawn.
 */
  void main(void)
  {
    unsigned 8      inChar;
    chan unsigned 0 gotChar;
    unsigned 0      sync;

    PalPS2PortRequire(2);
    PalSevenSegRequire(2);
		PalVideoOutRequire(1);

    //  Initialize current foreground color
    setForeground(palette[0].fg.red,
                  palette[0].fg.green,
                  palette[0].fg.blue);

    //  Initialize fade increments
    calcDeltas();

    //  Runtime processing
    //  ------------------
    par
    {
			PalVideoOutRun(video, ClockRate);
      PalKeyboardRun(&keybd, PalPS2PortCT(1), ClockRate);
      seq
      {
        PalKeyboardEnable(keybd);
        PalSevenSegEnable(PalSevenSegCT(0));
        PalSevenSegEnable(PalSevenSegCT(1));
        PalVideoOutEnable(video);
        par
        {

          while (1)
          { //  Keyboard Loop
            PalKeyboardReadASCII(keybd, &inChar);
            gotChar ! 0;
            PalSevenSegWriteDigit(PalSevenSegCT(0), inChar \\ 4, 0);
            PalSevenSegWriteDigit(PalSevenSegCT(1), inChar <- 4, 0);
            leftShape = hex2seven[inChar \\ 4];
            rightShape = hex2seven[inChar <- 4];
          }

          while (1)
          { //  Drawing Loop
            if ( isForeground )
  					  PalVideoOutWrite(video, fgColor);
            else if ( isBorder )
              PalVideoOutWrite(video, boColor);
            else
              PalVideoOutWrite(video, bgColor);
          }

          while (1)
          { //  Fade Loop
            gotChar ? sync;
            setForeground(palette[paletteIndex].fg.red,
                          palette[paletteIndex].fg.green,
                          palette[paletteIndex].fg.blue);
            frameCount = fadeSteps - 1;
            while (frameCount != 0)
            {
              msecDelay(msecPerFadeStep, ClockRate);
              par
              {
                updateForeground();
                frameCount--;
              }
            }
            setForeground(palette[paletteIndex].bg.red,
                          palette[paletteIndex].bg.green,
                          palette[paletteIndex].bg.blue);
          }
        }
      }
    }
  }


//  main() -- buttons
//  -------------------------------------------------------------------
/*
 *    Use buttons to change color schemes:
 *      Button 0: Swap current background and foreground colors.
 *      Button 1: Cycle through the set of fg/bg color palettes.
 */
  void main(void)
  {
    macro expr debounceMsec = 32;
    unsigned 1 button_0, button_1;

    par
    {
      while (1)
      {
        //  Update the states of the switches on every clock
        par
        {
          PalSwitchRead(PalSwitchCT(0), &button_0);
          PalSwitchRead(PalSwitchCT(1), &button_1);
        }
      }
      while (1)
      {
        //  Monitor Button 0
        //  ----------------

        //  Wait for press and debounce
        while (!button_0)
          delay;
        msecDelay(debounceMsec, ClockRate);

        //  Cycle fg/bg pair
        paletteIndex++;
        setForeground(palette[paletteIndex].fg.red,
                      palette[paletteIndex].fg.green,
                      palette[paletteIndex].fg.blue);
        calcDeltas();

        //  Wait for release and debounce
        while (button_0)
          delay;
        msecDelay(debounceMsec, ClockRate);
      }

      while (1)
      {
        //  Monitor Button 1
        //  ----------------

        //  Wait for press and debounce
        while (!button_1)
          delay;
        msecDelay(debounceMsec, ClockRate);

        //  Swap bg/fg
        par
        {
          palette[paletteIndex].fg = palette[paletteIndex].bg;
          palette[paletteIndex].bg = palette[paletteIndex].fg;
        }
        setForeground(palette[paletteIndex].fg.red,
                      palette[paletteIndex].fg.green,
                      palette[paletteIndex].fg.blue);
        calcDeltas();

        //  Wait for release and debounce
        while (button_1)
          delay;
        msecDelay(debounceMsec, ClockRate);
      }
    }
  }