// =====================================================
//
//   miniLA_win - I/O routine (LPT)
//
//   (c) miniLA Team
//
// =====================================================
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.


unit dlgIO_LPT;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, Spin, ExtCtrls;

type
  TfrmIO_LPT = class(TForm)
    btnCancel: TBitBtn;
    btnOK: TBitBtn;
    rgLPTMode: TRadioGroup;
    rgLPTPort: TRadioGroup;
    edLPTPort: TSpinEdit;
    rgIOAccess: TRadioGroup;
    procedure btnOKClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    LPTPort		: word;

    lpt_base      	: word;		// bazova adresa LPT1
    lpt_status      	: word;		// LPT status register
    lpt_control      	: word;		// LPT control register
    lpt_eppaddress	: word;
    lpt_eppdata		: word;
    lpt_ecpexctrl	: word;

    last_address	: word;

    EPP_mode		: boolean;	// EPP mode (true)/ emulated EPP mode (false)
    DirectIO		: boolean;	// DirectI/O access (true)/ WINAPI access (false)
    PrivInstrRep	: boolean;	// Privileged instruction reported

    procedure	OutPort(Addr: word; Value: byte);
    function	InPort(Addr: word): byte;
    procedure	Delay;
    procedure   SetPort(Value: word);
public
    { Public declarations }
    procedure	Init;
    function	ReadReg(Addr: Integer): Byte;
    procedure 	WriteReg(Addr: Integer; Val: Byte);

  end;

var
  frmIO_LPT: TfrmIO_LPT;


//******************************************************************************
//******************************************************************************
//******************************************************************************

implementation

uses dlgMain, uINIFile;

{$R *.DFM}

// routines for kernel mode access
procedure Out32(PortAddress:word;Value:byte);stdcall;export;external 'inpout32.dll';
function Inp32(PortAddress:word):byte;stdcall;export;external 'inpout32.dll';


//=================================================================
procedure TfrmIO_LPT.Init;
//=================================================================
begin
   last_address := 0;
   SetPort(LPTPort);
end;


//=================================================================
procedure OutPortIO(Addr: word; Value: byte);
//=================================================================
asm
  xchg AX, DX
  out  DX, AL
end;

//=================================================================
function InPortIO(Addr: word): byte;
//=================================================================
asm
  mov DX,AX
  in  AL,DX
end;

