Hardware

Hardware

Determine a CD-Rom-Drive
Need help on overriding Sys Err
Hard disk serial number
System Ram / CMOS
Disk Serial Numbers
Reading a byte from the parallel port
Getting disk information
Setting PC Clock
Detecting Disk in Drive A:
Port and mem
Managing disk volume labels
How to check if a drive is ready
Detecting a Pentium processor
Determining drive type


Determine a CD-Rom-Drive

Question


Q:

How can i determine if a CD-ROM drive is on the machine. I tried using the WIN API GetDriveType but it return the same number (4) if it is a CD-ROM or a network drive.



Answer


A:

This should do the trick..



function IsCDROM(DriveNum: Integer): Boolean; assembler;

asm

  MOV   AX,1500h { look for MSCDEX }

  XOR   BX,BX

  INT   2fh

  OR    BX,BX

  JZ    @Finish

  MOV   AX,150Bh { check for using CD driver }

  MOV   CX,DriveNum

  INT   2fh

  OR    AX,AX

  @Finish:

end;



BTW under Win32 GetDriveType properly returns a CD-ROM drive.



A:

Function IsCdRom(DriveNum : Word) : Boolean;

Var

   F : WordBool;

Begin

   asm

      mov ax, 1500h      { test for presence of MSCDEX }

      xor bx, bx

      int 2fh

      mov ax, bx         { if bx = zero, MSCDEX is not present }

      or  ax, ax         { return FALSE }

      jz  @no_mscdex

      mov ax, 150bh      { MSCDEX drive check }

      mov cx, DriveNum   { cx contains drive }

      int 2fh

      @no_mscdex:

      mov f,ax

   end;

   Result := F;          { Assign function return value }

End;




Need help on overriding Sys Err

Question


Want the app. to read and recognize a drive not ready (i.e., user selects a

floppy drive with no disk in drive) -  then want the app to display the error

box rather than Windows System Error display box.

Answer


A:

Here's a routine that will do the trick for diskette drives:



 function DisketteDriveReady (DisketteDrive: char): boolean;

{----------------------------------------------------------------}

{ Returns true if specified Diskette drive [A/a or B/b] is ready }

{ with a diskette inserted, otherwise false.  From a Delphi-Talk }

{ posting by Per Ola Svensson    }

{----------------------------------------------------------------}

  var

   Drive: byte;

   PrevInt24, DiscardReturnValue: word;

 begin

   DisketteDriveReady := false;    {until proven otherwise}

   case DisketteDrive of

     'A', 'a':  Drive := 1;

     'B', 'b':  Drive := 2;

     else Exit;

   end; {case}

   PrevInt24 := SetErrorMode(SEM_FAILCRITICALERRORS);

   if DiskFree(Drive) <> -1 then

     DisketteDriveReady := true;

   DiscardReturnValue := SetErrorMode(PrevInt24);

 end; {DisketteDriveReady}


Hard disk serial number

Question


Is there anyone who knows how to obtain the hard disk serial number?

Answer


I have an unit to get the Hd Name and the Hd Serial number for Borland

Pascal 7.0. I don't know if it's working with Delphi and the other thing is

that it is in dutch and I don't have time to translate it in englisch. Mayby

you can use it, otherwise drop it out of the window.



Unit HardDisk;



INTERFACE



FUNCTION  GetHardDiskNaam               : STRING;

FUNCTION  GetHardDiskSerieNummer        : STRING;

FUNCTION  GetHardDiskControlleNummer    : STRING;

PROCEDURE GetHardDiskGegevens;



CONST

  CodeerTabel : ARRAY[0..24] OF BYTE =

(3,1,2,1,4,1,3,2,6,4,6,5,1,2,6,4,2,6,3,4,6,2,4,1,2);



TYPE

  CharArray = ARRAY[0..24] OF CHAR;



VAR

  HardDiskGegevens          : ARRAY[1..256] OF INTEGER;

  HardDiskNaam              : CharArray;

  SerieNummer               : CharArray;

  ControlleNummer           : CharArray;

  C_HardDiskNaam            : STRING;

  C_HardDiskSerieNummer     : STRING;

  C_HardDiskControlleNummer : STRING;

  C_LicentieNaam            : STRING;



