mirror of
https://github.com/holub/mame
synced 2025-06-25 22:04:15 +03:00
1447 lines
40 KiB
C
1447 lines
40 KiB
C
/***************************************************************************
|
|
|
|
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:
|
|
- 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
|
|
- telerobo: crashes if you die
|
|
- telerobo: hangs after winning first match;
|
|
- 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_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 = 0;
|
|
|
|
m_vboy_timer.count = 0;
|
|
m_vboy_timer.latch = 0;
|
|
}
|
|
|
|
required_device<timer_device> m_maintimer;
|
|
required_device<cpu_device> m_maincpu;
|
|
|
|
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();
|
|
virtual void palette_init();
|
|
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);
|
|
}
|
|
|
|
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)) = machine().pens[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) = machine().pens[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) = machine().pens[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(machine().pens[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) = machine().pens[pix & 3];
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
UINT32 vboy_state::screen_update_vboy_right(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
|
|
{
|
|
bitmap.fill(machine().pens[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);
|
|
palette_set_color_rgb(machine(), 1, a,0,0);
|
|
palette_set_color_rgb(machine(), 2, b,0,0);
|
|
palette_set_color_rgb(machine(), 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_BUTTON2 ) PORT_NAME("B") PORT_PLAYER(1) // B button (Mario Clash Jump button)
|
|
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON1 ) 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;
|
|
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);
|
|
}
|
|
|
|
void vboy_state::palette_init()
|
|
{
|
|
palette_set_color(machine(), 0, RGB_BLACK);
|
|
palette_set_color(machine(), 1, RGB_BLACK);
|
|
palette_set_color(machine(), 2, RGB_BLACK);
|
|
palette_set_color(machine(), 3, RGB_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().primary_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 )
|
|
{
|
|
vboy_state *state = image.device().machine().driver_data<vboy_state>();
|
|
UINT32 chip = 0;
|
|
UINT8 *ROM = image.device().machine().root_device().memregion("cartridge")->base();
|
|
UINT32 cart_size;
|
|
|
|
state->m_nvptr = (UINT8 *)NULL;
|
|
if (image.software_entry() == NULL)
|
|
{
|
|
cart_size = image.length();
|
|
image.fread(ROM, cart_size);
|
|
}
|
|
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)
|
|
{
|
|
state->m_nvptr = (UINT8 *)&state->m_vboy_sram;
|
|
|
|
image.device().machine().device("maincpu")->memory().space(AS_PROGRAM).install_read_handler(0x06000000, 0x0600ffff, read32_delegate(FUNC(vboy_state::sram_r),state));
|
|
image.device().machine().device("maincpu")->memory().space(AS_PROGRAM).install_write_handler(0x06000000, 0x0600ffff, write32_delegate(FUNC(vboy_state::sram_w),state));
|
|
|
|
image.battery_load(state->m_nvptr, 0x10000, 0x00);
|
|
state->m_nvimage = image;
|
|
}
|
|
else
|
|
{
|
|
state->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_LENGTH(4)
|
|
|
|
/* 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)
|
|
|
|
/* 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)
|
|
|
|
/* 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)
|