(*


     See/text-search in file COMM.TXT for "WATCHES.TXT".


     WATCHES  TXT   :watches & how to format them; values & how they
                     are stored in memory; TP-IDE; (guess from 6.0 onwards)

  ................................................................
  "Beginners only"  {to skip, text-search "WATCH-FORMATS"
     Watches invisible ?
       UNITs
     Watches invisible after CTRL-F9 and a run-error
     Watches = Variables only?
  ................................................................
  WATCH-FORMATS
  VALUE & HOW IT IS STORED
   POINTERS
  MANY VARS TO DISPLAY
  BINARY FORMAT

  ================================================================


  ................................................................


  "Beginners only"  {to skip, text-search "WATCH-FORMATS"

     Watches are what you get via eg. CTRL+F7. You put variables
     in the watches-window. It watches what the variables are
     looking at. To watch them carefully, their value is closely
     monitored. If you want to keep a close eye on this, you can
     split your monitor with ALT-W, T, and have a look at the
     watches in the lower window.


     (BTW, you can change a value with ALT+D, E while the program is
      "running", i.e. while debugging.
     )


     Watches invisible ?
       Well, firstly you need to enable the compiler to compile
       with the necessary information for debugging, before you
       running a program step by step - and watching any variables.
       See the online-help on eg.: $D $L
       (put cursor on $D, press CTRL-F1}
       ((also, there are some notes in file: Define.txt of upload
         loader.zip
       ))

       UNITs
         Note that if a program uses units you can step through
         their routines, only if the units are compiled with
         the corresponding $... specifications.

         Also: If you press F8 and the highlighted line jumps to
         the "BEGIN" of the main program, you can then step through
         the init-code (BEGIN...END part of units) of all such
         compiled units by pressing F7.

         You can however, simply put an END instead of a BEGIN END
         in the interface of an unit which would "avoid" this. If
         there is no "real" ini-code, well, you can still put BEGIN END
         in a unit, which then perhaps serves to check which units are
         loaded when (stepping via F7), etcetera.

         Units as CRT, SYSTEM, etc. are not compiled for this kind of
         "stepping" via F7.


       Hence, you'd need to press F7 or F8 to start debugging
       before watches "become visible". Also, you'd need to take
       care that watches inside procedures might have the same
       name as global variables. Inside a procedure P, the
       watch-variable "w" represents the "w" of the procedure,
       outside the procedure it refers to the global variable "w".


       You can specify a variable however:

       Program x;
       var w:word;
         procedure a;
         var w:word;
         begin
         end;
       begin
         a;
       end.

       Here you can use: "x.w" for the global variable "w", and
       "a.w" for the procedure's variable "w"

       The watch "w" may refer to any of these, depending on
       whether "the program" is *currently* "inside the procedure"
       or not.

       ((
         Other specifications can apply to the units CRT, DOS, etc.
         eg.: "crt.lastmode". You can of course also use this to
         "de-reference" procedure/functions. "built-in" routines
         are de-referenced with System.*
         eg.:
           program x; uses crt;
           {---------------------------------------------------------------}
           PROCEDURE writeln (s:string);
           BEGIN
             system.writeln(#7,s); {#7=bell character (ASCII), beeps}
           END;
           {---------------------------------------------------------------}
           BEGIN
             ClrScr;
             writeln('...NET !');
             System.Writeln('...first service.');
             System.Writeln('...quiet, please...');
             writeln('...OUT !');
           END.
       ))


     Watches invisible after CTRL-F9 and a run-error
       When your program stops with a run-error the watches of a
       procedure might no longer be "visible". For test-purposes
       you might instead use a global variable, which will still
       be visible:
       (eg included via $define... compiling)
       ((about $define, see file DEFINE.TXT / upload: Loader.zip))

       For example:

       Program x;
       var w:word;
         procedure a;
         var b:byte;
         begin
           b:=4;
           w:=4;
           runError;
           {<function to artificially cause a runError as if a "real"
            error had occurred.
           }
         end;
       begin
         a;
       end.

       Follwing a screen-copy *after* this sample code has been run:


   File  Edit  Search  Run  Compile  Debug  Tools  Options  Window  Help
 [] WATCHES.TXT 1[]ͻ
  Error 0: Runtime error.                                                      
          procedure a;                                                         
          var b:byte;                                                          
          begin                                                                
            b:=4;                                                              
            w:=4;                                                              
            runError;                                                          
            {function to artificially cause a runError as if a "real"          
             error had occurred.                                                
            }                                                                  
          end;                                                                 
        begin                                                                  
          a;                                                                   
        end.                                                                   
  73:1 
  Watches 2Ŀ
  b: Unknown identifier                                                        
  w: 4                                                                         
                                                                               
                                                                               
                                                                               
 
  F1 Help  F2 Save  F3 Open  Alt+F9 Compile  F9 Make  Alt+F10 Local menu

       Note:  Variable w is still "visible", when variable b is not.




     Watches = Variables only?
       Not necessarily. You can easily enter any "expression"
       If it is wrong, the IDE will tell you - that's about all.
       Invalid format-specifiers are mostly be simply ignored.

       Not forgetting that if you don't have a pocket calculator
       handy, you can also do calculations with the IDE. Later on,
       I'll show how this "calculator" could also convert decimal
       values to hexa-decimal.


       Program x;
       var w:word;
       begin
         w:=4;
       end.

       [] Watches 
        w: 4
        w * 30 - 50: 70
        1.25 * 4 + (1.2 / 3 ): 5.4
        Oh no, it's an: Unknown identifier
        trunc(2.4) * trunc(2.5): 4
        w mod 2: 0
        w > 2.5: True
        not true: False

       ...etc...type-casts...etc...







  ................................................................




  WATCH-FORMATS

     You can specify a couple of formats with which the value of a
     "watch-variable" is to be displayed. And, if you want ice for
     dessert, you can even combine them. That is, both combinations
     of values and/or variables.

     I assume this to be easier to explain by giving so called
     screen-dumps as examples. You can also use this file in the
     IDE and check the code yourself. If you want to run some
     sample code, simply move the end-of-comment sign (the
     comment sign with an asterix* ) above the sample code's
     "program" start-line.


     By default strings and arrays might appear quite "ugly":


     program x; var s:string; a:array[1..100] of char; b:byte;
     begin
       s[0]:=#100; for b:=32 to 132 do s[b-31]:=char(b);
       for b:=100 to 200 do a[b-99]:=char(b);
     end.
 Watches 1Ŀ
 s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
 a: ('d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','
                                                                              

     Both variables do not fit on the screen, and you'd need to use
     CURSOR-RIGHT etc. to see "higher values".

     You could try this format specifier for variables:
       s[40],40
     => beginning with element #40, display 40 "elements".

[] Watches 1[]ͻ
 s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
 s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W'
 a: ('d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','




     Sometimes, you might prefer to see values in hexa-decimal format.
     You can add ",h" - but this won't work with strings/chars. Yet,
     alternatively you can specify ",m" which displays "WHAT IS ACTUALLY
     STORED IN MEMORY", which displays in hexa-decimal format.

 Watches 1Ŀ
 s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
 s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W'
 s[40],40m: 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 




     "s[40],40" has "s" displayed like the array-of-char "a" was displayed.
     This can be considered ugly, since one char uses 4 "digits" to get
     displayed, eg: 'G',

     For vanilla ice-cream, you could alternatively combine the format-
     specifiers ",c" (char) with ",m":

[] Watches 1[]ͻ
 s: ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
 s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W'
 s[40],40mc: 'GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn'                       
 a: ('d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','
 a,mc: 'defghijklmnopqrstuvwxyz{|}~


     The "mc" specifier is s.th to keep in mind whenever you want
     to format as "ASCII-code chars".

     File WATCHES2.txt gives an example for ASM-registers as
     "watches as chars" - and for other points discussed further on
     here in this file.

     ((
       BTW, the ",c" is meant to display the #0..#31 as "ASCII-chars"
       (opposed to displaying them as #0, #1, etc.) Yet, this is not
       a good example to include, since it may cause havoc when printing.
       An example for this & "strings"-const is in file Watches3.txt
     ))



     Now, if you want to see decimal/Hexa-decimal equivalents,
     these are some ideas:
 Watches 
 s[40],40: 'G','H','I','J','K','L','M','N','O','P','Q','R','S','
 s[40],40m: 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 5
 s[40],40md: 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
 'G': 'G'
 byte('G'): 71
 byte('G'),h: $47





  VALUE & HOW IT IS STORED

     ",m" might help to visualize how "multi-byte unities" are actually
     stored in memory:

     program x;
     var pB:^Byte; w:word;
     begin
       w:=259;
       pB:=@w;
     end.
      Watches 
      pB^,h: $3
      pB^,2m: 03 01
      w: 259
      w,h: $103
      w,m: 03 01


     ",m" by default displays memory starting from the start-address
     of the variable "over the length/size" of the variable.

     As you can see the 2-bytes variable "w" has the hexa-VALUE $0103.
     Yet, it is STORED as $0301 - as "w,m" indicates.

     Also, see below about $A+ and $A-  (Text-search: "!!!")

     As pB is a pointer on a single byte only, pB^ does represent just
     one byte of w instead of its two bytes. Therefore the format
     specifier ",2" to display the two bytes stored in memory, "pB^,2m"

     Since the value of pB^ is $03, it obviously points to what "w,m"
     displays as the "high-byte" ("left-most" byte). More about this
     will follow later on, when giving a suggestion for "Binary format"
     and all that jazz.





   POINTERS

     ( This links to file COMM.TXT (text-search for "WATCHES.TXT")
       (about pointers<->strings as parameters for child-processes)
       upload: loader.zip
     )

     PROGRAM x;
     {$D+,L+,I+,R+,S+,Q+,V+,Y+}
     VAR s:string; pByte:^byte;
     BEGIN
       pByte:= PTR( $3344, $5566 ); {artificial value for testing only}

       s[0]:=#4; {<set length to 4}

       {>move the "address-pByte-is-pointing-to" to the string}
       move( pByte , s[1], 4);  { pByte = @pByte^ }
     END.
     {-----------------------------------------------------------------}
[] Watches 
 pByte: Ptr($3344,$5566)
 pByte,m: 66 55 44 33
 @pByte^: Ptr($3344,$5566)
 @pByte^,p: 3344:5566
 s: 'fUD3'
 s,m: 04 66 55 44 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 s[1],4m: 66 55 44 33


     As you can see, while a pointer is considered to be Ptr($3344,$5566)
     that is Ptr($segment,$offset), it is differently stored in memory.
     Here as: pByte,m: 66 55 44 33,

     Which is reflected in the type PtrRec used in unit WinDos to
     "access" a pointer:
       TYPE
         PtrRec = RECORD
                    ofs, seg:word;
                  END;

     Now, to obtain the word-value of eg. the segment you might eg. use
     the function seg():
       "w:=seg(pointerVar)"
     or type-casting:
       "w:= PtrRec( pointerVar ).seg"  (etc.... word()<-> offset, etc.)

     In a Bpascal forum message, Peter Below (CIS address 100113,1101)
     strongly recommended to me using type-casting, saying that, the
     Seg/Ofs functions would actually load the value into CPU-registers,
     and that with invalid values this would cause a GPF.





  MANY VARS TO DISPLAY
     If you have many byte/words as watches, each will "take up" one line.
     Even if they might not need more than a couple of columns to display
     a value.

     To not waste all those lines, you might eg. declare them following
     one another in the var-statement, eg:  var a,b,c,d:byte;

     program x;
     var a,b:byte; c:byte; d:byte;
         s:string;
         e:byte;
     begin
       a:=1;b:=2;c:=3;d:=4;e:=5;
       move(a,s[1],5);
     end.
 Watches 
 a: 1
 b: 2
 c: 3
 d: 4
 e: 5
 a,5: 1,2,3,4,0
 s,m: 00 01 02 03 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

     Note
      -"a,5" does not display "e", as variable "s" was declared before "e".
      -(BTW, the 0 in a,5 is obviously the length byte of s)


     (((
     ....BTW...BEGIN...................
         While using
           var a,b:byte; c:byte;
         or
           var a,b,c:byte;"
         did not seem to make any difference, I found a difference when
         using "var" several times:

         program x;
         var a,b:byte;
         var c:byte;
         var d:byte;
         var s:string;
             e:byte;
         begin
           a:=1;b:=2;c:=3;d:=4;e:=5;
           move(a,s[1],5);
         end.

          Watches 
          a: 1
          b: 2
          c: 3
          d: 4
          e: 5
          a,5: 1,2,3,0,4
          s,m: 00 01 02 03 00 04 00 00 00 00 00 00 00 00 00 00


         And mixing bytes/words:

         program x;
         {$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+}
         var a,b,c:byte; d:word; e:byte;
         var s:string;
         begin
           a:=1;b:=2;c:=3;d:=259;e:=5;
           move(a,s[1],7);
         end.
          Watches 
          a: 1
          b: 2
          c: 3
          e: 5
          a,7: 1,2,3,0,3,1,5
          a,7m: 01 02 03 00 03 01 05
          s,m: 00 01 02 03 00 03 01 05 00 00 00 00 00 00
          d,m: 03 01
          e,2m: 05 00
         



         !!!  And the effect $A+ and $A- can have.

         program x;
         {$A-,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+}
         var a,b,c:byte; d:word; e:byte;
         var s:string;
         begin
           a:=1;b:=2;c:=3;d:=259;e:=5;
           move(a,s[1],7);
         end.
         Watches 
         a: 1
         b: 2
         c: 3
         e: 5
         a,7: 1,2,3,3,1,5,0
         a,7m: 01 02 03 03 01 05 00
         s,m: 00 01 02 03 03 01 05 00 00 00 00 00 00 00
         d,m: 03 01
         e,2m: 05 00
        

         $A+ a,7: 1,2,3,0,3,1,5
         $A- a,7: 1,2,3,3,1,5,0

         ($A+ has "inserted a 0" before the word-var "d" (here the "3,1")
          - for details see online help on $A (put cursor on $A, press CTRL+F1)
            or manuals
         )


     ....BTW...END...................
     )))






     Or, as I was suggested with a <g>, group them into records:

     program x;
     const ar :record
                 a,b,c,d,e:byte;
               end = (a:1 ; b:2 ; c:3 ; d:4 ; e:5 );
     begin
     end.
[] Watches 
 ar: (1,2,3,4,5)
 ar,r: (a:1;b:2;c:3;d:4;e:5)


     NOTE that the format-specifier ",r" adds the names of the
     record-elements, which could be quite useful for larger records.


     NOTE
       Mixing CONST and VAR declaration won't be useful - the Borland's
       Programmer manual indicates different "locations" for the "storage"
       of CONST variables.

       That is to say for example:

        {$A- compiled}

        const byteVar:byte=$80;
              userInput:string='';

        will have userInput[0] directly after byteVar -
        Yet, this won't work for:

          const byteVar:byte=$80;
          var   userInput:string;

        See Borland's programmer manual on details about "internals,
        how variables are stored", CONST

        (again, I can't give titles or pages, since I don't have the
         manuals in English
        )



  BINARY FORMAT

     There seem to be no format-specifiers for eg. binary or octal format.
     I'd suggest one of my procedures enclosed here for this conversion.

     It can surely be improved (eg. wouldn't work on x.xxxx, i.e. digits
     after the "decimal"-point). Possible use while testing could be
     adding test-code via $defined-compiling and then using a watch-variable
     which displays the binary value.


 -------------------------------------------------------------------------

 basisDesZahlenSystemsFuerDenString:
  = the base of the system, eg 2 for binary, 8 for octal, 10 for decimal.

 dezimalzahl2string converts to "any system", just that you'd need to
 expand "ch" for anything with a higher base as 16.


 (((
   BTW:
     Actually the decimal-system is the odd one out. Converting from
     binary to octal to hexadecimal is much easier. "laengeGruppierung"
     gives the number of "chars" needed for conversion:

     8 binary chars = 3 octal chars = 2 hexa-decimal chars.

     eg: binary  11111111

        =      11 111 111
       octal    3   7   7
        =       1111 1111
       hexa        F    F

     If you'd like some homework, convert your Compuserve-Address from
     its octal format to hexa-decimal format.
     Or convert the hexa-decimal value $000000000009 to decimal format.
   -------------------------------------------------------------------------
 )))

*)

 program x;
 var pB:^Byte; w:word; s:string;

 {-DDDD---------------------------------------------------------------------}
 FUNCTION dezimalzahl2string
  ( zahl:longint;
    basisDesZahlenSystemsFuerDenString:byte
  ) :string;
 CONST ch:array[0..15]of Char = '0123456789ABCDEF'; {max. fr hexa-zahlen}
 VAR s:string; laengeGruppierung:byte;
 BEGIN
   IF zahl < 0
    THEN s:='-'
   ELSE s[0]:=#0;
   REPEAT
     {Beim Restwertverfahren ergeben sich die Vorkommastellen "rckwrts"}
     s:=ch[ (zahl mod basisDesZahlenSystemsFuerDenString) ] + s;
     zahl:= zahl div basisDesZahlenSystemsFuerDenString;
   UNTIL zahl = 0;
   CASE basisDesZahlenSystemsFuerDenString OF
     2:laengeGruppierung:=8;
     8:laengeGruppierung:=3;
     16:laengeGruppierung:=2;
     ELSE laengeGruppierung:=0;
   END;
   IF laengeGruppierung<>0
   THEN while (byte(s[0]) mod laengeGruppierung) <> 0 do s:='0'+s
   ;
   dezimalzahl2string:=s;
 END; {dezimalzahl2string}
 {--------------------------------------------------------------------------}
 FUNCTION int2binStr (CONST zahl:longint):string;
 VAR s:string; b:byte;
 BEGIN
   s:=dezimalZahl2String(zahl, 2);
   b:=byte(s[0]);
   while b>0 do begin
     insert(' ',s,b-3);
     insert('  ',s,b-7);
     dec(b,8);
   end;
   int2BinStr:='BIN:'+s;
 END; {Wandelt integer in eine Binrzahl im Format eines Strings}
 {--------------------------------------------------------------------------}
 begin
   w:=259;

   s:='w->'+int2BinStr(w);
   pB:=@w;
   s:=s+' // pb->'+int2BinStr(pB^);

 { At this stage of the program, the watches would display:
   Watches 
   pB^,h: $3
   pB^,2m: 03 01
   w: 259
   w,h: $103
   w,m: 03 01
   s[1],30mc: 'w->BIN:  0000 0001  0000 0011 '
   s[30],60mc: ' // pb->BIN:  0000 0011
  
 }


   inc(pB);
   s:=s+' // inc(pB)->'+int2BinStr(pB^);

 { At this stage of the program, the watches would display:
   Watches 
   pB^,h: $1
   pB^,2m: 01 50
   w: 259
   w,h: $103
   w,m: 03 01
   s[1],30mc: 'w->BIN:  0000 0001  0000 0011 '
   s[30],60mc: ' // pb->BIN:  0000 0011 // inc(pB)->BIN:  0000 0001
  
 }
 END.



 If you look at "w" as its value:
     'w->BIN:  0000 0001  0000 0011 '

 inc(pB) seems to move "from the right to the left":

                pb->BIN:  0000 0011
 inc(pB)->BIN: 0000 0001

 This is not the case. As mentioned you need to look at "w",
 as it is stored in memory, which is what "w,m" displays.
 Hence pB did move from "the left to the right" as to be expected.




(Copyright)Armin Schmitt at 100552.1041@compuserve.com
Nov-1995,
jC,v960224


 Homework:

 octal                      100552.1041
 =        1   0   0   5   5   2   .   1   0   4   1
 =bin.  001 000 000 101 101 010   . 001 000 100 001
 =           001000000101101010   . 001000100001
 =       00 1000 0001 0110 1010   . 0010 0010 0001
 =hexa.dec.    8    1    6    A   .    2    2    1
 =
 hexadecimal                  816A.221


 hexadecimal $000000000009
 =
 decimal 9.0000000000000

