a short description about what the example codes are supposed to do would be appreciated!
Code: Select all
/*
/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Initial Developer of this code is John Hansen.
 * Portions created by John Hansen are Copyright (C) 2009-2013 John Hansen.
 * All Rights Reserved.
 *
 */
#include "ev3_lcd.h"
#include "ev3_timer.h"
#define DRAW_PIXELS_SET    0x00 //Basic options for pixel, line and shape drawing.
#define DRAW_PIXELS_CLEAR  0x01
#define DRAW_PIXELS_INVERT 0x02
#define DRAW_SHAPE_HOLLOW 0x00 //Extra options for shape drawing.
#define DRAW_SHAPE_FILLED 0x01
#define DRAW_BITMAP_PLAIN  0x00
#define DRAW_BITMAP_INVERT 0x01
#define DRAW_LOGICAL_COPY 0x00
#define DRAW_LOGICAL_AND  0x01
#define DRAW_LOGICAL_OR   0x02
#define DRAW_LOGICAL_XOR  0x03
#define LCD_MEM_WIDTH 60 // width of HW Buffer in bytes
#define LCD_BUFFER_LENGTH (LCD_MEM_WIDTH*LCD_HEIGHT)
#define ABS(a)    (((a)<0) ? -(a) : (a))
#define SGN(a)    (((a)<0) ? -1 : 1)
#define MAX(a,b)    (((a>b) ? (a) : (b)))
#define MIN(a,b)    (((a<b) ? (a) : (b)))
byte hwBuffer[LCD_BUFFER_LENGTH];
byte PixelTab[] = {
    0x00, // 000 00000000
    0xE0, // 001 11100000
    0x1C, // 010 00011100
    0xFC, // 011 11111100
    0x03, // 100 00000011
    0xE3, // 101 11100011
    0x1F, // 110 00011111
    0xFF  // 111 11111111
  };
byte UnPixelTab[] = {
// 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
   0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 6, // 1
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
   1, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 7  // F
  };
  
