comparison Plugins/Input/console/Gbs_Emu.cpp @ 493:c04dff121e1d trunk

[svn] hostile merge, phase 2: reimport based on new plugin code
author nenolod
date Tue, 24 Jan 2006 20:19:01 -0800
parents
children f12d7e208b43
comparison
equal deleted inserted replaced
492:ccb68bad47b2 493:c04dff121e1d
1
2 // Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
3
4 #include "Gbs_Emu.h"
5
6 #include <string.h>
7
8 #include "blargg_endian.h"
9
10 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
11 can redistribute it and/or modify it under the terms of the GNU Lesser
12 General Public License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version. This
14 module is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17 more details. You should have received a copy of the GNU Lesser General
18 Public License along with this module; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20
21 #include BLARGG_SOURCE_BEGIN
22
23 #ifndef RUN_GB_CPU
24 #define RUN_GB_CPU( cpu, n ) cpu.run( n )
25 #endif
26
27 const long bank_size = 0x4000;
28 const gb_addr_t ram_addr = 0xa000;
29 const gb_addr_t halt_addr = 0x9EFE;
30 static BOOST::uint8_t unmapped_code [Gb_Cpu::page_size];
31
32 Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000 };
33 Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 300 };
34
35 // RAM
36
37 int Gbs_Emu::read_ram( Gbs_Emu* emu, gb_addr_t addr )
38 {
39 return emu->ram [addr - ram_addr];
40 }
41
42 void Gbs_Emu::write_ram( Gbs_Emu* emu, gb_addr_t addr, int data )
43 {
44 emu->ram [addr - ram_addr] = data;
45 }
46
47 // Unmapped
48
49 int Gbs_Emu::read_unmapped( Gbs_Emu*, gb_addr_t addr )
50 {
51 dprintf( "Read from unmapped memory $%.4x\n", (unsigned) addr );
52 return 0xFF; // open bus value
53 }
54
55 void Gbs_Emu::write_unmapped( Gbs_Emu*, gb_addr_t addr, int )
56 {
57 dprintf( "Wrote to unmapped memory $%.4x\n", (unsigned) addr );
58 }
59
60 // ROM
61
62 int Gbs_Emu::read_rom( Gbs_Emu* emu, gb_addr_t addr )
63 {
64 return emu->rom [addr];
65 }
66
67 int Gbs_Emu::read_bank( Gbs_Emu* emu, gb_addr_t addr )
68 {
69 return emu->rom_bank [addr & (bank_size - 1)];
70 }
71
72 void Gbs_Emu::set_bank( int n )
73 {
74 if ( n >= bank_count )
75 {
76 n = 0;
77 dprintf( "Set to non-existent bank %d\n", (int) n );
78 }
79 if ( n == 0 && bank_count > 1 )
80 {
81 // to do: what is the correct behavior? Current Wario Land 3 and
82 // Tetris DX GBS rips require that this have no effect or set to bank 1.
83 //return;
84 //dprintf( "Selected ROM bank 0\n" );
85 }
86 rom_bank = &rom [n * bank_size];
87 cpu.map_code( bank_size, bank_size, rom_bank );
88 }
89
90 void Gbs_Emu::write_rom( Gbs_Emu* emu, gb_addr_t addr, int data )
91 {
92 if ( unsigned (addr - 0x2000) < 0x2000 )
93 emu->set_bank( data & 0x1F );
94 }
95
96 // I/O: Timer, APU
97
98 void Gbs_Emu::set_timer( int modulo, int rate )
99 {
100 if ( timer_mode )
101 {
102 static byte const rates [4] = { 10, 4, 6, 8 };
103 play_period = (gb_time_t) (256 - modulo) << (rates [rate & 3] - double_speed);
104 }
105 }
106
107 inline gb_time_t Gbs_Emu::clock() const
108 {
109 return cpu_time - cpu.remain();
110 }
111
112 int Gbs_Emu::read_io( Gbs_Emu* emu, gb_addr_t addr )
113 {
114 // hi_page is accessed most
115 if ( addr >= 0xFF80 )
116 return emu->hi_page [addr & 0xFF];
117
118 if ( unsigned (addr - Gb_Apu::start_addr) <= Gb_Apu::register_count )
119 return emu->apu.read_register( emu->clock(), addr );
120
121 if ( addr == 0xFF00 )
122 return 0; // joypad
123
124 dprintf( "Unhandled I/O read 0x%4X\n", (unsigned) addr );
125
126 return 0xFF;
127 }
128
129 void Gbs_Emu::write_io( Gbs_Emu* emu, gb_addr_t addr, int data )
130 {
131 // apu is accessed most
132 if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count )
133 {
134 emu->apu.write_register( emu->clock(), addr, data );
135 }
136 else
137 {
138 emu->hi_page [addr & 0xFF] = data;
139
140 if ( addr == 0xFF06 || addr == 0xFF07 )
141 emu->set_timer( emu->hi_page [6], emu->hi_page [7] );
142
143 //if ( addr == 0xFFFF )
144 // dprintf( "Wrote interrupt mask\n" );
145 }
146 }
147
148 Gbs_Emu::Gbs_Emu( double gain ) : cpu( this )
149 {
150 apu.volume( gain );
151
152 static equalizer_t const eq = { -1.0, 120 };
153 set_equalizer( eq );
154
155 // unmapped code is all HALT instructions
156 memset( unmapped_code, 0x76, sizeof unmapped_code );
157
158 // cpu
159 cpu.reset( unmapped_code, read_unmapped, write_unmapped );
160 cpu.map_memory( 0x0000, 0x4000, read_rom, write_rom );
161 cpu.map_memory( 0x4000, 0x4000, read_bank, write_rom );
162 cpu.map_memory( ram_addr, 0x4000, read_ram, write_ram );
163 cpu.map_code( ram_addr, 0x4000, ram );
164 cpu.map_code( 0xFF00, 0x0100, hi_page );
165 cpu.map_memory( 0xFF00, 0x0100, read_io, write_io );
166 }
167
168 Gbs_Emu::~Gbs_Emu()
169 {
170 }
171
172 void Gbs_Emu::unload()
173 {
174 cpu.r.pc = halt_addr;
175 rom.clear();
176 }
177
178 void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
179 {
180 apu.osc_output( i, c, l, r );
181 }
182
183 void Gbs_Emu::update_eq( blip_eq_t const& eq )
184 {
185 apu.treble_eq( eq );
186 }
187
188 blargg_err_t Gbs_Emu::load( Data_Reader& in )
189 {
190 header_t h;
191 BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
192 return load( h, in );
193 }
194
195 blargg_err_t Gbs_Emu::load( const header_t& h, Data_Reader& in )
196 {
197 header_ = h;
198 unload();
199
200 // check compatibility
201 if ( 0 != memcmp( header_.tag, "GBS", 3 ) )
202 return "Not a GBS file";
203 if ( header_.vers != 1 )
204 return "Unsupported GBS format";
205
206 // gather relevant fields
207 load_addr = get_le16( header_.load_addr );
208 init_addr = get_le16( header_.init_addr );
209 play_addr = get_le16( header_.play_addr );
210 stack_ptr = get_le16( header_.stack_ptr );
211 double_speed = (header_.timer_mode & 0x80) != 0;
212 timer_modulo_init = header_.timer_modulo;
213 timer_mode = header_.timer_mode;
214 if ( !(timer_mode & 0x04) )
215 timer_mode = 0; // using vbl
216
217 #ifndef NDEBUG
218 {
219 if ( header_.timer_mode & 0x78 )
220 dprintf( "TAC field has extra bits set: 0x%02x\n", (unsigned) header_.timer_mode );
221
222 if ( load_addr < 0x400 || load_addr >= 0x8000 ||
223 init_addr < 0x400 || init_addr >= 0x8000 ||
224 play_addr < 0x400 || play_addr >= 0x8000 )
225 dprintf( "Load/init/play address violates GBS spec.\n" );
226 }
227 #endif
228
229 // rom
230 bank_count = (load_addr + in.remain() + bank_size - 1) / bank_size;
231 BLARGG_RETURN_ERR( rom.resize( bank_count * bank_size ) );
232 memset( rom.begin(), 0, rom.size() );
233 blargg_err_t err = in.read( &rom [load_addr], in.remain() );
234 if ( err )
235 {
236 unload();
237 return err;
238 }
239
240 // cpu
241 cpu.rst_base = load_addr;
242 cpu.map_code( 0x0000, 0x4000, rom.begin() );
243
244 set_voice_count( Gb_Apu::osc_count );
245 set_track_count( header_.track_count );
246
247 return setup_buffer( 4194304 );
248 }
249
250 const char** Gbs_Emu::voice_names() const
251 {
252 static const char* names [] = { "Square 1", "Square 2", "Wave", "Noise" };
253 return names;
254 }
255
256 // Emulation
257
258 static const BOOST::uint8_t sound_data [Gb_Apu::register_count] = {
259 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1
260 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2
261 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave
262 0x00, 0xFF, 0x00, 0x00, 0xBF, // noise
263 0x77, 0xF3, 0xF1, // vin/volume, status, power mode
264 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused
265 0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data
266 0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48
267 };
268
269 void Gbs_Emu::cpu_jsr( gb_addr_t addr )
270 {
271 cpu.write( --cpu.r.sp, cpu.r.pc >> 8 );
272 cpu.write( --cpu.r.sp, cpu.r.pc&0xFF );
273 cpu.r.pc = addr;
274 }
275
276 void Gbs_Emu::start_track( int track_index )
277 {
278 require( rom.size() ); // file must be loaded
279
280 Classic_Emu::start_track( track_index );
281
282 apu.reset();
283
284 memset( ram, 0, sizeof ram );
285 memset( hi_page, 0, sizeof hi_page );
286
287 // configure hardware
288 set_bank( bank_count > 1 );
289 for ( int i = 0; i < (int) sizeof sound_data; i++ )
290 apu.write_register( 0, i + apu.start_addr, sound_data [i] );
291 play_period = 70224; // 59.73 Hz
292 set_timer( timer_modulo_init, timer_mode ); // ignored if using vbl
293 next_play = play_period;
294
295 // set up init call
296 cpu.r.a = track_index;
297 cpu.r.b = 0;
298 cpu.r.c = 0;
299 cpu.r.d = 0;
300 cpu.r.e = 0;
301 cpu.r.h = 0;
302 cpu.r.l = 0;
303 cpu.r.flags = 0;
304 cpu.r.pc = halt_addr;
305 cpu.r.sp = stack_ptr;
306 cpu_jsr( init_addr );
307 }
308
309 blip_time_t Gbs_Emu::run_clocks( blip_time_t duration, bool* added_stereo )
310 {
311 require( rom.size() ); // file must be loaded
312
313 cpu_time = 0;
314 while ( cpu_time < duration )
315 {
316 // check for idle cpu
317 if ( cpu.r.pc == halt_addr )
318 {
319 if ( next_play > duration )
320 {
321 cpu_time = duration;
322 break;
323 }
324
325 if ( cpu_time < next_play )
326 cpu_time = next_play;
327 next_play += play_period;
328 cpu_jsr( play_addr );
329 }
330
331 long count = duration - cpu_time;
332 cpu_time = duration;
333 Gb_Cpu::result_t result = RUN_GB_CPU( cpu, count );
334 cpu_time -= cpu.remain();
335
336 if ( (result == Gb_Cpu::result_halt && cpu.r.pc != halt_addr) ||
337 result == Gb_Cpu::result_badop )
338 {
339 if ( cpu.r.pc > 0xFFFF )
340 {
341 dprintf( "PC wrapped around\n" );
342 cpu.r.pc &= 0xFFFF;
343 }
344 else
345 {
346 log_error();
347 dprintf( "Bad opcode $%.2x at $%.4x\n",
348 (int) cpu.read( cpu.r.pc ), (int) cpu.r.pc );
349 cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF;
350 cpu_time += 6;
351 }
352 }
353 }
354
355 // end time frame
356
357 next_play -= cpu_time;
358 if ( next_play < 0 ) // could go negative if routine is taking too long to return
359 next_play = 0;
360
361 if ( apu.end_frame( cpu_time ) && added_stereo )
362 *added_stereo = true;
363
364 return cpu_time;
365 }
366