From 1c0ed14c006d68b1b45616ddaed7472f52fddc41 Mon Sep 17 00:00:00 2001 From: couriersud Date: Tue, 7 May 2019 23:54:33 +0200 Subject: tutankham: Implement starfield, better colors. [Guru, Couriersud] Used color code from galaxian for better color approximation. The starfield code was added to better document the 084 custom chip. There are two star fields implemented now. The active one can be selected in Machine Configuration. a) Scramble Starfield 1/3 star width like scramble/galaxian, starfield code same as scramble. This one is similar to the Konami starfield video below. The 084 uses the same signals as scramble. b) Bootleg Starfield Guru provided schematics of a daughter-board which on a Konami bootleg replaced the custom chip 084. This starfield matches the starfield (picture) of this bootleg. At least there is now a starfield which matches a real-life(tm) bootleg. None of the two alterntives reproduces the star field which can be observed in the Stern videos on youtube. The Konami video mentioned in video/tutankhm.cpp is different from tutankhm. Looks like we are missing an early board dump --- src/mame/drivers/junofrst.cpp | 4 +- src/mame/drivers/tutankhm.cpp | 133 +++++++++---- src/mame/includes/tutankhm.h | 42 ++++- src/mame/video/tutankhm.cpp | 421 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 552 insertions(+), 48 deletions(-) diff --git a/src/mame/drivers/junofrst.cpp b/src/mame/drivers/junofrst.cpp index 1a7d65d4cb1..de889c12050 100644 --- a/src/mame/drivers/junofrst.cpp +++ b/src/mame/drivers/junofrst.cpp @@ -376,8 +376,8 @@ void junofrst_state::machine_start() save_item(NAME(m_last_irq)); save_item(NAME(m_irq_toggle)); save_item(NAME(m_irq_enable)); - save_item(NAME(m_flip_x)); - save_item(NAME(m_flip_y)); + save_item(NAME(m_flipscreen_x)); + save_item(NAME(m_flipscreen_y)); save_item(NAME(m_blitterdata)); } diff --git a/src/mame/drivers/tutankhm.cpp b/src/mame/drivers/tutankhm.cpp index 037f1e38ba8..f0cc24a3384 100644 --- a/src/mame/drivers/tutankhm.cpp +++ b/src/mame/drivers/tutankhm.cpp @@ -23,32 +23,65 @@ Custom Chip 084 (Starfield generation) - * Inputs: - * - * NE555 ==> 10 (approx 1 HZ) - * 401 ==> 1 (crystal clock, most likely around 6 MHz) - * 109 ==> 2 (Video related) - * 403 ==> 3 (Video related) - * 403 ==> 4 (Video related) - * 405 ==> 5 (Video related) - * 406 ==> 6 (Video related) - * 420 ==> 20 (Video related) - * 421 ==> 21 (Video related) - * 422 ==> 22 (Video related) - * 423 ==> 23 (Video horizontal signal H1) - *0x8206 HFF ==> 7 (Horizontal flip) - *0x8204 407 ==> 8 (Enable ???) - * - *Outputs: - * 13 Red - * 14 Red - * 15 Green - * 16 Green - * 17 Blue - * 18 Blue - - -***************************************************************************/ + + ---------------------- + NE555 1Hz | 10 24 | VCC + | | + | | + 18Mhz 401 | 1 19 | 419 Reset Enable + 6MHz 109 | 2 | + 256H* 403 | 3 Custom 084 | + Vblank* 404 | 4 | + /SYNC* 405 | 5 18 | Blue + Aux. Enable 406 | 6 17 | Blue + Stars Enable 407 | 7 | + Horz.Flip HFF | 8 16 | Green + GND | 11 15 | Green + | | + H8Q 423 | 23 14 | Red + V1 422 | 22 13 | Red + V2 421 | 21 | + 0x8120 420 | 20 | + ---------------------- + + line 420 seems to be related to 419 which is connect to reset circuit -> watchdog + + There are two star fields implemented here. The active one can be selected in + Machine Configuration. + + a) Scramble Starfield + + 1/3 star width like scramble/galaxian, starfield code same as scramble. + This one is similar to the Konami starfield video below. + + b) Bootleg Starfield + + Guru provided schematics of a daughter-board which on a Konami bootleg replaced + custom chip 084. This starfield matches the starfield (picture) of this bootleg. + + None of the two alterntives reproduces the star field which can be observed in + the Stern videos below. + + FIXME: For an exact emulation we need a 084 at some time. + FIXME: The Konami video below is different from tutankhm. Looks like we + are missing an early board dump + + + Starfield videos .... + + Konami + https://www.youtube.com/watch?v=YwVjJtQK2n4 + + Stern + https://www.youtube.com/watch?v=g6nv6jHFP80 + https://www.youtube.com/watch?v=D_hmvFi2ehw + https://www.youtube.com/watch?v=EZNqH-JPzPM + https://www.youtube.com/watch?v=1MPvSIEIpFU + + */ + + +/***************************************************************************/ #include "emu.h" #include "includes/tutankhm.h" @@ -59,6 +92,7 @@ #include "machine/watchdog.h" #include "screen.h" #include "speaker.h" +#include "schedule.h" /************************************* @@ -134,8 +168,16 @@ void tutankhm_state::main_map(address_map &map) { map(0x0000, 0x7fff).ram().share("videoram"); map(0x8000, 0x800f).mirror(0x00f0).ram().w(m_palette, FUNC(palette_device::write8)).share("palette"); + //0x8100 -> Custom 089 D9 Pin 15 map(0x8100, 0x8100).mirror(0x000f).ram().share("scroll"); + + /* a read here produces a 1-0-1 write to line 420 (084). + * This most likely resets some sort of timer implemented by the 084 custom chip + * which would on line 419 trigger a reset. + */ + //0x8720 -> Custom 084 F3 Pin 20 map(0x8120, 0x8120).mirror(0x000f).r("watchdog", FUNC(watchdog_timer_device::reset_r)); + //0x8740 -> Custom 089 D9 Pin 11 - Unknown, not used map(0x8160, 0x8160).mirror(0x000f).portr("DSW2"); /* DSW2 (inverted bits) */ map(0x8180, 0x8180).mirror(0x000f).portr("IN0"); /* IN0 I/O: Coin slots, service, 1P/2P buttons */ map(0x81a0, 0x81a0).mirror(0x000f).portr("IN1"); /* IN1: Player 1 I/O */ @@ -145,6 +187,7 @@ void tutankhm_state::main_map(address_map &map) map(0x8300, 0x8300).mirror(0x00ff).w(FUNC(tutankhm_state::tutankhm_bankselect_w)); map(0x8600, 0x8600).mirror(0x00ff).w(FUNC(tutankhm_state::sound_on_w)); map(0x8700, 0x8700).mirror(0x00ff).w(m_timeplt_audio, FUNC(timeplt_audio_device::sound_data_w)); + map(0x8800, 0x8fff).ram(); map(0x9000, 0x9fff).bankr("bank1"); map(0xa000, 0xffff).rom(); @@ -202,6 +245,12 @@ static INPUT_PORTS_START( tutankhm ) PORT_START("DSW1") KONAMI_COINAGE_LOC(DEF_STR( Free_Play ), "No Coin B", SW1) /* "No Coin B" = coins produce sound, but no effect on coin counter */ + + PORT_START("STARS") + PORT_CONFNAME( 0x01, 0x01, "Starfield selection" ) + PORT_CONFSETTING( 0x00, "Konami HW bootleg (6MHz stars)" ) + PORT_CONFSETTING( 0x01, "Scramble implementation" ) + INPUT_PORTS_END @@ -215,10 +264,17 @@ void tutankhm_state::machine_start() { membank("bank1")->configure_entries(0, 16, memregion("maincpu")->base() + 0x10000, 0x1000); + m_star_mode = 0; + save_item(NAME(m_irq_toggle)); save_item(NAME(m_irq_enable)); - save_item(NAME(m_flip_x)); - save_item(NAME(m_flip_y)); + save_item(NAME(m_flipscreen_x)); + save_item(NAME(m_flipscreen_y)); + //rgb_t m_star_color[64]; + //std::unique_ptr m_stars; + save_item(NAME(m_stars_enabled)); + save_item(NAME(m_stars_blink_state)); + save_item(NAME(m_star_mode)); } void tutankhm_state::machine_reset() @@ -237,7 +293,7 @@ void tutankhm_state::tutankhm(machine_config &config) mainlatch.q_out_cb<1>().set_nop(); // PAY OUT - not used mainlatch.q_out_cb<2>().set(FUNC(tutankhm_state::coin_counter_2_w)); mainlatch.q_out_cb<3>().set(FUNC(tutankhm_state::coin_counter_1_w)); - mainlatch.q_out_cb<4>().set_nop(); // starfield? + mainlatch.q_out_cb<4>().set(FUNC(tutankhm_state::galaxian_stars_enable_w)); mainlatch.q_out_cb<5>().set("timeplt_audio", FUNC(timeplt_audio_device::mute_w)); mainlatch.q_out_cb<6>().set(FUNC(tutankhm_state::flip_screen_x_w)); mainlatch.q_out_cb<7>().set(FUNC(tutankhm_state::flip_screen_y_w)); @@ -245,18 +301,21 @@ void tutankhm_state::tutankhm(machine_config &config) WATCHDOG_TIMER(config, "watchdog"); /* video hardware */ - screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_refresh_hz(60); - screen.set_vblank_time(ATTOSECONDS_IN_USEC(0)); - screen.set_size(32*8, 32*8); - screen.set_visarea(0*8, 32*8-1, 2*8, 30*8-1); /* not sure about the visible area */ - screen.set_screen_update(FUNC(tutankhm_state::screen_update_tutankhm)); - screen.screen_vblank().set(FUNC(tutankhm_state::vblank_irq)); + //screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_raw(GALAXIAN_PIXEL_CLOCK, GALAXIAN_HTOTAL, GALAXIAN_HBEND, GALAXIAN_HBSTART, GALAXIAN_VTOTAL, GALAXIAN_VBEND, GALAXIAN_VBSTART); + PALETTE(config, m_palette).set_format(1, tutankhm_state::raw_to_rgb_func, 16); + + m_screen->set_screen_update(FUNC(tutankhm_state::screen_update_tutankhm)); + m_screen->screen_vblank().set(FUNC(tutankhm_state::vblank_irq)); - PALETTE(config, m_palette).set_format(palette_device::BGR_233, 16); /* sound hardware */ TIMEPLT_AUDIO(config, "timeplt_audio"); + + /* blinking frequency is determined by 555 counter with Ra=100k, Rb=10k, C=10uF */ + TIMER(config, "stars").configure_periodic(FUNC(tutankhm_state::scramble_stars_blink_timer), PERIOD_OF_555_ASTABLE(100000, 10000, 0.00001)); + } diff --git a/src/mame/includes/tutankhm.h b/src/mame/includes/tutankhm.h index 2111d73a16f..4484a4dea78 100644 --- a/src/mame/includes/tutankhm.h +++ b/src/mame/includes/tutankhm.h @@ -7,6 +7,22 @@ #include "audio/timeplt.h" #include "emupal.h" +#include "machine/timer.h" +#include "screen.h" + +static constexpr int GALAXIAN_XSCALE = 3; +static constexpr XTAL GALAXIAN_MASTER_CLOCK(18.432_MHz_XTAL); +static constexpr XTAL GALAXIAN_PIXEL_CLOCK(GALAXIAN_XSCALE*GALAXIAN_MASTER_CLOCK / 3); +static constexpr int GALAXIAN_HTOTAL = (384 * GALAXIAN_XSCALE); +static constexpr int GALAXIAN_HBEND = (0 * GALAXIAN_XSCALE); +//static constexpr int GALAXIAN_H0START = (6*GALAXIAN_XSCALE) +//static constexpr int GALAXIAN_HBSTART = (264*GALAXIAN_XSCALE) +static constexpr int GALAXIAN_H0START = (0 * GALAXIAN_XSCALE); +static constexpr int GALAXIAN_HBSTART = (256 * GALAXIAN_XSCALE); + +static constexpr int GALAXIAN_VTOTAL = (264); +static constexpr int GALAXIAN_VBEND = (16); +static constexpr int GALAXIAN_VBSTART = (224 + 16); class tutankhm_state : public driver_device { @@ -17,6 +33,7 @@ public: m_scroll(*this, "scroll"), m_maincpu(*this, "maincpu"), m_palette(*this, "palette"), + m_screen(*this, "screen"), m_timeplt_audio(*this, "timeplt_audio") { } @@ -33,9 +50,14 @@ protected: DECLARE_WRITE_LINE_MEMBER(flip_screen_y_w); virtual void machine_start() override; virtual void machine_reset() override; + uint32_t screen_update_tutankhm_bootleg(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + uint32_t screen_update_tutankhm_scramble(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); uint32_t screen_update_tutankhm(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); DECLARE_WRITE_LINE_MEMBER(vblank_irq); void main_map(address_map &map); + virtual void video_start() override; + void galaxian_palette(palette_device &palette); + static rgb_t raw_to_rgb_func(u32 raw); /* memory pointers */ required_shared_ptr m_videoram; @@ -43,8 +65,8 @@ protected: /* video-related */ tilemap_t *m_bg_tilemap; - uint8_t m_flip_x; - uint8_t m_flip_y; + uint8_t m_flipscreen_x; + uint8_t m_flipscreen_y; /* misc */ uint8_t m_irq_toggle; @@ -53,7 +75,23 @@ protected: /* devices */ required_device m_maincpu; required_device m_palette; + required_device m_screen; optional_device m_timeplt_audio; + + TIMER_DEVICE_CALLBACK_MEMBER(scramble_stars_blink_timer); + DECLARE_WRITE8_MEMBER(galaxian_stars_enable_w); + void stars_init(); + void stars_init_bootleg(); + void stars_draw_row(bitmap_rgb32 &bitmap, int maxx, int y, uint32_t star_offs); + void scramble_draw_stars(bitmap_rgb32 &bitmap, const rectangle &cliprect, int maxx); + void scramble_draw_background(bitmap_rgb32 &bitmap, const rectangle &cliprect); + + uint8_t m_star_mode; + rgb_t m_star_color[64]; + std::unique_ptr m_stars; + uint8_t m_stars_enabled; + uint8_t m_stars_blink_state; + }; #endif // MAME_INCLUDES_TUTANKHM_H diff --git a/src/mame/video/tutankhm.cpp b/src/mame/video/tutankhm.cpp index 8cd1a5a4bf6..eaf4560d316 100644 --- a/src/mame/video/tutankhm.cpp +++ b/src/mame/video/tutankhm.cpp @@ -10,7 +10,10 @@ #include "emu.h" #include "includes/tutankhm.h" +#include "video/resnet.h" +#define STAR_RNG_PERIOD ((1 << 17) - 1) +#define RGB_MAXIMUM 224 /************************************* * @@ -20,13 +23,13 @@ WRITE_LINE_MEMBER(tutankhm_state::flip_screen_x_w) { - m_flip_x = state; + m_flipscreen_x = state; } WRITE_LINE_MEMBER(tutankhm_state::flip_screen_y_w) { - m_flip_y = state; + m_flipscreen_y = state; } @@ -36,25 +39,429 @@ WRITE_LINE_MEMBER(tutankhm_state::flip_screen_y_w) * *************************************/ -uint32_t tutankhm_state::screen_update_tutankhm(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +uint32_t tutankhm_state::screen_update_tutankhm_bootleg(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { - int xorx = m_flip_x ? 255 : 0; - int xory = m_flip_y ? 255 : 0; + bitmap.fill(rgb_t::black(), cliprect); + + int xorx = m_flipscreen_x ? 255 : 0; + int xory = m_flipscreen_y ? 255 : 0; for (int y = cliprect.min_y; y <= cliprect.max_y; y++) { uint32_t *dst = &bitmap.pix32(y); - for (int x = cliprect.min_x; x <= cliprect.max_x; x++) + for (int x = cliprect.min_x / GALAXIAN_XSCALE; x <= cliprect.max_x / GALAXIAN_XSCALE; x++) { uint8_t effx = x ^ xorx; uint8_t yscroll = (effx < 192 && m_scroll.found()) ? *m_scroll : 0; uint8_t effy = (y ^ xory) + yscroll; uint8_t vrambyte = m_videoram[effy * 128 + effx / 2]; uint8_t shifted = vrambyte >> (4 * (effx % 2)); - dst[x] = m_palette->pen_color(shifted & 0x0f); + + uint8_t blink_state = m_stars_blink_state & 3; + uint8_t enab = 0; + switch (blink_state) + { + case 0: enab = 1; break; + case 1: enab = (y & 1) == 1; break; + case 2: enab = (y & 2) == 2; break; + case 3: enab = (x & 8) == 0; break; + } + //enab &= (((y>>1) ^ (x >> 3)) & 1); + + int offset = y * 384 + x + 84; + + uint8_t star = m_stars[offset % STAR_RNG_PERIOD ]; + if (m_stars_enabled && enab && (shifted & 0x02) == 0 && (star & 0x80) != 0 + && x > 63) + { + bitmap.pix32(y, GALAXIAN_XSCALE*x + 0) = m_star_color[star & 0x3f]; + bitmap.pix32(y, GALAXIAN_XSCALE*x + 1) = m_star_color[star & 0x3f]; + bitmap.pix32(y, GALAXIAN_XSCALE*x + 2) = m_star_color[star & 0x3f]; + } + + else if (shifted) + { + dst[x * GALAXIAN_XSCALE + 0] = m_palette->pen_color(shifted & 0x0f); + dst[x * GALAXIAN_XSCALE + 1] = m_palette->pen_color(shifted & 0x0f); + dst[x * GALAXIAN_XSCALE + 2] = m_palette->pen_color(shifted & 0x0f); + } } } return 0; } + +uint32_t tutankhm_state::screen_update_tutankhm_scramble(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + scramble_draw_background(bitmap, cliprect); + + int xorx = m_flipscreen_x ? 255 : 0; + int xory = m_flipscreen_y ? 255 : 0; + + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + uint32_t *dst = &bitmap.pix32(y); + + for (int x = cliprect.min_x / GALAXIAN_XSCALE; x <= cliprect.max_x / GALAXIAN_XSCALE; x++) + { + uint8_t effx = x ^ xorx; + uint8_t yscroll = (effx < 192 && m_scroll.found()) ? *m_scroll : 0; + uint8_t effy = (y ^ xory) + yscroll; + uint8_t vrambyte = m_videoram[effy * 128 + effx / 2]; + uint8_t shifted = vrambyte >> (4 * (effx % 2)); + if (shifted) + { + dst[x * GALAXIAN_XSCALE + 0] = m_palette->pen_color(shifted & 0x0f); + dst[x * GALAXIAN_XSCALE + 1] = m_palette->pen_color(shifted & 0x0f); + dst[x * GALAXIAN_XSCALE + 2] = m_palette->pen_color(shifted & 0x0f); + } + } + } + + return 0; +} + +uint32_t tutankhm_state::screen_update_tutankhm(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + ioport_port *port = ioport("STARS"); + if (port != nullptr) + { + int newset = port->read(); + if (newset != m_star_mode) + { + m_star_mode = newset; + switch (newset) + { + case 0: + stars_init_bootleg(); + break; + case 1: + stars_init(); + break; + } + } + } + + if (m_star_mode) + return screen_update_tutankhm_scramble(screen, bitmap, cliprect); + else + return screen_update_tutankhm_bootleg(screen, bitmap, cliprect); + +} + +/************************************* + * + * Copied from galaxian.cpp video code + * + *************************************/ + +rgb_t tutankhm_state::raw_to_rgb_func(u32 raw) +{ + static const int rgb_resistances[3] = { 1000, 470, 220 }; + /* + Sprite/tilemap colors are mapped through a color PROM as follows: + + bit 7 -- 220 ohm resistor -- BLUE + -- 470 ohm resistor -- BLUE + -- 220 ohm resistor -- GREEN + -- 470 ohm resistor -- GREEN + -- 1 kohm resistor -- GREEN + -- 220 ohm resistor -- RED + -- 470 ohm resistor -- RED + bit 0 -- 1 kohm resistor -- RED + + Note that not all boards have this configuration. Namco PCBs may + have 330 ohm resistors instead of 220, but the default setup has + also been used by Namco. + + In parallel with these resistors are a pair of 150 ohm and 100 ohm + resistors on each R,G,B component that are connected to the star + generator. + + And in parallel with the whole mess are a set of 100 ohm resistors + on each R,G,B component that are enabled when a shell/missile is + enabled. + + When computing weights, we use RGB_MAXIMUM as the maximum to give + headroom for stars and shells/missiles. This is not fully accurate, + but if we included all possible sources in parallel, the brightness + of the main game would be very low to allow for all the oversaturation + of the stars and shells/missiles. + */ + double rweights[3], gweights[3], bweights[2]; + compute_resistor_weights(0, RGB_MAXIMUM, -1.0, + 3, &rgb_resistances[0], rweights, 470, 0, + 3, &rgb_resistances[0], gweights, 470, 0, + 2, &rgb_resistances[1], bweights, 470, 0); + + // decode the palette first + + uint8_t bit0, bit1, bit2; + + // red component + bit0 = BIT(raw, 0); + bit1 = BIT(raw, 1); + bit2 = BIT(raw, 2); + int const r = combine_weights(rweights, bit0, bit1, bit2); + + // green component + bit0 = BIT(raw, 3); + bit1 = BIT(raw, 4); + bit2 = BIT(raw, 5); + int const g = combine_weights(gweights, bit0, bit1, bit2); + + // blue component + bit0 = BIT(raw, 6); + bit1 = BIT(raw, 7); + int const b = combine_weights(bweights, bit0, bit1); + + return rgb_t(r, g, b); +} + +void tutankhm_state::galaxian_palette(palette_device &palette) +{ + /* + The maximum sprite/tilemap resistance is ~130 Ohms with all RGB + outputs enabled (1/(1/1000 + 1/470 + 1/220)). Since we normalized + to RGB_MAXIMUM, this maps RGB_MAXIMUM -> 130 Ohms. + + The stars are at 150 Ohms for the LSB, and 100 Ohms for the MSB. + This means the 3 potential values are: + + 150 Ohms -> RGB_MAXIMUM * 130 / 150 + 100 Ohms -> RGB_MAXIMUM * 130 / 100 + 60 Ohms -> RGB_MAXIMUM * 130 / 60 + + Since we can't saturate that high, we instead approximate this + by compressing the values proportionally into the 194->255 range. + */ + int const minval = RGB_MAXIMUM * 130 / 150; + int const midval = RGB_MAXIMUM * 130 / 100; + int const maxval = RGB_MAXIMUM * 130 / 60; + + // compute the values for each of 4 possible star values + uint8_t const starmap[4]{ + 0, + minval, + minval + (255 - minval) * (midval - minval) / (maxval - minval), + 255 }; + + // generate the colors for the stars + for (int i = 0; i < 64; i++) + { + uint8_t bit0, bit1; + + // bit 5 = red @ 150 Ohm, bit 4 = red @ 100 Ohm + bit0 = BIT(i, 5); + bit1 = BIT(i, 4); + int const r = starmap[(bit1 << 1) | bit0]; + + // bit 3 = green @ 150 Ohm, bit 2 = green @ 100 Ohm + bit0 = BIT(i, 3); + bit1 = BIT(i, 2); + int const g = starmap[(bit1 << 1) | bit0]; + + // bit 1 = blue @ 150 Ohm, bit 0 = blue @ 100 Ohm + bit0 = BIT(i, 1); + bit1 = BIT(i, 0); + int const b = starmap[(bit1 << 1) | bit0]; + + // set the RGB color + m_star_color[i] = rgb_t(r, g, b); + } + +} + + +void tutankhm_state::video_start() +{ + + /* initialize globals */ + m_flipscreen_x = 0; + m_flipscreen_y = 0; + + m_stars_enabled = 0; + m_stars_blink_state = 0; + + /* initialize stars */ + if (m_star_mode) + stars_init(); + else + stars_init_bootleg(); + + galaxian_palette(*m_palette); +} + +void tutankhm_state::stars_init_bootleg() +{ + uint32_t shiftreg; + int i; + + /* reset the blink and enabled states */ + m_stars_enabled = false; + m_stars_blink_state = 0; + + /* precalculate the RNG */ + m_stars = std::make_unique(STAR_RNG_PERIOD); + shiftreg = 0; + for (i = 0; i < STAR_RNG_PERIOD; i++) + { + int newbit = ((shiftreg >> 12) ^ ~shiftreg) & 1; + + /* stars are enabled if the upper 8 bits are 1 and the new bit is 0 */ + int enabled = ((shiftreg & 0x1fe00) == 0x1fe00) && (newbit == 0); + //int enabled = ((shiftreg & 0x1fe01) == 0x1fe00); // <- scramble + + /* color comes from the 6 bits below the top 8 bits */ + int color = (~shiftreg & 0x1f8) >> 3; + + /* store the color value in the low 6 bits and the enable in the upper bit */ + m_stars[i] = color | (enabled << 7); + + /* the LFSR is fed based on the XOR of bit 12 and the inverse of bit 0 */ + shiftreg = (shiftreg >> 1) | (newbit << 16); + } +} + +void tutankhm_state::stars_init() +{ + uint32_t shiftreg; + int i; + + + /* precalculate the RNG */ + m_stars = std::make_unique(STAR_RNG_PERIOD); + shiftreg = 0; + for (i = 0; i < STAR_RNG_PERIOD; i++) + { + const uint8_t shift = 12; + /* stars are enabled if the upper 8 bits are 1 and the low bit is 0 */ + int enabled = ((shiftreg & 0x1fe01) == 0x1fe00); + + /* color comes from the 6 bits below the top 8 bits */ + int color = (~shiftreg & 0x1f8) >> 3; + + /* store the color value in the low 6 bits and the enable in the upper bit */ + m_stars[i] = color | (enabled << 7); + + /* the LFSR is fed based on the XOR of bit 12 and the inverse of bit 0 */ + //shiftreg = (shiftreg >> 1) | ((((shiftreg >> 12) ^ ~shiftreg) & 1) << 16); + shiftreg = (shiftreg >> 1) | ((((shiftreg >> shift) ^ ~shiftreg) & 1) << 16); + } +} + + + +/************************************* + * + * Star blinking + * + *************************************/ + +TIMER_DEVICE_CALLBACK_MEMBER(tutankhm_state::scramble_stars_blink_timer) +{ + m_stars_blink_state++; +} + +void tutankhm_state::stars_draw_row(bitmap_rgb32 &bitmap, int maxx, int y, uint32_t star_offs) +{ + int x; + uint8_t flipxor = (m_flipscreen_x ? 0xC0 : 0x00); + + /* ensure our star offset is valid */ + star_offs %= STAR_RNG_PERIOD; + + /* iterate over the specified number of 6MHz pixels */ + for (x = 0; x < maxx; x++) + { + uint8_t h8q = ((x>>3) & 1) ^ 1; // H8 signal is inverted. + /* stars are suppressed unless V1 ^ H8 == 1 */ + int enable_star = (y ^ h8q) & 1; + uint8_t star; + + uint8_t blink_state = m_stars_blink_state & 3; + uint8_t enab = 0; + switch (blink_state) + { + case 0: enab = 1; break; + case 1: enab = (y & 1) == 1; break; + case 2: enab = (y & 2) == 2; break; + case 3: enab = h8q; break; // H8 signal is inverted. + } + + enable_star &= (enab && ((x & 0xC0) ^ flipxor) != 0xC0); + + /* + The RNG clock is the master clock (18MHz) ANDed with the pixel clock (6MHz). + The divide-by-3 circuit that produces the pixel clock generates a square wave + with a 2/3 duty cycle, so the result of the AND generates a clock like this: + _ _ _ _ _ _ _ _ + MASTER: _| |_| |_| |_| |_| |_| |_| |_| | + _______ _______ ______ + PIXEL: _| |___| |___| + _ _ _ _ _ _ + RNG: _| |_| |_____| |_| |_____| |_| | + + Thus for each pixel, there are 3 master clocks and 2 RNG clocks, and the RNG + is clocked asymmetrically. To simulate this, we expand the horizontal screen + size by 3 and handle the first RNG clock with one pixel and the second RNG + clock with two pixels. + */ + + /* first RNG clock: one pixel */ + star = m_stars[star_offs++]; + if (star_offs >= STAR_RNG_PERIOD) + star_offs = 0; + if (enable_star && (star & 0x80) != 0) + bitmap.pix32(y, GALAXIAN_XSCALE*x + 0) = m_star_color[star & 0x3f]; + + /* second RNG clock: two pixels */ + star = m_stars[star_offs++]; + if (star_offs >= STAR_RNG_PERIOD) + star_offs = 0; + if (enable_star && (star & 0x80) != 0) + { + bitmap.pix32(y, GALAXIAN_XSCALE*x + 1) = m_star_color[star & 0x3f]; + bitmap.pix32(y, GALAXIAN_XSCALE*x + 2) = m_star_color[star & 0x3f]; + } + } +} + +void tutankhm_state::scramble_draw_stars(bitmap_rgb32 &bitmap, const rectangle &cliprect, int maxx) +{ + /* update the star origin to the current frame */ + //stars_update_origin(); + + /* render stars if enabled */ + if (m_stars_enabled) + { + int y; + + /* iterate over scanlines */ + for (y = cliprect.min_y; y <= cliprect.max_y; y++) + { + stars_draw_row(bitmap, maxx, y, y * 512); + } + } +} + + +void tutankhm_state::scramble_draw_background(bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + /* blue background - 390 ohm resistor */ + bitmap.fill(rgb_t::black(), cliprect); + + scramble_draw_stars(bitmap, cliprect, 256); +} + +WRITE8_MEMBER(tutankhm_state::galaxian_stars_enable_w) +{ + if ((m_stars_enabled ^ data) & 0x01) + { +// m_screen->update_now(); + m_screen->update_partial(m_screen->vpos()); + } + + m_stars_enabled = data & 0x01; +} -- cgit v1.2.3