1 /* opus.c: Routines for handling the Opus Discovery interface
2 Copyright (c) 1999-2011 Stuart Brady, Fredrick Meunier, Philip Kendall,
3 Dmitry Sanarin, Darren Salt, Michael D Wynne, Gergely Szasz
4
5 $Id: opus.c 4926 2013-05-05 07:58:18Z sbaldovi $
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 Author contact information:
22
23 Philip: philip-fuse@shadowmagic.org.uk
24
25 Stuart: stuart.brady@gmail.com
26
27 */
28
29 #include <config.h>
30
31 #include <libspectrum.h>
32
33 #include <string.h>
34
35 #include "compat.h"
36 #include "machine.h"
37 #include "module.h"
38 #include "opus.h"
39 #include "peripherals/printer.h"
40 #include "settings.h"
41 #include "ui/ui.h"
42 #include "unittests/unittests.h"
43 #include "utils.h"
44 #include "wd_fdc.h"
45 #include "options.h" /* needed for get combo options */
46 #include "z80/z80.h"
47
48 #define DISK_TRY_MERGE(heads) ( option_enumerate_diskoptions_disk_try_merge() == 2 || \
49 ( option_enumerate_diskoptions_disk_try_merge() == 1 && heads == 1 ) )
50
51 /* FIXME: this is wrong. Opus has only 2 Kb of RAM, but we can't handle
52 anything less than our page size */
53 #define OPUS_RAM_SIZE 0x1000
54
55 #define OPUS_RAM_PAGES ( OPUS_RAM_SIZE / MEMORY_PAGE_SIZE \
56 ? OPUS_RAM_SIZE / MEMORY_PAGE_SIZE : 1 )
57
58 #define TRUE_OPUS_RAM_SIZE 0x800
59
60 static int opus_rom_memory_source, opus_ram_memory_source;
61
62 /* Two memory chunks accessible by the Z80 when /ROMCS is low */
63 static memory_page opus_memory_map_romcs_rom[ MEMORY_PAGES_IN_8K ];
64 static memory_page opus_memory_map_romcs_ram[ OPUS_RAM_PAGES ];
65
66 int opus_available = 0;
67 int opus_active = 0;
68
69 static int opus_index_pulse;
70
71 static int index_event;
72
73 #define OPUS_NUM_DRIVES 2
74
75 static wd_fdc *opus_fdc;
76 static wd_fdc_drive opus_drives[ OPUS_NUM_DRIVES ];
77
78 static libspectrum_byte opus_ram[ OPUS_RAM_SIZE ];
79
80 /* 6821 PIA internal registers */
81 static libspectrum_byte data_reg_a, data_dir_a, control_a;
82 static libspectrum_byte data_reg_b, data_dir_b, control_b;
83
84 static void opus_reset( int hard_reset );
85 static void opus_memory_map( void );
86 static void opus_enabled_snapshot( libspectrum_snap *snap );
87 static void opus_from_snapshot( libspectrum_snap *snap );
88 static void opus_to_snapshot( libspectrum_snap *snap );
89 static void opus_event_index( libspectrum_dword last_tstates, int type,
90 void *user_data );
91
92 static module_info_t opus_module_info = {
93
94 opus_reset,
95 opus_memory_map,
96 opus_enabled_snapshot,
97 opus_from_snapshot,
98 opus_to_snapshot,
99
100 };
101
102 static const periph_t opus_periph = {
103 &settings_current.opus,
104 NULL,
105 1,
106 NULL
107 };
108
109 void
opus_page(void)110 opus_page( void )
111 {
112 opus_active = 1;
113 machine_current->ram.romcs = 1;
114 machine_current->memory_map();
115 }
116
117 void
opus_unpage(void)118 opus_unpage( void )
119 {
120 opus_active = 0;
121 machine_current->ram.romcs = 0;
122 machine_current->memory_map();
123 }
124
125 static void
opus_memory_map(void)126 opus_memory_map( void )
127 {
128 if( !opus_active ) return;
129
130 memory_map_romcs_8k( 0x0000, opus_memory_map_romcs_rom );
131 memory_map_romcs_4k( 0x2000, opus_memory_map_romcs_ram );
132 }
133
134 static void
opus_set_datarq(struct wd_fdc * f)135 opus_set_datarq( struct wd_fdc *f )
136 {
137 event_add( 0, z80_nmi_event );
138 }
139
140 void
opus_init(void)141 opus_init( void )
142 {
143 int i;
144 wd_fdc_drive *d;
145
146 opus_fdc = wd_fdc_alloc_fdc( WD1770, 0, WD_FLAG_OPUS );
147
148 for( i = 0; i < OPUS_NUM_DRIVES; i++ ) {
149 d = &opus_drives[ i ];
150 fdd_init( &d->fdd, FDD_SHUGART, NULL, 0 ); /* drive geometry 'autodetect' */
151 d->disk.flag = DISK_FLAG_NONE;
152 }
153
154 opus_fdc->current_drive = &opus_drives[ 0 ];
155 fdd_select( &opus_drives[ 0 ].fdd, 1 );
156 opus_fdc->dden = 1;
157 opus_fdc->set_intrq = NULL;
158 opus_fdc->reset_intrq = NULL;
159 opus_fdc->set_datarq = opus_set_datarq;
160 opus_fdc->reset_datarq = NULL;
161 opus_fdc->iface = NULL;
162
163 index_event = event_register( opus_event_index, "Opus index" );
164
165 module_register( &opus_module_info );
166
167 opus_rom_memory_source = memory_source_register( "Opus ROM" );
168 opus_ram_memory_source = memory_source_register( "Opus RAM" );
169 for( i = 0; i < MEMORY_PAGES_IN_8K; i++ )
170 opus_memory_map_romcs_rom[i].source = opus_rom_memory_source;
171 for( i = 0; i < OPUS_RAM_PAGES; i++ )
172 opus_memory_map_romcs_ram[i].source = opus_ram_memory_source;
173
174 periph_register( PERIPH_TYPE_OPUS, &opus_periph );
175 }
176
177 static void
opus_reset(int hard_reset)178 opus_reset( int hard_reset )
179 {
180 int i;
181 wd_fdc_drive *d;
182 const fdd_params_t *dt;
183
184 opus_active = 0;
185 opus_available = 0;
186
187 event_remove_type( index_event );
188
189 if( !periph_is_active( PERIPH_TYPE_OPUS ) ) {
190 return;
191 }
192
193 if( machine_load_rom_bank( opus_memory_map_romcs_rom, 0,
194 settings_current.rom_opus,
195 settings_default.rom_opus, 0x2000 ) ) {
196 settings_current.opus = 0;
197 periph_activate_type( PERIPH_TYPE_OPUS, 0 );
198 return;
199 }
200
201 for( i = 0; i < OPUS_RAM_PAGES; i++ ) {
202 struct memory_page *page =
203 &opus_memory_map_romcs_ram[ i ];
204 page->page = opus_ram + i * MEMORY_PAGE_SIZE;
205 page->offset = i * MEMORY_PAGE_SIZE;
206 }
207
208 machine_current->ram.romcs = 0;
209
210 for( i = 0; i < OPUS_RAM_PAGES; i++ )
211 opus_memory_map_romcs_ram[ i ].writable = 1;
212
213 data_reg_a = 0;
214 data_dir_a = 0;
215 control_a = 0;
216 data_reg_b = 0;
217 data_dir_b = 0;
218 control_b = 0;
219
220 opus_available = 1;
221 opus_index_pulse = 0;
222
223 if( hard_reset )
224 memset( opus_ram, 0, TRUE_OPUS_RAM_SIZE );
225
226 wd_fdc_master_reset( opus_fdc );
227
228 for( i = 0; i < OPUS_NUM_DRIVES; i++ ) {
229 d = &opus_drives[ i ];
230
231 d->index_pulse = 0;
232 d->index_interrupt = 0;
233 }
234
235 /* We can eject disks only if they are currently present */
236 dt = &fdd_params[ option_enumerate_diskoptions_drive_opus1_type() + 1 ]; /* +1 => there is no `Disabled' */
237 fdd_init( &opus_drives[ OPUS_DRIVE_1 ].fdd, FDD_SHUGART, dt, 1 );
238 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1, dt->enabled );
239 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_EJECT,
240 opus_drives[ OPUS_DRIVE_1 ].fdd.loaded );
241 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_FLIP_SET,
242 !opus_drives[ OPUS_DRIVE_1 ].fdd.upsidedown );
243 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_WP_SET,
244 !opus_drives[ OPUS_DRIVE_1 ].fdd.wrprot );
245
246
247 dt = &fdd_params[ option_enumerate_diskoptions_drive_opus2_type() ];
248 fdd_init( &opus_drives[ OPUS_DRIVE_2 ].fdd, dt->enabled ? FDD_SHUGART : FDD_TYPE_NONE, dt, 1 );
249 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2, dt->enabled );
250 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_EJECT,
251 opus_drives[ OPUS_DRIVE_2 ].fdd.loaded );
252 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_FLIP_SET,
253 !opus_drives[ OPUS_DRIVE_2 ].fdd.upsidedown );
254 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_WP_SET,
255 !opus_drives[ OPUS_DRIVE_2 ].fdd.wrprot );
256
257
258 opus_fdc->current_drive = &opus_drives[ 0 ];
259 fdd_select( &opus_drives[ 0 ].fdd, 1 );
260 machine_current->memory_map();
261 opus_event_index( 0, index_event, NULL );
262
263 ui_statusbar_update( UI_STATUSBAR_ITEM_DISK, UI_STATUSBAR_STATE_INACTIVE );
264 }
265
266 void
opus_end(void)267 opus_end( void )
268 {
269 opus_available = 0;
270 free( opus_fdc );
271 }
272
273 /*
274 * opus_6821_access( reg, data, dir )
275 *
276 * reg - register to access:
277 *
278 * data - if dir = 1 the value being written else ignored
279 *
280 * dir - direction of data. 0 = read, 1 = write
281 *
282 * returns: value of register if dir = 0 else 0
283 *
284 * Mostly borrowed from EightyOne - A Windows ZX80/81/clone emulator
285 */
286
287 static libspectrum_byte
opus_6821_access(libspectrum_byte reg,libspectrum_byte data,libspectrum_byte dir)288 opus_6821_access( libspectrum_byte reg, libspectrum_byte data,
289 libspectrum_byte dir )
290 {
291 int drive, side;
292 int i;
293
294 switch( reg & 0x03 ) {
295 case 0:
296 if( dir ) {
297 if( control_a & 0x04 ) {
298 data_reg_a = data;
299
300 drive = ( data & 0x02 ) == 2 ? 1 : 0;
301 side = ( data & 0x10 )>>4 ? 1 : 0;
302
303 for( i = 0; i < OPUS_NUM_DRIVES; i++ ) {
304 fdd_set_head( &opus_drives[ i ].fdd, side );
305 }
306
307 fdd_select( &opus_drives[ (!drive) ].fdd, 0 );
308 fdd_select( &opus_drives[ drive ].fdd, 1 );
309
310 if( opus_fdc->current_drive != &opus_drives[ drive ] ) {
311 if( opus_fdc->current_drive->fdd.motoron ) { /* swap motoron */
312 fdd_motoron( &opus_drives[ (!drive) ].fdd, 0 );
313 fdd_motoron( &opus_drives[ drive ].fdd, 1 );
314 }
315 opus_fdc->current_drive = &opus_drives[ drive ];
316 }
317 } else {
318 data_dir_a = data;
319 }
320 } else {
321 if( control_a & 0x04 ) {
322 /* printer never busy (bit 6) */
323 data_reg_a &= ~0x40;
324 return data_reg_a;
325 } else {
326 return data_dir_a;
327 }
328 }
329 break;
330 case 1:
331 if( dir ) {
332 control_a = data;
333 } else {
334 /* Always return bit 6 set to ACK parallel port actions */
335 return control_a | 0x40;
336 }
337 break;
338 case 2:
339 if( dir ) {
340 if( control_b & 0x04 ) {
341 data_reg_b = data;
342 printer_parallel_write( 0x00, data );
343 /* Don't worry about emulating the strobes from the ROM, they are
344 all bound up with checking current printer busy status which we
345 don't emulate, so just send the char now */
346 printer_parallel_strobe_write( 0 );
347 printer_parallel_strobe_write( 1 );
348 printer_parallel_strobe_write( 0 );
349 } else {
350 data_dir_b = data;
351 }
352 } else {
353 if( control_b & 0x04 ) {
354 return data_reg_b;
355 } else {
356 return data_dir_b;
357 }
358 }
359 break;
360 case 3:
361 if( dir ) {
362 control_b = data;
363 } else {
364 return control_b;
365 }
366 break;
367 }
368
369 return 0;
370 }
371
372 int
opus_disk_insert(opus_drive_number which,const char * filename,int autoload)373 opus_disk_insert( opus_drive_number which, const char *filename,
374 int autoload )
375 {
376 int error;
377 wd_fdc_drive *d;
378 const fdd_params_t *dt;
379
380 if( which >= OPUS_NUM_DRIVES ) {
381 ui_error( UI_ERROR_ERROR, "opus_disk_insert: unknown drive %d",
382 which );
383 fuse_abort();
384 }
385
386 d = &opus_drives[ which ];
387
388 /* Eject any disk already in the drive */
389 if( d->fdd.loaded ) {
390 /* Abort the insert if we want to keep the current disk */
391 if( opus_disk_eject( which ) ) return 0;
392 }
393
394 if( filename ) {
395 error = disk_open( &d->disk, filename, 0, DISK_TRY_MERGE( d->fdd.fdd_heads ) );
396 if( error != DISK_OK ) {
397 ui_error( UI_ERROR_ERROR, "Failed to open disk image: %s",
398 disk_strerror( error ) );
399 return 1;
400 }
401 } else {
402 switch( which ) {
403 case 0:
404 /* +1 => there is no `Disabled' */
405 dt = &fdd_params[ option_enumerate_diskoptions_drive_opus1_type() + 1 ];
406 break;
407 case 1:
408 default:
409 dt = &fdd_params[ option_enumerate_diskoptions_drive_opus2_type() ];
410 break;
411 }
412 error = disk_new( &d->disk, dt->heads, dt->cylinders, DISK_DENS_AUTO, DISK_UDI );
413 if( error != DISK_OK ) {
414 ui_error( UI_ERROR_ERROR, "Failed to create disk image: %s",
415 disk_strerror( error ) );
416 return 1;
417 }
418 }
419
420 fdd_load( &d->fdd, &d->disk, 0 );
421
422 /* Set the 'eject' item active */
423 switch( which ) {
424 case OPUS_DRIVE_1:
425 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_EJECT, 1 );
426 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_FLIP_SET,
427 !opus_drives[ OPUS_DRIVE_1 ].fdd.upsidedown );
428 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_WP_SET,
429 !opus_drives[ OPUS_DRIVE_1 ].fdd.wrprot );
430 break;
431 case OPUS_DRIVE_2:
432 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_EJECT, 1 );
433 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_FLIP_SET,
434 !opus_drives[ OPUS_DRIVE_2 ].fdd.upsidedown );
435 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_WP_SET,
436 !opus_drives[ OPUS_DRIVE_2 ].fdd.wrprot );
437 break;
438 }
439
440 if( filename && autoload ) {
441 /* XXX */
442 }
443
444 return 0;
445 }
446
447 int
opus_disk_eject(opus_drive_number which)448 opus_disk_eject( opus_drive_number which )
449 {
450 wd_fdc_drive *d;
451
452 if( which >= OPUS_NUM_DRIVES )
453 return 1;
454
455 d = &opus_drives[ which ];
456
457 if( d->disk.type == DISK_TYPE_NONE )
458 return 0;
459
460 if( d->disk.dirty ) {
461
462 ui_confirm_save_t confirm = ui_confirm_save(
463 "Disk in Opus Discovery drive %c has been modified.\n"
464 "Do you want to save it?",
465 which == OPUS_DRIVE_1 ? '1' : '2'
466 );
467
468 switch( confirm ) {
469
470 case UI_CONFIRM_SAVE_SAVE:
471 if( opus_disk_save( which, 0 ) ) return 1; /* first save */
472 break;
473
474 case UI_CONFIRM_SAVE_DONTSAVE: break;
475 case UI_CONFIRM_SAVE_CANCEL: return 1;
476
477 }
478 }
479
480 fdd_unload( &d->fdd );
481 disk_close( &d->disk );
482
483 /* Set the 'eject' item inactive */
484 switch( which ) {
485 case OPUS_DRIVE_1:
486 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_EJECT, 0 );
487 break;
488 case OPUS_DRIVE_2:
489 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_EJECT, 0 );
490 break;
491 }
492 return 0;
493 }
494
495 int
opus_disk_save(opus_drive_number which,int saveas)496 opus_disk_save( opus_drive_number which, int saveas )
497 {
498 wd_fdc_drive *d;
499
500 if( which >= OPUS_NUM_DRIVES )
501 return 1;
502
503 d = &opus_drives[ which ];
504
505 if( d->disk.type == DISK_TYPE_NONE )
506 return 0;
507
508 if( d->disk.filename == NULL ) saveas = 1;
509 if( ui_opus_disk_write( which, saveas ) ) return 1;
510 d->disk.dirty = 0;
511 return 0;
512 }
513
514 int
opus_disk_flip(opus_drive_number which,int flip)515 opus_disk_flip( opus_drive_number which, int flip )
516 {
517 wd_fdc_drive *d;
518
519 if( which >= OPUS_NUM_DRIVES )
520 return 1;
521
522 d = &opus_drives[ which ];
523
524 if( !d->fdd.loaded )
525 return 1;
526
527 fdd_flip( &d->fdd, flip );
528
529 /* Update the 'write flip' menu item */
530 switch( which ) {
531 case OPUS_DRIVE_1:
532 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_FLIP_SET,
533 !opus_drives[ OPUS_DRIVE_1 ].fdd.upsidedown );
534 break;
535 case OPUS_DRIVE_2:
536 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_FLIP_SET,
537 !opus_drives[ OPUS_DRIVE_2 ].fdd.upsidedown );
538 break;
539 }
540 return 0;
541 }
542
543 int
opus_disk_writeprotect(opus_drive_number which,int wrprot)544 opus_disk_writeprotect( opus_drive_number which, int wrprot )
545 {
546 wd_fdc_drive *d;
547
548 if( which >= OPUS_NUM_DRIVES )
549 return 1;
550
551 d = &opus_drives[ which ];
552
553 if( !d->fdd.loaded )
554 return 1;
555
556 fdd_wrprot( &d->fdd, wrprot );
557
558 /* Update the 'write protect' menu item */
559 switch( which ) {
560 case OPUS_DRIVE_1:
561 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_1_WP_SET,
562 !opus_drives[ OPUS_DRIVE_1 ].fdd.wrprot );
563 break;
564 case OPUS_DRIVE_2:
565 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_OPUS_2_WP_SET,
566 !opus_drives[ OPUS_DRIVE_2 ].fdd.wrprot );
567 break;
568 }
569 return 0;
570 }
571
572 int
opus_disk_write(opus_drive_number which,const char * filename)573 opus_disk_write( opus_drive_number which, const char *filename )
574 {
575 wd_fdc_drive *d = &opus_drives[ which ];
576 int error;
577
578 d->disk.type = DISK_TYPE_NONE;
579 if( filename == NULL ) filename = d->disk.filename; /* write over original file */
580 error = disk_write( &d->disk, filename );
581
582 if( error != DISK_OK ) {
583 ui_error( UI_ERROR_ERROR, "couldn't write '%s' file: %s", filename,
584 disk_strerror( error ) );
585 return 1;
586 }
587
588 if( d->disk.filename && strcmp( filename, d->disk.filename ) ) {
589 free( d->disk.filename );
590 d->disk.filename = utils_safe_strdup( filename );
591 }
592 return 0;
593 }
594
595 fdd_t *
opus_get_fdd(opus_drive_number which)596 opus_get_fdd( opus_drive_number which )
597 {
598 return &( opus_drives[ which ].fdd );
599 }
600
601 static void
opus_event_index(libspectrum_dword last_tstates,int type GCC_UNUSED,void * user_data GCC_UNUSED)602 opus_event_index( libspectrum_dword last_tstates, int type GCC_UNUSED,
603 void *user_data GCC_UNUSED )
604 {
605 int next_tstates;
606 int i;
607
608 opus_index_pulse = !opus_index_pulse;
609 for( i = 0; i < OPUS_NUM_DRIVES; i++ ) {
610 wd_fdc_drive *d = &opus_drives[ i ];
611
612 d->index_pulse = opus_index_pulse;
613 if( !opus_index_pulse && d->index_interrupt ) {
614 wd_fdc_set_intrq( opus_fdc );
615 d->index_interrupt = 0;
616 }
617 }
618 next_tstates = ( opus_index_pulse ? 10 : 190 ) *
619 machine_current->timings.processor_speed / 1000;
620 event_add( last_tstates + next_tstates, index_event );
621 }
622
623 libspectrum_byte
opus_read(libspectrum_word address)624 opus_read( libspectrum_word address )
625 {
626 libspectrum_byte data = 0xff;
627
628 if( address >= 0x3800 ) data = 0xff; /* Undefined on Opus */
629 else if( address >= 0x3000 ) /* 6821 PIA */
630 data = opus_6821_access( address, 0, 0 );
631 else if( address >= 0x2800 ) { /* WD1770 FDC */
632 switch( address & 0x03 ) {
633 case 0:
634 data = wd_fdc_sr_read( opus_fdc );
635 break;
636 case 1:
637 data = wd_fdc_tr_read( opus_fdc );
638 break;
639 case 2:
640 data = wd_fdc_sec_read( opus_fdc );
641 break;
642 case 3:
643 data = wd_fdc_dr_read( opus_fdc );
644 break;
645 }
646 }
647
648 return data;
649 }
650
651 void
opus_write(libspectrum_word address,libspectrum_byte b)652 opus_write( libspectrum_word address, libspectrum_byte b )
653 {
654 if( address < 0x2000 ) return;
655 if( address >= 0x3800 ) return;
656
657 if( address >= 0x3000 ) {
658 opus_6821_access( address, b, 1 );
659 } else if( address >= 0x2800 ) {
660 switch( address & 0x03 ) {
661 case 0:
662 wd_fdc_cr_write( opus_fdc, b );
663 break;
664 case 1:
665 wd_fdc_tr_write( opus_fdc, b );
666 break;
667 case 2:
668 wd_fdc_sec_write( opus_fdc, b );
669 break;
670 case 3:
671 wd_fdc_dr_write( opus_fdc, b );
672 break;
673 }
674 }
675 }
676
677 static libspectrum_byte *
alloc_and_copy_page(libspectrum_byte * source_page)678 alloc_and_copy_page( libspectrum_byte* source_page )
679 {
680 libspectrum_byte *buffer;
681 buffer = malloc( MEMORY_PAGE_SIZE );
682 if( !buffer ) {
683 ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__,
684 __LINE__ );
685 return 0;
686 }
687
688 memcpy( buffer, source_page, MEMORY_PAGE_SIZE );
689 return buffer;
690 }
691
692 static void
opus_enabled_snapshot(libspectrum_snap * snap)693 opus_enabled_snapshot( libspectrum_snap *snap )
694 {
695 if( libspectrum_snap_opus_active( snap ) )
696 settings_current.opus = 1;
697 }
698
699 static void
opus_from_snapshot(libspectrum_snap * snap)700 opus_from_snapshot( libspectrum_snap *snap )
701 {
702 if( !libspectrum_snap_opus_active( snap ) ) return;
703
704 if( libspectrum_snap_opus_custom_rom( snap ) &&
705 libspectrum_snap_opus_rom( snap, 0 ) &&
706 machine_load_rom_bank_from_buffer(
707 opus_memory_map_romcs_rom, 0,
708 libspectrum_snap_opus_rom( snap, 0 ),
709 0x2000, 1 ) )
710 return;
711
712 if( libspectrum_snap_opus_ram( snap, 0 ) ) {
713 memcpy( opus_ram,
714 libspectrum_snap_opus_ram( snap, 0 ), TRUE_OPUS_RAM_SIZE );
715 }
716
717 /* ignore drive count for now, there will be an issue with loading snaps where
718 drives have been disabled
719 libspectrum_snap_opus_drive_count( snap )
720 */
721
722 opus_fdc->direction = libspectrum_snap_opus_direction( snap );
723
724 wd_fdc_cr_write ( opus_fdc, libspectrum_snap_opus_status ( snap ) );
725 wd_fdc_tr_write ( opus_fdc, libspectrum_snap_opus_track ( snap ) );
726 wd_fdc_sec_write( opus_fdc, libspectrum_snap_opus_sector ( snap ) );
727 wd_fdc_dr_write ( opus_fdc, libspectrum_snap_opus_data ( snap ) );
728 data_reg_a = libspectrum_snap_opus_data_reg_a( snap );
729 data_dir_a = libspectrum_snap_opus_data_dir_a( snap );
730 control_a = libspectrum_snap_opus_control_a ( snap );
731 data_reg_b = libspectrum_snap_opus_data_reg_b( snap );
732 data_dir_b = libspectrum_snap_opus_data_dir_b( snap );
733 control_b = libspectrum_snap_opus_control_b ( snap );
734
735 if( libspectrum_snap_opus_paged( snap ) ) {
736 opus_page();
737 } else {
738 opus_unpage();
739 }
740 }
741
742 static void
opus_to_snapshot(libspectrum_snap * snap)743 opus_to_snapshot( libspectrum_snap *snap )
744 {
745 libspectrum_byte *buffer;
746 int drive_count = 0;
747
748 if( !periph_is_active( PERIPH_TYPE_OPUS ) ) return;
749
750 libspectrum_snap_set_opus_active( snap, 1 );
751
752 buffer = alloc_and_copy_page( opus_memory_map_romcs_rom[0].page );
753 if( !buffer ) return;
754 libspectrum_snap_set_opus_rom( snap, 0, buffer );
755 if( opus_memory_map_romcs_rom[0].save_to_snapshot )
756 libspectrum_snap_set_opus_custom_rom( snap, 1 );
757
758 buffer = alloc_and_copy_page( opus_ram );
759 if( !buffer ) return;
760 libspectrum_snap_set_opus_ram( snap, 0, buffer );
761
762 drive_count++; /* Drive 1 is not removable */
763 if( option_enumerate_diskoptions_drive_opus2_type() > 0 ) drive_count++;
764 libspectrum_snap_set_opus_drive_count( snap, drive_count );
765
766 libspectrum_snap_set_opus_paged ( snap, opus_active );
767 libspectrum_snap_set_opus_direction ( snap, opus_fdc->direction );
768 libspectrum_snap_set_opus_status ( snap, opus_fdc->status_register );
769 libspectrum_snap_set_opus_track ( snap, opus_fdc->track_register );
770 libspectrum_snap_set_opus_sector ( snap, opus_fdc->sector_register );
771 libspectrum_snap_set_opus_data ( snap, opus_fdc->data_register );
772 libspectrum_snap_set_opus_data_reg_a( snap, data_reg_a );
773 libspectrum_snap_set_opus_data_dir_a( snap, data_dir_a );
774 libspectrum_snap_set_opus_control_a ( snap, control_a );
775 libspectrum_snap_set_opus_data_reg_b( snap, data_reg_b );
776 libspectrum_snap_set_opus_data_dir_b( snap, data_dir_b );
777 libspectrum_snap_set_opus_control_b ( snap, control_b );
778 }
779
780 int
opus_unittest(void)781 opus_unittest( void )
782 {
783 int r = 0;
784
785 opus_page();
786
787 r += unittests_assert_8k_page( 0x0000, opus_rom_memory_source, 0 );
788 r += unittests_assert_4k_page( 0x2000, opus_ram_memory_source, 0 );
789 r += unittests_assert_4k_page( 0x3000, memory_source_rom, 0 );
790 r += unittests_assert_16k_ram_page( 0x4000, 5 );
791 r += unittests_assert_16k_ram_page( 0x8000, 2 );
792 r += unittests_assert_16k_ram_page( 0xc000, 0 );
793
794 opus_unpage();
795
796 r += unittests_paging_test_48( 2 );
797
798 return r;
799 }
800
801