{$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=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 (Offset0) and (Offset0) and (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.