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:
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:
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.
But what about the other two modes?
So that would mean:
SP1 > BG2 > SP2 > BG1 (BG1 = highest priority. Again, weird usage of > here.)
And finally:
So thus:
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.)
... but it doesn't work well at all.
Gallery of results here: http://imgur.com/a/1fE82
So ... what am I missing here? :/
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:
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.
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:
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
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:
The 2-bit priority field affects the layer ordering. The above default
priority setting is selected for values of 00b and 11b.
priority setting is selected for values of 00b and 11b.
But what about the other two modes?
Code:
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.
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:
SP1 = VDC #1 sprites
BG2 = VDC #2 background
SP2 = VDC #2 sprites
BG1 = VDC #1 background
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:
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.
behind VDC #1 sprites. However, low priority VDC #2 sprites are still
shown behind VDC #2 background tiles.
So thus:
Code:
BG2 = VDC #2 background
BG1 = VDC #1 background
SP2 = VDC #2 sprites
SP1 = VDC #1 sprites
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:
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;
}
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? :/