Original Post

There seems to be a little bit of confusion about how to do this so I thought I’d post a couple of examples.

The program main loop should be executed once per game frame. A simple way to do this is to sync with the end of the VIP drawing process.

Examples:

1. Sync with end of drawing

#include "libgccvb.h"

int main()
{
  int count= 0;

  vbDisplayOn();
  vbDisplayShow();

  // main loop @ 50 Hz
  while(1)
  {
    // wait for the VIP to finish drawing
    while(!(VIP_REGS[INTPND] & XPEND)); 
    // clear flag
    VIP_REGS[INTCLR]= XPEND;

    // read controller

    // do stuff        

    if((count%7)==0)
      VIP_REGS[BKCOL] ^= 3;

    count++;
  }

  return 0;
}

2: As above but using a VIP interrupt

#include "libgccvb.h"

volatile int drawsync= 0;

void vip_isr()
{
  VIP_REGS[INTENB]= 0;

  if(VIP_REGS[INTPND] & XPEND)
  {
    VIP_REGS[INTCLR]= XPEND;
    drawsync= 1;
  }

  VIP_REGS[INTENB]= XPEND;
}

int main()
{
  int count= 0;

  VPU_VECTOR= (u32)vip_isr;
  VIP_REGS[INTENB]= XPEND;

  vbDisplayOn();
  vbDisplayShow();

  // main loop @ 50 Hz
  while(1)
  {
    // wait for the VIP to finish drawing
    while(!drawsync);
    // clear flag
    drawsync= 0;

    // read controller

    // do stuff

    if((count&15)==0)
      VIP_REGS[BKCOL] ^= 3;

    count++;
  }

  return 0;
}
10 Replies

Makes sense… But why are you flipping BKCOL every 7 steps? Is that just demo code?

Fwirt wrote:
Makes sense… But why are you flipping BKCOL every 7 steps? Is that just demo code?

It makes the screens blink to show that it’s working without having to set up any graphics.

// wait for the VIP to finish drawing
while(!(VIP_REGS[REG_INTPND] & XPEND));
// clear flag
VIP_REGS[INTCLR]= XPEND;

I’ve just tried this code on Soviet Union 2010 and it makes everything really slow on emulators, even though I’ve disabled my slowdown loop. What should I do?

I think it may be due to less than 100% accurate VIP timing in the emulators. It’s probably better to use the interrupt example; although, even then, it’s probably more “proper” to use the display frame or game frame interrupt flags (no offense to dasi ;-)).

If you don’t want to use interrupts, you could sync to the actual screen refresh (which is always the same on hardware and, thus, easier to emulate) by using the DPSTTS register instead of XPSTTS. The relevant bits are the four least-significant. Each bit corresponds to a frame-buffer and represents which one is currently being read from (sent to the scanner). They’re all cleared when the display is idle (between frames).

The examples given above work correctly in Mednafen, Reality Boy, and on hardware.

> What should I do?

It’s likely you will need to restructure or rewrite your main loop.

> If you don’t want to use interrupts, you
> could sync to the actual screen refresh
> by using the DPSTTS [DPBSY flags]…

The reason for syncing to XPEND or using the XPEND interrupt is to avoid writing to VRAM, the world attributes, OAM, the parameter tables, etc, during the VIP drawing process. Syncing to GAMESTART is fine too, but means you need to use an interrupt:

#include "libgccvb.h"

void vip_isr()
{
  VIP_REGS[INTENB]= 0;

  if(VIP_REGS[INTPND] & XPEND)
  {
    VIP_REGS[INTCLR]= XPEND;

    // drawing finished, write to VRAM, OAM, etc, here!
  }

  VIP_REGS[INTENB]= XPEND;
}

int main()
{
  int count= 0;

  VPU_VECTOR= (u32)vip_isr;
  VIP_REGS[INTENB]= XPEND;

  vbDisplayOn();
  vbDisplayShow();

  // main loop @ 50 Hz
  while(1)
  {
    // sync with start of gameframe
    while(!(VIP_REGS[INTPND] & GAMESTART));
    // clear flag
    VIP_REGS[INTCLR]= GAMESTART; 

    // read controller

    // do stuff

    if((count&15)==0)
      VIP_REGS[BKCOL] ^= 3;

    count++;
  }

  return 0;
}
  • This reply was modified 10 years, 8 months ago by dasi.

I’ve just tried this code on Soviet Union 2010 and it makes everything really slow on emulators, even though I’ve disabled my slowdown loop. What should I do?

You should send it to me so I can try it out with my FlashBoy Plus and finally play it like it should be played. 😉

I’m trying to solve this problem again. I’ve done a number of improvements to the code, including doing all the changes to OBJs at once (but only if they need to be changed) after they’ve been drawn. However, it’s still not fast enough. Then I came across this in the vbSetWorld of my world.h:

while (VIP_REGS[XPSTTS] & XPBSYR);	// Wait for the screen to stop updating.

This seems to be much faster. However, I can’t test it on hardware. How does that work? The Nintendo VB development manual describes XPSTTS simply as “drawing control”. According to vip.h, it contains a number of flags. Is XPBSYR clear when it’s safe to write to the screen?

HorvatM wrote:

while (VIP_REGS[XPSTTS] & XPBSYR);	// Wait for the screen to stop updating.

This seems to be much faster. However, I can’t test it on hardware. How does that work? The Nintendo VB development manual describes XPSTTS simply as “drawing control”. According to vip.h, it contains a number of flags. Is XPBSYR clear when it’s safe to write to the screen?

I think “XPSTTS” is an abbreviation for “piXel Processor STaTuS” and yes, it’s a register full of flags. XPBSYR is defined as 0x0C, which is 1100b. In that code snippet, XPBSYR is used as a mask for the bits XPBSY0 (0100b) and XPBSY1 (1000b) which are set when the first and second framebuffers are being drawn, respectively. However when both of those bits are clear, the XP is idle, (VIP_REGS[XPSTTS] & XPBSYR) == 0, and thus the do-nothing while loop exits at a point when it is safe to update the areas in RAM that the XP will read from at the top of the next frame.

HorvatM, blitter has also written a clear and detailed description of the VIP drawing process in his wiki article on direct screen drawing which you may find useful.

  • This reply was modified 10 years, 3 months ago by dasi.

This isn’t working…

 

Write a reply

You must be logged in to reply to this topic.