Compare commits

...

10 Commits

Author SHA1 Message Date
David Banks
ef2cc5ab45 Merge pull request #1 from willisblackburn/master
Apply RDY fix from cpu.v to cpu_65c02.v
2025-12-14 14:51:12 +00:00
Willis Blackburn
6b47307d48 Applied fix to DIHOLD logic that was already applied to original cpu.v core 2025-12-13 12:02:48 -05:00
David Banks
a5f605d00d 65C02: fix a bug with TSB/TRB when RDY used
Change-Id: I4abeb6b92935b2f82d7803f9f29f409a47e296b9
2021-06-12 16:12:21 +01:00
David Banks
4f141c7a13 Merge remote-tracking branch 'upstream/master'
Change-Id: I754ba57320720ff7f46e34d80b1719a37dcd3d1e
2021-06-10 11:19:41 +01:00
David Banks
fec47c5427 Fix synthesis warnings, add SYNC output
Change-Id: If14a39f0621803c7105d6289aca9621c2ff67b99
2021-06-10 11:17:32 +01:00
David Banks
9c1181b5e1 Remove trailing whitespace
Change-Id: I25e1b337211032bb572351f8a02d3b05013330ea
2021-06-10 11:16:20 +01:00
Arlet Ottens
e6f361d764 Updated README 2020-10-21 12:12:56 +02:00
Arlet Ottens
a11631082b Added fix for 1-cycle RDY bug 2016-08-19 20:19:40 +02:00
David Banks
791ed43bae Updated README and 65C02 Copyright
Change-Id: I88efe06d7959744165c35b54edf6e3633ff05406
2016-08-01 12:36:46 +01:00
David Banks
4053c24317 Arlet 65C02 WIP: Implement correct 65C02 BCD N/Z flags (optional, disabled)
Change-Id: Ib72bcc1a100ee3c642048ce195bf1424feb4bf50
2016-08-01 12:02:45 +01:00
4 changed files with 243 additions and 134 deletions

8
README
View File

@@ -1,8 +0,0 @@
A Verilog HDL version of the old MOS 6502 CPU.
Note: the 6502 core assumes a synchronous memory. This means that valid
data (DI) is expected on the cycle *after* valid address. This allows
direct connection to (Xilinx) block RAMs. When using asynchronous memory,
I suggest registering the address/control lines for glitchless output signals.
Have fun.

69
README.md Normal file
View File

