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