added "iv.dynstring"
[iv.d.git] / zymosis / z80emu.d
blob1c117d0df04f6826df2d079187fd3f5a38b0d0f3
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module iv.zymosis.z80emu /*is aliced*/;
18 import iv.alice;
20 //version = Zymosis_Testing; // to use with FUSE testing suite
21 //version = Zymosis_Check_Contention;
23 version(Zymosis_Testing) pragma(msg, "Zymosis: building test version");
24 version(Zymosis_Check_Contention) pragma(msg, "Zymosis: do some sanity checks for contention tables");
27 // ////////////////////////////////////////////////////////////////////////// //
28 alias ZymCPU = ZymCPUBase!16384; ///
30 public class ZymCPUBase(ushort MemPageSize=16384) {
31 public:
32 ///
33 static struct MemPage {
34 //enum Size = 4096; // in page
35 //enum Size = 16384; // in page
36 enum Size = MemPageSize; /// in page
37 ubyte* mem; /// pointer to Size bytes
38 bool contended; /// is this page contended?
39 bool rom; /// read-only?
40 bool writeHook; /// call write hook before something was written to this page
43 public:
44 /// flag masks
45 enum Z80Flag : ubyte {
46 C = 0x01, /// carry flag
47 N = 0x02, /// add/substract flag (0: last was add, 1: last was sub)
48 PV = 0x04, /// parity/overflow flag
49 F3 = 0x08,
50 H = 0x10, /// half-carry flag
51 F5 = 0x20,
52 Z = 0x40, /// zero flag
53 S = 0x80, /// sign flag
55 F35 = F3|F5,
56 S35 = S|F35
59 /// Previous instruction type.
60 enum EIDDR {
61 LdIorR = -1, /// LD A,I or LD A,R
62 Normal, /// normal instruction
63 BlockInt /// EI/FD/DD (they blocks /INT)
66 /// register pair
67 union RegPair(string hi, string lo) {
68 align(1):
69 ushort w;
70 version(LittleEndian) {
71 mixin("struct { align(1): ubyte "~lo~", "~hi~"; }");
73 version(BigEndian) {
74 mixin("struct { align(1): ubyte "~hi~", "~lo~"; }");
76 alias w this; // allow to use RegPair as ushort
79 private:
80 RegPair!("h", "l")* DD; // pointer to current HL/IX/IY (inside this class) for the current command
81 ubyte mIM; // Interrupt Mode (0-2)
83 public:
84 MemPage[65536/MemPage.Size+1] mem; /// machine memory; MUST be initialized before executing anything
85 bool[65536] bpmap; /// breakpoint map; breakpoint hook will be called if this is true; no autoreset
86 ubyte[] ulacont; /// contention for memory access, with mreq; indexed by `tstates`
87 ubyte[] ulacontport; /// contention for memory access operation, without mreq; indexed by `tstates`
88 /// registers
89 RegPair!("b", "c") BC; ///
90 RegPair!("d", "e") DE; ///
91 RegPair!("h", "l") HL; ///
92 RegPair!("a", "f") AF; ///
93 RegPair!("xh", "xl") IX; ///
94 RegPair!("yh", "yl") IY; ///
95 /// alternate registers
96 RegPair!("b", "c") BCx; ///
97 RegPair!("d", "e") DEx; ///
98 RegPair!("h", "l") HLx; ///
99 RegPair!("a", "f") AFx; ///
100 RegPair!("hi", "lo") MEMPTR; /// special MEMPTR register
101 ubyte I, R; /// C.O.: I and R registers respectively
102 ushort SP; /// stack pointer
103 ushort PC; /// program counter
104 ushort prevPC; /// first byte of the previous (before last) executed command
105 ushort lastPC; /// first byte of the last executed command
106 bool IFF1, IFF2; /// interrupt flip-flops
107 /** is CPU halted? main progam must manually reset this flag when it's appropriate
108 * Zymosis will automatically reset it in intr() and nmi(). */
109 bool halted;
110 int tstates; /// t-states passed from previous interrupt (0-...)
111 int nextEventTS = -1; /// zym_exec() will exit when tstates>=nextEventTS
112 /** previous instruction type.
113 * Zymosis will reset this flag to Normal only if it executed at least one instruction. */
114 EIDDR prevWasEIDDR;
115 bool evenM1; /// emulate 128K/Scorpion M1 contention?
116 bool bpHit; /// will be set if execution loop was stopped by breakpoint; autoreset
117 ubyte flags_q; /// SCF/CCF emulation: presumably, internal register where Z80 assembles the new content of the F register, before moving it back to F
119 public:
121 * Page execute hook. Will be called before opcode fetching and tstate changing.
122 * Ideal place to switch ROMs.
124 * PC is current instruction address (to be fetched). Entering page `mpage`.
126 void execHook () nothrow @trusted {}
128 /// this will be called before writing occurs
129 void memWriteHook (ushort addr, ubyte b) nothrow @trusted {}
132 * Breakpoint hook
134 * PC is current instruction address (to be fetched). Return `true` to break emulation loop.
136 bool checkBreakpoint () nothrow @trusted { return false; }
138 version(Zymosis_Testing) {
140 * Will be called when port contention is necessary.
141 * Function must increase z80.tstates by at least 'atstates' arg.
143 * Default: tstates += atstates;
145 * Params:
146 * port = port address
147 * atstates = how much tstates we should spend (always 1 when 'early' is set and 2 otherwise)
148 * doIN = true, if this is 'IN' instruction and false if this is 'OUT' instruction
149 * early = true, if doing 'early' port contention (yes, ZX Spectrum port contention is complex)
151 * Returns:
152 * nothing
154 void portContention (ushort port, int atstates, bool doIN, bool early) nothrow @trusted { tstates += atstates; }
156 void memContention (ushort addr, bool mreq) nothrow @trusted {}
157 void memReading (ushort addr) nothrow @trusted {}
158 void memWriting (ushort addr, ubyte b) nothrow @trusted {}
162 * Read byte from emulated port.
164 * Params:
165 * addr = port address
167 * Returns:
168 * readed byte from emulated port
170 abstract ubyte portRead (ushort port) nothrow @trusted;
173 * Write byte to emulated port.
175 * Params:
176 * addr = port address
177 * value = byte to store
179 * Returns:
180 * Nothing
182 abstract void portWrite (ushort port, ubyte value) nothrow @trusted;
184 /// get byte from memory, don't trigger any breakpoints or such
185 public final ubyte memPeekB (ushort addr) nothrow @trusted {
186 pragma(inline, true);
187 return mem.ptr[addr/MemPage.Size].mem[addr%MemPage.Size];
190 /// set byte in memory, don't trigger any breakpoints or such, but respect ROM
191 public final void memPokeB (ushort addr, ubyte b) nothrow @trusted {
192 pragma(inline, true);
193 if (!mem.ptr[addr/MemPage.Size].rom) mem.ptr[addr/MemPage.Size].mem[addr%MemPage.Size] = b;
197 * This function will be called on invalid ED command (so-called 'trap command').
198 * CPU's PC will point to the next instruction (use origpc to inspect trap code).
200 * trapCode=0xFB: .SLT trap
201 * HL: address to load;
202 * A: A --> level number
203 * return: CARRY complemented --> error
205 * Params:
206 * trapCode = actual trap code
208 * Returns:
209 * 'stop emulation' flag (return true to stop emulation loop immediately)
211 bool trapED (ubyte trapCode) nothrow @trusted { return false; }
214 * This function will be called *AFTER* RETI command executed, iff changed and return address set.
216 * Params:
217 * opcode = actual opcode (there is more than one RETI in Z80)
219 * Returns:
220 * 'stop emulation' flag (return true to stop emulation loop immediately)
222 bool trapRETI (ubyte opcode) nothrow @trusted { return false; }
225 * This function will be called *AFTER* RETN command executed, iff changed and return address set.
227 * Params:
228 * opcode = actual opcode (there is more than one RETI in Z80)
230 * Returns:
231 * 'stop emulation' flag (return true to stop emulation loop immediately)
233 bool trapRETN (ubyte opcode) nothrow @trusted { return false; }
236 // ////////////////////////////////////////////////////////////////////// //
238 this () {
239 evenM1 = false;
240 bpHit = false;
241 tstates = 0;
242 reset(true);
245 /** "Soft" reset: clear only absolutely necessary things. */
246 void softReset () {
247 PC = prevPC = lastPC = 0;
248 DD = &HL;
249 MEMPTR.w = 0;
250 I = R = 0;
251 IFF1 = IFF2 = false;
252 mIM = 0;
253 halted = false;
254 prevWasEIDDR = EIDDR.Normal;
255 bpHit = false;
256 setupMemory();
259 /** Reset emulated CPU. Will NOT reset tstate counter. */
260 void reset (bool poweron=false) {
261 //TODO: don't touch IX and IY values?
262 BC = DE = HL = AF = SP = IX = IY = (poweron ? 0xFFFF: 0);
263 BCx = DEx = HLx = AFx = (poweron ? 0xFFFF: 0);
264 flags_q = 0;
265 softReset();
268 /** This will be called before reset seqience is complete. */
269 abstract void setupMemory ();
271 // ////////////////////////////////////////////////////////////////////////// //
272 final:
273 @property ubyte IM () const pure nothrow @safe @nogc { pragma(inline, true); return mIM; } /** get Interrupt Mode (0-2) */
274 @property void IM (in ubyte v) pure nothrow @safe @nogc { pragma(inline, true); mIM = (v > 2 ? 0 : v); } /** set Interrupt Mode (0-2) */
276 /** increment R register. note that Z80 never changes the high bit of R. */
277 void incR () pure nothrow @safe @nogc { pragma(inline, true); R = ((R+1)&0x7f)|(R&0x80); }
280 * Execute emulated CPU instructions.
281 * This function will execute Z80 code until tstates reaches nextEventTS
282 * or at least tscount tstates passed.
283 * Note that it can spend more that tscount states and slightly miss
284 * nextEventTS so tstates will be >= nextEventTS.
286 * WARNING: don't decrease tstates value in callbacks, or everything
287 * will be really bad!
289 * Params:
290 * tscount = how much tstates we should spend executing;
291 * pass -1 to execute until nextEventTS reached
293 * Returns:
294 * number of tstates actually spent
296 int exec (int tscount=-1) nothrow @trusted {
297 static bool isRepeated (ushort opc) pure nothrow @safe @nogc { pragma(inline, true); return ((opc&0x10) != 0); }
298 static bool isBackward (ushort opc) pure nothrow @safe @nogc { pragma(inline, true); return ((opc&0x08) != 0); }
299 ubyte opcode;
300 bool gotDD, trueCC;
301 int disp;
302 ubyte tmpB, tmpC, rsrc, rdst;
303 ushort tmpW;
304 int tstart = tstates;
305 bpHit = false;
306 /* previous Q for SCF/CCF */
307 ubyte lastq = void;
309 /* `code` is guaranteed to be correct (i.e. [0..7]) */
310 static bool doCond (ubyte code, ubyte flags) {
312 final switch (code&0x07) {
313 case 0: return (flags&Z80Flag.Z) == 0;
314 case 1: return (flags&Z80Flag.Z) != 0;
315 case 2: return (flags&Z80Flag.C) == 0;
316 case 3: return (flags&Z80Flag.C) != 0;
317 case 4: return (flags&Z80Flag.PV) == 0;
318 case 5: return (flags&Z80Flag.PV) != 0;
319 case 6: return (flags&Z80Flag.S) == 0;
320 case 7: return (flags&Z80Flag.S) != 0;
322 assert(0);
324 /* branch-less (assuming that `!` is branch-less) code */
325 immutable ubyte[4] ccmask = [Z80Flag.Z, Z80Flag.C, Z80Flag.PV, Z80Flag.S];
326 return (!(flags&ccmask[code>>1]))^(code&0x01u);
329 //FIXME: call enter/leave hooks here too?
330 ubyte fetchOpcodeExt () nothrow @trusted {
331 //pragma(inline, true);
332 contention(PC, 4, true);
333 //contention(((I<<8)|R), 2, false); // memory refresh
334 // it doesn't really matter when R is incremented here
335 incR();
336 return peekb(PC++);
339 // main loop
340 while ((nextEventTS < 0 || tstates < nextEventTS) && (tscount < 0 || tstates-tstart <= tscount)) {
341 // process enter/leave hooks
342 execHook();
343 // process breakpoints
344 if (bpmap[PC] && checkBreakpoint()) { bpHit = true; return tstates-tstart; }
345 // read opcode -- OCR(4)
346 // t1: setting /MREQ & /RD
347 // t2: memory read
348 // t3, t4: decode command, increment R
349 // Woody: The Z80 places IR on to the address bus during T3+T4 of opcode fetch (M1) cycles for DRAM refresh,
350 // then if following cycles have internal operations before placing another address on the address bus
351 // then the ULA continues to see IR and contends as normal as it would with any other register paired address.
352 // it doesn't matter if we will really emulate IR here, 'cause T1 contention should end up in "non-contended" state
353 contention(PC, 4, true);
354 //contention(((I<<8)|R), 2, false); // memory refresh
355 if (evenM1 && (tstates&0x01)) ++tstates;
356 // read opcode
357 prevPC = lastPC;
358 lastPC = PC;
359 opcode = peekb(PC);
360 version(Zymosis_Run_Log) { { import core.stdc.stdio; stderr.fprintf("PC=%04X; OP:%02X; tstates=%d\n", PC, opcode, tstates); } }
361 ++PC;
362 incR();
363 /* previous Q for SCF/CCF */
364 lastq = flags_q;
365 flags_q = 0;
366 prevWasEIDDR = EIDDR.Normal;
367 disp = 0;
368 gotDD = false;
369 DD = &HL;
370 if (halted) { --PC; continue; }
371 // check for I[XY] prefix
372 if (opcode == 0xdd || opcode == 0xfd) {
373 //TODO: generate this table in compile time
374 static immutable uint[8] withIndexBmp = [0x00,0x700000,0x40404040,0x40bf4040,0x40404040,0x40404040,0x0800,0x00];
375 // IX/IY prefix
376 DD = (opcode == 0xdd ? cast(RegPair!("h", "l")*)&IX : cast(RegPair!("h", "l")*)&IY);
377 // read opcode -- OCR(4)
378 opcode = fetchOpcodeExt();
379 // test if this instruction have (HL)
380 if (withIndexBmp[opcode>>5]&(1<<(opcode&0x1f))) {
381 // 3rd byte is always DISP here
382 disp = peekb_3ts_args();
383 if (disp > 127) disp -= 256;
384 ++PC;
385 MEMPTR.w = (cast(int)DD.w+disp)&0xffff;
386 } else if (opcode == 0xdd && opcode == 0xfd) {
387 // double prefix; restart main loop
388 prevWasEIDDR = EIDDR.BlockInt;
389 /*FIXME: logically this should reset our Q value, but i am not sure; anyway, this works*/
390 flags_q = 0;
391 continue;
393 gotDD = true;
395 // ED-prefixed instructions
396 if (opcode == 0xed) {
397 DD = &HL; // а нас -- рать!
398 // read opcode -- OCR(4)
399 opcode = fetchOpcodeExt();
400 switch (opcode) {
401 // LDI, LDIR, LDD, LDDR
402 case 0xa0: case 0xb0: case 0xa8: case 0xb8:
403 tmpB = peekb_3ts(HL);
404 pokeb_3ts(DE, tmpB);
405 // MWR(5)
406 contention_by1ts(DE, 2);
407 --BC;
408 tmpB = (tmpB+AF.a)&0xff;
409 flags_q = AF.f = // BOO! FEAR THE MIGHTY BITS!
410 (tmpB&Z80Flag.F3)|(AF.f&(Z80Flag.C|Z80Flag.Z|Z80Flag.S))|
411 (BC != 0 ? Z80Flag.PV : 0)|
412 (tmpB&0x02 ? Z80Flag.F5 : 0);
413 if (isRepeated(opcode)) {
414 if (BC != 0) {
415 // IOP(5)
416 contention_by1ts(DE, 5);
417 // do it again
418 PC -= 2;
419 MEMPTR.w = (PC+1)&0xffff;
422 if (!isBackward(opcode)) { ++HL; ++DE; } else { --HL; --DE; }
423 break;
424 // CPI, CPIR, CPD, CPDR
425 case 0xa1: case 0xb1: case 0xa9: case 0xb9:
426 // MEMPTR
427 if (isRepeated(opcode) && (!(BC == 1 || memPeekB(HL) == AF.a))) {
428 MEMPTR.w = (lastPC+1)&0xffff;
429 } else {
430 MEMPTR.w = (cast(int)MEMPTR.w+(isBackward(opcode) ? -1 : 1))&0xffff;
432 tmpB = peekb_3ts(HL);
433 // IOP(5)
434 contention_by1ts(HL, 5);
435 --BC;
436 AF.f = // BOO! FEAR THE MIGHTY BITS!
437 Z80Flag.N|
438 (AF.f&Z80Flag.C)|
439 (BC != 0 ? Z80Flag.PV : 0)|
440 (cast(int)(AF.a&0x0f)-cast(int)(tmpB&0x0f) < 0 ? Z80Flag.H : 0);
441 tmpB = (cast(int)AF.a-cast(int)tmpB)&0xff;
442 AF.f |= (tmpB == 0 ? Z80Flag.Z : 0)|(tmpB&Z80Flag.S);
443 if (AF.f&Z80Flag.H) tmpB = (cast(ushort)tmpB-1)&0xff;
444 AF.f |= (tmpB&Z80Flag.F3)|(tmpB&0x02 ? Z80Flag.F5 : 0);
445 flags_q = AF.f;
446 if (isRepeated(opcode)) {
447 // repeated
448 if ((AF.f&(Z80Flag.Z|Z80Flag.PV)) == Z80Flag.PV) {
449 // IOP(5)
450 contention_by1ts(HL, 5);
451 // do it again
452 PC -= 2;
455 if (isBackward(opcode)) --HL; else ++HL;
456 break;
457 // OUTI, OTIR, OUTD, OTDR
458 case 0xa3: case 0xb3: case 0xab: case 0xbb:
459 --BC.b;
460 // fallthru
461 goto case 0xa2;
462 // INI, INIR, IND, INDR
463 case 0xa2: case 0xb2: case 0xaa: case 0xba:
464 MEMPTR.w = (cast(int)BC.w+(isBackward(opcode) ? -1 : 1))&0xffff;
465 // OCR(5)
466 contention_by1ts_ir(1);
467 if (opcode&0x01) {
468 // OUT*
469 tmpB = peekb_3ts(HL); // MRD(3)
470 port_write(BC, tmpB);
471 tmpW = cast(ushort)(HL+(isBackward(opcode) ? -1 : 1));
472 tmpC = (tmpB+tmpW)&0xff;
473 } else {
474 // IN*
475 tmpB = port_read(BC);
476 pokeb_3ts(HL, tmpB); // MWR(3)
477 --BC.b;
478 if (isBackward(opcode)) tmpC = (cast(int)tmpB+cast(int)BC.c-1)&0xff; else tmpC = (tmpB+BC.c+1)&0xff;
480 flags_q = AF.f =
481 (tmpB&0x80 ? Z80Flag.N : 0)|
482 (tmpC < tmpB ? Z80Flag.H|Z80Flag.C : 0)|
483 tblParity.ptr[(tmpC&0x07)^BC.b]|
484 tblSZ53.ptr[BC.b];
485 if (isRepeated(opcode)) {
486 // repeating commands
487 if (BC.b != 0) {
488 ushort a = (opcode&0x01 ? BC : HL);
489 // IOP(5)
490 contention_by1ts(a, 5);
491 // do it again
492 PC -= 2;
495 if (isBackward(opcode)) --HL; else ++HL;
496 break;
497 // not strings, but some good instructions anyway
498 default:
499 if ((opcode&0xc0) == 0x40) {
500 // 0x40...0x7f
501 final switch (opcode&0x07) {
502 // IN r8,(C)
503 case 0:
504 MEMPTR.w = (BC.w+1)&0xffff;
505 tmpB = port_read(BC);
506 flags_q = AF.f = tblSZP53.ptr[tmpB]|(AF.f&Z80Flag.C);
507 final switch ((opcode>>3)&0x07) {
508 case 0: BC.b = tmpB; break;
509 case 1: BC.c = tmpB; break;
510 case 2: DE.d = tmpB; break;
511 case 3: DE.e = tmpB; break;
512 case 4: HL.h = tmpB; break;
513 case 5: HL.l = tmpB; break;
514 case 6: break; // 6 affects only flags
515 case 7: AF.a = tmpB; break;
517 break;
518 // OUT (C),r8
519 case 1:
520 MEMPTR.w = (BC.w+1)&0xffff;
521 final switch ((opcode>>3)&0x07) {
522 case 0: tmpB = BC.b; break;
523 case 1: tmpB = BC.c; break;
524 case 2: tmpB = DE.d; break;
525 case 3: tmpB = DE.e; break;
526 case 4: tmpB = HL.h; break;
527 case 5: tmpB = HL.l; break;
528 case 6: tmpB = 0; break; // 0 on NMOS, 255 on CMOS
529 case 7: tmpB = AF.a; break;
531 port_write(BC, tmpB);
532 break;
533 // SBC HL,rr/ADC HL,rr
534 case 2:
535 // IOP(4),IOP(3)
536 contention_by1ts_ir(7);
537 switch ((opcode>>4)&0x03) {
538 case 0: tmpW = BC; break;
539 case 1: tmpW = DE; break;
540 case 2: tmpW = HL; break;
541 default: tmpW = SP; break;
543 HL = (opcode&0x08 ? ADC_DD(tmpW, HL) : SBC_DD(tmpW, HL));
544 break;
545 // LD (nn),rr/LD rr,(nn)
546 case 3:
547 tmpW = getpcw(0);
548 MEMPTR.w = (tmpW+1)&0xffff;
549 if (opcode&0x08) {
550 // LD rr,(nn)
551 final switch ((opcode>>4)&0x03) {
552 case 0: BC = peekw_6ts(tmpW); break;
553 case 1: DE = peekw_6ts(tmpW); break;
554 case 2: HL = peekw_6ts(tmpW); break;
555 case 3: SP = peekw_6ts(tmpW); break;
557 } else {
558 // LD (nn),rr
559 final switch ((opcode>>4)&0x03) {
560 case 0: pokew_6ts(tmpW, BC); break;
561 case 1: pokew_6ts(tmpW, DE); break;
562 case 2: pokew_6ts(tmpW, HL); break;
563 case 3: pokew_6ts(tmpW, SP); break;
566 break;
567 // NEG
568 case 4:
569 tmpB = AF.a;
570 AF.a = 0;
571 SUB_A(tmpB);
572 break;
573 // RETI/RETN
574 case 5:
575 // RETI: 0x4d, 0x5d, 0x6d, 0x7d
576 // RETN: 0x45, 0x55, 0x65, 0x75
577 IFF1 = IFF2;
578 MEMPTR.w = PC = pop_6ts();
579 if (opcode&0x08) {
580 // RETI
581 if (trapRETI(opcode)) return tstates-tstart;
582 } else {
583 // RETN
584 if (trapRETN(opcode)) return tstates-tstart;
586 break;
587 // IM n
588 case 6:
589 switch (opcode) {
590 case 0x56: case 0x76: mIM = 1; break;
591 case 0x5e: case 0x7e: mIM = 2; break;
592 default: mIM = 0; break;
594 break;
595 // specials
596 case 7:
597 final switch (opcode) {
598 // LD I,A
599 case 0x47:
600 // OCR(5)
601 contention_by1ts_ir(1);
602 I = AF.a;
603 break;
604 // LD R,A
605 case 0x4f:
606 // OCR(5)
607 contention_by1ts_ir(1);
608 R = AF.a;
609 break;
610 // LD A,I
611 case 0x57: LD_A_IR(I); break;
612 // LD A,R
613 case 0x5f: LD_A_IR(R); break;
614 // RRD
615 case 0x67: RRD_A(); break;
616 // RLD
617 case 0x6F: RLD_A(); break;
620 } else {
621 // slt and other traps
622 if (trapED(opcode)) return tstates-tstart;
624 break;
626 continue;
627 } // 0xed done
628 // CB-prefixed instructions
629 if (opcode == 0xcb) {
630 // shifts and bit operations
631 // read opcode -- OCR(4)
632 if (!gotDD) {
633 opcode = fetchOpcodeExt();
634 } else {
635 contention(PC, 3, true);
636 //FIXME: call enter/leave hooks here too?
637 opcode = peekb(PC);
638 contention_by1ts_pc(2, true);
639 ++PC;
641 if (gotDD) {
642 tmpW = cast(ushort)(cast(int)DD.w+disp);
643 tmpB = peekb_3ts(tmpW);
644 contention_by1ts(tmpW, 1);
645 } else {
646 final switch (opcode&0x07) {
647 case 0: tmpB = BC.b; break;
648 case 1: tmpB = BC.c; break;
649 case 2: tmpB = DE.d; break;
650 case 3: tmpB = DE.e; break;
651 case 4: tmpB = HL.h; break;
652 case 5: tmpB = HL.l; break;
653 case 6: tmpB = peekb_3ts(HL); contention(HL, 1, false); break;
654 case 7: tmpB = AF.a; break;
657 switch ((opcode>>3)&0x1f) {
658 case 0: tmpB = RLC(tmpB); break;
659 case 1: tmpB = RRC(tmpB); break;
660 case 2: tmpB = RL(tmpB); break;
661 case 3: tmpB = RR(tmpB); break;
662 case 4: tmpB = SLA(tmpB); break;
663 case 5: tmpB = SRA(tmpB); break;
664 case 6: tmpB = SLL(tmpB); break;
665 case 7: tmpB = SLR(tmpB); break;
666 default:
667 final switch ((opcode>>6)&0x03) {
668 case 1: BIT((opcode>>3)&0x07, tmpB, (gotDD || (opcode&0x07u) == 6)); break;
669 case 2: tmpB &= ~(1<<((opcode>>3)&0x07)); break; // RES
670 case 3: tmpB |= (1<<((opcode>>3)&0x07)); break; // SET
672 break;
674 if ((opcode&0xc0) != 0x40) {
675 // BITs are not welcome here
676 if (gotDD) {
677 // tmpW was set earlier
678 if ((opcode&0x07) != 6) pokeb_3ts(tmpW, tmpB);
680 final switch (opcode&0x07) {
681 case 0: BC.b = tmpB; break;
682 case 1: BC.c = tmpB; break;
683 case 2: DE.d = tmpB; break;
684 case 3: DE.e = tmpB; break;
685 case 4: HL.h = tmpB; break;
686 case 5: HL.l = tmpB; break;
687 case 6: pokeb_3ts(cast(ushort)(cast(int)DD.w+disp), tmpB); break;
688 case 7: AF.a = tmpB; break;
691 continue;
692 } // 0xcb done
693 // normal things
694 final switch (opcode&0xc0) {
695 // 0x00..0x3F
696 case 0x00:
697 final switch (opcode&0x07) {
698 // misc,DJNZ,JR,JR cc
699 case 0:
700 if (opcode&0x30) {
701 // branches
702 if (opcode&0x20) {
703 // JR cc
704 trueCC = doCond((opcode>>3)&0x03, AF.f);
705 } else {
706 // DJNZ/JR
707 if ((opcode&0x08) == 0) {
708 // DJNZ
709 // OCR(5)
710 contention_by1ts_ir(1);
711 --BC.b;
712 trueCC = (BC.b != 0);
713 } else {
714 // JR
715 trueCC = true;
718 //???disp = peekb_3ts_args(); // FUSE reads it only if condition was taken
719 if (trueCC) {
720 // execute branch (relative)
721 disp = peekb_3ts_args(); // FUSE reads it only if condition was taken
722 // IOP(5)
723 if (disp > 127) disp -= 256; // convert to int8_t
724 contention_by1ts_pc(5, false);
725 ++PC;
726 PC += disp;
727 MEMPTR.w = PC;
728 } else {
729 peekb_3ts_args_noread(); // FUSE reads it only if condition was taken
730 ++PC;
732 } else {
733 // EX AF,AF' or NOP
734 if (opcode != 0) exafaf();
736 break;
737 // LD rr,nn/ADD HL,rr
738 case 1:
739 if (opcode&0x08) {
740 // ADD HL,rr
741 // IOP(4),IOP(3)
742 contention_by1ts_ir(7);
743 final switch ((opcode>>4)&0x03) {
744 case 0: DD.w = ADD_DD(BC, DD.w); break;
745 case 1: DD.w = ADD_DD(DE, DD.w); break;
746 case 2: DD.w = ADD_DD(DD.w, DD.w); break;
747 case 3: DD.w = ADD_DD(SP, DD.w); break;
749 } else {
750 // LD rr,nn
751 tmpW = getpcw(0);
752 final switch ((opcode>>4)&0x03) {
753 case 0: BC = tmpW; break;
754 case 1: DE = tmpW; break;
755 case 2: DD.w = tmpW; break;
756 case 3: SP = tmpW; break;
759 break;
760 // LD xxx,xxx
761 case 2:
762 final switch ((opcode>>3)&0x07) {
763 // LD (BC),A
764 case 0: pokeb_3ts(BC, AF.a); MEMPTR.lo = (BC.c+1)&0xff; MEMPTR.hi = AF.a; break;
765 // LD A,(BC)
766 case 1: AF.a = peekb_3ts(BC); MEMPTR.w = (BC.w+1)&0xffff; break;
767 // LD (DE),A
768 case 2: pokeb_3ts(DE, AF.a); MEMPTR.lo = (DE.e+1)&0xff; MEMPTR.hi = AF.a; break;
769 // LD A,(DE)
770 case 3: AF.a = peekb_3ts(DE); MEMPTR.w = (DE.w+1)&0xffff; break;
771 // LD (nn),HL
772 case 4:
773 tmpW = getpcw(0);
774 MEMPTR.w = (tmpW+1)&0xffff;
775 pokew_6ts(tmpW, DD.w);
776 break;
777 // LD HL,(nn)
778 case 5:
779 tmpW = getpcw(0);
780 MEMPTR.w = (tmpW+1)&0xffff;
781 DD.w = peekw_6ts(tmpW);
782 break;
783 // LD (nn),A
784 case 6:
785 tmpW = getpcw(0);
786 MEMPTR.lo = (tmpW+1)&0xff;
787 MEMPTR.hi = AF.a;
788 pokeb_3ts(tmpW, AF.a);
789 break;
790 // LD A,(nn)
791 case 7:
792 tmpW = getpcw(0);
793 MEMPTR.w = (tmpW+1)&0xffff;
794 AF.a = peekb_3ts(tmpW);
795 break;
797 break;
798 // INC rr/DEC rr
799 case 3:
800 // OCR(6)
801 contention_by1ts_ir(2);
802 if (opcode&0x08) {
803 // DEC
804 final switch ((opcode>>4)&0x03) {
805 case 0: --BC; break;
806 case 1: --DE; break;
807 case 2: --DD.w; break;
808 case 3: --SP; break;
810 } else {
811 // INC
812 final switch ((opcode>>4)&0x03) {
813 case 0: ++BC; break;
814 case 1: ++DE; break;
815 case 2: ++DD.w; break;
816 case 3: ++SP; break;
819 break;
820 // INC r8
821 case 4:
822 final switch ((opcode>>3)&0x07) {
823 case 0: BC.b = INC8(BC.b); break;
824 case 1: BC.c = INC8(BC.c); break;
825 case 2: DE.d = INC8(DE.d); break;
826 case 3: DE.e = INC8(DE.e); break;
827 case 4: DD.h = INC8(DD.h); break;
828 case 5: DD.l = INC8(DD.l); break;
829 case 6:
830 if (gotDD) { --PC; contention_by1ts_pc(5, false); ++PC; }
831 tmpW = cast(ushort)(cast(int)DD.w+disp);
832 tmpB = peekb_3ts(tmpW);
833 contention_by1ts(tmpW, 1);
834 tmpB = INC8(tmpB);
835 pokeb_3ts(tmpW, tmpB);
836 break;
837 case 7: AF.a = INC8(AF.a); break;
839 break;
840 // DEC r8
841 case 5:
842 final switch ((opcode>>3)&0x07) {
843 case 0: BC.b = DEC8(BC.b); break;
844 case 1: BC.c = DEC8(BC.c); break;
845 case 2: DE.d = DEC8(DE.d); break;
846 case 3: DE.e = DEC8(DE.e); break;
847 case 4: DD.h = DEC8(DD.h); break;
848 case 5: DD.l = DEC8(DD.l); break;
849 case 6:
850 if (gotDD) { --PC; contention_by1ts_pc(5, false); ++PC; }
851 tmpW = cast(ushort)(cast(int)DD.w+disp);
852 tmpB = peekb_3ts(tmpW);
853 contention_by1ts(tmpW, 1);
854 tmpB = DEC8(tmpB);
855 pokeb_3ts(tmpW, tmpB);
856 break;
857 case 7: AF.a = DEC8(AF.a); break;
859 break;
860 // LD r8,n
861 case 6:
862 tmpB = peekb_3ts_args();
863 ++PC;
864 final switch ((opcode>>3)&0x07) {
865 case 0: BC.b = tmpB; break;
866 case 1: BC.c = tmpB; break;
867 case 2: DE.d = tmpB; break;
868 case 3: DE.e = tmpB; break;
869 case 4: DD.h = tmpB; break;
870 case 5: DD.l = tmpB; break;
871 case 6:
872 if (gotDD) { --PC; contention_by1ts_pc(2, false); ++PC; }
873 tmpW = cast(ushort)(cast(int)DD.w+disp);
874 pokeb_3ts(tmpW, tmpB);
875 break;
876 case 7: AF.a = tmpB; break;
878 break;
879 // swim-swim-hungry
880 case 7:
881 final switch ((opcode>>3)&0x07) {
882 case 0: RLCA(); break;
883 case 1: RRCA(); break;
884 case 2: RLA(); break;
885 case 3: RRA(); break;
886 case 4: DAA(); break;
887 case 5: // CPL
888 AF.a ^= 0xff;
889 flags_q = AF.f = (AF.a&Z80Flag.F35)|(Z80Flag.N|Z80Flag.H)|(AF.f&(Z80Flag.C|Z80Flag.PV|Z80Flag.Z|Z80Flag.S));
890 break;
891 case 6: // SCF
892 flags_q = AF.f = (AF.f&(Z80Flag.PV|Z80Flag.Z|Z80Flag.S))|((AF.a|(lastq^AF.f))&Z80Flag.F35)|Z80Flag.C;
893 break;
894 case 7: // CCF
895 flags_q = AF.f = (AF.f&(Z80Flag.PV|Z80Flag.Z|Z80Flag.S))|((AF.a|(lastq^AF.f))&Z80Flag.F35)|(AF.f&Z80Flag.C ? Z80Flag.H : Z80Flag.C);
896 break;
897 /* SCF/CCF:
898 * Patrik Rak however later discovered that the way how the flags 5 and 3 are affected after SCF/CCF actually depends on
899 * the previous instruction completed. In case of genuine Zilog CPU, if an instruction modifies the flags, the immediately
900 * following SCF/CCF does move of bits 5 and 3 from A to F, whereas if an instruction doesn't modify the flags (and after
901 * interrupt), the SCF/CCF does OR of bits 5 and 3 from A to F. In case of NEC and other clones, it is similar, except that
902 * instead of OR it does AND with some unknown value, making the result unreliable. */
904 break;
906 break;
907 // 0x40..0x7F: LD r8,r8
908 case 0x40:
909 if (opcode == 0x76) { halted = true; --PC; continue; } // HALT
910 rsrc = (opcode&0x07);
911 rdst = ((opcode>>3)&0x07);
912 final switch (rsrc) {
913 case 0: tmpB = BC.b; break;
914 case 1: tmpB = BC.c; break;
915 case 2: tmpB = DE.d; break;
916 case 3: tmpB = DE.e; break;
917 case 4: tmpB = (gotDD && rdst == 6 ? HL.h : DD.h); break;
918 case 5: tmpB = (gotDD && rdst == 6 ? HL.l : DD.l); break;
919 case 6:
920 if (gotDD) { --PC; contention_by1ts_pc(5, false); ++PC; }
921 tmpW = cast(ushort)(cast(int)DD.w+disp);
922 tmpB = peekb_3ts(tmpW);
923 break;
924 case 7: tmpB = AF.a; break;
926 final switch (rdst) {
927 case 0: BC.b = tmpB; break;
928 case 1: BC.c = tmpB; break;
929 case 2: DE.d = tmpB; break;
930 case 3: DE.e = tmpB; break;
931 case 4: if (gotDD && rsrc == 6) HL.h = tmpB; else DD.h = tmpB; break;
932 case 5: if (gotDD && rsrc == 6) HL.l = tmpB; else DD.l = tmpB; break;
933 case 6:
934 if (gotDD) { --PC; contention_by1ts_pc(5, false); ++PC; }
935 tmpW = cast(ushort)(cast(int)DD.w+disp);
936 pokeb_3ts(tmpW, tmpB);
937 break;
938 case 7: AF.a = tmpB; break;
940 break;
941 // 0x80..0xBF: ALU A,r8
942 case 0x80:
943 final switch (opcode&0x07) {
944 case 0: tmpB = BC.b; break;
945 case 1: tmpB = BC.c; break;
946 case 2: tmpB = DE.d; break;
947 case 3: tmpB = DE.e; break;
948 case 4: tmpB = DD.h; break;
949 case 5: tmpB = DD.l; break;
950 case 6:
951 if (gotDD) { --PC; contention_by1ts_pc(5, false); ++PC; }
952 tmpW = cast(ushort)(cast(int)DD.w+disp);
953 tmpB = peekb_3ts(tmpW);
954 break;
955 case 7: tmpB = AF.a; break;
957 final switch ((opcode>>3)&0x07) {
958 case 0: ADD_A(tmpB); break;
959 case 1: ADC_A(tmpB); break;
960 case 2: SUB_A(tmpB); break;
961 case 3: SBC_A(tmpB); break;
962 case 4: AND_A(tmpB); break;
963 case 5: XOR_A(tmpB); break;
964 case 6: OR_A(tmpB); break;
965 case 7: CP_A(tmpB); break;
967 break;
968 // 0xC0..0xFF
969 case 0xC0:
970 final switch (opcode&0x07) {
971 // RET cc
972 case 0:
973 contention_by1ts_ir(1);
974 trueCC = doCond((opcode>>3)&0x07, AF.f);
975 if (trueCC) MEMPTR.w = PC = pop_6ts();
976 break;
977 // POP rr/special0
978 case 1:
979 if (opcode&0x08) {
980 // special 0
981 final switch ((opcode>>4)&0x03) {
982 // RET
983 case 0: MEMPTR.w = PC = pop_6ts(); break;
984 // EXX
985 case 1: exx(); break;
986 // JP (HL)
987 case 2: PC = DD.w; break;
988 // LD SP,HL
989 case 3:
990 // OCR(6)
991 contention_by1ts_ir(2);
992 SP = DD.w;
993 break;
995 } else {
996 // POP rr
997 tmpW = pop_6ts();
998 final switch ((opcode>>4)&0x03) {
999 case 0: BC = tmpW; break;
1000 case 1: DE = tmpW; break;
1001 case 2: DD.w = tmpW; break;
1002 case 3: AF = tmpW; break;
1005 break;
1006 // JP cc,nn
1007 case 2:
1008 trueCC = doCond((opcode>>3)&0x07, AF.f);
1009 MEMPTR.w = getpcw(0);
1010 if (trueCC) PC = MEMPTR.w;
1011 break;
1012 // special1/special3
1013 case 3:
1014 final switch ((opcode>>3)&0x07) {
1015 // JP nn
1016 case 0: MEMPTR.w = PC = getpcw(0); break;
1017 // OUT (n),A
1018 case 2:
1019 tmpW = peekb_3ts_args();
1020 ++PC;
1021 MEMPTR.lo = (tmpW+1)&0xff;
1022 MEMPTR.hi = AF.a;
1023 tmpW |= AF.a<<8;
1024 port_write(tmpW, AF.a);
1025 break;
1026 // IN A,(n)
1027 case 3:
1028 tmpB = peekb_3ts_args();
1029 tmpW = cast(ushort)((AF.a<<8)|tmpB);
1030 ++PC;
1031 MEMPTR.w = (tmpW+1)&0xffff;
1032 AF.a = port_read(tmpW);
1033 break;
1034 // EX (SP),HL
1035 case 4:
1036 // SRL(3),SRH(4)
1037 tmpW = peekw_6ts(SP);
1038 contention_by1ts((SP+1)&0xffff, 1);
1039 // SWL(3),SWH(5)
1040 pokew_6ts_inverted(SP, DD.w);
1041 contention_by1ts(SP, 2);
1042 MEMPTR.w = DD.w = tmpW;
1043 break;
1044 // EX DE,HL
1045 case 5:
1046 tmpW = DE;
1047 DE = HL;
1048 HL = tmpW;
1049 break;
1050 // DI
1051 case 6: IFF1 = IFF2 = 0; break;
1052 // EI
1053 case 7: IFF1 = IFF2 = 1; prevWasEIDDR = EIDDR.BlockInt; break;
1055 break;
1056 // CALL cc,nn
1057 case 4:
1058 trueCC = doCond((opcode>>3)&0x07, AF.f);
1059 MEMPTR.w = getpcw(!!trueCC);
1060 if (trueCC) {
1061 push_6ts(PC);
1062 PC = MEMPTR.w;
1064 break;
1065 // PUSH rr/special2
1066 case 5:
1067 if (opcode&0x08) {
1068 if (((opcode>>4)&0x03) == 0) {
1069 // CALL
1070 MEMPTR.w = tmpW = getpcw(1);
1071 push_6ts(PC);
1072 PC = tmpW;
1074 } else {
1075 // PUSH rr
1076 // OCR(5)
1077 contention_by1ts_ir(1);
1078 switch ((opcode>>4)&0x03) {
1079 case 0: tmpW = BC; break;
1080 case 1: tmpW = DE; break;
1081 case 2: tmpW = DD.w; break;
1082 default: tmpW = AF; break;
1084 push_6ts(tmpW);
1086 break;
1087 // ALU A,n
1088 case 6:
1089 tmpB = peekb_3ts_args();
1090 ++PC;
1091 final switch ((opcode>>3)&0x07) {
1092 case 0: ADD_A(tmpB); break;
1093 case 1: ADC_A(tmpB); break;
1094 case 2: SUB_A(tmpB); break;
1095 case 3: SBC_A(tmpB); break;
1096 case 4: AND_A(tmpB); break;
1097 case 5: XOR_A(tmpB); break;
1098 case 6: OR_A(tmpB); break;
1099 case 7: CP_A(tmpB); break;
1101 break;
1102 // RST nnn
1103 case 7:
1104 // OCR(5)
1105 contention_by1ts_ir(1);
1106 push_6ts(PC);
1107 MEMPTR.w = PC = opcode&0x38;
1108 break;
1110 break;
1111 } // end switch
1113 return tstates-tstart;
1117 * Execute one instruction.
1118 * WARNING: this function ignores z80.nextEventTS.
1120 * Params:
1121 * none
1123 * Returns:
1124 * number of tstates spent
1126 int execStep () {
1127 int one = nextEventTS;
1128 nextEventTS = -1;
1129 int res = exec(1);
1130 nextEventTS = one;
1131 return res;
1134 /** Execute at least 'atstates' t-states; return real number of executed t-states.
1135 * WARNING: this function ignores z80.nextEventTS.
1137 * Params:
1138 * atstates = minimum tstates to spend
1140 * Returns:
1141 * number of tstates actually spent
1143 int execTS (in int atstates) {
1144 if (atstates > 0) {
1145 int one = nextEventTS;
1146 nextEventTS = -1;
1147 int res = exec();
1148 nextEventTS = one;
1149 return res;
1151 return 0;
1154 /** Initiate maskable interrupt (if interrupts are enabled).
1155 * May change z80.tstates.
1157 * Params:
1158 * none
1160 * Returns:
1161 * number of tstates taken by interrupt initiation or 0 if interrupts was disabled/ignored
1163 int intr () {
1164 int ots = tstates;
1165 /*FIXME: what is the state of `z80->flags_q` here? */
1166 if (prevWasEIDDR == EIDDR.LdIorR) { prevWasEIDDR = EIDDR.Normal; flags_q = (AF.f &= ~(Z80Flag.PV)); } // Z80 bug, NMOS only
1167 if (prevWasEIDDR == EIDDR.BlockInt || !IFF1) return 0; // not accepted
1168 flags_q = 0; /* we cannot do it earlier, because ignored interrupt won't reset flags */
1169 if (halted) { halted = false; ++PC; }
1170 IFF1 = IFF2 = false; // disable interrupts
1171 final switch (mIM&0x03) {
1172 case 3: /* ??? */ /*IM = 0;*/ /* fallthru */ goto case 0;
1173 case 0: // take instruction from the bus (for now we assume that reading from bus always returns 0xff)
1174 // with a CALL nnnn on the data bus, it takes 19 cycles:
1175 // M1 cycle: 7 T to acknowledge interrupt (where exactly data bus reading occures?)
1176 // M2 cycle: 3 T to read low byte of 'nnnn' from data bus
1177 // M3 cycle: 3 T to read high byte of 'nnnn' and decrement SP
1178 // M4 cycle: 3 T to write high byte of PC to the stack and decrement SP
1179 // M5 cycle: 3 T to write low byte of PC and jump to 'nnnn'
1180 // BUT! FUSE says this:
1181 // Only the first byte is provided directly to the Z80: all remaining bytes
1182 // of the instruction are fetched from memory using PC, which is incremented as normal.
1183 tstates += 6;
1184 // fallthru
1185 goto case 1;
1186 case 1: // just do RST #38
1187 incR();
1188 tstates += 7; // M1 cycle: 7 T to acknowledge interrupt and decrement SP
1189 // M2 cycle: 3 T states write high byte of PC to the stack and decrement SP
1190 // M3 cycle: 3 T states write the low byte of PC and jump to #0038
1191 push_6ts(PC);
1192 MEMPTR.w = PC = 0x38;
1193 break;
1194 case 2:
1195 incR();
1196 tstates += 7; // M1 cycle: 7 T to acknowledge interrupt and decrement SP
1197 // M2 cycle: 3 T states write high byte of PC to the stack and decrement SP
1198 // M3 cycle: 3 T states write the low byte of PC
1199 push_6ts(PC);
1200 // M4 cycle: 3 T to read high byte from the interrupt vector
1201 // M5 cycle: 3 T to read low byte from bus and jump to interrupt routine
1202 ushort a = ((cast(ushort)I)<<8)|0xff;
1203 MEMPTR.w = PC = peekw_6ts(a);
1204 break;
1206 return tstates-ots; // accepted
1209 /** Initiate non-maskable interrupt.
1210 * May change z80.tstates.
1212 * Params:
1213 * none
1215 * Returns:
1216 * number of tstates taken by interrupt initiation or 0 if interrupts was disabled/ignored
1218 int nmi () {
1219 int ots = tstates;
1220 /*FIXME: what is the state of `z80->flags_q` here? */
1221 if (prevWasEIDDR == EIDDR.LdIorR) { prevWasEIDDR = EIDDR.Normal; flags_q = (AF.f &= ~(Z80Flag.PV)); } // emulate Z80 bug with interrupted LD A,I/R, NMOS only
1222 if (prevWasEIDDR == EIDDR.BlockInt || !IFF1) return 0; // not accepted
1223 flags_q = 0; /* we cannot do it earlier, because ignored interrupt won't reset flags */
1224 if (halted) { halted = false; ++PC; }
1225 incR();
1226 IFF1 = false; // IFF2 is not changed
1227 tstates += 5; // M1 cycle: 5 T states to do an opcode read and decrement SP
1228 // M2 cycle: 3 T states write high byte of PC to the stack and decrement SP
1229 // M3 cycle: 3 T states write the low byte of PC and jump to #0066
1230 push_6ts(PC);
1231 MEMPTR.w = PC = 0x66;
1232 return tstates-ots;
1235 /** Pop 16-bit word from stack without contention and breakpoint triggering. Changes SP.
1237 * Params:
1238 * none
1240 * Returns:
1241 * popped word
1243 ushort pop () nothrow @trusted {
1244 ushort res = memPeekB(SP++);
1245 res |= memPeekB(SP++)<<8;
1246 return res;
1249 /** Push 16-bit word to stack without contention and breakpoint triggering. Changes SP.
1251 * Params:
1252 * value = word to push
1254 * Returns:
1255 * nothing
1257 void push (ushort value) nothrow @trusted {
1258 memPokeB(--SP, (value>>8)&0xff);
1259 memPokeB(--SP, value&0xff);
1262 /** Execute EXX command. */
1263 void exx () @safe nothrow @nogc {
1264 ushort t = BC; BC = BCx; BCx = t;
1265 t = DE; DE = DEx; DEx = t;
1266 t = HL; HL = HLx; HLx = t;
1269 /** Execute EX AF,AF' command. */
1270 void exafaf () @safe nothrow @nogc {
1271 ushort t = AF; AF = AFx; AFx = t;
1274 protected nothrow @trusted /*@nogc*/:
1275 /* ************************************************************************** */
1276 /* simulate contented memory access */
1277 /* (tstates = tstates+contention+atstates)*cnt */
1278 /* (ushort addr, int tstates, MemIO mio) */
1279 final void contention (ushort addr, int cnt, bool mreq) {
1280 version(Zymosis_Testing) memContention(addr, mreq);
1281 if (mem.ptr[addr/MemPage.Size].contended) {
1282 auto cc = (mreq ? ulacont : ulacontport);
1283 if (tstates >= 0 && tstates < cc.length) {
1284 tstates += cc.ptr[tstates];
1285 //{ import core.stdc.stdio; printf("CT: #%04X ts:%u; (%u)\n", cast(uint)PC, cast(uint)(tstates-3), cast(uint)ulacont.ptr[tstates-3]); }
1286 version(Zymosis_Check_Contention) if (cc.ptr[tstates] != 0) assert(0, "intertal error: contention tables are wrong");
1289 tstates += cnt;
1292 final void contention_by1ts (ushort addr, int cnt, bool mreq=true) {
1293 if (mem.ptr[addr/MemPage.Size].contended) {
1294 auto cc = (mreq ? ulacont : ulacontport);
1295 while (cnt-- > 0) {
1296 version(Zymosis_Testing) memContention(addr, mreq);
1297 if (tstates >= 0 && tstates < cc.length) {
1298 tstates += cc.ptr[tstates];
1299 version(Zymosis_Check_Contention) if (cc.ptr[tstates] != 0) assert(0, "intertal error: contention tables are wrong");
1301 ++tstates;
1303 } else {
1304 version(Zymosis_Testing) {
1305 while (cnt-- > 0) {
1306 memContention(addr, mreq);
1307 ++tstates;
1309 } else {
1310 tstates += cnt;
1315 final void contention_by1ts_ir (int cnt) { contention_by1ts(((I<<8)|R), cnt, false); }
1316 final void contention_by1ts_pc (int cnt, bool mreq) { contention_by1ts(PC, cnt, mreq); }
1318 /* ************************************************************************** */
1319 version(Zymosis_Testing) {} else {
1320 // port contention:
1321 // Ports with A0 low are contended at the 2nd T-state
1322 // Ports with A0 high are uncontended
1323 // Ports with a high byte between 0x40 and 0x7F comply with 1 and 2 above, but are additionally contended:
1324 // If A0 is low, the 1st T-state is also contended, 3rd and 4th are not.
1325 // If A0 is high, all T-states are contended
1326 // Reading the floating bus returns the second attribute byte of fetched pair (the last byte fetched), or 0xFF.
1327 final void doPortOp(bool lateio) (ushort port, scope void delegate (ushort port) nothrow @trusted doio) nothrow @trusted {
1328 bool ulaPort = ((port&0x01) == 0);
1329 if (mem.ptr[port/MemPage.Size].contended) {
1330 // the port looks like contended memory address
1331 // first tstate is contended
1332 if (tstates >= 0 && tstates < ulacontport.length) tstates += ulacontport.ptr[tstates];
1334 ++tstates;
1335 static if (!lateio) doio(port);
1336 // second tstate is contended for all ULA ports
1337 if (tstates >= 0 && tstates < ulacontport.length) tstates += ulacontport.ptr[tstates];
1338 ++tstates;
1339 // here port i/o occurs, at third tstate
1340 //static if (lateio) doio();
1341 // third and fourth tstates are contended for non-ULA ports
1342 if (mem.ptr[port/MemPage.Size].contended) {
1343 if (!ulaPort && tstates >= 0 && tstates < ulacontport.length) tstates += ulacontport.ptr[tstates];
1344 ++tstates;
1345 if (!ulaPort && tstates >= 0 && tstates < ulacontport.length) tstates += ulacontport.ptr[tstates];
1346 } else {
1347 ++tstates;
1349 static if (lateio) doio(port); // and FUSE says that it is here
1350 ++tstates;
1354 ubyte port_read (ushort port) {
1355 ubyte value;
1356 version(Zymosis_Testing) {
1357 portContention(port, 1, true, true); /* early */
1358 value = portRead(port);
1359 portContention(port, 2, true, false); /* normal */
1360 ++tstates;
1361 } else {
1362 doPortOp!true(port, (ushort port) { value = portRead(port); });
1364 return value;
1367 void port_write (ushort port, ubyte value) {
1368 version(Zymosis_Testing) {
1369 portContention(port, 1, false, true); /* early */
1370 portWrite(port, value);
1371 portContention(port, 2, false, false); /* normal */
1372 ++tstates;
1373 } else {
1374 doPortOp!false(port, (ushort port) { portWrite(port, value); });
1378 // ////////////////////////////////////////////////////////////////////////// //
1379 //TODO: memory breakpoints
1380 ubyte peekb (ushort addr) {
1381 pragma(inline, true);
1382 version(Zymosis_Testing) memReading(addr);
1383 return mem.ptr[addr/MemPage.Size].mem[addr%MemPage.Size];
1386 void pokeb (ushort addr, ubyte b) {
1387 version(Zymosis_Testing) memWriting(addr, b);
1388 if (auto mpg = mem.ptr+addr/MemPage.Size) {
1389 if (!mpg.rom) {
1390 if (mpg.writeHook) memWriteHook(addr, b);
1391 mpg.mem[addr%MemPage.Size] = b;
1396 // t1: setting /MREQ & /RD
1397 // t2: memory read
1398 ubyte peekb_3ts (ushort addr) {
1399 pragma(inline, true);
1400 version(Zymosis_Testing) {} else pragma(inline, true);
1401 contention(addr, 3, true);
1402 return peekb(addr);
1405 void peekb_3ts_args_noread () {
1406 version(Zymosis_Testing) {} else pragma(inline, true);
1407 contention(PC, 3, false); //FIXME???
1410 ubyte peekb_3ts_args () {
1411 pragma(inline, true);
1412 version(Zymosis_Testing) {} else pragma(inline, true);
1413 contention(PC, 3, true);
1414 return peekb(PC);
1417 // t1: setting /MREQ & /WR
1418 // t2: memory write
1419 void pokeb_3ts (ushort addr, ubyte b) {
1420 //pragma(inline, true);
1421 contention(addr, 3, true);
1422 pokeb(addr, b);
1425 ushort peekw_6ts (ushort addr) {
1426 version(Zymosis_Testing) {} else pragma(inline, true);
1427 ushort res = peekb_3ts(addr++);
1428 res |= peekb_3ts(addr)<<8;
1429 return res;
1432 void pokew_6ts (ushort addr, ushort value) {
1433 //pragma(inline, true);
1434 pokeb_3ts(addr, value&0xff);
1435 pokeb_3ts((addr+1)&0xffff, (value>>8)&0xff);
1438 void pokew_6ts_inverted (ushort addr, ushort value) {
1439 //pragma(inline, true);
1440 pokeb_3ts((addr+1)&0xffff, (value>>8)&0xff);
1441 pokeb_3ts(addr, value&0xff);
1444 ushort getpcw (int wait1) {
1445 //pragma(inline, true);
1446 ushort res = peekb_3ts_args();
1447 ++PC;
1448 res |= peekb_3ts_args()<<8;
1449 if (wait1) contention_by1ts(PC, wait1, true);
1450 ++PC;
1451 return res;
1455 ushort getpcw_cc (int wait1, bool truecc) {
1456 //pragma(inline, true);
1457 peekb_3ts_args_noread();
1458 version(Zymosis_Testing) {
1459 /*if (truecc)*/ memReading(PC);
1460 ushort res = memPeekB(PC++);
1461 } else {
1462 ushort res = peekb(PC++);
1464 peekb_3ts_args_noread();
1465 version(Zymosis_Testing) {
1466 /*if (truecc)*/ memReading(PC);
1467 res |= memPeekB(PC)<<8;
1468 } else {
1469 res |= peekb(PC)<<8;
1471 if (wait1) contention_by1ts_pc(wait1, true);
1472 ++PC;
1473 return res;
1477 ushort pop_6ts () {
1478 pragma(inline, true);
1479 ushort res = peekb_3ts(SP++);
1480 res |= peekb_3ts(SP++)<<8;
1481 return res;
1484 // 3 T states write high byte of PC to the stack and decrement SP
1485 // 3 T states write the low byte of PC and jump to #0066
1486 void push_6ts (ushort value) {
1487 //pragma(inline, true);
1488 pokeb_3ts(--SP, (value>>8)&0xff);
1489 pokeb_3ts(--SP, value&0xff);
1492 // you are not expected to understand the following bitmess
1493 // the only thing you want to know that IT WORKS; just believe me and testing suite
1494 nothrow @trusted @nogc {
1495 void ADC_A (ubyte b) {
1496 pragma(inline, true);
1497 ushort newv, o = AF.a;
1498 AF.a = (newv = cast(ushort)(o+b+(AF.f&Z80Flag.C)))&0xff; // Z80Flag.C is 0x01, so it's safe
1499 flags_q = AF.f =
1500 tblSZ53.ptr[newv&0xff]|
1501 (newv > 0xff ? Z80Flag.C : 0)|
1502 ((o^(~(b)))&(o^newv)&0x80 ? Z80Flag.PV : 0)|
1503 ((o&0x0f)+(b&0x0f)+(AF.f&Z80Flag.C) >= 0x10 ? Z80Flag.H : 0);
1506 void SBC_A (ubyte b) {
1507 pragma(inline, true);
1508 ushort newv, o = AF.a;
1509 AF.a = (newv = (cast(int)o-cast(int)b-cast(int)(AF.f&Z80Flag.C))&0xffff)&0xff; // Z80Flag.C is 0x01, so it's safe
1510 flags_q = AF.f =
1511 Z80Flag.N|
1512 tblSZ53.ptr[newv&0xff]|
1513 (newv > 0xff ? Z80Flag.C : 0)|
1514 ((o^b)&(o^newv)&0x80 ? Z80Flag.PV : 0)|
1515 (cast(int)(o&0x0f)-cast(int)(b&0x0f)-cast(int)(AF.f&Z80Flag.C) < 0 ? Z80Flag.H : 0);
1518 void ADD_A (ubyte b) {
1519 pragma(inline, true);
1520 AF.f &= ~(Z80Flag.C);
1521 ADC_A(b);
1524 void SUB_A (ubyte b) {
1525 pragma(inline, true);
1526 AF.f &= ~(Z80Flag.C);
1527 SBC_A(b);
1530 void CP_A (ubyte b) {
1531 pragma(inline, true);
1532 ubyte o = AF.a, newv = (cast(int)o-cast(int)b)&0xff;
1533 flags_q = AF.f =
1534 Z80Flag.N|
1535 (newv&Z80Flag.S)|
1536 (b&Z80Flag.F35)|
1537 (newv == 0 ? Z80Flag.Z : 0)|
1538 (o < b ? Z80Flag.C : 0)|
1539 ((o^b)&(o^newv)&0x80 ? Z80Flag.PV : 0)|
1540 (cast(int)(o&0x0f)-cast(int)(b&0x0f) < 0 ? Z80Flag.H : 0);
1543 void AND_A (ubyte b) { pragma(inline, true); flags_q = AF.f = tblSZP53.ptr[AF.a &= b]|Z80Flag.H; }
1544 void OR_A (ubyte b) { pragma(inline, true); flags_q = AF.f = tblSZP53.ptr[AF.a |= b]; }
1545 void XOR_A (ubyte b) { pragma(inline, true); flags_q = AF.f = tblSZP53.ptr[AF.a ^= b]; }
1547 // carry unchanged
1548 ubyte DEC8 (ubyte b) {
1549 pragma(inline, true);
1550 AF.f &= Z80Flag.C;
1551 AF.f |= Z80Flag.N|
1552 (b == 0x80 ? Z80Flag.PV : 0)|
1553 (b&0x0f ? 0 : Z80Flag.H)|
1554 tblSZ53.ptr[(cast(int)b-1)&0xff];
1555 flags_q = AF.f;
1556 return (cast(int)b-1)&0xff;
1559 // carry unchanged
1560 ubyte INC8 (ubyte b) {
1561 pragma(inline, true);
1562 AF.f &= Z80Flag.C;
1563 AF.f |=
1564 (b == 0x7f ? Z80Flag.PV : 0)|
1565 ((b+1)&0x0f ? 0 : Z80Flag.H )|
1566 tblSZ53.ptr[(b+1)&0xff];
1567 flags_q = AF.f;
1568 return ((b+1)&0xff);
1571 // cyclic, carry reflects shifted bit
1572 void RLCA () {
1573 pragma(inline, true);
1574 ubyte c = ((AF.a>>7)&0x01);
1575 AF.a = cast(ubyte)((AF.a<<1)|c);
1576 flags_q = AF.f = cast(ubyte)(c|(AF.a&Z80Flag.F35)|(AF.f&(Z80Flag.PV|Z80Flag.Z|Z80Flag.S)));
1579 // cyclic, carry reflects shifted bit
1580 void RRCA () {
1581 pragma(inline, true);
1582 ubyte c = (AF.a&0x01);
1583 AF.a = cast(ubyte)((AF.a>>1)|(c<<7));
1584 flags_q = AF.f = cast(ubyte)(c|(AF.a&Z80Flag.F35)|(AF.f&(Z80Flag.PV|Z80Flag.Z|Z80Flag.S)));
1587 // cyclic thru carry
1588 void RLA () {
1589 pragma(inline, true);
1590 ubyte c = ((AF.a>>7)&0x01);
1591 AF.a = cast(ubyte)((AF.a<<1)|(AF.f&Z80Flag.C));
1592 flags_q = AF.f = cast(ubyte)(c|(AF.a&Z80Flag.F35)|(AF.f&(Z80Flag.PV|Z80Flag.Z|Z80Flag.S)));
1595 // cyclic thru carry
1596 void RRA () {
1597 pragma(inline, true);
1598 ubyte c = (AF.a&0x01);
1599 AF.a = (AF.a>>1)|((AF.f&Z80Flag.C)<<7);
1600 flags_q = AF.f = c|(AF.a&Z80Flag.F35)|(AF.f&(Z80Flag.PV|Z80Flag.Z|Z80Flag.S));
1603 // cyclic thru carry
1604 ubyte RL (ubyte b) {
1605 pragma(inline, true);
1606 ubyte c = (b>>7)&Z80Flag.C;
1607 flags_q = AF.f = tblSZP53.ptr[(b = ((b<<1)&0xff)|(AF.f&Z80Flag.C))]|c;
1608 return b;
1611 ubyte RR (ubyte b) {
1612 pragma(inline, true);
1613 ubyte c = (b&0x01);
1614 flags_q = AF.f = tblSZP53.ptr[(b = (b>>1)|((AF.f&Z80Flag.C)<<7))]|c;
1615 return b;
1618 // cyclic, carry reflects shifted bit
1619 ubyte RLC (ubyte b) {
1620 pragma(inline, true);
1621 ubyte c = ((b>>7)&Z80Flag.C);
1622 flags_q = AF.f = tblSZP53.ptr[(b = ((b<<1)&0xff)|c)]|c;
1623 return b;
1626 // cyclic, carry reflects shifted bit
1627 ubyte RRC (ubyte b) {
1628 pragma(inline, true);
1629 ubyte c = (b&0x01);
1630 flags_q = AF.f = tblSZP53.ptr[(b = cast(ubyte)((b>>1)|(c<<7)))]|c;
1631 return b;
1634 ubyte SLA (ubyte b) {
1635 pragma(inline, true);
1636 ubyte c = ((b>>7)&0x01);
1637 flags_q = AF.f = tblSZP53.ptr[(b <<= 1)]|c;
1638 return b;
1641 ubyte SRA (ubyte b) {
1642 pragma(inline, true);
1643 ubyte c = (b&0x01);
1644 flags_q = AF.f = tblSZP53.ptr[(b = (b>>1)|(b&0x80))]|c;
1645 return b;
1648 ubyte SLL (ubyte b) {
1649 pragma(inline, true);
1650 ubyte c = ((b>>7)&0x01);
1651 flags_q = AF.f = tblSZP53.ptr[(b = cast(ubyte)((b<<1)|0x01))]|c;
1652 return b;
1655 ubyte SLR (ubyte b) {
1656 pragma(inline, true);
1657 ubyte c = (b&0x01);
1658 flags_q = AF.f = tblSZP53.ptr[(b >>= 1)]|c;
1659 return b;
1662 static immutable ubyte[8] hct = [ 0, Z80Flag.H, Z80Flag.H, Z80Flag.H, 0, 0, 0, Z80Flag.H ];
1664 // ddvalue+value
1665 ushort ADD_DD (ushort value, ushort ddvalue) {
1666 pragma(inline, true);
1667 uint res = cast(uint)value+cast(uint)ddvalue;
1668 ubyte b = ((value&0x0800)>>11)|((ddvalue&0x0800)>>10)|((res&0x0800)>>9);
1669 MEMPTR.w = (ddvalue+1)&0xffff;
1670 flags_q = AF.f =
1671 (AF.f&(Z80Flag.PV|Z80Flag.Z|Z80Flag.S))|
1672 (res > 0xffff ? Z80Flag.C : 0)|
1673 ((res>>8)&Z80Flag.F35)|
1674 hct.ptr[b];
1675 return cast(ushort)res;
1678 // ddvalue+value
1679 ushort ADC_DD (ushort value, ushort ddvalue) {
1680 pragma(inline, true);
1681 ubyte c = (AF.f&Z80Flag.C);
1682 uint newv = cast(uint)value+cast(uint)ddvalue+cast(uint)c;
1683 ushort res = newv&0xffff;
1684 MEMPTR.w = (ddvalue+1)&0xffff;
1685 flags_q = AF.f =
1686 ((res>>8)&Z80Flag.S35)|
1687 (res == 0 ? Z80Flag.Z : 0)|
1688 (newv > 0xffff ? Z80Flag.C : 0)|
1689 ((value^((~(ddvalue))&0xffff))&(value^newv)&0x8000 ? Z80Flag.PV : 0)|
1690 ((value&0x0fff)+(ddvalue&0x0fff)+c >= 0x1000 ? Z80Flag.H : 0);
1691 return res;
1694 // ddvalue-value
1695 ushort SBC_DD (ushort value, ushort ddvalue) {
1696 //pragma(inline, true);
1697 ubyte tmpB = AF.a;
1698 MEMPTR.w = (ddvalue+1)&0xffff;
1699 AF.a = ddvalue&0xff;
1700 SBC_A(value&0xff);
1701 ushort res = AF.a;
1702 AF.a = (ddvalue>>8)&0xff;
1703 SBC_A((value>>8)&0xff);
1704 res |= (AF.a<<8);
1705 AF.a = tmpB;
1706 flags_q = AF.f = (res ? AF.f&(~(Z80Flag.Z)) : AF.f|Z80Flag.Z);
1707 return res;
1710 void BIT (ubyte bit, ubyte num, bool mptr) {
1711 pragma(inline, true);
1712 AF.f =
1713 Z80Flag.H|
1714 (AF.f&Z80Flag.C)|
1715 (num&Z80Flag.F35)|
1716 (num&(1<<bit) ? 0 : Z80Flag.PV|Z80Flag.Z)|
1717 (bit == 7 ? num&Z80Flag.S : 0);
1718 if (mptr) AF.f = ((AF.f&~(Z80Flag.F35))|(MEMPTR.hi&Z80Flag.F35))&0xff;
1719 flags_q = AF.f;
1722 void DAA () {
1723 //pragma(inline, true);
1724 ubyte tmpI = 0, tmpC = (AF.f&Z80Flag.C), tmpA = AF.a;
1725 if ((AF.f&Z80Flag.H) || (tmpA&0x0f) > 9) tmpI = 6;
1726 if (tmpC != 0 || tmpA > 0x99) tmpI |= 0x60;
1727 if (tmpA > 0x99) tmpC = Z80Flag.C;
1728 if (AF.f&Z80Flag.N) SUB_A(tmpI); else ADD_A(tmpI);
1729 flags_q = AF.f = (AF.f&~(Z80Flag.C|Z80Flag.PV))|tmpC|tblParity.ptr[AF.a];
1731 } // //
1733 void RRD_A () {
1734 //pragma(inline, true);
1735 ubyte tmpB = peekb_3ts(HL);
1736 //IOP(4)
1737 MEMPTR.w = (HL+1)&0xffff;
1738 contention_by1ts(HL, 4, true);
1739 pokeb_3ts(HL, cast(ubyte)((AF.a<<4)|(tmpB>>4)));
1740 AF.a = (AF.a&0xf0)|(tmpB&0x0f);
1741 flags_q = AF.f = (AF.f&Z80Flag.C)|tblSZP53.ptr[AF.a];
1744 void RLD_A () {
1745 //pragma(inline, true);
1746 ubyte tmpB = peekb_3ts(HL);
1747 //IOP(4)
1748 MEMPTR.w = (HL+1)&0xffff;
1749 contention_by1ts(HL, 4, true);
1750 pokeb_3ts(HL, cast(ubyte)((tmpB<<4)|(AF.a&0x0f)));
1751 AF.a = (AF.a&0xf0)|(tmpB>>4);
1752 flags_q = AF.f = (AF.f&Z80Flag.C)|tblSZP53.ptr[AF.a];
1755 void LD_A_IR (ubyte ir) {
1756 pragma(inline, true);
1757 AF.a = ir;
1758 prevWasEIDDR = EIDDR.LdIorR;
1759 contention_by1ts_ir(1);
1760 flags_q = AF.f = tblSZ53.ptr[AF.a]|(AF.f&Z80Flag.C)|(IFF2 ? Z80Flag.PV : 0);
1763 // ////////////////////////////////////////////////////////////////////////// //
1764 // opcode info interface
1765 public:
1766 /// conditions for various jump instructions
1767 enum ZOICond {
1768 None = -1, ///
1769 NZ, Z, ///
1770 NC, C, ///
1771 PO, PE, ///
1772 P, M, ///
1773 BCNZ, BNZ, ///
1774 RETI, RETN ///
1777 /// indirect memory access type
1778 enum ZOIMemIO : int {
1779 None = -666, ///
1780 SP = -6, ///
1781 BC = -5, ///
1782 DE = -4, ///
1783 IY = -3, ///
1784 IX = -2, ///
1785 HL = -1 ///
1788 /// indirect jump type
1789 enum ZOIJump : int {
1790 None = -666, ///
1791 RET = -4, ///
1792 IY = -3, ///
1793 IX = -2, ///
1794 HL = -1 ///
1797 /// port i/o type
1798 enum ZOIPortIO : int {
1799 None = -666, ///
1800 BC = -1, ///
1801 BCM1 = -2 /// (B-1)C, for OUT*
1804 /// stack access type
1805 enum ZOIStack {
1806 None = -666, ///
1807 BC = 0, DE, HL, AF, IX, IY, PC ///
1810 enum ZOIDisp { None = 0xffff }
1812 /// opcode info
1813 struct ZOInfo {
1814 int len = 0; /// instruction length
1815 bool memrwword = false; /// true: reading word
1816 int memread = ZOIMemIO.None; /// ZOIMemIO or addr
1817 int memwrite = ZOIMemIO.None; /// ZOIMemIO or addr
1818 int jump = ZOIJump.None; /// ZOIJump or addr
1819 ZOICond cond = ZOICond.None; /// ZOICond
1820 int portread = ZOIPortIO.None; /// ZOIPortIO or addr; if addr is specified, high byte must be taken from A
1821 int portwrite = ZOIPortIO.None; /// ZOIPortIO or addr; if addr is specified, high byte must be taken from A
1822 ZOIStack push = ZOIStack.None; /// ZOIStack; CALL/RST will set ZOIStack.PC
1823 ZOIStack pop = ZOIStack.None; /// ZOIStack; RET will set ZOIStack.PC
1824 int disp = ZOIDisp.None; /// for (IX+n) / (IY+n), else ZOIDisp.None
1825 int trap = -1; /// slt and other trap opcode or -1
1828 /// Get opcode information. Useful for debuggers.
1829 final void opcodeInfo (ref ZOInfo nfo, ushort pc) nothrow @trusted {
1830 static bool isRepeated() (ushort opc) pure nothrow @safe @nogc { pragma(inline, true); return ((opc&0x10) != 0); }
1832 ushort orgpc = pc;
1833 int ixy = -1; // 0: ix; 1: iy
1834 int disp = 0;
1835 bool gotDD = false;
1836 nfo = nfo.init;
1838 ubyte memReadPC () nothrow @trusted { immutable ubyte b = mem.ptr[pc/MemPage.Size].mem[pc%MemPage.Size]; ++pc; return b; }
1839 ushort readPCW () nothrow @trusted { ushort res = memReadPC; res |= memReadPC<<8; return res; }
1841 ubyte opcode = memReadPC;
1842 if (opcode == 0xdd || opcode == 0xfd) {
1843 static immutable uint[8] withIndexBmp = [0x00u,0x700000u,0x40404040u,0x40bf4040u,0x40404040u,0x40404040u,0x0800u,0x00u];
1844 // IX/IY prefix
1845 ixy = (opcode == 0xfd ? 1 : 0); // just in case, hehe
1846 //opcode = memRead(pc++, MemIO.Other);
1847 opcode = memReadPC;
1848 if (withIndexBmp[opcode>>5]&(1<<(opcode&0x1f))) {
1849 // 3rd byte is always DISP here
1850 disp = memReadPC;
1851 if (disp > 127) disp -= 256; // convert to int8_t
1852 nfo.disp = disp;
1853 nfo.memread = (ixy ? ZOIMemIO.IY : ZOIMemIO.IX);
1854 } else if (opcode == 0xdd && opcode == 0xfd) {
1855 // double prefix
1856 nfo.len = 1;
1857 return;
1859 gotDD = 1;
1861 // instructions
1862 if (opcode == 0xed) {
1863 ixy = 0; // а нас -- рать!
1864 opcode = memReadPC;
1865 switch (opcode) {
1866 // LDI, LDIR, LDD, LDDR
1867 case 0xa0: case 0xb0: case 0xa8: case 0xb8:
1868 nfo.memwrite = ZOIMemIO.DE;
1869 goto case;
1870 // CPI, CPIR, CPD, CPDR
1871 case 0xa1: case 0xb1: case 0xa9: case 0xb9:
1872 nfo.memread = ZOIMemIO.HL;
1873 if (isRepeated(opcode)) { nfo.cond = ZOICond.BNZ; nfo.jump = orgpc; }
1874 break;
1875 // INI, INIR, IND, INDR
1876 case 0xa2: case 0xb2: case 0xaa: case 0xba:
1877 goto case;
1878 // OUTI, OTIR, OUTD, OTDR
1879 case 0xa3: case 0xb3: case 0xab: case 0xbb:
1880 if (opcode&0x01) nfo.portwrite = ZOIPortIO.BCM1; else nfo.portread = ZOIPortIO.BC;
1881 if (isRepeated(opcode)) { nfo.cond = ZOICond.BNZ; nfo.jump = orgpc; }
1882 break;
1883 // not strings, but some good instructions anyway
1884 default: // traps
1885 if ((opcode&0xc0) == 0x40) {
1886 switch (opcode&0x07) {
1887 // IN r8,(C)
1888 case 0: nfo.portread = ZOIPortIO.BC; break;
1889 // OUT (C),r8
1890 case 1: nfo.portwrite = ZOIPortIO.BC; break;
1891 // SBC HL,rr/ADC HL,rr
1892 /*case 2: break;*/
1893 // LD (nn),rr/LD rr,(nn)
1894 case 3:
1895 if (opcode&0x08) nfo.memread = readPCW; else nfo.memwrite = readPCW;
1896 nfo.memrwword = true;
1897 break;
1898 // NEG
1899 /*case 4: break;*/
1900 // RETI/RETN
1901 case 5:
1902 nfo.jump = ZOIJump.RET;
1903 nfo.pop = ZOIStack.PC;
1904 nfo.cond = (opcode&0x08 ? ZOICond.RETI : ZOICond.RETN);
1905 nfo.memread = ZOIMemIO.SP;
1906 nfo.memrwword = true;
1907 break;
1908 // IM n
1909 /*case 6: break;*/
1910 // specials
1911 case 7:
1912 switch (opcode) {
1913 // LD I,A
1914 /*case 0x47: break;*/
1915 // LD R,A
1916 /*case 0x4f: break;*/
1917 // LD A,I
1918 /*case 0x57: break;*/
1919 // LD A,R
1920 /*case break;*/
1921 // RRD
1922 case 0x67:
1923 // RLD
1924 case 0x6F:
1925 nfo.memread = nfo.memwrite = ZOIMemIO.HL;
1926 break;
1927 default:
1929 break;
1930 default:
1932 } else {
1933 nfo.trap = opcode;
1935 break;
1937 // 0xed done
1938 } else if (opcode == 0xcb) {
1939 // shifts and bit operations
1940 //opcode = memRead(pc++, MemIO.Other);
1941 opcode = memReadPC;
1942 if (!gotDD && (opcode&0x07) == 6) nfo.memread = nfo.memwrite = ZOIMemIO.HL;
1943 if ((opcode&0xc0) != 0x40) {
1944 if (gotDD) nfo.memwrite = nfo.memread; // all except BIT writes back
1945 } else {
1946 nfo.memwrite = ZOIMemIO.None;
1948 // 0xcb done
1949 } else {
1950 // normal things
1951 final switch (opcode&0xc0) {
1952 // 0x00..0x3F
1953 case 0x00:
1954 switch (opcode&0x07) {
1955 // misc,DJNZ,JR,JR cc
1956 case 0:
1957 if (opcode&0x30) {
1958 // branches
1959 if (opcode&0x20) nfo.cond = cast(ZOICond)((opcode>>3)&0x03); // JR cc
1960 else if ((opcode&0x08) == 0) nfo.cond = ZOICond.BNZ; // DJNZ ; else -- JR
1961 //disp = memRead(pc++, MemIO.Other);
1962 disp = memReadPC;
1963 if (disp > 127) disp -= 256; // convert to byte
1964 nfo.jump = (pc+disp)&0xffff;
1965 } // else EX AF,AF' or NOP
1966 break;
1967 // LD rr,nn/ADD HL,rr
1968 case 1:
1969 if (!(opcode&0x08)) pc = (pc+2)&0xffff;
1970 break;
1971 // LD xxx,xxx
1972 case 2:
1973 final switch ((opcode>>3)&0x07) {
1974 // LD (BC),A
1975 case 0: nfo.memwrite = ZOIMemIO.BC; break;
1976 // LD A,(BC)
1977 case 1: nfo.memread = ZOIMemIO.BC; break;
1978 // LD (DE),A
1979 case 2: nfo.memwrite = ZOIMemIO.DE; break;
1980 // LD A,(DE)
1981 case 3: nfo.memread = ZOIMemIO.DE; break;
1982 // LD (nn),HL
1983 case 4:
1984 nfo.memwrite = readPCW;
1985 nfo.memrwword = true;
1986 break;
1987 // LD HL,(nn)
1988 case 5:
1989 nfo.memread = readPCW;
1990 nfo.memrwword = true;
1991 break;
1992 // LD (nn),A
1993 case 6:
1994 nfo.memwrite = readPCW;
1995 break;
1996 // LD A,(nn)
1997 case 7:
1998 nfo.memread = readPCW;
1999 break;
2001 break;
2002 // INC rr/DEC rr
2003 /*case 3: break;*/
2004 // INC r8
2005 case 4:
2006 goto case;
2007 // DEC r8
2008 case 5:
2009 if (((opcode>>3)&0x07) == 6) {
2010 // (HL) or (IXY+n)
2011 if (gotDD) nfo.memwrite = nfo.memread;
2012 else nfo.memwrite = nfo.memread = ZOIMemIO.HL;
2014 break;
2015 // LD r8,n
2016 case 6:
2017 ++pc;
2018 if (((opcode>>3)&0x07) == 6) {
2019 if (!gotDD) nfo.memwrite = ZOIMemIO.HL;
2020 else { nfo.memwrite = nfo.memread; nfo.memread = ZOIMemIO.None; }
2022 break;
2023 // swim-swim-hungry
2024 /*case 7: break;*/
2025 default:
2027 break;
2028 // 0x40..0x7F: LD r8,r8
2029 case 0x40:
2030 if (opcode != 0x76) {
2031 if (!gotDD && (opcode&0x07) == 6) nfo.memread = ZOIMemIO.HL;
2032 if (((opcode>>3)&0x07) == 6) { nfo.memwrite = (gotDD ? nfo.memread : ZOIMemIO.HL); nfo.memread = ZOIMemIO.None; }
2034 break;
2035 // 0x80..0xBF: ALU A,r8
2036 case 0x80:
2037 if (!gotDD && (opcode&0x07) == 6) nfo.memread = ZOIMemIO.HL;
2038 break;
2039 // 0xC0..0xFF
2040 case 0xC0:
2041 final switch (opcode&0x07) {
2042 // RET cc
2043 case 0:
2044 nfo.jump = ZOIJump.RET;
2045 nfo.pop = ZOIStack.PC;
2046 nfo.cond = cast(ZOICond)((opcode>>3)&0x07);
2047 nfo.memread = ZOIMemIO.SP;
2048 nfo.memrwword = true;
2049 break;
2050 // POP rr/special0
2051 case 1:
2052 if (opcode&0x08) {
2053 // special 0
2054 switch ((opcode>>4)&0x03) {
2055 // RET
2056 case 0:
2057 nfo.jump = ZOIJump.RET;
2058 nfo.pop = ZOIStack.PC;
2059 nfo.memread = ZOIMemIO.SP;
2060 nfo.memrwword = true;
2061 break;
2062 // EXX
2063 /*case 1: break;*/
2064 // JP (HL)
2065 case 2:
2066 nfo.jump = (ixy < 0 ? ZOIJump.HL : (ixy ? ZOIJump.IY : ZOIJump.IX));
2067 break;
2068 // LD SP,HL
2069 /*case 3: break;*/
2070 default:
2072 } else {
2073 // POP rr
2074 nfo.memread = ZOIMemIO.SP;
2075 nfo.memrwword = true;
2076 final switch ((opcode>>4)&0x03) {
2077 case 0: nfo.pop = ZOIStack.BC; break;
2078 case 1: nfo.pop = ZOIStack.DE; break;
2079 case 2: nfo.pop = (ixy < 0 ? ZOIStack.HL : (ixy ? ZOIStack.IY : ZOIStack.IX)); break;
2080 case 3: nfo.pop = ZOIStack.AF; break;
2083 break;
2084 // JP cc,nn
2085 case 2:
2086 nfo.jump = readPCW;
2087 nfo.cond = cast(ZOICond)((opcode>>3)&0x07);
2088 break;
2089 // special1/special3
2090 case 3:
2091 switch ((opcode>>3)&0x07) {
2092 // JP nn
2093 case 0:
2094 nfo.jump = readPCW;
2095 break;
2096 // OUT (n),A
2097 case 2:
2098 //nfo.portwrite = memRead(pc++, MemIO.Other);
2099 nfo.portwrite = memReadPC;
2100 break;
2101 // IN A,(n)
2102 case 3:
2103 //nfo.portread = memRead(pc++, MemIO.Other);
2104 nfo.portread = memReadPC;
2105 break;
2106 // EX (SP),HL
2107 case 4:
2108 nfo.memread = nfo.memwrite = ZOIMemIO.SP;
2109 nfo.memrwword = true;
2110 break;
2111 // EX DE,HL
2112 /*case 5: break;*/
2113 // DI
2114 /*case 6: break;*/
2115 // EI
2116 /*case 7: break;*/
2117 default:
2119 break;
2120 // CALL cc,nn
2121 case 4:
2122 nfo.jump = readPCW;
2123 nfo.push = ZOIStack.PC;
2124 nfo.cond = cast(ZOICond)((opcode>>3)&0x07);
2125 nfo.memwrite = ZOIMemIO.SP;
2126 nfo.memrwword = true;
2127 break;
2128 // PUSH rr/special2
2129 case 5:
2130 if (opcode&0x08) {
2131 if (((opcode>>4)&0x03) == 0) {
2132 // CALL
2133 nfo.jump = readPCW;
2134 nfo.push = ZOIStack.PC;
2135 nfo.memwrite = ZOIMemIO.SP;
2136 nfo.memrwword = true;
2138 } else {
2139 // PUSH rr
2140 nfo.memwrite = ZOIMemIO.SP;
2141 nfo.memrwword = true;
2142 final switch ((opcode>>4)&0x03) {
2143 case 0: nfo.push = ZOIStack.BC; break;
2144 case 1: nfo.push = ZOIStack.DE; break;
2145 case 2: nfo.push = (ixy >= 0 ? (ixy ? ZOIStack.IY : ZOIStack.IX) : ZOIStack.HL); break;
2146 case 3: nfo.push = ZOIStack.AF; break;
2149 break;
2150 // ALU A,n
2151 case 6:
2152 ++pc;
2153 break;
2154 // RST nnn
2155 case 7:
2156 nfo.jump = (opcode&0x38);
2157 nfo.push = ZOIStack.PC;
2158 nfo.memwrite = ZOIMemIO.SP;
2159 nfo.memrwword = true;
2160 break;
2162 break;
2165 nfo.len = (pc >= orgpc ? pc-orgpc : pc+0x10000-orgpc);
2168 // ////////////////////////////////////////////////////////////////////////// //
2169 // simple disasm
2170 public:
2171 /// disassembled opcode
2172 struct ZDisop {
2173 int len; /// instruction length
2174 char[128] disbuf; /// buffer that holds disassembled text
2175 const(char)[] mnemo; /// points into disbuf
2176 const(char)[][3] args; /// points into disbuf
2177 // disassembler options
2178 bool decimal; /// use decimal numbers instead of #XXXX
2179 bool locase; /// use lower-cased text
2182 /// Disassemble command
2183 final void disasm (ref ZDisop nfo, ushort pc) nothrow @trusted {
2184 ubyte memReadPC () nothrow @trusted { /*pragma(inline, true);*/ immutable ubyte b = mem.ptr[pc/MemPage.Size].mem[pc%MemPage.Size]; ++pc; return b; }
2185 ushort memReadPCW () nothrow @trusted {
2186 ushort w = mem.ptr[pc/MemPage.Size].mem[pc%MemPage.Size];
2187 ++pc;
2188 w |= mem.ptr[pc/MemPage.Size].mem[pc%MemPage.Size]<<8;
2189 ++pc;
2190 return w;
2193 uint stpc = pc;
2194 scope(exit) nfo.len = (pc < stpc ? pc+0x10000-stpc : pc-stpc);
2196 bool gotDD;
2197 int disp;
2198 string DD = "HL";
2200 nfo.len = 0;
2201 nfo.mnemo = null;
2202 nfo.args[] = null;
2203 uint dbpos = 0;
2204 uint dbstpos = 0;
2205 ubyte curarg = 0;
2207 void endMnemo () {
2208 if (nfo.mnemo.length) assert(0, "internal error");
2209 nfo.mnemo = nfo.disbuf[0..dbpos];
2210 dbstpos = dbpos;
2213 void endArg () {
2214 if (curarg >= nfo.args.length) assert(0, "internal error");
2215 nfo.args[curarg++] = nfo.disbuf[dbstpos..dbpos];
2216 dbstpos = dbpos;
2219 void putKeepCase (const(char)[] s...) nothrow @trusted {
2220 assert(s.length <= 1024);
2221 if (dbpos+s.length > nfo.disbuf.length) s = s[0..nfo.disbuf.length-dbpos];
2222 if (s.length) {
2223 nfo.disbuf[dbpos..dbpos+s.length] = s[];
2224 dbpos += cast(uint)s.length;
2228 void put (const(char)[] s...) nothrow @trusted {
2229 assert(s.length <= 1024);
2230 if (dbpos+s.length > nfo.disbuf.length) s = s[0..nfo.disbuf.length-dbpos];
2231 foreach (char ch; s) {
2232 if (nfo.locase) {
2233 if (ch >= 'A' && ch <= 'Z') ch += 32;
2234 } else {
2235 if (ch >= 'a' && ch <= 'z') ch -= 32;
2237 nfo.disbuf.ptr[dbpos++] = ch;
2241 void putMnemo (string s) nothrow @trusted {
2242 put(s);
2243 endMnemo();
2246 void putArg (string s) nothrow @trusted {
2247 put(s);
2248 endArg();
2251 void putUIntDec (uint n) nothrow @trusted {
2252 char[16] buf = void;
2253 uint bpos = cast(uint)buf.length;
2254 do { buf.ptr[--bpos] = cast(char)('0'+n%10); } while ((n /= 10) != 0);
2255 put(buf[bpos..$]);
2258 void putUIntHex (uint n, int len) nothrow @trusted {
2259 char[16] buf = void;
2260 uint bpos = cast(uint)buf.length;
2261 if (len > 16) len = 16;
2262 do { buf.ptr[--bpos] = cast(char)('0'+n%16+(n%16 > 9 ? 7 : 0)); } while ((n /= 16) != 0);
2263 while (buf.length-bpos < len) buf.ptr[--bpos] = '0';
2264 buf.ptr[--bpos] = '#';
2265 put(buf[bpos..$]);
2268 void putUInt (uint n, int len) nothrow @trusted {
2269 if (nfo.decimal) putUIntDec(n); else putUIntHex(n, len);
2272 void putDisp (int n) nothrow @trusted {
2273 if (n < 0) {
2274 put("-");
2275 putUIntDec(-n);
2276 } else {
2277 put("+");
2278 putUIntDec(n);
2282 void putCC (uint n) {
2283 final switch (n) {
2284 case 0: putArg("NZ"); break;
2285 case 1: putArg("Z"); break;
2286 case 2: putArg("NC"); break;
2287 case 3: putArg("C"); break;
2288 case 4: putArg("PO"); break;
2289 case 5: putArg("PE"); break;
2290 case 6: putArg("P"); break;
2291 case 7: putArg("M"); break;
2295 void putR8(string hlspec=null) (uint n) {
2296 final switch (n) {
2297 case 0: putArg("B"); break;
2298 case 1: putArg("C"); break;
2299 case 2: putArg("D"); break;
2300 case 3: putArg("E"); break;
2301 case 4: putArg("H"); break;
2302 case 5: putArg("L"); break;
2303 case 6:
2304 static if (hlspec.length) {
2305 putArg(hlspec);
2306 } else {
2307 put("(");
2308 put(DD);
2309 if (gotDD) putDisp(disp);
2310 put(")");
2312 break;
2313 case 7: putArg("A"); break;
2317 void putR16SP(string hlspec=null) (uint n) {
2318 final switch (n) {
2319 case 0: putArg("BC"); break;
2320 case 1: putArg("DE"); break;
2321 case 2: static if (hlspec.length) putArg(hlspec); else putArg(DD); break;
2322 case 3: putArg("SP"); break;
2326 void putR16AF(string hlspec=null) (uint n) {
2327 final switch (n) {
2328 case 0: putArg("BC"); break;
2329 case 1: putArg("DE"); break;
2330 case 2: static if (hlspec.length) putArg(hlspec); else putArg(DD); break;
2331 case 3: putArg("AF"); break;
2335 ubyte opcode = memReadPC;
2336 disp = 0;
2337 gotDD = false;
2338 //DD = &HL;
2339 // check for I[XY] prefix
2340 if (opcode == 0xdd || opcode == 0xfd) {
2341 //TODO: generate this table in compile time
2342 static immutable uint[8] withIndexBmp = [0x00,0x700000,0x40404040,0x40bf4040,0x40404040,0x40404040,0x0800,0x00];
2343 // IX/IY prefix
2344 DD = (opcode == 0xdd ? "IX" : "IY");
2345 // read opcode -- OCR(4)
2346 opcode = memReadPC;
2347 // test if this instruction have (HL)
2348 if (withIndexBmp[opcode>>5]&(1<<(opcode&0x1f))) {
2349 // 3rd byte is always DISP here
2350 disp = memReadPC;
2351 if (disp > 127) disp -= 256;
2352 } else if (opcode == 0xdd && opcode == 0xfd) {
2353 // double prefix; put special NOP
2354 --pc; // rollback for correct length
2355 putMnemo("NOP");
2356 if (opcode == 0xdd) putArg("#DD"); else putArg("#FD");
2357 return;
2359 gotDD = true;
2361 // ED-prefixed instructions
2362 if (opcode == 0xed) {
2363 DD = "HL"; // а нас -- рать!
2364 // read opcode -- OCR(4)
2365 opcode = memReadPC;
2366 switch (opcode) {
2367 // LDI, LDIR, LDD, LDDR
2368 case 0xa0: putMnemo("LDI"); return;
2369 case 0xb0: putMnemo("LDIR"); return;
2370 case 0xa8: putMnemo("LDD"); return;
2371 case 0xb8: putMnemo("LDDR"); return;
2372 // CPI, CPIR, CPD, CPDR
2373 case 0xa1: putMnemo("CPI"); return;
2374 case 0xb1: putMnemo("CPIR"); return;
2375 case 0xa9: putMnemo("CPD"); return;
2376 case 0xb9: putMnemo("CPDR"); return;
2377 // OUTI, OTIR, OUTD, OTDR
2378 case 0xa3: putMnemo("OUTI"); return;
2379 case 0xb3: putMnemo("OTIR"); return;
2380 case 0xab: putMnemo("OUTD"); return;
2381 case 0xbb: putMnemo("OTDR"); return;
2382 // INI, INIR, IND, INDR
2383 case 0xa2: putMnemo("INI"); return;
2384 case 0xb2: putMnemo("INIR"); return;
2385 case 0xaa: putMnemo("IND"); return;
2386 case 0xba: putMnemo("INDR"); return;
2387 // not strings, but some good instructions anyway
2388 default:
2389 if ((opcode&0xc0) == 0x40) {
2390 // 0x40...0x7f
2391 final switch (opcode&0x07) {
2392 case 0: // IN r8,(C)
2393 case 1: // OUT (C),r8
2394 if (opcode&0x07) { putMnemo("OUT"); putArg("(C)"); } else putMnemo("IN");
2395 if (opcode&0x07) putR8!"0"((opcode>>3)&0x07); else putR8!"F"((opcode>>3)&0x07); // 0 on NMOS, 255 on CMOS
2396 if ((opcode&0x07) == 0) putArg("(C)");
2397 return;
2398 // SBC HL,rr/ADC HL,rr
2399 case 2:
2400 putMnemo(opcode&0x08 ? "ADC" : "SBC");
2401 putArg("HL");
2402 putR16SP!"HL"((opcode>>4)&0x03);
2403 return;
2404 // LD (nn),rr/LD rr,(nn)
2405 case 3:
2406 putMnemo("LD");
2407 if ((opcode&0x08) == 0) {
2408 // LD (nn),rr
2409 put("(");
2410 putUInt(memReadPC, 4);
2411 put(")");
2412 endArg();
2414 putR16SP!"HL"((opcode>>4)&0x03);
2415 if ((opcode&0x08) != 0) {
2416 // LD rr,(nn)
2417 put("(");
2418 putUInt(memReadPC, 4);
2419 put(")");
2420 endArg();
2422 return;
2423 // NEG
2424 case 4:
2425 putMnemo("NEG");
2426 return;
2427 // RETI/RETN
2428 case 5:
2429 // RETI: 0x4d, 0x5d, 0x6d, 0x7d
2430 // RETN: 0x45, 0x55, 0x65, 0x75
2431 if (opcode&0x08) putMnemo("RETI"); else putMnemo("RETN");
2432 return;
2433 // IM n
2434 case 6:
2435 putMnemo("IM");
2436 switch (opcode) {
2437 case 0x56: case 0x76: putArg("1"); break;
2438 case 0x5e: case 0x7e: putArg("2"); break;
2439 default: putArg("0"); break;
2441 return;
2442 // specials
2443 case 7:
2444 final switch (opcode) {
2445 case 0x47: // LD I,A
2446 putMnemo("LD");
2447 putArg("I");
2448 putArg("A");
2449 return;
2450 case 0x4f: // LD R,A
2451 putMnemo("LD");
2452 putArg("R");
2453 putArg("A");
2454 return;
2455 case 0x57: // LD A,I
2456 putMnemo("LD");
2457 putArg("A");
2458 putArg("I");
2459 return;
2460 case 0x5f: // LD A,R
2461 putMnemo("LD");
2462 putArg("A");
2463 putArg("R");
2464 return;
2465 case 0x67: // RRD
2466 putMnemo("RRD");
2467 return;
2468 case 0x6F: // RLD
2469 putMnemo("RLD");
2470 return;
2473 } else {
2474 // slt and other traps
2475 if (opcode == 0xFB) {
2476 putMnemo("TSLT");
2477 } else {
2478 putMnemo("TRAP");
2479 putUIntHex(opcode, 2);
2480 endArg();
2483 return;
2485 // NOP
2486 putMnemo("NOP");
2487 putArg("#ED");
2488 putUIntHex(opcode, 2);
2489 endArg();
2490 return;
2491 } // 0xed done
2492 // CB-prefixed instructions
2493 if (opcode == 0xcb) {
2494 // shifts and bit operations
2495 // read opcode
2496 opcode = memReadPC;
2497 // mnemonics
2498 switch ((opcode>>3)&0x1f) {
2499 case 0: putMnemo("RLC"); break;
2500 case 1: putMnemo("RRC"); break;
2501 case 2: putMnemo("RL"); break;
2502 case 3: putMnemo("RR"); break;
2503 case 4: putMnemo("SLA"); break;
2504 case 5: putMnemo("SRA"); break;
2505 case 6: putMnemo("SLL"); break;
2506 case 7: putMnemo("SLR"); break;
2507 default:
2508 final switch ((opcode>>6)&0x03) {
2509 case 1: putMnemo("BIT"); break;
2510 case 2: putMnemo("RES"); break;
2511 case 3: putMnemo("SET"); break;
2513 putUIntDec((opcode>>3)&0x07);
2514 endArg();
2515 break;
2517 // R8X arg
2518 if (gotDD) {
2519 put("(");
2520 put(DD);
2521 putDisp(disp);
2522 put(")");
2523 endArg();
2524 } else {
2525 putR8!"(HL)"(opcode&0x07);
2527 // possible extra operand
2528 if ((opcode&0xc0) != 0x40) {
2529 // BITs are not welcome here
2530 putR8(opcode&0x07);
2532 return;
2533 } // 0xcb done
2534 // normal things
2535 final switch (opcode&0xc0) {
2536 // 0x00..0x3F
2537 case 0x00:
2538 final switch (opcode&0x07) {
2539 // misc,DJNZ,JR,JR cc
2540 case 0:
2541 if (opcode&0x30) {
2542 // branches
2543 if (opcode&0x20) {
2544 // JR cc
2545 putMnemo("JR");
2546 putCC((opcode>>3)&0x03);
2547 } else {
2548 // DJNZ/JR
2549 putMnemo(opcode&0x08 ? "JR" : "DJNZ");
2551 disp = memReadPC;
2552 if (disp > 127) disp -= 256; // convert to int8_t
2553 ushort addr = cast(ushort)(pc+disp);
2554 putUInt(addr, 4);
2555 endArg();
2556 } else {
2557 // EX AF,AF' or NOP
2558 if (opcode != 0) {
2559 putMnemo("EX");
2560 putArg("AF");
2561 putArg("AF'");
2562 } else {
2563 putMnemo("NOP");
2566 return;
2567 // LD rr,nn/ADD HL,rr
2568 case 1:
2569 if (opcode&0x08) {
2570 // ADD HL,rr
2571 putMnemo("ADD");
2572 putArg(DD);
2573 putR16SP((opcode>>4)&0x03);
2574 } else {
2575 // LD rr,nn
2576 putMnemo("LD");
2577 putR16SP((opcode>>4)&0x03);
2578 putUInt(memReadPCW, 4);
2579 endArg();
2581 return;
2582 // LD xxx,xxx
2583 case 2:
2584 putMnemo("LD");
2585 final switch ((opcode>>3)&0x07) {
2586 // LD (BC),A
2587 case 0: putArg("(BC)"); putArg("A"); break;
2588 // LD A,(BC)
2589 case 1: putArg("A"); putArg("(BC)"); break;
2590 // LD (DE),A
2591 case 2: putArg("(DE)"); putArg("A"); break;
2592 // LD A,(DE)
2593 case 3: putArg("A"); putArg("(DE)"); break;
2594 // LD (nn),HL
2595 case 4: put("("); putUInt(memReadPCW, 4); put(")"); endArg(); put(DD); endArg(); break;
2596 // LD HL,(nn)
2597 case 5: put(DD); endArg(); put("("); putUInt(memReadPCW, 4); put(")"); endArg(); break;
2598 // LD (nn),A
2599 case 6: put("("); putUInt(memReadPCW, 4); put(")"); endArg(); putArg("A"); break;
2600 // LD A,(nn)
2601 case 7: putArg("A"); put("("); putUInt(memReadPCW, 4); put(")"); endArg(); break;
2603 return;
2604 // INC rr/DEC rr
2605 case 3:
2606 putMnemo(opcode&0x08 ? "DEC" : "INC");
2607 putR16SP((opcode>>4)&0x03);
2608 return;
2609 // INC/DEC r8; LD r8,n
2610 case 4: // INC r8
2611 case 5: // DEC r8
2612 case 6: // LD r8,n
2613 final switch (opcode&0x07) {
2614 case 4: putMnemo("INC"); break;
2615 case 5: putMnemo("DEC"); break;
2616 case 6: putMnemo("LD"); break;
2618 final switch ((opcode>>3)&0x07) {
2619 case 0: putArg("B"); break;
2620 case 1: putArg("C"); break;
2621 case 2: putArg("D"); break;
2622 case 3: putArg("E"); break;
2623 case 4: if (gotDD) put(DD); putArg("H"); endArg(); break;
2624 case 5: if (gotDD) put(DD); putArg("L"); endArg(); break;
2625 case 6:
2626 put("(");
2627 put(DD);
2628 if (gotDD) putDisp(disp);
2629 put(")");
2630 endArg();
2631 break;
2632 case 7: putArg("A"); break;
2634 // LD?
2635 if ((opcode&0x07) == 6) {
2636 putUInt(memReadPC, 2);
2637 endArg();
2639 return;
2640 // swim-swim-hungry
2641 case 7:
2642 final switch ((opcode>>3)&0x07) {
2643 case 0: putMnemo("RLCA"); break;
2644 case 1: putMnemo("RRCA"); break;
2645 case 2: putMnemo("RLA"); break;
2646 case 3: putMnemo("RRA"); break;
2647 case 4: putMnemo("DAA"); break;
2648 case 5: putMnemo("CPL"); break;
2649 case 6: putMnemo("SCF"); break;
2650 case 7: putMnemo("CCF"); break;
2652 return;
2654 assert(0);
2655 // 0x40..0x7F: LD r8,r8
2656 case 0x40:
2657 if (opcode == 0x76) { putMnemo("HALT"); return; }
2658 putMnemo("LD");
2659 immutable ubyte rsrc = (opcode&0x07);
2660 immutable ubyte rdst = ((opcode>>3)&0x07);
2661 final switch (rsrc) {
2662 case 0: putArg("B"); break;
2663 case 1: putArg("C"); break;
2664 case 2: putArg("D"); break;
2665 case 3: putArg("E"); break;
2666 case 4: if (gotDD && rdst != 6) put(DD); put("H"); endArg(); break;
2667 case 5: if (gotDD && rdst != 6) put(DD); put("L"); endArg(); break;
2668 case 6:
2669 put("(");
2670 put(DD);
2671 if (gotDD) putDisp(disp);
2672 put(")");
2673 endArg();
2674 break;
2675 case 7: putArg("A"); break;
2677 final switch (rdst) {
2678 case 0: putArg("B"); break;
2679 case 1: putArg("C"); break;
2680 case 2: putArg("D"); break;
2681 case 3: putArg("E"); break;
2682 case 4: if (gotDD && rsrc != 6) put(DD); put("H"); endArg(); break;
2683 case 5: if (gotDD && rsrc != 6) put(DD); put("L"); endArg(); break;
2684 case 6:
2685 put("(");
2686 put(DD);
2687 if (gotDD) putDisp(disp);
2688 put(")");
2689 endArg();
2690 break;
2691 case 7: putArg("A"); break;
2693 return;
2694 // 0x80..0xBF: ALU A,r8
2695 case 0x80:
2696 final switch ((opcode>>3)&0x07) {
2697 case 0: putMnemo("ADD"); putArg("A"); break;
2698 case 1: putMnemo("ADC"); putArg("A"); break;
2699 case 2: putMnemo("SUB"); putArg("A"); break;
2700 case 3: putMnemo("SBC"); putArg("A"); break;
2701 case 4: putMnemo("AND"); break;
2702 case 5: putMnemo("XOR"); break;
2703 case 6: putMnemo("OR"); break;
2704 case 7: putMnemo("CP"); break;
2706 //putArg("A");
2707 putR8(opcode&0x07);
2708 return;
2709 // 0xC0..0xFF
2710 case 0xC0:
2711 final switch (opcode&0x07) {
2712 // RET cc
2713 case 0:
2714 putMnemo("RET");
2715 putCC((opcode>>3)&0x07);
2716 break;
2717 // POP rr/special0
2718 case 1:
2719 if (opcode&0x08) {
2720 // special 0
2721 final switch ((opcode>>4)&0x03) {
2722 // RET
2723 case 0: putMnemo("RET"); break;
2724 // EXX
2725 case 1: putMnemo("EXX"); break;
2726 // JP (HL)
2727 case 2: putMnemo("JP"); put("("); put(DD); put(")"); endArg(); break;
2728 // LD SP,HL
2729 case 3: putMnemo("LD"); putArg("SP"); put(DD); endArg(); break;
2731 } else {
2732 // POP rr
2733 putMnemo("POP");
2734 putR16AF((opcode>>4)&0x03);
2736 break;
2737 // JP cc,nn
2738 case 2:
2739 putMnemo("JP");
2740 putCC((opcode>>3)&0x07);
2741 putUInt(memReadPCW, 4);
2742 endArg();
2743 break;
2744 // special1/special3
2745 case 3:
2746 final switch ((opcode>>3)&0x07) {
2747 // JP nn
2748 case 0: putMnemo("JP"); putUInt(memReadPCW, 4); endArg(); break;
2749 // OUT (n),A
2750 case 2: putMnemo("OUT"); put("("); putUInt(memReadPC, 2); put(")"); endArg(); putArg("A"); break;
2751 // IN A,(n)
2752 case 3: putMnemo("IN"); putArg("A"); put("("); putUInt(memReadPC, 2); put(")"); endArg(); break;
2753 // EX (SP),HL
2754 case 4: putMnemo("EX"); putArg("(SP)"); put(DD); endArg(); break;
2755 // EX DE,HL
2756 case 5: putMnemo("EX"); putArg("DE"); putArg("HL"); break;
2757 // DI
2758 case 6: putMnemo("DI"); break;
2759 // EI
2760 case 7: putMnemo("EI"); break;
2762 break;
2763 // CALL cc,nn
2764 case 4:
2765 putMnemo("CALL");
2766 putCC((opcode>>3)&0x07);
2767 putUInt(memReadPCW, 4);
2768 endArg();
2769 break;
2770 // PUSH rr/special2
2771 case 5:
2772 if (opcode&0x08) {
2773 if (((opcode>>4)&0x03) == 0) {
2774 // CALL
2775 putMnemo("CALL");
2776 putUInt(memReadPCW, 4);
2777 endArg();
2779 } else {
2780 // PUSH rr
2781 putMnemo("PUSH");
2782 putR16AF((opcode>>4)&0x03);
2784 break;
2785 // ALU A,n
2786 case 6:
2787 final switch ((opcode>>3)&0x07) {
2788 case 0: putMnemo("ADD"); putArg("A"); break;
2789 case 1: putMnemo("ADC"); putArg("A"); break;
2790 case 2: putMnemo("SUB"); putArg("A"); break;
2791 case 3: putMnemo("SBC"); putArg("A"); break;
2792 case 4: putMnemo("AND"); break;
2793 case 5: putMnemo("XOR"); break;
2794 case 6: putMnemo("OR"); break;
2795 case 7: putMnemo("CP"); break;
2797 //putArg("A");
2798 putUInt(memReadPC, 2);
2799 endArg();
2800 break;
2801 // RST nnn
2802 case 7:
2803 putMnemo("RST");
2804 putUInt(opcode&0x38, 2);
2805 endArg();
2806 break;
2808 break;
2809 } // end switch
2814 // ////////////////////////////////////////////////////////////////////////// //
2815 // build tables through CTFE
2816 private immutable ubyte[256] tblParity = (){
2817 ubyte[256] t;
2818 foreach (immutable f; 0..256) {
2819 int n, p;
2820 for (n = f, p = 0; n != 0; n >>= 1) p ^= n&0x01;
2821 t[f] = (p ? 0 : ZymCPU.Z80Flag.PV);
2823 return t;
2824 }();
2826 private immutable ubyte[256] tblSZ53 = (){
2827 ubyte[256] t;
2828 foreach (immutable f; 0..256) t[f] = (f&ZymCPU.Z80Flag.S35);
2829 t[0] |= ZymCPU.Z80Flag.Z;
2830 return t;
2831 }();
2833 private immutable ubyte[256] tblSZP53 = (){
2834 ubyte[256] t;
2835 foreach (immutable f; 0..256) {
2836 int n, p;
2837 for (n = f, p = 0; n != 0; n >>= 1) p ^= n&0x01;
2838 t[f] = (f&ZymCPU.Z80Flag.S35)|(p ? 0 : ZymCPU.Z80Flag.PV);
2840 t[0] |= ZymCPU.Z80Flag.Z;
2841 return t;
2842 }();