/src/vlc/contrib/contrib-build/game-music-emu/gme/Gbs_Emu.cpp
Line | Count | Source |
1 | | // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ |
2 | | |
3 | | #include "Gbs_Emu.h" |
4 | | |
5 | | #include "blargg_endian.h" |
6 | | #include <string.h> |
7 | | |
8 | | /* Copyright (C) 2003-2006 Shay Green. This module is free software; you |
9 | | can redistribute it and/or modify it under the terms of the GNU Lesser |
10 | | General Public License as published by the Free Software Foundation; either |
11 | | version 2.1 of the License, or (at your option) any later version. This |
12 | | module is distributed in the hope that it will be useful, but WITHOUT ANY |
13 | | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
14 | | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
15 | | details. You should have received a copy of the GNU Lesser General Public |
16 | | License along with this module; if not, write to the Free Software Foundation, |
17 | | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ |
18 | | |
19 | | #include "blargg_source.h" |
20 | | |
21 | | Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = |
22 | | Music_Emu::make_equalizer( -47.0, 2000 ); |
23 | | Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = |
24 | | Music_Emu::make_equalizer( 0.0, 300 ); |
25 | | |
26 | | Gbs_Emu::Gbs_Emu() |
27 | 1.18k | { |
28 | 1.18k | set_type( gme_gbs_type ); |
29 | | |
30 | 1.18k | static const char* const names [Gb_Apu::osc_count] = { |
31 | 1.18k | "Square 1", "Square 2", "Wave", "Noise" |
32 | 1.18k | }; |
33 | 1.18k | set_voice_names( names ); |
34 | | |
35 | 1.18k | static int const types [Gb_Apu::osc_count] = { |
36 | 1.18k | wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0 |
37 | 1.18k | }; |
38 | 1.18k | set_voice_types( types ); |
39 | | |
40 | 1.18k | set_silence_lookahead( 6 ); |
41 | 1.18k | set_max_initial_silence( 21 ); |
42 | 1.18k | set_gain( 1.2 ); |
43 | | |
44 | 1.18k | set_equalizer( make_equalizer( -1.0, 120 ) ); |
45 | 1.18k | } |
46 | | |
47 | 1.18k | Gbs_Emu::~Gbs_Emu() { } |
48 | | |
49 | | void Gbs_Emu::unload() |
50 | 1.19k | { |
51 | 1.19k | rom.clear(); |
52 | 1.19k | Music_Emu::unload(); |
53 | 1.19k | } |
54 | | |
55 | | // Track info |
56 | | |
57 | | static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) |
58 | 1.36k | { |
59 | 1.36k | GME_COPY_FIELD( h, out, game ); |
60 | 1.36k | GME_COPY_FIELD( h, out, author ); |
61 | 1.36k | GME_COPY_FIELD( h, out, copyright ); |
62 | 1.36k | } |
63 | | |
64 | | blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const |
65 | 1.36k | { |
66 | 1.36k | copy_gbs_fields( header_, out ); |
67 | 1.36k | return 0; |
68 | 1.36k | } |
69 | | |
70 | | static blargg_err_t check_gbs_header( void const* header ) |
71 | 1.18k | { |
72 | 1.18k | if ( memcmp( header, "GBS", 3 ) ) |
73 | 0 | return gme_wrong_file_type; |
74 | 1.18k | return 0; |
75 | 1.18k | } |
76 | | |
77 | | struct Gbs_File : Gme_Info_ |
78 | | { |
79 | | Gbs_Emu::header_t h; |
80 | | |
81 | 0 | Gbs_File() { set_type( gme_gbs_type ); } |
82 | | |
83 | | blargg_err_t load_( Data_Reader& in ) |
84 | 0 | { |
85 | 0 | blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); |
86 | 0 | if ( err ) |
87 | 0 | return (err == in.eof_error ? gme_wrong_file_type : err); |
88 | | |
89 | 0 | set_track_count( h.track_count ); |
90 | 0 | return check_gbs_header( &h ); |
91 | 0 | } |
92 | | |
93 | | blargg_err_t track_info_( track_info_t* out, int ) const |
94 | 0 | { |
95 | 0 | copy_gbs_fields( h, out ); |
96 | 0 | return 0; |
97 | 0 | } |
98 | | }; |
99 | | |
100 | 1.18k | static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } |
101 | 0 | static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } |
102 | | |
103 | | static gme_type_t_ const gme_gbs_type_ = { "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }; |
104 | | extern gme_type_t const gme_gbs_type = &gme_gbs_type_; |
105 | | |
106 | | // Setup |
107 | | |
108 | | blargg_err_t Gbs_Emu::load_( Data_Reader& in ) |
109 | 1.18k | { |
110 | 1.18k | blaarg_static_assert( offsetof (header_t,copyright [32]) == header_size, "GBS Header layout incorrect!" ); |
111 | 1.18k | RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); |
112 | | |
113 | 1.18k | set_track_count( header_.track_count ); |
114 | 1.18k | RETURN_ERR( check_gbs_header( &header_ ) ); |
115 | | |
116 | 1.18k | if ( header_.vers != 1 ) |
117 | 0 | set_warning( "Unknown file version" ); |
118 | | |
119 | 1.18k | if ( header_.timer_mode & 0x78 ) |
120 | 330 | set_warning( "Invalid timer mode" ); |
121 | | |
122 | 1.18k | unsigned load_addr = get_le16( header_.load_addr ); |
123 | 1.18k | if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || |
124 | 1.10k | load_addr < 0x400 ) |
125 | 95 | set_warning( "Invalid load/init/play address" ); |
126 | | |
127 | 1.18k | set_voice_count( Gb_Apu::osc_count ); |
128 | | |
129 | 1.18k | apu.volume( gain() ); |
130 | | |
131 | 1.18k | return setup_buffer( 4194304 ); |
132 | 1.18k | } |
133 | | |
134 | | void Gbs_Emu::update_eq( blip_eq_t const& eq ) |
135 | 2.37k | { |
136 | 2.37k | apu.treble_eq( eq ); |
137 | 2.37k | } |
138 | | |
139 | | void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) |
140 | 4.74k | { |
141 | 4.74k | apu.osc_output( i, c, l, r ); |
142 | 4.74k | } |
143 | | |
144 | | // Emulation |
145 | | |
146 | | // see gb_cpu_io.h for read/write functions |
147 | | |
148 | | void Gbs_Emu::set_bank( int n ) |
149 | 94.9M | { |
150 | | // Only valid for MBC1 cartridges, but hopefully shouldn't hurt |
151 | 94.9M | n &= 0x1f; |
152 | 94.9M | if (n == 0) |
153 | 5.45M | { |
154 | 5.45M | n = 1; |
155 | 5.45M | } |
156 | | |
157 | 94.9M | int32_t addr = n * (int32_t) bank_size; |
158 | 94.9M | if (addr > rom.size()) |
159 | 85.0M | { |
160 | 85.0M | return; |
161 | 85.0M | } |
162 | 9.88M | cpu::map_code( bank_size, bank_size, rom.at_addr( rom.mask_addr( addr ) ) ); |
163 | 9.88M | } |
164 | | |
165 | | void Gbs_Emu::update_timer() |
166 | 29.2k | { |
167 | 29.2k | if ( header_.timer_mode & 0x04 ) |
168 | 3.19k | { |
169 | 3.19k | static byte const rates [4] = { 10, 4, 6, 8 }; |
170 | 3.19k | int shift = rates [ram [hi_page + 7] & 3] - (header_.timer_mode >> 7); |
171 | 3.19k | play_period = (256L - ram [hi_page + 6]) << shift; |
172 | 3.19k | } |
173 | 26.0k | else |
174 | 26.0k | { |
175 | 26.0k | play_period = 70224; // 59.73 Hz |
176 | 26.0k | } |
177 | 29.2k | if ( tempo() != 1.0 ) |
178 | 0 | play_period = blip_time_t (play_period / tempo()); |
179 | 29.2k | } |
180 | | |
181 | | static uint8_t const sound_data [Gb_Apu::register_count] = { |
182 | | 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1 |
183 | | 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 |
184 | | 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave |
185 | | 0x00, 0xFF, 0x00, 0x00, 0xBF, // noise |
186 | | 0x77, 0xF3, 0xF1, // vin/volume, status, power mode |
187 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused |
188 | | 0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data |
189 | | 0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48 |
190 | | }; |
191 | | |
192 | | void Gbs_Emu::cpu_jsr( gb_addr_t addr ) |
193 | 37.9k | { |
194 | 37.9k | check( cpu::r.sp == get_le16( header_.stack_ptr ) ); |
195 | 37.9k | cpu::r.pc = addr; |
196 | 37.9k | cpu_write( --cpu::r.sp, idle_addr >> 8 ); |
197 | 37.9k | cpu_write( --cpu::r.sp, idle_addr&0xFF ); |
198 | 37.9k | } |
199 | | |
200 | | void Gbs_Emu::set_tempo_( double t ) |
201 | 1.18k | { |
202 | 1.18k | apu.set_tempo( t ); |
203 | 1.18k | update_timer(); |
204 | 1.18k | } |
205 | | |
206 | | blargg_err_t Gbs_Emu::start_track_( int track ) |
207 | 1.36k | { |
208 | 1.36k | RETURN_ERR( Classic_Emu::start_track_( track ) ); |
209 | | |
210 | 1.36k | memset( ram, 0, 0x4000 ); |
211 | 1.36k | memset( ram + 0x4000, 0xFF, 0x1F00 ); |
212 | 1.36k | memset( ram + 0x5F00, 0, sizeof ram - 0x5F00 ); |
213 | 1.36k | ram [hi_page] = 0; // joypad reads back as 0 |
214 | | |
215 | 1.36k | apu.reset(); |
216 | 66.7k | for ( int i = 0; i < (int) sizeof sound_data; i++ ) |
217 | 65.3k | apu.write_register( 0, i + apu.start_addr, sound_data [i] ); |
218 | | |
219 | 1.36k | unsigned load_addr = get_le16( header_.load_addr ); |
220 | 1.36k | rom.set_addr( load_addr ); |
221 | 1.36k | cpu::rst_base = load_addr; |
222 | | |
223 | 1.36k | cpu::reset( rom.unmapped() ); |
224 | | |
225 | 1.36k | cpu::map_code( ram_addr, 0x10000 - ram_addr, ram ); |
226 | 1.36k | cpu::map_code( 0, bank_size, rom.at_addr( 0 ) ); |
227 | 1.36k | set_bank( rom.size() > bank_size ); |
228 | | |
229 | 1.36k | ram [hi_page + 6] = header_.timer_modulo; |
230 | 1.36k | ram [hi_page + 7] = header_.timer_mode; |
231 | 1.36k | update_timer(); |
232 | 1.36k | next_play = play_period; |
233 | | |
234 | 1.36k | cpu::r.a = track; |
235 | 1.36k | cpu::r.pc = idle_addr; |
236 | 1.36k | cpu::r.sp = get_le16( header_.stack_ptr ); |
237 | 1.36k | cpu_time = 0; |
238 | 1.36k | cpu_jsr( get_le16( header_.init_addr ) ); |
239 | | |
240 | 1.36k | return 0; |
241 | 1.36k | } |
242 | | |
243 | | blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) |
244 | 384k | { |
245 | 384k | cpu_time = 0; |
246 | 47.4M | while ( cpu_time < duration ) |
247 | 47.0M | { |
248 | 47.0M | long count = duration - cpu_time; |
249 | 47.0M | cpu_time = duration; |
250 | 47.0M | bool result = cpu::run( count ); |
251 | 47.0M | cpu_time -= cpu::remain(); |
252 | | |
253 | 47.0M | if ( result ) |
254 | 46.7M | { |
255 | 46.7M | if ( cpu::r.pc == idle_addr ) |
256 | 46.1k | { |
257 | 46.1k | if ( next_play > duration ) |
258 | 9.53k | { |
259 | 9.53k | cpu_time = duration; |
260 | 9.53k | break; |
261 | 9.53k | } |
262 | | |
263 | 36.6k | if ( cpu_time < next_play ) |
264 | 36.3k | cpu_time = next_play; |
265 | 36.6k | next_play += play_period; |
266 | 36.6k | cpu_jsr( get_le16( header_.play_addr ) ); |
267 | 36.6k | GME_FRAME_HOOK( this ); |
268 | | // TODO: handle timer rates different than 60 Hz |
269 | 36.6k | } |
270 | 46.6M | else if ( cpu::r.pc > 0xFFFF ) |
271 | 0 | { |
272 | 0 | debug_printf( "PC wrapped around\n" ); |
273 | 0 | cpu::r.pc &= 0xFFFF; |
274 | 0 | } |
275 | 46.6M | else |
276 | 46.6M | { |
277 | 46.6M | set_warning( "Emulation error (illegal/unsupported instruction)" ); |
278 | 46.6M | debug_printf( "Bad opcode $%.2x at $%.4x\n", |
279 | 0 | (int) *cpu::get_code( cpu::r.pc ), (int) cpu::r.pc ); |
280 | 46.6M | cpu::r.pc = (cpu::r.pc + 1) & 0xFFFF; |
281 | 46.6M | cpu_time += 6; |
282 | 46.6M | } |
283 | 46.7M | } |
284 | 47.0M | } |
285 | | |
286 | 384k | duration = cpu_time; |
287 | 384k | next_play -= cpu_time; |
288 | 384k | if ( next_play < 0 ) // could go negative if routine is taking too long to return |
289 | 372k | next_play = 0; |
290 | 384k | apu.end_frame( cpu_time ); |
291 | | |
292 | 384k | return 0; |
293 | 384k | } |