vchip8/cpu.v

202 lines
6.3 KiB
Verilog

// Copyright (c) 2014 Sergiusz 'q3k' BazaƄski <sergiusz@bazanski.pl>
// Licensed under 2-clause BSD - see COPYING file from software distribution
`timescale 1ns / 1ps
/* verilator lint_off UNDRIVEN */
/* verilator lint_off UNUSED */
/* verilator lint_off LITENDIAN */
module cpu (
input reset,
input cpu_clock,
input [15:0] keypad
);
// 4k (12 bits) of 8-bit ram
reg [7:0] ram[0:4095]/* verilator public */;
// 16 8-bit V registers
reg [7:0] vr[0:15]/* verilator public */;
// 16-bit I register
reg [15:0] i;
// 12-bit program counter
reg [11:0] pc/* verilator public */;
// 16-element, 12-bit stack
reg [11:0] stack [0:15];
// 4-bit stack pointer
reg [3:0] sp;
// internal cycle counter (0 - fetch)
`define FETCH 0
`define EXECUTE 1
`define WRITEBACK 2
reg [1:0] c;
// Instruction register
reg [15:0] ins;
wire [3:0] family = ins[15:12];
// ALU operation
wire [3:0] alu_op = ins[3:0];
wire is_alu_op = (c == `EXECUTE) && (family == 8);
// Source register indexes
wire [3:0] x = ins[11:8];
wire [3:0] y = ins[7:4];
// Immediates
wire [7:0] kk = ins[7:0];
wire [11:0] nnn = ins[11:0];
// Jump register
reg[11:0] jmp;
// Should jump on FETCH?
reg should_jmp;
always @(posedge cpu_clock)
begin
if (!reset) begin
i <= 0;
pc <= 0;
sp <= 0;
should_jmp <= 0;
c <= 0;
vr[0] <= 0;
vr[1] <= 0;
vr[2] <= 0;
vr[3] <= 0;
vr[4] <= 0;
vr[5] <= 0;
vr[6] <= 0;
vr[7] <= 0;
vr[8] <= 0;
vr[9] <= 0;
vr[10] <= 0;
vr[11] <= 0;
vr[12] <= 0;
vr[13] <= 0;
vr[14] <= 0;
vr[15] <= 0;
end else begin
case(c)
`FETCH: begin
ins <= {ram[pc], ram[pc + 1]};
c <= 1;
end
`EXECUTE: begin
case (family)
// Jump to address
1: begin
should_jmp <= 1;
jmp <= nnn;
end
// Call address
2: begin
should_jmp <= 1;
stack[sp] <= pc;
sp <= sp + 1;
jmp <= nnn;
end
// Skip next if Vx == kk (3xkk)
3: begin
if (vr[x] == kk) begin
should_jmp <= 1;
jmp <= pc + 4;
end
end
// Skip next if Vx != kk (4xkk)
4: begin
if (vr[x] != kk) begin
should_jmp <= 1;
jmp <= pc + 4;
end
end
// Skip next if Vx == Vy (5xy0)
5: begin
if (vr[x] == vr[y]) begin
should_jmp <= 1;
jmp <= pc + 4;
end
end
// Vx <= kk
6: vr[x] <= kk;
// Vx += kk
7: vr[x] <= vr[x] + kk;
// Skip if Vx != Vy (9xy0)
9: begin
if (vr[x] != vr[y]) begin
should_jmp <= 1;
jmp <= pc + 4;
end
end
// Set I to nnn
10: i <= {4'h0, nnn};
// Jump to nnn + V0
11: begin
should_jmp <= 1;
jmp <= nnn + {4'h0, vr[0]};
end
// Set Vx to random and kk
// TODO: undo fair dice roll
12: vr[x] <= 42 & kk;
// TODO: draw sprites
13: begin end
// Key functions
14: begin
if (kk == 8'h9E) begin
if (keypad[x]) begin
should_jmp <= 1;
jmp <= pc + 4;
end
end else if (kk == 8'hA1) begin
if (~keypad[x]) begin
should_jmp <= 1;
jmp <= pc + 4;
end
end
end
endcase
c <= 2;
end
`WRITEBACK: begin
if (should_jmp) begin
pc <= jmp;
end else begin
pc <= pc + 2;
end
should_jmp <= 0;
c <= 0;
end
endcase
end
end
// ALU
always @(posedge cpu_clock)
begin
if (is_alu_op) begin
case(alu_op)
0: vr[x] <= vr[y];
1: vr[x] <= vr[x] | vr[y];
2: vr[x] <= vr[x] & vr[y];
3: vr[x] <= vr[x] ^ vr[y];
4: { vr[15], vr[x] } <= {8'b0, vr[x]} + {8'b0, vr[y]};
5: begin
vr[x] <= vr[x] - vr[y];
vr[15] <= {7'b0, vr[x] >= vr[y]};
end
6: begin
vr[15] <= {7'b0, vr[x][0]};
vr[x] <= vr[x] >> 1;
end
7: begin
vr[x] <= vr[y] - vr[x];
vr[15] <= {7'b0, vr[y] >= vr[x]};
end
14: begin
vr[15] <= {7'b0, vr[x][7]};
vr[x] <= vr[x] << 1;
end
endcase
end
end
endmodule