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,12 +1,22 @@
/* /*
* 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>
* *
* Feel free to use this code in any project (commercial or not), as long as you * 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", * 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.
*
*/ */
/* /*
@@ -22,15 +32,25 @@
* Two things were needed to correctly implement 65C02 NOPs * Two things were needed to correctly implement 65C02 NOPs
* 1. Ensure the microcode state machine uses an appropriate addressing mode for the opcode length * 1. Ensure the microcode state machine uses an appropriate addressing mode for the opcode length
* 2. Ensure there are no side-effects (e.g. register updates, memory stores, etc) * 2. Ensure there are no side-effects (e.g. register updates, memory stores, etc)
* *
* If IMPLEMENT_NOPS is defined, the state machine is modified accordingly. * If IMPLEMENT_NOPS is defined, the state machine is modified accordingly.
*/ */
`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
*/
input clk; // CPU clock // `define IMPLEMENT_CORRECT_BCD_FLAGS
module cpu_65c02( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY, SYNC );
input clk; // CPU clock
input reset; // reset signal input reset; // reset signal
output reg [15:0] AB; // address bus output reg [15:0] AB; // address bus
input [7:0] DI; // data in, read bus input [7:0] DI; // data in, read bus
@@ -38,13 +58,14 @@ output [7:0] DO; // data out, write bus
output WE; // write enable 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
*/ */
reg [15:0] PC; // Program Counter reg [15:0] PC; // Program Counter
reg [7:0] ABL; // Address Bus Register LSB reg [7:0] ABL; // Address Bus Register LSB
reg [7:0] ABH; // Address Bus Register MSB reg [7:0] ABH; // Address Bus Register MSB
wire [7:0] ADD; // Adder Hold Register (registered in ALU) wire [7:0] ADD; // Adder Hold Register (registered in ALU)
@@ -53,7 +74,7 @@ reg [7:0] DIHOLD; // Hold for Data In
reg DIHOLD_valid; // reg DIHOLD_valid; //
wire [7:0] DIMUX; // wire [7:0] DIMUX; //
reg [7:0] IRHOLD; // Hold for Instruction register reg [7:0] IRHOLD; // Hold for Instruction register
reg IRHOLD_valid; // Valid instruction in IRHOLD reg IRHOLD_valid; // Valid instruction in IRHOLD
reg [7:0] AXYS[3:0]; // A, X, Y and S register file reg [7:0] AXYS[3:0]; // A, X, Y and S register file
@@ -65,19 +86,22 @@ 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
reg [7:0] BI; // ALU Input B 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
wire [7:0] PCH = PC[15:8]; wire [7:0] PCH = PC[15:8];
wire [7:0] PCL = PC[7:0]; wire [7:0] PCL = PC[7:0];
@@ -86,10 +110,10 @@ reg NMI_edge = 0; // captured NMI edge
reg [1:0] regsel; // Select A, X, Y or S register reg [1:0] regsel; // Select A, X, Y or S register
wire [7:0] regfile = AXYS[regsel]; // Selected register output wire [7:0] regfile = AXYS[regsel]; // Selected register output
parameter parameter
SEL_A = 2'd0, SEL_A = 2'd0,
SEL_S = 2'd1, SEL_S = 2'd1,
SEL_X = 2'd2, SEL_X = 2'd2,
SEL_Y = 2'd3; SEL_Y = 2'd3;
/* /*
@@ -100,8 +124,8 @@ parameter
`ifdef SIM `ifdef SIM
wire [7:0] A = AXYS[SEL_A]; // Accumulator wire [7:0] A = AXYS[SEL_A]; // Accumulator
wire [7:0] X = AXYS[SEL_X]; // X register wire [7:0] X = AXYS[SEL_X]; // X register
wire [7:0] Y = AXYS[SEL_Y]; // Y register wire [7:0] Y = AXYS[SEL_Y]; // Y register
wire [7:0] S = AXYS[SEL_S]; // Stack pointer wire [7:0] S = AXYS[SEL_S]; // Stack pointer
`endif `endif
wire [7:0] P = { N, V, 2'b11, D, I, Z, C }; wire [7:0] P = { N, V, 2'b11, D, I, Z, C };
@@ -117,15 +141,15 @@ reg [5:0] state;
*/ */
reg PC_inc; // Increment PC reg PC_inc; // Increment PC
reg [15:0] PC_temp; // intermediate value of PC reg [15:0] PC_temp; // intermediate value of PC
reg [1:0] src_reg; // source register index reg [1:0] src_reg; // source register index
reg [1:0] dst_reg; // destination register index reg [1:0] dst_reg; // destination register index
reg index_y; // if set, then Y is index reg rather than X reg index_y; // if set, then Y is index reg rather than X
reg load_reg; // loading a register (A, X, Y, S) in this instruction reg load_reg; // loading a register (A, X, Y, S) in this instruction
reg inc; // increment reg inc; // increment
reg write_back; // set if memory is read/modified/written reg write_back; // set if memory is read/modified/written
reg load_only; // LDA/LDX/LDY instruction reg load_only; // LDA/LDX/LDY instruction
reg store; // doing store (STA/STX/STY) reg store; // doing store (STA/STX/STY)
reg adc_sbc; // doing ADC/SBC reg adc_sbc; // doing ADC/SBC
@@ -135,14 +159,14 @@ reg rotate; // doing rotate (no shift)
reg backwards; // backwards branch reg backwards; // backwards branch
reg cond_true; // branch condition is true reg cond_true; // branch condition is true
reg [3:0] cond_code; // condition code bits from instruction reg [3:0] cond_code; // condition code bits from instruction
reg shift_right; // Instruction ALU shift/rotate right reg shift_right; // Instruction ALU shift/rotate right
reg alu_shift_right; // Current cycle shift right enable reg alu_shift_right; // Current cycle shift right enable
reg [3:0] op; // Main ALU operation for instruction reg [3:0] op; // Main ALU operation for instruction
reg [3:0] alu_op; // Current cycle ALU operation reg [3:0] alu_op; // Current cycle ALU operation
reg adc_bcd; // ALU should do BCD style carry reg adc_bcd; // ALU should do BCD style carry
reg adj_bcd; // results should be BCD adjusted reg adj_bcd; // results should be BCD adjusted
/* /*
* some flip flops to remember we're doing special instructions. These * some flip flops to remember we're doing special instructions. These
* get loaded at the DECODE state, and used later * get loaded at the DECODE state, and used later
*/ */
@@ -152,15 +176,14 @@ reg txb_ins; // doing TSB/TRB instruction
reg bit_ins; // doing BIT instruction reg bit_ins; // doing BIT instruction
reg bit_ins_nv; // doing BIT instruction that will update the n and v flags (i.e. not BIT imm) reg bit_ins_nv; // doing BIT instruction that will update the n and v flags (i.e. not BIT imm)
reg plp; // doing PLP instruction reg plp; // doing PLP instruction
reg php; // doing PHP instruction reg php; // doing PHP instruction
reg clc; // clear carry reg clc; // clear carry
reg sec; // set carry reg sec; // set carry
reg cld; // clear decimal reg cld; // clear decimal
reg sed; // set decimal 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
@@ -181,17 +204,17 @@ parameter
* Microcode state machine. Basically, every addressing mode has its own * Microcode state machine. Basically, every addressing mode has its own
* path through the state machine. Additional information, such as the * path through the state machine. Additional information, such as the
* operation, source and destination registers are decoded in parallel, and * operation, source and destination registers are decoded in parallel, and
* kept in separate flops. * kept in separate flops.
*/ */
parameter parameter
ABS0 = 6'd0, // ABS - fetch LSB ABS0 = 6'd0, // ABS - fetch LSB
ABS1 = 6'd1, // ABS - fetch MSB ABS1 = 6'd1, // ABS - fetch MSB
ABSX0 = 6'd2, // ABS, X - fetch LSB and send to ALU (+X) ABSX0 = 6'd2, // ABS, X - fetch LSB and send to ALU (+X)
ABSX1 = 6'd3, // ABS, X - fetch MSB and send to ALU (+Carry) ABSX1 = 6'd3, // ABS, X - fetch MSB and send to ALU (+Carry)
ABSX2 = 6'd4, // ABS, X - Wait for ALU (only if needed) ABSX2 = 6'd4, // ABS, X - Wait for ALU (only if needed)
BRA0 = 6'd5, // Branch - fetch offset and send to ALU (+PC[7:0]) BRA0 = 6'd5, // Branch - fetch offset and send to ALU (+PC[7:0])
BRA1 = 6'd6, // Branch - fetch opcode, and send PC[15:8] to ALU BRA1 = 6'd6, // Branch - fetch opcode, and send PC[15:8] to ALU
BRA2 = 6'd7, // Branch - fetch opcode (if page boundary crossed) BRA2 = 6'd7, // Branch - fetch opcode (if page boundary crossed)
BRK0 = 6'd8, // BRK/IRQ - push PCH, send S to ALU (-1) BRK0 = 6'd8, // BRK/IRQ - push PCH, send S to ALU (-1)
BRK1 = 6'd9, // BRK/IRQ - push PCL, send S to ALU (-1) BRK1 = 6'd9, // BRK/IRQ - push PCL, send S to ALU (-1)
@@ -202,9 +225,9 @@ parameter
INDX0 = 6'd14, // (ZP,X) - fetch ZP address, and send to ALU (+X) INDX0 = 6'd14, // (ZP,X) - fetch ZP address, and send to ALU (+X)
INDX1 = 6'd15, // (ZP,X) - fetch LSB at ZP+X, calculate ZP+X+1 INDX1 = 6'd15, // (ZP,X) - fetch LSB at ZP+X, calculate ZP+X+1
INDX2 = 6'd16, // (ZP,X) - fetch MSB at ZP+X+1 INDX2 = 6'd16, // (ZP,X) - fetch MSB at ZP+X+1
INDX3 = 6'd17, // (ZP,X) - fetch data INDX3 = 6'd17, // (ZP,X) - fetch data
INDY0 = 6'd18, // (ZP),Y - fetch ZP address, and send ZP to ALU (+1) INDY0 = 6'd18, // (ZP),Y - fetch ZP address, and send ZP to ALU (+1)
INDY1 = 6'd19, // (ZP),Y - fetch at ZP+1, and send LSB to ALU (+Y) INDY1 = 6'd19, // (ZP),Y - fetch at ZP+1, and send LSB to ALU (+Y)
INDY2 = 6'd20, // (ZP),Y - fetch data, and send MSB to ALU (+Carry) INDY2 = 6'd20, // (ZP),Y - fetch data, and send MSB to ALU (+Carry)
INDY3 = 6'd21, // (ZP),Y) - fetch data (if page boundary crossed) INDY3 = 6'd21, // (ZP),Y) - fetch data (if page boundary crossed)
JMP0 = 6'd22, // JMP - fetch PCL and hold JMP0 = 6'd22, // JMP - fetch PCL and hold
@@ -223,15 +246,15 @@ parameter
READ = 6'd35, // Read memory for read/modify/write (INC, DEC, shift) READ = 6'd35, // Read memory for read/modify/write (INC, DEC, shift)
REG = 6'd36, // Read register for reg-reg transfers REG = 6'd36, // Read register for reg-reg transfers
RTI0 = 6'd37, // RTI - send S to ALU (+1) RTI0 = 6'd37, // RTI - send S to ALU (+1)
RTI1 = 6'd38, // RTI - read P from stack RTI1 = 6'd38, // RTI - read P from stack
RTI2 = 6'd39, // RTI - read PCL from stack RTI2 = 6'd39, // RTI - read PCL from stack
RTI3 = 6'd40, // RTI - read PCH from stack RTI3 = 6'd40, // RTI - read PCH from stack
RTI4 = 6'd41, // RTI - read PCH from stack RTI4 = 6'd41, // RTI - read PCH from stack
RTS0 = 6'd42, // RTS - send S to ALU (+1) RTS0 = 6'd42, // RTS - send S to ALU (+1)
RTS1 = 6'd43, // RTS - read PCL from stack RTS1 = 6'd43, // RTS - read PCL from stack
RTS2 = 6'd44, // RTS - write PCL to ALU, read PCH RTS2 = 6'd44, // RTS - write PCL to ALU, read PCH
RTS3 = 6'd45, // RTS - load PC and increment RTS3 = 6'd45, // RTS - load PC and increment
WRITE = 6'd46, // Write memory for read/modify/write WRITE = 6'd46, // Write memory for read/modify/write
ZP0 = 6'd47, // Z-page - fetch ZP address ZP0 = 6'd47, // Z-page - fetch ZP address
ZPX0 = 6'd48, // ZP, X - fetch ZP, and send to ALU (+X) ZPX0 = 6'd48, // ZP, X - fetch ZP, and send to ALU (+X)
ZPX1 = 6'd49, // ZP, X - load from memory ZPX1 = 6'd49, // ZP, X - load from memory
@@ -303,7 +326,7 @@ always @*
JMPIX0: statename = "JMPIX0"; JMPIX0: statename = "JMPIX0";
JMPIX1: statename = "JMPIX1"; JMPIX1: statename = "JMPIX1";
JMPIX2: statename = "JMPIX2"; JMPIX2: statename = "JMPIX2";
endcase endcase
//always @( PC ) //always @( PC )
@@ -329,15 +352,15 @@ always @*
JMPI1, JMPI1,
JMPIX1, JMPIX1,
JSR3, JSR3,
RTS3, RTS3,
RTI4: PC_temp = { DIMUX, ADD }; RTI4: PC_temp = { DIMUX, ADD };
BRA1: PC_temp = { ABH, ADD }; BRA1: PC_temp = { ABH, ADD };
JMPIX2, JMPIX2,
BRA2: PC_temp = { ADD, PCL }; BRA2: PC_temp = { ADD, PCL };
BRK2: PC_temp = res ? 16'hfffc : BRK2: PC_temp = res ? 16'hfffc :
NMI_edge ? 16'hfffa : 16'hfffe; NMI_edge ? 16'hfffa : 16'hfffe;
default: PC_temp = PC; default: PC_temp = PC;
@@ -373,15 +396,15 @@ always @*
default: PC_inc = 0; default: PC_inc = 0;
endcase endcase
/* /*
* Set new PC * Set new PC
*/ */
always @(posedge clk) always @(posedge clk)
if( RDY ) if( RDY )
PC <= PC_temp + PC_inc; PC <= PC_temp + PC_inc;
/* /*
* Address Generator * Address Generator
*/ */
parameter parameter
@@ -421,7 +444,7 @@ always @*
RTI2, RTI2,
RTI3, RTI3,
BRK2: AB = { STACKPAGE, ADD }; BRK2: AB = { STACKPAGE, ADD };
INDY1, INDY1,
INDX1, INDX1,
ZPX1, ZPX1,
@@ -443,7 +466,7 @@ always @*
* source of the address, such as the ALU or DI. * source of the address, such as the ALU or DI.
*/ */
always @(posedge clk) always @(posedge clk)
if( state != PUSH0 && state != PUSH1 && RDY && if( state != PUSH0 && state != PUSH1 && RDY &&
state != PULL0 && state != PULL1 && state != PULL2 ) state != PULL0 && state != PULL1 && state != PULL2 )
begin begin
ABL <= AB[7:0]; ABL <= AB[7:0];
@@ -451,7 +474,7 @@ always @(posedge clk)
end end
/* /*
* Data Out MUX * Data Out MUX
*/ */
always @* always @*
case( state ) case( state )
@@ -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
/* /*
@@ -506,8 +529,8 @@ always @*
case( state ) case( state )
DECODE: write_register = load_reg & ~plp; DECODE: write_register = load_reg & ~plp;
PULL1, PULL1,
RTS2, RTS2,
RTI3, RTI3,
BRK3, BRK3,
JSR0, JSR0,
@@ -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,14 +599,14 @@ 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
* S registers will be accessed. * S registers will be accessed.
*/ */
always @* always @*
case( state ) case( state )
INDY1, INDY1,
INDX0, INDX0,
@@ -578,7 +615,7 @@ always @*
ABSX0 : regsel = index_y ? SEL_Y : SEL_X; ABSX0 : regsel = index_y ? SEL_Y : SEL_X;
DECODE : regsel = dst_reg; DECODE : regsel = dst_reg;
BRK0, BRK0,
BRK3, BRK3,
@@ -591,8 +628,8 @@ always @*
RTI3, RTI3,
RTS0, RTS0,
RTS2 : regsel = SEL_S; RTS2 : regsel = SEL_S;
default: regsel = src_reg; default: regsel = src_reg;
endcase endcase
/* /*
@@ -622,10 +659,10 @@ always @*
case( state ) case( state )
READ: alu_op = op; READ: alu_op = op;
BRA1: alu_op = backwards ? OP_SUB : OP_ADD; BRA1: alu_op = backwards ? OP_SUB : OP_ADD;
FETCH, FETCH,
REG : alu_op = op; REG : alu_op = op;
DECODE, DECODE,
ABS1: alu_op = 1'bx; ABS1: alu_op = 1'bx;
@@ -651,15 +688,15 @@ always @*
alu_shift_right = 0; alu_shift_right = 0;
/* /*
* Sign extend branch offset. * Sign extend branch offset.
*/ */
always @(posedge clk) always @(posedge clk)
if( RDY ) if( RDY )
backwards <= DIMUX[7]; backwards <= DIMUX[7];
/* /*
* ALU A Input MUX * ALU A Input MUX
*/ */
always @* always @*
@@ -690,9 +727,9 @@ always @*
BRA0, BRA0,
READ: AI = DIMUX; READ: AI = DIMUX;
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
@@ -720,7 +757,7 @@ always @*
BRK0, BRK0,
BRK1, BRK1,
BRK2, BRK2,
PUSH0, PUSH0,
PUSH1, PUSH1,
PULL0, PULL0,
RTS0: BI = 8'h00; RTS0: BI = 8'h00;
@@ -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,
@@ -764,7 +801,7 @@ always @*
RTS0, RTS0,
RTS1, RTS1,
INDY0, INDY0,
INDX1: CI = 1; INDX1: CI = 1;
default: CI = 0; default: CI = 0;
endcase endcase
@@ -778,7 +815,7 @@ always @*
* Update C flag when doing ADC/SBC, shift/rotate, compare * Update C flag when doing ADC/SBC, shift/rotate, compare
*/ */
always @(posedge clk ) always @(posedge clk )
if( shift && state == WRITE ) if( shift && state == WRITE )
C <= CO; C <= CO;
else if( state == RTI2 ) else if( state == RTI2 )
C <= DIMUX[0]; C <= DIMUX[0];
@@ -795,36 +832,37 @@ always @(posedge clk )
/* /*
* Special Z flag got TRB/TSB * Special Z flag got TRB/TSB
*/ */
always @(posedge clk) always @(posedge clk)
AZ2 <= ~|(AI & regfile); if (RDY)
AZ2 <= ~|(AI & regfile);
/* /*
* Update Z, N flags when writing A, X, Y, Memory, or when doing compare * Update Z, N flags when writing A, X, Y, Memory, or when doing compare
*/ */
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];
@@ -846,7 +884,7 @@ always @(posedge clk)
/* /*
* Update D flag * Update D flag
*/ */
always @(posedge clk ) always @(posedge clk )
if( state == RTI2 ) if( state == RTI2 )
D <= DIMUX[3]; D <= DIMUX[3];
else if( state == DECODE ) begin else if( state == DECODE ) begin
@@ -859,7 +897,7 @@ always @(posedge clk )
* Update V flag * Update V flag
*/ */
always @(posedge clk ) always @(posedge clk )
if( state == RTI2 ) if( state == RTI2 )
V <= DIMUX[6]; V <= DIMUX[6];
else if( state == DECODE ) begin else if( state == DECODE ) begin
if( adc_sbc ) V <= AV; if( adc_sbc ) V <= AV;
@@ -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 )
assign DIMUX = DI; DIHOLD <= DI;
assign DIMUX = ~RDY ? DIHOLD : DI;
/* /*
* Microcode state machine * Microcode state machine
@@ -912,7 +943,7 @@ always @(posedge clk or posedge reset)
if( reset ) if( reset )
state <= BRK0; state <= BRK0;
else if( RDY ) case( state ) else if( RDY ) case( state )
DECODE : DECODE :
casex ( IR ) casex ( IR )
// TODO Review for simplifications as in verilog the first matching case has priority // TODO Review for simplifications as in verilog the first matching case has priority
8'b0000_0000: state <= BRK0; 8'b0000_0000: state <= BRK0;
@@ -920,23 +951,23 @@ always @(posedge clk or posedge reset)
8'b0010_1100: state <= ABS0; // BIT abs 8'b0010_1100: state <= ABS0; // BIT abs
8'b1001_1100: state <= ABS0; // STZ abs 8'b1001_1100: state <= ABS0; // STZ abs
8'b000x_1100: state <= ABS0; // TSB/TRB 8'b000x_1100: state <= ABS0; // TSB/TRB
8'b0100_0000: state <= RTI0; // 8'b0100_0000: state <= RTI0; //
8'b0100_1100: state <= JMP0; 8'b0100_1100: state <= JMP0;
8'b0110_0000: state <= RTS0; 8'b0110_0000: state <= RTS0;
8'b0110_1100: state <= JMPI0; 8'b0110_1100: state <= JMPI0;
8'b0111_1100: state <= JMPIX0; 8'b0111_1100: state <= JMPIX0;
`ifdef IMPLEMENT_NOPS `ifdef IMPLEMENT_NOPS
8'bxxxx_xx11: state <= REG; // (NOP1: 3/7/B/F column) 8'bxxxx_xx11: state <= REG; // (NOP1: 3/7/B/F column)
8'bxxx0_0010: state <= FETCH; // (NOP2: 2 column, 4 column handled correctly below) 8'bxxx0_0010: state <= FETCH; // (NOP2: 2 column, 4 column handled correctly below)
8'bx1x1_1100: state <= ABS0; // (NOP3: C column) 8'bx1x1_1100: state <= ABS0; // (NOP3: C column)
`endif `endif
8'b0x00_1000: state <= PUSH0; 8'b0x00_1000: state <= PUSH0;
8'b0x10_1000: state <= PULL0; 8'b0x10_1000: state <= PULL0;
8'b0xx1_1000: state <= REG; // CLC, SEC, CLI, SEI 8'b0xx1_1000: state <= REG; // CLC, SEC, CLI, SEI
8'b11x0_00x0: state <= FETCH; // IMM 8'b11x0_00x0: state <= FETCH; // IMM
8'b1x10_00x0: state <= FETCH; // IMM 8'b1x10_00x0: state <= FETCH; // IMM
8'b1xx0_1100: state <= ABS0; // X/Y abs 8'b1xx0_1100: state <= ABS0; // X/Y abs
8'b1xxx_1000: state <= REG; // DEY, TYA, ... 8'b1xxx_1000: state <= REG; // DEY, TYA, ...
8'bxxx0_0001: state <= INDX0; 8'bxxx0_0001: state <= INDX0;
8'bxxx1_0010: state <= IND0; // (ZP) odd 2 column 8'bxxx1_0010: state <= IND0; // (ZP) odd 2 column
8'b000x_0100: state <= ZP0; // TSB/TRB 8'b000x_0100: state <= ZP0; // TSB/TRB
@@ -991,18 +1022,18 @@ always @(posedge clk or posedge reset)
FETCH : state <= DECODE; FETCH : state <= DECODE;
REG : state <= DECODE; REG : state <= DECODE;
PUSH0 : state <= PUSH1; PUSH0 : state <= PUSH1;
PUSH1 : state <= DECODE; PUSH1 : state <= DECODE;
PULL0 : state <= PULL1; PULL0 : state <= PULL1;
PULL1 : state <= PULL2; PULL1 : state <= PULL2;
PULL2 : state <= DECODE; PULL2 : state <= DECODE;
JSR0 : state <= JSR1; JSR0 : state <= JSR1;
JSR1 : state <= JSR2; JSR1 : state <= JSR2;
JSR2 : state <= JSR3; JSR2 : state <= JSR3;
JSR3 : state <= FETCH; JSR3 : state <= FETCH;
RTI0 : state <= RTI1; RTI0 : state <= RTI1;
RTI1 : state <= RTI2; RTI1 : state <= RTI2;
@@ -1020,7 +1051,7 @@ always @(posedge clk or posedge reset)
BRA2 : state <= DECODE; BRA2 : state <= DECODE;
JMP0 : state <= JMP1; JMP0 : state <= JMP1;
JMP1 : state <= DECODE; JMP1 : state <= DECODE;
JMPI0 : state <= JMPI1; JMPI0 : state <= JMPI1;
JMPI1 : state <= JMP0; JMPI1 : state <= JMP0;
@@ -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
*/ */
@@ -1050,7 +1104,7 @@ always @(posedge clk)
8'b0xxx_1010, // ASLA, INCA, ROLA, DECA, LSRA, PHY, RORA, PLY 8'b0xxx_1010, // ASLA, INCA, ROLA, DECA, LSRA, PHY, RORA, PLY
8'b0xxx_xx01, // ORA, AND, EOR, ADC 8'b0xxx_xx01, // ORA, AND, EOR, ADC
8'b100x_10x0, // DEY, TYA, TXA, TXS 8'b100x_10x0, // DEY, TYA, TXA, TXS
8'b1010_xxx0, // LDA/LDX/LDY 8'b1010_xxx0, // LDA/LDX/LDY
8'b1011_1010, // TSX 8'b1011_1010, // TSX
8'b1011_x1x0, // LDX/LDY 8'b1011_x1x0, // LDX/LDY
8'b1100_1010, // DEX 8'b1100_1010, // DEX
@@ -1090,15 +1144,15 @@ always @(posedge clk)
always @(posedge clk) always @(posedge clk)
if( state == DECODE && RDY ) if( state == DECODE && RDY )
casex( IR ) casex( IR )
8'b1011_1010: // TSX 8'b1011_1010: // TSX
src_reg <= SEL_S; src_reg <= SEL_S;
8'b100x_x110, // STX 8'b100x_x110, // STX
8'b100x_1x10, // TXA, TXS 8'b100x_1x10, // TXA, TXS
8'b1110_xx00, // INX, CPX 8'b1110_xx00, // INX, CPX
8'b1101_1010, // PHX 8'b1101_1010, // PHX
8'b1100_1010: // DEX 8'b1100_1010: // DEX
src_reg <= SEL_X; src_reg <= SEL_X;
8'b100x_x100, // STY 8'b100x_x100, // STY
8'b1001_1000, // TYA 8'b1001_1000, // TYA
@@ -1110,7 +1164,7 @@ always @(posedge clk)
default: src_reg <= SEL_A; default: src_reg <= SEL_A;
endcase endcase
always @(posedge clk) always @(posedge clk)
if( state == DECODE && RDY ) if( state == DECODE && RDY )
casex( IR ) casex( IR )
8'bxxx1_0001, // INDY 8'bxxx1_0001, // INDY
@@ -1141,7 +1195,7 @@ always @(posedge clk )
casex( IR ) // DMB: Checked for 65C02 NOP collisions casex( IR ) // DMB: Checked for 65C02 NOP collisions
8'b0xxx_x110, // ASL, ROL, LSR, ROR 8'b0xxx_x110, // ASL, ROL, LSR, ROR
8'b000x_x100, // TSB/TRB 8'b000x_x100, // TSB/TRB
8'b11xx_x110: // DEC/INC 8'b11xx_x110: // DEC/INC
write_back <= 1; write_back <= 1;
default: write_back <= 0; default: write_back <= 0;
@@ -1160,7 +1214,7 @@ always @(posedge clk )
if( state == DECODE && RDY ) if( state == DECODE && RDY )
casex( IR ) casex( IR )
8'b0001_1010, // INCA 8'b0001_1010, // INCA
8'b111x_x110, // INC 8'b111x_x110, // INC
8'b11x0_1000: // INX, INY 8'b11x0_1000: // INX, INY
inc <= 1; inc <= 1;
@@ -1203,7 +1257,7 @@ always @(posedge clk )
8'b1101_0010, // CMP (zp) 8'b1101_0010, // CMP (zp)
8'b11x0_0x00, // CPX, CPY (imm/zp) 8'b11x0_0x00, // CPX, CPY (imm/zp)
8'b11x0_1100, // CPX, CPY (abs) 8'b11x0_1100, // CPX, CPY (abs)
8'b110x_xx01: // CMP 8'b110x_xx01: // CMP
compare <= 1; compare <= 1;
default: compare <= 0; default: compare <= 0;
@@ -1216,17 +1270,17 @@ always @(posedge clk )
8'b01xx_1x10: // ROR, LSR 8'b01xx_1x10: // ROR, LSR
shift_right <= 1; shift_right <= 1;
default: shift_right <= 0; default: shift_right <= 0;
endcase endcase
always @(posedge clk ) always @(posedge clk )
if( state == DECODE && RDY ) if( state == DECODE && RDY )
casex( IR ) casex( IR )
8'b0x10_1010, // ROL A, ROR A 8'b0x10_1010, // ROL A, ROR A
8'b0x1x_x110: // ROR, ROL 8'b0x1x_x110: // ROR, ROL
rotate <= 1; rotate <= 1;
default: rotate <= 0; default: rotate <= 0;
endcase endcase
always @(posedge clk ) always @(posedge clk )
@@ -1253,8 +1307,8 @@ always @(posedge clk )
8'b11x1_0010, // CMP, SBC (zp) 8'b11x1_0010, // CMP, SBC (zp)
8'b0011_1010, // DEC A 8'b0011_1010, // DEC A
8'b1000_1000, // DEY 8'b1000_1000, // DEY
8'b1100_1010, // DEX 8'b1100_1010, // DEX
8'b110x_x110, // DEC 8'b110x_x110, // DEC
8'b11xx_xx01, // CMP, SBC 8'b11xx_xx01, // CMP, SBC
8'b11x0_0x00, // CPX, CPY (imm, zpg) 8'b11x0_0x00, // CPX, CPY (imm, zpg)
8'b11x0_1100: op <= OP_SUB; 8'b11x0_1100: op <= OP_SUB;
@@ -1264,8 +1318,8 @@ always @(posedge clk )
8'b010x_xx01, // EOR 8'b010x_xx01, // EOR
8'b00xx_xx01: // ORA, AND 8'b00xx_xx01: // ORA, AND
op <= { 2'b11, IR[6:5] }; op <= { 2'b11, IR[6:5] };
default: op <= OP_ADD; default: op <= OP_ADD;
endcase endcase
always @(posedge clk ) always @(posedge clk )
@@ -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)