Help with getting Cluster Size 
Author Message
 Help with getting Cluster Size

The following code will retrieve the cluster size of the current drive:

  uses dos;

  var r:registers;

  begin
    r.ah:=$1C
    r.dl:=$00
    intr($21,r);
    write('Cluster size: ',r.ah*r.cx);
  end.

In this case, why does the following translation to raw ASM provide me
with runtime error 005?

  var pal:byte; pcx:word;

  begin
    asm
       mov ah,1ch
       mov dl,00h
       int 21h
       mov pal,al
       mov pcx,cx
    end;
    write('Cluster size: ',pcx*pal);
  end.

I'm doing exactly the same thing, aren't I?  My goal here is merely to
avoid using the DOS unit, which is required of the first method.

Any help would be appreciated.

Brian
SPAMBLOCK: remove ``SHOOSPAM'' from address if replying via e-mail.



Wed, 18 Jun 1902 08:00:00 GMT  
 Help with getting Cluster Size

Probably you will have to push DS and push BP as a minimum
before you enter int 21H?
Cheers, Franz Glaser, Austria
http://members.eunet.at/meg-glaser



Wed, 18 Jun 1902 08:00:00 GMT  
 Help with getting Cluster Size

Quote:

> See, the function alters DS. That is a no-no in TP. You need to use:

>   var pal:byte; pcx:word;

>   begin
>     asm
>       mov ah,1ch
>       mov dl,00h
>       push ds
>       int 21h
>       pop ds
>       mov pal,al
>       mov pcx,cx
>       mov pdx,dx
>     end;
>     write(^M^J'Cluster size: ',pcx*pal,
>           ^M^J'Bytes on disk: ',(pcx*pal)*pdx);
>   end.

That took care of it.  Thanks!  However, I've run into another small
obstacle, and I'm wondering if you wouldn't mind looking this over?  :-)

I decided to add another variable to the procedure, and have modified
the quoted text above to reflect my change.  The change is the addition
of a variable called "pdx", which is set to equal the content of the dx
register in the basm body (dx = total clusters on disk).

When I try to get the total disk size in bytes with "(pcx*pal)*pdx",
the result is an arithmatic overflow.  Turbo Pascal is apparently taking
the largest variable type present (word) and limiting the result of the
calculation to a 16 bit number, even though the result will be large
enough to constitute a longint.  Thus, an overflow occurs.  In order to
make this calculation work I've had to throw a longint() around around
one of the variables in order for the proper answer to be generated.
I don't understand why this is.  I realize that pal is a BYTE variable
while pcx is a WORD variable, and that the numer I'm calculating will
qualify as a LONGINT.  However, since I'm not setting the result TO a
byte or word variable, why would I get an arithmatic overflow?  I can
do pretty much the same thing as follows, but THIS works fine:

   var b1,b2:byte;

   begin
     b1:=255;
     b2:=255;
     write(b1+b2);  { prints "510" }
   end.

If the answer calculated here (510) is beyond the realm of an 8 bit byte
variable, why is it that when calculating an answer via (pcx*pal)*pdx--
which is also beyond the realm of the largest variable (a word)--I get
an arithmatic overflow?

I hope I'm being clear enough on this.  <g>  Thanks in advance.

Brian
SPAMBLOCK: remove ``SHOOSPAM'' from address if replying via e-mail.



Wed, 18 Jun 1902 08:00:00 GMT  
 Help with getting Cluster Size


Quote:

>The following code will retrieve the cluster size of the current drive:

>  uses dos;

>  var r:registers;

>  begin
>    r.ah:=$1C
>    r.dl:=$00
>    intr($21,r);

Mtw there is a procedure called MsDos() for Intr($21..)

Quote:
>    write('Cluster size: ',r.ah*r.cx);
>  end.

>In this case, why does the following translation to raw ASM provide me
>with runtime error 005?

>  var pal:byte; pcx:word;

>  begin
>    asm
>       mov ah,1ch
>       mov dl,00h
>       int 21h
>       mov pal,al
>       mov pcx,cx
>    end;
>    write('Cluster size: ',pcx*pal);
>  end.

>I'm doing exactly the same thing, aren't I?  My goal here is merely to
>avoid using the DOS unit, which is required of the first method.

--------D-211C-------------------------------
INT 21 - DOS 1+ - GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE
        AH = 1Ch
        DL = drive (00h = default, 01h = A:, etc)
Return: AL = sectors per cluster (allocation unit), or FFh if invalid drive
        CX = bytes per sector
        DX = total number of clusters
        DS:BX -> media ID byte (see AH=1Bh)
