mirror of
https://github.com/Gehstock/Mist_FPGA.git
synced 2026-02-11 02:29:55 +00:00
1572 lines
36 KiB
Verilog
1572 lines
36 KiB
Verilog
/* ===============================================================
|
|
(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 /*verilator public_flat*/ = 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
|
|
|
|
reg debug_cpu/*verilator public_flat*/;
|
|
|
|
`ifdef SIMULATION
|
|
|
|
always @(posedge clk) begin
|
|
if (reset == 1'b0 && debug_cpu == 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
|
|
|