1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-01-18 09:02:08 +00:00
Gehstock.Mist_FPGA/common/CPU/x86/InsnDecoder.sv
2019-07-22 23:42:05 +02:00

238 lines
8.4 KiB
Systemverilog

// Copyright Jamie Iles, 2018
//
// This file is part of s80x86.
//
// s80x86 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// s80x86 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with s80x86. If not, see <http://www.gnu.org/licenses/>.
module InsnDecoder(input logic clk,
input logic reset,
input logic flush,
input logic stall,
input logic nearly_full,
output logic fifo_rd_en,
input logic [7:0] fifo_rd_data,
input logic fifo_empty,
output Instruction instruction,
output logic complete,
output logic immed_start,
input logic immed_complete,
output logic immed_is_8bit,
input logic [15:0] immediate);
typedef enum bit[2:0] {
STATE_OPCODE,
STATE_MODRM,
STATE_DISPLACEMENT,
STATE_IMMED1,
STATE_IMMED2,
STATE_WAIT_SPACE
} state_t;
state_t state, next_state;
logic insn_complete;
assign fifo_rd_en = ~reset & ~fifo_empty & ~immed_start & state != STATE_WAIT_SPACE;
function logic has_displacement;
// verilator lint_off UNUSED
input logic [7:0] mbyte;
// verilator lint_on UNUSED
has_displacement = (mbyte[2:0] == 3'b110 && mbyte[7:6] == 2'b00) || ^mbyte[7:6];
endfunction
function logic is_prefix;
input logic [7:0] b;
case (b)
8'h26, 8'h2e, 8'h36, 8'h3e, 8'hf2, 8'hf3, 8'hf0: is_prefix = 1'b1;
default: is_prefix = 1'b0;
endcase
endfunction
function state_t next_instr;
next_instr = (nearly_full || stall) && insn_complete ? STATE_WAIT_SPACE : STATE_OPCODE;
endfunction
always_comb begin
insn_complete = 1'b0;
case (state)
STATE_OPCODE: begin
insn_complete = (!fifo_empty && !is_prefix(fifo_rd_data) &&
!insn_has_modrm(fifo_rd_data) &&
insn_immed_count(fifo_rd_data, 3'b0) == 2'b00) ||
instruction.invalid;
next_state = fifo_empty ? STATE_OPCODE :
is_prefix(fifo_rd_data) ? STATE_OPCODE :
insn_has_modrm(fifo_rd_data) ? STATE_MODRM :
insn_immed_count(fifo_rd_data, 3'b0) != 2'b00 ? STATE_IMMED1 :
insn_complete ? next_instr() : STATE_OPCODE;
end
STATE_MODRM: begin
insn_complete = !fifo_empty && !has_displacement(fifo_rd_data) &&
insn_immed_count(instruction.opcode, fifo_rd_data[5:3]) == 2'b00;
next_state = fifo_empty ? STATE_MODRM :
has_displacement(fifo_rd_data) ? STATE_DISPLACEMENT :
insn_immed_count(instruction.opcode, fifo_rd_data[5:3]) != 2'b00 ?
STATE_IMMED1 : next_instr();
end
STATE_DISPLACEMENT: begin
insn_complete = immed_complete &&
insn_immed_count(instruction.opcode, instruction.mod_rm[5:3]) == 2'b0;
next_state = immed_complete ?
(insn_immed_count(instruction.opcode, instruction.mod_rm[5:3]) != 2'b00 ?
STATE_IMMED1 : next_instr()) : STATE_DISPLACEMENT;
end
STATE_IMMED1: begin
insn_complete = insn_immed_count(instruction.opcode,
instruction.mod_rm[5:3]) == 2'b01 &&
immed_complete;
next_state = immed_complete ?
(insn_immed_count(instruction.opcode, instruction.mod_rm[5:3]) == 2'd2 ?
STATE_IMMED2 : next_instr()) :
STATE_IMMED1;
end
STATE_IMMED2: begin
insn_complete = immed_complete;
next_state = immed_complete ? next_instr(): STATE_IMMED2;
end
STATE_WAIT_SPACE: begin
next_state = stall ? STATE_WAIT_SPACE : STATE_OPCODE;
end
default: next_state = STATE_OPCODE;
endcase
if (reset || flush)
next_state = STATE_OPCODE;
end
task add_segment_override;
input logic [1:0] seg;
begin
instruction.has_segment_override <= 1'b1;
instruction.segment <= SR_t'(seg);
end
endtask
always_ff @(posedge clk or posedge reset) begin
if (reset) begin
complete <= 1'b0;
instruction <= Instruction'(1'b0);
immed_start <= 1'b0;
end else if (flush) begin
complete <= 1'b0;
instruction <= Instruction'(1'b0);
immed_start <= 1'b0;
end else begin
if (complete && !stall) begin
instruction <= Instruction'(1'b0);
complete <= 1'b0;
end
if (insn_complete)
complete <= 1'b1;
case (state)
STATE_OPCODE: begin
if (!fifo_empty) begin
case (fifo_rd_data)
8'hf0: instruction.lock <= 1'b1;
8'hf2: instruction.rep <= REP_PREFIX_NE;
8'hf3: instruction.rep <= REP_PREFIX_E;
8'h26: add_segment_override(ES);
8'h2e: add_segment_override(CS);
8'h36: add_segment_override(SS);
8'h3e: add_segment_override(DS);
default: begin
instruction.opcode <= fifo_rd_data;
instruction.has_modrm <= insn_has_modrm(fifo_rd_data);
end
endcase
if (complete)
instruction.length <= 4'b1;
else if (!fifo_empty)
{instruction.invalid, instruction.length} <=
{instruction.invalid, instruction.length} + 5'b1;
if (next_state == STATE_IMMED1) begin
immed_start <= 1'b1;
immed_is_8bit <= insn_immed_is_8bit(fifo_rd_data,
3'b0, 1'b0);
end
end
end
STATE_MODRM: begin
if (!fifo_empty) begin
{instruction.invalid, instruction.length} <=
{instruction.invalid, instruction.length} + 5'b1;
instruction.mod_rm <= fifo_rd_data;
end
if (next_state == STATE_DISPLACEMENT) begin
immed_start <= 1'b1;
immed_is_8bit <= fifo_rd_data[7:6] == 2'b01;
end else if (next_state == STATE_IMMED1) begin
immed_start <= 1'b1;
immed_is_8bit <= insn_immed_is_8bit(instruction.opcode,
fifo_rd_data[5:3], 1'b0);
end
end
STATE_DISPLACEMENT: begin
if (immed_complete) begin
instruction.displacement <= immediate;
{instruction.invalid, instruction.length} <=
{instruction.invalid, instruction.length} +
(immed_is_8bit ? 5'd1 : 5'd2);
immed_start <= 1'b0;
end
if (next_state == STATE_IMMED1) begin
immed_start <= 1'b1;
immed_is_8bit <= insn_immed_is_8bit(instruction.opcode,
instruction.mod_rm[5:3], 1'b0);
end
end
STATE_IMMED1: begin
if (immed_complete) begin
instruction.immediates[0] <= immediate;
{instruction.invalid, instruction.length} <=
{instruction.invalid, instruction.length} +
(immed_is_8bit ? 5'd1 : 5'd2);
immed_start <= 1'b0;
end
if (next_state == STATE_IMMED2) begin
immed_start <= 1'b1;
immed_is_8bit <= insn_immed_is_8bit(instruction.opcode,
instruction.mod_rm[5:3], 1'b1);
end
end
STATE_IMMED2: begin
if (immed_complete) begin
instruction.immediates[1] <= immediate;
{instruction.invalid, instruction.length} <=
{instruction.invalid, instruction.length} +
(immed_is_8bit ? 5'd1 : 5'd2);
immed_start <= 1'b0;
end
end
default: ;
endcase
end
end
always_ff @(posedge clk)
state <= next_state;
endmodule