unit i2c;

{ -------------------------- } interface { ------------------------- }

uses
  Ppar;
  
{ $define debug}
  
const
  I2C_SDAOUT = 7;		{ Bit 7 of the data port }
  I2C_SDAIN  = PPAR_BUSY;
  I2C_SCLOUT = PPAR_SELECT;
  I2C_SCLIN  = PPAR_ERROR;
  
  I2C_READ = $01;
  I2C_WRITE = $00;
  
  { These compensate the external hardware connected to the parport }
  I2C_INV_SDAIN = true;
  I2C_INV_SDAOUT = true;
  I2C_INV_SCLOUT = true;
  I2C_INV_SCLIN = false;	{ There are two inverters, so no inversion }

type
  i2c_port = object (par_port)
    i2c_addr: word;
    
    procedure Initialize(paddr, iaddr: word);
    procedure SetSDA(state: boolean);
    function  GetSDA: boolean;
    procedure SetSCL(state: boolean);
    function  GetSCL: boolean;
    procedure Start;
    procedure Stop;
    function  ByteSent(b: byte): boolean;
    function  ByteReceived(var b: byte): boolean;
    function  Responds: boolean;
  end;


{ -------------------------- } implementation { -------------------- }

procedure i2c_port.Initialize(paddr, iaddr: word);
begin
  inherited Initialize(paddr);
  i2c_addr := iaddr;
  {$ifdef debug}
    writeln('i2cport Initialize. Port: ', HexStr(paddr, 4), '  i2c: ',
            HexStr(iaddr, 2));
  {$endif}
  SetSCL(true);
  SetSDA(true);
end;

procedure i2c_port.SetSDA(state: boolean);
begin
  SetDataBit(I2C_SDAOUT, state xor I2C_INV_SDAOUT);
end;

function i2c_port.GetSDA: boolean;
begin
  GetSDA := GetStatusBit(I2C_SDAIN) xor I2C_INV_SDAIN;
end;

procedure i2c_port.SetSCL(state: boolean);
begin
  SetControlBit(I2C_SCLOUT, state xor I2C_INV_SCLOUT);
end;

function i2c_port.GetSCL: boolean;
begin
  GetSCL := GetStatusBit(I2C_SCLIN) xor I2C_INV_SCLIN;
end;

procedure i2c_port.Start;
begin
  SetSDA(false);
  SetSCL(false);
end;

procedure i2c_port.Stop;
begin
  SetSDA(false);
  SetSCL(true);
  SetSDA(true);
end;

function i2c_port.ByteSent(b: byte): boolean;
var
  bit: integer;
begin
  {$ifdef debug}
  writeln('ByteSent: ', HexStr(b, 2));
  {$endif}
  for bit := 7 downto 0 do begin
    SetSDA((b and (1 shl bit)) <> 0);
    SetSCL(true);
    SetSCL(false);
    {$ifdef debug}
      write((b and (1 shl bit)) <> 0);
    {$endif}
  end;
  {$ifdef debug}
    writeln;
  {$endif}
  SetSDA(true);
  SetSCL(true);
  ByteSent := not GetSDA;
  SetSCL(false);
end;

function i2c_port.ByteReceived(var b: byte): boolean;
var
  bit: integer;
  data: byte;
begin
  data := 0;
  SetSDA(true);		{ Be sure to float the SDA line }
  for bit := 0 to 7 do begin
    SetSCL(true);
    data := (data shl 1);
    if GetSDA then
      data := data or 1;
    SetSCL(false);
  end;
  SetSDA(false);	{ Send ACK to peripheral }
  SetSCL(true);
  SetSCL(false);
  b := data;
  ByteReceived := true;
end;

function  i2c_port.Responds: boolean;
begin
  Responds := false;
  Start;
  if ByteSent(i2c_addr + I2C_READ) then
    Responds := true;
  Stop;
end;
    

begin
end.
