SCSI Controller and PCI Device Initialization

Technical discussion for those interested in Supermodel development and Model 3 reverse engineering. Prospective contributors welcome.
Forum rules
Keep it classy!

  • No ROM requests or links.
  • Do not ask to be a play tester.
  • Do not ask about release dates.
  • No drama!

SCSI Controller and PCI Device Initialization

Postby Bart » Wed Oct 17, 2018 1:07 am

Last year I had tried to run some additional tests on my Model 3 board that failed. Both my SCSI DMA test (which may be required for accessing Real3D RAM regions) and Real3D flush tests failed. I was using SCSI DMA code I found in Fighting Vipers 2 as the basis for my routines. The last couple of days, I've been looking at VF3's routines and stumbled upon something interesting: an initialization routine that isn't being executed in Supermodel because Supermodel fails to return the PCI vendor and device ID for the SCSI controller.

From an emulation point of view, this doesn't matter too much, although looking at how the device is supposed to be initialized clarifies some things that previously didn't make sense to me in VF3's SCSI logic. For completeness' sake, we should eventually make the SCSI emulator more strictly correct. More importantly, though, this is definitely one of the reasons my SCSI DMA code failed. I'd like to try to run another test, possibly this Saturday (though I won't be able to do much more than that even if it succeeds), since I'll be at my parents' for the weekend.

I'll eventually need to check for any other PCI devices (like the Real3D itself) that may not be getting initialized properly. While not really important from an emulation perspective, it is important for understanding how to bring the device up when trying to run new code on it. I was assuming Supermodel was accurate enough and was simply trapping all writes to hardware I was interested in bringing up, and then replicating those sequences in my Model 3 test program. This obviously isn't sufficient if Supermodel is skipping over important initialization routines!

Here's VF3's init routine. Note the test it performs to ensure that an LSI 53c810a SCSI controller is actually present on the PCI bus. Unsure yet whether there are other routines that need to run, too.

Code: Select all
void Sub619B0()
{
  uint32_t device_and_vendor_id = ReadPCIConfig(0, 0xe, 0, 0);  // device in high 16 bits, vendor in low 16 bits
 
  uint32_t lsi53c810a_id = 0x11000;
  if (device_and_vendor_id != lsi53c810a_id)
    goto _61a2c;
 
  WritePCIConfig(0x14, 0xe, 0, 0, 0xc0000000);  // set device base address
 
  WritePCIConfig(0x0c, 0xe, 0, 0, 0xff00);      // cache line size and latency timer
 
  WritePCIConfig(4, 0xe, 0, 0, 6);              // enable bus mastering and memory space
 
  scsi_regs[0x38] = 0xc1;                   // DMODE (DMA Mode) = 0xc1 (16-transfer burst, manual start mode)
  scsi_regs[0x39] = scsi_regs[0x39] | 0x08; // DIEN (DMA Interrupt Enable) |= 0x08 (enable single-step interrupt)
 
_61a2c: 
  _6c4f = 0x00;
  _6c50 = 0x00;
 
  return device_and_vendor_id
}

uint32_t ReadPCIConfig(uint32_t pci_reg, uint32_t pci_device, uint32_t pci_bus, uint32_t pci_function)
{
  uint32_t cmd = 0x80000000;
  cmd |= (pci_reg & 0xfc);
  cmd |= ((pci_device << 11) & 0xf800);
  cmd |= ((pci_bus << 16) & 0x00ff0000);
  cmd |= ((pci_function << 8) & 0x700);
  ppc_stwbrx(0xf0800cf8, cmd);
  return ppc_lwbrx(0xf0c00cfc);
}

void WritePCIConfig(uint32_t pci_reg, uint32_t pci_device, uint32_t pci_bus, uint32_t pci_function, uint32_t data)
{
  uint32_t cmd = 0x80000000;
  cmd |= (pci_reg & 0xfc);
  cmd |= ((pci_device << 11) & 0xf800);
  cmd |= ((pci_bus << 16) & 0xff0000);
  cmd |= ((pci_function << 8) & 0x700);
  ppc_stwbrx(0xf0800cf8, cmd);
  ppc_stwbrx(0xf0c00cfc, data);
}
User avatar
Bart
Site Admin
 
Posts: 2200
Joined: Thu Sep 01, 2011 2:13 pm
Location: New York City

Re: SCSI Controller and PCI Device Initialization

Postby Ian » Wed Oct 17, 2018 3:56 pm

What code are you hoping to be able to run on the board? :)
Ian
 
Posts: 1535
Joined: Tue Feb 23, 2016 9:23 am

Re: SCSI Controller and PCI Device Initialization

Postby Bart » Wed Oct 17, 2018 10:00 pm

