mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-28 23:49:20 +00:00
280 lines
7.3 KiB
Verilog
280 lines
7.3 KiB
Verilog
`ifndef CPU16_H
|
|
`define CPU16_H
|
|
|
|
// include ALU module
|
|
`include "cpu8.v"
|
|
|
|
/*
|
|
00000aaa 0++++bbb operation A+B->A
|
|
00001aaa 0++++bbb operation A+[B]->A
|
|
00011aaa 0++++000 operation A+imm16 -> A
|
|
00101aaa ######## load zero page
|
|
00110aaa ######## store zero page
|
|
01001aaa #####bbb load [B+#] -> A
|
|
01010aaa #####bbb store A -> [B+#]
|
|
01101aaa 0++++000 operation A+[imm16] -> A
|
|
01110aaa 00cccbbb store A -> [B+#], C -> IP
|
|
1000tttt ######## conditional branch
|
|
11+++aaa ######## immediate binary operation
|
|
*/
|
|
|
|
module CPU16(clk, reset, hold, busy,
|
|
address, data_in, data_out, write);
|
|
|
|
input clk;
|
|
input reset;
|
|
input hold;
|
|
output busy;
|
|
output [15:0] address;
|
|
input [15:0] data_in;
|
|
output [15:0] data_out;
|
|
output write;
|
|
|
|
reg [15:0] regs[0:7]; // 8 16-bit registers
|
|
reg [2:0] state; // CPU state
|
|
|
|
reg carry; // carry flag
|
|
reg zero; // zero flag
|
|
reg neg; // negative flag
|
|
|
|
wire [16:0] Y; // ALU 16-bit + carry output
|
|
reg [3:0] aluop; // ALU operation
|
|
|
|
reg [15:0] opcode; // used to decode ALU inputs
|
|
wire [2:0] rdest = opcode[10:8]; // ALU A input reg.
|
|
wire [2:0] rsrc = opcode[2:0]; // ALU B input reg.
|
|
wire Bconst = opcode[15]; // ALU B = 8-bit constant
|
|
wire Bload = opcode[11]; // ALU B = data bus
|
|
|
|
// CPU states
|
|
localparam S_RESET = 0;
|
|
localparam S_SELECT = 1;
|
|
localparam S_DECODE = 2;
|
|
localparam S_COMPUTE = 3;
|
|
|
|
localparam SP = 6; // stack ptr = register 6
|
|
localparam IP = 7; // IP = register 7
|
|
|
|
ALU #(16) alu(
|
|
.A(regs[rdest]),
|
|
.B(Bconst ? {8'b0, opcode[7:0]}
|
|
: Bload ? data_in
|
|
: regs[rsrc]),
|
|
.Y(Y),
|
|
.aluop(aluop),
|
|
.carry(carry));
|
|
|
|
always @(posedge clk)
|
|
if (reset) begin
|
|
state <= S_RESET;
|
|
busy <= 1;
|
|
end else begin
|
|
case (state)
|
|
// state 0: reset
|
|
S_RESET: begin
|
|
regs[IP] <= 16'h8000;
|
|
write <= 0;
|
|
state <= S_SELECT;
|
|
end
|
|
// state 1: select opcode address
|
|
S_SELECT: begin
|
|
write <= 0;
|
|
if (hold) begin
|
|
busy <= 1;
|
|
state <= S_SELECT;
|
|
end else begin
|
|
busy <= 0;
|
|
address <= regs[IP];
|
|
regs[IP] <= regs[IP] + 1;
|
|
state <= S_DECODE;
|
|
end
|
|
end
|
|
// state 2: read/decode opcode
|
|
S_DECODE: begin
|
|
casez (data_in)
|
|
// 00000aaa0++++bbb operation A+B->A
|
|
16'b00000???0???????: begin
|
|
aluop <= data_in[6:3];
|
|
state <= S_COMPUTE;
|
|
end
|
|
// 00001aaa01+++bbb operation A+[B]->A
|
|
16'b00001???01??????: begin
|
|
address <= regs[data_in[2:0]];
|
|
aluop <= data_in[6:3];
|
|
state <= S_COMPUTE;
|
|
if (data_in[2:0] == SP)
|
|
regs[SP] <= regs[SP] + 1;
|
|
end
|
|
// 00011aaa0++++000 operation A+imm16 -> A
|
|
16'b00011???0????000: begin
|
|
address <= regs[IP];
|
|
regs[IP] <= regs[IP] + 1;
|
|
aluop <= data_in[6:3];
|
|
state <= S_COMPUTE;
|
|
end
|
|
// 11+++aaa######## immediate binary operation
|
|
16'b11??????????????: begin
|
|
aluop <= data_in[14:11];
|
|
state <= S_COMPUTE;
|
|
end
|
|
// 00101aaa######## load ZP memory
|
|
16'b00101???????????: begin
|
|
address <= {8'b0, data_in[7:0]};
|
|
aluop <= `OP_LOAD_B;
|
|
state <= S_COMPUTE;
|
|
end
|
|
// 00110aaa######## store ZP memory
|
|
16'b00110???????????: begin
|
|
address <= {8'b0, data_in[7:0]};
|
|
data_out <= regs[data_in[10:8]];
|
|
write <= 1;
|
|
state <= S_SELECT;
|
|
end
|
|
// 01001aaa#####bbb [B+#] -> A
|
|
16'b01001???????????: begin
|
|
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
|
aluop <= `OP_LOAD_B;
|
|
state <= S_COMPUTE;
|
|
if (data_in[2:0] == SP)
|
|
regs[SP] <= regs[SP] + 1;
|
|
end
|
|
// 01010aaa#####bbb store A -> [B+#]
|
|
16'b01010???????????: begin
|
|
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
|
data_out <= regs[data_in[10:8]];
|
|
write <= 1;
|
|
state <= S_SELECT;
|
|
if (data_in[2:0] == SP)
|
|
regs[SP] <= regs[SP] - 1;
|
|
end
|
|
// 01011aaa0++++000 operation A+[imm16] -> A
|
|
16'b01011????????000: begin
|
|
address <= regs[IP];
|
|
regs[IP] <= regs[IP] + 1;
|
|
aluop <= data_in[6:3];
|
|
state <= S_COMPUTE;
|
|
end
|
|
// 01110aaa00cccbbb store A -> [B+#], C -> IP
|
|
16'b01110???00??????: begin
|
|
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
|
data_out <= regs[data_in[10:8]];
|
|
write <= 1;
|
|
state <= S_SELECT;
|
|
if (data_in[2:0] == SP)
|
|
regs[SP] <= regs[SP] - 1;
|
|
regs[IP] <= regs[data_in[5:3]];
|
|
end
|
|
// 1000????######## conditional branch
|
|
16'b1000????????????: begin
|
|
if (
|
|
(data_in[8] && (data_in[11] == carry)) ||
|
|
(data_in[9] && (data_in[11] == zero)) ||
|
|
(data_in[10] && (data_in[11] == neg)))
|
|
begin
|
|
// relative branch, sign extended
|
|
regs[IP] <= regs[IP] + 16'($signed(data_in[7:0]));
|
|
end
|
|
state <= S_SELECT;
|
|
end
|
|
// fall-through RESET
|
|
default: begin
|
|
state <= S_RESET; // reset
|
|
end
|
|
endcase
|
|
opcode <= data_in; // (only use opcode next cycle)
|
|
end
|
|
// state 3: compute ALU op and flags
|
|
S_COMPUTE: begin
|
|
// transfer ALU output to destination
|
|
regs[rdest] <= Y[15:0];
|
|
// set carry for certain operations (4-7,12-15)
|
|
if (aluop[2]) carry <= Y[16];
|
|
// set zero flag
|
|
zero <= ~|Y[15:0];
|
|
neg <= Y[15];
|
|
// repeat CPU loop
|
|
state <= S_SELECT;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
endmodule
|
|
|
|
`ifdef TOPMOD__test_CPU16_top
|
|
|
|
module test_CPU16_top(
|
|
input clk,
|
|
input reset,
|
|
output [15:0] address_bus,
|
|
output reg [15:0] to_cpu,
|
|
output [15:0] from_cpu,
|
|
output write_enable,
|
|
output [15:0] IP,
|
|
output zero,
|
|
output carry,
|
|
output busy
|
|
);
|
|
|
|
reg [15:0] ram[0:65535];
|
|
reg [15:0] rom[0:255];
|
|
|
|
assign IP = cpu.regs[7];
|
|
assign zero = cpu.zero;
|
|
assign carry = cpu.carry;
|
|
|
|
CPU16 cpu(
|
|
.clk(clk),
|
|
.reset(reset),
|
|
.hold(0),
|
|
.busy(busy),
|
|
.address(address_bus),
|
|
.data_in(to_cpu),
|
|
.data_out(from_cpu),
|
|
.write(write_enable));
|
|
|
|
always @(posedge clk)
|
|
if (write_enable) begin
|
|
ram[address_bus] <= from_cpu;
|
|
end
|
|
|
|
always @(*)
|
|
if (address_bus[15] == 0)
|
|
to_cpu = ram[address_bus];
|
|
else
|
|
to_cpu = rom[address_bus[7:0]];
|
|
|
|
`ifdef EXT_INLINE_ASM
|
|
initial begin
|
|
rom = '{
|
|
__asm
|
|
.arch femto16
|
|
.org 0x8000
|
|
.len 256
|
|
mov dx,@Fib
|
|
jsr dx
|
|
reset
|
|
Fib:
|
|
mov ax,#1
|
|
mov bx,#0
|
|
mov sp,@$6fff
|
|
Loop:
|
|
mov cx,ax
|
|
add ax,bx
|
|
mov bx,cx
|
|
push ax
|
|
pop ax
|
|
mov [42],ax
|
|
mov ax,[42]
|
|
bcc Loop
|
|
rts
|
|
__endasm
|
|
};
|
|
end
|
|
`endif
|
|
|
|
endmodule
|
|
|
|
`endif
|
|
|
|
`endif
|