diff --git a/src/mame/includes/atari.h b/src/mame/includes/atari.h index 67ef44d505d..19d9600e4a9 100644 --- a/src/mame/includes/atari.h +++ b/src/mame/includes/atari.h @@ -26,6 +26,8 @@ public: m_gtia(*this, "gtia"), tv_artifacts(0) { } + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual void video_start(); UINT32 screen_update_atari(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); @@ -44,6 +46,11 @@ public: POKEY_KEYBOARD_CB_MEMBER(a800_keyboard); private: + static const device_timer_id TIMER_CYCLE_STEAL = 0; + static const device_timer_id TIMER_ISSUE_DLI = 1; + static const device_timer_id TIMER_LINE_REND = 2; + static const device_timer_id TIMER_LINE_DONE = 3; + required_device m_gtia; UINT32 tv_artifacts; void prio_init(); diff --git a/src/mame/video/antic.c b/src/mame/video/antic.c index 523cc1f7e90..0101992bb66 100644 --- a/src/mame/video/antic.c +++ b/src/mame/video/antic.c @@ -1508,3 +1508,681 @@ static ANTIC_RENDERER( gtia_mode_3_48 ) REP48(GTIA3); POST_GFX(48); } + +/************************************************************************ + * atari_vh_screenrefresh + * Refresh screen bitmap. + * Note: Actual drawing is done scanline wise during atari_interrupt + ************************************************************************/ +UINT32 atari_common_state::screen_update_atari(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + UINT32 new_tv_artifacts = screen.ioport("artifacts")->read_safe(0); + copybitmap(bitmap, *antic.bitmap, 0, 0, 0, 0, cliprect); + + if (tv_artifacts != new_tv_artifacts) + tv_artifacts = new_tv_artifacts; + + return 0; +} + +void atari_common_state::artifacts_gfx(UINT8 *src, UINT8 *dst, int width) +{ + UINT8 n, bits = 0; + UINT8 b = m_gtia->get_w_colbk() & 0xf0; + UINT8 c = m_gtia->get_w_colpf1() & 0x0f; + UINT8 atari_A = ((b + 0x30) & 0xf0) + c; + UINT8 atari_B = ((b + 0x70) & 0xf0) + c; + UINT8 atari_C = b + c; + UINT8 atari_D = m_gtia->get_w_colbk(); + UINT16 *color_lookup = m_gtia->get_color_lookup(); + + for (int x = 0; x < width * 4; x++) + { + n = *src++; + bits <<= 2; + switch( n ) + { + case G00: + break; + case G01: + bits |= 1; + break; + case G10: + bits |= 2; + break; + case G11: + bits |= 3; + break; + default: + *dst++ = color_lookup[n]; + *dst++ = color_lookup[n]; + continue; + } + switch ((bits >> 1) & 7) + { + case 0: /* 0 0 0 */ + case 1: /* 0 0 1 */ + case 4: /* 1 0 0 */ + *dst++ = atari_D; + break; + case 3: /* 0 1 1 */ + case 6: /* 1 1 0 */ + case 7: /* 1 1 1 */ + *dst++ = atari_C; + break; + case 2: /* 0 1 0 */ + *dst++ = atari_B; + break; + case 5: /* 1 0 1 */ + *dst++ = atari_A; + break; + } + switch (bits & 7) + { + case 0: /* 0 0 0 */ + case 1: /* 0 0 1 */ + case 4: /* 1 0 0 */ + *dst++ = atari_D; + break; + case 3: /* 0 1 1 */ + case 6: /* 1 1 0 */ + case 7: /* 1 1 1 */ + *dst++ = atari_C; + break; + case 2: /* 0 1 0 */ + *dst++ = atari_A; + break; + case 5: /* 1 0 1 */ + *dst++ = atari_B; + break; + } + } +} + +void atari_common_state::artifacts_txt(UINT8 * src, UINT8 * dst, int width) +{ + UINT8 n, bits = 0; + UINT8 b = m_gtia->get_w_colpf2() & 0xf0; + UINT8 c = m_gtia->get_w_colpf1() & 0x0f; + UINT8 atari_A = ((b+0x30)&0xf0)+c; + UINT8 atari_B = ((b+0x70)&0xf0)+c; + UINT8 atari_C = b+c; + UINT8 atari_D = m_gtia->get_w_colpf2(); + UINT16 *color_lookup = m_gtia->get_color_lookup(); + + for (int x = 0; x < width * 4; x++) + { + n = *src++; + bits <<= 2; + switch( n ) + { + case T00: + break; + case T01: + bits |= 1; + break; + case T10: + bits |= 2; + break; + case T11: + bits |= 3; + break; + default: + *dst++ = color_lookup[n]; + *dst++ = color_lookup[n]; + continue; + } + switch( (bits >> 1) & 7 ) + { + case 0: /* 0 0 0 */ + case 1: /* 0 0 1 */ + case 4: /* 1 0 0 */ + *dst++ = atari_D; + break; + case 3: /* 0 1 1 */ + case 6: /* 1 1 0 */ + case 7: /* 1 1 1 */ + *dst++ = atari_C; + break; + case 2: /* 0 1 0 */ + *dst++ = atari_A; + break; + case 5: /* 1 0 1 */ + *dst++ = atari_B; + break; + } + switch( bits & 7 ) + { + case 0:/* 0 0 0 */ + case 1:/* 0 0 1 */ + case 4:/* 1 0 0 */ + *dst++ = atari_D; + break; + case 3: /* 0 1 1 */ + case 6: /* 1 1 0 */ + case 7: /* 1 1 1 */ + *dst++ = atari_C; + break; + case 2: /* 0 1 0 */ + *dst++ = atari_B; + break; + case 5: /* 1 0 1 */ + *dst++ = atari_A; + break; + } + } +} + + +void atari_common_state::antic_linerefresh() +{ + int x, y; + UINT8 *src; + UINT32 *dst; + UINT32 scanline[4 + (HCHARS * 2) + 4]; + UINT16 *color_lookup = m_gtia->get_color_lookup(); + + /* increment the scanline */ + if( ++antic.scanline == machine().first_screen()->height() ) + { + /* and return to the top if the frame was complete */ + antic.scanline = 0; + antic.modelines = 0; + /* count frames gone since last write to hitclr */ + m_gtia->count_hitclr_frames(); + } + + if( antic.scanline < MIN_Y || antic.scanline > MAX_Y ) + return; + + y = antic.scanline - MIN_Y; + src = &antic.cclock[PMOFFSET - antic.hscrol_old + 12]; + dst = scanline; + + if( tv_artifacts ) + { + if( (antic.cmd & 0x0f) == 2 || (antic.cmd & 0x0f) == 3 ) + { + artifacts_txt(src, (UINT8*)(dst + 3), HCHARS); + return; + } + else + if( (antic.cmd & 0x0f) == 15 ) + { + artifacts_gfx(src, (UINT8*)(dst + 3), HCHARS); + return; + } + } + dst[0] = color_lookup[PBK] | color_lookup[PBK] << 16; + dst[1] = color_lookup[PBK] | color_lookup[PBK] << 16; + dst[2] = color_lookup[PBK] | color_lookup[PBK] << 16; + if ( (antic.cmd & ANTIC_HSCR) == 0 || (antic.pfwidth == 48) || (antic.pfwidth == 32)) + { + /* no hscroll */ + dst[3] = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; + src += 2; + dst += 4; + for( x = 1; x < HCHARS-1; x++ ) + { + *dst++ = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; + *dst++ = color_lookup[src[BYTE_XOR_LE(2)]] | color_lookup[src[BYTE_XOR_LE(3)]] << 16; + src += 4; + } + dst[0] = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; + } + else + { + /* if hscroll is enabled, more data are fetched by ANTIC, but it still renders playfield + of width defined by pfwidth. */ + switch( antic.pfwidth ) + { + case 0: + { + dst[3] = color_lookup[PBK] | color_lookup[PBK] << 16; + dst += 4; + for ( x = 1; x < HCHARS-1; x++ ) + { + *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; + *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; + } + dst[0] = color_lookup[PBK] | color_lookup[PBK] << 16; + } + break; + /* support for narrow playfield (32) with horizontal scrolling should be added here */ + case 40: + { + dst[3] = color_lookup[PBK] | color_lookup[PBK] << 16; + dst += 4; + for ( x = 1; x < HCHARS-2; x++ ) + { + if ( x == 1 ) + { + *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; + } + else + { + *dst++ = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; + } + *dst++ = color_lookup[src[BYTE_XOR_LE(2)]] | color_lookup[src[BYTE_XOR_LE(3)]] << 16; + src += 4; + } + for ( ; x < HCHARS-1; x++ ) + { + *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; + *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; + } + dst[0] = color_lookup[PBK] | color_lookup[PBK] << 16; + } + break; + } + } + dst[1] = color_lookup[PBK] | color_lookup[PBK] << 16; + dst[2] = color_lookup[PBK] | color_lookup[PBK] << 16; + dst[3] = color_lookup[PBK] | color_lookup[PBK] << 16; + + draw_scanline8(*antic.bitmap, 12, y, MIN(antic.bitmap->width() - 12, sizeof(scanline)), (const UINT8 *) scanline, NULL); +} + + +#define ANTIC_TIME_FROM_CYCLES(cycles) \ +(attotime)(machine().first_screen()->scan_period() * (cycles) / CYCLES_PER_LINE) + +void atari_common_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_CYCLE_STEAL: + antic_steal_cycles(ptr, param); + break; + case TIMER_ISSUE_DLI: + antic_issue_dli(ptr, param); + break; + case TIMER_LINE_REND: + antic_scanline_render(ptr, param); + break; + case TIMER_LINE_DONE: + antic_line_done(ptr, param); + break; + } +} + +int atari_common_state::cycle() +{ + return machine().first_screen()->hpos() * CYCLES_PER_LINE / machine().first_screen()->width(); +} + +TIMER_CALLBACK_MEMBER( atari_common_state::antic_issue_dli ) +{ + if( antic.w.nmien & DLI_NMI ) + { + LOG((" @cycle #%3d issue DLI\n", cycle())); + antic.r.nmist |= DLI_NMI; + machine().device("maincpu")->execute().set_input_line(INPUT_LINE_NMI, PULSE_LINE); + } + else + { + LOG((" @cycle #%3d DLI not enabled\n", cycle())); + } +} + + +/***************************************************************************** + * + * Antic Line Done + * + *****************************************************************************/ +TIMER_CALLBACK_MEMBER( atari_common_state::antic_line_done ) +{ + LOG((" @cycle #%3d antic_line_done\n", cycle())); + if( antic.w.wsync ) + { + LOG((" @cycle #%3d release WSYNC\n", cycle())); + /* release the CPU if it was actually waiting for HSYNC */ + machine().scheduler().trigger(TRIGGER_HSYNC); + /* and turn off the 'wait for hsync' flag */ + antic.w.wsync = 0; + } + LOG((" @cycle #%3d release CPU\n", cycle())); + /* release the CPU (held for emulating cycles stolen by ANTIC DMA) */ + machine().scheduler().trigger(TRIGGER_STEAL); + + /* refresh the display (translate color clocks to pixels) */ + antic_linerefresh(); +} + +/***************************************************************************** + * + * Antic Steal Cycles + * This is called once per scanline by a interrupt issued in the + * atari_scanline_render function. Set a new timer for the HSYNC + * position and release the CPU; but hold it again immediately until + * TRIGGER_HSYNC if WSYNC (D01A) was accessed + * + *****************************************************************************/ +TIMER_CALLBACK_MEMBER( atari_common_state::antic_steal_cycles ) +{ + LOG((" @cycle #%3d steal %d cycles\n", cycle(), antic.steal_cycles)); + timer_set(ANTIC_TIME_FROM_CYCLES(antic.steal_cycles), TIMER_LINE_DONE); + antic.steal_cycles = 0; + machine().device("maincpu")->execute().spin_until_trigger(TRIGGER_STEAL); +} + + +/***************************************************************************** + * + * Antic Scan Line Render + * Render the scanline to the scrbitmap buffer. + * Also transport player/missile data to the grafp and grafm registers + * of the GTIA if enabled (DMA_PLAYER or DMA_MISSILE) + * + *****************************************************************************/ +TIMER_CALLBACK_MEMBER( atari_common_state::antic_scanline_render ) +{ + address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); + + LOG((" @cycle #%3d render mode $%X lines to go #%d\n", cycle(), (antic.cmd & 0x0f), antic.modelines)); + + antic_render(space, m_antic_render1, m_antic_render2, m_antic_render3); + + /* if player/missile graphics is enabled */ + if( antic.scanline < 256 && (antic.w.dmactl & (DMA_PLAYER|DMA_MISSILE)) ) + { + /* new player/missile graphics data for every scanline ? */ + if( antic.w.dmactl & DMA_PM_DBLLINE ) + { + /* transport missile data to GTIA ? */ + if( antic.w.dmactl & DMA_MISSILE ) + { + antic.steal_cycles += 1; + m_gtia->write(space, 0x11, RDPMGFXD(space, 3*256)); + } + /* transport player data to GTIA ? */ + if( antic.w.dmactl & DMA_PLAYER ) + { + antic.steal_cycles += 4; + m_gtia->write(space, 0x0d, RDPMGFXD(space, 4*256)); + m_gtia->write(space, 0x0e, RDPMGFXD(space, 5*256)); + m_gtia->write(space, 0x0f, RDPMGFXD(space, 6*256)); + m_gtia->write(space, 0x10, RDPMGFXD(space, 7*256)); + } + } + else + { + /* transport missile data to GTIA ? */ + if( antic.w.dmactl & DMA_MISSILE ) + { + if( (antic.scanline & 1) == 0 ) /* even line ? */ + antic.steal_cycles += 1; + m_gtia->write(space, 0x11, RDPMGFXS(space, 3*128)); + } + /* transport player data to GTIA ? */ + if( antic.w.dmactl & DMA_PLAYER ) + { + if( (antic.scanline & 1) == 0 ) /* even line ? */ + antic.steal_cycles += 4; + m_gtia->write(space, 0x0d, RDPMGFXS(space, 4*128)); + m_gtia->write(space, 0x0e, RDPMGFXS(space, 5*128)); + m_gtia->write(space, 0x0f, RDPMGFXS(space, 6*128)); + m_gtia->write(space, 0x10, RDPMGFXS(space, 7*128)); + } + } + } + + if (antic.scanline >= VBL_END && antic.scanline < 256) + m_gtia->render((UINT8 *)antic.pmbits + PMOFFSET, (UINT8 *)antic.cclock + PMOFFSET - antic.hscrol_old, (UINT8 *)antic.prio_table[m_gtia->get_w_prior() & 0x3f], (UINT8 *)&antic.pmbits); + + antic.steal_cycles += CYCLES_REFRESH; + LOG((" run CPU for %d cycles\n", CYCLES_HSYNC - CYCLES_HSTART - antic.steal_cycles)); + timer_set(ANTIC_TIME_FROM_CYCLES(CYCLES_HSYNC - CYCLES_HSTART - antic.steal_cycles), TIMER_CYCLE_STEAL); +} + + + +void atari_common_state::LMS(int new_cmd) +{ + /************************************************************** + * If the LMS bit (load memory scan) of the current display + * list command is set, load the video source address from the + * following two bytes and split it up into video page/offset. + * Steal two more cycles from the CPU for fetching the address. + **************************************************************/ + if( new_cmd & ANTIC_LMS ) + { + address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); + int addr = RDANTIC(space); + antic.doffs = (antic.doffs + 1) & DOFFS; + addr += 256 * RDANTIC(space); + antic.doffs = (antic.doffs + 1) & DOFFS; + antic.vpage = addr & VPAGE; + antic.voffs = addr & VOFFS; + LOG((" LMS $%04x\n", addr)); + /* steal two more clock cycles from the cpu */ + antic.steal_cycles += 2; + } +} + +/***************************************************************************** + * + * Antic Scan Line DMA + * This is called once per scanline from Atari Interrupt + * If the ANTIC DMA is active (DMA_ANTIC) and the scanline not inside + * the VBL range (VBL_START - TOTAL_LINES or 0 - VBL_END) + * check if all mode lines of the previous ANTIC command were done and + * if so, read a new command and set up the renderer function + * + *****************************************************************************/ +void atari_common_state::antic_scanline_dma(int param) +{ + address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); + LOG((" @cycle #%3d DMA fetch\n", cycle())); + if (antic.scanline == VBL_END) + antic.r.nmist &= ~VBL_NMI; + if( antic.w.dmactl & DMA_ANTIC ) + { + if( antic.scanline >= VBL_END && antic.scanline < VBL_START ) + { + if( antic.modelines <= 0 ) + { + m_antic_render1 = 0; + m_antic_render3 = antic.w.dmactl & 3; + UINT8 vscrol_subtract = 0; + UINT8 new_cmd; + + new_cmd = RDANTIC(space); + antic.doffs = (antic.doffs + 1) & DOFFS; + /* steal at one clock cycle from the CPU for fetching the command */ + antic.steal_cycles += 1; + LOG((" ANTIC CMD $%02x\n", new_cmd)); + /* command 1 .. 15 ? */ + if (new_cmd & ANTIC_MODE) + { + antic.w.chbasl = 0; + /* vertical scroll mode changed ? */ + if( (antic.cmd ^ new_cmd) & ANTIC_VSCR ) + { + /* vertical scroll activate now ? */ + if( new_cmd & ANTIC_VSCR ) + { + antic.vscrol_old = + vscrol_subtract = + antic.w.chbasl = antic.w.vscrol; + } + else + { + vscrol_subtract = ~antic.vscrol_old; + } + } + /* does this command have horizontal scroll enabled ? */ + if( new_cmd & ANTIC_HSCR ) + { + m_antic_render1 = 1; + antic.hscrol_old = antic.w.hscrol; + } + else + { + antic.hscrol_old = 0; + } + } + /* Set the ANTIC mode renderer function */ + m_antic_render2 = new_cmd & ANTIC_MODE; + + switch( new_cmd & ANTIC_MODE ) + { + case 0x00: + /* generate 1 .. 8 empty lines */ + antic.modelines = ((new_cmd >> 4) & 7) + 1; + /* did the last ANTIC command have vertical scroll enabled ? */ + if( antic.cmd & ANTIC_VSCR ) + { + /* yes, generate vscrol_old additional empty lines */ + antic.modelines += antic.vscrol_old; + } + /* leave only bit 7 (DLI) set in ANTIC command */ + new_cmd &= ANTIC_DLI; + break; + case 0x01: + /* ANTIC "jump" with DLI: issue interrupt immediately */ + if( new_cmd & ANTIC_DLI ) + { + /* remove the DLI bit */ + new_cmd &= ~ANTIC_DLI; + timer_set(ANTIC_TIME_FROM_CYCLES(CYCLES_DLI_NMI), TIMER_ISSUE_DLI); + } + /* load memory scan bit set ? */ + if( new_cmd & ANTIC_LMS ) + { + int addr = RDANTIC(space); + antic.doffs = (antic.doffs + 1) & DOFFS; + addr += 256 * RDANTIC(space); + antic.dpage = addr & DPAGE; + antic.doffs = addr & DOFFS; + /* produce empty scanlines until vblank start */ + antic.modelines = VBL_START + 1 - antic.scanline; + if( antic.modelines < 0 ) + antic.modelines = machine().first_screen()->height() - antic.scanline; + LOG((" JVB $%04x\n", antic.dpage|antic.doffs)); + } + else + { + int addr = RDANTIC(space); + antic.doffs = (antic.doffs + 1) & DOFFS; + addr += 256 * RDANTIC(space); + antic.dpage = addr & DPAGE; + antic.doffs = addr & DOFFS; + /* produce a single empty scanline */ + antic.modelines = 1; + LOG((" JMP $%04x\n", antic.dpage|antic.doffs)); + } + break; + case 0x02: + LMS(new_cmd); + antic.chbase = (antic.w.chbash & 0xfc) << 8; + antic.modelines = 8 - (vscrol_subtract & 7); + if( antic.w.chactl & 4 ) /* decrement chbasl? */ + antic.w.chbasl = antic.modelines - 1; + break; + case 0x03: + LMS(new_cmd); + antic.chbase = (antic.w.chbash & 0xfc) << 8; + antic.modelines = 10 - (vscrol_subtract & 9); + if( antic.w.chactl & 4 ) /* decrement chbasl? */ + antic.w.chbasl = antic.modelines - 1; + break; + case 0x04: + LMS(new_cmd); + antic.chbase = (antic.w.chbash & 0xfc) << 8; + antic.modelines = 8 - (vscrol_subtract & 7); + if( antic.w.chactl & 4 ) /* decrement chbasl? */ + antic.w.chbasl = antic.modelines - 1; + break; + case 0x05: + LMS(new_cmd); + antic.chbase = (antic.w.chbash & 0xfc) << 8; + antic.modelines = 16 - (vscrol_subtract & 15); + if( antic.w.chactl & 4 ) /* decrement chbasl? */ + antic.w.chbasl = antic.modelines - 1; + break; + case 0x06: + LMS(new_cmd); + antic.chbase = (antic.w.chbash & 0xfe) << 8; + antic.modelines = 8 - (vscrol_subtract & 7); + if( antic.w.chactl & 4 ) /* decrement chbasl? */ + antic.w.chbasl = antic.modelines - 1; + break; + case 0x07: + LMS(new_cmd); + antic.chbase = (antic.w.chbash & 0xfe) << 8; + antic.modelines = 16 - (vscrol_subtract & 15); + if( antic.w.chactl & 4 ) /* decrement chbasl? */ + antic.w.chbasl = antic.modelines - 1; + break; + case 0x08: + LMS(new_cmd); + antic.modelines = 8 - (vscrol_subtract & 7); + break; + case 0x09: + LMS(new_cmd); + antic.modelines = 4 - (vscrol_subtract & 3); + break; + case 0x0a: + LMS(new_cmd); + antic.modelines = 4 - (vscrol_subtract & 3); + break; + case 0x0b: + LMS(new_cmd); + antic.modelines = 2 - (vscrol_subtract & 1); + break; + case 0x0c: + LMS(new_cmd); + antic.modelines = 1; + break; + case 0x0d: + LMS(new_cmd); + antic.modelines = 2 - (vscrol_subtract & 1); + break; + case 0x0e: + LMS(new_cmd); + antic.modelines = 1; + break; + case 0x0f: + LMS(new_cmd); + /* bits 6+7 of the priority select register determine */ + /* if newer GTIA or plain graphics modes are used */ + switch (m_gtia->get_w_prior() >> 6) + { + case 0: break; + case 1: m_antic_render2 = 16; break; + case 2: m_antic_render2 = 17; break; + case 3: m_antic_render2 = 18; break; + } + antic.modelines = 1; + break; + } + /* set new (current) antic command */ + antic.cmd = new_cmd; + } + } + else + { + LOG((" out of visible range\n")); + antic.cmd = 0x00; + m_antic_render1 = 0; + m_antic_render2 = 0; + m_antic_render3 = 0; + } + } + else + { + LOG((" DMA is off\n")); + antic.cmd = 0x00; + m_antic_render1 = 0; + m_antic_render2 = 0; + m_antic_render3 = 0; + } + + antic.r.nmist &= ~DLI_NMI; + if (antic.modelines == 1 && (antic.cmd & antic.w.nmien & DLI_NMI)) + timer_set(ANTIC_TIME_FROM_CYCLES(CYCLES_DLI_NMI), TIMER_ISSUE_DLI); + + timer_set(ANTIC_TIME_FROM_CYCLES(CYCLES_HSTART), TIMER_LINE_REND); +} diff --git a/src/mame/video/atari.c b/src/mame/video/atari.c index 95b2cc35f9a..cc571306ed8 100644 --- a/src/mame/video/atari.c +++ b/src/mame/video/atari.c @@ -34,668 +34,6 @@ void atari_common_state::video_start() antic_vstart(machine()); } -/************************************************************************ - * atari_vh_screenrefresh - * Refresh screen bitmap. - * Note: Actual drawing is done scanline wise during atari_interrupt - ************************************************************************/ -UINT32 atari_common_state::screen_update_atari(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) -{ - UINT32 new_tv_artifacts = screen.ioport("artifacts")->read_safe(0); - copybitmap(bitmap, *antic.bitmap, 0, 0, 0, 0, cliprect); - - if (tv_artifacts != new_tv_artifacts) - tv_artifacts = new_tv_artifacts; - - return 0; -} - -void atari_common_state::artifacts_gfx(UINT8 *src, UINT8 *dst, int width) -{ - UINT8 n, bits = 0; - UINT8 b = m_gtia->get_w_colbk() & 0xf0; - UINT8 c = m_gtia->get_w_colpf1() & 0x0f; - UINT8 atari_A = ((b + 0x30) & 0xf0) + c; - UINT8 atari_B = ((b + 0x70) & 0xf0) + c; - UINT8 atari_C = b + c; - UINT8 atari_D = m_gtia->get_w_colbk(); - UINT16 *color_lookup = m_gtia->get_color_lookup(); - - for (int x = 0; x < width * 4; x++) - { - n = *src++; - bits <<= 2; - switch( n ) - { - case G00: - break; - case G01: - bits |= 1; - break; - case G10: - bits |= 2; - break; - case G11: - bits |= 3; - break; - default: - *dst++ = color_lookup[n]; - *dst++ = color_lookup[n]; - continue; - } - switch ((bits >> 1) & 7) - { - case 0: /* 0 0 0 */ - case 1: /* 0 0 1 */ - case 4: /* 1 0 0 */ - *dst++ = atari_D; - break; - case 3: /* 0 1 1 */ - case 6: /* 1 1 0 */ - case 7: /* 1 1 1 */ - *dst++ = atari_C; - break; - case 2: /* 0 1 0 */ - *dst++ = atari_B; - break; - case 5: /* 1 0 1 */ - *dst++ = atari_A; - break; - } - switch (bits & 7) - { - case 0: /* 0 0 0 */ - case 1: /* 0 0 1 */ - case 4: /* 1 0 0 */ - *dst++ = atari_D; - break; - case 3: /* 0 1 1 */ - case 6: /* 1 1 0 */ - case 7: /* 1 1 1 */ - *dst++ = atari_C; - break; - case 2: /* 0 1 0 */ - *dst++ = atari_A; - break; - case 5: /* 1 0 1 */ - *dst++ = atari_B; - break; - } - } -} - -void atari_common_state::artifacts_txt(UINT8 * src, UINT8 * dst, int width) -{ - UINT8 n, bits = 0; - UINT8 b = m_gtia->get_w_colpf2() & 0xf0; - UINT8 c = m_gtia->get_w_colpf1() & 0x0f; - UINT8 atari_A = ((b+0x30)&0xf0)+c; - UINT8 atari_B = ((b+0x70)&0xf0)+c; - UINT8 atari_C = b+c; - UINT8 atari_D = m_gtia->get_w_colpf2(); - UINT16 *color_lookup = m_gtia->get_color_lookup(); - - for (int x = 0; x < width * 4; x++) - { - n = *src++; - bits <<= 2; - switch( n ) - { - case T00: - break; - case T01: - bits |= 1; - break; - case T10: - bits |= 2; - break; - case T11: - bits |= 3; - break; - default: - *dst++ = color_lookup[n]; - *dst++ = color_lookup[n]; - continue; - } - switch( (bits >> 1) & 7 ) - { - case 0: /* 0 0 0 */ - case 1: /* 0 0 1 */ - case 4: /* 1 0 0 */ - *dst++ = atari_D; - break; - case 3: /* 0 1 1 */ - case 6: /* 1 1 0 */ - case 7: /* 1 1 1 */ - *dst++ = atari_C; - break; - case 2: /* 0 1 0 */ - *dst++ = atari_A; - break; - case 5: /* 1 0 1 */ - *dst++ = atari_B; - break; - } - switch( bits & 7 ) - { - case 0:/* 0 0 0 */ - case 1:/* 0 0 1 */ - case 4:/* 1 0 0 */ - *dst++ = atari_D; - break; - case 3: /* 0 1 1 */ - case 6: /* 1 1 0 */ - case 7: /* 1 1 1 */ - *dst++ = atari_C; - break; - case 2: /* 0 1 0 */ - *dst++ = atari_B; - break; - case 5: /* 1 0 1 */ - *dst++ = atari_A; - break; - } - } -} - - -void atari_common_state::antic_linerefresh() -{ - int x, y; - UINT8 *src; - UINT32 *dst; - UINT32 scanline[4 + (HCHARS * 2) + 4]; - UINT16 *color_lookup = m_gtia->get_color_lookup(); - - /* increment the scanline */ - if( ++antic.scanline == machine().first_screen()->height() ) - { - /* and return to the top if the frame was complete */ - antic.scanline = 0; - antic.modelines = 0; - /* count frames gone since last write to hitclr */ - m_gtia->count_hitclr_frames(); - } - - if( antic.scanline < MIN_Y || antic.scanline > MAX_Y ) - return; - - y = antic.scanline - MIN_Y; - src = &antic.cclock[PMOFFSET - antic.hscrol_old + 12]; - dst = scanline; - - if( tv_artifacts ) - { - if( (antic.cmd & 0x0f) == 2 || (antic.cmd & 0x0f) == 3 ) - { - artifacts_txt(src, (UINT8*)(dst + 3), HCHARS); - return; - } - else - if( (antic.cmd & 0x0f) == 15 ) - { - artifacts_gfx(src, (UINT8*)(dst + 3), HCHARS); - return; - } - } - dst[0] = color_lookup[PBK] | color_lookup[PBK] << 16; - dst[1] = color_lookup[PBK] | color_lookup[PBK] << 16; - dst[2] = color_lookup[PBK] | color_lookup[PBK] << 16; - if ( (antic.cmd & ANTIC_HSCR) == 0 || (antic.pfwidth == 48) || (antic.pfwidth == 32)) - { - /* no hscroll */ - dst[3] = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; - src += 2; - dst += 4; - for( x = 1; x < HCHARS-1; x++ ) - { - *dst++ = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; - *dst++ = color_lookup[src[BYTE_XOR_LE(2)]] | color_lookup[src[BYTE_XOR_LE(3)]] << 16; - src += 4; - } - dst[0] = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; - } - else - { - /* if hscroll is enabled, more data are fetched by ANTIC, but it still renders playfield - of width defined by pfwidth. */ - switch( antic.pfwidth ) - { - case 0: - { - dst[3] = color_lookup[PBK] | color_lookup[PBK] << 16; - dst += 4; - for ( x = 1; x < HCHARS-1; x++ ) - { - *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; - *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; - } - dst[0] = color_lookup[PBK] | color_lookup[PBK] << 16; - } - break; - /* support for narrow playfield (32) with horizontal scrolling should be added here */ - case 40: - { - dst[3] = color_lookup[PBK] | color_lookup[PBK] << 16; - dst += 4; - for ( x = 1; x < HCHARS-2; x++ ) - { - if ( x == 1 ) - { - *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; - } - else - { - *dst++ = color_lookup[src[BYTE_XOR_LE(0)]] | color_lookup[src[BYTE_XOR_LE(1)]] << 16; - } - *dst++ = color_lookup[src[BYTE_XOR_LE(2)]] | color_lookup[src[BYTE_XOR_LE(3)]] << 16; - src += 4; - } - for ( ; x < HCHARS-1; x++ ) - { - *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; - *dst++ = color_lookup[PBK] | color_lookup[PBK] << 16; - } - dst[0] = color_lookup[PBK] | color_lookup[PBK] << 16; - } - break; - } - } - dst[1] = color_lookup[PBK] | color_lookup[PBK] << 16; - dst[2] = color_lookup[PBK] | color_lookup[PBK] << 16; - dst[3] = color_lookup[PBK] | color_lookup[PBK] << 16; - - draw_scanline8(*antic.bitmap, 12, y, MIN(antic.bitmap->width() - 12, sizeof(scanline)), (const UINT8 *) scanline, NULL); -} - -int atari_common_state::cycle() -{ - return machine().first_screen()->hpos() * CYCLES_PER_LINE / machine().first_screen()->width(); -} - -void atari_common_state::after(int cycles, timer_expired_delegate function) -{ - attotime duration = machine().first_screen()->scan_period() * cycles / CYCLES_PER_LINE; - //(void)funcname; - //LOG((" after %3d (%5.1f us) %s\n", cycles, duration.as_double() * 1.0e6, funcname)); - machine().scheduler().timer_set(duration, function); -} - -TIMER_CALLBACK_MEMBER( atari_common_state::antic_issue_dli ) -{ - if( antic.w.nmien & DLI_NMI ) - { - LOG((" @cycle #%3d issue DLI\n", cycle())); - antic.r.nmist |= DLI_NMI; - machine().device("maincpu")->execute().set_input_line(INPUT_LINE_NMI, PULSE_LINE); - } - else - { - LOG((" @cycle #%3d DLI not enabled\n", cycle())); - } -} - - -/***************************************************************************** - * - * Antic Line Done - * - *****************************************************************************/ -TIMER_CALLBACK_MEMBER( atari_common_state::antic_line_done ) -{ - LOG((" @cycle #%3d antic_line_done\n", cycle())); - if( antic.w.wsync ) - { - LOG((" @cycle #%3d release WSYNC\n", cycle())); - /* release the CPU if it was actually waiting for HSYNC */ - machine().scheduler().trigger(TRIGGER_HSYNC); - /* and turn off the 'wait for hsync' flag */ - antic.w.wsync = 0; - } - LOG((" @cycle #%3d release CPU\n", cycle())); - /* release the CPU (held for emulating cycles stolen by ANTIC DMA) */ - machine().scheduler().trigger(TRIGGER_STEAL); - - /* refresh the display (translate color clocks to pixels) */ - antic_linerefresh(); -} - -/***************************************************************************** - * - * Antic Steal Cycles - * This is called once per scanline by a interrupt issued in the - * atari_scanline_render function. Set a new timer for the HSYNC - * position and release the CPU; but hold it again immediately until - * TRIGGER_HSYNC if WSYNC (D01A) was accessed - * - *****************************************************************************/ -TIMER_CALLBACK_MEMBER( atari_common_state::antic_steal_cycles ) -{ - LOG((" @cycle #%3d steal %d cycles\n", cycle(), antic.steal_cycles)); - after(antic.steal_cycles, timer_expired_delegate(FUNC(atari_common_state::antic_line_done),this)); - antic.steal_cycles = 0; - machine().device("maincpu")->execute().spin_until_trigger(TRIGGER_STEAL ); -} - - -/***************************************************************************** - * - * Antic Scan Line Render - * Render the scanline to the scrbitmap buffer. - * Also transport player/missile data to the grafp and grafm registers - * of the GTIA if enabled (DMA_PLAYER or DMA_MISSILE) - * - *****************************************************************************/ -TIMER_CALLBACK_MEMBER( atari_common_state::antic_scanline_render ) -{ - address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); - - LOG((" @cycle #%3d render mode $%X lines to go #%d\n", cycle(), (antic.cmd & 0x0f), antic.modelines)); - - antic_render(space, m_antic_render1, m_antic_render2, m_antic_render3); - - /* if player/missile graphics is enabled */ - if( antic.scanline < 256 && (antic.w.dmactl & (DMA_PLAYER|DMA_MISSILE)) ) - { - /* new player/missile graphics data for every scanline ? */ - if( antic.w.dmactl & DMA_PM_DBLLINE ) - { - /* transport missile data to GTIA ? */ - if( antic.w.dmactl & DMA_MISSILE ) - { - antic.steal_cycles += 1; - m_gtia->write(space, 0x11, RDPMGFXD(space, 3*256)); - } - /* transport player data to GTIA ? */ - if( antic.w.dmactl & DMA_PLAYER ) - { - antic.steal_cycles += 4; - m_gtia->write(space, 0x0d, RDPMGFXD(space, 4*256)); - m_gtia->write(space, 0x0e, RDPMGFXD(space, 5*256)); - m_gtia->write(space, 0x0f, RDPMGFXD(space, 6*256)); - m_gtia->write(space, 0x10, RDPMGFXD(space, 7*256)); - } - } - else - { - /* transport missile data to GTIA ? */ - if( antic.w.dmactl & DMA_MISSILE ) - { - if( (antic.scanline & 1) == 0 ) /* even line ? */ - antic.steal_cycles += 1; - m_gtia->write(space, 0x11, RDPMGFXS(space, 3*128)); - } - /* transport player data to GTIA ? */ - if( antic.w.dmactl & DMA_PLAYER ) - { - if( (antic.scanline & 1) == 0 ) /* even line ? */ - antic.steal_cycles += 4; - m_gtia->write(space, 0x0d, RDPMGFXS(space, 4*128)); - m_gtia->write(space, 0x0e, RDPMGFXS(space, 5*128)); - m_gtia->write(space, 0x0f, RDPMGFXS(space, 6*128)); - m_gtia->write(space, 0x10, RDPMGFXS(space, 7*128)); - } - } - } - - if (antic.scanline >= VBL_END && antic.scanline < 256) - m_gtia->render((UINT8 *)antic.pmbits + PMOFFSET, (UINT8 *)antic.cclock + PMOFFSET - antic.hscrol_old, (UINT8 *)antic.prio_table[m_gtia->get_w_prior() & 0x3f], (UINT8 *)&antic.pmbits); - - antic.steal_cycles += CYCLES_REFRESH; - LOG((" run CPU for %d cycles\n", CYCLES_HSYNC - CYCLES_HSTART - antic.steal_cycles)); - after(CYCLES_HSYNC - CYCLES_HSTART - antic.steal_cycles, timer_expired_delegate(FUNC(atari_common_state::antic_steal_cycles),this)); -} - - - -void atari_common_state::LMS(int new_cmd) -{ - /************************************************************** - * If the LMS bit (load memory scan) of the current display - * list command is set, load the video source address from the - * following two bytes and split it up into video page/offset. - * Steal two more cycles from the CPU for fetching the address. - **************************************************************/ - if( new_cmd & ANTIC_LMS ) - { - address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); - int addr = RDANTIC(space); - antic.doffs = (antic.doffs + 1) & DOFFS; - addr += 256 * RDANTIC(space); - antic.doffs = (antic.doffs + 1) & DOFFS; - antic.vpage = addr & VPAGE; - antic.voffs = addr & VOFFS; - LOG((" LMS $%04x\n", addr)); - /* steal two more clock cycles from the cpu */ - antic.steal_cycles += 2; - } -} - -/***************************************************************************** - * - * Antic Scan Line DMA - * This is called once per scanline from Atari Interrupt - * If the ANTIC DMA is active (DMA_ANTIC) and the scanline not inside - * the VBL range (VBL_START - TOTAL_LINES or 0 - VBL_END) - * check if all mode lines of the previous ANTIC command were done and - * if so, read a new command and set up the renderer function - * - *****************************************************************************/ -void atari_common_state::antic_scanline_dma(int param) -{ - address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); - LOG((" @cycle #%3d DMA fetch\n", cycle())); - if (antic.scanline == VBL_END) - antic.r.nmist &= ~VBL_NMI; - if( antic.w.dmactl & DMA_ANTIC ) - { - if( antic.scanline >= VBL_END && antic.scanline < VBL_START ) - { - if( antic.modelines <= 0 ) - { - m_antic_render1 = 0; - m_antic_render3 = antic.w.dmactl & 3; - UINT8 vscrol_subtract = 0; - UINT8 new_cmd; - - new_cmd = RDANTIC(space); - antic.doffs = (antic.doffs + 1) & DOFFS; - /* steal at one clock cycle from the CPU for fetching the command */ - antic.steal_cycles += 1; - LOG((" ANTIC CMD $%02x\n", new_cmd)); - /* command 1 .. 15 ? */ - if (new_cmd & ANTIC_MODE) - { - antic.w.chbasl = 0; - /* vertical scroll mode changed ? */ - if( (antic.cmd ^ new_cmd) & ANTIC_VSCR ) - { - /* vertical scroll activate now ? */ - if( new_cmd & ANTIC_VSCR ) - { - antic.vscrol_old = - vscrol_subtract = - antic.w.chbasl = antic.w.vscrol; - } - else - { - vscrol_subtract = ~antic.vscrol_old; - } - } - /* does this command have horizontal scroll enabled ? */ - if( new_cmd & ANTIC_HSCR ) - { - m_antic_render1 = 1; - antic.hscrol_old = antic.w.hscrol; - } - else - { - antic.hscrol_old = 0; - } - } - /* Set the ANTIC mode renderer function */ - m_antic_render2 = new_cmd & ANTIC_MODE; - - switch( new_cmd & ANTIC_MODE ) - { - case 0x00: - /* generate 1 .. 8 empty lines */ - antic.modelines = ((new_cmd >> 4) & 7) + 1; - /* did the last ANTIC command have vertical scroll enabled ? */ - if( antic.cmd & ANTIC_VSCR ) - { - /* yes, generate vscrol_old additional empty lines */ - antic.modelines += antic.vscrol_old; - } - /* leave only bit 7 (DLI) set in ANTIC command */ - new_cmd &= ANTIC_DLI; - break; - case 0x01: - /* ANTIC "jump" with DLI: issue interrupt immediately */ - if( new_cmd & ANTIC_DLI ) - { - /* remove the DLI bit */ - new_cmd &= ~ANTIC_DLI; - after(CYCLES_DLI_NMI, timer_expired_delegate(FUNC(atari_common_state::antic_issue_dli),this)); - } - /* load memory scan bit set ? */ - if( new_cmd & ANTIC_LMS ) - { - int addr = RDANTIC(space); - antic.doffs = (antic.doffs + 1) & DOFFS; - addr += 256 * RDANTIC(space); - antic.dpage = addr & DPAGE; - antic.doffs = addr & DOFFS; - /* produce empty scanlines until vblank start */ - antic.modelines = VBL_START + 1 - antic.scanline; - if( antic.modelines < 0 ) - antic.modelines = machine().first_screen()->height() - antic.scanline; - LOG((" JVB $%04x\n", antic.dpage|antic.doffs)); - } - else - { - int addr = RDANTIC(space); - antic.doffs = (antic.doffs + 1) & DOFFS; - addr += 256 * RDANTIC(space); - antic.dpage = addr & DPAGE; - antic.doffs = addr & DOFFS; - /* produce a single empty scanline */ - antic.modelines = 1; - LOG((" JMP $%04x\n", antic.dpage|antic.doffs)); - } - break; - case 0x02: - LMS(new_cmd); - antic.chbase = (antic.w.chbash & 0xfc) << 8; - antic.modelines = 8 - (vscrol_subtract & 7); - if( antic.w.chactl & 4 ) /* decrement chbasl? */ - antic.w.chbasl = antic.modelines - 1; - break; - case 0x03: - LMS(new_cmd); - antic.chbase = (antic.w.chbash & 0xfc) << 8; - antic.modelines = 10 - (vscrol_subtract & 9); - if( antic.w.chactl & 4 ) /* decrement chbasl? */ - antic.w.chbasl = antic.modelines - 1; - break; - case 0x04: - LMS(new_cmd); - antic.chbase = (antic.w.chbash & 0xfc) << 8; - antic.modelines = 8 - (vscrol_subtract & 7); - if( antic.w.chactl & 4 ) /* decrement chbasl? */ - antic.w.chbasl = antic.modelines - 1; - break; - case 0x05: - LMS(new_cmd); - antic.chbase = (antic.w.chbash & 0xfc) << 8; - antic.modelines = 16 - (vscrol_subtract & 15); - if( antic.w.chactl & 4 ) /* decrement chbasl? */ - antic.w.chbasl = antic.modelines - 1; - break; - case 0x06: - LMS(new_cmd); - antic.chbase = (antic.w.chbash & 0xfe) << 8; - antic.modelines = 8 - (vscrol_subtract & 7); - if( antic.w.chactl & 4 ) /* decrement chbasl? */ - antic.w.chbasl = antic.modelines - 1; - break; - case 0x07: - LMS(new_cmd); - antic.chbase = (antic.w.chbash & 0xfe) << 8; - antic.modelines = 16 - (vscrol_subtract & 15); - if( antic.w.chactl & 4 ) /* decrement chbasl? */ - antic.w.chbasl = antic.modelines - 1; - break; - case 0x08: - LMS(new_cmd); - antic.modelines = 8 - (vscrol_subtract & 7); - break; - case 0x09: - LMS(new_cmd); - antic.modelines = 4 - (vscrol_subtract & 3); - break; - case 0x0a: - LMS(new_cmd); - antic.modelines = 4 - (vscrol_subtract & 3); - break; - case 0x0b: - LMS(new_cmd); - antic.modelines = 2 - (vscrol_subtract & 1); - break; - case 0x0c: - LMS(new_cmd); - antic.modelines = 1; - break; - case 0x0d: - LMS(new_cmd); - antic.modelines = 2 - (vscrol_subtract & 1); - break; - case 0x0e: - LMS(new_cmd); - antic.modelines = 1; - break; - case 0x0f: - LMS(new_cmd); - /* bits 6+7 of the priority select register determine */ - /* if newer GTIA or plain graphics modes are used */ - switch (m_gtia->get_w_prior() >> 6) - { - case 0: break; - case 1: m_antic_render2 = 16; break; - case 2: m_antic_render2 = 17; break; - case 3: m_antic_render2 = 18; break; - } - antic.modelines = 1; - break; - } - /* set new (current) antic command */ - antic.cmd = new_cmd; - } - } - else - { - LOG((" out of visible range\n")); - antic.cmd = 0x00; - m_antic_render1 = 0; - m_antic_render2 = 0; - m_antic_render3 = 0; - } - } - else - { - LOG((" DMA is off\n")); - antic.cmd = 0x00; - m_antic_render1 = 0; - m_antic_render2 = 0; - m_antic_render3 = 0; - } - - antic.r.nmist &= ~DLI_NMI; - if( antic.modelines == 1 && (antic.cmd & antic.w.nmien & DLI_NMI) ) - after(CYCLES_DLI_NMI, timer_expired_delegate(FUNC(atari_common_state::antic_issue_dli),this)); - - after(CYCLES_HSTART, timer_expired_delegate(FUNC(atari_common_state::antic_scanline_render),this)); -} /***************************************************************************** *