251 lines
5.4 KiB
C++
251 lines
5.4 KiB
C++
|
/* Copyright notice for this file:
|
||
|
* Copyright (C) 2004-2006 Shay Green
|
||
|
* Copyright (C) 2007 CaH4e3
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
*
|
||
|
* General code is from FCEUX https://sourceforge.net/p/fceultra/code/HEAD/tree/fceu/trunk/src/boards/vrc2and4.cpp
|
||
|
* IRQ portion is from existing VRC6/VRC7 by Shay Green
|
||
|
* This mapper was ported by retrowertz for Libretro port of QuickNES.
|
||
|
* 3-19-2018
|
||
|
*
|
||
|
* VRC-2/VRC-4 Konami
|
||
|
*/
|
||
|
|
||
|
#pragma once
|
||
|
#include "Nes_Mapper.h"
|
||
|
|
||
|
struct vrc2_state_t
|
||
|
{
|
||
|
uint8_t prg_banks [ 2 ];
|
||
|
uint8_t chr_banks [ 8 ];
|
||
|
uint8_t mirroring;
|
||
|
uint8_t prg_swap;
|
||
|
uint8_t irq_latch;
|
||
|
uint8_t irq_control;
|
||
|
|
||
|
// internal state
|
||
|
uint16_t next_time;
|
||
|
uint8_t irq_pending;
|
||
|
};
|
||
|
|
||
|
BOOST_STATIC_ASSERT( sizeof ( vrc2_state_t ) == 18 );
|
||
|
|
||
|
template <bool type_a, bool type_b>
|
||
|
class Mapper_VRC2_4 : public Nes_Mapper, vrc2_state_t {
|
||
|
public:
|
||
|
Mapper_VRC2_4()
|
||
|
{
|
||
|
if (type_a && type_b) // mapper 21
|
||
|
{
|
||
|
is22 = 0;
|
||
|
reg1mask = 0x42;
|
||
|
reg2mask = 0x84;
|
||
|
}
|
||
|
else if (!type_a && type_b) // mapper 22
|
||
|
{
|
||
|
is22 = 1;
|
||
|
reg1mask = 2;
|
||
|
reg2mask = 1;
|
||
|
}
|
||
|
else if (!type_a && !type_b) // mapper 23
|
||
|
{
|
||
|
is22 = 0;
|
||
|
reg1mask = 0x15;
|
||
|
reg2mask = 0x2a;
|
||
|
}
|
||
|
else if (type_a && !type_b) // mapper 25
|
||
|
{
|
||
|
is22 = 0;
|
||
|
reg1mask = 0xa;
|
||
|
reg2mask = 0x5;
|
||
|
}
|
||
|
vrc2_state_t * state = this;
|
||
|
register_state( state, sizeof * state );
|
||
|
}
|
||
|
|
||
|
void reset_state()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void apply_mapping()
|
||
|
{
|
||
|
if ( !is22 ) enable_sram();
|
||
|
update_prg();
|
||
|
update_chr();
|
||
|
set_mirroring();
|
||
|
}
|
||
|
|
||
|
void reset_timer( nes_time_t present )
|
||
|
{
|
||
|
next_time = present + unsigned ( ( 0x100 - irq_latch ) * timer_period ) / 4;
|
||
|
}
|
||
|
|
||
|
virtual void run_until( nes_time_t end_time )
|
||
|
{
|
||
|
if ( irq_control & 2 )
|
||
|
{
|
||
|
while ( next_time < end_time )
|
||
|
{
|
||
|
// printf( "%d timer expired\n", next_time );
|
||
|
irq_pending = true;
|
||
|
reset_timer( next_time );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual void end_frame( nes_time_t end_time )
|
||
|
{
|
||
|
run_until( end_time );
|
||
|
|
||
|
// to do: next_time might go negative if IRQ is disabled
|
||
|
next_time -= end_time;
|
||
|
}
|
||
|
|
||
|
virtual nes_time_t next_irq( nes_time_t present )
|
||
|
{
|
||
|
if ( irq_pending )
|
||
|
return present;
|
||
|
|
||
|
if ( irq_control & 2 )
|
||
|
return next_time + 1;
|
||
|
|
||
|
return no_irq;
|
||
|
}
|
||
|
|
||
|
void write_irq( nes_time_t time, nes_addr_t addr, int data );
|
||
|
|
||
|
void write( nes_time_t time, nes_addr_t addr, int data )
|
||
|
{
|
||
|
addr = ( addr & 0xF000 ) | !!( addr & reg2mask ) << 1 | !!( addr & reg1mask );
|
||
|
|
||
|
if( addr >= 0xB000 && addr <= 0xE003)
|
||
|
{
|
||
|
unsigned banknumber = ( ( addr >> 1 ) & 1 ) | ( ( addr - 0xB000 ) >> 11 );
|
||
|
unsigned offset = ( addr & 1 ) << 2;
|
||
|
|
||
|
chr_banks [ banknumber ] &= ( 0xF0 ) >> offset;
|
||
|
chr_banks [ banknumber ] |= ( data & 0xF ) << offset;
|
||
|
chr_banks [ banknumber ] |= ( offset ? ( ( data & 0x10 ) << 4 ) : 0 );
|
||
|
update_chr();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch ( addr & 0xF003 )
|
||
|
{
|
||
|
case 0x8000:
|
||
|
case 0x8001:
|
||
|
case 0x8002:
|
||
|
case 0x8003:
|
||
|
prg_banks [ 0 ] = data & 0x1F;
|
||
|
update_prg();
|
||
|
break;
|
||
|
case 0xA000:
|
||
|
case 0xA001:
|
||
|
case 0xA002:
|
||
|
case 0xA003:
|
||
|
prg_banks [ 1 ] = data & 0x1F;
|
||
|
update_prg();
|
||
|
break;
|
||
|
case 0x9000:
|
||
|
case 0x9001:
|
||
|
mirroring = data;
|
||
|
set_mirroring();
|
||
|
break;
|
||
|
case 0x9002:
|
||
|
case 0x9003:
|
||
|
prg_swap = data;
|
||
|
update_prg();
|
||
|
break;
|
||
|
case 0xF000:
|
||
|
case 0xF001:
|
||
|
case 0xF002:
|
||
|
case 0xF003:
|
||
|
write_irq( time, addr, data );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned is22, reg1mask, reg2mask;
|
||
|
enum { timer_period = 113 * 4 + 3 };
|
||
|
|
||
|
private:
|
||
|
void set_mirroring()
|
||
|
{
|
||
|
switch ( mirroring & 3 )
|
||
|
{
|
||
|
case 0: mirror_vert(); break;
|
||
|
case 1: mirror_horiz(); break;
|
||
|
case 2:
|
||
|
case 3: mirror_single( mirroring & 1 ); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void update_prg()
|
||
|
{
|
||
|
if ( prg_swap & 2 )
|
||
|
{
|
||
|
set_prg_bank( 0x8000, bank_8k, ( 0xFE ) );
|
||
|
set_prg_bank( 0xC000, bank_8k, prg_banks [ 0 ] );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
set_prg_bank( 0x8000, bank_8k, prg_banks [ 0 ] );
|
||
|
set_prg_bank( 0xC000, bank_8k, ( 0xFE ) );
|
||
|
}
|
||
|
|
||
|
set_prg_bank( 0xA000, bank_8k, prg_banks [ 1 ] );
|
||
|
set_prg_bank( 0xE000, bank_8k, ( 0xFF ) );
|
||
|
}
|
||
|
|
||
|
void update_chr()
|
||
|
{
|
||
|
for ( int i = 0; i < (int) sizeof chr_banks; i++ )
|
||
|
set_chr_bank( i * 0x400, bank_1k, chr_banks [ i ] >> is22 );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <bool type_a, bool type_b>
|
||
|
void Mapper_VRC2_4<type_a, type_b>::write_irq( nes_time_t time,
|
||
|
nes_addr_t addr, int data )
|
||
|
{
|
||
|
// IRQ
|
||
|
run_until( time );
|
||
|
//printf("%6d VRC2_4 [%d] A:%04x V:%02x\n", time, addr & 3, addr, data);
|
||
|
switch ( addr & 3 )
|
||
|
{
|
||
|
case 0:
|
||
|
irq_latch = ( irq_latch & 0xF0 ) | ( data & 0xF );
|
||
|
break;
|
||
|
case 1:
|
||
|
irq_latch = ( irq_latch & 0x0F ) | ( ( data & 0xF ) << 4 );
|
||
|
break;
|
||
|
case 2:
|
||
|
irq_pending = false;
|
||
|
irq_control = data & 3;
|
||
|
if ( data & 2 ) reset_timer( time );
|
||
|
break;
|
||
|
case 3:
|
||
|
irq_pending = false;
|
||
|
irq_control = ( irq_control & ~2 ) | ( ( irq_control << 1 ) & 2 );
|
||
|
break;
|
||
|
}
|
||
|
irq_changed();
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef Mapper_VRC2_4<true,true> Mapper021;
|