diff packages/hal/arm/arm9/aaed2000/current/src/lcd_support.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children d2c90368aeef
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/hal/arm/arm9/aaed2000/current/src/lcd_support.c
@@ -0,0 +1,999 @@
+//==========================================================================
+//
+//        Lcd_support.c
+//
+//        Agilent AAED2000 - LCD support routines
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     gthomas
+// Contributors:  gthomas
+// Date:          2001-11-03
+// Description:   Simple LCD support
+//####DESCRIPTIONEND####
+
+#include <pkgconf/hal.h>
+
+#include <cyg/infra/diag.h>
+#include <cyg/hal/hal_io.h>       // IO macros
+#include <cyg/hal/hal_if.h>       // Virtual vector support
+#include <cyg/hal/hal_arch.h>     // Register state info
+#include <cyg/hal/hal_intr.h>     // HAL interrupt macros
+
+#include <cyg/hal/aaed2000.h>  // Board definitions
+#include <cyg/hal/lcd_support.h>
+#include <cyg/hal/hal_cache.h>
+
+#include <string.h>
+
+#ifdef CYGPKG_ISOINFRA
+# include <pkgconf/isoinfra.h>
+# ifdef CYGINT_ISO_STDIO_FORMATTED_IO
+#  include <stdio.h>  // sscanf
+# endif
+#endif
+
+#include CYGHWR_MEMORY_LAYOUT_H
+#define LCD_FRAMEBUFFER CYGMEM_REGION_lcd
+static cyg_uint32 lcd_framebuffer;
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE  1
+#endif
+
+// Physical dimensions of LCD display
+#define DISPLAY_WIDTH  640
+#define DISPLAY_HEIGHT 480
+
+// Logical layout
+#ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+#define LCD_WIDTH  DISPLAY_HEIGHT
+#define LCD_HEIGHT DISPLAY_WIDTH
+#else
+#define LCD_WIDTH  DISPLAY_WIDTH
+#define LCD_HEIGHT DISPLAY_HEIGHT
+#endif
+#define LCD_DEPTH   16
+
+#define RGB_RED(x)   (((x)&0x1F)<<0)
+#define RGB_GREEN(x) (((x)&0x1F)<<5)
+#define RGB_BLUE(x)  (((x)&0x1F)<<10)
+
+// Physical screen info
+//static int lcd_depth  = LCD_DEPTH;  // Should be 1, 2, or 4
+static int lcd_bpp;
+#ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+static int lcd_width  = LCD_WIDTH;
+#endif
+static int lcd_height = LCD_HEIGHT;
+
+// Black on light blue
+static int bg = RGB_RED(0x17) | RGB_GREEN(0x17) | RGB_BLUE(0x1F);
+#ifdef CYGSEM_AAED2000_LCD_COMM
+static int fg = RGB_RED(0) | RGB_GREEN(0) | RGB_BLUE(0);
+#endif
+
+// Compute the location for a pixel within the framebuffer
+static cyg_uint32 *
+lcd_fb(int row, int col)
+{
+    return (cyg_uint32 *)(lcd_framebuffer+(((row*DISPLAY_WIDTH)+col)*2));
+}
+
+void
+lcd_on(bool enable)
+{
+    cyg_uint32 ctl;
+
+    if (!enable) return;  // FIXME - need a way to [safely] blank screen
+
+    // Turn on PWM [pulse wave modulated] voltage pump
+    HAL_WRITE_UINT32(AAEC_PUMP_CONTROL,    0x800);  // Magic FIXME
+    HAL_WRITE_UINT32(AAEC_PUMP_FREQUENCY, 0x3733);  // Magic FIXME
+    // Turn on Power
+    HAL_READ_UINT32(AAED_EXT_GPIO, ctl);  
+    ctl |= AAED_EXT_GPIO_LCD_PWR_EN;
+    HAL_WRITE_UINT32(AAED_EXT_GPIO, ctl);  
+    // Power must be on for 0..20ms before enabling signals
+    CYGACC_CALL_IF_DELAY_US(500);  // 500us should be long enough
+    HAL_READ_UINT32(AAEC_LCD_CONTROL, ctl);
+    ctl |= AAEC_LCD_CONTROL_ENAB | AAEC_LCD_CONTROL_PWR_ENAB;
+    HAL_WRITE_UINT32(AAEC_LCD_CONTROL, ctl);
+}
+
+// Initialize LCD hardware
+
+void
+lcd_init(int depth)
+{
+    cyg_uint32 ctl;
+
+    HAL_VIRT_TO_PHYS_ADDRESS(LCD_FRAMEBUFFER, lcd_framebuffer);
+    lcd_clear();
+    HAL_READ_UINT32(AAEC_LCD_CONTROL, ctl);
+    if (ctl & (AAEC_LCD_CONTROL_ENAB | AAEC_LCD_CONTROL_PWR_ENAB)) {
+        // LCD currently on - turn it off carefully
+        ctl &= ~(AAEC_LCD_CONTROL_ENAB | AAEC_LCD_CONTROL_PWR_ENAB);
+        HAL_WRITE_UINT32(AAEC_LCD_CONTROL, ctl);
+        // Now wait a little
+        CYGACC_CALL_IF_DELAY_US(500);
+        // Turn off Power
+        HAL_READ_UINT32(AAED_EXT_GPIO, ctl);  
+        ctl &= ~AAED_EXT_GPIO_LCD_PWR_EN;
+        HAL_WRITE_UINT32(AAED_EXT_GPIO, ctl);  
+        // Now wait for 1.0 second
+        CYGACC_CALL_IF_DELAY_US(1000*1000);
+    }
+    HAL_WRITE_UINT32(AAEC_LCD_CONTROL, 0x00010028);   // Magic FIXME
+    HAL_WRITE_UINT32(AAEC_LCD_TIMING0, 0x2B135F9C);   // Magic FIXME
+    HAL_WRITE_UINT32(AAEC_LCD_TIMING1, 0x211401DF);   // Magic FIXME
+    HAL_WRITE_UINT32(AAEC_LCD_TIMING2, 0x067F1820);   // Magic FIXME
+    HAL_WRITE_UINT32(AAEC_LCD_TIMING3, 0x00000000);   // Magic FIXME
+    HAL_WRITE_UINT32(AAEC_LCD_UPBASE,  lcd_framebuffer);
+    HAL_WRITE_UINT32(AAEC_LCD_LPBASE,  lcd_framebuffer);
+#if 0 // For some reason, this crashes miserably
+    lcd_framebuffer = LCD_FRAMEBUFFER;  // Logical operations use cached space
+#endif
+    lcd_on(true);
+    lcd_bpp = 16;
+}
+
+// Get information about the frame buffer
+int
+lcd_getinfo(struct lcd_info *info)
+{
+    if (lcd_bpp == 0) {
+        return 0;  // LCD not initialized
+    }
+    info->width = DISPLAY_WIDTH;
+    info->height = DISPLAY_HEIGHT;
+    info->bpp = lcd_bpp;
+    info->fb = (void*)LCD_FRAMEBUFFER;
+    info->rlen = DISPLAY_WIDTH * 2;
+    info->type = FB_TRUE_RGB555;
+    return 1; // Information valid
+}
+
+// Clear screen
+void
+lcd_clear(void)
+{
+#ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+    cyg_uint32 *fb_row0, *fb_rown;
+    cyg_uint32 _bg = (bg<<16)|bg;
+
+    fb_row0 = lcd_fb(0, 0);
+    fb_rown = lcd_fb(lcd_height, 0);
+    while (fb_row0 != fb_rown) {
+        *fb_row0++ = _bg;
+    }
+#else
+    int row, col;
+    for (row = 0;  row < lcd_height;  row++) {
+        for (col = 0;  col < lcd_width;  col++) {
+            set_pixel(row, col, bg);
+        }
+    }
+#endif
+}
+
+#ifdef CYGSEM_AAED2000_LCD_COMM
+
+//
+// Additional support for LCD/Keyboard as 'console' device
+//
+
+#ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
+#include "banner.xpm"
+#endif
+#include "font.h"
+
+// Virtual screen info
+static int curX = 0;  // Last used position
+static int curY = 0;
+//static int width = LCD_WIDTH / (FONT_WIDTH*NIBBLES_PER_PIXEL);
+//static int height = LCD_HEIGHT / (FONT_HEIGHT*SCREEN_SCALE);
+
+#define SCREEN_PAN            20
+#define SCREEN_WIDTH          80
+#define SCREEN_HEIGHT         (LCD_HEIGHT/FONT_HEIGHT)
+#define VISIBLE_SCREEN_WIDTH  (LCD_WIDTH/FONT_WIDTH)
+#define VISIBLE_SCREEN_HEIGHT (LCD_HEIGHT/FONT_HEIGHT)
+static char screen[SCREEN_HEIGHT][SCREEN_WIDTH];
+static int screen_height = SCREEN_HEIGHT;
+static int screen_width = SCREEN_WIDTH;
+static int screen_pan = 0;
+
+// Usable area on screen [logical pixel rows]
+static int screen_start = 0;                       
+static int screen_end = LCD_HEIGHT/FONT_HEIGHT;
+
+static bool cursor_enable = true;
+
+// Functions
+static void lcd_drawc(cyg_int8 c, int x, int y);
+
+// Note: val is a 16 bit, RGB555 value which must be mapped
+// onto a 12 bit value.
+#define RED(v)   ((v>>12) & 0x0F)
+#define GREEN(v) ((v>>7) & 0x0F)
+#define BLUE(v)  ((v>>1) & 0x0F)
+#ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+#error PORTRAIT MODE NOT IMPLEMENTED
+// Translate coordinates, rotating clockwise 90 degrees
+static void
+set_pixel(int row, int col, unsigned short val)
+{
+//    fp->pixels[col][(DISPLAY_WIDTH-1)-row] = val;
+    int _row = (240-1) - col;
+    int _col = row;
+    unsigned char *pxptr = (unsigned char *)(0xC0000000 + (_row * 480) + ((_col * 3) / 2));
+
+    diag_printf("%s\n", __FUNCTION__);  return;
+    if ((row >= LCD_HEIGHT) || (col >= LCD_WIDTH)) return;
+    if (0)
+    {
+        int old = start_console();
+        diag_printf("row=%d/%d, col=%d/%d, pxptr = %p\n", row, _row, col, _col, pxptr);
+        end_console(old);
+    }
+    if ((row % 2) == 0) {
+        // Even row
+        *pxptr++ = RED(val) | (GREEN(val) << 4);
+        *pxptr = (*pxptr & 0xF0) | BLUE(val);   
+    } else {
+        // Odd row
+        *pxptr = (*pxptr & 0x0F) | (RED(val) << 4);
+        *++pxptr = GREEN(val) | (BLUE(val) << 4);
+    }
+}
+#else
+
+static void
+set_pixel(int row, int col, unsigned short val)
+{
+    unsigned short *pix = (unsigned short *)lcd_fb(row, col);
+    *pix = val;
+}
+#endif
+
+static int
+_hexdigit(char c)
+{
+    if ((c >= '0') && (c <= '9')) {
+        return c - '0';
+    } else
+    if ((c >= 'A') && (c <= 'F')) {
+        return (c - 'A') + 0x0A;
+    } else
+    if ((c >= 'a') && (c <= 'f')) {
+        return (c - 'a') + 0x0a;
+    }
+
+    return 0;
+}
+
+static int
+_hex(char *cp)
+{
+    return (_hexdigit(*cp)<<4) | _hexdigit(*(cp+1));
+}
+
+static unsigned short
+parse_color(char *cp)
+{
+    int red, green, blue;
+
+    while (*cp && (*cp != 'c')) cp++;
+    if (cp) {
+        cp += 2;
+        if (*cp == '#') {
+            red = _hex(cp+1);
+            green = _hex(cp+3);
+            blue = _hex(cp+5);
+#ifdef USE_RGB565
+            return RGB_RED(red>>3) | RGB_GREEN(green>>2) | RGB_BLUE(blue>>3);
+#else
+            return RGB_RED(red>>3) | RGB_GREEN(green>>3) | RGB_BLUE(blue>>3);
+#endif
+        } else {
+            // Should be "None"
+            return 0xFFFF;
+        }
+    } else {
+        return 0xFFFF;
+    }
+}
+
+#ifndef CYGINT_ISO_STDIO_FORMATTED_IO
+static int
+get_int(char **_cp)
+{
+    char *cp = *_cp;
+    char c;
+    int val = 0;
+    
+    while ((c = *cp++) && (c != ' ')) {
+        if ((c >= '0') && (c <= '9')) {
+            val = val * 10 + (c - '0');
+        } else {
+            return -1;
+        }
+    }
+    *_cp = cp;
+    return val;
+}
+#endif
+
+#ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
+int
+show_xpm(char *xpm[], int screen_pos)
+{
+    int i, row, col, offset;
+    char *cp;
+    int nrows, ncols, nclrs;
+    unsigned short colors[256];  // Mapped by character index
+
+    cp = xpm[0];
+#ifdef CYGINT_ISO_STDIO_FORMATTED_IO
+    if (sscanf(cp, "%d %d %d", &ncols, &nrows, &nclrs) != 3) {
+#else
+    if (((ncols = get_int(&cp)) < 0) ||
+        ((nrows = get_int(&cp)) < 0) ||
+        ((nclrs = get_int(&cp)) < 0)) {
+
+#endif
+        diag_printf("Can't parse XPM data, sorry\n");
+        return 0;
+    }
+    // printf("%d rows, %d cols, %d colors\n", nrows, ncols, nclrs);
+
+    for (i = 0;  i < 256;  i++) {
+        colors[i] = 0x0000;
+    }
+    for (i = 0;  i < nclrs;  i++) {
+        cp = xpm[i+1];
+        colors[(unsigned int)*cp] = parse_color(&cp[1]);
+        // printf("Color[%c] = %x\n", *cp, colors[(unsigned int)*cp]);
+    }
+
+#ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
+    offset = screen_pos;
+#else
+    offset = screen_pos-nrows;
+#endif
+    for (row = 0;  row < nrows;  row++) {            
+        cp = xpm[nclrs+1+row];        
+        for (col = 0;  col < ncols;  col++) {
+            set_pixel(row+offset, col, colors[(unsigned int)*cp++]);
+        }
+    }
+#ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
+    screen_start = (nrows + (FONT_HEIGHT-1))/FONT_HEIGHT;
+    screen_end = LCD_HEIGHT/FONT_HEIGHT;
+    return offset+nrows;
+#else    
+    screen_start = 0;
+    screen_height = offset / FONT_HEIGHT;
+    screen_end = screen_height;
+    return offset;
+#endif
+}
+#endif
+
+void
+lcd_screen_clear(void)
+{
+    int row, col, pos;
+    for (row = 0;  row < screen_height;  row++) {
+        for (col = 0;  col < screen_width;  col++) {
+            screen[row][col] = ' ';
+        }
+    }
+#ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
+    // Note: Row 0 seems to wrap incorrectly
+#ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
+    pos = 0;
+#else
+    pos = (LCD_HEIGHT-1);
+#endif
+    show_xpm(banner_xpm, pos);
+#endif // CYGOPT_AAED2000_LCD_COMM_LOGO
+    curX = 0;  curY = screen_start;
+    if (cursor_enable) {
+        lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
+    }
+}
+
+// Position cursor
+void
+lcd_moveto(int X, int Y)
+{
+    if (cursor_enable) {
+        lcd_drawc(screen[curY][curX], curX-screen_pan, curY);
+    }
+    if (X < 0) X = 0;
+    if (X >= screen_width) X = screen_width-1;
+    curX = X;
+    if (Y < screen_start) Y = screen_start;
+    if (Y >= screen_height) Y = screen_height-1;
+    curY = Y;
+    if (cursor_enable) {
+        lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
+    }
+}
+
+// Render a character at position (X,Y) with current background/foreground
+static void
+lcd_drawc(cyg_int8 c, int x, int y)
+{
+    cyg_uint8 bits;
+    int l, p;
+    int xoff, yoff;
+#ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+    cyg_uint32 *fb;
+#endif
+
+
+    if ((x < 0) || (x >= VISIBLE_SCREEN_WIDTH) || 
+        (y < 0) || (y >= screen_height)) return;  
+    for (l = 0;  l < FONT_HEIGHT;  l++) {
+        bits = font_table[c-FIRST_CHAR][l]; 
+        yoff = y*FONT_HEIGHT + l;
+        xoff = x*FONT_HEIGHT;
+#ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+        // Caution - only works for little-endian & font sizes multiple of 2
+        fb = lcd_fb(yoff, xoff);
+        for (p = 0;  p < FONT_WIDTH;  p += 2) {
+            switch (bits & 0x03) {
+            case 0:
+                *fb++ = (bg << 16) | bg;
+                break;
+            case 1:
+                *fb++ = (bg << 16) | fg;
+                break;
+            case 2:
+                *fb++ = (fg << 16) | bg;
+                break;
+            case 3:
+                *fb++ = (fg << 16) | fg;
+                break;
+            }
+            bits >>= 2;
+        }
+#else
+        for (p = 0;  p < FONT_WIDTH;  p++) {
+            set_pixel(yoff, xoff + p, (bits & 0x01) ? fg : bg);
+            bits >>= 1;
+        }
+#endif
+    }
+}
+
+static void
+lcd_refresh(void)
+{
+    int row, col;
+
+    for (row = screen_start;  row < screen_height;  row++) {
+        for (col = 0;  col < VISIBLE_SCREEN_WIDTH;  col++) {
+            if ((col+screen_pan) < screen_width) {
+                lcd_drawc(screen[row][col+screen_pan], col, row);
+            } else {
+                lcd_drawc(' ', col, row);
+            }
+        }
+    }
+    if (cursor_enable) {
+        lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
+    }
+}
+
+static void
+lcd_scroll(void)
+{
+    int col;
+    cyg_uint8 *c1;
+    cyg_uint32 *lc0, *lc1, *lcn;
+#ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+    cyg_uint32 *fb_row0, *fb_row1, *fb_rown;
+#endif
+
+    // First scroll up the virtual screen
+#if ((SCREEN_WIDTH%4) != 0)
+#error Scroll code optimized for screen with multiple of 4 columns
+#endif
+    lc0 = (cyg_uint32 *)&screen[0][0];
+    lc1 = (cyg_uint32 *)&screen[1][0];
+    lcn = (cyg_uint32 *)&screen[screen_height][0];
+    while (lc1 != lcn) {
+        *lc0++ = *lc1++;
+    }
+    c1 = &screen[screen_height-1][0];
+    for (col = 0;  col < screen_width;  col++) {
+        *c1++ = 0x20;
+    }
+#ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
+    // Redrawing the screen in this mode is hard :-)
+    lcd_refresh();
+#else
+    fb_row0 = lcd_fb(screen_start*FONT_HEIGHT, 0);
+    fb_row1 = lcd_fb((screen_start+1)*FONT_HEIGHT, 0);
+    fb_rown = lcd_fb(screen_end*FONT_HEIGHT, 0);
+#if 1
+    while (fb_row1 != fb_rown) {
+        *fb_row0++ = *fb_row1++;
+    }
+#else
+    // Optimized ARM assembly "move" code
+    asm __volatile(
+        "mov r0,%0;"
+        "mov r1,%1;"
+        "mov r2,%2;"
+        "10: ldmia r1!,{r3-r10};"
+        "stmia r0!,{r3-r10};"
+        "cmp r1,r2;"
+        "bne 10b"
+        :
+        : "r"(fb_row0), "r"(fb_row1), "r"(fb_rown)
+        : "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10"
+        );
+#endif
+    // Erase bottom line
+    for (col = 0;  col < screen_width;  col++) {
+        lcd_drawc(' ', col, screen_end-1);
+    }
+#endif
+}
+
+// Draw one character at the current position
+void
+lcd_putc(cyg_int8 c)
+{
+    if (cursor_enable) {
+        lcd_drawc(screen[curY][curX], curX-screen_pan, curY);
+    }
+    switch (c) {
+    case '\r':
+        curX = 0;
+        break;
+    case '\n':
+        curY++;
+        break;
+    case '\b':
+        curX--;
+        if (curX < 0) {
+            curY--;
+            if (curY < 0) curY = 0;
+            curX = screen_width-1;
+        }
+        break;
+    default:
+        if (((cyg_uint8)c < FIRST_CHAR) || ((cyg_uint8)c > LAST_CHAR)) c = '.';
+        screen[curY][curX] = c;
+        lcd_drawc(c, curX-screen_pan, curY);
+        curX++;
+        if (curX == screen_width) {
+            curY++;
+            curX = 0;
+        }
+    } 
+    if (curY >= screen_height) {
+        lcd_scroll();
+        curY = (screen_height-1);
+    }
+    if (cursor_enable) {
+        lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
+    }
+}
+
+// Basic LCD 'printf()' support
+
+#include <stdarg.h>
+
+#define is_digit(c) ((c >= '0') && (c <= '9'))
+
+static int
+_cvt(unsigned long val, char *buf, long radix, char *digits)
+{
+    char temp[80];
+    char *cp = temp;
+    int length = 0;
+    if (val == 0) {
+        /* Special case */
+        *cp++ = '0';
+    } else {
+        while (val) {
+            *cp++ = digits[val % radix];
+            val /= radix;
+        }
+    }
+    while (cp != temp) {
+        *buf++ = *--cp;
+        length++;
+    }
+    *buf = '\0';
+    return (length);
+}
+
+static int
+lcd_vprintf(void (*putc)(cyg_int8), const char *fmt0, va_list ap)
+{
+    char c, sign, *cp;
+    int left_prec, right_prec, zero_fill, length, pad, pad_on_right;
+    char buf[32];
+    long val;
+    while ((c = *fmt0++)) {
+        cp = buf;
+        length = 0;
+        if (c == '%') {
+            c = *fmt0++;
+            left_prec = right_prec = pad_on_right = 0;
+            if (c == '-') {
+                c = *fmt0++;
+                pad_on_right++;
+            }
+            if (c == '0') {
+                zero_fill = TRUE;
+                c = *fmt0++;
+            } else {
+                zero_fill = FALSE;
+            }
+            while (is_digit(c)) {
+                left_prec = (left_prec * 10) + (c - '0');
+                c = *fmt0++;
+            }
+            if (c == '.') {
+                c = *fmt0++;
+                zero_fill++;
+                while (is_digit(c)) {
+                    right_prec = (right_prec * 10) + (c - '0');
+                    c = *fmt0++;
+                }
+            } else {
+                right_prec = left_prec;
+            }
+            sign = '\0';
+            switch (c) {
+            case 'd':
+            case 'x':
+            case 'X':
+                val = va_arg(ap, long);
+                switch (c) {
+                case 'd':
+                    if (val < 0) {
+                        sign = '-';
+                        val = -val;
+                    }
+                    length = _cvt(val, buf, 10, "0123456789");
+                    break;
+                case 'x':
+                    length = _cvt(val, buf, 16, "0123456789abcdef");
+                    break;
+                case 'X':
+                    length = _cvt(val, buf, 16, "0123456789ABCDEF");
+                    break;
+                }
+                break;
+            case 's':
+                cp = va_arg(ap, char *);
+                length = strlen(cp);
+                break;
+            case 'c':
+                c = va_arg(ap, long /*char*/);
+                (*putc)(c);
+                continue;
+            default:
+                (*putc)('?');
+            }
+            pad = left_prec - length;
+            if (sign != '\0') {
+                pad--;
+            }
+            if (zero_fill) {
+                c = '0';
+                if (sign != '\0') {
+                    (*putc)(sign);
+                    sign = '\0';
+                }
+            } else {
+                c = ' ';
+            }
+            if (!pad_on_right) {
+                while (pad-- > 0) {
+                    (*putc)(c);
+                }
+            }
+            if (sign != '\0') {
+                (*putc)(sign);
+            }
+            while (length-- > 0) {
+                (*putc)(c = *cp++);
+                if (c == '\n') {
+                    (*putc)('\r');
+                }
+            }
+            if (pad_on_right) {
+                while (pad-- > 0) {
+                    (*putc)(' ');
+                }
+            }
+        } else {
+            (*putc)(c);
+            if (c == '\n') {
+                (*putc)('\r');
+            }
+        }
+    }
+
+    // FIXME
+    return 0;
+}
+
+int
+_lcd_printf(char const *fmt, ...)
+{
+    int ret;
+    va_list ap;
+
+    va_start(ap, fmt);
+    ret = lcd_vprintf(lcd_putc, fmt, ap);
+    va_end(ap);
+    return (ret);
+}
+
+void
+lcd_setbg(int red, int green, int blue)
+{
+    bg = RGB_RED(red) | RGB_GREEN(green) | RGB_BLUE(blue);
+}
+
+void
+lcd_setfg(int red, int green, int blue)
+{
+    fg = RGB_RED(red) | RGB_GREEN(green) | RGB_BLUE(blue);
+}
+
+//
+// Support LCD/keyboard (PS2) as a virtual I/O channel
+//   Adapted from i386/pcmb_screen.c
+//
+
+static int  _timeout = 500;
+
+static cyg_bool
+lcd_comm_getc_nonblock(void* __ch_data, cyg_uint8* ch)
+{
+    if( !aaed2000_KeyboardTest() )
+        return false;
+    *ch = aaed2000_KeyboardGetc();
+    return true;
+}
+
+static cyg_uint8
+lcd_comm_getc(void* __ch_data)
+{
+    cyg_uint8 ch;
+
+    while (!lcd_comm_getc_nonblock(__ch_data, &ch)) ;
+    return ch;
+}
+
+static void
+lcd_comm_putc(void* __ch_data, cyg_uint8 c)
+{
+    lcd_putc(c);
+}
+
+static void
+lcd_comm_write(void* __ch_data, const cyg_uint8* __buf, cyg_uint32 __len)
+{
+#if 0
+    CYGARC_HAL_SAVE_GP();
+
+    while(__len-- > 0)
+        lcd_comm_putc(__ch_data, *__buf++);
+
+    CYGARC_HAL_RESTORE_GP();
+#endif
+}
+
+static void
+lcd_comm_read(void* __ch_data, cyg_uint8* __buf, cyg_uint32 __len)
+{
+#if 0
+    CYGARC_HAL_SAVE_GP();
+
+    while(__len-- > 0)
+        *__buf++ = lcd_comm_getc(__ch_data);
+
+    CYGARC_HAL_RESTORE_GP();
+#endif
+}
+
+static cyg_bool
+lcd_comm_getc_timeout(void* __ch_data, cyg_uint8* ch)
+{
+    int delay_count;
+    cyg_bool res;
+
+    delay_count = _timeout * 2; // delay in .5 ms steps
+    for(;;) {
+        res = lcd_comm_getc_nonblock(__ch_data, ch);
+        if (res || 0 == delay_count--)
+            break;
+        CYGACC_CALL_IF_DELAY_US(500);
+    }
+    return res;
+}
+
+static int
+lcd_comm_control(void *__ch_data, __comm_control_cmd_t __func, ...)
+{
+    static int vector = 0;
+    int ret = -1;
+    static int irq_state = 0;
+
+    CYGARC_HAL_SAVE_GP();
+
+    switch (__func) {
+    case __COMMCTL_IRQ_ENABLE:
+        ret = irq_state;
+        irq_state = 1;
+        break;
+    case __COMMCTL_IRQ_DISABLE:
+        ret = irq_state;
+        irq_state = 0;
+        break;
+    case __COMMCTL_DBG_ISR_VECTOR:
+        ret = vector;
+        break;
+    case __COMMCTL_SET_TIMEOUT:
+    {
+        va_list ap;
+
+        va_start(ap, __func);
+
+        ret = _timeout;
+        _timeout = va_arg(ap, cyg_uint32);
+
+        va_end(ap);
+	break;
+    }
+    case __COMMCTL_FLUSH_OUTPUT:
+        ret = 0;
+	break;
+    default:
+        break;
+    }
+    CYGARC_HAL_RESTORE_GP();
+    return ret;
+}
+
+static int
+lcd_comm_isr(void *__ch_data, int* __ctrlc, 
+           CYG_ADDRWORD __vector, CYG_ADDRWORD __data)
+{
+#if 0
+    char ch;
+
+    cyg_drv_interrupt_acknowledge(__vector);
+    *__ctrlc = 0;
+    if (lcd_comm_getc_nonblock(__ch_data, &ch)) {
+        if (ch == 0x03) {
+            *__ctrlc = 1;
+        }
+    }
+    return CYG_ISR_HANDLED;
+#else
+    return 0;
+#endif
+}
+
+void
+lcd_comm_init(void)
+{
+    static int init = 0;
+
+    if (!init) {
+        hal_virtual_comm_table_t* comm;
+        int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
+
+        init = 1;
+        lcd_on(false);
+        if (!aaed2000_KeyboardInit()) {
+            // No keyboard - no LCD display
+            return;
+        }
+        // Initialize screen
+        cursor_enable = true;
+        lcd_init(16);
+        lcd_screen_clear();
+
+        // Setup procs in the vector table
+        CYGACC_CALL_IF_SET_CONSOLE_COMM(1);  // FIXME - should be controlled by CDL
+        comm = CYGACC_CALL_IF_CONSOLE_PROCS();
+        //CYGACC_COMM_IF_CH_DATA_SET(*comm, chan);
+        CYGACC_COMM_IF_WRITE_SET(*comm, lcd_comm_write);
+        CYGACC_COMM_IF_READ_SET(*comm, lcd_comm_read);
+        CYGACC_COMM_IF_PUTC_SET(*comm, lcd_comm_putc);
+        CYGACC_COMM_IF_GETC_SET(*comm, lcd_comm_getc);
+        CYGACC_COMM_IF_CONTROL_SET(*comm, lcd_comm_control);
+        CYGACC_COMM_IF_DBG_ISR_SET(*comm, lcd_comm_isr);
+        CYGACC_COMM_IF_GETC_TIMEOUT_SET(*comm, lcd_comm_getc_timeout);
+
+        // Restore original console
+        CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
+    }
+}
+
+#ifdef CYGPKG_REDBOOT
+#include <redboot.h>
+
+// Get here when RedBoot is idle.  If it's been long enough, then
+// dim the LCD.  The problem is - how to determine other activities
+// so at this doesn't get in the way.  In the default case, this will
+// be called from RedBoot every 10ms (CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT)
+
+#define MAX_IDLE_TIME (30*100)
+
+static void
+idle(bool is_idle)
+{
+    static int idle_time = 0;
+    static bool was_idled = false;
+
+    if (is_idle) {
+        if (!was_idled) {
+            if (++idle_time == MAX_IDLE_TIME) {
+                was_idled = true;
+                lcd_on(false);
+            }
+        }
+    } else {        
+        idle_time = 0;
+        if (was_idled) {
+            was_idled = false;
+                lcd_on(true);
+        }
+    }
+}
+
+RedBoot_idle(idle, RedBoot_AFTER_NETIO);
+#endif
+#endif