Help with getting Cluster Size
Author |
Message |
SHOOSPAMpseudo.. #1 / 8
|
 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 |
|
 |
Ing. Franz Glase #2 / 8
|
 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 |
|
 |
SHOOSPAMpseudo.. #3 / 8
|
 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 |
|
 |
Osmo Ronkan #4 / 8
|
 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 |
|
 |
Scott Earnes #5 / 8
|
 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 |
|
 |
Osmo Ronkan #6 / 8
|
 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 |
|
 |
Osmo Ronkan #7 / 8
|
 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 |
|
 |
Osmo Ronkan #8 / 8
|
 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 |
|
|
|