// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller
/***************************************************************************
Atari 400/800
GTIA graphics television interface adapter
Juergen Buchmueller, June 1998
***************************************************************************/
#include "emu.h"
#include "video/gtia.h"
#include "screen.h"
#define GTIA_P0 0x01
#define GTIA_P1 0x02
#define GTIA_P2 0x04
#define GTIA_P3 0x08
#define GTIA_M0 0x10
#define GTIA_M1 0x20
#define GTIA_M2 0x40
#define GTIA_M3 0x80
#define GTIA_HWIDTH 48 /* total characters per line */
#define GTIA_TRIGGER 0x04
#define CHECK_GRACTL 0
#define VERBOSE 0
#define PFD 0x00 /* 00000000b playfield default color */
#define PBK 0x00 /* 00000000b playfield background */
#define PF0 0x01 /* 00000001b playfield color #0 */
#define PF1 0x02 /* 00000010b playfield color #1 */
#define PF2 0x04 /* 00000100b playfield color #2 */
#define PF3 0x08 /* 00001000b playfield color #3 */
#define PL0 0x11 /* 00010001b player #0 */
#define PL1 0x12 /* 00010010b player #1 */
#define PL2 0x14 /* 00010100b player #2 */
#define PL3 0x18 /* 00011000b player #3 */
#define MI0 0x21 /* 00100001b missile #0 */
#define MI1 0x22 /* 00100010b missile #1 */
#define MI2 0x24 /* 00100100b missile #2 */
#define MI3 0x28 /* 00101000b missile #3 */
#define T00 0x40 /* 01000000b text mode pixels 00 */
#define P000 0x48 /* 01001000b player #0 pixels 00 */
#define P100 0x4a /* 01001010b player #1 pixels 00 */
#define P200 0x4c /* 01001100b player #2 pixels 00 */
#define P300 0x4e /* 01001110b player #3 pixels 00 */
#define P400 0x4f /* 01001111b missiles pixels 00 */
#define T01 0x50 /* 01010000b text mode pixels 01 */
#define P001 0x58 /* 01011000b player #0 pixels 01 */
#define P101 0x5a /* 01011010b player #1 pixels 01 */
#define P201 0x5c /* 01011100b player #2 pixels 01 */
#define P301 0x5e /* 01011110b player #3 pixels 01 */
#define P401 0x5f /* 01011111b missiles pixels 01 */
#define T10 0x60 /* 01100000b text mode pixels 10 */
#define P010 0x68 /* 01101000b player #0 pixels 10 */
#define P110 0x6a /* 01101010b player #1 pixels 10 */
#define P210 0x6c /* 01101100b player #2 pixels 10 */
#define P310 0x6e /* 01101110b player #3 pixels 10 */
#define P410 0x6f /* 01101111b missiles pixels 10 */
#define T11 0x70 /* 01110000b text mode pixels 11 */
#define P011 0x78 /* 01111000b player #0 pixels 11 */
#define P111 0x7a /* 01111010b player #1 pixels 11 */
#define P211 0x7c /* 01111100b player #2 pixels 11 */
#define P311 0x7e /* 01111110b player #3 pixels 11 */
#define P411 0x7f /* 01111111b missiles pixels 11 */
#define G00 0x80 /* 10000000b hires gfx pixels 00 */
#define G01 0x90 /* 10010000b hires gfx pixels 01 */
#define G10 0xa0 /* 10100000b hires gfx pixels 10 */
#define G11 0xb0 /* 10110000b hires gfx pixels 11 */
#define GT1 0xc0 /* 11000000b gtia mode 1 */
#define GT2 0xd0 /* 11010000b gtia mode 2 */
#define GT3 0xe0 /* 11100000b gtia mode 3 */
#define ILL 0xfe /* 11111110b illegal priority */
#define EOR 0xff /* 11111111b EOR mode color */
#define LUM 0x0f /* 00001111b luminance bits */
#define HUE 0xf0 /* 11110000b hue bits */
/**********************************************
* split a color into hue and luminance values
**********************************************/
#define SPLIT_HUE(data, hue) \
hue = (data & HUE)
#define SPLIT_LUM(data, lum) \
lum = (data & LUM)
/**********************************************
* set both color clocks equal for one color
**********************************************/
#define SETCOL_B(o, d) \
m_color_lookup[o] = ((d) << 8) | (d)
/**********************************************
* set left color clock for one color
**********************************************/
#define SETCOL_L(o, d) \
*((uint8_t*)&m_color_lookup[o] + 0) = d
/**********************************************
* set right color clock for one color
**********************************************/
#define SETCOL_R(o, d) \
*((uint8_t*)&m_color_lookup[o] + 1) = d
// devices
DEFINE_DEVICE_TYPE(ATARI_GTIA, gtia_device, "gtia", "Atari GTIA")
//-------------------------------------------------
// upd7220_device - constructor
//-------------------------------------------------
gtia_device::gtia_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, ATARI_GTIA, tag, owner, clock)
, m_region(GTIA_NTSC)
, m_read_cb(*this)
, m_write_cb(*this)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void gtia_device::device_start()
{
m_read_cb.resolve();
m_write_cb.resolve();
save_item(NAME(m_r.m0pf));
save_item(NAME(m_r.m1pf));
save_item(NAME(m_r.m2pf));
save_item(NAME(m_r.m3pf));
save_item(NAME(m_r.p0pf));
save_item(NAME(m_r.p1pf));
save_item(NAME(m_r.p2pf));
save_item(NAME(m_r.p3pf));
save_item(NAME(m_r.m0pl));
save_item(NAME(m_r.m1pl));
save_item(NAME(m_r.m2pl));
save_item(NAME(m_r.m3pl));
save_item(NAME(m_r.p0pl));
save_item(NAME(m_r.p1pl));
save_item(NAME(m_r.p2pl));
save_item(NAME(m_r.p3pl));
save_item(NAME(m_r.but));
save_item(NAME(m_r.pal));
save_item(NAME(m_r.gtia15));
save_item(NAME(m_r.gtia16));
save_item(NAME(m_r.gtia17));
save_item(NAME(m_r.gtia18));
save_item(NAME(m_r.gtia19));
save_item(NAME(m_r.gtia1a));
save_item(NAME(m_r.gtia1b));
save_item(NAME(m_r.gtia1c));
save_item(NAME(m_r.gtia1d));
save_item(NAME(m_r.gtia1e));
save_item(NAME(m_r.cons));
save_item(NAME(m_w.hposp0));
save_item(NAME(m_w.hposp1));
save_item(NAME(m_w.hposp2));
save_item(NAME(m_w.hposp3));
save_item(NAME(m_w.hposm0));
save_item(NAME(m_w.hposm1));
save_item(NAME(m_w.hposm2));
save_item(NAME(m_w.hposm3));
save_item(NAME(m_w.sizep0));
save_item(NAME(m_w.sizep1));
save_item(NAME(m_w.sizep2));
save_item(NAME(m_w.sizep3));
save_item(NAME(m_w.sizem));
save_item(NAME(m_w.grafp0));
save_item(NAME(m_w.grafp1));
save_item(NAME(m_w.grafp2));
save_item(NAME(m_w.grafp3));
save_item(NAME(m_w.grafm));
save_item(NAME(m_w.colpm0));
save_item(NAME(m_w.colpm1));
save_item(NAME(m_w.colpm2));
save_item(NAME(m_w.colpm3));
save_item(NAME(m_w.colpf0));
save_item(NAME(m_w.colpf1));
save_item(NAME(m_w.colpf2));
save_item(NAME(m_w.colpf3));
save_item(NAME(m_w.colbk));
save_item(NAME(m_w.prior));
save_item(NAME(m_w.vdelay));
save_item(NAME(m_w.gractl));
save_item(NAME(m_w.hitclr));
save_item(NAME(m_w.cons));
save_item(NAME(m_h.grafp0));
save_item(NAME(m_h.grafp1));
save_item(NAME(m_h.grafp2));
save_item(NAME(m_h.grafp3));
save_item(NAME(m_h.grafm0));
save_item(NAME(m_h.grafm1));
save_item(NAME(m_h.grafm2));
save_item(NAME(m_h.grafm3));
save_item(NAME(m_h.hitclr_frames));
save_item(NAME(m_h.sizem));
save_item(NAME(m_h.usedp));
save_item(NAME(m_h.usedm0));
save_item(NAME(m_h.usedm1));
save_item(NAME(m_h.usedm2));
save_item(NAME(m_h.usedm3));
save_item(NAME(m_h.vdelay_m0));
save_item(NAME(m_h.vdelay_m1));
save_item(NAME(m_h.vdelay_m2));
save_item(NAME(m_h.vdelay_m3));
save_item(NAME(m_h.vdelay_p0));
save_item(NAME(m_h.vdelay_p1));
save_item(NAME(m_h.vdelay_p2));
save_item(NAME(m_h.vdelay_p3));
save_item(NAME(m_lumpf1));
save_item(NAME(m_huepm0));
save_item(NAME(m_huepm1));
save_item(NAME(m_huepm2));
save_item(NAME(m_huepm3));
save_item(NAME(m_huepm4));
save_item(NAME(m_huepf2));
save_item(NAME(m_huebk));
save_item(NAME(m_color_lookup));
machine().save().register_postload(save_prepost_delegate(FUNC(gtia_device::gtia_postload), this));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void gtia_device::device_reset()
{
memset(&m_r, 0, sizeof(m_r));
memset(&m_h, 0, sizeof(m_h));
memset(m_color_lookup, 0, sizeof(m_color_lookup));
m_lumpf1 = 0;
/* reset the GTIA read/write/helper registers */
for (int i = 0; i < 32; i++)
write(machine().dummy_space(), i, 0);
if (is_ntsc())
m_r.pal = 0xff;
else
m_r.pal = 0xf1;
m_r.gtia15 = 0xff;
m_r.gtia16 = 0xff;
m_r.gtia17 = 0xff;
m_r.gtia18 = 0xff;
m_r.gtia19 = 0xff;
m_r.gtia1a = 0xff;
m_r.gtia1b = 0xff;
m_r.gtia1c = 0xff;
m_r.gtia1d = 0xff;
m_r.gtia1e = 0xff;
m_r.cons = 0x07; /* console keys */
SETCOL_B(ILL, 0x3e); /* bright red */
SETCOL_B(EOR, 0xff); /* yellow */
m_huepm0 = 0;
m_huepm1 = 0;
m_huepm2 = 0;
m_huepm3 = 0;
m_huepm4 = 0;
m_huepf2 = 0;
m_huebk = 0;
}
int gtia_device::is_ntsc()
{
return m_region == GTIA_NTSC;
}
void gtia_device::button_interrupt(int button_count, uint8_t button_port)
{
/* specify buttons relevant to this Atari variant */
for (int i = 0; i < button_count; i++)
{
if ((m_w.gractl & GTIA_TRIGGER) == 0)
m_r.but[i] = 1;
m_r.but[i] &= BIT(button_port, i);
}
/* button registers for xl/xe */
if (button_count == 2)
{
m_r.but[2] = 1; /* not used on xl/xe */
m_r.but[3] = 0; /* 1 if external cartridge is inserted */
}
}
/**************************************************************
*
* Read GTIA hardware registers
*
**************************************************************/
READ8_MEMBER( gtia_device::read )
{
switch (offset & 31)
{
case 0: return m_r.m0pf;
case 1: return m_r.m1pf;
case 2: return m_r.m2pf;
case 3: return m_r.m3pf;
case 4: return m_r.p0pf;
case 5: return m_r.p1pf;
case 6: return m_r.p2pf;
case 7: return m_r.p3pf;
case 8: return m_r.m0pl;
case 9: return m_r.m1pl;
case 10: return m_r.m2pl;
case 11: return m_r.m3pl;
case 12: return m_r.p0pl;
case 13: return m_r.p1pl;
case 14: return m_r.p2pl;
case 15: return m_r.p3pl;
case 16: return m_r.but[0];
case 17: return m_r.but[1];
case 18: return m_r.but[2];
case 19: return m_r.but[3];
case 20: return m_r.pal;
case 21: return m_r.gtia15;
case 22: return m_r.gtia16;
case 23: return m_r.gtia17;
case 24: return m_r.gtia18;
case 25: return m_r.gtia19;
case 26: return m_r.gtia1a;
case 27: return m_r.gtia1b;
case 28: return m_r.gtia1c;
case 29: return m_r.gtia1d;
case 30: return m_r.gtia1e;
case 31:
m_r.cons = !m_read_cb.isnull() ? (m_read_cb(0) & 0x0f) : 0x00;
return m_r.cons;
}
return 0xff;
}
void gtia_device::recalc_p0()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_PLAYER) == 0 ||
#endif
m_w.grafp0[m_h.vdelay_p0] == 0 || m_w.hposp0 >= 224 )
{
m_h.grafp0 = 0;
m_h.usedp &= ~0x10;
}
else
{
m_h.grafp0 = m_w.grafp0[m_h.vdelay_p0];
m_h.usedp |= 0x10;
}
}
void gtia_device::recalc_p1()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_PLAYER) == 0 ||
#endif
m_w.grafp1[m_h.vdelay_p1] == 0 || m_w.hposp1 >= 224 )
{
m_h.grafp1 = 0;
m_h.usedp &= ~0x20;
}
else
{
m_h.grafp1 = m_w.grafp1[m_h.vdelay_p1];
m_h.usedp |= 0x20;
}
}
void gtia_device::recalc_p2()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_PLAYER) == 0 ||
#endif
m_w.grafp2[m_h.vdelay_p2] == 0 || m_w.hposp2 >= 224 )
{
m_h.grafp2 = 0;
m_h.usedp &= ~0x40;
}
else
{
m_h.grafp2 = m_w.grafp2[m_h.vdelay_p2];
m_h.usedp |= 0x40;
}
}
void gtia_device::recalc_p3()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_PLAYER) == 0 ||
#endif
m_w.grafp3[m_h.vdelay_p3] == 0 || m_w.hposp3 >= 224 )
{
m_h.grafp3 = 0;
m_h.usedp &= ~0x80;
}
else
{
m_h.grafp3 = m_w.grafp3[m_h.vdelay_p3];
m_h.usedp |= 0x80;
}
}
void gtia_device::recalc_m0()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_MISSILE) == 0 ||
#endif
(m_w.grafm[m_h.vdelay_m0] & 0x03) == 0 || m_w.hposm0 >= 224 )
{
m_h.grafm0 = 0;
m_h.usedm0 = 0;
}
else
{
m_h.grafm0 = (m_w.grafm[m_h.vdelay_m0] << 6) & 0xc0;
m_h.usedm0 = (m_w.prior & 0x10) ? 0x08 : 0x10;
}
}
void gtia_device::recalc_m1()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_MISSILE) == 0 ||
#endif
(m_w.grafm[m_h.vdelay_m1] & 0x0c) == 0 || m_w.hposm1 >= 224 )
{
m_h.grafm1 = 0;
m_h.usedm1 = 0;
}
else
{
m_h.grafm1 = (m_w.grafm[m_h.vdelay_m1] << 4) & 0xc0;
m_h.usedm1 = (m_w.prior & 0x10) ? 0x08 : 0x20;
}
}
void gtia_device::recalc_m2()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_MISSILE) == 0 ||
#endif
(m_w.grafm[m_h.vdelay_m2] & 0x30) == 0 || m_w.hposm2 >= 224 )
{
m_h.grafm2 = 0;
m_h.usedm2 = 0;
}
else
{
m_h.grafm2 = (m_w.grafm[m_h.vdelay_m2] << 2) & 0xc0;
m_h.usedm2 = (m_w.prior & 0x10) ? 0x08 : 0x40;
}
}
void gtia_device::recalc_m3()
{
if (
#if CHECK_GRACTL
(m_w.gractl & GTIA_MISSILE) == 0 ||
#endif
(m_w.grafm[m_h.vdelay_m3] & 0xc0) == 0 || m_w.hposm3 >= 224)
{
m_h.grafm3 = 0;
m_h.usedm3 = 0;
}
else
{
m_h.grafm3 = (m_w.grafm[m_h.vdelay_m3] << 0) & 0xc0;
m_h.usedm3 = (m_w.prior & 0x10) ? 0x08 : 0x80;
}
}
WRITE8_MEMBER( gtia_device::write )
{
/* used for mixing hue/lum of different colors */
// static uint8_t lumpm0=0,lumpm1=0,lumpm2=0,lumpm3=0,lumpm4=0;
// static uint8_t lumpf2=0;
// static uint8_t lumbk= 0;
// static uint8_t huepf1=0;
switch (offset & 31)
{
case 0:
m_w.hposp0 = data;
recalc_p0();
break;
case 1:
m_w.hposp1 = data;
recalc_p1();
break;
case 2:
m_w.hposp2 = data;
recalc_p2();
break;
case 3:
m_w.hposp3 = data;
recalc_p3();
break;
case 4:
m_w.hposm0 = data;
recalc_m0();
break;
case 5:
m_w.hposm1 = data;
recalc_m1();
break;
case 6:
m_w.hposm2 = data;
recalc_m2();
break;
case 7:
m_w.hposm3 = data;
recalc_m3();
break;
case 8:
data &= 3;
m_w.sizep0 = data;
recalc_p0();
break;
case 9:
data &= 3;
m_w.sizep1 = data;
recalc_p1();
break;
case 10:
data &= 3;
m_w.sizep2 = data;
recalc_p2();
break;
case 11:
data &= 3;
m_w.sizep3 = data;
recalc_p3();
break;
case 12:
data &= 3;
m_w.sizem = data;
recalc_m0();
recalc_m1();
recalc_m2();
recalc_m3();
break;
case 13:
m_w.grafp0[0] = data;
recalc_p0();
break;
case 14:
m_w.grafp1[0] = data;
recalc_p1();
break;
case 15:
m_w.grafp2[0] = data;
recalc_p2();
break;
case 16:
m_w.grafp3[0] = data;
recalc_p3();
break;
case 17:
m_w.grafm[0] = data;
recalc_m0();
recalc_m1();
recalc_m2();
recalc_m3();
break;
case 18: /* color for player/missile #0 */
if (data == m_w.colpm0)
break;
m_w.colpm0 = data;
if (VERBOSE)
logerror("atari colpm0 $%02x\n", data);
SETCOL_B(PL0, data); /* set player 0 color */
SETCOL_B(MI0, data); /* set missile 0 color */
SETCOL_B(GT2, data); /* set GTIA mode 2 color 0 */
SETCOL_B(P000, data); /* set player 0 both pixels 0 */
SETCOL_L(P001, data); /* set player 0 left pixel 0 */
SETCOL_R(P010, data); /* set player 0 right pixel 0 */
SPLIT_HUE(data, m_huepm0);
data = m_huepm0 | m_lumpf1;
SETCOL_R(P001, data); /* set player 0 right pixel 1 */
SETCOL_L(P010, data); /* set player 0 left pixel 1 */
SETCOL_B(P011, data); /* set player 0 both pixels 1 */
break;
case 19: /* color for player/missile #1 */
if (data == m_w.colpm1)
break;
m_w.colpm1 = data;
if (VERBOSE)
logerror("atari colpm1 $%02x\n", data);
SETCOL_B(PL1, data); /* set player color 1 */
SETCOL_B(MI1, data); /* set missile color 1 */
SETCOL_B(GT2+1, data); /* set GTIA mode 2 color 1 */
SETCOL_B(P100, data); /* set player 1 both pixels 0 */
SETCOL_L(P101, data); /* set player 1 left pixel 0 */
SETCOL_R(P110, data); /* set player 1 right pixel 0 */
SPLIT_HUE(data, m_huepm1);
data = m_huepm1 | m_lumpf1;
SETCOL_R(P101, data); /* set player 1 right pixel 1 */
SETCOL_L(P110, data); /* set player 1 left pixel 1 */
SETCOL_B(P111, data); /* set player 1 both pixels 1 */
break;
case 20: /* color for player/missile #2 */
if (data == m_w.colpm2)
break;
m_w.colpm2 = data;
if (VERBOSE)
logerror("atari colpm2 $%02x\n", data);
SETCOL_B(PL2, data); /* set player 2 color */
SETCOL_B(MI2, data); /* set missile 2 color */
SETCOL_B(GT2+2, data); /* set GTIA mode 2 color 2 */
SETCOL_B(P200, data); /* set player 2 both pixels 0 */
SETCOL_L(P201, data); /* set player 2 left pixel 0 */
SETCOL_R(P210, data); /* set player 2 right pixel 0 */
SPLIT_HUE(data, m_huepm2);
data = m_huepm2 | m_lumpf1;
SETCOL_R(P201, data); /* set player 2 right pixel 1 */
SETCOL_L(P210, data); /* set player 2 left pixel 1 */
SETCOL_B(P211, data); /* set player 2 both pixels 1 */
break;
case 21: /* color for player/missile #3 */
if (data == m_w.colpm3)
break;
m_w.colpm3 = data;
if (VERBOSE)
logerror("atari colpm3 $%02x\n", data);
SETCOL_B(PL3, data); /* set player 3 color */
SETCOL_B(MI3, data); /* set missile 3 color */
SETCOL_B(GT2+3, data); /* set GTIA mode 2 color 3 */
SETCOL_B(P300, data); /* set player 3 both pixels 0 */
SETCOL_L(P301, data); /* set player 3 left pixel 0 */
SETCOL_R(P310, data); /* set player 3 right pixel 0 */
SPLIT_HUE(data, m_huepm3);
data = m_huepm3 | m_lumpf1;
SETCOL_R(P301, data); /* set player 3 right pixel 1 */
SETCOL_L(P310, data); /* set player 3 left pixel 1 */
SETCOL_B(P311, data); /* set player 3 both pixels 1 */
break;
case 22: /* playfield color #0 */
if (data == m_w.colpf0)
break;
m_w.colpf0 = data;
if (VERBOSE)
logerror("atari colpf0 $%02x\n", data);
SETCOL_B(PF0, data); /* set playfield 0 color */
SETCOL_B(GT2+4, data); /* set GTIA mode 2 color 4 */
break;
case 23: /* playfield color #1 */
if (data == m_w.colpf1)
break;
m_w.colpf1 = data;
if (VERBOSE)
logerror("atari colpf1 $%02x\n", data);
SETCOL_B(PF1, data); /* set playfield 1 color */
SETCOL_B(GT2+5, data); /* set GTIA mode 2 color 5 */
SPLIT_LUM(data, m_lumpf1);
data = m_huepf2 | m_lumpf1;
SETCOL_R(T01, data); /* set text mode right pixel 1 */
SETCOL_L(T10, data); /* set text mode left pixel 1 */
SETCOL_B(T11, data); /* set text mode both pixels 1 */
data = m_huebk | m_lumpf1;
SETCOL_R(G01, data); /* set graphics mode right pixel 1 */
SETCOL_L(G10, data); /* set graphics mode left pixel 1 */
SETCOL_B(G11, data); /* set graphics mode both pixels 1 */
data = m_huepm0 | m_lumpf1;
SETCOL_R(P001, data); /* set player 0 right pixel 1 */
SETCOL_L(P010, data); /* set player 0 left pixel 1 */
SETCOL_B(P011, data); /* set player 0 both pixels 1 */
data = m_huepm1 | m_lumpf1;
SETCOL_R(P101, data); /* set player 1 right pixel 1 */
SETCOL_L(P110, data); /* set player 1 left pixel 1 */
SETCOL_B(P111, data); /* set player 1 both pixels 1 */
data = m_huepm2 | m_lumpf1;
SETCOL_R(P201, data); /* set player 2 right pixel 1 */
SETCOL_L(P210, data); /* set player 2 left pixel 1 */
SETCOL_B(P211, data); /* set player 2 both pixels 1 */
data = m_huepm3 | m_lumpf1;
SETCOL_R(P301, data); /* set player 3 right pixel 1 */
SETCOL_L(P310, data); /* set player 3 left pixel 1 */
SETCOL_B(P311, data); /* set player 3 both pixels 1 */
data = m_huepm4 | m_lumpf1;
SETCOL_R(P401, data); /* set missiles right pixel 1 */
SETCOL_L(P410, data); /* set missiles left pixel 1 */
SETCOL_B(P411, data); /* set missiles both pixels 1 */
break;
case 24: /* playfield color #2 */
if (data == m_w.colpf2)
break;
m_w.colpf2 = data;
if (VERBOSE)
logerror("atari colpf2 $%02x\n", data);
SETCOL_B(PF2, data); /* set playfield color 2 */
SETCOL_B(GT2+6, data); /* set GTIA mode 2 color 6 */
SETCOL_B(T00, data); /* set text mode both pixels 0 */
SETCOL_L(T01, data); /* set text mode left pixel 0 */
SETCOL_R(T10, data); /* set text mode right pixel 0 */
SPLIT_HUE(data, m_huepf2);
data = m_huepf2 | m_lumpf1;
SETCOL_R(T01, data); /* set text mode right pixel 1 */
SETCOL_L(T10, data); /* set text mode left pixel 1 */
SETCOL_B(T11, data); /* set text mode both pixels 1 */
break;
case 25: /* playfield color #3 */
if (data == m_w.colpf3)
break;
m_w.colpf3 = data;
if (VERBOSE)
logerror("atari colpf3 $%02x\n", data);
SETCOL_B(PF3, data); /* set playfield color 3 */
SETCOL_B(GT2+7, data); /* set GTIA mode 2 color 7 */
SETCOL_B(P400, data); /* set p/m xor mode both pixels 0 */
SETCOL_L(P401, data); /* set p/m xor mode left pixel 0 */
SETCOL_R(P410, data); /* set p/m xor mode right pixel 0 */
SPLIT_HUE(data, m_huepm4);
data = m_huepm4 | m_lumpf1;
SETCOL_R(P401, data); /* set p/m xor mode right pixel 1 */
SETCOL_L(P410, data); /* set p/m xor mode left pixel 1 */
SETCOL_B(P411, data); /* set p/m xor mode both pixels 1 */
break;
case 26: /* playfield background */
if (data == m_w.colbk)
break;
m_w.colbk = data;
if (VERBOSE)
logerror("atari colbk $%02x\n", data);
SETCOL_B(PBK, data); /* set background color */
SETCOL_B(GT2+8, data); /* set GTIA mode 2 color 8 */
SETCOL_B(GT2+9, data); /* set GTIA mode 2 color 9 */
SETCOL_B(GT2+10, data); /* set GTIA mode 2 color 10 */
SETCOL_B(GT2+11, data); /* set GTIA mode 2 color 11 */
SETCOL_B(GT2+12, data); /* set GTIA mode 2 color 12 */
SETCOL_B(GT2+13, data); /* set GTIA mode 2 color 13 */
SETCOL_B(GT2+14, data); /* set GTIA mode 2 color 14 */
SETCOL_B(GT2+15, data); /* set GTIA mode 2 color 15 */
SETCOL_B(G00, data); /* set 2 color graphics both pixels 0 */
SETCOL_L(G01, data); /* set 2 color graphics left pixel 0 */
SETCOL_R(G10, data); /* set 2 color graphics right pixel 0 */
SPLIT_HUE(data, m_huebk);
data = m_huebk | m_lumpf1;
SETCOL_R(G01, data); /* set 2 color graphics right pixel 1 */
SETCOL_L(G10, data); /* set 2 color graphics left pixel 1 */
SETCOL_B(G11, data); /* set 2 color graphics both pixels 1 */
SETCOL_B(GT1+ 0, (data & HUE) + (0x00 & LUM)); /* set GTIA mode 1 HUE + LUM 0..15 */
SETCOL_B(GT1+ 1, (data & HUE) + (0x11 & LUM));
SETCOL_B(GT1+ 2, (data & HUE) + (0x22 & LUM));
SETCOL_B(GT1+ 3, (data & HUE) + (0x33 & LUM));
SETCOL_B(GT1+ 4, (data & HUE) + (0x44 & LUM));
SETCOL_B(GT1+ 5, (data & HUE) + (0x55 & LUM));
SETCOL_B(GT1+ 6, (data & HUE) + (0x66 & LUM));
SETCOL_B(GT1+ 7, (data & HUE) + (0x77 & LUM));
SETCOL_B(GT1+ 8, (data & HUE) + (0x88 & LUM));
SETCOL_B(GT1+ 9, (data & HUE) + (0x99 & LUM));
SETCOL_B(GT1+10, (data & HUE) + (0xaa & LUM));
SETCOL_B(GT1+11, (data & HUE) + (0xbb & LUM));
SETCOL_B(GT1+12, (data & HUE) + (0xcc & LUM));
SETCOL_B(GT1+13, (data & HUE) + (0xdd & LUM));
SETCOL_B(GT1+14, (data & HUE) + (0xee & LUM));
SETCOL_B(GT1+15, (data & HUE) + (0xff & LUM));
SETCOL_B(GT3+ 0, (data & LUM) + (0x00 & HUE)); /* set GTIA mode 3 LUM + HUE 0..15 */
SETCOL_B(GT3+ 1, (data & LUM) + (0x11 & HUE));
SETCOL_B(GT3+ 2, (data & LUM) + (0x22 & HUE));
SETCOL_B(GT3+ 3, (data & LUM) + (0x33 & HUE));
SETCOL_B(GT3+ 4, (data & LUM) + (0x44 & HUE));
SETCOL_B(GT3+ 5, (data & LUM) + (0x55 & HUE));
SETCOL_B(GT3+ 6, (data & LUM) + (0x66 & HUE));
SETCOL_B(GT3+ 7, (data & LUM) + (0x77 & HUE));
SETCOL_B(GT3+ 8, (data & LUM) + (0x88 & HUE));
SETCOL_B(GT3+ 9, (data & LUM) + (0x99 & HUE));
SETCOL_B(GT3+10, (data & LUM) + (0xaa & HUE));
SETCOL_B(GT3+11, (data & LUM) + (0xbb & HUE));
SETCOL_B(GT3+12, (data & LUM) + (0xcc & HUE));
SETCOL_B(GT3+13, (data & LUM) + (0xdd & HUE));
SETCOL_B(GT3+14, (data & LUM) + (0xee & HUE));
SETCOL_B(GT3+15, (data & LUM) + (0xff & HUE));
break;
case 27:
m_w.prior = data;
recalc_m0();
recalc_m1();
recalc_m2();
recalc_m3();
break;
case 28: /* delay until vertical retrace */
m_w.vdelay = data;
m_h.vdelay_m0 = (data >> 0) & 1;
m_h.vdelay_m1 = (data >> 1) & 1;
m_h.vdelay_m2 = (data >> 2) & 1;
m_h.vdelay_m3 = (data >> 3) & 1;
m_h.vdelay_p0 = (data >> 4) & 1;
m_h.vdelay_p1 = (data >> 5) & 1;
m_h.vdelay_p2 = (data >> 6) & 1;
m_h.vdelay_p3 = (data >> 7) & 1;
break;
case 29:
m_w.gractl = data;
recalc_p0();
recalc_p1();
recalc_p2();
recalc_p3();
recalc_m0();
recalc_m1();
recalc_m2();
recalc_m3();
break;
case 30: /* clear collisions */
m_r.m0pf = m_r.m1pf = m_r.m2pf = m_r.m3pf =
m_r.p0pf = m_r.p1pf = m_r.p2pf = m_r.p3pf =
m_r.m0pl = m_r.m1pl = m_r.m2pl = m_r.m3pl =
m_r.p0pl = m_r.p1pl = m_r.p2pl = m_r.p3pl = 0;
m_w.hitclr = data;
break;
case 31: /* write console (speaker) */
if (data == m_w.cons)
break;
m_w.cons = data;
if (!m_write_cb.isnull())
m_write_cb((offs_t)0, m_w.cons);
break;
}
}
void gtia_device::gtia_postload()
{
recalc_p0();
recalc_p1();
recalc_p2();
recalc_p3();
recalc_m0();
recalc_m1();
recalc_m2();
recalc_m3();
}
static const uint8_t pf_collision[256] = {
0,1,2,0,4,0,0,0,8,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
static const int pf_prioindex[256] = {
/* PBK PF0 PF1 PF2 PF3 */
/* */ 0x000,0x100,0x100,0x000,0x200,0x000,0x000,0x000,0x200,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* */ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* */ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* */ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* T00 */ 0x400,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* T01 */ 0x500,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* T10 */ 0x600,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* T11 */ 0x700,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* G00 */ 0x400,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* G01 */ 0x500,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* G10 */ 0x600,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* G11 */ 0x700,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* GT1 */ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* GT2 */ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* GT3 */ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,
/* */ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000
};
inline void gtia_device::player_render(uint8_t gfx, int size, uint8_t color, uint8_t *dst)
{
// size is the number of bits in *dst to be filled: 1, 2 or 4
if (size == 3)
size = 2;
for (int i = 0; i < 8; i++)
if (BIT(gfx, 7 - i))
for (int s = 0; s < size; s++)
dst[i * size + s] |= color;
}
inline void gtia_device::missile_render(uint8_t gfx, int size, uint8_t color, uint8_t *dst)
{
// size is the number of bits in *dst to be filled: 1, 2 or 4
if (size == 3)
size = 2;
for (int i = 0; i < 2; i++)
if (BIT(gfx, 7 - i))
for (int s = 0; s < size; s++)
dst[i * size + s] |= color;
}
void gtia_device::render(uint8_t *src, uint8_t *dst, uint8_t *prio, uint8_t *pmbits)
{
if (m_h.grafp0)
player_render(m_h.grafp0, m_w.sizep0 + 1, GTIA_P0, &pmbits[m_w.hposp0]);
if (m_h.grafp1)
player_render(m_h.grafp1, m_w.sizep1 + 1, GTIA_P1, &pmbits[m_w.hposp1]);
if (m_h.grafp2)
player_render(m_h.grafp2, m_w.sizep2 + 1, GTIA_P2, &pmbits[m_w.hposp2]);
if (m_h.grafp3)
player_render(m_h.grafp3, m_w.sizep3 + 1, GTIA_P3, &pmbits[m_w.hposp3]);
if (m_h.grafm0)
missile_render(m_h.grafm0, m_w.sizem + 1, GTIA_M0, &pmbits[m_w.hposm0]);
if (m_h.grafm1)
missile_render(m_h.grafm1, m_w.sizem + 1, GTIA_M1, &pmbits[m_w.hposm1]);
if (m_h.grafm2)
missile_render(m_h.grafm2, m_w.sizem + 1, GTIA_M2, &pmbits[m_w.hposm2]);
if (m_h.grafm3)
missile_render(m_h.grafm3, m_w.sizem + 1, GTIA_M3, &pmbits[m_w.hposm3]);
for (int x = 0; x < GTIA_HWIDTH * 4; x++, src++, dst++)
{
uint8_t pm, pc, pf;
if (!*src)
continue;
/* get the player/missile combination bits and reset the buffer */
pm = *src;
*src = 0;
/* get the current playfield color */
pc = *dst;
pf = pf_collision[pc];
if (pm & GTIA_P0) { m_r.p0pf |= pf; m_r.p0pl |= pm & ( GTIA_P1 | GTIA_P2 | GTIA_P3); }
if (pm & GTIA_P1) { m_r.p1pf |= pf; m_r.p1pl |= pm & (GTIA_P0 | GTIA_P2 | GTIA_P3); }
if (pm & GTIA_P2) { m_r.p2pf |= pf; m_r.p2pl |= pm & (GTIA_P0 | GTIA_P1 | GTIA_P3); }
if (pm & GTIA_P3) { m_r.p3pf |= pf; m_r.p3pl |= pm & (GTIA_P0 | GTIA_P1 | GTIA_P2 ); }
if (pm & GTIA_M0) { m_r.m0pf |= pf; m_r.m0pl |= pm & (GTIA_P0 | GTIA_P1 | GTIA_P2 | GTIA_P3); }
if (pm & GTIA_M1) { m_r.m1pf |= pf; m_r.m1pl |= pm & (GTIA_P0 | GTIA_P1 | GTIA_P2 | GTIA_P3); }
if (pm & GTIA_M2) { m_r.m2pf |= pf; m_r.m2pl |= pm & (GTIA_P0 | GTIA_P1 | GTIA_P2 | GTIA_P3); }
if (pm & GTIA_M3) { m_r.m3pf |= pf; m_r.m3pl |= pm & (GTIA_P0 | GTIA_P1 | GTIA_P2 | GTIA_P3); }
/* color with higher priority? change playfield */
pc = prio[pf_prioindex[pc] | pm];
if (pc) *dst = pc;
}
/* copy player/missile graphics in case of vdelay */
m_w.grafp0[1] = m_w.grafp0[0];
m_w.grafp1[1] = m_w.grafp1[0];
m_w.grafp2[1] = m_w.grafp2[0];
m_w.grafp3[1] = m_w.grafp3[0];
m_w.grafm[1] = m_w.grafm[0];
}