-
Notifications
You must be signed in to change notification settings - Fork 0
/
z80ex.c
414 lines (342 loc) · 9.85 KB
/
z80ex.c
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*
* Z80Ex, ZILoG Z80 CPU emulator.
*
* by Pigmaker57 aka boo_boo [[email protected]]
*
* contains some code from the FUSE project (http://fuse-emulator.sourceforge.net)
* Released under GNU GPL v2
*
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define __Z80EX_SELF_INCLUDE
#include "typedefs.h"
#include "z80ex.h"
#include "macros.h"
#define temp_byte cpu->tmpbyte
#define temp_byte_s cpu->tmpbyte_s
#define temp_addr cpu->tmpaddr
#define temp_word cpu->tmpword
#include "ptables.c"
#include "opcodes/opcodes_base.c"
#include "opcodes/opcodes_dd.c"
#include "opcodes/opcodes_fd.c"
#include "opcodes/opcodes_cb.c"
#include "opcodes/opcodes_ed.c"
#include "opcodes/opcodes_ddcb.c"
#include "opcodes/opcodes_fdcb.c"
#define DOQUOTE(x) #x
#define TOSTRING(x) DOQUOTE(x)
static char revision_type[]=TOSTRING(Z80EX_RELEASE_TYPE);
static char ver_str[]=TOSTRING(Z80EX_VERSION_STR);
static Z80EX_VERSION version = {Z80EX_API_REVISION, Z80EX_VERSION_MAJOR, Z80EX_VERSION_MINOR, revision_type, ver_str};
LIB_EXPORT Z80EX_VERSION *z80ex_get_version()
{
return(&version);
}
/* do one opcode (instruction or prefix) */
LIB_EXPORT int z80ex_step(Z80EX_CONTEXT *cpu)
{
Z80EX_BYTE opcode, d;
z80ex_opcode_fn ofn=NULL;
cpu->doing_opcode=1;
cpu->noint_once=0;
cpu->reset_PV_on_int=0;
cpu->tstate=0;
cpu->op_tstate=0;
opcode=READ_OP_M1(); /*fetch opcode*/
if(cpu->int_vector_req)
{
TSTATES(2); /*interrupt eats two extra wait-states*/
}
R++; /*R increased by one on every first M1 cycle*/
T_WAIT_UNTIL(4); /*M1 cycle eats min 4 t-states*/
if(!cpu->prefix) opcodes_base[opcode](cpu);
else
{
if((cpu->prefix | 0x20) == 0xFD && ((opcode | 0x20) == 0xFD || opcode == 0xED))
{
cpu->prefix=opcode;
cpu->noint_once=1; /*interrupts are not accepted immediately after prefix*/
}
else
{
switch(cpu->prefix)
{
case 0xDD:
case 0xFD:
if(opcode == 0xCB)
{
d=READ_OP(); /*displacement*/
temp_byte_s=(d & 0x80)? -(((~d) & 0x7f)+1): d;
opcode=READ_OP();
ofn = (cpu->prefix == 0xDD)? opcodes_ddcb[opcode]: opcodes_fdcb[opcode];
}
else
{
ofn = (cpu->prefix == 0xDD)? opcodes_dd[opcode]: opcodes_fd[opcode];
if(ofn == NULL) ofn=opcodes_base[opcode]; /*'mirrored' instructions*/
}
break;
case 0xED:
ofn = opcodes_ed[opcode];
if(ofn == NULL) ofn=opcodes_base[0x00];
break;
case 0xCB:
ofn = opcodes_cb[opcode];
break;
default:
/*this must'nt happen!*/
assert(0);
break;
}
ofn(cpu);
cpu->prefix=0;
}
}
cpu->doing_opcode=0;
return(cpu->tstate);
}
LIB_EXPORT Z80EX_BYTE z80ex_last_op_type(Z80EX_CONTEXT *cpu)
{
return(cpu->prefix);
}
LIB_EXPORT void z80ex_reset(Z80EX_CONTEXT *cpu)
{
PC=0x0000; IFF1=IFF2=0; IM=IM0;
AF=SP=BC=DE=HL=IX=IY=AF_=BC_=DE_=HL_=0xffff;
I=R=R7=0;
cpu->noint_once=0; cpu->reset_PV_on_int=0; cpu->halted=0;
cpu->int_vector_req=0;
cpu->doing_opcode=0;
cpu->tstate=cpu->op_tstate=0;
cpu->prefix=0;
}
/**/
LIB_EXPORT Z80EX_CONTEXT *z80ex_create(
z80ex_mread_cb mrcb_fn, void *mrcb_data,
z80ex_mwrite_cb mwcb_fn, void *mwcb_data,
z80ex_pread_cb prcb_fn, void *prcb_data,
z80ex_pwrite_cb pwcb_fn, void *pwcb_data,
z80ex_intread_cb ircb_fn, void *ircb_data
)
{
Z80EX_CONTEXT *cpu;
if((cpu=(Z80EX_CONTEXT *)malloc(sizeof(Z80EX_CONTEXT))) == NULL) return(NULL);
memset(cpu,0x00,sizeof(Z80EX_CONTEXT));
z80ex_reset(cpu);
cpu->mread_cb=mrcb_fn;
cpu->mread_cb_user_data=mrcb_data;
cpu->mwrite_cb=mwcb_fn;
cpu->mwrite_cb_user_data=mwcb_data;
cpu->pread_cb=prcb_fn;
cpu->pread_cb_user_data=prcb_data;
cpu->pwrite_cb=pwcb_fn;
cpu->pwrite_cb_user_data=pwcb_data;
cpu->intread_cb=ircb_fn;
cpu->intread_cb_user_data=ircb_data;
return(cpu);
}
LIB_EXPORT void z80ex_destroy(Z80EX_CONTEXT *cpu)
{
free(cpu);
}
LIB_EXPORT void z80ex_set_tstate_callback(Z80EX_CONTEXT *cpu, z80ex_tstate_cb cb_fn, void *user_data)
{
cpu->tstate_cb=cb_fn;
cpu->tstate_cb_user_data=user_data;
}
LIB_EXPORT void z80ex_set_reti_callback(Z80EX_CONTEXT *cpu, z80ex_reti_cb cb_fn, void *user_data)
{
cpu->reti_cb=cb_fn;
cpu->reti_cb_user_data=user_data;
}
LIB_EXPORT void z80ex_set_memread_callback(Z80EX_CONTEXT *cpu, z80ex_mread_cb mrcb_fn, void *mrcb_data)
{
cpu->mread_cb=mrcb_fn;
cpu->mread_cb_user_data=mrcb_data;
}
LIB_EXPORT void z80ex_set_memwrite_callback(Z80EX_CONTEXT *cpu, z80ex_mwrite_cb mwcb_fn, void *mwcb_data)
{
cpu->mwrite_cb=mwcb_fn;
cpu->mwrite_cb_user_data=mwcb_data;
}
LIB_EXPORT void z80ex_set_portread_callback(Z80EX_CONTEXT *cpu, z80ex_pread_cb prcb_fn, void *prcb_data)
{
cpu->pread_cb=prcb_fn;
cpu->pread_cb_user_data=prcb_data;
}
LIB_EXPORT void z80ex_set_portwrite_callback(Z80EX_CONTEXT *cpu, z80ex_pwrite_cb pwcb_fn, void *pwcb_data)
{
cpu->pwrite_cb=pwcb_fn;
cpu->pwrite_cb_user_data=pwcb_data;
}
LIB_EXPORT void z80ex_set_intread_callback(Z80EX_CONTEXT *cpu, z80ex_intread_cb ircb_fn, void *ircb_data)
{
cpu->intread_cb=ircb_fn;
cpu->intread_cb_user_data=ircb_data;
}
/*non-maskable interrupt*/
LIB_EXPORT int z80ex_nmi(Z80EX_CONTEXT *cpu)
{
if(cpu->doing_opcode || cpu->noint_once || cpu->prefix) return(0);
if(cpu->halted) { PC++; cpu->halted = 0; } /*so we met an interrupt... stop waiting*/
cpu->doing_opcode=1;
R++; /*accepting interrupt increases R by one*/
/*IFF2=IFF1;*/ /*contrary to zilog z80 docs, IFF2 is not modified on NMI. proved by Slava Tretiak aka restorer*/
IFF1=0;
TSTATES(5);
cpu->mwrite_cb(cpu, --SP, cpu->pc.b.h, cpu->mwrite_cb_user_data); /*PUSH PC -- high byte */
TSTATES(3);
cpu->mwrite_cb(cpu, --SP, cpu->pc.b.l, cpu->mwrite_cb_user_data); /*PUSH PC -- low byte */
TSTATES(3);
PC=0x0066;
MEMPTR=PC; /*FIXME: is that really so?*/
cpu->doing_opcode=0;
return(11); /*NMI always takes 11 t-states*/
}
/*maskable interrupt*/
LIB_EXPORT int z80ex_int(Z80EX_CONTEXT *cpu)
{
Z80EX_WORD inttemp;
Z80EX_BYTE iv;
unsigned long tt;
/*If the INT line is low and IFF1 is set, and there's no opcode executing just now,
a maskable interrupt is accepted, whether or not the
last INT routine has finished*/
if(!IFF1 || cpu->noint_once || cpu->doing_opcode || cpu->prefix) return(0);
cpu->tstate=0;
cpu->op_tstate=0;
if(cpu->halted) { PC++; cpu->halted = 0; } /*so we met an interrupt... stop waiting*/
/*When an INT is accepted, both IFF1 and IFF2 are cleared, preventing another interrupt from
occurring which would end up as an infinite loop*/
IFF1=IFF2=0;
/*original (NMOS) zilog z80 bug:*/
/*If a LD A,I or LD A,R (which copy IFF2 to the P/V flag) is interrupted, then the P/V flag is reset, even if interrupts were enabled beforehand.*/
/*(this bug was fixed in CMOS version of z80)*/
if(cpu->reset_PV_on_int) {F = (F & ~FLAG_P);}
cpu->reset_PV_on_int=0;
cpu->int_vector_req=1;
cpu->doing_opcode=1;
switch(IM)
{
case IM0:
/*note: there's no need to do R++ and WAITs here, it'll be handled by z80ex_step*/
tt=z80ex_step(cpu);
while(cpu->prefix) /*this is not the end?*/
{
tt+=z80ex_step(cpu);
}
cpu->tstate=tt;
break;
case IM1:
R++;
TSTATES(2); /*two extra wait-states*/
/*An RST 38h is executed, no matter what value is put on the bus or what
value the I register has. 13 t-states (2 extra + 11 for RST).*/
opcodes_base[0xff](cpu); /*RST38*/
break;
case IM2:
R++;
/*takes 19 clock periods to complete (seven to fetch the
lower eight bits from the interrupting device, six to save the program
counter, and six to obtain the jump address)*/
iv=READ_OP();
T_WAIT_UNTIL(7);
inttemp=(0x100*I)+iv;
PUSH(PC,7,10);
READ_MEM(PCL,inttemp++,13); READ_MEM(PCH,inttemp,16);
MEMPTR=PC;
T_WAIT_UNTIL(19);
break;
}
cpu->doing_opcode=0;
cpu->int_vector_req=0;
return(cpu->tstate);
}
LIB_EXPORT void z80ex_w_states(Z80EX_CONTEXT *cpu, unsigned w_states)
{
TSTATES(w_states);
}
LIB_EXPORT void z80ex_next_t_state(Z80EX_CONTEXT *cpu)
{
if(cpu->tstate_cb != NULL) cpu->tstate_cb(cpu, cpu->tstate_cb_user_data);
cpu->tstate++;
cpu->op_tstate++;
}
LIB_EXPORT Z80EX_WORD z80ex_get_reg(Z80EX_CONTEXT *cpu, Z80_REG_T reg)
{
switch(reg)
{
case regAF: return(AF);
case regBC: return(BC);
case regDE: return(DE);
case regHL: return(HL);
case regAF_: return(AF_);
case regBC_: return(BC_);
case regDE_: return(DE_);
case regHL_: return(HL_);
case regIX: return(IX);
case regIY: return(IY);
case regPC: return(PC);
case regSP: return(SP);
case regI: return(I);
case regR: return(R);
case regR7: return(R7);
case regIM: return(IM);
case regIFF1: return(IFF1);
case regIFF2: return(IFF2);
}
return(0);
}
LIB_EXPORT void z80ex_set_reg(Z80EX_CONTEXT *cpu, Z80_REG_T reg, Z80EX_WORD value)
{
switch(reg)
{
case regAF: AF=value; return;
case regBC: BC=value; return;
case regDE: DE=value; return;
case regHL: HL=value; return;
case regAF_: AF_=value; return;
case regBC_: BC_=value; return;
case regDE_: DE_=value; return;
case regHL_: HL_=value; return;
case regIX: IX=value; return;
case regIY: IY=value; return;
case regPC: PC=value; return;
case regSP: SP=value; return;
case regI: I=(value & 0xff); return;
case regR: R=(value & 0xff); return;
case regR7: R7=(value & 0xff); return;
case regIM:
switch(value & 0x03)
{
case 0: IM=IM0; return;
case 1: IM=IM1; return;
case 2: IM=IM2; return;
}
case regIFF1: IFF1=(value & 0x01); return;
case regIFF2: IFF2=(value & 0x01); return;
}
}
LIB_EXPORT int z80ex_op_tstate(Z80EX_CONTEXT *cpu)
{
return(cpu->tstate);
}
LIB_EXPORT int z80ex_doing_halt(Z80EX_CONTEXT *cpu)
{
return(cpu->halted);
}
/*LIB_EXPORT int z80ex_get_noint_once(Z80EX_CONTEXT *cpu)
{
return(cpu->noint_once);
}*/
LIB_EXPORT int z80ex_int_possible(Z80EX_CONTEXT *cpu)
{
return((!IFF1 || cpu->noint_once || cpu->doing_opcode || cpu->prefix)? 0 : 1);
}
LIB_EXPORT int z80ex_nmi_possible(Z80EX_CONTEXT *cpu)
{
return((cpu->noint_once || cpu->doing_opcode || cpu->prefix)? 0 : 1);
}