home *** CD-ROM | disk | FTP | other *** search
/ RBBS in a Box Volume 1 #2 / RBBS_vol1_no2.iso / 001g / dumtrm.pas < prev    next >
Pascal/Delphi Source File  |  1985-03-19  |  19KB  |  431 lines

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