/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/I8080.c 80    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  i8080 Processor Emulator

  Copyright (c) 1991-1997 Claus T. Giloi
  Copyright (c) 2000-2006 Richard A. Cini


Change Log:
  2000/08/31  RAC -- Initial modifications
  2000/09/10  RAC -- Changes to fix external calls to bus80.dll
  2000/09/11  RAC -- Moved def of Mem[] to i8080.h
  2000/09/18  RAC -- Redefined (byte tmp) to (word tmp) to fix 
						conversion warnings.
  2000/10/16  RAC -- Initial checkin after mods from Theo. Changes
						included primarily (word) overrides.
  2000/11/21  RAC -- Added code for calling table for I/O routines. Will
						require changes to or elimination of bus.c because
						each device "attaches" to the processor I/O space
						through this table.
  2000/11/29  RAC -- Added code to automatically load boot PROM
  2000/12/05  RAC -- Made the conversion to jump-table I/O routines
  2000/12/15  RAC -- Reinstated GetIO/SetIO as interface to altair.c
  2001/01/02  RAC -- Tied lpr routines to device table; went back to
						old method for using GetIO/SetIO
  2001/02/20  RAC -- Made fixes to several op code routines SHLD, LHLD,
						LXI B/D
  2001/03/15  RAC -- Figuring out how to tie processor test code into emu
						(using HEX file)
  2001/04/07  RAC -- Ran test code. Fixed JM;SBI;RET
  2001/04/11  RAC -- It appears that either CMP B/D/E/H/L/M *or* MOV M,r or
						MOV r,M does not work properly. Also need to look at
						CC. UPDATE-partially traced to defective assembler
						coding MVI B,H,L as same opcode.
  2001/05/11  RAC -- Moved some arrays around to put them at the end of DSEG
  2001/05/18  RAC -- Traced problem with INX/DCX to use of *w_regs. C method
						for storing word values in array is incompatible with
						the way a microprocessor stores same (an "endian" issue)
  2001/05/22  RAC -- Added macros to manipulate byte registers (courtesy of
						Sean Connor). Made substitutions in INX, DCX, DAD, 
						LDAX, LXI, STAX, XCHG, SPHL, PCHL, LHLD, PUSH, POP,
						MOV, INR, DCR, XRA, ORA, ANA, CMP, ADD, ADC, SUB, SBB
  2001/05/23  RAC -- Fixed RST (didn't save PC on the stack); replaced *pACC
						with rACC and *pFLAGS with F. Added instruction
						comments. Added processor reset function to move
						"reset" function out of altair.c
  2001/05/24  RAC -- Moved b_regs to "static" area.
  2001/08/06  TMP -- Bug fixes to RST, MVI and LHLD
  2001/08/19  RAC -- RELEASE MARKER -- v2.0
  2001/08/22  RAC -- Made calls to OUT 20Q/21Q fall through SIO00/SIO01.
  2001/08/26  RAC -- Began mods for CoreDump and Register display. Also
						checking into double-clocking anomaly in two-byte
						instructions like ANI.
  2001/08/29  RAC -- Minor changes to IN/OUT to speed-up sense switch stuff
  2001/09/13  RAC -- Moved forward protos of instruction routines here
  2001/09/17  RAC -- Disconnected punch code in favor of the "console stuff"
						method used by true TTYs.
  2001/09/21  RAC -- Moved LoadBootProm call and changed array type to 
						static byte. Memory dump now shows right code.
  2001/11/15  RAC -- Began conversion from regs[] to struct; uchar->byte;
						ushort->word
  2001/11/26  RAC -- Separated tape console from disk console.
  2001/11/28  RAC -- Added conditional for HLT to break into AID.
  2001/12/04  RAC -- Changed clock8080 to take I80Regs as a param; moved
  						nCycle to I80Regs. Still need to tie Get/Set
  						calls to Get/Set_func calls.
  2001/12/07  RAC -- Used new call macros in IN/OUT opcodes as a test.
  2001/12/14  RAC -- RELEASE MARKER -- v2.1
  2001/12/27  RAC -- Sync with Solace 3.0
  2002/01/02  RAC -- Added support for OpCode traps (Trap_ED_ops) and
  						used real "invalid opcode" handler (nul)
  2002/01/31  RAC -- RELEASE MARKER -- v2.2
  2002/05/22  RAC -- Minor comments clean-up; began adding support for
  						installable memory-mapped handlers and IRQs.
  2002/07/07  RAC -- RELEASE MARKER -- v2.3
  2002/08/23  RAC -- RELEASE MARKER -- v2.30.10
  2002/08/27  RAC -- Began prototying for timeslicing
  2002/09/23  RAC -- Added dumping of PageAccessRights to CoreDump routine.
  						Removed hard-coded bootrom code in favor of loadable
  						module.
  2002/10/15  RAC -- Changed return value usage in simstepover, simstep,
  						and simrun to keep data bus buffer var MBR
  						current during stepping.
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2002/11/19  RAC -- Moved two CPU cycle statements in Clock8080 to inside
  						instruction fetch conditional (thanks Rodger
  						Smedley). Made changes to conditional calls (cc,
  						cz, cp, cpe, cnc, cnz, cm, cpo) and conditional
  						returns (rc, rz, rp, rpe, rnc, rnz, rm, rpo).
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2003/06/24  RAC -- Changed opcode mappings for 10h to "nop" from "nul" as
  						4kBASIC seems to call this undocumented opcode. This
  						is to avoid trapping to the debugger. May also need
  						to change 08, 18, 20, 28, 30 and 38 to "nop" from
  						"nul", changed cb to c3 (jmp), d9 to c9 (return)
  						and dd and fd to cd (call). Cannot change 0xed to
  						0xcd (call) as that's the trap for file import/
  						export operations (Tim Mann). Added hidden WZ register
  						pair to i8080 typedef. Fix provided by Rodger Smedley.
  2003/06/25  RAC -- Applied pProposed change to LDAX/STAX as workaround for
  						"Kill the Bit" game which relies on a live address
  						"on	the bus". Variable PC is really an address bus
  						buffer like MBR is a data bus buffer. Instruction
  						operand is moved to PC during fetch and PC is restored
  						during last cycle of current instruction. There are
  						other opcodes that this should be done to but changes
  						would likely slow emulator too much. Fix provided by
  						Rodger Smedley.
  2003/10/16  RAC -- Added new ACR device to device table and removed
						mapping to sio00/sio01
  2004/01/21  RAC -- Diff'ed changes from FJS (Fred J. Scipione)
  2004/03/09  RAC -- Diff'ed more changes from FJS (Fred J. Scipione)
  2004/05/15  RAC -- Changes to Cycles for ED_trap
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135  
  2004/09/17  RAC -- Added hooks to Dazzler I/O handlers.
  2004/10/09  FJS -- Z80 related changes
  2006/03/24  RAC -- Reserved port range for front panel serial ports
  2006/05/04  RAC -- Removed automatic popup of debugger on illegal_op in
  						nul().
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/06/09  RAC -- Added Compu/Time handler and Dazzler joystick to IO matrix.
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/
#include <windows.h>	// required for all Windows applications
#include <commdlg.h>	// Windows common dialogs
#include <stdlib.h>		// C standard lib
#include <string.h>		// C-lib - strings
#include <stdio.h>		// C-lib - I/O 
#include <ctype.h>		// C-lib - types
#include "altair32.h"	// includes "resource.h" and exports from other major modules
#include "fpdll.h"		// for serial port stuff
#include "windbg.h"		// debugger
#include "emt_traps.h"  // invalid opcode trap for import/export

#define	AM_I8080 1		/* avoid duplications in i8080.h */
#include	"i8080.h"	// module header

/**  Module Globals - Public  *********************************/
I8080	I80Regs;						// CPU processor state
byte	Mem[MEMSIZE];					// Memory of MEMSIZE bytes
byte	PageAccessRights[MEMSIZE];		// R/W bits: 0=wrt 1=R/O
word	IOPANEL = 0;
word	MBR = 0;						// data (memory) bus buffer register
word	ABR = 0;						// Address Buss buffer register <FJS>
word	Aswitches = 0;					// current state of address toggles
// ushort Dswitches;					// future
byte	CLK_OP = 0317;					// RST1 code for interrupt <FJS>
#define FLAGS	I80Regs.nAF.B.l			// compatibility/readability macro
#define ACCUM	I80Regs.nAF.B.h			// compatibility/readability macro
// #define DISP	I80Regs.Disp			// compatibility/readability macro
#define ADVANCE_PC ABR = ++(I80Regs.nPC.W) // compatibility/readability macro


/**  Forward prototypes  **************************************/
void report_curstate(void);
static int  sensesw (int, int);	// sense switch handler
static int  nulldev (int, int);	// NULL I/O device

#if 0
#define OP_ARG byte
#define OP_ARG_U byte u
#define U_ARG u
#define PC_ABR ABR
#else
#define OP_ARG void
#define OP_ARG_U void
#define U_ARG /* nil */
#define PC_ABR I80Regs.nPC.W = ABR
#endif
static void	Trap_ED_ops ( OP_ARG );	// opcode trap for emt import/export

static void	nul  ( OP_ARG );

static void	add  ( OP_ARG );
static void	adc  ( OP_ARG );
static void	sub  ( OP_ARG );
static void	sbb  ( OP_ARG );
static void	ana  ( OP_ARG );
static void	xra  ( OP_ARG );
static void	ora  ( OP_ARG );
static void	cmp  ( OP_ARG );
static void	adi  ( OP_ARG );
static void	aci  ( OP_ARG );
static void	sui  ( OP_ARG );
static void	sbi  ( OP_ARG );
static void	ani  ( OP_ARG );
static void	xri  ( OP_ARG );
static void	ori  ( OP_ARG );
static void	cpi  ( OP_ARG );
static void	rlc  ( OP_ARG );
static void	rar  ( OP_ARG );
static void	ral  ( OP_ARG );
static void	cma  ( OP_ARG );
static void	inr  ( OP_ARG );
static void	dcr  ( OP_ARG );
static void	rrc  ( OP_ARG );
static void	daa  ( OP_ARG );

static void	mov  ( OP_ARG );
static void	mvm  ( OP_ARG ); // FJS
static void	mvr  ( OP_ARG ); // FJS

static void	lxi  ( OP_ARG );
static void	stc  ( OP_ARG );
static void	cmc  ( OP_ARG );
static void	inx  ( OP_ARG );
static void	dcx  ( OP_ARG );
static void	dad  ( OP_ARG );
static void	sphl ( OP_ARG );
static void	pchl ( OP_ARG );
static void	rst  ( OP_ARG );
static void	jmp  ( OP_ARG );
static void	jc   ( OP_ARG );
static void	jz   ( OP_ARG );
static void	jp   ( OP_ARG );
static void	jpe  ( OP_ARG );
static void	jnc  ( OP_ARG );
static void	jnz  ( OP_ARG );
static void	jm   ( OP_ARG );
static void	jpo  ( OP_ARG );
static void	cc   ( OP_ARG );
static void	cz   ( OP_ARG );
static void	cp   ( OP_ARG );
static void	cpe  ( OP_ARG );
static void	cnc  ( OP_ARG );
static void	cnz  ( OP_ARG );
static void	cm   ( OP_ARG );
static void	cpo  ( OP_ARG );
static void	rc   ( OP_ARG );
static void	rz   ( OP_ARG );
static void	rp   ( OP_ARG );
static void	rpe  ( OP_ARG );
static void	rnc  ( OP_ARG );
static void	rnz  ( OP_ARG );
static void	rm   ( OP_ARG );
static void	rpo  ( OP_ARG );
static void	in   ( OP_ARG );
static void	out  ( OP_ARG );
static void	ldax ( OP_ARG );
static void	stax ( OP_ARG );
static void	lda  ( OP_ARG );
static void	sta  ( OP_ARG );

static void	mvi  ( OP_ARG );
static void	mim  ( OP_ARG ); // FJS

static void	nop  ( OP_ARG );
static void	hlt  ( OP_ARG );
static void	ei   ( OP_ARG );
static void	di   ( OP_ARG );
static void	push ( OP_ARG );
static void	pop  ( OP_ARG );
static void	shld ( OP_ARG );
static void	lhld ( OP_ARG );
static void	ret  ( OP_ARG );
static void	call ( OP_ARG );
static void	xthl ( OP_ARG );
static void	xchg ( OP_ARG );

static void	ZIX  ( OP_ARG ); // FJS Z80
static void	ZIY  ( OP_ARG ); // FJS Z80
static void	ZED  ( OP_ARG ); // FJS Z80
static void	ZCB  ( OP_ARG ); // FJS Z80

static void	EXAF ( OP_ARG ); // FJS Z80
static void	EXX  ( OP_ARG ); // FJS Z80
static void	DJNZ ( OP_ARG ); // FJS Z80
static void	jr   ( OP_ARG ); // FJS Z80
static void	JRC  ( OP_ARG ); // FJS Z80
static void	JRNC ( OP_ARG ); // FJS Z80
static void	JRZ  ( OP_ARG ); // FJS Z80
static void	JRNZ ( OP_ARG ); // FJS Z80

static void	ZIC  ( OP_ARG ); // FJS Z80
static void	IPC  ( OP_ARG ); // FJS Z80
static void	OCZ  ( OP_ARG ); // FJS Z80
static void	OPC  ( OP_ARG ); // FJS Z80
static void	SXC  ( OP_ARG ); // FJS Z80
static void	AXC  ( OP_ARG ); // FJS Z80
static void	LDX  ( OP_ARG ); // FJS Z80
static void	LXD  ( OP_ARG ); // FJS Z80
static void	NEG  ( OP_ARG ); // FJS Z80
static void	RTN  ( OP_ARG ); // FJS Z80
static void	RTI  ( OP_ARG ); // FJS Z80
static void	IMM  ( OP_ARG ); // FJS Z80
static void	LIA  ( OP_ARG ); // FJS Z80
static void	LRA  ( OP_ARG ); // FJS Z80
static void	LAI  ( OP_ARG ); // FJS Z80
static void	LAR  ( OP_ARG ); // FJS Z80
static void	RRD  ( OP_ARG ); // FJS Z80
static void	RLD  ( OP_ARG ); // FJS Z80

static void	LDI  ( OP_ARG ); // FJS Z80
static void	LDD  ( OP_ARG ); // FJS Z80
static void	LIR  ( OP_ARG ); // FJS Z80
static void	LDR  ( OP_ARG ); // FJS Z80
static void	CPI  ( OP_ARG ); // FJS Z80
static void	CPD  ( OP_ARG ); // FJS Z80
static void	CIR  ( OP_ARG ); // FJS Z80
static void	CDR  ( OP_ARG ); // FJS Z80
static void	INI  ( OP_ARG ); // FJS Z80
static void	IND  ( OP_ARG ); // FJS Z80
static void	IIR  ( OP_ARG ); // FJS Z80
static void	IDR  ( OP_ARG ); // FJS Z80
static void	OTI  ( OP_ARG ); // FJS Z80
static void	OTD  ( OP_ARG ); // FJS Z80
static void	OIR  ( OP_ARG ); // FJS Z80
static void	ODR  ( OP_ARG ); // FJS Z80
 
static void	ROT  ( OP_ARG ); // FJS Z80
static void	RTM  ( OP_ARG ); // FJS Z80
static void	BIT  ( OP_ARG ); // FJS Z80
static void	BTM  ( OP_ARG ); // FJS Z80
static void	RES  ( OP_ARG ); // FJS Z80
static void	RSM  ( OP_ARG ); // FJS Z80
static void	SET  ( OP_ARG ); // FJS Z80
static void	STM  ( OP_ARG ); // FJS Z80


/**  Module Globals - Private *********************************/
#define PFX_ED	0xED 		// invalid instruction (8080)
static	byte	CurInstruction;
static  byte	dummy;		// must be declared before b_regs
static	byte	Vtmp;		// fudge DS==SS compatibility by using static  
static	word	Atmp;		// temp variables (artifact of Win16 origin)
static	int		is_Z80 = 0; // FJS cpu state for 8080 vs Z80 & fast vs cycle
static	int		pfx_state = 0; // FJS prefix op-code state for Z80

// TBD separate PC from ABR -
static	word	save_PC;	// save PC while used for ABR
#if 0
#define PC_ABR ABR
#define HOLD_PC /* nil */
#define SAVE_PC /* nil */
#define RESTORE_PC /* nil */
#else
#define PC_ABR I80Regs.nPC.W = ABR
#define HOLD_PC save_PC =
#define SAVE_PC save_PC = I80Regs.nPC.W;
#define RESTORE_PC I80Regs.nPC.W = save_PC;
#endif

// pointer array for sss, ddd addressing
static byte * b_regs[24] = {
	  &I80Regs.nBC.B.h,	// B
	  &I80Regs.nBC.B.l,	// C
	  &I80Regs.nDE.B.h,	// D
	  &I80Regs.nDE.B.l,	// E
	  &I80Regs.nHL.B.h,	// H
	  &I80Regs.nHL.B.l,	// L
	  &dummy,			// trap/filler
	  &I80Regs.nAF.B.h,	// ACC

	  &I80Regs.nBC.B.h,	// B
	  &I80Regs.nBC.B.l,	// C
	  &I80Regs.nDE.B.h,	// D
	  &I80Regs.nDE.B.l,	// E
	  &I80Regs.nIX.B.h,	// IX.B.h
	  &I80Regs.nIX.B.l,	// IX.B.l
	  &dummy,			// trap/filler
	  &I80Regs.nAF.B.h,	// ACC

	  &I80Regs.nBC.B.h,	// B
	  &I80Regs.nBC.B.l,	// C
	  &I80Regs.nDE.B.h,	// D
	  &I80Regs.nDE.B.l,	// E
	  &I80Regs.nIY.B.h,	// IY.B.h
	  &I80Regs.nIY.B.l,	// IY.B.l
	  &dummy,			// trap/filler
	  &I80Regs.nAF.B.h	// ACC
	};

#if 0
// pointer array for rp addressing
static word	* w_regs[4] =
	{ &I80Regs.nBC.W,	// BC
	  &I80Regs.nDE.W,	// DE
	  &I80Regs.nHL.W,	// HL
	  &I80Regs.nSP.W	// SP
	};
#endif
	 
// 4-bit bit-count table
static byte bit_table[16] = {
  // 0000 0001 0010 0011 0100 0101 0110 0111
		0,   1,   1,   2,   1,   2,   2,   3,
  // 1000 1001 1010 1011 1100 1101 1110 1111
		1,   2,   2,   3,   2,   3,   3,   4 };

static byte Cycles8080[256] =
{
   4,10, 7, 5, 5, 5, 7, 4,
   0,10, 7, 5, 5, 5, 7, 4,	// 0
   0,10, 7, 5, 5, 5, 7, 4,
   0,10, 7, 5, 5, 5, 7, 4,	// 1
   0,10,16, 5, 5, 5, 7, 4,
   0,10,16, 5, 5, 5, 7, 4,	// 2
   0,10,13, 5,10,10,10, 4,
   0,10,13, 5, 5, 5, 7, 4,	// 3

   5, 5, 5, 5, 5, 5, 7, 5,
   5, 5, 5, 5, 5, 5, 7, 5,	// 4
   5, 5, 5, 5, 5, 5, 7, 5,
   5, 5, 5, 5, 5, 5, 7, 5,	// 5
   5, 5, 5, 5, 5, 5, 7, 5,
   5, 5, 5, 5, 5, 5, 7, 5,	// 6
   7, 7, 7, 7, 7, 7, 7, 7,
   5, 5, 5, 5, 5, 5, 7, 5,	// 7

   4, 4, 4, 4, 4, 4, 7, 4,
   4, 4, 4, 4, 4, 4, 7, 4,	// 8
   4, 4, 4, 4, 4, 4, 7, 4,
   4, 4, 4, 4, 4, 4, 7, 4,	// 9
   4, 4, 4, 4, 4, 4, 7, 4,
   4, 4, 4, 4, 4, 4, 7, 4,	// A
   4, 4, 4, 4, 4, 4, 7, 4,
   4, 4, 4, 4, 4, 4, 7, 4,	// B

   5,10,10,10,11,11, 7,11,
   5,10,10, 0,11,17, 7,11,	// C
   5,10,10,10,11,11, 7,11,
   5, 0,10,10,11, 0, 7,11,	// D
   5,10,10,18,11,11, 7,11,
   5, 5,10, 4,11, 5, 7,11,	// E  '0xED' changed from 0 to 5
   5,10,10, 4,11,11, 7,11,
   5, 5,10, 4,11, 0, 7,11	// F
};

// CPU instruction call table(s)
typedef void (*op_funct)( OP_ARG ); // FJS
#define TED Trap_ED_ops
// static void (*Itable[])( OP_ARG ) = { // FJS
static op_funct Itable[] = {		// 8080 op-codes -
	nop, lxi, stax,inx, inr, dcr, mvi, rlc,		// 000-007 (0x00-0x07)
	nul, dad, ldax,dcx, inr, dcr, mvi, rrc,		// 010-017 (0x08-0x0f)
	nop, lxi, stax,inx, inr, dcr, mvi, ral,		// 020-027 (0x10-0x17)
	nul, dad, ldax,dcx, inr, dcr, mvi, rar,		// 030-037 (0x18-0x1f)
	nul, lxi, shld,inx, inr, dcr, mvi, daa,		// 040-047 (0x20-0x27)
	nul, dad, lhld,dcx, inr, dcr, mvi, cma,		// 050-057 (0x28-0x2f)
	nul, lxi, sta, inx, inr, dcr, mim, stc,		// 060-067 (0x30-0x37)
	nul, dad, lda, dcx, inr, dcr, mvi, cmc,		// 070-077 (0x38-0x3f)

	mov, mov, mov, mov, mov, mov, mvr, mov,		// 100-107 (0x40-0x47)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 110-117 (0x48-0x4f)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 120-127 (0x50-0x57)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 130-137 (0x58-0x5f)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 140-147 (0x60-0x67)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 150-157 (0x68-0x6f)
	mvm, mvm, mvm, mvm, mvm, mvm, hlt, mvm,		// 160-167 (0x70-0x77)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 170-177 (0x78-0x7f)

	add, add, add, add, add, add, add, add,		// 200-207 (0x80-0x87)
	adc, adc, adc, adc, adc, adc, adc, adc,		// 210-217 (0x88-0x8f)
	sub, sub, sub, sub, sub, sub, sub, sub,		// 220-227 (0x90-0x97)
	sbb, sbb, sbb, sbb, sbb, sbb, sbb, sbb,		// 230-237 (0x98-0x9f)
	ana, ana, ana, ana, ana, ana, ana, ana,		// 240-247 (0xa0-0xa7)
	xra, xra, xra, xra, xra, xra, xra, xra,		// 250-257 (0xa8-0xaf)
	ora, ora, ora, ora, ora, ora, ora, ora, 	// 260-267 (0xb0-0xb7)
	cmp, cmp, cmp, cmp, cmp, cmp, cmp, cmp,		// 270-277 (0xb8-0xbf)

	rnz, pop, jnz, jmp, cnz, push,adi, rst,		// 300-307 (0xc0-0xc7)
	rz,  ret, jz,  nul, cz,  call,aci, rst,		// 310-317 (0xc8-0xcf)
	rnc, pop, jnc, out, cnc, push,sui, rst,		// 320-327 (0xd0-0xd7)
	rc,  nul, jc,  in,  cc,  nul, sbi, rst,		// 330-337 (0xd8-0xdf)
	rpo, pop, jpo, xthl,cpo, push,ani, rst,		// 340-347 (0xe0-0xe7)
	rpe, pchl,jpe, xchg,cpe, TED, xri, rst,		// 350-357 (0xe8-0xef)
	rp,  pop, jp,  di,  cp,  push,ori, rst,		// 360-367 (0xf0-0xf7)
	rm,  sphl,jm,  ei,  cm,  nul, cpi, rst		// 370-377 (0xf8-0xff)
};

static op_funct *Utable[7] = { // FJS for future Z80 support & fast vs cycle
	Itable, Itable, Itable, Itable, Itable, Itable, Itable // 8080
};

#if 1
static op_funct Z80table[] = { // Z80 - baseline ops & prefixes...
	nop, lxi, stax,inx, inr, dcr, mvi, rlc,		// 000-007 (0x00-0x07)
	EXAF,dad, ldax,dcx, inr, dcr, mvi, rrc,		// 010-017 (0x08-0x0f)
	DJNZ,lxi, stax,inx, inr, dcr, mvi, ral,		// 020-027 (0x10-0x17)
	jr,  dad, ldax,dcx, inr, dcr, mvi, rar,		// 030-037 (0x18-0x1f)
	JRNZ,lxi, shld,inx, inr, dcr, mvi, daa,		// 040-047 (0x20-0x27)
	JRZ, dad, lhld,dcx, inr, dcr, mvi, cma,		// 050-057 (0x28-0x2f)
	JRNC,lxi, sta, inx, inr, dcr, mim, stc,		// 060-067 (0x30-0x37)
	JRC, dad, lda, dcx, inr, dcr, mvi, cmc,		// 070-077 (0x38-0x3f)

	mov, mov, mov, mov, mov, mov, mvr, mov,		// 100-107 (0x40-0x47)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 110-117 (0x48-0x4f)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 120-127 (0x50-0x57)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 130-137 (0x58-0x5f)
	mov, mov, mov, mov, TED, mov, mvr, mov,		// 140-147 (0x60-0x67)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 150-157 (0x68-0x6f)
	mvm, mvm, mvm, mvm, mvm, mvm, hlt, mvm,		// 160-167 (0x70-0x77)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 170-177 (0x78-0x7f)

	add, add, add, add, add, add, add, add,		// 200-207 (0x80-0x87)
	adc, adc, adc, adc, adc, adc, adc, adc,		// 210-217 (0x88-0x8f)
	sub, sub, sub, sub, sub, sub, sub, sub,		// 220-227 (0x90-0x97)
	sbb, sbb, sbb, sbb, sbb, sbb, sbb, sbb,		// 230-237 (0x98-0x9f)
	ana, ana, ana, ana, ana, ana, ana, ana,		// 240-247 (0xa0-0xa7)
	xra, xra, xra, xra, xra, xra, xra, xra,		// 250-257 (0xa8-0xaf)
	ora, ora, ora, ora, ora, ora, ora, ora, 	// 260-267 (0xb0-0xb7)
	cmp, cmp, cmp, cmp, cmp, cmp, cmp, cmp,		// 270-277 (0xb8-0xbf)

	rnz, pop, jnz, jmp, cnz, push,adi, rst,		// 300-307 (0xc0-0xc7)
	rz,  ret, jz,  ZCB, cz,  call,aci, rst,		// 310-317 (0xc8-0xcf)
	rnc, pop, jnc, out, cnc, push,sui, rst,		// 320-327 (0xd0-0xd7)
	rc,  EXX, jc,  in,  cc,  ZIX, sbi, rst,		// 330-337 (0xd8-0xdf)
	rpo, pop, jpo, xthl,cpo, push,ani, rst,		// 340-347 (0xe0-0xe7)
	rpe, pchl,jpe, xchg,cpe, ZED, xri, rst,		// 350-357 (0xe8-0xef)
	rp,  pop, jp,  di,  cp,  push,ori, rst,		// 360-367 (0xf0-0xf7)
	rm,  sphl,jm,  ei,  cm,  ZIY, cpi, rst		// 370-377 (0xf8-0xff)
};

static op_funct ZIXtable[] = { // Z80 w/ IX prefix (only)
	nop, lxi, stax,inx, inr, dcr, mvi, rlc,		// 000-007 (0x00-0x07)
	EXAF,dad, ldax,dcx, inr, dcr, mvi, rrc,		// 010-017 (0x08-0x0f)
	DJNZ,lxi, stax,inx, inr, dcr, mvi, ral,		// 020-027 (0x10-0x17)
	jr,  dad, ldax,dcx, inr, dcr, mvi, rar,		// 030-037 (0x18-0x1f)
	JRNZ,lxi, shld,inx, inr, dcr, mvi, daa,		// 040-047 (0x20-0x27)
	JRZ, dad, lhld,dcx, inr, dcr, mvi, cma,		// 050-057 (0x28-0x2f)
	JRNC,lxi, sta, inx, inr, dcr, mim, stc,		// 060-067 (0x30-0x37)
	JRC, dad, lda, dcx, inr, dcr, mvi, cmc,		// 070-077 (0x38-0x3f)

	mov, mov, mov, mov, mov, mov, mvr, mov,		// 100-107 (0x40-0x47)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 110-117 (0x48-0x4f)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 120-127 (0x50-0x57)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 130-137 (0x58-0x5f)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 140-147 (0x60-0x67)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 150-157 (0x68-0x6f)
	mvm, mvm, mvm, mvm, mvm, mvm, hlt, mvm,		// 160-167 (0x70-0x77)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 170-177 (0x78-0x7f)

	add, add, add, add, add, add, add, add,		// 200-207 (0x80-0x87)
	adc, adc, adc, adc, adc, adc, adc, adc,		// 210-217 (0x88-0x8f)
	sub, sub, sub, sub, sub, sub, sub, sub,		// 220-227 (0x90-0x97)
	sbb, sbb, sbb, sbb, sbb, sbb, sbb, sbb,		// 230-237 (0x98-0x9f)
	ana, ana, ana, ana, ana, ana, ana, ana,		// 240-247 (0xa0-0xa7)
	xra, xra, xra, xra, xra, xra, xra, xra,		// 250-257 (0xa8-0xaf)
	ora, ora, ora, ora, ora, ora, ora, ora, 	// 260-267 (0xb0-0xb7)
	cmp, cmp, cmp, cmp, cmp, cmp, cmp, cmp,		// 270-277 (0xb8-0xbf)

	rnz, pop, jnz, jmp, cnz, push,adi, rst,		// 300-307 (0xc0-0xc7)
	rz,  ret, jz,  ZCB, cz,  call,aci, rst,		// 310-317 (0xc8-0xcf)
	rnc, pop, jnc, out, cnc, push,sui, rst,		// 320-327 (0xd0-0xd7)
	rc,  EXX, jc,  in,  cc,  ZIX, sbi, rst,		// 330-337 (0xd8-0xdf)
	rpo, pop, jpo, xthl,cpo, push,ani, rst,		// 340-347 (0xe0-0xe7)
	rpe, pchl,jpe, xchg,cpe, ZED, xri, rst,		// 350-357 (0xe8-0xef)
	rp,  pop, jp,  di,  cp,  push,ori, rst,		// 360-367 (0xf0-0xf7)
	rm,  sphl,jm,  ei,  cm,  ZIY, cpi, rst		// 370-377 (0xf8-0xff)
};

static op_funct ZIYtable[] = { // Z80 w/ IY prefix (only)
	nop, lxi, stax,inx, inr, dcr, mvi, rlc,		// 000-007 (0x00-0x07)
	EXAF,dad, ldax,dcx, inr, dcr, mvi, rrc,		// 010-017 (0x08-0x0f)
	DJNZ,lxi, stax,inx, inr, dcr, mvi, ral,		// 020-027 (0x10-0x17)
	jr,  dad, ldax,dcx, inr, dcr, mvi, rar,		// 030-037 (0x18-0x1f)
	JRNZ,lxi, shld,inx, inr, dcr, mvi, daa,		// 040-047 (0x20-0x27)
	JRZ, dad, lhld,dcx, inr, dcr, mvi, cma,		// 050-057 (0x28-0x2f)
	JRNC,lxi, sta, inx, inr, dcr, mim, stc,		// 060-067 (0x30-0x37)
	JRC, dad, lda, dcx, inr, dcr, mvi, cmc,		// 070-077 (0x38-0x3f)

	mov, mov, mov, mov, mov, mov, mvr, mov,		// 100-107 (0x40-0x47)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 110-117 (0x48-0x4f)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 120-127 (0x50-0x57)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 130-137 (0x58-0x5f)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 140-147 (0x60-0x67)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 150-157 (0x68-0x6f)
	mvm, mvm, mvm, mvm, mvm, mvm, hlt, mvm,		// 160-167 (0x70-0x77)
	mov, mov, mov, mov, mov, mov, mvr, mov,		// 170-177 (0x78-0x7f)

	add, add, add, add, add, add, add, add,		// 200-207 (0x80-0x87)
	adc, adc, adc, adc, adc, adc, adc, adc,		// 210-217 (0x88-0x8f)
	sub, sub, sub, sub, sub, sub, sub, sub,		// 220-227 (0x90-0x97)
	sbb, sbb, sbb, sbb, sbb, sbb, sbb, sbb,		// 230-237 (0x98-0x9f)
	ana, ana, ana, ana, ana, ana, ana, ana,		// 240-247 (0xa0-0xa7)
	xra, xra, xra, xra, xra, xra, xra, xra,		// 250-257 (0xa8-0xaf)
	ora, ora, ora, ora, ora, ora, ora, ora, 	// 260-267 (0xb0-0xb7)
	cmp, cmp, cmp, cmp, cmp, cmp, cmp, cmp,		// 270-277 (0xb8-0xbf)

	rnz, pop, jnz, jmp, cnz, push,adi, rst,		// 300-307 (0xc0-0xc7)
	rz,  ret, jz,  ZCB, cz,  call,aci, rst,		// 310-317 (0xc8-0xcf)
	rnc, pop, jnc, out, cnc, push,sui, rst,		// 320-327 (0xd0-0xd7)
	rc,  EXX, jc,  in,  cc,  ZIX, sbi, rst,		// 330-337 (0xd8-0xdf)
	rpo, pop, jpo, xthl,cpo, push,ani, rst,		// 340-347 (0xe0-0xe7)
	rpe, pchl,jpe, xchg,cpe, ZED, xri, rst,		// 350-357 (0xe8-0xef)
	rp,  pop, jp,  di,  cp,  push,ori, rst,		// 360-367 (0xf0-0xf7)
	rm,  sphl,jm,  ei,  cm,  ZIY, cpi, rst		// 370-377 (0xf8-0xff)
};

static op_funct ZEDtable[] = { // Z80 ED prefixed operations...
	nul, nul, nul, nul, nul, nul, nul, nul,		// 000-007 (0x00-0x07)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 010-017 (0x08-0x0f)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 020-027 (0x10-0x17)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 030-037 (0x18-0x1f)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 040-047 (0x20-0x27)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 050-057 (0x28-0x2f)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 060-067 (0x30-0x37)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 070-077 (0x38-0x3f)

	IPC, OPC, SXC, LDX, NEG, RTN, IMM, LIA,		// 100-107 (0x40-0x47)
	IPC, OPC, AXC, LXD, NEG, RTI, IMM, LRA,		// 110-117 (0x48-0x4f)
	IPC, OPC, SXC, LDX, NEG, RTN, IMM, LAI,		// 120-127 (0x50-0x57)
	IPC, OPC, AXC, LXD, NEG, RTN, IMM, LAI,		// 130-137 (0x58-0x5f)
	IPC, OPC, SXC, LDX, NEG, RTN, IMM, RRD,		// 140-147 (0x60-0x67)
	IPC, OPC, AXC, LXD, NEG, RTN, IMM, RLD,		// 150-157 (0x68-0x6f)
	ZIC, OCZ, SXC, LDX, NEG, RTN, IMM, nop,		// 160-167 (0x70-0x77)
	IPC, OPC, AXC, LXD, NEG, RTN, IMM, nop,		// 170-177 (0x78-0x7f)

	nul, nul, nul, nul, nul, nul, nul, nul,		// 200-207 (0x80-0x87)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 210-217 (0x88-0x8f)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 220-227 (0x90-0x97)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 230-237 (0x98-0x9f)
	LDI, CPI, INI, OTI, nul, nul, nul, nul,		// 240-247 (0xa0-0xa7)
	LDD, CPD, IND, OTD, nul, nul, nul, nul,		// 250-257 (0xa8-0xaf)
	LIR, CIR, IIR, OIR, nul, nul, nul, nul, 	// 260-267 (0xb0-0xb7)
	LDR, CDR, IDR, ODR, nul, nul, nul, nul,		// 270-277 (0xb8-0xbf)

	nul, nul, nul, nul, nul, nul, nul, nul,		// 300-307 (0xc0-0xc7)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 310-317 (0xc8-0xcf)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 320-327 (0xd0-0xd7)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 330-337 (0xd8-0xdf)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 340-347 (0xe0-0xe7)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 350-357 (0xe8-0xef)
	nul, nul, nul, nul, nul, nul, nul, nul,		// 360-367 (0xf0-0xf7)
	nul, nul, nul, nul, nul, nul, nul, nul		// 370-377 (0xf8-0xff)
};

static op_funct ZCBtable[] = { // Z80 CB prefixed operations (no IX or IY)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 000-007 (0x00-0x07)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 010-017 (0x08-0x0f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 020-027 (0x10-0x17)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 030-037 (0x18-0x1f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 040-047 (0x20-0x27)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 050-057 (0x28-0x2f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 060-067 (0x30-0x37)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 070-077 (0x38-0x3f)

	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 100-107 (0x40-0x47)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 110-117 (0x48-0x4f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 120-127 (0x50-0x57)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 130-137 (0x58-0x5f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 140-147 (0x60-0x67)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 150-157 (0x68-0x6f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 160-167 (0x70-0x77)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 170-177 (0x78-0x7f)

	RES, RES, RES, RES, RES, RES, RSM, RES,		// 200-207 (0x80-0x87)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 210-217 (0x88-0x8f)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 220-227 (0x90-0x97)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 230-237 (0x98-0x9f)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 240-247 (0xa0-0xa7)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 250-257 (0xa8-0xaf)
	RES, RES, RES, RES, RES, RES, RSM, RES, 	// 260-267 (0xb0-0xb7)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 270-277 (0xb8-0xbf)

	SET, SET, SET, SET, SET, SET, STM, SET,		// 300-307 (0xc0-0xc7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 310-317 (0xc8-0xcf)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 320-327 (0xd0-0xd7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 330-337 (0xd8-0xdf)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 340-347 (0xe0-0xe7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 350-357 (0xe8-0xef)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 360-367 (0xf0-0xf7)
	SET, SET, SET, SET, SET, SET, STM, SET		// 370-377 (0xf8-0xff)
};

static op_funct ZXCtable[] = { // Z80 IX+CB prefixed...
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 000-007 (0x00-0x07)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 010-017 (0x08-0x0f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 020-027 (0x10-0x17)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 030-037 (0x18-0x1f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 040-047 (0x20-0x27)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 050-057 (0x28-0x2f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 060-067 (0x30-0x37)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 070-077 (0x38-0x3f)

	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 100-107 (0x40-0x47)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 110-117 (0x48-0x4f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 120-127 (0x50-0x57)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 130-137 (0x58-0x5f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 140-147 (0x60-0x67)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 150-157 (0x68-0x6f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 160-167 (0x70-0x77)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 170-177 (0x78-0x7f)

	RES, RES, RES, RES, RES, RES, RSM, RES,		// 200-207 (0x80-0x87)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 210-217 (0x88-0x8f)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 220-227 (0x90-0x97)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 230-237 (0x98-0x9f)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 240-247 (0xa0-0xa7)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 250-257 (0xa8-0xaf)
	RES, RES, RES, RES, RES, RES, RSM, RES, 	// 260-267 (0xb0-0xb7)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 270-277 (0xb8-0xbf)

	SET, SET, SET, SET, SET, SET, STM, SET,		// 300-307 (0xc0-0xc7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 310-317 (0xc8-0xcf)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 320-327 (0xd0-0xd7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 330-337 (0xd8-0xdf)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 340-347 (0xe0-0xe7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 350-357 (0xe8-0xef)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 360-367 (0xf0-0xf7)
	SET, SET, SET, SET, SET, SET, STM, SET		// 370-377 (0xf8-0xff)
};

static op_funct ZYCtable[] = { // Z80 IY+CB prefixed...
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 000-007 (0x00-0x07)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 010-017 (0x08-0x0f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 020-027 (0x10-0x17)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 030-037 (0x18-0x1f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 040-047 (0x20-0x27)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 050-057 (0x28-0x2f)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 060-067 (0x30-0x37)
	ROT, ROT, ROT, ROT, ROT, ROT, RTM, ROT,		// 070-077 (0x38-0x3f)

	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 100-107 (0x40-0x47)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 110-117 (0x48-0x4f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 120-127 (0x50-0x57)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 130-137 (0x58-0x5f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 140-147 (0x60-0x67)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 150-157 (0x68-0x6f)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 160-167 (0x70-0x77)
	BIT, BIT, BIT, BIT, BIT, BIT, BTM, BIT,		// 170-177 (0x78-0x7f)

	RES, RES, RES, RES, RES, RES, RSM, RES,		// 200-207 (0x80-0x87)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 210-217 (0x88-0x8f)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 220-227 (0x90-0x97)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 230-237 (0x98-0x9f)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 240-247 (0xa0-0xa7)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 250-257 (0xa8-0xaf)
	RES, RES, RES, RES, RES, RES, RSM, RES, 	// 260-267 (0xb0-0xb7)
	RES, RES, RES, RES, RES, RES, RSM, RES,		// 270-277 (0xb8-0xbf)

	SET, SET, SET, SET, SET, SET, STM, SET,		// 300-307 (0xc0-0xc7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 310-317 (0xc8-0xcf)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 320-327 (0xd0-0xd7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 330-337 (0xd8-0xdf)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 340-347 (0xe0-0xe7)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 350-357 (0xe8-0xef)
	SET, SET, SET, SET, SET, SET, STM, SET,		// 360-367 (0xf0-0xf7)
	SET, SET, SET, SET, SET, SET, STM, SET		// 370-377 (0xf8-0xff)
};
#endif


/*-------------------------------------------------------------
	This is the I/O configuration table.  There are 256 possible
	port addresses.  If a "device" is connected to a port, the
	address of the related routine is put in the table.
	'nulldev' means that the port address is unused/available.
	All routines should return int and be called with (int, int).
	Use type casting if shorter variables are required.  0=read;
	1=write.  Routines listed using their respective hex addresses.
-------------------------------------------------------------*/
static int (*dev_table[])(int, int) = {
	nulldev, nulldev, lpc02h,  lpc03h,   // 000 (0x00-0x03) lpr(lpr.c)
	nulldev, nulldev, acr06h,  acr07h,   // 004 (0x04-0x07) sio0(cass.c)
	dsk08h,  dsk09h,  dsk0ah,  nulldev,  // 010 (0x08-0x0b) 88disk.c
	nulldev, nulldev, dazz0eh, dazz0fh,  // 014 (0x0c-0x0f) dazzler.
	sio10h,  sio11h,  sio12h,  sio13h,   // 020 (0x10-0x13) sio1(console.c)/sio2(taperdr.c)
	fp_s14h, fp_s15h, fp_s16h, fp_s17h,  // 024 (0x14-0x17) front panel serial ports
	joybtns, joy1x,   joy1y,   joy2x,    // 030 (0x18-0x1b)	dazzler
	joy2y,   joy1z,   joy2z,   nulldev,  // 034 (0x1c-0x1f)	dazzler
	nulldev, nulldev, nulldev, nulldev,  // 040 (0x20-0x23) 
	nulldev, nulldev, nulldev, nulldev,  // 044 (0x24-0x27)
	nulldev, nulldev, nulldev, nulldev,  // 050 (0x28-0x2b)
	nulldev, nulldev, nulldev, nulldev,  // 054 (0x2c-0x2f)
	nulldev, nulldev, nulldev, nulldev,  // 060 (0x30-0x33)
	nulldev, nulldev, nulldev, nulldev,  // 064 (0x34-0x37)
	nulldev, nulldev, nulldev, nulldev,  // 070 (0x38-0x3b)
	nulldev, nulldev, nulldev, nulldev,  // 074 (0x3c-0x3f)

	nulldev, nulldev, nulldev, nulldev,  // 100 (0x40-0x43)
	nulldev, nulldev, nulldev, nulldev,  // 104 (0x44-0x47)
	nulldev, nulldev, nulldev, nulldev,  // 110 (0x48-0x4b)
	nulldev, nulldev, nulldev, nulldev,  // 114 (0x4c-0x4f)
	nulldev, nulldev, nulldev, nulldev,  // 120 (0x50-0x53)
	nulldev, nulldev, nulldev, nulldev,  // 124 (0x54-0x57)
	nulldev, nulldev, nulldev, nulldev,  // 130 (0x58-0x5b)
	nulldev, nulldev, nulldev, nulldev,  // 134 (0x5c-0x5f)
	nulldev, nulldev, nulldev, nulldev,  // 140 (0x60-0x63)
	nulldev, nulldev, nulldev, nulldev,  // 144 (0x64-0x67)
	nulldev, nulldev, nulldev, nulldev,  // 150 (0x68-0x6b)
	nulldev, nulldev, nulldev, nulldev,  // 154 (0x6c-0x6f)
	nulldev, nulldev, nulldev, nulldev,  // 160 (0x70-0x73)
	nulldev, nulldev, nulldev, nulldev,  // 164 (0x74-0x77)
	nulldev, nulldev, nulldev, nulldev,  // 170 (0x78-0x7b)
	nulldev, nulldev, nulldev, nulldev,  // 174 (0x7c-0x7f)

	rtc80h,  nulldev, nulldev, nulldev,  // 200 (0x80-0x83)
	nulldev, nulldev, nulldev, nulldev,  // 204 (0x84-0x87)
	nulldev, nulldev, nulldev, nulldev,  // 210 (0x88-0x8b)
	nulldev, nulldev, nulldev, nulldev,  // 214 (0x8c-0x8f)
	nulldev, nulldev, nulldev, nulldev,  // 220 (0x90-0x93)
	nulldev, nulldev, nulldev, nulldev,  // 224 (0x94-0x97)
	nulldev, nulldev, nulldev, nulldev,  // 230 (0x98-0x9b)
	nulldev, nulldev, nulldev, nulldev,  // 234 (0x9c-0x9f)
	nulldev, nulldev, nulldev, nulldev,  // 240 (0xa0-0xa3)
	nulldev, nulldev, nulldev, nulldev,  // 244 (0xa4-0xa7)
	nulldev, nulldev, nulldev, nulldev,  // 250 (0xa8-0xab)
	nulldev, nulldev, nulldev, nulldev,  // 254 (0xac-0xaf)
	nulldev, nulldev, nulldev, nulldev,  // 260 (0xb0-0xb3)
	nulldev, nulldev, nulldev, nulldev,  // 264 (0xb4-0xb7)
	nulldev, nulldev, nulldev, nulldev,  // 270 (0xb8-0xbb)
	nulldev, nulldev, nulldev, nulldev,  // 274 (0xbc-0xbf)

	nulldev, nulldev, nulldev, nulldev,  // 300 (0xc0-0xc3)
	nulldev, nulldev, nulldev, nulldev,  // 304 (0xc4-0xc7)
	nulldev, nulldev, nulldev, nulldev,  // 310 (0xc8-0xcb)
	nulldev, nulldev, nulldev, nulldev,  // 314 (0xcc-0xcf)
	nulldev, nulldev, nulldev, nulldev,  // 320 (0xd0-0xd3)
	nulldev, nulldev, nulldev, nulldev,  // 324 (0xd4-0xd7)
	nulldev, nulldev, nulldev, nulldev,  // 330 (0xd8-0xdb)
	nulldev, nulldev, nulldev, nulldev,  // 334 (0xdc-0xdf)
	nulldev, nulldev, nulldev, nulldev,  // 340 (0xe0-0xe3)
	nulldev, nulldev, nulldev, nulldev,  // 344 (0xe4-0xe7)
	nulldev, nulldev, nulldev, nulldev,  // 350 (0xe8-0xeb)
	nulldev, nulldev, nulldev, nulldev,  // 354 (0xec-0xef)
	nulldev, nulldev, nulldev, nulldev,  // 360 (0xf0-0xf3)
	nulldev, nulldev, nulldev, nulldev,  // 364 (0xf4-0xf7)
	nulldev, nulldev, nulldev, nulldev,  // 370 (0xf8-0xfb)

	nulldev, hdskfdh, nulldev, sensesw };// 374 (0xfc-0xff)
	// Port 377 is the sense switch port


/**************************************************************\
*   I/O Management   ******************************************|
\**************************************************************/
byte GetIO( word Port )
{
	/*  Interface to I/O from altair.c and instruction code.  Static
	 *	temp vars used for DS=SS compatibility (Win16 leftover)
	 */

	Atmp = Port;
	I80Regs.CPUstatus |= INP;
	return (byte)(dev_table[ (int) Atmp ] (0 /* read */, 0 /* dummy */));
}


byte GetIO_brkpt( word Port )
{
	/*  Interface to I/O from altair.c and instruction code and used
	 *	for managing breakpoints.  Static temp vars used for DS=SS
	 *	compatibility (Win16 leftover)
	 */

	register byte data = GetIO(Port);

	// FJS - rearranged test to speed execution
	if (BRKPT_IO(Port)) { // potentially a hit -- do slow check
		if (/* hit */ breakpoint_test(BRK_IN, Port, (word)data, &I80Regs.brknum)) {
			I80Regs.brkpt = 1;
			I80Regs.reason = BRKPT_BRK;
	   		I80Regs.XCount = I80Regs.ICount;
			I80Regs.ICount = 0;	// terminates execution loop (if any)
		}
    }
    return data;
}


void SetIO( word Port, byte Value )
{
	/*  Interface to I/O from altair.c and instruction code.  Static
	 *  temp vars used for DS=SS compatibility (Win16 leftover)
	 */

	Atmp = Port; Vtmp = Value;
	I80Regs.CPUstatus |= pOUT;
	dev_table[ (int) Atmp ] (1 /* write */, (int) Vtmp);
}


void SetIO_brkpt( word Port, byte Value )
{
	/*  Interface to I/O from altair.c and instruction code and used
	 *  for managing breakpoints. Static temp vars used for DS=SS
	 *  compatibility (Win16 leftover)
	 */
	
    SetIO(Port, Value);

	// FJS - rearranged test to speed execution
    if (BRKPT_IO(Port)) { // potentially a hit -- do slow check
		if (/* hit */ breakpoint_test(BRK_OUT, Port, (word)Value, &I80Regs.brknum)) {
			I80Regs.brkpt = 1;
			I80Regs.reason = BRKPT_BRK;
			I80Regs.XCount = I80Regs.ICount;
			I80Regs.ICount = 0;	// terminates execution loop (if any)
		}
    }
}


/**************************************************************\
*   Memory Access   *******************************************|
\**************************************************************/
byte GetMem( word Addr )
{
	I80Regs.CPUstatus |= MEMR;
	return Mem[ Addr ]; // ABR
}


byte GetMem_brkpt( word Addr )
{
	register /* FJS */ byte data = GetMem(Addr); // ABR

	// FJS - rearranged test to speed execution
    if (BRKPT_ADDR(Addr)) {	// potentially a hit -- do slow check
		if (/* hit */ breakpoint_test(BRK_RD, Addr, (word)data, &I80Regs.brknum)) {
		    I80Regs.brkpt = 1;
	    	I80Regs.reason = BRKPT_BRK;
		    I80Regs.XCount = I80Regs.ICount;
		    I80Regs.ICount = 0;	// terminates execution loop (if any)
		}
    }
    return data;
}


void SetMem( word Addr, byte Value )
{
	I80Regs.CPUstatus &= ~WO;
	// Do page writability test here.  0=writable/1=R/O -
	if (!PageAccessRights[Addr]) Mem[ Addr ] = Value; // ABR
	return;
}


void SetMem_brkpt( word Addr, byte Value )
{
    SetMem(Addr, Value); // ABR

	// FJS - rearranged test to speed execution
    if (BRKPT_ADDR(Addr)) {	// potentially a hit -- do slow check
		if (/* hit */ breakpoint_test(BRK_WR, Addr, (word)Value, &I80Regs.brknum)) {
		    I80Regs.brkpt = 1;
	    	I80Regs.reason = BRKPT_BRK;
		    I80Regs.XCount = I80Regs.ICount;
		    I80Regs.ICount = 0;	// terminates execution loop (if any)
		}
    }
}


/**************************************************************\
*   CPU Control   *********************************************|
\**************************************************************/
byte clock8080( I8080 *R )
{
	register byte tmp;			// instruction
	word savestatus;

// FJS activated proto-interrupt handler code to check speed hit -
#if 0
	// Skip IRQ processing for now -- no emulated hardware requires it

	/*  This would also be a likely place to handle HOLD (13)/HLDA (21) pin
		emulation in support of emulated DMA transfers.
	
		The HOLD signal alerts the CPU that an external device wants to gain
		control of the data/address buss at the end of the then current
		machine	cycle. The HOLD pin is acknowledged under the following
		conditions:

		(1) the CPU is in the HALT state, or 
		(2) the CPU is in the T2 or TW machine cycle and the READY signal is
			active.

		When the CPU enters the HOLD state, the address and data busses are
		tri-stated and the CPU raises the HLDA (Hold Acknowledge) pin. The
		HLDA signal begins at:

		(1) T3 for memory read or input operations pending;
		(2) the clock period following T3 for memory write or output operations.

		In either case, the HLDA signal appears after the rising edge of the
		PHI2 clock.

	*/
#else	
	/*  Check for any pending IRQs before M1 machine cycle. IRQ only occurs
		as a result of the transition of the IRQ pin (pin 14) and processing
		occurs *only if* interrupts are enabled using EI and !HALT. Upon
		receiving IRQ, the CPU resets the IFF and enters the INTA cycle. 
		IRQ routine terminates with RET instruction which pops the return
		address off of the stack. The IRQ routine is responsible for saving
		and restoring any registers it uses.
		
		The INTA cycle is identical to the OF (opcode fetch) cycle except
		that *INTA is strobed instead of *RD.  The following status bits
		are set to 1: INTA, *WO and M1.
		
		Upon receiving the *INTA signal, the external device jams an opcode
		onto the data bus.  Appropriate opcodes are RST or CALL.  RST is 1
		byte and CALL is 3 bytes.  Multi-byte opcode jamming results in
		multiple strobes of *INTA to clock in the target address.  The
		one-byte RST is the better choice.  During execution of an interrupt,
		normal incrementing of PC is suppressed.
		
		For a RST instruction, the entry PC is then saved to the stack and
		the target address (in the op-code) is loaded to PC.  Execution
		resumes at the new PC address.
	*/
	
	if ((R->IFF == 1) && (R->CPUstatus & sINT)) { // FJS

		R->IFF = 0;					// mask new IRQs during service routine
		R->CPUstatus = WO | M1 | sINT;	// set proper status bits for INTA cycle

#if 0
		// should fetch hardware generated op-code(s) here? (no PC change!)
#else
		CurInstruction = CLK_OP;		// FJS assume RST opcode from clock.

		// simulate PUSH of (un-incremented) return address
		R->nWZ.W = R->nPC.W;
		PC_ABR = --(R->nSP.W);	// push hi ret addr
		R->CPUstatus |= STACK;
		R->CPUstatus &= ~(WO | M1);

		Wr80(ABR, R->nWZ.B.h);
		PC_ABR = --(R->nSP.W);	// push lo ret addr

		Wr80(ABR, R->nWZ.B.l);
		// R->CPUstatus &= ~STACK;		
		// R->nPC.W = Rd80(0xfff8); // set new PC to...??? Set by hardware device.
		ABR = R->nPC.W = (word)(CurInstruction & 070); // for RST op-code
		R->ICount -= 11;			// decrement clock tick count by fixed amount
       	// R->CPUstatus &= ~sINT;		// Clear IRQ pending bit
#endif		
		// fall-through to execute code at IRQ address
		R->CPUstatus = CPU_CLR;		// starting CPU status
	}
//	END OF IRQ CODE
#endif
	// not interrupt or post-interrupt -
	if (R->IFF /* != 0 */) R->IFF = 1; // FJS for 1 instruction delay 
	R->CPUstatus = CPU_CLR;		// starting CPU status
	if (R->CPUcycle == 0) {			// M1 instruction fetch

		CurInstruction = tmp = GetMem(ABR = R->nPC.W);
		R->ICount -= Cycles8080[tmp];	// set clock countdown
		R->startPC = ABR;	// save current PC for breakpoint/debugger

		// The check for a magic trap instruction PFX_ED is handled in the
		// Itable.  This is a little different than Solace which uses a
		// switch statement to process the opcodes.
		Utable[pfx_state][ tmp ] ( /* tmp /*, R->CPUcycle */ ); // FJS
	} 
	else {
		Utable[pfx_state][ CurInstruction ] ( /* CurInstruction /*, R->CPUcycle */ );
		R->CPUcycle--;
	}

	if (R->CPUcycle != 0)
		R->CPUstatus &= ~M1;
	else {
		R->CPUstatus = CPU_CLR | (R->CPUstatus & sINT); // FJS
		if (R->IFF > 1) --(R->IFF); // FJS for 1 instruction delay 
		if ((R->IFF == 1) && (R->CPUstatus & sINT))
			R->CPUstatus = WO | M1 | sINT;	// set proper status bits for INTA cycle
	}

	savestatus = R->CPUstatus; {	// set-up for next Front Panel display.
		// TBD hardware interrupt code set-up
		tmp = GetMem(ABR /* = R->nPC.W */);	// FJS ABR !!!
	}; R->CPUstatus = savestatus;
	return tmp;
}


byte clock8080_brkpt(I8080 *R)
{
    register word addr = ABR /* = R->nPC.W */; // ABR
    byte retval;

	// FJS - rearranged test to speed execution
	if (BRKPT_ADDR(addr) && !simstate.firststep) { // potentially a hit -- do slow check
		if (/* hit */ breakpoint_test(BRK_OP, addr, (word)0, &R->brknum) ) { // ABR
		    R->brkpt = 1;
		    R->reason = BRKPT_BRK;
		    R->XCount = R->ICount;
		    R->ICount = 0;	// terminates execution loop (if any)
		    simstate.firststep = 0;
		    return GetMem_brkpt(addr); // ABR
		}

    }

    retval = clock8080(R);    // execute the instruction

	// FJS - rearranged test to speed execution
    if (R->brkpt) { // validate breakpoint (rd16 and wr16 are troublesome)
		if (! /* hit */ breakpoint_verify(&R->brknum) ) { // oops, false alarm
		    R->brkpt  = FALSE;
		    R->ICount = R->XCount;	// restore remaining count
		}
    }
    simstate.firststep = 0;	// clear it in case this is the first time
    return retval;
}


void Reset8080( void )
{
	/* Called by altair.c to reset the processor on power-off and when
	 * the reset switch is toggled.  According to the data sheets, on
	 * reset, PC=0, INTE and HLDA flags are reset but no other registers
	 * or flags are affected.
	 */

	I80Regs.CPUstatus = CPU_CLR;	// WO|M1
	I80Regs.CPUcycle = 0;			// reset cycle to FETCH
	I80Regs.IFF = 0;				// no interrupts
	MBR = GetMem ( ABR = I80Regs.nPC.W = 0 );	// update data bus LED buffer
}


void SYS_Set8080Speed( speed_t clkrate )
{
    simstate.up_speed = clkrate;	// change to new speed
    UpdateTimerInfo();				// UTI() uses up_speed to set scale factor
    								//   for scheduler.
}


#if 0
speed_t SYS_Get8080Speed( void ) // FJS not used
{
    return simstate.up_speed;
}
#endif


void SYS_SetCPUType( cputype_t cputype ) {
register int n;

	simstate.up_type = cputype;

	if ( (is_Z80 = cputype) & 1 ) {		// Z80
		// CPUCycles = &CyclesZ80[0];

		Utable[0] = Z80table;
		Utable[1] = ZIXtable;
		Utable[2] = ZIYtable;
		Utable[3] = ZEDtable;
		Utable[4] = ZCBtable;
		Utable[5] = ZXCtable;
		Utable[6] = ZYCtable;
	} else {						// 8080
		// CPUCycles = &Cycles8080[0];

		for (n = 0; n < 7; ++n)
			Utable[n] = Itable; // for i8080 only Utable[0] is used
	}
	pfx_state = 0;
}


cputype_t SYS_GetCPUType( void )
{
    return simstate.up_type;
}


void CpuPOR ( void )
{
	/* Power-on reset CPU function. Per the data sheets, on POR all regs and
	 * flags are "undefined" except for PC.  We'll clear the working registers
	 * to be safe and to avoid variable persistence through simulated power
	 * cycles.
	 */
	register i;

	
	I80Regs.CPUstatus = CPU_CLR;	// WO|M1
	I80Regs.nSP.W = STACK_TOP;		// 0xffff <FJS Z80>
	I80Regs.CPUcycle = // 0;		// fetch cycle
	I80Regs.IFF   = // 0;			// no interrupts
//	I80Regs.nPC.W = 0;				// initialized below
	I80Regs.nAF.W = // 0;			// A=F=0
	I80Regs.nBC.W = // 0;
	I80Regs.nDE.W = // 0;
	I80Regs.nHL.W = // 0;
	I80Regs.nAFp.W = // 0;			// A=F=0 Z80 <FJS Z80>
	I80Regs.nBCp.W = // 0;
	I80Regs.nDEp.W = // 0;
	I80Regs.nIX.W = // 0;
	I80Regs.nIY.W = // 0;
	pfx_state =
	I80Regs.nWZ.W = 0;				// clear hidden word register

	// memset(&Mem, 0, MEMSIZE);	// clear memory
	for (i=0; i< MEMSIZE; i++)		// or make random on power-up!
		Mem[i] = (rand() & 0xff);

	memset(&PageAccessRights, 0, MEMSIZE);		// assume memory is all RAM
	MBR = GetMem ( ABR = I80Regs.nPC.W = 0 );	// first fetch

	// Clear ROM array
	// ZeroMemory(&load_rom, (sizeof(load_rom) * MAXROMS));
    for (i=0; i<MAXROMS; i++){
    	load_rom[i].iUsed = 0;
		load_rom[i].szROMName[0] = 0;
		load_rom[i].iROMStart = 0;
		load_rom[i].iROMSize = 0;
		load_rom[i].Read_only = 0;
	}
}


/**************************************************************\
*   Invalid Opcode Trap   *************************************|
\**************************************************************/
/*-- Trap_*() -------------------------------------------------
	These functions are used by the emulator to provide a
	simple mechanism for importing and exporting files between
	the emulated computer and the real disk system.  These
	encodings have been borrowed from xtrs80, a TRS-80 emulator
	written by Tim Mann for Unix platforms.  Modified by RAC for
	use in the Altair32 Emulator
-------------------------------------------------------------*/

// make sure it is an extended op we support, then dispatch it.
// use to return 1 if an extended up, 0 if not.
static void Trap_ED_ops( OP_ARG_U )
{

	I8080 *R;
	int badop = 0;
	register byte tmp;
	
	R = &I80Regs;	// cast a pointer for use with trap instructions.
	tmp = Rd80(ABR /* = R->nPC.W */); // get opcode or trap_op, depending on cycle

	switch ( R->CPUcycle ) {
		case 0:
			R->CPUcycle = 1;
			ADVANCE_PC;
		break;

		case 1:
			switch (tmp) {	// switch on operand
		      /* Emulator traps -- not real x80 instructions */
#if 0
				case 0x28: do_emt_system(R)		;	break;
				case 0x29: do_emt_mouse(R)		;	break;
				case 0x2a: do_emt_getddir(R)	;	break;
				case 0x2b: do_emt_setddir(R)	;	break;
				case 0x2f:
					if (trs_continuous > 0) trs_continuous = 0;
					debug = 1;
					R->nPC.W--; // pre-cancel default increment FJS
				break;
#endif
				case 0x30: do_emt_open(R)		;	break;
				case 0x31: do_emt_close(R)		;	break;
				case 0x32: do_emt_read(R)		;	break;
				case 0x33: do_emt_write(R)		;	break;
				case 0x34: do_emt_lseek(R)		;	break;
				case 0x35: do_emt_strerror(R)	;	break;
#if 0
				case 0x36: do_emt_time(R)		;	break;
				case 0x37: do_emt_opendir(R)	;	break;
				case 0x38: do_emt_closedir(R)	;	break;
				case 0x39: do_emt_readdir(R)	;	break;
				case 0x3a: do_emt_chdir(R)		;	break;
				case 0x3b: do_emt_getcwd(R)    ;	break;
				case 0x3c: do_emt_misc(R)		;	break;
				case 0x3d: do_emt_ftruncate(R)	;	break;
#endif
				case 0x3e: do_emt_opendisk(R)	;	break;
#if 0
				case 0x3f: do_emt_closedisk(R)	;	break;
#endif
				default:   badop = 1 /* YES */	;	// break;
			}	// end case 1/switch (tmp)

			if (badop)	R->nPC.W--; // unfetch 2nd byte of trap_op
			else		R->nPC.W++;	// advance PC past 2nd byte FJS
			// return /* !badop */; // end case 1
		// break;

	}	// end switch(cycle)
	pfx_state = 0;
	ABR = R->nPC.W; // FJS
	// return;
}


/**************************************************************\
*   Simulation Control/Breakpoints   **************************|
\**************************************************************/
void simstep(void)
{
	// execute one instruction and return
	
    register /* FJS */ int t1;

    I80Regs.startPC = ABR = I80Regs.nPC.W;
    t1 = I80Regs.ICount;
    MBR = (simstate.exec8080_func)(&I80Regs);
    TimerTick(t1 - I80Regs.ICount);
}


void simrun(void)
{
	// run for I80Regs.ICount simulated 8080 clock cycles and return
	
	register /* FJS */ int t1;

    while (pfx_state || (I80Regs.ICount > 0)) { // FJS
		I80Regs.startPC = ABR = I80Regs.nPC.W;
		t1 = I80Regs.ICount;
	    MBR = (simstate.exec8080_func)(&I80Regs);
		TimerTick(t1 - I80Regs.ICount);
    }
}


void simstepover(void)
{

	// step over simstate.stepcount instructions or until ICount
	// simulated 8080 cycles have elapsed.
	
    register /* FJS */ int t1;
    byte nextop;

    while (I80Regs.ICount > 0) {

		I80Regs.startPC = I80Regs.nPC.W;
		t1 = I80Regs.ICount;

		if (simstate.runstate == STEP) {
		    nextop = Di80(ABR = I80Regs.nPC.W);
	    	if (  (nextop         == 0xCD) // call
				 || ((nextop & 0xC7) == 0xC4)) { // call conditional
				breakpoint_temp_add((word)(I80Regs.nPC.W + 3));  // just past call
				simstate.runstate = STEPRUN;	// run until breakpoint
		    }
		}

	    MBR = (simstate.exec8080_func)(&I80Regs);
		TimerTick(t1 - I80Regs.ICount);

		if (I80Regs.brkpt) {
			if (I80Regs.brknum /* != 0 */) return; // we hit a real breakpoint
			// ok, we're not really done, we've just hit the
			// step-over temporary breakpoint.  Note: we didn't
			// perform the operation, so don't dec stepcount.
			breakpoint_temp_kill();
			I80Regs.ICount = I80Regs.XCount; // restore count
			simstate.runstate = STEP;
			simstate.stepcount--;
		} else if (simstate.runstate == STEP)
		     simstate.stepcount--;

		if (simstate.stepcount < 1) {
		    // we've stepped the appropriate number of times;
		    // go back to debugger mode
		    I80Regs.ICount = 0;
	    	breakpoint_temp_kill();
		    UI_DbgWin(DW_ActivateStep, 0);
		    simstate.runstate = HALTED;
		    report_curstate();
		    return;
		}

    } // while

    // we ran out of cycles in this time slice, but leave active
    // the STEP state and the remaining number of stepcounts.
}


void brkpt_notify(void)
{
	// report why we've stopped
	
	char buf[80];

    // if it was a temporary breakpoint (run-to address, single step),
    // don't squawk about it
    if (I80Regs.brknum == 0) return;

    UI_DbgWin(DW_Activate, 0);

    switch (I80Regs.reason) {
	case BRKPT_BRK:
	    sprintf(buf, "  Processor hit breakpoint %d at PC=$%04X.",
	    	I80Regs.brknum, I80Regs.startPC);
	    UI_DbgWinLog(buf, FALSE);
	    sprintf(buf, "blist %d", I80Regs.brknum);
	    dbg_interp(buf);
	break;
	case BRKPT_ILLEGAL:
	    sprintf(buf, "  Processor executed undefined instruction at PC=$%04X.\n"
				"Reset the Altair.",
	    	I80Regs.nPC.W);
	    UI_DbgWinLog(buf, FALSE);
	break;
	case BRKPT_HALT:
	    sprintf(buf, "  Processor executed HALT instruction at PC=$%04X.\n"
				"Reset the Altair.",
	    	I80Regs.nPC.W);
	    UI_DbgWinLog(buf, FALSE);
	break;
	case BRKPT_DEBUG:
	    sprintf(buf, "  Processor stopped for debugging at PC=$%04X.\n"
					"  You must reset the Altair.",
	    	I80Regs.nPC.W);
	    UI_DbgWinLog(buf, FALSE);
	// break;
	// default: /* no-op */ break;
    }
}


void report_curstate(void)
{
	// this is called every time we transition to the halted state
	
	char buf[120];
    register /* FJS */ int len;

	// TBD Z80
    len = sprintf(buf,
 "  A=%02X F=%c%cx%cx%c%c%c BC=%04X DE=%04X HL=%04X SP=%04X PC=%04X ",
		      FLAGS /* = 0 */, // FJS
		    ((FLAGS) & 0x80) ? 'S' : '-',
		    ((FLAGS) & 0x40) ? 'Z' : '-',
		    ((FLAGS) & 0x10) ? 'H' : '-',
		    ((FLAGS) & 0x04) ? 'P' : '-',
		    ((FLAGS) & 0x02) ? 'N' : '-',
		    ((FLAGS) & 0x01) ? 'C' : '-',
		      I80Regs.nBC.W,
		      I80Regs.nDE.W,
		      I80Regs.nHL.W,
		      I80Regs.nSP.W,
		      I80Regs.nPC.W
		);

#if 1
    // add disassembly to tail end of buf
    (void)DAsm(&buf[len], (word)I80Regs.nPC.W, 0);
#endif

    UI_DbgWinLog(buf, FALSE);
}


/**************************************************************\
*   Instruction Helper Routines
\**************************************************************/
static void SetFlags ( word t ) // TBD Z80
{
	// Flag-setting routine...called with word-size copy of 
	// accumulator: [pad][AC].  AC_FLAG used as carry from bit3
	// (BCD instructions)

	// check for all zeros and set zero flag
	if (!(t & 0x0ff)) FLAGS |= Z_FLAG;
	else	FLAGS &= ~Z_FLAG;

	// check for carry out
	if (t & ~0x0ff) FLAGS |= CY_FLAG;
	else	FLAGS &= ~CY_FLAG;

	// check sign and set flag
	if (t & 0x080) FLAGS |= S_FLAG;
	else	FLAGS &= ~S_FLAG;

	// check parity and set flag
	if ((bit_table[ t&0x0f ] + bit_table[ (t>>4)&0x0f ]) & 0x01)
		FLAGS &= ~P_FLAG;
	else	FLAGS |= P_FLAG;
}
	
static void SetNCFlags ( word t ) // TBD Z80
{
	// Flag-setting routine...called with word-size copy of 
	// accumulator: [pad][AC].  AC_FLAG used as carry from bit3
	// (BCD instructions).  Don't change CT_FLAG.

	// check for all zeros and set zero flag
	if (!(t & 0x0ff)) FLAGS |= Z_FLAG;
	else	FLAGS &= ~Z_FLAG;

	// check sign and set flag
	if (t & 0x080) FLAGS |= S_FLAG;
	else	FLAGS &= ~S_FLAG;

	// check parity and set flag
	if ((bit_table[ t&0x0f ] + bit_table[ (t>>4)&0x0f ]) & 0x01)
		FLAGS &= ~P_FLAG;
	else	FLAGS |= P_FLAG;
}
	
static void SetVFlgs ( word t ) // TBD Z80
{
	// Flag-setting routine...called with word-size copy of 
	// accumulator: [pad][AC].  AC_FLAG used as carry from bit3
	// (BCD instructions).  Set P_FLAG per overflow.

	// check for all zeros and set zero flag
	if (!(t & 0x0ff)) FLAGS |= Z_FLAG;
	else	FLAGS &= ~Z_FLAG;

	// check for carry out
	if (t & ~0x0ff) FLAGS |= CY_FLAG;
	else	FLAGS &= ~CY_FLAG;

	// check sign and set flag
	if (t & 0x0080) FLAGS |= S_FLAG;
	else	FLAGS &= ~S_FLAG;

	// check parity and set flag
	if ((bit_table[ t&0x0f ] + bit_table[ (t>>4)&0x0f ]) & 0x01)
		FLAGS &= ~P_FLAG;
	else	FLAGS |= P_FLAG;
}
	

static void SetACFlag ( word a, word b, byte op ) // TBD Z80
{
	// AuxCarry flag set routine.  Called with two byte-padded nibbles
	// and an arithmetic op code (ADD, SUBTRACT).  A=ACC, B=Operand

	register /* FJS */ word tmp = a & 0x0f;	// usually the accumulator

	if ( op == ADD )
		tmp += b & 0x0f;	// addition operation
	else
		tmp -= b & 0x0f;	// subtraction operation (like CMP)

	if ( tmp & ~0x0f )		// carry from bit3
		FLAGS |= AC_FLAG;
	else
		FLAGS &= ~AC_FLAG;
}


static void	nul ( OP_ARG_U )
{

	/*
	   Step PC to skip over bad opcode. This routine is here to
	   enable the automatic pop-up of the debugger if the user
	   wants that functionality. Illegal opcodes are normally
	   treated as "nop", which only steps the PC.
	 */
	
	//OPTIONAL: break to debugger on illegal instruction
    //I80Regs.brkpt  = 1;
    //I80Regs.reason = BRKPT_ILLEGAL;
	/////////////////////////////////
	pfx_state = 0;
	ADVANCE_PC;
}


/**************************************************************\
*   Processor Instruction Routines
*
*	Flags: SZAPC = Sign/Zero/Aux Carry/Parity/Carry
*	Flags: -*01 = unaffected/affected/reset/set
*
*	Notes:
*	To accurately emulate the 8080 bus cycle, several instructions
*	will need to be changed so that they temporarily change PC
*	(which is effectively the front panel address LED buffer) to
*	the then current address during M1 (fetch) and reset it to the
*	original value during the last cycle of the instruction.
*
*	The trade off for accurate emulation is that it will slow
*	the emulator dramatically and the integrated debugger was
*	not designed to work with accurate bus cycles, so these
*	changes will probably break the debugger.
*
*	LDAX/STAX have already been changed to support the
*		"Kill the Bit" program.
*
*	MOV r,M   MOV M,r   MVI M,data LDA addr  STA addr
*	LHLD addr SHLD addr LDAX rp    STAX rp   ADD M
*	ADC M     SUB M     SBB M      INR M     DCR M
*	ANA M     XRA M     ORA M      CMP M     CALL
*	CALL cond RET       RET cond   POP PSW   PUSH PSW
*	POP rp    PUSH rp   XTHL       IN port   OUT port
\**************************************************************/

static void	ZIX ( OP_ARG_U ) { pfx_state = 1;   ADVANCE_PC; } // FJS Z80
static void	ZIY ( OP_ARG_U ) { pfx_state = 2;   ADVANCE_PC; } // FJS Z80
static void	ZED ( OP_ARG_U ) { pfx_state = 3;   ADVANCE_PC; } // FJS Z80
static void	ZCB ( OP_ARG_U ) { pfx_state |= 4;  ADVANCE_PC; } // FJS Z80

static void	EXAF ( OP_ARG ) { // FJS Z80
	I80Regs.nWZ.W = I80Regs.nAFp.W;
	I80Regs.nAFp.W = I80Regs.nAF.W;
	I80Regs.nAF.W = I80Regs.nWZ.W; 

	pfx_state = 0;
	ADVANCE_PC;
}

static void	EXX  ( OP_ARG ) { // FJS Z80
	I80Regs.nWZ.W = I80Regs.nBCp.W;
	I80Regs.nBCp.W = I80Regs.nBC.W;
	I80Regs.nBC.W = I80Regs.nWZ.W; 

	I80Regs.nWZ.W = I80Regs.nDEp.W;
	I80Regs.nDEp.W = I80Regs.nDE.W;
	I80Regs.nAF.W = I80Regs.nWZ.W; 

	I80Regs.nWZ.W = I80Regs.nHLp.W;
	I80Regs.nHLp.W = I80Regs.nHL.W;
	I80Regs.nHL.W = I80Regs.nWZ.W; 

	pfx_state = 0;
	ADVANCE_PC;
}

static void	DJNZ ( OP_ARG ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80

static void	jr  ( OP_ARG_U ) { // FJS Z80
	ADVANCE_PC; // skip op-code

	I80Regs.nWZ.B.l = Rd80(ABR); // get displacement
	ADVANCE_PC;

	ABR = (I80Regs.nPC.W += (signed char)I80Regs.nWZ.B.l); // use displacement
	pfx_state = 0;
}

static void	JRC ( OP_ARG_U ) { // FJS Z80
	if (FLAGS & CY_FLAG) {
		jr();
	} else {
		I80Regs.nPC.W++;
		ADVANCE_PC;
	}
	pfx_state = 0;
}

static void	JRNC( OP_ARG_U ) { // FJS Z80
	if (!(FLAGS & CY_FLAG)) {
		jr();
	} else {
		I80Regs.nPC.W++;
		ADVANCE_PC;
	}
	pfx_state = 0;
}

static void	JRZ ( OP_ARG_U ) { // FJS Z80
	if (FLAGS & Z_FLAG) {
		jr();
	} else {
		I80Regs.nPC.W++;
		ADVANCE_PC;
	}
	pfx_state = 0;
}

static void	JRNZ( OP_ARG_U ) { // FJS Z80
	if (!(FLAGS & Z_FLAG)) {
		jr();
	} else {
		I80Regs.nPC.W++;
		ADVANCE_PC;
	}
	pfx_state = 0;
}

static void	ZIC ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	IPC ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	OCZ ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	OPC ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	SXC ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	AXC ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LDX ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LXD ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	NEG ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	RTN ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	RTI ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	IMM ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LIA ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LRA ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LAI ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LAR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	RRD ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	RLD ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80

static void	LDI ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LDD ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LIR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	LDR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	CPI ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	CPD ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	CIR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	CDR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	INI ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	IND ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	IIR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	IDR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	OTI ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	OTD ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	OIR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	ODR ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80

static void	ROT ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	RTM ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	BIT ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	BTM ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	RES ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	RSM ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	SET ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80
static void	STM ( OP_ARG_U ) {/* STUB */ pfx_state = 0; ADVANCE_PC; } // FJS Z80



static void	add ( OP_ARG_U )
{
// ADD
// ADD r:: A=A+r
// ADD M:: A=A+[HL]
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;
	byte actmp = 0;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory add
		if (pfx_state == 1) { // FJS Z80
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIX.W + (signed char)actmp);
		} else if (pfx_state == 2) {
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIY.W + (signed char)actmp );
		} else
			actmp = Rd80 (ABR = I80Regs.nHL.W );	// removed (word) casting
	} else  // register add
		actmp = *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];
	SetACFlag( (word)tmp, (word)actmp, ADD );
	tmp += actmp;
	ACCUM = (byte) tmp;
	SetFlags( tmp );
	pfx_state = 0;
	ADVANCE_PC;
}

static void	adc ( OP_ARG_U )
{
// ADD with Carry
// ADC r:: A=A+r+CY
// ADC M:: A=A+[HL]+CY
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;
	byte actmp = 0;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory add
		if (pfx_state == 1) { // FJS Z80
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIX.W + (signed char)actmp);
		} else if (pfx_state == 2) {
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIY.W + (signed char)actmp );
		} else
			actmp = Rd80 (ABR = I80Regs.nHL.W );	// removed (word) casting
	} else  // register add
		actmp = *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];
	SetACFlag ( (word)tmp, (word)actmp, ADD );
	tmp += actmp;
	if ( FLAGS & CY_FLAG ) tmp++;

	ACCUM = (byte) tmp;
	SetFlags ( tmp );
	pfx_state = 0;
	ADVANCE_PC;
}

static void	sub ( OP_ARG_U )
{
// Subtract
// SUB r:: A=A-r
// SUB M:: A=A-[HL]
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;
	byte actmp /* = 0 */;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory SUB
		if (pfx_state == 1) { // FJS Z80
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIX.W + (signed char)actmp);
		} else if (pfx_state == 2) {
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIY.W + (signed char)actmp );
		} else
			actmp = Rd80 (ABR = I80Regs.nHL.W );	// removed (word) casting
	} else  // register SUB
		actmp = *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];
	SetACFlag ( (word)tmp, (word)actmp, SUBTRACT );
	tmp -= actmp;
	ACCUM = (byte) tmp;
	SetFlags ( tmp );
	pfx_state = 0;
	ADVANCE_PC;
}

static void	sbb ( OP_ARG_U ) 
{
// Subtract with Borrow
// SBB r:: A=A-r-CY
// SBB M:: A=A-[HL]-CY
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;
	byte actmp /* = 0 */;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory SUB
		if (pfx_state == 1) { // FJS Z80
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIX.W + (signed char)actmp);
		} else if (pfx_state == 2) {
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIY.W + (signed char)actmp );
		} else
			actmp = Rd80 (ABR = I80Regs.nHL.W );	// removed (word) casting
	} else  // register SUB
		actmp = *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];
	SetACFlag ( (word)(ACCUM), (word)actmp, ADD );
	tmp -= actmp;

	// added:subtract carry flag
	if ( FLAGS & CY_FLAG ) tmp--;

	ACCUM = (byte) tmp;
	SetFlags ( tmp );
	pfx_state = 0;
	ADVANCE_PC;
}

