1 /* z80.c: z80 supplementary functions
2 Copyright (c) 1999-2013 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-fuse@shadowmagic.org.uk
21
22 */
23
24 #include <config.h>
25
26 #include <libspectrum.h>
27
28 #include "event.h"
29 #include "fuse.h"
30 #include "memory.h"
31 #include "module.h"
32 #include "peripherals/scld.h"
33 #include "peripherals/spectranet.h"
34 #include "rzx.h"
35 #include "spectrum.h"
36 #include "ui/ui.h"
37 #include "z80.h"
38 #include "z80_macros.h"
39
40 /* Whether a half carry occurred or not can be determined by looking at
41 the 3rd bit of the two arguments and the result; these are hashed
42 into this table in the form r12, where r is the 3rd bit of the
43 result, 1 is the 3rd bit of the 1st argument and 2 is the
44 third bit of the 2nd argument; the tables differ for add and subtract
45 operations */
46 const libspectrum_byte halfcarry_add_table[] =
47 { 0, FLAG_H, FLAG_H, FLAG_H, 0, 0, 0, FLAG_H };
48 const libspectrum_byte halfcarry_sub_table[] =
49 { 0, 0, FLAG_H, 0, FLAG_H, 0, FLAG_H, FLAG_H };
50
51 /* Similarly, overflow can be determined by looking at the 7th bits; again
52 the hash into this table is r12 */
53 const libspectrum_byte overflow_add_table[] = { 0, 0, 0, FLAG_V, FLAG_V, 0, 0, 0 };
54 const libspectrum_byte overflow_sub_table[] = { 0, FLAG_V, 0, 0, 0, 0, FLAG_V, 0 };
55
56 /* Some more tables; initialised in z80_init_tables() */
57
58 libspectrum_byte sz53_table[0x100]; /* The S, Z, 5 and 3 bits of the index */
59 libspectrum_byte parity_table[0x100]; /* The parity of the lookup value */
60 libspectrum_byte sz53p_table[0x100]; /* OR the above two tables together */
61
62 /* This is what everything acts on! */
63 processor z80;
64
65 int z80_interrupt_event, z80_nmi_event;
66
67 static void z80_init_tables(void);
68 static void z80_from_snapshot( libspectrum_snap *snap );
69 static void z80_to_snapshot( libspectrum_snap *snap );
70 static void z80_nmi( libspectrum_dword ts, int type, void *user_data );
71
72 static module_info_t z80_module_info = {
73
74 z80_reset,
75 NULL,
76 NULL,
77 z80_from_snapshot,
78 z80_to_snapshot,
79
80 };
81
82 static void
z80_interrupt_event_fn(libspectrum_dword tstates,int type,void * user_data)83 z80_interrupt_event_fn( libspectrum_dword tstates, int type, void *user_data )
84 {
85 /* Retriggered interrupt; firstly, ignore if we're doing RZX playback
86 as all interrupts are generated by the RZX code */
87 if( rzx_playback ) return;
88
89 /* Otherwise, see if we actually accept an interrupt. If we do and
90 we're doing RZX recording, store a frame */
91 if( z80_interrupt() ) rzx_frame();
92 }
93
94 /* Set up the z80 emulation */
95 void
z80_init(void)96 z80_init( void )
97 {
98 z80_init_tables();
99
100 z80_interrupt_event = event_register( z80_interrupt_event_fn,
101 "Retriggered interrupt" );
102 z80_nmi_event = event_register( z80_nmi, "Non-maskable interrupt" );
103
104 module_register( &z80_module_info );
105 }
106
107 /* Initalise the tables used to set flags */
z80_init_tables(void)108 static void z80_init_tables(void)
109 {
110 int i,j,k;
111 libspectrum_byte parity;
112
113 for(i=0;i<0x100;i++) {
114 sz53_table[i]= i & ( FLAG_3 | FLAG_5 | FLAG_S );
115 j=i; parity=0;
116 for(k=0;k<8;k++) { parity ^= j & 1; j >>=1; }
117 parity_table[i]= ( parity ? 0 : FLAG_P );
118 sz53p_table[i] = sz53_table[i] | parity_table[i];
119 }
120
121 sz53_table[0] |= FLAG_Z;
122 sz53p_table[0] |= FLAG_Z;
123
124 }
125
126 /* Reset the z80 */
127 void
z80_reset(int hard_reset GCC_UNUSED)128 z80_reset( int hard_reset GCC_UNUSED )
129 {
130 AF =BC =DE =HL =0;
131 AF_=BC_=DE_=HL_=0;
132 IX=IY=0;
133 I=R=R7=0;
134 SP=PC=0;
135 IFF1=IFF2=IM=0;
136 z80.halted=0;
137
138 z80.interrupts_enabled_at = -1;
139 }
140
141 /* Process a z80 maskable interrupt */
142 int
z80_interrupt(void)143 z80_interrupt( void )
144 {
145 /* An interrupt will occur if IFF1 is set and the /INT line hasn't
146 gone high again. On a Timex machine, we also need the SCLD's
147 INTDISABLE to be clear */
148 if( IFF1 &&
149 tstates < machine_current->timings.interrupt_length &&
150 !scld_last_dec.name.intdisable ) {
151
152 /* If interrupts have just been enabled, don't accept the interrupt now,
153 but check after the next instruction has been executed */
154 if( tstates == z80.interrupts_enabled_at ) {
155 event_add( tstates + 1, z80_interrupt_event );
156 return 0;
157 }
158
159 if( z80.halted ) { PC++; z80.halted = 0; }
160
161 IFF1=IFF2=0;
162
163 writebyte( --SP, PCH ); writebyte( --SP, PCL );
164
165 R++; rzx_instructions_offset--;
166
167 switch(IM) {
168 case 0: PC = 0x0038; tstates += 7; break;
169 case 1: PC = 0x0038; tstates += 7; break;
170 case 2:
171 {
172 libspectrum_word inttemp=(0x100*I)+0xff;
173 PCL = readbyte(inttemp++); PCH = readbyte(inttemp);
174 tstates += 7;
175 break;
176 }
177 default:
178 ui_error( UI_ERROR_ERROR, "Unknown interrupt mode %d", IM );
179 fuse_abort();
180 }
181
182 return 1; /* Accepted an interrupt */
183
184 } else {
185
186 return 0; /* Did not accept an interrupt */
187
188 }
189 }
190
191 /* Process a z80 non-maskable interrupt */
192 static void
z80_nmi(libspectrum_dword ts,int type,void * user_data)193 z80_nmi( libspectrum_dword ts, int type, void *user_data )
194 {
195 /* TODO: this isn't ideal */
196 if( spectranet_available && spectranet_nmi_flipflop() )
197 return;
198
199 if( z80.halted ) { PC++; z80.halted = 0; }
200
201 IFF1 = 0;
202
203 writebyte( --SP, PCH ); writebyte( --SP, PCL );
204
205 if( machine_current->capabilities &
206 LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY ) {
207
208 /* Page in ROM 2 */
209 writeport_internal( 0x1ffd, machine_current->ram.last_byte2 | 0x02 );
210
211 } else if( beta_available ) {
212
213 /* Page in TR-DOS ROM */
214 beta_page();
215 } else if( spectranet_available ) {
216
217 /* Page in spectranet */
218 spectranet_nmi();
219 }
220
221 /* FIXME: how is R affected? */
222
223 /* FIXME: how does contention apply here? */
224 tstates += 11; PC = 0x0066;
225 }
226
227 /* Special peripheral processing for RETN */
228 void
z80_retn(void)229 z80_retn( void )
230 {
231 spectranet_retn();
232 }
233
234 /* Routines for transferring the Z80 contents to and from snapshots */
235 static void
z80_from_snapshot(libspectrum_snap * snap)236 z80_from_snapshot( libspectrum_snap *snap )
237 {
238 A = libspectrum_snap_a ( snap ); F = libspectrum_snap_f ( snap );
239 A_ = libspectrum_snap_a_( snap ); F_ = libspectrum_snap_f_( snap );
240
241 BC = libspectrum_snap_bc ( snap ); DE = libspectrum_snap_de ( snap );
242 HL = libspectrum_snap_hl ( snap ); BC_ = libspectrum_snap_bc_( snap );
243 DE_ = libspectrum_snap_de_( snap ); HL_ = libspectrum_snap_hl_( snap );
244
245 IX = libspectrum_snap_ix( snap ); IY = libspectrum_snap_iy( snap );
246 I = libspectrum_snap_i ( snap ); R = R7 = libspectrum_snap_r( snap );
247 SP = libspectrum_snap_sp( snap ); PC = libspectrum_snap_pc( snap );
248
249 IFF1 = libspectrum_snap_iff1( snap ); IFF2 = libspectrum_snap_iff2( snap );
250 IM = libspectrum_snap_im( snap );
251
252 z80.halted = libspectrum_snap_halted( snap );
253
254 z80.interrupts_enabled_at =
255 libspectrum_snap_last_instruction_ei( snap ) ? tstates : -1;
256 }
257
258 static void
z80_to_snapshot(libspectrum_snap * snap)259 z80_to_snapshot( libspectrum_snap *snap )
260 {
261 libspectrum_byte r_register;
262
263 r_register = ( R7 & 0x80 ) | ( R & 0x7f );
264
265 libspectrum_snap_set_a ( snap, A ); libspectrum_snap_set_f ( snap, F );
266 libspectrum_snap_set_a_ ( snap, A_ ); libspectrum_snap_set_f_ ( snap, F_ );
267
268 libspectrum_snap_set_bc ( snap, BC ); libspectrum_snap_set_de ( snap, DE );
269 libspectrum_snap_set_hl ( snap, HL ); libspectrum_snap_set_bc_( snap, BC_ );
270 libspectrum_snap_set_de_( snap, DE_ ); libspectrum_snap_set_hl_( snap, HL_ );
271
272 libspectrum_snap_set_ix ( snap, IX ); libspectrum_snap_set_iy ( snap, IY );
273 libspectrum_snap_set_i ( snap, I );
274 libspectrum_snap_set_r ( snap, r_register );
275 libspectrum_snap_set_sp ( snap, SP ); libspectrum_snap_set_pc ( snap, PC );
276
277 libspectrum_snap_set_iff1( snap, IFF1 );
278 libspectrum_snap_set_iff2( snap, IFF2 );
279 libspectrum_snap_set_im( snap, IM );
280
281 libspectrum_snap_set_halted( snap, z80.halted );
282 libspectrum_snap_set_last_instruction_ei(
283 snap, z80.interrupts_enabled_at == tstates
284 );
285 }
286