Search code examples
delphidelphi-7

Pascal is counting backwards not upwards? so wierd


http://rextester.com/OXRFB95557

The watch shows the counter j going down not up...any ideas why? thanks

program NEACardTrick;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Math;

type
  Tcards = array[1..21] of string;    
var
   cards: Tcards = ('H A', 'H 2', 'H 3', 'H 4', 'H 5', 'H 6', 'H 7',
             'S A', 'S 2', 'S 3', 'S 4', 'S 5', 'S 6', 'S 7',
             'D A', 'D 2', 'D 3', 'D 4', 'D 5', 'D 6', 'D 7');
   shuffledCards:Tcards;
   i,j,y, x: integer;


function get_cards(var cards: array of string): Tcards;    
begin    
  y := 1;
  Repeat
    Randomize;
    x := RandomRange(1,21);
    If cards[x] <> 'Done' then
    begin
      shuffledCards[y] := cards[x];
      y := y + 1;
      cards[x] := 'Done';
    end    
  Until y >= 21;        
  result := shuffledCards;
end;

procedure PrintCards(var shuffledCards: Tcards);
var
  j: integer;
begin
  for j := 1 to 21 do
    writeln(shuffledCards[j]);
end;

begin    
  get_cards(cards);
  PrintCards(shuffledCards);
  readln;
end.

Solution

  • Your observation in Delphi 7 regarding the j: integer variable in procedure PrintCards() is exactly as it is shown in a debugger watch. However, rest assured, the for loop works correctly.

    In your code, you have given j two duties, 1) to act as a loop control and 2) to act as a index to the shuffledCards[] array.

    The compiler translates your delphi code to as efficient (but, of course, correct) machine code it can. Arranging a loop so, that detecting ZF (Zero Flag) as a loop terminating condition, instead of explicit comparison with a const value, is one way to improve efficiency. Therefore, duty 1) is solved by having a decrementing loop control, register esi in this case (see disassembly below).

    For the second duty, the esi register can not be used, as it counts in the wrong direction. So, another register, ebx is used for duty 2). It is setup as a pointer to the first element of the array (the element with index 1). Then at every round in the loop ebx is incremented to point to the next element.

    Here's a disassembly of the PrintCards() procedure:

    Project2.dpr.38: begin
    0040876C 53               push ebx
    0040876D 56               push esi
    0040876E 57               push edi
    0040876F 8BF8             mov edi,eax
    
    Project2.dpr.39: for j := 1 to 21 do
    00408771 BE15000000       mov esi,$00000015    // Initialize loop control
    
    00408776 8BDF             mov ebx,edi          // set up pointer to array
    Project2.dpr.40: writeln(shuffledCards[j]);
    00408778 A160934000       mov eax,[$00409360]  // loop start
    0040877D 8B13             mov edx,[ebx]
    0040877F E8B4B8FFFF       call @WriteOLString
    00408784 E8CFA5FFFF       call @WriteLn
    00408789 E8B69EFFFF       call @_IOTest
    
    0040878E 83C304           add ebx,$04          // advance array element pointer
    Project2.dpr.39: for j := 1 to 21 do
    00408791 4E               dec esi              // decrement loop control
    00408792 75E4             jnz -$1c             // jump if not zero to loop start
    
    Project2.dpr.41: end;
    00408794 5F               pop edi
    00408795 5E               pop esi
    00408796 5B               pop ebx
    00408797 C3               ret
    

    Put a break point on line 39 and then run. When stopped at line 39, call up the CPU view (View - Debug Windows - CPU or Ctrl-Alt-C). Then single step (F8) and follow the changes of the registers, to see for yourself

    The debugger watch for j shows the value of esi, therefore you see the value going down from 21 to 1 (actually you can see the final 0 show up right after the last time dec esi is executed).