Notes:  under DOS 1.x, DS:BX points at an actual copy of the FAT; later
          versions return a pointer to a copy of the FAT's ID byte
        on a DBLSPACE drive, the total number of clusters is based on the
          estimated compression ratio
SeeAlso: AH=1Bh,AH=36h

See, the function alters DS. That is a no-no in TP. You need to use:

Quote:
>  var pal:byte; pcx:word;

  begin
    asm
       mov ah,1ch
       mov dl,00h
       push ds
       int 21h
       pop ds
       mov pal,al
       mov pcx,cx
    end;
    write('Cluster size: ',pcx*pal);
  end.

Osmo



Wed, 18 Jun 1902 08:00:00 GMT  
 Help with getting Cluster Size

Quote:

> [...]
> That took care of it.  Thanks!  However, I've run into another small
> obstacle, and I'm wondering if you wouldn't mind looking this over?  :-)

I suppose I could chip my two cents in. . . .

Quote:
> I decided to add another variable to the procedure, and have modified
> the quoted text above to reflect my change.  The change is the addition
> of a variable called "pdx", which is set to equal the content of the dx
> register in the basm body (dx = total clusters on disk).

> When I try to get the total disk size in bytes with "(pcx*pal)*pdx",
> the result is an arithmatic overflow.  Turbo Pascal is apparently taking
> the largest variable type present (word) and limiting the result of the
> calculation to a 16 bit number, even though the result will be large
> enough to constitute a longint.  Thus, an overflow occurs.  In order to
> make this calculation work I've had to throw a longint() around around
> one of the variables in order for the proper answer to be generated.
> I don't understand why this is.  I realize that pal is a BYTE variable
> while pcx is a WORD variable, and that the numer I'm calculating will
> qualify as a LONGINT.  However, since I'm not setting the result TO a
> byte or word variable, why would I get an arithmatic overflow?  I can
> do pretty much the same thing as follows, but THIS works fine:

Operations aren't determined by the size of the result, but by the size of the
operands present.  The Language Guide lays out the following rules:

"Arithmetic operations with integer-type operands use 8-bit, 16-bit, or 32-bit
precision, according to the following rules:

- The type of an integer constant is the predefined integer type with the
  smallest range that includes the value of the integer constant.

- For a binary operator (an operator that takes two operands), both operands
  are converted to their common type before the operation.  The common type
  is the predefined integer type with that smallest range that includes all
  possible values of both types.  For example, the common type of Integer
  and Byte is Integer, and the common type of Integer and Word is Longint.
  The operation is performed using the precision of the common type, and the
  result is the common type.

- The expression on the right of an assignment statment is evaluated
  independently from the size or type of the variable on the left.

- Any byte-sized operand is converted to an intermediate word-sized operand
  that is compatible with both Integer and Word before any aritmetic
  operation is performed."

Basically, what's happening is your evaluation is trying to behave as a word
evaluation, even though the result won't fit into a word.  You were right in
typecasting.  If you rewrite your assigment as:

  longvar := longint(pcx)*pal*pdx;

this makes the common type longint and avoids the possible overflow.  Note
that:

  longvar := longint(pcx*pal)*pdx;

would be incorrect, since pcx*pal would be evaluated *before* the typecast --
if that overflows, the typecast won't help.

Quote:
>    var b1,b2:byte;

>    begin
>      b1:=255;
>      b2:=255;
>      write(b1+b2);  { prints "510" }
>    end.

> If the answer calculated here (510) is beyond the realm of an 8 bit byte
> variable, why is it that when calculating an answer via (pcx*pal)*pdx--
> which is also beyond the realm of the largest variable (a word)--I get
> an arithmatic overflow?

Because in a simple calculation like this, byte values are automatically
converted to an internal signed 16-bit value.  You're using the result, but
you're not assigning it to any variable that could overflow, so it's going to
be contained to the internal 16-bit promotion.  If you were to create a third
byte variable (we'll call it b3) then write "b3 := b1+b2;" a range check error
would be generated (assuming range checking is enabled).

Quote:
> I hope I'm being clear enough on this.  <g>  Thanks in advance.

Hopefully this make some sense of why you noticed happening what you did.  :-)

Quote:
> Brian
> SPAMBLOCK: remove ``SHOOSPAM'' from address if replying via e-mail.

--
Scott Earnest        | We now return you to our regularly |



Wed, 18 Jun 1902 08:00:00 GMT  
 Help with getting Cluster Size


Quote:


