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

Mobile

Remote Connectivity for Portable Terminals: Part I


FEB91: REMOTE CONNECTIVITY FOR PORTABLE TERMINALS: PART I

REMOTE CONNECTIVITY FOR PORTABLE TERMINALS: PART I

VT100 terminal emulation and more for 8051-based systems

Dan Troy

Dan is software manager at Murata Hand-Held Terminals and is currently developing more operating system firmware and application software for the Links product line. He can be reached at Murata HHT, 9 Columbia Dr., Amherst, NH 03031.


In an effort to achieve remote VT100 hand-held connectivity and thereby extend the usefulness of our Links terminal (the 8051-based, hand-held, touchscreen terminal with built-in modem seen in Figure 1), we developed a standard application called "Links100" that would emulate the VT100's 24 x 80 virtual screen image on the much smaller 12 x 20 Links display. This two-part article discusses the development of that custom VT100 terminal-emulation application. This month, I'll examine the development of the background VT100; in Part II, I'll discuss the application development process itself.

Virtually all of the development work was done in C using our Speed development package (which is built around ANSI standard C language with special Links extensions to C) and an ANSI Standard 8051 Cross Compiler with a modified library to interface with the PROM-based operating system residing in the Links. Applications were written in C on a PC, compiled under Microsoft C, tested on the PC using the Murata Speed C Emulator (with optional use of Codeview), and cross compiled and loaded from the PC into the Links' non-volatile program RAM.

Developing the VT100 Driver

Because we were working on a tight development schedule, I decided early on to build the program around an existing VT100 terminal-emulation program, if possible. The criterion for selecting an emulation program was straight-forward: It had to be written in C for an IBM-compatible PC, preferrably in DOS-file format. My plan was to subsequently port the library over to our hand-held terminal with minimal changes, thereby saving a substantial amount of software development time. After evaluating several libraries, I settled on the C Communications Toolkit (Magna Carta Software, Garland, Texas) because it seemed to be complete (among other features, it includes a variety of terminal emulation options, single and multifile transfer with ASCII, XModem, YModem, Kermit, and so on). Furthermore, the toolkit comes with full source code, requires no royalties for object code developed from the source, and is relatively inexpensive.

Once I had the C Communication Toolkit source code in hand, the first step in porting the code to run on the Links terminal was to replace Magna Carta's DOS int86( ) interrupt function calls and 8250 UART accessing calls with Links 8051-based operating system calls. Table 1 lists the Links functions that were substituted for Magna Carta serial read and write commands in the VT100 emulation code. The primary VT100 emulator routine was rewritten as shown in Listing One. TERMINALP represents the default VT100 setup parameters, which are defined in the structure shown in Listing Two. The write_screen function in process_VT100_input (see Listing One) interprets the character according to VT100 rules. Likewise, the DOS int86() interrupt calls within Magna Carta's screen access and cursor positioning functions were replaced with Links functions.

Table 1: Links functions that were introduced into the Magna Carta VT100 emulation source code.

  Functions          Description
  ------------------------------------------------------------------------

  char eof (void);   Checks to see if a character has been received in the
                     Links terminal serial port (end of file?).  Returns a
                     TRUE/FALSE condition.

  char read(char *); Reads a character out of the Links terminal serial
                     input buffer.  Character read is passed back in the
                     function call, and the read status is returned.

  void write(char);  Writes a character out of the Links terminal serial
                     port.

Next, our software development team considered two possible ways of implementing the VT100 command processor (the VT100 driver). The first approach was to call the driver from the Links100 application, requiring the driver to be called in a main-processing loop of the application. The second approach, on the other hand, was to call the driver transparently from an 8051 interrupt. This would allow the driver to be installed from the application at initialization, but process the VT100 commands transparently from the low-level operating system timer interrupt handler.

We decided to have the VT100 driver executed in the background via the interrupt so that the engineer writing the Links100 application would not have to be concerned with calling the VT100 driver repetitively from a main-processing loop, or in multiple locations within the application. This required modifying the existing Links operating system to handle the installation and calling of the driver. Furthermore, this same installation methodology can now be used for any background driver for future applications.

Installing the VT100 Driver

In order to install the background driver, we developed an initialization routine, written in 8051 assembly code and callable from C (see Listing Three). This function takes the address of the background driver, storing it at emul_processor_address. It then stores the pointer to any data to pass to it in data_struct.

