1 /* scld.c: Routines for handling the Timex SCLD
2 Copyright (c) 2002-2011 Fredrick Meunier, Philip Kendall, Witold Filipczyk
3
4 $Id: scld.c 4926 2013-05-05 07:58:18Z sbaldovi $
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 Philip: philip-fuse@shadowmagic.org.uk
23
24 Fred: fredm@spamcop.net
25
26 */
27
28 #include <config.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "compat.h"
35 #include "dck.h"
36 #include "display.h"
37 #include "machine.h"
38 #include "memory.h"
39 #include "module.h"
40 #include "periph.h"
41 #include "scld.h"
42 #include "spectrum.h"
43 #include "ui/ui.h"
44 #include "z80/z80.h"
45
46 scld scld_last_dec; /* The last byte sent to Timex DEC port */
47
48 libspectrum_byte scld_last_hsr = 0; /* The last byte sent to Timex HSR port */
49
50 memory_page * timex_home[MEMORY_PAGES_IN_64K];
51 memory_page timex_exrom[MEMORY_PAGES_IN_64K];
52 memory_page timex_dock[MEMORY_PAGES_IN_64K];
53
54 static void scld_reset( int hard_reset );
55 static void scld_from_snapshot( libspectrum_snap *snap );
56 static void scld_to_snapshot( libspectrum_snap *snap );
57
58 static module_info_t scld_module_info = {
59
60 scld_reset,
61 NULL,
62 NULL,
63 scld_from_snapshot,
64 scld_to_snapshot,
65
66 };
67
68 static libspectrum_byte scld_dec_read( libspectrum_word port, int *attached );
69 static libspectrum_byte scld_hsr_read( libspectrum_word port, int *attached );
70
71 static const periph_port_t scld_ports[] = {
72 { 0x00ff, 0x00f4, scld_hsr_read, scld_hsr_write },
73 { 0x00ff, 0x00ff, scld_dec_read, scld_dec_write },
74 { 0, 0, NULL, NULL }
75 };
76
77 static const periph_t scld_periph = {
78 NULL,
79 scld_ports,
80 0,
81 NULL
82 };
83
84 void
scld_init(void)85 scld_init( void )
86 {
87 module_register( &scld_module_info );
88 periph_register( PERIPH_TYPE_SCLD, &scld_periph );
89 }
90
91 static libspectrum_byte
scld_dec_read(libspectrum_word port GCC_UNUSED,int * attached)92 scld_dec_read( libspectrum_word port GCC_UNUSED, int *attached )
93 {
94 *attached = 1;
95
96 return scld_last_dec.byte;
97 }
98
99 void
scld_dec_write(libspectrum_word port GCC_UNUSED,libspectrum_byte b)100 scld_dec_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
101 {
102 scld old_dec = scld_last_dec;
103 scld new_dec;
104 libspectrum_byte ink,paper;
105
106 /* We use new_dec as we don't want to have the new colours, modes etc.
107 to take effect until we have updated the critical region */
108 new_dec.byte = b;
109
110 /* If we changed the active screen, or change the colour in hires
111 * mode, update the critical region and mark the entire display file as
112 * dirty so we redraw it on the next pass */
113 if( new_dec.mask.scrnmode != old_dec.mask.scrnmode ||
114 new_dec.name.hires != old_dec.name.hires ||
115 ( new_dec.name.hires &&
116 ( new_dec.mask.hirescol != old_dec.mask.hirescol ) ) ) {
117 display_update_critical( 0, 0 );
118 display_refresh_main_screen();
119 }
120
121 /* Commit change to scld_last_dec */
122 scld_last_dec = new_dec;
123
124 /* If we just reenabled interrupts, check for a retriggered interrupt */
125 if( old_dec.name.intdisable && !scld_last_dec.name.intdisable )
126 z80_interrupt();
127
128 if( scld_last_dec.name.altmembank != old_dec.name.altmembank )
129 machine_current->memory_map();
130
131 display_parse_attr( hires_get_attr(), &ink, &paper );
132 display_set_hires_border( paper );
133 }
134
135 static void
scld_reset(int hard_reset GCC_UNUSED)136 scld_reset( int hard_reset GCC_UNUSED )
137 {
138 scld_last_dec.byte = 0;
139 }
140
141 void
scld_hsr_write(libspectrum_word port GCC_UNUSED,libspectrum_byte b)142 scld_hsr_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
143 {
144 scld_last_hsr = b;
145
146 machine_current->memory_map();
147 }
148
149 static libspectrum_byte
scld_hsr_read(libspectrum_word port GCC_UNUSED,int * attached)150 scld_hsr_read( libspectrum_word port GCC_UNUSED, int *attached )
151 {
152 *attached = 1;
153
154 return scld_last_hsr;
155 }
156
157 libspectrum_byte
hires_get_attr(void)158 hires_get_attr( void )
159 {
160 return( hires_convert_dec( scld_last_dec.byte ) );
161 }
162
163 libspectrum_byte
hires_convert_dec(libspectrum_byte attr)164 hires_convert_dec( libspectrum_byte attr )
165 {
166 scld colour;
167
168 colour.byte = attr;
169
170 switch ( colour.mask.hirescol )
171 {
172 case BLACKWHITE: return 0x47;
173 case BLUEYELLOW: return 0x4e;
174 case REDCYAN: return 0x55;
175 case MAGENTAGREEN: return 0x5c;
176 case GREENMAGENTA: return 0x63;
177 case CYANRED: return 0x6a;
178 case YELLOWBLUE: return 0x71;
179 default: return 0x78; /* WHITEBLACK */
180 }
181 }
182
183 void
scld_memory_map(void)184 scld_memory_map( void )
185 {
186 int i;
187 memory_page *exrom_dock;
188
189 exrom_dock =
190 scld_last_dec.name.altmembank ? timex_exrom : timex_dock;
191
192 for( i = 0; i < 8; i++ )
193 if( scld_last_hsr & (1 << i) )
194 memory_map_8k( i * 0x2000, exrom_dock, i );
195 }
196
197 /* Initialise the memory map to point to the home bank */
198 void
scld_memory_map_home(void)199 scld_memory_map_home( void )
200 {
201 int i;
202
203 for( i = 0; i < MEMORY_PAGES_IN_64K; i++ )
204 memory_map_page( timex_home, i );
205 }
206
207 static void
scld_dock_exrom_from_snapshot(memory_page * dest,int page_num,int writable,void * source)208 scld_dock_exrom_from_snapshot( memory_page *dest, int page_num, int writable,
209 void *source )
210 {
211 int i;
212 libspectrum_byte *data = memory_pool_allocate( 0x2000 );
213
214 memcpy( data, source, 0x2000 );
215
216 for( i = 0; i < MEMORY_PAGES_IN_8K; i++ ) {
217 memory_page *page = &dest[ page_num * MEMORY_PAGES_IN_8K + i ];
218 page->offset = i * MEMORY_PAGE_SIZE;
219 page->page_num = page_num;
220 page->writable = writable;
221 page->page = data + page->offset;
222 page->save_to_snapshot = 1;
223 }
224 }
225
226 static void
scld_from_snapshot(libspectrum_snap * snap)227 scld_from_snapshot( libspectrum_snap *snap )
228 {
229 size_t i;
230 int capabilities = machine_current->capabilities;
231
232 if( capabilities & ( LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY |
233 LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY ) )
234 scld_hsr_write( 0x00f4, libspectrum_snap_out_scld_hsr( snap ) );
235
236 if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_VIDEO )
237 scld_dec_write( 0x00ff, libspectrum_snap_out_scld_dec( snap ) );
238
239 if( libspectrum_snap_dock_active( snap ) ) {
240
241 dck_active = 1;
242
243 for( i = 0; i < 8; i++ ) {
244
245 if( libspectrum_snap_dock_cart( snap, i ) )
246 scld_dock_exrom_from_snapshot( timex_dock, i,
247 libspectrum_snap_dock_ram( snap, i ),
248 libspectrum_snap_dock_cart( snap, i ) );
249
250 if( libspectrum_snap_exrom_cart( snap, i ) )
251 scld_dock_exrom_from_snapshot( timex_exrom, i,
252 libspectrum_snap_exrom_ram( snap, i ),
253 libspectrum_snap_exrom_cart( snap, i ) );
254
255 }
256
257 if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_DOCK )
258 ui_menu_activate( UI_MENU_ITEM_MEDIA_CARTRIDGE_DOCK_EJECT, 1 );
259
260 machine_current->memory_map();
261 }
262
263 }
264
265 static void
scld_to_snapshot(libspectrum_snap * snap)266 scld_to_snapshot( libspectrum_snap *snap )
267 {
268 size_t i;
269 libspectrum_byte *buffer;
270
271 libspectrum_snap_set_out_scld_hsr( snap, scld_last_hsr );
272 libspectrum_snap_set_out_scld_dec( snap, scld_last_dec.byte );
273
274 if( dck_active ) {
275
276 libspectrum_snap_set_dock_active( snap, 1 );
277
278 for( i = 0; i < 8; i++ ) {
279
280 memory_page *exrom_base = &timex_exrom[i * MEMORY_PAGES_IN_8K],
281 *dock_base = &timex_dock[i * MEMORY_PAGES_IN_8K];
282 int j;
283
284 /* In theory, Fuse could somehow have set up a memory map in which the
285 first 4Kb of a DOCK page is writable, but the second 4Kb is not, and
286 this structure is not representable in a snapshot.
287
288 However, there's currently no code which could produce this situation
289 so for simplicity's sake, we assume the properties of the lowest page
290 in the 8Kb chunk apply to all the pages */
291
292 if( exrom_base->save_to_snapshot || exrom_base->writable ) {
293 buffer = malloc( 0x2000 * sizeof( libspectrum_byte ) );
294 if( !buffer ) {
295 ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__,
296 __LINE__ );
297 return;
298 }
299
300 libspectrum_snap_set_exrom_ram( snap, i, exrom_base->writable );
301 for( j = 0; j < MEMORY_PAGES_IN_8K; j++ ) {
302 memory_page *page = exrom_base + j;
303 memcpy( buffer + j * MEMORY_PAGE_SIZE, page->page, MEMORY_PAGE_SIZE );
304 }
305 libspectrum_snap_set_exrom_cart( snap, i, buffer );
306 }
307
308 if( dock_base->save_to_snapshot || dock_base->writable ) {
309 buffer = malloc( 0x2000 * sizeof( libspectrum_byte ) );
310 if( !buffer ) {
311 ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__,
312 __LINE__ );
313 return;
314 }
315
316 libspectrum_snap_set_dock_ram( snap, i, dock_base->writable );
317 for( j = 0; j < MEMORY_PAGES_IN_8K; j++ ) {
318 memory_page *page = dock_base + j;
319 memcpy( buffer + j * MEMORY_PAGE_SIZE, page->page, MEMORY_PAGE_SIZE );
320 }
321 libspectrum_snap_set_dock_cart( snap, i, buffer );
322 }
323
324 }
325
326 }
327 }
328
329 /* Map 16K of memory and record default mapping for dock */
330 void
scld_home_map_16k(libspectrum_word address,memory_page source[],int page_num)331 scld_home_map_16k( libspectrum_word address, memory_page source[],
332 int page_num )
333 {
334 int i;
335
336 memory_map_16k( address, source, page_num );
337
338 for( i = 0; i < MEMORY_PAGES_IN_16K; i++ ) {
339 int page = ( address >> MEMORY_PAGE_SIZE_LOGARITHM ) + i;
340 timex_home[ page ] = &source[ page_num * MEMORY_PAGES_IN_16K + i ];
341 }
342 }
343