1 /* loader.c: loader detection
2    Copyright (c) 2006 Philip Kendall
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation, Inc.,
16    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18    Author contact information:
19 
20    E-mail: philip-fuse@shadowmagic.org.uk
21 
22 */
23 
24 #include <config.h>
25 
26 #include "event.h"
27 #include "loader.h"
28 #include "memory_pages.h"
29 #include "rzx.h"
30 #include "settings.h"
31 #include "spectrum.h"
32 #include "tape.h"
33 #include "z80/z80.h"
34 
35 static int successive_reads = 0;
36 static libspectrum_signed_dword last_tstates_read = -100000;
37 static libspectrum_byte last_b_read = 0x00;
38 static int length_known1 = 0, length_known2 = 0;
39 static int length_long1 = 0, length_long2 = 0;
40 
41 typedef enum acceleration_mode_t {
42   ACCELERATION_MODE_NONE = 0,
43   ACCELERATION_MODE_INCREASING,
44   ACCELERATION_MODE_DECREASING,
45 } acceleration_mode_t;
46 
47 static acceleration_mode_t acceleration_mode;
48 static size_t acceleration_pc;
49 
50 void
loader_frame(libspectrum_dword frame_length)51 loader_frame( libspectrum_dword frame_length )
52 {
53   if( last_tstates_read > -100000 ) {
54     last_tstates_read -= frame_length;
55   }
56 }
57 
58 void
loader_tape_play(void)59 loader_tape_play( void )
60 {
61   successive_reads = 0;
62   acceleration_mode = ACCELERATION_MODE_NONE;
63 }
64 
65 void
loader_tape_stop(void)66 loader_tape_stop( void )
67 {
68   successive_reads = 0;
69   acceleration_mode = ACCELERATION_MODE_NONE;
70 }
71 
72 static void
do_acceleration(void)73 do_acceleration( void )
74 {
75   if( length_known1 ) {
76     /* B is used to indicate the length of the pulses */
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 
85     /* Bit 5 of C is used to indicate the current microphone level */
86     z80.bc.b.l = (z80.bc.b.l & ~0x20) | (tape_microphone ? 0x00 : 0x20);
87 
88     z80.af.b.l |= 0x01;
89 
90     /* Simulate the RET at the end of the edge-finding loop */
91     z80.pc.b.l = readbyte_internal( z80.sp.w ); z80.sp.w++;
92     z80.pc.b.h = readbyte_internal( z80.sp.w ); z80.sp.w++;
93 
94     event_remove_type( tape_edge_event );
95     tape_next_edge( tstates, 1 );
96 
97     successive_reads = 0;
98   }
99 
100   length_known1 = length_known2;
101   length_long1 = length_long2;
102 }
103 
104 static acceleration_mode_t
acceleration_detector(libspectrum_word pc)105 acceleration_detector( libspectrum_word pc )
106 {
107   int state = 0, count = 0;
108   while( 1 ) {
109     libspectrum_byte b = readbyte_internal( pc ); pc++; count++;
110     switch( state ) {
111     case 0:
112       switch( b ) {
113       case 0x03: state = 28; break;     /* Data byte of JR NZ, ... - Alkatraz */
114       case 0x04: state = 1; break;	/* INC B - Many loaders */
115       default: state = 13; break;	/* Possible Digital Integration */
116       }
117       break;
118     case 1:
119       switch( b ) {
120       case 0x20: state = 40; break;     /* JR NZ - variant Alkatraz */
121       case 0xc8: state = 2; break;	/* RET Z */
122       default: return ACCELERATION_MODE_NONE;
123       }
124       break;
125     case 2:
126       switch( b ) {
127       case 0x3e: state = 3; break;	/* LD A,nn */
128       default: return ACCELERATION_MODE_NONE;
129       }
130       break;
131     case 3:
132       switch( b ) {
133       case 0x00:			/* Search Loader */
134       case 0x7f:			/* ROM loader and variants */
135       case 0xff:                        /* Dinaload */
136 	state = 4; break;		/* Data byte */
137       default: return ACCELERATION_MODE_NONE;
138       }
139       break;
140     case 4:
141       switch( b ) {
142       case 0xdb: state = 5; break;	/* IN A,(nn) */
143       default: return ACCELERATION_MODE_NONE;
144       }
145       break;
146     case 5:
147       switch( b ) {
148       case 0xfe: state = 6; break;	/* Data byte */
149       default: return ACCELERATION_MODE_NONE;
150       }
151       break;
152     case 6:
153       switch( b ) {
154       case 0x1f: state = 7; break;	/* RRA */
155       case 0xa9: state = 24; break;	/* XOR C - Search Loader */
156       default: return ACCELERATION_MODE_NONE;
157       }
158       break;
159     case 7:
160       switch( b ) {
161       case 0x00:			/* NOP - Bleepload */
162       case 0xa7:			/* AND A - Microsphere */
163       case 0xc8:			/* RET Z - Paul Owens */
164       case 0xd0:			/* RET NC - ROM loader */
165 	state = 8; break;
166       case 0xa9: state = 9; break;	/* XOR C - Speedlock */
167       default: return ACCELERATION_MODE_NONE;
168       }
169       break;
170     case 8:
171       switch( b ) {
172       case 0xa9: state = 9; break;	/* XOR C */
173       default: return ACCELERATION_MODE_NONE;
174       }
175       break;
176     case 9:
177       switch( b ) {
178       case 0xe6: state = 10; break;	/* AND nn */
179       default: return ACCELERATION_MODE_NONE;
180       }
181       break;
182     case 10:
183       switch( b ) {
184       case 0x20: state = 11; break;	/* Data byte */
185       default: return ACCELERATION_MODE_NONE;
186       }
187       break;
188     case 11:
189       switch( b ) {
190       case 0x28: state = 12; break;	/* JR nn */
191       default: return ACCELERATION_MODE_NONE;
192       }
193       break;
194     case 12:
195       if( b == 0x100 - count ) {
196 	return ACCELERATION_MODE_INCREASING;
197       } else {
198 	return ACCELERATION_MODE_NONE;
199       }
200       break;
201 
202       /* Digital Integration loader */
203 
204     case 13:
205       state = 14; break;		/* Possible Digital Integration */
206     case 14:
207       switch( b ) {
208       case 0x05: state = 15; break;	/* DEC B - Digital Integration */
209       default: return ACCELERATION_MODE_NONE;
210       }
211       break;
212     case 15:
213       switch( b ) {
214       case 0xc8: state = 16; break;	/* RET Z */
215       default: return ACCELERATION_MODE_NONE;
216       }
217       break;
218     case 16:
219       switch( b ) {
220       case 0xdb: state = 17; break;	/* IN A,(nn) */
221       default: return ACCELERATION_MODE_NONE;
222       }
223       break;
224     case 17:
225       switch( b ) {
226       case 0xfe: state = 18; break;	/* Data byte */
227       default: return ACCELERATION_MODE_NONE;
228       }
229       break;
230     case 18:
231       switch( b ) {
232       case 0xa9: state = 19; break;	/* XOR C */
233       default: return ACCELERATION_MODE_NONE;
234       }
235       break;
236     case 19:
237       switch( b ) {
238       case 0xe6: state = 20; break;	/* AND nn */
239       default: return ACCELERATION_MODE_NONE;
240       }
241       break;
242     case 20:
243       switch( b ) {
244       case 0x40: state = 21; break;	/* Data byte */
245       default: return ACCELERATION_MODE_NONE;
246       }
247       break;
248     case 21:
249       switch( b ) {
250       case 0xca: state = 22; break;	/* JP Z,nnnn */
251       default: return ACCELERATION_MODE_NONE;
252       }
253       break;
254     case 22:				/* LSB of jump target */
255       if( b == ( z80.pc.w - 4 ) % 0x100 ) {
256 	state = 23;
257       } else {
258 	return ACCELERATION_MODE_NONE;
259       }
260       break;
261     case 23:				/* MSB of jump target */
262       if( b == ( z80.pc.w - 4 ) / 0x100 ) {
263 	return ACCELERATION_MODE_DECREASING;
264       } else {
265 	return ACCELERATION_MODE_NONE;
266       }
267 
268       /* Search loader */
269 
270     case 24:
271       switch( b ) {
272       case 0xe6: state = 25; break;	/* AND nn */
273       default: return ACCELERATION_MODE_NONE;
274       }
275       break;
276     case 25:
277       switch( b ) {
278       case 0x40: state = 26; break;	/* Data byte */
279       default: return ACCELERATION_MODE_NONE;
280       }
281       break;
282     case 26:
283       switch( b ) {
284       case 0x28: state = 12; break;     /* JR Z - Space Crusade */
285       case 0xd8: state = 27; break;	/* RET C */
286       default: return ACCELERATION_MODE_NONE;
287       }
288       break;
289     case 27:
290       switch( b ) {
291       case 0x00: state = 11; break;	/* NOP */
292       default: return ACCELERATION_MODE_NONE;
293       }
294       break;
295 
296     /* Alkatraz */
297 
298     case 28:
299       switch( b ) {
300       case 0xc3: state = 29; break;     /* JP nnnn */
301       default: return ACCELERATION_MODE_NONE;
302       }
303       break;
304     case 29:
305       state = 30; break;                /* First data byte of JP */
306     case 30:
307       state = 31; break;                /* Second data byte of JP */
308     case 31:
309       switch( b ) {
310       case 0xdb: state = 32; break;	/* IN A,(nn) */
311       default: return ACCELERATION_MODE_NONE;
312       }
313       break;
314     case 32:
315       switch( b ) {
316       case 0xfe: state = 33; break;	/* Data byte */
317       default: return ACCELERATION_MODE_NONE;
318       }
319       break;
320     case 33:
321       switch( b ) {
322       case 0x1f: state = 34; break;	/* RRA */
323       default: return ACCELERATION_MODE_NONE;
324       }
325       break;
326     case 34:
327       switch( b ) {
328       case 0xc8: state = 35; break;	/* RET Z */
329       default: return ACCELERATION_MODE_NONE;
330       }
331       break;
332     case 35:
333       switch( b ) {
334       case 0xa9: state = 36; break;	/* XOR C */
335       default: return ACCELERATION_MODE_NONE;
336       }
337       break;
338     case 36:
339       switch( b ) {
340       case 0xe6: state = 37; break;	/* AND nn */
341       default: return ACCELERATION_MODE_NONE;
342       }
343       break;
344     case 37:
345       switch( b ) {
346       case 0x20: state = 38; break;	/* Data byte */
347       default: return ACCELERATION_MODE_NONE;
348       }
349       break;
350     case 38:
351       switch( b ) {
352       case 0x28: state = 39; break;	/* JR Z,nn */
353       default: return ACCELERATION_MODE_NONE;
354       }
355       break;
356     case 39:
357       switch( b ) {
358       case 0xf1:                        /* Normal data byte */
359       case 0xf3:                        /* Variant data byte */
360         return ACCELERATION_MODE_INCREASING;
361       default: return ACCELERATION_MODE_NONE;
362       }
363       break;
364 
365     /* "Variant" Alkatraz */
366 
367     case 40:
368       switch( b ) {
369       case 0x01: state = 41; break;     /* Data byte of JR NZ */
370       default: return ACCELERATION_MODE_NONE;
371       }
372       break;
373     case 41:
374       switch( b ) {
375       case 0xc9: state = 31; break;     /* RET */
376       default: return ACCELERATION_MODE_NONE;
377       }
378       break;
379 
380     default:
381       /* Can't happen */
382       break;
383     }
384   }
385 
386 }
387 
388 static void
check_for_acceleration(void)389 check_for_acceleration( void )
390 {
391   /* If the IN occured at a different location to the one we're
392      accelerating, stop acceleration */
393   if( acceleration_mode && z80.pc.w != acceleration_pc )
394     acceleration_mode = ACCELERATION_MODE_NONE;
395 
396   /* If we're not accelerating, check if this is a loader */
397   if( !acceleration_mode ) {
398     acceleration_mode = acceleration_detector( z80.pc.w - 6 );
399     acceleration_pc = z80.pc.w;
400   }
401 
402   if( acceleration_mode ) do_acceleration();
403 }
404 
405 void
loader_detect_loader(void)406 loader_detect_loader( void )
407 {
408   libspectrum_dword tstates_diff = tstates - last_tstates_read;
409   libspectrum_byte b_diff = z80.bc.b.h - last_b_read;
410 
411   last_tstates_read = tstates;
412   last_b_read = z80.bc.b.h;
413 
414   if( settings_current.detect_loader ) {
415 
416     if( tape_is_playing() ) {
417       if( tstates_diff > 1000 || ( b_diff != 1 && b_diff != 0 &&
418 				   b_diff != 0xff ) ) {
419 	successive_reads++;
420 	if( successive_reads >= 2 ) {
421 	  tape_stop();
422 	}
423       } else {
424 	successive_reads = 0;
425       }
426     } else {
427       if( tstates_diff <= 500 && ( b_diff == 1 || b_diff == 0xff ) ) {
428 	successive_reads++;
429 	if( successive_reads >= 10 ) {
430 	  tape_do_play( 1 );
431 	}
432       } else {
433 	successive_reads = 0;
434       }
435     }
436 
437   } else {
438 
439     successive_reads = 0;
440 
441   }
442 
443   if( settings_current.accelerate_loader && tape_is_playing() &&
444       !rzx_recording )
445     check_for_acceleration();
446 
447 }
448 
449 void
loader_set_acceleration_flags(int flags,int from_acceleration)450 loader_set_acceleration_flags( int flags, int from_acceleration )
451 {
452   if( flags & LIBSPECTRUM_TAPE_FLAGS_LENGTH_SHORT ) {
453     length_known2 = 1;
454     length_long2 = 0;
455   } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LENGTH_LONG ) {
456     length_known2 = 1;
457     length_long2 = 1;
458   } else {
459     length_known2 = 0;
460   }
461 
462   /* If this tape edge occurred due to normal timings rather than
463      our tape acceleration, turn off acceleration for the next edge
464      or we miss an edge. See [bugs:#387] for more details */
465   if( !from_acceleration ) {
466     length_known1 = 0;
467   }
468 }
469