SWAGOLX.EXE (c) 1993 GDSOFT  ALL RIGHTS RESERVED 00022         FREQUENTLY ASKED QUESTIONS/TUTORIALS                              1      05-28-9313:45ALL                      SWAG SUPPORT TEAM        General PASCAL FAQ       IMPORT              289                 ANSWERS TO FREQUENTLY ASKED PASCAL QUESTIONSã        ============================================ãã1...ã                                                                              ã  Q. How do I pass an error level code when my program finishes?              ã                                                                              ã  A. The halt procedure takes an optional parameter of type word. Thus -      ã                                                                              ã         halt(1);                                                             ã                                                                              ã     terminates the program with an errorlevel of 1.  If halt is used without ã     a parameter it is the same as -                                          ã                                                                              ã         halt(0);                                                             ã                                                                              ã     Note:  When a program is terminated using the halt procedure any exit    ã            procedure that has previously been set up is executed.            ããã2...ã                                                                          ã  Q. How do I empty the keyboard buffer?                                      ã                                                                              ã  A. There are several ways that this can be achieved.  However the safest    ã     is -                                                                     ã                                                                              ã        while Keypressed do ch := ReadKey;                                    ã                                                                              ã     This requires that a variable ch of type char is declared and the crt    ã     unit be used.  To do it without using a variable -                       ã                                                                              ã       while Keypressed do while ReadKey = #0 do;                             ã                                                                              ã     or if using TP6 with extended syntax enabled -                           ã                                                                              ã        while KeyPressed do ReadKey;                                          ã                                                                              ã     If you do not wish to incur the substantial overhead involved with the   ã     use of the CRT unit and there is no requirement for the program to run   ã     under a multi-tasker -                                                   ã                                                                              ã        var                                                                   ã          head : byte absolute $40:$1c;                                       ã          tail : byte absolute $40:$1e;                                       ã                                                                              ã        tail := head;                                                         ãã3...ãã  Q. When I redirect the screen output of my programs to a file the file is   ã     empty and the output still appears on the screen. What am I doing        ã     wrong?                                                                   ã                                                                              ã  A. You are probably using the CRT unit and its default method of writing    ã     to stdout is by direct screen writes.  In order to enable output to be   ã     redirected all writes must be done by DOS.  Setting the variable         ã     DirectVideo to false has no effect on redirection as all it does is use  ã     the BIOS for screen writes - not DOS.                                    ã                                                                              ã     To enable redirection you must not use the CRT unit                      ã                                                                              ã     OR                                                                       ã                                                                              ã     assign(output,'');                                                       ã     rewrite(output);                                                         ã                                                                              ã     This will make all output go through DOS and thus can be redirected if   ã     desired.  To restore the default situation -                             ã                                                                              ã     AssignCRT(output); rewrite(output);                                      ããã4...ãã   Q. How do I make a string that is lower or mixed case all uppercase?ã                                                                              ã   A. There are several ways to convert lower case characters to upper case.  ã      Here are some of them.                                                  ã                                                                              ã      As a procedure (excluding asm code this is the fastest way)             ã                                                                              ã        procedure StrUpper(var st: string);                                   ã          var x : byte;                                                       ã          begin                                                               ã            for x := 1 to length(st) do                                       ã              st[x] := UpCase(st[x]);                                         ã          end;                                                                ã                                                                              ã      As a function (slower but sometimes more convenient) -                  ã                                                                              ã        function StrUpper(st: string): string;                                ã          var x : byte;                                                       ã          begin                                                               ã            StrUpper[0] := st[0];                                             ã            for x := 1 to length(st) do                                       ã              StrUpper[x] := UpCase(st[x]);                                   ã          end;                                                                ã                                                                              ã      Both the above are suitable for the English language .  However from    ã      version 4.0 onwards, DOS has had the facility to do this in a way that  ã      is country (language) specific.  I am indebted to Norbert Igl for the   ã      basic routine.  I have modified his code slightly.  For the anti-goto   ã      purists this is a good example of a goto that is convenient, efficient, ã      self-documenting and structured.  The dos calls would make this method  ã      the slowest of all.                                                     ã                                                                              ã     function StrUpper(s: string): string;                                    ã       { Country specific string-to-uppercase conversion. Requires DOS unit } ã       label                                                                  ã         fail;                                                                ã       var                                                                    ã         regs : registers;                                                    ã         x    : byte;                                                         ã       begin                                                                  ã         if lo(DosVersion) >= 4 then begin                                    ã           with regs do begin                                                 ã             ax := $6521;                                                     ã             ds := seg(s);                                                    ã             dx := ofs(s[1]);                                                 ã             cx := length(s);                                                 ã             msdos(regs);                                                     ã             if odd(flags) then { the attempted conversion failed so }        ã               goto fail;                                                     ã           end; { with }                                                      ã         end { if DOS >= 4.0 } else                                           ã       fail:                                                                  ã           for x := 1 to length(s) do                                         ã             s[x] := UpCase(s[x]);                                            ã         StrUpper := s;                                                       ã       end; { StrUpper }                                                      ãããã5...ã                                                                              ã   Q. When I include ANSI codes in a string and write that string to the      ã      screen the actual codes appear on the screen, rather than the results   ã      they are supposed to achieve.                                           ã                                                                              ã   A. In order for ANSI codes to be interpreted, screen writes must be        ã      directed through DOS and there must have been a suitable driver loaded  ã      via the config.sys file at boot time.  All output can be directed       ã      through DOS and the driver by -                                         ã                                                                              ã      Not using the crt unit                                                  ã                                                                              ã      OR -                                                                    ã                                                                              ã      assign(output,'');                                                      ã      rewrite(output);                                                        ã                                                                              ã      in which case ALL screen writes are "ANSI code sensitive"               ã                                                                              ã      OR -                                                                    ã                                                                              ã      You can set up write procedures that will be "ANSI code sensitive".     ã      (You will need an initialisation procedure to set this up.)             ã                                                                              ã      var                                                                     ã        ansi : text;                                                          ã                                                                              ã      procedure AssignANSI(var ansifile : text);                              ã        begin                                                                 ã          assign(ansifile,'CON');                                             ã          rewrite(ansifile);                                                  ã        end; { AssignANSI }                                                   ã                                                                              ã      procedure WriteANSI(var st: string);                                    ã        begin                                                                 ã          write(ansi,st)                                                      ã        end; { WriteANSI }                                                    ã                                                                              ã      procedure WriteLnANSI(var st: string);                                  ã        begin                                                                 ã          writeANSI(st);                                                      ã          writeln(ansi);                                                      ã        end; { WriteANSI }                                                    ã                                                                              ã      ObviousLy, if the ANSI.SYS driver (or an equivalent) is not installed   ã      none of the above can work.                                             ã                                                                              ã      Setting the variable DirectVideo in the CRT unit to false will not      ã      achieve the desired result as this merely turns off direct screen       ã      writes and uses the BIOS for all screen output.                         ããã6...ã                                                                              ã   Q. When I try to shell to DOS nothing happens. What am I doing wrong?      ã                                                                              ã   A. In order to be able to execute any child process there must be          ã      sufficient memory available for it to load and execute.  Unless you     ã      advise differently at compile time, a Turbo Pascal program grabs all    ã      available memory for itself when it is loaded.  To reserve memory for a ã      child process use the compiler memory directive -                       ã                                                                              ã        {$M 16384,0,0)                                                        ã      the default is -                                                        ã        {$M 16384,0,655360}                                                   ã                                                                              ã      The first figure - StackMin - is the amount of memory to be allocated   ã      for the stack:                                                          ã                                                                              ã      Minimum is:    1024                                                     ã      Default is:   16384                                                     ã      Maximum is:   65520                                                     ã                                                                              ã      The next figure - HeapMin -is the minumum amount of memory to be        ã      allocated for the heap. If there is less memory available than this     ã      figure the program will not load.                                       ã                                                                              ã      Minimum is:          0                                                  ã      Default is:          0                                                  ã      Maximum is:     655360  In practice it will be the amount of free       ã                              memory less the space required for the stack,   ã                              less the code space of the program.  You should ã                              set this to 0 unless your program uses the      ã                              heap.  In that case, set it to the lowest       ã                              possible figure to prevent heap allocation      ã                              errors.  In most cases it is best to leave it   ã                              at zero and do error checking within the        ã                              program for sufficient memory at allocation     ã                              time.                                           ã                                                                              ã      The last figure is the crucial on as regards child processes.  It       ã      should always be low enough to leave memory left over for a child       ã      process and high enough not to cause problems for the program when      ã      allocating heap memory.                                                 ã                                                                              ã      Minimum is:  HeapMin                                                    ã      Default is:  655360                                                     ã      Maximum is:  655360     If less than the requested amount is available  ã                              no error is reorted.  Instead all available     ã                              memory is allocated for heap use.               ãããã7...ã                                                                              ã   Q. How do I shell to DOS?                                                  ã                                                                              ã   A. SwapVectors;                                                            ã      exec(GetEnv('COMSPEC','');                                              ã      SwapVectors;                                                            ã                                                                              ã      Read previous section on memory allocation.                             ã                                                                              ã      I find that it is a good idea to write my own Exec function which will  ã      do everything that is needed for me.  I have it return an integer value ã      that is the DosError code.                                              ã                                                                              ã      function Exec(p1,p2: string);                                           ã        begin                                                                 ã          SwapVectors;                                                        ã          Dos.Exec(p1,p2);                                                    ã          SwapVectors;                                                        ã          Exec := DosError;                                                   ã        end;                                                                  ã                                                                              ã      This enables me to have a statement such as -                           ã                                                                              ã      ReportError(Exec(GetEnv('COMPSEC'),''));                                ã                                                                              ã      Now you can have an empty ReportError procedure or you can make it      ã      report the error - whatever is suitable for you application.            ããã8...ã                                                                              ã   Q. When I execute a child process redirection does not work. Why?          ã                                                                              ã   A. Redirection of a child process's output only works if it is run under   ã      another copy of the command processor.  So -                            ã                                                                              ã      exec('YourProg.exe',' > nul');    will not work but                     ã      exec(GetEnv('COMSPEC'),'/c YourProg > nul'); will work.                 ããã9...ãã   Q. How do I read an errorlevel from a child process?ãã   A. After executing a child process the errorlevel returned can be readã      by calling the DosExitCode function which returns a word.  The lowã      byte is the errorlevel.  A full description is in the manual.ãã      If the command interpreter is the child process and it in turnã      executes a child process then the errorlevel of the second childã      process cannot be read without resorting to some trickery.ããã10...ãã   Q. When I read a text file that has lines exceeding 255 characters Iã      lose all those characters from the 256th one on each time there is aã      line that exceeds that length.  How can I prevent this?ãã   A. Turbo Pascal's readln procedure reads a line up to the 255thã      character then skips to the next line.  To get around this youã      should declare a buffer at least as large as the longest possibleã      line and then use the read procedure.  The best size for the bufferã      is a multiple of 2048 bytes.ãã      constã        BufferSize = 2048;ã        LineLength = 78;ã      typeã        textbuffer = array[1..BufferSize] of char;ã      varã        st          : string;ã        f           : text;ã        buffer      : textbuffer;ãã      function ReadTxtLn(var tf: text; var s: string; max: byte): integer;ã        { Reads a string of a maximum length from a text file }ã        varã          len         : byte absolute s;ã        beginã          len := 0;ã          {$I-}ã          while (len < max) and not eoln(tf) do beginã            inc(len);ã            read(tf);ã          end;ã          if eoln(tf) thenã            readln(tf);ã          ReadTxtLn := IOResult;ã          {$I+}ã        end; { ReadTxtLn }ãã      beginã        assign(f,filename);ã        reset(f);ã        SetTextBuf(f,buffer);ã        while not eof(f) and (ReadTxtLn(f,st,LineLength) = 0) doã          writeln(st);ã        close(f);ã      end.ããã11...ãã   Q. How do I convert nul terminated asciiz strings to Turbo Pascalã      strings?ãã   A. Here is a function that will do that -ãã      function Asc2Str(var s; max: byte): string;ã        { Converts an ASCIIZ string to a Turbo Pascal string }ã        { with a maximum length of max.                      }ã        var starray  : array[1..255] of char absolute s;ã            len      : integer;ã        beginã          len        := pos(#0,starray)-1;              { Get the length }ã          if (len > max) or (len < 0) then      { length exceeds maximum }ã            len      := max;                         { so set to maximum }ã          Asc2Str    := starray;ã          Asc2Str[0] := chr(len);                           { Set length }ã        end;  { Asc2Str }ããã12...ãã   Q. How can I tell if a particular bit of a variable is set or not? How canã      I set it?  How can I turn it off? How can I make a large bit map andã      then determine if a particular bit - say bit 10000 is on/of?ãã   A. This question, or a variation of it, is one of the most commonly askedã      questions in the echo and there are several ways of doing what isã      wanted.  None are necessarily right or wrong.  The way I will describeã      is designed to take up as little code/data space as possible.  I do notã      attempt to explain the theory behind these functions as this can beã      obtained from any good book. Question 16 also contains valuable extraã      help on the subject of truth tables.ãã      The use of sets can be the best bit manipulation method if you haveã      control over the data being used. Here is an example of a byte variableã      for a BBS program which sets various user access level flags.ãã         Bit 0 = Registered Userã             1 = Twitã             2 = Normalã             3 = Extraã             4 = Privilegedã             5 = Visiting Sysopã             6 = Assistant Sysopã             7 = Sysopãã       typeã         status_type  = (Registered,ã                         Twit,ã                         Normal,ã                         Extra,ã                         Privileged,ã                         VisitingSysop,ã                         AssistantSysop,ã                         Sysop);ã          status_level = set of status_type;ãã       varã         access_flags  : status_level;ãã      Let us assume you have someone who logs on and you wish to determineã      his user access level.  After reading access_flags from the user dataã      file -ãã           if Sysop in access_flags then ....ãã      To set the sysop flag -ãã           access_flags := access_flags + [Sysop];ãã      To reset the sysop flag -ãã           access_flags := access_flags - [Sysop];ãã      However on many occasions using a set may not be a suitable method.ã      You may simply need to know if bit 5 is set or not.  Here is the methodã      that I consider the best -ãã        function BitIsSet(var V,  bit: byte): boolean;ã          beginã            BitIsSet := odd(V shr bit);ã          end;ãã      To set a bit -ãã         procedure SetBit(var V: byte; bit: byte);ã           beginã             V := V or (1 shl bit);ã           end;ãã      To reset a bit -ãã         procedure ResetBit(var V: byte; bit: byte);ã           beginã             V := V and not(1 shl bit);ã           end;ãã      To toggle (flip) a bit -ãã         procedure ToggleBit(var V: byte; bit: byte);ã           beginã             V := V xor (1 shl bit);ã           end;ãã      Now a bit map can be made up from an array of bytes.  If stored on theã      heap you can test any bit up to number 524159 (zero based).  Here'sã      how.ãã      typeã        map = array[0..maxsize] of byte;ã        { set maxsize to number of bits div 8 -1 needed in the bit map }ãã      function BitSetInBitMap(var x; numb : longint): boolean;ã        { Tests the numb bit in the bitmap array }ã        var m: map absolute x;ã        beginã          BitSetInBitMap := odd(m[numb shr 3] shr (numb and 7));ã        end;ãã      procedure SetBitInBitMap(var x; numb: word);ã        { Sets the numb bit in the bitmap array }ã        var m: map absolute x;ã        beginã          m[numb shr 3] := m[numb shr 3] or (1 shl (numb and 7))ã        end;ãã      procedure ResetBitInBitMap(var x; numb : longint);ã        { Resets the numb bit in the bitmap array }ã        var m: map absolute x;ã        beginã         m[numb shr 3] := m[numb shr 3] and not(1 shl (numb and 7));ã        end;ãã      procedure ToggleBitInBitMap(var x; numb : longint);ã        { Toggles (flips) the numb bit in the bitmap array }ã        var m: map absolute x;ã        beginã          m[numb shr 3] := m[numb shr 3] xor (1 shl (numb and 7));ã        end;ããã13...ãã   Q. How can I find a particular string in any file - text or binary?ãã   A. The Boyer-Moore string search algorithm is considered to be the fastestã      method available.  However in a rare worst-case scenario it can beã      slightly slower than a linear brute-force method.  The followingã      demonstration program will show how it works and could easily beã      modified to allow for command line paramters etc.ããã      program BMSearchDemo;ãã      typeã        bigarray = array[0..32767] of byte;ã        baptr    = ^bigarray;ã        BMTable  = array[0..255] of byte;ãã      constã        KeyStr : string = 'Put whatever you want found here';ã        fname  : string = 'f:\Filename.txt';ãã      varã        Btable : BMtable;ã        buffer : baptr;ã        f      : file;ã        result,ã        position : word;ã        offset : longint;ã        finished,ã        Strfound  : boolean;ãã      procedure MakeBMTable(var t : BMtable; var s);ã        { Makes a Boyer-Moore search table. s = the search string t = the table }ã        varã          st  : BMtable absolute s;ã          slen: byte absolute s;ã          x   : byte;ã        beginã          FillChar(t,sizeof(t),slen);ã          for x := slen downto 1 doã            if (t[st[x]] = slen) thenã              t[st[x]] := slen - xã        end;ãã      function BMSearch(var buff,st; size : word): word;ã        { Not quite a standard Boyer-Moore algorithm search routine }ã        { To use:  pass buff as a dereferenced pointer to the buffer}ã        {          st is the string being searched for              }ã        {          size is the size of the buffer                   }ã        { If st is not found, returns $ffff                         }ã        varã          buffer : bigarray absolute buff;ã          s      : array[0..255] of byte absolute st;ã          len    : byte absolute st;ã          s1     : string absolute st;ã          s2     : string;ã          count,ã          x      : word;ã          found  : boolean;ã        beginã          s2[0] := chr(len);       { sets the length to that of the search string }ã          found := false;ã          count := pred(len);ã          while (not found) and (count < (size - len)) do beginã            if (buffer[count] = s[len]) then { there is a partial match } beginã              if buffer[count-pred(len)] = s[1] then { less partial! } beginã                move(buffer[count-pred(len)],s2[1],len);ã                found := s1 = s2;                   { if = it is a complete match }ã                BMSearch := count - pred(len);      { will stick unless not found }ã              end;ã              inc(count);                { bump by one char - match is irrelevant }ã            endã            elseã              inc(count,Btable[buffer[count]]);   { no match so increment maximum }ã          end;ã          if not found thenã            BMSearch := $ffff;ã        end;  { BMSearch }ããã      beginã        new(buffer);ã        assign(f,fname);ã        reset(f,1);ã        offset := 0;ã        MakeBMTable(Btable,KeyStr);ã        repeatã          BlockRead(f,buffer^,sizeof(buffer^),result);ã          position := BMSearch(buffer^,KeyStr,result);ã          finished := (result < sizeof(buffer^)) or (position <> $ffff);ã          if position = $ffff thenã            inc(offset,result);ã          Strfound := position <> $ffff;ã        until finished;ã        close(f);ã        if Strfound thenã          writeln('Found at offset ',offset)ã        elseã          writeln('Not found');ã      end.ãã14...ãã   Q. How can I put a apostrophe in a string?ãã   A. Just put in extra apostrophes.  If you want st to be equal to theã      string -ã        The word 'quoted' is in quotesã      do this -ã        st := 'The word ''quoted'' is in quotes';ãã      if you want the following to be written to screen -ã        'This is a quoted string'ã      do this -ã        writeln('''This is a quoted string''');ããã15...ãã   Q. What are the best books to purchase to help me learn Turbo Pascal?ãã   A. There are many good books for learners.  Here are a few -ãã      Complete Turbo Pascal - Third Edition - Jeff Duntemannã      Mastering Turbo Pascal 6 - Tom Swannã      Turbo Pascal - The Complete Reference - O'Brien.ãã      For advanced users there are also many good books.  Here are a fewã      that I have found useful - (Those marked with an asterisk are notã      purely for Turbo Pascal)ãã      Turbo Pascal 6 - Techniques and Utilities - Rubenkingã      Turbo Pascal Internals - Tischerã      * PC System Programming for Developers - Tischerã      * Undocumented DOS - Schulmanãã      Any learner would be well advised to obtain a well known libraryã      such as Technojock's Turbo Toolkit (TTT) which is shareware andã      study the source code.ãã 16.ãã   Q. hat are "truth tables" and how do they work?ãã   A. Truth tables are a set of rules that are used to determine the result ofã      logical operations.  The logical operators are -ãã        NOTã        ANDã        ORã        XOR.ãã      Here is a brief explanation of truth tables.  When two values areã      logically compared by using a logical operator each bit of one value isã      directly compared to the corresponding bit in the other value and theã      same bit in the returned value is set or reset according to theã      following truth table.ãã             NOT         AND             OR            XORã         not 1 = 0    0 and 0 = 0    0 or 0 = 0    0 xor 0 = 0ã         not 0 = 1    0 and 1 = 0    0 or 1 = 1    0 xor 1 = 1ã                      1 and 0 = 0    1 or 0 = 1    1 xor 0 = 1ã                      1 and 1 = 1    1 or 1 = 1    1 xor 1 = 0ãã      NOT reverses the bit.ã      AND sets the returned bit if both compared bits are set.ã      OR  sets the returned bit if either of the compared bits are set.ã      XOR sets the returned bit if the compared bits are not the same.ããã 17.ãã   Q. What are pointers and how can I use them?  I have heard that they areã      variables that can be created and discarded as required thus savingã      memory.  Is this true?ãã   A. A pointer is a variable that contains a memory address.ãã      The heap is all of that memory allocated by DOS to a program for itsã      use that has not been used by the program for its code, global data orã      stack.ãã      Dynamic variables are variables that have had space allocated for themã      on the heap.ãã      Dynamic variables have no identifier (are unnamed).  Because of thisã      they need an associated variable that can be used to find where theyã      reside in memory. Pointers are ideal for this but need some method toã      define what type of data it is that they are pointing at.  Pascalã      provides this method.ãã        typeã          Str10Ptr = ^string[10];ã          { This means Str10Ptr is a pointer that points to data of type }ã          { string[10].                                                  }ã        varã          S : Str10Ptr;ãã      In the above example S is a pointer that has been defined as pointingã      to an address in memory that will contain (or should contain) data ofã      type string[10].ãã      However how does S get this value?  How does it know where that data'sã      address is supposed to be?  Well until the programmer allocates memoryã      for that data S's value is undefined, so it could be literallyã      pointing anywhere. So it is *vital* that before we try to use it toã      use/assign data from/to that memory location we give S a memoryã      address that is not being used for any other purpose at the moment andã      that is big enough to hold the data that we want to place into it - inã      this case at least 11 bytes.  We do this by -ãã        new(S);ãã      Pascal has now allocated at least 11 bytes of heap and has allocated Sã      with the address of the FIRST byte of that allocation.ãã      Ok... so far so good! How do we access that data (remembering that itã      has no name).  Well we "dereference" the pointer. This is done byã      placing a carat sign immediately following the pointer's identifier.ãã        S^ := 'Joe Bloggs';ãã      This statement actually means "Place the string 'Joe Bloggs' into theã      memory address that S contains". This is referred to as "derferencing"ã      the pointer S.ãã      To "reference" a dynamic variable we "dereference" its associatedã      pointer variable.  We cannot say -ãã        S := 'Joe Bloggs';ãã      because S is a pointer and that would be trying to give a pointer aã      string type value - a compiler "type mismatch" would occur. So everyã      time we wish to access that dynamic variable we dereference it.ãã      To delete the dynamic variable once it is of no further use is just aã      matter of -ãã        dispose(S);ãã      What this statement does is release the memory previously used by S^ã      and make it available to be used for other purposes by the program.ã      Depending on the version of Pascal you are using it may not erase orã      alter the contents of that memory and it may not give S a new value.ã      However any attempt to dereference S is an error as the integrity ofã      that memory location has been lost - it may have been allocated toã      other data.ãã      Pointers do not *have* to point to a memory location in the heap orã      even have their value always allocated by using the New procedure. Anyã      valid memory address can be assigned to them and then they can beã      dereferenced as shown above.  As a simple example of this lets say youã      want to examine the contents of the 16 byte area at $40:$f0 (the ICAã      area). You could - (TP specific)ãã         typeã           ICA_Ptr = ^array[0..15] of byte;ã         varã           B       : byte;ã           ICA     : ICA_Ptr;ãã          ICA := ptr($40,$f0);ãã      Now ICA points to the address specified and you can dereference it -ãã          B := ICA^[10];ãã      Hope that helps get you started into the complex world of memoryã      management and manipulation using pointers.  There are countlessã      permutations and methods that can be used.ããã 18.ãã   Q. How do I do word wrap?ãã   A. The demo program WRAP.PAS in this archive demonstrates both word wrapã      and the justifying of text.ããããããããããã                                                                                                  2      05-28-9313:45ALL                      SWAG SUPPORT TEAM        Dos Programming FAQ      IMPORT              491         =====================ãsection 1. General questionsã    101. Why won't my code work?ã    102. What is this newsgroup about?ã    103. What's the difference from comp.sys.ibm.pc.programmer?ã    104. What other newsgroups should I know about?ããsection 2. Compile and linkã    201. What the heck is "DGROUP > 64K"?ã    202. How do I fix "automatic data segment exceeds 64K" or "stackã         plus data exceed 64K"?ã    203. Will Borland C code and Microsoft C code link together?ã    204. Why did my program bomb at run time with "floating pointã         formats not linked"?ã    205. Why did my program bomb with "floating point not loaded"?ã    206. How can I change the stack size in Borland's C compilers?ã    207. What's the format of an .OBJ file?ã    208. What's the format of an .EXE header?ã    209. What's the difference between .COM and .EXE formats?ããsection 3. Keyboardã    301. How can I read a character without echoing it to the screen,ã         and without waiting for the user to press the Enter key?ã    302. How can I find out whether a character has been typed, withoutã         waiting for one?ã    303. How can I disable Ctrl-C/Ctrl-Break and/or Ctrl-Alt-Del?ã    304. How can I disable the print screen function?ã    305. How can my program turn NumLock (CapsLock, ScrollLock) on/off?ã    306. How can I speed up the keyboard's auto-repeat?ã    307. What is the SysRq key for?ã    308. How can my program tell what kind of keyboard is on the system?ã    309. How can I tell if input, output, or stderr has been redirected?ããsection 4. Disks and filesã    401. What drive was the PC booted from?ã    402. How can I boot from drive b:?ã    403. Which real and virtual disk drives are valid?ã    404. How can I make my single floppy drive both a: and b:?ã    405. Why won't my C program open a file with a path?ã    406. How can I redirect printer output to a file?ã    407. How can my program open more files than DOS's limit of 20?ã    408. How can I read, create, change, or delete the volume label?ã    409. How can I get the disk serial number?ã    410. What's the format of .OBJ, .EXE., .COM files?ããsection 5. Serial ports (COM ports)ã    501. How do I set my machine up to use COM3 and COM4?ã    502. How do I find the I/O address of a COM port?ã    503. But aren't the COM ports always at I/O addresses 3F8, 2F8, 3E8,ã         and 2E8?ã    504. How do I configure a COM port and use it to transmit data?ãsection 6. Other hardware questions and problemsã    601. Which 80x86 CPU is running my program?ã    602. How can a C program send control codes to my printer?ã    603. How can I redirect printer output to a file?ã    604. Which video adapter is installed?ã    605. How do I switch to 43- or 50-line mode?ã    606. How can I find the Microsoft mouse position and button status?ã    607. How can I access a specific address in the PC's memory?ã    608. How can I read or write my PC's CMOS memory?ã    609. How can I access memory beyond 640K?ãsection 7. Other software questions and problemsã    701. How can a program reboot my PC?ã    702. How can I time events with finer resolution than the systemã         clock's 55 ms (about 18 ticks a second)?ã    703. How can I find the error level of the previous program?ã    704. How can a program set DOS environment variables?ã    705. How can I change the switch character to - from /?ã    706. Why does my interrupt function behave strangely?ã    707. How can I write a TSR (terminate-stay-resident) utility?ã    708. How can I write a device driver?ã    709. What can I use to manage versions of software?ã    710. What's this "null pointer assignment" after my C programã         executes?ããsection A. Downloadingã    A01. What is garbo?  What is wustl?ã    A02. What are Simtel and "mirror sites"?  What good are they?ã    A03. Where do I find program <mumble>?ã    A04. How can I check Simtel or garbo before I post a request for aã         program?ã    A05. How do I download and decode a program I found?ã    A06. Where is UUDECODE?ã    A07. Why do I get errors when extracting from a ZIP file Iã         downloaded?ããsection B. Vendors and productsã    B01. How can I contact Borland?ã    B02. How can I contact Microsoft?ã    B03. What's the current version of PKZIP?ã    B04. What's in Borland Pascal/Turbo Pascal 7.0?ã    B05. What's in Microsoft C/C++ 7.0?ãsection C. More informationã    C01. Are there any good on-line references for PC hardwareã         components?ã    C02. Are there any good on-line references for PC interrupts?ã    C03. What and where is "Ralf Brown's interrupt list"?ã    C04. Where can I find lex, yacc, and language grammars?ã    C05. What's the best book to learn programming?ã    C06. Where are FAQ lists archived?ã    C07. Where can I get the latest copy of this FAQ list?ããããsection 1. General questionsã============================ããQ101. Why won't my code work?ãã    First you need to try to determine whether the problem is in yourã    use of the programming language or in your use of MSDOS and your PCã    hardware.  (Your manual should tell you which features are standardã    and which are vendor- or MSDOS- or PC-specific.  You _have_ readã    your manual carefully, haven't you?)ãã    If the feature that seems to be working wrong is something relatedã    to your PC hardware or to the internals of MS-DOS, this group is theã    right place to ask.  (Please check this list first, to make sureã    your question isn't already answered.)ãã    On the other hand, if your problem is with the programming language,ã    the comp.lang hierarchy (including comp.lang.pascal and comp.lang c)ã    is probably a better resource.  Please read the other group's FAQã    list thoroughly before posting.  (These exist in comp.lang.c,ã    comp.lang.c++, comp.lang.modula3, comp.lang.lisp, comp.lang.perl;ã    they may exist in other groups as well.)  It's almost never a goodã    idea to crosspost between this group and a language group.ãã    Before posting in either place, try to make your program as small asã    possible while still exhibiting the bad behavior.  Sometimes thisã    alone is enough to show you where the trouble is.  Also edit yourã    description of the problem to be as short as possible.  This makesã    it look more like you tried to solve the problem on your own, andã    makes people more inclined to try to help you.ãã    When you do post a question, it's good manners to say "email please;ã    I'll post a summary."  Then everybody else in the group doesn't haveã    to read ten virtually identical responses.  Of course, then you haveã    to follow through.  A summary is not simply pasting together all theã    email you received.  Instead, write your own (brief) description ofã    the solution:  this is the best way to make sure you reallyã    understand it.  Definitely don't repost people's cute signatures.ããQ102. What is this newsgroup about?ãã    comp.os.msdos.programmer (comp.sys.ibm.pc.programmer until Septemberã    1990) concerns programming for MS-DOS systems.  The article "USENETã    Readership report for Nov 92" in news.lists shows 42,000 readers ofã    this newsgroup worldwide.  Traffic was 1090.7 Kbytes (exclusive ofã    crossposts), comprised in 611 articles.ãã    Much of our traffic is about language products (chiefly from Borlandã    and Microsoft).  More programming topics focus on C than on any oneã    other language.ãã    Since most MS-DOS systems run on hardware that is roughly compatibleã    with the IBM PC, on Intel 8088, 80188, or 80x86 chips, we tend toã    get a lot of questions and answers about programming other parts ofã    the hardware.ããQ103. What's the difference from comp.sys.ibm.pc.programmer?ãã    c.s.i.p.programmer is the old name of comp.os.msdos.programmer, andã    has been obsolete since September 1990.  However, many systems haveã    not removed the old group, or have removed it but aliased it to theã    new name.  This means that some people still think they're postingã    to c.s.i.p.programmer even though they're actually posting toã    c.o.m.programmer.ãã    You can easily verify the non-existence of c.s.i.p.programmer byã    reference to the "List of Active Newsgroups" posted to news.groups.ã    It's available as /pub/usenet/news.answers/active-newsgroups/part1ã    from the archives (see "Where are FAQ lists archived?" in section C,ã    "More information").ããQ104. What other newsgroups should I know about?ãã    Your best bet is to read the periodic information postings in theã    comp.binaries.ibm.pc newsgroup.  Specially helpful articles:ã        Using the comp.binaries.ibm.pc.d groupsã        Beginner's guide to binariesã        Starter kitã        About archives and archiversã    Please wait for these articles to come around; don't post a request.ãã    Also check out news.announce.newusers, even if you're not a newã    user.  You may be surprised how much useful information is in theã    monthly postings there.  Lots of old-timers also get useful stuffã    from news.newusers.questions, especially the periodic postings.ãã    Remember that it's good manners to subscribe to any newsgroup andã    read it for a while before you post a question.  When you post, it'sã    also good manners to ask for replies to be emailed and then to postã    a summary, which you've edited down to the absolute minimum size.ãã    You may also be interested in the following newsgroups.  Caution:ã    Some of them have specialized charters; you'll probably get (andã    deserve) some flames if you post to an inappropriate group.ãã    - misc.forsale.computers and misc.forsale.computers.pc-clone areã      where you post notices of equipment, software, or computer booksã      that you want to sell.  Please don't post or crosspost thoseã      notices to comp.os.msdos.programmer.ãã    - comp.os.ms-windows.programmer.tools and ...misc (formerly part ofã      comp.windows.ms.programmer):  Similar to this group, but focusã      on programming for the MS-Windows platform.ãã    - comp.sys.ibm.pc.hardware is for more hardware-oriented discussionsã      of the machines that run DOS.ãã    - comp.binaries.ibm.pc.wanted: AFTER you have looked in the otherã      groups, this is the place to post a request for a particularã      binary program.ãã    - comp.binaries.msdos.announce (moderated) explains how to use theã      archive sites, especially garbo and Simtel, and lists filesã      uploaded to them.  Discussions belong in comp.binaries.msdos.d,ã      which replaced comp.binaries.ibm.pc.archives.ãã    - comp.binaries.ibm.pc.d is for discussions about programs posted inã      comp.binaries.ibm.pc, and only those programs.  This is a goodã      place to report bugs in the programs, but not to ask where to findã      them (see cbip.wanted, above).  cbip.d is NOT supposed to be aã      general PC discussion group.ãã    - comp.sources.misc: a moderated group for source code for manyã      computer systems.  It tends to get lots of Unix stuff, but you mayã      also pick up some DOS-compatible code here.ãã    - alt.sources: an unmoderated group for source code.  Guidelines areã      posted periodically.ãã    - Turbo Vision is a mailing list, not a newsgroup; send email toã      listserv@vtvm1.cc.vt.edu if you want to subscribe.ãããsection 2. Compile and linkã===========================ããQ201. What the heck is "DGROUP > 64K"?ãã    DGROUP is a link-time group of data segments, and the compilerã    typically generates code that expects DS to be pointing to DGROUP.ã    (Exception: Borland's huge model has no DGROUP.)ãã    Here's what goes into DGROUP:ãã    - tiny model (all pointers near):  DGROUP holds the entire program.ãã    - small and medium models (data pointers near):  DGROUP holds allã      globals and static variables including string literals, plus theã      stack and the heap.ãã    - large, compact, and huge models in Microsoft (data pointers far):ã      DGROUP holds only initialized globals and static variablesã      including string literals, plus the stack and the near heap.ãã    - large and compact models in Borland (data pointers far): DGROUPã      holds initialized and uninitialized globals and static variablesã      including string literals, but not the stack or heap.ãã    - huge model in Borland (data pointers far): there is no DGROUP, soã      the 64K limit doesn't apply.ãã    In all of the above, which is to say all six models in Microsoft Cã    and all but huge in Borland C, DGROUP is limited to 64K includingã    string literals (which are treated as static data).  This limitationã    is due to the Intel CPU's segmented architecture.ãã    See the next Q for possible remedies.ãã    For more information, see topics like "memory models" and "memoryã    management" in the index of your compiler manual.  Also seeã    TI738.ASC in PD1:<MSDOS.TURBO-C>BCHELP10.ZIP at Simtel for anã    extended general discussion of memory usage in Borland C programs,ã    of which much applies to any C compiler in DOS.ããQ202. How do I fix "automatic data segment exceeds 64K" or "stack plusã      data exceed 64K"?ãã    These messages are a variation of "DGROUP > 64K".  For causes,ã    please see the preceding Q.ãã    If you get this error in tiny model, your program is simply too bigã    and you must use a different memory model.  If you get this linkã    error in models S, C, M, L, or Microsoft's H, there are some thingsã    you can do.  (This error can't occur in Borland's huge model.)ãã    If you have one or two big global arrays, simply declare them far.ã    The compiler takes this to mean that any references to them will useã    32-bit pointers, so they'll be in separate segments and no longerã    part of DGROUP.ãã    Or you can use the /Gt[number] option with Microsoft or -Ff[=size]ã    with Borland C++ 2.0 and up.  This will automatically put variablesã    above a certain size into their own segments outside of DGROUP.ãã    Yet another option is to change global arrays to far pointers.  Thenã    at the beginning of your program, allocate them from the far heapã    (_fmalloc in Microsoft, farmalloc in Borland).ãã    Finally, you can change to huge model (with Borland compilers, notã    Microsoft).  Borland's H model still uses far pointers by default,ã    but "sets aside the [64K] limit" and has no DGROUP group, accordingã    to the BC++ 2.0 Programmer's Guide.  Microsoft's H model does useã    huge data pointers by default but retains DGROUP and its 64K limit,ã    so switching to the H model doesn't buy you anything if you haveã    DGROUP problems.ããQ203. Will Borland C code and Microsoft C code link together?ãã    Typically this question is asked by someone who owns compiler A andã    is trying to write code to link with a third-party library that wasã    compiled under compiler B.ãã    The answer to the question is, Not in general.  Here are some of theã    reasons:ãã    - "Helper" functions (undocumented functions for stack checking,ã      floating-point arithmetic, and operations on longs) differ betweenã      the two compilers.ãã    - The compilers may embed instructions in the object code that tellã      the linker to look for their own run-time libraries.ãã    Those problems will generate link-time errors.  Others may not showã    up until run time:ãã    - Borland's compact, large, and huge models don't assume DS=SS, butã      Microsoft's do.  The -Fs option on the Borland compiler, or one ofã      the /A options on Microsoft, should take care of this problem --ã      once you know that's what's going on.ãã    - Check conventions for ordering and packing structure members, andã      for alignment of various types on byte, word, paragraph, or otherã      boundaries.  Again, you can generally adjust your code to match ifã      you know what conventions were used in compiling the "foreign"ã      libraries.ãã    - Check the obvious and make sure that your code was compiled underã      the same memory model as the code you're trying to link with.ã      (That's necessary, but no guarantee.  Microsoft and Borland don'tã      use exactly the same conventions for segments and groups,ã      particularly in the larger memory models.)ãã    That said, there are some circumstances where you can link hybrids.ã    Your best chance of success comes if you avoid longs and floatingã    point, use only 16-bit pointers, suppress stack checking, andã    specify all libraries used in the link.ããQ204. Why did my program bomb at run time with "floating point formatsã      not linked"?ãã    First, is that the actual message, or did it say "floating point notã    loaded"?  If it was the latter, see the next Q.ãã    You're probably using a Borland compiler for C or C++ (includingã    Turbo C and Turbo C++).  Borland's compilers try to be smart and notã    link in the floating-point (f-p) library unless you need it.  Alas,ã    they all get the decision wrong.  One common case is where you don'tã    call any f-p functions, but you have %f or other f-p formats inã    scanf/printf calls.  The cure is to call an f-p function, or atã    least force one to be present in the link.ãã    To do that, define this function somewhere in a source file butã    don't call it:ãã        static void forcefloat(float *p)ã            { float f = *p; forcefloat(&f); }ãã    It doesn't have to be in the module with the main program, as longã    as it's in a module that will be included in the link.ãã    A new solution for Borland C++ 3.0 was posted, but I don't own theã    product and have not been able to verify it.  Insert theseã    statements in your program:ãã        extern unsigned _floatconvert;ã        #pragma extref _floatconvertããQ205. Why did my program bomb with "floating point not loaded"?ãã    That is Microsoft C's run-time message when the code requires aã    numeric coprocessor but your computer doesn't have one installed.ãã    If the program is yours, relink it using the xLIBCE or xLIBCAã    library (where x is the memory model).ããQ206. How can I change the stack size in Borland's C compilers?ãã    In Turbo C, Turbo C++, and Borland C++, you may not find "stackã    size" in the index but the global variable _stklen should be there.ã    The manual will instruct you to put a statement likeãã        extern unsigned _stklen = 54321U;ãã    in your code, outside of any function.  You must assign the valueã    right in the extern statement; it won't work to assign a value atã    run time.  (The "extern" in this context isn't ANSI C and ought notã    to be required, but the above statement is a direct quote from theã    Library Reference manual of Borland C++ 2.0.)  The linker may giveã    you a duplicate symbol warning, which you can ignore.ããQ207. What's the format of an .OBJ file?ãã    Here's what I've been told, though I have verified any of theseã    references myself:ãã    - base .OBJ format:  Intel's document number #121748-001, {8086ã      Relocatable Object Module Formats}.  (Note however that bothã      Microsoft and Borland formats have extended the .OBJ format.)ãã    - Microsoft-specific .OBJ formats:  a "Microsoft OMF Specification"ã      (document number ??), as well as a section in the MS-DOSã      encyclopedia.ãã    - A "tutorial on the .OBJ format" comes with the VAL experimentalã      linker, which is VAL-LINK.ARC in PD1:<MSDOS.PGMUTL> at Simtel.ãã    If you have specific references, either to fpt-able documents or toã    published works (author, title, order number or ISBN), please emailã    them to brown@ncoast.org for inclusion in the next edition of thisã    list.ããQ208. What's the format of an .EXE header?ãã    See pages 349-350 of {PC Magazine}'s June 30, 1992 issue (xi:12) forã    the old and new formats.  For a more detailed layout, look under INTã    21 function 4B in Ralf Brown's interrupt list.  Ralf Brown's listã    includes extensions for Borland's TLINK and Borland debugger info.ãã    Among the books that detail formats of executable files are {DOSã    Programmer's Reference: 2d Edition} by Terry Dettman and Jim Kyle,ã    ISBN 0-88022-458-4; and {Microsoft MS-DOS Programmer's Reference},ã    ISBN 1-55615-329-5.ããQ209. What's the difference between .COM and .EXE formats?ãã    To oversimplify:  a .COM file is a direct image of core, and an .EXEã    file will undergo some further relocation when it is run (and so itã    begins with a relocation header).  A .COM file is limited to 64K forã    all segments combined, but an .EXE file can have as many segments asã    your linker will handle and be as large as RAM can take.ãã    The actual file extension doesn't matter.  DOS knows that a fileã    being loaded is in .EXE format if its first two bytes are MZ or ZM;ã    otherwise it is assumed to be in .COM format.  For instance, I amã    told that DR-DOS 6.0's COMMAND.COM is in .EXE format.ãããsection 3. Keyboardã===================ããQ301. How can I read a character without echoing it to the screen, andã      without waiting for the user to press the Enter key?ãã    The C compilers from Microsoft and Borland offer getch (or getche toã    echo the character); Turbo Pascal has ReadKey.ãã    In other programming languages, load 8 in register AH and executeã    INT 21; AL is returned with the character from standard inputã    (possibly redirected).  If you don't want to allow redirection, orã    you want to capture Ctrl-C and other special keys, use INT 16 withã    AH=10; this will return the scan code in AH and ASCII code (ifã    possible) in AL, except that AL=E0 with AH nonzero indicates one ofã    the grey "extended" keys was pressed.  (If your BIOS doesn'tã    support the extended keyboard, use INT 16 function 0 not 10.)ããQ302. How can I find out whether a character has been typed, withoutã      waiting for one?ãã    In Turbo Pascal, use KeyPressed.  Both Microsoft C and Turbo C offerã    the kbhit( ) function.  All of these tell you whether a key has beenã    pressed.  If no key has been pressed, they return that informationã    to your program.  If a keystroke is waiting, they tell your programã    that but leave the key in the input buffer.ãã    You can use the BIOS call, INT 16 function 01 or 11, to checkã    whether an actual keystroke is waiting; or the DOS call, INT 21ã    function 0B, to check for a keystroke from stdin (subject toã    redirection).  See Ralf Brown's interrupt list.ããQ303. How can I disable Ctrl-C/Ctrl-Break and/or Ctrl-Alt-Del?ãã    You can download the file PD1:<MSDOS.KEYBOARD>CADEL.ZIP from Simtel.ã    It contains a TSR to disable those keys, with source code in ASM.ãã    To disable only Ctrl-Alt-Del (actually, to change the boot keys toã    leftShift-Alt-Del), use DEBOOT.COM.  Along with KEYKILL.COM, whichã    lets you disable up to three keys of your choice, it is at Simtel inã    the file PD1:<MSDOS.KEYBOARD>KEYKILL.ARC.ãã    C programmers who simply want to make sure that the user can'tã    Ctrl-Break out of their program can use the ANSI-standard signal( )ã    function; the Borland compilers also offer ctrlbrk( ) for handlingã    Ctrl-Break.  However, if your program uses normal DOS input, theã    characters ^C will appear on the screen when the user presses Ctrl-Cã    or Ctrl-Break.  There are many ways to work around that, including:ã    use INT 21 function 7, which allows redirection but doesn't displayã    the ^C (or echo any other character, for that matter); or use INT 16ã    function 0 or 10; or call _bios_keybrd( ) in MSC or bioskey( ) inã    BC++; or hook INT 9 to discard Ctrl-C and Ctrl-Break before theã    regular BIOS keyboard handler sees them; etc., etc.ãã    You should be aware that Ctrl-C and Ctrl-Break are processed quiteã    differently internally.  Ctrl-Break, like all keystrokes, isã    processed by the BIOS code at INT 9 as soon as the user presses theã    keys, even if earlier keys are still in the keyboard buffer:  byã    default the handler at INT 1B is called.  Ctrl-C is not special toã    the BIOS, nor is it special to DOS functions 6 and 7; it _is_ã    special to DOS functions 1 and 8 when at the head of the keyboardã    buffer.  You will need to make sure BREAK is OFF to prevent DOSã    polling the keyboard for Ctrl-C during non-keyboard operations.ãã    Some good general references are {Advanced MS-DOS} by Ray Duncan,ã    ISBN 1-55615-157-8; {8088 Assembler Language Programming:  The IBMã    PC}, ISBN 0-672-22024-5, by Willen & Krantz; and {COMPUTE!'s Mappingã    the IBM PC}, ISBN 0-942386-92-2.ããQ304. How can I disable the print screen function?ãã    There are really two print screen functions:  1) print currentã    screen snapshot, triggered by PrintScreen or Shift-PrtSc orã    Shift-grey*, and 2) turn on continuous screen echo, started andã    stopped by Ctrl-P or Ctrl-PrtSc.ãã    1) Screen snapshot to printerã       --------------------------ãã    The BIOS uses INT 5 for this.  Fortunately, you don't need to messã    with that interrupt handler.  The standard handler, in BIOSes datedã    December 1982 or later, uses a byte at 0040:0100 (alias 0000:0500)ã    to determine whether a print screen is currently in progress.  If itã    is, pressing PrintScreen again is ignored.  So to disable the screenã    snapshot, all you have to do is write a 1 to that byte.  When theã    user presses PrintScreen, the BIOS will think that a print screen isã    already in progress and will ignore the user's keypress.  You canã    re-enable PrintScreen by zeroing the same byte.ãã    Here's some simple code:ãã        void prtsc_allow(int allow) /* 0=disable, nonzero=enable */ {ã            unsigned char far* flag = (unsigned char far*)0x00400100UL;ã            *flag = (unsigned char)!allow;ã        }ãã    2) Continuous echo of screen to printerã       ------------------------------------ãã    If ANSI.SYS is loaded, you can easily disable the continuous echo ofã    screen to printer (Ctrl-P or Ctrl-PrtSc).  Just redefine the keys byã    "printing" strings like these to the screen (BASIC print, C printf,ã    Pascal Write statements, or ECHO command in batch files):ãã        <27>[0;114;"Ctrl-PrtSc disabled"pã        <27>[16;"^P"pãã    Change <27> in the above to an Escape character, ASCII 27.ãã    If you haven't installed ANSI.SYS, I can't offer an easy way toã    disable the echo-screen-to-printer function.  Please send any testedã    solutions to brown@ncoast.org and I'll add them to this list.ãã    Actually, you might not need to disable Ctrl-P and Ctrl-PrtSc.  Ifã    your only concern is not locking up your machine, when you see theã    "Abort, Retry, Ignore, Fail" prompt just press Ctrl-P again and thenã    I.  As an alternative, install one of the many print spoolers thatã    intercept printer-status queries and always return "Printer ready".ããQ305. How can my program turn NumLock (CapsLock, ScrollLock) on or off?ãã    You need to twiddle bit 5, 6, or 4 of location 0040:0017.  Here'sã    some code:  lck( ) turns on a lock state, and unlck( ) turns it off.ã    (The status lights on some keyboards may not reflect the change.  Ifã    yours is one, call INT 16 function 2, "get shift status", and thatã    may update them.  It will certainly do no harm.)ãã        #define NUM_LOCK  (1 << 5)ã        #define CAPS_LOCK (1 << 6)ã        #define SCRL_LOCK (1 << 4)ã        void lck(int shiftype) {ã            char far* kbdstatus = (char far*)0x00400017UL;ã            *kbdstatus |= (char)shiftype;ã        }ã        void unlck(int shiftype) {ã            char far* kbdstatus = (char far*)0x00400017UL;ã            *kbdstatus &= ~(char)shiftype;ã        }ããQ306. How can I speed up the keyboard's auto-repeat?ãã    The keyboard speed has two components: delay (before a key that youã    hold down starts repeating) and typematic rate (the speed once theã    key starts repeating).  Most BIOSes since 1986 let software changeã    the delay and typematic rate by calling INT 16 function 3, "setã    typematic rate and delay"; see Ralf Brown's interrupt list.  If youã    have DOS 4.0 or later, you can use the MODE CON command that you'llã    find in your DOS manual.ãã    On 83-key keyboards (mostly XTs), the delay and typematic rate can'tã    easily be changed.  According to the {PC Magazine} of 15 Jan 1991,ã    page 409, to adjust the typematic rate you need "a memory-residentã    program which simply '[watches]' the keyboard to see if you'reã    holding down a key ... and after a certain time [starts] stuffingã    extra copies of the held-down key into the buffer."  No source codeã    is given in that issue; but I'm told that the QUICKEYS utility thatã    {PC} published in 1986 does this sort of watching; you can downloadã    source and object code in PD1:<MSDOS.PCMAG>VOL5N05.ARC from Simtel.ããQ307. What is the SysRq key for?ãã    There is no standard use for the key.  The BIOS keyboard routines inã    INT 16 simply ignore it; therefore so do the DOS input routines inã    INT 21 as well as the keyboard routines in libraries supplied withã    high-level languages.ãã    When you press or release a key, the keyboard triggers hardware lineã    IRQ1, and the CPU calls INT 9.  INT 9 reads the scan code from theã    keyboard and the shift states from the BIOS data area.ãã    What happens next depends on whether your PC's BIOS supports anã    enhanced keyboard (101 or 102 keys).  If so, INT 9 calls INT 15ã    function 4F to translate the scan code.  If the translated scan codeã    is 54 hex (for the SysRq key) then INT 9 calls INT 15 function 85ã    and doesn't put the keystroke into the keyboard buffer.  The defaultã    handler of that function does nothing and simply returns.  (If yourã    PC has an older BIOS that doesn't support the extended keyboards,ã    INT 15 function 4F is not called.  Early ATs have 84-key keyboards,ã    so their BIOS calls INT 15 function 85 but nor 4F.)ãã    Thus your program is free to use SysRq for its own purposes, but atã    the cost of some programming.  You could hook INT 9, but it'sã    probably easier to hook INT 15 function 85, which is called whenã    SysRq is pressed or released.ããQ308. How can my program tell what kind of keyboard is on the system?ãã    Ralf Brown's Interrupt List includes MEMORY.LST, a detailedã    breakdown by Robin Walker of the contents of the BIOS system blockã    that starts at 0040:0000.  Bit 4 of byte 0040:0096 is "1=enhancedã    keyboard installed".  C code to test the keyboard type:ã        char far *kbd_stat_byte3 = (char far *)0x00400096UL;ã        if (0x10 & *kbd_stat_byte3)ã            /* 101- or 102-key keyboard is installed */ãã    {PC Magazine}'s 15 Jan 1991 issue suggests on page 412 that "forã    some clones [the above test] is not foolproof".  If you use thisã    method in your program you should provide the user some way toã    override this test, or at least some way to tell your program toã    assume a non-enhanced keyboard.  The {PC Magazine} article suggestsã    a different approach to determining the type of keyboard.ããQ309. How can I tell if input, output, or stderr has been redirected?ãã    Normally, input and output are associated with the console (i.e.,ã    with the keyboard and the screen, respectively).  If either is not,ã    you know that it has been redirected.  Some source code to checkã    this is available at the usual archive sites.ãã    If you program in Turbo Pascal, download the /pc/ts/tspa*.zipã    collection of Turbo Pascal units from garbo; or from Simtel,ã    PD1:<MSDOS.TURBOPAS>TSPA*.ZIP.  (Choose TSPA3060.ZIP, TSPA3055.ZIP,ã    TSPA3050.ZIP, or TSPA3040.ZIP for Turbo Pascal 6.0, 5.5, 5.0, or 4.0ã    respectively.)  Source code is not included.  Also see theã    information in garbo.uwasa.fi:/pc/ts/tsfaq*.zip Frequently Askedã    Questions, the Turbo Pascal section.ãã    If you program in C, use isatty( ) if your implementation has it.ã    Otherwise, you can download PD1:<MSDOS.SYSUTL>IS_CON10.ZIP fromã    Simtel; it includes source code.ãã    Good references for the principles are {PC Magazine} 16 Apr 1991ã    (vol 10 nr 7) pg 374; Ray Duncan's {Advanced MS-DOS}, ISBNã    1-55615-157-8, or Ralf Brown's interrupt list for INT 21 functionã    4400; and Terry Dettman and Jim Kyle's {DOS Programmer's Reference:ã    2d edition}, ISBN 0-88022-458-4, pp 602-603.ãããIf the posting date is more than six weeks in the past, see instructionsãin part 4 of this list for how to get an updated copy.ãã            Copyright (C) 1992  Stan Brown, Oak Road Systemsãããsection 4.  Disks and filesã===========================ããQ401. What drive was the PC booted from?ãã    Under DOS 4.0 or later, load 3305 hex into AX; do an INT 21.  DL isã    returned with an integer indicating the boot drive (1=A:, etc.).ããQ402. How can I boot from drive b:?ãã    Download PD1:<MSDOS.DSKUTL>BOOT_B.ZIP (shareware) from Simtel.  Theã    included documentation says it works by writing a new boot sector onã    a disk in your a: drive that redirects the boot to your b: drive.ããQ403. Which real and virtual disk drives are valid?ãã    Use INT 21 function 29 (parse filename).  Point DS:SI at a null-ã    terminated ASCII string that contains the drive letter and a colon,ã    point ES:DI at a 37-byte dummy FCB buffer, set AX to 2900h, and doã    an INT 21.  On return, AL is FF if the drive is invalid, somethingã    else if the drive is valid.  RAM disks and SUBSTed drives areã    considered valid.ãã    Unfortunately, the b: drive is considered valid even on a single-ã    diskette system.  You can check that special case by interrogatingã    the BIOS equipment byte at 0040:0010.  Bits 7-6 contain the one lessã    than the number of diskette drives, so if those bits are zero youã    know that b: is an invalid drive even though function 29 says it'sã    valid.ãã    Following is some code originally posted by Doug Dougherty, with myã    fix for the b: special case, tested only in Borland C++ 2.0 (inã    the small model):ãã        #include <dos.h>ã        void drvlist(void)  {ã            char *s = "A:", fcb_buff[37];ã            int valid;ã            for (   ;  *s<='Z';  (*s)++) {ã                _SI = (unsigned) s;ã                _DI = (unsigned) fcb_buff;ã                _ES = _DS;ã                _AX = 0x2900;ã                geninterrupt(0x21);ã                valid = _AL != 0xFF;ã                if (*s == 'B'  &&  valid) {ã                    char far *equipbyte = (char far *)0x00400010UL;ã                    valid = (*equipbyte & (3 << 6)) != 0;ã                }ã                printf("Drive '%s' is %sa valid drive.\n",ã                        s, valid ? "" : "not ");ã            }ã        }ããQ404. How can I make my single floppy drive both a: and b:?ãã    Under any DOS since DOS 2.0, you can put the commandãã        assign b=aãã    into your AUTOEXEC.BAT file.  Then, when you type "DIR B:" you'll noã    longer get the annoying prompt to insert diskette B (and the evenã    more annoying prompt to insert A the next time you type "DIR A:").ãã    You may be wondering why anybody would want to do this.  Suppose youã    use two different machines, maybe one at home and one at work.  Oneã    of them has only a 3.5" diskette drive; the other machine has twoã    drives, and b: is the 3.5" one.  You're bound to type "dir b:" onã    the first one, and get the nuisance messageãã        Insert diskette for drive B: and press any key when ready.ãã    But if you assign drive b: to point to a:, you avoid this problem.ãã    Caution:  there are a few commands, such as DISKCOPY, that will notã    work right on ASSIGNed or SUBSTed drives.  See the DOS manual forã    the full list.  Before typing one of those commands, be sure to turnã    off the mapping by typing "assign" without arguments.ãã    The DOS 5.0 manual says that ASSIGN is obsolete, and recommends theã    equivalent form of SUBST: "subst b: a:\".  Unfortunately, if thisã    command is executed when a: doesn't hold a diskette, the commandã    fails.  ASSIGN doesn't have this problem, so I must advise you toã    disregard that particular bit of advice in the DOS manual.ããQ405. Why won't my C program open a file with a path?ãã    You've probably got something like the following code:ãã        char *filename = "c:\foo\bar\mumble.dat";ã        . . .  fopen(filename, "r");ãã    The problem is that \f is a form feed, \b is a backspace, and \m isã    m.  Whenever you want a backslash in a string constant in C, youã    must use two backslashes:ãã        char *filename = "c:\\foo\\bar\\mumble.dat";ãã    This is a feature of every C compiler, because Dennis Ritchieã    designed C this way.  It's a problem only on MS-DOS systems, becauseã    only DOS (and Atari ST/TT running TOS, I'm told) uses the backslashã    in directory paths.  But even in DOS this backslash conventionã    applies _only_ to string constants in your source code.  For fileã    and keyboard input at run time, \ is just a normal character, soã    users of your program would type in file specs at run time the sameã    way as in DOS commands, with single backslashes.ãã    Another possibility is to code all paths in source programs with /ã    rather than \ characters:ãã        char *filename = "c:/foo/bar/mumble.dat";ãã    Ralf Brown writes that "All versions of the DOS kernel accept eitherã    forward or backslashes as directory separators.  I tend to use thisã    form more frequently than backslashes since it is easier to type andã    read."  This applies to DOS function calls (and therefore to callsã    to the file library of every programming language), but not to DOSã    commands.ããQ406. How can I redirect printer output to a file?ãã    My personal favorite utility for this purpose is PRN2FILE from {PCã    Magazine}, available from Simtel as PD1:<MSDOS.PRINTER>PRN2FILE.ARC,ã    or from garbo as prn2file.zip in /pc/printer.  ({PC Magazine} hasã    given copies away as part of its utilities disks, so you may alreadyã    have a copy.)ãã    Check the PD1:<MSDOS.PRINTER> directory at Simtel, or /pc/printerã    at garbo, for lots of other printer-redirection utilities.ããQ407. How can my program open more files than DOS's limit of 20?ãã    (This is a summary of an article Ralf Brown posted on 8 August 1992.)ãã    There are separate limits on files and file handles.  For example,ã    DOS opens three files but five file handles:  CON (stdin, stdout,ã    and stderr), AUX (stdaux), and PRN (stdprn).ãã    The limit in FILES= in CONFIG.SYS is a system-wide limit on filesã    opened by all programs (including the three that DOS opens and anyã    opened by TSRs); each process has a limit of 20 handles (includingã    the five that DOS opens).  Example:  CONFIG.SYS has FILES=40.  Thenã    program #1 will be able to open 15 file handles.  Assuming that theã    program actually does open 15 handles pointing to 15 differentã    files, other programs could still open a total of 22 files (40-3-15ã    = 22), though no one program could open more than 15 file handles.ãã    If you're running DOS 3.3 or later, you can increase the per-processã    limit of 20 file handles by a call to INT 21 function 67, Set Handleã    Count.  Your program is still limited by the system-wide limit onã    open files, so you may also need to increase the FILES= value inã    your CONFIG.SYS file (and reboot).  The run-time library that you'reã    using may have a fixed-size table of file handles, so you may alsoã    need to get source code for the module that contains the table,ã    increase the table size, and recompile it.ããQ408. How can I read, create, change, or delete the volume label?ãã    In DOS 5.0 (and, I believe, in 4.0 as well), there are actually twoã    volume labels: one, the traditional one, is an entry in the rootã    directory of the disk; and the other is in the boot record alongã    with the serial number (see next Q).  The DIR and VOL commandsã    report the traditional label; the LABEL command reports theã    traditional one but changes both of them.ãã    In DOS 4.0 and later, use INT 21 function 69 to access the bootã    record's serial number and volume label together; see the next Q.ãã    Assume that by "volume label" you mean the traditional one, the oneã    that DIR and VOL display.  Though it's a directory entry in the rootã    directory, you can't change it using the newer DOS file-accessã    functions (3C, 41, 43); instead, use the old FCB-oriented directoryã    functions.  Specifically, you need to allocate a 64-byte buffer andã    a 41- byte extended FCB (file control block).  Call INT 21 AH=1A toã    find out whether there is a volume label.  If there is, AL returns 0ã    and you can change the label using DOS function 17 or delete itã    using DOS function 13.  If there's no volume label, function 1A willã    return FF and you can create a label via function 16.  Importantã    points to notice are that ? wildcards are allowed but * are not; theã    volume label must be space filled not null terminated.ãã    The following MSC 7.0 code worked for me in DOS 5.0; the functionsã    it uses have been around since DOS 2.0.  The function parameter is 0ã    for the current disk, 1 for a:, 2 for b:, etc.  It doesn't matterã    what your current directory is; these functions always search theã    root directory for volume labels.  (I didn't try to change theã    volume label of any networked drives.)ãã    // Requires DOS.H, STDIO.H, STRING.Hã    void vollabel(unsigned char drivenum) {ã        static unsigned char extfcb[41], dta[64], status, *newlabel;ã        int chars_got = 0;ã        #define DOS(buff,func) __asm { __asm mov dx,offset buff \ã            __asm mov ax,seg buff  __asm push ds  __asm mov ds,ax \ã            __asm mov ah,func  __asm int 21h  __asm pop ds \ã            __asm mov status,al }ã        #define getlabel(buff,prompt) newlabel = buff;  \ã            memset(newlabel,' ',11);  printf(prompt);   \ã            scanf("%11[^\n]%n", newlabel, &chars_got);  \ã            if (chars_got < 11) newlabel[chars_got] = ' ';ãã        // Set up the 64-byte transfer area used by function 1A.ã        DOS(dta, 1Ah)ã        // Set up an extended FCB and search for the volume label.ã        memset(extfcb, 0, sizeof extfcb);ã        extfcb[0] = 0xFF;             // denotes extended FCBã        extfcb[6] = 8;                // volume-label attribute bitã        extfcb[7] = drivenum;         // 1=A, 2=B, etc.; 0=current driveã        memset(&extfcb[8], '?', 11);  // wildcard *.*ã        DOS(extfcb,11h)ã        if (status == 0) {            // DTA contains volume label's FCBã            printf("volume label is %11.11s\n", &dta[8]);ã            getlabel(&dta[0x18], "new label (\"delete\" to delete): ");ã            if (chars_got == 0)ã                printf("label not changed\n");ã            else if (strncmp(newlabel,"delete     ",11) == 0) {ã                DOS(dta,13h)ã                printf(status ? "label failed\n" : "label deleted\n");ã            }ã            else {                    // user wants to change labelã                DOS(dta,17h)ã                printf(status ? "label failed\n" : "label changed\n");ã            }ã        }ã        else {                        // no volume label was foundã            printf("disk has no volume label.\n");ã            getlabel(&extfcb[8], "new label (<Enter> for none): ");ã            if (chars_got > 0) {ã                DOS(extfcb,16h)ã                printf(status ? "label failed\n" : "label created\n");ã            }ã        }ã    }   // end function vollabelããQ409. How can I get the disk serial number?ãã    Use INT 21.  AX=6900 gets the serial number; AX=6901 sets it.  Seeã    Ralf Brown's interrupt list, or page 496 of the July 1992 {PCã    Magazine}, for details.ãã    This function also gets and sets the volume label, but it's theã    volume label in the boot record, not the volume label that a DIRã    command displays.  See the preceding Q.ããQ410. What's the format of .OBJ, .EXE., .COM files?ãã    Please see section 2, "Compile and link".ãããsection 5. Serial ports (COM ports)ã===================================ããQ501. How do I set my machine up to use COM3 and COM4?ãã    Unless your machine is fairly old, it's probably already set up.ã    After installing the board that contains the extra COM port(s),ã    check the I/O addresses in word 0040:0004 or 0040:0006.  (In DEBUG,ã    type "D 40:4 L4" and remember that every word is displayed lowã    byte first, so if you see "03 56" the word is 5603.)  If thoseã    addresses are nonzero, your PC is ready to use the ports and youã    don't need the rest of this answer.ãã    If the I/O address words in the 0040 segment are zero after you'veã    installed the I/O board, you need some code to store these valuesã    into the BIOS data segment:ãã        0040:0004  word  I/O address of COM3ã        0040:0006  word  I/O address of COM4ã        0040:0011  byte (bits 3-1): number of serial ports installedãã    The documentation with your I/O board should tell you the portã    addresses.  When you know the proper port addresses, you can addã    code to your program to store them and the number of serial portsã    into the BIOS data area before you open communications.  Or you canã    use DEBUG to create a little program to include in your AUTOEXEC.BATã    file, using this script:ãã            n SET_ADDR.COM      <--- or a different name ending in .COMã            a 100ã            mov  AX,0040ã            mov  DS,AXã            mov  wo [0004],aaaa <--- replace aaaa with COM3 address or 0ã            mov  wo [0006],ffff <--- replace ffff with COM4 address or 0ã            and  by [0011],f1ã            or   by [0011],8    <--- use number of serial ports times 2ã            mov  AH,0ã            int  21ã                                <--- this line must be blankã            rCXã            1fã            rBXã            0ã            wã            qããQ502. How do I find the I/O address of a COM port?ãã    Look in the four words beginning at 0040:0000 for COM1 through COM4.ã    (The DEBUG command "D 40:0 L8" will do this.  Remember that wordsã    are stored and displayed low byte first, so a word value of 03F8ã    will be displayed as F8 03.)  If the value is zero, that COM port isã    not installed (or you've got an old BIOS; see the preceding Q).  Ifã    the value is nonzero, it is the I/O address of the transmit/receiveã    register for the COM port.  Each COM port occupies eight consecutiveã    I/O addresses (though only seven are used by many chips).ãã    Here's some C code to find the I/O address:ãã        unsigned ptSel(unsigned comport) {ã            unsigned io_addr;ã            if (comport >= 1  &&  comport <= 4) {ã                unsigned far *com_addr = (unsigned far *)0x00400000UL;ã                io_addr = com_addr[comport-1];ã            }ã            elseã                io_addr = 0;ã            return io_addr;ã        }ããQ503. But aren't the COM ports always at I/O addresses 3F8, 2F8, 3E8,ã      and 2E8?ãã    The first two are usually right (though not always); the last twoã    are different on many machines.ããQ504. How do I configure a COM port and use it to transmit data?ãã    After hearing several recommendations, I looked at Joe Campbell's {Cã    Programmer's Guide to Serial Communications}, ISBN 0-672-22584-0,ã    and agree that it is excellent.  He gives complete details on howã    serial ports work, along with complete programs for doing polled orã    interrupt-driver I/O.  The book is quite thick, and none of it looksã    like filler.ãã    If Campbell's book is overkill for you, you'll find a good shortã    description of serial I/O in {DOS 5: A Developer's Guide}, ISBNã    1-55851-177-6, by Al Williams.ãã    You may also want to look at an extended example in Borland'sã    TechFax TI445, part of PD1:<MSDOS.TURBO-C> at Simtel.  Thoughã    written by Borland, much of it is applicable to other forms of C,ã    and it should give you ideas for other programming languages.ããsection 6. Other hardware questions and problemsã================================================ããQ601. Which 80x86 CPU is running my program?ãã    According to an article posted by Michael Davidson, Intel's approvedã    code for distinguishing among 8086, 80286, 80386, and 80486 and forã    detecting the presence of an 80287 or 80387 is published in theã    Intel's 486SX processor manual (order number 240950-001).  You canã    download David Kirschbaum's improved version of this from Simtel asã    PD1:<MSDOS.SYSUTL>CPUID593.ZIP.ãã    According to an article posted by its author, WCPU041.ZIP knows theã    differences between DX and SX varieties of 386 and 486 chips, andã    can also detect a math coprocessor.  It's in PD1:<MSDOS.SYSUTL> atã    Simtel.ããQ602. How can a C program send control codes to my printer?ãã    If you just fprintf(stdprn, ...), C will translate some of yourã    control codes.  The way around this is to reopen the printer inã    binary mode:ãã        prn = fopen("PRN", "wb");ãã    You must use a different file handle because stdprn isn't an lvalue.ã    By the way, PRN or LPT1 must not be followed by a colon in DOS 5.0.ãã    There's one special case, Ctrl-Z (ASCII 26), the DOS end-of-fileã    character.  If you try to send an ASCII 26 to your printer, DOSã    simply ignores it.  To get around this, you need to reset theã    printer from "cooked" to "raw" mode.  Microsoft C users must use intã    21 function 44, "get/set device information".  Turbo C and Borlandã    C++ users can use ioctl to accomplish the same thing:ãã        ioctl(fileno(prn), 1, ioctl(fileno(prn),0) & 0xFF | 0x20, 0);ãã    An alternative approach is simply to write the printer output into aã    disk file, then copy the file to the printer with the /B switch.ãã    A third approach is to bypass DOS functions entirely and use theã    BIOS printer functions at INT 17.  If you also fprintf(stdprn,...)ã    in the same program, you'll need to use fflush( ) to synchronizeã    fprintf( )'s buffered output with the BIOS's unbuffered.ãã    By the way, if you've opened the printer in binary mode from a Cã    program, remember that outgoing \n won't be translated to carriageã    return/line feed.  Depending on your printer, you may need to sendã    explicit \n\r sequences.ããQ603. How can I redirect printer output to a file?ãã    Please see section 4, "Disks and files", for the answer.ããQ604. Which video adapter is installed?ãã    The technique below should work if your BIOS is not too old.  Itã    uses three functions from INT 10, the BIOS video interrupt.  (Ifã    you're using a Borland language, you may not have to do this theã    hard way.  Look for a function called DetectGraph or somethingã    similar.)ãã    Set AH=12h, AL=0, BL=32h; INT 10h.  If AL is 12h, you have a VGA.ã    If not, set AH=12h, BL=10h; INT 10h.  If BL is 0,1,2,3, you have anã    EGA with 64,128,192,256K memory.  If not, set AH=0Fh; INT 10h.  Ifã    AL is 7, you have an MDA (original monochrome adapter) or Hercules;ã    if not, you have a CGA.ãã    I've tested this for my VGA and got the right answer; but I can'tã    test it for the other equipment types.  Please let me know by emailã    at brown@ncoast.org if your results vary.ããQ605. How do I switch to 43- or 50-line mode?ãã    Download PD1:<MSDOS.SCREEN>VIDMODE.ZIP from Simtel or one of theã    mirror sites.  It contains .COM utilities and .ASM source code.ããQ606. How can I find the Microsoft mouse position and button status?ãã    Use INT 33 function 3, described in Ralf Brown's interrupt list.ãã    The Windows manual says that the Logitech mouse is compatible withã    the Microsoft one, so I assume the interrupt will work the same.ãã    Also, see the directory PD1:<MSDOS.MOUSE> at Simtel.ããQ607. How can I access a specific address in the PC's memory?ãã    First check the library that came with your compiler.  Many vendorsã    have some variant of peek and poke functions; in Turbo Pascal useã    the pseudo-arrays Mem, MemW, and MemL.  As an alternative, you canã    construct a far pointer:  use Ptr in Turbo Pascal, MK_FP in theã    Turbo C family, and FP_OFF and FP_SEG in Microsoft C.ãã    Caution:  Turbo C and Turbo C++ also have FP_OFF and FP_SEG macros,ã    but they can't be used to construct a pointer.  In Borland C++ thoseã    macros work the same as in Microsoft C, but MK_FP is easier to use.ãã    By the way, it's not useful to talk about "portable" ways to doã    this.  Any operation that is tied to a specific memory address isã    not likely to work on another kind of machine.ããQ608. How can I read or write my PC's CMOS memory?ãã    There are a great many public-domain utilities that do this.  Theseã    were available for download from Simtel as of 31 March 1992:ãã    PD1:<MSDOS.AT>ã    CMOS14.ZIP     5965  920817  Saves/restores CMOS to/from fileã    CMOSER11.ZIP  28323  910721  386/286 enhanced CMOS setup programã    CMOSRAM.ZIP   76096  920214  Save AT/386/486 CMOS data to file and restoreã    ROM2.ARC      20497  900131  Save AT and 386 CMOS data to file and restoreã    SETUP21.ARC   24888  880613  Setup program which modifies CMOS RAMã    VIEWCMOS.ARC  15374  900225  Display contents of AT CMOS RAM, w/C sourceãã    At garbo, /pc/ts/tsutle17.zip contains a CMOS program to check andã    display CMOS memory, but not to write to it.ãã    I have heard good reports of CMOS299.ZIP, available in the pc.dirã    directory of cantva.canterbury.ac.nz [132.181.30.3].ãã    Of the above, my only experience is with CMOSRAM, which seems toã    work fine.  It contains an excellent (and witty) .DOC file thatã    explains the hardware involved and gives specific recommendationsã    for preventing disaster or recovering from it.  It's $5 shareware.ãã    Robert Jourdain's {Programmer's Problem Solver for the IBM PC, XT,ã    and AT} has code for accessing the CMOS RAM, according to an articleã    posted in this newsgroup.ããQ609. How can I access memory beyond 640K?ãã    I'm outside my expertise on this one, but in late 1992 Jamshidã    Afshar (jamshid@emx.utexas.edu) kindly supplied the following, whichã    incorporates some corrections agreed with Duncan Murdoch (dmurdoch@ã    mast.queensu.ca).  If you have any corrections or comments, pleaseã    send them to both the above addresses.ãã    ...........................(begin quote)............................ã    1. Use XMS or EMS memory.  XMS is preferable in most cases, butã    some machines won't provide it.  There are some libraries availableã    at Simtel to access XMS or EMS.  The disadvantage is that youã    don't allocate the memory as you would with malloc() (or `new' inã    C++).  I believe it also requires that you lock this memory when inã    use.  This means your code is not easily ported to other (andã    future) operating systems and that your code is more convoluted thanã    it would be under a "real" os.  The advantage is that the libraryã    works with compilers since Turbo C 2.0 (I think) and that yourã    program will easily run on even 286s.ãã    2.  Program under MS Windows.  MS Windows functions as a 16-bit DOSã    Extender (see #3).  Borland/Turbo C++ 3.x includes EasyWin [andã    Microsoft C/C++ 7.0 has QuickWin --ed.] which is a library thatã    automatically lets you compile your current code using C/C++ã    standard input or <conio.h> into a MS Windows program so your codeã    can immediately allocate many MBs of memory (Windows enhanced modeã    even does virtual memory).  The disadvantage is that like any 16-bitã    Extender a single malloc() is restricted to 64K (unless you want toã    mess with huge pointers in Windows).  Also, EasyWin's screen outputã    is significantly slower than a DOS character-mode program's and youã    must of course run the program from Windows.ãã    3.  Use a 16-bit or 32-bit DOS Extender.  This is definitely theã    best solution from the programmer's standpoint.  You just allocateã    as much memory as you need using malloc() or 'new'.  A 16-bitã    Extender still has 16-bit ints and restricts arrays to 64K, but aã    32-bit Extender has 32-bits ints (which makes porting a lot of UNIXã    code easier) so there are no 64K limits.  A 32-bit Extender requiresã    a 32-bit compiler and the program will not run on 286s.  Someã    Extenders also do virtual memory.  Using an Extender doesn't requireã    source code changes and unlike option #1 your code is portable andã    not obsolete in a few months.  Your options for this solution are:ãã    - Buy PharLap's 16-bit Extender that works with BC++ 3.0+ and MSCã      (just requires a relink).  Note, the BC++ 3.1 upgrade came withã      PharLap "lite".  Pharlap's 32-bit Extender works with 32-bitã      compilers like [?]ãã    - Get the GNU (free,copylefted) gcc 2.x compiler which DJ Delorieã      ported from UNIX and which uses his 32-bit Extender.  It supportsã      C and C++, but the Extender is VCPI which means neither theã      compiler nor programs it produces will run in a DOS session underã      Windows.  FTP to barnacle.erc.clarkson.edu and getã      pub/msdos/djgpp/readme.ãã    - Get a 32-bit compiler or one that comes with a DOS Extender.ã      Zortech comes with 16-bit and a 32-bit Extenders (no debugger forã      32-bit programs, but Flashtek sells one).  Watcom also makes a Cã      [and C++?] 32-bit compiler.  [If anyone else has products or plansã      to announce, please let me know.]ãã    - Buy Borland Pascal 7.0.  It includes a 16 bit royalty-free DOSã      extender using the same interface as MS Windows.  It functionsã      under a DPMI server like Windows or QDPMI from Quarterdeck, andã      also provides its own server which you can distribute with yourã      programs.ãã    4.  This option doesn't really count since it's not a solution inã    DOS, but you could switch to a full 32-bit operating system likeã    OS/2 2.0 or UNIX (or NT when it comes out).  I believe Win32 willã    allow you to write 32-bit Windows programs.  [can someone fill me inã    on what exactly Win32 is?]ã    ............................(end quote).............................ãããsection 7. Other software questions and problemsã================================================ããQ701. How can a program reboot my PC?ãã    You can generate a "cold" boot or a "warm" boot.  A cold boot isã    the same as turning the power off and on; a warm boot is the same asã    Ctrl-Alt-Del and skips the power-on self test.ãã    For a warm boot, store the hex value 1234 in the word at 0040:0072.ã    For a cold boot, store 0 in that word.  Then, if you want to liveã    dangerously, jump to address FFFF:0000.  Here's C code to do it:ãã        /* WARNING:  data loss possible */ã        void bootme(int want_warm)  /* arg 0 = cold boot, 1 = warm */ {ã            void (far* boot)(void) = (void (far*)(void))0xFFFF0000UL;ã            unsigned far* type = (unsigned far*)0x00400072UL;ã            *type = (want_warm ? 0x1234 : 0);ã            (*boot)( );ã        }ãã    What's wrong with that method?  It will boot right away, withoutã    closing files, flushing disk caches, etc.  If you boot withoutã    flushing a write-behind disk cache (if one is running), you couldã    lose data or even trash your hard drive.ãã    There are two methods of signaling the cache to flush its buffers:ã    (1) simulate a keyboard Ctrl-Alt-Del in the keystroke translationã    function of the BIOS (INT 15 function 4F), and (2) issue a diskã    reset (DOS function 0D).  Most disk-cache programs hook one or bothã    of those interrupts, so if you use both methods you'll probably beã    safe.ãã    When user code simulates a Ctrl-Alt-Del, one or more of the programsã    that have hooked INT 15 function 4F can ask that the key be ignored byã    clearing the carry flag.  For example, HyperDisk does this when itã    has started but not finished a cache flush.  So if the carry flagã    comes back cleared, the boot code has to wait a couple of cluckã    ticks and then try again.  (None of this matters on older machinesã    whose BIOS can't support 101- or 102-key keyboards; see "What is theã    SysRq key for?" in section 3, "Keyboard".)ãã    Here's C code that tries to signal the disk cache (if any) to flush:ãã        #include <dos.h>ã        void bootme(int want_warm)  /* arg 0 = cold boot, 1 = warm */ {ã            union REGS reg;ã            void    (far* boot)(void) = (void (far*)(void))0xFFFF0000UL;ã            unsigned far* boottype    =     (unsigned far*)0x00400072UL;ã            char     far* shiftstate  =         (char far*)0x00400017UL;ã            unsigned      ticks;ã            int           time_to_waste;ã            /* Simulate reception of Ctrl-Alt-Del: */ã            for (;;) {ã                *shiftstate |= 0x0C;    /* turn on Ctrl & Alt */ã                reg.x.ax = 0x4F53;      /* 0x53 = Del's scan code */ã                reg.x.cflag = 1;        /* sentinel for ignoring key */ã                int86(0x15, &reg, &reg);ã                /* If carry flag is still set, we've finished. */ã                if (reg.x.cflag)ã                    break;ã                /* Else waste some time before trying again: */ã                reg.h.ah = 0;ã                int86(0x1A, &reg, &reg);/* system time into CX:DX */ã                ticks = reg.x.dx;ã                for (time_to_waste = 3;  time_to_waste > 0;  ) {ã                    reg.h.ah = 0;ã                    int86(0x1A, &reg, &reg);ã                    if (ticks != reg.x.dx)ã                        ticks = reg.x.dx , --time_to_waste;ã                }ã            }ã            /* Issue a DOS disk reset request: */ã            reg.h.ah = 0x0D;ã            int86(0x21, &reg, &reg);ã            /* Set boot type and boot: */ã            *boottype = (want_warm ? 0x1234 : 0);ã            (*boot)( );ã        }ããQ702. How can I time events with finer resolution than the systemã      clock's 55 ms (about 18 ticks a second)?ãã    The following files, among others, can be downloaded from Simtel:ãã    PD1:<MSDOS.AT>ã    ATIM.ARC       5946  881126  Precision program timing for ATãã    PD1:<MSDOS.C>ã    MILLISEC.ZIP  37734  911205  MSC/asm src for millisecond res timingã    MSCHRT3.ZIP   53708  910605  High-res timer toolbox for MSC 5.1ã    MSEC_12.ZIP    8484  920320  High-def millisec timer v1.2 (C,ASM)ã    ZTIMER11.ZIP  77625  920428  Microsecond timer for C, C++, ASMãã    PD1:<MSDOS.TURBO-C>ã    TCHRT3.ZIP    53436  910606  High-res timer toolbox for Turbo C 2.0ã    TCTIMER.ARC   20087  891030  High-res timing of events for Turbo Cãã    PD1:<MSDOS.TURBOPAS>ã    BONUS507.ARC 150435  900205  [Turbo Pascal source: high-res timing]ãã    Pascal users can download source code in /pc/turbopas/bonus507.zipã    at garbo.ããQ703. How can I find the error level of the previous program?ãã    First, which previou                                                              3      05-28-9313:45ALL                      SWAG SUPPORT TEAM        Strings, Compiler, OutputIMPORT              28          PASCAL.FAQ             Frequently asked questions about PascalããThe aim of this document is to give answers to frequently askedãquestions in the pascal echo. Thumb rules before asking for help are toãlook in the manuals and in the online help first. Many problems can beãsolved by just looking into either / both of them. Here are someãtopics, that come very often in the Pascal Echo.ãã                                 Part Iã       #1: Changing the case of stringsã       #2: Compiler errorsã       #3: Redirection of outputãã---------------------------------------------------------------------ã                               #1 StringsããQ1: How do I access a single char in a string ?ãQ2: How can I make a string all upper cases ?ããA1: A string is an Array[0..255] of Char, where the 0th char is theã    length of the string. To access any character in the string, you canã    writeãã    MyChar := String[ I ];ããA2: To map a single character to uppercase, you can use the UpCase()ã    function of the run time library. To turn a whole string into upperã    cases, just use this function :ããFunction UpperCase( const S : String ) : String;ãVar I : Integer;ãBeginã  { first store the length in the result }ã  UpperCase[ 0 ] := S[ 0 ]ã  { now translate each char in S into a upper case char in UpperCase }ã  For I := 1 to Length( S ) doã    UpperCase[ I ] := UpCase( S[ I ]);ãEnd;ããThere is a assembler implementation in the manuals ( the linkingãassembly language chaprt ), and there are many other optimized upperãcase routines out there.ãã---------------------------------------------------------------------ã                           #2 Compiler ErrorsããQ1: I get a "Code segment too large" error. How can I fix it ?ãQ2: I get a "Data segment too large" error. How can I fix it ?ããA1: This error means, that you have more than 64K code in one partã    of your program. To reduce the size of this code segment, you needã    to move parts of it into other units. This is possible to virtuallyã    unlimited units.ããA2: This error means, that you have more than 64K data in your program.ã    You need to put some data on the heap ( -> GetMem / FreeMem / Newã    / Dispose ) or to reduce your global data and make it local data onã    the stack.ãã---------------------------------------------------------------------ã                        #3 Redirection of outputããQ1: How can I make the output of my program redirectable under DOS ?ããA1: In general, the output of TP programs _is_ redirectable, except ifã    you use the CRT unit. Then you need to either reassign output toã    '' or to declare a Text variable called for example ReOutput ( forã    Redirectable Output ), and write the output to it.ããExample :ããUses CRT;ããBeginã  WriteLn( 'This will always show up. Just a copyright.' );ã  Assign( Output, '' );ã  Rewrite( Output );ã  WriteLn( 'This is redirectable.' );ã  AssignCRT( Output );ã  Rewrite( Output );ã  WriteLn( 'And this will alyways show up again.' );ãEnd.ããThere are some myths that setting DirectVideo to False would result inãredirectable output even when using CRT, or that TP _always_ writesãdirectly to the screen, and that TP output is _never_ redirectable. Youãcan ignore thos myths, TP writes to the screen using DOS, _except_ ifãyou use the unit CRT. Then TP writes directly to the screen. If you setãthe variable DirectVideo to False, TP uses BIOS calls to write to theãscreen.ã                                              4      06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND - LINKER QA      IMPORT              7           ãTP 5.0 5.5 - LINKER ELIMINATES UNUSED DATAãQ. Does the built-in linker eliminate unused data?ãA. Yes. Unused code AND data are stripped when you compile toã   disk.  However, if more than one variable is defined in theã   same VAR block and any one is used, the others will not beã   stripped from the .EXE.  For example:ãã     var  A, B: integer;ã     var  C: integer;ã     beginã       A:= 0;ã     end.ãã  In this example, although variable B is never used, it wasã  defined in the same block as a variable A. Therefore, B willã  not be linked out.  Variable C will be removed from the .EXE asã  it is not used and is not in the same VAR block.ãã                                                                                                                       5      06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND - MAC Pascal QA  IMPORT              9           ãTP / TPMAC - PC / MACINTOSH PORTABILITYãQ. How portable is the PC version of Turbo Pascal to the ã   Macintosh?ãA. Any Portion of the program that is Standard Pascal will port ã   over nicely.  However, the PC and the Macintosh are twoã   completely different machines and any PC or Macintoshã   specific code will have to be rewritten.  PC programmers willã   generally want to rewrite the user interface so that isã   similar to what Macintosh users expect to see on a seriousã   Macintosh application.ãããTPMAC 1.0 1.1 - PASPRINTER PRINT WITHIN PROGRAMãQ. How do I print from within a program?ãA. Uses PasPrinter and Writeln(Printer, data);ããTPMAC 1.0 1.1 READLN WRITELN - THE MISSING SUPPORT UNIT ERRORãQ. I get a runtime error message saying that the support unitã   is missing when I try to use a Readln or Writeln.ãA. Take out the {$U-} directive.  It causes the PasConsole andã   PasInOut units to not link in with the program and units.ãã                                                                             6      06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND - MEMORY QA      IMPORT              16          ããTITLE: TURBO PASCAL MEMORY ISSUESã===========================================================ããTP 4.0 5.0 5.5 EXEC - SETTING HEAP MEMORYãQ. How do I set the heap memory when executing a child process?ãA. Probably the best way to do this is through trial and error. ã   Set the heap maximum to some large value, and run the program.ã   If it runs out of memory, then divide the maximum by 2, and soã   on, until the parent and child programs have enough memory. ã   The same can be done with the heap minimum.  Also, if you haveã   access to CompuServe, you can download a routine that swapsã   the parent program between memory and disk.ããTP 4.0 5.0 5.5 - ALLOCATING AND RECLAIMING MEMORY USING DOS ãQ. What Dos functions can I use to reclaim memory to Dos? ãA. You essentially must write your own GetMem and FreeMemã   routines.  GetMem would make a call to Dos function $48 andã   FreeMem would make a call to Dos function $49.  For moreã   details on these functions please consult an IBM PCã   programmer's guide.ããTP 4.0 5.0 5.5 - MEMORY CONTROL BLOCKSãQ. How many memory control blocks does a Turbo Pascal programã   use?ãA. Just one.ãããTP 5.0 5.5 - EMS SUPPORTãQ. Does Turbo Pascal 5.0+ support EMS?ãA. Yes. Turbo Pascal 5.0+ will use up to 64K (if selected inã   TINST) of EMS for storing the edit buffer. In addition, youã   can instruct the Overlay unit to place your overlaid units onã   EMS. Finally, EMS.PAS on the distribution disk shows you howã   to access EMS memory.ãããTP 5.5 - HEAP SPACE STILL CREATED WITH HEAP MAX 0ãQ. Compiling to disk with my heap maximum set to 0 still createsã   heap space when running .EXE file.  Why?ãA. This has to do with the way DOS allocates memory for .EXEã   files in chunks of 512 bytes, rather than in chunks of 16ã   bytes. As a result of DOS's allocation, you always get 0-496ã   bytes more than you request.ãã                                                     7      06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND Mixed Lang QA    IMPORT              17          TP 4.0 5.0 5.5 - LINKING TURBO C OR ASSEMBLER .OBJ OBJECT FILESãQ. Are the .OBJ files generated by Turbo C and Turboã   Assembler compatible with 4.0+?ãA. Yes.  You can write Turbo C or Turbo Assembler routines andã   link the .OBJ files into your Turbo Pascal programs by usingã   the {$L} compiler directive.  See the CTOPAS example on theã   distribution diskette.ããTP 4.0 5.0 5.5 -  OBJECT FILE CREATIONãQ. Does Turbo Pascal create object files that can be linked intoã   other languages?ãA. Turbo Pascal 4.0+ generates .TPU (Turbo Pascal Unit) files, notã   .OBJ files. We've made that decision for many reasons: ã   ã      1. TP 4.0+'s .TPU files are smaller than .OBJ's, and theyã         contain symbolic information important to the support ofã         Pascal's strict type conventions (types, constants,ã         etc.).     ã      2. .TPU files allow "smart linking" - elimination of unusedã         code and data on a procedure-by-procedure basis. ã      3. .TPU's allow built-in project management through versionã         4.0+'s Make and Build commands.          ã      4. .TPU's allow faster compilation speeds (34,000 lines perã         minute on a PS/2 Model 60).ããTP 4.0 5.0 5.5 - LINKING .OBJ OBJECT FILES FROM OTHER ASSEMBLERSãQ. Will the $L compiler directive work for compiler object filesã   other than assembler?ãA. That depends on the language. TURBO requires all the codeã   in the .OBJ to be in *one* CODE segment, and all the data toã   be in *one* DATA segment. With assembly language that's easy,ã   but it may not work with some high-level language compilers.ã   You can use Turbo C to generate .OBJ files for use by Turboã   Pascal programs. An example, CPASDEMO.PAS is included on theã   distribution disks.ããTP 4.0 5.0 5.5 - INTERFACING MODULES WRITTEN IN MICROSOFT CãQ. Can I link modules, written in Microsoft C, with Turbo Pascalã   programs?ãA. Yes.  M/S C .OBJ modules can be linked provided they do notã   use the C runtime library.  The same limitations apply to M/Sã   C modules as to Turbo C modules.ãã    8      06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND - OOP QA         IMPORT              56          ãTP 5.5 OOP - OBJECT EXE FILE SIZE OVERHEADãQ. How much overhead will result in the *.EXE file from using theã   object oriented style?ãA. The overhead will result from the pointer from the object toã   its method.  This is a 4 byte pointer, so there isn't thatã   much extra code generated.ãããTP 5.5 OOP - PROTECTED AND PRIVATE FIELDSãQ. Does Turbo Pascal 5.5 support Protected or Private fields?ãA. No it does not.ãããTP 5.5 OOP - RECORDS VS. OBJECTSãQ. What things can be done with Records that can not be done withã   Objects?ãA. You cannot have:ãã     1. Variant Objects.ã     2. Objects with absolutes.ã     3. Directly nested Objects.ãã   You can have:ãã       1. Pointers to objects.ããTP 5.5 OOP - EXTERNAL METHODSãQ. Can methods within an object be external?ãA. Yes. Virtual and Static methods can be written as externalã   code. There is no difference between an external Virtual orã   Static method. External Constructors and Destructors areã   difficult to write due to the Prolog and Epilog code withinã   them.ããTP 5.5 OOP - CONSTRUCTOR USEãQ. What are the three purposes of a Constructor?ãA. 1. Insert the address of the VMT into the Object variable.ã        (Implicit)ã   2. To allocate memory for the Object variable. ã        (Implicit)ã   3. To initialize the Object variable. ã        (Explicit)ããTP 5.5 OOP - DESTRUCTOR HEAP CORRUPTIONãQ. Why is my destructor fragging my heap?ãA. Use: ã     ã     Dispose(ptr,done);ãã   instead of:ã   ã     ptr^.done;ã     Dispose(done);ããTP 5.5 OOP - SAVING OBJECTS TO DISKãQ. Can I save my objects to a disk file like I can a recordã   structure?ãA. We have provided an example program on the distributionã   diskette, STREAMS, which documents how to save an object toã   disk.ããTP 5.5 OOP - FILES OF OBJECT TYPEãQ. Why can't I make a file of ObjectType?ãA. Because by the rules of polymorphism, any descendant ofã   ObjectType would be type compatible and be able to be written ã   to disk as well. The problem with this is that the descendantsã   may be (and usually are) of a larger size than the ObjectType ã   itself.  Pascal's file structure require all records to be ã   the same size, otherwise, how do you know which size object ã   do you read in? For an example on how to do Object disk I/O, ã   please see the STREAMS example on the distribution diskettes.ããTP 5.5 OOP - SIZEOF OBJECTS CONSTRUCTORãQ. Why does Sizeof(MyObject) return a size 2 bytes larger than Iã   expect when no virtual methods are used?ãA. By placing a constructor in your object, you're making the ã   object virtual.ããTP 5.5 OOP - LINKER STRIPS UNUSED STATIC METHODSãQ. It appears that my static methods are getting stripped from ã   my program by the smart linker when they are unused. Yet, ã   my virtual methods are not. How come?ãA. Static methods can be stripped because it can be determined atã   link time what methods will be called. Virtual methods can ã   not be stripped because the program will not know what methodsã   will be used, and what will not, until run time. This is ã   because of late binding.ããTP 5.5 OOP - CONSTRUCTOR CALL TO ANCESTOR WITH VIRTUALSãQ. If a descendant of a virtual object defines no virtualã   methods of its own, does it need to call the ancestor'sã   constructor?ãA. If an object is a descendant of a virtual object, it must callã   that ancestor's constructor or it's own constructor. Even ifã   the new object does not define any virtuals of its own. Forã   example:ãã     Typeã       A = Object ã             Constructor Init; ã             Procedure AA; Virtual; ã       End; ã       B = Object ( A ) ã       End; ãã   For each instance of A and of B, Init must be called or theã   Virtual Method Table pointer will not be loaded correctly.ããTP 5.5 OOP - OVERRIDE VIRTUAL METHOD CALLING ANCESTORãQ. Can I override a virtual method and force a call to theã   ancestor objects method?ãA. No. Late binding will always call the current method and itã   defeats the purpose of object oriented program to go around ã   this feature.ããTP 5.5 OOP - CONSTRUCTOR CALL WITHIN METHODãQ. I am calling my constructor from within a method, why am Iã   having problems?ãA. The problem will arise when the constructor loads the new VMTã   pointer. It loads the pointer to the VMT for the constructor'sã   table, not the instances. Therefore if a descendant calls anã   ancestor's constructor, the descendant's VMT will now point toã   the ancestors VMT. The problem now occurs when the descendantã   tries to call a method that was defined after the ancestor.ã   The VMT entry for this method is unknown. Look at theã   following example: ãã     Type ã       L1 = Object ã              Constructor Init;ã              Procedure First; Virtual; ã       End; ã       L2 = Object ( L1 ); ã              Constructor Init; ã              Procedure Second; Virtual; ã       End;ãã     Constructor L1.Init;ã     Beginã     End;ãã     Constructor L2.Init;ã     Beginã     End;ãã     Procedure L1.First;ã     Beginã       Init;ã     End;ãã     Procedure L2.Second;ã     Beginã       Init;ã     End;ãã     Varã       L : L2;ãã     Beginã       L.Init;   { This calls L2.Init and loads a pointer to }ã                 { the L2 VMT into L.                        }ã       L.First;  { This will call L1.First, which in turn calls }ã                 { L1.Init because as far as the procedure is   }ã                 { concerned, the Self pointer is a pointer     }ã                 { to an object of type L1.                     }ã       L.Second; { This is undefined. Since the VMT now       }ã                 { pointed to by L is L1's, the pointer to    } ã                 { method Second is undefined. Therefore, the }ã                 { call to this method is undefined.          }ã       ...ããTP 5.5 OOP - CONSTRUCTOR CALL WITHIN POLYMORPHIC METHODãQ. Does the previous question apply to polymorphic procedures?ãA. Yes. The previous question and answer apply to every caseã   where the compiler may think of an object as its ancestor. Aã   polymorphic example that is incorrect follows:ãã     Procedure Init ( var x : L1 );ã     Beginã       x.Init;  { This will ALWAYS call L1.Init }ã     End;ããTP 5.5 OOP - CONSTRUCTOR CALLING ANCESTOR'S CONSTRUCTORãQ. Should my current object's constructor call it's ancestor'sã   constructors?ãA. As a rule of thumb, this is the correct thing to do. It isã   okay to not do it, but it does allow initialization ofã   whatever the previous constructors did.ããTP 5.5 OOP - CALLING ANCESTORS CONSTRUCTORãQ. How do you call a constructor from an ancestor's object?ãA. You can call an ancestor's constructor directly from withinã   the constructor of the current object.  For example:ãã     Typeã       Type1 = Objectã                 Constructor Init;ã       End;ã       Type2 = Object ( Type1 )ã                 Constructor Init;ã       End;ãã     Constructor Type1.Init;ã     Beginã     End;ãã     Constructor Type2.Init;ã     Beginã       Type1.Init;ã     End;ã     ...ãã                                                                   9      06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND OVERLAYS QA      IMPORT              21          ããTITLE: TRUBO PASCAL OVERLAY ISSUESã===========================================================ããTP 5.0 5.5 - OVERLAY SUPPORTãQ. Are overlays supported in 5.0+?ãA. Yes! See the example program OVRDEMO.PAS and refer to theã   Turbo Pascal manual for information on overlays.ããTP 5.0 5.5 - OVERLAY UNITS LOADED INTO BUFFERãQ. Is there any way to determine what overlay units are loadedã   into the overlay buffer?ãA. Using Turbo Pascal 5.0+, there is no defined method forã   determining which units are loaded into the overlay bufferã   area. ããTP 5.0 5.5 - OVERLAYS *.OVR FILESãQ. How can I transfer a large overlay file onto a floppy disk?ãA. If the file does not fit on a single density disk, thenã   transfer it to a double density disk.  In the latter case,ã   your application will run only from the double density disk orã   a hard disk.ããTP5.5 OVERLAYãQ. How do I reclaim the memory used by overlay buffer in TP5.5?ããA. The following example demonstrates how to do so.ããunit Marker;ããinterfaceããprocedure RestoreHeap; procedure RestoreOverlay;ããimplementationããvar OldHeapPtr,OldHeapOrg,Temp:pointer;ããvar OldHeapPtr,OldHeapOrg,Temp:pointer;ããprocedure RestoreHeap; beginã  Release(Temp);                {2. Release all dynamic variables}ã  OldHeapOrg:=HeapOrg;          {3. Save Current Heap state }ã  OldHeapPtr:=HeapPtr;ã  HeapOrg:=ptr(OvrHeapOrg,$0);  {4. Set Heap to Origin of Overlay Buffer}ã  HeapPtr:=HeapOrg;ã  Mark(Temp);                   {5. Mark the origin of this heap } end;ããprocedure RestoreOverlay; beginã  Release(Temp);                {6. Release all dynamic variables }ã  HeapOrg:=OldHeapOrg;          {7. Restore heap pointers }ã  HeapPtr:=OldHeapPtr; end;ããbeginã  mark(temp);  {1. Mark the beginning of heap after overlay buffer} end.ãend.ããThe unit is to be used before any unit that places items into the heap duringãtheir initialization.  You would call RESTOREHEAP before using the dynamicãvariables requiring memory used by the overlay buffer.  A call to OVRCLEARBUFãmust come before any call to RESTOREHEAP to ensure that the overlay buffer ãis empty and to force a reload of overlays when you are done.  Note, that all ãdynamic variables residing on the heap, before a RESTOREHEAP call is made, areãlost. (But, you could code around this by manipulating the free list).ããRESTOREOVERLAY clears the heap again and restores the heap pointers to pointãabove the overlay buffer.  You may then start using overlaid procedures andãfunctions again.ããThis is a simple example and may not be suitable for all purposes.ããããã        10     06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND TOP TEN QA       IMPORT              47          Top Ten Turbo Pascal Technical Support Questionsããã  1. How do you read and write a file inside a Turbo Pascalã     program?ãã     Answer: The following example demonstrates how to create,  ã     write and read from a text file.ãã     Program FileDemo;ã     Var FileVar : Text;ã         InString,OutString : String;ã     Beginã        OutString := 'Write this to a text file';ã        Assign(FileVar,'TEST.TXT'); ã        Rewrite(FileVar);           {Creates a file for writing}ã        Writeln(FileVar,OutString); ã        Close(FileVar);ã        Assign(FileVar,'TEST.TXT'); ã        Reset(FileVar);      {Opens an existing file for reading}ã        ReadLn(FileVar,InString);ã        Close(FileVar);ã     End.        ã                 ãã  2. Where is the GRAPH.TPU file?ãã     Answer: The GRAPH.TPU is archived in the BGI.ARC file. Useã     the UNPACK.COM program to dearchive GRAPH.TPU from theã     BGI.ARC file. For example:  ã     ã         UNPACK BGIãã  3. How do you send a program's output to the printer?ãã     Answer: Use the LST file variable declared in the PRINTERã     unit. For example:ãã         Program SendToPrinter;ã         Uses Printer;ã         Beginã           Writeln(LST,'This will go to the printer.');ã         End.ãã  4. Why am I getting a "Unit file format error" when I compileã     my program with the new Turbo Pascal compiler?ãã     Answer: You are using a unit that has been compiled with aã     different Turbo Pascal version. From the main program, use ã     the BUILD option to recompile all dependent units.ããã  5. How do you dump graphics to a printer?ãã     Answer: Please see the enclosed Technical Informationã     Handouts, #433 and #432, for examples of printing graphicsã     on Epson compatible and HP Laser Jet printers.ããã  6. How do you communicate with the serial port?ãã     Answer: Modify the AuxInOut unit described in the Referenceã     Guide as follows: To read incoming data, use the Resetã     procedure before reading. Also, you should read from a    ã     buffer rather than directly from the serial port. ã     Furthermore, the AuxInOut is not an ideal example to use asã     input buffering is not supported;  you should use the    ã     enclosed Technical Information Handout #407 routines forã     this purpose. ããã  7. Why doesn't the Exec procedure execute my subprograms?ãã     Answer: Make certain that you use the {$M} directive to setã     the maximum heap size to the lowest possible value.  If thisã     is done, check the value of the variable DosError toã     diagnose other problems. What is DosError Returning afterã     the call: ã   ã     8) Not enough Memory:  User needs to lower MAX Heap ã                            {$M Stack, Min, Max}    ã     2) File not found:  User needs to specify the fill Path andã                         extension of the command.  If you'reã                         trying to execute a DOS internalã                         command, you need to use COMMAND.COMã                         (see DIR example in manual).ããã  8. What do I do about running out of memory during compilation?ãã     Answer: There are a number of solutions to this problem:ãã      1. If Compiler/Destination is set to Memory, set it to Diskã         in the integrated environment.ã      2. If Options/Compile/Link buffer in the integratedã         environment is set to Memory, set it to Disk. ã         Alternatively, if you're using 4.0, place a {$L-}ã         directive at the beginning of your program.  Use the /Lã         option to link to disk in the command-line compiler.ã      3. If you are using any memory-resident utilities, such asã         Sidekick and Superkey, remove them from memory.ã      4. If you are using TURBO.EXE, try using TPC.EXE instead -ã         it takes up less memory.ã      5. Turn off any compiler directives which are notã         necessarily needed.  By simply turning off rangeã         checking {$R-} and software emulation {$E-}, your codeã         size will be reduced dramatically.ã      6. Move all units, except PRINTER.TPU, out of the TURBO.TPLã         file and into the installed units directory.ã      ã    If none of these suggestions help, your program or unit may ã    simply be too large to compile in the amount of memoryã    available,  and you may have to break in into two or moreã    smaller units.  Alternatively, if you're using 5.0+, youã    should consider using overlays.ããã  9. How can my program be over-writing memory?ãã     Answer: The most common causes for memory overwrites are:ãã      1. Indexes out of range     (Turn range checking on {$R+}) ã      2. Uninitialized variables  (Write an initialization proc) ã      3. Pointers out of bounds   (Verify that pointers are not ã         pointing outside of the heap space)     ã      4. Improper use of FillChar or Move (Be sure to use theã         SizeOf function)     ã      5. Illogical operations on stringsã   ãã 10. How come I don't get the results that I expect when Iã     compare and print real numbers?ãã     Answer: The problem with real numbers in Turbo Pascal is aã     problem with how a real number is stored in binary form. A ã     binary number has no decimal point and thus a real number  ã     cannot directly translate into a binary number easily.    ã     Calculations must be performed to break a real number downã     into it's binary representation.  As with any calculation  ã     that involves division or multiplication, small rounding   ã     errors will occur. The problem you are experiencin g is aã     rounding error that occurs during translation from a realã     number into it's binary representation and back. I suggestã     that you round the results of your calculation to the numberã     of decimal points that you require to alleviate the problem.ã                                                                                                      11     06-01-9306:59ALL                      SWAG SUPPORT TEAM        BORLAND QA for TPW       IMPORT              81          QNA-4/8/91ãTurbo Pascal for Windowsã1.0ãLocal Heap and Global HeapãTPW's New, GetMem, Dispose and Freemem all use Window's global heap.ãã[[[]]]ããThe Local Heap is very small, and not used by TPW, and is occasionally usedãby Windows functions (like edit controls).ããThe Global Heap is generally very big, and is where your pascal program'sã"heap" is stored.  The GetMem, FreeMem, New, and Dispose procedures allãoperate on the Global Heap.ãã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãExec from within a TPW program.ãUse WinExec in the WinDOS to launch DOS and WinApp programs.ãã[[[]]]ãQuestion:ã   I want to be able to EXEC a DOS or Windows program in Windows. How do Iã  do that?ãAnswer:ã   Take a look at the WinExec procedure in your Windows Reference Guide.ã  You can use Exec on both WinApps and DOSApps.ããã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãTPW New and GetMem proceduresãNew, GetMem, HeapBlock, HeapLimit in TPWãã[[[]]]ãThe way memory allocation works for new and getmem is as follows:ããThere are two system variables:  HeapBlock and HeapLimit.ããWhen memory is allocated for a size less than HeapBlock (Default is 1k)ãthen the memory will be suballocated in a block of size HeapBlockã(default is 8k).ãAllocation of blocks larger than HeapLimit will have there own block.  Allãallocations will be in global memory.ããYou will not be able to change the way this works.ããã<<<>>>ãQNA-4/8/91ãTurbo Pascal Windowsã1.0ãWinCrt and Windows 3.0ãWinCrt cannot be used with OWL.ãã[[[]]]ãã WinCrt is an emulation of the Dos and Crt units.  Basically, it provides aãquick and dirty means of output to a Windows window.  It's not intendedãto be a Dos to Windows porting service - WinCrt apps are not full Windowsãapps - just a Windows window with some text, key scanning loops, andãscrollbars.ãã  Note that writelns to the 'screen' will cause a runtime error in TPWãunless the WinCrt unit is used. (WinCrt opens the standard Input andãOutput files, which are closed by default in TPW).  The Readln, Keypressed,ãprocedures, etc., are also only valid under the WinCrt umbrella. The textãcolors and text inverse are not supported.  And since WinCrt takes control ofãthe message loop and dispatch services of your windows app, WinCrt cannot beãused in conjunction with OWL. You can call Windows API functions directly,ãhowever.ããã<<<>>>ãQNA-4/8/91ãTurbo Pascal Windowsã1.0ãPChars in TPW.ãWhat is a PChar type?ãã[[[]]]ãã  PChar is a special pointer type that has been added to the PascalãWindows language definition.  A PChar is a pointer to an array ofãcharacters with a maximum size of 64K and terminated by a null characterã(#0).  This is a C string, and comes with all the memory management hasslesãand pointer arithmetic advantages of C. The PChars and arrays of char areãtype compatible, and a Strings unit is devoted to C-string manipulationãfunctions (all start with "STR").  When passing a string literal to aãprocedure, the compiler will figure out whether the string literalãshould be stored in c-string format or in pascal string format, or both.ããã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãTDW popping in dual mode in TPW.ãHow to make TDW come up in dual mode from within TPW.ãã[[[]]]ãQuestion:ã I want to make TDW come up in dual mode from within the TPW.ãAnswer:ã  Add the following lines to the tpw.ini file:ãã  [Debugger]ã  Exepath=<pathname>ã  Switches=<command-line options>ãã  where <pathname> is the path to the TDW.EXE and <command-line options>ã  are the options you normally want to use with TDW, this will do it.  Theã  command-line option for the dual display is -do.ããã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãChanging Button TextãUse SetCaption method in OWL or SendMessage API to change button text.ãã[[[]]]ãQuestion:ãHow do you change the text on a button?ããAnswer:ãTo change the text on a button, send a WM_SETTEXT message with the string forãthe lParam.  For example:ãã  SendMessage(btnWindow, WM_SETTEXT, 0, 'New Button Title');ããOr if you are using OWL, call the SetCaption method of TButton.ããã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãStatus line in a MDI Application.ãHow can I have a status line in an MDI Application?ãã[[[]]]ããTo have a status line in an MDI application, override the windows MDIãWMSize method like this:ãã  TGDIDemoWindow.WMSize(var Message: TMessage);ã  beginã    TMDIWindow.WMSize(Message);ã    {Calculate the new size of the client area}ã    MoveWindow(ClientWnd^.HWindow, size of the new client area)ã  end;ããThis will keep the client window from obscuring your status line or otherãcontrols you might like to have in your window.ããã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãTPW  1.0 Hot Sheet.ãFeature set of TPW 1.0.ãã[[[]]]ãTurbo Pascal for Windows 1.0 Hot Sheet.ããFeature Highlightsã----------------------ãNEW! state-of-the-art Windows integrated development environmentã(IDE).ãã	Runs under Windowsã	Multiple editor windowsã	Full Mouse Supportã	Supports TP6 type hot keysã	Multi-file editor that can edit files up to 1MBã	complete save and restore of desktopããNEW! ObjectWindow library - Built in support for Windows, Menus,ãDialogs, Buttons, List boxes, Edit fields, Icons and more.  Allãfor use in your applications.ããNEW! Full access to all Windows API functions and messages.ããNEW! Fully Supports creation of DLLs.ããNEW! Turbo Debugger for Windowsãã	Supports Windows messagesã	Advanced breakpointsã	Reverse executionã	Automatic DLL debuggingã	Object browser and inspectorsã	Single and dual monitor supportããINCLUDED! Whitewater Resource Toolkit - Visually create Dialogs,ãMenus, Icons, Bitmaps and String resources.ããINCLUDED! Resource CompilerããINCLUDED! Windows Help CompilerããFull featured inline assembler (BASM)ããPrivate fields and methods in object declarationsããExtended syntax directive ($X) that lets you treat functions likeãprocedures (and ignore function results)ãã286 code generationããAddress references in typed constantsããFar and near procedures directives ($F)ããLink in initialized data ($L) from object (OBJ) filesããSmart linker removes unused objects and codeããcomplete math coprocessor emulation and support -ã8087/80287/80387ããNEW! Turbo Help hypertext on-line help facilities, includingãreferences to all Windows API finctions and messages.ããTurbo Pascal for Windows includes everything you need to createãWindows applications.  It does not require the Microsoft WindowsãSDK.ããSystem Requirementsã---------------------ãIBM PC or PS/2 and all 100% compatiblesãMicrosoft Windows 3.0 or laterã2Mb of memoryãEGA, Hercules or VGA graphicsãMouse or other pointing deviceãHard Disk (Requires 6.5 Meg for full product)ããBenchmarksã-----------ãMachine				Lines/MinãCompaq DeskPro 386/33	84,000ããDocumentationã----------------ã1. User's Guideã2. Programmer's Guideã3. Windows Reference Guideã4. Windows Programming Guideã5. Whitwater Resource Toolkit Users Guideã6. Turbo Debugger for Windows Users Guideã7. Help Compiler Reference GuideããPricing Informationã-------------------------ãTurbo Pascal for Windows   $249.95ãããSpecial Offer Informationã---------------------------ãRegistered user of ANY BORLAND LANGUAGE PRODUCT $99.95 ***ããã***Turbo Pascal for Windows is exclusively for WindowsãDevelopment and does not replace Turbo Pascal version 6.0.  TP6ãis the current product for standard DOS development.  The offerãis not an upgrade it is a limited special offer to our currentãTurbo Pascal Customers.ãã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãThe $G+ directive, 286 code generation, and Real Mode.ãHow can I detect if Windows is in Real mode?ãã[[[]]]ãQuestion:ããIf I use the $G+ 286 code generation directive, how can I be sure thatãWindows is running in Real mode?ããAnswer:ã  The 286 code generation option allows the compiler to substitute moreãefficient 286 instructions in place of normal 8088 instructions for thingsãlike loops, jumps and memory operations.  If you compile your program withã$G+, then you should call GetWinFlags early in the startup of yourãapplication - such as in your Application.Init.  Check for the wf_PtModeãflag set.  If it's not set, then you're not running in protected mode, andãyou should display a message box and exit your program.  Setting theãApplication's status variable to a non-zero value should also work, althoughãthat would cause more code to be executed, implying greater risk of hitting aã286 instruction.ãã  All the precompiled units in TPW will run in real mode and are notãaffected by the $G+ directive.  The $G+ directive only affects code that youãcompile yourself.ãã<<<>>>ãQNA-4/8/91ãTurbo Pascal for Windowsã1.0ãUsing the 80X87 emulator in TPW.ãDo I need a coprocessor to run TPW?ãã[[[]]]ãQuestion:ã Must TPW a the math coprocessor?ããAnswer:ã  Windows implements its own 80x87 instruction emulator as a DLL that isãloaded when an 80x87 instruction is detected.  The reason that the '87 codeãgeneration is an option is speed:  emulators are always slower than theãreal hardware, and slower than using the 6 byte reals.  If you need double,ãor  extended reals, or Comp integers, then you must turn on '87ãcode generation, and Windows will adjust itself to compensate if thereãis no '87 hardware when the program is running.  You do not need aãcoprocessor to compile or run the program.ãã<<<>>>ããTN-4/8/91ãTurbo Pascal for Windowsã1.0ãTPW "Can't find Debugger" error messageã"Can't find Debugger" error message can be caused by not enough memoryãavailable.ã[[[]]]ãWith Turbo Pascal for Windows you can get the Can't Find Debugger errorãmessage when there is not enough memory to load the debugger.  Thereãmust be at lease 250K free in order to load the debugger.ããIf you have checked that TDW is in the path etc., and this error is stillãdisplayed,  check the memory free...ããã<<<>>>ãTN-4/8/91ãTurbo Pascal for Windowsã1.0ãTPW Strings literals in the Data SegmentãTPW String literals are stored in the Data Segment.ãã[[[]]]ãUnlike the DOS version of Pascal, String Literals are stored in the dataãsegment along with all global variables etc.  If customer is running outãof data segment space, suggest using pointers to strings instead ofãstring literals.ããã<<<>>>ã                                                                                                                12     06-08-9308:29ALL                      CHRIS PRIEDE             BASM Tutoral #1          IMPORT              131         ===========================================================================ã BBS: The Beta ConnectionãDate: 06-03-93 (00:08)             Number: 680ãFrom: CHRIS PRIEDE                 Refer#: NONEã  To: ALL                           Recvd: NO  ãSubj: BASM TUT01 (1/4)               Conf: (232) T_Pascal_Rã---------------------------------------------------------------------------ãã    No matter what HLL you use -- Pascal, C, COBOL or some otherãlanguage -- there is still place for assembly in your programs.ãCorresponding directly to the native language of your computer, itãcombines unlimited control with unmatched efficiency.ãã    Since Borland added the built-in assembler in TP 6.0, enhancingãPascal programs with some assembly has become much easier than before.ãHowever, I have read many books and found the BASM sections in TurboãPascal books are inadequate and frequently contain errors. There areãvery good assembly books available, but they focus on writingãassembly-only programs using standalone assemblers.ãã    Considering this, I decided to write a text file -- assemblyãlanguage lessons for TP programmers, mostly using BASM. When I asked ourãhost Guy for his opinion on this idea, he aproved and suggested I postãsections weekly in this conference.ãã    A large part of this tutorial will be dedicated not to assemblyãlanguage itself, but tasks that often require it: writing interruptãhandlers and TSRs, accessing hardware directly. I will try to post newãsections weekly, if my schedule permits. Questions, suggestions andãcriticism are very welcome.ãã    You will need a copy of TP 6.0 or later. Turbo Debugger, TASM orãMASM could be useful, but are not required.ããã - I -ãã    To get started, we will take a Pascal routine, convert it toãassembly (using BASM) and see if we can beat the compiler at codeãgeneration. We will use a simple integer square root function; it is notãsomething you would need often (unless you are writing graphicsãroutines), but serves well as an example and only requires simple dataãmovement and arithmetic instructions.ããfunction ISqr(I: word): word;ãvar Root, LRoot: word;ãbeginã  Root := 1;ã  repeatã    LRoot := Root;ã    Root := ((I div Root) + Root) div 2;ã  until ((integer(Root - LRoot) <= 1) andã   (integer(Root - LRoot) >= -1));ã  ISqr := Root;ãend;ãã    It is based on a well-known formula (name has escaped my memory).ãThe loop usually continues until Root and LRoot are equal, but thatãmight never happen with integers because fraction is truncated. Ourãversion loops until difference is less than 1, resulting in almostãcorrectly rounded result. The number of iterations required to findãsquare root of N never exceeds (ln N) +1, which means our function willãfind the square root of any valid argument in 12 or less iterations.ãã    Now, let's convert this to assembly. One major improvement we canãmake is to place both temporary variables in registers. CPU can accessãregisters much faster than memory. AX and DX are needed for division, soãwe will assign Root to register CX and LRoot -- to BX:ããfunction ISqr(I: word): word; assembler;ãasmã  mov   cx, 1           {  Root := 1                                  }ã@@1:                    { loop start label                            }ã  mov   bx, cx          {  LRoot := Root                              }ã  mov   ax, I           {<                    <                       }ã  sub   dx, dx          {< AX := (I div Root) <                       }ã  div   cx              {<                    < Root := ((I div Root) }ã  add   ax, cx          {  AX := AX + Root    <   + Root) div 2       }ã  shr   ax, 1           {  AX := AX div 2     <                       }ã  mov   cx, ax          {  Root := AX         <                       }ã  sub   ax, bx          {  AX := Root - LRoot                         }ã  cmp   ax, 1           {  Compare AX to 1...                         }ã  jg    @@1             {  Greater than 1  -- continue loop           }ã  cmp   ax, -1          {  Compare AX to -1...                        }ã  jl    @@1             {  Less than -1 -- continue loop              }ã  mov   ax, cx          {  ISqr := Root -- return result in AX        }ãend;ãã    Simple statements translate to one instruction, while complexãexpressions have to be broken down in smaller steps. Notice we computeãexpression (Root - LRoot) only once, although it's result is testedãtwice. As you will see shortly, Turbo Pascal is not smart enough yet toãtake advantage of this: compiled Pascal code would subtract twice.ãã(continued in next message...)ã---ã * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636ã * PostLink(tm) v1.05  DWTOOLBOX (#1035) : RelayNet(tm)ã===========================================================================ã BBS: The Beta ConnectionãDate: 06-03-93 (00:10)             Number: 681ãFrom: CHRIS PRIEDE                 Refer#: NONEã  To: ALL                           Recvd: NO  ãSubj: BASM TUT01 (2/4)               Conf: (232) T_Pascal_Rã---------------------------------------------------------------------------ã    Function result is left in AX register. TP expects function returnãvalues in the following registers:ãã    Char, Byte          : ALã    Word, Integer       : AXã    LongInt, pointers   : DX:AXã    (low order word/offset in AX, high order word/segment in DX)ããã    Let's go through this line by line...ãã* function ISqr(I: word): word; assembler;ãã    This is a standard function declaration, except for the wordã"assembler", which tells Turbo Pascal entire function is going to be inãassembly (if you try to insert some Pascal code in assembler function,ãit won't work).ãã* asmãã    "Asm" means start of assembly block, just like "begin" means startãof Pascal block. Since our function was declared assembler, it has onlyãasm block; without "assembler" keyword it would have to start withã"begin":ããfunction Foo;ãbeginã  asmã    { some assembly code }ã  end;ãend;ãã    You can use asm blocks anywhere in your Pascal code, but conventionsãfor pure asm functions are somewhat different.ãã*  mov   cx, 1ãã    MOV (Move) instruction is assembly assignment statement. This lineãis equivalent to CX := 1 in Pascal.ãã* @@1:ãã    This is a local label, not unlike Pascal labels, used with dreadedãGOTO statements. Unfortunately, the only means of flow control in asm isãusing GOTO-like jumps (conditional or unconditional), so you wouldãbetter get used to it. Destination of such jumps can be a Pascal-style,ãpreviously declared label or a local label, like the one above. Localãlabels don't have to be previously declared, but they should start withã@ (at sign).ãã*  mov   bx, cxã*  mov   ax, Iãã    Some more MOVing. Notice we can move data between two registers orãregister and memory (argument I is stored on stack -- in memory). Weãcan't, however, directly move one memory variable into another: mostã80x86 instructions can't have two memory operands. If you ever need toãassign one memory variable to another, it should be done through aãregister, like this:ãã    mov     ax, Xã    mov     Y, axãã    The same applies to most other instructions with two operands.ãã*  sub   dx, dxãã    SUB (Subtract) subtracts the right operand from left and leaves theãresult in left operand. SUB AX, CX is equivalent to AX := AX - CX inãPascal.ãã    As you may have noticed, we are subtracting DX from itself. This isãa better way of setting register to 0 (100 - 100 = 0). We could use MOVãDX, 0, but SUB instruction is one byte smaller and a few clock cyclesãfaster. Some programmers use XOR for the same purpose: a number XORedãwith itself results in 0 too. We need DX to be 0 for division.ãã*  div   cxãã    DIV (Divide). 80x86 divide and multiply instructions are differentãfrom other arithmetic instructions. You only need to specify divisor;ãother operands are assumed to be AL, AH or AX, DX registers. This tableãsummarizes both DIV variants:ããDividend       ³Divisor    ³Quotient       ³RemainderãÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅã16 bit (AX)    ³8 bit      ³8 bit (AL)     ³8 bit (AH)ã32 bit (DX:AX) ³16 bit     ³16 bit (AX)    ³16 bit (DX)ãã(continued in next message...)ã---ã * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636ã * PostLink(tm) v1.05  DWTOOLBOX (#1035) : RelayNet(tm)ã===========================================================================ã BBS: The Beta ConnectionãDate: 06-03-93 (00:12)             Number: 682ãFrom: CHRIS PRIEDE                 Refer#: NONEã  To: ALL                           Recvd: NO  ãSubj: BASM TUT01 (3/4)               Conf: (232) T_Pascal_Rã---------------------------------------------------------------------------ã    The size of divisor selects between 16 and 32 bit divide. In ourãfunction both dividend and divisor are 16 bit words, which is why we hadãto zero DX, effectively extending word in AX to 32 bits.ãã    Divisor should be a register or memory variable. If you need toãdivide by immediate value, first move it in a register:ãã    mov     bx, 320ã    div     bxãã    Use IDIV (Integer Divide) for signed numbers (integer, longint).ãIDIV works exactly like DIV, but performs signed division.ãã* add   ax, cxãã    ADD (Addition). Pascal equivalent: AX := AX + CX.ãã* shr   ax, 1ãã    Bitwise Shift Right. Pascal equivalent: AX := AX shr 1. As the nameãimplies, this instruction shifts bits right:ããAX before shift:    0000101100110110    (decimal 2870)ãAX after shift:     0000010110011011    (decimal 1435)ãã    If you look at the decimal values on the right, you will noticeãshifting divided the number by 2. That is correct: shifting a binaryãnumber N bits left/right is equivalent to multiplying/dividing it byã2^N. CPU can shift bits much faster than divide and shift instructionsãare not restricted to certain register(s) like DIV -- remember thisãwhen you need to multiply/divide by a power of 2.ãã    The first operand (value to be shifted) can be either register orãmemory. The second operand is number of bits to shift -- immediate valueãor CL register. 8086 allows _only_ immediate value of 1; to shift by moreãthan one bit use the following:ãã    mov     cl, 3   (bit count)ã    shr     ax, clãã    You can also shift several times by one (I would use this method onlyãto shift by 2 - 3 bits, otherwise it gets too long):ãã    shr     ax, 1ã    shr     ax, 1ã    shr     ax, 1ãã    286+ can shift by any immediate count. If you are compiling for 286ãand better ({$G+} compiler directive), you can do this:ãã    shr     ax, 3ãã*  cmp   ax, 1ãã    CMP (Compare) compares two operands and sets CPU flags to reflectãtheir relationship. Flag state are later used to decide if a conditionalãjump instruction should jump or not. This two step process is used toãcontrol program flow, like if..then statements and loops in Pascal.ãã*  jg    @@1ãã    ...and this is a conditional jump. JG (Jump if Greater) will transferãcontrol (GOTO) to label @@1 if the last compare found left operand to beãgreater than right, otherwise it "falls through": execution continues atãthe next instruction. JG assumes operands were signed (integers). Use JAã(Jump if Above) for unsigned values (words). The following is a summaryãof conditional jumps for arithmetic relationships:ããJA/JNBE     Jump if Above                   (">",  unsigned)ãJG/JNLE     Jump if Greater                 (">",  signed)ãJAE/JNB     Jump if Above or Equal          (">=", unsigned)ãJGE/JNL     Jump if Greater or Equal        (">=", signed)ãJE/JZ       Jump if Equal                   ("=")ãJNE/JNZ     Jump if Not Equal               ("<>")ãJB/JNAE     Jump if Below                   ("<",  usigned)ãJL/JNGE     Jump if Less                    ("<",  signed)ãJBE/JNA     Jump if Below or Equal          ("<=", unsigned)ãJLE/JNG     Jump if Less or Equal           ("<=", signed)ãã    For ease of use, assemblers recognize two different mnemonics forãmost conditional jumps. Use the one you find less cryptic.ãã    Since conditional jump instructions simply inspect flags set byãprevious compare, there may be other instructions in between, providedãthey don't alter flags -- for example, MOV. Flags are not cleared andãcan be tested more than once. For example, you could do this:ãã(continued in next message...)ã---ã * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636ã * PostLink(tm) v1.05  DWTOOLBOX (#1035) : RelayNet(tm)ã===========================================================================ã BBS: The Beta ConnectionãDate: 06-03-93 (00:29)             Number: 683ãFrom: CHRIS PRIEDE                 Refer#: NONEã  To: ALL                           Recvd: NO  ãSubj: BASM TUT01 (4/4)               Conf: (232) T_Pascal_Rã---------------------------------------------------------------------------ã    mov     ax, Xã    cmp     ax, Yã    jg      @XGreater   { X > Y }ã    jl      @XLess      { X < Y }ã    ....                { fell through -- X = Y }ãã*   end;ãã    Like Pascal blocks, this means the end of BASM block. If you wereãusing a standalone assembler, you would have to add RET (Return fromãsubroutine) instruction and possibly some other (cleanup) code. BASMãadds this and similar purpose code at the top of the functionãautomatically -- it is called entry & exit code and usually amountsãto 1 - 3 instructions, depending from number of arguments and localãvariables.ãã    Well, looks like everything is covered... Quite a bit for the firstãlesson too. If you have the instruction set reference, check it for moreãdetailed descriptions. If you don't, I strongly recommend to obtain one.ãThis is just one of many sources:ãã    The Waite Group's "Microsoft Macro Assembler Bible" or "TurboãAssembler Bible", ISBN 0-672-22659-6 (MASM flavor). $29.95, publishedãby SAMS. A very good reference book, comes in MASM and TASM flavors,ãyou choose.ãã    Finally, here is disassembly of compiled TP code. [BP+n] meansãreference to function argument, [BP-n] -- to local variable. This isãprovided mainly to satisfy your curiosity, but it shows we thought veryãmuch like the compiler, only coded it better:ããISQR:ãisqr1.pas#9:beginã   0000:0000 55              PUSH    BPã   0000:0001 89E5            MOV     BP,SPã   0000:0003 83EC06          SUB     SP,+06ãisqr1.pas#10:  Root := 1;ã   0000:0006 C746FC0100      MOV     [WORD BP-04],0001ãisqr1.pas#11:  repeatãisqr1.pas#12:    LRoot := Root;ã   0000:000B 8B46FC          MOV     AX,[BP-04]ã   0000:000E 8946FA          MOV     [BP-06],AXãisqr1.pas#13:    Root := ((I div Root) + Root) div 2;ã   0000:0011 8B4604          MOV     AX,[BP+04]ã   0000:0014 31D2            XOR     DX,DXã   0000:0016 F776FC          DIV     [BP-04]ã   0000:0019 0346FC          ADD     AX,[BP-04]ã   0000:001C D1E8            SHR     AX,1ã   0000:001E 8946FC          MOV     [BP-04],AXãisqr1.pas#14:  until ((integer(Root - LRoot) <= 1) andãisqr1.pas#15:      (integer(Root - LRoot) >= -1));ã   0000:0021 8B46FC          MOV     AX,[BP-04]ã   0000:0024 2B46FA          SUB     AX,[BP-06]ã   0000:0027 3D0100          CMP     AX,0001ã   0000:002A 7FDF            JG      isqr1.pas#12(000B)ã   0000:002C 8B46FC          MOV     AX,[BP-04]ã   0000:002F 2B46FA          SUB     AX,[BP-06]ã   0000:0032 3DFFFF          CMP     AX,0FFFF  (-1)ã   0000:0035 7CD4            JL      isqr1.pas#12(000B)ãisqr1.pas#16:  ISqr := Root;ã   0000:0037 8B46FC          MOV     AX,[BP-04]ã   0000:003A 8946FE          MOV     [BP-02],AXãisqr1.pas#17:end;ã   0000:003D 8B46FE          MOV     AX,[BP-02]ã   0000:0040 89EC            MOV     SP,BPã   0000:0042 5D              POP     BPã   0000:0043 C20200          RETN    0002ãã    TP's code doesn't place variables in registers and does someãunnecessary work (ie, subtracts Root - Lroot twice; see above). Entryãand exit code is shown too.ãã    Our version is slightly faster, but I didn't pick this routine toãdemonstrate optimization -- integer math is about the only area whereãmost compilers do well. A good optimizing compiler should be able toãgenerate code as good as ours. I would be curious to see what thisãlooks like compiled with SBP+ (Guy: consider this a strong hint :)).ãMost code you would normally write in assembly will show much greaterãimprovement (string routines, etc.).ãã    What to expect: next time we will discuss how to call DOS or BIOSãinterrupts using BASM; what registers are available; which have specialãuses or should be preserved; how to access strings, arrays and records.ãSomewhere in near future: writing object methods in BASM.ãã                             *  *  *ã---ã * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636ã * PostLink(tm) v1.05  DWTOOLBOX (#1035) : RelayNet(tm)ã                                                                             13     08-17-9308:46ALL                      JACK MOFFITT             Modem Reference          IMPORT              163    ~O   From: JACK MOFFITT                 Refer#: NONEã  To: ALL                           Recvd: NOãSubj: MODEM REFERENCE       1/       Conf: (1221) F-PASCALã---------------------------------------------------------------------------ã            Pascal Programmer's Reference to Modem Communicationsãã                                   byãã                               Jack Moffittãã___-------------------------------------------------------------------------ãããINTRODUCTIONã~~~~~~~~~~~~ã        Direct UART programming is a subject that not many people areãfamiliar with.  Since the advent of FOSSIL, many people advise that oneãshould use that for all communications, to make it more portable.  But forãsome instances, it is necessary to have internal modem routines to go on.ãBecause no one seems to know or understand this subject, and because I haveãfound no other texts on the subject, I have decided to put it all into oneãtext, and maybe round off the edges on this subject.ãããTHE ASYNCRONOUS MODEMã~~~~~~~~~~~~~~~~~~~~~ã        The asyncronous modem uses one (or more) specific ports on aãcomputer, as well as an IRQ (Interrupt Request).  Every time a characterãof data is received in the device, an interrupt is processed.  One mustãmake a interrupt service routine to handle this input, but where does it go?ãSince the IRQs are tied into interrupts, knowing the IRQ the device is using,ãwe can replace that interrupt.  The port addresses and IRQ vectors are asãfollows:ããPort Addresses: COM1  --  03F8h        IRQ Vectors   :  0  --  08hã                COM2  --  02F8h                         1  --  09hã                COM3  --  03E8h                         2  --  0Ahã                COM4  --  02E8h                         3  --  0Bhã                                                        4  --  0Chã                                                        5  --  0DhãStandard Port IRQs: COM1  --  4                         6  --  0Ehã                    COM2  --  3                         7  --  0Fhã                    COM3  --  4                         8  --  70hã                    COM4  --  3                         9  --  71hã                                                       10  --  72hã                                                       11  --  73hã                                                       12  --  74hã                                                       13  --  75hã                                                       14  --  76hã                                                       15  --  77hããFor standard use, the IRQ for comm ports 1 and 3 is 4, and for 2 and 4 it'sã3.  The 8250 UART has 10 registers available for getting, receiving andãinterperating data.  They are all located at offsets from the base addressãof the port.  Here are the registers and their offsets:ããRegister Offsets:  Transmitter Holding Register (THR)       --  00hã                   Receiver Data Register (RDR)             --  00hã                   Baud Rate Divisor Low Byte (BRDL)        --  00hã                   Baud Rate Divisor High Byte (BRDH)       --  01hã                   Interrupt Enable Register (IER)          --  01hã                   Interrupt Identification Register (IIR)  --  02hã                   Line Control Register (LCR)              --  03hã                   Modem Control Register (MCR)             --  04hã                   Line Status Register (LSR)               --  05hã                   Modem Status Register (MSR)              --  06hããWith this information one can address any register by adding the offset toãthe base address.  Therefor, if one is using COM2 (base address 02F8h) theyãwould access the Modem Status Register with: port[$02F8 + $06].ãããTRANSMITTER HOLDING REGISTERã~~~~~~~~~~~~~~~~~~~~~~~~~~~~ã        This register contains the data to be sent to the remote PC or modem.ãWhen bit 5 (THR empty) of the LSR is set, one can write to this port, thusãsending data over the phone line or null modem cable.ãããRECEIVER DATA REGISTERã~~~~~~~~~~~~~~~~~~~~~~ã        This register contains the incoming data.  Read this register onlyãif bit 0 (Data Received) of the LSR is set, otherwise one will getãunpredictable characters.ãããBAUD RATE DIVISORã~~~~~~~~~~~~~~~~~ã        The Baud Rate Divisor is used to set the BPS rate.  To calculate theãBaud Rate Divisor, one must use the formula: (UART Clock Speed)/(16*BPS).ãThe UART Clock Speed is 1843200.  To set the BRD one must first set bit 7ã(port toggle) of the Line Control Register to 1, and then write the low andãhigh bytes to the correct offsets.  Always remember to reset LCR bit 7 to 0ãafter one is finished setting the BPS rate.ãããINTERRUPT ENABLE REGISTERã~~~~~~~~~~~~~~~~~~~~~~~~~ã        The IER is used to simulate real interrupt calls.  Write a byteãcontaining to interrupt information to enable any interrupts, all interruptsãalso have corresponding actions to clear the interrupts.  Here's the list:ããInfo Byte:ããbit   7-6-5-4       3                 2                 1           0ã      ~~~~~~~       ~                 ~                 ~           ~ã     Always 0   MSR Change   Data Error or Break    THR empty  Data ReceivedããTo Clear:       Read MSR          Read LSR        Output to THR   Read RDRãããINTERRUPT IDENTIFICATION REGISTERã~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ã        This register is used to determine what kind of interrupts haveãoccured.  Read one byte from this register, and use AND masks to find outãwhat has happened.  The information in the byte is:ããInfo Byte:ããbit   7-6-5-4-3    2-1                                    0ã      ~~~~~~~~~    ~~~                                    ~ã       Unused      0-0 = Change in MSR                If this bit is setã                   0-1 = THR empty                    more than oneã                   1-0 = Data Received                interrupt hasã                   1-1 = Data Error or Break          occured.ãããLINE CONTROL REGISTERã~~~~~~~~~~~~~~~~~~~~~ã        The Line Control Register (LCR) is used for changing the settingsãon the serial line.  It is also used for initializing the modem settings.ãWrite a byte to the port, containing the following info:ããLCR Byte.ãã      Port Toggle   Break Condition   Parity      Stop Bits   Data Bitsãbit       7                6           5-4-3          2          1-0ã          ~                ~           ~~~~~          ~          ~~~ã          0 = Normal       0 = Off     0-0-0 = None   0 = 1      0-0 = 5ã          1 = Set BRD      1 = On      1-0-0 = Odd    1 = 2      0-1 = 6ã                                       1-1-0 = Even              1-0 = 7ã                                       1-0-1 = Mark              1-1 = 8ã                                       1-1-1 = SpaceãEverything is pretty clear except for the purpose of bits 6 and 7.  Bit 6ãcontrols the sending of the break signal.  Bit 7 should always be 0, exceptãif one is changing the baud rate.  Then one must set it to one, write toãthe BRD and then set it back to zero.  One can only write to the BRD if thisãbit is set.ãããMODEM CONTROL REGISTERã~~~~~~~~~~~~~~~~~~~~~~ã        The MCR is used to control the modem and it's function.  Write oneãbyte to the MCR containing the following info:ããMCR Byte.ããbit      0 = Set DTR Lineã         1 = Set RTS Lineã         2 = User Output #1ã         3 = User Output #2ã         4 = UART Loopbackã     7-6-5 = Unused (Set to 0)ããTypically one will set bits 3 through 0 to 1.  Bit 4 is used for testingãtheir routines without another modem, and the other bits are unused, butãshould always be set to 0.ãããLINE STATUS REGISTERã~~~~~~~~~~~~~~~~~~~~ã        The LSR reports the current status of the RS232 serial line.  Theãinformation contained is obtained by reading one byte from the LSR.  Theãbits and the info associated with each are listed below.ããLSR Byte.ããbit     0 = Data Receivedã        1 = Overrun Errorã        2 = Parity Errorã        3 = Framing Errorã        4 = Break Detectã        5 = THR emptyã        6 = Transmit Shift Register (TSR) emptyã        7 = Time OutããThe TSR takes the byte in the THR and transmits is one bit at a time.  Whenãbit 0 is set one should read from the RDR, and when bit 5 is set one shouldãwrite to the THR.  What actions are taken on various errors are left up toãthe programmer.ãããMODEM STATUS REGISTERã~~~~~~~~~~~~~~~~~~~~~ã        Just like the LSR returns the status of the RS232 line, the MSRãreturns the status of the modem.  As with other registers, each bit in theãbyte one reads from this port contains a certain piece of info.ããMSR byte.ããbit     0 = Change in CTSã        1 = Change in DSRã        2 = Change in RIã        3 = Change in DCDã        4 = CTS onã        5 = DSR onã        6 = RI onã        7 = DCD onããCarrier Detect is achieved by testing bit 7, to see if the line is ringingãtest bit 6.ããããPUTTING IT ALL TOGETHERã~~~~~~~~~~~~~~~~~~~~~~~ã        One can now use this information about the 8250 UART to startãprogramming their own modem routines.  But before they can do that, theyãmust learn a little about interrupts and the 8259 PIC (ProgrammableãInterrupt Controller).  This information is necessary to write modemãroutines that are not dependant on a slow BIOS.ãããINTERRUPTSã~~~~~~~~~~ã        Interrupts are a broad subject, and this is not a reference for them.ãFor for information on interrupts, one should look at DOS Programmer'sãReference 4th Edition.  Although there are two kinds of interrupts - Non-ãMaskable and Maskable, maskable interrupts are the only ones that one shouldãbe concerned with.  When an interrupt generates, the processor finishes theãcurrent command, and then saves a few variables (the address to return to)ãon the stack and jumps to the vector of the interrupt.  One can turn offãmaskable interrupts with the STI, and back on with CLI.  One can not turnãoff non-maskable interrupts.  Replacing interrupt routines in pascal is veryãeasy.  Include the DOS unit in their program, and use the procedures GetIntVecãand SetIntVec.  To replace the interrupt for COM2 (remember it's 0Bh) oneãwould do this:ã               GetIntVec($0B, OldInt0Bh);ã               SetIntVec($0B, NewInt0Bh);ãAt the end of the program, one MUST restore the interrupt using:ã               SetIntVec($0B, OldInt0Bh);ãFailing to do this will most likely result in a system crash after theãprogram terminates.  Because another interrupt may be called inside anotherãinterrupt at any time, it is necessary to turn off interrupts, as mentionedãabove, every once in a while.  Remember all this, and programming for theãmodem will be much easier ( :) ).ããã8259 PROGRAMMABLE INTERRUPT CONTROLLERã~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ã        The 8259 PIC is used by the processor as a gateway for interrupts.ãThe 8259 decides which interrupts go first and which are currently active.ãThe order interrupts are processed in in the order of their IRQ number.ãThus, IRQ0 will always be processed before IRQ1 if both are generated at theãsame time.  Since asyncronous communication uses IRQs, we must instruct theã8259 PIC on when are interrupts should start interrupting, and when theyãshould stop.  When initializing the modem, one must "turn on" the IRQ beforeãone can start to use it.  Turning back off is identical, but don't turn itãoff if one is writing door routines!  To do either requires one assign theãvalue contained at the port the value AND the mask.  The masks for turningãon and off the 8259 follows.ããTo Turn On:ã            mask = (1 shl (IRQ number)) xor $00FFãTo Turn Off:ã             mask = 1 shl (IRQ number)ããOne must also reset the PIC in the custom interrupt handler after one isãfinished with it.  That will allow the PIC to process the next interrupt.ãTo reset the PIC, write 20h to it.  This is also refered to as the End OfãInterrupt (EOI) signal.  This must also be done after first initializing theãmodem.  There is another PIC on the 286, allowing the last 8 IRQs (7 - 15).ãThe second PIC is called the cascade PIC.  The addresses for the PIC commandãand mask ports are listed next.ãã8259 PIC command address         = 20hã8259 PIC mask address            = 21hãCascade 8259 PIC command address = A0hãCascade 8259 PIC mask address    = A1hããTo reset the PIC always write to the command, and for turning off with theãmasks always write to the mask.  The masks for the cascade PIC are the sameãfor the other PIC.  So the mask for IRQ0 is equal to the mask for IRQ7.ãAlso, one should write 20h to the cascade PIC as the EOI signal.ãããINPUT/OUTPUT CONTROLã~~~~~~~~~~~~~~~~~~~~ã        To keep the text simple, only buffered input will be covered.ãBuffered output is a subject of more depth than one can provide in a shortãreference.  Buffered input is relatively simple, but there are a few thingsãone must consider.  The size of the buffer is very import, make the bufferãto big and one will eat up the datasegment, make the buffer to small andãone will get overruns.  A good choice for a general buffer would be in theãrange of 4 to 8k.  This should allow plenty of room for all incoming data.ãAnother inportant factor is the type of buffer.  For simplicity and ease ofãuse, a circular input buffer is recommended.  A head and a tail pointãto the start and end of the buffer, and they will both wrap around whenãeither go past the end of the buffer, thus making the buffer a kind ofãcircle.  Getting data in the buffer is the primary job of the customãinterrupt routine.  Clearing the buffer and reading characters from theãbuffer is then as easy as reading a character from an array, and advancingãthe head of the buffer.  Sending characters over the phone can beãaccomplished by waiting for the flow control and then sending the characterãto the THR, repeating for every character.ããTHE INTERRUPT SERVICE ROUTINEã~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ã        The ISR (Interrupt Service Routine) is the backbone for asyncronousãcommunication.  The interrupt is called for every character that comesãthrough the modem.  So in the interrupt one must process these incomingãcharacters or else they will be lost.  Since the the interrupt got called,ãone must check the IIR (Interrupt Identification Register) to see whatãactually cause the interrupt to be called.  Since the interrupt is mainlyãdealing with handling the incoming data, and for reasons of simplicity,ãflow control will be ommited from the routine but will be discussed laterãin this text.  Since one is writing to the buffer, and since anotherãcharacter is likely to come in during this time, one must disable interruptsãfor the shortest time possible while writing to the buffer, and then reenableãthem so no data is lost.  (NOTE: If the ISR is to be contained in a unit, itãmust be declared in the unit's interface section as an INTERRUPT procedure.)ãAfter disabling interrupts, checking for data, discarding data if no bufferãspace is available, putting the data in the buffer if there is room, andãclearing the RDR if any data error or break occured, one must turn on theãinterrupts and issue the EOI signal to the 8259 PIC or both the 8259 PICãand the cascade PIC if IRQ7 - IRQ15 is used.  Here is a sample routine:ãããconstã  BaseAddr: array[1 .. 4] of word = ($03F8, $02F8, $03E8, $02E8);ã  { Nice array to make finding the base address easy }ããvarã  Buffer: array[1 .. 4096] of char;  { A 4k buffer for input }ã  Temp,  { Varible to hold various modem statuses }ã  CommPort: byte;  { Comm Port in use }ã  Head,  { Start of the buffer }ã  Tail,  { End of the buffer }ã  Size: word;  { Size of the buffer }ã  Cascade: boolean;  { For IRQ7 - IRQ15 }ããprocedure Async_ISR; interrupt; { NOTE: must declare the procedure interrupt }ãbeginã  inline($FB); { STI - Disable interrupts }ã  Temp := port[BaseAddr[CommPort] + $02];  { Read a byte from the IIR }ã  if Temp and $06 = $04 then  { Character received }ã  beginã    if Head <> Tail then  { Make sure there is room in the buffer }ã    beginã      Buffer[Tail] := Chr(port[BaseAddr[CommPort] + $00]);  { Read char }ã      inc(Tail);  { Position the Tail for the next char }ã      if Tail > 4096 then Tail := 0;  { If Tail is greater, wrap the buffer }ã    endã    else temp := port[BaseAddr[CommPort] + $00];  { Throw away overruns }ã  endã  else if Temp and $06 = $06 then  { Data error or break }ã    Temp := port[BaseAddr[CommPort] + $00];  { Clear RDR }ã  inline($FA);  { CLI - Enable interrupts }ã  port[$20] := $20;  { Reset the 8259 PIC }ã  if Cascade then port[$A0] := $20;  { Reset the cascade PIC }ãend;ãããFirst the procedure disables interrupts, then it reads the IIR to find outãwhat kind of interrupt needs processing.  The procedure then masks out bitsã2 and 1 and tests it to see if bit 4 is set.  If data is received it checksãto make sure there is room in the buffer, and places the character at theãposition marked by Tail, otherwise it disregards the character as overrun.ãIf a data error occured it clears the RDR to make sure no garbage isãreceived.  Finally it enables interrupts and resets the 8259 (and the cascadeãif necessary).ãããSENDING CHARACTERSã~~~~~~~~~~~~~~~~~~ã        Sending character over the modem is much simpler than getting them.ãFirst one must wait for the flow control and for the UART and then write theãcharacter to the THR.  Here's an example:ããprocedure XmitChar(C: char);  { Uses variable and constant declarations fromãbegin                           the previous example }ã  while ((port[BaseAddr[CommPort] + $05] and $20 <> $20) and  { Wait for THR }ã         (port[BaseAddr[CommPort] + $06] and $10 <> $10))  { Wait for CTS }ã  do ;  { Do nothing until CTS and THR empty }ã  port[BaseAddr[CommPort] + $00] := Ord(C);  { Send character }ãend;ããThis waits for the CTS signal and for the THR to be clear and then sends theãcharacter.  To send strings just use this in a repeat loop such as:ããfor x := 1 to length(s) doã  XmitChar(s[x]);ãããREADING CHARACTERSã~~~~~~~~~~~~~~~~~~ã        The actual reading of character takes place in the ISR, but one stillãhas to get them from the buffer.  Just read the character at the head ofãthe buffer and pass it back.  An example:ããfunction RemoteReadKey: char;  { Uses var and const from above }ãbeginã  RemoteReadKey := Buffer[Head];  { Get the character }ã  inc(Head);  { Move Head to the next character }ã  if Head > 4096 then Head := 0;  { Wrap Head around if necessary }ã  dec(Size);  { Remove the character }ãend;ããTo find out if a character is waiting is even easier:ããfunction RemoteKeyPressed: boolean;  { Uses vars and consts from above }ãbeginã  RemoteKeyPressed := Size > 0;  { A key was pressed if there is data inãend;                               the buffer }ãããINITIALIZING MODEM PARAMETERS AND OTHER TOPICSã~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ã        For most cases one can use interrupt 14h function 00h to initializeãmodem parameters, but if the baud rate is over 9600, this function willãnot work.  One must change the BRD themselves.  It is a simple matter ofãaccessing the BRD by setting the LCR bit 7 to 1 and writing to the BRD andãthen reseting the LCR bit 7 back to 0.  Everything else, clearing buffers,ãflushing buffers, formatting input, is all up to the programmer.  I haveãprovided one with enough information to grasp the basis of modem programmingãand the I/O involved.ããFLOW CONTROLã~~~~~~~~~~~~ã        Flow control is mainly used to prevent overflow error on today'sãhigh speed modems.  CTS/RTS was already covered earlier, but nothing hasãbeen said for XOn/XOff.  XOn/XOff will send a certain character (usuallyãa ^S) when the input buffer has reached a certain percentage of capacity.ãThis signal is XOff.  When the buffer has gone down to another percentage ofãcapacity, XOn (usually a ^Q) will be sent.  It is the programmer's job toãlook for XOn/XOff codes and interperate them, as there are no standard waysãto do it as with CTS/RTS.  It is also his job to make sure he or she sendsãthe signals at the appropriate time.ãããCONCLUSIONã~~~~~~~~~~ã        This text is general, and won't satisfy the needs of advanced modemãprogrammers.  It was written to help those just starting, or thinking aboutãstarting, through the ordeal of finding a book, or read through source notãknowing what some of it does.  If one finds any mistakes, please feel freeãto contact me via the Pascal FIDONet echo, and he will gladly correctãthem.  Also, if one would like more information on other related topics,ãcontact me via the Pascal echo, and I will try to help.ãã_____________________________________________________----ãããI hope everyone will find this text useful, and please feel free toãcomment or correct anything.  I posted it once but it got choped off inãplaces, so i'm posting it again.  Enjoy.ãã        Jackãã      14     08-27-9321:36ALL                      SWAG SUPPORT TEAM        How Much Memory          IMPORT              19     ~O   > Can anyone help me in determining how much stack, and heap memoryã> you need to give a TSR, does it depend on how many variables or howã> long your code is?ããYour three requirements are based on the following:ããStack Spaceã-----------ãThis is based on how deep your procedure nesting goes, whether yourãprocedures are recursive and how much they'll recurse, how large theãParameters to those procedures are, and size of local variables. Keep inãmind that you only have to cover the largest/deepest combo of everythingãto be safe, not the total. TO be safe you should leave a little extraãfor interrupts and stuff to use ($100 or so)ãI use $2000 for most programs, and $400 for my TSR's, which will hardlyãever use that much. $400 is the minimum you can declare.ãIf you do alot of recursion or put huge things on the stack, use more.ãActually, use the smallest number you can get away with without an errorãwhile stack checking is enabled.ããMinimum Heapã------------ãThis is the LEAST heap space your program can run with. Your programãABSOLUTELY HAS to have at least this much heap space when it's run.ããYou use heap space when you declare variables with New or getMem (likeãwith Turbo Vision or other objects)ããSo if you don't use any dynamic variables, you can pretty safely setãthis to 0. Otherwise, set it to a reasonable number for yourãapplication. (I mean if your database program only has memory for ONEãrecord, what use is it for it to run?)ããMaximum Heapã------------ãThis is the most heap space your program WILL reserve, even if more isãavailable. This needs to be large enough to hold all the dynamicãvariables you plan on allocating, plus a little...ããProblem with reserving it ALL is that you can no longer spawn childãprocesses with exec. So this needs to be small enough to let otherãprocesses you plan on running (dos shells) run, yet large enough so youãdon't run out of memory easily...ããThis is the toughest one to set.ããIn programs that don't use the heap, set it to 0.ããin programs that will never ever call a child process with Exec, set itãto 655360 ($100000, or all available memory)ããotherwise, your guess is as good as mine...ããI have my default set at 65536. ($10000)ãã                                                                                                        15     11-02-9306:06ALL                      ALTON PRILLAMAN          MIDI Information         SWAG9311            73     ~O   {ãALTON PRILLAMANããHOWEVER, <g> now would be a good time to learn about "Bitwise Operators"ãto accomplish your goal With minimal memory requirements.  I'll start Withãthe basics (no offense intended).  You may have heard, or remember from aãProgramming class that a Byte is made up of 8 bits.  When looking at a Byteãin binary, each bit holds a value of 0 or 1 that when put together inãtheir respective places will add up to make the number.  Here's anãexample of a Byte:ãã                               B I N A R Yã                                T A B L Eã=========================================================================ãã             Power |   7   6   5   4   3   2   1   0 | of 2ã             ------+---------------------------------+-----ã             Bit # |   8   7   6   5   4   3   2   1 |ã             ------+---------------------------------+-----ã             Value | 128  64  32  16   8   4   2   1 | HEXã             ------+---------------------------------+-----ã                0  |   0   0   0   0   0   0   0   0 | $00ã                1  |   0   0   0   0   0   0   0   1 | $01ã            *   2  |   0   0   0   0   0   0   1   0 | $02ã                3  |   0   0   0   0   0   0   1   1 | $03ã            *   4  |   0   0   0   0   0   1   0   0 | $04ã                5  |   0   0   0   0   0   1   0   1 | $05ã                6  |   0   0   0   0   0   1   1   0 | $06ã                7  |   0   0   0   0   0   1   1   1 | $07ã            *   8  |   0   0   0   0   1   0   0   0 | $08ã                9  |   0   0   0   0   1   0   0   1 | $09ã               10  |   0   0   0   0   1   0   1   0 | $0Aã               11  |   0   0   0   0   1   0   1   1 | $0Bã               12  |   0   0   0   0   1   1   0   0 | $0Cã               13  |   0   0   0   0   1   1   0   1 | $0Dã               14  |   0   0   0   0   1   1   1   0 | $0Eã               15  |   0   0   0   0   1   1   1   1 | $0Fã            *  16  |   0   0   0   1   0   0   0   0 | $10ã                   |                                 |ã            *  32  |   0   0   1   0   0   0   0   0 | $20ã            *  64  |   0   1   0   0   0   0   0   0 | $40ã            * 128  |   1   0   0   0   0   0   0   0 | $80ã                   |                                 |ã              255  |   1   1   1   1   1   1   1   1 | $FFã             ------+---------------------------------+-----ãã * = All columns to the right had filled up With 1s, so we carried to theã     next column to the left.ããNotice that when all of the "bit places" have a "1" in them, that theãtotal adds up to be 255 which is the maximum number that a Byte can hold.ãIn binary (the inner part of the Chart), "1" is the maximum value a bitãcan hold Until it carries to the next column to the left.  This brings usãto the next Chart, HEXIDECIMAL:ããã                          H E X I D E C I M A Lã                                T A B L Eã=========================================================================ãã            Power| 1   0   |of 16        Power|  1  0   |of 16ã          -------+---------+-----      -------+---------+-----ã          Decimal|         |           Decimal|         |ã            Value| 16  0   | HEX         Value| 16  0   | HEXã          -------+---------+-----      -------+---------+-----ã                0|  0  0   | $00            31|  1  1   |  $1Fã                1|  0  1   | $01         *  32|  2  0   |  $20ã                2|  0  2   | $02            33|  2  1   |  $21ã                3|  0  3   | $03              |         |ã                4|  0  4   | $04            47|  2  F   |  $2Fã                5|  0  5   | $05         *  48|  3  0   |  $30ã                6|  0  6   | $06            63|  3  F   |  $3Fã                7|  0  7   | $07         *  64|  4  0   |  $40ã                8|  0  8   | $08            79|  4  F   |  $4Fã                9|  0  9   | $09            80|  5  0   |  $50ã               10|  0  A   | $0A            95|  5  F   |  $5Fã               11|  0  B   | $0B         *  96|  6  0   |  $60ã               12|  0  C   | $0C           111|  6  F   |  $6Fã               13|  0  D   | $0D         * 112|  7  0   |  $70ã               14|  0  E   | $0E           127|  7  F   |  $7Fã               15|  0  F   | $0F         * 128|  8  0   |  $80ã           *   16|  1  0   | $10           255|  F  F   |  $FFã               17|  1  1   | $11         * 256|         |$0100ã          -------+---------+-----      -------+---------+-----ãã * = All columns to the right had filled up With 15 (F) so we carriedã     to the next column to the left.ããThe hexidecimal table is derived from BASE 16.  The value that each columnãmay hold a value of 15 (F) before we carry to the next column.  Alsoãnotice that when both columns fill up With a value of "F" ($FF) that theãresult is 255, which is the maximum For a Byte.ãããOkay, With that behind us, let's take a look at your application.  As youãmay have noticed in the binary table in the previous message, a Byte willãgive us the ability to track up to 8 bits.  Our goal here is to turn on orãoff each of the 8 bits as each channel is turned on or off.  I assume thatãyou've got 16 channels to work With, so we'll use a Word instead of aãByte.  When looked at in binary, a Word is like placing two Bytesãside-by-side.  Notice that the HEXIDECIMAL works the same way.ãã   256-------------------------+  +---------------------------- 128ã   512----------------------+  |  |  +-------------------------  64ã  1024-------------------+  |  |  |  |  +----------------------  32ã  2048----------------+  |  |  |  |  |  |  +-------------------  16ã  4096-------------+  |  |  |  |  |  |  |  |  +----------------   8ã  8192----------+  |  |  |  |  |  |  |  |  |  |  +-------------   4ã 16384-------+  |  |  |  |  |  |  |  |  |  |  |  |  +----------   2ã 32768----+  |  |  |  |  |  |  |  |  |  |  |  |  |  |  +-------   1ã          |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |ã          |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |ã Power | 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0|of 2ã-------+------------------------------------------------+-------ã Bit # | 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1|ã-------+------------------------------------------------+-------ãDecimal|                                                |ã  Value|                   BINARY                       |   HEXã-------+------------------------------------------------+-------ã      1|  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1| $0001ã      2|  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0| $0002ã      4|  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0| $0004ã      8|  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0| $0008ã     16|  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0| $0010ã     32|  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0| $0020ã     64|  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0| $0040ã    128|  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0| $0080ã    256|  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0| $0100ã    512|  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0| $0200ã   1024|  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0| $0400ã   2048|  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0| $0800ã   4096|  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0| $1000ã   8192|  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0| $2000ã  16384|  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0| $4000ã  32768|  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0| $8000ã-------+------------------------------------------------+-------ããThough it has taken us a While to get here, you now have a "value" forãeach of your midi channels.  if you need to use more than 16 channels, youãcan use the same methods applied above using a LongInt to give you a totalãof 32 channels (or bits).ããYou can now declare these as Constants in your Program like so:ã}ããProgram MidiStuff;ãConstã   {Midi Channels}ã   Ch1  = $0001;ã   Ch2  = $0002;ã   Ch3  = $0004;ã   Ch4  = $0008;ã   Ch5  = $0010;ã   Ch6  = $0020;ã   Ch7  = $0040;ã   Ch8  = $0080;ã   Ch9  = $0100;ã   Ch10 = $0200;ã   Ch11 = $0400;ã   Ch12 = $0800;ã   Ch13 = $1000;ã   Ch14 = $2000;ã   Ch15 = $4000;ã   Ch16 = $8000;ããVarã  MidiChannels : Word;ãã{ Now you can turn on or off each channel and check to see if one is set byãusing the following Procedures and Functions.  You can accomplish this byãusing the or and and operators. }ããFunction ChannelIsOn(Ch : Word) : Boolean;ãbeginã   ChannelIsOn := (MidiChannels and Ch = Ch);ãend;ããProcedure TurnOnChannel(Ch : Word);ãbeginã   MidiChannels := MidiChannels or Ch;ãend;ããProcedure TurnOffChannel(Ch : Word);ãbeginã   MidiChannels := MidiChannels and not Ch;ãend;ããbeginã   MidiChannels := $0000; {Initialize MidiChannels - No channels on!}ã   TurnOnChannel(Ch2);ã   if ChannelIsOn(Ch2) thenã     Writeln('Channel 2 is on!')ã   elseã     Writeln('Channel 2 is off!');ã   if ChannelIsOn(Ch3) thenã     Writeln('Channel 3 is on!')ã   elseã     Writeln('Channel 3 is off!');ã   TurnOnChannel(Ch16);ã   TurnOnChannel(Ch10);ã   TurnOffChannel(Ch2);ã   if ChannelIsOn(Ch2) thenã     Writeln('Channel 2 is on!')ã   elseã     Writeln('Channel 2 is off!');ãend.ãã                                                 16     11-02-9305:04ALL                      DEREK POWLES             BP7 Help file format     SWAG9311            111    ~O   {ãDerek Powles <derek.powles@eng.ox.ac.uk>ããSubject: Re Help File formatããExpanded information on Help File sources.ã   In an earlier message I referred to four authors For the book in (1)ãbelow - my mistake - there are three.ã    Derekãã(1) 'A Programmer's Guide to Turbo Vision' by Ertl, Machholz and Golgath,ãauthored by the Pascal product management team at Borland in Germany.ãPub. Addison-Wesley, ISBN 0-201-62401-X.ãThe book contains chapter 12 ( 18 pages ) describing how to add aãconText sensitive help Unit to your own TV Program.ãThe use of TVHC is described ( 2 pages ). TVHC generates helptxt.pas andãhelptxt.hlp Files from a special helptxt.txt File. The .pas File isãincluded in your main routine as a Unit. The HelpFile Unit is also needed,ãthis Unit, TVHC and a working example are in the TVDEMO subdirectory.ãThis .hlp File is not compatible With the Borland supplied Dos help Files,ãthese have a name of the form *.t*h. I believe I am correct in stating that,ãother than demohelp.hlp, all supplied *.hlp Files are For Windows use.ãã(2) The 'Borland Open Architecture Handbook For Pascal' describes andãsupplies the Program HL.EXE. Chapter 8 tells you '...how to create, build,ãand maintain Borland Dos Help Files'. I have attached information gleanedãfrom this book.ãThere are two sections, the first is information taken from the book andãthe second is a Unit holding data structures.ããlook For the *****cut here*****.ãã******************cut here*********************ã{  See `Borland Open Architecture Handbook For Pascal' pages 170-177 }ã(***************************************************************ãBinary help File format -ããRecords are grouped in four sectionsãã 1 - File stampã 2 - File signatureã 3 - File versionã 4 - Record Headersã   a - File header Recordã   b - conTextã   c - Textã   d - keyWordã   e - indexã   f - compression Recordã   g - indextags {= subheadings, = qualifiers}ãã   ***************************************************************ãã 1 - A File stamp is a null terminated String, note case,ã      `TURBO PASCAL HelpFile.\0' orã      `TURBO C Help File.\0'  identifying the File in readable form.ã      The null Character is followed by a Dos end-of-File Charcater $1A.ã      This is defined as ;STAMP in the help source File.ãã 2 - The File signature For Borland products is a null terminatedã      String `$*$* &&&&$*$' (no quotes in help Files).ã      This is defined as ;SIGNATURE in the help source File.ãã 3 - The File version is a Record of two Bytes that define the versionã      of help format and the help File Text respectively,ãã      Typeã        TPVersionRec    = Recordã          Formatversion : Byte;ã          TextVersion   : Byte   {defined With ;VERSION}ã          { this is For info only }ã        end;ã      FormatVersion For BP7     = $34ã                        TP6     = $33ã                        TP5     = $04ã                        TP4     = $02ã                        TC++3.0 = $04ãã 4 - Record Headers -ã  All the remaining Records have a common format that includes a headerã  identifying the Record's Type and length.ãã    Typeã      TPrecHdr  = Record;ã        RecType   : Byte;ã        RecLength : Word;ã      end;ãã  Field RecType is a code that identifies the Record Type.ã    Typeã      RecType =ã        (RT_FileHeader,    {0}ã         RT_ConText,       {1}ã         RT_Text,          {2}ã         RT_KeyWord,       {3}ã         RT_Index,         {4}ã         RT_Compression);  {5}ã         RT_IndexTags,     {6 - only in FormatVersion $34}ãã  Field RecLength gives the length of the contents of the Record inã  Bytes, not including the Record header. The contents begin With theã  first Byte following the header.ãã  The field `RecType' Types are explained in the following Text.ãã  Although this structure allows an arbitrary order of Records, existingã  Borland products assume a fixed Record ordering as follows:-ã        File header Recordã        compression Recordã        conText tableã        index tableã        indextags Record {introduced in BP7}ã        Text Recordã        keyWord Recordãã 4.a The File header Record defines Various parameters and optionsã  common to the help File.ãã  Typeã    TPFileHdrRec     = Recordã      Options        : Word;ã      MainIndexScreen: Word;ã      Maxscreen size : Word;ã      Height         : Byte;ã      Width          : Byte;ã      LeftMargin     : Byte;ã    end;ãã      Options - is a bitmapped field. Only one option currentlyã                supported.ã                OF_CaseSense ($0004) (C only, not Pascal)ã                  if set, index tokens are listed in mixed caseã                  in the Index Record, and index searches willã                  be Case sensitive.ã                  if cleared, index tokens are all uppercase inã                  the Index Record, and index searches will ignoreã                  case.ã                  Defined With ;CASESENSE.ãã      MainIndexScreen - this is the number assigned to the conTextã                  designated by the ;MAININDEX command in the helpã                  source File.ã                  if ;MAININDEX is not used, MainIndexScreen is setã                  to zero.ãã      MaxScreenSize - this is the number of Bytes in the longest Textã                  Record in the File (not including the header). Thisã                  field is not currently in use.ãã      Height, Width - This is the default size in rows and columns,ã                  respectively, of the display area of a help Window.ã                  Defined With ;HEIGHT and ;WIDTH.ãã      LeftMargin - Specifies the number of columns to leave blank onã                  the left edge of all rows of help Text displayed.ã                  Defined With ;LEFTMARGIN.ãã 4.b ConText table -ã  This is a table of Absolute File offsets that relates Help conTexts to theirã  associated Text. The first Word of the Record gives the number of conTextsã  in the table.ã  The remainder of the Record is a table of n ( n given by first Word) 3-Byteã  Integers (LSByte first). The table is indexed by conText number (0 to n-1).ã  The 3-Byte Integer at a given index is an Absolute Byte that is offset inã  the Help File where the Text of the associated conText begins.ã  The 3-Byte Integer is signed (2's complement).ã  Two special values are defined -ã        -1 use Index Screen Text (defined in File Header Record).ã        -2 no help available For this conText.ã  ConText table entry 0 is not used.ãã 4.c Text descriptions -ã  The Text Record defines the compressed Text of conText.ã  Text Records and keyWords appear in pairs, With one pair For eachã  conText in the File. The Text Record always precedes its associatedã  keyWord. Text Records are addressed through the File offset valuesã  found in the conText table described above.ãã  The RecLength field of the Text Record header defines the numberã  of Bytes of compressed Text in the Record.ã  The Compression Record defines how the Text is compressed.ã  if the Text is nibble encoded, and the last nibble of the last Byteã  is not used, it is set to 0.ã  Lines of Text comprising the the Text Record are stored as nullã  terminated Strings.ãã 4.d the keyWord Record defines keyWords embedded in the preceeding Textã  Record, and identifies related Text Records.ã  The Record begins With the following fixed fields:ã    UpConText   : Word;ã    DownConText : Word;ã    KeyWordCnt  : Word;ãã  UpConText and DownConText give the conText numbers of the previousã  and next sections of Text in a sequence, either may be zero,ã  indicating the end of the conText chain.ãã  KeyWordCnt gives the number of keyWords encoded in the associatedã  Text Record.ã  Immediately following this field is an Array of KeyWord Descriptorã  Records of the following form:ã    Typeã      TPKwDesc = Record;ã        KwConText: Word;ã      end;ãã  The keyWords in a Text Record are numbered from 1 to KeyWordCnt in theã  order they appear in the Text (reading left to right, top to bottom).ãã  KwConText is a conText number (index into the conText table) indicatingã  which conText to switch to if this keyWord is selected by the user.ãã 4.e Index table -ã  This is a list of index descriptors.ã  An index is a token (normally a Word or name) that has been explicitlyã  associated With a conText using the ;INDEX command in the source Text File.ã  More than one index may be associated With a conText, but any given indexã  can not be associated With more than one conText.ã  The list of index descriptors in the Index Record allows the Text of anã  index token to be mapped into its associated conText number.ãã  The first Word of the Record gives the number of indexes defined in theã  Record.ã  The remaining Bytes of the Record are grouped into index descriptors.ã  The descriptors are listed in ascending order based on the Text of the indexã  token (normal ascii collating sequence). if the OF_CaseSense flag is not setã  in the option field of the File header Record, all indexes are in uppercaseã  only.ãã  Each index descriptor Uses the following format:ã    LengthCode    : Byte;ã    UniqueChars   : Array of Byte;ã    ConTextNumber : Word;ãã  The bits of LengthCode are divided into two bit fields.ã  Bits(7..5) specify the number of Characters to carry over from the start ofã  the previous index token String.ã  Bits(4..0) specify the number of unique Characters to add to the end of theã  inherited Characters.ãã  Field UniqueChars gives the number of unique Characters to add.ã  e.g. if the previous index token is `addition', and the next index token isã  `advanced', we inherit two Characters from the previous token (ad), and addã  six Characters (vanced); thus LengthCode would be $46.ãã  ConTextNumber gives the number of the conText associated With the index.ã  This number is an index into the conText table described above.ãã 4.f A compression Record defines how the contents of Text Records areã  encoded.ã     Typeã       TPCompress = Recordã         CompType : Byte;ã         CharTable: Array[0..13] of Byte;ã       end;ãã  CompType - nibble encoding is the only compression method currentlyã             in use.ã                     Const CT_Nibble = 2;ã  The Text is encoded as a stream of nibbles. The nibbles are storedã  sequentially; the low nibble preceeds the high nibble of a Byte.ã  Nibble values ($0..$D) are direct indexes into the CharTable field ofã  the compression Record. The indexed Record is the literal Characterã  represented by the nibble. The Help Linker chooses the 14 (13?) mostã  frequent Characters For inclusion in this table. One exception is thatã  element 0 always maps to a Byte value of 0.ãã  The remaining two nibble values have special meanings:ã          Constã            NC_RawChar = $F;ã            NC_RepChar = $E;ãã  Nibble code NC_Char introduces two additional nibbles that define aã  literal Character; the least significant nibble first.ãã  Nibble code NC_RepChar defines a Repeated sequence of a single Character.ã  The next nibble gives the Repeat count less two, (counts 2 to 17 areã  possible)ã  The next nibble(s) define the Character to Repeat; it can be either aã  single nibble in the range ($0..$D) representing an index intoã  CharTable, or it can be represented by a three nibble NC_RawCharã  sequence.ããã 4.g RT_IndexTags is in FormatVersion $34 only. This provides a meansã  For including index sub headings in the help File.ã  The Record header is followed by a list of Variable length tag Recordsã  Typeã    IndRecType = Record;ã      windexNumber: Word; {index into the RT_Index Record }ã      StrLen: Byte;  {length of tag String(not including terminating 0)}ã      szTag: Array[0..0] of Char; {disable range checking}{zero term tagã                                   String}ã    end;ã   The first structure in the Array is a special entry which has theã   windexnumber set to $FFFF, and contains the default ;DESCRIPTIONã   in Case there are duplicate index entries and no tags were specified.ã*)ããUnit HelpGlobals;ã{ This File contains only the structures which are found in the help Files }ããInterfaceããConstã  Signature      = '$*$* &&&&*$';   {+ null terminator }ã  NC_RawChar     = $F;ã  NC_RepChar     = $E;ããTypeã  FileStamp      = Array [0..32] Of Char; {+ null terminator + $1A }ã  FileSignature  = Array [0..12] Of Char; {+ null terminator }ãã  TPVersion      = Recordã    FormatVersion : Byte;ã    TextVersion   : Byte;ã  end;ãã  TPRecHdr       = Recordã    RecType   : Byte; {TPRecType}ã    RecLength : Word;ã  end;ããConst   {RecType}ã  RT_FileHeader  = Byte ($0);ã  RT_ConText     = Byte ($1);ã  RT_Text        = Byte ($2);ã  RT_KeyWord     = Byte ($3);ã  RT_Index       = Byte ($4);ã  RT_Compression = Byte ($5);ã  RT_IndexTags   = Byte ($6);ãããTypeã  TPIndRecType = Recordã    windexNumber : Word;ã    StrLen       : Byte;ã    szTag        : Array [0..0] Of Char;ã    { disable range checking}ã  end;ãã  TPFileHdrRec = Recordã    Options         : Word;ã    MainIndexScreen : Word;ã    Maxscreensize   : Word;ã    Height          : Byte;ã    Width           : Byte;ã    LeftMargin      : Byte;ã  end;ããã  TP4FileHdrRec = Record                  {derived from sample File}ã    Options    : Word;ã    Height     : Byte;ã    Width      : Byte;ã    LeftMargin : Byte;ã  end;ãã  TPCompress = Recordã    CompType  : Byte;ã    CharTable : Array [0..13] Of Byte;ã  end;ãã  TPIndexDescriptor = Recordã    LengthCode    : Byte;ã    UniqueChars   : Array [0..0] Of Byte;ã    ConTextNumber : Word;ã  end;ãã  TPKeyWord = Recordã    UpConText   : Word;ã    DownConText : Word;ã    KeyWordCnt  : Word;ã  end;ãã  TPKwDesc = Recordã    KwConText : Word;ã  end;ããã  TmyStream = Object(TBufStream) end;ãã  PListRecHdr = ^RListRecHdr;ãã  RListRecHdr = Recordã    PNextHdr : PListRecHdr; {Pointer to next list element}ã    RRecType : TPRecHdr;    {RecType, RecLength}ã    PRecPtr  : Pointer;     {Pointer to copy of Record}ã  end;ãã  ConTextRecHd = Recordã    NoConTexts  : Word;ã    PConTextRec : Pointer;ã  end;ãããImplementationãend.ãã                                                         17     11-02-9306:02ALL                      FRED JOHNSON             Accessing locals in BASM SWAG9311            7      ~O   {ãRAPHAEL VANNEYãã> I've got a question about BAsm: How would I go about accessing a localã> Variable in an assembly block?  I know that locals are stored on theã> stack: Var temp:Byte;ã}ããProcedure TestLocal(Var a : Integer); Assembler;ãVarã  i    : Byte;ã  Stri : String;ãAsmã  { Getting Pointers... }ã  Push SSã  Pop  ESã  LEA  SI, i     { ES:SI points to i }ã  LEA  DI, Stri  { ...and ES:DI points to Stri }ãã  { if you Really need DS as a segment... }ã  Push DS        { Save DS }ã  Mov  AX, SS    { Copy SS to AX... }ã  Mov  DS, AX    { ...then to DS }ã  LEA  DX, Stri  { DS:DX points to Stri }ã  Pop  DS        { Restore DS }ãã  LES  DX, a     { ES:DX points to a }ãã  { Now using local Vars }ã  Inc  iã  Mov  i, 10ã  { etc... }ãend;ãã                          18     11-02-9305:45ALL                      HERBERT ZARB             GIF File format          SWAG9311            222    ~O   Herbert Zarb <panther!jaguar!hzarb@relay.iunet.it>ãã       This Text File explains the format of Gif Files.ãã---------------------------------------------------------------------------ã    G I F (tm)ã       Graphics Interchange Format (tm)ãã        A standard defining a mechanismã       For the storage and transmissionã     of raster-based Graphics informationãã          June 15, 1987ãã       (c) CompuServe Incorporated, 1987ã       All rights reservedãã     While this document is copyrighted, the informationã   contained Within is made available For use in computerã   software Without royalties, or licensing restrictions.ãã   Gif and 'Graphics Interchange Format' are trademarks ofã    CompuServe, Incorporated.ã      an H&R Block Companyãã   5000 Arlington Centre Blvd.ã      Columbus, Ohio 43220ã         (614) 457-8600ã             Page 2ããã       Graphics Interchange Format (GIF) Specificationããã        Table of Contentsãã INTRODUCTION . . . . . . . . . . . . . . . . . page 3ã GENERAL File FORMAT  . . . . . . . . . . . . . page 3ã Gif SIGNATURE  . . . . . . . . . . . . . . . . page 4ã SCREEN DESCRIPTOR  . . . . . . . . . . . . . . page 4ã GLOBAL COLOR MAP . . . . . . . . . . . . . . . page 5ã IMAGE DESCRIPTOR . . . . . . . . . . . . . . . page 6ã LOCAL COLOR MAP  . . . . . . . . . . . . . . . page 7ã RASTER DATA  . . . . . . . . . . . . . . . . . page 7ã Gif TERMINATOR . . . . . . . . . . . . . . . . page 8ã Gif EXTENSION BLOCKS . . . . . . . . . . . . . page 8ã APPendIX A - GLOSSARY  . . . . . . . . . . . . page 9ã APPendIX B - INTERACTIVE SEQUENCES . . . . . . page 10ã APPendIX C - IMAGE PACKAGING & COMPRESSION . . page 12ã APPendIX D - MULTIPLE IMAGE PROCESSING . . . . page 15ãããINTRODUCTIONãã 'GIF' (tm) is CompuServe's standard For defining generalized  colorã   raster   images.    This   'Graphics  Interchange  Format'  (tm)  allowsã   high-quality, high-resolution Graphics to be displayed on a  Variety  ofã   Graphics  hardware  and is intended as an exchange and display mechanismã   For Graphics images.  The image format described  in  this  document  isã   designed  to  support  current  and  future image technology and will inã   addition serve as a basis For future CompuServe Graphics products.ãã The main focus  of  this  document  is  to  provide  the  technicalã   information  necessary  For  a  Programmer to implement Gif encoders andã   decoders.  As such, some assumptions are made as to terminology relaventã   to Graphics and Programming in general.ãã The first section of this document describes the  Gif  data  formatã   and its components and applies to all Gif decoders, either as standaloneã   Programs or as part of  a  communications  package.   Appendix  B  is  aã   section  relavent to decoders that are part of a communications softwareã   package and describes the protocol requirements For entering and Exitingã   Gif mode, and responding to host interrogations.  A glossary in Appendixã   A defines some of the terminology used in  this  document.   Appendix  Cã   gives  a  detailed  explanation  of  how  the  Graphics  image itself isã   packaged as a series of data Bytes.ããã  Graphics Interchange Format Data Definitionããã GENERAL File FORMATãã +-----------------------+ã | +-------------------+ |ã | |   Gif Signature   | |ã | +-------------------+ |ã | +-------------------+ |ã | | Screen Descriptor | |ã | +-------------------+ |ã | +-------------------+ |ã | | Global Color Map  | |ã | +-------------------+ |ã . . .               . . .ã | +-------------------+ |    ---+ã | |  Image Descriptor | |       |ã | +-------------------+ |       |ã | +-------------------+ |       |ã | |  Local Color Map  | |       |-   Repeated 1 to n timesã | +-------------------+ |       |ã | +-------------------+ |       |ã | |    Raster Data    | |       |ã | +-------------------+ |    ---+ã . . .               . . .ã |-    Gif Terminator   -|ã +-----------------------+ããã Gif SIGNATUREãã The following Gif Signature identifies  the  data  following  as  aã   valid Gif image stream.  It consists of the following six Characters:ãã      G I F 8 7 aãã The last three Characters '87a' may be viewed as a  version  numberã   For  this  particular  Gif  definition  and will be used in general as aã   reference  in  documents  regarding  Gif  that   address   any   versionã   dependencies.ãã SCREEN DESCRIPTORãã The Screen Descriptor describes the overall parameters For all  GIFã   images  following.  It defines the overall dimensions of the image spaceã   or logical screen required, the existance of color mapping  information,ã   background  screen color, and color depth information.  This informationã   is stored in a series of 8-bit Bytes as described below.ãã       bitsã  7 6 5 4 3 2 1 0  Byte #ã +---------------+ã |               |  1ã +-Screen Width -+      Raster width in pixels (LSB first)ã |               |  2ã +---------------+ã |               |  3ã +-Screen Height-+      Raster height in pixels (LSB first)ã |               |  4ã +-+-----+-+-----+      M = 1, Global color map follows Descriptorã |M|  cr |0|pixel|  5   cr+1 = # bits of color resolutionã +-+-----+-+-----+      pixel+1 = # bits/pixel in imageã |   background  |  6   background=Color index of screen backgroundã +---------------+          (color is defined from the Global colorã |0 0 0 0 0 0 0 0|  7        map or default map if none specified)ã +---------------+ããã The logical screen width and height can both  be  larger  than  theã   physical  display.   How  images  larger  than  the physical display areã   handled is Implementation dependent and can take advantage  of  hardwareã   Characteristics  (e.g.   Macintosh scrolling Windows).  Otherwise imagesã   can be clipped to the edges of the display.ãã The value of 'pixel' also defines  the  maximum  number  of  colorsã   Within  an  image.   The  range  of  values  For 'pixel' is 0 to 7 whichã   represents 1 to 8 bits.  This translates to a range of 2 (B & W) to  256ã   colors.   Bit  3 of Word 5 is reserved For future definition and must beã   zero.ããã GLOBAL COLOR MAPãã The Global Color Map is optional but recommended For  images  whereã   accurate color rendition is desired.  The existence of this color map isã   indicated in the 'M' field of Byte 5 of the Screen Descriptor.  A  colorã   map  can  also  be associated With each image in a Gif File as describedã   later.  However this  global  map  will  normally  be  used  because  ofã   hardware  restrictions  in equipment available today.  In the individualã   Image Descriptors the 'M' flag will normally be  zero.   if  the  Globalã   Color  Map  is  present,  it's definition immediately follows the Screenã   Descriptor.   The  number  of  color  map  entries  following  a  Screenã   Descriptor  is equal to 2**(# bits per pixel), where each entry consistsã   of three Byte values representing the relative intensities of red, greenã   and blue respectively.  The structure of the Color Map block is:ãã       bitsã  7 6 5 4 3 2 1 0  Byte #ã +---------------+ã | red intensity |  1    Red value For color index 0ã +---------------+ã |green intensity|  2    Green value For color index 0ã +---------------+ã | blue intensity|  3    Blue value For color index 0ã +---------------+ã | red intensity |  4    Red value For color index 1ã +---------------+ã |green intensity|  5    Green value For color index 1ã +---------------+ã | blue intensity|  6    Blue value For color index 1ã +---------------+ã :               :       (Continues For remaining colors)ãã Each image pixel value received will be displayed according to  itsã   closest match With an available color of the display based on this colorã   map.  The color components represent a fractional intensity  value  fromã   none  (0)  to  full (255).  White would be represented as (255,255,255),ã   black as (0,0,0) and medium yellow as (180,180,0).  For display, if  theã   device  supports fewer than 8 bits per color component, the higher orderã   bits of each component are used.  In the creation of  a  Gif  color  mapã   entry  With  hardware  supporting  fewer  than 8 bits per component, theã   component values For the hardware  should  be  converted  to  the  8-bitã   format With the following calculation:ãã <map_value> = <component_value>*255/(2**<nbits> -1)ãã This assures accurate translation of colors For all  displays.   Inã   the  cases  of  creating  Gif images from hardware Without color paletteã   capability, a fixed palette should be created  based  on  the  availableã   display  colors For that hardware.  if no Global Color Map is indicated,ã   a default color map is generated internally  which  maps  each  possibleã   incoming  color  index to the same hardware color index modulo <n> whereã   <n> is the number of available hardware colors.ããã IMAGE DESCRIPTORãã The Image Descriptor defines the actual placement  and  extents  ofã   the  following  image Within the space defined in the Screen Descriptor.ã   Also defined are flags to indicate the presence of a local color  lookupã   map, and to define the pixel display sequence.  Each Image Descriptor isã   introduced by an image separator  Character.   The  role  of  the  Imageã   Separator  is simply to provide a synchronization Character to introduceã   an Image Descriptor.  This is desirable if a Gif File happens to containã   more  than  one  image.   This  Character  is defined as 0x2C hex or ','ã   (comma).  When this Character is encountered between images,  the  Imageã   Descriptor will follow immediately.ãã Any Characters encountered between the end of a previous image  andã   the image separator Character are to be ignored.  This allows future GIFã   enhancements to be present in newer image formats and yet ignored safelyã   by older software decoders.ããã       bitsã  7 6 5 4 3 2 1 0  Byte #ã +---------------+ã |0 0 1 0 1 1 0 0|  1    ',' - Image separator Characterã +---------------+ã |               |  2    Start of image in pixels from theã +-  Image Left -+       left side of the screen (LSB first)ã |               |  3ã +---------------+ã |               |  4ã +-  Image Top  -+       Start of image in pixels from theã |               |  5    top of the screen (LSB first)ã +---------------+ã |               |  6ã +- Image Width -+       Width of the image in pixels (LSB first)ã |               |  7ã +---------------+ã |               |  8ã +- Image Height-+       Height of the image in pixels (LSB first)ã |               |  9ã +-+-+-+-+-+-----+       M=0 - Use global color map, ignore 'pixel'ã |M|I|0|0|0|pixel| 10    M=1 - Local color map follows, use 'pixel'ã +-+-+-+-+-+-----+       I=0 - Image formatted in Sequential orderã    I=1 - Image formatted in Interlaced orderã    pixel+1 - # bits per pixel For this imageãã The specifications For the image position and size must be confinedã   to  the  dimensions defined by the Screen Descriptor.  On the other handã   it is not necessary that the image fill the entire screen defined.ããã LOCAL COLOR MAPãã A Local Color Map is optional and defined here For future use.   Ifã   the  'M' bit of Byte 10 of the Image Descriptor is set, then a color mapã   follows the Image Descriptor that applies only to the  following  image.ã   At the end of the image, the color map will revert to that defined afterã   the Screen Descriptor.  Note that the 'pixel' field of Byte  10  of  theã   Image  Descriptor  is used only if a Local Color Map is indicated.  Thisã   defines the parameters not only For the image pixel size, but determinesã   the  number  of color map entries that follow.  The bits per pixel valueã   will also revert to the value specified in the  Screen  Descriptor  whenã   processing of the image is complete.ãã RASTER DATAãã The format of the actual image is defined as the  series  of  pixelã   color  index  values that make up the image.  The pixels are stored leftã   to right sequentially For an image row.  By default each  image  row  isã   written  sequentially, top to bottom.  In the Case that the Interlace orã   'I' bit is set in Byte 10 of the Image Descriptor then the row order  ofã   the  image  display  follows  a  four-pass process in which the image isã   filled in by widely spaced rows.  The first pass Writes every  8th  row,ã   starting  With  the top row of the image Window.  The second pass Writesã   every 8th row starting at the fifth row from the top.   The  third  passã   Writes every 4th row starting at the third row from the top.  The fourthã   pass completes the image, writing  every  other  row,  starting  at  theã   second row from the top.  A Graphic description of this process follows:ããã   Imageã   Row  Pass 1  Pass 2  Pass 3  Pass 4          Resultã   ---------------------------------------------------ã     0  **1a**                                  **1a**ã     1                          **4a**          **4a**ã     2                  **3a**                  **3a**ã     3                          **4b**          **4b**ã     4          **2a**                          **2a**ã     5                          **4c**          **4c**ã     6                  **3b**                  **3b**ã     7                          **4d**          **4d**ã     8  **1b**                                  **1b**ã     9                          **4e**          **4e**ã    10                  **3c**                  **3c**ã    11                          **4f**          **4f**ã    12          **2b**                          **2b**ã   . . .ãããã The image pixel values are processed as a series of  color  indicesã   which  map  into the existing color map.  The resulting color value fromã   the map is what is actually displayed.  This series  of  pixel  indices,ã   the  number  of  which  is equal to image-width*image-height pixels, areã   passed to the Gif image data stream one value per pixel, compressed  andã   packaged  according  to  a  version  of the LZW compression algorithm asã   defined in Appendix C.ãã Gif TERMINATORãã In order to provide a synchronization For the termination of a  GIFã   image  File,  a  Gif  decoder  will process the end of Gif mode when theã   Character 0x3B hex or ';' is found after an image  has  been  processed.ã   By  convention  the  decoding software will pause and wait For an actionã   indicating that the user is ready to continue.  This may be  a  carriageã   return  entered  at  the  keyboard  or  a  mouse click.  For interactiveã   applications this user action must  be  passed  on  to  the  host  as  aã   carriage  return  Character  so  that the host application can continue.ã   The decoding software will then typically leave Graphics mode and resumeã   any previous process.ããã Gif EXTENSION BLOCKSãã To provide For orderly extension of the Gif definition, a mechanismã   For  defining  the  packaging  of extensions Within a Gif data stream isã   necessary.  Specific Gif extensions are to be defined and documented  byã   CompuServe in order to provide a controlled enhancement path.ãã Gif Extension Blocks are packaged in a manner similar to that  usedã   by the raster data though not compressed.  The basic structure is:ãã  7 6 5 4 3 2 1 0  Byte #ã +---------------+ã |0 0 1 0 0 0 0 1|  1       '!' - Gif Extension Block Introducerã +---------------+ã | Function code |  2       Extension Function code (0 to 255)ã +---------------+    ---+ã |  Byte count   |       |ã +---------------+       |ã :               :       +-- Repeated as many times as necessaryã |func data Bytes|       |ã :               :       |ã +---------------+    ---+ã . . .       . . .ã +---------------+ã |0 0 0 0 0 0 0 0|       zero Byte count (terminates block)ã +---------------+ãã A Gif Extension Block may immediately preceed any Image  Descriptorã   or occur before the Gif Terminator.ãã All Gif decoders must be able to recognize  the  existence  of  GIFã   Extension  Blocks  and  read past them if unable to process the Functionã   code.  This ensures that older decoders will be able to process extendedã   Gif   image   Files   in  the  future,  though  Without  the  additionalã   Functionality.ããã     GLOSSARYããPixel - The smallest picture element of a  Graphics  image.   This  usuallyã   corresponds  to  a single dot on a Graphics screen.  Image resolution isã   typically given in Units of  pixels.   For  example  a  fairly  standardã   Graphics  screen  format  is  one 320 pixels across and 200 pixels high.ã   Each pixel can  appear  as  one  of  several  colors  depending  on  theã   capabilities of the Graphics hardware.ããRaster - A horizontal row of pixels representing one line of an  image.   Aã   typical method of working With images since most hardware is oriented toã   work most efficiently in this manner.ããLSB - Least Significant Byte.  Refers to a convention For two Byte  numericã   values in which the less significant Byte of the value preceeds the moreã   significant Byte.  This convention is typical on many microcomputers.ããColor Map - The list of definitions of each color  used  in  a  Gif  image.ã   These  desired  colors are converted to available colors through a tableã   which is derived by assigning an incoming color index (from  the  image)ã   to  an  output  color  index  (of  the  hardware).   While the color mapã   definitons are specified in a Gif image, the output  pixel  colors  willã   Vary  based  on  the  hardware used and its ability to match the definedã   color.ããInterlace - The method of displaying a Gif image in which  multiple  passesã   are  made,  outputting  raster  lines  spaced  apart to provide a way ofã   visualizing the general content of an entire image  before  all  of  theã   data has been processed.ããB Protocol - A CompuServe-developed error-correcting File transfer protocolã   available  in  the  public  domain  and implemented in CompuServe VIDTEXã   products.  This error checking mechanism will be used  in  transfers  ofã   Gif images For interactive applications.ããLZW - A sophisticated data compression algorithm  based  on  work  done  byã   Lempel-Ziv  &  Welch  which  has  the feature of very efficient one-passã   encoding and decoding.  This allows the image  to  be  decompressed  andã   displayed  at  the  same  time.   The  original  article from which thisã   technique was adapted is:ãã   Terry  A.   Welch,  "A  Technique  For  High   Performance   Dataã   Compression", IEEE Computer, vol 17 no 6 (June 1984)ãã This basic algorithm is also used in the  public  domain  ARC  Fileã   compression  utilities.   The  CompuServe  adaptation  of LZW For Gif isã   described in Appendix C.ãã    Gif Sequence Exchanges For an Interactive Environmentããã The following sequences are defined For use  in  mediating  controlã   between a Gif sender and Gif receiver over an interactive communicationsã   line.  These  sequences  do  not  apply  to  applications  that  involveã   downloading  of  static  Gif  Files and are not considered part of a GIFã   File.ãã Gif CAPABILITIES ENQUIRYãã The GCE sequence is issued from a host and requests an  interactiveã   Gif  decoder  to  return  a  response  message that defines the Graphicsã   parameters For the decoder.  This involves returning  information  aboutã   available screen sizes, number of bits/color supported and the amount ofã   color detail supported.  The escape sequence For the GCE is defined as:ãã ESC [ > 0 g     (g is lower case, spaces inserted For clarity)ã    (0x1B 0x5B 0x3E 0x30 0x67)ããã Gif CAPABILITIES RESPONSEãã The Gif Capabilities Response message is returned by an interactiveã   Gif  decoder  and  defines  the  decoder's  display capabilities For allã   Graphics modes that are supported by the software.  Note that  this  canã   also include Graphics Printers as well as a monitor screen.  The generalã   format of this message is:ããã     #version;protocol{;dev, width, height, color-bits, color-res}... <CR>ãã   '#'          - GCR identifier Character (Number Sign)ã   version      - Gif format version number;  initially '87a'ã   protocol='0' - No end-to-end protocol supported by decoderã    Transfer as direct 8-bit data stream.ã   protocol='1' - Can use an error correction protocol to transfer Gif dataã        interactively from the host directly to the display.ãã   dev = '0'    - Screen parameter set followsã   dev = '1'    - Printer parameter set followsãã   width        - Maximum supported display width in pixelsã   height       - Maximum supported display height in pixelsã   color-bits   - Number of  bits  per  pixel  supported.   The  number  ofã        supported colors is therefore 2**color-bits.ã   color-res    - Number of bits  per  color  component  supported  in  theã        hardware  color  palette.   if  color-res  is  '0'  then  noã        hardware palette table is available.ããã Note that all values in the  GCR  are  returned  as  ASCII  decimalã   numbers and the message is terminated by a Carriage Return Character.ãã The  following   GCR   message   describes   three   standard   EGAã   configurations  With  no  Printer;  the Gif data stream can be processedã   Within an error correcting protocol:ãã #87a;1 ;0,320,200,4,0 ;0,640,200,2,2 ;0,640,350,4,2<CR>ããã ENTER Gif GraphICS MODEãã Two sequences are currently defined to invoke  an  interactive  GIFã   decoder into action.  The only difference between them is that differentã   output media are selected.  These sequences are:ãã     ESC [ > 1 g   Display Gif image on screenã     (0x1B 0x5B 0x3E 0x31 0x67)ãã     ESC [ > 2 g   Display image directly to an attached Graphics  Printer.ã     The  image  may optionally be displayed on the screen asã     well.ã     (0x1B 0x5B 0x3E 0x32 0x67)ãã Note that the 'g' Character terminating each sequence is  in  lowerã   case.ããã INTERACTIVE ENVIRONMENTãã The assumed environment For the transmission of Gif image data fromã   an  interactive  application  is  a  full 8-bit data stream from host toã   micro.  All 256 Character codes must be transferrable.  The establishingã   of  an 8-bit data path For communications will normally be taken care ofã   by the host application Programs.  It is however  up  to  the  receivingã   communications Programs supporting Gif to be able to receive and pass onã   all 256 8-bit codes to the Gif decoder software.ãã The Raster Data stream that represents the actual output image  canã   be represented as:ãã  7 6 5 4 3 2 1 0ã +---------------+ã |   code size   |ã +---------------+     ---+ã |blok Byte count|        |ã +---------------+        |ã :               :        +-- Repeated as many times as necessaryã |  data Bytes   |        |ã :               :        |ã +---------------+     ---+ã . . .       . . .ã +---------------+ã |0 0 0 0 0 0 0 0|       zero Byte count (terminates data stream)ã +---------------+ãã The conversion of the image from a series  of  pixel  values  to  aã   transmitted or stored Character stream involves several steps.  In briefã   these steps are:ãã   1.  Establish the Code Size -  Define  the  number  of  bits  needed  toã       represent the actual data.ãã   2.  Compress the Data - Compress the series of image pixels to a  seriesã       of compression codes.ãã   3.  Build a Series of Bytes - Take the  set  of  compression  codes  andã       convert to a String of 8-bit Bytes.ãã   4.  Package the Bytes - Package sets of Bytes into blocks  preceeded  byã       Character counts and output.ããããESTABLISH CODE SIZEãã The first Byte of the Gif Raster Data stream is a value  indicatingã   the minimum number of bits required to represent the set of actual pixelã   values.  Normally this will be the same as the  number  of  color  bits.ã   Because  of  some  algorithmic Constraints however, black & white imagesã   which have one color bit must be indicated as having a code size  of  2.ã   This  code size value also implies that the compression codes must startã   out one bit longer.ãããCOMPRESSIONãã The LZW algorithm converts a series of data values into a series ofã   codes  which may be raw values or a code designating a series of values.ã   Using Text Characters as an analogy,  the  output  code  consists  of  aã   Character or a code representing a String of Characters.ãã The LZW algorithm used in  Gif  matches  algorithmically  With  theã   standard LZW algorithm With the following differences:ãã   1.  A   special   Clear   code   is    defined    which    resets    allã       compression/decompression parameters and tables to a start-up state.ã       The value of this code is 2**<code size>.  For example if  the  codeã       size  indicated  was 4 (image was 4 bits/pixel) the Clear code valueã       would be 16 (10000 binary).  The Clear code can appear at any  pointã       in the image data stream and therefore requires the LZW algorithm toã       process succeeding codes as if  a  new  data  stream  was  starting.ã       Encoders  should output a Clear code as the first code of each imageã       data stream.ãã   2.  An end of Information code is defined that explicitly indicates  theã       end  of  the image data stream.  LZW processing terminates when thisã       code is encountered.  It must be the last code output by the encoderã       For an image.  The value of this code is <Clear code>+1.ãã   3.  The first available compression code value is <Clear code>+2.ãã   4.  The output codes are of Variable length, starting  at  <code size>+1ã       bits  per code, up to 12 bits per code.  This defines a maximum codeã       value of 4095 (hex FFF).  Whenever the LZW code value  would  exceedã       the  current  code length, the code length is increased by one.  Theã       packing/unpacking of these codes must then be altered to reflect theã       new code length.ãããBUILD 8-BIT ByteSãã Because the LZW compression  used  For  Gif  creates  a  series  ofã   Variable  length  codes, of between 3 and 12 bits each, these codes mustã   be reformed into a series of 8-bit Bytes that  will  be  the  Charactersã   actually stored or transmitted.  This provides additional compression ofã   the image.  The codes are formed into a stream of bits as if  they  wereã   packed  right to left and then picked off 8 bits at a time to be output.ã   Assuming a Character Array of 8 bits per Character and using 5 bit codesã   to be packed, an example layout would be similar to:ãã  Byte n       Byte 5   Byte 4   Byte 3   Byte 2   Byte 1ã +-.....-----+--------+--------+--------+--------+--------+ã | and so on |hhhhhggg|ggfffffe|eeeedddd|dcccccbb|bbbaaaaa|ã +-.....-----+--------+--------+--------+--------+--------+ãã Note that the physical  packing  arrangement  will  change  as  theã   number  of  bits per compression code change but the concept remains theã   same.ããPACKAGE THE ByteSãã Once the Bytes have been created, they are grouped into blocks  forã   output by preceeding each block of 0 to 255 Bytes With a Character countã   Byte.  A block With a zero Byte count terminates the Raster Data  streamã   For  a  given  image.  These blocks are what are actually output For theã   Gif image.  This block format has the side effect of allowing a decodingã   Program  the  ability to read past the actual image data if necessary byã   reading block counts and then skipping over the data.ãGraphics Interchange Format (GIF)                                   Page 15ãAppendix D - Multiple Image Processingããã Since a  Gif  data  stream  can  contain  multiple  images,  it  isã   necessary  to  describe  processing and display of such a File.  Becauseã   the image descriptor allows  For  placement  of  the  image  Within  theã   logical  screen,  it is possible to define a sequence of images that mayã   each be a partial screen, but in total  fill  the  entire  screen.   Theã   guidelines For handling the multiple image situation are:ãã   1.  There is no pause between images.  Each is processed immediately  asã       seen by the decoder.ãã   2.  Each image explicitly overWrites any image  already  on  the  screenã       inside  of  its Window.  The only screen clears are at the beginningã       and end of the  Gif  image  process.   See  discussion  on  the  GIFã       terminator.ãã                                                                                                                             19     11-02-9306:07ALL                      JOHN GUILLORY            More MIDI Information    SWAG9311            25     ~O   {ãJOHN GUILLORYãã> - the UART 8250, the chip which is their I/O controllerãI don't think they use an 8250 UART chip, yet they are similar.  the MIDIãUART is supposed to be faster than the 8250.ãã> - the MIDI protocol ( as far as there is one ... )ãBasically it's like this:  Commands start at 80h the lower 4 bits (nibble)ãdesignate the channel number such that 0 = Channel 1, 0f = 16.  Once aãcommand is given, it is assumed that that command is in effect Until anotherãcommand has been given. eg.ãã$C0 is a command to change the Program number, and I think 80 is note on.ãif so, an example from the MIDI would be:ãã80 33 60 80 33 00 C0 02 80 33 60 80 33 00ããwhere the 33 is the note to play For the command 80h and 60 is theãpressure/volume (non-presure sensitive keyboards send 64 For the pressure)ãNotes are in the order of a keyboard e.g. 0 would tech. be the first C onãthe keyboard, 1 would be a C sharp, 2 = D ..., Although most keyboardsã(unless they're their enormous) start w/Middle C at around 36 or so, and anyãkey above middle C is that much above 36, any key below middle C is thatãmuch below....  in MIDI, you can add an octive to a note by adding 12,ãsubtract 12 to lower it an octive....ããTo setup keyboards, you can send a System Request command (think its F0 orãsomething like that...) then an ID and a series of Bytes.  The ID Designatesãthe manufacturer of the keyboard, such that only the devices With that IDãwill respond to that event.ããSeek the Electronic Musician Magazine, May 1989 I'm told gives an article onãhandling the MPU-401 interrupts, as well as lots of source code that I usedãto have on the MPU-401 seems to come from this magazine.ãããThe MIDI is quite easy to Program compared to the Sound blaster where youãhave to count of so many clock-tick's etc. the MPU-401 is pretty much aã'check and see if we can send, then do it...' Type card.  Certain commandsãdo however take a little time For the devices to process eg. change a Programã# it takes so many ms. For that device to be ready For another command, playãa note, a few ms. before the next one...ããThis can become frustation before you learn how to use it...  (I never couldãfind out why it'd change the first Program # but none of the rest...<grin>)ããI/O Address 330h is the Std. (though can change on some MPU-401's) I/O Portãfor Data. I/O Address 331 is the Status/Comport.ããReading the Status port (331h) and masking 80h will tell you if something isãwaiting to be received from the mpu-401. e.g.ã}ããFunction Receive_MPU(Var B : Byte) : Boolean;ãbeginã  if (Port[$331] and $80) = 0 thenã  beginã    B := Port[$330];ã    Receive_MPU := True;ã  endã  elseã    Receive_MPU := False;ãend;ãã{ãTo Send a Command to the MPU, you must wait till there's no data in theãbuffer... The original code I used to have would flush the data if it wasãfor some reason present when you'd send a Byte...  here's a rough example ofãsending data...ã}ããProcedure Send_MPU(B : Byte);ãbeginã  Repeat Until (Port[$331] and $80) = 0;ã  Port[$330] := B;ãend;ãã                                          20     11-02-9306:12ALL                      MARK OUELLET             Reading Array from REGS  SWAG9311            10     ~O   {ãMARK OUELLETãã>  How  can  I  read what appears to be an Array from the Registers valueã>  (this is after  making  the  interrupt  call,  and  is  returned  Withã>  information...   I'll   be   durned   if   I  know  how  to  use  it):ãã> values upon returnã> AX    = clear on successful (or whatever ... not important)ã> ES:DX = see table 2.1ã>ã> table 2.1ã> offset - info (size)ã> -----------------------------ã> 00h    - blah blah (4 Bytes)ã> 03h    - blah blah (16 Bytes)ã> etc ....ã>ã> And the ES:DX usually points to what appears to be a Record, or a bufferã> of data using an offset to identify what's what.  How can I use and/orã> access this info?ã}ãã Typeã    TablePtr = ^Tableã    Table = Recordã      BlahBlah1 : LongInt; { 4Bytes }ã      BlahBlah2 : Array[1..16] of Byte;ã      .ã      .ã      etc....ã    end;ã{ã    if using Intr() or MSDos() and the Registers  structure  defined  inãDos.tpu then:ã}ãVarã  Regs    : Registers;   {Defined in Dos.tpu}ã  MyTable : TablePtr;ããbeginã  Regs.AX := ??;ã  Regs.BX := ??;ã  Intr(Regs);ã  TablePtr := Ptr(Regs.ES, Regs.DX);ãã  Write(TablePtr^.BlahBlah1);ã  .ã  .ã  etc...ããã                     21     11-02-9305:27ALL                      ROBERT ROTHENBURG        Ins and Outs of Compress SWAG9311            100    ~O   {ãRobert RothenburgããOk, since a few people have requested info about compression routines Iãthought some actual descriptions of the algorithm(s) involved would be aãgood idea.  Rather than dig out someone else's text file or mail (whichãmight be copyrighted or arcane) I'll try my hand at explaining a fewãmethods.ããIt seems better that programmers have at least a rudimentary understandingãof the algortihms if they plan on using (or not using :) them.ããDISCLAIMER: Please pardon any innacuracies: What I know is based on otherã  people's mail, text files or from what I've gleaned from spending a fewã  hours at the library (adivce: your local college research-library is aã  wonderful resource of algorithms and source-code. Old magazines as wellã  as academic papers like the IEEE Transactions are worth examining).ãããIn this insanely long post:ããI.   "Garbage Collection"ãII.  "Keywords"ãIII. Run-Length Encoding (RLE)ãIV.  Static and Dynamic Huffmann Codes  <--(BTW, is it one or two n's?)ãV.   Lampev-Ziv (LZ)ãVI.  Lampev-Ziv-Welch (LZW) et al.ãããI.  "Garbage Collection"ãã  The simplest methods of compression in weeding out unnecessary data,ã  especially from text files.  Spaces at the end of a line, or extraã  carriage returns at the end of a file. (Especially with MS-DOS textã  files, which use a Carriage Return *and* Line Feed--many editors andã  file-browsers do not need both. Eliminating one of them can cut a largeã  text file's size by a few percent!)ãã  I've also seen some utilities that "clean up" the headers of EXEã  files (such as UNP) by eliminating `unnecessary' info.  Otherã  utilities will clean up graphics files so that they'll compressã  better.ãã  In fact, removing excess "garbage" from any file will probablyã  improve its compressability.ããII. "Keywords"ãã  Another way to compress text files is to use a "keyword" for each word.ãã  I.E. we can assume most text files will have less than 65,536 differentã  words (16 bits=two bytes), and thus we can assign a different value forã  each word: since most words are longer than three letters (more than twoã  bytes), we will save space in the file. However, we'll need some form ofã  look-up table for each keyword--one way around this might be to use aã  reference to a standard dictionary file that is included with an operatingã  system or word processor.ãã  (This has other advantages as well. Many BASIC interpreters will storeã  commands like PRINT or INPUT as a character code rather than the entireã  name in ASCII text.  Not only does this save memory but improves theã  run-speed of the program.)ãã  This method can be adapted to other (non-text) files, of course.ããIII. Run-Length Encoding (RLE)ãã  With RLE, repeated characters (such as leading spaces in text, or largeã  areas of one color in graphics) are expressed with a code noting whichã  byte is repeated and how many times it's repeated.ããIV.  Static and Dynamic Huffmann Codesãã  The logic behind this method is that certain characters occur more oftenã  than others, and thus the more common characters can be expressed withã  fewer bits. For example, take a text file: 'e' might be the most commonã  character, then 'a', then 's', then 'i', etc....ãã  We can express these characters in a bit-stream like so:ãã                'e' = 1ã                'a' = 01         <--These are Static Huffmann Codes.ã                's' = 001ã                'i' = 0001ã                    etc...ãã  Since these characters normally take up 8-bits, the first (most common)ã  seven will save space when expressed this way, if they occur often enoughã  in relation to the other 249 characters.ãã  This is often represented as a (Static) Huffmann Tree:ãã                        /\              Note that a compression routineã                 'e' = 1  0             will have to first scan the dataã                         / \            and count which characters areã                  'a' = 1   0           more common and assign the codesã                           /  \         appropriately.ã                   etc... 1    0ã                              /  \ã                                etc...ãã  Notice that if we use the full 256 ASCII characters we'll run intoã  a problem with long strings of 0's.  We can get around that by usingã  RLE (Which is what the compressor SQUEEZE uses).ãã  Again, since most files use a large range of the character set, andã  their occurences are much more "even", we can use use Dynamic Huffmannã  Codes as an alternative.  They are like their static cousins, only afterã  the string of n zeros they are followed by n (binary) digits:ãã                1        = 1                             1 characterã               01x       = 010, 011                      2 charsã              001xx      = 00100, 00101, 00110, 0011     4 charsã             0001xxx     = 0001000, 0001001 ... 0001111  8 charsã                    etc...ãã  As you can guess, the Huffmann Tree would look something like:ãã                      /\            It's a bit too complicated toã                     1  0           express with ASCII <g>ã                       / \ã                      1    0ã                     / \   /\ã                    1   0    etc...ãã  Huffmann Coding is based on how often an item (such as an ASCIIã  character) occurs, and not where or in relation to other items.ã  It's not the msot efficient mothod, but it is one of the simplest.ã  One only needs to make an initial pass to count the characters andã  then re-pass translating them into the appropriate codes. Noã  pattern-matching or search routines are required.ããV.  Lampev-Ziv (LZ) Encoding.ãã  This method _does_ require some fast pattern-matching/search routines.ã  It takes advantage of the fact that in most files (especially text)ã  whole strings of characters repeat themselves.ãã  Take this sample line of text:ãã      "THAT WHICH IS, IS. THAT WHICH IS NOT, IS NOT. IS IT? IT IS!"ãã  (Ok, so "Flowers for Algernon" was on TV the other night... :)ã  With LZ, we read in from the beginning of the file and keep addingã  groups ("Windows") of characters that have not already occurred.ã  So we add the first sentence, then get to the second "IS"...insteadã  of repeating it we note that it's a duplicate, with a referenceã  to where the original occurred and how long the string is. (I.E. we noteã  that the "IS" occurred 4 characters previously, and is two charactersã  long.)ãã  This method can be tweaked by using a "Sliding Window" approach where theã  largest matches are found...we can compress the second "IS" with the first,ã  but when don't gain much.  However the two "IS NOT"s, when matched againstã  each other, would compress better.ãã  Our compressed file will have two types of data: the raw characters andã  the LZ-references (containing the pointer and length) to the rawã  characters, or even to other LZ-references.ãã  One way to tweak this further is to compress the LZ-References usingã  Huffmann codes. (I think this is what's done with the ZIP algorithm.)ããVI.  Lampev-Ziv-Welch (LZW) -- Not to be confused with LZ compression.ãã  This is the method used by Unix COMPRESS, as well as the GIF-graphicsã  file format.ãã  Like LZ, LZW compression can compress a stream of data on one-passã  relatively quickly (assuming you've got the fast search routines!).ã  However, LZW uses a hash table (essentially it's an array of strings,ã  also known as a string table), and a "match" is expressed as its indexã  in the hash table rather than a reference to where and how long theã  match is.ãã  The input stream is read in, strings are assembled and sent out whenã  they are already in the hash table, or they are added to the hash tableã  and sent out in such a way that a decoder can still re-assemble theã  file.ãã  Needless to say, this method is a memory hog.  Most compressors thatã  use this method are usually limited to a certain number of entriesã  in the hash-table (12- or 16-bits, or 4096 and 65536 respectively).ãã  Here's an outline of the LZW Compression Algorithm:ãã         0. a. Initialize the hash table. (That means for each possibleã               character there is one "root" entry in the table. Someã               flavors of LZW may actually have a "null" non-characterã               at the base of the table.)ã            b. Initialize the "current" string (S) to null.ãã         1. Read a character (K) from the input stream.ã            (If there are none left, then we're done, of course.)ãã         2. Is the string S+K in the string-table?ãã         3. If yes, then a. S := S+Kã                         b. Go to Step 1.ãã            If no, then  a. add S+K to the string table.ã                         b. output the code for Sã                         c. S := Kã                         d. Go to Step 1.ãã  Decompression is very similar.ãã         0. a. Initialize the string table (the same as with compression).ã            b. Read the first code from the compressed file into Cã            c. Then output the equivalent string for code Cã            d. Let code O := code C (The code, not the string!)ãã         1. Read the next code from the input stream into Cãã         2. Does code C exist in the string/hash table?ãã         3. If yes, then a. output the string for code Cã                         b. S := equivalent string for code Oã                         c. K := first character of the string for code Cã                         d. add S+K to the string tableãã            If no, then  a. S := equivalent string for code Oã                         b. K := first character of Sã                         c. output S+Kã                         d. add S+K to the tableãã         4. code O := code Cãã         5. Go to Step 1.ãã  It may seem psychotic at first, but it works.  Just keep reading it andã  thinking about it.  The best way is with a pencil and pad experimentingã  with a character set of four characters (A, B, C, and D) and a "word"ã  like "ABACADABA" and following through the steps.ãã  (An actual walk-through is not included here, since it will take upã  *way* too much space.  I recommend getting further, more detailedã  descriptions of LZW if you plan on writing an implementation.)ãã  One important thing is to *not* confuse between the `code' for a stringã  (it's index in the hash table) and the string itself.ãã  Two points about LZW implementations:ãã      1. The code stream (i.e. the compressed output) is not usuallyã         a set number of bits, but reflects how many entries are inã         the string table.  This is another way to further compressã         the data.ãã         Usually, LZW schemes will output the code in the number of bitsã         exactly reflecting the string table.  Thus for the first 512ã         codes the output is 9-bits.  Once the 513th string is added toã         the table, it's 10-bits and so on.ãã         Some implementations will use a constant output until a thresholdã         is reached.  For example, the code output will always be 12-bitsã         until the 4097th string is added, then the code output is inã         16-bit segments etc...ãã         If these scenarios are used, the decompression routine *must*ã         "think ahead" so as to read in the proper number of bits fromã         the encoded data.ãã      2. Because full string tables are incredible memory hogs, manyã         flavors of LZW will use a "hash" table (see, string and hashã         table aren't quite the same).ãã         Instead of a string of characters like "ABC", there'll beã         a table containing the last character of the string, and aã         reference to the previous character(s).  Thus we'll haveã         "A", "[A]B" and "[[A]B]C" where [x] is the reference (or theã         actual "code") for that character/string.  Using this methodã         may be slower, but it saves memory and makes the implement-ã         ation a little easier.ã  There are many variations on LZW using other sets of strings toã  compare with the string table--this is based on the assumption thatã  the more entries there are in the table, the more efficient theã  compression will be.ãã  One variation (Lampev-Ziv-Yakoo) would add the ButFirst(S+K) everyã  time S+K was added. "ButFirst" means all characters but the firstã  one. So ButFirst("ABCD") = "BCD".ãããAnyhow, those are the compression algorithms that I know about which Iãcan even remotely attempt to explain.ãI apologize for any (glaring?) mistakes. It's late...what started as aãmeaningless reply to somebody asking something about SQUEEZE explodedãinto this post.  I hope it's useful to anybody interested.ã}ã                                          22     11-26-9317:05ALL                      VARIOUS                  FAQ - Modems             SWAG9311            375    ~O   ãThis is a FAQ answer on serial communications using the TTY protocol. Itãcontains information on the TTY protocol and hardware and software implemen-ãtations on IBM PCs which is derived from National Semiconductor data sheets.ããPART ONE - HARDWARE & SOFTWAREãããAcknowledgementsã================ãã  The following persons have contributed (directly or indirectly :-) to thisãsummary:ã      Madis Kaal <mast@anubis.kbfi.ee ??>     this address is known to be badã      Steve Poulsen <stevep@ims.com>ã      Scott C. Sadow <NS16550A@mycro.UUCP>ã      Dan Norstedt <?>ã        [Commercial: This line could display YOUR name!]ããããIntroductionã============ãã  One of the most universal parts of the PC is its serial port. You canãconnect a mouse, a modem, a printer, a plotter, another PC, ...ã  But its usage (both software and hardware) is one of the best-kept secretsãfor most users, besides that it is not difficult to understand how toãconnect (not plug-in) devices to it and how to program it.ã  Regard this FAQ as a manual of the serial port of your PC for bothãhardware and software.ãããHistorical summaryã------------------ãã  In early days of telecommunications, errand-boys and optical signals (flags,ãlights, clouds of smoke) were the only methods of transmitting informationãacross long distances. With increasing requirements on speed and growingãamount of information, more practical methods were developed. One milestoneãwas the first wire-bound transmission on May 24th, 1844 ("What hath Godãwrought", using the famous Morse-alphabet). Well, technology improved a bit,ãand soon there were machines that could be used like typewriters, except thatãyou typed not only on your own piece of paper but also on somebody elses.ãThe only thing that has changed on the step from the teletyper to your PCãis speed.ãããThe TTY (teletyping) protocolã-----------------------------ãã  Definition: A protocol is a clear description of the LOGICAL method ofãtransmitting information. This does NOT include physical realisation.ãã  The TTYp uses two different states of the line called 'mark' and 'space'.ãIf no data is transmitted, the line is in the 'space' state. Data looksãlikeãã      space  ----------+   +-------+   +---+   +-------ã                       |   |       |   |   |   |ã      mark             +---+       +---+   +---+ãã                        (1)  --------(2)-------- (3)ãã  (1) start bit   (2) data bits   (3) stop bit(s)ãã  Both transmitter (TX) and receiver (RX) use the same data rate (measuredãin baud, which is the reciprocal value of the smallest time interval betweenãtwo changes of the line state. TX and RX know about the number of dataãbits (probably with a parity bit added), and both know about the size ofãthe stop step (called the stop bit or the stop bits, depending on the sizeãof the stop step; normally 1, 1.5 or 2 times the size of a data bit). Dataãis transmitted bit-synchroneously and word-asynchroneously, which means thatãthe size of the bits, the length of the word etc.pp. is clearly definedãbut the time between two words is undefined.ã  The start bit indicates the beginning of a new data word. It is used toãsynchronize transmitter and receiver.ã  Data is transmitted LSB to MSB, which means that the least significantãbit (Bit 0) is transmitted first with 4 to 7 bits of data following, re-ãsulting in 5 to 8 bits of data. A logical '0' is transmitted by theã'space' state of the line, a logical '1' by 'mark'.ã  A parity bit can be added to the data bits to allow error detection.ãThere are two (well, actually five) kinds of parity: odd and even (plusãnone, mark and space). Odd parity means that the number of 'mark' steps inãthe data word (including parity bit) is always odd, so the parity bit isãset accordingly (I don't have to explain 'even' parity, must I?). It isãalso possible to set the parity bit to a fixed state or to omit it.ã  The stop bit does not indicate the end of the word (as it could beãderived from its name); it rather separates two consecutive words byãputting the line into the 'space' state for a minimum time.ã  The protocol is usually described by a sequence of numbers and letters,ãe.g. 8n1 means 1 start bit (always), 8 bits of data, no parity bit, 1 stopãbit. 7e2 would indicate 7 bits of data, even parity, 2 stop bits (but I'veãnever seen this one...). The usual thing is 8n1 or 7e1.ã  Early teletypers used the neckbreaking speed of 50 baud (which meansãthat one step is 20ms), 5 bits of data, no parity and 1.5 stop bits (don'tãask me why!). Your PC is capable of serial transmission at up to 115,200ãbaud (step size of 8.68 microseconds!). Typical rates are 300 baud, 1200 baud,ã2400 baud and 9600 baud.ãããThe physical transmissionã-------------------------ãã  Teletypers used a closed-loop line with a space current of 20ma and aãmark current of 0ma (typical), which allowed to detect a 'broken line'.ãThe RS232C port of your PC uses voltages rather than currents to indicateãlogical states: 'space' is signaled by +3v to +15v (typically +12v), 'mark'ãby -3v to -15v (typically -12V). The typical output impedance of the serialãport of a PC is 2 kiloohms (resulting in about 5ma @ 10v), the typical inputãimpedance is about 4.3 kiloohms, so there should be a maximum fan-out of 5ã(5 inputs can be connected to 1 output). Please don't rely on this, it mayãdiffer from PC to PC.ã  Three lines (RX, TX & ground) are needed.ããQ. Why does my PC have a 25pin/9pin connector if there are only 3 linesã   needed?ãA. There are several status lines that are only used with a modem. See theã   software section of this FAQ.ããQ. How can I easily connect two PCs by a three-wire lead?ãA. This connection is called a 'null-modem' connection. RX1 is connectedã   to TX2 and vice versa, GND1 to GND2. In addition to this, connect RTSã   to CTS & DCD and DTR to DSR (modem software often relies on that). Seeã   the hardware section for further details.ãããHardwareã========ãããThe connectorsã--------------ãã  PCs have 9pin/25pin male SUB-D connectors. The pin layout is as followsã(looking at the back side of your PC):ãã        1                         13             1               5ã      _______________________________      _______________ã      \  . . . . . . . . . . . . .  /      \  . . . . .  /ã       \  . . . . . . . . . . . .  /            \  . . . .  /ã        ---------------------------          -----------ã        14                      25            6       9ãã Name (V24)  25pin  9pin  Dir  Full name               Remarksã--------------------------------------------------------------------------ã    TxD         2     2    o   Transmit Dataã    RxD         3     3    i   Receive Dataã    RTS         4     5    o   Request To Sendã    CTS         5     8    i   Clear To Sendã    DTR        20     4    o   Data Terminal Readyã    DSR         6     6    i   Data Set Readyã    RI         22     9    i   Ring Indicatorã    DCD         8     1    i   Data Carrier Detectã    GND         7     5    -   Signal groundã     -          1     -    -   Protective ground       Don't use this one!ã    SCTE       24     -    -   Sync. clock trans. end  Several PCs onlyã    SCT        15     -    o   Sync. clock TX          dito.ã    SCR        17     -    i   Sync. clock RX          dito.ãã  The most important lines are RxD, TxD, and GND. Others are used withãmodems, printers and plotters to indicate internal states or to useãsynchroneous transmission (this is rarely used, and most PCs don't supportãit).ã  '0' means +3v to +15V, '1' means -3v to -15v. '1' is the active state.ãã  The lines are:ãã  RxD, TxD: These lines carry the data.ã  RTS, CTS: Are used by the PC and the modem/printer/whatsoever (furtherã    on referred to as the data set) to start/stop a communication. The PCã    sets RTS to its active state ('1'), and the data set responds with CTSã    '1' (always in this order). If the data set wants to stop/interrupt theã    communication (e.g. buffer overflow), it drops CTS to '0'; the PC usesã    RTS to control the data flow.ã  DTR, DSR: Are used to establish a connection at the very beginning, i.e.ã    the PC and the data set 'shake hands' first to assure they are bothã    present. The PC sets DTR to '1', and the data set answers with DSRã    '1'. Modems often indicate hang-up by resetting DSR to '0'.ã  (These six lines plus GND are often referred to as '7 wire'-connection orã  'hand shake'-connection.)ã  DCD: The modem uses this line to indicate that it has detected theã    carrier of the modem on the other side of the line.ã  RI: The modem uses this line to signal that 'the phone rings' (even ifã    there isn't a bell fitted to your modem).ã  SCTE, SCT, SCR: forget about these.ã  Protective ground: This line is connected to the power ground of theã    serial adapter. It should not be used as a signal ground, and itã    MUST NOT be connected to GND (even if your DMM shows up aã    connection!). Connect this line to the screen of the lead (if there isã    one).ãã  Technical data (typical):ãã    Signal level: -10.5v/+11vã    Short circuit current: 6.8ma  (yes, that's enough for your mouse!)ã    Output impedance: 2 kiloohmsã    Input impedance: 4.3 kiloohmsãããConnecting devicesã------------------ãã  Normally, a 7 wire connection is used. Connect:ã        GND1    to    GND2ã        RxD1    to    TxD2ã        TxD1    to    RxD2ã        DTR1    to    DSR2ã        DSR1    to    DTR2ã        RTS1    to    CTS2ã        CTS1    to    RTS2ã  If a modem is connected, add lines for the following:ã        RI, DCDã  If software wants it, connect DCD1 to CTS1 and DCD2 to CTS2.ã  BEWARE! While PCs use pin 2 for RxD and pin 3 for TxD, modems normallyãhave those pins reversed! This allows to easily connect pin1 to pin1, pin2ãto pin 2 etc. If you connect two PCs, cross RxD and TxD.ãã  If hardware handshaking is not needed, a so-called null-modem connectionãcan be used. Connect:ã        GND1    to    GND2ã        RxD1    to    TxD2ã        TxD1    to    RxD2ãAdditionally, connect (if software needs it):ã        RTS1    to    CTS1 & DCD1ã        RTS2    to    CTS2 & DCD2ã        DTR1    to    DSR1ã        DTR2    to    DSR2ãYou won't need long wires for these!ã  The null-modem connection is used to establish an XON/XOFF-transmissionãbetween two PCs (see software section for details).ã  Remember: the names DTR, DSR, CTS & RTS refer to the lines as seen fromãthe PC. This means that for your data set DTR & RTS are incoming signalsãand DSR & CTS are outputs!ãããBase addresses & interruptsã---------------------------ãã  Normally, the following list is correct:ããã    Port     Base address    Int #ãã    COM1         0x3F8        0xCã    COM2         0x2F8        0xBã    COM3         0x3E8        0xCã    COM4         0x2E8        0xBããã  In PCs, serial communication is realized with a set of three chipsã(there are no further components needed!): a UART (Universal AsynchroneousãReceiver/Transmitter) and two line drivers. Normally, the 82450/16450/8250ãdoes the 'brain work' while the 1488 and 1489 drive the lines.ã  The chips are produced by many manufacturers; it's of no importanceãwhich letters are printed in front of the numbers (mostly NS for NationalãSemiconductor). Don't regard the letters behind the number also; they justãindicate special features and packaging (Advanced, FIFO, New, MILitary,ãbug fixes [see below] etc.).ã  You might have heard of the possibility to replace the 16450 by a 16550Aãto improve reliability and software throughput. This is only useful if yourãsoftware is able to use the FIFO (first in-first out) buffer features. Theãchips are fully pin-compatible except for two pins that are not used byãany board known to the author: pin 24 (CSOUT, chip select out) and pin 29ã(NC, no connection to be made). With the 16550A, pin 24 is -TXRDY and pinã29 is -RXRDY, signals that aren't needed and that even won't care if theyãare shorted to +5v or ground. Therefore it should always be possible toãsimply replace the 16450 by the 16550A - even if it's not always useful dueãto lacking software capabilities. IT IS DEFINITELY NOT NECESSARY FORãCOMMUNICATION UP TO LOUSY 9600 BAUD! These rates can easily be handled byãany CPU and the interrupt-driven communication won't slow down the computerãsubstantially. But if you want to use high-speed transfer with or withoutãusing the interrupt features (i.e. by 'polling'), it is recommended to useãthe 16550A in order to make transmission more reliable if your softwareãsupports it (see excursion some pages below).ãããHow to detect which chip is usedã--------------------------------ãã  This is really not difficult. The 8250 has no scratch register (see dataãsheet info below), the 16450/82450 has no FIFO, the 16550 has no workingãFIFO :-) and the 16550A performs alright. See the software section forãan example.ãããData sheet informationã----------------------ãã  Some hardware information taken from the data sheet of NationalãSemiconductor (shortened and commented):ãã  Pin description of the 16450(16550A) [Dual-In-Line package]:ãã                   +-----+ +-----+ã               D0 -|  1  +-+   40|- VCCã               D1 -|  2        39|- -RIã               D2 -|  3        38|- -DCDã               D3 -|  4        37|- -DSRã               D4 -|  5        36|- -CTSã               D5 -|  6        35|- MRã               D6 -|  7        34|- -OUT1ã               D7 -|  8        33|- -DTRã             RCLK -|  9        32|- -RTSã              SIN -| 10        31|- -OUT2ã             SOUT -| 11        30|- INTRã              CS0 -| 12        29|- NC (-RXRDY)ã              CS1 -| 13        28|- A0ã             -CS2 -| 14        27|- A1ã         -BAUDOUT -| 15        26|- A2ã              XIN -| 16        25|- -ADSã             XOUT -| 17        24|- CSOUT (-TXRDY)ã              -WR -| 18        23|- DDISã               WR -| 19        22|- RDã              VSS -| 20        21|- -RDã                   +-------------+ããA0, A1, A2, Register Select, Pins 26-28:ãAddress signals connected to these 3 inputs select a UART register forãthe CPU to read from or to write to during data transfer. A table ofãregisters and their addresses is shown below. Note that the state of theãDivisor Latch Access Bit (DLAB), which is the most significant bit of theãLine Control Register, affects the selection of certain UART registers.ãThe DLAB must be set high by the system software to access the BaudãGenerator Divisor Latches.ãã  DLAB  A2  A1  A0    Registerã    0    0   0   0    Receive Buffer (read) Transmitter Holding Reg. (write)ã    0    0   0   1    Interrupt Enableã    x    0   1   0    Interrupt Identification (read)ã    x    0   1   0    FIFO Control (write) (undefined on the 16450. CB)ã    x    0   1   1    Line Controlã    x    1   0   0    Modem Controlã    x    1   0   1    Line Statusã    x    1   1   0    Modem Statusã    x    1   1   1    Scratch (special use on some boards. CB)ã    1    0   0   0    Divisor Latch (LSB)ã    1    0   0   1    Divisor Latch (MSB)ãã-ADS, Address Strobe, Pin 25: The positive edge of an active AddressãStrobe (-ADS) signal latches the Register Select (A0, A1, A2) and ChipãSelect (CS0, CS1, -CS2) signals.ãNote: An active -ADS input is required when Register Select and ChipãSelect signals are not stable for the duration of a read or writeãoperation. If not required, tie the -ADS input permanently low. (As it isãdone in your PC. CB)ãã-BAUDOUT, Baud Out, Pin 15: This is the 16 x clock signal from theãtransmitter section of the UART. The clock rate is equal to the mainãreference oscillator frequency divided by the specified divisor in theãBaud Generator Divisor Latches. The -BAUDOUT may also be used for theãreceiver section by tying this output to the RCLK input of the chip. (Yep,ãthat's true for your PC. CB).ããCS0, CS1, -CS2, Chip Select, Pins 12-14: When CS0 and CS1 are high and CS2ãis low, the chip is selected. This enables communication between the UARTãand the CPU.ãã-CTS, Clear To Send, Pin 36: When low, this indicates that the modem orãdata set is ready to exchange data. This signal can be tested by readingãbit 4 of the MSR. Bit 4 is the complement of this signal, and Bit 0 is '1'ãif -CTS has changed state since the previous reading (bit0=1 generates anãinterrupt if the modem status interrupt has been enabled).ããD0-D7, Data Bus, Pins 1-8: Connected to the data bus of the CPU.ãã-DCD, Data Carrier Detect, Pin 38: blah blah blah, can be tested byãreading bit 7 / bit 3 of the MSR. Same text as -CTS.ããDDIS, Driver Disable, Pin 23: This goes low whenever the CPU is readingãdata from the UART.ãã-DSR, Data Set Ready, Pin 37: blah, blah, blah, bit 5 / bit 1 of MSR.ãã-DTR, Data Terminal Ready, Pin 33: can be set active low by programmingãbit 0 of the MCR '1'. Loop mode operation holds this signal in itsãinactive state.ããINTR, Interrupt, Pin 30: goes high when an interrupt is requested by theãUART. Reset low by the MR.ããMR, Master Reset, Pin 35: Schmitt Trigger input, resets internal registersãto their initial values (see below).ãã-OUT1, Out 1, Pin 34: user-designated output, can be set low byãprogramming bit 2 of the MCR '1' and vice versa. Loop mode operation holdsãthis signal inactive high.ãã-OUT2, Out 2, Pin 31: blah blah blah, bit 3. (Used in your PC to connectãthe UART to the interrupt line of the slot when '1'. CB)ããRCLK, Receiver Clock, Pin 9: This input is the 16 x baud rate clock forãthe receiver section of the chip. (Normally connected to -BAUDOUT, as inãyour PC. CB)ããRD, -RD, Read, Pins 22 and 21: When Rd is high *or* -RD is low while theãchip is selected, the CPU can read data from the UART. (One of these isãnormally tied. CB)ãã-RI, Ring Indicator, Pin 39: blah blah blah, Bit 6 / Bit 2 of the MSR.ã(Bit 2 indicates only change from active low to inactive high! Curious,ãisn't it? CB)ãã-RTS, Request To Send, Pin 32: blah blah blah, see DTR (Bit 1).ããSIN, Serial Input, Pin 10.ããSOUT, Serial Output, Pin 11: ... Set to 'space' (high) upon MR.ãã-RXRDY, -TYRDY: refer to NS data sheet. Those pins are used for DMAãchanneling. Since they are not connected in your PC, I won't describe themãhere.ããVCC, Pin 40, +5vããVSS, Pin 20, GNDããWR, -WR: same as Rd, -RD for writing data.ããXIN, XOUT, Pins 16 and 17: Connect a crystal here (1.5k betw. xtal & pin 17)ãand pin 16 with a capacitor of approx. 20p to GND and other xtal conn. 40pãto GND. Resistor of approx. 1meg parallel to xtal. Or use pin 16 as an inputãand pin 17 as an output for an external clock signal.ãããAbsolute Maximum Ratings:ãã  Temperature under bias: 0 C to +70 Cã  Storage Temperature: -65 C to 150 Cã  All input or output voltages with respect to VSS: -0.5v to +7.0vã  Power dissipation: 1WããFurther electrical characteristics see the very good data sheet of NS.ããUART Reset ConfigurationããRegister/Signal        Reset Control      Reset Stateã--------------------------------------------------------------------ã  IER                       MR            0000 0000ã  IIR                       MR            0000 0001ã  FCR                       MR            0000 0000ã  LCR                       MR            0000 0000ã  MCR                       MR            0000 0000ã  LSR                       MR            0110 0000ã  MSR                       MR            xxxx 0000 (according to signals)ã  SOUT                      MR            highã  INTR (RCVR errs)     Read LSR/MR        lowã  INTR (data ready)    Read RBR/MR        lowã  INTR (THRE)          Rd IIR/Wr THR/MR   lowã  INTR (modem status)  Read MSR/MR        lowã  -OUT2                     MR            highã  -RTS                      MR            highã  -DTR                      MR            highã  -OUt1                     MR            highã  RCVR FIFO           MR/FCR1&FCR0/DFCR0  all bits lowã  XMIT FIFO           MR/FCR1&FCR0/DFCR0  all bits lowããããKnown problems with several chipsã---------------------------------ãã(From material Madis Kaal received from Dan Norstedt)ãã    8250 and 8250-B:ãã        * These UARTs pulse the INT line after each interrupt cause hasã          been serviced (which none of the others do). [Generates interruptã          overhead. CB]ãã        * The start bit is about 1 us longer than it ought to be. [Thisã          shouldn't be a problem. CB]ãã        * 5 data bits and 1.5 stop bits doesn't work.ãã        * When a 1 bit is written to the bit 1 (Tx int enab) in the IER,ã          a Tx interrupt is generated. This is an erroneous interruptã          if the THRE bit is not set. [So don't set this bit as long asã          the THRE bit isn't set. CB]ãã        * The first valid Tx interrupt after the Tx interrupt is enabledã          is probably missed. Suggested workaround:ã          1) Wait for the TRHE bit to become set.ã          2) Disable CPU interrupts.ã          3) Write Tx interrupt enable to the IER.ã          4) Write Tx interrupt enable to the IER, again.ã          5) Enable CPU interrupts.ãã        * The TSRE (bit 6) doesn't work properly.ãã        * If both the Rx and Tx interrupts are enabled, and a Rx interruptã          occurs, the IIR indication may be lost; Suggested workarounds:ã          1) Test THRE bit in the Rx routine, and either set IER bit 1ã             or call the Tx routine directly if it is set.ã          2) Test the THRE bit instead of using the IIR.ãã        * [If one of these chips vegetates in your PC, go get your solderã          iron heated... CB]ãã    8250A, 82C50A, 16450 and 16C450:ãã        * (Same problem as above:)ã          If both the Rx and Tx interrupts are enabled, and a Rx interruptã          occurs, the IIR indication may be lost; Suggested workarounds:ã          1) Test THRE bit in the Rx routine, and either set IER bit 1ã             or call the Tx routine directly if it is set.ã          2) Test the THRE bit instead of using the IIR.ã          3) [Don't enable both interrupts at the same time. I've neverã             had any need to do this. CB]ãã    16550 (without the A):ãã        * Rx FIFO bug: Sometimes a FIFO will get extra characters.ã          [This seemed to be very embarracing for NS; they've added aã          simple detection method in the 16550A (bit 6 of IIR). CB]ããNo bugs reported in the 16550A (yet?)ãã[Same is true for the 16C552, a two-in-one version of the 16550A. CB]ããããSoftwareã========ããã  First some information from the data sheet. Then: HOW TO USE IT.ãããRegister Descriptionã--------------------ããSee "Hardware" for addresses.ããRegister  Bit 0    Bit 1    Bit 2    Bit 3    Bit 4    Bit 5    Bit 6    Bit 7ããRBR (r/o)  ----------------------- data bits received ------------------------ãTHR (w/o)  ------------------ data bits to be transmitted --------------------ãIER       ERBFI    ETBEI    ELSI     EDSSI      0        0        0       0ãIIR (r/o) pending  IID0     IID1     IID2       0        0      FIFO en  FIFOenãFCR (w/o) enable   RFres    XFres    DMAsel     0        0      - RX trigger -ãLCR       - word length -   stopbits PAR en   even sel stick par SBR     DLABãMCR       DTR      RTS      OUT1     OUT2     Loop       0        0       0ãLSR       RBF      OE       PE       FE       Break    THRE     TEMT    FIFOerrãMSR       DCTS     DDSR     TERI     DDCD     CTS      DSR      RI      DCDããERBFI:   Enable Receiver Buffer Full InterruptãETBEI:   Enable Transmitter Buffer Empty InterruptãELSI:    Enable Line Status InterruptãEDSSI:   Enable Delta Status Signals InterruptãIID#:    Interrupt IDentificationãRFres:   Receiver FIFO resetãXFres:   Transmitter FIFO resetãSBR:     Set BReakãRBF:     Receiver Buffer Full (Data Available)ãOE:      Overrun ErrorãPE:      Parity ErrorãFE:      Framing ErrorãTHRE:    Transmitter Holding Register Empty (new data can be written to THR)ãTEMT:    Transmitter Empty (last word has been sent)ãDCTS:    Delta Clear To SendãDDSR:    Delta Data Set ReadyãTERI:    Trailing Edge Ring IndicatorãDDCD:    Delta Data Carrier DetectããLCR (Line Control Register):ãã   Bit 1  Bit 0    word length         Bit 2      Stop bitsã     0      0        5 bits              0            1ã     0      1        6 bits              1          1.5/2ã     1      0        7 bits         (1.5 if word length is 5)ã     1      1        8 bits   (1.5 does not work with some chips, see above)ãã   Bit 5  Bit 4  Bit 3     Parity type       Bit 6   SOUT conditionã     x      x      0       no parity           0     normal operationã     0      0      1       odd parity          1     force 'mark' (break)ã     0      1      1       even parity       Bit 7   DLABã     1      0      1       mark parity         0     normal registersã     1      1      1       space parity        1     divisor at reg 0, 1ããBaud Rate Generator:ãã  DLAB must be set. Write word (16 bits) to address 0 of the UART (this isãthe base address) to program baud rate as follows:ã     xtal frequency in Hz / 16 / rate = divisorã  Your PC uses an xtal frequency of 1.8432 MHz.ã  Do *NOT* use 0 as a divisor (your maths teacher told you so)! [Itãresults in a rate of some 1000 baud. CB]ã  An error of up to 5 percent is irrelevant.ã  Some values:ãã     Baud rate   Divisor (hex)   Percent Errorã         50          900             0.0%ã         75          600             0.0%ã        110          417             0.026%ã        134.5        359             0.058%ã        150          300             0.0%ã        300          180             0.0%ã        600           C0             0.0%ã       1200           60             0.0%ã       1800           40             0.0%ã       2000           3A             0.69%ã       2400           30             0.0%ã       3600           20             0.0%ã       4800           18             0.0%ã       7200           10             0.0%ã       9600            C             0.0%ã      19200            6             0.0%ã      38400            3             0.0%ã      56000            2             2.86%ã     115200            1             0.0%ãã  NS specifies that the 16550A is capable of 256 kbaud if you use a 4 MHzãor an 8 MHz crystal. But a staff member of NS Germany (I know that thisãabbreviation is not well-chosen :-( ) told one of my friends on the phoneãthat it runs correctly at 512 kbaud as well, but I don't know if theã1488/1489 manage this. This is true for the 16C552, too.ã  BTW: Ever tried 1.76 baud? Kindergarten kids write faster.ã  Mice typically use 2400 baud, 8n1.ãããLSR (Line Status Register):ãã   Bit 0    Data Ready (DR). Reset by reading RBR.ã   Bit 1    Overrun Error (OE). Reset by reading LSR. Indicates loss of data.ã   Bit 2    Parity Error (PE). Indicates transmission error. Reset by LSR.ã   Bit 3    Framing Error (FE). Indicates missing stop bit. Reset by LSR.ã   Bit 4    Break Indicator (BI). Set if 'space' for more than 1 word. Resetã            by LSR.ã   Bit 5    Transmitter Holding Register Empty (THRE). Indicates that a newã            word can be written to THR. Reset by writing THR.ã   Bit 6    Transmitter Empty (TEMT). Indicates that no transmission isã            running. Reset by reading LSR.ã   Bit 7    Set if at least 1 word in FIFO has been received with an error.ã            Cleared by reading LSR if there is no further error in the FIFO.ããFCR (FIFO Control Register):ãã   Bit 0:   FIFO enable.ã   Bit 1:   Clear receiver FIFO. This bit is self-clearing.ã   Bit 2:   Clear transmitter FIFO. This bit is self-clearing.ã   Bit 3:   DMA mode (pins -RXRDY and -TXRDY), see sheetã   Bits 6-7:Trigger level of the DR-interrupt.ã   Bit 7  Bit 6    Receiver FIFO trigger levelã     0      0         01ã     0      1         04ã     1      0         08ã     1      1         14ããã   Excursion: why and how to use the FIFOs (by Scott C. Sadow)ã   -----------------------------------------------------------ãã   Normally when transmitting or receiving, the UART generates anã   interrupt for every character sent or received. For 2400 baud, typicallyã   this is 240/second. For 115,200 baud, this means 11,520/second. With FIFOsã   enabled, the number of interrupts is greatly reduced. For transmitã   interrupts, the UART indicates the transmit holding register is not busyã   until the 16 byte FIFO is full. A transmit hold register empty interruptã   is not generated until the FIFO is empty (last byte is being sent) Thus,ã   the number of transmit interrupts is reduced by a factor of 16. Forã   115,200 baud, this means only 7,200 interrupts/second. For receive dataã   interrupts, the processing is similar to transmit interrupts. The mainã   difference is that the number of bytes in the FIFO before generating anã   interrupt can be set. When the trigger level is reached, a recieve dataã   interrupt is generated, but any other data received is put in the FIFO.ã   The receive data interrupt is not cleared until the number of bytes in theã   FIFO is below the trigger level.ãã   To add 16550A support to existing code, there are 2 requirements.ãã      1) When reading the IIR to determine the interrupt source, onlyã         use the lower 3 bits.ãã      2) After the existing UART initialization code, try to enable theã         FIFOs by writing to the FCR. (A value of C7 hex will enable FIFO mode,ã         clear both FIFOs, and set the receive trigger level at 14 bytes) Next,ã         read the IIR. If Bit 6 of the IIR is not set, the UART is not aã         16550A, so write 0 to the FCR to disable FIFO mode.ãããIIR (Interrupt Identification Register):ãã   Bit 3  Bit 2  Bit 1  Bit 0    Priority   Source    Descriptionã     0      0      0      1                 noneã     0      1      1      0      highest    Status    OE, PE, FE or BI of theã                                                      LSR set. Serviced byã                                                      reading the LSR.ã     0      1      0      0      second     Receiver  DR or trigger level rea-ã                                                      ched. Serviced by read-ã                                                      ing RBR 'til under levelã     1      1      0      0      second     FIFO      No Receiver FIFO actionã                                                      since 4 words' timeã                                                      (neither in nor out) butã                                                      data in RX-FIFO. Servicedã                                                      by reading RBR.ã     0      0      1      0      third      Transm.   THRE. Serviced by read-ã                                                      ing IIR (if source ofã                                                      int only!!) or writingã                                                      to THR.ã     0      0      0      0      fourth     Modem     One of the delta flagsã                                                      in the MSR set. Servicedã                                                      by reading MSR.ã   Bit 6 & 7: 16550A: set if FCR bit 0 set.ã              16550:  bit 7 set, bit 6 clearedã              others: clearã   In most software applications bits 3, 6 & 7 should be masked when servicingã   the interrupt since they are not relevant. These bits cause trouble withã   old software relying on that they are cleared...ã   NOTE! Even if some of these interrupts are masked, the service routineã   can be confronted with *all* states shown above when the IIR is loop-polledã   until bit 0 is set. Check examples.ããIER (Interrupt Enable Register):ãã   Bit 0:   If set, DR interrupt is enabled.ã   Bit 1:   If set, THRE interrupt is enabled.ã   Bit 2:   If set, Status interrupt is enabled.ã   Bit 3:   If set, Modem status interrupt is enabled.ããMCR (Modem Control Register):ãã   Bit 0:   Programs -DTR. If set, -DTR is low and the DTR pin of the port isã            '1'.ã   Bit 1:   Programs -RTS.ã   Bit 2:   Programs -OUT1. Not used in a PC.ã   Bit 3:   Programs -OUT2. If set, interrupts generated by the UART are trans-ã            ferred to the ICU (Interrupt Control Unit).ã   Bit 4:   '1': local loopback. All outputs disabled.ããMSR (Modem Status Register):ãã   Bit 0:   Delta CTS. Set if CTS has changed state since last reading.ã   Bit 1:   Delta DSR. Set if DSR has changed state since last reading.ã   Bit 2:   TERI. Set if -RI has changed from low to high (i.e. RI at portã            has changed from '1' to '0').ã   Bit 3:   Delta DCD. Set if DCD has changed state since last reading.ã   Bit 4:   CTS. 1 if '1' at port.ã   Bit 5:   DSR.ã   Bit 6:   RI. If loopback is selected, it is equivalent to OUT1.ã   Bit 7:   DCD.ãããPART TWO - PROGRAMMINGãããProgrammingã-----------ãã  Now for the clickety-clickety thing. I hope you're a bit keen inãassembler programming (if not, you've got a problem B-). Programming the UARTãin high level languages is, of course, possible, but not at very highãrates or interrupt-driven. I give you several routines in assembler (and,ãwherever possible, in C) that do the dirty work for you.ãã  First thing to do is detect which chip is used. It shouldn't be difficultãto convert this C function into assembler; I'll omit the assembly version.ããint detect_UART(unsigned baseaddr)ã{ã   // this function returns 0 if no UART is installed.ã   // 1: 8250, 2: 16450, 3: 16550, 4: 16550Aã   int x;ã   // first step: see if the LCR is thereã   outp(baseaddr+3,0x1b);ã   if (inp(baseaddr+3)!=0x1b) return 0;ã   outp(baseaddr+3,0x3);ã   if (inp(baseaddr+3)!=0x3) return 0;ã   // next thing to do is look for the scratch registerã   outp(baseaddr+7,0x55);ã   if (inp(baseaddr+7)!=0x55) return 1;ã   outp(baseaddr+7,0xAA);ã   if (inp(baseaddr+7)!=0xAA) return 1;ã   // then check if there's a FIFOã   outp(baseaddr+2,0x1);ã   x=inp(baseaddr+2);ã   if ((x&0x80)==0) return 2;ã   if ((x&0x40)==0) return 3;ã   return 4;ã}ãã  Remember: if it's not a 16550A, don't use the FIFO mode!ããã  Now the non-interrupt version of TX and RX.ãã  Let's assume the following constants are set correctly (either byã'CONSTANT EQU value' or by '#define CONSTANT value'). You can easily useãvariables instead, but I wanted to save the extra lines for the ADDãcommands necessary then...ãã  UART_BASEADDR   the base address of the UARTã  UART_BAUDRATE   the divisor value (e.g. 12 for 9600 baud)ã  UART_LCRVAL     the value to be written to the LCR (e.g. 0x1b for 8N1)ã  UART_FCRVAL     the value to be written to the FCR. Bit 0, 1 and 2 set,ã                  bits 6 & 7 according to trigger level wished (see above).ã                  0x87 is a good value.ãã  First thing to do is initializing the UART. This works as follows:ããinit_UART proc nearã  push ax  ; we are 'clean guys'ã  push dxã  mov  dx,UART_BASEADDR+3  ; LCRã  mov  al,80h  ; set DLABã  out  dx,alã  mov  dx,UART_BASEADDR    ; divisorã  mov  ax,UART_BAUDRATEã  out  dx,axã  mov  dx,UART_BASEADDR+3  ; LCRã  mov  al,UART_LCRVAL  ; paramsã  out  dx,alã  mov  dx,UART_BASEADDR+4  ; MCRã  xor  ax,ax  ; clear loopbackã  out  dx,alã  ;***ã  pop  dxã  pop  axã  retãinit_UART endpããvoid init_UART()ã{ã   outp(UART_BASEADDR+3,0x80);ã   outpw(UART_BASEADDR,UART_BAUDRATE);ã   outp(UART_BASEADDR+3,UART_LCRVAL);ã   outp(UART_BASEADDR+4,0);ã   //***ã}ãã  If we wanted to use the FIFO functions of the 16550A, we'd have to addãsome lines to the routines above (where the ***s are).ãIn assembler:ã  mov  dx,UART_BASEADDR+2  ; FCRã  mov  al,UART_FCRVALã  out  dx,alãAnd in C:ã   outp(UART_BASEADDR+2,UART_FCRVAL);ãã  Don't forget to disable the FIFO when your program exits! Some otherãsoftware may rely on this!ãã  Not very complex so far, isn't it? Well, I told you so at the veryãbeginning, and we wanted to start easy. Now let's send a character.ããUART_send proc nearã  ; character to be sent in ALã  push dxã  push axã  mov  dx,UART_BASEADDR+5ãus_wait:ã  in   al,dx  ; wait until we are allowed to write a byte to the THRã  test al,20hã  jz   us_waitã  pop  axã  mov  dx,UART_BASEADDRã  out  dx,al  ; then write the byteã  pop  axã  pop  dxã  retãUART_send endpããvoid UART_send(char character)ã{ã   while ((inp(UART_BASEADDR+5)&0x20)!=0) {;}ã   outp(UART_BASEADDR,(int)character);ã}ãã  This one sends a null-terminated string.ããUART_send_string proc nearã  ; DS:SI contains a pointer to the string to be sent.ã  push siã  push axã  push dxã  cld  ; we want to read the string in its correct orderãuss_loop:ã  lodsbã  or   al,al  ; last character sent?ã  jz   uss_endeã  ;*1*ã  mov  dx,UART_BASEADDR+5ã  push axãuss_wait:ã  in   al,dxã  test al,20hã  jz   uss_waitã  mov  dx,UART_BASEADDRã  pop  axã  out  dx,alã  ;*2*ã  jmp  uss_loopãuss_ende:ã  pop  dxã  pop  axã  pop  siã  retãUART_send_string endpããvoid UART_send_string(char *string)ã{ã   int i;ã   for (i=0; string[i]!=0; i++)ã      {ã      //*1*ã      while ((inp(UART_BASEADDR+5)&0x20)!=0) {;}ã      outp(UART_BASEADDR,(int)string[i]);ã      //*2*ã      }ã}ãã  Of course, we could have used our already programmed function/procedureãUART_send instead of the piece of code limited by *1* and *2*, but we areãinterested in high-speed code.ãã  It shouldn't be a hard nut for you to modify the above function/procedureãso that it sends a block of data rather than a null-terminated string. I'llãomit that here.ãã  Now for reception. We want to program routines that do the following:ã  - check if a character received or an error occuredã  - read a character if there's one availableãã  Both the C and the assembler routines return 0 (in AX) if there isãneither an error condition nor a character available. If a character isãavailable, Bit 8 is set and AL or the lower byte of the return valueãcontains the character. Bit 9 is set if we lost data (overrun), bit 10ãsignals a parity error, bit 11 signals a framing error, bit 12 shows ifãthere is a break in the data stream and bit 15 signals if there are anyãerrors in the FIFO (if we turned it on). The procedure/function is muchãsmaller than this paragraph:ããUART_get_char proc nearã  push dxã  mov  dx,UART_BASEADDR+5ã  in   al,dxã  mov  ah,alã  and  ah,9fhã  test al,1ã  jz   ugc_nocharã  mov  dx,UART_BASEADDRã  in   al,dxãugc_nochar:ã  pop  dxã  retãUART_get_char endpããunsigned UART_get_char()ã{ã   unsigned x;ã   x=(inp(UART_BASEADDR+5)<<8)&0x9f;ã   if (x&0x100) x|=((unsigned)inp(UART_BASEADDR))&0xff);ã   return x;ã}ãã  This procedure/function lets us easily keep track of what's happeningãwith the RxD pin. It does not provide any information on the modem statusãlines! We'll program that later on.ãã  If we wanted to show what's happening with the RxD pin, we'd just have toãwrite a routine like the following (I use a macro in the assembler versionãto shorten the source code):ããDOS_print macro pointerã  ; prints a string in the code segmentã  push axã  push dsã  push csã  pop  dsã  mov  dx,pointerã  mov  ah,9ã  int  21hã  pop  dsã  pop  axã  endmããUART_watch_rxd proc nearãuwr_loop:ã  ; check if keyboard hit; we want a possibility to break the loopã  mov  ah,1ã  int  16hã  jnz  uwr_exitã  call UART_get_charã  or   ax,axã  jz   uwr_loopã  test ah,1  ; is there a character in AL?ã  jz   uwr_nodataã  push ax    ; yes, print itã  mov  dl,alã  mov  ah,2ã  int  21hã  pop  axãuwr_nodata:ã  test ah,0eh ; any error at all?ã  jz   uwr_loop  ; this speeds up thingsã  test ah,2  ; overrun error?ã  jz   uwr_nooverã  DOS_print overrun_textãuwr_noover:ã  test ah,4  ; parity error?ã  jz   uwr_noparã  DOS_print parity_textãuwr_nopar:ã  test ah,8  ; framing error?ã  jz   uwr_loopã  DOS_print framing_textã  jmp  uwr_loopãoverrun_text    db "*** Overrun Error ***$"ãparity_text     db "*** Parity Error ***$"ãframing_text    db "*** Framing Error ***$"ãUART_watch_rxd endpããvoid UART_watch_rxd()ã{ã   union _useful_ã      {ã      unsigned val;ã      char character;ã      } x;ã   while (!kbhit())ã      {ã      x.val=UART_get_char();ã      if (!x.val) continue;  // nothing? Continueã      if (x.val&0x100) putc(x.character);  // character? Print itã      if (!(x.val&0x0e00)) continue;  // any error condidion? No, continueã      if (x.val&0x200) printf("*** Overrun Error ***");ã      if (x.val&0x400) printf("*** Parity Error ***");ã      if (x.val&0x800) printf("*** Framing Error ***");ã      }ã}ãã  If you call these routines from a function/procedure as shown below,ãyou've got a small terminal program!ããterminal proc nearãter_loop:ã  call UART_watch_rxd  ; watch line until a key is pressedã  xor  ax,ax  ; get that key from the bufferã  int  16hã  cmp  al,27  ; is it ESC?ã  jz   ter_end  ; yes, then end this functionã  call UART_send  ; send the character typed if it's not ESCã  jmp  ter_loop  ; don't forget to check if data comes inãter_end:ã  retãterminal endpããvoid terminal()ã{ã   int key;ã   while (1)ã      {ã      UART_watch_rxd();ã      key=getche();ã      if (key==27) break;ã      UART_send((char)key);ã      }ã}ãã  These, of course, should be called from an embedding routine like theãfollowing (the assembler routines concatenated will assemble as an .EXEãfile. Put the lines 'code segment' and 'assume cs:code,ss:stack' to theãfront).ããmain proc nearã  call UART_initã  call terminalã  mov  ax,4c00hã  int  21hãmain endpãcode endsãstack segment stack 'stack'ã  dw 128 dup (?)ãstack endsãend mainããvoid main()ã{ã   UART_init();ã   terminal();ã}ãã  Here we are. Now you've got everything you need to program null-modemãpolling UART software.ã  You know the way. Now go and add functions to check if a data set isãthere, then establish a connection. Don't know how? Set DTR, wait for DSR.ãIf you want to send, set RTS and wait for CTS before you actually transmitãdata. You don't need to store old values of the MCR: this register isãreadable. Just read in the data, AND/OR the bit required and write theãbyte back.ããã  Now for the interrupt-driven version of the program. This is going to beãa bit voluminous, so I draw the scene and leave the painting to you. If youãwant to implement interrupt-driven routines in a C program use either theãinline-assembler feature or link the objects together.ãã  First thing to do is initialize the UART the same way as shown above.ãBut there is some more work to be done before you enable the UARTãinterrupt: FIRST SET THE INTERRUPT VECTOR CORRECTLY! Use Function 0x25 ofãthe DOS interrupt 0x21. See also the note on known bugs if you've got aã8250.ããUART_INT      EQU 0Ch  ; for COM2 / COM4 use 0bhãUART_ONMASK   EQU 11101111b  ; for COM2 / COM4 use 11110111bãUART_OFFMASK  EQU 00010000b  ; for COM2 / COM4 use 00001000bãUART_IERVAL   EQU ?   ; replace ? by any value between 0h and 0fhã                      ; (dependent on which ints you want)ã                      ; DON'T SET bit 1 yet!ããinitialize_UART_interrupt proc nearã  push dsã  push cs  ; build a pointer in DS:DXã  pop  dsã  lea  dx,interrupt_service_routineã  mov  ax,2500h+UART_INTã  int  21hã  pop  dsã  mov  dx,UART_BASEADDR+4  ; MCRã  in   al,dxã  or   al,8  ; set OUT2 bit to enable interruptsã  out  dx,alã  mov  dx,UART_BASEADDR+1  ; IERã  mov  al,UART_IERVALã  out  dx,alã  in   al,21h  ; last thing to do is unmask the int in the ICUã  and  al,UART_ONMASKã  out  21h,alã  sti  ; and free interrupts if they have been disabledã  retãinitialize_UART_interrupt endpãã  Now the interrupt service routine. It has to follow several rules:ãfirst, it MUST NOT change the contents of any register of the CPU! Then itãhas to tell the ICU (did I tell you that this is the interrupt controlãunit?) that the interrupt is being serviced. Next thing is test which partãof the UART needs service. Let's have a look at the following procedure:ããinterupt_service_routine proc far  ; define as near if you want to link .COMã  ;*1*ã  push axã  push cxã  push dxã  push bxã  push spã  push bpã  push siã  push diã  ;*2*   replace the part between *1* and *2* by pusha on an 80186+ systemã  push dsã  push esã  mov  al,20h    ; remember: first thing to do in interrupt routines is tellã  out  20h,al    ; the ICU about it. This avoids lock-upãint_loop:ã  mov  dx,UART_BASEADDR+2  ; IIRã  xor  ax,ax  ; clear AH; this is the fastest and shortest possibilityã  in   al,dx  ; check IIR infoã  test al,1ã  jnz  int_endã  and  al,6  ; we're interested in bit 1 & 2 (see data sheet info)ã  mov  si,ax ; this is already an index! Well-devised, huh?ã  call word ptr cs:int_servicetab[si]  ; ensure a near call is used...ã  jmp  int_loopãint_end:ã  pop  esã  pop  dsã  ;*3*ã  pop  diã  pop  siã  pop  bpã  pop  spã  pop  bxã  pop  dxã  pop  cxã  pop  axã  ;*4*   *3* - *4* can be replaced by popa on an 80186+ based systemã  iretãinterupt_service_routine endpãã  This is the part of the service routine that does the decisions. Now weãneed four different service routines to cover all four interrupt sourceãpossibilities (EVEN IF WE DIDN'T ENABLE THEM!! Since 'unexpected'ãinterrupts can have higher priority than 'expected' ones, they can appearãif an expected [not masked] interrupt situation shows up).ããint_servicetab    DW int_modem, int_tx, int_rx, int_statusããint_modem proc nearã  mov  dx,UART_BASE+6  ; MSRã  in   al,dxã  ; do with the info what you like; probably just ignore it...ã  ; but YOU MUST READ THE MSR or you'll lock up the system!ã  retãint_modem endpããint_tx proc nearã  ; get next byte of data from a buffer or somethingã  ; (remember to set the segment registers correctly!)ã  ; and write it to the THR (offset 0)ã  ; if no more data is to be sent, disable the THRE interruptã  ; If the FIFO is used, you can write data as long as bit 5ã  ; of the LSR is 0ãã  ; end of data to be sent?ã  ; no, jump to end_int_txã  mov  dx,UART_BASEADDR+1ã  in   al,dxã  and  al,00001101bã  out  dx,alãend_int_tx:ã  retãint_tx endpããint_rx proc nearã  mov  dx,UART_BASEADDRã  in   al,dxã  ; do with the character what you like (best write it to aã  ; FIFO buffer)ã  ; the following lines speed up FIFO mode operationã  mov  dx,UART_BASEADDR+5ã  in   al,dxã  test al,1ã  jnz  int_rxã  retãint_rx endpããint_status proc nearã  mov  dx,UART_BASEADDR+5ã  in   al,dxã  ; do what you like. It's just important to read the LSRã  retãint_status endpãã  How is data sent now? Write it to a FIFO buffer that is read by theãinterrupt routine. Then set bit 1 of the IER and check if this has alreadyãstarted transmission. If not, you'll have to start it by yourself... THISãIS DUE TO THOSE NUTTY GUYS AT BIG BLUE WHO DECIDED TO USE EDGE TRIGGEREDãINTERRUPTS INSTEAD OF PROVIDING ONE SINGLE FLIP FLOP FOR THE 8253/8254!ã  This procedure can be a C function, too. It is not time-critical at all.ãã  ; copy data to bufferã  mov  dx,UART_BASEADDR+1  ; IERã  in   al,dxã  or   al,2  ; set bit 1ã  out  dx,alã  mov  dx,UART_BASEADDR+5  ; LSRã  in   al,dxã  test al,40h  ; is there a transmission running?ã  jz   dont_crank  ; yes, so don't mess it upã  call int_tx  ; no, crank it upãdont_crank:ãã  Well, that's it! Your main program has to take care about the buffers,ãnothing else!ãã  One more thing: always remember that at 115,200 baud there is service toãbe done at least every 8 microseconds! On an XT with 4.77 MHz this isãabout 5 assembler commands!! So forget about servicing the serial port atãthis rate in an interrupt-driven manner on such computers. An AT with 12ãMHz probably will manage it if you use 'macro commands' such as pusha and/orãa 16550A in FIFO mode. An AT can perform about 20 instructions between twoãcharacters, a 386 with 25 MHz will do about 55, and a 486 with 33 MHz willãmanage about 150. Using a 16550A is strongly recommended at high rates.ã  The interrupt service routines can be accelerated by not pushing thatãmuch registers, and pusha and popa are fast instructions.ãã  Another last thing: due to the poor construction of the PC interruptãsystem, one interrupt line can only be driven by one device. This means ifãyou want to use COM3 and your mouse is connected to COM1, you can't useãinterrupt features without disabling the mouse (write 0x0 to the mouse'sãMCR).ã                                                                                                                        