{
¯ Does anyone know how to get the hard drive type(s) from CMOS ?
¯ I can't seem to find this information documented anywhere.

This is probably a lot more than you are asking for but. . .
NOTE: one function (Long2Str) is not defined in this because it comes from
a commercial unit.  Basically all it does is convert a number to a string
and return the string.
     This code comes from a unit I wrote to get all kinds of niffty
information about your system.  I think I included everything you will need
to get it up and running.  If you get any strange problems or ones you
can't seem to resolve, let me know and I'll see if I can pass you the right
information.
}

Uses
  KMath,
  Dos;

type
  String80 = String[80];  { some general purpose string types }
  String40 = String[40];
  String30 = String[30];
  String20 = String[20];
  String12 = String[12];
  String10 = String[10];
  String5  = String[5];

  CMOSRec = Record
    Found     : Boolean;  { was a CMOS found to exist }
    CmosDate  : String30; { the date found in CMOS }
    CmosTime  : String30; { the time found in CMOS }
    VideoType : String10; { Type of video found in CMOS }
    Coproc    : Boolean;  { does CMOS report a math coprocessor }
    FloppyA   : String12; { type of floppy drive for A }
    FloppyB   : String12; { Type of floppy drive for B }
    Hard0     : Byte;     { Type of hard drive for drive 0 }
    Hard1     : Byte;     { Type of hard drive for Drive 1 }
    ConvenRam : Word;     { amount of conventional ram indicated }
    ExtendRam : Word;     { amount of extended Ram indicated }
    checkSum  : Boolean;  { Did checksum pass }
  end; { CMOS Rec }

const
  { values of constants for CMOS }
  DayName: array[0..7] of string[9] = ('Sunday', 'Monday', 'Tuesday',
                                       'Wednesday', 'Thursday', 'Friday',
                                       'Saturday', 'Sunday');
  MonthName: array[0..12] of string[9] = ('???', 'January', 'February', 'March',
                                          'April', 'May', 'June', 'July',
                                          'August', 'September', 'October',
                                          'November', 'December');
  ScreenName: array[0..3] of string[10] = ('EGA/VGA', 'CGA 40col',
                                           'CGA 80col', 'Monochrome');
  FloppyName: array[0..5] of string[11] = ('none', '5.25" 360K',
                                           '5.25" 1.2M', '3.5"  720K',
                                           '3.5"  1.44M', '3.5"  2.88M');
  CMOSport : Byte = $70; { port to access the CMOS }

{===========================================================================}


VAR
  Regs : Registers; { General purpose variable to access registers }
  CMOS : CMOSRec;   { variable to hold CMOS data }

function nocarry : boolean;
{ returns the status of the carry flag }
begin
  nocarry:=regs.flags and fcarry = $0000
end; {nocarry}

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

Function ByteToWord(ByteA, ByteB : byte) : word;
begin
   ByteToWord := Word(ByteB) shl 8 + ByteA
end; {cbw}

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

Function BitIsSet(CheckWord : Word; AndValue : Word) : Boolean;
{ returns true if the bit(s) indicated in AndValue are set in CheckByte }
BEGIN
  BitIsSet := CheckWord AND AndValue = AndValue;
end;

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

Function ReadCMOS(addr: byte): byte;
{ read a value from the CMOS }
Begin
  if CMOSport = $70 then
  begin
    inline($FA);
    Port[CMOSport] := addr;
    readCMOS := Port[CMOSport + 1];
    inline($FB)
  end
end; {readCMOS}

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

function addzero(b: byte): string5;
var
  c2: string[2];
begin
  Str(b:0, c2);
  if b < 10 then
    c2:='0' + c2;
  addzero:=c2
end; {addzero}

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

Function ChangeBCD(b: byte): byte;
{ change a BCD into a byte structure }
Begin
  ChangeBCD:=(b and $0F) + ((b shr 4) * 10)
end; {ChangeBCD}

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

Function GetCMOSDate : String30;
{ gets the date found in the CMOS and returns it in string format }
VAR
  Date,
  Century,
  Year,
  Month : Byte;
  WorkStr : String30;
