Compare commits
10 Commits
bf3dad7af1
...
ef2cc5ab45
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef2cc5ab45 | ||
|
|
6b47307d48 | ||
|
|
a5f605d00d | ||
|
|
4f141c7a13 | ||
|
|
fec47c5427 | ||
|
|
9c1181b5e1 | ||
|
|
e6f361d764 | ||
|
|
a11631082b | ||
|
|
791ed43bae | ||
|
|
4053c24317 |
8
README
8
README
@@ -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
69
README.md
Normal 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
15
cpu.v
@@ -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
|
||||||
|
|||||||
103
cpu_65c02.v
103
cpu_65c02.v
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user