SuperGrafx VPC emulation

Discussion of development of software for any "obsolete" computer or video game system. See the WSdev wiki and ObscureDev wiki for more information on certain platforms.
Post Reply
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

SuperGrafx VPC emulation

Post by Near »

I'm trying to emulate the priority effects of the VPC, and it's giving me quite a bit of trouble.

First up, i notice that only Daimakaimura uses window 1 (set to 1023), and nothing else uses either window.

Yet still ... sgxtech says this:

Code: Select all

 The VPC has two 10-bit registers which define the width of Window 1 and
 Window 2. The windows start from the leftmost pixel of the physical screen,
 not the active display area, which is a window width setting of $0040.
 Values smaller than this disable the window, and values larger than this
 make the window extend horizontally across the screen, from left to right,
 A setting of $3FF makes the window take up the entire width of the screen.
So the invalid ranges are 0 - 63. And the valid ranges are 64 - 1023.

But each VDC has its own H/V timing values, and of course the VCE has the clock frequency select of 5/7/10mhz.

So when it says it starts from the leftmost pixel of the physical screen, what is it talking about, exactly?

Given the regular resolutions are 256/340/512, should I assume that with a range of 1023, that a value of 66 is saying "the window affects one pixel at 256-width, one and a half pixels (roughly) at 340-width, two pixels at 512-width"?

I don't think it'd be rated at the full 21mhz, because then 1023 wouldn't cover the entire width of the screen. Let alone if you overdrive the width to its theoretical limits (~285/~380/~560).

Next problem is the priorities themselves ... what a mess. sgxtech lists it like this:

Code: Select all

 Back                          Front
 SP2 > BG2 > SP2' > SP1 > BG1 > SP1'

 BG2 = VDC #2 background
 SP2 = VDC #2 low priority sprites
 SP2'= VDC #2 high priority sprites
 BG1 = VDC #1 background
 SP1 = VDC #1 low priority sprites
 SP1'= VDC #1 high priority sprites
First of all, the sprite priority isn't exposed on the supposed 9-bit data bus from each VDC, that's:
d8 => 0=background, 1=sprite
d7-d4 => palette index
d3-d0 => color
Which is also a direct index into CRAM, which is convenient.

