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