diff options
Diffstat (limited to 'src/mame/video/tia.c')
-rw-r--r-- | src/mame/video/tia.c | 2004 |
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); +} + |