The function call to install the driver within the Links application is in Listing Four. To utilize this function, the prototype can be declared as shown in Listing Five. The execution of the call to init_emulation_mode accesses the Links operating system by calling an 8051 assembly library routine. The routine jumps to a fixed-location jump table in the operating system, which, in turn, jumps to the beginning address of the init_emulation_mode routine previously described. This is how the two separately linked programs (the Links100 application and the Links operating system) are linked together. Once the address of the driver has been stored, it can be easily accessed by the execution mechanism.

Executing the VT100 driver

Our next step was to modify the 8051 timer 0 interrupt handler within the Links operating system in order to periodically execute the process_VT100_input function. This timer was the only one available, and it was already being used by the operating system to handle several other processes. Because the VT100 driver could possibly have taken longer to execute than the interrupt rate, a recursion protection semaphore was introduced in the setup to the VT100 driver function call (Listing Six).

When the timer 0 interrupt handler is called, it first protects all 8051 registers that the C code driver (in this case, process_VT100_input) could possibly use. Next, it puts the address of the return_from_emul_processorroutine on the 8051 program stack (see Listing Seven). It then puts the address of the process_VT100_input function on the stack. Before returning from the interrupt, the pointer to the default data structure being passed to the C function is put into 8051 registers r2 and r3 (in this case, the pointer to the VT100 default parameters data structure). Upon performing the return from interrupt, the execution of the program resumes at process_VT100_input. When it is finished executing, program execution resumes at return_from_emul_processor and restores the 8051 program stack to the state that existed prior to the timer 0 interrupt. The program execution then returns to the point where it was interrupted.

Accessing the VT100 Virtual Screen Image

We developed two support routines to access the 24 x 80 VT100 virtual image: One to read characters from the image and the other to get the cursor position. The function in Listing Eight was developed to read characters from the image. This function passes back a string of characters starting at the specified row (1 - 24) and column (1 - 80) and ending at either the number of characters specified in number_to_read or the end of the row, whichever occurs first in the read. The return parameter is the pointer to the string. The get cursor position function is shown in Listing Nine.

Some global parameters were made available to determine whether certain expected information had arrived. The active flag is a TRUE/FALSE condition that notifies the user if any data has come in. It must be initialized and reset to FALSE by the application program using the driver in order to detect the active state change. The total character, line feed, and carriage return count variables were created to allow the application program knowledge of how much of the expected data has arrived. This was done because it is often impossible to know how long the host will take to send the expected data.

Benchmarking

