2 // Copyright (C) 2008 Tomasz Malesinski <tmal@mimuw.edu.pl>
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 // TODO: separate decoding of addressing mode and execution of instruction
19 // TODO: constants for opcodes
20 // TODO: on RESET, stb_o and cyc_o=0 (Wishbonem p.38)
22 module cpu6502_alu(a
, b
, op
, out
, cin
, cout
, vout
, d
);
41 parameter [3:0] OPOR
= 4'b0000;
42 parameter [3:0] OPAND
= 4'b0001;
43 parameter [3:0] OPEOR
= 4'b0010;
44 parameter [3:0] OPADC
= 4'b0011;
45 parameter [3:0] OPA
= 4'b0100;
46 parameter [3:0] OPB
= 4'b0101;
47 parameter [3:0] OPCMP
= 4'b0110;
48 parameter [3:0] OPSBC
= 4'b0111;
49 parameter [3:0] OPASL
= 4'b1000;
50 parameter [3:0] OPROL
= 4'b1001;
51 parameter [3:0] OPLSR
= 4'b1010;
52 parameter [3:0] OPROR
= 4'b1011;
53 parameter [3:0] OPDEC
= 4'b1110;
54 parameter [3:0] OPINC
= 4'b1111;
56 always @ (a
or b
or op
or cin
or d
) begin
57 cout
= 0; // don't care
59 // TODO: v, decimal mode
64 OPADC
: {cout
, out
} = {1'b0, a
} + {1'b0, b
} + cin
;
67 OPCMP
: {cout
, out
} = {1'b0, a
} + {1'b0, ~b
} + 1;
68 OPSBC
: {cout
, out
} = {1'b0, a
} + {1'b0, ~b
} + cin
;
69 OPASL
: {cout
, out
} = {a
, 1'b0};
70 OPROL
: {cout
, out
} = {a
, cin
};
71 OPLSR
: {out
, cout
} = {1'b0, a
};
72 OPROR
: {out
, cout
} = {cin
, a
};
76 out
= 0; // don't care
81 endmodule // cpu6502_alu
83 module cpu6502_adr(clk
, rst
, halt
, dat_i
,
84 regx
, regy
, regs
, pc
, dreg
,
85 adro_sel
, indx_sel
, base_sel
, zp
,
90 input clk
, rst
, halt
, dat_i
;
91 input regx
, regy
, regs
, pc
, dreg
;
92 input adro_sel
, indx_sel
, base_sel
, zp
;
94 input low_ld
, high_ld
;
111 wire [1:0] interrupt
;
112 wire low_ld
, high_ld
;
117 parameter [2:0] ADRO_PC
= 3'b000;
118 parameter [2:0] ADRO_BASE
= 3'b001;
119 parameter [2:0] ADRO_BASEIND
= 3'b010;
120 parameter [2:0] ADRO_SP
= 3'b011;
121 parameter [2:0] ADRO_VECL
= 3'b100;
122 parameter [2:0] ADRO_VECH
= 3'b101;
123 parameter [2:0] ADRO_BASE1
= 3'b110;
130 assign ind
= indx_sel ? regx
: regy
;
131 assign base
= base_sel ? dreg
: areg
[7:0];
132 assign sum
= base
+ ind
;
133 assign adr_c
= sum
[8];
135 always @ (adro_sel
or pc
or zp
or areg
or base
or sum
or regs
or
138 ADRO_PC
: adr_out
= pc
;
140 adr_out
[15:8] = zp ?
0 : areg
[15:8];
141 adr_out
[7:0] = base
[7:0];
144 adr_out
[15:8] = zp ?
0 : areg
[15:8];
145 adr_out
[7:0] = base
[7:0] + 1;
148 adr_out
[15:8] = zp ?
{7'h0
, sum
[8]} : areg
[15:8] + sum
[8];
149 adr_out
[7:0] = sum
[7:0];
155 ADRO_VECL
: adr_out
= { 12'hfff
, 1'b1, interrupt
, 1'b0 };
156 ADRO_VECH
: adr_out
= { 12'hfff
, 1'b1, interrupt
, 1'b1 };
157 default: adr_out
= pc
; // don't care
161 always @ (posedge clk
) begin
170 endmodule // cpu6502_adr
173 module cpu6502(rst_i
,
216 wire [7:0] pc_rel_low
;
239 parameter [1:0] INT_NONE
= 2'b00;
240 parameter [1:0] INT_NMI
= 2'b01;
241 parameter [1:0] INT_RES
= 2'b10;
242 parameter [1:0] INT_IRQ
= 2'b11;
247 parameter [3:0] MODE_IMP
= 4'b0000;
248 parameter [3:0] MODE_IMM
= 4'b0001;
249 parameter [3:0] MODE_INDX
= 4'b0010;
250 parameter [3:0] MODE_INDY
= 4'b0011;
251 parameter [3:0] MODE_ZP
= 4'b0100;
252 parameter [3:0] MODE_ABS
= 4'b0101;
253 parameter [3:0] MODE_ZPX
= 4'b0110;
254 parameter [3:0] MODE_ZPY
= 4'b0111;
255 parameter [3:0] MODE_ABSX
= 4'b1000;
256 parameter [3:0] MODE_ABSY
= 4'b1001;
258 parameter [3:0] INSTR_ALU
= 4'b0000;
259 parameter [3:0] INSTR_RMW
= 4'b0001;
260 parameter [3:0] INSTR_STA
= 4'b0010;
261 parameter [3:0] INSTR_STXY
= 4'b0011;
262 parameter [3:0] INSTR_LDXY
= 4'b0100;
263 parameter [3:0] INSTR_CPXY
= 4'b0101;
264 parameter [3:0] INSTR_BIT
= 4'b0110;
265 parameter [3:0] INSTR_OTHER
= 4'b1111;
279 reg [3:0] instr_type
;
283 reg lastcyc
, skipcyc
, inc_rmwcyccnt
;
284 reg regxld
, regyld
, regs_ld
;
286 reg nld
, zld
, cld
, vld
, fld
;
289 parameter [3:0] ALUA_A
= 3'b000;
290 parameter [3:0] ALUA_X
= 3'b001;
291 parameter [3:0] ALUA_Y
= 3'b010;
292 parameter [3:0] ALUA_S
= 3'b011;
293 parameter [3:0] ALUA_D
= 3'b100;
300 wire alucout
, alud
, aluvout
;
302 parameter [2:0] DATO_ACC
= 3'b000;
303 parameter [2:0] DATO_PCH
= 3'b001;
304 parameter [2:0] DATO_PCL
= 3'b010;
305 parameter [2:0] DATO_FLG
= 3'b011;
306 parameter [2:0] DATO_X
= 3'b100;
307 parameter [2:0] DATO_Y
= 3'b101;
308 parameter [2:0] DATO_D
= 3'b110;
313 assign halt
= stb_o
&& !ack_i
;
315 always @ (alua_sel
or acc
or regx
or regy
or regs
or dreg
) begin
328 always @ (ir
or regf
) begin
330 2'b00: br_flag
= regf
[N_IND
];
331 2'b01: br_flag
= regf
[V_IND
];
332 2'b10: br_flag
= regf
[C_IND
];
333 2'b11: br_flag
= regf
[Z_IND
];
335 br_taken
= (br_flag
== ir
[5]);
340 if (ir
== 'h4c || ir
== 'h6c || ir
== 'h20 || ir
== 'h40 ||
341 ir
== 'h60 || ir
[4:0] == 'h10
)
343 else if (ir
[4:0] == 'h00
) begin
345 end else if (ir
[4:0] == 'h01
) begin
346 adr_mode
= MODE_INDX
;
347 end else if (ir
[4:0] == 'h02
) begin
349 end else if (ir
[4:0] == 'h04 || ir
[4:0] == 'h05 || ir
[4:0] == 'h06
) begin
351 end else if (ir
[4:0] == 'h08
) begin
353 end else if (ir
[4:0] == 'h09
) begin
355 end else if (ir
[4:0] == 'h0a
) begin
357 end else if (ir
[4:0] == 'h0c || ir
[4:0] == 'h0d || ir
[4:0] == 'h0e
) begin
359 end else if (ir
[4:0] == 'h11
) begin
360 adr_mode
= MODE_INDY
;
361 end else if (ir
[4:0] == 'h14 || ir
[4:0] == 'h15 || ir
[4:0] == 'h16
) begin
362 if (ir
== 'h96 || ir
== 'hb6
)
366 end else if (ir
[4:0] == 'h19
) begin
367 adr_mode
= MODE_ABSY
;
368 end else if (ir
[4:0] == 'h1c || ir
[4:0] == 'h1d || ir
[4:0] == 'h1e
) begin
370 adr_mode
= MODE_ABSY
;
372 adr_mode
= MODE_ABSX
;
377 instr_type
= INSTR_OTHER
;
378 if (ir
== 'h89 || ir
== 'h24 || ir
== 'h2c
)
379 instr_type
= INSTR_BIT
;
380 else if (ir
[1:0] == 'h01
) begin
381 if (ir
[7:5] == 3'b100)
382 instr_type
= INSTR_STA
;
384 instr_type
= INSTR_ALU
;
385 end else if (ir
== 'h84 || ir
== 'h86 || ir
== 'h8c || ir
== 'h8e ||
386 ir
== 'h94 || ir
== 'h96
)
387 instr_type
= INSTR_STXY
;
388 else if (ir
== 'ha0 || ir
== 'ha2 || ir
== 'ha4 || ir
== 'ha6 ||
389 ir
== 'hac || ir
== 'hae ||
390 ir
== 'hb4 || ir
== 'hb6 || ir
== 'hbc || ir
== 'hbe
)
391 instr_type
= INSTR_LDXY
;
392 else if (ir
== 'hc0 || ir
== 'hc4 || ir
== 'hcc ||
393 ir
== 'he0 || ir
== 'he4 || ir
== 'hec
)
394 instr_type
= INSTR_CPXY
;
395 else if (ir
[2:0] == 'h06
)
396 instr_type
= INSTR_RMW
;
399 // TODO: look at this list
400 always @ (interrupt
or cyccnt
or rmwcyccnt
or ir
or br_taken
or
401 pc_c
or dreg
or adr_mode
or instr_type
or adr_c
) begin
435 aluop
= 0; // don't care
440 if (cyccnt
== 0) begin
441 adro_sel
= adr.ADRO_PC
;
443 pcinc
= (interrupt
== INT_NONE
);
444 end else if (ir
== 'h00 || interrupt
!= INT_NONE
)
449 adro_sel
= adr.ADRO_SP
;
458 adro_sel
= adr.ADRO_SP
;
467 adro_sel
= adr.ADRO_SP
;
476 adro_sel
= adr.ADRO_VECL
;
481 adro_sel
= adr.ADRO_VECH
;
486 else if (adr_mode
== MODE_INDX
) begin
489 adro_sel
= adr.ADRO_PC
;
494 // TODO: add X to DREG here?
495 adro_sel
= adr.ADRO_BASE
;
501 adro_sel
= adr.ADRO_BASEIND
;
512 adro_sel
= adr.ADRO_BASEIND
;
520 adro_sel
= adr.ADRO_BASE
;
525 end else if (adr_mode
== MODE_ZP
) begin
528 adro_sel
= adr.ADRO_PC
;
533 adro_sel
= adr.ADRO_BASE
;
539 end else if (adr_mode
== MODE_IMM
) begin
542 adro_sel
= adr.ADRO_PC
;
546 end else if (adr_mode
== MODE_ABS
) begin
551 adro_sel
= adr.ADRO_BASE
;
556 end else if (adr_mode
== MODE_INDY
) begin
559 adro_sel
= adr.ADRO_PC
;
564 adro_sel
= adr.ADRO_BASE
;
574 adro_sel
= adr.ADRO_BASE
;
583 adro_sel
= adr.ADRO_BASEIND
;
589 end else if (adr_mode
== MODE_ZPX || adr_mode
== MODE_ZPY
) begin
592 adro_sel
= adr.ADRO_PC
;
597 adro_sel
= adr.ADRO_BASE
;
602 adro_sel
= adr.ADRO_BASEIND
;
604 adr_indx_sel
= (adr_mode
== MODE_ZPX
);
609 end else if (adr_mode
== MODE_ABSX || adr_mode
== MODE_ABSY
) begin
618 adro_sel
= adr.ADRO_BASEIND
;
619 adr_indx_sel
= (adr_mode
== MODE_ABSX
);
620 // TODO what about RMW?
621 adr_mode_done
= (adr_c || instr_type
== INSTR_STA ||
622 instr_type
== INSTR_STXY
) ?
0 : 1;
626 adro_sel
= adr.ADRO_BASEIND
;
627 adr_indx_sel
= (adr_mode
== MODE_ABSX
);
632 end else if (ir
== 'h4c
) begin
646 end else if (ir
== 'h6c
) begin
653 adro_sel
= adr.ADRO_BASE
;
658 adro_sel
= adr.ADRO_BASE1
;
664 end else if (ir
== 'h20
) begin
672 adro_sel
= adr.ADRO_SP
;
677 adro_sel
= adr.ADRO_SP
;
686 adro_sel
= adr.ADRO_SP
;
700 end else if (ir
== 'h40 || ir
== 'h60
) begin
713 regs_ld
= (ir
== 'h40
);
714 adro_sel
= (ir
== 'h40
) ? adr.ADRO_SP
: adr.ADRO_PC
;
720 adro_sel
= adr.ADRO_SP
;
728 adro_sel
= adr.ADRO_SP
;
732 pcinc
= (ir
== 'h60
);
735 end else if (ir
[4:0] == 'h10
) begin
740 adro_sel
= adr.ADRO_PC
;
749 lastcyc
= (pc_c
& dreg
[7]) |
(~pc_c
& ~dreg
[7]);
757 end else if (ir
== 'h48 || ir
== 'h08
) begin
763 adro_sel
= adr.ADRO_SP
;
767 dato_sel
= (ir
== 'h48
) ? DATO_ACC
: DATO_FLG
;
772 end else if (ir
== 'h68 || ir
== 'h28
) begin
785 accld
= (ir
== 'h68
);
787 adro_sel
= adr.ADRO_SP
;
791 end else if (ir
== 'h18 || ir
== 'h38 || ir
== 'h58 || ir
== 'h78 ||
792 ir
== 'hb8 || ir
== 'hd8 || ir
== 'hf8
) begin
794 // Everything is done in regf process.
797 end else if (ir
== 'h0a || ir
== 'h2a || ir
== 'h4a || ir
== 'h6a
) begin
799 aluop
= {1'b1, ir
[7:5]};
806 end else if (ir
== 'hca || ir
== 'h88
) begin
809 regxld
= (ir
== 'hca
);
810 regyld
= (ir
== 'h88
);
811 alua_sel
= (ir
== 'hca
) ? ALUA_X
: ALUA_Y
;
816 end else if (ir
== 'he8 || ir
== 'hc8
) begin
819 regxld
= (ir
== 'he8
);
820 regyld
= (ir
== 'hc8
);
821 alua_sel
= (ir
== 'he8
) ? ALUA_X
: ALUA_Y
;
826 end else if (ir
== 'h8a || ir
== 'h98
) begin
830 alua_sel
= (ir
== 'h8a
) ? ALUA_X
: ALUA_Y
;
835 end else if (ir
== 'haa || ir
== 'ha8
) begin
839 regxld
= (ir
== 'haa
);
840 regyld
= (ir
== 'ha8
);
845 end else if (ir
== 'h9a
) begin
852 end else if (ir
== 'hba
) begin
861 end else if (ir
== 'hea
) begin
867 if (adr_mode_done
) begin
868 if (instr_type
== INSTR_ALU
) begin
869 aluop
= {1'b0, ir
[7:5]};
873 if (ir
[7:5] == alu.OPCMP
) begin
877 end else if (ir
[7:5] == alu.OPADC
) begin
878 // TODO: delay with decimal mode
881 end else if (ir
[7:5] == alu.OPSBC
) begin
886 end else if (instr_type
== INSTR_RMW
) begin
896 aluop
= {1'b1, ir
[7:5]};
908 end else if (instr_type
== INSTR_LDXY
) begin
910 regxld
= (ir
[1] == 1);
911 regyld
= (ir
[1] == 0);
915 end else if (instr_type
== INSTR_STA
) begin
919 end else if (instr_type
== INSTR_STXY
) begin
920 dato_sel
= (ir
[1] == 1) ? DATO_X
: DATO_Y
;
923 end else if (instr_type
== INSTR_CPXY
) begin
925 alua_sel
= ir
[5] ? ALUA_X
: ALUA_Y
;
931 end else if (instr_type
== INSTR_BIT
) begin
932 // N and V load from dat_i on INSTR_BIT
942 always @ (posedge clk_i
)
944 interrupt
<= INT_RES
;
945 else if (!halt
&& lastcyc
== 1)
947 interrupt
<= INT_NMI
;
948 else if (irq
&& !regf
[I_IND
])
949 interrupt
<= INT_IRQ
;
951 interrupt
<= INT_NONE
;
953 always @ (posedge clk_i
)
956 // This condition can be added to nmi_pending above to speed up
958 else if (nmi
&& !last_nmi
)
960 else if (!halt
&& lastcyc
== 1)
963 always @ (posedge clk_i
)
966 always @ (posedge clk_i
)
972 else if (skipcyc
== 1)
973 cyccnt
<= cyccnt
+ 2;
975 cyccnt
<= cyccnt
+ 1;
977 always @ (posedge clk_i
)
983 else if (inc_rmwcyccnt
)
984 rmwcyccnt
<= rmwcyccnt
+ 1;
986 assign {pc_c
, pc_rel_low
} = pc
[7:0] + dreg
;
988 always @ (posedge clk_i
)
990 pc
<= 0; // Only to avoid undefined address.
997 else if (pc_rel_low_ld
)
998 pc
[7:0] <= pc_rel_low
;
1004 else if (pc_rel_high_ld
)
1005 pc
[15:8] <= dreg
[7] ? pc
[15:8] - 1 : pc
[15:8] + 1;
1008 always @ (posedge clk_i
)
1009 if (!halt
&& irld
== 1)
1012 always @ (posedge clk_i
)
1013 if (!halt
&& dreg_ld
== 1)
1016 always @ (posedge clk_i
)
1017 if (!halt
&& accld
== 1)
1020 always @ (posedge clk_i
)
1021 if (!halt
&& regxld
== 1)
1024 always @ (posedge clk_i
)
1025 if (!halt
&& regyld
== 1)
1028 always @ (posedge clk_i
)
1029 if (!halt
&& regs_ld
== 1)
1032 always @ (posedge clk_i
) begin
1035 regf
[5:4] <= dat_i
[7:6];
1036 regf
[3:0] <= dat_i
[3:0];
1040 regf
[C_IND
] <= alucout
;
1041 else if (ir
== 'h18
&& lastcyc
== 1)
1043 else if (ir
== 'h38
&& lastcyc
== 1)
1047 regf
[V_IND
] <= (instr_type
== INSTR_BIT
) ? dat_i
[6] : aluvout
;
1048 else if (ir
== 'hb8
&& lastcyc
== 1)
1052 regf
[Z_IND
] <= (aluout
== 0);
1055 regf
[N_IND
] <= (instr_type
== INSTR_BIT
) ? dat_i
[7] : aluout
[7];
1057 if (ir
== 'h58
&& lastcyc
== 1)
1059 else if ((ir
== 'h78
&& lastcyc
== 1) ||
1060 (interrupt
!= INT_NONE
&& cyccnt
== 5))
1063 if (ir
== 'hd8
&& lastcyc
== 1)
1065 else if (ir
== 'hf8
&& lastcyc
== 1)
1070 cpu6502_alu
alu(.
a(alua
), .
b(alub
), .
op(aluop
),
1071 .
out(aluout
), .
cin(regf
[C_IND
]), .
cout(alucout
),
1075 cpu6502_adr
adr(.
clk(clk_i
), .
rst(rst_i
), .
halt(halt
), .
dat_i(dat_i
),
1076 .
regx(regx
), .
regy(regy
), .
regs(regs
), .
pc(pc
),
1078 .
adro_sel(adro_sel
), .
indx_sel(adr_indx_sel
),
1079 .
base_sel(adr_base_sel
),
1081 .
interrupt(interrupt
),
1082 .
low_ld(adr_low_ld
), .
high_ld(adr_high_ld
),
1086 always @ (dato_sel
or acc
or pc
or regf
or regx
or regy
or dreg
) begin
1088 DATO_ACC
: dat_o
= acc
;
1089 DATO_PCL
: dat_o
= pc
[7:0];
1090 DATO_PCH
: dat_o
= pc
[15:8];
1091 DATO_FLG
: dat_o
= {regf
[5:4], 1'b1, (ir
== 'h00
), regf
[3:0]};
1092 DATO_X
: dat_o
= regx
;
1093 DATO_Y
: dat_o
= regy
;
1094 DATO_D
: dat_o
= dreg
;
1095 default: dat_o
= acc
;
1099 endmodule // cpu6502