{$C MOVEABLE DEMANDLOAD DISCARDABLE}
unit PCMCIA;
interface
uses Timer;   { 35 Hz time stamp counter unit, two functions used: }
	{ GetTimer35(variable, value) sets variable to "now + value" }
	{ Timer35LaterThan(value) returns true if "now" is > value.    }
        { Only used for delays during power voltage config / card init.  }

{   Part of the PCMCIA module of DeskWork, released into public domain   }
{ by the author, Konstantin Koll. English translation by Eric Auer 2004. }
{  Many new comments by Eric Auer, with help of Konstantin Koll 2004...  }

{ Bridge caveat: The bridge can access a 64k address space, and the card }
{ CIS (config data...) address space with all addresses used modulo 4k,  }
{ of which often only a few 100 bytes are used. The space must be mapped }
{ to an UNCACHED memory area (i.e. in UMB space, but there MUSTN'T be an }
{ UMB at that address). So you need EMM386 / UMBPCI X=????-???? options. }

const
  pcmciaNotPresent=0;
  pcmciaEmpty=1;
  pcmciaBooting=2;
  pcmciaCard=3;
  pcmciaNoPower=4;

  clsNone=0;
  clsEthernet=1;
  clsEIDE=2;
  clsMemstick=3;
  clsHarddisk=4;
  clsSerial=5;

  PowerConfig: array[1..2] of Byte=($BF,$91); { possible voltages }

type
  SlotType=record
    Class,Status,IRQ,Power: Byte;
    Vendor,Device,IOBase: Word;
    Name: String[33];
    BootTime: Time;
end;

  CPortType=record
    BasePort: Word;
    Slots: array[0..3] of SlotType;
end;

procedure AddPort(_BasePort: Word); { ISA-PnP }
function HotplugPCMCIA(Mount: Boolean): Boolean;
procedure DetectPCMCIA;
procedure DonePCMCIA;

var CPort: CPortType;

{ ********************************************************************* }

implementation

uses Kernel, { Window,Input,FrameBtn,Views,VGADRV,GDI, }
     Boot, Ethernet, ISA, EIDE, ExtFS, MMSound, SerPorts;
     { Sorry, MANY other DeskWork parts are involved here...      }
     { GUI stuff used in older versions for some dialogues only }

     { Kernel -> FillChar(string, count, char) ... ... ... }
     {   LTrim(string) and RTrim(string) zaps leading/trailing spaces... }
     { Boot -> BootText(string,color), prints a message at boot }
     { Ethernet -> Ethernet.AddPort(iobase, (2nd iobase), IRQ, typecode) }
     {   Ethernet.InitCard, Ethernet.RemovePort(iobase), only NE2000 here }
     { ISA -> boolean RegionFree(iobase, size), to avoid clashes }
     { EIDE -> boolean EIDE.CheckInterface(iobase), tests drive presence }
     {   and mounts drives when found ... }
     {   EIDE.AddPort(iobase, iobase2, irq, dev_count, test_if_ide, force, name), }
     {   iobase2 is usually iobase+0x206, controller reset port, IRQ is 0. }
     {   PCMCIA needs no controller test, exactly 1 device per controller. }
     {   EIDE.RemovePort(iobase, iobase2), ... }
     {   EIDE.FirstIDE, a linked list of IDE devices }
     { ExtFS -> ExtFS.AddPort(value), value is used for N * 4 bit bit field }
     {   ExtFS.RemovePort(value) ... at most 4 PCMCIA slots supported }
     { MMSound -> to avoid clashes with soundcard, read MMSound.VocIRQ }
     { SerPorts -> for PCMCIA modems:  SerPorts.AddPort(iobase, irq), }
     {   SerPorts.SPorts array, SerPorts.RemovePort(iobase), }
     {   SerPorts.SetSPortDevice(arrayindex, typecode, deviceconnected) }


var MemWindowBase,Sel: Word;

{ Init functions (do port config, init device) implemented below include: }
{   EnableEIDEAutoSize EnableShiningEIDE EnableEIDENormal EnableNinjaEIDE }
{   EnableClik EnableMemstick EnableModem EnableNE2000 }

{ PCMCIA bridge programming: indexed register, 4 slots * 64 registers }
function InCard(Slot,Index: Byte): Byte; assembler;
Asm
  MOV DX,CPort.BasePort
  MOV AL,Slot
  AND AL,3
  SHL AL,6
  ADD AL,Index
  OUT DX,AL
  INC DX
  IN AL,DX
end; { out_byte(base,(slot&3)<<6 + index); return in_byte(base+1); }

{ PCMCIA bridge programming: indexed register, 4 slots * 64 registers }
procedure OutCard(Slot,Index,Wert: Byte); assembler;
Asm
  MOV DX,CPort.BasePort
  MOV AL,Slot
  AND AL,3
  SHL AL,6
  ADD AL,Index
  OUT DX,AL
  INC DX
  MOV AL,Wert
  OUT DX,AL
end; { out_byte(base,(slot&3)<<6 + index); out_byte(base+1, wert); }

{ PCMCIA bridge registers   (base: 0/64/128/192 for slot 0..3)   ->  }
{   0 "always 82..8f", 1 card presence / powerok, 2 power config..., }
{   3 IRQ config / reset, 0x16/0x2f "used during reset", 6/7 config, }
{   8 + (N*4) I/O window config,  16 + (N*8) memory window config    }
{      I/O windows: startL, startH, endL, endH                       }
{      memory windows: startL, startH, endL, endH, offsL, offsH/attr }
{   You could also use int 1a.80 ... 1a.af, but that is an extra TSR }
{   -> nice(?) PCMCIA (and CardBus) services, IF you have the TSR... }

function CardPresent(Slot: Byte): Boolean;
begin
  CardPresent := (InCard(Slot,1) and $C) = $C;
end;


procedure DisableINT(Slot: Byte);
begin
  OutCard(Slot,3, InCard(Slot,3) and not $1F );
end;


procedure EnableINT(Slot,IRQ: Byte);
begin
  CPort.Slots[Slot].IRQ := IRQ;
  OutCard(Slot,3, InCard(Slot,3) or $10 or (IRQ and 15) );
end;


procedure DisableIOWindow(Slot,Window: Byte);
var Ctrl: Byte;
begin
  CPort.Slots[Slot].IRQ := 0;
  Ctrl := InCard(Slot,6);
  OutCard(Slot,6, (Ctrl and not ($40 shl (Window and 1))) );
end;


procedure EnableIOWindow(Slot,Window: Byte; Start,Ende: Word; Autosize: Boolean);
var Ctrl,Ofs: Byte;
begin
  Window:=Window and 1;
{ disable window }
  Ctrl := InCard(Slot,6) or 32;
  OutCard(Slot,6,(Ctrl and not ($40 shl Window)));
{ new window configuration }
  Ofs := Window*4+8;
  OutCard(Slot,Ofs,Lo(Start));
  OutCard(Slot,Ofs+1,Hi(Start));
  OutCard(Slot,Ofs+2,Lo(Ende));
  OutCard(Slot,Ofs+3,Hi(Ende));
{ set parameters }
  Ctrl := InCard(Slot,7);
    if Window=0 then begin
      Ctrl := (Ctrl and $F0) or $09;
        if AutoSize=True then Ctrl := Ctrl or $02;
    end else begin
      Ctrl := (Ctrl and $0F) or $90;
        if AutoSize=True then Ctrl := Ctrl or $20;
    end;
  OutCard(Slot,7,Ctrl);
{ enable window }
  Ctrl := InCard(Slot,6);
  OutCard(Slot,6,(Ctrl or ($40 shl Window)));
end;

procedure DisableMemoryWindow(Slot,Window: Byte);
var Ctrl: Byte;
begin
  Ctrl := InCard(Slot,6);
  OutCard(Slot,6, (Ctrl and not (1 shl (Window and 3))) );
end;

procedure EnableMemoryWindow(Slot,Window: Byte; SystemStart,SystemEnde,CardOffs: Word; Attr: Boolean);
var Ctrl,Ofs,B: Byte;
begin
  Window:=Window and 3;
{ disable window }
  Ctrl := InCard(Slot,6) or 32;
  OutCard(Slot,6,(Ctrl and not (1 shl Window)));
{ new window configuration }
  Ofs := Window*8+16;
  OutCard(Slot,Ofs,Lo(SystemStart));
  OutCard(Slot,Ofs+1,Hi(SystemStart));
  OutCard(Slot,Ofs+2,Lo(SystemEnde));
  OutCard(Slot,Ofs+3,Hi(SystemEnde));
  OutCard(Slot,Ofs+4,Lo(CardOffs));
  OutCard(Slot,Ofs+5,Hi(CardOffs) or ($40*Byte(Attr)));
{ enable window }
  OutCard(Slot,6,(Ctrl or (1 shl Window)));
end;

procedure SkipMove(var Src,Dest; Size: Word); assembler;
Asm
  MOV DX,DS
  LDS SI,Src
  LES DI,Dest
  MOV CX,Size
@1:
  LODSW       { MUST be a WORD sized access! }
  STOSB
  DEC CX
  JNE @1
  MOV DS,DX
end;



{*--*}



procedure EnableModem(Socket: Byte);
var A,IRQ: Byte;
begin
    with CPort.Slots[Socket] do begin
      if RegionFree($3F8,8)=True then begin IOBase:=$3F8; IRQ:=4; end else
        if RegionFree($2F8,8)=True then begin IOBase:=$2F8; IRQ:=3; end else
          if RegionFree($3E8,8)=True then begin
            IOBase:=$3E8; IRQ:=5;
              if MMSound.VocIRQ=5 then IRQ:=7;
          end else
            if RegionFree($2E8,8)=True then begin
              IOBase:=$2E8; IRQ:=7;
                if MMSound.VocIRQ=7 then IRQ:=5;
            end else
              exit;
    EnableIOWindow(Socket,0,IOBase,IOBase+7,True);
    EnableINT(Socket,IRQ);
    SerPorts.AddPort(IOBase,IRQ);
      for A:=0 to High(SerPorts.SPorts) do { Pascal function: High(array) }
      if SPorts[A].BasePort=IOBase then SerPorts.SetSPortDevice(A,serModem,True);
    Class:=clsSerial;
    end;
end;

procedure EnableNE2000(Socket: Byte);
label Next,Fertig;
var IRQ: Byte;
begin
    with CPort.Slots[Socket] do begin { Use $280 or higher, soundcard conflicts! }
      if RegionFree($280,32)=True then IOBase:=$280 else
        if RegionFree($300,32)=True then IOBase:=$300 else
          if RegionFree($320,32)=True then IOBase:=$320 else
            if RegionFree($340,32)=True then IOBase:=$340 else exit;
    IRQ:=5;
      if MMSound.VocIRQ=5 then IRQ:=7;
    EnableIOWindow(Socket,0,IOBase,IOBase+$1F,False);
    EnableINT(Socket,IRQ);
    Ethernet.AddPort(IOBase,0,IRQ,ethNE2000);
    Ethernet.InitCard;
    Class:=clsEthernet;
    end;
end;

procedure EnableShiningEIDE(Socket: Byte);
var PortNo: Byte;
begin
    with CPort.Slots[Socket] do begin
      if (RegionFree($150,8)=True) and (RegionFree($356,2)=True) then IOBase:=$150 else
        if (RegionFree($190,8)=True) and (RegionFree($396,2)=True) then IOBase:=$190 else
          if (RegionFree($180,8)=True) and (RegionFree($386,2)=True) then IOBase:=$180 else
            if (RegionFree($1A0,8)=True) and (RegionFree($3A6,2)=True) then IOBase:=$1A0 else exit;
    EnableIOWindow(Socket,0,IOBase,IOBase+7,False);
    EnableIOWindow(Socket,1,IOBase+$206,IOBase+$207,False);
    DisableINT(Socket);
    Port[IOBase+6]:=$10; { enable True ATA }
    PortNo:=EIDE.AddPort(IOBase,IOBase+$206,0,1,False,False,Name);
      if PortNo<>0 then EIDE.CheckInterface(PortNo);
    Class:=clsEIDE;
    end;
end;

procedure EnableNinjaEIDE(Socket: Byte);
var PortNo: Byte;
begin
    with CPort.Slots[Socket] do begin
      if (RegionFree($180,8)=True) and (RegionFree($386,2)=True) then IOBase:=$180 else
        if (RegionFree($190,8)=True) and (RegionFree($396,2)=True) then IOBase:=$190 else
          if (RegionFree($1A0,8)=True) and (RegionFree($3A6,2)=True) then IOBase:=$1A0 else exit;
    EnableIOWindow(Socket,0,IOBase,IOBase+7,False);
    EnableIOWindow(Socket,1,IOBase+$206,IOBase+$207,False);
    DisableINT(Socket);
    Mem[Sel:512]:=97; {enable True ATA }
    Port[IOBase+6]:=$A0; Port[IOBase+7]:=8;
    PortNo:=EIDE.AddPort(IOBase,IOBase+$206,0,1,False,False,Name);
      if PortNo<>0 then EIDE.CheckInterface(PortNo);
    Class:=clsEIDE;
    end;
end;

procedure EnableMemstick(Socket: Byte);
var PortNo: Byte;
begin
    with CPort.Slots[Socket] do begin
      if (RegionFree($190,8)=True) and (RegionFree($396,2)=True) then IOBase:=$190 else
        if (RegionFree($180,8)=True) and (RegionFree($386,2)=True) then IOBase:=$180 else
          if (RegionFree($1A0,8)=True) and (RegionFree($3A6,2)=True) then IOBase:=$1A0 else exit;
    EnableIOWindow(Socket,0,IOBase,IOBase+7,False);
    EnableIOWindow(Socket,1,IOBase+$206,IOBase+$207,False);
    DisableINT(Socket);
    Mem[Sel:512]:=1; { enable True ATA }
    PortNo := EIDE.AddPort(IOBase,IOBase+$206,0,1,False,False,'PCMCIA MemoryStick-Adapter');
      if PortNo<>0 then
        if EIDE.CheckInterface(PortNo)<>0 then ExtFS.AddPort(ExtFS.memstickPCMCIA1 shl Socket);
    Class:=clsMemstick;
    end;
end;

procedure EnableClik(Socket: Byte);
var PortNo: Byte;
begin
    with CPort.Slots[Socket] do begin
      if (RegionFree($190,8)=True) and (RegionFree($396,2)=True) then IOBase:=$190 else
        if (RegionFree($180,8)=True) and (RegionFree($386,2)=True) then IOBase:=$180 else
          if (RegionFree($1A0,8)=True) and (RegionFree($3A6,2)=True) then IOBase:=$1A0 else exit;
    EnableIOWindow(Socket,0,IOBase,IOBase+7,False);
    EnableIOWindow(Socket,1,IOBase+$206,IOBase+$207,False);
    DisableINT(Socket);
    Mem[Sel:2048]:=41; { enable True ATA }
    Port[IOBase+7]:=8;
    PortNo:=EIDE.AddPort(IOBase,IOBase+$206,0,1,False,False,'IOMEGA Clik!');
      if PortNo<>0 then
        if EIDE.CheckInterface(PortNo)<>0 then ExtFS.AddPort(ExtFS.pcmciaHD1 shl Socket);
    Class:=clsHarddisk;
    end;
end;

procedure EnableEIDENormal(Socket: Byte);
label Fertig;
var
  P: PIDEDevice;
  PortNo: Byte;
begin
    with CPort.Slots[Socket] do begin
      if (RegionFree($190,8)=True) and (RegionFree($396,2)=True) then IOBase:=$190 else
        if (RegionFree($180,8)=True) and (RegionFree($386,2)=True) then IOBase:=$180 else
          if (RegionFree($1A0,8)=True) and (RegionFree($3A6,2)=True) then IOBase:=$1A0 else exit;
    EnableIOWindow(Socket,0,IOBase,IOBase+7,False);
    EnableIOWindow(Socket,1,IOBase+$206,IOBase+$207,False);
    DisableINT(Socket);
    Mem[Sel:512]:=1; { enable True ATA }
    PortNo:=EIDE.AddPort(IOBase,IOBase+$206,0,1,False,False,Name);
    Class:=clsEIDE;
      if PortNo<>0 then
        if EIDE.CheckInterface(PortNo)<>0 then begin
          P:=EIDE.FirstIDE;
            while P<>nil do begin
              with P^ do
              if (P^.Controller=PortNo) and (P^.Typ=typGenericATA) then begin
                ExtFS.AddPort(ExtFS.pcmciaHD1 shl Socket);
                Class:=clsHarddisk; goto Fertig;
              end;
            P:=P^.NextIDE;
            end;
Fertig:
        end;
    end;
end;

procedure EnableEIDEAutosize(Socket: Byte);
label Fertig;
var
  P: PIDEDevice;
  PortNo: Byte;
begin
    with CPort.Slots[Socket] do begin
      if (RegionFree($190,8)=True) and (RegionFree($396,2)=True) then IOBase:=$190 else
        if (RegionFree($180,8)=True) and (RegionFree($386,2)=True) then IOBase:=$180 else
          if (RegionFree($1A0,8)=True) and (RegionFree($3A6,2)=True) then IOBase:=$1A0 else exit;
    EnableIOWindow(Socket,0,IOBase,IOBase+7,True);
    EnableIOWindow(Socket,1,IOBase+$206,IOBase+$207,True);
    DisableINT(Socket);
    Mem[Sel:512]:=1; { enable True ATA }
    PortNo:=EIDE.AddPort(IOBase,IOBase+$206,0,1,False,False,Name);
    Class:=clsEIDE;
      if PortNo<>0 then
        if EIDE.CheckInterface(PortNo)<>0 then begin
          P:=EIDE.FirstIDE;
            while P<>nil do begin
              with P^ do
              if (P^.Controller=PortNo) and (P^.Typ=typGenericATA) then begin
                ExtFS.AddPort(ExtFS.pcmciaHD1 shl Socket);
                Class:=clsHarddisk; goto Fertig;
              end;
            P:=P^.NextIDE;
            end;
Fertig:
        end;
    end;
end;

{*--*}

procedure AddPort(_BasePort: Word); { ISA-PnP: check for PCMCIA bridge }
begin
  Port[_BasePort]:=0;
    if (Port[_BasePort+1] in [$82..$8F])=False then exit;
{ store }
  CPort.BasePort:=_BasePort;
end;

function HotplugPCMCIA(Mount: Boolean): Boolean;
label NextPowerConfig;
var
  CIS: array[0..2047] of Byte;
  Ti: Time;
  Offset,Pos,Ende: Word;
  Func,A,B,AStatus: Byte;
  Done: Boolean;
begin
  HotplugPCMCIA := False;
    if CPort.BasePort=0 then exit; { no PCMCIA i/o base set }
{ search for new devices }
    with CPort do for A:=0 to 3 do with Slots[A] do begin
    AStatus := Status;
      if InCard(A,0)=$FF then Status := pcmciaNotPresent else
        if CardPresent(A)=False then Status := pcmciaEmpty else
          if (Mount=True) and (Status<pcmciaCard) then begin
            Status:=pcmciaBooting;
              if (AStatus>=pcmciaBooting)
                 and (Timer35LaterThan(BootTime)) then Status := pcmciaCard;
                 { from Timer unit }
          end;
      if Status<>AStatus then begin
        HotplugPCMCIA := True;
          case Status of
          pcmciaEmpty: begin
              { unregister removed devices }
                if IRQ<>0 then DisableINT(A);
              OutCard(A,6,$20);
                case Class of
                clsEthernet: Ethernet.RemovePort(IOBase);
                clsEIDE: EIDE.RemovePort(IOBase,IOBase+$206);
                clsMemstick: begin
                               EIDE.RemovePort(IOBase,IOBase+$206);
                               ExtFS.RemovePort(ExtFS.memstickPCMCIA1 shl A);
                             end;
                clsHarddisk: begin
                               EIDE.RemovePort(IOBase,IOBase+$206);
                               ExtFS.RemovePort(ExtFS.pcmciaHD1 shl A);
                             end;
                clsSerial: SerPorts.RemovePort(IOBase);
                end;
              FillChar(Slots[A],SizeOf(SlotType),0); Status := pcmciaEmpty;
            end;
          pcmciaBooting: begin
              OutCard(A,3,$20); { Reset }
              OutCard(A,$16,3);
              OutCard(A,$2F,0);
              OutCard(A,3,$60); { Reset completed }
NextPowerConfig:
              Inc(Power,1);
                if Power>High(PowerConfig) then Status := pcmciaNoPower else begin
                  OutCard(A,2,PowerConfig[Power]); { power supply }
                  GetTimer35(Ti,2); { from Timer unit }
                    repeat until Timer35LaterThan(Ti); { from Timer unit }
                    if (InCard(A,1) and 64)=0 then goto NextPowerConfig;
                  GetTimer35(BootTime,75); { from Timer unit }
                end;
            end;
          pcmciaCard: begin
              GetTimer35(Ti,5); { from Timer unit }
                repeat until Timer35LaterThan(Ti)=True; { from Timer unit }
              EnableMemoryWindow(A, 0, MemWindowBase shr 8,MemWindowBase shr 8, 16180, True);
              GetTimer35(Ti,2); { from Timer unit }
                repeat until Timer35LaterThan(Ti)=True; { from Timer unit }
              SkipMove(Ptr(Sel,0)^,CIS,SizeOf(CIS)); { defined in this unit }
                { CIS is at most 2k bytes, but accessed as 2k words }
              Name := 'incompatible device';
              Offset := 0;
              Func := 0;
              Done := False;
                repeat begin
                Ende := Offset+CIS[Offset+1]+1;
                  case CIS[Offset] of
                  $15: begin
                         Inc(Offset,4); Name := '';
                           while (CIS[Offset]<>0) and (Offset<Ende) do begin
                           Name := Name+Char(CIS[Offset]);
                           Inc(Offset,1);
                           end;
                         Name := LTrim(RTrim(Name))+#32; Inc(Offset,1);
                           while (CIS[Offset]<>0) and (Offset<Ende) do begin
                           Name := Name+Char(CIS[Offset]);
                           Inc(Offset,1);
                           end;
                         Name := LTrim(RTrim(Name))+#32; Inc(Offset,1);
                           while (CIS[Offset]<>0) and (Offset<Ende) do begin
                           Name := Name+Char(CIS[Offset]);
                           Inc(Offset,1);
                           end;
                       end;
                  $20: begin
                         Move(CIS[Offset+2],Vendor,2);
                         Move(CIS[Offset+4],Device,2);
                       end;
                  $21: Func := CIS[Offset+2];
                  $FF: Done := True;
                  end;
              Offset := Ende+1;
              end until (Done=True) or (Offset>=SizeOf(CIS));
          { mount special devices }
              if LeftStr(Name,9)='NinjaATA-' then EnableNinjaEIDE(A) else
                  { Ninja ATA } { Pascal function: LeftStr(string,size) }
                  case Func of
                  2: case Vendor of
                     $0115: if Device=$3330 then EnableModem(A);
                     end;
                  4: if (Vendor=$000A) and (Device=$0000) then EnableMemStick(A) else { Noname MemoryStick }
                     if (Vendor=$00F1) and (Device=$0000) then EnableMemStick(A) else { Sony MemoryStick }
                     if (Vendor=$FFFF) and (Device=$0003) then EnableClik(A) else { iOMEGA Clik }
                     if Vendor=$5241 then EnableEIDEAutosize(A) else { Generic ATA Autosize }
                     if LeftStr(Name,17)='Shining PMIDE-ASC' then EnableShiningEIDE(A) else { Shing Technology EIDE Adaptor }
                       EnableEIDENormal(A); { Generic ATA, if none of the special ones }
                       { Pascal function: LeftStr(string,size) }
                  6: EnableNE2000(A); { Generic NE2000, just assume all LAN cards are NE2000 }
                  end;
          { Fenster wieder freigeben }
            DisableMemoryWindow(A,0);
          end;
        end;
      end;
    end;
end;

procedure DetectPCMCIA;
label Schleife,Weiter;
var
  St: String[4];
  A: Word;
begin
    if CPort.BasePort=0 then AddPort($3E0); { Standard }
    if CPort.BasePort=0 then
      BootText('No PCMCIA devices found!',15)
    else begin
      BootText('Scanning for PCMCIA devices...',11);
    { find ROM area / area in adapter segment to map CIS data }
      Sel := AllocSelector;
      SetSelectorLimit(Sel,4096);
      MemWindowBase := $CC00;
        repeat begin
        SetSelectorBase(Sel,MemWindowBase*LongInt(16));
          for A := 0 to 4095 do if Mem[Sel:A]<>$FF then goto Weiter;
Schleife:
        HotplugPCMCIA(True);
          for A := 0 to 3 do with CPort.Slots[A] do
          if (Status and 3)=pcmciaBooting then goto Schleife;
        BootText('PCMCIA devices found.',7);
        St[0] := #4;
        Hex(Hi(CPort.BasePort),St[1]);
        Hex(Lo(CPort.BasePort),St[3]);
        BootText(#32#32#16#32+'Controller at port 0x'+St+'',8);
          for A := 0 to 3 do with CPort.Slots[A] do
          if (Status and 3)=pcmciaCard then BootText(#32#32#16#32+Name,8);
        exit;
Weiter:
        Inc(MemWindowBase,$400);
        end until MemWindowBase=$F000;
      CPort.BasePort := 0;
      BootText('No ROM area free for PCMCIA devices!',15);
    end;
end;

procedure DonePCMCIA;
var A: Byte;
begin
    if CPort.BasePort=0 then exit;
    for A := 0 to 3 do with CPort.Slots[A] do
    if (IRQ<>0) and (Status=pcmciaCard) then DisableINT(A);
end;

begin
  FillChar(CPort,SizeOf(CPort),0); Sel := 0;
end.
