mirror of
https://github.com/sxpert/hp-saturn
synced 2024-11-16 19:50:19 +01:00
837 lines
No EOL
21 KiB
Verilog
837 lines
No EOL
21 KiB
Verilog
/*
|
|
(c) Raphaël Jacquot 2019
|
|
|
|
This file is part of hp_saturn.
|
|
|
|
hp_saturn 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
|
|
any later version.
|
|
|
|
hp_saturn 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 Foobar. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Instruction decoder module
|
|
*
|
|
*****************************************************************************/
|
|
|
|
`include "def-fields.v"
|
|
`include "def-alu.v"
|
|
|
|
module saturn_decoder(
|
|
i_clk, i_reset, i_cycles, i_en_dbg, i_en_dec,
|
|
i_bus_load_pc, i_stalled,
|
|
i_pc, i_nibble,
|
|
|
|
i_reg_p,
|
|
|
|
o_inc_pc, o_push, o_pop,
|
|
o_dec_error,
|
|
`ifdef SIM
|
|
o_unimplemented,
|
|
`endif
|
|
o_alu_debug,
|
|
|
|
o_ins_addr, o_ins_decoded,
|
|
|
|
o_fields_table, o_field, o_field_valid, o_field_start, o_field_last,
|
|
o_imm_value,
|
|
|
|
o_alu_op, o_alu_no_stall, o_reg_dest, o_reg_src1, o_reg_src2,
|
|
|
|
o_ins_rtn, o_set_xm, o_en_intr,
|
|
o_set_carry, o_test_carry, o_carry_val,
|
|
o_ins_set_mode, o_mode_dec,
|
|
o_ins_alu_op, o_ins_test_go,
|
|
o_ins_reset, o_ins_config,
|
|
o_ins_mem_xfr, o_xfr_dir_out,
|
|
|
|
o_dbg_nibbles, o_dbg_nb_nbls, o_mem_load, o_mem_pos
|
|
);
|
|
|
|
/*
|
|
* module input / output ports
|
|
*/
|
|
input wire [0:0] i_clk;
|
|
input wire [0:0] i_reset;
|
|
input wire [31:0] i_cycles;
|
|
input wire [0:0] i_en_dbg;
|
|
input wire [0:0] i_en_dec;
|
|
input wire [0:0] i_bus_load_pc;
|
|
input wire [0:0] i_stalled;
|
|
input wire [19:0] i_pc;
|
|
input wire [3:0] i_nibble;
|
|
|
|
input wire [3:0] i_reg_p;
|
|
|
|
output reg [0:0] o_inc_pc;
|
|
output reg [0:0] o_push;
|
|
output reg [0:0] o_pop;
|
|
output reg [0:0] o_dec_error;
|
|
`ifdef SIM
|
|
output reg [0:0] o_unimplemented;
|
|
`endif
|
|
output reg [0:0] o_alu_debug;
|
|
|
|
// instructions related outputs
|
|
output reg [19:0] o_ins_addr;
|
|
output reg [0:0] o_ins_decoded;
|
|
|
|
output reg [1:0] o_fields_table;
|
|
output reg [3:0] o_field;
|
|
output reg o_field_valid;
|
|
output reg [3:0] o_field_start;
|
|
output reg [3:0] o_field_last;
|
|
output reg [3:0] o_imm_value;
|
|
|
|
output reg [4:0] o_alu_op;
|
|
output reg [0:0] o_alu_no_stall;
|
|
output reg [4:0] o_reg_dest;
|
|
output reg [4:0] o_reg_src1;
|
|
output reg [4:0] o_reg_src2;
|
|
|
|
// rtn specific
|
|
output reg [0:0] o_ins_rtn;
|
|
output reg [0:0] o_set_xm;
|
|
output reg [0:0] o_set_carry;
|
|
output reg [0:0] o_test_carry;
|
|
output reg [0:0] o_carry_val;
|
|
output reg [0:0] o_en_intr;
|
|
|
|
// setdec/hex
|
|
output reg [0:0] o_ins_set_mode;
|
|
output reg [0:0] o_mode_dec;
|
|
|
|
// alu_operations
|
|
output reg [0:0] o_ins_alu_op;
|
|
output reg [0:0] o_ins_test_go;
|
|
|
|
// bus operations
|
|
output reg [0:0] o_ins_reset;
|
|
output reg [0:0] o_ins_config;
|
|
output reg [0:0] o_ins_mem_xfr;
|
|
output reg [0:0] o_xfr_dir_out;
|
|
|
|
/* data used by the debugger
|
|
*
|
|
*/
|
|
output reg [(21*4-1):0] o_dbg_nibbles;
|
|
output reg [4:0] o_dbg_nb_nbls;
|
|
output reg [63:0] o_mem_load;
|
|
output reg [4:0] o_mem_pos;
|
|
|
|
|
|
/*
|
|
* state registers
|
|
*/
|
|
|
|
reg [31:0] inst_counter;
|
|
reg [0:0] next_nibble;
|
|
reg [4:0] inst_cycles;
|
|
|
|
reg inval_opcode_regs;
|
|
|
|
|
|
initial begin
|
|
`ifdef SIM
|
|
// $monitor({"i_clk %b | i_reset %b | i_cycles %d | i_en_dec %b | i_en_exec %b |",
|
|
// " next_nibble %b | instr_start %b | i_nibble %h"},
|
|
// i_clk, i_reset, i_cycles, i_en_dec, i_en_exec, next_nibble,
|
|
// instr_start, i_nibble);
|
|
// $monitor("i_en_dec %b | i_cycles %d | nb %h | cont %b | b0x %b | rtn %b | sxm %b | sc %b | cv %b",
|
|
// i_en_dec, i_cycles, i_nibble, next_nibble, block_0x, ins_rtn, set_xm, set_carry, carry_val);
|
|
`endif
|
|
end
|
|
|
|
`include "saturn_decoder_debugger.v"
|
|
|
|
/******************************************************************************
|
|
*
|
|
* handles part of the instruction decoding,
|
|
* acts as the main FSM
|
|
*
|
|
*****************************************************************************/
|
|
|
|
// general variables
|
|
reg use_fields_tbl;
|
|
|
|
wire count_cycles;
|
|
wire decoder_active;
|
|
// wire decoder_stalled;
|
|
wire do_on_first_nibble;
|
|
wire do_on_other_nibbles;
|
|
|
|
assign count_cycles = !i_reset && i_en_dec && (next_nibble || i_stalled);
|
|
assign decoder_active = !i_reset && i_en_dec && !i_stalled;
|
|
// assign decoder_stalled = !i_reset && i_en_dec && i_stalled;
|
|
assign do_on_first_nibble = decoder_active && !next_nibble;
|
|
assign do_on_other_nibbles = decoder_active && next_nibble;
|
|
|
|
// all regs and wires for the decoder states
|
|
`include "saturn_decoder_block_vars.v"
|
|
|
|
/*
|
|
* variables specific to a particular use
|
|
*/
|
|
|
|
reg [4:0] mem_load_max;
|
|
|
|
/* most instructions are groupped by sets of 4 with
|
|
* varrying series of registers that are common
|
|
* this generates all the required series from i_nibble
|
|
*/
|
|
|
|
wire [4:0] dbg_write_pos;
|
|
assign dbg_write_pos = (!next_nibble?0:o_dbg_nb_nbls);
|
|
|
|
always @(posedge i_clk) begin
|
|
|
|
if (i_reset) begin
|
|
inst_cycles <= 0;
|
|
inst_counter <= 0;
|
|
next_nibble <= 0;
|
|
use_fields_tbl <= 0;
|
|
o_inc_pc <= 1;
|
|
o_dec_error <= 0;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
o_alu_debug <= 0;
|
|
o_ins_decoded <= 0;
|
|
o_alu_op <= 0;
|
|
o_ins_rtn <= 0;
|
|
o_push <= 0;
|
|
o_pop <= 0;
|
|
o_ins_set_mode <= 0;
|
|
o_ins_reset <= 0;
|
|
o_ins_config <= 0;
|
|
o_ins_mem_xfr <= 0;
|
|
o_xfr_dir_out <= 0;
|
|
o_test_carry <= 0;
|
|
end
|
|
|
|
if (decoder_active) begin
|
|
/*
|
|
* stuff that is always done
|
|
*/
|
|
`ifdef SIM
|
|
// $display("DEC_RUN 2: nibble %h", i_nibble);
|
|
`endif
|
|
o_inc_pc <= 1; // may be set to 0 later
|
|
o_dbg_nibbles[dbg_write_pos*4+:4] <= i_nibble;
|
|
o_dbg_nb_nbls <= o_dbg_nb_nbls + 1;
|
|
end
|
|
|
|
// if (decoder_stalled) begin
|
|
// $display("DEC_STAL 2:");
|
|
// end
|
|
|
|
if (count_cycles) begin
|
|
inst_cycles <= inst_cycles + 1;
|
|
end
|
|
|
|
/*
|
|
* cleanup
|
|
*/
|
|
if (do_on_first_nibble) begin
|
|
inst_counter <= inst_counter + 1;
|
|
inst_cycles <= 1;
|
|
next_nibble <= 1;
|
|
use_fields_tbl <= 0;
|
|
o_alu_debug <= 0;
|
|
|
|
o_push <= 0;
|
|
o_pop <= 0;
|
|
|
|
o_ins_decoded <= 0;
|
|
`ifdef SIM
|
|
o_unimplemented <= 1;
|
|
`endif
|
|
// store the address where the instruction starts
|
|
o_ins_addr <= i_pc;
|
|
|
|
// decoder subroutine states
|
|
|
|
block_load_reg_imm <= 0;
|
|
|
|
|
|
// cleanup fields table variables
|
|
go_fields_table <= 0;
|
|
o_fields_table <= 3;
|
|
|
|
o_alu_op <= 0;
|
|
o_alu_no_stall <= 0;
|
|
|
|
o_ins_rtn <= 0;
|
|
o_set_xm <= 0;
|
|
o_set_carry <= 0;
|
|
o_carry_val <= 0;
|
|
|
|
o_ins_set_mode <= 0;
|
|
o_mode_dec <= 0;
|
|
|
|
o_ins_alu_op <= 0;
|
|
o_ins_test_go <= 0;
|
|
|
|
// bus instructions
|
|
$display("cleanup instruction modes");
|
|
o_ins_reset <= 0;
|
|
o_ins_config <= 0;
|
|
o_ins_mem_xfr <= 0;
|
|
o_xfr_dir_out <= 0;
|
|
|
|
// counters for debugger info
|
|
o_dbg_nb_nbls <= 1;
|
|
o_mem_pos <= 0;
|
|
|
|
/*
|
|
* x first nibble
|
|
*/
|
|
if (block_jump_test) begin
|
|
// $display("BLOCK JUMP_TEST ON %h", i_nibble);
|
|
o_pop <= i_nibble == 0;
|
|
o_alu_no_stall <= 1;
|
|
// o_alu_debug <= 1;
|
|
o_ins_test_go <= 1;
|
|
o_imm_value <= i_nibble;
|
|
block_jump_test2 <= 1;
|
|
block_jump_test <= 0;
|
|
end else begin
|
|
// assign block regs
|
|
// $display("FIRST NIBBLE %h", i_nibble);
|
|
case (i_nibble)
|
|
4'h0: block_0x <= 1;
|
|
4'h1: block_1x <= 1;
|
|
4'h2: block_2x <= 1;
|
|
4'h3: block_3x <= 1;
|
|
4'h4, 4'h5: begin
|
|
// 400 RTNC
|
|
// 420 NOP3
|
|
// 4xy GOC
|
|
// 500 RTNNC
|
|
// 5xy GONC
|
|
o_alu_debug <= 1;
|
|
o_alu_no_stall <= 1;
|
|
o_alu_op <= `ALU_OP_JMP_REL2;
|
|
mem_load_max <= 1;
|
|
o_mem_pos <= 0;
|
|
o_test_carry <= 1;
|
|
o_carry_val <= !i_nibble[0];
|
|
block_jmp <= 1;
|
|
end
|
|
4'h6, 4'h7: begin
|
|
// 6xxx GOTO
|
|
// 7xxx GOSUB
|
|
o_alu_no_stall <= 1;
|
|
o_alu_op <= `ALU_OP_JMP_REL3;
|
|
mem_load_max <= 2;
|
|
o_mem_pos <= 0;
|
|
o_push <= i_nibble[0];
|
|
block_jmp <= 1;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
end
|
|
4'h8: block_8x <= 1;
|
|
4'h9: begin
|
|
go_fields_table <= 1;
|
|
// we don't know, safe bet is table a, but could be table b,
|
|
// works either way, table is fixed on the next nibble
|
|
o_fields_table <= `FT_TABLE_a;
|
|
block_9x <= 1;
|
|
end
|
|
4'hA: begin
|
|
go_fields_table <= 1;
|
|
// we don't know, safe bet is table a, but could be table b,
|
|
// works either way, table is fixed on the next nibble
|
|
o_fields_table <= `FT_TABLE_a;
|
|
block_Ax <= 1;
|
|
end
|
|
4'hB: begin
|
|
go_fields_table <= 1;
|
|
// we don't know, safe bet is table a, but could be table b,
|
|
// works either way, table is fixed on the next nibble
|
|
o_fields_table <= `FT_TABLE_a;
|
|
block_Bx <= 1;
|
|
end
|
|
4'hC: block_Cx <= 1;
|
|
4'hD: block_Dx <= 1;
|
|
4'hF: block_Fx <= 1;
|
|
default: begin
|
|
`ifdef SIM
|
|
$display("DEC_INIT 2: nibble %h not handled", i_nibble);
|
|
`endif
|
|
o_dec_error <= 1;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
if (do_block_jump_test2) begin
|
|
// $display("BLOCK_JUMP_TEST_2 %h | pop %b | pop-nxt %b", i_nibble, o_pop, (i_nibble == 0) && o_pop);
|
|
o_alu_op <= `ALU_OP_TEST_GO;
|
|
o_imm_value <= i_nibble;
|
|
o_ins_rtn <= (i_nibble == 0) && o_pop;
|
|
block_jump_test2 <= 0;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
end
|
|
|
|
/******************************************************************************
|
|
*
|
|
* 0x
|
|
*
|
|
* 00 RTNSXM 08 CLRST
|
|
* 01 RTN 09 C=ST
|
|
* 02 RTNSC 0A ST=C
|
|
* 03 RTNCC 0B CSTEX
|
|
* 04 SETHEX 0C P=P+1
|
|
* 05 SETDEC 0D P=P-1
|
|
* 06 RSTK=C
|
|
* 07 C=RSTK 0F RTI
|
|
*
|
|
*****************************************************************************/
|
|
|
|
if (do_block_0x) begin
|
|
case (i_nibble)
|
|
4'h0, 4'h1, 4'h2, 4'h3, 4'hF: begin
|
|
o_ins_rtn <= 1;
|
|
o_pop <= 1;
|
|
o_set_xm <= i_nibble == 4'h0;
|
|
o_set_carry <= !i_nibble[3] && i_nibble[1];
|
|
o_carry_val <= i_nibble[1] && !i_nibble[0];
|
|
o_en_intr <= i_nibble[3];
|
|
`ifdef SIM
|
|
o_unimplemented <= i_nibble[3];
|
|
`endif
|
|
end
|
|
4'h4, 4'h5: begin
|
|
// 04 SETHEX
|
|
// 05 SETDEC
|
|
o_ins_set_mode <= 1;
|
|
o_mode_dec <= (i_nibble[0]);
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
end
|
|
4'h6, 4'h7: begin
|
|
// 06 RSTK=C
|
|
// 07 C=RSTK
|
|
// those 2 are alu copy ops between RSTK and C
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_COPY;
|
|
o_push <= !i_nibble[0];
|
|
o_pop <= i_nibble[0];
|
|
end
|
|
4'h8: begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_ZERO;
|
|
end
|
|
4'h9, 4'hA: begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_COPY;
|
|
end
|
|
4'hB: begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_EXCH;
|
|
end
|
|
4'hC, 4'hD: begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= i_nibble[0]?`ALU_OP_DEC:`ALU_OP_INC;
|
|
end
|
|
4'hE: o_fields_table <= `FT_TABLE_f;
|
|
default: begin
|
|
`ifdef SIM
|
|
$display("block_0x: nibble %h not handled", i_nibble);
|
|
`endif
|
|
o_dec_error <= 1;
|
|
end
|
|
endcase
|
|
next_nibble <= (i_nibble == 4'hE);
|
|
block_0Efx <= (i_nibble == 4'hE);
|
|
go_fields_table <= (i_nibble == 4'hE);
|
|
o_ins_decoded <= (i_nibble != 4'hE);
|
|
block_0x <= 0;
|
|
end
|
|
|
|
/******************************************************************************
|
|
*
|
|
* 0Ex R1=R1[&!]R2 table_f
|
|
*
|
|
*****************************************************************************/
|
|
|
|
if (do_block_0Efx && !in_fields_table) begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= (!i_nibble[3])?`ALU_OP_AND:`ALU_OP_OR;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
end
|
|
|
|
/******************************************************************************
|
|
*
|
|
* 1x jump table to other things
|
|
*
|
|
*****************************************************************************/
|
|
|
|
if (do_block_1x) begin
|
|
case (i_nibble)
|
|
4'h0: // save A/C to Rn W
|
|
block_save_to_R_W <= 1;
|
|
4'h1: // restore A/C from Rn W
|
|
block_rest_from_R_W <= 1;
|
|
4'h2: // exchange A/C with Rn W
|
|
block_exch_with_R_W <= 1;
|
|
4'h3: // move/exch A/C with Dn A/[0:3]
|
|
block_13x <= 1;
|
|
4'h4, 4'h5: // DAT[01]=[AC] <field>
|
|
begin
|
|
o_alu_debug <= 0;
|
|
`ifdef SIM
|
|
$display("block_1x %h | use table <= %b", i_nibble, i_nibble[0]);
|
|
`endif
|
|
block_14x_15xx <= 1;
|
|
use_fields_tbl <= i_nibble[0];
|
|
end
|
|
4'h6, 4'h7,
|
|
4'h8, 4'hC: // D[01]=D[01][+-] n+1;
|
|
begin
|
|
block_pointer_arith_const <= 1;
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= i_nibble[1]?`ALU_OP_ADD:`ALU_OP_SUB;
|
|
end
|
|
4'h9, 4'hA,
|
|
4'hB, 4'hD,
|
|
4'hE, 4'hF: // D[0]=([245]) <stuff>
|
|
begin
|
|
mem_load_max <= {2'b00, i_nibble[1], !i_nibble[1], i_nibble[1] && i_nibble[0]};
|
|
o_mem_pos <= 0;
|
|
block_load_reg_imm <= 1;
|
|
o_alu_no_stall <= 1;
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_COPY;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
end
|
|
endcase
|
|
block_1x <= 0;
|
|
end
|
|
|
|
if (do_block_save_to_R_W || do_block_rest_from_R_W) begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_COPY;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
block_save_to_R_W <= 0;
|
|
block_rest_from_R_W <= 0;
|
|
end
|
|
|
|
if (do_block_exch_with_R_W) begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_EXCH;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
end
|
|
|
|
if (do_block_13x) begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= i_nibble[1]?`ALU_OP_EXCH:`ALU_OP_COPY;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
block_13x <= 0;
|
|
end
|
|
|
|
if (do_block_14x_15xx) begin
|
|
`ifdef SIM
|
|
$display("block_14x_15xx nibble %h | use_tbl %b", i_nibble, use_fields_tbl);
|
|
$display("fields_table %d",i_nibble[3]?`FT_TABLE_value:`FT_TABLE_a);
|
|
`endif
|
|
// o_alu_debug <= 1;
|
|
o_fields_table <= i_nibble[3]?`FT_TABLE_value:`FT_TABLE_a;
|
|
// o_alu_op <= `ALU_OP_COPY;
|
|
go_fields_table <= use_fields_tbl;
|
|
use_fields_tbl <= 0;
|
|
|
|
// do not block when we're reading
|
|
// o_alu_no_stall <= !use_fields_tbl && i_nibble[1];
|
|
// o_alu_debug <= i_nibble[1];
|
|
|
|
// set the info about this being a memory transfer
|
|
o_ins_mem_xfr <= !(use_fields_tbl);
|
|
o_xfr_dir_out <= !i_nibble[1];
|
|
|
|
block_15xx <= use_fields_tbl;
|
|
|
|
// o_ins_alu_op <= !(use_fields_tbl);
|
|
next_nibble <= use_fields_tbl;
|
|
o_ins_decoded <= !(use_fields_tbl);
|
|
block_14x_15xx <= 0;
|
|
end
|
|
|
|
if (do_block_15xx) begin
|
|
`ifdef SIM
|
|
$display("block_15xx %h", i_nibble);
|
|
`endif
|
|
o_alu_debug <= 1;
|
|
// o_alu_no_stall <= 1;
|
|
// o_ins_alu_op <= 1;
|
|
o_ins_mem_xfr <= 1;
|
|
o_ins_decoded <= 1;
|
|
next_nibble <= 0;
|
|
block_15xx <= 0;
|
|
end
|
|
|
|
if (do_block_pointer_arith_const) begin
|
|
next_nibble <= 0;
|
|
o_imm_value <= i_nibble;
|
|
o_ins_decoded <= 1;
|
|
end
|
|
|
|
if (do_block_2x) begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_COPY;
|
|
o_imm_value <= i_nibble;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
block_2x <= 0;
|
|
end
|
|
|
|
if (do_block_3x) begin
|
|
$write("block load C hex %h\n", i_nibble);
|
|
mem_load_max <= i_nibble + 1;
|
|
o_mem_pos <= 0;
|
|
o_alu_no_stall <= 1;
|
|
o_alu_op <= `ALU_OP_COPY;
|
|
block_load_reg_imm <= 1;
|
|
o_ins_alu_op <= 1;
|
|
block_3x <= 0;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
end
|
|
|
|
`include "saturn_decoder_block_8.v"
|
|
|
|
/*
|
|
* Block 9xx
|
|
*
|
|
*
|
|
*/
|
|
|
|
if (do_block_9x) begin
|
|
`ifdef SIM
|
|
$display("block_9x %h", i_nibble);
|
|
`endif
|
|
o_fields_table <= i_nibble[3]?`FT_TABLE_b:`FT_TABLE_a;
|
|
block_9ax <= !i_nibble[3];
|
|
block_9bx <= i_nibble[3];
|
|
block_9x <= 0;
|
|
end
|
|
|
|
if (do_block_9ax) begin
|
|
`ifdef SIM
|
|
$display("block_9ax %h", i_nibble);
|
|
`endif
|
|
o_dec_error <= 1;
|
|
block_9ax <= 0;
|
|
end
|
|
|
|
if (do_block_9bx) begin
|
|
`ifdef SIM
|
|
$display("block_9bx %h", i_nibble);
|
|
`endif
|
|
o_dec_error <= 1;
|
|
block_9bx <= 0;
|
|
end
|
|
|
|
/*
|
|
* Block Axx
|
|
* ra=ra+rb a
|
|
* ra=ra-1 a
|
|
* ra=0 b
|
|
* ra=rb b
|
|
* rarbEX b
|
|
*/
|
|
|
|
if (do_block_Ax) begin
|
|
o_fields_table <= i_nibble[3]?`FT_TABLE_b:`FT_TABLE_a;
|
|
block_Aax <= !i_nibble[3];
|
|
block_Abx <= i_nibble[3];
|
|
block_Ax <= 0;
|
|
end
|
|
|
|
if (do_block_Aax) begin
|
|
`ifdef SIM
|
|
$display("block_Aax %h", i_nibble);
|
|
`endif
|
|
o_dec_error <= 1;
|
|
end
|
|
|
|
if (do_block_Abx) begin
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= (i_nibble[3] && i_nibble[2])?`ALU_OP_EXCH:`ALU_OP_COPY;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
block_Abx <= 0;
|
|
end
|
|
|
|
|
|
/*
|
|
* Block Bxx
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
if (do_block_Bx) begin
|
|
o_fields_table <= i_nibble[3]?`FT_TABLE_b:`FT_TABLE_a;
|
|
block_Bax <= !i_nibble[3];
|
|
block_Bbx <= i_nibble[3];
|
|
block_Bx <= 0;
|
|
end
|
|
|
|
if (do_block_Bax) begin
|
|
`ifdef SIM
|
|
$display("block_Bax %h", i_nibble);
|
|
`endif
|
|
o_ins_alu_op <= 1;
|
|
case ({i_nibble[3],i_nibble[2]})
|
|
2'b00: o_alu_op <= `ALU_OP_SUB;
|
|
2'b01: o_alu_op <= `ALU_OP_INC;
|
|
2'b10: o_alu_op <= `ALU_OP_SUB;
|
|
2'b11: o_alu_op <= `ALU_OP_SUB;
|
|
endcase
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
block_Bax <= 0;
|
|
end
|
|
|
|
if (do_block_Bbx) begin
|
|
o_ins_alu_op <= 1;
|
|
case ({i_nibble[3],i_nibble[2]})
|
|
2'b00: o_alu_op <= `ALU_OP_SHL;
|
|
2'b01: o_alu_op <= `ALU_OP_SHR;
|
|
2'b10: o_alu_op <= `ALU_OP_2CMPL;
|
|
2'b11: o_alu_op <= `ALU_OP_1CMPL;
|
|
endcase
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
`ifdef SIM
|
|
o_unimplemented <= 0;
|
|
`endif
|
|
block_Bbx <= 0;
|
|
end
|
|
|
|
/*
|
|
* Block Cx
|
|
*
|
|
*
|
|
*/
|
|
|
|
if (do_block_Cx) begin
|
|
`ifdef SIM
|
|
$display("block_Cx %h", i_nibble);
|
|
`endif
|
|
// o_alu_debug <= 1;
|
|
o_fields_table <= `FT_TABLE_f;
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= (i_nibble[3] && i_nibble[2])?`ALU_OP_DEC:`ALU_OP_ADD;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
`ifdef SIM
|
|
// o_unimplemented <= 0;
|
|
`endif
|
|
block_Cx <= 0;
|
|
end
|
|
|
|
if (do_block_Dx) begin
|
|
`ifdef SIM
|
|
$display("block_Dx %h", i_nibble);
|
|
`endif
|
|
o_fields_table <= `FT_TABLE_f;
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= (i_nibble[3] && i_nibble[2])?`ALU_OP_EXCH:`ALU_OP_COPY;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
`ifdef SIM
|
|
// o_unimplemented <= 0;
|
|
`endif
|
|
block_Dx <= 0;
|
|
end
|
|
|
|
if (do_block_Fx) begin
|
|
`ifdef SIM
|
|
|
|
`endif
|
|
|
|
case (i_nibble)
|
|
4'h8, 4'h9, 4'hA, 4'hB: // r=-r A
|
|
begin
|
|
o_fields_table <= `FT_TABLE_f;
|
|
//o_alu_debug <= 1;
|
|
o_ins_alu_op <= 1;
|
|
o_alu_op <= `ALU_OP_2CMPL;
|
|
next_nibble <= 0;
|
|
o_ins_decoded <= 1;
|
|
end
|
|
default: begin
|
|
`ifdef SIM
|
|
$display("block_Fx %h error", i_nibble);
|
|
`endif
|
|
o_dec_error <= 1;
|
|
end
|
|
endcase
|
|
block_Fx <= 0;
|
|
end
|
|
|
|
// utilities
|
|
|
|
if (do_load_reg_imm) begin
|
|
// $write("load reg imm %h | ", i_nibble);
|
|
// $write("pos %d | max %d | ", o_mem_pos, mem_load_max);
|
|
// $write("next %b | dec %b | ", (o_mem_pos+1) != mem_load_max, (o_mem_pos+1) == mem_load_max);
|
|
// $write("\n");
|
|
o_ins_alu_op <= 1;
|
|
o_imm_value <= i_nibble;
|
|
o_mem_load[o_mem_pos*4+:4] <= i_nibble;
|
|
o_mem_pos <= o_mem_pos + 1;
|
|
next_nibble <= (o_mem_pos+1) != mem_load_max;
|
|
o_ins_decoded <= (o_mem_pos+1) == mem_load_max;
|
|
block_load_reg_imm <= (o_mem_pos+1) != mem_load_max;
|
|
end
|
|
|
|
if (do_block_jmp) begin
|
|
o_ins_alu_op <= 1;
|
|
o_imm_value <= i_nibble;
|
|
o_mem_load[o_mem_pos*4+:4] <= i_nibble;
|
|
o_mem_pos <= o_mem_pos + 1;
|
|
next_nibble <= mem_load_max != o_mem_pos;
|
|
o_ins_decoded <= mem_load_max == o_mem_pos;
|
|
block_jmp <= mem_load_max != o_mem_pos;
|
|
end
|
|
|
|
end
|
|
|
|
`include "saturn_decoder_registers.v"
|
|
`include "saturn_decoder_fields.v"
|
|
|
|
|
|
endmodule |