1 /* memory.c: Routines for accessing memory
2 Copyright (c) 1999-2012 Philip Kendall
3
4 $Id: memory.c 4857 2013-01-23 12:00:08Z 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 <string.h>
29
30 #include <libspectrum.h>
31
32 #include "debugger/debugger.h"
33 #include "display.h"
34 #include "fuse.h"
35 #include "machines/pentagon.h"
36 #include "machines/spec128.h"
37 #include "memory.h"
38 #include "module.h"
39 #include "peripherals/disk/opus.h"
40 #include "peripherals/spectranet.h"
41 #include "peripherals/ula.h"
42 #include "settings.h"
43 #include "spectrum.h"
44 #include "ui/ui.h"
45 #include "utils.h"
46
47 /* The various sources of memory available to us */
48 static GArray *memory_sources;
49
50 /* Some "well-known" memory sources */
51 int memory_source_rom; /* System ROM */
52 int memory_source_ram; /* System RAM */
53 int memory_source_dock; /* Timex DOCK */
54 int memory_source_exrom; /* Timex EXROM */
55 int memory_source_any; /* Used by the debugger to signify an absolute address */
56 int memory_source_none; /* No memory attached here */
57
58 /* Each RAM chunk accessible by the Z80 */
59 memory_page memory_map_read[MEMORY_PAGES_IN_64K];
60 memory_page memory_map_write[MEMORY_PAGES_IN_64K];
61
62 /* Standard mappings for the 'normal' RAM */
63 memory_page memory_map_ram[SPECTRUM_RAM_PAGES * MEMORY_PAGES_IN_16K];
64
65 /* Standard mappings for the ROMs */
66 memory_page memory_map_rom[SPECTRUM_ROM_PAGES * MEMORY_PAGES_IN_16K];
67
68 /* Some allocated memory */
69 typedef struct memory_pool_entry_t {
70 int persistent;
71 libspectrum_byte *memory;
72 } memory_pool_entry_t;
73
74 /* All the memory we've allocated for this machine */
75 static GSList *pool;
76
77 /* Which RAM page contains the current screen */
78 int memory_current_screen;
79
80 /* Which bits to look at when working out where the screen is */
81 libspectrum_word memory_screen_mask;
82
83 static void memory_from_snapshot( libspectrum_snap *snap );
84 static void memory_to_snapshot( libspectrum_snap *snap );
85
86 static module_info_t memory_module_info = {
87
88 NULL,
89 NULL,
90 NULL,
91 memory_from_snapshot,
92 memory_to_snapshot,
93
94 };
95
96 /* Set up the information about the normal page mappings.
97 Memory contention and usable pages vary from machine to machine and must
98 be set in the appropriate _reset function */
99 void
memory_init(void)100 memory_init( void )
101 {
102 size_t i, j;
103
104 memory_sources = g_array_new( FALSE, FALSE, sizeof( const char* ) );
105
106 memory_source_rom = memory_source_register( "ROM" );
107 memory_source_ram = memory_source_register( "RAM" );
108 memory_source_dock = memory_source_register( "Timex Dock" );
109 memory_source_exrom = memory_source_register( "Timex EXROM" );
110 memory_source_any = memory_source_register( "Absolute address" );
111 memory_source_none = memory_source_register( "None" );
112
113 /* Nothing in the memory pool as yet */
114 pool = NULL;
115
116 for( i = 0; i < SPECTRUM_ROM_PAGES; i++ )
117 for( j = 0; j < MEMORY_PAGES_IN_16K; j++ ) {
118 memory_page *page = &memory_map_rom[i * MEMORY_PAGES_IN_16K + j];
119 page->writable = 0;
120 page->contended = 0;
121 page->source = memory_source_rom;
122 }
123
124 for( i = 0; i < SPECTRUM_RAM_PAGES; i++ )
125 for( j = 0; j < MEMORY_PAGES_IN_16K; j++ ) {
126 memory_page *page = &memory_map_ram[i * MEMORY_PAGES_IN_16K + j];
127 page->page = &RAM[i][j * MEMORY_PAGE_SIZE];
128 page->page_num = i;
129 page->offset = j * MEMORY_PAGE_SIZE;
130 page->writable = 1;
131 page->source = memory_source_ram;
132 }
133
134 module_register( &memory_module_info );
135 }
136
137 static void
memory_pool_free_entry(gpointer data,gpointer user_data GCC_UNUSED)138 memory_pool_free_entry( gpointer data, gpointer user_data GCC_UNUSED )
139 {
140 memory_pool_entry_t *entry = data;
141 libspectrum_free( entry->memory );
142 libspectrum_free( entry );
143 }
144
145 /* Tidy-up function called at end of emulation */
146 void
memory_end(void)147 memory_end( void )
148 {
149 int i;
150 char *description;
151
152 /* Free all the memory we've allocated for this machine */
153 if( pool ) {
154 g_slist_foreach( pool, memory_pool_free_entry, NULL );
155 g_slist_free( pool );
156 pool = NULL;
157 }
158
159 /* Free memory source types */
160 if( memory_sources ) {
161 for( i = 0; i < memory_sources->len; i++ ) {
162 description = g_array_index( memory_sources, char *, i );
163 libspectrum_free( description );
164 }
165
166 g_array_free( memory_sources, TRUE );
167 memory_sources = NULL;
168 }
169 }
170
171 int
memory_source_register(const char * description)172 memory_source_register( const char *description )
173 {
174 const char *copy = utils_safe_strdup( description );
175
176 g_array_append_val( memory_sources, copy );
177
178 return memory_sources->len - 1;
179 }
180
181 const char*
memory_source_description(int source)182 memory_source_description( int source )
183 {
184 return g_array_index( memory_sources, const char*, source );
185 }
186
187 int
memory_source_find(const char * description)188 memory_source_find( const char *description )
189 {
190 int i, source = -1;
191
192 for( i = 0; i < memory_sources->len; i++ ) {
193 const char *found = g_array_index( memory_sources, const char*, i );
194 if( !strcasecmp( description, found ) ) {
195 source = i;
196 break;
197 }
198 }
199
200 return source;
201 }
202
203 /* Allocate some memory from the pool */
204 libspectrum_byte*
memory_pool_allocate(size_t length)205 memory_pool_allocate( size_t length )
206 {
207 return memory_pool_allocate_persistent( length, 0 );
208 }
209
210 libspectrum_byte*
memory_pool_allocate_persistent(size_t length,int persistent)211 memory_pool_allocate_persistent( size_t length, int persistent )
212 {
213 memory_pool_entry_t *entry;
214 libspectrum_byte *memory;
215
216 memory = libspectrum_malloc( length * sizeof( *memory ) );
217
218 entry = libspectrum_malloc( sizeof( *entry ) );
219
220 entry->persistent = persistent;
221 entry->memory = memory;
222
223 pool = g_slist_prepend( pool, entry );
224
225 return memory;
226 }
227
228 static gint
find_non_persistent(gconstpointer data,gconstpointer user_data GCC_UNUSED)229 find_non_persistent( gconstpointer data, gconstpointer user_data GCC_UNUSED )
230 {
231 const memory_pool_entry_t *entry = data;
232 return entry->persistent;
233 }
234
235 /* Free all non-persistent memory in the pool */
236 void
memory_pool_free(void)237 memory_pool_free( void )
238 {
239 GSList *ptr;
240
241 while( ( ptr = g_slist_find_custom( pool, NULL, find_non_persistent ) ) != NULL )
242 {
243 memory_pool_entry_t *entry = ptr->data;
244 libspectrum_free( entry->memory );
245 pool = g_slist_remove( pool, entry );
246 libspectrum_free( entry );
247 }
248 }
249
250 /* Set contention for 16K of RAM */
251 void
memory_ram_set_16k_contention(int page_num,int contended)252 memory_ram_set_16k_contention( int page_num, int contended )
253 {
254 int i;
255
256 for( i = 0; i < MEMORY_PAGES_IN_16K; i++ )
257 memory_map_ram[ page_num * MEMORY_PAGES_IN_16K + i ].contended = contended;
258 }
259
260 /* Map 16K of memory */
261 void
memory_map_16k(libspectrum_word address,memory_page source[],int page_num)262 memory_map_16k( libspectrum_word address, memory_page source[], int page_num )
263 {
264 int i;
265
266 for( i = 0; i < MEMORY_PAGES_IN_16K; i++ ) {
267 int page = ( address >> MEMORY_PAGE_SIZE_LOGARITHM ) + i;
268 memory_map_read[ page ] = memory_map_write[ page ] =
269 source[ page_num * MEMORY_PAGES_IN_16K + i ];
270 }
271 }
272
273 /* Map 8K of memory */
274 void
memory_map_8k(libspectrum_word address,memory_page source[],int page_num)275 memory_map_8k( libspectrum_word address, memory_page source[], int page_num )
276 {
277 int i;
278
279 for( i = 0; i < MEMORY_PAGES_IN_8K; i++ ) {
280 int page = ( address >> MEMORY_PAGE_SIZE_LOGARITHM ) + i;
281 memory_map_read[ page ] = memory_map_write[ page ] =
282 source[ page_num * MEMORY_PAGES_IN_8K + i ];
283 }
284 }
285
286 /* Map one page of memory */
287 void
memory_map_page(memory_page * source[],int page_num)288 memory_map_page( memory_page *source[], int page_num )
289 {
290 memory_map_read[ page_num ] = memory_map_write[ page_num ] =
291 *source[ page_num ];
292 }
293
294 /* Page in from /ROMCS */
295 void
memory_map_romcs(memory_page source[])296 memory_map_romcs( memory_page source[] )
297 {
298 int i;
299
300 for( i = 0; i < MEMORY_PAGES_IN_16K; i++ )
301 memory_map_read[i] = memory_map_write[i] = source[i];
302 }
303
304 /* Page in 8K from /ROMCS */
305 void
memory_map_romcs_8k(libspectrum_word address,memory_page source[])306 memory_map_romcs_8k( libspectrum_word address, memory_page source[] )
307 {
308 int i, start;
309
310 start = address >> MEMORY_PAGE_SIZE_LOGARITHM;
311 for( i = 0; i < MEMORY_PAGES_IN_8K; i++ )
312 memory_map_read[ start + i ] = memory_map_write[ start + i ] = source[ i ];
313 }
314
315 /* Page in 4K from /ROMCS */
316 void
memory_map_romcs_4k(libspectrum_word address,memory_page source[])317 memory_map_romcs_4k( libspectrum_word address, memory_page source[] )
318 {
319 int i, start;
320
321 start = address >> MEMORY_PAGE_SIZE_LOGARITHM;
322 for( i = 0; i < MEMORY_PAGES_IN_4K; i++ )
323 memory_map_read[ start + i ] = memory_map_write[ start + i ] = source[ i ];
324 }
325
326 libspectrum_byte
readbyte(libspectrum_word address)327 readbyte( libspectrum_word address )
328 {
329 libspectrum_word bank;
330 memory_page *mapping;
331
332 bank = address >> MEMORY_PAGE_SIZE_LOGARITHM;
333 mapping = &memory_map_read[ bank ];
334
335 if( debugger_mode != DEBUGGER_MODE_INACTIVE )
336 debugger_check( DEBUGGER_BREAKPOINT_TYPE_READ, address );
337
338 if( mapping->contended ) tstates += ula_contention[ tstates ];
339 tstates += 3;
340
341 if( opus_active && address >= 0x2800 && address < 0x3800 )
342 return opus_read( address );
343
344 if( spectranet_paged ) {
345 if( spectranet_w5100_paged_a && address >= 0x1000 && address < 0x2000 )
346 return spectranet_w5100_read( mapping, address );
347 if( spectranet_w5100_paged_b && address >= 0x2000 && address < 0x3000 )
348 return spectranet_w5100_read( mapping, address );
349 }
350
351 return mapping->page[ address & MEMORY_PAGE_SIZE_MASK ];
352 }
353
354 void
writebyte(libspectrum_word address,libspectrum_byte b)355 writebyte( libspectrum_word address, libspectrum_byte b )
356 {
357 libspectrum_word bank;
358 memory_page *mapping;
359
360 bank = address >> MEMORY_PAGE_SIZE_LOGARITHM;
361 mapping = &memory_map_write[ bank ];
362
363 if( debugger_mode != DEBUGGER_MODE_INACTIVE )
364 debugger_check( DEBUGGER_BREAKPOINT_TYPE_WRITE, address );
365
366 if( mapping->contended ) tstates += ula_contention[ tstates ];
367
368 tstates += 3;
369
370 writebyte_internal( address, b );
371 }
372
373 void
memory_display_dirty_pentagon_16_col(libspectrum_word address,libspectrum_byte b)374 memory_display_dirty_pentagon_16_col( libspectrum_word address,
375 libspectrum_byte b )
376 {
377 libspectrum_word bank = address >> MEMORY_PAGE_SIZE_LOGARITHM;
378 memory_page *mapping = &memory_map_write[ bank ];
379 libspectrum_word offset = address & MEMORY_PAGE_SIZE_MASK;
380 libspectrum_byte *memory = mapping->page;
381
382 /* The offset into the 16Kb RAM page (as opposed to the 8Kb chunk) */
383 libspectrum_word offset2 = offset + mapping->offset;
384
385 /* If this is a write to the current screen areas (and it actually changes
386 the destination), redraw that bit.
387 The trick here is that we need to check the home bank screen areas in
388 page 5 and 4 (if screen 1 is in use), and page 7 & 6 (if screen 2 is in
389 use) and both the standard and ALTDFILE areas of those pages
390 */
391 if( mapping->source == memory_source_ram &&
392 ( ( memory_current_screen == 5 &&
393 ( mapping->page_num == 5 || mapping->page_num == 4 ) ) ||
394 ( memory_current_screen == 7 &&
395 ( mapping->page_num == 7 || mapping->page_num == 6 ) ) ) &&
396 ( offset2 & 0xdfff ) < 0x1b00 &&
397 memory[ offset ] != b )
398 display_dirty_pentagon_16_col( offset2 );
399 }
400
401 void
memory_display_dirty_sinclair(libspectrum_word address,libspectrum_byte b)402 memory_display_dirty_sinclair( libspectrum_word address, libspectrum_byte b ) \
403 {
404 libspectrum_word bank = address >> MEMORY_PAGE_SIZE_LOGARITHM;
405 memory_page *mapping = &memory_map_write[ bank ];
406 libspectrum_word offset = address & MEMORY_PAGE_SIZE_MASK;
407 libspectrum_byte *memory = mapping->page;
408
409 /* The offset into the 16Kb RAM page (as opposed to the 8Kb chunk) */
410 libspectrum_word offset2 = offset + mapping->offset;
411
412 /* If this is a write to the current screen (and it actually changes
413 the destination), redraw that bit */
414 if( mapping->source == memory_source_ram &&
415 mapping->page_num == memory_current_screen &&
416 ( offset2 & memory_screen_mask ) < 0x1b00 &&
417 memory[ offset ] != b )
418 display_dirty( offset2 );
419 }
420
421 memory_display_dirty_fn memory_display_dirty;
422
423 void
writebyte_internal(libspectrum_word address,libspectrum_byte b)424 writebyte_internal( libspectrum_word address, libspectrum_byte b )
425 {
426 libspectrum_word bank = address >> MEMORY_PAGE_SIZE_LOGARITHM;
427 memory_page *mapping = &memory_map_write[ bank ];
428
429 if( spectranet_paged ) {
430 /* all writes need to be parsed by the flash rom emulation */
431 spectranet_flash_rom_write(address, b);
432
433 if( spectranet_w5100_paged_a && address >= 0x1000 && address < 0x2000 ) {
434 spectranet_w5100_write( mapping, address, b );
435 return;
436 }
437 if( spectranet_w5100_paged_b && address >= 0x2000 && address < 0x3000 ) {
438 spectranet_w5100_write( mapping, address, b );
439 return;
440 }
441 }
442
443 if( opus_active && address >= 0x2800 && address < 0x3800 ) {
444 opus_write( address, b );
445 } else if( mapping->writable ||
446 (mapping->source != memory_source_none &&
447 settings_current.writable_roms) ) {
448 libspectrum_word offset = address & MEMORY_PAGE_SIZE_MASK;
449 libspectrum_byte *memory = mapping->page;
450
451 memory_display_dirty( address, b );
452
453 memory[ offset ] = b;
454 }
455 }
456
457 void
memory_romcs_map(void)458 memory_romcs_map( void )
459 {
460 /* Nothing changes if /ROMCS is not set */
461 if( !machine_current->ram.romcs ) return;
462
463 /* FIXME: what should we do if more than one of these devices is
464 active? What happen in the real situation? e.g. if1+if2 with cartridge?
465
466 OK. in the Interface 1 service manual: p.: 1.2 par.: 1.3.1
467 All the additional software needed in IC2 (the if1 ROM). IC2 enable
468 is discussed in paragraph 1.2.2 above. In addition to control from
469 IC1 (the if1 ULA), the ROM maybe disabled by a device connected to
470 the (if1's) expansion connector J1. ROMCS2 from (B25), for example,
471 Interface 2 connected to J1 would disable both ROM IC2 (if1 ROM) and
472 the Spectrum ROM, via isolating diodes D10 and D9 respectively.
473
474 All comment in parenthesis added by me (Gergely Szasz).
475 The ROMCS2 (B25 conn) in Interface 1 J1 edge connector is in the
476 same position than ROMCS (B25 conn) in the Spectrum edge connector.
477
478 */
479
480 module_romcs();
481 }
482
483 static void
memory_from_snapshot(libspectrum_snap * snap)484 memory_from_snapshot( libspectrum_snap *snap )
485 {
486 size_t i;
487 int capabilities = machine_current->capabilities;
488
489 if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_PENT1024_MEMORY ) {
490 pentagon1024_memoryport_write( 0x7ffd,
491 libspectrum_snap_out_128_memoryport( snap ) );
492 pentagon1024_v22_memoryport_write( 0xeff7,
493 libspectrum_snap_out_plus3_memoryport( snap ) );
494 } else {
495 if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY )
496 spec128_memoryport_write( 0x7ffd,
497 libspectrum_snap_out_128_memoryport( snap ) );
498
499 if( ( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY ) ||
500 ( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY ) )
501 specplus3_memoryport2_write(
502 0x1ffd, libspectrum_snap_out_plus3_memoryport( snap )
503 );
504 }
505
506 for( i = 0; i < 64; i++ )
507 if( libspectrum_snap_pages( snap, i ) )
508 memcpy( RAM[i], libspectrum_snap_pages( snap, i ), 0x4000 );
509
510 if( libspectrum_snap_custom_rom( snap ) ) {
511 for( i = 0; i < libspectrum_snap_custom_rom_pages( snap ) && i < 4; i++ ) {
512 if( libspectrum_snap_roms( snap, i ) ) {
513 machine_load_rom_bank_from_buffer( memory_map_rom, i,
514 libspectrum_snap_roms( snap, i ),
515 libspectrum_snap_rom_length( snap, i ), 1 );
516 }
517 }
518 }
519 }
520
521 static void
write_rom_to_snap(libspectrum_snap * snap,int * current_rom_num,libspectrum_byte ** current_rom,size_t * rom_length)522 write_rom_to_snap( libspectrum_snap *snap, int *current_rom_num,
523 libspectrum_byte **current_rom, size_t *rom_length )
524 {
525 libspectrum_snap_set_roms( snap, *current_rom_num, *current_rom );
526 libspectrum_snap_set_rom_length( snap, *current_rom_num, *rom_length );
527 (*current_rom_num)++;
528 *current_rom = NULL;
529 }
530
531 /* Look at all ROM entries, to see if any are marked as being custom ROMs */
532 int
memory_custom_rom(void)533 memory_custom_rom( void )
534 {
535 size_t i;
536
537 for( i = 0; i < SPECTRUM_ROM_PAGES * MEMORY_PAGES_IN_16K; i++ )
538 if( memory_map_rom[ i ].save_to_snapshot )
539 return 1;
540
541 return 0;
542 }
543
544 /* Reset all ROM entries to being non-custom ROMs */
545 void
memory_reset(void)546 memory_reset( void )
547 {
548 size_t i;
549
550 for( i = 0; i < SPECTRUM_ROM_PAGES * MEMORY_PAGES_IN_16K; i++ )
551 memory_map_rom[ i ].save_to_snapshot = 0;
552 }
553
554 static void
memory_rom_to_snapshot(libspectrum_snap * snap)555 memory_rom_to_snapshot( libspectrum_snap *snap )
556 {
557 libspectrum_byte *current_rom = NULL;
558 int current_page_num = -1;
559 int current_rom_num = 0;
560 size_t rom_length = 0;
561 size_t i;
562
563 /* If we have custom ROMs trigger writing all roms to the snap */
564 if( !memory_custom_rom() ) return;
565
566 libspectrum_snap_set_custom_rom( snap, 1 );
567
568 /* write all ROMs to the snap */
569 for( i = 0; i < SPECTRUM_ROM_PAGES * MEMORY_PAGES_IN_16K; i++ ) {
570 if( memory_map_rom[ i ].page ) {
571 if( current_page_num != memory_map_rom[ i ].page_num ) {
572 if( current_rom )
573 write_rom_to_snap( snap, ¤t_rom_num, ¤t_rom, &rom_length );
574
575 /* Start a new ROM image */
576 rom_length = MEMORY_PAGE_SIZE;
577 current_rom = libspectrum_malloc( rom_length );
578
579 memcpy( current_rom, memory_map_rom[ i ].page, MEMORY_PAGE_SIZE );
580 current_page_num = memory_map_rom[ i ].page_num;
581 } else {
582 /* Extend the current ROM image */
583 current_rom = libspectrum_realloc( current_rom,
584 rom_length + MEMORY_PAGE_SIZE );
585
586 memcpy( current_rom + rom_length, memory_map_rom[ i ].page,
587 MEMORY_PAGE_SIZE );
588 rom_length += MEMORY_PAGE_SIZE;
589 }
590 }
591 }
592
593 if( current_rom )
594 write_rom_to_snap( snap, ¤t_rom_num, ¤t_rom, &rom_length );
595
596 libspectrum_snap_set_custom_rom_pages( snap, current_rom_num );
597 }
598
599 static void
memory_to_snapshot(libspectrum_snap * snap)600 memory_to_snapshot( libspectrum_snap *snap )
601 {
602 size_t i;
603 libspectrum_byte *buffer;
604
605 libspectrum_snap_set_out_128_memoryport( snap,
606 machine_current->ram.last_byte );
607 libspectrum_snap_set_out_plus3_memoryport( snap,
608 machine_current->ram.last_byte2 );
609
610 for( i = 0; i < 64; i++ ) {
611 if( RAM[i] != NULL ) {
612
613 buffer = libspectrum_malloc( 0x4000 * sizeof( libspectrum_byte ) );
614
615 memcpy( buffer, RAM[i], 0x4000 );
616 libspectrum_snap_set_pages( snap, i, buffer );
617 }
618 }
619
620 memory_rom_to_snapshot( snap );
621 }
622