unit PCI;

interface

uses SysUtils,Iodev, Config, Service;

type
  bx_def440fx_t = record
    confAddr:Bit32u;
    confData:Bit32u;
    array_:array[0..256] of Bit8u;
    shadow:array[0..4*16*4096] of Bit8u;     // 256k of memory
  end;

  bx_pci_c = class
  public
    s:record
      i440fx:bx_def440fx_t;
    end;

    constructor Create;
    destructor Destroy;
    procedure init(d:Pbx_devices_c);
    procedure reset;
    procedure print_i440fx_state;
    function rd_memType(addr:Bit32u):Bit32u;
    function wr_memType(addr:Bit32u):Bit32u;
    function i440fx_fetch_ptr(addr:Bit32u):PBit8u;

  private
    devices:pbx_devices_c;

    function read_handler(this_ptr:pointer; address:Bit32u; io_len:unsigned):Bit32u;
    procedure write_handler(this_ptr:pointer; address:Bit32u; value:Bit32u; io_len:unsigned);
  {$if BX_USE_PCI_SMF=0}
    Bit32u read(Bit32u address, unsigned io_len);
    void   write(Bit32u address, Bit32u value, unsigned io_len);
  {$ifend}
    function mapRead(val:Bit32u):Bit32u;
    function mapWrite(val:Bit32u):Bit32u;
  end;

var
  bx_pci:bx_pci_c;
implementation

constructor bx_pci_c.Create;
begin
  inherited;
end;

destructor bx_pci_c.Destroy;
begin
  // nothing for now
end;

procedure bx_pci_c.init(d:pbx_devices_c );
var
  I:unsigned;
begin
  // called once when bochs initializes

  Self.devices := d;

  if SUPPORT440FX <> 0 then begin
    for i:=$0CFC to $0CFF do begin
      d^.register_io_read_handler(Self, read_handler, i, 'i440FX');
      end;

    d^.register_io_write_handler(Self, write_handler, $0CF8, 'i440FX');
    for i:=$0CFC to $0CFF do begin
      d^.register_io_write_handler(Self, write_handler, i, 'i440FX');
      end;

    for i:=0 to 256 do
      Self.s.i440fx.array_[i] := $0;
    end;
    // readonly registers
    Self.s.i440fx.array_[$00] := $86;
    Self.s.i440fx.array_[$01] := $80;
    Self.s.i440fx.array_[$02] := $37;
    Self.s.i440fx.array_[$03] := $12;
    Self.s.i440fx.array_[$0b] := $06;
end;

procedure bx_pci_c.reset;
var
  I:unsigned;
begin
  Self.s.i440fx.array_[$04] := $06;
  Self.s.i440fx.array_[$05] := $00;
  Self.s.i440fx.array_[$06] := $80;
  Self.s.i440fx.array_[$07] := $02;
  Self.s.i440fx.array_[$0d] := $00;
  Self.s.i440fx.array_[$0f] := $00;
  Self.s.i440fx.array_[$50] := $00;
  Self.s.i440fx.array_[$51] := $01;
  Self.s.i440fx.array_[$52] := $00;
  Self.s.i440fx.array_[$53] := $80;
  Self.s.i440fx.array_[$54] := $00;
  Self.s.i440fx.array_[$55] := $00;
  Self.s.i440fx.array_[$56] := $00;
  Self.s.i440fx.array_[$57] := $01;
  Self.s.i440fx.array_[$58] := $10;
  for i:= $59 to $60 do
    Self.s.i440fx.array_[i] := $00;
end;

  // static IO port read callback handler
  // redirects to non-static class handler to aprocedure virtual functions

function bx_pci_c.read_handler(this_ptr:pointer; address:Bit32u; io_len:unsigned):Bit32u;
var
  val440fx, retMask:Bit32u;
begin
  case (address) of
    $0CFC,
    $0CFD,
    $0CFE,
    $0CFF:
      begin
      // PMC is bus 0 / device 0 / function 0
      if Boolean((Self.s.i440fx.confAddr  and $80FFFF00) = $80000000) then begin
        val440fx := Self.s.i440fx.confData shr ((address  and $3)*8);

        if io_len = 1 then retMask:=$FF;
        if io_len = 2 then retMask:=$FFFF;
        if io_len = 4 then retMask:=$FFFFFFFF;
        if (io_len <> 1) and (io_len <> 2) and (io_len <> 4) then retMask:=$FFFFFFFF;

        val440fx := (val440fx  and retMask);
      	BX_DEBUG(Format('440FX PMC read register $%02x value $%08x',[
  		  Self.s.i440fx.confAddr + (address and $3), val440fx]));
        Result:=val440fx;
        exit;
        end
      else
        Result:=$FFFFFFFF;
        exit;
      end;
    end;

  BX_PANIC(Format('unsupported IO read to port $%x',[unsigned(address)]));
  Result:=$ffffffff;
