1 /* divxxx.c: Shared DivIDE/DivMMC interface routines
2    Copyright (c) 2005-2017 Matthew Westcott, Stuart Brady, Philip Kendall
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation, Inc.,
16    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18    Author contact information:
19 
20    E-mail: Philip Kendall <philip-fuse@shadowmagic.org.uk>
21 
22 */
23 
24 #include <config.h>
25 
26 #include <string.h>
27 
28 #include <libspectrum.h>
29 
30 #include "debugger/debugger.h"
31 #include "divxxx.h"
32 #include "machine.h"
33 #include "periph.h"
34 
35 static const libspectrum_byte DIVXXX_CONTROL_CONMEM = 0x80;
36 static const libspectrum_byte DIVXXX_CONTROL_MAPRAM = 0x40;
37 
38 #define DIVXXX_PAGE_LENGTH 0x2000
39 
40 struct divxxx_t {
41   libspectrum_byte control;
42 
43   int active;
44 
45   /* automap tracks opcode fetches to entry and exit points to determine
46      whether interface memory *would* be paged in at this moment if mapram / wp
47      flags allowed it */
48   int automap;
49 
50   /* True once memory has been allocated for this interface */
51   int memory_allocated;
52 
53   /* EPROM */
54   int eprom_memory_source;
55   memory_page memory_map_eprom[ MEMORY_PAGES_IN_8K ];
56   libspectrum_byte *eprom;
57 
58   /* RAM */
59   size_t ram_page_count;
60   int ram_memory_source;
61   memory_page **memory_map_ram;
62   libspectrum_byte **ram;
63 
64   /* The debugger paging events for this interface */
65   int page_event;
66   int unpage_event;
67 
68   /* References to the current settings for this interface */
69   const int *enabled;
70   const int *write_protect;
71 
72 };
73 
74 divxxx_t*
divxxx_alloc(const char * eprom_source_name,size_t ram_page_count,const char * ram_source_name,const char * event_type_string,const int * enabled,const int * write_protect)75 divxxx_alloc( const char *eprom_source_name, size_t ram_page_count,
76     const char *ram_source_name, const char *event_type_string,
77     const int *enabled, const int *write_protect )
78 {
79   size_t i, j;
80   divxxx_t *divxxx = libspectrum_new( divxxx_t, 1 );
81 
82   divxxx->control = 0;
83   divxxx->active = 0;
84   divxxx->automap = 0;
85 
86   divxxx->memory_allocated = 0;
87 
88   divxxx->eprom_memory_source = memory_source_register( eprom_source_name );
89   for( i = 0; i < MEMORY_PAGES_IN_8K; i++ ) {
90     memory_page *page = &divxxx->memory_map_eprom[i];
91     page->source = divxxx->eprom_memory_source;
92     page->contended = 0;
93     page->page_num = 0;
94   }
95   divxxx->eprom = NULL;
96 
97   divxxx->ram_page_count = ram_page_count;
98   divxxx->ram_memory_source = memory_source_register( ram_source_name );
99   divxxx->memory_map_ram =
100     libspectrum_new( memory_page*, divxxx->ram_page_count );
101   for( i = 0; i < divxxx->ram_page_count; i++ ) {
102     divxxx->memory_map_ram[i] =
103       libspectrum_new( memory_page, MEMORY_PAGES_IN_8K );
104     for( j = 0; j < MEMORY_PAGES_IN_8K; j++ ) {
105       memory_page *page = &divxxx->memory_map_ram[i][j];
106       page->source = divxxx->ram_memory_source;
107       page->contended = 0;
108       page->page_num = i;
109     }
110   }
111   divxxx->ram = NULL;
112 
113   periph_register_paging_events( event_type_string, &divxxx->page_event,
114                                  &divxxx->unpage_event );
115 
116   divxxx->enabled = enabled;
117   divxxx->write_protect = write_protect;
118 
119   return divxxx;
120 }
121 
122 void
divxxx_free(divxxx_t * divxxx)123 divxxx_free( divxxx_t *divxxx )
124 {
125   size_t i;
126 
127   for( i = 0; i < divxxx->ram_page_count; i++ )
128     libspectrum_free( divxxx->memory_map_ram[i] );
129   libspectrum_free( divxxx->memory_map_ram );
130   libspectrum_free( divxxx->ram );
131 
132   libspectrum_free( divxxx );
133 }
134 
135 libspectrum_byte
divxxx_get_control(divxxx_t * divxxx)136 divxxx_get_control( divxxx_t *divxxx )
137 {
138   return divxxx->control;
139 }
140 
141 int
divxxx_get_active(divxxx_t * divxxx)142 divxxx_get_active( divxxx_t *divxxx )
143 {
144   return divxxx->active;
145 }
146 
147 int
divxxx_get_eprom_memory_source(divxxx_t * divxxx)148 divxxx_get_eprom_memory_source( divxxx_t *divxxx )
149 {
150   return divxxx->eprom_memory_source;
151 }
152 
153 memory_page*
divxxx_get_eprom_page(divxxx_t * divxxx,size_t which)154 divxxx_get_eprom_page( divxxx_t *divxxx, size_t which )
155 {
156   return &divxxx->memory_map_eprom[ which ];
157 }
158 
159 libspectrum_byte*
divxxx_get_eprom(divxxx_t * divxxx)160 divxxx_get_eprom( divxxx_t *divxxx )
161 {
162   return divxxx->eprom;
163 }
164 
165 int
divxxx_get_ram_memory_source(divxxx_t * divxxx)166 divxxx_get_ram_memory_source( divxxx_t *divxxx )
167 {
168   return divxxx->ram_memory_source;
169 }
170 
171 libspectrum_byte*
divxxx_get_ram(divxxx_t * divxxx,size_t which)172 divxxx_get_ram( divxxx_t *divxxx, size_t which )
173 {
174   return divxxx->ram[ which ];
175 }
176 
177 /* DivIDE/DivMMC does not page in immediately on a reset condition (we do that by
178    trapping PC instead); however, it needs to perform housekeeping tasks upon
179    reset */
180 void
divxxx_reset(divxxx_t * divxxx,int hard_reset)181 divxxx_reset( divxxx_t *divxxx, int hard_reset )
182 {
183   int i;
184 
185   divxxx->active = 0;
186 
187   if( !*divxxx->enabled ) return;
188 
189   if( hard_reset ) {
190     divxxx->control = 0;
191 
192     if( divxxx->ram ) {
193       for( i = 0; i < divxxx->ram_page_count; i++ )
194         memset( divxxx->ram[i], 0, DIVXXX_PAGE_LENGTH );
195     }
196   } else {
197     divxxx->control &= DIVXXX_CONTROL_MAPRAM;
198   }
199   divxxx->automap = 0;
200   divxxx_refresh_page_state( divxxx );
201 }
202 
203 void
divxxx_activate(divxxx_t * divxxx)204 divxxx_activate( divxxx_t *divxxx )
205 {
206   if( !divxxx->memory_allocated ) {
207     int i, j;
208     libspectrum_byte *memory =
209       memory_pool_allocate_persistent( divxxx->ram_page_count * DIVXXX_PAGE_LENGTH, 1 );
210 
211     divxxx->ram = libspectrum_new( libspectrum_byte*, divxxx->ram_page_count );
212 
213     for( i = 0; i < divxxx->ram_page_count; i++ ) {
214       divxxx->ram[i] = memory + i * DIVXXX_PAGE_LENGTH;
215       for( j = 0; j < MEMORY_PAGES_IN_8K; j++ ) {
216         memory_page *page = &divxxx->memory_map_ram[i][j];
217         page->page = divxxx->ram[i] + j * MEMORY_PAGE_SIZE;
218         page->offset = j * MEMORY_PAGE_SIZE;
219       }
220     }
221 
222     divxxx->eprom = memory_pool_allocate_persistent( DIVXXX_PAGE_LENGTH, 1 );
223     memset( divxxx->eprom, 0xff, DIVXXX_PAGE_LENGTH );
224     for( i = 0; i < MEMORY_PAGES_IN_8K; i++ ) {
225       memory_page *page = divxxx_get_eprom_page( divxxx, i );
226       page->page = divxxx->eprom + i * MEMORY_PAGE_SIZE;
227       page->offset = i * MEMORY_PAGE_SIZE;
228     }
229 
230     divxxx->memory_allocated = 1;
231   }
232 }
233 
234 void
divxxx_control_write(divxxx_t * divxxx,libspectrum_byte data)235 divxxx_control_write( divxxx_t *divxxx, libspectrum_byte data )
236 {
237   int old_mapram;
238 
239   /* MAPRAM bit cannot be reset, only set */
240   old_mapram = divxxx->control & DIVXXX_CONTROL_MAPRAM;
241   divxxx_control_write_internal( divxxx, data | old_mapram );
242 }
243 
244 void
divxxx_control_write_internal(divxxx_t * divxxx,libspectrum_byte data)245 divxxx_control_write_internal( divxxx_t *divxxx, libspectrum_byte data )
246 {
247   divxxx->control = data;
248   divxxx_refresh_page_state( divxxx );
249 }
250 
251 void
divxxx_set_automap(divxxx_t * divxxx,int automap)252 divxxx_set_automap( divxxx_t *divxxx, int automap )
253 {
254   divxxx->automap = automap;
255   divxxx_refresh_page_state( divxxx );
256 }
257 
258 void
divxxx_refresh_page_state(divxxx_t * divxxx)259 divxxx_refresh_page_state( divxxx_t *divxxx )
260 {
261   if( divxxx->control & DIVXXX_CONTROL_CONMEM ) {
262     /* always paged in if conmem enabled */
263     divxxx_page( divxxx );
264   } else if( *divxxx->write_protect
265     || ( divxxx->control & DIVXXX_CONTROL_MAPRAM ) ) {
266     /* automap in effect */
267     if( divxxx->automap ) {
268       divxxx_page( divxxx );
269     } else {
270       divxxx_unpage( divxxx );
271     }
272   } else {
273     divxxx_unpage( divxxx );
274   }
275 }
276 
277 void
divxxx_memory_map(divxxx_t * divxxx)278 divxxx_memory_map( divxxx_t *divxxx )
279 {
280   int i;
281   int upper_ram_page;
282   int lower_page_writable, upper_page_writable;
283   memory_page *lower_page, *upper_page;
284 
285   if( !divxxx->active ) return;
286 
287   upper_ram_page = divxxx->control & (divxxx->ram_page_count - 1);
288 
289   if( divxxx->control & DIVXXX_CONTROL_CONMEM ) {
290     lower_page = divxxx->memory_map_eprom;
291     lower_page_writable = !*divxxx->write_protect;
292     upper_page = divxxx->memory_map_ram[ upper_ram_page ];
293     upper_page_writable = 1;
294   } else {
295     if( divxxx->control & DIVXXX_CONTROL_MAPRAM ) {
296       lower_page = divxxx->memory_map_ram[3];
297       lower_page_writable = 0;
298       upper_page = divxxx->memory_map_ram[ upper_ram_page ];
299       upper_page_writable = ( upper_ram_page != 3 );
300     } else {
301       lower_page = divxxx->memory_map_eprom;
302       lower_page_writable = 0;
303       upper_page = divxxx->memory_map_ram[ upper_ram_page ];
304       upper_page_writable = 1;
305     }
306   }
307 
308   for( i = 0; i < MEMORY_PAGES_IN_8K; i++ ) {
309     lower_page[i].writable = lower_page_writable;
310     upper_page[i].writable = upper_page_writable;
311   }
312 
313   memory_map_romcs_8k( 0x0000, lower_page );
314   memory_map_romcs_8k( 0x2000, upper_page );
315 }
316 
317 void
divxxx_page(divxxx_t * divxxx)318 divxxx_page( divxxx_t *divxxx )
319 {
320   divxxx->active = 1;
321   machine_current->ram.romcs = 1;
322   machine_current->memory_map();
323 
324   debugger_event( divxxx->page_event );
325 }
326 
327 void
divxxx_unpage(divxxx_t * divxxx)328 divxxx_unpage( divxxx_t *divxxx )
329 {
330   divxxx->active = 0;
331   machine_current->ram.romcs = 0;
332   machine_current->memory_map();
333 
334   debugger_event( divxxx->unpage_event );
335 }
336