1 /* beta.c: Routines for handling the Beta disk interface
2 Copyright (c) 2004-2011 Stuart Brady, Philip Kendall
3
4 $Id: beta.c 4926 2013-05-05 07:58:18Z sbaldovi $
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 Philip: philip-fuse@shadowmagic.org.uk
23
24 Stuart: stuart.brady@gmail.com
25
26 */
27
28 #include <config.h>
29
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <string.h>
36 #if defined(HAVE_STRINGS_H) && !defined(__CELLOS_LV2__)
37 #include <strings.h> /* Needed for strncasecmp() on QNX6 */
38 #endif /* #ifdef HAVE_STRINGS_H */
39 #include <limits.h>
40 #include <sys/stat.h>
41
42 #include <libspectrum.h>
43
44 #include "beta.h"
45 #include "compat.h"
46 #include "event.h"
47 #include "machine.h"
48 #include "module.h"
49 #include "settings.h"
50 #include "ui/ui.h"
51 #include "unittests/unittests.h"
52 #include "utils.h"
53 #include "wd_fdc.h"
54 #include "z80/z80.h"
55 #include "z80/z80_macros.h"
56 #include "options.h" /* needed for get combo options */
57
58 #define DISK_TRY_MERGE(heads) ( option_enumerate_diskoptions_disk_try_merge() == 2 || \
59 ( option_enumerate_diskoptions_disk_try_merge() == 1 && heads == 1 ) )
60
61 /* A 16KB memory chunk accessible by the Z80 when /ROMCS is low */
62 memory_page beta_memory_map_romcs[MEMORY_PAGES_IN_16K];
63 static int beta_memory_source;
64
65 int beta_available = 0;
66 int beta_active = 0;
67 int beta_builtin = 0;
68
69 static libspectrum_byte beta_system_register; /* FDC system register */
70
71 libspectrum_word beta_pc_mask;
72 libspectrum_word beta_pc_value;
73
74 static int beta_index_pulse = 0;
75
76 static int index_event;
77
78 #define BETA_NUM_DRIVES 4
79
80 static wd_fdc *beta_fdc;
81 static wd_fdc_drive beta_drives[ BETA_NUM_DRIVES ];
82
83 static const periph_port_t beta_ports[] = {
84 { 0x00ff, 0x001f, beta_sr_read, beta_cr_write },
85 { 0x00ff, 0x003f, beta_tr_read, beta_tr_write },
86 { 0x00ff, 0x005f, beta_sec_read, beta_sec_write },
87 { 0x00ff, 0x007f, beta_dr_read, beta_dr_write },
88 { 0x00ff, 0x00ff, beta_sp_read, beta_sp_write },
89 { 0, 0, NULL, NULL }
90 };
91
92 static const periph_t beta_peripheral = {
93 &settings_current.beta128,
94 beta_ports,
95 1,
96 NULL
97 };
98
99 static void beta_reset( int hard_reset );
100 static void beta_memory_map( void );
101 static void beta_enabled_snapshot( libspectrum_snap *snap );
102 static void beta_from_snapshot( libspectrum_snap *snap );
103 static void beta_to_snapshot( libspectrum_snap *snap );
104 static void beta_event_index( libspectrum_dword last_tstates, int type,
105 void *user_data );
106
107 static module_info_t beta_module_info = {
108
109 beta_reset,
110 beta_memory_map,
111 beta_enabled_snapshot,
112 beta_from_snapshot,
113 beta_to_snapshot,
114
115 };
116
117 void
beta_page(void)118 beta_page( void )
119 {
120 beta_active = 1;
121 machine_current->ram.romcs = 1;
122 machine_current->memory_map();
123 }
124
125 void
beta_unpage(void)126 beta_unpage( void )
127 {
128 beta_active = 0;
129 machine_current->ram.romcs = 0;
130 machine_current->memory_map();
131 }
132
133 static void
beta_memory_map(void)134 beta_memory_map( void )
135 {
136 if( !beta_active ) return;
137
138 memory_map_romcs( beta_memory_map_romcs );
139 }
140
141 static void
beta_select_drive(int i)142 beta_select_drive( int i )
143 {
144 if( beta_fdc->current_drive != &beta_drives[ i & 0x03 ] ) {
145 if( beta_fdc->current_drive != NULL )
146 fdd_select( &beta_fdc->current_drive->fdd, 0 );
147 beta_fdc->current_drive = &beta_drives[ i & 0x03 ];
148 fdd_select( &beta_fdc->current_drive->fdd, 1 );
149 }
150 }
151
152 void
beta_init(void)153 beta_init( void )
154 {
155 int i;
156 wd_fdc_drive *d;
157
158 beta_fdc = wd_fdc_alloc_fdc( FD1793, 0, WD_FLAG_BETA128 );
159 beta_fdc->current_drive = NULL;
160
161 for( i = 0; i < BETA_NUM_DRIVES; i++ ) {
162 d = &beta_drives[ i ];
163 fdd_init( &d->fdd, FDD_SHUGART, NULL, 0 ); /* drive geometry 'autodetect' */
164 d->disk.flag = DISK_FLAG_NONE;
165 }
166 beta_select_drive( 0 );
167
168 beta_fdc->dden = 1;
169 beta_fdc->set_intrq = NULL;
170 beta_fdc->reset_intrq = NULL;
171 beta_fdc->set_datarq = NULL;
172 beta_fdc->reset_datarq = NULL;
173
174 index_event = event_register( beta_event_index, "Beta disk index" );
175
176 module_register( &beta_module_info );
177
178 beta_memory_source = memory_source_register( "Betadisk" );
179 for( i = 0; i < MEMORY_PAGES_IN_16K; i++ )
180 beta_memory_map_romcs[i].source = beta_memory_source;
181
182 periph_register( PERIPH_TYPE_BETA128, &beta_peripheral );
183 }
184
185 static void
beta_reset(int hard_reset GCC_UNUSED)186 beta_reset( int hard_reset GCC_UNUSED )
187 {
188 int i;
189 wd_fdc_drive *d;
190 const fdd_params_t *dt;
191
192 event_remove_type( index_event );
193
194 if( !(periph_is_active( PERIPH_TYPE_BETA128 ) ||
195 periph_is_active( PERIPH_TYPE_BETA128_PENTAGON ) ||
196 periph_is_active( PERIPH_TYPE_BETA128_PENTAGON_LATE )) ) {
197 beta_active = 0;
198 beta_available = 0;
199 return;
200 }
201
202 beta_available = 1;
203
204 beta_pc_mask = 0xff00;
205 beta_pc_value = 0x3d00;
206
207 wd_fdc_master_reset( beta_fdc );
208
209 for( i = 0; i < BETA_NUM_DRIVES; i++ ) {
210 d = &beta_drives[ i ];
211
212 d->index_pulse = 0;
213 d->index_interrupt = 0;
214 }
215
216 if( !beta_builtin ) {
217 if( machine_load_rom_bank( beta_memory_map_romcs, 0,
218 settings_current.rom_beta128,
219 settings_default.rom_beta128, 0x4000 ) ) {
220 beta_active = 0;
221 beta_available = 0;
222 periph_activate_type( PERIPH_TYPE_BETA128, 0 );
223 settings_current.beta128 = 0;
224 return;
225 }
226
227 beta_active = 0;
228
229 if( !( machine_current->capabilities &
230 LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) ) {
231 beta_pc_mask = 0xfe00;
232 beta_pc_value = 0x3c00;
233
234 /* For 48K type machines, the Beta 128 is supposed to be configured
235 to start with the Beta ROM paged in (System switch in centre position)
236 but we also allow the settion where the Beta does not auto-boot (System
237 switch is in the off position 3)
238 */
239 if ( settings_current.beta128_48boot )
240 beta_page();
241 }
242 }
243
244 /* We can eject disks only if they are currently present */
245 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128a_type() + 1 ]; /* +1 => there is no `Disabled' */
246 fdd_init( &beta_drives[ BETA_DRIVE_A ].fdd, FDD_SHUGART, dt, 1 );
247 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A, dt->enabled );
248 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_EJECT,
249 beta_drives[ BETA_DRIVE_A ].fdd.loaded );
250 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_FLIP_SET,
251 !beta_drives[ BETA_DRIVE_A ].fdd.upsidedown );
252 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_WP_SET,
253 !beta_drives[ BETA_DRIVE_A ].fdd.wrprot );
254
255
256 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128b_type() ];
257 fdd_init( &beta_drives[ BETA_DRIVE_B ].fdd, dt->enabled ? FDD_SHUGART : FDD_TYPE_NONE, dt, 1 );
258 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B, dt->enabled );
259 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_FLIP_SET,
260 !beta_drives[ BETA_DRIVE_B ].fdd.upsidedown );
261 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_EJECT,
262 beta_drives[ BETA_DRIVE_B ].fdd.loaded );
263 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_WP_SET,
264 !beta_drives[ BETA_DRIVE_B ].fdd.wrprot );
265
266
267 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128c_type() ];
268 fdd_init( &beta_drives[ BETA_DRIVE_C ].fdd, dt->enabled ? FDD_SHUGART : FDD_TYPE_NONE, dt, 1 );
269 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C, dt->enabled );
270 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_FLIP_SET,
271 !beta_drives[ BETA_DRIVE_C ].fdd.upsidedown );
272 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_EJECT,
273 beta_drives[ BETA_DRIVE_C ].fdd.loaded );
274 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_WP_SET,
275 !beta_drives[ BETA_DRIVE_C ].fdd.wrprot );
276
277
278 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128d_type() ];
279 fdd_init( &beta_drives[ BETA_DRIVE_D ].fdd, dt->enabled ? FDD_SHUGART : FDD_TYPE_NONE, dt, 1 );
280 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D, dt->enabled );
281 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_FLIP_SET,
282 !beta_drives[ BETA_DRIVE_D ].fdd.upsidedown );
283 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_EJECT,
284 beta_drives[ BETA_DRIVE_D ].fdd.loaded );
285 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_WP_SET,
286 !beta_drives[ BETA_DRIVE_D ].fdd.wrprot );
287
288
289 beta_select_drive( 0 );
290 machine_current->memory_map();
291 beta_event_index( 0, 0, NULL );
292
293 ui_statusbar_update( UI_STATUSBAR_ITEM_DISK, UI_STATUSBAR_STATE_INACTIVE );
294 }
295
296 void
beta_end(void)297 beta_end( void )
298 {
299 beta_available = 0;
300 free( beta_fdc );
301 }
302
303 libspectrum_byte
beta_sr_read(libspectrum_word port GCC_UNUSED,int * attached)304 beta_sr_read( libspectrum_word port GCC_UNUSED, int *attached )
305 {
306 if( !beta_active ) return 0xff;
307
308 *attached = 1;
309 return wd_fdc_sr_read( beta_fdc );
310 }
311
312 void
beta_cr_write(libspectrum_word port GCC_UNUSED,libspectrum_byte b)313 beta_cr_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
314 {
315 if( !beta_active ) return;
316
317 wd_fdc_cr_write( beta_fdc, b );
318 }
319
320 libspectrum_byte
beta_tr_read(libspectrum_word port GCC_UNUSED,int * attached)321 beta_tr_read( libspectrum_word port GCC_UNUSED, int *attached )
322 {
323 if( !beta_active ) return 0xff;
324
325 *attached = 1;
326 return wd_fdc_tr_read( beta_fdc );
327 }
328
329 void
beta_tr_write(libspectrum_word port GCC_UNUSED,libspectrum_byte b)330 beta_tr_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
331 {
332 if( !beta_active ) return;
333
334 wd_fdc_tr_write( beta_fdc, b );
335 }
336
337 libspectrum_byte
beta_sec_read(libspectrum_word port GCC_UNUSED,int * attached)338 beta_sec_read( libspectrum_word port GCC_UNUSED, int *attached )
339 {
340 if( !beta_active ) return 0xff;
341
342 *attached = 1;
343 return wd_fdc_sec_read( beta_fdc );
344 }
345
346 void
beta_sec_write(libspectrum_word port GCC_UNUSED,libspectrum_byte b)347 beta_sec_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
348 {
349 if( !beta_active ) return;
350
351 wd_fdc_sec_write( beta_fdc, b );
352 }
353
354 libspectrum_byte
beta_dr_read(libspectrum_word port GCC_UNUSED,int * attached)355 beta_dr_read( libspectrum_word port GCC_UNUSED, int *attached )
356 {
357 if( !beta_active ) return 0xff;
358
359 *attached = 1;
360 return wd_fdc_dr_read( beta_fdc );
361 }
362
363 void
beta_dr_write(libspectrum_word port GCC_UNUSED,libspectrum_byte b)364 beta_dr_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
365 {
366 if( !beta_active ) return;
367
368 wd_fdc_dr_write( beta_fdc, b );
369 }
370
371 void
beta_sp_write(libspectrum_word port GCC_UNUSED,libspectrum_byte b)372 beta_sp_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
373 {
374 if( !beta_active ) return;
375
376 /* reset 0x04 and then set it to reset controller */
377 beta_select_drive( b & 0x03 );
378 /* 0x08 = block hlt, normally set */
379 wd_fdc_set_hlt( beta_fdc, ( ( b & 0x08 ) ? 1 : 0 ) );
380 fdd_set_head( &beta_fdc->current_drive->fdd, ( ( b & 0x10 ) ? 0 : 1 ) );
381 /* 0x20 = density, reset = FM, set = MFM */
382 beta_fdc->dden = b & 0x20 ? 1 : 0;
383
384 beta_system_register = b;
385 }
386
387 libspectrum_byte
beta_sp_read(libspectrum_word port GCC_UNUSED,int * attached)388 beta_sp_read( libspectrum_word port GCC_UNUSED, int *attached )
389 {
390 libspectrum_byte b;
391
392 if( !beta_active ) return 0xff;
393
394 *attached = 1;
395 b = 0;
396
397 if( beta_fdc->intrq )
398 b |= 0x80;
399
400 if( beta_fdc->datarq )
401 b |= 0x40;
402
403 /* we should reset beta_datarq, but we first need to raise it for each byte
404 * transferred in wd_fdc.c */
405
406 return b;
407 }
408
409 int
beta_disk_insert(beta_drive_number which,const char * filename,int autoload)410 beta_disk_insert( beta_drive_number which, const char *filename,
411 int autoload )
412 {
413 int error;
414 wd_fdc_drive *d;
415 const fdd_params_t *dt;
416
417 if( which >= BETA_NUM_DRIVES ) {
418 ui_error( UI_ERROR_ERROR, "beta_disk_insert: unknown drive %d",
419 which );
420 fuse_abort();
421 }
422
423 d = &beta_drives[ which ];
424
425 /* Eject any disk already in the drive */
426 if( d->fdd.loaded ) {
427 /* Abort the insert if we want to keep the current disk */
428 if( beta_disk_eject( which ) ) return 0;
429 }
430
431 if( filename ) {
432 error = disk_open( &d->disk, filename, 0, DISK_TRY_MERGE( d->fdd.fdd_heads ) );
433 if( error != DISK_OK ) {
434 ui_error( UI_ERROR_ERROR, "Failed to open disk image: %s",
435 disk_strerror( d->disk.status ) );
436 return 1;
437 }
438 } else {
439 switch( which ) {
440 case 0:
441 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128a_type() + 1 ]; /* +1 => there is no `Disabled' */
442 break;
443 case 1:
444 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128b_type() ];
445 break;
446 case 2:
447 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128c_type() ];
448 break;
449 case 3:
450 default:
451 dt = &fdd_params[ option_enumerate_diskoptions_drive_beta128d_type() ];
452 break;
453 }
454 error = disk_new( &d->disk, dt->heads, dt->cylinders, DISK_DENS_AUTO, DISK_UDI );
455 if( error != DISK_OK ) {
456 ui_error( UI_ERROR_ERROR, "Failed to create disk image: %s",
457 disk_strerror( d->disk.status ) );
458 return 1;
459 }
460 }
461
462 fdd_load( &d->fdd, &d->disk, 0 );
463
464 /* Set the 'eject' item active */
465 switch( which ) {
466 case BETA_DRIVE_A:
467 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_EJECT, 1 );
468 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_FLIP_SET,
469 !beta_drives[ BETA_DRIVE_A ].fdd.upsidedown );
470 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_WP_SET,
471 !beta_drives[ BETA_DRIVE_A ].fdd.wrprot );
472 break;
473 case BETA_DRIVE_B:
474 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_EJECT, 1 );
475 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_FLIP_SET,
476 !beta_drives[ BETA_DRIVE_B ].fdd.upsidedown );
477 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_WP_SET,
478 !beta_drives[ BETA_DRIVE_B ].fdd.wrprot );
479 break;
480 case BETA_DRIVE_C:
481 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_EJECT, 1 );
482 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_FLIP_SET,
483 !beta_drives[ BETA_DRIVE_C ].fdd.upsidedown );
484 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_WP_SET,
485 !beta_drives[ BETA_DRIVE_C ].fdd.wrprot );
486 break;
487 case BETA_DRIVE_D:
488 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_EJECT, 1 );
489 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_FLIP_SET,
490 !beta_drives[ BETA_DRIVE_D ].fdd.upsidedown );
491 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_WP_SET,
492 !beta_drives[ BETA_DRIVE_D ].fdd.wrprot );
493 break;
494 }
495
496 if( filename && autoload ) {
497 PC = 0;
498 machine_current->ram.last_byte |= 0x10; /* Select ROM 1 */
499 beta_page();
500 }
501
502 return 0;
503 }
504
505 int
beta_disk_flip(beta_drive_number which,int flip)506 beta_disk_flip( beta_drive_number which, int flip )
507 {
508 wd_fdc_drive *d;
509
510 if( which >= BETA_NUM_DRIVES )
511 return 1;
512
513 d = &beta_drives[ which ];
514
515 if( !d->fdd.loaded )
516 return 1;
517
518 fdd_flip( &d->fdd, flip );
519
520 /* Set the 'flip' item */
521 switch( which ) {
522 case BETA_DRIVE_A:
523 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_FLIP_SET,
524 !beta_drives[ BETA_DRIVE_A ].fdd.upsidedown );
525 break;
526 case BETA_DRIVE_B:
527 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_FLIP_SET,
528 !beta_drives[ BETA_DRIVE_B ].fdd.upsidedown );
529 break;
530 case BETA_DRIVE_C:
531 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_FLIP_SET,
532 !beta_drives[ BETA_DRIVE_C ].fdd.upsidedown );
533 break;
534 case BETA_DRIVE_D:
535 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_FLIP_SET,
536 !beta_drives[ BETA_DRIVE_D ].fdd.upsidedown );
537 break;
538 }
539 return 0;
540 }
541
542 int
beta_disk_writeprotect(beta_drive_number which,int wrprot)543 beta_disk_writeprotect( beta_drive_number which, int wrprot )
544 {
545 wd_fdc_drive *d;
546
547 if( which >= BETA_NUM_DRIVES )
548 return 1;
549
550 d = &beta_drives[ which ];
551
552 if( !d->fdd.loaded )
553 return 1;
554
555 fdd_wrprot( &d->fdd, wrprot );
556
557 /* Set the 'writeprotect' item */
558 switch( which ) {
559 case BETA_DRIVE_A:
560 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_WP_SET,
561 !beta_drives[ BETA_DRIVE_A ].fdd.wrprot );
562 break;
563 case BETA_DRIVE_B:
564 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_WP_SET,
565 !beta_drives[ BETA_DRIVE_B ].fdd.wrprot );
566 break;
567 case BETA_DRIVE_C:
568 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_WP_SET,
569 !beta_drives[ BETA_DRIVE_C ].fdd.wrprot );
570 break;
571 case BETA_DRIVE_D:
572 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_WP_SET,
573 !beta_drives[ BETA_DRIVE_D ].fdd.wrprot );
574 break;
575 }
576 return 0;
577 }
578
579 int
beta_disk_eject(beta_drive_number which)580 beta_disk_eject( beta_drive_number which )
581 {
582 wd_fdc_drive *d;
583 char drive;
584
585 if( which >= BETA_NUM_DRIVES )
586 return 1;
587
588 d = &beta_drives[ which ];
589
590 if( !d->fdd.loaded )
591 return 0;
592
593 if( d->disk.dirty ) {
594 ui_confirm_save_t confirm;
595
596 switch( which ) {
597 case BETA_DRIVE_A: drive = 'A'; break;
598 case BETA_DRIVE_B: drive = 'B'; break;
599 case BETA_DRIVE_C: drive = 'C'; break;
600 case BETA_DRIVE_D: drive = 'D'; break;
601 default: drive = '?'; break;
602 }
603
604 confirm = ui_confirm_save(
605 "Disk in Beta drive %c: has been modified.\n"
606 "Do you want to save it?",
607 drive
608 );
609
610 switch( confirm ) {
611
612 case UI_CONFIRM_SAVE_SAVE:
613 if( beta_disk_save( which, 0 ) ) return 1; /* first save */
614 break;
615
616 case UI_CONFIRM_SAVE_DONTSAVE: break;
617 case UI_CONFIRM_SAVE_CANCEL: return 1;
618
619 }
620 }
621
622 fdd_unload( &d->fdd );
623 disk_close( &d->disk );
624
625 /* Set the 'eject' item inactive */
626 switch( which ) {
627 case BETA_DRIVE_A:
628 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_A_EJECT, 0 );
629 break;
630 case BETA_DRIVE_B:
631 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_B_EJECT, 0 );
632 break;
633 case BETA_DRIVE_C:
634 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_C_EJECT, 0 );
635 break;
636 case BETA_DRIVE_D:
637 ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_BETA_D_EJECT, 0 );
638 break;
639 }
640 return 0;
641 }
642
643 int
beta_disk_save(beta_drive_number which,int saveas)644 beta_disk_save( beta_drive_number which, int saveas )
645 {
646 wd_fdc_drive *d;
647
648 if( which >= BETA_NUM_DRIVES )
649 return 1;
650
651 d = &beta_drives[ which ];
652
653 if( !d->fdd.loaded )
654 return 0;
655
656 if( d->disk.filename == NULL ) saveas = 1;
657 if( ui_beta_disk_write( which, saveas ) ) return 1;
658 d->disk.dirty = 0;
659 return 0;
660 }
661
662 int
beta_disk_write(beta_drive_number which,const char * filename)663 beta_disk_write( beta_drive_number which, const char *filename )
664 {
665 wd_fdc_drive *d = &beta_drives[ which ];
666 int error;
667
668 d->disk.type = DISK_TYPE_NONE;
669 if( filename == NULL ) filename = d->disk.filename; /* write over original file */
670 error = disk_write( &d->disk, filename );
671
672 if( error != DISK_OK ) {
673 ui_error( UI_ERROR_ERROR, "couldn't write '%s' file: %s", filename,
674 disk_strerror( error ) );
675 return 1;
676 }
677
678 if( d->disk.filename && strcmp( filename, d->disk.filename ) ) {
679 free( d->disk.filename );
680 d->disk.filename = utils_safe_strdup( filename );
681 }
682 return 0;
683 }
684
685 fdd_t *
beta_get_fdd(beta_drive_number which)686 beta_get_fdd( beta_drive_number which )
687 {
688 return &( beta_drives[ which ].fdd );
689 }
690
691 static void
beta_event_index(libspectrum_dword last_tstates,int type,void * user_data)692 beta_event_index( libspectrum_dword last_tstates, int type, void *user_data )
693 {
694 int next_tstates;
695 int i;
696
697 beta_index_pulse = !beta_index_pulse;
698 for( i = 0; i < BETA_NUM_DRIVES; i++ ) {
699 wd_fdc_drive *d = &beta_drives[ i ];
700
701 d->index_pulse = beta_index_pulse;
702 /* disabled, until we have better timing emulation,
703 * to avoid interrupts while reading/writing data */
704 if( !beta_index_pulse && d->index_interrupt ) {
705 wd_fdc_set_intrq( beta_fdc );
706 d->index_interrupt = 0;
707 }
708 }
709 next_tstates = ( beta_index_pulse ? 10 : 190 ) *
710 machine_current->timings.processor_speed / 1000;
711
712 event_add( last_tstates + next_tstates, index_event );
713 }
714
715 static void
beta_enabled_snapshot(libspectrum_snap * snap)716 beta_enabled_snapshot( libspectrum_snap *snap )
717 {
718 if( libspectrum_snap_beta_active( snap ) )
719 settings_current.beta128 = 1;
720 }
721
722 static void
beta_from_snapshot(libspectrum_snap * snap)723 beta_from_snapshot( libspectrum_snap *snap )
724 {
725 if( !libspectrum_snap_beta_active( snap ) ) return;
726
727 if( !( machine_current->capabilities &
728 LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) ) {
729 settings_current.beta128_48boot = libspectrum_snap_beta_autoboot( snap );
730 }
731
732 beta_active = libspectrum_snap_beta_paged( snap );
733
734 if( beta_active ) {
735 beta_page();
736 } else {
737 beta_unpage();
738 }
739
740 if( libspectrum_snap_beta_custom_rom( snap ) &&
741 libspectrum_snap_beta_rom( snap, 0 ) &&
742 machine_load_rom_bank_from_buffer(
743 beta_memory_map_romcs, 0,
744 libspectrum_snap_beta_rom( snap, 0 ),
745 0x4000, 1 ) )
746 return;
747
748 /* ignore drive count for now, there will be an issue with loading snaps where
749 drives have been disabled
750 libspectrum_snap_beta_drive_count( snap )
751 */
752
753 beta_fdc->direction = libspectrum_snap_beta_direction( snap );
754
755 beta_cr_write ( 0x001f, 0 );
756 beta_tr_write ( 0x003f, libspectrum_snap_beta_track ( snap ) );
757 beta_sec_write( 0x005f, libspectrum_snap_beta_sector( snap ) );
758 beta_dr_write ( 0x007f, libspectrum_snap_beta_data ( snap ) );
759 beta_sp_write ( 0x00ff, libspectrum_snap_beta_system( snap ) );
760 }
761
762 void
beta_to_snapshot(libspectrum_snap * snap)763 beta_to_snapshot( libspectrum_snap *snap )
764 {
765 wd_fdc *f = beta_fdc;
766 libspectrum_byte *buffer;
767 int drive_count = 0;
768
769 if( !periph_is_active( PERIPH_TYPE_BETA128 ) ) return;
770
771 libspectrum_snap_set_beta_active( snap, 1 );
772
773 if( beta_memory_map_romcs[0].save_to_snapshot ) {
774 size_t rom_length = MEMORY_PAGE_SIZE * 2;
775
776 buffer = malloc( rom_length );
777 if( !buffer ) {
778 ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__, __LINE__ );
779 return;
780 }
781
782 memcpy( buffer, beta_memory_map_romcs[0].page, MEMORY_PAGE_SIZE );
783 memcpy( buffer + MEMORY_PAGE_SIZE, beta_memory_map_romcs[1].page,
784 MEMORY_PAGE_SIZE );
785
786 libspectrum_snap_set_beta_rom( snap, 0, buffer );
787 libspectrum_snap_set_beta_custom_rom( snap, 1 );
788 }
789
790 drive_count++; /* Drive A is not removable */
791 if( option_enumerate_diskoptions_drive_beta128b_type() > 0 ) drive_count++;
792 if( option_enumerate_diskoptions_drive_beta128c_type() > 0 ) drive_count++;
793 if( option_enumerate_diskoptions_drive_beta128d_type() > 0 ) drive_count++;
794 libspectrum_snap_set_beta_drive_count( snap, drive_count );
795
796 libspectrum_snap_set_beta_paged ( snap, beta_active );
797 if( !( machine_current->capabilities &
798 LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) ) {
799 libspectrum_snap_set_beta_autoboot( snap, settings_current.beta128_48boot );
800 }
801 libspectrum_snap_set_beta_direction( snap, beta_fdc->direction );
802 libspectrum_snap_set_beta_status( snap, f->status_register );
803 libspectrum_snap_set_beta_track ( snap, f->track_register );
804 libspectrum_snap_set_beta_sector( snap, f->sector_register );
805 libspectrum_snap_set_beta_data ( snap, f->data_register );
806 libspectrum_snap_set_beta_system( snap, beta_system_register );
807 }
808
809 int
beta_unittest(void)810 beta_unittest( void )
811 {
812 int r = 0;
813
814 beta_page();
815
816 r += unittests_assert_16k_page( 0x0000, beta_memory_source, 0 );
817 r += unittests_assert_16k_ram_page( 0x4000, 5 );
818 r += unittests_assert_16k_ram_page( 0x8000, 2 );
819 r += unittests_assert_16k_ram_page( 0xc000, 0 );
820
821 beta_unpage();
822
823 r += unittests_paging_test_48( 2 );
824
825 return r;
826 }
827
828