1 /* tape.c: tape handling routines
2 Copyright (c) 1999-2011 Philip Kendall, Darren Salt, Witold Filipczyk
3
4 $Id: tape.c 4863 2013-01-27 11:28:00Z fredm $
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 <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <libspectrum.h>
34
35 #include "debugger/debugger.h"
36 #include "event.h"
37 #include "fuse.h"
38 #include "loader.h"
39 #include "machine.h"
40 #include "memory.h"
41 #include "peripherals/ula.h"
42 #include "settings.h"
43 #include "sound.h"
44 #include "settings.h"
45 #include "snapshot.h"
46 #include "tape.h"
47 #include "timer/timer.h"
48 #include "ui/ui.h"
49 #include "utils.h"
50 #include "z80/z80.h"
51 #include "z80/z80_macros.h"
52
53 /* The current tape */
54 static libspectrum_tape *tape;
55
56 /* Has the current tape been modified since it was last loaded/saved? */
57 int tape_modified;
58
59 /* Is the emulated tape deck playing? */
60 int tape_playing;
61
62 /* Was the tape playing started automatically? */
63 static int tape_autoplay;
64
65 /* Is there a high input to the EAR socket? */
66 int tape_microphone;
67
68 /* Debugger events */
69 static const char *event_type_string = "tape";
70
71 static const char *play_event_detail_string = "play",
72 *stop_event_detail_string = "stop";
73 static int play_event, stop_event = -1;
74
75 /* Spectrum events */
76 int tape_edge_event;
77 static int record_event;
78
79 /* Function prototypes */
80
81 static int tape_autoload( libspectrum_machine hardware );
82 static int trap_load_block( libspectrum_tape_block *block );
83 static int tape_play( int autoplay );
84 static int trap_check_rom( void );
85 static void make_name( unsigned char *name, const unsigned char *data );
86 static void
87 tape_event_record_sample( libspectrum_dword last_tstates, int type,
88 void *user_data );
89
90 /* Function definitions */
91
92 void
tape_init(void)93 tape_init( void )
94 {
95 tape = libspectrum_tape_alloc();
96
97 play_event = debugger_event_register( event_type_string,
98 play_event_detail_string );
99 stop_event = debugger_event_register( event_type_string,
100 stop_event_detail_string );
101
102 tape_edge_event = event_register( tape_next_edge, "Tape edge" );
103 record_event = event_register( tape_event_record_sample,
104 "Tape sample record" );
105
106 tape_modified = 0;
107
108 /* Don't call tape_stop() here as the UI hasn't been initialised yet,
109 so we can't update the statusbar */
110 tape_playing = 0;
111 tape_microphone = 0;
112 }
113
114 void
tape_end(void)115 tape_end( void )
116 {
117 libspectrum_tape_free( tape );
118 tape = NULL;
119 }
120
tape_open(const char * filename,int autoload)121 int tape_open( const char *filename, int autoload )
122 {
123 utils_file file;
124 int error;
125
126 error = utils_read_file( filename, &file );
127 if( error ) return error;
128
129 error = tape_read_buffer( file.buffer, file.length, LIBSPECTRUM_ID_UNKNOWN,
130 filename, autoload );
131 if( error ) { utils_close_file( &file ); return error; }
132
133 utils_close_file( &file );
134
135 return 0;
136 }
137
138 /* Use an already open tape file as the current tape */
139 int
tape_read_buffer(unsigned char * buffer,size_t length,libspectrum_id_t type,const char * filename,int autoload)140 tape_read_buffer( unsigned char *buffer, size_t length, libspectrum_id_t type,
141 const char *filename, int autoload )
142 {
143 int error;
144
145 if( libspectrum_tape_present( tape ) ) {
146 error = tape_close(); if( error ) return error;
147 }
148
149 error = libspectrum_tape_read( tape, buffer, length, type, filename );
150 if( error ) return error;
151
152 tape_modified = 0;
153 ui_tape_browser_update( UI_TAPE_BROWSER_NEW_TAPE, NULL );
154
155 if( autoload ) {
156 error = tape_autoload( machine_current->machine );
157 if( error ) return error;
158 }
159
160 return 0;
161 }
162
163 /* Load a snap to start the current tape autoloading */
164 static int
tape_autoload(libspectrum_machine hardware)165 tape_autoload( libspectrum_machine hardware )
166 {
167 int error; const char *id;
168 char filename[80];
169 utils_file snap;
170 libspectrum_id_t type;
171
172 id = machine_get_id( hardware );
173 if( !id ) {
174 ui_error( UI_ERROR_ERROR, "Unknown machine type %d!", hardware );
175 return 1;
176 }
177
178 /* Look for an autoload snap. Try .szx first, then .z80 */
179 type = LIBSPECTRUM_ID_SNAPSHOT_SZX;
180 snprintf( filename, sizeof(filename), "tape_%s.szx", id );
181 error = utils_read_auxiliary_file( filename, &snap, UTILS_AUXILIARY_LIB );
182 if( error == -1 ) {
183 type = LIBSPECTRUM_ID_SNAPSHOT_Z80;
184 snprintf( filename, sizeof(filename), "tape_%s.z80", id );
185 error = utils_read_auxiliary_file( filename, &snap, UTILS_AUXILIARY_LIB );
186 }
187
188 /* If we couldn't find either, give up */
189 if( error == -1 ) {
190 ui_error( UI_ERROR_ERROR,
191 "Couldn't find autoload snap for machine type '%s'", id );
192 return 1;
193 }
194 if( error ) return error;
195
196 error = snapshot_read_buffer( snap.buffer, snap.length, type );
197 if( error ) { utils_close_file( &snap ); return error; }
198
199 utils_close_file( &snap );
200
201 return 0;
202 }
203
204 /* Close the active tape file */
tape_close(void)205 int tape_close( void )
206 {
207 int error;
208 ui_confirm_save_t confirm;
209
210 /* If the tape has been modified, check if we want to do this */
211 if( tape_modified ) {
212
213 confirm =
214 ui_confirm_save( "Tape has been modified.\nDo you want to save it?" );
215 switch( confirm ) {
216
217 case UI_CONFIRM_SAVE_SAVE:
218 error = ui_tape_write(); if( error ) return error;
219 break;
220
221 case UI_CONFIRM_SAVE_DONTSAVE: break;
222 case UI_CONFIRM_SAVE_CANCEL: return 1;
223
224 }
225 }
226
227 /* Stop the tape if it's currently playing */
228 if( tape_playing ) {
229 error = tape_stop();
230 if( error ) return error;
231 }
232
233 /* And then remove it from memory */
234 error = libspectrum_tape_clear( tape );
235 if( error ) return error;
236
237 tape_modified = 0;
238 ui_tape_browser_update( UI_TAPE_BROWSER_NEW_TAPE, NULL );
239
240 return 0;
241 }
242
243 /* Select the nth block on the tape; 0 => 1st block */
244 int
tape_select_block(size_t n)245 tape_select_block( size_t n )
246 {
247 int error;
248
249 error = tape_select_block_no_update( n ); if( error ) return error;
250
251 ui_tape_browser_update( UI_TAPE_BROWSER_SELECT_BLOCK, NULL );
252
253 return 0;
254 }
255
256 /* The same, but without updating the browser display */
257 int
tape_select_block_no_update(size_t n)258 tape_select_block_no_update( size_t n )
259 {
260 return libspectrum_tape_nth_block( tape, n );
261 }
262
263 /* Which block is current? */
264 int
tape_get_current_block(void)265 tape_get_current_block( void )
266 {
267 int n;
268 libspectrum_error error;
269
270 if( !libspectrum_tape_present( tape ) ) return -1;
271
272 error = libspectrum_tape_position( &n, tape );
273 if( error ) return -1;
274
275 return n;
276 }
277
278 /* Write the current in-memory tape file out to disk */
tape_write(const char * filename)279 int tape_write( const char* filename )
280 {
281 libspectrum_id_t type;
282 libspectrum_class_t class;
283 libspectrum_byte *buffer; size_t length;
284
285 int error;
286
287 /* Work out what sort of file we want from the filename; default to
288 .tzx if we couldn't guess */
289 error = libspectrum_identify_file_with_class( &type, &class, filename, NULL,
290 0 );
291 if( error ) return error;
292
293 if( class != LIBSPECTRUM_CLASS_TAPE || type == LIBSPECTRUM_ID_UNKNOWN )
294 type = LIBSPECTRUM_ID_TAPE_TZX;
295
296 length = 0;
297
298 error = libspectrum_tape_write( &buffer, &length, tape, type );
299 if( error != LIBSPECTRUM_ERROR_NONE ) return error;
300
301 error = utils_write_file( filename, buffer, length );
302 if( error ) { libspectrum_free( buffer ); return error; }
303
304 tape_modified = 0;
305 ui_tape_browser_update( UI_TAPE_BROWSER_MODIFIED, NULL );
306
307 libspectrum_free( buffer );
308
309 return 0;
310
311 }
312
tape_can_autoload(void)313 int tape_can_autoload( void )
314 {
315 return( settings_current.auto_load && !memory_custom_rom() );
316 }
317
318 /* Load the next tape block into memory; returns 0 if a block was
319 loaded (even if it had an tape loading error or equivalent) or
320 non-zero if there was an error at the emulator level, or tape traps
321 are not active */
tape_load_trap(void)322 int tape_load_trap( void )
323 {
324 libspectrum_tape_block *block, *next_block;
325 int error;
326
327 /* Do nothing if tape traps aren't active, or the tape is already playing */
328 if( !settings_current.tape_traps || tape_playing ) return 2;
329
330 /* Do nothing if we're not in the correct ROM */
331 if( ! trap_check_rom() ) return 3;
332
333 /* Return with error if no tape file loaded */
334 if( !libspectrum_tape_present( tape ) ) return 1;
335
336 block = libspectrum_tape_current_block( tape );
337
338 /* Skip over any meta-data blocks */
339 while( libspectrum_tape_block_metadata( block ) ) {
340 block = libspectrum_tape_select_next_block( tape );
341 if( !block ) return 1;
342 }
343
344 /* If this block isn't a ROM loader, start the block playing. After
345 that, return with `error' so that we actually do whichever
346 instruction it was that caused the trap to hit */
347 if( libspectrum_tape_block_type( block ) != LIBSPECTRUM_TAPE_BLOCK_ROM ||
348 libspectrum_tape_state( tape ) != LIBSPECTRUM_TAPE_STATE_PILOT ) {
349 tape_play( 1 );
350 return -1;
351 }
352
353 /* We don't properly handle the case of partial loading, so don't run
354 the traps in that situation */
355 if( libspectrum_tape_block_data_length( block ) != DE + 2 ) {
356 tape_play( 1 );
357 return -1;
358 }
359
360 /* All returns made via the RET at #05E2, except on Timex 2068 at #0136 */
361 if ( machine_current->machine == LIBSPECTRUM_MACHINE_TC2068 ||
362 machine_current->machine == LIBSPECTRUM_MACHINE_TS2068 ) {
363 PC = 0x0136;
364 } else {
365 PC = 0x05e2;
366 }
367
368 error = trap_load_block( block );
369 if( error ) return error;
370
371 /* Peek at the next block. If it's a ROM block, move along, initialise
372 the block, and return */
373 next_block = libspectrum_tape_peek_next_block( tape );
374
375 if( libspectrum_tape_block_type(next_block) == LIBSPECTRUM_TAPE_BLOCK_ROM ) {
376
377 next_block = libspectrum_tape_select_next_block( tape );
378 if( !next_block ) return 1;
379
380 ui_tape_browser_update( UI_TAPE_BROWSER_SELECT_BLOCK, NULL );
381
382 return 0;
383 }
384
385 /* If the next block isn't a ROM block, set ourselves up such that the
386 next thing to occur is the pause at the end of the current block */
387 libspectrum_tape_set_state( tape, LIBSPECTRUM_TAPE_STATE_PAUSE );
388
389 return 0;
390 }
391
392 static int
trap_load_block(libspectrum_tape_block * block)393 trap_load_block( libspectrum_tape_block *block )
394 {
395 libspectrum_byte parity, *data;
396 int i = 0, length, read;
397
398 /* On exit:
399 * A = calculated parity byte if parity checked, else 0 (CHECKME)
400 * F : if parity checked, all flags are modified
401 * else carry only is modified (FIXME)
402 * B = 0xB0 (success) or 0x00 (failure)
403 * C = 0x01 (confirmed), 0x21, 0xFE or 0xDE (CHECKME)
404 * DE : decremented by number of bytes loaded or verified
405 * H = calculated parity byte or undefined
406 * L = last byte read, or 1 if none
407 * IX : incremented by number of bytes loaded or verified
408 * A' = unchanged on error + no flag byte, else 0x01
409 * F' = 0x01 on error + no flag byte, else 0x45
410 * R = no point in altering it :-)
411 * Other registers unchanged.
412 */
413
414 data = libspectrum_tape_block_data( block );
415 length = libspectrum_tape_block_data_length( block );
416
417 /* Number of bytes to load or verify */
418 read = length - 1;
419 if( read > DE )
420 read = DE;
421
422 /* If there's no data in the block (!), set L then error exit.
423 * We don't need to alter H, IX or DE here */
424 if( !length ) {
425 L = F_ = 1;
426 F &= ~FLAG_C;
427 return 0;
428 }
429
430 i = A_; /* i = A' (flag byte) */
431 AF_ = 0x0145;
432 A = 0;
433
434 /* Initialise the parity check and L to the block ID byte */
435 L = parity = *data++;
436
437 /* If the block ID byte != the flag byte, clear carry and return */
438 if( parity != i )
439 goto error_ret;
440
441 /* Now set L to the *last* byte in the block */
442 L = data[read - 1];
443
444 /* Loading or verifying determined by the carry flag of F' */
445 if( F_ & FLAG_C ) {
446 for( i = 0; i < read; i++ ) {
447 parity ^= data[i];
448 writebyte_internal( IX+i, data[i] );
449 }
450 } else { /* verifying */
451 for( i = 0; i < read; i++ ) {
452 parity ^= data[i];
453 if( data[i] != readbyte_internal(IX+i) ) {
454 /* Verification failure */
455 L = data[i];
456 goto error_ret;
457 }
458 }
459 }
460
461 /* At this point, i == number of bytes actually read or verified */
462
463 /* If |DE| bytes have been read and there's more data, do the parity check */
464 if( DE == i && read + 1 < length ) {
465 parity ^= data[read];
466 A = parity;
467 CP( 1 ); /* parity check is successful if A==0 */
468 B = 0xB0;
469 } else {
470 /* Failure to read first bit of the next byte (ref. 48K ROM, 0x5EC) */
471 B = 255;
472 L = 1;
473 INC( B );
474 error_ret:
475 F &= ~FLAG_C;
476 }
477
478 /* At this point, AF, AF', B and L are already modified */
479 C = 1;
480 H = parity;
481 DE -= i;
482 IX += i;
483 return 0;
484 }
485
486 /* Append to the current tape file in memory; returns 0 if a block was
487 saved or non-zero if there was an error at the emulator level, or tape
488 traps are not active */
tape_save_trap(void)489 int tape_save_trap( void )
490 {
491 libspectrum_tape_block *block;
492 libspectrum_byte parity, *data;
493 size_t length;
494
495 int i;
496
497 /* Do nothing if tape traps aren't active */
498 if( !settings_current.tape_traps || tape_recording ) return 2;
499
500 /* Check we're in the right ROM */
501 if( ! trap_check_rom() ) return 3;
502
503 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_ROM );
504
505 /* The +2 here is for the flag and parity bytes */
506 length = DE + 2;
507 libspectrum_tape_block_set_data_length( block, length );
508
509 data = libspectrum_malloc( length * sizeof(libspectrum_byte) );
510 libspectrum_tape_block_set_data( block, data );
511
512 /* First, store the flag byte (and initialise the parity counter) */
513 data[0] = parity = A;
514
515 /* then the main body of the data, counting parity along the way */
516 for( i=0; i<DE; i++) {
517 libspectrum_byte b = readbyte_internal( IX+i );
518 parity ^= b;
519 data[i+1] = b;
520 }
521
522 /* And finally the parity byte */
523 data[ DE+1 ] = parity;
524
525 /* Give a 1 second pause after this block */
526 libspectrum_tape_block_set_pause( block, 1000 );
527
528 libspectrum_tape_append_block( tape, block );
529
530 tape_modified = 1;
531 ui_tape_browser_update( UI_TAPE_BROWSER_NEW_BLOCK, block );
532
533 /* And then return via the RET at #053E, except on Timex 2068 at #00E4 */
534 if ( machine_current->machine == LIBSPECTRUM_MACHINE_TC2068 ||
535 machine_current->machine == LIBSPECTRUM_MACHINE_TS2068 ) {
536 PC = 0x00e4;
537 } else {
538 PC = 0x053e;
539 }
540
541 return 0;
542
543 }
544
545 /* Check whether we're actually in the right ROM when a tape trap hit */
546 static int
trap_check_rom(void)547 trap_check_rom( void )
548 {
549 if( plusd_available && plusd_active )
550 return 0; /* +D must not be active */
551
552 if( disciple_available && disciple_active )
553 return 0; /* DISCiPLE must not be active */
554
555 if( opus_available && opus_active )
556 return 0; /* Opus must not be active */
557
558 if( memory_custom_rom() )
559 return 0; /* and we can't be using a custom ROM */
560
561 switch( machine_current->machine ) {
562 case LIBSPECTRUM_MACHINE_16:
563 case LIBSPECTRUM_MACHINE_48:
564 case LIBSPECTRUM_MACHINE_48_NTSC:
565 case LIBSPECTRUM_MACHINE_SE:
566 case LIBSPECTRUM_MACHINE_TC2048:
567 return 1; /* Always OK here */
568
569 case LIBSPECTRUM_MACHINE_TC2068:
570 case LIBSPECTRUM_MACHINE_TS2068:
571 /* OK if we're in the EXROM (location of the tape routines) */
572 return( memory_map_read[0].source == memory_source_exrom );
573
574 case LIBSPECTRUM_MACHINE_128:
575 case LIBSPECTRUM_MACHINE_PLUS2:
576 /* OK if we're in ROM 1 */
577 return( machine_current->ram.current_rom == 1 );
578
579 case LIBSPECTRUM_MACHINE_PLUS2A:
580 case LIBSPECTRUM_MACHINE_PLUS3:
581 case LIBSPECTRUM_MACHINE_PLUS3E:
582 /* OK if we're not in a 64Kb RAM configuration and we're in
583 ROM 3 */
584 return( ! machine_current->ram.special &&
585 machine_current->ram.current_rom == 3 );
586
587 case LIBSPECTRUM_MACHINE_128E:
588 /* OK if we're not in a 64Kb RAM configuration and we're in
589 either ROM 1 or ROM 3 (which are the same) */
590 return( ! machine_current->ram.special &&
591 ( machine_current->ram.current_rom == 1 ||
592 machine_current->ram.current_rom == 3 ));
593
594 case LIBSPECTRUM_MACHINE_PENT:
595 case LIBSPECTRUM_MACHINE_PENT512:
596 case LIBSPECTRUM_MACHINE_PENT1024:
597 case LIBSPECTRUM_MACHINE_SCORP:
598 /* OK if we're in ROM 1 and the Beta disk interface is not active */
599 return( machine_current->ram.current_rom == 1 && !beta_active );
600
601 case LIBSPECTRUM_MACHINE_UNKNOWN: /* should never happen */
602 ui_error( UI_ERROR_ERROR,
603 "trap_check_rom: machine type is LIBSPECTRUM_MACHINE_UNKNOWN" );
604 fuse_abort();
605
606 }
607
608 ui_error( UI_ERROR_ERROR, "trap_check_rom: unknown machine type %d",
609 machine_current->machine );
610 fuse_abort();
611 }
612
613 static int
tape_play(int autoplay)614 tape_play( int autoplay )
615 {
616 if( !libspectrum_tape_present( tape ) ) return 1;
617
618 /* Otherwise, start the tape going */
619 tape_playing = 1;
620 tape_autoplay = autoplay;
621 tape_microphone = 0;
622
623 /* Update the status bar */
624 ui_statusbar_update( UI_STATUSBAR_ITEM_TAPE, UI_STATUSBAR_STATE_ACTIVE );
625
626 /* If we're fastloading, turn sound off */
627 if( settings_current.fastload ) sound_pause();
628
629 loader_tape_play();
630
631 tape_next_edge( tstates, 0, NULL );
632
633 debugger_event( play_event );
634
635 return 0;
636 }
637
638 int
tape_do_play(int autoplay)639 tape_do_play( int autoplay )
640 {
641 if( !tape_playing ) {
642 return tape_play( autoplay );
643 } else {
644 return 0;
645 }
646 }
647
tape_toggle_play(int autoplay)648 int tape_toggle_play( int autoplay )
649 {
650 if( tape_playing ) {
651 return tape_stop();
652 } else {
653 return tape_play( autoplay );
654 }
655 }
656
tape_stop(void)657 int tape_stop( void )
658 {
659 if( tape_playing ) {
660
661 tape_playing = 0;
662 ui_statusbar_update( UI_STATUSBAR_ITEM_TAPE, UI_STATUSBAR_STATE_INACTIVE );
663 loader_tape_stop();
664
665 /* If we were fastloading, sound was off, so turn it back on, and
666 reset the speed counter */
667 if( settings_current.fastload ) {
668 sound_unpause();
669 timer_estimate_reset();
670 }
671
672 event_remove_type( tape_edge_event );
673 }
674
675 if( stop_event != -1 ) debugger_event( stop_event );
676
677 return 0;
678 }
679
680 int
tape_is_playing(void)681 tape_is_playing( void )
682 {
683 return tape_playing;
684 }
685
686 int
tape_present(void)687 tape_present( void )
688 {
689 return libspectrum_tape_present( tape );
690 }
691
692 typedef struct
693 {
694 libspectrum_byte *tape_buffer;
695 libspectrum_dword tape_buffer_size;
696 libspectrum_dword tape_buffer_used;
697 int tstates_per_sample;
698 int last_level;
699 int last_level_count;
700 } tape_rec_state;
701
702 int tape_recording = 0;
703
704 static tape_rec_state rec_state;
705
706 void
tape_record_start(void)707 tape_record_start( void )
708 {
709 /* sample rate will be 44.1KHz */
710 rec_state.tstates_per_sample =
711 machine_current->timings.processor_speed/44100;
712
713 rec_state.tape_buffer_size = 8192;
714 rec_state.tape_buffer = libspectrum_malloc(rec_state.tape_buffer_size);
715 rec_state.tape_buffer_used = 0;
716
717 /* start scheduling events that record into a buffer that we
718 start allocating here */
719 event_add( tstates + rec_state.tstates_per_sample, record_event );
720
721 rec_state.last_level = ula_tape_level();
722 rec_state.last_level_count = 1;
723
724 tape_recording = 1;
725
726 /* Also want to disable other tape actions */
727 ui_menu_activate( UI_MENU_ITEM_TAPE_RECORDING, 1 );
728 }
729
730 static int
write_rec_buffer(libspectrum_byte * tape_buffer,libspectrum_dword tape_buffer_used,int last_level_count)731 write_rec_buffer( libspectrum_byte *tape_buffer,
732 libspectrum_dword tape_buffer_used,
733 int last_level_count )
734 {
735 if( last_level_count <= 0xff ) {
736 tape_buffer[ tape_buffer_used++ ] = last_level_count;
737 } else {
738 tape_buffer[ tape_buffer_used++ ] = 0;
739 tape_buffer[ tape_buffer_used++ ] = ( last_level_count & 0x000000ff ) ;
740 tape_buffer[ tape_buffer_used++ ] = ( last_level_count & 0x0000ff00 ) >> 8;
741 tape_buffer[ tape_buffer_used++ ] = ( last_level_count & 0x00ff0000 ) >> 16;
742 tape_buffer[ tape_buffer_used++ ] = ( last_level_count & 0xff000000 ) >> 24;
743 }
744
745 return tape_buffer_used;
746 }
747
748 void
tape_event_record_sample(libspectrum_dword last_tstates,int type,void * user_data)749 tape_event_record_sample( libspectrum_dword last_tstates, int type,
750 void *user_data )
751 {
752 if( rec_state.last_level != (ula_tape_level()) ) {
753 /* put a sample into the recording buffer */
754 rec_state.tape_buffer_used =
755 write_rec_buffer( rec_state.tape_buffer,
756 rec_state.tape_buffer_used,
757 rec_state.last_level_count );
758
759 rec_state.last_level_count = 0;
760 rec_state.last_level = ula_tape_level();
761 /* make sure we can still fit a dword and a flag byte in the buffer */
762 if( rec_state.tape_buffer_used+5 >= rec_state.tape_buffer_size ) {
763 rec_state.tape_buffer_size = rec_state.tape_buffer_size*2;
764 rec_state.tape_buffer = libspectrum_realloc( rec_state.tape_buffer,
765 rec_state.tape_buffer_size );
766 }
767 }
768
769 rec_state.last_level_count++;
770
771 /* schedule next timer */
772 event_add( last_tstates + rec_state.tstates_per_sample, record_event );
773 }
774
775 int
tape_record_stop(void)776 tape_record_stop( void )
777 {
778 libspectrum_tape_block* block;
779
780 /* put last sample into the recording buffer */
781 rec_state.tape_buffer_used = write_rec_buffer( rec_state.tape_buffer,
782 rec_state.tape_buffer_used,
783 rec_state.last_level_count );
784
785 /* stop scheduling events and turn buffer into a block and
786 pop into the current tape */
787 event_remove_type( record_event );
788
789 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE );
790
791 libspectrum_tape_block_set_scale( block, rec_state.tstates_per_sample );
792 libspectrum_tape_block_set_data_length( block, rec_state.tape_buffer_used );
793 libspectrum_tape_block_set_data( block, rec_state.tape_buffer );
794
795 libspectrum_tape_append_block( tape, block );
796
797 rec_state.tape_buffer = NULL;
798 rec_state.tape_buffer_size = 0;
799 rec_state.tape_buffer_used = 0;
800
801 tape_modified = 1;
802 ui_tape_browser_update( UI_TAPE_BROWSER_NEW_BLOCK, block );
803
804 tape_recording = 0;
805
806 /* Also want to reenable other tape actions */
807 ui_menu_activate( UI_MENU_ITEM_TAPE_RECORDING, 0 );
808
809 return 0;
810 }
811
812 void
tape_next_edge(libspectrum_dword last_tstates,int type,void * user_data)813 tape_next_edge( libspectrum_dword last_tstates, int type, void *user_data )
814 {
815 libspectrum_error libspec_error;
816 libspectrum_tape_block *block;
817
818 libspectrum_dword edge_tstates;
819 int flags;
820
821 /* If the tape's not playing, just return */
822 if( ! tape_playing ) return;
823
824 /* Get the time until the next edge */
825 libspec_error = libspectrum_tape_get_next_edge( &edge_tstates, &flags,
826 tape );
827 if( libspec_error != LIBSPECTRUM_ERROR_NONE ) return;
828
829 /* Invert the microphone state */
830 if( edge_tstates ||
831 ( flags & ( LIBSPECTRUM_TAPE_FLAGS_STOP |
832 LIBSPECTRUM_TAPE_FLAGS_LEVEL_LOW |
833 LIBSPECTRUM_TAPE_FLAGS_LEVEL_HIGH ) ) ) {
834
835 if( flags & LIBSPECTRUM_TAPE_FLAGS_NO_EDGE ) {
836 /* Do nothing */
837 } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LEVEL_LOW ) {
838 tape_microphone = 0;
839 } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LEVEL_HIGH ) {
840 tape_microphone = 1;
841 } else {
842 tape_microphone = !tape_microphone;
843 }
844 }
845
846 /* If we've been requested to stop the tape, do so and then
847 return without stacking another edge */
848 if( ( flags & LIBSPECTRUM_TAPE_FLAGS_STOP ) ||
849 ( ( flags & LIBSPECTRUM_TAPE_FLAGS_STOP48 ) &&
850 ( !( libspectrum_machine_capabilities( machine_current->machine ) &
851 LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY
852 )
853 )
854 )
855 )
856 {
857 tape_stop();
858 return;
859 }
860
861 /* If that was the end of a block, update the browser */
862 if( flags & LIBSPECTRUM_TAPE_FLAGS_BLOCK ) {
863
864 ui_tape_browser_update( UI_TAPE_BROWSER_SELECT_BLOCK, NULL );
865
866 /* If the tape was started automatically, tape traps are active
867 and the new block is a ROM loader, stop the tape and return
868 without putting another event into the queue */
869 block = libspectrum_tape_current_block( tape );
870 if( tape_autoplay && settings_current.tape_traps &&
871 libspectrum_tape_block_type( block ) == LIBSPECTRUM_TAPE_BLOCK_ROM
872 ) {
873 tape_stop();
874 return;
875 }
876 }
877
878 /* Otherwise, put this into the event queue; remember that this edge
879 should occur 'edge_tstates' after the last edge, not after the
880 current time (these will be slightly different as we only process
881 events between instructions). */
882 event_add( last_tstates + edge_tstates, tape_edge_event );
883
884 /* Store length flags for acceleration purposes */
885 loader_set_acceleration_flags( flags );
886 }
887
888 /* Call a user-supplied function for every block in the current tape */
889 int
tape_foreach(void (* function)(libspectrum_tape_block * block,void * user_data),void * user_data)890 tape_foreach( void (*function)( libspectrum_tape_block *block,
891 void *user_data),
892 void *user_data )
893 {
894 libspectrum_tape_block *block;
895 libspectrum_tape_iterator iterator;
896
897 for( block = libspectrum_tape_iterator_init( &iterator, tape );
898 block;
899 block = libspectrum_tape_iterator_next( &iterator ) )
900 function( block, user_data );
901
902 return 0;
903 }
904
905 int
tape_block_details(char * buffer,size_t length,libspectrum_tape_block * block)906 tape_block_details( char *buffer, size_t length,
907 libspectrum_tape_block *block )
908 {
909 libspectrum_byte *data;
910 const char *type; unsigned char name[11];
911 int offset;
912
913 buffer[0] = '\0';
914
915 switch( libspectrum_tape_block_type( block ) ) {
916
917 case LIBSPECTRUM_TAPE_BLOCK_ROM:
918 /* See if this looks like a standard Spectrum header and if so
919 display some extra data */
920 if( libspectrum_tape_block_data_length( block ) != 19 ) goto normal;
921
922 data = libspectrum_tape_block_data( block );
923
924 /* Flag byte is 0x00 for headers */
925 if( data[0] != 0x00 ) goto normal;
926
927 switch( data[1] ) {
928 case 0x00: type = "Program"; break;
929 case 0x01: type = "Number array"; break;
930 case 0x02: type = "Character array"; break;
931 case 0x03: type = "Bytes"; break;
932 default: goto normal;
933 }
934
935 make_name( name, &data[2] );
936
937 snprintf( buffer, length, "%s: \"%s\"", type, name );
938
939 break;
940
941 normal:
942 snprintf( buffer, length, "%lu bytes",
943 (unsigned long)libspectrum_tape_block_data_length( block ) );
944 break;
945
946 case LIBSPECTRUM_TAPE_BLOCK_TURBO:
947 case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA:
948 case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA:
949 case LIBSPECTRUM_TAPE_BLOCK_DATA_BLOCK:
950 snprintf( buffer, length, "%lu bytes",
951 (unsigned long)libspectrum_tape_block_data_length( block ) );
952 break;
953
954 case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE:
955 snprintf( buffer, length, "%lu tstates",
956 (unsigned long)libspectrum_tape_block_pulse_length( block ) );
957 break;
958
959 case LIBSPECTRUM_TAPE_BLOCK_PULSES:
960 case LIBSPECTRUM_TAPE_BLOCK_PULSE_SEQUENCE:
961 snprintf( buffer, length, "%lu pulses",
962 (unsigned long)libspectrum_tape_block_count( block ) );
963 break;
964
965 case LIBSPECTRUM_TAPE_BLOCK_PAUSE:
966 snprintf( buffer, length, "%lu ms",
967 (unsigned long)libspectrum_tape_block_pause( block ) );
968 break;
969
970 case LIBSPECTRUM_TAPE_BLOCK_GROUP_START:
971 case LIBSPECTRUM_TAPE_BLOCK_COMMENT:
972 case LIBSPECTRUM_TAPE_BLOCK_MESSAGE:
973 case LIBSPECTRUM_TAPE_BLOCK_CUSTOM:
974 snprintf( buffer, length, "%s", libspectrum_tape_block_text( block ) );
975 break;
976
977 case LIBSPECTRUM_TAPE_BLOCK_JUMP:
978 offset = libspectrum_tape_block_offset( block );
979 if( offset > 0 ) {
980 snprintf( buffer, length, "Forward %d blocks", offset );
981 } else {
982 snprintf( buffer, length, "Backward %d blocks", -offset );
983 }
984 break;
985
986 case LIBSPECTRUM_TAPE_BLOCK_LOOP_START:
987 snprintf( buffer, length, "%lu iterations",
988 (unsigned long)libspectrum_tape_block_count( block ) );
989 break;
990
991 case LIBSPECTRUM_TAPE_BLOCK_SELECT:
992 snprintf( buffer, length, "%lu options",
993 (unsigned long)libspectrum_tape_block_count( block ) );
994 break;
995
996 case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA:
997 snprintf( buffer, length, "%lu data symbols",
998 (unsigned long)libspectrum_tape_generalised_data_symbol_table_symbols_in_block( libspectrum_tape_block_data_table( block ) ) );
999 break;
1000
1001 case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE:
1002 /* Could do something better with this one */
1003 break;
1004
1005 case LIBSPECTRUM_TAPE_BLOCK_GROUP_END:
1006 case LIBSPECTRUM_TAPE_BLOCK_LOOP_END:
1007 case LIBSPECTRUM_TAPE_BLOCK_STOP48:
1008 case LIBSPECTRUM_TAPE_BLOCK_SET_SIGNAL_LEVEL:
1009 case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO:
1010 case LIBSPECTRUM_TAPE_BLOCK_HARDWARE:
1011 case LIBSPECTRUM_TAPE_BLOCK_CONCAT:
1012 break;
1013
1014 }
1015
1016 return 0;
1017 }
1018
1019 static void
make_name(unsigned char * name,const unsigned char * data)1020 make_name( unsigned char *name, const unsigned char *data )
1021 {
1022 size_t i;
1023
1024 for( i = 0; i < 10; i++, name++, data++ ) {
1025 if( *data >= 32 && *data < 127 ) {
1026 *name = *data;
1027 } else {
1028 *name = '?';
1029 }
1030 }
1031
1032 *name = '\0';
1033 }
1034