Well, I'd ultimately like to replicate the frame timing test that each of the games performs on boot up. This involves setting up the Real3D to some basic functional state. I think it can be done via CPU alone but only if the PowerPC's MMU is set up correctly. I'm not confident I've done that. I do think I can make the SCSI device accessible from the CPU and go from there. It's... complicated :)
User avatar
Bart
Site Admin
 
Posts: 2200
Joined: Thu Sep 01, 2011 2:13 pm
Location: New York City

Re: SCSI Controller and PCI Device Initialization

Postby Bart » Sat Oct 20, 2018 9:19 pm

Good news and bad news:

1. My SCSI code works :) libmodel3 has been updated accordingly.
2. Still haven't been able to get anything useful out of the Real3D (see test output below).

I don't have much time tomorrow, so this may need to be put off until Christmas. It'll take some time to write a more complete test program that properly initializes the Real3D memory regions.

real3d_test_failure.jpeg
Real3D test failure.
real3d_test_failure.jpeg (716.17 KiB) Viewed 555 times
User avatar
Bart
Site Admin
 
Posts: 2200
Joined: Thu Sep 01, 2011 2:13 pm
Location: New York City

Re: SCSI Controller and PCI Device Initialization

Postby Ian » Sun Oct 21, 2018 7:15 am

Well at least you made some progress :)
Wonder if the GPU needs something with jtag to get going ..

This is what the init functions looks like in the sdk

Code: Select all
signed int __cdecl PRO_Init(int a1, int a2, int a3, int a4)
{
  signed int result; // eax@2
  int v5; // [sp+0h] [bp-2Ch]@39
  PRO_Point_Color_Table *v6; // [sp+4h] [bp-28h]@38
  PRO_API_Data *v7; // [sp+8h] [bp-24h]@12
  PRO_API_Data *v8; // [sp+Ch] [bp-20h]@11
  int v9; // [sp+10h] [bp-1Ch]@25
  int v10; // [sp+10h] [bp-1Ch]@34
  int v11; // [sp+10h] [bp-1Ch]@36
  int i; // [sp+14h] [bp-18h]@20
  int v13; // [sp+18h] [bp-14h]@31
  int v14; // [sp+1Ch] [bp-10h]@1
  unsigned int v15; // [sp+20h] [bp-Ch]@1
  _UNKNOWN *v16; // [sp+24h] [bp-8h]@1
  int v17; // [sp+28h] [bp-4h]@1

  v17 = -1;
  v16 = &_L33207;
  v15 = __readfsdword((signed __int32)&_except_list);
  __writefsdword((signed __int32)&_except_list, (unsigned int)&v15);
  v14 = 1;
  printf(_SG32665, _SG32664);
  if ( box_init_done_S32599 )
  {
    result = 2;
  }
  else
  {
    InitPolygonSizes();
    if ( g_API_Data )
    {
      if ( !((*((_DWORD *)g_API_Data + 504) >> 1) & 1) && !init_ls(&_SG32673) )
      {
        result = 2;
        goto LABEL_48;
      }
    }
    else if ( !init_ls(&_SG32669) )
    {
      result = 2;
      goto LABEL_48;
    }
    if ( !g_API_Data )
    {
      v8 = (PRO_API_Data *)operator new(0x4BF4u);
      v17 = 0;
      if ( v8 )
        v7 = (PRO_API_Data *)PRO_API_Data::PRO_API_Data(v8);
      else
        v7 = 0;
      v17 = -1;
      g_API_Data = v7;
      printf(_SG32678);
    }
    if ( (*((_DWORD *)g_API_Data + 504) >> 1) & 1 )
      printf(_SG32680);
    PRO_GetHardwareConfig();
    if ( *((_DWORD *)g_API_Data + 327) < 4096 )
      *((_DWORD *)g_API_Data + 327) = 4096;
    if ( !((*((_DWORD *)g_API_Data + 504) >> 1) & 1) )
    {
      for ( i = 0; *((_DWORD *)g_API_Data + 338) > i; ++i )
      {
        if ( *((_DWORD *)g_API_Data + i + 345) )
          ls_unit_off(i);
      }
    }
    *((_DWORD *)g_API_Data + 500) = a3;
    *((_DWORD *)g_API_Data + 501) = a4;
    *((_DWORD *)g_API_Data + 502) = a2;
    v9 = PRO_API_Data::Init(g_API_Data);
    if ( v9 == 1 )
    {
      PRO_Culling_Data_Block::NotifyUpdate(*((PRO_Culling_Data_Block **)g_API_Data + 20));
      PRO_Pingpong_Data_Block::NotifyUpdate(*((PRO_Pingpong_Data_Block **)g_API_Data + 24));
    }
    if ( v9 == 7 )
    {
      result = 7;
    }
    else
    {
      if ( v14 == 1 )
        v14 = v9;
      *((_DWORD *)g_API_Data + 342) = 2;
      v13 = 0;
      PRO_IO(&v13, 1, 0x20000001, -1, 0);
      v13 = 1;
      PRO_IO(&v13, 1, 0x20000002, -1, 0);
      PRO_Flush();
      PRO_SetGenlock(-1);
      if ( *((_DWORD *)g_API_Data + 483) )
      {
        *((_DWORD *)g_API_Data + 486) = 512;
        *((_DWORD *)g_API_Data + 487) = 8;
      }
      else
      {
        *((_DWORD *)g_API_Data + 486) = 256;
        *((_DWORD *)g_API_Data + 487) = 4;
      }
      PRO_SetMicrotextureMapCount(*((_DWORD *)g_API_Data + 487));
      v10 = PRO_SetUpdateRate(a1, -1);
      if ( v14 == 1 )
        v14 = v10;
      PRO_SetScroll(1);
      v11 = PRO_InitializeMemoryConfiguration();
      if ( v11 == 1 )
      {
        v6 = (PRO_Point_Color_Table *)operator new(0x4Cu);
        v17 = 1;
        if ( v6 )
          v5 = PRO_Point_Color_Table::PRO_Point_Color_Table(v6);
        else
          v5 = 0;
        v17 = -1;
        *((_DWORD *)g_API_Data + 505) = v5;
        if ( *((_DWORD *)g_API_Data + 505) )
        {
          if ( **((_DWORD **)g_API_Data + 505) == 1 )
          {
            PRO_API_Data::SetUpFirstViewport(g_API_Data);
            box_init_done_S32599 = 1;
            if ( *((_DWORD *)g_API_Data + 47) )
              PRO_Viewport::UpdateSiblingPointers(*((PRO_Viewport **)g_API_Data + 47));
            PRO_PingPong_Manager::WriteAllDataToHardware(*(_DWORD *)g_API_Data);
            PRO_Flush();
            PRO_PingPong_Manager::WriteAllDataToHardware(*(_DWORD *)g_API_Data);
            PRO_Flush();
            result = v14;
          }
          else
          {
            result = **((_DWORD **)g_API_Data + 505);
          }
        }
        else
        {
          result = 7;
        }
      }
      else
      {
        result = v11;
      }
    }
  }
LABEL_48:
  __writefsdword((signed __int32)&_except_list, v15);
  return result;
}


