// 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]; }