diff --git a/src/mame/audio/laserbat.cpp b/src/mame/audio/laserbat.cpp index a588c839317..207ab4633c4 100644 --- a/src/mame/audio/laserbat.cpp +++ b/src/mame/audio/laserbat.cpp @@ -2,9 +2,36 @@ // copyright-holders:Vas Crabb /* Laser Battle / Lazarian (c) 1981 Zaccaria + Cat and Mouse (c) 1982 Zaccaria audio emulation by Vas Crabb +*/ +#include "includes/laserbat.h" + + +READ8_MEMBER(laserbat_state_base::rhsc_r) +{ + return m_rhsc; +} + +WRITE8_MEMBER(laserbat_state_base::whsc_w) +{ + m_whsc = data; +} + +WRITE8_MEMBER(laserbat_state_base::csound1_w) +{ + m_csound1 = data; +} + +WRITE8_MEMBER(laserbat_state_base::csound2_w) +{ + m_csound2 = data; +} + + +/* The Laser Battle/Lazarian sound board has a SN76477 CSG, two TMS3615 tone synthesisers, and a TDA1010 power amplifier. It receives commands from the game board over a 16-bit unidirectional data bus. @@ -99,30 +126,6 @@ */ -#include "includes/laserbat.h" - - -READ8_MEMBER(laserbat_state_base::rhsc_r) -{ - return m_rhsc; -} - -WRITE8_MEMBER(laserbat_state_base::whsc_w) -{ - m_whsc = data; -} - -WRITE8_MEMBER(laserbat_state_base::csound1_w) -{ - m_csound1 = data; -} - -WRITE8_MEMBER(laserbat_state_base::csound2_w) -{ - m_csound2 = data; -} - - WRITE8_MEMBER(laserbat_state::csound2_w) { // there are a bunch of edge-triggered things, so grab changes @@ -221,3 +224,153 @@ WRITE8_MEMBER(laserbat_state::csound2_w) // keep for detecting changes next time m_csound2 = data; } + + +/* + The Cat and Mouse sound board has a 6802 processor with three ROMs, + a 6821 PIA, two AY-3-8910 PSGs, and some other logic and analog + circuitry. Unfortunately we lack a schematic, so all knowledge of + this board is based on tracing the sound program. + + The 6821 PIA is mapped at addresses $005C..$005F. The known PIA + signal assignments are as follows: + + +------+-----------------------+ + | PA0 | PSG1/PSG2 DA0 | + | PA1 | PSG1/PSG2 DA1 | + | PA2 | PSG1/PSG2 DA2 | + | PA3 | PSG1/PSG2 DA3 | + | PA4 | PSG1/PSG2 DA4 | + | PA5 | PSG1/PSG2 DA5 | + | PA6 | PSG1/PSG2 DA6 | + | PA7 | PSG1/PSG2 DA7 | + | PB0 | PSG1 BC1 | + | PB1 | PSG1 BDIR | + | PB2 | PSG2 BC1 | + | PB3 | PSG2 BDIR | + | CA1 | Host interface bit 6 | + | CB1 | periodic IRQ source | + | IRQA | 6802 NMI | + | IRQB | 6802 IRQ | + +------+-----------------------+ + + The program makes use of I/O port A on the first PSG as outputs. At + a guess, it could have the same function as it does on other + Zaccaria sound boards. + + The first PSG receives commands from the game board in the low five + bits of port B. Commands are processed on receiving an NMI. The + sound program always masks out the high three bits of the value so + they could be connected to anything on the board. + + The I/O ports on the second PSG don't appear to be used at all. + + The game board sends commands to the sound board over a 16-bit + unidirectional data bus. The CPU cannot write all sixteen lines + atomically, it write to lines 1-8 as one group and 9-16 as another + group. Not all bit functions are known: + + +-----+----------------------------------------------------------+ + | Bit | Function | + +-----+----------------------------------------------------------+ + | 1 | PSG1 IOB0 | + | 2 | PSG1 IOB1 | + | 3 | PSG1 IOB2 | + | 4 | PSG1 IOB3 | + | 5 | PSG1 IOB4 | + | 6 | PIA CA1 | + | 7 | | + | 8 | | + | 9 | Used but unknown purpose | + | 10 | | + | 11 | | + | 12 | | + | 13 | | + | 14 | | + | 15 | | + | 16 | Used but unknown purpose | + +-----+----------------------------------------------------------+ + + The game board makes the the audio output from the first S2636 PVI + (5E) available on a pin at the sound board interface connector, but + it may or may not be routed anywhere. +*/ + +WRITE8_MEMBER(catnmous_state::csound1_w) +{ + m_pia->ca1_w((data & 0x20) ? 1 : 0); + m_csound1 = data; +} + +READ8_MEMBER(catnmous_state::pia_porta_r) +{ + UINT8 const control = m_pia->b_output(); + UINT8 data = 0xff; + + if (0x01 == (control & 0x03)) + data &= m_psg1->data_r(space, 0); + + if (0x04 == (control & 0x0c)) + data &= m_psg2->data_r(space, 0); + + return data; +} + +WRITE8_MEMBER(catnmous_state::pia_porta_w) +{ + UINT8 const control = m_pia->b_output(); + + if (control & 0x02) + m_psg1->data_address_w(space, (control >> 0) & 0x01, data); + + if (control & 0x08) + m_psg2->data_address_w(space, (control >> 2) & 0x01, data); +} + +WRITE8_MEMBER(catnmous_state::pia_portb_w) +{ + if (data & 0x02) + m_psg1->data_address_w(space, (data >> 0) & 0x01, m_pia->a_output()); + + if (data & 0x08) + m_psg2->data_address_w(space, (data >> 2) & 0x01, m_pia->a_output()); +} + +WRITE_LINE_MEMBER(catnmous_state::pia_irqa) +{ + m_audiocpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE); +} + +WRITE_LINE_MEMBER(catnmous_state::pia_irqb) +{ + m_audiocpu->set_input_line(INPUT_LINE_IRQ0, state ? ASSERT_LINE : CLEAR_LINE); +} + +WRITE8_MEMBER(catnmous_state::psg1_porta_w) +{ + // similar to zaccaria.c since we have no clue how this board really works + // this code could be completely wrong/inappropriate for this game for all we know + static double const table[8] = { + RES_K(8.2), + RES_R(820), + RES_K(3.3), + RES_R(150), + RES_K(5.6), + RES_R(390), + RES_K(1.5), + RES_R(47) }; + RES_VOLTAGE_DIVIDER(RES_K(4.7), table[data & 0x07]); + m_psg2->set_volume(1, 150 * RES_VOLTAGE_DIVIDER(RES_K(4.7), table[data & 0x07])); +} + +READ8_MEMBER(catnmous_state::psg1_portb_r) +{ + // the program masks out the three high bits - no clue what they're connected to + return m_csound1 & 0x1f; +} + +INTERRUPT_GEN_MEMBER(catnmous_state::cb1_toggle) +{ + m_cb1 = !m_cb1; + m_pia->cb1_w(m_cb1 ? 1 : 0); +} diff --git a/src/mame/drivers/laserbat.cpp b/src/mame/drivers/laserbat.cpp index ed932816c95..589c94c39f4 100644 --- a/src/mame/drivers/laserbat.cpp +++ b/src/mame/drivers/laserbat.cpp @@ -1,12 +1,15 @@ // license:BSD-3-Clause -// copyright-holders:Pierpaolo Prazzoli, Vas Crabb +// copyright-holders:Vas Crabb /* Laser Battle / Lazarian (c) 1981 Zaccaria Cat and Mouse (c) 1982 Zaccaria original driver by Pierpaolo Prazzoli - The two games have a similar video hardware, but sound hardware is + The two games have identical game/video boards hardware, but + completely different sound boards. Laser Battle/Lazarian have a + dumb sound board with TMS organ and CSG chips driven directly by the + game program. Cat'N'Mouse very different and they don't use the collision detection provided by the s2636 chips. @@ -18,10 +21,14 @@ written at I/O address 3. The sound board controls data direction on J7 and when input from sound board to game board is latched. + Both Laser Battle/Lazarian and Cat and Mouse use the unidirectional + interface on J3. It seems there are no games that actually use the + bidirectional interface on J7. + Laser Battle/Lazarian notes: * Cocktail cabinet has an additional "image commutation board" consuming the screen flip output, presumably flipping the image by - means of dark magic + reversing the deflection coil connections * Player 2 inputs are only used in cocktail mode * Tilt input resets Laser Battle, but just causes loss of one credit in Lazarian @@ -35,7 +42,7 @@ TODO: - work out where all the magic layer offsets come from - - second bank of DIP switches in laserbat + - second bank of DIP switches in laserbat and catnmous - sound in laserbat (with schematics) and in catnmous */ @@ -99,8 +106,6 @@ READ8_MEMBER(laserbat_state_base::rrowx_r) /* - Color handling with 2716.14L and 82S100.10M - 2716.14L address lines are connected as follows: A0 4H @@ -134,7 +139,7 @@ static ADDRESS_MAP_START( laserbat_map, AS_PROGRAM, 8, laserbat_state_base ) AM_RANGE(0x6000, 0x73ff) AM_ROM AM_RANGE(0x7800, 0x7bff) AM_ROM - AM_RANGE(0x1400, 0x14ff) AM_MIRROR(0x6000) AM_WRITENOP // always 0 (bullet ram in Quasar) + AM_RANGE(0x1400, 0x14ff) AM_MIRROR(0x6000) AM_WRITENOP AM_RANGE(0x1500, 0x15ff) AM_MIRROR(0x6000) AM_DEVREADWRITE("pvi1", s2636_device, read_data, write_data) AM_RANGE(0x1600, 0x16ff) AM_MIRROR(0x6000) AM_DEVREADWRITE("pvi2", s2636_device, read_data, write_data) AM_RANGE(0x1700, 0x17ff) AM_MIRROR(0x6000) AM_DEVREADWRITE("pvi3", s2636_device, read_data, write_data) @@ -159,7 +164,8 @@ ADDRESS_MAP_END static ADDRESS_MAP_START( catnmous_sound_map, AS_PROGRAM, 8, catnmous_state ) AM_RANGE(0x0000, 0x007f) AM_RAM AM_RANGE(0x500c, 0x500f) AM_DEVREADWRITE("pia", pia6821_device, read, write) - AM_RANGE(0xf000, 0xffff) AM_ROM + AM_RANGE(0xc000, 0xcfff) AM_ROM + AM_RANGE(0xe000, 0xffff) AM_ROM ADDRESS_MAP_END @@ -348,7 +354,7 @@ static INPUT_PORTS_START( catnmous ) // PORT_DIPSETTING( 0x50, DEF_STR(Infinite) ) // PORT_DIPSETTING( 0x60, DEF_STR(Infinite) ) // PORT_DIPSETTING( 0x70, DEF_STR(Infinite) ) - PORT_DIPNAME( 0x80, 0x80, "Game Over Melody" ) PORT_DIPLOCATION("SW-1:8") + PORT_DIPNAME( 0x80, 0x80, DEF_STR(Demo_Sounds) ) PORT_DIPLOCATION("SW-1:8") PORT_DIPSETTING( 0x00, DEF_STR(Off) ) PORT_DIPSETTING( 0x80, DEF_STR(On) ) INPUT_PORTS_END @@ -387,70 +393,11 @@ static GFXDECODE_START( laserbat ) GFXDECODE_END -/* Cat'N Mouse sound ***********************************/ - -WRITE_LINE_MEMBER(catnmous_state::zaccaria_irq0a) -{ - m_audiocpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE); -} - -WRITE_LINE_MEMBER(catnmous_state::zaccaria_irq0b) -{ - m_audiocpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE); -} - -READ8_MEMBER(catnmous_state::zaccaria_port0a_r) -{ - ay8910_device *ay8910 = (m_active_8910 == 0) ? m_ay1 : m_ay2; - return ay8910->data_r(space, 0); -} - -WRITE8_MEMBER(catnmous_state::zaccaria_port0a_w) -{ - m_port0a = data; -} - -WRITE8_MEMBER(catnmous_state::zaccaria_port0b_w) -{ - /* bit 1 goes to 8910 #0 BDIR pin */ - if ((m_last_port0b & 0x02) == 0x02 && (data & 0x02) == 0x00) - { - /* bit 0 goes to the 8910 #0 BC1 pin */ - m_ay1->data_address_w(space, m_last_port0b >> 0, m_port0a); - } - else if ((m_last_port0b & 0x02) == 0x00 && (data & 0x02) == 0x02) - { - /* bit 0 goes to the 8910 #0 BC1 pin */ - if (m_last_port0b & 0x01) - m_active_8910 = 0; - } - /* bit 3 goes to 8910 #1 BDIR pin */ - if ((m_last_port0b & 0x08) == 0x08 && (data & 0x08) == 0x00) - { - /* bit 2 goes to the 8910 #1 BC1 pin */ - m_ay2->data_address_w(space, m_last_port0b >> 2, m_port0a); - } - else if ((m_last_port0b & 0x08) == 0x00 && (data & 0x08) == 0x08) - { - /* bit 2 goes to the 8910 #1 BC1 pin */ - if (m_last_port0b & 0x04) - m_active_8910 = 1; - } - - m_last_port0b = data; -} - INTERRUPT_GEN_MEMBER(laserbat_state_base::laserbat_interrupt) { device.execute().set_input_line_and_vector(0, HOLD_LINE, 0x0a); } -INTERRUPT_GEN_MEMBER(catnmous_state::zaccaria_cb1_toggle) -{ - m_pia->cb1_w(m_cb1_toggle & 1); - m_cb1_toggle ^= 1; -} - DRIVER_INIT_MEMBER(laserbat_state_base, laserbat) { m_scanline_timer = timer_alloc(TIMER_SCANLINE); @@ -492,20 +439,7 @@ void catnmous_state::machine_start() { laserbat_state_base::machine_start(); - save_item(NAME(m_active_8910)); - save_item(NAME(m_port0a)); - save_item(NAME(m_last_port0b)); - save_item(NAME(m_cb1_toggle)); -} - -void catnmous_state::machine_reset() -{ - laserbat_state_base::machine_reset(); - - m_active_8910 = 0; - m_port0a = 0; - m_last_port0b = 0; - m_cb1_toggle = 0; + save_item(NAME(m_cb1)); } void laserbat_state_base::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) @@ -589,22 +523,22 @@ static MACHINE_CONFIG_DERIVED_CLASS( catnmous, laserbat_base, catnmous_state ) // sound board devices MCFG_CPU_ADD("audiocpu", M6802, 3580000) // ? MCFG_CPU_PROGRAM_MAP(catnmous_sound_map) - MCFG_CPU_PERIODIC_INT_DRIVER(catnmous_state, zaccaria_cb1_toggle, (double)3580000/4096) + MCFG_CPU_PERIODIC_INT_DRIVER(catnmous_state, cb1_toggle, (double)3580000/4096) MCFG_DEVICE_ADD("pia", PIA6821, 0) - MCFG_PIA_READPA_HANDLER(READ8(catnmous_state, zaccaria_port0a_r)) - MCFG_PIA_WRITEPA_HANDLER(WRITE8(catnmous_state, zaccaria_port0a_w)) - MCFG_PIA_WRITEPB_HANDLER(WRITE8(catnmous_state, zaccaria_port0b_w)) - MCFG_PIA_IRQA_HANDLER(WRITELINE(catnmous_state, zaccaria_irq0a)) - MCFG_PIA_IRQB_HANDLER(WRITELINE(catnmous_state, zaccaria_irq0b)) + MCFG_PIA_READPA_HANDLER(READ8(catnmous_state, pia_porta_r)) + MCFG_PIA_WRITEPA_HANDLER(WRITE8(catnmous_state, pia_porta_w)) + MCFG_PIA_WRITEPB_HANDLER(WRITE8(catnmous_state, pia_portb_w)) + MCFG_PIA_IRQA_HANDLER(WRITELINE(catnmous_state, pia_irqa)) + MCFG_PIA_IRQB_HANDLER(WRITELINE(catnmous_state, pia_irqb)) MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("ay1", AY8910, 3580000/2) // ? - MCFG_AY8910_PORT_B_READ_CB(READ8(driver_device, soundlatch_byte_r)) + MCFG_SOUND_ADD("psg1", AY8910, 3580000/2) // ? + MCFG_AY8910_PORT_B_READ_CB(READ8(catnmous_state, psg1_portb_r)) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) - MCFG_SOUND_ADD("ay2", AY8910, 3580000/2) // ? + MCFG_SOUND_ADD("psg2", AY8910, 3580000/2) // ? MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) MACHINE_CONFIG_END @@ -706,7 +640,7 @@ Sound Board 1b11107 6802 6821 -8910 +2*8910 */ ROM_START( catnmous ) @@ -746,8 +680,8 @@ ROM_START( catnmous ) ROM_LOAD( "82s100.13m", 0x0000, 0x00f5, CRC(6b724cdb) SHA1(8a0ca3b171b103661a3b2fffbca3d7162089e243) ) ROM_REGION( 0x10000, "audiocpu", 0 ) - ROM_LOAD( "sound01.1d", 0xd000, 0x1000, CRC(f65cb9d0) SHA1(a2fe7563c6da055bf6aa20797b2d9fa184f0133c) ) - ROM_LOAD( "sound01.1f", 0xe000, 0x1000, CRC(473c44de) SHA1(ff08b02d45a2c23cabb5db716aa203225a931424) ) + ROM_LOAD( "sound01.1f", 0xc000, 0x1000, CRC(473c44de) SHA1(ff08b02d45a2c23cabb5db716aa203225a931424) ) + ROM_LOAD( "sound01.1d", 0xe000, 0x1000, CRC(f65cb9d0) SHA1(a2fe7563c6da055bf6aa20797b2d9fa184f0133c) ) ROM_LOAD( "sound01.1e", 0xf000, 0x1000, CRC(1bd90c93) SHA1(20fd2b765a42e25cf7f716e6631b8c567785a866) ) ROM_END @@ -788,13 +722,13 @@ ROM_START( catnmousa ) ROM_LOAD( "catnmousa_82s100.13m", 0x0000, 0x00f5, CRC(6b724cdb) SHA1(8a0ca3b171b103661a3b2fffbca3d7162089e243) BAD_DUMP ) ROM_REGION( 0x10000, "audiocpu", 0 ) - ROM_LOAD( "snd.1d", 0xd000, 0x1000, CRC(f65cb9d0) SHA1(a2fe7563c6da055bf6aa20797b2d9fa184f0133c) ) - ROM_LOAD( "snd.1f", 0xe000, 0x1000, CRC(473c44de) SHA1(ff08b02d45a2c23cabb5db716aa203225a931424) ) + ROM_LOAD( "snd.1f", 0xc000, 0x1000, CRC(473c44de) SHA1(ff08b02d45a2c23cabb5db716aa203225a931424) ) + ROM_LOAD( "snd.1d", 0xe000, 0x1000, CRC(f65cb9d0) SHA1(a2fe7563c6da055bf6aa20797b2d9fa184f0133c) ) ROM_LOAD( "snd.1e", 0xf000, 0x1000, CRC(1bd90c93) SHA1(20fd2b765a42e25cf7f716e6631b8c567785a866) ) ROM_END -GAME( 1981, laserbat, 0, laserbat, laserbat, laserbat_state_base, laserbat, ROT0, "Zaccaria", "Laser Battle", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) -GAME( 1981, lazarian, laserbat, laserbat, lazarian, laserbat_state_base, laserbat, ROT0, "Zaccaria (Bally Midway license)", "Lazarian", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) -GAME( 1982, catnmous, 0, catnmous, catnmous, laserbat_state_base, laserbat, ROT90, "Zaccaria", "Cat and Mouse (set 1)", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE) -GAME( 1982, catnmousa,catnmous, catnmous, catnmous, laserbat_state_base, laserbat, ROT90, "Zaccaria", "Cat and Mouse (set 2)", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE) +GAME( 1981, laserbat, 0, laserbat, laserbat, laserbat_state_base, laserbat, ROT0, "Zaccaria", "Laser Battle", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) +GAME( 1981, lazarian, laserbat, laserbat, lazarian, laserbat_state_base, laserbat, ROT0, "Zaccaria (Bally Midway license)", "Lazarian", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) +GAME( 1982, catnmous, 0, catnmous, catnmous, laserbat_state_base, laserbat, ROT90, "Zaccaria", "Cat and Mouse (set 1)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) +GAME( 1982, catnmousa, catnmous, catnmous, catnmous, laserbat_state_base, laserbat, ROT90, "Zaccaria", "Cat and Mouse (set 2)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/includes/laserbat.h b/src/mame/includes/laserbat.h index 1440cf1daf9..6f96ee2b926 100644 --- a/src/mame/includes/laserbat.h +++ b/src/mame/includes/laserbat.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Pierpaolo Prazzoli, Vas Crabb +// copyright-holders:Vas Crabb /************************************************************************* Laser Battle / Lazarian - Cat and Mouse @@ -185,43 +185,42 @@ class catnmous_state : public laserbat_state_base public: catnmous_state(const machine_config &mconfig, device_type type, const char *tag) : laserbat_state_base(mconfig, type, tag) - , m_pia(*this, "pia") , m_audiocpu(*this, "audiocpu") - , m_ay1(*this, "ay1") - , m_ay2(*this, "ay2") - , m_active_8910(0) - , m_port0a(0) - , m_last_port0b(0) - , m_cb1_toggle(0) + , m_pia(*this, "pia") + , m_psg1(*this, "psg1") + , m_psg2(*this, "psg2") + , m_cb1(false) { } - // control ports - DECLARE_WRITE_LINE_MEMBER(zaccaria_irq0a); - DECLARE_WRITE_LINE_MEMBER(zaccaria_irq0b); - DECLARE_READ8_MEMBER(zaccaria_port0a_r); - DECLARE_WRITE8_MEMBER(zaccaria_port0a_w); - DECLARE_WRITE8_MEMBER(zaccaria_port0b_w); + // sound control ports + virtual DECLARE_WRITE8_MEMBER(csound1_w) override; + + // PIA handlers + DECLARE_READ8_MEMBER(pia_porta_r); + DECLARE_WRITE8_MEMBER(pia_porta_w); + DECLARE_WRITE8_MEMBER(pia_portb_w); + DECLARE_WRITE_LINE_MEMBER(pia_irqa); + DECLARE_WRITE_LINE_MEMBER(pia_irqb); + + // PSG handlers + DECLARE_WRITE8_MEMBER(psg1_porta_w); + DECLARE_READ8_MEMBER(psg1_portb_r); // periodic signal generators - INTERRUPT_GEN_MEMBER(zaccaria_cb1_toggle); + INTERRUPT_GEN_MEMBER(cb1_toggle); protected: // initialisation/startup virtual void machine_start() override; - virtual void machine_reset() override; // sound board devices - required_device m_pia; required_device m_audiocpu; - required_device m_ay1; - required_device m_ay2; - + required_device m_pia; + required_device m_psg1; + required_device m_psg2; // control line states - int m_active_8910; - int m_port0a; - int m_last_port0b; - int m_cb1_toggle; + bool m_cb1; }; diff --git a/src/mame/video/laserbat.cpp b/src/mame/video/laserbat.cpp index aaa364d4787..f119d59af5b 100644 --- a/src/mame/video/laserbat.cpp +++ b/src/mame/video/laserbat.cpp @@ -53,7 +53,8 @@ up indicating when player 2 is playing. In a cocktail cabinet this goes to an "image commutation board". It's not connected to anything in an upright cabinet. The "image commutation board" must - flip the image somehow, presumably by dark magic. + flip the image somehow, presumably by reversing the deflection coil + connections. There are still issues with horizontal alignment between layers. I have the schematic, yet I really can't understand where these issues