/*************************************************************************** Popper Omori Electric CAD (OEC) 1983 PPR-12 18.432MHz 6148 6148 6148 4148 2128 2128 2128 2128 p3 p6 z80A p2 p5 p0 p1 p4 z80A 8910 8910 SW2 SW1 p.m4 p.m3 Notes: - a lower interleave causes some sounds to miss, different interleave values change random elements eg position of falling logs, it may be enough to synchronise the cpus on read/writes to the 0xdd00/0f range - it's possible that the two score rows shouldn't be overlaid or shouldn't be drawn at all times, the only time there is anything underneath is the colour bars on the startup screen - on odd/bonus levels the chars are not aligned with the sprites when the pylons are initially drawn (too low) resulting in the top 3 pylons being black, as the pylons move up the screen the chars go out of alignment again (this time too high) for a single frame to show another glitch - front to back ordering of object/enemy sprites appears wrong - on even levels the front most balloon carrying new enemies swaps between the left and middle one, clipping to 16 pixel strips solves this but causes other obvious sprite drawing errors - balloon sprites not displayed on high score entry due to the way videoram is being drawn, possibly trying to create a stencil effect - bursting a balloon on bonus levels just before dying from a log causes a sprite glitch for a few frames and then a 9999 point bonus - reads from 0xffff appear to be a harmless bug in the code that initialises memory blocks for enemies on odd levels, it goes on to read from 0x001f, 0x003f, 0x005f, 0x007f in the rom before giving up - a reset while a game is in progress or with credits inserted shows a "sorry!! over run" message instead of the test screen during startup, looks like the game assumes the reset was caused by the watchdog after a hang/crash and is apologising to the player for the loss of their game - title screen sprite colour glitch in lower right corner of the large pylon when an even level was shown before, caused by the game trying to colour the top of the pylon in the same way as for odd levels - enemies stop moving on even levels on rare occasions - enemy sprites entering from the bridges on odd levels are clipped by a few pixels at the top, this would happen even without the overlay as the first few rows of videoram are high priority solid chars most of the time - unknown 0xe002 writes, on for title and level 2 off for all else, looks to be graphic related - bottom of screen is scrappy on odd levels, amongst other things the sprites leave early, screen size could be clipped tighter or there could be some special handling needed, possibly tied to the 0xe002 writes - the game freezes in much the same way as the stop dip switch when the coin inputs are high (a short PORT_IMPULSE duration will still cause a hitch), so potentially there's a coin lockout mechanism ***************************************************************************/ #include "emu.h" #include "cpu/z80/z80.h" #include "sound/ay8910.h" #include "includes/popper.h" /************************************* * * Memory handlers * *************************************/ //e000 e001 e002 e003 //76543210 76543210 76543210 76543210 //x------- unused x------- unused x------- unused x------- unused //-x------ dsw1:1 -x------ dsw1:2 -x------ dsw1:3 -x------ dsw1:4 //--x----- unused --x----- unused --x----- unused --x----- unused //---x---- dsw2:1 ---x---- dsw2:2 ---x---- dsw2:3 ---x---- dsw2:4 //----x--- service ----xxxx p1 udlr ----x--- unused ----xxxx p2 udlr //-----x-- coin a -----x-- coin b //------x- start 1 ------x- start 2 //-------x p1 b1 -------x p2 b1 // //e004 e005 e006 e007 //x------- dsw1:5 x------- dsw1:6 x------- dsw1:7 x------- dsw1:8 //-x------ unused -x------ unused -x------ unused -x------ unused //--x----- dsw2:5 --x----- dsw2:6 --x----- dsw2:7 --x----- dsw2:8 //---xxxxx unused ---xxxxx unused ---xxxxx unused ---xxxxx unused // //dsw1 dsw2 //87654321 87654321 //xx------ extra x------- stop //--xx---- poppers -x------ clear (current level) //----xx-- coin b --x----- upright (cabinet) //------xx coin a ---x---- crt dir. (flip screen) // ----x--- pass (unlimited lives) // -----x-- free play // ------x- continue // -------x sound static READ8_HANDLER( popper_input_ports_r ) { UINT8 data = 0; switch (offset) { // player inputs dsw1 dsw2 case 0: data = input_port_read(space->machine(), "IN0") | ((input_port_read(space->machine(), "DSW1") & 0x02) << 5) | ((input_port_read(space->machine(), "DSW2") & 0x01) << 4); break; case 1: data = input_port_read(space->machine(), "IN1") | ((input_port_read(space->machine(), "DSW1") & 0x01) << 6) | ((input_port_read(space->machine(), "DSW2") & 0x02) << 3); break; case 2: data = input_port_read(space->machine(), "IN2") | ((input_port_read(space->machine(), "DSW1") & 0x08) << 3) | ((input_port_read(space->machine(), "DSW2") & 0x04) << 2); break; case 3: data = input_port_read(space->machine(), "IN3") | ((input_port_read(space->machine(), "DSW1") & 0x04) << 4) | ((input_port_read(space->machine(), "DSW2") & 0x08) << 1); break; case 4: data = ((input_port_read(space->machine(), "DSW1") & 0x20) << 2) | ((input_port_read(space->machine(), "DSW2") & 0x10) << 1); break; case 5: data = ((input_port_read(space->machine(), "DSW1") & 0x10) << 3) | ((input_port_read(space->machine(), "DSW2") & 0x20) << 0); break; case 6: data = ((input_port_read(space->machine(), "DSW1") & 0x80) << 0) | ((input_port_read(space->machine(), "DSW2") & 0x40) >> 1); break; case 7: data = ((input_port_read(space->machine(), "DSW1") & 0x40) << 1) | ((input_port_read(space->machine(), "DSW2") & 0x80) >> 2); break; } return data; } static READ8_HANDLER( popper_soundcpu_nmi_r ) { popper_state *state = space->machine().driver_data(); device_set_input_line(state->m_audiocpu, INPUT_LINE_NMI, PULSE_LINE); return 0; } /************************************* * * Memory maps * *************************************/ static ADDRESS_MAP_START( popper_map, AS_PROGRAM, 8 ) AM_RANGE(0x0000, 0x5fff) AM_ROM AM_RANGE(0xc000, 0xc1bf) AM_RAM AM_RANGE(0xc1c0, 0xc1ff) AM_RAM_WRITE(popper_ol_videoram_w) AM_BASE_MEMBER(popper_state, m_ol_videoram) AM_RANGE(0xc200, 0xc61f) AM_RAM_WRITE(popper_videoram_w) AM_BASE_MEMBER(popper_state, m_videoram) AM_RANGE(0xc620, 0xc9bf) AM_RAM AM_RANGE(0xc9c0, 0xc9ff) AM_RAM_WRITE(popper_ol_attribram_w) AM_BASE_MEMBER(popper_state, m_ol_attribram) AM_RANGE(0xca00, 0xce1f) AM_RAM_WRITE(popper_attribram_w) AM_BASE_MEMBER(popper_state, m_attribram) AM_RANGE(0xce20, 0xcfff) AM_RAM AM_RANGE(0xd000, 0xd7ff) AM_RAM AM_BASE_SIZE_MEMBER(popper_state, m_spriteram, m_spriteram_size) AM_RANGE(0xd800, 0xdfff) AM_RAM AM_SHARE("share1") AM_RANGE(0xe000, 0xe007) AM_READ(popper_input_ports_r) AM_RANGE(0xe000, 0xe000) AM_WRITE(interrupt_enable_w) AM_RANGE(0xe001, 0xe001) AM_WRITE(popper_flipscreen_w) AM_RANGE(0xe002, 0xe002) AM_WRITE(popper_e002_w) //?? seems to be graphic related AM_RANGE(0xe003, 0xe003) AM_WRITE(popper_gfx_bank_w) AM_RANGE(0xe004, 0xe007) AM_WRITENOP //?? range cleared once when the SP is set AM_RANGE(0xe400, 0xe400) AM_READ(popper_soundcpu_nmi_r) AM_RANGE(0xf800, 0xf800) AM_READNOP //?? read once at startup AM_RANGE(0xfc00, 0xfc00) AM_READNOP //?? possibly watchdog AM_RANGE(0xffff, 0xffff) AM_READNOP ADDRESS_MAP_END static ADDRESS_MAP_START( popper_sound_map, AS_PROGRAM, 8 ) AM_RANGE(0x0000, 0x0fff) AM_ROM AM_RANGE(0x8000, 0x8001) AM_DEVWRITE("ay1", ay8910_address_data_w) AM_RANGE(0x8002, 0x8002) AM_READNOP //?? all read once at startup and the AM_RANGE(0x8002, 0x8002) AM_WRITENOP //?? same writes as 0x8000 (mostly) AM_RANGE(0x8003, 0x8003) AM_READNOP //?? result ignored, looks like part AM_RANGE(0xa000, 0xa001) AM_DEVWRITE("ay2", ay8910_address_data_w) AM_RANGE(0xa002, 0xa002) AM_READNOP //?? of AY8910 initialisation AM_RANGE(0xa002, 0xa002) AM_WRITENOP //?? same writes as 0xa000 AM_RANGE(0xd800, 0xdfff) AM_RAM AM_SHARE("share1") ADDRESS_MAP_END /************************************* * * Input ports * *************************************/ static INPUT_PORTS_START( popper ) PORT_START("IN0") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START1 ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_COIN1 ) //ignored if held for 12 or more frames PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_START("IN1") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_START("IN2") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_COIN2 ) //ignored if held for 12 or more frames PORT_START("IN3") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_COCKTAIL PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_COCKTAIL PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_COCKTAIL PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_COCKTAIL PORT_START("DSW1") /* FAKE DSW1 */ PORT_DIPNAME( 0x03, 0x00, DEF_STR( Coin_A ) ) //SW1:1-2 PORT_DIPSETTING( 0x02, DEF_STR( 3C_1C ) ) PORT_DIPSETTING( 0x01, DEF_STR( 2C_1C ) ) PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x03, DEF_STR( 1C_2C ) ) PORT_DIPNAME( 0x0c, 0x08, DEF_STR( Coin_B ) ) //SW1:3-4 PORT_DIPSETTING( 0x00, DEF_STR( 1C_3C ) ) PORT_DIPSETTING( 0x04, DEF_STR( 1C_4C ) ) PORT_DIPSETTING( 0x08, DEF_STR( 1C_5C ) ) PORT_DIPSETTING( 0x0c, DEF_STR( 1C_6C ) ) PORT_DIPNAME( 0x30, 0x10, DEF_STR( Lives ) ) //SW1:5-6 PORT_DIPSETTING( 0x00, "2" ) PORT_DIPSETTING( 0x10, "3" ) PORT_DIPSETTING( 0x20, "4" ) PORT_DIPSETTING( 0x30, "5" ) PORT_DIPNAME( 0xc0, 0x00, DEF_STR( Bonus_Life ) ) //SW1:7-8 PORT_DIPSETTING( 0x00, "20k, then every 70k" ) PORT_DIPSETTING( 0x40, "30k, then every 70k" ) PORT_DIPSETTING( 0x80, "40k, then every 70k" ) PORT_DIPSETTING( 0xc0, "50k, then every 70k" ) PORT_START("DSW2") /* FAKE DSW2 */ PORT_DIPNAME( 0x01, 0x01, DEF_STR( Demo_Sounds ) ) //SW2:1 PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x00, DEF_STR( Allow_Continue ) ) //SW2:2 (stored in 0xd987, never read) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x02, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x00, DEF_STR( Free_Play ) ) //SW2:3 PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x04, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x00, "Pass (Unlimited Lives) (Cheat)") //SW2:4 PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x08, DEF_STR( On ) ) PORT_DIPNAME( 0x10, 0x00, DEF_STR( Flip_Screen ) ) //SW2:5 PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x10, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x20, DEF_STR( Cabinet ) ) //SW2:6 PORT_DIPSETTING( 0x20, DEF_STR( Upright ) ) PORT_DIPSETTING( 0x00, DEF_STR( Cocktail ) ) PORT_DIPNAME( 0x40, 0x00, "Clear (Current Level) (Cheat)") //SW2:7 PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x00, "Stop" ) //SW2:8 PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x80, DEF_STR( On ) ) INPUT_PORTS_END /************************************* * * Graphics definitions * *************************************/ static const gfx_layout popper_charlayout = { 8,8, RGN_FRAC(2,2), 2, { 0, 4 }, { STEP4(8,1), STEP4(0,1) }, { STEP8(0,16) }, 16*8 }; static const gfx_layout popper_spritelayout = { 16,16, RGN_FRAC(1,2), 2, { 0, RGN_FRAC(1,2) }, { STEP8(8,1), STEP8(0,1) }, { STEP16(0, 16) }, 16*2*8 }; static GFXDECODE_START( popper ) GFXDECODE_ENTRY( "gfx1", 0, popper_charlayout, 0, 16 ) GFXDECODE_ENTRY( "gfx2", 0, popper_spritelayout, 0, 16 ) GFXDECODE_END /************************************* * * Machine driver * *************************************/ static MACHINE_START( popper ) { popper_state *state = machine.driver_data(); state->m_audiocpu = machine.device("audiocpu"); state->save_item(NAME(state->m_flipscreen)); state->save_item(NAME(state->m_e002)); state->save_item(NAME(state->m_gfx_bank)); } static MACHINE_RESET( popper ) { popper_state *state = machine.driver_data(); state->m_flipscreen = 0; state->m_e002 = 0; state->m_gfx_bank = 0; } static MACHINE_CONFIG_START( popper, popper_state ) /* basic machine hardware */ MCFG_CPU_ADD("maincpu", Z80,18432000/6) MCFG_CPU_PROGRAM_MAP(popper_map) MCFG_CPU_VBLANK_INT("screen", nmi_line_pulse) MCFG_CPU_ADD("audiocpu", Z80,18432000/12) MCFG_CPU_PROGRAM_MAP(popper_sound_map) MCFG_CPU_PERIODIC_INT(irq0_line_hold,4*60) //NMIs caused by the main CPU MCFG_QUANTUM_TIME(attotime::from_hz(1800)) MCFG_MACHINE_START(popper) MCFG_MACHINE_RESET(popper) /* video hardware */ MCFG_SCREEN_ADD("screen", RASTER) MCFG_SCREEN_REFRESH_RATE(60) MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(0)) MCFG_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16) MCFG_SCREEN_SIZE(33*8, 32*8) MCFG_SCREEN_VISIBLE_AREA(0*8, 33*8-1, 2*8, 30*8-1) MCFG_SCREEN_UPDATE(popper) MCFG_GFXDECODE(popper) MCFG_PALETTE_LENGTH(64) MCFG_PALETTE_INIT(popper) MCFG_VIDEO_START(popper) /* sound hardware */ MCFG_SPEAKER_STANDARD_MONO("mono") MCFG_SOUND_ADD("ay1", AY8910, 18432000/12) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) MCFG_SOUND_ADD("ay2", AY8910, 18432000/12) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) MACHINE_CONFIG_END /************************************* * * ROM definition * *************************************/ ROM_START( popper ) ROM_REGION( 0x10000, "maincpu", 0 ) ROM_LOAD( "p1", 0x0000, 0x2000, CRC(56881b70) SHA1(d3ade7a54a6cb8a0babf0d667a6b27f492a739dc) ) ROM_LOAD( "p2", 0x2000, 0x2000, CRC(a054d9d2) SHA1(fcd86e7247b40cf07ea595a64c104b99b0e93ced) ) ROM_LOAD( "p3", 0x4000, 0x2000, CRC(6201928a) SHA1(53b571b9f2c0568f10cd974641863c2e00777b46) ) ROM_REGION( 0x10000, "audiocpu", 0 ) ROM_LOAD( "p0", 0x0000, 0x1000, CRC(ef5f7c5b) SHA1(c63a3d9ef2868ad7eaacddec810d62d2e124dc15) ) ROM_REGION( 0x2000, "gfx1", 0 ) ROM_LOAD( "p4", 0x0000, 0x2000, CRC(86203349) SHA1(cce2dd3fa786c2fb3ca80e7b93adf94db3b46b01) ) ROM_REGION( 0x4000, "gfx2", 0 ) ROM_LOAD( "p5", 0x0000, 0x2000, CRC(a21ac194) SHA1(2c0e3df8981a12d383b1c4619a0b95a7c2d176a7) ) ROM_LOAD( "p6", 0x2000, 0x2000, CRC(d99fa790) SHA1(201271ee4fb812236a38cb5f9070ac29e8186097) ) ROM_REGION( 0x0040, "proms", 0 ) ROM_LOAD( "p.m3", 0x0000, 0x0020, CRC(713217aa) SHA1(6083c3432bf94c9e983fcc79171529f519c86105) ) ROM_LOAD( "p.m4", 0x0020, 0x0020, CRC(384de5c1) SHA1(892c89a01c11671c5708113b4e4c27b84be37ea6) ) ROM_END /************************************* * * Game driver * *************************************/ GAME( 1983, popper, 0, popper, popper, 0, ROT90, "Omori Electric Co., Ltd.", "Popper", GAME_IMPERFECT_COLORS | GAME_SUPPORTS_SAVE )