Skip to content

Commit

Permalink
DMD: fix Alvin G timings
Browse files Browse the repository at this point in the history
  • Loading branch information
vbousquet committed Sep 2, 2024
1 parent 8ff78b5 commit 617082d
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 62 deletions.
23 changes: 5 additions & 18 deletions src/wpc/alvg.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,30 +615,23 @@ static MACHINE_INIT(alvg) {
install_mem_write_handler(0, 0x2c00, 0x2c00, LED_LATCH);
install_mem_write_handler(0, 0x2c80, 0x2c83, LED_DATA);
}

// Al's Garage Band (PCA020 DMD driver board)
static MACHINE_INIT(alvgdmd1) {
init_common();
/* Init the dmd board */
install_mem_write_handler(0, 0x2c00, 0x2fff, DMD_LATCH);
sndbrd_1_init(core_gameData->hw.display, ALVGDMD_CPUNO, memory_region(ALVGDMD_ROMREGION),data_from_dmd,NULL);
}

//Pistol Poker
//Pistol Poker & Mystery Castle (PCA020A DMD driver board)
static MACHINE_INIT(alvgdmd2) {
init_common();
/* Init the dmd board */
install_mem_write_handler(0, 0x2c00, 0x2fff, DMD_LATCH);
sndbrd_1_init(core_gameData->hw.display, ALVGDMD_CPUNO, memory_region(ALVGDMD_ROMREGION),data_from_dmd,NULL);
}

//Mystery Castle, exactly same as Pistol Poker, but different clock rate
static MACHINE_INIT(alvgdmd3) {
init_common();
/* Init the dmd board */
install_mem_write_handler(0, 0x2c00, 0x2fff, DMD_LATCH);
sndbrd_1_init(core_gameData->hw.display, ALVGDMD_CPUNO, memory_region(ALVGDMD_ROMREGION),data_from_dmd,NULL);
}