>> See, the function alters DS. That is a no-no in TP. You need to use:

>>   var pal:byte; pcx:word;

>>   begin
>>     asm
>>       mov ah,1ch
>>       mov dl,00h
>>       push ds
>>       int 21h
>>       pop ds
>>       mov pal,al
>>       mov pcx,cx
>>       mov pdx,dx
>>     end;
>>     write(^M^J'Cluster size: ',pcx*pal,
>>           ^M^J'Bytes on disk: ',(pcx*pal)*pdx);
>>   end.

>That took care of it.  Thanks!  However, I've run into another small
>obstacle, and I'm wondering if you wouldn't mind looking this over?  :-)

>I decided to add another variable to the procedure, and have modified
>the quoted text above to reflect my change.

Do not ever do that again. The quoted text is my words. You have no
right to tamper with it.

Quote:
>  The change is the addition
>of a variable called "pdx", which is set to equal the content of the dx
>register in the basm body (dx = total clusters on disk).

>When I try to get the total disk size in bytes with "(pcx*pal)*pdx",
>the result is an arithmatic overflow.  Turbo Pascal is apparently taking
>the largest variable type present (word) and limiting the result of the
>calculation to a 16 bit number, even though the result will be large
>enough to constitute a longint.

Of course. The result of multiplication between two words is naturally
a word.

Quote:
> Thus, an overflow occurs.  In order to
>make this calculation work I've had to throw a longint() around around
>one of the variables in order for the proper answer to be generated.
>I don't understand why this is.  I realize that pal is a BYTE variable
>while pcx is a WORD variable, and that the numer I'm calculating will
>qualify as a LONGINT.  However, since I'm not setting the result TO a
>byte or word variable, why would I get an arithmatic overflow?

Because word and longint multiplications are completely different
things. The first is just a processor instruction whereas the second
cases a subroutine call. If you want the latter then you need to specify
so. The compiler cannot read your mind and decide what you want.

Note that in case of multiplying just two numbers producing a longint
result would be easy as the mul produces 32-bit result (TP just discards
the high 16 bits) but as in your case where you have three
multiplications the second would have to be done with longint anyway.

Finally  the type of the variable (if any) where you store the result
has noting whatsoever to do with what kind of arithmetic is done. So if
you have:

var x,y:integer;
    z:longint;

...
  z:=x*y the compiler produces following code

PROGRAM.6:  z:=x*y;
  cs:000F A15000         mov    ax,[PROGRAM.X]
  cs:0012 F7265200       mul    word ptr [PROGRAM.Y]
  cs:0016 99             cwd
  cs:0017 A35400         mov    [PROGRAM.Z],ax
  cs:001A 89165600       mov    [0056],dx

Now take away that "cwd" and the result stored in DX would be perfectly
OK. Now it makes sure that DX is 0 on positive and $ff on negative
values in AX.

