sms: improve irq handling
[picodrive.git] / pico / z80if.c
CommitLineData
cff531af 1/*
2 * PicoDrive
3 * (C) notaz, 2007-2010
4 *
5 * This work is licensed under the terms of MAME license.
6 * See COPYING file in the top-level directory.
7 */
8
b8a1c09a 9#include <stddef.h>
c8d1e9b6 10#include "pico_int.h"
b8a1c09a 11#include "memory.h"
c8d1e9b6 12
b8a1c09a 13uptr z80_read_map [0x10000 >> Z80_MEM_SHIFT];
14uptr z80_write_map[0x10000 >> Z80_MEM_SHIFT];
c8d1e9b6 15
b4db550e 16#ifdef _USE_DRZ80
61290a35 17// this causes trouble in some cases, like doukutsu putting sp in bank area
18// no perf difference for most, upto 1-2% for some others
19//#define FAST_Z80SP
c8d1e9b6 20
61290a35 21struct DrZ80 drZ80;
c8d1e9b6 22
b4db550e 23static void drz80_load_pcsp(u32 pc, u32 sp)
c8d1e9b6 24{
b4db550e 25 drZ80.Z80PC_BASE = z80_read_map[pc >> Z80_MEM_SHIFT];
26 if (drZ80.Z80PC_BASE & (1<<31)) {
27 elprintf(EL_STATUS|EL_ANOMALY, "load_pcsp: bad PC: %04x", pc);
28 drZ80.Z80PC_BASE = drZ80.Z80PC = z80_read_map[0];
29 } else {
30 drZ80.Z80PC_BASE <<= 1;
31 drZ80.Z80PC = drZ80.Z80PC_BASE + pc;
32 }
61290a35 33 drZ80.Z80SP = sp;
34#ifdef FAST_Z80SP
b4db550e 35 drZ80.Z80SP_BASE = z80_read_map[sp >> Z80_MEM_SHIFT];
36 if (drZ80.Z80SP_BASE & (1<<31)) {
37 elprintf(EL_STATUS|EL_ANOMALY, "load_pcsp: bad SP: %04x", sp);
38 drZ80.Z80SP_BASE = z80_read_map[0];
39 drZ80.Z80SP = drZ80.Z80SP_BASE + (1 << Z80_MEM_SHIFT);
40 } else {
41 drZ80.Z80SP_BASE <<= 1;
42 drZ80.Z80SP = drZ80.Z80SP_BASE + sp;
43 }
61290a35 44#endif
b4db550e 45}
c8d1e9b6 46
b4db550e 47// called only if internal xmap rebase fails
48static unsigned int dz80_rebase_pc(unsigned short pc)
c8d1e9b6 49{
b4db550e 50 elprintf(EL_STATUS|EL_ANOMALY, "dz80_rebase_pc: fail on %04x", pc);
51 drZ80.Z80PC_BASE = z80_read_map[0] << 1;
52 return drZ80.Z80PC_BASE;
c8d1e9b6 53}
54
cf83610b 55static void dz80_noop_irq_ack(void) {}
56
61290a35 57#ifdef FAST_Z80SP
58static u32 drz80_sp_base;
59
b4db550e 60static unsigned int dz80_rebase_sp(unsigned short sp)
61{
62 elprintf(EL_STATUS|EL_ANOMALY, "dz80_rebase_sp: fail on %04x", sp);
63 drZ80.Z80SP_BASE = z80_read_map[drz80_sp_base >> Z80_MEM_SHIFT] << 1;
64 return drZ80.Z80SP_BASE + (1 << Z80_MEM_SHIFT) - 0x100;
65}
61290a35 66#else
67#define dz80_rebase_sp NULL
c8d1e9b6 68#endif
61290a35 69#endif // _USE_DRZ80
c8d1e9b6 70
71
b4db550e 72void z80_init(void)
c8d1e9b6 73{
c8d1e9b6 74#ifdef _USE_DRZ80
75 memset(&drZ80, 0, sizeof(drZ80));
b4db550e 76 drZ80.z80_rebasePC = dz80_rebase_pc;
77 drZ80.z80_rebaseSP = dz80_rebase_sp;
78 drZ80.z80_read8 = (void *)z80_read_map;
79 drZ80.z80_read16 = NULL;
80 drZ80.z80_write8 = (void *)z80_write_map;
81 drZ80.z80_write16 = NULL;
82 drZ80.z80_irq_callback = NULL;
c8d1e9b6 83#endif
84#ifdef _USE_CZ80
85 memset(&CZ80, 0, sizeof(CZ80));
86 Cz80_Init(&CZ80);
87 Cz80_Set_ReadB(&CZ80, NULL); // unused (hacked in)
88 Cz80_Set_WriteB(&CZ80, NULL);
89#endif
90}
91
b4db550e 92void z80_reset(void)
c8d1e9b6 93{
c8d1e9b6 94#ifdef _USE_DRZ80
cf82669f 95 drZ80.Z80I = 0;
96 drZ80.Z80IM = 0;
97 drZ80.Z80IF = 0;
98 drZ80.z80irqvector = 0xff0000; // RST 38h
99 drZ80.Z80PC_BASE = drZ80.Z80PC = z80_read_map[0] << 1;
100 // others not changed, undefined on cold boot
101/*
c8d1e9b6 102 drZ80.Z80F = (1<<2); // set ZFlag
103 drZ80.Z80F2 = (1<<2); // set ZFlag
104 drZ80.Z80IX = 0xFFFF << 16;
105 drZ80.Z80IY = 0xFFFF << 16;
cf82669f 106*/
61290a35 107#ifdef FAST_Z80SP
d8f51995 108 // drZ80 is locked in single bank
93f9619e 109 drz80_sp_base = (PicoIn.AHW & PAHW_SMS) ? 0xc000 : 0x0000;
b4db550e 110 drZ80.Z80SP_BASE = z80_read_map[drz80_sp_base >> Z80_MEM_SHIFT] << 1;
61290a35 111#endif
cf83610b 112 drZ80.z80_irq_callback = NULL; // use auto-clear
113 if (PicoIn.AHW & PAHW_SMS) {
b4db550e 114 drZ80.Z80SP = drZ80.Z80SP_BASE + 0xdff0; // simulate BIOS
cf83610b 115 drZ80.z80_irq_callback = dz80_noop_irq_ack;
116 }
cf82669f 117 // XXX: since we use direct SP pointer, it might make sense to force it to RAM,
118 // but we'll rely on built-in stack protection for now
c8d1e9b6 119#endif
120#ifdef _USE_CZ80
121 Cz80_Reset(&CZ80);
93f9619e 122 if (PicoIn.AHW & PAHW_SMS)
b4db550e 123 Cz80_Set_Reg(&CZ80, CZ80_SP, 0xdff0);
c8d1e9b6 124#endif
125}
126
b4db550e 127struct z80sr_main {
128 u8 a, f;
129 u8 b, c;
130 u8 d, e;
131 u8 h, l;
132};
133
134struct z80_state {
135 char magic[4];
136 // regs
137 struct z80sr_main m; // main regs
138 struct z80sr_main a; // alt (') regs
139 u8 i, r;
140 u16 ix, iy;
141 u16 sp;
142 u16 pc;
143 // other
144 u8 halted;
145 u8 iff1, iff2;
146 u8 im; // irq mode
147 u8 irq_pending; // irq line level, 1 if active
148 u8 irq_vector[3]; // up to 3 byte vector for irq mode0 handling
149 u8 reserved[8];
150};
151
152void z80_pack(void *data)
c8d1e9b6 153{
b4db550e 154 struct z80_state *s = data;
155 memset(data, 0, Z80_STATE_SIZE);
156 strcpy(s->magic, "Z80");
157#if defined(_USE_DRZ80)
158 #define DRR8(n) (drZ80.Z80##n >> 24)
159 #define DRR16(n) (drZ80.Z80##n >> 16)
160 #define DRR16H(n) (drZ80.Z80##n >> 24)
161 #define DRR16L(n) ((drZ80.Z80##n >> 16) & 0xff)
83b1fb32 162 s->m.a = DRR8(A); s->m.f = drZ80.Z80F;
b4db550e 163 s->m.b = DRR16H(BC); s->m.c = DRR16L(BC);
164 s->m.d = DRR16H(DE); s->m.e = DRR16L(DE);
165 s->m.h = DRR16H(HL); s->m.l = DRR16L(HL);
83b1fb32 166 s->a.a = DRR8(A2); s->a.f = drZ80.Z80F2;
b4db550e 167 s->a.b = DRR16H(BC2); s->a.c = DRR16L(BC2);
168 s->a.d = DRR16H(DE2); s->a.e = DRR16L(DE2);
169 s->a.h = DRR16H(HL2); s->a.l = DRR16L(HL2);
170 s->i = DRR8(I); s->r = drZ80.spare;
171 s->ix = DRR16(IX); s->iy = DRR16(IY);
172 s->sp = drZ80.Z80SP - drZ80.Z80SP_BASE;
173 s->pc = drZ80.Z80PC - drZ80.Z80PC_BASE;
174 s->halted = !!(drZ80.Z80IF & 4);
175 s->iff1 = !!(drZ80.Z80IF & 1);
176 s->iff2 = !!(drZ80.Z80IF & 2);
177 s->im = drZ80.Z80IM;
178 s->irq_pending = !!drZ80.Z80_IRQ;
179 s->irq_vector[0] = drZ80.z80irqvector >> 16;
180 s->irq_vector[1] = drZ80.z80irqvector >> 8;
181 s->irq_vector[2] = drZ80.z80irqvector;
182#elif defined(_USE_CZ80)
183 {
184 const cz80_struc *CPU = &CZ80;
185 s->m.a = zA; s->m.f = zF;
186 s->m.b = zB; s->m.c = zC;
187 s->m.d = zD; s->m.e = zE;
188 s->m.h = zH; s->m.l = zL;
189 s->a.a = zA2; s->a.f = zF2;
190 s->a.b = CZ80.BC2.B.H; s->a.c = CZ80.BC2.B.L;
191 s->a.d = CZ80.DE2.B.H; s->a.e = CZ80.DE2.B.L;
192 s->a.h = CZ80.HL2.B.H; s->a.l = CZ80.HL2.B.L;
193 s->i = zI; s->r = zR;
194 s->ix = zIX; s->iy = zIY;
195 s->sp = Cz80_Get_Reg(&CZ80, CZ80_SP);
196 s->pc = Cz80_Get_Reg(&CZ80, CZ80_PC);
197 s->halted = !!Cz80_Get_Reg(&CZ80, CZ80_HALT);
198 s->iff1 = !!zIFF1;
199 s->iff2 = !!zIFF2;
200 s->im = zIM;
201 s->irq_pending = (Cz80_Get_Reg(&CZ80, CZ80_IRQ) == HOLD_LINE);
202 s->irq_vector[0] = 0xff;
203 }
c8d1e9b6 204#endif
205}
206
b4db550e 207int z80_unpack(const void *data)
208{
209 const struct z80_state *s = data;
210 if (strcmp(s->magic, "Z80") != 0) {
e6488636 211 elprintf(EL_STATUS, "legacy z80 state - ignored");
b4db550e 212 return 0;
213 }
214
215#if defined(_USE_DRZ80)
216 #define DRW8(n, v) drZ80.Z80##n = (u32)(v) << 24
217 #define DRW16(n, v) drZ80.Z80##n = (u32)(v) << 16
218 #define DRW16HL(n, h, l) drZ80.Z80##n = ((u32)(h) << 24) | ((u32)(l) << 16)
83b1fb32 219 DRW8(A, s->m.a); drZ80.Z80F = s->m.f;
b4db550e 220 DRW16HL(BC, s->m.b, s->m.c);
221 DRW16HL(DE, s->m.d, s->m.e);
222 DRW16HL(HL, s->m.h, s->m.l);
83b1fb32 223 DRW8(A2, s->a.a); drZ80.Z80F2 = s->a.f;
b4db550e 224 DRW16HL(BC2, s->a.b, s->a.c);
225 DRW16HL(DE2, s->a.d, s->a.e);
226 DRW16HL(HL2, s->a.h, s->a.l);
227 DRW8(I, s->i); drZ80.spare = s->r;
228 DRW16(IX, s->ix); DRW16(IY, s->iy);
229 drz80_load_pcsp(s->pc, s->sp);
230 drZ80.Z80IF = 0;
231 if (s->halted) drZ80.Z80IF |= 4;
232 if (s->iff1) drZ80.Z80IF |= 1;
233 if (s->iff2) drZ80.Z80IF |= 2;
234 drZ80.Z80IM = s->im;
235 drZ80.Z80_IRQ = s->irq_pending;
236 drZ80.z80irqvector = ((u32)s->irq_vector[0] << 16) |
237 ((u32)s->irq_vector[1] << 8) | s->irq_vector[2];
238 return 0;
239#elif defined(_USE_CZ80)
240 {
241 cz80_struc *CPU = &CZ80;
242 zA = s->m.a; zF = s->m.f;
243 zB = s->m.b; zC = s->m.c;
244 zD = s->m.d; zE = s->m.e;
245 zH = s->m.h; zL = s->m.l;
246 zA2 = s->a.a; zF2 = s->a.f;
247 CZ80.BC2.B.H = s->a.b; CZ80.BC2.B.L = s->a.c;
248 CZ80.DE2.B.H = s->a.d; CZ80.DE2.B.L = s->a.e;
249 CZ80.HL2.B.H = s->a.h; CZ80.HL2.B.L = s->a.l;
250 zI = s->i; zR = s->r;
251 zIX = s->ix; zIY = s->iy;
252 Cz80_Set_Reg(&CZ80, CZ80_SP, s->sp);
253 Cz80_Set_Reg(&CZ80, CZ80_PC, s->pc);
254 Cz80_Set_Reg(&CZ80, CZ80_HALT, s->halted);
255 Cz80_Set_Reg(&CZ80, CZ80_IFF1, s->iff1);
256 Cz80_Set_Reg(&CZ80, CZ80_IFF2, s->iff2);
257 zIM = s->im;
258 Cz80_Set_Reg(&CZ80, CZ80_IRQ, s->irq_pending ? HOLD_LINE : CLEAR_LINE);
259 return 0;
260 }
e6488636 261#else
262 return 0;
b4db550e 263#endif
b4db550e 264}
265
266void z80_exit(void)
267{
268}
269
270void z80_debug(char *dstr)
c8d1e9b6 271{
272#if defined(_USE_DRZ80)
273 sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);
274#elif defined(_USE_CZ80)
b8a1c09a 275 sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", (unsigned int)(CZ80.PC - CZ80.BasePC), CZ80.SP.W);
c8d1e9b6 276#endif
277}
61290a35 278
279// vim:ts=2:sw=2:expandtab