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