The source code of the central PCMCIA unit of DeskWork.de
has been donated for DOS development (by the author, in 2004).

However, he asked me to move my version (English comments,
original version used German comments, plus explanations
about how the unit interacts with other parts of DeskWork)
out of reach for Google in 1/2005.

If you want to do some experiments with PCMCIA and DOS,
please contact me by mail: eric $ coli.uni-sb.de
(replace the $ by @ to get the actual address).

Eric

--- excerpts from the file / notes ---

DeskWork uses a 35 Hz time stamp counter.
You have to map the CIS config data (max 4k, often less than 1k)
and the bridge 64k address space to an uncached high area which
is not used by any UMB. The ISA-PnP base port has to be known.

States: 0 not present, 1 empty, 2 booting, 3 card, 4 nopower
Classes: 0 none, 1 ethernet, 2 eide, 3 memstick, 4 harddisk, 5 serial
Voltage codes used: 0xbf, 0x91
Each slot has class, status, IRQ, voltage, vendor-id/device-id,
  I/O base, name string, boot timestamp.

Used DeskWork units (you have to write replacements for those
to make the unit happy - obvious) are:
Kernel (Window, Input, FrameBtn, Views, VGA driver, GDI),
Boot, Ethernet, ISA, EIDE, ExtFS, MMSound, SerPorts.

Kernel functions: FillChar, LTrim, RTrim (trivial string stuff)
Boot functions: BootText (shows a message / boot log)
Ethernet: AddPort (register ports and IRQ), InitCard, RemovePort
ISA functions: RegionFree (tests if a port range is in use)
EIDE functions: CheckInterface (test if an IDE controller is
  present at a given I/O port, and if drives are connected...
  for PCMCIA, at most 1 drive is connected for each controller)
  AddPort (as above: usually 2 ranges, distance 0x206, no IRQ),
  RemovePort (as above), FirstIDE (variable: linked disk list)
ExtFS functions: AddPort, RemovePort (as above)
MMSound: only VocIRQ value needed, to avoid IRQ clashes
SerPorts functions: for modems - AddPort, RemovePort, and the
  SetSPortDevice initialization / configuration function which
  uses the SPorts array

{ 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;
  "out_byte(base,(slot&3)<<6 + index); return in_byte(base+1);"

procedure OutCard(Slot,Index,Wert: Byte); assembler;
  "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;
  "read string of WORDs (must use lodsw!) and store only the low BYTEs"

procedure EnableModem(Socket: Byte);
  available on request

procedure EnableNE2000(Socket: Byte);
  available on request

procedure EnableShiningEIDE(Socket: Byte);
  available on request

procedure EnableNinjaEIDE(Socket: Byte);
  available on request

procedure EnableMemstick(Socket: Byte);
  available on request

procedure EnableClik(Socket: Byte);
  available on request

procedure EnableEIDENormal(Socket: Byte);
  available on request... Selects IOBase based on RegionFree output
  as 190, 180 or 1a0, then:
    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);
  ...
      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:
   ...

procedure EnableEIDEAutosize(Socket: Byte);
  available on request

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

function HotplugPCMCIA(Mount: Boolean): Boolean;
  available on request... If CPort.BasePort is nonzero, check all four
  slots for possible status changes... If card inserted, start driver
  boot process for it, using a timeout. If card removed, stop drivers
  and deregister ports and IRQ.
  ...
          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 := NoPower 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;
          { release window again }
            DisableMemoryWindow(A,0);
          end;
        end;
      end;
    end;
end;

procedure DetectPCMCIA;
  available on request... try BasePort 0x3e0...
  use 4k window at 0xcc00:0... if not all 4k contain 0xff,
  do HotplugPCMCIA(True), and do for all 4 slots (except
  those which are in "driver booting" state)...: show BasePort
  and (if card present) card name string. Increment search
  pointer by 0x400:0 (stop at 0xf000:0) to find other devices.

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;

{ "main()" of this unit }
  begin FillChar(CPort,SizeOf(CPort),0); Sel := 0; end.

