gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Beginners > SIO Normal Mode

#66060 - Ultima2876 - Sat Jan 07, 2006 11:37 pm

I've got some normal mode multiplayer code, for sending game assets from the master to the slave (just one way communications):

Code:
void transfer_data(u8 master, u32 *data, u32 size)      //master - 1 for master, 0 for slave, *data - pointer to data to send, size - size of data in bytes
   {
      u32 location = 0;

      REG_RCNT = 0;         //init RCNT
      if(master)
         {
            //make sure size is correct
            if(size == 0) { return; }
            if(size & 3 != 0) { size += 4; }
            size = (size - (size & 3)) >> 2;
            REG_SIOCNT = 0x1001;            //init SIOCNT for master
            while(location < size)            //start sending
               {
                  REG_SIODATA32 = ((u32*)data)[location];      //set the data to send
                  while(REG_SIOCNT & 4) { }                  //while the SI is HIGH (slave NOT READY), wait
                  REG_SIOCNT |= 0x80;                        //set the start bit to start the transfer
                  while((REG_SIOCNT & 0x80) == 1) { }            //wait for the start bit to clear (transfer done)
                  errorcount += 1;
                  location += 1;
                  //WaitForVsync();
               }
         }
      else
         {
            //make sure size is correct
            if(size == 0) { return; }
            if(size & 3 != 0) { size += 4; }
            size = (size - (size & 3)) >> 2;
            REG_SIOCNT = 0x1008;            //init for slave (with SO HIGH, to indicate NOT READY)
            while(location < size)
               {
                  REG_SIODATA32 = 0;               //clear the data
                  REG_SIOCNT |= 0x80;               //set the start bit so that you can wait for the transfer to complete
                  REG_SIOCNT &= 0xFFF7;            //Set SO LOW - READY
                  while(REG_SIOCNT & 0x80) { }      //wait for the start bit to clear (transfer done)
                  REG_SIOCNT |= 8;               //Set SO HIGH - NOT READY
                  ((u32*)data)[location] = REG_SIODATA32;      //write the received data
                  errorcount += 1;
                  location += 1;
                  //WaitForVsync();
               }
         }
   }


There are two WaitForVsync calls in there - if they're commented, as they are above, the game crashes after sending 4 bytes (1 transfer). It only works if it waits for Vsync every frame -- surely this can't be due to speed limits? 4 bytes/frame? that's disgustingly slow o_O

The SI/SO bits should be taking care of sync here, shouldn't they? Or is there something I'm missing? I saw another topic with a very similar problem, but when the guy fixed the problem he didn't actually say what the solution was...

Are there any working examples of one-way normal mode transfers around?

Thanks for any help.


Last edited by Ultima2876 on Sun Jan 08, 2006 3:06 pm; edited 1 time in total

#66117 - Ultima2876 - Sun Jan 08, 2006 10:20 am

EDIT: I've got it almost working (it works in no$) but I have a problem on hardware... If the SLAVE sets its SO LOW on an AGB-005 link cable, will the master 's SI change to LOW? I suspect that my problem is something to do with this - and am wondering if by "one way" communications you mean even this doesn't work.

#66203 - ScottLininger - Mon Jan 09, 2006 12:30 am

Can you post the your DEFINEs so I can see what all you're doing? The defines I have in my code have different names, so I'm having trouble comparing.

Then I might be able to shed some light. :)

-Scott

#66222 - wintermute - Mon Jan 09, 2006 3:16 am

A lot of GBA link cables I've seen are set up as a master/slave cable, i.e. SI on the master side is grounded - this is one of the reasons GBA cables aren't always suitable for making xboo cables with :/

The SI line floats high so you can test this by writing an app which monitors the SI state - it should be high with no cable attached, with the cable plugged in ( and no gba on the other end) it will go low. Check both cable ends.

I *think* a gbc cable can be used for 32bit mode comms but I've never actually got round to confirming this.

#66266 - Ultima2876 - Mon Jan 09, 2006 11:02 am

