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