This is sometimes annoying but it is how Pascal should work. It is not
a bug. (Of course there should be error checking. So lets try that again
with $Q+

PROGRAM.8:  z:=x*y;
  cs:000F A15000         mov    ax,[PROGRAM.X]
  cs:0012 F72E5200       imul   word ptr [PROGRAM.Y]
  cs:0016 7105           jno    001D
  cs:0018 9AC7025D5A     call   5A5D:02C7
  cs:001D 99             cwd
  cs:001E A35400         mov    [PROGRAM.Z],ax
  cs:0021 89165600       mov    [0056],dx

Now if you want do do the multiplication correctly there are two options
here. First the typecast that you said. Lets have it (without error
checking)

PROGRAM.8:  z:=longint(x)*y;
  cs:000F A15200         mov    ax,[PROGRAM.Y]
  cs:0012 99             cwd
  cs:0013 8BC8           mov    cx,ax
  cs:0015 8BDA           mov    bx,dx
  cs:0017 A15000         mov    ax,[PROGRAM.X]
  cs:001A 99             cwd
  cs:001B 9A99045D5A     call   5A5D:0499
  cs:0020 A35400         mov    [PROGRAM.Z],ax
  cs:0023 89165600       mov    [0056],dx

See, now both integers are first converted as longints (cwd) and then
subroutine that does the longint multiplication is called. All this when
the first code without the cwd would have done it.

The second way is to get dirty with inline functions.

Function Imul(x,y:integer):longint;
  inline(
          $5B/              {POP     BX}
          $58/              {POP     AX}
          $F7/$EB           {IMUL    BX}
         );
...

z:=imul(x,y);

PROGRAM.14:  z:=imul(x,y);
  cs:000F FF365000       push   word ptr [PROGRAM.X]
  cs:0013 FF365200       push   word ptr [PROGRAM.Y]
  cs:0017 5B             pop    bx
  cs:0018 58             pop    ax
  cs:0019 F7EB           imul   bx
  cs:001B A35400         mov    [PROGRAM.Z],ax
  cs:001E 89165600       mov    [0056],dx

Not the most elegant but better than the typecast method. Note if you add
this by z:=imul(x,y)*x the second multiply will naturally be longint
one.

Here are two other nice things. First word multiplication

Function Wmul(x,y:word):longint;
  inline(
          $5B/              {POP     BX}
          $58/              {POP     AX}
          $F7/$E3           {MUL     BX}
         );

And  then multiplication followed by division, like x*10 div 17
(wmuldiv(x,10,17)) so that the intermediate result is 32 bits.

Function WmulDiv(x,y,z:word):word;
  inline(
          $59/             {POP     CX}
          $5B/             {POP     BX}
          $58/             {POP     AX}
          $F7/$E3/         {MUL     BX}
          $F7/$F1          {DIV     CX}
         );

Now maybe TP should have some relaxed way for multiplication. That is
the result of integer/word multiplication would be something between
longint and integer/word. If the result was assigned to longint then it
would become true longint. If it was followed by division then it would be
used as longint but the result of the division would be integer/word.
Otherwise it would be treated as word (including in your code as it was
followed by multiplication). This would be non-standard, but it would
better fit the 8086 architecture.

Quote:
> I can
>do pretty much the same thing as follows, but THIS works fine:

>   var b1,b2:byte;

>   begin
>     b1:=255;
>     b2:=255;
>     write(b1+b2);  { prints "510" }
>   end.

>If the answer calculated here (510) is beyond the realm of an 8 bit byte
>variable, why is it that when calculating an answer via (pcx*pal)*pdx--
>which is also beyond the realm of the largest variable (a word)--I get
>an arithmatic overflow?

That is interesting. The compiler is here truly inconsistent. There are
two explanations. First 8086 is 16-bit processor, so there is no penalty
for doing arithmetic in 16 bits instead of 8 bits. Same cannot be said
with 32 bits vs. 16. Second this may have to do something with longints
being later addition to TP. (This is just guessing though)

Osmo



Wed, 18 Jun 1902 08:00:00 GMT  
 Help with getting Cluster Size


Quote:


>> See, the function alters DS. That is a no-no in TP. You need to use:

>>   var pal:byte; pcx:word;

>>   begin
>>     asm
>>       mov ah,1ch
>>       mov dl,00h
>>       push ds
>>       int 21h
>>       pop ds
>>       mov pal,al
>>       mov pcx,cx
>>       mov pdx,dx
>>     end;
>>     write(^M^J'Cluster size: ',pcx*pal,
>>           ^M^J'Bytes on disk: ',(pcx*pal)*pdx);
>>   end.

Of course you could do the arithmetic in asm:

   var cluster:word;
       dsize:longint;

   begin
     asm
       mov ah,1ch
       mov dl,00h
       push ds
       int 21h
       pop ds
       mov bx,dx   { save dx }
       xor ah,ah
       mul cx
       mov cluster,ax    {lets assume dx=0}
       mul bx
       mov word ptr dsize,ax
       mov word ptr dsize+2,dx
     end;

(not tested)

Osmo



Wed, 18 Jun 1902 08:00:00 GMT  
 Help with getting Cluster Size



Quote:

>this makes the common type longint and avoids the possible overflow.  Note
>that:

>  longvar := longint(pcx*pal)*pdx;

>would be incorrect, since pcx*pal would be evaluated *before* the typecast --
>if that overflows, the typecast won't help.

Actually it cannot overflow as it is the cluster size. There hardly are
larger than 64K clusters.

Osmo



Wed, 18 Jun 1902 08:00:00 GMT  
 
 [ 8 post ] 

 Relevant Pages 

1. Cluster Size

2. Cluster Size of Disk?

3. cluster size?

4. cluster size?

5. TP7 DiskFree() returns 0 when cluster size = 64K

6. Find Cluster size inn hard drive via Pascal

7. Getting drive sizes larger than 2 gigs...

8. FAT32 - Getting size of partitions greater than 2GB

9. Getting size of a TBlobField?

10. Getting the Block Size of a table

11. HELP: Start cluster of a file

12. WANT to read a sector/cluster from FAT32 HD

 

 
Powered by phpBB® Forum Software