unit supervga;

interface
uses dos;

type
  str10=string[10];

  mmods=(_text,
         _text2,
         _text4,
         _pl2 ,   {plain mono, 8 pixels per byte}
         _pl2e,   {mono odd/even, 8 pixels per byte, two planes}
         _herc,   {Hercules mono, 4 "banks" of 8kbytes}
         _cga2,   {CGA 2 color, 2 "banks" of 16kbytes}
         _cga4,   {CGA 4 color, 2 "banks" of 16kbytes}
         _pl4 ,   {4 color odd/even planes}
         _pk4 ,   {4 color "packed" pixels 4 pixels per byte}
         _pl16,   {std EGA/VGA 16 color: 4 planes, 8 pixels per byte}
         _pk16,   {ATI mode 65h two 16 color pixels per byte}
         _p256,   {one 256 color pixel per byte}
         _p32k,   {Sierra 15 bit}
         _p64k,   {Sierra 16bit/XGA}
         _p16m);  {RGB 3bytes per pixel}

  modetype=record
             md,xres,yres,bytes:word;
             memmode:mmods;
           end;

  CHIPS=(__EGA,__VGA,__chips451,__chips452,__chips453,__paradise,__video7
        ,__tseng3,__tseng4,__tridBR,__tridCS,__trid89,__everex,__ati1,__ati2
        ,__genoa,__oak,__cirrus,__aheadA,__aheadB,__ncr,__yamaha,__poach
        ,__s3,__al2101,__acumos,__mxic,__vesa,__realtek,__p2000,__cirrus54
        ,__none);


