// license:LGPL-2.1+ // 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 "audio/vboy.h" #include "cpu/v810/v810.h" #include "bus/vboy/slot.h" #include "bus/vboy/rom.h" #include "machine/timer.h" #include "emupal.h" #include "screen.h" #include "softlist.h" #include "speaker.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_t 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 = bitswap<16>(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_t */ struct vboy_regs_t { uint32_t lpc, lpc2, lpt, lpr; uint32_t khb, klb; uint8_t thb, tlb; uint32_t tcr, wcr, kcr; }; struct vip_regs_t { uint16_t INTPND; uint16_t INTENB; uint16_t DPSTTS; uint16_t DPCTRL; uint16_t BRTA; uint16_t BRTB; uint16_t BRTC; uint16_t REST; uint16_t FRMCYC; uint16_t CTA; uint16_t XPSTTS; uint16_t XPCTRL; uint16_t VER; uint16_t SPT[4]; uint16_t GPLT[4]; uint16_t JPLT[4]; uint16_t BKCOL; }; struct vboy_timer_t { uint16_t count; uint16_t 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_maincpu(*this, "maincpu"), m_cart(*this, "cartslot"), m_maintimer(*this, "timer_main"), 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; } void vboy(machine_config &config); private: required_device m_maincpu; required_device m_cart; required_device m_maintimer; required_device m_palette; memory_region *m_cart_rom; DECLARE_READ32_MEMBER(io_r); DECLARE_WRITE32_MEMBER(io_w); DECLARE_READ16_MEMBER(vip_r); DECLARE_WRITE16_MEMBER(vip_w); DECLARE_WRITE16_MEMBER(font0_w); DECLARE_WRITE16_MEMBER(font1_w); DECLARE_WRITE16_MEMBER(font2_w); DECLARE_WRITE16_MEMBER(font3_w); DECLARE_READ16_MEMBER(font0_r); DECLARE_READ16_MEMBER(font1_r); DECLARE_READ16_MEMBER(font2_r); DECLARE_READ16_MEMBER(font3_r); DECLARE_WRITE16_MEMBER(vboy_bgmap_w); DECLARE_READ16_MEMBER(vboy_bgmap_r); DECLARE_READ8_MEMBER(lfb0_r); DECLARE_READ8_MEMBER(lfb1_r); DECLARE_READ8_MEMBER(rfb0_r); DECLARE_READ8_MEMBER(rfb1_r); DECLARE_WRITE8_MEMBER(lfb0_w); DECLARE_WRITE8_MEMBER(lfb1_w); DECLARE_WRITE8_MEMBER(rfb0_w); DECLARE_WRITE8_MEMBER(rfb1_w); std::unique_ptr m_font; std::unique_ptr m_bgmap; std::unique_ptr m_l_frame_0; std::unique_ptr m_l_frame_1; std::unique_ptr m_r_frame_0; std::unique_ptr m_r_frame_1; vboy_regs_t m_vboy_regs; vip_regs_t m_vip_regs; vboy_timer_t m_vboy_timer; std::unique_ptr m_ovr_tempdraw_map; uint16_t m_frame_count; uint8_t m_displayfb; uint8_t m_drawfb; uint8_t m_row_num; attotime m_input_latch_time; void m_timer_tick(void); void m_scanline_tick(int scanline, uint8_t screen_type); void m_set_irq(uint16_t irq_vector); void put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, uint16_t code, uint8_t pal); void fill_ovr_char(uint16_t code, uint8_t pal); int8_t get_bg_map_pixel(int num, int xpos, int ypos); void draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my,int h, int w, uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num); void draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int gx, int gp, int gy, int h, int w, uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num); uint8_t display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt); void m_set_brightness(void); virtual void machine_start() override; virtual void machine_reset() override; virtual void video_start() override; DECLARE_PALETTE_INIT(vboy); uint32_t screen_update_vboy_left(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); uint32_t 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); void vboy_io(address_map &map); void vboy_mem(address_map &map); }; void vboy_state::video_start() { // Allocate memory for temporary screens m_ovr_tempdraw_map = make_unique_clear(0x40); // Allocate memory for framebuffers m_l_frame_0 = make_unique_clear(0x6000); m_l_frame_1 = make_unique_clear(0x6000); m_r_frame_0 = make_unique_clear(0x6000); m_r_frame_1 = make_unique_clear(0x6000); m_font = make_unique_clear((0x8000 >> 1)*4 * 2); m_bgmap = make_unique_clear(0x20000 >> 1); } void vboy_state::put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, uint16_t code, uint8_t pal) { uint16_t data; uint8_t 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_t code, uint8_t pal) { uint16_t data; uint8_t 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_t vboy_state::get_bg_map_pixel(int num, int xpos, int ypos) { // g_profiler.start(PROFILER_USER1); int x, y; uint8_t stepx, stepy; y = ypos >>3; x = xpos >>3; stepx = (x & 0x1c0) >> 6; stepy = ((y & 0x1c0) >> 6) * (stepx+1); uint16_t 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_t data; uint8_t 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_t param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my, int h, int w, uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num) { // g_profiler.start(PROFILER_USER2); int x,y; for(y=0;y<=h;y++) { int32_t 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_t 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_t)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_t param_base, int gx, int gp, int gy, int h, int w, uint16_t x_mask, uint16_t y_mask, uint8_t 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_t)READ_BGMAP(param_base + (y*8+0)) / 8.0; float prlx = (int16_t)READ_BGMAP(param_base + (y*8+1)) / 8.0; float v_skw = (int16_t)READ_BGMAP(param_base + (y*8+2)) / 8.0; float h_scl = (int16_t)READ_BGMAP(param_base + (y*8+3)) / 512.0; float v_scl = (int16_t)READ_BGMAP(param_base + (y*8+4)) / 512.0; h_skw += right ? -prlx : prlx; for(x=0;x<=w;x++) { int32_t src_x,src_y; int16_t y1 = (y+gy); int16_t x1 = (x+gx); int pix = 0; x1 += right ? -gp : gp; src_x = (int32_t)((h_skw) + (h_scl * x)); src_y = (int32_t)((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_t vboy_state::display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt) { num <<= 4; uint16_t def = READ_WORLD(num); uint8_t lon = (def >> 15) & 1; uint8_t ron = (def >> 14) & 1; uint8_t mode = (def >> 12) & 3; uint16_t scx = 64 << ((def >> 10) & 3); uint16_t scy = 64 << ((def >> 8) & 3); uint8_t ovr = (def >> 7) & 1; uint8_t end = (def >> 6) & 1; int16_t gx = READ_WORLD(num+1); int16_t gp = READ_WORLD(num+2); int16_t gy = READ_WORLD(num+3); int16_t mx = READ_WORLD(num+4); int16_t mp = READ_WORLD(num+5); int16_t my = READ_WORLD(num+6); uint16_t w = READ_WORLD(num+7); uint16_t h = READ_WORLD(num+8); uint16_t param_base = READ_WORLD(num+9) & 0xfff0; uint16_t ovr_char = READ_BGMAP(READ_WORLD(num+10)); uint8_t 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_t start_ndx = i * 4; int16_t jx = READ_OBJECTS(start_ndx+0); int16_t jp = READ_OBJECTS(start_ndx+1) & 0x3fff; int16_t jy = READ_OBJECTS(start_ndx+2) & 0x1ff; uint16_t val = READ_OBJECTS(start_ndx+3); uint8_t jlon = (READ_OBJECTS(start_ndx+1) & 0x8000) >> 15; uint8_t 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_t 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_t pen; uint8_t 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_t 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_t 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_t 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_t 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::font0_w ) { WRITE_FONT(offset); } WRITE16_MEMBER( vboy_state::font1_w ) { WRITE_FONT(offset+0x1000); } WRITE16_MEMBER( vboy_state::font2_w ) { WRITE_FONT(offset+0x2000); } WRITE16_MEMBER( vboy_state::font3_w ) { WRITE_FONT(offset+0x3000); } READ16_MEMBER( vboy_state::font0_r ) { return READ_FONT(offset); } READ16_MEMBER( vboy_state::font1_r ) { return READ_FONT(offset + 0x1000); } READ16_MEMBER( vboy_state::font2_r ) { return READ_FONT(offset + 0x2000); } READ16_MEMBER( vboy_state::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::lfb0_r ) { return m_l_frame_0[offset]; } READ8_MEMBER( vboy_state::lfb1_r ) { return m_l_frame_1[offset]; } READ8_MEMBER( vboy_state::rfb0_r ) { return m_r_frame_0[offset]; } READ8_MEMBER( vboy_state::rfb1_r ) { return m_r_frame_1[offset]; } WRITE8_MEMBER( vboy_state::lfb0_w ) { m_l_frame_0[offset] = data; } WRITE8_MEMBER( vboy_state::lfb1_w ) { m_l_frame_1[offset] = data; } WRITE8_MEMBER( vboy_state::rfb0_w ) { m_r_frame_0[offset] = data; } WRITE8_MEMBER( vboy_state::rfb1_w ) { m_r_frame_1[offset] = data; } void vboy_state::vboy_mem(address_map &map) { map.global_mask(0x07ffffff); map(0x00000000, 0x00005fff).rw(FUNC(vboy_state::lfb0_r), FUNC(vboy_state::lfb0_w)); // L frame buffer 0 map(0x00006000, 0x00007fff).rw(FUNC(vboy_state::font0_r), FUNC(vboy_state::font0_w)); // Font 0-511 map(0x00008000, 0x0000dfff).rw(FUNC(vboy_state::lfb1_r), FUNC(vboy_state::lfb1_w)); // L frame buffer 1 map(0x0000e000, 0x0000ffff).rw(FUNC(vboy_state::font1_r), FUNC(vboy_state::font1_w)); // Font 512-1023 map(0x00010000, 0x00015fff).rw(FUNC(vboy_state::rfb0_r), FUNC(vboy_state::rfb0_w)); // R frame buffer 0 map(0x00016000, 0x00017fff).rw(FUNC(vboy_state::font2_r), FUNC(vboy_state::font2_w)); // Font 1024-1535 map(0x00018000, 0x0001dfff).rw(FUNC(vboy_state::rfb1_r), FUNC(vboy_state::rfb1_w)); // R frame buffer 1 map(0x0001e000, 0x0001ffff).rw(FUNC(vboy_state::font3_r), FUNC(vboy_state::font3_w)); // Font 1536-2047 map(0x00020000, 0x0003ffff).rw(FUNC(vboy_state::vboy_bgmap_r), FUNC(vboy_state::vboy_bgmap_w)); // VIPC memory //AM_RANGE( 0x00040000, 0x0005ffff ) AM_RAM // VIPC map(0x0005f800, 0x0005f87f).rw(FUNC(vboy_state::vip_r), FUNC(vboy_state::vip_w)); map(0x00078000, 0x00079fff).rw(FUNC(vboy_state::font0_r), FUNC(vboy_state::font0_w)); // Font 0-511 mirror map(0x0007a000, 0x0007bfff).rw(FUNC(vboy_state::font1_r), FUNC(vboy_state::font1_w)); // Font 512-1023 mirror map(0x0007c000, 0x0007dfff).rw(FUNC(vboy_state::font2_r), FUNC(vboy_state::font2_w)); // Font 1024-1535 mirror map(0x0007e000, 0x0007ffff).rw(FUNC(vboy_state::font3_r), FUNC(vboy_state::font3_w)); // Font 1536-2047 mirror map(0x01000000, 0x010005ff).rw("vbsnd", FUNC(vboysnd_device::read), FUNC(vboysnd_device::write)); map(0x02000000, 0x0200002b).mirror(0x0ffff00).rw(FUNC(vboy_state::io_r), FUNC(vboy_state::io_w)); // Hardware control registers mask 0xff //AM_RANGE( 0x04000000, 0x04ffffff ) // Expansion area map(0x05000000, 0x0500ffff).mirror(0x0ff0000).ram().share("wram");// Main RAM - 64K mask 0xffff map(0x06000000, 0x06003fff).rw(m_cart, FUNC(vboy_cart_slot_device::read_eeprom), FUNC(vboy_cart_slot_device::write_eeprom)); // Cart RAM - 8K NVRAM // AM_RANGE( 0x07000000, 0x071fffff ) AM_MIRROR(0x0e00000) AM_DEVREAD("cartslot", vboy_cart_slot_device, read_cart) /* ROM */ } void vboy_state::vboy_io(address_map &map) { map.global_mask(0x07ffffff); map(0x00000000, 0x00005fff).ram().share("l_frame_0"); // L frame buffer 0 map(0x00006000, 0x00007fff).rw(FUNC(vboy_state::font0_r), FUNC(vboy_state::font0_w)); // Font 0-511 map(0x00008000, 0x0000dfff).ram().share("l_frame_1"); // L frame buffer 1 map(0x0000e000, 0x0000ffff).rw(FUNC(vboy_state::font1_r), FUNC(vboy_state::font1_w)); // Font 512-1023 map(0x00010000, 0x00015fff).ram().share("r_frame_0"); // R frame buffer 0 map(0x00016000, 0x00017fff).rw(FUNC(vboy_state::font2_r), FUNC(vboy_state::font2_w)); // Font 1024-1535 map(0x00018000, 0x0001dfff).ram().share("r_frame_1"); // R frame buffer 1 map(0x0001e000, 0x0001ffff).rw(FUNC(vboy_state::font3_r), FUNC(vboy_state::font3_w)); // Font 1536-2047 map(0x00020000, 0x0003ffff).rw(FUNC(vboy_state::vboy_bgmap_r), FUNC(vboy_state::vboy_bgmap_w)); // VIPC memory //AM_RANGE( 0x00040000, 0x0005ffff ) AM_RAM // VIPC map(0x0005f800, 0x0005f87f).rw(FUNC(vboy_state::vip_r), FUNC(vboy_state::vip_w)); map(0x00078000, 0x00079fff).rw(FUNC(vboy_state::font0_r), FUNC(vboy_state::font0_w)); // Font 0-511 mirror map(0x0007a000, 0x0007bfff).rw(FUNC(vboy_state::font1_r), FUNC(vboy_state::font1_w)); // Font 512-1023 mirror map(0x0007c000, 0x0007dfff).rw(FUNC(vboy_state::font2_r), FUNC(vboy_state::font2_w)); // Font 1024-1535 mirror map(0x0007e000, 0x0007ffff).rw(FUNC(vboy_state::font3_r), FUNC(vboy_state::font3_w)); // Font 1536-2047 mirror map(0x01000000, 0x010005ff).rw("vbsnd", FUNC(vboysnd_device::read), FUNC(vboysnd_device::write)); map(0x02000000, 0x0200002b).mirror(0x0ffff00).rw(FUNC(vboy_state::io_r), FUNC(vboy_state::io_w)); // Hardware control registers mask 0xff // AM_RANGE( 0x04000000, 0x04ffffff ) // Expansion area map(0x05000000, 0x0500ffff).mirror(0x0ff0000).ram().share("wram"); // Main RAM - 64K mask 0xffff map(0x06000000, 0x06003fff).noprw(); // Cart RAM - 8K NVRAM ? // AM_RANGE( 0x07000000, 0x071fffff ) AM_MIRROR(0x0e00000) AM_DEVREAD("cartslot", vboy_cart_slot_device, read_cart) /* ROM */ } /* 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::machine_start() { // install the cart ROM as a bank into the address map. // this speeds up the rom access, by skipping the m_cart->read_rom // trampoline (but forces us to alloc always a 0x200000-wide region) if (m_cart->exists()) { std::string region_tag; m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(VBOYSLOT_ROM_REGION_TAG).c_str()); m_maincpu->space(AS_PROGRAM).install_read_bank(0x07000000, 0x071fffff, 0x0e00000, "prog_cart_bank"); m_maincpu->space(AS_IO).install_read_bank(0x07000000, 0x071fffff, 0x0e00000, "io_cart_bank"); membank("prog_cart_bank")->set_base(m_cart_rom->base()); membank("io_cart_bank")->set_base(m_cart_rom->base()); m_cart->save_eeprom(); } } 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_t 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_t screen_type) { 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 static void vboy_cart(device_slot_interface &device) { device.option_add_internal("vb_rom", VBOY_ROM_STD); device.option_add_internal("vb_eeprom", VBOY_ROM_EEPROM); } MACHINE_CONFIG_START(vboy_state::vboy) /* basic machine hardware */ MCFG_DEVICE_ADD( "maincpu", V810, XTAL(20'000'000) ) MCFG_DEVICE_PROGRAM_MAP(vboy_mem) MCFG_DEVICE_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 */ config.set_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(20'000'000)/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(20'000'000)/2,757,0,384,264,0,224) MCFG_SCREEN_UPDATE_DRIVER(vboy_state, screen_update_vboy_right) MCFG_SCREEN_PALETTE("palette") /* cartridge */ MCFG_VBOY_CARTRIDGE_ADD("cartslot", vboy_cart, nullptr) /* software lists */ MCFG_SOFTWARE_LIST_ADD("cart_list","vboy") /* sound hardware */ SPEAKER(config, "lspeaker").front_left(); SPEAKER(config, "rspeaker").front_right(); MCFG_DEVICE_ADD("vbsnd", VBOYSND) 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, "maincpu", ROMREGION_ERASEFF ) ROM_END /* Driver */ /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ CONS( 1995, vboy, 0, 0, vboy, vboy, vboy_state, empty_init, "Nintendo", "Virtual Boy", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)