term.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log: term.c,v $
00036  * Revision 1.8  2009/02/13 14:52:05  haraldkipp
00037  * Include memdebug.h for heap management debugging support.
00038  *
00039  * Revision 1.7  2008/08/11 06:59:42  haraldkipp
00040  * BSD types replaced by stdint types (feature request #1282721).
00041  *
00042  * Revision 1.6  2005/08/02 17:46:47  haraldkipp
00043  * Major API documentation update.
00044  *
00045  * Revision 1.5  2004/05/24 17:11:05  olereinhardt
00046  * dded terminal device driver for hd44780 compatible LCD displays directly
00047  * connected to the memory bus (memory mapped). See hd44780.c for more
00048  * information.Therefore some minor changed in include/dev/term.h and
00049  * dev/term.c are needet to
00050  * pass a base address to the lcd driver.
00051  *
00052  * Revision 1.4  2004/03/18 18:30:11  haraldkipp
00053  * Added Michael Fischer's TIOCGWINSZ ioctl
00054  *
00055  * Revision 1.3  2004/03/18 14:02:46  haraldkipp
00056  * Comments updated
00057  *
00058  * Revision 1.2  2004/03/16 16:48:27  haraldkipp
00059  * Added Jan Dubiec's H8/300 port.
00060  *
00061  * Revision 1.1.1.1  2003/05/09 14:40:52  haraldkipp
00062  * Initial using 3.2.1
00063  *
00064  * Revision 1.3  2003/05/06 18:34:22  harald
00065  * Cleanup
00066  *
00067  * Revision 1.2  2003/04/21 16:25:24  harald
00068  * Release prep
00069  *
00070  * Revision 1.1  2003/03/31 14:53:08  harald
00071  * Prepare release 3.1
00072  *
00073  */
00074 
00075 #include <dev/term.h>
00076 
00077 #include <stdlib.h>
00078 #include <string.h>
00079 #include <fcntl.h>
00080 #include <memdebug.h>
00081 
00087 
00088 static prog_char termid[] = "Term 1.0";
00089 
00090 static void TermRefreshLineEnd(CONST TERMDCB * dcb, uint8_t row, uint8_t col)
00091 {
00092     uint8_t i = col;
00093     uint8_t *cp = dcb->dcb_smem + row * dcb->dcb_vcols + col;
00094 
00095     /* Disable cursor to avoid flickering. */
00096     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00097         (*dcb->dss_cursor_mode) (0);
00098 
00099     /* Position cursor to the rwo and column to refresh. */
00100     (*dcb->dss_set_cursor) (row * dcb->dcb_ncols + col);
00101 
00102     /*
00103      * This loop looks weird. But it was the only way I found to get 
00104      * around a GCC bug in reload1.c:1920.
00105      */
00106     for (;;) {
00107         if (i++ >= dcb->dcb_vcols)
00108             break;
00109         (*dcb->dss_write) (*cp++);
00110     }
00111 
00112     /* Re-enable cursor. */
00113     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00114         (*dcb->dss_cursor_mode) (1);
00115 }
00116 
00117 void TermRefresh(TERMDCB * dcb)
00118 {
00119     uint8_t ir;
00120 
00121     for (ir = 0; ir < dcb->dcb_nrows; ir++)
00122         TermRefreshLineEnd(dcb, ir, 0);
00123     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00124 }
00125 
00126 static void TermClear(TERMDCB * dcb)
00127 {
00128     memset(dcb->dcb_smem, ' ', dcb->dcb_vcols * dcb->dcb_nrows);
00129     dcb->dcb_col = 0;
00130     dcb->dcb_row = 0;
00131     (*dcb->dss_clear) ();
00132 }
00133 
00134 static void TermDeleteLine(TERMDCB * dcb, uint8_t row)
00135 {
00136     uint8_t i;
00137     uint8_t *dcp;
00138 
00139     for (i = row; i < dcb->dcb_nrows - 1; i++) {
00140         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00141         memcpy(dcp, dcp + dcb->dcb_vcols, dcb->dcb_vcols);
00142     }
00143     memset(dcb->dcb_smem + (dcb->dcb_nrows - 1) * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00144     TermRefresh(dcb);
00145 }
00146 
00147 static void TermInsertLine(TERMDCB * dcb, uint8_t row)
00148 {
00149     uint8_t i;
00150     uint8_t *dcp;
00151 
00152     for (i = dcb->dcb_nrows - 1; i > row; i--) {
00153         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00154         memcpy(dcp, dcp - dcb->dcb_vcols, dcb->dcb_vcols);
00155     }
00156     memset(dcb->dcb_smem + row * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00157     TermRefresh(dcb);
00158 }
00159 
00160 static void TermCursorLeft(TERMDCB * dcb)
00161 {
00162     if (dcb->dcb_col) {
00163         (*dcb->dss_cursor_left) ();
00164         dcb->dcb_col--;
00165     }
00166 }
00167 
00168 static void TermCursorRight(TERMDCB * dcb)
00169 {
00170     if (++dcb->dcb_col < dcb->dcb_vcols)
00171         (*dcb->dss_cursor_right) ();
00172     else
00173         dcb->dcb_col = dcb->dcb_vcols - 1;
00174 }
00175 
00176 static void TermCursorUp(TERMDCB * dcb)
00177 {
00178     if (dcb->dcb_row) {
00179         dcb->dcb_row--;
00180         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00181     }
00182 }
00183 
00184 static void TermLinefeed(TERMDCB * dcb)
00185 {
00186     if (++dcb->dcb_row >= dcb->dcb_nrows) {
00187         dcb->dcb_row = dcb->dcb_nrows - 1;
00188         TermDeleteLine(dcb, 0);
00189     } else
00190         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00191 }
00192 
00193 static void TermReverseLinefeed(TERMDCB * dcb)
00194 {
00195     if (dcb->dcb_row--)
00196         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00197     else {
00198         dcb->dcb_row = 0;
00199         TermInsertLine(dcb, 0);
00200     }
00201 }
00202 
00203 static void TermEraseLineEnd(TERMDCB * dcb, uint8_t col)
00204 {
00205     if (col < dcb->dcb_vcols) {
00206         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col, ' ', dcb->dcb_vcols - col);
00207         TermRefresh(dcb);
00208     }
00209 }
00210 
00211 static void TermEraseEnd(TERMDCB * dcb)
00212 {
00213     uint8_t i;
00214 
00215     if (dcb->dcb_col < dcb->dcb_vcols)
00216         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col, ' ', dcb->dcb_vcols - dcb->dcb_col);
00217     for (i = dcb->dcb_row + 1; i < dcb->dcb_nrows; i++)
00218         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00219     TermRefresh(dcb);
00220 }
00221 
00222 static void TermEraseLineStart(TERMDCB * dcb)
00223 {
00224     if (dcb->dcb_col) {
00225         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00226         TermRefresh(dcb);
00227     }
00228 }
00229 
00230 static void TermEraseStart(TERMDCB * dcb)
00231 {
00232     uint8_t i;
00233 
00234     if (dcb->dcb_col)
00235         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00236     for (i = 0; i < dcb->dcb_row; i++)
00237         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00238     TermRefresh(dcb);
00239 }
00240 
00241 static void TermDeleteChar(TERMDCB * dcb, uint8_t col)
00242 {
00243     uint8_t i;
00244     uint8_t *cp = dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col;
00245 
00246     for (i = col; i < dcb->dcb_vcols - 1; i++, cp++)
00247         *cp = *(cp + 1);
00248     *cp = ' ';
00249     TermRefresh(dcb);
00250 }
00251 
00252 /*
00253  * Insert a space at the cursor position.
00254  */
00255 static void TermInsertSpace(TERMDCB * dcb)
00256 {
00257     uint8_t i;
00258     uint8_t *cp = dcb->dcb_smem + (dcb->dcb_row + 1) * dcb->dcb_vcols - 1;
00259 
00260     for (i = dcb->dcb_col; i < dcb->dcb_vcols - 1; i++, cp--)
00261         *cp = *(cp - 1);
00262     *cp = ' ';
00263     TermRefreshLineEnd(dcb, dcb->dcb_row, dcb->dcb_col);
00264     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00265 }
00266 
00267 /*
00268  * Clear display and print identification.
00269  */
00270 static void TermIdentify(TERMDCB * dcb)
00271 {
00272     PGM_P pcp = termid;
00273 
00274     TermClear(dcb);
00275     while (PRG_RDB(pcp)) {
00276         (*dcb->dss_write) (PRG_RDB(pcp));
00277         pcp++;
00278     }
00279 }
00280 
00318 int TermIOCtl(NUTDEVICE * dev, int req, void *conf)
00319 {
00320     TERMDCB *dcb = dev->dev_dcb;
00321     uint16_t usv;
00322     uint32_t ulv;
00323     WINSIZE *win_size;
00324 
00325     switch (req) {
00326     case LCD_CMDBYTE:
00327         (*dcb->dss_command) (*(uint8_t *)conf, 10);
00328         break;
00329     case LCD_CMDWORD16:
00330         usv = *(uint16_t *)conf;
00331         (*dcb->dss_command) ((uint8_t)(usv >> 8), 10);
00332         (*dcb->dss_command) ((uint8_t)usv, 10);
00333         break;
00334     case LCD_CMDWORD32:
00335         ulv = *(uint32_t *)conf;
00336         (*dcb->dss_command) ((uint8_t)(ulv >> 24), 10);
00337         (*dcb->dss_command) ((uint8_t)(ulv >> 16), 10);
00338         (*dcb->dss_command) ((uint8_t)(ulv >> 8), 10);
00339         (*dcb->dss_command) ((uint8_t)ulv, 10);
00340         break;
00341     case LCD_DATABYTE:
00342         (*dcb->dss_write) (*(uint8_t *)conf);
00343         break;
00344     case LCD_DATAWORD16:
00345         usv = *(uint16_t *)conf;
00346         (*dcb->dss_write) ((uint8_t)(usv >> 8));
00347         (*dcb->dss_write) ((uint8_t)usv);
00348         break;
00349     case LCD_DATAWORD32:
00350         ulv = *(uint32_t *)conf;
00351         (*dcb->dss_write) ((uint8_t)(ulv >> 24));
00352         (*dcb->dss_write) ((uint8_t)(ulv >> 16));
00353         (*dcb->dss_write) ((uint8_t)(ulv >> 8));
00354         (*dcb->dss_write) ((uint8_t)ulv);
00355         break;
00356     case LCD_SETCOOKEDMODE:
00357         if (*(uint32_t *)conf)
00358             dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00359         else
00360             dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00361         break;
00362     case LCD_GETCOOKEDMODE:
00363         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE)
00364             *(uint32_t *)conf = 1;
00365         else
00366             *(uint32_t *)conf = 0;
00367         break;
00368     case TIOCGWINSZ:
00369         win_size = (WINSIZE *)conf;
00370         win_size->ws_col    = dcb->dcb_nrows;
00371         win_size->ws_row    = dcb->dcb_vcols;
00372         win_size->ws_xpixel = 0;
00373         win_size->ws_ypixel = 0;
00374         break;
00375     }
00376     return 0;
00377 }
00378 
00379 
00392 int TermInit(NUTDEVICE * dev)
00393 {
00394     TERMDCB *dcb = dev->dev_dcb;
00395 
00396     /*
00397      * Initialize the display hardware.
00398      */
00399     (*dcb->dss_init) (dev);
00400 
00401     /*
00402      * Initialize driver control block.
00403      */
00404     dcb->dcb_smem = malloc(dcb->dcb_nrows * dcb->dcb_vcols);
00405     TermClear(dcb);
00406 
00407     return 0;
00408 }
00409 
00421 static int TermPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00422 {
00423     int rc;
00424     CONST uint8_t *cp;
00425     uint8_t ch;
00426     TERMDCB *dcb = dev->dev_dcb;
00427 
00428     /*
00429      * Call without data pointer is accepted.
00430      */
00431     if (buffer == 0)
00432         return 0;
00433 
00434     /*
00435      * Put characters in transmit buffer.
00436      */
00437     cp = buffer;
00438     for (rc = 0; rc < len; cp++, rc++) {
00439         ch = pflg ? PRG_RDB(cp) : *cp;
00440 
00441         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00442             /* Process special characters. */
00443             if (dcb->dcb_ctlseq == 0) {
00444                 /* Linefeed. */
00445                 if (ch == 10) {
00446                     dcb->dcb_col = 0;
00447                     TermLinefeed(dcb);
00448                     continue;
00449                 }
00450 
00451                 /* Carriage return. */
00452                 if (ch == 13) {
00453                     dcb->dcb_col = 0;
00454                     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols);
00455                     continue;
00456                 }
00457 
00458                 /* Escape. */
00459                 if (ch == 27) {
00460                     dcb->dcb_ctlseq = 1;
00461                     continue;
00462                 }
00463 
00464                 /* Backspace. */
00465                 if (ch == 8) {
00466                     if (dcb->dcb_col) {
00467                         dcb->dcb_col--;
00468                         TermDeleteChar(dcb, dcb->dcb_col);
00469                     }
00470                     continue;
00471                 }
00472 
00473                 /* Formfeed. */
00474                 if (ch == 12) {
00475                     TermClear(dcb);
00476                     continue;
00477                 }
00478             }
00479 
00480             /* Last character was ESC. */
00481             if (dcb->dcb_ctlseq == 1) {
00482                 dcb->dcb_ctlseq = 0;
00483 
00484                 switch (ch) {
00485                     /* Insert space. */
00486                 case '@':
00487                     TermInsertSpace(dcb);
00488                     break;
00489 
00490                     /* Cursor up. */
00491                 case 'A':
00492                     TermCursorUp(dcb);
00493                     break;
00494 
00495                     /* Cursor down. */
00496                 case 'B':
00497                     if (++dcb->dcb_row >= dcb->dcb_nrows)
00498                         dcb->dcb_row = dcb->dcb_nrows - 1;
00499                     else
00500                         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00501                     break;
00502 
00503                     /* Cursor right. */
00504                 case 'C':
00505                     TermCursorRight(dcb);
00506                     break;
00507 
00508                     /* Cursor left. */
00509                 case 'D':
00510                     TermCursorLeft(dcb);
00511                     break;
00512 
00513                     /* Clear screen and cursor home. */
00514                 case 'E':
00515                     TermClear(dcb);
00516                     break;
00517 
00518                     /* Cursor home. */
00519                 case 'H':
00520                     dcb->dcb_col = 0;
00521                     dcb->dcb_row = 0;
00522                     (*dcb->dss_cursor_home) ();
00523                     break;
00524 
00525                     /* Reverse linefeed. */
00526                 case 'I':
00527                     TermReverseLinefeed(dcb);
00528                     break;
00529 
00530                     /* Erase to end of screen. */
00531                 case 'J':
00532                     TermEraseEnd(dcb);
00533                     break;
00534 
00535                     /* Erase to end of line. */
00536                 case 'K':
00537                     TermEraseLineEnd(dcb, dcb->dcb_col);
00538                     break;
00539 
00540                     /* Insert line. */
00541                 case 'L':
00542                     TermInsertLine(dcb, dcb->dcb_row);
00543                     break;
00544 
00545                     /* Delete line. */
00546                 case 'M':
00547                     TermDeleteLine(dcb, dcb->dcb_row);
00548                     break;
00549 
00550                     /* Delete character. */
00551                 case 'P':
00552                     TermDeleteChar(dcb, dcb->dcb_col);
00553                     break;
00554 
00555                     /* Cursor position. */
00556                 case 'Y':
00557                     dcb->dcb_ctlseq = 2;
00558                     break;
00559 
00560                     /* Identify. */
00561                 case 'Z':
00562                     TermIdentify(dcb);
00563                     break;
00564 
00565                     /* Cursor on. */
00566                 case 'e':
00567                     dcb->dcb_modeflags |= LCD_MF_CURSORON;
00568                     (*dcb->dss_cursor_mode) (1);
00569                     break;
00570 
00571                     /* Cursor off. */
00572                 case 'f':
00573                     dcb->dcb_modeflags &= ~LCD_MF_CURSORON;
00574                     (*dcb->dss_cursor_mode) (0);
00575                     break;
00576 
00577                     /* Erase to start of screen. */
00578                 case 'd':
00579                     TermEraseStart(dcb);
00580                     break;
00581 
00582                     /* Erase to start of line. */
00583                 case 'o':
00584                     TermEraseLineStart(dcb);
00585                     break;
00586                 }
00587                 continue;
00588             }
00589 
00590             /* Receive cursor row position. */
00591             if (dcb->dcb_ctlseq == 2) {
00592                 dcb->dcb_ctlseq = 3;
00593                 if (ch < 32)
00594                     dcb->dcb_row = 0;
00595                 else if (ch - 32 >= dcb->dcb_nrows)
00596                     dcb->dcb_row = dcb->dcb_nrows - 1;
00597                 else
00598                     dcb->dcb_row = ch - 32;
00599                 continue;
00600             }
00601 
00602             /* Receive cursor column position. */
00603             if (dcb->dcb_ctlseq == 3) {
00604                 dcb->dcb_ctlseq = 0;
00605                 if (ch < 32)
00606                     dcb->dcb_col = 0;
00607                 else if (ch - 32 >= dcb->dcb_vcols)
00608                     dcb->dcb_col = dcb->dcb_vcols - 1;
00609                 else
00610                     dcb->dcb_col = ch - 32;
00611                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00612                 continue;
00613             }
00614         }
00615 
00616         /* 
00617          * Send any character to the LCD driver, which had been left 
00618          * unprocessed upto this point.
00619          */
00620         (*dcb->dss_write) (ch);
00621 
00622         /* Update shadow memory. */
00623         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00624             *(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col) = ch;
00625             if (++dcb->dcb_col >= dcb->dcb_vcols) {
00626                 dcb->dcb_col = dcb->dcb_vcols - 1;
00627                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00628             }
00629         }
00630     }
00631     return rc;
00632 }
00633 
00685 int TermWrite(NUTFILE * fp, CONST void *buffer, int len)
00686 {
00687     return TermPut(fp->nf_dev, buffer, len, 0);
00688 }
00689 
00704 #ifdef __HARVARD_ARCH__
00705 int TermWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00706 {
00707     return TermPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00708 }
00709 #endif
00710 
00728 NUTFILE *TermOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00729 {
00730     TERMDCB *dcb = dev->dev_dcb;
00731     NUTFILE *fp = malloc(sizeof(NUTFILE));
00732 
00733     if (fp == 0)
00734         return NUTFILE_EOF;
00735 
00736     if (mode & _O_BINARY)
00737         dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00738     else
00739         dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00740     fp->nf_next = 0;
00741     fp->nf_dev = dev;
00742     fp->nf_fcb = 0;
00743 
00744     return fp;
00745 }
00746 
00757 int TermClose(NUTFILE * fp)
00758 {
00759     if (fp && fp != NUTFILE_EOF) {
00760         free(fp);
00761         return 0;
00762     }
00763     return -1;
00764 }
00765 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/