/******************************************************************************
watara supervision handheld
PeT mess@utanet.at in december 2000
******************************************************************************/
#include "emu.h"
#include "cpu/m6502/m65c02.h"
#include "includes/svision.h"
#include "imagedev/cartslot.h"
#include "svision.lh"
#define MAKE8_RGB32(red3, green3, blue2) ( ( (red3)<<(16+5)) | ( (green3)<<(8+5)) | ( (blue2)<<(0+6)) )
#define MAKE9_RGB32(red3, green3, blue3) ( ( (red3)<<(16+5)) | ( (green3)<<(8+5)) | ( (blue3)<<(0+5)) )
#define MAKE12_RGB32(red4, green4, blue4) ( ( (red4)<<(16+4)) | ((green4)<<(8+4)) | ((blue4)<<(0+4)) )
#define MAKE24_RGB32(red8, green8, blue8) ( (((red8)&0xf8)<<16) | (((green8)&0xf8)<<8) | (((blue8)&0xf8)) )
// in pixel
#define XSIZE (m_reg[0]&~3)
#define XPOS m_reg[2]
#define YPOS m_reg[3]
#define BANK m_reg[0x26]
TIMER_CALLBACK_MEMBER(svision_state::svision_pet_timer)
{
switch (m_pet.state)
{
case 0:
if ( m_joy2 )
{
m_pet.input = m_joy2->read();
}
/* fall through */
case 2: case 4: case 6: case 8:
case 10: case 12: case 14:
m_pet.clock = m_pet.state & 2;
m_pet.data = m_pet.input & 1;
m_pet.input >>= 1;
m_pet.state++;
break;
case 16+15:
m_pet.state = 0;
break;
default:
m_pet.state++;
break;
}
}
TIMER_DEVICE_CALLBACK_MEMBER(svision_state::svision_pet_timer_dev)
{
svision_pet_timer(ptr,param);
}
void svision_state::svision_irq()
{
int irq = m_svision.timer_shot && (BANK & 2);
irq = irq || (*m_dma_finished && (BANK & 4));
m_maincpu->set_input_line(M65C02_IRQ_LINE, irq ? ASSERT_LINE : CLEAR_LINE);
}
TIMER_CALLBACK_MEMBER(svision_state::svision_timer)
{
m_svision.timer_shot = TRUE;
m_svision.timer1->enable(FALSE);
svision_irq();
}
READ8_MEMBER(svision_state::svision_r)
{
int data = m_reg[offset];
switch (offset)
{
case 0x20:
data = m_joy->read();
break;
case 0x21:
data &= ~0xf;
data |= m_reg[0x22] & 0xf;
if (m_pet.on)
{
if (!m_pet.clock)
{
data &= ~4;
}
if (!m_pet.data)
{
data &= ~8;
}
}
break;
case 0x27:
data &= ~3;
if (m_svision.timer_shot)
{
data|=1;
}
if (*m_dma_finished)
{
data|=2;
}
break;
case 0x24:
m_svision.timer_shot = FALSE;
svision_irq();
break;
case 0x25:
*m_dma_finished = FALSE;
svision_irq();
break;
default:
logerror("%.6f svision read %04x %02x\n", machine().time().as_double(),offset,data);
break;
}
return data;
}
WRITE8_MEMBER(svision_state::svision_w)
{
int value;
int delay;
m_reg[offset] = data;
switch (offset)
{
case 2:
case 3:
break;
case 0x26: /* bits 5,6 memory management for a000? */
logerror("%.6f svision write %04x %02x\n", machine().time().as_double(),offset,data);
m_bank1->set_base(m_user1->base() + ((m_reg[0x26] & 0xe0) << 9));
svision_irq();
break;
case 0x23: /* delta hero irq routine write */
value = data;
if (!data)
{
value = 0x100;
}
if (BANK & 0x10)
{
delay = 16384;
}
else
{
delay = 256;
}
m_svision.timer1->enable(TRUE);
m_svision.timer1->reset(m_maincpu->cycles_to_attotime(value * delay));
break;
case 0x10: case 0x11: case 0x12: case 0x13:
m_sound->soundport_w(0, offset & 3, data);
break;
case 0x14: case 0x15: case 0x16: case 0x17:
m_sound->soundport_w(1, offset & 3, data);
break;
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c:
m_sound->svision_sounddma_w(space, offset - 0x18, data);
break;
case 0x28: case 0x29: case 0x2a:
m_sound->svision_noise_w(space, offset - 0x28, data);
break;
default:
logerror("%.6f svision write %04x %02x\n", machine().time().as_double(), offset, data);
break;
}
}
READ8_MEMBER(svision_state::tvlink_r)
{
switch(offset)
{
default:
if (offset >= 0x800 && offset < 0x840)
{
/* strange effects when modifying palette */
return svision_r(space, offset);
}
else
{
return svision_r(space, offset);
}
}
}
WRITE8_MEMBER(svision_state::tvlink_w)
{
switch (offset)
{
case 0x0e:
m_reg[offset] = data;
m_tvlink.palette_on = data & 1;
if (m_tvlink.palette_on)
{
// hack, normally initialising with palette from ram
m_tvlink.palette[0] = MAKE12_RGB32(163/16,172/16,115/16); // these are the tron colors messured from screenshot
m_tvlink.palette[1] = MAKE12_RGB32(163/16,155/16,153/16);
m_tvlink.palette[2] = MAKE12_RGB32(77/16,125/16,73/16);
m_tvlink.palette[3] = MAKE12_RGB32(59/16,24/16,20/16);
}
else
{
// cleaner to use colors from compile time palette, or compose from "fixed" palette values
m_tvlink.palette[0]=MAKE12_RGB32(0,0,0);
m_tvlink.palette[1]=MAKE12_RGB32(5*16/256,18*16/256,9*16/256);
m_tvlink.palette[2]=MAKE12_RGB32(48*16/256,76*16/256,100*16/256);
m_tvlink.palette[3]=MAKE12_RGB32(190*16/256,190*16/256,190*16/256);
}
break;
default:
svision_w(space, offset,data);
if (offset >= 0x800 && offset < 0x840)
{
UINT16 c;
if (offset == 0x803 && data == 0x07)
{
/* tron hack */
m_reg[0x0804]=0x00;
m_reg[0x0805]=0x01;
m_reg[0x0806]=0x00;
m_reg[0x0807]=0x00;
}
c = m_reg[0x800] | (m_reg[0x804] << 8);
m_tvlink.palette[0] = MAKE9_RGB32( (c>>0)&7, (c>>3)&7, (c>>6)&7);
c = m_reg[0x801] | (m_reg[0x805] << 8);
m_tvlink.palette[1] = MAKE9_RGB32( (c>>0)&7, (c>>3)&7, (c>>6)&7);
c = m_reg[0x802] | (m_reg[0x806]<<8);
m_tvlink.palette[2]=MAKE9_RGB32( (c>>0)&7, (c>>3)&7, (c>>6)&7);
c = m_reg[0x803] | (m_reg[0x807]<<8);
m_tvlink.palette[3]=MAKE9_RGB32( (c>>0)&7, (c>>3)&7, (c>>6)&7);
/* writes to palette effect video color immediately */
/* some writes modify other registers, */
/* encoding therefor not known (rgb8 or rgb9) */
}
}
}
static ADDRESS_MAP_START( svision_mem , AS_PROGRAM, 8, svision_state )
AM_RANGE( 0x0000, 0x1fff) AM_RAM
AM_RANGE( 0x2000, 0x3fff) AM_READWRITE(svision_r, svision_w) AM_SHARE("reg")
AM_RANGE( 0x4000, 0x5fff) AM_RAM AM_SHARE("videoram")
AM_RANGE( 0x6000, 0x7fff) AM_NOP
AM_RANGE( 0x8000, 0xbfff) AM_ROMBANK("bank1")
AM_RANGE( 0xc000, 0xffff) AM_ROMBANK("bank2")
ADDRESS_MAP_END
static ADDRESS_MAP_START( tvlink_mem , AS_PROGRAM, 8, svision_state )
AM_RANGE( 0x0000, 0x1fff) AM_RAM
AM_RANGE( 0x2000, 0x3fff) AM_READWRITE(tvlink_r, tvlink_w) AM_SHARE("reg")
AM_RANGE( 0x4000, 0x5fff) AM_RAM AM_SHARE("videoram")
AM_RANGE( 0x6000, 0x7fff) AM_NOP
AM_RANGE( 0x8000, 0xbfff) AM_ROMBANK("bank1")
AM_RANGE( 0xc000, 0xffff) AM_ROMBANK("bank2")
ADDRESS_MAP_END
static INPUT_PORTS_START( svision )
PORT_START("JOY")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT)
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("B")
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("A")
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Select")
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("Start/Pause")
INPUT_PORTS_END
static INPUT_PORTS_START( svisions )
PORT_START("JOY")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1)
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("B") PORT_PLAYER(1)
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("A") PORT_PLAYER(1)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("Select") PORT_PLAYER(1)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("Start/Pause") PORT_PLAYER(1)
PORT_START("JOY2")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("2nd B") PORT_PLAYER(2)
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("2nd A") PORT_PLAYER(2)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("2nd Select") PORT_PLAYER(2)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("2nd Start/Pause") PORT_PLAYER(2)
INPUT_PORTS_END
/* most games contain their graphics in roms, and have hardware to
draw complete rectangular objects */
#define PALETTE_START 0
/* palette in red, green, blue triples */
static const unsigned char svision_palette[] =
{
#if 0
/* greens grabbed from a scan of a handheld
* in its best adjustment for contrast
*/
86, 121, 86,
81, 115, 90,
74, 107, 101,
54, 78, 85
#else
/* grabbed from chris covell's black white pics */
0xe0, 0xe0, 0xe0,
0xb9, 0xb9, 0xb9,
0x54, 0x54, 0x54,
0x12, 0x12, 0x12
#endif
};
/* palette in red, green, blue tribles */
static const unsigned char svisionp_palette[] =
{
// pal
1, 1, 3,
5, 18, 9,
48, 76, 100,
190, 190, 190
};
/* palette in red, green, blue tribles */
static const unsigned char svisionn_palette[] =
{
0, 0, 0,
188, 242, 244, // darker
129, 204, 255,
245, 249, 248
};
PALETTE_INIT_MEMBER(svision_state, svision)
{
int i;
for( i = 0; i < sizeof(svision_palette) / 3; i++ ) {
palette.set_pen_color(i, svision_palette[i*3], svision_palette[i*3+1], svision_palette[i*3+2] );
}
}
PALETTE_INIT_MEMBER(svision_state,svisionn)
{
int i;
for ( i = 0; i < sizeof(svisionn_palette) / 3; i++ ) {
palette.set_pen_color(i, svisionn_palette[i*3], svisionn_palette[i*3+1], svisionn_palette[i*3+2] );
}
}
PALETTE_INIT_MEMBER(svision_state,svisionp)
{
int i;
for ( i = 0; i < sizeof(svisionn_palette) / 3; i++ ) {
palette.set_pen_color(i, svisionp_palette[i*3], svisionp_palette[i*3+1], svisionp_palette[i*3+2] );
}
}
UINT32 svision_state::screen_update_svision(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
int x, y, i, j=XPOS/4+YPOS*0x30;
UINT8 *videoram = m_videoram;
if (BANK&8)
{
for (y=0; y<160; y++)
{
UINT16 *line = &bitmap.pix16(y, 3 - (XPOS & 3));
for (x=3-(XPOS&3),i=0; x<160+3 && x<XSIZE+3; x+=4,i++)
{
UINT8 b=videoram[j+i];
line[3]=((b>>6)&3)+PALETTE_START;
line[2]=((b>>4)&3)+PALETTE_START;
line[1]=((b>>2)&3)+PALETTE_START;
line[0]=((b>>0)&3)+PALETTE_START;
line+=4;
}
j += 0x30;
if (j >= 8160)
j = 0; //sssnake
}
}
else
{
bitmap.plot_box(3, 0, 162, 159, PALETTE_START);
}
return 0;
}
UINT32 svision_state::screen_update_tvlink(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
int x, y, i, j = XPOS/4+YPOS*0x30;
UINT8 *videoram = m_videoram;
if (BANK & 8)
{
for (y = 0; y < 160; y++)
{
UINT32 *line = &bitmap.pix32(y, 3 - (XPOS & 3));
for (x = 3 - (XPOS & 3), i = 0; x < 160 + 3 && x < XSIZE + 3; x += 4, i++)
{
UINT8 b=videoram[j+i];
line[3]=m_tvlink.palette[(b>>6)&3];
line[2]=m_tvlink.palette[(b>>4)&3];
line[1]=m_tvlink.palette[(b>>2)&3];
line[0]=m_tvlink.palette[(b>>0)&3];
line+=4;
}
j += 0x30;
if (j >= 8160)
j = 0; //sssnake
}
}
else
{
bitmap.plot_box(3, 0, 162, 159, m_palette->pen(PALETTE_START));
}
return 0;
}
INTERRUPT_GEN_MEMBER(svision_state::svision_frame_int)
{
if (BANK&1)
device.execute().set_input_line(INPUT_LINE_NMI, PULSE_LINE);
m_sound->sound_decrement();
}
DRIVER_INIT_MEMBER(svision_state,svision)
{
m_svision.timer1 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(svision_state::svision_timer),this));
m_sound = machine().device<svision_sound_device>("custom");
m_dma_finished = m_sound->dma_finished();
m_pet.on = FALSE;
m_user1 = memregion("user1");
m_bank1 = membank("bank1");
m_bank2 = membank("bank2");
m_bank2->set_base(m_user1->base() + 0x1c000);
}
DRIVER_INIT_MEMBER(svision_state,svisions)
{
m_svision.timer1 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(svision_state::svision_timer),this));
m_sound = machine().device<svision_sound_device>("custom");
m_dma_finished = m_sound->dma_finished();
m_user1 = memregion("user1");
m_bank1 = membank("bank1");
m_bank2 = membank("bank2");
m_bank2->set_base(m_user1->base() + 0x1c000);
m_pet.on = TRUE;
m_pet.timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(svision_state::svision_pet_timer),this));
}
DEVICE_IMAGE_LOAD_MEMBER( svision_state, svision_cart )
{
UINT32 size;
dynamic_buffer temp_copy;
int mirror, i;
if (image.software_entry() == NULL)
{
size = image.length();
if (size > memregion("user1")->bytes())
{
image.seterror(IMAGE_ERROR_UNSPECIFIED, "Unsupported cartridge size");
return IMAGE_INIT_FAIL;
}
temp_copy.resize(size);
if (image.fread( temp_copy, size) != size)
{
image.seterror(IMAGE_ERROR_UNSPECIFIED, "Unable to fully read from file");
return IMAGE_INIT_FAIL;
}
}
else
{
size = image.get_software_region_length("rom");
temp_copy.resize(size);
memcpy(temp_copy, image.get_software_region("rom"), size);
}
mirror = memregion("user1")->bytes() / size;
/* With the following, we mirror the cart in the whole "user1" memory region */
for (i = 0; i < mirror; i++)
{
memcpy(memregion("user1")->base() + i * size, temp_copy, size);
}
return IMAGE_INIT_PASS;
}
void svision_state::machine_reset()
{
m_svision.timer_shot = FALSE;
*m_dma_finished = FALSE;
m_bank1->set_base(m_user1->base());
}
MACHINE_RESET_MEMBER(svision_state,tvlink)
{
m_svision.timer_shot = FALSE;
*m_dma_finished = FALSE;
m_bank1->set_base(m_user1->base());
m_tvlink.palette_on = FALSE;
memset(m_reg + 0x800, 0xff, 0x40); // normally done from m_tvlink microcontroller
m_reg[0x82a] = 0xdf;
m_tvlink.palette[0] = MAKE24_RGB32(svisionp_palette[(PALETTE_START+0)*3+0], svisionp_palette[(PALETTE_START+0)*3+1], svisionp_palette[(PALETTE_START+0)*3+2]);
m_tvlink.palette[1] = MAKE24_RGB32(svisionp_palette[(PALETTE_START+1)*3+0], svisionp_palette[(PALETTE_START+1)*3+1], svisionp_palette[(PALETTE_START+1)*3+2]);
m_tvlink.palette[2] = MAKE24_RGB32(svisionp_palette[(PALETTE_START+2)*3+0], svisionp_palette[(PALETTE_START+2)*3+1], svisionp_palette[(PALETTE_START+2)*3+2]);
m_tvlink.palette[3] = MAKE24_RGB32(svisionp_palette[(PALETTE_START+3)*3+0], svisionp_palette[(PALETTE_START+3)*3+1], svisionp_palette[(PALETTE_START+3)*3+2]);
}
static MACHINE_CONFIG_START( svision, svision_state )
/* basic machine hardware */
MCFG_CPU_ADD("maincpu", M65C02, 4000000) /* ? stz used! speed? */
MCFG_CPU_PROGRAM_MAP(svision_mem)
MCFG_CPU_VBLANK_INT_DRIVER("screen", svision_state, svision_frame_int)
/* video hardware */
MCFG_SCREEN_ADD("screen", LCD)
MCFG_SCREEN_REFRESH_RATE(61)
MCFG_SCREEN_SIZE(3+160+3, 160)
MCFG_SCREEN_VISIBLE_AREA(3+0, 3+160-1, 0, 160-1)
MCFG_SCREEN_UPDATE_DRIVER(svision_state, screen_update_svision)
MCFG_SCREEN_PALETTE("palette")
MCFG_PALETTE_ADD("palette", ARRAY_LENGTH(svision_palette) * 3)
MCFG_PALETTE_INIT_OWNER(svision_state, svision )
MCFG_DEFAULT_LAYOUT(layout_svision)
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_SOUND_ADD("custom", SVISION, 0)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.50)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.50)
/* cartridge */
MCFG_CARTSLOT_ADD("cart")
MCFG_CARTSLOT_EXTENSION_LIST("bin,ws,sv")
MCFG_CARTSLOT_MANDATORY
MCFG_CARTSLOT_INTERFACE("svision_cart")
MCFG_CARTSLOT_LOAD(svision_state, svision_cart)
/* Software lists */
MCFG_SOFTWARE_LIST_ADD("cart_list","svision")
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( svisions, svision )
MCFG_TIMER_DRIVER_ADD_PERIODIC("pet_timer", svision_state, svision_pet_timer_dev, attotime::from_seconds(8))
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( svisionp, svision )
MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_CLOCK(4430000)
MCFG_SCREEN_MODIFY("screen")
MCFG_SCREEN_REFRESH_RATE(50)
MCFG_PALETTE_MODIFY("palette")
MCFG_PALETTE_INIT_OWNER(svision_state, svisionp )
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( svisionn, svision )
MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_CLOCK(3560000/*?*/)
MCFG_SCREEN_MODIFY("screen")
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_PALETTE_MODIFY("palette")
MCFG_PALETTE_INIT_OWNER(svision_state, svisionn )
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( tvlinkp, svisionp )
MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_PROGRAM_MAP(tvlink_mem)
MCFG_MACHINE_RESET_OVERRIDE(svision_state, tvlink )
MCFG_SCREEN_MODIFY("screen")
MCFG_SCREEN_NO_PALETTE
MCFG_SCREEN_UPDATE_DRIVER(svision_state, screen_update_tvlink)
MACHINE_CONFIG_END
ROM_START(svision)
ROM_REGION(0x20000, "user1", ROMREGION_ERASE00)
ROM_END
#define rom_svisions rom_svision
#define rom_svisionn rom_svision
#define rom_svisionp rom_svision
#define rom_tvlinkp rom_svision
/***************************************************************************
Game driver(s)
***************************************************************************/
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */
// marketed under a ton of firms and names
CONS(1992, svision, 0, 0, svision, svision, svision_state, svision, "Watara", "Super Vision", 0)
// svdual 2 connected via communication port
CONS( 1992, svisions, svision, 0,svisions, svisions, svision_state, svisions, "Watara", "Super Vision (PeT Communication Simulation)", 0 )
CONS( 1993, svisionp, svision, 0,svisionp, svision, svision_state, svision, "Watara", "Super Vision (PAL TV Link Colored)", 0 )
CONS( 1993, svisionn, svision, 0,svisionn, svision, svision_state, svision, "Watara", "Super Vision (NTSC TV Link Colored)", 0 )
// svtvlink (2 supervisions)
// tvlink (pad supervision simulated)
CONS( 199?, tvlinkp, svision, 0,tvlinkp, svision, svision_state, svision, "Watara", "TV Link PAL", 0 )