Merge pull request #2332 from DavidHaywood/nesvt2

preliminary steps for basic VT03 (NES clone) support
This commit is contained in:
R. Belmont 2017-05-23 21:22:07 -04:00 committed by GitHub
commit fbd2e33023
16 changed files with 1580 additions and 150 deletions

View File

@ -80938,23 +80938,6 @@ that the real dumps might surface -->
<!-- These are dumps from famiclones (of plug-and-play type) which are based on the famicom hw but with a slightly more powerful PPU. Hence it needs to be supported separately. We document here the dumps, in the meanwhile, but these do not belong to this list!! -->
<software name="mc_dgear" supported="no">
<description>DreamGEAR 75 in 1</description>
<year>19??</year>
<publisher>&lt;unknown&gt;</publisher>
<part name="cart" interface="nes_cart">
<feature name="slot" value="onebus" />
<feature name="pcb" value="UNL-OneBus" />
<dataarea name="prg" size="4194304">
<rom name="dreamgear 75-in-1(unl)[!].prg" size="4194304" crc="9aabcb8f" sha1="aa9446b7777fa64503871225fcaf2a17aafd9af1" offset="00000" status="baddump" />
</dataarea>
<!-- 8k VRAM on cartridge? -->
<dataarea name="vram" size="8192">
</dataarea>
</part>
</software>
<software name="mc_dg101" supported="no">
<description>DreamGEAR 101 in 1</description>
<year>19??</year>

View File

@ -982,6 +982,8 @@ if (VIDEOS["PPU2C0X"]~=null) then
files {
MAME_DIR .. "src/devices/video/ppu2c0x.cpp",
MAME_DIR .. "src/devices/video/ppu2c0x.h",
MAME_DIR .. "src/devices/video/ppu2c0x_vt.cpp",
MAME_DIR .. "src/devices/video/ppu2c0x_vt.h",
}
end

View File

