home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / DUMBTERM.ZIP / DUMBTERM.PAS
Encoding:
Pascal/Delphi Source File  |  1985-12-28  |  18.6 KB  |  429 lines

  1.   TITLE : Interrupt handler
  2. PRODUCT : Turbo Pascal
  3. VERSION : All
  4.      OS : Ms DOS 1.00 or greater
  5.    DATE : 2/1/85
  6.  
  7. {----------------------------------------------------------------------------
  8.       DumbTerm is an example program written to demonstrate the use of both
  9.  interrupt routines and com port communication.  There are some inline
  10.  instructions that are used in the interrupt routine that do not appear in
  11.  the Turbo manual.  This is because the Turbo manual contains incomplete
  12.  inline coding.  When making your own interrupt handlers be sure to save
  13.  all registers,  as has been done in this example.  There is also a
  14.  restoration of the DS register in the handler.  When an interrupt occurs
  15.  all segment registers are set to the code segment of the interrupt routine
  16.  this disallows the use of global variables,  because of a destroyed DS
  17.  register.  To counter act this affect,  the DS is restored via an absolute
  18.  variable "segment".  This program was
  19.  
  20.                                                 Written by,
  21.                                                   Jim McCarthy
  22.                                                   Technical Support
  23.                                                   Borland International
  24.  
  25.                                                 and
  26.                                                   Andy Batony
  27.                                                   Teleware Incorporated
  28.  
  29. -----------------------------------------------------------------------------}
  30.  
  31. PROGRAM dumbterm;
  32.  
  33.   CONST
  34.     hex        : string[16] = '0123456789ABCDEF'; { Constant used to convert }
  35.                                                   { decimal to hex           }
  36.     irq4       = $30;                         { Interrupt vector address for }
  37.                                               { COM1.                        }
  38.     irq3       = $2C;                         { Vector for COM2.             }
  39.     eoi        = $20;                         {                              }
  40.     com1base   = $03F8;                       { Port address of COM1.        }
  41.     com2base   = $02F8;                       { Port address of COM2.        }
  42.                                               { Offset to add to com#base for}
  43.     intenreg   = 1;                           {   Interrupt enable register  }
  44.     intidreg   = 2;                           {   Interrupt id register      }
  45.     linectrl   = 3;                           {   Line control register      }
  46.     modemctrl  = 4;                           {   Modem control register     }
  47.     linestat   = 5;                           {   Line status register       }
  48.     modemstat  = 6;                           {   Modem status register      }
  49.     buffsize   = 1024;                        { Size of the ring buffer      }
  50.  
  51.   TYPE                                        { Type declarations            }
  52.     str4       = string[4];
  53.     str80      = string[80];
  54.     ratetype   = (rate300,rate1200,rate4800,rate9600);
  55.     comtype    = (com1,com2);
  56.     bytechar   = record case boolean of
  57.                    true :(o:byte);
  58.                    false:(c:char)
  59.                  end;
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.     regrec     = record
  70.                    ax,bx,cx,dx,bp,di,si,ds,es,flags : integer;
  71.                  end;
  72.  
  73.   VAR
  74.     segment      : integer absolute cseg:$00A0;  { Address for storing DS    }
  75.     intbuffer    : array [0..buffsize] of bytechar;    { Ring buffer         }
  76.     oldvecseg,                                { Segment of DOS set           }
  77.     oldvecoff,                                { Offset of DOS set com int.   }
  78.     head,                                     { Index to the head of the     }
  79.                                               { ring buffer.                 }
  80.     tail,                                     { Tail index of the ring buff  }
  81.     comport,                                  { Comport address              }
  82.     i            : integer;                   { Counter                      }
  83.     comp         : comtype;                   { Used to specify which comport}
  84.     ch,                                       { Temperary character buffer   }
  85.     kch          : char;                      { Char keyed in from the key-  }
  86.                                               { board                        }
  87.     temp         : string[80];                { Temperary buffer             }
  88.     tbyte,
  89.     lbyte        : byte;
  90.     showok       : boolean;
  91.     registers    : regrec;                    { Registers used in DOS call   }
  92.  
  93. {----------------------------------------------------------------------------
  94.       This is the interrupt handler for the COM1 or COM2 comports.  Notice
  95.  the restoration of the DS register through a move to the AX from address
  96.  CS:00A0.  The absolute variable "segment" is initialized at the begining
  97.  of the program to contain the value of "DSEG".  The inline statments should
  98.  replace the current ones in the Turbo reference manual.
  99. ----------------------------------------------------------------------------}
  100.  
  101.   PROCEDURE IntHandler;
  102.  
  103.     BEGIN
  104.       inline( $50            { push ax        }
  105.              /$53            { push bx        }
  106.              /$51            { push cx        }
  107.              /$52            { push dx        }    { Save all the registers }
  108.              /$57            { push di        }
  109.              /$56            { push si        }
  110.              /$06            { push es        }
  111.              /$1E            { push ds        }
  112.              /$2E            { cs:            }
  113.              /$A1 /$A0 /$00  { mov ax, [00A0] }    { Get the Current data    }
  114.              /$50            { push ax        }    { segment                 }
  115.              /$1F            { pop ds         } ); { Restore the DS register }
  116.       tbyte := port[ comport ];               { Get the character in the port}
  117.       lbyte := port[ comport + linestat ];    { Get the status of the port   }
  118.       If ( head < buffsize ) then             { Check bounds of the ring     }
  119.         head := head + 1                      { buffer,  and if smaller then }
  120.       else                                    { increment by one otherwise   }
  121.         head := 0;                            { set to the first element     }
  122.       intbuffer[ head ].o := tbyte;           { Load the buffer w/ the char. }
  123.       port[$20] := $20;                       {                              }
  124.       inline( $1F            { pop ds         }
  125.              /$07            { pop es         }
  126.              /$5E            { pop si         }
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.              /$5F            { pop di         }
  136.              /$5A            { pop dx         }
  137.              /$59            { pop cx         }   { Restore all registers  }
  138.              /$5B            { pop bx         }
  139.              /$58            { pop ax         }
  140.              /$5D            { pop     bp     }   { Reset the stack to its }
  141.              /$89 /$EC       { mov     sp,bp  }   { proper position        }
  142.              /$5D            { pop     bp     }
  143.              /$CF );         { iret           }   { Return                 }
  144.     END;
  145.  
  146. {-----------------------------------------------------------------------------
  147.       The procedure AskCom gets the comport to comunicate through.
  148. -----------------------------------------------------------------------------}
  149.  
  150.   PROCEDURE AskCom( var comp : comtype );
  151.  
  152.     VAR
  153.       ch : char;
  154.  
  155.     BEGIN
  156.       write( 'What port is the modem in ( 1 or 2 ) : ' );  { Write prompt }
  157.       Repeat
  158.         read( kbd,ch );                       { Get the character and        }
  159.       Until ( ch in ['1','2'] );              { check bounds                 }
  160.       If ( ch = '1' ) then
  161.         Begin
  162.           writeln( 'COM1:' );                 { Set to COM1                  }
  163.           comp := com1;
  164.         End
  165.       else
  166.         Begin
  167.           writeln( 'COM2:' );
  168.           comp := com2;                       { Set to COM2                  }
  169.         End;
  170.     END;
  171.  
  172. {-----------------------------------------------------------------------------
  173.       This procedure sets the baud rate of the comport to either 300, 1200,
  174.  4800, or 9600 baud.  The Divisor latches are set according to the table in
  175.  the IBM hardware technical reference manual ( p. 1-238 ).
  176. -----------------------------------------------------------------------------}
  177.  
  178.   PROCEDURE SetRate(r:ratetype);
  179.  
  180.     VAR
  181.       tlcr,                                   { Line control register        }
  182.       tdlmsb,                                 { Divisor latch MSB            }
  183.       tdllsb    : byte;                       { Divisor latch LSB            }
  184.  
  185.     BEGIN
  186.       tdlmsb:=0;                              { Set DL MSB to 0 for 1200,    }
  187.                                               { 4800 and 9600 baud           }
  188.       case r of                               { Use case to check baud rate  }
  189.         rate300 :  begin                      { Check for 300 baud           }
  190.                      tdlmsb:=1;               { Set DL MSB to 01             }
  191.                      tdllsb:=$80;             { Set DL LSB to 80             }
  192.                    end;                       { for a total of 0180          }
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.         rate1200 : tdllsb:=$60;               { 1200 set LSB to 60           }
  202.         rate4800 : tdllsb:=$18;               { 4800 set LSB to 18           }
  203.         rate9600 : tdllsb:=$0c;               { 0C for 9600 baud             }
  204.       end;
  205.       tlcr:=port[comport+linectrl];           { Get the Line control register}
  206.       port[comport+linectrl]:=tlcr or $80;    { Set Divisor Latch Access Bit }
  207.       port[comport]:=tdllsb;                  { in order to access divisor   }
  208.       port[comport+1]:=tdlmsb;                { latches, then store the      }
  209.                                               { values for the desired baud  }
  210.                                               { rate                         }
  211.       port[comport+linectrl]:=tlcr and $7f;   { then clear the DLAB in order }
  212.                                               { to access to the receiver    }
  213.                                               { buffer                       }
  214.     END;
  215.  
  216. {----------------------------------------------------------------------------
  217.       WhatRate is the input procedure that uses SetRate to set the correct
  218.  baud rate.
  219. ----------------------------------------------------------------------------}
  220.  
  221.   PROCEDURE WhatRate;
  222.  
  223.     BEGIN
  224.       writeln;                                { Display prompt             }
  225.       write('what baud rate ([3]00,[1]200,[4]800,[9]600) ');
  226.       read(kbd,kch);                          { Read in the baud rate      }
  227.       case kch of
  228.         '3':SetRate(rate300);                 { Set the corrisponding rate }
  229.         '1':SetRate(rate1200);                {             .             }
  230.         '4':SetRate(rate4800);                {             .              }
  231.         '9':SetRate(rate9600);                {             .              }
  232.       end;
  233.       writeln(kch);
  234.     END;
  235.  
  236. {-------------------------------------------------------------------------
  237.       The procedure IntOn sets up the interrupt handler vectors,  and
  238.  communication protocal.
  239. -------------------------------------------------------------------------}
  240.  
  241.   PROCEDURE IntOn(com:comtype);
  242.  
  243.     CONST
  244.       bits5=0;
  245.       bits6=1;
  246.       bits7=2;
  247.       bits8=3;
  248.       stopbit1=0;                             { These are constants used     }
  249.       stopbit2=4;                             { to define parity, stop bits, }
  250.       noparity=0;                             { data bits, etc.              }
  251.       parity=8;
  252.       evenparity=16;
  253.       dtrtrue=1;
  254.       rtstrue=2;
  255.       bit3true=8;
  256.  
  257.     VAR
  258.       tbyte   : byte;                         { Temperary byte buffer        }
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.       i       : integer;                      { counter                      }
  268.  
  269.     BEGIN
  270.       head:=0;                                { Initialize the ring buffer   }
  271.       tail:=0;                                { indexes                      }
  272.       case com of
  273.         com1:comport:=com1base;               { Set the com port to talk to  }
  274.         com2:comport:=com2base;
  275.       end;
  276.       tbyte := port[ comport ];               { Read the ports to clear any  }
  277.       tbyte := port[ comport + linestat ];    { error conditions             }
  278.       WhatRate;                               { Get the baud rate            }
  279.       port[ comport + linectrl ] := bits7 + stopbit1 + noparity;
  280.                                               { Set the protocall            }
  281.       port[ comport + modemctrl ] := dtrtrue + rtstrue + bit3true;
  282.       port[ comport + intenreg ] := 1;        { Enable com port interrupts   }
  283.       tbyte := port[$21];                     {                              }
  284.       with registers do
  285.         begin
  286.           ax:=$2500;                          { Load the function number for }
  287.                                               { redefining an interrupt      }
  288.           ds:=cseg;                           { Get and set the segment and  }
  289.           dx:=ofs(IntHandler);                { offset of the handler        }
  290.         end;
  291.       case com of
  292.         com1: begin
  293.                 oldvecoff:=memw[0000:irq4];   { Save the segment and offset  }
  294.                 oldvecseg:=memw[0000:irq4+2]; { of the DOS interrupt handler }
  295.                 registers.ax:=registers.ax+$0c; { Use the COM1: interrupt    }
  296.                 intr($21,registers);          { Call DOS to reset INT 0C     }
  297.                 port[$21]:=tbyte and $ef;     {                              }
  298.               end;
  299.         com2: begin
  300.                 oldvecoff:=memw[0000:irq3];   { Same as above                }
  301.                 oldvecseg:=memw[0000:irq3+2]; { Same as above                }
  302.                 registers.ax:=registers.ax+$0b; { Use the COM2: interrupt    }
  303.                 intr($21,registers);          { Call DOS                     }
  304.                 port[$21]:=tbyte and $f7;     {                              }
  305.               end;
  306.       end;
  307.       inline($fb);                            { Enable interrupts            }
  308.     END;
  309.  
  310. {-----------------------------------------------------------------------------
  311.       This procedure restores the original system values to what they
  312.  were before the interrupt handler was set into action.
  313. -----------------------------------------------------------------------------}
  314.  
  315.   PROCEDURE IntOff;
  316.  
  317.     VAR
  318.       tbyte:byte;
  319.  
  320.     BEGIN
  321.       inline($FA);  { CLI }                   { Disable interrupts           }
  322.       tbyte:=port[$21];                       {                              }
  323.       port[comport+intenreg]:=0;              { Disable COM interrupts       }
  324.       If comport=$3f8 then                    { If using COM1: then          }
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.         begin
  334.           port[$21]:=tbyte or $10;            {                              }
  335.           memw[0000:irq4]:=oldvecoff;         { Restore the DOS interrupt    }
  336.           memw[0000:irq4+2]:=oldvecseg;       { handler                      }
  337.         end
  338.       else
  339.         begin
  340.           memw[0000:irq3]:=oldvecoff;         { Restore the DOS interrupt    }
  341.           memw[0000:irq3+2]:=oldvecseg;       { handler                      }
  342.           port[$21]:=tbyte or $08;            {                              }
  343.         end;
  344.     END;
  345.  
  346. {-----------------------------------------------------------------------------
  347.       If the ring buffer indexes are not equal then ReadCom returns the
  348.  char from either the COM1: or COM2: port.  The character is read from the
  349.  ring buffer and is stored in the FUNCTION result.
  350. -----------------------------------------------------------------------------}
  351.  
  352.   FUNCTION ReadCom : char;
  353.  
  354.     BEGIN
  355.       If ( head <> tail ) then           { Check for ring buffer character   }
  356.         begin
  357.           If ( tail < buffsize ) then    { Check the limits of the ring      }
  358.             tail := tail + 1             { and set tail accordingly          }
  359.           else
  360.             tail := 0;
  361.           ReadCom := intbuffer[tail].c;  { Get the character                 }
  362.         end;
  363.     END;
  364.  
  365. {----------------------------------------------------------------------------
  366.       This procedure outputs directly to the communications port the byte
  367.  equivilent of the character to be sent.
  368. ----------------------------------------------------------------------------}
  369.  
  370.   PROCEDURE WriteCom( ch : char );
  371.  
  372.     VAR
  373.       tbyte:byte;
  374.  
  375.     BEGIN
  376.       tbyte:=ord(ch);                { Change to byte format                }
  377.       port[comport]:=tbyte;          { Output the character                 }
  378.     END;
  379.  
  380. {---------------------------------------------------------------------------
  381.       When the interrupt routine is called because of a com port interrupt
  382.  the head index is incremented by one,  but does not increment the tail
  383.  index.  This causes the two indexes to be unequal,  and ModemInput to
  384.  become true.
  385. ---------------------------------------------------------------------------}
  386.  
  387.   FUNCTION ModemInput:boolean;
  388.  
  389.     begin
  390.       ModemInput:=(head<>tail);
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.     end;
  400.  
  401. BEGIN
  402.   segment := dseg;                { segment is an absolute variable used  }
  403.                                   { by the interrupt routine to restore   }
  404.                                   { the DS register to point to the DSEG  }
  405.   AskCom( comp );                 { Get the com port to use               }
  406.   IntOn( comp );                  { Set up the interrupt routine          }
  407.   ch:=' ';                        { Initialize ch for the loop            }
  408.   Repeat
  409.     If keypressed then            { If a key is pressed on the keyboard   }
  410.       begin
  411.         read(kbd,kch);            { then the program reads it in and      }
  412.         kch := Upcase(kch);
  413.         write( kch );
  414.         If ( kch = chr(13)) then writeln;
  415.                                   { checks if the program should be ended }
  416.         WriteCom(kch);            { Write the character to the com port   }
  417.       end;
  418.     If ModemInput then            { If something was placed in the ring   }
  419.       begin                       { buffer then                           }
  420.         write('[');
  421.         ch:=ReadCom;              { it is read in and printed to the      }
  422.         write(ch);                { screen                                }
  423.         If ch=chr(13) then writeln;
  424.         write(']');
  425.       end;
  426.   Until kch=chr(27);              { This loops ends when <esc> is hit     }
  427.   IntOff;                         { Restore the enviornment               }
  428. END.       { Main program }
  429.