BEGIN
  WorkStr := '';
  date    := ChangeBCD(readCMOS(7));
  century := ChangeBCD(readCMOS($32));
  year    := ChangeBCD(readCMOS(9));
  month   := ChangeBCD(readCMOS(8));
  WorkStr := DayName[readCMOS(6)]+', ';
  {case country.DateFormat of
    0, 3..255 :}
      WorkStr := WorkStr + Monthname[month]+' '+IntToStr(date)+', '+IntToStr(century)+addzero(year);
 {   1 :
      WorkStr := WorkStr + Long2Str(date)+', '+Monthname[month]+' '+Long2Str(century)+addzero(Year);
    2 :
      WorkStr := WorkStr + Long2Str(century)+addzero(Year)+', '+Monthname[month]+' '+Long2Str(date);
  end; {case}
  GetCMosDate := workStr;
end; { GetCMOSDate }

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

Function GetCmosTime : String30;
{ returns the time as found in the CMOS }
VAR
  CH : Char;
  Hour,
  Min,
  Sec  : Byte;
  WorkStr : String30;
  IsPM    : Boolean;
BEGIN
  workStr := '';
  hour := ChangeBCD(readCMOS(4));
  min := ChangeBCD(readCMOS(2));
  sec := ChangeBCD(readCMOS(0));
  IsPm := false;
  case hour of
        0: hour := 12;
        1..11: hour := hour;
        12: IsPM := true;
        13..23: begin
                  IsPM := true;
                  hour := hour - 12
                end;
  end; {case}
  WorkStr := WorkStr + AddZero(hour)+':'+addzero(min)+':'+addzero(sec);
  if IsPM then
    workStr := WorkStr + ' PM'
  Else
    WorkStr := WorkStr + ' AM';
  GetCMOSTime := WorkStr;
end; { GetCmosTime }

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

Function GetCmosCheckSum : Boolean;
{ performs checksum on CMOS and returns true if ok }
VAR
  CheckSum1,
  CheckSum2 : word;
  Count     : Byte;
BEGIN
  checksum1 := 0;
  for count := $10 to $2D do
    Inc(checksum1, readCMOS(count));
  checksum2 := (word(256) * readCMOS($2E)) + readCMOS($2F);
  if checksum1 = checksum2 then
    GetCmosCheckSum := true
  else
    GetCmosCheckSum := false;
end; { GetCmosCheckSum }

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

Procedure GetCMos;
{ gets the cmos record if it exist }
VAR
  Floppy : Byte;
BEGIN
  FillChar(CMOS, SizeOf(CMos), 0);
  regs.AH:=$C0;
  Intr($15, regs);
  if nocarry or (Mem[$F000:$FFFE] <= $FC) then
  With CMOS DO
  begin
    Found := true;
    CMOSDate := GetCMOSDate;
    CMOSTime := GetCmosTime;
    VideoType := ScreenName[(readCMOS($14) shr 4) and 3];
    CoProc := BitIsSet(readCMOS($14), 2);
    Floppy := readCMOS($10);
    if (Floppy shr 4) < 5 then
      FloppyA := FloppyName[floppy shr 4]
    else
      FloppyA := 'Unknown '+ Byte2Hex(floppy shr 4);
    if (floppy and $0F) < 5 then
      FloppyB := FloppyName[floppy and $0F]
    else
      FloppyB := 'Unknown '+ Byte2Hex(floppy and $0F);

    Hard0 := readCMOS($12);
    Hard0 := Hard0 shr 4;
    Hard1 := ReadCmos($12);
    Hard1 := Hard1 and $0F;
    if Hard0 = $F then
      Hard0 := readCMOS($19)
    Else Hard0 := $FF; { error }
    if Hard1 = $F then
      Hard1 := readCMOS($1A)
    Else Hard1 := $FF;
    ConvenRam := word(256) * readCMOS($16) + readCMOS($15); { value in K }
    ExtendRam := word(256) * readCMOS($18) + readCMOS($17); { value in K }
    CheckSum := GetCmosCheckSum;
  end
  else
    CMOS.Found := false;
end;

begin
  GetCmos;
  Writeln(CMOS.Found);
  Writeln(CMOS.CmosDate);
  Writeln(CMOS.CmosTime);
  Writeln(CMOS.VideoType);
  Writeln(CMOS.Coproc);
  Writeln(CMOS.FloppyA);
  Writeln(CMOS.FloppyB);
  Writeln(CMOS.Hard0);
  Writeln(CMOS.Hard1);
  Writeln(CMOS.ConvenRam);
  Writeln(CMOS.ExtendRam);
  Writeln(CMOS.checkSum);
end.
