diff options
Diffstat (limited to 'src/mame/video/avgdvg.c')
-rw-r--r-- | src/mame/video/avgdvg.c | 1523 |
1 files changed, 1523 insertions, 0 deletions
diff --git a/src/mame/video/avgdvg.c b/src/mame/video/avgdvg.c new file mode 100644 index 00000000000..85a82b03205 --- /dev/null +++ b/src/mame/video/avgdvg.c @@ -0,0 +1,1523 @@ +/************************************************************************* + + avgdvg.c: Atari DVG and AVG + + Some parts of this code are based on the original version by Eric + Smith, Brad Oliver, Bernd Wiebelt, Aaron Giles, Andrew Caldwell + + The schematics and Jed Margolin's article on Vector Generators were + very helpful in understanding the hardware. + + +**************************************************************************/ + +#include "driver.h" +#include "avgdvg.h" +#include "video/vector.h" + + +/************************************* + * + * Global variables + * + *************************************/ + +UINT8 *tempest_colorram; +UINT8 *mhavoc_colorram; +UINT16 *quantum_colorram; + +UINT16 *quantum_vectorram; + + + +/************************************* + * + * Macros and defines + * + *************************************/ + +#define MASTER_CLOCK (12096000) +#define VGSLICE (10000) +#define MAXVECT (10000) + +#define VGVECTOR 0 +#define VGCLIP 1 + +#define OP0 (vg->op & 1) +#define OP1 (vg->op & 2) +#define OP2 (vg->op & 4) +#define OP3 (vg->op & 8) + +#define ST3 (vg->state_latch & 8) + + +/************************************* + * + * Typedefs + * + *************************************/ + +typedef struct _vgvector +{ + int x; int y; + rgb_t color; + int intensity; + int arg1; int arg2; + int status; +} vgvector; + +typedef struct _vgdata +{ + UINT16 pc; + UINT8 sp; + UINT16 dvx; + UINT16 dvy; + UINT8 dvy12; + UINT16 timer; + UINT16 stack[4]; + UINT16 data; + + UINT8 state_latch; + UINT8 int_latch; + UINT8 scale; + UINT8 bin_scale; + UINT8 intensity; + UINT8 color; + UINT8 enspkl; + UINT8 spkl_shift; + UINT8 map; + + UINT16 hst; + UINT16 lst; + UINT16 izblank; + + UINT8 op; + UINT8 halt; + UINT8 sync_halt; + + UINT16 xdac_xor; + UINT16 ydac_xor; + + INT32 xpos; + INT32 ypos; + + INT32 clipx_min; + INT32 clipy_min; + INT32 clipx_max; + INT32 clipy_max; +} vgdata; + +typedef struct _vgconf +{ + int (*handler[8])(vgdata *); + UINT8 (*state_addr)(vgdata *); + void (*update_databus)(vgdata *); + void (*vggo)(vgdata *); + void (*vgrst)(vgdata *); +} vgconf; + + +/************************************* + * + * Static variables + * + *************************************/ + +static int xmin, xmax, ymin, ymax; +static int xcenter, ycenter; +static emu_timer *vg_run_timer, *vg_halt_timer; + +static int flip_x, flip_y; + +static vgdata vgd, *vg; +static vgconf *vgc; +static int nvect; +static vgvector vectbuf[MAXVECT]; + + +/************************************* + * + * Flipping + * + *************************************/ + +void avg_set_flip_x(int flip) +{ + flip_x = flip; +} + +void avg_set_flip_y(int flip) +{ + flip_y = flip; +} + +static void avg_apply_flipping(int *x, int *y) +{ + if (flip_x) + *x += (xcenter - *x) << 1; + if (flip_y) + *y += (ycenter - *y) << 1; +} + + +/************************************* + * + * Vector buffering + * + *************************************/ + +static void vg_flush (void) +{ + int i; + + for (i = 0; i < nvect; i++) + { + if (vectbuf[i].status == VGVECTOR) + vector_add_point(vectbuf[i].x, vectbuf[i].y, vectbuf[i].color, vectbuf[i].intensity); + + if (vectbuf[i].status == VGCLIP) + vector_add_clip(vectbuf[i].x, vectbuf[i].y, vectbuf[i].arg1, vectbuf[i].arg2); + } + + nvect=0; +} + +static void vg_add_point_buf(int x, int y, rgb_t color, int intensity) +{ + if (nvect < MAXVECT) + { + vectbuf[nvect].status = VGVECTOR; + vectbuf[nvect].x = x; + vectbuf[nvect].y = y; + vectbuf[nvect].color = color; + vectbuf[nvect].intensity = intensity; + nvect++; + } +} + +static void vg_add_clip (int xmin, int ymin, int xmax, int ymax) +{ + if (nvect < MAXVECT) + { + vectbuf[nvect].status = VGCLIP; + vectbuf[nvect].x = xmin; + vectbuf[nvect].y = ymin; + vectbuf[nvect].arg1 = xmax; + vectbuf[nvect].arg2 = ymax; + nvect++; + } +} + + +/************************************* + * + * DVG handler functions + * + *************************************/ + +static void dvg_data(vgdata *vg) +{ + /* + * DVG uses low bit of state for address + */ + vg->data = vectorram[(vg->pc << 1) | (vg->state_latch & 1)]; +} + +static UINT8 dvg_state_addr(vgdata *vg) +{ + UINT8 addr; + + addr =((((vg->state_latch >> 4) ^ 1) & 1) << 7) | (vg->state_latch & 0xf); + + if (OP3) + addr |= ((vg->op & 7) << 4); + + return addr; +} + +static int dvg_dmapush(vgdata *vg) +{ + if (OP0 == 0) + { + vg->sp = (vg->sp + 1) & 0xf; + vg->stack[vg->sp & 3] = vg->pc; + } + return 0; +} + +static int dvg_dmald(vgdata *vg) +{ + if (OP0) + { + vg->pc = vg->stack[vg->sp & 3]; + vg->sp = (vg->sp - 1) & 0xf; + } + else + { + vg->pc = vg->dvy; + } + + return 0; +} + +static void dvg_draw_to(int x, int y, int intensity) +{ + if (((x | y) & 0x400) == 0) + vg_add_point_buf(xcenter + ((x - 0x200) << 16), + ycenter - ((y - 0x200) << 16), + VECTOR_COLOR111(7), intensity << 4); +} + +static int dvg_gostrobe(vgdata *vg) +{ + int scale, fin, dx, dy, c, mx, my, countx, county, bit, cycles; + + if (vg->op == 0xf) + { + + scale = (vg->scale + + (((vg->dvy & 0x800) >> 11) + | (((vg->dvx & 0x800) ^ 0x800) >> 10) + | ((vg->dvx & 0x800) >> 9))) & 0xf; + + vg->dvy &= 0xf00; + vg->dvx &= 0xf00; + } + else + { + scale = (vg->scale + vg->op) & 0xf; + } + + fin = 0xfff - (((2 << scale) & 0x7ff) ^ 0xfff); + + /* Count up or down */ + dx = (vg->dvx & 0x400)? -1: +1; + dy = (vg->dvy & 0x400)? -1: +1; + + /* Scale factor for rate multipliers */ + mx = (vg->dvx << 2) & 0xfff; + my = (vg->dvy << 2) & 0xfff; + + cycles = 8 * fin; + c=0; + + while (fin--) + { + + /* + * The 7497 Bit Rate Multiplier is a 6 bit counter with + * clever decoding of output bits to perform the following + * operation: + * + * fout = m/64 * fin + * + * where fin is the input frequency, fout is the output + * frequency and m is a factor at the input pins. Output + * pulses are more or less evenly spaced so we get straight + * lines. The DVG has two cascaded 7497s for each coordinate. + */ + + countx = 0; + county = 0; + + for (bit = 0; bit < 12; bit++) + { + if ((c & ((1 << (bit+1)) - 1)) == ((1 << bit) - 1)) + { + if (mx & (1 << (11 - bit))) + countx = 1; + + if (my & (1 << (11 - bit))) + county = 1; + } + } + + c = (c + 1) & 0xfff; + + /* + * Since x- and y-counters always hold the correct count + * wrt. to each other, we can do clipping exactly like the + * hardware does. That is, as soon as any counter's bit 10 + * changes to high, we finish the vector. If bit 10 changes + * from high to low, we start a new vector. + */ + + if (countx) + { + /* Is y valid and x entering or leaving the valid range? */ + if (((vg->ypos & 0x400) == 0) + && ((vg->xpos ^ (vg->xpos + dx)) & 0x400)) + { + if ((vg->xpos + dx) & 0x400) + /* We are leaving the valid range */ + dvg_draw_to(vg->xpos, vg->ypos, vg->intensity); + else + /* We are entering the valid range */ + dvg_draw_to((vg->xpos + dx) & 0xfff, vg->ypos, 0); + } + vg->xpos = (vg->xpos + dx) & 0xfff; + } + + if (county) + { + if (((vg->xpos & 0x400) == 0) + && ((vg->ypos ^ (vg->ypos + dy)) & 0x400)) + { + if ((vg->xpos & 0x400) == 0) + { + if ((vg->ypos + dy) & 0x400) + dvg_draw_to(vg->xpos, vg->ypos, vg->intensity); + else + dvg_draw_to(vg->xpos, (vg->ypos + dy) & 0xfff, 0); + } + } + vg->ypos = (vg->ypos + dy) & 0xfff; + } + + } + + dvg_draw_to(vg->xpos, vg->ypos, vg->intensity); + + return cycles; +} + +static int dvg_haltstrobe(vgdata *vg) +{ + vg->halt = OP0; + + if (OP0 == 0) + { + vg->xpos = vg->dvx & 0xfff; + vg->ypos = vg->dvy & 0xfff; + dvg_draw_to(vg->xpos, vg->ypos, 0); + } + return 0; +} + +static int dvg_latch3(vgdata *vg) +{ + vg->dvx = (vg->dvx & 0xff) | ((vg->data & 0xf) << 8); + vg->intensity = vg->data >> 4; + return 0; +} + +static int dvg_latch2(vgdata *vg) +{ + vg->dvx &= 0xf00; + if (vg->op != 0xf) + vg->dvx = (vg->dvx & 0xf00) | vg->data; + + if ((vg->op & 0xa) == 0xa) + vg->scale = vg->intensity; + + vg->pc++; + return 0; +} + +static int dvg_latch1(vgdata *vg) +{ + vg->dvy = (vg->dvy & 0xff) + | ((vg->data & 0xf) << 8); + vg->op = vg->data >> 4; + + if (vg->op == 0xf) + { + vg->dvx &= 0xf00; + vg->dvy &= 0xf00; + } + + return 0; +} + +static int dvg_latch0(vgdata *vg) +{ + vg->dvy &= 0xf00; + if (vg->op == 0xf) + dvg_latch3(vg); + else + vg->dvy = (vg->dvy & 0xf00) | vg->data; + + vg->pc++; + return 0; +} + + +/******************************************************************** + * + * AVG handler functions + * + * AVG is in many ways different from DVG. The only thing they have + * in common is the state machine approach. There are small + * differences among the AVGs, mostly related to color and vector + * clipping. + * + *******************************************************************/ + +static void avg_data(vgdata *vg) +{ + vg->data = vectorram[vg->pc ^ 1]; +} + +static void starwars_data(vgdata *vg) +{ + vg->data = vectorram[vg->pc]; +} + +static void quantum_data(vgdata *vg) +{ + vg->data = quantum_vectorram[vg->pc >> 1]; +} + +static void mhavoc_data(vgdata *vg) +{ + UINT8 *bank; + + if (vg->pc & 0x2000) + { + bank = &memory_region(REGION_CPU1)[0x18000]; + vg->data = bank[(vg->map << 13) | ((vg->pc ^ 1) & 0x1fff)]; + } + else + { + vg->data = vectorram[vg->pc ^ 1]; + } +} + +static UINT8 avg_state_addr(vgdata *vg) +{ + return (((vg->state_latch >> 4) ^ 1) << 7) + | (vg->op << 4) + | (vg->state_latch & 0xf); +} + +static int avg_latch0(vgdata *vg) +{ + vg->dvy = (vg->dvy & 0x1f00) | vg->data; + vg->pc++; + + return 0; +} + +static int quantum_st2st3(vgdata *vg) +{ + /* Quantum doesn't decode latch0 or latch2 but ST2 and ST3 are fed + * into the address controller which incremets the PC + */ + vg->pc++; + return 0; +} + +static int avg_latch1(vgdata *vg) +{ + vg->dvy12 = (vg->data >> 4) &1; + vg->op = vg->data >> 5; + + vg->int_latch = 0; + vg->dvy = (vg->dvy12 << 12) + | ((vg->data & 0xf) << 8 ); + vg->dvx = 0; + vg->pc++; + + return 0; +} + +static int quantum_latch1(vgdata *vg) +{ + vg->dvy = vg->data & 0x1fff; + vg->dvy12 = (vg->data >> 12) & 1; + vg->op = vg->data >> 13; + + vg->int_latch = 0; + vg->dvx = 0; + vg->pc++; + + return 0; +} + +static int bzone_latch1(vgdata *vg) +{ + /* + * Battle Zone has clipping hardware. We need to remember the + * position of the beam when the analog switches hst or lst get + * turened off. + */ + + if (vg->hst == 0) + { + vg->clipx_max = vg->xpos; + vg->clipy_min = vg->ypos; + } + + if (vg->lst == 0) + { + vg->clipx_min = vg->xpos; + vg->clipy_max = vg->ypos; + } + + if (vg->lst==0 || vg->hst==0) + { + vg_add_clip(vg->clipx_min, vg->clipy_min, vg->clipx_max, vg->clipy_max); + } + vg->lst = vg->hst = 1; + + return avg_latch1(vg); +} + +static int mhavoc_latch1(vgdata *vg) +{ + /* + * Major Havoc just has ymin clipping + */ + + if (vg->lst == 0) + { + vg_add_clip(0, vg->ypos, xmax << 16, ymax << 16); + } + vg->lst = 1; + + return avg_latch1(vg); +} + +static int avg_latch2(vgdata *vg) +{ + vg->dvx = (vg->dvx & 0x1f00) | vg->data; + vg->pc++; + + return 0; +} + +static int avg_latch3(vgdata *vg) +{ + vg->int_latch = vg->data >> 4; + vg->dvx = ((vg->int_latch & 1) << 12) + | ((vg->data & 0xf) << 8 ) + | (vg->dvx & 0xff); + vg->pc++; + + return 0; +} + +static int quantum_latch3(vgdata *vg) +{ + vg->int_latch = vg->data >> 12; + vg->dvx = vg->data & 0xfff; + vg->pc++; + + return 0; +} + + +static int avg_strobe0(vgdata *vg) +{ + int i; + + if (OP0) + { + vg->stack[vg->sp & 3] = vg->pc; + } + else + { + /* + * Normalization is done to get roughly constant deflection + * speeds. See Jed's essay why this is important. In addition + * to the intensity and overall time saving issues it is also + * needed to avoid accumulation of DAC errors. The X/Y DACs + * only use bits 3-12. The normalization ensures that the + * first three bits hold no important information. + * + * The circuit doesn't check for dvx=dvy=0. In this case + * shifting goes on as long as VCTR, SCALE and CNTR are + * low. We cut off after 16 shifts. + */ + i = 0; + while ((((vg->dvy ^ (vg->dvy << 1)) & 0x1000) == 0) + && (((vg->dvx ^ (vg->dvx << 1)) & 0x1000) == 0) + && (i++ < 16)) + { + vg->dvy = (vg->dvy & 0x1000) | ((vg->dvy << 1) & 0x1fff); + vg->dvx = (vg->dvx & 0x1000) | ((vg->dvx << 1) & 0x1fff); + vg->timer >>= 1; + vg->timer |= 0x4000 | (OP1 << 6); + } + + if (OP1) + vg->timer &= 0xff; + } + + return 0; +} + +static int quantum_strobe0(vgdata *vg) +{ + int i; + + if (OP0) + { + vg->stack[vg->sp & 3] = vg->pc; + } + else + { + /* + * Quantum normalizes to 12 bit + */ + i = 0; + while ((((vg->dvy ^ (vg->dvy << 1)) & 0x800) == 0) + && (((vg->dvx ^ (vg->dvx << 1)) & 0x800) == 0) + && (i++ < 16)) + { + vg->dvy = (vg->dvy << 1) & 0xfff; + vg->dvx = (vg->dvx << 1) & 0xfff; + vg->timer >>= 1; + vg->timer |= 0x2000 ; + } + } + + return 0; +} + +static int avg_common_strobe1(vgdata *vg) +{ + if (OP2) + { + if (OP1) + vg->sp = (vg->sp - 1) & 0xf; + else + vg->sp = (vg->sp + 1) & 0xf; + } + return 0; +} + +static int avg_strobe1(vgdata *vg) +{ + int i; + + if (OP2 == 0) + { + for (i = vg->bin_scale; i > 0; i--) + { + vg->timer >>= 1; + vg->timer |= 0x4000 | (OP1 << 6); + } + if (OP1) + vg->timer &= 0xff; + } + + return avg_common_strobe1(vg); +} + +static int quantum_strobe1(vgdata *vg) +{ + int i; + + if (OP2 == 0) + { + for (i = vg->bin_scale; i > 0; i--) + { + vg->timer >>= 1; + vg->timer |= 0x2000; + } + } + + return avg_common_strobe1(vg); +} + +static int avg_common_strobe2(vgdata *vg) +{ + if (OP2) + { + if (OP0) + { + vg->pc = vg->dvy << 1; + + if (vg->dvy == 0) + { + /* + * Tempest and Quantum keep the AVG in an endless + * loop. I.e. at one point the AVG jumps to address 0 + * and starts over again. The main CPU updates vector + * RAM while AVG is running. The hardware takes care + * that the AVG dosen't read vector RAM while the CPU + * writes to it. Usually we wait until the AVG stops + * (halt flag) and then draw all vectors at once. This + * doesn't work for Tempest and Quantum so we wait for + * the jump to zero and draw vectors then. + * + * Note that this has nothing to do with the real hardware + * because for a vector monitor it is perfectly okay to + * have the AVG drawing all the time. In the emulation we + * somehow have to divide the stream of vectors into + * 'frames'. + */ + + vector_clear_list(); + vg_flush(); + } + } + else + { + vg->pc = vg->stack[vg->sp & 3]; + } + } + else + { + if (vg->dvy12) + { + vg->scale = vg->dvy & 0xff; + vg->bin_scale = (vg->dvy >> 8) & 7; + } + } + + return 0; +} + +static int avg_strobe2(vgdata *vg) +{ + if ((OP2 == 0) && (vg->dvy12 == 0)) + { + vg->color = vg->dvy & 0x7; + vg->intensity = (vg->dvy >> 4) & 0xf; + } + + return avg_common_strobe2(vg); +} + +static int mhavoc_strobe2(vgdata *vg) +{ + if (OP2 == 0) + { + if (vg->dvy12) + { + if (vg->dvy & 0x800) + vg->lst = 0; + } + else + { + vg->color = vg->dvy & 0xf; + + vg->intensity = (vg->dvy >> 4) & 0xf; + vg->map = (vg->dvy >> 8) & 0x3; + if (vg->dvy & 0x800) + { + vg->enspkl = 1; + vg->spkl_shift = ((vg->dvy >> 3) & 1) + | ((vg->dvy >> 1) & 2) + | ((vg->dvy << 1) & 4) + | ((vg->dvy << 2) & 8) + | ((mame_rand(Machine) & 0x7) << 4); + } + else + { + vg->enspkl = 0; + } + + /* Major Havoc can do X-flipping by inverting the DAC input */ + if (vg->dvy & 0x400) + vg->xdac_xor = 0x1ff; + else + vg->xdac_xor = 0x200; + } + } + + return avg_common_strobe2(vg); +} + +static int tempest_strobe2(vgdata *vg) +{ + if ((OP2 == 0) && (vg->dvy12 == 0)) + { + if (vg->dvy & 0x800) + vg->color = vg->dvy & 0xf; + else + vg->intensity = (vg->dvy >> 4) & 0xf; + } + + return avg_common_strobe2(vg); +} + +static int quantum_strobe2(vgdata *vg) +{ + if ((OP2 == 0) && (vg->dvy12 == 0) && (vg->dvy & 0x800)) + { + vg->color = vg->dvy & 0xf; + vg->intensity = (vg->dvy >> 4) & 0xf; + } + + return avg_common_strobe2(vg); +} + +static int starwars_strobe2(vgdata *vg) +{ + if ((OP2 == 0) && (vg->dvy12 == 0)) + { + vg->intensity = vg->dvy & 0xff; + vg->color = (vg->dvy >> 8) & 0xf; + } + + return avg_common_strobe2(vg); +} + +static int bzone_strobe2(vgdata *vg) +{ + if ((OP2 == 0) && (vg->dvy12 == 0)) + { + vg->intensity = (vg->dvy >> 4) & 0xf; + + if (!(vg->dvy & 0x400)) + { + vg->lst = vg->dvy & 0x200; + vg->hst = vg->lst ^ 0x200; + /* + * If izblank is true the zblank signal gets + * inverted. This behaviour can't be handled with the + * clipping we have right now. Battle Zone doesn't seem to + * invert zblank so it's no issue. + */ + vg->izblank = vg->dvy & 0x100; + } + } + return avg_common_strobe2(vg); +} + +static int avg_common_strobe3(vgdata *vg) +{ + int cycles=0; + + vg->halt = OP0; + + if ((vg->op & 5) == 0) + { + if (OP1) + { + cycles = 0x100 - (vg->timer & 0xff); + } + else + { + cycles = 0x8000 - vg->timer; + } + vg->timer = 0; + + vg->xpos += ((((vg->dvx >> 3) ^ vg->xdac_xor) - 0x200) * cycles * (vg->scale ^ 0xff)) >> 4; + vg->ypos -= ((((vg->dvy >> 3) ^ vg->ydac_xor) - 0x200) * cycles * (vg->scale ^ 0xff)) >> 4; + } + if (OP2) + { + cycles = 0x8000 - vg->timer; + vg->timer = 0; + vg->xpos = xcenter; + vg->ypos = ycenter; + vg_add_point_buf(vg->xpos, vg->ypos, 0, 0); + } + + return cycles; +} + +static int avg_strobe3(vgdata *vg) +{ + int cycles; + + cycles = avg_common_strobe3(vg); + + if ((vg->op & 5) == 0) + { + vg_add_point_buf(vg->xpos, vg->ypos, VECTOR_COLOR111(vg->color), + (((vg->int_latch >> 1) == 1)? vg->intensity: vg->int_latch & 0xe) << 4); + } + + return cycles; +} + +static int bzone_strobe3(vgdata *vg) +{ + /* Battle Zone is B/W */ + int cycles; + + cycles = avg_common_strobe3(vg); + + if ((vg->op & 5) == 0) + { + vg_add_point_buf(vg->xpos, vg->ypos, VECTOR_COLOR111(7), + (((vg->int_latch >> 1) == 1)? vg->intensity: vg->int_latch & 0xe) << 4); + } + + return cycles; +} + +static int tempest_strobe3(vgdata *vg) +{ + int cycles, r, g, b, bit0, bit1, bit2, bit3, x, y; + UINT8 data; + + cycles = avg_common_strobe3(vg); + + if ((vg->op & 5) == 0) + { + data = tempest_colorram[vg->color]; + bit3 = (~data >> 3) & 1; + bit2 = (~data >> 2) & 1; + bit1 = (~data >> 1) & 1; + bit0 = (~data >> 0) & 1; + + r = bit1 * 0xf3 + bit0 * 0x0c; + g = bit3 * 0xf3; + b = bit2 * 0xf3; + + x = vg->xpos; + y = vg->ypos; + + avg_apply_flipping(&x, &y); + + vg_add_point_buf(y - ycenter + xcenter, + x - xcenter + ycenter, MAKE_RGB(r, g, b), + (((vg->int_latch >> 1) == 1)? vg->intensity: vg->int_latch & 0xe) << 4); + } + + return cycles; +} + +static int mhavoc_strobe3(vgdata *vg) +{ + int cycles, r, g, b, bit0, bit1, bit2, bit3, dx, dy, i; + + UINT8 data; + + vg->halt = OP0; + cycles = 0; + + if ((vg->op & 5) == 0) + { + if (OP1) + { + cycles = 0x100 - (vg->timer & 0xff); + } + else + { + cycles = 0x8000 - vg->timer; + } + vg->timer = 0; + dx = ((((vg->dvx >> 3) ^ vg->xdac_xor) - 0x200) * (vg->scale ^ 0xff)); + dy = ((((vg->dvy >> 3) ^ vg->ydac_xor) - 0x200) * (vg->scale ^ 0xff)); + + + if (vg->enspkl) + { + for (i=0; i<cycles/8; i++) + { + vg->xpos += dx/2; + vg->ypos -= dy/2; + data = mhavoc_colorram[0xf + + (((vg->spkl_shift & 1) << 3) + | (vg->spkl_shift & 4) + | ((vg->spkl_shift & 0x10) >> 3) + | ((vg->spkl_shift & 0x40) >> 6))]; + bit3 = (~data >> 3) & 1; + bit2 = (~data >> 2) & 1; + bit1 = (~data >> 1) & 1; + bit0 = (~data >> 0) & 1; + r = bit3 * 0xcb + bit2 * 0x34; + g = bit1 * 0xcb; + b = bit0 * 0xcb; + + vg_add_point_buf(vg->xpos, vg->ypos, MAKE_RGB(r, g, b), + (((vg->int_latch >> 1) == 1)? vg->intensity: vg->int_latch & 0xe) << 4); + vg->spkl_shift = (((vg->spkl_shift & 0x40) >> 6) + ^ ((vg->spkl_shift & 0x20) >> 5) + ^ 1 ) + | (vg->spkl_shift << 1); + + if ((vg->spkl_shift & 0x7f) == 0x7f) + vg->spkl_shift = 0; + } + } + else + { + vg->xpos += (dx * cycles) >> 4; + vg->ypos -= (dy * cycles) >> 4; + data = mhavoc_colorram[vg->color]; + + bit3 = (~data >> 3) & 1; + bit2 = (~data >> 2) & 1; + bit1 = (~data >> 1) & 1; + bit0 = (~data >> 0) & 1; + r = bit3 * 0xcb + bit2 * 0x34; + g = bit1 * 0xcb; + b = bit0 * 0xcb; + + vg_add_point_buf(vg->xpos, vg->ypos, MAKE_RGB(r, g, b), + (((vg->int_latch >> 1) == 1)? vg->intensity: vg->int_latch & 0xe) << 4); + } + } + if (OP2) + { + cycles = 0x8000 - vg->timer; + vg->timer = 0; + vg->xpos = xcenter; + vg->ypos = ycenter; + vg_add_point_buf(vg->xpos, vg->ypos, 0, 0); + } + + return cycles; +} + +static int starwars_strobe3(vgdata *vg) +{ + int cycles; + + cycles = avg_common_strobe3(vg); + + if ((vg->op & 5) == 0) + { + vg_add_point_buf(vg->xpos, vg->ypos, VECTOR_COLOR111(vg->color), + ((vg->int_latch >> 1) * vg->intensity) >> 3); + } + + return cycles; +} + +static int quantum_strobe3(vgdata *vg) +{ + int cycles=0, r, g, b, bit0, bit1, bit2, bit3, x, y; + + UINT16 data; + + vg->halt = OP0; + + if ((vg->op & 5) == 0) + { + data = quantum_colorram[vg->color]; + bit3 = (~data >> 3) & 1; + bit2 = (~data >> 2) & 1; + bit1 = (~data >> 1) & 1; + bit0 = (~data >> 0) & 1; + + g = bit1 * 0xaa + bit0 * 0x54; + b = bit2 * 0xce; + r = bit3 * 0xce; + + cycles = 0x4000 - vg->timer; + vg->timer = 0; + + vg->xpos += (((((vg->dvx & 0xfff) >> 2) ^ vg->xdac_xor) - 0x200) * cycles * (vg->scale ^ 0xff)) >> 4; + vg->ypos -= (((((vg->dvy & 0xfff) >> 2) ^ vg->ydac_xor) - 0x200) * cycles * (vg->scale ^ 0xff)) >> 4; + + x = vg->xpos; + y = vg->ypos; + + avg_apply_flipping(&x, &y); + + vg_add_point_buf(y - ycenter + xcenter, + x - xcenter + ycenter, MAKE_RGB(r, g, b), + ((vg->int_latch == 2)? vg->intensity: vg->int_latch) << 4); + } + if (OP2) + { + cycles = 0x4000 - vg->timer; + vg->timer = 0; + vg->xpos = xcenter; + vg->ypos = ycenter; + vg_add_point_buf(vg->xpos, vg->ypos, 0, 0); + } + + return cycles; +} + +static void avg_vggo(vgdata *vg) +{ + vg->pc = 0; + vg->sp = 0; +} + +static void dvg_vggo(vgdata *vg) +{ + vg->dvy = 0; + vg->op = 0; +} + +static void avg_vgrst(vgdata *vg) +{ + vg->state_latch = 0; + vg->bin_scale = 0; + vg->scale = 0; + vg->color = 0; +} + +static void mhavoc_vgrst(vgdata *vg) +{ + avg_vgrst(vg); + vg->enspkl = 0; +} + +static void dvg_vgrst(vgdata *vg) +{ + vg->state_latch = 0; + vg->dvy = 0; + vg->op = 0; +} + +static void vg_set_halt(int dummy) +{ + vg->halt = dummy; + vg->sync_halt = dummy; +} + +static TIMER_CALLBACK( vg_set_halt_callback ) +{ + vg_set_halt(param); +} + + +/******************************************************************** + * + * State Machine + * + * The state machine is a 256x4 bit PROM connected to a latch. The + * address of the next state is generated from the latched previous + * state, an op code and the halt flag. Op codes come from vector + * RAM/ROM. The state machine is clocked with 1.5 MHz. Three bits of + * the state are decoded and used to trigger various parts of the + * hardware. + * + *******************************************************************/ + +static TIMER_CALLBACK( run_state_machine ) +{ + int cycles = 0; + UINT8 *state_prom = memory_region(REGION_USER1); + + while (cycles < VGSLICE) + { + /* Get next state */ + vg->state_latch = (vg->state_latch & 0x10) + | (state_prom[vgc->state_addr(vg)] & 0xf); + + if (ST3) + { + /* Read vector RAM/ROM */ + vgc->update_databus(vg); + + /* Decode state and call the corresponding handler */ + cycles += (vgc->handler[vg->state_latch & 7])(vg); + } + + /* If halt flag was set, let CPU catch up before we make halt visible */ + if (vg->halt && !(vg->state_latch & 0x10)) + timer_adjust(vg_halt_timer, attotime_mul(ATTOTIME_IN_HZ(MASTER_CLOCK), cycles), 1, attotime_zero); + + vg->state_latch = (vg->halt << 4) | (vg->state_latch & 0xf); + cycles += 8; + } + + timer_adjust(vg_run_timer, attotime_mul(ATTOTIME_IN_HZ(MASTER_CLOCK), cycles), 0, attotime_zero); +} + + +/************************************* + * + * VG halt/vggo + * + ************************************/ + +int avgdvg_done(void) +{ + return vg->sync_halt; +} + +WRITE8_HANDLER( avgdvg_go_w ) +{ + if (vg->sync_halt && (nvect > 10)) + { + /* + * This is a good time to start a new frame. Major Havoc + * sometimes sets VGGO after a very short vector list. That's + * why we ignore frames with less than 10 vectors. + */ + vector_clear_list(); + } + vg_flush(); + + vgc->vggo(vg); + vg_set_halt(0); + timer_adjust(vg_run_timer, attotime_zero, 0, attotime_zero); +} + +WRITE16_HANDLER( avgdvg_go_word_w ) +{ + avgdvg_go_w(offset, data); +} + + +/************************************* + * + * Reset + * + ************************************/ + +WRITE8_HANDLER( avgdvg_reset_w ) +{ + vgc->vgrst(vg); + vg_set_halt(1); +} + +WRITE16_HANDLER( avgdvg_reset_word_w ) +{ + avgdvg_reset_w (0,0); +} + +MACHINE_RESET( avgdvg ) +{ + avgdvg_reset_w (0,0); +} + + +/************************************* + * + * Vector generator init + * + ************************************/ + +static VIDEO_START( avgdvg ) +{ + xmin = machine->screen[0].visarea.min_x; + ymin = machine->screen[0].visarea.min_y; + xmax = machine->screen[0].visarea.max_x; + ymax = machine->screen[0].visarea.max_y; + + xcenter = ((xmax + xmin) / 2) << 16; + ycenter = ((ymax + ymin) / 2) << 16; + + flip_x = flip_y = 0; + + vg_halt_timer = timer_alloc(vg_set_halt_callback); + vg_run_timer = timer_alloc(run_state_machine); + + /* + * The x and y DACs use 10 bit of the counter values which are in + * two's complement representation. The DAC input is xored with + * 0x200 to convert the value to unsigned. + */ + vg->xdac_xor = 0x200; + vg->ydac_xor = 0x200; + + state_save_register_item("AVG", 0, vg->pc); + state_save_register_item("AVG", 0, vg->sp); + state_save_register_item("AVG", 0, vg->dvx); + state_save_register_item("AVG", 0, vg->dvy); + state_save_register_item("AVG", 0, vg->dvy12); + state_save_register_item("AVG", 0, vg->timer); + state_save_register_item_array("AVG", 0, vg->stack); + state_save_register_item("AVG", 0, vg->data); + state_save_register_item("AVG", 0, vg->state_latch); + state_save_register_item("AVG", 0, vg->int_latch); + state_save_register_item("AVG", 0, vg->scale); + state_save_register_item("AVG", 0, vg->bin_scale); + state_save_register_item("AVG", 0, vg->intensity); + state_save_register_item("AVG", 0, vg->color); + state_save_register_item("AVG", 0, vg->enspkl); + state_save_register_item("AVG", 0, vg->spkl_shift); + state_save_register_item("AVG", 0, vg->map); + state_save_register_item("AVG", 0, vg->hst); + state_save_register_item("AVG", 0, vg->lst); + state_save_register_item("AVG", 0, vg->izblank); + state_save_register_item("AVG", 0, vg->op); + state_save_register_item("AVG", 0, vg->halt); + state_save_register_item("AVG", 0, vg->sync_halt); + state_save_register_item("AVG", 0, vg->xdac_xor); + state_save_register_item("AVG", 0, vg->ydac_xor); + state_save_register_item("AVG", 0, vg->xpos); + state_save_register_item("AVG", 0, vg->ypos); + state_save_register_item("AVG", 0, vg->clipx_min); + state_save_register_item("AVG", 0, vg->clipy_min); + state_save_register_item("AVG", 0, vg->clipx_max); + state_save_register_item("AVG", 0, vg->clipy_max); + + video_start_vector(machine); +} + + +/************************************* + * + * Configuration of VG variants + * + *************************************/ + +static vgconf dvg_default = +{ + { + dvg_dmapush, + dvg_dmald, + dvg_gostrobe, + dvg_haltstrobe, + dvg_latch0, + dvg_latch1, + dvg_latch2, + dvg_latch3 + }, + dvg_state_addr, + dvg_data, + dvg_vggo, + dvg_vgrst +}; + +static vgconf avg_default = +{ + { + avg_latch0, + avg_latch1, + avg_latch2, + avg_latch3, + avg_strobe0, + avg_strobe1, + avg_strobe2, + avg_strobe3 + }, + avg_state_addr, + avg_data, + avg_vggo, + avg_vgrst +}; + +static vgconf avg_mhavoc = +{ + { + avg_latch0, + mhavoc_latch1, + avg_latch2, + avg_latch3, + avg_strobe0, + avg_strobe1, + mhavoc_strobe2, + mhavoc_strobe3 + }, + avg_state_addr, + mhavoc_data, + avg_vggo, + mhavoc_vgrst +}; + +static vgconf avg_starwars = +{ + { + avg_latch0, + avg_latch1, + avg_latch2, + avg_latch3, + avg_strobe0, + avg_strobe1, + starwars_strobe2, + starwars_strobe3 + }, + avg_state_addr, + starwars_data, + avg_vggo, + avg_vgrst +}; + +static vgconf avg_tempest = +{ + { + avg_latch0, + avg_latch1, + avg_latch2, + avg_latch3, + avg_strobe0, + avg_strobe1, + tempest_strobe2, + tempest_strobe3 + }, + avg_state_addr, + avg_data, + avg_vggo, + avg_vgrst +}; + +static vgconf avg_bzone = +{ + { + avg_latch0, + bzone_latch1, + avg_latch2, + avg_latch3, + avg_strobe0, + avg_strobe1, + bzone_strobe2, + bzone_strobe3 + }, + avg_state_addr, + avg_data, + avg_vggo, + avg_vgrst +}; + +static vgconf avg_quantum = +{ + { + quantum_st2st3, + quantum_latch1, + quantum_st2st3, + quantum_latch3, + quantum_strobe0, + quantum_strobe1, + quantum_strobe2, + quantum_strobe3 + }, + avg_state_addr, + quantum_data, + avg_vggo, + avg_vgrst +}; + + +/************************************* + * + * Video startup + * + ************************************/ + +VIDEO_START( dvg ) +{ + vgc = &dvg_default; + vg = &vgd; + video_start_avgdvg(machine); +} + +VIDEO_START( avg ) +{ + vgc = &avg_default; + vg = &vgd; + video_start_avgdvg(machine); +} + +VIDEO_START( avg_starwars ) +{ + vgc = &avg_starwars; + vg = &vgd; + video_start_avgdvg(machine); +} + +VIDEO_START( avg_tempest ) +{ + vgc = &avg_tempest; + vg = &vgd; + video_start_avgdvg(machine); +} + +VIDEO_START( avg_mhavoc ) +{ + vgc = &avg_mhavoc; + vg = &vgd; + video_start_avgdvg(machine); +} + +VIDEO_START( avg_bzone ) +{ + vgc = &avg_bzone; + vg = &vgd; + video_start_avgdvg(machine); +} + +VIDEO_START( avg_quantum ) +{ + vgc = &avg_quantum; + vg = &vgd; + video_start_avgdvg(machine); +} + |