static void	ana ( OP_ARG_U )
{
// AND Accumulator
// ANA r:: A=A&r
// ANA M:: A=A&[HL]
// Flags:	SZAPC
//			****0 CY=0 and AC= logical OR of bits 3

	register /* FJS */ word tmp = ACCUM;		// padded copy of acc
	word actmp = ACCUM;	// saved copy of acc
	word tmp2 /* = 0 */;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory logical 'AND'
		if (pfx_state == 1) { // FJS Z80
			tmp2 = Rd80 (ADVANCE_PC);

			tmp2 = Rd80 (ABR = I80Regs.nIX.W + (signed char)tmp2 );
		} else if (pfx_state == 2) {
			tmp2 = Rd80 (ADVANCE_PC);

			tmp2 = Rd80 (ABR = I80Regs.nIY.W + (signed char)tmp2 );
		} else
			tmp2 = Rd80 (ABR = I80Regs.nHL.W ); // get operand <removed (word) casting>
	} else  // register logical 'AND'
		tmp2 = *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];

	tmp &= tmp2;

	ACCUM = (byte) tmp;
	SetNCFlags ( tmp ); // FJS
	FLAGS &= ~CY_FLAG;			// clear CY flag

	// set AC to OR of bits 3
	if ( (actmp | tmp2) & 0x08 ) // FJS
		FLAGS |= AC_FLAG;			// set AC flag
	else
		FLAGS &= ~AC_FLAG;			// clear AC flag
		
	pfx_state = 0;
	ADVANCE_PC; // FJS
}

