443 lines
11 KiB
Verilog
443 lines
11 KiB
Verilog
//
|
|
// TV80 8-Bit Microprocessor Core
|
|
// Based on the VHDL T80 core by Daniel Wallner (jesus@opencores.org)
|
|
//
|
|
// Copyright (c) 2004 Guy Hutchison (ghutchis@opencores.org)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
module tv80_alu (/*AUTOARG*/
|
|
// Outputs
|
|
Q, F_Out,
|
|
// Inputs
|
|
Arith16, Z16, ALU_Op, IR, ISet, BusA, BusB, F_In
|
|
);
|
|
|
|
parameter Mode = 0;
|
|
parameter Flag_C = 0;
|
|
parameter Flag_N = 1;
|
|
parameter Flag_P = 2;
|
|
parameter Flag_X = 3;
|
|
parameter Flag_H = 4;
|
|
parameter Flag_Y = 5;
|
|
parameter Flag_Z = 6;
|
|
parameter Flag_S = 7;
|
|
|
|
input Arith16;
|
|
input Z16;
|
|
input [3:0] ALU_Op ;
|
|
input [5:0] IR;
|
|
input [1:0] ISet;
|
|
input [7:0] BusA;
|
|
input [7:0] BusB;
|
|
input [7:0] F_In;
|
|
output [7:0] Q;
|
|
output [7:0] F_Out;
|
|
reg [7:0] Q;
|
|
reg [7:0] F_Out;
|
|
|
|
function [4:0] AddSub4;
|
|
input [3:0] A;
|
|
input [3:0] B;
|
|
input Sub;
|
|
input Carry_In;
|
|
begin
|
|
AddSub4 = { 1'b0, A } + { 1'b0, (Sub)?~B:B } + Carry_In;
|
|
end
|
|
endfunction // AddSub4
|
|
|
|
function [3:0] AddSub3;
|
|
input [2:0] A;
|
|
input [2:0] B;
|
|
input Sub;
|
|
input Carry_In;
|
|
begin
|
|
AddSub3 = { 1'b0, A } + { 1'b0, (Sub)?~B:B } + Carry_In;
|
|
end
|
|
endfunction // AddSub4
|
|
|
|
function [1:0] AddSub1;
|
|
input A;
|
|
input B;
|
|
input Sub;
|
|
input Carry_In;
|
|
begin
|
|
AddSub1 = { 1'b0, A } + { 1'b0, (Sub)?~B:B } + Carry_In;
|
|
end
|
|
endfunction // AddSub4
|
|
|
|
// AddSub variables (temporary signals)
|
|
reg UseCarry;
|
|
reg Carry7_v;
|
|
reg OverFlow_v;
|
|
reg HalfCarry_v;
|
|
reg Carry_v;
|
|
reg [7:0] Q_v;
|
|
|
|
reg [7:0] BitMask;
|
|
|
|
|
|
always @(/*AUTOSENSE*/ALU_Op or BusA or BusB or F_In or IR)
|
|
begin
|
|
case (IR[5:3])
|
|
3'b000 : BitMask = 8'b00000001;
|
|
3'b001 : BitMask = 8'b00000010;
|
|
3'b010 : BitMask = 8'b00000100;
|
|
3'b011 : BitMask = 8'b00001000;
|
|
3'b100 : BitMask = 8'b00010000;
|
|
3'b101 : BitMask = 8'b00100000;
|
|
3'b110 : BitMask = 8'b01000000;
|
|
default: BitMask = 8'b10000000;
|
|
endcase // case(IR[5:3])
|
|
|
|
UseCarry = ~ ALU_Op[2] && ALU_Op[0];
|
|
{ HalfCarry_v, Q_v[3:0] } = AddSub4(BusA[3:0], BusB[3:0], ALU_Op[1], ALU_Op[1] ^ (UseCarry && F_In[Flag_C]) );
|
|
{ Carry7_v, Q_v[6:4] } = AddSub3(BusA[6:4], BusB[6:4], ALU_Op[1], HalfCarry_v);
|
|
{ Carry_v, Q_v[7] } = AddSub1(BusA[7], BusB[7], ALU_Op[1], Carry7_v);
|
|
OverFlow_v = Carry_v ^ Carry7_v;
|
|
end // always @ *
|
|
|
|
reg [7:0] Q_t;
|
|
reg [8:0] DAA_Q;
|
|
|
|
always @ (/*AUTOSENSE*/ALU_Op or Arith16 or BitMask or BusA or BusB
|
|
or Carry_v or F_In or HalfCarry_v or IR or ISet
|
|
or OverFlow_v or Q_v or Z16)
|
|
begin
|
|
Q_t = 8'hxx;
|
|
DAA_Q = {9{1'bx}};
|
|
|
|
F_Out = F_In;
|
|
case (ALU_Op)
|
|
4'b0000, 4'b0001, 4'b0010, 4'b0011, 4'b0100, 4'b0101, 4'b0110, 4'b0111 :
|
|
begin
|
|
F_Out[Flag_N] = 1'b0;
|
|
F_Out[Flag_C] = 1'b0;
|
|
|
|
case (ALU_Op[2:0])
|
|
|
|
3'b000, 3'b001 : // ADD, ADC
|
|
begin
|
|
Q_t = Q_v;
|
|
F_Out[Flag_C] = Carry_v;
|
|
F_Out[Flag_H] = HalfCarry_v;
|
|
F_Out[Flag_P] = OverFlow_v;
|
|
end
|
|
|
|
3'b010, 3'b011, 3'b111 : // SUB, SBC, CP
|
|
begin
|
|
Q_t = Q_v;
|
|
F_Out[Flag_N] = 1'b1;
|
|
F_Out[Flag_C] = ~ Carry_v;
|
|
F_Out[Flag_H] = ~ HalfCarry_v;
|
|
F_Out[Flag_P] = OverFlow_v;
|
|
end
|
|
|
|
3'b100 : // AND
|
|
begin
|
|
Q_t[7:0] = BusA & BusB;
|
|
F_Out[Flag_H] = 1'b1;
|
|
end
|
|
|
|
3'b101 : // XOR
|
|
begin
|
|
Q_t[7:0] = BusA ^ BusB;
|
|
F_Out[Flag_H] = 1'b0;
|
|
end
|
|
|
|
default : // OR 3'b110
|
|
begin
|
|
Q_t[7:0] = BusA | BusB;
|
|
F_Out[Flag_H] = 1'b0;
|
|
end
|
|
|
|
endcase // case(ALU_OP[2:0])
|
|
|
|
if (ALU_Op[2:0] == 3'b111 )
|
|
begin // CP
|
|
F_Out[Flag_X] = BusB[3];
|
|
F_Out[Flag_Y] = BusB[5];
|
|
end
|
|
else
|
|
begin
|
|
F_Out[Flag_X] = Q_t[3];
|
|
F_Out[Flag_Y] = Q_t[5];
|
|
end
|
|
|
|
if (Q_t[7:0] == 8'b00000000 )
|
|
begin
|
|
F_Out[Flag_Z] = 1'b1;
|
|
if (Z16 == 1'b1 )
|
|
begin
|
|
F_Out[Flag_Z] = F_In[Flag_Z]; // 16 bit ADC,SBC
|
|
end
|
|
end
|
|
else
|
|
begin
|
|
F_Out[Flag_Z] = 1'b0;
|
|
end // else: !if(Q_t[7:0] == 8'b00000000 )
|
|
|
|
F_Out[Flag_S] = Q_t[7];
|
|
case (ALU_Op[2:0])
|
|
3'b000, 3'b001, 3'b010, 3'b011, 3'b111 : // ADD, ADC, SUB, SBC, CP
|
|
;
|
|
|
|
default :
|
|
F_Out[Flag_P] = ~(^Q_t);
|
|
endcase // case(ALU_Op[2:0])
|
|
|
|
if (Arith16 == 1'b1 )
|
|
begin
|
|
F_Out[Flag_S] = F_In[Flag_S];
|
|
F_Out[Flag_Z] = F_In[Flag_Z];
|
|
F_Out[Flag_P] = F_In[Flag_P];
|
|
end
|
|
end // case: 4'b0000, 4'b0001, 4'b0010, 4'b0011, 4'b0100, 4'b0101, 4'b0110, 4'b0111
|
|
|
|
4'b1100 :
|
|
begin
|
|
// DAA
|
|
F_Out[Flag_H] = F_In[Flag_H];
|
|
F_Out[Flag_C] = F_In[Flag_C];
|
|
DAA_Q[7:0] = BusA;
|
|
DAA_Q[8] = 1'b0;
|
|
if (F_In[Flag_N] == 1'b0 )
|
|
begin
|
|
// After addition
|
|
// Alow > 9 || H == 1
|
|
if (DAA_Q[3:0] > 9 || F_In[Flag_H] == 1'b1 )
|
|
begin
|
|
if ((DAA_Q[3:0] > 9) )
|
|
begin
|
|
F_Out[Flag_H] = 1'b1;
|
|
end
|
|
else
|
|
begin
|
|
F_Out[Flag_H] = 1'b0;
|
|
end
|
|
DAA_Q = DAA_Q + 6;
|
|
end // if (DAA_Q[3:0] > 9 || F_In[Flag_H] == 1'b1 )
|
|
|
|
// new Ahigh > 9 || C == 1
|
|
if (DAA_Q[8:4] > 9 || F_In[Flag_C] == 1'b1 )
|
|
begin
|
|
DAA_Q = DAA_Q + 96; // 0x60
|
|
end
|
|
end
|
|
else
|
|
begin
|
|
// After subtraction
|
|
if (DAA_Q[3:0] > 9 || F_In[Flag_H] == 1'b1 )
|
|
begin
|
|
if (DAA_Q[3:0] > 5 )
|
|
begin
|
|
F_Out[Flag_H] = 1'b0;
|
|
end
|
|
DAA_Q[7:0] = DAA_Q[7:0] - 6;
|
|
end
|
|
if (BusA > 153 || F_In[Flag_C] == 1'b1 )
|
|
begin
|
|
DAA_Q = DAA_Q - 352; // 0x160
|
|
end
|
|
end // else: !if(F_In[Flag_N] == 1'b0 )
|
|
|
|
F_Out[Flag_X] = DAA_Q[3];
|
|
F_Out[Flag_Y] = DAA_Q[5];
|
|
F_Out[Flag_C] = F_In[Flag_C] || DAA_Q[8];
|
|
Q_t = DAA_Q[7:0];
|
|
|
|
if (DAA_Q[7:0] == 8'b00000000 )
|
|
begin
|
|
F_Out[Flag_Z] = 1'b1;
|
|
end
|
|
else
|
|
begin
|
|
F_Out[Flag_Z] = 1'b0;
|
|
end
|
|
|
|
F_Out[Flag_S] = DAA_Q[7];
|
|
F_Out[Flag_P] = ~ (^DAA_Q);
|
|
end // case: 4'b1100
|
|
|
|
4'b1101, 4'b1110 :
|
|
begin
|
|
// RLD, RRD
|
|
Q_t[7:4] = BusA[7:4];
|
|
if (ALU_Op[0] == 1'b1 )
|
|
begin
|
|
Q_t[3:0] = BusB[7:4];
|
|
end
|
|
else
|
|
begin
|
|
Q_t[3:0] = BusB[3:0];
|
|
end
|
|
F_Out[Flag_H] = 1'b0;
|
|
F_Out[Flag_N] = 1'b0;
|
|
F_Out[Flag_X] = Q_t[3];
|
|
F_Out[Flag_Y] = Q_t[5];
|
|
if (Q_t[7:0] == 8'b00000000 )
|
|
begin
|
|
F_Out[Flag_Z] = 1'b1;
|
|
end
|
|
else
|
|
begin
|
|
F_Out[Flag_Z] = 1'b0;
|
|
end
|
|
F_Out[Flag_S] = Q_t[7];
|
|
F_Out[Flag_P] = ~(^Q_t);
|
|
end // case: when 4'b1101, 4'b1110
|
|
|
|
4'b1001 :
|
|
begin
|
|
// BIT
|
|
Q_t[7:0] = BusB & BitMask;
|
|
F_Out[Flag_S] = Q_t[7];
|
|
if (Q_t[7:0] == 8'b00000000 )
|
|
begin
|
|
F_Out[Flag_Z] = 1'b1;
|
|
F_Out[Flag_P] = 1'b1;
|
|
end
|
|
else
|
|
begin
|
|
F_Out[Flag_Z] = 1'b0;
|
|
F_Out[Flag_P] = 1'b0;
|
|
end
|
|
F_Out[Flag_H] = 1'b1;
|
|
F_Out[Flag_N] = 1'b0;
|
|
F_Out[Flag_X] = 1'b0;
|
|
F_Out[Flag_Y] = 1'b0;
|
|
if (IR[2:0] != 3'b110 )
|
|
begin
|
|
F_Out[Flag_X] = BusB[3];
|
|
F_Out[Flag_Y] = BusB[5];
|
|
end
|
|
end // case: when 4'b1001
|
|
|
|
4'b1010 :
|
|
// SET
|
|
Q_t[7:0] = BusB | BitMask;
|
|
|
|
4'b1011 :
|
|
// RES
|
|
Q_t[7:0] = BusB & ~ BitMask;
|
|
|
|
4'b1000 :
|
|
begin
|
|
// ROT
|
|
case (IR[5:3])
|
|
3'b000 : // RLC
|
|
begin
|
|
Q_t[7:1] = BusA[6:0];
|
|
Q_t[0] = BusA[7];
|
|
F_Out[Flag_C] = BusA[7];
|
|
end
|
|
|
|
3'b010 : // RL
|
|
begin
|
|
Q_t[7:1] = BusA[6:0];
|
|
Q_t[0] = F_In[Flag_C];
|
|
F_Out[Flag_C] = BusA[7];
|
|
end
|
|
|
|
3'b001 : // RRC
|
|
begin
|
|
Q_t[6:0] = BusA[7:1];
|
|
Q_t[7] = BusA[0];
|
|
F_Out[Flag_C] = BusA[0];
|
|
end
|
|
|
|
3'b011 : // RR
|
|
begin
|
|
Q_t[6:0] = BusA[7:1];
|
|
Q_t[7] = F_In[Flag_C];
|
|
F_Out[Flag_C] = BusA[0];
|
|
end
|
|
|
|
3'b100 : // SLA
|
|
begin
|
|
Q_t[7:1] = BusA[6:0];
|
|
Q_t[0] = 1'b0;
|
|
F_Out[Flag_C] = BusA[7];
|
|
end
|
|
|
|
3'b110 : // SLL (Undocumented) / SWAP
|
|
begin
|
|
if (Mode == 3 )
|
|
begin
|
|
Q_t[7:4] = BusA[3:0];
|
|
Q_t[3:0] = BusA[7:4];
|
|
F_Out[Flag_C] = 1'b0;
|
|
end
|
|
else
|
|
begin
|
|
Q_t[7:1] = BusA[6:0];
|
|
Q_t[0] = 1'b1;
|
|
F_Out[Flag_C] = BusA[7];
|
|
end // else: !if(Mode == 3 )
|
|
end // case: 3'b110
|
|
|
|
3'b101 : // SRA
|
|
begin
|
|
Q_t[6:0] = BusA[7:1];
|
|
Q_t[7] = BusA[7];
|
|
F_Out[Flag_C] = BusA[0];
|
|
end
|
|
|
|
default : // SRL
|
|
begin
|
|
Q_t[6:0] = BusA[7:1];
|
|
Q_t[7] = 1'b0;
|
|
F_Out[Flag_C] = BusA[0];
|
|
end
|
|
endcase // case(IR[5:3])
|
|
|
|
F_Out[Flag_H] = 1'b0;
|
|
F_Out[Flag_N] = 1'b0;
|
|
F_Out[Flag_X] = Q_t[3];
|
|
F_Out[Flag_Y] = Q_t[5];
|
|
F_Out[Flag_S] = Q_t[7];
|
|
if (Q_t[7:0] == 8'b00000000 )
|
|
begin
|
|
F_Out[Flag_Z] = 1'b1;
|
|
end
|
|
else
|
|
begin
|
|
F_Out[Flag_Z] = 1'b0;
|
|
end
|
|
F_Out[Flag_P] = ~(^Q_t);
|
|
|
|
if (ISet == 2'b00 )
|
|
begin
|
|
F_Out[Flag_P] = F_In[Flag_P];
|
|
F_Out[Flag_S] = F_In[Flag_S];
|
|
F_Out[Flag_Z] = F_In[Flag_Z];
|
|
end
|
|
end // case: 4'b1000
|
|
|
|
|
|
default :
|
|
;
|
|
|
|
endcase // case(ALU_Op)
|
|
|
|
Q = Q_t;
|
|
end // always @ (Arith16, ALU_OP, F_In, BusA, BusB, IR, Q_v, Carry_v, HalfCarry_v, OverFlow_v, BitMask, ISet, Z16)
|
|
|
|
endmodule // T80_ALU
|