Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DMD more fixes, WPC should be finally right after all these years! #338

Merged
merged 2 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions docs/dmd.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ accurately (mainly for backward compatibility, since this was the only thing ava

All DMD control board use Pulse Width Modulation (PWM) to create shades: they quickly toggle the dots on and
off, and the observer eye perceive a shade between fully off and fully on. The limit between perceiving flicker vs perceiving
a stable shade is called the flicker/fusion limit and has lots of scientific documentation as this is a key point when
designing a display. It depends mainly on 2 factors:
a stable shade is called the [flicker/fusion threshold](https://en.wikipedia.org/wiki/Flicker_fusion_threshold). It depends mainly on 2 factors:
- the speed at which the dots switch on/off (do they ramp up/down quickly or do they fade more slowly),
- the device luminance level, which impacts how the human eye perceive light (Ferry/Porter law).

Expand Down Expand Up @@ -55,9 +54,13 @@ The table below gives the main information (PWM FPS / Display FPS / PWM pattern)

## WPC

WPC simply renders frame at 122.1Hz, expecting the viewer eye to merge the 3 last rasterized frame.
WPC continuously renders frame at 122.1Hz. The game code uses this to render sequences of
either 1 (0/100), 2 (0/50/100), or 3 (0/33/66/100) frames, expecting the viewer eye to
merge these frames into shades. For example, Terminator 2 uses a 2 frame PWM pattern while
Creature from the Black Lagoon uses a 3 frame PWM pattern.

Phantom Haus uses a double height DMD which is therefore rasterized at half frequency: 61.5Hz. As a consequence, the game only uses a PWM pattern limited to 2 frame patterns, likely to avoid flicker.
Phantom Haus uses a double height DMD which is rasterized by the same hardware at half frequency: 61.5Hz.
As a consequence, the game only uses a 2 frame PWM pattern, likely to avoid flicker.


## Alvin G
Expand Down
12 changes: 6 additions & 6 deletions src/p-roc/p-roc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,12 @@ PRMachineType getRomMachineType() {
switch (core_gameData->gen) {
case GEN_WPCALPHA_1:
case GEN_WPCALPHA_2:
case GEN_DE:
case GEN_S4:
case GEN_S11A:
case GEN_S11:
case GEN_S11B2:
case GEN_S11C:
case GEN_DE:
case GEN_S4:
case GEN_S11A:
case GEN_S11:
case GEN_S11B2:
case GEN_S11C:
if (pmoptions.alpha_on_dmd) {
fprintf(stderr, "ROM machine type: kPRMachineWPCAlphanumeric,\nbut using kPRMachineWPC due to alpha_on_dmd option\n");
return kPRMachineWPC;
Expand Down
59 changes: 22 additions & 37 deletions src/wpc/gts3.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,9 @@ static WRITE_HANDLER( xvia_0_b_w ) {
// - LCLR H->L / LCLR L->H
// We do not simulate the LCLR signals since the pulse is too short (50us) for the output resolution
//printf("t=%8.5f Col=%3x STRB=%d DATA=%d LCLR=%d\n", timer_get_time(), GTS3locals.lampColumn, data & LSTRB, data & LDATA, data & LCLR);
if (data & ~GTS3locals.u4pb & LSTRB) // Positive edge on LSTRB: shift 12bit register and set bit0 to LDATA
{
if (~GTS3locals.u4pb & data & LSTRB) { // Positive edge on LSTRB: shift 12bit register and set bit0 to LDATA
GTS3locals.lampColumn = ((GTS3locals.lampColumn << 1) & 0x0ffe) | (data & LDATA);
if (GTS3locals.lampColumn == 0x001) // Simple strobe emulation: accumulate lamp matrix until strobe restarts from first column
{
if (GTS3locals.lampColumn == 0x001) { // Simple strobe emulation: accumulate lamp matrix until strobe restarts from first column
memcpy(coreGlobals.lampMatrix, coreGlobals.tmpLampMatrix, sizeof(coreGlobals.tmpLampMatrix));
memset(coreGlobals.tmpLampMatrix, 0, sizeof(coreGlobals.tmpLampMatrix));
}
Expand All @@ -172,11 +170,8 @@ static WRITE_HANDLER( xvia_0_b_w ) {
core_write_pwm_output_lamp_matrix(CORE_MODOUT_LAMP0 + 64, (GTS3locals.lampColumn >> 8) & 0x0F, lampRow, 4);


if (GTS3locals.alphagen)
{ // Alpha generation
//printf("%8.5f Alpha Strobe %02x %05x\n", timer_get_time(), data, GTS3locals.alphaNumColShiftRegister);
if (data & ~GTS3locals.u4pb & DSTRB) // Positive edge on DSTRB: shift 20bit register and set bit0 to DDATA
{
if (GTS3locals.alphagen) { // Alpha generation
if (~GTS3locals.u4pb & data & DSTRB) { // Positive edge on DSTRB: shift 20bit register and set bit0 to DDATA
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16, 0);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16 + 8, 0);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 , 0);
Expand All @@ -186,29 +181,21 @@ static WRITE_HANDLER( xvia_0_b_w ) {
// This should never happens but you can drive the hardware to it (multiple resets,...), this will lead to incorrect rendering
// assert((GTS3locals.alphaNumColShiftRegister == 0) || (GTS3locals.alphaNumColShiftRegister == (1 << GTS3locals.alphaNumCol)));
}
if (data & ~GTS3locals.u4pb & DBLNK) // DBlank start (positive edge)
{
if (~GTS3locals.u4pb & data & DBLNK) { // DBlank start (positive edge)
for (int i = 0; i < 20 * 2 * 2; i++)
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + i * 8, 0);
}
else if (GTS3locals.alphaNumCol < 20) {
if (~data & GTS3locals.u4pb & DBLNK) // DBlank end (negative edge)
{
// Basic non dimmed segments emulation: just use the column and value defined during DBLNK
coreGlobals.segments[GTS3locals.alphaNumCol + 20].w = GTS3locals.activeSegments[0].w;
coreGlobals.segments[GTS3locals.alphaNumCol ].w = GTS3locals.activeSegments[1].w;
}
if ((data & DBLNK) == 0)
{
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16 , GTS3locals.activeSegments[0].b.lo);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16 + 8, GTS3locals.activeSegments[0].b.hi);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 , GTS3locals.activeSegments[1].b.lo);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 + 8, GTS3locals.activeSegments[1].b.hi);
}
else if (GTS3locals.u4pb & ~data & DBLNK && GTS3locals.alphaNumCol < 20) { // DBlank end (negative edge)
// Non dimmed segments emulation: use the column and value defined during DBLNK
coreGlobals.segments[GTS3locals.alphaNumCol + 20].w = GTS3locals.activeSegments[0].w;
coreGlobals.segments[GTS3locals.alphaNumCol ].w = GTS3locals.activeSegments[1].w;
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16 , GTS3locals.activeSegments[0].b.lo);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16 + 8, GTS3locals.activeSegments[0].b.hi);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 , GTS3locals.activeSegments[1].b.lo);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 + 8, GTS3locals.activeSegments[1].b.hi);
}
}
else
{ // DMD generation
else { // DMD generation
GTS3_dmdlocals[0].dstrb = (data & DSTRB) != 0;
}

Expand Down Expand Up @@ -821,25 +808,23 @@ static WRITE_HANDLER(display_control) { GTS3locals.DISPLAY_CONTROL(offset,data);
*/
static WRITE_HANDLER(alpha_display){
/* Adjust the 16 Segment Layout to match the output order expected by core.c */
if (offset & 1) // Hi byte (8..15)
{
if (offset & 1) { // Hi byte (8..15)
GTS3locals.activeSegments[offset >> 1].w &= 0x063F; // Remove bits 6..8 and 11..15
GTS3locals.activeSegments[offset >> 1].w |= ((data & 0x0F) << 11) /* 8..11 => 11..14 */
| ((data & 0x10) << 2) /* 12 => 6 */
| ((data & 0x20) << 3) /* 13 => 8 */
| ((data & 0x40) << 9) /* 14 => 15 */
| ((data & 0x80) ); /* 15 => 7 */
| ((data & 0x10) << 2) /* 12 => 6 */
| ((data & 0x20) << 3) /* 13 => 8 */
| ((data & 0x40) << 9) /* 14 => 15 */
| ((data & 0x80) ); /* 15 => 7 */
}
else // Lo byte (0..7)
{
else { // Lo byte (0..7)
GTS3locals.activeSegments[offset >> 1].w &= 0xF9C0; // Remove bits 0..5 and 9..10
GTS3locals.activeSegments[offset >> 1].w |= ((data & 0x3F) ) /* 0.. 5 => 0.. 5 */
| ((data & 0xC0) << 3); /* 6.. 7 => 9..10 */
| ((data & 0xC0) << 3); /* 6.. 7 => 9..10 */
}
if (((GTS3locals.u4pb & DBLNK) == 0) && (GTS3locals.alphaNumCol < 20)) { // This should never happen since character pattern is loaded to the latch registers while DBLNK is raised
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16 , GTS3locals.activeSegments[0].b.lo);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol + 20) * 16 + 8, GTS3locals.activeSegments[0].b.hi);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 , GTS3locals.activeSegments[0].b.lo);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 , GTS3locals.activeSegments[1].b.lo);
core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (GTS3locals.alphaNumCol ) * 16 + 8, GTS3locals.activeSegments[1].b.hi);
}
}
Expand Down
Loading
Loading