A lot of that stuff is client side. But it seems to need some default viewport setup.

Guessing this is important somehow ..
PRO_API_Data::SetUpFirstViewport(g_API_Data);
box_init_done_S32599 = 1;
if ( *((_DWORD *)g_API_Data + 47) )
PRO_Viewport::UpdateSiblingPointers(*((PRO_Viewport **)g_API_Data + 47));
PRO_PingPong_Manager::WriteAllDataToHardware(*(_DWORD *)g_API_Data);
PRO_Flush();
PRO_PingPong_Manager::WriteAllDataToHardware(*(_DWORD *)g_API_Data);
PRO_Flush();
Ian
 
Posts: 1535
Joined: Tue Feb 23, 2016 9:23 am

Re: SCSI Controller and PCI Device Initialization

Postby Bart » Sun Oct 21, 2018 10:18 am

Yup. All the games have an initial set of transfers: one to culling RAM, two to ping pong memory (presumably so that both buffers are loaded with identical display lists). In my case, I'm almost certain the JTAG values are not being loaded correctly. I've got enough to get the tile generator up but I'm probably missing something. In the future, I'll have to sit down and rework the JTAG stuff completely.

Unfortunately, my EPROMs are starting to wear out. One of them appears to be permanently hosed. Replacements can no longer be purchased new and must be found on eBay. I should look into designing a riser PCB that plugs into the 4 EPROM slots and hosts 4 zero insertion force sockets for easier insertion and removal. It might also be possible to use a different kind of ROM in that case, too (like a flash ROM).
User avatar
Bart
Site Admin
 
Posts: 2200
Joined: Thu Sep 01, 2011 2:13 pm
Location: New York City

Re: SCSI Controller and PCI Device Initialization

Postby Ian » Sun Oct 21, 2018 10:22 am

What were u giving it to render?
The default viewport seems to attach a single polygon to it

Not quite sure what the point of a single poly is, but maybe the HW needs something to render

