/*************************************************************************** video/odyssey2.c ***************************************************************************/ #include #include "driver.h" #include "mslegacy.h" #include "cpu/i8039/i8039.h" #include "video/generic.h" #include "includes/odyssey2.h" /* character sprite colors dark grey, red, green, orange, blue, violet, light grey, white dark back / grid colors black, dark blue, dark green, light green, red, violet, orange, light grey light back / grid colors black, blue, green, light green, red, violet, orange, light grey */ UINT8 odyssey2_colors[] = { /* Background,Grid Dim */ 0x00,0x00,0x00, 0x00,0x00,0xFF, /* Blue */ 0x00,0x80,0x00, /* DK Green */ 0xff,0x9b,0x60, 0xCC,0x00,0x00, /* Red */ 0xa9,0x80,0xff, 0x82,0xfd,0xdb, 0xFF,0xFF,0xFF, /* Background,Grid Bright */ 0x80,0x80,0x80, 0x50,0xAE,0xFF, /* Blue */ 0x00,0xFF,0x00, /* Dk Green */ 0x82,0xfb,0xdb, /* Lt Grey */ 0xEC,0x02,0x60, /* Red */ 0xa9,0x80,0xff, /* Violet */ 0xff,0x9b,0x60, /* Orange */ 0xFF,0xFF,0xFF, /* Character,Sprite colors */ 0x71,0x71,0x71, /* Dark Grey */ 0xFF,0x80,0x80, /* Red */ 0x00,0xC0,0x00, /* Green */ 0xff,0x9b,0x60, /* Orange */ 0x50,0xAE,0xFF, /* Blue */ 0xa9,0x80,0xff, /* Violet */ 0x82,0xfb,0xdb, /* Lt Grey */ 0xff,0xff,0xff /* White */ }; UINT8 o2_shape[0x40][8]={ { 0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00 }, // 0 { 0x18,0x38,0x18,0x18,0x18,0x18,0x3C,0x00 }, { 0x3C,0x66,0x0C,0x18,0x30,0x60,0x7E,0x00 }, { 0x7C,0xC6,0x06,0x3C,0x06,0xC6,0x7C,0x00 }, { 0xCC,0xCC,0xCC,0xFE,0x0C,0x0C,0x0C,0x00 }, { 0xFE,0xC0,0xC0,0x7C,0x06,0xC6,0x7C,0x00 }, { 0x7C,0xC6,0xC0,0xFC,0xC6,0xC6,0x7C,0x00 }, { 0xFE,0x06,0x0C,0x18,0x30,0x60,0xC0,0x00 }, { 0x7C,0xC6,0xC6,0x7C,0xC6,0xC6,0x7C,0x00 }, { 0x7C,0xC6,0xC6,0x7E,0x06,0xC6,0x7C,0x00 }, { 0x00,0x18,0x18,0x00,0x18,0x18,0x00,0x00 }, { 0x18,0x7E,0x58,0x7E,0x58,0x7E,0x18,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00 }, { 0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFE,0x00 }, { 0xFC,0xC6,0xC6,0xFC,0xC0,0xC0,0xC0,0x00 }, { 0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00 }, { 0xC6,0xC6,0xC6,0xD6,0xFE,0xEE,0xC6,0x00 }, { 0xFE,0xC0,0xC0,0xF8,0xC0,0xC0,0xFE,0x00 }, { 0xFC,0xC6,0xC6,0xFC,0xD8,0xCC,0xC6,0x00 }, { 0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00 }, { 0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00 }, { 0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00 }, { 0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00 }, { 0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x76,0x00 }, { 0x7C,0xC6,0xC0,0x7C,0x06,0xC6,0x7C,0x00 }, { 0xFC,0xC6,0xC6,0xC6,0xC6,0xC6,0xFC,0x00 }, { 0xFE,0xC0,0xC0,0xF8,0xC0,0xC0,0xC0,0x00 }, { 0x7C,0xC6,0xC0,0xC0,0xCE,0xC6,0x7E,0x00 }, { 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00 }, { 0x06,0x06,0x06,0x06,0x06,0xC6,0x7C,0x00 }, { 0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x00 }, { 0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00 }, { 0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00 }, { 0xC6,0xC6,0x6C,0x38,0x6C,0xC6,0xC6,0x00 }, { 0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x00 }, { 0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00 }, { 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00 }, { 0xC6,0xEE,0xFE,0xD6,0xC6,0xC6,0xC6,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00 }, { 0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00 }, { 0x00,0x66,0x3C,0x18,0x3C,0x66,0x00,0x00 }, { 0x00,0x18,0x00,0x7E,0x00,0x18,0x00,0x00 }, { 0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00 }, { 0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00 }, { 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00 }, { 0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0x00 }, { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00 }, { 0xCE,0xDB,0xDB,0xDB,0xDB,0xDB,0xCE,0x00 }, { 0x00,0x00,0x3C,0x7E,0x7E,0x7E,0x3C,0x00 }, { 0x1C,0x1C,0x18,0x1E,0x18,0x18,0x1C,0x00 }, { 0x1C,0x1C,0x18,0x1E,0x18,0x34,0x26,0x00 }, { 0x38,0x38,0x18,0x78,0x18,0x2C,0x64,0x00 }, { 0x38,0x38,0x18,0x78,0x18,0x18,0x38,0x00 }, { 0x00,0x18,0x0C,0xFE,0x0C,0x18,0x00,0x00 }, { 0x18,0x3C,0x7E,0xFF,0xFF,0x18,0x18,0x00 }, { 0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF,0x00 }, { 0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF,0x00 }, { 0x38,0x38,0x12,0xFE,0xB8,0x28,0x6C,0x00 }, { 0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x00 }, { 0x00,0x00,0x0C,0x08,0x08,0x7F,0x3E,0x00 }, { 0x00,0x03,0x63,0xFF,0xFF,0x18,0x08,0x00 }, { 0x00,0x00,0x00,0x10,0x38,0xFF,0x7E,0x00 } }; static UINT8 *odyssey2_display; int odyssey2_vh_hpos; union { UINT8 reg[0x100]; struct { struct { UINT8 y,x,color,res; } sprites[4]; struct { UINT8 y,x,ptr,color; } foreground[12]; struct { struct { UINT8 y,x,ptr,color; } single[4]; } quad[4]; UINT8 shape[4][8]; UINT8 control; UINT8 status; UINT8 collision; UINT8 color; UINT8 y; UINT8 x; UINT8 res; UINT8 shift1,shift2,shift3; UINT8 sound; UINT8 res2[5+0x10]; UINT8 hgrid[2][0x10]; UINT8 vgrid[0x10]; } s; } o2_vdc= { { 0 } }; static UINT8 collision[8]; /* only 7 used but easier to index */ static int line; static mame_time line_time; static UINT32 o2_snd_shift[2]; static UINT8 x_beam_pos; static UINT8 y_beam_pos; /*************************************************************************** Start the video hardware emulation. ***************************************************************************/ VIDEO_START( odyssey2 ) { o2_snd_shift[0] = Machine->sample_rate / 983; o2_snd_shift[1] = Machine->sample_rate / 3933; odyssey2_vh_hpos = 0; odyssey2_display = (UINT8 *) auto_malloc(8 * 8 * 256); memset(odyssey2_display, 0, 8 * 8 * 256); } INLINE int get_horiz_clock_beam_pos( void ) { int h; h = scale_up_mame_time(sub_mame_times(mame_timer_get_time(), line_time), Machine->drv->cpu[0].cpu_clock * 8).seconds; return h; } INLINE int get_horiz_clock( void ) { int h; h = scale_up_mame_time(sub_mame_times(mame_timer_get_time(), line_time), Machine->drv->cpu[0].cpu_clock).seconds; return h; } INLINE int horiz_scan_active( void ) { int h; h = get_horiz_clock(); if ( h > 15 ) return 1; return 0; } INLINE int in_vblank( void ) { if ((line > 239) && (line < 262)) return 1; return 0; } PALETTE_INIT( odyssey2 ) { palette_set_colors_rgb(machine, 0, odyssey2_colors, sizeof(odyssey2_colors) / 3); colortable[0] = 0; colortable[1] = 1; } extern READ8_HANDLER( odyssey2_video_r ) { UINT8 data = 0; int bit, i; switch (offset) { case 0xa1: if (horiz_scan_active()) data |= 1; if (in_vblank()) data |= 8; break; case 0xa2: bit = 0x01; for (i = 0; i < 8; i++) { /* o2_vdc.s.collision at this point has the requested bit(s) * to test the current collisions against. TODO: Sometimes * $A2 has more than one bit set. What are the correct semantics * of that when reading back? For now having an entire byte * for each graphics object that can collide is working. */ if (bit & o2_vdc.s.collision) data |= (collision[i] & ~bit); bit <<= 1; } break; case 0xa4: if ((o2_vdc.s.control & VDC_CONTROL_REG_STROBE_XY)) y_beam_pos = line; data = y_beam_pos; break; case 0xa5: if ((o2_vdc.s.control & VDC_CONTROL_REG_STROBE_XY)) x_beam_pos = get_horiz_clock_beam_pos(); data = x_beam_pos; break; default: data = o2_vdc.reg[offset]; } return data; } extern WRITE8_HANDLER( odyssey2_video_w ) { /* Update the sound */ if( offset >= 0xa7 && offset <= 0xaa ) stream_update( odyssey2_sh_channel ); if (offset == 0xa0) { if ( o2_vdc.s.control & VDC_CONTROL_REG_STROBE_XY && !(data & VDC_CONTROL_REG_STROBE_XY)) { /* Toggling strobe bit, tuck away values */ x_beam_pos = get_horiz_clock_beam_pos(); y_beam_pos = line; /* This is wrong but more games work with it, TODO: Figure * out correct change. Maybe update the screen here?? * It seems what happens is 0x0 is written to $A0 just before * VLBANK (video update) so no screen updates happen. */ return; } } o2_vdc.reg[offset] = data; } extern READ8_HANDLER( odyssey2_t1_r ) { static int t = FALSE; t = !t; return t; } INTERRUPT_GEN( odyssey2_line ) { line_time = mame_timer_get_time(); line = (line + 1) % 262; switch (line) { case 252: cpunum_set_input_line(0, 0, ASSERT_LINE); /* vsync?? */ break; case 253: cpunum_set_input_line(0, 0, CLEAR_LINE); /* vsync?? */ break; } } INLINE void odyssey2_draw_box(UINT8 bg[][320], int x, int y, int width, int height, UINT8 collision_id) { int x1,y1; for (y1 = 0; y1 < height; y1++) { for (x1 = 0; x1 < width; x1++) bg[y+y1][x+x1] |= collision_id; } } INLINE void odyssey2_draw(UINT8 bg[][320], UINT8 code, int x, int y, int scale_x, int scale_y, UINT8 collision_id) { int m,x1,y1; for (m=0x80; m>0; m>>=1, x+=scale_x) { if (code & m) { for (y1=0; y1> 3) & 8; for (i=0, x=0; x<9; x++, i++) { for (j=1, y=0; y<9; y++, j<<=1) { if ( ((j<=0x80)&&(o2_vdc.s.hgrid[0][i]&j)) ||((j>0x80)&&(o2_vdc.s.hgrid[1][i]&1)) ) { odyssey2_draw_box(bg, x_grid_offset + x * width, y_grid_offset + y * height, width + 2, 3, COLLISION_HORIZ_GRID_DOTS); plot_box(bitmap, x_grid_offset + x * width, y_grid_offset + y * height, width + 2, 3, Machine->pens[color]); } } } if (o2_vdc.s.control & 0x80) /* fill solid to end of next vert line */ w=width; for (i=0, x=0; x<10; x++, i++) { for (j=1, y=0; y<8; y++, j<<=1) { if (o2_vdc.s.vgrid[i] & j) { odyssey2_draw_box(bg, x_grid_offset + x * width, y_grid_offset + y * height, w, height, COLLISION_VERTICAL_GRID); plot_box(bitmap, x_grid_offset + x * width, y_grid_offset + y * height, w, height, Machine->pens[color]); } } } } INLINE void odyssey2_draw_char(mame_bitmap *bitmap, UINT8 bg[][320], int x, int y, int ptr, int color) { int n, i; int offset = ptr | ((color & 1) << 8); offset=(offset + (y >> 1)) & 0x1ff; /* 7-Sep-2007 - whomever wrote this crap code was dynamically remapping * the color table, a vile gross hack. Doesn't look like this is going * to survive the 0.118u5 transition */ /* Machine->gfx[0]->colortable[1]=Machine->pens[16 + ((color & 0xe) >> 1)]; */ // don't ask me about the technical background, but also this height thingy is needed // invaders aliens (!) and shoot (-) n = 8 - (ptr & 7) - ((y >> 1) & 7); if (n < 3) n += 7; for (i=0; i= bitmap->height ) break; odyssey2_draw(bg, ((char*)o2_shape)[offset], x, y+i*2, 1, 2, COLLISION_CHARACTERS); drawgfxzoom(bitmap, Machine->gfx[0], ((char*)o2_shape)[offset],0, 0,0,x,y+i*2, 0, TRANSPARENCY_PEN,0, 0x10000, 0x20000); offset=(offset+1) & 0x1ff; } } /*************************************************************************** Refresh the video screen ***************************************************************************/ VIDEO_UPDATE( odyssey2 ) { int i, j, x, y; UINT8 bg[300][320]= { { 0 } }; assert(bitmap->width<=ARRAY_LENGTH(bg[0]) && bitmap->height<=ARRAY_LENGTH(bg)); plot_box( bitmap, 0, 0, bitmap->width, bitmap->height, Machine->pens[(o2_vdc.s.color >> 3) & 0x7] ); if (o2_vdc.s.control & 0x08) /* show grid */ odyssey2_draw_grid(bitmap, bg); if (o2_vdc.s.control & 0x20) /* show foreground objects */ { for (i=0; igfx[0]->colortable[1]=Machine->pens[16+((o2_vdc.s.sprites[i].color>>3)&7)]; */ y=o2_vdc.s.sprites[i].y; x=o2_vdc.s.sprites[i].x; for (j=0; j<8; j++) { if (o2_vdc.s.sprites[i].color & 4) { if (y+4*j>=bitmap->height) break; odyssey2_draw_sprite(bg, o2_vdc.s.shape[i][j], x, y+j*4, 2, 4, 1<gfx[1], o2_vdc.s.shape[i][j],0, 0,0,x,y+j*4, 0, TRANSPARENCY_PEN,0,0x20000, 0x40000); } else { if (y+j*2>=bitmap->height) break; odyssey2_draw_sprite(bg, o2_vdc.s.shape[i][j], x, y+j*2, 1, 2, 1<gfx[1], o2_vdc.s.shape[i][j],0, 0,0,x,y+j*2, 0, TRANSPARENCY_PEN,0, 0x10000, 0x20000); } } } } memset( collision, 0, sizeof( collision ) ); for (y=0; y<300; y++) { for (x=0; x<320; x++) { switch (bg[y][x]) { case 0: case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x80: /* Only one collidable entity in this spot, no collision to mark */ break; default: { int bit = 0x01; for (i = 0; i < 8; i++) { if (bit & o2_vdc.s.collision && bit & bg[y][x]) collision[i] |= bg[y][x] & ~bit; bit <<= 1; } break; } } } } return 0; } void odyssey2_sh_update( void *param,stream_sample_t **inputs, stream_sample_t **_buffer,int length ) { static UINT32 signal; static UINT16 count = 0; int ii; int period; stream_sample_t *buffer = _buffer[0]; /* Generate the signal */ signal = o2_vdc.s.shift3 | (o2_vdc.s.shift2 << 8) | (o2_vdc.s.shift1 << 16); if( o2_vdc.s.sound & 0x80 ) /* Sound is enabled */ { for( ii = 0; ii < length; ii++, buffer++ ) { *buffer = 0; *buffer = signal & 0x1; period = (o2_vdc.s.sound & 0x20) ? 11 : 44; if( ++count >= period ) { count = 0; signal >>= 1; /* Loop sound */ if( o2_vdc.s.sound & 0x40 ) { signal |= *buffer << 23; } } /* Throw an interrupt if enabled */ if( o2_vdc.s.control & 0x4 ) { cpunum_set_input_line(0, 1, HOLD_LINE); /* Is this right? */ } /* Adjust volume */ *buffer *= o2_vdc.s.sound & 0xf; /* Pump the volume up */ *buffer <<= 10; } } else { /* Sound disabled, so clear the buffer */ for( ii = 0; ii < length; ii++, buffer++ ) *buffer = 0; } }