////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2023 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// Some Intel 8255 chip routines.                                             //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20030329 - Struggle for safety (add some try/except checks)...             //
// 20230622 - Modified for FPC (A.K.)                                         //
////////////////////////////////////////////////////////////////////////////////

unit _crw_i8255; // Intel 8255 chip routines, mode 0

{$I _crw_sysdef}

{$IFDEF FPC}{$mode Delphi}{$ENDIF}{$H+}

interface

uses
 _crw_sharemem, // NB: THIS UNIT MUST BE FIRST !!!
 {$IFDEF UNIX} cthreads, dl, {$ENDIF}
 {$IFDEF WINDOWS} windows, {$ENDIF}
 sysutils, classes,
 _crw_alloc, _crw_str, _crw_fio, _crw_pio;

const
 id8255PortCLo  =  $01; {Port C, low nibble}
 id8255PortB    =  $02; {Port B}
 id8255PortCHi  =  $08; {Port C, high nibble}
 id8255PortA    =  $10; {Port A}
 id8255PortC    =  id8255PortCLo+id8255PortCHi;

 {
 [Config]
 Name = 1 1 1 1 ; 0/1 = out/input mode of port A,B,Clo,CHi
 }
type
 TI8255 = class(TMasterObject)
 private
  myBaseIO : Word;
  myMode   : Word;
  function GetBaseIO:Word;
  function GetMode:Word;
 public
  constructor Create(aBaseIO:Word);
  property    BaseIO : Word read GetBaseIO;
  property    Mode   : Word read GetMode;
  procedure   SetInputMode(PortA,PortB,PortCLo,PortCHi:Boolean);
  procedure   Write(const Data:array of byte);
  procedure   Read(var Data:array of byte);
  procedure   Config(const IniFile,Section,Name:LongString);
 end;

function  NewI8255(aBaseIO:Word):TI8255;
procedure Kill(var TheObject:TI8255); overload;

implementation

constructor TI8255.Create(aBaseIO:Word);
begin
 inherited Create;
 myBaseIO:=aBaseIO;
 IOPM_Permit(myBaseIO,4,true);
 SetInputMode(true,true,true,true);
end;

function TI8255.GetBaseIO:Word;
begin
 if Assigned(Self) then Result:=myBaseIO else Result:=0;
end;

function TI8255.GetMode:Word;
begin
 if Assigned(Self) then Result:=myMode else Result:=0;
end;

procedure TI8255.SetInputMode(PortA,PortB,PortCLo,PortCHi:Boolean);
begin
 if Assigned(Self) then begin
  myMode:=$80;
  if PortA   then myMode:=myMode or id8255PortA;
  if PortB   then myMode:=myMode or id8255PortB;
  if PortCLo then myMode:=myMode or id8255PortCLo;
  if PortCHi then myMode:=myMode or id8255PortCHi;
  Port[myBaseIO+3]:=myMode;
 end;
end;

 {
 Output 3 bytes (Port A,B,C) - only ports which in output mode
 }
procedure TI8255.Write(const Data:array of byte);
var b:Byte;
begin
 if Assigned(Self) then begin
  if myMode and id8255PortA <> id8255PortA then Port[myBaseIO+0]:=Data[0];
  if myMode and id8255PortB <> id8255PortB then Port[myBaseIO+1]:=Data[1];
  if myMode and id8255PortC <> id8255PortC then begin
   b:=Data[2];
   if myMode and id8255PortCLo <> 0 then b:=b and $F0; {low  nibble is 0}
   if myMode and id8255PortCHi <> 0 then b:=b and $0F; {high nibble is 0}
   Port[myBaseIO+2]:=b;
  end;
 end;
end;

 {
 Input 3 bytes (Port A,B,C) - only ports which in input mode
 }
procedure TI8255.Read(var Data:array of byte);
var b:Byte;
begin
 if Assigned(Self) then begin
  if myMode and id8255PortA <> 0 then Data[0]:=Port[myBaseIO+0] else Data[0]:=0;
  if myMode and id8255PortB <> 0 then Data[1]:=Port[myBaseIO+1] else Data[1]:=0;
  Data[2]:=0;
  if myMode and id8255PortC <> 0 then begin
   b:=Port[myBaseIO+2];
   if myMode and id8255PortCLo <> 0 then Data[2]:=Data[2] or (b and $0F);
   if myMode and id8255PortCHi <> 0 then Data[2]:=Data[2] or (b and $F0);
  end;
 end;
end;

 {
 Read config
 Name = b0,b1,b2,b3
 b0 - portA
 b1 - portB
 b3/b4 - lo/hi nibble of PortC
 }
procedure TI8255.Config(const IniFile,Section,Name:LongString);
var b:packed array[0..3] of Boolean;
begin
 if ReadIniFileVariable(IniFile,Section,Name+'%b;%b;%b;%b',b)
 then SetInputMode(b[0],b[1],b[2],b[3]);
end;

function  NewI8255(aBaseIO:Word):TI8255;
begin
 Result:=nil;
 try
  Result:=TI8255.Create(aBaseIO);
 except
  on E:Exception do BugReport(E,nil,'NewI8255');
 end;
end;

procedure Kill(var TheObject:TI8255); overload;
begin
 try
  FreeAndNil(TheObject);
 except
  on E:Exception do BugReport(E,nil,'Kill');
 end;
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_i8255;
begin
end;

procedure Free_crw_i8255;
begin
end;

initialization

 Init_crw_i8255;

finalization

 Free_crw_i8255;

end.

//////////////
// END OF FILE
//////////////

