File: z80.c

package info (click to toggle)
fuse-emulator 1.1.1%2Bdfsg1-2
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 11,120 kB
  • ctags: 8,968
  • sloc: ansic: 78,960; sh: 11,228; perl: 3,742; makefile: 1,104; yacc: 236; lex: 140
file content (285 lines) | stat: -rw-r--r-- 8,252 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/* z80.c: z80 supplementary functions
   Copyright (c) 1999-2013 Philip Kendall

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   Author contact information:

   E-mail: philip-fuse@shadowmagic.org.uk

*/

#include <config.h>

#include <libspectrum.h>

#include "event.h"
#include "fuse.h"
#include "memory.h"
#include "module.h"
#include "peripherals/scld.h"
#include "peripherals/spectranet.h"
#include "rzx.h"
#include "spectrum.h"
#include "ui/ui.h"
#include "z80.h"
#include "z80_macros.h"

/* Whether a half carry occurred or not can be determined by looking at
   the 3rd bit of the two arguments and the result; these are hashed
   into this table in the form r12, where r is the 3rd bit of the
   result, 1 is the 3rd bit of the 1st argument and 2 is the
   third bit of the 2nd argument; the tables differ for add and subtract
   operations */
const libspectrum_byte halfcarry_add_table[] =
  { 0, FLAG_H, FLAG_H, FLAG_H, 0, 0, 0, FLAG_H };
const libspectrum_byte halfcarry_sub_table[] =
  { 0, 0, FLAG_H, 0, FLAG_H, 0, FLAG_H, FLAG_H };

/* Similarly, overflow can be determined by looking at the 7th bits; again
   the hash into this table is r12 */
const libspectrum_byte overflow_add_table[] = { 0, 0, 0, FLAG_V, FLAG_V, 0, 0, 0 };
const libspectrum_byte overflow_sub_table[] = { 0, FLAG_V, 0, 0, 0, 0, FLAG_V, 0 };

/* Some more tables; initialised in z80_init_tables() */

libspectrum_byte sz53_table[0x100]; /* The S, Z, 5 and 3 bits of the index */
libspectrum_byte parity_table[0x100]; /* The parity of the lookup value */
libspectrum_byte sz53p_table[0x100]; /* OR the above two tables together */

/* This is what everything acts on! */
processor z80;

int z80_interrupt_event, z80_nmi_event;

static void z80_init_tables(void);
static void z80_from_snapshot( libspectrum_snap *snap );
static void z80_to_snapshot( libspectrum_snap *snap );
static void z80_nmi( libspectrum_dword ts, int type, void *user_data );

static module_info_t z80_module_info = {

  z80_reset,
  NULL,
  NULL,
  z80_from_snapshot,
  z80_to_snapshot,

};

static void
z80_interrupt_event_fn( libspectrum_dword tstates, int type, void *user_data )
{
  /* Retriggered interrupt; firstly, ignore if we're doing RZX playback
     as all interrupts are generated by the RZX code */
  if( rzx_playback ) return;

  /* Otherwise, see if we actually accept an interrupt. If we do and
     we're doing RZX recording, store a frame */
  if( z80_interrupt() ) rzx_frame();
}

/* Set up the z80 emulation */
void
z80_init( void )
{
  z80_init_tables();

  z80_interrupt_event = event_register( z80_interrupt_event_fn,
					"Retriggered interrupt" );
  z80_nmi_event = event_register( z80_nmi, "Non-maskable interrupt" );

  module_register( &z80_module_info );
}

/* Initalise the tables used to set flags */
static void z80_init_tables(void)
{
  int i,j,k;
  libspectrum_byte parity;

  for(i=0;i<0x100;i++) {
    sz53_table[i]= i & ( FLAG_3 | FLAG_5 | FLAG_S );
    j=i; parity=0;
    for(k=0;k<8;k++) { parity ^= j & 1; j >>=1; }
    parity_table[i]= ( parity ? 0 : FLAG_P );
    sz53p_table[i] = sz53_table[i] | parity_table[i];
  }

  sz53_table[0]  |= FLAG_Z;
  sz53p_table[0] |= FLAG_Z;

}

/* Reset the z80 */
void
z80_reset( int hard_reset GCC_UNUSED )
{
  AF =BC =DE =HL =0;
  AF_=BC_=DE_=HL_=0;
  IX=IY=0;
  I=R=R7=0;
  SP=PC=0;
  IFF1=IFF2=IM=0;
  z80.halted=0;

  z80.interrupts_enabled_at = -1;
}

