/* ===============================================================
	(C) 2002  Bird Computer
	All rights reserved.

	bc6502.v
	Version 1.0

	You are free to use and modify this code for non-commercial
	or evaluation purposes.
	
	If you do modify the code, please state the origin and
	note that you have modified the code.
	
	Please read the license agreement (license.html) file.
	
	Generic original 6502 compatible core.
	Undoc'd instructions are not supported.
	
==============================================================-= */

`timescale 1ps / 1ps

`define RESET_VEC	16'hFFFC
`define NMI_VEC		16'hFFFA
`define IRQ_VEC		16'hFFFE
`define BRK_VEC		16'hFFFE
`define SP_RESET	8'hFF		// reset value of the stack pointer

`define DECMD_SUPPORT	1

// Don't confuse these with the parameters!
`define DBW		8
`define ABW		16

`define JSR		8'h20
`define JMP		8'h4C
`define JMP_I	8'h6C
`define RTS		8'h60
`define RTI		8'h40

`define PHP		8'h08
`define PHA		8'h48
`define PLP		8'h28
`define PLA		8'h68

`define BRK		8'h00
`define INX		8'hE8
`define DEX		8'hCA
`define INY		8'hC8
`define DEY		8'h88

`define TYA		8'h98
`define TAY		8'ha8
`define TXA		8'h8A
`define	TAX		8'hAA
`define TXS		8'h9A
`define	TSX		8'hBA

`define ASL_A	8'h0A
`define ASL_Z	8'h06
`define ASL_ZX	8'h16
`define ASL_ABS	8'h0E
`define ASL_AX	8'h1E
`define	ROL_A	8'h2A
`define ROL_Z	8'h26
`define ROL_ZX	8'h36
`define ROL_ABS	8'h2E
`define ROL_AX	8'h3e
`define LSR_A	8'h4A
`define LSR_Z	8'h46
`define LSR_ZX	8'h56
`define LSR_ABS	8'h4E
`define LSR_AX	8'h5E
`define	ROR_A	8'h6A
`define ROR_Z	8'h66
`define ROR_ZX	8'h76
`define ROR_ABS	8'h6E
`define ROR_AX	8'h7E

`define DEC_Z	8'hC6
`define DEC_ZX	8'hD6
`define DEC_A	8'hCE
`define DEC_AX	8'hDE
`define INC_Z	8'hE6
`define INC_ZX	8'hF6
`define INC_A	8'hEE
`define INC_AX	8'hFE

`define CLD		8'hD8
`define SED		8'hF8
`define CLC		8'h18
`define	SEC		8'h38
`define	CLI		8'h58
`define	SEI		8'h78
`define CLV		8'hB8

`define NOP		8'hEA

// Group0 opcodes
`define GROUP0	2'b00
`define BIT		3'b001
`define STY		3'b100
`define LDY		3'b101
`define CPY		3'b110
`define CPX		3'b111

// Group1 opcodes
`define GROUP1	2'b01
`define	ORA		3'b000
`define AND		3'b001
`define EOR		3'b010
`define ADC		3'b011
`define STA		3'b100
`define LDA		3'b101
`define CMP		3'b110
`define SBC		3'b111

// Group2 opcodes
`define GROUP2	2'b10
`define ASL		3'b000
`define ROL		3'b001
`define LSR		3'b010
`define ROR		3'b011
`define STX		3'b100
`define LDX		3'b101
`define DEC		3'b110
`define INC		3'b111

`define BPL		3'b000
`define BMI		3'b001
`define BVC		3'b010
`define BVS		3'b011
`define BCC		3'b100
`define BCS		3'b101
`define BNE		3'b110
`define BEQ		3'b111

`define LDA_I	8'hA9
`define LDA_Z	8'hA5
`define LDA_ZX	8'hB5
`define LDA_IX	8'hA1
`define LDA_IY	8'hB1
`define LDA_A	8'hAD
`define LDA_AX	8'hBD
`define LDA_AY	8'hB9

`define LDY_I	8'hA0
`define LDY_Z	8'hA4
`define LDY_ZX	8'hB4
`define LDY_A	8'hAC
`define LDY_AX	8'hBC

`define CPY_I	8'hC0

`define LDX_I	8'hA2
`define	LDX_Z	8'hA6
`define LDX_ZY	8'hB6
`define LDX_A	8'hAE
`define LDX_AY	8'hBE

`define CPX_I	8'hE0

`define STA_Z	8'h85
`define STA_ZX	8'h95
`define STA_IX	8'h81
`define STA_IY	8'h91
`define STA_A	8'h8D
`define STA_AX	8'h9D
`define STA_AY	8'h99

`define STY_Z	8'h84
`define STY_ZX	8'h94
`define STY_A	8'h8C

`define	STX_Z	8'h86
`define STX_ZY	8'h96
`define STX_A	8'h8E


module bc6502(reset, clk, nmi, irq, rdy, so, di, dout, rw, ma,
	rw_nxt, ma_nxt, sync, state, flags);
	parameter ABW = `ABW;
	parameter DBW = `DBW;

	input reset/*verilator public_flat*/;
	input clk;
	input nmi;				// active high
	input irq/*verilator public_flat*/;				// active high
	input rdy;
	input so;				// set overflow
	input [DBW-1:0] di/*verilator public_flat*/;		// data input bus
	output [DBW-1:0] dout;	// data output bus
	reg [DBW-1:0] dout;
	output rw;
	reg rw;
	output [ABW-1:0] ma;
	reg [ABW-1:0] ma/*verilator public_flat*/;
	// The following two signals can be useful for interfacing
	// to synchronous memory by providing values just before the
	// clock edge rather than after.
	output rw_nxt;
	output [ABW-1:0] ma_nxt;
	output sync;
	// The following two signals are provided mainly for
	// debugging.
	output [31:0] state;	// cpu state
	output [4:0] flags;

	//-----------------------------------
	reg [7:0] cres;		// critical reset sequencer
	wire creset = cres[7];
	reg [DBW-1:0] ir;
	reg [DBW-1:0] dil;	// data input latch

	// Processor Programming Model registers
	reg	[DBW-1:0] a_reg;		// A accumulator
	reg [DBW-1:0] x_reg;		// X index register
	reg [DBW-1:0] y_reg;		// Y index register
	reg [DBW-1:0] sp_reg;		// SP stack pointer
	reg	[ABW-1:0] pc_reg;		// PC program counter
	reg nf,vf,bf,df,im,zf,cf;	// SR status register