const
  colbits:array[mmods] of integer=
               (0,0,0,1,1,1,1,2,2,2,4,4,8,15,16,24);
  modecols:array[mmods] of longint=
               (0,0,0,2,2,2,2,4,4,4,16,16,256,32768,65536,16777216);

  mdtxt:array[mmods] of string[210]=('Text','2 color Text','4 color Text'
                ,'Monochrome','2 colors planar','Hercules','CGA 2 color','CGA 4 color'
                ,'4 colors planar','4 colors packed','16 colors planar','16 colors packed'
                ,'256 colors packed','32768 colors','65536 colors'
                ,'16777216 colors');

  mmodenames:array[mmods] of string[4]=('TXT ','TXT2','TXT4','PL2 ','PL2E','HERC'
              ,'CGA2','CGA4','PL4 ','PK4 ','PL16','PK16','P256','P32K','P64K','P16M');


  header:array[CHIPS] of string[14]=
         ('EGA','VGA','Chips&Tech','Chips&Tech','Chips&Tech'
         ,'Paradise','Video7','ET3000','ET4000'
         ,'Trident','Trident','Trident','Everex','ATI','ATI'
         ,'Genoa','Oak','Cirrus','Ahead','Ahead','NCR'
         ,'Yamaha','Poach','S3','AL2101','Acumos','MXIC'
         ,'VESA','Realtek','PRIMUS','Cirrus54','');


  novgamodes=10;
  stdmodetbl:array[1..novgamodes] of modetype=
            ((md: 4;xres:320;yres:200;bytes: 80;memmode:_cga4)
            ,(md: 5;xres:320;yres:200;bytes: 80;memmode:_cga4)
            ,(md: 6;xres:640;yres:200;bytes: 80;memmode:_cga2)
            ,(md:13;xres:320;yres:200;bytes: 40;memmode:_pl16)
            ,(md:14;xres:640;yres:200;bytes: 80;memmode:_pl16)
            ,(md:15;xres:640;yres:350;bytes: 80;memmode:_pl2)
            ,(md:16;xres:640;yres:350;bytes: 80;memmode:_pl16)
            ,(md:17;xres:640;yres:480;bytes: 80;memmode:_pl2)
            ,(md:18;xres:640;yres:480;bytes: 80;memmode:_pl16)
            ,(md:19;xres:320;yres:200;bytes:320;memmode:_p256));



  _dac0     =0;   {No DAC (MDA/CGA/EGA ..}
  _dac8     =1;   {Std VGA DAC 256 cols.}
  _dac15    =2;   {Sierra 32k DAC}
  _dac16    =3;   {Sierra 64k DAC}
  _dacss24  =4;   {Sierra?? 24bit RGB DAC}
  _dacatt   =5;   {ATT 20c491/2  15/16/24 bit DAC}
  _dacADAC1 =6;   {Acumos ADAC1  15/16/24 bit DAC}




  vesa:word=0;



var
  rp:registers;

  memmode:mmods;   {current memory mode}
  vseg:word;       {Video buffer base segment}
  video:string[5];
  mm:word;         {Video memory in kilobytes}
  CHIP:CHIPS;
  dacname:string[20];
  dactype:word;
  crtc:word;       {I/O address of CRTC registers}
  _crt:string[20];
  secondary:string[20];
  extra:string[80];
  name:string[40];

  curmode:word;    {Current mode number}
  pixels:word;     {Pixels in a scanline in current mode}
  lins:word;       {lines in current mode}
  bytes:longint;   {bytes in a scanline}
  planes:word;     {number of video planes}


  nomodes:word;
  modetbl:array[1..30] of modetype;


  vesarec:record
            attr:word;
            wina,winb:byte;
            gran,winsiz,sega,segb:word;
            pagefunc:pointer;
            bytes,width,height:word;
            charw,charh,planes,bits,nbanks,model,banks:byte;
            x:array[byte] of byte;    {might get trashed by 4F01h}
          end;


  dotest:array[CHIPS] of boolean;





function strip(s:string):string;       {strip leading and trailing spaces}
function upstr(s:string):string;       {convert a string to upper case}
function istr(w:longint):str10;
function hex2(w:word):str10;
function hex4(w:word):str10;


procedure vio(ax:word);         {INT 10h reg ax=AX. other reg. set from RP
                                 on return rp.ax=reg AX}

function inp(reg:word):byte;     {Reads a byte from I/O port REG}

procedure outp(reg,val:word);    {Write the low byte of VAL to I/O port REG}

function rdinx(pt,inx:word):word;       {read register PT index INX}

procedure wrinx(pt,inx,val:word);       {write VAL to register PT index INX}

procedure modinx(pt,inx,mask,nwv:word);  {In register PT index INX sets
                                          the bits in MASK as in NWV
                                          the other are left unchanged}

procedure setbank(bank:word);

procedure setvstart(l:longint);       {Set the display start address}

function setmode(md:word):boolean;

procedure vesamodeinfo(md:word);

procedure dactocomm;

procedure dactopel;

procedure findvideo;


implementation


const
  mmmask :array[0..8] of byte=(0,0,0,0,1,3,3,7,15);

  hx:array[0..15] of char='0123456789ABCDEF';






var

  atireg:word;    {ATI extended registers}

  old,curbank:word;

  biosseg:word;
  vgran:word;



function strip(s:string):string;       {strip leading and trailing spaces}
begin
  while s[length(s)]=' ' do dec(s[0]);
  while copy(s,1,1)=' ' do delete(s,1,1);
  strip:=s;
end;

function upstr(s:string):string;       {convert a string to upper case}
var x:word;
begin
  for x:=1 to length(s) do
    s[x]:=upcase(s[x]);
  upstr:=s;
end;

function istr(w:longint):str10;
var s:str10;
begin
  str(w,s);
  istr:=s;
end;

function hex2(w:word):str10;
begin
  hex2:=hx[(w shr 4) and 15]+hx[w and 15];
end;

function hex4(w:word):str10;
begin
  hex4:=hex2(hi(w))+hex2(lo(w));
end;



procedure vio(ax:word);         {INT 10h reg ax=AX. other reg. set from RP
                                 on return rp.ax=reg AX}
begin
  rp.ax:=ax;
  intr(16,rp);
end;

function inp(reg:word):byte;     {Reads a byte from I/O port REG}
begin
  reg:=port[reg];
  inp:=reg;
end;

procedure outp(reg,val:word);    {Write the low byte of VAL to I/O port REG}
begin
  port[reg]:=val;
end;


function rdinx(pt,inx:word):word;       {read register PT index INX}
var x:word;
begin
  if pt=$3C0 then x:=inp($3DA);    {If Attribute Register then reset Flip-Flop}
  outp(pt,inx);
  rdinx:=inp(pt+1);
end;

procedure wrinx(pt,inx,val:word);       {write VAL to register PT index INX}
begin
  outp(pt,inx);
  outp(pt+1,val);
end;

procedure modinx(pt,inx,mask,nwv:word);  {In register PT index INX sets
                                          the bits in MASK as in NWV
                                          the other are left unchanged}
var temp:word;
begin
  temp:=(rdinx(pt,inx) and not mask)+(nwv and mask);
  wrinx(pt,inx,temp);
end;


function getbios(offs,lnn:word):string;
var s:string;
begin
  s[0]:=chr(lnn);
  move(mem[biosseg:offs],s[1],lnn);
  getbios:=s;
end;

function tstrg(pt,msk:word):boolean;       {Returns true if the bits in MSK
                                            of register PT are read/writable}
var old,nw1,nw2:word;
begin
  old:=inp(pt);
  outp(pt,old and not msk);
  nw1:=inp(pt) and msk;
  outp(pt,old or msk);
  nw2:=inp(pt) and msk;
  outp(pt,old);
  tstrg:=(nw1=0) and (nw2=msk);
end;

function testinx2(pt,rg,msk:word):boolean;   {Returns true if the bits in MSK
                                              of register PT index RG are
                                              read/writable}
var old,nw1,nw2:word;
begin
  old:=rdinx(pt,rg);
  wrinx(pt,rg,old and not msk);
  nw1:=rdinx(pt,rg) and msk;
  wrinx(pt,rg,old or msk);
  nw2:=rdinx(pt,rg) and msk;
  wrinx(pt,rg,old);
  testinx2:=(nw1=0) and (nw2=msk);
end;

function testinx(pt,rg:word):boolean;     {Returns true if all bits of
                                           register PT index RG are
                                           read/writable.}
var old,nw1,nw2:word;
begin
  testinx:=testinx2(pt,rg,$ff);
end;

procedure dactopel;    {Force DAC back to PEL mode}
begin
  if inp($3c8)=0 then;
end;

var
  daccomm:word;

procedure dactocomm;    {Enter command mode of HiColor DACs}
var x:word;
begin
  dactopel;
  x:=inp($3c6);
  x:=inp($3c6);
  x:=inp($3c6);
  daccomm:=inp($3c6);
end;




  (*  Set memory bank  *)

procedure setbank(bank:word);
var x:word;
begin
  vseg:=$a000;
  if bank=curbank then exit;   {Only set bank if diff. from current value}
  case chip of
    __acumos:modinx($3ce,9,$f0,bank shl 4);
    __aheadA:begin
               wrinx($3ce,13,bank shr 1);
               x:=inp($3cc) and $df;
               if odd(bank) then inc(x,32);
               outp($3c2,x);
             end;
    __aheadB:wrinx($3ce,13,bank*17);
    __al2101:outp($3d7,bank);
      __ati1:modinx(atireg,$b2,$1e,bank shl 1);
      __ati2:modinx(atireg,$b2,$ee,bank*$22);
  __chips451:wrinx(crtc+2,11,bank);
  __chips452:wrinx(crtc+2,16,bank shl 2);
  __chips453:wrinx(crtc+2,16,bank shl 4);
    __everex:begin
               x:=inp($3cc) and $df;
               if (bank and 2)>0 then inc(x,32);
               outp($3c2,x);
               modinx($3c4,8,$80,bank shl 7);
             end;
     __genoa:wrinx($3c4,6,bank*9+64);
      __mxic:wrinx($3c4,$c5,bank*17);
       __ncr:begin
               if memmode<=_pl16 then bank:=bank shl 2;
               wrinx($3c4,$18,bank shl 2);
             end;
       __oak:wrinx($3de,17,bank*17);
  __paradise:wrinx($3ce,9,bank shl 4);

     __p2000,
   __realtek:begin
               outp($3d6,bank);
               outp($3d7,bank);
             end;
        __s3:begin
               wrinx(crtc,$38,$48);
               modinx(crtc,$31,9,9);
               if memmode=_pl16 then bank:=bank*4;
               modinx(crtc,$35,$f,bank);
               wrinx(crtc,$38,0);
             end;
    __tridBR:;
    __tridCS,__poach,__trid89
            :begin
               wrinx($3c4,11,0);
               if rdinx($3c4,11)=0 then;
               modinx($3c4,14,$f,bank xor 2);
             end;
    __tseng3:outp($3cd,bank*9+64);
    __tseng4:outp($3cd,bank*17);
    __video7:begin
               x:=inp($3cc) and $df;
               if (bank and 2)>0 then inc(x,32);
               outp($3c2,x);
               modinx($3c4,$f9,1,bank);
               modinx($3c4,$f6,$80,(bank shr 2)*5);

             end;
  __cirrus54:wrinx($3CE,9,bank*16);
      __vesa:begin
               rp.bx:=0;
               rp.dx:=bank*longint(64) div vgran;
               vio($4f05);
               rp.bx:=1;
               vio($4f05);
             end;
  end;
  curbank:=bank;
end;


procedure vesamodeinfo(md:word);
begin
  rp.cx:=md;
  rp.es:=seg(vesarec);
  rp.di:=ofs(vesarec);
  vio($4f01);
  vgran:=vesarec.gran;
  bytes:=vesarec.bytes;
  pixels:=vesarec.width;
  lins:=vesarec.height;
  case vesarec.bits of
    4:memmode:=_pl16;
    8:memmode:=_p256;
   15:memmode:=_p32k;
   16:memmode:=_p64k;
   24:memmode:=_p16m;
  end;
end;

function safemode(md:word):boolean;
var x,y:word;
begin                 {Checks if we entered a Graph. mode}
  vio(3);
  vio(lo(md));
  y:=rdinx($3ce,6);
  safemode:=odd(y);
end;

function tsvio(ax,bx:word):boolean;   {Tseng 4000 Hicolor mode set}
begin
  rp.bx:=bx;
  vio(ax);
  tsvio:=rp.ax=16;
end;

function setmode(md:word):boolean;
var x:word;
begin
  setmode:=true;
  curmode:=md;
  case chip of
__ati1,__ati2:begin
                rp.bx:=$5506;
                rp.bp:=$ffff;
                rp.si:=0;
                vio($1200+md);
                if rp.bp=$ffff then setmode:=false
                else vio(md);
              end;
   __chips451:begin
                setmode:=safemode(md);
                x:=inp($46e8);
                outp($46e8,x or 16);
                outp($103,inp($103) or $80);
                outp($46e8,x and $ef);
                modinx(crtc+2,4,4,4);
                modinx(crtc+2,11,3,1);
              end;
   __chips452,__chips453:
              begin
                setmode:=safemode(md);
                x:=inp($46e8);
                outp($46e8,x or 16);
                outp($103,inp($103) or $80);
                outp($46e8,x and $ef);
                modinx(crtc+2,4,4,4);
                modinx(crtc+2,11,3,1);
                wrinx(crtc+2,12,0);
              end;
     __everex:begin
                rp.bl:=md;
                vio($70);
              end;
   __paradise:begin
                setmode:=safemode(md);
                modinx($3ce,15,$17,5);
                wrinx(crtc,$29,$85);
                modinx($3ce,$b,8,0);
                modinx(crtc,$2f,$62,0);
              end;
        __ncr:begin
                setmode:=safemode(md);
                wrinx($3c4,5,5);
                wrinx($3c4,$18,0);
                wrinx($3c4,$19,0);
                wrinx($3c4,$1a,0);
                wrinx($3c4,$1b,0);

                modinx($3c4,$1e,$1c,$18);
              end;
     __video7:begin
                rp.bl:=md;
                vio($6f05);
              end;
       __mxic:begin
                setmode:=safemode(md);
                wrinx($3c4,$a7,$87);    {enable extensions}
              end;
       __vesa:begin
                rp.bx:=md;
                vio($4f02);
                if rp.ax<>$4f then setmode:=false
                else begin
                  vesamodeinfo(md);
                  chip:=__vesa;
                end;
              end;
     __acumos:begin
                vio(md);
                wrinx($3c4,6,$12);
              end;
     __tseng3:begin
                vio(md);
                modinx($3c4,4,2,2);
              end;
     __tseng4:case hi(md) of
                0:setmode:=safemode(md);
                1:if tsvio($10e0,lo(md)) then
                  begin
                    {Diamond SpeedStar 24 does not clear memory}
                    for x:=0 to 15 do         {clear memory}
                    begin
                      setbank(x);
                      mem[$a000:0]:=0;
                      fillchar(mem[$a000:1],65535,0);
                    end;
                  end else setmode:=false;
                2:if tsvio($10f0,md shl 8+$ff) then
                  begin
                    outp($3bf,3);
                    outp(crtc+4,$a0);   {enable Tseng 4000 Extensions}
                    wrinx(crtc,$13,0);
                    modinx(crtc,$3f,$80,$80);
               {     outp(crtc+4,$29);
                    outp($3bf,1);      do we need these ? }
                    wrinx(crtc,$13,0);
                    modinx(crtc,$3f,$80,$80);
                  end else setmode:=false;
                3:if not tsvio($10f0,lo(md)) then setmode:=false;
                4:if tsvio($10f0,lo(md)) then
                  begin
                    dactocomm;
                    x:=inp($3c6);
                    outp($3c6,x or 64);  {set DAC to 64K colors}
                    dactopel;
                  end else setmode:=false;
              end;
         __s3:if md<$100 then setmode:=safemode(md)
              else begin
                rp.bx:=md;
                vio($4f02);
                if rp.ax=$4f then
                begin
                  if md<$200 then vesamodeinfo(md);
                end
                else setmode:=false;
              end;
      __p2000:begin
                setmode:=safemode(md);
                if memmode=_p64k then
                begin
                  dactocomm;
                  outp($3c6,$c0);
                end;
         (*       if memmode=_p16m then
                begin            {This can trick a ATT20c492 into 24bit mode}
                  dactocomm;
                  outp($3c6,$e0);
                  bytes:=1600;
                  pixels:=530;
                end;  *)
              end;
  else setmode:=safemode(md)
  end;
  curbank:=$ffff;    {Set curbank invalid }
  case memmode of
  _pl2e,_pl4:planes:=2;
    _pl16:planes:=4;
  else planes:=1;
  end;
  for x:=1 to mm div 64 do
  begin
    setbank(x-1);
    mem[$a000:$ffff]:=0;
    fillchar(mem[$a000:0],$ffff,0);
  end;
  modinx($3c4,4,2,2);    {Set "more than 64K" flag}
  vseg:=$a000;
end;

procedure checkmem(mx:word);
var
  fail:boolean;
  ma:array[0..99] of byte;
  x:word;
begin
  memmode:=_p256;

  fail:=true;
  while (mx>1) and fail do
  begin
    setbank(mx-1);
    move(mem[$a000:0],ma,100);
    for x:=0 to 99 do
      mem[$a000:x]:=ma[x] xor $aa;
    setbank(mx-1);
    fail:=false;
    for x:=0 to 99 do
      if mem[$a000:x]<>ma[x] xor $aa then fail:=true;
    move(ma,mem[$a000:0],100);
    if not fail then
    begin
      setbank((mx shr 1)-1);
      for x:=0 to 99 do
        mem[$a000:x]:=ma[x] xor $55;
      setbank(mx-1);
      fail:=true;
      for x:=0 to 99 do
        if mem[$a000:x]<>ma[x] xor $55 then fail:=false;
      move(ma,mem[$a000:0],100);
    end;
    mx:=mx shr 1;
  end;
  mm:=mx*128;
end;


procedure setvstart(l:longint);       {Set the display start address}
var x,y:word;
begin
  if chip<>__vesa then
  begin
    x:=l shr 2;
    y:=(l shr 18) and (pred(mm) shr 8);   {Mask out any "too" high bits}
    wrinx(crtc,13,lo(x));
    wrinx(crtc,12,hi(x));
  end;
  case chip of
    __tseng3:modinx(crtc,$23,2,y shl 1);
    __tseng4:modinx(crtc,$33,3,y);
    __tridcs:modinx(crtc,$1e,32,y shl 5);
    __trid89:begin
               modinx(crtc,$1e,$a0,y shl 5+128);
               wrinx($3c4,11,0);
               modinx($3c4,$e,1,y shr 1);
             end;
    __video7:modinx($3c4,$f6,$70,(y shl 4) and $30);
  __paradise:modinx($3ce,$d,$18,y shl 3);
  __chips452,__chips453:
             begin
               wrinx($3d6,12,y);
               modinx($3d6,4,4,4);
             end;
     __ncr:begin
             modinx(crtc,$31,$f,y);
           end;
    __ati1:modinx(atireg,$b0,$40,y shl 6);
    __ati2:modinx(atireg,$b0,$c0,y shl 6);
  __aheadb:modinx($3ce,$1c,3,y);
    __vesa:begin
             rp.bx:=0;
             rp.cx:=l mod 320;
             rp.dx:=l div 320;
             vio($4f07);
             if rp.ax=0 then;
           end;
      __s3:begin
             wrinx(crtc,$38,$48);
             modinx(crtc,$31,$30,y shl 4);
             wrinx(crtc,$38,0);
           end;
__cirrus54:begin
             if y>1 then inc(y,2);
             modinx(crtc,$1b,5,y);
           end;
   __p2000:modinx($3ce,$21,$7,y);
  end;
end;


procedure UNK(chp:string;id:word);
begin
  name:='Unknown '+chp+' chip ('+istr(id)+')';
end;

   (*  Tests for various adapters  *)


function _chipstech:boolean;
begin
  _chipstech:=false;
  if dotest[__CHIPS451] then
  begin
    vio($5f00);
    if rp.al=$5f then
    begin
      _chipstech:=true;
      case rp.bl shr 4 of
        0:name:='Chips & Tech 82c451';
        1:name:='Chips & Tech 82c452';
        2:name:='Chips & Tech 82c455';
        3:name:='Chips & Tech 82c453';
        5:name:='Chips & Tech 82c456';
        6:name:='Chips & Tech 82c457';
        7:name:='Chips & Tech F65520';
        8:name:='Chips & Tech F65530';
      else UNK('Chips & Tech',rp.bl shr 4);
      end;
      case rp.bl shr 4 of
        1:CHIP:=__chips452;
        3:CHIP:=__chips453;
      else chip:=__chips451;
      end;
      case rp.bh of
        1:mm:=512;
        2:mm:=1024;
      end;
    end;
  end;
end;

function _paradise:boolean;
var old,old1,old2:word;
begin
  _paradise:=false;
  if dotest[__PARADISE] then
  begin
    old:=rdinx($3ce,15);
    modinx($3ce,15,$17,0);   {Lock registers}

    if not testinx2($3ce,9,$7f) then
    begin
      wrinx($3ce,15,5);      {Unlock them again}
      if testinx2($3ce,9,$7f) then
      begin
        _paradise:=true;
        old2:=rdinx(crtc,$29);
        name:='Paradise ';
        modinx(crtc,$29,$8f,$85);   {Unlock WD90Cxx registers}
        if not testinx(crtc,$2b) then name:=name+'PVGA1A'
        else begin
          old1:=rdinx($3c4,6);
          wrinx($3c4,6,$48);
          if not testinx2($3c4,7,$f0) then name:=name+'WD90C00'
          else if not testinx($3c4,16) then
          begin
            name:=name+'WD90C2x';
            wrinx(crtc,$34,$a6);
            if (rdinx(crtc,$32) and 32)<>0 then wrinx(crtc,$34,0);
          end
          else if testinx2($3c4,20,15) then
               begin
                 if rdinx(crtc,$37)=$31 then name:=name+'WD90C31'
                                        else name:=name+'WD90C30';
               end
               else if not testinx2($3c4,16,4) then name:=name+'WD90C10'
                                               else name:=name+'WD90C11';

          wrinx($3c4,6,old1);
        end;
        case rdinx($3ce,11) shr 6 of
           2:mm:=512;
           3:mm:=1024;
        end;
        wrinx(crtc,$29,old2);
        chip:=__paradise;
      end;
    end;
    wrinx($3ce,15,old);
  end;
end;

function _video7:boolean;
begin
  _video7:=false;
  if dotest[__video7] then
  begin
    vio($6f00);
    if rp.bx=$5637 then
    begin
      _video7:=true;
      vio($6f07);
      case rp.bl of
        $80..$ff:name:='Video7 VEGA VGA';
        $70..$7f:name:='Video7 FASTWRITE/VRAM';
        $50..$5f:name:='Video7 Version 5';
        $41..$4f:name:='Video7 1024i';
      end;
      case rp.ah and 127 of
        2:mm:=512;
        4:mm:=1024;
      end;
      chip:=__video7;
    end
  end;
end;

function _genoa:boolean;
var ad:word;
begin
  _genoa:=false;
  if dotest[__genoa] then
  begin
    ad:=memw[biosseg:$37];
    if (memw[biosseg:ad+2]=$6699) and (mem[biosseg:ad]=$77) then
    begin
      _genoa:=true;
      case mem[biosseg:ad+1] of
        0:name:='Genoa 62/300';
      $11:begin
            name:='Genoa 64/500';
            mm:=512;
          end;
      $22:name:='Genoa 6100';
      $33:name:='Genoa 51/5200 (Tseng 3000)';
      $55:begin
            name:='Genoa 53/5400 (Tseng 3000)';
            mm:=512;
          end;
      end;
      if mem[biosseg:ad+1]<$33 then chip:=__genoa else chip:=__tseng3;
    end
  end;
end;

function _tseng:boolean;
var x,vs:word;
begin
  _tseng:=false;
  if dotest[__TSENG3] or dotest[__TSENG4] then
  begin
    outp($3bf,3);
    outp($3d8,$a0);    {Enable Tseng 4000 extensions}
    if tstrg($3cd,$3f) then
    begin
      _tseng:=true;
      if testinx2(crtc,$33,$f) then
      begin
        name:='Tseng ET4000';
        case rdinx(crtc,$37) and 11 of
         3,9:mm:=256;
          10:mm:=512;
          11:mm:=1024;
        end;
    (*    vio($10f1);
        if (rp.ax=$10) then
          case rp.bl of
            1:name:=name+' /w Sierra RAMDAC';
            2:name:=name+' /w SS24 RAMDAC';
          end; *)
        chip:=__tseng4;
      end
      else begin
        name:='Tseng ET3000';
        chip:=__tseng3;
        if setmode($13) then;
        x:=port[$3da];
        x:=rdinx($3c0,$36);
        port[$3c0]:=x or 16;
        case (rdinx($3ce,6) shr 2) and 3 of
         0,1:vs:=$a000;
           2:vs:=$b000;
           3:vs:=$b800;
        end;

        meml[vs:1]:=$12345678;
        if memw[vs:2]=$3456 then mm:=512;

        wrinx($3c0,$36,x);     {reset value and reenable DAC}
      end;
    end;
  end;
end;

function _trident:boolean;
var chp,old,val:word;
begin
  _trident:=false;
  if dotest[__tridBR] or dotest[__trid89] or dotest[__tridCS] then
  begin
    wrinx($3c4,11,0);
    chp:=inp($3c5);
    old:=rdinx($3c4,14);
    outp($3c5,0);
    val:=inp($3c5);
    outp($3c5,old);
    if (val and 15)=2 then
    begin
      _trident:=true;
      case chp of
        1:name:='Trident 8800BR';
        2:name:='Trident 8800CS';
        3:name:='Trident 8900';
        4:name:='Trident 8900C';
      $13:name:='Trident 8900C';
      $23:name:='Trident 9000';
      $83:name:='Trident LX9200';
      $93:name:='Trident LCD9100';
      else UNK('Trident',chp);
      end;
      case chp and 15 of
        1:chip:=__tridbr;
        2:chip:=__tridCS;
        3:chip:=__trid89;
      end;
      if (pos('Zymos Poach 51',getbios(0,255))>0) or
         (pos('Zymos Poach 51',getbios(230,255))>0) then
      begin
        name:=name+' (Zymos Poach)';
        chip:=__poach;
      end;
      if (chp>=3) then
      begin
        case rdinx(crtc,$1f) and 3 of
          0:mm:=256;
          1:mm:=512;
          2:mm:=768;
          3:mm:=1024;
        end;
      end
      else
      if (rdinx(crtc,$1f) and 2)>0 then mm:=512;

    end;
  end;
end;

function _oak:boolean;
begin
  _oak:=false;
  if dotest[__oak] then
  begin
    if testinx2($3de,$d,$38) then
    begin
      _oak:=true;
      name:='OAK 037C';
      if testinx($3DE,$11) then
      begin
        if rdinx($3DE,$B)=5 then name:='OAK 077'
                            else name:='OAK 067';
      end;
      case rdinx($3de,13) shr 6 of
        2:mm:=512;
      1,3:mm:=1024;    {1 might not give 1M??}
      end;
      chip:=__oak;
    end;
  end;
end;

function _cirrus:boolean;
var old,eagle:word;
begin
  _cirrus:=false;
  if dotest[__cirrus] then
  begin
    old:=rdinx(crtc,12);
    outp(crtc+1,0);
    eagle:=rdinx(crtc,$1f);
    wrinx($3c4,6,lo(eagle shr 4) or lo(eagle shl 4));
    if inp($3c5)=0 then
    begin
      outp($3c5,eagle);
      if inp($3c5)=1 then
      begin
        _cirrus:=true;
        case eagle of
          $EC:name:='Cirrus 510/520';
          $CA:name:='Cirrus 610/620';
          $EA:name:='Cirrus Video 7 OEM'
        else UNK('Cirrus',eagle);
        end;
        chip:=__cirrus;
      end;
    end;
    wrinx(crtc,12,old);
  end;
end;


function _cirrus54:boolean;
var x,old:word;
begin
  _cirrus54:=false;
  if dotest[__cirrus54] then
  begin
    old:=rdinx($3C4,6);
    wrinx($3c4,6,$12);
    if (rdinx($3C4,6)=$12) and testinx2($3C4,$1E,$3F) and testinx2(crtc,$1B,$ff) then
    begin
      x:=rdinx(crtc,$27);
      case x of
          $8A:name:='Cirrus 54xx typ 2';
     $8C..$8F:name:='Cirrus 54xx typ 3';
     $90..$93:name:='Cirrus 54xx typ 5';
     $94..$97:name:='Cirrus 54xx typ 4';
      else UNK('Cirrus54',x);
      end;
      case rdinx($3C4,$F) and $18 of
        0:mm:=0;
        8:mm:=512;
       16:mm:=1024;
      end;
      _cirrus54:=true;
      chip:=__cirrus54;
    end
    else wrinx($3C4,6,old);
  end;
end;

function _ahead:boolean;
var old:word;
begin
  _ahead:=false;
  if dotest[__aheadA] or dotest[__aheadB] then
  begin
    old:=rdinx($3ce,15);
    wrinx($3ce,15,0);
    if not testinx2($3ce,12,$FB) then
    begin
      wrinx($3ce,15,$20);
      if testinx2($3ce,12,$FB) then
      begin
        _ahead:=true;
        case rdinx($3ce,15) and 15 of
          0:begin
              name:='Ahead A';
              chip:=__aheadA;
            end;
          1:begin
              name:='Ahead B';
              chip:=__aheadB;
            end;
        end;
      end;
    end;
    wrinx($3ce,15,old);
  end;
end;

function _everex:boolean;
var x:word;
begin
  _everex:=false;
  if dotest[__everex] then
  begin
    rp.bx:=0;
    vio($7000);
    if rp.al=$70 then
    begin
      x:=rp.dx shr 4;
      if  (x<>$678) and (x<>$236)
      and (x<>$620) and (x<>$673) then     {Some Everex boards use Trident chips.}
      begin
        _everex:=true;
        case rp.ch shr 6 of
          0:mm:=256;
          1:mm:=512;
          2:mm:=1024;
          3:mm:=2048;
        end;
        name:='Everex Ev'+hx[x shr 8]+hx[(x shr 4) and 15]+hx[x and 15];
        chip:=__everex;
      end;
    end;
  end;
end;

function _ati:boolean;
var w:word;
begin
  _ati:=false;
  if dotest[__ATI1] or dotest[__ati2] then
  begin
    if getbios($31,9)='761295520' then
    begin
      _ati:=true;
      case memw[biosseg:$40] of
       $3133:begin
               atireg:=memw[biosseg:$10];
               name:='ATI VGA Wonder';
               w:=rdinx(atireg,$bb);
               case w and 15 of
                 0:_crt:='EGA';
                 1:_crt:='Analog Monochrome';
                 2:_crt:='Monochrome';
                 3:_crt:='Analog Color';
                 4:_crt:='CGA';
                 6:_crt:='';
                 7:_crt:='IBM 8514/A';
               else _crt:='Multisync';
               end;
               chip:=__ati2;
               case chr(mem[biosseg:$43]) of
                '1':begin
                      name:=name+' (18800)';
                      chip:=__ati1;
                    end;
                '2':name:=name+' (18800-1)';
                '3':name:=name+' (28800-2)';
                '4':name:=name+' (28800-4)';
                '5':begin
                      name:=name+' (28800-5)';
                      if (mem[biosseg:$44] and 128)<>0 then
                        name:=name+' /w HICOLOR DAC';
                    end;
               end;
               case chr(mem[biosseg:$43]) of
                 '1','2':if (rdinx(atireg,$bb) and 32)<>0 then mm:=512;
                     '3':if (rdinx(atireg,$b0) and 16)<>0 then mm:=512;
                 '4','5':case rdinx(atireg,$b0) and $18 of
                             0:mm:=256;
                           $10:mm:=512;
                         8,$18:mm:=1024;
                         end;
               end;
             end;
       $3233:begin
               name:='ATI EGA Wonder';
               video:='EGA';
               chip:=__ega;
             end;
      end;
    end;
  end;
end;

function _s3:boolean;
var x:word;
begin
  _s3:=false;
  if dotest[__s3] then
  begin
    wrinx(crtc,$38,0);
    if not testinx2(crtc,$35,$f) then
    begin
      wrinx(crtc,$38,$48);
      if testinx2(crtc,$35,$f) then
      begin
        _s3:=true;
        chip:=__s3;
        x:=rdinx(crtc,$30);
        case x of
          $81:name:='S3 86c911';
          $82:name:='S3 86c911A';  {Whats the diff?}
        else UNK('S3',x);
        end;
        if (rdinx(crtc,$41) and $10)<>0 then mm:=1024
                                        else mm:=512;
      end;
    end;
  end;
end;

function _al2101:boolean;
begin
  _al2101:=false;
  if dotest[__al2101] then
  begin
    if tstrg($8286,$ff) and testinx2(crtc,$1f,$3b)
       and testinx2($3ce,13,15) then
    begin
      _al2101:=true;
      name:='Avance Logic 2101';
      chip:=__al2101;
      case rdinx(crtc,$1e) and 3 of
        0:mm:=256;
        1:mm:=512;
        2:mm:=1024;
        3:mm:=2048;
      end;
    end;
  end;
end;

function _vesa:boolean;
begin
  _vesa:=false;
  if dotest[__vesa] then
  begin
    vio($4f03);
    if rp.al=$4f then
    begin
      _vesa:=true;
      name:='VESA';
      chip:=__vesa;
      vesa:=1;
    end;
  end;
end;

function _yamaha:boolean;
begin
  _yamaha:=false;
  if dotest[__yamaha] then
  begin
    if testinx2($3d4,$7c,$7c) then
    begin
      _yamaha:=true;
      name:='Yamaha 6388'
    end;
  end;
end;

function _ncr:boolean;
var x:word;
begin
  _ncr:=false;
  if dotest[__ncr] then
  begin
    if testinx2($3c4,5,5) then
    begin
      wrinx($3c4,5,0);        {Disable extended registers}
      if not testinx2($3c4,16,$ff) then
      begin
        wrinx($3c4,5,1);        {Enable extended registers}
        if testinx2($3c4,16,$ff) then
        begin
          _ncr:=true;
          chip:=__ncr;
          x:=rdinx($3c4,8) shr 4;
          case x of
            0:name:='NCR 77C22';
            1:name:='NCR 77C21';
            2:name:='NCR 77C22E';
        8..15:name:='NCR 77C22E+';
          else UNK('NCR',x);
          end;
          name:=name+' Rev. '+istr(rdinx($3c4,8) and 15);
          if setmode($13) then;
          checkmem(64);
        end;
      end;
    end;
  end;
end;

function _acumos:boolean;
var old:word;
begin
  _acumos:=false;
  if dotest[__acumos] then
  begin
    old:=rdinx($3c4,6);
  {  wrinx($3c4,6,0);
    if not testinx2($3ce,9,$f0) then }
    begin
      wrinx($3c4,6,$12);
      if testinx2($3ce,9,$30) then
      begin
        _acumos:=true;
        name:='Acumos AVGA2';
        chip:=__acumos;
        case rdinx($3c4,$a) and 3 of
          0:mm:=256;
          1:mm:=512;
          2:mm:=1024;
        end;
      end;
    end;
    wrinx($3c4,6,old);
  end;
end;

function _mxic:boolean;
begin
  _mxic:=false;
  if dotest[__mxic] then
  begin
    old:=rdinx($3c4,$a7);
    wrinx($3c4,$a7,0);       {disable extensions}
    if not testinx($3c4,$c5) then
    begin
      wrinx($3c4,$a7,$87);   {enable extensions}
      if testinx($3c4,$c5) then
      begin
        _mxic:=true;
        chip:=__mxic;
        name:='MX 86010';
        case (rdinx($3c4,$c2)  shr 2) and 3 of
          0:mm:=256;
          1:mm:=512;
          2:mm:=1024;
        end;
      end;
    end;
    wrinx($3c4,$a7,old);
  end;
end;

function _p2000:boolean;
begin
  _p2000:=false;
  if dotest[__p2000] then
  begin
    if testinx2($3CE,$3d,$3f) and tstrg($3d6,$1f) and tstrg($3d7,$1f) then
    begin
      _p2000:=true;
      name:='Primus P2000';
      chip:=__p2000;
      if setmode($13) then;
      checkmem(32);
    end;
  end;
end;

function _realtek:boolean;
var x:word;
begin
  _realtek:=false;
  if dotest[__realtek] then
  begin
    if testinx2(crtc,$1f,$3f) and tstrg($3d6,$f) and tstrg($3d7,$f) then
    begin
      chip:=__realtek;
      name:='Realtek';
      _realtek:=true;
      x:=rdinx(crtc,$1a) shr 6;
      case x of
     0..2:name:='Realtek version '+istr(x);
      else UNK('Realtek',x);
      end;
      case rdinx(crtc,$1e) and 15 of
        0:mm:=256;
        1:mm:=512;
        2:if x=0 then mm:=768  else mm:=1024;
        3:if x=0 then mm:=1024 else mm:=2048;
      end;
    end;
  end;
end;



function testdac:string;      {Test for type of DAC}
var
  x,y,z,v,oldcommreg,oldpelreg:word;

begin
  IF chip=__al2101 then    (* Special case -- weird DAC *)
  begin
    dactype:=_dac16;
    testdac:='AVL DAC 16';
    exit;
  end;
  testdac:='Normal';
  dactype:=_dac8;
  dactopel;
  x:=inp($3c6);
  repeat
    y:=x;         {wait for the same value twice}
    x:=inp($3c6);
  until (x=y);
  z:=x;
  dactocomm;
  if daccomm<>$8e then
  begin                      {If command register=$8e, we've got an SS24}
    y:=8;
    repeat
      x:=inp($3c6);
      dec(y);
    until (x=$8e) or (y=0);
  end
  else x:=daccomm;
  if x=$8e then
  begin
    dactype:=_dacss24;
    testdac:='SS24';
    dactopel;
  end
  else begin

    dactocomm;
    oldcommreg:=inp($3c6);
    dactopel;
    oldpelreg:=inp($3c6);
    x:=oldcommreg xor 255;
    outp($3c6,x);
    dactocomm;
    v:=inp($3c6);
    if v<>x then
    begin
      dactocomm;
      x:=oldcommreg xor $60;
      outp($3c6,x);
      dactocomm;
      v:=inp($3c6);
      testdac:='Sierra SC11486';
      dactype:=_dac15;

      if (x and $e0)=(v and $e0) then
      begin
        x:=inp($3c6);
        dactopel;
        testdac:='Sierra 32k/64k';
        dactype:=_dac15;             (* Can't tell the difference *)

        if x=inp($3c6) then
        begin
          testdac:='ATT 20c491/2';
          dactype:=_dacatt;
          dactocomm;
          outp($3c6,255);
          dactocomm;
          x:=inp($3c6);
          if x<>255 then
          begin
            testdac:='Acumos ADAC';
            dactype:=_dacadac1;
          end;
        end;
      end;

      dactocomm;
      outp($3c6,oldcommreg);
    end;
    dactopel;
    outp($3c6,oldpelreg);
  end;
end;


procedure findbios;     {Finds the most likely BIOS segment}
var
  score:array[0..7] of byte;
  x,y:word;
begin
  biosseg:=$c000;
  for x:=0 to 6 do score[x]:=1;
  for x:=0 to 7 do
  begin
    rp.bh:=x;
    vio($1130);
    if (rp.es>=$c000) and ((rp.es and $7ff)=0) then
      inc(score[(rp.es-$c000) shr 11]);
  end;

  for x:=0 to 6 do
  begin
    y:=$c000+(x shl 11);
    if (memw[y:0]<>$aa55) or (mem[y:2]<48) then
      score[x]:=0;                       {fail if no rom}
  end;
  for x:=6 downto 0 do
    if score[x]>0 then
      biosseg:=$c000+(x shl 11);
end;


procedure findvideo;
begin
  dactype:=_dac0;
  extra:='';
  _crt:='';
  chip:=__none;
  secondary:='';
  name:='';
  video:='none';
  rp.ah:=18;
  rp.bx:=$1010;
  intr(16,rp);
  if rp.bh<=1 then
  begin
    video:='EGA';
    chip:=__ega;
    if odd(inp($3cc)) then crtc:=$3d4
                      else crtc:=$3b4;

    mm:=rp.bl;
    vio($1a00);
    if rp.al=$1a then
    begin
      if (rp.bl<4) and (rp.bh>3) then
      begin
        old:=rp.bl;
        rp.bl:=rp.bh;
        rp.bh:=old;
      end;
      video:='MCGA';
      case rp.bl of
        2,4,6,10:_crt:='TTL Color';
        1,5,7,11:_crt:='Monochrome';
        8,12:_crt:='Analog Color';
      end;
      case rp.bh of
        1:secondary:='Monochrome';
        2:secondary:='CGA';
      end;
      findbios;
      if (getbios($31,9)='') and (getbios($40,2)='22') then
      begin
        video:='EGA';       {@#%@  lying ATI EGA Wonder !}
        name:='ATI EGA Wonder';

      end else
      if (rp.bl<10) or (rp.bl>12) then
      begin
        video:='VGA';
        chip:=__vga;
        mm:=256;
        if _vesa then extra:=extra+'VESA ';
        if _chipstech then
        else if _paradise then
        else if _video7 then
        else if _genoa then
        else if _everex then
        else if _trident then
        else if _ati then
        else if _ahead then
        else if _ncr then
        else if _s3 then
        else if _al2101 then
        else if _mxic then
        else if _cirrus54 then
        else if _acumos then
        else if _tseng then
        else if _realtek then
        else if _p2000 then
        else if _yamaha then
        else if _oak then
        else if _cirrus then;

        dacname:=testdac;

      end;
    end;
  end;
end;

begin
end.