static MACHINE_STOP(alvg) {
sndbrd_0_exit();
sndbrd_1_exit();
Expand Down Expand Up @@ -725,27 +718,21 @@ MACHINE_DRIVER_END

extern void construct_alvgdmd1(struct InternalMachineDriver *machine); // workaround to fix confusion in the linker, as this is defined in alvgdmd.c
extern void construct_alvgdmd2(struct InternalMachineDriver *machine);
extern void construct_alvgdmd3(struct InternalMachineDriver *machine);

//Main CPU, DMD, Sound hardware Driver (Generation #2)
//Main CPU, DMD (Generation #1: part PCA020), Sound hardware Driver (Generation #2)
MACHINE_DRIVER_START(alvgs2dmd1)
MDRV_IMPORT_FROM(alvgs2)
MDRV_IMPORT_FROM(alvgdmd1)
MDRV_CORE_INIT_RESET_STOP(alvgdmd1,NULL,alvg)
MACHINE_DRIVER_END

//Main CPU, DMD (Generation #2: part PCA020A), Sound hardware Driver (Generation #2)
MACHINE_DRIVER_START(alvgs2dmd2)
MDRV_IMPORT_FROM(alvgs2)
MDRV_IMPORT_FROM(alvgdmd2)
MDRV_CORE_INIT_RESET_STOP(alvgdmd2,NULL,alvg)
MACHINE_DRIVER_END

MACHINE_DRIVER_START(alvgs2dmd3)
MDRV_IMPORT_FROM(alvgs2)
MDRV_IMPORT_FROM(alvgdmd3)
MDRV_CORE_INIT_RESET_STOP(alvgdmd3,NULL,alvg)
MACHINE_DRIVER_END

//Use only to test 8031 core
#ifdef MAME_DEBUG
MACHINE_DRIVER_START(alvg_test8031)
Expand Down
2 changes: 0 additions & 2 deletions src/wpc/alvg.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,12 @@ extern MACHINE_DRIVER_EXTERN(alvgs1);
extern MACHINE_DRIVER_EXTERN(alvgs2);
extern MACHINE_DRIVER_EXTERN(alvgs2dmd1);
extern MACHINE_DRIVER_EXTERN(alvgs2dmd2);
extern MACHINE_DRIVER_EXTERN(alvgs2dmd3);

#define mALVG alvg
#define mALVGS1 alvgs1
#define mALVGS2 alvgs2
#define mALVGS2DMD1 alvgs2dmd1
#define mALVGS2DMD2 alvgs2dmd2
#define mALVGS2DMD3 alvgs2dmd3

//Use only for testing the 8031 core emulation
#ifdef MAME_DEBUG
Expand Down
70 changes: 39 additions & 31 deletions src/wpc/alvgdmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ static struct {
/*declarations*/
static WRITE_HANDLER(dmd32_bank_w);
static READ_HANDLER(dmd32_latch_r);
static INTERRUPT_GEN(dmd32_firq);
static INTERRUPT_GEN(dmd32_firq1);
static INTERRUPT_GEN(dmd32_firq2);
static WRITE_HANDLER(dmd32_data_w);
static void dmd32_init(struct sndbrdData *brdData);
static void dmd32_exit(int boardNo);
Expand Down Expand Up @@ -107,7 +108,6 @@ static WRITE_HANDLER(control_w)
case 0xf000:
LOG(("setsync=%x\n", data));
dmdlocals.selsync = 0;
//printf("%8.5f selsync off\n", timer_get_time());
break;

default:
Expand Down Expand Up @@ -229,44 +229,43 @@ static PORT_WRITE_START( alvgdmd_writeport )
{ 0x00,0xff, port_w },
PORT_END

// Rasterization timing explanation (from reading the schematics, no verification on real hardware):
//
// CPU Clock @12MHz directly drives VCLOCK on PCA020 (Al's Garage Band) while it is divided by 4 on PCA020A (Mystery Castle & Pistol Poker)
// - Rows: VCLOCK is divided by 384 per row: 256 for the 128 DOTCLOCK [U26/U21] + 128 for the COLLATCH [U23]
// - Frame: row signal is divided by 128 to generate INT1 [U22/U26B], therefore after rasterizing 4 frames of 32 rows
//
// Rasterized memory address A0..A14 is computed like this:
// - A0 .. A3 is reseted at each row start with col address stored in the COLSTART register (as well as an initial bit shift which seems unused)
// - A4 .. A8 is reseted each 4 frames (on INT1) with the frame address stored in the ROWSTART register
// - A9 ..A10 is reseted each 4 frames (on INT1) either with 0 if PLANSENABLE=0 or with Bit67 of ROWSTART register on PCA020 or 01 on PCA020A
// - A11..A14 is directly defined by the CODEPAGE register (can be changed directly while rasterizing, but I doubt this is ever done)

// Al's Garage Band Goes On A World Tour
// CPU Clock, directly drives VCLOCK, then divided by 640 (ROWCLOCK = 512 for DOTCLOCK [U26/U21] + 128 for COLLATCH [U23]), then by 256 for INT1 [U22])
MACHINE_DRIVER_START(alvgdmd1)
MDRV_CPU_ADD(I8051, 12000000) /*12 Mhz*/
MDRV_CPU_MEMORY(alvgdmd_readmem, alvgdmd_writemem)
MDRV_CPU_PORTS(alvgdmd_readport, alvgdmd_writeport)
MDRV_CPU_PERIODIC_INT(dmd32_firq, 12000000./(640.*256.))
MDRV_CPU_PERIODIC_INT(dmd32_firq1, 12000000./(384.*128.)) // 244.14Hz for 4 frames => 976.56Hz per frame (!)
MDRV_INTERLEAVE(50)
MACHINE_DRIVER_END

// Pistol Poker & Mystery Castle
// CPU Clock, divided by 4 (VCLOCK), then by 640 (ROWCLOCK = 512 for DOTCLOCK [U26/U21] + 128 for COLLATCH [U23]), then by 256 for INT1 [U22])
// This leads to fairly slow animation, but seems ot match the real pin (f.e. see https://www.youtube.com/watch?v=KY_spGpQC-Q)
MACHINE_DRIVER_START(alvgdmd2)
MDRV_CPU_ADD(I8051, 12000000) /*12 Mhz*/
MDRV_CPU_MEMORY(alvgdmd_readmem, alvgdmd_writemem)
MDRV_CPU_PORTS(alvgdmd_readport, alvgdmd_writeport)
//MDRV_CPU_PERIODIC_INT(dmd32_firq, 12000000./(4.*640.*256.))
MDRV_CPU_PERIODIC_INT(dmd32_firq, 12000000./(640.*256.)) // HACK: U36 divide main clock by 4 but this leads to slow animation, so skipped here
MDRV_CPU_PERIODIC_INT(dmd32_firq2, 12000000./(4.*384.*128.)) // 61.03Hz for 4 frames => 244.14Hz per frame
MDRV_INTERLEAVE(50)
MACHINE_DRIVER_END
MACHINE_DRIVER_START(alvgdmd3)
MDRV_CPU_ADD(I8051, 12000000) /*12 Mhz*/
MDRV_CPU_MEMORY(alvgdmd_readmem, alvgdmd_writemem)
MDRV_CPU_PORTS(alvgdmd_readport, alvgdmd_writeport)
//MDRV_CPU_PERIODIC_INT(dmd32_firq, 12000000./(4.*640.*256.))
MDRV_CPU_PERIODIC_INT(dmd32_firq, 12000000./(640.*256.)) // HACK: U36 divide main clock by 4 but this leads to slow animation, so skipped here
MDRV_INTERLEAVE(50)
MACHINE_DRIVER_END


//Use only for testing the 8031 core emulation
#ifdef MAME_DEBUG
MACHINE_DRIVER_START(test8031)
MDRV_CPU_ADD(I8051, 12000000) /*12 Mhz*/
MDRV_CPU_MEMORY(alvgdmd_readmem, alvgdmd_writemem)
MDRV_CPU_PORTS(alvgdmd_readport, alvgdmd_writeport)
MDRV_CPU_PERIODIC_INT(dmd32_firq, 12000000. / (640. * 256.))
MDRV_CPU_PERIODIC_INT(dmd32_firq2, 12000000. / (4.*384.*128.))
MACHINE_DRIVER_END
#endif

Expand All @@ -275,8 +274,7 @@ static void dmd32_init(struct sndbrdData *brdData) {
dmdlocals.brdData = *brdData;
dmd32_bank_w(0,0); //Set DMD Bank to 0
dmdlocals.selsync = 1; //Start Sync @ 1
// Init PWM shading
core_dmd_pwm_init(&dmdlocals.pwm_state, 128, 32, CORE_DMD_PWM_FILTER_ALVG);
core_dmd_pwm_init(&dmdlocals.pwm_state, 128, 32, IS_PCA020 ? CORE_DMD_PWM_FILTER_ALVG1 : CORE_DMD_PWM_FILTER_ALVG2);
}

static void dmd32_exit(int boardNo) {
Expand All @@ -301,24 +299,16 @@ static READ_HANDLER(dmd32_latch_r) {
return dmdlocals.cmd;
}

// PCA020A rasterize 128 rows at once (so 4 frames), with a main VCLOCK of 3MHz (Pistol Poker and Mystery Castle)
static INTERRUPT_GEN(dmd32_firq) {
if (!IS_PCA020 && (dmdlocals.selsync == 0)) { // VCLOCK is disabled so FIRQ may not happen [not present on Al's Garage Band Hardware]
LOG(("Skipping INT1\n"));
return;
}
//Pulse the INT1 Line (wired to /ROWDATA so pulsed when vertical blanking after a sequence of 4 PWM frames)
static INTERRUPT_GEN(dmd32_firq1) {
//static double prev; printf("DMD VBlank %8.5fms => %8.5fHz for 4 frames so %8.5fHz\n", timer_get_time() - prev, 1. / (timer_get_time() - prev), 4. / (timer_get_time() - prev)); prev = timer_get_time();
//Pulse the INT1 Line (wired to /ROWDATA so pulsed when vertical blanking after a sequence of PWM frames)
LOG(("INT1 Pulse\n"));
cpu_set_irq_line(dmdlocals.brdData.cpuNo, I8051_INT1_LINE, PULSE_LINE);
assert((dmdlocals.colstart & 0x07) == 0); // Lowest 3 bits are actually loaded to the shift register, so it is possible to perform a dot shift, but we don't support it
const UINT8* RAM = (UINT8*)dmd32RAM + (dmdlocals.vid_page << 11) + ((dmdlocals.colstart >> 3) & 0x0F);
const unsigned int plan_mask = dmdlocals.plans_enable ? 0x7F : 0x1F; // either render 4 different frames or 4 times the same
if (dmdlocals.pwm_state.legacyColorization) {
// For backwards compatibility regarding colorization, previous implementation:
// - for Al's Garage submitted first frame with a weight of 1, and second (same as first if plans_enable = 0) with a weight of 2 => 4 shades (with unbalanced luminance between frames)
// - for PP & MC submitted a 16 shade frame made up of the 4 frames (if plans_enable = 0, four time the same frame) => 16 shades (with balanced luminance between frames)
assert(IS_PCA020); // PCA020A does not need this and is not supported
// For backwards compatibility regarding colorization, previous implementation submitted first frame with a weight of 1, and second (same as first if plans_enable = 0) with a weight of 2 => 4 shades (with unbalanced luminance between frames)
core_dmd_submit_frame(&dmdlocals.pwm_state, RAM + (((dmdlocals.rowstart + 0x00) & plan_mask) << 4));
core_dmd_submit_frame(&dmdlocals.pwm_state, RAM + (((dmdlocals.rowstart + 0x20) & plan_mask) << 4));
core_dmd_submit_frame(&dmdlocals.pwm_state, RAM + (((dmdlocals.rowstart + 0x20) & plan_mask) << 4));
Expand All @@ -330,6 +320,24 @@ static INTERRUPT_GEN(dmd32_firq) {
}
}

static INTERRUPT_GEN(dmd32_firq2) {
if (dmdlocals.selsync == 0) { // VCLOCK is disabled so FIRQ may not happen [not present on Al's Garage Band Hardware]
LOG(("Skipping INT1\n"));
return;
}
//static double prev; printf("DMD VBlank %8.5fms => %8.5fHz for 4 frames so %8.5fHz\n", timer_get_time() - prev, 1. / (timer_get_time() - prev), 4. / (timer_get_time() - prev)); prev = timer_get_time();
LOG(("INT1 Pulse\n"));
cpu_set_irq_line(dmdlocals.brdData.cpuNo, I8051_INT1_LINE, PULSE_LINE);
assert((dmdlocals.colstart & 0x07) == 0); // Lowest 3 bits are actually loaded to the shift register, so it is possible to perform a dot shift, but we don't support it
const UINT8* RAM = (UINT8*)dmd32RAM + (dmdlocals.vid_page << 11) + ((dmdlocals.colstart >> 3) & 0x0F);
const unsigned int plan_mask = dmdlocals.plans_enable ? 0x7F : 0x1F; // either render 4 different frames or 4 times the same
// For backwards compatibility regarding colorization, previous implementation submitted a 16 shade frame made up of the 4 frames (if plans_enable = 0, four time the same frame) => 16 shades (with balanced luminance between frames)
core_dmd_submit_frame(&dmdlocals.pwm_state, RAM + (((dmdlocals.rowstart + 0x00) & plan_mask) << 4));
core_dmd_submit_frame(&dmdlocals.pwm_state, RAM + (((dmdlocals.rowstart + 0x20) & plan_mask) << 4));
core_dmd_submit_frame(&dmdlocals.pwm_state, RAM + (((dmdlocals.rowstart + 0x40) & plan_mask) << 4));
core_dmd_submit_frame(&dmdlocals.pwm_state, RAM + (((dmdlocals.rowstart + 0x60) & plan_mask) << 4));
}

PINMAME_VIDEO_UPDATE(alvgdmd_update) {
core_dmd_update_pwm(&dmdlocals.pwm_state);
video_update_core_dmd(bitmap, cliprect, layout);
Expand Down
4 changes: 2 additions & 2 deletions src/wpc/alvggames.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ ALVGS_SOUNDROM( "mcastle.102", CRC(752822d0) SHA1(36461ef03cac5aefa0c03dfdc63c
ALVGDMD_ROM2R( "mcastle.du4", CRC(686e253a) SHA1(28aff34c120c61e231e2111dc396df515bcbbb89),
"mcastle.du5", CRC(9095c367) SHA1(9d3e9416f662ee2aad891eef059278c530448fcc))
ALVG_ROMEND
CORE_GAMEDEFNV(mystcast,"Mystery Castle (R02)",1993,"Alvin G",mALVGS2DMD3,0)
CORE_GAMEDEFNV(mystcast,"Mystery Castle (R02)",1993,"Alvin G",mALVGS2DMD2,0)

// R03 has game ID EPC081
INITGAME2(mystcasa, DMD, FLIP78, 3/*?*/, SNDBRD_ALVGS2, SNDBRD_ALVGDMD, 2)
Expand All @@ -181,7 +181,7 @@ ALVGDMD_ROM( "u4.bin", CRC(a6969efc) SHA1(82da976cb3d30d6fb1576e4c67febd7235f7
"u5.bin", CRC(e5126980) SHA1(2c6d412c87bf27098dae4351958d84e8f9348423),
"u6.bin", CRC(eb241633) SHA1(8e5db75b32ed2ea74088615bbe1403d4c8feafbd))
ALVG_ROMEND
CORE_CLONEDEFNV(mystcasa,mystcast,"Mystery Castle (R03)",199?,"Alvin G",mALVGS2DMD3,0)
CORE_CLONEDEFNV(mystcasa,mystcast,"Mystery Castle (R03)",199?,"Alvin G",mALVGS2DMD2,0)

/*-------------------------------------------------------------------
/ Pistol Poker (AG10)
Expand Down
18 changes: 13 additions & 5 deletions src/wpc/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3053,7 +3053,7 @@ void core_dmd_pwm_init(core_tDMDPWMState* dmd_state, const int width, const int
dmd_state->fir_size = dmd_state->nFrames = sizeof(fir_122_15) / sizeof(UINT16);
}
break;
case CORE_DMD_PWM_FILTER_ALVG: // Alvin G.: 293Hz refresh rate / 15Hz low pass filter / 4 frames PWM pattern
case CORE_DMD_PWM_FILTER_ALVG1: // Alvin G. DMD Generation 1: 976Hz refresh rate / 15Hz low pass filter / 4 frames PWM pattern
if (dmd_state->legacyColorization)
{
// Al's Garage Band previous implementation would weight the first rasterized frame half less than the second and would only share this
Expand All @@ -3063,10 +3063,18 @@ void core_dmd_pwm_init(core_tDMDPWMState* dmd_state, const int width, const int
dmd_state->fir_sum = 60000;
dmd_state->fir_size = dmd_state->nFrames = sizeof(fir_colorization_2_frames) / sizeof(UINT16);
} else {
static const UINT16 fir_293_15[] = { 1258, 4169, 10900, 16440, 16440, 10900, 4169, 1258 };
dmd_state->fir_weights = fir_293_15;
dmd_state->fir_sum = 65534;
dmd_state->fir_size = dmd_state->nFrames = sizeof(fir_293_15) / sizeof(UINT16);
static const UINT16 fir_976_15[] = { 928, 1973, 4725, 8162, 10962, 12035, 10962, 8162, 4725, 1973, 928 };
dmd_state->fir_weights = fir_976_15;
dmd_state->fir_sum = 65535;
dmd_state->fir_size = dmd_state->nFrames = sizeof(fir_976_15) / sizeof(UINT16);
}
break;
case CORE_DMD_PWM_FILTER_ALVG2: // Alvin G. DMD Generation 2: 244Hz refresh rate / 15Hz low pass filter / 4 frames PWM pattern
{
static const UINT16 fir_244_15[] = { 521, 1451, 4187, 8183, 11798, 13255, 11798, 8183, 4187, 1451, 521 };
dmd_state->fir_weights = fir_244_15;
dmd_state->fir_sum = 65535;
dmd_state->fir_size = dmd_state->nFrames = sizeof(fir_244_15) / sizeof(UINT16);
}
break;
default:
Expand Down
9 changes: 5 additions & 4 deletions src/wpc/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -614,10 +614,11 @@ typedef struct {
unsigned int fir_sum; // Sum of filter weights
unsigned int frame_index; // Raw frame index
} core_tDMDPWMState;
#define CORE_DMD_PWM_FILTER_DE 0
#define CORE_DMD_PWM_FILTER_GTS3 1
#define CORE_DMD_PWM_FILTER_WPC 2
#define CORE_DMD_PWM_FILTER_ALVG 3
#define CORE_DMD_PWM_FILTER_DE 0
#define CORE_DMD_PWM_FILTER_GTS3 1
#define CORE_DMD_PWM_FILTER_WPC 2
#define CORE_DMD_PWM_FILTER_ALVG1 3
#define CORE_DMD_PWM_FILTER_ALVG2 4
extern void core_dmd_pwm_init(core_tDMDPWMState* dmd_state, const int width, const int height, const int filter);
extern void core_dmd_pwm_exit(core_tDMDPWMState* dmd_state);
extern void core_dmd_submit_frame(core_tDMDPWMState* dmd_state, const UINT8* frame);
Expand Down

0 comments on commit 617082d

Please sign in to comment.