static void	xra ( OP_ARG_U )
{
// Exclusive OR Accumulator  CY and AC flags cleared
// XRA r:: A=Axr
// XRA M:: A=Ax[HL]
// Flags:	SZAPC
//			**0*0

	register /* FJS */ word tmp /* = ACCUM */;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory logical 'XOR'
		if (pfx_state == 1) { // FJS Z80
			tmp = Rd80 (ADVANCE_PC);

			tmp = (byte)ACCUM ^= Rd80 (ABR = I80Regs.nIX.W + (signed char)tmp );
		} else if (pfx_state == 2) {
			tmp = Rd80 (ADVANCE_PC);

			tmp = (byte)ACCUM ^= Rd80 (ABR = I80Regs.nIY.W + (signed char)tmp );
		} else
			tmp = (byte)ACCUM ^= Rd80 (ABR = I80Regs.nHL.W ); // removed (word) casting
	} else  // register logical 'XOR'
		tmp = (byte)ACCUM ^= *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];

	SetNCFlags ( tmp ); // FJS
	FLAGS &= ~(CY_FLAG | AC_FLAG);	// clear CY & AC flag FJS
	// FLAGS &= ~AC_FLAG;			// clear AC flag
	pfx_state = 0;
	ADVANCE_PC;
}

static void	ora ( OP_ARG_U )
{
// Inclusive OR Accumulator  CY and AC flags cleared
// ORA r:: A=Avr
// ORA M:: A=Av[HL]
// Flags:	SZAPC
//			**0*0

	register /* FJS */ word tmp = ACCUM;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory logical 'OR'
		if (pfx_state == 1) { // FJS Z80
			tmp = Rd80 (ADVANCE_PC);

			tmp = (byte)ACCUM |= Rd80 (ABR = I80Regs.nIX.W + (signed char)tmp );
		} else if (pfx_state == 2) {
			tmp = Rd80 (ADVANCE_PC);

			tmp = (byte)ACCUM |= Rd80 (ABR = I80Regs.nIY.W + (signed char)tmp );
		} else
			tmp = (byte)ACCUM |= Rd80 (ABR = I80Regs.nHL.W ); // removed (word) casting
	} else  // register logical 'OR'
		tmp = (byte)ACCUM |= *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];

	SetNCFlags ( tmp );
	FLAGS &= ~(CY_FLAG | AC_FLAG);	// clear CY & AC flag FJS
	// FLAGS &= ~AC_FLAG;			// clear AC flag
	pfx_state = 0;
	ADVANCE_PC;
}

