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