// license:MAME
// copyright-holders:Angelo Salese, Miodrag Milanovic
/***************************************************************************
Nintendo Virtual Boy
driver by Miodrag Milanovic & Angelo Salese
Great info at http://www.goliathindustries.com/vb/
and http://www.vr32.de/modules/dokuwiki/doku.php?
TODO:
- sound is way incomplete
- various timing issues (irq & events aren't known)
- 3dtetris: missing gfxs on gameplay (writes to framebuffer)
- boundh: game is way too fast
- galactic: ball goes out of bounds sometimes?
- golf: missing gfxs on gameplay (writes to framebuffer)
- marioten: title screen logo is misplaced if Mario completes his animation
- nesterfb: once that you hit the pins, animation phase takes a while to start
- redalarm: gameplay doesn't work
- spaceinv: Taito logo only if you press the button, framebuffer?
- spaceinv: missing shots
- vlab: doesn't boot (irq issue?)
- wariolnd: brightness gets suddently darker during intro.
****************************************************************************/
#include "emu.h"
#include "cpu/v810/v810.h"
#include "imagedev/cartslot.h"
#include "audio/vboy.h"
#include "vboy.lh"
#define READ_BGMAP(bgoffs) m_bgmap[(bgoffs) & 0xffff]
#define READ_WORLD(wldoffs) READ_BGMAP((0x1d800 >> 1) + wldoffs)
#define READ_COLTAB1(wldoffs) READ_BGMAP((0x1dc00 >> 1) + wldoffs)
#define READ_COLTAB2(wldoffs) READ_BGMAP((0x1de00 >> 1) + wldoffs)
#define READ_OBJECTS(wldoffs) READ_BGMAP((0x1e000 >> 1) + wldoffs)
#define WRITE_OVR_TEMPDRAW_MAP(woffs, wdat) m_ovr_tempdraw_map[(woffs) & 0x3f] = wdat;
#define READ_OVR_TEMPDRAW_MAP(roffs) m_ovr_tempdraw_map[(roffs) & 0x3f];
#define READ_FONT(roffs) m_font[(roffs)&0x1ffff]
// bit of magic here, we also write pre-flipped copies of the data to extra ram we've allocated
// to simplify the draw loop (we can just pass the flip / unused bits as the upper character bits)
// (all TILE words are in the format of ccxy -ttt tttt tttt
// where 'c' = palette, 'x/y' are flips, '-' is unused(?) and 't' is your basic tile number
#define WRITE_FONT(woffs) \
COMBINE_DATA(&m_font[woffs]); /* normal */ \
UINT16 dat = m_font[woffs]; \
m_font[((woffs) + 0x4000)] = dat; /* normal */ \
m_font[((woffs) + 0x8000) ^ 7] = dat; /* flip y */ \
m_font[((woffs) + 0xc000) ^ 7] = dat; /* flip y */ \
dat = BITSWAP16(dat,1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14); \
m_font[((woffs) + 0x10000)] = dat; /* flip x */ \
m_font[((woffs) + 0x14000)] = dat; /* flip x */ \
m_font[((woffs) + 0x18000) ^ 7] = dat; /* flip x+y */ \
m_font[((woffs) + 0x1c000) ^ 7] = dat; /* flip x+y */
/* FIXME: most if not all of these must be UINT8 */
struct vboy_regs_t
{
UINT32 lpc, lpc2, lpt, lpr;
UINT32 khb, klb;
UINT8 thb, tlb;
UINT32 tcr, wcr, kcr;
};
struct vip_regs_t
{
UINT16 INTPND;
UINT16 INTENB;
UINT16 DPSTTS;
UINT16 DPCTRL;
UINT16 BRTA;
UINT16 BRTB;
UINT16 BRTC;
UINT16 REST;
UINT16 FRMCYC;
UINT16 CTA;
UINT16 XPSTTS;
UINT16 XPCTRL;
UINT16 VER;
UINT16 SPT[4];
UINT16 GPLT[4];
UINT16 JPLT[4];
UINT16 BKCOL;
};
struct vboy_timer_t
{
UINT16 count;
UINT16 latch;
};
struct vboy_timer_t;
class vboy_state : public driver_device
{
public:
vboy_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
m_maintimer(*this, "timer_main"),
m_maincpu(*this, "maincpu"),
m_palette(*this, "palette")
{
m_vip_regs.INTPND = 0;
m_vip_regs.INTENB = 0;
m_vip_regs.DPSTTS = 0;
m_vip_regs.DPCTRL = 0;
m_vip_regs.BRTA = 0;
m_vip_regs.BRTB = 0;
m_vip_regs.BRTC = 0;
m_vip_regs.REST = 0;
m_vip_regs.FRMCYC = 0;
m_vip_regs.CTA = 0;
m_vip_regs.XPSTTS = 0;
m_vip_regs.XPCTRL = 0;
m_vip_regs.VER = 0;
m_vip_regs.SPT[0] = 0;
m_vip_regs.SPT[1] = 0;
m_vip_regs.SPT[2] = 0;
m_vip_regs.SPT[3] = 0;
m_vip_regs.GPLT[0] = 0;
m_vip_regs.GPLT[1] = 0;
m_vip_regs.GPLT[2] = 0;
m_vip_regs.GPLT[3] = 0;
m_vip_regs.JPLT[0] = 0;
m_vip_regs.JPLT[1] = 0;
m_vip_regs.JPLT[2] = 0;
m_vip_regs.JPLT[3] = 0;
m_vip_regs.BKCOL = 0;
m_vboy_regs.lpc = 0;
m_vboy_regs.lpc2 = 0;
m_vboy_regs.lpt = 0;
m_vboy_regs.lpr = 0;
m_vboy_regs.khb = 0;
m_vboy_regs.klb = 0;
m_vboy_regs.thb = 0;
m_vboy_regs.tlb = 0;
m_vboy_regs.tcr = 0;
m_vboy_regs.wcr = 0;
m_vboy_regs.kcr = 0x80;
m_vboy_timer.count = 0;
m_vboy_timer.latch = 0;
}
required_device<timer_device> m_maintimer;
required_device<cpu_device> m_maincpu;
required_device<palette_device> m_palette;
DECLARE_READ32_MEMBER(io_r);
DECLARE_WRITE32_MEMBER(io_w);
DECLARE_READ16_MEMBER(vip_r);
DECLARE_WRITE16_MEMBER(vip_w);
DECLARE_WRITE16_MEMBER(vboy_font0_w);
DECLARE_WRITE16_MEMBER(vboy_font1_w);
DECLARE_WRITE16_MEMBER(vboy_font2_w);
DECLARE_WRITE16_MEMBER(vboy_font3_w);
DECLARE_READ16_MEMBER(vboy_font0_r);
DECLARE_READ16_MEMBER(vboy_font1_r);
DECLARE_READ16_MEMBER(vboy_font2_r);
DECLARE_READ16_MEMBER(vboy_font3_r);
DECLARE_WRITE16_MEMBER(vboy_bgmap_w);
DECLARE_READ16_MEMBER(vboy_bgmap_r);
DECLARE_READ8_MEMBER(vboy_lfb0_r);
DECLARE_READ8_MEMBER(vboy_lfb1_r);
DECLARE_READ8_MEMBER(vboy_rfb0_r);
DECLARE_READ8_MEMBER(vboy_rfb1_r);
DECLARE_WRITE8_MEMBER(vboy_lfb0_w);
DECLARE_WRITE8_MEMBER(vboy_lfb1_w);
DECLARE_WRITE8_MEMBER(vboy_rfb0_w);
DECLARE_WRITE8_MEMBER(vboy_rfb1_w);
UINT16 *m_font;
UINT16 *m_bgmap;
UINT8 *m_l_frame_0;
UINT8 *m_l_frame_1;
UINT8 *m_r_frame_0;
UINT8 *m_r_frame_1;
vboy_regs_t m_vboy_regs;
vip_regs_t m_vip_regs;
vboy_timer_t m_vboy_timer;
INT32 *m_ovr_tempdraw_map;
UINT16 m_frame_count;
UINT8 m_displayfb;
UINT8 m_drawfb;
UINT8 m_row_num;
attotime m_input_latch_time;
void m_timer_tick(void);
void m_scanline_tick(int scanline, UINT8 screen_type);
void m_set_irq(UINT16 irq_vector);
UINT8 *m_nvptr;
UINT32 m_vboy_sram[0x10000/4];
device_t *m_nvimage;
DECLARE_READ32_MEMBER(sram_r);
DECLARE_WRITE32_MEMBER(sram_w);
void put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, UINT16 code, UINT8 pal);
void fill_ovr_char(UINT16 code, UINT8 pal);
INT8 get_bg_map_pixel(int num, int xpos, int ypos);
void draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, UINT16 param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my,int h, int w,
UINT16 x_mask, UINT16 y_mask, UINT8 ovr, bool right, int bg_map_num);
void draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect, UINT16 param_base, int gx, int gp, int gy, int h, int w,
UINT16 x_mask, UINT16 y_mask, UINT8 ovr, bool right, int bg_map_num);
UINT8 display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt);
void m_set_brightness(void);
virtual void machine_start();
virtual void machine_reset();
virtual void video_start();
DECLARE_PALETTE_INIT(vboy);
UINT32 screen_update_vboy_left(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
UINT32 screen_update_vboy_right(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
TIMER_DEVICE_CALLBACK_MEMBER(timer_main_tick);
TIMER_DEVICE_CALLBACK_MEMBER(timer_pad_tick);
TIMER_DEVICE_CALLBACK_MEMBER(vboy_scanlineL);
TIMER_DEVICE_CALLBACK_MEMBER(vboy_scanlineR);
void vboy_machine_stop();
DECLARE_DEVICE_IMAGE_LOAD_MEMBER(vboy_cart);
};
void vboy_state::video_start()
{
//int i;
// Allocate memory for temporary screens
m_ovr_tempdraw_map = auto_alloc_array_clear(machine(), INT32, 0x40);
// Allocate memory for framebuffers
m_l_frame_0 = auto_alloc_array_clear(machine(), UINT8, 0x6000);
m_l_frame_1 = auto_alloc_array_clear(machine(), UINT8, 0x6000);
m_r_frame_0 = auto_alloc_array_clear(machine(), UINT8, 0x6000);
m_r_frame_1 = auto_alloc_array_clear(machine(), UINT8, 0x6000);
m_font = auto_alloc_array_clear(machine(), UINT16, (0x8000 >> 1)*4 * 2);
m_bgmap = auto_alloc_array(machine(), UINT16, 0x20000 >> 1);
memset(m_bgmap, 0, sizeof(UINT16) * (0x20000 >> 1));
}
void vboy_state::put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, UINT16 code, UINT8 pal)
{
UINT16 data;
UINT8 yi, xi, dat, col;
for (yi = 0; yi < 8; yi++)
{
data = READ_FONT(code * 8 + yi);
for (xi = 0; xi < 8; xi++)
{
int res_x,res_y;
dat = ((data >> (xi << 1)) & 0x03);
res_x = x + xi;
res_y = y + yi;
col = (pal >> (dat*2)) & 3;
if (dat)
{
if (cliprect.contains(res_x, res_y))
bitmap.pix16((res_y), (res_x)) = m_palette->pen(col);
}
}
}
}
void vboy_state::fill_ovr_char(UINT16 code, UINT8 pal)
{
UINT16 data;
UINT8 yi, xi, dat;
int col;
for (yi = 0; yi < 8; yi++)
{
data = READ_FONT(code * 8 + yi);
for (xi = 0; xi < 8; xi++)
{
dat = ((data >> (xi << 1)) & 0x03);
col = (pal >> (dat*2)) & 3;
if(dat == 0)
col = -1;
WRITE_OVR_TEMPDRAW_MAP(yi*8+xi, col);
}
}
}
inline INT8 vboy_state::get_bg_map_pixel(int num, int xpos, int ypos)
{
// g_profiler.start(PROFILER_USER1);
int x, y;
UINT8 stepx, stepy;
y = ypos >>3;
x = xpos >>3;
stepx = (x & 0x1c0) >> 6;
stepy = ((y & 0x1c0) >> 6) * (stepx+1);
UINT16 val = READ_BGMAP((x & 0x3f) + (64 * (y & 0x3f)) + ((num + stepx + stepy) * 0x1000));
int pal = m_vip_regs.GPLT[(val >> 14) & 3];
int code = val & 0x3fff;
UINT16 data;
UINT8 yi, xi, dat;
yi = ypos & 7;
data = READ_FONT(code * 8 + yi);
xi = xpos & 7;
dat = ((data >> (xi << 1)) & 0x03);
if(dat == 0)
{
//g_profiler.stop();
return -1;
}
// g_profiler.stop();
return (pal >> (dat*2)) & 3;
}
void vboy_state::draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, UINT16 param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my, int h, int w,
UINT16 x_mask, UINT16 y_mask, UINT8 ovr, bool right, int bg_map_num)
{
// g_profiler.start(PROFILER_USER2);
int x,y;
for(y=0;y<=h;y++)
{
INT32 y1 = (y+gy);
if ((y1 < cliprect.min_y) || (y1 > cliprect.max_y))
continue;
int src_y = y+my;
for(x=0;x<=w;x++)
{
INT32 x1 = (x+gx);
x1 += right ? -gp : gp;
if ((x1 < cliprect.min_x) || (x1 > cliprect.max_x))
continue;
int src_x;
src_x = x+mx;
if (mode==1)
src_x += (INT16)READ_BGMAP(param_base + (y*2+(right ^ 1)));
src_x += right ? -mp : mp;
int pix = 0;
if(ovr)
{
if ((src_x > x_mask || src_y > y_mask || src_x < 0 || src_y < 0))
{
g_profiler.start(PROFILER_USER3);
pix = READ_OVR_TEMPDRAW_MAP((src_y & 7)*8+(src_x & 7));
g_profiler.stop();
}
else
{
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask);
}
}
else
{
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask);
}
if(pix != -1)
bitmap.pix16(y1, x1) = m_palette->pen(pix & 3);
}
}
// g_profiler.stop();
}
void vboy_state::draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect, UINT16 param_base, int gx, int gp, int gy, int h, int w,
UINT16 x_mask, UINT16 y_mask, UINT8 ovr, bool right, int bg_map_num)
{
// g_profiler.start(PROFILER_USER3);
int x,y;
for(y=0;y<=h;y++)
{
float h_skw = (INT16)READ_BGMAP(param_base + (y*8+0)) / 8.0;
float prlx = (INT16)READ_BGMAP(param_base + (y*8+1)) / 8.0;
float v_skw = (INT16)READ_BGMAP(param_base + (y*8+2)) / 8.0;
float h_scl = (INT16)READ_BGMAP(param_base + (y*8+3)) / 512.0;
float v_scl = (INT16)READ_BGMAP(param_base + (y*8+4)) / 512.0;
h_skw += right ? -prlx : prlx;
for(x=0;x<=w;x++)
{
INT32 src_x,src_y;
INT16 y1 = (y+gy);
INT16 x1 = (x+gx);
int pix = 0;
x1 += right ? -gp : gp;
src_x = (INT32)((h_skw) + (h_scl * x));
src_y = (INT32)((v_skw) + (v_scl * x));
if(ovr && (src_y > y_mask || src_x > x_mask || src_x < 0 || src_y < 0))
{
pix = READ_OVR_TEMPDRAW_MAP((src_y & 7)*8+(src_x & 7));
}
else
{
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask);
}
if(pix != -1)
if (cliprect.contains(x1, y1))
bitmap.pix16(y1, x1) = m_palette->pen(pix & 3);
}
}
// g_profiler.stop();
}
/*
x--- ---- ---- ---- [0] LON
-x-- ---- ---- ---- RON
--xx ---- ---- ---- BGM type
---- xx-- ---- ---- SCX
---- --xx ---- ---- SCY
---- ---- x--- ---- OVR
---- ---- -x-- ---- END
---- ---- --00 ----
---- ---- ---- xxxx BGMAP_BASE
*/
UINT8 vboy_state::display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt)
{
num <<= 4;
UINT16 def = READ_WORLD(num);
UINT8 lon = (def >> 15) & 1;
UINT8 ron = (def >> 14) & 1;
UINT8 mode = (def >> 12) & 3;
UINT16 scx = 64 << ((def >> 10) & 3);
UINT16 scy = 64 << ((def >> 8) & 3);
UINT8 ovr = (def >> 7) & 1;
UINT8 end = (def >> 6) & 1;
INT16 gx = READ_WORLD(num+1);
INT16 gp = READ_WORLD(num+2);
INT16 gy = READ_WORLD(num+3);
INT16 mx = READ_WORLD(num+4);
INT16 mp = READ_WORLD(num+5);
INT16 my = READ_WORLD(num+6);
UINT16 w = READ_WORLD(num+7);
UINT16 h = READ_WORLD(num+8);
UINT16 param_base = READ_WORLD(num+9) & 0xfff0;
UINT16 ovr_char = READ_BGMAP(READ_WORLD(num+10));
UINT8 bg_map_num = def & 0x0f;
int i;
if(end)
return 1;
if (mode < 2) // Normal / HBias Mode
{
if(ovr)
fill_ovr_char(ovr_char & 0x3fff, m_vip_regs.GPLT[(ovr_char >> 14) & 3]);
if (lon && (!right))
{
draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
}
if (ron && (right))
{
draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
}
}
else if (mode==2) // Affine Mode
{
if(ovr)
fill_ovr_char(ovr_char & 0x3fff, m_vip_regs.GPLT[(ovr_char >> 14) & 3]);
if (lon && (!right))
{
draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
}
if (ron && (right))
{
draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
}
}
else if (mode==3) // OBJ Mode
{
int start_offs, end_offs;
if(cur_spt == -1)
{
popmessage("Cur spt used with -1 pointer!");
return 0;
}
start_offs = m_vip_regs.SPT[cur_spt];
end_offs = 0x3ff;
if(cur_spt != 0)
end_offs = m_vip_regs.SPT[cur_spt-1];
i = start_offs;
do
{
UINT16 start_ndx = i * 4;
INT16 jx = READ_OBJECTS(start_ndx+0);
INT16 jp = READ_OBJECTS(start_ndx+1) & 0x3fff;
INT16 jy = READ_OBJECTS(start_ndx+2) & 0x1ff;
UINT16 val = READ_OBJECTS(start_ndx+3);
UINT8 jlon = (READ_OBJECTS(start_ndx+1) & 0x8000) >> 15;
UINT8 jron = (READ_OBJECTS(start_ndx+1) & 0x4000) >> 14;
if (!right && jlon)
put_obj(bitmap, cliprect, (jx-jp) & 0x1ff, jy, val & 0x3fff, m_vip_regs.JPLT[(val>>14) & 3]);
if(right && jron)
put_obj(bitmap, cliprect, (jx+jp) & 0x1ff, jy, val & 0x3fff, m_vip_regs.JPLT[(val>>14) & 3]);
i --;
i &= 0x3ff;
}while(i != end_offs);
if((lon && !right) || (ron && right))
cur_spt --;
}
return 0;
}
UINT32 vboy_state::screen_update_vboy_left(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
bitmap.fill(m_palette->pen(m_vip_regs.BKCOL), cliprect);
int cur_spt;
if(!(m_vip_regs.DPCTRL & 2)) /* Don't bother if screen is off */
return 0;
cur_spt = 3;
for(int i=31; i>=0; i--)
if (display_world(i, bitmap, cliprect, false, cur_spt)) break;
if(0)
{
int x,y;
for(y=0;y<224;y++)
{
for(x=0;x<384;x++)
{
UINT8 pen;
UINT8 pix;
int yi;
pen = m_l_frame_1[(x*0x40)+(y >> 2)];
yi = ((y & 0x3)*2);
pix = (pen >> yi) & 3;
bitmap.pix16(y, x) = m_palette->pen(pix & 3);
}
}
}
return 0;
}
UINT32 vboy_state::screen_update_vboy_right(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
bitmap.fill(m_palette->pen(m_vip_regs.BKCOL), cliprect);
int cur_spt;
if(!(m_vip_regs.DPCTRL & 2)) /* Don't bother if screen is off */
return 0;
cur_spt = 3;
for(int i=31; i>=0; i--)
if (display_world(i, bitmap, cliprect, true, cur_spt)) break;
return 0;
}
/**********************************
*
* I/O
*
*********************************/
READ32_MEMBER( vboy_state::io_r )
{
UINT32 value = 0x00;
switch ((offset << 2))
{
case 0x10: // KLB (Keypad Low Byte)
value = m_vboy_regs.klb; // 0x02 is always 1
break;
case 0x14: // KHB (Keypad High Byte)
value = m_vboy_regs.khb;
break;
case 0x18: // TLB (Timer Low Byte)
value = m_vboy_regs.tlb;
break;
case 0x1c: // THB (Timer High Byte)
value = m_vboy_regs.thb;
break;
case 0x20: // TCR (Timer Control Reg)
value = m_vboy_regs.tcr;
break;
case 0x24: // WCR (Wait State Control Reg)
value = m_vboy_regs.wcr;
break;
case 0x28: // KCR (Keypad Control Reg)
{
// attotime new_time = machine().time();
// if((new_time - m_input_latch_time) < m_maincpu->cycles_to_attotime(640))
value |= machine().rand() & 2;
value = m_vboy_regs.kcr | 0x4c;
}
break;
case 0x00: // LPC (Link Port Control Reg)
case 0x04: // LPC2 (Link Port Control Reg)
case 0x08: // LPT (Link Port Transmit)
case 0x0c: // LPR (Link Port Receive)
default:
logerror("Unemulated read: offset %08x\n", 0x02000000 + (offset << 2));
break;
}
return value;
}
WRITE32_MEMBER( vboy_state::io_w )
{
switch (offset<<2)
{
case 0x0c: // LPR (Link Port Receive)
case 0x10: // KLB (Keypad Low Byte)
case 0x14: // KHB (Keypad High Byte)
//logerror("Ilegal write: offset %02x should be only read\n", offset);
break;
case 0x18: // TLB (Timer Low Byte)
m_vboy_regs.tlb = data;
m_vboy_timer.latch = m_vboy_regs.tlb | (m_vboy_timer.latch & 0xff00);
break;
case 0x1c: // THB (Timer High Byte)
m_vboy_regs.thb = data;
m_vboy_timer.latch = (m_vboy_regs.thb<<8) | (m_vboy_timer.latch & 0xff);
break;
case 0x20: // TCR (Timer Control Reg)
/*
111- ---- always 1
---x ---- timer select (1=20 us, 0=100 us)
---- x--- timer irq
---- -x-- resets timer zero flag
---- --x- timer is zero flag
---- ---x enables timer
*/
if (!(data & 0x08))
{
m_maincpu->set_input_line(1, CLEAR_LINE);
}
if (data & 1)
{
m_vboy_regs.tlb = m_vboy_timer.latch & 0xff;
m_vboy_regs.thb = m_vboy_timer.latch >> 8;
m_vboy_timer.count = m_vboy_timer.latch;
// only start timer if tcr & 1 is 1 and wasn't before?
if (!(m_vboy_regs.tcr & 1))
{
if (data & 0x10)
{
m_maintimer->adjust(attotime::from_hz(50000));
}
else
{
m_maintimer->adjust(attotime::from_hz(10000));
}
}
}
m_vboy_regs.tcr = (data & 0xfd) | (0xe4) | (m_vboy_regs.tcr & 2); // according to docs: bits 5, 6 & 7 are unused and set to 1, bit 1 is read only.
if(data & 4)
m_vboy_regs.tcr &= 0xfd;
break;
case 0x24: // WCR (Wait State Control Reg)
m_vboy_regs.wcr = data | 0xfc; // according to docs: bits 2 to 7 are unused and set to 1.
break;
case 0x28: // KCR (Keypad Control Reg)
if (data & 0x04 )
{
m_vboy_regs.klb = (ioport("INPUT")->read() & 0x00ff);
m_vboy_regs.khb = (ioport("INPUT")->read() & 0xff00) >> 8;
//m_input_latch_time = machine().time();
}
if (data & 1)
{
m_vboy_regs.klb = 0;
m_vboy_regs.khb = 0;
//m_input_latch_time = attotime::zero;
}
m_vboy_regs.kcr = (data | 0x48) & 0xfd; // according to docs: bit 6 & bit 3 are unused and set to 1, bit 1 is read only.
break;
case 0x00: // LPC (Link Port Control Reg)
case 0x04: // LPC2 (Link Port Control Reg)
case 0x08: // LPT (Link Port Transmit)
default:
logerror("Unemulated write: offset %08x, data %04x\n", 0x02000000 + (offset << 2), data);
break;
}
}
/**********************************
*
* VIP
*
*********************************/
/*
TODO: brightness presumably isn't a linear algorythm, also REST needs to be taken into account (needs a working example)
*/
void vboy_state::m_set_brightness(void)
{
int a,b,c;
//d = (m_vip_regs.BRTA + m_vip_regs.BRTB + m_vip_regs.BRTC + m_vip_regs.REST);
a = (0xff * (m_vip_regs.BRTA)) / 0x80;
b = (0xff * (m_vip_regs.BRTA + m_vip_regs.BRTB)) / 0x80;
c = (0xff * (m_vip_regs.BRTA + m_vip_regs.BRTB + m_vip_regs.BRTC)) / 0x80;
if(a < 0) { a = 0; }
if(b < 0) { b = 0; }
if(c < 0) { c = 0; }
if(a > 0xff) { a = 0xff; }
if(b > 0xff) { b = 0xff; }
if(c > 0xff) { c = 0xff; }
// popmessage("%02x %02x %02x %02x",m_vip_regs.BRTA,m_vip_regs.BRTB,m_vip_regs.BRTC,m_vip_regs.REST);
m_palette->set_pen_color(1, a,0,0);
m_palette->set_pen_color(2, b,0,0);
m_palette->set_pen_color(3, c,0,0);
}
READ16_MEMBER( vboy_state::vip_r )
{
switch(offset << 1) {
case 0x00: //INTPND
return m_vip_regs.INTPND;
case 0x02: //INTENB
return m_vip_regs.INTENB;
case 0x04: //INTCLR
logerror("Error reading INTCLR\n");
break;
/*
---- -x-- ---- ---- LOCK (status column table address (CTA) lock)
---- --x- ---- ---- SYNCE (status of sync signal enable)
---- ---x ---- ---- RE (status of memory refresh cycle)
---- ---- x--- ---- FCLK
---- ---- -x-- ---- SCANRDY (active low)
---- ---- --xx xx-- DPBSY (current framebuffer displayed)
---- ---- --10 00-- RFB1
---- ---- --01 00-- LFB1
---- ---- --00 10-- RFB0
---- ---- --00 01-- LFB0
---- ---- ---- --x- DISP
*/
case 0x20: //DPSTTS
{
UINT16 res;
res = m_vip_regs.DPCTRL & 0x0702;
if(m_vip_regs.DPCTRL & 2)
{
if(m_row_num < 224/8)
{
if(m_displayfb == 0)
res |= 0x0c;
else
res |= 0x30;
}
}
res |= 0x40;
//printf("%04x\n",res);
return res;
}
case 0x22: //DPCTRL
return m_vip_regs.DPCTRL;
case 0x24: //BRTA
return m_vip_regs.BRTA;
case 0x26: //BRTB
return m_vip_regs.BRTB;
case 0x28: //BRTC
return m_vip_regs.BRTC;
case 0x2A: //REST
return m_vip_regs.REST;
case 0x2E: //FRMCYC
return m_vip_regs.FRMCYC;
case 0x30: //CTA
printf("Read CTA\n");
return m_vip_regs.CTA;
case 0x40: //XPSTTS, piXel Processor STaTuS
{
/*
x--- ---- ---- ---- SBOUT
---x xxxx ---- ---- SBCOUNT
---- ---- ---x ---- OVERTIME (process overflow)
---- ---- ---- x--- XPBSY1 (second framebuffer busy flag)
---- ---- ---- -x-- XPBSY0 (first framebfuffer busy flag)
---- ---- ---- --x- XPEN (starts drawing at beginning of game frame)
---- ---- ---- ---x XPRST (force drawing process to idle)
*/
UINT16 res;
//printf("%d\n",row_num);
res = m_vip_regs.XPSTTS & 0x00f3; // empty ^^'
res |= m_drawfb << 2;
if(m_row_num < 224/8)
{
res |= 0x8000;
res |= m_row_num<<8;
}
return res;
}
case 0x42: //XPCTRL
return m_vip_regs.XPCTRL;
case 0x44: //VER
printf("%08x read VER\n",m_maincpu->pc());
return m_vip_regs.VER;
case 0x48: //SPT0
return m_vip_regs.SPT[0];
case 0x4A: //SPT1
return m_vip_regs.SPT[1];
case 0x4C: //SPT2
return m_vip_regs.SPT[2];
case 0x4E: //SPT3
return m_vip_regs.SPT[3];
case 0x60: //GPLT0
return m_vip_regs.GPLT[0];
case 0x62: //GPLT1
return m_vip_regs.GPLT[1];
case 0x64: //GPLT2
return m_vip_regs.GPLT[2];
case 0x66: //GPLT3
return m_vip_regs.GPLT[3];
case 0x68: //JPLT0
return m_vip_regs.JPLT[0];
case 0x6A: //JPLT1
return m_vip_regs.JPLT[1];
case 0x6C: //JPLT2
return m_vip_regs.JPLT[2];
case 0x6E: //JPLT3
return m_vip_regs.JPLT[3];
case 0x70: //BKCOL
return m_vip_regs.BKCOL;
default:
logerror("Unemulated read: addr %08x\n", offset * 2 + 0x0005f800);
break;
}
return 0xffff;
}
WRITE16_MEMBER( vboy_state::vip_w )
{
if(mem_mask != 0xffff)
printf("%04x %02x\n",mem_mask,offset*2);
switch(offset << 1) {
/*
x--- ---- ---- ---- TIME_ERR
-x-- ---- ---- ---- XP_END
--x- ---- ---- ---- SB_HIT
---- ---- ---x ---- FRAME_START
---- ---- ---- x--- GAME_START
---- ---- ---- -x-- RFB_END
---- ---- ---- --x- LFB_END
---- ---- ---- ---x SCAN_ERR
*/
case 0x00: //INTPND
logerror("Error writing INTPND\n");
break;
case 0x02: //INTENB
m_vip_regs.INTENB = data;
m_set_irq(0);
//printf("%04x ENB\n",data);
break;
case 0x04: //INTCLR
m_vip_regs.INTPND &= ~data;
m_set_irq(0);
//else
// printf("%04x\n",m_vip_regs.INTPND);
break;
case 0x20: //DPSTTS
logerror("Error writing DPSTTS\n");
break;
/*
---- -x-- ---- ---- LOCK (status column table address (CTA) lock)
---- --x- ---- ---- SYNCE (status of sync signal enable)
---- ---x ---- ---- RE (status of memory refresh cycle)
---- ---- ---- --x- DISP
---- ---- ---- ---x DPRST (Resets the VIP internal counter)
*/
case 0x22: //DPCTRL
m_vip_regs.DPCTRL = data & 0x0702;
if(data & 1)
{
m_vip_regs.INTPND &= 0xe000; // reset FRAME_START, GAME_START, RFB_END, LFB_END and SCAN_ERR irqs
m_set_irq(0);
}
break;
case 0x24: //BRTA
m_vip_regs.BRTA = data;
m_set_brightness();
break;
case 0x26: //BRTB
m_vip_regs.BRTB = data;
m_set_brightness();
break;
case 0x28: //BRTC
m_vip_regs.BRTC = data;
m_set_brightness();
break;
case 0x2A: //REST
m_vip_regs.REST = data;
m_set_brightness();
if(data)
printf("%04x REST\n",data);
break;
case 0x2E: //FRMCYC
//printf("%d\n",data);
m_vip_regs.FRMCYC = data;
break;
case 0x30: //CTA
m_vip_regs.CTA = data;
printf("%04x CTA\n",data);
break;
case 0x40: //XPSTTS
logerror("Error writing XPSTTS\n");
break;
case 0x42: //XPCTRL, w/o
/*
---- ---- ---- --x-
---- ---- ---- ---x Reset Pixel Processor
*/
m_vip_regs.XPCTRL = data & 0x1f02;
//if(data & 0x1f00)
// printf("%04x SBCMP\n",data);
if(data & 1)
{
m_vip_regs.INTPND &= 0x1fff; // reset SB_HIT, XP_END and TIME_ERR irqs
m_set_irq(0);
}
break;
case 0x44: //VER
//m_vip_regs.VER = data;
break;
case 0x48: //SPT0
m_vip_regs.SPT[0] = data & 0x3ff;
break;
case 0x4A: //SPT1
m_vip_regs.SPT[1] = data & 0x3ff;
break;
case 0x4C: //SPT2
m_vip_regs.SPT[2] = data & 0x3ff;
break;
case 0x4E: //SPT3
m_vip_regs.SPT[3] = data & 0x3ff;
break;
case 0x60: //GPLT0
m_vip_regs.GPLT[0] = data;
break;
case 0x62: //GPLT1
m_vip_regs.GPLT[1] = data;
break;
case 0x64: //GPLT2
m_vip_regs.GPLT[2] = data;
break;
case 0x66: //GPLT3
m_vip_regs.GPLT[3] = data;
break;
case 0x68: //JPLT0
m_vip_regs.JPLT[0] = data & 0xfc;
break;
case 0x6A: //JPLT1
m_vip_regs.JPLT[1] = data & 0xfc;
break;
case 0x6C: //JPLT2
m_vip_regs.JPLT[2] = data & 0xfc;
break;
case 0x6E: //JPLT3
m_vip_regs.JPLT[3] = data & 0xfc;
break;
case 0x70: //BKCOL
m_vip_regs.BKCOL = data & 3;
break;
default:
logerror("Unemulated write: addr %08x, data %04x\n", offset * 2 + 0x0005f800, data);
break;
}
}
WRITE16_MEMBER( vboy_state::vboy_font0_w )
{
WRITE_FONT(offset);
}
WRITE16_MEMBER( vboy_state::vboy_font1_w )
{
WRITE_FONT(offset+0x1000);
}
WRITE16_MEMBER( vboy_state::vboy_font2_w )
{
WRITE_FONT(offset+0x2000);
}
WRITE16_MEMBER( vboy_state::vboy_font3_w )
{
WRITE_FONT(offset+0x3000);
}
READ16_MEMBER( vboy_state::vboy_font0_r )
{
return READ_FONT(offset);
}
READ16_MEMBER( vboy_state::vboy_font1_r )
{
return READ_FONT(offset + 0x1000);
}
READ16_MEMBER( vboy_state::vboy_font2_r )
{
return READ_FONT(offset + 0x2000);
}
READ16_MEMBER( vboy_state::vboy_font3_r )
{
return READ_FONT(offset + 0x3000);
}
WRITE16_MEMBER( vboy_state::vboy_bgmap_w )
{
m_bgmap[offset] = data | (m_bgmap[offset] & (mem_mask ^ 0xffff));
}
READ16_MEMBER( vboy_state::vboy_bgmap_r )
{
return m_bgmap[offset];
}
READ8_MEMBER( vboy_state::vboy_lfb0_r ) { return m_l_frame_0[offset]; }
READ8_MEMBER( vboy_state::vboy_lfb1_r ) { return m_l_frame_1[offset]; }
READ8_MEMBER( vboy_state::vboy_rfb0_r ) { return m_r_frame_0[offset]; }
READ8_MEMBER( vboy_state::vboy_rfb1_r ) { return m_r_frame_1[offset]; }
WRITE8_MEMBER( vboy_state::vboy_lfb0_w ) { m_l_frame_0[offset] = data; }
WRITE8_MEMBER( vboy_state::vboy_lfb1_w ) { m_l_frame_1[offset] = data; }
WRITE8_MEMBER( vboy_state::vboy_rfb0_w ) { m_r_frame_0[offset] = data; }
WRITE8_MEMBER( vboy_state::vboy_rfb1_w ) { m_r_frame_1[offset] = data; }
static ADDRESS_MAP_START( vboy_mem, AS_PROGRAM, 32, vboy_state )
ADDRESS_MAP_GLOBAL_MASK(0x07ffffff)
AM_RANGE( 0x00000000, 0x00005fff ) AM_READWRITE8(vboy_lfb0_r,vboy_lfb0_w,0xffffffff) // L frame buffer 0
AM_RANGE( 0x00006000, 0x00007fff ) AM_READWRITE16(vboy_font0_r, vboy_font0_w, 0xffffffff) // Font 0-511
AM_RANGE( 0x00008000, 0x0000dfff ) AM_READWRITE8(vboy_lfb1_r,vboy_lfb1_w,0xffffffff) // L frame buffer 1
AM_RANGE( 0x0000e000, 0x0000ffff ) AM_READWRITE16(vboy_font1_r, vboy_font1_w, 0xffffffff) // Font 512-1023
AM_RANGE( 0x00010000, 0x00015fff ) AM_READWRITE8(vboy_rfb0_r,vboy_rfb0_w,0xffffffff) // R frame buffer 0
AM_RANGE( 0x00016000, 0x00017fff ) AM_READWRITE16(vboy_font2_r, vboy_font2_w, 0xffffffff) // Font 1024-1535
AM_RANGE( 0x00018000, 0x0001dfff ) AM_READWRITE8(vboy_rfb1_r,vboy_rfb1_w,0xffffffff) // R frame buffer 1
AM_RANGE( 0x0001e000, 0x0001ffff ) AM_READWRITE16(vboy_font3_r, vboy_font3_w, 0xffffffff) // Font 1536-2047
AM_RANGE( 0x00020000, 0x0003ffff ) AM_READWRITE16(vboy_bgmap_r,vboy_bgmap_w, 0xffffffff) // VIPC memory
//AM_RANGE( 0x00040000, 0x0005ffff ) AM_RAM // VIPC
AM_RANGE( 0x0005f800, 0x0005f87f ) AM_READWRITE16(vip_r, vip_w, 0xffffffff)
AM_RANGE( 0x00078000, 0x00079fff ) AM_READWRITE16(vboy_font0_r, vboy_font0_w, 0xffffffff) // Font 0-511 mirror
AM_RANGE( 0x0007a000, 0x0007bfff ) AM_READWRITE16(vboy_font1_r, vboy_font1_w, 0xffffffff) // Font 512-1023 mirror
AM_RANGE( 0x0007c000, 0x0007dfff ) AM_READWRITE16(vboy_font2_r, vboy_font2_w, 0xffffffff) // Font 1024-1535 mirror
AM_RANGE( 0x0007e000, 0x0007ffff ) AM_READWRITE16(vboy_font3_r, vboy_font3_w, 0xffffffff) // Font 1536-2047 mirror
AM_RANGE( 0x01000000, 0x010005ff ) AM_DEVREADWRITE8("vbsnd", vboysnd_device, read, write, 0xffffffff)
AM_RANGE( 0x02000000, 0x0200002b ) AM_MIRROR(0x0ffff00) AM_READWRITE(io_r, io_w) // Hardware control registers mask 0xff
//AM_RANGE( 0x04000000, 0x04ffffff ) // Expansion area
AM_RANGE( 0x05000000, 0x0500ffff ) AM_MIRROR(0x0ff0000) AM_RAM AM_SHARE("wram")// Main RAM - 64K mask 0xffff
// AM_RANGE( 0x06000000, 0x06003fff ) AM_RAM AM_SHARE("nvram") // Cart RAM - 8K NVRAM
AM_RANGE( 0x07000000, 0x071fffff ) AM_MIRROR(0x0e00000) AM_ROM AM_REGION("cartridge", 0) /* ROM */
ADDRESS_MAP_END
static ADDRESS_MAP_START( vboy_io, AS_IO, 32, vboy_state )
ADDRESS_MAP_GLOBAL_MASK(0x07ffffff)
AM_RANGE( 0x00000000, 0x00005fff ) AM_RAM AM_SHARE("l_frame_0") // L frame buffer 0
AM_RANGE( 0x00006000, 0x00007fff ) AM_READWRITE16(vboy_font0_r, vboy_font0_w, 0xffffffff) // Font 0-511
AM_RANGE( 0x00008000, 0x0000dfff ) AM_RAM AM_SHARE("l_frame_1") // L frame buffer 1
AM_RANGE( 0x0000e000, 0x0000ffff ) AM_READWRITE16(vboy_font1_r, vboy_font1_w, 0xffffffff) // Font 512-1023
AM_RANGE( 0x00010000, 0x00015fff ) AM_RAM AM_SHARE("r_frame_0") // R frame buffer 0
AM_RANGE( 0x00016000, 0x00017fff ) AM_READWRITE16(vboy_font2_r, vboy_font2_w, 0xffffffff) // Font 1024-1535
AM_RANGE( 0x00018000, 0x0001dfff ) AM_RAM AM_SHARE("r_frame_1") // R frame buffer 1
AM_RANGE( 0x0001e000, 0x0001ffff ) AM_READWRITE16(vboy_font3_r, vboy_font3_w, 0xffffffff) // Font 1536-2047
AM_RANGE( 0x00020000, 0x0003ffff ) AM_READWRITE16(vboy_bgmap_r,vboy_bgmap_w, 0xffffffff) // VIPC memory
//AM_RANGE( 0x00040000, 0x0005ffff ) AM_RAM // VIPC
AM_RANGE( 0x0005f800, 0x0005f87f ) AM_READWRITE16(vip_r, vip_w, 0xffffffff)
AM_RANGE( 0x00078000, 0x00079fff ) AM_READWRITE16(vboy_font0_r, vboy_font0_w, 0xffffffff) // Font 0-511 mirror
AM_RANGE( 0x0007a000, 0x0007bfff ) AM_READWRITE16(vboy_font1_r, vboy_font1_w, 0xffffffff) // Font 512-1023 mirror
AM_RANGE( 0x0007c000, 0x0007dfff ) AM_READWRITE16(vboy_font2_r, vboy_font2_w, 0xffffffff) // Font 1024-1535 mirror
AM_RANGE( 0x0007e000, 0x0007ffff ) AM_READWRITE16(vboy_font3_r, vboy_font3_w, 0xffffffff) // Font 1536-2047 mirror
AM_RANGE( 0x01000000, 0x010005ff ) AM_DEVREADWRITE8("vbsnd", vboysnd_device, read, write, 0xffffffff)
AM_RANGE( 0x02000000, 0x0200002b ) AM_MIRROR(0x0ffff00) AM_READWRITE(io_r, io_w) // Hardware control registers mask 0xff
// AM_RANGE( 0x04000000, 0x04ffffff ) // Expansion area
AM_RANGE( 0x05000000, 0x0500ffff ) AM_MIRROR(0x0ff0000) AM_RAM AM_SHARE("wram") // Main RAM - 64K mask 0xffff
AM_RANGE( 0x06000000, 0x06003fff ) AM_RAM AM_SHARE("nvram") // Cart RAM - 8K NVRAM
AM_RANGE( 0x07000000, 0x071fffff ) AM_MIRROR(0x0e00000) AM_ROM AM_REGION("cartridge", 0) /* ROM */
ADDRESS_MAP_END
/* Input ports */
static INPUT_PORTS_START( vboy )
PORT_START("INPUT")
PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_PLAYER(1)
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_PLAYER(1)
PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_PLAYER(1)
PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_PLAYER(1)
PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_PLAYER(1)
PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_PLAYER(1)
PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_PLAYER(1)
PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_PLAYER(1)
PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("L") PORT_PLAYER(1) // Left button on back
PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("R") PORT_PLAYER(1) // Right button on back
PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B") PORT_PLAYER(1) // B button (Mario Clash Jump button)
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A") PORT_PLAYER(1) // A button
PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNUSED ) // Always 1
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED ) // Battery low
INPUT_PORTS_END
void vboy_state::vboy_machine_stop()
{
// only do this if the cart loader detected some form of backup
if (m_nvptr != NULL)
{
device_image_interface *image = dynamic_cast<device_image_interface *>(m_nvimage);
image->battery_save(m_nvptr, 0x10000);
}
}
void vboy_state::machine_start()
{
/* add a hook for battery save */
machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(vboy_state::vboy_machine_stop),this));
// m_vboy_sram = auto_alloc_array(machine(), UINT32, 0x10000/4);
}
void vboy_state::machine_reset()
{
/* Initial values taken from Reality Boy, to be verified when emulation improves */
m_vboy_regs.lpc = 0x6d;
m_vboy_regs.lpc2 = 0xff;
m_vboy_regs.lpt = 0x00;
m_vboy_regs.lpr = 0x00;
m_vboy_regs.klb = 0x00;
m_vboy_regs.khb = 0x00;
m_vboy_regs.tlb = 0xff;
m_vboy_regs.thb = 0xff;
m_vboy_regs.tcr = 0xe4;
m_vboy_regs.wcr = 0xfc;
m_vboy_regs.kcr = 0x4c | 0x80;
m_vip_regs.DPCTRL = 2; // ssquash relies on this at boot otherwise no frame_start irq is fired
m_displayfb = 0;
m_drawfb = 0;
m_vboy_timer.count = 0;
m_maintimer->adjust(attotime::never);
}
void vboy_state::m_timer_tick()
{
if(m_vboy_timer.count > 0)
{
m_vboy_timer.count--;
m_vboy_regs.tlb = m_vboy_timer.count & 0xff;
m_vboy_regs.thb = m_vboy_timer.count >> 8;
}
if (m_vboy_timer.count == 0)
{
m_vboy_timer.count = m_vboy_timer.latch;
m_vboy_regs.tcr |= 0x02;
if(m_vboy_regs.tcr & 8)
{
m_maincpu->set_input_line(1, ASSERT_LINE);
}
}
if (m_vboy_regs.tcr & 0x10)
{
m_maintimer->adjust(attotime::from_hz(50000));
}
else
{
m_maintimer->adjust(attotime::from_hz(10000));
}
}
TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::timer_main_tick)
{
m_timer_tick();
}
TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::timer_pad_tick)
{
if((m_vboy_regs.kcr & 0x80) == 0)
m_maincpu->set_input_line(0, HOLD_LINE);
}
PALETTE_INIT_MEMBER(vboy_state, vboy)
{
palette.set_pen_color(0, rgb_t::black);
palette.set_pen_color(1, rgb_t::black);
palette.set_pen_color(2, rgb_t::black);
palette.set_pen_color(3, rgb_t::black);
}
void vboy_state::m_set_irq(UINT16 irq_vector)
{
m_vip_regs.INTPND |= irq_vector;
if(m_vip_regs.INTENB & m_vip_regs.INTPND)
m_maincpu->set_input_line(4, ASSERT_LINE);
if((m_vip_regs.INTENB & m_vip_regs.INTPND) == 0)
m_maincpu->set_input_line(4, CLEAR_LINE);
}
/* TODO: obviously all of this needs clean-ups and better implementation ... */
void vboy_state::m_scanline_tick(int scanline, UINT8 screen_type)
{
//int frame_num = machine().first_screen()->frame_number();
if(screen_type == 0)
m_row_num = (scanline / 8) & 0x1f;
if(scanline == 0)
{
if(m_vip_regs.DPCTRL & 2)
m_set_irq(0x0010); // FRAME_START
m_frame_count++;
if(m_frame_count > m_vip_regs.FRMCYC)
{
m_set_irq(0x0008); // GAME_START
m_frame_count = 0;
}
if(m_vip_regs.DPCTRL & 2)
m_displayfb ^= 1;
}
if(scanline == 224)
{
if(m_displayfb)
m_drawfb = 1;
else
m_drawfb = 2;
m_set_irq(0x4000); // XPEND
}
if(scanline == 232)
{
m_drawfb = 0;
m_set_irq(0x0002); // LFBEND
}
if(scanline == 240)
{
m_set_irq(0x0004); // RFBEND
}
if(m_row_num == ((m_vip_regs.XPCTRL & 0x1f00) >> 8))
{
m_set_irq(0x2000); // SBHIT
}
}
TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::vboy_scanlineL)
{
int scanline = param;
m_scanline_tick(scanline,0);
}
#if 0
TIMER_DEVICE_CALLBACK_MEMBER(vboy_state::vboy_scanlineR)
{
int scanline = param;
//m_scanline_tick(scanline,1);
}
#endif
READ32_MEMBER(vboy_state::sram_r)
{
return m_vboy_sram[offset];
}
WRITE32_MEMBER(vboy_state::sram_w)
{
COMBINE_DATA(&m_vboy_sram[offset]);
}
DEVICE_IMAGE_LOAD_MEMBER( vboy_state, vboy_cart )
{
UINT32 chip = 0;
UINT8 *ROM = memregion("cartridge")->base();
UINT32 cart_size;
m_nvptr = (UINT8 *)NULL;
if (image.software_entry() == NULL)
{
cart_size = image.length();
image.fread(ROM, cart_size);
switch (cart_size)
{
case 0x001000:
memcpy(ROM + 0x001000, ROM, 0x001000);
case 0x002000:
memcpy(ROM + 0x002000, ROM, 0x002000);
case 0x004000:
memcpy(ROM + 0x004000, ROM, 0x004000);
case 0x008000:
memcpy(ROM + 0x008000, ROM, 0x008000);
case 0x010000:
memcpy(ROM + 0x010000, ROM, 0x010000);
case 0x020000:
memcpy(ROM + 0x020000, ROM, 0x020000);
case 0x040000:
memcpy(ROM + 0x040000, ROM, 0x040000);
case 0x080000:
memcpy(ROM + 0x080000, ROM, 0x080000);
case 0x100000:
memcpy(ROM + 0x100000, ROM, 0x100000);
default:
break;
}
}
else
{
cart_size = image.get_software_region_length("rom");
memcpy(ROM, image.get_software_region("rom"), cart_size);
UINT8 *tmp_eeprom = image.get_software_region("eeprom");
if (tmp_eeprom)
chip = 1;
}
if (chip)
{
m_nvptr = (UINT8 *)&m_vboy_sram;
m_maincpu->space(AS_PROGRAM).install_read_handler(0x06000000, 0x0600ffff, read32_delegate(FUNC(vboy_state::sram_r),this));
m_maincpu->space(AS_PROGRAM).install_write_handler(0x06000000, 0x0600ffff, write32_delegate(FUNC(vboy_state::sram_w),this));
image.battery_load(m_nvptr, 0x10000, 0x00);
m_nvimage = image;
}
else
{
m_nvimage = NULL;
}
return IMAGE_INIT_PASS;
}
static MACHINE_CONFIG_START( vboy, vboy_state )
/* basic machine hardware */
MCFG_CPU_ADD( "maincpu", V810, XTAL_20MHz )
MCFG_CPU_PROGRAM_MAP(vboy_mem)
MCFG_CPU_IO_MAP(vboy_io)
MCFG_TIMER_DRIVER_ADD_SCANLINE("scantimer_l", vboy_state, vboy_scanlineL, "3dleft", 0, 1)
//MCFG_TIMER_DRIVER_ADD_SCANLINE("scantimer_r", vboy_state, vboy_scanlineR, "3dright", 0, 1)
// programmable timer
MCFG_TIMER_DRIVER_ADD("timer_main", vboy_state, timer_main_tick)
// pad ready, which should be once per VBL
MCFG_TIMER_DRIVER_ADD_PERIODIC("timer_pad", vboy_state, timer_pad_tick, attotime::from_hz(50.038029f))
/* video hardware */
MCFG_DEFAULT_LAYOUT(layout_vboy)
MCFG_PALETTE_ADD("palette", 4)
MCFG_PALETTE_INIT_OWNER(vboy_state, vboy)
/* Left screen */
MCFG_SCREEN_ADD("3dleft", RASTER)
MCFG_SCREEN_RAW_PARAMS(XTAL_20MHz/2,757,0,384,264,0,224)
MCFG_SCREEN_UPDATE_DRIVER(vboy_state, screen_update_vboy_left)
MCFG_SCREEN_PALETTE("palette")
/* Right screen */
MCFG_SCREEN_ADD("3dright", RASTER)
MCFG_SCREEN_RAW_PARAMS(XTAL_20MHz/2,757,0,384,264,0,224)
MCFG_SCREEN_UPDATE_DRIVER(vboy_state, screen_update_vboy_right)
MCFG_SCREEN_PALETTE("palette")
/* cartridge */
MCFG_CARTSLOT_ADD("cart")
MCFG_CARTSLOT_EXTENSION_LIST("vb,bin")
MCFG_CARTSLOT_MANDATORY
MCFG_CARTSLOT_INTERFACE("vboy_cart")
MCFG_CARTSLOT_LOAD(vboy_state, vboy_cart)
/* software lists */
MCFG_SOFTWARE_LIST_ADD("cart_list","vboy")
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_VBOYSND_ADD("vbsnd")
MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
MACHINE_CONFIG_END
/* ROM definition */
ROM_START( vboy )
ROM_REGION( 0x2000000, "cartridge", ROMREGION_ERASEFF )
ROM_END
/* Driver */
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */
CONS( 1995, vboy, 0, 0, vboy, vboy, driver_device, 0, "Nintendo", "Virtual Boy", GAME_NOT_WORKING | GAME_IMPERFECT_SOUND)