/* memory.c: Routines for accessing memory Copyright (c) 1999-2004 Philip Kendall $Id: memory.c,v 1.42 2007/02/02 16:21:51 pak21 Exp $ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author contact information: E-mail: philip-fuse@shadowmagic.org.uk */ #include #include #include #include "debugger/debugger.h" #include "display.h" #include "divide.h" #include "fuse.h" #include "if1.h" #include "if2.h" #include "machines/spec128.h" #include "memory.h" #include "settings.h" #include "spectrum.h" #include "ui/ui.h" #include "ula.h" #include "zxatasp.h" #include "zxcf.h" /* Each 8Kb RAM chunk accessible by the Z80 */ memory_page memory_map_read[8]; memory_page memory_map_write[8]; /* Two 8Kb memory chunks accessible by the Z80 when /ROMCS is low */ memory_page memory_map_romcs[2]; /* Mappings for the 'home' (normal ROM/RAM) pages, the Timex DOCK and the Timex EXROM */ memory_page *memory_map_home[8]; memory_page *memory_map_dock[8]; memory_page *memory_map_exrom[8]; /* Standard mappings for the 'normal' RAM */ memory_page memory_map_ram[ 2 * SPECTRUM_RAM_PAGES ]; /* Standard mappings for the ROMs */ memory_page memory_map_rom[8]; /* All the memory we've allocated for this machine */ static GSList *pool; /* Which RAM page contains the current screen */ int memory_current_screen; /* Which bits to look at when working out where the screen is */ libspectrum_word memory_screen_mask; /* Set up the information about the normal page mappings. Memory contention and useable pages vary from machine to machine and must be set in the appropriate _reset function */ int memory_init( void ) { size_t i; memory_page *mapping1, *mapping2; /* Nothing in the memory pool as yet */ pool = NULL; for( i = 0; i < 8; i++ ) { mapping1 = &memory_map_rom[ i ]; mapping1->page = NULL; mapping1->writable = 0; mapping1->bank = MEMORY_BANK_HOME; mapping1->page_num = i; mapping1->source = MEMORY_SOURCE_SYSTEM; } for( i = 0; i < SPECTRUM_RAM_PAGES; i++ ) { mapping1 = &memory_map_ram[ 2 * i ]; mapping2 = &memory_map_ram[ 2 * i + 1 ]; mapping1->page = &RAM[i][ 0x0000 ]; mapping2->page = &RAM[i][ MEMORY_PAGE_SIZE ]; mapping1->writable = mapping2->writable = 0; mapping1->bank = mapping2->bank = MEMORY_BANK_HOME; mapping1->page_num = mapping2->page_num = i; mapping1->offset = 0x0000; mapping2->offset = MEMORY_PAGE_SIZE; mapping1->source = mapping2->source = MEMORY_SOURCE_SYSTEM; } /* Just initialise these with something */ for( i = 0; i < 8; i++ ) memory_map_home[i] = memory_map_dock[i] = memory_map_exrom[i] = &memory_map_ram[0]; for( i = 0; i < 2; i++ ) memory_map_romcs[i].bank = MEMORY_BANK_ROMCS; return 0; } /* Allocate some memory from the pool */ libspectrum_byte* memory_pool_allocate( size_t length ) { libspectrum_byte *ptr; ptr = malloc( length * sizeof( libspectrum_byte ) ); if( !ptr ) { ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__, __LINE__ ); return NULL; } pool = g_slist_prepend( pool, ptr ); return ptr; } static void free_memory( gpointer data, gpointer user_data ) { free( data ); } void memory_pool_free( void ) { g_slist_foreach( pool, free_memory, NULL ); g_slist_free( pool ); pool = NULL; } const char* memory_bank_name( memory_page *page ) { switch( page->bank ) { case MEMORY_BANK_NONE: return "Empty"; case MEMORY_BANK_HOME: return page->writable ? "RAM" : "ROM"; case MEMORY_BANK_DOCK: return "Dock"; case MEMORY_BANK_EXROM: return "Exrom"; case MEMORY_BANK_ROMCS: return "Chip Select"; } return "[Undefined]"; } libspectrum_byte readbyte( libspectrum_word address ) { libspectrum_word bank; memory_page *mapping; bank = address >> 13; mapping = &memory_map_read[ bank ]; if( debugger_mode != DEBUGGER_MODE_INACTIVE && debugger_check( DEBUGGER_BREAKPOINT_TYPE_READ, address ) ) debugger_mode = DEBUGGER_MODE_HALTED; if( mapping->contended ) tstates += ula_contention[ tstates ]; tstates += 3; return mapping->page[ address & 0x1fff ]; } void writebyte( libspectrum_word address, libspectrum_byte b ) { libspectrum_word bank; memory_page *mapping; bank = address >> 13; mapping = &memory_map_write[ bank ]; if( debugger_mode != DEBUGGER_MODE_INACTIVE && debugger_check( DEBUGGER_BREAKPOINT_TYPE_WRITE, address ) ) debugger_mode = DEBUGGER_MODE_HALTED; if( mapping->contended ) tstates += ula_contention[ tstates ]; tstates += 3; writebyte_internal( address, b ); } void writebyte_internal( libspectrum_word address, libspectrum_byte b ) { libspectrum_word bank, offset; memory_page *mapping; libspectrum_byte *memory; bank = address >> 13; offset = address & 0x1fff; mapping = &memory_map_write[ bank ]; memory = mapping->page; if( mapping->writable || settings_current.writable_roms ) { /* The offset into the 16Kb RAM page (as opposed to the 8Kb chunk) */ libspectrum_word offset2 = offset + mapping->offset; /* If this is a write to the current screen (and it actually changes the destination), redraw that bit */ if( mapping->bank == MEMORY_BANK_HOME && mapping->page_num == memory_current_screen && ( offset2 & memory_screen_mask ) < 0x1b00 && memory[ offset ] != b ) display_dirty( offset2 ); memory[ offset ] = b; } } void memory_romcs_map( void ) { /* Nothing changes if /ROMCS is not set */ if( !machine_current->ram.romcs ) return; /* FIXME: what should we do if more than one of these devices is active? What happen in the real situation? e.g. if1+if2 with cartridge? OK. in the Interface 1 service manual: p.: 1.2 par.: 1.3.1 All the additional software needed in IC2 (the if1 ROM). IC2 enable is discussed in paragraph 1.2.2 above. In addition to control from IC1 (the if1 ULA), the ROM maybe disabled by a device connected to the (if1's) expansion connector J1. ROMCS2 from (B25), for example, Interface 2 connected to J1 would disable both ROM IC2 (if1 ROM) and the Spectrum ROM, via isolating diodes D10 and D9 respectively. All comment in paranthesis added by me (Gergely Szasz). The ROMCS2 (B25 conn) in Interface 1 J1 edge connector is in the same position than ROMCS (B25 conn) in the Spectrum edge connector. */ if( machine_current->capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TRDOS_DISK ) trdos_memory_map(); if( if1_active ) if1_memory_map(); /* if2 is superior */ if( if2_active ) if2_memory_map(); if( settings_current.zxatasp_active ) zxatasp_memory_map(); if( settings_current.zxcf_active ) zxcf_memory_map(); if( divide_active ) divide_memory_map(); } int memory_ram_from_snapshot( libspectrum_snap *snap, int capabilities ) { size_t i; if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) spec128_memoryport_write( 0x7ffd, libspectrum_snap_out_128_memoryport( snap ) ); if( ( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY ) || ( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY ) ) specplus3_memoryport2_write( 0x1ffd, libspectrum_snap_out_plus3_memoryport( snap ) ); for( i = 0; i < 16; i++ ) if( libspectrum_snap_pages( snap, i ) ) memcpy( RAM[i], libspectrum_snap_pages( snap, i ), 0x4000 ); return 0; } int memory_ram_to_snapshot( libspectrum_snap *snap ) { size_t i; libspectrum_byte *buffer; libspectrum_snap_set_out_128_memoryport( snap, machine_current->ram.last_byte ); libspectrum_snap_set_out_plus3_memoryport( snap, machine_current->ram.last_byte2 ); for( i = 0; i < 16; i++ ) { if( RAM[i] != NULL ) { buffer = malloc( 0x4000 * sizeof( libspectrum_byte ) ); if( !buffer ) { ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__, __LINE__ ); return 1; } memcpy( buffer, RAM[i], 0x4000 ); libspectrum_snap_set_pages( snap, i, buffer ); } } return 0; }