end;


  // static IO port write callback handler
  // redirects to non-static class handler to aprocedure virtual functions

procedure bx_pci_c.write_handler(this_ptr:pointer; address:Bit32u; value:Bit32u; io_len:unsigned);
var
  idx440fx:Bit8u;
  max_len:Bit8u;
  i:unsigned;
  label end_case;
begin
{$if BX_USE_PCI_SMF=0}
  bx_pci_c *class_ptr := (bx_pci_c *) Self_ptr;

  class_ptr^.write(address, value, io_len);
end;

  procedure
bx_pci_c.write(Bit32u address, Bit32u value, unsigned io_len)
begin
{$else}
  //UNUSED(Self_ptr);
{$ifend} // !BX_USE_PCI_SMF

  case address of
    $CF8:
      begin
        // confAddr accepts a dword value only
        if Boolean(io_len = 4) then begin
          Self.s.i440fx.confAddr := value;
          if Boolean((value  and $80FFFF00) = $80000000) then begin
            idx440fx := Bit8u(value  and $FC);
            Move(Self.s.i440fx.confData, Self.s.i440fx.array_[idx440fx], 4);
            BX_DEBUG(Format('440FX PMC register $%02x selected',[idx440fx]));
            end
          else begin
            Self.s.i440fx.confData := 0;
            BX_DEBUG(Format('440FX request for bus $%02x device $%02x function $%02x',
                [(value shr 16)  and $FF, (value shr 11)  and $1F, (value shr 8)  and $07]));
              end;
        end;
        end;

    $CFC,
    $CFD,
    $CFE,
    $CFF:
      begin

      idx440fx := Bit8u((Self.s.i440fx.confAddr  and $FC) + (address  and $3));
      max_len := 4 - (address  and $3);
      if Boolean(io_len < max_len) then max_len := io_len;
      if Boolean((Self.s.i440fx.confAddr  and $80FFFF00) = $80000000) then begin
        for i:=0 to max_len do
          begin
            case idx440fx+i of
              $00,
              $01,
              $02,
              $03,
              $06,
              $08,
              $09,
              $0a,
              $0b:
                goto end_case;
              else
                begin
                  Self.s.i440fx.array_[idx440fx+i] := (value shr (i*8))  and $FF;
                  BX_DEBUG(Format('440FX PMC write register $%02x value $%02x',
                  [idx440fx, (value shr (i*8))  and $FF]));
                end;
            end;
          end;
      end_case:
    	Move(Self.s.i440fx.confData, Self.s.i440fx.array_[idx440fx], 4);
        end;
      end
    else
      BX_PANIC(Format('IO write to port $%x',[unsigned(address)]));
    end;
end;

function bx_pci_c.mapRead(val:Bit32u):Bit32u;
begin
   case val of
     $0,
     $2:
      begin
          Result:=1;    // (0) Goto ROM
          Exit;
      end;

     $1,
     $3:
      begin
          Result:=0;    // (1) Goto Shadow
          Exit;
      end;
   end;
   Result:=2;
end;

function bx_pci_c.mapWrite(val:Bit32u):Bit32u;
begin
   case val of
     $0,
     $1:
      begin
          Result:=1;    // (0) Goto ROM
          Exit;
      end;

     $2,
     $3:
      begin
          Result:=0;    // (1) Goto Shadow
          Exit;
      end;
   end;
   Result:=2;
end;

function bx_pci_c.rd_memType(addr:Bit32u):Bit32u;
begin
   case ((addr and $FC000) shr 12) of
      $C0:
        begin
           Result:=(mapRead ( Self.s.i440fx.array_[$5A]  and $3));
        end;
      $C4:
        begin
           Result:=(mapRead ( (Self.s.i440fx.array_[$5A] shr 4)  and $3));
        end;
      $C8:
        begin
           Result:=(mapRead ( Self.s.i440fx.array_[$5B]   and $3));
        end;
      $CC:
        begin
           Result:=(mapRead ( (Self.s.i440fx.array_[$5B] shr 4)  and $3));
        end;
      $D0:
        begin
           Result:=(mapRead ( Self.s.i440fx.array_[$5C]  and $3));
        end;
      $D4:
        begin
           Result:=(mapRead ( (Self.s.i440fx.array_[$5C] shr 4)  and $3));
        end;
      $D8:
        begin
           Result:=(mapRead ( Self.s.i440fx.array_[$5D]  and $3));
        end;
      $DC:
        begin
           Result:=(mapRead ( (Self.s.i440fx.array_[$5D] shr 4)  and $3));
        end;
      $E0:
        begin
           Result:=(mapRead ( Self.s.i440fx.array_[$5E]  and $3));
        end;
      $E4:
        begin
           Result:=(mapRead ( (Self.s.i440fx.array_[$5E] shr 4)  and $3));
        end;
      $E8:
        begin
           Result:=(mapRead ( Self.s.i440fx.array_[$5F]  and $3));
        end;
      $EC:
        begin
           Result:=(mapRead ( (Self.s.i440fx.array_[$5F] shr 4)  and $3));
        end;

      $F0,
      $F4,
      $F8,
      $FC:
        begin
           Result:=(mapRead ( (Self.s.i440fx.array_[$59] shr 4)  and $3));
        end;

      else
        begin
           BX_PANIC(('rd_memType () Error: Memory Type not known !'));
           Result:=0; // keep compiler happy
           exit;
        end;
   end;

