summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/tia.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/tia.c')
-rw-r--r--src/mame/video/tia.c2004
1 files changed, 2004 insertions, 0 deletions
diff --git a/src/mame/video/tia.c b/src/mame/video/tia.c
new file mode 100644
index 00000000000..e48f4aea805
--- /dev/null
+++ b/src/mame/video/tia.c
@@ -0,0 +1,2004 @@
+/***************************************************************************
+
+ Atari TIA video emulation
+
+***************************************************************************/
+
+#include "driver.h"
+#include "tia.h"
+#include "sound/tiaintf.h"
+#include <math.h>
+
+#define HMOVE_INACTIVE -200
+#define PLAYER_GFX_SLOTS 4
+
+// Per player graphic
+// - pixel number to start drawing from (0-7, from GRPx) / number of pixels drawn from GRPx
+// - display position to start drawing
+// - size to use
+struct player_gfx {
+ int start_pixel[PLAYER_GFX_SLOTS];
+ int start_drawing[PLAYER_GFX_SLOTS];
+ int size[PLAYER_GFX_SLOTS];
+ int skipclip[PLAYER_GFX_SLOTS];
+};
+
+static struct player_gfx p0gfx;
+static struct player_gfx p1gfx;
+
+static UINT32 frame_cycles;
+static UINT32 paddle_cycles;
+
+static int horzP0;
+static int horzP1;
+static int horzM0;
+static int horzM1;
+static int horzBL;
+static int motclkP0;
+static int motclkP1;
+static int motclkM0;
+static int motclkM1;
+static int motclkBL;
+static int startP0;
+static int startP1;
+static int startM0;
+static int startM1;
+static int skipclipP0;
+static int skipclipP1;
+static int skipM0delay;
+static int skipM1delay;
+
+static int current_bitmap;
+
+static int prev_x;
+static int prev_y;
+
+static UINT8 VSYNC;
+static UINT8 VBLANK;
+static UINT8 COLUP0;
+static UINT8 COLUP1;
+static UINT8 COLUBK;
+static UINT8 COLUPF;
+static UINT8 CTRLPF;
+static UINT8 GRP0;
+static UINT8 GRP1;
+static UINT8 REFP0;
+static UINT8 REFP1;
+static UINT8 HMP0;
+static UINT8 HMP1;
+static UINT8 HMM0;
+static UINT8 HMM1;
+static UINT8 HMBL;
+static UINT8 VDELP0;
+static UINT8 VDELP1;
+static UINT8 VDELBL;
+static UINT8 NUSIZ0;
+static UINT8 NUSIZ1;
+static UINT8 ENAM0;
+static UINT8 ENAM1;
+static UINT8 ENABL;
+static UINT8 CXM0P;
+static UINT8 CXM1P;
+static UINT8 CXP0FB;
+static UINT8 CXP1FB;
+static UINT8 CXM0FB;
+static UINT8 CXM1FB;
+static UINT8 CXBLPF;
+static UINT8 CXPPMM;
+static UINT8 RESMP0;
+static UINT8 RESMP1;
+static UINT8 PF0;
+static UINT8 PF1;
+static UINT8 PF2;
+static UINT8 INPT4;
+static UINT8 INPT5;
+
+static UINT8 prevGRP0;
+static UINT8 prevGRP1;
+static UINT8 prevENABL;
+
+static int HMOVE_started;
+static int HMOVE_started_previous;
+static UINT8 HMP0_latch;
+static UINT8 HMP1_latch;
+static UINT8 HMM0_latch;
+static UINT8 HMM1_latch;
+static UINT8 HMBL_latch;
+static UINT8 REFLECT; /* Should playfield be reflected or not */
+static UINT8 NUSIZx_changed;
+
+static mame_bitmap *helper[3];
+
+static UINT16 screen_height;
+
+static const int nusiz[8][3] =
+{
+ { 1, 1, 0 },
+ { 2, 1, 1 },
+ { 2, 1, 3 },
+ { 3, 1, 1 },
+ { 2, 1, 7 },
+ { 1, 2, 0 },
+ { 3, 1, 3 },
+ { 1, 4, 0 }
+};
+
+static read16_handler tia_read_input_port;
+static read8_handler tia_get_databus;
+static write16_handler tia_vsync_callback;
+
+static void extend_palette(running_machine *machine) {
+ int i,j;
+
+ for( i = 0; i < 128; i ++ )
+ {
+ rgb_t new_rgb = palette_get_color( machine, i );
+ UINT8 new_r = RGB_RED( new_rgb );
+ UINT8 new_g = RGB_GREEN( new_rgb );
+ UINT8 new_b = RGB_BLUE( new_rgb );
+
+ for ( j = 0; j < 128; j++ )
+ {
+ rgb_t old_rgb = palette_get_color( machine, j );
+ UINT8 old_r = RGB_RED( old_rgb );
+ UINT8 old_g = RGB_GREEN( old_rgb );
+ UINT8 old_b = RGB_BLUE( old_rgb );
+
+ palette_set_color_rgb(machine, ( ( i + 1 ) << 7 ) | j,
+ ( new_r + old_r ) / 2,
+ ( new_g + old_g ) / 2,
+ ( new_b + old_b ) / 2 );
+ }
+ }
+}
+
+PALETTE_INIT( tia_NTSC )
+{
+ int i, j;
+
+ static const double color[16][2] =
+ {
+ { 0.000, 0.000 },
+ { 0.144, -0.189 },
+ { 0.231, -0.081 },
+ { 0.243, 0.032 },
+ { 0.217, 0.121 },
+ { 0.117, 0.216 },
+ { 0.021, 0.233 },
+ { -0.066, 0.196 },
+ { -0.139, 0.134 },
+ { -0.182, 0.062 },
+ { -0.175, -0.022 },
+ { -0.136, -0.100 },
+ { -0.069, -0.150 },
+ { 0.005, -0.159 },
+ { 0.071, -0.125 },
+ { 0.124, -0.089 }
+ };
+
+ for (i = 0; i < 16; i++)
+ {
+ double I = color[i][0];
+ double Q = color[i][1];
+
+ for (j = 0; j < 8; j++)
+ {
+ double Y = j / 7.0;
+
+ double R = Y + 0.956 * I + 0.621 * Q;
+ double G = Y - 0.272 * I - 0.647 * Q;
+ double B = Y - 1.106 * I + 1.703 * Q;
+
+ R = pow(R, 0.9) / pow(1, 0.9);
+ G = pow(G, 0.9) / pow(1, 0.9);
+ B = pow(B, 0.9) / pow(1, 0.9);
+
+ if (R < 0) R = 0;
+ if (G < 0) G = 0;
+ if (B < 0) B = 0;
+
+ if (R > 1) R = 1;
+ if (G > 1) G = 1;
+ if (B > 1) B = 1;
+
+ palette_set_color_rgb(machine,8 * i + j,
+ (UINT8) (255 * R + 0.5),
+ (UINT8) (255 * G + 0.5),
+ (UINT8) (255 * B + 0.5));
+ }
+ }
+ extend_palette( machine );
+}
+
+
+PALETTE_INIT( tia_PAL )
+{
+ int i, j;
+
+ static const double color[16][2] =
+ {
+ { 0.000, 0.000 },
+ { 0.000, 0.000 },
+ { -0.222, 0.032 },
+ { -0.199, -0.027 },
+ { -0.173, 0.117 },
+ { -0.156, -0.197 },
+ { -0.094, 0.200 },
+ { -0.071, -0.229 },
+ { 0.070, 0.222 },
+ { 0.069, -0.204 },
+ { 0.177, 0.134 },
+ { 0.163, -0.130 },
+ { 0.219, 0.053 },
+ { 0.259, -0.042 },
+ { 0.000, 0.000 },
+ { 0.000, 0.000 }
+ };
+
+ for (i = 0; i < 16; i++)
+ {
+ double U = color[i][0];
+ double V = color[i][1];
+
+ for (j = 0; j < 8; j++)
+ {
+ double Y = j / 7.0;
+
+ double R = Y + 1.403 * V;
+ double G = Y - 0.344 * U - 0.714 * V;
+ double B = Y + 1.770 * U;
+
+ R = pow(R, 1.2) / pow(1, 1.2);
+ G = pow(G, 1.2) / pow(1, 1.2);
+ B = pow(B, 1.2) / pow(1, 1.2);
+
+ if (R < 0) R = 0;
+ if (G < 0) G = 0;
+ if (B < 0) B = 0;
+
+ if (R > 1) R = 1;
+ if (G > 1) G = 1;
+ if (B > 1) B = 1;
+
+ palette_set_color_rgb(machine,8 * i + j,
+ (UINT8) (255 * R + 0.5),
+ (UINT8) (255 * G + 0.5),
+ (UINT8) (255 * B + 0.5));
+ }
+ }
+ extend_palette( machine );
+}
+
+
+VIDEO_START( tia )
+{
+ int cx = machine->screen[0].width;
+
+ screen_height = machine->screen[0].height;
+ helper[0] = auto_bitmap_alloc(cx, TIA_MAX_SCREEN_HEIGHT, machine->screen[0].format);
+ helper[1] = auto_bitmap_alloc(cx, TIA_MAX_SCREEN_HEIGHT, machine->screen[0].format);
+ helper[2] = auto_bitmap_alloc(cx, TIA_MAX_SCREEN_HEIGHT, machine->screen[0].format);
+}
+
+
+VIDEO_UPDATE( tia )
+{
+ screen_height = machine->screen[0].height;
+ copybitmap(bitmap, helper[2], 0, 0, 0, 0,
+ cliprect, TRANSPARENCY_NONE, 0);
+ return 0;
+}
+
+
+static void draw_sprite_helper(UINT8* p, UINT8 *col, struct player_gfx *gfx,
+ UINT8 GRP, UINT8 COLUP, UINT8 REFP)
+{
+ int i;
+ int j;
+ int k;
+
+ if (REFP & 8)
+ {
+ GRP = BITSWAP8(GRP, 0, 1, 2, 3, 4, 5, 6, 7);
+ }
+
+ for (i = 0; i < PLAYER_GFX_SLOTS; i++)
+ {
+ int start_pos = gfx->start_drawing[i];
+ for (j = gfx->start_pixel[i]; j < 8; j++)
+ {
+ for (k = 0; k < gfx->size[i]; k++)
+ {
+ if (GRP & (0x80 >> j))
+ {
+ if ( start_pos < 160 || ! gfx->skipclip[i] ) {
+ p[start_pos % 160] = COLUP >> 1;
+ col[start_pos % 160] = COLUP >> 1;
+ }
+ }
+ start_pos++;
+ }
+ }
+ }
+}
+
+
+static void draw_missile_helper(UINT8* p, UINT8* col, int horz, int skipdelay, int latch, int start,
+ UINT8 RESMP, UINT8 ENAM, UINT8 NUSIZ, UINT8 COLUM)
+{
+ int num = nusiz[NUSIZ & 7][0];
+ int skp = nusiz[NUSIZ & 7][2];
+
+ int width = 1 << ((NUSIZ >> 4) & 3);
+
+ int i;
+ int j;
+
+ for (i = 0; i < num; i++)
+ {
+ if ( i == 0 )
+ horz -= skipdelay;
+ if ( i == 1 )
+ horz += skipdelay;
+ if ( i > 0 || start ) {
+ for (j = 0; j < width; j++)
+ {
+ if ((ENAM & 2) && !(RESMP & 2))
+ {
+ if ( latch ) {
+ switch ( horz % 4 ) {
+ case 1:
+ if ( horz >= 0 )
+ {
+ if ( horz < 156 ) {
+ p[(horz + 1) % 160] = COLUM >> 1;
+ col[(horz + 1) % 160] = COLUM >> 1;
+ }
+ p[horz % 160] = COLUM >> 1;
+ col[horz % 160] = COLUM >> 1;
+ }
+ break;
+ case 2:
+ case 3:
+ if ( horz >= 0 )
+ {
+ p[horz % 160] = COLUM >> 1;
+ col[horz % 160] = COLUM >> 1;
+ }
+ break;
+ }
+ } else {
+ if ( horz >= 0 )
+ {
+ p[horz % 160] = COLUM >> 1;
+ col[horz % 160] = COLUM >> 1;
+ }
+ }
+ }
+
+ horz++;
+ }
+ } else {
+ horz+= width;
+ }
+
+ horz += 8 * (skp + 1) - width;
+ }
+}
+
+
+static void draw_playfield_helper(UINT8* p, UINT8* col, int horz,
+ UINT8 COLU, UINT8 REFPF)
+{
+ UINT32 PF =
+ (BITSWAP8(PF0, 0, 1, 2, 3, 4, 5, 6, 7) << 0x10) |
+ (BITSWAP8(PF1, 7, 6, 5, 4, 3, 2, 1, 0) << 0x08) |
+ (BITSWAP8(PF2, 0, 1, 2, 3, 4, 5, 6, 7) << 0x00);
+
+ int i;
+ int j;
+
+ if (REFPF)
+ {
+ UINT32 swap = 0;
+
+ for (i = 0; i < 20; i++)
+ {
+ swap <<= 1;
+
+ if (PF & 1)
+ {
+ swap |= 1;
+ }
+
+ PF >>= 1;
+ }
+
+ PF = swap;
+ }
+
+ for (i = 0; i < 20; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ if (PF & (0x80000 >> i))
+ {
+ p[horz] = COLU >> 1;
+ col[horz] = COLU >> 1;
+ }
+
+ horz++;
+ }
+ }
+}
+
+
+static void draw_ball_helper(UINT8* p, UINT8* col, int horz, UINT8 ENAB)
+{
+ int width = 1 << ((CTRLPF >> 4) & 3);
+
+ int i;
+
+ for (i = 0; i < width; i++)
+ {
+ if (ENAB & 2)
+ {
+ p[horz % 160] = COLUPF >> 1;
+ col[horz % 160] = COLUPF >> 1;
+ }
+
+ horz++;
+ }
+}
+
+
+static void drawS0(UINT8* p, UINT8* col)
+{
+ draw_sprite_helper(p, col, &p0gfx, (VDELP0 & 1) ? prevGRP0 : GRP0, COLUP0, REFP0);
+}
+
+
+static void drawS1(UINT8* p, UINT8* col)
+{
+ draw_sprite_helper(p, col, &p1gfx, (VDELP1 & 1) ? prevGRP1 : GRP1, COLUP1, REFP1);
+}
+
+
+static void drawM0(UINT8* p, UINT8* col)
+{
+ draw_missile_helper(p, col, horzM0, skipM0delay, HMM0_latch, startM0, RESMP0, ENAM0, NUSIZ0, COLUP0);
+}
+
+
+static void drawM1(UINT8* p, UINT8* col)
+{
+ draw_missile_helper(p, col, horzM1, skipM1delay, HMM1_latch, startM1, RESMP1, ENAM1, NUSIZ1, COLUP1);
+}
+
+
+static void drawBL(UINT8* p, UINT8* col)
+{
+ draw_ball_helper(p, col, horzBL, (VDELBL & 1) ? prevENABL : ENABL);
+}
+
+
+static void drawPF(UINT8* p, UINT8 *col)
+{
+ draw_playfield_helper(p, col, 0,
+ ((CTRLPF & 6) == 2) ? COLUP0 : COLUPF, 0);
+
+ draw_playfield_helper(p, col, 80,
+ ((CTRLPF & 6) == 2) ? COLUP1 : COLUPF, REFLECT);
+}
+
+
+static int collision_check(UINT8* p1, UINT8* p2, int x1, int x2)
+{
+ int i;
+
+ for (i = x1; i < x2; i++)
+ {
+ if (p1[i] != 0xFF && p2[i] != 0xFF)
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+INLINE int current_x(void)
+{
+ return 3 * ((activecpu_gettotalcycles() - frame_cycles) % 76) - 68;
+}
+
+
+INLINE int current_y(void)
+{
+ return (activecpu_gettotalcycles() - frame_cycles) / 76;
+}
+
+
+static void setup_pXgfx(void)
+{
+ int i;
+ for ( i = 0; i < PLAYER_GFX_SLOTS; i++ )
+ {
+ if ( i < nusiz[NUSIZ0 & 7][0] && i >= ( startP0 ? 0 : 1 ) )
+ {
+ p0gfx.size[i] = nusiz[NUSIZ0 & 7][1];
+ if ( i )
+ {
+ p0gfx.start_drawing[i] = ( horzP0 + (p0gfx.size[i] > 1 ? 1 : 0)
+ + i * 8 * ( nusiz[NUSIZ0 & 7][2] + p0gfx.size[i] ) ) % 160;
+ p0gfx.skipclip[i] = 0;
+ }
+ else
+ {
+ p0gfx.start_drawing[i] = horzP0 + (p0gfx.size[i] > 1 ? 1 : 0 );
+ p0gfx.skipclip[i] = skipclipP0;
+ }
+ p0gfx.start_pixel[i] = 0;
+ }
+ else
+ {
+ p0gfx.start_pixel[i] = 8;
+ }
+ if ( i < nusiz[NUSIZ1 & 7][0] && i >= ( startP1 ? 0 : 1 ) )
+ {
+ p1gfx.size[i] = nusiz[NUSIZ1 & 7][1];
+ if ( i )
+ {
+ p1gfx.start_drawing[i] = ( horzP1 + (p1gfx.size[i] > 1 ? 1 : 0)
+ + i * 8 * ( nusiz[NUSIZ1 & 7][2] + p1gfx.size[i] ) ) % 160;
+ p1gfx.skipclip[i] = 0;
+ }
+ else
+ {
+ p1gfx.start_drawing[i] = horzP1 + (p1gfx.size[i] > 1 ? 1 : 0);
+ p1gfx.skipclip[i] = skipclipP1;
+ }
+ p1gfx.start_pixel[i] = 0;
+ }
+ else
+ {
+ p1gfx.start_pixel[i] = 8;
+ }
+ }
+}
+
+static void update_bitmap(int next_x, int next_y)
+{
+ int x;
+ int y;
+
+ UINT8 linePF[160];
+ UINT8 lineP0[160];
+ UINT8 lineP1[160];
+ UINT8 lineM0[160];
+ UINT8 lineM1[160];
+ UINT8 lineBL[160];
+
+ UINT8 temp[160];
+
+ if (prev_y >= next_y && prev_x >= next_x)
+ {
+ return;
+ }
+
+ memset(linePF, 0xFF, sizeof linePF);
+ memset(lineP0, 0xFF, sizeof lineP0);
+ memset(lineP1, 0xFF, sizeof lineP1);
+ memset(lineM0, 0xFF, sizeof lineM0);
+ memset(lineM1, 0xFF, sizeof lineM1);
+ memset(lineBL, 0xFF, sizeof lineBL);
+
+ if (VBLANK & 2)
+ {
+ memset(temp, 0, 160);
+ }
+ else
+ {
+ memset(temp, COLUBK >> 1, 160);
+
+ if (CTRLPF & 4)
+ {
+ drawS1(temp, lineP1);
+ drawM1(temp, lineM1);
+ drawS0(temp, lineP0);
+ drawM0(temp, lineM0);
+ drawPF(temp, linePF);
+ drawBL(temp, lineBL);
+ }
+ else
+ {
+ drawPF(temp, linePF);
+ drawBL(temp, lineBL);
+ drawS1(temp, lineP1);
+ drawM1(temp, lineM1);
+ drawS0(temp, lineP0);
+ drawM0(temp, lineM0);
+ }
+ }
+
+ for (y = prev_y; y <= next_y; y++)
+ {
+ UINT16* p;
+
+ int x1 = prev_x;
+ int x2 = next_x;
+ int colx1;
+
+ /* Check if we have crossed a line boundary */
+ if ( y != prev_y ) {
+ int redraw_line = 0;
+
+ HMOVE_started_previous = HMOVE_INACTIVE;
+
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ /* Apply pending motion clocks from a HMOVE initiated during the scanline */
+ if ( HMOVE_started >= 97 && HMOVE_started < 157 ) {
+ horzP0 -= motclkP0;
+ horzP1 -= motclkP1;
+ horzM0 -= motclkM0;
+ horzM1 -= motclkM1;
+ horzBL -= motclkBL;
+ if (horzP0 < 0)
+ horzP0 += 160;
+ if (horzP1 < 0)
+ horzP1 += 160;
+ if (horzM0 < 0)
+ horzM0 += 160;
+ if (horzM1 < 0)
+ horzM1 += 160;
+ if (horzBL < 0)
+ horzBL += 160;
+ HMOVE_started_previous = HMOVE_started;
+ }
+ HMOVE_started = HMOVE_INACTIVE;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if the playfield reflect bit was changed after the center of the screen */
+ if ( REFLECT != ( CTRLPF & 0x01 ) ) {
+ REFLECT = CTRLPF & 0x01;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if a RESPx or NUSIZx occured during the last line */
+ if ( ! startP0 || ! startP1 || ! startM0 || ! startM1) {
+ startP0 = 1;
+ startP1 = 1;
+ startM0 = 1;
+ startM1 = 1;
+
+ redraw_line = 1;
+ }
+
+ if ( skipclipP0 ) {
+ skipclipP0--;
+ redraw_line = 1;
+ }
+
+ if ( skipclipP1 ) {
+ skipclipP1--;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if HMP0 latch is still set */
+ if ( HMP0_latch ) {
+ horzP0 -= 17;
+ if ( horzP0 < 0 )
+ horzP0 += 160;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if HMP1 latch is still set */
+ if ( HMP1_latch ) {
+ horzP1 -= 17;
+ if ( horzP1 < 0 )
+ horzP1 += 160;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if HMM0 latch is still set */
+ if ( HMM0_latch ) {
+ horzM0 -= 17;
+ if ( horzM0 < 0 )
+ horzM0 += 160;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if HMM1 latch is still set */
+ if ( HMM1_latch ) {
+ horzM1 -= 17;
+ if ( horzM1 < 0 )
+ horzM1 += 160;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if HMBL latch is still set */
+ if ( HMBL_latch ) {
+ horzBL -= 17;
+ if ( horzBL < 0 )
+ horzBL += 160;
+ redraw_line = 1;
+ }
+
+ /* Redraw line if NUSIZx data was changed */
+ if ( NUSIZx_changed ) {
+ NUSIZx_changed = 0;
+ redraw_line = 1;
+ }
+
+ if ( skipM0delay || skipM1delay ) {
+ skipM0delay = 0;
+ skipM1delay = 0;
+ redraw_line = 1;
+ }
+
+ if ( redraw_line ) {
+ if (VBLANK & 2)
+ {
+ setup_pXgfx();
+ memset(temp, 0, 160);
+ }
+ else
+ {
+ memset(linePF, 0xFF, sizeof linePF);
+ memset(lineP0, 0xFF, sizeof lineP0);
+ memset(lineP1, 0xFF, sizeof lineP1);
+ memset(lineM0, 0xFF, sizeof lineM0);
+ memset(lineM1, 0xFF, sizeof lineM1);
+ memset(lineBL, 0xFF, sizeof lineBL);
+
+ memset(temp, COLUBK >> 1, 160);
+
+ setup_pXgfx();
+
+ if (CTRLPF & 4)
+ {
+ drawS1(temp, lineP1);
+ drawM1(temp, lineM1);
+ drawS0(temp, lineP0);
+ drawM0(temp, lineM0);
+ drawPF(temp, linePF);
+ drawBL(temp, lineBL);
+ }
+ else
+ {
+ drawPF(temp, linePF);
+ drawBL(temp, lineBL);
+ drawS1(temp, lineP1);
+ drawM1(temp, lineM1);
+ drawS0(temp, lineP0);
+ drawM0(temp, lineM0);
+ }
+ }
+ }
+ }
+ if (y != prev_y || x1 < 0)
+ {
+ x1 = 0;
+ }
+ if (y != next_y || x2 > 160)
+ {
+ x2 = 160;
+ }
+
+ /* Collision detection also takes place under the extended hblank area */
+ colx1 = ( x1 == 8 && HMOVE_started != HMOVE_INACTIVE ) ? 0 : x1;
+
+ if (collision_check(lineM0, lineP1, colx1, x2))
+ CXM0P |= 0x80;
+ if (collision_check(lineM0, lineP0, colx1, x2))
+ CXM0P |= 0x40;
+ if (collision_check(lineM1, lineP0, colx1, x2))
+ CXM1P |= 0x80;
+ if (collision_check(lineM1, lineP1, colx1, x2))
+ CXM1P |= 0x40;
+ if (collision_check(lineP0, linePF, colx1, x2))
+ CXP0FB |= 0x80;
+ if (collision_check(lineP0, lineBL, colx1, x2))
+ CXP0FB |= 0x40;
+ if (collision_check(lineP1, linePF, colx1, x2))
+ CXP1FB |= 0x80;
+ if (collision_check(lineP1, lineBL, colx1, x2))
+ CXP1FB |= 0x40;
+ if (collision_check(lineM0, linePF, colx1, x2))
+ CXM0FB |= 0x80;
+ if (collision_check(lineM0, lineBL, colx1, x2))
+ CXM0FB |= 0x40;
+ if (collision_check(lineM1, linePF, colx1, x2))
+ CXM1FB |= 0x80;
+ if (collision_check(lineM1, lineBL, colx1, x2))
+ CXM1FB |= 0x40;
+ if (collision_check(lineBL, linePF, colx1, x2))
+ CXBLPF |= 0x80;
+ if (collision_check(lineP0, lineP1, colx1, x2))
+ CXPPMM |= 0x80;
+ if (collision_check(lineM0, lineM1, colx1, x2))
+ CXPPMM |= 0x40;
+
+ p = BITMAP_ADDR16(helper[current_bitmap], y % screen_height, 34);
+
+ for (x = x1; x < x2; x++)
+ {
+ p[x] = temp[x];
+ }
+
+ if ( x2 == 160 && y % screen_height == (screen_height - 1) ) {
+ int t_y;
+ for ( t_y = 0; t_y < helper[2]->height; t_y++ ) {
+ UINT16* l0 = BITMAP_ADDR16( helper[current_bitmap], t_y, 0 );
+ UINT16* l1 = BITMAP_ADDR16( helper[1 - current_bitmap], t_y, 0 );
+ UINT16* l2 = BITMAP_ADDR16( helper[2], t_y, 0 );
+ int t_x;
+ for( t_x = 0; t_x < helper[2]->width; t_x++ ) {
+ if ( l0[t_x] != l1[t_x] ) {
+ /* Combine both entries */
+ l2[t_x] = ( ( l0[t_x] + 1 ) << 7 ) | l1[t_x];
+ } else {
+ l2[t_x] = l0[t_x];
+ }
+ }
+ }
+ current_bitmap ^= 1;
+ }
+ }
+
+ prev_x = next_x;
+ prev_y = next_y;
+}
+
+
+static WRITE8_HANDLER( WSYNC_w )
+{
+ int cycles = activecpu_gettotalcycles() - frame_cycles;
+
+ if (cycles % 76)
+ {
+ activecpu_adjust_icount(cycles % 76 - 76);
+ }
+}
+
+
+static WRITE8_HANDLER( VSYNC_w )
+{
+ if (data & 2)
+ {
+ if (!(VSYNC & 2))
+ {
+ int curr_y = current_y();
+
+ if ( curr_y > 5 )
+ update_bitmap(
+ Machine->screen[0].width,
+ Machine->screen[0].height);
+
+ if ( tia_vsync_callback ) {
+ tia_vsync_callback( 0, curr_y, 0xFFFF );
+ }
+
+ prev_y = 0;
+ prev_x = 0;
+
+ frame_cycles += 76 * current_y();
+ }
+ }
+
+ VSYNC = data;
+}
+
+
+static WRITE8_HANDLER( VBLANK_w )
+{
+ if (data & 0x80)
+ {
+ paddle_cycles = activecpu_gettotalcycles();
+ }
+ if ( ! ( VBLANK & 0x40 ) ) {
+ INPT4 = 0x80;
+ INPT5 = 0x80;
+ }
+ VBLANK = data;
+}
+
+
+static WRITE8_HANDLER( CTRLPF_w )
+{
+ int curr_x = current_x();
+
+ CTRLPF = data;
+ if ( curr_x < 80 ) {
+ REFLECT = CTRLPF & 1;
+ }
+}
+
+static WRITE8_HANDLER( HMP0_w )
+{
+ int curr_x = current_x();
+
+ data &= 0xF0;
+
+ if ( data == HMP0 )
+ return;
+
+ /* Check if HMOVE cycles are still being applied */
+ if ( HMOVE_started != HMOVE_INACTIVE && curr_x < MIN( HMOVE_started + 6 + motclkP0 * 4, 7 ) ) {
+ int new_motclkP0 = ( data ^ 0x80 ) >> 4;
+
+ /* Check if new horizontal move can still be applied normally */
+ if ( new_motclkP0 > motclkP0 || curr_x <= MIN( HMOVE_started + 6 + new_motclkP0 * 4, 7 ) ) {
+ horzP0 -= ( new_motclkP0 - motclkP0 );
+ motclkP0 = new_motclkP0;
+ } else {
+ horzP0 -= ( 15 - motclkP0 );
+ motclkP0 = 15;
+ if ( data != 0x70 && data != 0x80 ) {
+ HMP0_latch = 1;
+ }
+ }
+ if ( horzP0 < 0 )
+ horzP0 += 160;
+ horzP0 %= 160;
+ setup_pXgfx();
+ }
+ HMP0 = data;
+}
+
+static WRITE8_HANDLER( HMP1_w )
+{
+ int curr_x = current_x();
+
+ data &= 0xF0;
+
+ if ( data == HMP1 )
+ return;
+
+ /* Check if HMOVE cycles are still being applied */
+ if ( HMOVE_started != HMOVE_INACTIVE && curr_x < MIN( HMOVE_started + 6 + motclkP1 * 4, 7 ) ) {
+ int new_motclkP1 = ( data ^ 0x80 ) >> 4;
+
+ /* Check if new horizontal move can still be applied normally */
+ if ( new_motclkP1 > motclkP1 || curr_x <= MIN( HMOVE_started + 6 + new_motclkP1 * 4, 7 ) ) {
+ horzP1 -= ( new_motclkP1 - motclkP1 );
+ motclkP1 = new_motclkP1;
+ } else {
+ horzP1 -= ( 15 - motclkP1 );
+ motclkP1 = 15;
+ if ( data != 0x70 && data != 0x80 ) {
+ HMP1_latch = 1;
+ }
+ }
+ if ( horzP1 < 0 )
+ horzP1 += 160;
+ horzP1 %= 160;
+ setup_pXgfx();
+ }
+ HMP1 = data;
+}
+
+static WRITE8_HANDLER( HMM0_w )
+{
+ int curr_x = current_x();
+
+ data &= 0xF0;
+
+ if ( data == HMM0 )
+ return;
+
+ /* Check if HMOVE cycles are still being applied */
+ if ( HMOVE_started != HMOVE_INACTIVE && curr_x < MIN( HMOVE_started + 6 + motclkM0 * 4, 7 ) ) {
+ int new_motclkM0 = ( data ^ 0x80 ) >> 4;
+
+ /* Check if new horizontal move can still be applied normally */
+ if ( new_motclkM0 > motclkM0 || curr_x <= MIN( HMOVE_started + 6 + new_motclkM0 * 4, 7 ) ) {
+ horzM0 -= ( new_motclkM0 - motclkM0 );
+ motclkM0 = new_motclkM0;
+ } else {
+ horzM0 -= ( 15 - motclkM0 );
+ motclkM0 = 15;
+ if ( data != 0x70 && data != 0x80 ) {
+ HMM0_latch = 1;
+ }
+ }
+ if ( horzM0 < 0 )
+ horzM0 += 160;
+ horzM0 %= 160;
+ }
+ HMM0 = data;
+}
+
+static WRITE8_HANDLER( HMM1_w )
+{
+ int curr_x = current_x();
+
+ data &= 0xF0;
+
+ if ( data == HMM1 )
+ return;
+
+ /* Check if HMOVE cycles are still being applied */
+ if ( HMOVE_started != HMOVE_INACTIVE && curr_x < MIN( HMOVE_started + 6 + motclkM1 * 4, 7 ) ) {
+ int new_motclkM1 = ( data ^ 0x80 ) >> 4;
+
+ /* Check if new horizontal move can still be applied normally */
+ if ( new_motclkM1 > motclkM1 || curr_x <= MIN( HMOVE_started + 6 + new_motclkM1 * 4, 7 ) ) {
+ horzM1 -= ( new_motclkM1 - motclkM1 );
+ motclkM1 = new_motclkM1;
+ } else {
+ horzM1 -= ( 15 - motclkM1 );
+ motclkM1 = 15;
+ if ( data != 0x70 && data != 0x80 ) {
+ HMM1_latch = 1;
+ }
+ }
+ if ( horzM1 < 0 )
+ horzM1 += 160;
+ horzM1 %= 160;
+ }
+ HMM1 = data;
+}
+
+static WRITE8_HANDLER( HMBL_w )
+{
+ int curr_x = current_x();
+
+ data &= 0xF0;
+
+ if ( data == HMBL )
+ return;
+
+ /* Check if HMOVE cycles are still being applied */
+ if ( HMOVE_started != HMOVE_INACTIVE && curr_x < MIN( HMOVE_started + 6 + motclkBL * 4, 7 ) ) {
+ int new_motclkBL = ( data ^ 0x80 ) >> 4;
+
+ /* Check if new horizontal move can still be applied normally */
+ if ( new_motclkBL > motclkBL || curr_x <= MIN( HMOVE_started + 6 + new_motclkBL * 4, 7 ) ) {
+ horzBL -= ( new_motclkBL - motclkBL );
+ motclkBL = new_motclkBL;
+ } else {
+ horzBL -= ( 15 - motclkBL );
+ motclkBL = 15;
+ if ( data != 0x70 && data != 0x80 ) {
+ HMBL_latch = 1;
+ }
+ }
+ if ( horzBL < 0 )
+ horzBL += 160;
+ horzBL %= 160;
+ }
+ HMBL = data;
+}
+
+static WRITE8_HANDLER( HMOVE_w )
+{
+ int curr_x = current_x();
+ int curr_y = current_y();
+
+ HMOVE_started = curr_x;
+
+ /* Check if we have to undo some of the already applied cycles from an active graphics latch */
+ if ( curr_x + 68 < 17 * 4 ) {
+ int cycle_fix = 17 - ( ( curr_x + 68 + 7 ) / 4 );
+ if ( HMP0_latch )
+ horzP0 = ( horzP0 + cycle_fix ) % 160;
+ if ( HMP1_latch )
+ horzP1 = ( horzP1 + cycle_fix ) % 160;
+ if ( HMM0_latch )
+ horzM0 = ( horzM0 + cycle_fix ) % 160;
+ if ( HMM1_latch )
+ horzM1 = ( horzM1 + cycle_fix ) % 160;
+ if ( HMBL_latch )
+ horzBL = ( horzBL + cycle_fix ) % 160;
+ }
+
+ HMP0_latch = 0;
+ HMP1_latch = 0;
+ HMM0_latch = 0;
+ HMM1_latch = 0;
+ HMBL_latch = 0;
+
+ /* Check if HMOVE activities can be ignored */
+ if ( curr_x >= -5 && curr_x < 97 ) {
+ motclkP0 = 0;
+ motclkP1 = 0;
+ motclkM0 = 0;
+ motclkM1 = 0;
+ motclkBL = 0;
+ HMOVE_started = HMOVE_INACTIVE;
+ return;
+ }
+
+ motclkP0 = ( HMP0 ^ 0x80 ) >> 4;
+ motclkP1 = ( HMP1 ^ 0x80 ) >> 4;
+ motclkM0 = ( HMM0 ^ 0x80 ) >> 4;
+ motclkM1 = ( HMM1 ^ 0x80 ) >> 4;
+ motclkBL = ( HMBL ^ 0x80 ) >> 4;
+
+ /* Adjust number of graphics motion clocks for active display */
+ if ( curr_x >= 97 && curr_x < 151 ) {
+ int skip_motclks = ( 160 - HMOVE_started - 6 ) / 4;
+ motclkP0 -= skip_motclks;
+ motclkP1 -= skip_motclks;
+ motclkM0 -= skip_motclks;
+ motclkM1 -= skip_motclks;
+ motclkBL -= skip_motclks;
+ if ( motclkP0 < 0 )
+ motclkP0 = 0;
+ if ( motclkP1 < 0 )
+ motclkP1 = 0;
+ if ( motclkM0 < 0 )
+ motclkM0 = 0;
+ if ( motclkM1 < 0 )
+ motclkM1 = 0;
+ if ( motclkBL < 0 )
+ motclkBL = 0;
+ }
+
+ if ( curr_x >= -56 && curr_x < -5 ) {
+ int max_motclks = ( 7 - ( HMOVE_started + 5 ) ) / 4;
+ if ( motclkP0 > max_motclks )
+ motclkP0 = max_motclks;
+ if ( motclkP1 > max_motclks )
+ motclkP1 = max_motclks;
+ if ( motclkM0 > max_motclks )
+ motclkM0 = max_motclks;
+ if ( motclkM1 > max_motclks )
+ motclkM1 = max_motclks;
+ if ( motclkBL > max_motclks )
+ motclkBL = max_motclks;
+ }
+
+ /* Apply horizontal motion */
+ if ( curr_x < -5 || curr_x >= 157 ) {
+ horzP0 += 8 - motclkP0;
+ horzP1 += 8 - motclkP1;
+ horzM0 += 8 - motclkM0;
+ horzM1 += 8 - motclkM1;
+ horzBL += 8 - motclkBL;
+
+ if (horzP0 < 0)
+ horzP0 += 160;
+ if (horzP1 < 0)
+ horzP1 += 160;
+ if (horzM0 < 0)
+ horzM0 += 160;
+ if (horzM1 < 0)
+ horzM1 += 160;
+ if (horzBL < 0)
+ horzBL += 160;
+
+ horzP0 %= 160;
+ horzP1 %= 160;
+ horzM0 %= 160;
+ horzM1 %= 160;
+ horzBL %= 160;
+
+ /* When HMOVE is triggered on CPU cycle 75, the HBlank period on the
+ next line is also extended. */
+ if (curr_x >= 157)
+ {
+ curr_y += 1;
+ update_bitmap( -8, curr_y );
+ }
+ else
+ {
+ setup_pXgfx();
+ }
+ if (curr_y < screen_height)
+ {
+ memset(BITMAP_ADDR16(helper[current_bitmap], curr_y, 34), 0, 16);
+ }
+
+ prev_x = 8;
+ }
+}
+
+
+static WRITE8_HANDLER( RSYNC_w )
+{
+ /* this address is used in chip testing */
+}
+
+
+static WRITE8_HANDLER( NUSIZ0_w )
+{
+ int curr_x = current_x();
+
+ /* Check if relevant bits have changed */
+ if ( ( data & 7 ) != ( NUSIZ0 & 7 ) ) {
+ int i;
+ /* Check if we are (about to start) drawing a copy of the player 0 graphics */
+ for ( i = 0; i < PLAYER_GFX_SLOTS; i++ ) {
+ if ( p0gfx.start_pixel[i] < 8 ) {
+ int min_x = p0gfx.start_drawing[i];
+ int size = ( 8 - p0gfx.start_pixel[i] ) * p0gfx.size[i];
+ if ( curr_x >= ( min_x - 5 ) % 160 && curr_x < ( min_x + size ) % 160 ) {
+ if ( curr_x >= min_x % 160 || p0gfx.start_pixel[i] != 0 ) {
+ /* This copy has started drawing */
+ if ( p0gfx.size[i] == 1 && nusiz[data & 7][1] > 1 ) {
+ int delay = 1 + ( ( p0gfx.start_pixel[i] + ( curr_x - p0gfx.start_drawing[i] ) ) & 1 );
+ update_bitmap( curr_x + delay, current_y() );
+ p0gfx.start_pixel[i] += ( curr_x + delay - p0gfx.start_drawing[i] );
+ if ( p0gfx.start_pixel[i] > 8 )
+ p0gfx.start_pixel[i] = 8;
+ p0gfx.start_drawing[i] = curr_x + delay;
+ } else if ( p0gfx.size[1] > 1 && nusiz[data & 7][1] == 1 ) {
+ int delay = ( curr_x - p0gfx.start_drawing[i] ) & ( p0gfx.size[i] - 1 );
+ if ( delay ) {
+ delay = p0gfx.size[i] - delay;
+ }
+ update_bitmap( curr_x + delay, current_y() );
+ p0gfx.start_pixel[i] += ( curr_x - p0gfx.start_drawing[i] ) / p0gfx.size[i];
+ p0gfx.start_drawing[i] = curr_x + delay;
+ } else {
+ p0gfx.start_pixel[i] += ( curr_x - p0gfx.start_drawing[i] ) / p0gfx.size[i];
+ p0gfx.start_drawing[i] = curr_x;
+ }
+ p0gfx.size[i] = nusiz[data & 7][1];
+ } else {
+ /* This copy was just about to start drawing (meltdown) */
+ /* Adjust for 1 clock delay between zoomed and non-zoomed sprites */
+ if ( p0gfx.size[i] == 1 && nusiz[data & 7][1] > 1 ) {
+ /* Check for hardware oddity */
+ if ( p0gfx.start_drawing[i] - curr_x == 2 ) {
+ p0gfx.start_drawing[i]--;
+ } else {
+ p0gfx.start_drawing[i]++;
+ }
+ } else if ( p0gfx.size[i] > 1 && nusiz[data & 7][1] == 1 ) {
+ p0gfx.start_drawing[i]--;
+ }
+ p0gfx.size[i] = nusiz[data & 7][1];
+ }
+ } else {
+ /* We are passed the copy or the copy still needs to be done. Mark
+ it as done/invalid, the data will be reset in the next loop. */
+ p0gfx.start_pixel[i] = 8;
+ }
+ }
+ }
+ /* Apply NUSIZ updates to not yet drawn copies */
+ for ( i = ( startP0 ? 0 : 1 ); i < nusiz[data & 7][0]; i++ ) {
+ int j;
+ /* Find an unused p0gfx entry */
+ for ( j = 0; j < PLAYER_GFX_SLOTS; j++ ) {
+ if ( p0gfx.start_pixel[j] == 8 )
+ break;
+ }
+ p0gfx.size[j] = nusiz[data & 7][1];
+ p0gfx.start_drawing[j] = ( horzP0 + (p0gfx.size[j] > 1 ? 1 : 0)
+ + i * 8 * ( nusiz[data & 7][2] + p0gfx.size[j] ) ) % 160;
+ if ( curr_x < p0gfx.start_drawing[j] % 160 ) {
+ p0gfx.start_pixel[j] = 0;
+ }
+ }
+ NUSIZx_changed = 1;
+ }
+ NUSIZ0 = data;
+}
+
+
+static WRITE8_HANDLER( NUSIZ1_w )
+{
+ int curr_x = current_x();
+
+ /* Check if relevant bits have changed */
+ if ( ( data & 7 ) != ( NUSIZ1 & 7 ) ) {
+ int i;
+ /* Check if we are (about to start) drawing a copy of the player 1 graphics */
+ for ( i = 0; i < PLAYER_GFX_SLOTS; i++ ) {
+ if ( p1gfx.start_pixel[i] < 8 ) {
+ int min_x = p1gfx.start_drawing[i];
+ int size = ( 8 - p1gfx.start_pixel[i] ) * p1gfx.size[i];
+ if ( curr_x >= ( min_x - 5 ) % 160 && curr_x < ( min_x + size ) % 160 ) {
+ if ( curr_x >= min_x % 160 || p1gfx.start_pixel[i] != 0 ) {
+ /* This copy has started drawing */
+ if ( p1gfx.size[i] == 1 && nusiz[data & 7][1] > 1 ) {
+ int delay = 1 + ( ( p0gfx.start_pixel[i] + ( curr_x - p0gfx.start_drawing[i] ) ) & 1 );
+ update_bitmap( curr_x + delay, current_y() );
+ p1gfx.start_pixel[i] += ( curr_x + delay - p1gfx.start_drawing[i] );
+ if ( p1gfx.start_pixel[i] > 8 )
+ p1gfx.start_pixel[i] = 8;
+ p1gfx.start_drawing[i] = curr_x + delay;
+ } else if ( p1gfx.size[1] > 1 && nusiz[data & 7][1] == 1 ) {
+ int delay = ( curr_x - p1gfx.start_drawing[i] ) & ( p1gfx.size[i] - 1 );
+ if ( delay ) {
+ delay = p1gfx.size[i] - delay;
+ }
+ update_bitmap( curr_x + delay, current_y() );
+ p1gfx.start_pixel[i] += ( curr_x - p1gfx.start_drawing[i] ) / p1gfx.size[i];
+ p1gfx.start_drawing[i] = curr_x + delay;
+ } else {
+ p1gfx.start_pixel[i] += ( curr_x - p1gfx.start_drawing[i] ) / p1gfx.size[i];
+ p1gfx.start_drawing[i] = curr_x;
+ }
+ p1gfx.size[i] = nusiz[data & 7][1];
+ } else {
+ /* This copy was just about to start drawing (meltdown) */
+ /* Adjust for 1 clock delay between zoomed and non-zoomed sprites */
+ if ( p1gfx.size[i] == 1 && nusiz[data & 7][1] > 1 ) {
+ /* Check for hardware oddity */
+ if ( p1gfx.start_drawing[i] - curr_x == 2 ) {
+ p1gfx.start_drawing[i]--;
+ } else {
+ p1gfx.start_drawing[i]++;
+ }
+ } else if ( p1gfx.size[i] > 1 && nusiz[data & 7][1] == 1 ) {
+ p1gfx.start_drawing[i]--;
+ }
+ p1gfx.size[i] = nusiz[data & 7][1];
+ }
+ } else {
+ /* We are passed the copy or the copy still needs to be done. Mark
+ it as done/invalid, the data will be reset in the next loop. */
+ p1gfx.start_pixel[i] = 8;
+ }
+ }
+ }
+ /* Apply NUSIZ updates to not yet drawn copies */
+ for ( i = ( startP1 ? 0 : 1 ); i < nusiz[data & 7][0]; i++ ) {
+ int j;
+ /* Find an unused p1gfx entry */
+ for ( j = 0; j < PLAYER_GFX_SLOTS; j++ ) {
+ if ( p1gfx.start_pixel[j] == 8 )
+ break;
+ }
+ p1gfx.size[j] = nusiz[data & 7][1];
+ p1gfx.start_drawing[j] = ( horzP1 + (p1gfx.size[j] > 1 ? 1 : 0)
+ + i * 8 * ( nusiz[data & 7][2] + p1gfx.size[j] ) ) % 160;
+ if ( curr_x < p1gfx.start_drawing[j] % 160 ) {
+ p1gfx.start_pixel[j] = 0;
+ }
+ }
+ NUSIZx_changed = 1;
+ }
+ NUSIZ1 = data;
+}
+
+
+static WRITE8_HANDLER( HMCLR_w )
+{
+ HMP0_w( offset, 0 );
+ HMP1_w( offset, 0 );
+ HMM0_w( offset, 0 );
+ HMM1_w( offset, 0 );
+ HMBL_w( offset, 0 );
+}
+
+
+static WRITE8_HANDLER( CXCLR_w )
+{
+ CXM0P = 0;
+ CXM1P = 0;
+ CXP0FB = 0;
+ CXP1FB = 0;
+ CXM0FB = 0;
+ CXM1FB = 0;
+ CXBLPF = 0;
+ CXPPMM = 0;
+}
+
+
+#define RESXX_APPLY_ACTIVE_HMOVE(HORZ,MOTION,MOTCLK) \
+ if ( curr_x < MIN( HMOVE_started + 6 + 16 * 4, 7 ) ) { \
+ int decrements_passed = ( curr_x - ( HMOVE_started + 4 ) ) / 4; \
+ HORZ += 8; \
+ if ( ( MOTCLK - decrements_passed ) > 0 ) { \
+ HORZ -= ( MOTCLK - decrements_passed ); \
+ if ( HORZ < 0 ) \
+ HORZ += 160; \
+ } \
+ }
+
+#define RESXX_APPLY_PREVIOUS_HMOVE(HORZ,MOTION) \
+ if ( HMOVE_started_previous != HMOVE_INACTIVE ) \
+ { \
+ UINT8 motclk = ( MOTION ^ 0x80 ) >> 4; \
+ if ( curr_x <= HMOVE_started_previous - 228 + 5 + motclk * 4 ) \
+ { \
+ UINT8 motclk_passed = ( curr_x - ( HMOVE_started_previous - 228 + 6 ) ) / 4; \
+ HORZ -= ( motclk - motclk_passed ); \
+ } \
+ }
+
+static WRITE8_HANDLER( RESP0_w )
+{
+ int curr_x = current_x();
+ int new_horzP0;
+
+ /* Check if HMOVE is activated during this line */
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ new_horzP0 = ( curr_x < 7 ) ? 3 : ( curr_x + 5 );
+ /* If HMOVE is active, adjust for remaining horizontal move clocks if any */
+ RESXX_APPLY_ACTIVE_HMOVE( new_horzP0, HMP0, motclkP0 );
+ } else {
+ new_horzP0 = ( curr_x < -2 ) ? 3 : ( curr_x + 5 );
+ RESXX_APPLY_PREVIOUS_HMOVE( new_horzP0, HMP0 );
+ }
+
+ if ( new_horzP0 != horzP0 ) {
+ int i;
+ horzP0 = new_horzP0;
+ startP0 = 0;
+ skipclipP0 = 2;
+ /* Check if we are (about to start) drawing a copy of the player 0 graphics */
+ for ( i = 0; i < PLAYER_GFX_SLOTS; i++ ) {
+ if ( p0gfx.start_pixel[i] < 8 ) {
+ int min_x = p0gfx.start_drawing[i];
+ int size = ( 8 - p0gfx.start_pixel[i] ) * p0gfx.size[i];
+ if ( curr_x >= ( min_x - 5 ) % 160 && curr_x < ( min_x + size ) % 160 ) {
+ if ( curr_x >= min_x ) {
+ /* This copy has started drawing */
+ p0gfx.start_pixel[i] += ( curr_x - p0gfx.start_drawing[i] ) / p0gfx.size[i];
+ p0gfx.start_drawing[i] = curr_x;
+ } else {
+ /* This copy is waiting to start drawing */
+ p0gfx.start_drawing[i] = horzP0;
+ }
+ } else {
+ /* We are passed the copy or the copy still needs to be done. Mark
+ it as done/invalid, the data will be reset in the next loop. */
+ p0gfx.start_pixel[i] = 8;
+ }
+ }
+ }
+ /* Apply NUSIZ and position updates to not yet drawn copies */
+ for ( i = 1; i < nusiz[NUSIZ0 & 7][0]; i++ ) {
+ int j;
+ /* Find an unused p0gfx entry */
+ for ( j = 0; j < PLAYER_GFX_SLOTS; j++ ) {
+ if ( p0gfx.start_pixel[j] == 8 )
+ break;
+ }
+ p0gfx.size[j] = nusiz[NUSIZ0 & 7][1];
+ p0gfx.start_drawing[j] = ( horzP0 + (p0gfx.size[j] > 1 ? 1 : 0)
+ + i * 8 * ( nusiz[NUSIZ0 & 7][2] + p0gfx.size[j] ) ) % 160;
+ if ( curr_x < p0gfx.start_drawing[j] % 160 ) {
+ p0gfx.start_pixel[j] = 0;
+ }
+ }
+ }
+}
+
+
+static WRITE8_HANDLER( RESP1_w )
+{
+ int curr_x = current_x();
+ int new_horzP1;
+
+ /* Check if HMOVE is activated during this line */
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ new_horzP1 = ( curr_x < 7 ) ? 3 : ( curr_x + 5 );
+ /* If HMOVE is active, adjust for remaining horizontal move clocks if any */
+ RESXX_APPLY_ACTIVE_HMOVE( new_horzP1, HMP1, motclkP1 );
+ } else {
+ new_horzP1 = ( curr_x < -2 ) ? 3 : ( curr_x + 5 );
+ RESXX_APPLY_PREVIOUS_HMOVE( new_horzP1, HMP1 );
+ }
+
+ if ( new_horzP1 != horzP1 ) {
+ int i;
+ horzP1 = new_horzP1;
+ startP1 = 0;
+ skipclipP1 = 2;
+ /* Check if we are (about to start) drawing a copy of the player 1 graphics */
+ for ( i = 0; i < PLAYER_GFX_SLOTS; i++ ) {
+ if ( p1gfx.start_pixel[i] < 8 ) {
+ int min_x = p1gfx.start_drawing[i];
+ int size = ( 8 - p1gfx.start_pixel[i] ) * p1gfx.size[i];
+ if ( curr_x >= ( min_x - 5 ) % 160 && curr_x < ( min_x + size ) % 160 ) {
+ if ( curr_x >= min_x ) {
+ /* This copy has started drawing */
+ p1gfx.start_pixel[i] += ( curr_x - p1gfx.start_drawing[i] ) / p1gfx.size[i];
+ p1gfx.start_drawing[i] = curr_x;
+ } else {
+ /* This copy is waiting to start drawing */
+ p1gfx.start_drawing[i] = horzP1;
+ }
+ } else {
+ /* We are passed the copy or the copy still needs to be done. Mark
+ it as done/invalid, the data will be reset in the next loop. */
+ p1gfx.start_pixel[i] = 8;
+ }
+ }
+ }
+ /* Apply NUSIZ and position updates to not yet drawn copies */
+ for ( i = 1; i < nusiz[NUSIZ1 & 7][0]; i++ ) {
+ int j;
+ /* Find an unused p1gfx entry */
+ for ( j = 0; j < PLAYER_GFX_SLOTS; j++ ) {
+ if ( p1gfx.start_pixel[j] == 8 )
+ break;
+ }
+ p1gfx.size[j] = nusiz[NUSIZ1 & 7][1];
+ p1gfx.start_drawing[j] = ( horzP1 + (p1gfx.size[j] > 1 ? 1 : 0)
+ + i * 8 * ( nusiz[NUSIZ1 & 7][2] + p1gfx.size[j] ) ) % 160;
+ if ( curr_x < p1gfx.start_drawing[j] % 160 ) {
+ p1gfx.start_pixel[j] = 0;
+ }
+ }
+ }
+}
+
+
+static WRITE8_HANDLER( RESM0_w )
+{
+ int curr_x = current_x();
+ int new_horzM0;
+
+ /* Check if HMOVE is activated during this line */
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ new_horzM0 = ( curr_x < 7 ) ? 2 : ( ( curr_x + 4 ) % 160 );
+ /* If HMOVE is active, adjust for remaining horizontal move clocks if any */
+ RESXX_APPLY_ACTIVE_HMOVE( new_horzM0, HMM0, motclkM0 );
+ } else {
+ new_horzM0 = ( curr_x < -1 ) ? 2 : ( ( curr_x + 4 ) % 160 );
+ skipM0delay = ( curr_x < -1 && horzM0 % 160 >= 0 && horzM0 % 160 < 1 ) ? 4 : 0;
+ RESXX_APPLY_PREVIOUS_HMOVE( new_horzM0, HMM0 );
+ }
+ if ( new_horzM0 != horzM0 ) {
+ startM0 = skipM0delay ? 1 : 0;
+ horzM0 = new_horzM0;
+ }
+}
+
+
+static WRITE8_HANDLER( RESM1_w )
+{
+ int curr_x = current_x();
+ int new_horzM1;
+
+ /* Check if HMOVE is activated during this line */
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ new_horzM1 = ( curr_x < 7 ) ? 2 : ( ( curr_x + 4 ) % 160 );
+ /* If HMOVE is active, adjust for remaining horizontal move clocks if any */
+ RESXX_APPLY_ACTIVE_HMOVE( new_horzM1, HMM1, motclkM1 );
+ } else {
+ new_horzM1 = ( curr_x < -1 ) ? 2 : ( ( curr_x + 4 ) % 160 );
+ skipM1delay = ( curr_x < -1 && horzM1 % 160 >= 0 && horzM1 % 160 < 1 ) ? 4 : 0;
+ RESXX_APPLY_PREVIOUS_HMOVE( new_horzM1, HMM1 );
+ }
+ if ( new_horzM1 != horzM1 ){
+ startM1 = skipM1delay ? 1 : 0;
+ horzM1 = new_horzM1;
+ }
+}
+
+
+static WRITE8_HANDLER( RESBL_w )
+{
+ int curr_x = current_x();
+
+ /* Check if HMOVE is activated during this line */
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ horzBL = ( curr_x < 7 ) ? 2 : ( ( curr_x + 4 ) % 160 );
+ /* If HMOVE is active, adjust for remaining horizontal move clocks if any */
+ RESXX_APPLY_ACTIVE_HMOVE( horzBL, HMBL, motclkBL );
+ } else {
+ horzBL = ( curr_x < 0 ) ? 2 : ( ( curr_x + 4 ) % 160 );
+ RESXX_APPLY_PREVIOUS_HMOVE( horzBL, HMBL );
+ }
+}
+
+
+static WRITE8_HANDLER( RESMP0_w )
+{
+ if (RESMP0 & 2)
+ {
+ if ( nusiz[NUSIZ0 & 7][1] > 1 ) {
+ horzM0 = horzP0 + 3 * nusiz[NUSIZ0 & 7][1] - 1;
+ } else {
+ horzM0 = horzP0 + 4 * nusiz[NUSIZ0 & 7][1];
+ }
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ horzM0 -= ( 8 - motclkP0 );
+ horzM0 += 8 - motclkM0;
+ if ( horzM0 < 0 )
+ horzM0 += 160;
+ }
+ horzM0 %= 160;
+ }
+
+ RESMP0 = data;
+}
+
+
+static WRITE8_HANDLER( RESMP1_w )
+{
+ if (RESMP1 & 2)
+ {
+ if ( nusiz[NUSIZ1 & 7][1] > 1 ) {
+ horzM1 = horzP1 + 3 * nusiz[NUSIZ1 & 7][1] - 1;
+ } else {
+ horzM1 = horzP1 + 4 * nusiz[NUSIZ1 & 7][1];
+ }
+ if ( HMOVE_started != HMOVE_INACTIVE ) {
+ horzM1 -= ( 8 - motclkP1 );
+ horzM1 += 8 - motclkM1;
+ if ( horzM1 < 0 )
+ horzM1 += 160;
+ }
+ horzM1 %= 160;
+ }
+
+ RESMP1 = data;
+}
+
+
+static WRITE8_HANDLER( GRP0_w )
+{
+ prevGRP1 = GRP1;
+
+ GRP0 = data;
+}
+
+
+static WRITE8_HANDLER( GRP1_w )
+{
+ prevGRP0 = GRP0;
+
+ GRP1 = data;
+
+ prevENABL = ENABL;
+}
+
+
+static READ8_HANDLER( INPT_r )
+{
+ UINT32 elapsed = activecpu_gettotalcycles() - paddle_cycles;
+ int input = TIA_INPUT_PORT_ALWAYS_ON;
+
+ if ( tia_read_input_port )
+ {
+ input = tia_read_input_port(offset & 3, 0xFFFF);
+ }
+
+ if ( input == TIA_INPUT_PORT_ALWAYS_ON )
+ return 0x80;
+ if ( input == TIA_INPUT_PORT_ALWAYS_OFF )
+ return 0x00;
+
+ return elapsed > 76 * input ? 0x80 : 0x00;
+}
+
+
+READ8_HANDLER( tia_r )
+{
+ /* lower bits 0 - 5 seem to depend on the last byte on the
+ data bus. If the driver supplied a routine to retrieve
+ that we will call that, otherwise we will use the lower
+ bit of the offset.
+ */
+ UINT8 data = offset & 0x3f;
+
+ if ( tia_get_databus )
+ {
+ data = tia_get_databus(offset) & 0x3f;
+ }
+
+ if (!(offset & 0x8))
+ {
+ update_bitmap(current_x(), current_y());
+ }
+
+ switch (offset & 0xF)
+ {
+ case 0x0:
+ return data | CXM0P;
+ case 0x1:
+ return data | CXM1P;
+ case 0x2:
+ return data | CXP0FB;
+ case 0x3:
+ return data | CXP1FB;
+ case 0x4:
+ return data | CXM0FB;
+ case 0x5:
+ return data | CXM1FB;
+ case 0x6:
+ return data | CXBLPF;
+ case 0x7:
+ return data | CXPPMM;
+ case 0x8:
+ return data | INPT_r(0);
+ case 0x9:
+ return data | INPT_r(1);
+ case 0xA:
+ return data | INPT_r(2);
+ case 0xB:
+ return data | INPT_r(3);
+ case 0xC:
+ {
+ int button = tia_read_input_port ? ( tia_read_input_port(4,0xFFFF) & 0x80 ) : 0x80;
+ INPT4 = ( VBLANK & 0x40) ? ( INPT4 & button ) : button;
+ }
+ return data | INPT4;
+ case 0xD:
+ {
+ int button = tia_read_input_port ? ( tia_read_input_port(5,0xFFFF) & 0x80 ) : 0x80;
+ INPT5 = ( VBLANK & 0x40) ? ( INPT5 & button ) : button;
+ }
+ return data | INPT5;
+ }
+
+ return data;
+}
+
+
+WRITE8_HANDLER( tia_w )
+{
+ static const int delay[0x40] =
+ {
+ 0, // VSYNC
+ 0, // VBLANK
+ 0, // WSYNC
+ 0, // RSYNC
+ 0, // NUSIZ0
+ 0, // NUSIZ1
+ 0, // COLUP0
+ 0, // COLUP1
+ 0, // COLUPF
+ 0, // COLUBK
+ 0, // CTRLPF
+ 1, // REFP0
+ 1, // REFP1
+ 4, // PF0
+ 4, // PF1
+ 4, // PF2
+ 0, // RESP0
+ 0, // RESP1
+ 0, // RESM0
+ 0, // RESM1
+ 0, // RESBL
+ -1, // AUDC0
+ -1, // AUDC1
+ -1, // AUDF0
+ -1, // AUDF1
+ -1, // AUDV0
+ -1, // AUDV1
+ 1, // GRP0
+ 1, // GRP1
+ 1, // ENAM0
+ 1, // ENAM1
+ 1, // ENABL
+ 0, // HMP0
+ 0, // HMP1
+ 0, // HMM0
+ 0, // HMM1
+ 0, // HMBL
+ 0, // VDELP0
+ 0, // VDELP1
+ 0, // VDELBL
+ 0, // RESMP0
+ 0, // RESMP1
+ 3, // HMOVE
+ 0, // HMCLR
+ 0, // CXCLR
+ };
+
+ int curr_x = current_x();
+ int curr_y = current_y();
+
+ offset &= 0x3F;
+
+ if (offset >= 0x0D && offset <= 0x0F)
+ {
+ curr_x = ( curr_x + 1 ) & ~3;
+ }
+
+ if (delay[offset] >= 0)
+ {
+ update_bitmap(curr_x + delay[offset], curr_y);
+ }
+
+ switch (offset)
+ {
+ case 0x00:
+ VSYNC_w(offset, data);
+ break;
+ case 0x01:
+ VBLANK_w(offset, data);
+ break;
+ case 0x02:
+ WSYNC_w(offset, data);
+ break;
+ case 0x03:
+ RSYNC_w(offset, data);
+ break;
+ case 0x04:
+ NUSIZ0_w(offset, data);
+ break;
+ case 0x05:
+ NUSIZ1_w(offset, data);
+ break;
+ case 0x06:
+ COLUP0 = data;
+ break;
+ case 0x07:
+ COLUP1 = data;
+ break;
+ case 0x08:
+ COLUPF = data;
+ break;
+ case 0x09:
+ COLUBK = data;
+ break;
+ case 0x0A:
+ CTRLPF_w(offset, data);
+ break;
+ case 0x0B:
+ REFP0 = data;
+ break;
+ case 0x0C:
+ REFP1 = data;
+ break;
+ case 0x0D:
+ PF0 = data;
+ break;
+ case 0x0E:
+ PF1 = data;
+ break;
+ case 0x0F:
+ PF2 = data;
+ break;
+ case 0x10:
+ RESP0_w(offset, data);
+ break;
+ case 0x11:
+ RESP1_w(offset, data);
+ break;
+ case 0x12:
+ RESM0_w(offset, data);
+ break;
+ case 0x13:
+ RESM1_w(offset, data);
+ break;
+ case 0x14:
+ RESBL_w(offset, data);
+ break;
+
+ case 0x15: /* AUDC0 */
+ case 0x16: /* AUDC1 */
+ case 0x17: /* AUDF0 */
+ case 0x18: /* AUDF1 */
+ case 0x19: /* AUDV0 */
+ case 0x1A: /* AUDV1 */
+ tia_sound_w(offset, data);
+ break;
+
+ case 0x1B:
+ GRP0_w(offset, data);
+ break;
+ case 0x1C:
+ GRP1_w(offset, data);
+ break;
+ case 0x1D:
+ ENAM0 = data;
+ break;
+ case 0x1E:
+ ENAM1 = data;
+ break;
+ case 0x1F:
+ ENABL = data;
+ break;
+ case 0x20:
+ HMP0_w(offset, data);
+ break;
+ case 0x21:
+ HMP1_w(offset, data);
+ break;
+ case 0x22:
+ HMM0_w(offset, data);
+ break;
+ case 0x23:
+ HMM1_w(offset, data);
+ break;
+ case 0x24:
+ HMBL_w(offset, data);
+ break;
+ case 0x25:
+ VDELP0 = data;
+ break;
+ case 0x26:
+ VDELP1 = data;
+ break;
+ case 0x27:
+ VDELBL = data;
+ break;
+ case 0x28:
+ RESMP0_w(offset, data);
+ break;
+ case 0x29:
+ RESMP1_w(offset, data);
+ break;
+ case 0x2A:
+ HMOVE_w(offset, data);
+ break;
+ case 0x2B:
+ HMCLR_w(offset, data);
+ break;
+ case 0x2C:
+ CXCLR_w(offset, 0);
+ break;
+ }
+}
+
+
+static void tia_reset(running_machine *machine)
+{
+ int i;
+
+ frame_cycles = 0;
+
+ INPT4 = 0x80;
+ INPT5 = 0x80;
+
+ HMP0 = 0;
+ HMP1 = 0;
+ HMM0 = 0;
+ HMM1 = 0;
+ HMBL = 0;
+
+ startP0 = 1;
+ startP1 = 1;
+
+ HMP0_latch = 0;
+ HMP1_latch = 0;
+ HMM0_latch = 0;
+ HMM1_latch = 0;
+ HMBL_latch = 0;
+
+ startM0 = 1;
+ startM1 = 1;
+ skipM0delay = 0;
+ skipM1delay = 0;
+
+ REFLECT = 0;
+
+ prev_x = 0;
+ prev_y = 0;
+
+ HMOVE_started = HMOVE_INACTIVE;
+
+ motclkP0 = 0;
+ motclkP1 = 0;
+ motclkM0 = 0;
+ motclkM1 = 0;
+ motclkBL = 0;
+
+ for( i = 0; i < PLAYER_GFX_SLOTS; i++ )
+ {
+ p0gfx.start_pixel[i] = 8;
+ p0gfx.size[i] = 1;
+ p1gfx.start_pixel[i] = 8;
+ p1gfx.size[i] = 1;
+ }
+
+ NUSIZx_changed = 0;
+}
+
+
+
+void tia_init(const struct tia_interface* ti)
+{
+ assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call tia_init at init time!");
+
+ if ( ti ) {
+ tia_read_input_port = ti->read_input_port;
+ tia_get_databus = ti->databus_contents;
+ tia_vsync_callback = ti->vsync_callback;
+ } else {
+ tia_read_input_port = NULL;
+ tia_get_databus = NULL;
+ tia_vsync_callback = NULL;
+ }
+
+ tia_reset( Machine );
+
+ add_reset_callback(Machine, tia_reset);
+}
+