IMPLEMENTATION



 FUNCTION GetHardDiskNaam : STRING;

 VAR

   Teller : INTEGER;

   Lus    : INTEGER;

 BEGIN

    GetHardDiskNaam := '';

    Teller := 1;

    FOR Lus := 1 TO 18 DO

    BEGIN

      HardDiskNaam[Teller] := CHR( ( HardDiskGegevens[27+Lus] DIV 256 ));

      Inc(Teller);

      HardDiskNaam[Teller] := CHR( ( HardDiskGegevens[27+Lus] MOD 256 ));

      Inc(Teller);

    END;

    GetHardDiskNaam := HardDiskNaam;

 END;



 FUNCTION GetHardDiskSerieNummer : STRING;

 VAR

   Teller : INTEGER;

   Lus    : INTEGER;

 BEGIN

    GetHardDiskSerieNummer := '';

    Teller := 1;

    FOR Lus := 1 TO 8 DO

    BEGIN

      SerieNummer[Teller] := CHR( ( HardDiskGegevens[10+Lus] DIV 256 ));

      Inc(Teller);

      SerieNummer[Teller] := CHR( ( HardDiskGegevens[10+Lus] MOD 256 ));

      Inc(Teller);

    END;

    GetHardDiskSerieNummer := SerieNummer;

 END;



 FUNCTION GetHardDiskControlleNummer : STRING;

 VAR

   Teller : INTEGER;

   Lus    : INTEGER;

 BEGIN

    GetHardDiskControlleNummer := '';

    Teller := 1;

    FOR Lus := 1 TO 3 DO

    BEGIN

      ControlleNummer[Teller] := CHR( ( HardDiskGegevens[23+Lus] DIV 256 ));

      Inc(Teller);

      ControlleNummer[Teller] := CHR( ( HardDiskGegevens[23+Lus] MOD 256 ));

      Inc(Teller);

    END;

    GetHardDiskControlleNummer := ControlleNummer;

 END;



 PROCEDURE GetHardDiskGegevens;

 VAR

   Lus    : INTEGER;

 BEGIN

   WHILE ( Port[$1f7] <> $50) DO ;

   Port[$1F6] := $A0 ;

   Port[$1F7] := $EC ;

   WHILE ( Port[$1f7] <> $58 ) DO ;

   FOR Lus := 1 TO 256 DO

   BEGIN

     HardDiskGegevens[Lus] := Portw[$1F0] ;

   END;

 END;



END.





A:

unit Chiunit4;



interface



function Chk...(ParamIn ... ,=20

         ParamDatabaseNamePchar: pchar ): longint; export;



implementation



uses  SysUtils, DBTables, ExtCtrls ;



const

  ide_drive_C           =3D $00A0;

  ide_Data              =3D $1F0;

  ide_Error             =3D $1F1;

  ide_DriveAndHead      =3D $1F6;

  ide_Command           =3D $1F7;

  ide_command_readpar   =3D $EC;

  ide_Status            =3D $1F7;

  ide_status_busy       =3D $80;

  ide_status_ready      =3D $40;

  ide_status_error      =3D $01;

  ide_Fixed             =3D $3F6;

  ide_Fixed_Irq         =3D $02;



  IntervalleMinimum  =3D 0.0000232;

  { 0.000011574 =3D 1 seconde (.0001 (hh.mmss) (->DEG=3D.0002777) / 24) }

  { .0000174 =3D 1 1/2 sec }  { .0000232 =3D 2 sec }



type

  tIdeRec =3D Record

    rec : array[0..255] of word;

  end;



var

  ExitSave :  Pointer;

  IdeRec :    tIdeRec;



function ConvertToString : string;

var

   i,j : integer;

begin

     FillChar( Result, 20, ' ' ); Result[0] :=3D #20;

     for i :=3D 1 to 20 do

       begin

         j :=3D Trunc( (i-1) /2 )  +10 ;

         if Lo(IdeRec.Rec[j]) =3D (0)

            then Result[i]:=3D ' '

            else

            Result[i]:=3D Chr ( Lo( IdeRec.Rec[j] ) ) ;

         i :=3D i +1;

         if Hi(IdeRec.Rec[j]) =3D (0)

            then Result[i]:=3D ' '

            else

            Result[i]:=3D Chr ( Hi( IdeRec.Rec[j] ) ) ;

       end;

end;



function DoIt(Numero: string) : longint;

var

  portchar    :byte;

  boo         :Boolean;

  i           :integer;

  S,S1        :String;

