1 /* z80_ops.c: Process the next opcode
2    Copyright (c) 1999-2005 Philip Kendall, Witold Filipczyk
3    Copyright (c) 2015 Stuart Brady
4    Copyright (c) 2015 Gergely Szasz
5    Copyright (c) 2015 Sergio Baldoví
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License along
18    with this program; if not, write to the Free Software Foundation, Inc.,
19    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21    Author contact information:
22 
23    E-mail: philip-fuse@shadowmagic.org.uk
24 
25 */
26 
27 #include <config.h>
28 
29 #include <stdio.h>
30 
31 #include "debugger/debugger.h"
32 #include "event.h"
33 #include "machine.h"
34 #include "memory_pages.h"
35 #include "periph.h"
36 #include "peripherals/disk/beta.h"
37 #include "peripherals/disk/didaktik.h"
38 #include "peripherals/disk/disciple.h"
39 #include "peripherals/disk/opus.h"
40 #include "peripherals/disk/plusd.h"
41 #include "peripherals/ide/divide.h"
42 #include "peripherals/ide/divmmc.h"
43 #include "peripherals/if1.h"
44 #include "peripherals/multiface.h"
45 #include "peripherals/spectranet.h"
46 #include "peripherals/ula.h"
47 #include "peripherals/usource.h"
48 #include "profile.h"
49 #include "rzx.h"
50 #include "settings.h"
51 #include "slt.h"
52 #include "svg.h"
53 #include "tape.h"
54 #include "z80.h"
55 
56 #include "z80_macros.h"
57 
58 #ifndef HAVE_ENOUGH_MEMORY
59 static int z80_cbxx( libspectrum_byte opcode2 );
60 static int z80_ddxx( libspectrum_byte opcode2 );
61 static int z80_edxx( libspectrum_byte opcode2 );
62 static int z80_fdxx( libspectrum_byte opcode2 );
63 static void z80_ddfdcbxx( libspectrum_byte opcode3 );
64 #endif				/* #ifndef HAVE_ENOUGH_MEMORY */
65 
66 /* Certain features (eg RZX playback trigged interrupts, the debugger,
67    TR-DOS ROM paging) can't be handled within the normal 'events'
68    framework as they don't happen at a specified tstate. In order to
69    support these, we basically need to check every opcode as to
70    whether they have occurred or not.
71 
72    There are (fairly common) circumstances under which we know that
73    the features will never occur (eg we will never get an interrupt
74    from RZX playback unless we're doing RZX playback), and we would
75    quite like to skip the check in this state. We can do this if we
76    use gcc's computed goto feature[1]. What follows is some
77    preprocessor hackery to moderately transparently do this while
78    still retaining the "normal" behaviour for non-gcc compilers.
79 
80    Ensure that the same arguments are given to respective
81    SETUP_CHECK() and CHECK() macros or everything will break.
82 
83    [1] see 'C Extensions', 'Labels as Values' in the gcc info page.
84 */
85 
86 #ifdef __GNUC__
87 
88 #define SETUP_CHECK( label, condition ) \
89   pos_##label,
90 #define SETUP_NEXT( label )
91 
92 enum {
93 #include "z80_checks.h"
94   numchecks
95 };
96 
97 #define CHECK( label, condition ) goto *cgoto[ pos_##label ]; label:
98 #define END_CHECK
99 
100 #else				/* #ifdef __GNUC__ */
101 
102 #define CHECK( label, condition ) if( condition ) {
103 #define END_CHECK }
104 
105 #endif				/* #ifdef __GNUC__ */
106 
107 #ifndef HAVE_ENOUGH_MEMORY
108 static libspectrum_byte opcode = 0x00;
109 #endif
110 
111 /* Execute Z80 opcodes until the next event */
112 void
z80_do_opcodes(void)113 z80_do_opcodes( void )
114 {
115 #ifdef HAVE_ENOUGH_MEMORY
116   libspectrum_byte opcode = 0x00;
117 #endif
118   libspectrum_byte last_Q;
119 
120   int even_m1 =
121     machine_current->capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_EVEN_M1;
122 
123 #ifdef __GNUC__
124 
125 #undef SETUP_CHECK
126 #define SETUP_CHECK( label, condition ) \
127   if( condition ) { cgoto[ next ] = &&label; next = pos_##label + 1; } \
128   check++;
129 
130 #undef SETUP_NEXT
131 #define SETUP_NEXT( label ) \
132   if( next != check ) { cgoto[ next ] = &&label; } \
133   next = check;
134 
135   void *cgoto[ numchecks ]; size_t next = 0; size_t check = 0;
136 
137 #include "z80_checks.h"
138 
139 #endif				/* #ifdef __GNUC__ */
140 
141   while( tstates < event_next_event ) {
142 
143     /* Profiler */
144     CHECK( profile, profile_active )
145 
146     profile_map( PC );
147 
148     END_CHECK
149 
150     /* If we're due an end of frame from RZX playback, generate one */
151     CHECK( rzx, rzx_playback )
152 
153     if( R + rzx_instructions_offset >= rzx_instruction_count ) {
154       event_add( tstates, spectrum_frame_event );
155       break;		/* And break out of the execution loop to let
156 			   the interrupt happen */
157     }
158 
159     END_CHECK
160 
161     /* Check if the debugger should become active at this point */
162     CHECK( debugger, debugger_mode != DEBUGGER_MODE_INACTIVE )
163 
164     if( debugger_check( DEBUGGER_BREAKPOINT_TYPE_EXECUTE, PC ) )
165       debugger_trap();
166 
167     END_CHECK
168 
169     CHECK( beta, beta_available )
170 
171 #define NOT_128_TYPE_OR_IS_48_TYPE ( !( machine_current->capabilities & \
172             LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) || \
173             machine_current->ram.current_rom )
174 
175     if( beta_active ) {
176       if( NOT_128_TYPE_OR_IS_48_TYPE && PC >= 16384 ) {
177 	beta_unpage();
178       }
179     } else if( ( PC & beta_pc_mask ) == beta_pc_value &&
180                NOT_128_TYPE_OR_IS_48_TYPE ) {
181       beta_page();
182     }
183 
184     END_CHECK
185 
186     CHECK( plusd, plusd_available )
187 
188     if( PC == 0x0008 || PC == 0x003a || PC == 0x0066 || PC == 0x028e ) {
189       plusd_page();
190     }
191 
192     END_CHECK
193 
194     CHECK( didaktik80, didaktik80_available )
195 
196     if( PC == 0x0000 || PC == 0x0008 ) {
197       didaktik80_page();
198     } else if( PC == 0x1700 ) {
199       didaktik80_unpage();
200     }
201 
202     END_CHECK
203 
204     CHECK( disciple, disciple_available )
205 
206     if( PC == 0x0001 || PC == 0x0008 || PC == 0x0066 || PC == 0x028e ) {
207       disciple_page();
208     }
209 
210     END_CHECK
211 
212     CHECK( usource, usource_available )
213 
214     if( PC == 0x2bae ) {
215       usource_toggle();
216     }
217 
218     END_CHECK
219 
220     CHECK( multiface, multiface_activated )
221 
222     if( PC == 0x0066 ) {
223       multiface_setic8();
224     }
225 
226     END_CHECK
227 
228     CHECK( if1p, if1_available )
229 
230     if( PC == 0x0008 || PC == 0x1708 ) {
231       if1_page();
232     }
233 
234     END_CHECK
235 
236     CHECK( divide_early, settings_current.divide_enabled )
237 
238     if( ( PC & 0xff00 ) == 0x3d00 ) {
239       divide_set_automap( 1 );
240     }
241 
242     END_CHECK
243 
244     CHECK( divmmc_early, settings_current.divmmc_enabled )
245 
246     if( ( PC & 0xff00 ) == 0x3d00 ) {
247       divmmc_set_automap( 1 );
248     }
249 
250     END_CHECK
251 
252     CHECK( spectranet_page, spectranet_available && !settings_current.spectranet_disable )
253 
254     if( PC == 0x0008 || ((PC & 0xfff8) == 0x3ff8) )
255       spectranet_page( 0 );
256 
257     if( PC == spectranet_programmable_trap &&
258       spectranet_programmable_trap_active )
259       event_add( 0, z80_nmi_event );
260 
261     END_CHECK
262 
263   opcode_delay:
264 
265     contend_read( PC, 4 );
266 
267     /* Check to see if M1 cycles happen on even tstates */
268     CHECK( evenm1, even_m1 )
269 
270     if( tstates & 1 ) {
271       if( ++tstates == event_next_event ) {
272 	break;
273       }
274     }
275 
276     END_CHECK
277 
278   run_opcode:
279     /* Do the instruction fetch; readbyte_internal used here to avoid
280        triggering read breakpoints */
281     opcode = readbyte_internal( PC );
282 
283     CHECK( if1u, if1_available )
284 
285     if( PC == 0x0700 ) {
286       if1_unpage();
287     }
288 
289     END_CHECK
290 
291     CHECK( divide_late, settings_current.divide_enabled )
292 
293     if( ( PC & 0xfff8 ) == 0x1ff8 ) {
294       divide_set_automap( 0 );
295     } else if( (PC == 0x0000) || (PC == 0x0008) || (PC == 0x0038)
296       || (PC == 0x0066) || (PC == 0x04c6) || (PC == 0x0562) ) {
297       divide_set_automap( 1 );
298     }
299 
300     END_CHECK
301 
302     CHECK( divmmc_late, settings_current.divmmc_enabled )
303 
304     if( ( PC & 0xfff8 ) == 0x1ff8 ) {
305       divmmc_set_automap( 0 );
306     } else if( (PC == 0x0000) || (PC == 0x0008) || (PC == 0x0038)
307       || (PC == 0x0066) || (PC == 0x04c6) || (PC == 0x0562) ) {
308       divmmc_set_automap( 1 );
309     }
310 
311     END_CHECK
312 
313     CHECK( opus, opus_available )
314 
315     if( opus_active ) {
316       if( PC == 0x1748 ) {
317         opus_unpage();
318       }
319     } else if( PC == 0x0008 || PC == 0x0048 || PC == 0x1708 ) {
320       opus_page();
321     }
322 
323     END_CHECK
324 
325     CHECK( spectranet_unpage, spectranet_available )
326 
327     if( PC == 0x007c )
328       spectranet_unpage();
329 
330     END_CHECK
331 
332     CHECK( z80_iff2_read, z80.iff2_read )
333 
334     z80.iff2_read = 0;
335     /* Execute *one* instruction before reevaluating the checks */
336     event_add( tstates, z80_nmos_iff2_event );
337 
338     END_CHECK
339 
340     CHECK( didaktik80snap, didaktik80_snap )
341 
342     if( PC == 0x0066 && !didaktik80_active ) {
343       opcode = 0xc7;	/* RST 00 */
344       didaktik80_snap = 0; /* FIXME: this should be a time-based reset */
345     }
346 
347     END_CHECK
348 
349     CHECK( svg_capture, svg_capture_active )
350 
351     svg_capture();
352 
353     END_CHECK
354 
355   end_opcode:
356     PC++; R++;
357     last_Q = Q; /* keep Q value from previous opcode for SCF and CCF */
358     Q = 0;      /* preempt Q value assuming next opcode doesn't set flags */
359 
360     switch(opcode) {
361 #include "z80/opcodes_base.c"
362     }
363 
364   }
365 
366 }
367 
368 #ifndef HAVE_ENOUGH_MEMORY
369 
370 static int
z80_cbxx(libspectrum_byte opcode2)371 z80_cbxx( libspectrum_byte opcode2 )
372 {
373   switch(opcode2) {
374 #include "z80/z80_cb.c"
375   }
376   return 0;
377 }
378 
379 static int
z80_ddxx(libspectrum_byte opcode2)380 z80_ddxx( libspectrum_byte opcode2 )
381 {
382   switch(opcode2) {
383 #define REGISTER  IX
384 #define REGISTERL IXL
385 #define REGISTERH IXH
386 #include "z80/z80_ddfd.c"
387 #undef REGISTERH
388 #undef REGISTERL
389 #undef REGISTER
390   }
391   return 0;
392 }
393 
394 static int
z80_edxx(libspectrum_byte opcode2)395 z80_edxx( libspectrum_byte opcode2 )
396 {
397   switch(opcode2) {
398 #include "z80/z80_ed.c"
399   }
400   return 0;
401 }
402 
403 static int
z80_fdxx(libspectrum_byte opcode2)404 z80_fdxx( libspectrum_byte opcode2 )
405 {
406   switch(opcode2) {
407 #define REGISTER  IY
408 #define REGISTERL IYL
409 #define REGISTERH IYH
410 #include "z80/z80_ddfd.c"
411 #undef REGISTERH
412 #undef REGISTERL
413 #undef REGISTER
414   }
415   return 0;
416 }
417 
418 static void
z80_ddfdcbxx(libspectrum_byte opcode3)419 z80_ddfdcbxx( libspectrum_byte opcode3 )
420 {
421   switch(opcode3) {
422 #include "z80/z80_ddfdcb.c"
423   }
424 }
425 
426 #endif			/* #ifndef HAVE_ENOUGH_MEMORY */
427