@@ -0,0 +1,69 @@
========================================================
A Verilog HDL version of the old MOS 6502 and 65C02 CPUs
========================================================
Original 6502 core by Arlet Ottens
65C02 extensions by David Banks and Ed Spittles
==========
6502 Core
==========
Arlet's original 6502 core (cpu.v) is unchanged.
Note: the 6502/65C02 cores assumes a synchronous memory. This means
that valid data (DI) is expected on the cycle *after* valid
address. This allows direct connection to (Xilinx) block RAMs. When
using asynchronous memory, I suggest registering the address/control
lines for glitchless output signals.
[Also check out my new 65C02 project](https://github.com/Arlet/verilog-65c02)
Have fun.
==========
65C02 Core
==========
A second core (cpu_65c02.v) has been added, based on Arlet's 6502
core, with additional 65C02 instructions and addressing modes:
- PHX, PHY, PLX, PLY
- BRA
- INC A, DEC A
- (zp) addressing mode
- STZ
- BIT zpx, absx, imm
- TSB/TRB
- JMP (,X)
- NOPs (optional)
- 65C02 BCD N/Z flags (optional, disabled)
The Rockwell/WDC specific instructions (RMB/SMB/BBR/BBS/WAI/STP) are
not currently implemented
The 65C02 core passes the Dormann 6502 test suite, and also passes the
Dormann 65C02 test suite if the optional support for NOPs and 65C02
BCD flags is enabled.
It has been tested as a BBC Micro "Matchbox" 65C02 Co Processor, in a
XC6SLX9-2 FPGA, running at 80MHz using 64KB of internel block RAM. It
just meets timing at 80MHz in this environment. It successfully runs
BBC Basic IV and Tube Elite.
============
Known Issues
============
The Matchbox Co Processor needed one wait state (via RDY) to be added
to each ROM access (only needed early in the boot process, as
eventually everything runs from RAM). The DIHOLD logic did not work
correctly with a single wait state, and so has been commented out.
I now believe the correct fix is actually just:
always @(posedge clk )
if( RDY )
DIHOLD <= DI;
assign DIMUX = ~RDY ? DIHOLD : DI;

15
cpu.v
View File

@@ -836,15 +836,6 @@ always @(posedge clk )
* time to read the IR again before the next decode. * time to read the IR again before the next decode.
*/ */
reg RDY1 = 1;
always @(posedge clk )
RDY1 <= RDY;
always @(posedge clk )
if( ~RDY && RDY1 )
DIHOLD <= DI;
always @(posedge clk ) always @(posedge clk )
if( reset ) if( reset )
IRHOLD_valid <= 0; IRHOLD_valid <= 0;
@@ -859,7 +850,11 @@ always @(posedge clk )
assign IR = (IRQ & ~I) | NMI_edge ? 8'h00 : assign IR = (IRQ & ~I) | NMI_edge ? 8'h00 :
IRHOLD_valid ? IRHOLD : DIMUX; IRHOLD_valid ? IRHOLD : DIMUX;
assign DIMUX = ~RDY1 ? DIHOLD : DI; always @(posedge clk )
if( RDY )
DIHOLD <= DI;
assign DIMUX = ~RDY ? DIHOLD : DI;
/* /*
* Microcode state machine * Microcode state machine

View File

@@ -1,5 +1,7 @@
/* /*
* verilog model of 6502 CPU. * verilog model of 65C02 CPU.
*
* Based on original 6502 "Arlet 6502 Core" by Arlet Ottens
* *
* (C) Arlet Ottens, <arlet@c-scape.nl> * (C) Arlet Ottens, <arlet@c-scape.nl>
* *
@@ -7,6 +9,14 @@
* keep this message, and the copyright notice. This code is provided "as is", * keep this message, and the copyright notice. This code is provided "as is",
* without any warranties of any kind. * without any warranties of any kind.
* *
* Support for 65C02 instructions and addressing modes by David Banks and Ed Spittles
*
* (C) 2016 David Banks and Ed Spittles
*
* Feel free to use this code in any project (commercial or not), as long as you
* keep this message, and the copyright notice. This code is provided "as is",
* without any warranties of any kind.
*
*/ */
/* /*
@@ -28,7 +38,17 @@
`define IMPLEMENT_NOPS `define IMPLEMENT_NOPS
module cpu_65c02( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY ); /*
* Two things were needed to correctly implement 65C02 BCD arithmentic
* 1. The Z flag needs calculating over the BCD adjusted ALU output
* 2. The N flag needs calculating over the BCD adjusted ALU output
*
* If IMPLEMENT_CORRECT_BCD_FLAGS is defined, this additional logic is added
*/
// `define IMPLEMENT_CORRECT_BCD_FLAGS
module cpu_65c02( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY, SYNC );
input clk; // CPU clock input clk; // CPU clock
input reset; // reset signal input reset; // reset signal
@@ -39,6 +59,7 @@ output WE; // write enable
input IRQ; // interrupt request input IRQ; // interrupt request
input NMI; // non-maskable interrupt request input NMI; // non-maskable interrupt request
input RDY; // Ready signal. Pauses CPU when RDY=0 input RDY; // Ready signal. Pauses CPU when RDY=0
output reg SYNC; // AB is first cycle of the intruction
/* /*
* internal signals * internal signals
@@ -65,9 +86,11 @@ reg D = 0; // decimal flag
reg V = 0; // overflow flag reg V = 0; // overflow flag
reg N = 0; // negative flag reg N = 0; // negative flag
wire AZ; // ALU Zero flag wire AZ; // ALU Zero flag
wire AZ1; // ALU Zero flag (BCD adjusted)
reg AZ2; // ALU Second Zero flag, set using TSB/TRB semantics reg AZ2; // ALU Second Zero flag, set using TSB/TRB semantics
wire AV; // ALU overflow flag wire AV; // ALU overflow flag
wire AN; // ALU negative flag wire AN; // ALU negative flag
wire AN1; // ALU negative flag (BCD adjusted)
wire HC; // ALU half carry wire HC; // ALU half carry
reg [7:0] AI; // ALU Input A reg [7:0] AI; // ALU Input A
@@ -75,6 +98,7 @@ reg [7:0] BI; // ALU Input B
wire [7:0] DI; // Data In wire [7:0] DI; // Data In
wire [7:0] IR; // Instruction register wire [7:0] IR; // Instruction register
reg [7:0] DO; // Data Out reg [7:0] DO; // Data Out
wire [7:0] AO; // ALU output after BCD adjustment
reg WE; // Write Enable reg WE; // Write Enable
reg CI; // Carry In reg CI; // Carry In
wire CO; // Carry Out wire CO; // Carry Out
@@ -160,7 +184,6 @@ reg sed; // set decimal
reg cli; // clear interrupt reg cli; // clear interrupt
reg sei; // set interrupt reg sei; // set interrupt
reg clv; // clear overflow reg clv; // clear overflow
reg brk; // doing BRK
reg res; // in reset reg res; // in reset
@@ -467,7 +490,7 @@ always @*
BRK2: DO = (IRQ | NMI_edge) ? (P & 8'b1110_1111) : P; BRK2: DO = (IRQ | NMI_edge) ? (P & 8'b1110_1111) : P;
default: DO = store_zero ? 0 : regfile; default: DO = store_zero ? 8'b0 : regfile;
endcase endcase
/* /*
@@ -554,6 +577,20 @@ always @* begin
endcase endcase
end end
assign AO = { ADD[7:4] + ADJH, ADD[3:0] + ADJL };
`ifdef IMPLEMENT_CORRECT_BCD_FLAGS
assign AN1 = AO[7];
assign AZ1 = ~|AO;
`else
assign AN1 = AN;
assign AZ1 = AZ;
`endif
/* /*
* write to a register. Usually this is the (BCD corrected) output of the * write to a register. Usually this is the (BCD corrected) output of the
* ALU, but in case of the JSR0 we use the S register to temporarily store * ALU, but in case of the JSR0 we use the S register to temporarily store
@@ -562,7 +599,7 @@ end
*/ */
always @(posedge clk) always @(posedge clk)
if( write_register & RDY ) if( write_register & RDY )
AXYS[regsel] <= (state == JSR0) ? DIMUX : { ADD[7:4] + ADJH, ADD[3:0] + ADJL }; AXYS[regsel] <= (state == JSR0) ? DIMUX : AO;
/* /*
* register select logic. This determines which of the A, X, Y or * register select logic. This determines which of the A, X, Y or
@@ -692,7 +729,7 @@ always @*
BRA1: AI = ABH; // don't use PCH in case we're BRA1: AI = ABH; // don't use PCH in case we're
FETCH: AI = load_only ? 0 : regfile; FETCH: AI = load_only ? 8'b0 : regfile;
DECODE, DECODE,
ABS1: AI = 8'hxx; // don't care ABS1: AI = 8'hxx; // don't care
@@ -751,11 +788,11 @@ always @*
READ, READ,
REG: CI = rotate ? C : REG: CI = rotate ? C :
shift ? 0 : inc; shift ? 1'b0 : inc;
FETCH: CI = rotate ? C : FETCH: CI = rotate ? C :
compare ? 1 : compare ? 1'b1 :
(shift | load_only) ? 0 : C; (shift | load_only) ? 1'b0 : C;
PULL0, PULL0,
RTI0, RTI0,
@@ -797,6 +834,7 @@ always @(posedge clk )
* Special Z flag got TRB/TSB * Special Z flag got TRB/TSB
*/ */
always @(posedge clk) always @(posedge clk)
if (RDY)
AZ2 <= ~|(AI & regfile); AZ2 <= ~|(AI & regfile);
/* /*
@@ -805,26 +843,26 @@ always @(posedge clk)
always @(posedge clk) always @(posedge clk)
if( state == WRITE) if( state == WRITE)
Z <= txb_ins ? AZ2 : AZ; Z <= txb_ins ? AZ2 : AZ1;
else if( state == RTI2 ) else if( state == RTI2 )
Z <= DIMUX[1]; Z <= DIMUX[1];
else if( state == DECODE ) begin else if( state == DECODE ) begin
if( plp ) if( plp )
Z <= ADD[1]; Z <= ADD[1];
else if( (load_reg & (regsel != SEL_S)) | compare | bit_ins ) else if( (load_reg & (regsel != SEL_S)) | compare | bit_ins )
Z <= AZ; Z <= AZ1;
end end
always @(posedge clk) always @(posedge clk)
if( state == WRITE && ~txb_ins) if( state == WRITE && ~txb_ins)
N <= AN; N <= AN1;
else if( state == RTI2 ) else if( state == RTI2 )
N <= DIMUX[7]; N <= DIMUX[7];
else if( state == DECODE ) begin else if( state == DECODE ) begin
if( plp ) if( plp )
N <= ADD[7]; N <= ADD[7];
else if( (load_reg & (regsel != SEL_S)) | compare ) else if( (load_reg & (regsel != SEL_S)) | compare )
N <= AN; N <= AN1;
end else if( state == FETCH && bit_ins_nv ) end else if( state == FETCH && bit_ins_nv )
N <= DIMUX[7]; N <= DIMUX[7];
@@ -878,15 +916,6 @@ always @(posedge clk )
* time to read the IR again before the next decode. * time to read the IR again before the next decode.
*/ */
//reg RDY1 = 1;
//always @(posedge clk )
// RDY1 <= RDY;
//always @(posedge clk )
// if( ~RDY && RDY1 )
// DIHOLD <= DI;
always @(posedge clk ) always @(posedge clk )
if( reset ) if( reset )
IRHOLD_valid <= 0; IRHOLD_valid <= 0;
@@ -901,9 +930,11 @@ always @(posedge clk )
assign IR = (IRQ & ~I) | NMI_edge ? 8'h00 : assign IR = (IRQ & ~I) | NMI_edge ? 8'h00 :
IRHOLD_valid ? IRHOLD : DIMUX; IRHOLD_valid ? IRHOLD : DIMUX;
//assign DIMUX = ~RDY1 ? DIHOLD : DI; always @(posedge clk )
if( RDY )
DIHOLD <= DI;
assign DIMUX = DI; assign DIMUX = ~RDY ? DIHOLD : DI;
/* /*
* Microcode state machine * Microcode state machine
@@ -1032,6 +1063,29 @@ always @(posedge clk or posedge reset)
endcase endcase
/*
* Sync state machine
*/
always @(posedge clk or posedge reset)
if( reset )
SYNC <= 1'b0;
else if( RDY ) case( state )
BRA0 : SYNC <= !cond_true;
BRA1 : SYNC <= !(CO ^ backwards);
BRA2,
FETCH,
REG,
PUSH1,
PULL2,
RTI4,
JMP1,
BRA2 : SYNC <= 1'b1;
default: SYNC <= 1'b0;
endcase
//assign SYNC = state == DECODE;
/* /*
* Additional control signals * Additional control signals
*/ */
@@ -1323,7 +1377,6 @@ always @(posedge clk )
clv <= (IR == 8'hb8); clv <= (IR == 8'hb8);
cld <= (IR == 8'hd8); cld <= (IR == 8'hd8);
sed <= (IR == 8'hf8); sed <= (IR == 8'hf8);
brk <= (IR == 8'h00);
end end
always @(posedge clk) always @(posedge clk)