blob: 944e11ff03258a3444a65b61bef75c3b1a746619 [file] [log] [blame]
`timescale 1ns / 1ps
`default_nettype wire
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: Wenting Zhang
//
// Create Date: 17:30:26 02/08/2018
// Module Name: alu
// Project Name: VerilogBoy
// Description:
// The Game Boy ALU.
// Dependencies:
//
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module alu(
input [7:0] alu_b,
input [7:0] alu_a,
input [2:0] alu_bit_index,
output reg [7:0] alu_result,
input [3:0] alu_flags_in,
output reg [3:0] alu_flags_out,
input [4:0] alu_op
);
localparam OP_ADD = 5'b00000;
localparam OP_ADC = 5'b00001;
localparam OP_SUB = 5'b00010;
localparam OP_SBC = 5'b00011;
localparam OP_AND = 5'b00100;
localparam OP_XOR = 5'b00101;
localparam OP_OR = 5'b00110;
localparam OP_CP = 5'b00111;
localparam OP_RLC = 5'b01000;
localparam OP_RRC = 5'b01001;
localparam OP_RL = 5'b01010;
localparam OP_RR = 5'b01011;
localparam OP_SLA = 5'b01100;
localparam OP_SRA = 5'b01101;
localparam OP_SWAP= 5'b01110;
localparam OP_SRL = 5'b01111;
localparam OP_LF = 5'b10000; // Load Flags
// unused 5'b10001
localparam OP_SF = 5'b10010; // Save Flags
// unused 5'b10011
localparam OP_DAA = 5'b10100;
localparam OP_CPL = 5'b10101;
localparam OP_SCF = 5'b10110;
localparam OP_CCF = 5'b10111;
// unused 5'b11000
// unused 5'b11001
// unused 5'b11010
// unused 5'b11011
// unused 5'b11100
localparam OP_BIT = 5'b11101;
localparam OP_RES = 5'b11110;
localparam OP_SET = 5'b11111;
localparam F_Z = 2'd3;
localparam F_N = 2'd2;
localparam F_H = 2'd1;
localparam F_C = 2'd0;
reg [8:0] intermediate_result1, intermediate_result2;
reg [4:0] result_low;
reg [4:0] result_high;
wire [2:0] bit_index;
reg carry;
assign bit_index = alu_bit_index;
always@(*) begin
alu_flags_out = 4'b0;
carry = 1'b0;
result_low = 5'd0;
result_high = 5'd0;
intermediate_result1 = 9'd0;
intermediate_result2 = 9'd0;
case (alu_op)
OP_ADD, OP_ADC: begin
carry = (alu_op == OP_ADC) ? alu_flags_in[F_C] : 1'b0;
result_low = {1'b0, alu_a[3:0]} + {1'b0, alu_b[3:0]} +
{4'b0, carry};
alu_flags_out[F_H] = result_low[4];
result_high = {1'b0, alu_a[7:4]} +
{1'b0, alu_b[7:4]} +
{4'b0, result_low[4]};
alu_flags_out[F_C] = result_high[4];
alu_result = {result_high[3:0], result_low[3:0]};
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_SUB, OP_SBC, OP_CP: begin
alu_flags_out[F_N] = 1'b1;
carry = (alu_op == OP_SBC) ? alu_flags_in[F_C] : 1'b0;
result_low = {1'b0, alu_a[3:0]} +
~({1'b0, alu_b[3:0]} +
{4'b0, carry}) + 5'b1;
alu_flags_out[F_H] = result_low[4];
result_high = {1'b0, alu_a[7:4]} +
~({1'b0, alu_b[7:4]}) +
{4'b0, ~result_low[4]};
alu_flags_out[F_C] = result_high[4];
alu_result = (alu_op == OP_CP) ? (alu_a[7:0]) : {result_high[3:0], result_low[3:0]};
alu_flags_out[F_Z] = ({result_high[3:0], result_low[3:0]} == 8'd0) ? 1'b1 : 1'b0;
end
OP_AND: begin
alu_flags_out[F_H] = 1'b1;
alu_result = alu_a & alu_b;
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_OR: begin
alu_result = alu_a | alu_b;
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_XOR: begin
alu_result = alu_a ^ alu_b;
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_DAA: begin
if (~alu_flags_in[F_N]) begin
if (alu_flags_in[F_H] |
((alu_a & 8'h0f) > 8'h9)) begin
intermediate_result1 = {1'b0, alu_a} + 9'h6;
end
else begin
intermediate_result1 = {1'b0, alu_a};
end
if (alu_flags_in[F_C] | (intermediate_result1 > 9'h9f)) begin
intermediate_result2 = intermediate_result1 + 9'h60;
end
else begin
intermediate_result2 = intermediate_result1;
end
end
else begin
if (alu_flags_in[F_H]) begin
intermediate_result1 = {1'b0, (alu_a - 8'h6)};
end
else begin
intermediate_result1 = {1'b0, alu_a};
end
if (alu_flags_in[F_C]) begin
intermediate_result2 = intermediate_result1 - 9'h60;
end
else begin
intermediate_result2 = intermediate_result1;
end
end // else: !if(alu_flags_in[F_N])
alu_result = intermediate_result2[7:0];
alu_flags_out[F_N] = alu_flags_in[F_N];
alu_flags_out[F_H] = 1'b0;
alu_flags_out[F_C] = intermediate_result2[8] ? 1'b1 :
alu_flags_in[F_C];
alu_flags_out[F_Z] = (intermediate_result2[7:0] == 8'd0) ?
1'b1 : 1'b0;
end
OP_CPL: begin
alu_flags_out[F_Z] = alu_flags_in[F_Z];
alu_flags_out[F_N] = 1'b1;
alu_flags_out[F_H] = 1'b1;
alu_flags_out[F_C] = alu_flags_in[F_C];
alu_result = ~alu_a;
end
OP_CCF: begin
alu_flags_out[F_Z] = alu_flags_in[F_Z];
alu_flags_out[F_N] = 1'b0;
alu_flags_out[F_H] = 1'b0;
alu_flags_out[F_C] = ~alu_flags_in[F_C];
alu_result = alu_b;
end
OP_SCF: begin
alu_flags_out[F_Z] = alu_flags_in[F_Z];
alu_flags_out[F_N] = 1'b0;
alu_flags_out[F_H] = 1'b0;
alu_flags_out[F_C] = 1'b1;
alu_result = alu_b;
end
OP_RLC: begin
alu_result[0] = alu_a[7];
alu_result[7:1] = alu_a[6:0];
alu_flags_out[F_C] = alu_a[7];
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_RL: begin
alu_result[0] = alu_flags_in[F_C];
alu_result[7:1] = alu_a[6:0];
alu_flags_out[F_C] = alu_a[7];
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_RRC: begin
alu_result[7] = alu_a[0];
alu_result[6:0] = alu_a[7:1];
alu_flags_out[F_C] = alu_a[0];
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_RR: begin
alu_result[7] = alu_flags_in[F_C];
alu_result[6:0] = alu_a[7:1];
alu_flags_out[F_C] = alu_a[0];
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_SLA: begin
alu_result[7:1] = alu_a[6:0];
alu_result[0] = 1'b0;
alu_flags_out[F_C] = alu_a[7];
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_SRA: begin
alu_result[7] = alu_a[7];
alu_result[6:0] = alu_a[7:1];
alu_flags_out[F_C] = alu_a[0];
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_SRL: begin
alu_result[7] = 1'b0;
alu_result[6:0] = alu_a[7:1];
alu_flags_out[F_C] = alu_a[0];
alu_flags_out[F_Z] = (alu_result == 8'd0) ? 1'b1 : 1'b0;
end
OP_BIT: begin
// Bit index must be in data0[5:3]
alu_flags_out[F_C] = alu_flags_in[F_C];
alu_flags_out[F_H] = 1'b1;
alu_flags_out[F_N] = 1'b0;
alu_flags_out[F_Z] = ~alu_a[bit_index];
alu_result = alu_b;
end
OP_SET: begin
alu_flags_out = alu_flags_in;
alu_result = alu_a;
alu_result[bit_index] = 1'b1;
end
OP_RES: begin
alu_flags_out = alu_flags_in;
alu_result = alu_a;
alu_result[bit_index] = 1'b0;
end
OP_SWAP: begin
alu_flags_out[F_Z] = (alu_a == 8'd0) ? 1'd1: 1'd0;
alu_flags_out[F_H] = 1'b0;
alu_flags_out[F_C] = 1'b0;
alu_flags_out[F_N] = 1'b0;
alu_result = {alu_a[3:0], alu_a[7:4]};
end
OP_SF: begin
alu_flags_out = alu_b[7:4];
alu_result = alu_a;
end
OP_LF: begin
alu_result = {alu_flags_in, 4'b0};
end
default: begin
alu_result = alu_b;
alu_flags_out = alu_flags_in;
end
endcase
end
endmodule