/** * @file * * @ingroup raspberrypi * * @brief displaying characters on the console */ /** * * Copyright (c) 2015 Yang Qiao * based on work by: * Copyright (C) 1998 Eric Valette (valette@crf.canon.fr) * Canon Centre Recherche France. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. * * Till Straumann , 2003/9: * - added handling of basic escape sequences (cursor movement * and erasing; just enough for the line editor 'libtecla' to * work...) * */ #include #include #include #include #include #include #include "font_data.h" static void wr_cursor( int r, int c ) { /* dummy function for now */ } #define TAB_SPACE 4 #define CONSOLE_BG_COL 0x00 #define CONSOLE_FG_COL 0xa0 static void *fb_mem = NULL; static unsigned short maxCol; static unsigned short maxRow; static unsigned short bytes_per_pixel; static unsigned int bytes_per_line; static unsigned int bytes_per_char_line; static unsigned char row; static unsigned char column; static unsigned int nLines; static uint32_t fgx, bgx, eorx; static int rpi_video_initialized; static const int video_font_draw_table32[ 16 ][ 4 ] = { { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x00000000, 0x00000000, 0x00000000, 0x00ffffff }, { 0x00000000, 0x00000000, 0x00ffffff, 0x00000000 }, { 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff }, { 0x00000000, 0x00ffffff, 0x00000000, 0x00000000 }, { 0x00000000, 0x00ffffff, 0x00000000, 0x00ffffff }, { 0x00000000, 0x00ffffff, 0x00ffffff, 0x00000000 }, { 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff }, { 0x00ffffff, 0x00000000, 0x00000000, 0x00000000 }, { 0x00ffffff, 0x00000000, 0x00000000, 0x00ffffff }, { 0x00ffffff, 0x00000000, 0x00ffffff, 0x00000000 }, { 0x00ffffff, 0x00000000, 0x00ffffff, 0x00ffffff }, { 0x00ffffff, 0x00ffffff, 0x00000000, 0x00000000 }, { 0x00ffffff, 0x00ffffff, 0x00000000, 0x00ffffff }, { 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00000000 }, { 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff } }; static void scroll( void ) { int i, j; /* Counters */ uint8_t *pt_scroll, *pt_bitmap; /* Pointers on the bit-map */ pt_bitmap = fb_mem; j = 0; pt_bitmap = pt_bitmap + j; pt_scroll = pt_bitmap + bytes_per_char_line; for ( i = j; i < maxRow - 1; i++ ) { memcpy( pt_bitmap, pt_scroll, bytes_per_char_line ); pt_bitmap = pt_bitmap + bytes_per_char_line; pt_scroll = pt_bitmap + bytes_per_char_line; } /* * Blank characters are displayed on the last line. */ memset( pt_bitmap, 0, bytes_per_char_line ); } static void doCRNL( int cr, int nl ) { if ( nl ) { if ( ++row == maxRow ) { scroll(); /* Scroll the screen now */ row = maxRow - 1; } nLines++; } if ( cr ) column = 0; /* Move cursor on the next location */ if ( cr || nl ) { wr_cursor( row, column ); } } static void advanceCursor( void ) { if ( ++column == maxCol ) doCRNL( 1, 1 ); else wr_cursor( row, column ); } static void gotorc( int r, int c ) { column = c; row = r; wr_cursor( row, column ); } static void video_drawchars( int r, int c, unsigned char ch ) { if ( fb_mem == NULL ) { return; } uint8_t *cdat, *dest, *dest0; int rows, offset; offset = r * bytes_per_char_line + c * bytes_per_pixel * RPI_FONT_WIDTH; dest0 = fb_mem + offset; /* * only 32-bit per pixel format is supported for now */ cdat = rpi_font + ch * RPI_FONT_HEIGHT; for ( rows = RPI_FONT_HEIGHT, dest = dest0; rows--; dest += bytes_per_line ) { uint8_t bits = *cdat++; ( (uint32_t *) dest )[ 0 ] = ( video_font_draw_table32 [ bits >> 4 ][ 0 ] & eorx ) ^ bgx; ( (uint32_t *) dest )[ 1 ] = ( video_font_draw_table32 [ bits >> 4 ][ 1 ] & eorx ) ^ bgx; ( (uint32_t *) dest )[ 2 ] = ( video_font_draw_table32 [ bits >> 4 ][ 2 ] & eorx ) ^ bgx; ( (uint32_t *) dest )[ 3 ] = ( video_font_draw_table32 [ bits >> 4 ][ 3 ] & eorx ) ^ bgx; ( (uint32_t *) dest )[ 4 ] = ( video_font_draw_table32 [ bits & 15 ][ 0 ] & eorx ) ^ bgx; ( (uint32_t *) dest )[ 5 ] = ( video_font_draw_table32 [ bits & 15 ][ 1 ] & eorx ) ^ bgx; ( (uint32_t *) dest )[ 6 ] = ( video_font_draw_table32 [ bits & 15 ][ 2 ] & eorx ) ^ bgx; ( (uint32_t *) dest )[ 7 ] = ( video_font_draw_table32 [ bits & 15 ][ 3 ] & eorx ) ^ bgx; } } #define ESC ( (char) 27 ) /* erase current location without moving the cursor */ #define BLANK ( (char) 0x7f ) static void videoPutChar( char ch ) { switch ( ch ) { case '\b': { if ( column ) column--; /* Move cursor on the previous location */ wr_cursor( row, column ); return; } case '\t': { int i; i = TAB_SPACE - ( column & ( TAB_SPACE - 1 ) ); while ( i-- ) { video_drawchars( row, column, ' ' ); column += 1; if ( column >= maxCol ) { doCRNL( 1, 1 ); return; } } wr_cursor( row, column ); return; } case '\n': { doCRNL( 0, 1 ); return; } case 7: { /* Bell code must be inserted here */ return; } case '\r': { doCRNL( 1, 0 ); return; } case BLANK: { video_drawchars( row, column, ' ' ); wr_cursor( row, column ); return; } default: { // *pt_bitmap = (unsigned char)ch | attribute; video_drawchars( row, column, ch ); advanceCursor(); return; } } } /* trivial state machine to handle escape sequences: * * --------------------------------- * | | * | | * KEY: esc V [ DCABHKJ esc | * STATE: 0 -----> 27 -----> '[' ----------> -1 ----- * ^\ \ \ \ * KEY: | \other \ other \ other \ other * <------------------------------------- * * in state '-1', the DCABHKJ cases are handled * * (cursor motion and screen clearing) */ #define DONE ( -1 ) static int handleEscape( int oldState, char ch ) { int rval = 0; int ro, co; switch ( oldState ) { case DONE: /* means the previous char terminated an ESC sequence... */ case 0: if ( 27 == ch ) { rval = 27; /* START of an ESC sequence */ } break; case 27: if ( '[' == ch ) { rval = ch; /* received ESC '[', so far */ } else { /* dump suppressed 'ESC'; outch will append the char */ videoPutChar( ESC ); } break; case '[': /* handle 'ESC' '[' sequences here */ ro = row; co = column; rval = DONE; /* done */ switch ( ch ) { case 'D': /* left */ if ( co > 0 ) co--; break; case 'C': /* right */ if ( co < maxCol ) co++; break; case 'A': /* up */ if ( ro > 0 ) ro--; break; case 'B': /* down */ if ( ro < maxRow ) ro++; break; case 'H': /* home */ ro = co = 0; break; case 'K': /* clear to end of line */ while ( column < maxCol - 1 ) videoPutChar( ' ' ); videoPutChar( BLANK ); break; case 'J': /* clear to end of screen */ while ( ( ( row < maxRow - 1 ) || ( column < maxCol - 1 ) ) ) videoPutChar( ' ' ); videoPutChar( BLANK ); break; default: videoPutChar( ESC ); videoPutChar( '[' ); /* DONT move the cursor */ ro = -1; rval = 0; break; } // /* reset cursor */ if ( ro >= 0 ) gotorc( ro, co ); default: break; } return rval; } static void clear_screen( void ) { int i, j; for ( j = 0; j < maxRow; j++ ) { for ( i = 0; i < maxCol; i++ ) { videoPutChar( ' ' ); } } column = 0; row = 0; } void rpi_fb_outch( char c ) { static int escaped = 0; if ( !( escaped = handleEscape( escaped, c ) ) ) { if ( '\n' == c ) videoPutChar( '\r' ); videoPutChar( c ); } } void rpi_video_init( void ) { int ret = rpi_fb_init(); if ( ( ret != RPI_FB_INIT_OK ) && ( ret != RPI_FB_INIT_ALREADY_INITIALIZED ) ) { rpi_video_initialized = 0; return; } struct fb_var_screeninfo fb_var_info; struct fb_fix_screeninfo fb_fix_info; rpi_get_var_screen_info( &fb_var_info ); rpi_get_fix_screen_info( &fb_fix_info ); maxCol = fb_var_info.xres / RPI_FONT_WIDTH; maxRow = fb_var_info.yres / RPI_FONT_HEIGHT; bytes_per_pixel = fb_var_info.bits_per_pixel / 8; bytes_per_line = bytes_per_pixel * fb_var_info.xres; bytes_per_char_line = RPI_FONT_HEIGHT * bytes_per_line; fb_mem = RTEMS_DEVOLATILE( void *, fb_fix_info.smem_start ); column = 0; row = 0; nLines = 0; fgx = ( CONSOLE_FG_COL << 24 ) | ( CONSOLE_FG_COL << 16 ) | ( CONSOLE_FG_COL << 8 ) | CONSOLE_FG_COL; bgx = ( CONSOLE_BG_COL << 24 ) | ( CONSOLE_BG_COL << 16 ) | ( CONSOLE_BG_COL << 8 ) | CONSOLE_BG_COL; eorx = fgx ^ bgx; clear_screen(); rpi_video_initialized = 1; } int rpi_video_is_initialized( void ) { return rpi_video_initialized; } /* for old DOS compatibility n-curses type of applications */ void gotoxy( int x, int y ); int whereX( void ); int whereY( void ); void gotoxy( int x, int y ) { gotorc( y, x ); } int whereX( void ) { return row; } int whereY( void ) { return column; }