Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Writing MS-DOS Device Drivers


December 1990/Writing MS-DOS Device Drivers/Listing 7

Listing 7 (write.c) Routines Used to Write to the Screen

#includde   "char.h"

/*--------- external function prototypes: ---------*/

             /* look for unused key buffer */
extern char *k_alloc (void);
             /* look for key buffer */
extern char *k_seek (void);
             /* write decimal integer to ring buffer */
extern void r_puti (char);
             /* write byte to ring buffer */
extern void r_write (char);
             /* clear selected part of the screen */
extern void rawclear (void);
             /* set the video mode */
extern void rawmode (void);
             /* move the cursor */
extern void rawmv (void);
             /* scroll the screen up */
extern void rawscroll (void);
             /* output character as raw tty */
extern void rawtty (void);
             /* output character to screen */
extern void rawwrite (void);

/*
 * delimiters used for quoted characters as
 * parameters of escape sequences
 */

#define DELIM1 '\"
#define DELIM2 '"'

/*
 * characters that require special handling
 */

#define BEL '\007'
#define BS  '\010'
#define NL  '\012'
#define CR  '\015'
#define ESC '\033'

/*
 * color codes
 */

#define BLUE    (01)
#define GREEN   (02)
#define RED (04)
#define CYAN    (BLUE <FONT FACE="Symbol" SIZE=2>½ GREEN)
#define MAGENTA (BLUE <FONT FACE="Symbol" SIZE=2>½ RED)
#define YELLOW  (GREEN <FONT FACE="Symbol" SIZE=2>½ RED)
#clefine WHITE  (BLUE <FONT FACE="Symbol" SIZE=2>½ GREEN <FONT FACE="Symbol" SIZE=2>½ RED)

/*
 * macro's for turning on and off attributes or designating
 * a color as foreground or background
 */

#define ON(x)    (attrib <FONT FACE="Symbol" SIZE=2>½= (char) (x))
#define OFF(x)   (attrib &= (char) (~(x)))
#define FORE(x)  (attrib <FONT FACE="Symbol" SIZE=2>½= (char) (x))
#define BACK(x)  (attrib <FONT FACE="Symbol" SIZE=2>½= (char) ((x) << 4))

/*
 * we don't want to use the standard 'c' isdigit(); it's
 * either implemented as a function (we don't want to use
 * osmebody else's functions that might have unpleasant side
 * effects) or a macro invoking an array of values that
 * dictate what lexical properties a given character possesses
 * (a waste of precious memory)
 */

#define isdigit(x) (((x) >= '0') && ((x) <= '9'))

/*
 * w_write()
 *
 * w_write() keeps track of actually getting stuff on the screen
 * and moving the cursor around
 */

void   w_write()
   {
   switch (outchar)
      {
      case     CR:

          /* just set the column to 0 for a carriage return */

          curr.loc.col = 0;

          /* and fall through to the backspace handler */

      case    BS:

          /* decrement the current column unless at the left
           * margin */

          if (curr.loc.col)
             --curr.loc.col;

          /* move the cursor and that's it... */

          rawmv();
          break;

      default:

          /* first, write the character without
             moving the cursor */

          rawwrite();

          /* then, if we're not on the right margin, bump the
           * cursor right and that's it */

          if ((curr.loc.col + 1) <= max.loc.col)
              {
              ++curr.loc.col;
              rawrmv();
              break;
              }

          /* but if we were at the right margin, check the wrap
           * flag; if it's clear, just return. if not, execute
           * a carriage return (just set the column to zero -
           * we'll do a rawmv() call later), set the current
           * character to newline, and fall into the newline
           * routine */

          if (!wrap)
              break;
          curr.loc.col = 0;
          outchar = NL;
      case     NL:

          /* if we're not at the bottom of the screen, just bump
           * the line down and call rawmv() */

          if (++curr.loc.line < 25)
              {
              rawmv();
              break;
              }

          /* but if we were at the bottom (or somehow below!),
           * make sure we're on the bottom line. If we're in
           * one of the CGA 80x25 text modes, do our fancy
           * assembly language scroll routine, else just let
           * the BIOS handle it */

          curr.loc.line = 24;
          if (video_mode == 2 || video_mode == 3)
              {
              rawscroll();
              break;
              }

      case     BEL:

          /* do a raw tty output; it handles the cursor movement
           * too */

          rawtty();
          break;
      }
   }

/*
 * w_buffer()
 *
 * w_buffer() writes a byte into the escape buffer. silently
 * overwrites the last byte in the buffer if we get that far. it
 * was either that or trash the new byte
 */

void  w_buffer(c)
char  c;
   {
   if (char_cnt == BUF_LEN)
      esc_buf[ BUF_LEN - 1 ] = c;
   else
      esc_buf[ char_cnt++ ] = c;
   }