I am also unsure what happens when both the background and sprite are off. It sounds like on a regular PCE, it returns 0x100 (so you use the sprite palette #0 color), and on the SGX, it returns 0x000 (so you use the background palette #0 color.) Is that right?

So with that in mind, we can refactor the default priority to:
BG2 > SP2 > BG1 > SP1

Which ... using > is a really poor choice. Up until just now I thought that meant BG2 had greater priority >_<

So this can be further refactored to this:
return VDC1.color ? VDC1 : VDC2;

But that's only for modes 0 and 3.

Code: Select all

 The 2-bit priority field affects the layer ordering. The above default
 priority setting is selected for values of 00b and 11b.
But what about the other two modes?

Code: Select all

 For 01b, VDC #1 sprites are shown behind the VDC #2 background. However,
 the VDC #1 background has missing sprite-shaped areas where it's sprites
 are, even though they are hidden behind both background layers.
So that would mean:

Code: Select all

 SP1 = VDC #1 sprites
 BG2 = VDC #2 background
 SP2 = VDC #2 sprites
 BG1 = VDC #1 background
SP1 > BG2 > SP2 > BG1 (BG1 = highest priority. Again, weird usage of > here.)

And finally:

Code: Select all

 For 10b, VDC #2 sprites are shown in front of the VDC #1 background and
 behind VDC #1 sprites. However, low priority VDC #2 sprites are still
 shown behind VDC #2 background tiles.
So thus:

Code: Select all

 BG2 = VDC #2 background
 BG1 = VDC #1 background
 SP2 = VDC #2 sprites
 SP1 = VDC #1 sprites
BG2 > BG1 > SP2 > SP1.

Of course, the enable VDC1/2 flags come into play as well. So I skip them when said VDC is not enabled.

And if both VDCs aren't enable and we fall through, for now I'm returning 0x000 (CRAM color #0), is that right?

Thus, my code ends up looking like this:

(I know it's horribly unoptimized, it's just to get things working initially, then I'll revise.)

Code: Select all

auto VPC::bus(uint hclock) -> uint9 {
  auto bus0 = vdc0.bus();  //0 = VDC1
  auto bus1 = vdc1.bus();  //1 = VDC2

  auto color0 = bus0.bits(0,3);
  auto color1 = bus1.bits(0,3);

  auto palette0 = bus0.bits(4,7);
  auto palette1 = bus1.bits(4,7);

  auto mode0 = bus0.bit(8);  //0 = background, 1 = sprite
  auto mode1 = bus1.bit(8);

  bool window0 = window[0] >= 64 && (window[0] - 64) >= hclock / 2;
  bool window1 = window[1] >= 64 && (window[1] - 64) >= hclock / 2;

  uint2 mode;
  if(!window0 && !window1) mode = 1;
  if( window0 && !window1) mode = 0;
  if(!window0 &&  window1) mode = 3;
  if( window0 &&  window1) mode = 2;

  //mode 0 = $08.d0-d3, 1 = $08.d4-d7, 2 = $09.d0-d3, 3 = $09.d4-d7
  auto enableVDC0 = settings[mode].enableVDC0;
  auto enableVDC1 = settings[mode].enableVDC1;
  auto priority = settings[mode].priority;

  if(priority == 0 || priority == 3) {
    if( mode0 && enableVDC0) return bus0;
    if(!mode0 && enableVDC0) return bus0;
    if( mode1 && enableVDC1) return bus1;
    if(!mode1 && enableVDC1) return bus1;
  }

  if(priority == 1) {
    if(!mode0 && enableVDC0) return bus0;
    if( mode1 && enableVDC1) return bus1;
    if(!mode1 && enableVDC1) return bus1;
    if( mode0 && enableVDC0) return bus0;
  }

  if(priority == 2) {
    if( mode0 && enableVDC0) return bus0;
    if( mode1 && enableVDC1) return bus1;
    if(!mode0 && enableVDC0) return bus0;
    if(!mode1 && enableVDC1) return bus1;
  }

  return 0x000;
}
... but it doesn't work well at all.

Gallery of results here: http://imgur.com/a/1fE82

So ... what am I missing here? :/
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: SuperGrafx VPC emulation

Post by Near »

In-game, Daimakaimura sets $08 = #$75, $09 = #$56. Since our first window doesn't match, and second window does match, that gives us $08.d4-d7, or #$07.

d0=enable VDC0
d1=enable VDC1
d2-d3=priority 01

Code: Select all

 Bits 7-4 of $0008 are for the region occupied by window 2
 Bits 3-0 of $0008 are for the region where window 1 and 2 overlap
 Bits 7-4 of $0009 are for the region where no window is present
 Bits 3-0 of $0009 are for the region occupied by window 1
So with that said ... this is the priority that makes Daimakaimura run correctly:

Code: Select all

  if(priority == 1) {
    if( mode0 && enableVDC0) return bus0;  //SP1
    if( mode1 && enableVDC1) return bus1;  //SP2
    if(!mode0 && enableVDC0) return bus0;  //BG1
    if(!mode1 && enableVDC1) return bus1;  //BG2
  }
In other words: BG2 > BG1 > SP2 > SP1, or per sgxtech, this is mode 2.

So ... my suspicion is that sgxtech has accidentally swapped mode 1 and mode 2's priorities.

My questions still remain about the fallback colors when both BG and Sprites are disabled, both on individual VDCs and on the VPC's combined mode (eg with the two enable VDC bits being cleared.) And also I am still unsure on how the window should work. But ... this code seems to be enough to run all the games.

Code: Select all

auto VPC::bus(uint hclock) -> uint9 {
  auto bus0 = vdc0.bus();
  auto bus1 = vdc1.bus();

  auto color0 = bus0.bits(0,3);
  auto color1 = bus1.bits(0,3);

  auto palette0 = bus0.bits(4,7);
  auto palette1 = bus1.bits(4,7);

  auto mode0 = bus0.bit(8);
  auto mode1 = bus1.bit(8);

  bool window0 = window[0] >= 64 && (window[0] - 64) >= hclock / 2;
  bool window1 = window[1] >= 64 && (window[1] - 64) >= hclock / 2;

  uint2 mode;
  if(!window0 && !window1) mode = 3;
  if( window0 && !window1) mode = 2;
  if(!window0 &&  window1) mode = 1;
  if( window0 &&  window1) mode = 0;

  auto enableVDC0 = (bool)color0 && settings[mode].enableVDC0;
  auto enableVDC1 = (bool)color1 && settings[mode].enableVDC1;
  auto priority = settings[mode].priority;

  if(priority == 0 || priority == 3) {
    if( mode0 && enableVDC0) return bus0;
    if(!mode0 && enableVDC0) return bus0;
    if( mode1 && enableVDC1) return bus1;
    if(!mode1 && enableVDC1) return bus1;
  }

  if(priority == 1) {
    if( mode0 && enableVDC0) return bus0;  //SP1
    if( mode1 && enableVDC1) return bus1;  //SP2
    if(!mode0 && enableVDC0) return bus0;  //BG1
    if(!mode1 && enableVDC1) return bus1;  //BG2
  }

  if(priority == 2) {
    if(!mode0 && enableVDC0) return bus0;  //BG1
    if( mode1 && enableVDC1) return bus1;  //SP2
    if(!mode1 && enableVDC1) return bus1;  //BG2
    if( mode1 && enableVDC1) return bus1;  //SP1
  }

  return 0x000;
}
ccovell
Posts: 1045
Joined: Sun Mar 19, 2006 9:44 pm
Location: Japan
Contact:

Re: SuperGrafx VPC emulation

Post by ccovell »

byuu wrote:But each VDC has its own H/V timing values, and of course the VCE has the clock frequency select of 5/7/10mhz.

So when it says it starts from the leftmost pixel of the physical screen, what is it talking about, exactly?

Given the regular resolutions are 256/340/512, should I assume that with a range of 1023, that a value of 66 is saying "the window affects one pixel at 256-width, one and a half pixels (roughly) at 340-width, two pixels at 512-width"?

Next problem is the priorities themselves ... what a mess. sgxtech lists it like this:

Code: Select all

 Back                          Front
 SP2 > BG2 > SP2' > SP1 > BG1 > SP1'[/quote]
I'm pretty sure the VPC is clocked at the same rate as the VDC, based on the VCE's data rate (resolution setting).  There are no 1/2 or 1/3 pixels on this system, as far as I know.

And I'm sure Charles Macdonald intended for the >  > to be read as arrows, not 'greater than' signs.  Back --> Front.

I wonder if there are any adjustable SGX windowing programs/demos out there?  Perhaps I could make one sometime, someday... :/

Tomaitheous made some SGX demos with sine-wave effects from windowing adjustments; perhaps he could explain how the windows are finely-controlled?
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: SuperGrafx VPC emulation

Post by Near »

> I'm pretty sure the VPC is clocked at the same rate as the VDC, based on the VCE's data rate (resolution setting). There are no 1/2 or 1/3 pixels on this system, as far as I know.

So in other words, a window of 66 counts for two pixels regardless of the clock setting. Okay, that's doable.

A minor detail that's tricky for me ... with there being a fixed 1365 clock cycles per scanline so as to meet the NTSC standard, and the VCE clock rate being immediately adjustable, it seems like there's a fixed window where the chip can run and generate a 5mhz, 7mhz, 10mhz-wide "pixel". And then eventually it'll have to resynchronize things again through areas where it's not driving the VDC(s)/VPC at all. Easy enough to handle, but still feels really clunky. I'm not sure why they didn't cache the VCE clock rate once per scanline. You'd have to be insane to try and tweak it mid-scanline, but apparently you can :/

> And I'm sure Charles Macdonald intended for the > > to be read as arrows, not 'greater than' signs. Back --> Front.

Yeah, he does amazingly great work and I hate to seem like I'm complaining, but yeah I would have ordered the priority the other way in this one instance.

> I wonder if there are any adjustable SGX windowing programs/demos out there?

Again my personal challenge here is that I don't own a real PCE or flash cart, so I can't do much with a demo, unless there's an emulator that we know gets this 100% right. And even then, I'm weary of emulating an emulator.
User avatar
TOUKO
Posts: 306
Joined: Mon Mar 30, 2015 10:14 am
Location: FRANCE

Re: SuperGrafx VPC emulation

Post by TOUKO »

First up, i notice that only Daimakaimura uses window 1 (set to 1023), and nothing else uses either window.
You can use a game i'am currently doing for testing window(successfully tested on a real SGX):
https://www.dropbox.com/s/tb8at21xz5w46 ... C.pce?dl=0

PS: But beware it's in 10mhz dot clock(i don't know if it's important or not for you to know) .
PS2: if you test with mednafen, be sure using the latest 0.9.41.
Post Reply