1 /* divmmc.c: DivMMC interface routines
2    Copyright (c) 2005-2017 Matthew Westcott, Philip Kendall
3    Copyright (c) 2015 Stuart Brady
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, write to the Free Software Foundation, Inc.,
17    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 
19    Author contact information:
20 
21    E-mail: Philip Kendall <philip-fuse@shadowmagic.org.uk>
22 
23 */
24 
25 #include <config.h>
26 
27 #include <libspectrum.h>
28 
29 #include <string.h>
30 
31 #include "debugger/debugger.h"
32 #include "ide.h"
33 #include "infrastructure/startup_manager.h"
34 #include "machine.h"
35 #include "module.h"
36 #include "periph.h"
37 #include "settings.h"
38 #include "ui/ui.h"
39 #include "unittests/unittests.h"
40 #include "divmmc.h"
41 #include "divxxx.h"
42 
43 /* Private function prototypes */
44 
45 static void divmmc_control_write( libspectrum_word port, libspectrum_byte data );
46 static void divmmc_card_select( libspectrum_word port, libspectrum_byte data );
47 static libspectrum_byte divmmc_mmc_read( libspectrum_word port, libspectrum_byte *attached );
48 static void divmmc_mmc_write( libspectrum_word port, libspectrum_byte data );
49 static void divmmc_activate( void );
50 static libspectrum_dword get_control_register( void );
51 static void set_control_register( libspectrum_dword value );
52 
53 /* Data */
54 
55 static const periph_port_t divmmc_ports[] = {
56   { 0x00ff, 0x00e3, NULL, divmmc_control_write },
57   { 0x00ff, 0x00e7, NULL, divmmc_card_select },
58   { 0x00ff, 0x00eb, divmmc_mmc_read, divmmc_mmc_write },
59   { 0, 0, NULL, NULL }
60 };
61 
62 static const periph_t divmmc_periph = {
63   /* .option = */ &settings_current.divmmc_enabled,
64   /* .ports = */ divmmc_ports,
65   /* .hard_reset = */ 1,
66   /* .activate = */ divmmc_activate,
67 };
68 
69 static divxxx_t *divmmc_state;
70 
71 /* The card inserted into the DivMMC. For now, we emulate only one card. */
72 static libspectrum_mmc_card *card;
73 
74 /* The card currently selected via the "card select" call */
75 static libspectrum_mmc_card *current_card;
76 
77 /* *Our* DivMMC has 128 Kb of RAM */
78 #define DIVMMC_PAGES 16
79 #define DIVMMC_PAGE_LENGTH 0x2000
80 
81 static void divmmc_reset( int hard_reset );
82 static void divmmc_memory_map( void );
83 static void divmmc_enabled_snapshot( libspectrum_snap *snap );
84 static void divmmc_from_snapshot( libspectrum_snap *snap );
85 static void divmmc_to_snapshot( libspectrum_snap *snap );
86 
87 static module_info_t divmmc_module_info = {
88 
89   /* .reset = */ divmmc_reset,
90   /* .romcs = */ divmmc_memory_map,
91   /* .snapshot_enabled = */ divmmc_enabled_snapshot,
92   /* .snapshot_from = */ divmmc_from_snapshot,
93   /* .snapshot_to = */ divmmc_to_snapshot,
94 
95 };
96 
97 /* Debugger events */
98 static const char * const event_type_string = "divmmc";
99 
100 /* Debugger system variables */
101 static const char * const debugger_type_string = "divmmc";
102 static const char * const control_register_detail_string = "control";
103 
104 /* Eject menu item */
105 static const ui_menu_item eject_menu_item = UI_MENU_ITEM_MEDIA_IDE_DIVMMC_EJECT;
106 
107 /* Housekeeping functions */
108 
109 static int
divmmc_init(void * context)110 divmmc_init( void *context )
111 {
112   card = libspectrum_mmc_alloc();
113 
114   ui_menu_activate( eject_menu_item, 0 );
115 
116   if( settings_current.divmmc_file ) {
117     int error;
118 
119     error =
120       libspectrum_mmc_insert( card, settings_current.divmmc_file );
121     if( error ) return error;
122 
123     error = ui_menu_activate( eject_menu_item, 1 );
124     if( error ) return error;
125   }
126 
127   module_register( &divmmc_module_info );
128 
129   periph_register( PERIPH_TYPE_DIVMMC, &divmmc_periph );
130 
131   divmmc_state = divxxx_alloc( "DivMMC EPROM", DIVMMC_PAGES, "DivMMC RAM",
132       event_type_string, &settings_current.divmmc_enabled,
133       &settings_current.divmmc_wp );
134 
135   debugger_system_variable_register(
136     debugger_type_string, control_register_detail_string, get_control_register,
137     set_control_register );
138 
139   return 0;
140 }
141 
142 static void
divmmc_end(void)143 divmmc_end( void )
144 {
145   divxxx_free( divmmc_state );
146   libspectrum_mmc_free( card );
147 }
148 
149 void
divmmc_register_startup(void)150 divmmc_register_startup( void )
151 {
152   startup_manager_module dependencies[] = {
153     STARTUP_MANAGER_MODULE_DEBUGGER,
154     STARTUP_MANAGER_MODULE_DISPLAY,
155     STARTUP_MANAGER_MODULE_MEMORY,
156     STARTUP_MANAGER_MODULE_SETUID,
157   };
158   startup_manager_register( STARTUP_MANAGER_MODULE_DIVMMC, dependencies,
159                             ARRAY_SIZE( dependencies ), divmmc_init, NULL,
160                             divmmc_end );
161 }
162 
163 static void
divmmc_reset(int hard_reset)164 divmmc_reset( int hard_reset )
165 {
166   divxxx_reset( divmmc_state, hard_reset );
167   libspectrum_mmc_reset( card );
168 }
169 
170 static int
dirty_fn_wrapper(void * context)171 dirty_fn_wrapper( void *context )
172 {
173   return libspectrum_mmc_dirty( (libspectrum_mmc_card*)context );
174 }
175 
176 static libspectrum_error
commit_fn_wrapper(void * context)177 commit_fn_wrapper( void *context )
178 {
179   libspectrum_mmc_commit( (libspectrum_mmc_card*)context );
180   return LIBSPECTRUM_ERROR_NONE;
181 }
182 
183 static libspectrum_error
eject_fn_wrapper(void * context)184 eject_fn_wrapper( void *context )
185 {
186   libspectrum_mmc_eject( (libspectrum_mmc_card*)context );
187   return LIBSPECTRUM_ERROR_NONE;
188 }
189 
190 static int
mmc_eject(libspectrum_mmc_card * card)191 mmc_eject( libspectrum_mmc_card *card )
192 {
193   return ide_eject_mass_storage( dirty_fn_wrapper, commit_fn_wrapper,
194       eject_fn_wrapper, card,
195       "Card has been modified.\nDo you want to save it?",
196       &settings_current.divmmc_file,
197       eject_menu_item );
198 }
199 
200 int
divmmc_insert(const char * filename)201 divmmc_insert( const char *filename )
202 {
203   int error;
204 
205   /* Remove any currently inserted card; abort if we want to keep the current
206      card */
207   if( settings_current.divmmc_file )
208     if( mmc_eject( card ) )
209       return 0;
210 
211   settings_set_string( &settings_current.divmmc_file, filename );
212 
213   error = libspectrum_mmc_insert( card, filename );
214   if( error ) return error;
215   return ui_menu_activate( eject_menu_item, 1 );
216 }
217 
218 void
divmmc_commit(void)219 divmmc_commit( void )
220 {
221   libspectrum_mmc_commit( card );
222 }
223 
224 int
divmmc_eject(void)225 divmmc_eject( void )
226 {
227   return mmc_eject( card );
228 }
229 
230 /* Port read/writes */
231 
232 static void
divmmc_control_write(libspectrum_word port GCC_UNUSED,libspectrum_byte data)233 divmmc_control_write( libspectrum_word port GCC_UNUSED, libspectrum_byte data )
234 {
235   divxxx_control_write( divmmc_state, data );
236 }
237 
238 static void
divmmc_card_select(libspectrum_word port GCC_UNUSED,libspectrum_byte data)239 divmmc_card_select( libspectrum_word port GCC_UNUSED, libspectrum_byte data )
240 {
241 //  printf("divmmc_card_select( 0x%02x )\n", data );
242 
243   /* D0 = MMC0, D1 = MMC1, active LOW
244      somehow logic prevents enabling both cards at the same time */
245   switch( data & 0x03 ) {
246     case 0x02:
247       current_card = card;
248       break;
249     case 0x01:
250       /* TODO: select second card */
251       current_card = NULL;
252       break;
253     default:
254       current_card = NULL;
255       break;
256   }
257 }
258 
259 static libspectrum_byte
divmmc_mmc_read(libspectrum_word port GCC_UNUSED,libspectrum_byte * attached)260 divmmc_mmc_read( libspectrum_word port GCC_UNUSED, libspectrum_byte *attached )
261 {
262   *attached = 0xff;
263 
264   return current_card ? libspectrum_mmc_read( card ) : 0xff;
265 }
266 
267 static void
divmmc_mmc_write(libspectrum_word port GCC_UNUSED,libspectrum_byte data)268 divmmc_mmc_write( libspectrum_word port GCC_UNUSED, libspectrum_byte data )
269 {
270   if( current_card ) libspectrum_mmc_write( card, data );
271 }
272 
273 void
divmmc_set_automap(int state)274 divmmc_set_automap( int state )
275 {
276   divxxx_set_automap( divmmc_state, state );
277 }
278 
279 void
divmmc_refresh_page_state(void)280 divmmc_refresh_page_state( void )
281 {
282   divxxx_refresh_page_state( divmmc_state );
283 }
284 
285 void
divmmc_memory_map(void)286 divmmc_memory_map( void )
287 {
288   divxxx_memory_map( divmmc_state );
289 }
290 
291 static void
divmmc_enabled_snapshot(libspectrum_snap * snap)292 divmmc_enabled_snapshot( libspectrum_snap *snap )
293 {
294   if( libspectrum_snap_divmmc_active( snap ) )
295     settings_current.divmmc_enabled = 1;
296 }
297 
298 static void
divmmc_from_snapshot(libspectrum_snap * snap)299 divmmc_from_snapshot( libspectrum_snap *snap )
300 {
301   size_t i;
302 
303   if( !libspectrum_snap_divmmc_active( snap ) ) return;
304 
305   settings_current.divmmc_wp =
306     libspectrum_snap_divmmc_eprom_writeprotect( snap );
307   divxxx_control_write_internal( divmmc_state, libspectrum_snap_divmmc_control( snap ) );
308 
309   if( libspectrum_snap_divmmc_eprom( snap, 0 ) ) {
310     memcpy( divxxx_get_eprom( divmmc_state ),
311 	    libspectrum_snap_divmmc_eprom( snap, 0 ), DIVMMC_PAGE_LENGTH );
312   }
313 
314   for( i = 0; i < libspectrum_snap_divmmc_pages( snap ); i++ )
315     if( libspectrum_snap_divmmc_ram( snap, i ) ) {
316       libspectrum_byte *ram = divxxx_get_ram( divmmc_state, i );
317       memcpy( ram, libspectrum_snap_divmmc_ram( snap, i ), DIVMMC_PAGE_LENGTH );
318     }
319 
320   if( libspectrum_snap_divmmc_paged( snap ) ) {
321     divxxx_page( divmmc_state );
322   } else {
323     divxxx_unpage( divmmc_state );
324   }
325 }
326 
327 static void
divmmc_to_snapshot(libspectrum_snap * snap)328 divmmc_to_snapshot( libspectrum_snap *snap )
329 {
330   size_t i;
331   libspectrum_byte *buffer;
332 
333   if( !settings_current.divmmc_enabled ) return;
334 
335   libspectrum_snap_set_divmmc_active( snap, 1 );
336   libspectrum_snap_set_divmmc_eprom_writeprotect( snap,
337                                                   settings_current.divmmc_wp );
338   libspectrum_snap_set_divmmc_paged( snap, divxxx_get_active( divmmc_state ) );
339   libspectrum_snap_set_divmmc_control( snap, divxxx_get_control( divmmc_state ) );
340 
341   buffer = libspectrum_new( libspectrum_byte, DIVMMC_PAGE_LENGTH );
342 
343   memcpy( buffer, divxxx_get_eprom( divmmc_state ), DIVMMC_PAGE_LENGTH );
344   libspectrum_snap_set_divmmc_eprom( snap, 0, buffer );
345 
346   libspectrum_snap_set_divmmc_pages( snap, DIVMMC_PAGES );
347 
348   for( i = 0; i < DIVMMC_PAGES; i++ ) {
349 
350     buffer = libspectrum_new( libspectrum_byte, DIVMMC_PAGE_LENGTH );
351 
352     memcpy( buffer, divxxx_get_ram( divmmc_state, i ), DIVMMC_PAGE_LENGTH );
353     libspectrum_snap_set_divmmc_ram( snap, i, buffer );
354   }
355 }
356 
357 static void
divmmc_activate(void)358 divmmc_activate( void )
359 {
360   divxxx_activate( divmmc_state );
361 }
362 
363 static libspectrum_dword
get_control_register(void)364 get_control_register( void )
365 {
366   return divxxx_get_control( divmmc_state );
367 }
368 
369 static void
set_control_register(libspectrum_dword value)370 set_control_register( libspectrum_dword value )
371 {
372   divxxx_control_write_internal( divmmc_state, value & 0xff );
373 }
374 
375 int
divmmc_unittest(void)376 divmmc_unittest( void )
377 {
378   int r = 0;
379   int eprom_memory_source = divxxx_get_eprom_memory_source( divmmc_state );
380   int ram_memory_source = divxxx_get_ram_memory_source( divmmc_state );
381 
382   divmmc_set_automap( 1 );
383 
384   divmmc_control_write( 0x00e3, 0x80 );
385   r += unittests_assert_8k_page( 0x0000, eprom_memory_source, 0 );
386   r += unittests_assert_8k_page( 0x2000, ram_memory_source, 0 );
387   r += unittests_assert_16k_ram_page( 0x4000, 5 );
388   r += unittests_assert_16k_ram_page( 0x8000, 2 );
389   r += unittests_assert_16k_ram_page( 0xc000, 0 );
390 
391   divmmc_control_write( 0x00e3, 0x83 );
392   r += unittests_assert_8k_page( 0x0000, eprom_memory_source, 0 );
393   r += unittests_assert_8k_page( 0x2000, ram_memory_source, 3 );
394   r += unittests_assert_16k_ram_page( 0x4000, 5 );
395   r += unittests_assert_16k_ram_page( 0x8000, 2 );
396   r += unittests_assert_16k_ram_page( 0xc000, 0 );
397 
398   divmmc_control_write( 0x00e3, 0x40 );
399   r += unittests_assert_8k_page( 0x0000, ram_memory_source, 3 );
400   r += unittests_assert_8k_page( 0x2000, ram_memory_source, 0 );
401   r += unittests_assert_16k_ram_page( 0x4000, 5 );
402   r += unittests_assert_16k_ram_page( 0x8000, 2 );
403   r += unittests_assert_16k_ram_page( 0xc000, 0 );
404 
405   divmmc_control_write( 0x00e3, 0x02 );
406   r += unittests_assert_8k_page( 0x0000, ram_memory_source, 3 );
407   r += unittests_assert_8k_page( 0x2000, ram_memory_source, 2 );
408   r += unittests_assert_16k_ram_page( 0x4000, 5 );
409   r += unittests_assert_16k_ram_page( 0x8000, 2 );
410   r += unittests_assert_16k_ram_page( 0xc000, 0 );
411 
412   /* We have only 128 Kb of RAM (16 x 8 Kb pages), so setting all of bits 0-5
413      results in page 15 being selected */
414   divmmc_control_write( 0x00e3, 0x3f );
415   r += unittests_assert_8k_page( 0x0000, ram_memory_source, 3 );
416   r += unittests_assert_8k_page( 0x2000, ram_memory_source, 15 );
417   r += unittests_assert_16k_ram_page( 0x4000, 5 );
418   r += unittests_assert_16k_ram_page( 0x8000, 2 );
419   r += unittests_assert_16k_ram_page( 0xc000, 0 );
420 
421   divmmc_set_automap( 0 );
422 
423   r += unittests_paging_test_48( 2 );
424 
425   return r;
426 }
427