home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / bor_tips / ti152.asc < prev    next >
Encoding:
Text File  |  1988-06-01  |  18.5 KB  |  416 lines

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