§ Процессор
Проверенный с помощью тестов процессор для NES эмулятора.
module cpu
(
input clock,
input reset_n,
input ce,
input nmi,
output m0,
output [ 7:0] _a,
output [ 7:0] _x,
output [ 7:0] _y,
output [ 7:0] _p,
output [15:0] A,
input [ 7:0] I,
output reg [ 7:0] D,
output reg R,
output reg W
);
assign A = m ? cp : pc;
assign m0 = (t == LOAD);
assign {_a, _x, _y, _p} = {a, x, y, p};
localparam
LOAD = 5'h00, NDX = 5'h01, NDY = 5'h02, ABX = 5'h03,
ABY = 5'h04, ABS = 5'h05, REL = 5'h06, RUN = 5'h07,
ZP = 5'h08, ZPX = 5'h09, ZPY = 5'h0A, NDX2 = 5'h0B,
NDX3 = 5'h0C, LAT = 5'h0D, NDY2 = 5'h0E, NDY3 = 5'h0F,
ABS2 = 5'h10, ABXY = 5'h11, REL1 = 5'h12, REL2 = 5'h13,
BRK = 5'h14, JSR = 5'h15, RTS = 5'h16, RTI = 5'h17;
localparam
ORA = 0, AND = 1, EOR = 2, ADC = 3, STA = 4, LDA = 5, CMP = 6, SBC = 7,
ASL = 8, ROL = 9, LSR = 10, ROR = 11, BIT = 12, DEC = 14, INC = 15;
localparam CF = 0, ZF = 1, IF = 2, DF = 3, BF = 4, VF = 6, SF = 7;
localparam IRQ_NMI = 2'b01, IRQ_RST = 2'b10, IRQ_BRK = 2'b11;
localparam
DST_A = 2'b00, DST_X = 2'b01, DST_Y = 2'b10, DST_S = 2'b11,
SRC_D = 2'b00, SRC_X = 2'b01, SRC_Y = 2'b10, SRC_A = 2'b11;
reg [ 7:0] a, x, y, s, p;
reg [15:0] pc;
reg m;
reg rd;
reg [ 4:0] t;
reg [ 2:0] n;
reg [15:0] cp;
reg [ 7:0] opcode;
reg [ 7:0] tr;
reg [ 1:0] intr;
reg cout;
reg cnext;
reg nmitr;
wire [ 8:0] Xi = x + I;
wire [ 8:0] Yi = y + I;
wire [15:0] pcn = pc + 1;
wire [15:0] pcr = pcn + {{8{I[7]}}, I};
wire [15:0] cpn = cp + 1;
wire [15:0] itr = {I, tr};
wire [15:0] cpc = itr + {cout, 8'h00};
wire [ 3:0] branch = {p[ZF], p[CF], p[VF], p[SF]};
wire [ 4:0] NEXT = (cout || cnext) ? LAT : RUN;
reg [ 3:0] alu;
reg [ 1:0] dst_r, src_r;
wire [ 7:0] dst = dst_r == DST_A ? a : dst_r == DST_X ? x : dst_r == DST_Y ? y : s;
wire [ 7:0] src = src_r == SRC_D ? I : src_r == SRC_X ? x : src_r == SRC_Y ? y : a;
wire [8:0] ar =
alu == ORA ? dst | src :
alu == AND ? dst & src :
alu == EOR ? dst ^ src :
alu == ADC ? dst + src + cin :
alu == STA ? dst :
alu == LDA ? src :
alu == CMP ? dst - src :
alu == SBC ? dst - src - !cin :
alu == ASL ? {src[6:0], 1'b0} :
alu == ROL ? {src[6:0], cin} :
alu == LSR ? {1'b0, src[7:1]} :
alu == ROR ? {cin, src[7:1]} :
alu == BIT ? dst & src :
alu == DEC ? src - 1 :
alu == INC ? src + 1 : src;
wire zf = ar[7:0] == 0;
wire sf = ar[7];
wire oadc = (dst[7] ^ src[7] ^ 1'b1) & (dst[7] ^ ar[7]);
wire osbc = (dst[7] ^ src[7] ) & (dst[7] ^ ar[7]);
wire cin = p[CF];
wire carry = ar[8];
wire [7:0] ap =
alu[3:1] == 3'b000_ ||
alu[3:0] == 4'b0010 ||
alu[3:1] == 3'b010_ ||
alu[3:1] == 4'b111_ ? {sf, p[6:2], zf, p[0]} :
alu[3:0] == 4'b0011 ? {sf, oadc, p[5:2], zf, carry} :
alu[3:0] == 4'b0111 ? {sf, osbc, p[5:2], zf, ~carry} :
alu[3:0] == 4'b0110 ? {sf, p[6:2], zf, ~carry} :
alu[3:1] == 3'b100_ ? {sf, p[6:2], zf, src[7]} :
alu[3:1] == 3'b101_ ? {sf, p[6:2], zf, src[0]} :
alu[3:0] == 4'b1100 ? {src[7:6], p[5:2], zf, p[0]} : 8'hFF;
always @(posedge clock)
if (reset_n == 1'b0) begin
t <= BRK;
m <= 0;
n <= 0;
a <= 8'h21;
x <= 8'h83;
y <= 8'h81;
s <= 8'h00;
p <= 8'b0000_0010;
pc <= 16'h0000;
nmitr <= 1'b0;
intr <= IRQ_RST;
end
else if (ce) begin
R <= 0;
W <= 0;
case (t)
LOAD: begin
cout <= 0;
cnext <= 0;
rd <= 1;
n <= 0;
alu <= I[7:5];
intr <= IRQ_BRK;
dst_r <= DST_A;
src_r <= SRC_D;
if (nmitr ^ nmi && nmi) begin
t <= BRK;
intr <= IRQ_NMI;
end else
begin
pc <= pcn;
opcode <= I;
casex (I)
8'b001_000_00: begin t <= JSR; end
8'b010_000_00: begin t <= RTI; end
8'b011_000_00: begin t <= RTS; end
8'b000_000_00: begin t <= BRK; pc <= pc + 2; end
8'bxxx_000_x1: begin t <= NDX; end
8'bxxx_010_x1,
8'b1xx_000_x0: begin t <= RUN; end
8'bxxx_100_x1: begin t <= NDY; end
8'bxxx_110_x1: begin t <= ABY; end
8'bxxx_001_xx: begin t <= ZP; end
8'bxxx_011_xx,
8'b001_000_00: begin t <= ABS; end
8'b10x_101_1x: begin t <= ZPY; end
8'bxxx_101_xx: begin t <= ZPX; end
8'b10x_111_1x: begin t <= ABY; end
8'bxxx_111_xx: begin t <= ABX; end
8'bxxx_100_00: begin t <= REL; end
8'b0xx_010_10: begin t <= RUN; end
default: begin t <= RUN; end
endcase
casex (I)
8'hC0,
8'hC4,
8'hCC: begin alu <= CMP; dst_r <= DST_Y; end
8'hE0,
8'hE4,
8'hEC: begin alu <= CMP; dst_r <= DST_X; end
8'h8A: begin alu <= LDA; src_r <= SRC_X; end
8'h98: begin alu <= LDA; src_r <= SRC_Y; end
8'hAA,
8'hA8: begin alu <= LDA; src_r <= SRC_A; end
8'h24,
8'h2C: begin alu <= BIT; end
8'hCA: begin alu <= DEC; src_r <= SRC_X; end
8'hE8: begin alu <= INC; src_r <= SRC_X; end
8'h88: begin alu <= DEC; src_r <= SRC_Y; end
8'hC8: begin alu <= INC; src_r <= SRC_Y; end
8'h0B, 8'h2B, 8'h4B, 8'h6B: begin alu <= AND; end
8'b0xx_xx1_10: begin alu <= ASL + I[6:5]; end
8'b0xx_010_10: begin alu <= ASL + I[6:5]; src_r <= SRC_A; end
8'b11x_xx1_10: begin alu <= DEC + I[5]; end
endcase
casex (I) 8'b100_xx1_10: D <= x; 8'b100_xx1_00: D <= y; default: D <= a; endcase
casex (I) 8'b100_xxx_01, 8'b100_xx1_x0: rd <= 1'b0; endcase
casex (I) 8'b100xxxxx, 8'b11xxx110, 8'b0xxxx110: cnext <= 1; endcase
end
nmitr <= nmi;
end
NDX: begin t <= NDX2; cp <= Xi[7:0]; m <= 1; pc <= pcn; end
NDX2: begin t <= NDX3; cp <= cpn[7:0]; tr <= I; end
NDX3: begin t <= LAT; cp <= itr; {R,W} <= {rd,~rd}; end
NDY: begin t <= NDY2; cp <= I; m <= 1; pc <= pcn; end
NDY2: begin t <= NDY3; cp <= cpn[7:0]; {cout,tr} <= Yi; end
NDY3: begin t <= NEXT; cp <= cpc; {R,W} <= {rd,~rd}; end
ZP: begin t <= RUN; cp <= I; m <= 1; {R,W} <= {rd,~rd}; pc <= pcn; end
ZPX: begin t <= LAT; cp <= Xi[7:0]; m <= 1; {R,W} <= {rd,~rd}; pc <= pcn; end
ZPY: begin t <= LAT; cp <= Yi[7:0]; m <= 1; {R,W} <= {rd,~rd}; pc <= pcn; end
ABS: begin t <= ABS2; tr <= I; pc <= pcn; end
ABS2: begin
if (opcode == 8'h4C)
begin t <= LOAD; pc <= itr; end
else begin t <= RUN; cp <= itr; pc <= pcn; m <= 1; {R,W} <= {rd,~rd}; end
end
ABX: begin t <= ABXY; tr <= Xi[7:0]; pc <= pcn; cout <= Xi[8]; end
ABY: begin t <= ABXY; tr <= Yi[7:0]; pc <= pcn; cout <= Yi[8]; end
ABXY: begin t <= NEXT; cp <= cpc; pc <= pcn; m <= 1; {R,W} <= {rd,~rd}; end
REL: begin
if (branch[opcode[7:6]] == opcode[5]) begin
t <= pcr[15:8] == pc[15:8] ? REL2 : REL1;
pc <= pcr;
end
else begin pc <= pcn; t <= LOAD; end
end
REL1: begin t <= REL2; end
REL2: begin t <= LOAD; end
LAT: begin t <= RUN; end
RUN: begin
m <= 0;
t <= LOAD;
casex (opcode) 8'bxxx_010_x1, 8'b1xx_000_x0: pc <= pcn; endcase
casex (opcode)
8'h9A: begin s <= x; end
8'hBA: begin x <= s; p[ZF] <= (s == 0); p[SF] <= s[7]; end
8'b101_xx1_10, 8'hA2, 8'hAA, 8'hCA, 8'hE8: begin x <= ar[7:0]; p <= ap; end
8'b101_xx1_00, 8'hA0, 8'hA8, 8'h88, 8'hC8: begin y <= ar[7:0]; p <= ap; end
8'hC0,8'hC4,8'hCC,
8'hE0,8'hE4,8'hEC,
8'h24,8'h2C: begin p <= ap; end
8'b100_xxx_01, 8'b100_xx1_x0: begin end
8'b00x_110_00: p[CF] <= opcode[5];
8'b01x_110_00: p[IF] <= opcode[5];
8'b101_110_00: p[VF] <= 1'b0;
8'b11x_110_00: p[DF] <= opcode[5];
8'bxxx_xxx_01: begin p <= ap; if (alu != CMP) a <= ar[7:0]; end
8'b0xx_010_10,
8'h8A, 8'h98, 8'hEB: begin a <= ar[7:0]; p <= ap; end
8'h0B,
8'h2B: begin a <= ar[7:0]; p <= ap; p[CF] <= ar[7]; end
8'h4B: begin a <= ar[7:1]; {p[CF], p[SF], p[ZF]} <= {ar[0], 1'b0, ar[7:1] == 0}; end
8'h6B: begin a <= {p[CF], ar[7:1]}; {p[VF], p[CF], p[ZF]} <= {ar[6] ^ ar[7], ar[7], {p[CF], ar[7:1]} == 0}; end
8'b0xx_xx1_10,
8'b11x_xx1_10: case (n)
0: begin n <= 1; t <= RUN; W <= 1; D <= I; m <= 1; end
1: begin n <= 2; t <= RUN; W <= 1; D <= ar; m <= 1; p <= ap; end
endcase
8'h6C: case (n)
0: begin n <= 1; m <= 1; t <= RUN; tr <= I; cp[7:0] <= cp[7:0] + 1; R <= 1; end
1: begin pc <= {I, tr}; end
endcase
8'h08, 8'h48: if (n == 0) begin
t <= RUN;
m <= 1;
n <= 1;
cp <= {8'h01, s};
D <= opcode[6] ? a : (p | 8'h30);
W <= 1;
s <= s - 1;
end
8'h68, 8'h28: case (n)
0: begin
t <= RUN;
n <= 1;
m <= 1;
s <= s + 1;
cp[15:8] <= 8'h01;
cp[ 7:0] <= s + 1;
end
1: begin
n <= 2;
t <= RUN;
if (opcode[6]) begin a <= I; p[ZF] <= I == 0; p[SF] <= I[7]; end
else begin p <= I; end
end
endcase
endcase
end
BRK: case (n)
0: begin n <= 1; cp <= {8'h01, s}; W <= 1; s <= s - 1; D <= pc[15:8]; m <= 1; end
1: begin n <= 2; cp[7:0] <= s; W <= 1; s <= s - 1; D <= pc[7:0]; p[BF] <= 1; p[5] <= 1; end
2: begin n <= 3; cp[7:0] <= s; W <= 1; s <= s - 1; D <= p; p[IF] <= 1; end
3: begin n <= 4; cp <= {12'hFFF, 1'b1, intr, 1'b0}; end
4: begin n <= 5; cp[0] <= 1; tr <= I; end
5: begin m <= 0; t <= LOAD; pc <= {I, tr}; end
endcase
JSR: case (n)
0: begin n <= 1; tr <= I; pc <= pcn; cp[15:8] <= 8'h01; end
1: begin n <= 2; cp[7:0] <= s; s <= s - 1; D <= pc[15:8]; W <= 1; pc[15:8] <= I; m <= 1; end
2: begin n <= 3; cp[7:0] <= s; s <= s - 1; D <= pc[ 7:0]; W <= 1; end
3: begin n <= 4; pc[7:0] <= tr; m <= 0; end
4: begin t <= LOAD; end
endcase
RTS: case (n)
0: begin n <= 1; m <= 1; cp[7:0] <= s + 1; s <= s + 1; cp[15:8] <= 8'h01; end
1: begin n <= 2; pc[7:0] <= I; cp[7:0] <= s + 1; s <= s + 1; end
2: begin n <= 3; pc <= {I, pc[7:0]} + 1; m <= 0; end
3: begin n <= 4; end
4: begin t <= LOAD; end
endcase
RTI: case (n)
0: begin n <= 1; cp[7:0] <= s + 1; s <= s + 1; cp[15:8] <= 8'h01; m <= 1; end
1: begin n <= 2; cp[7:0] <= s + 1; s <= s + 1; p <= I; end
2: begin n <= 3; cp[7:0] <= s + 1; s <= s + 1; pc[7:0] <= I; end
3: begin n <= 4; pc[15:8] <= I; m <= 0; end
4: begin t <= LOAD; end
endcase
endcase
end
endmodule