end;

function bx_pci_c.wr_memType(addr:Bit32u):Bit32u;
begin
   case ((addr and $FC000) shr 12) of
      $C0:
        begin
           Result:=(mapwrite ( Self.s.i440fx.array_[$5A]  and $3));
        end;
      $C4:
        begin
           Result:=(mapwrite ( (Self.s.i440fx.array_[$5A] shr 4)  and $3));
        end;
      $C8:
        begin
           Result:=(mapwrite ( Self.s.i440fx.array_[$5B]   and $3));
        end;
      $CC:
        begin
           Result:=(mapwrite ( (Self.s.i440fx.array_[$5B] shr 4)  and $3));
        end;
      $D0:
        begin
           Result:=(mapwrite ( Self.s.i440fx.array_[$5C]  and $3));
        end;
      $D4:
        begin
           Result:=(mapwrite ( (Self.s.i440fx.array_[$5C] shr 4)  and $3));
        end;
      $D8:
        begin
           Result:=(mapwrite ( Self.s.i440fx.array_[$5D]  and $3));
        end;
      $DC:
        begin
           Result:=(mapwrite ( (Self.s.i440fx.array_[$5D] shr 4)  and $3));
        end;
      $E0:
        begin
           Result:=(mapwrite ( Self.s.i440fx.array_[$5E]  and $3));
        end;
      $E4:
        begin
           Result:=(mapwrite ( (Self.s.i440fx.array_[$5E] shr 4)  and $3));
        end;
      $E8:
        begin
           Result:=(mapwrite ( Self.s.i440fx.array_[$5F]  and $3));
        end;
      $EC:
        begin
           Result:=(mapwrite ( (Self.s.i440fx.array_[$5F] shr 4)  and $3));
        end;

      $F0,
      $F4,
      $F8,
      $FC:
        begin
           Result:=(mapwrite ( (Self.s.i440fx.array_[$59] shr 4)  and $3));
        end;

      else
        begin
           BX_PANIC(('rd_memType () Error: Memory Type not known !'));
           Result:=0; // keep compiler happy
           exit;
        end;
   end;

end;

procedure bx_pci_c.print_i440fx_state;
var
  i:integer;
begin

  BX_INFO(Format( 'i440fxConfAddr:$%08x', [Self.s.i440fx.confAddr]));
  BX_INFO(Format( 'i440fxConfData:$%08x', [Self.s.i440fx.confData]));

{$if DUMP_FULL_I440FX=1}
  for (i:=0; i<256; i++) then begin
    BX_INFO(( 'i440fxArray%02x:$%02x', i, Self.s.i440fx.array[i] ));
    end;
{$else} (* DUMP_FULL_I440FX *)
  for i:=$59 to $60 do
    begin
      BX_INFO(Format( 'i440fxArray%02x:$%02x',[ i, Self.s.i440fx.array_[i]]));
    end;
{$ifend} (* DUMP_FULL_I440FX *)
end;

function bx_pci_c.i440fx_fetch_ptr(addr:Bit32u):PBit8u;
begin
  if SUPPORT440FX <> 0 then begin
    case (rd_memType (addr)) of
      $0:   // Read from ShadowRAM
        begin
          Result:=(@Self.devices^.mem^.vector[addr]);
          exit;
        end;

      $1:   // Read from ROM
        begin
          Result:=(@Self.s.i440fx.shadow[(addr - $c0000)]);
          exit;
        end;
      else
        begin
          BX_PANIC(('i440fx_fetch_ptr(): default case'));
          Result:=0;
          exit;
        end;
      end;
    end
  else
    Result:=(@Self.devices^.mem^.vector[addr]);
end;
end.