static void	cmp ( OP_ARG_U )
{
// Compare
// CMP r:: A-r
// CMP M:: A-[HL]
// Flags:	SZAPC
//			*****
// Instruction decoding	76 543 210
//						xx xxx xxx
//						op dst src

	register /* FJS */ word tmp = ACCUM;
	byte actmp /* = 0 */;

	// fast version -
	if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) { // memory cmp
		if (pfx_state == 1) { // FJS Z80
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIX.W + (signed char)actmp);
		} else if (pfx_state == 2) {
			actmp = Rd80 (ADVANCE_PC);

			actmp = Rd80 (ABR = I80Regs.nIY.W + (signed char)actmp );
		} else
			actmp = Rd80 (ABR = I80Regs.nHL.W );	// removed (word) casting
	} else				// register cmp
		actmp = *b_regs [ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];

	SetACFlag ( (word)tmp, (word)actmp, SUBTRACT );
	tmp -= actmp;

	SetFlags ( tmp );
	pfx_state = 0; // FJS
	ADVANCE_PC;
}

static void	adi ( OP_ARG_U )
{
// Add immediate
// ADD n:: A=A+n
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp =  ACCUM;
	byte actmp = 0;
	
	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;				// increment to operand
		break;

		case 1:
			actmp = Rd80(ABR);
			SetACFlag ( (word)tmp, (word)actmp, ADD );
			tmp += actmp;
			ACCUM = (byte) tmp;
			SetFlags ( tmp );
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	aci ( OP_ARG_U )
{
// Add with carry immediate
// ACI n:: A=A+n+CY
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;
	byte actmp = 0;

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;					// increment to operand
		break;

		case 1:
			actmp = Rd80(ABR);
			SetACFlag ( (word)tmp, (word)actmp, ADD );
			tmp += actmp;

			// added: add carry flag
			if ( FLAGS & CY_FLAG ) tmp++;

			ACCUM = (byte) tmp;
			SetFlags ( tmp );
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	sui ( OP_ARG_U ) 
{
// Subtract immediate
// SUI n:: A=A-n
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;
	byte actmp = 0;

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;				// increment to operand
		break;

		case 1:
			actmp = Rd80(ABR);
			SetACFlag ( (word)tmp, (word)actmp, SUBTRACT );
			tmp -= actmp;

			ACCUM = (byte) tmp;
			SetFlags ( tmp );
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	sbi ( OP_ARG_U ) 
{
// Subtract with borrow immediate
// SBI n:: A=A-n-CY
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;	//padded ACC
	byte actmp = 0;

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;				// increment to operand
		break;

		case 1:
			actmp = Rd80(ABR);
			SetACFlag ( (word)tmp, (word)actmp, SUBTRACT );
			tmp -= actmp;

			//added:subtract carry flag
			if ( FLAGS & CY_FLAG ) tmp--;

			ACCUM = (byte) tmp;
			SetFlags ( tmp );
			pfx_state = 0;
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	ani ( OP_ARG_U )
{
// AND Immediate  
// ANI n:: A=A&n
// Flags:	SZAPC
//			**0*0  CY=0 and AC = logical OR of bits3

	register /* FJS */ word tmp = ACCUM;
	word tmp2 = ACCUM;	// another copy of acc
	byte actmp = 0;

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;				// increment to operand
		break;

		case 1:
			actmp = Rd80(ABR);	// get operand
			tmp &= (word) actmp;	// AND operand with temp ACC
			
			ACCUM = (byte) tmp;
			SetNCFlags ( tmp );		// set flags FJS
			FLAGS &= ~CY_FLAG;			// clear CY flag

			// Set AC to OR of bits 3
			if ( (tmp2 | actmp) & 0x08 ) // FJS
				FLAGS |= AC_FLAG;		// set AC flag
			else
				FLAGS &= ~AC_FLAG;		// clear AC flag
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	xri ( OP_ARG_U ) 
{
// Exclusive OR immediate
// XRI n:: A=Axn
// Flags:	SZAPC
//			**0*0

	register /* FJS */ word tmp = ACCUM;

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;			// increment to operand
		break;

		case 1:
			tmp ^= Rd80(ABR);
			ACCUM = (byte) tmp;
			SetNCFlags ( tmp ); // FJS
			FLAGS &= ~(CY_FLAG | AC_FLAG);	// clear CY flag FJS
			// FLAGS &= ~AC_FLAG;			// clear AC flag
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	ori ( OP_ARG_U ) 
{
// Inclusive OR immediate
// ORI n:: A=Avn
// Flags:	SZAPC
//			**0*0

	register /* FJS */ word tmp = ACCUM;

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;				// increment to operand
		break;

		case 1:
			tmp |= Rd80(ABR);
			ACCUM = (byte) tmp;
			SetNCFlags ( tmp ); // FJS
			FLAGS &= ~(CY_FLAG | AC_FLAG);	// clear CY flag FJS
			// FLAGS &= ~AC_FLAG;			// clear AC flag
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	cpi ( OP_ARG_U ) 
{
// Compare immediate
// CPI n:: A-n
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;
	byte actmp = 0;

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 1;
			// I80Regs.nPC.W++;				// increment to operand
		break;

		case 1:
			actmp = Rd80(ABR);
			SetACFlag ( (word)(ACCUM), (word)actmp, SUBTRACT );
			tmp -= actmp;
			SetFlags ( tmp );
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
		// default: /* no-op */ break;
	}
	ADVANCE_PC; // FJS
}

static void	rlc ( OP_ARG_U ) 
{
// Rotate Left Circular
// RLC:: A=A<-
// Flags:	SZAPC
//			----*

	register /* FJS */ word tmp = ACCUM;

	if ( ACCUM & 0x80 )
		FLAGS |= CY_FLAG;
	else
		FLAGS &= ~CY_FLAG;
	tmp <<= 1;
	tmp |= ( tmp >> 8 );
	ACCUM = (byte)tmp;
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	rar ( OP_ARG_U ) 
{
// Rotate ACC Right through Carry
// RAR:: A=->{CY,A}
// Flags:	SZAPC
//			----*

	register /* FJS */ word tmp = (ACCUM << 8);	// copy accumulator to short

	tmp >>= 1;			// rotate right, padding MSB with 0

	if ( FLAGS & CY_FLAG )		// if CY=1, then...
		tmp |= 0x8000;		// set MSB
	else					// else
		tmp &= ~0x8000;	// clear MSB

							// Update CY_FLAG
	if ( tmp & 0x0080 )		// if 1 rotated out of LSB, then...
		FLAGS |= CY_FLAG;		// set CY flag
	else					// else
		FLAGS &= ~CY_FLAG;		// clear carry

	ACCUM = (byte)( tmp >> 8 );	//save new acc
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	ral ( OP_ARG_U ) 
{
// Rotate ACC Left through Carry
// RAL:: A={CY,A)<-
// Flags:	SZAPC
//			----*

	register /* FJS */ word tmp = ACCUM;

	tmp <<= 1;
	tmp += ( ( FLAGS & CY_FLAG ) == CY_FLAG );
	FLAGS &= ~CY_FLAG;
	FLAGS |= tmp >> 8;
	ACCUM = (byte) tmp;
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	cma ( OP_ARG_U )
{
// Complement Accumulator
// CMA:: A=~A
// Flags:	SZAPC
//			-----

	ACCUM = ~ACCUM;
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	inr ( OP_ARG_U ) 
{
// Increment r or M
// INR r:: r=r+1
// INR M:: [HL]=[HL]+1
// Flags:	SZAPC
//			****-

	// byte save_carry = FLAGS & CY_FLAG; // inr must not change CY_FLAG
	// register /* FJS */ word tmp;


	// fast version -
	if ( ( CurInstruction & DST_MASK ) == MEMDST ) { // FJS
	// register byte actmp /* = 0 */;

		if (pfx_state == 1) { // FJS Z80
			I80Regs.nWZ.B.l = Rd80 (ADVANCE_PC);

			SAVE_PC
			PC_ABR = I80Regs.nIX.W + (signed char)I80Regs.nWZ.B.l;
		} else if (pfx_state == 2) {
			I80Regs.nWZ.B.l = Rd80 (ADVANCE_PC);

			SAVE_PC
			PC_ABR = I80Regs.nIY.W + (signed char)I80Regs.nWZ.B.l;
		} else {
			SAVE_PC
			PC_ABR = I80Regs.nHL.W;
		}

		I80Regs.nWZ.B.l = Rd80( ABR );
		I80Regs.CPUstatus &= ~WO; // FJS

		Wr80 ( ABR, (byte)(I80Regs.nWZ.W = ((word)I80Regs.nWZ.B.l + 1)) );
		RESTORE_PC
	} else
		I80Regs.nWZ.W = ++(*b_regs[ ((CurInstruction & DST_MASK) >> 3) + (pfx_state << 3) ]);

	SetNCFlags ( I80Regs.nWZ.W ); // FJS don't change carry
	SetACFlag ( (word)(I80Regs.nWZ.W - 1) , (word)1, ADD );
	// if ( ( FLAGS & CY_FLAG ) != save_carry ) FLAGS ^= CY_FLAG; // undo change

	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	dcr ( OP_ARG_U ) 
{
// Decrement r or M
// DCR r:: r=r-1
// DCR M:: [HL]=[HL]-1
// Flags:	SZAPC
//			****-

	// byte save_carry = FLAGS & CY_FLAG; // dcr must not change CY_FLAG
	// register /* FJS */ word tmp;


	// fast version -
	if ( ( CurInstruction & DST_MASK ) == MEMDST ) { // FJS
	// register byte actmp /* = 0 */;

		if (pfx_state == 1) { // FJS Z80
			I80Regs.nWZ.B.l = Rd80 (ADVANCE_PC);
			SAVE_PC

			PC_ABR = I80Regs.nIX.W + (signed char)I80Regs.nWZ.B.l;
		} else if (pfx_state == 2) {
			I80Regs.nWZ.B.l = Rd80 (ADVANCE_PC);
			SAVE_PC

			PC_ABR = I80Regs.nIY.W + (signed char)I80Regs.nWZ.B.l;
		} else {
			SAVE_PC
			PC_ABR = I80Regs.nHL.W;
		}

		I80Regs.nWZ.B.l = Rd80( ABR );
		I80Regs.CPUstatus &= ~WO; // FJS

		Wr80 ( ABR, (byte)(I80Regs.nWZ.W = ((word)I80Regs.nWZ.B.l - 1)) );
		RESTORE_PC
	} else
		I80Regs.nWZ.W = --(*b_regs[ ((CurInstruction & DST_MASK) >> 3) + (pfx_state << 3) ]);

	SetNCFlags ( I80Regs.nWZ.W ); // FJS don't change carry
	SetACFlag ( (word)(I80Regs.nWZ.W + 1), (word)1, SUBTRACT );
	// if ( ( FLAGS & CY_FLAG ) != save_carry ) FLAGS ^= CY_FLAG; // undo change

	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	rrc ( OP_ARG_U ) 
{
// Rotate Right Circular
// RRC:: A=->A
// Flags:	SZAPC
//			----*

	register /* FJS */ word tmp = ACCUM;

	if ( tmp & 0x01 ) // FJS
		FLAGS |= CY_FLAG;
	else
		FLAGS &= ~CY_FLAG;
	tmp <<= 7;
	tmp  |= ( tmp >> 8 );
	ACCUM = (byte)tmp;
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	daa ( OP_ARG_U ) 
{
// Decimal Adjust Accumulator
// DAA:: A=BCD format
// Flags:	SZAPC
//			*****

	register /* FJS */ word tmp = ACCUM;

	if ( (( tmp & 0x0f ) > 0x09 ) || ( FLAGS & AC_FLAG ))
		tmp += 0x06;

	if (tmp > 0x0f)
		FLAGS |= AC_FLAG;		// if adjusted LSB > 0xf, set AC
	else
		FLAGS &= ~AC_FLAG;		// else clear SC	

	if ( ((tmp & 0x0f0) > 0x090 ) || ( FLAGS & CY_FLAG )) // FJS
		tmp += 0x060;

	ACCUM = (byte)tmp;
	SetFlags(tmp);			// picks-up CY_FLAG changes
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	mvm ( OP_ARG_U ) { // FJS
// MOV r,M::   r=[HL/IX+d/IY+d]
// Flags:	SZAPC
//			-----

	// fast version -
	if (pfx_state == 1)
		I80Regs.nWZ.W = I80Regs.nIX.W + (signed char)Rd80 (ADVANCE_PC);

	else if (pfx_state == 2)
		I80Regs.nWZ.W = I80Regs.nIY.W + (signed char)Rd80 (ADVANCE_PC);

	else
		I80Regs.nWZ.W = I80Regs.nHL.W;
	SAVE_PC
	PC_ABR = I80Regs.nWZ.W;
	I80Regs.CPUstatus &= ~WO;

	Wr80(ABR, *b_regs[ (CurInstruction & SRC_MASK) + (pfx_state << 3) ] );
	RESTORE_PC
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	mvr ( OP_ARG_U ) { // FJS
// MOV M,r::   [HL/IX+d/IY+d]=r
// Flags:	SZAPC
//			-----


	// fast version -
	if (pfx_state == 1)
		I80Regs.nWZ.W = I80Regs.nIX.W + (signed char)Rd80 (ADVANCE_PC);

	else if (pfx_state == 2)
		I80Regs.nWZ.W = I80Regs.nIY.W + (signed char)Rd80 (ADVANCE_PC);

	else
		I80Regs.nWZ.W = I80Regs.nHL.W;
	SAVE_PC
	PC_ABR = I80Regs.nWZ.W;

	*b_regs[ ((CurInstruction & DST_MASK) >> 3) + (pfx_state << 3) ] = Rd80(ABR);
	RESTORE_PC
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	mov ( OP_ARG_U )
{
// Move register, immediate, memory
// MOV r1,r2:: r1=r2
// MOV M,r::   [HL]=r
// MOV r,M::   r=[HL]
// Flags:	SZAPC
//			-----

	// if ( ( CurInstruction & DST_MASK ) == MEMDST ) {	// move to memory I80Regs.nHL.W is HL
		// I80Regs.CPUstatus &= ~WO;
		// Wr80(ABR = I80Regs.nHL.W, *b_regs[ CurInstruction & SRC_MASK ] );
	// }
	// else if ( ( CurInstruction & SRC_MASK ) == MEMSRC ) {	// move from memory
		// *b_regs[ ( CurInstruction & DST_MASK ) >> 3 ] = Rd80(ABR = I80Regs.nHL.W);
	// }
	// else {	// register-to-register move
		*b_regs[ ((CurInstruction & DST_MASK) >> 3) + (pfx_state << 3) ] =
			*b_regs[ (CurInstruction & SRC_MASK) + (pfx_state << 3) ];
	// }
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	lxi ( OP_ARG_U ) 
{
// Load Immediate rp BC/DE/HL/SP
// LXI B,nn::  BC=nn
// LXI D,nn::  DE=nn
// LXI H,nn::  HL=nn
// LXI SP,nn:: SP=nn
// Flags:	SZAPC
//			-----

/* NEW CODE BY FJS */

	// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 2;
			// ADVANCE_PC;		// inc to operand lo
			break;

		case 2:
			I80Regs.nWZ.B.l = Rd80(ABR);
			// ADVANCE_PC;		// inc to operand hi
			break;

		default: // case 1:
			I80Regs.nWZ.B.h = Rd80(ABR);
			switch ( /* pair */ (CurInstruction & RP_MASK) >> 4) {
				case 0:	// BC
					I80Regs.nBC.W = I80Regs.nWZ.W;
				break;

				case 1:	// DE
					I80Regs.nDE.W = I80Regs.nWZ.W;
				break;

				case 2:	// HL
					if (pfx_state == 1) // FJS Z80
						I80Regs.nIX.W = I80Regs.nWZ.W;
					else if (pfx_state == 2)
						I80Regs.nIY.W = I80Regs.nWZ.W;
					else
						I80Regs.nHL.W = I80Regs.nWZ.W;
				break;
				
				default: // case 3:	// SP
					I80Regs.nSP.W = I80Regs.nWZ.W;
				// break;
			}
			pfx_state = 0; // FJS
			// ADVANCE_PC;		// inc past operand2
		// break;
	}
	ADVANCE_PC;		// inc past operand2
	// return;

/* OLD CODE
	static word tmp;			// initial PC
	static word usValue;		// operand value

	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 2;
			tmp = I80Regs.nPC.W;
			usValue = (Rd80((word)(tmp+2)) << 8) + (Rd80((word)(tmp+1)));
			I80Regs.nPC.W++;		// inc to operand1
			break;

		case 2:
			I80Regs.nPC.W++;		// inc to operand2
			break;

		case 1:
			switch ((u & RP_MASK) >> 4){
				case 0:	//BC
					I80Regs.nBC.W = usValue;
					break;

				case 1:	//DE
					I80Regs.nDE.W = usValue;
					break;

				case 2:	//HL
					I80Regs.nHL.W = usValue;
					break;
				
				case 3:	//SP
					I80Regs.nSP.W = usValue;
					break;
			}
			I80Regs.nPC.W++;
			break;
	}
 END OF OLD CODE */
}

static void	stc ( OP_ARG_U ) 
{
// Set Carry Flag
// Flags:	SZAPC
//			----1

	FLAGS |= CY_FLAG;
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	cmc ( OP_ARG_U )
{
// Complement Carry
// Flags:	SZAPC
//			----*

	FLAGS ^= CY_FLAG;
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	inx ( OP_ARG_U ) 
{
// Increment register
// INX B:: BC=BC+1
// INX D:: DE=DE+1
// INX H:: HL=HL+1
// INX SP:: SP=SP+1
// Flags:	SZAPC
//			-----

	// cycle version -
	switch ((CurInstruction & RP_MASK) >> 4) {
		// unsigned int tmp;

		case 0:	// BC
			I80Regs.nBC.W++;
		break;

		case 1:	// DE
			I80Regs.nDE.W++;
		break;

		case 2:	// HL
			if (pfx_state == 1) // FJS Z80...
				I80Regs.nIX.W++;
			else if (pfx_state == 2)
				I80Regs.nIY.W++;
			else
				I80Regs.nHL.W++;
		break;
		/* FJS - */
		default: // case 3: // SP
			I80Regs.nSP.W++;
//		break;
	}
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	dcx ( OP_ARG_U ) 
{
// Decrement register
// DCX B:: BC=BC-1
// DCX D:: DE=DE-1
// DCX H:: HL=HL-1
// DCX SP:: SP=SP-1
// Flags:	SZAPC
//			-----

	// cycle version -
	switch ((CurInstruction & RP_MASK) >> 4){
		//unsigned int tmp;

		case 0:	// BC
			I80Regs.nBC.W--;
		break;

		case 1:	// DE
			I80Regs.nDE.W--;
		break;

		case 2:	// HL
			if (pfx_state == 1) // FJS Z80...
				I80Regs.nIX.W--;
			else if (pfx_state == 2)
				I80Regs.nIY.W--;
			else
				I80Regs.nHL.W--;
		break;
/* FJS */
		default: // case 3: // SP
			I80Regs.nSP.W--;
//			break;
	}
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	dad ( OP_ARG_U ) 
{
// Double add rp to HL
// DAD B:: HL=HL+BC
// DAD D:: HL=HL+DE
// DAD H:: HL=HL+HL
// DAD SP:: HL=HL+SP
// Flags:	SZAPC
//			----*

	long tmp /* = I80Regs.nHL.W */;	

	if (pfx_state == 1) // FJS Z80...
		tmp = I80Regs.nIX.W;	
	else if (pfx_state == 2)
		tmp = I80Regs.nIY.W;	
	else
		tmp = I80Regs.nHL.W;	

	switch ((CurInstruction & RP_MASK) >> 4){
		case 0:	// BC
			tmp += I80Regs.nBC.W;
		break;

		case 1:	// DE
			tmp += I80Regs.nDE.W;
		break;

		case 2:	// HL
			tmp <<= 1;
		break;
/* FJS */
		default: // case 3:	// SP
			tmp += I80Regs.nSP.W;
//		break;
	}

	if (pfx_state == 1) // FJS Z80...
		I80Regs.nIX.W = (word) tmp;	// save to IX
	else if (pfx_state == 2)
		I80Regs.nIY.W = (word) tmp;	// save to IY
	else
		I80Regs.nHL.W = (word) tmp;	// save to HL

	if ( tmp & ~0x0ffffUL ) // FJS
		FLAGS |= CY_FLAG;
	else
		FLAGS &= ~CY_FLAG;

	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	sphl ( OP_ARG_U ) 
{
// Move HL to SP
// SPHL:: SP=HL
// Flags:	SZAPC
//			-----

	I80Regs.CPUstatus |= STACK;
	if (pfx_state == 1) // FJS Z80...
		I80Regs.nSP.W = I80Regs.nIX.W;
	else if (pfx_state == 2)
		I80Regs.nSP.W = I80Regs.nIY.W;
	else
		I80Regs.nSP.W = I80Regs.nHL.W;
	pfx_state = 0; // FJS
	ADVANCE_PC; // FJS
}

static void	pchl ( OP_ARG_U ) 
{
// Jump to HL indirect
// PCHL:: PC=[HL]
// Flags:	SZAPC
//			-----

	if (pfx_state == 1) // FJS Z80...
		ABR = I80Regs.nIX.W;
	else if (pfx_state == 2)
		ABR = I80Regs.nIY.W;
	else
		ABR = I80Regs.nHL.W;

	pfx_state = 0; // FJS
	I80Regs.nPC.W = ABR;

}

static void	rst ( OP_ARG_U ) 
{
// Restart
// RST z::  PC = [z * 8]
// Flags:	SZAPC
//			-----

	/* Should simulate a CALL because return address is pushed onto
	 * the stack and the RST call ends with RET which is documented
	 * as RTI).  Single-byte opcode, so use tmp+1 to arrive at return
	 * address.
	 */

	// fast version -
	// simulate PUSH of return address (beginning of instruction following RST)
	I80Regs.nWZ.W = ++I80Regs.nPC.W; // ret addr
	// FJS commented out AND in second Wr80
	I80Regs.CPUstatus |= STACK;
	I80Regs.CPUstatus &= ~WO;
	
	Wr80 ( PC_ABR = --I80Regs.nSP.W , I80Regs.nWZ.B.h); // push hi ret addr

	Wr80 ( PC_ABR = --I80Regs.nSP.W , I80Regs.nWZ.B.l); // push lo ret addr

	// Alternative...
	// Wr80 ( --I80Regs.nSP.W , (byte)I80Regs.nPC.B.h);	// push high-order ret addr
	// Wr80 ( --I80Regs.nSP.W , (byte)I80Regs.nPC.B.l);	// push low-order ret addr

	pfx_state = 0; // FJS
	// set new PC to RST address * 8
	ABR = I80Regs.nPC.W = (word) ( CurInstruction & 070 );	// 0x38 Address is actually in these bits of the opcode
}

static void	jmp ( OP_ARG_U ) 
{
// Jump unconditional
// JMP a:: PC=a
// Flags:	SZAPC
//			-----

/* NEW CODE FJS */ // cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:								// opcode
			I80Regs.CPUcycle = 2;
		break;

		case 2:								// lo-byte of immedeate word
			I80Regs.nWZ.B.l = Rd80(ABR);
		break;

		default: // case 1:					// hi-byte of immedeate word
			I80Regs.nWZ.B.h = Rd80(ABR);
			ABR = I80Regs.nPC.W = I80Regs.nWZ.W;
			pfx_state = 0; // FJS
		return; // FJS don't advance PC/ABR
	}
	ADVANCE_PC; // FJS
	// return;

/* OLD CODE
	static word tmp;

	switch ( I80Regs.CPUcycle ) {
		case 0:		// opcode
			I80Regs.CPUcycle = 2;
			tmp = I80Regs.nPC.W;
			I80Regs.nPC.W++;
			break;

		case 2:
			I80Regs.nPC.W++;
			break;

		case 1:
			I80Regs.nPC.W = (word)Rd80((word)(tmp+1));
			I80Regs.nPC.W += (word)(Rd80((word)(tmp+2)) << 8);
			break;
	}
 END OF OLD CODE */
}

static void	jc ( OP_ARG_U ) 
{
// Jump on Carry
// JC a:: if CY=1; PC=a
// Flags:	SZAPC
//			-----

	if ( FLAGS & CY_FLAG )
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle /* == 0 */ ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	jz ( OP_ARG_U ) 
{
// Jump on Zero
// JZ a:: if Z=1; PC=a
// Flags:	SZAPC
//			-----

	if ( FLAGS & Z_FLAG )
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	jp ( OP_ARG_U ) 
{
// Jump on Plus
// JP a:: if S=0; PC=a
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & S_FLAG ))
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	jpe ( OP_ARG_U ) 
{
// Jump on Parity Even
// JPE a:: if P=1; PC=a
// Flags:	SZAPC
//			-----

	if ( FLAGS & P_FLAG )
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	jnc ( OP_ARG_U ) 
{
// Jump on No Carry
// JNC a:: if CY=0; PC=a
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & CY_FLAG ))
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	jnz ( OP_ARG_U ) 
{
// Jump on Not Zero
// JNZ a:: if Z=0; PC=a
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & Z_FLAG ))
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	jm ( OP_ARG_U ) 
{
// Jump on Minus
// JM a:: if S=1; PC=a
// Flags:	SZAPC
//			-----

	if ( FLAGS & S_FLAG )
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	jpo ( OP_ARG_U ) 
{
// Jump on Parity Odd
// JPO a:: if P=0; PC=a
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & P_FLAG ))
		jmp ( U_ARG );
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	call ( OP_ARG_U )
{
// Call unconditional
// CALL a:: -[SP]=PC+3; PC=a
// Flags:	SZAPC
//			-----

/* FJS */
	// word v, subr = I80Regs.nPC.W;		// address of subroutine
	// register /* FJS */ word tmp;

	// fast version -

	ADVANCE_PC; // finish fetch #0;  I80Regs.CPUcycle = 4

	I80Regs.nWZ.B.l = Rd80(ABR = I80Regs.nPC.W++);	// get loword #4

	I80Regs.nWZ.B.h = Rd80(ABR = I80Regs.nPC.W++);	// get hiword #3
	// Push address of return location (the beginning of next instruction)
	I80Regs.CPUstatus |= STACK;
	I80Regs.CPUstatus &= ~WO;
	SAVE_PC
	PC_ABR = --I80Regs.nSP.W;

	Wr80 ( ABR, (byte)(save_PC >> 8));			// push hi ret addr #2
	PC_ABR = --I80Regs.nSP.W;

	Wr80 ( ABR, (byte)(save_PC /* & 0xff */ ));	// push lo ret addr #1
	ABR = I80Regs.nPC.W = I80Regs.nWZ.W;	// Jump to the target location
	pfx_state = 0; // FJS


/*	ORIGINAL
	word tmp = I80Regs.nPC.W;

	I80Regs.CPUstatus |= STACK;

	// Push address of return location (the beginning of next instruction)
	Wr80 ( --I80Regs.nSP.W , (byte)(( I80Regs.nPC.W + 3 ) >> 8 ));	// push high-order ret addr
	Wr80 ( --I80Regs.nSP.W , (byte)(( I80Regs.nPC.W + 3 ) & 0xff ));	// push low-order ret addr

	// Jump to the target location
	I80Regs.nPC.W = Rd80((word)(tmp+1));	// get loword
	I80Regs.nPC.W += (Rd80((word)(tmp+2)) << 8);	// get hiword
*/
}

static void	cc ( OP_ARG_U )
{
// Call on Carry
// CC a:: if CY=1
// Flags:	SZAPC
//			-----

	if ( FLAGS & CY_FLAG ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	cz ( OP_ARG_U )
{
// Call on Zero
// CZ a:: if Z=1
// Flags:	SZAPC
//			-----

	if ( FLAGS & Z_FLAG ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	cp ( OP_ARG_U ) 
{
// Call on Plus
// CP a:: if S=0
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & S_FLAG) ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	cpe ( OP_ARG_U ) 
{
// Call on Parity Even
// CPE a:: if P=1
// Flags:	SZAPC
//			-----

	if ( FLAGS & P_FLAG ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	cnc ( OP_ARG_U ) 
{
// Call on No Carry
// CNC a:: if CY=0
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & CY_FLAG) ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	cnz ( OP_ARG_U ) 
{
// Call on Not Zero
// CNZ a:: if Z=0 
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & Z_FLAG) ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	cm ( OP_ARG_U )
{
// Call on Minus
// CM a:: if S=1
// Flags:	SZAPC
//			-----

	if ( FLAGS & S_FLAG ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	cpo ( OP_ARG_U ) 
{
// Call on Parity Odd
// CPO a:: if P=0
// Flags:	SZAPC
//			-----

	if ( !(FLAGS & P_FLAG) ){  // we make the call
		call ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else {
		if ( !I80Regs.CPUcycle ) {
			I80Regs.CPUcycle = 2;
			// I80Regs.nPC.W++;
		}
		// else
			// I80Regs.nPC.W++;
		pfx_state = 0; // FJS
		ADVANCE_PC; // FJS
	}
}

static void	rc ( OP_ARG_U ) 
{
// Return on Carry
// Flags:	SZAPC
//			-----

	if ( FLAGS & CY_FLAG ){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	rz ( OP_ARG_U ) 
{
// Return on Zero
// Flags:	SZAPC
//			-----

	if ( FLAGS & Z_FLAG ){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	rp ( OP_ARG_U ) 
{
// Return on Plus
// Flags:	SZAPC
//			-----

	if ( !( FLAGS & S_FLAG )){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	rpe ( OP_ARG_U ) 
{
// Return on Parity Even
// Flags:	SZAPC
//			-----

	if ( FLAGS & P_FLAG ){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	rnc ( OP_ARG_U ) 
{
// Return on No Carry
// Flags:	SZAPC
//			-----

	if ( !( FLAGS & CY_FLAG )){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	rnz ( OP_ARG_U ) 
{
// Return on No Zero
// Flags:	SZAPC
//			-----

	if ( !( FLAGS & Z_FLAG )){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	rm ( OP_ARG_U ) 
{
// Return on Minus
// Flags:	SZAPC
//			-----

	if ( FLAGS & S_FLAG ){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	rpo ( OP_ARG_U ) 
{
// Return on Parity Odd
// Flags:	SZAPC
//			-----

	if ( !( FLAGS & P_FLAG )){
		ret ( U_ARG );
		I80Regs.ICount -= 6;
	}
	else
		ADVANCE_PC; // FJS
	pfx_state = 0; // FJS
}

static void	in ( OP_ARG_U ) 
{
// Input from port
// IN p:: A=[p]
// Flags:	SZAPC
//			-----
// Sense switches are handled by a special device handler
// like any other device.

/* FJS */
#if 0
// fast version -
	ADVANCE_PC; // FJS cycle 0

	PC_ABR = Rd80(HOLD_PC ABR); // cycle 2, port #
	I80Regs.CPUstatus |= INP;

	ACCUM = In80(ABR); // FJS do input cycle 1
	RESTORE_PC // FJS restore PC for advance
#else
// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 2; // FJS just set count & advance PC
//			I80Regs.nPC.W++;
		break;

		default: /* case 2 */ // FJS get port addr & set-up for input
			PC_ABR = Rd80(HOLD_PC ABR);
			I80Regs.CPUstatus |= INP;
		return; // FJS don't advance PC yet (used for input ABR...)

		case 1:
			ACCUM = In80(ABR); // FJS do input
			pfx_state = 0; // FJS
			RESTORE_PC // FJS restore PC for advance
		// break;
	}
#endif
	ADVANCE_PC; // FJS
}

static void	out ( OP_ARG_U ) 
{
// Output to port
// OUT p:: p=[A]
// Flags:	SZAPC
//			-----

/* FJS */
#if 0
// fast version -
	ADVANCE_PC;

	PC_ABR = Rd80(HOLD_PC ABR);
	I80Regs.CPUstatus |= pOUT;	// set processor status

	Out80( ABR, ACCUM );
	RESTORE_PC
#else
// cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 2; // FJS
//			I80Regs.nPC.W++;
		break;
		
		default: /* case 2 */ // FJS
			PC_ABR = Rd80(HOLD_PC ABR);
			I80Regs.CPUstatus |= pOUT;	// set processor status
		return; // don't advance PC yet (used for output ABR...)

		case 1:
			Out80( ABR, ACCUM );
			RESTORE_PC
			pfx_state = 0; // FJS
//			I80Regs.nPC.W++;
//		break;
	}
#endif
	ADVANCE_PC;
}

static void	ldax ( OP_ARG_U ) 
{
// Load accumulator indirect [BC]/[DE]
// LDAX B:: A=[BC]
// LDAX D:: A=[DE]
// Flags:	SZAPC
//			-----

/* FJS */ // fast version -
	SAVE_PC
	if ( ( CurInstruction & 0x10 ) /* >> 4 */ )
		PC_ABR = I80Regs.nDE.W;
	else
		PC_ABR = I80Regs.nBC.W;

	ACCUM = Rd80(ABR);
	RESTORE_PC
	pfx_state = 0; // FJS
	ADVANCE_PC;

/*
	static word PCT;

	switch (I80Regs.CPUcycle){
		case 0:
			I80Regs.CPUcycle = 1;
			PCT = I80Regs.nPC.W;

			if ( ( u & 0x10 ) >> 4 )
				I80Regs.nPC.W = I80Regs.nDE.W;
			else
				I80Regs.nPC.W = I80Regs.nBC.W;
			return;

		case 1:
			ACCUM = Rd80(I80Regs.nPC.W);
			I80Regs.nPC.W = ++PCT;
	}
*/
}


static void	stax ( OP_ARG_U ) 
{
// Store accumulator indirect
// STAX B:: [BC]=A
// STAX D:: [DE]=A
// Flags:	SZAPC
//			-----

/* FJS */ // fast version -
	SAVE_PC // FJS
	if ( ( CurInstruction & 0x10 ) /* >> 4 */ )
		PC_ABR = I80Regs.nDE.W;
	else
		PC_ABR = I80Regs.nBC.W;
	I80Regs.CPUstatus &= ~WO; // FJS

	Wr80 ( ABR, ACCUM );
	RESTORE_PC
	pfx_state = 0; // FJS
	ADVANCE_PC;

/*
	static word PCT;

	switch (I80Regs.CPUcycle){
		case 0:
			I80Regs.CPUcycle = 1;
			PCT = I80Regs.nPC.W;

			if ( ( u & 0x10 ) >> 4 )
				I80Regs.nPC.W = I80Regs.nDE.W;
			else
				I80Regs.nPC.W = I80Regs.nBC.W;

			Wr80 (I80Regs.nPC.W, ACCUM);	// should be in cycle 1??
			return;

		case 1:
			I80Regs.nPC.W = ++PCT;
	}
*/
}

static void	lda ( OP_ARG_U ) 
{
// Load accumulator direct
// LDA a:: A=[a]
// Flags:	SZAPC
//			-----

	// static word tmp;

/* FJS */ // cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 3; // FJS
			// tmp = I80Regs.nPC.W++;
		break;

		default: // case 3:
			I80Regs.nWZ.B.l = Rd80(ABR);
			// I80Regs.nPC.W++;
		break;
			
		case 2:
			I80Regs.nWZ.B.h = Rd80(ABR);
			SAVE_PC
			PC_ABR = I80Regs.nWZ.W;
		return; // don't advance PC/ABR

		case 1:
			ACCUM = Rd80( ABR );
			RESTORE_PC
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
	}
	ADVANCE_PC;
}

static void	sta ( OP_ARG_U ) 
{
// Store accumulator direct
// STA a:: [a]=A
// Flags:	SZAPC
//			-----

	// static word tmp;

/* FJS */ // cycle version -
	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 3;	// FJS
			// tmp = I80Regs.nPC.W++;
		break;

		case 3:
			I80Regs.nWZ.B.l = Rd80(ABR);
			// I80Regs.nPC.W++;
		break;

		case 2:
			I80Regs.nWZ.B.h = Rd80(ABR);
			SAVE_PC
			PC_ABR = I80Regs.nWZ.W;
			I80Regs.CPUstatus &= ~WO; // FJS
		return; // don't advance PC/ABR

		case 1:
			Wr80( ABR, ACCUM );
			RESTORE_PC
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
	}
	ADVANCE_PC;
}

static void	mim ( OP_ARG_U ) // FJS memory only
{
// Move immediate (memory)
// MVI M,n:: [HL/IX+d/IY+d]=n
// Flags:	SZAPC
//			-----

	// cycle version -
	
	switch ( I80Regs.CPUcycle ) {
		case 0:
			if (!(pfx_state & 3)) { // 8080 HL
				I80Regs.CPUcycle = 2;
				I80Regs.nWZ.W = I80Regs.nHL.W;
			} else
				I80Regs.CPUcycle = 3; // Z80 IX+d/IY+d
			// I80Regs.nPC.W++;
		break;

		case 3: // memory address Z80 IX+d/IY+d
			if (pfx_state == 1) // FJS Z80
				I80Regs.nWZ.W = I80Regs.nIX.W + (signed char)Rd80(ABR);
			else
				I80Regs.nWZ.W = I80Regs.nIY.W + (signed char)Rd80(ABR);
			// I80Regs.nPC.W++;
		break;

		case 2: // immeadeate data
			{	register byte tmp; // FJS

				tmp = Rd80(HOLD_PC ABR);
				PC_ABR = I80Regs.nWZ.W;
				I80Regs.nWZ.B.l = tmp;
			}
			I80Regs.CPUstatus &= ~WO; // FJS
		return; // don't advance PC

		default: // case 1:
			Wr80(ABR, I80Regs.nWZ.B.l);
			RESTORE_PC
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
	}
	ADVANCE_PC;
}

static void	mvi ( OP_ARG_U ) // FJS register only (no memory dest.)
{
// Move immediate (register)
// MVI r,n:: r=n
// Flags:	SZAPC
//			-----

	// cycle version
	
	if ( I80Regs.CPUcycle == 0 )
		I80Regs.CPUcycle = 1;
	else {
		*b_regs[ ((CurInstruction & DST_MASK) >> 3) + (pfx_state << 3) ] = Rd80(ABR);
		pfx_state = 0; // FJS
	}

	ADVANCE_PC;
}

static void	hlt ( OP_ARG_U ) 
{
// Halt processor
// Flags:	SZAPC
//			-----

	I80Regs.CPUstatus |= HLTA;
	simstate.bPrun = RUN_OFF;
	simstate.runstate = HALTED;
	pfx_state = 0;
}

static void	ei ( OP_ARG_U ) 
{
// Enable interrupts
// Flags:	SZAPC
//			-----

	I80Regs.IFF = 3; // FJS was 1; changed for delay
	pfx_state = 0; // FJS
	ADVANCE_PC;
}

static void	di ( OP_ARG_U ) 
{
// Disable interrupts
// Flags:	SZAPC
//			-----

	I80Regs.IFF = // 0;
	pfx_state = 0; // FJS
	ADVANCE_PC;
}

static void	push ( OP_ARG_U ) 
{
// Push register pair on stack
// PUSH B:: -[SP]=BC
// PUSH D:: -[SP]=DE
// PUSH H:: -[SP]=HL
// PUSH PSW:: -[SP]={PSW,A}
// Flags:	SZAPC
//			-----

	// fast version -

	I80Regs.CPUstatus |= STACK;
	I80Regs.CPUstatus &= ~WO;

	// push HIWORD (B/D/H/A) first, then LOWORD (C/E/L/F).

	// if ( ( CurInstruction & RP_MASK ) == 0x30 ) {
		// push ACC first, then Flags
		// Wr80 ( ABR = --I80Regs.nSP.W, ACCUM);	// A
		// Wr80 ( ABR = --I80Regs.nSP.W, FLAGS);	// F
	// }
	// else {
	SAVE_PC
	PC_ABR = --I80Regs.nSP.W;
	switch (CurInstruction & RP_MASK) { // FJS
		case 0x00:	// BC
			Wr80 ( ABR, I80Regs.nBC.B.h); // B
			PC_ABR = --I80Regs.nSP.W;

			Wr80 ( ABR, I80Regs.nBC.B.l); // C
		break;

		case 0x10:	// DE
			Wr80 ( ABR, I80Regs.nDE.B.h);	// D
			PC_ABR = --I80Regs.nSP.W;

			Wr80 ( ABR, I80Regs.nDE.B.l);	// E
		break;

		case 0x20:	// HL/IX/IY
			if (pfx_state == 1) { // FJS Z80
				Wr80 ( ABR, I80Regs.nIX.B.h);
				PC_ABR = --I80Regs.nSP.W;

				Wr80 ( ABR, I80Regs.nIX.B.l);
			} else if (pfx_state == 2) {
				Wr80 ( ABR, I80Regs.nIY.B.h);
				PC_ABR = --I80Regs.nSP.W;

				Wr80 ( ABR, I80Regs.nIY.B.l);
			} else {
				Wr80 ( ABR, I80Regs.nHL.B.h);	// H
				PC_ABR = --I80Regs.nSP.W;

				Wr80 ( ABR, I80Regs.nHL.B.l);	// L
			}
		break;

		default: // case 0x30: // AF // FJS
			// push ACC first, then Flags
			Wr80 ( ABR, ACCUM);	// A
			PC_ABR = --I80Regs.nSP.W;

			Wr80 ( ABR, FLAGS);	// F
		// break;
	}
	RESTORE_PC
	// }
	pfx_state = 0; // FJS
	ADVANCE_PC;
}

static void	pop ( OP_ARG_U ) 
{
// Pop register pair from stack
// POP B:: BC=[SP]+
// POP D:: DE=[SP]+
// POP H:: HL=[SP]+
// POP PSW:: {PSW,A}=[SP]+
// Flags:	SZAPC
//			-----

// POP in reverse order of PUSH. POP LOWORD first, then HIWORD.

	// fast version -

	I80Regs.CPUstatus |= STACK;

	// if ( ( CurInstruction & RP_MASK ) == 0x30 ) {
		// FLAGS = Rd80(ABR = I80Regs.nSP.W++);	// F
		// ACCUM = Rd80(ABR = I80Regs.nSP.W++);	// A
	// }
	// else {
	SAVE_PC
	PC_ABR = I80Regs.nSP.W++;
	switch (CurInstruction & RP_MASK) { // FJS
		case 0x00:	// BC
			I80Regs.nBC.B.l = Rd80(ABR);	// C
			PC_ABR = I80Regs.nSP.W++;

			I80Regs.nBC.B.h = Rd80(ABR);	// B
		break;

		case 0x010:	// DE
			I80Regs.nDE.B.l = Rd80(ABR);	// E
			PC_ABR = I80Regs.nSP.W++;

			I80Regs.nDE.B.h = Rd80(ABR);	// D
		break;

		case 0x020:	// HL // TBD Z80 IX/IY
			if (pfx_state == 1) {
				I80Regs.nIX.B.l = Rd80(ABR);
				PC_ABR = I80Regs.nSP.W++;

				I80Regs.nIX.B.h = Rd80(ABR);
			} else if (pfx_state == 1) {
				I80Regs.nIY.B.l = Rd80(ABR);
				PC_ABR = I80Regs.nSP.W++;

				I80Regs.nIY.B.h = Rd80(ABR);
			} else {
				I80Regs.nHL.B.l = Rd80(ABR);	// L
				PC_ABR = I80Regs.nSP.W++;

				I80Regs.nHL.B.h = Rd80(ABR);	// H
			}
		break;

		default: // case 0x030: // AF // FJS
			FLAGS = Rd80(ABR);	// F
			PC_ABR = I80Regs.nSP.W++;

			ACCUM = Rd80(ABR);	// A
		// break;
	}
	RESTORE_PC
	// }
	pfx_state = 0; // FJS
	ADVANCE_PC;
}

static void	shld ( OP_ARG_U ) 
{
// Store HL direct
// SHLD a:: [a]=HL; a=16-bit address
// Flags:	SZAPC
//			-----

	// static word tmp;
	// register /* FJS */ word usDestAddr;	// address to save HL at

#if 0
	// cycle version -

	switch ( I80Regs.CPUcycle ) {
		case 0:								// fetch
			I80Regs.CPUcycle = 4;			// added cycle
			// I80Regs.nPC.W++;			// original code
		break;
		
		case 4:
			I80Regs.nWZ.B.l = Rd80(ABR);	// lo byte of dest.
			// I80Regs.nPC.W++;
		break;

		case 3:
			I80Regs.nWZ.B.h = Rd80(ABR);	// hi byte of dest.
			SAVE_PC
			PC_ABR = I80Regs.nWZ.W++;
			I80Regs.CPUstatus &= ~WO;
		return; // don't advance PC/ABR

		case 2:
			if (pfx_state == 1) // FJS Z80
				Wr80 (ABR, I80Regs.nIX.B.l );
			else if (pfx_state == 2)
				Wr80 (ABR, I80Regs.nIY.B.l );
			else
				Wr80 (ABR, I80Regs.nHL.B.l ); // save L at this address
			PC_ABR = I80Regs.nWZ.W;
		return; // don't advance PC/ABR

		default: // case 1:
			if (pfx_state == 1) { // FJS Z80
				Wr80 (ABR, I80Regs.nIX.B.h );
			} else if (pfx_state == 2) {
				Wr80 (ABR, I80Regs.nIY.B.h );
			} else {
				Wr80 (ABR, I80Regs.nHL.B.h ); // save H at address+1
			}
			RESTORE_PC
			pfx_state = 0; // FJS
  			// I80Regs.nPC.W++;			// increment PC
		// break;
	}
#else
	// fast version -

	ADVANCE_PC;
		
	I80Regs.nWZ.B.l = Rd80(ABR);	// lo byte of dest.
	ADVANCE_PC;

	I80Regs.nWZ.B.h = Rd80(ABR);	// hi byte of dest.
	SAVE_PC
	PC_ABR = I80Regs.nWZ.W++;
	I80Regs.CPUstatus &= ~WO;

	if (pfx_state == 1) // FJS Z80
		Wr80 (ABR, I80Regs.nIX.B.l );
	else if (pfx_state == 2)
		Wr80 (ABR, I80Regs.nIY.B.l );
	else
		Wr80 (ABR, I80Regs.nHL.B.l ); // save L at this address
	PC_ABR = I80Regs.nWZ.W;

	if (pfx_state == 1) { // FJS Z80
		Wr80 (ABR, I80Regs.nIX.B.h );
	} else if (pfx_state == 2) {
		Wr80 (ABR, I80Regs.nIY.B.h );
	} else {
		Wr80 (ABR, I80Regs.nHL.B.h ); // save H at address+1
	}
	RESTORE_PC
	pfx_state = 0; // FJS
  	// I80Regs.nPC.W++;			// increment PC
#endif
	ADVANCE_PC;
}

static void	lhld ( OP_ARG_U ) 
{
// Load HL Direct
// LHLD a:: HL=[a]; a=16-bit address
// Flags:	SZAPC
//			-----

	// static word usDestAddr;	// "new" PC
	// static word tmp;			// original PC
	// word usIndirectAddr;		// address to load from

#if 0
	// cycle version -

	switch ( I80Regs.CPUcycle ) {
		case 0:
			I80Regs.CPUcycle = 4;
			// I80Regs.nPC.W++;
		break;
			
		case 4:
			I80Regs.nWZ.B.l = Rd80(ABR); // lo byte of addr
			// I80Regs.nPC.W++;
		break;
			
		case 3:
			I80Regs.nWZ.B.h = Rd80(ABR); // hi byte of addr
			SAVE_PC
			PC_ABR = I80Regs.nWZ.W++;
		return; // don't advance PC/ABR

		case 2:
			if (pfx_state == 1) // FJS Z80
				I80Regs.nIX.B.l = Rd80(ABR);
			else if (pfx_state == 2)
				I80Regs.nIY.B.l = Rd80(ABR);
			else
				I80Regs.nHL.B.l = Rd80(ABR); // lo byte of source
			PC_ABR = I80Regs.nWZ.W;
		return; // don't advance PC/ABR

		default: // case 1:
			if (pfx_state == 1) // FJS Z80
				I80Regs.nIX.B.h = Rd80(ABR);
			else if (pfx_state == 2)
				I80Regs.nIY.B.h = Rd80(ABR);
			else
				I80Regs.nHL.B.h = Rd80(ABR);	// hi byte of source
			RESTORE_PC
			pfx_state = 0; // FJS
			// I80Regs.nPC.W++;
		// break;
	}
#else
	// fast version -

	ADVANCE_PC;

	I80Regs.nWZ.B.l = Rd80(ABR); // lo byte of addr
	ADVANCE_PC;
			
	I80Regs.nWZ.B.h = Rd80(ABR); // hi byte of addr
	SAVE_PC
	PC_ABR = I80Regs.nWZ.W++;

	if (pfx_state == 1) // FJS Z80
		I80Regs.nIX.B.l = Rd80(ABR);
	else if (pfx_state == 2)
		I80Regs.nIY.B.l = Rd80(ABR);
	else
		I80Regs.nHL.B.l = Rd80(ABR); // lo byte of source
	PC_ABR = I80Regs.nWZ.W;

	if (pfx_state == 1) // FJS Z80
		I80Regs.nIX.B.h = Rd80(ABR);
	else if (pfx_state == 2)
		I80Regs.nIY.B.h = Rd80(ABR);
	else
		I80Regs.nHL.B.h = Rd80(ABR);	// hi byte of source
	RESTORE_PC
	pfx_state = 0; // FJS
	// I80Regs.nPC.W++;
#endif
	ADVANCE_PC;
}

static void	ret ( OP_ARG_U ) 
{
// Return
// RET:: PC=[SP]+
// Flags:	SZAPC
//			-----

	// fast version -

	I80Regs.CPUstatus |= STACK;
	I80Regs.nWZ.B.l = Rd80(PC_ABR = I80Regs.nSP.W++);		// pop low-order
	I80Regs.nWZ.B.h = Rd80(PC_ABR = I80Regs.nSP.W++);		// pop high-order
	I80Regs.nPC.W = I80Regs.nWZ.W;
	pfx_state = 0; // FJS

}

static void	xthl ( OP_ARG_U ) 
{
// Exchange stack top with HL
// XTHL:: [SP]<->HL
// Flags:	SZAPC
//			-----

	// register /* FJS */ byte tmp;

	// fast version -

	ADVANCE_PC;
	if (pfx_state == 1) // FJS Z80
		I80Regs.nWZ.W = I80Regs.nIX.W; // copy IX
	else if (pfx_state == 2)
		I80Regs.nWZ.W = I80Regs.nIY.W; // copy IY
	else
		I80Regs.nWZ.W = I80Regs.nHL.W; // copy HL
	I80Regs.CPUstatus |= STACK;		// POP L
	SAVE_PC
	PC_ABR = I80Regs.nSP.W++;

	if (pfx_state == 1) // FJS Z80
		I80Regs.nIX.B.l = Rd80(ABR);
	else if (pfx_state == 2)
		I80Regs.nIY.B.l = Rd80(ABR);
	else
		I80Regs.nHL.B.l = Rd80(ABR);
	PC_ABR = I80Regs.nSP.W;			// POP H		

	if (pfx_state == 1)
		I80Regs.nIX.B.h = Rd80(ABR);
	else if (pfx_state == 2)
		I80Regs.nIY.B.h = Rd80(ABR);
	else
		I80Regs.nHL.B.h = Rd80(ABR);
	I80Regs.CPUstatus &= ~WO;		// PUSH old H

	Wr80 ( ABR, I80Regs.nWZ.B.h );
	PC_ABR = --(I80Regs.nSP.W);		// PUSH old L

	Wr80 ( ABR, I80Regs.nWZ.B.l );
	RESTORE_PC
	pfx_state = 0; // FJS
}

static void	xchg ( OP_ARG_U ) 
{
// Exchange HL with DE
// XCHG:: HL <-> DE
// Flags:	SZAPC
//			-----

	I80Regs.nWZ.W = I80Regs.nDE.W;

	if (pfx_state == 1) { // FJS Z80
		I80Regs.nDE.W = I80Regs.nIX.W;
		I80Regs.nIX.W = I80Regs.nWZ.W;
	} else if (pfx_state == 2) {
		I80Regs.nDE.W = I80Regs.nIY.W;
		I80Regs.nIY.W = I80Regs.nWZ.W;
	} else {
		I80Regs.nDE.W = I80Regs.nHL.W;
		I80Regs.nHL.W = I80Regs.nWZ.W;
	}
	pfx_state = 0; // FJS
	ADVANCE_PC;
}


static void nop ( OP_ARG_U )
{
// No Operation
// Flags:	SZAPC
//			-----

	// Do nothing except step PC
	pfx_state = 0; // FJS
	ADVANCE_PC;
}


/**************************************************************\
*   NULL I/O Device   *****************************************|
\**************************************************************/
static int nulldev (int flag, int data)
{
	return 0xff;			// return 0xff on nonexistent port
	
}


/**************************************************************\
*   Sense Switch Handler   ************************************|
\**************************************************************/
static int sensesw (int io, int data)
{
	// io == 0 (read) or 1 (write)

	if (io == 0) return (int)(Aswitches >> 8);		// get switch setting

	IOPANEL = ~(data);			// data is ACCUM
	return 0;					// return ignored by SetIO
}


/**************************************************************\
*   Core Dumper   *********************************************|
\**************************************************************/
BOOL CoreDump(void)
{
	/* Dumps a complete core image to a text file. The dump
		routine stops the emulator, dumps the core and then
		restarts the emulator.
	*/

	byte ucDumpVal;
	char *szFName = {"core.dmp"};
	FILE *pFile;
	SYSTEMTIME systime;
	word usOL;
	register word usIL;
	int	prev_runstate;
	ushort prev_bPrun;


    if (NULL == (pFile = fopen(szFName, "w"))){
		HOST_ThrowErrMsg("CoreDump: Unable to open core dump file!");
		return FALSE;
	}

	prev_bPrun = simstate.bPrun;
	prev_runstate = simstate.runstate;
	simstate.bPrun = RUN_OFF;
	simstate.runstate = HALTED;
	GetLocalTime(&systime);

	fprintf(pFile, "START OF CORE DUMP\n");
	fprintf(pFile, "Timestamp: %02d/%02d/%02d - %02d:%02d:%02d\n\n", systime.wMonth,
		systime.wDay, systime.wYear, systime.wHour, systime.wMinute, systime.wSecond);

	// setup address/data loops
	for (usOL=0; usOL <= 0xfff; usOL++){
		// print line address to file
		fprintf(pFile, "%04x: ", (usOL << 4));
				
		for (usIL=0; usIL <= 0x0f; usIL++){
			// do dumping of a line here
			ucDumpVal = GetMem((word)((usOL << 4) + usIL));

			//Line format: aaaa: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
			// xx = usDumpVal
			fprintf(pFile, "%02x ", ucDumpVal);
		}
		fprintf(pFile, "\n");
	}

	fprintf(pFile, "\nPageAccessRights\n");

	// setup address/data loops
	for (usOL=0; usOL <= 0xfff; usOL++){
		// print line address to file
		fprintf(pFile, "%04x: ", (usOL << 4));
				
		for (usIL=0; usIL <= 0x0f; usIL++){
			// do dumping of a line here
			ucDumpVal = PageAccessRights[((word)((usOL << 4) + usIL))];

			//Line format: aaaa: xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
			// xx = usDumpVal
			fprintf(pFile, "%02x ", ucDumpVal);
		}
		fprintf(pFile, "\n");
	}

	fprintf(pFile, "\n\nEND OF CORE DUMP\n");
	fclose(pFile);
	simstate.bPrun = prev_bPrun;
	simstate.runstate = prev_runstate;
	return TRUE;
}

/* end of file: i8080.c */