Edit:
This is the code for it
Code: Select all
void __thiscall PRO_API_Data::SetUpFirstViewport(PRO_API_Data *this)

        v18 = operator new(0x20u);
        v23 = 1;
        if ( v18 )
          v17 = PRO_Vertex::PRO_Vertex(v18, 0, 0x3F800000, 0xBF800000);
        else
          v17 = 0;
        v23 = -1;
        *((_DWORD *)v2 + 49) = v17;
        v16 = operator new(0x20u);
        v23 = 2;
        if ( v16 )
          v15 = PRO_Vertex::PRO_Vertex(v16, 0, 0x3F800000, 0x3F800000);
        else
          v15 = 0;
        v23 = -1;
        *((_DWORD *)v2 + 50) = v15;
        v14 = operator new(0x20u);
        v23 = 3;
        if ( v14 )
          v13 = PRO_Vertex::PRO_Vertex(v14, 0, 0xBF800000, 0x3F800000);
        else
          v13 = 0;
        v23 = -1;
        *((_DWORD *)v2 + 51) = v13;
        v12 = operator new(0x20u);
        v23 = 4;
        if ( v12 )
          v11 = PRO_Vertex::PRO_Vertex(v12, 0, 0xBF800000, 0xBF800000);
        else


            PRO_Polygon::IsDoubleSided((PRO_Polygon *)v9);
            PRO_Polygon::EnableAutoVertexDelete((PRO_Polygon *)v9);
            *(_DWORD *)(v9 + 24) |= 8u;
            *(_DWORD *)(v9 + 24) &= 0xFF03FFFF;
            PRO_Polygon::AddVertex((PRO_Polygon *)v9, *((struct PRO_Vertex **)v2 + 49));
            PRO_Polygon::AddVertex((PRO_Polygon *)v9, *((struct PRO_Vertex **)v2 + 50));
            PRO_Polygon::AddVertex((PRO_Polygon *)v9, *((struct PRO_Vertex **)v2 + 51));
            PRO_Polygon::AddVertex((PRO_Polygon *)v9, *((struct PRO_Vertex **)v2 + 52));

Ian
 
Posts: 1535
Joined: Tue Feb 23, 2016 9:23 am

Re: SCSI Controller and PCI Device Initialization

Postby Bart » Mon Oct 22, 2018 10:30 pm

I've just pushed my latest test code to Github. test_real3d_status_bit() is the function that now locks up right after the initial stat packet is printed, indicating that the subsequent memory copies lock up the entire system. The data being copied is the same as what VF3 initializes them to, with the exception of the stop bit in the display list being set on the first element because I can't embed all of that data into the 2MB of ROM I have to work with. You're right that it probably needs something to render but the error I'm seeing is that the entire system locks up, which probably indicates some sort of bus error. Even if the Real3D itself was locking up, you wouldn't think it would take down the whole system with it, which is why I suspect that the device is simply set up incorrectly.

I'll probably need to do two things in the future:

1. Replicate the JTAG data *exactly*.
2. Replicate all PCI and config register writes. VF3 does something a little strange in that it seems to write the same value to a particular PCI register 3 times.
3. Replicate the timing of initial transfers to Real3D memory.

I also need to snag a cheap Model 3 ROM board ASAP so I can take the measurements necessary to lay out an adapter PCB for ZIF sockets.
User avatar
Bart
Site Admin
 
Posts: 2200
Joined: Thu Sep 01, 2011 2:13 pm
Location: New York City

Re: SCSI Controller and PCI Device Initialization

Postby Ian » Tue Oct 23, 2018 2:51 am

Not sure if helpful
But the first things written to the GPU are actually to the configuration registers.
So far we have no idea what these things do. My initial guess was that it was related to setup to do with

Code: Select all
PRO_Status PRO_Init ( PRO_Update_Rate    update_rate    = UPDATE_60_HERTZ
                    , PRO_Overload_Mode  overload_mode  = OVERLOAD_OFF
                    , PRO_Display_Select display_select = SINGLE_DISPLAY
                    , PRO_Line_Rate      line_rate      = VGA_640X480 ) ;


In the sdk on the real3d they seem to be written here

ls_and(0x70000000, -3, a2);
This is
#define PRO_MODE_WORD_BASE 0x70000000

which seems to be totally unused on the model3.

Should probably add, the resolution and refresh rate used on the model3 don't exist on the real3d pro-1000 stand alone. So maybe they used these config registers instead.
Ian
 
Posts: 1535
Joined: Tue Feb 23, 2016 9:23 am

Re: SCSI Controller and PCI Device Initialization

Postby Bart » Tue Oct 23, 2018 7:42 pm

How many words are written to that address? On Model 3 there are 3 32-bit word-sized registers at 0x9c000000. Could this be the same thing?
User avatar
Bart
Site Admin
 
Posts: 2200
Joined: Thu Sep 01, 2011 2:13 pm
Location: New York City

Next

Return to The Dark Room

Who is online

Users browsing this forum: No registered users and 4 guests

cron