Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/quicknes/nes_emu/Mapper_Vrc6.cpp
2 views
1
2
// Konami VRC6 mapper
3
4
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
5
6
#include "Nes_Mapper.h"
7
8
#include <string.h>
9
#include "Nes_Vrc6_Apu.h"
10
#include "blargg_endian.h"
11
12
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
13
can redistribute it and/or modify it under the terms of the GNU Lesser
14
General Public License as published by the Free Software Foundation; either
15
version 2.1 of the License, or (at your option) any later version. This
16
module is distributed in the hope that it will be useful, but WITHOUT ANY
17
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
19
more details. You should have received a copy of the GNU Lesser General
20
Public License along with this module; if not, write to the Free Software
21
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
22
23
#include "blargg_source.h"
24
25
struct vrc6_state_t
26
{
27
// written registers
28
byte prg_16k_bank;
29
// could move sound regs int and out of vrc6_apu_state_t for state saving,
30
// allowing them to be stored here
31
byte old_sound_regs [3] [3]; // to do: eliminate this duplicate
32
byte mirroring;
33
byte prg_8k_bank;
34
byte chr_banks [8];
35
byte irq_reload;
36
byte irq_mode;
37
38
// internal state
39
BOOST::uint16_t next_time;
40
byte irq_pending;
41
byte unused;
42
43
vrc6_apu_state_t sound_state;
44
45
void swap();
46
};
47
BOOST_STATIC_ASSERT( sizeof (vrc6_state_t) == 26 + sizeof (vrc6_apu_state_t) );
48
49
void vrc6_state_t::swap()
50
{
51
set_le16( &next_time, next_time );
52
for ( unsigned i = 0; i < sizeof sound_state.delays / sizeof sound_state.delays [0]; i++ )
53
set_le16( &sound_state.delays [i], sound_state.delays [i] );
54
}
55
56
class Mapper_Vrc6 : public Nes_Mapper, vrc6_state_t {
57
int swap_mask;
58
Nes_Vrc6_Apu sound;
59
enum { timer_period = 113 * 4 + 3 };
60
public:
61
Mapper_Vrc6( int sm )
62
{
63
swap_mask = sm;
64
vrc6_state_t* state = this;
65
register_state( state, sizeof *state );
66
}
67
68
virtual int channel_count() const { return sound.osc_count; }
69
70
virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); }
71
72
virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); }
73
74
virtual void reset_state()
75
{
76
prg_8k_bank = last_bank - 1;
77
sound.reset();
78
}
79
80
virtual void save_state( mapper_state_t& out )
81
{
82
sound.save_state( &sound_state );
83
vrc6_state_t::swap();
84
Nes_Mapper::save_state( out );
85
vrc6_state_t::swap(); // to do: kind of hacky to swap in place
86
}
87
88
virtual void read_state( mapper_state_t const& in );
89
90
virtual void apply_mapping()
91
{
92
enable_sram();
93
set_prg_bank( 0x8000, bank_16k, prg_16k_bank );
94
set_prg_bank( 0xC000, bank_8k, prg_8k_bank );
95
96
for ( int i = 0; i < (int) sizeof chr_banks; i++ )
97
set_chr_bank( i * 0x400, bank_1k, chr_banks [i] );
98
99
write_bank( 0xb003, mirroring );
100
}
101
102
void reset_timer( nes_time_t present )
103
{
104
next_time = present + unsigned ((0x100 - irq_reload) * timer_period) / 4;
105
}
106
107
virtual void run_until( nes_time_t end_time )
108
{
109
if ( irq_mode & 2 )
110
{
111
while ( next_time < end_time )
112
{
113
//dprintf( "%d timer expired\n", next_time );
114
irq_pending = true;
115
reset_timer( next_time );
116
}
117
}
118
}
119
120
virtual void end_frame( nes_time_t end_time )
121
{
122
run_until( end_time );
123
124
// to do: next_time might go negative if IRQ is disabled
125
next_time -= end_time;
126
127
sound.end_frame( end_time );
128
}
129
130
virtual nes_time_t next_irq( nes_time_t present )
131
{
132
if ( irq_pending )
133
return present;
134
135
if ( irq_mode & 2 )
136
return next_time + 1;
137
138
return no_irq;
139
}
140
141
void write_bank( nes_addr_t, int data );
142
void write_irq( nes_time_t, nes_addr_t, int data );
143
144
virtual void write( nes_time_t time, nes_addr_t addr, int data )
145
{
146
int osc = unsigned (addr - sound.base_addr) / sound.addr_step;
147
148
if ( (addr + 1) & 2 ) // optionally swap 1 and 2
149
addr ^= swap_mask;
150
151
int reg = addr & 3;
152
if ( (unsigned) osc < sound.osc_count && reg < sound.reg_count )
153
sound.write_osc( time, osc, reg, data );
154
else if ( addr < 0xf000 )
155
write_bank( addr, data );
156
else
157
write_irq( time, addr, data );
158
}
159
};
160
161
void Mapper_Vrc6::read_state( mapper_state_t const& in )
162
{
163
Nes_Mapper::read_state( in );
164
vrc6_state_t::swap();
165
166
// to do: eliminate when format is updated
167
// old-style registers
168
static char zero [sizeof old_sound_regs] = { 0 };
169
if ( 0 != memcmp( old_sound_regs, zero, sizeof zero ) )
170
{
171
dprintf( "Using old VRC6 sound register format\n" );
172
memcpy( sound_state.regs, old_sound_regs, sizeof sound_state.regs );
173
memset( old_sound_regs, 0, sizeof old_sound_regs );
174
}
175
176
sound.load_state( sound_state );
177
}
178
179
void Mapper_Vrc6::write_irq( nes_time_t time, nes_addr_t addr, int data )
180
{
181
// IRQ
182
run_until( time );
183
//dprintf( "%d VRC6 IRQ [%d] = %02X\n", time, addr & 3, data );
184
switch ( addr & 3 )
185
{
186
case 0:
187
irq_reload = data;
188
break;
189
190
case 1:
191
irq_pending = false;
192
irq_mode = data;
193
if ( data & 2 )
194
reset_timer( time );
195
break;
196
197
case 2:
198
irq_pending = false;
199
irq_mode = (irq_mode & ~2) | ((irq_mode << 1) & 2);
200
break;
201
}
202
irq_changed();
203
}
204
205
void Mapper_Vrc6::write_bank( nes_addr_t addr, int data )
206
{
207
switch ( addr & 0xf003 )
208
{
209
case 0x8000:
210
prg_16k_bank = data;
211
set_prg_bank( 0x8000, bank_16k, data );
212
break;
213
214
case 0xb003: {
215
mirroring = data;
216
217
//dprintf( "Change mirroring %d\n", data );
218
// emu()->enable_sram( data & 0x80 ); // to do: needed?
219
int page = data >> 5 & 1;
220
if ( data & 8 )
221
mirror_single( ((data >> 2) ^ page) & 1 );
222
else if ( data & 4 )
223
mirror_horiz( page );
224
else
225
mirror_vert( page );
226
break;
227
}
228
229
case 0xc000:
230
prg_8k_bank = data;
231
set_prg_bank( 0xC000, bank_8k, data );
232
break;
233
234
default:
235
int bank = (addr >> 11 & 4) | (addr & 3);
236
if ( addr >= 0xd000 )
237
{
238
//dprintf( "change chr bank %d\n", bank );
239
chr_banks [bank] = data;
240
set_chr_bank( bank * 0x400, bank_1k, data );
241
}
242
break;
243
}
244
}
245
246
static Nes_Mapper* make_vrc6a()
247
{
248
return BLARGG_NEW Mapper_Vrc6( 0 );
249
}
250
251
static Nes_Mapper* make_vrc6b()
252
{
253
return BLARGG_NEW Mapper_Vrc6( 3 );
254
}
255
256
void register_vrc6_mapper();
257
void register_vrc6_mapper()
258
{
259
Nes_Mapper::register_mapper( 24, make_vrc6a );
260
Nes_Mapper::register_mapper( 26, make_vrc6b );
261
}
262
263
264