some more antic refactorization. nw.

This commit is contained in:
Fabio Priuli 2014-09-08 19:40:45 +00:00
parent f156d1704d
commit 885a377e22
3 changed files with 685 additions and 662 deletions

View File

@ -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<gtia_device> m_gtia;
UINT32 tv_artifacts;
void prio_init();

View File

@ -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);
}

View File

@ -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));
}
/*****************************************************************************
*