Some VT100 command processing functions did not meet the required performance execution time because of some of the 8051 code generated by the cross compiler. For one thing, we found that the clear screen VT100 escape sequence (ESC [J), when sent from a host to the Links, was much too slow. This "slow" version appears in Listing Ten. The code generated by the cross compiler for this version calculated the screen position by first determining the screen image offset from the indices and then adding it to the screen image base address. This is very intense processing when assembly code is generated from C, especially because we are using an 8-bit processor for 16-bit addressing. In the improved version (see Listing Eleven) we did not use offset calculations, but used one tenth as many loop comparison operations. Execution time for the slow version was 1.96 seconds, compared to the improved time of 0.33 seconds. The code size, however, increased from 145 to 213 bytes. We decided that this was a small price to pay for the dramatic performance increase.

Similar improvements were made in the clear from cursor position to end of screen, clear from beginning of screen to cursor position, and clear line escape commands.

Next, we determined that scrolling performance was also below an acceptable level (see Listing Twelve), so we changed the VT100 scroll-up function as shown in Listing Thirteen. In this case, the slow version took 2.80 seconds to execute versus 0.57 seconds for the improved version (based on scrolling up 23 rows). Likewise, the code size went up from 294 to 565 bytes. The scroll_down function was similarly improved so that all of the performance criteria were met.

Emulating a VT100 Application on a PC

Earlier, I mentioned the Speed C development package as a tool available to develop applications on a PC to run on the Links terminal. To adequately develop a VT100-based application for the Links, we needed to extend the Speed C PC-based Links terminal emulator so that it could simulate the VT100 driver we had just developed.

First, we needed to introduce a timer-interrupt handler so that it could execute the VT100 driver on a periodic basis, just like the Links terminal. Even though the timing would not be exactly the same, it would be close enough for simulation. When the timer interrupt is called, it calls the same VT100 driver that will ultimately reside in the Links, except that it is compiled with Microsoft C.

Second, we added to the Speed C PC-based emulator the ability to display the 24 x 80 character virtual image on the PC screen. We utilized an off-the-shelf PC Windows package to display the virtual buffer. As VT100 commands are processed by the Links PC-based emulator, the displayable characters are shown in the window. Also, the characters are displayed on the Links screen as if the application were running on the Links instead of the PC. This is accomplished by loading a specialized application into the Links prior to executing the application to be tested on the PC. This Links-based application accepts PC characters, which are to be displayed on the Links, and also tells the Links terminal where to display them so that it is properly emulated.

Using the VT100 Driver in an Application

To demonstrate use of the VT100, the application in Listing Fourteen extracts the first 20 characters of the first three rows of the 24 x 80 VT100 virtual buffer image in the Links terminal. It then displays them on the Links screen using the special x Links function, which handles all the Links display commands. The D command ("define a touch-sensitive display area on the Links terminal") and the P command ("print a character string to a defined touch-sensitive display area") are used to display the received data. This data is processed through the Links RS-232 port at 9600 baud, 8-data bits, no parity, and 1 stop bit (see the statement open ("98N1",1) in Listing Fourteen). The second parameter, 1, means that the XON/XOFF protocol is enabled.

To execute the demonstration, I compiled the program using Microsoft C on my PC, then linked with the Speed C emulation libraries and the VT100 emulation object code. Next, I tested the mini-application on the PC. I was able to see the full 24 x 80 character virtual screen image on the PC and the first three rows and first 20 columns of the VT100 image displayed on the Links terminal simultaneously. The Links terminal was tied into the COM1 serial port of my PC, and the simulated host was sending VT100 commands and ASCII data to my PC through the COM2 serial port.

The next step was to cross compile the application using the Speed C cross compiler. I also cross compiled the VT100 driver C source and linked it with the application and the Speed C library, using the Speed C linker. I then loaded the executable application via the Speed C PC-based loader into the Links terminal, and used the same PC as the simulated host to send the VT100 commands and data to test the application. I was able to display the same screen image on the Links that was on the Speed C emulator when I sent the same VT100 data from the simulated host!

Next Month

In Part II, I'll develop the Links100 application, which emulates a VT100 terminal using just about every feature of the Links touch-sensitive display, including graphics. I'll also cover the ergonomics involved in developing applications for small-size, touch-sensitive systems.


_REMOTE CONNECTIVITY FOR PORTABLE TERMINALS: PART I_
by Dan Troy


[LISTING ONE]

<a name="0074_000e">

void process_VT100_input(char *z)
{
char data;
TERMINALP t = (TERMINALP)z;   /* default VT100 parameters */

  if(!eof())                  /* if character exists in   */
  {                           /*      serial buffer       */
     read(&data);             /* then read it             */
     active = TRUE;           /* set global activity flag */
     write_screen(t, data);   /* process VT100 character  */
  }
}





<a name="0074_000f">
<a name="0074_0010">
[LISTING TWO]
<a name="0074_0010">

typedef struct
{
     char addlf;            /* line feed/new line   */
     char keymode;          /* cursor/application   */
     char insert;           /* replace/insert       */
     char autowrap;         /* off/on               */
     char keypad;           /* numeric/alternate    */
     char origin;           /* absolute/relative    */
     char kblock;           /* keyboard unlock/lock */
}TERMINAL, *TERMINALP;






<a name="0074_0011">
<a name="0074_0012">
[LISTING THREE]
<a name="0074_0012">

init_emulation_mode:

clr ea                             ;shut off interrupts
mov dptr,#emul_processor_address   ;get c function address
mov a,r3                           ;get low byte of function
                                   ;from C call
movx @dptr,a                       ;save at storage address
inc dptr                           ;inc processor address ptr
mov a,r2                           ;get high byte of function
movx @dptr,a                       ;save at storage address
setb ea                            ;turn back on interrupts

lcall get_and_decr_stack_pointer   ;get data stack ptr
                                   ;parameter data struct
movx a,@dptr                       ;get low byte of setup
                                   ;parameter data struct
xch a,b                            ;save in b reg
lcall get_and_decr_stack_pointer   ;adjust data stack ptr
movx a,@dptr                       ;get high byte of setup
                                   ;parameter data struct
push acc                           ;save on program stack
mov dptr,#data_struct              ;get storage address
xch a,b
movx @dptr,a                       ;save low byte of setup
inc dptr
pop acc
movx @dptr,a                       ;save high byte of setup

ret






<a name="0074_0013">
<a name="0074_0014">
[LISTING FOUR]
<a name="0074_0014">

init_emulation_mode(process_VT100_input, (char *) t);





<a name="0074_0015">

<a name="0074_0016">
[LISTING FIVE]
<a name="0074_0016">

typedef void(*PTF) ();         /* a pointer to a function */
extern void init_emulation_mode(PTF, char *);

And the VT100 driver can be installed as follows:

/* initialize VT100 default parameters */

cursor.row = cursor.col = 1;
t->origin = t->addlf = t->keymode = RESET;
t->kblock = t->insert = t->autowrap = RESET;
t->keypad = NUMERIC;

clr_display();              /* clear LINKS display */

init_emulation_mode(process_VT100_input, (char *) t);





<a name="0074_0017">
<a name="0074_0018">
[LISTING SIX]
<a name="0074_0018">

BEFORE

timer0:
 .
 .
 .
reti


AFTER

timer0:
 .
 .
 .
jnb in_emul_processor,process_emul ;if the emulation driver
reti                               ;is already running, then
                                   ;return from interrupt
process_emul:                      ;else call the driver

clr ea                       ;shut off interrupts while the
setb in_emul_processor       ;recursion prevention semaphore
setb ea                      ;is set

push dph     ;protect all registers that the C code driver
push dpl     ;could possibly use (includes all of bank 3
push psw     ;registers)
push acc
push b
push 18h
push 19h
push 1ah
push 1bh
push 1ch
push 1dh
push 1eh
push 1fh

mov dptr,#return_from_emul_processor   ;put return from
push dpl                               ;emulation driver
push dph                               ;on stack

mov dptr,#emul_processor_address   ;put emulation processor
movx a,@dptr                       ;driver address on stack
push acc
inc dptr
movx a,@dptr
push acc

mov dptr,#data_struct              ;get the pointer to any
movx a,@dptr                       ;data to be passed to
mov r2,a                           ;the C language driver.
inc dptr                           ;pointer address is
movx a,@dptr                       ;stored in r2 and r3
mov r3,a

reti               ;calls generic emulation driver
                   ;(last address on program stack)






<a name="0074_0019">
<a name="0074_001a">
[LISTING SEVEN]
<a name="0074_001a">

return_from_emul_processor:

pop 1fh     ; restore stack prior to call to VT100 driver
pop 1eh
pop 1dh
pop 1ch
pop 1bh
pop 1ah
pop 19h
pop 18h
pop b
pop acc
pop psw
pop dpl
pop dph

clr ea                 ;reset recursion prevention semaphore
clr in_emul_processor  ;while interrupts are off
setb ea
 .
 .
ret      ; gets address of next instruction to execute
         ; in the routine that had been interrupted by
         ; timer 0.
         ; address taken off the 8051 stack and the stack
         ; pointer is updated






<a name="0074_001b">
<a name="0074_001c">
[LISTING EIGHT]
<a name="0074_001c">

char *read_VT100_image(char row, char col, char *string,
     char number_to_read)
{
     short i;
     char *ptr;

     if(row <= VT100_MAX_ROWS && col <= VT100_MAX_COLS)
     {
       /* calculate number of characters to read on row */
         if((number_to_read + col) > (VT100_MAX_COLS+1))
             number_to_read = (VT100_MAX_COLS+1) - col;

      /* get string start address from global screen array */
         ptr = &screen[row - 1][col - 1];

      /* transfer string to return string array */
         for(i = 0; i < number_to_read; i++;)
             string[i] = *ptr++;
         str[i] = 0;             /* terminate string */
     }
     return(string);
}






<a name="0074_001d">
<a name="0074_001e">
[LISTING NINE]
<a name="0074_001e">


void get_cursor_position(TERMINALP t, char *row, char *col)
{
/* if cursor origin is relative, then calc row position
   based on scrolling start position, else use global row
   position */
    if(t->origin == SET)*row = cursor.row - begin.scroll + 1;
    else *row = cursor.row;

    *col = cursor.col;
}





<a name="0074_001f">
<a name="0074_0020">
[LISTING TEN]
<a name="0074_0020">


static char screen[24][80];  /* VT100 virtual screen image */

/* put a space character in each virtual image position */

static void clr_display()
{
      short i,j;

      for(j = 0;j < VT100_MAX_ROWS; j++)
         for(i = 0;i < VT100_MAX_COLS; i++)
            screen[j][i]=' ';
}





<a name="0074_0021">
<a name="0074_0022">
[LISTING ELEVEN]
<a name="0074_0022">

static char screen[24][80];  /* VT100 virtual screen image */
char *current, *next, *last;

/* put a space character in each virtual image position */

static void clr_display()
{
current = &screen[0][0];
last = &screen[24 - 1][80 - 1];

do{
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  }while(current < last);
}





<a name="0074_0023">
<a name="0074_0024">
[LISTING TWELVE]
<a name="0074_0024">

static char screen[24][80];  /* VT100 virtual screen image */
static short begin_scroll_row, end_scroll_row;

/*  Scroll screen up one row. Last row is blank. */

static void scroll_up()
{
    short i,j;

    for(j = (begin_scroll_row-1; j<(end_scroll_row-1); i++)
         for(i = 0;i < VT100_MAX_COLS; i++)
            screen[j][i]=screen[j+1][i];

    for(i = 0;i < VT100_MAX_COLS; screen[j][i] = ' ', i++);
}





<a name="0074_0025">
<a name="0074_0026">
[LISTING THIRTEEN]
<a name="0074_0026">

static char screen[24][80];  /* VT100 virtual screen image */
static char *current, *next, *last;
static short begin_scroll_row, end_scroll_row;

/* Scroll screen up one row. Last row is blank.  */

static void scroll_up()
{
    current = &screen[begin_scroll_row - 1][0];
    next    = current + 80;
    last    = &screen[end_scroll_row - 1][0];

do{
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  *current++ = *next++;
  }while(current < last);

last += 80;

do{
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  *current++ = ' ';
  }while(current < last);
}





<a name="0074_0027">
<a name="0074_0028">
[LISTING FOURTEEN]
<a name="0074_0028">

  #include "speedc.h"
  #include "vt100.h"
  #include "string.h"

  void exception_handler(char code);

  void main()
  {
   TERMINAL t;          /* define VT100 setup parameters */
   char string[21];
   char display_string[23];

   cursor.row = cursor.col = 1;               /* globals defined in vt100.h */
   t->origin = t->addlf = t->keymode = 0;
   t->kblock = t->insert = t-> autowrap = 0;
   t->keypad = NUMERIC;

init_emulation_mode(process_VT100_input, (char *)t); /* prototype in vt100.h */

  /* initialize the first 3 lines on the LINKS terminal display by
   using the special LINKS x function.  This function allows the
   user to define distinct display regions on the terminal.  The
   nomenclature is as follows:
   D         means define a display region which is touch sensitive.
   1,2, or 3 means that the touch sensitive area defined will
             generate transmit that particular ASCII character in the
             key buffer when that touch sensitive area is pressed on
             the LINKS screen.  This is also referred to as its name.
   A18;      means the touch sensitive display area in row A (first row
             on the LINKS), columns 1-8 (touch display areas have 8
             distinct areas per row).  The semicolon means the end of
             the touch display definition, and what follows is the
             message to be displayed in that display area.
   B18;      means row B (second row), columns 1-8.
   C18;      means row C (third row), columns 1-8.
  */

   x("D1 A18;  ");
   x("D2 B18;  ");
   x("D3 C18;  ");

   open("98N1",1);      /* open LINKS RS232 port with special LINKS
                              operating system function */

   /* continuously update LINKS terminal display with the current VT100
      virtual image found in rows 1-3, columns 1-20) using read_VT100_scr
      whose prototype is in vt100.h.
   */
   do{
      read_VT100_scr(1, 1, string, 20);     /* read row 1, cols 1-20       */
      strcpy(display_string, "P1");         /* prefix string with special  */
      strcat(display_string, string);       /* links P cmd (print to touch */
      x(display_string);                    /* display area named '1')     */

      read_VT100_scr(2, 1, string, 20);     /* read row 2, cols 1-20       */
      strcpy(display_string, "P2");         /* prefix string with special  */
      strcat(display_string, string);       /* links P cmd (print to touch */
      x(display_string);                    /* display area named '2')     */

      read_VT100_scr(3, 1, string, 20);     /* read row 3, cols 1-20       */
      strcpy(display_string, "P3");         /* prefix string with special  */
      strcat(display_string, string);       /* links P cmd (print to touch */
      x(display_string);                    /* display area named '3')     */

      }while(-1);
}

/* The exception handler is needed for all LINKS applications to handle
   special LINKS control functions.
*/

void exception_handler(char code)
{
   if (code == 4)turn_off();    /* detects ON/OFF button pressed,
                                   and turns LINKS off via LINKS turn_off */
}


Copyright © 1991, Dr. Dobb's Journal


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.