begin

  Result:=3D 19 ; { fail per default }

  FillChar( IdeRec.Rec, 512, ' ' ) ;



  { en premier lieu v=E9rifier l'=E9tat }

         boo :=3D true;

         { poll DRQ wait }

         i :=3D 5000 ;

         repeat

           i :=3D i -1;

           portchar :=3D Lo(port[ide_status]) ; { get status }

         until

         ( i < 1 ) or not

         ( ( portchar AND ide_status_busy ) =3D ide_status_busy ) ;

         if i < 1 then

              begin

                 { Result:=3D 'status allways busy'; }

                 Result :=3D 180 ;

                 boo :=3D false;

              end;



  if boo then

  try

    { premi=E8rement disable drive interrupts }

  port[ide_Fixed] :=3D 0;



  port[ide_DriveAndHead] :=3D ide_drive_C ;  { set drive }

  portchar :=3D Lo(port[ide_status]) ; { get status }

  if portchar =3D $ff then begin

                         { Result:=3D 'set drive status $ff'; }

                         Result :=3D 11 ;

                         boo :=3D false;

                         end;



  if boo then

     begin

         { poll DRQ wait }

         i :=3D 1024 ;

         repeat

           i :=3D i -1;

           portchar :=3D Lo(port[ide_status]) ;

         until

         ( i < 1 ) or not

         ( ( portchar AND ide_status_busy ) =3D ide_status_busy ) ;

         if i < 1 then

              begin

                 { Result:=3D 'status allways busy'; }

                 Result :=3D 181 ;

                 boo :=3D false;

              end;

     end;



  if boo then

         { check if ready }

         if ( portchar AND ide_status_ready ) =3D 0

            then begin

                 { Result:=3D 'set drive status not ready'; }

                 Result :=3D 12 ;

                 boo :=3D false;

                 end;



  if boo then

         { ok now want to readIDE }

         { send ReadParameters command }

         port[ide_Command] :=3D ide_command_readpar ;



         { poll DRQ wait }

         i :=3D 5000 ;

         repeat

           i :=3D i -1;

           portchar :=3D Lo(port[ide_status]) ;

         until

         ( i < 1 ) or not

         ( ( portchar AND ide_status_busy ) =3D ide_status_busy ) ;

         if i < 1 then

              begin

                 { Result:=3D 'status allways busy'; }

                 Result :=3D 182 ;

                 boo :=3D false;

              end;



  if boo then

         { check if no error}

         if ( portchar AND ide_status_error ) =3D ide_status_error

            then begin

                 { Result:=3D 'drive status error after ReadPar'; }

                 Result :=3D 13 ;

                 boo :=3D false;

                 end;



  if boo then

         { check if ready }

         if ( portchar AND ide_status_ready ) =3D 0

            then begin

                 { Result:=3D 'after ReadPar drive status not ready'; }

                 Result :=3D 14 ;

                 boo :=3D false;

                 end;



  if boo then

        try

        { ok now read the buffer 256 word }

         for i :=3D 0 to 255 do

             begin

             IdeRec.Rec[i] :=3D ( portw[ide_Data] ) ;

             end;

        except

          on Exception do begin

                          { ShowMessage( 'Erreur portw i=3D '+intToStr(i)=

 ) ; }

                          boo :=3D false;

                          Result :=3D 15 ;

                          end;

          else begin

               boo :=3D false;

               Result :=3D 16 ;

               raise;

               end;

        end;



  if boo Then

     begin

      S :=3D ConvertToString;

      if length(Numero) < 20 then S1:=3D Numero +'                    '

                             else S1:=3D Numero;

      if CompareStr ( S, Copy(S1,1,20) ) =3D 0

         then Result :=3D 10

         else Result :=3D 17 ;

             { Result :=3D '('+S+')<>('+Copy(S1,1,20)+')' ; }

     end;

  finally

  { re-enable disk interrupts }

  port[ide_Fixed] :=3D ide_Fixed_Irq ;

  end;

END;



procedure MyExit; far;

{ reset disk parameters so other disk operations won't be desturbed in ca=

se

  of program abort }

begin

  ExitProc :=3D ExitSave;        { restore previous exitproc }

{  Port[ide_Command]:=3D$10;      { send command: reset current drive }

end;



function GetParam(ParamAlias: string): String;

var

   i : integer ;

   t : TTable ;

   S : String ;

begin

 Result :=3D '';

 try

  t :=3D nil;

  t :=3D TTable.Create(nil);

  t.DatabaseName :=3D ParamAlias;

  t.TableName :=3D  ...;

  t.TableType :=3D ttPARADOX;

  t.open;

...

 finally

  if Assigned(t) then t.free ;

 end;

end;



function FixParam(ParamAlias: string): boolean;

var

   i : integer ;

   t : TTable ;

   S : String ;

begin

 Result :=3D False;

 try

  t :=3D nil;

  t :=3D TTable.Create(nil);

  t.DatabaseName :=3D ParamAlias;

  t.TableName :=3D  ;

  t.TableType :=3D ttPARADOX;

  t.open;

  if=20

    begin

...         t.Edit;

         t.setFields([nil, S]);

         t.post;

    end;

  t.close;

  Result :=3D True;

 finally

  if Assigned(t) then t.free ;

 end;

end;



  {----------------------------------------------------}



function Chk...(ParamIn: ;

                  ParamDatabaseNamePchar: pchar ): longInt ;

var

   ParamString :  String; =20

   Temps :        Real;

   Ok :           boolean;

   i:             integer;

   S :            string[20];

   S6 :           string[6];

   r :            longInt;



Label

     Jump;

BEGIN

  Result:=3D 0 ;  { par d=E9faut }

 if Ok then

       i :=3D 0;

       repeat

           begin

           i :=3D i +1 ;

           r :=3D DoIt(Copy(ParamString,54,20)) ;

           if r =3D 10 then begin

                          Ok :=3D True ;

                          break

                          end

                     else begin

                          Ok :=3D False ;

                          Result:=3D r;

                          Continue;

                          end;

           end;

       until i =3D 3 ;

  If Ok

     then begin

          Ok :=3D FixParam(ParamDatabaseName) ;

          If Ok then else { Result :=3D 'FixParam fail'; }

                          Result :=3D 2 ;

          end;

  If Ok then Result :=3D 1 ;

END;



Begin

  ExitSave:=3D ExitProc;

  ExitProc:=3D @MyExit;

end.


System Ram / CMOS

Question


Does anyone have any source code for extracting from the BIOS or any

other way the amount of RAM fitted to a system.

Answer


A:

You can try this... It will return the amount of extended mem from

the CMOS (Total K's minus the first Mb).



  Function MyGetExt: Integer; Assembler;

  asm

    Mov  AX,$3031;

    Out  $70,AL;

    NOP;

    IN   AL,$71;

    XCHG AH,AL;

    Out  $70,AL;

    NOP;

    IN   AL,$71;

  end;





A:

Attention CMOS-busters:



Here's what's happening for those not so sharp on their assembly language

directives.



To read from CMOS do the following:



        write to port $70 with the address value to read or write.

        write to port $71 with the new value or read the value of interest

from port $71.



CMOS is reasnably generic in some locations.  Most noteable are the

locations $0-$F, (the time,date and timer settings) and the locations from

$10-$1F, (general system settings).  The locations above $20 are usually

more subject to the whims of mfg's than the others which are reasonably

standard.



My example will be in C as ASM was just represented!  I'll get to Delphi in

a minute.



write to port $70 (hex) with the address value of the CMOS memory location

that you are interested



outp(0x70,0x31);     { tells CMOS we want to 'talk' to CMOS location 31hex }



next, read the desired value at port $71 or write a new value to the port.



outp(0x71, 0x10);      { this writes the value 10 hex (16

dec) to CMOS location 31as previously defined }



                        OR



x = inp(0x71);             { this reads the hex value from

CMOS location 31 as previously declared }



it is also good form to include a delay between the write to

port 70 and the read or write at port 71.  This is more important in

assembly routines and maybe real fast cpu's then in higher level languages.

That's the purpose of the NOP in Leif's code example and also generally

recommended.



You will note that in Leif's ASM code two locations are stuffed into the

AX, 30 and 31.  This is to allow for a quick load to search two addresses.

This code contains two instances, both CMOS reads.  The memory values are

stored in two consecutive locations but I believe it's a case of most

significant byte last.  And they also only concern the amount of memory

beyond 1 Mb as also stated below.



OK, ok, I know this is supposed to be Delphi but I'm a C programmer

reasonably new to Delphi and Pascal so to do this in Delphi probably

involves using Port or PortW but I haven't tried it yet so don't know exact

syntax.  Probably pretty similar to C style though.  Would be interested in

hearing from anyone that has done it that way.  Couldn't really find an

example yet and haven't had time for exploring.



Of a rather important note, it should be pointed out that this method will

only read so much memory.  Back in the old days, no one considered the vast

amounts of RAM available today system-wise and I believe that certain large

sizes of RAM will not be correctly loaded into CMOS.  Therefore this is not

a cure-all concept.  Some of this is caused by BIOS

shortcomings/short-sightings.  Also, you will need a CMOS map to show what

locations are loaded for which system parameters.  Some differences can

exist between different mfg's.  This can be a rather messy job...



Here's the most important thing for budding CMOS editors to remember:



       IF you edit a location between $10 and $2F then you WILL NEED to

calculate the NEW checksum by adding together all the location values from

$10-$2F and THEN WRITE the new value to $3E, $3F or YOU WILL GET A CMOS

CHECKSUM ERROR WHEN YOU REBOOT!  GUARANTEED!!!  THIS should concern you... 



        Also, don't try to test the values $0 - $1F.  Time marches on, don't

you know...



A:

There is no way of getting from the BIOS the total amount fo memory. The

BIOS will only report you that if you have 640K or minus (i.e. 256K...).

Since the real memory is virtualized, expecially in Windows enviroment,

you can only test for the presence of EMS, XMS, or DPMI memory using the

interrupt calls of this services, that reports you the free mem for every

one of this. I don't know if there is a way of know the phisical memory

fitted in the system under Win. Don't result to me there is an API function

providing this; you can only test for the total virtualized memory

(including swap files, etc.). I think under Win95 there is a method.

If you want, I can send you interrupt protocols for obtaining EMS etc., but

I don't think it can be useful to you under Win.


Disk Serial Numbers

Question


It is a C++ Class for reading/writing disk volume labels and serial numbers.

Any C++ gurus out there that can convert to DELPHI?

Answer


A:

This is not a conversion, but here's a Delphi unit with a function

that reads them. To write them,  you can change the AX value to $6901

and fill the buffer with your values before calling the interrupt.  

DOS 4.00+ required.







unit Sernumu;



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls;



type

  TMediaID = Record

    InfoLevel : Word;

    SerialNumber : LongInt;

    VolumeLabel : Array[0..10] of Char;

    SysName : Array[0..7] of Char;

    End;

  TForm1 = class(TForm)

    Button1: TButton;

    Label1: TLabel;

    Label2: TLabel;

    Label3: TLabel;

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

    MediaID : TMediaID;

  public

    { Public declarations }

  end;



var

  Form1: TForm1;



implementation



{$R *.DFM}



type

  DPMIRegisters =

    record

      DI : LongInt;

      SI : LongInt;

      BP : LongInt;

      Reserved : LongInt;

      BX : LongInt;

      DX : LongInt;

      CX : LongInt;

      AX : LongInt;

      Flags : Word;

      ES : Word;

      DS : Word;

      FS : Word;

      GS : Word;

      IP : Word;

      CS : Word;

      SP : Word;

      SS : Word;

    end;





  function RealIntr(IntNo : Byte; var Regs : DPMIRegisters) : Word; Assembler;

  asm

    xor     bx,bx

    mov     bl,IntNo

    xor     cx,cx        {StackWords = 0}

    les     di,Regs

    mov     ax,0300h

    int     31h

    jc      @@ExitPoint

    xor     ax,ax

  @@ExitPoint:

  end;





function GetDiskInfo(Drive : Word; var MediaID : TMediaID) : Boolean;

type

  tLong = Record

    LoWord, HiWord : Word;

    End;

var

  Regs : DPMIRegisters;

  dwAddress : LongInt;

  Address : tLong absolute dwAddress;

begin

  Result := False;

  FillChar(MediaID, SizeOf(MediaID), 0);

  dwAddress := GlobalDosAlloc(SizeOf(MediaID)); { two paragraphs of DOS memory }

  if dwAddress = 0

    then            { address is zero if error occurred }

      exit;



  With Regs do

    begin

      bx := Drive;

      cx := $66;

      ds := Address.HiWord;

      ax := $6900;

      dx := 0;

      es := 0;

      flags := 0;

    end;

  If RealIntr($21, Regs) <> 0

    Then

      Exit;

  Move(ptr(Address.LoWord, 0)^, MediaID, SizeOf(MediaID));

  GlobalDosFree(Address.LoWord)     { free DOS memory block }

  Result := True;

end;







procedure TForm1.Button1Click(Sender: TObject);

begin

  GetDiskInfo(1, MediaID);

  With MediaID do

    Begin

      Label1.Caption := IntToHex(SerialNumber, 8);

      Label2.Caption := VolumeLabel;

      Label3.Caption := SysName;

    End;

end;



end.


Reading a byte from the parallel port

Question


I needed to read a byte from the parallel port (0x379).  I did it using inline

assemble language.  I noticed their is no way of doing it using the Windows API.

I have a sensor attached to this port.  It works fine but, is it safe to address

hardware directly in windows.  Windows is intercepting the call anyway (I

think).



Var

    BytesRead : BYTE;

begin

              asm                { Read port (LPT1) via Inline asm  }

                MOV dx,$379;

                IN  al,dx;

                MOV BytesRead,al;

              end;

BytesRead:=(BytesRead OR $07);   { OR and then XOR the data }

BytesRead:=(BytesRead XOR $80);  { to mask the unused bits  }

Answer


It's no problem, use the Turbo Pascal command ...



 value:=port[$379]; { read from port }



and



 port[$379]:=value; { write to port }



The port command doesn't seem to be documented in online help, but it

certainly works!


Getting disk information

Question


I was tring to get the serial number of a disk using Delphi, but the code does

not seem to work. It works only on a DOS window.

Answer


A:

I didn't find info regarding function 69h but I wrote something using

4409h:



type

  MIDPtr = ^MIDRec;

  MIDRec = Record

    InfoLevel: word;

    SerialNum: LongInt;

    VolLabel: Packed Array [0..10] of Char;

    FileSysType: Packed Array [0..7] of Char;

  end;



function GetDriveSerialNum(MID: MIDPtr; drive: Word): Boolean; assembler;

asm

  push  DS    { Just for safety, I dont think its really needed }

  mov   ax,440Dh { Function Get Media ID }

  mov   bx,drive    { drive no (0-Default, 1-A ...) } 

  mov   cx,0866h  { category and minor code }

  lds   dx,MID      { Load pointeraddr. } 

  call  DOS3Call   { Supposed to be faster than INT 21H } 

  jc    @@err

  mov   al,1           { No carry so return TRUE }

  jmp   @@ok

 @@err:

  mov   al,0           { Carry set so return FALSE }

 @@ok:

  pop   DS            { Restore DS, were not supposed to change it }

end;



procedure TForm1.NrBtnClick(Sender: TObject);

var

  Info: MIDRec;

begin

  Info.InfoLevel:=0; { Information Level }

  If GetDriveSerialNum(@Info,0) then  { Do something with it... }

    ListBox.Items.Add(IntToStr(Info.SerialNum)+' '+Info.VolLabel);

end;


Setting PC Clock

Question


Who can tell me how to SET the pc real time clock from a delphi program?

Answer


A:

{ SetDate sets the current date in the operating system. Valid  }

{ parameter ranges are: Year 1980-2099, Month 1-12 and Day      }

{ 1-31. If the date is not valid, the function call is ignored. }

procedure SetDate(Year, Month, Day: Word); assembler;

asm

	MOV	CX,Year

	MOV	DH,BYTE PTR Month

	MOV	DL,BYTE PTR Day

	MOV	AH,2BH

	INT	21H

end;



{ SetTime sets the time in the operating system. Valid          }

{ parameter ranges are: Hour 0-23, Minute 0-59, Second 0-59 and }

{ Sec100 (hundredths of seconds) 0-99. If the time is not       }

{ valid, the function call is ignored.                          }

procedure SetTime(Hour, Minute, Second, Sec100: Word); assembler;

asm

	MOV	CH,BYTE PTR Hour

	MOV	CL,BYTE PTR Minute

	MOV	DH,BYTE PTR Second

	MOV	DL,BYTE PTR Sec100

	MOV	AH,2DH

	INT	21H

end;



function SetSystemDateTime(Year, Month, Day, Hour, Minute, Second: word): integer;   export;

begin

  SetDate(Year, Month, Day);

  SetTime(Hour, Minute + 1, Second, 0);

  result := 1;

end;


Detecting Disk in Drive A:

Question


I am very new to delphi, so i apologize if this is an easy

question. What i need is to be able to detect if there is a disk in

drive A: and display a message if there is. Obviously, i do not want

the system to do anything if the drive is empty. Every way i have

tried this, i get a Windows Cancel/Retry message appearing when the

drive is empty.

Answer


This function should do the trick.



function DiskInDrive(Drive: Char): Boolean;

var

   ErrorMode: word;



begin

   if Drive in ['a'..'z'] then Dec(Drive, $20);

   if not (Drive in ['A'..'Z']) then

     raise EConvertError.Create('Not a valid drive ID');

   ErrorMode := SetErrorMode(SEM_FailCriticalErrors);

   try

    if DiskSize(Ord(Drive) - $40) = -1 then

      Result := False

    else

      Result := True;

    finally

      SetErrorMode(ErrorMode);

    end;

end;


Port and mem

Question


Does anyone knows how to access the port and mem arrays? The

documentation talks (very little) about them but don't say in what unit

they are.

Answer


For the ports in Delphi 2 I use



procedure OutPort( port :word; value :byte );

begin

  asm

    mov   dx,port

    mov   al,value

    out   dx,al

  end;

end;



function InPort( port :word ) :byte;

begin

  asm

    mov   dx,port

    in    al,dx

    mov   result,al

  end;

end;


Managing disk volume labels

Question


How to manage disk volume labels?

Answer


{

 This document contains the source code for a unit that is useful for

 getting, setting, and deleting volume labels from a floppy or hard disk.

 The code for getting a volume label uses the Delphi FindFirst function,

 and the code for setting and deleting volume labels involves calling DOS

 interrupt 21h, functions 16h and 13h respectively.  Since function 16h

 isn't supported by Windows, it must be called through DPMI interrupt 31h,

 function 300h.

}



unit VolLabel;



interface



uses Classes, SysUtils, WinProcs;



type

  EInterruptError = class(Exception);

  EDPMIError = class(EInterruptError);

  Str11 = String[11];



procedure SetVolumeLabel(NewLabel: Str11; Drive: Char);

function GetVolumeLabel(Drive: Char): Str11;

procedure DeleteVolumeLabel(Drv: Char);



implementation



type

  PRealModeRegs = ^TRealModeRegs;

  TRealModeRegs = record

    case Integer of

      0: (

        EDI, ESI, EBP, EXX, EBX, EDX, ECX, EAX: Longint;

         Flags, ES, DS, FS, GS, IP, CS, SP, SS: Word);

      1: (

        DI, DIH, SI, SIH, BP, BPH, XX, XXH: Word;

        case Integer of

          0: (

            BX, BXH, DX, DXH, CX, CXH, AX, AXH: Word);

          1: (

            BL, BH, BLH, BHH, DL, DH, DLH, DHH,

            CL, CH, CLH, CHH, AL, AH, ALH, AHH: Byte));

  end;



  PExtendedFCB = ^TExtendedFCB;

  TExtendedFCB = Record

    ExtendedFCBflag : Byte;

    Reserved1       : array[1..5] of Byte;

    Attr            : Byte;

    DriveID         : Byte;

    FileName        : array[1..8] of Char;

    FileExt         : array[1..3] of Char;

    CurrentBlockNum : Word;

    RecordSize      : Word;

    FileSize        : LongInt;

    PackedDate      : Word;

    PackedTime      : Word;

    Reserved2       : array[1..8] of Byte;

    CurrentRecNum   : Byte;

    RandomRecNum    : LongInt;

  end;



procedure RealModeInt(Int: Byte; var Regs: TRealModeRegs);

{ procedure invokes int 31h function 0300h to simulate aa real mode }

{ interrupt  from protected mode. }

var

  ErrorFlag: Boolean;

begin

  asm

    mov ErrorFlag, 0       { assume success }

    mov ax, 0300h          { function 300h }

    mov bl, Int            { real mode interrupt to execute }

    mov bh, 0              { required }

    mov cx, 0              { stack words to copy, assume zero }

    les di, Regs           { es:di = Regs }

    int 31h                { DPMI int 31h }

    jnc @@End              { carry flag set on error }

  @@Error:

    mov ErrorFlag, 1       { return false on error }

  @@End:

  end;

  if ErrorFlag then

    raise EDPMIError.Create('Failed to execute DPMI interrupt');

end;



function DriveLetterToNumber(DriveLet: Char): Byte;

{ function converts a character drive letter into its numerical equiv. }

begin

  if DriveLet in ['a'..'z'] then

    DriveLet := Chr(Ord(DriveLet) -32);

  if not (DriveLet in ['A'..'Z']) then

    raise EConvertError.CreateFmt('Cannot convert %s to drive number',



                                  [DriveLet]);

  Result := Ord(DriveLet) - 64;

end;



procedure PadVolumeLabel(var Name: Str11);

{ procedure pads Volume Label string with spaces }

var

  i: integer;

begin

  for i := Length(Name) + 1 to 11 do

    Name := Name + ' ';

end;



function GetVolumeLabel(Drive: Char): Str11;

{ function returns volume label of a disk }

var

  SR: TSearchRec;

  DriveLetter: Char;

  SearchString: String[7];

  P: Byte;

begin

  SearchString := Drive + ':\*.*';

  { find vol label }

  if FindFirst(SearchString, faVolumeID, SR) = 0 then begin

    P := Pos('.', SR.Name);

    if P > 0 then begin                      { if it has a dot... }

      Result := '           ';               { pad spaces between name }

      Move(SR.Name[1], Result[1], P - 1);    { and extension }

      Move(SR.Name[P + 1], Result[9], 3);

    end

    else begin

      Result := SR.Name;                     { otherwise, pad to end }

      PadVolumeLabel(Result);

    ennd;

  end

  else

    Result := '';

end;



procedure DeleteVolumeLabel(Drv: Char);

{ procedure deletes volume label from given drive }

var

  CurName: Str11;

  FCB: TExtendedFCB;

  ErrorFlag: WordBool;

begin

  ErrorFlag := False;

  CurName := GetVolumeLabel(Drv);        { get current volume label }

  FillChar(FCB, SizeOf(FCB), 0);         { initialize FCB with zeros }

  with FCB do begin

    ExtendedFCBflag := $FF;              { always }

    Attr := faVolumeID;                  { Volume ID attribute }

    DriveID := DriveLetterToNumber(Drv); { Drive number }

    Move(CurName[1], FileName, 8);       { must enter volume label }

    Move(CurName[9], FileExt, 3);

  end;

  asm

    push ds                              { preserve ds }

    mov ax, ss                           { put seg of FCB (ss) in ds }

    mov ds, ax

    lea dx, FCB                          { put offset of FCB in dx }

    mov ax, 1300h                        { function 13h }

    Call DOS3Call                         { invoke int 21h }

    pop ds                               { restore ds }

    cmp al, 00h                          { check for success }

    je @@End

  @@Error:                               { set flag on error }

    mov ErrorFlag, 1

  @@End:

  end;

  if ErrorFlag then

    raise EInterruptError.Create('Failed to delete volume name');

end;



procedure SetVolumeLabel(NewLabel: Str11; Drive: Char);

{ procedure sets volume label of a disk.  Note that this procedure }

{ deletes the current label before setting the new one.  This is }

{ required for the set function to work. }

var

  Regs: TRealModeRegs;

  FCB: PExtendedFCB;

  Buf: Longint;

begin

  PadVolumeLabel(NewLabel);

  if GetVolumeLabel(Drive) <> '' then           { if has label... }

    DeleteVolumeLabel(Drive);                   { delete label }

  Buf := GlobalDOSAlloc(SizeOf(PExtendedFCB));  { allocate real buffer }

  FCB := Ptr(LoWord(Buf), 0);

  FillChar(FCB^, SizeOf(FCB), 0);                { init FCB with zeros }

  with FCB^ do begin

    ExtendedFCBflag := $FF;                     { required }

    Attr := faVolumeID;                         { Volume ID attribute }

    DriveID := DriveLetterToNumber(Drive);      { Drive number }

    Move(NewLabel[1], FileName, 8);             { set new label }

    Move(NewLabel[9], FileExt, 3);

  end;

  FillChar(Regs, SizeOf(Regs), 0);

  with Regs do begin                            { SEGMENT of FCB }

    ds := HiWord(Buf);                          { offset = zero }

    dx := 0;

    ax := $1600;                                { function 16h }

  end;

  RealModeInt($21, Regs);                       { create file }

  if (Regs.al <> 0) then                        { check for success }

    raise EInterruptError.Create('Failed to create volume label');

end;



end.




How to check if a drive is ready

Question


How to check if a drive is ready?

Answer


{

  The following function accepts a drive letter as a parameter,

  and it will return a boolean value that indicates whether

  or not there is a disk in the drive.

}

function DiskInDrive(Drive: Char): Boolean;

var

  ErrorMode: word;

begin

  { make it upper case }

  if Drive in ['a'..'z'] then Dec(Drive, $20);

  { make sure it's a letter }

  if not (Drive in ['A'..'Z']) then

    raise EConvertError.Create('Not a valid drive ID');

  { turn off critical errors }

  ErrorMode := SetErrorMode(SEM_FailCriticalErrors);

  try

    { drive 1 = a, 2 = b, 3 = c, etc. }

    if DiskSize(Ord(Drive) - $40) = -1 then

      Result := False

    else

      Result := True;

  finally

    { restore old error mode }

    SetErrorMode(ErrorMode);

  end;

end;




Detecting a Pentium processor

Question


How to detect a Pentium processor?

Answer


{

Here is a Delphi unit to detect the CPU type, modified from Intel's

code. Use should be fairly obvious.  If not, send me email, and I can

send you an example program.  Because Delphi's assembler is 16-bit,

the code looks a little wierd.  Try using a 32-bit disassembler to see

the 32-bit instructions (or read the comments).

}



unit CpuId;



{ This code comes from Intel, and has been modified for Delphi's

  inline assembler.  Since Intel made the original code freely

  available, I am making my changes freely available.



  Share and enjoy!



  Ray Lischner

  Tempest Software

  6/18/95

}



interface



type

  { All the types currently known.  As new types are created,

    add suitable names, and extend the case statement in

    CpuTypeString.

  }

  TCpuType = (cpu8086, cpu80286, cpu386, cpu486, cpuPentium);



{ Return the type of the current CPU }

function CpuType: TCpuType;



{ Return the type as a short string }

function CpuTypeString: String;



implementation



uses SysUtils;



function CpuType: TCpuType; assembler;

asm

  push DS



{ First check for an 8086 CPU }

{ Bits 12-15 of the FLAGS register are always set on the }

{ 8086 processor. }

  pushf				       { save EFLAGS }

  pop		bx		          { store EFLAGS in BX }

  mov		ax,0fffh		    { clear bits 12-15 }

  and		ax,bx		       { in EFLAGS }

  push	ax			       { store new EFLAGS value on stack }

  popf	 			       { replace current EFLAGS value }

  pushf				       { set new EFLAGS }

  pop		ax		          { store new EFLAGS in AX }

  and		ax,0f000h	    { if bits 12-15 are set, then CPU }

  cmp		ax,0f000h	    { is an 8086/8088 }

  mov 	ax, cpu8086     { turn on 8086/8088 flag }

  je		@@End_CpuType



  { 80286 CPU check }

  { Bits 12-15 of the FLAGS register are always clear on the }

  { 80286 processor. }

  or		bx,0f000h	    { try to set bits 12-15 }

  push 	bx

  popf

  pushf

  pop		ax

  and		ax,0f000h	      { if bits 12-15 are cleared, CPU=80286 }

  mov 	ax, cpu80286      { turn on 80286 flag }

  jz		@@End_CpuType



  { To test for 386 or better, we need to use 32 bit instructions,

    but the 16-bit Delphi assembler does not recognize the 32 bit opcodes

    or operands.  Instead, use the 66H operand size prefix to change

    each instruction to its 32-bit equivalent. For 32-bit immediate

    operands, we also need to store the high word of the operand immediately

    following the instruction.  The 32-bit instruction is shown in a comment

    after the 66H instruction.

  }



  { i386 CPU check }

  { The AC bit, bit #18, is a new bit introduced in the EFLAGS }

  { register on the i486 DX CPU to generate alignment faults. }

  { This bit can not be set on the i386 CPU. }



  db 66h                    { pushfd }

  pushf

  db 66h                    { pop eax }

  pop	ax		                { get original EFLAGS }

  db 66h                    { mov ecx, eax }

  mov	cx,ax		             { save original EFLAGS }

  db 66h                    { xor eax,40000h }

  xor	ax,0h	                { flip AC bit in EFLAGS }

  dw 0004h

  db 66h                    { push eax }

  push ax			          { save for EFLAGS }

  db 66h                    { popfd }

  popf				          { copy to EFLAGS }

  db 66h                    { pushfd }

  pushf				          { push EFLAGS }

  db 66h                    { pop eax }

  pop	ax		                { get new EFLAGS value }

  db 66h                    { xor eax,ecx }

  xor	ax,cx		             { can't toggle AC bit, CPU=Intel386 }

  mov ax, cpu386            { turn on 386 flag }

  je @@End_CpuType



{ i486 DX CPU / i487 SX MCP and i486 SX CPU checking }

{ Checking for ability to set/clear ID flag (Bit 21) in EFLAGS }

{ which indicates the presence of a processor }

{ with the ability to use the CPUID instruction. }

  db 66h                    { pushfd }

  pushf				          { push original EFLAGS }

  db 66h                    { pop eax }

  pop	ax		                { get original EFLAGS in eax }

  db 66h                    { mov ecx, eax }

  mov	cx,ax		             { save original EFLAGS in ecx }

  db 66h                    { xor eax,200000h }

  xor	ax,0h	                { flip ID bit in EFLAGS }

  dw 0020h

  db 66h                    { push eax }

  push ax			          { save for EFLAGS }

  db 66h                    { popfd }

  popf				          { copy to EFLAGS }

  db 66h                    { pushfd }

  pushf                     { push EFLAGS }

  db 66h                    { pop eax }

  pop	ax		                { get new EFLAGS value }

  db 66h                    { xor eax, ecx }

  xor ax, cx

  mov ax, cpu486            { turn on i486 flag }

  je @@End_CpuType	       { if ID bit cannot be changed, CPU=486

}

  { without CPUID instruction functionality }



{ Execute CPUID instruction to determine vendor, family, }

{ model and stepping.  The use of the CPUID instruction used }

{ in this program can be used for B0 and later steppings }

{ of the P5 processor. }

   db 66h                  { mov eax, 1 }

   mov ax, 1			      { set up for CPUID instruction }

   dw 0

   db 66h                  { cpuid }

	db	0Fh	              { Hardcoded opcode for CPUID instruction }

	db	0a2h

   db 66h                  { and eax, 0F00H }

	and ax, 0F00H	         { mask everything but family }

   dw 0

   db 66h                  { shr eax, 8 }

	shr ax, 8               { shift the cpu type down to the low byte }

   sub ax, 1               { subtract 1 to map to TCpuType }



@@End_CpuType:

   pop ds

end;



function CpuTypeString: String;

var

  kind: TCpuType;

begin

  kind := CpuType;

  case kind of

  cpu8086:

    Result := '8086';

  cpu80286:

    Result := '80286';

  cpu386:

    Result := '386';

  cpu486:

    Result := '486';

  cpuPentium:

    Result := 'Pentium';

  else

    { Try to be flexible for future cpu types, e.g., P6. }

    Result := Format('P%d', [Ord(kind)]);

  end;

end;



end.




Determining drive type

Question


How to determining drive types?

Answer


When dealing with multiple drives, it is helpful to know 

whether a drive is associated with a  is attached to a letter 

(A, B, C, etc), and what its type is.  This code uses the API

GetDriveType function to do that.



function ShowDriveType(DriveLetter: char): string;

var

  i: word;

begin

  if DriveLetter in ['A'..'Z'] then {Make it lower case.}

    DriveLetter := chr(ord(DriveLetter) + $20);

  i := GetDriveType(ord(DriveLetter) - ord('a'));

  case i of

    DRIVE_REMOVABLE: result := 'floppy';

    DRIVE_FIXED: result := 'hard disk';

    DRIVE_REMOTE: result := 'network drive';

    else result := 'does not exist';

  end;

end;



DISCLAIMER: You have the right to use this technical information

subject to the terms of the No-Nonsense License Statement that

you received with the Borland product to which this information

pertains.




Close    To Top
  • Prev Article-Programming:
  • Next Article-Programming:
  • Now: Tutorial for Web and Software Design > Programming > delphi > Programming Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction