1 /* loader.c: loader detection
2 Copyright (c) 2006 Philip Kendall
3
4 $Id: loader.c 3941 2009-01-09 22:38:21Z pak21 $
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 Author contact information:
21
22 E-mail: philip-fuse@shadowmagic.org.uk
23
24 */
25
26 #include <config.h>
27
28 #include "event.h"
29 #include "loader.h"
30 #include "memory.h"
31 #include "settings.h"
32 #include "spectrum.h"
33 #include "tape.h"
34 #include "z80/z80.h"
35
36 static int successive_reads = 0;
37 static libspectrum_signed_dword last_tstates_read = -100000;
38 static libspectrum_byte last_b_read = 0x00;
39 static int length_known1 = 0, length_known2 = 0;
40 static int length_long1 = 0, length_long2 = 0;
41
42 typedef enum acceleration_mode_t {
43 ACCELERATION_MODE_NONE = 0,
44 ACCELERATION_MODE_INCREASING,
45 ACCELERATION_MODE_DECREASING,
46 } acceleration_mode_t;
47
48 static acceleration_mode_t acceleration_mode;
49 static size_t acceleration_pc;
50
51 void
loader_frame(libspectrum_dword frame_length)52 loader_frame( libspectrum_dword frame_length )
53 {
54 if( last_tstates_read > -100000 ) {
55 last_tstates_read -= frame_length;
56 }
57 }
58
59 void
loader_tape_play(void)60 loader_tape_play( void )
61 {
62 successive_reads = 0;
63 acceleration_mode = ACCELERATION_MODE_NONE;
64 }
65
66 void
loader_tape_stop(void)67 loader_tape_stop( void )
68 {
69 successive_reads = 0;
70 acceleration_mode = ACCELERATION_MODE_NONE;
71 }
72
73 static void
do_acceleration(void)74 do_acceleration( void )
75 {
76 if( length_known1 ) {
77 int set_b_high = length_long1;
78 set_b_high ^= ( acceleration_mode == ACCELERATION_MODE_DECREASING );
79 if( set_b_high ) {
80 z80.bc.b.h = 0xfe;
81 } else {
82 z80.bc.b.h = 0x00;
83 }
84 z80.af.b.l |= 0x01;
85 z80.pc.b.l = readbyte_internal( z80.sp.w ); z80.sp.w++;
86 z80.pc.b.h = readbyte_internal( z80.sp.w ); z80.sp.w++;
87
88 event_remove_type( tape_edge_event );
89 tape_next_edge( tstates, 0, NULL );
90
91 successive_reads = 0;
92 }
93
94 length_known1 = length_known2;
95 length_long1 = length_long2;
96 }
97
98 static acceleration_mode_t
acceleration_detector(libspectrum_word pc)99 acceleration_detector( libspectrum_word pc )
100 {
101 int state = 0, count = 0;
102 while( 1 ) {
103 libspectrum_byte b = readbyte_internal( pc ); pc++; count++;
104 switch( state ) {
105 case 0:
106 switch( b ) {
107 case 0x04: state = 1; break; /* INC B - Many loaders */
108 default: state = 13; break; /* Possible Digital Integration */
109 }
110 break;
111 case 1:
112 switch( b ) {
113 case 0xc8: state = 2; break; /* RET Z */
114 default: return ACCELERATION_MODE_NONE;
115 }
116 break;
117 case 2:
118 switch( b ) {
119 case 0x3e: state = 3; break; /* LD A,nn */
120 default: return ACCELERATION_MODE_NONE;
121 }
122 break;
123 case 3:
124 switch( b ) {
125 case 0x00: /* Search Loader */
126 case 0x7f: /* ROM loader and variants */
127 state = 4; break; /* Data byte */
128 default: return ACCELERATION_MODE_NONE;
129 }
130 break;
131 case 4:
132 switch( b ) {
133 case 0xdb: state = 5; break; /* IN A,(nn) */
134 default: return ACCELERATION_MODE_NONE;
135 }
136 break;
137 case 5:
138 switch( b ) {
139 case 0xfe: state = 6; break; /* Data byte */
140 default: return ACCELERATION_MODE_NONE;
141 }
142 break;
143 case 6:
144 switch( b ) {
145 case 0x1f: state = 7; break; /* RRA */
146 case 0xa9: state = 24; break; /* XOR C - Search Loader */
147 default: return ACCELERATION_MODE_NONE;
148 }
149 break;
150 case 7:
151 switch( b ) {
152 case 0x00: /* NOP - Bleepload */
153 case 0xa7: /* AND A - Microsphere */
154 case 0xc8: /* RET Z - Paul Owens */
155 case 0xd0: /* RET NC - ROM loader */
156 state = 8; break;
157 case 0xa9: state = 9; break; /* XOR C - Speedlock */
158 default: return ACCELERATION_MODE_NONE;
159 }
160 break;
161 case 8:
162 switch( b ) {
163 case 0xa9: state = 9; break; /* XOR C */
164 default: return ACCELERATION_MODE_NONE;
165 }
166 break;
167 case 9:
168 switch( b ) {
169 case 0xe6: state = 10; break; /* AND nn */
170 default: return ACCELERATION_MODE_NONE;
171 }
172 break;
173 case 10:
174 switch( b ) {
175 case 0x20: state = 11; break; /* Data byte */
176 default: return ACCELERATION_MODE_NONE;
177 }
178 break;
179 case 11:
180 switch( b ) {
181 case 0x28: state = 12; break; /* JR nn */
182 default: return ACCELERATION_MODE_NONE;
183 }
184 break;
185 case 12:
186 if( b == 0x100 - count ) {
187 return ACCELERATION_MODE_INCREASING;
188 } else {
189 return ACCELERATION_MODE_NONE;
190 }
191 break;
192
193 /* Digital Integration loader */
194
195 case 13:
196 state = 14; break; /* Possible Digital Integration */
197 case 14:
198 switch( b ) {
199 case 0x05: state = 15; break; /* DEC B - Digital Integration */
200 default: return ACCELERATION_MODE_NONE;
201 }
202 break;
203 case 15:
204 switch( b ) {
205 case 0xc8: state = 16; break; /* RET Z */
206 default: return ACCELERATION_MODE_NONE;
207 }
208 break;
209 case 16:
210 switch( b ) {
211 case 0xdb: state = 17; break; /* IN A,(nn) */
212 default: return ACCELERATION_MODE_NONE;
213 }
214 break;
215 case 17:
216 switch( b ) {
217 case 0xfe: state = 18; break; /* Data byte */
218 default: return ACCELERATION_MODE_NONE;
219 }
220 break;
221 case 18:
222 switch( b ) {
223 case 0xa9: state = 19; break; /* XOR C */
224 default: return ACCELERATION_MODE_NONE;
225 }
226 break;
227 case 19:
228 switch( b ) {
229 case 0xe6: state = 20; break; /* AND nn */
230 default: return ACCELERATION_MODE_NONE;
231 }
232 break;
233 case 20:
234 switch( b ) {
235 case 0x40: state = 21; break; /* Data byte */
236 default: return ACCELERATION_MODE_NONE;
237 }
238 break;
239 case 21:
240 switch( b ) {
241 case 0xca: state = 22; break; /* JP Z,nnnn */
242 default: return ACCELERATION_MODE_NONE;
243 }
244 break;
245 case 22: /* LSB of jump target */
246 if( b == ( z80.pc.w - 4 ) % 0x100 ) {
247 state = 23;
248 } else {
249 return ACCELERATION_MODE_NONE;
250 }
251 break;
252 case 23: /* MSB of jump target */
253 if( b == ( z80.pc.w - 4 ) / 0x100 ) {
254 return ACCELERATION_MODE_DECREASING;
255 } else {
256 return ACCELERATION_MODE_NONE;
257 }
258
259 /* Search loader */
260
261 case 24:
262 switch( b ) {
263 case 0xe6: state = 25; break; /* AND nn */
264 default: return ACCELERATION_MODE_NONE;
265 }
266 break;
267 case 25:
268 switch( b ) {
269 case 0x40: state = 26; break; /* Data byte */
270 default: return ACCELERATION_MODE_NONE;
271 }
272 break;
273 case 26:
274 switch( b ) {
275 case 0xd8: state = 27; break; /* RET C */
276 default: return ACCELERATION_MODE_NONE;
277 }
278 break;
279 case 27:
280 switch( b ) {
281 case 0x00: state = 11; break; /* NOP */
282 default: return ACCELERATION_MODE_NONE;
283 }
284 break;
285
286 default:
287 /* Can't happen */
288 break;
289 }
290 }
291
292 }
293
294 static void
check_for_acceleration(void)295 check_for_acceleration( void )
296 {
297 /* If the IN occured at a different location to the one we're
298 accelerating, stop acceleration */
299 if( acceleration_mode && z80.pc.w != acceleration_pc )
300 acceleration_mode = ACCELERATION_MODE_NONE;
301
302 /* If we're not accelerating, check if this is a loader */
303 if( !acceleration_mode ) {
304 acceleration_mode = acceleration_detector( z80.pc.w - 6 );
305 acceleration_pc = z80.pc.w;
306 }
307
308 if( acceleration_mode ) do_acceleration();
309 }
310
311 void
loader_detect_loader(void)312 loader_detect_loader( void )
313 {
314 libspectrum_dword tstates_diff = tstates - last_tstates_read;
315 libspectrum_byte b_diff = z80.bc.b.h - last_b_read;
316
317 last_tstates_read = tstates;
318 last_b_read = z80.bc.b.h;
319
320 if( settings_current.detect_loader ) {
321
322 if( tape_is_playing() ) {
323 if( tstates_diff > 1000 || ( b_diff != 1 && b_diff != 0 &&
324 b_diff != 0xff ) ) {
325 successive_reads++;
326 if( successive_reads >= 2 ) {
327 tape_stop();
328 }
329 } else {
330 successive_reads = 0;
331 }
332 } else {
333 if( tstates_diff <= 500 && ( b_diff == 1 || b_diff == 0xff ) ) {
334 successive_reads++;
335 if( successive_reads >= 10 ) {
336 tape_do_play( 1 );
337 }
338 } else {
339 successive_reads = 0;
340 }
341 }
342
343 } else {
344
345 successive_reads = 0;
346
347 }
348
349 if( settings_current.accelerate_loader && tape_is_playing() )
350 check_for_acceleration();
351
352 }
353
354 void
loader_set_acceleration_flags(int flags)355 loader_set_acceleration_flags( int flags )
356 {
357 if( flags & LIBSPECTRUM_TAPE_FLAGS_LENGTH_SHORT ) {
358 length_known2 = 1;
359 length_long2 = 0;
360 } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LENGTH_LONG ) {
361 length_known2 = 1;
362 length_long2 = 1;
363 } else {
364 length_known2 = 0;
365 }
366 }
367