@ -2410,6 +2410,7 @@ files {
MAME_DIR .. "src/mame/includes/nes.h",
MAME_DIR .. "src/mame/machine/nes.cpp",
MAME_DIR .. "src/mame/video/nes.cpp",
MAME_DIR .. "src/mame/drivers/nes_vt.cpp",
MAME_DIR .. "src/mame/drivers/pokemini.cpp",
MAME_DIR .. "src/mame/drivers/snes.cpp",
MAME_DIR .. "src/mame/includes/snes.h",

View File

@ -129,9 +129,23 @@ const address_space_config *n2a03_device::memory_space_config(address_spacenum s
}
}
WRITE_LINE_MEMBER(n2a03_device::apu_irq)
{
// games relying on the APU_IRQ don't seem to work anyway? (nes software list : timelord, mig29sf, firehawk)
set_input_line(N2A03_APU_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}
READ8_MEMBER(n2a03_device::apu_read_mem)
{
return mintf->program->read_byte(offset);
}
static MACHINE_CONFIG_START( n2a03_device )
MCFG_SOUND_ADD("nesapu", NES_APU, DERIVED_CLOCK(1,1) )
MCFG_NES_APU_IRQ_HANDLER(WRITELINE(n2a03_device, apu_irq))
MCFG_NES_APU_MEM_READ_CALLBACK(READ8(n2a03_device, apu_read_mem))
MCFG_SOUND_ROUTE(ALL_OUTPUTS, ":mono", 0.50)
MACHINE_CONFIG_END

View File

@ -33,6 +33,9 @@ public:
WRITE8_MEMBER(psg1_4015_w);
WRITE8_MEMBER(psg1_4017_w);
DECLARE_WRITE_LINE_MEMBER(apu_irq);
DECLARE_READ8_MEMBER(apu_read_mem);
protected:
class mi_2a03_normal : public memory_interface {
public:

View File

@ -49,7 +49,6 @@
#include "emu.h"
#include "nes_apu.h"
#include "cpu/m6502/n2a03.h"
#include "screen.h"
@ -112,7 +111,9 @@ nesapu_device::nesapu_device(const machine_config &mconfig, const char *tag, dev
m_samps_per_sync(0),
m_buffer_size(0),
m_real_rate(0),
m_stream(nullptr)
m_stream(nullptr),
m_irq_handler(*this),
m_mem_read_cb(*this)
{
for (auto & elem : m_noise_lut)
{
@ -176,9 +177,11 @@ void nesapu_device::calculate_rates()
void nesapu_device::device_start()
{
create_noise(m_noise_lut, 13, apu_t::NOISE_LONG);
// resolve callbacks
m_irq_handler.resolve_safe();
m_mem_read_cb.resolve_safe(0x00);
(m_APU.dpcm).memory = &downcast<n2a03_device &>(*owner()).space(AS_PROGRAM);
create_noise(m_noise_lut, 13, apu_t::NOISE_LONG);
calculate_rates();
@ -484,7 +487,7 @@ s8 nesapu_device::apu_dpcm(apu_t::dpcm_t *chan)
if (chan->regs[0] & 0x80) /* IRQ Generator */
{
chan->irq_occurred = true;
downcast<n2a03_device &>(m_APU.dpcm.memory->device()).set_input_line(N2A03_APU_IRQ_LINE, ASSERT_LINE);
m_irq_handler(true);
}
break;
}
@ -495,7 +498,7 @@ s8 nesapu_device::apu_dpcm(apu_t::dpcm_t *chan)
bit_pos = 7 - (chan->bits_left & 7);
if (7 == bit_pos)
{
chan->cur_byte = m_APU.dpcm.memory->read_byte(chan->address);
chan->cur_byte = m_mem_read_cb(chan->address);
chan->address++;
chan->length--;
}
@ -634,7 +637,7 @@ inline void nesapu_device::apu_regwrite(int address, u8 value)
case apu_t::WRE0:
m_APU.dpcm.regs[0] = value;
if (0 == (value & 0x80)) {
downcast<n2a03_device &>(m_APU.dpcm.memory->device()).set_input_line(N2A03_APU_IRQ_LINE, CLEAR_LINE);
m_irq_handler(false);
m_APU.dpcm.irq_occurred = false;
}
break;
@ -709,6 +712,7 @@ inline void nesapu_device::apu_regwrite(int address, u8 value)
else
m_APU.dpcm.enabled = false;
//m_irq_handler(false);
m_APU.dpcm.irq_occurred = false;
break;

View File

@ -39,11 +39,22 @@
* processor, as each is shared.
*/
class nesapu_device : public device_t, public device_sound_interface
#define MCFG_NES_APU_IRQ_HANDLER(_devcb) \
devcb = &nesapu_device::set_irq_handler(*device, DEVCB_##_devcb);
#define MCFG_NES_APU_MEM_READ_CALLBACK(_devcb) \
devcb = &nesapu_device::set_mem_read_callback(*device, DEVCB_##_devcb);
class nesapu_device : public device_t,
public device_sound_interface
{
public:
nesapu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
// static configuration helpers
template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<nesapu_device &>(device).m_irq_handler.set_callback(object); }
template<class _Object> static devcb_base &set_mem_read_callback(device_t &device, _Object object) { return downcast<nesapu_device &>(device).m_mem_read_cb.set_callback(object); }
virtual void device_clock_changed() override;
DECLARE_READ8_MEMBER( read );
@ -72,6 +83,8 @@ private:
u32 m_sync_times1[SYNCS_MAX1]; /* Samples per sync table */
u32 m_sync_times2[SYNCS_MAX2]; /* Samples per sync table */
sound_stream *m_stream;
devcb_write_line m_irq_handler;
devcb_read8 m_mem_read_cb;
void calculate_rates();
void create_syncs(unsigned long sps);

View File

@ -121,7 +121,6 @@ struct apu_t
uint8 cur_byte = 0;
bool enabled = false;
bool irq_occurred = false;
address_space *memory = nullptr;
signed char vol = 0;
};

View File

@ -308,7 +308,12 @@ inline void ppu2c0x_device::writebyte(offs_t address, uint8_t data)
*
*************************************/
void ppu2c0x_device::init_palette( palette_device &palette, int first_entry )
void ppu2c0x_device::init_palette(palette_device &palette, int first_entry)
{
init_palette(palette, first_entry, false);
}
void ppu2c0x_device::init_palette(palette_device &palette, int first_entry, bool indirect)
{
/* This routine builds a palette using a transformation from */
/* the YUV (Y, B-Y, R-Y) to the RGB color space */
@ -346,14 +351,14 @@ void ppu2c0x_device::init_palette( palette_device &palette, int first_entry )
switch (color_emphasis)
{
case 0: r_mod = 1.0; g_mod = 1.0; b_mod = 1.0; break;
case 1: r_mod = 1.24; g_mod = .915; b_mod = .743; break;
case 2: r_mod = .794; g_mod = 1.09; b_mod = .882; break;
case 3: r_mod = .905; g_mod = 1.03; b_mod = 1.28; break;
case 4: r_mod = .741; g_mod = .987; b_mod = 1.0; break;
case 5: r_mod = 1.02; g_mod = .908; b_mod = .979; break;
case 6: r_mod = 1.02; g_mod = .98; b_mod = .653; break;
case 7: r_mod = .75; g_mod = .75; b_mod = .75; break;
case 0: r_mod = 1.0; g_mod = 1.0; b_mod = 1.0; break;
case 1: r_mod = 1.24; g_mod = .915; b_mod = .743; break;
case 2: r_mod = .794; g_mod = 1.09; b_mod = .882; break;
case 3: r_mod = .905; g_mod = 1.03; b_mod = 1.28; break;
case 4: r_mod = .741; g_mod = .987; b_mod = 1.0; break;
case 5: r_mod = 1.02; g_mod = .908; b_mod = .979; break;
case 6: r_mod = 1.02; g_mod = .98; b_mod = .653; break;
case 7: r_mod = .75; g_mod = .75; b_mod = .75; break;
}
*/
@ -369,26 +374,26 @@ void ppu2c0x_device::init_palette( palette_device &palette, int first_entry )
switch (color_num)
{
case 0:
sat = 0; rad = 0;
y = brightness[0][color_intensity];
break;
case 0:
sat = 0; rad = 0;
y = brightness[0][color_intensity];
break;
case 13:
sat = 0; rad = 0;
y = brightness[2][color_intensity];
break;
case 13:
sat = 0; rad = 0;
y = brightness[2][color_intensity];
break;
case 14:
case 15:
sat = 0; rad = 0; y = 0;
break;
case 14:
case 15:
sat = 0; rad = 0; y = 0;
break;
default:
sat = tint;
rad = M_PI * ((color_num * 30 + hue) / 180.0);
y = brightness[1][color_intensity];
break;
default:
sat = tint;
rad = M_PI * ((color_num * 30 + hue) / 180.0);
y = brightness[1][color_intensity];
break;
}
u = sat * cos(rad);
@ -414,7 +419,10 @@ void ppu2c0x_device::init_palette( palette_device &palette, int first_entry )
B = 255;
/* Round, and set the value */
palette.set_pen_color(first_entry++, floor(R + .5), floor(G + .5), floor(B + .5));
if (indirect)
palette.set_indirect_color(first_entry++, rgb_t(floor(R + .5), floor(G + .5), floor(B + .5)));
else
palette.set_pen_color(first_entry++, floor(R + .5), floor(G + .5), floor(B + .5));
}
}
}
@ -564,6 +572,61 @@ void ppu2c0x_device::device_timer(emu_timer &timer, device_timer_id id, int para
}
}
void ppu2c0x_device::read_tile_plane_data(int address, int color)
{
m_planebuf[0] = readbyte((address & 0x1fff));
m_planebuf[1] = readbyte((address + 8) & 0x1fff);
}
void ppu2c0x_device::shift_tile_plane_data(uint8_t &pix)
{
pix = ((m_planebuf[0] >> 7) & 1) | (((m_planebuf[1] >> 7) & 1) << 1);
m_planebuf[0] = m_planebuf[0] << 1;
m_planebuf[1] = m_planebuf[1] << 1;
}
void ppu2c0x_device::draw_tile_pixel(uint8_t pix, int color, uint16_t back_pen, uint16_t *&dest, const pen_t *color_table)
{
uint16_t pen;
if (pix)
{
const pen_t *paldata = &color_table[4 * color];
pen = paldata[pix];
}
else
{
pen = back_pen;
}
*dest = pen;
}
void ppu2c0x_device::draw_tile(uint8_t *line_priority, int color_byte, int color_bits, int address, int start_x, uint16_t back_pen, uint16_t *&dest, const pen_t *color_table)
{
int color = (((color_byte >> color_bits) & 0x03));
read_tile_plane_data(address, color);
/* render the pixel */
for (int i = 0; i < 8; i++)
{
uint8_t pix;
shift_tile_plane_data(pix);
if ((start_x + i) >= 0 && (start_x + i) < VISIBLE_SCREEN_WIDTH)
{
draw_tile_pixel(pix, color, back_pen, dest, color_table);
// priority marking
if (pix)
line_priority[start_x + i] |= 0x02;
}
dest++;
}
}
void ppu2c0x_device::draw_background( uint8_t *line_priority )
{
bitmap_ind16 &bitmap = *m_bitmap;
@ -575,7 +638,6 @@ void ppu2c0x_device::draw_background( uint8_t *line_priority )
int x, tile_index, i;
const pen_t *color_table;
const pen_t *paldata;
m_tilecount = 0;
@ -616,7 +678,6 @@ void ppu2c0x_device::draw_background( uint8_t *line_priority )
int pos;
int index1;
int page, page2, address;
uint16_t pen;
index1 = tile_index + x;
@ -640,39 +701,12 @@ void ppu2c0x_device::draw_background( uint8_t *line_priority )
if (start_x < VISIBLE_SCREEN_WIDTH)
{
uint8_t plane1, plane2;
paldata = &color_table[4 * (((color_byte >> color_bits) & 0x03))];
// need to read 0x0000 or 0x1000 + 16*nametable data
address = ((m_tile_page) ? 0x1000 : 0) + (page2 * 16);
// plus something that accounts for y
address += scroll_y_fine;
plane1 = readbyte((address & 0x1fff));
plane2 = readbyte((address + 8) & 0x1fff);
/* render the pixel */
for (i = 0; i < 8; i++)
{
uint8_t pix;
pix = ((plane1 >> 7) & 1) | (((plane2 >> 7) & 1) << 1);
plane1 = plane1 << 1;
plane2 = plane2 << 1;
if ((start_x + i) >= 0 && (start_x + i) < VISIBLE_SCREEN_WIDTH)
{
if (pix)
{
pen = paldata[pix];
line_priority[start_x + i] |= 0x02;
}
else
{
pen = back_pen;
}
*dest = pen;
}
dest++;
}
draw_tile(line_priority, color_byte, color_bits, address, start_x, back_pen, dest, color_table);
start_x += 8;
@ -699,6 +733,39 @@ void ppu2c0x_device::draw_background( uint8_t *line_priority )
}
}
void ppu2c0x_device::read_sprite_plane_data(int address)
{
m_planebuf[0] = readbyte((address + 0) & 0x1fff);
m_planebuf[1] = readbyte((address + 8) & 0x1fff);
}
void ppu2c0x_device::make_sprite_pixel_data(uint8_t &pixel_data, int flipx)
{
if (flipx)
{
pixel_data = (m_planebuf[0] & 1) + ((m_planebuf[1] & 1) << 1);
m_planebuf[0] = m_planebuf[0] >> 1;
m_planebuf[1] = m_planebuf[1] >> 1;
}
else
{
pixel_data = ((m_planebuf[0] >> 7) & 1) | (((m_planebuf[1] >> 7) & 1) << 1);
m_planebuf[0] = m_planebuf[0] << 1;
m_planebuf[1] = m_planebuf[1] << 1;
}
}
void ppu2c0x_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_ind16& bitmap)
{
const pen_t *paldata = &m_colortable[4 * color];
bitmap.pix16(m_scanline, sprite_xpos + pixel) = paldata[pixel_data];
}
void ppu2c0x_device::read_extra_sprite_bits(int sprite_index)
{
// needed for some clones
}
void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
{
bitmap_ind16 &bitmap = *m_bitmap;
@ -713,8 +780,6 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
int sprite_line;
int first_pixel;
const pen_t *paldata;
int pixel;
/* determine if the sprites are 8x8 or 8x16 */
@ -724,9 +789,6 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
for (sprite_index = 0; sprite_index < SPRITERAM_SIZE; sprite_index += 4)
{
uint8_t plane1;
uint8_t plane2;
sprite_ypos = m_spriteram[sprite_index] + 1;
sprite_xpos = m_spriteram[sprite_index + 3];
@ -754,6 +816,7 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
pri = m_spriteram[sprite_index + 2] & 0x20;
flipx = m_spriteram[sprite_index + 2] & 0x40;
flipy = m_spriteram[sprite_index + 2] & 0x80;
read_extra_sprite_bits(sprite_index);
if (size == 16)
{
@ -774,8 +837,6 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
if (flipy)
sprite_line = (size - 1) - sprite_line;
paldata = &m_colortable[4 * color];
if (size == 16 && sprite_line > 7)
{
tile++;
@ -786,8 +847,7 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
if (size == 8)
index1 += ((m_sprite_page == 0) ? 0 : 0x1000);
plane1 = readbyte((index1 + sprite_line + 0) & 0x1fff);
plane2 = readbyte((index1 + sprite_line + 8) & 0x1fff);
read_sprite_plane_data(index1+sprite_line);
/* if there are more than 8 sprites on this line, set the flag */
if (sprite_count == 8)
@ -811,18 +871,7 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
for (pixel = 0; pixel < 8; pixel++)
{
uint8_t pixel_data;
if (flipx)
{
pixel_data = (plane1 & 1) + ((plane2 & 1) << 1);
plane1 = plane1 >> 1;
plane2 = plane2 >> 1;
}
else
{
pixel_data = ((plane1 >> 7) & 1) | (((plane2 >> 7) & 1) << 1);
plane1 = plane1 << 1;
plane2 = plane2 << 1;
}
make_sprite_pixel_data(pixel_data, flipx);
/* is this pixel non-transparent? */
if (sprite_xpos + pixel >= first_pixel)
@ -835,7 +884,7 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
if (!line_priority[sprite_xpos + pixel])
{
/* no, draw */
bitmap.pix16(m_scanline, sprite_xpos + pixel) = paldata[pixel_data];
draw_sprite_pixel(sprite_xpos, color, pixel, pixel_data, bitmap);
}
/* indicate that a sprite was drawn at this location, even if it's not seen */
line_priority[sprite_xpos + pixel] |= 0x01;
@ -854,18 +903,7 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
for (pixel = 0; pixel < 8; pixel++)
{
uint8_t pixel_data;
if (flipx)
{
pixel_data = (plane1 & 1) + ((plane2 & 1) << 1);
plane1 = plane1 >> 1;
plane2 = plane2 >> 1;
}
else
{
pixel_data = ((plane1 >> 7) & 1) | (((plane2 >> 7) & 1) << 1);
plane1 = plane1 << 1;
plane2 = plane2 << 1;
}
make_sprite_pixel_data(pixel_data, flipx);
/* is this pixel non-transparent? */
if (sprite_xpos + pixel >= first_pixel)
@ -878,7 +916,7 @@ void ppu2c0x_device::draw_sprites( uint8_t *line_priority )
if (!(line_priority[sprite_xpos + pixel] & 0x01))
{
/* no, draw */
bitmap.pix16(m_scanline, sprite_xpos + pixel) = paldata[pixel_data];
draw_sprite_pixel(sprite_xpos, color, pixel, pixel_data, bitmap);
line_priority[sprite_xpos + pixel] |= 0x01;
}
}

View File

@ -102,20 +102,31 @@ public:
// are non-rendering and non-vblank.
};
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
DECLARE_READ8_MEMBER( palette_read );
DECLARE_WRITE8_MEMBER( palette_write );
virtual DECLARE_READ8_MEMBER( read );
virtual DECLARE_WRITE8_MEMBER( write );
virtual DECLARE_READ8_MEMBER( palette_read );
virtual DECLARE_WRITE8_MEMBER( palette_write );
static void set_cpu_tag(device_t &device, const char *tag) { downcast<ppu2c0x_device &>(device).m_cpu.set_tag(tag); }
static void set_color_base(device_t &device, int colorbase) { downcast<ppu2c0x_device &>(device).m_color_base = colorbase; }
static void set_nmi_delegate(device_t &device, nmi_delegate &&cb);
/* routines */
void init_palette( palette_device &palette, int first_entry );
void init_palette_rgb( palette_device &palette, int first_entry );
virtual void init_palette(palette_device &palette, int first_entry);
void init_palette(palette_device &palette, int first_entry, bool indirect);
void init_palette_rgb(palette_device &palette, int first_entry);
virtual void read_tile_plane_data(int address, int color);
virtual void shift_tile_plane_data(uint8_t &pix);
virtual void draw_tile_pixel(uint8_t pix, int color, uint16_t back_pen, uint16_t *&dest, const pen_t *color_table);
virtual void draw_tile(uint8_t *line_priority, int color_byte, int color_bits, int address, int start_x, uint16_t back_pen, uint16_t *&dest, const pen_t *color_table);
void draw_background( uint8_t *line_priority );
virtual void read_sprite_plane_data(int address);
virtual void make_sprite_pixel_data(uint8_t &pixel_data, int flipx);
virtual void draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_ind16& bitmap);
virtual void read_extra_sprite_bits(int sprite_index);
void draw_sprites( uint8_t *line_priority );
void render_scanline();
void update_scanline();
@ -203,6 +214,11 @@ protected:
int m_security_value; /* 2C05 protection */
int m_vblank_first_scanline; /* the very first scanline where VBLANK occurs */
// used in rendering
uint8_t m_planebuf[2];
int m_scanline; /* scanline count */
std::unique_ptr<uint8_t[]> m_spriteram; /* sprite ram */
private:
static constexpr device_timer_id TIMER_HBLANK = 0;
static constexpr device_timer_id TIMER_NMI = 1;
@ -212,14 +228,14 @@ private:
inline void writebyte(offs_t address, uint8_t data);
std::unique_ptr<bitmap_ind16> m_bitmap; /* target bitmap */
std::unique_ptr<uint8_t[]> m_spriteram; /* sprite ram */
std::unique_ptr<pen_t[]> m_colortable; /* color table modified at run time */
std::unique_ptr<pen_t[]> m_colortable_mono; /* monochromatic color table modified at run time */
int m_scanline; /* scanline count */
scanline_delegate m_scanline_callback_proc; /* optional scanline callback */
hblank_delegate m_hblank_callback_proc; /* optional hblank callback */
vidaccess_delegate m_vidaccess_callback_proc; /* optional video access callback */
nmi_delegate m_nmi_callback_proc; /* nmi access callback from interface */
int m_regs[PPU_MAX_REG]; /* registers */
int m_refresh_data; /* refresh-related */
int m_refresh_latch; /* refresh-related */

View File

@ -0,0 +1,353 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************
VT video emulation
The VT video is based on the ppu2c0x but with enhanced capabilities such
as 16 colour sprites.
******************************************************************************/
#include "emu.h"
#include "video/ppu2c0x_vt.h"
/* constant definitions */
#define VISIBLE_SCREEN_WIDTH (32*8) /* Visible screen width */
// devices
DEFINE_DEVICE_TYPE(PPU_VT03, ppu_vt03_device, "ppu_vt03", "VT03 PPU")
ppu_vt03_device::ppu_vt03_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: ppu2c0x_device(mconfig, PPU_VT03, tag, owner, clock),
m_read_bg(*this),
m_read_sp(*this)
{
}
READ8_MEMBER(ppu_vt03_device::palette_read)
{
if (m_201x_regs[0] & 0x80)
{
return m_newpal[offset];
}
else
{
return ppu2c0x_device::palette_read(space, offset);
}
}
void ppu_vt03_device::set_new_pen(int i)
{
uint16_t palval = (m_newpal[i&0x7f] & 0x3f) | ((m_newpal[(i&0x7f)+0x80] & 0x3f)<<6);
// &0x3f so we don't attempt to use any of the extended colours right now because
// I haven't managed to work out the format
m_palette->set_pen_indirect(i&0x7f,palval&0x3f);
}
WRITE8_MEMBER(ppu_vt03_device::palette_write)
{
if (m_201x_regs[0] & 0x80)
{
m_newpal[offset] = data;
set_new_pen(offset);
}
else
{
ppu2c0x_device::palette_write(space, offset, data);
}
}
READ8_MEMBER( ppu_vt03_device::read )
{
return ppu2c0x_device::read(space,offset);
}
void ppu_vt03_device::init_palette(palette_device &palette, int first_entry)
{
// todo, work out the format of the 12 palette bits instead of just calling the main init
m_palette = &palette;
ppu2c0x_device::init_palette(palette, first_entry, true);
}
void ppu_vt03_device::device_start()
{
ppu2c0x_device::device_start();
m_newpal = std::make_unique<uint8_t[]>(0x100);
save_pointer(&m_newpal[0], "m_newpal", 0x100);
save_item(NAME(m_201x_regs));
}
uint8_t ppu_vt03_device::get_201x_reg(int reg)
{
//logerror(" getting reg %d is %02x ", reg, m_201x_regs[reg]);
return m_201x_regs[reg];
}
void ppu_vt03_device::set_201x_reg(int reg, uint8_t data)
{
m_201x_regs[reg] = data;
}
void ppu_vt03_device::device_reset()
{
ppu2c0x_device::device_reset();
m_read_bg.resolve_safe(0);
m_read_sp.resolve_safe(0);
set_2010_reg(0x00);
// todo: what are the actual defaults for these?
for (int i = 0;i < 0x20;i++)
set_201x_reg(i, 0x00);
m_read_bg4_bg3 = 0;
m_va34 = 0;
}
uint8_t ppu_vt03_device::get_m_read_bg4_bg3()
{
return m_read_bg4_bg3;
}
uint8_t ppu_vt03_device::get_va34()
{
return m_va34;
}
void ppu_vt03_device::read_sprite_plane_data(int address)
{
m_va34 = 0;
m_planebuf[0] = m_read_sp((address + 0) & 0x1fff);
m_planebuf[1] = m_read_sp((address + 8) & 0x1fff);
m_va34 = 1;
m_extplanebuf[0] = m_read_sp((address + 0) & 0x1fff);
m_extplanebuf[1] = m_read_sp((address + 8) & 0x1fff);
}
void ppu_vt03_device::make_sprite_pixel_data(uint8_t &pixel_data, int flipx)
{
ppu2c0x_device::make_sprite_pixel_data(pixel_data, flipx);
if (flipx)
{
pixel_data |= (((m_extplanebuf[0] & 1) << 5) | ((m_extplanebuf[1] & 1) << 6)); // yes, shift by 5 and 6 because of the way the palette is arranged in RAM
m_extplanebuf[0] = m_extplanebuf[0] >> 1;
m_extplanebuf[1] = m_extplanebuf[1] >> 1;
}
else
{
pixel_data |= (((m_extplanebuf[0] >> 7) & 1) << 5) | (((m_extplanebuf[1] >> 7) & 1) << 6); // yes, shift by 5 and 6 because of the way the palette is arranged in RAM
m_extplanebuf[0] = m_extplanebuf[0] << 1;
m_extplanebuf[1] = m_extplanebuf[1] << 1;
}
}
void ppu_vt03_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_ind16& bitmap)
{
bitmap.pix16(m_scanline, sprite_xpos + pixel) = pixel_data + (4 * color);
}
void ppu_vt03_device::read_tile_plane_data(int address, int color)
{
int is4bpp = get_201x_reg(0x0) & 0x02;
if (m_201x_regs[0] & 0x10) // extended mode
m_read_bg4_bg3 = color;
else
m_read_bg4_bg3 = 0;
if (is4bpp)
{
m_va34 = 0;
m_planebuf[0] = m_read_bg((address & 0x1fff));
m_planebuf[1] = m_read_bg((address + 8) & 0x1fff);
m_va34 = 1;
m_extplanebuf[0] = m_read_bg((address & 0x1fff));
m_extplanebuf[1] = m_read_bg((address + 8) & 0x1fff);
}
else
{
m_va34 = 0;
m_planebuf[0] = m_read_bg((address & 0x1fff));
m_planebuf[1] = m_read_bg((address + 8) & 0x1fff);
}
}
void ppu_vt03_device::shift_tile_plane_data(uint8_t &pix)
{
int is4bpp = get_201x_reg(0x0) & 0x02;
ppu2c0x_device::shift_tile_plane_data(pix);
if (is4bpp)
{
pix |= (((m_extplanebuf[0] >> 7) & 1) << 5) | (((m_extplanebuf[1] >> 7) & 1) << 6); // yes, shift by 5 and 6 because of the way the palette is arranged in RAM
m_extplanebuf[0] = m_extplanebuf[0] << 1;
m_extplanebuf[1] = m_extplanebuf[1] << 1;
}
}
void ppu_vt03_device::draw_tile_pixel(uint8_t pix, int color, uint16_t back_pen, uint16_t *&dest, const pen_t *color_table)
{
int is4bpp = get_201x_reg(0x0) & 0x02;
if (!is4bpp)
{
ppu2c0x_device::draw_tile_pixel(pix, color, back_pen, dest, color_table);
}
else
{
int basepen;
int pen;
if (m_201x_regs[0] & 0x10) // extended mode
{
basepen = 0;
}
else
{
basepen = 4 * color; // for use in the palette decoding
}
if (pix)
{
pen = pix + basepen;
}
else
{
pen = back_pen; // fixme backpen logic probably differs on vt03 due to extra colours
}
*dest = pen;
}
}
void ppu_vt03_device::read_extra_sprite_bits(int sprite_index)
{
m_extra_sprite_bits = (m_spriteram[sprite_index + 2] & 0x1c) >>2;
}
uint8_t ppu_vt03_device::get_speva2_speva0()
{
return m_extra_sprite_bits;
}
void ppu_vt03_device::set_2010_reg(uint8_t data)
{
/* 7 : COLCOMP
6 : UNUSED (8bpp enable on VT09?)
5 : UNUSED
4 : BKEXTEN
3 : SPEXTEN
2 : SP16EN
1 : BK16EN
0 : PIX16EN */
if ((m_201x_regs[0x0] & 0x80) != (data & 0x80))
{
if (data & 0x80)
{
for (int i = 0;i < 256;i++)
{
set_new_pen(i);
}
}
else
{
for (int i = 0;i < 256;i++)
{
m_palette->set_pen_indirect(i, i);
}
}
}
m_201x_regs[0x0] = data;
}
WRITE8_MEMBER(ppu_vt03_device::write)
{
if (offset < 0x10)
{
ppu2c0x_device::write(space, offset, data);
}
else
{
switch (offset)
{
case 0x10:
logerror("%s: write to reg 0x2010 %02x\n", machine().describe_context(), data);
set_2010_reg(data);
break;
case 0x11:
logerror("%s: write to reg 0x2011 %02x\n", machine().describe_context(), data);
break;
case 0x12:
m_201x_regs[0x2] = data;
break;
case 0x13:
m_201x_regs[0x3] = data;
break;
case 0x14:
m_201x_regs[0x4] = data;
break;
case 0x15:
m_201x_regs[0x5] = data;
break;
case 0x16:
m_201x_regs[0x6] = data;
break;
case 0x17:
logerror("set reg 7 %02x\n", data);
m_201x_regs[0x7] = data;
break;
case 0x18:
logerror("set reg 8 %02x\n", data);
m_201x_regs[0x8] = data;
break;
case 0x19:
// reset gun port (value doesn't matter)
break;
case 0x1a:
m_201x_regs[0xa] = data;
break;
case 0x1b:
// unused
break;
case 0x1c:
// (READ) x-coordinate of gun
break;
case 0x1d:
// (READ) y-coordinate of gun
break;
case 0x1e:
// (READ) x-coordinate of gun 2
break;
case 0x1f:
// (READ) y-coordinate of gun 2
break;
}
}
}

View File

@ -0,0 +1,78 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
/******************************************************************************
VT video emulation
The VT video is based on the ppu2c0x but with enhanced capabilities such
as 16 colour sprites.
******************************************************************************/
#include "emu.h"
#include "video/ppu2c0x.h"
#define MCFG_PPU_VT03_ADD(_tag) \
MCFG_PPU2C0X_ADD(_tag, PPU_VT03)
#define MCFG_PPU_VT03_READ_BG_CB(_devcb) \
devcb = &ppu_vt03_device::set_read_bg_callback(*device, DEVCB_##_devcb);
#define MCFG_PPU_VT03_READ_SP_CB(_devcb) \
devcb = &ppu_vt03_device::set_read_sp_callback(*device, DEVCB_##_devcb);
class ppu_vt03_device : public ppu2c0x_device {
public:
ppu_vt03_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
template<class _Object> static devcb_base &set_read_bg_callback(device_t &device, _Object object) { return downcast<ppu_vt03_device &>(device).m_read_bg.set_callback(object); }
template<class _Object> static devcb_base &set_read_sp_callback(device_t &device, _Object object) { return downcast<ppu_vt03_device &>(device).m_read_sp.set_callback(object); }
virtual DECLARE_READ8_MEMBER(read) override;
virtual DECLARE_WRITE8_MEMBER(write) override;
virtual DECLARE_READ8_MEMBER(palette_read) override;
virtual DECLARE_WRITE8_MEMBER(palette_write) override;
virtual void init_palette( palette_device &palette, int first_entry ) override;
virtual void read_tile_plane_data(int address, int color) override;
virtual void shift_tile_plane_data(uint8_t &pix) override;
virtual void draw_tile_pixel(uint8_t pix, int color, uint16_t back_pen, uint16_t *&dest, const pen_t *color_table) override;
virtual void read_sprite_plane_data(int address) override;
virtual void make_sprite_pixel_data(uint8_t &pixel_data, int flipx) override;
virtual void draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_ind16& bitmap) override;
virtual void read_extra_sprite_bits(int sprite_index) override;
virtual void device_start() override;
virtual void device_reset() override;
void set_201x_reg(int reg, uint8_t data);
uint8_t get_201x_reg(int reg);
uint8_t get_va34();
uint8_t get_m_read_bg4_bg3();
uint8_t get_speva2_speva0();
private:
devcb_read8 m_read_bg;
devcb_read8 m_read_sp;
std::unique_ptr<uint8_t[]> m_newpal;
int m_read_bg4_bg3;
int m_va34;
uint8_t m_extplanebuf[2];
uint8_t m_extra_sprite_bits;
palette_device *m_palette;
uint8_t m_201x_regs[0x20];
void set_2010_reg(uint8_t data);
void set_new_pen(int i);
};
DECLARE_DEVICE_TYPE(PPU_VT03, ppu_vt03_device)

904
src/mame/drivers/nes_vt.cpp Normal file
View File

@ -0,0 +1,904 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************
nes_vt.c
The 'VT' series are SoC solutions that implement enhanced NES hardware
there are several generations of these chips each adding additional
functionality.
This list is incomplete
VT01 - plain famiclone?
VT02 - banking scheme to access 32MB, Dual APU with PCM support
VT03 - above + 4bpp sprite / bg modes, enhanced palette
VT08 - ?
VT09 - 8bpp or direct colour modes?
VT16 - ?
VT18 - ?
(more)
VT1682 - NOT compatible with NES, different video system, sound CPU (4x
main CPU clock), optional internal ROM etc. (will need it's own
driver)
todo (VT03):
add support for basic NES PPU page mirroring (selectable with register)
work out format of the 12-bit palette, it's meant to be
4-bits saturation, 4-bits luminance, 4-bits phase
as opposed to the basic NES format of 2-luminance, 4-phase
but getting it correct is tricky:
APU refactoring to allow for mostly doubled up functionality + PCM channel
*more*
todo (newer VTxx):
everything
todo (general)
Add more Famiclone roms here, there should be plenty more dumps of VTxx
based systems floating about.
***************************************************************************/
#include "emu.h"
#include "includes/nes.h"
#include "cpu/m6502/n2a03.h"
#include "machine/bankdev.h"
#include "video/ppu2c0x_vt.h"
#include "cpu/m6502/m6502.h"
#include "screen.h"
#include "speaker.h"
class nes_vt_state : public nes_base_state
{
public:
nes_vt_state(const machine_config &mconfig, device_type type, const char *tag)
: nes_base_state(mconfig, type, tag),
m_ppu(*this, "ppu"),
m_apu(*this, "apu"),
m_prg(*this, "prg"),
m_prgbank0(*this, "prg_bank0"),
m_prgbank1(*this, "prg_bank1"),
m_prgbank2(*this, "prg_bank2"),
m_prgbank3(*this, "prg_bank3"),
m_prgrom(*this, "mainrom")
{ }
/* APU handling */
DECLARE_WRITE_LINE_MEMBER(apu_irq);
DECLARE_READ8_MEMBER(apu_read_mem);
DECLARE_READ8_MEMBER(psg1_4014_r);
DECLARE_READ8_MEMBER(psg1_4015_r);
DECLARE_WRITE8_MEMBER(psg1_4015_w);
DECLARE_WRITE8_MEMBER(psg1_4017_w);
/* Misc PPU */
DECLARE_WRITE8_MEMBER(nes_vh_sprite_dma_w);
void ppu_nmi(int *ppu_regs);
void scanline_irq(int scanline, int vblank, int blanked);
uint32_t screen_update_vt(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
DECLARE_PALETTE_INIT(nesvt);
/* VT03 extension handling */
DECLARE_WRITE8_MEMBER(vt03_410x_w);
DECLARE_WRITE8_MEMBER(vt03_8000_w);
uint8_t m_410x[0xc];
int m_timer_irq_enabled;
int m_timer_running;
int m_timer_val;
uint8_t m_8000_addr_latch;
/* banking etc. */
int m_romsize;
int m_numbanks;
uint32_t get_banks(uint8_t bnk);
void update_banks();
/* OneBus read callbacks for getting sprite and tile data during rendering*/
DECLARE_READ8_MEMBER(spr_r);
DECLARE_READ8_MEMBER(chr_r);
int calculate_real_video_address(int addr, int extended, int readtype);
virtual void machine_start() override;
virtual void machine_reset() override;
required_device<ppu_vt03_device> m_ppu;
required_device<nesapu_device> m_apu;
required_device<address_map_bank_device> m_prg;
required_memory_bank m_prgbank0;
required_memory_bank m_prgbank1;
required_memory_bank m_prgbank2;
required_memory_bank m_prgbank3;
required_region_ptr<uint8_t> m_prgrom;
private:
};
uint32_t nes_vt_state::get_banks(uint8_t bnk)
{
switch (m_410x[0xb] & 0x07)
{
case 0: return ((m_410x[0x0] & 0xF0) << 4) | (m_410x[0xa] & 0xC0) | (bnk & 0x3F); // makes bank 0xff at 0xe000 map to 0x07e000 by default for vectors at 0x007fffx
case 1: return ((m_410x[0x0] & 0xF0) << 4) | (m_410x[0xa] & 0xE0) | (bnk & 0x1F);
case 2: return ((m_410x[0x0] & 0xF0) << 4) | (m_410x[0xa] & 0xF0) | (bnk & 0x0F);
case 3: return ((m_410x[0x0] & 0xF0) << 4) | (m_410x[0xa] & 0xF8) | (bnk & 0x07);
case 4: return ((m_410x[0x0] & 0xF0) << 4) | (m_410x[0xa] & 0xFC) | (bnk & 0x03);
case 5: return ((m_410x[0x0] & 0xF0) << 4) | (m_410x[0xa] & 0xFE) | (bnk & 0x01);
case 6: return ((m_410x[0x0] & 0xF0) << 4) | m_410x[0xa];
case 7: return ((m_410x[0x0] & 0xF0) << 4) | bnk;
}
return 0;
}
// 8000 needs to bank in 60000 ( bank 0x30 )
void nes_vt_state::update_banks()
{
uint8_t bank;
// 8000-9fff
if ((m_410x[0xb] & 0x40) == 0)
{
if ((m_410x[0x5] & 0x40) == 0)
bank = m_410x[0x7];
else
bank = m_410x[0x9];
}
else
bank = 0xfe;
m_prgbank0->set_entry(get_banks(bank) & (m_numbanks-1));
// a000-bfff
bank = m_410x[0x8];
m_prgbank1->set_entry(get_banks(bank) & (m_numbanks-1));
// c000-dfff
if ((m_410x[0xb] & 0x40) != 0)
{
if ((m_410x[0x5] & 0x40) == 0)
bank = m_410x[0x9];
else
bank = m_410x[0x7];
}
else
bank = 0xfe;
m_prgbank2->set_entry(get_banks(bank) & (m_numbanks-1));
// e000 - ffff
bank = 0xff;
m_prgbank3->set_entry(get_banks(bank) & (m_numbanks-1));
}
WRITE8_MEMBER(nes_vt_state::vt03_410x_w)
{
switch (offset)
{
case 0x0:
m_410x[0x0] = data;
update_banks();
break;
case 0x1:
// latch timer value
m_410x[0x1] = data;
m_timer_running = 0;
break;
case 0x2:
// load latched value and start counting
m_410x[0x2] = data; // value doesn't matter?
m_timer_val = m_410x[0x1];
m_timer_running = 1;
break;
case 0x3:
// disable timer irq
m_410x[0x3] = data; // value doesn't matter?
m_timer_irq_enabled = 0;
break;
case 0x4:
// enable timer irq
m_410x[0x4] = data; // value doesn't matter?
m_timer_irq_enabled = 1;
break;
case 0x5:
logerror("vt03_4105_w %02x\n", data);
m_410x[0x5] = data;
update_banks();
break;
case 0x6:
m_410x[0x6] = data;
break;
case 0x7:
m_410x[0x7] = data;
update_banks();
break;
case 0x8:
m_410x[0x8] = data;
update_banks();
break;
case 0x9:
logerror("vt03_4109_w %02x\n", data);
m_410x[0x9] = data;
update_banks();
break;
case 0xa:
m_410x[0xa] = data;
update_banks();
break;
case 0xb:
/*
D7 TSYNEN - Timer clock select 0:AD12, 1:HSYNC
D6 Prg Bank 0 Reg 2 enable / disable 0:Disable 1:Enable
D5 RS232 enable / disable 0:Disable 1:Enable
D4 Bus output control 0: normal 1: tristate
D3 6000-7fff and 8000-ffff control - 0 will not active XRWB, 1 will activate
D2-D0 - program bank 0 selector
*/
logerror("vt03_410b_w %02x\n", data);
m_410x[0xb] = data;
update_banks();
break;
}
}
uint32_t nes_vt_state::screen_update_vt(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
// render the ppu
m_ppu->render(bitmap, 0, 0, 0, 0);
return 0;
}
READ8_MEMBER(nes_vt_state::spr_r)
{
int realaddr = calculate_real_video_address(offset, 0, 1);
return m_prgrom[realaddr & (m_romsize-1)];
}
READ8_MEMBER(nes_vt_state::chr_r)
{
int realaddr = calculate_real_video_address(offset, 1, 0);
return m_prgrom[realaddr & (m_romsize-1)];
}
PALETTE_INIT_MEMBER(nes_vt_state, nesvt)
{
m_ppu->init_palette(palette, 0);
}
void nes_vt_state::scanline_irq(int scanline, int vblank, int blanked)
{
int irqstate = 0;
//logerror("scanline_irq %d\n", scanline);
if (m_timer_running && scanline < 0xe0)
{
m_timer_val--;
if (m_timer_val < 0)
{
if (m_timer_irq_enabled)
{
irqstate = 1;
}
}
}
if (irqstate)
m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
else
m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
}
void nes_vt_state::machine_start()
{
m_romsize = memregion("mainrom")->bytes();
m_numbanks = m_romsize / 0x2000;
m_prg->set_bank(0);
m_prgbank0->configure_entries(0, m_numbanks, &m_prgrom[0x00000], 0x2000);
m_prgbank1->configure_entries(0, m_numbanks, &m_prgrom[0x00000], 0x2000);
m_prgbank2->configure_entries(0, m_numbanks, &m_prgrom[0x00000], 0x2000);
m_prgbank3->configure_entries(0, m_numbanks, &m_prgrom[0x00000], 0x2000);
save_item(NAME(m_410x));
save_item(NAME(m_8000_addr_latch));
save_item(NAME(m_timer_irq_enabled));
save_item(NAME(m_timer_running));
save_item(NAME(m_timer_val));
m_ppu->set_scanline_callback(ppu2c0x_device::scanline_delegate(FUNC(nes_vt_state::scanline_irq),this));
// m_ppu->set_hblank_callback(ppu2c0x_device::hblank_delegate(FUNC(device_nes_cart_interface::hblank_irq),m_cartslot->m_cart));
}
void nes_vt_state::machine_reset()
{
// what are the actual defaults?
m_410x[0x0] = 0x00;
m_410x[0x1] = 0x00;
m_410x[0x2] = 0x00;
m_410x[0x3] = 0x00;
m_410x[0x4] = 0x00;
m_410x[0x5] = 0x00;
m_410x[0x6] = 0x00;
m_410x[0x7] = 0x00;
m_410x[0x8] = 0x01;
m_410x[0x9] = 0x02;
m_410x[0xa] = 0x00;
m_410x[0xb] = 0x00;
m_timer_irq_enabled = 0;
m_timer_running = 0;
m_timer_val = 0;
update_banks();
}
int nes_vt_state::calculate_real_video_address(int addr, int extended, int readtype)
{
// might be a VT09 only feature (8bpp or 4bpp direct colour mode??)
int alt_order = m_ppu->get_201x_reg(0x0) & 0x40;
if (readtype == 0)
{
if (m_ppu->get_201x_reg(0x0) & 0x10)
{
extended = 1;
}
else
{
extended = 0;
}
}
else if (readtype == 1)
{
if (m_ppu->get_201x_reg(0x0) & 0x08)
{
extended = 1;
}
else
{
extended = 0;
}
}
/*
Calculating TVA17 - TVA10
--------------------------------------------------------------------------------------------
| COMR7 | AD[12:10] | TVA17 | TVA16 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| (4105, 0x80) | (2006) | | | | | | | | |
--------------------------------------------------------------------------------------------
| 0/1/C/D | RV47 | RV46 | RV45 | RV44 | RV43 | RV42 | RV41 | AD10 | ** RV40 is never used
| 2/3/E/F | RV57 | RV56 | RV55 | RV54 | RV53 | RV52 | RV51 | AD10 | ** RV50 is never used
| 4/8 | RV07 | RV06 | RV05 | RV04 | RV03 | RV02 | RV01 | RV00 |
| 5/9 | RV17 | RV16 | RV15 | RV14 | RV13 | RV12 | RV11 | RV10 |
| 6/A | RV27 | RV26 | RV25 | RV24 | RV23 | RV22 | RV21 | RV20 |
| 7/B | RV37 | RV36 | RV35 | RV34 | RV33 | RV32 | RV31 | RV30 |
--------------------------------------------------------------------------------------------
m_r2012 = rv0x
m_r2013 = rv1x
m_r2014 = rv2x
m_r2015 = rv3x
m_r2016 = rv4x
m_r2017 = rv5x
*/
int finaladdr = 0;
int sel = (addr & 0x1c00) | ((m_410x[0x5] & 0x80) ? 0x2000 : 0x000);
int vbank_tva17_tva10 = 0x00;
switch ((sel>>10) & 0xf)
{
case 0x0:
case 0x1:
case 0xc:
case 0xd:
vbank_tva17_tva10 = (m_ppu->get_201x_reg(0x6) & 0xfe) | ((addr & 0x0400) ? 1 : 0);
break;
case 0x2:
case 0x3:
case 0xe:
case 0xf:
vbank_tva17_tva10 = (m_ppu->get_201x_reg(0x7) & 0xfe) | ((addr & 0x0400) ? 1 : 0);
break;
case 0x4:
case 0x8:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x2);
break;
case 0x5:
case 0x9:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x3);
break;
case 0x6:
case 0xa:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x4);
break;
case 0x7:
case 0xb:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x5);
break;
}
/*
Calculating VA17 - VA10 (requires TVA17-TVA10 to have been calculated)
------------------------------------------------------------------------------
| VB0S[2:0] | VA[17:10] |
| 201a & 0x7 | VA17 | VA16 | VA15 | VA14 | VA13 | VA12 | VA11 | VA10 |
|-----------------------------------------------------------------------------
| 0x0 | TVA17 | TVA16 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x1 | TV67 | TVA16 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x2 | RV67 | RV66 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x3 | INVALID ***************************************************** |
| 0x4 | RV67 | RV66 | RV65 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x5 | RV67 | RV66 | RV65 | RV64 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x6 | RV67 | RV66 | RV65 | RV64 | RV63 | TVA12 | TVA11 | TVA10 |
| 0x7 | INVALID ***************************************************** |
------------------------------------------------------------------------------
RV67- RV63 = 0x201a & 0xf8
*/
int va17_va10 = 0;
int swit = m_ppu->get_201x_reg(0xa);
switch (swit & 0x07)
{
case 0x0: va17_va10 = vbank_tva17_tva10; break;
case 0x1: va17_va10 = (vbank_tva17_tva10 & 0x7f) | (m_ppu->get_201x_reg(0xa) & 0x80); break;
case 0x2: va17_va10 = (vbank_tva17_tva10 & 0x3f) | (m_ppu->get_201x_reg(0xa) & 0xc0); break;
case 0x3: return -1;
case 0x4: va17_va10 = (vbank_tva17_tva10 & 0x1f) | (m_ppu->get_201x_reg(0xa) & 0xe0); break;
case 0x5: va17_va10 = (vbank_tva17_tva10 & 0x0f) | (m_ppu->get_201x_reg(0xa) & 0xf0); break;
case 0x6: va17_va10 = (vbank_tva17_tva10 & 0x07) | (m_ppu->get_201x_reg(0xa) & 0xf8); break;
case 0x7: return -1;
}
int va34 =m_ppu->get_va34();
if (!extended)
{
int is4bpp = 0;
if (readtype==0) is4bpp = m_ppu->get_201x_reg(0x0) & 0x02;
else if (readtype==1) is4bpp = m_ppu->get_201x_reg(0x0) & 0x04;
int va20_va18 = (m_ppu->get_201x_reg(0x8) & 0x70) >> 4;
finaladdr = ((m_410x[0x0] & 0x0F) << 21) | (va20_va18 << 18) | (va17_va10 << 10) | (addr & 0x03ff);
if (is4bpp)
{
if (!alt_order)
{
finaladdr = ((finaladdr &~0xf) << 1) | (va34 << 4) | (finaladdr & 0xf);
}
else
{
finaladdr = (finaladdr << 1) | (va34 << 4);
}
}
}
else
{
int eva2_eva0 = 0x00;
int is4bpp = 0;
switch (readtype)
{
case 0: // background display
is4bpp = m_ppu->get_201x_reg(0x0) & 0x02;
eva2_eva0 |= m_ppu->get_m_read_bg4_bg3();
if (m_ppu->get_201x_reg(0x1) & 0x02)
{
if (m_410x[0x6] & 0x1) eva2_eva0 |= 0x4;
}
else
{
if (m_ppu->get_201x_reg(0x8) & 0x08) eva2_eva0 |= 0x4;
}
break;
case 1: // sprite display
is4bpp = m_ppu->get_201x_reg(0x0) & 0x04; // 16 colors or 16-pixel wide (both adjust the read)
eva2_eva0 |= m_ppu->get_speva2_speva0();
break;
case 2: // CPU R/W access
// todo
break;
}
finaladdr = ((m_410x[0x0] & 0x0f) << 21) | (va17_va10 << 13) | (eva2_eva0 << 10) | (addr & 0x03ff);
if (is4bpp)
{
if (!alt_order)
{
finaladdr = ((finaladdr &~0xf) << 1) | (va34 << 4) | (finaladdr & 0xf);
}
else
{
finaladdr = (finaladdr << 1) | (va34 << 4);
}
}
}
return finaladdr;
}
/*
nes_vt_state::vt03_8000_w notes
this is used by
VT03dogA.bin
at least, but even in EmuVT the sprite graphics there are corrupt
so I'm not sure if it's just an incomplete demo, or there is more to this
*/
// the demo program writes to the banking registers like this.. how does this really work? is this mode selectable? some kind of legacy mode?
WRITE8_MEMBER(nes_vt_state::vt03_8000_w)
{
switch (offset + 0x8000)
{
case 0x8000:
m_8000_addr_latch = data;
break;
case 0x8001:
switch (m_8000_addr_latch)
{
case 0x00:
m_ppu->set_201x_reg(0x6, data);
break;
case 0x01:
m_ppu->set_201x_reg(0x7, data);
break;
case 0x02: // hand?
//if ((data != 0x00) && (data != 0x2f) && (data != 0x31) && (data != 0x32) )
// logerror("%s vt03_8001_data_w latch %02x data %02x\n", machine().describe_context(), m_8000_addr_latch, data);
m_ppu->set_201x_reg(0x2, data);
break;
case 0x03: // dog?
//if ((data != 0x00) && (data != 0x2c) && (data != 0x2d) && (data != 0x2e) && (data != 0x2f) && (data != 0x32) && (data != 0x3d) && (data != 0x3e) && (data != 0x3f) && (data != 0x40) && (data != 0x41) && (data != 0x42) && (data != 0x43) && (data != 0x44) && (data != 0x45) && (data != 0x46))
// logerror("%s vt03_8001_data_w latch %02x data %02x\n", machine().describe_context(), m_8000_addr_latch, data);
m_ppu->set_201x_reg(0x3, data);
break;
case 0x04: // ball thrown
//if ((data != 0x00) && (data != 0x10) && (data != 0x12))
// logerror("%s vt03_8001_data_w latch %02x data %02x\n", machine().describe_context(), m_8000_addr_latch, data);
m_ppu->set_201x_reg(0x4, data);
break;
case 0x05: // ball thrown
//if ((data != 0x00) && (data != 0x11))
// logerror("%s vt03_8001_data_w latch %02x data %02x\n", machine().describe_context(), m_8000_addr_latch, data);
m_ppu->set_201x_reg(0x5, data);
break;
case 0x06:
m_410x[0x7] = data;
update_banks();
break;
case 0x07:
m_410x[0x8] = data;
update_banks();
break;
default:
logerror("%s vt03_8001_data_w latch %02x data %02x\n", machine().describe_context(), m_8000_addr_latch, data);
break;
}
break;
case 0xa000:
logerror("%s: vt03_a000_w %02x\n", machine().describe_context(), data);
break;
case 0xa001:
logerror("%s: vt03_a001_w %02x\n", machine().describe_context(), data);
break;
// registers below appear to provide an alt way of setting the scanline counter/timer
case 0xc000:
// seems to be a mirror of 4101 (timer latch)
//logerror("%s: vt03_c000_w %02x\n", machine().describe_context(), data);
vt03_410x_w(space, 1, data);
break;
case 0xc001:
// seems to be a mirror of 4102 (load timer with latched data, start counting)
// logerror("%s: vt03_c001_w %02x\n", machine().describe_context(), data);
vt03_410x_w(space, 2, data);
break;
case 0xe000:
// seems to be a mirror of 4103 (disable timer interrupt)
// logerror("%s: vt03_e000_w %02x\n", machine().describe_context(), data);
vt03_410x_w(space, 3, data);
break;
case 0xe001:
// seems to be a mirror of 4104 (enable timer interrupt)
//logerror("%s: vt03_e001_w %02x\n", machine().describe_context(), data);
vt03_410x_w(space, 4, data);
break;
default:
logerror("%s: vt03_8000_w (%04x) unhandled %02x\n", machine().describe_context(), offset+0x8000, data );
break;
}
}
/* APU plumbing, this is because we have a plain M6502 core in the VT03, otherwise this is handled in the core */
READ8_MEMBER(nes_vt_state::psg1_4014_r)
{
return m_apu->read(space, 0x14);
}
READ8_MEMBER(nes_vt_state::psg1_4015_r)
{
return m_apu->read(space, 0x15);
}
WRITE8_MEMBER(nes_vt_state::psg1_4015_w)
{
m_apu->write(space, 0x15, data);
}
WRITE8_MEMBER(nes_vt_state::psg1_4017_w)
{
m_apu->write(space, 0x17, data);
}
WRITE8_MEMBER(nes_vt_state::nes_vh_sprite_dma_w)
{
m_ppu->spriteram_dma(space, data);
}
static ADDRESS_MAP_START( nes_vt_map, AS_PROGRAM, 8, nes_vt_state )
AM_RANGE(0x0000, 0x07ff) AM_RAM
AM_RANGE(0x2000, 0x3fff) AM_DEVREADWRITE("ppu", ppu2c0x_device, read, write) /* PPU registers */
AM_RANGE(0x4000, 0x4013) AM_DEVREADWRITE("apu", nesapu_device, read, write)
AM_RANGE(0x4014, 0x4014) AM_READ(psg1_4014_r) AM_WRITE(nes_vh_sprite_dma_w)
AM_RANGE(0x4015, 0x4015) AM_READWRITE(psg1_4015_r, psg1_4015_w) /* PSG status / first control register */
AM_RANGE(0x4016, 0x4016) AM_READWRITE(nes_in0_r, nes_in0_w)
AM_RANGE(0x4017, 0x4017) AM_READ(nes_in1_r) AM_WRITE(psg1_4017_w)
AM_RANGE(0x4100, 0x410b) AM_WRITE(vt03_410x_w)
AM_RANGE(0x8000, 0xffff) AM_WRITE(vt03_8000_w)
AM_RANGE(0x8000, 0xffff) AM_DEVICE("prg", address_map_bank_device, amap8)
ADDRESS_MAP_END
/* Some later VT models have more RAM */
static ADDRESS_MAP_START( nes_vt_xx_map, AS_PROGRAM, 8, nes_vt_state )
AM_IMPORT_FROM(nes_vt_map)
AM_RANGE(0x0800, 0x0fff) AM_RAM
ADDRESS_MAP_END
static ADDRESS_MAP_START( prg_map, AS_PROGRAM, 8, nes_vt_state )
AM_RANGE(0x0000, 0x1fff) AM_ROMBANK("prg_bank0")
AM_RANGE(0x2000, 0x3fff) AM_ROMBANK("prg_bank1")
AM_RANGE(0x4000, 0x5fff) AM_ROMBANK("prg_bank2")
AM_RANGE(0x6000, 0x7fff) AM_ROMBANK("prg_bank3")
ADDRESS_MAP_END
WRITE_LINE_MEMBER(nes_vt_state::apu_irq)
{
// set_input_line(N2A03_APU_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}
READ8_MEMBER(nes_vt_state::apu_read_mem)
{
return 0x00;//mintf->program->read_byte(offset);
}
void nes_vt_state::ppu_nmi(int *ppu_regs)
{
m_maincpu->set_input_line(INPUT_LINE_NMI, PULSE_LINE);
}
/* not strictly needed, but helps us see where things are in ROM to aid with figuring out banking schemes*/
static const gfx_layout helper_layout =
{
8,8,
RGN_FRAC(1,1),
4,
{ 0*64, 1*64, 2*64, 3*64 },
{ 0,1,2,3,4,5,6,7 },
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
4*64
};
static const gfx_layout helper2_layout =
{
8,8,
RGN_FRAC(1,1),
4,
{ 0*8, 1*8, 2*8, 3*8 },
{ 0,1,2,3,4,5,6,7 },
{ 0*16, 1*16, 2*16, 3*16,4*16,5*16,5*16,6*16,7*16 },
4*64
};
static GFXDECODE_START( vt03_helper )
GFXDECODE_ENTRY( "mainrom", 0, helper_layout, 0x0, 2 )
GFXDECODE_ENTRY( "mainrom", 0, helper2_layout, 0x0, 2 )
GFXDECODE_END
static MACHINE_CONFIG_START( nes_vt )
/* basic machine hardware */
MCFG_CPU_ADD("maincpu", M6502, NTSC_APU_CLOCK) // selectable speed?
MCFG_CPU_PROGRAM_MAP(nes_vt_map)
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_REFRESH_RATE(60.0988)
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC((113.66/(NTSC_APU_CLOCK/1000000)) * (ppu2c0x_device::VBLANK_LAST_SCANLINE_NTSC-ppu2c0x_device::VBLANK_FIRST_SCANLINE+1+2)))
MCFG_SCREEN_SIZE(32*8, 262)
MCFG_SCREEN_VISIBLE_AREA(0*8, 32*8-1, 0*8, 30*8-1)
MCFG_SCREEN_UPDATE_DRIVER(nes_vt_state, screen_update_vt)
MCFG_SCREEN_PALETTE("palette")
MCFG_GFXDECODE_ADD("gfxdecode", "palette", vt03_helper)
MCFG_PALETTE_ADD("palette", 256)
MCFG_PALETTE_INIT_OWNER(nes_vt_state, nesvt)
MCFG_PALETTE_INDIRECT_ENTRIES(4*16*8)
MCFG_PPU_VT03_ADD("ppu")
MCFG_PPU2C0X_CPU("maincpu")
MCFG_PPU2C0X_SET_NMI(nes_vt_state, ppu_nmi)
MCFG_PPU_VT03_READ_BG_CB(READ8(nes_vt_state,chr_r))
MCFG_PPU_VT03_READ_SP_CB(READ8(nes_vt_state,spr_r))
MCFG_DEVICE_ADD("prg", ADDRESS_MAP_BANK, 0)
MCFG_DEVICE_PROGRAM_MAP(prg_map)
MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
MCFG_ADDRESS_MAP_BANK_DATABUS_WIDTH(8)
MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(15)
MCFG_ADDRESS_MAP_BANK_STRIDE(0x8000)
MCFG_NES_CONTROL_PORT_ADD("ctrl1", nes_control_port1_devices, "joypad")
//MCFG_NESCTRL_BRIGHTPIXEL_CB(nes_state, bright_pixel)
MCFG_NES_CONTROL_PORT_ADD("ctrl2", nes_control_port2_devices, "joypad")
//MCFG_NESCTRL_BRIGHTPIXEL_CB(nes_state, bright_pixel)
/* sound hardware */
MCFG_SPEAKER_STANDARD_MONO("mono")
/* this should actually be a custom *almost* doubled up APU, however requires more thought
than just using 2 APUs as registers in the 2nd one affect the PCM channel mode but the
DMA control still comes from the 1st, but in the new mode, sound always outputs via the
2nd. Probably need to split the APU into interface and sound gen logic. */
MCFG_SOUND_ADD("apu", NES_APU, NTSC_APU_CLOCK )
MCFG_NES_APU_IRQ_HANDLER(WRITELINE(nes_vt_state, apu_irq))
MCFG_NES_APU_MEM_READ_CALLBACK(READ8(nes_vt_state, apu_read_mem))
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( nes_vt_xx, nes_vt )
MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_PROGRAM_MAP(nes_vt_xx_map)
MACHINE_CONFIG_END
static INPUT_PORTS_START( nes_vt )
INPUT_PORTS_END
ROM_START( vdogdemo )
ROM_REGION( 0x80000, "mainrom", 0 )
ROM_LOAD( "rom.bin", 0x00000, 0x80000, CRC(054af705) SHA1(e730aeaa94b9cc28aa8b512a5bf411ec45226831) )
ROM_END
ROM_START( mc_dgear )
ROM_REGION( 0x400000, "mainrom", 0 )
ROM_LOAD( "dreamgear 75-in-1(unl)[!].prg", 0x00000, 0x400000, CRC(9aabcb8f) SHA1(aa9446b7777fa64503871225fcaf2a17aafd9af1) )
ROM_END
ROM_START( dgun2500 )
ROM_REGION( 0x2000000, "mainrom", 0 )
ROM_LOAD( "dgun2500.bin", 0x00000, 0x2000000, CRC(a2f963f3) SHA1(e29ed20ccdcf25b5640a607b3d2c9e6a4944e172) ) // 1ST AND 2ND HALF IDENTICAL
ROM_END
ROM_START( dgun2561 )
ROM_REGION( 0x4000000, "mainrom", 0 )
ROM_LOAD( "dgun2561.bin", 0x00000, 0x4000000, CRC(a6e627b4) SHA1(2667d2feb02de349387f9dcfa5418e7ed3afeef6) )
ROM_END
ROM_START( lexcyber )
ROM_REGION( 0x4000000, "mainrom", 0 )
ROM_LOAD( "lexcyber.bin", 0x00000, 0x4000000, CRC(3f3af72c) SHA1(76127054291568fcce1431d21af71f775cfb05a6) )
ROM_END
ROM_START( cybar120 )
ROM_REGION( 0x2000000, "mainrom", 0 )
ROM_LOAD( "M2500P-VT09-EPSON_(20091222VER05,_30R-SX1067-01_PCB,_12R0COB128M_12001-3D05_FW).bin", 0x00000, 0x1000000, CRC(f7138980) SHA1(de31264ee3a5a5c77a86733b2e2d6845fee91ea5) )
ROM_END
ROM_START( ii8in1 )
ROM_REGION( 0x2000000, "mainrom", 0 )
ROM_LOAD( "ii8in1.bin", 0x00000, 0x2000000, CRC(7aee7464) SHA1(7a9cf7f54a350f0853a17459f2dcbef34f4f7c30) ) // 2ND HALF EMPTY
ROM_END
ROM_START( ii32in1 )
ROM_REGION( 0x2000000, "mainrom", 0 )
ROM_LOAD( "ii32in1.bin", 0x00000, 0x2000000, CRC(ddee4eac) SHA1(828c0c18a66bb4872299f9a43d5e3647482c5925) )
ROM_END
// this is glitchy even in other emulators, might just be entirely unfinished, it selects banks but they don't contain the required gfx?
GAME( 200?, vdogdemo, 0, nes_vt, nes_vt, nes_vt_state, 0, ROT0, "VRT", "V-Dog (prototype)", MACHINE_NOT_WORKING )
// should be VT03 based
// for testing 'Shark', 'Octopus', 'Harbor', and 'Earth Fighter' use the extended colour modes, other games just seem to use standard NES modes
GAME( 200?, mc_dgear, 0, nes_vt, nes_vt, nes_vt_state, 0, ROT0, "dreamGEAR", "dreamGEAR 75-in-1", MACHINE_NOT_WORKING )
// this is VT09 based, and needs 8bpp modes at least
// it boots, but gfx look wrong due to unsupported mode
GAME( 2009, cybar120, 0, nes_vt_xx, nes_vt, nes_vt_state, 0, ROT0, "<unknown>", "Cyber Arcade 120-in-1", MACHINE_NOT_WORKING )
// these are NOT VT03, but something newer but based around the same basic designs
GAME( 200?, dgun2500, 0, nes_vt, nes_vt, nes_vt_state, 0, ROT0, "dreamGEAR", "dreamGEAR Wireless Motion Control with 130 games (DGUN-2500)", MACHINE_NOT_WORKING )
GAME( 2012, dgun2561, 0, nes_vt, nes_vt, nes_vt_state, 0, ROT0, "dreamGEAR", "dreamGEAR My Arcade Portable Gaming System (DGUN-2561)", MACHINE_NOT_WORKING )
GAME( 200?, lexcyber, 0, nes_vt_xx, nes_vt, nes_vt_state, 0, ROT0, "Lexibook", "Lexibook Compact Cyber Arcade", MACHINE_NOT_WORKING )
// these seem to have custom CPU opcodes? looks similar, has many of the same games, but isn't 100% valid 6502
GAME( 200?, ii8in1, 0, nes_vt, nes_vt, nes_vt_state, 0, ROT0, "Intec", "InterAct 8-in-1", MACHINE_NOT_WORKING )
GAME( 200?, ii32in1, 0, nes_vt, nes_vt, nes_vt_state, 0, ROT0, "Intec", "InterAct 32-in-1", MACHINE_NOT_WORKING )

View File

@ -47,28 +47,42 @@
#define NES_BATTERY 0
#define NES_WRAM 1
class nes_state : public driver_device
// so that the NES and Famiclones (VT03 for example) can use some common functionality
class nes_base_state : public driver_device
{
public:
nes_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_ppu(*this, "ppu")
, m_ctrl1(*this, "ctrl1")
, m_ctrl2(*this, "ctrl2")
, m_exp(*this, "exp")
, m_cartslot(*this, "nes_slot")
, m_disk(*this, "disk")
{
}
nes_base_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_ctrl1(*this, "ctrl1"),
m_ctrl2(*this, "ctrl2")
{ }
int nes_ppu_vidaccess(int address, int data);
void ppu_nmi(int *ppu_regs);
required_device<cpu_device> m_maincpu;
required_device<nes_control_port_device> m_ctrl1;
required_device<nes_control_port_device> m_ctrl2;
DECLARE_READ8_MEMBER(nes_in0_r);
DECLARE_READ8_MEMBER(nes_in1_r);
DECLARE_WRITE8_MEMBER(nes_in0_w);
};
class nes_state : public nes_base_state
{
public:
nes_state(const machine_config &mconfig, device_type type, const char *tag)
: nes_base_state(mconfig, type, tag),
m_ppu(*this, "ppu"),
m_exp(*this, "exp"),
m_cartslot(*this, "nes_slot"),
m_disk(*this, "disk")
{ }
int nes_ppu_vidaccess(int address, int data);
void ppu_nmi(int *ppu_regs);
DECLARE_READ8_MEMBER(fc_in0_r);
DECLARE_READ8_MEMBER(fc_in1_r);
DECLARE_WRITE8_MEMBER(fc_in0_w);
@ -91,6 +105,8 @@ public:
void setup_disk(nes_disksys_device *slot);
private:
memory_bank *m_prg_bank_mem[5];
/* video-related */
int m_last_frame_flip;
@ -100,15 +116,11 @@ private:
uint8_t *m_vram;
std::unique_ptr<uint8_t[]> m_ciram; //PPU nametable RAM - external to PPU!
required_device<cpu_device> m_maincpu;
required_device<ppu2c0x_device> m_ppu;
required_device<nes_control_port_device> m_ctrl1;
required_device<nes_control_port_device> m_ctrl2;
optional_device<nes_control_port_device> m_exp;
optional_device<nes_cart_slot_device> m_cartslot;
optional_device<nes_disksys_device> m_disk;
memory_bank *m_prg_bank_mem[5];
};
#endif // MAME_INCLUDES_NES_H

View File

@ -112,7 +112,7 @@ void nes_state::machine_start()
// INPUTS
//-------------------------------------------------
READ8_MEMBER(nes_state::nes_in0_r)
READ8_MEMBER(nes_base_state::nes_in0_r)
{
uint8_t ret = 0x40;
ret |= m_ctrl1->read_bit0();
@ -120,7 +120,7 @@ READ8_MEMBER(nes_state::nes_in0_r)
return ret;
}
READ8_MEMBER(nes_state::nes_in1_r)
READ8_MEMBER(nes_base_state::nes_in1_r)
{
uint8_t ret = 0x40;
ret |= m_ctrl2->read_bit0();
@ -128,7 +128,7 @@ READ8_MEMBER(nes_state::nes_in1_r)
return ret;
}
WRITE8_MEMBER(nes_state::nes_in0_w)
WRITE8_MEMBER(nes_base_state::nes_in0_w)
{
m_ctrl1->write(data);
m_ctrl2->write(data);

View File

@ -29292,6 +29292,16 @@ m82p // Nintendo M82 Display Unit PAL
nes // Nintendo Entertainment System
nespal // Nintendo Entertainment System PAL
@source:nes_vt.cpp
vdogdemo
mc_dgear
dgun2500
dgun2561
ii8in1
ii32in1
lexcyber
cybar120
@source:newbrain.cpp
newbrain //
newbraina //