I've updated my code a bit - this one displays a purple screen on the slave GBA, and on the master there's nothing (indicating that it's waiting for the SI to go LOW from the slave).

Code:
void transfer_data(u8 master, u32 *data, u32 size)      //master - 1 for master, 0 for slave, *data - pointer to data to send, size - size of data in bytes
   {
      u32 location = 0;
      u32 temp = 0;
      u32 sync = 0;
      u8 flip = 0;
      
      temp = IntrTable[7];

      REG_RCNT = 0;         //init RCNT
      if(master)
         {
            //make sure size is correct
            if(size == 0) { return; }
            if(size & 3 != 0) { size += 4; }
            size = (size - (size & 3)) >> 2;
            REG_IE |= 0x80;      //enable serial interrupt
            IntrTable[7] = transferint_master;
            REG_SIOCNT = 0x5001;            //init SIOCNT for master
            while(location < size)            //start sending
               {
                  REG_SIODATA32 = ((u32*)data)[location];      //set the data to send
                  REG_SIOCNT |= 8;                      //set SO HIGH
                  while((REG_SIOCNT & 4) == 0) { }         //while the SI is LOW (slave NOT READY), wait
                  BG_PaletteMem[0] = 0x7FFF;
                  REG_SIOCNT &= 0xFFF7;                  //set SO LOW
                  REG_SIOCNT |= 0x80;                     //set the start bit to start the transfer
                  IntrWait(1, 0x80);                     //wait for the interrupt to trigger
                  errorcount += 1;
                  location += 1;
               }
         }
      else
         {
            //make sure size is correct
            if(size == 0) { return; }
            if(size & 3 != 0) { size += 4; }
            size = (size - (size & 3)) >> 2;
            REG_SIOCNT = 0x5000;                        //init for slave
            REG_IE |= 0x80;                              //enable serial interrupt
            IntrTable[7] = transferint_slave;
            BG_PaletteMem[0] = RGB(31, 31, 31);
            while(location < size)
               {
                  REG_SIODATA32 = 0;                     //clear the data
                  BG_PaletteMem[0] = RGB(0, 31, 0);
                  while((REG_SIOCNT & 4) == 0) { }         //wait until SI HIGH
                  REG_SIOCNT |= 0x80;                     //set the start bit so that you can wait for the transfer to complete
                  REG_SIOCNT |= 8;                     //Set SO HIGH - READY
                  BG_PaletteMem[0] = RGB(31, 0, 31);
                  IntrWait(1, 0x80);                     //wait for the interrupt to trigger
                  BG_PaletteMem[0] = RGB(31, 31, 0);
                  ((u32*)data)[location] = REG_SIODATA32;      //write the received data
                  errorcount += 1;
                  location += 1;
               }
         }
      IntrTable[7] = temp;
}


These are the interrupt functions:

Code:
void transferint_slave(void)
   {
      ((vu16*)0x03007FF8)[0] |= 0x0080;
      BG_PaletteMem[0] = RGB(0, 0, 0);
   }
   
void transferint_master(void)
   {
      ((vu16*)0x03007FF8)[0] |= 0x0080;
   }


And the defines:

Code:
#define REG_SIOCNT          (*(volatile u16 *)0x04000128)
#define REG_RCNT     (*(volatile u16 *)0x04000134)
#define REG_SIOMPRECV ((volatile u16 *)0x04000120)
#define REG_SIODATA8   (*(volatile u16 *)0x0400012A)
#define REG_SIODATA32 (*(volatile u32 *)0x04000120)


On hardware, the master's SI never goes LOW - the slave definitely sets its SO LOW because it gets to the purple screen - which indicates that it's waiting for the transfer-done interrupt to occur.

This code fails on no$ - no$ treats the SI as always LOW, so the master doesn't wait for the slave (it waits for LOW, sees that it's already LOW) and carries on sending to nothing - the slave crashes with a green screen (waiting for SI HIGH). The same happens on hardware if I change it to work on no$ by switching the HIGHs and LOWs that are sent - the code then works perfectly in no$.

This is what lead me to believe that the slave's SO is completely ignored with the normal link cable... is this the case, or am I still doing something wrong?

Thanks again =P

EDIT: If it's the case that on the official cable the master's SI is grounded, is there any way I can do this? maybe by using general purpose mode, then switching to normal mode and doing the one way transfer, then back to general purpose to send the sync bit back to the master - rinse and repeat until the transfer is finished?

There must be some way to sync them, otherwise how/why would anyone ever want to use normal mode?