/*
 * w_cursor()
 *
 * w_cursor() handles the cursor left, right, up and down
 * functions. bumps the value by the value of the 1st parameter
 * in the escape sequence (if there isn't one, we put a 1 in for
 * it) in the direction specified by the delta, until it hits the
 * specified limit. then execute the cursor move...
 */

void   w_cursor()
   {
   if (!char_cnt)
      esc_buf[ 0 ] = 1;
   while (*cur_val != limit)
      {
      *cur_val += delta;
      esc_buf[ 0 ]--;
      if (!esc_buf[ 0 ])
          break;
      }
   rawmv();
   }

/*

 * w_putc()
 *
 * w_putc() updates the parameters that might have changed since
 * last time, then runs the character through the escape sequence
 * state machine
 */

void   w_putc()
   {

   /* update parameters */

   max.loc.col = SCREEN_WIDTH - 1;
   cur_page = CURRENT_PAGE;
   curr.position = (PAGE_TABLE [ cur_page ]).position;
   if (curr.loc.col > max.loc.col)
      curr.loc.col = max.loc.col;
   if ((video_mode = CURRENT_MODE) == 7)
      video_address = MONOCHROME + SCREEN_OFFSET;
   else
      video_address = GRAPHIC + SCREEN_OFFSET;

   /* process the escape sequence state */

   switch (state)
      {

      case    HAVE_ESC:
          /* if we have an escape, we want a left bracket.
           * if we get it, change the state and return,
           * else reset back to the RAW state and fall
           * through */

          if (outchar == '[')
              {
              state = HAVE_LBRACE;
              break;
              }
          state = RAW;

      case     RAW:

          /* if it's an escape, change the stae, else output the
           * character */

          if (outchar == ESC)
              state = HAVE_ESC;
          else
              w_write();
          break;

      case     IN_NUMBER:

          /* if it's another digit, roll it into the value. else
           * the state falls back to HAVE_LBRACE, and we fall
           * through */

          if (isdigit(outchar))
              {
              tmp.value *= 10;
              tmp.value += outchar - '0';
              break;
              }
          else
              {
              state = HAVE_LBRACE;
              w_buffer(tmp.value);
              }

      case     HAVE_LBRACE:

          /* if we have a string delimiter, change the state and
           * save the delimiter */
             if (outchar == DELIM1 || outchar == DELIM2)
                {
                state = IN_STRING;
                tmp.delim = outchar;
                break;
                }

             /* else if it's 'punctuation', ignore it */

             if (outchar == ';' || outchar == '=' ||
                       outchar == '?')
                break;

             /* else if it's a digit, start a number and
                change the state */

             if (isdigit(outchar))
                {
                state = IN_NUMBER;
                tmp.value = outchar - '0';
                break;
                }

             /* else it terminates the escape sequence, and
              * identifies its purpose */

             switch (outchar)
                {

                case     'A':           /* cursor up */
                    limit = 0;
                    delta = (char) -1;
                    cur_val = &curr.loc.line;
                    w_cursor();
                    break;
                case     'B':           /* cursor down */
                    limit = 24;
                    delta = 1;
                    cur_val= &curr.loc.line;
                    w_cursor();
                    break;

                case     'C':           /* cursor right */
                    limit = max.loc.col;
                    delta = 1;
                    cur_val = &curr.loc.col;
                    w_cursor();
                    break;

                case     'D':           /* cursor left */
                    limit = 0;
                    delta = (char) -1;
                    cur_val = &curr.loc.col;
                    w_cursor();
                    break;

                case     'H':
                case     'R':
                case     'f':

                    /* set cursor position: make sure there
                     * are at least 2 parameters stored,
                     * correct any out-of-range parameters,
                     * and execute the move. if the
                     * character was 'R', fall through into
                     * the report position sequence */

                    switch (char_cnt)
                       {
                       case  0:
                           w_buffer(1);
                       case    1:
                           w_buffer(1);
                       default:
                           break;

                    /* set graphic rendition - just do all
                     * the parameters and set/reset the
                     * appropriate bits in the attribute
                     * byte */

                    while (char_cnt)
                       {
                       switch (esc_buff[ --char_cnt ])
                           {
                           case    0:
                              attrib = 0007; break;
                           case  1:
                              ON(010); break;
                           case    4:
                              OFF(07); ON(01); break;
                           case    5:
                              ON(0200); break;
                           case    7:
                              OFF(07); 0N(0160); break;
                           case    8:
                              0FF(0167); break;
                           case    30:
                              OFF(07);  break;
                           case    31:
                              OFF(07); FORE(RED); break;
                           case    32:
                              OFF(07); FORE(GREEN); break;
                           case    33:
                              OFF(07); FORE(YELLOW); break;
                           case    34:
                              OFF(07); FORE(BLUE); break;
                           case    35:
                              OFF(07); FORE(MAGENTA); break;
                           case    36:
                              OFF(07); FORE(CYAN); break;
                           case    37:
                              OFF(07); FORE(WHITE); break;
                           case    40:
                              OFF(0160);  break;
                           case    41:
                              OFF(0160); BACK(RED); break;
                           case    42:
                              OFF(0160); BACK(GREEN); break;
                           case    43:
                              OFF(0160); BACK(YELLOW); break;
                           case    44:
                              OFF(0160); BACK(BLUE); break;
                           case    45:
                              OFF(0160); BACK(MAGENTA); break;
                           case    46:
                              OFF(0160); BACK(CYAN); break;
                           case    47:
                              OFF(0160); BACK(WHITE); break;
                           default:
                              break;
                           }
                        }
                    break;
                case     'p':
                    if (esc_buf[ 0 ])
                        {

      /* if the first parameter is not nul, then we're
         redefining a 'normal' key. Clear the msb of
         keyc1heck to indicate this */
    
                        keycheck = (esc_buf[ 0 ]) &
                           0000377;

      /* check first to see if we've already allocated
         a buffer to this key; then if not, see if we have
         an unused buffer to hand out */

                        if (k_seek() <FONT FACE="Symbol" SIZE=2>½<FONT FACE="Symbol" SIZE=2>½ k_alloc()))
                           }

                        if (!esc_buf[ 0 ])
                           curr.loc.line = 1;
                        else if (esc_buf[ 0 ] > 25)
                           curr.loc.line = 25;
                        else
                           curr.loc.line = esc_buf[ 0 ];

                        if (!esc_buf[ 1 ])
                           curr.loc.col = 1;
                        else if (esc_buf[ 1 ] > max.loc.col + 1)
                           curr.loc.col = max.loc.col + 1;
                        else
                           curr.loc.col = esc_buf[ 1 ];

                        curr.loc.line--;
                        curr.loc.col--;
                        rawmv();

                        if (outchar != 'R')
                           break;

                    case     'n':
                        /* output the position; format is
                         * "\033[%.2d;%.2dR\015" */

                        r_write(ESC);
                        r_write('[');
                        r_puti(curr.loc.line + 1);
                        r_write(';');
                        r_puti(curr.loc.col + 1);
                        r_write('R');
                        r_write(CR);
                        break;

                    case     'J':
                        /* rawclear clears the screen from
                         * (curr.loc.line, curr.loc.col) to
                         * (max.loc.line, max.loc.col); so for
                         * clear screen, set the current
                         * position to the upper left hand
                         * corner of the screen, and the max
                         * line to the bottom of the screen */

                        curr.loc.line = curr.loc.col = 0;
                        max.loc.line = 24;
                        rawclear();
                        break;

                    case     'K':
                        /* and clear to end of line is even
                         * simpler - just set the max line equal
                         * to the same line we're on */

                        max.loc.line = curr.loc.line;
                        rawclear();
                        break;

                    case     'h':
                    case     'l':
                        /* set and reset mode do the same thing
                         * unless the mode is 7. easy */

                        if (!char_cnt)
                           w_buffer(2);
                        if (esc_buf[ 0 ] > 7)
                           break;
                        if (esc_buf[ 0 ] == 7)
                           wrap = (char) (outchar == 'h');
                        else
                           rawmode();
                        break;

                    case     'm':
                           {
                           k = 1;
                           kp->keystroke =
                              (esc_buf[ 0 ])
                              & 0000377;
                           }
                        else
                           break;
                        }
                    else
                        {

      /* first byte was nul - an extended key. indicate
         by setting msb of keycheck to $FF */

                        keycheck = (esc_buf[ 1 ]) <FONT FACE="Symbol" SIZE=2>|
                           0177400;
                        if (k_seek() <FONT FACE="Symbol" SIZE=2>½<FONT FACE="Symbol" SIZE=2>½ k_alloc())
                           {
                           k = 2;
                           kp->keystroke =
                              (esc_buf[ 1 ])
                              <FONT FACE="Symbol" SIZE=2>½ 0177400; 
                           }
                        else
                           break;
                        }55

      /* copy the parameters into the buffer, counting as we go */

                        for (*len= 0; (k < char_cnt) &&
                           (k < KEY_BUFLEN); ++*len)
                           *ptr++ = esc_buf[ k++ ];

                        break;

                    case     's':

                        /* save current position */

                        saved.position = curr.position;
                        break;

                    case     'u':

                        /* restore current position */

                        curr.position = saved.position;
                        rawmv();
                        break;

                    default:

                        /* anything else? discard the parameters
                         * and output the final character to the
                         * screen */

                        w_write();
                        break;
                    }

                /* finally, clear the buffer by resetting the count
                   and fall back to the RAW state */

                char_cnt = 0;
                state = RAW;
                break;

            case    IN_STRING:

                /* finally, the IN_STRING case - if the character
                 * isn't the delimiter we saved, then put it into
                 * the buffer as it was received */

                if (outchar == tmp.delim)
                        state = HAVE_LBRACE;
                    else
                        w_buffer(outchar);
                    break;
                }
            }

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.