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