//=================================================================
procedure TfrmIO_LPT.OutPort(Addr: word; Value: byte);
//=================================================================
begin
   if DirectIO and not(PrivInstrRep) then
      try
	 OutPortIO(Addr, Value)
      except
         on EPrivilege do
          begin
            PrivInstrRep := true;
            MessageDlg('Privileged instruction - direct I/O access not allowed.'+#10+'Kernel driver access forced.', mtError,[mbOK],0);
            Out32(Addr, Value);
          end;
      end
   else
      Out32(Addr, Value);
end;

//=================================================================
function TfrmIO_LPT.InPort(Addr: word): byte;
//=================================================================
begin
   if DirectIO and not(PrivInstrRep)then
      try
	 Result:=InPortIO(Addr)
      except
         on EPrivilege do
          begin
            PrivInstrRep := true;
	    MessageDlg('Privileged instruction - direct I/O access not allowed.'+#10+'Kernel driver access forced.', mtError,[mbOK],0);
            Result:=Inp32(Addr);
          end;
      end
   else
      Result:=Inp32(Addr);
end;

//=================================================================
procedure TfrmIO_LPT.Delay;
//=================================================================
var d: integer;
begin
  for d:=0 to 10 do
   asm NOP end;
end;


//=================================================================
function TfrmIO_LPT.ReadReg(Addr: Integer): Byte;
//=================================================================
var res	: byte;
begin
   if (EPP_mode) then
    begin
      if Addr <> last_address then
         OutPort(lpt_eppaddress,Addr);	// set addr (data)
      res := InPort(lpt_eppdata);
      if (InPort(lpt_status) and $1) <> 0 then
         raise Exception.Create('EPP Timeout detected.');
    end
   else
    begin
      if Addr <> last_address then
       begin
	 OutPort(lpt_control,$00);	// set direction OUT
	 Delay;
	 OutPort(lpt_control,$01);	// write to 0 (write)
	 Delay;
	 OutPort(lpt_base,Addr);	// set addr (data)
	 Delay;
	 OutPort(lpt_control,$09);	// adrstb to 0 (slct in)
	 Delay;
	 OutPort(lpt_control,$01);	// adrstb to 1 (slct in)
	 Delay;
       end;
      OutPort(lpt_control,$20);		// write to 1 (read), direction in
      Delay;
      OutPort(lpt_control,$22);		// datastb to 0 (autofeed)
      Delay;
      res := InPort(lpt_base);		// read data (data)
      Delay;
      OutPort(lpt_control,$20);  	// datastb to 1 (autofeed)
      Delay;
    end;
   last_address := addr;
   Result := res;
end;


//=================================================================
procedure TfrmIO_LPT.WriteReg(Addr: Integer; Val: Byte);
//=================================================================
begin
   if (EPP_mode) then
    begin
      if Addr <> last_address then
         OutPort(lpt_eppaddress,Addr);	// set addr (data)
      OutPort(lpt_eppdata,val);
      if (InPort(lpt_status) and $1) <> 0 then
         raise Exception.Create('EPP Timeout detected.');
    end
   else
    begin
      OutPort(lpt_control,$00);         // set direction OUT
      Delay;
      OutPort(lpt_control,$01);         // write do 0 (write)
      Delay;
      if Addr <> last_address then
       begin
	 OutPort(lpt_base,Addr);	// set adr (data)
	 Delay;
	 OutPort(lpt_control,$09);	// adrstb to 0 (slct in)
	 Delay;
	 OutPort(lpt_control,$01);   	// adrstb to 1 (slct in)
	 Delay;
       end;
      OutPort(lpt_base,Val);     	// set data (data)
      Delay;
      OutPort(lpt_control,$03); 	// datastb to 0 (autofeed)
      Delay;
      OutPort(lpt_control,$01);  	// datastb to 1 (autofeed)
      Delay;
      OutPort(lpt_control,$21);    	// direction IN
      Delay;
    end;
   last_address := addr;
end;


//=================================================================
procedure TfrmIO_LPT.SetPort (Value: word);
//=================================================================
var i   : word;
begin
   lpt_base:= Value;
   lpt_status:= Value+1;
   lpt_control:= Value+2;
   lpt_eppaddress := Value+3;
   lpt_eppdata := Value+4;
   lpt_ecpexctrl := Value+$402;
   PrivInstrRep := false;

   // configure if in ECP mode
   if not EPP_Mode then
      Out32(lpt_ecpexctrl, $20)		// Byte Mode
   else
      Out32(lpt_ecpexctrl, $80);	// EPP Mode

   // initialize I/O pins
   Out32(lpt_base, $FF);
   Out32(lpt_status, $FF);
   Out32(lpt_control, $00);

   // pulses on /INIT to set JTAG to IDLE (if JTAG cable connected)
   for i:=1 to 5 do
    begin
      Out32(lpt_control, $04);
      Delay;
      Out32(lpt_control, $00);
      Delay;
    end;    
end;

//=================================================================
procedure TfrmIO_LPT.FormCreate(Sender: TObject);
//=================================================================
begin
   EPP_mode := INIFile.ReadBool('HW', 'EPP_MODE', false);
   DirectIO := INIFile.ReadBool('HW', 'DIRECTIO', false);
   PrivInstrRep := false;
   LPTPort := INIFile.ReadInteger('HW', 'LPT_PORT', $378);
end;

//=================================================================
procedure TfrmIO_LPT.FormDestroy(Sender: TObject);
//=================================================================
begin
   INIFile.WriteBool('HW', 'EPP_MODE', EPP_mode);
   INIFile.WriteBool('HW', 'DIRECTIO', DirectIO and not(PrivInstrRep));
   INIFile.WriteInteger('HW', 'LPT_PORT', LPTPort);
end;

//=================================================================
procedure TfrmIO_LPT.FormActivate(Sender: TObject);
//=================================================================
begin
  // LPT port address
  case LPTPort of
    $0378 : rgLPTPort.ItemIndex := 0;
    $0278 : rgLPTPort.ItemIndex := 1;
    $03BC : rgLPTPort.ItemIndex := 2;
    $1020 : rgLPTPort.ItemIndex := 3;
  else
   begin
     rgLPTPort.ItemIndex := 4;
     edLPTPort.Value := lpt_base;
   end;
  end;

  // EPP/Emulated SPP mode
  if EPP_Mode then
     rgLPTMode.ItemIndex := 1
  else
     rgLPTMode.ItemIndex := 0;

  // direct access/kernel mode
  if DirectIO and not PrivInstrRep then
     rgIOAccess.ItemIndex := 1
  else
     rgIOAccess.ItemIndex := 0;

end;

//=================================================================
procedure TfrmIO_LPT.btnOKClick(Sender: TObject);
//=================================================================
begin
   EPP_Mode := rgLPTMode.ItemIndex = 1;
   DirectIO := rgIOAccess.ItemIndex = 1;

   case rgLPTPort.ItemIndex of
     0: LPTPort := $0378;
     1: LPTPort := $0278;
     2: LPTPort := $03BC;
     3: LPTPort := $1020;
     4: LPTPort := edLPTPort.Value;
   end;

end;



end.