typedef struct {
  bool Dirty;
  int DispFile;
  byte *pFB0;
  byte displayBuf[LCD_BUFFER_SIZE];
  byte *font;
  byte *pLcd;
  byte currentFont;
  bool autoRefresh;
} LCDGlobals;
LCDGlobals LCDInstance;
int WriteToLcdDevice(char * bytes, int num_bytes)
{
  int result = -1;
  if (LCDInstance.DispFile >= 0)
  {
    // for some reason write is not returning num_bytes -
    // it usually returns zero
    result = write(LCDInstance.DispFile, bytes, num_bytes);
    if (result >= 0)
      return num_bytes;
  }
  return result;
}
typedef struct
{
  const char  *pFontBits;           // Pointer to start of font bitmap
  const short FontHeight;           // Character height (all inclusive)
  const short FontWidth;            // Character width (all inclusive)
  const short FontHorz;             // Number of horizontal character in font bitmap
  const char  FontFirst;            // First character supported
  const char  FontLast;             // Last character supported
} FONTINFO;
#include "normal_font.xbm"
#include "small_font.xbm"
#include "large_font.xbm"
#include "tiny_font.xbm"
FONTINFO FontInfo[] =
{
  [FONTTYPE_NORMAL] = {
                    .pFontBits    = (const char*)normal_font_bits,
                    .FontHeight   = 9,
                    .FontWidth    = 8,
                    .FontHorz     = 16,
                    .FontFirst    = 0x20,
                    .FontLast     = 0x7F
                  },
  [FONTTYPE_SMALL] =  {
                    .pFontBits    = (const char*)small_font_bits,
                    .FontHeight   = 8,
                    .FontWidth    = 8,
                    .FontHorz     = 16,
                    .FontFirst    = 0x20,
                    .FontLast     = 0x7F
                  },
  [FONTTYPE_LARGE] =  {
                    .pFontBits    = (const char*)large_font_bits,
                    .FontHeight   = 16,
                    .FontWidth    = 16,
                    .FontHorz     = 16,
                    .FontFirst    = 0x20,
                    .FontLast     = 0x7F
                  },
  [FONTTYPE_TINY] =   {
                    .pFontBits    = (const char*)tiny_font_bits,
                    .FontHeight   = 7,
                    .FontWidth    = 5,
                    .FontHorz     = 16,
                    .FontFirst    = 0x20,
                    .FontLast     = 0x7F
                  },
};
short dLcdGetFontWidth(byte Font)
{
  return (FontInfo[Font].FontWidth);
}
short dLcdGetFontHeight(byte Font)
{
  return (FontInfo[Font].FontHeight);
}
typedef   struct
{
  const char  *pIconBits;
  const short IconSize;
  const short IconHeight;
  const short IconWidth;
} ICONINFO;
#include "normal_icons.xbm"
#include "small_icons.xbm"
#include "large_icons.xbm"
#include "menu_icons.xbm"
#include "arrow_icons.xbm"
ICONINFO IconInfo[] =
{
  [ICONTYPE_NORMAL] = {
                    .pIconBits    = normal_icons_bits,
                    .IconSize     = normal_icons_height,
                    .IconHeight   = 12,
                    .IconWidth    = normal_icons_width
                  },
  [ICONTYPE_SMALL] =  {
                    .pIconBits    = small_icons_bits,
                    .IconSize     = small_icons_height,
                    .IconHeight   = 8,
                    .IconWidth    = small_icons_width
                  },
  [ICONTYPE_LARGE] =  {
                    .pIconBits    = large_icons_bits,
                    .IconSize     = large_icons_height,
                    .IconHeight   = 22,
                    .IconWidth    = large_icons_width
                  },
  [ICONTYPE_MENU] =   {
                    .pIconBits    = menu_icons_bits,
                    .IconSize     = menu_icons_height,
                    .IconHeight   = 12,
                    .IconWidth    = menu_icons_width
                  },
  [ICONTYPE_ARROW] =  {
                    .pIconBits    = arrow_icons_bits,
                    .IconSize     = arrow_icons_height,
                    .IconHeight   = 12,
                    .IconWidth    = arrow_icons_width
                  },
};
byte *dLcdGetIconBits(byte Icon)
{
  return (byte*)IconInfo[Icon].pIconBits;
}
short dLcdGetIconWidth(byte Icon)
{
  return (IconInfo[Icon].IconWidth);
}
short dLcdGetIconHeight(byte Icon)
{
  return (IconInfo[Icon].IconHeight);
}
short dLcdGetNumIcons(byte Icon)
{
  return (IconInfo[Icon].IconSize / IconInfo[Icon].IconHeight);
}
void frameBufferToLcd(byte* pSrc, byte* pDst)
{
  unsigned long Pixels;
  unsigned short X;
  unsigned short Y;
  for (Y = 0; Y < LCD_HEIGHT; Y++)
  {
    for (X = 0; X < 7; X++)
    {
      // read 8 bytes (3 bits per byte) into a 32-bit unsigned long from our source
      Pixels  = (unsigned long)UnPixelTab[*pSrc] << 0;  pSrc++;
      Pixels |= (unsigned long)UnPixelTab[*pSrc] << 3;  pSrc++;
      Pixels |= (unsigned long)UnPixelTab[*pSrc] << 6;  pSrc++;
      Pixels |= (unsigned long)UnPixelTab[*pSrc] << 9;  pSrc++;
      Pixels |= (unsigned long)UnPixelTab[*pSrc] << 12; pSrc++;
      Pixels |= (unsigned long)UnPixelTab[*pSrc] << 15; pSrc++;
      Pixels |= (unsigned long)UnPixelTab[*pSrc] << 18; pSrc++;
      Pixels |= (unsigned long)UnPixelTab[*pSrc] << 21; pSrc++;
      // now write the 24 bits to 3 8-bit bytes in our destination
      *pDst = (byte)(Pixels >> 0);  pDst++;
      *pDst = (byte)(Pixels >> 8);  pDst++;
      *pDst = (byte)(Pixels >> 16); pDst++;
    }
    Pixels  = (unsigned long)UnPixelTab[*pSrc] << 0;  pSrc++;
    Pixels |= (unsigned long)UnPixelTab[*pSrc] << 3;  pSrc++;
    Pixels |= (unsigned long)UnPixelTab[*pSrc] << 6;  pSrc++;
    Pixels |= (unsigned long)UnPixelTab[*pSrc] << 9;  pSrc++;
    *pDst = (byte)(Pixels >> 0);  pDst++;
    *pDst = (byte)(Pixels >> 8);  pDst++;
  }
}
void lcdToFrameBuffer(byte* pSrc, byte* pDst)
{
  unsigned long Pixels;
  unsigned short X;
  unsigned short Y;
  for (Y = 0; Y < LCD_HEIGHT; Y++)
  {
    for (X = 0; X < 7; X++)
    {
      Pixels  =  (unsigned long)*pSrc << 0;  pSrc++;
      Pixels |=  (unsigned long)*pSrc << 8;  pSrc++;
      Pixels |=  (unsigned long)*pSrc << 16; pSrc++;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
      Pixels >>= 3;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
      Pixels >>= 3;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
      Pixels >>= 3;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
      Pixels >>= 3;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
      Pixels >>= 3;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
      Pixels >>= 3;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
      Pixels >>= 3;
      *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
    }
    Pixels  =  (unsigned long)*pSrc << 0; pSrc++;
    Pixels |=  (unsigned long)*pSrc << 8; pSrc++;
    *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
    Pixels >>= 3;
    *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
    Pixels >>= 3;
    *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
    Pixels >>= 3;
    *pDst   =  PixelTab[Pixels & 0x07]; pDst++;
  }
}
void doUpdateScreen()
{
  if (LCDInstance.Dirty && (LCDInstance.pFB0 != NULL) && (LCDInstance.pLcd != NULL))
  {
    lcdToFrameBuffer(LCDInstance.pLcd, hwBuffer);
    memmove((void*)LCDInstance.pFB0, (const void *)hwBuffer, LCD_BUFFER_LENGTH);
    LCDInstance.Dirty = false;
  }
}
void LcdUpdateHandler(int sig)
{
  if (LCDInstance.autoRefresh)
      doUpdateScreen();
}
void LcdCloseDevices()
{
  if (!LcdInitialized())
    return;
  if (LCDInstance.DispFile >= 0)
  {
    close(LCDInstance.DispFile);
    if (LCDInstance.pFB0 != NULL)
      munmap(LCDInstance.pFB0, LCD_BUFFER_LENGTH);
    LCDInstance.pFB0 = NULL;
    LCDInstance.DispFile = -1;
  }
}
bool LcdInit()
{
  int i;
  byte * pTmp;
  if (LcdInitialized()) return true;
  LCDInstance.autoRefresh = true;
  LCDInstance.Dirty = false;
  LCDInstance.pFB0 = NULL;
  LCDInstance.DispFile = -1;
  LCDInstance.font = NULL;
  LCDInstance.pLcd = NULL;
  LCDInstance.currentFont = FONTTYPE_NORMAL;
  LCDInstance.DispFile = open(LMS_LCD_DEVICE_NAME, O_RDWR);
  if (LCDInstance.DispFile != -1)
  {
    pTmp = (byte*)mmap(NULL, LCD_BUFFER_LENGTH, PROT_READ + PROT_WRITE, MAP_SHARED, LCDInstance.DispFile, 0);
    if (pTmp == MAP_FAILED)
    {
      LcdCloseDevices();
      return false;
    }
    else
    {
      LCDInstance.pFB0 = pTmp;
//      LCDInstance.font := @(font_data[0]);
      LCDInstance.pLcd = LCDInstance.displayBuf;
      
      // initialize timer system
      TimerInit();
      
      // register update handler with timer system
      SetTimerCallback(ti250ms, &LcdUpdateHandler);
      return true;
    }
  }
}
bool LcdOpen()
{
  return true;
}
bool LcdClose()
{
  return true;
}
bool LcdExit()
{
  // if not initialized then just exit
  if (!LcdInitialized())
    return true;
  LcdCloseDevices();
  LCDInstance.font = NULL;
  LCDInstance.pLcd = NULL;
  return true;
}
bool LcdInitialized()
{
  return (LCDInstance.DispFile != -1) &&
         (LCDInstance.pFB0 != NULL);
}
void dLcdDrawPixel(byte *pImage, char Color, short X0, short Y0)
{
  if ((X0 >= 0) && (X0 < LCD_WIDTH) && (Y0 >= 0) && (Y0 < LCD_HEIGHT))
  {
    if (Color)
    {
      pImage[(X0 >> 3) + Y0 * LCD_BYTE_WIDTH]  |=  (1 << (X0 % 8));
    }
    else
    {
      pImage[(X0 >> 3) + Y0 * LCD_BYTE_WIDTH]  &= ~(1 << (X0 % 8));
    }
  }
}
char dLcdReadPixel(byte *pImage, short X0, short Y0)
{
  char Result = 0;
  if ((X0 >= 0) && (X0 < LCD_WIDTH) && (Y0 >= 0) && (Y0 < LCD_HEIGHT))
  {
    if ((pImage[(X0 >> 3) + Y0 * LCD_BYTE_WIDTH] & (1 << (X0 % 8))))
    {
      Result = 1;
    }
  }
  return (Result);
}
void dLcdDrawChar(byte *pImage, char Color, short X0, short Y0, char Font, char Char)
{
  short CharWidth;
  short CharHeight;
  short CharByteIndex;
  short LcdByteIndex;
  byte  CharByte;
  short Tmp,X,Y,TmpX,MaxX;
  byte  bC1, bC2;
  CharWidth  = FontInfo[Font].FontWidth;
  CharHeight = FontInfo[Font].FontHeight;
  if ((Char >= FontInfo[Font].FontFirst) && (Char <= FontInfo[Font].FontLast))
  {
    Char -= FontInfo[Font].FontFirst;
    CharByteIndex  = (Char % FontInfo[Font].FontHorz) * ((CharWidth + 7) / 8);
    CharByteIndex += ((Char / FontInfo[Font].FontHorz) * ((CharWidth + 7) / 8) * CharHeight * FontInfo[Font].FontHorz);
    if (((CharWidth % 8) == 0) && ((X0 % 8) == 0))
    {
      // Font aligned
      X0           = (X0 >> 3) << 3;
      LcdByteIndex = (X0 >> 3) + Y0 * LCD_BYTE_WIDTH;
      while (CharHeight)
      {
        Tmp = 0;
        do
        {
          if (LcdByteIndex < LCD_BUFFER_SIZE)
          {
            if (Color)
              CharByte = FontInfo[Font].pFontBits[CharByteIndex + Tmp];
            else
              CharByte = ~FontInfo[Font].pFontBits[CharByteIndex + Tmp];
            pImage[LcdByteIndex + Tmp] = CharByte;
          }
          Tmp++;
        }
        while (Tmp < (CharWidth / 8));
        CharByteIndex += (CharWidth * FontInfo[Font].FontHorz) / 8;
        LcdByteIndex  += LCD_BYTE_WIDTH;
        CharHeight--;
      }
    }
    else
    {
      // Font not aligned
      MaxX = X0 + CharWidth;
      if (Color)
      {
        bC1 = 1;
        bC2 = 0;
      }
      else
      {
        bC1 = 0;
        bC2 = 1;
      }
      for (Y = 0;Y < CharHeight;Y++)
      {
        TmpX = X0;
        for (X = 0;X < ((CharWidth + 7) / 8);X++)
        {
          CharByte = FontInfo[Font].pFontBits[CharByteIndex + X];
          for (Tmp = 0;(Tmp < 8) && (TmpX < MaxX);Tmp++)
          {
            if (CharByte & 1)
            {
              dLcdDrawPixel(pImage,bC1,TmpX,Y0);
            }
            else
            {
              dLcdDrawPixel(pImage,bC2,TmpX,Y0);
            }
            CharByte >>= 1;
            TmpX++;
          }
        }
        Y0++;
        CharByteIndex += ((CharWidth + 7) / 8) * FontInfo[Font].FontHorz;
      }
    }
  }
}
void dLcdDrawText(byte *pImage, char Color, short X0, short Y0, char Font, char *pText)
{
  while (*pText)
  {
    if (X0 < (LCD_WIDTH - FontInfo[Font].FontWidth))
    {
      dLcdDrawChar(pImage,Color,X0,Y0,Font,*pText);
      X0 += FontInfo[Font].FontWidth;
    }
    pText++;
  }
}
void dLcdDrawPicture(byte *pImage,char Color,short X0,short Y0,short IconWidth,short IconHeight,byte *pIconBits)
{
  short IconByteIndex;
  short LcdByteIndex;
  short Tmp;
  byte  IconByte;
  IconByteIndex = 0;
  X0           = (X0 >> 3) << 3;
  LcdByteIndex = (X0 >> 3) + Y0 * LCD_BYTE_WIDTH;
  while (IconHeight)
  {
    for (Tmp = 0;Tmp < (IconWidth / 8);Tmp++)
    {
      if (Color)
        IconByte = pIconBits[IconByteIndex + Tmp];
      else
        IconByte = ~pIconBits[IconByteIndex + Tmp];
      pImage[LcdByteIndex + Tmp] = IconByte;
    }
    IconByteIndex += IconWidth / 8;
    LcdByteIndex  += LCD_BYTE_WIDTH;
    IconHeight--;
  }
}
void dLcdDrawIcon(byte *pImage, char Color, short X0, short Y0, char Icon, char Num)
{
  short IconByteIndex;
  short IconHeight;
  short IconWidth;
  byte  *pIconBits;
  IconHeight = dLcdGetIconHeight(Icon);
  IconWidth  = dLcdGetIconWidth(Icon);
  if ((Num >= 0) && (Num <= dLcdGetNumIcons(Icon)))
  {
    pIconBits     = dLcdGetIconBits(Icon);
    IconByteIndex = ((short)Num * IconWidth * IconHeight) / 8;
    dLcdDrawPicture(pImage,Color,X0,Y0,IconWidth,IconHeight,&pIconBits[IconByteIndex]);
  }
}
void dLcdGetBitmapSize(IP pBitmap, short *pWidth, short *pHeight)
{
  *pWidth  = 0;
  *pHeight = 0;
  if (pBitmap)
  {
    *pWidth  = (short)pBitmap[0];
    *pHeight = (short)pBitmap[1];
  }
}
void dLcdDrawBitmap(byte *pImage, char Color, short X0, short Y0, IP pBitmap)
{
  short  BitmapWidth;
  short  BitmapHeight;
  short  BitmapByteIndex;
  byte   *pBitmapBytes;
  byte   BitmapByte;
  short  Tmp,X,Y,TmpX,MaxX;
  short  LcdByteIndex;
  byte   bC1, bC2;
  if (pBitmap)
  {
    BitmapWidth   =  (short)pBitmap[0];
    BitmapHeight  =  (short)pBitmap[1];
    MaxX          =  X0 + BitmapWidth;
    pBitmapBytes  =  &pBitmap[2];
    if ((BitmapWidth >=0) && (BitmapHeight >= 0))
    {
      if ((X0 % 8) || (BitmapWidth % 8))
      { // X is not aligned
        BitmapWidth = ((BitmapWidth + 7) >> 3) << 3;
        if (Color)
        {
          bC1 = 1;
          bC2 = 0;
        }
        else
        {
          bC1 = 0;
          bC2 = 1;
        }
        for (Y = 0;Y < BitmapHeight;Y++)
        {
          BitmapByteIndex = (Y * BitmapWidth) / 8;
          TmpX            = X0;
          for (X = 0;X < (BitmapWidth / 8);X++)
          {
            BitmapByte = pBitmapBytes[BitmapByteIndex + X];
            for (Tmp = 0;(Tmp < 8) && (TmpX < MaxX);Tmp++)
            {
              if (BitmapByte & 1)
              {
                dLcdDrawPixel(pImage,bC1,TmpX,Y0);
              }
              else
              {
                dLcdDrawPixel(pImage,bC2,TmpX,Y0);
              }
              BitmapByte >>= 1;
              TmpX++;
            }
          }
          Y0++;
        }
      }
      else
      { // X is byte aligned
        BitmapByteIndex = 0;
        LcdByteIndex    = (X0 >> 3) + Y0 * LCD_BYTE_WIDTH;
        while (BitmapHeight)
        {
          X  =  X0;
          for (Tmp = 0;Tmp < (BitmapWidth / 8);Tmp++)
          {
            if (((LcdByteIndex + Tmp) < LCD_BUFFER_SIZE) && (X < LCD_WIDTH) && (X >= 0))
            {
              if (Color)
                BitmapByte = pBitmapBytes[BitmapByteIndex + Tmp];
              else
                BitmapByte = ~pBitmapBytes[BitmapByteIndex + Tmp];
              pImage[LcdByteIndex + Tmp] = BitmapByte;
            }
            X +=  8;
          }
          BitmapByteIndex += BitmapWidth / 8;
          LcdByteIndex    += LCD_BYTE_WIDTH;
          BitmapHeight--;
        }
      }
    }
  }
}
void LcdRefresh()
{
  if (!LcdInitialized())
    return;
  doUpdateScreen();
}
void LcdSetAutoRefresh(bool bOn)
{
  LCDInstance.autoRefresh = bOn;
}
bool LcdUpdate()
{
  if (!LcdInitialized())
    return false;
  doUpdateScreen();
  LCDInstance.Dirty = false;
  return true;
}
bool LcdClean()
{
  if (!LcdInitialized())
    return false;
  LCDInstance.currentFont = FONTTYPE_NORMAL;
  memset((void*)LCDInstance.pLcd, 0, LCD_BUFFER_SIZE);
  LCDInstance.Dirty = true;
  return true;
}
void LcdClearDisplay()
{
  LcdClean();
  LCDInstance.Dirty = true;
}
bool LcdScroll(short Y)
{
  if (LcdInitialized())
  {
    if ((Y > 0) && (Y < LCD_HEIGHT))
    {
      memmove(LCDInstance.pLcd,&(LCDInstance.pLcd[LCD_BYTE_WIDTH * Y]),(LCD_HEIGHT - Y) * LCD_BYTE_WIDTH);
      memset(&(LCDInstance.pLcd[(LCD_HEIGHT - Y) * LCD_BYTE_WIDTH]),0,LCD_BYTE_WIDTH * Y);
    }
    else if ((Y < 0) && (Y > -LCD_HEIGHT))
    {
      Y = ABS(Y);
      memmove(&(LCDInstance.pLcd[LCD_BYTE_WIDTH * Y]),LCDInstance.pLcd,(LCD_HEIGHT - Y) * LCD_BYTE_WIDTH);
      memset(LCDInstance.pLcd,0,LCD_BYTE_WIDTH * Y);
    }
    return true;
  }
  return false;
}
bool LcdSelectFont(byte FontType)
{
  if (!LcdInitialized())
    return false;
  if (FontType >= NUM_FONTTYPES)
  {
    LCDInstance.currentFont = FONTTYPE_NORMAL;
  }
  else
  {
    LCDInstance.currentFont = FontType;
  }
  return true;
}
byte* LcdGetDisplay()
{
  if (!LcdInitialized())
    return NULL;
  return LCDInstance.pLcd;
}
byte* LcdGetFrameBuffer()
{
  if (!LcdInitialized())
    return NULL;
  // read from frame buffer into hwBuffer
  memmove((void *)hwBuffer, (const void*)LCDInstance.pFB0, LCD_BUFFER_LENGTH);
  // convert to display buffer
  frameBufferToLcd(hwBuffer, LCDInstance.pLcd);
  // return display buffer
  return LCDInstance.pLcd;
}
static const unsigned char reverse_bits_table[] = {
  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
  0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
  0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
  0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
  0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
  0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
  0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
  0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
  0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
  0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
byte reverse_bits(byte b)
{
  return reverse_bits_table[b];
}
void _lcdWriteBytesToFile(ImageFormat fmt, byte* data, char* filename, byte width, byte height)
{
  char fullname[128];
  char* fn_copy = strdup(filename);
  char *base, *path;
  path = dirname(fn_copy);
  base = basename(filename);
  byte len = strlen(base);
  // check for and remove extension if it exists
  char* ext = strrchr(base, '.');
  if (ext) {
    len = ext-base;
  }
  // now build full path
  strcpy(fullname, path);
  strcat(fullname, "/");
  strncat(fullname, base, len);
  switch(fmt)
  {
    case ifP1:
    case ifP4:
      strcat(fullname, ".pbm");
      break;
    case ifBMP:
      strcat(fullname, ".bmp");
      break;
    case ifPNG:
      strcat(fullname, ".png");
      break;
    default:
      // default is XBM
      strcat(fullname, ".xbm");
      break;
  }
  FILE * pFile = NULL;
  pFile = fopen(fullname, "w");
  if (pFile != NULL)
  {
    int i, j, k, cnt, bw;
    bw = ((width + 7) / 8);
    switch (fmt)
    {
      case ifP1:
        fprintf(pFile, "P1\n%d %d\n", width, height);
        for (i = 0; i < height; i++)
        {
          byte b;
          for (j = 0; j < bw-1; j++)
          {
            // get a byte
            b = reverse_bits(data[i*bw+j]);
            // output 8 bits per byte except for last byte per line
            for (k = 7; k >= 0; k--)
            {
              fprintf(pFile, "%c ", (b & (1 << k)) ? '1' : '0');
            }
          }
          // handle last byte per line
          b = reverse_bits(data[i*bw+(bw-1)]);
          // how many bits are extra?
          cnt = bw*8 - width; // e.g., 6 if width = 178
          for (k = 7; k >= cnt; k--)
          {
            fprintf(pFile, "%c ", (b & (1 << k)) ? '1' : '0');
          }
          fprintf(pFile, "\n");
        }
        break;
      case ifP4:
        fprintf(pFile, "P4\n%d %d\n", width, height);
        cnt = bw*height;
        for (i=0; i < cnt; i++)
        {
          byte b = reverse_bits(data[i]);
          fwrite(&b, 1, 1, pFile);
        }
        break;
      case ifBMP:
      case ifPNG:
        printf("BMP and PNG image formats are not yet supported\n");
        break;
      default:
        // XBM format
        fprintf(pFile, "#define %s_width %d\n", base, width);
        fprintf(pFile, "#define %s_height %d\n", base, height);
        fprintf(pFile, "static char %s_bits[] = {\n", base);
        cnt = bw*height;
        for (i=0; i < cnt; i++)
        {
          fprintf(pFile, "0x%2.2X,", data[i]);
          if ((i+1) % 12 == 0)
            fprintf(pFile, "\n");
        }
        fprintf(pFile, " };\n");
        break;
    }
    fclose(pFile);
  }
}
void LcdWriteDisplayToFile(char* filename, ImageFormat fmt)
{
  if (!LcdInitialized())
    return;
  byte * pSrc = LcdGetDisplay();
  _lcdWriteBytesToFile(fmt, pSrc, filename, 178, 128);
}
void LcdWriteFrameBufferToFile(char* filename, ImageFormat fmt)
{
  if (!LcdInitialized())
    return;
  byte * pSrc = LcdGetFrameBuffer();
  _lcdWriteBytesToFile(fmt, pSrc, filename, 178, 128);
}
bool LcdText(char Color, short X, short Y, char* Text)
{
  if (!LcdInitialized())
    return false;
  dLcdDrawText(LCDInstance.pLcd, Color, X, Y, LCDInstance.currentFont, Text);
  LCDInstance.Dirty = true;
  return true;
}
bool LcdIcon(char Color, short X, short Y, char IconType, char IconNum)
{
  if (!LcdInitialized())
    return false;
  dLcdDrawIcon(LCDInstance.pLcd, Color, X, Y, IconType, IconNum);
  LCDInstance.Dirty = true;
  return true;
}
bool LcdBmpFile(char Color, short X, short Y, char* Name)
{
  if (LcdInitialized())
  {
    byte pBmp[LCD_BUFFER_SIZE];
    int File = -1;
    File = open(Name, O_RDONLY);
    if (File >= 0)
    {
      read(File, pBmp, LCD_BUFFER_SIZE);
      close(File);
      dLcdDrawBitmap(LCDInstance.pLcd, Color, X, Y, (IP)pBmp);
      LCDInstance.Dirty = true;
      return true;
    }
  }
  return false;
}
bool LcdPicture(char Color, short X, short Y, IP pBitmap)
{
  if (!LcdInitialized())
    return false;
  dLcdDrawBitmap(LCDInstance.pLcd, Color, X, Y, pBitmap);
  LCDInstance.Dirty = true;
  return true;
}
bool LcdFillWindow(char Color, short Y, short Y1)
{
  if (!LcdInitialized())
    return false;
  short Y2, Y3, Tmp;
  
  LCDInstance.currentFont = FONTTYPE_NORMAL;
  if ((Y + Y1) < LCD_HEIGHT)
  {
    if ((Color == 0) || (Color == 1))
    {
      Y *= LCD_BYTE_WIDTH;
      if (Y1)
      {
        Y1 *= LCD_BYTE_WIDTH;
      }
      else
      {
        Y1 = LCD_BUFFER_SIZE - Y;
      }
      if (Color)
      {
        Color = -1;
      }
      memset(&(LCDInstance.pLcd[Y]), Color, Y1);
    }
    else
    {
      if (Y1 == 0)
      {
        Y1 = LCD_HEIGHT;
      }
      Y2 = LCD_BYTE_WIDTH;
      for (Tmp = Y;Tmp < Y1;Tmp++)
      {
        Y3 = Tmp * LCD_BYTE_WIDTH;
        memset(&(LCDInstance.pLcd[Y3]), Color, Y2);
        Color = ~Color;
      }
    }
  }
  LCDInstance.Dirty = true;
  return true;
}
//****************************************************************************
//****************************************************************************
//****************************************************************************
#define TRANSLATE_Y(_Y) (LCD_HEIGHT - 1 - (_Y))
// OP codes supported by RIC files
enum {
  IMG_DESCRIPTION_ID = 0, // Ignored at this time
  IMG_SPRITE_ID = 1,
  IMG_VARMAP_ID = 2,
  IMG_COPYBITS_ID = 3,
  IMG_PIXEL_ID = 4,
  IMG_LINE_ID = 5,
  IMG_RECTANGLE_ID = 6,
  IMG_CIRCLE_ID = 7,
  IMG_NUMBOX_ID = 8,
  IMG_ELLIPSE_ID = 9,
  IMG_POLYGON_ID = 10
};
#define IMG_SYMB_USEARGS(_v) (_v & (SWORD)0xF000)
#define IMG_SYMB_MAP(_v)  ((_v & 0x0F00) >> 8)
#define IMG_SYMB_ARG(_v)  (_v & 0x00FF)
int DisplayLineHeight()
{
  // return the height of a character in the current font
  return dLcdGetFontHeight(LCDInstance.currentFont);
}
void DisplayEraseLine(byte Line)
{
  int cnt = DisplayLineHeight()*LCD_BYTE_WIDTH;
  memset((void*)&(LCDInstance.pLcd[Line*cnt]), 0, cnt);
  LCDInstance.Dirty = true;
}
void DisplayErase()
{
  memset((void*)LCDInstance.pLcd, 0, LCD_BUFFER_SIZE);
  LCDInstance.Dirty = true;
}
void DisplaySetPixel(byte X, byte Y)
{
  if ((X < LCD_WIDTH) && (Y < LCD_HEIGHT))
  {
    LCDInstance.pLcd[(X / 8) + (LCD_BYTE_WIDTH * Y)] |= (1 << (X % 8));
    LCDInstance.Dirty = true;
  }
}
void DisplayClrPixel(byte X, byte Y)
{
  if ((X < LCD_WIDTH) && (Y < LCD_HEIGHT))
  {
    LCDInstance.pLcd[(X / 8) + (LCD_BYTE_WIDTH * Y)] &= ~(1 << (X % 8));
    LCDInstance.Dirty = true;
  }
}
void DisplayXorPixel(byte X, byte Y)
{
  if ((X < LCD_WIDTH) && (Y < LCD_HEIGHT))
  {
    LCDInstance.pLcd[(X / 8) + (LCD_BYTE_WIDTH * Y)] ^= (1 << (X % 8));
    LCDInstance.Dirty = true;
  }
}
byte Masks[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
void DisplayFillScreen(byte StartX, byte StartY, byte PixelsX, byte PixelsY, byte PixelMode)
{
  byte X1, Y1, X2, Y2, X, Y, M;
  byte* pDst;
  X1 = StartX;
  Y1 = StartY;
  X2 = (byte)((int)StartX + (int)PixelsX - 1);
  Y2 = (byte)((int)StartY + (int)PixelsY - 1);
  if (X2 > (LCD_WIDTH-1))
    X2 = (LCD_WIDTH-1);
  if (Y2 > (LCD_HEIGHT-1))
    Y2 = (LCD_HEIGHT-1);
  X = (X1 / 8) * 8; // multiple of 8
  while (X <= X2)
  {
    M = 0xff;
    if (X < X1)
      M &= ~Masks[X1 % 8];
    if ((X2-X) < 8)
      M &= Masks[(X2 % 8) + 1];
    pDst = &(LCDInstance.pLcd[(X / 8) + (LCD_BYTE_WIDTH * Y1)]);
    switch (PixelMode)
    {
      case DRAW_PIXELS_INVERT:
        for (Y = Y1; Y <= Y2; Y++)
        {
          *pDst ^= M;
          pDst += LCD_BYTE_WIDTH;
        }
        break;
      case DRAW_PIXELS_CLEAR:
        for (Y = Y1; Y <= Y2; Y++)
        {
          *pDst &= ~M;
          pDst += LCD_BYTE_WIDTH;
        }
        break;
      default:
        for (Y = Y1; Y <= Y2; Y++)
        {
          *pDst |= M;
          pDst += LCD_BYTE_WIDTH;
        }
        break;
    }
    X += 8;
  }
  LCDInstance.Dirty = true;
}
void DisplayLineX(byte X1, byte X2, byte Y, byte PixelMode)
{
  byte X, M, t;
  byte* pDst;
  if (Y > LCD_HEIGHT) return;
  if (X1 > X2)
  {
    t = X1; X1 = X2; X2 = t;
  }
  if (X2 > (LCD_WIDTH-1))
    X2 = (LCD_WIDTH-1);
  // starting point of X is the byte containing X1
  X = (X1 / 8) * 8;
  while (X <= X2)
  {
    M = 0xff;
    if (X < X1)
      M &= ~Masks[X1 % 8];
    if ((X2-X) < 8)
      M &= Masks[(X2 % 8) + 1];
    pDst = &(LCDInstance.pLcd[(X / 8) + (LCD_BYTE_WIDTH * Y)]);
    switch (PixelMode)
    {
      case DRAW_PIXELS_INVERT:
        *pDst ^= M;
        break;
      case DRAW_PIXELS_CLEAR:
        *pDst &= ~M;
        break;
      default:
        *pDst |= M;
        break;
    }
    X += 8;
  }
  LCDInstance.Dirty = true;
}
void DisplayLineY(byte X, byte Y1, byte Y2, byte PixelMode)
{
  byte Y, M, t;
  byte* pDst;
  if (X > LCD_WIDTH) return;
  if (Y1 > Y2)
  {
    t = Y1; Y1 = Y2; Y2 = t;
  }
  if (Y2 > (LCD_HEIGHT-1))
    Y2 = (LCD_HEIGHT-1);
  M = 1 << (X % 8);
  pDst = &(LCDInstance.pLcd[(X / 8) + (LCD_BYTE_WIDTH * Y1)]);
  for (Y = Y1; Y <= Y2; Y++)
  {
    switch (PixelMode)
    {
      case DRAW_PIXELS_INVERT:
        *pDst ^= M;
        break;
      case DRAW_PIXELS_CLEAR:
        *pDst &= ~M;
        break;
      default:
        *pDst |= M;
        break;
    }
    pDst += LCD_BYTE_WIDTH;
  }
  LCDInstance.Dirty = true;
}
void DisplayFrame(byte StartX, byte StartY, byte PixelsX, byte PixelsY, byte PixelMode)
{
  DisplayLineX(StartX, StartX + PixelsX-1, StartY, PixelMode);
  if (PixelsY > 1)
  {
    DisplayLineY(StartX, StartY + 1, StartY + PixelsY - 1, PixelMode);
    DisplayLineY(StartX + PixelsX - 1, StartY + 1, StartY + PixelsY - 1, PixelMode);
  }
  LCDInstance.Dirty = true;
}
void DisplayDraw(byte Cmd, byte PixelMode, byte X1, byte Y1, byte X2, byte Y2)
{
  if (!LcdInitialized())
    return;
    
  switch (Cmd)
  {
    case DISPLAY_ERASE_ALL :
      DisplayErase();
      break;
    case DISPLAY_PIXEL :
      switch (PixelMode)
      {
        case DRAW_PIXELS_INVERT:
          DisplayXorPixel(X1, Y1);
          break;
        case DRAW_PIXELS_CLEAR:
          DisplayClrPixel(X1, Y1);
          break;
        default:
          DisplaySetPixel(X1, Y1);
          break;
      }
      break;
    case DISPLAY_HORIZONTAL_LINE :
      DisplayLineX(X1,X2,Y1,PixelMode);
      break;
    case DISPLAY_VERTICAL_LINE :
      DisplayLineY(X1,Y1,Y2,PixelMode);
      break;
    case DISPLAY_CHAR :
//      DisplayChar(IOMapDisplay.pFont, PixelMode, X1, Y1, X2);
      break;
    case DISPLAY_ERASE_LINE :
      DisplayEraseLine(X1);
      break;
    case DISPLAY_FILL_REGION :
      DisplayFillScreen(X1, Y1, X2, Y2, PixelMode);
      break;
    case DISPLAY_FRAME :
      DisplayFrame(X1, Y1, X2, Y2, PixelMode);
      break;
  }
}
bool CmdResolveDrawingMode(unsigned short DrawingOptions, byte* pPixelMode, byte* pFillMode)
{
  // Extract shape fill option:
  if (DrawingOptions & DRAW_OPT_FILL_SHAPE)
    *pFillMode = DRAW_SHAPE_FILLED;
  else
    *pFillMode = DRAW_SHAPE_HOLLOW;
  // Extract pixel drawing options:
  if (DrawingOptions & DRAW_OPT_INVERT)
  {
    //Drawing with white pixels:
    switch (DrawingOptions & DRAW_OPT_LOGICAL_OPERATIONS)
    {
      //Only these cases do anything:
      case DRAW_OPT_LOGICAL_COPY:
      case DRAW_OPT_LOGICAL_AND:
        *pPixelMode = DRAW_PIXELS_CLEAR;
        return true;
    }
  }
  else
  {
    //Drawing with black pixels:
    switch (DrawingOptions & DRAW_OPT_LOGICAL_OPERATIONS)
    {
      //Only these cases do anything:
      case DRAW_OPT_LOGICAL_COPY:
      case DRAW_OPT_LOGICAL_OR:
          *pPixelMode = DRAW_PIXELS_SET;
          return true;
      case DRAW_OPT_LOGICAL_XOR:
          *pPixelMode = DRAW_PIXELS_INVERT;
          return true;
    }
  }
  // If no operation is required, set defaults and return FALSE.
  // e.g. 'AND' on its own is meaningless for line drawing,
  // 'INVERT + OR' and 'INVERT + XOR'  do nothing either.
  *pPixelMode = DRAW_PIXELS_SET;
  *pFillMode  = DRAW_SHAPE_HOLLOW;
  return false;
}
void CmdSetPixel(int X, int Y, byte PixelMode)
{
  Y = TRANSLATE_Y(Y);
  if ((X >= 0) && (X < LCD_WIDTH) && (Y >= 0) && (Y < LCD_HEIGHT))
    DisplayDraw(DISPLAY_PIXEL, PixelMode, (byte)X, (byte)Y, 0, 0);
}
void CmdDrawLine(int x1, int y1, int x2, int y2, byte PixelMode)
{
  int tx, ty, dx, dy;
  int d, x, y, ax, ay, sx, sy;
  
  dx = x2-x1;
  dy = y2-y1;
  //Clip line ends vertically - easier if y1<y2:
  if (y1 > y2)
  {
    tx = x1; x1 = x2; x2 = tx;
    ty = y1; y1 = y2; y2 = ty;
  }
  //Is line completely off screen?
  if ((y2 < 0) || (y1 >= LCD_HEIGHT)) return;
  //Trim y1 end:
  if (y1 < 0)
  {
    if ((dx != 0) && (dy != 0))
      x1 = x1 + (((0-y1)*dx) / dy);
    y1 = 0;
  }
  //Trim y2 end:
  if (y2 > LCD_HEIGHT-1)
  {
    if ((dx != 0) && (dy != 0))
      x2 = x2 - (((y2-(LCD_HEIGHT-1))*dx) / dy);
    y2 = LCD_HEIGHT-1;
  }
  //Clip horizontally - easier if x1<x2
  if (x1 > x2)
  {
    tx = x1; x1 = x2; x2 = tx;
    ty = y1; y1 = y2; y2 = ty;
  }
  //Is line completely off screen?
  if ((x2 < 0) || (x1 >= LCD_WIDTH)) return;
  //Trim x1 end:
  if (x1 < 0)
  {
    if ((dx != 0) && (dy != 0))
      y1 = y1 + (((0-x1)*dy) / dx);
    x1 = 0;
  }
  //Trim x2 end:
  if (x2 > LCD_WIDTH-1)
  {
    if ((dx != 0) && (dy != 0))
      y2 = y2 - (((x2-(LCD_WIDTH-1))*dy) / dx);
    x2 = LCD_WIDTH-1;
  }
  if (x1 == x2)
  {
    // vertical line or a single point
    if (y1 == y2)
    {
      DisplayDraw(DISPLAY_PIXEL, PixelMode, (byte)x1, TRANSLATE_Y(y1), 0, 0);   //JJR
    }
    else
    {
      DisplayDraw(DISPLAY_VERTICAL_LINE, PixelMode, x1, TRANSLATE_Y(y1), 0, TRANSLATE_Y(y2));
    }
  }
  else if (y1 == y2)
  {
    // horizontal line (single point already dealt with)
    DisplayDraw(DISPLAY_HORIZONTAL_LINE, PixelMode, x1, TRANSLATE_Y(y1), x2, 0);   //JJR
  }
  else
  {
    // Initialize variables
    dx = x2-x1;
    ax = ABS(dx) << 1;
    sx = SGN(dx);
    dy = y2-y1;
    ay = ABS(dy) << 1;
    sy = SGN(dy);
    x  = x1;
    y  = y1;
    if (ax > ay)
    {    // x dominant
      d = ay - (ax >> 1);
      while (true)
      {
        DisplayDraw(DISPLAY_PIXEL, PixelMode, (byte)x, TRANSLATE_Y(y), 0, 0);   //JJR
        if (x == x2) return;
        if (d >= 0)
        {
          y += sy;
          d -= ax;
        }
        x += sx;
        d += ay;
      }
    }
    else
    {      // y dominant
      d = ax - (ay >> 1);
      while (true)
      {
        DisplayDraw(DISPLAY_PIXEL, PixelMode, (byte)x, TRANSLATE_Y(y), 0, 0);   //JJR
        if (y == y2) return;
        if (d >= 0)
        {
          x += sx;
          d -= ay;
        }
        y += sy;
        d += ax;
      }
    }
  }
}
void CmdDrawRect(int left, int bottom, int width, int height, byte PixelMode, byte FillMode)
{
  int x1 = left;
  int x2 = left + width;
  int y1 = bottom;
  int y2 = bottom + height;
  int t;
  
  if (x1 > x2)
  {
    t = x1; x1 = x2; x2 = t;
  }
  if (y1 > y2)
  {
    t = y1; y1 = y2; y2 = t;
  }
  if ((y2 == y1) || (x2 == x1))
  {
    // height == 0 so draw a single pixel horizontal line OR
    // width == 0 so draw a single pixel vertical line
    CmdDrawLine(x1, y1, x2, y2, PixelMode);
    return;
  }
  // rectangle has abs(width) or abs(height) >= 1
  if (FillMode == DRAW_SHAPE_FILLED)
  {
    if ((x2 < 0) || (y2 < 0) || (x1 > LCD_WIDTH-1) || (y1 > LCD_HEIGHT-1))
      return;
    if (x1 < 0)
      x1 = 0;
    if (y1 < 0)
      y1 = 0;
    if (x2 > LCD_WIDTH-1)
      x2 = LCD_WIDTH-1;
    if (y2 > LCD_HEIGHT-1)
      y2 = LCD_HEIGHT-1;
    DisplayDraw(DISPLAY_FILL_REGION, PixelMode, x1, TRANSLATE_Y(y2), x2-x1+1, y2-y1+1);
  }
  else
  {
    //Use the full line drawing functions rather than horizontal/vertical
    //functions so these get clipped properly.  These will fall straight
    //through to the faster functions anyway.
    //Also don't re-draw parts of slim rectangles since XOR might be on.
    CmdDrawLine(x1, y1, x2, y1, PixelMode);
    if (y2 > y1)
    {
      CmdDrawLine(x1, y2, x2, y2, PixelMode);
      if (y2 > y1+1)
      {
        CmdDrawLine(x2, y1+1, x2, y2-1, PixelMode);
        if (x2 > x1)
          CmdDrawLine(x1, y1+1, x1, y2-1, PixelMode);
      }
    }
  }
}
void CmdDrawEllipse(short xc, short yc, short a, short b, byte PixelMode, byte FillMode)
{
//			(* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 *)
  short x  = 0;
  short y  = b;
  short rx = x;
  short ry = y;
  unsigned short width = 1;
  unsigned short height = 1;
  int a2 = (int)a*(int)a;
  int b2 = (int)b*(int)b;
  int crit1 = -((a2 / 4) + (a % 2) + b2);
  int crit2 = -((b2 / 4) + (b % 2) + a2);
  int crit3 = -((b2 / 4) + (b % 2));
  int t = -a2*y; //(* e(x+1/2,y-1/2) - (a^2+b^2)/4 *)
  int dxt  = 2*b2*x;
  int dyt  = -2*a2*y;
  int d2xt = 2*b2;
  int d2yt = 2*a2;
  if (b == 0)
  {
    CmdDrawRect(xc-a, yc, 2*a, 0, PixelMode, FillMode);
    return;
  }
  if (a == 0)
  {
    CmdDrawRect(xc, yc-b, 0, 2*b, PixelMode, FillMode);
    return;
  }
  while ((y >= 0) && (x <= a))
  {
    if (FillMode != DRAW_SHAPE_FILLED)
    {
      CmdSetPixel(xc+x, yc+y, PixelMode);
      if ((x != 0) || (y != 0))
        CmdSetPixel(xc-x, yc-y, PixelMode);
      if ((x != 0) && (y != 0))
      {
        CmdSetPixel(xc+x, yc-y, PixelMode);
        CmdSetPixel(xc-x, yc+y, PixelMode);
      }
    }
    if ((t + b2*x <= crit1) ||     //(* e(x+1,y-1/2) <= 0 *)
        (t + a2*y <= crit3))       //(* e(x+1/2,y) <= 0 *)
    {
      if (FillMode == DRAW_SHAPE_FILLED)
      {
        if (height == 1)
        {
          ; //(* draw nothing *)
        }
        else if (ry*2+1 > (height-1)*2)
        {
          CmdDrawRect(xc-rx, yc-ry, width-1, height-1, PixelMode, FillMode);
          CmdDrawRect(xc-rx, yc+ry, width-1, -(height-1), PixelMode, FillMode);
          ry -= (height-1);
          height = 1;
        }
        else
        {
          CmdDrawRect(xc-rx, yc-ry, width-1, ry*2, PixelMode, FillMode);
          ry -= ry;
          height = 1;
        }
        rx++;
        width += 2;
      }
      x++;
      dxt += d2xt;
      t   += dxt;
    }
    else if (t - a2*y > crit2) //(* e(x+1/2,y-1) > 0 *)
    {
      y--;
      dyt += d2yt;
      t   += dyt;
      if (FillMode == DRAW_SHAPE_FILLED)
        height++;
    }
    else
    {
      if (FillMode == DRAW_SHAPE_FILLED)
      {
        if (ry*2+1 > height*2)
        {
          CmdDrawRect(xc-rx, yc-ry, width-1, height-1, PixelMode, FillMode);
          CmdDrawRect(xc-rx, yc+ry, width-1, -(height-1), PixelMode, FillMode);
        }
        else
        {
          CmdDrawRect(xc-rx, yc-ry, width-1, ry*2, PixelMode, FillMode);
        }
        width += 2;
        ry -= height;
        height = 1;
        rx++;
      }
      x++;
      dxt += d2xt;
      t   += dxt;
      y--;
      dyt += d2yt;
      t   += dyt;
    }
  }
  if (FillMode == DRAW_SHAPE_FILLED)
  {
    if (ry > height)
    {
      CmdDrawRect(xc-rx, yc-ry, width-1, height-1, PixelMode, FillMode);
      CmdDrawRect(xc-rx, yc+ry, width-1, -(height-1), PixelMode, FillMode);
    }
    else
    {
      CmdDrawRect(xc-rx, yc-ry, width-1, ry*2, PixelMode, FillMode);
    }
  }
}
void CmdDrawCircle(int cx, int cy, int radius, byte PixelMode, byte FillMode)
{
  CmdDrawEllipse(cx, cy, radius, radius, PixelMode, FillMode);
}
char CircleOutEx(int x, int y, byte radius, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
  byte pixelMode, fillMode;
  if (CmdResolveDrawingMode(options, &pixelMode, &fillMode))
    CmdDrawCircle(x, y, radius, pixelMode, fillMode);
  return 0;
}
char LineOutEx(int x1, int y1, int x2, int y2, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
  byte pixelMode, fillMode;
  if (CmdResolveDrawingMode(options, &pixelMode, &fillMode))
    CmdDrawLine(x1, y1, x2, y2, pixelMode);
  return 0;
}
char PointOutEx(int x, int y, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
  byte pixelMode, fillMode;
  if (CmdResolveDrawingMode(options, &pixelMode, &fillMode))
    CmdSetPixel(x, y, pixelMode);
  return 0;
}
char RectOutEx(int x, int y, int width, int height, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
  byte pixelMode, fillMode;
  if (CmdResolveDrawingMode(options, &pixelMode, &fillMode))
    CmdDrawRect(x, y, width, height, pixelMode, fillMode);
  return 0;
}
char TextOutEx(int x, int y, char* str, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
//  byte pixelMode, fillMode;
//  if (CmdResolveDrawingMode(options, &pixelMode, &fillMode))
//    CmdDrawRect(x, y, width, height, pixelMode);
  return 0;
}
char NumOutEx(int x, int y, int value, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
  return 0;
}
char EllipseOutEx(int x, int y, byte radiusX, byte radiusY, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
  byte pixelMode, fillMode;
  if (CmdResolveDrawingMode(options, &pixelMode, &fillMode))
    CmdDrawEllipse(x, y, radiusX, radiusY, pixelMode, fillMode);
  return 0;
}
char PolyOutEx(PLocationType points, unsigned long options)
{
  return 0;
}
char GraphicOutEx(int x, int y, char* filename, unsigned long options)
{
  return 0;
}
char GraphicArrayOutEx(int x, int y, byte* data, unsigned long options)
{
  return 0;
}