Multi-processor program structure

Ask anything your want about the 32X Mushroom programming.

Moderator: BigEvilCorporation

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Tue Dec 11, 2012 9:27 pm

My examples normally set FM in one of two places: in the MD init code right before allowing the Master to run, or in the 32X crt0.s Master init code, right after starting the Slave. If you don't find it in one of those two spots, you need to add it.

ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Post by ammianus » Sat Dec 22, 2012 1:51 pm

Chilly Willy wrote:My examples normally set FM in one of two places: in the MD init code right before allowing the Master to run, or in the 32X crt0.s Master init code, right after starting the Slave. If you don't find it in one of those two spots, you need to add it.
In sh2_crt0.s I found where it is being set

Code: Select all

! let Slave SH2 run
        mov     #0,r1
        mov.l   r1,@(4,r0)  /* clear slave status */

        mov     #0x80,r0
        mov.l   _master_adapter,r1
        mov.b   r0,@r1      /* set FM */
        mov     #0x00,r0
        mov.b   r0,@(1,r1)  /* set int enables */
        mov     #0x20,r0
        ldc     r0,sr       /* allow ints */
Where _master_adapter is being set to

Code: Select all

_master_adapter:
        .long   0x20004000
m68k_crt1.s:

Code: Select all

__m68k_start:
        move.b  #1,0xA15107         /* set RV */
        move.b  #2,0xA130F1         /* SRAM disabled, write protected */
        move.b  #0,0xA15107         /* clear RV */

        move.w  0xA15100,d0
        or.w    #0x8000,d0
        move.w  d0,0xA15100         /* set FM - allow SH2 access to MARS hw */
        move.l  #0,0xA15120         /* let Master SH2 run */

        lea     vert_blank(pc),a0
        move.l  a0,0xFF0FFC         /* set vertical blank interrupt handler */
        move.w  #0x2000,sr          /* enable interrupts */
From reading the docs on FM bit:
Access to the 32X VDP
The FM (VDP access authorization) bit number must be 0 before the MEGA Drive can
access the Mars frame buffer, overwrite images, VDP register or color palette. When the
bit is 1, reads are undefined and writes are ignored. Color palette access is words only, not
bytes.
The frame buffer, overwrite image, VDP register, and color palette can be accessed from
the MEGA Drive side only when the FM (VDP access authorization) bit is 0. . When the
bit is 1, reads are undefined and writes are ignored. Color palette access is words only, not
bytes.
Since it doesn't distinguish between master/slave when bit is 1, then it should be working right?

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sat Dec 22, 2012 5:05 pm

Yes, FM affects either SH2. Once it's set by either side, the SH2 can access the VDP resources. So that's not your problem.

ammianus
Very interested
Posts: 124
Joined: Sun Jan 29, 2012 2:10 pm
Location: North America
Contact:

Post by ammianus » Wed Dec 26, 2012 7:43 pm

Well I am not sure if this was the only problem, I abandoned trying to draw to FB using the slave CPU.

So instead I flipped it around

Master = Handle Input, Draw graphics
Slave = Game logic/screen location calculations

This still didn't work. But that seemed to be a synchronization issue between the slave and master.

When I added a back and forth communication using COMM6 port to gate the slave from entering it's main loop.

Master:

Code: Select all

main(){
 //do ALL init code
 ...
 MARS_SYS_COMM6 = 1; // tells slave to start
 
 while(1){
  //handle input
  //draw graphics
  ...
 }
}
Slave:

Code: Select all

slave(){
 ...
 //wait for main to initialize stuff
 while (MARS_SYS_COMM6 != 1) ; // wait until master has signaled to start
 MARS_SYS_COMM6 = 2; //tell master slave is starting

 while(1){
  //update game state/logic
  ...
 }
}
This worked! In the sense that the slave did something and could update shared variables correctly! But, this sort of ran "too fast" as the game logic appears to be much less work than drawing all the graphics, so it becomes jerky as the ai updates things too quickly. So I have to artificially slow down the slave's loop using delays, making it's updates a fixed frequency i.e. each slave's while loop iteration takes 3 vcount ticks, so after doing the work in elapsed time of "n" vcount ticks. I have to wait for at most 3 - n more counts if that makes sense. I also use the same COMM6 port to control when the game logic or main updates the shared variables (characters, level state, "camera location", etc..). Don't want to be drawing sprites while updating the characters at the same time!


p.s. how many absolute vcount "ticks" will there be per second in 32X? is it constant?

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Thu Dec 27, 2012 12:43 am

ammianus wrote:Well I am not sure if this was the only problem, I abandoned trying to draw to FB using the slave CPU.

So instead I flipped it around

Master = Handle Input, Draw graphics
Slave = Game logic/screen location calculations

This still didn't work. But that seemed to be a synchronization issue between the slave and master.

When I added a back and forth communication using COMM6 port to gate the slave from entering it's main loop.

Master:

Code: Select all

main(){
 //do ALL init code
 ...
 MARS_SYS_COMM6 = 1; // tells slave to start
 
 while(1){
  //handle input
  //draw graphics
  ...
 }
}
Slave:

Code: Select all

slave(){
 ...
 //wait for main to initialize stuff
 while (MARS_SYS_COMM6 != 1) ; // wait until master has signaled to start
 MARS_SYS_COMM6 = 2; //tell master slave is starting

 while(1){
  //update game state/logic
  ...
 }
}
This worked! In the sense that the slave did something and could update shared variables correctly! But, this sort of ran "too fast" as the game logic appears to be much less work than drawing all the graphics, so it becomes jerky as the ai updates things too quickly. So I have to artificially slow down the slave's loop using delays, making it's updates a fixed frequency i.e. each slave's while loop iteration takes 3 vcount ticks, so after doing the work in elapsed time of "n" vcount ticks. I have to wait for at most 3 - n more counts if that makes sense. I also use the same COMM6 port to control when the game logic or main updates the shared variables (characters, level state, "camera location", etc..). Don't want to be drawing sprites while updating the characters at the same time!


p.s. how many absolute vcount "ticks" will there be per second in 32X? is it constant?
Well, it should work the other way around as well. There's really no difference at all between the master and slave other than the master is started first, and then the slave. You could use the slave for everything while the master idles, for example. If I had to guess, you probably had a cache issue somewhere in your code... I do that all the time when trying to use both CPUs with shared variables.

Yes, the SH2's will be WAY TOO FAST for the game logic of many simple games. That's where timing comes in. You can use the ticks for "crude" timing. A tick is every vertical blank, so 50 per second on PAL, and 60 on NTSC. "32x.h" has equates for checking PAL/NTSC as you can see in the slave() init where I set the PWM frequency based on PAL vs NTSC.

For more precise timing, you can use the free-run timers - although SEGA restricted use of the FRTs in the devkit because of a bug in the interrupts on the developer hardware, it should be fine to use the FRTs for your own usage on consumer boards. Most of my examples either init the FRT for nothing, or don't init them at all.

Another timing method is to use the audio period - depending on the number of samples you play, you could have timing based on a DMA block of samples giving anywhere from 20 Hz to 200 Hz.

Post Reply