//	wire [7:0] sr_reg = {nf,vf,1'b1,bf,df,im,zf,cf};
   	wire [7:0] sr_reg = {nf,vf,1'b0,bf,df,im,zf,cf};

//	tri [DBW-1:0] res;					// internal result bus
   	wire [DBW-1:0] res;					// internal result bus
	reg [DBW-1:0] tmp;					// temp reg needed for some operations
	reg [ABW-1:0] pc_nxt;
	reg [DBW-1:0] dout_nxt;
	reg rw_nxt;

	reg prev_nmi;				// track previous nmi state for edge detection
	wire nmi_edge = nmi & ~prev_nmi;
	wire any_int = nmi_edge | (irq & ~im);

	// cpu states
	wire s_reset;
	wire s_reset1;
	wire s_reset2;
	wire s_reset3;
	wire s_nmi1, s_nmi2, s_nmi3, s_nmi4, s_nmi5;
	wire s_ld_pch;
	wire s_exec;
	wire s_branch;
	wire s_dataFetch;
	wire s_update;
	wire s_afterWrite;
	wire s_ix1, s_ix2;
	wire s_iy1, s_iy2;
	wire s_abs1;
	wire s_jmpi1;
	wire s_jsr1, s_jsr2;
	wire s_pul;
	wire s_rts1, s_rts2, s_rts3;
	wire s_rti1, s_rti2, s_rti3;
	wire s_sync;

	assign state = {
		s_reset, s_reset1, s_reset2, s_reset3,
		s_nmi1,	s_nmi2,	s_nmi3,	s_nmi4,	s_nmi5,
		s_ld_pch,
		s_exec,
		s_branch,
		s_dataFetch,
		s_update,
		s_afterWrite,
		s_ix1, s_ix2,
		s_iy1, s_iy2,
		s_abs1,
		s_jmpi1,
		s_jsr1,	s_jsr2,
		s_pul,
		s_rts1,	s_rts2,	s_rts3,
		s_rti1,	s_rti2, s_rti3,
		s_sync, 1'b0};

	assign sync = s_sync;

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
	// Instruction Decoding Section
	wire [2:0] cond = ir[7:5];
	wire branch = ir[4:0]==5'b10000;
	reg taken;
	assign flags = {nf,zf,cf,vf,taken};

	wire jmp = (ir == `JMP);
	wire jmpi = (ir == `JMP_I);

	wire php = ir==`PHP;
	wire pha = ir==`PHA;
	wire psh = php|pha;
	wire plp = ir==`PLP;
	wire pla = ir==`PLA;
	wire pul = plp|pla;

	wire jsr = ir==`JSR;
	wire rts = ir==`RTS;
	wire rti = ir==`RTI;
	wire brk = ir==`BRK;

	wire nop = ir==`NOP;	
	wire inx = ir==`INX;
	wire iny = ir==`INY;
	wire dex = ir==`DEX;
	wire dey = ir==`DEY;

	wire clc = ir==`CLC;
	wire cld = ir==`CLD;
	wire clv = ir==`CLV;
	wire cli = ir==`CLI;
	wire sec = ir==`SEC;
	wire sed = ir==`SED;
	wire sei = ir==`SEI;
	
	wire tay = ir==`TAY;
	wire tya = ir==`TYA;
	wire tax = ir==`TAX;
	wire txa = ir==`TXA;
	wire tsx = ir==`TSX;
	wire txs = ir==`TXS;

	wire ror_a = ir==`ROR_A;
	wire lsr_a = ir==`LSR_A;
	wire rol_a = ir==`ROL_A;
	wire asl_a = ir==`ASL_A;
	
	wire ldx = ir[7:5]==`LDX;
	wire stxx= ir[7:5]==`STX;
	wire ror = ir[7:5]==`ROR;
	wire rol = ir[7:5]==`ROL;
	wire lsr = ir[7:5]==`LSR;
	wire asl = ir[7:5]==`ASL;
	wire inc = ir[7:5]==`INC;
	wire dec = ir[7:5]==`DEC;

	wire ldy = ir[7:5]==`LDY;
	wire styy = ir[7:5]==`STY;
	wire cmp = ir[7:5]==`CMP;
	
	wire ldy_i = ir==`LDY_I;
	wire cpy_i = ir==`CPY_I;
	wire ldx_i = ir==`LDX_I;
	wire cpx_i = ir==`CPX_I;

	// miscellaneous operations
	wire mop = nop | inx | dex | iny | dey |
				txa | tax | tya | tay | txs | tsx |
				cld | sed | sei | cli | sec | clc | clv |
				asl_a | lsr_a | ror_a | rol_a;

	// address modes zp:zp,x:zp,y:(zp,x):(zp),y:#:abs:abs,x:abs,y
	wire ix = 	(ir[4:2]==3'b000&&ir[0]==1'b1);
	wire zp = 	(ir[4:2]==3'b001);
	wire imm =	(ir[4:2]==3'b010&&ir[0]==1'b1) || (ir[7]==1'b1 && ir[4:0]==5'b00000) || ldx_i;
	wire abs = 	(ir[4:2]==3'b011);
	wire iy = 	(ir[4:2]==3'b100&&ir[0]==1'b1);
	wire zpy =  (ir==`STX_ZY || ir==`LDX_ZY);
	wire zpx = 	(ir[4:2]==3'b101) && !zpy;
	wire absy = (ir[4:2]==3'b110 && ir[0]==1'b1) || ir==`LDX_AY;
	wire absx = (ir[4:2]==3'b111) && !absy;
	wire zpxy = zp|zpx|zpy;
	wire absxy = abs|absx|absy;

	// sta/stx/sty
	wire sta = ir==`STA_Z||ir==`STA_ZX||ir==`STA_IX
		||ir==`STA_IY||ir==`STA_A||ir==`STA_AX
		||ir==`STA_AY;
	wire sty = ir==`STY_Z||ir==`STY_ZX||ir==`STY_A;
	wire stx = ir==`STX_Z||ir==`STX_ZY||ir==`STX_A;
	wire staxy = sta | stx | sty;

	wire grp0 = ir[1:0]==2'b00;
	wire grp1 = ir[1:0]==2'b01;
	wire grp2 = ir[1:0]==2'b10;
	wire grp2m = (asl | rol | lsr | ror | dec | inc) & grp2;	// memory ops
	wire grp2x = (ldx | stxx) & grp2;	// X reg. ops
	// When to update the status flags
	wire grp0_us = (ir[3:0]==4'h4 || ir[3:0]==4'hC || (ldy_i | cpy_i | cpx_i));
	wire grp1_us = grp1;
	wire grp2_us = grp2 && ir[3:0]!=4'hA;


	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Select between A, X, or Y registers
	reg [7:0] axy_reg;
	always @(a_reg or x_reg or y_reg or stx or sty or tya or txa or
		inx or dex or iny or dey or txs)
	begin
		if (inx|dex|stx|txa|txs)
			axy_reg = x_reg;
		else if (iny|dey|sty|tya)
			axy_reg = y_reg;
		else
			axy_reg = a_reg;
	end

	// incrementers / decrementers
	// Note: addsub needs inverted carry for subtract!!!
	// A separate module is used here to allow the synthesizer
	// to make better use of resources.
	wire [7:0] axy_addo;
	addsub #(8) axy_adder(
		.op(dex | dey),
		.ci(dex | dey),
		.a({1'b0,axy_reg}),
		.b(9'h1),
		.o(axy_addo),
		.co(), .v()
	);

	wire [7:0] sp_addo;
	addsub #(8) spadder(
		.op(s_nmi1 | s_nmi2 |
			s_jsr1 | s_jsr2 |
			(s_sync & any_int) |
			(s_exec & (brk|psh))),
		.ci(s_nmi1 | s_nmi2 |
			s_jsr1 | s_jsr2 |
			(s_sync & any_int) |
			(s_exec & (brk|psh))),
		.a({1'b0,sp_reg}),
		.b(9'h1),
		.o(sp_addo),
		.co(), .v()
	);

	// these flags are used to merge states together
	reg firq, fbrk;

	wire nfo0,cfo0,vfo0,zfo0;
	wire nfo1,cfo1,vfo1,zfo1;
	wire nfo2,cfo2,zfo2;
	wire [DBW-1:0] grp0_o;
	wire [DBW-1:0] grp1_o;
	wire [DBW-1:0] grp2_o;

	// datapath processing
	dp_group0 grp0_inst(.ir(ir),
		.a_reg(a_reg), .x_reg(x_reg), .y_reg(y_reg), .d(dil),
		.ni(nf), .vi(vf), .zi(zf), .ci(cf),
		.n(nfo0), .v(vfo0), .z(zfo0), .c(cfo0) );

	dp_group1 grp1_inst(.ir(ir), .dec(df),
		.ni(nf), .vi(vf), .zi(zf), .ci(cf),
		.a_reg(a_reg), .d(dil), .o(grp1_o),
		.n(nfo1), .v(vfo1), .z(zfo1), .c(cfo1) );

	dp_group2 grp2_inst(.ir(ir), .ldx(ldx), .stxx(stxx), .d(dil),
		.ci(cf), .ni(nf), .zi(zf),
		.c(cfo2), .n(nfo2), .z(zfo2),
		.o(grp2_o) );

	// inline datapath processing
   	assign res =
		s_exec & (inx|iny)|(dex|dey) ? axy_addo :
		s_exec & (tay|tax|txa|tya|txs) ? axy_reg :
		s_exec & tsx ? sp_reg :
		s_exec & ror_a ? {cf,a_reg[DBW-1:1]} :
		s_exec & lsr_a ? {1'b0,a_reg[DBW-1:1]} :
		s_exec & rol_a ? {a_reg[DBW-2:0],cf} :
		s_exec & asl_a ? {a_reg[DBW-2:0],1'b0} :
		s_pul ? di :
		s_rti1 ? di :
		s_update & (grp0|grp2) ? dil :
		s_update & grp1 ? grp1_o :
		8'bz;

	// control unit
	sequencer seq0(
		.reset(reset), .creset(creset), .clk(clk), .rdy(rdy),
		.any_int(any_int),
		.grp0(grp0), .grp1(grp1), .grp2x(grp2x), .grp2m(grp2m),
		.brk(brk),
		.mop(mop),
		.rti(rti), .rts(rts),
		.pul(pul), .psh(psh),
		.jsr(jsr),
		.jmp(jmp), .jmpi(jmpi), .branch(branch), .staxy(staxy),
		.ix(ix), .iy(iy), .absxy(absxy), .imm(imm), .zpxy(zpxy),
		.s_reset(s_reset), .s_reset1(s_reset1), .s_reset2(s_reset2), .s_reset3(s_reset3),
		.s_nmi1(s_nmi1), .s_nmi2(s_nmi2), .s_nmi3(s_nmi3), .s_nmi4(s_nmi4), .s_nmi5(s_nmi5),
		.s_ld_pch(s_ld_pch), .s_exec(s_exec), .s_branch(s_branch), .s_dataFetch(s_dataFetch), .s_update(s_update),
		.s_afterWrite(s_afterWrite),
		.s_ix1(s_ix1), .s_ix2(s_ix2), .s_iy1(s_iy1), .s_iy2(s_iy2),
		.s_abs1(s_abs1),
		.s_jmpi1(s_jmpi1),
		.s_jsr1(s_jsr1), .s_jsr2(s_jsr2),
		.s_pul(s_pul),
		.s_rts1(s_rts1), .s_rts2(s_rts2), .s_rts3(s_rts3),
		.s_rti1(s_rti1), .s_rti2(s_rti2), .s_rti3(s_rti3),
		.s_sync(s_sync) );

	// Generate critical reset
	always @(posedge clk)
		if (reset)
			cres <= 8'hFF;
		else
			cres <= {cres[6:0],1'b0};

	//-------------------------------------------------------------
	// Memory related hardware
	//-------------------------------------------------------------

	// Determine exception vector address
	reg [ABW-1:0] vec;
	always @(s_nmi3 or firq or fbrk) begin
		if (s_nmi3) begin
			if (firq)
				vec = `IRQ_VEC;
			else if (fbrk)
				vec = `BRK_VEC;
			else
				vec = `NMI_VEC;
		end
		else
			vec = `RESET_VEC;
	end

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Select between X, Y, Z registers for memory addressing.
	reg [7:0] xyz_reg;
	always @(x_reg or y_reg or absx or absy or zpx or zpy or
		s_iy2 or ix or s_abs1 or s_exec)
	begin
		if ((s_abs1 & absx)|(s_exec & (zpx|ix)))
			xyz_reg = x_reg;
		else if ((s_abs1 &absy)|(s_exec & zpy)|s_iy2)
			xyz_reg = y_reg;
		else
			xyz_reg = 8'h00;
	end

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
	// memory address multiplexing
	// Because of the amount of multiplexing on the address bus
	// a multiplexor is implemented with a tri-state bus.
	// Otherwise a 16 bit 16 to 1 multiplexor would be required
	// which uses a lot of resources.

//---   
	assign ma_nxt =
		// ma = vector
		(reset | s_reset | s_reset1 | s_nmi3) ? vec :
		// ma = ma + 1
		(s_reset2 | s_nmi4 | s_jmpi1 | s_ix1 | s_iy1) ? ma + 1 :
		// ma = pc + 1
		((s_sync & ~any_int) | (s_exec & (absxy | jsr | branch)) | s_rts3) ? pc_reg + 1 :
		// ma = tmp
		// abs,y must take precedence over abs,x
		(s_reset3 | s_nmi5 | s_rti3 | s_rts2 | s_ld_pch | 
	s_iy2 | s_ix2 | s_abs1 ) ? {{di,tmp}+{8'b0,xyz_reg}} :		// abs : abs,x : abs,y : (zp),y
		// zero page modes
		(s_exec & (zpxy | ix | iy)) ? {8'h00,di + xyz_reg} :	// zp : zp,x : zp,y : (zp,x) : (zp),y  // all zp modes
		// branch instr.
		// ma = pc + (sign extend)disp
		(s_branch & taken) ? {pc_reg + {{8{tmp[DBW-1]}},tmp}} :
		// ma = ma
		((s_dataFetch|s_update) & grp2m) ? ma :
		// ma = pc
		((s_branch & ~taken) | s_pul | s_afterWrite |
	(s_exec & (imm | mop)) | ((s_dataFetch|s_update) & (grp0 | grp1 | grp2x)) ) ?
		pc_reg :

		// all stack modes
		((s_exec & (brk|psh)) |
	s_nmi1 | s_nmi2 |
	s_jsr1 | s_jsr2 |
	(s_sync & any_int)) ? { 8'h1, sp_reg } :

		((s_exec & (pul|rts|rti)) |
	s_rts1 | s_rti1 | s_rti2) ? { 8'h1, sp_addo } :

		16'b0;

	//-------------------------------------------------------------
	//-------------------------------------------------------------
	// latch incoming immediate data
	always @(posedge clk)
		if (reset)
			dil <= 8'b0;
		else begin
			if (rdy) begin
				if (s_exec & imm)
					dil <= di;
				else if (s_dataFetch)
					dil <= di;
			end
		end


	// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
	// pc / ma manipulation
	always @(s_reset3 or s_rts2 or s_rts3 or s_rti3 or s_nmi5 or
		s_exec or s_ld_pch or s_abs1 or 
		jmp or jsr or s_sync or any_int or 
		branch or s_branch or s_jsr1 or s_jsr2 or imm or zpxy or
		ix or iy or absxy or jmpi or pc_reg or di or tmp or ir or
		ma_nxt)
	begin
		if (
			s_rts2 | s_rts3 |
			s_rti3 | s_nmi5 |
			(s_reset3 | s_ld_pch | (s_abs1 & jmp)) |
			(s_sync & ~any_int) |
			s_branch |
			(s_exec & branch) |
			(s_exec & jsr)
			)
			pc_nxt = ma_nxt;
		else if (s_jsr1)
			pc_nxt = {di,pc_reg[7:0]};
		else if (s_jsr2)
			pc_nxt = {pc_reg[15:8],tmp};
		else if (
			(s_exec & (imm | zpxy | ix | iy | absxy)) |
			(s_abs1 & ~(jmp | jmpi)) )
			pc_nxt = pc_reg + 16'd1;
		else
			pc_nxt = pc_reg;
	end

	always @(posedge clk)
	begin
		if (reset)
		begin
			pc_reg <= `RESET_VEC;
			ma <= `RESET_VEC;
		end
		else 
		if (rdy)
		begin
			ma <= ma_nxt;
			pc_reg <= pc_nxt;
		end
	end
	
`ifdef SIMULATION
	reg debug1/*verilator public_flat*/;
	reg debug2/*verilator public_flat*/;
	always @(posedge clk) begin
    	if (reset == 1'b0 && debug2 == 1'b1) begin
	 		$display("POS pc=%x %s ma=%x ir=%x di=%x dil=%x do=%x rw=%x a=%x x=%x y=%x vf=%x if=%x zf=%x nf=%x cf=%x sync=%x irq %x anyint %x im %x", pc_reg, getstatename(ir), ma , ir, di, dil, dout, rw, a_reg, x_reg, y_reg, vf, im, zf, nf, cf, sync, irq, any_int, im);
	 	end
	end

	function [8*6-1:0] getstatename;
		input [7:0] state;
		begin
			/* verilator lint_off WIDTH */
		case(state)
			
			`JSR:		getstatename = "JSR";
			`JMP:		getstatename = "JMP";
			`JMP_I:		getstatename = "JMP_I";
			`RTS:		getstatename = "RTS";
			`RTI:		getstatename = "RTI";

			`PHP:		getstatename = "PHP";
			`PHA:		getstatename = "PHA";
			`PLP:		getstatename = "PLP";
			`PLA:		getstatename = "PLA";

			`BRK:		getstatename = "BRK";
			`INX:		getstatename = "INX";
			`DEX:		getstatename = "DEX";
			`INY:		getstatename = "INY";
			`DEY:		getstatename = "DEY";
			
			`TYA:		getstatename = "TYA";
			`TAY:		getstatename = "TAY";
			`TXA:		getstatename = "TXA";
			`TAX:		getstatename = "TAX";
			`TXS:		getstatename = "TXS";
			`TSX:		getstatename = "TSX";


			`ASL_A:		getstatename = "ASL_A";
			`ASL_Z:		getstatename = "ASL_Z";
	// `define ASL_ZX	8'h16
	// `define ASL_ABS	8'h0E
	// `define ASL_AX	8'h1E
	// `define	ROL_A	8'h2A
	// `define ROL_Z	8'h26
	// `define ROL_ZX	8'h36
	// `define ROL_ABS	8'h2E
	// `define ROL_AX	8'h3e
			`LSR_A:		getstatename = "LSR_A";
			`LSR_Z:		getstatename = "LSR_Z";
			`LSR_ZX:	getstatename = "LSR_ZX";
			`LSR_ABS:	getstatename = "LSR_ABS";
			`LSR_AX:	getstatename = "LSR_AX";
	// `define	ROR_A	8'h6A
	// `define ROR_Z	8'h66
	// `define ROR_ZX	8'h76
	// `define ROR_ABS	8'h6E
	// `define ROR_AX	8'h7E

			`DEC_Z:		getstatename = "DEC_Z";
			`DEC_ZX:	getstatename = "DEC_ZX";
			`DEC_A:		getstatename = "DEC_A";
			`DEC_AX:	getstatename = "DEC_AX";
			`INC_Z:		getstatename = "INC_Z";
			`INC_ZX:	getstatename = "INC_ZX";
			`INC_A:		getstatename = "INC_A";
			`INC_AX:	getstatename = "INC_AX";
			`CLD:		getstatename = "CLD";
			`SED:		getstatename = "SED";
			`CLC:		getstatename = "CLC";
			`SEC:		getstatename = "SEC";
			`CLI:		getstatename = "CLI";
			`SEI:		getstatename = "SEI";
			`CLV:		getstatename = "CLV";

			`NOP:		getstatename = "NOP";

			8'h24:		getstatename = "BIT_Z";
			8'h2c:		getstatename = "BIT_A";

	// `define STY		3'b100
	// `define LDY		3'b101
	// `define CPY		3'b110
	// `define CPX		3'b111

	// // Group1 opcodes
	// `define GROUP1	2'b01
	// `define	ORA		3'b000
			8'h29:		getstatename = "AND_I";
			8'h59:		getstatename = "AND_Z";
	// `define EOR		3'b010
	// `define ADC		3'b011
	// `define STA		3'b100
	// `define LDA		3'b101
			8'hC9:		getstatename = "CMP_I";
			8'hC5:		getstatename = "CMP_Z";
	// `define SBC		3'b111

	// // Group2 opcodes
	// `define GROUP2	2'b10
	// `define ASL		3'b000
	// `define ROL		3'b001
	// `define LSR		3'b010
	// `define ROR		3'b011
	// `define STX		3'b100
	// `define LDX		3'b101
	// `define DEC		3'b110
	// `define INC		3'b111

			8'h10:		getstatename = "BPL";
			8'h30:		getstatename = "BMI";
			8'h50:		getstatename = "BVC";
			8'h70:		getstatename = "BVS";
			8'h90:		getstatename = "BCC";
			8'hB0:		getstatename = "BCS";
			8'hd0:		getstatename = "BNE";
			8'hf0:		getstatename = "BEQ";

			`LDA_I:		getstatename = "LDA_I";
			`LDA_Z:		getstatename = "LDA_Z";
			`LDA_ZX:	getstatename = "LDA_ZX";
			`LDA_IX:	getstatename = "LDA_IX";
			`LDA_IY:	getstatename = "LDA_IY";
			`LDA_A:		getstatename = "LDA_A";
			`LDA_AX:	getstatename = "LDA_AX";
			`LDA_AY:	getstatename = "LDA_AY";

			`LDY_I:		getstatename = "LDY_I";
			`LDY_Z:		getstatename = "LDY_Z";
			`LDY_ZX:	getstatename = "LDY_ZX";
			`LDY_A:		getstatename = "LDY_A";
			`LDY_AX:	getstatename = "LDY_AX";
			
			`CPY_I:		getstatename = "CPY_I";

			`LDX_I:		getstatename = "LDX_I";
			`LDX_Z:		getstatename = "LDX_Z";
			`LDX_ZY:	getstatename = "LDX_ZY";
			`LDX_A:		getstatename = "LDX_A";
			`LDX_AY:	getstatename = "LDX_AY";

			`CPX_I:		getstatename = "CPX_I";

			`STA_Z:		getstatename = "STA_Z";
			`STA_ZX:	getstatename = "STA_ZX";
			`STA_IX:	getstatename = "STA_IX";
			`STA_IY:	getstatename = "STA_IY";
			`STA_A:		getstatename = "STA_A";
			`STA_AX:	getstatename = "STA_AX";
			`STA_AY:	getstatename = "STA_AY";

			`STY_Z:		getstatename = "STY_Z";
			`STY_ZX:	getstatename = "STY_ZX";
			`STY_A:		getstatename = "STY_A";

			`STX_Z:		getstatename = "STX_Z";
			`STX_ZY:	getstatename = "STX_ZY";
			`STX_A:		getstatename = "STX_A";

			default:	getstatename = "???";
		endcase
	/* verilator lint_on WIDTH */		
		end

	endfunction

`endif
	// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
	// sp manipulation
	// we also reset the other regs here
	always @(posedge clk)
		if (reset)
			sp_reg <= `SP_RESET;
		else if (rdy) begin
			if (
				s_nmi1 | s_nmi2 |
				s_jsr1 | s_jsr2 |
				(s_sync & any_int) |
				(s_exec & (brk|psh|pul|rts|rti)) |
				s_rts1 | s_rti1 | s_rti2
				)
				sp_reg <= sp_addo;
			else if (s_exec & txs)
				sp_reg <= res;
		end


	// tmp manipulation
	always @(posedge clk)
		if (reset)
			tmp <= 0;
		else if (rdy) begin
			if (s_reset2 | s_nmi4 | s_rts1 | s_rti2 |
				s_jmpi1 |
				s_ix1 | s_iy1 | (s_exec & absxy) |
				(s_exec & (jsr | branch))
				)
				tmp <= di;
		end


	// rw manipulation - - - - - - - - - - - - - - - - - - - - - -
	// Note: the cpu is always either reading or writing to memory,
	// it doesn't have any idle cycles. This can be seen from the
	// memory address multiplexer, which is always set to a valid
	// value.
	always @(s_sync or any_int or zpxy or psh or brk or ldx or 
		staxy or s_exec or ir or s_jsr1 or s_jsr2 or
		s_ix2 or
		s_iy2 or s_abs1 or
		s_nmi1 or s_nmi2 or s_update or grp2m)
	begin
		if ( (s_sync & any_int) |
			(s_exec & zpxy & staxy) |
			(s_exec & (brk|psh)) |
			s_jsr1 | s_jsr2 |
			(staxy & (s_ix2 | s_iy2 | s_abs1)) |
			s_nmi1 | s_nmi2 |
			(s_update & grp2m) )
			rw_nxt = 0;
		else
			rw_nxt = 1;
	end

	always @(posedge clk)
		if (reset)
			rw <= 1;
		else if (rdy)
			rw <= rw_nxt;


	// dout manipulation - - - - - - - - - - - - - - - - - - - - -
	always @(s_nmi1 or s_nmi2 or
		s_jsr1 or s_jsr2 or pc_reg or sr_reg or
		php or pha or staxy or axy_reg or 
		nf or vf or bf or df or zf or cf or im or brk or 
		s_exec or zpxy or s_ix2 or s_iy2 or grp2m or
		s_abs1 or sta or stx or s_sync or any_int or
		s_update or a_reg or x_reg or y_reg or dout or grp2_o)
	begin
		// Place pc on bus for interrupt, or subroutine call
		if ((s_sync & any_int) | (s_exec & brk) | s_jsr1)
			dout_nxt = pc_reg[ABW-1:8];
		else if (s_nmi1 | s_jsr2)
			dout_nxt = pc_reg[7:0];
		else if ((staxy & ((s_exec & zpxy) | (s_ix2 | s_iy2 | s_abs1))) |
			(s_exec & pha) )
			dout_nxt = axy_reg;
		else if ((s_exec & php) | s_nmi2)
			dout_nxt = sr_reg;
		else if (s_update & grp2m)
			dout_nxt = grp2_o;
		else
			dout_nxt = dout;
	end

	always @(posedge clk)
		if (reset)
			dout <= 8'b0;
		else if (rdy)
			dout <= dout_nxt;


	// A,X,Y Register loading -  -  -  -  -  -  -  -  -  -  -  -  -
	always @(posedge clk) begin
		if (reset) begin
			a_reg <= 8'b0;
			x_reg <= 8'b0;
			y_reg <= 8'b0;
		end
		else begin
			if (rdy) begin
				if ( (s_exec & (tya | txa | ror_a | lsr_a | rol_a | asl_a)) |
					(s_pul & pla) |
					(s_update & grp1 & ~cmp) )
					a_reg <= res;
				if ( (s_exec & (inx | dex | tax | tsx)) |
					(s_update & grp2 & ldx) )
					x_reg <= res;
				if ( (s_exec & (iny | dey | tay)) |
					(s_update & grp0 & ldy) )
					y_reg <= res;
			end
		end
	end


	// Load instruction register - - - - - - - - - - - - - - - - -
	// Also track the nmi state for nmi edge detection
	always @(posedge clk)
		if (reset) begin
			ir <= `NOP;
			prev_nmi <= 0;
		end
		else if (s_reset1) begin
			ir <= `NOP;
			prev_nmi <= 0;
		end
		else if (rdy & s_sync) begin
			ir <= di;
			prev_nmi <= nmi;
		end


	// Evaluate branch condition - - - - - - - - - - - - - - - - -
	reg takb;
	always @(cond or nf or vf or cf or zf)
		begin
			case (cond)
			`BPL:	takb = ~nf;
			`BMI: 	takb = nf;
			`BVC:	takb = ~vf;
			`BVS: 	takb = vf;
			`BCC:	takb = ~cf;
			`BCS: 	takb = cf;
			`BNE:	takb = ~zf;
			`BEQ: 	takb = zf;
			endcase
		end


	// SR flags updating - - - - - - - - - - - - - - - - - - - - - 
	always @(posedge clk) begin
		if (reset) begin
			firq <= 0;
			vf <= 0;
			nf <= 0;
			im <= 1;
			zf <= 0;
			cf <= 0;
			df <= 0;
			bf <= 0;
		end
		else if (rdy) begin

			if (so)
				vf <= 1'b1;

			if (s_reset1) begin
				firq <= 0;
				fbrk <= 0;
				cf <= 0;
				df <= 0;
				nf <= 0;
				vf <= 0;
				zf <= 0;
				im <= 1;
				bf <= 0;
			end
			// NMI / IRQ / BRK ---------------------
			if (s_nmi2) begin
				bf <= fbrk;
				im <= 1;
			end
			if (s_nmi4) begin
				fbrk <= 0;
				firq <= 0;
			end
			// sync indicates the start of an instruction cycle
			// when active. we can check for an nmi or irq first
			// before proceding with the instruction.
			if (s_sync) begin
				// irq is level sensitive
				if (any_int) begin
					// nmi takes precedence
					if (~nmi_edge)
						firq <= 1;
				end
			end

			else if (s_exec) begin

				taken <= takb;

				case (ir)
				`BRK:	fbrk <= 1;
				`INX,`DEX,`TAX,`TSX,
				`INY,`DEY,`TAY,
				`TYA,`TXA:
						begin
						zf <= res==0;
						nf <= res[DBW-1];
						end
				`CLD:	df <= 0;
				`SED:	df <= 1;
				`CLC:	cf <= 0;
				`SEC:	cf <= 1;
				`CLI:	im <= 0;
				`SEI:	im <= 1;
				`CLV:	vf <= 0;
				`ROR_A,`LSR_A:
						begin
						cf <= a_reg[0];
						zf <= res==0;
						nf <= res[DBW-1];
						end
				`ROL_A,`ASL_A:
						begin
						cf <= a_reg[DBW-1];
						zf <= res==0;
						nf <= res[DBW-1];
						end
				// eval branch cond.
				default: ;
				endcase
			end

			//======================================
			// subsequent opcode states
			//======================================
			
			if ((s_pul & plp) | s_rti1) begin
				nf <= res[7];
				vf <= res[6];
				bf <= res[4];
				df <= res[3];
				im <= res[2];
				zf <= res[1];
				cf <= res[0];
			end
			
			if (s_pul & pla) begin
				zf <= res==0;
				nf <= res[DBW-1];
			end

			// mega state1
			// at this point the fetched op should be incoming
			// from d.
			// store will already have been done, except for
			// memory direct
			if (s_update) begin
			
				if (grp0_us) begin
					nf <= nfo0;
					vf <= vfo0;
					cf <= cfo0;
					zf <= zfo0;
				end

				if (grp1_us) begin
					nf <= nfo1;
					vf <= vfo1;
					cf <= cfo1;
					zf <= zfo1;
				end

				if (grp2_us) begin
					nf <= nfo2;
					cf <= cfo2;
					zf <= zfo2;
				end

			end	// `s_update

		end // if (rdy)
	end
endmodule


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Handle Group 0 opcodes - opcodes with the two lsb = 00
// ------00
// LDY, STY, CPY, CPX, and BIT
module dp_group0(ir,a_reg,x_reg,y_reg,d,ni,vi,zi,ci,n,v,z,c);
	parameter DBW = 8;
	input [DBW-1:0] ir;
	input [DBW-1:0] a_reg;
	input [DBW-1:0] x_reg;
	input [DBW-1:0] y_reg;
	input [DBW-1:0] d;
	input ni, vi, zi, ci;
	output n, v, z, c;

	wire [DBW-1:0] w_bit = a_reg & d;
	wire [DBW:0] w_cpy = y_reg - d;
	wire [DBW:0] w_cpx = x_reg - d;

	reg [DBW-1:0] res;
	wire ldy = ir[7:5]==`LDY;
	wire cpy = ir[7:5]==`CPY;
	wire cpx = ir[7:5]==`CPX;
	wire bitt = ir[7:5]==`BIT;

	always @(ir or w_cpx or w_cpy or w_bit or d) begin
		case (ir[7:5])
		`CPX:		res = w_cpx[DBW-1:0];
		`CPY:		res = w_cpy[DBW-1:0];
		`BIT:		res = w_bit;
		default:	res = d;
		endcase
	end

	assign n = cpy | cpx | ldy ? res[DBW-1] : bitt ? d[7] : ni;
	assign z = cpy | cpx | bitt | ldy ? res==0 : zi;
	assign v = bitt ? d[DBW-2] : vi;
	assign c = cpy ? ~w_cpy[DBW] : cpx ? ~w_cpx[DBW] : ci;

endmodule


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Handle Group 1 opcodes - opcodes with the two lsb = 01
// ------01
// 
module dp_group1(ir,dec,ni,vi,zi,ci,a_reg,d,o,n,v,z,c);
	parameter DBW = 8;
	input [7:0] ir;
	input dec;
	input ni, vi, zi, ci;
	input [DBW-1:0] a_reg;
	input [DBW-1:0] d;
	output [DBW-1:0] o;
	reg [DBW-1:0] o;
	output n, v, z, c;

	wire [DBW-1:0] adc_o, sbc_o, cmp_o;
	wire adc_c, sbc_c, cmp_c;

	wire adc = ir[7:5]==`ADC;
	wire sbc = ir[7:5]==`SBC;
	wire cmp = ir[7:5]==`CMP;
	wire sta = ir[7:5]==`STA;


	// alu ops
	always @(ir or a_reg or d or adc_o or cmp_o or sbc_o) begin
		case(ir[7:5])
		`ORA: 	o = a_reg | d;
		`AND:	o = a_reg & d;
		`EOR:	o = a_reg ^ d;
		`ADC:	o = adc_o;
		`STA:	o = a_reg;
		`LDA:	o = d;
		`CMP:	o = cmp_o;
		`SBC:	o = sbc_o;
		endcase
	end


	add #(`DECMD_SUPPORT) add0 (.dec(dec), .ci(ci), .a(a_reg), .b(d), .o(adc_o), .c(adc_c) );
	// 6502 uses inverted carry on subtract
	sub #(`DECMD_SUPPORT) sub0 (.dec(dec), .ci(~ci), .a(a_reg), .b(d), .o(sbc_o), .c(sbc_c) );

	wire [DBW:0] cmp_tmp = a_reg - d;	// compare
	assign cmp_c = cmp_tmp[DBW];
	assign cmp_o = cmp_tmp[DBW-1:0];
	assign n = sta ? ni : o[DBW-1];
	assign z = sta ? zi : o==0;
	assign c = adc ? adc_c : sbc ? ~sbc_c : cmp ? ~cmp_c : ci;
	assign v = (adc | sbc) ?
//		 (sbc ^ o[DBW-1] ^ d[DBW-1]) & (~sbc ^ a_reg[DBW-1] ^ d[DBW-1]) : vi;
		   (o[DBW-1] ^ a_reg[DBW-1]) & (o[DBW-1] ^ d[DBW-1]) : vi;

endmodule


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Handle Group 2 opcodes - opcodes with the two lsb = 10
// ------10
// 
module dp_group2(ir,ldx,stxx,d,ci,ni,zi,c,n,z,o);
	parameter DBW = 8;
	input [7:0] ir;
	input ldx;
	input stxx;
	input [DBW-1:0] d;
	input ci, ni, zi;
	output c, n, z;
	output [DBW-1:0] o;
	reg [DBW-1:0] o;

	reg sc;		// shift carry
	wire d_shift = ~ir[7];
	/* verilator lint_off LATCH */
	always @(ir or d or ci) begin
		case(ir[7:5])
		`ASL:	begin
				o = {d[DBW-2:0],1'b0};
				sc = d[DBW-1];
				end
		`ROL:	begin
				o = {d[DBW-2:0],ci};
				sc = d[DBW-1];
				end
		`LSR:	begin
				o = {1'b0,d[DBW-1:1]};
				sc = d[0];
				end
		`ROR:	begin
				o = {ci,d[DBW-1:1]};
				sc = d[0];
				end
		// A store does not affect the flags so the output can
		// be set to anything.
		`STX:	o = d;
		// Output needs to be set on a load so the flags can
		// be set.
		`LDX:	o = d;
		`DEC:	o = d - 8'd1;
		`INC:	o = d + 8'd1;
		default: begin sc = sc; o = o; end
		endcase
	end
	/* verilator lint_on LATCH */

	assign c = d_shift ? sc : ci;	// cf set only for shifts
	assign n = stxx ? ni : o[DBW-1];
	assign z = stxx ? zi : o==0;

endmodule


// Yikes!
// If you understand this, you're doing well. It took me
// quite a bit of head scratching. I've basically broken the
// add down into two half adds and adjusted the carry and
// sum based on decimal mode, mimicing the 6502.
// hc2 will only be set in decimal mode if least significant
// nybble is 10 or greater.
module add(dec, ci, a, b, o, c);
	parameter dec_support = 1;	// indicates whether or not to support decimal mode
	input dec;
	input ci;
	input [7:0] a;
	input [7:0] b;
	output [7:0] o;
	output c;

	reg [7:0] o;
	reg c;

	wire c4, c8;
	wire [7:0] dec_sum;
	reg [9:0] bin_sum;

	nyb_add na0(.dec(dec), .ci(ci), .a(a[3:0]), .b(b[3:0]), .o(dec_sum[3:0]), .c(c4) );
	nyb_add na1(.dec(dec), .ci(c4), .a(a[7:4]), .b(b[7:4]), .o(dec_sum[7:4]), .c(c8) );

	always @(a or b or ci)
		bin_sum = {a,ci} + {b,1'b1};

	always @(bin_sum or dec_sum or c8)
		if (dec_support) begin
			o = dec_sum;
			c = c8;
		end
		else begin
			o = bin_sum[8:1];
			c = bin_sum[9];
		end

endmodule


module nyb_add(dec, ci, a, b, o, c);
	input dec;	// decimal mode indicator
	input ci;	// carry in
	input [3:0] a;
	input [3:0] b;
	output [3:0] o;
	output c;

	// Note: XST does not like assigning to grouped bits on LHS
	// which is why all the temps
	reg [5:0] tmp1;
	reg [4:0] tmp2;
	wire hc1 = tmp1[5];
	wire hc2 = tmp2[4];
	assign c = hc1 | hc2;
	wire [3:0] sum = tmp1[4:1];
	assign o = tmp2[3:0];

	always @(dec or a or b or ci or sum or hc1) begin
		tmp1 = {a,ci} + {b,1'b1};
		// +6 if in decimal mode and lo nybble > 10
		if (sum >= 4'd10)
			tmp2 = sum + {1'b0,dec,dec,1'b0};
		else
			tmp2 = { 1'b0, sum};
	end
		
endmodule


// Subtract is similar to add. In decimal mode, if the subtract
// results in a negative number, then six has to be subtracted
// from the nybble.

module sub(dec, ci, a, b, o, c);
	parameter dec_support = 1;	// indicates whether or not to support decimal mode
	input dec;
	input ci;
	input [7:0] a;
	input [7:0] b;
	output [7:0] o;
	output c;

	reg [7:0] o;
	reg c;

	wire c4, c8;
	wire [7:0] dec_dif;
	reg [9:0] bin_dif;

	nyb_sub ns0(.dec(dec), .ci(ci), .a(a[3:0]), .b(b[3:0]), .o(dec_dif[3:0]), .c(c4) );
	nyb_sub ns1(.dec(dec), .ci(c4), .a(a[7:4]), .b(b[7:4]), .o(dec_dif[7:4]), .c(c8) );

	always @(a or b or ci)
		bin_dif = {a,~ci} - {b,1'b1};

	always @(bin_dif or dec_dif or c8)
		if (dec_support) begin
			o = dec_dif;
			c = c8;
		end
		else begin
			o = bin_dif[8:1];
			c = bin_dif[9];
		end

endmodule


module nyb_sub(dec, ci, a, b, o, c);
	input dec;	// decimal mode indicator
	input ci;	// carry in
	input [3:0] a;
	input [3:0] b;
	output [3:0] o;
	output c;

	// Note: XST does not like assigning to grouped bits on LHS
	// which is why all the temps
	reg [5:0] tmp1;
	reg [4:0] tmp2;
	wire hb1 = tmp1[5];
	wire hb2 = tmp2[4];
	assign c = hb1 | hb2;
	wire [3:0] dif = tmp1[4:1];
	assign o = tmp2[3:0];

	always @(dec or a or b or ci or dif or hb1) begin
		tmp1 = {a,~ci} - {b,1'b1};
		// -6 if in decimal mode and lo nybble < 0
		tmp2 = dif - {1'b0, hb1 & dec, hb1 & dec, 1'b0};
	end
		
endmodule


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 		This module sequences through the states based on previous
// states, the ir value, and some control signals.
//		A one hot state machine is used because we have plenty
// of regs and it eases decoding.
module sequencer(reset, creset, clk, rdy,
	any_int, grp0, grp1, grp2x, grp2m,
	brk,
	mop,
	rti, rts, pul, psh,
	jsr,
	jmp, jmpi, branch, staxy, ix, iy, absxy, imm, zpxy,
	s_reset, s_reset1, s_reset2, s_reset3,
	s_nmi1, s_nmi2, s_nmi3, s_nmi4, s_nmi5,
	s_ld_pch, s_exec, s_branch, s_dataFetch, s_update, s_afterWrite,
	s_ix1, s_ix2, s_iy1, s_iy2,
	s_abs1,
	s_jmpi1,
	s_jsr1, s_jsr2,
	s_pul,
	s_rts1, s_rts2, s_rts3,
	s_rti1, s_rti2, s_rti3,
	s_sync);
	input reset;
	input creset;
	input clk;
	input rdy;
	input any_int;
	input grp0, grp1, grp2x, grp2m;
	input brk;
	input mop;
	input rti;
	input rts;
	input pul;
	input psh;
	input jsr;
	input jmp;
	input jmpi;
	input branch;
	input staxy;
	input ix;
	input iy;
	input absxy;
	input imm;
	input zpxy;
	output s_reset;
	output s_reset1;
	output s_reset2;
	output s_reset3;
	output s_nmi1, s_nmi2, s_nmi3, s_nmi4, s_nmi5;
	output s_ld_pch;
	output s_exec;
	output s_branch;
	output s_dataFetch;
	output s_update;
	output s_afterWrite;
	output s_ix1, s_ix2;
	output s_iy1, s_iy2;
 	output s_abs1;
	output s_jmpi1;
	output s_jsr1, s_jsr2;
	output s_pul;
	output s_rts1, s_rts2, s_rts3;
	output s_rti1, s_rti2, s_rti3;
	output s_sync;

	reg s_reset;
	reg s_reset1;
	reg s_reset2;
	reg s_reset3;
	reg s_nmi1, s_nmi2, s_nmi3, s_nmi4, s_nmi5;
	reg s_ld_pch;
	reg s_exec;
	reg s_branch;
	reg s_dataFetch;
	reg s_update;
	reg s_afterWrite;
	reg s_ix1;
	reg s_ix2;
	reg s_iy1;
	reg s_iy2;
	reg s_abs1;
	reg s_jmpi1;
	reg s_jsr1, s_jsr2;
	reg s_pul;
	reg s_rts1, s_rts2, s_rts3;
	reg s_rti1, s_rti2, s_rti3;
	reg s_sync;

	always @(posedge clk) begin
		// put our states in a known condition - none selected
		if (reset) begin
			s_reset <= 1;
			s_reset1 <= 0;
			s_reset2 <= 0;
			s_reset3 <= 0;
			s_nmi1 <= 0;
			s_nmi2 <= 0;
			s_nmi3 <= 0;
			s_nmi4 <= 0;
			s_nmi5 <= 0;
			s_ld_pch <= 0;
			s_exec <= 0;
			s_branch <= 0;
			s_dataFetch <= 0;	// Latch in non-immediate data
			s_update <= 0;		// Update the machine state
			s_afterWrite <= 0;	// Switch the address bus back to pc after a write.
			s_ix1 <= 0;
			s_ix2 <= 0;
			s_iy1 <= 0;
			s_iy2 <= 0;
			s_abs1 <= 0;
			s_jmpi1 <= 0;
			s_jsr1 <= 0;
			s_jsr2 <= 0;
			s_pul <= 0;
			s_rts1 <= 0;
			s_rts2 <= 0;
			s_rts3 <= 0;
			s_rti1 <= 0;
			s_rti2 <= 0;
			s_rti3 <= 0;
			s_sync <= 0;
		end
		else if (creset)
			s_reset <= 1;
		else if (rdy) begin
			// advance states
			// Only a single state should be active at any one time
			
			// the reset state is actually evaluated here rather
			// than at a higher level as it is convenient to do so
			if (s_reset)
				s_reset <= 0;

			s_reset1 <= s_reset;
			s_reset2 <= s_reset1;
			s_reset3 <= s_reset2;
			s_nmi1 <= (s_sync & any_int) | (s_exec & brk);
			s_exec <= s_sync & ~any_int;
			s_nmi2 <= s_nmi1;
			s_nmi3 <= s_nmi2;
			s_nmi4 <= s_nmi3;
			s_nmi5 <= s_nmi4;
			s_sync <= s_reset3 | s_afterWrite | s_ld_pch | s_pul | s_rts3 |
				s_rti3 | s_nmi5 |
				(s_abs1 & jmp) | s_branch |
				(s_update & (grp0 | grp1 | grp2x)) |
				(s_exec & mop);
			s_branch <= s_exec & branch;
			s_rts1 <= s_exec & rts;
			s_rts2 <= s_rts1;
			s_rts3 <= s_rts2;
			s_rti1 <= s_exec & rti;
			s_rti2 <= s_rti1;
			s_rti3 <= s_rti2;
			s_pul <= (s_exec & pul);
			s_jsr1 <= (s_exec & jsr);
			s_jsr2 <= s_jsr1;
			s_jmpi1 <= s_abs1 & jmpi;
			s_ld_pch <= s_jmpi1;
			s_dataFetch <= ~staxy & (
				(s_abs1 & ~(jmp | jmpi)) |
				s_ix2 | s_iy2 | (s_exec & zpxy));
			s_update <= s_dataFetch | (s_exec & imm);
			s_afterWrite <= s_jsr2 |
				(s_update & grp2m) |
				(s_abs1 & staxy) |
				(s_ix2 & staxy) |
				(s_iy2 & staxy) |
				(s_exec & ((zpxy & staxy) | psh));
			s_ix1 <= s_exec & ix;
			s_ix2 <= s_ix1;
			s_iy1 <= s_exec & iy;
			s_iy2 <= s_iy1;
			s_abs1 <= s_exec & absxy;
		end // if (rdy)
	end	// always

endmodule