/* Process a z80 maskable interrupt */
int
z80_interrupt( void )
{
  /* An interrupt will occur if IFF1 is set and the /INT line hasn't
     gone high again. On a Timex machine, we also need the SCLD's
     INTDISABLE to be clear */
  if( IFF1 &&
      tstates < machine_current->timings.interrupt_length &&
      !scld_last_dec.name.intdisable ) {

    /* If interrupts have just been enabled, don't accept the interrupt now,
       but check after the next instruction has been executed */
    if( tstates == z80.interrupts_enabled_at ) {
      event_add( tstates + 1, z80_interrupt_event );
      return 0;
    }

    if( z80.halted ) { PC++; z80.halted = 0; }
    
    IFF1=IFF2=0;

    writebyte( --SP, PCH ); writebyte( --SP, PCL );

    R++; rzx_instructions_offset--;

    switch(IM) {
      case 0: PC = 0x0038; tstates += 7; break;
      case 1: PC = 0x0038; tstates += 7; break;
      case 2: 
	{
	  libspectrum_word inttemp=(0x100*I)+0xff;
	  PCL = readbyte(inttemp++); PCH = readbyte(inttemp);
	  tstates += 7;
	  break;
	}
      default:
	ui_error( UI_ERROR_ERROR, "Unknown interrupt mode %d", IM );
	fuse_abort();
    }

    return 1;			/* Accepted an interrupt */

  } else {

    return 0;			/* Did not accept an interrupt */

  }
}

/* Process a z80 non-maskable interrupt */
static void
z80_nmi( libspectrum_dword ts, int type, void *user_data )
{
  /* TODO: this isn't ideal */
  if( spectranet_available && spectranet_nmi_flipflop() )
    return;

  if( z80.halted ) { PC++; z80.halted = 0; }

  IFF1 = 0;

  writebyte( --SP, PCH ); writebyte( --SP, PCL );

  if( machine_current->capabilities &
      LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY ) {

    /* Page in ROM 2 */
    writeport_internal( 0x1ffd, machine_current->ram.last_byte2 | 0x02 );

  } else if( beta_available ) {

    /* Page in TR-DOS ROM */
    beta_page();
  } else if( spectranet_available ) {
    
    /* Page in spectranet */
    spectranet_nmi();
  }

  /* FIXME: how is R affected? */

  /* FIXME: how does contention apply here? */
  tstates += 11; PC = 0x0066;
}

/* Special peripheral processing for RETN */
void
z80_retn( void )
{
  spectranet_retn();
}

/* Routines for transferring the Z80 contents to and from snapshots */
static void
z80_from_snapshot( libspectrum_snap *snap )
{
  A  = libspectrum_snap_a ( snap ); F  = libspectrum_snap_f ( snap );
  A_ = libspectrum_snap_a_( snap ); F_ = libspectrum_snap_f_( snap );

  BC  = libspectrum_snap_bc ( snap ); DE  = libspectrum_snap_de ( snap );
  HL  = libspectrum_snap_hl ( snap ); BC_ = libspectrum_snap_bc_( snap );
  DE_ = libspectrum_snap_de_( snap ); HL_ = libspectrum_snap_hl_( snap );

  IX = libspectrum_snap_ix( snap ); IY = libspectrum_snap_iy( snap );
  I  = libspectrum_snap_i ( snap ); R = R7 = libspectrum_snap_r( snap );
  SP = libspectrum_snap_sp( snap ); PC = libspectrum_snap_pc( snap );

  IFF1 = libspectrum_snap_iff1( snap ); IFF2 = libspectrum_snap_iff2( snap );
  IM = libspectrum_snap_im( snap );

  z80.halted = libspectrum_snap_halted( snap );

  z80.interrupts_enabled_at =
    libspectrum_snap_last_instruction_ei( snap ) ? tstates : -1;
}
  
static void
z80_to_snapshot( libspectrum_snap *snap )
{
  libspectrum_byte r_register;

  r_register = ( R7 & 0x80 ) | ( R & 0x7f );

  libspectrum_snap_set_a  ( snap, A   ); libspectrum_snap_set_f  ( snap, F   );
  libspectrum_snap_set_a_ ( snap, A_  ); libspectrum_snap_set_f_ ( snap, F_  );

  libspectrum_snap_set_bc ( snap, BC  ); libspectrum_snap_set_de ( snap, DE  );
  libspectrum_snap_set_hl ( snap, HL  ); libspectrum_snap_set_bc_( snap, BC_ );
  libspectrum_snap_set_de_( snap, DE_ ); libspectrum_snap_set_hl_( snap, HL_ );

  libspectrum_snap_set_ix ( snap, IX  ); libspectrum_snap_set_iy ( snap, IY  );
  libspectrum_snap_set_i  ( snap, I   );
  libspectrum_snap_set_r  ( snap, r_register );
  libspectrum_snap_set_sp ( snap, SP  ); libspectrum_snap_set_pc ( snap, PC  );

  libspectrum_snap_set_iff1( snap, IFF1 );
  libspectrum_snap_set_iff2( snap, IFF2 );
  libspectrum_snap_set_im( snap, IM );

  libspectrum_snap_set_halted( snap, z80.halted );
  libspectrum_snap_set_last_instruction_ei(
    snap, z80.interrupts_enabled_at == tstates
  );
}